Skip to content

Commit a6f73e8

Browse files
authored
Merge pull request #133 from graelo/feat/ecobin-scoop
Add scoop functionality to EcoBin classes
2 parents f406830 + 7d4caff commit a6f73e8

6 files changed

Lines changed: 56 additions & 15 deletions

File tree

freecad/gridfinity_workbench/feature_construction.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Module containing gridfinity feature constructions."""
22

3+
from __future__ import annotations
4+
35
import math
46
from typing import Literal
57

@@ -187,8 +189,24 @@ def scoop_properties(obj: fc.DocumentObject, *, scoop_default: bool) -> None:
187189
).Scoop = scoop_default
188190

189191

190-
def make_scoop(obj: fc.DocumentObject) -> Part.Shape:
191-
"""Create scoop."""
192+
def make_scoop(
193+
obj: fc.DocumentObject,
194+
*,
195+
usable_height: None | fc.Units.Quantity = None,
196+
) -> Part.Shape:
197+
"""Create scoop.
198+
199+
Args:
200+
obj: The object onto which to add the scoop.
201+
usable_height: Override the obj's UsableHeight value (for EcoBins).
202+
203+
EcoBins are constructed in such a way that when the scoop is added, the
204+
proper usable height (for correct geometry) has to be provided separately.
205+
206+
"""
207+
if usable_height is None:
208+
usable_height = obj.UsableHeight
209+
192210
scooprad1 = obj.ScoopRadius + unitmm
193211
scooprad2 = obj.ScoopRadius + unitmm
194212
scooprad3 = obj.ScoopRadius + unitmm
@@ -203,8 +221,8 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape:
203221
scooprad1 = xdivscoop - unitmm
204222
if obj.ScoopRadius > xcomp_w and obj.xDividers > 0:
205223
scooprad2 = xcomp_w - 2 * unitmm
206-
if obj.ScoopRadius > obj.UsableHeight > 0:
207-
scooprad3 = obj.UsableHeight - obj.LabelShelfStackingOffset
224+
if obj.ScoopRadius > usable_height > 0:
225+
scooprad3 = usable_height - obj.LabelShelfStackingOffset
208226

209227
scooprad = min(obj.ScoopRadius, scooprad1, scooprad2, scooprad3)
210228

@@ -214,17 +232,17 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape:
214232
v1 = fc.Vector(
215233
obj.xTotalWidth + obj.Clearance - obj.WallThickness,
216234
0,
217-
-obj.UsableHeight + scooprad,
235+
-usable_height + scooprad,
218236
)
219237
v2 = fc.Vector(
220238
obj.xTotalWidth + obj.Clearance - obj.WallThickness,
221239
0,
222-
-obj.UsableHeight,
240+
-usable_height, # type: ignore[arg-type]
223241
)
224242
v3 = fc.Vector(
225243
obj.xTotalWidth + obj.Clearance - obj.WallThickness - scooprad,
226244
0,
227-
-obj.UsableHeight,
245+
-usable_height, # type: ignore[arg-type]
228246
)
229247

230248
l1 = Part.LineSegment(v1, v2)
@@ -237,7 +255,7 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape:
237255
- scooprad
238256
+ scooprad * math.sin(math.pi / 4),
239257
0,
240-
-obj.UsableHeight + scooprad - scooprad * math.sin(math.pi / 4),
258+
-usable_height + scooprad - scooprad * math.sin(math.pi / 4),
241259
)
242260

243261
c1 = Part.Arc(v1, vc1, v3)
@@ -268,7 +286,7 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape:
268286
scoopbox = Part.makeBox(
269287
stacking_lip_offset.Value,
270288
obj.yTotalWidth - obj.WallThickness * 2,
271-
obj.UsableHeight,
289+
usable_height, # type: ignore[arg-type]
272290
fc.Vector(
273291
obj.xTotalWidth + obj.Clearance - obj.WallThickness,
274292
obj.Clearance + obj.WallThickness,
@@ -280,7 +298,7 @@ def make_scoop(obj: fc.DocumentObject) -> Part.Shape:
280298
edges = [
281299
edge
282300
for edge in funcfuse.Edges
283-
if abs(edge.Vertexes[0].Z - edge.Vertexes[1].Z) == obj.UsableHeight
301+
if abs(edge.Vertexes[0].Z - edge.Vertexes[1].Z) == usable_height
284302
and edge.Vertexes[0].X == edge.Vertexes[1].X
285303
]
286304

freecad/gridfinity_workbench/features.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def __init__(self, obj: fc.DocumentObject) -> None:
270270
feat.bin_base_values_properties(obj)
271271
feat.label_shelf_properties(obj, label_style_default="Standard")
272272
feat.eco_compartments_properties(obj)
273+
feat.scoop_properties(obj, scoop_default=False)
273274

274275
obj.setExpression("UsableHeight", "TotalHeight - HeightUnitValue")
275276

@@ -309,7 +310,17 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape:
309310
compartment_solid = face.extrude(
310311
fc.Vector(0, 0, obj.TotalHeight - obj.BaseProfileHeight - obj.BaseWallThickness),
311312
)
312-
fuse_total = fuse_total.cut(feat.make_eco_compartments(obj, layout, compartment_solid))
313+
314+
# First cut eco compartments to create the interior spaces
315+
eco_compartments = feat.make_eco_compartments(obj, layout, compartment_solid)
316+
fuse_total = fuse_total.cut(eco_compartments)
317+
318+
# Now add scoop, but only where eco compartments exist (reversed logic)
319+
if obj.Scoop:
320+
scoop = feat.make_scoop(obj, usable_height=obj.TotalHeight - obj.BaseWallThickness)
321+
# Only add scoop where compartments exist - use intersection to constrain
322+
scoop_constrained = scoop.common(eco_compartments)
323+
fuse_total = fuse_total.fuse(scoop_constrained)
313324

314325
if obj.ScrewHoles or obj.MagnetHoles:
315326
fuse_total = fuse_total.cut(feat.make_bin_bottom_holes(obj, layout))
@@ -602,6 +613,7 @@ def __init__(self, obj: fc.DocumentObject, layout: list[list[bool]]) -> None:
602613
feat.bin_base_values_properties(obj)
603614
feat.label_shelf_properties(obj, label_style_default="Off")
604615
feat.eco_compartments_properties(obj)
616+
feat.scoop_properties(obj, scoop_default=False)
605617

606618
obj.Proxy = self
607619

@@ -657,11 +669,19 @@ def generate_gridfinity_shape(self, obj: fc.DocumentObject) -> Part.Shape:
657669
inside_wall_solid_full_height,
658670
obj.BinOuterRadius - obj.WallThickness,
659671
)
672+
# First cut eco compartments to create the interior spaces
660673
compartments = feat.make_eco_compartments(obj, layout, compartments_solid)
661674
inside_wall_negative = cut_outside_shape(obj, inside_wall_solid_full_height)
662675
compartments = compartments.cut(inside_wall_negative)
663676
fuse_total = fuse_total.cut(compartments)
664677

678+
# Now add scoop, but only where eco compartments exist (reversed logic)
679+
if obj.Scoop:
680+
scoop = feat.make_scoop(obj, usable_height=obj.TotalHeight - obj.BaseWallThickness)
681+
# Only add scoop where compartments exist - use intersection to constrain
682+
scoop_constrained = scoop.common(compartments)
683+
fuse_total = fuse_total.fuse(scoop_constrained)
684+
665685
if obj.LabelShelfStyle != "Off":
666686
label_shelf = feat.make_label_shelf(obj, "eco")
667687
label_shelf = label_shelf.cut(inside_wall_negative)

freecad/gridfinity_workbench/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import math
1010
from typing import TYPE_CHECKING
1111

12-
import FreeCAD as fc # noqa:N813
12+
import FreeCAD as fc # noqa: N813
1313
import FreeCADGui as fcg # noqa: N813
1414
import Part
1515

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Module containing version information."""
22

3-
__version__ = "0.12.2"
3+
__version__ = "0.12.3"

package.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
<description>This Workbench will generate several variations of parametric Gridfinity bins and baseplates that can be easily customized. </description>
77

8-
<version>0.12.2</version>
8+
<version>0.12.3</version>
99

10-
<date>2026-02-28</date>
10+
<date>2026-03-03</date>
1111

1212
<license file="LICENSE">lgpl-2.1-or-later</license>
1313

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,6 @@ ignore = ["ANN003", "EM101", "EM102", "RET504", "TRY003", "S101", "SIM300"]
3333
# PT009 - pytest-unittest-assertion: we use unittest framework, not pytest
3434
"**/tests/*" = ["INP001", "D100", "D101", "D102", "PT027", "PT009"]
3535
"freecad/gridfinity_workbench/test_gridfinity.py" = ["D100", "D101", "D102", "PT027", "PT009"]
36+
37+
[tool.ruff.lint.isort]
38+
known-third-party = ["FreeCAD", "FreeCADGui", "Part"]

0 commit comments

Comments
 (0)