Skip to content

kumiki.joints.workshop.basic_joints

Flat import path

kumiki/__init__.py re-exports everything on this page via from kumiki import *, so every name below is also available directly as kumiki.cut_basic_plain_miter_joint -- you do not need to import from the submodule path shown in the heading above.

kumiki.joints.workshop.basic_joints

Kumiki - Basic joint construction functions

Convenience wrappers (cut_basic_ and cut_basic_plain_) that call the underlying joint functions with sensible default sizing. Use these for quick prototyping; for full control over dimensions and parameters, call the underlying cut_plain_, cut_mortise_and_tenon_, or cut_lapped_* functions directly.

cut_basic_kanawa_tsugi_joint_on_aligned_timbers module-attribute

cut_basic_kanawa_tsugi_joint_on_aligned_timbers = cut_basic_half_blind_tenoned_dadoed_rabbeted_scarf_joint_on_aligned_timbers

cut_basic_plain_miter_joint

cut_basic_plain_miter_joint(arrangement: CornerJointTimberArrangement) -> Joint

Creates a miter joint between two timbers, cutting each end at half the angle between them.

Convenience wrapper with no additional sizing logic. See cut_plain_miter_joint for details.

Parameters:

Name Type Description Default
arrangement CornerJointTimberArrangement

Corner joint arrangement specifying the two timbers and their ends.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_miter_joint(arrangement: CornerJointTimberArrangement) -> Joint:
    """
    Creates a miter joint between two timbers, cutting each end at half the angle between them.

    Convenience wrapper with no additional sizing logic. See `cut_plain_miter_joint` for details.

    Args:
        arrangement: Corner joint arrangement specifying the two timbers and their ends.

    Returns:
        Joint object containing the two CutTimbers.
    """
    return cut_plain_miter_joint(arrangement)

cut_basic_plain_miter_joint_on_face_aligned_timbers

cut_basic_plain_miter_joint_on_face_aligned_timbers(arrangement: CornerJointTimberArrangement) -> Joint

Creates a miter joint between two face-aligned timbers meeting at a 90-degree corner.

Timbers must be orthogonal. Convenience wrapper; see cut_plain_miter_joint_on_face_aligned_timbers for details.

Parameters:

Name Type Description Default
arrangement CornerJointTimberArrangement

Corner joint arrangement. Timbers must be face-aligned and orthogonal.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_miter_joint_on_face_aligned_timbers(arrangement: CornerJointTimberArrangement) -> Joint:
    """
    Creates a miter joint between two face-aligned timbers meeting at a 90-degree corner.

    Timbers must be orthogonal. Convenience wrapper; see
    `cut_plain_miter_joint_on_face_aligned_timbers` for details.

    Args:
        arrangement: Corner joint arrangement. Timbers must be face-aligned and orthogonal.

    Returns:
        Joint object containing the two CutTimbers.
    """
    error = arrangement.check_face_aligned_and_orthogonal()
    assert error is None, error
    return cut_plain_miter_joint_on_face_aligned_timbers(arrangement)

cut_basic_tongue_and_fork_corner_joint_on_plane_aligned_timbers

cut_basic_tongue_and_fork_corner_joint_on_plane_aligned_timbers(arrangement: CornerJointTimberArrangement, tongue_thickness: Optional[Numeric] = None, tongue_position: Numeric = scalar(0)) -> Joint

Creates a tongue-and-fork corner joint (corner bridle style).

Convenience wrapper around cut_tongue_and_fork_corner_joint_on_plane_aligned_timbers. Timbers must be plane-aligned and non-parallel.

Parameters:

Name Type Description Default
arrangement CornerJointTimberArrangement

Corner joint arrangement where timber1 is tongue and timber2 is fork.

required
tongue_thickness Optional[Numeric]

Tongue thickness along shared plane normal. None defaults to 1/3 of tongue timber dimension in that axis.

None
tongue_position Numeric

Offset of tongue center from centerline in shared plane normal axis.

scalar(0)

Returns:

Type Description
Joint

Joint object containing both cut timbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_tongue_and_fork_corner_joint_on_plane_aligned_timbers(
    arrangement: CornerJointTimberArrangement,
    tongue_thickness: Optional[Numeric] = None,
    tongue_position: Numeric = scalar(0),
) -> Joint:
    """
    Creates a tongue-and-fork corner joint (corner bridle style).

    Convenience wrapper around `cut_tongue_and_fork_corner_joint_on_plane_aligned_timbers`. Timbers must be plane-aligned
    and non-parallel.

    Args:
        arrangement: Corner joint arrangement where timber1 is tongue and timber2 is fork.
        tongue_thickness: Tongue thickness along shared plane normal. None defaults to 1/3
            of tongue timber dimension in that axis.
        tongue_position: Offset of tongue center from centerline in shared plane normal axis.

    Returns:
        Joint object containing both cut timbers.
    """
    error = arrangement.check_plane_aligned()
    assert error is None, error
    return cut_tongue_and_fork_corner_joint_on_plane_aligned_timbers(
        arrangement=arrangement,
        tongue_thickness=tongue_thickness,
        tongue_position=tongue_position,
    )

cut_basic_tongue_and_fork_joint_on_plane_aligned_timbers

cut_basic_tongue_and_fork_joint_on_plane_aligned_timbers(arrangement: CornerJointTimberArrangement, tongue_thickness: Optional[Numeric] = None, tongue_position: Numeric = scalar(0)) -> Joint

Compatibility alias for cut_basic_tongue_and_fork_corner_joint_on_plane_aligned_timbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_tongue_and_fork_joint_on_plane_aligned_timbers(
    arrangement: CornerJointTimberArrangement,
    tongue_thickness: Optional[Numeric] = None,
    tongue_position: Numeric = scalar(0),
) -> Joint:
    """Compatibility alias for `cut_basic_tongue_and_fork_corner_joint_on_plane_aligned_timbers`."""
    return cut_basic_tongue_and_fork_corner_joint_on_plane_aligned_timbers(
        arrangement=arrangement,
        tongue_thickness=tongue_thickness,
        tongue_position=tongue_position,
    )

cut_basic_tongue_and_fork_butt_joint_on_plane_aligned_timbers

cut_basic_tongue_and_fork_butt_joint_on_plane_aligned_timbers(arrangement: ButtJointTimberArrangement, tongue_thickness: Optional[Numeric] = None, tongue_position: Numeric = scalar(0), shoulder_inset: Numeric = scalar(0)) -> Joint

Creates a tongue-and-fork butt joint.

Convenience wrapper around cut_tongue_and_fork_butt_joint_on_plane_aligned_timbers. Timbers must be plane-aligned and non-parallel. The receiving (tongue) timber is not end-cut.

Parameters:

Name Type Description Default
arrangement ButtJointTimberArrangement

Butt arrangement where butt_timber is fork and receiving_timber is tongue.

required
tongue_thickness Optional[Numeric]

Tongue thickness along shared plane normal. None defaults to 1/3 of tongue timber dimension in that axis.

None
tongue_position Numeric

Offset of tongue center from centerline in shared plane normal axis.

scalar(0)
shoulder_inset Numeric

Distance from receiving timber entry face to shoulder plane.

scalar(0)

Returns:

Type Description
Joint

Joint object containing both cut timbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_tongue_and_fork_butt_joint_on_plane_aligned_timbers(
    arrangement: ButtJointTimberArrangement,
    tongue_thickness: Optional[Numeric] = None,
    tongue_position: Numeric = scalar(0),
    shoulder_inset: Numeric = scalar(0),
) -> Joint:
    """
    Creates a tongue-and-fork butt joint.

    Convenience wrapper around `cut_tongue_and_fork_butt_joint_on_plane_aligned_timbers`. Timbers must be
    plane-aligned and non-parallel. The receiving (tongue) timber is not end-cut.

    Args:
        arrangement: Butt arrangement where butt_timber is fork and receiving_timber is tongue.
        tongue_thickness: Tongue thickness along shared plane normal. None defaults to 1/3
            of tongue timber dimension in that axis.
        tongue_position: Offset of tongue center from centerline in shared plane normal axis.
        shoulder_inset: Distance from receiving timber entry face to shoulder plane.

    Returns:
        Joint object containing both cut timbers.
    """
    error = arrangement.check_plane_aligned()
    assert error is None, error
    return cut_tongue_and_fork_butt_joint_on_plane_aligned_timbers(
        arrangement=arrangement,
        tongue_thickness=tongue_thickness,
        tongue_position=tongue_position,
        shoulder_inset=shoulder_inset,
    )

cut_basic_plain_butt_joint

cut_basic_plain_butt_joint(arrangement: ButtJointTimberArrangement) -> Joint

Creates a butt joint where the butt timber is cut flush with the receiving timber's face.

The butt timber's end is cut along the plane of the best-matching long face of the receiving timber. Works at any non-parallel angle. The receiving timber is not cut. Convenience wrapper; see cut_plain_butt_joint for details.

Parameters:

Name Type Description Default
arrangement ButtJointTimberArrangement

Butt joint arrangement with butt_timber, receiving_timber, butt_timber_end.

required

Returns:

Type Description
Joint

Joint object containing the cut butt timber and uncut receiving timber.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_butt_joint(arrangement: ButtJointTimberArrangement) -> Joint:
    """
    Creates a butt joint where the butt timber is cut flush with the receiving timber's face.

    The butt timber's end is cut along the plane of the best-matching long face of the
    receiving timber. Works at any non-parallel angle. The receiving timber is not cut.
    Convenience wrapper; see `cut_plain_butt_joint` for details.

    Args:
        arrangement: Butt joint arrangement with butt_timber, receiving_timber, butt_timber_end.

    Returns:
        Joint object containing the cut butt timber and uncut receiving timber.
    """
    return cut_plain_butt_joint(arrangement)

cut_basic_plain_butt_joint_on_face_aligned_timbers

cut_basic_plain_butt_joint_on_face_aligned_timbers(arrangement: ButtJointTimberArrangement) -> Joint

Creates a butt joint where the butt timber is cut flush with the receiving timber's face.

The receiving timber is not cut. Convenience wrapper; see cut_plain_butt_joint_on_face_aligned_timbers for details.

Parameters:

Name Type Description Default
arrangement ButtJointTimberArrangement

Butt joint arrangement. Timbers must be face-aligned and orthogonal.

required

Returns:

Type Description
Joint

Joint object containing the cut butt timber and uncut receiving timber.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_butt_joint_on_face_aligned_timbers(arrangement: ButtJointTimberArrangement) -> Joint:
    """
    Creates a butt joint where the butt timber is cut flush with the receiving timber's face.

    The receiving timber is not cut. Convenience wrapper; see
    `cut_plain_butt_joint_on_face_aligned_timbers` for details.

    Args:
        arrangement: Butt joint arrangement. Timbers must be face-aligned and orthogonal.

    Returns:
        Joint object containing the cut butt timber and uncut receiving timber.
    """
    error = arrangement.check_face_aligned_and_orthogonal()
    assert error is None, error
    return cut_plain_butt_joint_on_face_aligned_timbers(arrangement)

cut_basic_plain_butt_splice_joint_on_aligned_timbers

cut_basic_plain_butt_splice_joint_on_aligned_timbers(arrangement: SpliceJointTimberArrangement) -> Joint

Creates a plain butt splice joint between two parallel timbers cut at a shared plane.

Convenience wrapper with no additional sizing logic. See cut_plain_butt_splice_joint_on_aligned_timbers for details.

Parameters:

Name Type Description Default
arrangement SpliceJointTimberArrangement

Splice joint arrangement. Timbers must be face-aligned with parallel axes.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_butt_splice_joint_on_aligned_timbers(arrangement: SpliceJointTimberArrangement) -> Joint:
    """
    Creates a plain butt splice joint between two parallel timbers cut at a shared plane.

    Convenience wrapper with no additional sizing logic. See
    `cut_plain_butt_splice_joint_on_aligned_timbers` for details.

    Args:
        arrangement: Splice joint arrangement. Timbers must be face-aligned with parallel axes.

    Returns:
        Joint object containing the two CutTimbers.
    """
    error = arrangement.check_face_aligned_and_parallel_axis()
    assert error is None, error
    return cut_plain_butt_splice_joint_on_aligned_timbers(arrangement)

cut_basic_plain_cross_lap_joint_on_face_aligned_timbers

cut_basic_plain_cross_lap_joint_on_face_aligned_timbers(arrangement: CrossJointTimberArrangement) -> Joint

Creates a cross-lap joint between two intersecting timbers with equal material removal.

Material is split equally (half from each timber). Convenience wrapper; see cut_plain_cross_lap_joint for full control over cut faces and split ratio.

Parameters:

Name Type Description Default
arrangement CrossJointTimberArrangement

Cross joint arrangement. Timbers must be face-aligned and orthogonal.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_cross_lap_joint_on_face_aligned_timbers(arrangement: CrossJointTimberArrangement) -> Joint:
    """
    Creates a cross-lap joint between two intersecting timbers with equal material removal.

    Material is split equally (half from each timber). Convenience wrapper; see
    `cut_plain_cross_lap_joint` for full control over cut faces and split ratio.

    Args:
        arrangement: Cross joint arrangement. Timbers must be face-aligned and orthogonal.

    Returns:
        Joint object containing the two CutTimbers.
    """
    error = arrangement.check_face_aligned_and_orthogonal()
    assert error is None, error
    return cut_plain_cross_lap_joint(arrangement)

cut_basic_plain_house_joint_on_face_aligned_timbers

cut_basic_plain_house_joint_on_face_aligned_timbers(arrangement: CrossJointTimberArrangement) -> Joint

Creates a house (dado/housing) joint where the housing timber is relieved to receive the housed timber.

Only the housing timber is cut; the housed timber is unaffected. Convenience wrapper; see cut_plain_cross_lap_house_joint for details.

Parameters:

Name Type Description Default
arrangement CrossJointTimberArrangement

Cross joint arrangement where timber1 is the housing timber and timber2 is the housed timber. Timbers must be face-aligned and orthogonal.

required

Returns:

Type Description
Joint

Joint object containing both timbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_house_joint_on_face_aligned_timbers(arrangement: CrossJointTimberArrangement) -> Joint:
    """
    Creates a house (dado/housing) joint where the housing timber is relieved to receive the housed timber.

    Only the housing timber is cut; the housed timber is unaffected. Convenience wrapper; see
    `cut_plain_cross_lap_house_joint` for details.

    Args:
        arrangement: Cross joint arrangement where timber1 is the housing timber and timber2
                     is the housed timber. Timbers must be face-aligned and orthogonal.

    Returns:
        Joint object containing both timbers.
    """
    error = arrangement.check_face_aligned_and_orthogonal()
    assert error is None, error
    return cut_plain_cross_lap_house_joint(arrangement)

cut_basic_splined_opposing_double_butt_joint_on_face_aligned_timbers

cut_basic_splined_opposing_double_butt_joint_on_face_aligned_timbers(arrangement: DoubleButtJointTimberArrangement, slot_facing_end_on_receiving_timber: TimberEnd) -> Joint

Creates a splined opposing double butt joint with default sizing and a default peg.

This basic wrapper always enables pegs and uses a default peg recipe: - one square peg - distance from shoulder = 30 mm - lateral offset = 0 - peg size = 15 mm

Parameters:

Name Type Description Default
arrangement DoubleButtJointTimberArrangement

Double butt joint arrangement with butt_timber_1, butt_timber_2, receiving_timber, butt_timber_1_end, butt_timber_2_end.

required
slot_facing_end_on_receiving_timber TimberEnd

Receiving-timber end that the slot faces.

required

Returns:

Type Description
Joint

Joint containing all three cut timbers and spline/peg accessories.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_splined_opposing_double_butt_joint_on_face_aligned_timbers(
    arrangement: DoubleButtJointTimberArrangement,
    slot_facing_end_on_receiving_timber: TimberEnd,
) -> Joint:
    """
    Creates a splined opposing double butt joint with default sizing and a default peg.

    This basic wrapper always enables pegs and uses a default peg recipe:
    - one square peg
    - distance from shoulder = 30 mm
    - lateral offset = 0
    - peg size = 15 mm

    Args:
        arrangement: Double butt joint arrangement with butt_timber_1, butt_timber_2,
            receiving_timber, butt_timber_1_end, butt_timber_2_end.
        slot_facing_end_on_receiving_timber: Receiving-timber end that the slot faces.

    Returns:
        Joint containing all three cut timbers and spline/peg accessories.
    """
    # Ensure peg entry direction is defined. If unset, choose the butt-1 long face whose
    # normal is closest to the joint-plane normal so drilling is perpendicular to the joint plane.
    arrangement_with_peg_face = arrangement
    if arrangement.front_face_on_butt_timber_1 is None:
        butt_1_len = arrangement.butt_timber_1.get_length_direction_global()
        receiving_len = arrangement.receiving_timber.get_length_direction_global()
        joint_plane_normal = safe_normalize_vector(cross_product(butt_1_len, receiving_len))
        peg_face = arrangement.butt_timber_1.get_closest_oriented_long_face_from_global_direction(
            joint_plane_normal
        )
        arrangement_with_peg_face = replace(
            arrangement,
            front_face_on_butt_timber_1=peg_face,
        )

    default_peg_parameters = SimplePegParameters(
        shape=PegShape.SQUARE,
        peg_positions=[(mm(30), scalar(0))],
        size=mm(15),
    )

    butt_timber_1 = arrangement_with_peg_face.butt_timber_1
    receiving_timber = arrangement_with_peg_face.receiving_timber

    butt_length_direction_global = butt_timber_1.get_length_direction_global()
    receiving_length_direction_global = receiving_timber.get_length_direction_global()
    slot_direction_global = receiving_timber.get_face_direction_global(
        slot_facing_end_on_receiving_timber
    )
    joint_plane_normal_global = safe_normalize_vector(
        cross_product(butt_length_direction_global, receiving_length_direction_global)
    )

    slot_face_on_butt_1 = butt_timber_1.get_closest_oriented_long_face_from_global_direction(
        slot_direction_global
    )

    slot_thickness = receiving_timber.get_size_in_direction_3d(
        joint_plane_normal_global
    ) / scalar(3)
    slot_depth = butt_timber_1.get_size_in_face_normal_axis(slot_face_on_butt_1) / scalar(2)
    spline_length = receiving_timber.get_size_in_direction_3d(
        butt_length_direction_global
    ) * scalar(4)

    return cut_splined_opposing_double_butt_joint_on_face_aligned_timbers(
        arrangement=arrangement_with_peg_face,
        slot_thickness=slot_thickness,
        slot_depth=slot_depth,
        spline_length=spline_length,
        slot_facing_end_on_receiving_timber=slot_facing_end_on_receiving_timber,
        spline_extra_depth=None,
        slot_symmetric_extra_length=mm(3),
        shoulder_symmetric_inset=scalar(0),
        slot_lateral_offset=scalar(0),
        peg_parameters=default_peg_parameters,
    )

cut_basic_plain_splice_lap_joint_on_aligned_timbers

cut_basic_plain_splice_lap_joint_on_aligned_timbers(arrangement: SpliceJointTimberArrangement) -> Joint

Creates a splice lap joint between two parallel timbers with default half-lap sizing.

The lap length and shoulder position are derived from the timber face dimension indicated by arrangement.front_face_on_timber1 (defaults to FRONT if None). For full control over dimensions, use cut_plain_splice_lap_joint_on_aligned_timbers directly.

Parameters:

Name Type Description Default
arrangement SpliceJointTimberArrangement

Splice joint arrangement. Timbers must be face-aligned with parallel axes. front_face_on_timber1 specifies the lap cut face on timber1.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers with lap cuts.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_plain_splice_lap_joint_on_aligned_timbers(
    arrangement: SpliceJointTimberArrangement,
) -> Joint:
    """
    Creates a splice lap joint between two parallel timbers with default half-lap sizing.

    The lap length and shoulder position are derived from the timber face dimension indicated
    by `arrangement.front_face_on_timber1` (defaults to FRONT if None).
    For full control over dimensions, use `cut_plain_splice_lap_joint_on_aligned_timbers` directly.

    Args:
        arrangement: Splice joint arrangement. Timbers must be face-aligned with parallel axes.
                     `front_face_on_timber1` specifies the lap cut face on timber1.

    Returns:
        Joint object containing the two CutTimbers with lap cuts.
    """
    error = arrangement.check_face_aligned_and_parallel_axis()
    assert error is None, error
    lap_face = arrangement.front_face_on_timber1 if arrangement.front_face_on_timber1 is not None else TimberLongFace.FRONT
    lap_length = arrangement.timber1.get_size_in_face_normal_axis(lap_face)
    return cut_plain_splice_lap_joint_on_aligned_timbers(
        arrangement,
        lap_length,
        top_lap_shoulder_position_from_top_lap_shoulder_timber_end=lap_length
    )

cut_basic_half_blind_tenoned_dadoed_rabbeted_scarf_joint_on_aligned_timbers

cut_basic_half_blind_tenoned_dadoed_rabbeted_scarf_joint_on_aligned_timbers(arrangement: SpliceJointTimberArrangement) -> Joint

Creates a half-blind tenoned, dadoed, rabbeted scarf joint (金輪継ぎ / Kanawa Tsugi) with default proportions.

Stepped shoulder depth is 1/5 of timber 1's dimension in front_face_on_timber1 normal axis. Scarf length is 4× stepped shoulder depth. Dado depth and height are 1/5 of stepped shoulder depth. Stub tenon width is 1/5 of stepped shoulder depth.

Parameters:

Name Type Description Default
arrangement SpliceJointTimberArrangement

Splice joint timber arrangement. Timbers must be face-aligned with parallel axes.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_half_blind_tenoned_dadoed_rabbeted_scarf_joint_on_aligned_timbers(
    arrangement: SpliceJointTimberArrangement,
) -> Joint:
    """
    Creates a half-blind tenoned, dadoed, rabbeted scarf joint (金輪継ぎ / Kanawa Tsugi)
    with default proportions.

    Stepped shoulder depth is 1/5 of timber 1's dimension in front_face_on_timber1 normal axis.
    Scarf length is 4× stepped shoulder depth.
    Dado depth and height are 1/5 of stepped shoulder depth.
    Stub tenon width is 1/5 of stepped shoulder depth.

    Args:
        arrangement: Splice joint timber arrangement. Timbers must be face-aligned with parallel axes.

    Returns:
        Joint object containing the two CutTimbers.
    """
    error = arrangement.check_face_aligned_and_parallel_axis()
    assert error is None, error

    front_face = (
        arrangement.front_face_on_timber1
        if arrangement.front_face_on_timber1 is not None
        else TimberLongFace.FRONT
    )
    if arrangement.front_face_on_timber1 is None:
        arrangement = replace(arrangement, front_face_on_timber1=front_face)

    timber_width = arrangement.timber1.get_size_in_face_normal_axis(front_face.rotate_right())
    stepped_shoulder_depth = timber_width * scalar(1, 5)
    scarf_length = timber_width * scalar(4)
    dado_depth = timber_width * scalar(1, 5)
    dado_height = timber_width * scalar(1, 5)
    stub_tenon_width = timber_width * scalar(1, 5)

    return cut_half_blind_tenoned_dadoed_rabbeted_scarf_joint_on_aligned_timbers(
        arrangement=arrangement,
        stepped_shoulder_depth=stepped_shoulder_depth,
        scarf_length=scarf_length,
        dado_depth=dado_depth,
        dado_height=dado_height,
        stub_tenon_width=stub_tenon_width,
    )

cut_basic_mortise_and_tenon_joint_on_face_aligned_timbers

cut_basic_mortise_and_tenon_joint_on_face_aligned_timbers(tenon_timber: TimberLike, mortise_timber: TimberLike, tenon_end: TimberEnd, use_peg: bool = False) -> Joint

Creates a mortise and tenon joint between two face-aligned orthogonal timbers, with automatic sizing.

Tenon dimensions are derived automatically: 3/4 of the mortise timber's relevant depth for the height and 1/3 of its face width for the width. Tenon length equals the full width of the mortise timber. For full control over sizing, use cut_mortise_and_tenon_joint_on_face_aligned_timbers directly.

Parameters:

Name Type Description Default
tenon_timber TimberLike

The timber that will receive the tenon cut.

required
mortise_timber TimberLike

The timber that will receive the mortise hole.

required
tenon_end TimberEnd

Which end of the tenon timber gets the tenon (TOP or BOTTOM).

required
use_peg bool

If True, adds a square peg through the joint for draw-bore tightening.

False

Returns:

Type Description
Joint

Joint object containing the two CutTimbers and, if use_peg=True, a Peg accessory.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_mortise_and_tenon_joint_on_face_aligned_timbers(
    tenon_timber: TimberLike,
    mortise_timber: TimberLike,
    tenon_end: TimberEnd,
    use_peg: bool = False,
) -> Joint:
    """
    Creates a mortise and tenon joint between two face-aligned orthogonal timbers, with automatic sizing.

    Tenon dimensions are derived automatically: 3/4 of the mortise timber's relevant depth for
    the height and 1/3 of its face width for the width. Tenon length equals the full width of the
    mortise timber. For full control over sizing, use `cut_mortise_and_tenon_joint_on_face_aligned_timbers` directly.

    Args:
        tenon_timber: The timber that will receive the tenon cut.
        mortise_timber: The timber that will receive the mortise hole.
        tenon_end: Which end of the tenon timber gets the tenon (TOP or BOTTOM).
        use_peg: If True, adds a square peg through the joint for draw-bore tightening.

    Returns:
        Joint object containing the two CutTimbers and, if use_peg=True, a Peg accessory.
    """
    assert isinstance(tenon_end, TimberEnd), f"expected TimberEnd, got {type(tenon_end).__name__}"
    # this is the "side" of the joint
    joint_side_mortise_timber_face = mortise_timber.get_closest_oriented_face_from_global_direction(cross_product(mortise_timber.get_length_direction_global(), tenon_timber.get_face_direction_global(tenon_end.to.face())))
    joint_side_tenon_timber_face = tenon_timber.get_closest_oriented_face_from_global_direction(mortise_timber.get_face_direction_global(joint_side_mortise_timber_face))

    # the sizing XY depends on the orientation of the tenon timber relative to the mortise timber
    mortise_length_on_tenon_timber_face = tenon_timber.get_closest_oriented_face_from_global_direction(mortise_timber.get_length_direction_global())

    mortise_timber_entry_face = joint_side_mortise_timber_face.to.long_face().rotate_right().to.face()

    tenon_mortise_length_size = tenon_timber.get_size_in_face_normal_axis(mortise_length_on_tenon_timber_face)*scalar(3,4)
    tenon_mortise_width_size = mortise_timber.get_size_in_face_normal_axis(joint_side_mortise_timber_face)*scalar(1,3)

    if mortise_length_on_tenon_timber_face == TimberLongFace.FRONT or mortise_length_on_tenon_timber_face == TimberLongFace.BACK:
        tenon_size = Matrix([tenon_mortise_length_size, tenon_mortise_width_size])
    else:
        tenon_size = Matrix([tenon_mortise_width_size, tenon_mortise_length_size])


    tenon_length = mortise_timber.get_size_in_face_normal_axis(mortise_timber_entry_face)
    mortise_depth = tenon_length

    tenon_position = create_v2(scalar(0), scalar(0))
    peg_parameters = None
    front_face_on_butt_timber = None
    if use_peg:
        front_face_on_butt_timber = joint_side_tenon_timber_face.to.long_face()
        peg_parameters = SimplePegParameters(
            shape=PegShape.SQUARE,
            peg_positions=[cast(Tuple[Numeric, Numeric], (tenon_length / 3, 0))],
            size=inches(1, 2),
            depth=None,
            tenon_hole_offset=inches(scalar(1, 16))
        )

    arrangement = ButtJointTimberArrangement(
        receiving_timber=cast(Timber, mortise_timber),
        butt_timber=cast(Timber, tenon_timber),
        butt_timber_end=tenon_end,
        front_face_on_butt_timber=front_face_on_butt_timber,
    )
    return cut_mortise_and_tenon_joint_on_face_aligned_timbers(
        arrangement=arrangement,
        tenon_width_relative_to_joint=tenon_mortise_width_size,
        tenon_height_relative_to_joint=tenon_mortise_length_size,
        tenon_length=tenon_length,
        mortise_depth=mortise_depth,
        tenon_position=tenon_position,
        peg_parameters=peg_parameters,
    )

cut_basic_wedged_half_dovetail_mortise_and_tenon_joint_on_face_aligned_timbers

cut_basic_wedged_half_dovetail_mortise_and_tenon_joint_on_face_aligned_timbers(tenon_timber: TimberLike, mortise_timber: TimberLike, tenon_end: TimberEnd, use_wedge: bool = True) -> Joint

Creates a half-dovetail mortise-and-tenon joint between two face-aligned orthogonal timbers, with automatic sizing and an optional wedge.

Tenon dimensions are derived automatically: full size of the tenon timber along the axis parallel to the mortise timber's length (this is also the dovetail's slope axis -- the flat dovetail "top" must run along the receiving timber's length axis for its pull-out resistance to sit along the joint's load axis), and 1/3 of the mortise timber's size along the perpendicular axis. The tenon is a through-tenon (tenon_depth = the mortise timber's full size along the entry axis), and dovetail_depth is 1/4 of the tenon's size in the dovetail (length) axis. When use_wedge is True, the wedge's taper angle matches the dovetail's own slope and its narrow end (wedge_small_height) is 1/8 of the tenon's size in the dovetail axis. For full control over sizing, use cut_wedged_half_dovetail_mortise_and_tenon_joint_on_face_aligned_timbers directly.

Parameters:

Name Type Description Default
tenon_timber TimberLike

The timber that will receive the dovetail tenon cut.

required
mortise_timber TimberLike

The timber that will receive the dovetail mortise.

required
tenon_end TimberEnd

Which end of the tenon timber gets the tenon (TOP or BOTTOM).

required
use_wedge bool

If True, adds a wedge accessory (and matching slot) on the dovetail's flat side, tapered to match the dovetail's own slope angle.

True

Returns:

Type Description
Joint

Joint object containing the two CutTimbers and, if use_wedge=True, a "wedge" accessory.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_wedged_half_dovetail_mortise_and_tenon_joint_on_face_aligned_timbers(
    tenon_timber: TimberLike,
    mortise_timber: TimberLike,
    tenon_end: TimberEnd,
    use_wedge: bool = True,
) -> Joint:
    """
    Creates a half-dovetail mortise-and-tenon joint between two face-aligned orthogonal
    timbers, with automatic sizing and an optional wedge.

    Tenon dimensions are derived automatically: full size of the tenon timber along the axis
    parallel to the mortise timber's length (this is also the dovetail's slope axis -- the
    flat dovetail "top" must run along the receiving timber's length axis for its pull-out
    resistance to sit along the joint's load axis), and 1/3 of the mortise timber's size
    along the perpendicular axis. The tenon is a through-tenon (tenon_depth = the mortise
    timber's full size along the entry axis), and dovetail_depth is 1/4 of the tenon's size
    in the dovetail (length) axis. When use_wedge is True, the wedge's taper angle matches
    the dovetail's own slope and its narrow end (wedge_small_height) is 1/8 of the tenon's
    size in the dovetail axis. For full control over sizing, use
    `cut_wedged_half_dovetail_mortise_and_tenon_joint_on_face_aligned_timbers` directly.

    Args:
        tenon_timber: The timber that will receive the dovetail tenon cut.
        mortise_timber: The timber that will receive the dovetail mortise.
        tenon_end: Which end of the tenon timber gets the tenon (TOP or BOTTOM).
        use_wedge: If True, adds a wedge accessory (and matching slot) on the dovetail's
            flat side, tapered to match the dovetail's own slope angle.

    Returns:
        Joint object containing the two CutTimbers and, if use_wedge=True, a "wedge" accessory.
    """

    assert isinstance(tenon_end, TimberEnd), f"expected TimberEnd, got {type(tenon_end).__name__}"
    assert isinstance(tenon_timber, TimberLike), f"expected TimberLike, got {type(tenon_timber).__name__}"
    assert isinstance(mortise_timber, TimberLike), f"expected TimberLike, got {type(mortise_timber).__name__}"

    # Same "side of the joint" / "length axis" derivation as cut_basic_mortise_and_tenon_joint_on_face_aligned_timbers.
    joint_side_mortise_timber_face = mortise_timber.get_closest_oriented_face_from_global_direction(cross_product(mortise_timber.get_length_direction_global(), tenon_timber.get_face_direction_global(tenon_end.to.face())))
    joint_side_tenon_timber_face = tenon_timber.get_closest_oriented_face_from_global_direction(mortise_timber.get_face_direction_global(joint_side_mortise_timber_face))
    mortise_length_on_tenon_timber_face = tenon_timber.get_closest_oriented_face_from_global_direction(mortise_timber.get_length_direction_global())

    tenon_end_direction = tenon_timber.get_face_direction_global(tenon_end.to.face())
    mortise_entry_face = mortise_timber.get_closest_oriented_long_face_from_global_direction(-tenon_end_direction).to.face()

    # Dovetail's flat "top" (and its slope) must run along the axis parallel to the
    # receiving timber's length -- that's a hard requirement of dovetail_tenon_geometry.
    tenon_dovetail_axis_full_size = tenon_timber.get_size_in_face_normal_axis(mortise_length_on_tenon_timber_face)
    tenon_dovetail_axis_dovetail_depth = tenon_dovetail_axis_full_size * scalar(1, 4)
    tenon_dovetail_axis_size = tenon_dovetail_axis_full_size - tenon_dovetail_axis_dovetail_depth
    tenon_other_axis_size = mortise_timber.get_size_in_face_normal_axis(joint_side_mortise_timber_face)*scalar(1, 3)

    if mortise_length_on_tenon_timber_face == TimberLongFace.FRONT or mortise_length_on_tenon_timber_face == TimberLongFace.BACK:
        tenon_size = Matrix([tenon_dovetail_axis_size, tenon_other_axis_size])
    else:
        tenon_size = Matrix([tenon_other_axis_size, tenon_dovetail_axis_size])

    tenon_depth = mortise_timber.get_size_in_face_normal_axis(mortise_entry_face)

    wedge_accessory_parameters = DovetailTenonWedgeAccessoryParameters(
        wedge_angle=atan(tenon_dovetail_axis_dovetail_depth / tenon_depth),
        wedge_extra_height=0
    )

    arrangement = ButtJointTimberArrangement(
        receiving_timber=cast(Timber, mortise_timber),
        butt_timber=cast(Timber, tenon_timber),
        butt_timber_end=tenon_end,
        top_face_on_butt_timber=mortise_length_on_tenon_timber_face.to.long_face(),
    )
    return cut_wedged_half_dovetail_mortise_and_tenon_joint_on_face_aligned_timbers(
        arrangement=arrangement,
        tenon_size=tenon_size,
        tenon_depth=tenon_depth,
        dovetail_depth=tenon_dovetail_axis_dovetail_depth,
        wedge_accessory_parameters=wedge_accessory_parameters,
    )

cut_basic_lapped_gooseneck_joint_on_aligned_timbers

cut_basic_lapped_gooseneck_joint_on_aligned_timbers(gooseneck_timber: TimberLike, receiving_timber: TimberLike, receiving_timber_end: TimberEnd, gooseneck_timber_face: TimberLongFace) -> Joint

Creates a lapped gooseneck splice joint (腰掛鎌継ぎ / Koshikake Kama Tsugi) with default proportions.

Gooseneck dimensions scale with the timber width: length = 2×width, small_width = 1/4×width, large_width = 1/2×width, head_length = 1/2×width. For full control, use cut_lapped_gooseneck_joint_on_aligned_timbers directly.

Parameters:

Name Type Description Default
gooseneck_timber TimberLike

The timber with the gooseneck feature cut into it.

required
receiving_timber TimberLike

The timber that receives the gooseneck.

required
receiving_timber_end TimberEnd

Which end of the receiving timber is joined.

required
gooseneck_timber_face TimberLongFace

The face on the gooseneck timber where the profile is visible.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers with gooseneck cuts.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_lapped_gooseneck_joint_on_aligned_timbers(
    gooseneck_timber: TimberLike,
    receiving_timber: TimberLike,
    receiving_timber_end: TimberEnd,
    gooseneck_timber_face: TimberLongFace,
) -> Joint:
    """
    Creates a lapped gooseneck splice joint (腰掛鎌継ぎ / Koshikake Kama Tsugi) with default proportions.

    Gooseneck dimensions scale with the timber width: length = 2×width, small_width = 1/4×width,
    large_width = 1/2×width, head_length = 1/2×width. For full control, use
    `cut_lapped_gooseneck_joint_on_aligned_timbers` directly.

    Args:
        gooseneck_timber: The timber with the gooseneck feature cut into it.
        receiving_timber: The timber that receives the gooseneck.
        receiving_timber_end: Which end of the receiving timber is joined.
        gooseneck_timber_face: The face on the gooseneck timber where the profile is visible.

    Returns:
        Joint object containing the two CutTimbers with gooseneck cuts.
    """
    assert isinstance(receiving_timber_end, TimberEnd), f"expected TimberEnd, got {type(receiving_timber_end).__name__}"
    assert isinstance(gooseneck_timber_face, TimberLongFace), f"expected TimberLongFace, got {type(gooseneck_timber_face).__name__}"
    assert isinstance(gooseneck_timber, Timber), f"expected Timber, got {type(gooseneck_timber).__name__}"
    assert isinstance(receiving_timber, Timber), f"expected Timber, got {type(receiving_timber).__name__}"
    width = gooseneck_timber.get_size_in_face_normal_axis(gooseneck_timber_face.rotate_right())
    gooseneck_length = width*scalar(2)
    gooseneck_small_width = width*scalar(1, 4)
    gooseneck_large_width = width*scalar(1, 2)
    gooseneck_head_length = width*scalar(1, 2)
    gooseneck_timber_end = TimberEnd.BOTTOM if receiving_timber_end == TimberEnd.TOP else TimberEnd.TOP

    return cut_lapped_gooseneck_joint_on_aligned_timbers(
        arrangement=SpliceJointTimberArrangement(
            timber1=gooseneck_timber,
            timber2=receiving_timber,
            timber1_end=gooseneck_timber_end,
            timber2_end=receiving_timber_end,
            front_face_on_timber1=gooseneck_timber_face,
        ),
        gooseneck_length=gooseneck_length,
        gooseneck_small_width=gooseneck_small_width,
        gooseneck_large_width=gooseneck_large_width,
        gooseneck_head_length=gooseneck_head_length
    )

cut_basic_dropin_dovetail_butt_joint_on_face_aligned_timbers

cut_basic_dropin_dovetail_butt_joint_on_face_aligned_timbers(dovetail_timber: TimberLike, receiving_timber: TimberLike, dovetail_timber_end: TimberEnd, dovetail_timber_face: TimberLongFace, receiving_timber_shoulder_inset: Numeric, dovetail_length: Numeric, dovetail_small_width: Numeric, dovetail_large_width: Numeric) -> Joint

Creates a housed dovetail butt joint (蟻継ぎ / Ari Tsugi) with default proportions.

Dovetail dimensions scale with the timber width regardless of the values passed for dovetail_length, dovetail_small_width, and dovetail_large_width — those parameters are overridden internally (present for API compatibility). For full control, use cut_dropin_dovetail_butt_joint_on_face_aligned_timbers directly.

Parameters:

Name Type Description Default
dovetail_timber TimberLike

The timber with the dovetail tenon.

required
receiving_timber TimberLike

The timber that receives the dovetail socket.

required
dovetail_timber_end TimberEnd

Which end of the dovetail timber is cut.

required
dovetail_timber_face TimberLongFace

The face on the dovetail timber where the profile is visible.

required
receiving_timber_shoulder_inset Numeric

Distance to inset the shoulder notch on the receiving timber.

required
dovetail_length Numeric

Overridden internally by default proportions.

required
dovetail_small_width Numeric

Overridden internally by default proportions.

required
dovetail_large_width Numeric

Overridden internally by default proportions.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers with dovetail cuts.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_dropin_dovetail_butt_joint_on_face_aligned_timbers(
    dovetail_timber: TimberLike,
    receiving_timber: TimberLike,
    dovetail_timber_end: TimberEnd,
    dovetail_timber_face: TimberLongFace,
    receiving_timber_shoulder_inset: Numeric,
    dovetail_length: Numeric,
    dovetail_small_width: Numeric,
    dovetail_large_width: Numeric,
) -> Joint:
    """
    Creates a housed dovetail butt joint (蟻継ぎ / Ari Tsugi) with default proportions.

    Dovetail dimensions scale with the timber width regardless of the values passed for
    dovetail_length, dovetail_small_width, and dovetail_large_width — those parameters are
    overridden internally (present for API compatibility). For full control, use
    `cut_dropin_dovetail_butt_joint_on_face_aligned_timbers` directly.

    Args:
        dovetail_timber: The timber with the dovetail tenon.
        receiving_timber: The timber that receives the dovetail socket.
        dovetail_timber_end: Which end of the dovetail timber is cut.
        dovetail_timber_face: The face on the dovetail timber where the profile is visible.
        receiving_timber_shoulder_inset: Distance to inset the shoulder notch on the receiving timber.
        dovetail_length: Overridden internally by default proportions.
        dovetail_small_width: Overridden internally by default proportions.
        dovetail_large_width: Overridden internally by default proportions.

    Returns:
        Joint object containing the two CutTimbers with dovetail cuts.
    """
    assert isinstance(dovetail_timber_end, TimberEnd), f"expected TimberEnd, got {type(dovetail_timber_end).__name__}"
    assert isinstance(dovetail_timber_face, TimberLongFace), f"expected TimberLongFace, got {type(dovetail_timber_face).__name__}"
    assert isinstance(dovetail_timber, TimberLike), f"expected TimberLike, got {type(dovetail_timber).__name__}"
    assert isinstance(receiving_timber, TimberLike), f"expected TimberLike, got {type(receiving_timber).__name__}"
    width = dovetail_timber.get_size_in_face_normal_axis(dovetail_timber_face.rotate_right())
    dovetail_length = width/scalar(2)
    dovetail_small_width = width*scalar(1, 2)
    dovetail_large_width = width*scalar(2, 3)

    return cut_dropin_dovetail_butt_joint_on_face_aligned_timbers(
        arrangement=ButtJointTimberArrangement(
            butt_timber=dovetail_timber,
            receiving_timber=receiving_timber,
            butt_timber_end=dovetail_timber_end,
            front_face_on_butt_timber=dovetail_timber_face,
        ),
        receiving_timber_shoulder_inset=receiving_timber_shoulder_inset,
        dovetail_length=dovetail_length,
        dovetail_small_width=dovetail_small_width,
        dovetail_large_width=dovetail_large_width
    )

cut_basic_dropin_housed_butt_joint_on_face_aligned_timbers

cut_basic_dropin_housed_butt_joint_on_face_aligned_timbers(housed_timber: TimberLike, receiving_timber: TimberLike, housed_timber_end: TimberEnd, housed_timber_face: TimberLongFace, receiving_timber_shoulder_inset: Numeric = scalar(0)) -> Joint

Creates a drop-in housed butt joint (大入れ仕口 / Oire Shiguchi) with default proportions.

All housing dimensions (housing_length and housing_width) are auto-calculated from the housed timber's size. For full control, use cut_dropin_housed_butt_joint_on_face_aligned_timbers directly.

Parameters:

Name Type Description Default
housed_timber TimberLike

The timber with the housed end.

required
receiving_timber TimberLike

The timber that receives the housing pocket.

required
housed_timber_end TimberEnd

Which end of the housed timber is cut.

required
housed_timber_face TimberLongFace

The face on the housed timber where the pocket profile is open (top face).

required
receiving_timber_shoulder_inset Numeric

Distance to inset the shoulder notch. Default 0.

scalar(0)

Returns:

Type Description
Joint

Joint object containing the two CutTimbers with housing cuts.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_dropin_housed_butt_joint_on_face_aligned_timbers(
    housed_timber: TimberLike,
    receiving_timber: TimberLike,
    housed_timber_end: TimberEnd,
    housed_timber_face: TimberLongFace,
    receiving_timber_shoulder_inset: Numeric = scalar(0),
) -> Joint:
    """
    Creates a drop-in housed butt joint (大入れ仕口 / Oire Shiguchi) with default proportions.

    All housing dimensions (housing_length and housing_width) are auto-calculated from the
    housed timber's size. For full control, use `cut_dropin_housed_butt_joint_on_face_aligned_timbers` directly.

    Args:
        housed_timber: The timber with the housed end.
        receiving_timber: The timber that receives the housing pocket.
        housed_timber_end: Which end of the housed timber is cut.
        housed_timber_face: The face on the housed timber where the pocket profile is open (top face).
        receiving_timber_shoulder_inset: Distance to inset the shoulder notch. Default 0.

    Returns:
        Joint object containing the two CutTimbers with housing cuts.
    """
    assert isinstance(housed_timber_end, TimberEnd), f"expected TimberEnd, got {type(housed_timber_end).__name__}"
    assert isinstance(housed_timber_face, TimberLongFace), f"expected TimberLongFace, got {type(housed_timber_face).__name__}"
    assert isinstance(housed_timber, TimberLike), f"expected TimberLike, got {type(housed_timber).__name__}"
    assert isinstance(receiving_timber, TimberLike), f"expected TimberLike, got {type(receiving_timber).__name__}"

    width = housed_timber.get_size_in_face_normal_axis(housed_timber_face.rotate_right())
    housing_length = width / scalar(2)
    housing_width = width

    return cut_dropin_housed_butt_joint_on_face_aligned_timbers(
        arrangement=ButtJointTimberArrangement(
            receiving_timber=receiving_timber,
            butt_timber=housed_timber,
            butt_timber_end=housed_timber_end,
            front_face_on_butt_timber=housed_timber_face,
        ),
        receiving_timber_shoulder_inset=receiving_timber_shoulder_inset,
        housing_length=housing_length,
        housing_width=housing_width,
    )

cut_basic_mitered_and_keyed_lap_joint_on_plane_aligned_timbers

cut_basic_mitered_and_keyed_lap_joint_on_plane_aligned_timbers(arrangement: CornerJointTimberArrangement) -> Joint

Creates a mitered and keyed lap joint (箱相欠き車知栓仕口 / Hako Aikaki Shachi Sen Shikuchi) with default proportions.

Combines a miter cut with interlocking finger laps on the inside of the corner for mechanical strength. All lap and key dimensions are auto-calculated. For full control, use cut_mitered_and_keyed_lap_joint_on_plane_aligned_timbers directly.

Parameters:

Name Type Description Default
arrangement CornerJointTimberArrangement

Corner joint arrangement. Timbers must be plane-aligned and front_face_on_timber1 must specify the reference miter face on timber1.

required

Returns:

Type Description
Joint

Joint object containing the two CutTimbers with miter and finger cuts.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_mitered_and_keyed_lap_joint_on_plane_aligned_timbers(
    arrangement: CornerJointTimberArrangement,
) -> Joint:
    """
    Creates a mitered and keyed lap joint (箱相欠き車知栓仕口 / Hako Aikaki Shachi Sen Shikuchi) with default proportions.

    Combines a miter cut with interlocking finger laps on the inside of the corner for
    mechanical strength. All lap and key dimensions are auto-calculated. For full control,
    use `cut_mitered_and_keyed_lap_joint_on_plane_aligned_timbers` directly.

    Args:
        arrangement: Corner joint arrangement. Timbers must be plane-aligned and
            `front_face_on_timber1` must specify the reference miter face on timber1.

    Returns:
        Joint object containing the two CutTimbers with miter and finger cuts.
    """
    error = arrangement.check_plane_aligned()
    assert error is None, error
    assert arrangement.front_face_on_timber1 is not None, (
        "arrangement.front_face_on_timber1 must be set to determine the reference miter face"
    )
    return cut_mitered_and_keyed_lap_joint_on_plane_aligned_timbers(
        arrangement=arrangement
    )

cut_basic_practice_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers

cut_basic_practice_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers(arrangement: ButtJointTimberArrangement) -> Joint

Creates a tusked through mortise-and-tenon joint with automatic sizing.

front_face_on_butt_timber/top_face_on_butt_timber are auto-derived when not already set on the arrangement: front_face is the butt timber's long face parallel to the joint alignment plane, and top_face is 90 degrees from it (the tusk enters from this face, since TuskParameters.entry_face defaults to Front). Tenon dimensions are derived automatically: 1/3 of the butt timber's size along the front-face axis, and 4/5 of its size along the top-face axis. For full control over sizing, use cut_practice_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers directly.

Parameters:

Name Type Description Default
arrangement ButtJointTimberArrangement

Butt joint arrangement (butt_timber = tenon, receiving_timber = mortise). Must be plane-aligned.

required

Returns:

Type Description
Joint

Joint object with cuts on both timbers and a "tusk" accessory.

Source code in kumiki/joints/workshop/basic_joints.py
def cut_basic_practice_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers(
    arrangement: ButtJointTimberArrangement,
) -> Joint:
    """
    Creates a tusked through mortise-and-tenon joint with automatic sizing.

    front_face_on_butt_timber/top_face_on_butt_timber are auto-derived when not already set
    on the arrangement: front_face is the butt timber's long face parallel to the joint
    alignment plane, and top_face is 90 degrees from it (the tusk enters from this face, since
    TuskParameters.entry_face defaults to Front). Tenon dimensions are derived automatically:
    1/3 of the butt timber's size along the front-face axis, and 4/5 of its size along the
    top-face axis. For full control over sizing, use
    `cut_practice_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers` directly.

    Args:
        arrangement: Butt joint arrangement (butt_timber = tenon, receiving_timber = mortise).
            Must be plane-aligned.

    Returns:
        Joint object with cuts on both timbers and a "tusk" accessory.
    """
    error = arrangement.check_plane_aligned()
    assert error is None, error

    front_face = arrangement.front_face_on_butt_timber
    if front_face is None:
        front_face = arrangement.butt_timber.get_closest_oriented_long_face_from_global_direction(arrangement.compute_normalized_timber_cross_product())
        arrangement = replace(arrangement, front_face_on_butt_timber=front_face)
    top_face = arrangement.top_face_on_butt_timber
    if top_face is None:
        top_face = front_face.rotate_right()
        arrangement = replace(arrangement, top_face_on_butt_timber=top_face)

    # tenon_size[0]/[1] are always the butt timber's RIGHT/LEFT-axis / FRONT/BACK-axis sizes
    # respectively (the tenon-cutting code below indexes tenon_size that way regardless of
    # which face is labeled "front"), so swap front/top into the right slot depending on
    # which axis front_face actually landed on.
    front_face_size = arrangement.butt_timber.get_size_in_face_normal_axis(front_face) / scalar(3)
    top_face_size = arrangement.butt_timber.get_size_in_face_normal_axis(top_face) * scalar(4/5)
    if front_face in (TimberLongFace.RIGHT, TimberLongFace.LEFT):
        tenon_size = create_v2(front_face_size, top_face_size)
    else:
        tenon_size = create_v2(top_face_size, front_face_size)

    mortise_insertion_face = arrangement.receiving_timber.get_closest_oriented_long_face_from_global_direction(-arrangement.butt_timber.get_face_direction_global(arrangement.butt_timber_end))
    # center to center
    mortise_length = arrangement.receiving_timber.get_size_in_face_normal_axis(mortise_insertion_face)
    acute_angle = arrangement.compute_arrangement_acute_angle()
    # center to tip
    mortise_length_tip = mortise_length + top_face_size * cos(acute_angle) / 2
    tenon_length_past_opposite_shoulder = max(mortise_length_tip, front_face_size*2)

    tusk = TuskParameters(
        tusk_thickness = top_face_size * scalar(2/5),
        tusk_small_width = min(tenon_length_past_opposite_shoulder/scalar(2), min(front_face_size, top_face_size))
    )

    return cut_practice_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers(
        arrangement=arrangement,
        tenon_size=tenon_size,
        tenon_length_past_opposite_shoulder=tenon_length_past_opposite_shoulder,
        tusk_parameters=tusk,
    )