kopia lustrzana https://github.com/vilemduha/blendercam
experimenting with interlocking twist joints
rodzic
3122b9caf3
commit
a01430c7da
|
@ -26,7 +26,8 @@ import bpy
|
|||
from bpy.props import *
|
||||
from bpy.types import Operator
|
||||
from bpy_extras.io_utils import ImportHelper
|
||||
from cam import utils, pack, polygon_utils_cam, simple, gcodepath, bridges, parametric, gcodeimportparser, joinery, curvecamtools
|
||||
from cam import utils, pack, polygon_utils_cam, simple, gcodepath, bridges, parametric, gcodeimportparser, joinery, \
|
||||
curvecamtools
|
||||
import shapely
|
||||
from shapely.geometry import Point, LineString, Polygon
|
||||
import mathutils
|
||||
|
@ -225,7 +226,7 @@ class CamCurveMortise(bpy.types.Operator):
|
|||
loop_length = c.length
|
||||
print("line Length:", loop_length)
|
||||
|
||||
if self.adaptive > 0.0 and not self.interlocking_groove:
|
||||
if self.adaptive > 0.0:
|
||||
joinery.variable_finger(c, length, self.min_finger_size, self.finger_size, self.plate_thickness,
|
||||
self.finger_tolerance, self.adaptive)
|
||||
locations = joinery.variable_finger(c, length, self.min_finger_size, self.finger_size,
|
||||
|
@ -236,21 +237,17 @@ class CamCurveMortise(bpy.types.Operator):
|
|||
joinery.make_variable_flex_pocket(self.side_height, self.plate_thickness, self.flex_pocket,
|
||||
locations)
|
||||
|
||||
elif not self.interlocking_groove:
|
||||
else:
|
||||
joinery.fixed_finger(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance)
|
||||
joinery.fixed_finger(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance, True)
|
||||
joinery.create_flex_side(loop_length, self.side_height, self.plate_thickness, self.top_bottom)
|
||||
if self.flex_pocket > 0:
|
||||
joinery.make_flex_pocket(length, self.side_height, self.plate_thickness, self.finger_size,
|
||||
self.flex_pocket)
|
||||
|
||||
elif self.interlocking_groove:
|
||||
if self.twist:
|
||||
joinery.distributed_interlock(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance, self.finger_amount, fixed_angle=self.fixed_angle, tangent=self.tangent_angle,closed=not self.opencurve, type='twist')
|
||||
else:
|
||||
joinery.distributed_interlock(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance, self.finger_amount, fixed_angle=self.fixed_angle, tangent=self.tangent_angle,closed=not self.opencurve)
|
||||
simple.removeMultiple('_')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class CamCurveInterlock(bpy.types.Operator):
|
||||
"""Generates interlock along a curve""" # by Alain Pelletier December 2021
|
||||
bl_idname = "object.curve_interlock"
|
||||
|
@ -264,61 +261,70 @@ class CamCurveInterlock(bpy.types.Operator):
|
|||
plate_thickness: bpy.props.FloatProperty(name="Plate thickness", default=0.00477, min=0.001, max=3.0,
|
||||
unit="LENGTH")
|
||||
opencurve: bpy.props.BoolProperty(name="OpenCurve", default=False)
|
||||
interlocking_groove: bpy.props.BoolProperty(name="interlocking_groove", default=False)
|
||||
twist: bpy.props.BoolProperty(name="twist_interlock", default=False)
|
||||
interlock_type: EnumProperty(name='Type of interlock',
|
||||
items=(('TWIST', 'Twist', 'Iterlock requires 1/4 turn twist'),
|
||||
('GROOVE', 'Groove', 'Simple sliding groove')),
|
||||
description='Type of interlock',
|
||||
default='GROOVE')
|
||||
finger_amount: bpy.props.IntProperty(name="Finger Amount", default=2, min=1, max=100)
|
||||
tangent_angle: bpy.props.FloatProperty(name="Tangent deviation", default=0.0, min=0.000, max=2, subtype="ANGLE",
|
||||
unit="ROTATION")
|
||||
unit="ROTATION")
|
||||
fixed_angle: bpy.props.FloatProperty(name="fixed angle", default=0.0, min=0.000, max=2, subtype="ANGLE",
|
||||
unit="ROTATION")
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object is not None and (context.active_object.type in ['CURVE', 'FONT'])
|
||||
unit="ROTATION")
|
||||
|
||||
def execute(self, context):
|
||||
o1 = bpy.context.active_object
|
||||
print(len(context.selected_objects), "selected object", context.selected_objects)
|
||||
if len(context.selected_objects) > 0 and (context.active_object.type in ['CURVE', 'FONT']):
|
||||
o1 = bpy.context.active_object
|
||||
|
||||
bpy.context.object.data.resolution_u = 60
|
||||
bpy.ops.object.duplicate()
|
||||
obj = context.active_object
|
||||
bpy.ops.object.convert(target='MESH')
|
||||
bpy.context.active_object.name = "_temp_mesh"
|
||||
bpy.context.object.data.resolution_u = 60
|
||||
bpy.ops.object.duplicate()
|
||||
obj = context.active_object
|
||||
bpy.ops.object.convert(target='MESH')
|
||||
bpy.context.active_object.name = "_temp_mesh"
|
||||
|
||||
if self.opencurve:
|
||||
coords = []
|
||||
for v in obj.data.vertices: # extract X,Y coordinates from the vertices data
|
||||
coords.append((v.co.x, v.co.y))
|
||||
line = LineString(coords) # convert coordinates to shapely LineString datastructure
|
||||
simple.removeMultiple("-converted")
|
||||
utils.shapelyToCurve('-converted_curve', line, 0.0)
|
||||
shapes = utils.curveToShapely(o1)
|
||||
if self.opencurve:
|
||||
coords = []
|
||||
for v in obj.data.vertices: # extract X,Y coordinates from the vertices data
|
||||
coords.append((v.co.x, v.co.y))
|
||||
line = LineString(coords) # convert coordinates to shapely LineString datastructure
|
||||
simple.removeMultiple("-converted")
|
||||
utils.shapelyToCurve('-converted_curve', line, 0.0)
|
||||
shapes = utils.curveToShapely(o1)
|
||||
|
||||
for s in shapes:
|
||||
if s.boundary.type == 'LineString':
|
||||
loops = [s.boundary]
|
||||
else:
|
||||
loops = s.boundary
|
||||
|
||||
for ci, c in enumerate(loops):
|
||||
if self.opencurve:
|
||||
length = line.length
|
||||
for s in shapes:
|
||||
if s.boundary.type == 'LineString':
|
||||
loops = [s.boundary]
|
||||
else:
|
||||
length = c.length
|
||||
print("loop Length:", length)
|
||||
if self.opencurve:
|
||||
loop_length = line.length
|
||||
else:
|
||||
loop_length = c.length
|
||||
print("line Length:", loop_length)
|
||||
loops = s.boundary
|
||||
|
||||
if self.twist:
|
||||
joinery.distributed_interlock(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance, self.finger_amount, fixed_angle=self.fixed_angle, tangent=self.tangent_angle,closed=not self.opencurve, type='twist')
|
||||
else:
|
||||
joinery.distributed_interlock(c, length, self.finger_size, self.plate_thickness, self.finger_tolerance, self.finger_amount, fixed_angle=self.fixed_angle, tangent=self.tangent_angle,closed=not self.opencurve)
|
||||
for ci, c in enumerate(loops):
|
||||
if self.opencurve:
|
||||
length = line.length
|
||||
else:
|
||||
length = c.length
|
||||
print("loop Length:", length)
|
||||
if self.opencurve:
|
||||
loop_length = line.length
|
||||
else:
|
||||
loop_length = c.length
|
||||
print("line Length:", loop_length)
|
||||
|
||||
joinery.distributed_interlock(c, length, self.finger_size, self.plate_thickness,
|
||||
self.finger_tolerance, self.finger_amount,
|
||||
fixed_angle=self.fixed_angle, tangent=self.tangent_angle,
|
||||
closed=not self.opencurve, type=self.interlock_type)
|
||||
|
||||
else:
|
||||
location = bpy.context.scene.cursor.location
|
||||
joinery.single_interlock(self.finger_size, self.plate_thickness, self.finger_tolerance, location[0],
|
||||
location[1], self.fixed_angle, self.interlock_type)
|
||||
bpy.context.active_object.name = "interlock"
|
||||
bpy.context.scene.cursor.location = location
|
||||
# simple.removeMultiple('_')
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
class CamCurveDrawer(bpy.types.Operator):
|
||||
"""Generates drawers""" # by Alain Pelletier December 2021 inspired by The Drawinator
|
||||
bl_idname = "object.curve_drawer"
|
||||
|
|
|
@ -58,26 +58,28 @@ def mortise(length, thickness, finger_play, cx=0, cy=0, rotation=0):
|
|||
|
||||
def interlock_groove(length, thickness, finger_play, cx=0, cy=0, rotation=0):
|
||||
mortise(length, thickness, finger_play, 0, 0, 0)
|
||||
bpy.ops.transform.translate(value=(length/2-finger_play/2, 0.0, 0.0))
|
||||
bpy.ops.transform.translate(value=(length / 2 - finger_play / 2, 0.0, 0.0))
|
||||
bpy.ops.object.transform_apply(location=True, rotation=False, scale=False)
|
||||
bpy.context.active_object.rotation_euler.z = rotation
|
||||
bpy.ops.transform.translate(value=(cx, cy, 0.0))
|
||||
bpy.context.active_object.name = "_groove"
|
||||
|
||||
|
||||
def interlock_twist(length, thickness, finger_play, cx=0, cy=0, rotation=0, percentage=0.5,):
|
||||
def interlock_twist(length, thickness, finger_play, cx=0, cy=0, rotation=0, percentage=0.5, ):
|
||||
mortise(length, thickness, finger_play, 0, 0, 0)
|
||||
bpy.context.active_object.name = "_tmp"
|
||||
mortise(length*percentage, thickness, finger_play, 0, 0, math.pi/2)
|
||||
mortise(length * percentage, thickness, finger_play, 0, 0, math.pi / 2)
|
||||
bpy.context.active_object.name = "_tmp"
|
||||
h = math.hypot(thickness, length*percentage)
|
||||
oangle = math.degrees(math.asin(length*percentage/h))
|
||||
h = math.hypot(thickness, length * percentage)
|
||||
oangle = math.degrees(math.asin(length * percentage / h))
|
||||
bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Sector',
|
||||
Simple_startangle=90+oangle, Simple_endangle=180-oangle, Simple_radius=h/2, use_cyclic_u=True, edit_mode=False)
|
||||
Simple_startangle=90 + oangle, Simple_endangle=180 - oangle, Simple_radius=h / 2,
|
||||
use_cyclic_u=True, edit_mode=False)
|
||||
bpy.context.active_object.name = "_tmp"
|
||||
|
||||
bpy.ops.curve.simple(align='WORLD', location=(0, 0, 0), rotation=(0, 0, 0), Simple_Type='Sector',
|
||||
Simple_startangle=270+oangle, Simple_endangle=360-oangle, Simple_radius=h/2, use_cyclic_u=True, edit_mode=False)
|
||||
Simple_startangle=270 + oangle, Simple_endangle=360 - oangle, Simple_radius=h / 2,
|
||||
use_cyclic_u=True, edit_mode=False)
|
||||
bpy.context.active_object.name = "_tmp"
|
||||
|
||||
simple.selectMultiple('_tmp')
|
||||
|
@ -86,6 +88,7 @@ def interlock_twist(length, thickness, finger_play, cx=0, cy=0, rotation=0, perc
|
|||
bpy.ops.transform.translate(value=(cx, cy, 0.0))
|
||||
bpy.context.active_object.name = "_groove"
|
||||
simple.removeMultiple('_tmp')
|
||||
simple.makeActive("_groove")
|
||||
|
||||
|
||||
def horizontal_finger(length, thickness, finger_play, amount, center=True):
|
||||
|
@ -436,7 +439,15 @@ def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, fi
|
|||
return hpos
|
||||
|
||||
|
||||
def distributed_interlock(loop, loop_length, finger_depth, finger_thick, finger_tolerance, finger_amount, tangent=0, fixed_angle=0, start=0.01, end=0.01, closed=True, type='groove'):
|
||||
def single_interlock(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle, type):
|
||||
if type == "GROOVE":
|
||||
interlock_groove(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle)
|
||||
elif type == "TWIST":
|
||||
interlock_twist(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle)
|
||||
|
||||
|
||||
def distributed_interlock(loop, loop_length, finger_depth, finger_thick, finger_tolerance, finger_amount, tangent=0,
|
||||
fixed_angle=0, start=0.01, end=0.01, closed=True, type='GROOVE'):
|
||||
# distributes interlocking joints of a fixed amount
|
||||
# dynamically changes the finger tolerance with the angle differences
|
||||
# loop = takes in a shapely shape
|
||||
|
@ -447,7 +458,7 @@ def distributed_interlock(loop, loop_length, finger_depth, finger_thick, finger_
|
|||
coords = list(loop.coords)
|
||||
old_mortise_angle = 0
|
||||
if not closed:
|
||||
spacing = (loop_length-start-end) / finger_amount
|
||||
spacing = (loop_length - start - end) / finger_amount
|
||||
distance = start
|
||||
else:
|
||||
spacing = loop_length / finger_amount
|
||||
|
@ -470,22 +481,19 @@ def distributed_interlock(loop, loop_length, finger_depth, finger_thick, finger_
|
|||
if not_start:
|
||||
while distance <= pd:
|
||||
if fixed_angle == 0:
|
||||
groove_angle = angle(oldp, p) + math.pi/2 + tangent
|
||||
groove_angle = angle(oldp, p) + math.pi / 2 + tangent
|
||||
else:
|
||||
groove_angle = fixed_angle
|
||||
|
||||
groove_point = loop.interpolate(distance)
|
||||
|
||||
print(j, "groove_angle", round(180*(groove_angle)/math.pi),"distance", round(distance * 1000),"mm")
|
||||
if type == "groove":
|
||||
interlock_groove(finger_depth, finger_thick, finger_tolerance, groove_point.x, groove_point.y,groove_angle)
|
||||
elif type == "twist":
|
||||
interlock_twist(finger_depth, finger_thick, finger_tolerance, groove_point.x, groove_point.y,
|
||||
groove_angle)
|
||||
print(j, "groove_angle", round(180 * groove_angle / math.pi), "distance", round(distance * 1000), "mm")
|
||||
single_interlock(finger_depth, finger_thick, finger_tolerance, groove_point.x, groove_point.y,
|
||||
groove_angle, type)
|
||||
|
||||
j += 1
|
||||
distance = j * spacing + start
|
||||
oldp = p
|
||||
|
||||
simple.joinMultiple("_groove")
|
||||
bpy.context.active_object.name = "groove"
|
||||
bpy.context.active_object.name = "interlock"
|
||||
|
|
Ładowanie…
Reference in New Issue