kopia lustrzana https://github.com/vilemduha/blendercam
puzzle joints
rodzic
b0c42d9b04
commit
c28364e14e
|
@ -1257,6 +1257,8 @@ def get_panels(): # convenience function for bot register and unregister functi
|
|||
curvecamcreate.CamCurveDrawer,
|
||||
curvecamcreate.CamCurveMortise,
|
||||
curvecamcreate.CamCurveInterlock,
|
||||
curvecamcreate.CamCurvePuzzle,
|
||||
|
||||
|
||||
CAM_CUTTER_MT_presets,
|
||||
CAM_OPERATION_MT_presets,
|
||||
|
@ -1463,6 +1465,7 @@ classes = [
|
|||
curvecamcreate.CamCurveDrawer,
|
||||
curvecamcreate.CamCurveMortise,
|
||||
curvecamcreate.CamCurveInterlock,
|
||||
curvecamcreate.CamCurvePuzzle,
|
||||
|
||||
CAM_CUTTER_MT_presets,
|
||||
CAM_OPERATION_MT_presets,
|
||||
|
|
|
@ -26,8 +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, joinery, \
|
||||
curvecamtools, puzzle_joinery
|
||||
import shapely
|
||||
from shapely.geometry import Point, LineString, Polygon
|
||||
import mathutils
|
||||
|
@ -263,7 +263,8 @@ class CamCurveInterlock(bpy.types.Operator):
|
|||
opencurve: bpy.props.BoolProperty(name="OpenCurve", default=False)
|
||||
interlock_type: EnumProperty(name='Type of interlock',
|
||||
items=(('TWIST', 'Twist', 'Iterlock requires 1/4 turn twist'),
|
||||
('GROOVE', 'Groove', 'Simple sliding groove')),
|
||||
('GROOVE', 'Groove', 'Simple sliding groove'),
|
||||
('PUZZLE', 'Puzzle interlock', 'puzzle good for flat joints')),
|
||||
description='Type of interlock',
|
||||
default='GROOVE')
|
||||
finger_amount: bpy.props.IntProperty(name="Finger Amount", default=2, min=1, max=100)
|
||||
|
@ -318,12 +319,36 @@ class CamCurveInterlock(bpy.types.Operator):
|
|||
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"
|
||||
location[1], self.fixed_angle, self.interlock_type,self.finger_amount)
|
||||
|
||||
bpy.context.scene.cursor.location = location
|
||||
# simple.removeMultiple('_')
|
||||
return {'FINISHED'}
|
||||
|
||||
class CamCurvePuzzle(bpy.types.Operator):
|
||||
"""Generates interlock along a curve""" # by Alain Pelletier December 2021
|
||||
bl_idname = "object.curve_puzzle"
|
||||
bl_label = "Puzzle joints"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
diameter: bpy.props.FloatProperty(name="tool diameter", default=0.003, min=0.001, max=3.0, precision=4,
|
||||
unit="LENGTH")
|
||||
width: bpy.props.FloatProperty(name="Width", default=0.05, min=0.005, max=3.0, precision=4,
|
||||
unit="LENGTH")
|
||||
height: bpy.props.FloatProperty(name="height", default=0.025, min=0.005, max=3.0, precision=4,
|
||||
unit="LENGTH")
|
||||
finger_tolerance: bpy.props.FloatProperty(name="Finger play room", default=0.0001, min=0, max=0.003, precision=4,
|
||||
unit="LENGTH")
|
||||
finger_amount: bpy.props.IntProperty(name="Finger Amount", default=2, min=0, max=100)
|
||||
|
||||
angle: bpy.props.FloatProperty(name="angle", default=0.0, min=0.000, max=2, subtype="ANGLE",
|
||||
unit="ROTATION")
|
||||
|
||||
radius: bpy.props.FloatProperty(name="Curve Radius", default=0.050, min=0.005, max=3.0, precision=4,
|
||||
unit="LENGTH")
|
||||
|
||||
def execute(self, context):
|
||||
puzzle_joinery.bar(self.width, self.height, self.diameter, self.finger_tolerance, self.finger_amount)
|
||||
return {'FINISHED'}
|
||||
|
||||
class CamCurveDrawer(bpy.types.Operator):
|
||||
"""Generates drawers""" # by Alain Pelletier December 2021 inspired by The Drawinator
|
||||
|
|
|
@ -27,7 +27,7 @@ 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
|
||||
from cam import utils, pack, polygon_utils_cam, simple, gcodepath, bridges, parametric, puzzle_joinery
|
||||
import shapely
|
||||
from shapely.geometry import Point, LineString, Polygon
|
||||
import mathutils
|
||||
|
@ -439,11 +439,13 @@ def variable_finger(loop, loop_length, min_finger, finger_size, finger_thick, fi
|
|||
return hpos
|
||||
|
||||
|
||||
def single_interlock(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle, type):
|
||||
def single_interlock(finger_depth, finger_thick, finger_tolerance, x, y, groove_angle, type, amount=1):
|
||||
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)
|
||||
elif type == "PUZZLE":
|
||||
puzzle_joinery.fingers(finger_thick, finger_tolerance, amount)
|
||||
|
||||
|
||||
def distributed_interlock(loop, loop_length, finger_depth, finger_thick, finger_tolerance, finger_amount, tangent=0,
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
# blender CAM ops.py (c) 2021 Alain Pelletier
|
||||
#
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
# blender operators definitions are in this file. They mostly call the functions from utils.py
|
||||
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import Operator
|
||||
|
||||
from cam import utils, pack, polygon_utils_cam, simple, gcodepath, bridges, parametric, joinery
|
||||
import shapely
|
||||
from shapely.geometry import Point, LineString, Polygon
|
||||
import mathutils
|
||||
import math
|
||||
|
||||
def rotate(angle):
|
||||
bpy.context.active_object.rotation_euler.z = angle
|
||||
|
||||
def finger(diameter, inside, DT=1.025):
|
||||
RESOLUTION = 12 # Data resolution
|
||||
cube_sx = diameter * DT * 2 + inside
|
||||
cube_ty = diameter * DT + inside
|
||||
cube_sy = 2 * diameter * DT + inside / 2
|
||||
circle_radius = diameter * DT / 2
|
||||
c1x = -diameter * DT - inside
|
||||
c2x = diameter * DT + inside
|
||||
c2y = 3 * circle_radius # + inside / 2
|
||||
c1y = circle_radius
|
||||
|
||||
bpy.ops.curve.simple(align='WORLD', location=(0, cube_ty, 0), rotation=(0, 0, 0), Simple_Type='Rectangle',
|
||||
Simple_width=cube_sx, Simple_length=cube_sy, use_cyclic_u=True, edit_mode=False)
|
||||
bpy.context.active_object.name = "_tmprect"
|
||||
|
||||
|
||||
bpy.ops.curve.simple(align='WORLD', location=(c2x, c2y, 0), rotation=(0, 0, 0), Simple_Type='Ellipse', Simple_a=circle_radius,
|
||||
Simple_b=circle_radius + inside, Simple_sides=4, use_cyclic_u=True, edit_mode=False)
|
||||
|
||||
bpy.context.active_object.name = "_tmpcirc_add"
|
||||
bpy.context.object.data.resolution_u = RESOLUTION
|
||||
|
||||
bpy.ops.curve.simple(align='WORLD', location=(-c2x, c2y, 0), rotation=(0, 0, 0), Simple_Type='Ellipse', Simple_a=circle_radius,
|
||||
Simple_b=circle_radius + inside, Simple_sides=4, use_cyclic_u=True, edit_mode=False)
|
||||
|
||||
bpy.context.active_object.name = "_tmpcirc_add"
|
||||
bpy.context.object.data.resolution_u = RESOLUTION
|
||||
simple.joinMultiple('_tmpcirc')
|
||||
simple.selectMultiple('_tmp')
|
||||
bpy.ops.object.curve_boolean(boolean_type='UNION')
|
||||
bpy.context.active_object.name = "sum"
|
||||
bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN')
|
||||
bpy.context.object.scale[0] = inside * 3 + 1
|
||||
bpy.context.object.scale[1] = inside * 3 + 1
|
||||
simple.removeMultiple('_tmp')
|
||||
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)
|
||||
|
||||
bpy.context.active_object.name = "_circ_delete"
|
||||
bpy.context.object.data.resolution_u = RESOLUTION
|
||||
|
||||
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)
|
||||
bpy.context.active_object.name = "_circ_delete"
|
||||
bpy.context.object.data.resolution_u = RESOLUTION
|
||||
|
||||
simple.selectMultiple("_") # select everything starting with _
|
||||
|
||||
bpy.context.view_layer.objects.active = bpy.data.objects['_sum'] # Make the plate base active
|
||||
bpy.ops.object.curve_boolean(boolean_type='DIFFERENCE')
|
||||
bpy.context.active_object.name = "PUZZLE"
|
||||
simple.removeMultiple("_") # Remove temporary base and holes
|
||||
simple.makeActive("PUZZLE")
|
||||
bpy.context.active_object.name = "_puzzle"
|
||||
|
||||
def fingers(diameter, inside, amount):
|
||||
DT = 1.025
|
||||
translate = -4 * (amount - 1) * diameter * DT/2
|
||||
for i in range(amount):
|
||||
print('i 1:', i)
|
||||
finger(diameter, 0, DT=DT)
|
||||
bpy.ops.transform.translate(value=(i * 4 * diameter * DT, 0, 0.0))
|
||||
bpy.context.active_object.name = "puzzle"
|
||||
|
||||
simple.selectMultiple('puzzle')
|
||||
bpy.ops.object.curve_boolean(boolean_type='UNION')
|
||||
bpy.context.active_object.name = "fingers"
|
||||
simple.removeMultiple("puzzle")
|
||||
simple.makeActive('fingers')
|
||||
bpy.ops.object.curve_remove_doubles()
|
||||
bpy.ops.transform.translate(value=(translate, -0.00002, 0.0))
|
||||
bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN')
|
||||
|
||||
|
||||
# receptacle is smaller by the inside tolerance amount
|
||||
for i in range(amount):
|
||||
print('i 2:', i)
|
||||
finger(diameter, inside, DT=DT)
|
||||
bpy.ops.transform.translate(value=(i * 4 * diameter * DT, 0, 0.0))
|
||||
bpy.context.active_object.name = "puzzle"
|
||||
simple.selectMultiple('puzzle')
|
||||
bpy.ops.object.curve_boolean(boolean_type='UNION')
|
||||
bpy.context.active_object.name = "receptacle"
|
||||
simple.removeMultiple("puzzle")
|
||||
simple.makeActive('receptacle')
|
||||
bpy.ops.transform.translate(value=(translate, -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):
|
||||
if amount == 0:
|
||||
amount = round(thick / (5 * diameter * 1.025))
|
||||
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)
|
||||
bpy.context.active_object.name = "tmprect"
|
||||
|
||||
if amount < 2:
|
||||
finger(diameter, tolerance, DT=1.025)
|
||||
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)
|
||||
simple.rename('_puzzle', '_tmpfingers')
|
||||
else:
|
||||
fingers(diameter, tolerance, amount)
|
||||
simple.rename('fingers', '_tmpfingers')
|
||||
|
||||
rotate(-math.pi/2)
|
||||
bpy.ops.transform.translate(value=(width/2, 0, 0.0))
|
||||
simple.rename('tmprect', '_tmprect')
|
||||
simple.selectMultiple('_tmp')
|
||||
bpy.ops.object.curve_boolean(boolean_type='UNION')
|
||||
bpy.context.active_object.name = "base"
|
||||
simple.removeMultiple('_tmp')
|
||||
simple.rename('base', '_tmpbase')
|
||||
|
||||
simple.rename('receptacle', '_tmpreceptacle')
|
||||
rotate(-math.pi/2)
|
||||
bpy.ops.transform.translate(value=(-width/2, 0, 0.0))
|
||||
simple.selectMultiple("_tmp")
|
||||
|
||||
|
||||
simple.selectMultiple("_") # select everything starting with plate_
|
||||
|
||||
bpy.context.view_layer.objects.active = bpy.data.objects['_tmpbase'] # Make the plate base active
|
||||
bpy.ops.object.curve_boolean(boolean_type='DIFFERENCE')
|
||||
bpy.context.active_object.name = "PUZZLE_bar"
|
||||
simple.removeMultiple("_") # Remove temporary base and holes
|
||||
|
||||
|
||||
|
||||
|
|
@ -224,9 +224,18 @@ def removeMultiple(name):
|
|||
ob.select_set(True)
|
||||
bpy.ops.object.delete()
|
||||
|
||||
|
||||
def makeActive(name):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
ob = bpy.context.scene.objects[name]
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = ob # Make the cube the active object
|
||||
ob.select_set(True)
|
||||
|
||||
|
||||
def activeName(name):
|
||||
bpy.context.active_object.name = "name"
|
||||
|
||||
def rename(name,name2):
|
||||
makeActive(name)
|
||||
bpy.context.active_object.name = name2
|
|
@ -989,6 +989,7 @@ class VIEW3D_PT_tools_create(bpy.types.Panel):
|
|||
layout.operator("object.curve_drawer")
|
||||
layout.operator("object.curve_mortise")
|
||||
layout.operator("object.curve_interlock")
|
||||
layout.operator("object.curve_puzzle")
|
||||
layout.operator("object.sine")
|
||||
layout.operator("object.lissajous")
|
||||
layout.operator("object.hypotrochoid")
|
||||
|
|
Ładowanie…
Reference in New Issue