Skip to content

Commit 8e67848

Browse files
Fix Calculation of Compound Center of Masses (#1822)
* Added a new method to help inspect compound shapes * Extended Center of mass. It now supports an optional argument that helps correctly analyze the shape. * Refactored Center method. Reimplemented Center of a Compound class * Moved the compound-specific method implementation inside the child class * Added new test-case. The test case compares the center of mass of a compound consisting of faces with the expected value * Add docstring --------- Co-authored-by: AU <[email protected]>
1 parent 9bd1cb6 commit 8e67848

File tree

2 files changed

+50
-12
lines changed

2 files changed

+50
-12
lines changed

cadquery/occ_impl/shapes.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,35 @@ def CombinedCenter(objects: Iterable["Shape"]) -> Vector:
756756

757757
return Vector(sum_wc.multiply(1.0 / total_mass))
758758

759+
@staticmethod
760+
def _mass_calc_function(obj: "Shape") -> Any:
761+
"""
762+
Helper to find the correct mass calculation function with special compound handling.
763+
"""
764+
765+
type_ = shapetype(obj.wrapped)
766+
767+
# special handling of compounds - first non-compound child is assumed to define the type of the operation
768+
if type_ == ta.TopAbs_COMPOUND:
769+
770+
# if the compound is not empty check its children
771+
if obj:
772+
# first child
773+
child = next(iter(obj))
774+
775+
# if compound, go deeper
776+
while child.ShapeType() == "Compound":
777+
child = next(iter(child))
778+
779+
type_ = shapetype(child.wrapped)
780+
781+
# if the compound is empty assume it was meant to be a solid
782+
else:
783+
type_ = ta.TopAbs_SOLID
784+
785+
# get the function based on dimensionality of the object
786+
return shape_properties_LUT[type_]
787+
759788
@staticmethod
760789
def computeMass(obj: "Shape") -> float:
761790
"""
@@ -764,13 +793,11 @@ def computeMass(obj: "Shape") -> float:
764793
:param obj: Compute the mass of this object
765794
"""
766795
Properties = GProp_GProps()
767-
calc_function = shape_properties_LUT[shapetype(obj.wrapped)]
796+
calc_function = Shape._mass_calc_function(obj)
768797

769-
if calc_function:
770-
calc_function(obj.wrapped, Properties)
771-
return Properties.Mass()
772-
else:
773-
raise NotImplementedError
798+
calc_function(obj.wrapped, Properties)
799+
800+
return Properties.Mass()
774801

775802
@staticmethod
776803
def centerOfMass(obj: "Shape") -> Vector:
@@ -780,13 +807,11 @@ def centerOfMass(obj: "Shape") -> Vector:
780807
:param obj: Compute the center of mass of this object
781808
"""
782809
Properties = GProp_GProps()
783-
calc_function = shape_properties_LUT[shapetype(obj.wrapped)]
810+
calc_function = Shape._mass_calc_function(obj)
784811

785-
if calc_function:
786-
calc_function(obj.wrapped, Properties)
787-
return Vector(Properties.CentreOfMass())
788-
else:
789-
raise NotImplementedError
812+
calc_function(obj.wrapped, Properties)
813+
814+
return Vector(Properties.CentreOfMass())
790815

791816
@staticmethod
792817
def CombinedCenterOfBoundBox(objects: List["Shape"]) -> Vector:

tests/test_cadquery.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5904,3 +5904,16 @@ def test_loft_to_vertex(self):
59045904
# in both cases we get a solid
59055905
assert w1.solids().size() == 1
59065906
assert w2.solids().size() == 1
5907+
5908+
def test_compound_faces_center(self):
5909+
sk = Sketch().rect(50, 50).faces()
5910+
face1 = sk.val()
5911+
face2 = face1.copy().translate(Vector(100, 0, 0))
5912+
compound = Compound.makeCompound([face1, face2])
5913+
expected_center = Shape.CombinedCenter([face1, face2])
5914+
5915+
assert (
5916+
compound.Center() == expected_center
5917+
), "Incorrect center of mass of the compound, expected {}, got {}".format(
5918+
expected_center, compound.Center()
5919+
)

0 commit comments

Comments
 (0)