Skip to content

Commit

Permalink
Use NozzleSize property to reduce number of points
Browse files Browse the repository at this point in the history
fixes #3
  • Loading branch information
furti committed Jul 2, 2018
1 parent 6c42247 commit acda049
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 23 deletions.
11 changes: 2 additions & 9 deletions create_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import lithophane_utils
from utils.timer import Timer, computeOverallTime
from utils.geometry_utils import pointCloudToLines

def makeBlockBase(lines):
facets = []
Expand Down Expand Up @@ -113,19 +112,13 @@ def Activated(self):
# https://www.freecadweb.org/wiki/Mesh_Scripting
timers = []

timers.append(Timer('Calculating Lines from PointCloud'))
lines = pointCloudToLines(lithophaneImage.points)
timers[-1].stop()
lithophane_utils.processEvents()


timers.append(Timer('Creating ImagePlane'))
imagePlane = makeImagePlane(lines)
imagePlane = makeImagePlane(lithophaneImage.lines)
timers[-1].stop()
lithophane_utils.processEvents()

timers.append(Timer('Creating ImageBase'))
block = makeBlockBase(lines)
block = makeBlockBase(lithophaneImage.lines)
timers[-1].stop()
lithophane_utils.processEvents()

Expand Down
69 changes: 58 additions & 11 deletions lithophane_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,34 @@

from __future__ import division

import math
import FreeCAD, FreeCADGui
from PySide import QtGui, QtCore
from pivy import coin

from image_viewer import ImageViewer
from utils.geometry_utils import pointCloudToLines
from lithophane_utils import toChunks

baseHeight = 0.5 # basically the height for white color
maximumHeight = 3 # The maximum height for black colors

class AverageVector:
def __init__(self):
self.baseVector = None
self.heights = []

def add(self, vector):
if self.baseVector is None:
self.baseVector = vector

self.heights.append(vector.z)

def average(self):
averageHeight = sum(self.heights) / len(self.heights)

return FreeCAD.Vector(self.baseVector.x, self.baseVector.y, averageHeight)

def mmPerPixel(ppi):
pixelsPerMm = ppi / 25.4

Expand Down Expand Up @@ -117,7 +136,7 @@ def calculatePixelHeight(image, x, y):

return baseHeight + ((maximumHeight - baseHeight) * percentage) / 100

def computePoints(image, ppi):
def computeLines(image, ppi):
pixelSize = mmPerPixel(ppi)
imageSize = image.size()
imageHeight = imageSize.height()
Expand All @@ -139,17 +158,46 @@ def computePoints(image, ppi):

pts.append(FreeCAD.Vector(x * pixelSize, (imageHeight - (y + 1)) * pixelSize, pixelHeight))

lines = pointCloudToLines(pts)
#FreeCAD.Console.PrintMessage(maxHeight)
#FreeCAD.Console.PrintMessage(pts)

return (pts, maxHeight)
return (lines, maxHeight)

def averageByNozzleSize(lines, ppi, nozzleSize):
if nozzleSize == 0:
return lines

reducedLines = []
pixelSize = mmPerPixel(ppi)
numberOfPointsToReduce = int(round((nozzleSize.Value / pixelSize)))

for linesToCombine in toChunks(lines, numberOfPointsToReduce):
combined = []

for line in linesToCombine:
for index, rowsToCombine in enumerate(toChunks(line, numberOfPointsToReduce)):
if len(combined) < index + 1:
combined.append(AverageVector())

for point in rowsToCombine:
combined[index].add(point)

del rowsToCombine

reducedLines.append([vector.average() for vector in combined])
del linesToCombine


return reducedLines

class LithophaneImage:
def __init__(self, obj, imagePath):
'''Add properties for image like path'''
obj.addProperty("App::PropertyString","Path","LithophaneImage","Path to the original image").Path=imagePath
obj.addProperty("App::PropertyInteger", "ppi", "LithophaneImage", "Pixels per Inch").ppi = 300
obj.addProperty("App::PropertyBool", "ReducePoints", "LithophaneImage", "Remove unneeded pixels from the iamge").ReducePoints = False
obj.addProperty("App::PropertyLength", "NozzleSize", "LithophaneImage", "Size of your 3D printers Nozzle").NozzleSize = 0.4
obj.Proxy = self

self.lastPath = imagePath
Expand All @@ -169,22 +217,21 @@ def execute(self, fp):

FreeCAD.Console.PrintMessage("LithophaneImage: Recompute Point cloud" + str(self) + "\n")

pointData = computePoints(self.image, fp.ppi)
pointData = computeLines(self.image, fp.ppi)
lines = averageByNozzleSize(pointData[0], fp.ppi, fp.NozzleSize)

if(fp.ReducePoints):
reducedPoints = reducePoints(pointData[0], self.imageHeight, self.imageWidth)
else:
reducedPoints = pointData[0]
# if(fp.ReducePoints):
# points = reducePoints(points, self.imageHeight, self.imageWidth)

self.points = reducedPoints
self.lines = lines
self.maxHeight = pointData[1]

def __getstate__(self):
'''Store the image as base64 inside the document'''

base64ImageOriginal = imgToBase64(self.image)

return (base64ImageOriginal, self.lastPath, self.points, self.maxHeight)
return (base64ImageOriginal, self.lastPath, self.lines, self.maxHeight)

def __setstate__(self,state):
'''Restore the state'''
Expand All @@ -193,7 +240,7 @@ def __setstate__(self,state):

self.image = imageFromBase64(base64ImageOriginal)
self.lastPath = state[1]
self.points = state[2]
self.lines = state[2]
self.maxHeight = state[3]

imageSize = self.image.size()
Expand Down Expand Up @@ -255,7 +302,7 @@ def createImage(imagePath):

if __name__ == "__main__":
import os
imagePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), './testimages/tini.png')
imagePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), './testimages/medium.png')

imageReader = QtGui.QImageReader(imagePath)
image = imageReader.read()
Expand Down
16 changes: 15 additions & 1 deletion lithophane_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FreeCAD, FreeCADGui
import time
import time, itertools
from PySide import QtGui

def recomputeView():
Expand Down Expand Up @@ -30,3 +30,17 @@ def vectorAtGround(vector):
def processEvents():
time.sleep(0.001)
QtGui.QApplication.processEvents()

def toChunks(iterable, chunksize):
"""
Splits the iterable into evenly sized chunks. The last chunk can be smaller when iterable contains not enough elements
"""
i = iter(iterable)

while True:
wrapped_chunk = [list(itertools.islice(i, int(chunksize)))]

if not wrapped_chunk[0]:
break

yield wrapped_chunk.pop()
5 changes: 3 additions & 2 deletions show_pointcloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from PySide import QtGui

import lithophane_utils
from utils.geometry_utils import linesToPointCloud

def showPointCloud(pts, name):
pointCloud = Points.Points()
pointCloud.addPoints(pts)
pointCloud.addPoints(linesToPointCloud(pts))

Points.show(pointCloud, name)

Expand All @@ -27,7 +28,7 @@ def Activated(self):

return

showPointCloud(lithophaneImage.points, 'ImagePointCloud')
showPointCloud(lithophaneImage.lines, 'ImagePointCloud')

lithophane_utils.recomputeView()

Expand Down
4 changes: 4 additions & 0 deletions utils/geometry_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ def pointCloudToLines(pointCloud):
lines.append(line)

return lines

def linesToPointCloud(lines):
return [point for line in lines for point in line]

0 comments on commit acda049

Please sign in to comment.