Source code for fabex.joinery.interlock_twist

from math import (
    asin,
    degrees,
    hypot,
    pi,
)

import bpy

from shapely.geometry import Point

from .finger import fingers
from .mortise import mortise

from ..utilities.logging_utils import log
from ..utilities.simple_utils import (
    duplicate,
    mirror_y,
    union,
    difference,
    active_name,
    move,
    rotate,
    make_active,
    join_multiple,
    remove_doubles,
    add_rectangle,
)


[docs] def interlock_groove(length, thickness, finger_play, cx=0, cy=0, rotation=0): """Generates an interlocking groove. Args: length (float): Length of groove thickness (float): thickness of groove finger_play (float): tolerance for proper fit cx (float): center offset x cy (float): center offset y rotation (float): angle of rotation """ mortise(length, thickness, finger_play, 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)) active_name("_groove")
[docs] def interlock_twist(length, thickness, finger_play, cx=0, cy=0, rotation=0, percentage=0.5): """Generates an interlocking twist. Args: length (float): Length of groove thickness (float): thickness of groove finger_play (float): tolerance for proper fit cx (float): center offset x cy (float): center offset y rotation (float): angle of rotation percentage (float): percentage amount the twist will take (between 0 and 1) """ mortise(length, thickness, finger_play, 0, 0, 0) active_name("_tmp") mortise(length * percentage, thickness, finger_play, 0, 0, pi / 2) active_name("_tmp") h = hypot(thickness, length * percentage) oangle = degrees(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, ) active_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, ) active_name("_tmp") union("_tmp") rotate(rotation) move(x=cx, y=cy) active_name("_groove") remove_doubles()
[docs] def twist_line(length, thickness, finger_play, percentage, amount, distance, center=True): """Generates a multiple interlocking twist. Args: length (float): Length of groove thickness (float): thickness of groove finger_play (float): tolerance for proper fit percentage (float): percentage amount the twist will take (between 0 and 1) amount (int):amount of twists generated distance (float): distance between twists center (bool): center or not from origin """ spacing = distance / amount while amount > 0: position = spacing * amount interlock_twist(length, thickness, finger_play, percentage=percentage, cx=position) log.info(f"Twistline {amount}, {distance}, {position}") amount -= 1 join_multiple("_groove") active_name("twist_line") if center: move(x=(-distance - spacing) / 2)
[docs] def twist_separator_slot(length, thickness, finger_play=0.00005, percentage=0.5): """Generates a slot for interlocking twist separator. Args: length (float): Length of slot thickness (float): thickness of slot finger_play (float): tolerance for proper fit percentage (float): percentage amount the twist will take (between 0 and 1) """ add_rectangle(thickness + finger_play / 2, length, center_y=False) move(y=((length * percentage - finger_play / 2) / 2)) duplicate() mirror_y() join_multiple("simple_rectangle") active_name("_separator_slot")
[docs] def interlock_twist_separator( length, thickness, amount, spacing, edge_distance, finger_play=0.00005, percentage=0.5, start="rounded", end="rounded", ): """Generates a interlocking twist separator. Args: length (float): Length of separator thickness (float): thickness of separator amount (int): quantity of separation grooves spacing (float): distance between slots edge_distance (float): distance of the first slots close to the edge finger_play (float): tolerance for proper fit percentage (float): percentage amount the twist will take (between 0 and 1) start (string): type of start wanted (rounded, flat or other) not implemented start (string): type of end wanted (rounded, flat or other) not implemented """ amount -= 1 base_width = 2 * edge_distance + spacing * amount + thickness add_rectangle(base_width, length - finger_play * 2, center_x=False) active_name("_base") twist_separator_slot(length, thickness, finger_play, percentage) while amount > 0: duplicate(x=spacing) amount -= 1 join_multiple("_separator_slot") move(x=edge_distance + thickness / 2) difference("_", "_base") active_name("twist_separator")
[docs] def single_interlock( finger_depth, finger_thick, finger_tolerance, x, y, groove_angle, type, amount=1, twist_percentage=0.5, ): """Generates a single interlock at coodinate x,y. Args: finger_depth (float): depth of finger finger_thick (float): thickness of finger finger_tolerance (float): tolerance for proper fit x (float): offset x y (float): offset y groove_angle (float): angle of rotation type (str): GROOVE, TWIST, PUZZLE are the valid choices twist_percentage: percentage of thickness for twist (not used in puzzle or groove) """ 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, percentage=twist_percentage, ) elif type == "PUZZLE": fingers(finger_thick, finger_tolerance)
[docs] 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", twist_percentage=0.5, ): """Distributes interlocking joints of a fixed amount. Dynamically changes the finger tolerance with the angle differences Args: loop (list of tuples): coordinates curve loop_length (float): length of the curve finger_depth (float): depth of the mortise finger_thick (float) thickness of the material finger_tolerance (float): minimum finger tolerance finger_amount (int): quantity of fingers tangent (int): fixed_angle (float): 0 will be variable, desired angle for the finger closed (bool): False:open curve - True:closed curved twist_percentage = portion of twist finger which is the stem (for twist joint only) type (str): GROOVE, TWIST, PUZZLE are the valid choices start (float): start distance from first point end (float): end distance from last point """ coords = list(loop.coords) log.info(closed) if not closed: spacing = (loop_length - start - end) / (finger_amount - 1) distance = start end_distance = loop_length - end else: spacing = loop_length / finger_amount distance = 0 end_distance = loop_length j = 0 log.info(f"Joinery Loop Length {round(loop_length * 1000)}mm") log.info(f"Distance Between Joints {round(spacing * 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 and end_distance >= distance: if fixed_angle == 0: groove_angle = angle(oldp, p) + pi / 2 + tangent else: groove_angle = fixed_angle groove_point = loop.interpolate(distance) log.info( f"{j} groove_angle {round(180 * groove_angle / pi)} distance {round(distance * 1000)}mm" ) single_interlock( finger_depth, finger_thick, finger_tolerance, groove_point.x, groove_point.y, groove_angle, type, twist_percentage=twist_percentage, ) j += 1 distance = j * spacing + start oldp = p join_multiple("_groove") active_name("interlock")
[docs] def twist_female(name, length, diameter, tolerance, twist, tneck, tthick, twist_keep=False): """Add a twist lock to a receptacle. This function modifies the receptacle by adding a twist lock feature if the `twist` parameter is set to True. It performs several operations including interlocking the twist, rotating the object, and moving it to the correct position. If `twist_keep` is True, it duplicates the twist lock for further modifications. The function utilizes parameters such as length, diameter, tolerance, and thickness to accurately create the twist lock. Args: name (str): The name of the receptacle to be modified. length (float): The length of the receptacle. diameter (float): The diameter of the receptacle. tolerance (float): The tolerance value for the twist lock. twist (bool): A flag indicating whether to add a twist lock. tneck (float): The neck thickness for the twist lock. tthick (float): The thickness of the twist lock. twist_keep (bool?): A flag indicating whether to keep the twist lock after duplication. Defaults to False. """ # add twist lock to receptacle if twist: interlock_twist(length, tthick, tolerance, cx=0, cy=0, rotation=0, percentage=tneck) rotate(pi / 2) move(y=-tthick / 2 + 2 * diameter + 2 * tolerance) active_name("xtemptwist") if twist_keep: duplicate() active_name("twist_keep_f") make_active(name) active_name("xtemp") union("xtemp") active_name(name)
[docs] def twist_male( name, length, diameter, tolerance, twist, tneck, tthick, angle, twist_keep=False, x=0, y=0 ): """Add a twist lock to a male connector. This function modifies the geometry of a male connector by adding a twist lock feature. It utilizes various parameters to determine the dimensions and positioning of the twist lock. If the `twist_keep` parameter is set to True, it duplicates the twist lock for further modifications. The function also allows for adjustments in position through the `x` and `y` parameters. Args: name (str): The name of the connector to be modified. length (float): The length of the connector. diameter (float): The diameter of the connector. tolerance (float): The tolerance level for the twist lock. twist (bool): A flag indicating whether to add a twist lock. tneck (float): The neck thickness for the twist lock. tthick (float): The thickness of the twist lock. angle (float): The angle at which to rotate the twist lock. twist_keep (bool?): A flag indicating whether to keep the twist lock duplicate. Defaults to False. x (float?): The x-coordinate for positioning. Defaults to 0. y (float?): The y-coordinate for positioning. Defaults to 0. Returns: None: This function modifies the state of the connector but does not return a value. """ # add twist lock to male connector global DT if twist: interlock_twist(length, tthick, tolerance, cx=0, cy=0, rotation=0, percentage=tneck) rotate(pi / 2) move(y=-tthick / 2 + 2 * diameter * DT) rotate(angle) move(x=x, y=y) active_name("_twist") if twist_keep: duplicate() active_name("twist_keep_m") make_active(name) active_name("_tmp") difference("_", "_tmp") active_name(name)