def execute(self, context): selected = context.selected_objects # save original selected items simple.remove_multiple('intarsion_') for ob in selected: ob.select_set(True) # select original curves # Perimeter cut largen then intarsion pocket externally, optional diam = self.diameter * 1.05 + self.backlight * 2 # make the diameter 5% larger and compensate for backlight utils.silhoueteOffset(context, -diam / 2) o1 = bpy.context.active_object utils.silhoueteOffset(context, diam) o2 = bpy.context.active_object utils.silhoueteOffset(context, -diam / 2) o3 = bpy.context.active_object o1.select_set(True) o2.select_set(True) o3.select_set(False) bpy.ops.object.delete( use_global=False) # delete o1 and o2 temporary working curves o3.name = "intarsion_pocket" # this is the pocket for intarsion bpy.context.object.location[2] = -self.intarsion_thickness if self.perimeter_cut > 0.0: utils.silhoueteOffset(context, self.perimeter_cut) bpy.context.active_object.name = "intarsion_perimeter" bpy.context.object.location[2] = -self.base_thickness bpy.ops.object.select_all(action='DESELECT') # deselect new curve o3.select_set(True) context.view_layer.objects.active = o3 # intarsion profile is the inside piece of the intarsion utils.silhoueteOffset(context, -self.tolerance / 2) # make smaller curve for material profile bpy.context.object.location[2] = self.intarsion_thickness o4 = bpy.context.active_object bpy.context.active_object.name = "intarsion_profil" o4.select_set(False) if self.backlight > 0.0: # Make a smaller curve for backlighting purposes utils.silhoueteOffset(context, (-self.tolerance / 2) - self.backlight) bpy.context.active_object.name = "intarsion_backlight" bpy.context.object.location[ 2] = -self.backlight_depth_from_top - self.intarsion_thickness o4.select_set(True) o3.select_set(True) return {'FINISHED'}
def t(length, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, combination='MF', base_gender='M', corner=False): if corner: if combination == 'MF': base_gender = 'M' combination = 'f' elif combination == 'F': base_gender = 'F' combination = 'f' elif combination == 'M': base_gender = 'M' combination = 'm' bar(length, thick, diameter, tolerance, amount=amount, stem=stem, twist=twist, tneck=tneck, tthick=tthick, which=base_gender) simple.active_name('tmp') fingers(diameter, tolerance, amount=amount, stem=stem) if combination == 'MF' or combination == 'M' or combination == 'm': simple.make_active('fingers') simple.move(y=thick / 2) simple.duplicate() simple.active_name('tmp') simple.union('tmp') if combination == 'M': simple.make_active('fingers') simple.mirrory() simple.active_name('tmp') simple.union('tmp') if combination == 'MF' or combination == 'F' or combination == 'f': simple.make_active('receptacle') simple.move(y=-thick / 2) simple.duplicate() simple.active_name('tmp') simple.difference('tmp', 'tmp') if combination == 'F': simple.make_active('receptacle') simple.mirrory() simple.active_name('tmp') simple.difference('tmp', 'tmp') simple.remove_multiple('receptacle') simple.remove_multiple('fingers') simple.rename('tmp', 't') simple.make_active('t')
def tile(diameter, tolerance, tile_x_amount, tile_y_amount, stem=1): global DT diameter = diameter * DT # diameter * DT * (2 + stem - 1) (4 + 2 * (stem - 1)) * diameter width = (tile_x_amount) * (4 + 2 * (stem - 1)) * diameter + diameter height = (tile_y_amount) * (4 + 2 * (stem - 1)) * diameter + diameter print('size:', width, height) fingers(diameter, tolerance, amount=tile_x_amount+2, stem=stem) simple.add_rectangle(width, height) simple.active_name('_base') simple.make_active('fingers') simple.active_name('_fingers') simple.intersect('_') simple.remove_multiple('_fingers') simple.rename('intersection', '_fingers') simple.move(y=height/2) simple.union('_') simple.active_name('_base') simple.remove_doubles() simple.rename('receptacle', '_receptacle') simple.move(y=-height/2) simple.difference('_', '_base') simple.active_name('base') fingers(diameter, tolerance, amount=tile_y_amount, stem=stem) simple.rename('base', '_base') simple.remove_doubles() simple.rename('fingers', '_fingers') simple.rotate(math.pi/2) simple.move(x=-width/2) simple.union('_') simple.active_name('_base') simple.rename('receptacle', '_receptacle') simple.rotate(math.pi/2) simple.move(x=width/2) simple.difference('_', '_base') simple.active_name('tile_ ' + str(tile_x_amount) + '_' + str(tile_y_amount))
def bar(width, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, twist_keep=False, twist_line=False, twist_line_amount=2, which='MF'): # width = length of the bar # thick = thickness of the bar # diameter = diameter of the tool for joint creation # tolerance = Tolerance in the joint # amount = amount of fingers in the joint 0 means auto generate # stem = amount of radius the stem or neck of the joint will have # twist = twist lock addition # tneck = percentage the twist neck will have compared to thick # tthick = thicknest of the twist material # Which M,F, MF, MM, FF global DT if amount == 0: amount = round(thick / ((4 + 2 * (stem - 1)) * diameter * DT)) - 1 bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=width, Simple_length=thick, use_cyclic_u=True, edit_mode=False) simple.active_name('tmprect') fingers(diameter, tolerance, amount, stem=stem) if which == 'MM' or which == 'M' or which == 'MF': simple.rename('fingers', '_tmpfingers') simple.rotate(-math.pi / 2) simple.move(x=width / 2) simple.rename('tmprect', '_tmprect') simple.union('_tmp') simple.active_name("tmprect") twistm('tmprect', thick, diameter, tolerance, twist, tneck, tthick, -math.pi / 2, x=width / 2, twist_keep=twist_keep) twistf('receptacle', thick, diameter, tolerance, twist, tneck, tthick, twist_keep=twist_keep) simple.rename('receptacle', '_tmpreceptacle') if which == 'FF' or which == 'F' or which == 'MF': simple.rotate(-math.pi / 2) simple.move(x=-width / 2) simple.rename('tmprect', '_tmprect') simple.difference('_tmp', '_tmprect') simple.active_name("tmprect") if twist_keep: simple.make_active('twist_keep_f') simple.rotate(-math.pi / 2) simple.move(x=-width / 2) simple.remove_multiple("_") # Remove temporary base and holes simple.remove_multiple("fingers") # Remove temporary base and holes if twist_line: joinery.twist_line(thick, tthick, tolerance, tneck, twist_line_amount, width) if twist_keep: simple.duplicate() simple.active_name('tmptwist') simple.difference('tmp', 'tmprect') simple.rename('tmprect', 'Puzzle_bar') simple.remove_multiple("tmp") # Remove temporary base and holes simple.make_active('Puzzle_bar')
def create_flex_side(length, height, finger_thick, top_bottom=False): # assumes the base fingers were created and exist # crates a flex side for mortise on curve # length = length of curve # height = height of side # finger_length = lenght of finger or mortise # finger_thick = finger thickness or thickness of material # finger_tol = Play for finger 0 is very tight # top_bottom = fingers on top and bottom if true, just on bottom if false # flex_pocket = width of pocket on the flex side. This is for kerf bending. if top_bottom: fingers = finger_pair("base", 0, height - finger_thick) else: simple.make_active("base") fingers = bpy.context.active_object bpy.ops.transform.translate(value=(0.0, height / 2 - finger_thick / 2 + 0.0003, 0.0)) bpy.ops.curve.simple(align='WORLD', location=(length / 2 + 0.00025, 0, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=length, Simple_length=height, shape='3D', outputType='POLY', use_cyclic_u=True, handleType='AUTO', edit_mode=False) simple.active_name("_side") simple.make_active('_side') fingers.select_set(True) bpy.ops.object.curve_boolean(boolean_type='DIFFERENCE') simple.active_name("side") simple.remove_multiple('_') simple.remove_multiple('base')
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 mitre(length, thick, angle, angleb, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, which='MF'): # length is the total width of the segments including 2 * radius and thick # radius = radius of the curve # thick = thickness of the bar # angle = angle of the female part # angleb = angle of the male part # diameter = diameter of the tool for joint creation # tolerance = Tolerance in the joint # amount = amount of fingers in the joint 0 means auto generate # stem = amount of radius the stem or neck of the joint will have # twist = twist lock addition # tneck = percentage the twist neck will have compared to thick # tthick = thicknest of the twist material # which = which joint to generate, Male Female MaleFemale M, F, MF # generate base rectangle bpy.ops.curve.simple(align='WORLD', location=(0, -thick / 2, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=length * 1.005 + 4 * thick, Simple_length=thick, use_cyclic_u=True, edit_mode=False, shape='3D') simple.active_name("tmprect") # generate cutout shapes bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=4 * thick, Simple_length=6 * thick, use_cyclic_u=True, edit_mode=False, shape='3D') simple.move(x=2 * thick) simple.rotate(angle) simple.move(x=length / 2) simple.active_name('tmpmitreright') bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=4 * thick, Simple_length=6 * thick, use_cyclic_u=True, edit_mode=False, shape='3D') simple.move(x=2 * thick) simple.rotate(angleb) simple.move(x=length / 2) simple.mirrorx() simple.active_name('tmpmitreleft') simple.difference('tmp', 'tmprect') simple.make_active('tmprect') fingers(diameter, tolerance, amount, stem=stem) # Generate male section and join to the base if which == 'M' or which == 'MF': simple.make_active('fingers') simple.duplicate() simple.active_name('tmpfingers') simple.rotate(angle - math.pi / 2) h = thick / math.cos(angle) h /= 2 simple.move(x=length / 2 + h * math.sin(angle), y=-thick / 2) if which == 'M': simple.rename('fingers', 'tmpfingers') simple.rotate(angleb - math.pi / 2) h = thick / math.cos(angleb) h /= 2 simple.move(x=length / 2 + h * math.sin(angleb), y=-thick / 2) simple.mirrorx() simple.union('tmp') simple.active_name('tmprect') # Generate female section and join to base if which == 'MF' or which == 'F': simple.make_active('receptacle') simple.mirrory() simple.duplicate() simple.active_name('tmpreceptacle') simple.rotate(angleb - math.pi / 2) h = thick / math.cos(angleb) h /= 2 simple.move(x=length / 2 + h * math.sin(angleb), y=-thick / 2) simple.mirrorx() if which == 'F': simple.rename('receptacle', 'tmpreceptacle2') simple.rotate(angle - math.pi / 2) h = thick / math.cos(angle) h /= 2 simple.move(x=length / 2 + h * math.sin(angle), y=-thick / 2) simple.difference('tmp', 'tmprect') simple.remove_multiple('receptacle') simple.remove_multiple('fingers') simple.rename('tmprect', 'mitre')
def arc(radius, thick, angle, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, twist_keep=False, which='MF'): # radius = radius of the curve # thick = thickness of the bar # angle = angle of the arc # diameter = diameter of the tool for joint creation # tolerance = Tolerance in the joint # amount = amount of fingers in the joint 0 means auto generate # stem = amount of radius the stem or neck of the joint will have # twist = twist lock addition # tneck = percentage the twist neck will have compared to thick # tthick = thicknest of the twist material # which = which joint to generate, Male Female MaleFemale M, F, MF global DT # diameter tolerance for diameter of finger creation if angle == 0: # angle cannot be 0 angle = 0.01 negative = False if angle < 0: # if angle < 0 then negative is true angle = -angle negative = True if amount == 0: amount = round(thick / ((4 + 2 * (stem - 1)) * diameter * DT)) - 1 fingers(diameter, tolerance, amount, stem=stem) twistf('receptacle', thick, diameter, tolerance, twist, tneck, tthick, twist_keep=twist_keep) # generate arc bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Segment', Simple_a=radius - thick / 2, Simple_b=radius + thick / 2, Simple_startangle=-0.0001, Simple_endangle=math.degrees(angle), Simple_radius=radius, use_cyclic_u=False, edit_mode=False) bpy.context.active_object.name = "tmparc" simple.rename('fingers', '_tmpfingers') simple.rotate(math.pi) simple.move(x=radius) bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') simple.rename('tmparc', '_tmparc') if which == 'MF' or which == 'M': simple.union('_tmp') simple.active_name("base") twistm('base', thick, diameter, tolerance, twist, tneck, tthick, math.pi, x=radius) simple.rename('base', '_tmparc') simple.rename('receptacle', '_tmpreceptacle') simple.mirrory() simple.move(x=radius) bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') simple.rotate(angle) simple.make_active('_tmparc') if which == 'MF' or which == 'F': simple.difference('_tmp', '_tmparc') bpy.context.active_object.name = "PUZZLE_arc" bpy.ops.object.curve_remove_doubles() simple.remove_multiple("_") # Remove temporary base and holes simple.make_active('PUZZLE_arc') if which == 'M': simple.rotate(-angle) simple.mirrory() bpy.ops.object.transform_apply(location=True, rotation=True, scale=False) simple.rotate(-math.pi / 2) simple.move(y=radius) simple.rename('PUZZLE_arc', 'PUZZLE_arc_male') elif which == 'F': simple.mirrorx() simple.move(x=radius) simple.rotate(math.pi / 2) simple.rename('PUZZLE_arc', 'PUZZLE_arc_receptacle') else: simple.move(x=-radius) # bpy.ops.object.transform_apply(location=True, rotation=False, scale=False, properties=False) # if negative: # mirror if angle is negative simple.mirrory()
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
def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, finger_tolerance, adaptive, base=False, double_adaptive=False): # distributes mortises of a fixed distance # dynamically changes the finger tolerance with the angle differences # loop = takes in a shapely shape # finger_size = size of the mortise # finger_thick = thickness of the material # finger_tolerance = minimum finger tolerance # adaptive = angle threshold to reduce finger size coords = list(loop.coords) old_mortise_angle = 0 distance = min_finger / 2 finger_sz = min_finger oldfinger_sz = min_finger hpos = [] # hpos is the horizontal positions of the middle of the mortise # slope_array(loop) print("joinery loop length", round(loop_length * 1000), "mm") for i, p in enumerate(coords): if i == 0: p_start = p if p != p_start: not_start = True else: not_start = False pd = loop.project(Point(p)) if not_start: while distance <= pd: mortise_angle = angle(oldp, p) mortise_angle_difference = abs(mortise_angle - old_mortise_angle) mad = (1 + 6 * min(mortise_angle_difference, math.pi / 4) / (math.pi / 4)) # factor for tolerance for the finger distance += mad * finger_tolerance # move finger by the factor mad greater with larger angle difference mortise_point = loop.interpolate(distance) if mad > 2 and double_adaptive: hpos.append(distance) # saves the mortise center hpos.append(distance + finger_sz) # saves the mortise center if base: mortise(finger_sz, finger_thick, finger_tolerance * mad, distance + finger_sz, 0, 0) simple.active_name("_base") else: mortise(finger_sz, finger_thick, finger_tolerance * mad, mortise_point.x, mortise_point.y, mortise_angle) if i == 1: # put a mesh cylinder at the first coordinates to indicate start simple.remove_multiple("start_here") bpy.ops.mesh.primitive_cylinder_add( radius=finger_thick / 2, depth=0.025, enter_editmode=False, align='WORLD', location=(mortise_point.x, mortise_point.y, 0), scale=(1, 1, 1)) simple.active_name("start_here_mortise") old_distance = distance old_mortise_point = mortise_point finger_sz = finger_size next_angle_difference = math.pi # adaptive finger length start while finger_sz > min_finger and next_angle_difference > adaptive: finger_sz *= 0.95 # reduce the size of finger by a percentage... the closer to 1.0, the slower distance = old_distance + 3 * oldfinger_sz / 2 + finger_sz / 2 mortise_point = loop.interpolate( distance) # get the next mortise point next_mortise_angle = angle( (old_mortise_point.x, old_mortise_point.y), (mortise_point.x, mortise_point.y)) # calculate next angle next_angle_difference = abs(next_mortise_angle - mortise_angle) oldfinger_sz = finger_sz old_mortise_angle = mortise_angle oldp = p if base: simple.join_multiple("_base") simple.active_name("base") else: print("placeholder") simple.join_multiple("_mort") simple.active_name("variable_mortise") return hpos