diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b1f2b60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +env_scad/ +env_scad/* \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..e4cd1c3 --- /dev/null +++ b/config.py @@ -0,0 +1,32 @@ + +from models.dimensions_3d import Dimensions_3D + +from pydantic import BaseModel +from typing import ClassVar + +class Config(BaseModel): + D_AXLE_FRONT: ClassVar[float] = 42 + D_AXLE_REAR: ClassVar[float] = 27 + D_WASHER_AXLE_REAR_WHEEL_POSITIONING: ClassVar[float] = 50 + D_WASHER_BOLT_REAR_WHEEL_POSITIONING: ClassVar[float] = 30 + D_WHEEL: ClassVar[float] = 400 + D_WHEEL_BONE: ClassVar[float] = 25 + L_BOLT_EXTENSION: ClassVar[float] = 10 + # M_TOTAL: ClassVar[float] = 160 + R_BED: ClassVar[Dimensions_3D] = Dimensions_3D(x=400, y=1200, z=18) + SIZE_BOLT: ClassVar[float] = 25 + SIZE_BOLT_REAR_WHEEL_POSITIONING: ClassVar[float] = 10 + SPACING_WASHER_AXLE_REAR_WHEEL_POSITIONING: ClassVar[float] = 1 + SPACING_XY_BED_BORDER: ClassVar[float] = 10 + T_CLAMP: ClassVar[float] = 20 + T_PLATE: ClassVar[float] = 8 + T_WASHER_AXLE_REAR_WHEEL_POSITIONING: ClassVar[float] = 4 + T_WASHER_BOLT_REAR_WHEEL_POSITIONING: ClassVar[float] = 1.5 + T_WHEEL: ClassVar[float] = 100 + Z_OVERLAP_TEE_CLAMP_ROUND: ClassVar[float] = 80 + Z_OFFSET_WHEEL_TO_BED_BASE: ClassVar[float] = 5 + + X_POSITION_WHEEL_CENTRE: float + Y_POSITION_AXLE_FRONT: float + Y_POSITION_AXLE_REAR: float + Z_OFFSET_BED_TO_AXLE: float diff --git a/config.scad b/config.scad index 142eb4b..18ad78b 100644 --- a/config.scad +++ b/config.scad @@ -45,7 +45,7 @@ X_POS_AXLE_LEG_REAR = min(X_POS_WHEEL_CENTRE - T_WHEEL / 2 - SPACING_WASHER_AXLE // THICKNESS_SHELL_BRAKE_BEAM = 5; // L_BEAM_BRAKE_BELT_TENSION_FIXING = 150; D_BEAM_BRAKE = 40; -T_BEAM_BRAKE = 5; +T_BEAM_BRAKE = 3; D_BRAKE_BALLSCREW_AXLE = 16; // M16 pitch 1.5 L_BRAKE_BALLSCREW_AXLE = 450; D_BRAKE_BALLSCREW_POWER_WHEEL = 120; diff --git a/docs/.~lock.UBOM.ods# b/docs/.~lock.UBOM.ods# deleted file mode 100644 index 6c2e65a..0000000 --- a/docs/.~lock.UBOM.ods# +++ /dev/null @@ -1 +0,0 @@ -,teddy,lord-T-1024,20.02.2025 13:01,file:///home/teddy/.config/libreoffice/4; \ No newline at end of file diff --git a/docs/UBOM.ods b/docs/UBOM.ods index 6c52c27..d8cb03b 100644 Binary files a/docs/UBOM.ods and b/docs/UBOM.ods differ diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/base_3d_object.py b/models/base_3d_object.py new file mode 100644 index 0000000..a2fb134 --- /dev/null +++ b/models/base_3d_object.py @@ -0,0 +1,69 @@ + +from coordinates_3d import Coordinates_3D +from dimensions_3d import Dimensions_3D +from material import Material + +from abc import ABC +from pydantic import BaseModel +import openpyscad as ops +# from solid import OpenSCADObject +from typing import Optional + +class Base_3D_Object(BaseModel, ABC): + colour: str = '' + dimensions_enclosing_cube: Dimensions_3D + __mass: float + material: Material + object: ops.base.BaseObject + parent: 'Base_3D_Object' = None + position_centre_local_assembly: Coordinates_3D = Coordinates_3D(x = 0, y = 0, z = 0) + rotation: Coordinates_3D = Coordinates_3D(x = 0, y = 0, z = 0) + __volume_solid: float + + """ + @classmethod + def make_local_object(cls, colour: str, dimensions_enclosing_cube: Dimensions_3D, material: Material, position_centre_local_assembly: Coordinates_3D, rotation: Coordinates_3D) -> 'Base_3D_Object': + return cls( + colour=colour, + dimensions_enclosing_cube=dimensions_enclosing_cube, + mass=material.density * dimensions_enclosing_cube.x * dimensions_enclosing_cube.y * dimensions_enclosing_cube.z, + material=material, + position_centre_local_assembly=position_centre_local_assembly, + position_centre_major_assembly=position_centre_local_assembly, + rotation=rotation, + volume_solid=dimensions_enclosing_cube.x * dimensions_enclosing_cube.y * dimensions_enclosing_cube.z + ) + """ + + @classmethod + def from_dimensions_and_material_and_parent(cls, dimensions_enclosing_cube: Dimensions_3D, material: Material, parent: Optional['Base_3D_Object']) -> 'Base_3D_Object': + return cls( + dimensions_enclosing_cube = dimensions_enclosing_cube, + material = material, + colour = material.colour, + parent = parent, + ) + + def get_position_centre_major_assembly(self) -> Coordinates_3D: + if self.parent is None: + return self.position_centre_local_assembly + else: + position_centre_parent_major_assembly = self.parent.get_position_centre_major_assembly() + return Coordinates_3D( + x=self.position_centre_local_assembly.x + position_centre_parent_major_assembly.x, + y=self.position_centre_local_assembly.y + position_centre_parent_major_assembly.y, + z=self.position_centre_local_assembly.z + position_centre_parent_major_assembly.z + ) + + def get_mass(self) -> float: + if self.__mass is None: + self.__mass = self.material.density * self.volume_solid + return self.__mass + + def get_volume_solid(self) -> float: + if self.__volume_solid is None: + self.__volume_solid = self.dimensions_enclosing_cube.x * self.dimensions_enclosing_cube.y * self.dimensions_enclosing_cube.z + return self.__volume_solid + + + \ No newline at end of file diff --git a/models/bed/__init__.py b/models/bed/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/bed/bed_assembly.py b/models/bed/bed_assembly.py new file mode 100644 index 0000000..e772887 --- /dev/null +++ b/models/bed/bed_assembly.py @@ -0,0 +1,10 @@ + +from models.base_3d_object import Base_3D_Object +import models.fixings.tube_clamp_round_base_plate_132 + +""" +import solid +import solid.utils +""" + +class Bed_Assembly(Base_3D_Object): diff --git a/models/common/metric_bolt.py b/models/common/metric_bolt.py new file mode 100644 index 0000000..b216ad2 --- /dev/null +++ b/models/common/metric_bolt.py @@ -0,0 +1,43 @@ + +from models.base_3d_object import Base_3D_Object +from models.dimensions_3d import Dimensions_3D +from models.material import Material + +from pydantic import Field + + +class Metric_Bolt(Base_3D_Object): + head_diameter: float = Field(gt = 0) + head_length: float = Field(gt = 0) + length: float = Field(gt = 0) + shaft_length: float = Field(gt = 0) + size: float = Field(gt = 0) + + @classmethod + def from_size_and_length_and_material(cls, size: float, length: float, material: Material) -> 'Metric_Bolt': + head_diameter = cls.get_head_diameter(size = size) + bolt = Metric_Bolt.from_dimensions_and_material( + dimensions_enclosing_cube = Dimensions_3D( + x = head_diameter, + y = head_diameter, + z = length + ), + material = material + ) + bolt.size = size + bolt.length = length + bolt.head_diameter = head_diameter + bolt.head_length = cls.get_head_length(size = size) + bolt.shaft_length = bolt.length - bolt.head_length + + @classmethod + def get_head_diameter(cls, size: float) -> float: + return size * 2 + + @classmethod + def get_head_length(cls, size: float) -> float: + return size + + @classmethod + def get_washer_diameter_safe_working_clearance(cls, size: float) -> float: + return size * 4 if size < 20 else size * 3 \ No newline at end of file diff --git a/models/coordinates_3d.py b/models/coordinates_3d.py new file mode 100644 index 0000000..4e02ecf --- /dev/null +++ b/models/coordinates_3d.py @@ -0,0 +1,7 @@ + +from pydantic import BaseModel, Field + +class Coordinates_3D(BaseModel): + x: float + y: float + z: float \ No newline at end of file diff --git a/models/dimensions_3d.py b/models/dimensions_3d.py new file mode 100644 index 0000000..2819439 --- /dev/null +++ b/models/dimensions_3d.py @@ -0,0 +1,7 @@ + +from pydantic import BaseModel, Field + +class Dimensions_3D(BaseModel): + x: float = Field(ge = 0) + y: float = Field(ge = 0) + z: float = Field(ge = 0) \ No newline at end of file diff --git a/models/fixings/base_tube_clamp.py b/models/fixings/base_tube_clamp.py new file mode 100644 index 0000000..65e3760 --- /dev/null +++ b/models/fixings/base_tube_clamp.py @@ -0,0 +1,17 @@ + +from models.base_3d_object import Base_3D_Object +from models.material import Material + +from abc import abstractmethod +from typing import ClassVar + +class Base_Tube_Clamp(Base_3D_Object): + MATERIAL_DEFAULT: ClassVar[Material] = Material( + name = 'Steel', + density = 7.85, + colour = 'LightGrey' + ) + + @abstractmethod + def from_tube_diameter(cls, tube_diameter: float) -> 'Base_Tube_Clamp': + pass \ No newline at end of file diff --git a/models/fixings/tube_clamp_round_3_way_outlet_tee_176.scad b/models/fixings/tube_clamp_round_3_way_outlet_tee_176.scad index e6b674c..fe69f08 100644 --- a/models/fixings/tube_clamp_round_3_way_outlet_tee_176.scad +++ b/models/fixings/tube_clamp_round_3_way_outlet_tee_176.scad @@ -31,7 +31,7 @@ module tube_clamp_round_3_way_outlet_tee_176(tube_diameter) { rotate([90, 0, 0]) cylinder(L_shaft_long, tube_diameter / 2, tube_diameter / 2, center = true); } // Shopping - echo("Round 3-way outlet tee 176: Tube clamp - x1"); + echo(str("Round 3-way outlet tee 176: Tube clamp Φ", tube_diameter, "mm - x1")); } // test output diff --git a/models/fixings/tube_clamp_round_3_way_through_116.scad b/models/fixings/tube_clamp_round_3_way_through_116.scad index 9bbddb2..228a015 100644 --- a/models/fixings/tube_clamp_round_3_way_through_116.scad +++ b/models/fixings/tube_clamp_round_3_way_through_116.scad @@ -19,7 +19,7 @@ module tube_clamp_round_3_way_through_116(tube_diameter) { translate([0, L_shaft_long / 2, 0]) rotate([90, 0, 0]) cylinder(L_shaft_long, tube_diameter / 2, tube_diameter / 2, center = true); } // Shopping - echo("Round 3-way through 116: Tube clamp - x1"); + echo(str("Round 3-way through 116: Tube clamp Φ", tube_diameter, "mm - x1")); } // test output diff --git a/models/fixings/tube_clamp_round_base_plate_132.py b/models/fixings/tube_clamp_round_base_plate_132.py new file mode 100644 index 0000000..89b9775 --- /dev/null +++ b/models/fixings/tube_clamp_round_base_plate_132.py @@ -0,0 +1,150 @@ + +from models.base_3d_object import Base_3D_Object +from models.fixings.base_tube_clamp import Base_Tube_Clamp +from models.dimensions_3d import Dimensions_3D +from models.material import Material + +from pydantic import Field +# import solid +import openpyscad as ops + +class Tube_Clamp_Round_Base_Plate_132(Base_Tube_Clamp): + + + @classmethod + def from_tube_diameter_and_parent(cls, tube_diameter: float, parent: Base_3D_Object) -> 'Tube_Clamp_Round_Base_Plate_132': + L_shaft_with_base_plate = cls.get_L_shaft_with_base_plate(tube_diameter = tube_diameter) + R_plate = cls.get_R_plate(tube_diameter = tube_diameter) + y_offset_hole = cls.get_offset_hole_y(tube_diameter = tube_diameter) + diameter_hole_bolt = cls.get_diameter_hole(tube_diameter = tube_diameter) + + tube_clamp = cls.from_dimensions_and_material_and_parent( + dimensions_enclosing_cube = Dimensions_3D( + x = R_plate.x, + y = R_plate.y, + z = L_shaft_with_base_plate + ), + material = cls.MATERIAL_DEFAULT, + parent = parent, + ) + + clamp_shaft = cls.__make_shaft_object( + tube_diameter = tube_diameter, + L_shaft_with_base_plate = L_shaft_with_base_plate, + R_plate = R_plate, + ) + clamp_shaft.translate([0, 0, L_shaft_with_base_plate / 2]) + + base_plate = cls.__make_base_plate_object( + R_plate = R_plate, + y_offset_hole = y_offset_hole, + diameter_hole_bolt = diameter_hole_bolt, + ) + base_plate.translate([0, 0, R_plate.z / 2]) + + tube_clamp.object = ops.Union() + tube_clamp.object.append(base_plate) + tube_clamp.object.append(clamp_shaft) + + tube_clamp.object.color(cls.MATERIAL_DEFAULT.colour) + return tube_clamp + + @classmethod + def __make_shaft_object(cls, tube_diameter: float, L_shaft_with_base_plate: float, R_plate: tuple[float, float, float]) -> ops.base.BaseObject: + clamp_shaft = ops.Difference() + clamp_shaft.append( + ops.Cylinder( + h = L_shaft_with_base_plate, + r = tube_diameter / 2 + R_plate.z, + ) + ) + clamp_shaft.append( + ops.Cylinder( + h = L_shaft_with_base_plate, + r = tube_diameter / 2, + ) + ) + return clamp_shaft + @classmethod + def __make_base_plate_object(cls, R_plate: Dimensions_3D, y_offset_hole: float, diameter_hole_bolt: float) -> ops.base.BaseObject: + base_plate = ops.Difference() + base_plate.append( + ops.Cube( + v = [R_plate.x, R_plate.y, R_plate.z], + center = True, + ) + ) + base_plate.append( + ops.Cylinder( + h = R_plate.z, + r = diameter_hole_bolt / 2, + center = True, + ).translate([0, y_offset_hole, 0]) + ) + base_plate.append( + ops.Cylinder( + h = R_plate.z, + r = diameter_hole_bolt / 2, + center = True, + ).translate([0, -y_offset_hole, 0]) + ) + return base_plate + + @classmethod + def get_L_shaft_with_base_plate(cls, tube_diameter: float) -> float: + L_shaft = 25 + if tube_diameter == 27: + L_shaft = 85 + elif tube_diameter == 42: + L_shaft = 122 + return L_shaft + + @classmethod + def get_R_plate(cls, tube_diameter: float) -> Dimensions_3D: + R_plate = Dimensions_3D( + x = 10, + y = 20, + z = 1 + ) + if tube_diameter == 27: + R_plate = Dimensions_3D( + x = 64, + y = 114, + z = 7 + ) + elif tube_diameter == 42: + R_plate = Dimensions_3D( + x = 80, + y = 140, + z = 10 + ) + else: + raise ValueError(f"Unsupported tube diameter: {tube_diameter}") + return R_plate + + @classmethod + def get_offset_hole_y(cls, tube_diameter: float) -> float: + y_offset = 10 + if tube_diameter == 27: + y_offset = 38 + elif tube_diameter == 42: + y_offset = 50 + else: + raise ValueError(f"Unsupported tube diameter: {tube_diameter}") + return y_offset + + @classmethod + def get_diameter_hole(cls, tube_diameter: float) -> float: + return 10 + + @classmethod + def get_mass(cls, tube_diameter: float) -> float: + mass = 0 + if tube_diameter == 27: + mass = 0.5 + elif tube_diameter == 42: + mass = 1.06 + else: + raise ValueError(f"Unsupported tube diameter: {tube_diameter}") + return mass + \ No newline at end of file diff --git a/models/fixings/tube_clamp_round_base_plate_132.scad b/models/fixings/tube_clamp_round_base_plate_132.scad index edad366..028d67d 100644 --- a/models/fixings/tube_clamp_round_base_plate_132.scad +++ b/models/fixings/tube_clamp_round_base_plate_132.scad @@ -21,7 +21,7 @@ module tube_clamp_round_base_plate_132(tube_diameter) { } } // Shopping - echo("Round base plate 132: Tube clamp - x1"); + echo(str("Round base plate 132: Tube clamp Φ", tube_diameter, "mm - x1")); } // test output diff --git a/models/fixings/tube_clamp_round_tee_long_104.scad b/models/fixings/tube_clamp_round_tee_long_104.scad index eef8a43..d6b92c3 100644 --- a/models/fixings/tube_clamp_round_tee_long_104.scad +++ b/models/fixings/tube_clamp_round_tee_long_104.scad @@ -16,7 +16,7 @@ module tube_clamp_round_tee_long_104(tube_diameter) { translate([L_shaft_short / 2, 0, 0]) rotate([0, 90, 0]) cylinder(L_shaft_short, tube_diameter / 2, tube_diameter / 2, center = true); } // Shopping - echo("Round tee long 104: Tube clamp - x1"); + echo(str("Round tee long 104: Tube clamp Φ", tube_diameter, "mm - x1")); } // test output diff --git a/models/fixings/tube_clamp_square_base_plate_132.scad b/models/fixings/tube_clamp_square_base_plate_132.scad index 4b6bda0..350f5c5 100644 --- a/models/fixings/tube_clamp_square_base_plate_132.scad +++ b/models/fixings/tube_clamp_square_base_plate_132.scad @@ -21,7 +21,7 @@ module tube_clamp_square_base_plate_132(beam_width) { } } // Shopping - echo("Square base plate 132: Tube clamp - x1"); + echo(str("Square base plate 132: Tube clamp Φ", beam_width, "mm - x1")); } // test output diff --git a/models/material.py b/models/material.py new file mode 100644 index 0000000..ab748fb --- /dev/null +++ b/models/material.py @@ -0,0 +1,7 @@ + +from pydantic import BaseModel, Field + +class Material(BaseModel): + colour: str + density: float = Field(gt = 0) + name: str \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f62f3ba --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +openpyscad +pydantic +# solid \ No newline at end of file