Skip to content

SAT plate writer cannot author holes (Loop.next_loop never set), while the reader handles multi-loop faces #410

Description

@oleandor

A plate with a hole written to Genie XML comes back without it, and nothing says so.

The SAT reader handles multi-loop faces, because GeniE-authored files have them: faces.py classifies each loop as periphery or hole via the next_loop chain, and PlateFactory._get_primary_loop (faces.py) picks the periphery over a hole. The writer cannot produce one for an adapy plate: plate_to_sat_entities (src/ada/cadit/sat/write/write_plate.py, the unfused path) authors a single loop per face and never sets Loop.next_loop, and the default imprint path is not given the plate's booleans either -- from_imprint.py does chain hole loops off next_loop, but only for regions the imprint itself encloses. from_brep.py (the path for a Part read from a Genie XML with its topology store) also chains loops. So the asymmetry is specifically adapy-authored plates: the reader understands holes the writer can never emit.

Noted in #406 (the plate-outline fix), where holes were deliberately left uncovered because this writer path cannot express one.

Reproduction

Measured on the #406 branch:

import re
import ada
from ada.cadit.sat.write.writer import part_to_sat_writer

pl = ada.Plate("pl", [(0, 0), (4, 0), (4, 3), (0, 3)], 0.01)
pl.add_boolean(ada.PrimBox("hole", (1.5, 1.0, -0.5), (2.5, 2.0, 0.5)))
a = ada.Assembly("A") / (ada.Part("P") / pl)

for imprint in (True, False):
    txt = part_to_sat_writer(a, imprint=imprint).to_str()
    print(imprint, len([ln for ln in txt.splitlines() if re.search(r"\bloop\b", ln)]))
# True 1
# False 1      -- one loop, the outline only; no hole loop on either path

a.to_genie_xml("hole.xml")
(rp,) = ada.from_genie_xml("hole.xml").get_all_physical_objects(by_type=ada.Plate)
print(len(pl.booleans), len(rp.booleans))
# 1 0          -- the hole is gone, with no warning or error logged

Expected

Either author the hole as a second loop (Loop.next_loop, marked hole) so it survives the SAT, or refuse by name at export time: a plate whose booleans the writer cannot represent is reported with the reason, not written as a solid plate without comment.


🤖 Generated with Claude Code

https://claude.ai/code/session_01Nej3bWxJevs9GkH6tg1WKv

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions