예제 #1
0
def curve(o):
    print('operation: curve')

    pathSamples = []
    utils.getOperationSources(o)
    if not o.onlycurves:
        o.warnings += 'at least one of assigned objects is not a curve\n'

    for ob in o.objects:
        pathSamples.extend(
            curveToChunks(ob))  # make the chunks from curve here
    pathSamples = utils.sortChunks(pathSamples, o)  # sort before sampling
    pathSamples = chunksRefine(pathSamples, o)  # simplify

    # layers here
    if o.use_layers:
        layers = getLayers(
            o, o.maxz, round(checkminz(o), 6)
        )  # layers is a list of lists [[0.00,l1],[l1,l2],[l2,l3]] containg the start and end of each layer
        extendorder = []
        chunks = []
        for layer in layers:
            for ch in pathSamples:
                extendorder.append(
                    [ch.copy(),
                     layer])  # include layer information to chunk list

        for chl in extendorder:  # Set offset Z for all chunks according to the layer information,
            chunk = chl[0]
            layer = chl[1]
            print('layer: ' + str(layer[1]))
            chunk.offsetZ(o.maxz * 2 - o.minz + layer[1])
            chunk.clampZ(o.minz)  # safety to not cut lower than minz
            chunk.clampmaxZ(o.free_movement_height
                            )  # safety, not higher than free movement height

        for chl in extendorder:  # strip layer information from extendorder and transfer them to chunks
            chunks.append(chl[0])

        chunksToMesh(chunks, o)  # finish by converting to mesh

    else:  # no layers, old curve
        for ch in pathSamples:
            ch.clampZ(o.minz)  # safety to not cut lower than minz
            ch.clampmaxZ(o.free_movement_height
                         )  # safety, not higher than free movement height
        chunksToMesh(pathSamples, o)
예제 #2
0
def getPath3axis(context, operation):
    s = bpy.context.scene
    o = operation
    utils.getBounds(o)

    if o.strategy == 'CUTOUT':
        strategy.cutout(o)

    elif o.strategy == 'CURVE':
        strategy.curve(o)

    elif o.strategy == 'PROJECTED_CURVE':
        strategy.proj_curve(s, o)

    elif o.strategy == 'POCKET':
        strategy.pocket(o)

    elif o.strategy in ['PARALLEL', 'CROSS', 'BLOCK', 'SPIRAL', 'CIRCLES', 'OUTLINEFILL', 'CARVE', 'PENCIL', 'CRAZY']:

        if o.strategy == 'CARVE':
            pathSamples = []
            # for ob in o.objects:
            ob = bpy.data.objects[o.curve_object]
            pathSamples.extend(curveToChunks(ob))
            pathSamples = utils.sortChunks(pathSamples, o)  # sort before sampling
            pathSamples = chunksRefine(pathSamples, o)
        elif o.strategy == 'PENCIL':
            prepareArea(o)
            utils.getAmbient(o)
            pathSamples = getOffsetImageCavities(o, o.offset_image)
            # for ch in pathSamples:
            #	for i,p in enumerate(ch.points):
            #	 ch.points[i]=(p[0],p[1],0)
            pathSamples = limitChunks(pathSamples, o)
            pathSamples = utils.sortChunks(pathSamples, o)  # sort before sampling
        elif o.strategy == 'CRAZY':
            prepareArea(o)

            # pathSamples = crazyStrokeImage(o)
            #####this kind of worked and should work:
            millarea = o.zbuffer_image < o.minz + 0.000001
            avoidarea = o.offset_image > o.minz + 0.000001

            pathSamples = crazyStrokeImageBinary(o, millarea, avoidarea)
            #####
            pathSamples = utils.sortChunks(pathSamples, o)
            pathSamples = chunksRefine(pathSamples, o)

        else:
            print("PARALLEL")
            if o.strategy == 'OUTLINEFILL':
                utils.getOperationSilhouete(o)

            pathSamples = getPathPattern(o)

            if o.strategy == 'OUTLINEFILL':
                pathSamples = utils.sortChunks(pathSamples,
                                               o)  # have to be sorted once before, because of the parenting inside of samplechunks
            # chunksToMesh(pathSamples,o)#for testing pattern script
            # return
            if o.strategy in ['BLOCK', 'SPIRAL', 'CIRCLES']:
                pathSamples = utils.connectChunksLow(pathSamples, o)

        # print (minz)

        chunks = []
        layers = strategy.getLayers(o, o.maxz, o.min.z)

        print("SAMPLE", o.name)
        chunks.extend(utils.sampleChunks(o, pathSamples, layers))
        print("SAMPLE OK")
        if o.strategy == 'PENCIL':  # and bpy.app.debug_value==-3:
            chunks = chunksCoherency(chunks)
            print('coherency check')

        if o.strategy in ['PARALLEL', 'CROSS', 'PENCIL', 'OUTLINEFILL']:  # and not o.parallel_step_back:
            print('sorting')
            chunks = utils.sortChunks(chunks, o)
            if o.strategy == 'OUTLINEFILL':
                chunks = utils.connectChunksLow(chunks, o)
        if o.ramp:
            for ch in chunks:
                ch.rampZigZag(ch.zstart, ch.points[0][2], o)
        # print(chunks)
        if o.strategy == 'CARVE':
            for ch in chunks:
                for vi in range(0, len(ch.points)):
                    ch.points[vi] = (ch.points[vi][0], ch.points[vi][1], ch.points[vi][2] - o.carve_depth)
        if o.use_bridges:
            for bridge_chunk in chunks:
                useBridges(bridge_chunk, o)
        strategy.chunksToMesh(chunks, o)

    elif o.strategy == 'WATERLINE' and o.use_opencamlib:
        utils.getAmbient(o)
        chunks = []
        oclGetWaterline(o, chunks)
        chunks = limitChunks(chunks, o)
        if (o.movement_type == 'CLIMB' and o.spindle_rotation_direction == 'CW') or (
                o.movement_type == 'CONVENTIONAL' and o.spindle_rotation_direction == 'CCW'):
            for ch in chunks:
                ch.points.reverse()
        strategy.chunksToMesh(chunks, o)

    elif o.strategy == 'WATERLINE' and not o.use_opencamlib:
        topdown = True
        tw = time.time()
        chunks = []
        progress('retrieving object slices')
        prepareArea(o)
        layerstep = 1000000000
        if o.use_layers:
            layerstep = math.floor(o.stepdown / o.slice_detail)
            if layerstep == 0:
                layerstep = 1

        # for projection of filled areas
        layerstart = o.max.z  #
        layerend = o.min.z  #
        layers = [[layerstart, layerend]]
        #######################
        nslices = ceil(abs(o.minz / o.slice_detail))
        lastislice = numpy.array([])
        lastslice = spolygon.Polygon()  # polyversion
        layerstepinc = 0

        slicesfilled = 0
        utils.getAmbient(o)
        # polyToMesh(o.ambient,0)
        for h in range(0, nslices):
            layerstepinc += 1
            slicechunks = []
            z = o.minz + h * o.slice_detail
            if h == 0:
                z += 0.0000001  # if people do mill flat areas, this helps to reach those... otherwise first layer would actually be one slicelevel above min z.
            # print(z)
            # sliceimage=o.offset_image>z
            islice = o.offset_image > z
            slicepolys = imageToShapely(o, islice, with_border=True)
            # for pviz in slicepolys:
            #    polyToMesh('slice',pviz,z)
            poly = spolygon.Polygon()  # polygversion
            lastchunks = []
            # imagechunks=imageToChunks(o,islice)
            # for ch in imagechunks:
            #    slicechunks.append(camPathChunk([]))
            #    for s in ch.points:
            #    slicechunks[-1].points.append((s[0],s[1],z))

            # print('found polys',layerstepinc,len(slicepolys))
            for p in slicepolys:
                # print('polypoints',p.nPoints(0))
                poly = poly.union(p)  # polygversion TODO: why is this added?
                # print()
                # polyToMesh(p,z)
                nchunks = shapelyToChunks(p, z)
                nchunks = limitChunks(nchunks, o, force=True)
                # print('chunksnum',len(nchunks))
                # if len(nchunks)>0:
                #    print('chunkpoints',len(nchunks[0].points))
                # print()
                lastchunks.extend(nchunks)
                slicechunks.extend(nchunks)
            # print('totchunks',len(slicechunks))
            if len(slicepolys) > 0:
                slicesfilled += 1
            # chunks.extend(polyToChunks(slicepolys[1],z))
            # print(len(p),'slicelen')

            #
            # print(len(lastslice))
            # """
            if o.waterline_fill:
                layerstart = min(o.maxz, z + o.slice_detail)  #
                layerend = max(o.min.z, z - o.slice_detail)  #
                layers = [[layerstart, layerend]]
                #####################################
                # fill top slice for normal and first for inverse, fill between polys
                if not lastslice.is_empty or (o.inverse and not poly.is_empty and slicesfilled == 1):
                    # offs=False
                    restpoly = None
                    if not lastslice.is_empty:  # between polys
                        if o.inverse:
                            restpoly = poly.difference(lastslice)
                        else:
                            restpoly = lastslice.difference(poly)
                    # print('filling between')
                    if (not o.inverse and poly.is_empty and slicesfilled > 0) or (
                            o.inverse and not poly.is_empty and slicesfilled == 1):  # first slice fill
                        restpoly = lastslice
                    # print('filling first')

                    # print(len(restpoly))
                    # polyToMesh('fillrest',restpoly,z)

                    restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)

                    fillz = z
                    i = 0
                    while not restpoly.is_empty:
                        nchunks = shapelyToChunks(restpoly, fillz)
                        # project paths TODO: path projection during waterline is not working
                        if o.waterline_project:
                            nchunks = chunksRefine(nchunks, o)
                            nchunks = utils.sampleChunks(o, nchunks, layers)

                        nchunks = limitChunks(nchunks, o, force=True)
                        #########################
                        slicechunks.extend(nchunks)
                        parentChildDist(lastchunks, nchunks, o)
                        lastchunks = nchunks
                        # slicechunks.extend(polyToChunks(restpoly,z))
                        restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)

                        i += 1
                # print(i)
                i = 0
                # """
                #####################################
                # fill layers and last slice, last slice with inverse is not working yet - inverse millings end now always on 0 so filling ambient does have no sense.
                if (slicesfilled > 0 and layerstepinc == layerstep) or (
                        not o.inverse and not poly.is_empty and slicesfilled == 1) or (
                        o.inverse and poly.is_empty and slicesfilled > 0):
                    fillz = z
                    layerstepinc = 0

                    # ilim=1000#TODO:this should be replaced... no limit, just check if the shape grows over limits.

                    # offs=False
                    boundrect = o.ambient
                    restpoly = boundrect.difference(poly)
                    if (o.inverse and poly.is_empty and slicesfilled > 0):
                        restpoly = boundrect.difference(lastslice)

                    restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)

                    i = 0
                    while not restpoly.is_empty:  # 'GeometryCollection':#len(restpoly.boundary.coords)>0:
                        # print(i)
                        nchunks = shapelyToChunks(restpoly, fillz)
                        #########################
                        nchunks = limitChunks(nchunks, o, force=True)
                        slicechunks.extend(nchunks)
                        parentChildDist(lastchunks, nchunks, o)
                        lastchunks = nchunks
                        # slicechunks.extend(polyToChunks(restpoly,z))
                        restpoly = restpoly.buffer(-o.dist_between_paths, resolution=o.circle_detail)
                        i += 1

                # """
                percent = int(h / nslices * 100)
                progress('waterline layers ', percent)
                lastslice = poly

            # print(poly)
            # print(len(lastslice))

            # if len(lastislice)>0:
            #     i=numpy.logical_xor(lastislice , islice)
            #
            #     n=0
            #     while i.sum()>0 and n<10000:
            #         i=outlineImageBinary(o,o.dist_between_paths,i,False)
            #         polys=imageToShapely(o,i)
            #         for poly in polys:
            #             chunks.extend(polyToChunks(poly,z))
            #         n+=1
            #
            #
            #         #restpoly=outlinePoly(restpoly,o.dist_between_paths,oo.circle_detail,o.optimize,o.optimize_threshold,,False)
            #         #chunks.extend(polyToChunks(restpoly,z))
            #
            # lastislice=islice

            # if bpy.app.debug_value==1:
            if (o.movement_type == 'CONVENTIONAL' and o.spindle_rotation_direction == 'CCW') or (
                    o.movement_type == 'CLIMB' and o.spindle_rotation_direction == 'CW'):
                for chunk in slicechunks:
                    chunk.points.reverse()
            slicechunks = utils.sortChunks(slicechunks, o)
            if topdown:
                slicechunks.reverse()
            # project chunks in between

            chunks.extend(slicechunks)
        # chunks=sortChunks(chunks,o)
        if topdown:
            chunks.reverse()

            # chi=0
            # if len(chunks)>2:
            #     while chi<len(chunks)-2:
            #         d=dist2d((chunks[chi][-1][0],chunks[chi][-1][1]),(chunks[chi+1][0][0],chunks[chi+1][0][1]))
            #         if chunks[chi][0][2]>=chunks[chi+1][0][2] and d<o.dist_between_paths*2:
            #             chunks[chi].extend(chunks[chi+1])
            #             chunks.remove(chunks[chi+1])
            #             chi=chi-1
            #         chi+=1

        print(time.time() - tw)
        strategy.chunksToMesh(chunks, o)

    elif o.strategy == 'DRILL':
        strategy.drill(o)

    elif o.strategy == 'MEDIAL_AXIS':
        strateg.medial_axis(o)
예제 #3
0
def medial_axis(o):
    print('operation: Medial Axis')
    print('doing highly experimental stuff')

    from cam.voronoi import Site, computeVoronoiDiagram

    chunks = []

    gpoly = spolygon.Polygon()
    angle = o.cutter_tip_angle
    slope = math.tan(math.pi * (90 - angle / 2) / 180)
    if o.cutter_type == 'VCARVE':
        angle = o.cutter_tip_angle
        # start the max depth calc from the "start depth" of the operation.
        maxdepth = o.maxz - math.tan(
            math.pi * (90 - angle / 2) / 180) * o.cutter_diameter / 2
        # don't cut any deeper than the "end depth" of the operation.
        if maxdepth < o.minz:
            maxdepth = o.minz
            # the effective cutter diameter can be reduced from it's max since we will be cutting shallower than the original maxdepth
            # without this, the curve is calculated as if the diameter was at the original maxdepth and we get the bit
            # pulling away from the desired cut surface
            o.cutter_diameter = (maxdepth - o.maxz) / (
                -math.tan(math.pi * (90 - angle / 2) / 180)) * 2
    elif o.cutter_type == 'BALLNOSE' or o.cutter_type == 'BALL':
        # angle = o.cutter_tip_angle
        maxdepth = o.cutter_diameter / 2
    else:
        o.warnings += 'Only Ballnose, Ball and V-carve cutters\n are supported'
        return
    # remember resolutions of curves, to refine them,
    # otherwise medial axis computation yields too many branches in curved parts
    resolutions_before = []
    for ob in o.objects:
        if ob.type == 'CURVE' or ob.type == 'FONT':
            resolutions_before.append(ob.data.resolution_u)
            if ob.data.resolution_u < 64:
                ob.data.resolution_u = 64

    polys = utils.getOperationSilhouete(o)
    mpoly = sgeometry.asMultiPolygon(polys)
    mpoly_boundary = mpoly.boundary
    for poly in polys:
        schunks = shapelyToChunks(poly, -1)
        schunks = chunksRefineThreshold(
            schunks, o.medial_axis_subdivision,
            o.medial_axis_threshold)  # chunksRefine(schunks,o)

        verts = []
        for ch in schunks:
            for pt in ch.points:
                # pvoro = Site(pt[0], pt[1])
                verts.append(pt)  # (pt[0], pt[1]), pt[2])
        # verts= points#[[vert.x, vert.y, vert.z] for vert in vertsPts]
        nDupli, nZcolinear = unique(verts)
        nVerts = len(verts)
        print(str(nDupli) + " duplicates points ignored")
        print(str(nZcolinear) + " z colinear points excluded")
        if nVerts < 3:
            print("Not enough points")
            return {'FINISHED'}
        # Check colinear
        xValues = [pt[0] for pt in verts]
        yValues = [pt[1] for pt in verts]
        if checkEqual(xValues) or checkEqual(yValues):
            print("Points are colinear")
            return {'FINISHED'}
        # Create diagram
        print("Tesselation... (" + str(nVerts) + " points)")
        xbuff, ybuff = 5, 5  # %
        zPosition = 0
        vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts]
        # vertsPts= [Point(vert[0], vert[1]) for vert in verts]

        pts, edgesIdx = computeVoronoiDiagram(vertsPts,
                                              xbuff,
                                              ybuff,
                                              polygonsOutput=False,
                                              formatOutput=True)

        #
        # pts=[[pt[0], pt[1], zPosition] for pt in pts]
        newIdx = 0
        vertr = []
        filteredPts = []
        print('filter points')
        for p in pts:
            if not poly.contains(sgeometry.Point(p)):
                vertr.append((True, -1))
            else:
                vertr.append((False, newIdx))
                if o.cutter_type == 'VCARVE':
                    # start the z depth calc from the "start depth" of the operation.
                    z = o.maxz - mpoly.boundary.distance(
                        sgeometry.Point(p)) * slope
                    if z < maxdepth:
                        z = maxdepth
                elif o.cutter_type == 'BALL' or o.cutter_type == 'BALLNOSE':
                    d = mpoly_boundary.distance(sgeometry.Point(p))
                    r = o.cutter_diameter / 2.0
                    if d >= r:
                        z = -r
                    else:
                        # print(r, d)
                        z = -r + sqrt(r * r - d * d)
                else:
                    z = 0  #
                # print(mpoly.distance(sgeometry.Point(0,0)))
                # if(z!=0):print(z)
                filteredPts.append((p[0], p[1], z))
                newIdx += 1

        print('filter edges')
        filteredEdgs = []
        ledges = []
        for e in edgesIdx:

            do = True
            p1 = pts[e[0]]
            p2 = pts[e[1]]
            # print(p1,p2,len(vertr))
            if vertr[e[0]][0]:  # exclude edges with allready excluded points
                do = False
            elif vertr[e[1]][0]:
                do = False
            if do:
                filteredEdgs.append(((vertr[e[0]][1], vertr[e[1]][1])))
                ledges.append(
                    sgeometry.LineString((filteredPts[vertr[e[0]][1]],
                                          filteredPts[vertr[e[1]][1]])))
        # print(ledges[-1].has_z)

        bufpoly = poly.buffer(-o.cutter_diameter / 2, resolution=64)

        lines = shapely.ops.linemerge(ledges)
        # print(lines.type)

        if bufpoly.type == 'Polygon' or bufpoly.type == 'MultiPolygon':
            lines = lines.difference(bufpoly)
            chunks.extend(shapelyToChunks(bufpoly, maxdepth))
        chunks.extend(shapelyToChunks(lines, 0))

        # segments=[]
        # processEdges=filteredEdgs.copy()
        # chunk=camPathChunk([])
        # chunk.points.append(filteredEdgs.pop())
        # while len(filteredEdgs)>0:

        # Create new mesh structure

        # print("Create mesh...")
        # voronoiDiagram = bpy.data.meshes.new("VoronoiDiagram") #create a new mesh
        #
        #
        #
        # voronoiDiagram.from_pydata(filteredPts, filteredEdgs, []) #Fill the mesh with triangles
        #
        # voronoiDiagram.update(calc_edges=True) #Update mesh with new data
        # #create an object with that mesh
        # voronoiObj = bpy.data.objects.new("VoronoiDiagram", voronoiDiagram)
        # #place object
        # #bpy.ops.view3d.snap_cursor_to_selected()#move 3d-cursor
        #
        # #update scene
        # bpy.context.scene.objects.link(voronoiObj) #Link object to scene
        # bpy.context.scene.objects.active = voronoiObj
        # voronoiObj.select = True

    # bpy.ops.object.convert(target='CURVE')
    oi = 0
    for ob in o.objects:
        if ob.type == 'CURVE' or ob.type == 'FONT':
            ob.data.resolution_u = resolutions_before[oi]
            oi += 1

    # bpy.ops.object.join()
    chunks = utils.sortChunks(chunks, o)

    layers = getLayers(o, o.maxz, o.min.z)

    chunklayers = []

    for layer in layers:
        for chunk in chunks:
            if chunk.isbelowZ(layer[0]):
                newchunk = chunk.copy()
                newchunk.clampZ(layer[1])
                chunklayers.append(newchunk)

    if o.first_down:
        chunklayers = utils.sortChunks(chunklayers, o)

    chunksToMesh(chunklayers, o)
예제 #4
0
def cutout(o):
    if o.straight:
        join = 2
    else:
        join = 1
    print('operation: cutout')
    offset = True
    if o.cut_type == 'ONLINE' and o.onlycurves == True:  # is separate to allow open curves :)
        print('separate')
        chunksFromCurve = []
        for ob in o.objects:
            chunksFromCurve.extend(curveToChunks(ob, o.use_modifiers))
        for ch in chunksFromCurve:
            # print(ch.points)

            if len(ch.points) > 2:
                ch.poly = chunkToShapely(ch)

    # p.addContour(ch.poly)
    else:
        chunksFromCurve = []
        if o.cut_type == 'ONLINE':
            p = utils.getObjectOutline(0, o, True)

        else:
            offset = True
            if o.cut_type == 'INSIDE':
                offset = False

            p = utils.getObjectOutline(o.cutter_diameter / 2, o, offset)
            if o.outlines_count > 1:
                for i in range(1, o.outlines_count):
                    chunksFromCurve.extend(shapelyToChunks(p, -1))
                    p = p.buffer(distance=o.dist_between_paths * offset,
                                 resolution=o.circle_detail,
                                 join_style=join,
                                 mitre_limit=2)

        chunksFromCurve.extend(shapelyToChunks(p, -1))
        if o.outlines_count > 1 and o.movement_insideout == 'OUTSIDEIN':
            chunksFromCurve.reverse()

    # parentChildPoly(chunksFromCurve,chunksFromCurve,o)
    chunksFromCurve = limitChunks(chunksFromCurve, o)
    if not o.dont_merge:
        parentChildPoly(chunksFromCurve, chunksFromCurve, o)
    if o.outlines_count == 1:
        chunksFromCurve = utils.sortChunks(chunksFromCurve, o)

    # if o.outlines_count>0 and o.cut_type!='ONLINE' and o.movement_insideout=='OUTSIDEIN':#reversing just with more outlines
    #	chunksFromCurve.reverse()

    if (o.movement_type == 'CLIMB' and o.spindle_rotation_direction
            == 'CCW') or (o.movement_type == 'CONVENTIONAL'
                          and o.spindle_rotation_direction == 'CW'):
        for ch in chunksFromCurve:
            ch.points.reverse()

    if o.cut_type == 'INSIDE':  # there would bee too many conditions above, so for now it gets reversed once again when inside cutting.
        for ch in chunksFromCurve:
            ch.points.reverse()

    layers = getLayers(o, o.maxz, checkminz(o))
    extendorder = []

    if o.first_down:  # each shape gets either cut all the way to bottom, or every shape gets cut 1 layer, then all again. has to create copies, because same chunks are worked with on more layers usually
        for chunk in chunksFromCurve:
            dir_switch = False  # needed to avoid unnecessary lifting of cutter with open chunks and movement set to "MEANDER"
            for layer in layers:
                chunk_copy = chunk.copy()
                if dir_switch:
                    chunk_copy.points.reverse()
                extendorder.append([chunk_copy, layer])
                if (not chunk.closed) and o.movement_type == "MEANDER":
                    dir_switch = not dir_switch
    else:
        for layer in layers:
            for chunk in chunksFromCurve:
                extendorder.append([chunk.copy(), layer])

    for chl in extendorder:  # Set Z for all chunks
        chunk = chl[0]
        layer = chl[1]
        print(layer[1])
        chunk.setZ(layer[1])

    chunks = []

    if o.use_bridges:  # add bridges to chunks
        # bridges=getBridges(p,o)
        print('using bridges')

        bridgeheight = min(o.max.z, o.min.z + abs(o.bridges_height))

        for chl in extendorder:
            chunk = chl[0]
            layer = chl[1]
            if layer[1] < bridgeheight:
                bridges.useBridges(chunk, o)

    if o.profile_start > 0:
        print("cutout change profile start")
        for chl in extendorder:
            chunk = chl[0]
            if chunk.closed:
                chunk.changePathStart(o)

    ## Lead in
    if o.lead_in > 0.0 or o.lead_out > 0:
        print("cutout leadin")
        for chl in extendorder:
            chunk = chl[0]
            if chunk.closed:
                chunk.breakPathForLeadinLeadout(o)
                chunk.leadContour(o)

    if o.ramp:  # add ramps or simply add chunks
        for chl in extendorder:
            chunk = chl[0]
            layer = chl[1]
            if chunk.closed:
                chunk.rampContour(layer[0], layer[1], o)
                chunks.append(chunk)
            else:
                chunk.rampZigZag(layer[0], layer[1], o)
                chunks.append(chunk)
    else:
        for chl in extendorder:
            chunks.append(chl[0])

    chunksToMesh(chunks, o)
예제 #5
0
def drill(o):
    print('operation: Drill')
    chunks = []
    for ob in o.objects:
        activate(ob)

        bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={
            "linked": False,
            "mode": 'TRANSLATION'
        },
                                      TRANSFORM_OT_translate={
                                          "value": (0, 0, 0),
                                          "constraint_axis":
                                          (False, False, False),
                                          "orient_type": 'GLOBAL',
                                          "mirror": False,
                                          "use_proportional_edit": False,
                                          "proportional_edit_falloff":
                                          'SMOOTH',
                                          "proportional_size": 1,
                                          "snap": False,
                                          "snap_target": 'CLOSEST',
                                          "snap_point": (0, 0, 0),
                                          "snap_align": False,
                                          "snap_normal": (0, 0, 0),
                                          "texture_space": False,
                                          "release_confirm": False
                                      })
        # bpy.ops.collection.objects_remove_all()
        bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')

        ob = bpy.context.active_object
        if ob.type == 'CURVE':
            ob.data.dimensions = '3D'
        try:
            bpy.ops.object.transform_apply(location=True,
                                           rotation=False,
                                           scale=False)
            bpy.ops.object.transform_apply(location=False,
                                           rotation=True,
                                           scale=False)
            bpy.ops.object.transform_apply(location=False,
                                           rotation=False,
                                           scale=True)

        except:
            pass
        l = ob.location

        if ob.type == 'CURVE':

            for c in ob.data.splines:
                maxx, minx, maxy, miny, maxz, minz = -10000, 10000, -10000, 10000, -10000, 10000
                for p in c.points:
                    if o.drill_type == 'ALL_POINTS':
                        chunks.append(
                            camPathChunk([(p.co.x + l.x, p.co.y + l.y,
                                           p.co.z + l.z)]))
                    minx = min(p.co.x, minx)
                    maxx = max(p.co.x, maxx)
                    miny = min(p.co.y, miny)
                    maxy = max(p.co.y, maxy)
                    minz = min(p.co.z, minz)
                    maxz = max(p.co.z, maxz)
                for p in c.bezier_points:
                    if o.drill_type == 'ALL_POINTS':
                        chunks.append(
                            camPathChunk([(p.co.x + l.x, p.co.y + l.y,
                                           p.co.z + l.z)]))
                    minx = min(p.co.x, minx)
                    maxx = max(p.co.x, maxx)
                    miny = min(p.co.y, miny)
                    maxy = max(p.co.y, maxy)
                    minz = min(p.co.z, minz)
                    maxz = max(p.co.z, maxz)
                cx = (maxx + minx) / 2
                cy = (maxy + miny) / 2
                cz = (maxz + minz) / 2

                center = (cx, cy)
                aspect = (maxx - minx) / (maxy - miny)
                if (1.3 > aspect > 0.7 and o.drill_type
                        == 'MIDDLE_SYMETRIC') or o.drill_type == 'MIDDLE_ALL':
                    chunks.append(
                        camPathChunk([(center[0] + l.x, center[1] + l.y,
                                       cz + l.z)]))

        elif ob.type == 'MESH':
            for v in ob.data.vertices:
                chunks.append(
                    camPathChunk([(v.co.x + l.x, v.co.y + l.y, v.co.z + l.z)]))
        delob(ob)  # delete temporary object with applied transforms

    layers = getLayers(o, o.maxz, checkminz(o))

    chunklayers = []
    for layer in layers:
        for chunk in chunks:
            # If using object for minz then use z from points in object
            if o.minz_from_ob:
                z = chunk.points[0][2]
            else:  # using operation minz
                z = o.minz
            # only add a chunk layer if the chunk z point is in or lower than the layer
            if z <= layer[0]:
                if z <= layer[1]:
                    z = layer[1]
                # perform peck drill
                newchunk = chunk.copy()
                newchunk.setZ(z)
                chunklayers.append(newchunk)
                # retract tool to maxz (operation depth start in ui)
                newchunk = chunk.copy()
                newchunk.setZ(o.maxz)
                chunklayers.append(newchunk)

    chunklayers = utils.sortChunks(chunklayers, o)
    chunksToMesh(chunklayers, o)
예제 #6
0
def pocket(o):
    print('operation: pocket')
    p = utils.getObjectOutline(o.cutter_diameter / 2, o, False)
    approxn = (min(o.max.x - o.min.x, o.max.y - o.min.y) /
               o.dist_between_paths) / 2
    print("approximative:" + str(approxn))
    i = 0
    chunks = []
    chunksFromCurve = []
    lastchunks = []
    centers = None
    firstoutline = p  # for testing in the end.
    prest = p.buffer(-o.cutter_diameter / 2, o.circle_detail)
    while not p.is_empty:
        nchunks = shapelyToChunks(p, o.min.z)
        #print("nchunks")
        pnew = p.buffer(-o.dist_between_paths, o.circle_detail)
        #print("pnew")

        # caused a bad slow down
        #        if o.dist_between_paths > o.cutter_diameter / 2.0:
        #            prest = prest.difference(pnew.boundary.buffer(o.cutter_diameter / 2, o.circle_detail))
        #            if not (pnew.contains(prest)):
        #                prest = shapelyToMultipolygon(prest)
        #                fine = []
        #                go = []
        #                for p1 in prest:
        #                    if pnew.contains(p1):
        #                        fine.append(p1)
        #                    else:
        #                        go.append(p1)
        #                if len(go) > 0:
        #                    for p1 in go:
        #                        nchunks1 = shapelyToChunks(p1, o.min.z)
        #                        nchunks.extend(nchunks1)
        #                        prest = sgeometry.MultiPolygon(fine)

        nchunks = limitChunks(nchunks, o)
        chunksFromCurve.extend(nchunks)
        parentChildDist(lastchunks, nchunks, o)
        lastchunks = nchunks

        percent = int(i / approxn * 100)
        progress('outlining polygons ', percent)
        p = pnew

        i += 1

    # if (o.poc)#TODO inside outside!
    if (o.movement_type == 'CLIMB' and o.spindle_rotation_direction
            == 'CW') or (o.movement_type == 'CONVENTIONAL'
                         and o.spindle_rotation_direction == 'CCW'):
        for ch in chunksFromCurve:
            ch.points.reverse()

    # if bpy.app.debug_value==1:

    chunksFromCurve = utils.sortChunks(chunksFromCurve, o)

    chunks = []
    layers = getLayers(o, o.maxz, checkminz(o))

    # print(layers)
    # print(chunksFromCurve)
    # print(len(chunksFromCurve))
    for l in layers:
        lchunks = setChunksZ(chunksFromCurve, l[1])
        if o.ramp:
            for ch in lchunks:
                ch.zstart = l[0]
                ch.zend = l[1]

        ###########helix_enter first try here TODO: check if helix radius is not out of operation area.
        if o.helix_enter:
            helix_radius = o.cutter_diameter * 0.5 * o.helix_diameter * 0.01  # 90 percent of cutter radius
            helix_circumference = helix_radius * pi * 2

            revheight = helix_circumference * tan(o.ramp_in_angle)
            for chi, ch in enumerate(lchunks):
                if chunksFromCurve[chi].children == []:
                    p = ch.points[
                        0]  # TODO:intercept closest next point when it should stay low
                    # first thing to do is to check if helix enter can really enter.
                    checkc = Circle(helix_radius + o.cutter_diameter / 2,
                                    o.circle_detail)
                    checkc = affinity.translate(checkc, p[0], p[1])
                    covers = False
                    for poly in o.silhouete:
                        if poly.contains(checkc):
                            covers = True
                            break

                    if covers:
                        revolutions = (l[0] - p[2]) / revheight
                        # print(revolutions)
                        h = Helix(helix_radius, o.circle_detail, l[0], p,
                                  revolutions)
                        # invert helix if not the typical direction
                        if (o.movement_type == 'CONVENTIONAL'
                                and o.spindle_rotation_direction == 'CW') or (
                                    o.movement_type == 'CLIMB'
                                    and o.spindle_rotation_direction == 'CCW'):
                            nhelix = []
                            for v in h:
                                nhelix.append((2 * p[0] - v[0], v[1], v[2]))
                            h = nhelix
                        ch.points = h + ch.points
                    else:
                        o.warnings = o.warnings + 'Helix entry did not fit! \n '
                        ch.closed = True
                        ch.rampZigZag(l[0], l[1], o)
        # Arc retract here first try:
        if o.retract_tangential:  # TODO: check for entry and exit point before actual computing... will be much better.
            # TODO: fix this for CW and CCW!
            for chi, ch in enumerate(lchunks):
                # print(chunksFromCurve[chi])
                # print(chunksFromCurve[chi].parents)
                if chunksFromCurve[chi].parents == [] or len(
                        chunksFromCurve[chi].parents) == 1:

                    revolutions = 0.25
                    v1 = Vector(ch.points[-1])
                    i = -2
                    v2 = Vector(ch.points[i])
                    v = v1 - v2
                    while v.length == 0:
                        i = i - 1
                        v2 = Vector(ch.points[i])
                        v = v1 - v2

                    v.normalize()
                    rotangle = Vector((v.x, v.y)).angle_signed(Vector((1, 0)))
                    e = Euler((0, 0, pi / 2.0))  # TODO:#CW CLIMB!
                    v.rotate(e)
                    p = v1 + v * o.retract_radius
                    center = p
                    p = (p.x, p.y, p.z)

                    # progress(str((v1,v,p)))
                    h = Helix(o.retract_radius, o.circle_detail,
                              p[2] + o.retract_height, p, revolutions)

                    e = Euler(
                        (0, 0,
                         rotangle + pi))  # angle to rotate whole retract move
                    rothelix = []
                    c = []  # polygon for outlining and checking collisions.
                    for p in h:  # rotate helix to go from tangent of vector
                        v1 = Vector(p)

                        v = v1 - center
                        v.x = -v.x  # flip it here first...
                        v.rotate(e)
                        p = center + v
                        rothelix.append(p)
                        c.append((p[0], p[1]))

                    c = sgeometry.Polygon(c)
                    # print('çoutline')
                    # print(c)
                    coutline = c.buffer(o.cutter_diameter / 2, o.circle_detail)
                    # print(h)
                    # print('çoutline')
                    # print(coutline)
                    # polyToMesh(coutline,0)
                    rothelix.reverse()

                    covers = False
                    for poly in o.silhouete:
                        if poly.contains(coutline):
                            covers = True
                            break

                    if covers:
                        ch.points.extend(rothelix)

        chunks.extend(lchunks)

    if o.ramp:
        for ch in chunks:
            ch.rampZigZag(ch.zstart, ch.points[0][2], o)

    if o.first_down:
        chunks = utils.sortChunks(chunks, o)

    chunksToMesh(chunks, o)
예제 #7
0
def medial_axis(o):
    print('operation: Medial Axis')

    simple.remove_multiple("medialMesh")

    from cam.voronoi import Site, computeVoronoiDiagram

    chunks = []

    gpoly = spolygon.Polygon()
    angle = o.cutter_tip_angle
    slope = math.tan(math.pi * (90 - angle / 2) / 180)  # angle in degrees
    # slope = math.tan((math.pi-angle)/2) #angle in radian
    new_cutter_diameter = o.cutter_diameter
    m_o_name = o.object_name
    if o.cutter_type == 'VCARVE':
        angle = o.cutter_tip_angle
        # start the max depth calc from the "start depth" of the operation.
        maxdepth = o.maxz - slope * o.cutter_diameter / 2
        # don't cut any deeper than the "end depth" of the operation.
        if maxdepth < o.minz:
            maxdepth = o.minz
            # the effective cutter diameter can be reduced from it's max
            # since we will be cutting shallower than the original maxdepth
            # without this, the curve is calculated as if the diameter was at the original maxdepth and we get the bit
            # pulling away from the desired cut surface
            new_cutter_diameter = (maxdepth - o.maxz) / (-slope) * 2
    elif o.cutter_type == 'BALLNOSE':
        maxdepth = -new_cutter_diameter / 2
    else:
        o.warnings += 'Only Ballnose, Ball and V-carve cutters\n are supported'
        return
    # remember resolutions of curves, to refine them,
    # otherwise medial axis computation yields too many branches in curved parts
    resolutions_before = []

    for ob in o.objects:
        if ob.type == 'CURVE' or ob.type == 'FONT':
            resolutions_before.append(ob.data.resolution_u)
            if ob.data.resolution_u < 64:
                ob.data.resolution_u = 64

    polys = utils.getOperationSilhouete(o)
    mpoly = sgeometry.shape(polys)
    mpoly_boundary = mpoly.boundary
    ipol = 0
    for poly in polys.geoms:
        ipol = ipol + 1
        print("polygon:", ipol)
        schunks = shapelyToChunks(poly, -1)
        schunks = chunksRefineThreshold(
            schunks, o.medial_axis_subdivision,
            o.medial_axis_threshold)  # chunksRefine(schunks,o)

        verts = []
        for ch in schunks:
            for pt in ch.points:
                # pvoro = Site(pt[0], pt[1])
                verts.append(pt)  # (pt[0], pt[1]), pt[2])
        # verts= points#[[vert.x, vert.y, vert.z] for vert in vertsPts]
        nDupli, nZcolinear = unique(verts)
        nVerts = len(verts)
        print(str(nDupli) + " duplicates points ignored")
        print(str(nZcolinear) + " z colinear points excluded")
        if nVerts < 3:
            print("Not enough points")
            return {'FINISHED'}
        # Check colinear
        xValues = [pt[0] for pt in verts]
        yValues = [pt[1] for pt in verts]
        if checkEqual(xValues) or checkEqual(yValues):
            print("Points are colinear")
            return {'FINISHED'}
        # Create diagram
        print("Tesselation... (" + str(nVerts) + " points)")
        xbuff, ybuff = 5, 5  # %
        zPosition = 0
        vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts]
        # vertsPts= [Point(vert[0], vert[1]) for vert in verts]

        pts, edgesIdx = computeVoronoiDiagram(vertsPts,
                                              xbuff,
                                              ybuff,
                                              polygonsOutput=False,
                                              formatOutput=True)

        # pts=[[pt[0], pt[1], zPosition] for pt in pts]
        newIdx = 0
        vertr = []
        filteredPts = []
        print('filter points')
        ipts = 0
        for p in pts:
            ipts = ipts + 1
            if ipts % 500 == 0:
                sys.stdout.write('\r')
                # the exact output you're looking for:
                prog_message = "points: " + str(ipts) + " / " + str(
                    len(pts)) + " " + str(round(100 * ipts / len(pts))) + "%"
                sys.stdout.write(prog_message)
                sys.stdout.flush()

            if not poly.contains(sgeometry.Point(p)):
                vertr.append((True, -1))
            else:
                vertr.append((False, newIdx))
                if o.cutter_type == 'VCARVE':
                    # start the z depth calc from the "start depth" of the operation.
                    z = o.maxz - mpoly.boundary.distance(
                        sgeometry.Point(p)) * slope
                    if z < maxdepth:
                        z = maxdepth
                elif o.cutter_type == 'BALL' or o.cutter_type == 'BALLNOSE':
                    d = mpoly_boundary.distance(sgeometry.Point(p))
                    r = new_cutter_diameter / 2.0
                    if d >= r:
                        z = -r
                    else:
                        # print(r, d)
                        z = -r + sqrt(r * r - d * d)
                else:
                    z = 0  #
                # print(mpoly.distance(sgeometry.Point(0,0)))
                # if(z!=0):print(z)
                filteredPts.append((p[0], p[1], z))
                newIdx += 1

        print('filter edges')
        filteredEdgs = []
        ledges = []
        for e in edgesIdx:
            do = True
            # p1 = pts[e[0]]
            # p2 = pts[e[1]]
            # print(p1,p2,len(vertr))
            if vertr[e[0]][0]:  # exclude edges with allready excluded points
                do = False
            elif vertr[e[1]][0]:
                do = False
            if do:
                filteredEdgs.append((vertr[e[0]][1], vertr[e[1]][1]))
                ledges.append(
                    sgeometry.LineString((filteredPts[vertr[e[0]][1]],
                                          filteredPts[vertr[e[1]][1]])))
        # print(ledges[-1].has_z)

        bufpoly = poly.buffer(-new_cutter_diameter / 2, resolution=64)

        lines = shapely.ops.linemerge(ledges)
        # print(lines.type)

        if bufpoly.type == 'Polygon' or bufpoly.type == 'MultiPolygon':
            lines = lines.difference(bufpoly)
            chunks.extend(shapelyToChunks(bufpoly, maxdepth))
        chunks.extend(shapelyToChunks(lines, 0))

        # generate a mesh from the medial calculations
        if o.add_mesh_for_medial:
            polygon_utils_cam.shapelyToCurve('medialMesh', lines, 0.0)
            bpy.ops.object.convert(target='MESH')

    oi = 0
    for ob in o.objects:
        if ob.type == 'CURVE' or ob.type == 'FONT':
            ob.data.resolution_u = resolutions_before[oi]
            oi += 1

    # bpy.ops.object.join()
    chunks = utils.sortChunks(chunks, o)

    layers = getLayers(o, o.maxz, o.min.z)

    chunklayers = []

    for layer in layers:
        for chunk in chunks:
            if chunk.isbelowZ(layer[0]):
                newchunk = chunk.copy()
                newchunk.clampZ(layer[1])
                chunklayers.append(newchunk)

    if o.first_down:
        chunklayers = utils.sortChunks(chunklayers, o)

    if o.add_mesh_for_medial:  # make curve instead of a path
        simple.joinMultiple("medialMesh")

    chunksToMesh(chunklayers, o)
    # add pocket operation for medial if add pocket checked
    if o.add_pocket_for_medial:
        #        o.add_pocket_for_medial = False
        # export medial axis parameter to pocket op
        ops.Add_Pocket(None, maxdepth, m_o_name, new_cutter_diameter)