From 0e40c6cadd974000c8fe0dbec473b1f1f1033b0e Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Tue, 6 Jan 2026 20:06:47 +0300 Subject: [PATCH 01/17] Add notches and some typing --- freecad/gridfinity_workbench/const.py | 2 + .../feature_construction.py | 369 ++++++++++++++---- freecad/gridfinity_workbench/features.py | 150 +++++-- .../grid_initial_layout.py | 23 +- .../gridfinity_workbench/gridfinity_types.py | 148 +++++++ freecad/gridfinity_workbench/utils.py | 5 +- 6 files changed, 595 insertions(+), 102 deletions(-) create mode 100644 freecad/gridfinity_workbench/gridfinity_types.py diff --git a/freecad/gridfinity_workbench/const.py b/freecad/gridfinity_workbench/const.py index 102ae15e..9ceadf57 100644 --- a/freecad/gridfinity_workbench/const.py +++ b/freecad/gridfinity_workbench/const.py @@ -41,6 +41,8 @@ STACKING_LIP_TOP_LEDGE = 0.4 STACKING_LIP_BOTTOM_CHAMFER = 0.7 STACKING_LIP_VERTICAL_SECTION = 1.8 +STACKING_LIP_NOTCHES = False +STACKING_LIP_NOTCHES_CHAMFER = 0.7 RECESSED_TOP_DEPTH = 0 SEQUENTIAL_BRIDGING_LAYER_HEIGHT = 0.2 diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 007a0a4d..1fb4a0d8 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1,16 +1,23 @@ """Module containing gridfinity feature constructions.""" +# ruff: noqa: D101, D102, D107 import math +import typing from typing import Literal import FreeCAD as fc # noqa: N813 import Part -from . import const, utils +from . import const +from . import gridfinity_types as gft from . import label_shelf as label_shelf_module +from . import utils +from .grid_initial_layout import GridfinityObject unitmm = fc.Units.Quantity("1 mm") zeromm = fc.Units.Quantity("0 mm") +# unitmm = 1 # mm +# zeromm = 0 # mm ECO_USABLE_HEIGHT = 14 SMALL_NUMBER = 0.01 @@ -81,7 +88,9 @@ def label_shelf_properties(obj: fc.DocumentObject, *, label_style_default: str) ).LabelShelfVerticalThickness = const.LABEL_SHELF_VERTICAL_THICKNESS -def make_label_shelf(obj: fc.DocumentObject, bintype: Literal["eco", "standard"]) -> Part.Shape: +def make_label_shelf( + obj: fc.DocumentObject, bintype: Literal["eco", "standard"] +) -> Part.Shape: """Create label shelf.""" if ( bintype == "eco" @@ -89,7 +98,9 @@ def make_label_shelf(obj: fc.DocumentObject, bintype: Literal["eco", "standard"] and obj.LabelShelfStyle != "Overhang" ): obj.LabelShelfStyle = "Overhang" - fc.Console.PrintWarning("Label shelf style set to Overhang due to low bin height\n") + fc.Console.PrintWarning( + "Label shelf style set to Overhang due to low bin height\n" + ) xdiv = obj.xDividers + 1 ydiv = obj.yDividers + 1 @@ -134,7 +145,9 @@ def make_label_shelf(obj: fc.DocumentObject, bintype: Literal["eco", "standard"] ) if height > obj.UsableHeight: - boundingbox = Part.makeBox(width, length, height, fc.Vector(0, 0, -obj.UsableHeight)) + boundingbox = Part.makeBox( + width, length, height, fc.Vector(0, 0, -obj.UsableHeight) + ) funcfuse = funcfuse.common(boundingbox) funcfuse = utils.copy_in_grid( @@ -192,15 +205,15 @@ def scoop_properties(obj: fc.DocumentObject, *, scoop_default: bool) -> None: ).Scoop = scoop_default -def make_scoop(obj: fc.DocumentObject) -> Part.Shape: +def make_scoop(obj: gft.ScoopMixin) -> Part.Shape: """Create scoop.""" scooprad1 = obj.ScoopRadius + unitmm scooprad2 = obj.ScoopRadius + unitmm scooprad3 = obj.ScoopRadius + unitmm - xcomp_w = (obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness) / ( - obj.xDividers + 1 - ) + xcomp_w = ( + obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness + ) / (obj.xDividers + 1) xdivscoop = obj.xDividerHeight - obj.HeightUnitValue - obj.LabelShelfStackingOffset @@ -254,9 +267,9 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape: face = Part.Face(wire) xdiv = obj.xDividers + 1 - compwidth = (obj.xTotalWidth - obj.WallThickness * 2 - obj.DividerThickness * obj.xDividers) / ( - xdiv - ) + compwidth = ( + obj.xTotalWidth - obj.WallThickness * 2 - obj.DividerThickness * obj.xDividers + ) / (xdiv) scoopbox = Part.makeBox( obj.StackingLipBottomChamfer @@ -310,7 +323,7 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape: def _corner_fillets( - obj: fc.DocumentObject, + obj: gft.CompartmentsMixin, xcomp_width: float, ycomp_width: float, ) -> Part.Shape: @@ -400,7 +413,7 @@ def _make_compartments_no_deviders( def _make_compartments_with_deviders( - obj: fc.DocumentObject, + obj: gft.CompartmentsMixin, func_fuse: Part.Shape, ) -> Part.Shape: xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -408,12 +421,12 @@ def _make_compartments_with_deviders( stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm - xcomp_w = (obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness) / ( - obj.xDividers + 1 - ) - ycomp_w = (obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness) / ( - obj.yDividers + 1 - ) + xcomp_w = ( + obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness + ) / (obj.xDividers + 1) + ycomp_w = ( + obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness + ) / (obj.yDividers + 1) xtranslate = xcomp_w + obj.WallThickness - obj.DividerThickness ytranslate = ycomp_w + obj.WallThickness @@ -461,7 +474,9 @@ def _make_compartments_with_deviders( return func_fuse -def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_default: int) -> None: +def compartments_properties( + obj: fc.DocumentObject, x_div_default: int, y_div_default: int +) -> None: """Create bin compartments with the option for dividers. Args: @@ -531,7 +546,9 @@ def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_de ) -def make_compartments(obj: fc.DocumentObject, bin_inside_solid: Part.Shape) -> Part.Shape: +def make_compartments( + obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape +) -> Part.Shape: """Create compartment cutout objects. Args: @@ -544,7 +561,10 @@ def make_compartments(obj: fc.DocumentObject, bin_inside_solid: Part.Shape) -> P """ ## Error Checks divmin = ( - obj.HeightUnitValue + obj.InsideFilletRadius + 0.05 * unitmm + obj.LabelShelfStackingOffset + obj.HeightUnitValue + + obj.InsideFilletRadius + + 0.05 * unitmm + + obj.LabelShelfStackingOffset ) if obj.xDividerHeight < divmin and obj.xDividerHeight != 0: @@ -579,7 +599,7 @@ def make_compartments(obj: fc.DocumentObject, bin_inside_solid: Part.Shape) -> P return func_fuse.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def make_bottom_hole_shape(obj: fc.DocumentObject) -> Part.Shape: +def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: """Create bottom hole shape. Returns one combined shape containing of the different hole types. @@ -600,7 +620,9 @@ def make_bottom_hole_shape(obj: fc.DocumentObject) -> Part.Shape: p.recompute() p_wire: Part.Wire = p.Shape - magnet_hole_shape = Part.Face(p_wire).extrude(fc.Vector(0, 0, obj.MagnetHoleDepth)) + magnet_hole_shape = Part.Face(p_wire).extrude( + fc.Vector(0, 0, obj.MagnetHoleDepth) + ) fc.ActiveDocument.removeObject(p.Name) else: magnet_hole_shape = Part.makeCylinder( @@ -640,7 +662,8 @@ def make_bottom_hole_shape(obj: fc.DocumentObject) -> Part.Shape: ) arc_pt_off_x = ( math.sqrt( - ((obj.MagnetHoleDiameter / 2) ** 2) - ((obj.ScrewHoleDiameter / 2) ** 2), + ((obj.MagnetHoleDiameter / 2) ** 2) + - ((obj.ScrewHoleDiameter / 2) ** 2), ) ) * unitmm arc_pt_off_y = obj.ScrewHoleDiameter / 2 @@ -673,7 +696,9 @@ def make_bottom_hole_shape(obj: fc.DocumentObject) -> Part.Shape: return bottom_hole_shape -def _eco_bin_deviders(obj: fc.DocumentObject, xcomp_w: float, ycomp_w: float) -> Part.Shape: +def _eco_bin_deviders( + obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: float +) -> Part.Shape: stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -722,7 +747,7 @@ def _eco_bin_deviders(obj: fc.DocumentObject, xcomp_w: float, ycomp_w: float) -> return assembly.translate(fc.Vector(obj.xGridSize / 2, obj.yGridSize / 2)) -def eco_error_check(obj: fc.DocumentObject) -> None: +def eco_error_check(obj: gft.EcoCompartmentsMixin) -> None: """Check if eco dividers are possible with current parameters.""" # Divider Minimum Height @@ -819,7 +844,7 @@ def eco_compartments_properties(obj: fc.DocumentObject) -> None: def make_eco_compartments( - obj: fc.DocumentObject, + obj: gft.EcoCompartmentsMixin, layout: GridfinityLayout, bin_inside_solid: Part.Shape, ) -> Part.Shape: @@ -884,7 +909,9 @@ def make_eco_compartments( obj.BaseProfileBottomChamfer + obj.BaseProfileVerticalSection + base_offset ): tp_chf_offset = (obj.MagnetHoleDepth + obj.BaseWallThickness) - ( - obj.BaseProfileBottomChamfer + obj.BaseProfileVerticalSection + base_offset + obj.BaseProfileBottomChamfer + + obj.BaseProfileVerticalSection + + base_offset ) bottom_chamfer = utils.rounded_rectangle_chamfer( @@ -949,12 +976,12 @@ def make_eco_compartments( func_fuse = func_fuse.cut(outer_trim2) - xcomp_w = (obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness) / ( - obj.xDividers + 1 - ) - ycomp_w = (obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness) / ( - obj.yDividers + 1 - ) + xcomp_w = ( + obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness + ) / (obj.xDividers + 1) + ycomp_w = ( + obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness + ) / (obj.yDividers + 1) if obj.xDividers > 0 or obj.yDividers > 0: func_fuse = func_fuse.cut(_eco_bin_deviders(obj, xcomp_w, ycomp_w)) @@ -1047,14 +1074,16 @@ def bin_base_values_properties(obj: fc.DocumentObject) -> None: def make_complex_bin_base( - obj: fc.DocumentObject, + obj: gft.BaseMixin, layout: GridfinityLayout, + adjust: fc.Units.Quantity | None = None, ) -> Part.Shape: """Creaet complex shaped bin base.""" if obj.Baseplate: + obj = gft.cast(gft.BaseplateMixin, obj) baseplate_size_adjustment = obj.BaseplateTopLedgeWidth - obj.Clearance else: - baseplate_size_adjustment = 0 * unitmm + baseplate_size_adjustment = adjust if adjust is not None else zeromm # mm x_bt_cmf_width = ( (obj.xGridSize - obj.Clearance * 2) @@ -1099,7 +1128,9 @@ def make_complex_bin_base( top_chamfer = utils.rounded_rectangle_chamfer( x_vert_width, y_vert_width, - -obj.TotalHeight + obj.BaseProfileBottomChamfer + obj.BaseProfileVerticalSection, + -obj.TotalHeight + + obj.BaseProfileBottomChamfer + + obj.BaseProfileVerticalSection, obj.BaseProfileTopChamfer, obj.BinVerticalRadius, ) @@ -1109,7 +1140,10 @@ def make_complex_bin_base( fuse_total = utils.copy_in_layout(assembly, layout, obj.xGridSize, obj.yGridSize) return fuse_total.translate( - fc.Vector(obj.xGridSize / 2 - obj.xLocationOffset, obj.yGridSize / 2 - obj.yLocationOffset), + fc.Vector( + obj.xGridSize / 2 - obj.xLocationOffset, + obj.yGridSize / 2 - obj.yLocationOffset, + ), ) @@ -1129,14 +1163,18 @@ def blank_bin_recessed_top_properties(obj: fc.DocumentObject) -> None: ).RecessedTopDepth = const.RECESSED_TOP_DEPTH -def make_blank_bin_recessed_top(obj: fc.DocumentObject, bin_inside_shape: Part.Wire) -> Part.Shape: +def make_blank_bin_recessed_top( + obj: fc.DocumentObject, bin_inside_shape: Part.Wire +) -> Part.Shape: """Generate Rectanble layout and calculate relevant parameters.""" face = Part.Face(bin_inside_shape) fuse_total = face.extrude(fc.Vector(0, 0, -obj.RecessedTopDepth)) return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def bin_bottom_holes_properties(obj: fc.DocumentObject, *, magnet_holes_default: bool) -> None: +def bin_bottom_holes_properties( + obj: fc.DocumentObject, *, magnet_holes_default: bool +) -> None: """Create bin solid mid section. Args: @@ -1226,7 +1264,7 @@ def bin_bottom_holes_properties(obj: fc.DocumentObject, *, magnet_holes_default: def make_bin_bottom_holes( - obj: fc.DocumentObject, + obj: gft.HoleMixin, layout: GridfinityLayout, ) -> Part.Shape: """Make bin bottom holes.""" @@ -1240,49 +1278,129 @@ def make_bin_bottom_holes( utils.corners(x_hole_pos, y_hole_pos, -obj.TotalHeight), ) - fuse_total = utils.copy_in_layout(hole_shape_sub_array, layout, obj.xGridSize, obj.yGridSize) + fuse_total = utils.copy_in_layout( + hole_shape_sub_array, + layout, + obj.xGridSize, + obj.yGridSize, + ) fuse_total.translate(fc.Vector(obj.xGridSize / 2, obj.yGridSize / 2)) return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def _stacking_lip_profile(obj: fc.DocumentObject) -> Part.Wire: +def _stacking_lip_profile(obj: gft.StackingLipMixin) -> Part.Wire: """Create stacking lip profile wire.""" ## Calculated Values - obj.StackingLipTopChamfer = obj.BaseProfileTopChamfer - obj.Clearance - obj.StackingLipTopLedge - - ## Stacking Lip Generation + obj.StackingLipTopChamfer = ( + obj.BaseProfileTopChamfer - obj.Clearance - obj.StackingLipTopLedge + ) x1 = obj.Clearance x2 = x1 + obj.StackingLipTopLedge x3 = x2 + obj.StackingLipTopChamfer x4 = x3 + obj.StackingLipBottomChamfer x5 = obj.Clearance + obj.WallThickness y = obj.yGridSize / 2 - z1 = obj.StackingLipBottomChamfer + obj.StackingLipVerticalSection + obj.StackingLipTopChamfer + z1 = ( + obj.StackingLipBottomChamfer + + obj.StackingLipVerticalSection + + obj.StackingLipTopChamfer + ) z2 = obj.StackingLipBottomChamfer + obj.StackingLipVerticalSection z3 = obj.StackingLipBottomChamfer z4 = -obj.StackingLipVerticalSection - z5 = ( - z4 + z5 = z4 - abs(x4 - x5) + if obj.StackingLipNotches: + st = [ + fc.Vector(x1, y, 0), + fc.Vector(x4, y, 0), + fc.Vector(x4, y, z4), + fc.Vector(x5, y, z5), + fc.Vector(x1, y, z5), + ] + else: + st = [ + fc.Vector(x1, y, 0), + fc.Vector(x1, y, z1), + fc.Vector(x2, y, z1), + fc.Vector(x3, y, z2), + fc.Vector(x3, y, z3), + fc.Vector(x4, y, 0), + fc.Vector(x4, y, z4), + fc.Vector(x5, y, z5), + fc.Vector(x5, y, 0), + ] + + stacking_lip_profile = Part.Wire(Part.Shape(utils.loop(st)).Edges) + + return stacking_lip_profile + + +def _stacking_lip_plate( + obj: gft.StackingLipMixin, + layout: GridfinityLayout, +) -> Part.Shape: + """Creaet complex shaped bin base.""" + x_bt_cmf_width = ( + (obj.xGridSize - obj.Clearance * 2) + - 2 * obj.StackingLipBottomChamfer + - 2 * obj.StackingLipTopChamfer + - 2 * obj.StackingLipTopLedge + ) + y_bt_cmf_width = ( + (obj.yGridSize - obj.Clearance * 2) + - 2 * obj.StackingLipBottomChamfer + - 2 * obj.StackingLipTopChamfer + - 2 * obj.StackingLipTopLedge + ) + x_vert_width = ( + (obj.xGridSize - obj.Clearance * 2) + - 2 * obj.StackingLipTopChamfer + - 2 * obj.StackingLipTopLedge + ) + y_vert_width = ( + (obj.yGridSize - obj.Clearance * 2) + - 2 * obj.StackingLipTopChamfer + - 2 * obj.StackingLipTopLedge + ) + + bottom_chamfer = utils.rounded_rectangle_chamfer( + x_bt_cmf_width, + y_bt_cmf_width, + zeromm, + obj.StackingLipBottomChamfer, + obj.BinOuterRadius - obj.StackingLipTopLedge - obj.StackingLipTopChamfer - - obj.StackingLipBottomChamfer - + obj.WallThickness + - obj.StackingLipBottomChamfer, ) - st = [ - fc.Vector(x1, y, 0), - fc.Vector(x1, y, z1), - fc.Vector(x2, y, z1), - fc.Vector(x3, y, z2), - fc.Vector(x3, y, z3), - fc.Vector(x4, y, 0), - fc.Vector(x4, y, z4), - fc.Vector(x5, y, z5), - fc.Vector(x5, y, 0), - ] - stacking_lip_profile = Part.Wire(Part.Shape(utils.loop(st)).Edges) + vertical_section = utils.rounded_rectangle_extrude( + x_vert_width, + y_vert_width, + obj.StackingLipBottomChamfer, + obj.StackingLipVerticalSection, + obj.BinOuterRadius - obj.StackingLipTopLedge - obj.StackingLipTopChamfer, + ) + assembly = bottom_chamfer.fuse(vertical_section) - return stacking_lip_profile + top_chamfer = utils.rounded_rectangle_chamfer( + x_vert_width, + y_vert_width, + obj.StackingLipBottomChamfer + obj.StackingLipVerticalSection, + obj.StackingLipTopChamfer, + obj.BinOuterRadius - obj.StackingLipTopLedge - obj.StackingLipTopChamfer, + ) + + assembly = bottom_chamfer.multiFuse([vertical_section, top_chamfer]) + + fuse_total = utils.copy_in_layout(assembly, layout, obj.xGridSize, obj.yGridSize) + + return fuse_total.translate( + fc.Vector( + obj.xGridSize / 2 - obj.xLocationOffset, + obj.yGridSize / 2 - obj.yLocationOffset, + ), + ) def stacking_lip_properties( @@ -1338,8 +1456,24 @@ def stacking_lip_properties( read_only=True, ).StackingLipVerticalSection = const.STACKING_LIP_VERTICAL_SECTION + ## Gridfinity Non Standard Parameters + obj.addProperty( + "App::PropertyBool", + "StackingLipNotches", + "GridfinityNonStandard", + "Toggle the notches on the stacking lip on or off", + ).StackingLipNotches = const.STACKING_LIP_NOTCHES + obj.addProperty( + "App::PropertyLength", + "StackingLipNotchesChamfer", + "GridfinityNonStandard", + f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", + ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER + -def make_stacking_lip(obj: fc.DocumentObject, bin_outside_shape: Part.Wire) -> Part.Shape: +def make_stacking_lip( + obj: gft.StackingLipMixin, layout: GridfinityLayout +) -> Part.Shape: """Create stacking lip based on input bin shape. Args: @@ -1347,13 +1481,102 @@ def make_stacking_lip(obj: fc.DocumentObject, bin_outside_shape: Part.Wire) -> P bin_outside_shape (Part.Wire): exterior wall of the bin """ + + bin_outside_shape = utils.create_rounded_rectangle( + obj.xTotalWidth, + obj.yTotalWidth, + 0, + obj.BinOuterRadius, + ) + bin_outside_shape.translate( + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ), + ) + + bin_inside_shape = utils.create_rounded_rectangle( + obj.xTotalWidth - obj.WallThickness * 2, + obj.yTotalWidth - obj.WallThickness * 2, + 0, + obj.BinOuterRadius - obj.WallThickness, + ) + bin_inside_shape.translate( + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ), + ) wire = _stacking_lip_profile(obj) stacking_lip = Part.Wire(bin_outside_shape).makePipe(wire) stacking_lip = Part.makeSolid(stacking_lip) + if obj.StackingLipNotches: + height = ( + obj.StackingLipBottomChamfer + + obj.StackingLipVerticalSection + + obj.StackingLipTopChamfer + ) + cover = utils.rounded_rectangle_extrude( + obj.xTotalWidth, + obj.yTotalWidth, + 0, + height, + obj.BinOuterRadius, + ).translate( + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ) + ) + base = _stacking_lip_plate(obj, layout) + # base.translate(fc.Vector(0, 0, obj.TotalHeight)) + cover = cover.cut(base) + offset = ( + obj.StackingLipTopLedge + + obj.StackingLipTopChamfer + + obj.StackingLipBottomChamfer + ) + cutout = utils.rounded_rectangle_extrude( + obj.xTotalWidth - offset * 2, + obj.yTotalWidth - offset * 2, + 0, + height, + obj.BinOuterRadius - offset, + ).translate( + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ) + ) + cover = cover.cut(cutout) + if obj.StackingLipNotchesChamfer > 0: + chamfer_offset = ( + obj.StackingLipTopLedge + + obj.StackingLipTopChamfer + + obj.StackingLipBottomChamfer + ) + cutout_chamfer = utils.rounded_rectangle_chamfer( + obj.xTotalWidth - chamfer_offset * 2, + obj.yTotalWidth - chamfer_offset * 2, + height - obj.StackingLipNotchesChamfer, + obj.StackingLipNotchesChamfer, + obj.BinOuterRadius + - obj.StackingLipTopLedge + - obj.StackingLipTopChamfer + + obj.StackingLipNotchesChamfer, + ).translate( + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ) + ) + cover = cover.cut(cutout_chamfer) + + stacking_lip = stacking_lip.fuse(cover) + stacking_lip = stacking_lip.translate( fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset), ) - return stacking_lip @@ -1424,7 +1647,10 @@ def bin_solid_mid_section_properties( ) -def make_bin_solid_mid_section(obj: fc.DocumentObject, bin_outside_shape: Part.Wire) -> Part.Shape: +def make_bin_solid_mid_section( + obj: gft.SolidMidSectionMixin, + bin_outside_shape: Part.Wire, +) -> Part.Shape: """Generate bin solid mid section. Args: @@ -1435,6 +1661,11 @@ def make_bin_solid_mid_section(obj: fc.DocumentObject, bin_outside_shape: Part.W face = Part.Face(bin_outside_shape) fuse_total = face.extrude(fc.Vector(0, 0, -obj.TotalHeight + obj.BaseProfileHeight)) - fuse_total = fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) + fuse_total = fuse_total.translate( + fc.Vector( + -obj.xLocationOffset, + -obj.yLocationOffset, + ), + ) return fuse_total diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 16fd6f3f..b57f01c3 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -2,14 +2,16 @@ # ruff: noqa: D101, D102, D107 +import typing from abc import abstractmethod import FreeCAD as fc # noqa: N813 import Part from . import baseplate_feature_construction as baseplate_feat -from . import const, grid_initial_layout, label_shelf, utils +from . import const from . import feature_construction as feat +from . import grid_initial_layout, label_shelf, utils from .custom_shape_features import ( clean_up_layout, custom_shape_solid, @@ -18,6 +20,13 @@ cut_outside_shape, vertical_edge_fillet, ) +from .gridfinity_types import ( + BaseplateObject, + EcoBinObject, + FullBinObject, + StorageBinObject, + cast, +) from .version import __version__ unitmm = fc.Units.Quantity("1 mm") @@ -54,7 +63,7 @@ def execute(self, fp: Part.Feature) -> None: fp.Shape = gridfinity_shape @abstractmethod - def generate_gridfinity_shape(self, fp: fc.DocumentObject) -> Part.Shape: + def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: """Generate the TopoShape of the object.""" def dumps(self) -> dict: @@ -67,6 +76,52 @@ def loads(self, state: dict) -> None: State argument required, otherwise expecting argument error message. """ + def addPropertyIfMissing( + self, + obj: fc.DocumentObject, + type: str, + name: str, + group: str = "", + doc: str = "", + attr: int = 0, + read_only: bool = False, + hidden: bool = False, + enum_vals=[], + ): + if name not in obj.PropertiesList: + return obj.addProperty( + type=type, + name=name, + group=group, + doc=doc, + attr=attr, + read_only=read_only, + hidden=hidden, + enum_vals=enum_vals, + ) + from types import SimpleNamespace + + dummy = SimpleNamespace() + setattr(dummy, name, None) + return dummy + + def onDocumentRestored(self, obj: fc.DocumentObject) -> None: # noqa: N802 + # Syntax: addProperty(type, name, group, docstring) + self.addPropertyIfMissing( + obj, + "App::PropertyBool", + "StackingLipNotches", + "GridfinityNonStandard", + "Toggle the notches on the stacking lip on or off", + ).StackingLipNotches = const.STACKING_LIP_NOTCHES + self.addPropertyIfMissing( + obj, + "App::PropertyLength", + "StackingLipNotchesChamfer", + "GridfinityNonStandard", + f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", + ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER + class FullBin(FoundationGridfinity): """Gridfinity abstract FullBin object. @@ -95,6 +150,7 @@ def __init__( feat.bin_base_values_properties(obj) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: + obj = cast(FullBinObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) bin_outside_shape = utils.create_rounded_rectangle( @@ -104,7 +160,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius, ) bin_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance + ), ) bin_inside_shape = utils.create_rounded_rectangle( @@ -114,17 +172,21 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius - obj.WallThickness, ) bin_inside_shape.translate( - fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance + ), ) fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) fuse_total = fuse_total.fuse(feat.make_complex_bin_base(obj, layout)) if obj.RecessedTopDepth > 0: - fuse_total = fuse_total.cut(feat.make_blank_bin_recessed_top(obj, bin_inside_shape)) + fuse_total = fuse_total.cut( + feat.make_blank_bin_recessed_top(obj, bin_inside_shape) + ) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, bin_outside_shape)) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -179,11 +241,15 @@ def __init__( feat.stacking_lip_properties(obj, stacking_lip_default=const.STACKING_LIP) feat.bin_bottom_holes_properties(obj, magnet_holes_default=const.MAGNET_HOLES) feat.bin_base_values_properties(obj) - feat.compartments_properties(obj, x_div_default=x_div_default, y_div_default=y_div_default) + feat.compartments_properties( + obj, x_div_default=x_div_default, y_div_default=y_div_default + ) feat.label_shelf_properties(obj, label_style_default=label_style_default) feat.scoop_properties(obj, scoop_default=scoop_default) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: + obj = cast(StorageBinObject, obj) + layout = grid_initial_layout.make_rectangle_layout(obj) bin_outside_shape = utils.create_rounded_rectangle( @@ -193,7 +259,10 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius, ) bin_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ), ) bin_inside_shape = utils.create_rounded_rectangle( @@ -203,7 +272,10 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius - obj.WallThickness, ) bin_inside_shape.translate( - fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ), ) fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) @@ -215,7 +287,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_compartments(obj, compartments)) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, bin_outside_shape)) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -268,6 +340,7 @@ def __init__(self, obj: fc.DocumentObject) -> None: feat.eco_compartments_properties(obj) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: + obj = cast(EcoBinObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) bin_outside_shape = utils.create_rounded_rectangle( @@ -309,15 +382,21 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ) compartment_solid = face.extrude( - fc.Vector(0, 0, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness), + fc.Vector( + 0, + 0, + obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, + ), + ) + fuse_total = fuse_total.cut( + feat.make_eco_compartments(obj, layout, compartment_solid), ) - fuse_total = fuse_total.cut(feat.make_eco_compartments(obj, layout, compartment_solid)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, bin_outside_shape)) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout)) if obj.LabelShelfStyle != "Off": fuse_total = fuse_total.fuse(feat.make_label_shelf(obj, "eco")) @@ -334,6 +413,7 @@ def __init__(self, obj: fc.DocumentObject) -> None: baseplate_feat.base_values_properties(obj) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: + obj = cast(BaseplateObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) baseplate_outside_shape = utils.create_rounded_rectangle( @@ -342,7 +422,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: 0, obj.BinOuterRadius, ) - baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) + baseplate_outside_shape.translate( + fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0) + ) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -376,7 +458,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.MagnetHoleDepth - obj.MagnetBase, obj.BinOuterRadius, ) - baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) + baseplate_outside_shape.translate( + fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0) + ) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -414,7 +498,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.BaseThickness, obj.BinOuterRadius, ) - baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) + baseplate_outside_shape.translate( + fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0) + ) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -427,7 +513,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) + fuse_total = fuse_total.cut( + baseplate_feat.make_screw_bottom_chamfer(obj, layout) + ) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -513,7 +601,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, layout, obj.TotalHeight - obj.BaseProfileHeight + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -589,7 +679,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, layout, obj.TotalHeight - obj.BaseProfileHeight + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -667,7 +759,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, layout, obj.TotalHeight - obj.BaseProfileHeight + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -697,7 +791,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: layout, obj.TotalHeight, ) - inside_wall_solid_full_height = inside_wall_solid_full_height.cut(compartment_trim) + inside_wall_solid_full_height = inside_wall_solid_full_height.cut( + compartment_trim + ) inside_wall_solid_full_height = inside_wall_solid_full_height.removeSplitter() inside_wall_solid_full_height = vertical_edge_fillet( inside_wall_solid_full_height, @@ -777,7 +873,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, layout, obj.TotalHeight - obj.BaseProfileHeight + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -961,7 +1059,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) + fuse_total = fuse_total.cut( + baseplate_feat.make_screw_bottom_chamfer(obj, layout) + ) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -1054,7 +1154,9 @@ def execute(self, obj: Part.Feature) -> None: check_point = obj.Placement.Base + obj.Placement.Rotation.multVec( fc.Vector(stacking_lip_offset / 2), ) - if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside(check_point, 1e-6, False): # noqa: FBT003 + if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside( + check_point, 1e-6, False + ): # noqa: FBT003 width += stacking_lip_offset shape = label_shelf.from_angle( diff --git a/freecad/gridfinity_workbench/grid_initial_layout.py b/freecad/gridfinity_workbench/grid_initial_layout.py index 50af3b54..7f9ad978 100644 --- a/freecad/gridfinity_workbench/grid_initial_layout.py +++ b/freecad/gridfinity_workbench/grid_initial_layout.py @@ -1,8 +1,9 @@ """Makes grid layouts, calculates total width properties.""" -import FreeCAD as fc # noqa: N813 +import FreeCAD as fc from . import const +from .gridfinity_types import * def _location_properties(obj: fc.DocumentObject) -> None: @@ -67,7 +68,9 @@ def _grid_size_properties(obj: fc.DocumentObject) -> None: ).yGridSize = const.Y_GRID_SIZE -def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: +def rectangle_layout_properties( + obj: fc.DocumentObject, *, baseplate_default: bool +) -> None: """Create Rectangle Layout. Args: @@ -114,7 +117,7 @@ def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bo ) -def make_rectangle_layout(obj: fc.DocumentObject) -> list[list[bool]]: +def make_rectangle_layout(obj: RectangleLayoutMixin) -> list[list[bool]]: """Generate Rectanble layout and calculate relevant parameters.""" if obj.GenerationLocation == "Centered at Origin": if obj.Baseplate: @@ -130,7 +133,9 @@ def make_rectangle_layout(obj: fc.DocumentObject) -> list[list[bool]]: return [[True] * obj.yGridUnits for x in range(obj.xGridUnits)] -def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: +def custom_shape_layout_properties( + obj: fc.DocumentObject, *, baseplate_default: bool +) -> None: """Add relevant properties for a custom shape gridfinity object. Args: @@ -156,7 +161,7 @@ def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: ).Baseplate = baseplate_default -def make_custom_shape_layout(obj: fc.DocumentObject, layout: list[list[bool]]) -> None: +def make_custom_shape_layout(obj: LayoutMixin, layout: list[list[bool]]) -> None: """Calculate values for custom shape. Args: @@ -184,5 +189,9 @@ def make_custom_shape_layout(obj: fc.DocumentObject, layout: list[list[bool]]) - obj.xTotalWidth = (x_max_grid + 1 - x_min_grid) * obj.xGridSize obj.yTotalWidth = (y_max_grid + 1 - y_min_grid) * obj.yGridSize else: - obj.xTotalWidth = (x_max_grid + 1 - x_min_grid) * obj.xGridSize - obj.Clearance * 2 - obj.yTotalWidth = (y_max_grid + 1 - y_min_grid) * obj.yGridSize - obj.Clearance * 2 + obj.xTotalWidth = ( + x_max_grid + 1 - x_min_grid + ) * obj.xGridSize - obj.Clearance * 2 + obj.yTotalWidth = ( + y_max_grid + 1 - y_min_grid + ) * obj.yGridSize - obj.Clearance * 2 diff --git a/freecad/gridfinity_workbench/gridfinity_types.py b/freecad/gridfinity_workbench/gridfinity_types.py new file mode 100644 index 00000000..b1066e66 --- /dev/null +++ b/freecad/gridfinity_workbench/gridfinity_types.py @@ -0,0 +1,148 @@ +"""Type declarations for casting""" + +# ruff: noqa: D101, D102, D107, N815 + +import typing +from typing import Final, TypeVar + +import FreeCAD as fc # noqa: N813 + +from . import const + +CALCULATED_VALUE = 0 + + +class LayoutMixin(fc.DocumentObject): + GenerationLocation: str = "" + xLocationOffset: float = 0.0 + yLocationOffset: float = 0.0 + xTotalWidth: float = 0.0 + yTotalWidth: float = 0.0 + xGridSize: float = const.X_GRID_SIZE + yGridSize: float = const.Y_GRID_SIZE + Baseplate: bool = False + + +class RectangleLayoutMixin(LayoutMixin): + xGridUnits: int = const.X_GRID_UNITS + yGridUnits: int = const.Y_GRID_UNITS + + +class SolidShapeMixin(LayoutMixin): + TotalHeight: float = CALCULATED_VALUE + + +class BaseMixin(SolidShapeMixin): + BaseProfileBottomChamfer: float = const.BIN_BASE_BOTTOM_CHAMFER + BaseProfileVerticalSection: float = const.BIN_BASE_VERTICAL_SECTION + BaseProfileTopChamfer: float = const.BIN_BASE_TOP_CHAMFER + BinOuterRadius: float = const.BIN_OUTER_RADIUS + BinVerticalRadius: float = const.BIN_BASE_VERTICAL_RADIUS + BinBottomRadius: float = const.BIN_BASE_BOTTOM_RADIUS + Clearance: float = const.CLEARANCE + BaseProfileHeight: Final[float] = CALCULATED_VALUE + + +class BaseplateMixin(BaseMixin): + BaseplateTopLedgeWidth: float = const.BASEPLATE_TOP_LEDGE_WIDTH + + +class HoleMixin(SolidShapeMixin): + MagnetHoles: bool = const.MAGNET_HOLES + ScrewHoles: bool = const.SCREW_HOLES + SequentialBridgingLayerHeight: float = const.SEQUENTIAL_BRIDGING_LAYER_HEIGHT + MagnetHolesShape: str = const.HOLE_SHAPES[0] + MagnetHoleDiameter: float = const.MAGNET_HOLE_DIAMETER + MagnetHoleDepth: float = const.MAGNET_HOLE_DEPTH + ScrewHoleDiameter: float = const.SCREW_HOLE_DIAMETER + ScrewHoleDepth: float = const.SCREW_HOLE_DEPTH + MagnetHoleDistanceFromEdge: float = const.MAGNET_HOLE_DISTANCE_FROM_EDGE + + +class SolidMidSectionMixin(BaseMixin): + HeightUnits: int = 1 + ## Gridfinity Non Standard Parameters + CustomHeight: float = 42 + NonStandardHeight: bool = False + WallThickness: float = 2 + HeightUnitValue: float = const.HEIGHT_UNIT_VALUE + + +class StackingLipMixin(SolidMidSectionMixin): + StackingLip: bool = False + StackingLipNotches: bool = False + StackingLipTopLedge: float = const.STACKING_LIP_TOP_LEDGE + StackingLipTopChamfer: float = 0.0 + StackingLipBottomChamfer: float = const.STACKING_LIP_BOTTOM_CHAMFER + StackingLipVerticalSection: float = const.STACKING_LIP_VERTICAL_SECTION + StackingLipNotchesChamfer: float = const.STACKING_LIP_NOTCHES_CHAMFER + + +class SimpleBinMixin(StackingLipMixin, HoleMixin): + pass + + +class LabelShelfMixin: + LabelShelfStyle: str = "" + LabelShelfPlacement: str = "" + LabelShelfWidth: float = const.LABEL_SHELF_WIDTH + LabelShelffloat: float = const.LABEL_SHELF_LENGTH + LabelShelfAngle: float = const.LABEL_SHELF_ANGLE + LabelShelfStackingOffset: float = const.LABEL_SHELF_STACKING_OFFSET + LabelShelfVerticalThickness: float = const.LABEL_SHELF_VERTICAL_THICKNESS + + +class CompartmentsMixin(SimpleBinMixin, LabelShelfMixin): + xDividers: int = const.X_DIVIDERS + yDividers: int = const.Y_DIVIDERS + InsideFilletRadius: float = const.INSIDE_FILLET_RADIUS + DividerThickness: float = const.DIVIDER_THICKNESS + xDividerHeight: float = const.CUSTOM_X_DIVIDER_HEIGHT + yDividerHeight: float = const.CUSTOM_Y_DIVIDER_HEIGHT + UsableHeight: float = CALCULATED_VALUE + + +class EcoCompartmentsMixin(CompartmentsMixin): + BaseWallThickness: float = const.BASE_WALL_THICKNESS + + +class ScoopMixin(CompartmentsMixin): + ScoopRadius: float = const.SCOOP_RADIUS + Scoop: bool = False + + +class BaseplateObject( + RectangleLayoutMixin, + BaseplateMixin, +): + pass + + +class FullBinObject( + RectangleLayoutMixin, + StackingLipMixin, + HoleMixin, +): + ## Gridfinity Non Standard Parameters + RecessedTopDepth: float = const.RECESSED_TOP_DEPTH + + +class StorageBinObject( + RectangleLayoutMixin, + ScoopMixin, +): + pass + + +class EcoBinObject( + RectangleLayoutMixin, + EcoCompartmentsMixin, +): + pass + + +GridfinityObject = TypeVar("GridfinityObject", bound=fc.DocumentObject) + + +def cast(type: type[GridfinityObject], obj: fc.DocumentObject) -> GridfinityObject: + return typing.cast(GridfinityObject, typing.cast(object, obj)) diff --git a/freecad/gridfinity_workbench/utils.py b/freecad/gridfinity_workbench/utils.py index 0408be9e..1723f285 100644 --- a/freecad/gridfinity_workbench/utils.py +++ b/freecad/gridfinity_workbench/utils.py @@ -13,6 +13,7 @@ import FreeCADGui as fcg # noqa: N813 import Part + if TYPE_CHECKING: from collections.abc import Sequence @@ -73,8 +74,8 @@ def copy_and_translate(shape: Part.Shape, vec_list: list[fc.Vector]) -> Part.Sha def copy_in_layout( shape: Part.Shape, layout: GridfinityLayout, - x_gird_size: fc.Units.Quantity, - y_grid_size: fc.Units.Quantity, + x_gird_size: float, + y_grid_size: float, ) -> Part.Shape: """Copy a shape in a layout. From 007b047025679e6b29d728f4cf8d3f233620b165 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Thu, 8 Jan 2026 16:01:27 +0300 Subject: [PATCH 02/17] Add notches recess and fix notches positioning --- freecad/gridfinity_workbench/const.py | 3 +- .../feature_construction.py | 40 +++++++++++++++---- freecad/gridfinity_workbench/features.py | 36 ++++++++++------- .../gridfinity_workbench/gridfinity_types.py | 1 + 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/freecad/gridfinity_workbench/const.py b/freecad/gridfinity_workbench/const.py index 9ceadf57..65d4bae6 100644 --- a/freecad/gridfinity_workbench/const.py +++ b/freecad/gridfinity_workbench/const.py @@ -42,7 +42,8 @@ STACKING_LIP_BOTTOM_CHAMFER = 0.7 STACKING_LIP_VERTICAL_SECTION = 1.8 STACKING_LIP_NOTCHES = False -STACKING_LIP_NOTCHES_CHAMFER = 0.7 +STACKING_LIP_NOTCHES_CHAMFER = 0.6 +STACKING_LIP_NOTCHES_RECESS = 0.6 RECESSED_TOP_DEPTH = 0 SEQUENTIAL_BRIDGING_LAYER_HEIGHT = 0.2 diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 1fb4a0d8..cdc755cf 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1397,8 +1397,8 @@ def _stacking_lip_plate( return fuse_total.translate( fc.Vector( - obj.xGridSize / 2 - obj.xLocationOffset, - obj.yGridSize / 2 - obj.yLocationOffset, + obj.xGridSize / 2, + obj.yGridSize / 2, ), ) @@ -1469,6 +1469,12 @@ def stacking_lip_properties( "GridfinityNonStandard", f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER + obj.addProperty( + "App::PropertyLength", + "StackingLipNotchesRecess", + "GridfinityNonStandard", + f"Recess of the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", + ).StackingLipNotchesRecess = const.STACKING_LIP_NOTCHES_RECESS def make_stacking_lip( @@ -1529,7 +1535,6 @@ def make_stacking_lip( ) ) base = _stacking_lip_plate(obj, layout) - # base.translate(fc.Vector(0, 0, obj.TotalHeight)) cover = cover.cut(base) offset = ( obj.StackingLipTopLedge @@ -1548,7 +1553,25 @@ def make_stacking_lip( obj.yTotalWidth / 2 + obj.Clearance, ) ) - cover = cover.cut(cutout) + + if obj.StackingLipNotchesRecess > 0: + chamfer_offset = obj.StackingLipTopLedge + obj.StackingLipNotchesRecess + cutout_recess = utils.rounded_rectangle_chamfer( + obj.xTotalWidth - chamfer_offset * 2, + obj.yTotalWidth - chamfer_offset * 2, + height - obj.StackingLipNotchesRecess, + obj.StackingLipNotchesRecess, + obj.BinOuterRadius + - obj.StackingLipTopLedge + - obj.StackingLipNotchesRecess, + ).translate( + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ) + ) + cutout = cutout.fuse(cutout_recess) + if obj.StackingLipNotchesChamfer > 0: chamfer_offset = ( obj.StackingLipTopLedge @@ -1558,20 +1581,21 @@ def make_stacking_lip( cutout_chamfer = utils.rounded_rectangle_chamfer( obj.xTotalWidth - chamfer_offset * 2, obj.yTotalWidth - chamfer_offset * 2, - height - obj.StackingLipNotchesChamfer, + height - obj.StackingLipNotchesRecess - obj.StackingLipNotchesChamfer, obj.StackingLipNotchesChamfer, obj.BinOuterRadius - obj.StackingLipTopLedge - - obj.StackingLipTopChamfer - + obj.StackingLipNotchesChamfer, + - obj.StackingLipNotchesRecess + - obj.StackingLipNotchesChamfer, ).translate( fc.Vector( obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance, ) ) - cover = cover.cut(cutout_chamfer) + cutout = cutout.fuse(cutout_chamfer) + cover = cover.cut(cutout) stacking_lip = stacking_lip.fuse(cover) stacking_lip = stacking_lip.translate( diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index b57f01c3..0bd5be8f 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -107,20 +107,28 @@ def addPropertyIfMissing( def onDocumentRestored(self, obj: fc.DocumentObject) -> None: # noqa: N802 # Syntax: addProperty(type, name, group, docstring) - self.addPropertyIfMissing( - obj, - "App::PropertyBool", - "StackingLipNotches", - "GridfinityNonStandard", - "Toggle the notches on the stacking lip on or off", - ).StackingLipNotches = const.STACKING_LIP_NOTCHES - self.addPropertyIfMissing( - obj, - "App::PropertyLength", - "StackingLipNotchesChamfer", - "GridfinityNonStandard", - f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", - ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER + if hasattr(obj, "StackingLip"): + self.addPropertyIfMissing( + obj, + "App::PropertyBool", + "StackingLipNotches", + "GridfinityNonStandard", + "Toggle the notches on the stacking lip on or off", + ).StackingLipNotches = const.STACKING_LIP_NOTCHES + self.addPropertyIfMissing( + obj, + "App::PropertyLength", + "StackingLipNotchesChamfer", + "GridfinityNonStandard", + f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", + ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER + self.addPropertyIfMissing( + obj, + "App::PropertyLength", + "StackingLipNotchesRecess", + "GridfinityNonStandard", + f"Recess of the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", + ).StackingLipNotchesRecess = const.STACKING_LIP_NOTCHES_RECESS class FullBin(FoundationGridfinity): diff --git a/freecad/gridfinity_workbench/gridfinity_types.py b/freecad/gridfinity_workbench/gridfinity_types.py index b1066e66..c3f568b7 100644 --- a/freecad/gridfinity_workbench/gridfinity_types.py +++ b/freecad/gridfinity_workbench/gridfinity_types.py @@ -76,6 +76,7 @@ class StackingLipMixin(SolidMidSectionMixin): StackingLipBottomChamfer: float = const.STACKING_LIP_BOTTOM_CHAMFER StackingLipVerticalSection: float = const.STACKING_LIP_VERTICAL_SECTION StackingLipNotchesChamfer: float = const.STACKING_LIP_NOTCHES_CHAMFER + StackingLipNotchesRecess: float = const.STACKING_LIP_NOTCHES_RECESS class SimpleBinMixin(StackingLipMixin, HoleMixin): From 52801ee818465a8f68be4cc85f6ac83e065a6729 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 14:35:15 +0300 Subject: [PATCH 03/17] Clean --- .../feature_construction.py | 7 +- freecad/gridfinity_workbench/features.py | 2 - .../gridfinity_workbench/gridfinity_types.py | 91 ++++++++++--------- 3 files changed, 51 insertions(+), 49 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index cdc755cf..202c6226 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -89,7 +89,7 @@ def label_shelf_properties(obj: fc.DocumentObject, *, label_style_default: str) def make_label_shelf( - obj: fc.DocumentObject, bintype: Literal["eco", "standard"] + obj: gft.CompartmentsMixin, bintype: Literal["eco", "standard"] ) -> Part.Shape: """Create label shelf.""" if ( @@ -397,7 +397,7 @@ def make_fillet(rotation: float, translation: fc.Vector) -> Part.Shape: def _make_compartments_no_deviders( - obj: fc.DocumentObject, + obj: gft.CompartmentsMixin, func_fuse: Part.Shape, ) -> Part.Shape: # Fillet Bottom edges @@ -1366,7 +1366,7 @@ def _stacking_lip_plate( bottom_chamfer = utils.rounded_rectangle_chamfer( x_bt_cmf_width, y_bt_cmf_width, - zeromm, + 0, obj.StackingLipBottomChamfer, obj.BinOuterRadius - obj.StackingLipTopLedge @@ -1597,6 +1597,7 @@ def make_stacking_lip( cover = cover.cut(cutout) stacking_lip = stacking_lip.fuse(cover) + stacking_lip = stacking_lip.removeSplitter() stacking_lip = stacking_lip.translate( fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset), diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 0bd5be8f..48c203f0 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -29,8 +29,6 @@ ) from .version import __version__ -unitmm = fc.Units.Quantity("1 mm") - class FoundationGridfinity: def __init__(self, obj: fc.DocumentObject) -> None: diff --git a/freecad/gridfinity_workbench/gridfinity_types.py b/freecad/gridfinity_workbench/gridfinity_types.py index c3f568b7..170c68d5 100644 --- a/freecad/gridfinity_workbench/gridfinity_types.py +++ b/freecad/gridfinity_workbench/gridfinity_types.py @@ -11,15 +11,18 @@ CALCULATED_VALUE = 0 +Length = float +Angle = float + class LayoutMixin(fc.DocumentObject): GenerationLocation: str = "" - xLocationOffset: float = 0.0 - yLocationOffset: float = 0.0 - xTotalWidth: float = 0.0 - yTotalWidth: float = 0.0 - xGridSize: float = const.X_GRID_SIZE - yGridSize: float = const.Y_GRID_SIZE + xLocationOffset: Length = 0.0 + yLocationOffset: Length = 0.0 + xTotalWidth: Length = 0.0 + yTotalWidth: Length = 0.0 + xGridSize: Length = const.X_GRID_SIZE + yGridSize: Length = const.Y_GRID_SIZE Baseplate: bool = False @@ -29,54 +32,54 @@ class RectangleLayoutMixin(LayoutMixin): class SolidShapeMixin(LayoutMixin): - TotalHeight: float = CALCULATED_VALUE + TotalHeight: Length = CALCULATED_VALUE class BaseMixin(SolidShapeMixin): - BaseProfileBottomChamfer: float = const.BIN_BASE_BOTTOM_CHAMFER - BaseProfileVerticalSection: float = const.BIN_BASE_VERTICAL_SECTION - BaseProfileTopChamfer: float = const.BIN_BASE_TOP_CHAMFER - BinOuterRadius: float = const.BIN_OUTER_RADIUS - BinVerticalRadius: float = const.BIN_BASE_VERTICAL_RADIUS - BinBottomRadius: float = const.BIN_BASE_BOTTOM_RADIUS - Clearance: float = const.CLEARANCE - BaseProfileHeight: Final[float] = CALCULATED_VALUE + BaseProfileBottomChamfer: Length = const.BIN_BASE_BOTTOM_CHAMFER + BaseProfileVerticalSection: Length = const.BIN_BASE_VERTICAL_SECTION + BaseProfileTopChamfer: Length = const.BIN_BASE_TOP_CHAMFER + BinOuterRadius: Length = const.BIN_OUTER_RADIUS + BinVerticalRadius: Length = const.BIN_BASE_VERTICAL_RADIUS + BinBottomRadius: Length = const.BIN_BASE_BOTTOM_RADIUS + Clearance: Length = const.CLEARANCE + BaseProfileHeight: Final[Length] = CALCULATED_VALUE class BaseplateMixin(BaseMixin): - BaseplateTopLedgeWidth: float = const.BASEPLATE_TOP_LEDGE_WIDTH + BaseplateTopLedgeWidth: Length = const.BASEPLATE_TOP_LEDGE_WIDTH class HoleMixin(SolidShapeMixin): MagnetHoles: bool = const.MAGNET_HOLES ScrewHoles: bool = const.SCREW_HOLES - SequentialBridgingLayerHeight: float = const.SEQUENTIAL_BRIDGING_LAYER_HEIGHT + SequentialBridgingLayerHeight: Length = const.SEQUENTIAL_BRIDGING_LAYER_HEIGHT MagnetHolesShape: str = const.HOLE_SHAPES[0] - MagnetHoleDiameter: float = const.MAGNET_HOLE_DIAMETER - MagnetHoleDepth: float = const.MAGNET_HOLE_DEPTH - ScrewHoleDiameter: float = const.SCREW_HOLE_DIAMETER - ScrewHoleDepth: float = const.SCREW_HOLE_DEPTH - MagnetHoleDistanceFromEdge: float = const.MAGNET_HOLE_DISTANCE_FROM_EDGE + MagnetHoleDiameter: Length = const.MAGNET_HOLE_DIAMETER + MagnetHoleDepth: Length = const.MAGNET_HOLE_DEPTH + ScrewHoleDiameter: Length = const.SCREW_HOLE_DIAMETER + ScrewHoleDepth: Length = const.SCREW_HOLE_DEPTH + MagnetHoleDistanceFromEdge: Length = const.MAGNET_HOLE_DISTANCE_FROM_EDGE class SolidMidSectionMixin(BaseMixin): HeightUnits: int = 1 ## Gridfinity Non Standard Parameters - CustomHeight: float = 42 + CustomHeight: Length = 42 NonStandardHeight: bool = False - WallThickness: float = 2 - HeightUnitValue: float = const.HEIGHT_UNIT_VALUE + WallThickness: Length = 2 + HeightUnitValue: Length = const.HEIGHT_UNIT_VALUE class StackingLipMixin(SolidMidSectionMixin): StackingLip: bool = False StackingLipNotches: bool = False - StackingLipTopLedge: float = const.STACKING_LIP_TOP_LEDGE - StackingLipTopChamfer: float = 0.0 - StackingLipBottomChamfer: float = const.STACKING_LIP_BOTTOM_CHAMFER - StackingLipVerticalSection: float = const.STACKING_LIP_VERTICAL_SECTION - StackingLipNotchesChamfer: float = const.STACKING_LIP_NOTCHES_CHAMFER - StackingLipNotchesRecess: float = const.STACKING_LIP_NOTCHES_RECESS + StackingLipTopLedge: Length = const.STACKING_LIP_TOP_LEDGE + StackingLipTopChamfer: Length = 0.0 + StackingLipBottomChamfer: Length = const.STACKING_LIP_BOTTOM_CHAMFER + StackingLipVerticalSection: Length = const.STACKING_LIP_VERTICAL_SECTION + StackingLipNotchesChamfer: Length = const.STACKING_LIP_NOTCHES_CHAMFER + StackingLipNotchesRecess: Length = const.STACKING_LIP_NOTCHES_RECESS class SimpleBinMixin(StackingLipMixin, HoleMixin): @@ -86,29 +89,29 @@ class SimpleBinMixin(StackingLipMixin, HoleMixin): class LabelShelfMixin: LabelShelfStyle: str = "" LabelShelfPlacement: str = "" - LabelShelfWidth: float = const.LABEL_SHELF_WIDTH - LabelShelffloat: float = const.LABEL_SHELF_LENGTH - LabelShelfAngle: float = const.LABEL_SHELF_ANGLE - LabelShelfStackingOffset: float = const.LABEL_SHELF_STACKING_OFFSET - LabelShelfVerticalThickness: float = const.LABEL_SHELF_VERTICAL_THICKNESS + LabelShelfWidth: Length = const.LABEL_SHELF_WIDTH + LabelShelfLength: float = const.LABEL_SHELF_LENGTH + LabelShelfAngle: Angle = const.LABEL_SHELF_ANGLE + LabelShelfStackingOffset: Length = const.LABEL_SHELF_STACKING_OFFSET + LabelShelfVerticalThickness: Length = const.LABEL_SHELF_VERTICAL_THICKNESS class CompartmentsMixin(SimpleBinMixin, LabelShelfMixin): xDividers: int = const.X_DIVIDERS yDividers: int = const.Y_DIVIDERS - InsideFilletRadius: float = const.INSIDE_FILLET_RADIUS - DividerThickness: float = const.DIVIDER_THICKNESS - xDividerHeight: float = const.CUSTOM_X_DIVIDER_HEIGHT - yDividerHeight: float = const.CUSTOM_Y_DIVIDER_HEIGHT - UsableHeight: float = CALCULATED_VALUE + InsideFilletRadius: Length = const.INSIDE_FILLET_RADIUS + DividerThickness: Length = const.DIVIDER_THICKNESS + xDividerHeight: Length = const.CUSTOM_X_DIVIDER_HEIGHT + yDividerHeight: Length = const.CUSTOM_Y_DIVIDER_HEIGHT + UsableHeight: Length = CALCULATED_VALUE class EcoCompartmentsMixin(CompartmentsMixin): - BaseWallThickness: float = const.BASE_WALL_THICKNESS + BaseWallThickness: Length = const.BASE_WALL_THICKNESS class ScoopMixin(CompartmentsMixin): - ScoopRadius: float = const.SCOOP_RADIUS + ScoopRadius: Length = const.SCOOP_RADIUS Scoop: bool = False @@ -125,7 +128,7 @@ class FullBinObject( HoleMixin, ): ## Gridfinity Non Standard Parameters - RecessedTopDepth: float = const.RECESSED_TOP_DEPTH + RecessedTopDepth: Length = const.RECESSED_TOP_DEPTH class StorageBinObject( From 6eaefbb6e7f8ff866d115a35feb11f4ad1d0fe4a Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 14:44:51 +0300 Subject: [PATCH 04/17] Clean --- .../feature_construction.py | 2 +- freecad/gridfinity_workbench/features.py | 29 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 202c6226..67755e9f 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1366,7 +1366,7 @@ def _stacking_lip_plate( bottom_chamfer = utils.rounded_rectangle_chamfer( x_bt_cmf_width, y_bt_cmf_width, - 0, + zeromm, obj.StackingLipBottomChamfer, obj.BinOuterRadius - obj.StackingLipTopLedge diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 48c203f0..7e11bab3 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -74,9 +74,10 @@ def loads(self, state: dict) -> None: State argument required, otherwise expecting argument error message. """ - def addPropertyIfMissing( + def add_property_if_missing( self, obj: fc.DocumentObject, + default_value: object, type: str, name: str, group: str = "", @@ -87,7 +88,7 @@ def addPropertyIfMissing( enum_vals=[], ): if name not in obj.PropertiesList: - return obj.addProperty( + obj = obj.addProperty( type=type, name=name, group=group, @@ -97,36 +98,34 @@ def addPropertyIfMissing( hidden=hidden, enum_vals=enum_vals, ) - from types import SimpleNamespace + setattr(obj, name, default_value) - dummy = SimpleNamespace() - setattr(dummy, name, None) - return dummy - - def onDocumentRestored(self, obj: fc.DocumentObject) -> None: # noqa: N802 - # Syntax: addProperty(type, name, group, docstring) + def onDocumentRestored(self, obj: fc.DocumentObject) -> None: if hasattr(obj, "StackingLip"): - self.addPropertyIfMissing( + self.add_property_if_missing( obj, + const.STACKING_LIP_NOTCHES, "App::PropertyBool", "StackingLipNotches", "GridfinityNonStandard", "Toggle the notches on the stacking lip on or off", - ).StackingLipNotches = const.STACKING_LIP_NOTCHES - self.addPropertyIfMissing( + ) + self.add_property_if_missing( obj, + const.STACKING_LIP_NOTCHES_CHAMFER, "App::PropertyLength", "StackingLipNotchesChamfer", "GridfinityNonStandard", f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", - ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER - self.addPropertyIfMissing( + ) + self.add_property_if_missing( obj, + const.STACKING_LIP_NOTCHES_RECESS, "App::PropertyLength", "StackingLipNotchesRecess", "GridfinityNonStandard", f"Recess of the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", - ).StackingLipNotchesRecess = const.STACKING_LIP_NOTCHES_RECESS + ) class FullBin(FoundationGridfinity): From dd647e701222824a82de290efeec7f643abf1a90 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 14:52:13 +0300 Subject: [PATCH 05/17] Clean --- .../feature_construction.py | 27 +------------------ freecad/gridfinity_workbench/features.py | 12 ++++++--- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 67755e9f..bfe657ef 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1478,7 +1478,7 @@ def stacking_lip_properties( def make_stacking_lip( - obj: gft.StackingLipMixin, layout: GridfinityLayout + obj: gft.StackingLipMixin, layout: GridfinityLayout, bin_outside_shape: Part.Wire ) -> Part.Shape: """Create stacking lip based on input bin shape. @@ -1488,31 +1488,6 @@ def make_stacking_lip( """ - bin_outside_shape = utils.create_rounded_rectangle( - obj.xTotalWidth, - obj.yTotalWidth, - 0, - obj.BinOuterRadius, - ) - bin_outside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, - obj.yTotalWidth / 2 + obj.Clearance, - ), - ) - - bin_inside_shape = utils.create_rounded_rectangle( - obj.xTotalWidth - obj.WallThickness * 2, - obj.yTotalWidth - obj.WallThickness * 2, - 0, - obj.BinOuterRadius - obj.WallThickness, - ) - bin_inside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, - obj.yTotalWidth / 2 + obj.Clearance, - ), - ) wire = _stacking_lip_profile(obj) stacking_lip = Part.Wire(bin_outside_shape).makePipe(wire) stacking_lip = Part.makeSolid(stacking_lip) diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 7e11bab3..25ace62f 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -191,7 +191,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout)) + fuse_total = fuse_total.fuse( + feat.make_stacking_lip(obj, layout, bin_outside_shape) + ) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -292,7 +294,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_compartments(obj, compartments)) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout)) + fuse_total = fuse_total.fuse( + feat.make_stacking_lip(obj, layout, bin_outside_shape) + ) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -401,7 +405,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout)) + fuse_total = fuse_total.fuse( + feat.make_stacking_lip(obj, layout, bin_outside_shape) + ) if obj.LabelShelfStyle != "Off": fuse_total = fuse_total.fuse(feat.make_label_shelf(obj, "eco")) From 2eb89d0c8609cd935f0534307cc67be42b1b56ff Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:00:23 +0300 Subject: [PATCH 06/17] Format. --- .../feature_construction.py | 112 ++++++------------ freecad/gridfinity_workbench/features.py | 72 +++-------- .../grid_initial_layout.py | 16 +-- 3 files changed, 58 insertions(+), 142 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index bfe657ef..4fcf69a2 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -88,9 +88,7 @@ def label_shelf_properties(obj: fc.DocumentObject, *, label_style_default: str) ).LabelShelfVerticalThickness = const.LABEL_SHELF_VERTICAL_THICKNESS -def make_label_shelf( - obj: gft.CompartmentsMixin, bintype: Literal["eco", "standard"] -) -> Part.Shape: +def make_label_shelf(obj: gft.CompartmentsMixin, bintype: Literal["eco", "standard"]) -> Part.Shape: """Create label shelf.""" if ( bintype == "eco" @@ -98,9 +96,7 @@ def make_label_shelf( and obj.LabelShelfStyle != "Overhang" ): obj.LabelShelfStyle = "Overhang" - fc.Console.PrintWarning( - "Label shelf style set to Overhang due to low bin height\n" - ) + fc.Console.PrintWarning("Label shelf style set to Overhang due to low bin height\n") xdiv = obj.xDividers + 1 ydiv = obj.yDividers + 1 @@ -145,9 +141,7 @@ def make_label_shelf( ) if height > obj.UsableHeight: - boundingbox = Part.makeBox( - width, length, height, fc.Vector(0, 0, -obj.UsableHeight) - ) + boundingbox = Part.makeBox(width, length, height, fc.Vector(0, 0, -obj.UsableHeight)) funcfuse = funcfuse.common(boundingbox) funcfuse = utils.copy_in_grid( @@ -211,9 +205,9 @@ def make_scoop(obj: gft.ScoopMixin) -> Part.Shape: scooprad2 = obj.ScoopRadius + unitmm scooprad3 = obj.ScoopRadius + unitmm - xcomp_w = ( - obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness - ) / (obj.xDividers + 1) + xcomp_w = (obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness) / ( + obj.xDividers + 1 + ) xdivscoop = obj.xDividerHeight - obj.HeightUnitValue - obj.LabelShelfStackingOffset @@ -267,9 +261,9 @@ def make_scoop(obj: gft.ScoopMixin) -> Part.Shape: face = Part.Face(wire) xdiv = obj.xDividers + 1 - compwidth = ( - obj.xTotalWidth - obj.WallThickness * 2 - obj.DividerThickness * obj.xDividers - ) / (xdiv) + compwidth = (obj.xTotalWidth - obj.WallThickness * 2 - obj.DividerThickness * obj.xDividers) / ( + xdiv + ) scoopbox = Part.makeBox( obj.StackingLipBottomChamfer @@ -421,12 +415,12 @@ def _make_compartments_with_deviders( stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm - xcomp_w = ( - obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness - ) / (obj.xDividers + 1) - ycomp_w = ( - obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness - ) / (obj.yDividers + 1) + xcomp_w = (obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness) / ( + obj.xDividers + 1 + ) + ycomp_w = (obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness) / ( + obj.yDividers + 1 + ) xtranslate = xcomp_w + obj.WallThickness - obj.DividerThickness ytranslate = ycomp_w + obj.WallThickness @@ -474,9 +468,7 @@ def _make_compartments_with_deviders( return func_fuse -def compartments_properties( - obj: fc.DocumentObject, x_div_default: int, y_div_default: int -) -> None: +def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_default: int) -> None: """Create bin compartments with the option for dividers. Args: @@ -546,9 +538,7 @@ def compartments_properties( ) -def make_compartments( - obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape -) -> Part.Shape: +def make_compartments(obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape) -> Part.Shape: """Create compartment cutout objects. Args: @@ -561,10 +551,7 @@ def make_compartments( """ ## Error Checks divmin = ( - obj.HeightUnitValue - + obj.InsideFilletRadius - + 0.05 * unitmm - + obj.LabelShelfStackingOffset + obj.HeightUnitValue + obj.InsideFilletRadius + 0.05 * unitmm + obj.LabelShelfStackingOffset ) if obj.xDividerHeight < divmin and obj.xDividerHeight != 0: @@ -620,9 +607,7 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: p.recompute() p_wire: Part.Wire = p.Shape - magnet_hole_shape = Part.Face(p_wire).extrude( - fc.Vector(0, 0, obj.MagnetHoleDepth) - ) + magnet_hole_shape = Part.Face(p_wire).extrude(fc.Vector(0, 0, obj.MagnetHoleDepth)) fc.ActiveDocument.removeObject(p.Name) else: magnet_hole_shape = Part.makeCylinder( @@ -662,8 +647,7 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: ) arc_pt_off_x = ( math.sqrt( - ((obj.MagnetHoleDiameter / 2) ** 2) - - ((obj.ScrewHoleDiameter / 2) ** 2), + ((obj.MagnetHoleDiameter / 2) ** 2) - ((obj.ScrewHoleDiameter / 2) ** 2), ) ) * unitmm arc_pt_off_y = obj.ScrewHoleDiameter / 2 @@ -696,9 +680,7 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: return bottom_hole_shape -def _eco_bin_deviders( - obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: float -) -> Part.Shape: +def _eco_bin_deviders(obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: float) -> Part.Shape: stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -909,9 +891,7 @@ def make_eco_compartments( obj.BaseProfileBottomChamfer + obj.BaseProfileVerticalSection + base_offset ): tp_chf_offset = (obj.MagnetHoleDepth + obj.BaseWallThickness) - ( - obj.BaseProfileBottomChamfer - + obj.BaseProfileVerticalSection - + base_offset + obj.BaseProfileBottomChamfer + obj.BaseProfileVerticalSection + base_offset ) bottom_chamfer = utils.rounded_rectangle_chamfer( @@ -976,12 +956,12 @@ def make_eco_compartments( func_fuse = func_fuse.cut(outer_trim2) - xcomp_w = ( - obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness - ) / (obj.xDividers + 1) - ycomp_w = ( - obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness - ) / (obj.yDividers + 1) + xcomp_w = (obj.xTotalWidth - obj.WallThickness * 2 - obj.xDividers * obj.DividerThickness) / ( + obj.xDividers + 1 + ) + ycomp_w = (obj.yTotalWidth - obj.WallThickness * 2 - obj.yDividers * obj.DividerThickness) / ( + obj.yDividers + 1 + ) if obj.xDividers > 0 or obj.yDividers > 0: func_fuse = func_fuse.cut(_eco_bin_deviders(obj, xcomp_w, ycomp_w)) @@ -1128,9 +1108,7 @@ def make_complex_bin_base( top_chamfer = utils.rounded_rectangle_chamfer( x_vert_width, y_vert_width, - -obj.TotalHeight - + obj.BaseProfileBottomChamfer - + obj.BaseProfileVerticalSection, + -obj.TotalHeight + obj.BaseProfileBottomChamfer + obj.BaseProfileVerticalSection, obj.BaseProfileTopChamfer, obj.BinVerticalRadius, ) @@ -1163,18 +1141,14 @@ def blank_bin_recessed_top_properties(obj: fc.DocumentObject) -> None: ).RecessedTopDepth = const.RECESSED_TOP_DEPTH -def make_blank_bin_recessed_top( - obj: fc.DocumentObject, bin_inside_shape: Part.Wire -) -> Part.Shape: +def make_blank_bin_recessed_top(obj: fc.DocumentObject, bin_inside_shape: Part.Wire) -> Part.Shape: """Generate Rectanble layout and calculate relevant parameters.""" face = Part.Face(bin_inside_shape) fuse_total = face.extrude(fc.Vector(0, 0, -obj.RecessedTopDepth)) return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def bin_bottom_holes_properties( - obj: fc.DocumentObject, *, magnet_holes_default: bool -) -> None: +def bin_bottom_holes_properties(obj: fc.DocumentObject, *, magnet_holes_default: bool) -> None: """Create bin solid mid section. Args: @@ -1291,20 +1265,14 @@ def make_bin_bottom_holes( def _stacking_lip_profile(obj: gft.StackingLipMixin) -> Part.Wire: """Create stacking lip profile wire.""" ## Calculated Values - obj.StackingLipTopChamfer = ( - obj.BaseProfileTopChamfer - obj.Clearance - obj.StackingLipTopLedge - ) + obj.StackingLipTopChamfer = obj.BaseProfileTopChamfer - obj.Clearance - obj.StackingLipTopLedge x1 = obj.Clearance x2 = x1 + obj.StackingLipTopLedge x3 = x2 + obj.StackingLipTopChamfer x4 = x3 + obj.StackingLipBottomChamfer x5 = obj.Clearance + obj.WallThickness y = obj.yGridSize / 2 - z1 = ( - obj.StackingLipBottomChamfer - + obj.StackingLipVerticalSection - + obj.StackingLipTopChamfer - ) + z1 = obj.StackingLipBottomChamfer + obj.StackingLipVerticalSection + obj.StackingLipTopChamfer z2 = obj.StackingLipBottomChamfer + obj.StackingLipVerticalSection z3 = obj.StackingLipBottomChamfer z4 = -obj.StackingLipVerticalSection @@ -1511,11 +1479,7 @@ def make_stacking_lip( ) base = _stacking_lip_plate(obj, layout) cover = cover.cut(base) - offset = ( - obj.StackingLipTopLedge - + obj.StackingLipTopChamfer - + obj.StackingLipBottomChamfer - ) + offset = obj.StackingLipTopLedge + obj.StackingLipTopChamfer + obj.StackingLipBottomChamfer cutout = utils.rounded_rectangle_extrude( obj.xTotalWidth - offset * 2, obj.yTotalWidth - offset * 2, @@ -1536,9 +1500,7 @@ def make_stacking_lip( obj.yTotalWidth - chamfer_offset * 2, height - obj.StackingLipNotchesRecess, obj.StackingLipNotchesRecess, - obj.BinOuterRadius - - obj.StackingLipTopLedge - - obj.StackingLipNotchesRecess, + obj.BinOuterRadius - obj.StackingLipTopLedge - obj.StackingLipNotchesRecess, ).translate( fc.Vector( obj.xTotalWidth / 2 + obj.Clearance, @@ -1549,9 +1511,7 @@ def make_stacking_lip( if obj.StackingLipNotchesChamfer > 0: chamfer_offset = ( - obj.StackingLipTopLedge - + obj.StackingLipTopChamfer - + obj.StackingLipBottomChamfer + obj.StackingLipTopLedge + obj.StackingLipTopChamfer + obj.StackingLipBottomChamfer ) cutout_chamfer = utils.rounded_rectangle_chamfer( obj.xTotalWidth - chamfer_offset * 2, diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 25ace62f..e74572c4 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -165,9 +165,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius, ) bin_outside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance - ), + fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), ) bin_inside_shape = utils.create_rounded_rectangle( @@ -177,23 +175,17 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius - obj.WallThickness, ) bin_inside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance - ), + fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), ) fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) fuse_total = fuse_total.fuse(feat.make_complex_bin_base(obj, layout)) if obj.RecessedTopDepth > 0: - fuse_total = fuse_total.cut( - feat.make_blank_bin_recessed_top(obj, bin_inside_shape) - ) + fuse_total = fuse_total.cut(feat.make_blank_bin_recessed_top(obj, bin_inside_shape)) if obj.StackingLip: - fuse_total = fuse_total.fuse( - feat.make_stacking_lip(obj, layout, bin_outside_shape) - ) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -248,9 +240,7 @@ def __init__( feat.stacking_lip_properties(obj, stacking_lip_default=const.STACKING_LIP) feat.bin_bottom_holes_properties(obj, magnet_holes_default=const.MAGNET_HOLES) feat.bin_base_values_properties(obj) - feat.compartments_properties( - obj, x_div_default=x_div_default, y_div_default=y_div_default - ) + feat.compartments_properties(obj, x_div_default=x_div_default, y_div_default=y_div_default) feat.label_shelf_properties(obj, label_style_default=label_style_default) feat.scoop_properties(obj, scoop_default=scoop_default) @@ -294,9 +284,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_compartments(obj, compartments)) if obj.StackingLip: - fuse_total = fuse_total.fuse( - feat.make_stacking_lip(obj, layout, bin_outside_shape) - ) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -405,9 +393,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) if obj.StackingLip: - fuse_total = fuse_total.fuse( - feat.make_stacking_lip(obj, layout, bin_outside_shape) - ) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) if obj.LabelShelfStyle != "Off": fuse_total = fuse_total.fuse(feat.make_label_shelf(obj, "eco")) @@ -433,9 +419,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: 0, obj.BinOuterRadius, ) - baseplate_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0) - ) + baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -469,9 +453,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.MagnetHoleDepth - obj.MagnetBase, obj.BinOuterRadius, ) - baseplate_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0) - ) + baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -509,9 +491,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.BaseThickness, obj.BinOuterRadius, ) - baseplate_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0) - ) + baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -524,9 +504,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut( - baseplate_feat.make_screw_bottom_chamfer(obj, layout) - ) + fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -612,9 +590,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, layout, obj.TotalHeight - obj.BaseProfileHeight - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -690,9 +666,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, layout, obj.TotalHeight - obj.BaseProfileHeight - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -770,9 +744,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, layout, obj.TotalHeight - obj.BaseProfileHeight - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -802,9 +774,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: layout, obj.TotalHeight, ) - inside_wall_solid_full_height = inside_wall_solid_full_height.cut( - compartment_trim - ) + inside_wall_solid_full_height = inside_wall_solid_full_height.cut(compartment_trim) inside_wall_solid_full_height = inside_wall_solid_full_height.removeSplitter() inside_wall_solid_full_height = vertical_edge_fillet( inside_wall_solid_full_height, @@ -884,9 +854,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, layout, obj.TotalHeight - obj.BaseProfileHeight - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -1070,9 +1038,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut( - baseplate_feat.make_screw_bottom_chamfer(obj, layout) - ) + fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -1165,9 +1131,7 @@ def execute(self, obj: Part.Feature) -> None: check_point = obj.Placement.Base + obj.Placement.Rotation.multVec( fc.Vector(stacking_lip_offset / 2), ) - if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside( - check_point, 1e-6, False - ): # noqa: FBT003 + if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside(check_point, 1e-6, False): # noqa: FBT003 width += stacking_lip_offset shape = label_shelf.from_angle( diff --git a/freecad/gridfinity_workbench/grid_initial_layout.py b/freecad/gridfinity_workbench/grid_initial_layout.py index 7f9ad978..9d666a6f 100644 --- a/freecad/gridfinity_workbench/grid_initial_layout.py +++ b/freecad/gridfinity_workbench/grid_initial_layout.py @@ -68,9 +68,7 @@ def _grid_size_properties(obj: fc.DocumentObject) -> None: ).yGridSize = const.Y_GRID_SIZE -def rectangle_layout_properties( - obj: fc.DocumentObject, *, baseplate_default: bool -) -> None: +def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: """Create Rectangle Layout. Args: @@ -133,9 +131,7 @@ def make_rectangle_layout(obj: RectangleLayoutMixin) -> list[list[bool]]: return [[True] * obj.yGridUnits for x in range(obj.xGridUnits)] -def custom_shape_layout_properties( - obj: fc.DocumentObject, *, baseplate_default: bool -) -> None: +def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: """Add relevant properties for a custom shape gridfinity object. Args: @@ -189,9 +185,5 @@ def make_custom_shape_layout(obj: LayoutMixin, layout: list[list[bool]]) -> None obj.xTotalWidth = (x_max_grid + 1 - x_min_grid) * obj.xGridSize obj.yTotalWidth = (y_max_grid + 1 - y_min_grid) * obj.yGridSize else: - obj.xTotalWidth = ( - x_max_grid + 1 - x_min_grid - ) * obj.xGridSize - obj.Clearance * 2 - obj.yTotalWidth = ( - y_max_grid + 1 - y_min_grid - ) * obj.yGridSize - obj.Clearance * 2 + obj.xTotalWidth = (x_max_grid + 1 - x_min_grid) * obj.xGridSize - obj.Clearance * 2 + obj.yTotalWidth = (y_max_grid + 1 - y_min_grid) * obj.yGridSize - obj.Clearance * 2 From 866346acc5d5b29445c568bc8e3170cb206ed78a Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:17:13 +0300 Subject: [PATCH 07/17] Format and linter --- .../feature_construction.py | 76 ++++++++---- freecad/gridfinity_workbench/features.py | 112 +++++++++++++----- .../grid_initial_layout.py | 20 +++- .../gridfinity_workbench/gridfinity_types.py | 12 +- freecad/gridfinity_workbench/utils.py | 1 - 5 files changed, 154 insertions(+), 67 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 4fcf69a2..c36e7202 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1,23 +1,17 @@ """Module containing gridfinity feature constructions.""" -# ruff: noqa: D101, D102, D107 import math -import typing from typing import Literal import FreeCAD as fc # noqa: N813 import Part -from . import const +from . import const, utils from . import gridfinity_types as gft from . import label_shelf as label_shelf_module -from . import utils -from .grid_initial_layout import GridfinityObject unitmm = fc.Units.Quantity("1 mm") zeromm = fc.Units.Quantity("0 mm") -# unitmm = 1 # mm -# zeromm = 0 # mm ECO_USABLE_HEIGHT = 14 SMALL_NUMBER = 0.01 @@ -88,7 +82,10 @@ def label_shelf_properties(obj: fc.DocumentObject, *, label_style_default: str) ).LabelShelfVerticalThickness = const.LABEL_SHELF_VERTICAL_THICKNESS -def make_label_shelf(obj: gft.CompartmentsMixin, bintype: Literal["eco", "standard"]) -> Part.Shape: +def make_label_shelf( + obj: gft.CompartmentsMixin, + bintype: Literal["eco", "standard"], +) -> Part.Shape: """Create label shelf.""" if ( bintype == "eco" @@ -96,7 +93,9 @@ def make_label_shelf(obj: gft.CompartmentsMixin, bintype: Literal["eco", "standa and obj.LabelShelfStyle != "Overhang" ): obj.LabelShelfStyle = "Overhang" - fc.Console.PrintWarning("Label shelf style set to Overhang due to low bin height\n") + fc.Console.PrintWarning( + "Label shelf style set to Overhang due to low bin height\n", + ) xdiv = obj.xDividers + 1 ydiv = obj.yDividers + 1 @@ -141,7 +140,12 @@ def make_label_shelf(obj: gft.CompartmentsMixin, bintype: Literal["eco", "standa ) if height > obj.UsableHeight: - boundingbox = Part.makeBox(width, length, height, fc.Vector(0, 0, -obj.UsableHeight)) + boundingbox = Part.makeBox( + width, + length, + height, + fc.Vector(0, 0, -obj.UsableHeight), + ) funcfuse = funcfuse.common(boundingbox) funcfuse = utils.copy_in_grid( @@ -468,7 +472,11 @@ def _make_compartments_with_deviders( return func_fuse -def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_default: int) -> None: +def compartments_properties( + obj: fc.DocumentObject, + x_div_default: int, + y_div_default: int, +) -> None: """Create bin compartments with the option for dividers. Args: @@ -538,7 +546,10 @@ def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_de ) -def make_compartments(obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape) -> Part.Shape: +def make_compartments( + obj: gft.CompartmentsMixin, + bin_inside_solid: Part.Shape, +) -> Part.Shape: """Create compartment cutout objects. Args: @@ -607,7 +618,9 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: p.recompute() p_wire: Part.Wire = p.Shape - magnet_hole_shape = Part.Face(p_wire).extrude(fc.Vector(0, 0, obj.MagnetHoleDepth)) + magnet_hole_shape = Part.Face(p_wire).extrude( + fc.Vector(0, 0, obj.MagnetHoleDepth), + ) fc.ActiveDocument.removeObject(p.Name) else: magnet_hole_shape = Part.makeCylinder( @@ -680,7 +693,11 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: return bottom_hole_shape -def _eco_bin_deviders(obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: float) -> Part.Shape: +def _eco_bin_deviders( + obj: gft.EcoCompartmentsMixin, + xcomp_w: float, + ycomp_w: float, +) -> Part.Shape: stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -1141,14 +1158,21 @@ def blank_bin_recessed_top_properties(obj: fc.DocumentObject) -> None: ).RecessedTopDepth = const.RECESSED_TOP_DEPTH -def make_blank_bin_recessed_top(obj: fc.DocumentObject, bin_inside_shape: Part.Wire) -> Part.Shape: +def make_blank_bin_recessed_top( + obj: fc.DocumentObject, + bin_inside_shape: Part.Wire, +) -> Part.Shape: """Generate Rectanble layout and calculate relevant parameters.""" face = Part.Face(bin_inside_shape) fuse_total = face.extrude(fc.Vector(0, 0, -obj.RecessedTopDepth)) return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def bin_bottom_holes_properties(obj: fc.DocumentObject, *, magnet_holes_default: bool) -> None: +def bin_bottom_holes_properties( + obj: fc.DocumentObject, + *, + magnet_holes_default: bool, +) -> None: """Create bin solid mid section. Args: @@ -1435,27 +1459,31 @@ def stacking_lip_properties( "App::PropertyLength", "StackingLipNotchesChamfer", "GridfinityNonStandard", - f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", + "Chamfer on the notches of the Stacking lip
" + f"
0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", ).StackingLipNotchesChamfer = const.STACKING_LIP_NOTCHES_CHAMFER obj.addProperty( "App::PropertyLength", "StackingLipNotchesRecess", "GridfinityNonStandard", - f"Recess of the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", + "Recess of the notches of the Stacking lip
" + f"
0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", ).StackingLipNotchesRecess = const.STACKING_LIP_NOTCHES_RECESS def make_stacking_lip( - obj: gft.StackingLipMixin, layout: GridfinityLayout, bin_outside_shape: Part.Wire + obj: gft.StackingLipMixin, + layout: GridfinityLayout, + bin_outside_shape: Part.Wire, ) -> Part.Shape: """Create stacking lip based on input bin shape. Args: obj (FreeCAD.DocumentObject): DocumentObject + layout (GridfinityLayout): layout of the bin bin_outside_shape (Part.Wire): exterior wall of the bin """ - wire = _stacking_lip_profile(obj) stacking_lip = Part.Wire(bin_outside_shape).makePipe(wire) stacking_lip = Part.makeSolid(stacking_lip) @@ -1475,7 +1503,7 @@ def make_stacking_lip( fc.Vector( obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance, - ) + ), ) base = _stacking_lip_plate(obj, layout) cover = cover.cut(base) @@ -1490,7 +1518,7 @@ def make_stacking_lip( fc.Vector( obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance, - ) + ), ) if obj.StackingLipNotchesRecess > 0: @@ -1505,7 +1533,7 @@ def make_stacking_lip( fc.Vector( obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance, - ) + ), ) cutout = cutout.fuse(cutout_recess) @@ -1526,7 +1554,7 @@ def make_stacking_lip( fc.Vector( obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance, - ) + ), ) cutout = cutout.fuse(cutout_chamfer) diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index e74572c4..cbdc8ad5 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -2,16 +2,14 @@ # ruff: noqa: D101, D102, D107 -import typing from abc import abstractmethod import FreeCAD as fc # noqa: N813 import Part from . import baseplate_feature_construction as baseplate_feat -from . import const +from . import const, grid_initial_layout, label_shelf, utils from . import feature_construction as feat -from . import grid_initial_layout, label_shelf, utils from .custom_shape_features import ( clean_up_layout, custom_shape_solid, @@ -74,33 +72,31 @@ def loads(self, state: dict) -> None: State argument required, otherwise expecting argument error message. """ - def add_property_if_missing( + def add_property_if_missing( # noqa: PLR0913 self, obj: fc.DocumentObject, default_value: object, - type: str, + prop_type: str, name: str, group: str = "", doc: str = "", attr: int = 0, - read_only: bool = False, - hidden: bool = False, - enum_vals=[], - ): + read_only: bool = False, # noqa: FBT001, FBT002 + hidden: bool = False, # noqa: FBT001, FBT002 + ) -> None: if name not in obj.PropertiesList: obj = obj.addProperty( - type=type, + type=prop_type, name=name, group=group, doc=doc, attr=attr, read_only=read_only, hidden=hidden, - enum_vals=enum_vals, ) setattr(obj, name, default_value) - def onDocumentRestored(self, obj: fc.DocumentObject) -> None: + def onDocumentRestored(self, obj: fc.DocumentObject) -> None: # noqa: N802 if hasattr(obj, "StackingLip"): self.add_property_if_missing( obj, @@ -116,7 +112,8 @@ def onDocumentRestored(self, obj: fc.DocumentObject) -> None: "App::PropertyLength", "StackingLipNotchesChamfer", "GridfinityNonStandard", - f"Chamfer on the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", + "Chamfer on the notches of the Stacking lip
" + f"
0 to disable

default = {const.STACKING_LIP_NOTCHES_CHAMFER} mm ", ) self.add_property_if_missing( obj, @@ -124,7 +121,8 @@ def onDocumentRestored(self, obj: fc.DocumentObject) -> None: "App::PropertyLength", "StackingLipNotchesRecess", "GridfinityNonStandard", - f"Recess of the notches of the Stacking lip

0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", + "Recess of the notches of the Stacking lip
" + f"
0 to disable

default = {const.STACKING_LIP_NOTCHES_RECESS} mm ", ) @@ -165,7 +163,10 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius, ) bin_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ), ) bin_inside_shape = utils.create_rounded_rectangle( @@ -175,17 +176,24 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius - obj.WallThickness, ) bin_inside_shape.translate( - fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), + fc.Vector( + obj.xTotalWidth / 2 + obj.Clearance, + obj.yTotalWidth / 2 + obj.Clearance, + ), ) fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) fuse_total = fuse_total.fuse(feat.make_complex_bin_base(obj, layout)) if obj.RecessedTopDepth > 0: - fuse_total = fuse_total.cut(feat.make_blank_bin_recessed_top(obj, bin_inside_shape)) + fuse_total = fuse_total.cut( + feat.make_blank_bin_recessed_top(obj, bin_inside_shape), + ) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) + fuse_total = fuse_total.fuse( + feat.make_stacking_lip(obj, layout, bin_outside_shape), + ) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -240,7 +248,11 @@ def __init__( feat.stacking_lip_properties(obj, stacking_lip_default=const.STACKING_LIP) feat.bin_bottom_holes_properties(obj, magnet_holes_default=const.MAGNET_HOLES) feat.bin_base_values_properties(obj) - feat.compartments_properties(obj, x_div_default=x_div_default, y_div_default=y_div_default) + feat.compartments_properties( + obj, + x_div_default=x_div_default, + y_div_default=y_div_default, + ) feat.label_shelf_properties(obj, label_style_default=label_style_default) feat.scoop_properties(obj, scoop_default=scoop_default) @@ -284,7 +296,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_compartments(obj, compartments)) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) + fuse_total = fuse_total.fuse( + feat.make_stacking_lip(obj, layout, bin_outside_shape), + ) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -393,7 +407,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) if obj.StackingLip: - fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) + fuse_total = fuse_total.fuse( + feat.make_stacking_lip(obj, layout, bin_outside_shape), + ) if obj.LabelShelfStyle != "Off": fuse_total = fuse_total.fuse(feat.make_label_shelf(obj, "eco")) @@ -419,7 +435,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: 0, obj.BinOuterRadius, ) - baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) + baseplate_outside_shape.translate( + fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0), + ) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -453,7 +471,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.MagnetHoleDepth - obj.MagnetBase, obj.BinOuterRadius, ) - baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) + baseplate_outside_shape.translate( + fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0), + ) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -491,7 +511,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.BaseThickness, obj.BinOuterRadius, ) - baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) + baseplate_outside_shape.translate( + fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0), + ) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -504,7 +526,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) + fuse_total = fuse_total.cut( + baseplate_feat.make_screw_bottom_chamfer(obj, layout), + ) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -590,7 +614,11 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, + layout, + obj.TotalHeight - obj.BaseProfileHeight, + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -666,7 +694,11 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, + layout, + obj.TotalHeight - obj.BaseProfileHeight, + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -744,7 +776,11 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, + layout, + obj.TotalHeight - obj.BaseProfileHeight, + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -774,7 +810,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: layout, obj.TotalHeight, ) - inside_wall_solid_full_height = inside_wall_solid_full_height.cut(compartment_trim) + inside_wall_solid_full_height = inside_wall_solid_full_height.cut( + compartment_trim, + ) inside_wall_solid_full_height = inside_wall_solid_full_height.removeSplitter() inside_wall_solid_full_height = vertical_edge_fillet( inside_wall_solid_full_height, @@ -854,7 +892,11 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) + solid_shape = custom_shape_solid( + obj, + layout, + obj.TotalHeight - obj.BaseProfileHeight, + ) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -1038,7 +1080,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) + fuse_total = fuse_total.cut( + baseplate_feat.make_screw_bottom_chamfer(obj, layout), + ) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -1131,7 +1175,11 @@ def execute(self, obj: Part.Feature) -> None: check_point = obj.Placement.Base + obj.Placement.Rotation.multVec( fc.Vector(stacking_lip_offset / 2), ) - if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside(check_point, 1e-6, False): # noqa: FBT003 + if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside( + check_point, + 1e-6, + False, + ): width += stacking_lip_offset shape = label_shelf.from_angle( diff --git a/freecad/gridfinity_workbench/grid_initial_layout.py b/freecad/gridfinity_workbench/grid_initial_layout.py index 9d666a6f..c03a6de3 100644 --- a/freecad/gridfinity_workbench/grid_initial_layout.py +++ b/freecad/gridfinity_workbench/grid_initial_layout.py @@ -1,9 +1,9 @@ """Makes grid layouts, calculates total width properties.""" -import FreeCAD as fc +import FreeCAD as fc # noqa: N813 from . import const -from .gridfinity_types import * +from . import gridfinity_types as gft def _location_properties(obj: fc.DocumentObject) -> None: @@ -68,7 +68,11 @@ def _grid_size_properties(obj: fc.DocumentObject) -> None: ).yGridSize = const.Y_GRID_SIZE -def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: +def rectangle_layout_properties( + obj: fc.DocumentObject, + *, + baseplate_default: bool, +) -> None: """Create Rectangle Layout. Args: @@ -115,7 +119,7 @@ def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bo ) -def make_rectangle_layout(obj: RectangleLayoutMixin) -> list[list[bool]]: +def make_rectangle_layout(obj: gft.RectangleLayoutMixin) -> list[list[bool]]: """Generate Rectanble layout and calculate relevant parameters.""" if obj.GenerationLocation == "Centered at Origin": if obj.Baseplate: @@ -131,7 +135,11 @@ def make_rectangle_layout(obj: RectangleLayoutMixin) -> list[list[bool]]: return [[True] * obj.yGridUnits for x in range(obj.xGridUnits)] -def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: +def custom_shape_layout_properties( + obj: fc.DocumentObject, + *, + baseplate_default: bool, +) -> None: """Add relevant properties for a custom shape gridfinity object. Args: @@ -157,7 +165,7 @@ def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: ).Baseplate = baseplate_default -def make_custom_shape_layout(obj: LayoutMixin, layout: list[list[bool]]) -> None: +def make_custom_shape_layout(obj: gft.LayoutMixin, layout: list[list[bool]]) -> None: """Calculate values for custom shape. Args: diff --git a/freecad/gridfinity_workbench/gridfinity_types.py b/freecad/gridfinity_workbench/gridfinity_types.py index 170c68d5..8dbf9828 100644 --- a/freecad/gridfinity_workbench/gridfinity_types.py +++ b/freecad/gridfinity_workbench/gridfinity_types.py @@ -1,6 +1,6 @@ -"""Type declarations for casting""" +"""Type declarations for casting.""" -# ruff: noqa: D101, D102, D107, N815 +# ruff: noqa: D101, N815 import typing from typing import Final, TypeVar @@ -148,5 +148,9 @@ class EcoBinObject( GridfinityObject = TypeVar("GridfinityObject", bound=fc.DocumentObject) -def cast(type: type[GridfinityObject], obj: fc.DocumentObject) -> GridfinityObject: - return typing.cast(GridfinityObject, typing.cast(object, obj)) +def cast( + gftype: type[GridfinityObject], # noqa: ARG001 + obj: fc.DocumentObject, +) -> GridfinityObject: + """Casts DocumentObject to gridfinity specific object with additional properties.""" + return typing.cast("GridfinityObject", typing.cast("object", obj)) diff --git a/freecad/gridfinity_workbench/utils.py b/freecad/gridfinity_workbench/utils.py index 1723f285..7d83248d 100644 --- a/freecad/gridfinity_workbench/utils.py +++ b/freecad/gridfinity_workbench/utils.py @@ -13,7 +13,6 @@ import FreeCADGui as fcg # noqa: N813 import Part - if TYPE_CHECKING: from collections.abc import Sequence From 1ab83eb59e77c39189f596da10120e79e50c4017 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:23:01 +0300 Subject: [PATCH 08/17] Format --- freecad/gridfinity_workbench/features.py | 48 ++++++------------------ 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index cbdc8ad5..e8ddf46b 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -163,10 +163,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius, ) bin_outside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, - obj.yTotalWidth / 2 + obj.Clearance, - ), + fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), ) bin_inside_shape = utils.create_rounded_rectangle( @@ -176,24 +173,17 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius - obj.WallThickness, ) bin_inside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, - obj.yTotalWidth / 2 + obj.Clearance, - ), + fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), ) fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) fuse_total = fuse_total.fuse(feat.make_complex_bin_base(obj, layout)) if obj.RecessedTopDepth > 0: - fuse_total = fuse_total.cut( - feat.make_blank_bin_recessed_top(obj, bin_inside_shape), - ) + fuse_total = fuse_total.cut(feat.make_blank_bin_recessed_top(obj, bin_inside_shape)) if obj.StackingLip: - fuse_total = fuse_total.fuse( - feat.make_stacking_lip(obj, layout, bin_outside_shape), - ) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -268,10 +258,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius, ) bin_outside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, - obj.yTotalWidth / 2 + obj.Clearance, - ), + fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), ) bin_inside_shape = utils.create_rounded_rectangle( @@ -281,10 +268,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.BinOuterRadius - obj.WallThickness, ) bin_inside_shape.translate( - fc.Vector( - obj.xTotalWidth / 2 + obj.Clearance, - obj.yTotalWidth / 2 + obj.Clearance, - ), + fc.Vector(obj.xTotalWidth / 2 + obj.Clearance, obj.yTotalWidth / 2 + obj.Clearance), ) fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) @@ -296,9 +280,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_compartments(obj, compartments)) if obj.StackingLip: - fuse_total = fuse_total.fuse( - feat.make_stacking_lip(obj, layout, bin_outside_shape), - ) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -399,9 +381,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, ), ) - fuse_total = fuse_total.cut( - feat.make_eco_compartments(obj, layout, compartment_solid), - ) + fuse_total = fuse_total.cut(feat.make_eco_compartments(obj, layout, compartment_solid)) if obj.ScrewHoles or obj.MagnetHoles: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) @@ -435,9 +415,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: 0, obj.BinOuterRadius, ) - baseplate_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0), - ) + baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -471,9 +449,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.MagnetHoleDepth - obj.MagnetBase, obj.BinOuterRadius, ) - baseplate_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0), - ) + baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -526,9 +502,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut( - baseplate_feat.make_screw_bottom_chamfer(obj, layout), - ) + fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total From e68f1679deea138128cc95d8065343a7124f6569 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:28:31 +0300 Subject: [PATCH 09/17] Format --- freecad/gridfinity_workbench/features.py | 52 +++++------------------- 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index e8ddf46b..b661621b 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -238,11 +238,7 @@ def __init__( feat.stacking_lip_properties(obj, stacking_lip_default=const.STACKING_LIP) feat.bin_bottom_holes_properties(obj, magnet_holes_default=const.MAGNET_HOLES) feat.bin_base_values_properties(obj) - feat.compartments_properties( - obj, - x_div_default=x_div_default, - y_div_default=y_div_default, - ) + feat.compartments_properties(obj, x_div_default=x_div_default, y_div_default=y_div_default) feat.label_shelf_properties(obj, label_style_default=label_style_default) feat.scoop_properties(obj, scoop_default=scoop_default) @@ -367,19 +363,11 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) fuse_total = fuse_total.fuse(feat.make_complex_bin_base(obj, layout)) face = Part.Face(bin_inside_shape).translate( - fc.Vector( - 0, - 0, - -obj.TotalHeight + obj.BaseProfileHeight + obj.BaseWallThickness, - ), + fc.Vector(0, 0, -obj.TotalHeight + obj.BaseProfileHeight + obj.BaseWallThickness), ) compartment_solid = face.extrude( - fc.Vector( - 0, - 0, - obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, - ), + fc.Vector(0, 0, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness), ) fuse_total = fuse_total.cut(feat.make_eco_compartments(obj, layout, compartment_solid)) @@ -487,9 +475,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: -obj.BaseThickness, obj.BinOuterRadius, ) - baseplate_outside_shape.translate( - fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0), - ) + baseplate_outside_shape.translate(fc.Vector(obj.xTotalWidth / 2, obj.yTotalWidth / 2, 0)) solid_shape = baseplate_feat.make_solid_shape( obj, @@ -588,11 +574,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, - layout, - obj.TotalHeight - obj.BaseProfileHeight, - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -668,11 +650,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, - layout, - obj.TotalHeight - obj.BaseProfileHeight, - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -763,9 +741,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: feat.eco_error_check(obj) compartments_solid = custom_shape_solid( - obj, - layout, - obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, + obj, layout, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, ) compartment_trim = custom_shape_trim( obj, @@ -784,9 +760,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: layout, obj.TotalHeight, ) - inside_wall_solid_full_height = inside_wall_solid_full_height.cut( - compartment_trim, - ) + inside_wall_solid_full_height = inside_wall_solid_full_height.cut(compartment_trim) inside_wall_solid_full_height = inside_wall_solid_full_height.removeSplitter() inside_wall_solid_full_height = vertical_edge_fillet( inside_wall_solid_full_height, @@ -1054,9 +1028,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = solid_shape.cut(fuse_total) fuse_total = fuse_total.cut(baseplate_feat.make_magnet_holes(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_center_cut(obj, layout)) - fuse_total = fuse_total.cut( - baseplate_feat.make_screw_bottom_chamfer(obj, layout), - ) + fuse_total = fuse_total.cut(baseplate_feat.make_screw_bottom_chamfer(obj, layout)) fuse_total = fuse_total.cut(baseplate_feat.make_connection_holes(obj, layout)) return fuse_total @@ -1149,11 +1121,7 @@ def execute(self, obj: Part.Feature) -> None: check_point = obj.Placement.Base + obj.Placement.Rotation.multVec( fc.Vector(stacking_lip_offset / 2), ) - if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside( - check_point, - 1e-6, - False, - ): + if obj.Attachment.StackingLip and obj.Attachment.Shape.isInside(check_point, 1e-6, False): # noqa: FBT003 width += stacking_lip_offset shape = label_shelf.from_angle( From 597325d3158231a034f1d7d889fae10b18b6ade0 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:30:51 +0300 Subject: [PATCH 10/17] Shrink diff --- freecad/gridfinity_workbench/features.py | 2 +- freecad/gridfinity_workbench/grid_initial_layout.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index b661621b..ac1f85f8 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -741,7 +741,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: feat.eco_error_check(obj) compartments_solid = custom_shape_solid( - obj, layout, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, + obj, layout, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness ) compartment_trim = custom_shape_trim( obj, diff --git a/freecad/gridfinity_workbench/grid_initial_layout.py b/freecad/gridfinity_workbench/grid_initial_layout.py index c03a6de3..b7df1f37 100644 --- a/freecad/gridfinity_workbench/grid_initial_layout.py +++ b/freecad/gridfinity_workbench/grid_initial_layout.py @@ -135,11 +135,7 @@ def make_rectangle_layout(obj: gft.RectangleLayoutMixin) -> list[list[bool]]: return [[True] * obj.yGridUnits for x in range(obj.xGridUnits)] -def custom_shape_layout_properties( - obj: fc.DocumentObject, - *, - baseplate_default: bool, -) -> None: +def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: """Add relevant properties for a custom shape gridfinity object. Args: From bc5777498d5e7eda3f415d4a8b16e789a5a66e8b Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:32:06 +0300 Subject: [PATCH 11/17] Format --- freecad/gridfinity_workbench/features.py | 26 ++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index ac1f85f8..78e0ed34 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -363,7 +363,11 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = feat.make_bin_solid_mid_section(obj, bin_outside_shape) fuse_total = fuse_total.fuse(feat.make_complex_bin_base(obj, layout)) face = Part.Face(bin_inside_shape).translate( - fc.Vector(0, 0, -obj.TotalHeight + obj.BaseProfileHeight + obj.BaseWallThickness), + fc.Vector( + 0, + 0, + -obj.TotalHeight + obj.BaseProfileHeight + obj.BaseWallThickness, + ), ) compartment_solid = face.extrude( @@ -375,9 +379,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout)) if obj.StackingLip: - fuse_total = fuse_total.fuse( - feat.make_stacking_lip(obj, layout, bin_outside_shape), - ) + fuse_total = fuse_total.fuse(feat.make_stacking_lip(obj, layout, bin_outside_shape)) if obj.LabelShelfStyle != "Off": fuse_total = fuse_total.fuse(feat.make_label_shelf(obj, "eco")) @@ -728,11 +730,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, - layout, - obj.TotalHeight - obj.BaseProfileHeight, - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() @@ -741,7 +739,9 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: feat.eco_error_check(obj) compartments_solid = custom_shape_solid( - obj, layout, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness + obj, + layout, + obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness, ) compartment_trim = custom_shape_trim( obj, @@ -840,11 +840,7 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: ## calculated values over layout = clean_up_layout(self.layout) grid_initial_layout.make_custom_shape_layout(obj, layout) - solid_shape = custom_shape_solid( - obj, - layout, - obj.TotalHeight - obj.BaseProfileHeight, - ) + solid_shape = custom_shape_solid(obj, layout, obj.TotalHeight - obj.BaseProfileHeight) outside_trim = custom_shape_trim(obj, layout, obj.Clearance, obj.Clearance) fuse_total = solid_shape.cut(outside_trim) fuse_total = fuse_total.removeSplitter() From 4d32745a34f1c8edc44856b2b5a91a01b2b9c958 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:35:14 +0300 Subject: [PATCH 12/17] Fixes format --- freecad/gridfinity_workbench/grid_initial_layout.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/freecad/gridfinity_workbench/grid_initial_layout.py b/freecad/gridfinity_workbench/grid_initial_layout.py index b7df1f37..2566fc24 100644 --- a/freecad/gridfinity_workbench/grid_initial_layout.py +++ b/freecad/gridfinity_workbench/grid_initial_layout.py @@ -4,6 +4,7 @@ from . import const from . import gridfinity_types as gft +from .utils import GridfinityLayout def _location_properties(obj: fc.DocumentObject) -> None: @@ -68,11 +69,7 @@ def _grid_size_properties(obj: fc.DocumentObject) -> None: ).yGridSize = const.Y_GRID_SIZE -def rectangle_layout_properties( - obj: fc.DocumentObject, - *, - baseplate_default: bool, -) -> None: +def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bool) -> None: """Create Rectangle Layout. Args: @@ -119,7 +116,7 @@ def rectangle_layout_properties( ) -def make_rectangle_layout(obj: gft.RectangleLayoutMixin) -> list[list[bool]]: +def make_rectangle_layout(obj: gft.RectangleLayoutMixin) -> GridfinityLayout: """Generate Rectanble layout and calculate relevant parameters.""" if obj.GenerationLocation == "Centered at Origin": if obj.Baseplate: @@ -161,7 +158,7 @@ def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: ).Baseplate = baseplate_default -def make_custom_shape_layout(obj: gft.LayoutMixin, layout: list[list[bool]]) -> None: +def make_custom_shape_layout(obj: gft.LayoutMixin, layout: GridfinityLayout) -> None: """Calculate values for custom shape. Args: From a365c5b82f9cf497b437a3efe7e11c0776790a7d Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:39:36 +0300 Subject: [PATCH 13/17] Format --- .../feature_construction.py | 68 ++++--------------- 1 file changed, 13 insertions(+), 55 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index c36e7202..d022a24a 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -82,10 +82,7 @@ def label_shelf_properties(obj: fc.DocumentObject, *, label_style_default: str) ).LabelShelfVerticalThickness = const.LABEL_SHELF_VERTICAL_THICKNESS -def make_label_shelf( - obj: gft.CompartmentsMixin, - bintype: Literal["eco", "standard"], -) -> Part.Shape: +def make_label_shelf(obj: gft.CompartmentsMixin, bintype: Literal["eco", "standard"]) -> Part.Shape: """Create label shelf.""" if ( bintype == "eco" @@ -93,9 +90,7 @@ def make_label_shelf( and obj.LabelShelfStyle != "Overhang" ): obj.LabelShelfStyle = "Overhang" - fc.Console.PrintWarning( - "Label shelf style set to Overhang due to low bin height\n", - ) + fc.Console.PrintWarning("Label shelf style set to Overhang due to low bin height\n") xdiv = obj.xDividers + 1 ydiv = obj.yDividers + 1 @@ -140,12 +135,7 @@ def make_label_shelf( ) if height > obj.UsableHeight: - boundingbox = Part.makeBox( - width, - length, - height, - fc.Vector(0, 0, -obj.UsableHeight), - ) + boundingbox = Part.makeBox(width, length, height, fc.Vector(0, 0, -obj.UsableHeight)) funcfuse = funcfuse.common(boundingbox) funcfuse = utils.copy_in_grid( @@ -472,11 +462,7 @@ def _make_compartments_with_deviders( return func_fuse -def compartments_properties( - obj: fc.DocumentObject, - x_div_default: int, - y_div_default: int, -) -> None: +def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_default: int) -> None: """Create bin compartments with the option for dividers. Args: @@ -546,10 +532,7 @@ def compartments_properties( ) -def make_compartments( - obj: gft.CompartmentsMixin, - bin_inside_solid: Part.Shape, -) -> Part.Shape: +def make_compartments(obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape) -> Part.Shape: """Create compartment cutout objects. Args: @@ -618,9 +601,7 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: p.recompute() p_wire: Part.Wire = p.Shape - magnet_hole_shape = Part.Face(p_wire).extrude( - fc.Vector(0, 0, obj.MagnetHoleDepth), - ) + magnet_hole_shape = Part.Face(p_wire).extrude(fc.Vector(0, 0, obj.MagnetHoleDepth)) fc.ActiveDocument.removeObject(p.Name) else: magnet_hole_shape = Part.makeCylinder( @@ -693,11 +674,7 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: return bottom_hole_shape -def _eco_bin_deviders( - obj: gft.EcoCompartmentsMixin, - xcomp_w: float, - ycomp_w: float, -) -> Part.Shape: +def _eco_bin_deviders(obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: float) -> Part.Shape: stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -1073,14 +1050,13 @@ def bin_base_values_properties(obj: fc.DocumentObject) -> None: def make_complex_bin_base( obj: gft.BaseMixin, layout: GridfinityLayout, - adjust: fc.Units.Quantity | None = None, ) -> Part.Shape: """Creaet complex shaped bin base.""" if obj.Baseplate: obj = gft.cast(gft.BaseplateMixin, obj) baseplate_size_adjustment = obj.BaseplateTopLedgeWidth - obj.Clearance else: - baseplate_size_adjustment = adjust if adjust is not None else zeromm # mm + baseplate_size_adjustment = zeromm x_bt_cmf_width = ( (obj.xGridSize - obj.Clearance * 2) @@ -1158,21 +1134,14 @@ def blank_bin_recessed_top_properties(obj: fc.DocumentObject) -> None: ).RecessedTopDepth = const.RECESSED_TOP_DEPTH -def make_blank_bin_recessed_top( - obj: fc.DocumentObject, - bin_inside_shape: Part.Wire, -) -> Part.Shape: +def make_blank_bin_recessed_top(obj: fc.DocumentObject, bin_inside_shape: Part.Wire) -> Part.Shape: """Generate Rectanble layout and calculate relevant parameters.""" face = Part.Face(bin_inside_shape) fuse_total = face.extrude(fc.Vector(0, 0, -obj.RecessedTopDepth)) return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def bin_bottom_holes_properties( - obj: fc.DocumentObject, - *, - magnet_holes_default: bool, -) -> None: +def bin_bottom_holes_properties(obj: fc.DocumentObject, *, magnet_holes_default: bool) -> None: """Create bin solid mid section. Args: @@ -1276,12 +1245,7 @@ def make_bin_bottom_holes( utils.corners(x_hole_pos, y_hole_pos, -obj.TotalHeight), ) - fuse_total = utils.copy_in_layout( - hole_shape_sub_array, - layout, - obj.xGridSize, - obj.yGridSize, - ) + fuse_total = utils.copy_in_layout(hole_shape_sub_array, layout, obj.xGridSize, obj.yGridSize) fuse_total.translate(fc.Vector(obj.xGridSize / 2, obj.yGridSize / 2)) return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) @@ -1636,8 +1600,7 @@ def bin_solid_mid_section_properties( def make_bin_solid_mid_section( - obj: gft.SolidMidSectionMixin, - bin_outside_shape: Part.Wire, + obj: gft.SolidMidSectionMixin, bin_outside_shape: Part.Wire, ) -> Part.Shape: """Generate bin solid mid section. @@ -1649,11 +1612,6 @@ def make_bin_solid_mid_section( face = Part.Face(bin_outside_shape) fuse_total = face.extrude(fc.Vector(0, 0, -obj.TotalHeight + obj.BaseProfileHeight)) - fuse_total = fuse_total.translate( - fc.Vector( - -obj.xLocationOffset, - -obj.yLocationOffset, - ), - ) + fuse_total = fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) return fuse_total From bad91c009b65ee1b518e45e664ae60e630b1c0e6 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 10 Jan 2026 15:41:43 +0300 Subject: [PATCH 14/17] Format --- freecad/gridfinity_workbench/feature_construction.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index d022a24a..2e4690fd 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1111,10 +1111,7 @@ def make_complex_bin_base( fuse_total = utils.copy_in_layout(assembly, layout, obj.xGridSize, obj.yGridSize) return fuse_total.translate( - fc.Vector( - obj.xGridSize / 2 - obj.xLocationOffset, - obj.yGridSize / 2 - obj.yLocationOffset, - ), + fc.Vector(obj.xGridSize / 2 - obj.xLocationOffset, obj.yGridSize / 2 - obj.yLocationOffset), ) From e7e8f44d3d44cf2cef8be536d2db3d1428910030 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 14 Feb 2026 13:53:35 +0300 Subject: [PATCH 15/17] Remove custom types --- .../feature_construction.py | 35 ++-- freecad/gridfinity_workbench/features.py | 7 - .../grid_initial_layout.py | 5 +- .../gridfinity_workbench/gridfinity_types.py | 156 ------------------ 4 files changed, 19 insertions(+), 184 deletions(-) delete mode 100644 freecad/gridfinity_workbench/gridfinity_types.py diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 2e4690fd..7bf28c20 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -7,7 +7,6 @@ import Part from . import const, utils -from . import gridfinity_types as gft from . import label_shelf as label_shelf_module unitmm = fc.Units.Quantity("1 mm") @@ -82,7 +81,7 @@ def label_shelf_properties(obj: fc.DocumentObject, *, label_style_default: str) ).LabelShelfVerticalThickness = const.LABEL_SHELF_VERTICAL_THICKNESS -def make_label_shelf(obj: gft.CompartmentsMixin, bintype: Literal["eco", "standard"]) -> Part.Shape: +def make_label_shelf(obj: fc.DocumentObject, bintype: Literal["eco", "standard"]) -> Part.Shape: """Create label shelf.""" if ( bintype == "eco" @@ -193,7 +192,7 @@ def scoop_properties(obj: fc.DocumentObject, *, scoop_default: bool) -> None: ).Scoop = scoop_default -def make_scoop(obj: gft.ScoopMixin) -> Part.Shape: +def make_scoop(obj: fc.DocumentObject) -> Part.Shape: """Create scoop.""" scooprad1 = obj.ScoopRadius + unitmm scooprad2 = obj.ScoopRadius + unitmm @@ -311,7 +310,7 @@ def make_scoop(obj: gft.ScoopMixin) -> Part.Shape: def _corner_fillets( - obj: gft.CompartmentsMixin, + obj: fc.DocumentObject, xcomp_width: float, ycomp_width: float, ) -> Part.Shape: @@ -385,7 +384,7 @@ def make_fillet(rotation: float, translation: fc.Vector) -> Part.Shape: def _make_compartments_no_deviders( - obj: gft.CompartmentsMixin, + obj: fc.DocumentObject, func_fuse: Part.Shape, ) -> Part.Shape: # Fillet Bottom edges @@ -401,7 +400,7 @@ def _make_compartments_no_deviders( def _make_compartments_with_deviders( - obj: gft.CompartmentsMixin, + obj: fc.DocumentObject, func_fuse: Part.Shape, ) -> Part.Shape: xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -532,7 +531,7 @@ def compartments_properties(obj: fc.DocumentObject, x_div_default: int, y_div_de ) -def make_compartments(obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape) -> Part.Shape: +def make_compartments(obj: fc.DocumentObject, bin_inside_solid: Part.Shape) -> Part.Shape: """Create compartment cutout objects. Args: @@ -580,7 +579,7 @@ def make_compartments(obj: gft.CompartmentsMixin, bin_inside_solid: Part.Shape) return func_fuse.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: +def make_bottom_hole_shape(obj: fc.DocumentObject) -> Part.Shape: """Create bottom hole shape. Returns one combined shape containing of the different hole types. @@ -674,7 +673,7 @@ def make_bottom_hole_shape(obj: gft.HoleMixin) -> Part.Shape: return bottom_hole_shape -def _eco_bin_deviders(obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: float) -> Part.Shape: +def _eco_bin_deviders(obj: fc.DocumentObject, xcomp_w: float, ycomp_w: float) -> Part.Shape: stackingoffset = -obj.LabelShelfStackingOffset if obj.StackingLip else zeromm xdivheight = obj.xDividerHeight if obj.xDividerHeight != 0 else obj.TotalHeight @@ -723,7 +722,7 @@ def _eco_bin_deviders(obj: gft.EcoCompartmentsMixin, xcomp_w: float, ycomp_w: fl return assembly.translate(fc.Vector(obj.xGridSize / 2, obj.yGridSize / 2)) -def eco_error_check(obj: gft.EcoCompartmentsMixin) -> None: +def eco_error_check(obj: fc.DocumentObject) -> None: """Check if eco dividers are possible with current parameters.""" # Divider Minimum Height @@ -820,7 +819,7 @@ def eco_compartments_properties(obj: fc.DocumentObject) -> None: def make_eco_compartments( - obj: gft.EcoCompartmentsMixin, + obj: fc.DocumentObject, layout: GridfinityLayout, bin_inside_solid: Part.Shape, ) -> Part.Shape: @@ -1048,12 +1047,12 @@ def bin_base_values_properties(obj: fc.DocumentObject) -> None: def make_complex_bin_base( - obj: gft.BaseMixin, + obj: fc.DocumentObject, layout: GridfinityLayout, ) -> Part.Shape: """Creaet complex shaped bin base.""" if obj.Baseplate: - obj = gft.cast(gft.BaseplateMixin, obj) + obj = fc.DocumentObject, obj) baseplate_size_adjustment = obj.BaseplateTopLedgeWidth - obj.Clearance else: baseplate_size_adjustment = zeromm @@ -1228,7 +1227,7 @@ def bin_bottom_holes_properties(obj: fc.DocumentObject, *, magnet_holes_default: def make_bin_bottom_holes( - obj: gft.HoleMixin, + obj: fc.DocumentObject, layout: GridfinityLayout, ) -> Part.Shape: """Make bin bottom holes.""" @@ -1247,7 +1246,7 @@ def make_bin_bottom_holes( return fuse_total.translate(fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset)) -def _stacking_lip_profile(obj: gft.StackingLipMixin) -> Part.Wire: +def _stacking_lip_profile(obj: fc.DocumentObject) -> Part.Wire: """Create stacking lip profile wire.""" ## Calculated Values obj.StackingLipTopChamfer = obj.BaseProfileTopChamfer - obj.Clearance - obj.StackingLipTopLedge @@ -1289,7 +1288,7 @@ def _stacking_lip_profile(obj: gft.StackingLipMixin) -> Part.Wire: def _stacking_lip_plate( - obj: gft.StackingLipMixin, + obj: fc.DocumentObject, layout: GridfinityLayout, ) -> Part.Shape: """Creaet complex shaped bin base.""" @@ -1433,7 +1432,7 @@ def stacking_lip_properties( def make_stacking_lip( - obj: gft.StackingLipMixin, + obj: fc.DocumentObject, layout: GridfinityLayout, bin_outside_shape: Part.Wire, ) -> Part.Shape: @@ -1597,7 +1596,7 @@ def bin_solid_mid_section_properties( def make_bin_solid_mid_section( - obj: gft.SolidMidSectionMixin, bin_outside_shape: Part.Wire, + obj: fc.DocumentObject, bin_outside_shape: Part.Wire, ) -> Part.Shape: """Generate bin solid mid section. diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 78e0ed34..6e8ab472 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -18,13 +18,6 @@ cut_outside_shape, vertical_edge_fillet, ) -from .gridfinity_types import ( - BaseplateObject, - EcoBinObject, - FullBinObject, - StorageBinObject, - cast, -) from .version import __version__ diff --git a/freecad/gridfinity_workbench/grid_initial_layout.py b/freecad/gridfinity_workbench/grid_initial_layout.py index 2566fc24..428d10ff 100644 --- a/freecad/gridfinity_workbench/grid_initial_layout.py +++ b/freecad/gridfinity_workbench/grid_initial_layout.py @@ -3,7 +3,6 @@ import FreeCAD as fc # noqa: N813 from . import const -from . import gridfinity_types as gft from .utils import GridfinityLayout @@ -116,7 +115,7 @@ def rectangle_layout_properties(obj: fc.DocumentObject, *, baseplate_default: bo ) -def make_rectangle_layout(obj: gft.RectangleLayoutMixin) -> GridfinityLayout: +def make_rectangle_layout(obj: fc.DocumentObject) -> GridfinityLayout: """Generate Rectanble layout and calculate relevant parameters.""" if obj.GenerationLocation == "Centered at Origin": if obj.Baseplate: @@ -158,7 +157,7 @@ def custom_shape_layout_properties(obj: fc.DocumentObject, *, baseplate_default: ).Baseplate = baseplate_default -def make_custom_shape_layout(obj: gft.LayoutMixin, layout: GridfinityLayout) -> None: +def make_custom_shape_layout(obj: fc.DocumentObject, layout: GridfinityLayout) -> None: """Calculate values for custom shape. Args: diff --git a/freecad/gridfinity_workbench/gridfinity_types.py b/freecad/gridfinity_workbench/gridfinity_types.py deleted file mode 100644 index 8dbf9828..00000000 --- a/freecad/gridfinity_workbench/gridfinity_types.py +++ /dev/null @@ -1,156 +0,0 @@ -"""Type declarations for casting.""" - -# ruff: noqa: D101, N815 - -import typing -from typing import Final, TypeVar - -import FreeCAD as fc # noqa: N813 - -from . import const - -CALCULATED_VALUE = 0 - -Length = float -Angle = float - - -class LayoutMixin(fc.DocumentObject): - GenerationLocation: str = "" - xLocationOffset: Length = 0.0 - yLocationOffset: Length = 0.0 - xTotalWidth: Length = 0.0 - yTotalWidth: Length = 0.0 - xGridSize: Length = const.X_GRID_SIZE - yGridSize: Length = const.Y_GRID_SIZE - Baseplate: bool = False - - -class RectangleLayoutMixin(LayoutMixin): - xGridUnits: int = const.X_GRID_UNITS - yGridUnits: int = const.Y_GRID_UNITS - - -class SolidShapeMixin(LayoutMixin): - TotalHeight: Length = CALCULATED_VALUE - - -class BaseMixin(SolidShapeMixin): - BaseProfileBottomChamfer: Length = const.BIN_BASE_BOTTOM_CHAMFER - BaseProfileVerticalSection: Length = const.BIN_BASE_VERTICAL_SECTION - BaseProfileTopChamfer: Length = const.BIN_BASE_TOP_CHAMFER - BinOuterRadius: Length = const.BIN_OUTER_RADIUS - BinVerticalRadius: Length = const.BIN_BASE_VERTICAL_RADIUS - BinBottomRadius: Length = const.BIN_BASE_BOTTOM_RADIUS - Clearance: Length = const.CLEARANCE - BaseProfileHeight: Final[Length] = CALCULATED_VALUE - - -class BaseplateMixin(BaseMixin): - BaseplateTopLedgeWidth: Length = const.BASEPLATE_TOP_LEDGE_WIDTH - - -class HoleMixin(SolidShapeMixin): - MagnetHoles: bool = const.MAGNET_HOLES - ScrewHoles: bool = const.SCREW_HOLES - SequentialBridgingLayerHeight: Length = const.SEQUENTIAL_BRIDGING_LAYER_HEIGHT - MagnetHolesShape: str = const.HOLE_SHAPES[0] - MagnetHoleDiameter: Length = const.MAGNET_HOLE_DIAMETER - MagnetHoleDepth: Length = const.MAGNET_HOLE_DEPTH - ScrewHoleDiameter: Length = const.SCREW_HOLE_DIAMETER - ScrewHoleDepth: Length = const.SCREW_HOLE_DEPTH - MagnetHoleDistanceFromEdge: Length = const.MAGNET_HOLE_DISTANCE_FROM_EDGE - - -class SolidMidSectionMixin(BaseMixin): - HeightUnits: int = 1 - ## Gridfinity Non Standard Parameters - CustomHeight: Length = 42 - NonStandardHeight: bool = False - WallThickness: Length = 2 - HeightUnitValue: Length = const.HEIGHT_UNIT_VALUE - - -class StackingLipMixin(SolidMidSectionMixin): - StackingLip: bool = False - StackingLipNotches: bool = False - StackingLipTopLedge: Length = const.STACKING_LIP_TOP_LEDGE - StackingLipTopChamfer: Length = 0.0 - StackingLipBottomChamfer: Length = const.STACKING_LIP_BOTTOM_CHAMFER - StackingLipVerticalSection: Length = const.STACKING_LIP_VERTICAL_SECTION - StackingLipNotchesChamfer: Length = const.STACKING_LIP_NOTCHES_CHAMFER - StackingLipNotchesRecess: Length = const.STACKING_LIP_NOTCHES_RECESS - - -class SimpleBinMixin(StackingLipMixin, HoleMixin): - pass - - -class LabelShelfMixin: - LabelShelfStyle: str = "" - LabelShelfPlacement: str = "" - LabelShelfWidth: Length = const.LABEL_SHELF_WIDTH - LabelShelfLength: float = const.LABEL_SHELF_LENGTH - LabelShelfAngle: Angle = const.LABEL_SHELF_ANGLE - LabelShelfStackingOffset: Length = const.LABEL_SHELF_STACKING_OFFSET - LabelShelfVerticalThickness: Length = const.LABEL_SHELF_VERTICAL_THICKNESS - - -class CompartmentsMixin(SimpleBinMixin, LabelShelfMixin): - xDividers: int = const.X_DIVIDERS - yDividers: int = const.Y_DIVIDERS - InsideFilletRadius: Length = const.INSIDE_FILLET_RADIUS - DividerThickness: Length = const.DIVIDER_THICKNESS - xDividerHeight: Length = const.CUSTOM_X_DIVIDER_HEIGHT - yDividerHeight: Length = const.CUSTOM_Y_DIVIDER_HEIGHT - UsableHeight: Length = CALCULATED_VALUE - - -class EcoCompartmentsMixin(CompartmentsMixin): - BaseWallThickness: Length = const.BASE_WALL_THICKNESS - - -class ScoopMixin(CompartmentsMixin): - ScoopRadius: Length = const.SCOOP_RADIUS - Scoop: bool = False - - -class BaseplateObject( - RectangleLayoutMixin, - BaseplateMixin, -): - pass - - -class FullBinObject( - RectangleLayoutMixin, - StackingLipMixin, - HoleMixin, -): - ## Gridfinity Non Standard Parameters - RecessedTopDepth: Length = const.RECESSED_TOP_DEPTH - - -class StorageBinObject( - RectangleLayoutMixin, - ScoopMixin, -): - pass - - -class EcoBinObject( - RectangleLayoutMixin, - EcoCompartmentsMixin, -): - pass - - -GridfinityObject = TypeVar("GridfinityObject", bound=fc.DocumentObject) - - -def cast( - gftype: type[GridfinityObject], # noqa: ARG001 - obj: fc.DocumentObject, -) -> GridfinityObject: - """Casts DocumentObject to gridfinity specific object with additional properties.""" - return typing.cast("GridfinityObject", typing.cast("object", obj)) From 1ec3990d7dcf7b1af86e89a4862297341c303f94 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 14 Feb 2026 13:58:42 +0300 Subject: [PATCH 16/17] Fix --- freecad/gridfinity_workbench/feature_construction.py | 1 - freecad/gridfinity_workbench/features.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 7bf28c20..1a144825 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1052,7 +1052,6 @@ def make_complex_bin_base( ) -> Part.Shape: """Creaet complex shaped bin base.""" if obj.Baseplate: - obj = fc.DocumentObject, obj) baseplate_size_adjustment = obj.BaseplateTopLedgeWidth - obj.Clearance else: baseplate_size_adjustment = zeromm diff --git a/freecad/gridfinity_workbench/features.py b/freecad/gridfinity_workbench/features.py index 6e8ab472..fa121958 100644 --- a/freecad/gridfinity_workbench/features.py +++ b/freecad/gridfinity_workbench/features.py @@ -146,7 +146,6 @@ def __init__( feat.bin_base_values_properties(obj) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: - obj = cast(FullBinObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) bin_outside_shape = utils.create_rounded_rectangle( @@ -236,7 +235,6 @@ def __init__( feat.scoop_properties(obj, scoop_default=scoop_default) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: - obj = cast(StorageBinObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) @@ -322,7 +320,6 @@ def __init__(self, obj: fc.DocumentObject) -> None: feat.eco_compartments_properties(obj) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: - obj = cast(EcoBinObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) bin_outside_shape = utils.create_rounded_rectangle( @@ -389,7 +386,6 @@ def __init__(self, obj: fc.DocumentObject) -> None: baseplate_feat.base_values_properties(obj) def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape: - obj = cast(BaseplateObject, obj) layout = grid_initial_layout.make_rectangle_layout(obj) baseplate_outside_shape = utils.create_rounded_rectangle( From df23ff31e098bb63318d9c3c11069e7d989f8032 Mon Sep 17 00:00:00 2001 From: Timofey Dovgal Date: Sat, 14 Feb 2026 14:01:54 +0300 Subject: [PATCH 17/17] Shrink diff --- freecad/gridfinity_workbench/feature_construction.py | 5 ++--- freecad/gridfinity_workbench/utils.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/freecad/gridfinity_workbench/feature_construction.py b/freecad/gridfinity_workbench/feature_construction.py index 1a144825..065697ef 100644 --- a/freecad/gridfinity_workbench/feature_construction.py +++ b/freecad/gridfinity_workbench/feature_construction.py @@ -1524,6 +1524,7 @@ def make_stacking_lip( stacking_lip = stacking_lip.translate( fc.Vector(-obj.xLocationOffset, -obj.yLocationOffset), ) + return stacking_lip @@ -1594,9 +1595,7 @@ def bin_solid_mid_section_properties( ) -def make_bin_solid_mid_section( - obj: fc.DocumentObject, bin_outside_shape: Part.Wire, -) -> Part.Shape: +def make_bin_solid_mid_section(obj: fc.DocumentObject, bin_outside_shape: Part.Wire) -> Part.Shape: """Generate bin solid mid section. Args: diff --git a/freecad/gridfinity_workbench/utils.py b/freecad/gridfinity_workbench/utils.py index 7d83248d..0408be9e 100644 --- a/freecad/gridfinity_workbench/utils.py +++ b/freecad/gridfinity_workbench/utils.py @@ -73,8 +73,8 @@ def copy_and_translate(shape: Part.Shape, vec_list: list[fc.Vector]) -> Part.Sha def copy_in_layout( shape: Part.Shape, layout: GridfinityLayout, - x_gird_size: float, - y_grid_size: float, + x_gird_size: fc.Units.Quantity, + y_grid_size: fc.Units.Quantity, ) -> Part.Shape: """Copy a shape in a layout.