def execute(self, context):#this is almost same as getobjectoutline, just without the need of operation data ob=bpy.context.active_object self.silh=utils.getObjectSilhouete('OBJECTS', objects=bpy.context.selected_objects) bpy.context.scene.cursor_location=(0,0,0) #smp=sgeometry.asMultiPolygon(self.silh) for smp in self.silh: polygon_utils_cam.shapelyToCurve(ob.name+'_silhouette',smp,0)# #bpy.ops.object.convert(target='CURVE') bpy.context.scene.cursor_location=ob.location bpy.ops.object.origin_set(type='ORIGIN_CURSOR') return {'FINISHED'}
def sliceObject(ob): settings = bpy.context.scene.cam_slice layers = getSlices(ob, settings.slice_distance) # print(layers) sliceobjects = [] i = 1 for layer in layers: pi = 1 layerpolys = [] for slicechunk in layer: # these functions here are totally useless conversions, could generate slices more directly, just lazy to write new functions # print (slicechunk) nchp = [] for p in slicechunk: nchp.append((p[0], p[1])) # print(slicechunk) ch = chunk.camPathChunk(nchp) # print(ch) pslices = chunk.chunksToShapely([ch]) # print(pslices) for pslice in pslices: p = pslice # -p1 # print(p) text = '%i - %i' % (i, pi) bpy.ops.object.text_add() textob = bpy.context.active_object textob.data.size = 0.0035 textob.data.body = text textob.data.align = 'CENTER' # print(len(ch.points)) sliceobject = polygon_utils_cam.shapelyToCurve( 'slice', p, slicechunk[0][2]) textob.location = (0, 0, 0) textob.parent = sliceobject sliceobject.data.extrude = settings.slice_distance / 2 sliceobject.data.dimensions = '2D' sliceobjects.append(sliceobject) pi += 1 # FIXME: the polys on same layer which are hollow are not joined by now, this prevents doing hollow surfaces :( # for p in layerpolys: # for p1 in layerpolys: i += 1 for o in sliceobjects: o.select = True bpy.ops.group.create(name='slices')
def sliceObject(ob): settings = bpy.context.scene.cam_slice layers = getSlices(ob, settings.slice_distance) # print(layers) sliceobjects = [] i = 1 for layer in layers: pi = 1 layerpolys = [] for slicechunk in layer: # these functions here are totally useless conversions, could generate slices more directly, just lazy to write new functions # print (slicechunk) nchp = [] for p in slicechunk: nchp.append((p[0], p[1])) # print(slicechunk) ch = chunk.camPathChunk(nchp) # print(ch) pslices = chunk.chunksToShapely([ch]) # print(pslices) for pslice in pslices: p = pslice # -p1 # print(p) text = "%i - %i" % (i, pi) bpy.ops.object.text_add() textob = bpy.context.active_object textob.data.size = 0.0035 textob.data.body = text textob.data.align = "CENTER" # print(len(ch.points)) sliceobject = polygon_utils_cam.shapelyToCurve("slice", p, slicechunk[0][2]) textob.location = (0, 0, 0) textob.parent = sliceobject sliceobject.data.extrude = settings.slice_distance / 2 sliceobject.data.dimensions = "2D" sliceobjects.append(sliceobject) pi += 1 # FIXME: the polys on same layer which are hollow are not joined by now, this prevents doing hollow surfaces :( # for p in layerpolys: # for p1 in layerpolys: i += 1 for o in sliceobjects: o.select = True bpy.ops.group.create(name="slices")
def execute( self, context ): # this is almost same as getobjectoutline, just without the need of operation data ob = context.active_object if self.opencurve and ob.type == 'CURVE': bpy.ops.object.duplicate() obj = context.active_object bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) # apply all transforms bpy.context.object.data.resolution_u = 60 bpy.ops.object.convert(target='MESH') bpy.context.active_object.name = "temp_mesh" coords = [] for v in obj.data.vertices: # extract X,Y coordinates from the vertices data coords.append((v.co.x, v.co.y)) simple.remove_multiple('temp_mesh') # delete temporary mesh simple.remove_multiple('dilation') # delete old dilation objects line = LineString( coords ) # convert coordinates to shapely LineString datastructure print("line length=", round(line.length * 1000), 'mm') dilated = line.buffer( self.offset, cap_style=1, resolution=16, mitre_limit=self.mitrelimit) # use shapely to expand polygon_utils_cam.shapelyToCurve("dilation", dilated, 0) else: utils.silhoueteOffset(context, self.offset, int(self.style), self.mitrelimit) return {'FINISHED'}
def packCurves(): if speedups.available: speedups.enable() t = time.time() packsettings = bpy.context.scene.cam_pack sheetsizex = packsettings.sheet_x sheetsizey = packsettings.sheet_y direction = packsettings.sheet_fill_direction distance = packsettings.distance rotate = packsettings.rotate polyfield = [] # in this, position, rotation, and actual poly will be stored. for ob in bpy.context.selected_objects: allchunks = [] simple.activate(ob) bpy.ops.object.make_single_user(type='SELECTED_OBJECTS') bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') z = ob.location.z bpy.ops.object.location_clear() bpy.ops.object.rotation_clear() chunks = utils.curveToChunks(ob) npolys = utils.chunksToShapely(chunks) # add all polys in silh to one poly poly = shapely.ops.unary_union(npolys) poly = poly.buffer(distance / 1.5, 8) poly = poly.simplify(0.0003) polyfield.append([[0, 0], 0.0, poly, ob, z]) random.shuffle(polyfield) # primitive layout here: allpoly = prepared.prep(sgeometry.Polygon()) # main collision poly. # allpoly=sgeometry.Polygon()#main collision poly. shift = 0.0015 # one milimeter by now. rotchange = .3123456 # in radians xmin, ymin, xmax, ymax = polyfield[0][2].bounds if direction == 'X': mindist = -xmin else: mindist = -ymin i = 0 p = polyfield[0][2] placedpolys = [] rotcenter = sgeometry.Point(0, 0) for pf in polyfield: print(i) rot = 0 porig = pf[2] placed = False xmin, ymin, xmax, ymax = p.bounds # p.shift(-xmin,-ymin) if direction == 'X': x = mindist y = -ymin if direction == 'Y': x = -xmin y = mindist iter = 0 best = None hits = 0 besthit = None while not placed: # swap x and y, and add to x # print(x,y) p = porig if rotate: # ptrans=srotate(p,rot,0,0) ptrans = affinity.rotate(p, rot, origin=rotcenter, use_radians=True) # ptrans = translate(ptrans,x,y) ptrans = affinity.translate(ptrans, x, y) else: # ptrans = translate(p,x,y) ptrans = affinity.translate(p, x, y) xmin, ymin, xmax, ymax = ptrans.bounds # print(iter,p.bounds) if xmin > 0 and ymin > 0 and ( (direction == 'Y' and xmax < sheetsizex) or (direction == 'X' and ymax < sheetsizey)): if not allpoly.intersects(ptrans): # if allpoly.disjoint(ptrans): # print('gothit') # we do more good solutions, choose best out of them: hits += 1 if best == None: best = [x, y, rot, xmax, ymax] besthit = hits if direction == 'X': if xmax < best[3]: best = [x, y, rot, xmax, ymax] besthit = hits elif ymax < best[4]: best = [x, y, rot, xmax, ymax] besthit = hits if hits >= 15 or ( iter > 10000 and hits > 0): # here was originally more, but 90% of best solutions are still 1 placed = True pf[3].location.x = best[0] pf[3].location.y = best[1] pf[3].location.z = pf[4] pf[3].rotation_euler.z = best[2] pf[3].select_set(state=True) # print(mindist) mindist = mindist - 0.5 * (xmax - xmin) # print(mindist) # print(iter) # reset polygon to best position here: ptrans = affinity.rotate(porig, best[2], rotcenter, use_radians=True) # ptrans=srotate(porig,best[2],0,0) ptrans = affinity.translate(ptrans, best[0], best[1]) # ptrans = translate(ptrans,best[0],best[1]) # polygon_utils_cam.polyToMesh(p,0.1)#debug visualisation keep = [] print(best[0], best[1]) # print(len(ptrans.exterior)) # npoly=allpoly.union(ptrans) # for ci in range(0,len(allpoly)): # cminx,cmaxx,cminy,cmaxy=allpoly.boundingBox(ci) # if direction=='X' and cmaxx>mindist-.1: # npoly.addContour(allpoly[ci]) # if direction=='Y' and cmaxy>mindist-.1: # npoly.addContour(allpoly[ci]) # allpoly=npoly placedpolys.append(ptrans) allpoly = prepared.prep(sgeometry.MultiPolygon(placedpolys)) # *** temporary fix until prepared geometry code is setup properly # allpoly=sgeometry.MultiPolygon(placedpolys) # polygon_utils_cam.polyToMesh(allpoly,0.1)#debug visualisation # for c in p: # allpoly.addContour(c) # cleanup allpoly print(iter, hits, besthit) if not placed: if direction == 'Y': x += shift mindist = y if xmax + shift > sheetsizex: x = x - xmin y += shift if direction == 'X': y += shift mindist = x if ymax + shift > sheetsizey: y = y - ymin x += shift if rotate: rot += rotchange iter += 1 i += 1 t = time.time() - t polygon_utils_cam.shapelyToCurve('test', sgeometry.MultiPolygon(placedpolys), 0) print(t)
def packCurves(): if speedups.available: speedups.enable() t=time.time() packsettings=bpy.context.scene.cam_pack sheetsizex=packsettings.sheet_x sheetsizey=packsettings.sheet_y direction=packsettings.sheet_fill_direction distance=packsettings.distance rotate = packsettings.rotate polyfield=[]#in this, position, rotation, and actual poly will be stored. for ob in bpy.context.selected_objects: allchunks=[] simple.activate(ob) bpy.ops.object.make_single_user(type='SELECTED_OBJECTS') bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') z=ob.location.z bpy.ops.object.location_clear() bpy.ops.object.rotation_clear() chunks=utils.curveToChunks(ob) npolys=utils.chunksToShapely(chunks) #add all polys in silh to one poly poly=shapely.ops.unary_union(npolys) poly=poly.buffer(distance/1.5,8) poly=poly.simplify(0.0003) polyfield.append([[0,0],0.0,poly,ob,z]) random.shuffle(polyfield) #primitive layout here: allpoly=prepared.prep(sgeometry.Polygon())#main collision poly. #allpoly=sgeometry.Polygon()#main collision poly. shift=0.0015#one milimeter by now. rotchange=.3123456#in radians xmin,ymin,xmax,ymax=polyfield[0][2].bounds if direction=='X': mindist=-xmin else: mindist=-ymin i=0 p=polyfield[0][2] placedpolys=[] rotcenter=sgeometry.Point(0,0) for pf in polyfield: print(i) rot=0 porig=pf[2] placed=False xmin,ymin,xmax,ymax=p.bounds #p.shift(-xmin,-ymin) if direction=='X': x=mindist y=-ymin if direction=='Y': x=-xmin y=mindist iter=0 best=None hits=0 besthit=None while not placed: #swap x and y, and add to x #print(x,y) p=porig if rotate: #ptrans=srotate(p,rot,0,0) ptrans=affinity.rotate(p,rot,origin = rotcenter, use_radians=True) #ptrans = translate(ptrans,x,y) ptrans = affinity.translate(ptrans,x,y) else: #ptrans = translate(p,x,y) ptrans = affinity.translate(p,x,y) xmin,ymin,xmax,ymax=ptrans.bounds #print(iter,p.bounds) if xmin>0 and ymin>0 and ((direction=='Y' and xmax<sheetsizex) or (direction=='X' and ymax<sheetsizey)): if not allpoly.intersects(ptrans): #if allpoly.disjoint(ptrans): #print('gothit') #we do more good solutions, choose best out of them: hits+=1 if best==None: best=[x,y,rot,xmax,ymax] besthit=hits if direction=='X': if xmax<best[3]: best=[x,y,rot,xmax,ymax] besthit=hits elif ymax<best[4]: best=[x,y,rot,xmax,ymax] besthit=hits if hits>=15 or (iter>10000 and hits>0):#here was originally more, but 90% of best solutions are still 1 placed=True pf[3].location.x=best[0] pf[3].location.y=best[1] pf[3].location.z=pf[4] pf[3].rotation_euler.z=best[2] pf[3].select=True #print(mindist) mindist=mindist-0.5*(xmax-xmin) #print(mindist) #print(iter) #reset polygon to best position here: ptrans=affinity.rotate(porig,best[2],rotcenter, use_radians = True) #ptrans=srotate(porig,best[2],0,0) ptrans = affinity.translate(ptrans,best[0],best[1]) #ptrans = translate(ptrans,best[0],best[1]) #polygon_utils_cam.polyToMesh(p,0.1)#debug visualisation keep=[] print(best[0],best[1]) #print(len(ptrans.exterior)) #npoly=allpoly.union(ptrans) ''' for ci in range(0,len(allpoly)): cminx,cmaxx,cminy,cmaxy=allpoly.boundingBox(ci) if direction=='X' and cmaxx>mindist-.1: npoly.addContour(allpoly[ci]) if direction=='Y' and cmaxy>mindist-.1: npoly.addContour(allpoly[ci]) ''' #allpoly=npoly placedpolys.append(ptrans) allpoly=prepared.prep(sgeometry.MultiPolygon(placedpolys)) #*** temporary fix until prepared geometry code is setup properly #allpoly=sgeometry.MultiPolygon(placedpolys) #polygon_utils_cam.polyToMesh(allpoly,0.1)#debug visualisation #for c in p: # allpoly.addContour(c) #cleanup allpoly print(iter,hits,besthit) if not placed: if direction=='Y': x+=shift mindist=y if (xmax+shift>sheetsizex): x=x-xmin y+=shift if direction=='X': y+=shift mindist=x if (ymax+shift>sheetsizey): y=y-ymin x+=shift if rotate: rot+=rotchange iter+=1 i+=1 t=time.time()-t polygon_utils_cam.shapelyToCurve('test',sgeometry.MultiPolygon(placedpolys),0) print(t)
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)
def pocket(o): print('operation: pocket') scene = bpy.context.scene simple.remove_multiple("3D_poc") max_depth = checkminz(o) cutter_angle = math.radians(o.cutter_tip_angle / 2) c_offset = o.cutter_diameter / 2 if o.cutter_type == 'VCARVE': c_offset = -max_depth * math.tan(cutter_angle) elif o.cutter_type == 'CYLCONE': c_offset = -max_depth * math.tan(cutter_angle) + o.cylcone_diameter / 2 elif o.cutter_type == 'BALLCONE': c_offset = -max_depth * math.tan(cutter_angle) + o.ball_radius if c_offset > o.cutter_diameter / 2: c_offset = o.cutter_diameter / 2 p = utils.getObjectOutline(c_offset, 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)) print(o) i = 0 chunks = [] chunksFromCurve = [] lastchunks = [] centers = None firstoutline = p # for testing in the end. prest = p.buffer(-c_offset, o.circle_detail) while not p.is_empty: if o.pocketToCurve: polygon_utils_cam.shapelyToCurve( '3dpocket', p, 0.0) # make a curve starting with _3dpocket nchunks = shapelyToChunks(p, o.min.z) # print("nchunks") pnew = p.buffer(-o.dist_between_paths, o.circle_detail) # print("pnew") 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() chunksFromCurve = utils.sortChunks(chunksFromCurve, o) chunks = [] layers = getLayers(o, o.maxz, checkminz(o)) 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 = c_offset * 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 not 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 + c_offset, 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(c_offset, 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) if o.pocketToCurve: # make curve instead of a path simple.joinMultiple("3dpocket") else: chunksToMesh(chunks, o) # make normal pocket path