|
66 | 66 | ) |
67 | 67 | from OCC.Core.TopExp import TopExp_Explorer, topexp |
68 | 68 | from OCC.Core.TopLoc import TopLoc_Location |
69 | | -from OCC.Core.TopAbs import TopAbs_REVERSED, TopAbs_WIRE, TopAbs_EDGE, TopAbs_FACE, TopAbs_SOLID |
| 69 | +from OCC.Core.TopAbs import TopAbs_REVERSED, TopAbs_WIRE, TopAbs_EDGE, TopAbs_FACE, TopAbs_SHELL, TopAbs_SOLID |
70 | 70 | from OCC.Core.TopTools import TopTools_IndexedMapOfShape |
71 | 71 | from OCC.Core.TopoDS import topods |
72 | 72 | from OCC.Extend.TopologyUtils import TopologyExplorer |
@@ -2241,6 +2241,76 @@ def compound_of(*shapes): |
2241 | 2241 | "an empty leaf label is dropped with a warning and its siblings still convert", |
2242 | 2242 | detail or f"{len(logical_volumes)} volume(s), {len(occ)} placed") |
2243 | 2243 |
|
| 2244 | + # --- 4. faces that enclose no volume, as in the ALICE3 OTR stave ------------------------- |
| 2245 | + from OCC.Core.BRepGProp import brepgprop |
| 2246 | + from OCC.Core.GProp import GProp_GProps |
| 2247 | + from OCC.Core.TopoDS import TopoDS_Shell |
| 2248 | + |
| 2249 | + def volume_of(shape): |
| 2250 | + props = GProp_GProps() |
| 2251 | + brepgprop.VolumeProperties(shape, props) |
| 2252 | + return props.Mass() |
| 2253 | + |
| 2254 | + def n_faces(shape): |
| 2255 | + exp = TopExp_Explorer(shape, TopAbs_FACE) |
| 2256 | + n = 0 |
| 2257 | + while exp.More(): |
| 2258 | + n += 1 |
| 2259 | + exp.Next() |
| 2260 | + return n |
| 2261 | + |
| 2262 | + def faces_of(shape): |
| 2263 | + exp = TopExp_Explorer(shape, TopAbs_FACE) |
| 2264 | + out = [] |
| 2265 | + while exp.More(): |
| 2266 | + out.append(exp.Current()) |
| 2267 | + exp.Next() |
| 2268 | + return out |
| 2269 | + |
| 2270 | + def one_face_shells(faces): |
| 2271 | + shells = [] |
| 2272 | + for face in faces: |
| 2273 | + shell = TopoDS_Shell() |
| 2274 | + BRep_Builder().MakeShell(shell) |
| 2275 | + BRep_Builder().Add(shell, face) |
| 2276 | + shells.append(shell) |
| 2277 | + return compound_of(*shells) |
| 2278 | + |
| 2279 | + box = BRepPrimAPI_MakeBox(2., 3., 4.) |
| 2280 | + _doc, st = _self_test_shape_tool() |
| 2281 | + sheets = st.AddShape(one_face_shells(faces_of(box.Shape())[:3]), False) |
| 2282 | + good = st.AddShape(BRepPrimAPI_MakeBox(3., 3., 3.).Shape(), False) |
| 2283 | + _self_test_assembly(st, [(sheets, gp_Trsf()), (good, _self_test_shift(dx=10.0))]) |
| 2284 | + st.UpdateAssemblies() |
| 2285 | + _rep, occ = _self_test_convert(st) |
| 2286 | + report(len(logical_volumes) == 1 and len(occ) == 1, |
| 2287 | + "a leaf of open faces that enclose no volume is dropped, its sibling still converts", |
| 2288 | + f"{len(logical_volumes)} volume(s), {len(occ)} placed") |
| 2289 | + |
| 2290 | + # --- 5. a closed shell with no solid around it still encloses a volume and is kept -------- |
| 2291 | + _doc, st = _self_test_shape_tool() |
| 2292 | + shell_only = st.AddShape(box.Shell(), False) |
| 2293 | + _self_test_assembly(st, [(shell_only, gp_Trsf())]) |
| 2294 | + st.UpdateAssemblies() |
| 2295 | + _rep, occ = _self_test_convert(st) |
| 2296 | + vol = volume_of(next(iter(def_shapes.values()))) if len(def_shapes) == 1 else 0.0 |
| 2297 | + report(len(logical_volumes) == 1 and len(occ) == 1 and abs(vol - 24.0) < 1e-9, |
| 2298 | + "a closed shell without a solid is kept as one volume enclosing the right volume", |
| 2299 | + f"{len(logical_volumes)} volume(s), volume {vol:.6g} (expected 24)") |
| 2300 | + |
| 2301 | + # --- 6. a solid with a stray face beside it keeps only the solid -------------------------- |
| 2302 | + _doc, st = _self_test_shape_tool() |
| 2303 | + stray = compound_of(box.Shape(), faces_of(BRepPrimAPI_MakeBox(gp_Pnt(9., 0., 0.), 1., 1., 1.) |
| 2304 | + .Shape())[0]) |
| 2305 | + part = st.AddShape(stray, False) |
| 2306 | + _self_test_assembly(st, [(part, gp_Trsf())]) |
| 2307 | + st.UpdateAssemblies() |
| 2308 | + _rep, occ = _self_test_convert(st) |
| 2309 | + shape = next(iter(def_shapes.values())) if len(def_shapes) == 1 else None |
| 2310 | + report(shape is not None and len(occ) == 1 and n_faces(shape) == 6, |
| 2311 | + "a solid with a stray face keeps its 6 faces and drops the stray one", |
| 2312 | + f"{len(def_shapes)} volume(s), {n_faces(shape) if shape is not None else '-'} face(s)") |
| 2313 | + |
2244 | 2314 | print(f"\n{tally.checks} checks, {tally.failures} failure(s)") |
2245 | 2315 | return tally.failures |
2246 | 2316 |
|
@@ -3611,6 +3681,24 @@ def solid_bodies_of(shape) -> list: |
3611 | 3681 | return out |
3612 | 3682 |
|
3613 | 3683 |
|
| 3684 | +def shells_outside_solids(shape) -> Tuple[list, int]: |
| 3685 | + """The closed shells a shape carries outside any solid, and the count of its open faces.""" |
| 3686 | + closed, n_open = [], 0 |
| 3687 | + exp = TopExp_Explorer(shape, TopAbs_SHELL, TopAbs_SOLID) |
| 3688 | + while exp.More(): |
| 3689 | + shell = exp.Current() |
| 3690 | + if BRep_Tool.IsClosed(shell): |
| 3691 | + closed.append(shell) |
| 3692 | + else: |
| 3693 | + n_open += sum(1 for _ in TopologyExplorer(shell).faces()) |
| 3694 | + exp.Next() |
| 3695 | + exp = TopExp_Explorer(shape, TopAbs_FACE, TopAbs_SHELL) |
| 3696 | + while exp.More(): |
| 3697 | + n_open += 1 |
| 3698 | + exp.Next() |
| 3699 | + return closed, n_open |
| 3700 | + |
| 3701 | + |
3614 | 3702 | def _register_leaf_shape(def_key: str, shape, meshparam, scale_to_cm: float, |
3615 | 3703 | clip_enabled: bool, clip_box, clip_box_shape, |
3616 | 3704 | world_trsf, def_lid: str) -> bool: |
@@ -3793,7 +3881,18 @@ def expand_definition( |
3793 | 3881 | return def_key |
3794 | 3882 |
|
3795 | 3883 | shape = shape_tool.GetShape(def_label) |
3796 | | - bodies = solid_bodies_of(shape) |
| 3884 | + closed_shells, n_open = shells_outside_solids(shape) |
| 3885 | + bodies = solid_bodies_of(shape) + closed_shells |
| 3886 | + if n_open: |
| 3887 | + # Open faces enclose no volume: a sheet body, not something to fill with material. |
| 3888 | + if not bodies: |
| 3889 | + print(f"WARNING: CAD leaf {def_lid} ('{nm}') carries only {n_open} open face(s) " |
| 3890 | + f"that enclose no volume; skipping it.") |
| 3891 | + return None |
| 3892 | + print(f"WARNING: CAD leaf {def_lid} ('{nm}') carries {n_open} open face(s) beside " |
| 3893 | + f"its {len(bodies)} closed bod(y/ies); dropping the faces.") |
| 3894 | + if len(bodies) == 1: |
| 3895 | + shape = bodies[0] |
3797 | 3896 |
|
3798 | 3897 | # A leaf label may hold several bodies; each becomes a volume the label places once. |
3799 | 3898 | if len(bodies) > 1: |
|
0 commit comments