Reconstruction#

Recover the build sequence that produced a part, and the code that rebuilds it.

Note

Prerequisites: Feature-Driven Pipeline.

Reconstruction runs that pipeline first — the part is defeatured down to its stock, and what the pipeline recognised on the way is what becomes the build sequence. Reading it first explains where the operations come from.

Reconstruction#

Reconstruction answers a question defeaturing raises but does not settle: what sequence of operations produced this part? Given a solid read from a CAD file, it recognises the features that made the part, orders them into a sequence that builds, and emits the code for that sequence.

You need to know nothing about how shapes are represented to use it. What you hand in is a file or a solid; what you get back is Python you can read, run and edit.

Getting a reconstructor#

from volmdlr_tools.reverse_engineering.brep.reconstructor import CADReconstructor

reconstructor = CADReconstructor.from_step("part.step")

A file holding no solid, or more than one, is refused rather than narrowed: a build sequence reconstructs a single body, and choosing one of several here would report nothing about the rest. Read the file yourself and pass the solid you mean to CADReconstructor(solid).

The script#

print(reconstructor.script())

The script builds the part from the stock outwards: the body first, then material added, then material removed, then edges worked on what remains. Running it in a namespace binds result to the rebuilt solid.

By default every recognised dimension is declared as a named variable above the build sequence and the calls reference those names, so the script can be re-run at other sizes. script(parametric=False) writes the numbers inline instead.

The report#

The script alone says nothing about how much of the part it explains. The report does:

report = reconstructor.report()

report.mode          # "parametric" or "draft"
report.coverage      # fraction of the part's faces the emitted operations claim
report.failed_operations
report.manifest
report.executes, report.volume_ratio, report.execution_error

Obtaining a report runs the emitted script once, in your process, to answer executes, volume_ratio and execution_error. That costs a full rebuild and the script’s own side effects happen there, so report(execute=False) returns everything else without doing it.

Mode#

"parametric" means every operation in the script describes a recognised feature. "draft" means the script starts from a frozen body — see below.

Mode is not the same question as the parametric= argument to script(), which only decides whether dimensions are written as named variables. A script with named dimensions can still be a draft.

Coverage#

Coverage is the fraction of the part’s faces that some emitted operation accounts for. 1.0 means every face is explained by an operation that builds it.

Coverage counts explanation, not resemblance. A draft’s frozen body carries geometry without explaining it, so it claims no faces at all — which is why a draft can rebuild a part almost exactly while reporting coverage well under 1.0. If you want to know how close the rebuilt shape is, read volume_ratio.

A volume_ratio above 1.0 means the reconstruction left material behind: a cut that could not be described was never applied, so the metal it should have removed is still there.

What was left out#

A recognised feature that cannot be described or built is set aside rather than sinking the whole reconstruction. Each one is reported:

for failure in report.failed_operations:
    print(failure.source, failure.face_ids, failure.reason)

source says which build stage the feature came from — one of "stock", "extrusion", "cut", "hole", "blend", "seed", "ordering" — so you can group by stage without knowing which recogniser produced what. The recogniser’s own name travels in reason.

"ordering" is the one source that is not a feature. It records that the faces recognition offered as the ones features stand on contradicted each other, so no exact sequence existed and the operations were ordered by build phase alone.

The manifest#

One entry per face that no emitted operation accounts for:

for face in report.manifest:
    print(face.face_id, face.surface_type, face.area, face.region, face.journal)

region groups neighbouring unexplained faces, so one region reads as one unexplained feature-shaped patch of the part — usually more useful than the face count, because it says how many things are missing rather than how many faces they span.

journal is a one-line narrative of what happened to that face while the part was defeatured, which is often enough to say why nothing claimed it.

area and centroid are in whatever unit the input file used. That varies between files and is not recorded here, so normalise against the part’s own extent if you need to compare across files.

As a sidecar file#

open("part.py", "w").write(report.script)
open("part.json", "w").write(report.to_json())

to_json() serialises everything but the script — mode, coverage, failures, manifest — as plain JSON. Every field is a number, a string, or a tuple of numbers, so a consumer holding only those two files needs nothing installed to read what the script does and does not explain.

Drafts#

When the part cannot be reduced to a body the recognised operations rebuild, the reconstruction does not fail. It emits a draft: the shape the part was reduced to ships frozen inside the script, the recognised operations replay on top of it, and the script opens with a comment saying so.

A draft is a starting point, not an answer. What it gives you is the shape of the build sequence and the parameters recognition did measure — the frozen body is there so the script builds at all, and each manifest entry carries that face’s index inside it (frozen_body_face_index) so you can find the geometry the script is carrying rather than explaining.

report().frozen_body_face_count is how many faces that body has, and 0 for a parametric reconstruction.

Rebuilding the solid#

solid = reconstructor.solid()

solid() runs the script and hands back the rebuilt solid as a plain domain type. It is for a parametric reconstruction only: on a draft it raises, because a draft’s script starts from a copy of the input and the solid it builds therefore resembles the input however little of the part was recognised. Measuring one against the other would report a faithful rebuild for a part barely recognised at all. On a draft, read the report instead.