def dslope_array(loop, resolution=0.001): length = loop.length pnt_amount = round(length / resolution) sarray = [] dsarray = [] for i in range(pnt_amount): distance = i * resolution pt = loop.interpolate(distance) p = (pt.x, pt.y) if i != 0: slope = abs(angle(p, oldp)) sarray.append((distance, slope * -0.01)) oldp = p # derivative = LineString(sarray) for i, p in enumerate(sarray): distance = p[0] if i != 0: slope = find_slope(p, oldp) if abs(slope) > 10: print(distance) dsarray.append((distance, slope * -0.1)) oldp = p dderivative = LineString(dsarray) utils.shapelyToCurve('doublederivative', dderivative, 0.0) return sarray
def slope_array(loop): simple.remove_multiple("-") coords = list(loop.coords) # pnt_amount = round(length / resolution) sarray = [] dsarray = [] for i, p in enumerate(coords): distance = loop.project(Point(p)) if i != 0: slope = find_slope(p, oldp) sarray.append((distance, slope * -0.001)) oldp = p for i, p in enumerate(sarray): distance = p[0] if i != 0: slope = find_slope(p, oldp) if abs(slope) > 10: print(distance) dsarray.append((distance, slope * -0.00001)) oldp = p derivative = LineString(sarray) dderivative = LineString(dsarray) utils.shapelyToCurve('-derivative', derivative, 0.0) utils.shapelyToCurve('-doublederivative', dderivative, 0.0) return sarray
def rack(mm_per_tooth=0.01, number_of_teeth=11, height=0.012, pressure_angle=0.3488, backlash=0.0, hole_diameter=0.003175, tooth_per_hole=4): simple.deselect() pi = math.pi mm_per_tooth *= 1000 a = mm_per_tooth / pi # addendum t = (a * math.sin(pressure_angle) ) # tooth side is tilted so top/bottom corners move this amount a /= 1000 mm_per_tooth /= 1000 t /= 1000 shapely_gear = Polygon([(-mm_per_tooth * 2 / 4 * 1.001, a - height), (-mm_per_tooth * 2 / 4 * 1.001 - backlash, -a), (-mm_per_tooth * 1 / 4 + backlash - t, -a), (-mm_per_tooth * 1 / 4 + backlash + t, a), (mm_per_tooth * 1 / 4 - backlash - t, a), (mm_per_tooth * 1 / 4 - backlash + t, -a), (mm_per_tooth * 2 / 4 * 1.001 + backlash, -a), (mm_per_tooth * 2 / 4 * 1.001, a - height)]) utils.shapelyToCurve('_tooth', shapely_gear, 0.0) i = number_of_teeth while i > 1: simple.duplicate(x=mm_per_tooth) i -= 1 simple.union('_tooth') simple.move(y=height / 2) if hole_diameter > 0: bpy.ops.curve.simple(align='WORLD', location=(mm_per_tooth / 2, 0, 0), rotation=(0, 0, 0), Simple_Type='Circle', Simple_radius=hole_diameter / 2, shape='3D', use_cyclic_u=True, edit_mode=False) simple.active_name('_hole') distance = (number_of_teeth - 1) * mm_per_tooth while distance > tooth_per_hole * mm_per_tooth: simple.duplicate(x=tooth_per_hole * mm_per_tooth) distance -= tooth_per_hole * mm_per_tooth simple.difference('_', '_tooth') name = 'rack-' + str(round(mm_per_tooth * 1000, 1)) name += '-PA-' + str(round(math.degrees(pressure_angle), 1)) simple.active_name(name)
def open_curve(line, thick, diameter, tolerance, amount=0, stem=1, twist=False, t_neck=0.5, t_thick=0.01, twist_amount=1, which='MF', twist_keep=False): # puts puzzle connectors at the end of an open curve # optionally puts twist lock connectors at the puzzle connection # optionally puts twist lock connectors along the open curve # line = shapely linestring # 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 # twist_amount = twist amount distributed on the curve not counting the joint twist locks # tneck = percentage the twist neck will have compared to thick # tthick = thicknest of the twist material # Which M,F, MF, MM, FF coords = list(line.coords) start_angle = joinery.angle(coords[0], coords[1]) + math.pi/2 end_angle = joinery.angle(coords[-1], coords[-2]) + math.pi/2 p_start = coords[0] p_end = coords[-1] print('start angle', start_angle) print('end angle', end_angle) bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=thick*2, Simple_length=thick * 2, use_cyclic_u=True, edit_mode=False, shape='3D') simple.active_name('tmprect') simple.move(y=thick) simple.duplicate() simple.rotate(start_angle) simple.move(x=p_start[0], y=p_start[1]) simple.make_active('tmprect') simple.rotate(end_angle) simple.move(x=p_end[0], y=p_end[1]) simple.union('tmprect') dilated = line.buffer(thick/2) # expand shapely object to thickness utils.shapelyToCurve('tmp_curve', dilated, 0.0) simple.difference('tmp', 'tmp_curve') # truncate curve at both ends with the rectangles fingers(diameter, tolerance, amount, stem=stem) simple.make_active('fingers') simple.rotate(end_angle) simple.move(x=p_end[0], y=p_end[1]) simple.active_name('tmp_fingers') simple.union('tmp_') simple.active_name('tmp_curve') twistm('tmp_curve', thick, diameter, tolerance, twist, t_neck, t_thick, end_angle, x=p_end[0], y=p_end[1], twist_keep=twist_keep) twistf('receptacle', thick, diameter, tolerance, twist, t_neck, t_thick, twist_keep=twist_keep) simple.rename('receptacle', 'tmp') simple.rotate(start_angle+math.pi) simple.move(x=p_start[0], y=p_start[1]) simple.difference('tmp', 'tmp_curve') if twist_keep: simple.make_active('twist_keep_f') simple.rotate(start_angle + math.pi) simple.move(x=p_start[0], y=p_start[1]) if twist_amount > 0 and twist: twist_start = line.length / (twist_amount+1) joinery.distributed_interlock(line, line.length, thick, t_thick, tolerance, twist_amount, tangent=math.pi/2, fixed_angle=0, start=twist_start, end=twist_start, closed=False, type='TWIST', twist_percentage=t_neck) if twist_keep: simple.duplicate() simple.active_name('twist_keep') simple.join_multiple('twist_keep') simple.make_active('interlock') simple.active_name('tmp_twist') simple.difference('tmp', 'tmp_curve') simple.active_name('puzzle_curve')
def execute(self, context): o1 = bpy.context.active_object shapes = utils.curveToShapely(o1) negative_overcuts = [] positive_overcuts = [] # count all the corners including inside and out cornerCnt = 0 # a list of tuples for defining the inside corner # tuple is: (pos, v1, v2, angle, allCorners list index) insideCorners = [] diameter = self.diameter * 1.001 radius = diameter / 2 anglethreshold = math.pi - self.threshold centerv = Vector((0,0)) extendedv = Vector((0,0)) pos = Vector((0,0)) sign = -1 if self.do_invert else 1 isTBone = self.style == 'TBONE' # indexes in insideCorner tuple POS, V1, V2, A, IDX = range(5) def addOvercut(a): nonlocal pos, centerv, radius, extendedv, sign, negative_overcuts, positive_overcuts # move the overcut shape center position 1 radius in direction v pos -= centerv * radius if abs(a) < math.pi/2: shape = utils.Circle(radius, 64) shape = shapely.affinity.translate(shape, pos.x, pos.y) else: # elongate overcut circle to make sure tool bit can fit into slot p1 = pos + (extendedv * radius) l = shapely.geometry.LineString((pos, p1)) shape = l.buffer(radius, resolution = 64) if sign>0: negative_overcuts.append(shape) else: positive_overcuts.append(shape) def setOtherEdge(v1, v2, a): nonlocal centerv, extendedv if self.otherEdge: centerv = v1 extendedv = v2 else: centerv = -v2 extendedv = -v1 addOvercut(a) def setCenterOffset(a): nonlocal centerv, extendedv, sign centerv = v1 - v2 centerv.normalize() extendedv = centerv * math.tan(a/2) * -sign addOvercut(a) def getCorner(idx, offset): nonlocal insideCorners idx += offset if idx >= len(insideCorners): idx -= len(insideCorners) return insideCorners[idx] def getCornerDelta(curidx, nextidx): nonlocal cornerCnt delta = nextidx - curidx if delta < 0: delta += cornerCnt return delta for s in shapes: s = shapely.geometry.polygon.orient(s,1) loops = [s.boundary] if s.boundary.type == 'LineString' else s.boundary outercurve = self.do_outer or len(loops)==1 for ci,c in enumerate(loops): if ci>0 or outercurve: if isTBone: cornerCnt = 0 insideCorners = [] for i,co in enumerate(c.coords): i1 = i-1 if i1==-1: i1 = -2 i2 = i+1 if i2 == len(c.coords): i2 = 0 v1 = Vector(co).xy - Vector(c.coords[i1]).xy v2 = Vector(c.coords[i2]).xy - Vector(co).xy if not v1.length==0 and not v2.length == 0: a = v1.angle_signed(v2) insideCornerFound = False outsideCornerFound = False if a<-anglethreshold: if sign<0: insideCornerFound = True else: outsideCornerFound = True elif a>anglethreshold: if sign>0: insideCornerFound = True else: outsideCornerFound = True if insideCornerFound: # an inside corner with an overcut has been found # which means a new side has been found pos = Vector((co[0],co[1])) v1.normalize() v2.normalize() # figure out which direction vector to use # v is the main direction vector to move the overcut shape along # ev is the direction vector used to elongate the overcut shape if self.style != 'DOGBONE': # t-bone and opposite edge styles get treated nearly the same if isTBone: cornerCnt += 1 #insideCorner tuplet: (pos, v1, v2, angle, corner index) insideCorners.append((pos, v1, v2, a, cornerCnt-1)) #processing of corners for T-Bone are done after all points are processed continue setOtherEdge(v1, v2, a) else: # DOGBONE style setCenterOffset(a) elif isTBone and outsideCornerFound: # add an outside corner to the list cornerCnt += 1 # check if t-bone processing required # if no inside corners then nothing to do if isTBone and len(insideCorners) > 0: #print(cornerCnt, len(insideCorners)) # process all of the inside corners for i, corner in enumerate(insideCorners): pos, v1, v2, a, idx = corner # figure out which side of the corner to do overcut # if prev corner is outside corner # calc index distance between current corner and prev prevCorner = getCorner(i, -1) #print('first:', i, idx, prevCorner[IDX]) if getCornerDelta(prevCorner[IDX], idx) == 1: # make sure there is an outside corner #print(getCornerDelta(getCorner(i, -2)[IDX], idx)) if getCornerDelta(getCorner(i, -2)[IDX], idx) > 2: setOtherEdge(v1, v2, a) #print('first won') continue nextCorner = getCorner(i, 1) #print('second:', i, idx, nextCorner[IDX]) if getCornerDelta(idx, nextCorner[IDX]) == 1: # make sure there is an outside corner #print(getCornerDelta(idx, getCorner(i, 2)[IDX])) if getCornerDelta(idx, getCorner(i, 2)[IDX]) > 2: #print('second won') setOtherEdge(-v2, -v1, a) continue #print('third') if getCornerDelta(prevCorner[IDX], idx) == 3: # check if they share the same edge a1 = v1.angle_signed(prevCorner[V2])*180.0/math.pi #print('third won', a1) if a1 < -135 or a1 > 135: setOtherEdge(-v2, -v1, a) continue #print('fourth') if getCornerDelta(idx, nextCorner[IDX]) == 3: # check if they share the same edge a1 = v2.angle_signed(nextCorner[V1])*180.0/math.pi #print('fourth won', a1) if a1 < -135 or a1 > 135: setOtherEdge(v1, v2, a) continue #print('***No Win***') # the default if no other rules pass setCenterOffset(a) negative_overcuts = shapely.ops.unary_union(negative_overcuts) positive_overcuts = shapely.ops.unary_union(positive_overcuts) fs = shapely.ops.unary_union(shapes) fs = fs.union(positive_overcuts) fs = fs.difference(negative_overcuts) o=utils.shapelyToCurve(o1.name+'_overcuts',fs,o1.location.z) return {'FINISHED'}
def execute(self, context): #utils.silhoueteOffset(context,-self.diameter) o1=bpy.context.active_object shapes=utils.curveToShapely(o1) negative_overcuts=[] positive_overcuts=[] diameter = self.diameter*1.001 for s in shapes: s=shapely.geometry.polygon.orient(s,1) if s.boundary.type == 'LineString': loops = [s.boundary]#s=shapely.geometry.asMultiLineString(s) else: loops = s.boundary for ci,c in enumerate(loops): if ci>0 or self.do_outer: #c=s.boundary for i,co in enumerate(c.coords): i1=i-1 if i1==-1: i1=-2 i2=i+1 if i2 == len(c.coords): i2=0 v1 = Vector(co) - Vector(c.coords[i1]) v1 = v1.xy#Vector((v1.x,v1.y,0)) v2 = Vector(c.coords[i2]) - Vector(co) v2 = v2.xy#v2 = Vector((v2.x,v2.y,0)) if not v1.length==0 and not v2.length == 0: a=v1.angle_signed(v2) sign=1 #if ci==0: # sign=-1 #else: # sign=1 if self.invert:# and ci>0: sign*=-1 if (sign<0 and a<-self.threshold) or (sign>0 and a>self.threshold): p=Vector((co[0],co[1])) v1.normalize() v2.normalize() v=v1-v2 v.normalize() p=p-v*diameter/2 if abs(a)<math.pi/2: shape=utils.Circle(diameter/2,64) shape= shapely.affinity.translate(shape,p.x,p.y) else: l=math.tan(a/2)*diameter/2 p1=p-sign*v*l l=shapely.geometry.LineString((p,p1)) shape=l.buffer(diameter/2, resolution = 64) if sign>0: negative_overcuts.append(shape) else: positive_overcuts.append(shape) print(a) #for c in s.boundary: negative_overcuts = shapely.ops.unary_union(negative_overcuts) positive_overcuts = shapely.ops.unary_union(positive_overcuts) #shapes.extend(overcuts) fs=shapely.ops.unary_union(shapes) fs = fs.union(positive_overcuts) fs = fs.difference(negative_overcuts) o=utils.shapelyToCurve(o1.name+'_overcuts',fs,o1.location.z) #o=utils.shapelyToCurve('overcuts',overcuts,0) return {'FINISHED'}
def execute(self, context): #utils.silhoueteOffset(context,-self.diameter) o1 = bpy.context.active_object shapes = utils.curveToShapely(o1) negative_overcuts = [] positive_overcuts = [] diameter = self.diameter * 1.001 for s in shapes: s = shapely.geometry.polygon.orient(s, 1) if s.boundary.type == 'LineString': loops = [s.boundary] #s=shapely.geometry.asMultiLineString(s) else: loops = s.boundary for ci, c in enumerate(loops): if ci > 0 or self.do_outer: #c=s.boundary for i, co in enumerate(c.coords): i1 = i - 1 if i1 == -1: i1 = -2 i2 = i + 1 if i2 == len(c.coords): i2 = 0 v1 = Vector(co) - Vector(c.coords[i1]) v1 = v1.xy #Vector((v1.x,v1.y,0)) v2 = Vector(c.coords[i2]) - Vector(co) v2 = v2.xy #v2 = Vector((v2.x,v2.y,0)) if not v1.length == 0 and not v2.length == 0: a = v1.angle_signed(v2) sign = 1 #if ci==0: # sign=-1 #else: # sign=1 if self.invert: # and ci>0: sign *= -1 if (sign < 0 and a < -self.threshold) or ( sign > 0 and a > self.threshold): p = Vector((co[0], co[1])) v1.normalize() v2.normalize() v = v1 - v2 v.normalize() p = p - v * diameter / 2 if abs(a) < math.pi / 2: shape = utils.Circle(diameter / 2, 64) shape = shapely.affinity.translate( shape, p.x, p.y) else: l = math.tan(a / 2) * diameter / 2 p1 = p - sign * v * l l = shapely.geometry.LineString((p, p1)) shape = l.buffer(diameter / 2, resolution=64) if sign > 0: negative_overcuts.append(shape) else: positive_overcuts.append(shape) print(a) #for c in s.boundary: negative_overcuts = shapely.ops.unary_union(negative_overcuts) positive_overcuts = shapely.ops.unary_union(positive_overcuts) #shapes.extend(overcuts) fs = shapely.ops.unary_union(shapes) fs = fs.union(positive_overcuts) fs = fs.difference(negative_overcuts) o = utils.shapelyToCurve(o1.name + '_overcuts', fs, o1.location.z) #o=utils.shapelyToCurve('overcuts',overcuts,0) return {'FINISHED'}
def execute(self, context): o1 = bpy.context.active_object shapes = utils.curveToShapely(o1) negative_overcuts = [] positive_overcuts = [] # count all the corners including inside and out cornerCnt = 0 # a list of tuples for defining the inside corner # tuple is: (pos, v1, v2, angle, allCorners list index) insideCorners = [] diameter = self.diameter * 1.001 radius = diameter / 2 anglethreshold = math.pi - self.threshold centerv = mathutils.Vector((0, 0)) extendedv = mathutils.Vector((0, 0)) pos = mathutils.Vector((0, 0)) sign = -1 if self.do_invert else 1 isTBone = self.style == 'TBONE' # indexes in insideCorner tuple POS, V1, V2, A, IDX = range(5) def addOvercut(a): nonlocal pos, centerv, radius, extendedv, sign, negative_overcuts, positive_overcuts # move the overcut shape center position 1 radius in direction v pos -= centerv * radius if abs(a) < math.pi / 2: shape = utils.Circle(radius, 64) shape = shapely.affinity.translate(shape, pos.x, pos.y) else: # elongate overcut circle to make sure tool bit can fit into slot p1 = pos + (extendedv * radius) l = shapely.geometry.LineString((pos, p1)) shape = l.buffer(radius, resolution=64) if sign > 0: negative_overcuts.append(shape) else: positive_overcuts.append(shape) def setOtherEdge(v1, v2, a): nonlocal centerv, extendedv if self.otherEdge: centerv = v1 extendedv = v2 else: centerv = -v2 extendedv = -v1 addOvercut(a) def setCenterOffset(a): nonlocal centerv, extendedv, sign centerv = v1 - v2 centerv.normalize() extendedv = centerv * math.tan(a / 2) * -sign addOvercut(a) def getCorner(idx, offset): nonlocal insideCorners idx += offset if idx >= len(insideCorners): idx -= len(insideCorners) return insideCorners[idx] def getCornerDelta(curidx, nextidx): nonlocal cornerCnt delta = nextidx - curidx if delta < 0: delta += cornerCnt return delta for s in shapes: s = shapely.geometry.polygon.orient(s, 1) loops = [s.boundary ] if s.boundary.type == 'LineString' else s.boundary outercurve = self.do_outer or len(loops) == 1 for ci, c in enumerate(loops): if ci > 0 or outercurve: if isTBone: cornerCnt = 0 insideCorners = [] for i, co in enumerate(c.coords): i1 = i - 1 if i1 == -1: i1 = -2 i2 = i + 1 if i2 == len(c.coords): i2 = 0 v1 = mathutils.Vector(co).xy - mathutils.Vector( c.coords[i1]).xy v2 = mathutils.Vector( c.coords[i2]).xy - mathutils.Vector(co).xy if not v1.length == 0 and not v2.length == 0: a = v1.angle_signed(v2) insideCornerFound = False outsideCornerFound = False if a < -anglethreshold: if sign < 0: insideCornerFound = True else: outsideCornerFound = True elif a > anglethreshold: if sign > 0: insideCornerFound = True else: outsideCornerFound = True if insideCornerFound: # an inside corner with an overcut has been found # which means a new side has been found pos = mathutils.Vector((co[0], co[1])) v1.normalize() v2.normalize() # figure out which direction vector to use # v is the main direction vector to move the overcut shape along # ev is the direction vector used to elongate the overcut shape if self.style != 'DOGBONE': # t-bone and opposite edge styles get treated nearly the same if isTBone: cornerCnt += 1 # insideCorner tuplet: (pos, v1, v2, angle, corner index) insideCorners.append( (pos, v1, v2, a, cornerCnt - 1)) # processing of corners for T-Bone are done after all points are processed continue setOtherEdge(v1, v2, a) else: # DOGBONE style setCenterOffset(a) elif isTBone and outsideCornerFound: # add an outside corner to the list cornerCnt += 1 # check if t-bone processing required # if no inside corners then nothing to do if isTBone and len(insideCorners) > 0: # print(cornerCnt, len(insideCorners)) # process all of the inside corners for i, corner in enumerate(insideCorners): pos, v1, v2, a, idx = corner # figure out which side of the corner to do overcut # if prev corner is outside corner # calc index distance between current corner and prev prevCorner = getCorner(i, -1) # print('first:', i, idx, prevCorner[IDX]) if getCornerDelta(prevCorner[IDX], idx) == 1: # make sure there is an outside corner # print(getCornerDelta(getCorner(i, -2)[IDX], idx)) if getCornerDelta(getCorner(i, -2)[IDX], idx) > 2: setOtherEdge(v1, v2, a) # print('first won') continue nextCorner = getCorner(i, 1) # print('second:', i, idx, nextCorner[IDX]) if getCornerDelta(idx, nextCorner[IDX]) == 1: # make sure there is an outside corner # print(getCornerDelta(idx, getCorner(i, 2)[IDX])) if getCornerDelta(idx, getCorner(i, 2)[IDX]) > 2: # print('second won') setOtherEdge(-v2, -v1, a) continue # print('third') if getCornerDelta(prevCorner[IDX], idx) == 3: # check if they share the same edge a1 = v1.angle_signed( prevCorner[V2]) * 180.0 / math.pi # print('third won', a1) if a1 < -135 or a1 > 135: setOtherEdge(-v2, -v1, a) continue # print('fourth') if getCornerDelta(idx, nextCorner[IDX]) == 3: # check if they share the same edge a1 = v2.angle_signed( nextCorner[V1]) * 180.0 / math.pi # print('fourth won', a1) if a1 < -135 or a1 > 135: setOtherEdge(v1, v2, a) continue # print('***No Win***') # the default if no other rules pass setCenterOffset(a) negative_overcuts = shapely.ops.unary_union(negative_overcuts) positive_overcuts = shapely.ops.unary_union(positive_overcuts) fs = shapely.ops.unary_union(shapes) fs = fs.union(positive_overcuts) fs = fs.difference(negative_overcuts) o = utils.shapelyToCurve(o1.name + '_overcuts', fs, o1.location.z) return {'FINISHED'}
def gear(mm_per_tooth=0.003, number_of_teeth=5, hole_diameter=0.003175, pressure_angle=0.3488, clearance=0.0, backlash=0.0, rim_size=0.0005, hub_diameter=0.006, spokes=4): simple.deselect() pi = math.pi p = mm_per_tooth * number_of_teeth / pi / 2 # radius of pitch circle c = p + mm_per_tooth / pi - clearance # radius of outer circle b = p * math.cos(pressure_angle) # radius of base circle r = p - (c - p) - clearance # radius of root circle t = mm_per_tooth / 2 - backlash / 2 # tooth thickness at pitch circle k = -gear_iang( b, p ) - t / 2 / p # angle to where involute meets base circle on each side of tooth shapely_gear = Polygon([(0, 0), gear_polar(r, k if r < b else -pi / number_of_teeth), gear_q7(0, r, b, c, k, 1), gear_q7(0.1, r, b, c, k, 1), gear_q7(0.2, r, b, c, k, 1), gear_q7(0.3, r, b, c, k, 1), gear_q7(0.4, r, b, c, k, 1), gear_q7(0.5, r, b, c, k, 1), gear_q7(0.6, r, b, c, k, 1), gear_q7(0.7, r, b, c, k, 1), gear_q7(0.8, r, b, c, k, 1), gear_q7(0.9, r, b, c, k, 1), gear_q7(1.0, r, b, c, k, 1), gear_q7(1.0, r, b, c, k, -1), gear_q7(0.9, r, b, c, k, -1), gear_q7(0.8, r, b, c, k, -1), gear_q7(0.7, r, b, c, k, -1), gear_q7(0.6, r, b, c, k, -1), gear_q7(0.5, r, b, c, k, -1), gear_q7(0.4, r, b, c, k, -1), gear_q7(0.3, r, b, c, k, -1), gear_q7(0.2, r, b, c, k, -1), gear_q7(0.1, r, b, c, k, -1), gear_q7(0.0, r, b, c, k, -1), gear_polar(r, -k if r < b else pi / number_of_teeth)]) utils.shapelyToCurve('tooth', shapely_gear, 0.0) i = number_of_teeth while i > 1: simple.duplicate() simple.rotate(2 * math.pi / number_of_teeth) i -= 1 simple.join_multiple('tooth') simple.active_name('_teeth') bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Circle', Simple_radius=r, shape='3D', use_cyclic_u=True, edit_mode=False) simple.active_name('_hub') simple.union('_') simple.active_name('_gear') simple.remove_doubles() if spokes > 0: bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Circle', Simple_radius=r - rim_size, shape='3D', use_cyclic_u=True, edit_mode=False) simple.active_name('_hole') simple.difference('_', '_gear') bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Circle', Simple_radius=hub_diameter / 2, shape='3D', use_cyclic_u=True, edit_mode=False) simple.active_name('_hub') bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Circle', Simple_radius=hole_diameter / 2, shape='3D', use_cyclic_u=True, edit_mode=False) simple.active_name('_hub_hole') simple.difference('_hub', '_hub') simple.join_multiple('_') simple.add_rectangle( r - rim_size - ((hub_diameter - hole_diameter) / 4 + hole_diameter / 2), hub_diameter / 2, center_x=False) simple.move(x=(hub_diameter - hole_diameter) / 4 + hole_diameter / 2) simple.active_name('_spoke') angle = 2 * pi / spokes while spokes > 0: simple.duplicate() simple.rotate(angle) spokes -= 1 simple.union('_spoke') simple.remove_doubles() simple.union('_') else: bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Circle', Simple_radius=hole_diameter, shape='3D', use_cyclic_u=True, edit_mode=False) simple.active_name('_hole') simple.difference('_', '_gear') name = 'gear-' + str(round(mm_per_tooth * 1000, 1)) name += 'mm-pitch-' + str(number_of_teeth) name += 'teeth-PA-' + str(round(math.degrees(pressure_angle), 1)) simple.active_name(name)