Esempio n. 1
0
    def __init__(self, shape, options):

        self.solid = Wrappers.Solid(shape)
        self.slices = []
        #a list of slices
        self.sliceMap = {}
        #distinct slices. different because some slices are re-used instead of calculated again
        self.options = options

        self.__setDefaultOptions()

        self.extruder = Extruder(options)

        #note: make sure to fix the shape before translating!
        if options.fixShape:
            self.solid.fixDefects()

        if options.translateToPositiveCenter:
            self.solid = self.solid.translateToPositiveCenter(
                options.tableCenterX, options.tableCenterY)

        #now apply slicing limits, if the user specified them
        #warning, it is confusing if the user specifies these, and specifies translate=true, how do they know where zMin and zMax will be?
        if options.zMin == None:
            options.zMin = self.solid.zMin

        if options.zMax == None:
            options.zMax = self.solid.zMax
Esempio n. 2
0
    def __init__(self, shape, options):

        self.solid = Wrappers.Solid(shape)
        self.slices = []
        # a list of slices
        self.sliceMap = {}
        # distinct slices. different because some slices are re-used instead of calculated again
        self.options = options

        self.__setDefaultOptions()

        self.extruder = Extruder(options)

        # note: make sure to fix the shape before translating!
        if options.fixShape:
            self.solid.fixDefects()

        if options.translateToPositiveCenter:
            self.solid = self.solid.translateToPositiveCenter(options.tableCenterX, options.tableCenterY)

        # now apply slicing limits, if the user specified them
        # warning, it is confusing if the user specifies these, and specifies translate=true, how do they know where zMin and zMax will be?
        if options.zMin == None:
            options.zMin = self.solid.zMin

        if options.zMax == None:
            options.zMax = self.solid.zMax
Esempio n. 3
0
class Slicer:
    """
        Load the shape, and compute slicing options
    """
    def __init__(self, shape, options):

        self.solid = Wrappers.Solid(shape)
        self.slices = []
        #a list of slices
        self.sliceMap = {}
        #distinct slices. different because some slices are re-used instead of calculated again
        self.options = options

        self.__setDefaultOptions()

        self.extruder = Extruder(options)

        #note: make sure to fix the shape before translating!
        if options.fixShape:
            self.solid.fixDefects()

        if options.translateToPositiveCenter:
            self.solid = self.solid.translateToPositiveCenter(
                options.tableCenterX, options.tableCenterY)

        #now apply slicing limits, if the user specified them
        #warning, it is confusing if the user specifies these, and specifies translate=true, how do they know where zMin and zMax will be?
        if options.zMin == None:
            options.zMin = self.solid.zMin

        if options.zMax == None:
            options.zMax = self.solid.zMax

    def __setDefaultOptions(self):
        """
            sets defaults that may not have been set by the user.
            This is done here rather than inside of Slicing Defaults because
            in several cases, knowledge of the part and the options is required to do a good job.
        """
        options = self.options
        errs = options.errors()
        if len(errs) > 0:
            raise Exception("Problem with Options: " + str(errs))

        #does user want to translate to positive space?
        if options.translateToPositiveCenter == None:
            options.translateToPositiveCenter = True

        #guess unit of measure
        if options.units == None:
            options.units = self.solid.guessUnitOfMeasure()

        #guess sane values for options not supplied
        if options.trackWidth == None:
            options.trackWidth = options.nozzleDiameter * 1.05

        if options.layerHeight == None:
            options.layerHeight = 0.1

        if options.fillingWallThickness == None:
            options.fillingWallThickness = 1.0
            #must normalize to units

        #TODO: fix this later with smarter computations
        #hack-- we can do better than this later
        options.generateSupports = False
        options.machineMaxFeedRate = 200
        options.fixShape = True

        if options.fillingEnabled is None:
            options.fillingEnabled = True

        options.gcodeNumberFormat = "%0.3f"
        options.gcodeVerboseComments = True
        options.gcodeUseArcs = True
        options.gcodeAxisName = "E"
        options.gcodeCurveApproxTolerance = 0.001
        options.gcodeFileExtension = ".gcode"
        options.svgFileExtension = ".svg"

    """
        Slices the object, after which slices will contain a list of slices for printing
        A slice is a horizontal section of the part.
        TODO: add 'skeinning'-- more perimeters than fill lines
    """

    @Util.printTiming
    def execute(self):

        #TODO: use parallel cores to speed this up, see
        #http://code.google.com/p/pythonocc/source/browse/trunk/src/examples/Level2/Concurrency/parallel_slicer.py
        #at this point we have a solid ready to execute

        zLevel = self.options.zMin + TOLERANCE
        layerNo = 1
        fillAngle = 0

        while zLevel < self.options.zMax:
            print "Slicing At ZLevel=%0.3f" % zLevel
            #compute the faces for the slice. It may be a transformed
            #copy of another slice, or a new slice
            cSlice = self._computeSlice(zLevel, layerNo, fillAngle)
            self.slices.append(cSlice)
            zLevel += self.options.layerHeight

    #return (resultShape,outerWire,otherWires)
    @Util.printTiming
    def makeSection(self, cuttingPlane, shapeToSection, zLevel):
        """
            Uses halfspaces to make a cut.
        """
        bff = BRepBuilderAPI.BRepBuilderAPI_MakeFace(cuttingPlane)
        face = bff.Face()
        origin = gp.gp_Pnt(0, 0, zLevel - 1)

        #odd, a halfspace is faster than a box?
        hs = BRepPrimAPI.BRepPrimAPI_MakeHalfSpace(face, origin)
        hs.Build()
        halfspace = hs.Solid()

        #make the cut
        bc = BRepAlgoAPI.BRepAlgoAPI_Cut(self.solid.shape, halfspace)
        cutShape = bc.Shape()

        ff = []
        for face in Topo(cutShape).faces():
            if OCCUtil.isFaceAtZLevel(zLevel, face):
                ff.append(face)
        return ff

    @Util.printTiming
    def makeSection2(self, cuttingPlane, shapeToSection, zLevel):
        """
            Uses BrepSection Algo. this generally returns a list of wires, not a face      
        """
        #section is certainly faster, but produces only edges.
        #those have to be re-organized into wires, probably
        #using ShapeAnalysis_WireOrder

        face = BRepBuilderAPI.BRepBuilderAPI_MakeFace(cuttingPlane).Shape()
        # Computes Shape/Plane intersection
        section = BRepAlgoAPI.BRepAlgoAPI_Section(self.solid.shape, face)
        #section = BRepAlgo.BRepAlgo_Section(self.solid.shape,face);
        section.Build()
        if section.IsDone():
            #Topology.dumpTopology(section.Shape());

            #what we got back was a compound of edges
            t = Topo(section.Shape())
            wb = OCCUtil.MultiWireBuilder()
            for e in t.edges():
                wb.addEdge(e)
            wires = wb.getWires()
            print wires
            for w in wires:
                Topology.dumpTopology(w)
            return wires
        else:
            raise Exception("Could not compute Section!")

    "computes the boundaries for the given slice"

    #@Util.printTiming
    def _computeSlice(self, zLevel, layerNo, fillAngle):

        cSlice = Slice()
        cSlice.zLevel = zLevel
        cSlice.layerNo = layerNo
        cSlice.fillAngle = fillAngle
        cSlice.thickness = self.options.layerHeight

        #make a cutting plane
        p = gp.gp_Pnt(0, 0, zLevel)

        csys = gp.gp_Ax3(p, gp.gp().DZ())
        cuttingPlane = gp.gp_Pln(csys)

        #makeSection will use the old reliable way that will get a face from each cut.
        #makeSection2 will use BRepAlgoaPI_Section, but has problems ordering the edges correctly.
        newFaces = self.makeSection(cuttingPlane, self.solid.shape, zLevel)
        for f in newFaces:
            cSlice.addFace(Face(f))
        #bff = BRepBuilderAPI.BRepBuilderAPI_MakeFace(cuttingPlane);
        #face = bff.Face();

        #odd, a halfspace is faster than a box?
        #hs = BRepPrimAPI.BRepPrimAPI_MakeHalfSpace(face,origin);
        #hs.Build();
        #halfspace = hs.Solid();

        #make the cut
        #bc = BRepAlgoAPI.BRepAlgoAPI_Cut(self.solid.shape,halfspace);
        #cutShape = bc.Shape();

        #for face in Topo(cutShape).faces():
        #    if OCCUtil.isFaceAtZLevel(zLevel,face):
        #        cSlice.addFace(Face(face));
        #        break;

        mySum = cSlice.computeFingerPrint()

        #
        #uncomment to enable layer copying.
        #
        #check for identical slices. return matching ones if found.
        if self.sliceMap.has_key(mySum):
            #print "i can copy this layer!"
            return self.sliceMap[mySum].copyToZ(zLevel, layerNo)

        self.sliceMap[mySum] = cSlice
        #print "This slice has %d faces." % len(cSlice.faces)

        if self.options.fillingEnabled:
            self._fillSlice(cSlice)

        return cSlice

    """
        Fills a slice with vector paths for FDM
    """

    def _fillSlice(self, slice):
        #how many shells do we need?
        #attempt to create the number of shells requested by the user, plus one additional for infill
        for f in slice.faces:
            numShells = (int)(self.options.fillingWallThickness /
                              self.extruder.trackWidth()) + 1
            numShells = max(2, numShells)
            faceWires = OCCUtil.wireListFromFace(f.face)

            #regardless, the last successfully created offset is used for hatch infill
            shells = []
            for i in range(1, numShells):
                #compute offset inwards by one track width
                offset = (-1) * i * self.extruder.trackWidth()
                innerEdge = OCCUtil.offsetWireList(faceWires, offset)

                if len(innerEdge) == 0:
                    #performance: dont offset if there were already no wires
                    continue

                pathCenter = OCCUtil.offsetWireList(
                    innerEdge,
                    self.extruder.trackWidth() / 2)
                if len(pathCenter) > 0:
                    shells.append(pathCenter)

            if len(shells) > 1:
                #use last one for filling.
                print "%d shells were available for Hatching" % len(shells)
                lastShell = shells.pop()
                s = self.solid
                h = hatchlib.Hatcher(lastShell, zLevel,
                                     (s.xMin, s.yMin, s.xMax, s.yMax),
                                     self.extruder.trackWidth(),
                                     cSlice.fillAngle)
                h.hatch()
                ww = h.getWires()
                print "Hatching complete: %d fillWires created" % len(ww)
                f.fillWires = ww
            else:
                print "WARNING: not filling this layer, too few shells were computable"
            #add shells
            for s in shells:
                for ss in s:
                    f.shellWires.append(ss)

    def display(self):
        """
            for debugging purposes, display the result
        """
        for slice in self.slices:
            for f in slice.faces:
                for w in f.fillWires:
                    debugdisplay.DisplayColoredShape(w, 'BLUE', False)
                for w in f.shellWires:
                    debugdisplay.DisplayColoredShape(w, 'GREEN', False)
        debugdisplay.FitAll()
        debugdisplay.start_display()
Esempio n. 4
0
class Slicer:
    """
        Load the shape, and compute slicing options
    """

    def __init__(self, shape, options):

        self.solid = Wrappers.Solid(shape)
        self.slices = []
        # a list of slices
        self.sliceMap = {}
        # distinct slices. different because some slices are re-used instead of calculated again
        self.options = options

        self.__setDefaultOptions()

        self.extruder = Extruder(options)

        # note: make sure to fix the shape before translating!
        if options.fixShape:
            self.solid.fixDefects()

        if options.translateToPositiveCenter:
            self.solid = self.solid.translateToPositiveCenter(options.tableCenterX, options.tableCenterY)

        # now apply slicing limits, if the user specified them
        # warning, it is confusing if the user specifies these, and specifies translate=true, how do they know where zMin and zMax will be?
        if options.zMin == None:
            options.zMin = self.solid.zMin

        if options.zMax == None:
            options.zMax = self.solid.zMax

    def __setDefaultOptions(self):
        """
            sets defaults that may not have been set by the user.
            This is done here rather than inside of Slicing Defaults because
            in several cases, knowledge of the part and the options is required to do a good job.
        """
        options = self.options
        errs = options.errors()
        if len(errs) > 0:
            raise Exception("Problem with Options: " + str(errs))

        # does user want to translate to positive space?
        if options.translateToPositiveCenter == None:
            options.translateToPositiveCenter = True

        # guess unit of measure
        if options.units == None:
            options.units = self.solid.guessUnitOfMeasure()

        # guess sane values for options not supplied
        if options.trackWidth == None:
            options.trackWidth = options.nozzleDiameter * 1.05

        if options.layerHeight == None:
            options.layerHeight = 0.1

        if options.fillingWallThickness == None:
            options.fillingWallThickness = 1.0
            # must normalize to units

        # TODO: fix this later with smarter computations
        # hack-- we can do better than this later
        options.generateSupports = False
        options.machineMaxFeedRate = 200
        options.fixShape = True

        if options.fillingEnabled is None:
            options.fillingEnabled = True

        options.gcodeNumberFormat = "%0.3f"
        options.gcodeVerboseComments = True
        options.gcodeUseArcs = True
        options.gcodeAxisName = "E"
        options.gcodeCurveApproxTolerance = 0.001
        options.gcodeFileExtension = ".gcode"
        options.svgFileExtension = ".svg"

    """
        Slices the object, after which slices will contain a list of slices for printing
        A slice is a horizontal section of the part.
        TODO: add 'skeinning'-- more perimeters than fill lines
    """

    @Util.printTiming
    def execute(self):

        # TODO: use parallel cores to speed this up, see
        # http://code.google.com/p/pythonocc/source/browse/trunk/src/examples/Level2/Concurrency/parallel_slicer.py
        # at this point we have a solid ready to execute

        zLevel = self.options.zMin + TOLERANCE
        layerNo = 1
        fillAngle = 0

        while zLevel < self.options.zMax:
            print "Slicing At ZLevel=%0.3f" % zLevel
            # compute the faces for the slice. It may be a transformed
            # copy of another slice, or a new slice
            cSlice = self._computeSlice(zLevel, layerNo, fillAngle)
            self.slices.append(cSlice)
            zLevel += self.options.layerHeight

    # return (resultShape,outerWire,otherWires)
    @Util.printTiming
    def makeSection(self, cuttingPlane, shapeToSection, zLevel):
        """
            Uses halfspaces to make a cut.
        """
        bff = BRepBuilderAPI.BRepBuilderAPI_MakeFace(cuttingPlane)
        face = bff.Face()
        origin = gp.gp_Pnt(0, 0, zLevel - 1)

        # odd, a halfspace is faster than a box?
        hs = BRepPrimAPI.BRepPrimAPI_MakeHalfSpace(face, origin)
        hs.Build()
        halfspace = hs.Solid()

        # make the cut
        bc = BRepAlgoAPI.BRepAlgoAPI_Cut(self.solid.shape, halfspace)
        cutShape = bc.Shape()

        ff = []
        for face in Topo(cutShape).faces():
            if OCCUtil.isFaceAtZLevel(zLevel, face):
                ff.append(face)
        return ff

    @Util.printTiming
    def makeSection2(self, cuttingPlane, shapeToSection, zLevel):
        """
            Uses BrepSection Algo. this generally returns a list of wires, not a face      
        """
        # section is certainly faster, but produces only edges.
        # those have to be re-organized into wires, probably
        # using ShapeAnalysis_WireOrder

        face = BRepBuilderAPI.BRepBuilderAPI_MakeFace(cuttingPlane).Shape()
        # Computes Shape/Plane intersection
        section = BRepAlgoAPI.BRepAlgoAPI_Section(self.solid.shape, face)
        # section = BRepAlgo.BRepAlgo_Section(self.solid.shape,face);
        section.Build()
        if section.IsDone():
            # Topology.dumpTopology(section.Shape());

            # what we got back was a compound of edges
            t = Topo(section.Shape())
            wb = OCCUtil.MultiWireBuilder()
            for e in t.edges():
                wb.addEdge(e)
            wires = wb.getWires()
            print wires
            for w in wires:
                Topology.dumpTopology(w)
            return wires
        else:
            raise Exception("Could not compute Section!")

    "computes the boundaries for the given slice"
    # @Util.printTiming
    def _computeSlice(self, zLevel, layerNo, fillAngle):

        cSlice = Slice()
        cSlice.zLevel = zLevel
        cSlice.layerNo = layerNo
        cSlice.fillAngle = fillAngle
        cSlice.thickness = self.options.layerHeight

        # make a cutting plane
        p = gp.gp_Pnt(0, 0, zLevel)

        csys = gp.gp_Ax3(p, gp.gp().DZ())
        cuttingPlane = gp.gp_Pln(csys)

        # makeSection will use the old reliable way that will get a face from each cut.
        # makeSection2 will use BRepAlgoaPI_Section, but has problems ordering the edges correctly.
        newFaces = self.makeSection(cuttingPlane, self.solid.shape, zLevel)
        for f in newFaces:
            cSlice.addFace(Face(f))
        # bff = BRepBuilderAPI.BRepBuilderAPI_MakeFace(cuttingPlane);
        # face = bff.Face();

        # odd, a halfspace is faster than a box?
        # hs = BRepPrimAPI.BRepPrimAPI_MakeHalfSpace(face,origin);
        # hs.Build();
        # halfspace = hs.Solid();

        # make the cut
        # bc = BRepAlgoAPI.BRepAlgoAPI_Cut(self.solid.shape,halfspace);
        # cutShape = bc.Shape();

        # for face in Topo(cutShape).faces():
        #    if OCCUtil.isFaceAtZLevel(zLevel,face):
        #        cSlice.addFace(Face(face));
        #        break;

        mySum = cSlice.computeFingerPrint()

        #
        # uncomment to enable layer copying.
        #
        # check for identical slices. return matching ones if found.
        if self.sliceMap.has_key(mySum):
            # print "i can copy this layer!"
            return self.sliceMap[mySum].copyToZ(zLevel, layerNo)

        self.sliceMap[mySum] = cSlice
        # print "This slice has %d faces." % len(cSlice.faces)

        if self.options.fillingEnabled:
            self._fillSlice(cSlice)

        return cSlice

    """
        Fills a slice with vector paths for FDM
    """

    def _fillSlice(self, slice):
        # how many shells do we need?
        # attempt to create the number of shells requested by the user, plus one additional for infill
        for f in slice.faces:
            numShells = (int)(self.options.fillingWallThickness / self.extruder.trackWidth()) + 1
            numShells = max(2, numShells)
            faceWires = OCCUtil.wireListFromFace(f.face)

            # regardless, the last successfully created offset is used for hatch infill
            shells = []
            for i in range(1, numShells):
                # compute offset inwards by one track width
                offset = (-1) * i * self.extruder.trackWidth()
                innerEdge = OCCUtil.offsetWireList(faceWires, offset)

                if len(innerEdge) == 0:
                    # performance: dont offset if there were already no wires
                    continue

                pathCenter = OCCUtil.offsetWireList(innerEdge, self.extruder.trackWidth() / 2)
                if len(pathCenter) > 0:
                    shells.append(pathCenter)

            if len(shells) > 1:
                # use last one for filling.
                print "%d shells were available for Hatching" % len(shells)
                lastShell = shells.pop()
                s = self.solid
                h = hatchlib.Hatcher(
                    lastShell, zLevel, (s.xMin, s.yMin, s.xMax, s.yMax), self.extruder.trackWidth(), cSlice.fillAngle
                )
                h.hatch()
                ww = h.getWires()
                print "Hatching complete: %d fillWires created" % len(ww)
                f.fillWires = ww
            else:
                print "WARNING: not filling this layer, too few shells were computable"
            # add shells
            for s in shells:
                for ss in s:
                    f.shellWires.append(ss)

    def display(self):
        """
            for debugging purposes, display the result
        """
        for slice in self.slices:
            for f in slice.faces:
                for w in f.fillWires:
                    debugdisplay.DisplayColoredShape(w, "BLUE", False)
                for w in f.shellWires:
                    debugdisplay.DisplayColoredShape(w, "GREEN", False)
        debugdisplay.FitAll()
        debugdisplay.start_display()