Compare commits

..
326 Commits
Author SHA1 Message Date
Clifford Yapp 43ad5d4b97 Fix MSVC edit matrix warnings
Compare BOT face modes as booleans and use offsetof directly so the tests do not redefine the Windows FIELD_OFFSET macro.
2026-09-30 18:36:01 -04:00
Clifford Yapp 95fd44bbad Harden the Windows usage regression
Limit the command scan to executable files on Windows so DLL and debug artifacts are not invoked. Allow additional timeout headroom for the substantially larger and slower Windows executable set.
2026-09-30 18:36:00 -04:00
Clifford Yapp 86b93459a6 Preserve DB5 object sizes above 4 GB
Use size_t for encoded name and record byte counts so LLP64 Windows builds do not truncate large serialized objects through 32-bit long values.
2026-09-30 18:36:00 -04:00
Clifford Yapp 1bfaeba67b Validate BRep closest-point results
Evaluate the Newton gradient in release builds, reject invalid or non-finite projection results, and preserve trim-boundary points within tolerance. Add direct regression coverage for refined and midpoint projections.
2026-09-30 18:36:00 -04:00
Clifford Yapp 185559e373 Fix MSVC BRep build and linkage
Export topology healing entry points from libbrep and link the healing test to the library implementation instead of compiling it twice. Use the platform header wrapper and normalize the boolean XOR operand for MSVC.
2026-09-30 18:36:00 -04:00
Clifford Yapp a5659b38e8 check fixes 2026-09-30 18:36:00 -04:00
Clifford Yapp 4dcca4f4cf Bound pathological BRep repair fallbacks 2026-09-30 18:36:00 -04:00
Clifford Yapp d9005aaf8c Preserve source identities in full sphere repairs 2026-09-30 18:36:00 -04:00
Clifford Yapp bdc06aa8f9 Use valid storage for mesh point arrays 2026-09-30 18:36:00 -04:00
Clifford Yapp 285824cf58 Retry resource-limited BRep corpus tasks in isolation 2026-09-30 18:36:00 -04:00
Clifford Yapp 2918c80198 Preserve certified curved BRep repair patches 2026-09-30 18:36:00 -04:00
Clifford Yapp 8d21869426 Preserve shared boundaries in periodic BRep bands 2026-09-30 18:36:00 -04:00
Clifford Yapp a387959c93 Preserve BRep mesh vertices during export compaction 2026-09-30 18:36:00 -04:00
Clifford Yapp a9ff01b36a Apply shared geometry eligibility to BRep audit comparisons
Exclude no-face inputs from shaded and quality comparisons and documented
open surfaces from quality comparisons in both revisions. Retain raw totals
and evidence while applying the same eligibility to outcomes, failures, and
timings. Accept valid empty shading and wireframe-only quality records.

Validation: policy checks cover symmetric exclusions, retained repair
candidates, empty results, and invalid manifests. Matched trial comparisons
against main and the previous branch pass final preflight.
2026-09-30 18:36:00 -04:00
Clifford Yapp 813151775b Retain resource-limit history across BRep repair attempts
Accumulate time, memory, point, and refinement limits across nested repair
attempts, including failures preceding a successful retry. Distinguish mesh
repair allocation failures from geometric rejection and expose the retained
limit mask in audit output. Repair report layouts gain new fields.

Validation: tiny point and memory budgets retain their limit flags, and an
allocation-fault probe checks cleared outputs and reset on a later call.
The Steam Controller completes within 4 GiB while retaining prior limit
history. All 67 final preflight tests pass with matching rebuilt headers.
2026-09-30 18:36:00 -04:00
Clifford Yapp 3b448247d0 Report all solid mesh edge defects consistently
Collect unmatched, misoriented, and excess edges independently when a full
report is requested, retaining short-circuit checks for boolean callers.
Classify complete edge-use groups and fix the legacy wrapper's acceptance
of orientation-only or excess-only defects when bad edges are requested.

Validation: solid-diagnostics fixtures cover reversed tetrahedra and mixed
edge defects with and without reports. Existing mesh repair tests and all
67 final preflight tests pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp e20de53e11 Reduce exact nearest-triangle query work during BRep repair
Expand the search cube progressively and prune strictly farther triangle
bounds with a roundoff guard. Avoid candidate-vector allocations while
preserving exact distances, acceptance limits, and traversal-order ties.

Validation: 2,400 queries match exhaustive distances and source identities
across scales, translations, duplicate triangles, and degenerate triangles.
An isolated benchmark confirms fewer distance evaluations. All 67 final
preflight tests pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp 5aaf063f87 Correct interpolation of sampled surface lengths
Interpolate the lower, middle, and upper length measurements with their
proper adjacent weights. This removes a discontinuity in sampling estimates
on tapered surfaces while retaining independent measurements on each half.

Validation: rising and falling lengths, shifted domains, endpoints, quarter
points, and an interior maximum pass. The final combined tree passes all
67 preflight tests.
2026-09-30 18:36:00 -04:00
Clifford Yapp 49d2ff7fac Retain native chart samples for wide periodic mesh triangles
Preserve chart-derived centroid and edge-midpoint samples when seam welding
would lose a triangle's periodic image. Carry the sparse samples through
triangulation, atlas installation, edge splits, and flips so orientation
and surface-deviation checks evaluate the intended source region.

Validation: chart tests cover both windings, reversed faces, and cache
reset. All eight targeted solid regressions now pass native meshing and
solid validation without fallback repair. All 67 final preflight tests pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp da30bd04cc Recover drawable BRep components with bounded topology healing
Retry failed trim boundaries on an owned, validated topology copy while
preserving face identities and unsupported empty faces. Use component-local
closure evidence, discard only empty inner loops on supported faces, and
honor drawing budgets and authoritative source-sample contracts.

Validation: healing fixtures preserve holes, reject unsafe references and
unsupported open components, and leave source CRCs unchanged. Targeted
checks recover three additional Garrett faces with no newly failed faces;
all 40 drawing controls and all 67 final preflight tests pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp 624a18574a Select periodic drawing bands from directed boundary winding
Lift boundary walks continuously on doubly closed surfaces and select the
filled band from the outer boundary direction. Verify flattening against
model tolerance and existing edge agreement before accepting the chart.

Validation: eight band fixtures cover reversed winding, transposed charts,
and alternating periodic copies, checking both region and surface area.
The final combined tree passes all 67 preflight tests.
2026-09-30 18:36:00 -04:00
Clifford Yapp 6f0fc0e6e7 Normalize periodic surface evaluation and bound seam bridge sampling
Share periodic parameter normalization across drawing, edge normals, and
surface sampling. Sample seam bridges using model-space tolerances and
quarter-turn seeds so equivalent parameter copies produce the same curve.
Fail bounded sampling explicitly and keep bridge samples in owned storage.

Validation: periodic UV sampling and edge-normal regressions cover shifted
domains, equivalent copies, both parameter directions, and excessive
winding. The final combined tree passes all 67 preflight tests.
2026-09-30 18:36:00 -04:00
Clifford Yapp 4a17e43560 Preserve nested cutouts when decomposing touching polygon contours
Classify decomposed cycles by signed area and assign holes to their unique
smallest enclosing filled cycle. Preserve repeated boundary vertices so
reinserting authoritative samples cannot fill a cutout or lose an island.

Validation: contour-cleaning regressions cover both windings, every start
vertex, a touching hole, and a nested island with its own hole. The final
combined tree passes all 67 preflight tests.
2026-09-30 18:36:00 -04:00
Clifford Yapp 8fdba02189 Add missing header 2026-09-30 18:36:00 -04:00
Clifford Yapp 2fe4b2f08f Align shaded BRep triangle winding with its corner normals
Order chart triangles according to the source face orientation. After
resolving shared boundary vertices, replace opposing or invalid corner
normals with the realized facet normal while retaining compatible smooth
normals. Document the per-corner normal contract.

Validation: normal and reversed-face drawing tests pass. The Dreadnought
hull has no opposing normals on nondegenerate triangles, with unchanged
triangle geometry and face completion. Steam Controller and Product
watertight repair reports remain unchanged.
2026-09-30 18:36:00 -04:00
Clifford Yapp 3b53ca0093 Avoid folded fans when reconstructing concave BRep patches
Use a simple projected triangulation when a repair fan lies outside its
boundary kernel. Preserve exact boundary samples and valid existing fans;
split only new diagonals that conflict with retained mesh edges. Keep the
existing fidelity and thin-sheet intersection policies.

Validation: concave, convex, crossed, reversed, and conflicting-diagonal
fixtures pass. On Steam Controller, the reference/output area discrepancy
falls from 22.35% to 1.155% while retaining all rigorous triangles.
2026-09-30 18:36:00 -04:00
Clifford Yapp 324986134e Reduce mesh repair search costs and temporary storage
Index candidate hanging-boundary vertices spatially and score cap defects
over their complete affected neighborhoods. Avoid discarded Manifold
normalization copies, duplicate rigorous-triangle keys, and incidence
storage outside the requested boundary; release assembly scratch earlier.
Report allocation failures as memory limits in the audit tool.

Validation: mesh repair regressions and differential cap/boundary checks
preserve results across coordinate scales. The Steam Controller quality
case completes under a 4 GiB limit with the existing acceptance policy.
2026-09-30 18:36:00 -04:00
Clifford Yapp e4ee3b8362 Preserve periodic BRep charts and verify source coverage
Use topological seams to choose the closed direction of doubly periodic
faces and retain the required periodic support during conditioning.
Validate unwrapped polyline segments against their 3-D edges, sample full
periodic boundaries, and tolerate seam translation roundoff. Guard a
missing refinement neighbor after boundary collapse.

Reject face meshes outside their original support bounds. Independently
check repaired meshes against referenced source edges and proven full-face
interiors so an omitted surface cannot certify itself through an incomplete
reference mesh. Ignore unused construction edges.

Validation: transposed torus bands, wrapped strips, independent source
coverage, and local-defect regressions pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp 7cfb2b58e1 Retain validated pullback samples for closed-source repair retries
Retry a small unmatched boundary residue from whole-display repair of a
valid closed BRep with pullback simplification disabled. Preserve the
existing solid, resource, and fidelity limits and report the retry path.

Stop rejected pullbacks at the first source mismatch instead of spending
the remaining budget on projections that cannot yield a usable candidate.

Validation: dense and simplified collapsed-pcurve fixtures both pass and
leave the source unchanged.
2026-09-30 18:36:00 -04:00
Clifford Yapp 5aa60a2c29 Heal bounded BRep topology defects on an owned copy
After ordinary repair fails, interpret supported planar boundaries,
loop roles, unused edges, and face orientations without changing the
source surfaces or curves. Bound work, storage, and time; retain original
topology identities and publish only a certified candidate mesh.

Add opt-in planar caps with disjoint conservative bounds and an aggregate
area ceiling. Require Manifold acceptance and preserve the requested
intersection and fidelity policies. Expose healing results and the cap
setting through the audit tools.

Validation: healing regressions cover source preservation, provenance,
resource limits, ambiguous boundaries, cap bounds, and rejected candidates.
2026-09-30 18:36:00 -04:00
Clifford Yapp c9ac27102d Treat no-face BReps as empty display geometry
Allow all-faces drawing requests to return empty success for BReps with
no faces, while retaining errors for nonexistent individual faces.
Exclude these wireframe-only objects from watertight quality checks and
report the exclusion explicitly in audit output.

Validation: empty-object drawing contracts, fast-drawing regressions,
and the audit classification checks pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp 20206f696f Recover orphaned holes after polygon contour cleaning
Reattach a conditioned hole only to its unique smallest enclosing filled
contour. Require complete containment and reject ambiguous ownership so
overlapping input holes cannot silently become filled triangles.

Add a regression for the orphan-hole case with its expected filled area.
2026-09-30 18:36:00 -04:00
Clifford Yapp 0671152b75 Avoid shadowing lint options in the BRep shading check
Name the local fast-drawing settings explicitly so the shading check
builds with the repository's shadow warnings enabled.

Validation: the lint target builds and regress-ged_lint_brep passes.
2026-09-30 18:36:00 -04:00
Clifford Yapp 2509048c45 Report fast BRep shading failures from lint
Run the bounded display tessellator only when brep:fast_shading is
explicitly selected.  Record each failed face with its result, stage,
and diagnostic, while classifying resource-limited work separately as
inconclusive.

Exercise valid and invalid BReps in the lint regression and document
the command and its 30-second per-object budget.
2026-09-30 18:36:00 -04:00
Clifford Yapp 02f8a98ac9 Conservatively infer missing BRep boundaries
Recover all-inner open-chart faces only when winding and
containment define a complete alternating hierarchy.  Support disjoint
bounded roots through detria contour-role detection, but reject orphan
holes, conflicting nesting, and ambiguous closed-surface strips.

Treat faces whose every loop is provably zero-area as successful empty
display results, and ignore those loops when constructing an otherwise
well-defined periodic domain.  This recovers four corpus faces without
inventing support-surface geometry.
2026-09-30 18:36:00 -04:00
Clifford Yapp 7e6ebba263 Recover degenerate BRep loop assemblies
Treat an untrimmed periodic face domain as authoritative and seed it
from coordinate extents, not a particular corner ordering.  Ignore only
secondary outer loops proven to have no physical or parametric area.

Report surface evaluation and 3D degeneracy counts when all realized
chart triangles fail.  This recovers two MAZ faces and the untrimmed
Boat revolution, eliminating the focused boundary assembly failures.
2026-09-30 18:36:00 -04:00
Clifford Yapp 96a1a972c6 Decompose touching triangulation contours
Let Clipper first produce strictly simple boundaries, then restore
authoritative collinear samples.  Remove exact retrace spikes and split
any resulting nonadjacent vertex touches into independent closed walks.

Certify each triangulation and their combined chart area.  This recovers
eight residual display failures without perturbing UVs.
2026-09-30 18:36:00 -04:00
Clifford Yapp e43d741882 Recover collapsed periodic BRep caps
Interpret a one-turn trim as a band against the natural surface
boundary selected by its winding, even when that boundary is not
analytically singular.  Also collapse repeated seam and pole circuits
only when every closed-edge copy identifies the same boundary.

This recovers 19 previously failed corpus faces, including the repeated
Cutting-machine caps and single-edge Garrett periodic patches, while
retaining the cone, polar, cylinder, and NIST 6 regressions.
2026-09-30 18:36:00 -04:00
Clifford Yapp 36bdef78c5 Report display triangulation failure stages
Return structured diagnostics from cleaned polygon triangulation and
propagate per-face failure reasons through the fast B-Rep API.  Record
them in brep-audit so corpus failures identify the constraint, detria,
or realization stage without debugger-only logging.

Exercise both successful and failed report population in the libbg
clean triangulation test.
2026-09-30 18:36:00 -04:00
Clifford Yapp b6b2075c06 Canonicalize invalid BRep trim constraints
Keep sanitized polygons in Clipper's conditioned integer coordinates
through detria predicates so conversion back to ill-conditioned UV space
cannot reintroduce duplicate points or crossing constraints. Reinsert
only exactly collinear boundary vertices and treat rejected zero-area
hole paths as empty constraints.

Cover degenerate holes and edge-touching T-junctions. This recovers 38
additional unique archived face failures, including FarmBot face 987,
with no losses among the 21 earlier recoveries.
2026-09-30 18:36:00 -04:00
Clifford Yapp 78a24378dd Recover implicit periodic BRep domains
Interpret an all-inner face on a closed surface as the complete periodic
domain minus its loops only when every loop is contractible.  Select seam
locations outside the hole intervals so seam-straddling cutouts remain
intact in the universal cover.

Preserve all original loops as constrained holes.  This recovers eight
archived shared failures without accepting the non-contractible Garrett
cases or regressing existing periodic-strip handling.
2026-09-30 18:36:00 -04:00
Clifford Yapp 4e4b09fd2f Classify sub-tolerance BRep faces as complete
Distinguish exact zero-area faces from boundaries that collapse at the
requested display tolerance.  Retry coarse adaptive collapses at requested
quality before accepting an empty result.

Expose the outcome through the fast CDT report and brep-audit JSON.  This
turns ten archived shared failures, including HINO face 4, into successful
tolerance-tagged empty results.
2026-09-30 18:36:00 -04:00
Clifford Yapp 6ef47093b6 Recover failed coarse BRep faces directly
Display tessellation success is not monotonic with tolerance.  Coarse
sampling can collapse a narrow face even when the requested tolerance
resolves it, so retry missing faces at the requested setting instead of
walking through several known-insufficient intermediate passes.

Stop rescheduling faces once they have completed at requested quality.
This recovers HINO face 30 within the five-second object budget.
2026-09-30 18:36:00 -04:00
Clifford Yapp da39ca8ef4 Simplify repaired BRep trim boundaries
Pullback repair can expose every knot of an already approximated edge,
forcing the display CDT to process redundant boundary vertices.  Reduce
those polylines with model-space and surface-scaled UV chord bounds at
the declared repair tolerance.

Split closed boundaries into two arcs so coincident endpoints cannot
collapse a real loop.  This reduces OpenRC Rim face 94 from 196738 to
16218 vertices and from 24.84 to 1.30 seconds while preserving its
boundary envelope within tolerance.
2026-09-30 18:36:00 -04:00
Clifford Yapp fdf70af435 Confirm degenerate BRep loops in model space
Trim tolerances are not UV distances on arbitrarily scaled NURBS
surfaces.  Treat UV collinearity only as a candidate and require the
mapped deviation to remain within modeling tolerance before discarding
a loop.

This preserves tolerant zero-area slit handling while retaining narrow
drawable strips such as hAVYARD wheelhouse roof face 0.
2026-09-30 18:36:00 -04:00
Clifford Yapp ca0389d276 Add strict BRep audit comparison reporting
Validate the common main and branch result schema before reporting.
Match task coverage and run configuration.  Summarize outcomes and
timing, and identify failure categories so incomplete or incompatible
corpus runs cannot be mistaken for comparable data.
2026-09-30 18:36:00 -04:00
Clifford Yapp 1b750729e8 Harden the fast heap for arbitrary threads
The prior cache indexed storage with bu_parallel_id(), which is not a
unique identity for arbitrary C++ threads. Concurrent external workers
could race a page or free list, and retained pages grew without bound.

Use reusable thread-affine cache slots. Small exact-size operations are
lock-free after slot acquisition. Excess threads and full caches fall
back to the system allocator.

Bound the process cache charge at 64 MiB, account for allocator
metadata, and compact returned blocks without touching live storage.
Document the cross-thread, initialization, and size contracts.

Add cross-thread release, 80-thread oversubscription, live allocation,
and concurrent compaction coverage. ASan, LeakSanitizer, TSan, all 598
libbu tests, and all 745 focused BRep wireframe cases pass.
2026-09-30 18:36:00 -04:00
Clifford Yapp 29761eaa9a run nmg kill on failed return 2026-09-30 18:36:00 -04:00
Clifford Yapp df8f2890f1 Reuse released blocks in the fast heap
The fast heap never reused allocations returned by bu_heap_put.
Repeated BRep surface-tree construction therefore retained every
transient node, growing T-34 wireframe memory by more than 3 GiB
until its 4 GiB audit limit failed.

Maintain a per-worker free list for each eligible allocation size and
make the out-of-range fallback unconditional. Add tests for LIFO reuse
and fallback allocations. T-34 now renders at 1.5 GiB peak RSS with
pixel-identical output.
2026-09-30 18:36:00 -04:00
Clifford Yapp 7afeb57d5d Ensure corpus batches account for every selected task
Fall back to isolated per-object audits when a database batch cannot start, violates the protocol, or repeatedly exits without progress. Preserve flattened task indices and record the fallback cause so resume can continue without silently dropping the database tail.
2026-09-30 18:36:00 -04:00
Clifford Yapp d10ad36e1f Add lighting to shaded audit images
Apply deterministic directional and ambient lighting during shaded\nimage rasterization so curved surfaces expose their reconstructed\nnormals and depth instead of appearing as a flat constant fill.
2026-09-30 18:36:00 -04:00
Clifford Yapp 750566e77b Add resumable BRep display image capture
Render successful wireframe and shaded audit geometry to deterministic\n1024-pixel PPM files. Include a database-path hash and object name in\neach filename so corpus images remain traceable after archival. Keep\nimage generation optional and outside normal timing runs.
2026-09-30 18:36:00 -04:00
Clifford Yapp a265a0e4d0 Harden BRep corpus batch completion
Require an explicit end-of-batch marker and contiguous task indices
before a database batch is accepted as complete.  Retry interrupted or
malformed records and report selection count mismatches instead of
silently skipping objects.

This preserves complete corpus coverage when a geometry routine exits
the process successfully or writes diagnostics that corrupt a result
record.
2026-09-30 18:36:00 -04:00
Clifford Yapp f20ba0af07 Isolate bounded BRep display audits
Visualization corpus comparisons should measure drawing generators,
without repeating source validity and trimmed bounding-box analysis.
Add a display-only mode that skips those reference passes and rejects
incompatible quality filters.

Convert generator allocation failures into explicit resource-limit
reports and preserve whether deserialization itself succeeded, allowing
large capped runs to recover without losing the failing object identity.
2026-09-30 18:36:00 -04:00
Clifford Yapp 26652fc080 Make parallel BRep wire output deterministic
Workers previously admitted sampled points to the shared result budget
in completion order.  Near a point or byte limit, scheduler timing chose
different edge subsets and changed bounded results between runs.

Keep sampling parallel but commit results in edge order with checked
budget arithmetic and stop-path wakeups.  Let the caller finish
self-dispatched work if no child launches.  Verify repeated output and
serial-versus-parallel reports are identical.
2026-09-30 18:36:00 -04:00
Clifford Yapp dc6625dc45 Bound BRep wireframe worker address space
Each bu_parallel child reserves a stack and, on glibc, may also reserve
a large sparse malloc arena.  Under RLIMIT_AS those mappings made thread
creation depend on allocation order and left too little space for curve
sampling.

Scale workers to measured process headroom, account for their stacks in
the working budget, and use fixed curve probes for the display tolerance
instead of an allocation-heavy temporary NURBS bounding-box conversion.
2026-09-30 18:36:00 -04:00
Clifford Yapp 1fe49f5fff Reject incomplete OpenNURBS archive imports
Allocation pressure can make an archive read fail with an empty or
partial component manifest.  The importer previously ignored that result
and dereferenced the missing geometry item, turning a bounded resource
failure into SIGSEGV.

Validate every component lookup and catch conversion exceptions.  Leave
the partially initialized internal safe for normal cleanup, and add a
malformed-archive regression for the null-manifest case.
2026-09-30 18:36:00 -04:00
Clifford Yapp dbf732914a Document BRep resource and source identity APIs
Add Doxygen contracts for bounded CDT, curve-tree, surface-tree, and BRNode resource APIs. Clarify that BrepTrimPoint source identities are non-owning and part of the public C++ layout.
2026-09-30 18:36:00 -04:00
Clifford Yapp 99783e89b6 Complete BRep API documentation and source hygiene 2026-09-30 18:36:00 -04:00
Clifford Yapp 2cced67313 Bound BRep and mesh resource paths 2026-09-30 18:36:00 -04:00
Clifford Yapp 09b0760a0d Hide CDT test hooks from public BRep API 2026-09-30 18:36:00 -04:00
Clifford Yapp ca06b99b68 Centralize Manifold mesh certification in libbg
Route BoT checks and OpenVDB repair validation through the public
libbg certification API.  Preserve clockwise BoT handling, remove the
plugin direct dependency, and cover reversed winding and command use.
2026-09-30 18:36:00 -04:00
Clifford Yapp 1d0699a0f2 Name the advanced triangle repair API explicitly
Rename the branch-only numbered entry point to bg_trimesh_repair_ex.
Keep the established repair API as a compatibility-oriented convenience
wrapper while making advanced call sites self-describing.
2026-09-30 18:35:59 -04:00
Clifford Yapp b775ea9c1d Consolidate constrained polygon triangulation APIs
Use one strict and one cleaning entry point with optional interior
constraints.  Remove the experimental poly2tri test API and give
triangulation diagnostics a typed failure reason.
2026-09-30 18:35:59 -04:00
Clifford Yapp c951995a7c Reject empty degenerate repair meshes
Guard local degenerate-neighborhood repair before zero-size mesh
validation or allocation when every triangle belongs to a closed
collapsed island. Add a contract regression and make C17 invalid-input
cases fail normally instead of aborting.
2026-09-30 18:35:59 -04:00
Clifford Yapp 7b5f5198ac Retry only incomplete adaptive B-Rep faces
A coarse display pass can leave a few difficult faces incomplete under
host contention.  The adaptive scheduler estimated every retry as a
full-object refinement pass, so it declined useful work even when only a
handful of faces needed another attempt.

Add an incomplete-only recovery pass at the next finer tolerance before
ordinary refinement.  It skips all accepted faces, accounts retained
output with the existing hard byte and point guards, and checks the
absolute deadline before each remaining face instead of charging the
cost of the whole object.

All five corpus status changes now complete under parallel contention,
including the 13,234-face chess and 2,450-face tire models.  Existing
display geometry is left untouched and the focused 17-test gate passes.
2026-09-30 18:35:59 -04:00
Clifford Yapp ec8be4dc7f Reduce boundary guard cost on large B-Reps
Querying IsLinear on every proxy edge and revisiting every triangle
corner made the boundary-envelope guard consume too much of the display
deadline on models with thousands of faces.  Some initial face jobs then
had no time left to complete.

Treat degree-one spans structurally, sample only their endpoints, and
bound curved sampling to four points per span and 128 per edge.  Measure
the realized box from the compact output point array once rather than
from repeated triangle indices.

This preserves all three recovered corpus extents while restoring the
five large-model status changes.  KF-21 exterior now completes all
13,224 faces in 1.25 seconds instead of leaving two faces incomplete.
2026-09-30 18:35:59 -04:00
Clifford Yapp 0dd8a62358 Guard adaptive meshes with B-Rep boundary coverage
Unsigned area can stabilize while coarse surface sampling aliases an
entire curved extent.  A soft per-face triangle share could then
preserve a small but incomplete display mesh.

Sample the actual B-Rep edge proxy curves once, assemble a boundary
envelope for each face, and measure coverage using only vertices that
participate in output triangles.  Area convergence and soft triangle
limits now stop refinement only after the mesh covers that envelope.
Hard byte, point, and deadline limits remain authoritative.

Report residual incomplete envelopes as explicit approximation
provenance.  This restores the expected extents of all three corpus
regressions while preserving bounded display time and triangle counts.
2026-09-30 18:35:59 -04:00
Clifford Yapp 0a1a59147c Bound exact B-Rep surface cues by remaining time
Assign each optional surface cue a fair share of the remaining draw
deadline. Check it while finding trim tangents and subdividing the
CurveTree, then use the existing bounded untrimmed cue when exact trim
hierarchy construction cannot finish in its share.

Preserve the existing CurveTree and SurfaceTree constructor symbols.
Report time- and memory-driven approximations separately, avoid a false
edge timeout after the worker queue is empty, and let the audit accept a
complete tagged visual approximation while retaining its partial API
code.

The four prior wireframe timeouts now retain every exact edge and finish
all requested items in 2.7 to 4.6 seconds. Their optional approximated
cues remain identified by face for later inspection.
2026-09-30 18:35:59 -04:00
Clifford Yapp c40ef0a244 Adapt display tessellation to topology and time budgets
Generate a complete coarse whole-object mesh before walking toward the
requested tolerance. Give planar faces their requested boundary
tolerance immediately. Stop curved-face refinement after two stable
unsigned-area measurements or when a topology-weighted triangle share
is exhausted.

Reserve measured time for each new pass so optional refinement cannot
displace an already complete display result. Retain authoritative trim
boundaries even when they exceed a soft triangle target. Report coarse,
converged, budget-limited, and deadline-limited outcomes.

Keep rigorous repair fallbacks on their requested-tolerance path.
Expose adaptive controls and provenance through brep-audit and its
corpus driver. This reduces a 596k-triangle spring to 3k triangles in
0.07 seconds and a former 1.18M-triangle timeout to 23k in 0.59 seconds.
2026-09-30 18:35:59 -04:00
Clifford Yapp 705897a21d Budget concurrent B-Rep face tessellation
Reserve shared working memory for fast CDT face jobs according to the
trimmed NURBS control net and trim-span complexity.  The default is
capped at one GiB and one quarter of currently available memory, while
callers may set a lower hard allowance.

Check the deadline only after claiming real work so a worker leaving an
empty queue cannot report a false timeout.  Report the peak reservation
and expose the same working-memory control through brep-audit.

The prior 1.3 GiB corpus failure now completes near 271 MiB RSS.  The
former shaded timeout cases complete without limit flags, leaving only
actual face-generation failures.
2026-09-30 18:35:59 -04:00
Clifford Yapp e5aa5ed6cb Bound collapsed B-Rep face domains
Treat zero-width surface parameter intervals as line or point images
instead of passing them to two-dimensional surface trimming.  This
lets valid degenerate faces retain exact isocurve bounds.

Fall back to the complete supporting surface only when trim-envelope
bounding cannot produce a conservative result.  Focused tests cover
collapsed line, point, and face domains; previously failing corpus
faces now produce finite reference boxes.
2026-09-30 18:35:59 -04:00
Clifford Yapp ddb7c61234 Bound B-Rep display work by geometric significance
Stop trim subdivision when mapped features are below the requested
display tolerance or parameter subdivision cannot make progress.

Translate caller byte budgets into hierarchy work. Preserve exact edges
with a tagged bounded surface-cue fallback, and expose independent audit
controls and provenance.
2026-09-30 18:35:59 -04:00
Clifford Yapp c8772b24af Bound pathological B-Rep display cue trees
Limit optional trimmed surface cue construction so malformed p-curves cannot exhaust process memory. Retain completed edge wireframes when a cue tree exceeds its bounded display budget, while caching periodic shading state in the fast CDT path.
2026-09-30 18:35:59 -04:00
Clifford Yapp 05de8fb214 Retry final B-Rep repair with bounded hole area
Add a final-only candidate area ceiling for bounded open-edge
residues. Keep aggregate B-Rep area and surface-deviation checks
authoritative so larger local caps do not weaken accepted fidelity.
2026-09-30 18:35:59 -04:00
Clifford Yapp 299d8a7533 Validate overlapping mesh repair caps
Reject hole-fill faces that overuse or misorient existing edges and keep only added components that improve mesh topology.  Make non-manifold vertex splitting transactional and close exact touching three- and four-edge boundary cycles before their identity is lost.
2026-09-30 18:35:59 -04:00
Clifford Yapp 4906e40cf9 Bound large whole-display repair fallbacks
Route very large, nearly closed flat hybrids to an authorized display
fallback before generic repair.  Validate tagged whole-display results
against their existing display-mesh witness before constructing costly
trimmed-surface trees; retain coverage, area, solid, and Manifold gates.
2026-09-30 18:35:59 -04:00
Clifford Yapp c760669905 Split closed vertex links before flat removal
Separate disconnected fans while edge incidence is still closed.  Removing
flat triangles first can connect thousands of independent fans through
small holes and turn a topology-only operation into global repair.
2026-09-30 18:35:59 -04:00
Clifford Yapp ccd8e14af8 Skip repair for flat-dominated hybrid meshes
Route edge-closed hybrids whose flat-triangle population exceeds the
bounded local repair envelope directly to an authorized whole-display
fallback.  Preserve local repair for sparse defects and retain all final
solid, Manifold, and fidelity gates.
2026-09-30 18:35:59 -04:00
Clifford Yapp 1dc12d09d9 Repair rigorous degenerate mesh neighborhoods
Permit independently flat, source-attributed rigorous triangles to enter
the bounded local repair.  Traverse only incident edges, preserve directed
boundaries, and split weakly simple circuits into manifold patch fans.
Keep the operation transactional and tag every affected B-Rep face.
2026-09-30 18:35:59 -04:00
Clifford Yapp f47898d038 Certify B-Rep meshes with Manifold importer
Expose a read-only bundled Manifold acceptance check and report its
result from brep-audit.  Native rigorous meshes now avoid repair only
after the primary downstream consumer accepts them.
2026-09-30 18:35:59 -04:00
Clifford Yapp c4d35b7faa Skip disproportionate hybrid hole repair
Run transactional local reconstruction before deciding whether a hybrid
mesh is a viable generic hole-fill candidate. Route edge-closed hybrids
with flat triangles spanning disproportionately many invalid vertex fans
directly to the explicitly requested whole-display fallback.
2026-09-30 18:35:59 -04:00
Clifford Yapp 7c6569277c Accept bounded repaired mesh extent shortfalls
Keep the boundary bbox ratio guard for native and untagged meshes.
For a certified repair, allow a missing endpoint only when its absolute
shortfall is within the same strict or relaxed surface-deviation limit
that accepted the approximation.
2026-09-30 18:35:59 -04:00
Clifford Yapp 7c3f00d38e Bound difficult facets and repair collapsed fallbacks
Add opt-in per-face rigorous deadlines and avoid repeated full
self-intersection scans while tracking refinement progress.

Preserve fast-face provenance, normalize only locally resolvable
roundoff seams transactionally, and reuse degenerate-neighborhood
repair for edge-closed whole-display fallbacks.
2026-09-30 18:35:59 -04:00
Clifford Yapp d2a6e0b821 Make disconnected mesh repair scale linearly
Accumulate component volumes in one triangle pass instead of
rescanning every triangle for each disconnected component.

Use the bounded Steiner fan sooner for large holes when callers
explicitly accept self-intersections.
2026-09-30 18:35:59 -04:00
Clifford Yapp 0163af0684 Bound repair work for pathological hybrid meshes
Route explicitly self-intersection-tolerant holes above the validated
ear limit through the linear Steiner fan.

Preflight disproportionate large hybrid defects before a requested
whole-display fallback, preserving the object repair time budget.
2026-09-30 18:35:59 -04:00
Clifford Yapp 129f2ce3f0 Avoid full mesh rebuilds during crack pairing
Maintain edge incidence incrementally while welding approximate
patch boundaries, and use a spatial endpoint index to avoid scanning
every open-edge pair.

Add a multi-step boundary pairing regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp 797539df2a Retry bounded final mesh repair for large holes 2026-09-30 18:35:59 -04:00
Clifford Yapp c2015d8548 Bound adaptive repair fidelity from chart samples 2026-09-30 18:35:59 -04:00
Clifford Yapp eb00217017 Reduce distant periodic images in constant time 2026-09-30 18:35:59 -04:00
Clifford Yapp a326edde2e Avoid full chart scans in boundary checks 2026-09-30 18:35:59 -04:00
Clifford Yapp 0e4a54d223 Avoid trimmed trees for untrimmed repair checks
Fidelity validation built a full trimmed SurfaceTree for every candidate
face even when the caller explicitly allowed matches to the underlying
untrimmed surface.  Pathological trim trees could dominate repair time.

Use the cached underlying-surface closest-point context directly in that
mode and conservatively tag every such result as untrimmed.  Keep the
trimmed-tree path unchanged when trimmed containment is required.

This reduces the EMBER 91290 repair from more than 180 seconds to about
10 seconds while preserving the manifold, area, deviation, and coverage
gates.
2026-09-30 18:35:59 -04:00
Clifford Yapp a64fce1cab Reconstruct holed periodic B-Rep side faces
A failed periodic side face exposes two winding boundary rings after
its artificial seam is removed. Treating that topology as one disk
misclassifies a rim as a hole and cannot form a manifold patch.

Cut both rigorous rims at a bounded common phase, retain all other
rings as holes, and triangulate a rectangular chart without moving
any shared 3-D vertex. Require exact boundary incidence and the
existing whole-mesh manifold, area, deviation, and coverage gates.

This certifies the FarmBot closed-pullback family as tagged tier-2
approximations when the caller explicitly permits their measured
display-reference area difference.
2026-09-30 18:35:59 -04:00
Clifford Yapp 4b50396d2a Subdivide closed B-Rep edges beyond two vertices
A closed NURBS curve can report linear when coincident endpoints
make its chord test degenerate.  Do not stop at two half-edge
samples, since they cannot form a triangulable boundary loop.

Apply the curved seed subdivision to every closed trim and preserve
the authoritative 3-D edge when one pullback agrees and the other
must be quarantined.
2026-09-30 18:35:59 -04:00
Clifford Yapp ce95c4b427 Admit bounded large-area B-Rep face repairs
A crossed multi-ring fast face can omit most of its intended region,
making it an unreliable local area reference.  Permit exact-boundary
candidates up to five times that area.

The complete mesh must still pass the caller area, deviation,
reverse-coverage, manifold, and solid-validation gates.  This recovers
both failed faces of the SteamController ButtonA solid at an explicit
6 percent whole-object area limit.
2026-09-30 18:35:59 -04:00
Clifford Yapp b764b824a3 Repair overlapping B-Rep face neighborhoods
Permit exact-boundary topology candidates to proceed when their local
area remains within a factor of two, leaving the caller area,
deviation, coverage, and solid gates authoritative.

Quarantine retained source faces that overlap earlier faces with exact,
equally oriented triangles.  Reconstruct the complete affected faces
transactionally with the other failed-face neighborhoods.

Add focused duplicate-quarantine coverage and more specific topology
repair diagnostics.
2026-09-30 18:35:59 -04:00
Clifford Yapp 87ca7a7f80 Reconstruct multiple failed B-Rep faces transactionally
Treat edges used only by one face as internal chart seams rather than
shared shell boundaries. Rebuild independent failed faces in a
dependency-aware transaction while deferring defects owned by pending
faces; require the final assembled mesh to pass all solid checks.
2026-09-30 18:35:59 -04:00
Clifford Yapp 9651eb0601 Merge numerically identical B-Rep chart samples
Detect collisions after the same conditioning used by detria. Merge
only samples with matching mesh identity or compatible topology and
sub-tolerance 3-D positions, then normalize affected constraints.
2026-09-30 18:35:59 -04:00
Clifford Yapp b81cf5303b Cancel exact retraced polygon whiskers
Weakly-simple trim loops may contain an exact A-B-A retrace that does
not change their filled region.  Cancel these zero-area whiskers after
coordinate deduplication so detria receives a simple ring.

Add a clean triangulation regression with a retraced hole boundary.
2026-09-30 18:35:59 -04:00
Clifford Yapp 20e68cc8ad Repair independent failed B-Rep faces transactionally
Track every fallback triangle's source face and prove each failed
face's complete sampled boundary against the retained rigorous mesh.
Rebuild nonadjacent faces one at a time, remapping later boundaries
after mesh compaction, but commit only when the final assembly is
manifold.

Reject inferred topology whose local area differs by more than one
percent from the available face approximation.
2026-09-30 18:35:59 -04:00
Clifford Yapp a7a4175636 Repair toleranced closed cylinder chart paths
Rebuild a closed edge periodic embedding from its exact 3D samples
when imported p-curves collapse or overshoot an endpoint.  Keep every
shared spatial sample unchanged and bound all chart movement by the
declared edge tolerance.
2026-09-30 18:35:59 -04:00
Clifford Yapp 7f3091de7b Broaden authoritative B-Rep disk recovery
Allow one-loop faces with complete shared-edge samples to use the
tagged topological-disk approximation after face or chart construction
failures.  Keep refinement and later failures on their existing paths.
2026-09-30 18:35:59 -04:00
Clifford Yapp a97c4f0d6e Reject malformed local B-Rep repair charts
Preserve CDT point identity when assembling repaired faces.
Reject cleaned charts whose open edges do not match a topology-defined
boundary, allowing the authoritative disk fallback to close the face.
2026-09-30 18:35:59 -04:00
Clifford Yapp 5a2ce89967 Normalize closed repair mesh topology
Synchronize inconsistent triangle winding before broader repair. When
Manifold accepts a candidate that libbg still rejects, round-trip its
topology and retain the result only after libbg certifies it.

Report both operations and cover winding-only repair with a regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp 50009e543f Add bounded B-Rep geometry lint checks
Detect trim/edge disagreement, collapsed singular boundaries,
oversized stored tolerances, and sampled trim-loop crossings.
Bound work and JSON detail reporting, and cover tolerance-masked
geometry with a focused regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp 941d8a61da Repair local B-Rep defects with tagged fidelity
Grow failed-face repair regions through approximate edge stars while
preserving rigorous boundaries and requiring a valid final solid.

Add an opt-in relaxed fidelity tier for already-Manifold repairs, with
face and edge provenance plus corpus audit controls.
2026-09-30 18:35:59 -04:00
Clifford Yapp 5bcf5ab9c3 Recover meshes past inconsistent shared edges 2026-09-30 18:35:59 -04:00
Clifford Yapp 5c646a2007 Contain rigorous tessellation allocation failures 2026-09-30 18:35:59 -04:00
Clifford Yapp a3b9639a27 Rebuild holed B-Rep faces from exact boundary rings
Recover all closed rings exposed by the retained rigorous mesh.

Use source, cleaned, inferred, and planar charts in bounded order.

Keep exact shared vertices and gate inferred topology by area.

Cover planar holes and twisted projections with focused tests.
2026-09-30 18:35:59 -04:00
Clifford Yapp c30ad5b0fb Rebuild failed B-Rep faces from rigorous boundaries
Discard broken single-face fallback meshes when retained neighbors
expose an exact closed boundary. Span that boundary without moving
certified vertices, then retain the existing fidelity and manifold
gates.

Add local neighborhood diagnostics and contracts for boundary identity
pairing and reconstruction.
2026-09-30 18:35:59 -04:00
Clifford Yapp 9992cda8f2 Preserve topology in local B-Rep repair
Track caller identities through constrained fast-face samples so
locally reconstructed boundaries join rigorous face vertices without
coordinate welding.

Interpret explicit seams on closed revolution surfaces with a bounded
synthetic strip, and split edge-closed pinched vertex fans before
geometric repair. Add focused contracts for both paths.
2026-09-30 18:35:59 -04:00
Clifford Yapp e28afd0185 Stitch reconstructed faces by CDT identity
Preserve authoritative shared-point identities when inserting local
patches. Reject nonorientable periodic correspondences and retry the
opposite traversal without changing its geometric phase.
2026-09-30 18:35:59 -04:00
Clifford Yapp 510eb27926 Propagate mesh winding by face adjacency
Solve orientation parity across every manifold edge and reject
contradictory nonorientable cycles. Preserve indexed solids that
Manifold already accepts without coordinate welding.
2026-09-30 18:35:59 -04:00
Clifford Yapp a5cd853e45 Span failed disk faces from shared boundaries
Preserve authoritative edge samples and span only a failed disk face.

Report its source topology and exercise unusable projected pullbacks.
2026-09-30 18:35:59 -04:00
Clifford Yapp fab9b4fe90 Lift revolution cylinder paths across chart cuts
Recognize the angular parameter of revolution-cylinder surfaces.

Use existing periodic ring lifting to remove false boundary chords.

Retain native samples and topology throughout the chart repair.
2026-09-30 18:35:59 -04:00
Clifford Yapp bcfe64e959 Refine accidental cross-face chords
Independent face CDTs can select the same interior chord between shared
B-Rep boundary samples. In a thin solid this gives the chord four
incident triangles and invalidates its endpoint links.

Detect only multi-face chords with more than two incident triangles and
split that exact chord on one source surface. Preserve authoritative
B-Rep boundaries and use the existing assembly refinement limits.

Add focused classification coverage. Mirage now certifies natively,
while Volvo passes explicit self-intersection-tolerant repair as a
closed manifold.
2026-09-30 18:35:59 -04:00
Clifford Yapp 8369bb7ef2 Repair sparse holes across periodic charts
Allow the bounded local repair chart to move an artificial cut to a
nearby exact boundary sample. Keep native surface sampling coherent
when a point has more than one periodic chart image.
2026-09-30 18:35:59 -04:00
Clifford Yapp 38f18e1acf Validate local repairs against constrained inputs
Permit small topology repairs to use their explicitly enabled local
mesh as a final fidelity reference after source-surface projection fails.
Retain the area and reverse-coverage gates.

Strengthen the constrained fallback contract to require a certified
Manifold solid and approximation provenance.
2026-09-30 18:35:59 -04:00
Clifford Yapp 5c6b86db6a Reconstruct malformed cylindrical seam strips
Recognize non-closed analytic cylinders with paired seam topology.
Join exact constrained isocircle samples in a synthetic local chart and
verify the patch against the cylinder.

Add a focused contract and retain face and edge approximation
provenance.
2026-09-30 18:35:59 -04:00
Clifford Yapp 5fa9c8cb6b Check constrained fallback against its source face
Use recorded face provenance to test constrained fallback triangles
against their source surface before searching the entire B-Rep.

Keep the global surface search as a fallback when the local match fails.
2026-09-30 18:35:59 -04:00
Clifford Yapp 28d0941e3d Open multiple holes across periodic seams
Process disjoint seam-crossing holes one at a time and splice each
exact boundary arc into its remaining artificial seam interval.

Use an existing edge sample as the chart cut when adaptive sampling
straddles the cut, without changing its authoritative 3-D point.

Add a focused two-hole cylinder regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp 180dae1bad Reconstruct constrained spherical hemispheres
Join authoritative spherical edge samples across zero-length singular
trims without using the failed parameter chart.

Use exact great-circle loop orientation to select a hemisphere while
preserving every physical boundary point and recording its provenance.

Add a focused chart-free hemisphere regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp bc1da81a02 Reconstruct pole-boundary spherical patches
Detect when an imported singular pole is duplicated on the physical
spherical boundary.

Use the exact boundary's oriented spherical area as an interior
direction, preserve axial boundary samples, and omit their undefined
longitude from the phase certificate.

Keep shared points unchanged and add a focused hemisphere regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp c47c0fcf0c Open seam-coincident periodic holes
Recognize a contiguous hole boundary run on the artificial cut.

Splice its exact samples into the low chart side.

Move the complementary arc to the matching high side.

Preserve legacy two-point cut crossings.

Add a sampled-run triangulation and provenance regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp ba1deee51b Reconstruct closed spheres across malformed seams
Recognize a closed spherical face with exactly one self-mated seam.

Replace that isolated face with two analytic caps.

Make both caps share an exact equator and preserve orientation.

Bound chord deviation and record the original face and seam edge.

Add full-mesh and topology-gate regressions.
2026-09-30 18:35:59 -04:00
Clifford Yapp 18297705b1 Triangulate unsampled spherical lunes
Accept two-pole sphere charts with a nonzero-area boundary.

Interior meridian samples are not required for a physical lune.

Continue to reject unpaired zero-area periodic cuts.

Add focused lune coverage without interior points.
2026-09-30 18:35:59 -04:00
Clifford Yapp 3d43ed4a01 Reconstruct pole-selected spherical patches
Recognize spherical rings with an artificial pole excursion.

Use the singular vertex to select the retained pole.

Preserve exact shared boundary samples and source provenance.

Reject malformed topology, phase reversals, and excess deviation.

Add a nonplanar pole-selected spherical patch regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp 0832835dd2 Reconstruct malformed torus seam strips
Recognize imported torus faces whose paired seam trims define a strip
even when their NURBS surfaces do not advertise a closed direction.
Keep exact shared boundary samples and replace unreliable p-curve
coordinates with analytic torus angles.

Refine bounded rows until their measured chord error meets the repair
tolerance, preserve face and edge provenance, and reject ambiguous or
non-toroidal seam topology.

Exercise both major- and minor-isocircle strip orientations.
2026-09-30 18:35:59 -04:00
Clifford Yapp 8b7ec4660e Reconstruct constrained spherical cap faces
Keep exact shared boundary samples when a seam-only spherical face has
one fully constrained inner ring. Select the complementary cap from
the ring plane and sample bounded concentric rows on the source sphere.

Reject ambiguous or non-spherical inputs, measure every generated chord,
honor point and time limits, and retain existing face and edge
provenance.

Add a synthetic oblique spherical-cap regression.
2026-09-30 18:35:59 -04:00
Clifford Yapp 5f8b6442e5 Refine constrained periodic repair strips
Keep rigorous boundary samples fixed while adding bounded rows sampled
from the failed source surface. This avoids planar projection and
preserves exact neighboring-face incidence.

Measure every generated chord against its source parameters, stop at
configured work and time limits, and retain face and edge provenance
through the existing local repair tier.

Add a curved periodic strip regression and keep the boundary-only and
doubly periodic contracts.
2026-09-30 18:35:59 -04:00
Clifford Yapp c8fe72112c Open periodic chart holes across generic seams
Generalize the cylinder seam-hole transformation to periodic metric
charts. Split one hole at its seam samples and splice its arcs into
the two artificial outline sides while retaining native topology.

Add a toroidal metric-chart regression for the crossing-seam case.
2026-09-30 18:35:59 -04:00
Clifford Yapp 1fdb871c24 Reconstruct seam-only doubly periodic faces
Build a wrapped adaptive grid when a failed face covers a doubly
periodic surface and every explicit trim is a seam. Verify both
closures and source-surface deviation before admitting the result.

Preserve direct fidelity evidence and face/edge approximation
provenance through the existing repair report.
2026-09-30 18:35:59 -04:00
Clifford Yapp 139d94a231 Join segmented edge loops in periodic face repair
Assemble consecutive authoritative edge samples into closed boundary
rings before constructing a periodic repair strip.  This handles local
neighborhoods where one logical ring is represented by several B-Rep
edges while retaining every shared sample exactly.
2026-09-30 18:35:59 -04:00
Clifford Yapp 428ed6bf80 Reconstruct failed periodic faces from rigorous boundaries
Join two authoritative closed edge loops with a sparse local strip when
the intervening periodic face cannot be triangulated reliably.  Preserve
all shared boundary samples, support unequal loop densities, and report
the approximation and its source provenance separately.
2026-09-30 18:35:59 -04:00
Clifford Yapp 9c8ef43526 Defer empty mesh rejection until repair fallbacks
Let bounded best-effort, local chart, and constrained face recovery
supply repair geometry when no rigorous face completed.  Reject empty
input and enforce the automatic repair size limit only after enabled
local fallbacks have run.
2026-09-30 18:35:59 -04:00
Clifford Yapp b07c88e870 Retain bounded best-effort face meshes during repair
Use intersection-free stage-10 face meshes as explicitly approximate
repair input when their remaining folds are sparse.  Preserve the
certified prefix and report source faces, edges, folds, and fidelity.
2026-09-30 18:35:59 -04:00
Clifford Yapp 690911f337 Recover paired polar seams on faces with holes
Permit paired periodic seam recovery when unrelated inner loops are
present.  Refine polar chart cells that collapse only after their seam
copies are joined in model space.
2026-09-30 18:35:59 -04:00
Clifford Yapp 15b92dbb16 Report topology when Manifold rejects mesh repair
Classify unmatched, excess, misoriented, and invalid vertex-link
defects before returning from a failed Manifold postcondition.  This
keeps failed repair reports useful for selecting bounded follow-up work.
2026-09-30 18:35:59 -04:00
Clifford Yapp 4d82a3130d Recover bounded disagreements between B-Rep p-curves
Keep normal tessellation strict.  During explicit mesh repair, retry
edge initialization only when the midpoint shared by two disagreeing
p-curves remains within the requested fidelity bound of both faces and
the native edge curve.

Record affected edges, incident faces, and maximum discrepancy in
repair provenance and audit output.
2026-09-30 18:35:58 -04:00
Clifford Yapp 73e18a0083 Permit bounded local replacement of rigorous mesh patches
Allow mesh repair to replace only connected rigorous triangle
neighborhoods bounded by the explicit hole area and edge limits.

Require final solid and Manifold validation, area and surface deviation
checks, plus exhaustive coverage samples over every replaced triangle.
Report affected topology and replacement size as approximation
provenance.
2026-09-30 18:35:58 -04:00
Clifford Yapp 34df58327e Recognize bounded rigorous triangle subdivisions
Accept repair-created edge splits only when two output triangles
retain all original corners and their shared point lies within a tightly
bounded original chord.

Track affected B-Rep faces and expose the subdivision count in repair
reports. Actual rigorous geometry loss remains rejected.
2026-09-30 18:35:58 -04:00
Clifford Yapp 20faab5d64 Bound local B-Rep repair by edge tolerances
Allow approximate face refinement to retain residual discrepancies
only when source and mesh samples lie inside one incident edge
tolerance, capped relative to the requested tessellation tolerance.

Add focused coverage for the bounded tube rule and recover the
Smoke Box twins while preserving all rigorous triangles.
2026-09-30 18:35:58 -04:00
Clifford Yapp 0f893e1923 Reconstruct bounded local B-Rep surface patches
Allow repair to normalize weakly-simple failed-face charts only when
omitted retraces remain within declared B-Rep edge tolerances. Preserve
rigorous neighboring triangles and constrain reconstructed boundaries.

Validate local approximations against their underlying source surfaces,
retain face and edge provenance, and keep ordinary tessellation
fail-closed. Add an opt-in Engine Truck corpus regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp aec310d6c7 Track provenance of local B-Rep repairs
Preserve every certified rigorous triangle during local repair and use
ordered full-domain trim samples for constrained fallback faces. Report
the original faces, edges, and approximation tier needed by accepted
meshes so conversion callers can attach provenance attributes.
2026-09-30 18:35:58 -04:00
Clifford Yapp 183bc2ef89 Close articulation holes before mesh splitting 2026-09-30 18:35:58 -04:00
Clifford Yapp 9b1763fb0d Preserve rigorous regions during B-Rep repair
Constrain fast fallback faces to rigorous edge subdivision.

Try relaxed rigorous tessellation for safe invalid B-Reps.

Report patch provenance and compact repair output.

Preserve NIST 6's native pole-seam result with a full-solid test.
2026-09-30 18:35:58 -04:00
Clifford Yapp f13c8c031f Add Manifold-targeted B-Rep mesh repair
Allow explicitly requested repairs to retain self-intersecting caps
while preserving topology, area, and bidirectional fidelity gates.

Add optional Manifold import certification and corpus audit controls.
Make duplicate-fan separation collision aware.
2026-09-30 18:35:58 -04:00
Clifford Yapp 64aa3efe26 Sample B-Rep edges for audit boundary extents
GetTightBoundingBox can retain distant NURBS control hull extents and
make correct meshes look undersized. Sample each proxy-curve span for
the boundary reference while retaining face bounds as the upper guard.

This clears 11 false failures while preserving closed-solid checks.
2026-09-30 18:35:58 -04:00
Clifford Yapp d6b6a7af8b Bound non-manifold vertex fan walks
Malformed facet adjacency can contain a directed cycle that does not
return to the starting face. Stop forward and backward fan walks when
they revisit a corner, preventing unbounded mesh repair.

Add a synthetic cyclic-adjacency regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp 43332ce62e Use face surfaces for untrimmed tree estimates
Surface trees may omit their CurveTree when a face has no usable trim
boundary. Pass the owning face surface through closest-point queries
so invalid B-Reps cannot dereference missing trimming topology.

Cover the no-loop face case in the CDT contract.
2026-09-30 18:35:58 -04:00
Clifford Yapp cb32db1da4 Prefer local mesh repair before Poisson
Try bounded whole-display mesh repair before automatic Poisson
reconstruction. Use the tessellation minimum scale for seam welding and
repeat only defect-reducing welds before the next hole-fill pass.

Cap that opportunistic pass at 8192 triangles while leaving explicit
large local repairs available to callers.

Keep explicit Poisson scales as direct reconstruction requests and cover
both paths in the repair contract.
2026-09-30 18:35:58 -04:00
Clifford Yapp 36b876d111 Skip discarded rigorous repair refinement 2026-09-30 18:35:58 -04:00
Clifford Yapp 75be69c6a1 Preserve certified Poisson meshes during repair 2026-09-30 18:35:58 -04:00
Clifford Yapp 93276909a7 Add bounded Poisson closure fallback 2026-09-30 18:35:58 -04:00
Clifford Yapp 7a0882a220 Honor Screened Poisson boundary conditions 2026-09-30 18:35:58 -04:00
Clifford Yapp ae0e8ea47b Retry Poisson repairs after reference area failures 2026-09-30 18:35:58 -04:00
Clifford Yapp c15d780d96 Accept certified repairs with partial reference bounds
Invalid B-Reps can have individual trimmed-face bounding-box failures
while aggregate face and edge bounds remain usable. Once mesh repair
passes solid and bidirectional fidelity certification, retain the failed
face indices as diagnostics without overriding the successful result.
2026-09-30 18:35:58 -04:00
Clifford Yapp e4b3facbe9 Filter corpus selections by B-Rep validity
Add input-valid and input-invalid selection classes so repair surveys
can target the B-Rep validity flag independently of closed-solid
quality eligibility. Keep the existing valid and invalid filters
unchanged.
2026-09-30 18:35:58 -04:00
Clifford Yapp 5c80cc49e0 Measure repaired area against the B-Rep reference
When a display reference is available, enforce the configured area
change limit against that retained B-Rep tessellation instead of the
defective intermediate Poisson mesh. Preserve the intermediate-area
metric for diagnostics.

Forward deviation, reverse coverage, solid, and intersection checks
remain unchanged.
2026-09-30 18:35:58 -04:00
Clifford Yapp 75c4301477 Add bounded Steiner hole repair
When boundary-only triangulation cannot avoid surrounding geometry, try
one centroid fan and a fixed sequence of normal offsets capped at one
percent of the hole scale. Validate every candidate against existing
and accepted triangles, and roll back failed points and faces.

Retain final manifold, intersection, area, and fidelity gates. Cover
the offset retry with an oriented solid regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp 428a9cdac4 Improve bounded mesh hole triangulation
Preserve concave planar outlines with a strict planar projection before
using LSCM. Gate bounded 3-D ear candidates against the existing mesh
and accepted cap triangles so an obstructed diagonal can be avoided.

Correct direct-ear winding to match the hole boundary. Add regressions
for concave caps and alternate valid diagonals.
2026-09-30 18:35:58 -04:00
Clifford Yapp 4af5726d4f Use paired p-curves when edge curves are corrupt
Fall back to matching trim midpoints when an imported edge curve cannot
produce usable pullback parameters. Require both surface evaluations to
agree within the declared edge tolerance before substituting their
shared point.

Report the exact edge that prevents initialization and cover the
fallback with a corrupt-edge contract.
2026-09-30 18:35:58 -04:00
Clifford Yapp d93271312f Use B-Rep surface normals for Poisson repair
Feed normalized Fast-CDT corner normals into reconstruction.

Do not infer orientation from face-local triangle winding.

Interpolate normals with area samples on curved surfaces.
2026-09-30 18:35:58 -04:00
Clifford Yapp 07e26730c1 Separate touching mesh components inward
Move duplicate-coordinate fans against their outward normals so tangent
shells separate instead of intersecting.  Report wider-search distances
and source faces for failed B-Rep repair samples.
2026-09-30 18:35:58 -04:00
Clifford Yapp 6d97c5bbb5 Retry Poisson repair with area-weighted samples
Keep centroid sampling as the primary reconstruction.

After both domain scales fail input coverage, retry with one bounded

area-weighted sample budget to preserve undersampled thin faces.

Report failed coverage distances, source faces, and reference area.
2026-09-30 18:35:58 -04:00
Clifford Yapp 34b4d2d60e Preserve disconnected solids in Poisson repair
Track display triangles by B-Rep face and reconstruct each closed

face component independently.

Bound component work and retain global reconstruction when invalid

topology cannot identify reliable closed components.
2026-09-30 18:35:58 -04:00
Clifford Yapp 62f1b14233 Honor configured Poisson point weights
Validate the public SPSR point weight and pass it to the solver so the documented option is no longer ignored.
2026-09-30 18:35:58 -04:00
Clifford Yapp 3f99e5a390 Strengthen bounded mesh repair certification
Retain safe hole caps when another independent cap intersects the mesh,
and report the residual topology that prevents a solid result.

Preserve the display mesh across Poisson reconstruction and require
bounded source-to-output coverage in addition to output deviation. Add
a fixed-scale audit option for controlled reconstruction experiments.
2026-09-30 18:35:58 -04:00
Clifford Yapp 4cf8291866 Retry Poisson repair with bounded domain padding
Honor the public SPSR scale option and preserve its prior effective
1.1 default.

Let B-Rep repair retry at scale 1.2 when a scale 1.1 reconstruction
cannot pass the existing solid and fidelity certificate.
2026-09-30 18:35:58 -04:00
Clifford Yapp 86b970ea35 Use display meshes as repair deviation references
Use the whole fast tessellation as a secondary distance reference when

direct B-Rep projection fails during explicit reconstruction.
2026-09-30 18:35:58 -04:00
Clifford Yapp b5376cc87c Reject intersecting mesh repair caps
Detect intersections introduced by each hole-fill pass with an R-tree.

Retry the 3-D ear triangulation and retain the open boundary when

neither patch is safe.
2026-09-30 18:35:58 -04:00
Clifford Yapp a9a239a5ad Allow repair fallback without rigorous face meshes
Reject empty mesh exports without zero-byte allocations.

When the explicit whole-B-Rep fallback is enabled, continue to the

display mesh if no usable rigorous mesh can be assembled.
2026-09-30 18:35:58 -04:00
Clifford Yapp f3166082f0 Apply audit resource bounds to repair fallback
Forward the audit point, result-size, and time limits to fast mesh
generation used by the repair path.
2026-09-30 18:35:58 -04:00
Clifford Yapp 2e66cb2dd8 Add filtered B-Rep repair corpus surveys
Allow brep-audit batch processes to restrict work to object names from
a file and express deviation policy relative to each trimmed boundary.

Teach the corpus runner to select prior JSONL failures, preserve one
process per database, and forward bounded repair and reconstruction
controls.
2026-09-30 18:35:58 -04:00
Clifford Yapp f1a16f34ce Add certified Poisson B-Rep repair fallback
Add an explicit whole-display-mesh reconstruction tier for cases where
rigorous CDT and conservative mesh repair cannot produce a solid.

Bound input, output, and depth; retain final manifold, intersection,
area, and sampled B-Rep deviation checks. Export certified meshes from
invalid B-Reps and report reconstruction provenance through brep-audit.
2026-09-30 18:35:58 -04:00
Clifford Yapp fde87ca24e Strengthen triangle repair and Poisson safety
Separate disconnected point-contact fans within a bounded tolerance.
Normalize Manifold union output and reject remaining degenerate faces.

Validate Screened Poisson inputs and outputs, serialize calls around its
global worker state, and honor explicit serial reconstruction requests.
2026-09-30 18:35:58 -04:00
Clifford Yapp 8be6fae2ff Improve B-Rep tessellation and repair recovery
Normalize periodic and singular charts, bound edge subdivision, and repair
sub-tolerance topology defects before CDT. Add an explicit post-CDT repair
fallback with rigorous manifold, intersection, area, and sampled B-Rep
deviation gates, plus brep-audit reporting and contract coverage.
2026-09-30 18:35:58 -04:00
Clifford Yapp 3b23ae1cf1 Extend triangle mesh repair for B-Rep recovery
Handle collinear hole boundaries and hanging boundary vertices. Add an
opt-in Manifold union for overlapping closed components, preserving its
exact merge topology, and cover the new paths with focused tests.
2026-09-30 18:35:58 -04:00
Clifford Yapp 942956127e Add conservative triangle mesh repair API
Expose explicit controls for component removal and hole filling.
Require solid output and report repair changes and mesh measures.
Keep the legacy repair interface behavior through a compatibility wrapper.
2026-09-30 18:35:58 -04:00
Clifford Yapp 0f22dd800c Preserve constraints through polygon cleanup
Track Clipper's filled-component hierarchy and triangulate each outline
with only its direct holes.  Retain certified noncrossing interior
constraints after coordinate cleanup, and cover touching and overlapping
holes plus an interior constraint in the regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp f153244934 Split shared B-Rep edges at surface poles
Locate analytic surface poles on shared master curves before ordinary
edge subdivision.  Reuse the authoritative pole point and decompose
repeated-pole boundaries into chart disks so adjacent faces remain
watertight.
2026-09-30 18:35:58 -04:00
Clifford Yapp 9083367e5e Repair false cone seam closures
Lift one pole branch to an equivalent periodic image when a seam-copy
edge crosses the real boundary. Merge the coincident seam endpoints
only after the candidate chart proves simple.
2026-09-30 18:35:58 -04:00
Clifford Yapp 7ba91791ef Release CDT state before audit metrics
Discard face-local tessellation state once mesh export succeeds so
large corpus audits do not retain it beside independent topology data.
2026-09-30 18:35:58 -04:00
Clifford Yapp 19f4a3b12e Compact large CDT mesh assembly
Use dense and hashed point indexes during export. Discover mesh
components with packed edge sorting and union-find instead of per-edge
trees and neighbor vectors.
2026-09-30 18:35:58 -04:00
Clifford Yapp 70944341d7 Embed planar CDT charts from master curves
Project authoritative 3-D boundary samples into planar charts so
tolerance-separated STEP curves cannot overlap through imperfect UV
pullbacks.
2026-09-30 18:35:58 -04:00
Clifford Yapp e2db4288d8 Compact and cache certified B-Rep CDT meshes
Reduce shared-boundary bookkeeping overhead with compact hash maps
and drop unused normal provenance.

Retain certified exports and reconstruct optional normals on demand.
This avoids repeating million-triangle orientation and validation.
2026-09-30 18:35:58 -04:00
Clifford Yapp 3ffd1568a8 Repair incomplete corpus logs on resume
Trim only an invalid trailing JSONL record before computing batch
resume positions. Preserve a complete final record by restoring its
missing newline.
2026-09-30 18:35:58 -04:00
Clifford Yapp af1e161565 Index CDT polygon intersection candidates
Use an RTree broad phase to avoid quadratic projected boundary
segment comparisons while preserving the existing OpenNURBS
predicates and pair order.

Cover the high-edge-count path with a focused regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp 0570937347 Require progress for shared edge splits
Reject subdivisions whose master point or either pullback cannot
advance beyond both child endpoints.

This prevents invalid edge mappings from inserting the same boundary
coordinate repeatedly, and retires stalled proximity refinements.
2026-09-30 18:35:58 -04:00
Clifford Yapp e8b2994111 Refine nearby shared edges for assembled CDT
Cross-face intersections may come from a coarse shared boundary even
when the reported triangles are one element inward.

Refine the authoritative shared edge within the local chord sag
scale, and leave remote intersections to interior recovery.
2026-09-30 18:35:58 -04:00
Clifford Yapp b2d927fcbe Tighten shared edge orientation refinement
Limit shared B-Rep segments to 22.5 degrees of adjacent surface
normal change even when no normal tolerance is requested. This keeps
coarse boundary chords from forcing folded triangles on narrow curved
bands.
2026-09-30 18:35:58 -04:00
Clifford Yapp 3579b9757a Preserve periodic chart boundary images
Do not reconstruct a full ring through its synthetic closing
point. This avoids replacing the opening seam image and creating a
period-long boundary constraint.

Clamp periodic offsets at domain bounds before wrapping so roundoff
cannot move polar seam samples to the opposite side.
2026-09-30 18:35:58 -04:00
Clifford Yapp 8d5141e37b Orient spherical cap seams from boundary continuity
Apply the existing pole-wedge seam-run continuity rule to periodic
spherical caps as well as cones. This avoids forcing native parameter
bounds onto the wrong chart side when a closed trim winds oppositely.

Add direct regressions for both boundary winding directions.
2026-09-30 18:35:58 -04:00
Clifford Yapp 55fb4bb908 Harden topology-aware B-Rep CDT recovery
Recover analytic cone, polar, and cylinder charts while preserving
source boundary identities. Retain only proven mesh components, honor
finite edge tolerances for bounded collapse, and normalize
weakly-simple developable charts behind exact topology checks.

Add focused regressions for each corpus-derived failure class.
2026-09-30 18:35:58 -04:00
Clifford Yapp 090b219a5a Partition disconnected CDT face charts
Build an even-odd containment forest for valid faces with disjoint or
nested boundary islands. Triangulate each filled disk independently and
merge it through the existing face-atlas identity and validation path.

Share atlas installation with cone charts and add focused coverage for
disconnected loops and hole-within-hole topology.
2026-09-30 18:35:58 -04:00
Clifford Yapp 97d972177d Repair inverted CDT chart nesting
Treat metric UV and unrolled cylinder charts as planar embeddings when mutual containment proves that an imported outer and hole are swapped.

Add focused curved-surface and cylinder regressions.
2026-09-30 18:35:58 -04:00
Clifford Yapp 246c2ed6c0 Refine periodic chart cells before seam stitching
Distinct chart triangles can collapse to the same three model vertices
when periodic seam copies are canonicalized.  Detect these quotient
collisions, seed each chart cell with an authoritative surface sample,
and retriangulate under existing point and attempt bounds before
geometric repair.

Cover the collision with matched cone seam samples.
2026-09-30 18:35:58 -04:00
Clifford Yapp fb8c715e83 Place analytic cone seams on native sides
Treat native periodic bounds as authoritative for cone seam copies.
Analytic closest-point projection may otherwise place both copies in the
same periodic image and create duplicate chart constraints.

Cover matching topology and non-topology seam samples in the cone chart
regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp c24f37370f Include source UV in CDT failure dumps
Record the closed surface domain and each source point's native UV in
standalone constrained triangulation reproducers.  This makes periodic
image and seam-side mistakes diagnosable without reopening the database.
2026-09-30 18:35:58 -04:00
Clifford Yapp 6ed2a69a06 Recover weakly-simple planar face loops
Use the Clipper-based sanitizer only after strict CDT rejects an
ordinary planar chart.  Map every cleaned point back to an original
chart vertex and require the output triangle boundary to match every
original nonzero B-Rep constraint exactly.

Reject new points, missing samples, altered edges, periodic charts, and
charts with auxiliary constraints.  This preserves shared-edge identity
while allowing repeated topology vertices to be normalized.

Add a touching-hole regression.  The BUG light pipe and angle widget
now certify as closed solids without incidence, link, or area defects.
2026-09-30 18:35:58 -04:00
Clifford Yapp 21b32b0f69 Separate toleranced B-Rep endpoint samples
Some valid imports retain distinct master-edge samples whose p-curves
round to the same surface coordinate near a trim endpoint.  Redistribute
only non-topological samples along the existing chart chord when the
collapsed 3-D residual fits the effective edge tolerance.

Keep native UV and shared 3-D points authoritative, protect periodic,
singular, closed-surface, and topology vertices, and retain the
scale-aware modeling tolerance guard.

Add a focused regression and make Rocket 21D_v4 certify as a closed
mesh.
2026-09-30 18:35:58 -04:00
Clifford Yapp c13ae304f4 Report strict chart conflicts precisely
Identify duplicate chart coordinates, reused ring vertices, and
duplicate or intersecting constraints in strict PSLG diagnostics.

Include chart and source-point provenance in opt-in CDT failure
dumps so corpus failures can be diagnosed independently.
2026-09-30 18:35:58 -04:00
Clifford Yapp 5ce858cb7a Bound heuristic B-Rep edge refinement
Batch close-edge RTree updates by rebuilding each affected face once
per round. Avoid maintaining the unused 3-D index before finalization
and remove full-tree count diagnostics from the hot removal path.

Floor shape-only proximity and neighboring-curve refinement by the
requested minimum spacing and a per-source-curve subdivision scale.
Geometric curve and chord tolerance passes remain authoritative.
2026-09-30 18:35:58 -04:00
Clifford Yapp 3ce0e865c0 Release filtered CDT surface samples
Delete owned UV candidates when polygon and trim-edge filters
reject them. Previously those pointers were erased from their owning
sets and escaped the cdt_surf_info destructor.

Keep deletion opt-in at the generic polygon helper. Enable it only at
surface sampler call sites that pass owned candidates.
2026-09-30 18:35:58 -04:00
Clifford Yapp 80df3d1f98 Bound linear B-Rep edge subdivision
Floor inherited straight-edge spacing at the global and edge-local
requested resolution.  Stop when floating-point edge parameters have no
representable interior value, accepting only residuals covered by the
source edge tolerance.

Cap edge subdivision with actionable diagnostics.  This prevents the
FarmBot LED and Motor cable solids from exhausting memory while exposing
their later geometry failures.
2026-09-30 18:35:58 -04:00
Clifford Yapp e40e65b12c Collapse sub-tolerance B-Rep edges safely
Collapse only explicit edges whose full curve and bounded component fit
within the effective modeling tolerance.  Weld derived mesh state across
incident faces while retaining periodic seam provenance.

Add scale, transitivity, curve-shape, and ring regressions.  This lets
the Intermediate Nut twins produce certified closed meshes without
altering the source B-Rep.
2026-09-30 18:35:58 -04:00
Clifford Yapp 339a9f40f0 Lift seamless cylinder charts continuously
Unwrap each no-seam boundary ring through adjacent periodic images.
This prevents arbitrary analytic cuts from turning short boundary paths
into circumference-long constraint chords.

Reject nonzero winding that needs a real atlas cut, align holes and
interior samples with the lifted outline, and add a focused regression.
2026-09-30 18:35:58 -04:00
Clifford Yapp f10919d8a7 Repair periodic chart paths at native seams
Treat native periodic-domain bounds as authoritative seam copies even
when analytic surface fitting offsets their projected angle.

Normalize full-circle endpoints and propagate a single side along seam
trims. Add a cylinder regression with offset geometry and malformed
p-curves using equivalent bounds.
2026-09-30 18:35:58 -04:00
Clifford Yapp 620488965a Make RTree seed selection scale independent
Always accept the first candidate split pair before comparing waste.
This avoids duplicate seeds when large finite volumes make a numeric
sentinel round back to a tied candidate value.

Add a regression using the large, nearly identical boxes from BRep
facetization aborts.
2026-09-30 18:35:58 -04:00
Clifford Yapp 8c301eabd1 Refine assembled crossings along exact chords
Only split a shared B-Rep edge when both reported triangles are
incident to it.  Otherwise split the closest internal triangle edge in
its active chart and preserve native-to-3-D correspondence through
collapsed pole aliases.
2026-09-30 18:35:58 -04:00
Clifford Yapp 9744ca6b17 Refine folded faces in active CDT charts
Choose adaptive samples in topology charts for nonlinear polar maps.

Repair residual chords with transactional chart-certified flips.
2026-09-30 18:35:58 -04:00
Clifford Yapp 99ecf3beef Honor BRep tolerances at mesh boundaries
Shared edge points may lie off an adjacent surface within the source
edge or vertex tolerance.  Do not refine those boundary strips
indefinitely, while retaining strict checks for ordinary face folds and
final mesh geometry.
2026-09-30 18:35:58 -04:00
Clifford Yapp ce64ad5bcc Match closed edge splits by geometry
Closed edge curves and periodic p-curves may use different phase
origins, making m_bRev3d insufficient to pair their split halves.
Select each closed-edge child interval by model-space proximity and
retain native trim domains instead of rewriting unrelated trims.
2026-09-30 18:35:58 -04:00
Clifford Yapp 9afcf3f474 Filter rigorous corpus audits by input validity
Classify each BRep before realization and exclude inputs outside the
valid closed-solid topology contract.  Preserve the classification in
the JSON log and propagate the filter through database batch workers.
2026-09-30 18:35:58 -04:00
Clifford Yapp adbf34a7cc Refine certified CDT crossings structurally
Apply relative tessellation tolerance to shared edge chord error.

Feed certified cross-face intersections back into bounded refinement.
Split shared master edges for adjacent faces and refine native charts
for disjoint faces while preserving source triangle identity.
2026-09-30 18:35:58 -04:00
Clifford Yapp 0ca7c82d4b Repair generic periodic CDT face charts
Lift periodic boundaries from authoritative 3D edge samples and correct
stored pcurve winding before triangulation. Repair inverted planar loop
nesting and avoid uniform overrefinement of closed edges.

Add periodic and nesting regression coverage.
2026-09-30 18:35:58 -04:00
Clifford Yapp cfadf83b17 Repair periodic CDT charts for full seams
Lift spherical seam boundaries continuously and map full cylinders
from authoritative 3D samples.  Preserve valid refinement points and
tighten audit reference bounds.
2026-09-30 18:35:58 -04:00
Clifford Yapp 4f2e121ada Certify CDT mesh self-intersections
Reject nonadjacent triangle crossings in face charts and final meshes.

Refine defects in periodic-aware UV charts.

Retain source face provenance for deterministic diagnostics.
2026-09-30 18:35:58 -04:00
Clifford Yapp 8d88deec1f Decompose pinched cone face charts
Split weak cone outer walks into topology-preserving disk charts.
Recover damaged radial pcurve samples from shared 3-D edge points and
commit atlas boundaries only after every component is certified.
2026-09-30 18:35:58 -04:00
Clifford Yapp d24ca11f30 Add topology charts for trimmed conic faces
Preserve canonical B-Rep edge samples while improving trim pullbacks
and mapping final chart boundaries into the face mesh.

Unwrap partial cylinders in analytic coordinates, stabilize cone rays
at toleranced poles, and accept only orientation discrepancies covered
by declared boundary tolerances.
2026-09-30 18:35:58 -04:00
Clifford Yapp b435e292b2 Validate assembled mesh vertex links
Reject nonfinite or unused vertices, scale-aware zero-area faces, and
vertex links that are not one cycle before returning quality-path
success. Keep closed-edge incidence validation as the final topology
check.

Build each vertex link directly from incident triangle corners to keep
the certification pass linear in mesh size. Initialize standalone CDT
mesh state and cover valid, bow-tie, unused, and degenerate inputs.
2026-09-30 18:35:58 -04:00
Clifford Yapp 8fb43b8a81 Stop unproductive chart refinement
Track initial and best inconsistent-triangle counts across adaptive
rounds. Stop after four consecutive non-improving rounds and report
best and remaining counts, rather than allowing midpoint insertion to
amplify a failing triangulation through the full limit.
2026-09-30 18:35:58 -04:00
Clifford Yapp 6aa3a3fe99 Reject Steiner points outside face charts
Filter candidate interior samples against the chart outer loop before
passing them to detria. The existing boundary, hole, and duplicate
filters remain applied, so only samples in the represented trimmed
region can participate in triangulation.
2026-09-30 18:35:58 -04:00
Clifford Yapp df3597fbb1 Refine topology charts before failing
Retain adaptive points separately from initial surface samples so cone
charts can refine folded near-apex chords without reactivating
unsuitable native-UV samples. Evaluate face-local normals where native
UV is unambiguous.

Remove the boundary-sample triangle heuristic from mesh validity and
deduplicate roundoff-equivalent interior Steiner points. The actual
orientation, incidence, and geometric-degeneracy gates remain in force.
2026-09-30 18:35:58 -04:00
Clifford Yapp a1cdc7efbf Coordinate B-Rep edge normal refinement
Sample straight and curved shared edges against face-local normals.
Use a bounded orientation criterion when no normal tolerance is
requested. Both adjacent face charts now receive the samples needed
to avoid folded boundary triangles.

Record per-face failure diagnostics and make adaptive refinement
reports specific enough for corpus triage. Use deterministic reverse
UV mappings for regular-chart refinement and evaluate orientation
from face-local surface normals.
2026-09-30 18:35:58 -04:00
Clifford Yapp c45bdd9e12 Respect surface tessellation tolerances
Use the explicit face-scale tolerance for surface subdivision.

Keep shared-edge density from forcing excessive interior sampling.
2026-09-30 18:35:58 -04:00
Clifford Yapp e89653e374 Add topology-aware quality tessellation
Add topology-correct cone and polar charts and metric regular charts.

Make face orientation and refinement fail closed with bounded limits.

Extend quality audits and spatially index polygon preflight checks.
2026-09-30 18:35:57 -04:00
Clifford Yapp 54b41ac268 Introduce topology-correct cone charts
Map conical poles through explicit ruled wedge charts.
Constrain seam-safe fans and preserve B-Rep vertex identity.
Bound trim searches and cover transformed cones end to end.
2026-09-30 18:35:57 -04:00
Clifford Yapp ab4f5fff4d Harden constrained Delaunay triangulation
Compile detria under strict floating-point rules.

Validate and certify every constrained chart.

Propagate failure reasons and add adversarial numeric tests.
2026-09-30 18:35:57 -04:00
Clifford Yapp da414e23e1 Make quality B-Rep tessellation fail closed
Add structured diagnostics and transactional state reuse.

Make mesh export deterministic and conversion frontends fail closed.

Fix local trim, orientation, closest-point, and ownership defects.
2026-09-30 18:35:57 -04:00
Clifford Yapp 0f7fba5f8d Recover periodic B-Rep cut strips
Require repeated loop vertices to coincide in parameter space before
splitting touching subloops.  Stitch opposite-winding cuts on doubly
periodic surfaces in a universal cover before triangulation.

This completes every shaded face in the Leeuwenhoek corpus database.
2026-09-30 18:35:57 -04:00
Clifford Yapp 7d95ea711f Recover winding periodic B-Rep strips
Scale trim sampling from model-space edge extents and guarantee
angular samples for multi-turn periodic pcurves. Stitch matching
essential boundaries in the universal cover before triangulation.

This recovers the EMBER helical cylinder face.
2026-09-30 18:35:57 -04:00
Clifford Yapp 2abeb1d370 Recover bridged B-Rep inner loops
Split inner-loop Euler walks into simple hole cycles and discard their
zero-area connecting bridges. Accept declared-tolerance linear NURBS
pcurves when classifying degenerate slits.
2026-09-30 18:35:57 -04:00
Clifford Yapp 452a574d6a Ignore degenerate inner B-Rep loops
Factor zero-area classification down to individual loops and omit
degenerate inner constraints before triangulation. Preserve all outer
loop handling and add a face-with-slit regression.
2026-09-30 18:35:57 -04:00
Clifford Yapp dfc2a45d96 Recover untrimmed curved B-Rep faces
Build a domain boundary for any evaluable surface with one empty outer
loop, rather than limiting recovery to planar single-face databases.
Add a curved NURBS regression.
2026-09-30 18:35:57 -04:00
Clifford Yapp d3f4a8af77 Classify tolerant degenerate B-Rep loops
Check every span of piecewise-linear pcurves and honor bounded trim
tolerances when identifying zero-area slits. Treat faces with multiple
entirely empty loops as degenerate.

This clears the keycap and Spur Gear generation failures.
2026-09-30 18:35:57 -04:00
Clifford Yapp 9c3cf13748 Allow bounded periodic boundary backtracking
Accept one-turn periodic boundaries whose local converted-trim
excursions span up to 1.25 periods, while retaining a limit below a
second winding. Extend the split-boundary regression with an explicit
out-and-back subcycle.
2026-09-30 18:35:57 -04:00
Clifford Yapp dfd46f7807 Normalize split periodic B-Rep boundaries
Use trim topology to assign a consistent period to sampled pcurves
before closed-surface loop repair. Distinguish inter-trim seam jumps
from intentional full-period steps.

This recovers Rim face 98 and adds a split-boundary regression.
2026-09-30 18:35:57 -04:00
Clifford Yapp 4e088e4221 Skip zero-area closed B-Rep slits
Recognize reversed, coincident trim pairs on closed surfaces as true
zero-area slits while preserving shared-edge seams and full-period
boundaries. Add a cylindrical regression case.
2026-09-30 18:35:57 -04:00
Clifford Yapp c7ca85e6af Recover duplicate outer periodic B-Rep bands
Treat repeated outer-loop labels as recoverable metadata when periodic boundary geometry identifies a strip.  Keep the first declared outer as the reconstructed boundary.
2026-09-30 18:35:57 -04:00
Clifford Yapp a9263bcf0f Reject zero-area periodic B-Rep subloops
Require both sides of a touching-loop split to enclose meaningful parameter-space area.  This prevents periodic isocurves with roundoff area from being misclassified as holes.
2026-09-30 18:35:57 -04:00
Clifford Yapp c73d5d0107 Recover malformed periodic B-Rep strips
Repair periodic boundaries with corrupt winding, overlapping pcurves, or missing outer-loop labels.  Preserve exact period transitions and align cutouts with the reconstructed strip.

Keep non-owning surface proxy scratch scoped to its source B-Rep so batched drawing cannot retain freed geometry.
2026-09-30 18:35:57 -04:00
Clifford Yapp 5262ae183b Recover touching periodic B-Rep subloops
Preserve trim and edge identities on recursively sampled boundary
points. Initialize all trim point metadata to avoid indeterminate
topology fields.

Split periodic figure-eight loops at their shared topology vertex and
pass the smaller opposite-wound cycle to the Clipper sanitizer as a
hole.
2026-09-30 18:35:57 -04:00
Clifford Yapp 5fd66ec1f2 Recover missing B-Rep outer loop labels
Infer an outer loop only when no loop is labeled outer and the
largest nondegenerate UV polygon contains every other usable loop.
This recovers faces whose importer marked every loop as inner without
guessing across disconnected components.

Also rebuild a full periodic domain when one topologically closed
boundary is paired with an empty loop at the opposite surface limit.
Add planar, periodic, and empty-boundary regressions.

This restores all faces in both Big Boy gear-motor variants.
2026-09-30 18:35:57 -04:00
Clifford Yapp 194d277da7 Recover collapsed B-Rep hole pcurves
Detect closed singleton pcurves whose lifted spatial extent collapses
relative to their valid 3D edge. Pull the edge back before
triangulation and preserve its declared trim direction.

Unwrap noncontractible boundary pairs onto a common periodic seam.
Use those boundaries as the outer polygon even when imported loop
classification assigns a contractible cutout as the outer loop.

Add regressions for collapsed circular holes and misclassified
periodic boundaries. This recovers all 1,309 shaded faces in the SD40
cooler object.
2026-09-30 18:35:57 -04:00
Clifford Yapp b731d2c493 Recover multi-edge periodic B-Rep bands
Reconstruct periodic bands whose closed boundaries are divided across
one or more trims. Require connected topology and post-pullback
coverage of one complete period at both boundary isocurves.

Accept a mixed single/paired boundary only when two shared seam trims
join the same parameter levels. Add regression coverage for both
paired and mixed boundary encodings.

This eliminates the remaining shaded failures in the Woden database.
2026-09-30 18:35:57 -04:00
Clifford Yapp 278fe1027d Recover topologically closed B-Rep bands
Treat near-coincident loop endpoints as closed when their trims share
a B-Rep vertex and their UV and 3D gaps satisfy the model tolerances.
This removes microscopic closure edges that invalidate constraints.

Reconstruct periodic strips encoded as two complementary edge pairs
and a shared seam.  Require the post-pullback samples at both strip
boundaries to corroborate one complete surface period.

Add regressions for imprecise planar closure and paired periodic arcs.
This recovers the speaker face and all nine missing military-truck
faces without broadening recovery to arbitrary malformed loops.
2026-09-30 18:35:57 -04:00
Clifford Yapp 679085a233 Recover periodic B-Rep face domains
Reconstruct full closed-surface domains when imported trims collapse
onto seams or periodic isocurves.  Preserve ordinary holes, seed the
unwrapped domain, and limit repairs to corroborating topology.

Use bounded NURBS evaluations for revolution-surface audit boxes and
make face-index audits compare against the selected face.  Add
regressions for holes, singular domains, and collapsed annular trims.

This recovers all shaded faces in the 41-face Woden, 49-face KF-21,
and 94-face shell-pressing samples.
2026-09-30 18:35:57 -04:00
Clifford Yapp 7f302f2a36 Bound skinny B-Rep surface sampling
Refine surface interiors only when distance or normal error requires it.
Avoid filling geometrically flat strips with isotropic parameter cells.
This kept detria from processing hundreds of thousands of unnecessary
points.

Add a single-face audit selector and a regression for extreme planar
aspect ratios.
2026-09-30 18:35:57 -04:00
Clifford Yapp f900cff7ee Recover full-domain B-Rep faces
Reconstruct full periodic faces whose seam trims collapse in UV, seed
bounded interior samples, and preserve singular pole positions.

Use planar surface domains when a sole loop has no trims. Recognize
non-periodic collinear multi-trim loops as zero-area geometry.

Add focused regression coverage for each recovered topology.
2026-09-30 18:35:57 -04:00
Clifford Yapp e7c1634d69 Recover triangulation and batch process failures
Reject illegal Delaunay flips before evaluating the incircle predicate
and return recoverable detria invariant failures to callers.

Retry a died batch task once in a fresh process so accumulated heap
pressure does not skip otherwise valid geometry.
2026-09-30 18:35:57 -04:00
Clifford Yapp e02cbda1ba Batch B-Rep corpus audits by database
Open each database once while preserving per-object watchdogs,
restart points, and resource controls.

Report exact failed, skipped, and unprocessed face, edge, and surface
cue indices so corpus failures can be investigated directly.
2026-09-30 18:35:57 -04:00
Clifford Yapp ceea1de1a9 Recover malformed fast B-Rep face loops
Retain topology-minimum samples for thin nonlinear boundaries.
Rebuild outlier pcurves from validated 3-D edge pullbacks, then
reconstruct singular caps and periodic strips in a common UV cover.

Classify exact zero-area line faces as completed skips and expose
that count in audit reports. Add synthetic regression cases for each
recovery path.
2026-09-30 18:35:57 -04:00
Clifford Yapp 90ebcfb82f Recover malformed B-Rep trim triangulations 2026-09-30 18:35:57 -04:00
Clifford Yapp b1f96d992b Sanitize constrained polygon triangulation 2026-09-30 18:35:57 -04:00
Clifford Yapp 4698a1dbfa Cache B-Rep display sampling metrics
Hoist model bounds and surface scales out of face recursion.

This removes quadratic bounds work and allocator contention.
2026-09-30 18:35:57 -04:00
Clifford Yapp 82b1ad1ce7 Add isolated B-Rep corpus audit runner
Run each database object and drawing mode in a separate bounded process
and append JSONL results immediately.  Support resumable runs, wall and
address-space limits, temporary crash logs, and small sampling passes.
2026-09-30 18:35:57 -04:00
Clifford Yapp a73140d202 Use bounded CDT for shaded B-Rep plots
Route standard shaded VList generation through the deterministic fast
mesh core.  Guard seam and surface sampling progress, own all per-face
temporary data, catch allocation failures, and verify exact VList
assembly.
2026-09-30 18:35:57 -04:00
Clifford Yapp 3633954d6d Parallelize bounded B-Rep wireframes
Sample independent B-Rep edges in bounded worker jobs and assemble
VLists in topology order.  Add resource-aware surface cues,
diagnostics, loop guards, and deterministic regression coverage.
2026-09-30 18:35:57 -04:00
Clifford Yapp 61e0efc9b6 Bound parallel B-Rep display tessellation
Keep fast face sampling local to each worker and leave the input B-Rep
unchanged.  Add deterministic assembly, resource controls, per-face
diagnostics, audit options, and regression coverage.
2026-09-30 18:35:57 -04:00
Clifford Yapp dd7b4de256 Correct B-Rep drawing audit modes
Measure rt_brep_plot as wireframe generation and allow each drawing
path to be audited independently.  Keep sparse edge-only wireframes
from failing shaded-mesh extent checks.
2026-09-30 18:35:57 -04:00
Clifford Yapp d179622873 Update detria.hpp, add plan 2026-09-30 18:35:57 -04:00
Clifford Yapp 5d3a20f778 Extract logic specific to CDT work from old branch 2026-09-30 18:35:57 -04:00
Clifford Yapp 6e48ea9ddd Center README toplevel text 2026-09-30 06:44:36 -04:00
Clifford Yapp 488f538040 Update bext SHA1 to latest 2026-09-28 08:46:34 -04:00
Clifford Yapp 17cc9f3a5a List file for distcheck 2026-09-25 17:31:09 -04:00
Clifford Yapp bf2c508826 List cases.inc for distclean 2026-09-25 15:55:30 -04:00
Clifford Yapp f2e68b1a2a Initialize 2026-09-25 15:42:29 -04:00
Clifford Yapp 5e873e5081 Fix MGED live view panning and solid edit lookup
View-mode mouse panning now honors view coordinates even when MGED is
set to object coordinates. Correct the solid-edit lookup so live
geometry queries work during dragging.

Exercise numeric live and saved geometry for primitive, combination,
ARB, pipe, BoT, BREP, ARBN, and raster edits. Cover menu selection,
mode switching, repeated mouse edits, and BoT triangle picking under
Xmin.
2026-09-25 14:17:01 -04:00
Clifford Yapp 3beed70eb8 Fix MGED mouse edit modes and interactive feedback
Middle-click rotation and some Edit menu choices could move the view
or leave solid editing in a stale mode. Keep edit flags and modes
aligned so mouse actions change geometry, including snapped drags.

Defer button release while a pick is running to avoid a background
command-busy error. Extend MGED and Xmin regressions to verify
primitive mouse edits, live redraw, menu modes, and picking.
2026-09-25 17:00:00 +00:00
Clifford Yapp 73258d4264 Fix MGED interactive edit redraw and mouse routing
Object-edit transforms now update cached display matrices and mark
views dirty, so geometry moves visibly without a manual refresh.
Synchronize transient solid-edit flags with librt edit mode so mouse
and rate controls manipulate the solid instead of the view.

Add an Xmin mouse regression covering object and solid pan/spin,
direct transforms, redraws, and unchanged view state.
2026-09-25 17:00:00 +00:00
Clifford Yapp 8ae7482918 Gate librt rtcmp capture with runtime toggle 2026-09-25 12:25:44 -04:00
Clifford Yapp 917ce5554c Preserve primitive edits across failed saves and restores
A failed checkpoint could discard the last saved state, and a
failed revert could discard the current primitive. Restoring
selected pipe or NMG geometry could also leave stale handles.

Stage exports and imports before replacing live state, clear
selections and refresh the keypoint after restore, and preserve
SPH and REC types. Consume rejected edit input so a later command
cannot reuse it, and prevent view-update counts from accumulating.

Cover failed saves and restores, repeated revert, selected geometry,
and default and non-default database units.
2026-09-24 14:52:59 -04:00
Clifford Yapp 58ab0d5496 Prevent rejected primitive edits from changing geometry
Rejected height moves in TGC, CLINE, and EXTRUDE could replace
valid geometry; NMG edge moves could partly change faces while
reporting success. BOT edge splits could leave unused vertices, and
malformed parameter text could partly overwrite seven primitives.

Stage edits before applying them. Reject invalid REC and uniform
scale inputs without changing geometry, and report scale failures.
Cover these paths in default and non-default units, including CRLF
parameter text and NMG selection remapping.
2026-09-24 14:52:59 -04:00
Clifford Yapp 13b0699349 Prevent rejected ARB edits from corrupting geometry
Mouse face drags and several ARB edit helpers could change points or
planes before detecting invalid geometry. Subsequent edits then began
from inconsistent state and could produce unexpected shape changes.

Validate candidate geometry before committing it, restore state on
rejected edits, and cover repeated mouse drags with non-default units.
2026-09-24 14:52:59 -04:00
Clifford Yapp acd3a9800d Use bu_vls for capture records 2026-09-24 18:51:55 +00:00
Clifford Yapp 03b36c1b61 Restore MGED command prompt focus on GUI startup 2026-09-24 18:48:57 +00:00
Clifford Yapp b714d88185 See if we can optionally also write out pre-boolweave segment data for comparison. 2026-09-24 17:45:40 +00:00
Clifford Yapp eff3fea479 Add LIBRT level rtcmp debug printing option 2026-09-24 16:25:34 +00:00
Clifford Yapp 7358e90b54 Keep temporary files binary on Windows
Set binary mode on the temporary file descriptor before wrapping it
in a stream. This prevents newline translation from enlarging DSP
sample files, which otherwise makes DSP edits and raster operation
tests fail on Windows.

Add a byte-for-byte temporary-file regression test covering newline
and DOS EOF bytes.
2026-09-24 14:30:55 +00:00
Clifford Yapp 3c2b0de9d2 Fix unit-sensitive primitive edits and invalid edit reporting
Keep numeric edit values in database-local units while applying
geometry changes in base units. Correct parameter readers and edit
paths that could misplace solids or silently accept invalid changes,
including ARB, ARBN, BOT, pipe, sketch, metaball, and NMG.

Add mm/in operation matrices with exact-result checks across
primitive edits, parameter parsing, and repair paths.
2026-09-24 11:08:20 +00:00
Clifford Yapp a384f838fb Reject invalid BREP control-vertex edits and verify units
Propagate selection and CV edit errors through librt so callers do not
report rejected edits as successful. Validate indices and coordinates
before changing geometry, and mark selection indices as unitless.

Exercise persisted CV moves and sets in an inch-unit GED database,
and verify rejected commands leave stored geometry unchanged.
2026-09-24 02:03:12 +00:00
Clifford Yapp 1053594e9a Fix cursor state and BREP control-vertex mouse edits
Preserve cursor-relative translation knobs for ARB, BOT, CLINE,
extrusion, NMG, and TGC mouse edits, including in non-mm
databases. Prevent BREP control-vertex drags from moving the
whole solid. Report invalid ARB edit indices and geometry
failures to callers.

Add mouse and error-path regressions for the affected primitives.
2026-09-24 01:36:28 +00:00
Clifford Yapp 72cde263cc Fix ARB face rotation errors and endpoint drag state
ARB face rotation changed geometry but reported failure, causing GED
to reject the edit. Return success and let the shared edit path replot.

CLINE and extrusion mouse endpoint drags also reset translation
knob state. Keep endpoint moves separate from translation handling
and cover these workflows in primitive and GED edit tests.
2026-09-24 01:00:50 +00:00
Clifford Yapp b670a19aba Preserve local-unit inputs during primitive editing
Repeated edits in non-mm databases could compound unit conversions,
moving points and faces or resizing primitives unexpectedly. Mouse
coordinates could also be converted a second time.

Convert numeric values into temporary base-unit targets in librt
without rewriting the supplied parameters. Cover translation,
dimension changes, vertex and face moves, picks, and NMG extrusion
with non-mm and repeated-input tests.
2026-09-24 00:37:03 +00:00
Clifford Yapp 2d9fa5c033 Fix non-mm primitive edits and invalid scale handling
Numeric scale lengths now convert once in librt, so edits in
non-mm databases produce the requested geometry and repeated
inputs do not compound conversions. Cover torus, ellipsoid,
superellipsoid, particle, RPC, RHC, ETO, and TGC scaling,
plus TGC endpoint moves.

Reject impossible torus, ETO, and coupled TGC edits without
changing the solid. Add inch-unit, repeat-input, and rejection
coverage in librt and libged.
2026-09-23 23:59:19 +00:00
Clifford Yapp 8be0f0ae85 Fix EPA/EHY length edits and lock in ARB edit behavior
EPA and EHY length edits now convert local units without changing the
input, so repeating an edit cannot apply the conversion again. Invalid
semiaxis requests now fail visibly and leave the primitive unchanged;
empty parameter edits remain no-ops.

Add inch-unit MGED extrusion checks for both direct and interactive
editing, exercise ARB geometry helpers, and run multiline MGED tests
as sourced scripts so failures cannot be skipped.
2026-09-23 23:04:29 +00:00
Clifford Yapp 00ffa4bd80 Fix VOL edits with non-default database units
Convert voxel cell sizes to base units without modifying input
parameters, so repeated edits use the requested size. Keep file
sizes and density thresholds unitless, validate invalid dimensions,
and prevent threshold underflow and file-size overflow.

Cover direct librt edits and persisted GED edits in inch databases.
2026-09-23 22:27:58 +00:00
Clifford Yapp e5e0905d03 Fix pipe editing units, zero dimensions, and failures
Pipe point coordinates and dimensions entered in non-mm databases were
not consistently converted to base units. Some commands reconverted
their input on repeat, while invalid point operations could report
success without changing the pipe.

Convert numeric input once in librt, preserve base-unit mouse points,
and propagate operation failures. Reuse shared pipe point helpers and
allow all-zero pipe dimensions to receive an absolute value. Add
direct and GED tests for inch units, repeated edits, failures, and
database persistence.
2026-09-23 21:55:41 +00:00
Clifford Yapp 10d13793b0 Fix revolve sketch edits, error reporting, and ownership
Changing a revolve's sketch now loads the new geometry and rejects
missing or non-sketch references without persisting a broken object.
Invalid numeric edits report errors instead of appearing to succeed.

Transforms retain the start vector, angle, and sketch. Imported and
prepared objects own independent sketches and names, avoiding stale
geometry, leaks, and dangling references.

Cover inch-database edits, sketch replacement, transform ownership,
and prep/free lifecycles in edit tests.
2026-09-23 21:26:37 +00:00
Clifford Yapp 70d5180438 Fix sketch and extrusion edits and report invalid operations
Honor indexed sketch descriptor inputs and convert vertex and radius
lengths in non-mm databases. Replace extrusion sketch references only
after the new sketch is validated, so failed selections leave the
existing extrusion intact.

Return descriptor edit failures to GED instead of reporting success.
Remove two unimplemented TGC moves from advertised operations and
strengthen persisted sketch, extrusion, HRT, and pipe tests.
2026-09-23 20:55:57 +00:00
Clifford Yapp 50fe626694 Fix NMG edge deletion and strengthen edit operation checks
Deleting an NMG edge could also split another edge when stale edit
parameters were present. Stop after deletion and release temporary
extrusion loops on mode changes and reset.

Exercise NMG selection, traversal, edge edits, and extrusion using
non-mm input. Check that DSP file selection and sample counts are
independent of length units, sharing the filename test callback.
2026-09-23 20:11:14 +00:00
Clifford Yapp ca403c92ed Correct native primitive edits and broaden result coverage
Keep combination edit sessions valid after database writes, move BOT
plate thickness edits into librt, and repair ARS topology changes.
Correct unit-sensitive edits, malformed-input handling, and NMG
imports on unaligned data so geometry edits remain reliable.

Add primitive-operation inventory, outcome and inch-unit tests, and
bounded edit-sequence coverage. Use portable temporary data files
so DSP and EBM tests no longer require /tmp.
2026-09-23 19:49:07 +00:00
Clifford Yapp bab959474f Add Clang version of coverage tracking compilation flags 2026-09-23 19:49:07 +00:00
Clifford Yapp 27b522c155 Updates for OpenCV v5 2026-09-23 16:50:52 +00:00
Clifford Yapp 875231c335 Consolidate librt primitive edit tests into one executable
Run all 34 edit cases through a selectable rt_edit_test binary.
Register each case independently with CTest so failures remain
isolated and previously unregistered tests run routinely.
2026-09-23 16:01:04 +00:00
Clifford Yapp 89810b06ca Correct non-mm primitive edits and expand regression coverage
Keep MGED and gsh edits consistent in non-mm databases by fixing
length and dimensionless parameter handling in librt and libbrep.
Correct DSP absolute cell sizing and inverse transforms, preserve
rational BREP control-vertex weights, and retain NMG session units.

Add non-mm tests for affected primitives and update stale ARB8
descriptor and HRT translation assertions.
2026-09-23 16:01:04 +00:00
Clifford Yapp 90e512b5d6 Fix non-mm units across primitive edit commands
MGED tra moved primitives too far by passing a base-unit target
to handlers expecting local units. The libged edit commands used
by GSH mixed local numeric positions with base-unit keypoints.
This affected tra, absolute and relative translate, and rotation
and scale centers in non-mm databases.

Convert translation targets at the librt edit boundary and parse
numeric positions in base units. Keep scale reference factors
dimensionless and add inch-database regression coverage.
2026-09-23 16:01:04 +00:00
Clifford Yapp 19fe382208 Revert "Add opt-in forward parity shots for solid BoTs"
This was just an experiment - remove.

This reverts commit 90e5ede2b3.
2026-09-23 10:00:05 -04:00
Clifford Yapp 994c56b05f typo, ws 2026-09-23 09:34:21 -04:00
Christopher Sean Morrison db76164ea8 update docs with restored behavior and comparison with bwdiff 2026-09-23 07:36:04 -04:00
Christopher Sean Morrison 58c5ed3681 pixscale and bwscale improved 2026-09-23 07:35:28 -04:00
Christopher Sean Morrison 05bd5c7470 restored pixdiff 2026-09-23 07:34:02 -04:00
Christopher Sean Morrison 2276f4ef8c replaces seek-based scanline loading with a forward rolling buffer so large piped inputs behave like regular files. 2026-09-23 07:32:55 -04:00
Christopher Sean Morrison 188e72eb32 maps the first and last output pixels exactly onto source edges and clamps interpolation neighbors so enlargements retain every corner 2026-09-23 07:32:55 -04:00
Christopher Sean Morrison 03306043ea scaling test 2026-09-23 07:32:55 -04:00
Christopher Sean Morrison 28038fe0ea isolated pixdiff pix test 2026-09-23 07:32:55 -04:00
Christopher Sean Morrison 5b5cb8c538 now bw mode need -b option 2026-09-23 07:32:55 -04:00
Christopher Sean Morrison 1c20d18156 restore previous pixdiff behavior where we apply an ntsc-grayscaling damper on same pixels so differences are accentuated with color 2026-09-23 07:32:55 -04:00
Clifford Yapp 97920fbc90 license info 2026-09-21 22:07:58 -04:00
Clifford Yapp 37fb7d928a Not flipping these to const yet. 2026-09-21 22:06:05 -04:00
Clifford Yapp 2056b359bd Use EQUAL/ZERO 2026-09-21 22:05:39 -04:00
Clifford Yapp b4cf00292a ged_cmd_valid is deprecated, so don't need a test for it. 2026-09-21 20:08:03 -04:00
Clifford Yapp 6748dd37ab libged: restore ged_cmd_valid compatibility wrapper
The 2024 command API migration replaced its implementation with
ged_cmd_exists and ged_cmd_same, but retained the deprecated exported
ged_cmd_valid declaration.  This left source callers compiling against a
symbol that libged did not provide.

Restore the wrapper with its established status mapping and add regression
coverage for valid, alias, mismatch, invalid, and NULL command inputs.
2026-09-21 20:06:52 -04:00
Clifford Yapp ef35c1dca1 fix bu_scan_fastf_t delimiter parsing
Replace dynamically constructed scanf formats with direct numeric and
delimiter parsing.  The old implementation accepted only one literal
delimiter sequence, mishandled percent delimiters, and used an
uninitialized length when scans failed.

Define empty delimiter strings as invalid, reset optional consumed-count
outputs for invalid configurations, and document the delimiter and
destination-pointer contract.  Add regression coverage for repeated,
mixed, and percent delimiters, deterministic prefix counts, null
destinations, and invalid configurations.
2026-09-21 20:06:30 -04:00
Clifford Yapp 8cdedd8752 libbn: correct polynomial edge cases
Correct descending-order polynomial API documentation and make basic
arithmetic helpers safe when their output aliases an input.  Preserve
the output when multiplication exceeds the fixed degree limit.

Handle constant and higher-degree divisors without unsigned degree
underflow.  Normalize small nonzero leading coefficients before the
direct quadratic, cubic, and quartic solvers.

Repair the unreferenced bivariate polynomial implementation: expose its
API, add release support, preserve both dimensions while growing, and
correct its addition and convolution.  Add focused regressions for all
corrected cases.
2026-09-21 18:40:54 -04:00
Clifford Yapp e4ba0b166c libbn: correct matrix and quaternion edge cases
Use bn_tol's angular predicate in bn_mat_fromto instead of the
duplicated line-segment ratio test, which compared dimensionless
slopes against a distance tolerance.  Normalize inputs and return an
identity matrix when either cannot be normalized.

Make bn_mat_lookat support non-unit and Z-axis directions.  Correct
bn_wrt_point_direc's translation-back sign and replace its shared
scratch storage with local temporaries.

Define quaternion behavior for antipodal bisection and identity
logarithms.  Document related angle and input contracts, and add
regressions for the corrected cases.
2026-09-21 18:28:12 -04:00
Clifford Yapp 586ac06c7e libbg: correct geometry query contracts
Normalize point/normal planes without mutating caller input, and
correct coordinate frames for non-unit plane equations.

Support rank-deficient PCA input, correct 3D polygon centroids and
CCW ordering, honor polygon holes in view bounds, and gather every
mesh diagnostic while preserving the no-diagnostics fast path.

Fix Creo's inverted solidity result, make segment clipping reentrant,
and clarify geometry API contracts.  Add focused plane, PCA, polygon,
and trimesh regressions.
2026-09-21 18:17:12 -04:00
Clifford Yapp 0d745fca35 Use bu_vls for VFONT paths
The VFONT loader used a 128-byte buffer when constructing the bundled
font path. Odd-pathname distcheck directories exceeded that limit in CI
environments, causing fblabel to reject the default font and all
dependent rtwizard tests to fail. Use a bu_vls path so the installed
font can be opened regardless of build prefix length, and release it on
all loader exits.
2026-09-18 16:39:52 -04:00
Clifford Yapp 5270e67abe List files for distcheck 2026-09-18 15:47:02 -04:00
Clifford Yapp 14051845a3 facetize: add source-guided adaptive SPSR
Fixed-size point dumps make Poisson reconstruction unreliable as a
last-resort facetization method.  They can miss thin partitions while
oversampling surfaces that are already well resolved, and an arbitrary
point count does not show whether source characterization is complete.

Seed reconstruction with stabilized Crofton samples.  Retain paired
entry and exit normals, partition thickness, and ray identities so thin
geometry contributes explicit local information instead of unrelated
points.

Add a bounded adaptive libbg driver around the existing upstream
Poisson solver.  Each solve reports residual, density, normal, and
surface-variation hints to a caller callback.  Facetize responds with
exact source rays and validates partitions, endpoints, chord length,
area, volume, and closed-manifold topology before accepting a result.

Bound refinement by time, point, pass, and convergence limits.  Relax
only chord and thin-partition thresholds when the source BoT is already
known to be non-manifold.  Deprecate the fixed-array API, remove the
intermediate point-sampling path, adapt pnts callers, and add focused
coverage.  The vendored SPSR implementation remains unchanged.

Use configured method priority for perturbation variants so an MDC-only
run never invokes NMG.  Report finalization progress and retry counts
while the fallback hierarchy is evaluated.
2026-09-18 16:14:16 +00:00
Clifford Yapp d6fa937212 libanalyze: replace marching cubes with adaptive MDC
The legacy marching-cubes polygonizer sampled a uniform grid and could
miss thin or high-aspect features, merge undersampled tunnels, and emit
surfaces whose shared-edge winding was inconsistent.  Increasing the
global resolution made those failures prohibitively expensive rather
than addressing their local causes.

Add a ray-driven manifold dual contouring API built on an adaptive
octree.  Cache exact signs and Hermite edge crossings from librt
partitions, place feature-preserving vertices with bounded QEF solves,
and refine locally for thin walls, multiple crossings, interior
surfaces, and ambiguous topology.  Build faces from shared crossings
with deterministic winding and require closed manifold output.

Back analyze_polygonize with the new implementation, expose MDC as a
facetize method, and remove the obsolete polygonizer code, licensing,
continuation sampler, and CM default.  Keep NMG available when polygon
preservation is more important and it can successfully process the
input.

Document the Ju, Schaefer, and Warren algorithmic basis and the
bottom-up Euler-characteristic escalation path.  Add coverage for thin
walls, tunnels, overlapping CSG, toroidal cases, winding, resource
limits, and the compatibility polygonalize entry point.
2026-09-18 16:14:16 +00:00
Clifford Yapp 37aee260c1 facetize: retire the legacy pipeline and harden region results
The compatibility implementation duplicated command behavior and
retained unreliable paths that had been superseded by the planned
worker pipeline.  Keeping both implementations made option handling and
failure behavior needlessly difficult to reason about.

Remove the old facetize sources and MGED registration so all command
modes use the maintained orchestrator.  Preserve implicitly selected
region roots in the output database and keep region retries scoped to
validated perturbation inputs.

Tighten source and result validation, reject empty preparation results,
and suppress expected import or preparation diagnostics only inside a
scoped log hook.  Keep routine details out of the terminal summary and
write complete detail logs when concise reporting is not possible.

Extend regression and validation coverage for implicit roots, tolerated
failures, and malformed or unsupported inputs.
2026-09-18 16:14:16 +00:00
Clifford Yapp 56d64428ae facetize: separate orchestration and isolate NMG evaluation
The command implementation mixed option handling, mode selection,
database setup, scheduling, and result publication.  NMG Boolean work
also ran in the parent process, allowing known NMG failures to terminate
the overall conversion.

Introduce an explicit planning and orchestration layer so each command
mode follows one visible execution path.  Replace positional worker
arguments with typed request and result messages that are validated
before they can alter the working database.

Move NMG Boolean evaluation into the supervised subprocess framework.
Transfer trees and BoTs through the existing transaction protocol,
report failures without killing the parent, and reject primitives that
do not provide the required tessellation callback.

Update facetize regression and worker-protocol coverage for the new
boundaries and failure semantics.
2026-09-18 16:14:16 +00:00
Clifford Yapp 0f6db7016d facetize: add supervised parallel primitive workers
Primitive tessellation previously paid process and database setup
costs per object and left the parent with limited visibility into work
that timed out or died.  Large models consequently spent substantial
time outside the tessellation algorithms themselves.

Add long-lived supervised workers and a bounded scheduler for
independent objects.  Divide CPUs between worker processes and each
worker internal parallelism, reuse opened databases, and retain cache
state across requests.

Make result publication transactional.  A WRITE_STARTED acknowledgment
closes the READY and PROCEED race, while the parent detects timeouts and
abnormal exits, restarts workers, and records only completed object
results.  Reuse temporary result databases without leaking state between
requests.

Add process and database-transfer helpers together with worker,
recovery, and validation tests.  Document the parallel job control and
its resource behavior.
2026-09-18 16:14:15 +00:00
Clifford Yapp cbffc1171b libbu: support resource-aware worker processes
Large subprocess pools need to divide machine resources without allowing
each worker internal parallelism to consume every CPU.  Add a
process-local limit to bu_avail_cpus() and bu_parallel(), with atomic
updates and coverage for applying and removing the limit.

Make BU_MEM_AVAIL count FreeBSD reclaimable inactive and cache queues
in addition to immediately free pages.  This prevents idle systems from
appearing memory-starved to resource-aware algorithms and adds native
sysctl checks for the reported values.

Rename the private process.h to process_private.h.  This prevents the
source header from shadowing the MSVC CRT header used by std::thread,
which otherwise leaves _beginthreadex undeclared.
2026-09-18 16:14:15 +00:00
383 changed files with 94625 additions and 20328 deletions
+32 -17
View File
@@ -231,7 +231,7 @@ set(CMAKE_FIND_USE_PACKAGE_REGISTRY FALSE)
# Prepare to manage and build BRL-CAD's external dependencies. Specifies a
# specific commit hash to use in the 'bext' repository.
include(BRLCAD_ExternalDeps)
brlcad_bext_init(12115e6c5922e2db27a9b88e00dd714af5315f69)
brlcad_bext_init(0682795bcfe34644c17e5a4bdd0225c76f6bdc98)
#---------------------------------------------------------------------
# Define various utilities.
@@ -678,8 +678,13 @@ endif(MSVC)
# before use so platforms without GNU/LLVM-style linker options are skipped.
brlcad_check_linker_flag(C "LINKER:--no-undefined" BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND)
brlcad_check_linker_flag(CXX "LINKER:--no-undefined" BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND)
if(BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND AND BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND)
if(BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND AND BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND
AND NOT BRLCAD_ENABLE_ADDRESS_SANITIZER)
set(BRLCAD_LINKER_NO_UNDEFINED_FLAG "LINKER:--no-undefined" CACHE STRING "Linker flag requiring resolved shared library symbols" FORCE)
else()
# Clang links the AddressSanitizer runtime into executables, leaving its
# shared-library instrumentation symbols unresolved until then.
set(BRLCAD_LINKER_NO_UNDEFINED_FLAG "" CACHE STRING "Linker flag requiring resolved shared library symbols" FORCE)
endif()
brlcad_check_linker_flags(C BRLCAD_LINKER_WHOLE_ARCHIVE_GROUP_C_FLAG_FOUND "LINKER:--whole-archive" "LINKER:--no-whole-archive")
brlcad_check_linker_flags(CXX BRLCAD_LINKER_WHOLE_ARCHIVE_GROUP_CXX_FLAG_FOUND "LINKER:--whole-archive" "LINKER:--no-whole-archive")
@@ -1642,21 +1647,31 @@ if(NOT MSVC)
endif(BRLCAD_WARNINGS OR BRLCAD_ENABLE_STRICT)
if(BRLCAD_ENABLE_COVERAGE)
# TODO: These seem to GCC specific flags - should probably set up for clang
# as well if that's our compiler...
# https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
# If we need different settings for different tools, should
# encapsulate each tool's setup in a function
check_c_flag(ftest-coverage)
check_cxx_flag(ftest-coverage)
if(FTEST_COVERAGE_C_FLAG_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
else(FTEST_COVERAGE_C_FLAG_FOUND)
message(SEND_ERROR "Building with coverage is not supported by BRL-CAD on this platform.")
endif(FTEST_COVERAGE_C_FLAG_FOUND)
if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
check_c_flag(fprofile-instr-generate)
check_cxx_flag(fprofile-instr-generate)
check_c_flag(fcoverage-mapping)
check_cxx_flag(fcoverage-mapping)
if(NOT FPROFILE_INSTR_GENERATE_C_FLAG_FOUND OR
NOT FPROFILE_INSTR_GENERATE_CXX_FLAG_FOUND OR
NOT FCOVERAGE_MAPPING_C_FLAG_FOUND OR
NOT FCOVERAGE_MAPPING_CXX_FLAG_FOUND)
message(FATAL_ERROR "Clang source-based coverage is not supported by this toolchain.")
endif()
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate)
else()
check_c_flag(ftest-coverage)
check_cxx_flag(ftest-coverage)
if(FTEST_COVERAGE_C_FLAG_FOUND AND FTEST_COVERAGE_CXX_FLAG_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
else()
message(FATAL_ERROR "GCC coverage is not supported by this toolchain.")
endif()
endif()
endif(BRLCAD_ENABLE_COVERAGE)
if(BRLCAD_ENABLE_STRICT)
+2
View File
@@ -13,6 +13,8 @@ changes made. See document footer for additional details.
--- 20XX-XX-XX Release 7.46.0 ---
----------------------------------------------------------------------
* improved pixscale & bwscale scaling and image piping - Sean Morrison
* restored pixdiff behavior on .pix, added -b for .bw - Sean Morrison
* updated MGED and Archer for Tcl 8.6.18 and Itcl/Itk 4 - Cliff Yapp
* fixed writes to fileless geometry databases - Cliff Yapp
* fixed nastran-g handling of large property IDs - Rupesh, Cliff Yapp
+3 -3
View File
@@ -1,6 +1,6 @@
BRL-CAD
Release 7.44.2
http://brlcad.org/
BRL-CAD
Release 7.44.2
http://brlcad.org/
BRL-CAD is a powerful cross-platform open source combinatorial
solid modeling system that includes an interactive 3D solid geometry
-246
View File
@@ -1,246 +0,0 @@
Annotation and view-object rendering notes
==========================================
Scope
-----
BRL-CAD has two related, but deliberately distinct, ways to represent visual
markup:
* An annotation is an ID_ANNOT database primitive. It is persistent model
content with enough semantic and presentation information to be reproduced
outside the application that created it.
* A view object is application-owned scene state. It is useful for interactive
construction and manipulation, but is intentionally not backed by a database
object.
They share portions of the interactive display infrastructure, but they do not
currently have interchangeable ownership, lifetime, or rendering semantics.
Annotation capabilities
-----------------------
An rt_annot_internal stores a model-space anchor, a set of 2D vertices, and
either a model plane basis or screen-space placement. Its segment types cover
lines, circular arcs, NURBS, Beziers, text, and filled polygons. Segment style
records include a semantic role, line pattern and width, RGBA color, font or
symbol, text scaling, and decorative flags.
The libged annotate command can create styled text, leaders, linear/angular/
radius/diameter/ordinate dimensions, and bounding-box autodim groups. It can
regenerate derived annotations from saved attributes and associate them with
source model objects without making them part of those objects' CSG trees.
Annotations therefore have the properties needed for persistent markup:
* database naming, paths, transforms, and instances;
* survival across save/reopen and transfer to another application;
* semantic segment roles and per-segment presentation data;
* model-space and view-dependent screen-space placement; and
* high-level generation metadata and source-object associations.
An annotation is informational coverage, not a CSG solid. The raytracing
implementation preserves that distinction. It prepares annotation coverage
from active database paths and composites it on primary samples after ordinary
geometry has established color and depth. Model-space annotation coverage is
occluded by nearer solid geometry. Screen-space annotation coverage uses the
render view and image dimensions. Annotations do not acquire artificial
volume, alter model bounds, cast shadows, or participate in reflection and
refraction rays.
Visible coverage is returned in back-to-front layers. Model-space layers are
ordered by ray distance, followed by view-space layers ordered by projected
anchor depth; segment draw order breaks ties within an annotation. This lets
the image renderers apply source-over alpha consistently while retaining the
single-frontmost-hit query for compatibility. NIRT continues to report one
frontmost segment per model-space annotation rather than exposing these
presentation layers as solid partitions.
Color follows normal database tree state. An annotation segment with an
explicit RGBA style wins; otherwise it inherits the resolved combination-path
color; otherwise it is white. The legacy region-ID color table is not applied
to annotation display or coverage, since annotations are informational leaves
rather than regions.
Prepared coverage uses the existing libbg RTree implementation. A 3D RTree
culls model-space annotation instances and a 2D local RTree culls segments
inside every annotation. Screen-space instance extents cannot be placed in a
static world-space index because they change with the view, so those instances
remain in a compact list while still using their local segment RTree. This
avoids a new spatial data structure and keeps prepared scenes immutable and
safe for concurrent sample queries.
The rt, ART, and ADRT/ISST integrations follow this overlay model. ERT invokes
rt with the database, displayed database paths, and view, so database-backed
annotations can follow that route as well. ERT does not currently serialize
or pass application-owned view objects to its subprocess.
NIRT keeps annotation diagnostics opt-in. `useannot 1` enables model-space
annotation intersections, and the `fmt a` action reports them. Default NIRT
output is unchanged. Screen-space annotations are deliberately omitted
because a NIRT ray does not define an image size, pixel footprint, or complete
model-to-view mapping.
View-object capabilities
------------------------
The core representation is bv_scene_obj. A view set owns shared view objects,
and a particular bview can also own local objects. Independent views force
local ownership. bv_obj_put recycles objects when their application lifetime
ends; the objects are not written to the .g database.
The `view obj` command exposes the principal markup types:
* Lines are model-coordinate polylines represented by vlists. Generic scene
rendering can add arrowheads to segment ends.
* Labels have a model anchor and optional model-space target/leader. Their text
is projected and drawn as screen-facing HUD text. If the command omits Z,
screen X/Y coordinates are projected onto the current view plane at creation
time.
* Polygons include general polygons, circles, ellipses, rectangles, and
squares. Creation and editing operate on a saved view plane with a view-Z
offset. Selection, movement, open/close, area, overlap, and 2D boolean
operations are supported. A "fill" is currently a set of hatch lines, not
an opaque surface. Polygons can be explicitly imported from or exported to
persistent sketch primitives.
Scene objects also provide object color, transparency, pixel line width, dash
style, and arrow dimensions. Not all of these properties have a uniform
command interface for every object type.
There are other visual layers that should not be conflated with these objects.
Legacy gv_tcl data/sdata arrows, axes, lines, labels, and polygons have a
separate representation. Faceplate/HUD elements are screen UI. Database
objects temporarily cloned for editing are also transient, but retain database
primitive semantics and live in the database-object scene containers.
Comparison
----------
Both annotations and view objects eventually produce display-manager pixels,
often through bv_scene_obj and vlists. That common final path does not make
their source representations equivalent.
Annotations are the canonical choice when markup must be saved, named in a
database, instanced, shared, raytraced from a .g file, or understood by another
program. View objects are the right choice for application/session state that
must be created and manipulated without changing the database.
Some view objects have a direct annotation analogue, but conversion is not
lossless without type-aware logic:
* A view polyline maps naturally to line segments, including arrowhead style.
* A polygon outline and its hatch lines can map to annotation coverage, but the
existing view "fill" must not silently become an opaque filled polygon.
* A label is hybrid. Its target and leader are model-space geometry while its
glyphs remain screen-facing at a projected model anchor.
* Arbitrary vlist extraction is insufficient because display-matrix commands,
point and line sizes, child objects, glyph behavior, and semantic roles are
otherwise lost or ambiguous.
View-object line width and annotation line width also need an explicit unit
policy. View-object widths are display pixels. Screen-space annotation widths
are view-dependent coverage widths. A model-space annotation's line_width is
used as a scale on the automatically selected model-space stroke width. In
other words, removing a separate raytracer "stroke-width" setting does not
remove width control: the annotation's existing line_width/`--line-width`
style remains the single user-facing control. The renderer interprets it in
the appropriate coordinate system rather than exposing a second competing
knob.
Recommended transient rendering design
--------------------------------------
Do not make librt or the raytracers depend on bv_scene_obj, libbv application
containers, or libged. Do not manufacture tubes, TGCs, meshes, or geometry
just in front of the view plane. Artificial geometry would affect bounds,
depth, shadows, and secondary rays, and would make antialiasing and perspective
behavior depend on a fabricated distance from the eye.
If transient view objects need to appear in raytraced output, use an explicit
application-to-renderer snapshot boundary:
1. The GED/application layer snapshots selected live view objects when a render
is launched. This is the only layer that owns their lifetime and semantics.
2. A type-aware adapter in libged or libbv translates supported objects into a
renderer-neutral overlay/coverage description. It should reuse annotation
coverage preparation through an in-memory annotation-instance interface,
rather than duplicate line, glyph, and arrow intersection algorithms.
3. The snapshot travels with the exact view state: model-to-view transform,
perspective, image width and height, and sampling convention. In-process
renderers can receive it directly; subprocess rt/ERT needs an explicit
serialized side channel.
4. Raytracers composite this coverage only on primary image samples, after
geometry color and depth are known. It remains outside CSG, model bounds,
shadows, reflections, and refractions.
5. Initial support should be deliberately finite: polyline strokes and
arrowheads, label glyphs and leaders, polygon outlines, and polygon hatches.
Faceplate elements and legacy gv_tcl overlays should be separately specified
opt-in layers rather than being included accidentally.
6. Snapshot records should preserve object name and type, RGBA, visibility,
local/shared ownership, presentation units, and source identity. Unsupported
object types should produce a deliberate diagnostic or documented omission.
Application-side compositing of the interactive display-manager layer over an
ERT framebuffer is a viable smaller feature if only ERT screen captures need
view objects. It would closely reproduce the interactive view, but it would
not make the objects available to standalone rt, ART, or ISST output and should
not be presented as the general renderer solution.
Conversion between the two systems should remain explicit. Automatically
turning view objects into database annotations would violate their deliberately
transient contract. A user command to promote selected view markup to a
persistent annotation would be reasonable; polygon-to-sketch export is an
existing precedent. Conversely, a view-object editor may be used as a front
end for constructing an annotation, provided the persistence action is clear.
Maintenance findings before implementing the adapter
----------------------------------------------------
Several representation issues should be corrected before type-driven snapshot
conversion is relied upon:
* Container-selection flags (BV_DB_OBJS, BV_VIEW_OBJS, BV_LOCAL_OBJS, and
BV_CHILD_OBJS) reuse numeric bits also used for semantic s_type_flags
(BV_DBOBJ_BASED, BV_VIEWONLY, BV_LINES, and others). bv_obj_create currently
assigns its type argument to s_type_flags. A local view object consequently
acquires BV_LINES incidentally, while the line command does not explicitly
tag lines. The namespaces should be separated and every object explicitly
typed.
* Lines, labels, and polygons should have unambiguous type tags and per-type
lifecycle/extraction callbacks. Snapshot conversion must inspect typed data,
not infer semantics from a rendered vlist.
* Label construction should initialize every bv_label field deliberately,
including size, line_flag, anchor, arrow, and target. Current construction
does not establish all of those values on every path.
* The status of legacy gv_tcl overlay data should be decided explicitly:
migrate it, support it through a separate adapter, or document its exclusion.
* Screen-space annotation display and raytrace units need a parity test. The
legacy display-matrix vlist path and the ray coverage path currently express
screen offsets through different mechanisms; their apparent agreement must
be verified rather than assumed.
* Per-segment annotation colors exceed the legacy interactive vlist object's
single-color model. Uniform annotate-command colors work today, but true
multicolor interactive parity may require split scene objects or a richer
display representation.
Tests for a future adapter should cover shared and local ownership, cleanup,
label anchors and leaders, polygon holes/hatches/view-Z, perspective, pixel
width, model-space occlusion, screen-space overlay, and serialization across
the ERT subprocess boundary.
Conclusion
----------
Persistent annotations and transient view objects should continue to coexist.
The annotation coverage scene is the correct renderer-side abstraction for
both saved annotations and a future snapshot of transient markup. The
application should perform the semantic conversion and transfer a view-complete
snapshot; the renderer should treat it as primary-ray coverage, never as
invented scene geometry.
+543
View File
@@ -0,0 +1,543 @@
BRL-CAD B-Rep Tessellation Improvement Plan
===========================================
Date: 2026-07-17
1. Objective
------------
Produce deterministic, watertight, orientable manifold meshes from valid closed
NURBS B-Reps, with explicit and measurable geometric quality guarantees. The
conversion-quality path must either return a certified mesh or fail with a
specific diagnostic. It must never silently emit a mesh marked solid when the
result has not passed all required checks.
The existing fast tessellator should remain available for shaded display. It
should not be evolved into, or presented as, a conversion-quality tessellator:
its face-local vertex indexing means coincident samples on adjacent faces are
not topologically shared.
2. Central Design Decision
--------------------------
Do not triangulate every face directly in its native rectangular NURBS UV
domain.
Instead, introduce an explicit chart layer:
B-Rep topology
-> one or more topology-correct charts per face
-> constrained triangulation in chart coordinates
-> evaluation through chart -> native UV -> 3D surface
-> global assembly using explicit topology IDs
-> manifold and geometric certification
The chart domain must have the topology of the represented surface region.
Periodic seams and singularities are chart topology, not floating-point
coincidences to repair after triangulation.
For a cone, the first prototype should use a triangular or wedge chart. The
base is one chart boundary, the two sides are periodic copies of the seam, and
the apex is one chart vertex. Chart interior points map back to the original
rectangular native UV domain for surface evaluation. No boundary edge handed
to detria should collapse to one 3D point.
A triangle is not a universal replacement for a quad:
* Cone: triangular/wedge chart, or several angular wedges.
* Sphere: triangular pole caps plus one or more regular middle charts.
* Cylinder: regular periodic chart with explicit seam identification.
* Torus: a multi-chart atlas or explicit identifications in both periodic
directions.
* General trimmed faces: decomposition into certified disk-like charts, with
trims split where they cross chart boundaries.
Do not change the underlying NURBS representation merely to tessellate it.
Charts are adapters to native UV.
3. Findings Driving This Plan
-----------------------------
3.1 Current singularity handling has the wrong topology
The quality path subdivides singular trim segments in 2D even when all samples
map to the same 3D pole. Detria triangulates the resulting 2D strip, after
which BRL-CAD merges vertices by ON_3dPoint pointer identity and discards
degenerate triangles. Holes, duplicate triangles, and bad edge incidence are
then repaired heuristically. A valid manifold cannot be made reliable with
this ordering.
Relevant implementation:
* src/libbrep/cdt/cdt.cpp, especially singular-trim splitting around line 487.
* src/libbrep/cdt/edge.cpp, shared B-Rep edge discretization around line 1022.
* src/libbrep/cdt/mesh.cpp, face triangulation and repair.
3.2 Projection repair is handling invalid charts, not only difficult geometry
On failing NIST cases, local LSCM/mean-value and best-fit-plane projections
produce mixed triangle orientations or self-intersecting constrained polygons.
Detria then correctly rejects intersecting constraints. Retrying a different
projection does not address the source topology problem.
The implementation called LSCM is a positive-weight spring/mean-value-style
parameterization with circular or elliptical boundary placement. It does not
validate all conditions needed for injectivity, it does not adequately check
solver convergence, and its current result checks do not prove the absence of
fold-overs.
3.3 UV aspect normalization changes Delaunay decisions
The quality path anisotropically scales high-aspect UV domains toward a square.
Nonuniform scaling does not preserve Delaunay triangulations. It changes
incircle tests and selected diagonals and is therefore a metric choice, not
numerical conditioning.
Use translation and uniform, preferably power-of-two, scaling only to condition
coordinate range. Obtain good 3D triangles using the surface metric, adaptive
sampling, and constrained local optimization.
3.4 Several sources of nondeterminism and platform sensitivity exist
* Surface jitter is assigned while iterating pointer-ordered containers, so
allocation order changes which patch receives which perturbation.
* Other work queues and sets use pointer values as ordering keys.
* The detria translation unit is not currently compiled with all strict
floating-point options required by its robust predicates.
* An experimental facetize caller does not fully initialize its tolerance
structure.
* The vendored detria predates upstream changes to collinear-prefix handling
and expansion arithmetic.
3.5 Present final validation is necessary but not sufficient
bg_trimesh_solid2 verifies indexed degeneracy and two oppositely directed uses
of every edge. It does not establish geometric nondegeneracy, valid vertex
links, lack of self-intersections, surface error, normal error, connectedness,
outward orientation, or acceptable triangle quality.
4. Target Data Model
--------------------
Replace implicit topology based on ON_3dPoint pointers with explicit stable
identifiers. At minimum, distinguish:
* TopoVertexID: a vertex in global B-Rep/output topology.
* BrepEdgeID: a B-Rep edge.
* EdgeSampleID: one sample in the canonical ordered discretization of a B-Rep
edge.
* FaceChartID: one chart associated with a B-Rep face.
* ChartVertexID: a vertex in a chart; several chart vertices may intentionally
map to the same TopoVertexID at a seam or singularity.
* MeshVertexID, HalfedgeID, and TriangleID in the assembled output mesh.
Coordinates are attributes of these entities, not their identity.
Each B-Rep edge must be sampled once through a global edge manager. Both
adjacent face charts reference exactly the same EdgeSampleID sequence, with
opposite traversal direction. If refinement inserts an edge sample, both
adjacent charts must be invalidated or updated together.
The assembled output should use a halfedge structure or an equivalent data
structure that makes edge incidence and vertex-link validation explicit.
5. Work Plan
------------
Phase 0: Establish a reproducible baseline
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0.1 Build an end-to-end test driver around ON_Brep_CDT_Tessellate and
ON_Brep_CDT_Mesh. It must be able to:
* select individual solids and faces;
* set all tessellation tolerances explicitly;
* write the chart constraints, sampled points, and output mesh on failure;
* run validation independently of frontends such as facetize;
* produce stable textual summaries suitable for CTest comparison.
0.2 Register the ten cone fixtures and the primary solids in all NIST database
files as regression tests. Add smaller extracted regressions for every failure
so normal testing does not depend only on large databases.
0.3 Record, for every test:
* result status and failure stage;
* vertex, edge, and triangle counts;
* open, overused, and misoriented edge counts;
* invalid vertex-link count;
* degenerate and intersecting triangle counts;
* maximum surface and edge approximation errors;
* minimum angle and relevant aspect measures;
* runtime and refinement counts.
Exit criterion:
* Every baseline case has a repeatable result and a deterministic diagnostic
artifact when it fails.
Phase 1: Correctness, determinism, and fail-closed behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1.1 Fix known local defects before changing the architecture:
* In edge.cpp, correct the condition that tests trim1 twice rather than testing
trim1 and trim2.
* In mesh.cpp duplicate-triangle handling, compute the second direction from
the second triangle rather than calling tnorm(orig) twice.
* In cdt.cpp mesh export, index fmeshes using active_faces[fi].
* Update the closest-distance accumulator in the closest-point helper.
* Preserve orientation when comparing normals; do not erase it with an
unconditional absolute dot product.
* Make status initialization, update, and reuse semantics explicit.
* Invalidate and rebuild all tolerance-dependent data when tolerances change.
* Fully initialize bg_tess_tol in every caller.
* Require every frontend to accept only the defined success status.
* Never mark a BoT solid after a non-solid or failed return.
* Fail before tessellation when required closed-manifold B-Rep topology is
invalid.
* Destroy all state-owned diagnostic allocations.
1.2 Eliminate pointer-address ordering from all algorithms. Sort and queue by
stable B-Rep, chart, edge, sample, and triangle IDs.
1.3 Remove random UV jitter. Robust predicates must handle collinearity.
Where a geometric policy is needed for exactly coincident or on-constraint
points, resolve it topologically and report the decision.
1.4 Introduce explicit result codes and structured diagnostics. Suggested
categories:
* invalid input B-Rep topology;
* invalid tolerance configuration;
* chart construction failure;
* invalid planar straight-line graph;
* detria failure;
* adaptive refinement limit;
* manifold assembly failure;
* geometric validation failure;
* success.
Exit criteria:
* Repeated runs produce identical constraints and topology.
* Frontends never export or label failed results as solids.
* Known local defects have focused regression tests.
Phase 2: Harden detria and the libbg wrapper
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Detria should remain the constrained Delaunay triangulation engine. Its role
should be tightly bounded: triangulate an already valid planar straight-line
graph in a topology-correct chart.
2.1 src/libbg/detria.hpp has been udpated to the current reviewed upstream version.
Important changes:
* long collinear and nearly collinear initial point sequences;
* guarded expansion-array advancement in robust arithmetic.
Upstream references:
* https://github.com/Kimbatt/detria
* https://github.com/Kimbatt/detria/blob/master/DOCS.md
* https://github.com/Kimbatt/detria/commit/610840e27320199fa4d9de40d8565ddafadceb8d
* https://github.com/Kimbatt/detria/commit/2eaf7f8
2.2 Compile the translation unit instantiating detria with strict
floating-point semantics:
* GCC/Clang: no fast-math, -ffp-contract=off, -frounding-math, and initially no
LTO for that translation unit.
* MSVC: /fp:strict, or at minimum never /fp:fast.
Test LTO separately on each supported compiler before allowing it for this
translation unit.
2.3 Add wrapper preflight:
* validate all indices before dereferencing them;
* reject nonfinite coordinates;
* remove zero-length constraint segments;
* detect exact coordinate duplicates;
* coalesce duplicates only when they have the same semantic topology;
* promote a Steiner point lying on a constraint into the constraint sequence,
or discard it according to a documented policy;
* detect nonincident crossing constraints and return an invalid-chart error;
* condition coordinate range only with translation and uniform power-of-two
scaling;
* preserve semantic IDs through index compaction.
2.4 Add wrapper postconditions:
* every required constraint appears exactly as required in the output;
* every output index is valid;
* triangle orientations follow the requested convention;
* no indexed or geometric zero-area triangle is present;
* coverage and area agree with the chart polygon within a defined tolerance;
* no unexpected boundary or nonmanifold edge is present.
2.5 Add adversarial detria tests covering:
* short and very long collinear prefixes;
* nearly collinear points across a range of ULP separations;
* cocircular and nearly cocircular points;
* duplicate coordinate identities;
* Steiner points exactly and nearly on constraints;
* crossing constraints;
* holes and nested invalid loops;
* huge and tiny coordinate exponents;
* input-order permutations;
* translations, rotations, and uniform power-of-two scaling.
Do not require a particular diagonal for cocircular polygons unless BRL-CAD
defines a deterministic tie-break. Validate Delaunay, constraint, and manifold
properties instead.
Exit criteria:
* Detria tests pass under Debug, optimized Release, supported sanitizers, and
all primary compilers.
* Invalid inputs are classified before or at the wrapper boundary and never
enter higher-level mesh repair as mysterious missing triangles.
Phase 3: Introduce topology-correct charts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3.1 Add a FaceChart abstraction containing:
* a chart domain and its boundary loops;
* mappings from chart vertices and edges to global topology IDs;
* chart -> native UV evaluation;
* native UV -> chart mapping where available;
* singular and periodic boundary metadata;
* trim fragments assigned to the chart;
* validity checks for simple boundaries and a nonfolded interior.
3.2 Implement classification of each face and surface:
* regular nonperiodic;
* periodic in U or V;
* periodic in both directions;
* one collapsed boundary;
* two collapsed boundaries;
* irregular trim topology requiring decomposition.
Use B-Rep topology and surface properties, not only coordinate equality, to
identify seams and singularities.
3.3 Implement the cone prototype:
* construct one triangular/wedge chart for the lateral face;
* represent the apex once in the chart;
* represent the two periodic seam sides separately in chart space but map them
to the same ordered global topology IDs;
* map chart points to native cone UV for exact surface evaluation;
* remove singular-trim oversplitting for this chart type;
* ensure detria never receives a collapsed boundary segment;
* assemble and certify all cone fixtures across scale and orientation changes.
3.4 Expand to:
* spherical pole caps;
* cylinders and other single-periodic regular surfaces;
* tori and doubly periodic surfaces;
* trimmed singular surfaces;
* faces requiring multiple charts.
3.5 Split trim curves at chart boundaries. Each trim fragment must be
represented by one ordered chart constraint sequence with explicit global IDs.
Do not merge chart seam copies by coordinate comparison.
Exit criteria:
* No chart supplied to detria has a collapsed constraint edge.
* Cone and sphere poles are represented by explicit topology rather than
post-triangulation vertex collapse.
* Adjacent faces share the exact same boundary sample IDs.
* The cone fixture suite passes full manifold and geometry certification.
Phase 4: Geometric adaptive refinement and triangle quality
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4.1 Use detria for the initial constrained triangulation of a valid chart.
4.2 Measure error against the original B-Rep geometry, including:
* curve chord error for constrained edge segments;
* surface chord error at triangle edge midpoints and centroids;
* normal deviation;
* variation of the first fundamental form/Jacobian over a triangle;
* minimum 3D angle;
* 3D aspect and size relative to local feature scale.
4.3 Refine face interiors by inserting chart-space Steiner points selected from
the worst geometric error. Re-run constrained triangulation or perform a
locally certified insertion.
4.4 Refine shared B-Rep edges only through the global edge manager. A new
sample must be inserted into the ordered edge sequence used by both adjacent
charts before either face can be finalized.
4.5 Replace anisotropic global UV normalization. If a quality objective is
anisotropic, derive it from the surface metric and use local sampling or edge
flips. Numerical conditioning remains a uniform transformation only.
4.6 After the required geometric tolerance is met, improve quality with
constraint-preserving operations such as:
* metric-aware edge flips;
* additional Steiner points;
* surface-constrained vertex relocation where topology permits it.
Every operation must be transactional and retain the error and manifold
guarantees.
Exit criteria:
* Results meet explicit chord and normal tolerances.
* Quality is stable under different native UV aspect ratios for equivalent 3D
geometry.
* Shared-edge refinement cannot create a T-junction.
Phase 5: Replace heuristic repair with certified assembly
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5.1 Assemble all chart triangles using explicit global topology IDs into a
halfedge mesh.
5.2 Make patch replacement transactional. A replacement is committed only if:
* its boundary exactly matches the removed patch boundary IDs;
* every new triangle is valid and correctly oriented;
* its vertex links and edge incidence are valid;
* it introduces no geometric intersections;
* it meets surface and quality tolerances.
5.3 Retire projection repair paths once the corresponding chart types are
implemented. Retain a parameterization fallback only for a patch already
proved to be a topological disk. Require positive orientation for every mapped
triangle, a solver residual check, and a simple nonintersecting boundary before
calling detria.
5.4 Eliminate arbitrary orientation tie-breaks and repair loops whose progress
is based only on a changed triangle set. Bound every refinement and repair
operation with a clear failure code and diagnostic artifact.
Exit criteria:
* Standard successful tessellations do not rely on post-hoc hole or orientation
repair.
* Any remaining repair operation preserves its boundary and is locally
certified before commit.
Phase 6: Full certification and conversion gate
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before returning conversion-quality success, verify:
Topology:
* every mesh edge has exactly two oppositely oriented incident faces;
* every vertex link is one cycle for a closed manifold component;
* no T-junctions or duplicate directed halfedges exist;
* component count and Euler characteristic agree with the intended B-Rep;
* all required B-Rep edge sample sequences appear in the output.
Geometry:
* every triangle has scale-aware nonzero area;
* nonadjacent triangles do not intersect;
* edge and surface approximation errors meet the requested tolerances;
* normal deviations meet the requested tolerance;
* orientation is consistent and outward where the B-Rep supplies enough
information to determine it.
Quality:
* report minimum angle and aspect statistics;
* enforce defined minimum quality requirements, or return a distinct result
stating that topology and tolerance passed but quality did not;
* prevent unbounded refinement by reporting the limiting feature or tolerance.
Only after these checks may a caller mark the result as BOT_SOLID.
6. Test Matrix
-------------
Run the complete regression set under:
* GCC and Clang on Linux;
* MSVC on Windows;
* x86-64 and ARM64 where available;
* Debug and optimized Release;
* LTO enabled and disabled;
* ASan and UBSan, plus MSan for detria and parameterization tests where
practical;
* repeated executions with allocator perturbation.
Apply geometric transformations and topology-preserving reorderings:
* model scales from approximately 1e-12 through 1e12 where OpenNURBS and
BRL-CAD tolerances permit meaningful representation;
* large translations;
* arbitrary rotations;
* uniform power-of-two scale changes;
* reversed or permuted face, loop, edge, and input point ordering;
* small ULP perturbations around collinear, cocircular, seam, and pole cases.
The property result should be invariant even where the exact valid Delaunay
diagonal is not unique.
7. Initial Acceptance Targets
-----------------------------
The first useful architecture milestone is complete when:
1. All ten existing cone fixtures tessellate using the new cone chart, without
singular-boundary oversplitting or post-CDT pole collapse.
2. All ten outputs pass full topology, vertex-link, self-intersection, and
geometric error validation.
3. Results are invariant under input reordering, repeated execution, supported
compilers, and representative scale/rotation transforms.
4. The same global edge sample sequence is demonstrably used by both faces
adjacent to every nonsingular B-Rep edge.
5. Detria receives only preflighted simple constraints and runs with strict
floating-point semantics.
6. Failure produces a bounded, classified result and a reproducible chart
artifact rather than entering an open-ended repair loop.
After the cone milestone, require the same gates for spheres, periodic regular
surfaces, and then the complete NIST set.
8. Priority Order
-----------------
1. Reproducible end-to-end tests and diagnostic artifacts.
2. Local correctness bugs, initialized state, deterministic ordering, and
fail-closed frontends.
3. Detria update, strict compilation, wrapper preflight, and adversarial tests.
4. Explicit topology IDs and the cone triangular-chart prototype.
5. Sphere and periodic-surface chart support.
6. Metric-driven adaptive refinement and triangle-quality improvement.
7. Certified halfedge assembly and removal of heuristic projection repairs.
8. Full NIST and cross-platform certification.
Do not spend substantial effort tuning best-fit projection, UV jitter, or
post-triangulation singularity repair before the chart and explicit-topology
layers exist. Those mechanisms operate after the information needed for a
reliable solution has already been lost.
+25
View File
@@ -14,6 +14,31 @@ foreach(im ${NIST_SAMPLE_MODELS})
g_target(${im} "BRL-CAD Geometry Models/Sample")
endforeach(im ${NIST_SAMPLE_MODELS})
# NIST 6 face 51 contains an unrelated seam excursion at a spherical pole.
# It must retain the native rigorous result without invoking mesh repair.
if(BUILD_TESTING AND TARGET brep-audit AND TARGET NIST_MBE_PMI_6.g)
brlcad_add_test(
NAME brep_cdt_nist6_native
COMMAND
brep-audit
--mode
quality
--valid-solids-only
--memory-limit-mib
4096
"${CMAKE_BINARY_DIR}/${DATA_DIR}/db/nist/NIST_MBE_PMI_6.g"
Document_item.s
)
set_tests_properties(
brep_cdt_nist6_native
PROPERTIES
REQUIRED_FILES "${CMAKE_BINARY_DIR}/${DATA_DIR}/db/nist/NIST_MBE_PMI_6.g"
TIMEOUT 90
)
add_dependencies(check NIST_MBE_PMI_6.g)
add_dependencies(unit NIST_MBE_PMI_6.g)
endif()
cmakefiles(
${NIST_SAMPLE_MODELS}
CMakeLists.txt
+1 -53
View File
@@ -132,10 +132,6 @@ Command to set the backout flag. With no option, prints the current value. When
Sets the _useair_ member of the __rt_i__ structure to the integer _n_. If _n_ is 0, then *nirt* ignores any air in the geometry. If this command is invoked with no arguments, *nirt* prints the current value of _useair_. Default is 0.
*useannot [* _n_*]*::
Enables (_n_=1) or disables (_n_=0) diagnostic annotation intersections. When enabled, *nirt* evaluates all model-space annotations below the active database paths and invokes the *a* output action for each crossing. Screen-space annotations are not evaluated because an individual NIRT ray does not supply a pixel size or model-to-view transform. If this command is invoked with no arguments, *nirt* prints the current value. Default is 0.
*overlap_claims [* _n_*]*::
Specifies how to handle multiple regions' claims to segments of a ray. If _n_ is 0 or "resolve", then all overlaps are resolved in favor of a single region and any other claimants are ignored. If _n_ is 2 or "rebuild_all", then all overlaps are rebuilt, so any overlapping regions along the ray create individual (geometrically intersecting) partitions. If _n_ is 3 or "retain", then all overlaps are retained. In this case, the resulting partitions are always geometrically disjoint, each one is owned by a single region according to the current overlap resolution strategy, but every claimant is recorded. If _n_ is 1 or "rebuild_fastgen", then *nirt* takes on _FASTGEN_ behavior, so overlaps of plate-mode primitives are rebuilt, but other overlaps are retained. This command is useful with the __claimant_count__, __claimant_list__, and __claimant_listn__ output items. Default is "resolve".
@@ -201,11 +197,7 @@ Quits *nirt*.
[[output_statements]]
== OUTPUT STATEMENTS
*nirt* allows the user a high degree of control, via the _fmt_ command, over what information gets printed out for each ray and in what format. There are eight types of output statements, each of which is executed under appropriate circumstances. The types and their use are:
*a*::
Annotation. Executed once for each model-space annotation crossing when *useannot* is enabled. Crossings are reported in ray-distance order after the ordinary solid shotline output. The default format is empty.
*nirt* allows the user a high degree of control, via the _fmt_ command, over what information gets printed out for each ray and in what format. There are six types of output statements, each of which is executed under appropriate circumstances. The types and their use are:
*r*::
@@ -286,41 +278,6 @@ azimuth of view (i.e., of ray direction).
elevation of view (i.e., of ray direction).
Annotation Information
*annot_path*::
full database path of the annotation primitive.
*annot_segment*::
zero-based annotation segment index.
*annot_role*::
semantic role of the annotation segment, such as `text`, `leader`, `dimension`, or `arrowhead`.
*annot_x*::
_x_ coordinate of the annotation hit point.
*annot_y*::
_y_ coordinate of the annotation hit point.
*annot_z*::
_z_ coordinate of the annotation hit point.
*annot_d*::
_d_ grid-plane coordinate of the annotation hit point.
*annot_visible*::
1 if no non-air solid surface is closer to the ray origin, otherwise 0. Occluded annotation crossings are still reported so they can be inspected during debugging.
Partition Information
@@ -564,15 +521,6 @@ nirt -A material_id -A los_density model.g all
Each region the ray passes through is reported together with its _material_id_ and _los_density_ attribute values via the *attributes* output item, which is useful for vulnerability and mass-properties analyses.
====
.Reporting annotation crossings for debugging
====
[source,bash]
----
nirt -s -e 'useannot 1;fmt a "%s %d (%g %g %g) visible=%d\n" annot_path annot_segment annot_x annot_y annot_z annot_visible;s' model.g component dimensions
----
This opt-in format reports every model-space annotation crossing and whether it is in front of the first solid surface. Ordinary NIRT output is unchanged unless *useannot* is enabled and an *a* format is supplied.
====
[[hints]]
== HINTS
+34 -18
View File
@@ -4,38 +4,54 @@
:manmanual: BRL-CAD
== NAME
pixdiff - highlight samples which differ between two pix or bw files
pixdiff - highlight differences between two pix or bw images
== SYNOPSIS
[source]
----
pixdiff _file1.pix_ _file2.pix_ > _out.pix_
pixdiff _file1.bw_ _file2.bw_ > _out.bw_
pixdiff -b _file1.bw_ _file2.bw_ > _out.bw_
----
[[description]]
== DESCRIPTION
*pixdiff* compares two raw *pix*(5) or *bw*(5) images byte by byte and
writes a difference image of the same size to standard output. Bytes that
differ by +/-1 are written at intensity 192, while larger differences are
written at full intensity 255. Matching bytes are written at half their
original intensity, providing visual context for locating differences.
By default, *pixdiff* compares raw *pix*(5) images as RGB pixels and writes
a PIX difference image to standard output. When all three channels of a
pixel match, it writes a dim grayscale version of that pixel to all three
output channels. When any channel differs, each matching channel is written
as zero, each channel differing by one is written as 192, and each channel
differing by more than one is written as 255.
Because the comparison does not interpret pixel structure, no format option
is needed and *pixdiff* is independent of image resolution. The two inputs
must be the same size in bytes; otherwise the program reports the mismatch
and exits. Either input file may be given as
With *-b*, *pixdiff* compares raw *bw*(5) images one byte at a time and
writes a BW difference image. Differing samples use the same 192 and 255
intensities; matching samples are written at half their original intensity.
No image dimensions are needed. The two inputs must be the same size in
bytes, and PIX input must contain complete three-byte pixels. Otherwise,
*pixdiff* reports an error. Either input file may be given as
*-* to read from standard input. Output must be redirected; *pixdiff*
refuses to write a raw image stream to a terminal.
Upon completion, the count of bytes that matched, that were off by 1, and
that were off by many is reported on standard error.
Upon completion, *pixdiff* reports matching, off-by-one, and larger-difference
channel or sample counts on standard error.
Also see *bwdiff*(1), which can also operate on *pix*(5) files and can
produce other kinds of difference output.
*bwdiff -n -b* produces a similar BW highlight image, but writes 255 rather
than 192 for off-by-one differences. The default *bwdiff*(1) output instead
shows signed differences around a neutral gray.
[[options]]
== OPTIONS
*-b*::
Treat both inputs and the output as one-byte-per-pixel BW images. Without
this option, use three-byte-per-pixel PIX images.
*-h*::
Show usage information.
[[examples]]
@@ -59,10 +75,10 @@ on standard error give a quick quantitative sense of how much changed.
====
[source,bash]
----
pixdiff depth_a.bw depth_b.bw > depth_diff.bw
pixdiff -b depth_a.bw depth_b.bw > depth_diff.bw
----
Writes a grayscale highlight image using the same byte comparison and
statistics as the color example; no format option is needed.
Writes a grayscale highlight image using byte-wise comparison. Matching
samples retain half their input intensity as context.
====
+3 -2
View File
@@ -18,7 +18,8 @@ Link with *-lanalyze*.
The public interface is organized across the headers included by
_analyze.h_, including _analyze/diff.h_, _analyze/density.h_,
_analyze/grid.h_, _analyze/heal.h_, _analyze/info.h_, _analyze/pnts.h_,
_analyze/polygonize.h_, _analyze/nirt.h_, _analyze/voxelize.h_, and
_analyze/contour.h_, _analyze/polygonize.h_, _analyze/nirt.h_,
_analyze/voxelize.h_, and
_analyze/worker.h_.
== DESCRIPTION
@@ -32,7 +33,7 @@ to gather the information they need.
Facilities provided by the library include model differencing, material
density and mass property analysis, ray-based gridding and sampling,
geometry healing, model information queries, point-cloud generation and
processing, polygonization and voxelization of geometry, and the
processing, manifold dual contouring and voxelization of geometry, and the
interactive ray-tracing engine used by the *nirt*(1) command. A parallel
worker interface supports distributing analysis work across multiple
threads.
+50
View File
@@ -200,6 +200,56 @@ ____
The LIBRT_DEBUG environment variable may be set to the hexadecimal bit vector as described by the DEBUG_* flags listed in the raytrace.h header. Similarly, many LIBRT applications provide a "-x #" command-line option which will take precedence over the environment variable.
The RT_DEBUG_RTCMP flag (0x10000000) (set with LIBRT_DEBUG=10000000)
sets up rtcmp capture, but recording starts disabled. Set
LIBRT_RTCMP_FILE before enabling the flag, then call
rt_rtcmp_capture_set_enabled(1) from the application or a debugger to
record selected rays. Call rt_rtcmp_capture_set_enabled(0) to stop
recording. Enabling returns -1 if the debug flag is unset or the output
writer failed to initialize; disabling returns 0 even before setup. A
ray must begin while recording is enabled, and leaving recording
disabled when it finishes suppresses its record. The debug flag must be
set before concurrent ray shooting; use the setter to change recording
state while rays are running.
Captured hits append one high-precision rtcmp JSON record per ray with
final partitions. Misses produce no record. librt never clears the
output file; the caller must clear or rotate it before a new comparison
as needed. Separate processes must not write to the same file
concurrently. Pending records are flushed within two seconds under
normal operation and during normal process shutdown; call
rt_rtcmp_capture_flush() to wait for pending records when using a
debugger. Disabling recording does not discard records already queued. A
process terminated abnormally may lose pending records. rtcmp comparison
files should use the same hit-only capture mode on both sides; its
generation mode also includes misses. The output can be expanded to also
include primitive segments if LIBRT_RTCMP_PRIMITIVES is set to 1 before
capture is set up.
For example, run a single-threaded ray tracing application in a debugger
and stop at `rt_shootray` for the suspect ray. Capture just that call,
then compare it with a capture of the same ray from a reference build:
[source,shell]
----
$ LIBRT_DEBUG=10000000 LIBRT_RTCMP_FILE=trial.json \
gdb --args ./ray_application model.g
(gdb) break rt_shootray
(gdb) run
(gdb) call rt_rtcmp_capture_set_enabled(1)
(gdb) finish
(gdb) call rt_rtcmp_capture_set_enabled(0)
(gdb) call rt_rtcmp_capture_flush()
(gdb) quit
$ rtcmp -c reference.json trial.json
----
Continue to the suspect ray before enabling capture if the breakpoint
first stops on another ray. Produce `reference.json` with the same ray
and the same capture mode; set LIBRT_RTCMP_PRIMITIVES=1 for both runs
when primitive segments are needed to locate a difference before Boolean
evaluation.
The LIBRT_V4FLIP environment variable may be set to a true or false value to respectively force or disable endianness interpretation of v4 geometry database files. The default behavior is to automatically detect whether flipping the endian interpretation will help LIBRT read a binary-incompatible v4 geometry database file. Setting LIBRT_V4FLIP will override automatic detection.
+2 -32
View File
@@ -25,7 +25,7 @@ where the "char" argument is replaced by a single character identifying the cate
[[nirt_format_categories]]
== Output Categories
NIRT offers eight categories of output reporting:
NIRT offers seven categories of output reporting:
[cols="3*"]
[%noheader]
@@ -33,9 +33,6 @@ NIRT offers eight categories of output reporting:
|*Category Name*
|*Char*
|*Description*
|*Annotation*
|*a*
|Output for each model-space annotation crossing when annotation reporting is enabled with *useannot 1*. The default annotation format is empty.
|*Ray*
|*r*
|Report of ray information. The formatting associated with r will print once per ray, regardless of whether a region is encountered.
@@ -65,7 +62,7 @@ NIRT offers eight categories of output reporting:
[[nirt_format_str]]
== Format Strings
NIRT's format specifications are a subset of those found in printf implementations. The d, i, f, e, E, g, G, and s types are supported, as well as width and precision specifiers.
NIRT's format specifications are a subset of those found in printf implementations. Specifically, the d, i, and f types are supported, as well as width and precision specifiers.
[[nirt_format_keys]]
@@ -103,33 +100,6 @@ Unlike the full C/C++ printf, NIRT accepts only pre-defined string keys that ide
Footnote and Miss categories have no particular keys unique to them, although they (like all categories) can use the Ray keys if desired. Partition, Overlap, and Gap, on the other hand, have keys that are unique to their contexts and valid only within those contexts:
.Annotation Variables
[cols="2*"]
[%noheader]
|===
|annot_path
|Full database path of the annotation primitive.
|annot_segment
|Zero-based annotation segment index.
|annot_role
|Semantic role of the annotation segment, such as `text`, `leader`, `dimension`, or `arrowhead`.
|annot_x
|x coordinate of the annotation hit point.
|annot_y
|y coordinate of the annotation hit point.
|annot_z
|z coordinate of the annotation hit point.
|annot_d
|d grid-plane coordinate of the annotation hit point.
|annot_visible
|1 if no non-air solid surface is closer to the ray origin, otherwise 0.
|===
Annotation reporting is diagnostic and opt-in. It returns all model-space
annotation crossings in ray-distance order, including occluded crossings.
Screen-space annotations are not reported because a NIRT shot does not define
the pixel dimensions and model-to-view transform needed to evaluate them.
.Partition Variables
[cols="2*"]
[%noheader]
+9 -8
View File
@@ -40,11 +40,12 @@ Coordinates and explicit sizes use the database's current modeling units.
Creation commands draw the new annotation immediately. Use *--no-draw* to
create it without changing the display. Names must not already exist.
Ray-traced annotations are zero-thickness informational coverage rather than
solid regions. Model-space annotations are depth-tested against ordinary
geometry. Screen-space annotations are generated for the current camera and
composited in the foreground; they do not participate in shadows, reflections,
line-of-sight thickness, or Boolean evaluation.
Model-space annotations also participate in raytracing. Librt expands their
strokes and filled areas into transient mesh geometry during preparation; the
generated geometry is not written to the database. It uses the normal region
material and color resolved from the containing combination tree. Screen-space
annotations remain display-only because primitive preparation has no render
view or image dimensions with which to place them.
The supported dimensions follow the general presentation conventions of ISO
129-1. Lettering defaults to OSIFont, an ISO 3098-style technical font shipped
@@ -208,9 +209,9 @@ Set the persistent annotation color using integer red, green, and blue values
from 0 through 255.
*--line-width width*::
Set the presentation line width. Display and screen-space ray rendering use
the value as a pixel width. Model-space ray rendering uses it to scale the
automatic stroke width derived from the annotation's text height or extent.
Set the display line width. For a model-space raytrace, this value scales the
physical stroke width inferred from the annotation's text height or overall
extent.
*--line-style style*::
Set semantic line-pattern metadata to *continuous*, *dashed*, *dotted*,
+24 -16
View File
@@ -10,7 +10,7 @@ facetize - Creates __new_object__ using __old_object__ as a guide, creating an e
[source]
----
facetize [-h] [-v] [-q] [-n] [-r] [-s suffix] [-p prefix] [--in-place] [--max-time #] [--max-pnts #] [--resume] [--methods m1,m2,...] [--method-opts METHOD opt1=val opt2=val...] [--no-empty] [--log-file filename] [--nmg-booleval] [--disable-fixup] [--perturb] [--no-perturb] [--perturb-sa-tol #] [--perturb-vol-tol #] [--tolerate-failures] [-B -t threshold] {old_object new_object}
facetize [-h] [-v] [-q] [-n] [-r] [-j jobs] [-s suffix] [-p prefix] [--in-place] [--max-time #] [--max-pnts #] [--resume] [--methods m1,m2,...] [--method-opts METHOD opt1=val opt2=val...] [--no-empty] [--log-file filename] [--nmg-booleval] [--disable-fixup] [--perturb] [--no-perturb] [--perturb-sa-tol #] [--perturb-vol-tol #] [--tolerate-failures] [-B -t threshold] {old_object new_object}
----
@@ -45,6 +45,8 @@ By default, the Manifold BoT path can make use of a coplanarity-avoidance pertur
When the perturbation step is active, per-region *facetize* validates the initial Boolean output against the original CSG geometry using a Crofton stochastic raytrace. The default tolerance thresholds that trigger a perturb retry are 10% for both surface area and volume; these can be adjusted with *--perturb-sa-tol* and *--perturb-vol-tol*. At the end of a per-region facetization run *facetize* prints a concise validation summary listing how many regions passed, triggered a perturb retry, or encountered one of the following noteworthy conditions:
_Unbounded halfspace (validation unavailable)_: Crofton surface-area and volume comparisons require a finite sampling domain. A standalone halfspace, or another CSG result without finite bounds, is therefore not sampled and is counted as having validation unavailable. A bounded object intersected with or subtracted by a halfspace can still be validated because its finite operand supplies the sampling bounds.
_Sub-mm / few-hit bypass (accepted with note)_: When the Crofton sampler finds only a very small number of ray-surface intersections (below an internal threshold of 50 crossings), the sampling noise alone would exceed 14% relative error, making any SA comparison meaningless. Geometry in this category is typically sub-millimetre in cross-section (e.g., a TGC with a radius under 0.2 mm) but the stochastic sampler has confirmed that some geometry is present. The BoT conversion is accepted and the region is counted separately in the summary. Users should verify that such extreme geometry reflects actual modeling intent rather than an accidental near-degenerate primitive.
_Zero-hit replacement_: When the Crofton sampler fires rays but records zero intersections with the CSG geometry while the corresponding BoT is non-empty, *facetize* concludes that the Boolean evaluation most likely produces empty geometry (for example, a subtractor that fully engulfs the base object). In this case the non-empty BoT is discarded and replaced with an empty BoT to match the expected raytrace behavior. If this replacement appears unexpected for a given region, the *lint* command can be used to inspect the result.
@@ -61,13 +63,21 @@ Different tolerance settings will create finer or coarser meshes. See the *tol*
Primitive tessellation is performed in a child process against a private working copy of the database in the BRL-CAD cache. The original database is not modified as individual primitive meshes are generated. Only after the conversion succeeds are the requested results transferred from the working database. This separation also permits an interrupted conversion to retain its intermediate state for use with *--resume*.
The parent command sends primitive names to a persistent tessellation process one at a time. The child reports a result for each name, so a timeout, conversion failure, or child-process crash that occurs before a database write can be associated directly with the affected primitive. The child is restarted when necessary and processing continues with the next name; the parent does not subdivide and retry the whole group to discover which object failed. Unless output is suppressed with *--quiet*, long tessellation batches also report their current object and completed-object count at approximately five-second intervals.
The parent command sends primitive names to a small pool of persistent tessellation processes. Each child reports a result for every name, so a timeout, conversion failure, or child-process crash can be associated directly with the affected primitive. A child is restarted when necessary and processing continues with the next name; the parent does not subdivide and retry the whole group to discover which object failed. Unless output is suppressed with *--quiet*, long tessellation batches also report their completed-object count and active-worker state at approximately five-second intervals.
Database updates use a separate write handshake. After preparing a mesh in memory, the child announces that it is ready to write and reports the approximate mesh size, then waits. The parent acknowledges that it is waiting for the write before the child may update the working database. The child explicitly reports when the update is complete. Consequently, the normal per-object tessellation limit, including *--max-time* and method-specific limits, applies to mesh preparation and does not interrupt an acknowledged database write.
By default, *facetize* uses at most two worker processes and only enables the second when both CPU and memory information indicate it is safe. It reserves the larger of one GiB or one quarter of total physical memory for the rest of the system. Starting another job also requires at least 512 MiB beyond that reserve, or the largest worker resident-memory measurement seen so far if that is greater. If the platform cannot report the required memory information, *facetize* falls back to one worker. The available CPU count is divided among worker processes and is also imposed as the thread limit inside each worker, preventing nested algorithms from independently using all CPUs. *--jobs* changes the maximum process count; it does not bypass the memory admission checks.
Per-region BoT conversion uses this worker policy for both independent CSG validation and initial Manifold Boolean evaluation. When more than one worker is available, persistent validation processes each open their own read-only handle to the original database and compute Crofton surface-area and volume references for different regions. Persistent Boolean processes then evaluate different region trees using independent read-only handles to one shared snapshot of the tessellated working database. That snapshot also serves as the rollback checkpoint, so increasing *--jobs* does not create additional full database copies. This process isolation avoids sharing mutable raytrace preparation or database state between concurrent evaluations. Each result remains associated with the requested region name. The parent commits completed BoTs through one writer and continues to perform hierarchy changes, validation decisions, and any perturb retries serially. A one-worker run retains the established in-process order and avoids the snapshot phase.
Validation and Boolean workers report their completed-region count and active-worker state approximately every five seconds. When *--max-time* is specified, it also bounds each parallel CSG reference or Boolean request; a timed-out process is terminated, and the failure is associated with that region. Failure or timeout of supplementary CSG validation does not cause unrelated regions to be retried.
Database updates use a separate write handshake. After preparing a mesh in memory, a child announces that it is ready to write and reports the approximate mesh size and its current resident memory, then waits. The parent grants write permission, the child acknowledges when it is starting the write, and the child explicitly reports when the update is complete. Consequently, the normal per-object tessellation limit, including *--max-time* and method-specific limits, applies to mesh preparation and does not interrupt an acknowledged write.
With multiple workers, primitive tessellation children write to small staging databases rather than modifying the shared working database. Region Boolean children read one immutable cache-local snapshot while completed results are committed to the working database. The snapshot doubles as the recovery checkpoint and is opened independently by each worker; it is not duplicated per worker. For very large files, snapshot preparation reports the amount copied and elapsed time at five-second intervals. A region result is first completed in memory and enters the write handshake only when it is ready to replace the prior staged result. After a child reports a completed staging write, the parent submits that result to a single persistent writer process. The writer announces that it is ready, waits for the parent's permission, acknowledges that it is starting the write, transfers the result through one persistent working-database handle, and reports completion. This permits tessellation and initial region Boolean evaluation to proceed in parallel without concurrent database allocation updates or repeated full directory scans, while allowing the parent to supervise the canonical database write itself. With one worker, the established direct path avoids the extra staging I/O and database snapshot.
Before the first primitive conversion, the parent measures write throughput on the same cache filesystem as the working database. It writes a temporary file in 1 MiB chunks until approximately one second has elapsed or 128 MiB has been written, then removes the file immediately. Successful mesh writes of at least 1 MiB subsequently refine this profile. For each database update, the parent allows ten times the projected write time, with a minimum of five seconds and a maximum of five minutes. This measured allowance protects legitimate long writes on slow storage while still detecting a genuinely stalled update.
If a child fails before write permission is granted, the working database is known to be unchanged and no rollback is needed. A failure, crash, or write-deadline expiration after permission is granted but before completion is ambiguous: the working database may contain a partial update. In that case *facetize* restores its cache-local checkpoint and replays only the primitive conversions already known to have succeeded. The original database remains untouched during this recovery.
If a child fails before acknowledging that its write has started, the working database is known to be unchanged and no rollback is needed. In parallel mode, a failure during a child write affects only that worker's disposable staging database, so the failed object is reported and the worker can be restarted without replaying unrelated objects. A serial child failure during a direct write, or a writer-process failure after acknowledging write start, is ambiguous because the working database may contain a partial update. In those cases *facetize* restores its cache-local checkpoint and replays only the conversions not already tied to a specific failure. The original database remains untouched during this recovery.
[[facetize_options]]
@@ -94,6 +104,10 @@ Instructs *facetize* to produce NMG solids rather than BoT solids.
Rather than converting all specified inputs to one top level object, walk specified trees and individually convert regions to facetized objects. This mode is useful when wanting to display models in shaded mode while preserving color and region ID properties. If no regions are present a normal conversion is performed instead.
*-j, --jobs #*::
Set the maximum number of facetize worker processes. A value of 1 forces serial processing. The default value of 0 selects a conservative automatic limit based on available CPUs and memory. Memory checks may delay or reduce parallel work even when a larger value is requested. In per-region BoT mode the same limit controls parallel CSG reference sampling and initial Manifold Boolean evaluation.
*-s, --suffix*::
User specified suffix to use avoiding naming conflicts (naming conflict avoidance is needed when creating objects in *-r* mode.)
@@ -119,7 +133,7 @@ Resume an incomplete facetization process (will utilize cached data from a previ
*--methods*::
Accepts a comma separated list of one or more facetization methods to use. The default is to try all algorithms available as needed, but if a user knows they want to try a specific methodology they can call it out using this option.
Accepts a comma separated list of one or more facetization methods to use. The default tries NMG tessellation first, Manifold Dual Contouring (MDC) second, and SPSR as a final fallback. Other listed methods may be selected explicitly if a user wants to try a specific methodology.
*--method-opts*::
@@ -231,21 +245,15 @@ mged>**facetize --tolerate-failures large_model large_model.partial.bot**
====
.Increase Decimation in CM Mode
.Refine a Manifold Dual Contouring conversion
[example]
====
mged>**facetize --methods CM --method-opts feature_size=0.06 d_feature_size=2.0 ellipse.r ell.bot**
This selects MDC directly and requests a finer cell size with a larger ray
budget than the default:
The default settings on this object in CM mode will produce a little over 8000 faces in the final bot, but the addition of the *d_feature_size* option with a >1.5 value has dramatically reduced the count to 104.
mged>**facetize --methods MDC --method-opts "MDC feature_size=0.1 max_rays=400000" ellipse.r ell.bot**
====
.Decrease Decimation in CM Mode
[example]
====
mged>**facetize --methods CM --method-opts feature_size=0.06 d_feature_size=0.01 ellipse.r ell.bot**
If the intent is preserve a more detailed mesh, reducing *decimation-feature-size* will keep more of the original triangles - in the above example, almost 250 thousand of the original 6.9 million faces were retained. NOTE: use lower values here cautiously, as extremely large meshes can be very difficult for many tools to deal with.
Smaller feature sizes increase mesh detail, ray count, memory use, and runtime.
====
+16 -1
View File
@@ -43,7 +43,7 @@ Check for references to objects or files that are missing (not present in the da
*-I*, *--invalid-shape* [check [check ...]]::
Check for shapes in the database that do not satisfy one or more properties required for them to be valid definitions, where correcting the problem requires a change to the object's definition. (As of this writing the only implemented check is for BoT object solidity, but many more are possible and planned.) (Objects invalid due to missing objects are potentially valid without the necessity of changing the object's own definition - e.g. the missing object/file can be added. That category of error can be searched for with the *-M* check.)
Check for shapes in the database that do not satisfy one or more properties required for them to be valid definitions, where correcting the problem requires a change to the object's definition. Individual checks may be selected with _type:check_ names such as *brep:opennurbs*. Objects invalid because they refer to missing objects or files are reported by *-M* instead.
*-F*, *--filter*::
@@ -81,6 +81,21 @@ With *--perturb*, the reference BoT is the actual output that *facetize* would p
Fractional tolerance for raytrace validation comparisons (default: *0.10* = 10%). A value of 0.05 requires the two independent estimates to agree within 5%. Lower values are stricter. Only meaningful when *--raytrace* is also specified.
[[brep_checks]]
== B-REP CHECKS
The B-Rep invalid-shape checks include OpenNURBS validity, excessive stored tolerances, disagreement between edge curves and their surface trim paths, collapsed singular boundaries, and sampled trim-loop crossings. Their names are listed by *lint -h*. The full JSON report identifies the affected face, loop, trim, and edge where applicable.
The *brep:fast_shading* check runs the display-quality face tessellator and reports individual faces it cannot triangulate. Genuine failures include the tessellation result, processing stage, and diagnostic message. Faces not processed because a resource limit was reached are reported separately as inconclusive. Degenerate or tolerance-empty faces are considered successfully handled. Their counts, and the count of successful approximations, accompany any reported issue but are not classified as failures.
Fast shading is intentionally an explicit-only check because it is more expensive than the structural sampling checks. It has a 30-second per-object time budget, checked between face and refinement jobs, in addition to the display tessellator's default memory, point, and triangle budgets. It also attempts invalid B-Reps because the display path is designed to recover partial geometry from them. For example:
[source]
----
lint -I brep:fast_shading -j shading.json object
----
[[raytrace_mode]]
== RAYTRACE MODE
+13 -16
View File
@@ -679,23 +679,21 @@ Convert CSG geometry to a 3D voxel grid by:
fill fraction
* Level-of-detail parameter controls sub-voxel sampling
==== 7.5 Implicit Surface Polygonizer (Marching Cubes / Tetrahedral)
==== 7.5 Manifold Dual Contouring
An implicit surface polygonizer (Bloomenthal tetrahedral marching) for
converting any inside/outside function to a triangle mesh.
`contour.cpp` evaluates CSG geometry directly with `librt` and constructs
closed, oriented triangle meshes:
`polygonizer.c` (based on Jules Bloomenthal’s "`An Implicit Surface
Polygonizer,`" Graphics Gems IV, 1994) converts any implicit function
(inside/outside test) to a triangle mesh:
* Maintains a hash table of evaluated lattice corners and edges
* Expanding front: starts at a seed point and spreads outward, testing
adjacent cube faces
* Each transitive cube is optionally decomposed into 6 tetrahedra for
sub-cube accuracy
* Edge-surface crossing is found by bisection (`converge`) then the
vertex normal is estimated from finite differences
* Used in `libanalyze` for converting CSG regions to faceted form
* Recursively refines an implicit octree using cached axis-aligned rays along
node box edges and interior quarter- and center-lines
* Reuses complete ray partitions at the conforming finest grid and retains
evaluated boundary points with analytic surface normals
* Resolves ambiguous faces consistently and permits multiple vertices in a
cell to preserve local topology
* Places vertices with bounded quadratic-error minimization
* Validates the resulting indexed mesh before returning it
* Backs the legacy `analyze_polygonize` interface without retaining the old
seed-driven marching-tetrahedra implementation
==== 7.6 Geometry Differencing (raydiff)
@@ -1712,7 +1710,6 @@ import
runtime
* *MT19937*: Mersenne Twister PRNG
* *pbrt-v3* (derived): HLBVH construction algorithm
* *Bloomenthal polygonizer*: implicit surface polygonization
* *Screened Poisson Reconstruction* (Michael Kazhdan): surface
reconstruction from point clouds
* *Ball Pivoting Algorithm*: surface reconstruction
-1
View File
@@ -55,7 +55,6 @@ brlcad_manage_files(pbrt.txt ${DOC_DIR}/legal/embedded TRIGGER
brlcad_manage_files(picohash.txt ${DOC_DIR}/legal/embedded TRIGGER gist)
brlcad_manage_files(pixcii.txt ${DOC_DIR}/legal/embedded TRIGGER libicv)
brlcad_manage_files(point_in_polygon.txt ${DOC_DIR}/legal/embedded TRIGGER libbg)
brlcad_manage_files(polygonizer.txt ${DOC_DIR}/legal/embedded TRIGGER libanalyze)
brlcad_manage_files(predicates.txt ${DOC_DIR}/legal/embedded TRIGGER libbg)
brlcad_manage_files(profont.txt ${DOC_DIR}/legal/embedded TRIGGER libbu)
brlcad_manage_files(pstdint.txt ${DOC_DIR}/legal/embedded TRIGGER libbu)
-9
View File
@@ -1,9 +0,0 @@
https://www.bloomenthal.com/JBloom/code/polygonizer.c
Authored by Jules Bloomenthal, Xerox PARC.
Copyright (c) Xerox Corporation, 1991. All rights reserved.
Permission is granted to reproduce, use and distribute this code for
any and all purposes, provided that this notice appears in all copies.
file:/src/libanalyze/polygonizer.c
+1
View File
@@ -31,6 +31,7 @@
#include "common.h"
#include "analyze/defines.h"
#include "analyze/contour.h"
#include "analyze/debug.h"
#include "analyze/diff.h"
#include "analyze/density.h"
+1
View File
@@ -1,5 +1,6 @@
set(
analyze_headers
contour.h
debug.h
defines.h
density.h
+118
View File
@@ -0,0 +1,118 @@
/* C O N T O U R . H
* BRL-CAD
*
* Copyright (c) 2026 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; see the file named COPYING for more
* information.
*/
/** @addtogroup libanalyze
* @{ */
/** @file analyze/contour.h
*
* Ray-driven manifold dual contouring of evaluated BRL-CAD geometry.
*/
#ifndef ANALYZE_CONTOUR_H
#define ANALYZE_CONTOUR_H
#include "common.h"
#include <stddef.h>
#include "vmath.h"
#include "analyze/defines.h"
__BEGIN_DECLS
#define ANALYZE_MDC_DEFAULT_MAX_RAYS 0
#define ANALYZE_MDC_DEFAULT_MINIMUM_FREE_MEM 150000000
#define ANALYZE_MDC_DEFAULT_MIN_DEPTH 2
#define ANALYZE_MDC_DEFAULT_MAX_DEPTH 10
#define ANALYZE_MDC_MAX_DEPTH 20
#define ANALYZE_MDC_DEFAULT_MAX_TIME 600
struct db_i;
/** Resource and accuracy controls for analyze_mdc(). */
struct analyze_mdc_params {
/** Requested maximum finest-cell edge length. Zero selects an
* implementation default based on the model bounds. */
fastf_t feature_size;
/** Optional user ceiling for the automatically derived ray budget.
* Zero applies only geometry, depth, time, and memory limits. */
size_t max_rays;
/** Abort before available memory drops below this many bytes. */
size_t minimum_free_mem;
/** Minimum and maximum power-of-two grid depths. */
int min_depth;
int max_depth;
/** Maximum wall-clock processing time in seconds. Zero is unlimited. */
int max_time;
/** Emit progress diagnostics when non-zero. */
int verbosity;
};
#define ANALYZE_MDC_PARAMS_DEFAULT { \
0.0, \
ANALYZE_MDC_DEFAULT_MAX_RAYS, \
ANALYZE_MDC_DEFAULT_MINIMUM_FREE_MEM, \
ANALYZE_MDC_DEFAULT_MIN_DEPTH, \
ANALYZE_MDC_DEFAULT_MAX_DEPTH, \
ANALYZE_MDC_DEFAULT_MAX_TIME, \
0 \
}
enum analyze_mdc_status {
ANALYZE_MDC_OK = 0,
ANALYZE_MDC_INVALID_INPUT = 1,
ANALYZE_MDC_PREP_FAILED = 2,
ANALYZE_MDC_NO_SURFACE = 3,
ANALYZE_MDC_RAY_LIMIT = 4,
ANALYZE_MDC_TIMEOUT = 5,
ANALYZE_MDC_MEMORY_LIMIT = 6,
ANALYZE_MDC_AMBIGUOUS = 7,
ANALYZE_MDC_NOT_MANIFOLD = 8
};
/**
* Evaluate @p object with librt and construct an indexed triangle mesh.
*
* On success, the caller owns @p faces and @p vertices and must release them
* with bu_free(). Failure leaves all outputs empty.
*/
ANALYZE_EXPORT extern enum analyze_mdc_status
analyze_mdc(int **faces, size_t *num_faces, point_t **vertices,
size_t *num_vertices, const char *object, struct db_i *dbip,
const struct analyze_mdc_params *params);
__END_DECLS
#endif /* ANALYZE_CONTOUR_H */
/** @} */
/*
* Local Variables:
* mode: C
* tab-width: 8
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8
*/
+22 -2
View File
@@ -43,9 +43,29 @@ struct analyze_polygonize_params {
size_t minimum_free_mem;
int verbosity;
};
#define ANALYZE_POLYGONIZE_PARAMS_DEFAULT { 0, 0, 30, 150000000, 0 }
#define ANALYZE_POLYGONIZE_DEFAULT_MAX_CYCLE_TIME 30
#define ANALYZE_POLYGONIZE_DEFAULT_MINIMUM_FREE_MEM 150000000
#define ANALYZE_POLYGONIZE_PARAMS_DEFAULT { 0, 0, \
ANALYZE_POLYGONIZE_DEFAULT_MAX_CYCLE_TIME, \
ANALYZE_POLYGONIZE_DEFAULT_MINIMUM_FREE_MEM, 0 }
/**
* Legacy polygonization interface, implemented by analyze_mdc().
*
* @p size is the requested maximum cell edge length. @p p_s is retained for
* source and binary compatibility but is no longer used as a starting seed.
* The caller owns successful output arrays and must release them with
* bu_free(). Failure leaves all outputs empty.
*
* @return 0 on success
* @return -1 on invalid input or polygonization failure
* @return 2 when a time limit is reached
* @return 3 when the minimum-free-memory limit is reached
*/
ANALYZE_EXPORT extern int
analyze_polygonize(int **faces, int *num_faces, point_t **vertices, int *num_vertices, fastf_t size, point_t p_s, const char *obj, struct db_i *dbip, struct analyze_polygonize_params *p);
analyze_polygonize(int **faces, int *num_faces, point_t **vertices,
int *num_vertices, fastf_t size, point_t p_s, const char *obj,
struct db_i *dbip, struct analyze_polygonize_params *p);
__END_DECLS
+1
View File
@@ -55,6 +55,7 @@
#include "bg/clip.h"
#include "bg/lseg.h"
#include "bg/obr.h"
#include "bg/pca.h"
#include "bg/plane.h"
#include "bg/polygon.h"
#include "bg/sat.h"
+4 -4
View File
@@ -50,12 +50,12 @@ BG_EXPORT extern int bg_lseg_clip(fastf_t *xp1, fastf_t *yp1, fastf_t *xp2, fast
* The RPP has faces parallel to the coordinate planes and is defined
* by a minimum point and a maximum point.
*
* FIXME: the function name implies this takes a point,dir for a,b but
* it actually takes a line segment going from points a to b!
* Despite its historical name, this function clips the finite line segment
* from a to b.
*
* Returns -
* 0 if ray does not hit RPP,
* !0 if ray hits RPP.
* 0 if the segment does not hit the RPP,
* !0 if the segment hits the RPP.
*
* Implicit Return -
* if !0 was returned, "a" and "b" have been clipped to the RPP.
+6 -3
View File
@@ -126,9 +126,12 @@ BG_EXPORT extern int bg_3d_coplanar_obr(point_t *center,
* \endverbatim
*
*
* @param[out] pnts eight points of oriented bounding box
* @param points_3d array of 3D points
* @param pnt_cnt number of points in pnts array
* @param[out] pnts eight non-NULL, caller-provided point_t storage
* locations for the oriented bounding box vertices
* @param[in] points_3d flat array containing 3 * pnt_cnt coordinate values
* @param[in] pnt_cnt number of points in points_3d
*
* @return 0 on success and -1 for invalid input or when no box can be fit
*/
BG_EXPORT extern int bg_3d_obb(point_t **pnts,
const fastf_t *points_3d,
+4 -4
View File
@@ -21,7 +21,7 @@
/*----------------------------------------------------------------------*/
/** @addtogroup bg_pca
*
* Principle Component Analysis
* Principal Component Analysis
*
* Calculates an XYZ coordinate system such that it aligns with the largest
* variations in the supplied data. Intuitively, it "aligns" with the
@@ -50,7 +50,7 @@
* point_t p;
* for (size_t i = 0; i < pntcnt; i++) {
* MAT4X3PNT(p, RT, pnts[i]);
* VMOVE(pnts[i], v);
* VMOVE(pnts[i], p);
* }
* @endcode
*/
@@ -69,7 +69,7 @@ __BEGIN_DECLS
/**
* @brief
* Perform a Principle Component Analysis on a set of points.
* Perform a Principal Component Analysis on a set of points.
*
* Outputs are a center point and XYZ vectors for the coordinate system.
*
@@ -88,7 +88,7 @@ BG_EXPORT extern int bg_pca(point_t *c, vect_t *xaxis, vect_t *yaxis, vect_t *za
__END_DECLS
#endif /* BG_PLANE_H */
#endif /* BG_PCA_H */
/** @} */
/*
* Local Variables:
+7 -4
View File
@@ -1171,9 +1171,10 @@ BG_EXPORT extern int bg_isect_planes(point_t pt,
/**
* @brief
* Given an origin and a normal, create a plane_t.
* Given an origin and a normal, create a plane_t with a unit normal.
* The input point and normal are not modified. A zero-length normal fails.
*/
BG_EXPORT extern int bg_plane_pt_nrml(plane_t *p, point_t pt, vect_t nrml);
BG_EXPORT extern int bg_plane_pt_nrml(plane_t *p, const point_t pt, const vect_t nrml);
/**
* @brief
@@ -1188,13 +1189,15 @@ BG_EXPORT extern int bg_fit_plane(point_t *c, vect_t *n, size_t npnts, point_t *
/**
* @brief
* Find the closest U,V point on the plane p to 3d point pt.
* Find the closest U,V point on the plane p to 3d point pt. The plane normal
* need not be unit length.
*/
BG_EXPORT extern int bg_plane_closest_pt(fastf_t *u, fastf_t *v, plane_t *p, point_t *pt);
/**
* @brief
* Return the 3D point on the plane at parametric coordinates u, v.
* Return the 3D point on the plane at parametric coordinates u, v. The plane
* normal need not be unit length.
*/
BG_EXPORT extern int bg_plane_pt_at(point_t *pt, plane_t *p, fastf_t u, fastf_t v);
+64 -10
View File
@@ -199,6 +199,68 @@ bg_nested_poly_triangulate(int **faces, int *num_faces, point2d_t **out_pts, int
const int *steiner, const size_t steiner_npts,
const point2d_t *pts, const size_t npts, triangulation_t type);
enum bg_triangulation_reason {
BG_TRIANGULATION_OK = 0,
BG_TRIANGULATION_INVALID_INPUT = 1,
BG_TRIANGULATION_INVALID_PSLG = 2,
BG_TRIANGULATION_CROSSING_CONSTRAINTS = 3,
BG_TRIANGULATION_INVALID_NESTING = 4,
BG_TRIANGULATION_DETRIA_FAILED = 5,
BG_TRIANGULATION_POSTCONDITION_FAILED = 6
};
struct bg_triangulation_report {
enum bg_triangulation_reason reason;
int input_index;
char message[128];
};
/**
* @brief
* Sanitize and triangulate a 2D polygon with holes.
*
* This variant returns a newly allocated point array because topology repair
* may merge points, split intersections, or reorder contours. It first
* removes duplicate and boundary-coincident unconstrained points. If needed,
* it then uses integer Clipper operations to reconstruct valid contour
* nesting before constrained Delaunay triangulation. Optional noncrossing
* interior constraints contain two input point indices per edge. Constraints
* removed with canceled contour regions are omitted; every surviving
* constraint is certified in its cleaned component.
*
* The caller must free both faces and out_pts with bu_free.
*
* @param[out] report optional structured success or failure detail
*
* @return 0 if triangulation is successful
* @return 1 if triangulation is unsuccessful
*/
BG_EXPORT extern int
bg_nested_poly_triangulate_clean(int **faces, int *num_faces,
point2d_t **out_pts, int *num_outpts,
const int *poly, const size_t poly_npts,
const int **holes_array, const size_t *holes_npts, const size_t nholes,
const int *steiner, const size_t steiner_npts,
const int *constraints, const size_t constraint_cnt,
const point2d_t *pts, const size_t npts,
struct bg_triangulation_report *report);
/**
* Strict constrained-Delaunay entry point. In addition to the validation
* performed by bg_nested_poly_triangulate, this function reports the failure
* class and certifies the returned constraints, orientation, incidence, and
* chart coverage. out_pts aliases pts on success and must not be freed.
*/
BG_EXPORT extern int
bg_nested_poly_triangulate_strict(int **faces, int *num_faces,
point2d_t **out_pts, int *num_outpts,
const int *poly, const size_t poly_npts,
const int **holes_array, const size_t *holes_npts, const size_t nholes,
const int *steiner, const size_t steiner_npts,
const int *constraints, const size_t constraint_cnt,
const point2d_t *pts, const size_t npts,
struct bg_triangulation_report *report);
/**
* @brief
* Triangulate a 2D polygon without holes.
@@ -253,15 +315,6 @@ BG_EXPORT extern int
bg_polygon_triangulate(int **faces, int *num_faces, point_t **out_pts, int *num_outpts,
struct bg_polygon *p, triangulation_t type);
/* Test function - do not use */
BG_EXPORT extern int
bg_poly2tri_test(int **faces, int *num_faces, point2d_t **out_pts, int *num_outpts,
const int *poly, const size_t poly_pnts,
const int **holes_array, const size_t *holes_npts, const size_t nholes,
const int *steiner, const size_t steiner_npts,
const point2d_t *pts);
/*********************************************************
Operations on 3D point types - these are assumed to be
polygons embedded in 3D planes in space
@@ -293,6 +346,7 @@ BG_EXPORT extern int bg_3d_polygon_area(fastf_t *area, size_t npts, const point_
* @param[in] npts Number of point_ts, stored in pts
* @param[in] pts all points of the polygon, sorted counter-clockwise.
* The array mustn't contain duplicated points or non-coplanar points.
* A zero-area polygon fails.
*
* @return 0 if calculation was successful
* @return 1 if calculation failed, e.g. because one in-parameter is a NULL-pointer
@@ -307,7 +361,7 @@ BG_EXPORT extern int bg_3d_polygon_centroid(point_t *cent, size_t npts, const po
*@param[in] npts Number of points, pts contains
*@param pts Array of point_ts, building a convex polygon. Duplicated points
*aren't allowed. The points in the array will be sorted counter-clockwise.
*@param[in] cmp Plane equation of the polygon
*@param[in] cmp Plane equation of the polygon. Its normal must be nonzero.
*
*@return 0 if calculation was successful
*@return 1 if calculation failed, e.g. because pts is a NULL-pointer
+20 -8
View File
@@ -39,7 +39,9 @@
__BEGIN_DECLS
/**
* Test for an intersection between a line and an Axis-Aligned Bounding Box (AABB).
* Test for an intersection between an infinite line and an Axis-Aligned
* Bounding Box (AABB). ldir need not be unit length, but must be nonzero.
* aabb_extent supplies the positive half extents along the coordinate axes.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -50,7 +52,10 @@ bg_sat_line_aabb(
);
/**
* Test for an intersection between a line and an Oriented Bounding Box (OBB).
* Test for an intersection between an infinite line and an Oriented Bounding
* Box (OBB). ldir need not be unit length, but must be nonzero. Each OBB
* extent vector points from the center to a face center; nonzero vectors must
* be mutually perpendicular, and a zero vector represents a degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -61,7 +66,9 @@ bg_sat_line_obb(
);
/**
* Test for an intersection between a triangle and an Axis-Aligned Bounding Box (AABB).
* Test for an intersection between a triangle and an Axis-Aligned Bounding
* Box (AABB). extent supplies the positive half extents along the coordinate
* axes.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -72,7 +79,10 @@ bg_sat_tri_aabb(
);
/**
* Test for an intersection between a triangle and an Oriented Bounding Box (OBB).
* Test for an intersection between a triangle and an Oriented Bounding Box
* (OBB). Each extent vector points from the center to a face center; nonzero
* vectors must be mutually perpendicular, and a zero vector represents a
* degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -85,8 +95,9 @@ bg_sat_tri_obb(
/**
* Test for an intersection between an Axis-Aligned Bounding Box (AABB) and an
* Oriented Bounding Box (OBB). The latter is defined by a center point and
* three perpendicular vectors from the center to the centers of the various
* faces.
* three extent vectors from the center to the centers of the various faces.
* Nonzero extent vectors must be mutually perpendicular; a zero vector
* represents a degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -98,8 +109,9 @@ bg_sat_aabb_obb(
/**
* Test for an intersection between two Oriented Bounding Boxes (OBBs). The
* boxes are defined by a center point and three perpendicular vectors from the
* center to the centers of the various faces.
* boxes are defined by a center point and three extent vectors from the center
* to the centers of the various faces. Nonzero extent vectors must be
* mutually perpendicular; a zero vector represents a degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
+129 -7
View File
@@ -67,6 +67,7 @@
#define BG_SPSR_H
#include "common.h"
#include <stdint.h>
#include "vmath.h"
#include "bg/defines.h"
@@ -85,8 +86,8 @@ struct bg_3d_spsr_opts {
int full_depth; /**< full depth */
int base_depth; /**< coarse MG solver depth */
int baseVcycles; /**< coarse MG solver v-cycles */
int max_memory_GB; /**< maximum memory (in GB) */
size_t threads; /**< number of threads to use (default is max available) */
int max_memory_GB; /**< reserved; currently not enforced */
size_t threads; /**< one selects serial; otherwise uses the configured pool */
fastf_t samples_per_node; /**< minimum number of samples per node */
fastf_t scale; /**< scale factor */
fastf_t width; /**< voxel width */
@@ -109,7 +110,7 @@ struct bg_3d_spsr_opts {
#define BG_3D_SPSR_BOUNDARY_DIRICHLET 3
#define BG_3D_SPSR_DEFAULT_DEGREE 1 /* DEFAULT_FEM_DEGREE */
#define BG_3D_SPSR_DEFAULT_DEPTH 11
#define BG_3D_SPSR_DEFAULT_DEPTH 8
#define BG_3D_SPSR_DEFAULT_KERNELDEPTH 0
#define BG_3D_SPSR_DEFAULT_ITERATIONS 0
#define BG_3D_SPSR_DEFAULT_FULL_DEPTH 0
@@ -117,13 +118,13 @@ struct bg_3d_spsr_opts {
#define BG_3D_SPSR_DEFAULT_BASEVCYCLES 0
#define BG_3D_SPSR_DEFAULT_MAX_MEM 0
#define BG_3D_SPSR_DEFAULT_THREADS 0
#define BG_3D_SPSR_DEFAULT_SAMPLES_PER_NODE 1.1
#define BG_3D_SPSR_DEFAULT_SCALE 1.0
#define BG_3D_SPSR_DEFAULT_SAMPLES_PER_NODE 1.5
#define BG_3D_SPSR_DEFAULT_SCALE 1.1
#define BG_3D_SPSR_DEFAULT_WIDTH 0.0
#define BG_3D_SPSR_DEFAULT_CONFIDENCE 0.0
#define BG_3D_SPSR_DEFAULT_CONFIDENCE_BIAS 0.0
#define BG_3D_SPSR_DEFAULT_CGSOLVER_ACCURACY 1.0e-3
#define BG_3D_SPSR_DEFAULT_POINT_WEIGHT 8.0 /* DefaultPointWeightMultiplier * Degree */
#define BG_3D_SPSR_DEFAULT_POINT_WEIGHT 2.0
#define BG_3D_SPSR_DEFAULT_NONMANIFOLD 0
#define BG_3D_SPSR_DEFAULT_LINEARFIT 0
#define BG_3D_SPSR_DEFAULT_EXACT 1
@@ -167,10 +168,131 @@ struct bg_3d_spsr_opts {
* @return 0 if successful, else error
*
*/
BG_EXPORT int bg_3d_spsr(int **faces, int *num_faces, point_t **vertices, int *num_vertices,
DEPRECATED BG_EXPORT int bg_3d_spsr(int **faces, int *num_faces, point_t **vertices, int *num_vertices,
const point_t *input_points_3d, const vect_t *input_normals_3d,
int num_input_pnts, struct bg_3d_spsr_opts *opts);
/** Reasons the SPSR solver may request additional source samples. */
#define BG_3D_SPSR_REFINE_RESIDUAL 0x01u
#define BG_3D_SPSR_REFINE_NORMAL 0x02u
#define BG_3D_SPSR_REFINE_DENSITY 0x04u
#define BG_3D_SPSR_REFINE_SURFACE_VARIATION 0x08u
/** Adaptive SPSR termination states. */
#define BG_3D_SPSR_COMPLETE 0
#define BG_3D_SPSR_CALLBACK_STOP 1
#define BG_3D_SPSR_POINT_LIMIT 2
#define BG_3D_SPSR_TIME_LIMIT 3
#define BG_3D_SPSR_NO_NEW_SAMPLES 4
#define BG_3D_SPSR_SOLVER_ERROR 5
/**
* One oriented source sample. A positive @p thickness records the length of
* the solid ray partition that supplied the sample. Non-zero equal pair IDs
* identify the entry and exit samples of the same partition.
*/
struct bg_3d_spsr_sample {
point_t point;
vect_t normal;
fastf_t thickness;
uint64_t pair_id;
};
/** A spatial location at which the solver would benefit from more samples. */
struct bg_3d_spsr_refinement_hint {
point_t point;
vect_t normal;
fastf_t error;
unsigned int reasons;
};
/** Source-to-candidate measurements supplied by an adaptive callback. */
struct bg_3d_spsr_validation {
size_t ray_count;
size_t source_hit_rays;
size_t partition_mismatch_rays;
size_t thin_partition_count;
size_t thin_partition_mismatches;
fastf_t endpoint_error_p95;
fastf_t endpoint_error_p99;
fastf_t chord_error_fraction;
fastf_t surface_area_error_fraction;
fastf_t volume_error_fraction;
int passed;
};
/** Information passed to an adaptive source-sampling callback. */
struct bg_3d_spsr_refinement_request {
size_t pass;
fastf_t target_feature_size;
double remaining_time;
const point_t *vertices;
size_t vertex_count;
const int *faces;
size_t face_count;
const struct bg_3d_spsr_refinement_hint *hints;
size_t hint_count;
};
/**
* Callback response. Sample storage remains owned by the callback and need
* only remain valid until the callback returns; libbg copies it immediately.
*/
struct bg_3d_spsr_refinement_response {
const struct bg_3d_spsr_sample *samples;
size_t sample_count;
struct bg_3d_spsr_validation validation;
/** Non-zero asks the adaptive driver to stop after this candidate. */
int stop_refinement;
};
typedef int (*bg_3d_spsr_refinement_func_t)(
struct bg_3d_spsr_refinement_response *response,
const struct bg_3d_spsr_refinement_request *request,
void *client_data);
/** Options controlling a bounded adaptive SPSR reconstruction. */
struct bg_3d_spsr_adaptive_opts {
struct bg_3d_spsr_opts solver;
size_t max_refinement_passes;
size_t max_points;
fastf_t target_feature_size;
double max_time;
};
/** Last-resort solve budget; callbacks may stop earlier when progress stalls. */
#define BG_3D_SPSR_DEFAULT_REFINEMENT_PASSES 8
#define BG_3D_SPSR_ADAPTIVE_OPTS_DEFAULT { \
BG_3D_SPSR_OPTS_DEFAULT, \
BG_3D_SPSR_DEFAULT_REFINEMENT_PASSES, \
0, \
0.0, \
0.0 }
/** Summary of an adaptive SPSR attempt. */
struct bg_3d_spsr_report {
size_t solve_count;
size_t initial_sample_count;
size_t final_sample_count;
size_t requested_sample_count;
size_t accepted_sample_count;
double elapsed_time;
int termination;
struct bg_3d_spsr_validation validation;
};
/**
* Reconstruct a surface and, when requested, obtain targeted additional
* samples from @p refine. A callback is required when refinement passes are
* enabled. The final candidate must be marked as passed by the callback.
*/
BG_EXPORT int bg_3d_spsr_adaptive(int **faces, int *num_faces,
point_t **vertices, int *num_vertices,
const struct bg_3d_spsr_sample *input_samples, size_t sample_count,
const struct bg_3d_spsr_adaptive_opts *opts,
bg_3d_spsr_refinement_func_t refine, void *client_data,
struct bg_3d_spsr_report *report);
__END_DECLS
#endif /* BG_SPSR_H */
+1 -1
View File
@@ -52,7 +52,7 @@ BG_EXPORT extern double bg_tri_closest_pt(
__END_DECLS
#endif /* BG_TRI_TRI_H */
#endif /* BG_TRI_PT_H */
/** @} */
/*
* Local Variables:
+1 -1
View File
@@ -54,7 +54,7 @@ BG_EXPORT extern int bg_isect_tri_line(point_t *isect,
* If isect is non-NULL, it is written only when an intersection is found.
*/
BG_EXPORT extern int bg_isect_tri_ray(const point_t orig,
const point_t dir,
const vect_t dir,
const point_t vert0,
const point_t vert1,
const point_t vert2,
+118 -3
View File
@@ -84,10 +84,28 @@ BG_EXPORT extern int bg_trimesh_manifold_closed(int vcnt, int fcnt, fastf_t *v,
*/
BG_EXPORT extern int bg_trimesh_oriented(int vcnt, int fcnt, fastf_t *v, int *f);
/**
* Check whether the bundled Manifold library accepts an indexed triangle
* mesh and returns a topologically equivalent closed mesh. This is a
* read-only certification check; it does not expose Manifold's normalized
* output or perform a Boolean union.
*
* @return 1 if accepted and independently validated, 0 otherwise.
*
* Invalid or resource-exhausted inputs also return 0; C++ exceptions are not
* propagated across this C API.
*/
BG_EXPORT extern int bg_trimesh_manifold_accepted(int vcnt, int fcnt,
const fastf_t *v, const int *f);
/**
* Check if a mesh is topologically solid. Returns 1 if the mesh is NOT SOLID
* and 0 if the mesh is SOLID. A SOLID (0) outcome indicates the mesh satisfies
* all three criteria: Closed, Manifold, Oriented
* all three criteria: Closed, Manifold, Oriented. If @p bedges is non-NULL,
* it is set to NULL when no edge diagnostics are present, or to a bu_calloc()
* allocated array of vertex-index pairs for unmatched, misoriented, and excess
* edges. The caller must bu_free() a non-NULL result. Use
* bg_trimesh_solid2() when structured diagnostics and their counts are needed.
*/
BG_EXPORT extern int bg_trimesh_solid(int vcnt, int fcnt, fastf_t *v, int *f, int **bedges);
@@ -352,8 +370,8 @@ BG_EXPORT extern int bg_trimesh_3d_gc(int **ofaces, point_t **opnts, int *n_opnt
* @param[in] f input set of faces.
* @param[in] fcnt input face count
*
* @return -1 if error, otherwise return the number of times a face flipping
* operation was performed
* @return -1 if the input is invalid or a connected component is
* nonorientable, otherwise return the number of face flips performed
*/
BG_EXPORT extern int
bg_trimesh_sync(int *of, int *f, int fcnt);
@@ -568,6 +586,103 @@ bg_trimesh_repair(
struct bg_trimesh_repair_opts *opts);
/**
* Conservative controls for triangle mesh repair.
*
* Unlike the legacy bg_trimesh_repair_opts interface, component removal and
* hole filling are disabled unless their corresponding flags are set. A
* zero vertex_tolerance selects 1e-8 times the input bounding-box diagonal.
* Absolute area limits take precedence over percentage limits. A zero area
* limit with fill_holes enabled permits holes of any area, subject to
* max_hole_edges. A zero max_hole_edges permits any boundary length.
* union_components requests a regularized Manifold union of closed output
* components, resolving compatible bounded overlaps; it is disabled by
* default because it may change connectivity and triangulation.
* separate_touching_vertices moves disconnected duplicate-coordinate fans
* apart along their area-weighted normals by no more than vertex_tolerance.
* It is disabled by default because it changes vertex positions.
* allow_self_intersections permits hole caps and repaired surfaces to cross;
* topological solid, orientation, degeneracy, and vertex-link checks remain
* mandatory. require_manifold verifies that the bundled Manifold library can
* import the final indexed mesh. Both policies are disabled by default.
*/
struct bg_trimesh_repair_settings {
fastf_t vertex_tolerance;
int remove_small_components;
fastf_t max_component_area;
fastf_t max_component_area_percent;
int fill_holes;
fastf_t max_hole_area;
fastf_t max_hole_area_percent;
size_t max_hole_edges;
int max_iterations;
int separate_touching_vertices;
int union_components;
int allow_self_intersections;
int require_manifold;
int require_solid;
};
#define BG_TRIMESH_REPAIR_SETTINGS_INIT {0.0, 0, 0.0, 0.0, 0, 0.0, 0.0, 0, 10, 0, 0, 0, 0, 1}
/** Summary of the operations performed by bg_trimesh_repair_ex. */
struct bg_trimesh_repair_report {
int input_vertices;
int input_faces;
int output_vertices;
int output_faces;
int removed_faces;
int added_faces;
int repair_iterations;
int separated_vertices;
int component_union_applied;
int manifold_normalization_applied;
int self_intersections_allowed;
int manifold_accepted;
int solid;
int rejected_hole_faces;
int geometric_degenerate_faces;
int unmatched_edges;
int excess_edges;
int misoriented_edges;
int invalid_vertex_links;
int reoriented_faces;
fastf_t input_area;
fastf_t output_area;
fastf_t output_volume;
fastf_t max_vertex_displacement;
/** An allocation failure prevented completion of the repair attempt. */
int allocation_failed;
};
#define BG_TRIMESH_REPAIR_REPORT_INIT {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0.0, 0}
/**
* Attempt a bounded, explicitly configured triangle mesh repair.
*
* Unless separate_touching_vertices is enabled, current operations preserve
* input vertex positions: welding selects an existing representative,
* topology splitting duplicates an existing point, and hole filling
* triangulates existing boundary vertices. The report records the maximum
* displacement when point-contact separation is requested.
*
* @return 1 if the input was already solid, 0 if repair produced an accepted
* output, and -1 on invalid input or if the requested postconditions were not
* satisfied. On return 1, the output pointers remain NULL and their counts
* remain zero unless topology normalization required an exported copy.
* Allocation and library exceptions are converted to return value -1.
* The report distinguishes allocation failure from geometric rejection.
*/
BG_EXPORT extern int
bg_trimesh_repair_ex(
int **ofaces, int *n_ofaces,
point_t **opnts, int *n_opnts,
const int *ifaces, int n_ifaces,
const point_t *ipnts, int n_ipnts,
const struct bg_trimesh_repair_settings *settings,
struct bg_trimesh_repair_report *report);
/**
* Options governing triangle mesh remeshing operations.
*
+15 -12
View File
@@ -214,8 +214,8 @@ BN_EXPORT extern void bn_mat_ae(mat_t m,
double elev);
/**
* Find the azimuth and elevation angles that correspond to the
* direction (not including twist) given by a direction vector.
* Find the azimuth and elevation angles, in degrees, that correspond
* to the direction (not including twist) given by a direction vector.
*/
BN_EXPORT extern void bn_ae_vec(fastf_t *azp,
fastf_t *elp,
@@ -236,14 +236,16 @@ BN_EXPORT extern void bn_aet_vec(fastf_t *az,
fastf_t accuracy);
/**
* Find a unit vector from the origin given azimuth and elevation.
* Find a unit vector from the origin given azimuth and elevation in
* radians.
*/
BN_EXPORT extern void bn_vec_ae(vect_t vec,
fastf_t az,
fastf_t el);
/**
* Find a vector from the origin given azimuth, elevation, and distance.
* Find a vector from the origin given azimuth and elevation in radians,
* and distance.
*/
BN_EXPORT extern void bn_vec_aed(vect_t vec,
fastf_t az,
@@ -310,8 +312,10 @@ BN_EXPORT extern void bn_vec_perp(vect_t new_vec,
* space by the angle between the two. There are many candidate
* matrices.
*
* The input 'from' and 'to' vectors need not be unit length.
* MAT4X3VEC(to, m, from) is the identity that is created.
* The input 'from' and 'to' vectors need not be unit length. They are
* normalized internally. Near-parallel inputs are handled using the
* angular tolerance in 'tol'. If either input cannot be normalized,
* the result is the identity matrix.
*
*/
BN_EXPORT extern void bn_mat_fromto(mat_t m,
@@ -344,12 +348,11 @@ BN_EXPORT extern void bn_mat_zrot(mat_t m,
double cosz);
/**
* Given a direction vector D of unit length, product a matrix which
* rotates that vector D onto the -Z axis. This matrix will be
* suitable for use as a "model2view" matrix.
*
* XXX This routine will fail if the vector is already more or less
* aligned with the Z axis.
* Given a nonzero direction vector D, produce a matrix which
* rotates D onto the -Z axis. D is normalized internally. Z-axis
* directions use a deterministic roll, and a zero direction produces
* the identity matrix. This matrix will be suitable for use as a
* model2view matrix.
*
* This is done in several steps.
*
+31
View File
@@ -37,11 +37,42 @@ __BEGIN_DECLS
typedef struct bn_multipoly {
uint32_t magic;
/**< Number of coefficients in the first polynomial variable. */
int dgrs;
/**< Number of coefficients in the second polynomial variable. */
int dgrt;
/**< Coefficient of S^s * T^t is cf[s][t]. */
double **cf;
} bn_multipoly_t;
/**
* Allocate a zero-valued bivariate polynomial with the specified
* coefficient counts. Both counts must be positive.
*/
BN_EXPORT extern struct bn_multipoly *bn_multipoly_new(int dgrs, int dgrt);
/** Release a bivariate polynomial allocated by bn_multipoly_new(). */
BN_EXPORT extern void bn_multipoly_free(struct bn_multipoly *poly);
/** Grow a polynomial to at least the specified coefficient counts. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_grow(struct bn_multipoly *poly,
int dgrs,
int dgrt);
/** Set the coefficient of S^s * T^t, growing the polynomial as needed. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_set(struct bn_multipoly *poly,
int s,
int t,
double value);
/** Return the sum of two bivariate polynomials. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_add(const struct bn_multipoly *p1,
const struct bn_multipoly *p2);
/** Return the product of two bivariate polynomials. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_mul(const struct bn_multipoly *p1,
const struct bn_multipoly *p2);
__END_DECLS
#endif /* BN_MULTIPOLY_H */
+20 -4
View File
@@ -45,7 +45,10 @@ __BEGIN_DECLS
/**
* Polynomial data type
* bn_poly->cf[n] corresponds to X^n
*
* Coefficients are stored from the highest to the lowest degree:
* `cf[0]` is the coefficient of `X^dgr` and `cf[dgr]` is the
* constant coefficient.
*/
typedef struct bn_poly {
uint32_t magic;
@@ -63,6 +66,9 @@ typedef struct bn_poly {
*
* @brief
* multiply two polynomials
*
* The output may alias either input. If the result exceeds
* BN_MAX_POLY_DEGREE, returns BN_POLY_NULL without modifying product.
*/
BN_EXPORT extern struct bn_poly *bn_poly_mul(struct bn_poly *product,
const struct bn_poly *m1,
@@ -81,6 +87,8 @@ BN_EXPORT extern struct bn_poly *bn_poly_scale(struct bn_poly *eqn,
* bn_poly_add
* @brief
* add two polynomials
*
* The output may alias either input.
*/
BN_EXPORT extern struct bn_poly *bn_poly_add(struct bn_poly *sum,
const struct bn_poly *poly1,
@@ -90,6 +98,8 @@ BN_EXPORT extern struct bn_poly *bn_poly_add(struct bn_poly *sum,
* bn_poly_sub
* @brief
* subtract two polynomials
*
* The output may alias either input.
*/
BN_EXPORT extern struct bn_poly *bn_poly_sub(struct bn_poly *diff,
const struct bn_poly *poly1,
@@ -98,7 +108,10 @@ BN_EXPORT extern struct bn_poly *bn_poly_sub(struct bn_poly *diff,
/**
* @brief
* Divides any polynomial into any other polynomial using synthetic
* division. Both polynomials must have real coefficients.
* division. Both polynomials must have real coefficients and the
* divisor's leading coefficient must be nonzero. The quotient and
* remainder outputs must be distinct; the quotient may alias the
* dividend. All other output/input pairs must be distinct.
*/
BN_EXPORT extern void bn_poly_synthetic_division(struct bn_poly *quo,
struct bn_poly *rem,
@@ -108,7 +121,8 @@ BN_EXPORT extern void bn_poly_synthetic_division(struct bn_poly *quo,
/**
*@brief
* Uses the quadratic formula to find the roots (in `complex' form) of
* any quadratic equation with real coefficients.
* any quadratic equation with real coefficients. A nonzero leading
* coefficient need not be one.
*
* @return 1 for success
* @return 0 for fail.
@@ -144,6 +158,7 @@ BN_EXPORT extern int bn_poly_quadratic_roots(struct bn_complex roots[],
* If D < 0, there will be three unequal real roots.
*
* Returns 1 for success, 0 for fail.
* A nonzero leading coefficient need not be one.
*/
BN_EXPORT extern int bn_poly_cubic_roots(struct bn_complex roots[],
const struct bn_poly *eqn);
@@ -151,7 +166,8 @@ BN_EXPORT extern int bn_poly_cubic_roots(struct bn_complex roots[],
/**
*@brief
* Uses the quartic formula to find the roots (in `complex' form)
* of any quartic equation with real coefficients.
* of any quartic equation with real coefficients. A nonzero leading
* coefficient need not be one.
*
* @return 1 for success
* @return 0 for fail.
+4 -3
View File
@@ -100,8 +100,8 @@ BN_EXPORT extern void quat_double(quat_t qout,
/**
*@brief Gives the bisector of quaternions q1 and q2.
*
* (Could be done with quat_slerp and factor 0.5)
* [I believe they must be unit quaternions this to work]
* Inputs must be unit quaternions. Antipodal inputs use a valid
* great-circle midpoint.
*/
BN_EXPORT extern void quat_bisect(quat_t qout,
const quat_t q1,
@@ -161,7 +161,8 @@ BN_EXPORT extern void quat_exp(quat_t out,
/**
*@brief
* Take the natural logarithm of a unit quaternion.
* Take the natural logarithm of a unit quaternion. The identity
* quaternion maps to the zero quaternion.
* Code by Ken Shoemake.
*/
BN_EXPORT extern void quat_log(quat_t out,
+6
View File
@@ -41,6 +41,7 @@
extern "C++" {
namespace brlcad {
class BBNode;
class SurfaceTree;
}
}
#endif
@@ -152,8 +153,13 @@ namespace brlcad {
private:
friend class SurfaceTree;
BBNode(const BBNode &source);
BBNode &operator=(const BBNode &source);
ON_2dPoint getClosestPointEstimate(const ON_3dPoint &pt,
ON_Interval &u, ON_Interval &v,
const ON_Surface *surface) const;
void removeChild(BBNode *child);
bool intersectedBy(const ON_Ray &ray, double *tnear = NULL, double *tfar = NULL) const;
+7
View File
@@ -33,6 +33,7 @@
#ifdef __cplusplus
extern "C++" {
/* @cond */
# include <cstddef>
# include <vector>
# include <list>
/* @endcond */
@@ -77,6 +78,12 @@ namespace brlcad {
/** Report the depth of this node in the hierarchy */
int depth() const;
/**
* Return the conservative per-node storage estimate used to translate
* caller byte budgets into bounded hierarchy work.
*/
static std::size_t estimated_allocation_size();
/**
* Get 2 points defining bounding box:
*
+532 -18
View File
@@ -34,6 +34,7 @@
#include "bv/vlist.h"
#include "bn/tol.h"
#include "bg/defines.h"
#include "bg/trimesh.h"
#include "brep/defines.h"
__BEGIN_DECLS
@@ -41,29 +42,351 @@ __BEGIN_DECLS
/* Container that holds the state of a triangulation */
struct ON_Brep_CDT_State;
/* Create and initialize a CDT state with default tolerances. bv
/* Conversion-quality tessellation result categories. These supplement the
* legacy integer return from ON_Brep_CDT_Tessellate with a stable reason for
* failure. */
#define BREP_CDT_RESULT_UNATTEMPTED 0
#define BREP_CDT_RESULT_SUCCESS 1
#define BREP_CDT_RESULT_PARTIAL 2
#define BREP_CDT_RESULT_INVALID_BREP -1
#define BREP_CDT_RESULT_INVALID_TOLERANCE -2
#define BREP_CDT_RESULT_INITIALIZATION_FAILED -3
#define BREP_CDT_RESULT_FACE_FAILED -4
#define BREP_CDT_RESULT_MESH_EXPORT_FAILED -5
#define BREP_CDT_RESULT_NON_SOLID -6
#define BREP_CDT_RESULT_INVALID_PSLG -7
#define BREP_CDT_RESULT_DETRIA_FAILED -8
#define BREP_CDT_RESULT_CERTIFICATION_FAILED -9
#define BREP_CDT_RESULT_CHART_FAILED -10
#define BREP_CDT_RESULT_REFINEMENT_LIMIT -11
#define BREP_CDT_RESULT_GEOMETRIC_FAILED -12
#define BREP_CDT_RESULT_REPAIRED 3
#define BREP_CDT_RESULT_REPAIR_FAILED -13
#define BREP_CDT_STAGE_NONE 0
#define BREP_CDT_STAGE_INPUT 1
#define BREP_CDT_STAGE_TOPOLOGY 2
#define BREP_CDT_STAGE_EDGE_INITIALIZATION 3
#define BREP_CDT_STAGE_FACE_TRIANGULATION 4
#define BREP_CDT_STAGE_MESH_ASSEMBLY 5
#define BREP_CDT_STAGE_SOLID_VALIDATION 6
#define BREP_CDT_STAGE_PSLG_VALIDATION 7
#define BREP_CDT_STAGE_DETRIA 8
#define BREP_CDT_STAGE_CHART_CONSTRUCTION 9
#define BREP_CDT_STAGE_ADAPTIVE_REFINEMENT 10
#define BREP_CDT_STAGE_GEOMETRIC_VALIDATION 11
#define BREP_CDT_STAGE_MESH_REPAIR 12
/* A snapshot of the most recent tessellation attempt. message is always
* NUL-terminated. face_index is -1 when no individual face is responsible. */
struct brep_cdt_diagnostic {
int result;
int stage;
int face_index;
int completed_faces;
int failed_faces;
char message[256];
};
/**
* Explicit controls for the post-tessellation mesh-repair tier.
*
* Repair is never attempted by ON_Brep_CDT_Tessellate. Hole and component
* operations must also be enabled and bounded explicitly in @p mesh. A zero
* max_surface_deviation uses the tessellation's maximum chord tolerance. A
* zero max_deviation_samples uses the library's bounded default. The area
* change limit applies to the complete repaired mesh; zero disables that
* additional aggregate limit. By default deviation samples must project
* inside a trimmed source face. allow_untrimmed_surface_match permits an
* explicitly reported fallback to the source face's underlying analytic
* surface when defective trim topology prevents that classification. The
* fast-face fallback supplies display-CDT triangles for failed rigorous faces
* before mesh repair; its point, byte, and time limits bound that extra work.
* use_full_fast_fallback instead supplies one coherent whole-B-Rep display
* mesh. It is intended as a lower-fidelity alternative when mixing rigorous
* and display face meshes leaves irreparable boundary topology.
* use_full_fast_fallback_if_needed preserves the rigorous-first attempt and
* retries with that whole display mesh only if the mixed mesh cannot be
* certified. try_invalid_brep permits the repair entry point to make one
* rigorous attempt after broad OpenNURBS validity failure, while retaining
* the mesher's closed-manifold and paired-edge topology prerequisites.
* If ordinary repair fails, that option also permits a bounded topology
* healing attempt on an owned copy: unused edges, inconsistent orientations,
* and supported planar trim loops may be corrected without changing source
* surfaces or 3-D curves. Sampling, storage, and time are bounded by the fast
* fallback limits. Ambiguous boundaries are left unresolved; any candidate
* must still pass the requested mesh and fidelity checks. Provenance uses
* the caller's original face and edge indices, including ignored wire edges.
* A failed whole-display repair of a valid closed source may retry a small
* open boundary residue with its validated pullback samples retained. The
* same resource, solid, and fidelity requirements apply to that retry.
* Edge-initialization failures caused by disagreeing paired p-curves may make
* one repair-only retry when their shared midpoint stays within the same
* maximum surface-deviation bound of both faces and the native edge curve.
* With the automatic zero poisson_scale, use_poisson_reconstruction first
* gives conservative mesh repair one chance to close the whole display mesh
* without replacing its triangles. Only if that fails does Screened Poisson
* replace the display mesh with an implicit-surface reconstruction. An
* internal triangle cap bounds that opportunistic pass; use_full_fast_fallback
* without Poisson remains the explicit route for larger local repairs. An
* explicit nonzero scale requests Poisson directly. This is the most
* approximate tier and is therefore separately opt-in. Its depth
* is restricted to the bounded range accepted by this API. A zero
* poisson_scale tries the upstream 1.1 domain scale followed by a 1.2 retry
* if the first reconstructed mesh cannot be certified. If either scale
* exceeds the reference-area bound or omits input coverage, the same bounded
* scale pair is retried with one area-weighted sample budget. If the default
* Neumann reconstruction remains open, the bounded scale and sampling
* attempts are repeated with a Dirichlet boundary
* and stronger non-exact screening. This closure-biased fallback is reported
* explicitly and remains subject to every solid, area, deviation, and input
* coverage gate. A value from 1.0 through 2.0 requests one fixed scale
* without either automatic retry. Disconnected B-Rep face components are
* reconstructed independently so a large component cannot erase a smaller
* one; max_poisson_components bounds that work.
* relaxed_fidelity_factor is a separately opt-in final acceptance tier for a
* mesh which already satisfies every solid and Manifold requirement. A
* value from 1 through 4 multiplies the strict surface-deviation and enabled
* area-change limits without changing the generated mesh. Acceptance is
* tagged explicitly in the report and provenance callback; zero disables it.
*/
/* These identifiers describe accepted paths; their values do not rank
* geometric error. */
/** No approximate geometry was needed. */
#define BREP_CDT_REPAIR_APPROX_NONE 0
/** A failed face used display triangulation with rigorous edge samples. */
#define BREP_CDT_REPAIR_APPROX_CONSTRAINED_FACE 1
/** Bounded local mesh repair added or reconstructed a tagged neighborhood. */
#define BREP_CDT_REPAIR_APPROX_LOCAL_MESH 2
/** The complete B-Rep used display triangulation. */
#define BREP_CDT_REPAIR_APPROX_FULL_FAST 3
/** The complete display mesh was replaced by an implicit reconstruction. */
#define BREP_CDT_REPAIR_APPROX_POISSON 4
/** A Manifold mesh used the explicitly relaxed final fidelity bound. */
#define BREP_CDT_REPAIR_APPROX_RELAXED_FIDELITY 5
/** Source topology was healed without replacing its support geometry. */
#define BREP_CDT_REPAIR_APPROX_TOPOLOGY 6
/**
* Report the B-Rep topology whose interpretation required approximation.
* The callback is invoked only for an accepted repaired solid. face_indices
* and edge_indices remain valid only for the duration of the call. Callers
* producing a BoT may use these lists and approximation_tier to attach
* provenance attributes to the converted object.
*/
typedef void (*brep_cdt_repair_provenance_t)(int approximation_tier,
const int *face_indices, size_t face_count, const int *edge_indices,
size_t edge_count, void *data);
struct brep_cdt_repair_settings {
struct bg_trimesh_repair_settings mesh;
fastf_t max_surface_deviation;
size_t max_deviation_samples;
fastf_t max_area_change_percent;
int allow_untrimmed_surface_match;
int use_fast_face_fallback;
int use_full_fast_fallback;
int use_poisson_reconstruction;
int poisson_depth;
size_t max_poisson_components;
size_t max_fast_points;
size_t max_fast_result_bytes;
long max_fast_time_ms;
fastf_t poisson_scale;
int use_full_fast_fallback_if_needed;
int try_invalid_brep;
brep_cdt_repair_provenance_t provenance;
void *provenance_data;
fastf_t relaxed_fidelity_factor;
/**
* Optional second-pass ceiling when only open boundary edges remain.
* max_hole_edges remains the normal limit; zero disables the retry.
* Earlier rigorous-boundary reconstruction keeps its original budget.
*/
size_t max_adaptive_hole_edges;
/**
* Final-repair-only hole candidate ceiling for a bounded open-edge
* residue. The independent aggregate area-change and surface-deviation
* limits remain authoritative for accepting the resulting solid.
*/
fastf_t max_adaptive_hole_area_percent;
/** Opt-in planar caps for simple open boundaries on a multi-face B-Rep.
* Cap bounding boxes must be disjoint. Their aggregate conservative
* area bound must not exceed this percentage of
* the accepted mesh's remaining area. Zero disables capping. Capped
* candidates require Manifold acceptance and retain the caller's mesh
* intersection policy and fidelity limits. Requires try_invalid_brep. */
fastf_t max_planar_cap_area_percent;
};
#define BREP_CDT_REPAIR_SETTINGS_INIT {BG_TRIMESH_REPAIR_SETTINGS_INIT, 0.0, 4096, 1.0, 0, 1, 0, 0, 8, 64, 1048576, 134217728, 5000, 0.0, 0, 0, NULL, NULL, 0.0, 0, 0.0, 0.0}
/** Bounded topology interpretation, distinct from triangle-mesh repair.
* Counts describe a candidate; applied is set only after mesh acceptance. */
struct brep_cdt_healing_report {
int attempted;
int applied;
int limited;
int restored_outer_loops;
int corrected_loop_roles;
int reoriented_faces;
int removed_unused_edges;
/** Plane-distance bound for reconstructed outer loops and cap curves. */
fastf_t max_edge_deviation;
int capped_loops;
fastf_t cap_area_bound;
fastf_t cap_area_percent;
};
#define BREP_CDT_HEALING_REPORT_INIT {0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0.0, 0.0}
/* Resource limits encountered by any nested repair attempt. */
#define BREP_CDT_REPAIR_LIMIT_TIME 1u
#define BREP_CDT_REPAIR_LIMIT_MEMORY 2u
#define BREP_CDT_REPAIR_LIMIT_POINTS 4u
/* Bounded refinement or topology healing exhausted its work budget. */
#define BREP_CDT_REPAIR_LIMIT_REFINEMENT 8u
/** Provenance and quality measurements for a repair attempt. */
struct brep_cdt_repair_report {
struct bg_trimesh_repair_report mesh;
struct brep_cdt_diagnostic source_diagnostic;
int source_failed_faces;
int changed_faces;
size_t deviation_samples;
size_t deviation_projection_failures;
size_t untrimmed_surface_samples;
size_t input_mesh_surface_samples;
int fast_fallback_attempted_faces;
int fast_fallback_used_faces;
int fast_fallback_failed_faces;
int fast_fallback_triangles;
int full_fast_fallback_used;
int poisson_reconstruction_attempted;
int poisson_reconstruction_applied;
int poisson_input_points;
int poisson_components;
int poisson_output_points;
int poisson_output_faces;
int poisson_attempts;
int poisson_area_sampling_applied;
int poisson_boundary_fallback_applied;
fastf_t poisson_scale;
fastf_t max_surface_deviation;
fastf_t rms_surface_deviation;
fastf_t allowed_surface_deviation;
fastf_t reference_area;
fastf_t reference_area_change_percent;
fastf_t area_change_percent;
size_t coverage_samples;
size_t coverage_failures;
fastf_t max_coverage_deviation;
fastf_t rms_coverage_deviation;
int relaxed_tessellation_attempted;
int relaxed_tessellation_completed_faces;
int bounded_edge_retry_attempted;
int bounded_edge_retry_completed_faces;
int bounded_edge_approximation_edges;
int bounded_edge_approximation_faces;
fastf_t max_bounded_edge_deviation;
size_t fast_fallback_constrained_edges;
size_t fast_fallback_constrained_samples;
int added_patch_components;
int largest_added_patch_faces;
fastf_t largest_added_patch_area;
int rigorous_first_attempted;
int rigorous_first_result;
int rigorous_first_fast_faces;
size_t rigorous_first_constrained_edges;
size_t rigorous_first_constrained_samples;
fastf_t rigorous_first_reference_area;
fastf_t rigorous_first_output_area;
fastf_t rigorous_first_area_change_percent;
int approximation_tier;
int approximation_faces;
int approximation_edges;
/** Triangles retained byte-for-byte from the rigorous input mesh. */
int retained_rigorous_triangles;
/** Triangles replaced only inside accepted, tagged local neighborhoods. */
int missing_rigorous_triangles;
/** Certified triangles retained by a bounded hanging-edge subdivision. */
int subdivided_rigorous_triangles;
/** Accepted local neighborhoods replacing certified triangles. */
int replaced_rigorous_components;
int largest_replaced_rigorous_triangles;
size_t largest_replaced_boundary_edges;
fastf_t replaced_rigorous_area;
fastf_t largest_replaced_rigorous_area;
/** Failed adaptive faces retained as approximate repair input. */
int best_effort_faces;
int best_effort_triangles;
int best_effort_folded_triangles;
/** Chord samples projected to their source face within four tolerances. */
size_t best_effort_reference_samples;
/** Chord samples with no source projection inside that diagnostic bound. */
size_t best_effort_reference_failures;
fastf_t max_best_effort_surface_deviation;
/** Sparse local periodic strips joining rigorous neighboring boundaries. */
int boundary_strip_faces;
int boundary_strip_triangles;
size_t boundary_strip_constrained_edges;
size_t boundary_strip_constrained_samples;
/** Topological disks spanning complete authoritative shared boundaries. */
int topological_disk_faces;
int topological_disk_triangles;
size_t topological_disk_constrained_edges;
size_t topological_disk_constrained_samples;
/** Final acceptance used the separately enabled relaxed fidelity tier. */
int relaxed_fidelity_applied;
fastf_t relaxed_fidelity_factor;
fastf_t relaxed_surface_deviation_limit;
fastf_t relaxed_area_change_percent_limit;
int adaptive_hole_retry_attempted;
size_t adaptive_hole_edges;
int adaptive_hole_area_retry_attempted;
fastf_t adaptive_hole_area_percent;
struct brep_cdt_healing_report healing;
/** A closed-source retry retained validated pullback boundary samples. */
int pullback_retry_attempted;
int pullback_retry_applied;
/** Cumulative BREP_CDT_REPAIR_LIMIT_* flags, also retained on success. */
unsigned int resource_limits;
};
#define BREP_CDT_REPAIR_REPORT_INIT {BG_TRIMESH_REPAIR_REPORT_INIT, {BREP_CDT_RESULT_UNATTEMPTED, BREP_CDT_STAGE_NONE, -1, 0, 0, {0}}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, 0.0, 0, 0, 0, 0.0, BREP_CDT_HEALING_REPORT_INIT, 0, 0, 0u}
/** Create and initialize a CDT state with default tolerances. bv
* must be a pointer to an ON_Brep object. */
extern BREP_EXPORT struct ON_Brep_CDT_State *
ON_Brep_CDT_Create(void *bv, const char *objname);
/* Destroy a CDT state */
/** Destroy a CDT state. */
extern BREP_EXPORT void
ON_Brep_CDT_Destroy(struct ON_Brep_CDT_State *s);
/** Return the diagnostic object name associated with a CDT state. */
extern BREP_EXPORT const char *
ON_Brep_CDT_ObjName(struct ON_Brep_CDT_State *s);
/* Set/get the CDT tolerances. */
/** Set the CDT tolerances. */
extern BREP_EXPORT void
ON_Brep_CDT_Tol_Set(struct ON_Brep_CDT_State *s, const struct bg_tess_tol *t);
extern BREP_EXPORT void
ON_Brep_CDT_Tol_Get(struct bg_tess_tol *t, const struct ON_Brep_CDT_State *s);
/* Return the ON_Brep associated with state s. */
/** Set a wall-clock limit for each rigorous face triangulation. A positive
* value reports an adaptive-refinement failure when the limit is reached,
* allowing callers to continue with an explicitly bounded repair tier.
* Zero preserves the default unlimited behavior. */
extern BREP_EXPORT void
ON_Brep_CDT_Face_Time_Limit_Set(struct ON_Brep_CDT_State *s,
long max_time_ms);
/** Return the ON_Brep associated with state s. */
extern BREP_EXPORT void *
ON_Brep_CDT_Brep(struct ON_Brep_CDT_State *s);
/* Given a state, produce a triangulation. Returns 0 if a solid, valid
/** Given a state, produce a triangulation. Returns 0 if a solid, valid
* triangulation was produced, 1 if a triangulation was produced but it
* isn't solid, and -1 if no triangulation could be produced. If faces is
* non-null, the triangulation will only attempt to triangulate the
@@ -73,7 +396,31 @@ ON_Brep_CDT_Brep(struct ON_Brep_CDT_State *s);
extern BREP_EXPORT int
ON_Brep_CDT_Tessellate(struct ON_Brep_CDT_State *s, int face_cnt, int *faces);
/* Given a state, report the status of its triangulation. -3 indicates a
/**
* Attempt an explicitly bounded mesh repair after tessellation left a
* partial or non-solid mesh. The original failed-face diagnostics remain
* available as repair provenance. A repaired result is accepted only when
* it is a closed, oriented manifold with valid vertex links, nondegenerate
* triangles, bounded area change, and sampled deviation from the source
* B-Rep within the requested tolerance. Nonadjacent triangle intersections
* are rejected unless explicitly permitted by the mesh settings. A
* certified triangle neighborhood may be reconstructed only when its area
* and boundary stay within the configured hole limits; every affected B-Rep
* face and edge is then reported as approximation provenance. A failed
* single-loop face may also be spanned as a topological disk when every trim
* has complete authoritative shared-edge samples. This preserves its exact
* boundary while explicitly reporting the interior as a local approximation.
*
* Returns 1 if the state already contains a certified solid, 0 if repair
* produced a certified approximation, and -1 if repair was not possible or
* did not satisfy all postconditions.
*/
extern BREP_EXPORT int
ON_Brep_CDT_Repair(struct ON_Brep_CDT_State *s,
const struct brep_cdt_repair_settings *settings,
struct brep_cdt_repair_report *report);
/** Given a state, report the status of its triangulation. -3 indicates a
* failed attempt to tessellate, -2 indicates a non-solid tessellation is
* present after an attempt to tessellate all faces, -1 is a state which
* has had no tessellation attempt made, 0 indicates a solid, valid full
@@ -82,7 +429,26 @@ ON_Brep_CDT_Tessellate(struct ON_Brep_CDT_State *s, int face_cnt, int *faces);
extern BREP_EXPORT int
ON_Brep_CDT_Status(struct ON_Brep_CDT_State *s);
/* Construct a vlist plot from the tessellation. Modes are:
/** Retrieve structured information about the most recent tessellation attempt.
* Returns 0 on success and -1 for invalid arguments. */
extern BREP_EXPORT int
ON_Brep_CDT_Diagnostic(struct brep_cdt_diagnostic *diagnostic,
const struct ON_Brep_CDT_State *s);
/** Return the number of faces which failed in the most recent tessellation.
* If faces is non-NULL, copy at most capacity stable face indices. */
extern BREP_EXPORT int
ON_Brep_CDT_Failed_Faces(int *faces, int capacity,
const struct ON_Brep_CDT_State *s);
/** Copy the diagnostic recorded for a failed face in the most recent
* tessellation. Returns zero on success and -1 if no failure was recorded
* for face_index. */
extern BREP_EXPORT int
ON_Brep_CDT_Face_Diagnostic(struct brep_cdt_diagnostic *diagnostic,
int face_index, const struct ON_Brep_CDT_State *s);
/** Construct a vlist plot from the tessellation. Modes are:
*
* 0 - shaded 3D triangles
* 1 - 3D triangle wireframe
@@ -98,7 +464,7 @@ ON_Brep_CDT_VList(
int mode,
struct ON_Brep_CDT_State *s);
/* Given two or more triangulation states, refine them to clear any face
/** Given two or more triangulation states, refine them to clear any face
* overlaps introduced by the triangulation. If any of the states are
* un-tessellated, first perform the tessellation indicated by the state
* settings and then proceed to resolve after all states have an initial
@@ -119,7 +485,7 @@ extern BREP_EXPORT int
ON_Brep_CDT_UnResolvable_Ovlps(std::vector<struct ON_Brep_CDT_State *> *ovlps, struct ON_Brep_CDT_State *s);
#endif
/* Retrieve the face, vertex and normal information from a tessellation state
/** Retrieve the face, vertex and normal information from a tessellation state
* in the form of integer and fastf_t arrays. */
/* TODO - need to allow optional specification of specific faces here -
* have already hit one scenario where I want triangle information from
@@ -148,17 +514,165 @@ brep_facecdt_plot(struct bu_vls *vls, const char *solid_name,
extern BREP_EXPORT int
brep_cdt_fast(int **faces, int *face_cnt, vect_t **pnt_norms, point_t **pnts, int *pntcnt,
const ON_Brep *brep, int index, const struct bg_tess_tol *ttol, const struct bn_tol *tol);
#endif
/* PImpl exposure of some mesh operations for use in tests - not to be considered public API */
struct cdt_bmesh_impl;
struct cdt_bmesh {
struct cdt_bmesh_impl *i;
/* Resource controls and diagnostics for display-quality tessellation. Zero
* resource and tolerance values select library defaults; adaptive_quality is
* a boolean switch. max_time_ms is checked
* between faces; the per-face samplers also have fixed progress and recursion
* guards to prevent non-terminating refinement. face_status, when non-NULL,
* is called exactly once per requested face during serial result assembly.
* face_output, when non-NULL, reports the contiguous output ranges assigned
* to each completed face with drawable geometry. The optional trim-sample
* callbacks replace sampling for trims where trim_sample_count returns at
* least two points. Samples must be ordered in trim direction and supply
* the trim parameter, face UV coordinate, and exact 3D boundary coordinate.
* trim_sample_source may associate an opaque identity with each supplied
* sample. point_source reports that identity for output points derived from
* supplied samples, or NULL for generated points. Its point_index is in the
* final, concatenated output point array. The identity is never dereferenced
* by libbrep and need remain valid only until brep_cdt_fast_ex returns.
* Callbacks may be invoked concurrently when max_workers exceeds one, except
* face_status, face_diagnostic, and face_output, which run during serial
* result assembly. */
struct brep_cdt_fast_options {
size_t max_workers;
size_t max_result_bytes;
size_t max_points;
long max_time_ms;
int allow_partial;
void (*face_status)(int face_index, int status, void *data);
void *face_status_data;
void (*face_diagnostic)(int face_index, int result, int stage,
const char *message, void *data);
void *face_diagnostic_data;
void (*face_output)(int face_index, size_t first_face,
size_t face_count, size_t first_point, size_t point_count, void *data);
void *face_output_data;
size_t (*trim_sample_count)(int face_index, int trim_index, void *data);
int (*trim_sample)(int face_index, int trim_index, size_t sample_index,
fastf_t *trim_parameter, point2d_t uv, point_t point, void *data);
void *trim_sample_data;
const void *(*trim_sample_source)(int face_index, int trim_index,
size_t sample_index, void *data);
void (*point_source)(int face_index, size_t point_index,
const void *source, void *data);
void *point_source_data;
/* Shared estimated transient-memory allowance for concurrent face jobs.
* This is independent of max_result_bytes, which bounds retained output.
* A zero value selects an availability-calibrated library default. */
size_t max_working_bytes;
/* Display triangle target. Adaptive whole-object budgeting may select a
* lower target according to B-Rep topology. Authoritative trim
* boundaries are retained and may make the final count exceed it. */
size_t max_triangles;
/* Generate a complete coarse mesh before refining toward the requested
* tolerance. Intended for visual display; rigorous callers may disable
* it to request the specified tolerance directly. */
int adaptive_quality;
/* Initial relative display tolerance and per-face unsigned area-change
* convergence threshold. Zero values select library defaults. */
double coarse_relative_tolerance;
double area_change_tolerance;
/* Retain validated pcurve-repair samples for cross-face mesh assembly.
* Display callers normally simplify them within the repair tolerance. */
int preserve_pullback_samples;
};
extern BREP_EXPORT int cdt_bmesh_create(struct cdt_bmesh **m);
extern BREP_EXPORT void cdt_bmesh_destroy(struct cdt_bmesh *m);
extern BREP_EXPORT int cdt_bmesh_deserialize(const char *fname, struct cdt_bmesh *m);
extern BREP_EXPORT int cdt_bmesh_repair(struct cdt_bmesh *m);
#define BREP_CDT_FAST_FACE_COMPLETED 0
#define BREP_CDT_FAST_FACE_FAILED 1
#define BREP_CDT_FAST_FACE_SKIPPED_DEGENERATE 2
#define BREP_CDT_FAST_FACE_NOT_PROCESSED 3
#define BREP_CDT_FAST_FACE_APPROXIMATED 4
#define BREP_CDT_FAST_FACE_SKIPPED_TOLERANCE 5
/* completed_faces includes faces with no drawable output. Exact zero-area
* faces and faces which collapse at the requested display tolerance are
* reported separately. */
struct brep_cdt_fast_report {
int requested_faces;
int completed_faces;
int failed_faces;
size_t result_bytes;
int hit_time_limit;
int hit_memory_limit;
int hit_point_limit;
/* Completed faces proven to have exactly zero parametric area. */
int skipped_degenerate_faces;
/* Completed faces with no boundary resolvable at the requested
* tessellation and model tolerances. */
int skipped_tolerance_faces;
/* Peak sum of conservative face-work reservations. */
size_t peak_working_bytes;
/* Adaptive display-quality provenance. */
size_t triangle_budget;
int approximated_faces;
int area_converged_faces;
int triangle_budget_limited_faces;
int refinement_passes;
int refinement_time_limited;
/* Faces whose realized triangles do not cover the sampled B-Rep edge
* envelope within the requested display tolerance. */
int boundary_envelope_incomplete_faces;
};
#define BREP_CDT_FAST_OK 0
#define BREP_CDT_FAST_PARTIAL 1
#define BREP_CDT_FAST_ERROR -1
#define BREP_CDT_FAST_LIMIT -2
/**
* Initialize display-quality CDT options with library defaults.
*
* A NULL options pointer is ignored. Callers may override any nonzero
* resource limit or other option after initialization.
*
* @param options options structure to initialize
*/
extern BREP_EXPORT void
brep_cdt_fast_options_default(struct brep_cdt_fast_options *options);
/**
* Tessellate one B-Rep face, or all faces when @p index is -1, using the
* bounded display-quality CDT path.
*
* Output arrays are allocated with the BRL-CAD allocator and are owned by
* the caller. They are initialized to NULL/zero before any validation or
* processing. The optional report receives resource and per-face status
* information. A partial result is returned only when the options permit
* partial output. An all-faces request for a B-Rep with no faces succeeds
* with empty output; selecting an individual nonexistent face is an error.
*
* Failed trim boundaries may be retried after bounded topology healing on
* an owned copy. The source is unchanged and no holes are capped. This
* recovery is disabled when authoritative trim samples, source callbacks,
* or preserve_pullback_samples require the original topology identities.
*
* @param faces receives triangle vertex indices
* @param face_cnt receives the number of triangles
* @param pnt_norms receives three face-oriented corner normals per triangle,
* in the same order as faces; smooth normals opposing a realized
* nondegenerate facet are replaced by its geometric normal
* @param pnts receives output vertices
* @param pntcnt receives the number of output vertices
* @param brep source B-Rep
* @param index face index, or -1 for all faces
* @param ttol tessellation tolerance
* @param tol geometric tolerance
* @param options optional resource and callback settings; NULL selects
* defaults
* @param report optional diagnostic report
* @return BREP_CDT_FAST_OK for complete output, BREP_CDT_FAST_PARTIAL for
* permitted partial output, BREP_CDT_FAST_LIMIT for a bounded
* resource stop, or BREP_CDT_FAST_ERROR for invalid input or other
* failure
*/
extern BREP_EXPORT int
brep_cdt_fast_ex(int **faces, int *face_cnt, vect_t **pnt_norms,
point_t **pnts, int *pntcnt, const ON_Brep *brep, int index,
const struct bg_tess_tol *ttol, const struct bn_tol *tol,
const struct brep_cdt_fast_options *options,
struct brep_cdt_fast_report *report);
#endif
__END_DECLS
+35 -1
View File
@@ -34,6 +34,8 @@
#ifdef __cplusplus
// @cond SKIP_C++_INCLUDE
extern "C++" {
# include <cstddef>
# include <cstdint>
# include <map>
}
// @endcond
@@ -56,7 +58,24 @@ namespace brlcad {
*/
class BREP_EXPORT CurveTree : public PooledObject<CurveTree> {
public:
explicit CurveTree(const ON_BrepFace *face);
/**
* Build a curve tree with an optional transient-memory limit.
* A zero byte limit selects the library default.
*/
explicit CurveTree(const ON_BrepFace *face,
std::size_t max_bytes = 0);
/**
* Build a curve tree with a minimum feature-size sampling limit.
* A zero feature size selects the library default.
*/
CurveTree(const ON_BrepFace *face, std::size_t max_bytes,
double min_feature_size);
/**
* Build a curve tree with feature-size and deadline limits.
* The deadline is an absolute timestamp from bu_gettime(); zero disables it.
*/
CurveTree(const ON_BrepFace *face, std::size_t max_bytes,
double min_feature_size, int64_t deadline);
~CurveTree();
CurveTree(Deserializer &deserializer, const ON_BrepFace &face);
@@ -65,6 +84,14 @@ namespace brlcad {
std::list<const BRNode *> serialize_get_leaves(const std::size_t *keys, std::size_t num_keys) const;
void serialize_cleanup() const;
/**
* Report whether construction stopped at the node/memory limit.
* A limited tree must not be used for geometric queries.
*/
bool limit_reached() const { return m_limit_reached; }
/** Report whether construction stopped at its time deadline. */
bool time_limit_reached() const { return m_time_limit_reached; }
/**
* Return just the leaves of the surface tree
*/
@@ -95,12 +122,19 @@ namespace brlcad {
bool getHVTangents(const ON_Curve *curve, const ON_Interval &t, std::list<fastf_t> &list) const;
bool isLinear(const ON_Curve *curve, double min, double max) const;
bool deadlineExpired() const;
BRNode *subdivideCurve(const ON_Curve *curve, int trim_index, int adj_face_index, double min, double max, bool innerTrim, int depth) const;
BRNode *curveBBox(const ON_Curve *curve, int trim_index, int adj_face_index, const ON_Interval &t, bool isLeaf, bool innerTrim, const ON_BoundingBox &bb) const;
static ON_BoundingBox initialLoopBBox(const ON_BrepFace &face);
const ON_BrepFace * const m_face;
BRNode *m_root;
const std::size_t m_max_nodes;
const double m_min_feature_size;
const int64_t m_deadline;
mutable std::size_t m_node_count;
mutable bool m_limit_reached;
mutable bool m_time_limit_reached;
struct Stl : public PooledObject<Stl> {
+23 -11
View File
@@ -199,19 +199,31 @@ typedef struct _on_brep_placeholder {
extern "C++" {
struct BrepTrimPoint
{
int edge_ind;
double e; /* corresponding edge curve parameter (ON_UNSET_VALUE if using trim not edge) */
ON_3dPoint *p3d; /* 3d edge/trim point depending on whether we're using the 3d edge to generate points or the trims */
ON_3dPoint *n3d; /* normal on edge, average of the normals from the two surfaces at this point, or of all surface points associated with a vertex if this is a vertex point. */
ON_3dVector tangent; /* Tangent from the curve, or from the surfaces if the curve wasn't usable at this point. */
int edge_ind = -1;
double e = ON_UNSET_VALUE; /* corresponding edge curve parameter (ON_UNSET_VALUE if using trim not edge) */
ON_3dPoint *p3d = NULL; /* 3d edge/trim point depending on whether we're using the 3d edge to generate points or the trims */
ON_3dPoint *n3d = NULL; /* normal on edge, average of the normals from the two surfaces at this point, or of all surface points associated with a vertex if this is a vertex point. */
ON_3dVector tangent = ON_3dVector::UnsetVector; /* Tangent from the curve, or from the surfaces if the curve wasn't usable at this point. */
int trim_ind;
double t; /* corresponding trim curve parameter (ON_UNSET_VALUE if unknown or not pulled back) */
ON_2dPoint p2d; /* 2d surface parameter space point */
ON_3dVector normal; /* normal as calculated by this trim */
int trim_ind = -1;
double t = ON_UNSET_VALUE; /* corresponding trim curve parameter (ON_UNSET_VALUE if unknown or not pulled back) */
ON_2dPoint p2d = ON_2dPoint::UnsetPoint; /* 2d surface parameter space point */
ON_3dVector normal = ON_3dVector::UnsetVector; /* normal as calculated by this trim */
BrepTrimPoint *other_face_trim_pnt;
int from_singular;
/**
* Optional opaque caller identity retained when this point is copied.
*
* The pointer is non-owning: libbrep compares or reports its address but
* never dereferences it. The caller must keep the pointed-to identity at
* a stable address until the operation retaining the point has returned.
* This field is part of the public C++ structure layout; adding or removing
* fields in BrepTrimPoint changes its ABI and aggregate-initialization
* compatibility.
*/
const void *source_id = NULL;
BrepTrimPoint *other_face_trim_pnt = NULL;
int from_singular = -1;
};}
#endif
+31 -1
View File
@@ -31,6 +31,8 @@
#ifdef __cplusplus
extern "C++" {
/* @cond */
#include <cstddef>
#include <cstdint>
#include <list>
#include <queue>
/* @endcond */
@@ -57,7 +59,26 @@ namespace brlcad {
*/
class BREP_EXPORT SurfaceTree {
public:
explicit SurfaceTree(const ON_BrepFace *face, bool removeTrimmed = true, int depthLimit = BREP_MAX_FT_DEPTH, double within_distance_tol = BREP_EDGE_MISS_TOLERANCE);
/**
* Build a surface tree with optional curve-memory and depth limits.
* A zero curve-memory limit selects the library default.
*/
explicit SurfaceTree(const ON_BrepFace *face, bool removeTrimmed = true, int depthLimit = BREP_MAX_FT_DEPTH, double within_distance_tol = BREP_EDGE_MISS_TOLERANCE, std::size_t max_curve_bytes = 0);
/**
* Build a surface tree with a minimum curve feature-size limit.
* A zero feature size selects the library default.
*/
SurfaceTree(const ON_BrepFace *face, bool removeTrimmed,
int depthLimit, double within_distance_tol,
std::size_t max_curve_bytes, double min_curve_feature_size);
/**
* Build a surface tree with feature-size and curve deadline limits.
* The deadline is an absolute timestamp from bu_gettime(); zero disables it.
*/
SurfaceTree(const ON_BrepFace *face, bool removeTrimmed,
int depthLimit, double within_distance_tol,
std::size_t max_curve_bytes, double min_curve_feature_size,
int64_t curve_deadline);
~SurfaceTree();
/* Report whether the tree is in a usable state */
@@ -67,6 +88,15 @@ namespace brlcad {
return true;
};
/** Report whether curve-tree construction hit its resource limit. */
bool CurveTreeLimitReached() const {
return m_ctree && m_ctree->limit_reached();
};
/** Report whether curve-tree construction hit its time deadline. */
bool CurveTreeTimeLimitReached() const {
return m_ctree && m_ctree->time_limit_reached();
};
BBNode *getRootNode() const;
/**
+16 -9
View File
@@ -205,23 +205,30 @@ BU_EXPORT extern int bu_sscanf(const char *src, const char *fmt, ...) _BU_ATTR_S
/** Routines for scanning certain kinds of data. */
/**
* Scans a sequence of fastf_t numbers from a string or stdin
* Scans a sequence of fastf_t numbers from a string or stdin.
*
* Scanning fastf_t numbers with bu_sscanf() is difficult, because
* doing so requires scanning to some intermediate type like double
* and then assigning to the fastf_t variable to convert the value to
* whatever type fastf_t really is. This function makes it possible
* to scan a series of fastf_t numbers separated by some character(s)
* easily, by doing the required conversion internally to the
* functions. As series of delimiter characters will be skipped,
* empty scan fields are not supported (e.g., "0.0,,0.0,1.0" will scan
* as 3 fields, not 4 with the 2nd skipped).
* to scan a series of fastf_t numbers easily, by doing the required
* conversion internally. Delimiters form a non-empty character set:
* one or more delimiter characters must separate values, and repeated
* delimiters are skipped. Empty scan fields are therefore not
* supported (e.g., "0.0,,0.0,1.0" scans as 3 fields, not 4 with the
* second skipped). A trailing delimiter is not consumed after the
* final requested value.
*
* @param[out] c Returns number of characters scanned by the function
* @param[out] c Optional destination for the number of input characters
* consumed. It is set to zero before scanning and remains zero for
* invalid arguments or when no value is scanned.
* @param[in] src A source string to scan from, or NULL to read from stdin
* @param[in] delim Any delimiter character(s) to skip between scan values
* @param[in] delim Non-empty set of delimiter characters to skip between
* scan values
* @param[in] n Number of fastf_t values to scan from the src input string
* @param[out] ... Pointers to fastf_t for storing scanned values (optional)
* @param[out] ... Exactly n fastf_t pointers for storing scanned values;
* individual pointers may be NULL
* @return Number of values scanned
*
*/
BU_EXPORT extern int bu_scan_fastf_t(int *c, const char *src, const char *delim, size_t n, ...);
+7 -7
View File
@@ -146,10 +146,10 @@ BU_EXPORT extern size_t bu_malloc_len_roundup(size_t nbytes);
* really fast heap-based memory allocation intended for "small"
* allocation sizes (e.g., single structs).
*
* the implementation allocates chunks of memory ('pages') in order to
* substantially reduce calls to system malloc. it has a nice
* property of having O(1) constant time complexity and profiles
* significantly faster than system malloc().
* Allocations may be obtained and released by different threads. Small
* released allocations may be retained in bounded thread-affine caches.
* The requested size must be nonzero. Returned storage is suitably aligned,
* but its contents are unspecified.
*
* release memory with bu_heap_put() only.
*/
@@ -159,9 +159,9 @@ BU_EXPORT extern void *bu_heap_get(size_t sz);
* counterpart to bu_heap_get() for releasing fast heap-based memory
* allocations.
*
* the implementation may do nothing, relying on free-on-exit, or may
* mark deallocations for reuse. pass a NULL pointer and zero size to
* force compaction of any unused memory.
* The allocation size must match the size passed to bu_heap_get(). A NULL
* pointer is ignored; passing NULL with a zero size requests compaction of
* any unused implementation storage.
*/
BU_EXPORT extern void bu_heap_put(void *ptr, size_t sz);
+14 -1
View File
@@ -103,10 +103,23 @@ BU_EXPORT extern void bu_nice_set(int newnice);
/**
* Return the maximum number of physical CPUs that are considered to
* be available to this process now.
* be available to this process now, subject to any process-local limit set by
* bu_avail_cpus_set().
*/
BU_EXPORT extern size_t bu_avail_cpus(void);
/**
* Limit the CPU count reported by bu_avail_cpus() and honored by subsequent
* bu_parallel() calls in this process. A value of zero removes the limit.
* Call this before starting worker threads; changing the limit does not resize
* work that is already running.
*
* This is useful when several independent processes share one machine and
* each process contains code that selects its own parallelism with
* bu_avail_cpus().
*/
BU_EXPORT extern void bu_avail_cpus_set(size_t ncpu);
/**
* Create parallel threads of execution.
+2 -2
View File
@@ -84,7 +84,7 @@ __BEGIN_DECLS
#define RT_DEBUG_HF 0x04000000 /**< @brief 27 Height Field solids */
#define RT_DEBUG_MESHING 0x08000000 /**< @brief 28 Print meshing/triangulation details */
#define RT_DEBUG_UNUSED_3 0x10000000 /**< @brief 29 Unassigned */
#define RT_DEBUG_RTCMP 0x10000000 /**< @brief 29 Set up rtcmp capture; recording uses a runtime toggle. If LIBRT_RTCMP_PRIMITIVES=1 includes primitive segments. */
#define RT_DEBUG_UNUSED_4 0x20000000 /**< @brief 30 Unassigned */
/* Options which will cause the library to write binary debugging output */
@@ -96,7 +96,7 @@ __BEGIN_DECLS
"\040PL_BOX" \
"\037PL_SOLIDS" \
"\036UNUSED_4" \
"\035UNUSED_3" \
"\035RTCMP" \
"\034UNUSED_2" \
"\033HF" \
"\032EBM" \
+23 -6
View File
@@ -465,6 +465,17 @@ rt_get_solid_keypoint(struct rt_edit *s, point_t *pt, const char **strp, fastf_t
RT_EXPORT extern void
rt_edit_set_edflag(struct rt_edit *s, int edflag);
/**
* Set a parameter-edit translation target expressed in model base units.
*
* rt_edit's parameter-edit handlers consume e_para in database local units,
* while keypoints and edit axes are maintained in model base units. This
* helper performs the boundary conversion for callers that have computed a
* target from a keypoint, axis, or view transform.
*/
RT_EXPORT extern void
rt_edit_set_translation_target(struct rt_edit *s, const point_t target);
RT_EXPORT extern int
rt_edit_knob_cmd_process(
struct rt_edit *s,
@@ -499,8 +510,10 @@ rt_knob_edit_sca(
int matrix_edit);
/* Equivalent to sedit - run editing logic after input data is set in
* rt_edit container */
RT_EXPORT extern void
* rt_edit container. Transient input flags are consumed whether the edit
* succeeds or fails; parameter values remain available to the caller.
* Returns BRLCAD_ERROR if the primitive edit fails. */
RT_EXPORT extern int
rt_edit_process(struct rt_edit *s);
/**
@@ -521,7 +534,7 @@ rt_edit_snap_point(point2d_t pt, const struct rt_edit *s);
* restored later with rt_edit_revert().
*
* The snapshot is stored inside the rt_edit struct. Calling this
* function again overwrites any previous snapshot (single-level undo).
* function again replaces the previous snapshot only on success.
*
* @return BRLCAD_OK on success, BRLCAD_ERROR if the export failed.
*/
@@ -532,8 +545,8 @@ rt_edit_checkpoint(struct rt_edit *s);
* Restore primitive parameters from the snapshot saved by
* rt_edit_checkpoint().
*
* If no snapshot has been saved (or the last snapshot was already
* consumed) this function logs a message and returns BRLCAD_ERROR.
* The snapshot remains available for repeated restores. If none has
* been saved, this function logs a message and returns BRLCAD_ERROR.
*
* @return BRLCAD_OK on success, BRLCAD_ERROR otherwise.
*/
@@ -697,7 +710,11 @@ enum rt_constraint_edit_op_kind {
RT_CONSTRAINT_EDIT_OP_SET_BEND,
RT_CONSTRAINT_EDIT_OP_SCALE_OD,
RT_CONSTRAINT_EDIT_OP_SCALE_ID,
RT_CONSTRAINT_EDIT_OP_SCALE_BEND
RT_CONSTRAINT_EDIT_OP_SCALE_BEND,
/* Set every point when an all-zero dimension has no scale reference. */
RT_CONSTRAINT_EDIT_OP_SET_ALL_OD,
RT_CONSTRAINT_EDIT_OP_SET_ALL_ID,
RT_CONSTRAINT_EDIT_OP_SET_ALL_BEND
};
struct rt_constraint_edit_param_ref {
-85
View File
@@ -29,19 +29,10 @@
#include "bu/list.h"
#include "bu/vls.h"
#include "bn/tol.h"
#include "rt/geom.h"
#include "rt/defines.h"
__BEGIN_DECLS
struct db_i;
struct xray;
#define RT_ANNOT_SCREEN_DPI 96.0
#define RT_ANNOT_MILLIMETERS_PER_INCH 25.4
#define RT_ANNOT_SCREEN_PIXELS_PER_MM \
(RT_ANNOT_SCREEN_DPI / RT_ANNOT_MILLIMETERS_PER_INCH)
RT_EXPORT extern struct rt_annot_internal *rt_copy_annot(const struct rt_annot_internal *annot_ip);
/** Validate annotation topology, model-space placement, and optional segment
@@ -49,82 +40,6 @@ RT_EXPORT extern struct rt_annot_internal *rt_copy_annot(const struct rt_annot_i
RT_EXPORT extern int rt_annot_validate(const struct rt_annot_internal *annot_ip,
struct bu_vls *messages);
/** Opaque, immutable collection of prepared annotation instances. */
struct rt_annot_scene;
/** Camera state needed to evaluate view-space annotations. Pixel sample
* coordinates use a lower-left origin. */
struct rt_annot_view {
mat_t model2view;
size_t width;
size_t height;
fastf_t perspective;
};
/** A zero-thickness annotation coverage event. path is owned by the scene. */
struct rt_annot_hit {
const char *path;
size_t segment;
uint32_t role;
point_t point;
fastf_t distance;
unsigned char color[4];
int screen_space;
int visible;
};
/** Prepare annotations referenced by the supplied top-level database paths.
* Boolean operations are intentionally ignored: annotations are
* informational leaves, not solids. */
RT_EXPORT extern struct rt_annot_scene *rt_annot_scene_create(
struct db_i *dbip, int path_count, const char * const *paths,
const struct bg_tess_tol *ttol, const struct bn_tol *tol);
RT_EXPORT extern void rt_annot_scene_destroy(struct rt_annot_scene *scene);
RT_EXPORT extern size_t rt_annot_scene_count(const struct rt_annot_scene *scene);
/** Return model-space bounds for prepared annotations. Screen-space geometry
* contributes its model-space anchor because its extent is view-dependent. */
RT_EXPORT extern int rt_annot_scene_bounds(const struct rt_annot_scene *scene,
point_t minimum, point_t maximum);
/** Return the top visible coverage event for a primary sample. */
RT_EXPORT extern int rt_annot_scene_query(const struct rt_annot_scene *scene,
const struct rt_annot_view *view, const struct xray *ray,
fastf_t sample_x, fastf_t sample_y, fastf_t scene_distance,
struct rt_annot_hit *hit);
/** Return all visible primary-sample coverage events in back-to-front order.
* Model-space events precede screen-space events. When hits is NULL or
* capacity is zero, only the required result count is returned. */
RT_EXPORT extern size_t rt_annot_scene_query_layers(
const struct rt_annot_scene *scene, const struct rt_annot_view *view,
const struct xray *ray, fastf_t sample_x, fastf_t sample_y,
fastf_t scene_distance, struct rt_annot_hit *hits, size_t capacity);
/** Return all model-space coverage events, sorted by ray distance. When hits
* is NULL or capacity is zero, only the required result count is returned. */
RT_EXPORT extern size_t rt_annot_scene_query_model(const struct rt_annot_scene *scene,
const struct xray *ray, fastf_t scene_distance,
struct rt_annot_hit *hits, size_t capacity);
/** Alpha-composite a coverage result over a linear RGB sample. */
RT_EXPORT extern void rt_annot_hit_blend(fastf_t color[3],
const struct rt_annot_hit *hit);
/** Alpha-composite all visible primary-sample coverage over color. If
* front_hit is non-NULL, it receives the frontmost event. */
RT_EXPORT extern int rt_annot_scene_composite(const struct rt_annot_scene *scene,
const struct rt_annot_view *view, const struct xray *ray,
fastf_t sample_x, fastf_t sample_y, fastf_t scene_distance,
fastf_t color[3], struct rt_annot_hit *front_hit);
/** Generate one annotation segment in annotation-local XY coordinates. This
* is shared by plotting and the coverage preparer to keep curve and font
* expansion identical. */
RT_EXPORT extern int rt_annot_segment_vlist(struct bu_list *vlfree,
struct bu_list *vhead, const struct bg_tess_tol *ttol,
const struct rt_annot_internal *annot_ip, size_t segment);
__END_DECLS
/** @} */
-3
View File
@@ -55,14 +55,11 @@ struct clt_tri_specific {
};
#endif
#define RT_BOT_SURFNO_INTERIOR (-1)
/* Shared between bot and ars at the moment */
struct bot_specific {
unsigned char bot_mode;
unsigned char bot_orientation;
unsigned char bot_flags;
unsigned char bot_parity_shot;
size_t bot_ntri;
fastf_t *bot_thickness;
struct bu_bitv *bot_facemode;
+57
View File
@@ -24,6 +24,8 @@
#ifndef RT_PRIMITIVES_BREP_H
#define RT_PRIMITIVES_BREP_H
#include <stddef.h>
#include "common.h"
#include "vmath.h"
#include "bu/list.h"
@@ -39,6 +41,61 @@ RT_EXPORT extern int rt_brep_plot(struct bu_list *vhead,
const struct bg_tess_tol *ttol,
const struct bn_tol *tol,
const struct bview *info);
/** Options for bounded B-Rep wireframe and surface-cue drawing. */
struct rt_brep_draw_options {
size_t max_workers;
/** Retained output memory; zero selects the library default. */
size_t max_result_bytes;
/** Temporary edge and curve-tree working memory; zero selects default. */
size_t max_working_bytes;
size_t max_points;
long max_time_ms;
int include_surface_cues;
void (*item_status)(int item_type, int item_index, int status,
void *data);
void *item_status_data;
};
#define RT_BREP_DRAW_EDGE 0
#define RT_BREP_DRAW_SURFACE_CUE 1
#define RT_BREP_DRAW_ITEM_COMPLETED 0
#define RT_BREP_DRAW_ITEM_FAILED 1
#define RT_BREP_DRAW_ITEM_NOT_PROCESSED 2
#define RT_BREP_DRAW_ITEM_APPROXIMATED 3
struct rt_brep_draw_report {
int requested_edges;
int completed_edges;
int failed_edges;
int requested_surface_cues;
int completed_surface_cues;
/** Cues drawn from the bounded untrimmed surface envelope. */
int approximated_surface_cues;
int memory_approximated_surface_cues;
int time_approximated_surface_cues;
size_t output_points;
size_t result_bytes;
int hit_time_limit;
int hit_memory_limit;
int hit_point_limit;
};
#define RT_BREP_DRAW_OK 0
#define RT_BREP_DRAW_PARTIAL 1
#define RT_BREP_DRAW_ERROR -1
#define RT_BREP_DRAW_LIMIT -2
/** Initialize drawing options with safe bounded defaults. */
RT_EXPORT extern void
rt_brep_draw_options_default(struct rt_brep_draw_options *options);
/** Draw bounded B-Rep wireframe edges and optional surface cues. */
RT_EXPORT extern int
rt_brep_plot_ex(struct bu_list *vhead, struct rt_db_internal *ip,
const struct bg_tess_tol *ttol, const struct bn_tol *tol,
const struct bview *info, const struct rt_brep_draw_options *options,
struct rt_brep_draw_report *report);
RT_EXPORT extern int rt_brep_plot_poly(struct bu_list *vhead,
const struct directory *dp,
struct rt_db_internal *ip,
+1
View File
@@ -41,6 +41,7 @@ struct rt_nmg_edit {
struct loopuse *lu_copy; /* copy of loop to be extruded */
plane_t lu_pl; /* plane equation for loop to be extruded */
struct shell *es_s; /* Shell where extrusion is to end up */
int es_s_from_extrusion; /* Replace this edit's prior preview */
point_t lu_keypoint; /* keypoint of lu_copy for extrusion */
struct vertex *es_v; /* Currently selected NMG vertex (ECMD_NMG_VPICK) */
struct faceuse *es_fu; /* Currently selected NMG faceuse (ECMD_NMG_FPICK) */
+12
View File
@@ -55,6 +55,18 @@
__BEGIN_DECLS
#define RT_RTCMP_CAPTURE_RUNTIME_TOGGLE 1
/** Set RT_DEBUG_RTCMP recording: nonzero enables, zero disables.
* Enabling requires the debug flag and a usable output writer.
* Return 0 on success and -1 on failure. Disabling succeeds even
* before setup.
*/
RT_EXPORT extern int rt_rtcmp_capture_set_enabled(int enabled);
/** Wait until pending RT_DEBUG_RTCMP records have reached the output file. */
RT_EXPORT extern int rt_rtcmp_capture_flush(void);
/**
* @brief
* Shoot a ray
+5 -1
View File
@@ -2200,7 +2200,11 @@ macro(find_package_opencv)
set(OpenCV_DIR_TMP "${OpenCV_DIR}")
set(OpenCV_DIR "${CMAKE_BINARY_DIR}/${LIB_DIR}/cmake/opencv4")
set(OpenCV_ROOT ${CMAKE_BINARY_DIR})
find_package(OpenCV COMPONENTS core features2d imgproc highgui)
find_package(OpenCV CONFIG COMPONENTS core imgproc imgcodecs highgui)
if(OpenCV_FOUND AND OpenCV_VERSION_MAJOR GREATER_EQUAL 5)
# OpenCV 5 moved boundingRect from imgproc to geometry.
find_package(OpenCV CONFIG COMPONENTS geometry)
endif()
unset(OpenCV_ROOT)
# If no bundled copy, see what the system has
-1
View File
@@ -1,6 +1,5 @@
# default.nrt
# Description: default - Standard NIRT Reporting Format
fmt a ""
fmt r "Origin (x y z) = (%.8f %.8f %.8f) (h v d) = (%.4f %.4f %.4f)\nDirection (x y z) = (%.8f %.8f %.8f) (az el) = (%.8f %.8f)\n" x_orig y_orig z_orig h v d_orig x_dir y_dir z_dir a e
fmt h " Region Name Entry (x y z) LOS Obliq_in Attrib\n"
fmt p "%-20s (%9.4f %9.4f %9.4f) %8.4f %8.4f%s\n" reg_name x_in y_in z_in los obliq_in attributes
+7 -1
View File
@@ -140,7 +140,13 @@ add_subdirectory(ttcp)
if(SH_EXEC)
brlcad_add_test(NAME regress-usage COMMAND ${SH_EXEC} "${CMAKE_SOURCE_DIR}/regress/usage.sh" ${CMAKE_SOURCE_DIR})
brlcad_regression_test(regress-usage "rt" STAND_ALONE TEST_DEFINED)
set(regress_usage_timeout 300)
if(WIN32)
# Starting and inspecting the full executable set is substantially slower on Windows.
set(regress_usage_timeout 900)
endif()
brlcad_regression_test(regress-usage "rt" STAND_ALONE TEST_DEFINED TIMEOUT ${regress_usage_timeout})
unset(regress_usage_timeout)
# TODO - test for Python before adding this one
brlcad_add_test(NAME regress-flawfinder COMMAND ${SH_EXEC} "${CMAKE_SOURCE_DIR}/regress/flawfinder.sh" ${CMAKE_SOURCE_DIR})
+14
View File
@@ -117,6 +117,20 @@ if(TARGET libged)
endif(TARGET libged)
if(TARGET librt AND FUZZER_SUPPORTED)
set(RT_EDIT_FUZZ_DIR "${CMAKE_SOURCE_DIR}/src/librt/tests/edit")
brlcad_addexec(
fuzz_rt_edit_test
"${RT_EDIT_FUZZ_DIR}/fuzz.cpp;${RT_EDIT_FUZZ_DIR}/ars.cpp;${RT_EDIT_FUZZ_DIR}/bot.cpp"
"libbu;librt;libnmg;libwdb;libbrep"
TEST
)
set_target_properties(fuzz_rt_edit_test PROPERTIES UNITY_BUILD OFF)
target_compile_options(fuzz_rt_edit_test PRIVATE ${FUZZER_EXE_COMPILE_OPTIONS})
target_link_options(fuzz_rt_edit_test PRIVATE ${FUZZER_LINK_OPTIONS})
BRLCAD_ADD_TEST(NAME fuzz_rt_edit_smoke COMMAND fuzz_rt_edit_test -runs=1 -max_total_time=5)
endif()
cmakefiles(
CMakeLists.txt
fuzz_ged.cpp
+10
View File
@@ -37,6 +37,14 @@ if(TARGET libged)
endif(TARGET regress_lint_raytrace)
brlcad_regression_test(regress-ged_lint_raytrace "regress_lint_raytrace;ged_exec" EXEC regress_lint_raytrace)
distclean("${CMAKE_CURRENT_BINARY_DIR}/regress-ged_lint_raytrace.log")
# Bounded B-Rep geometry diagnostics used by lint -I brep:*.
brlcad_addexec(regress_lint_brep regress_lint_brep.cpp "libged;libwdb;librt;libbu" TEST_USESDATA)
if(TARGET regress_lint_brep)
add_dependencies(regress_lint_brep ged_plugins)
endif(TARGET regress_lint_brep)
brlcad_regression_test(regress-ged_lint_brep "regress_lint_brep;ged_exec" EXEC regress_lint_brep)
distclean("${CMAKE_CURRENT_BINARY_DIR}/regress-ged_lint_brep.log")
endif(TARGET libged)
cmakefiles(
@@ -46,6 +54,7 @@ cmakefiles(
push.cpp
regress_facetize.cpp
regress_lint_raytrace.cpp
regress_lint_brep.cpp
d2push_tests.g
dpush_tests.g
dxpush_tests.g
@@ -56,6 +65,7 @@ cmakefiles(
regress-ged_push.cmake.in
regress-ged_facetize.cmake.in
regress-ged_lint_raytrace.cmake.in
regress-ged_lint_brep.cmake.in
rpush_tests.g
xpush_tests.g
)
@@ -0,0 +1,47 @@
# Values set at CMake configure time
set(CBDIR "@CMAKE_CURRENT_BINARY_DIR@")
set(LOGFILE "${CBDIR}/regress-ged_lint_brep.log")
set(BU_DIR_CACHE ${CBDIR}/cache_lint_brep)
set(LIBRT_CACHE ${CBDIR}/rtcache_lint_brep)
set(ENV{BU_DIR_CACHE} ${BU_DIR_CACHE})
set(ENV{LIBRT_CACHE} ${LIBRT_CACHE})
file(REMOVE_RECURSE "${BU_DIR_CACHE}")
file(REMOVE_RECURSE "${LIBRT_CACHE}")
file(MAKE_DIRECTORY "${BU_DIR_CACHE}")
file(MAKE_DIRECTORY "${LIBRT_CACHE}")
set(TMPDIR "${CBDIR}/lint_brep_regress_tmp")
file(REMOVE_RECURSE "${TMPDIR}")
file(MAKE_DIRECTORY "${TMPDIR}")
file(WRITE "${LOGFILE}" "Starting B-Rep lint regression test run\n")
string(REPLACE "\\" "" LINT_BREP_EXEC "${EXEC}")
if(NOT EXISTS "${LINT_BREP_EXEC}")
file(READ "${LOGFILE}" LOG)
message(FATAL_ERROR "B-Rep lint regression program not found.\n${LOG}")
endif(NOT EXISTS "${LINT_BREP_EXEC}")
execute_process(
COMMAND "${LINT_BREP_EXEC}"
RESULT_VARIABLE lint_brep_result
OUTPUT_VARIABLE lint_brep_log
ERROR_VARIABLE lint_brep_log
WORKING_DIRECTORY "${TMPDIR}"
)
file(APPEND "${LOGFILE}" "${lint_brep_log}")
if(lint_brep_result)
file(READ "${LOGFILE}" LOG)
message(FATAL_ERROR "[regress-ged_lint_brep] Failure: ${lint_brep_result}.\n${LOG}")
endif(lint_brep_result)
file(REMOVE_RECURSE "${TMPDIR}")
execute_process(COMMAND "@CMAKE_COMMAND@" -E rm -rf "${BU_DIR_CACHE}")
execute_process(COMMAND "@CMAKE_COMMAND@" -E rm -rf "${LIBRT_CACHE}")
# Local Variables:
# tab-width: 8
# mode: cmake
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+259 -16
View File
@@ -68,6 +68,13 @@
* ("/tc6_reg.c/sub6.s#sub"), so the lookup missed and no variant was
* applied, leaving the phantom face. After the fix the plan is built
* from region roots so keys match and the phantom face is eliminated.
*
* TC7 - NMG Boolean isolation and output modes: verifies that an interrupted
* worker cannot commit output, then exercises BoT, NMG, region, and
* in-place output paths.
*
* TC8 - implicit solid roots survive the working-database close and remain
* valid members of the imported output hierarchy.
*/
#include "common.h"
@@ -78,7 +85,9 @@
#include <string>
#include "bu/app.h"
#include "bu/env.h"
#include "bu/file.h"
#include "bu/log.h"
#include "bu/malloc.h"
#include "bu/str.h"
#include "bu/vls.h"
@@ -142,6 +151,36 @@ write_comb2(struct rt_wdb *wdbp,
0, 0, 0, 0, 0, 0, 0);
}
static int
write_comb1(struct rt_wdb *wdbp, const char *comb_name, const char *member, int is_region)
{
struct bu_list head;
BU_LIST_INIT(&head);
mk_addmember(member, &head, NULL, WMOP_UNION);
return mk_comb(wdbp, comb_name, &head, is_region, NULL, NULL, NULL,
0, 0, 0, 0, 0, 0, 0);
}
static int
run_facetize_command(const char *gfile, int argc, const char **argv, int verbose)
{
struct ged *gedp = ged_open("db", gfile, 1);
if (!gedp) {
bu_log("[regress_facetize] ged_open(%s) failed\n", gfile);
return BRLCAD_ERROR;
}
int ret = ged_exec(gedp, argc, argv);
if (verbose || ret != BRLCAD_OK) {
const char *log = bu_vls_cstr(gedp->ged_result_str);
if (log && log[0])
bu_log("[facetize] %s\n", log);
}
ged_close(gedp);
return ret;
}
/**
* Open @a gfile with ged_open and run "facetize @a input @a output".
* Returns BRLCAD_OK on success, BRLCAD_ERROR otherwise.
@@ -149,23 +188,8 @@ write_comb2(struct rt_wdb *wdbp,
static int
run_facetize(const char *gfile, const char *input, const char *output, int verbose)
{
struct ged *gedp = ged_open("db", gfile, 1);
if (!gedp) {
bu_log("[regress_facetize] ged_open(%s) failed\n", gfile);
return BRLCAD_ERROR;
}
const char *av[4] = {"facetize", input, output, NULL};
int ret = ged_exec(gedp, 3, av);
if (verbose || ret != BRLCAD_OK) {
const char *log = bu_vls_cstr(gedp->ged_result_str);
if (log && log[0])
bu_log("[facetize] %s\n", log);
}
ged_close(gedp);
return ret;
return run_facetize_command(gfile, 3, av, verbose);
}
/**
@@ -212,6 +236,46 @@ bu_log("[regress_facetize] check_bot: '%s' type %d is not a BoT\n",
return ok;
}
static int
check_object_type(const char *gfile, const char *object_name, int expected_type)
{
struct db_i *dbip = db_open(gfile, DB_OPEN_READONLY);
if (!dbip || db_dirbuild(dbip) < 0) {
if (dbip)
db_close(dbip);
return BRLCAD_ERROR;
}
struct directory *dp = db_lookup(dbip, object_name, LOOKUP_QUIET);
if (!dp) {
bu_log("[regress_facetize] '%s' does not exist\n", object_name);
db_close(dbip);
return BRLCAD_ERROR;
}
struct rt_db_internal intern;
RT_DB_INTERNAL_INIT(&intern);
if (rt_db_get_internal(&intern, dp, dbip, NULL) < 0) {
db_close(dbip);
return BRLCAD_ERROR;
}
int ret = (intern.idb_minor_type == expected_type) ? BRLCAD_OK : BRLCAD_ERROR;
if (ret != BRLCAD_OK)
bu_log("[regress_facetize] '%s' type %d, expected %d\n",
object_name, intern.idb_minor_type, expected_type);
rt_db_free_internal(&intern);
db_close(dbip);
return ret;
}
static int
count_log_hook(void *data, void *UNUSED(message))
{
int *count = (int *)data;
(*count)++;
return 0;
}
/**
* Run rt_crofton_shoot on @a obj_name in @a gfile.
* Stores SA (mm2) and VOL (mm3) in *sa / *vol.
@@ -1120,6 +1184,183 @@ tc6_havoc_wind9_pattern(const char *tmpdir, int verbose)
return BRLCAD_OK;
}
static int
tc7_nmg_modes(const char *tmpdir, int verbose)
{
bu_log("[regress_facetize] TC7: NMG Boolean and output modes...\n");
struct bu_vls gpath = BU_VLS_INIT_ZERO;
bu_vls_printf(&gpath, "%s/tc7_nmg.g", tmpdir);
const char *gfile = bu_vls_cstr(&gpath);
if (bu_file_exists(gfile, NULL))
bu_file_delete(gfile);
struct db_i *dbip = db_create(gfile, 5);
if (!dbip) {
bu_vls_free(&gpath);
return BRLCAD_ERROR;
}
struct rt_wdb *wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DEFAULT);
point_t box_pts[8] = {
{0.0, 0.0, 0.0}, {10.0, 0.0, 0.0},
{10.0, 10.0, 0.0}, {0.0, 10.0, 0.0},
{0.0, 0.0, 10.0}, {10.0, 0.0, 10.0},
{10.0, 10.0, 10.0}, {0.0, 10.0, 10.0}
};
if (write_arb8(wdbp, "box.s", box_pts) < 0 ||
write_comb1(wdbp, "box.r", "box.s", 1) < 0 ||
write_comb1(wdbp, "box.c", "box.s", 0) < 0) {
wdb_close(wdbp);
bu_vls_free(&gpath);
return BRLCAD_ERROR;
}
wdb_close(wdbp);
const char *bot_av[] = {"facetize", "--nmg-booleval", "box.r", "box.bot", NULL};
const char *fault_av[] = {"facetize", "--nmg-booleval", "box.r", "fault.bot", NULL};
const char *nmg_av[] = {"facetize", "-n", "box.r", "box.nmg", NULL};
const char *regions_av[] = {"facetize", "-r", "--nmg-booleval", "box.r", "box.regions", NULL};
const char *fallback_av[] = {"facetize", "-r", "--nmg-booleval", "box.c", "box-fallback.bot", NULL};
const char *object_in_place_av[] = {"facetize", "--in-place", "--nmg-booleval", "box.c", NULL};
const char *in_place_av[] = {"facetize", "-r", "--in-place", "--nmg-booleval", "box.r", NULL};
int ret = BRLCAD_OK;
auto check_tc7 = [&ret](bool passed, const char *description) {
if (passed)
return;
bu_log("[regress_facetize] TC7: FAIL - %s\n", description);
ret = BRLCAD_ERROR;
};
struct bu_vls fault_path = BU_VLS_INIT_ZERO;
bu_vls_printf(&fault_path, "%s/tc7_nmg_worker.fault", tmpdir);
(void)bu_file_delete(bu_vls_cstr(&fault_path));
(void)bu_setenv("LIBGED_FACETIZE_TEST_FAULT",
"nmg-before-ready:fault.bot", 1);
(void)bu_setenv("LIBGED_FACETIZE_TEST_FAULT_FILE",
bu_vls_cstr(&fault_path), 1);
int fault_ret = run_facetize_command(gfile, 4, fault_av, verbose);
(void)bu_setenv("LIBGED_FACETIZE_TEST_FAULT", "", 1);
(void)bu_setenv("LIBGED_FACETIZE_TEST_FAULT_FILE", "", 1);
check_tc7(fault_ret == BRLCAD_ERROR,
"an interrupted NMG Boolean worker did not report failure");
check_tc7(bu_file_exists(bu_vls_cstr(&fault_path), NULL),
"NMG Boolean worker fault did not trigger");
check_tc7(check_object_type(gfile, "fault.bot", ID_BOT) != BRLCAD_OK,
"an interrupted NMG Boolean worker committed an output");
(void)bu_file_delete(bu_vls_cstr(&fault_path));
bu_vls_free(&fault_path);
int hook_calls = 0;
bu_log_add_hook(count_log_hook, &hook_calls);
int bot_ret = run_facetize_command(gfile, 4, bot_av, verbose);
int hook_calls_before_probe = hook_calls;
bu_log("facetize NMG hook restoration probe\n");
bu_log_delete_hook(count_log_hook, &hook_calls);
check_tc7(bot_ret == BRLCAD_OK, "NMG Boolean BoT command failed");
check_tc7(hook_calls == hook_calls_before_probe + 1,
"temporary log hook was not restored");
check_tc7(check_bot_exists(gfile, "box.bot") == BRLCAD_OK,
"NMG Boolean BoT output is missing");
int nmg_ret = run_facetize_command(gfile, 4, nmg_av, verbose);
check_tc7(nmg_ret == BRLCAD_OK, "NMG output command failed");
check_tc7(check_object_type(gfile, "box.nmg", ID_NMG) == BRLCAD_OK,
"NMG output has the wrong type");
int regions_ret = run_facetize_command(gfile, 5, regions_av, verbose);
check_tc7(regions_ret == BRLCAD_OK, "NMG Boolean region command failed");
check_tc7(check_object_type(gfile, "box.regions", ID_COMBINATION) == BRLCAD_OK,
"NMG Boolean region output is missing");
int fallback_ret = run_facetize_command(gfile, 5, fallback_av, verbose);
check_tc7(fallback_ret == BRLCAD_OK,
"region mode object fallback command failed");
check_tc7(check_bot_exists(gfile, "box-fallback.bot") == BRLCAD_OK,
"region mode object fallback output is missing");
int object_in_place_ret = run_facetize_command(gfile, 4,
object_in_place_av, verbose);
check_tc7(object_in_place_ret == BRLCAD_OK,
"in-place NMG Boolean object command failed");
check_tc7(check_object_type(gfile, "box.c", ID_BOT) == BRLCAD_OK,
"in-place NMG Boolean object has the wrong type");
int in_place_ret = run_facetize_command(gfile, 5, in_place_av, verbose);
check_tc7(in_place_ret == BRLCAD_OK,
"in-place NMG Boolean region command failed");
check_tc7(check_object_type(gfile, "box.r", ID_COMBINATION) == BRLCAD_OK,
"in-place region root has the wrong type");
check_tc7(check_bot_exists(gfile, "box.r.bot") == BRLCAD_OK,
"in-place region BoT output is missing");
bu_log("[regress_facetize] TC7: %s\n", ret == BRLCAD_OK ? "PASS" : "FAIL");
bu_vls_free(&gpath);
return ret;
}
/* ------------------------------------------------------------------ */
/* TC8: implicit root replacement persists across database reopen */
/* ------------------------------------------------------------------ */
static int
tc8_implicit_root_persistence(const char *tmpdir, int verbose)
{
bu_log("[regress_facetize] TC8: implicit root persistence...\n");
struct bu_vls gpath = BU_VLS_INIT_ZERO;
bu_vls_printf(&gpath, "%s/tc8_implicit_root.g", tmpdir);
const char *gfile = bu_vls_cstr(&gpath);
if (bu_file_exists(gfile, NULL))
bu_file_delete(gfile);
struct db_i *dbip = db_create(gfile, 5);
if (!dbip) {
bu_vls_free(&gpath);
return BRLCAD_ERROR;
}
struct rt_wdb *wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DEFAULT);
point_t region_points[8] = {
{0.0, 0.0, 0.0}, {10.0, 0.0, 0.0},
{10.0, 10.0, 0.0}, {0.0, 10.0, 0.0},
{0.0, 0.0, 10.0}, {10.0, 0.0, 10.0},
{10.0, 10.0, 10.0}, {0.0, 10.0, 10.0}
};
point_t loose_points[8] = {
{20.0, 0.0, 0.0}, {30.0, 0.0, 0.0},
{30.0, 10.0, 0.0}, {20.0, 10.0, 0.0},
{20.0, 0.0, 10.0}, {30.0, 0.0, 10.0},
{30.0, 10.0, 10.0}, {20.0, 10.0, 10.0}
};
if (!wdbp || write_arb8(wdbp, "region.s", region_points) < 0 ||
write_arb8(wdbp, "loose.s", loose_points) < 0 ||
write_comb1(wdbp, "box.r", "region.s", 1) < 0 ||
write_comb2(wdbp, "assembly.c", "box.r", 'u',
"loose.s", 'u') < 0) {
if (wdbp)
wdb_close(wdbp);
bu_vls_free(&gpath);
return BRLCAD_ERROR;
}
wdb_close(wdbp);
const char *facetize_args[] = {
"facetize", "-r", "assembly.c", "assembly.regions", NULL
};
int ret = run_facetize_command(gfile, 4, facetize_args, verbose);
if (ret == BRLCAD_OK &&
(check_object_type(gfile, "assembly.regions", ID_COMBINATION) != BRLCAD_OK ||
check_object_type(gfile, "facetize_assembly.c", ID_COMBINATION) != BRLCAD_OK ||
check_bot_exists(gfile, "box.r.bot") != BRLCAD_OK ||
check_bot_exists(gfile, "facetize_loose.s") != BRLCAD_OK))
ret = BRLCAD_ERROR;
bu_log("[regress_facetize] TC8: %s\n",
ret == BRLCAD_OK ? "PASS" : "FAIL");
bu_vls_free(&gpath);
return ret;
}
/* ------------------------------------------------------------------ */
/* main */
/* ------------------------------------------------------------------ */
@@ -1151,6 +1392,8 @@ return 1;
if (tc4_havoc_reuse(tmpdir, verbose) != BRLCAD_OK) ret = 1;
if (tc5_near_coplanar_subTOL(tmpdir, verbose) != BRLCAD_OK) ret = 1;
if (tc6_havoc_wind9_pattern(tmpdir, verbose) != BRLCAD_OK) ret = 1;
if (tc7_nmg_modes(tmpdir, verbose) != BRLCAD_OK) ret = 1;
if (tc8_implicit_root_persistence(tmpdir, verbose) != BRLCAD_OK) ret = 1;
if (ret == 0)
bu_log("[regress_facetize] All tests PASSED\n");
+259
View File
@@ -0,0 +1,259 @@
/* R E G R E S S _ L I N T _ B R E P . C P P
* BRL-CAD
*
* Copyright (c) 2026 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; see the file named COPYING for more
* information.
*/
/** @file regress_lint_brep.cpp
*
* Regression tests for the bounded B-Rep geometry lint checks.
*/
#include "common.h"
#include <cstdio>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
#include "bu/app.h"
#include "bu/file.h"
#include "bu/vls.h"
#include "ged.h"
#include "raytrace.h"
#include "wdb.h"
static std::string
read_file(const char *path)
{
std::ifstream f(path);
if (!f.is_open())
return std::string();
return std::string((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
}
static std::string
temporary_path(const char *suffix)
{
char path[MAXPATHLEN] = {0};
FILE *fp = bu_temp_file(path, MAXPATHLEN);
if (fp)
fclose(fp);
struct bu_vls result = BU_VLS_INIT_ZERO;
bu_vls_sprintf(&result, "%s_%s", path, suffix);
std::string ret(bu_vls_cstr(&result));
bu_vls_free(&result);
return ret;
}
static ON_Brep *
box_brep()
{
ON_3dPoint corners[8] = {
ON_3dPoint(0.0, 0.0, 0.0), ON_3dPoint(10.0, 0.0, 0.0),
ON_3dPoint(10.0, 10.0, 0.0), ON_3dPoint(0.0, 10.0, 0.0),
ON_3dPoint(0.0, 0.0, 10.0), ON_3dPoint(10.0, 0.0, 10.0),
ON_3dPoint(10.0, 10.0, 10.0), ON_3dPoint(0.0, 10.0, 10.0)
};
return ON_BrepBox(corners);
}
static bool
make_database(const char *path)
{
struct rt_wdb *wdbp = wdb_fopen(path);
if (!wdbp)
return false;
ON_Brep *good = box_brep();
ON_Brep *bad = box_brep();
ON_Brep *shading_failure = box_brep();
if (!good || !bad || !shading_failure) {
delete good;
delete bad;
delete shading_failure;
db_close(wdbp->dbip);
return false;
}
ON_BrepLoop *failed_loop = shading_failure->m_F[0].OuterLoop();
if (!failed_loop) {
delete good;
delete bad;
delete shading_failure;
db_close(wdbp->dbip);
return false;
}
shading_failure->FlipLoop(*failed_loop);
failed_loop->m_type = ON_BrepLoop::inner;
ON_BrepEdge &edge = bad->m_E[0];
ON_Curve *curve = (edge.m_c3i >= 0 && edge.m_c3i < bad->m_C3.Count()) ?
bad->m_C3[edge.m_c3i] : NULL;
const ON_Xform shift = ON_Xform::TranslationTransformation(
ON_3dVector(0.0, 0.0, 1.0));
if (!curve || !curve->Transform(shift)) {
delete good;
delete bad;
db_close(wdbp->dbip);
return false;
}
edge.m_tolerance = 5.0;
for (int vi = 0; vi < 2; ++vi) {
ON_BrepVertex *vertex = edge.Vertex(vi);
if (vertex)
vertex->m_tolerance = 5.0;
}
for (int eti = 0; eti < edge.m_ti.Count(); ++eti) {
const int ti = edge.m_ti[eti];
if (ti >= 0 && ti < bad->m_T.Count()) {
bad->m_T[ti].m_tolerance[0] = 5.0;
bad->m_T[ti].m_tolerance[1] = 5.0;
}
}
ON_TextLog text_log(stderr);
const bool valid = good->IsValid(&text_log) && bad->IsValid(&text_log);
const bool written = valid && mk_brep(wdbp, "good.s", good) == 0 &&
mk_brep(wdbp, "masked_mismatch.s", bad) == 0 &&
mk_brep(wdbp, "shading_failure.s", shading_failure) == 0;
delete good;
delete bad;
delete shading_failure;
db_close(wdbp->dbip);
return written;
}
static int
run_lint(const char *gfile, const char *object, const char *json_file,
const char *techniques)
{
struct ged *gedp = ged_open("db", gfile, 1);
if (!gedp)
return BRLCAD_ERROR;
const char *argv[] = {
"lint", "-I", techniques,
"-j", json_file, object
};
const int ret = ged_exec(gedp, 6, argv);
ged_close(gedp);
return ret;
}
static bool
contains_problem(const std::string &json, const char *problem)
{
struct bu_vls pattern = BU_VLS_INIT_ZERO;
bu_vls_sprintf(&pattern, "\"problem_type\": \"%s\"", problem);
const bool found = json.find(bu_vls_cstr(&pattern)) != std::string::npos;
bu_vls_free(&pattern);
return found;
}
int
main(int argc, char **argv)
{
bu_setprogname(argv[0]);
if (argc == 4) {
const char *checks = "brep:edge_surface_mismatch brep:large_tolerance "
"brep:singular_boundary brep:trim_loop_crossing";
return run_lint(argv[1], argv[2], argv[3], checks);
}
if (argc != 1)
return 1;
const std::string gfile = temporary_path("lint_brep.g");
const std::string good_json = temporary_path("lint_brep_good.json");
const std::string bad_json = temporary_path("lint_brep_bad.json");
const std::string shading_json = temporary_path("lint_brep_shading.json");
bool pass = make_database(gfile.c_str());
const char *checks = "brep:edge_surface_mismatch brep:large_tolerance "
"brep:singular_boundary brep:trim_loop_crossing";
if (pass)
pass = run_lint(gfile.c_str(), "good.s", good_json.c_str(), checks) ==
BRLCAD_OK;
const std::string good_result = read_file(good_json.c_str());
if (pass)
pass = !contains_problem(good_result, "edge_surface_mismatch") &&
!contains_problem(good_result, "large_tolerance") &&
!contains_problem(good_result, "singular_boundary") &&
!contains_problem(good_result, "trim_loop_crossing");
if (pass)
pass = run_lint(gfile.c_str(), "masked_mismatch.s", bad_json.c_str(),
checks) == BRLCAD_OK;
const std::string bad_result = read_file(bad_json.c_str());
if (pass)
pass = contains_problem(bad_result, "edge_surface_mismatch") &&
contains_problem(bad_result, "large_tolerance") &&
bad_result.find("\"declared_tolerance_masks\": true") !=
std::string::npos;
if (pass)
pass = run_lint(gfile.c_str(), "good.s", shading_json.c_str(),
"brep:fast_shading") == BRLCAD_OK;
std::string shading_result = read_file(shading_json.c_str());
if (pass)
pass = !contains_problem(shading_result, "fast_shading_failure") &&
!contains_problem(shading_result, "fast_shading_incomplete");
if (pass)
pass = run_lint(gfile.c_str(), "shading_failure.s",
shading_json.c_str(), "brep:opennurbs") == BRLCAD_OK;
shading_result = read_file(shading_json.c_str());
if (pass)
pass = contains_problem(shading_result, "opennurbs_invalid") &&
!contains_problem(shading_result, "fast_shading_failure") &&
!contains_problem(shading_result, "fast_shading_incomplete");
if (pass)
pass = run_lint(gfile.c_str(), "shading_failure.s",
shading_json.c_str(), "brep:fast_shading") == BRLCAD_OK;
shading_result = read_file(shading_json.c_str());
if (pass)
pass = contains_problem(shading_result, "fast_shading_failure") &&
shading_result.find("\"face\": 0") != std::string::npos &&
shading_result.find("\"stage_name\": \"pslg_validation\"") !=
std::string::npos;
bu_file_delete(gfile.c_str());
bu_file_delete(good_json.c_str());
bu_file_delete(bad_json.c_str());
bu_file_delete(shading_json.c_str());
if (!pass)
bu_log("B-Rep lint regression failed\n");
return pass ? 0 : 1;
}
// Local Variables:
// tab-width: 8
// mode: C++
// c-basic-offset: 4
// indent-tabs-mode: t
// c-file-style: "stroustrup"
// End:
// ex: shiftwidth=4 tabstop=8
+8
View File
@@ -100,6 +100,7 @@ if(TARGET mged)
continue()
endif(TNAME STREQUAL "")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${testfile}")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/${testfile}" TEST_CONTENTS)
if(TNAME STREQUAL "search_exec")
string(CONFIGURE "${TEST_CONTENTS}" TEST_CONTENTS @ONLY)
@@ -126,6 +127,12 @@ if(TARGET mged)
elseif(TNAME STREQUAL "arb")
set(TEST_EXPECT "'arb' testing completed")
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
elseif(TNAME MATCHES "^(extrude|facedef|mirface|permute)$")
set(TEST_EXPECT "'${TNAME}' testing completed")
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
elseif(TNAME STREQUAL "edgedir")
set(TEST_EXPECT "'edarb edgedir' testing completed")
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
elseif(TNAME STREQUAL "killtree")
set(TEST_EXPECT "KILL Solid: killtree_dup.s")
set(TEST_REJECT "db_lookup(killtree_dup.s) failed")
@@ -187,6 +194,7 @@ distclean(${mged_outfiles})
cmakefiles(
CMakeLists.txt
csg_edit.mged
mged.tcl
regression_resources.tcl
search_exec_procs.tcl
+406
View File
@@ -962,8 +962,140 @@ foreach test [list \
csg_matrix_mouse_check {*}$test
}
puts "--- Repeated graphical object transforms ---"
proc csg_matrix_repeated_mouse_check {} {
global orig_gui transform csg_mouse_dm_pixels csg_mouse_view_size
global csg_drag_scale
set prim csg_matrix_repeat.s
set inner csg_matrix_repeat_inner.c
set outer csg_matrix_repeat_outer.c
set saved_orig_gui $orig_gui
set expected [csg_edit_identity_matrix]
set failed [catch {
in $prim sph 0 0 0 10
comb $inner u $prim
comb $outer u $inner
set orig_gui 0
for {set step 0} {$step < 16} {incr step} {
e $outer
center 0 0 0
size $csg_mouse_view_size
setview 0 0 0
dm size $csg_mouse_dm_pixels $csg_mouse_dm_pixels
_mged_press oill
_mged_ill -e -i 1 /$outer/$inner/$prim
_mged_matpick 2
if {[status state] ne "OBJ EDIT" || $transform ne "e"} {
error "step $step did not enter object editing"
}
set view_before [csg_edit_view_state]
if {$step % 2 == 0} {
press Rotate
dm con r z 100 100
set operation [csg_matrix_axis_rotation z 10]
set motion {dm motion 140 100}
} else {
press Scale
dm am s 100 100
set operation [csg_matrix_scale all $csg_drag_scale]
set motion {dm motion 164 100}
}
set motion_code [catch {uplevel #0 $motion} motion_result]
if {$motion_code != 0 && $motion_code != 2} {
error "step $step mouse motion failed: $motion_result"
}
dm idle
press accept
set expected [csg_matrix_multiply $operation $expected]
csg_edit_comb_leaf_matrix_at $inner $prim $expected
csg_edit_view_state_at $view_before
d $outer
}
} failure]
set orig_gui $saved_orig_gui
catch {press reject}
catch {Z}
foreach object [list $outer $inner $prim] {
catch {kill $object}
}
if {$failed} {
puts " FAIL: repeated object mouse rotations/scales ($failure)"
} else {
puts " PASS: repeated object mouse rotations/scales"
}
}
csg_matrix_repeated_mouse_check
puts "--- All ARB point, edge, and face states ---"
proc csg_edit_sed_point {attribute} {
set serialized [get_sed]
set index [lsearch -exact $serialized $attribute]
if {$index < 0 || $index + 1 >= [llength $serialized]} {
error "get_sed has no $attribute: $serialized"
}
return [lindex $serialized [expr {$index + 1}]]
}
proc csg_arb_repeated_mouse_check {} {
global orig_gui csg_bv_coord_half_range csg_mouse_dm_pixels
global csg_mouse_view_size
set source csg_lifecycle_arb5.s
set prim csg_arb_repeat.s
set saved_orig_gui $orig_gui
set expected {}
set failed [catch {
cp $source $prim
e $prim
center 0 0 0
size $csg_mouse_view_size
setview 0 0 0
dm size $csg_mouse_dm_pixels $csg_mouse_dm_pixels
set orig_gui 0
sed $prim
press {Move Edges}
press {Move Point 5}
set original_z [lindex [db get $source V5] 2]
for {set step 0} {$step < 64} {incr step} {
if {$step > 0 && $step % 8 == 0} {
press sedit
press {Move Edges}
press {Move Point 5}
}
set mouse_x [expr {150 + 2 * ($step % 5)}]
set mouse_y [expr {150 + 2 * (($step * 3) % 5)}]
M 1 $mouse_x $mouse_y
set expected [list \
[expr {$mouse_x / $csg_bv_coord_half_range * $csg_mouse_view_size / 2.0}] \
[expr {$mouse_y / $csg_bv_coord_half_range * $csg_mouse_view_size / 2.0}] \
$original_z]
set actual [csg_edit_sed_point V5]
if {[csg_edit_point_distance $actual $expected] > 0.000001} {
error "step $step expected V5 $expected, got $actual"
}
}
press accept
d $prim
if {[csg_edit_point_distance [db get $prim V5] $expected] > 0.000001} {
error "accepted V5 differs from the final mouse target"
}
foreach vertex {V1 V2 V3 V4} {
csg_edit_point_matches_source $source $vertex $prim
}
arb_standard_storage_check arb5 $prim
} failure]
set orig_gui $saved_orig_gui
catch {press reject}
catch {d $prim}
catch {kill $prim}
if {$failed} {
puts " FAIL: repeated ARB5 mouse movement ($failure)"
} else {
puts " PASS: repeated ARB5 mouse movement"
}
}
csg_arb_repeated_mouse_check
# Every ARB menu entry is checked independently. The variants do not share
# menu indices, so sampling one edge or face would miss precisely the class of
# integration failure this test is intended to catch. Move each selection a
@@ -1322,6 +1454,280 @@ csg_edit_sequence_check csg_lifecycle_dsp.s "Toggle Smooth" {
{press {Toggle Smooth}}
} [list csg_edit_scalar_at sm 1]
puts "--- Specialized mouse editing and menu inventory ---"
proc csg_edit_prepare_mouse_view {} {
global csg_dispatch_view_size
center 0 0 0
size $csg_dispatch_view_size
setview 0 0 0
}
array set csg_mouse_covered {}
proc csg_mouse_case {source menu_items label commands validator} {
global csg_mouse_covered
csg_edit_sequence_check $source $label $commands $validator
set type [lindex [db get $source] 0]
if {![info exists csg_mouse_covered($type)]} {
set csg_mouse_covered($type) {}
}
foreach item $menu_items {
if {[lsearch -exact $csg_mouse_covered($type) $item] < 0} {
lappend csg_mouse_covered($type) $item
}
}
}
proc csg_edit_menu_inventory_check {source ignored} {
global csg_mouse_covered
if {[catch {
Z
e $source
sed $source
set actual [mmenu_get 0]
set type [lindex [db get $source] 0]
set covered {}
if {[info exists csg_mouse_covered($type)]} {
set covered $csg_mouse_covered($type)
}
foreach item $actual {
set tested [expr {[lsearch -exact $covered $item] >= 0}]
set exempt [expr {[lsearch -exact $ignored $item] >= 0}]
if {$tested == $exempt} {
error "menu entry '$item' needs exactly one mouse-test or exclusion"
}
}
foreach item [concat $covered $ignored] {
if {[lsearch -exact $actual $item] < 0} {
error "recorded menu entry '$item' is absent from $actual"
}
}
press reject
d $source
} failure]} {
puts " FAIL: menu inventory $source ($failure)"
catch {press reject}
catch {Z}
} else {
puts " PASS: menu inventory $source"
}
}
csg_edit_prepare_mouse_view
set csg_special_mouse_target [list \
[expr {512.0 / $csg_bv_coord_half_range * $csg_dispatch_view_size / 2.0}] \
[expr {512.0 / $csg_bv_coord_half_range * $csg_dispatch_view_size / 2.0}] 0]
set csg_special_mouse_scale [expr {1.0 + 2047.0 / ($csg_bv_coord_half_range * 4.0)}]
set csg_special_mouse_shrink [expr {
1.0 / (1.0 + 1024.0 / ($csg_bv_coord_half_range * 4.0))}]
csg_mouse_case csg_lifecycle_pipe.s {{Move Point}} "mouse select and move point" {
{csg_edit_prepare_mouse_view}
{press {Select Point}}
{p 0 0 0}
{press {Move Point}}
{M 1 512 512}
} [list csg_edit_point_at V0 $csg_special_mouse_target]
in csg_mouse_pipe_topology.s pipe 4 \
0 0 0 1 2 3 \
0 0 30 1 2 3 \
30 40 80 1 2 3 \
80 80 100 1 2 3
csg_mouse_case csg_mouse_pipe_topology.s {{Select Point} {Move Point}} "mouse select point" {
{csg_edit_prepare_mouse_view}
{press {Select Point}}
{M 1 0 0}
{press {Move Point}}
{M 1 512 512}
} [list csg_edit_point_at V0 $csg_special_mouse_target]
csg_mouse_case csg_mouse_pipe_topology.s {{Append Point}} "mouse append point" {
{csg_edit_prepare_mouse_view}
{press {Append Point}}
{M 1 512 512}
} [list csg_edit_point_at V4 {5 5 0}]
csg_mouse_case csg_mouse_pipe_topology.s {{Prepend Point}} "mouse prepend point" {
{csg_edit_prepare_mouse_view}
{press {Prepend Point}}
{M 1 512 512}
} [list csg_edit_point_at V0 {5 5 0}]
csg_mouse_case csg_mouse_pipe_topology.s {{Split Segment}} "mouse split segment" {
{csg_edit_prepare_mouse_view}
{press {Select Point}}
{p 0 0 0}
{press {Split Segment}}
{M 1 512 512}
} [list csg_edit_point_at V1 {5 5 0}]
foreach {item attribute initial} {
{Set Point OD} O0 5
{Set Point ID} I0 3
{Set Point Bend} R0 6
} {
csg_mouse_case csg_lifecycle_pipe.s [list $item] "mouse $item" [list \
{csg_edit_prepare_mouse_view} \
{press {Select Point}} {p 0 0 0} [list press $item] \
{M 1 0 -1024}] \
[list csg_edit_scalar_at $attribute [expr {$initial * $csg_special_mouse_shrink}]]
}
foreach {item attributes initial} {
{Set Pipe OD} {O0 O1 O2 O3} {5 5 6 6}
{Set Pipe ID} {I0 I1 I2 I3} {3 3 2 0}
{Set Pipe Bend} {R0 R1 R2 R3} {6 7 10 8}
} {
set expected {}
foreach value $initial {
lappend expected [expr {$value * $csg_special_mouse_shrink}]
}
csg_mouse_case csg_lifecycle_pipe.s [list $item] "mouse $item" [list \
{csg_edit_prepare_mouse_view} \
[list press $item] {M 1 0 -1024}] \
[list csg_edit_scalar_values_at $attributes $expected]
}
proc csg_edit_bot_vertices_moved {source moved delta prim} {
set vertices [db get $source V]
for {set index 0} {$index < [llength $vertices]} {incr index} {
set expected [lindex $vertices $index]
if {[lsearch -exact $moved $index] >= 0} {
set translated {}
foreach value $expected shift $delta {
lappend translated [expr {$value + $shift}]
}
set expected $translated
}
csg_edit_list_value_at V $index $expected $prim
}
}
csg_mouse_case csg_lifecycle_bot.s {{Move Vertex}} "mouse move vertex" {
{csg_edit_prepare_mouse_view}
{press {Pick Vertex}}
{p 0}
{press {Move Vertex}}
{M 1 512 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0} \
$csg_special_mouse_target]
csg_mouse_case csg_lifecycle_bot.s {{Pick Vertex} {Move Vertex}} "mouse pick and move vertex" {
{csg_edit_prepare_mouse_view}
{press {Pick Vertex}}
{M 1 0 0}
{press {Move Vertex}}
{M 1 512 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0} \
$csg_special_mouse_target]
csg_mouse_case csg_lifecycle_bot.s {{Move Edge}} "mouse move edge" {
{csg_edit_prepare_mouse_view}
{press {Pick Edge}}
{p 0 1}
{press {Move Edge}}
{M 1 1024 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0 1} {10 5 0}]
csg_mouse_case csg_lifecycle_bot.s {{Pick Edge} {Move Edge}} "mouse pick and move edge" {
{csg_edit_prepare_mouse_view}
{press {Pick Edge}}
{M 1 512 512}
{press {Move Edge}}
{M 1 1024 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0 1} {10 5 0}]
csg_mouse_case csg_lifecycle_bot.s {{Move Triangle}} "mouse move triangle" {
{csg_edit_prepare_mouse_view}
{press {Pick Triangle}}
{p 0 1 2}
{press {Move Triangle}}
{M 1 512 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0 1 2} \
$csg_special_mouse_target]
csg_mouse_case csg_lifecycle_ebm.s {{Extrude Depth}} "mouse extrusion depth" {
{csg_edit_prepare_mouse_view}
{press {Extrude Depth}}
{M 1 0 2047}
} [list csg_edit_scalar_at H [expr {4.0 * $csg_special_mouse_scale}]]
csg_mouse_case csg_lifecycle_vol.s {{Threshold (low)}} "mouse low threshold" {
{csg_edit_prepare_mouse_view}
{press {Threshold (low)}}
{p 10}
{M 1 0 2047}
} [list csg_edit_scalar_at lo [expr {int(10.0 * $csg_special_mouse_scale)}]]
csg_mouse_case csg_lifecycle_vol.s {{Voxel Size (X Y Z)}} "mouse voxel size" {
{csg_edit_prepare_mouse_view}
{press {Voxel Size (X Y Z)}}
{M 1 0 2047}
} [list csg_edit_point_at size [list \
$csg_special_mouse_scale $csg_special_mouse_scale $csg_special_mouse_scale]]
csg_mouse_case csg_lifecycle_vol.s {{Threshold (hi)}} "mouse high threshold" {
{csg_edit_prepare_mouse_view}
{press {Threshold (hi)}}
{M 1 0 -1024}
} [list csg_edit_scalar_at hi [expr {int(255.0 * $csg_special_mouse_shrink)}]]
csg_mouse_case csg_lifecycle_dsp.s {{Set X}} "mouse X scale" {
{csg_edit_prepare_mouse_view}
{press {Set X}}
{M 1 0 2047}
} [list csg_edit_matrix_component_at stom 0 $csg_special_mouse_scale]
foreach {item index} {{Set Y} 5 {Set ALT} 10} {
csg_mouse_case csg_lifecycle_dsp.s [list $item] "mouse $item" [list \
{csg_edit_prepare_mouse_view} \
[list press $item] {M 1 0 2047}] \
[list csg_edit_matrix_component_at stom $index $csg_special_mouse_scale]
}
foreach type {arbn brep} {
set source csg_lifecycle_${type}.s
csg_edit_sequence_check $source "generic mouse translation" {
{csg_edit_prepare_mouse_view}
{keypoint 0 0 0}
{press sxy}
{M 1 512 512}
} [list csg_edit_bbox_translated $source $csg_special_mouse_target]
}
# Every specialized menu entry must be accounted for. Exclusions are menu
# headings, non-positional actions/settings, or the BOT triangle picker (tested
# under Xmin because it needs Tk). A new entry without a mouse case or
# explicit exclusion fails here.
foreach {source ignored} {
csg_lifecycle_pipe.s {
{PIPE MENU} {Next Point} {Previous Point} {Delete Point}
}
csg_lifecycle_bot.s {
{BOT MENU} {Pick Triangle} {Move Vertex List}
{Split Edge} {Split Face} {Fuse Vertices} {Fuse Faces}
{Delete Triangle} {Select Mode} {Select Orientation}
{Set flags} {Set Face Thickness} {Set Face Mode}
}
csg_lifecycle_dsp.s {
{DSP MENU} Name {Toggle Smooth} {Set Data Source}
}
csg_lifecycle_ebm.s {
{EBM MENU} {File Name} {File Size (W N)}
}
csg_lifecycle_vol.s {
{VOL MENU} {File Name} {File Size (X Y Z)}
}
csg_lifecycle_arbn.s {}
csg_lifecycle_brep.s {}
} {
csg_edit_menu_inventory_check $source $ignored
}
# A view-space request must remain view-space even when the editor's current
# coordinate system is object-relative. This is the fast path underlying the
# corresponding real mouse drag in the Xmin GUI test.
set csg_saved_coords [rset var coords]
set csg_saved_transform [rset var transform]
if {[catch {
Z
e csg_lifecycle_bot.s
sed csg_lifecycle_bot.s
center 0 0 0
rset var coords o
rset var transform v
knob -i -v aX 1
if {[csg_edit_point_distance [center] {-1 0 0}] > 0.000001} {
error "explicit view knob left center at [center]"
}
} csg_view_override_error]} {
puts " FAIL: view-space translation with object coordinates ($csg_view_override_error)"
} else {
puts " PASS: view-space translation with object coordinates"
}
catch {press reject}
catch {Z}
rset var coords $csg_saved_coords
rset var transform $csg_saved_transform
file delete -force $csg_data_file
file delete -force $csg_dsp_file
+33
View File
@@ -43,4 +43,37 @@ foreach {arb_type face expected_type} $arb_extrude_cases {
arb_standard_storage_check $expected_type $prim
}
# A one-inch extrusion must extend a face by 25.4 base millimeters through
# both the direct libged command and MGED's interactive solid editor.
set inch_in_mm 25.4
set tolerance 0.001
foreach mode {direct interactive} {
set suffix "_inch_$mode"
in_arb8 extrude $suffix
set prim "extrude_arb8${suffix}.s"
set base_x [lindex [db get $prim V1] 0]
units in
if {$mode eq "direct"} {
if {[catch {edarb extrude $prim 1234 1} result]} {
error "inch edarb extrusion failed: $result"
}
} else {
Z
e $prim
sed $prim
if {[catch {extrude 1234 1} result]} {
error "inch MGED extrusion failed: $result"
}
accept
Z
}
units mm
set new_x [lindex [db get $prim V5] 0]
if {abs($new_x - $base_x - $inch_in_mm) > $tolerance} {
error "inch $mode extrusion moved V5 by [expr {$new_x - $base_x}] mm"
}
}
puts "*** 'extrude' testing completed ***"
+23
View File
@@ -31,6 +31,7 @@ in tor_pe.s tor 0 0 0 0 0 1 20 2
prim_edit_check tor_pe.s "Set Radius 1" 30 r_a 30 -1
prim_edit_check tor_pe.s "Set Radius 2" 5 r_h 5 -1
prim_edit_mouse_check tor_pe.s "Set Radius 1" r_a 30 -1
prim_edit_mouse_check tor_pe.s "Set Radius 2" r_h 5 -1
kill tor_pe.s
puts "--- ELL (ellipsoid: A, B, C) ---"
@@ -39,6 +40,8 @@ prim_edit_check ell_pe.s "Set A" 8 A 8 0
prim_edit_check ell_pe.s "Set B" 6 B 6 1
prim_edit_check ell_pe.s "Set C" 4 C 4 2
prim_edit_mouse_check ell_pe.s "Set A" A 8 0
prim_edit_mouse_check ell_pe.s "Set B" B 6 1
prim_edit_mouse_check ell_pe.s "Set C" C 4 2
kill ell_pe.s
puts "--- RPC (right parabolic cylinder: H, r) ---"
@@ -46,6 +49,7 @@ in rpc_pe.s rpc 0 0 0 0 0 10 0 5 0 3
prim_edit_check rpc_pe.s "Set H" 20 H 20 2
prim_edit_check rpc_pe.s "Set r" 6 r 6 -1
prim_edit_mouse_check rpc_pe.s "Set H" H 20 2
prim_edit_mouse_check rpc_pe.s "Set r" r 6 -1
kill rpc_pe.s
puts "--- RHC (right hyperbolic cylinder: H, r, c) ---"
@@ -54,6 +58,8 @@ prim_edit_check rhc_pe.s "Set H" 20 H 20 2
prim_edit_check rhc_pe.s "Set r" 6 r 6 -1
prim_edit_check rhc_pe.s "Set c" 3 c 3 -1
prim_edit_mouse_check rhc_pe.s "Set r" r 6 -1
prim_edit_mouse_check rhc_pe.s "Set H" H 20 2
prim_edit_mouse_check rhc_pe.s "Set c" c 3 -1
kill rhc_pe.s
puts "--- EPA (elliptical paraboloid: H, r_1, r_2) ---"
@@ -62,6 +68,8 @@ prim_edit_check epa_pe.s "Set H" 20 H 20 2
prim_edit_check epa_pe.s "Set A" 8 r_1 8 -1
prim_edit_check epa_pe.s "Set B" 4 r_2 4 -1
prim_edit_mouse_check epa_pe.s "Set H" H 20 2
prim_edit_mouse_check epa_pe.s "Set A" r_1 8 -1
prim_edit_mouse_check epa_pe.s "Set B" r_2 4 -1
kill epa_pe.s
puts "--- EHY (elliptical hyperboloid: H, r_1, c) ---"
@@ -70,6 +78,8 @@ prim_edit_check ehy_pe.s "Set H" 20 H 20 2
prim_edit_check ehy_pe.s "Set A" 8 r_1 8 -1
prim_edit_check ehy_pe.s "Set c" 3 c 3 -1
prim_edit_mouse_check ehy_pe.s "Set A" r_1 8 -1
prim_edit_mouse_check ehy_pe.s "Set H" H 20 2
prim_edit_mouse_check ehy_pe.s "Set c" c 3 -1
kill ehy_pe.s
puts "--- PART (particle: H, r_v, r_h) ---"
@@ -78,6 +88,8 @@ prim_edit_check part_pe.s "Set H" 20 H 20 2
prim_edit_check part_pe.s "Set v" 4 r_v 4 -1
prim_edit_check part_pe.s "Set h" 3 r_h 3 -1
prim_edit_mouse_check part_pe.s "Set v" r_v 4 -1
prim_edit_mouse_check part_pe.s "Set H" H 20 2
prim_edit_mouse_check part_pe.s "Set h" r_h 3 -1
kill part_pe.s
puts "--- TGC (truncated general cone: H, A, B, C, D) ---"
@@ -91,6 +103,10 @@ prim_edit_check tgc_pe.s "Set B" 3 B 3 1
prim_edit_check tgc_pe.s "Set C" 3 C 3 0
prim_edit_check tgc_pe.s "Set D" 4 D 4 1
prim_edit_mouse_check tgc_pe.s "Set H" H 8 2
prim_edit_mouse_check tgc_pe.s "Set A" A 4 0
prim_edit_mouse_check tgc_pe.s "Set B" B 3 1
prim_edit_mouse_check tgc_pe.s "Set C" C 3 0
prim_edit_mouse_check tgc_pe.s "Set D" D 4 1
kill tgc_pe.s
puts "--- ETO (elliptical torus: r, C, D) ---"
@@ -100,6 +116,7 @@ prim_edit_check eto_pe.s "Set r" 8 r 8 -1
prim_edit_check eto_pe.s "Set D" 1.5 r_d 1.5 -1
prim_edit_mouse_check eto_pe.s "Set r" r 8 -1
prim_edit_mouse_check eto_pe.s "Set C" C 5 0
prim_edit_mouse_check eto_pe.s "Set D" r_d 1.5 -1
kill eto_pe.s
puts "--- HYP (one-sheet hyperboloid: H, A, B, c) ---"
@@ -111,6 +128,9 @@ prim_edit_check hyp_pe.s "Set A" 2 A 10 1
prim_edit_check hyp_pe.s "Set B" 2 b 4 -1
prim_edit_check hyp_pe.s "Set c" 0.5 bnr 0.25 -1
prim_edit_mouse_check hyp_pe.s "Set H" H 20 2
prim_edit_mouse_check hyp_pe.s "Set A" A 10 1
prim_edit_mouse_check hyp_pe.s "Set B" b 4 -1
prim_edit_mouse_check hyp_pe.s "Set c" bnr 0.25 -1
kill hyp_pe.s
puts "--- SUPERELL (superellipsoid: A, B, C) ---"
@@ -119,6 +139,8 @@ prim_edit_check superell_pe.s "Set A" 5 A 5 0
prim_edit_check superell_pe.s "Set B" 4 B 4 1
prim_edit_check superell_pe.s "Set C" 3 C 3 2
prim_edit_mouse_check superell_pe.s "Set A" A 5 0
prim_edit_mouse_check superell_pe.s "Set B" B 4 1
prim_edit_mouse_check superell_pe.s "Set C" C 3 2
kill superell_pe.s
puts "--- CLINE (cylinder line: H, R, plate thickness) ---"
@@ -129,6 +151,7 @@ prim_edit_check cline_pe.s "Set R" 4 R 4 -1
prim_edit_check cline_pe.s "Set plate thickness" 0.4 T 0.4 -1
prim_edit_mouse_check cline_pe.s "Set H" H 8 2
prim_edit_mouse_check cline_pe.s "Set R" R 4 -1
prim_edit_mouse_check cline_pe.s "Set plate thickness" T 0.4 -1
kill cline_pe.s
puts "--- NMG (non-manifold geometry: Pick Edge, Move Edge, Next EU) ---"
+7 -1
View File
@@ -178,7 +178,13 @@ DEPREC=0
workers=0
pids=""
dir="`dirname $RT`"
for cmd in $dir/* ; do
suffix=
case "$RT" in
*.exe) suffix=.exe ;;
*) test -f "$RT.exe" && suffix=.exe ;;
esac
for cmd in "$dir"/*"$suffix" ; do
test -f "$cmd" || continue
test_usage "$cmd" &
pids="$pids $!"
+4 -2
View File
@@ -93,6 +93,7 @@ if(BRLCAD_X11_PROVIDER_RESOLVED STREQUAL "XMIN" AND
"PLOT3_ASC_BIN=$<TARGET_FILE:plot3-asc>"
)
mged_add_xmin_gui_fixture(help "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(edit-interaction "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(bot "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(editors "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(query-pattern "mged;dm-tkswrast;m35.g")
@@ -212,18 +213,19 @@ cmakefiles(
mged/mged_xmin_gui.sh
mged/xmin_gui_bot.tcl
mged/xmin_gui_controls.tcl
mged/xmin_gui_edit.tcl
mged/xmin_gui_edit_interaction.tcl
mged/xmin_gui_editors.tcl
mged/xmin_gui_help.tcl
mged/xmin_gui_lod.tcl
mged/xmin_gui_overlaps.tcl
mged/xmin_gui_pipe_oracle.tcl
mged/xmin_gui_query_pattern.tcl
mged/xmin_gui_render_outputs.tcl
mged/xmin_gui_search_exec.tcl
mged/xmin_gui_shotvis.tcl
mged/xmin_gui_test.tcl
mged/xmin_gui_workflows.tcl
mged/xmin_gui_edit.tcl
mged/xmin_gui_pipe_oracle.tcl
)
# Local Variables:
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -53,7 +53,7 @@ set(
)
brlcad_addlib(librender "${LIBRENDER_SRCS}" "" "${ADRT_LOCAL_INCLUDE_DIRS}"
PRIVATE_LIBS libgcv librt ZLIB::ZLIB ${M_LIBRARY}
PRIVATE_LIBS libgcv ZLIB::ZLIB ${M_LIBRARY}
)
set_target_properties(librender PROPERTIES VERSION 20.0.1 SOVERSION 20)
add_dependencies(librender gcv_plugins)
@@ -109,9 +109,8 @@ cmakefiles(isst.c)
add_subdirectory(master)
add_subdirectory(slave)
add_subdirectory(tests)
set(adrt_ignore_files load.h load_g.h tienet.h)
set(adrt_ignore_files load.h tienet.h)
cmakefiles(${adrt_ignore_files})
cmakefiles(CMakeLists.txt)
-8
View File
@@ -90,19 +90,11 @@ enum
#define ADRT_NAME_SIZE 256
/* TIE geometry is normalized from millimeters to meters while loading. */
#define ADRT_MODEL_UNITS_PER_TIE_UNIT 1000.0
#define ADRT_LOAD_FORMAT_G 42
#define ADRT_LOAD_FORMAT_REG 43
#define ADRT_LOAD_FORMAT_KDTREE 44
struct rt_annot_scene;
RENDER_EXPORT extern int load_g(struct tie_s *tie, const char *db, int argc, const char **argv, struct adrt_mesh_s **);
RENDER_EXPORT extern int load_g_annotations(struct tie_s *tie, const char *db,
int argc, const char **argv, struct adrt_mesh_s **,
struct rt_annot_scene **annotations);
#endif
+38 -23
View File
@@ -59,7 +59,6 @@ struct isst_s {
struct render_camera_s camera;
struct camera_tile_s tile;
struct adrt_mesh_s *meshes;
struct rt_annot_scene *annotations;
tienet_buffer_t buffer_image;
int ogl, sflags, w, h, gs, ui;
double dt, fps, uic;
@@ -68,7 +67,6 @@ struct isst_s {
void *texdata;
vect_t camera_pos_init;
vect_t camera_focus_init;
fastf_t scene_radius;
int64_t t1;
int64_t t2;
int dirty;
@@ -121,7 +119,13 @@ resize_isst(struct isst_s *isstp)
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
isstp->texdata = realloc(isstp->texdata, isstp->camera.w * isstp->camera.h * 3);
{
/* realloc through a temporary so a failure does not leak (and null out)
* the existing buffer. */
void *tmp = realloc(isstp->texdata, isstp->camera.w * isstp->camera.h * 3);
if (tmp)
isstp->texdata = tmp;
}
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, isstp->camera.w, isstp->camera.h, 0, GL_RGB, GL_UNSIGNED_BYTE, isstp->texdata);
glDisable(GL_LIGHTING);
@@ -154,14 +158,11 @@ isst_load_g(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc,
argv = (char **)malloc(sizeof(char *) * (strlen(Tcl_GetString(objv[3])) + 1)); /* allocate way too much. */
argc = (int)bu_argv_from_string(argv, strlen(Tcl_GetString(objv[3])), Tcl_GetString(objv[3]));
rt_annot_scene_destroy(isst->annotations);
isst->annotations = NULL;
load_g_annotations(isst->tie, Tcl_GetString(objv[2]), argc,
(const char **)argv, &(isst->meshes), &isst->annotations);
load_g(isst->tie, Tcl_GetString(objv[2]), argc, (const char **)argv, &(isst->meshes));
free(argv);
isst->scene_radius = render_camera_fit_scene(&isst->camera, isst->tie,
isst->annotations, ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSETALL(isst->camera.pos, isst->tie->radius);
VMOVE(isst->camera.focus, isst->tie->mid);
VMOVE(isst->camera_pos_init, isst->camera.pos);
VMOVE(isst->camera_focus_init, isst->camera.focus);
@@ -238,9 +239,7 @@ paint_window(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Ob
isst->buffer_image.ind = 0;
render_camera_prep(&isst->camera);
render_camera_render_annotations(&isst->camera, isst->tie, &isst->tile,
&isst->buffer_image, isst->annotations,
ADRT_MODEL_UNITS_PER_TIE_UNIT);
render_camera_render(&isst->camera, isst->tie, &isst->tile, &isst->buffer_image);
isst->t1 = bu_gettime();
@@ -283,6 +282,7 @@ set_resolution(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_
}
CLAMP(resolution, 1, 20);
/* gs is the render grid size; 0 means render at full window resolution */
if (resolution == 20)
isst->gs = 0;
else
@@ -326,10 +326,13 @@ isst_zap(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *c
return TCL_ERROR;
}
/* Release the buffers owned directly by the workspace before the struct
* itself. (The tie and mesh list have a more involved lifecycle and are
* left for a dedicated teardown.) */
TIENET_BUFFER_FREE(isst->buffer_image);
if (isst->texdata)
free(isst->texdata);
rt_annot_scene_destroy(isst->annotations);
bu_free(isst, "isst free");
isst = NULL;
@@ -351,7 +354,7 @@ render_mode(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj
if (objc == 4)
buf = Tcl_GetString(objv[3]);
/* pack the 'rest' into buf - probably should use a vls for this*/
/* pack the 'rest' into buf - probably should use a vls for this */
if ( strlen(mode) == 3 && bu_strncmp("cut", mode, 3) == 0 ) {
struct adrt_mesh_s *mesh;
@@ -388,22 +391,26 @@ zero_view(ClientData UNUSED(clientData), Tcl_Interp *UNUSED(interp), int UNUSED(
static int
move_walk(ClientData UNUSED(clientData), Tcl_Interp *interp, int UNUSED(objc), Tcl_Obj *const *objv)
move_walk(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
{
vect_t vec;
int flag;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 1, objv, "pathName flag");
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[2], &flag) != TCL_OK)
return TCL_ERROR;
if (flag >= 0) {
VSUB2(vec, isst->camera.focus, isst->camera.pos);
VSCALE(vec, vec, 0.1 * isst->scene_radius);
VSCALE(vec, vec, 0.1 * isst->tie->radius);
VADD2(isst->camera.pos, isst->camera.pos, vec);
VADD2(isst->camera.focus, isst->camera.focus, vec);
} else {
VSUB2(vec, isst->camera.pos, isst->camera.focus);
VSCALE(vec, vec, 0.1 * isst->scene_radius);
VSCALE(vec, vec, 0.1 * isst->tie->radius);
VADD2(isst->camera.pos, isst->camera.pos, vec);
VADD2(isst->camera.focus, isst->camera.focus, vec);
}
@@ -412,12 +419,16 @@ move_walk(ClientData UNUSED(clientData), Tcl_Interp *interp, int UNUSED(objc), T
}
static int
move_strafe(ClientData UNUSED(clientData), Tcl_Interp *interp, int UNUSED(objc), Tcl_Obj *const *objv)
move_strafe(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *const *objv)
{
vect_t vec, dir, up;
int flag;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 1, objv, "pathName flag");
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[2], &flag) != TCL_OK)
return TCL_ERROR;
@@ -426,13 +437,13 @@ move_strafe(ClientData UNUSED(clientData), Tcl_Interp *interp, int UNUSED(objc),
if (flag >= 0) {
VSUB2(dir, isst->camera.focus, isst->camera.pos);
VCROSS(vec, dir, up);
VSCALE(vec, vec, 0.1 * isst->scene_radius);
VSCALE(vec, vec, 0.1 * isst->tie->radius);
VADD2(isst->camera.pos, isst->camera.pos, vec);
VADD2(isst->camera.focus, isst->camera.pos, dir);
} else {
VSUB2(dir, isst->camera.focus, isst->camera.pos);
VCROSS(vec, dir, up);
VSCALE(vec, vec, -0.1 * isst->scene_radius);
VSCALE(vec, vec, -0.1 * isst->tie->radius);
VADD2(isst->camera.pos, isst->camera.pos, vec);
VADD2(isst->camera.focus, isst->camera.pos, dir);
}
@@ -540,10 +551,14 @@ aerotate(ClientData UNUSED(clientData), Tcl_Interp *interp, int objc, Tcl_Obj *c
VSCALE(vecdfoc, vecdfoc, mag_focus);
VADD2(isst->camera.focus, isst->camera_focus_init, vecdfoc);
}
/* Update the tcl copies of the az/el vars */
/* Update the tcl copies of the az/el vars. Store them in the same units
* as isst_load_g (radians via -DEG2RAD); previously aerotate wrote the raw
* degrees from bn_ae_vec, leaving the Tcl vars inconsistent after a drag. */
VSUB2(vec, isst->camera.focus, isst->camera.pos);
VUNITIZE(vec);
bn_ae_vec(&az, &el, vec);
az = az * -DEG2RAD;
el = el * -DEG2RAD;
bu_vls_sprintf(&tclstr, "%f", az);
Tcl_SetVar(interp, "az", bu_vls_addr(&tclstr), 0);
bu_vls_sprintf(&tclstr, "%f", el);
@@ -561,7 +576,7 @@ open_dm(ClientData UNUSED(cdata), Tcl_Interp *interp, int UNUSED(objc), Tcl_Obj
dmp = dm_open(NULL, (void *)interp, dm_default_type(), sizeof(av)/sizeof(void*)-1, (const char **)av);
if (dmp == DM_NULL) {
printf("dm failed?\n");
printf("Failed to open display manager\n");
return TCL_ERROR;
}
@@ -639,7 +654,7 @@ const char *fullname;
argv = __argv;
#endif
/* initialize progname for run-tim resource finding */
/* initialize progname for run-time resource finding */
bu_setprogname(argv[0]);
#ifdef HAVE_WINDOWS_H
+21 -225
View File
@@ -29,7 +29,6 @@
#include "bu/dylib.h"
#include "bu/log.h"
#include "bu/str.h"
#include "rt/primitives/annot.h"
#include "./camera.h"
@@ -44,27 +43,13 @@ struct render_shader_s {
static struct render_shader_s *shaders = NULL;
struct render_camera_annotation_data {
const struct rt_annot_scene *scene;
fastf_t model_units_per_tie_unit;
struct rt_annot_view view;
};
struct render_camera_parallel_data {
render_camera_thread_data_t thread;
struct render_camera_annotation_data annotation;
};
static const fastf_t render_camera_default_scene_radius = 1.0;
/* number of leading characters compared when matching a shader by name */
#define RENDER_SHADER_NAME_CMP_LEN 8
void render_camera_render_thread(int cpu, void *ptr); /* for bu_parallel */
static void render_camera_prep_ortho(render_camera_t *camera);
static void render_camera_prep_persp(render_camera_t *camera);
static void render_camera_prep_persp_dof(render_camera_t *camera);
static void render_camera_annotation_view(const render_camera_t *camera,
fastf_t model_units_per_tie_unit, struct rt_annot_view *view);
static struct render_shader_s *render_shader_register (const char *name, int (*init)(render_t *, const char *));
@@ -408,147 +393,20 @@ render_camera_prep(render_camera_t *camera)
}
fastf_t
render_camera_fit_scene(render_camera_t *camera, const struct tie_s *tie,
const struct rt_annot_scene *annotations,
fastf_t model_units_per_tie_unit)
{
point_t annotation_min, annotation_max;
point_t scene_min, scene_max;
vect_t offset;
fastf_t scene_radius;
if (!camera || !tie)
return 0.0;
if (!annotations || !rt_annot_scene_bounds(annotations,
annotation_min, annotation_max)) {
VSETALL(camera->pos, tie->radius);
VMOVE(camera->focus, tie->mid);
return tie->radius;
}
if (!isfinite(model_units_per_tie_unit) ||
model_units_per_tie_unit <= 0.0)
model_units_per_tie_unit = 1.0;
VSCALE(annotation_min, annotation_min,
1.0 / model_units_per_tie_unit);
VSCALE(annotation_max, annotation_max,
1.0 / model_units_per_tie_unit);
VMOVE(scene_min, annotation_min);
VMOVE(scene_max, annotation_max);
if (tie->tri_num) {
VMINMAX(scene_min, scene_max, tie->amin);
VMINMAX(scene_min, scene_max, tie->amax);
}
VADD2SCALE(camera->focus, scene_min, scene_max, 0.5);
VSUB2(offset, scene_max, scene_min);
scene_radius = 0.5 * MAGNITUDE(offset);
if (!isfinite(scene_radius) || scene_radius <= SMALL_FASTF)
scene_radius = render_camera_default_scene_radius;
/* Keep the established isometric initial direction while positioning it
* relative to the combined scene center. */
VSETALL(offset, scene_radius);
VADD2(camera->pos, camera->focus, offset);
return scene_radius;
}
static void
render_camera_annotation_view(const render_camera_t *camera,
fastf_t model_units_per_tie_unit, struct rt_annot_view *view)
{
vect_t look, up, side, temp, right;
point_t model_position;
fastf_t angle, sine, cosine, xy_scale = 1.0;
mat_t *matrix = &view->model2view;
VSUB2(look, camera->focus, camera->pos);
VUNITIZE(look);
VSET(up, 0.0, 0.0, 1.0);
VMOVE(temp, look);
angle = VDOT(up, temp);
VSCALE(temp, temp, angle);
VSUB2(up, up, temp);
VUNITIZE(up);
VCROSS(side, up, look);
sine = sin(-camera->tilt * DEG2RAD);
cosine = cos(-camera->tilt * DEG2RAD);
VSCALE(up, up, cosine);
VSCALE(side, side, sine);
VADD2(up, up, side);
VCROSS(side, up, look);
VREVERSE(right, side);
MAT_IDN(*matrix);
if (camera->type == RENDER_CAMERA_ORTHOGRAPHIC &&
camera->aspect > 0.0 && camera->gridsize > 0.0) {
xy_scale = 2.0 / (camera->aspect * camera->gridsize *
model_units_per_tie_unit);
} else if (camera->aspect > 0.0) {
xy_scale = 1.0 / camera->aspect;
}
for (size_t axis = 0; axis < 3; ++axis) {
(*matrix)[axis] = right[axis] * xy_scale;
(*matrix)[4 + axis] = up[axis] * xy_scale;
(*matrix)[8 + axis] = -look[axis];
}
VSCALE(model_position, camera->pos, model_units_per_tie_unit);
(*matrix)[3] = -VDOT(&(*matrix)[0], model_position);
(*matrix)[7] = -VDOT(&(*matrix)[4], model_position);
(*matrix)[11] = -VDOT(&(*matrix)[8], model_position);
view->width = camera->w;
view->height = camera->h;
view->perspective =
camera->type == RENDER_CAMERA_PERSPECTIVE ? 2.0 * camera->fov : 0.0;
}
static void
render_camera_composite_annotation(render_camera_thread_data_t *td,
const struct render_camera_annotation_data *annotation,
const struct tie_ray_s *tie_ray, fastf_t sample_x, fastf_t sample_y,
vect_t *pixel)
{
struct rt_annot_hit back_layer = {0};
struct xray ray;
fastf_t scene_distance = INFINITY;
if (!annotation || !annotation->scene)
return;
VSCALE(ray.r_pt, tie_ray->pos, annotation->model_units_per_tie_unit);
VMOVE(ray.r_dir, tie_ray->dir);
if (!rt_annot_scene_query_layers(annotation->scene, &annotation->view,
&ray, sample_x, sample_y, INFINITY, &back_layer, 1))
return;
if (!back_layer.screen_space) {
struct tie_id_s id;
struct tie_ray_s depth_ray = *tie_ray;
if (TIE_WORK(td->tie, &depth_ray, &id, render_hit, NULL))
scene_distance = id.dist * annotation->model_units_per_tie_unit;
}
(void)rt_annot_scene_composite(annotation->scene, &annotation->view,
&ray, sample_x, sample_y, scene_distance, *pixel, NULL);
}
static void
render_camera_render_thread_impl(int UNUSED(cpu), render_camera_thread_data_t *td,
const struct render_camera_annotation_data *annotation)
void
render_camera_render_thread(int UNUSED(cpu), void *ptr)
{
render_camera_thread_data_t *td;
int d, n, res_ind, scanline, v_scanline;
vect_t pixel, accum;
vect_t v1 = VINIT_ZERO;
vect_t v2 = VINIT_ZERO;
struct tie_ray_s ray;
struct tie_ray_s annotation_ray;
fastf_t view_inv;
VSETALL(v1, 0);
td = (render_camera_thread_data_t *)ptr;
view_inv = 1.0 / td->camera->view_num;
td->camera->render.tie = td->tie;
@@ -599,15 +457,9 @@ render_camera_render_thread_impl(int UNUSED(cpu), render_camera_thread_data_t *t
VMOVE(ray.pos, td->camera->view_list[d].pos);
ray.depth = 0;
VUNITIZE(ray.dir);
annotation_ray = ray;
/* Compute pixel value using this ray */
td->camera->render.work(&td->camera->render, td->tie, &ray, &pixel);
render_camera_composite_annotation(td, annotation,
&annotation_ray,
(fastf_t)n + 0.5,
(fastf_t)td->camera->h - (fastf_t)v_scanline - 0.5,
&pixel);
VADD2(accum, accum, pixel);
}
@@ -624,7 +476,6 @@ render_camera_render_thread_impl(int UNUSED(cpu), render_camera_thread_data_t *t
VMOVE(ray.pos, td->camera->view_list[0].pos);
ray.depth = 0;
VUNITIZE(ray.dir);
annotation_ray = ray;
/* Compute pixel value using this ray */
td->camera->render.work(&td->camera->render, td->tie, &ray, &pixel);
@@ -639,19 +490,12 @@ render_camera_render_thread_impl(int UNUSED(cpu), render_camera_thread_data_t *t
VSET(pixel, (TFLOAT)RENDER_CAMERA_BGR, (TFLOAT)RENDER_CAMERA_BGG, (TFLOAT)RENDER_CAMERA_BGB);
ray.depth = 0;
annotation_ray = ray;
/* Compute pixel value using this ray */
td->camera->render.work(&td->camera->render, td->tie, &ray, &pixel);
}
}
if (td->camera->view_num == 1)
render_camera_composite_annotation(td, annotation,
&annotation_ray, (fastf_t)n + 0.5,
(fastf_t)td->camera->h - (fastf_t)v_scanline - 0.5,
&pixel);
if (td->tile->format == RENDER_CAMERA_BIT_DEPTH_24) {
V_MIN(pixel[0], 1);
@@ -679,30 +523,9 @@ render_camera_render_thread_impl(int UNUSED(cpu), render_camera_thread_data_t *t
void
render_camera_render_thread(int cpu, void *ptr)
render_camera_render(render_camera_t *camera, struct tie_s *tie, camera_tile_t *tile, tienet_buffer_t *result)
{
render_camera_render_thread_impl(cpu, (render_camera_thread_data_t *)ptr,
NULL);
}
static void
render_camera_render_annotations_thread(int cpu, void *ptr)
{
struct render_camera_parallel_data *data =
(struct render_camera_parallel_data *)ptr;
render_camera_render_thread_impl(cpu, &data->thread, &data->annotation);
}
static void
render_camera_render_internal(render_camera_t *camera, struct tie_s *tie,
camera_tile_t *tile, tienet_buffer_t *result,
const struct rt_annot_scene *annotations,
fastf_t model_units_per_tie_unit)
{
struct render_camera_parallel_data data = {0};
render_camera_thread_data_t td;
unsigned int scanline;
uint32_t ind;
@@ -720,47 +543,19 @@ render_camera_render_internal(render_camera_t *camera, struct tie_s *tie,
TCOPY(camera_tile_t, tile, 0, result->data, result->ind);
result->ind += sizeof(camera_tile_t);
data.thread.tie = tie;
data.thread.camera = camera;
data.thread.tile = tile;
data.thread.res_buf = &((char *)result->data)[result->ind];
td.tie = tie;
td.camera = camera;
td.tile = tile;
td.res_buf = &((char *)result->data)[result->ind];
scanline = 0;
data.thread.scanline = &scanline;
data.thread.sem_tie_worker = bu_semaphore_register("sem_tie_worker");
data.annotation.scene = annotations;
data.annotation.model_units_per_tie_unit =
model_units_per_tie_unit > 0.0 ? model_units_per_tie_unit : 1.0;
if (annotations)
render_camera_annotation_view(camera,
data.annotation.model_units_per_tie_unit, &data.annotation.view);
td.scanline = &scanline;
td.sem_tie_worker = bu_semaphore_register("sem_tie_worker");
if (annotations)
bu_parallel(render_camera_render_annotations_thread,
camera->thread_num, &data);
else
bu_parallel(render_camera_render_thread, camera->thread_num,
&data.thread);
bu_parallel(render_camera_render_thread, camera->thread_num, &td);
result->ind = ind;
}
void
render_camera_render(render_camera_t *camera, struct tie_s *tie,
camera_tile_t *tile, tienet_buffer_t *result)
{
render_camera_render_internal(camera, tie, tile, result, NULL, 1.0);
}
void
render_camera_render_annotations(render_camera_t *camera, struct tie_s *tie,
camera_tile_t *tile, tienet_buffer_t *result,
const struct rt_annot_scene *annotations,
fastf_t model_units_per_tie_unit)
{
render_camera_render_internal(camera, tie, tile, result, annotations,
model_units_per_tie_unit);
return;
}
@@ -825,9 +620,9 @@ render_shader_unload_plugin(render_t *r, const char *name)
{
#ifdef HAVE_DLFCN_H
struct render_shader_s *t, *s = shaders, *meh;
if (!bu_strncmp(s->name, name, 8)) {
if (!bu_strncmp(s->name, name, RENDER_SHADER_NAME_CMP_LEN)) {
t = s->next;
if (r && r->shader && !bu_strncmp(r->shader, name, 8)) {
if (r && r->shader && !bu_strncmp(r->shader, name, RENDER_SHADER_NAME_CMP_LEN)) {
meh = s->next;
while (meh) {
if (render_shader_init(r, meh->name, NULL) != -1)
@@ -846,7 +641,7 @@ LOADED:
}
while (s->next) {
if (!bu_strncmp(s->next->name, name, 8)) {
if (!bu_strncmp(s->next->name, name, RENDER_SHADER_NAME_CMP_LEN)) {
if (r)
render_shader_init(r, s->name, NULL);
if (s->next->dlh)
@@ -856,6 +651,7 @@ LOADED:
bu_free(t, "unload shader");
return 0;
}
s = s->next;
}
bu_log("Could not find shader \"%s\"\n", name);
@@ -871,7 +667,7 @@ render_shader_init(render_t *r, const char *name, const char *buf)
{
struct render_shader_s *s = shaders;
while (s) {
if (!bu_strncmp(s->name, name, 8)) {
if (!bu_strncmp(s->name, name, RENDER_SHADER_NAME_CMP_LEN)) {
s->init(r, buf);
r->shader = s->name;
return 0;
-11
View File
@@ -99,18 +99,7 @@ __BEGIN_DECLS
RENDER_EXPORT extern void render_camera_init(render_camera_t *camera, size_t threads);
RENDER_EXPORT extern void render_camera_free(render_camera_t *camera);
RENDER_EXPORT extern void render_camera_prep(render_camera_t *camera);
/** Center a camera on TIE geometry and any prepared annotations. Returns the
* effective scene radius in TIE coordinates. */
RENDER_EXPORT extern fastf_t render_camera_fit_scene(render_camera_t *camera,
const struct tie_s *tie, const struct rt_annot_scene *annotations,
fastf_t model_units_per_tie_unit);
RENDER_EXPORT extern void render_camera_render(render_camera_t *camera, struct tie_s *tie, camera_tile_t *tile, tienet_buffer_t *result);
/** Render with an externally owned annotation scene. The coordinate scale
* converts TIE coordinates to database model coordinates. */
RENDER_EXPORT extern void render_camera_render_annotations(render_camera_t *camera,
struct tie_s *tie, camera_tile_t *tile, tienet_buffer_t *result,
const struct rt_annot_scene *annotations,
fastf_t model_units_per_tie_unit);
RENDER_EXPORT extern int render_shader_init(render_t *, const char *name, const char *buf);
RENDER_EXPORT extern const char *render_shader_load_plugin(const char *filename);
+10 -86
View File
@@ -40,13 +40,11 @@
#include "vmath.h"
#include "nmg.h"
#include "rt/geom.h"
#include "rt/search.h"
#include "raytrace.h"
#include "rt/tie.h"
#include "adrt.h"
#include "adrt_struct.h"
#include "load_g.h"
static struct bn_tol tol; /* calculation tolerance */
@@ -106,42 +104,6 @@ struct gcv_data {
static struct gcv_data gcvwriter = {{load_nmg_to_adrt_gcvwrite, NULL, NULL}, NULL};
int
adrt_path_has_geometry(struct db_i *database, const char *path)
{
static const char *geometry_search = "! -type comb ! -type annot";
struct directory *root;
if (!database || !path)
return 0;
root = db_lookup(database, path, LOOKUP_QUIET);
/* Let db_walk_tree report a missing path rather than hiding the error. */
if (!root)
return 1;
return db_search(NULL, DB_SEARCH_QUIET, geometry_search, 1, &root,
database, NULL, NULL, NULL) != 0;
}
union tree *
adrt_leaf_tess(struct db_tree_state *tsp, const struct db_full_path *pathp,
struct rt_db_internal *ip, void *client_data)
{
union tree *result;
if (!ip || ip->idb_type != ID_ANNOT)
return rt_booltree_leaf_tess(tsp, pathp, ip, client_data);
/* Annotations are rendered directly and have no NMG representation. */
BU_GET(result, union tree);
RT_TREE_INIT(result);
result->tr_op = OP_NOP;
return result;
}
/* load the region into the tie image */
static void
load_nmg_to_adrt_internal(struct adrt_mesh_s *mesh, struct nmgregion *r)
@@ -191,8 +153,7 @@ load_nmg_to_adrt_internal(struct adrt_mesh_s *mesh, struct nmgregion *r)
NMG_CK_VERTEX(v);
/* convert mm to m */
VSCALE((*tribuf[vert_count]).v, v->vg_p->coord,
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*tribuf[vert_count]).v, v->vg_p->coord, 1.0/1000.0);
vert_count++;
}
if (vert_count > 3)
@@ -287,12 +248,9 @@ load_nmg_to_adrt_regstart(struct db_tree_state *ts, const struct db_full_path *p
for (i=0;i<bot->num_faces;i++)
{
VSCALE((*tribuf[0]).v, (bot->vertices+3*bot->faces[3*i+0]),
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*tribuf[1]).v, (bot->vertices+3*bot->faces[3*i+1]),
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*tribuf[2]).v, (bot->vertices+3*bot->faces[3*i+2]),
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*tribuf[0]).v, (bot->vertices+3*bot->faces[3*i+0]), 1.0/1000.0);
VSCALE((*tribuf[1]).v, (bot->vertices+3*bot->faces[3*i+1]), 1.0/1000.0);
VSCALE((*tribuf[2]).v, (bot->vertices+3*bot->faces[3*i+2]), 1.0/1000.0);
TIE_VAL(tie_push)(cur_tie, tribuf, 1, mesh, 0);
}
@@ -340,16 +298,11 @@ load_nmg_to_adrt_gcvwrite(struct nmgregion *r, const struct db_full_path *pathp,
int
load_g_annotations(struct tie_s *tie, const char *db, int argc,
const char **argv, struct adrt_mesh_s **meshes,
struct rt_annot_scene **annotations)
load_g(struct tie_s *tie, const char *db, int argc, const char **argv, struct adrt_mesh_s **meshes)
{
struct model *the_model;
struct bg_tess_tol ttol; /* tessellation tolerance in mm */
struct db_tree_state tree_state; /* includes tol & model */
const char **geometry_paths = NULL;
int geometry_count = 0;
int walk_result = 0;
cur_tie = tie; /* NOTE: cur_tie is a file-scope global; this is not
* reentrant and would need locking to be thread-safe. */
@@ -385,21 +338,13 @@ load_g_annotations(struct tie_s *tie, const char *db, int argc,
if ((dbip = db_open(db, DB_OPEN_READONLY)) == DBI_NULL) {
perror(db);
bu_log("Unable to open geometry database file (%s)\n", db);
nmg_km(the_model);
return -1;
}
if (db_dirbuild(dbip)) {
bu_log("ERROR: db_dirbuild failed\n");
db_close(dbip);
dbip = DBI_NULL;
nmg_km(the_model);
return -1;
}
if (annotations) {
*annotations = rt_annot_scene_create(dbip, argc, argv, &ttol, &tol);
}
BN_CK_TOL(tree_state.ts_tol);
BG_CK_TESS_TOL(tree_state.ts_ttol);
@@ -417,26 +362,15 @@ load_g_annotations(struct tie_s *tie, const char *db, int argc,
tribuf[1] = (TIE_3 *)bu_malloc(sizeof(TIE_3) * 3, "triangle tribuffer");
tribuf[2] = (TIE_3 *)bu_malloc(sizeof(TIE_3) * 3, "triangle tribuffer");
if (argc > 0) {
geometry_paths = (const char **)bu_calloc((size_t)argc,
sizeof(const char *), "ADRT geometry paths");
for (int i = 0; i < argc; ++i) {
if (adrt_path_has_geometry(dbip, argv[i]))
geometry_paths[geometry_count++] = argv[i];
}
}
if (geometry_count) {
walk_result = db_walk_tree(dbip,
geometry_count, /* number of toplevel regions */
geometry_paths, /* region names */
(void) db_walk_tree(dbip,
argc, /* number of toplevel regions */
argv, /* region names */
1, /* ncpu */
&tree_state, /* initial tree state */
load_nmg_to_adrt_regstart, /* region start function */
gcv_region_end, /* region end function */
adrt_leaf_tess, /* leaf func */
rt_booltree_leaf_tess, /* leaf func */
(void *)&gcvwriter); /* client data */
}
/* Release dynamic storage */
nmg_km(the_model);
@@ -446,20 +380,10 @@ load_g_annotations(struct tie_s *tie, const char *db, int argc,
bu_free(tribuf[1], "vert");
bu_free(tribuf[2], "vert");
bu_free(tribuf, "tri");
if (geometry_paths)
bu_free(geometry_paths, "ADRT geometry paths");
TIE_VAL(tie_prep)(cur_tie);
return walk_result;
}
int
load_g(struct tie_s *tie, const char *db, int argc, const char **argv,
struct adrt_mesh_s **meshes)
{
return load_g_annotations(tie, db, argc, argv, meshes, NULL);
return 0;
}
/*
-43
View File
@@ -1,43 +0,0 @@
/* L O A D _ G . H
* BRL-CAD / ADRT
*
* Copyright (c) 2026 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef ADRT_LOAD_G_H
#define ADRT_LOAD_G_H
#include "common.h"
#include "adrt.h"
struct db_i;
struct db_tree_state;
struct db_full_path;
struct rt_db_internal;
union tree;
__BEGIN_DECLS
RENDER_EXPORT extern int adrt_path_has_geometry(struct db_i *dbip,
const char *path);
RENDER_EXPORT extern union tree *adrt_leaf_tess(struct db_tree_state *tsp,
const struct db_full_path *pathp, struct rt_db_internal *ip,
void *client_data);
__END_DECLS
#endif /* ADRT_LOAD_G_H */
/*
* Local Variables:
* tab-width: 8
* mode: C
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8
*/
-16
View File
@@ -1,16 +0,0 @@
brlcad_addexec(adrt_annotation_test annotation.c
"librender;libwdb;librt;libbu;${M_LIBRARY}" TEST)
target_include_directories(adrt_annotation_test PRIVATE
${BRLCAD_SOURCE_DIR}/src/adrt
${BRLCAD_SOURCE_DIR}/src/adrt/librender
)
brlcad_add_test(NAME adrt_annotation_test COMMAND adrt_annotation_test)
cmakefiles(annotation.c CMakeLists.txt)
# Local Variables:
# tab-width: 8
# mode: cmake
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
-179
View File
@@ -1,179 +0,0 @@
/* A N N O T A T I O N . C
* BRL-CAD
*
* Copyright (c) 2026 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "common.h"
#include <math.h>
#include <stdio.h>
#include "bu/app.h"
#include "bu/file.h"
#include "raytrace.h"
#include "rt/primitives/annot.h"
#include "wdb.h"
#include "load_g.h"
#include "librender/camera.h"
static void
write_test_database(struct db_i *dbip)
{
struct rt_wdb *wdbp = wdb_dbopen(dbip, RT_WDB_TYPE_DB_DISK);
struct rt_annot_internal annot = {0};
point2d_t vertices[2];
struct line_seg line = {0};
void *segments[1];
int reverse[1] = {0};
point_t center = VINIT_ZERO;
struct wmember members;
if (wdbp == RT_WDB_NULL)
bu_exit(1, "unable to open ADRT annotation test database\n");
annot.magic = RT_ANNOT_INTERNAL_MAGIC;
VSET(annot.V, 10.0, 0.0, 0.0);
annot.flags = RT_ANNOT_MODEL_SPACE;
VSET(annot.u_vec, 0.0, 1.0, 0.0);
VSET(annot.v_vec, 0.0, 0.0, 1.0);
annot.vert_count = 2;
annot.verts = vertices;
V2SET(vertices[0], -1.0, 0.0);
V2SET(vertices[1], 1.0, 0.0);
annot.ant.count = 1;
annot.ant.reverse = reverse;
annot.ant.segments = segments;
line.magic = CURVE_LSEG_MAGIC;
line.start = 0;
line.end = 1;
segments[0] = &line;
if (rt_annot_validate(&annot, NULL) ||
mk_annot(wdbp, "test.annot", &annot) < 0 ||
mk_sph(wdbp, "test.sph", center, 1.0) < 0)
bu_exit(1, "unable to write ADRT annotation test objects\n");
BU_LIST_INIT(&members.l);
if (!mk_addmember("test.annot", &members.l, NULL, WMOP_UNION) ||
mk_lcomb(wdbp, "annotation-only.c", &members, 0, NULL, NULL,
NULL, 0))
bu_exit(1, "unable to write annotation-only test combination\n");
BU_LIST_INIT(&members.l);
if (!mk_addmember("test.annot", &members.l, NULL, WMOP_UNION) ||
!mk_addmember("test.sph", &members.l, NULL, WMOP_UNION) ||
mk_lcomb(wdbp, "mixed.c", &members, 0, NULL, NULL, NULL, 0))
bu_exit(1, "unable to write mixed test combination\n");
}
static void
check_geometry_filter(struct db_i *dbip)
{
struct rt_db_internal intern;
union tree *leaf;
if (adrt_path_has_geometry(dbip, "test.annot") ||
adrt_path_has_geometry(dbip, "annotation-only.c") ||
!adrt_path_has_geometry(dbip, "test.sph") ||
!adrt_path_has_geometry(dbip, "mixed.c"))
bu_exit(1, "ADRT annotation geometry filtering failed\n");
RT_DB_INTERNAL_INIT(&intern);
intern.idb_type = ID_ANNOT;
leaf = adrt_leaf_tess(NULL, NULL, &intern, NULL);
if (!leaf || leaf->tr_op != OP_NOP)
bu_exit(1, "ADRT annotation leaf did not become a no-op\n");
db_free_tree(leaf);
}
static void
check_camera_fit(struct db_i *dbip)
{
const char *paths[] = {"test.annot"};
struct bg_tess_tol ttol = BG_TESS_TOL_INIT_TOL;
struct bn_tol tol = BN_TOL_INIT_TOL;
struct rt_annot_scene *scene = rt_annot_scene_create(dbip, 1, paths,
&ttol, &tol);
render_camera_t camera = {0};
struct tie_s tie = {0};
fastf_t radius;
const fastf_t combined_radius = 0.5 * sqrt(41.0);
if (!scene || rt_annot_scene_count(scene) != 1)
bu_exit(1, "unable to prepare ADRT annotation test scene\n");
tie.radius = 2.0;
VSET(tie.mid, 1.0, 2.0, 3.0);
radius = render_camera_fit_scene(&camera, &tie, NULL, 1.0);
if (!NEAR_EQUAL(radius, 2.0, BN_TOL_DIST) ||
!VNEAR_EQUAL(camera.focus, tie.mid, BN_TOL_DIST) ||
!NEAR_EQUAL(camera.pos[X], 2.0, BN_TOL_DIST) ||
!NEAR_EQUAL(camera.pos[Y], 2.0, BN_TOL_DIST) ||
!NEAR_EQUAL(camera.pos[Z], 2.0, BN_TOL_DIST))
bu_exit(1, "ADRT geometry-only camera framing changed\n");
radius = render_camera_fit_scene(&camera, &tie, scene, 10.0);
if (radius <= SMALL_FASTF ||
!NEAR_EQUAL(camera.focus[X], 1.0, BN_TOL_DIST) ||
!NEAR_ZERO(camera.focus[Y], BN_TOL_DIST) ||
!NEAR_ZERO(camera.focus[Z], BN_TOL_DIST) ||
!NEAR_EQUAL(camera.pos[X] - camera.focus[X], radius, BN_TOL_DIST) ||
!NEAR_EQUAL(camera.pos[Y] - camera.focus[Y], radius, BN_TOL_DIST) ||
!NEAR_EQUAL(camera.pos[Z] - camera.focus[Z], radius, BN_TOL_DIST))
bu_exit(1, "ADRT annotation-only camera framing failed: radius=%g "
"focus=(%g %g %g) pos=(%g %g %g)\n", radius,
V3ARGS(camera.focus), V3ARGS(camera.pos));
tie.tri_num = 1;
VSET(tie.amin, -2.0, -2.0, -2.0);
VSET(tie.amax, 0.0, 2.0, 2.0);
radius = render_camera_fit_scene(&camera, &tie, scene, 10.0);
if (!NEAR_EQUAL(radius, combined_radius, BN_TOL_DIST) ||
!NEAR_EQUAL(camera.focus[X], -0.5, BN_TOL_DIST) ||
!NEAR_ZERO(camera.focus[Y], BN_TOL_DIST) ||
!NEAR_ZERO(camera.focus[Z], BN_TOL_DIST))
bu_exit(1, "ADRT combined geometry and annotation framing failed\n");
rt_annot_scene_destroy(scene);
}
int
main(int argc, char **argv)
{
char path[MAXPATHLEN];
struct db_i *dbip;
bu_setprogname(argv[0]);
if (argc != 1)
return 1;
{
FILE *fp = bu_temp_file(path, MAXPATHLEN);
if (!fp)
bu_exit(1, "unable to create ADRT annotation test path\n");
fclose(fp);
}
dbip = db_create(path, 5);
if (dbip == DBI_NULL)
bu_exit(1, "unable to create ADRT annotation test database\n");
write_test_database(dbip);
check_geometry_filter(dbip);
check_camera_fit(dbip);
db_close(dbip);
bu_file_delete(path);
return 0;
}
/*
* Local Variables:
* tab-width: 8
* mode: C
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8
*/
+17 -4
View File
@@ -53,12 +53,23 @@ endif(BRLCAD_ENABLE_TCL)
# Archer plugins
set(
archer_utility_FILES
plugins/Utility/attrGroupsDisplayUtilityP.tcl
plugins/Utility/botUtilityP.tcl
plugins/Utility/lodUtilityP.tcl
plugins/Utility/README
plugins/Utility/attrGroupsDisplayUtilityP.tcl
plugins/Utility/attrGroupsDisplayUtilityP/AttrGroupsDisplayUtilityP.tcl
plugins/Utility/botUtilityP.tcl
plugins/Utility/botUtilityP/BotUtilityP.tcl
plugins/Utility/lodUtilityP.tcl
plugins/Utility/lodUtilityP/LODUtilityP.tcl
)
set(archer_wizard_FILES
plugins/Wizards/humanwizard.tcl
plugins/Wizards/humanwizard/HumanWizard.tcl
plugins/Wizards/tankwizard.tcl
plugins/Wizards/tankwizard/TankWizard.tcl
plugins/Wizards/tankwizard/images/tank.png
plugins/Wizards/tirewizard.tcl
plugins/Wizards/tirewizard/TireWizard.tcl
)
set(archer_wizard_FILES plugins/Wizards/humanwizard.tcl plugins/Wizards/tankwizard.tcl plugins/Wizards/tirewizard.tcl)
if(BRLCAD_ENABLE_TCL)
brlcad_adddata(archer_utility_FILES plugins/archer/Utility)
@@ -77,6 +88,8 @@ endif(BRLCAD_ENABLE_TCL)
set(
archer_ignore_files
CMakeLists.txt
plugins/Commands/README
plugins/Core/README
${archer_utility_FILES}
${archer_wizard_FILES}
${archer_txt}
+480 -399
View File
File diff suppressed because it is too large Load Diff
+4 -5
View File
@@ -64,17 +64,16 @@ ArtTileCallback::on_tile_end(const renderer::Frame* frame, const size_t tile_x,
{
foundation::Tile& t = frame->image().tile(tile_x, tile_y);
const foundation::Tile rgb(t, PixelFormatUInt8);
const foundation::CanvasProperties& properties = frame->image().properties();
// fb_write(fbp, tile_x, tile_y, rgb.get_storage(), rgb.get_size());
// printf("yay!\n");
// printf("%lu %lu \n", tile_x, tile_y);
// printf("%lu %lu \n", rgb.get_width(), rgb.get_height());
for (size_t y = 0; y < rgb.get_height(); y++) {
for (size_t x = 0; x < rgb.get_width(); x++) {
size_t x_coord = tile_x * properties.m_tile_width + x;
size_t y_coord = tile_y * properties.m_tile_height + y;
size_t img_h = properties.m_canvas_height;
fb_write(fbp, (int)x_coord, (int)(img_h - y_coord - 1), rgb.get_storage()+((y * rgb.get_width() * 4) + (x * 4)), 1);
size_t x_coord = tile_x * rgb.get_width() + x;
size_t y_coord = tile_y * rgb.get_height() + y;
size_t img_h = frame->image().properties().m_canvas_height;
fb_write(fbp, (int)x_coord, (int)(img_h - y_coord), rgb.get_storage()+((y * rgb.get_width() * 4) + (x * 4)), 1);
}
}
}
+1 -1
View File
@@ -752,7 +752,7 @@ tessellate_part(struct creo_conv_info *cinfo, ProMdl model, struct bu_vls **snam
}
/* Check solidity */
success = bg_trimesh_solid((int)vert_tree->curr_vert, (size_t)faces.size() / 3,
success = !bg_trimesh_solid((int)vert_tree->curr_vert, (size_t)faces.size() / 3,
vert_tree->the_array, &faces[0], NULL);
/* If it's not solid and we're testing solidity, keep trying... */
+4
View File
@@ -1,5 +1,6 @@
brlcad_adddata(gtransfer.c sample_applications)
brlcad_addexec(brep-audit brep-audit.cpp "libbrep;libbv;librt;libbu")
brlcad_addexec(ganalyze ganalyze.cpp "libanalyze;librt;libbu;libpkg;${LINENOISE_LIBRARIES}" NO_INSTALL)
target_include_directories(ganalyze BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../libtermio)
@@ -20,6 +21,9 @@ add_subdirectory(gsh)
add_subdirectory(tests)
cmakefiles(
brep-audit-corpus.py
brep-audit-compare.py
brep-audit.cpp
CMakeLists.txt
)
+489
View File
@@ -0,0 +1,489 @@
#!/usr/bin/env python3
"""Validate and summarize two BRep corpus audit result sets."""
import argparse
import collections
import hashlib
import json
import math
import statistics
import sys
from pathlib import Path
REALIZATION_FORMAT = "brlcad-brep-realization-audit-v1"
MODES = ("wireframe", "shaded", "quality")
EXCLUSION_MODES = {
"wireframe_only": ("shaded", "quality"),
"open_surface": ("quality",),
}
TOP_LEVEL_FIELDS = (
"database", "object", "task_index", "status", "mode",
"ratio_limits", "tessellation_tolerance", "memory_limit_mib",
"input", "fast_options", "wire_options", "generators", "reference",
"wireframe", "shaded", "quality", "images", "issues",
)
RESULT_FIELDS = (
"return_code", "seconds", "vertices", "primitives", "commands",
"peak_rss_bytes", "peak_working_bytes", "invalid_indices", "finite",
"bbox_valid", "bbox_min", "bbox_max", "dimensions",
"dimension_ratios", "limits", "solid_validation", "repair", "issues",
)
NONNEGATIVE_RESULT_FIELDS = (
"seconds", "vertices", "primitives", "commands", "peak_rss_bytes",
"peak_working_bytes", "invalid_indices",
)
PAIR_CONFIG_FIELDS = (
("memory_limit_mib",),
("ratio_limits",),
("tessellation_tolerance",),
("quality_options", "max_face_time_ms"),
)
def parse_args():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--baseline-name", default="main")
parser.add_argument("--candidate-name", default="branch")
parser.add_argument(
"--baseline", nargs="+", type=Path, required=True,
help="baseline JSONL files in chronological escalation order",
)
parser.add_argument(
"--candidate", nargs="+", type=Path, required=True,
help="candidate JSONL files in chronological escalation order",
)
parser.add_argument("--output", type=Path)
parser.add_argument(
"--exclusions", type=Path,
help="shared eligibility JSONL: database, object, reason, evidence",
)
parser.add_argument(
"--allow-incomplete", action="store_true",
help="report unequal task sets without returning an error",
)
return parser.parse_args()
def field(record, path):
value = record
for component in path:
if not isinstance(value, dict) or component not in value:
return None
value = value[component]
return value
def finite_nonnegative(value):
return isinstance(value, (int, float)) and not isinstance(value, bool) \
and math.isfinite(value) and value >= 0
def validate_vector(value):
return isinstance(value, list) and len(value) == 3 and \
all(isinstance(component, (int, float)) and
not isinstance(component, bool) and math.isfinite(component)
for component in value)
def expected_empty_shading(result):
counts = [result.get(name) for name in (
"requested_items", "completed_items", "failed_items",
"skipped_items", "tolerance_skipped_items",
)]
if not all(isinstance(count, int) and not isinstance(count, bool) and
count >= 0 for count in counts):
return False
requested, completed, failed, skipped, tolerance_skipped = counts
return result.get("return_code") == 0 and failed == 0 and \
requested == completed == skipped + tolerance_skipped and \
result.get("vertices") == 0 and result.get("primitives") == 0
def validate_realization(record):
errors = []
for name in TOP_LEVEL_FIELDS:
if name not in record:
errors.append("missing top-level field '{}'".format(name))
mode = record.get("mode")
if mode not in MODES:
errors.append("invalid mode {!r}".format(mode))
return errors
result = record.get(mode)
if mode == "quality" and record.get("status") == "excluded" and \
record.get("quality_exclusion_reason") == "wireframe_only" and \
field(record, ("input", "loaded")) is True and \
field(record, ("input", "wireframe_only")) is True and \
result is None:
if record.get("issues") != []:
errors.append("wireframe-only quality exclusion contains issues")
return errors
if not isinstance(result, dict):
errors.append("mode result '{}' is not an object".format(mode))
return errors
for name in RESULT_FIELDS:
if name not in result:
errors.append("missing {}.{}".format(mode, name))
for name in NONNEGATIVE_RESULT_FIELDS:
value = result.get(name)
if not finite_nonnegative(value):
errors.append("invalid {}.{} value {!r}".format(
mode, name, value
))
if not isinstance(result.get("return_code"), int):
errors.append("invalid {}.return_code".format(mode))
for name in ("finite", "bbox_valid"):
if not isinstance(result.get(name), bool):
errors.append("invalid {}.{}".format(mode, name))
if result.get("bbox_valid"):
for name in ("bbox_min", "bbox_max", "dimensions"):
if not validate_vector(result.get(name)):
errors.append("invalid {}.{}".format(mode, name))
if not isinstance(result.get("issues"), list):
errors.append("invalid {}.issues".format(mode))
if not isinstance(record.get("issues"), list):
errors.append("invalid top-level issues")
if record.get("status") == "ok":
empty_shading = mode == "shaded" and expected_empty_shading(result)
if result.get("issues") or record.get("issues"):
errors.append("ok record contains issues")
if not result.get("bbox_valid") and not empty_shading:
errors.append("ok record has no bounding box")
if (result.get("vertices", 0) == 0 or result.get("primitives", 0) == 0) \
and not empty_shading:
errors.append("ok record has empty geometry")
if mode == "quality":
solid = result.get("solid_validation")
if not isinstance(solid, dict) or not solid.get("checked") or \
not solid.get("solid"):
errors.append("ok quality record lacks solid validation")
return errors
def file_digest(path):
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def load_exclusions(path):
"""Load documented input exclusions, independently of meshing outcomes."""
exclusions = {}
if path is None:
return exclusions
with path.open(encoding="utf-8") as source:
for line_number, line in enumerate(source, 1):
try:
row = json.loads(line)
if not isinstance(row, dict) or not all(
isinstance(row.get(name), str) and row[name]
for name in ("database", "object", "reason")) or \
row["reason"] not in EXCLUSION_MODES or \
not isinstance(row.get("evidence"), dict) or \
not row["evidence"]:
raise ValueError("expected object identity, supported reason, and evidence")
for mode in EXCLUSION_MODES[row["reason"]]:
key = (row["database"], row["object"], mode)
if key in exclusions:
raise ValueError("duplicate exclusion for " + "/".join(key))
exclusions[key] = row
except ValueError as error:
raise ValueError("{}:{}: {}".format(path, line_number, error)) from error
return exclusions
def shared_exclusions(baseline, candidate, declared=None):
exclusions = dict(declared or {})
for records in (baseline, candidate):
for key, record in records.items():
# A positive face-count classification transfers across revisions;
# failed validity or solid checks alone do not establish intent.
if record.get("format") != REALIZATION_FORMAT or \
key[2] != "quality" or \
field(record, ("input", "loaded")) is not True or \
field(record, ("input", "wireframe_only")) is not True:
continue
row = {
"database": key[0], "object": key[1],
"reason": "wireframe_only", "evidence": {"input": record["input"]},
}
for mode in EXCLUSION_MODES["wireframe_only"]:
exclusions.setdefault((key[0], key[1], mode), row)
return exclusions
def comparison_status(key, record, exclusions):
return "excluded" if key in exclusions else record.get("status", "unknown")
def load_records(paths):
records = {}
errors = []
inputs = []
for path in paths:
inputs.append((path, file_digest(path)))
with path.open("r", encoding="utf-8", errors="replace") as source:
for line_number, line in enumerate(source, 1):
try:
record = json.loads(line)
except json.JSONDecodeError as error:
errors.append("{}:{}: invalid JSON: {}".format(
path, line_number, error
))
continue
database = record.get("database")
object_name = record.get("object")
mode = record.get("mode")
if not database or not object_name or mode not in MODES:
continue
key = (database, object_name, mode)
if record.get("format") == REALIZATION_FORMAT:
for error in validate_realization(record):
errors.append("{}:{}: {}: {}".format(
path, line_number, "/".join(key), error
))
elif record.get("status") in (None, "ok"):
errors.append("{}:{}: unrecognized result format".format(
path, line_number
))
records[key] = record
return records, errors, inputs
def percentile(values, fraction):
if not values:
return None
ordered = sorted(values)
index = math.ceil(fraction * len(ordered)) - 1
return ordered[max(0, min(index, len(ordered) - 1))]
def fmt_number(value):
if value is None:
return "-"
if abs(value) >= 1000:
return "{:.0f}".format(value)
return "{:.6g}".format(value)
def timing_values(records, mode, exclusions):
values = []
for key, record in records.items():
if record.get("mode") != mode or \
record.get("format") != REALIZATION_FORMAT or \
comparison_status(key, record, exclusions) == "excluded":
continue
seconds = field(record, (mode, "seconds"))
if finite_nonnegative(seconds):
values.append(seconds)
return values
def status_counts(records, mode, exclusions):
counts = collections.Counter()
for key, record in records.items():
if record.get("mode") == mode:
counts[comparison_status(key, record, exclusions)] += 1
return counts
def append_outcome_counts(lines, title, revisions, exclusions):
lines.extend(("", title, "-" * len(title)))
lines.append("revision mode total eligible ok fail timeout excluded other")
for name, records in revisions:
for mode in MODES:
counts = status_counts(records, mode, exclusions)
known = sum(counts[status] for status in
("ok", "fail", "timeout", "excluded"))
total = sum(counts.values())
lines.append(
"{:<14} {:<10} {:>5} {:>8} {:>7} {:>7} {:>8} {:>8} {:>5}".format(
name, mode, total, total - counts["excluded"], counts["ok"],
counts["fail"], counts["timeout"], counts["excluded"], total - known
)
)
def failure_category(record):
status = record.get("status", "unknown")
if status in ("ok", "excluded"):
return status
mode = record.get("mode")
mode_issues = field(record, (mode, "issues")) or []
top_issues = record.get("issues") or []
if mode_issues:
return mode_issues[0]
if top_issues:
return top_issues[0]
return status
def nested_equal(left, right, path):
return field(left, path) == field(right, path)
def comparison_report(args, baseline, candidate, baseline_inputs,
candidate_inputs, schema_errors, declared_exclusions=None):
baseline_keys = set(baseline)
candidate_keys = set(candidate)
common_keys = baseline_keys & candidate_keys
baseline_only = sorted(baseline_keys - candidate_keys)
candidate_only = sorted(candidate_keys - baseline_keys)
exclusions = shared_exclusions(baseline, candidate, declared_exclusions)
revisions = ((args.baseline_name, baseline), (args.candidate_name, candidate))
config_errors = []
for key in sorted(common_keys):
left = baseline[key]
right = candidate[key]
if left.get("format") != REALIZATION_FORMAT or \
right.get("format") != REALIZATION_FORMAT:
continue
for path in PAIR_CONFIG_FIELDS:
if not nested_equal(left, right, path):
config_errors.append(
"{}: {} differs: {!r} versus {!r}".format(
"/".join(key), ".".join(path), field(left, path),
field(right, path)
)
)
lines = []
lines.append("BRep audit comparison")
lines.append("=====================")
lines.append("")
lines.append("Inputs")
lines.append("------")
for name, inputs in (
(args.baseline_name, baseline_inputs),
(args.candidate_name, candidate_inputs)):
for path, digest in inputs:
lines.append("{} {} {}".format(name, digest, path))
lines.append("")
lines.append("Validation")
lines.append("----------")
lines.append("schema errors: {}".format(len(schema_errors)))
lines.append("configuration mismatches: {}".format(len(config_errors)))
lines.append("matched tasks: {}".format(len(common_keys)))
lines.append("{}-only tasks: {}".format(
args.baseline_name, len(baseline_only)
))
lines.append("{}-only tasks: {}".format(
args.candidate_name, len(candidate_only)
))
lines.extend(("", "Shared eligibility", "------------------"))
lines.append("No-face inputs are excluded from shaded and quality comparisons; "
"documented open surfaces from quality only.")
lines.append("Exclusions apply to both revisions, including successes, failures, "
"timeouts, and errors. Wireframe remains eligible.")
lines.append("Invalidity or an open boundary alone does not exclude a repair "
"candidate. Raw records remain unchanged.")
if getattr(args, "exclusions", None):
lines.append("Exclusion evidence: {} {}".format(file_digest(args.exclusions), args.exclusions))
exclusion_counts = collections.Counter(
(key[2], exclusions[key]["reason"])
for key in (baseline_keys | candidate_keys) & set(exclusions)
)
for (mode, reason), count in sorted(exclusion_counts.items()):
lines.append("{} {}: {} shared tasks".format(mode, reason, count))
append_outcome_counts(lines, "Outcome counts (shared eligibility)", revisions, exclusions)
append_outcome_counts(lines, "Raw outcome counts (recorded)", revisions, {})
lines.append("")
lines.append("Generator timing (seconds, eligible completed process records)")
lines.append("--------------------------------------------------------------")
lines.append("revision mode n total median p95 max")
for name, records in revisions:
for mode in MODES:
values = timing_values(records, mode, exclusions)
lines.append(
"{:<14} {:<10} {:>5} {:>11} {:>11} {:>11} {:>11}".format(
name, mode, len(values), fmt_number(sum(values)),
fmt_number(statistics.median(values) if values else None),
fmt_number(percentile(values, 0.95)),
fmt_number(max(values) if values else None),
)
)
lines.append("")
lines.append("Matched successful timing ratios")
lines.append("---------------------------------")
lines.append("mode n candidate/baseline median p95")
for mode in MODES:
ratios = []
for key in common_keys:
if key[2] != mode or key in exclusions:
continue
left = baseline[key]
right = candidate[key]
if left.get("status") != "ok" or right.get("status") != "ok":
continue
left_seconds = field(left, (mode, "seconds"))
right_seconds = field(right, (mode, "seconds"))
if finite_nonnegative(left_seconds) and left_seconds > 0 and \
finite_nonnegative(right_seconds):
ratios.append(right_seconds / left_seconds)
lines.append("{:<10} {:>5} {:>26} {:>11}".format(
mode, len(ratios),
fmt_number(statistics.median(ratios) if ratios else None),
fmt_number(percentile(ratios, 0.95)),
))
lines.append("")
lines.append("Failure categories")
lines.append("------------------")
for name, records in revisions:
counts = collections.Counter(
(record.get("mode"), failure_category(record))
for key, record in records.items()
if comparison_status(key, record, exclusions) not in ("ok", "excluded")
)
if not counts:
lines.append("{}: none".format(name))
continue
for (mode, category), count in sorted(counts.items()):
lines.append("{} {:<10} {:>5} {}".format(
name, mode, count, category
))
if schema_errors or config_errors or baseline_only or candidate_only:
lines.append("")
lines.append("Validation details")
lines.append("------------------")
lines.extend(schema_errors)
lines.extend(config_errors)
for key in baseline_only:
lines.append("{} only: {}".format(
args.baseline_name, "/".join(key)
))
for key in candidate_only:
lines.append("{} only: {}".format(
args.candidate_name, "/".join(key)
))
return "\n".join(lines) + "\n", bool(
schema_errors or config_errors or baseline_only or candidate_only
)
def main():
args = parse_args()
try:
exclusions = load_exclusions(args.exclusions)
except (OSError, ValueError) as error:
sys.stderr.write("Invalid exclusion manifest: {}\n".format(error))
return 1
baseline, baseline_errors, baseline_inputs = load_records(args.baseline)
candidate, candidate_errors, candidate_inputs = load_records(
args.candidate
)
report, invalid = comparison_report(
args, baseline, candidate, baseline_inputs, candidate_inputs,
baseline_errors + candidate_errors, exclusions,
)
if args.output:
args.output.write_text(report, encoding="utf-8")
else:
sys.stdout.write(report)
return 1 if invalid and not args.allow_incomplete else 0
if __name__ == "__main__":
sys.exit(main())
+1200
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3
View File
@@ -34,6 +34,9 @@
// OpenCV header files
#include <opencv2/opencv.hpp>
#if CV_VERSION_MAJOR >= 5
#include <opencv2/geometry.hpp>
#endif
#include <opencv2/core/core.hpp>
#include "opencv2/highgui/highgui.hpp"
+6 -1
View File
@@ -10,7 +10,12 @@ if(TARGET slow-bigdb_5gb)
set_property(TEST slow-bigdb_5gb PROPERTY SKIP_RETURN_CODE 123)
endif(TARGET slow-bigdb_5gb)
cmakefiles(CMakeLists.txt)
find_package(Python3 QUIET COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
brlcad_add_test(NAME brep_audit_corpus COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/brep_audit_corpus.py")
endif()
cmakefiles(CMakeLists.txt brep_audit_corpus.py)
# Local Variables:
# tab-width: 8
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env python3
# BRL-CAD
# Copyright (c) 2026 United States Government as represented by
# the U.S. Army Research Laboratory.
# Distributed under the terms of the GNU Lesser General Public License
# (LGPL), version 2.1.
"""Exercise bounded corpus retries with a deterministic audit subprocess."""
import importlib.util
import json
import os
from pathlib import Path
import shutil
import sys
import tempfile
import time
import unittest
from unittest.mock import patch
def fixture():
batch = "--batch" in sys.argv
database = Path(sys.argv[-1] if batch else sys.argv[-2])
scenario = json.loads(database.read_text())
start = int(sys.argv[sys.argv.index("--batch-start") + 1]) if batch else 0
with database.with_suffix(".calls").open("a") as calls:
calls.write(json.dumps({"batch": batch, "start": start,
"arguments": sys.argv[1:]}) + "\n")
if not batch:
previous_pid = database.with_suffix(".pid")
if previous_pid.exists():
try:
os.kill(int(previous_pid.read_text()), 0)
except ProcessLookupError:
pass
else:
raise RuntimeError("batch still running during isolated retry")
objects = scenario["objects"]
indices = range(start, len(objects)) if batch else [int(sys.argv[-1])]
for index in indices:
details = objects[index]
record = {"format": "brlcad-brep-realization-audit-v1",
"database": str(database), "object": str(index),
"mode": "quality", "task_index": index if batch else -1,
"status": "ok", "quality": {}}
if batch or not details.get("recovers", True):
record.update(details["result"])
if batch:
progress = dict(record, format="brlcad-brep-audit-progress-v1")
print(json.dumps(progress), flush=True)
should_wait = batch and index > start and details.get("retry", False)
if should_wait:
database.with_suffix(".pid").write_text(str(os.getpid()))
print(json.dumps(record), flush=True)
if should_wait:
# The parent must stop this process before the isolated attempt.
time.sleep(10)
raise RuntimeError("parent did not stop the failed batch")
if batch:
print(json.dumps({"format": "brlcad-brep-audit-complete-v1",
"task_count": len(objects)}), flush=True)
@unittest.skipUnless(os.name == "posix", "executable subprocess fixture")
class CorpusResourceRetry(unittest.TestCase):
@classmethod
def setUpClass(cls):
source = Path(__file__).resolve().parent.parent / "brep-audit-corpus.py"
spec = importlib.util.spec_from_file_location("brep_audit_corpus", source)
cls.corpus = importlib.util.module_from_spec(spec)
spec.loader.exec_module(cls.corpus)
def check_retry(self, failure, recovers=True, previous=True, retry=True):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
audit = root / "audit"
shutil.copyfile(__file__, audit)
audit.chmod(0o755)
objects = ([{"result": {}}] if previous else [])
objects.extend([{"result": failure, "recovers": recovers,
"retry": retry}, {"result": {}}])
database = root / "fixture.g"
database.write_text(json.dumps({"objects": objects}))
with patch.object(sys, "argv", ["corpus", str(root),
str(root / "results.jsonl"),
"--modes", "quality", "--jobs", "1",
"--object-timeout", "3"]):
args = self.corpus.parse_args()
records = []
self.corpus.audit_database(
str(audit), args, root, database, 0, records.append,
tasks=[(str(i), "quality") for i in range(len(objects))],
)
self.assertEqual([r["task_index"] for r in records],
list(range(len(objects))))
self.assertEqual(records[-1]["status"], "ok")
calls = [json.loads(line) for line in
database.with_suffix(".calls").read_text().splitlines()]
failed_index = int(previous)
if previous and retry:
self.assertEqual([c["batch"] for c in calls], [True, False, True])
self.assertEqual(calls[-1]["start"], failed_index + 1)
isolated = records[failed_index]
self.assertEqual(isolated["status"], "ok" if recovers else "fail")
self.assertEqual(isolated["batch_attempt"]["status"], "fail")
self.assertIn("batch_fallback_reason", isolated)
for option in ("--jobs", "--memory-limit-mib", "--max-time-ms"):
values = [c["arguments"][c["arguments"].index(option) + 1]
for c in calls]
self.assertEqual(values, [values[0]] * len(values))
else:
self.assertEqual(len(calls), 1)
self.assertNotIn("batch_attempt", records[failed_index])
self.assertEqual(records[failed_index]["status"], failure["status"])
def test_memory_retry_preserves_history_and_limits(self):
self.check_retry({"status": "fail", "quality": {"limits": {"memory": True}}})
def test_repair_memory_history_triggers_retry(self):
self.check_retry({"status": "fail", "quality": {"repair": {
"resource_limits": self.corpus.REPAIR_LIMIT_MEMORY}}})
def test_load_failure_retries(self):
self.check_retry({"status": "fail", "issues": ["database_internal_load_failed"]})
def test_fresh_process_failure_remains_a_failure(self):
self.check_retry({"status": "fail", "quality": {"limits": {"memory": True}}},
recovers=False)
def test_first_object_is_already_isolated(self):
self.check_retry({"status": "fail", "quality": {"limits": {"memory": True}}},
previous=False)
def test_geometry_failure_is_not_retried(self):
self.check_retry({"status": "fail", "quality": {"issues": ["open_mesh"]}},
retry=False)
def test_success_with_resource_history_is_not_retried(self):
self.check_retry({"status": "ok", "quality": {"limits": {"memory": True}}},
retry=False)
if __name__ == "__main__":
if "--mode" in sys.argv:
fixture()
else:
unittest.main()
+1
View File
@@ -42,6 +42,7 @@ if(BRLCAD_ENABLE_QT AND OPENGL_TARGETS)
Qt6::Widgets
Qt6::OpenGL
Qt6::OpenGLWidgets
${OPENGL_TARGETS}
)
else(Qt6Widgets_FOUND)
qt5_wrap_cpp(moc_srcs ${isst_headers})
+15 -52
View File
@@ -26,7 +26,6 @@
#include <QFile>
#include <QPlainTextEdit>
#include <QTextStream>
#include <vector>
#include "gfile.h"
#include "bu/malloc.h"
@@ -34,7 +33,6 @@
#include "gcv.h"
#include "nmg.h"
#include "rt/geom.h"
#include "load_g.h"
/* Replace the load_g globals from adrt with a struct that is
* passed through the callers. */
@@ -104,8 +102,7 @@ nmg_to_adrt_internal(TIE_3 **tribuf, struct tie_s *cur_tie, struct adrt_mesh_s *
NMG_CK_VERTEX(v);
/* convert mm to m */
VSCALE((*tribuf[vert_count]).v, v->vg_p->coord,
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*tribuf[vert_count]).v, v->vg_p->coord, 1.0/1000.0);
vert_count++;
}
if (vert_count > 3)
@@ -188,12 +185,9 @@ nmg_to_adrt_regstart(struct db_tree_state *ts, const struct db_full_path *path,
for (i=0;i<bot->num_faces;i++)
{
VSCALE((*d->tribuf[0]).v, (bot->vertices+3*bot->faces[3*i+0]),
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*d->tribuf[1]).v, (bot->vertices+3*bot->faces[3*i+1]),
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*d->tribuf[2]).v, (bot->vertices+3*bot->faces[3*i+2]),
1.0 / ADRT_MODEL_UNITS_PER_TIE_UNIT);
VSCALE((*d->tribuf[0]).v, (bot->vertices+3*bot->faces[3*i+0]), 1.0/1000.0);
VSCALE((*d->tribuf[1]).v, (bot->vertices+3*bot->faces[3*i+1]), 1.0/1000.0);
VSCALE((*d->tribuf[2]).v, (bot->vertices+3*bot->faces[3*i+2]), 1.0/1000.0);
TIE_VAL(tie_push)(d->cur_tie, d->tribuf, 1, mesh, 0);
}
@@ -239,13 +233,6 @@ nmg_to_adrt_gcvwrite(struct nmgregion *r, const struct db_full_path *pathp, stru
nmg_to_adrt_internal(d->tribuf, d->cur_tie, mesh, r);
}
GFile::~GFile()
{
closedb();
}
int
GFile::load_g(const char *filename, int argc, const char *argv[])
{
@@ -254,9 +241,6 @@ GFile::load_g(const char *filename, int argc, const char *argv[])
struct bg_tess_tol ttol; /* tessellation tolerance in mm */
struct db_tree_state tree_state; /* includes tol & model */
struct isst_nmg_data d;
std::vector<const char *> geometry_paths;
closedb();
RT_DBTS_INIT(&tree_state);
tree_state.ts_tol = &tol;
@@ -294,8 +278,6 @@ GFile::load_g(const char *filename, int argc, const char *argv[])
}
if (db_dirbuild(dbip)) {
bu_log("ERROR: db_dirbuild failed\n");
db_close(dbip);
dbip = NULL;
return -1;
}
d.dbip = dbip;
@@ -322,30 +304,20 @@ GFile::load_g(const char *filename, int argc, const char *argv[])
tribuf[2] = (TIE_3 *)bu_malloc(sizeof(TIE_3) * 3, "triangle tribuffer");
d.tribuf = this->tribuf;
annotations = rt_annot_scene_create(dbip, argc, argv, &ttol, &tol);
for (int i = 0; i < argc; ++i) {
if (adrt_path_has_geometry(dbip, argv[i]))
geometry_paths.push_back(argv[i]);
}
if (!geometry_paths.empty()) {
(void) db_walk_tree(dbip,
(int)geometry_paths.size(), /* number of toplevel regions */
geometry_paths.data(), /* region names */
1, /* ncpu */
&tree_state, /* initial tree state */
nmg_to_adrt_regstart, /* region start function */
gcv_region_end, /* region end function */
adrt_leaf_tess, /* leaf func */
(void *)&gcvwriter); /* client data */
}
(void) db_walk_tree(dbip,
argc, /* number of toplevel regions */
argv, /* region names */
1, /* ncpu */
&tree_state, /* initial tree state */
nmg_to_adrt_regstart, /* region start function */
gcv_region_end, /* region end function */
rt_booltree_leaf_tess, /* leaf func */
(void *)&gcvwriter); /* client data */
/* Release dynamic storage */
nmg_km(the_model);
rt_vlist_cleanup();
db_close(dbip);
dbip = NULL;
bu_free(tribuf[0], "vert");
bu_free(tribuf[1], "vert");
bu_free(tribuf[2], "vert");
@@ -359,17 +331,7 @@ GFile::load_g(const char *filename, int argc, const char *argv[])
void
GFile::closedb()
{
rt_annot_scene_destroy(annotations);
annotations = NULL;
if (tie) {
TIE_FREE(tie);
bu_free(tie, "free tie");
tie = NULL;
}
if (dbip) {
db_close(dbip);
dbip = NULL;
}
bu_free(tie, "free tie");
current_file.clear();
}
@@ -383,3 +345,4 @@ GFile::closedb()
* End:
* ex: shiftwidth=4 tabstop=8
*/
+5 -8
View File
@@ -32,7 +32,6 @@
extern "C" {
#include "bn/tol.h"
#include "raytrace.h"
#include "rt/primitives/annot.h"
#include "rt/tie.h"
#include "adrt.h"
@@ -44,16 +43,13 @@ class GFile : public QObject
Q_OBJECT
public:
~GFile() override;
int load_g(const char *filename, int argc, const char **argv);
void closedb();
struct tie_s *tie = NULL;
struct adrt_mesh_s *meshes = NULL;
struct db_i *dbip = NULL;
struct rt_annot_scene *annotations = NULL;
TIE_3 **tribuf = NULL;
struct tie_s *tie;
struct adrt_mesh_s *meshes;
struct db_i *dbip;
TIE_3 **tribuf;
QString current_file;
};
@@ -70,3 +66,4 @@ class GFile : public QObject
* End:
* ex: shiftwidth=4 tabstop=8
*/
+2 -4
View File
@@ -28,9 +28,6 @@
int
ISSTApp::load_g(const char *filename, int argc, const char *argv[])
{
/* Synchronize with the render thread before GFile releases the old
* annotation scene and TIE data. */
w.canvas->clear_tie();
if (g.load_g(filename, argc, argv)) {
w.statusBar()->showMessage("open failed");
return -1;
@@ -38,7 +35,7 @@ ISSTApp::load_g(const char *filename, int argc, const char *argv[])
// The OpenGL widget manages the rendering, so let it know about the
// TIE data structure associated with the current model
w.canvas->set_tie(g.tie, g.annotations);
w.canvas->set_tie(g.tie);
return 0;
}
@@ -54,3 +51,4 @@ ISSTApp::load_g(const char *filename, int argc, const char *argv[])
* End:
* ex: shiftwidth=4 tabstop=8
*/
+4 -1
View File
@@ -36,14 +36,16 @@
class ISSTApp : public QApplication
{
Q_OBJECT
public:
ISSTApp(int &argc, char *argv[]) :QApplication(argc, argv) {};
~ISSTApp() {};
int load_g(const char *filename, int argc, const char **argv);
GFile g;
ISST_MainWindow w;
GFile g;
};
#endif // ISSTAPP_H
@@ -58,3 +60,4 @@ class ISSTApp : public QApplication
* End:
* ex: shiftwidth=4 tabstop=8
*/
+9 -28
View File
@@ -159,13 +159,8 @@ void TIERenderer::render()
m_grabCond.wait(&m_grabMutex);
QMutexLocker lock(&m_renderMutex);
m_grabMutex.unlock();
/* The GUI may clear a scene while a queued render is waiting for the
* context. Return the context before leaving that stale request. */
if (m_exiting || !tie) {
ctx->moveToThread(qGuiApp->thread());
if (m_exiting)
return;
}
Q_ASSERT(ctx->thread() == QThread::currentThread());
@@ -188,8 +183,7 @@ void TIERenderer::render()
// Core TIE render
render_camera_prep(&camera);
render_camera_render_annotations(&camera, tie, &tile, &buffer_image,
annotations, ADRT_MODEL_UNITS_PER_TIE_UNIT);
render_camera_render(&camera, tie, &tile, &buffer_image);
glDisable(GL_LIGHTING);
@@ -303,31 +297,17 @@ void isstGL::grabContext()
}
void
isstGL::clear_tie()
isstGL::set_tie(struct tie_s *in_tie)
{
m_renderer->lockRenderer();
m_renderer->tie = NULL;
m_renderer->annotations = NULL;
m_renderer->changed = false;
m_renderer->unlockRenderer();
}
void
isstGL::set_tie(struct tie_s *in_tie, struct rt_annot_scene *annotations)
{
m_renderer->lockRenderer();
m_renderer->tie = in_tie;
m_renderer->annotations = annotations;
render_camera_fit_scene(&m_renderer->camera, m_renderer->tie, annotations,
ADRT_MODEL_UNITS_PER_TIE_UNIT);
// Initialize the camera position
VSETALL(m_renderer->camera.pos, m_renderer->tie->radius);
VMOVE(m_renderer->camera.focus, m_renderer->tie->mid);
// Record the initial settings for use in subsequent calculations
VMOVE(m_renderer->camera_pos_init, m_renderer->camera.pos);
VMOVE(m_renderer->camera_focus_init, m_renderer->camera.focus);
m_renderer->changed = true;
m_renderer->unlockRenderer();
VSETALL(m_renderer->camera_pos_init, m_renderer->tie->radius);
VMOVE(m_renderer->camera_focus_init, m_renderer->tie->mid);
// Having just loaded a new TIE scene,
// we need a new image
@@ -389,3 +369,4 @@ void isstGL::save_image() {
// c-file-style: "stroustrup"
// End:
// ex: shiftwidth=4 tabstop=8
+2 -3
View File
@@ -77,7 +77,6 @@ class TIERenderer : public QObject, protected QOpenGLFunctions
public:
struct tie_s *tie = NULL; // From parent app
struct rt_annot_scene *annotations = NULL; // Owned by the parent app
struct render_camera_s camera;
vect_t camera_pos_init;
vect_t camera_focus_init;
@@ -114,8 +113,7 @@ class isstGL : public QOpenGLWidget
explicit isstGL(QWidget *parent = nullptr);
~isstGL();
void clear_tie();
void set_tie(struct tie_s *in_tie, struct rt_annot_scene *annotations);
void set_tie(struct tie_s *in_tie);
void save_image();
@@ -155,3 +153,4 @@ class isstGL : public QOpenGLWidget
// c-file-style: "stroustrup"
// End:
// ex: shiftwidth=4 tabstop=8
+2 -1
View File
@@ -29,6 +29,7 @@ set(
api.c
centroid.c
check_options.c
contour.cpp
density.cpp
globals.c
inside.c
@@ -68,7 +69,7 @@ set(
cmakefiles(${libanalyze_ignored_files})
# Note - libanalyze_deps is defined by ${BRLCAD_SOURCE_DIR}/src/source_dirs.cmake
brlcad_addlib(libanalyze "${LIBANALYZE_SOURCES}" "" "${CMAKE_CURRENT_BINARY_DIR}"
brlcad_addlib(libanalyze "${LIBANALYZE_SOURCES}" "" "${CMAKE_CURRENT_BINARY_DIR};${EIGEN3_INCLUDE_DIR}"
PUBLIC_LIBS ${libanalyze_deps}
)
set_target_properties(libanalyze PROPERTIES VERSION 20.0.1 SOVERSION 20)
File diff suppressed because it is too large Load Diff
+3 -196
View File
@@ -480,22 +480,6 @@ _nirt_get_resource(struct nirt_state *nss)
}
static void
_nirt_annotation_result_clear(struct nirt_state *nss)
{
delete nss->i->vals->annotation;
nss->i->vals->annotation = NULL;
}
static void
_nirt_annotation_scene_clear(struct nirt_state *nss)
{
rt_annot_scene_destroy(nss->i->annotation_scene);
nss->i->annotation_scene = NULL;
}
int
_nirt_raytrace_prep(struct nirt_state *nss)
{
@@ -533,17 +517,11 @@ _nirt_raytrace_prep(struct nirt_state *nss)
bu_free(attrs, "objs");
return -1;
}
bu_free(objs, "objs");
bu_free(attrs, "objs");
nmsg(nss, "Prepping the geometry...\n");
rt_prep(nss->i->ap->a_rt_i);
_nirt_annotation_scene_clear(nss);
if (nss->i->use_annotations) {
nss->i->annotation_scene = rt_annot_scene_create(nss->i->dbip,
ocnt, objs, &nss->i->ap->a_rt_i->rti_ttol,
&nss->i->ap->a_rt_i->rti_tol);
}
bu_free(objs, "objs");
nmsg(nss, "%s ", (nss->i->active_paths.size() == 1) ? "Object" : "Objects");
for (size_t i = 0; i < nss->i->active_paths.size(); i++) {
if (i == 0) {
@@ -982,17 +960,6 @@ _nirt_print_fmt_substr(struct nirt_state *nss, struct bu_vls *ostr, const char *
nirt_print_key("a", r->a);
nirt_print_key("e", r->e);
if (r->annotation) {
nirt_print_key("annot_path", r->annotation->path.c_str());
nirt_print_key("annot_segment", r->annotation->segment);
nirt_print_key("annot_role", r->annotation->role.c_str());
nirt_print_key("annot_x", r->annotation->point[X] * base2local);
nirt_print_key("annot_y", r->annotation->point[Y] * base2local);
nirt_print_key("annot_z", r->annotation->point[Z] * base2local);
nirt_print_key("annot_d", r->annotation->d * base2local);
nirt_print_key("annot_visible", r->annotation->visible);
}
if (!r->seg || r->seg->type == 0) return;
if (r->seg->type == NIRT_PARTITION_SEG || r->seg->type == NIRT_ALL_SEG) {
@@ -1089,9 +1056,6 @@ _nirt_report(struct nirt_state *nss, char type, struct nirt_output_record *r)
std::vector<std::pair<std::string,std::string> >::iterator f_it;
switch (type) {
case 'a':
fmt_vect = &nss->i->fmt.annotation;
break;
case 'r':
fmt_vect = &nss->i->fmt.ray;
break;
@@ -1127,75 +1091,6 @@ _nirt_report(struct nirt_state *nss, char type, struct nirt_output_record *r)
}
static const char *
_nirt_annotation_role(uint32_t role)
{
switch (role) {
case RT_ANNOT_ROLE_UNSPECIFIED:
return "unspecified";
case RT_ANNOT_ROLE_GEOMETRY:
return "geometry";
case RT_ANNOT_ROLE_TEXT:
return "text";
case RT_ANNOT_ROLE_LEADER:
return "leader";
case RT_ANNOT_ROLE_EXTENSION:
return "extension";
case RT_ANNOT_ROLE_DIMENSION:
return "dimension";
case RT_ANNOT_ROLE_ARROWHEAD:
return "arrowhead";
case RT_ANNOT_ROLE_SYMBOL:
return "symbol";
case RT_ANNOT_ROLE_MASK:
return "mask";
case RT_ANNOT_ROLE_CENTERMARK:
return "centermark";
case RT_ANNOT_ROLE_PLACEHOLDER:
return "placeholder";
case RT_ANNOT_ROLE_TEXT_DECORATION:
return "text-decoration";
default:
return "unknown";
}
}
static void
_nirt_report_annotations(struct nirt_state *nss)
{
const size_t count = rt_annot_scene_query_model(nss->i->annotation_scene,
&nss->i->ap->a_ray, nss->i->first_solid_distance, NULL, 0);
if (!count)
return;
std::vector<struct rt_annot_hit> hits(count);
const size_t found = rt_annot_scene_query_model(nss->i->annotation_scene,
&nss->i->ap->a_ray, nss->i->first_solid_distance, hits.data(),
hits.size());
nirt_seg *saved_segment = nss->i->vals->seg;
nss->i->vals->seg = NULL;
for (size_t i = 0; i < std::min(found, hits.size()); ++i) {
const struct rt_annot_hit &hit = hits[i];
if (hit.segment > (size_t)std::numeric_limits<int>::max()) {
nerr(nss, "Annotation segment index is too large for NIRT reporting\n");
continue;
}
if (!nss->i->vals->annotation)
nss->i->vals->annotation = new nirt_annotation_record;
nirt_annotation_record *record = nss->i->vals->annotation;
record->path = hit.path ? hit.path : "";
record->segment = (int)hit.segment;
record->role = _nirt_annotation_role(hit.role);
VMOVE(record->point, hit.point);
record->d = d_calc(nss, record->point);
record->visible = hit.visible;
_nirt_report(nss, 'a', nss->i->vals);
}
nss->i->vals->seg = saved_segment;
}
/************************
* Raytracing Callbacks *
************************/
@@ -1318,21 +1213,6 @@ _nirt_if_hit(struct application *ap, struct partition *part_head, struct seg *UN
int ev_odd = 1; /* first partition is colored as "odd" */
point_t out_old = VINIT_ZERO;
double d_out_old = 0.0;
nss->i->first_solid_distance = INFINITY;
for (part = part_head->pt_forw; part != part_head; part = part->pt_forw) {
if (part->pt_regionp->reg_aircode)
continue;
if (part->pt_inhit->hit_dist >= 0.0) {
nss->i->first_solid_distance = part->pt_inhit->hit_dist;
break;
}
if (part->pt_outhit->hit_dist >= 0.0) {
nss->i->first_solid_distance = part->pt_outhit->hit_dist;
break;
}
}
nirt_seg *s = new nirt_seg;
if (vals->seg) {
delete vals->seg;
@@ -1669,10 +1549,9 @@ static const struct nirt_cmd_desc nirt_descs[] = {
{ "s", "shoot a ray at the target", NULL },
{ "backout", "back out of model", NULL },
{ "useair", "set/query use of air", "<0|1|2|...>" },
{ "useannot", "set/query annotation reporting", "<0|1>" },
{ "units", "set/query local units", "<mm|cm|m|in|ft>" },
{ "overlap_claims", "set/query overlap rebuilding/retention", "<0|1|2|3>" },
{ "fmt", "set/query output formats", "{arhpfmog} format item item ..." },
{ "fmt", "set/query output formats", "{rhpfmog} format item item ..." },
{ "plotfile", "designate an output file to hold plot data", "file" },
{ "print", "query an output item", "item" },
{ "debug", "set/query nirt debug flags", "[-h] [-l [lib]] [-C [lib]] [-V [lib] [val]] [lib [flag]]" },
@@ -1949,9 +1828,6 @@ _nirt_cmd_shoot(void *ns, int argc, const char **UNUSED(argv))
return -1;
}
_nirt_annotation_result_clear(nss);
nss->i->first_solid_distance = INFINITY;
/* If we have no active rtip, we don't need to prep or shoot */
if (!_nirt_get_rtip(nss)) {
return 0;
@@ -1984,10 +1860,9 @@ _nirt_cmd_shoot(void *ns, int argc, const char **UNUSED(argv))
nss->i->ap->a_ray.r_pt[2] * nss->i->base2local,
V3ARGS(nss->i->ap->a_ray.r_dir));
// TODO - any necessary initialization for data collection by callbacks
_nirt_init_ovlp(nss);
(void)rt_shootray(nss->i->ap);
if (nss->i->use_annotations)
_nirt_report_annotations(nss);
// Undo backout
for (i = 0; i < 3; ++i) {
@@ -2090,45 +1965,6 @@ _nirt_cmd_use_air(void *ns, int argc, const char *argv[])
}
extern "C" int
_nirt_cmd_use_annotations(void *ns, int argc, const char *argv[])
{
struct nirt_state *nss = (struct nirt_state *)ns;
struct bu_vls optparse_msg = BU_VLS_INIT_ZERO;
int enabled = 0;
if (!ns) return -1;
if (argc == 1) {
nout(nss, "use_annotations = %d\n", nss->i->use_annotations);
return 0;
}
if (argc > 2) {
nerr(nss, "Usage: useannot %s\n", _nirt_get_desc_args("useannot"));
return -1;
}
if (bu_opt_int(&optparse_msg, 1, (const char **)&argv[1],
(void *)&enabled) == -1) {
nerr(nss, "Error: bu_opt value read failure: %s\n\nUsage: useannot %s\n",
bu_vls_cstr(&optparse_msg), _nirt_get_desc_args("useannot"));
bu_vls_free(&optparse_msg);
return -1;
}
bu_vls_free(&optparse_msg);
if (enabled != 0 && enabled != 1) {
nerr(nss, "useannot must be set to 0 (off) or 1 (on)\n");
return -1;
}
if (enabled != nss->i->use_annotations) {
nss->i->use_annotations = enabled;
_nirt_annotation_scene_clear(nss);
_nirt_annotation_result_clear(nss);
if (enabled)
nss->i->need_reprep = 1;
}
return 0;
}
extern "C" int
_nirt_cmd_units(void *ns, int argc, const char *argv[])
{
@@ -2249,9 +2085,6 @@ _nirt_cmd_format_output(void *ns, int argc, const char **argv)
// print out current fmt str for the specified type
struct bu_vls ostr = BU_VLS_INIT_ZERO;
switch (type) {
case 'a':
_nirt_print_fmt_str(&ostr, nss->i->fmt.annotation);
break;
case 'r':
_nirt_print_fmt_str(&ostr, nss->i->fmt.ray);
break;
@@ -2358,10 +2191,6 @@ _nirt_cmd_format_output(void *ns, int argc, const char **argv)
* formatting instructions with the new. */
set_fmt:
switch (type) {
case 'a':
nss->i->fmt.annotation.clear();
nss->i->fmt.annotation = fmt_tmp;
break;
case 'r':
nss->i->fmt.ray.clear();
nss->i->fmt.ray = fmt_tmp;
@@ -2714,7 +2543,6 @@ _nirt_cmd_state(void *ns, int argc, const char *argv[])
bu_vls_printf(&dumpstr, "xyz %g %g %g\n", V3ARGS(nss->i->vals->orig));
bu_vls_printf(&dumpstr, "dir %g %g %g\n", V3ARGS(nss->i->vals->dir));
bu_vls_printf(&dumpstr, "useair %d\n", nss->i->use_air);
bu_vls_printf(&dumpstr, "useannot %d\n", nss->i->use_annotations);
bu_vls_printf(&dumpstr, "units %s\n", bu_units_string(nss->i->local2base));
bu_vls_printf(&dumpstr, "overlap_claims ");
switch (nss->i->overlap_claims) {
@@ -2735,8 +2563,6 @@ _nirt_cmd_state(void *ns, int argc, const char *argv[])
break;
}
struct bu_vls ostr = BU_VLS_INIT_ZERO;
_nirt_print_fmt_cmd(&ostr, 'a', nss->i->fmt.annotation);
bu_vls_printf(&dumpstr, "%s", bu_vls_cstr(&ostr));
_nirt_print_fmt_cmd(&ostr, 'r', nss->i->fmt.ray);
bu_vls_printf(&dumpstr, "%s", bu_vls_cstr(&ostr));
_nirt_print_fmt_cmd(&ostr, 'h', nss->i->fmt.head);
@@ -2791,7 +2617,6 @@ const struct bu_cmdtab _libanalyze_nirt_cmds[] = {
{ "state", _nirt_cmd_state},
{ "units", _nirt_cmd_units},
{ "useair", _nirt_cmd_use_air},
{ "useannot", _nirt_cmd_use_annotations},
{ "xyz", _nirt_cmd_target_coor},
{ (char *)NULL, NULL}
};
@@ -2958,14 +2783,6 @@ static const char *nirt_cmd_tbl_defs =
"z_dir, FNOUNIT, Ray direction unit vector z component,"
"a, FNOUNIT, Azimuth,"
"e, FNOUNIT, Elevation,"
"annot_path, STRING, Annotation database path,"
"annot_segment, INT, Annotation segment index,"
"annot_role, STRING, Annotation segment role,"
"annot_x, FLOAT, Annotation hit X coordinate,"
"annot_y, FLOAT, Annotation hit Y coordinate,"
"annot_z, FLOAT, Annotation hit Z coordinate,"
"annot_d, FLOAT, Annotation hit gridplane distance,"
"annot_visible, INT, Annotation hit visibility,"
"x_in, FLOAT, ,"
"y_in, FLOAT, ,"
"z_in, FLOAT, ,"
@@ -3078,7 +2895,6 @@ nirt_init(struct nirt_state *ns)
n->base2local = 0.0;
n->local2base = 0.0;
n->use_air = 0;
n->use_annotations = 0;
n->backout = 1;
n->b_state = false;
@@ -3093,8 +2909,6 @@ nirt_init(struct nirt_state *ns)
BU_GET(n->res_air, struct resource);
n->rtip = RTI_NULL;
n->rtip_air = RTI_NULL;
n->annotation_scene = NULL;
n->first_solid_distance = INFINITY;
n->need_reprep = 1;
BU_GET(n->val_types, struct bu_attribute_value_set);
@@ -3108,7 +2922,6 @@ nirt_init(struct nirt_state *ns)
n->vals->h = 0.0;
n->vals->v = 0.0;
n->vals->d_orig = 0.0;
n->vals->annotation = NULL;
n->vals->seg = NULL;
_nirt_diff_create(ns);
@@ -3212,8 +3025,6 @@ nirt_clear_dbip(struct nirt_state *ns)
{
if (!ns) return -1;
_nirt_annotation_scene_clear(ns);
_nirt_annotation_result_clear(ns);
db_close(ns->i->dbip);
ns->i->dbip = NULL;
@@ -3234,9 +3045,6 @@ void
nirt_destroy(struct nirt_state *ns)
{
if (!ns) return;
_nirt_annotation_scene_clear(ns);
_nirt_annotation_result_clear(ns);
bu_vls_free(&ns->nirt_cmd);
bu_vls_free(ns->i->err);
bu_vls_free(ns->i->msg);
@@ -3249,7 +3057,6 @@ nirt_destroy(struct nirt_state *ns)
db_close(ns->i->dbip);
delete ns->i->vals->seg;
BU_PUT(ns->i->vals, struct nirt_output_record);
BU_PUT(ns->i->res, struct resource);
+1 -17
View File
@@ -83,7 +83,7 @@ extern "C" b_off_t ftello(FILE *);
#define NIRT_OVLP_RETAIN 3
#define NIRT_PRINTF_SPECIFIERS "difeEgGs"
#define NIRT_OUTPUT_TYPE_SPECIFIERS "arhpfmog"
#define NIRT_OUTPUT_TYPE_SPECIFIERS "rhpfmog"
#define HELPFLAG "--print-help"
#define PURPOSEFLAG "--print-purpose"
@@ -274,16 +274,6 @@ class nirt_seg {
}
};
class nirt_annotation_record {
public:
std::string path;
int segment;
std::string role;
point_t point;
fastf_t d;
int visible;
};
/* This record structure doesn't have to correspond exactly to the above list
* of available values, but it needs to retain sufficient information to
* support the ability to generate all of them upon request. */
@@ -296,7 +286,6 @@ struct nirt_output_record {
fastf_t a;
fastf_t e;
struct nirt_overlap ovlp_list;
nirt_annotation_record *annotation;
nirt_seg *seg;
};
@@ -305,7 +294,6 @@ struct nirt_diff_state;
class nirt_fmt_state {
public:
std::vector<std::pair<std::string,std::string> > annotation;
std::vector<std::pair<std::string,std::string> > ray;
std::vector<std::pair<std::string,std::string> > head;
std::vector<std::pair<std::string,std::string> > part;
@@ -315,7 +303,6 @@ class nirt_fmt_state {
std::vector<std::pair<std::string,std::string> > gap;
void clear() {
annotation.clear();
ray.clear();
head.clear();
part.clear();
@@ -360,7 +347,6 @@ struct nirt_state_impl {
int backout;
int overlap_claims;
int use_air;
int use_annotations;
std::set<std::string> attrs; // active attributes
std::vector<std::string> active_paths; // active paths for raytracer
struct nirt_output_record *vals;
@@ -382,8 +368,6 @@ struct nirt_state_impl {
struct resource *res;
struct rt_i *rtip_air;
struct resource *res_air;
struct rt_annot_scene *annotation_scene;
fastf_t first_solid_distance;
int need_reprep;
/* internal format specifier arrays */
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -1,9 +1,9 @@
brlcad_addexec(analyze_contour contour.cpp "libanalyze;libwdb;librt;libbg;libbu" TEST)
brlcad_add_test(NAME analyze_contour COMMAND analyze_contour)
brlcad_addexec(analyze_raydiff raydiff.c "libanalyze;librt;libbu" TEST)
brlcad_addexec(analyze_sp solid_partitions.c "libanalyze;librt;libbu" TEST)
brlcad_addexec(analyze_nhit nhit.cpp "libanalyze;librt;libbu" TEST_USESDATA)
brlcad_addexec(analyze_nirt_annotations nirt_annotations.cpp
"libanalyze;libwdb;librt;libbu" TEST_USESDATA)
brlcad_add_test(NAME analyze_nirt_annotations COMMAND analyze_nirt_annotations)
#####################################
# analyze_densities testing #
@@ -16,6 +16,7 @@ brlcad_add_test(NAME analyze_densities_std COMMAND analyze_densities std)
cmakefiles(
CMakeLists.txt
arbs.g
contour.cpp
raydiff.g
)

Some files were not shown because too many files have changed in this diff Show More