Skip to content

Spool dryer

SpoolDryer

SpoolDryer(clearance=0.1)

Generates a box that fits in filament spool

Put some dessicant in it and keep the spools dry.

Parameters:

Name Type Description Default
clearance float

Optional assembly clearance. A good value is two times the diameter of the printing nozzle

0.1
Source code in src/bd_el_parts/spool_dryer.py
def __init__(self, clearance: float = 0.1):
    """
    {% 3d: /models/spool_dryer_assembly.gltf }

    Generates a box that fits in filament spool

    Put some dessicant in it and keep the spools dry.

    Args:
        clearance (float): Optional assembly clearance. A good value is two times the diameter of the printing nozzle
    """
    self.clearance = clearance

ALIGN_Z_MAX class-attribute instance-attribute

ALIGN_Z_MAX = (CENTER, CENTER, MAX)

ALIGN_Z_MIN class-attribute instance-attribute

ALIGN_Z_MIN = (CENTER, CENTER, MIN)

EXT_HEIGHT class-attribute instance-attribute

EXT_HEIGHT = 62.5

EXT_RADIUS class-attribute instance-attribute

EXT_RADIUS = 54 / 2

HOLES_RADIUS class-attribute instance-attribute

HOLES_RADIUS = 1

LID_GRIP_THICKNESS class-attribute instance-attribute

LID_GRIP_THICKNESS = 4

LID_SHOULDER class-attribute instance-attribute

LID_SHOULDER = 1.5

LID_THICKNESS class-attribute instance-attribute

LID_THICKNESS = 8

THICKNESS class-attribute instance-attribute

THICKNESS = 1.5

TOWER_HEIGHT class-attribute instance-attribute

TOWER_HEIGHT = (
    EXT_HEIGHT
    - THICKNESS * 2
    - LID_THICKNESS
    - LID_SHOULDER
)

TOWER_RADIUS class-attribute instance-attribute

TOWER_RADIUS = 10

clearance instance-attribute

clearance = clearance

assembly

assembly()

Generates a Compound with component bound by joints

Source code in src/bd_el_parts/spool_dryer.py
def assembly(self):
    """
    Generates a Compound with component bound by joints
    """
    body = self.body()
    lid = self.lid()
    lid_clip = self.lid_clip()

    lid.joints["top"].connect_to(body.joints["top"])
    lid.joints["top"].connect_to(lid_clip.joints["lid_top"])

    return Compound([body, lid, lid_clip])

body

body()

Generates the body

Joints

top: Represents the center at the top of the box

Source code in src/bd_el_parts/spool_dryer.py
def body(self):
    """
    Generates the body

    Joints:
        `top`: Represents the center at the top of the box
    """
    top_location = (0, 0, self.EXT_HEIGHT / 2)
    bottom_location = (0, 0, -self.EXT_HEIGHT / 2)

    # Base
    solid = Cylinder(self.EXT_RADIUS, self.EXT_HEIGHT)

    # Inner volume
    solid -= (
        Location(bottom_location)
        * Cylinder(self.EXT_RADIUS - self.THICKNESS, self.TOWER_HEIGHT + self.THICKNESS, align=self.ALIGN_Z_MIN)
    )  # fmt:off

    # Bottom stop
    bottom_stop = (
        Cylinder(self.EXT_RADIUS + self.THICKNESS, self.THICKNESS, align=self.ALIGN_Z_MAX)
        - Cylinder(self.TOWER_RADIUS - self.THICKNESS, self.THICKNESS, align=self.ALIGN_Z_MAX)
    )  # fmt:off
    solid += Location(bottom_location) * bottom_stop

    # Inner cylinder
    tower = Cylinder(self.TOWER_RADIUS, self.TOWER_HEIGHT, align=self.ALIGN_Z_MIN)
    tower = chamfer(tower.edges().filter_by(GeomType.CIRCLE).sort_by(Axis.Z)[-1], self.TOWER_RADIUS - 0.1)
    inner_tower = Cylinder(self.TOWER_RADIUS - self.THICKNESS, self.TOWER_HEIGHT - self.THICKNESS, align=self.ALIGN_Z_MIN)
    inner_tower = chamfer(
        inner_tower.edges().filter_by(GeomType.CIRCLE).sort_by(Axis.Z)[-1],
        self.TOWER_RADIUS - self.THICKNESS - 0.1
    )  # fmt: off
    tower -= inner_tower

    inner_cylinder_air_hole = Rotation(X=90) * Cylinder(self.HOLES_RADIUS, self.TOWER_RADIUS * 3)
    inner_cylinder_air_holes_base = Solid()
    amount = 8
    for i in range(amount):
        inner_cylinder_air_holes_base += Rotation((0, 0, 180 / amount * i)) * inner_cylinder_air_hole
    distribution_height = self.TOWER_HEIGHT - self.TOWER_RADIUS - self.THICKNESS
    amount = math.floor(distribution_height / (self.HOLES_RADIUS * 2 + self.THICKNESS))
    step = distribution_height / amount

    tower_holes = Solid()
    for i in range(amount):
        tower_holes += Location((0, 0, self.THICKNESS + self.HOLES_RADIUS + i * step)) * inner_cylinder_air_holes_base
    tower -= tower_holes

    solid += Location(bottom_location) * tower

    # Lock
    lock_hole = Location(top_location, (0, 180, 0)) * SlidingHole(
        cap_lid_hole_radius=self.EXT_RADIUS - self.THICKNESS * 2,
        cap_height=self.LID_THICKNESS,
        cap_outer_thickness=self.THICKNESS,
        lid_shoulder=self.LID_SHOULDER,
        lid_sliders=3,
    )
    solid -= lock_hole

    # Bottom holes
    bottom_holes = self._holes(
        radius=self.EXT_RADIUS,
        inner_radius=self.TOWER_RADIUS,
        location=(0, 0, -((self.EXT_HEIGHT - self.THICKNESS) / 2))
    )  # fmt: off
    solid -= bottom_holes

    solid = chamfer(solid.edges().filter_by(GeomType.CIRCLE).sort_by(SortBy.LENGTH)[-1], self.THICKNESS / 2)

    # --------------------------------------------------------------
    # Joints
    # --------------------------------------------------------------
    top_center = Location(solid.faces().filter_by(Plane.XY).sort_by(Axis.Z)[-1].center())
    RigidJoint(label="top", to_part=solid, joint_location=top_center)

    return solid

lid

lid()

Generates the lid

Joints

top: Represents the center at the top of the lid; connects well with the "top" joint of the body

Source code in src/bd_el_parts/spool_dryer.py
def lid(self):
    """
    Generates the lid

    Joints:
        `top`: Represents the center at the top of the lid; connects well with the "top" joint of the body
    """

    holes_loc = (0, 0, -self.LID_THICKNESS / 2)
    grip_area = (
        Location(holes_loc)
        * Box(length=self.LID_GRIP_THICKNESS * self.THICKNESS, width=self.EXT_RADIUS * 2, height=self.LID_THICKNESS * 2)
    )  # fmt: off
    holes_list = self._holes(radius=self.EXT_RADIUS - self.THICKNESS * 2, location=holes_loc)
    acceptable_length = truncate(holes_list[0].edges().filter_by(GeomType.CIRCLE)[0].length, 5)

    holes = Solid()
    # Remove holes that are not whole
    for hole in holes_list:
        remains = hole - grip_area

        # Ignore empty objects
        if type(remains) is ShapeList and len(remains) == 0:
            continue

        edges = remains.edges().filter_by(GeomType.CIRCLE)
        if len(edges) == 0 or truncate(edges[0].length, 5) < acceptable_length:
            continue
        holes += remains

    solid = SlidingLid(
        cap_lid_hole_radius=self.EXT_RADIUS - self.THICKNESS * 2,
        cap_lid_hole_wall_thickness=self.THICKNESS,
        cap_height=self.LID_THICKNESS,
        cap_outer_thickness=self.THICKNESS,
        lid_shoulder=self.LID_SHOULDER,
        lid_sliders=3,
        clearance=self.clearance,
        grip_thickness=self.LID_GRIP_THICKNESS,
    )

    solid -= holes

    stops = Locations((0, 15, -3), (0, -15, -3)) * (Rotation(Y=90) * Cylinder(2, self.LID_THICKNESS))
    solid -= stops

    # --------------------------------------------------------------
    # Joints
    # --------------------------------------------------------------
    bottom_center = Location(solid.faces().filter_by(Plane.XY).sort_by(Axis.Z)[-1].center())
    RigidJoint(label="top", to_part=solid, joint_location=bottom_center)

    return solid

lid_clip

lid_clip()

Generates the lid clip.

It prevents the spool dryer to fall from the spool.

Joints

lid_top: Point where the top of the lid should touch the clip.

Source code in src/bd_el_parts/spool_dryer.py
def lid_clip(self):
    """
    Generates the lid clip.

    It prevents the spool dryer to fall from the spool.

    Joints:
        `lid_top`: Point where the top of the lid should touch the clip.
    """
    solid = Box(
        self.LID_GRIP_THICKNESS + self.THICKNESS * 4,
        (self.EXT_RADIUS + self.THICKNESS) * 2,
        self.THICKNESS,
        align=self.ALIGN_Z_MIN,
    )
    solid = fillet(solid.edges().filter_by(Axis.Z), self.LID_GRIP_THICKNESS)

    side = Box(
        self.THICKNESS,
        self.EXT_RADIUS * 2 - (self.THICKNESS + self.LID_SHOULDER * 3) * 2,
        self.LID_THICKNESS - self.THICKNESS,
        align=self.ALIGN_Z_MAX,
    )

    pins = Sphere(2, arc_size3=180, rotation=(0, 0, -90), align=(Align.CENTER, Align.MIN, Align.CENTER))
    pins = Locations((-0.5, 15, -3), (-0.5, -15, -3)) * pins
    side += pins

    side = Location((-(self.LID_GRIP_THICKNESS / 2 + self.THICKNESS / 2 + self.clearance), 0, 0)) * side
    side = chamfer(side.edges().filter_by(Axis.X).sort_by(Axis.Z)[:-2], self.LID_SHOULDER)
    side += side.mirror(Plane.YZ)

    solid += side
    # --------------------------------------------------------------
    # Joints
    # --------------------------------------------------------------
    RigidJoint(label="lid_top", to_part=solid, joint_location=Location((0, 0, 0)))

    return solid

truncate

truncate(number, digits)
Source code in src/bd_el_parts/spool_dryer.py
def truncate(number, digits) -> float:
    # Improve accuracy with floating point operations, to avoid truncate(16.4, 2) = 16.39 or truncate(-1.13, 2) = -1.12
    nb_decimals = len(str(number).split(".")[1])
    if nb_decimals <= digits:
        return number
    stepper = 10.0**digits
    return math.trunc(stepper * number) / stepper