diff --git a/scripts/addons/cam/curvecamcreate.py b/scripts/addons/cam/curvecamcreate.py index f8b4728f..a82fab85 100644 --- a/scripts/addons/cam/curvecamcreate.py +++ b/scripts/addons/cam/curvecamcreate.py @@ -463,20 +463,22 @@ class CamCurvePuzzle(bpy.types.Operator): height: bpy.props.FloatProperty(name="height or thickness", default=0.025, min=0.005, max=3.0, precision=4, unit="LENGTH") - angle: bpy.props.FloatProperty(name="angle", default=math.pi/4, min=-10, max=10, subtype="ANGLE", + angle: bpy.props.FloatProperty(name="angle A", default=math.pi/4, min=-10, max=10, subtype="ANGLE", unit="ROTATION") - angleb: bpy.props.FloatProperty(name="angle", default=0, min=-10, max=10, subtype="ANGLE", + angleb: bpy.props.FloatProperty(name="angle B", default=math.pi/4, min=-10, max=10, subtype="ANGLE", unit="ROTATION") radius: bpy.props.FloatProperty(name="Arc Radius", default=0.02, min=0.005, max=5, precision=4, unit="LENGTH") interlock_type: EnumProperty(name='Type of shape', - items=(('BAR', 'Bar', 'Bar interlock'), - ('ARC', 'Arc', 'Curve interlock'), - ('CURVEBARCURVE', 'Curve Bar Curve', 'Curve Bar Curve interlock')), + items=(('JOINT', 'Joint', 'Puzzle Joint interlock'), + ('BAR', 'Bar', 'Bar interlock'), + ('ARC', 'Arc', 'Arc interlock'), + ('CURVEBAR', 'Arc Bar', 'Arc Bar interlock'), + ('CURVEBARCURVE', 'Arc Bar Arc', 'Arc Bar Arc interlock')), description='Type of interlock', - default='CURVEBARCURVE') + default='JOINT') arc_type: EnumProperty(name='Type of arc', items=(('MF', 'Male-Receptacle', 'Male and receptacle'), ('F', 'Receptacle only', 'Receptacle'), @@ -494,34 +496,47 @@ class CamCurvePuzzle(bpy.types.Operator): layout = self.layout scene = context.scene layout = self.layout + layout.prop(self, 'interlock_type') layout.label(text='Puzzle Joint Definition') layout.prop(self, 'stem_size') layout.prop(self, 'diameter') layout.prop(self, 'finger_tolerance') layout.prop(self, 'finger_amount') - layout.prop(self, 'twist_lock') - if self.twist_lock: - layout.prop(self, 'twist_thick') - layout.prop(self, 'twist_percent') - - layout.separator() - - layout.prop(self, 'height') - - layout.prop(self, 'interlock_type') + if self.interlock_type != 'JOINT': + layout.prop(self, 'twist_lock') + if self.twist_lock: + layout.prop(self, 'twist_thick') + layout.prop(self, 'twist_percent') + layout.separator() + layout.prop(self, 'height') if self.interlock_type == "ARC" or self.interlock_type == "CURVEBARCURVE": layout.prop(self, 'arc_type') layout.prop(self, 'radius') layout.prop(self, 'angle') - layout.prop(self, 'angleb') + if self.interlock_type == 'CURVEBARCURVE': + layout.prop(self, 'angleb') - if self.interlock_type == 'BAR' or self.interlock_type == 'CURVEBAR': - if self.interlock_type == 'CURVEBAR': + if self.interlock_type == 'BAR' or self.interlock_type == 'CURVEBARCURVE': + if self.interlock_type == 'CURVEBARCURVE': layout.label(text="Width includes 2 radius and thickness") layout.prop(self, 'width') def execute(self, context): + + if self.interlock_type == 'FINGER': + puzzle_joinery.finger(self.diameter, self.finger_tolerance, stem=self.stem_size) + simple.rename('_puzzle', 'receptacle') + puzzle_joinery.finger(self.diameter, 0, stem=self.stem_size) + simple.rename('_puzzle', 'finger') + + if self.interlock_type == 'JOINT': + if self.finger_amount == 0: # cannot be 0 in joints + self.finger_amount = 1 + puzzle_joinery.fingers(self.diameter, self.finger_tolerance, self.finger_amount, stem=self.stem_size) + simple.rename('fingers', 'joint_finger') + simple.rename('receptacle', 'joint_receptacle') + if self.interlock_type == 'BAR': puzzle_joinery.bar(self.width, self.height, self.diameter, self.finger_tolerance, self.finger_amount, stem=self.stem_size, twist=self.twist_lock, tneck=self.twist_percent, diff --git a/scripts/addons/cam/puzzle_joinery.py b/scripts/addons/cam/puzzle_joinery.py index 8151592c..eda1902e 100644 --- a/scripts/addons/cam/puzzle_joinery.py +++ b/scripts/addons/cam/puzzle_joinery.py @@ -19,7 +19,7 @@ # # ***** END GPL LICENCE BLOCK ***** -# blender operators definitions are in this file. They mostly call the functions from utils.py +# blender operators definitions are in this file. They mostly call the functions from curvecamcreate.py import bpy @@ -33,18 +33,24 @@ import mathutils import math def rotate(angle): + # rotate active object by angle in a xy plane. + # transformation is applied after rotation bpy.context.active_object.rotation_euler.z = angle bpy.ops.object.transform_apply(location=True, rotation=True, scale=False) def finger(diameter, inside, DT=1.025, stem=2): + # diameter = diameter of the tool for joint creation + # tolerance = Tolerance in the joint + # DT = Bit diameter tolerance + # stem = amount of radius the stem or neck of the joint will have RESOLUTION = 12 # Data resolution cube_sx = diameter * DT * (2 + stem - 1) + inside cube_ty = diameter * DT + inside cube_sy = 2 * diameter * DT + inside / 2 circle_radius = diameter * DT / 2 c1x = (cube_sx) / 2 + inside - c2x = (cube_sx + inside) / 2 + inside # stem*diameter * DT + inside - c2y = 3 * circle_radius # + inside / 2 + c2x = (cube_sx + inside) / 2 + inside + c2y = 3 * circle_radius c1y = circle_radius bpy.ops.curve.simple(align='WORLD', location=(0, cube_ty, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', @@ -74,8 +80,6 @@ def finger(diameter, inside, DT=1.025, stem=2): simple.makeActive('sum') bpy.context.active_object.name = "_sum" -# bpy.ops.curve.primitive_bezier_circle_add(radius=circle_radius, enter_editmode=False, align='WORLD', -# location=(c1x, circle_radius, 0)) rc1 = circle_radius - inside bpy.ops.curve.simple(align='WORLD', location=(c1x, c1y, 0), rotation=(0, 0, 0), Simple_Type='Ellipse', Simple_a=circle_radius, Simple_b=rc1, Simple_sides=4, use_cyclic_u=True, edit_mode=False) @@ -97,22 +101,32 @@ def finger(diameter, inside, DT=1.025, stem=2): simple.makeActive("PUZZLE") bpy.context.active_object.name = "_puzzle" -def fingers(diameter, inside, amount,stem=1): - DT = 1.025 + +def fingers(diameter, inside, amount, stem=1, DT=1.025): + # diameter = diameter of the tool for joint creation + # inside = Tolerance in the joint receptacle + # DT = Bit diameter tolerance + # stem = amount of radius the stem or neck of the joint will have + # amount = the amount of fingers + translate = -(4+2*(stem-1)) * (amount - 1) * diameter * DT/2 - finger(diameter, 0, DT=DT, stem=stem) - bpy.context.active_object.name = "puzzle" + finger(diameter, 0, DT=DT, stem=stem) # generate male finger + bpy.context.active_object.name = "puzzlem" bpy.ops.object.curve_remove_doubles() bpy.ops.transform.translate(value=(translate, -0.00002, 0.0)) - for i in range(amount-1): - bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked": False, "mode": 'TRANSLATION'}, + if amount > 1: + # duplicate translate the amount needed (faster than generating new) + for i in range(amount-1): + bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked": False, "mode": 'TRANSLATION'}, TRANSFORM_OT_translate={"value": ((4+2*(stem-1)) * diameter * DT, 0, 0.0)}) - simple.selectMultiple('puzzle') - bpy.ops.object.curve_boolean(boolean_type='UNION') - bpy.context.active_object.name = "fingers" - simple.removeMultiple("puzzle") + simple.selectMultiple('puzzle') + bpy.ops.object.curve_boolean(boolean_type='UNION') + bpy.context.active_object.name = "fingers" + simple.removeMultiple("puzzle") + else: + bpy.context.active_object.name = "fingers" simple.makeActive('fingers') bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') @@ -123,20 +137,37 @@ def fingers(diameter, inside, amount,stem=1): bpy.ops.object.curve_remove_doubles() bpy.ops.transform.translate(value=(translate, -inside * 1.05, 0.0)) - for i in range(amount - 1): - bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked": False, "mode": 'TRANSLATION'}, - TRANSFORM_OT_translate={"value": ((4+2*(stem-1)) * diameter * DT, 0, 0.0)}) - simple.selectMultiple('puzzle') - bpy.ops.object.curve_boolean(boolean_type='UNION') - bpy.context.active_object.name = "receptacle" - simple.removeMultiple("puzzle") + if amount > 1: + # duplicate translate the amount needed (faster than generating new) + for i in range(amount - 1): + bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked": False, "mode": 'TRANSLATION'}, + TRANSFORM_OT_translate={ + "value": ((4 + 2 * (stem - 1)) * diameter * DT, 0, 0.0)}) + + simple.selectMultiple('puzzle') + bpy.ops.object.curve_boolean(boolean_type='UNION') + simple.activeName("receptacle") + simple.removeMultiple("puzzle") + else: + simple.activeName("receptacle") simple.makeActive('receptacle') bpy.ops.transform.translate(value=(0, -inside * 1.05, 0.0)) bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') bpy.ops.object.curve_remove_doubles() -def bar(width, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01): +def bar(width, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, 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 + DT = 1.025 if amount == 0: amount = round(thick / ((4+2*(stem-1)) * diameter * DT))-1 @@ -144,17 +175,8 @@ def bar(width, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck= Simple_width=width, Simple_length=thick, use_cyclic_u=True, edit_mode=False) bpy.context.active_object.name = "tmprect" - if amount < 2: - finger(diameter, tolerance, DT=1.025, stem=stem) - simple.rename('_puzzle', 'receptacle') - bpy.ops.transform.translate(value=(0, -tolerance, 0.0)) - bpy.ops.object.transform_apply(location=True, rotation=False, scale=False) - - finger(diameter, 0, DT=1.025, stem=stem) - simple.rename('_puzzle', '_tmpfingers') - else: - fingers(diameter, tolerance, amount, stem=stem) - simple.rename('fingers', '_tmpfingers') + fingers(diameter, tolerance, amount, stem=stem) + simple.rename('fingers', '_tmpfingers') rotate(-math.pi/2) bpy.ops.transform.translate(value=(width/2, 0, 0.0)) @@ -190,34 +212,37 @@ def bar(width, thick, diameter, tolerance, amount=0, stem=1, twist=False, tneck= def arc(radius, thick, angle, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, which='MF'): - if angle == 0: - angle = 0.1 + # 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 + + if angle == 0: # angle cannot be 0 + angle = 0.01 negative = False - if angle < 0: + if angle < 0: # if angle < 0 then negative is true angle = -angle negative = True - - DT = 1.025 + DT = 1.025 # diameter tolerance for diameter of finger creation if amount == 0: amount = round(thick / ((4+2*(stem-1)) * diameter * DT))-1 + + # 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" - if amount < 2: - finger(diameter, tolerance, DT=1.025, stem=stem) - simple.rename('_puzzle', 'receptacle') - bpy.ops.transform.translate(value=(0, -tolerance, 0.0)) - bpy.ops.object.transform_apply(location=True, rotation=False, scale=False) - - finger(diameter, 0, DT=1.025, stem=stem) - simple.rename('_puzzle', '_tmpfingers') - else: - fingers(diameter, tolerance, amount, stem=stem) - simple.rename('fingers', '_tmpfingers') + fingers(diameter, tolerance, amount, stem=stem) + simple.rename('fingers', '_tmpfingers') rotate(math.pi) bpy.ops.transform.translate(value=(radius, 0, 0.0)) @@ -244,7 +269,6 @@ def arc(radius, thick, angle, diameter, tolerance, amount=0, stem=1, twist=False bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') rotate(angle) - # simple.selectMultiple('_') if twist: bpy.ops.object.curve_boolean(boolean_type='UNION') simple.activeName('_tmpreceptacle') @@ -276,7 +300,8 @@ def arc(radius, thick, angle, diameter, tolerance, amount=0, stem=1, twist=False bpy.ops.transform.translate(value=(-radius, 0, 0.0)) bpy.ops.object.transform_apply(location=True, rotation=False, scale=False, properties=False) - if negative: + + if negative: # mirror if angle is negative bpy.ops.transform.mirror(orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False)) @@ -284,13 +309,31 @@ def arc(radius, thick, angle, diameter, tolerance, amount=0, stem=1, twist=False def arcbararc(length, radius, thick, angle, angleb, diameter, tolerance, amount=0, stem=1, twist=False, tneck=0.5, tthick=0.01, which='MF'): - length -= (radius * 2 + thick) + # 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 + + length -= (radius * 2 + thick) # adjust length to include 2x radius + thick + + # generate base rectangle bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Rectangle', Simple_width=length*1.005, Simple_length=thick, use_cyclic_u=True, edit_mode=False) simple.activeName("tmprect") + # Generate male section and join to the base if which == 'M' or which == 'MF': - arc(radius, thick, angleb, diameter, tolerance, amount=amount, stem=stem, twist=twist, tneck=tneck, tthick=tthick, which='M') + arc(radius, thick, angleb, diameter, tolerance, amount=amount, stem=stem, twist=twist, tneck=tneck, + tthick=tthick, which='M') bpy.ops.transform.translate(value=(length / 2, 0, 0.0)) simple.activeName('tmp_male') simple.selectMultiple('tmp') @@ -299,6 +342,7 @@ def arcbararc(length, radius, thick, angle, angleb, diameter, tolerance, amount= simple.removeMultiple('tmp') simple.rename('male', 'tmprect') + # Generate female section and join to base if which == 'F' or which == 'MF': arc(radius, thick, angle, diameter, tolerance, amount=amount, stem=stem, twist=twist, tneck=tneck, tthick=tthick, which='F') bpy.ops.transform.translate(value=(-length / 2, 0, 0.0))