Compare commits

...
69 Commits
Author SHA1 Message Date
Clifford Yapp bc130e1e7a Merge branch 'RELEASE' 2026-10-12 10:39:22 -04:00
Clifford Yapp de83ba5d30 Bump to dev revision after tagging 7.46.0. We already have enough changes pending for another minor - if 7.46 needs a patch release it will come from the rel-7-46 branch. 2026-10-12 10:37:59 -04:00
Clifford Yapp 31470287a1 Update ChangeLog, set release date 2026-10-12 10:28:55 -04:00
Clifford Yapp dc3b4abaa1 Improve distcheck bext path handling 2026-10-10 20:17:12 -04:00
Clifford Yapp 3f1eb8f584 Update bext logic to make URL modular 2026-10-10 20:17:06 -04:00
Clifford Yapp 561b3c3aab move extern definition 2026-10-10 20:16:56 -04:00
Clifford Yapp d03b9838a1 no-tk and goto adjustments for FreeBSD building 2026-10-10 20:16:50 -04:00
Clifford Yapp 48cd318a6d Make github.com bext sha1 query non-interactive
This is an optional check, so we never want it to go interactive if
anything is weird with upstream.

`credential.helper=` prevents credential-manager/browser prompts,
`credential.interactive=false` disables Git credential interaction, and
the `GIT_TERMINAL_PROMPT=0` environment variable blocks terminal
prompting and helps with older Git versions
2026-10-10 20:16:44 -04:00
Clifford Yapp 1f0193dbaf Isolate GCV regression outputs
Give each GCV regression target a distinct output path so parallel
tests cannot remove one another's results. Validate and clean every
input immediately, and update companion-file cleanup paths.
2026-10-10 20:16:33 -04:00
Clifford Yapp 2e7e827ba0 Fix conditional datetime conversion constant
Derive nanoseconds per microsecond from the existing unit constants.

This avoids an unused file-scope constant in clock_gettime builds.
2026-10-10 20:16:17 -04:00
Clifford Yapp 342d8a9152 Improve distcheck bext path handling 2026-10-10 20:03:02 -04:00
Clifford Yapp 9e030edd60 Update bext logic to make URL modular 2026-10-10 11:39:21 -04:00
Clifford Yapp b5bf1683a2 move extern definition 2026-10-08 16:34:31 -04:00
Clifford Yapp eb7e9cb38c no-tk and goto adjustments for FreeBSD building 2026-10-08 16:31:46 -04:00
Clifford Yapp 84be85a74d Make github.com bext sha1 query non-interactive
This is an optional check, so we never want it to go interactive if
anything is weird with upstream.

`credential.helper=` prevents credential-manager/browser prompts,
`credential.interactive=false` disables Git credential interaction, and
the `GIT_TERMINAL_PROMPT=0` environment variable blocks terminal
prompting and helps with older Git versions
2026-10-07 11:05:52 -04:00
Clifford Yapp 3fd9ec8f4b Isolate GCV regression outputs
Give each GCV regression target a distinct output path so parallel
tests cannot remove one another's results. Validate and clean every
input immediately, and update companion-file cleanup paths.
2026-10-06 18:21:05 +00:00
Clifford Yapp 7d2392d754 Fix conditional datetime conversion constant
Derive nanoseconds per microsecond from the existing unit constants.

This avoids an unused file-scope constant in clock_gettime builds.
2026-10-06 18:20:41 +00:00
Christopher Sean Morrison e7c61497df new doc on representations 2026-10-06 10:51:33 -04:00
Christopher Sean Morrison 08da151a6e add new documentation explaining the differences between geometry representation schemes with examples 2026-10-06 10:50:13 -04:00
Clifford Yapp 757bb76203 Update log with commits being incorporated into rel-7-46-0 2026-10-06 01:47:38 -04:00
Clifford Yapp c70bab52c4 Update version numbers 2026-10-06 01:45:41 -04:00
Clifford Yapp 7929a747ad Fix Archer saving window sizes before they're ready 2026-10-06 01:42:31 -04:00
Clifford Yapp eac0f4d265 Adjustments for Archer/Itcl 2026-10-06 00:57:13 -04:00
Clifford Yapp d6ae424de0 Correct Archer plugin setup 2026-10-06 00:47:05 -04:00
Clifford Yapp 27ba3c4880 Be more aggressive about making sure the install rpath setup is our standard one. 2026-10-05 21:29:34 +00:00
Clifford Yapp 7ade8db130 Got the test backwards 2026-10-05 15:30:29 -04:00
Clifford Yapp e91023f354 Disable rtcmp tests for a BRLCAD_ENABLE_RUNTIME_DEBUG=OFF build
rtcmp logging in librt is in the hot loop for rt_shootray, so
even the enabled/disabled conditional check imposes about 1.5%
performance overhead in an optimized build configuration.  Since
that isn't acceptable when we're disabling debugging support for
maximum performance, disable the tests in that particular case.
2026-10-05 13:50:34 -04:00
Clifford Yapp 303b90f7f1 librt: handle empty and instanced trees during bounds traversal
rt_gettree can succeed for an empty combination with no primitives,
leaving its imported tree null.  The old traversal reached
RT_CK_TREE.  With runtime checks enabled, that bombs and facetize's
setjmp catches it.  Builds with runtime checks disabled compile out
the magic check and dereference null instead.  This explains the
different outcomes across build configurations.

Return an error for null or NOP trees at every recursion level, so
nested empty combinations also fail without a bomb.  Carry each
member's accumulated transform when selecting a prepared solid;
matching only the name chose the wrong instance when one primitive
appeared at multiple locations.  Release imported combinations
after recursion, use a stack tree for direct primitives, and return
lookup errors instead of exiting the process.

Cover empty and nested combinations, direct primitives, repeated
transformed instances, and transforms through nested combinations.
2026-10-05 16:45:33 +00:00
Clifford Yapp 875091f78a Check for null tree before doing rt_traverse_tree 2026-10-05 11:49:40 -04:00
Christopher Sean Morrison 0716e4c842 Revert "add a test to get regions"
This reverts commit d3e037a760.
2026-10-05 10:22:25 -04:00
Christopher Sean Morrison ee6e1bebdc write up a preamble for 7.46 2026-10-05 03:31:45 -04:00
Christopher Sean Morrison 9d4f5bb124 consolidate the list a little bit combining related changes and being a little more user-vis critical 2026-10-05 03:21:22 -04:00
Christopher Sean Morrison 21f442a4ad upper on cmd names outside summaries is jarring and inconsistent 2026-10-05 02:11:21 -04:00
Christopher Sean Morrison 24d6f6f4cb add data dir to the doc search paths also 2026-10-05 01:44:15 -04:00
Christopher Sean Morrison d3e037a760 add a test to get regions 2026-10-05 01:27:54 -04:00
Christopher Sean Morrison 5a38423677 release prep for 7.46.0 2026-10-04 23:34:49 -04:00
Christopher Sean Morrison 38113b1de7 nearly ready 2026-10-04 23:34:12 -04:00
Clifford Yapp fec9d07d43 See if we can update CI to macos-15 2026-10-04 15:32:50 -04:00
Clifford Yapp 4b09d6227f Try to shorten some of the NEWS lines 2026-10-02 19:22:07 -04:00
Clifford Yapp eab1e82de2 Update NEWS for post-7.44 user-visible changes
Add missing user-visible release notes from the 7.44-to-7.46
development range.  Remove Itcl 4-only migration fixes, narrow the
MGED editing claim to the ARB5 bug present in 7.44, and correct the
historical PNTS attribution.

Added item to source commit mapping:

- shared OpenGL display-list state: 85de3929e5
- MGED fallback manual viewer: 0a8c6d8bc2, a00efd15c6
- GIST subprocess diagnostics: 0d6cc34a13, 06c8ab6204
- GQA density validation and exit status: 6d142b62e7, cd55562c46
- ARBN inside support: 7b1898d9fd
- HYP edit label: e4d297893e
- MGED BoT face selection: 0173dd8c40
- MGED view-mode panning and edit queries: 5e873e5081
- MGED middle-click rotation behavior: 3beed70eb8
- transactional rejected primitive edits: 13b0699349, 58ab0d5496
- MGED startup prompt focus: 03b36c1b61
- Windows binary temporary files: 7358e90b54
- DSP, PNTS, and BoT ray intersections:
  8f81f27d92, 5c8092b4e6, de09372b73
- submodel transforms: c45fb38aea, 5c6563e2e2
- VOL data lookup and prep handling:
  641a1fea5e, 259e20ac56, e2cdfa3a6a
- MGED NIRT output and subprocess cleanup: 0d6cc34a13
- Windows subprocess argument preservation: bcaebb24e1
- Windows drive-letter paths: d4b902c599, c523397c03
- MGED gqa and rtcheck overlap plots: 75ef43ac37
- procedural tree use-after-free: fa687de934
- gaia Earth generator and textures:
  2f5be2c414, 9e10743773, fee67ac743
- redirected Windows fbcolor input: 01c4a39932
- bwstat median and input errors: 2307575ef1
- NIRT trailing whitespace: b5e03710eb, 730b448973
- GIST hierarchy overflow rendering: 58b3c9c9c0
- MGED gqa option parsing: da430b14d8
- rtxray grayscale output: 4608347774, f9055f240e
- submodel empty-file ASCII conversion: c0059383e0
- PNTS get/adjust/form and ASCII conversion: 71cbe6ed71
- annotation get/adjust and ASCII conversion: 604be9f454

Corrections to existing items:

- remove Itcl 4-only application-fix claims: e78d0530f3
- retain the baseline ARB5 fix but drop pipe and sketch claims:
  df19156341, 6c092182a6, be97d86e4e
- move PNTS conversion support from 7.44 to 7.46: 71cbe6ed71

Reorder the existing facetize and pnts export entries to retain the
newest-first organization of the release section.
2026-10-02 19:10:21 -04:00
Clifford Yapp f7a8d5b406 Fix Edit->Reset menu entry - uses reinit_edit_state() 2026-10-02 13:42:36 -04:00
Clifford Yapp 85de3929e5 Make sure zbuffer and lighting are set up when using share_dlist 2026-10-02 13:09:25 -04:00
Clifford Yapp 0a8c6d8bc2 Update Manual viewer in MGED to use tkhtml3 2026-10-01 15:43:07 -04:00
Clifford Yapp a00efd15c6 Fixes for man page viewer 2026-10-01 15:43:07 -04:00
f4alt 06c8ab6204 more uniform exiting
consolidate the call sites where we end up in bu_exit due to a failure.
Have a simple wrapper so we flush any results buffers that may be useful
for error context, and then call the bu_exit.
2026-10-01 14:34:31 -04:00
f4alt cd55562c46 bubble gqa return code 2026-10-01 14:34:31 -04:00
f4alt 6d142b62e7 validate material IDs are present in density table
previously, positive but not defined in the table values would silently
corrupt the calculation with a -1.0 for the density value and no
warning.
2026-10-01 14:34:31 -04:00
f4alt da2f503367 handle cache compression failures safely
ensure we can compress with bounds checks. If we got something failing
or unsupported, log and return instead of BU_ASSERT (bombing). This
allows the caller to appropriately free the external.
2026-10-01 14:34:31 -04:00
f4alt a9ac66788a guard logging behind debug flag 2026-10-01 14:34:31 -04:00
Clifford Yapp 4cc9f9cf01 Be more nuanced about what ARB edit ops we can support. 2026-10-01 12:10:33 -04:00
Clifford Yapp 7b1898d9fd Add ARBN inside logic. 2026-10-01 12:06:29 -04:00
Clifford Yapp e4d297893e label hyp neck-to-base ratio 2026-10-01 12:00:03 -04:00
Clifford Yapp 0173dd8c40 Correct bot face selection logic 2026-10-01 11:55:30 -04:00
Clifford Yapp cf344d773e Print result in mged (just assigning to interp isn't enough) 2026-10-01 11:14:39 -04:00
Clifford Yapp 8c51c1d527 Mention facetize improvements in NEWS 2026-10-01 08:55:26 -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
125 changed files with 13755 additions and 11996 deletions
+2 -2
View File
@@ -384,7 +384,7 @@ jobs:
osx:
name: macOS Clang
needs: [linux]
runs-on: macos-14
runs-on: macos-15
strategy:
fail-fast: true
steps:
@@ -403,7 +403,7 @@ jobs:
- name: Prepare bext dependencies
uses: ./brlcad/.github/actions/prepare-bext
with:
platform-channel: macos-14-${{ runner.arch }}-release
platform-channel: macos-15-${{ runner.arch }}-release
source-dir: ${{ github.workspace }}/brlcad
output-dir: ${{ runner.temp }}/bext_output
build-type: Release
+1 -1
View File
@@ -177,7 +177,7 @@ jobs:
osx_bext:
name: macOS - BRL-CAD bext build
needs: [linux_bext]
runs-on: macos-14
runs-on: macos-15
strategy:
fail-fast: true
if: ${{ !cancelled() }}
+5 -4
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(42d8d7ca57f9d2f61192b41f0b41a731f1ba6c47)
#---------------------------------------------------------------------
# Define various utilities.
@@ -352,9 +352,10 @@ set(CMAKE_SKIP_BUILD_RPATH FALSE)
# it should only be set to the installation value when actually installed.
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
# Add the automatically determined parts of the RPATH which point to
# directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# Installed runtime paths are defined explicitly below. Do not append
# directories discovered on the build host, since those paths may not exist
# or may resolve to unrelated libraries on the installed system.
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
# The following logic is what allows binaries to run successfully in the build
# directory AND install directory.
+8737 -10014
View File
File diff suppressed because it is too large Load Diff
+39 -5
View File
@@ -151,10 +151,13 @@ recommended for serious developers.
Various useful configurations for working with bext:
* Get a git clone that has everything which might be needed (large,
but useful for offline building)
* Acquire bext and its dependencies from another repository host
git clone --recurse-submodules https://github.com/BRL-CAD/brlcad
cmake .. -DBRLCAD_REPOSITORY_ROOT=https://git.example.org/brlcad
* Acquire bext and its dependencies from local bare Git mirrors
cmake .. "-DBRLCAD_REPOSITORY_ROOT=/path/to/mirror/BRL-CAD"
* Have prebuilt bext outputs to reuse - BRL-CAD will simply copy and prepare
the pre-existing files
@@ -398,10 +401,40 @@ Aliases: ENABLE_RUNTIME_DEBUG, ENABLE_RUN_TIME_DEBUG, ENABLE_RUNTIME_DEBUGGING
ENABLE_RUN_TIME_DEBUGGING
--- BRLCAD_REPOSITORY_ROOT ---
Repository URL or local directory containing bext and its dependency
repositories. Defaults to https://github.com/BRL-CAD. The root directly
contains repositories such as bext.git and astyle.git; BRL-CAD does not
append BRL-CAD to the root.
Remote Git URLs (including SSH), native paths, and file:// URLs are supported.
Relative native paths are resolved against the BRL-CAD source directory.
Quote the entire -D argument for paths containing spaces. The setting is
cached and is also passed as BEXT_REPOSITORY_ROOT when BRL-CAD configures
bext, including when BRLCAD_EXT_SOURCE_DIR supplies an existing checkout.
Alternate roots require bext sources with BEXT_REPOSITORY_ROOT support.
Local roots restrict CMake-driven Git acquisition and queries to file
transport, with no online fallback. Mirrors must contain all required pinned
commits and recursive submodules. Dependencies outside bext's canonical URL
prefix require their own Git URL mappings. This setting does not restrict
network access by dependency build scripts or change persistent Git settings.
BRLCAD_EXT_DIR still selects prebuilt outputs, and changing the repository
root alone does not force those outputs to be rebuilt.
To return to the default source:
cmake .. -DBRLCAD_REPOSITORY_ROOT=https://github.com/BRL-CAD
--- BRLCAD_EXT_DIR ---
If specified, BRL-CAD will look in this directory for bext compiled outputs to
bundle and use during the build.
bundle and use during the build. Relative paths are resolved against the
BRL-CAD source directory. Distcheck subconfigures reuse this resolved output
directory and preserve BRLCAD_REPOSITORY_ROOT, BRLCAD_EXT_SOURCE_DIR, and
BRLCAD_EXT_PARALLEL when specified.
--- BRLCAD_EXT_SOURCE_DIR ---
@@ -409,7 +442,8 @@ bundle and use during the build.
If specified, BRL-CAD will look in this directory for bext source files. Use
this when you have a pre-existing bext clone and don't want BRL-CAD's configure
process to pull down another copy, but are still looking to have the BRL-CAD
configure process manage producing bext build outputs.
configure process manage producing bext build outputs. Relative paths are
resolved against the BRL-CAD source directory.
--- BRLCAD_EXT_PARALLEL ---
+90 -46
View File
@@ -10,80 +10,113 @@ descriptive text that emphasizes or further describes significant
changes made. See document footer for additional details.
----------------------------------------------------------------------
--- 20XX-XX-XX Release 7.46.0 ---
--- 202X-XX-XX Release 7.48.0 ---
----------------------------------------------------------------------
----------------------------------------------------------------------
--- 2026-10-12 Release 7.46.0 ---
----------------------------------------------------------------------
This release expands geometry conversion and improves modeling
reliability. STEP support adds AP203e2, AP214, and AP242
import/export, native CSG round trips, and AP242 product and
manufacturing information (PMI) import. IGES and JT conversion is
expanded, and new OpenSCAD CSG import/export tools are available.
Facetization is more reliable, NMG solid ray tracing is 10-20 times
faster, CSG ray tracing is faster, and BoT, DSP, and point-cloud ray
intersections are corrected. MGED gains numerous fixes to primitive,
sketch, and combination editing, drawing, dialogs, and preferences.
New tools support object nesting with arrange, building generation
with mkbuilding, annotation editing, and primitive repair. Geometry
analysis, image utilities, Windows support, documentation, and bundled
dependencies also receive substantial updates.
* corrected Archer logic for saving window size - Cliff Yapp
* fixed inside BoT rays, DSP surface normals, PNTS bounds - Cliff Yapp
* improved mged fallback HTML manual viewer - Cliff Yapp
* fixed gqa undefined densities and exit status - Chris McGregor
* added ARBN support to the mged inside command - Cliff Yapp
* improved HYP neck-ratio labels and BoT face selection - Cliff Yapp
* fixed mged view panning, edit rotation, & ray queries - Cliff Yapp
* preserved ARB, CLINE, TGC, & extrusion geometry edits - Cliff Yapp
* restored mged command prompt focus at GUI startup - Cliff Yapp
* new documentation on geometry representation schemes - Sean Morrison
* 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
* improved facetize fallback methods and reliability - Cliff Yapp
* 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
* fixed dxf-g invalid polyface index crashes - Meghna45-code, Cliff Yapp
* fixed Archer primitive editors under Itcl 4 - Cliff Yapp
* fixed Archer shutdown menu cleanup errors - Cliff Yapp
* fixed MGED Build Pattern dialog under Itcl 4 - Cliff Yapp
* fixed dxf-g invalid polyface idx crash - Meghna45-code, Cliff Yapp
* fixed TclCAD framebuffer listing and deletion - Cliff Yapp
* fixed MGED Build Pattern progress updates - Cliff Yapp
* fixed MGED search -exec reentry and Ctrl-C crashes - Cliff Yapp
* fixed MGED asynchronous commands and clone progress - Cliff Yapp
* accelerated large and repeated MGED path drawing - Cliff Yapp
* fixed mged Build Pattern progress updates - Cliff Yapp
* fixed mged search -exec reentry and Ctrl-C crashes - Cliff Yapp
* fixed mged asynchronous commands and clone progress - Cliff Yapp
* accelerated large and repeated mged path drawing - Cliff Yapp
* fixed ARB7 inside solid construction - Cliff Yapp
* fixed direct ARB face and edge-direction editing - Cliff Yapp
* fixed rtwizard startup with framebuffer contention - Cliff Yapp
* fixed Archer annotation line display updates - Cliff Yapp
* fixed Archer sketch scale drift in non-mm databases - Cliff Yapp
* fixed toggle arrow callbacks during setup - Cliff Yapp
* fixed Tcl dialogs with multi-word titles and messages - Cliff Yapp
* fixed Tcl dialogs with multi-word titles and msgs - Cliff Yapp
* fixed Archer Tank Wizard gun generation - Cliff Yapp
* fixed MGED and Archer sketch arc tangency editing - Cliff Yapp
* fixed mged and Archer sketch arc tangency editing - Cliff Yapp
* fixed Archer ARB4 point 3 editing - Cliff Yapp
* fixed ARB4 storage from make and mk_arb4 - Cliff Yapp
* fixed ARB4 and ARB6 center point calculations - Cliff Yapp
* fixed concurrent MGED and rtwizard framebuffer use - Cliff Yapp
* fixed MGED ShotVis editing and reload failures - Cliff Yapp
* fixed concurrent mged and rtwizard framebuffer use - Cliff Yapp
* fixed mged ShotVis editing and reload failures - Cliff Yapp
* fixed Tcl widget geometry and selection defects - Cliff Yapp
* fixed MGED BoT editor property handling - Cliff Yapp
* fixed MGED LOD dialog and adaptive BoT wireframes - Cliff Yapp
* fixed MGED Plot, PostScript, and manual launch tools - Cliff Yapp
* fixed MGED Overlap Tool multi-object result handling - Cliff Yapp
* fixed mged BoT editor property handling - Cliff Yapp
* fixed mged LOD dialog and adaptive BoT wireframes - Cliff Yapp
* fixed mged Plot, PostScript, and manual launch tools - Cliff Yapp
* fixed mged Overlap Tool multi-object result handling - Cliff Yapp
* eliminated 16-fold redundant gchecker grid analysis - Cliff Yapp
* fixed Archer tree handling for object names with spaces - Cliff Yapp
* fixed submodel transforms & relative database paths - Sean Morrison
* fixed Archer tree handling for names with spaces - Cliff Yapp
* fixed RtWizard multi-object edge occlusion handling - Cliff Yapp
* fixed RtWizard title, About, and preference handling - Cliff Yapp
* fixed MGED startup file false legacy-syntax warnings - Cliff Yapp
* fixed MGED saved-view deletion and collaboration sync - Cliff Yapp
* fixed MGED font preferences apply and persistence - Cliff Yapp
* fixed mged startup file false legacy-syntax warnings - Cliff Yapp
* fixed mged saved-view deletion and collaboration sync - Cliff Yapp
* fixed mged font preferences apply and persistence - Cliff Yapp
* fixed Archer combination edits changing Boolean trees - Cliff Yapp
* fixed Archer command-line help startup crash - Cliff Yapp
* fixed MGED Geometry Browser menu and close behavior - Cliff Yapp
* fixed MGED collaboration view unshare crash - Cliff Yapp
* added an override for Archer's preference file location - Cliff Yapp
* fixed mged Geometry Browser menu and close behavior - Cliff Yapp
* fixed mged collaboration view unshare crash - Cliff Yapp
* added override for Archer's preference file location - Cliff Yapp
* fixed embedded Archer display and mode menus - Cliff Yapp
* added Archer -h and --help command-line options - Cliff Yapp
* fixed Archer Help opening the documentation menu - Sean Morrison
* added arrange nest command for packing database objects - Sean Morrison
* new mkbuilding tool for parametric/OSM/CityJSON models - Sean Morrison
* added arrange nest command for packing db objects - Sean Morrison
* added mkbuilding for parametric/OSM/CityJSON models - Sean Morrison
* cross-platform BINUNIF and DSP byte-order handling - Sean Morrison
* improved several cv default behaviors (s, 32, 64, n) - Sean Morrison
* improved several cv default behaviors (s,32,64,n) - Sean Morrison
* fixed invalid bot handling bug applying matrices - Sean Morrison
* improved glTF import robustness via gcv - Sean Morrison
* fixed bot dump glTF/GLB export; -F is now rejected - Cliff Yapp
* faster NMG solid ray tracing (10-20x faster) - Sean Morrison
* preserved primitive colors in shaded BoT and B-Rep drawing - Cliff Yapp
* preserved B-Rep attributes through in-place geometry edits - Cliff Yapp
* fixed gcv filter arguments containing one-character values - Cliff Yapp
* preserved prim colors in shaded BoT and B-Rep drawing - Cliff Yapp
* preserved B-Rep attrs through in-place geometry edits - Cliff Yapp
* fixed gcv filter arguments w/ one-character values - Cliff Yapp
* improved iges-g importer robustness - Cliff Yapp
* expanded g-iges native CSG and B-Rep export support - Cliff Yapp
* added IGES import and export support to gcv - Cliff Yapp
* rt -b query option now supports a single value index - Sean Morrison
* rt -b query option now supports a single value idx - Sean Morrison
* fixed .dpix file format portability and icv support - Sean Morrison
* expanded pixscale support for piped image data - Sean Morrison
* fixed HYP volume and ARBN totals reported by analyze - Cliff Yapp
* corrected rtarea sampling and exposed-area calculations - Cliff Yapp
* fixed VOL data-file lookup and missing-data errors - Chris McGregor
* corrected rtarea sampling/exposed-area calculations - Cliff Yapp
* bot split now groups results and preserves mesh data - Cliff Yapp
* bot decimate now preserves normals, UVs, and plate data - Cliff Yapp
* bot decimate now preserves nrmls, UVs, and plate data - Cliff Yapp
* added raytracing support for annotation objects - Cliff Yapp
* fixed MGED X embedded framebuffer display manager crash - Cliff Yapp
* fixed Windows subprocess quoting and empty arguments - Cliff Yapp
* fixed mged X embedded framebuffer display manager crash - Cliff Yapp
* fixed Windows drive-letter path handling - Cliff Yapp
* added annotate command for managing annotation objects - Cliff Yapp
* added repair command for supported invalid primitives - Cliff Yapp
* changed default new-region material code from 1 to 0 - Keith Bowman
@@ -105,26 +138,34 @@ changes made. See document footer for additional details.
* added option validation to image & framebuffer tools - Sean Morrison
* added contributor, API, command, & raytracing guides - Sean Morrison
* added and updated command and library manual pages - Sean Morrison
* added pnts write -f xyz export option - Chris McGregor
* fixed mged ARB5 point editing - Cliff Yapp
* fixed mged gqa and rtcheck stale overlap plots - Cliff Yapp
* fixed procedural tree generator crashes - Sean Morrison, Cliff Yapp
* added gaia Earth models with day/dusk/night textures - Sean Morrison
* fixed fbcolor ramps & Windows input - Cliff Yapp, Sean Morrison
* added parashot gravity trajectory path generator - Sean Morrison
* added metaball point scaling (via metaball_move_pnt) - Sean Morrison
* added metaball pnt scaling (via metaball_move_pnt) - Sean Morrison
* all raytracing apps now report object prep failures - Sean Morrison
* improved fast4-g robustness and error reporting - Cliff Yapp
* improved robustness of g-vrml plate mode handling - Cliff Yapp
* fixed MGED ARB5 point, pipe, and sketch editing - Cliff Yapp
* added pnts write -f xyz export option - Chris McGregor
* added support for tick drawing on MGED model axis - Cliff Yapp
* added support for tick drawing on mged model axis - Cliff Yapp
* added naca456 left and paired wing generation - Cliff Yapp
* added patch-g -j option, specify object thicknesses - Sean Morrison
* fixed fbcolor color ramp bug drawing the last color - Sean Morrison
* corrected bwstat median and input error handling - Sean Morrison
* fixed gist -Q quick preview option - Sean Morrison
* rt preserves eye_pt setting in command scripts - Sean Morrison
* removed trailing whitespace from nirt output - Sean Morrison
* rt -l8 heat graphs now measure CPU time - Sean Morrison
* documented gqa centroid & moments of inertia options - Sean Morrison
* pixdiff now calculates pixel differences on bw files - Sean Morrison
* fixed bwcrop single-axis crops and pixembed edge fill - Sean Morrison
* fixed bwcrop single-axis crops & pixembed edge fill - Sean Morrison
* fixed isize in non-mm databases - Josh Baker
* fixed metaball adjust clearing points on errors - Sean Morrison
* fixed benchmark+pixcmp correctness validation bug - Sean Morrison
* fixed mged gqa option and value parsing - Sean Morrison
* improved gist hierarchy overflow rendering - Chris McGregor
* fixed rtxray -o grayscale/BW output - Sean Morrison
* fbhelp writes help to stdout and rest to stderr - Sean Morrison
* FB now supports /dev/stdout and /dev/stderr devices - Sean Morrison
* fixed mged rt -C + -o background color to file bug - Sean Morrison
@@ -133,7 +174,7 @@ changes made. See document footer for additional details.
* added rtsil -o support for bw output - Sean Morrison
* added cutting plane animation generation to rtwizard - Cliff Yapp
* fixed pixdiff bug on Windows - Sean Morrison
* fixed MGED startup window placement on Windows - Chris McGregor
* fixed mged startup window placement on Windows - Chris McGregor
* fixed pixborder utility right-side edge bug - Sean Morrison
* fixed 'wavelet' image processing tool on Windows - Sean Morrison
* quieted mged killtree spurious lookup failure output - Sean Morrison
@@ -144,14 +185,16 @@ changes made. See document footer for additional details.
* new doc guide, a visual overview of BRL-CAD objects - Sean Morrison
* improved triangle quality produced by bot remesh - Cliff Yapp
* updated numerous manpage command references - Josh Baker
* fixed submodel paths relative to parent databases - Sean Morrison
* improved performance making huge combinations - Sean Morrison
* added PNTS get, adjust, form, & g2asc/asc2g support - Chris McGregor
* fixed ANNOT get, adjust, & g2asc/asc2g support - Chris McGregor
* added make -t primitive type listing - Chris McGregor
* added gcv import conversion support for vrml/wrl 1.0 - Sean Morrison
* fixed gcv appending to existing geometry databases - Sean Morrison
* added CSS names, hex, HSL/HSV to libbu color parsing - Sean Morrison
* improved OpenNURBS B-Rep import and adjustment safety - Cliff Yapp
* improved Debian application menus and MIME associations - Jordi Sayol
* improved Debian application menus/MIME associations - Jordi Sayol
* updated Tcl/Tk dependency to v8.6.18 using bundled Itcl - Cliff Yapp
* updated Clipper2 dependency to v1.5.4 - Cliff Yapp
* updated Eigen dependency to v5.0.1 - Cliff Yapp
* updated GDAL dependency to v3.13.3 - Cliff Yapp
@@ -163,7 +206,6 @@ changes made. See document footer for additional details.
* updated OpenNURBS dependency to v8.34.26223.11001 - Cliff Yapp
* updated PROJ dependency to v9.8.1 - Cliff Yapp
* updated PoissonRecon dependency to v18.75 - Cliff Yapp
* updated Tcl/Tk dependency to v8.6.18, using bundled Itcl - Cliff Yapp
* updated libjpeg-turbo dependency to v3.2.0 - Cliff Yapp
* updated libosmesa dependency to SHA1 bfa90de - Cliff Yapp
* updated libpng dependency to v1.6.58 - Cliff Yapp
@@ -272,7 +314,7 @@ and extensively improving existing docs.
* added metadata-aware image diff & nirt analysis - Cliff Yapp
* added basic PNTS point-cloud ray tracing - Sean Morrison
* improved nirt behavior in Windows bash consoles - Cliff Yapp
* added g2asc/asc2g support for PNTS, DSP, and EBM - Sean Morrison
* added g2asc/asc2g support for DSP and EBM - Sean Morrison
* updated gct mesh decimation dependency to mmesh - Cliff Yapp
* added procedural wing, propeller & tree generators - Cliff Yapp
* added ISO tire dimensions & named tread presets - Cliff Yapp
@@ -544,6 +586,7 @@ for more information.
----------------------------------------------------------------------
--- 2023-11-06 Release 7.38.0 ---
----------------------------------------------------------------------
This major release of BRL-CAD introduces significant improvements to
the robustness of the facetize command by leveraging the Manifold
library for boolean evaluation of meshes. Facetization is an operation
@@ -582,6 +625,7 @@ processing high level assemblies in BRL-CAD's example geometry files:
----------------------------------------------------------------------
--- 2023-07-26 Release 7.36.0 ---
----------------------------------------------------------------------
This major release of BRL-CAD introduces significant updates to
geometry conversion support. The update includes modernization for
NURBS importing and interaction, BoT manipulation, and incorporates
+3 -3
View File
@@ -1,6 +1,6 @@
BRL-CAD
Release 7.44.2
http://brlcad.org/
BRL-CAD
Release 7.46.0
http://brlcad.org/
BRL-CAD is a powerful cross-platform open source combinatorial
solid modeling system that includes an interactive 3D solid geometry
-9
View File
@@ -2668,15 +2668,6 @@ DOCUMENTATION
Comparison of how BRL-CAD overlaps feature-wise with various
major commercial CAD systems.
* Solid Geometry Representation Comparisons
Describe the implications, tradeoffs, and conversion issues
involved when converting between different geometry
representations. Includes describing implicit geometry,
explicit polygonal boundary representations, explicit spline
surface representations, volumetric models, wireframes,
boolean operations, feature edit operations, and parametric
modeling.
* BRL-CAD Open Source
An overview of how BRL-CAD is organized as an open source
project and community describing the developer meritocracy,
+62
View File
@@ -20,6 +20,7 @@ set(
procedural_geometry.adoc
projection_shader.adoc
renderer_showcase.adoc
solid_geometry_comparison.adoc
taxonomy.adoc
tire.adoc
)
@@ -227,6 +228,17 @@ set(
images/renderer_gallery_rthide.png
images/renderer_gallery_rtdepth.png
images/renderer_gallery_rtxray.png
images/solid_rep_csg.png
images/solid_rep_brep_boolean.png
images/solid_rep_brep_evaluated.png
images/solid_rep_polygonal_boolean.png
images/solid_rep_nmg.png
images/solid_rep_bot.png
images/solid_rep_revolve.png
images/solid_rep_voxel_cells.png
images/solid_rep_volume.png
images/solid_rep_point_cloud.png
images/solid_rep_wireframe.png
images/adrt_castle_path.png
images/adrt_castle_rt.png
images/adrt_castle_rt_ao.png
@@ -409,6 +421,54 @@ if(
set_target_properties(renderer-gallery-snapshot PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
# Solid geometry comparison -- opt-in regeneration of committed snapshots.
#
# The article and solid_rep_*.png images are normal committed documentation.
# This target rebuilds the common pawn, derives each representation with the
# built tools, and refreshes the images for review.
option(
BRLCAD_GENERATE_SOLID_REPRESENTATION_COMPARISON
"Regenerate the solid geometry representation comparison snapshots"
OFF
)
if(
BRLCAD_GENERATE_SOLID_REPRESENTATION_COMPARISON
AND TARGET mged
AND TARGET rt
AND TARGET g-voxel
AND TARGET asc2pix
AND TARGET fbclear
AND TARGET plot3-fb
AND TARGET fb-png
)
add_custom_target(
solid-representation-comparison-snapshot
COMMAND
${CMAKE_COMMAND}
-DMGED=$<TARGET_FILE:mged>
-DRT=$<TARGET_FILE:rt>
-DGVOXEL=$<TARGET_FILE:g-voxel>
-DASC2PIX=$<TARGET_FILE:asc2pix>
-DFBCLEAR=$<TARGET_FILE:fbclear>
-DPLOT3FB=$<TARGET_FILE:plot3-fb>
-DFBPNG=$<TARGET_FILE:fb-png>
-DMODEL_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/solid_geometry_comparison/create_pawn.tcl
-DIMGDIR=${CMAKE_CURRENT_SOURCE_DIR}/images
-DWORKDIR=${CMAKE_CURRENT_BINARY_DIR}/solid_representation_comparison_work
-DSIZE=512
-P ${CMAKE_CURRENT_SOURCE_DIR}/solid_geometry_comparison/generate_comparison.cmake
DEPENDS mged rt g-voxel asc2pix fbclear plot3-fb fb-png
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Regenerating solid geometry representation comparison snapshots"
VERBATIM
USES_TERMINAL
)
set_target_properties(
solid-representation-comparison-snapshot
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1
)
endif()
# ADRT path-tracing gallery -- opt-in regeneration of the committed snapshot.
#
# Like the galleries above, the adrt_pathtrace.adoc article is hand-written and
@@ -464,6 +524,8 @@ cmakefiles(
gallery/generate_gallery.cmake
renderer_gallery/renderer_gallery_manifest.cmake
renderer_gallery/generate_renderer_gallery.cmake
solid_geometry_comparison/create_pawn.tcl
solid_geometry_comparison/generate_comparison.cmake
adrt_gallery/generate_adrt_gallery.cmake
gallery/inputs/pipe.stdin
gallery/inputs/wire.stdin
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

@@ -0,0 +1,294 @@
= Solid Geometry Representation Comparisons
:doctype: article
:toc:
:toclevels: 2
The same shape can be stored in very different ways. This comparison uses a
purpose-built chess pawn to show which information BRL-CAD retains, evaluates,
or samples when moving among its principal geometry representations. It is a
guide to semantics and conversion choices, not a claim that one representation
is universally best.
== Common model and method
The reference pawn is a region containing seven overlapping ellipsoids. Their
union is non-trivial enough to exercise curved surfaces, overlapping interiors,
and several component boundaries while remaining reliable across the
conversions shown here. The sketch-and-revolve version independently follows
the same nominal profile; it is not an exact conversion of the seven-piece CSG
model.
Every shaded image uses the same orthographic view, 512x512 image size,
material, and single-threaded renderer. The wireframe uses the same view and
image size. The faceting and sampling settings are intentionally coarse enough
to remain visible. A similar silhouette therefore does not mean that two
database objects have the same structure or numerical boundary.
NOTE: This article and its images are a committed snapshot. Developers can
regenerate the figures from built BRL-CAD tools by configuring with
`-DBRLCAD_GENERATE_SOLID_REPRESENTATION_COMPARISON=ON` and building the
`solid-representation-comparison-snapshot` target.
== At a glance
[cols="2,2,1,2,4",options="header"]
|===
|Form
|BRL-CAD storage
|Solid?
|Boolean state
|Editing and conversion consequences
|Analytic CSG
|Seven `ell` leaves in a combination
|Yes
|Retained
|Compact primitive parameters and tree edits preserve construction intent.
|B-rep tree
|Closed `brep` leaves in a combination
|Yes
|Retained
|NURBS surfaces and trims replace analytic leaves, but the assembly tree remains.
|Evaluated B-rep
|One closed `brep`
|Yes
|Evaluated
|Direct face, edge, trim, and control-point editing is possible; original
features and hierarchy are gone.
|Polygonal tree
|Solid-mode `bot` leaves in a combination
|Yes
|Retained
|Leaf tessellation is approximate, but components and their union tree remain
independently replaceable.
|Evaluated NMG
|One `nmg`
|Yes
|Evaluated
|Stores explicit, topology-rich polygonal boundaries and can record
non-manifold relationships.
|Evaluated triangle mesh
|One solid-mode `bot`
|Yes
|Evaluated
|Portable triangle connectivity, but curved geometry and construction history
have been sampled away.
|Sketch and revolve
|One `sketch` referenced by one `revolve`
|Yes
|Not needed here
|The profile, axis, and sweep angle are compact, meaningful parameters.
|Voxel cells
|A region containing many `arb8` boxes
|Yes (sampled)
|Cell union retained
|Individual cells are ordinary CSG geometry; size grows rapidly as cells
shrink.
|Sampled volume
|A file-backed `vol` scalar grid
|Yes (thresholded)
|None
|Compact regular-grid occupancy; resolution and threshold determine the boundary.
|Point cloud
|One `pnts` object with positions and normals
|No pawn interior
|None
|Useful as measured or sampled evidence, but it has no inherent connected
boundary or inside/outside rule.
|Wireframe
|Derived line display of the CSG object
|No
|Uses source tree
|A view-dependent drawing aid, not an independent solid representation.
|===
"Solid" in this table means that the stored object supplies usable
inside/outside semantics for the pawn. An arbitrary B-rep or mesh is not
automatically a solid: its boundary must be closed and consistently oriented.
The point size used to render `pnts` makes each sample a small sphere, but those
spheres do not reconstruct the pawn interior. A wireframe likewise does not
become a solid merely because its curves appear closed in one view.
== Implicit and parametric forms
[cols="1,1"]
[%noheader]
|===
|image:images/solid_rep_csg.png[Analytic CSG pawn,512]
|image:images/solid_rep_revolve.png[Sketch and revolve pawn,512]
|*Analytic CSG.* Native primitives retain their equations and dimensions; the
region evaluates their union during ray tracing.
|*Sketch plus revolve.* A closed line-segment profile is swept through 360
degrees. The stepped profile is intentional and remains editable in two
dimensions.
|===
Analytic CSG is an implicit representation: intersections with ideal primitive
surfaces are computed as needed rather than read from a stored boundary mesh.
It is usually the most compact form here and makes edits such as changing the
head radius or stem proportions direct. Its Boolean hierarchy also records why the
shape was assembled.
`sketch` plus `revolve` is another procedural, parameterized description. It
works especially well for an axisymmetric part, although a sketch with only
line segments produces conical and cylindrical bands rather than the pawn's
original ellipsoidal surfaces. BRL-CAD supports primitive parameters, placement
matrices, sketches, and sweeps; that should not be confused with a general
constraint solver or an associative feature-history system.
== Spline boundary representations
[cols="1,1"]
[%noheader]
|===
|image:images/solid_rep_brep_boolean.png[B-rep leaves with Boolean tree,512]
|image:images/solid_rep_brep_evaluated.png[Single evaluated B-rep,512]
|*B-rep with Booleans.* Each analytic leaf becomes a closed B-rep whose
boundaries are trimmed NURBS surfaces. The original union tree is retained.
|*B-rep without Booleans.* Boolean evaluation produces one explicit boundary;
operand identities and the construction tree no longer exist as editable
features.
|===
A B-rep stores faces, trims, edges, vertices, and their topology explicitly.
Rational NURBS can represent the source quadrics exactly as surfaces, but that
does not make every CSG-to-B-rep conversion exact: intersections, trimming,
topology classification, and repair use numerical tolerances. Keeping a tree
of B-rep leaves isolates conversion to each primitive and retains high-level
editing. Evaluating the tree yields a more self-contained boundary for
exchange and face-level work, at the cost of that history. This snapshot's
evaluated result passes both `brep ... valid` and `brep ... solid`; conversion
results should be checked rather than assumed to be closed.
== Polygonal boundaries
[cols="1,1,1"]
[%noheader]
|===
|image:images/solid_rep_polygonal_boolean.png[BoT leaves with Boolean tree,512]
|image:images/solid_rep_nmg.png[Evaluated NMG pawn,512]
|image:images/solid_rep_bot.png[Evaluated BoT pawn,512]
|*Polygonal with Booleans.* Each leaf is independently tessellated to a BoT,
then placed in the original union tree.
|*Evaluated NMG.* One non-manifold geometry object records the evaluated
polygonal boundary and its richer topology.
|*Evaluated triangle mesh.* One solid, counter-clockwise BoT stores 982
vertices and 1,960 triangular faces in this snapshot.
|===
All three images expose the same coarse tessellation. Their similar appearance
hides an important distinction: the first still has seven editable operands,
whereas the NMG and single BoT are evaluated results. NMG can represent
polygonal faces and non-manifold adjacency; BoT faces are triangles only.
Neither recreates analytic curvature when converted back. Finer tessellation
reduces visible error but increases storage and processing cost.
== Sampled forms
[cols="1,1,1"]
[%noheader]
|===
|image:images/solid_rep_voxel_cells.png[Pawn represented by CSG voxel cells,512]
|image:images/solid_rep_volume.png[Pawn represented by a VOL grid,512]
|image:images/solid_rep_point_cloud.png[Pawn represented by surface points,512]
|*Explicit voxel cells.* `voxelize` tests 3 mm cells and creates a CSG union of
699 boxes for cells exceeding the occupancy threshold.
|*Sampled volume.* `g-voxel` samples a 2 mm grid; a file-backed `vol` interprets
the resulting 17x17x30 byte array through an occupancy window.
|*Point cloud.* `pnts gen` samples the analytic surface on a 2 mm grid. Small
rendered spheres make the otherwise zero-area sample locations visible.
|===
The two voxel images are deliberately separate. The `voxelize` command emits
ordinary box primitives and a combination, while `vol` reads scalar samples on
a regular grid and derives a threshold boundary at ray-trace time. Both are
volumetric approximations whose accuracy, memory, and feature size depend on
cell spacing. A point cloud samples only the surface and has no connectivity;
surface reconstruction requires additional assumptions and may invent or omit
geometry.
== Wireframe display
image::images/solid_rep_wireframe.png[Wireframe display of the analytic CSG pawn,512]
This wireframe is a view-plane plot of MGED's unevaluated display of the
analytic CSG pawn; hidden lines are not removed. Wireframes are useful for
selection, orientation, and seeing component curves, but they are ambiguous:
curves alone do not say which side is material, which loops bound faces, or
which surfaces enclose volume. Keep the solid that generated the display
rather than treating the drawing as an equivalent conversion target.
== Conversion and editing consequences
A conversion command is not an equivalence guarantee. Before replacing source
data, check at least these effects:
* *Tolerance and resolution:* B-rep Boolean evaluation uses intersection and
topology tolerances; tessellation uses chord and angular tolerances; voxels
and points use sample spacing and thresholds. Each can erase a feature
smaller than its controlling tolerance.
* *Construction intent:* evaluating a Boolean tree discards operand identity,
primitive parameters, and ordering. Converting leaves while retaining the
tree keeps more of that intent.
* *Topology:* a visually plausible B-rep or mesh may contain gaps, duplicate
faces, self-intersections, or inconsistent orientation. Validate closure
and ray behavior, not only a shaded image.
* *Attributes:* collapsing a hierarchy may change where region identifiers,
materials, colors, and other attributes reside. Verify them separately
from geometry.
* *Round trips:* CSG to mesh to B-rep does not recover the original equations;
points to a surface requires reconstruction; resampling a volume compounds
discretization. Retain the highest-level trustworthy source.
For parameter changes and repeated design work, prefer analytic primitives,
sketch sweeps, and preserved combination trees. Prefer a validated B-rep when
trimmed-surface topology or exchange requires it. Use NMG for topology-rich
polygonal processing and BoT for triangle-oriented interchange or algorithms.
Use voxels for occupancy operations and point clouds for measured or sampled
data, not as silent substitutes for a watertight solid.
BRL-CAD also supports specialized forms such as DSP height fields, EBM
extrusions, ARS surfaces, metaballs, and legacy `poly` and `bspline` objects.
They are not shown as interchangeable pawn encodings: DSP and EBM are
domain-specific, metaballs change the modeling method, and BoT and B-rep are the
preferred general successors to the legacy polygon and spline forms.
== Reproducing the conversions
The snapshot generator creates the database and runs these principal MGED
operations (names shortened here only where noted):
[source]
----
brep comparison.csg.r brep --no-evaluation
brep comparison.csg.r brep comparison.brep_evaluated.s
facetize -n comparison.csg.r comparison.nmg.s
facetize --methods NMG comparison.csg.r comparison.bot.s
facetize --methods NMG base.s base.bot # repeated for all seven leaves
voxelize -s "3 3 3" -d 4 -t 0.45 comparison.voxel_cells.r comparison.csg.r
pnts gen -t 2 --surface --grid comparison.csg.r comparison.samples
pnts write comparison.samples comparison.xyz
pnts read -f xyzijk --size 0.45 comparison.xyz comparison.point_cloud.s
----
The generator also runs `g-voxel` and expands its sparse occupied-cell report
into the x-fastest byte array used by `vol`. It then renders every shaded
object with `rt -B -M -P1` and the saved view; MGED's `plot -2d`, `plot3-fb`,
and `fb-png` produce the wireframe image. See
`solid_geometry_comparison/generate_comparison.cmake` and
`solid_geometry_comparison/create_pawn.tcl` for the complete reproducible
inputs.
@@ -0,0 +1,24 @@
# Create the shared pawn used by the solid-representation comparison.
title {Solid geometry representation comparison pawn}
units mm
# Deliberate overlaps keep the pawn connected while exercising curved-surface
# intersections during Boolean evaluation.
db put base.s ell V {0 0 4.5} A {17 0 0} B {0 17 0} C {0 0 4.5}
db put base_bead.s ell V {0 0 9.5} A {14 0 0} B {0 14 0} C {0 0 5}
db put body.s ell V {0 0 18} A {12 0 0} B {0 12 0} C {0 0 9}
db put stem.s ell V {0 0 29} A {7 0 0} B {0 7 0} C {0 0 13}
db put collar.s ell V {0 0 39} A {9 0 0} B {0 9 0} C {0 0 3.5}
db put neck.s ell V {0 0 43} A {6.5 0 0} B {0 6.5 0} C {0 0 5}
db put head.s ell V {0 0 50} A {9 0 0} B {0 9 0} C {0 0 9}
db put comparison.csg.r comb region yes id 1000 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {u {u {u {l base.s} {l base_bead.s}} {u {l body.s} {l stem.s}}} {u {u {l collar.s} {l neck.s}} {l head.s}}}
# The revolve uses a deliberately independent outline. Its single closed
# sketch approximates the same pawn silhouette without copying the CSG tree.
db put comparison.profile sketch V {0 0 0} A {1 0 0} B {0 0 1} VL {{0 0} {17 0} {17 2} {16.3 4} {15 6} {14 8} {15.4 7} {16 9} {15.4 11} {13.5 12.5} {11.2 14} {12 18} {11 21} {9.5 23} {8 26} {7.5 32} {6.5 36} {7.5 37} {9 39} {7.5 41} {6.5 42} {6.5 43} {7.5 44} {8.5 46} {9 50} {8.5 54} {6.7 56.5} {0 59}} SL {{line S 0 E 1} {line S 1 E 2} {line S 2 E 3} {line S 3 E 4} {line S 4 E 5} {line S 5 E 6} {line S 6 E 7} {line S 7 E 8} {line S 8 E 9} {line S 9 E 10} {line S 10 E 11} {line S 11 E 12} {line S 12 E 13} {line S 13 E 14} {line S 14 E 15} {line S 15 E 16} {line S 16 E 17} {line S 17 E 18} {line S 18 E 19} {line S 19 E 20} {line S 20 E 21} {line S 21 E 22} {line S 22 E 23} {line S 23 E 24} {line S 24 E 25} {line S 25 E 26} {line S 26 E 27} {line S 27 E 0}}
db put comparison.revolve.s revolve V {0 0 0} axis {0 0 1} R {1 0 0} ang 6.283185307179586 sk_name comparison.profile
db put comparison.revolve.r comb region yes id 1001 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.revolve.s}
q
@@ -0,0 +1,372 @@
# Regenerate the solid geometry representation comparison snapshots.
#
# The article is hand-written. This script builds its common pawn model,
# derives each representation using BRL-CAD tools, and replaces the committed
# images. All databases and sidecar data remain in WORKDIR.
cmake_minimum_required(VERSION 3.20)
foreach(v MGED RT GVOXEL ASC2PIX FBCLEAR PLOT3FB FBPNG
MODEL_SCRIPT IMGDIR WORKDIR)
if(NOT DEFINED ${v})
message(FATAL_ERROR "generate_comparison: -D${v}=... is required")
endif()
endforeach()
if(NOT DEFINED SIZE)
set(SIZE 512)
endif()
set(RENDER_TIMEOUT 1800)
set(PAWN_DB "${WORKDIR}/solid_representation_pawn.g")
set(VIEW_SCRIPT "${WORKDIR}/pawn.view")
set(MGED_SCRIPT "${WORKDIR}/assemble_variants.tcl")
set(VOXELIZE_SCRIPT "${WORKDIR}/voxelize.tcl")
set(VOXEL_LIST "${WORKDIR}/voxels1.txt")
set(VOLUME_ASCII "${WORKDIR}/comparison_volume.asc")
set(VOLUME_FILE "${WORKDIR}/comparison.vol")
file(REMOVE_RECURSE "${WORKDIR}")
file(MAKE_DIRECTORY "${WORKDIR}")
file(MAKE_DIRECTORY "${IMGDIR}")
function(run_process label)
execute_process(
COMMAND ${ARGN}
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
RESULT_VARIABLE process_result
OUTPUT_VARIABLE process_stdout
ERROR_VARIABLE process_stderr
)
if(NOT process_result EQUAL 0)
message(
FATAL_ERROR
"${label} failed (exit ${process_result})\n${process_stdout}${process_stderr}"
)
endif()
message(STATUS "${label}")
endfunction()
function(require_brep_property object property expected_output)
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}" brep "${object}" "${property}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
RESULT_VARIABLE property_result
OUTPUT_VARIABLE property_stdout
ERROR_VARIABLE property_stderr
)
set(property_output "${property_stdout}${property_stderr}")
if(NOT property_result EQUAL 0 OR NOT property_output MATCHES "${expected_output}")
message(
FATAL_ERROR
"B-rep ${object} is not ${property}\n${property_output}"
)
endif()
message(STATUS "Validated B-rep ${object}: ${property}")
endfunction()
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${MODEL_SCRIPT}"
RESULT_VARIABLE create_result
OUTPUT_VARIABLE create_stdout
ERROR_VARIABLE create_stderr
)
if(NOT create_result EQUAL 0 OR NOT EXISTS "${PAWN_DB}")
message(
FATAL_ERROR
"Creating the comparison pawn failed (exit ${create_result})\n${create_stdout}${create_stderr}"
)
endif()
# Preserve one Boolean hierarchy as B-rep leaves, and evaluate another into a
# single B-rep. The default suffix is intentional: supplying an explicit
# suffix changes parsing in older MGED versions.
set(PAWN_LEAVES base base_bead body stem collar neck head)
run_process(
"Created Boolean tree of B-rep leaves"
"${MGED}" --no-rc -c "${PAWN_DB}" brep comparison.csg.r brep --no-evaluation
)
foreach(leaf IN LISTS PAWN_LEAVES)
require_brep_property("${leaf}.s.brep" solid "brep is solid")
endforeach()
run_process(
"Created evaluated B-rep"
"${MGED}" --no-rc -c "${PAWN_DB}" brep comparison.csg.r brep comparison.brep_evaluated.s
)
require_brep_property(comparison.brep_evaluated.s valid "brep is valid")
require_brep_property(comparison.brep_evaluated.s solid "brep is solid")
# Generate both the topology-rich NMG form and the triangle-only BoT form.
run_process(
"Created evaluated NMG"
"${MGED}" --no-rc -c "${PAWN_DB}" facetize -n comparison.csg.r comparison.nmg.s
)
run_process(
"Created evaluated BoT"
"${MGED}" --no-rc -c "${PAWN_DB}" facetize --methods NMG comparison.csg.r comparison.bot.s
)
# Tessellating leaves independently preserves the editable Boolean hierarchy.
foreach(leaf IN LISTS PAWN_LEAVES)
run_process(
"Tessellated ${leaf}.s"
"${MGED}" --no-rc -c "${PAWN_DB}" facetize --methods NMG "${leaf}.s" "${leaf}.bot"
)
endforeach()
file(WRITE "${VOXELIZE_SCRIPT}" "voxelize -s {3 3 3} -d 4 -t 0.45 comparison.voxel_cells.r comparison.csg.r\nq\n")
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${VOXELIZE_SCRIPT}"
RESULT_VARIABLE voxelize_result
OUTPUT_VARIABLE voxelize_stdout
ERROR_VARIABLE voxelize_stderr
)
if(NOT voxelize_result EQUAL 0)
message(
FATAL_ERROR
"Creating explicit voxel cells failed (exit ${voxelize_result})\n${voxelize_stdout}${voxelize_stderr}"
)
endif()
message(STATUS "Created explicit voxel cells")
# PNTS stores point size at import time. The generator first writes all
# sampled fields (xyzijk), then reads them back with a visible point diameter.
run_process(
"Sampled pawn surface"
"${MGED}" --no-rc -c "${PAWN_DB}" pnts gen -t 2 --surface --grid
comparison.csg.r comparison.samples
)
run_process(
"Exported pawn point samples"
"${MGED}" --no-rc -c "${PAWN_DB}" pnts write comparison.samples comparison.xyz
)
run_process(
"Created visible point cloud"
"${MGED}" --no-rc -c "${PAWN_DB}" pnts read -f xyzijk --size 0.45
comparison.xyz comparison.point_cloud.s
)
# g-voxel reports occupied cells. Expand that sparse report into the regular,
# x-fastest byte array required by VOL. The pawn bounds are [-17,17] in X/Y
# and [0,60] in Z; 2 mm cells therefore form a 17 x 17 x 30 grid.
file(REMOVE "${VOXEL_LIST}")
run_process(
"Sampled VOL grid"
"${GVOXEL}" -s "2 2 2" -d 4 -t 0.5 "${PAWN_DB}" comparison.csg.r
)
if(NOT EXISTS "${VOXEL_LIST}")
message(FATAL_ERROR "g-voxel did not create ${VOXEL_LIST}")
endif()
file(STRINGS "${VOXEL_LIST}" voxel_lines)
list(LENGTH voxel_lines occupied_voxels)
if(occupied_voxels EQUAL 0)
message(FATAL_ERROR "g-voxel reported no occupied cells")
endif()
foreach(line IN LISTS voxel_lines)
string(
REGEX MATCH
"^\\((-?[0-9]+)\\.0+, (-?[0-9]+)\\.0+, (-?[0-9]+)\\.0+\\)[ \t]+[^ \t]+[ \t]+[0-9.]+$"
parsed "${line}"
)
if(NOT parsed)
message(FATAL_ERROR "Cannot parse g-voxel output: ${line}")
endif()
set(x_coord "${CMAKE_MATCH_1}")
set(y_coord "${CMAKE_MATCH_2}")
set(z_coord "${CMAKE_MATCH_3}")
math(EXPR x_index "(${x_coord} + 16) / 2")
math(EXPR y_index "(${y_coord} + 16) / 2")
math(EXPR z_index "(${z_coord} - 1) / 2")
if(x_index LESS 0 OR x_index GREATER 16 OR
y_index LESS 0 OR y_index GREATER 16 OR
z_index LESS 0 OR z_index GREATER 29)
message(FATAL_ERROR "g-voxel coordinate outside the expected pawn grid: ${line}")
endif()
set("voxel_${x_index}_${y_index}_${z_index}" 255)
endforeach()
set(volume_text "")
foreach(z_index RANGE 0 29)
foreach(y_index RANGE 0 16)
foreach(x_index RANGE 0 16)
if(DEFINED voxel_${x_index}_${y_index}_${z_index})
set(sample ff)
else()
set(sample 00)
endif()
string(APPEND volume_text "${sample}\n")
endforeach()
endforeach()
endforeach()
file(WRITE "${VOLUME_ASCII}" "${volume_text}")
execute_process(
COMMAND "${ASC2PIX}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${VOLUME_ASCII}"
OUTPUT_FILE "${VOLUME_FILE}"
RESULT_VARIABLE asc2pix_result
ERROR_VARIABLE asc2pix_error
)
if(NOT asc2pix_result EQUAL 0)
message(FATAL_ERROR "asc2pix failed (exit ${asc2pix_result})\n${asc2pix_error}")
endif()
file(SIZE "${VOLUME_FILE}" volume_size)
if(NOT volume_size EQUAL 8670)
message(FATAL_ERROR "VOL sidecar is ${volume_size} bytes; expected 8670")
endif()
message(STATUS "Encoded ${occupied_voxels} occupied VOL cells")
# Wrap single primitives in regions so every shaded comparison uses the same
# material. The polygonal tree repeats the original CSG union hierarchy with
# BoT leaves. VOL references a sidecar basename resolved beside the database.
file(WRITE "${MGED_SCRIPT}" [=[
db put comparison.brep_evaluated.r comb region yes id 1010 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.brep_evaluated.s}
db put comparison.nmg.r comb region yes id 1020 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.nmg.s}
db put comparison.bot.r comb region yes id 1030 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.bot.s}
db put comparison.polygonal_boolean.r comb region yes id 1040 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {u {u {u {l base.bot} {l base_bead.bot}} {u {l body.bot} {l stem.bot}}} {u {u {l collar.bot} {l neck.bot}} {l head.bot}}}
attr set comparison.voxel_cells.r region R region_id 1050 material_id 1 los 100 shader plastic color 235/190/90
db put comparison.point_cloud.r comb region yes id 1060 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.point_cloud.s}
db put comparison.volume.s vol file {comparison.vol} name {comparison.vol} src f w 17 n 17 d 30 lo 1 hi 255 size {2 2 2} mat {1 0 0 -17 0 1 0 -17 0 0 1 0 0 0 0 1}
db put comparison.volume.r comb region yes id 1070 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.volume.s}
q
]=])
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${MGED_SCRIPT}"
RESULT_VARIABLE assemble_result
OUTPUT_VARIABLE assemble_stdout
ERROR_VARIABLE assemble_stderr
)
if(NOT assemble_result EQUAL 0)
message(
FATAL_ERROR
"Assembling comparison regions failed (exit ${assemble_result})\n${assemble_stdout}${assemble_stderr}"
)
endif()
file(WRITE "${VIEW_SCRIPT}" "viewsize 8.000000000000000e+01;\n")
file(APPEND "${VIEW_SCRIPT}" "orientation 2.480970000000000e-01 4.765910000000000e-01 7.480970000000000e-01 3.894350000000000e-01;\n")
file(APPEND "${VIEW_SCRIPT}" "eye_pt 4.199070000000000e+01 2.940060000000000e+01 5.329140000000000e+01;\n")
file(APPEND "${VIEW_SCRIPT}" "start 0; clean;\n")
file(APPEND "${VIEW_SCRIPT}" "end;\n")
function(copy_snapshot local_image)
if(NOT EXISTS "${local_image}")
message(FATAL_ERROR "Expected image was not created: ${local_image}")
endif()
file(SIZE "${local_image}" image_size)
if(image_size EQUAL 0)
message(FATAL_ERROR "Rendered image is empty: ${local_image}")
endif()
file(COPY "${local_image}" DESTINATION "${IMGDIR}")
get_filename_component(image_name "${local_image}" NAME)
file(
CHMOD "${IMGDIR}/${image_name}"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)
endfunction()
function(render_snapshot name object)
set(filename "solid_rep_${name}.png")
set(local_image "${WORKDIR}/${filename}")
file(REMOVE "${local_image}")
execute_process(
COMMAND "${RT}" -B -M -W "-s${SIZE}" -P1 -o "${filename}" "${PAWN_DB}" "${object}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${VIEW_SCRIPT}"
RESULT_VARIABLE render_result
OUTPUT_VARIABLE render_stdout
ERROR_VARIABLE render_stderr
)
if(NOT render_result EQUAL 0)
message(
FATAL_ERROR
"Rendering ${name} failed (exit ${render_result})\n${render_stdout}${render_stderr}"
)
endif()
copy_snapshot("${local_image}")
message(STATUS "Rendered ${filename}")
endfunction()
render_snapshot(csg comparison.csg.r)
render_snapshot(brep_boolean comparison.csg.r.brep)
render_snapshot(brep_evaluated comparison.brep_evaluated.r)
render_snapshot(polygonal_boolean comparison.polygonal_boolean.r)
render_snapshot(nmg comparison.nmg.r)
render_snapshot(bot comparison.bot.r)
render_snapshot(revolve comparison.revolve.r)
render_snapshot(voxel_cells comparison.voxel_cells.r)
render_snapshot(volume comparison.volume.r)
render_snapshot(point_cloud comparison.point_cloud.r)
# MGED exports its current wireframe display as view-plane plot3. Rasterize
# that plot through a disk framebuffer so regeneration needs no display.
set(WIREFRAME_PLOT "${WORKDIR}/solid_rep_wireframe.plot3")
set(WIREFRAME_PNG "${WORKDIR}/solid_rep_wireframe.png")
set(WIREFRAME_FB_NAME "solid_rep_wireframe.fb")
set(WIREFRAME_SCRIPT "${WORKDIR}/wireframe.tcl")
file(REMOVE "${WIREFRAME_PLOT}" "${WORKDIR}/${WIREFRAME_FB_NAME}" "${WIREFRAME_PNG}")
file(WRITE "${WIREFRAME_SCRIPT}" [=[
draw comparison.csg.r
size 80
orientation 0.248097 0.476591 0.748097 0.389435
eye_pt 41.9907 29.4006 53.2914
plot -2d solid_rep_wireframe.plot3
q
]=])
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${WIREFRAME_SCRIPT}"
RESULT_VARIABLE wireframe_result
OUTPUT_VARIABLE wireframe_stdout
ERROR_VARIABLE wireframe_stderr
)
if(NOT wireframe_result EQUAL 0 OR NOT EXISTS "${WIREFRAME_PLOT}")
message(
FATAL_ERROR
"MGED wireframe export failed (exit ${wireframe_result})\n${wireframe_stdout}${wireframe_stderr}"
)
endif()
message(STATUS "Exported wireframe plot")
run_process(
"Cleared wireframe framebuffer"
"${FBCLEAR}" -c -F "${WIREFRAME_FB_NAME}" -w "${SIZE}" -n "${SIZE}" 24 24 24
)
execute_process(
COMMAND "${PLOT3FB}" -F "${WIREFRAME_FB_NAME}" -o -w "${SIZE}" -n "${SIZE}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${WIREFRAME_PLOT}"
RESULT_VARIABLE plot_result
OUTPUT_VARIABLE plot_stdout
ERROR_VARIABLE plot_stderr
)
if(NOT plot_result EQUAL 0)
message(FATAL_ERROR "plot3-fb failed (exit ${plot_result})\n${plot_stdout}${plot_stderr}")
endif()
run_process(
"Rasterized wireframe snapshot"
"${FBPNG}" -F "${WIREFRAME_FB_NAME}" -w "${SIZE}" -n "${SIZE}"
"solid_rep_wireframe.png"
)
copy_snapshot("${WIREFRAME_PNG}")
message(STATUS "Solid representation comparison: 11 snapshots regenerated")
+49 -11
View File
@@ -200,17 +200,55 @@ ____
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) appends
one high-precision rtcmp JSON record per ray with final hit partitions to the
file named by LIBRT_RTCMP_FILE. Misses produce no record. librt never clears
the 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. 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 the environment variable LIBRT_RTCMP_PRIMITIVES
is set to 1.
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.
+1
View File
@@ -22,4 +22,5 @@ Task-focused guides and how-to articles.
* link:articles/procedural_geometry.html[BRL-CAD Procedural Geometry Gallery]
* link:articles/projection_shader.html[Using the Projection Shader]
* link:articles/renderer_showcase.html[BRL-CAD Renderer Showcase]
* link:articles/solid_geometry_comparison.html[Solid Geometry Representation Comparisons]
* link:articles/tire.html[Vehicle Tire and Wheel Creation in BRL-CAD]
-1
View File
@@ -61,5 +61,4 @@ Author: Paul Mackerras (paulus@cs.anu.edu.au),
file:/src/libtclcad/tkImgFmtPIX.c
file:/src/tclscripts/cad_clrpick.tcl
file:/src/tclscripts/html_library.tcl
+1 -1
View File
@@ -1 +1 @@
45
47
+1 -1
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_RTCMP 0x10000000 /**< @brief 29 Append rays to LIBRT_RTCMP_FILE; LIBRT_RTCMP_PRIMITIVES=1 includes primitive segments */
#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 */
+6 -4
View File
@@ -510,7 +510,9 @@ rt_knob_edit_sca(
int matrix_edit);
/* Equivalent to sedit - run editing logic after input data is set in
* rt_edit container. Returns BRLCAD_ERROR if the primitive edit fails. */
* 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);
@@ -532,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.
*/
@@ -543,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.
*/
+9
View File
@@ -55,6 +55,15 @@
__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);
+62 -38
View File
@@ -30,6 +30,7 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include(CMakeParseArguments)
include("${CMAKE_CURRENT_LIST_DIR}/BRLCAD_Repository.cmake")
# Try to locate a usable git executable. On Windows, Visual Studio may
# provide git in non-standard locations; try common Program Files and
@@ -88,6 +89,15 @@ function(brlcad_bext_init BEXT_SHA1)
set(BRLCAD_EXT_DIR "${BRLCAD_EXT_DIR_ENV}")
endif(BRLCAD_EXT_DIR_ENV AND NOT DEFINED BRLCAD_EXT_DIR)
# Resolve paths before probing them so later subconfigures use the same
# directories even when launched from a different working directory.
foreach(_brlcad_ext_path_var BRLCAD_EXT_DIR BRLCAD_EXT_SOURCE_DIR)
if(DEFINED ${_brlcad_ext_path_var} AND NOT "${${_brlcad_ext_path_var}}" STREQUAL "")
get_filename_component(${_brlcad_ext_path_var} "${${_brlcad_ext_path_var}}" ABSOLUTE BASE_DIR "${CMAKE_SOURCE_DIR}")
set(${_brlcad_ext_path_var} "${${_brlcad_ext_path_var}}" PARENT_SCOPE)
endif()
endforeach()
set(_brlcad_ext_dir_specified FALSE)
set(_brlcad_ext_dir_managed FALSE)
@@ -148,14 +158,14 @@ function(brlcad_bext_init BEXT_SHA1)
elseif(NOT _brlcad_ext_dir_specified)
message(
WARNING
"External dependencies will be downloaded, configured, and built automatically as-needed. Set BRLCAD_EXT_DIR to specify instead."
"External dependencies will be acquired from ${_BRLCAD_REPOSITORY_ROOT}, configured, and built automatically as-needed. Set BRLCAD_EXT_DIR to specify instead."
)
endif()
message(
STATUS
"BRLCAD_EXT_DIR specifies prebuilt external dependencies directory\n"
"containing 'install' and 'noinstall' folders, outputs from installing\n"
"https://github.com/BRL-CAD/bext.\n"
"${_BRLCAD_BEXT_REPOSITORY}.\n"
)
endif(NOT BRLCAD_EXT_NOINSTALL_DIR OR NOT BRLCAD_EXT_INSTALL_DIR)
@@ -211,28 +221,24 @@ function(brlcad_rel_version BRLCAD_REL)
set(${BRLCAD_REL} "rel-${VMAJOR}-${VMINOR}-${VPATCH}" PARENT_SCOPE)
endfunction()
# Look up branch or tag SHA1 hashes from primary github.com repo
# Branch/tag queries are advisory; an unavailable source cannot validate them.
function(remote_sha1 SHA1_VAR BRANCH)
set(${SHA1_VAR} "" PARENT_SCOPE)
if(NOT GIT_EXEC)
set(${SHA1_VAR} "" PARENT_SCOPE)
return()
endif()
# This needs a working internet connection to succeed (and GitHub
# must be up and working as well.)
brlcad_repository_git_command(git_command)
execute_process(
COMMAND ${GIT_EXEC} ls-remote https://github.com/BRL-CAD/bext.git ${BRANCH}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE LS_REMOTE_STATUS
OUTPUT_VARIABLE LS_REMOTE_STR
ERROR_VARIABLE LS_REMOTE_STR
)
if(NOT LS_REMOTE_STATUS AND LS_REMOTE_STR)
string(REPLACE " " ";" LS_REMOTE_STR "${LS_REMOTE_STR}")
string(REPLACE "\t" ";" LS_REMOTE_STR "${LS_REMOTE_STR}")
list(GET LS_REMOTE_STR 0 REMOTE_SHA1)
set(${SHA1_VAR} "${REMOTE_SHA1}" PARENT_SCOPE)
else()
set(${SHA1_VAR} "" PARENT_SCOPE)
COMMAND "${CMAKE_COMMAND}" -E env GIT_TERMINAL_PROMPT=0 --
${git_command} -c credential.helper= -c credential.interactive=false
ls-remote "${_BRLCAD_BEXT_REPOSITORY}" "${BRANCH}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE git_result
OUTPUT_VARIABLE remote_refs
ERROR_QUIET
)
if(git_result STREQUAL "0" AND remote_refs MATCHES "^([0-9a-f]+)[ \t]")
set(${SHA1_VAR} "${CMAKE_MATCH_1}" PARENT_SCOPE)
endif()
endfunction()
@@ -367,24 +373,29 @@ function(setup_bext_dir)
# no pre-existing sources, so we're cloning into the build dir
set(BRLCAD_EXT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/bext")
# Write current SHA1 for subsequent processing
brlcad_repository_git_command(git_command)
message(STATUS "Cloning bext from ${_BRLCAD_BEXT_REPOSITORY}")
execute_process(
COMMAND ${git_command} clone "${_BRLCAD_BEXT_REPOSITORY}" "${BRLCAD_EXT_SOURCE_DIR}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
RESULT_VARIABLE git_result
)
if(NOT git_result STREQUAL "0")
message(FATAL_ERROR "Unable to clone bext from ${_BRLCAD_BEXT_REPOSITORY} (${git_result})")
endif()
execute_process(
COMMAND ${git_command} -c advice.detachedHead=false checkout "${BEXT_SHA1}"
WORKING_DIRECTORY "${BRLCAD_EXT_SOURCE_DIR}"
RESULT_VARIABLE git_result
)
if(NOT git_result STREQUAL "0")
message(FATAL_ERROR "Unable to check out pinned bext commit ${BEXT_SHA1} from ${_BRLCAD_BEXT_REPOSITORY} (${git_result}); ensure the repository contains this commit")
endif()
# Record the pin only after checkout succeeds.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bext.sha1" "${BEXT_SHA1}")
distclean("${CMAKE_CURRENT_BINARY_DIR}/bext.sha1")
# Clone
message("BRL-CAD bext clone command: ${GIT_EXEC} clone https://github.com/BRL-CAD/bext.git")
execute_process(
COMMAND ${GIT_EXEC} clone https://github.com/BRL-CAD/bext.git
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
message("BRL-CAD bext checkout command: ${GIT_EXEC} -c advice.detachedHead=false checkout ${BEXT_SHA1}")
execute_process(
COMMAND ${GIT_EXEC} -c advice.detachedHead=false checkout ${BEXT_SHA1}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bext
COMMAND_ERROR_IS_FATAL ANY
)
# Configure process will need to clean up bext
if (BRLCAD_BEXT_CLEANUP)
set(BEXT_SRC_CLEANUP TRUE PARENT_SCOPE)
@@ -425,6 +436,12 @@ function(brlcad_ext_setup)
message(FATAL_ERROR "Invalid bext directory: ${BRLCAD_EXT_SOURCE_DIR}")
endif()
if(NOT _BRLCAD_REPOSITORY_ROOT STREQUAL "${_BRLCAD_REPOSITORY_DEFAULT_ROOT}/"
AND NOT EXISTS "${BRLCAD_EXT_SOURCE_DIR}/CMake/BextGit.cmake")
message(FATAL_ERROR
"bext sources in ${BRLCAD_EXT_SOURCE_DIR} do not support BEXT_REPOSITORY_ROOT; update bext before building with BRLCAD_REPOSITORY_ROOT=${BRLCAD_REPOSITORY_ROOT}")
endif()
# We do allow the user to specify a build dir to be reused, however
# this is unpredictable and may cause unexpected results depending
# on what is or isn't rebuilt - use with care.
@@ -463,11 +480,18 @@ function(brlcad_ext_setup)
endif(BRLCAD_ENABLE_APPLESEED)
# Define CMake arguments we'll need regardless of mode
set(bext_shallow_clone ON)
if(_BRLCAD_REPOSITORY_LOCAL)
set(bext_shallow_clone OFF)
endif()
set(CMAKE_CMD_ARGS
-S ${BRLCAD_EXT_SOURCE_DIR} -B ${BRLCAD_EXT_BUILD_DIR} -DGIT_SHALLOW_CLONE=ON
-DENABLE_ALL=${BEXT_ENABLE_ALL} -DUSE_GDAL=${BEXT_USE_GDAL} -DUSE_QT=${BEXT_USE_QT} -DUSE_TCL=${BEXT_USE_TCL}
-DUSE_APPLESEED=${BEXT_USE_APPLESEED} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_INSTALL_PREFIX=${BRLCAD_EXT_INSTALL_DIR}
-S "${BRLCAD_EXT_SOURCE_DIR}" -B "${BRLCAD_EXT_BUILD_DIR}"
"-DBEXT_REPOSITORY_ROOT:STRING=${_BRLCAD_REPOSITORY_ROOT}"
"-DGIT_SHALLOW_CLONE:BOOL=${bext_shallow_clone}"
"-DENABLE_ALL=${BEXT_ENABLE_ALL}" "-DUSE_GDAL=${BEXT_USE_GDAL}"
"-DUSE_QT=${BEXT_USE_QT}" "-DUSE_TCL=${BEXT_USE_TCL}"
"-DUSE_APPLESEED=${BEXT_USE_APPLESEED}" "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
"-DCMAKE_INSTALL_PREFIX=${BRLCAD_EXT_INSTALL_DIR}"
)
if(BRLCAD_X11_PROVIDER_RESOLVED STREQUAL "XMIN" AND BRLCAD_XMIN_ROOT)
list(APPEND CMAKE_CMD_ARGS
+20 -15
View File
@@ -1108,9 +1108,9 @@ function(brlcad_install_strclear_replace stamp_dir strclear source install_file
endif()
endfunction()
function(brlcad_install_binary_postprocess stamp_dir strclear source install_file install_type mode rpath_tool install_rpath build_lib_path rel_rpath use_selective_rpath verbose)
function(brlcad_install_binary_postprocess stamp_dir strclear source install_file install_type mode rpath_tool install_rpath build_lib_path rel_rpath verbose)
set(_brlcad_physical_file "$ENV{DESTDIR}${install_file}")
set(_brlcad_signature "binary-postprocess|${install_type}|${mode}|${rpath_tool}|${install_rpath}|${build_lib_path}|${rel_rpath}|${use_selective_rpath}|${strclear}")
set(_brlcad_signature "binary-postprocess|${install_type}|${mode}|${rpath_tool}|${install_rpath}|${build_lib_path}|${rel_rpath}|${strclear}")
_brlcad_postprocess_needed(_brlcad_needed "${stamp_dir}" "${source}" "${_brlcad_physical_file}" "${_brlcad_signature}")
if(NOT _brlcad_needed)
return()
@@ -1121,32 +1121,41 @@ function(brlcad_install_binary_postprocess stamp_dir strclear source install_fil
message(FATAL_ERROR "Post-install copy did not create ${_brlcad_physical_file}")
endif()
set(_brlcad_result 0)
set(_brlcad_rpath_output)
set(_brlcad_rpath_error)
if("${mode}" STREQUAL "RPATH_TOOL")
set(_brlcad_selective_rpath_args)
if(use_selective_rpath)
set(_brlcad_selective_rpath_args --set-rpath-if-needed --set-rpath-if-needed-prepend --stale-rpath-prefix "${build_lib_path}")
endif()
# Install the exact project-defined search order. A selective update can
# leave an existing relative-only RPATH without the canonical install path.
execute_process(
COMMAND "${rpath_tool}" --set-rpath "${install_rpath}" ${_brlcad_selective_rpath_args} "${_brlcad_physical_file}"
COMMAND "${rpath_tool}" --set-rpath "${install_rpath}" "${_brlcad_physical_file}"
RESULT_VARIABLE _brlcad_result
OUTPUT_VARIABLE _brlcad_rpath_output
ERROR_VARIABLE _brlcad_rpath_error
)
elseif("${mode}" STREQUAL "APPLE")
execute_process(
COMMAND install_name_tool -delete_rpath "${build_lib_path}" "${_brlcad_physical_file}"
RESULT_VARIABLE _brlcad_result
OUTPUT_VARIABLE _brlcad_output
ERROR_VARIABLE _brlcad_error
OUTPUT_VARIABLE _brlcad_rpath_output
ERROR_VARIABLE _brlcad_rpath_error
)
if(_brlcad_result EQUAL 0)
execute_process(
COMMAND install_name_tool -add_rpath "${rel_rpath}" "${_brlcad_physical_file}"
RESULT_VARIABLE _brlcad_result
OUTPUT_VARIABLE _brlcad_rpath_output
ERROR_VARIABLE _brlcad_rpath_error
)
endif()
endif()
if(NOT _brlcad_result EQUAL 0)
message(WARNING "Post-install RPATH update failed for ${_brlcad_physical_file}")
message(
WARNING
"Post-install RPATH update failed for ${_brlcad_physical_file}\n"
"stdout: ${_brlcad_rpath_output}\n"
"stderr: ${_brlcad_rpath_error}"
)
return()
endif()
@@ -1840,13 +1849,9 @@ endfunction()
elseif(APPLE)
set(_brlcad_install_postprocess_mode "APPLE")
endif(P_RPATH_EXECUTABLE)
set(_brlcad_install_use_selective_rpath FALSE)
if(P_RPATH_SUPPORTS_SET_IF_NEEDED_PREPEND)
set(_brlcad_install_use_selective_rpath TRUE)
endif(P_RPATH_SUPPORTS_SET_IF_NEEDED_PREPEND)
install(
CODE
"include(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_SCRIPT}\")\nbrlcad_install_binary_postprocess(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_STAMP_DIR}\" \"${STRCLEAR_EXECUTABLE}\" \"${CMAKE_BINARY_DIR}/${tf}\" \"\${CMAKE_INSTALL_PREFIX}/${tf}\" \"PROGRAM\" \"${_brlcad_install_postprocess_mode}\" \"${_brlcad_install_postprocess_tool}\" \"${_brlcad_install_postprocess_rpath}\" \"${CMAKE_BINARY_DIR}/${LIB_DIR}\" \"${REL_RPATH}\" \"${_brlcad_install_use_selective_rpath}\" \"${BRLCAD_VERBOSE}\")"
"include(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_SCRIPT}\")\nbrlcad_install_binary_postprocess(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_STAMP_DIR}\" \"${STRCLEAR_EXECUTABLE}\" \"${CMAKE_BINARY_DIR}/${tf}\" \"\${CMAKE_INSTALL_PREFIX}/${tf}\" \"PROGRAM\" \"${_brlcad_install_postprocess_mode}\" \"${_brlcad_install_postprocess_tool}\" \"${_brlcad_install_postprocess_rpath}\" \"${CMAKE_BINARY_DIR}/${LIB_DIR}\" \"${REL_RPATH}\" \"${BRLCAD_VERBOSE}\")"
)
continue()
endif("${tf}" IN_LIST ALL_BINARY_FILES)
+108
View File
@@ -0,0 +1,108 @@
# Copyright (c) 2026 United States Government as represented by
# the U.S. Army Research Laboratory.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# 3. The name of the author may not be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include_guard(GLOBAL)
set(_BRLCAD_REPOSITORY_DEFAULT_ROOT "https://github.com/BRL-CAD")
set(BRLCAD_REPOSITORY_ROOT "${_BRLCAD_REPOSITORY_DEFAULT_ROOT}" CACHE STRING
"Repository URL or local mirror directory containing bext and its dependency repositories")
function(brlcad_repository_root out_root out_local)
set(root "${BRLCAD_REPOSITORY_ROOT}")
set(local FALSE)
if(root STREQUAL "")
message(FATAL_ERROR "BRLCAD_REPOSITORY_ROOT must be a repository URL or local mirror directory")
endif()
# Match bext's URL handling, but resolve relative paths against BRL-CAD.
# Drive paths must be checked before scp-style SSH locations.
if(root MATCHES "^file://")
set(local TRUE)
string(REPLACE "=" "%3D" root "${root}")
elseif(root MATCHES "^[A-Za-z]:[/\\\\]" OR
NOT (root MATCHES "^[A-Za-z][A-Za-z0-9+.-]*://" OR root MATCHES "^[^/]+:.+"))
set(local TRUE)
string(REPLACE "\\" "/" root "${root}")
if(WIN32 OR NOT root MATCHES "^[A-Za-z]:/")
get_filename_component(root "${root}" ABSOLUTE BASE_DIR "${CMAKE_SOURCE_DIR}")
endif()
# Escape literal percent signs first so native paths cannot introduce
# URL escapes. An equals sign also has meaning in Git's -c assignments.
string(REPLACE "%" "%25" root "${root}")
string(REPLACE " " "%20" root "${root}")
string(REPLACE "#" "%23" root "${root}")
string(REPLACE "?" "%3F" root "${root}")
string(REPLACE "=" "%3D" root "${root}")
if(root MATCHES "^[A-Za-z]:/")
set(root "file:///${root}")
elseif(root MATCHES "^//")
set(root "file:${root}")
else()
set(root "file://${root}")
endif()
endif()
if(NOT root MATCHES "^file:///*$")
string(REGEX REPLACE "/+$" "" root "${root}")
string(APPEND root "/")
else()
set(root "file:///")
endif()
set(${out_root} "${root}" PARENT_SCOPE)
set(${out_local} "${local}" PARENT_SCOPE)
endfunction()
function(brlcad_repository_git_command out_command)
set(git_command "${GIT_EXEC}")
if(WIN32)
list(APPEND git_command -c core.longpaths=true)
endif()
if(_BRLCAD_REPOSITORY_LOCAL)
list(APPEND git_command -c protocol.file.allow=always)
# This also constrains recursive Git processes and user URL rewrites.
list(PREPEND git_command "${CMAKE_COMMAND}" -E env GIT_ALLOW_PROTOCOL=file --)
endif()
set(${out_command} "${git_command}" PARENT_SCOPE)
endfunction()
brlcad_repository_root(_BRLCAD_REPOSITORY_ROOT _BRLCAD_REPOSITORY_LOCAL)
set(_BRLCAD_BEXT_REPOSITORY "${_BRLCAD_REPOSITORY_ROOT}bext.git")
message(STATUS "BRL-CAD repository root: ${_BRLCAD_REPOSITORY_ROOT}")
if(_BRLCAD_REPOSITORY_LOCAL)
message(STATUS "BRL-CAD Git acquisition: file transport only")
endif()
# Local Variables:
# tab-width: 8
# mode: cmake
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+1
View File
@@ -15,6 +15,7 @@ set(
BRLCAD_Options.cmake
BRLCAD_Probe_Validation.cmake
BRLCAD_Regress_Util.cmake
BRLCAD_Repository.cmake
BRLCAD_Summary.cmake
BRLCAD_Targets.cmake
BRLCAD_Test_Wrappers.cmake
+31 -1
View File
@@ -95,6 +95,33 @@ add_custom_target(
)
set_target_properties(distcheck-source_archives PROPERTIES FOLDER "BRL-CAD Distribution Checking")
# Dependency paths must survive CMake, build-tool, and shell parsing. Load
# their values from a cache file rather than embedding them in command text.
set(distcheck_dependency_cache "${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/distcheck_dependencies.cmake")
set(distcheck_dependency_vars BRLCAD_REPOSITORY_ROOT BRLCAD_EXT_DIR)
foreach(distcheck_optional_var BRLCAD_EXT_SOURCE_DIR BRLCAD_EXT_PARALLEL)
if(DEFINED ${distcheck_optional_var})
list(APPEND distcheck_dependency_vars ${distcheck_optional_var})
endif()
endforeach()
file(WRITE "${distcheck_dependency_cache}" "")
foreach(distcheck_var IN LISTS distcheck_dependency_vars)
set(distcheck_value "${${distcheck_var}}")
if(distcheck_var STREQUAL "BRLCAD_REPOSITORY_ROOT")
set(distcheck_value "${_BRLCAD_REPOSITORY_ROOT}")
endif()
set(distcheck_type STRING)
if(distcheck_var MATCHES "_DIR$")
set(distcheck_type PATH)
endif()
string(REPLACE "\\" "\\\\" distcheck_value "${distcheck_value}")
string(REPLACE "\"" "\\\"" distcheck_value "${distcheck_value}")
string(REPLACE "$" "\\$" distcheck_value "${distcheck_value}")
file(APPEND "${distcheck_dependency_cache}"
"set(${distcheck_var} \"${distcheck_value}\" CACHE ${distcheck_type} \"Inherited dependency setting\" FORCE)\n")
endforeach()
distclean("${distcheck_dependency_cache}")
# Utility function for defining individual distcheck targets
macro(
create_distcheck
@@ -120,7 +147,10 @@ macro(
# Need to set these locally so configure_file will pick them up...
set(TARGET_SUFFIX ${TARGET_SUFFIX})
set(CMAKE_OPTS ${CMAKE_OPTS_IN})
# Keep machine-specific settings out of the options saved for CI above.
# The shared cache replaces each variant's unquoted output argument.
string(REPLACE "-DBRLCAD_EXT_DIR=${BRLCAD_EXT_DIR}" "" CMAKE_OPTS "${CMAKE_OPTS_IN}")
string(APPEND CMAKE_OPTS " -C \"${distcheck_dependency_cache}\"")
# For configure_file, need to set these as variables not just input parameters
set(source_dir "${source_dir}")
+6
View File
@@ -1,3 +1,9 @@
brlcad (7.46.0-0) unstable; urgency=low
* update brlcad version
-- Cliff Yapp <cyapp@brlcad.org> Thu, 07 Oct 2026 20:24:00 +0000
brlcad (7.44.0-0) unstable; urgency=low
* update brlcad version
+1 -1
View File
@@ -11,7 +11,7 @@
\f0\i\b\fs72 \cf0 README
\fs36 \
\f1\i0\b0\fs28 This is BRL-CAD 7.44.0 for Mac OS X. Please send any bugs, comments, questions, or suggestions to the project website.\
\f1\i0\b0\fs28 This is BRL-CAD 7.46.0 for Mac OS X. Please send any bugs, comments, questions, or suggestions to the project website.\
\fs22 \
+1 -1
View File
@@ -4,7 +4,7 @@
\margl1440\margr1440\vieww8020\viewh6280\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\fs22 \cf0 This installer will guide you through the installation of BRL-CAD 7.44.0 for Mac OS X and Darwin.\
\f0\fs22 \cf0 This installer will guide you through the installation of BRL-CAD 7.46.0 for Mac OS X and Darwin.\
\
This distribution of BRL-CAD requires approximately 500 MB of available disk space and does not include source code.\
\
+13 -17
View File
@@ -79,7 +79,9 @@ endif(NOT EXISTS \"\${GCV_EXEC}\")\n"
# setup conversion tests
foreach(in_file ${INPUT_FILES})
get_filename_component(curr_in_basename ${in_file} NAME_WLE)
set(out_path "${CMAKE_CURRENT_BINARY_DIR}/conv-${curr_in_basename}")
# Tests in the same directory may run concurrently, so their output paths
# must not overlap.
set(out_path "${CMAKE_CURRENT_BINARY_DIR}/conv-${TARGET_DIRECTION}-${curr_in_basename}")
# need to split suffix so we can distinguish from different converters
if(${fmt}_EXPORTER)
@@ -120,7 +122,7 @@ endif(NOT EXISTS \"\${GCV_EXEC}\")\n"
)
endif(${fmt}_GCV_OPTIONS)
set(out_distclean ${out_distclean}} "${out_path}-${out_fmt}${out_suffix}")
set(out_distclean ${out_distclean} "${out_path}-${out_fmt}${out_suffix}")
set(expected_output_files "${expected_output_files} \"${out_path}-${out_fmt}${out_suffix}\"")
endforeach(out_fmt ${${fmt}_CONVERTERS})
@@ -162,19 +164,8 @@ execute_process(
file(APPEND \"\${LOGFILE}\" \"\${log_msg}\\n\")
unset(log_msg)
"
)
set(OUTPUT_FILES ${OUTPUT_FILES} ${out_distclean})
unset(expected_output_files)
unset(out_distclean)
endforeach(in_file ${INPUT_FILES})
file(
APPEND
${cmake_in_path}
"
# check output files exists and are a reasonable size
# check output files exist and are a reasonable size
foreach(out_file \${ofiles})
if(NOT EXISTS \"\${out_file}\")
file(APPEND \"\${LOGFILE}\" \"FAILURE: \${curr_result}, \${out_file} not found.\")
@@ -191,13 +182,18 @@ foreach(out_file \${ofiles})
file(APPEND \"\${LOGFILE}\" \"FAILURE: \${curr_result} file size is \${file_size} for \${out_file}\\n Expecting ${EXPECTED_FILE_SIZE} with delta ${FILE_SIZE_DELTA}\")
message(FATAL_ERROR \"[gcv] Failure while converting ${in_file}, see \${LOGFILE} for more info.\")
endif(\"\${size_delta}\" GREATER \"${FILE_SIZE_DELTA}\")
endif(NOT EXISTS \"\${out_file}\")\n
endif(NOT EXISTS \"\${out_file}\")
# cleanup file from build dir
file(REMOVE \"\${out_file}\")
endforeach(out_file \${ofiles})
"
)
"
)
set(OUTPUT_FILES ${OUTPUT_FILES} ${out_distclean})
unset(expected_output_files)
unset(out_distclean)
endforeach(in_file ${INPUT_FILES})
# assume the fmt is the gcv dependency unless specified
if(${fmt}_DEPS)
+1 -1
View File
@@ -2,7 +2,7 @@ if(TARGET gcv-assetimport)
gcv_regress_util("gltf" "tetra-gltf2.0.gltf;tetra-glb2.0.glb" DEPS "gcv;gcv-assetimport" GCV_OPTIONS "--input-format=assetimport")
gcv_regress_util("glb" "tetra.g" DEPS "gcv;gcv-assetimport" GCV_OPTIONS "--output-format=assetimport" EXPECTED_FILE_SIZE "1476" EXPORTER)
gcv_regress_util("gltf" "tetra.g" DEPS "gcv;gcv-assetimport" GCV_OPTIONS "--output-format=assetimport" EXPECTED_FILE_SIZE "3356" EXPORTER)
distclean("${CMAKE_CURRENT_BINARY_DIR}/conv-tetra.bin")
distclean("${CMAKE_CURRENT_BINARY_DIR}/conv-regress-g-gltf-tetra.bin")
endif(TARGET gcv-assetimport)
if(TARGET gcv-gltf)
+2 -2
View File
@@ -1,7 +1,7 @@
if(TARGET gcv-assetimport)
set(dep_str DEPS "gcv;gcv-obj;gcv-assetimport" CONVERTERS "assetimport")
distclean("${CMAKE_CURRENT_BINARY_DIR}/conv-tetra-assetimport.mtl") # hard to track companion files
distclean("${CMAKE_CURRENT_BINARY_DIR}/conv-regress-g-obj-tetra-assetimport.mtl") # hard to track companion files
else()
set(dep_str "")
endif(TARGET gcv-assetimport)
@@ -9,7 +9,7 @@ endif(TARGET gcv-assetimport)
gcv_regress_util("obj" "tetra.obj" ${dep_str})
gcv_regress_util("obj_unnamed_group" "tetra-unnamed-group.obj" DEPS "gcv;gcv-obj")
gcv_regress_util("obj" "tetra.g" ${dep_str} EXPORTER)
distclean("${CMAKE_CURRENT_BINARY_DIR}/conv-tetra.mtl")
distclean("${CMAKE_CURRENT_BINARY_DIR}/conv-regress-g-obj-tetra.mtl")
cmakefiles(
CMakeLists.txt
+1
View File
@@ -23,6 +23,7 @@ set(
gqa.mass_mix.am.out
gqa.mass_mix.aw.out
gqa.mass_mix.u0.out
gqa.missing_density.out
gqa.mged
gqa.overlaps.plot3
gqa.sphere_adaptive.out
+24
View File
@@ -67,6 +67,11 @@ run_capture ( ) {
return $ret
}
expect_failure ( ) {
"$@"
test $? -ne 0
}
extract_last_number ( ) {
label="$1"
file="$2"
@@ -101,6 +106,19 @@ assert_close ( ) {
fi
}
assert_contains ( ) {
expected="$1"
file="$2"
desc="$3"
if grep -F -q "$expected" "$file" ; then
log "PASS: $desc"
else
log "FAIL: $desc"
STATUS="`expr $STATUS + 1`"
fi
}
MGED="`ensearch mged`"
if test ! -f "$MGED" ; then
log "Unable to find mged, aborting"
@@ -177,6 +195,10 @@ adjust mass_air.r GIFTmater 2
g mass_air_only.g mass_air.r
g mass_air_mix.g mass_solid.r mass_air.r
in missing_density.s rpp 2 3 0 1 0 1
r missing_density.r u missing_density.s
adjust missing_density.r GIFTmater 123
in sphere.s sph 0 0 0 1
r sphere.r u sphere.s
adjust sphere.r GIFTmater 5
@@ -243,6 +265,7 @@ run_capture gqa.mass_mix.aw.out $GQABIN -u m,m^3,kg -g 250mm-50mm -Aw gqa.g mass
run_capture gqa.mass_mix.am.out $GQABIN -u m,m^3,kg -g 250mm-50mm -Am gqa.g mass_air_mix.g
run_capture gqa.mass_mix.u0.out $GQABIN -u m,m^3,kg -g 250mm-50mm -U 0 -Aw gqa.g mass_air_mix.g
run_capture gqa.sphere_adaptive.out $GQABIN -u m,m^3,kg -q -Av -g 0.05m gqa.g sphere.r
run_capture gqa.missing_density.out expect_failure $GQABIN -u m,m^3,kg -g 250mm-50mm -f density_table.txt -Aw gqa.g missing_density.r
MASS_AIR_AW="`extract_last_number 'Average total weight:' gqa.mass_air.aw.out`"
MASS_AIR_AM="`extract_last_number 'Average total weight:' gqa.mass_air.am.out`"
@@ -257,6 +280,7 @@ assert_close 1001 "$MASS_MIX_AW" 0.001 "mixed solid+air modeled weight with -Aw"
assert_close "$MASS_MIX_AW" "$MASS_MIX_AM" 0.001 "-Aw and -Am agree on mixed modeled air"
assert_close 1000 "$MASS_MIX_U0" 0.001 "-U 0 excludes modeled air from weight"
assert_close 4.18879 "$SPHERE_VOLUME" 0.01 "adaptive sphere volume refines beyond symmetric first pass"
assert_contains "Density 123 on region /missing_density.r is not defined in the density table." gqa.missing_density.out "undefined density produces an error"
if [ $STATUS = 0 ] ; then
+5
View File
@@ -68,6 +68,7 @@ set(
sca_view.mged
search_exec.mged
sed.mged
sed_reset.mged
set_perspective.mged
setview.mged
size.mged
@@ -145,6 +146,10 @@ if(TARGET mged)
elseif(TNAME STREQUAL "prim_edit")
set(TEST_EXPECT "Primitive parameter editing testing completed")
set(TEST_REJECT "FAIL:")
elseif(TNAME STREQUAL "sed_reset")
set(TEST_EXPECT "PASS: 'sed_reset' testing completed")
set(TEST_REJECT "FAIL:")
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
elseif(TNAME STREQUAL "overlap_plotting")
set(TEST_DB "${CMAKE_SOURCE_DIR}/src/libged/tests/ged_tests.g")
set(TEST_EXPECT "PASS overlap plotting")
+31
View File
@@ -119,4 +119,35 @@ if {![catch {get $invalid_inner}]} {
}
# ARBN uses one independent thickness per plane. Exercise unequal offsets so
# the test also verifies the plane ordering and inward normal convention.
set arbn_outer inside_arbn_outer.s
set arbn_inner inside_arbn_inner.s
set arbn_thicknesses {1 2 3 4 1.5 0.5}
in $arbn_outer arbn 6 \
1 0 0 10 -1 0 0 0 \
0 1 0 10 0 -1 0 0 \
0 0 1 10 0 0 -1 0
if {[catch {inside $arbn_outer $arbn_inner {*}$arbn_thicknesses} result]} {
error "inside failed for ARBN: $result"
}
set arbn_definition [get $arbn_inner]
set expected_offsets {9 -2 7 -4 8.5 -0.5}
for {set plane 0} {$plane < 6} {incr plane} {
set equation [lindex $arbn_definition [expr {4 + 2 * $plane}]]
set actual_offset [lindex $equation 3]
if {abs($actual_offset - [lindex $expected_offsets $plane]) > $plane_tolerance} {
error "ARBN inside plane $plane has offset $actual_offset: $arbn_definition"
}
}
facetize -n inside_arbn_inner_nmg.s $arbn_inner
set invalid_arbn_inner inside_arbn_invalid.s
if {![catch {inside $arbn_outer $invalid_arbn_inner {*}[lrepeat 6 6]}]} {
error "ARBN inside accepted thicknesses that collapse the solid"
}
if {![catch {get $invalid_arbn_inner}]} {
error "ARBN inside wrote an invalid collapsed solid"
}
puts "*** 'arb' testing completed ***"
+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
+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) ---"
+64
View File
@@ -0,0 +1,64 @@
puts "*** Testing 'sed_reset' command ***"
proc sed_reset_check {primitive edit_commands} {
e $primitive
sed $primitive
if {[status state] ne "SOL EDIT"} {
error "$primitive entered [status state], not SOL EDIT"
}
set original [get_sed]
foreach command $edit_commands {
uplevel #0 $command
}
if {[get_sed] eq $original} {
error "$primitive edit did not change the working geometry"
}
sed_reset
if {[status state] ne "SOL EDIT"} {
error "$primitive reset entered [status state], not SOL EDIT"
}
if {[get_sed] ne $original} {
error "$primitive reset did not restore the original geometry"
}
press reject
if {[status state] ne "VIEWING"} {
error "$primitive reject entered [status state], not VIEWING"
}
d $primitive
}
if {[catch {
in sed_reset_arb8.s arb8 \
0 0 0 10 0 0 10 10 0 0 10 0 \
0 0 10 10 0 10 10 10 10 0 10 10
sed_reset_check sed_reset_arb8.s {
{press {Move Faces}}
{press {Move Face 1234}}
{p 0 0 -1}
}
} failure]} {
puts "FAIL: ARB8 face move reset ($failure)"
catch {press reject}
catch {Z}
} else {
puts "PASS: ARB8 face move reset"
}
if {[catch {
make sed_reset_cline.s cline
sed_reset_check sed_reset_cline.s {
{press {Move End H}}
{p 3 4 12}
}
} failure]} {
puts "FAIL: CLINE edit reset ($failure)"
catch {press reject}
catch {Z}
} else {
puts "PASS: CLINE edit reset"
}
puts "PASS: 'sed_reset' testing completed"
+17 -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")
@@ -143,6 +144,18 @@ if(BRLCAD_X11_PROVIDER_RESOLVED STREQUAL "XMIN" AND
)
brlcad_regression_test(regress-archer-embedded-xmin-gui
"archer;bwish" STAND_ALONE TEST_DEFINED)
brlcad_add_gui_test(
NAME regress-archer-geometry-xmin-gui
TIMEOUT 120
ENVIRONMENT ${_gui_smoke_environment}
"GUI_TEST_LIBRARY=${_gui_test_library}"
COMMAND "${SH_EXEC}" "${_gui_tcl_runner}" archer-geometry
$<TARGET_FILE:bwish>
"${CMAKE_CURRENT_SOURCE_DIR}/archer_geometry_xmin_gui.tcl"
)
brlcad_regression_test(regress-archer-geometry-xmin-gui
"archer;bwish" STAND_ALONE TEST_DEFINED)
endif()
endif()
@@ -200,6 +213,7 @@ cmakefiles(
archer_action.sh
archer_smoke.sh
archer_embedded_xmin_gui.tcl
archer_geometry_xmin_gui.tcl
archer_menu_manifest.txt
archer_xmin_gui.tcl
qged_action.sh
@@ -212,18 +226,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:
+134
View File
@@ -0,0 +1,134 @@
# A R C H E R _ G E O M E T R Y _ X M I N _ G U I . T C L
# 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.
#
###
if {![info exists ::env(GUI_TEST_LIBRARY)]} {
puts stderr "GUI_TEST_LIBRARY is required"
exit 2
}
source $::env(GUI_TEST_LIBRARY)
set ::env(ARCHER_PREFS_FILE) [file join $::env(GUI_TEST_DIR) .archerrc]
proc archer_geometry_preferences {{geometry ""}} {
set channel [open $::env(ARCHER_PREFS_FILE) w]
if {$geometry ne ""} {
puts $channel [list set mWindowGeometry $geometry]
}
close $channel
}
proc archer_geometry_saved {} {
set channel [open $::env(ARCHER_PREFS_FILE) r]
set contents [read $channel]
close $channel
if {[regexp -line {^set mWindowGeometry (.+)$} $contents -> geometry]} {
return $geometry
}
return ""
}
proc archer_geometry_settle {} {
# Allow MapNotify and the deferred geometry restore to reach the event loop.
set settle_delay_ms 100
after $settle_delay_ms [list set ::archer_geometry_settled 1]
vwait ::archer_geometry_settled
update
::gui::test::require_no_background_errors
}
if {[catch {
package require Archer 1.0
::gui::test::capture_background_errors
wm withdraw .
set saved_geometry 900x650+120+90
set startup_geometry 1000x700+20+30
set updated_geometry 950x680+60+70
set default_geometry 920x660+40+50
archer_geometry_preferences $saved_geometry
Archer .unmapped
::gui::test::require {[wm state .unmapped] eq "withdrawn"} \
"startup fixture unexpectedly showed its window"
::itcl::delete object .unmapped
archer_geometry_settle
::gui::test::require {[archer_geometry_saved] eq $saved_geometry} \
"closing an unmapped Archer replaced its saved geometry"
archer_geometry_preferences
Archer .unmapped_new
::itcl::delete object .unmapped_new
archer_geometry_settle
::gui::test::require {[archer_geometry_saved] eq ""} \
"closing an unmapped Archer saved an unfinished layout size"
archer_geometry_preferences $saved_geometry
Archer .restore
.restore configure -geometry $startup_geometry
wm geometry .restore $startup_geometry
event generate [.restore component hpane] <Map>
archer_geometry_settle
::gui::test::require {[wm geometry .restore] eq $startup_geometry} \
"a child Map event restored the main window's geometry prematurely"
wm deiconify .restore
archer_geometry_settle
::gui::test::require {[wm geometry .restore] eq $saved_geometry} \
"mapping the main window did not restore its saved geometry"
wm geometry .restore $updated_geometry
event generate [.restore component hpane] <Map>
archer_geometry_settle
::gui::test::require {[wm geometry .restore] eq $updated_geometry} \
"a later child Map event reset the resized main window"
wm withdraw .restore
::itcl::delete object .restore
archer_geometry_settle
::gui::test::require {[archer_geometry_saved] eq $updated_geometry} \
"closing a previously mapped window did not save its latest geometry"
archer_geometry_preferences
Archer .default
wm geometry .default $default_geometry
wm deiconify .default
archer_geometry_settle
::gui::test::require {[wm geometry .default] eq $default_geometry} \
"an empty geometry option reset the window to its layout size"
wm withdraw .default
::itcl::delete object .default
archer_geometry_settle
} message options]} {
puts stderr $message
if {[dict exists $options -errorinfo]} {
puts stderr [dict get $options -errorinfo]
}
puts "FAIL: Archer window geometry regression"
exit 1
}
puts "PASS: Archer window geometry restoration and startup cancellation"
exit 0
# Local Variables:
# tab-width: 8
# mode: Tcl
# tcl-indent-level: 4
# indent-tabs-mode: t
# End:
# ex: shiftwidth=4 tabstop=8
File diff suppressed because it is too large Load Diff
+41 -1
View File
@@ -50,6 +50,18 @@ proc ::mged::xmin::help::exercise_command_manual {top} {
::gui::test::require {
[llength $selection] == 1 && [$toc get $selection] eq "rt"
} "man rt did not select and load the rt manual page"
event generate $toc <<ListboxSelect>>
::mged::gui::test::settle
man search
set html_view [$browser component browser].htmlview
set html_widget [$html_view html]
$html_view goto {#hierarchy_performance}
::mged::gui::test::settle
::gui::test::require {[lindex [$html_widget yview] 0] > 0.0} \
"manual browser did not follow an in-page hyperlink"
::gui::test::require {
[catch {man xmin_page_that_does_not_exist} message] &&
[string first "couldn't find manual page" $message] >= 0
@@ -151,10 +163,38 @@ proc ::mged::xmin::help::exercise_html_manual {top} {
set mged_browser [file join $::env(GUI_TEST_DIR) missing-browser]
::mged::gui::test::invoke $top {Help Manual}
set fallback $top.man
set viewer $fallback.html
::gui::test::require {
[winfo exists $fallback] && [winfo ismapped $fallback] &&
[string length [string trim [$fallback.text get 1.0 end]]] > 100
[winfo exists $viewer] && [$viewer title] eq "Table of Contents"
} "Manual did not load MGED's internal HTML viewer"
set special_path [file join [pwd] {manual path} {contents#1.html}]
::gui::test::require {
[::mged::manual::local_path \
[::mged::manual::file_uri $special_path]] eq \
[file normalize $special_path]
} "internal Manual viewer did not preserve special path characters"
set html [$viewer html]
set contents [$html text text]
::gui::test::require {
[string first "Preface" $contents] >= 0 &&
[string first "background-color" $contents] < 0
} "internal Manual viewer did not render its table of contents"
$viewer goto mged.html#csg
::mged::gui::test::settle
set contents [$html text text]
::gui::test::require {
[$viewer title] eq "Mged User's Manual" &&
[string first "Modeling With CSG" $contents] >= 0
} "internal Manual viewer did not follow its document links"
$fallback.f.back invoke
::mged::gui::test::settle
::gui::test::require {[$viewer title] eq "Table of Contents"} \
"internal Manual viewer Back button did not restore the prior page"
destroy $fallback
}
+7 -9
View File
@@ -72,17 +72,15 @@ set(archer_wizard_FILES
)
if(BRLCAD_ENABLE_TCL)
brlcad_adddata(archer_utility_FILES plugins/archer/Utility)
brlcad_adddata(archer_wizard_FILES plugins/archer/Wizards)
# Archer scans only the plugin category directories for registration scripts.
# Keep class definitions in their subdirectories so they are not sourced twice.
foreach(plugin_file IN LISTS archer_utility_FILES archer_wizard_FILES)
get_filename_component(plugin_dir "${plugin_file}" DIRECTORY)
file(RELATIVE_PATH plugin_dir "${CMAKE_CURRENT_SOURCE_DIR}/plugins" "${CMAKE_CURRENT_SOURCE_DIR}/${plugin_dir}")
brlcad_adddata("${plugin_file}" "plugins/archer/${plugin_dir}")
endforeach()
brlcad_adddata(plugins/Commands/README plugins/archer/Command)
brlcad_adddata(plugins/Core/README plugins/archer/Core)
brlcad_adddata(plugins/Utility/attrGroupsDisplayUtilityP/AttrGroupsDisplayUtilityP.tcl plugins/archer/Utility/attrGroupsDisplayUtilityP)
brlcad_adddata(plugins/Utility/botUtilityP/BotUtilityP.tcl plugins/archer/Utility/botUtilityP)
brlcad_adddata(plugins/Utility/lodUtilityP/LODUtilityP.tcl plugins/archer/Utility/lodUtilityP)
brlcad_adddata(plugins/Wizards/humanwizard/HumanWizard.tcl plugins/archer/Wizards/humanwizard)
brlcad_adddata(plugins/Wizards/tankwizard/TankWizard.tcl plugins/archer/Wizards/tankwizard)
brlcad_adddata(plugins/Wizards/tankwizard/images/tank.png plugins/archer/Wizards/tankwizardIA/images)
brlcad_adddata(plugins/Wizards/tirewizard/TireWizard.tcl plugins/archer/Wizards/tirewizard)
endif(BRLCAD_ENABLE_TCL)
set(
+30 -18
View File
@@ -23,10 +23,20 @@
#include "InformationGatherer.h"
#include "bu/log.h"
// flush results, if we have any, then exit
static void reportAndExit(const std::string &message, const std::string &result = "")
{
if (!result.empty())
bu_log("%s%s", result.c_str(), result.back() == '\n' ? "" : "\n");
bu_exit(BRLCAD_ERROR, "%s", message.c_str());
}
std::string getCmdPath(std::string exeDir, const char* cmd) {
char buf[MAXPATHLEN] = {0};
if (!bu_dir(buf, MAXPATHLEN, exeDir.c_str(), cmd, BU_DIR_EXT, NULL))
bu_exit(BRLCAD_ERROR, "Couldn't find %s, aborting.\n", cmd);
reportAndExit("Couldn't find " + std::string(cmd) + ", aborting.\n");
return std::string(buf);
}
@@ -58,13 +68,13 @@ getSurfaceArea(Options* opt, std::map<std::string, std::string> UNUSED(map), std
bu_process_create(&p, rtarea_av, BU_PROCESS_HIDE_WINDOW | BU_PROCESS_OUT_EQ_ERR);
if (bu_process_pid(p) <= 0) {
bu_exit(BRLCAD_ERROR, "Problem with getSurfaceArea, aborting\n");
reportAndExit("Problem with getSurfaceArea, aborting\n");
}
char buffer[128];
std::string result = "";
std::string result;
int read_cnt = 0;
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, 128-1, buffer)) > 0) {
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, sizeof(buffer) - 1, buffer)) > 0) {
/* NOTE: read does not ensure null-termination, thus buffersize-1 */
buffer[read_cnt] = '\0';
result += buffer;
@@ -80,7 +90,7 @@ getSurfaceArea(Options* opt, std::map<std::string, std::string> UNUSED(map), std
try {
surfArea += stod(result);
} catch (const std::invalid_argument& ia) {
bu_exit(BRLCAD_ERROR, "Invalid argument for getSurfaceArea. Got (%s), aborting.\n", ia.what());
reportAndExit("Invalid argument for getSurfaceArea. Got (" + std::string(ia.what()) + "), aborting.\n", result);
}
}
}
@@ -200,7 +210,7 @@ getVerificationData(struct ged* UNUSED(g), Options* opt, std::map<std::string, s
struct bu_process* p;
int read_cnt = 0;
char buffer[1024] = {0};
std::string result = "";
std::string result;
std::string ncpu(std::to_string(opt->getNCPU()));
std::string dFile(opt->getDensityFile());
@@ -233,21 +243,23 @@ getVerificationData(struct ged* UNUSED(g), Options* opt, std::map<std::string, s
bu_process_create(&p, const_cast<const char**>(gqa_av_vec.data()), BU_PROCESS_HIDE_WINDOW | BU_PROCESS_OUT_EQ_ERR);
if (bu_process_pid(p) <= 0) {
bu_exit(BRLCAD_ERROR, "Problem in getVerificationData gqa process creation, aborting\n");
reportAndExit("Problem in getVerificationData gqa process creation, aborting\n");
}
result = "";
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, 1024-1, buffer)) > 0) {
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, sizeof(buffer) - 1, buffer)) > 0) {
buffer[read_cnt] = '\0';
result += buffer;
}
if (bu_process_wait_n(&p, 0) != 0) {
bu_exit(BRLCAD_ERROR, "Problem collecting gqa volume and mass, aborting\n");
const int gqa_status = bu_process_wait_n(&p, 0);
// sanity - we should have clean status code and not the "no density found" message
const bool no_density_data = result.find(no_density_msg) != std::string::npos;
if (gqa_status != 0 && (!no_density_data || !dFile.empty())) {
reportAndExit("Problem collecting gqa volume and mass, aborting\n", result);
}
// make sure we did not get 'no density file' error message
if (result.find(no_density_msg) == std::string::npos) {
if (!no_density_data) {
// NOTE: tmp_val is zeroed at each parseLabeledNumber call
if (parseLabeledNumber(tmp_val, result, volume_label))
volume += tmp_val;
@@ -263,17 +275,17 @@ getVerificationData(struct ged* UNUSED(g), Options* opt, std::map<std::string, s
bu_process_create(&p, const_cast<const char**>(gqa_av_vec.data()), BU_PROCESS_HIDE_WINDOW | BU_PROCESS_OUT_EQ_ERR);
if (bu_process_pid(p) <= 0) {
bu_exit(BRLCAD_ERROR, "Problem in getVerificationData gqa process creation, aborting\n");
reportAndExit("Problem in getVerificationData gqa process creation, aborting\n");
}
result = "";
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, 1024-1, buffer)) > 0) {
result.clear();
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, sizeof(buffer) - 1, buffer)) > 0) {
buffer[read_cnt] = '\0';
result += buffer;
}
if (bu_process_wait_n(&p, 0) != 0) {
bu_exit(BRLCAD_ERROR, "Problem collecting gqa volume, aborting\n");
reportAndExit("Problem collecting gqa volume, aborting\n", result);
}
if (parseLabeledNumber(tmp_val, result, volume_label))
@@ -381,7 +393,7 @@ InformationGatherer::getMainComp()
ged_exec_exists(g, 2, cmd);
std::string res = bu_vls_addr(g->ged_result_str);
if (res != "1") {
bu_exit(BRLCAD_ERROR, "Could not find component (%s), aborting.\n", topname);
reportAndExit("Could not find component (" + topcomp + "), aborting.\n");
}
int entities = getNumEntities(opt->getTopComp());
+3 -2
View File
@@ -43,6 +43,7 @@ main(int argc, char *argv[])
int i, j;
int db_index;
int c;
int ret;
const char **av;
struct ged *gedp;
@@ -103,14 +104,14 @@ main(int argc, char *argv[])
bu_exit(1, usage, argv[0]);
}
(void)ged_exec_gqa(gedp, j, av);
ret = ged_exec_gqa(gedp, j, av);
if (bu_vls_strlen(gedp->ged_result_str) > 0)
bu_log("%s", bu_vls_addr(gedp->ged_result_str));
ged_close(gedp);
bu_free((void *)av, "av");
return 0;
return ret;
}
+2 -3
View File
@@ -63,7 +63,6 @@ extern int gettimeofday(struct timeval *, void *);
static constexpr int64_t usec_per_sec = 1000000;
static constexpr int64_t nsec_per_sec = 1000000000;
static constexpr int64_t nsec_per_usec = 1000;
#if defined(HAVE_GETPROCESSTIMES) || defined(HAVE_GETTHREADTIMES)
static constexpr int64_t nsec_per_windows_tick = 100;
#endif
@@ -107,7 +106,7 @@ timer_rusage(const struct rusage *usage)
+ (int64_t)usage->ru_stime.tv_sec * usec_per_sec
+ (int64_t)usage->ru_stime.tv_usec;
return usec * nsec_per_usec;
return usec * (nsec_per_sec / usec_per_sec);
}
@@ -149,7 +148,7 @@ static int64_t
timer_mach_time_val(const time_value_t *time_val)
{
return ((int64_t)time_val->seconds * nsec_per_sec
+ (int64_t)time_val->microseconds * nsec_per_usec);
+ (int64_t)time_val->microseconds * (nsec_per_sec / usec_per_sec));
}
static int64_t
+10 -2
View File
@@ -891,8 +891,8 @@ Done:
int
ogl_share_dlist(struct dm *dmp1, struct dm *dmp2)
{
struct gl_vars *mvars = (struct gl_vars *)dmp1->i->m_vars;
struct pogl_vars *privars = (struct pogl_vars *)dmp1->i->dm_vars.priv_vars;
struct gl_vars *mvars;
struct pogl_vars *privars;
GLfloat backgnd[4];
GLfloat vf;
GLXContext old_glxContext;
@@ -900,6 +900,9 @@ ogl_share_dlist(struct dm *dmp1, struct dm *dmp2)
if (dmp1 == (struct dm *)NULL)
return BRLCAD_ERROR;
mvars = (struct gl_vars *)dmp1->i->m_vars;
privars = (struct pogl_vars *)dmp1->i->dm_vars.priv_vars;
if (dmp2 == (struct dm *)NULL) {
/* create a new graphics context for dmp1 with private display lists */
@@ -1053,6 +1056,11 @@ ogl_share_dlist(struct dm *dmp1, struct dm *dmp2)
glXDestroyContext(((struct dm_glxvars *)dmp2->i->dm_vars.pub_vars)->dpy, old_glxContext);
}
struct dm *target_dmp = dmp2 ? dmp2 : dmp1;
struct gl_vars *target_mvars = (struct gl_vars *)target_dmp->i->m_vars;
gl_setZBuffer(target_dmp, target_mvars->zbuffer_on);
gl_setLight(target_dmp, target_mvars->lighting_on);
return BRLCAD_OK;
}
+8 -1
View File
@@ -88,11 +88,13 @@ wgl_share_dlist(struct dm *dmp1, struct dm *dmp2)
GLfloat backgnd[4];
GLfloat vf;
HGLRC old_glxContext;
struct gl_vars *mvars = (struct gl_vars *)dmp1->i->m_vars;
struct gl_vars *mvars;
if (dmp1 == (struct dm *)NULL)
return BRLCAD_ERROR;
mvars = (struct gl_vars *)dmp1->i->m_vars;
if (dmp2 == (struct dm *)NULL) {
/* create a new graphics context for dmp1 with private display lists */
@@ -244,6 +246,11 @@ wgl_share_dlist(struct dm *dmp1, struct dm *dmp2)
wglDeleteContext(old_glxContext);
}
struct dm *target_dmp = dmp2 ? dmp2 : dmp1;
struct gl_vars *target_mvars = (struct gl_vars *)target_dmp->i->m_vars;
gl_setZBuffer(target_dmp, target_mvars->zbuffer_on);
gl_setLight(target_dmp, target_mvars->lighting_on);
return BRLCAD_OK;
}
+115 -33
View File
@@ -138,30 +138,37 @@ static const char *p_nmgin[] = {
#define ARB_FACE_COUNT 6
#define ARB_MAX_PLANE_INTERSECTIONS 20
/* Verify that every plane bounds a non-degenerate convex volume. Translating
* the planes of an existing convex ARB cannot make their intersection
* the planes of an existing convex polyhedron cannot make their intersection
* unbounded, but excessive or unequal thicknesses can eliminate a face or
* collapse the volume. */
static int
arb_in_planes_valid(plane_t planes[ARB_FACE_COUNT], const struct bn_tol *tol)
in_planes_valid(const plane_t *planes, size_t plane_count,
const struct bn_tol *tol)
{
point_t vertices[ARB_MAX_PLANE_INTERSECTIONS];
int plane_used[ARB_FACE_COUNT] = {0};
size_t vertex_count = 0;
point_t basis[3];
plane_t basis_plane;
size_t basis_count = 0;
int have_volume = 0;
for (size_t i = 0; i < ARB_FACE_COUNT - 2; i++) {
for (size_t j = i + 1; j < ARB_FACE_COUNT - 1; j++) {
for (size_t k = j + 1; k < ARB_FACE_COUNT; k++) {
if (!planes || plane_count < 4)
return 0;
int *plane_used = (int *)bu_calloc(plane_count, sizeof(int),
"inside plane use flags");
for (size_t i = 0; i < plane_count - 2; i++) {
for (size_t j = i + 1; j < plane_count - 1; j++) {
for (size_t k = j + 1; k < plane_count; k++) {
point_t candidate;
int outside = 0;
if (bg_make_pnt_3planes(candidate, planes[i], planes[j], planes[k]) < 0)
continue;
for (size_t face = 0; face < ARB_FACE_COUNT; face++) {
for (size_t face = 0; face < plane_count; face++) {
if (DIST_PNT_PLANE(candidate, planes[face]) > tol->dist) {
outside = 1;
break;
@@ -173,35 +180,39 @@ arb_in_planes_valid(plane_t planes[ARB_FACE_COUNT], const struct bn_tol *tol)
plane_used[i] = 1;
plane_used[j] = 1;
plane_used[k] = 1;
VMOVE(vertices[vertex_count], candidate);
vertex_count++;
}
}
}
for (size_t face = 0; face < ARB_FACE_COUNT; face++)
if (!plane_used[face])
return 0;
/* At least four non-coplanar vertices are required for a volume. */
for (size_t i = 0; i + 2 < vertex_count; i++) {
for (size_t j = i + 1; j + 1 < vertex_count; j++) {
for (size_t k = j + 1; k < vertex_count; k++) {
plane_t vertex_plane;
if (bg_make_plane_3pnts(vertex_plane, vertices[i], vertices[j], vertices[k], tol) < 0)
if (have_volume)
continue;
for (size_t l = 0; l < vertex_count; l++) {
if (l == i || l == j || l == k)
continue;
if (!NEAR_ZERO(DIST_PNT_PLANE(vertices[l], vertex_plane), tol->dist))
return 1;
if (!basis_count) {
VMOVE(basis[0], candidate);
basis_count = 1;
} else if (basis_count == 1 &&
DIST_PNT_PNT_SQ(basis[0], candidate) > tol->dist_sq) {
VMOVE(basis[1], candidate);
basis_count = 2;
} else if (basis_count == 2 &&
bg_make_plane_3pnts(basis_plane, basis[0], basis[1],
candidate, tol) == 0) {
VMOVE(basis[2], candidate);
basis_count = 3;
} else if (basis_count == 3 &&
!NEAR_ZERO(DIST_PNT_PLANE(candidate, basis_plane),
tol->dist)) {
have_volume = 1;
}
}
}
}
return 0;
int all_planes_used = 1;
for (size_t face = 0; face < plane_count; face++) {
if (!plane_used[face]) {
all_planes_used = 0;
break;
}
}
bu_free(plane_used, "inside plane use flags");
return all_planes_used && have_volume;
}
@@ -227,7 +238,7 @@ arb7in(struct ged *gedp,
planes[i][W] -= thick[i];
}
if (!arb_in_planes_valid(planes, tol)) {
if (!in_planes_valid((const plane_t *)planes, ARB_FACE_COUNT, tol)) {
bu_vls_printf(gedp->ged_result_str,
"Cannot find a valid inside arb7: thicknesses eliminate a face or collapse the volume\n");
return BRLCAD_ERROR;
@@ -250,6 +261,43 @@ arb7in(struct ged *gedp,
}
/* Offset each outward-facing ARBN plane toward the interior. */
static int
arbnin(struct ged *gedp, struct rt_db_internal *ip, const fastf_t *thick,
const struct bn_tol *tol)
{
struct rt_arbn_internal *arbn =
(struct rt_arbn_internal *)ip->idb_ptr;
RT_ARBN_CK_MAGIC(arbn);
plane_t *planes = (plane_t *)bu_calloc(arbn->neqn, sizeof(plane_t),
"inside ARBN planes");
for (size_t i = 0; i < arbn->neqn; i++) {
fastf_t normal_length = MAGNITUDE(arbn->eqn[i]);
if (!isfinite(normal_length) || normal_length <= SQRT_SMALL_FASTF) {
bu_vls_printf(gedp->ged_result_str,
"Cannot find a valid inside ARBN: plane %zu has an invalid normal\n",
i);
bu_free(planes, "inside ARBN planes");
return BRLCAD_ERROR;
}
HSCALE(planes[i], arbn->eqn[i], 1.0 / normal_length);
planes[i][W] -= thick[i];
}
if (!in_planes_valid((const plane_t *)planes, arbn->neqn, tol)) {
bu_vls_printf(gedp->ged_result_str,
"Cannot find a valid inside ARBN: thicknesses eliminate a face or collapse the volume\n");
bu_free(planes, "inside ARBN planes");
return BRLCAD_ERROR;
}
bu_free(arbn->eqn, "arbn planes");
arbn->eqn = planes;
return BRLCAD_OK;
}
/* finds inside arbs */
static int
arbin(struct ged *gedp,
@@ -925,12 +973,22 @@ ged_inside_internal(struct ged *gedp, struct rt_db_internal *ip, int argc, const
if (ip->idb_type == ID_ARB8) {
/* find the comgeom arb type, & reorganize */
int uvec[8], svec[11];
int issues = 0;
struct bu_vls error_msg = BU_VLS_INIT_ZERO;
if (rt_arb_get_cgtype(&cgtype, (struct rt_arb_internal *)ip->idb_ptr, &wdbp->wdb_tol, uvec, svec) == 0) {
bu_vls_printf(gedp->ged_result_str, "%s: BAD ARB\n", o_name);
return BRLCAD_ERROR;
}
(void)rt_arb_validate(NULL,
(const struct rt_arb_internal *)ip->idb_ptr,
&wdbp->wdb_tol, &issues);
if (issues & RT_ARB_VALIDATE_CONCAVE) {
bu_vls_printf(gedp->ged_result_str,
"%s: inside requires a convex ARB; concave ARB8 solids are unsupported\n",
o_name);
return BRLCAD_ERROR;
}
/* must find new plane equations to account for
* any editing in the es_mat matrix or path to this solid.
@@ -1008,6 +1066,30 @@ ged_inside_internal(struct ged *gedp, struct rt_db_internal *ip, int argc, const
break;
}
case ID_ARBN: {
struct rt_arbn_internal *arbn =
(struct rt_arbn_internal *)ip->idb_ptr;
RT_ARBN_CK_MAGIC(arbn);
size_t supplied = argc > arg ? (size_t)(argc - arg) : 0;
if (supplied < arbn->neqn) {
bu_vls_printf(gedp->ged_result_str,
"Enter thickness for plane P%zu: ", supplied);
return GED_MORE;
}
fastf_t *arbn_thick = (fastf_t *)bu_calloc(arbn->neqn,
sizeof(fastf_t), "inside ARBN thicknesses");
for (i = 0; i < arbn->neqn; i++)
arbn_thick[i] = atof(argv[arg++]) * gedp->dbip->dbi_local2base;
int inside_result = arbnin(gedp, ip, arbn_thick, &wdbp->wdb_tol);
bu_free(arbn_thick, "inside ARBN thicknesses");
if (inside_result != BRLCAD_OK)
return BRLCAD_ERROR;
break;
}
case ID_TGC:
for (i = 0; i < 3; i++) {
if (argc < arg+1) {
+13
View File
@@ -1926,6 +1926,19 @@ densities_prep(struct ged *gedp, struct rt_i *rtip)
}
FOR_ALL_DIRECTORY_END;
}
// validation loop - all regions with a material ID should be in the density table
struct region *regp = REGION_NULL;
for (BU_LIST_FOR(regp, region, &(rtip->HeadRegion))) {
const long int material_id = regp->reg_gmater;
if (analyze_densities_density(_gd_densities, material_id) >= 0.0)
continue;
bu_vls_printf(gedp->ged_result_str,
"Error: Density %ld on region %s is not defined in the density table.\n", material_id, regp->reg_name);
analyze_densities_clear(_gd_densities);
return BRLCAD_ERROR;
}
}
return BRLCAD_OK;
+90 -70
View File
@@ -247,19 +247,41 @@ rt_in_rpp(struct xray *rp,
}
/* A name alone cannot distinguish transformed instances of a solid. */
static struct soltab *
rt_find_solid_with_mat(const struct rt_i *rtip,
const struct directory *dp, const mat_t mat)
{
struct soltab *stp;
RT_VISIT_ALL_SOLTABS_START(stp, (struct rt_i *)rtip) {
if (stp->st_dp != dp)
continue;
const fastf_t *st_mat = stp->st_matp ?
stp->st_matp : bn_mat_identity;
if (bn_mat_is_equal(mat, st_mat, &rtip->rti_tol))
return stp;
} RT_VISIT_ALL_SOLTABS_END;
return NULL;
}
/**
* Traverse the passed tree using rt_db_internals to show the way
* This function supports rt_bound_internal and is internal to librt
* Traverse the original database tree while tracking member transforms.
*
* Returns -
* 0 success
* -1 failure (tree_min and tree_max may have been altered)
* Returns 0 on success, -1 if no bounds can be computed.
*/
int
rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fastf_t *tree_max)
static int
rt_traverse_tree_mat(struct rt_i *rtip, const union tree *tp,
fastf_t *tree_min, fastf_t *tree_max,
const mat_t mat)
{
vect_t r_min, r_max; /* rpp for right side of tree */
if (!tp)
return -1;
RT_CK_TREE(tp);
switch (tp->tr_op) {
@@ -269,6 +291,8 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
const struct soltab *stp;
stp = tp->tr_a.tu_stp;
if (!stp)
return -1;
RT_CK_SOLTAB(stp);
if (stp->st_aradius <= 0) {
bu_log("rt_traverse_tree: encountered dead solid '%s'\n",
@@ -293,16 +317,20 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
case OP_XOR:
case OP_UNION:
/* BINARY type -- expand to contain both */
if (rt_traverse_tree(rtip, tp->tr_b.tb_left, tree_min, tree_max) < 0 ||
rt_traverse_tree(rtip, tp->tr_b.tb_right, r_min, r_max) < 0)
if (rt_traverse_tree_mat(rtip, tp->tr_b.tb_left,
tree_min, tree_max, mat) < 0 ||
rt_traverse_tree_mat(rtip, tp->tr_b.tb_right,
r_min, r_max, mat) < 0)
return -1;
VMIN(tree_min, r_min);
VMAX(tree_max, r_max);
break;
case OP_INTERSECT:
/* BINARY type -- find common area only */
if (rt_traverse_tree(rtip, tp->tr_b.tb_left, tree_min, tree_max) < 0 ||
rt_traverse_tree(rtip, tp->tr_b.tb_right, r_min, r_max) < 0)
if (rt_traverse_tree_mat(rtip, tp->tr_b.tb_left,
tree_min, tree_max, mat) < 0 ||
rt_traverse_tree_mat(rtip, tp->tr_b.tb_right,
r_min, r_max, mat) < 0)
return -1;
/* min = largest min, max = smallest max */
VMAX(tree_min, r_min);
@@ -313,7 +341,8 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
* rt_gettree's subtractor shaker may have already discarded a
* disjoint B (and its soltab) from the prepared tree, while this
* routine may be walking the original database tree. */
if (rt_traverse_tree(rtip, tp->tr_b.tb_left, tree_min, tree_max) < 0)
if (rt_traverse_tree_mat(rtip, tp->tr_b.tb_left,
tree_min, tree_max, mat) < 0)
return -1;
break;
@@ -324,6 +353,8 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
case OP_DB_LEAF:
{
const struct soltab *stp;
struct directory *dp;
mat_t leaf_mat;
if (rtip == NULL) {
bu_log("rt_traverse_tree: A valid rtip was not passed for calculating bounds of '%s'\n",
@@ -331,60 +362,44 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
return -1;
}
/* Good to go */
MAT_COPY(leaf_mat, mat);
if (tp->tr_l.tl_mat)
bn_mat_mul(leaf_mat, mat, tp->tr_l.tl_mat);
/* Attempt to get a solid pointer, will fail for combs */
stp = rt_find_solid(rtip, tp->tr_l.tl_name);
dp = db_lookup(rtip->rti_dbip, tp->tr_l.tl_name,
LOOKUP_QUIET);
if (dp == RT_DIR_NULL)
return -1;
stp = rt_find_solid_with_mat(rtip, dp, leaf_mat);
if (stp == NULL) {
/* It was a comb! get an internal format and
* repeat the whole thing that got us here in the
* 1st place
*/
/* Recurse through combinations using their original
* trees. A primitive without a matching prepared
* instance has no valid bounds. */
struct rt_db_internal intern;
struct directory *dp;
struct rt_comb_internal *combp;
/* Get the directory pointer */
if ((dp=db_lookup(rtip->rti_dbip, tp->tr_l.tl_name, LOOKUP_QUIET)) == RT_DIR_NULL) {
bu_log("rt_traverse_tree: db_lookup(%s) failed", tp->tr_l.tl_name);
return -1;
}
/* Why does recursion work with the internal
* format ? The internal format does have the
* boolean op for a comb in the tr_a.tu_op field
* in the root node, even though it has no prims
* at the leaves. So recursive calls to load the
* prim further down the tree, will return the
* correct bb as we are going through the proper
* switch case in each step down the tree
*/
if (!rt_db_lookup_internal(rtip->rti_dbip, tp->tr_l.tl_name, &dp, &intern, LOOKUP_NOISY)) {
bu_log("rt_traverse_tree: rt_db_lookup_internal(%s) failed to get the internal form",
tp->tr_l.tl_name);
return -1;
}
/* The passed rt_db_internal should be a comb,
* prepare a rt_comb_internal
*/
if (intern.idb_minor_type == ID_COMBINATION) {
combp = (struct rt_comb_internal *)intern.idb_ptr;
} else {
/* if it's not a comb, then something else is
* cooking.
*/
bu_log("rt_traverse_tree: WARNING : rt_db_lookup_internal(%s) got the internal form of a primitive when it should not, the bounds may not be correct", tp->tr_l.tl_name);
if (intern.idb_minor_type != ID_COMBINATION) {
bu_log("rt_traverse_tree: no prepared instance"
" for '%s'\n", tp->tr_l.tl_name);
rt_db_free_internal(&intern);
return -1;
}
combp = (struct rt_comb_internal *)intern.idb_ptr;
RT_CK_COMB(combp);
/* further down the rabbit hole */
if (rt_traverse_tree(rtip, combp->tree, tree_min, tree_max)) {
bu_log("rt_traverse_tree: rt_bound_tree() failed\n");
int ret = rt_traverse_tree_mat(rtip, combp->tree,
tree_min, tree_max, leaf_mat);
rt_db_free_internal(&intern);
if (ret)
return -1;
}
} else {
/* Got a solid pointer, get bounds and return */
RT_CK_SOLTAB(stp);
@@ -408,13 +423,24 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
}
case OP_NOP:
/* Implies that this tree has nothing in it */
break;
/* An empty tree has no bounds to return. */
return -1;
}
return 0;
}
int
rt_traverse_tree(struct rt_i *rtip, const union tree *tp,
fastf_t *tree_min, fastf_t *tree_max)
{
mat_t mat;
MAT_IDN(mat);
return rt_traverse_tree_mat(rtip, tp, tree_min, tree_max, mat);
}
int
rt_bound_instance(point_t *bmin, point_t *bmax,
struct directory *dp,
@@ -472,7 +498,8 @@ rt_bound_internal(struct db_i *dbip, struct directory *dp,
struct rt_db_internal intern;
struct rt_comb_internal *combp;
vect_t tree_min, tree_max;
union tree *tp;
const union tree *tp;
union tree primitive_tree;
/* Initialize RPP bounds */
VSETALL(rpp_min, MAX_FASTF);
@@ -494,7 +521,8 @@ rt_bound_internal(struct db_i *dbip, struct directory *dp,
if (!rt_db_lookup_internal(dbip, dp->d_namep, &dp, &intern, LOOKUP_NOISY)) {
bu_exit(1, "rt_bound_internal: rt_db_lookup_internal(%s) failed to get the internal form", dp->d_namep);
bu_log("rt_bound_internal: lookup failed for '%s'\n",
dp->d_namep);
rt_i_destroy(rtip);
return -1;
}
@@ -504,26 +532,18 @@ rt_bound_internal(struct db_i *dbip, struct directory *dp,
*/
if (intern.idb_minor_type == ID_COMBINATION) {
combp = (struct rt_comb_internal *)intern.idb_ptr;
RT_CK_COMB(combp);
tp = combp->tree;
} else {
/* A primitive was passed, construct a struct rt_comb_internal
* with a single leaf node.
*/
BU_ALLOC(combp, struct rt_comb_internal);
RT_COMB_INTERNAL_INIT(combp);
combp->region_flag = 0;
BU_GET(tp, union tree);
RT_TREE_INIT(tp);
tp->tr_l.tl_op = OP_SOLID;
tp->tr_l.tl_name = bu_strdup("dummy");
tp->tr_l.tl_mat = (matp_t)NULL;
tp->tr_a.tu_stp = rt_find_solid(rtip, dp->d_namep);
combp->tree = tp;
/* A primitive uses its prepared solid directly. */
RT_TREE_INIT(&primitive_tree);
primitive_tree.tr_l.tl_op = OP_SOLID;
primitive_tree.tr_a.tu_stp = rt_find_solid(rtip, dp->d_namep);
tp = &primitive_tree;
}
RT_CK_COMB(combp);
if (rt_traverse_tree(rtip, combp->tree, tree_min, tree_max)) {
bu_log("rt_bound_internal: rt_bound_tree() failed\n");
if (rt_traverse_tree(rtip, tp, tree_min, tree_max)) {
bu_log("rt_bound_internal: tree traversal failed\n");
rt_db_free_internal(&intern);
rt_i_destroy(rtip);
return -1;
+17 -4
View File
@@ -341,7 +341,7 @@ cache_init(struct rt_cache *cache)
}
static void
static int
compress_external(const struct rt_cache *cache, struct bu_external *external)
{
int ret;
@@ -352,8 +352,16 @@ compress_external(const struct rt_cache *cache, struct bu_external *external)
BU_CK_EXTERNAL(external);
BU_ASSERT(external->ext_nbytes < INT_MAX);
if (external->ext_nbytes >= INT_MAX) {
CACHE_DEBUG("++++++ [%lu.%lu] Compression input is too large (%zu bytes)\n", bu_pid(), bu_parallel_id(), external->ext_nbytes);
return 0;
}
compressed_size = brl_LZ4_compressBound((int)external->ext_nbytes);
if (compressed_size <= 0) {
CACHE_DEBUG("++++++ [%lu.%lu] Compression input is unsupported (%zu bytes)\n", bu_pid(), bu_parallel_id(), external->ext_nbytes);
return 0;
}
/* buffer is uncompsize + maxcompsize + compressed_data */
buffer = (uint8_t *)bu_malloc((size_t)compressed_size + SIZEOF_NETWORK_LONG, "buffer");
@@ -367,12 +375,14 @@ compress_external(const struct rt_cache *cache, struct bu_external *external)
if (!compressed) {
CACHE_DEBUG("++++++ [%lu.%lu] Compression failed (ret %d, %zu bytes @ %p to %d bytes max)\n", bu_pid(), bu_parallel_id(), ret, external->ext_nbytes, (void *) external->ext_buf, compressed_size);
return;
bu_free(buffer, "buffer");
return 0;
}
bu_free(external->ext_buf, "ext_buf");
external->ext_nbytes = compressed_size + SIZEOF_NETWORK_LONG;
external->ext_buf = buffer;
return 1;
}
@@ -569,7 +579,10 @@ cache_try_store(struct rt_cache *cache, const char *name, const struct rt_db_int
return 0; /* can't serialize */
}
compress_external(cache, &data_external);
if (!compress_external(cache, &data_external)) {
bu_free_external(&data_external);
return 0;
}
{
struct bu_attribute_value_set attributes = BU_AVS_INIT_ZERO;
+61 -23
View File
@@ -1107,6 +1107,14 @@ rt_knob_edit_sca(struct rt_edit *s, int matrix_edit)
}
}
static void
rt_edit_clear_input(struct rt_edit *s)
{
s->e_inpara = 0;
s->e_mvalid = 0;
s->es_scale = 0.0;
}
/*
* A great deal of magic takes place here, to accomplish solid editing.
*
@@ -1122,7 +1130,20 @@ rt_edit_process(struct rt_edit *s)
bu_clbk_t f = NULL;
void *d = NULL;
++s->update_views;
if (!s)
return BRLCAD_ERROR;
const int type = s->es_int.idb_type;
if (type <= ID_NULL || type > ID_MAX_SOLID ||
!s->es_int.idb_ptr || EDOBJ[type].magic != RT_FUNCTAB_MAGIC) {
bu_vls_printf(s->log_str, "rt_edit_process: no editable solid\n");
rt_edit_clear_input(s);
return BRLCAD_ERROR;
}
const int prior_update_views = s->update_views;
if (!s->update_views)
s->update_views = 1;
int had_method = 0;
const struct rt_db_internal *ip = &s->es_int;
@@ -1134,6 +1155,8 @@ rt_edit_process(struct rt_edit *s)
if (f)
(*f)(0, NULL, d, NULL);
}
s->update_views = prior_update_views;
rt_edit_clear_input(s);
return BRLCAD_ERROR;
}
if (bu_vls_strlen(s->log_str)) {
@@ -1149,7 +1172,7 @@ rt_edit_process(struct rt_edit *s)
case RT_EDIT_IDLE:
/* do nothing more */
--s->update_views;
s->update_views = prior_update_views;
break;
default:
{
@@ -1166,6 +1189,8 @@ rt_edit_process(struct rt_edit *s)
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
s->update_views = prior_update_views;
rt_edit_clear_input(s);
return BRLCAD_ERROR;
}
}
@@ -1196,9 +1221,7 @@ rt_edit_process(struct rt_edit *s)
}
// Inputs processed, reset
s->e_inpara = 0;
s->e_mvalid = 0;
s->es_scale = 0.0;
rt_edit_clear_input(s);
return BRLCAD_OK;
}
@@ -1221,15 +1244,15 @@ rt_edit_checkpoint(struct rt_edit *s)
RT_CK_DB_INTERNAL(&s->es_int);
/* Release any previous snapshot */
bu_free_external(&s->es_ckpt);
BU_EXTERNAL_INIT(&s->es_ckpt);
if (rt_obj_export(&s->es_ckpt, &s->es_int, 1.0, s->dbip) < 0) {
struct bu_external candidate = BU_EXTERNAL_INIT_ZERO;
if (rt_obj_export(&candidate, &s->es_int, 1.0, s->dbip) < 0) {
bu_free_external(&candidate);
bu_vls_printf(s->log_str, "rt_edit_checkpoint: export failed\n");
return BRLCAD_ERROR;
}
bu_free_external(&s->es_ckpt);
s->es_ckpt = candidate;
return BRLCAD_OK;
}
@@ -1245,26 +1268,41 @@ rt_edit_revert(struct rt_edit *s)
return BRLCAD_ERROR;
}
int type = s->es_int.idb_type;
/* Release current contents */
rt_db_free_internal(&s->es_int);
RT_DB_INTERNAL_INIT(&s->es_int);
/* rt_obj_import dispatches on ip->idb_minor_type, which RT_DB_INTERNAL_INIT
* resets to -1. Restore the saved type so the right ft_importN is called. */
s->es_int.idb_minor_type = type;
const int type = s->es_int.idb_type;
if (type <= ID_NULL || type > ID_MAX_SOLID || !s->es_int.idb_ptr) {
bu_vls_printf(s->log_str, "rt_edit_revert: no editable solid\n");
return BRLCAD_ERROR;
}
struct rt_db_internal restored;
RT_DB_INTERNAL_INIT(&restored);
restored.idb_minor_type = type;
mat_t identity;
MAT_IDN(identity);
if (rt_obj_import(&s->es_int, &s->es_ckpt, identity, s->dbip) < 0) {
int import_result = rt_obj_import(&restored, &s->es_ckpt, identity, s->dbip);
/* SPH and REC import through the ELL and TGC internal formats. */
bool compatible_type = restored.idb_type == type ||
(type == ID_SPH && restored.idb_type == ID_ELL) ||
(type == ID_REC && restored.idb_type == ID_TGC);
if (import_result < 0 || !compatible_type || !restored.idb_ptr) {
rt_db_free_internal(&restored);
bu_vls_printf(s->log_str, "rt_edit_revert: import failed\n");
return BRLCAD_ERROR;
}
/* If the type changed for some reason (shouldn't happen), keep the original */
if (s->es_int.idb_type != type)
s->es_int.idb_type = type;
restored.idb_type = type;
restored.idb_meth = &OBJ[type];
if (s->ipe_ptr && EDOBJ[type].ft_prim_edit_reset)
(*EDOBJ[type].ft_prim_edit_reset)(s);
rt_db_free_internal(&s->es_int);
s->es_int = restored;
rt_edit_clear_input(s);
s->acc_sc_sol = 1.0;
if (!s->e_keyfixed) {
s->e_keytag = "";
rt_get_solid_keypoint(s, &s->e_keypoint, &s->e_keytag, s->e_mat);
}
return BRLCAD_OK;
}
+1
View File
@@ -211,6 +211,7 @@ extern void flip_dbmat_mat(dbfloat_t *dbp, const fastf_t *ff);
*/
extern fastf_t ell_angle(fastf_t *p1, fastf_t a, fastf_t b, fastf_t dtol, fastf_t ntol);
int _rt_rtcmp_capture_ready(void);
/* Append an rtcmp record for a ray with final partitions. */
void _rt_rtcmp_capture(const struct application *ap, const struct partition *parts);
/* Optional per-ray primitive capture; state belongs to the caller until finish. */
+9 -9
View File
@@ -3277,26 +3277,23 @@ int
rt_arb_calc_points(struct rt_arb_internal *arb, int cgtype, const plane_t planes[6], const struct bn_tol *tol)
{
int i;
point_t pt[8];
struct rt_arb_internal candidate;
RT_ARB_CK_MAGIC(arb);
candidate = *arb;
/* find new points for entire solid */
for (i = 0; i < 8; i++) {
if (rt_arb_3face_intersect(pt[i], planes, cgtype, i*3) < 0) {
if (rt_arb_3face_intersect(candidate.pt[i], planes, cgtype, i*3) < 0) {
bu_log("rt_arb_calc_points: Intersection of planes fails %d\n", i);
return -1; /* FAIL */
}
}
/* Move new points to arb tol->dist))*/
for (i = 0; i < 8; i++) {
VMOVE(arb->pt[i], pt[i]);
}
if (rt_arb_check_points(arb, cgtype, tol) < 0)
if (rt_arb_check_points(&candidate, cgtype, tol) < 0)
return -1;
memcpy(arb->pt, candidate.pt, sizeof(arb->pt));
return 0; /* success */
}
@@ -3397,6 +3394,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
const struct bn_tol *tol)
{
const int arb_faces[5][24] = rt_arb_faces;
plane_t candidate[6];
/* ARB4 at location 0, ARB5 at 1, etc. */
int type = cgtype - ARB4;
@@ -3405,6 +3403,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
RT_ARB_CK_MAGIC(arb);
BN_CK_TOL(tol);
memcpy(candidate, planes, sizeof(candidate));
for (int i = 0; i < 6; i++) {
if (arb_faces[type][i*4] == -1)
@@ -3414,7 +3413,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
int p2 = arb_faces[type][i*4+1];
int p3 = arb_faces[type][i*4+2];
if (bg_make_plane_3pnts(planes[i],
if (bg_make_plane_3pnts(candidate[i],
arb->pt[p1],
arb->pt[p2],
arb->pt[p3],
@@ -3425,6 +3424,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
}
}
memcpy(planes, candidate, sizeof(candidate));
return 0;
}
+126 -64
View File
@@ -1169,6 +1169,17 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
return 1;
}
/* Intersections and plane updates can fail after changing several points. */
struct rt_arb_internal *original_arb = arb;
plane_t *original_planes = planes;
struct rt_arb_internal candidate = *arb;
plane_t candidate_planes[6];
vect_t edit_position;
memcpy(candidate_planes, planes, sizeof(candidate_planes));
VMOVE(edit_position, pos_model);
arb = &candidate;
planes = candidate_planes;
/* set the pointer */
switch (arb_type) {
case ARB4:
@@ -1246,7 +1257,7 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
/* do the arb editing */
if (edit_class == RT_ARB_EDIT_POINT) {
/* moving a point - not an edge */
VMOVE(arb->pt[edit_type], pos_model);
VMOVE(arb->pt[edit_type], edit_position);
edptr += 4;
} else if (edit_class == RT_ARB_EDIT_EDGE) {
vect_t edge_dir;
@@ -1256,8 +1267,8 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
pt2 = *edptr++;
if (flags & RT_ARB_EDIT_EDGE_DIR) {
VMOVE(edge_dir, pos_model);
VMOVE(pos_model, arb->pt[pt1]);
VMOVE(edge_dir, edit_position);
VMOVE(edit_position, arb->pt[pt1]);
} else {
/* calculate edge direction */
VSUB2(edge_dir, arb->pt[pt2], arb->pt[pt1]);
@@ -1271,7 +1282,7 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
bp2 = *edptr++;
/* move the edge */
if (rt_arb_move_edge(error_msg_ret, arb, pos_model, bp1, bp2, pt1, pt2,
if (rt_arb_move_edge(error_msg_ret, arb, edit_position, bp1, bp2, pt1, pt2,
edge_dir, planes, tol))
goto err;
}
@@ -1377,6 +1388,8 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
goto err;
}
memcpy(original_arb->pt, candidate.pt, sizeof(candidate.pt));
memcpy(original_planes, candidate_planes, sizeof(candidate_planes));
return 0; /* OK */
err:
@@ -1501,6 +1514,48 @@ edarb_canonicalize(struct rt_edit *s, struct rt_arb_internal *arb)
return BRLCAD_OK;
}
static int
arb_move_face_to_point(struct rt_edit *s, const point_t target)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
struct rt_arb_internal candidate;
plane_t planes[6];
struct bu_vls error_msg = BU_VLS_INIT_ZERO;
int arb_type;
RT_ARB_CK_MAGIC(arb);
if (a->edit_menu < 0 || a->edit_menu >= ARB8_FACE_COUNT) {
bu_vls_printf(s->log_str, "Invalid ARB face index %d\n", a->edit_menu);
return BRLCAD_ERROR;
}
candidate = *arb;
memcpy(planes, a->es_peqn, sizeof(planes));
planes[a->edit_menu][W] = VDOT(planes[a->edit_menu], target);
arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (!arb_type)
return BRLCAD_ERROR;
if (rt_arb_calc_points(&candidate, arb_type, (const plane_t *)planes, s->tol)) {
bu_vls_printf(s->log_str, "Cannot calculate ARB points\n");
return BRLCAD_ERROR;
}
if (edarb_canonicalize(s, &candidate) != BRLCAD_OK)
return BRLCAD_ERROR;
if (rt_arb_calc_planes(&error_msg, &candidate, arb_type, planes, s->tol)) {
bu_vls_printf(s->log_str, "%s", bu_vls_cstr(&error_msg));
bu_vls_free(&error_msg);
return BRLCAD_ERROR;
}
bu_vls_free(&error_msg);
memcpy(arb->pt, candidate.pt, sizeof(arb->pt));
memcpy(a->es_peqn, planes, sizeof(planes));
return BRLCAD_OK;
}
static int
arb_numeric_target(point_t target, struct rt_edit *s, int *edit_menu)
{
@@ -1535,38 +1590,13 @@ ecmd_arb_move_face(struct rt_edit *s)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
/* move face through definite point */
if (s->e_inpara) {
if (!s->e_inpara)
return BRLCAD_OK;
vect_t work;
if (arb_numeric_target(work, s, &a->edit_menu) != BRLCAD_OK)
return BRLCAD_ERROR;
if (a->edit_menu < 0 || a->edit_menu >= ARB8_FACE_COUNT) {
bu_vls_printf(s->log_str, "Invalid ARB face index %d\n", a->edit_menu);
return BRLCAD_ERROR;
}
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
RT_ARB_CK_MAGIC(arb);
/* change D of planar equation */
a->es_peqn[a->edit_menu][W]=VDOT(&a->es_peqn[a->edit_menu][0], work);
/* find new vertices, put in record in vector notation */
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
if (rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol)) {
bu_vls_printf(s->log_str, "Cannot calculate ARB8 points\n");
return BRLCAD_ERROR;
}
if (edarb_canonicalize(s, arb) != BRLCAD_OK)
return BRLCAD_ERROR;
}
return 0;
point_t target;
if (arb_numeric_target(target, s, &a->edit_menu) != BRLCAD_OK)
return BRLCAD_ERROR;
return arb_move_face_to_point(s, target);
}
void
@@ -1757,11 +1787,16 @@ ecmd_arb_rotate_face(struct rt_edit *s)
if (arb_type == 0)
return BRLCAD_ERROR;
(void)rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol);
if (rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol)) {
bu_vls_printf(s->log_str, "Cannot calculate ARB points\n");
return BRLCAD_ERROR;
}
if (edarb_canonicalize(s, arb) != BRLCAD_OK) {
return BRLCAD_ERROR;
}
if (rt_arb_calc_planes(s->log_str, arb, arb_type, a->es_peqn, s->tol))
return BRLCAD_ERROR;
MAT_IDN(s->incr_change);
s->e_inpara = 0;
@@ -1850,7 +1885,6 @@ arb_mouse_move_element(struct rt_edit *s, const vect_t mousevec)
static int
edarb_move_face_mousevec(struct rt_edit *s, const vect_t mousevec)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
vect_t pos_view = VINIT_ZERO; /* Unrotated view space pos */
vect_t temp = VINIT_ZERO;
vect_t pos_model = VINIT_ZERO; /* Rotated screen space pos */
@@ -1859,24 +1893,7 @@ edarb_move_face_mousevec(struct rt_edit *s, const vect_t mousevec)
pos_view[Y] = mousevec[Y];
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(pos_model, s->e_invmat, temp);
if (a->edit_menu < 0 || a->edit_menu >= ARB8_FACE_COUNT) {
bu_vls_printf(s->log_str, "Invalid ARB face index %d\n", a->edit_menu);
return BRLCAD_ERROR;
}
/* change D of planar equation */
a->es_peqn[a->edit_menu][W]=VDOT(&a->es_peqn[a->edit_menu][0], pos_model);
struct rt_arb_internal *arb =
(struct rt_arb_internal *)s->es_int.idb_ptr;
RT_ARB_CK_MAGIC(arb);
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
if (rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol)) {
bu_vls_printf(s->log_str, "Cannot calculate ARB8 points\n");
return BRLCAD_ERROR;
}
if (edarb_canonicalize(s, arb) != BRLCAD_OK)
if (arb_move_face_to_point(s, pos_model) != BRLCAD_OK)
return BRLCAD_ERROR;
edit_abs_tra(s, pos_view);
return BRLCAD_OK;
@@ -1889,13 +1906,27 @@ rt_edit_arb_edit(struct rt_edit *s)
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
RT_ARB_CK_MAGIC(arb);
struct rt_arb_internal original_arb = *arb;
plane_t original_planes[6];
memcpy(original_planes, a->es_peqn, sizeof(original_planes));
int ret = 0;
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
if (rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol)) {
bu_vls_printf(s->log_str, "\nCannot calculate plane equations for ARB8\n");
int validation_issues = 0;
(void)rt_arb_validate(NULL, arb, s->tol, &validation_issues);
int planes_valid = !(validation_issues & RT_ARB_VALIDATE_NONCOPLANAR) &&
!rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol);
int planes_required = s->edit_flag == ECMD_ARB_MOVE_FACE ||
s->edit_flag == ECMD_ARB_SETUP_ROTFACE ||
s->edit_flag == ECMD_ARB_ROTATE_FACE ||
s->edit_flag == PTARB || s->edit_flag == EARB;
if (!planes_valid && planes_required) {
bu_vls_printf(s->log_str,
"Cannot edit ARB faces or edges without valid face planes: %s",
bu_vls_cstr(&error_msg));
bu_vls_free(&error_msg);
return BRLCAD_ERROR;
}
@@ -1917,22 +1948,38 @@ rt_edit_arb_edit(struct rt_edit *s)
break;
case ECMD_ARB_MAIN_MENU:
ecmd_arb_main_menu(s);
break;
return BRLCAD_OK;
case ECMD_ARB_SPECIFIC_MENU:
if (ecmd_arb_specific_menu(s) != BRLCAD_OK)
return -1;
break;
return BRLCAD_OK;
case ECMD_ARB_MOVE_FACE:
return ecmd_arb_move_face(s);
case ECMD_ARB_SETUP_ROTFACE:
ecmd_arb_setup_rotface(s);
break;
case ECMD_ARB_ROTATE_FACE:
{
mat_t original_acc_rot, original_model_changes, original_incr_change;
int original_menu = a->edit_menu;
int original_fixv = a->fixv;
MAT_COPY(original_acc_rot, s->acc_rot_sol);
MAT_COPY(original_model_changes, s->model_changes);
MAT_COPY(original_incr_change, s->incr_change);
ret = ecmd_arb_rotate_face(s);
if (ret)
if (ret) {
memcpy(arb->pt, original_arb.pt, sizeof(arb->pt));
memcpy(a->es_peqn, original_planes, sizeof(original_planes));
MAT_COPY(s->acc_rot_sol, original_acc_rot);
MAT_COPY(s->model_changes, original_model_changes);
MAT_COPY(s->incr_change, original_incr_change);
a->edit_menu = original_menu;
a->fixv = original_fixv;
return ret;
}
/* The plane is current; bypass arb_planecalc below. */
return BRLCAD_OK;
}
case PTARB: /* move an ARB point */
case EARB: /* edit an ARB edge */
return edit_arb_element(s);
@@ -1942,19 +1989,34 @@ rt_edit_arb_edit(struct rt_edit *s)
arb_planecalc:
if (ret != BRLCAD_OK)
goto arb_failed;
/* Rigid and uniform whole-solid transforms preserve an invalid ARB's
* shape. They must remain usable even when face-specific editing cannot
* construct a complete set of planes. */
if (!planes_valid)
return ret;
if (edarb_canonicalize(s, arb) != BRLCAD_OK) {
return BRLCAD_ERROR;
goto arb_failed;
}
/* must re-calculate the face plane equations for arbs */
arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
if (rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol) < 0)
goto arb_failed;
if (rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol) < 0) {
bu_vls_printf(s->log_str, "%s", bu_vls_cstr(&error_msg));
bu_vls_free(&error_msg);
goto arb_failed;
}
bu_vls_free(&error_msg);
return ret;
arb_failed:
memcpy(arb->pt, original_arb.pt, sizeof(arb->pt));
memcpy(a->es_peqn, original_planes, sizeof(original_planes));
return BRLCAD_ERROR;
}
C_DECL int
+31 -16
View File
@@ -235,6 +235,7 @@ rt_edit_bot_labels(
VADD3(mid_pt, p1, p2, p3);
VSCALE(mid_pt, mid_pt, one_third);
POINT_LABEL_STR(mid_pt, "face");
*num_lines = 3;
VMOVE(lines[0], mid_pt);
@@ -608,6 +609,21 @@ ecmd_bot_movev_list(struct rt_edit *s)
* triangles that share the new vertex.
* No e_para needed.
*/
static int
bot_face_edge_other(const int *face, int v0, int v1)
{
if ((face[0] == v0 && face[1] == v1) ||
(face[0] == v1 && face[1] == v0))
return face[2];
if ((face[1] == v0 && face[2] == v1) ||
(face[1] == v1 && face[2] == v0))
return face[0];
if ((face[2] == v0 && face[0] == v1) ||
(face[2] == v1 && face[0] == v0))
return face[1];
return -1;
}
static int
ecmd_bot_esplit(struct rt_edit *s)
{
@@ -618,13 +634,25 @@ ecmd_bot_esplit(struct rt_edit *s)
int v0 = b->bot_verts[0];
int v1 = b->bot_verts[1];
if (v0 < 0 || v1 < 0 || b->bot_verts[2] >= 0) {
if (v0 < 0 || v1 < 0 || v0 == v1 ||
(size_t)v0 >= bot->num_vertices || (size_t)v1 >= bot->num_vertices ||
b->bot_verts[2] >= 0) {
bu_vls_printf(s->log_str,
"ERROR: ECMD_BOT_ESPLIT requires a single edge selection "
"(ECMD_BOT_PICKE first)\n");
return BRLCAD_ERROR;
}
size_t incident_faces = 0;
for (size_t fi = 0; fi < bot->num_faces; fi++) {
if (bot_face_edge_other(&bot->faces[fi * 3], v0, v1) >= 0)
incident_faces++;
}
if (!incident_faces) {
bu_vls_printf(s->log_str, "ERROR: selected BOT edge was not found\n");
return BRLCAD_ERROR;
}
/* Compute midpoint */
point_t mid;
VADD2SCALE(mid, &bot->vertices[v0*3], &bot->vertices[v1*3], 0.5);
@@ -642,19 +670,7 @@ ecmd_bot_esplit(struct rt_edit *s)
(size_t *)bu_malloc(orig_nf * sizeof(size_t), "split BOT face parents") :
NULL;
for (size_t fi = 0; fi < orig_nf; fi++) {
int f0 = bot->faces[fi*3];
int f1 = bot->faces[fi*3+1];
int f2 = bot->faces[fi*3+2];
/* Check whether this face contains the directed or reverse edge */
int other = -1;
if ((f0 == v0 && f1 == v1) || (f0 == v1 && f1 == v0)) {
other = f2;
} else if ((f1 == v0 && f2 == v1) || (f1 == v1 && f2 == v0)) {
other = f0;
} else if ((f2 == v0 && f0 == v1) || (f2 == v1 && f0 == v0)) {
other = f1;
}
int other = bot_face_edge_other(&bot->faces[fi * 3], v0, v1);
if (other < 0)
continue; /* edge not in this face */
@@ -1099,8 +1115,7 @@ ecmd_bot_pickt(struct rt_edit *s, const vect_t mousevec)
b->bot_verts[1] = -1;
b->bot_verts[2] = -1;
bu_vls_free(&vls);
}
if (hits == 1) {
} else if (hits == 1) {
sscanf(bu_vls_cstr(&vls), " { { %d %d %d", &b->bot_verts[0], &b->bot_verts[1], &b->bot_verts[2]);
bu_vls_free(&vls);
} else {
+14 -11
View File
@@ -174,7 +174,7 @@ cline_scale(struct rt_edit *s)
int
ecmd_cline_move_h(struct rt_edit *s)
{
vect_t work;
vect_t work, height;
point_t model_point;
struct rt_cline_internal *cli =
(struct rt_cline_internal *)s->es_int.idb_ptr;
@@ -182,6 +182,7 @@ ecmd_cline_move_h(struct rt_edit *s)
void *d = NULL;
RT_CLINE_CK_MAGIC(cli);
VMOVE(height, cli->h);
if (s->e_inpara) {
if (s->e_inpara != 3) {
@@ -194,24 +195,22 @@ ecmd_cline_move_h(struct rt_edit *s)
if (s->mv_context) {
MAT4X3PNT(work, s->e_invmat, model_point);
VSUB2(cli->h, work, cli->v);
VSUB2(height, work, cli->v);
} else
VSUB2(cli->h, model_point, cli->v);
VSUB2(height, model_point, cli->v);
}
/* check for zero H vector */
if (MAGNITUDE(cli->h) <= SQRT_SMALL_FASTF) {
bu_vls_printf(s->log_str, "Zero H vector not allowed, resetting to +Z\n");
if (edit_validate_height(s, height) != BRLCAD_OK) {
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
VSET(cli->h, 0.0, 0.0, 1.0);
return BRLCAD_ERROR;
}
VMOVE(cli->h, height);
return 0;
}
void
static int
ecmd_cline_move_h_mousevec(struct rt_edit *s, const vect_t mousevec)
{
vect_t pos_view = VINIT_ZERO; /* Unrotated view space pos */
@@ -228,8 +227,13 @@ ecmd_cline_move_h_mousevec(struct rt_edit *s, const vect_t mousevec)
/* Do NOT change pos_view[Z] ! */
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(tr_temp, s->e_invmat, temp);
VSUB2(cli->h, tr_temp, cli->v);
vect_t height;
VSUB2(height, tr_temp, cli->v);
if (edit_validate_height(s, height) != BRLCAD_OK)
return BRLCAD_ERROR;
VMOVE(cli->h, height);
edit_abs_tra(s, pos_view);
return BRLCAD_OK;
}
C_DECL int
@@ -279,8 +283,7 @@ rt_edit_cline_edit_xy(
edit_stra_xy(&pos_view, s, mousevec);
break;
case ECMD_CLINE_MOVE_H:
ecmd_cline_move_h_mousevec(s, mousevec);
return BRLCAD_OK;
return ecmd_cline_move_h_mousevec(s, mousevec);
default:
return edit_generic_xy(s, mousevec);
}
+65 -9
View File
@@ -114,6 +114,35 @@ edit_param_read_scalar(fastf_t *out, char **cursor, const char *label,
return isfinite(*out) ? BRLCAD_OK : BRLCAD_ERROR;
}
int
edit_param_read_fields(const char *input, const struct edit_param_field *fields,
size_t field_count)
{
if (!input || !fields || !field_count)
return BRLCAD_ERROR;
char *buffer = bu_strdup(input);
char *cursor = buffer;
int result = BRLCAD_OK;
for (size_t i = 0; i < field_count; i++) {
const struct edit_param_field *field = &fields[i];
if (field->count == ELEMENTS_PER_VECT)
result = edit_param_read_vector(field->value, &cursor,
field->label, field->conversion);
else if (field->count == 1)
result = edit_param_read_scalar(field->value, &cursor,
field->label, field->conversion);
else
result = BRLCAD_ERROR;
if (result != BRLCAD_OK)
break;
}
if (result == BRLCAD_OK && edit_param_next_line(&cursor))
result = BRLCAD_ERROR;
bu_free(buffer, "primitive parameter text");
return result;
}
int
edit_repair_parse_options(struct bu_vls *log_str, int argc,
const char **argv, const struct bu_opt_desc *options)
@@ -194,6 +223,7 @@ edit_sscale(struct rt_edit *s)
{
mat_t mat, mat1, scalemat;
struct rt_db_internal *ip = &s->es_int;
fastf_t scale = s->es_scale;
if (s->e_inpara > 1) {
bu_vls_printf(s->log_str, "ERROR: only one argument needed\n");
@@ -202,23 +232,37 @@ edit_sscale(struct rt_edit *s)
}
if (s->e_inpara) {
/* accumulate the scale factor */
s->es_scale = s->e_para[0] / s->acc_sc_sol;
s->acc_sc_sol = s->e_para[0];
if (!isfinite(s->e_para[0]) || s->e_para[0] <= 0.0 ||
!isfinite(s->acc_sc_sol) || s->acc_sc_sol <= 0.0) {
bu_vls_printf(s->log_str, "ERROR: scale must be finite and positive\n");
return BRLCAD_ERROR;
}
scale = s->e_para[0] / s->acc_sc_sol;
}
/* No pending scale operation — nothing to apply. */
if (!s->e_inpara && s->es_scale < SMALL_FASTF)
if (!s->e_inpara && ZERO(scale))
return 0;
bn_mat_scale_about_pnt(scalemat, s->e_keypoint, s->es_scale);
if (!isfinite(scale) || scale <= 0.0 ||
!OBJ[ip->idb_type].ft_mat ||
bn_mat_scale_about_pnt(scalemat, s->e_keypoint, scale)) {
bu_vls_printf(s->log_str, "ERROR: cannot apply solid scale\n");
return BRLCAD_ERROR;
}
bn_mat_mul(mat1, scalemat, s->e_mat);
bn_mat_mul(mat, s->e_invmat, mat1);
if (OBJ[ip->idb_type].ft_mat)
(*OBJ[ip->idb_type].ft_mat)(ip, mat, ip);
if ((*OBJ[ip->idb_type].ft_mat)(ip, mat, ip)) {
bu_vls_printf(s->log_str, "ERROR: solid scale failed\n");
return BRLCAD_ERROR;
}
/* reset solid scale factor */
s->es_scale = 1.0;
if (s->e_inpara)
s->acc_sc_sol = s->e_para[0];
return 0;
}
@@ -312,6 +356,19 @@ edit_scale_equal_axes(struct rt_edit *s, vect_t a, vect_t b, vect_t c)
return BRLCAD_OK;
}
int
edit_validate_height(struct rt_edit *s, const vect_t height)
{
fastf_t length = MAGNITUDE(height);
fastf_t min_length = s->tol ? fmax(s->tol->dist, SQRT_SMALL_FASTF) :
SQRT_SMALL_FASTF;
if (!isfinite(length) || length <= min_length) {
bu_vls_printf(s->log_str, "Zero or invalid H vector not allowed\n");
return BRLCAD_ERROR;
}
return BRLCAD_OK;
}
void
edit_stra(struct rt_edit *s)
{
@@ -467,8 +524,7 @@ edit_generic(
switch (s->edit_flag) {
case RT_PARAMS_EDIT_SCALE:
/* scale the solid uniformly about its vertex point */
edit_sscale(s);
return BRLCAD_OK;
return edit_sscale(s);
case RT_PARAMS_EDIT_TRANS:
/* translate solid */
edit_stra(s);
+15
View File
@@ -67,6 +67,10 @@ edit_scale_length(struct rt_edit *s, vect_t *axis, fastf_t *scalar);
int
edit_scale_equal_axes(struct rt_edit *s, vect_t a, vect_t b, vect_t c);
/* Reject height endpoint edits that would collapse the primitive. */
int
edit_validate_height(struct rt_edit *s, const vect_t height);
/* Validate dimension counts before converting numeric edit parameters. */
int
edit_parse_sample_count(uint32_t *count, fastf_t value);
@@ -95,6 +99,17 @@ int
edit_param_read_scalar(fastf_t *out, char **cursor, const char *label,
fastf_t local2base);
struct edit_param_field {
const char *label;
fastf_t *value;
int count;
fastf_t conversion;
};
int
edit_param_read_fields(const char *text, const struct edit_param_field *fields,
size_t field_count);
/* translate solid */
void
edit_stra(struct rt_edit *s);
+13 -75
View File
@@ -223,16 +223,6 @@ rt_edit_ehy_write_params(
bu_vls_printf(p, "Dist to asymptotes: %.9f\n", ehy->ehy_c * base2local);
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_ehy_read_params(
struct rt_db_internal *ip,
@@ -241,74 +231,22 @@ rt_edit_ehy_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_ehy_internal *ehy = (struct rt_ehy_internal *)ip->idb_ptr;
RT_EHY_CK_MAGIC(ehy);
if (!fc)
struct rt_ehy_internal candidate = *ehy;
const struct edit_param_field fields[] = {
{"Vertex", candidate.ehy_V, ELEMENTS_PER_VECT, local2base},
{"Height", candidate.ehy_H, ELEMENTS_PER_VECT, local2base},
{"Semi-major axis", candidate.ehy_Au, ELEMENTS_PER_VECT, 1.0},
{"Semi-major length", &candidate.ehy_r1, 1, local2base},
{"Semi-minor length", &candidate.ehy_r2, 1, local2base},
{"Dist to asymptotes", &candidate.ehy_c, 1, local2base}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK ||
ZERO(MAGNITUDE(candidate.ehy_Au)))
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(ehy->ehy_V, a, b, c);
VSCALE(ehy->ehy_V, ehy->ehy_V, local2base);
// Set up Height line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(ehy->ehy_H, a, b, c);
VSCALE(ehy->ehy_H, ehy->ehy_H, local2base);
// Set up Semi-major axis line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(ehy->ehy_Au, a, b, c);
VUNITIZE(ehy->ehy_Au);
// Set up Semi-major length line
read_params_line_incr;
sscanf(lc, "%lf", &a);
ehy->ehy_r1 = a * local2base;
// Set up Semi-minor length line
read_params_line_incr;
sscanf(lc, "%lf", &a);
ehy->ehy_r2 = a * local2base;
// Set up distance to asymptotes line
read_params_line_incr;
sscanf(lc, "%lf", &a);
ehy->ehy_c = a * local2base;
// Cleanup
bu_free(wc, "wc");
VUNITIZE(candidate.ehy_Au);
*ehy = candidate;
return BRLCAD_OK;
}
+12 -69
View File
@@ -196,16 +196,6 @@ rt_edit_epa_write_params(
bu_vls_printf(p, "Semi-minor length: %.9f\n", epa->epa_r2 * base2local);
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_epa_read_params(
struct rt_db_internal *ip,
@@ -214,68 +204,21 @@ rt_edit_epa_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_epa_internal *epa = (struct rt_epa_internal *)ip->idb_ptr;
RT_EPA_CK_MAGIC(epa);
if (!fc)
struct rt_epa_internal candidate = *epa;
const struct edit_param_field fields[] = {
{"Vertex", candidate.epa_V, ELEMENTS_PER_VECT, local2base},
{"Height", candidate.epa_H, ELEMENTS_PER_VECT, local2base},
{"Semi-major axis", candidate.epa_Au, ELEMENTS_PER_VECT, 1.0},
{"Semi-major length", &candidate.epa_r1, 1, local2base},
{"Semi-minor length", &candidate.epa_r2, 1, local2base}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK ||
ZERO(MAGNITUDE(candidate.epa_Au)))
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (Vertex)
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(epa->epa_V, a, b, c);
VSCALE(epa->epa_V, epa->epa_V, local2base);
// Set up Height line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(epa->epa_H, a, b, c);
VSCALE(epa->epa_H, epa->epa_H, local2base);
// Set up Semi-major axis line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(epa->epa_Au, a, b, c);
VUNITIZE(epa->epa_Au);
// Set up Semi-major length line
read_params_line_incr;
sscanf(lc, "%lf", &a);
epa->epa_r1 = a * local2base;
// Set up Semi-minor length line
read_params_line_incr;
sscanf(lc, "%lf", &a);
epa->epa_r2 = a * local2base;
// Cleanup
bu_free(wc, "wc");
VUNITIZE(candidate.epa_Au);
*epa = candidate;
return BRLCAD_OK;
}
+12 -69
View File
@@ -148,16 +148,6 @@ rt_edit_eto_write_params(
bu_vls_printf(p, "Radius of rotation: %.9f\n", eto->eto_r * base2local);
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_eto_read_params(
struct rt_db_internal *ip,
@@ -166,68 +156,21 @@ rt_edit_eto_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_eto_internal *eto = (struct rt_eto_internal *)ip->idb_ptr;
RT_ETO_CK_MAGIC(eto);
if (!fc)
struct rt_eto_internal candidate = *eto;
const struct edit_param_field fields[] = {
{"Vertex", candidate.eto_V, ELEMENTS_PER_VECT, local2base},
{"Normal", candidate.eto_N, ELEMENTS_PER_VECT, 1.0},
{"Semi-major axis", candidate.eto_C, ELEMENTS_PER_VECT, local2base},
{"Semi-minor length", &candidate.eto_rd, 1, local2base},
{"Radius of rotation", &candidate.eto_r, 1, local2base}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK ||
ZERO(MAGNITUDE(candidate.eto_N)))
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (Vertex)
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(eto->eto_V, a, b, c);
VSCALE(eto->eto_V, eto->eto_V, local2base);
// Set up Normal line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(eto->eto_N, a, b, c);
VUNITIZE(eto->eto_N);
// Set up Semi-major axis line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(eto->eto_C, a, b, c);
VSCALE(eto->eto_C, eto->eto_C, local2base);
// Set up Semi-minor length line
read_params_line_incr;
sscanf(lc, "%lf", &a);
eto->eto_rd = a * local2base;
// Set up Radius of rotation line
read_params_line_incr;
sscanf(lc, "%lf", &a);
eto->eto_r = a * local2base;
// Cleanup
bu_free(wc, "wc");
VUNITIZE(candidate.eto_N);
*eto = candidate;
return BRLCAD_OK;
}
+14 -11
View File
@@ -248,7 +248,7 @@ ecmd_extr_skt_name(struct rt_edit *s)
int
ecmd_extr_mov_h(struct rt_edit *s)
{
vect_t work;
vect_t work, height;
point_t model_point;
struct rt_extrude_internal *extr =
(struct rt_extrude_internal *)s->es_int.idb_ptr;
@@ -256,6 +256,7 @@ ecmd_extr_mov_h(struct rt_edit *s)
void *d = NULL;
RT_EXTRUDE_CK_MAGIC(extr);
VMOVE(height, extr->h);
if (s->e_inpara) {
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str, "ERROR: three arguments needed\n");
@@ -268,22 +269,20 @@ ecmd_extr_mov_h(struct rt_edit *s)
if (s->mv_context) {
/* apply s->e_invmat to convert to real model coordinates */
MAT4X3PNT(work, s->e_invmat, model_point);
VSUB2(extr->h, work, extr->V);
VSUB2(height, work, extr->V);
} else {
VSUB2(extr->h, model_point, extr->V);
VSUB2(height, model_point, extr->V);
}
}
/* check for zero H vector */
if (MAGNITUDE(extr->h) <= SQRT_SMALL_FASTF) {
bu_vls_printf(s->log_str, "Zero H vector not allowed, resetting to +Z\n");
if (edit_validate_height(s, height) != BRLCAD_OK) {
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
VSET(extr->h, 0.0, 0.0, 1.0);
return BRLCAD_ERROR;
}
VMOVE(extr->h, height);
return 0;
}
@@ -380,7 +379,7 @@ ecmd_extr_rot_h(struct rt_edit *s)
}
/* Use mouse to change location of point V+H */
void
static int
ecmd_extr_mov_h_mousevec(struct rt_edit *s, const vect_t mousevec)
{
vect_t pos_view = VINIT_ZERO; /* Unrotated view space pos */
@@ -396,8 +395,13 @@ ecmd_extr_mov_h_mousevec(struct rt_edit *s, const vect_t mousevec)
/* Do NOT change pos_view[Z] ! */
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(tr_temp, s->e_invmat, temp);
VSUB2(extr->h, tr_temp, extr->V);
vect_t height;
VSUB2(height, tr_temp, extr->V);
if (edit_validate_height(s, height) != BRLCAD_OK)
return BRLCAD_ERROR;
VMOVE(extr->h, height);
edit_abs_tra(s, pos_view);
return BRLCAD_OK;
}
@@ -558,8 +562,7 @@ rt_edit_extrude_edit_xy(
edit_stra_xy(&pos_view, s, mousevec);
break;
case ECMD_EXTR_MOV_H:
ecmd_extr_mov_h_mousevec(s, mousevec);
return BRLCAD_OK;
return ecmd_extr_mov_h_mousevec(s, mousevec);
default:
return edit_generic_xy(s, mousevec);
}
+4 -2
View File
@@ -270,10 +270,12 @@ rt_hlf_norm(register struct hit *hitp, struct soltab *stp, register struct xray
/* We are expected to compute hit_point here. May be infinite. */
f = hitp->hit_dist;
if (f <= -INFINITY) {
bu_log("rt_hlf_norm: hit_dist = -INFINITY, unable to compute pt.\n");
if (RT_G_DEBUG & RT_DEBUG_ARB8)
bu_log("rt_hlf_norm: hit_dist = -INFINITY, unable to compute pt.\n");
VSETALL(hitp->hit_point, -INFINITY);
} else if (f >= INFINITY) {
bu_log("rt_hlf_norm: hit_dist = +INFINITY, unable to compute pt.\n");
if (RT_G_DEBUG & RT_DEBUG_ARB8)
bu_log("rt_hlf_norm: hit_dist = +INFINITY, unable to compute pt.\n");
VSETALL(hitp->hit_point, INFINITY);
} else {
VJOIN1(hitp->hit_point, rp->r_pt, f, rp->r_dir);
+10 -69
View File
@@ -155,16 +155,6 @@ rt_edit_hyp_write_params(
bu_vls_printf(p, "Ratio of Neck to Base: %.9f\n", hyp->hyp_bnr);
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_hyp_read_params(
struct rt_db_internal *ip,
@@ -173,68 +163,19 @@ rt_edit_hyp_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_hyp_internal *hyp = (struct rt_hyp_internal *)ip->idb_ptr;
RT_HYP_CK_MAGIC(hyp);
if (!fc)
struct rt_hyp_internal candidate = *hyp;
const struct edit_param_field fields[] = {
{"Vertex", candidate.hyp_Vi, ELEMENTS_PER_VECT, local2base},
{"Height", candidate.hyp_Hi, ELEMENTS_PER_VECT, local2base},
{"Semi-major axis", candidate.hyp_A, ELEMENTS_PER_VECT, local2base},
{"Semi-minor length", &candidate.hyp_b, 1, local2base},
{"Ratio of Neck to Base", &candidate.hyp_bnr, 1, 1.0}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK)
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (Vertex)
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(hyp->hyp_Vi, a, b, c);
VSCALE(hyp->hyp_Vi, hyp->hyp_Vi, local2base);
// Set up Height line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(hyp->hyp_Hi, a, b, c);
VSCALE(hyp->hyp_Hi, hyp->hyp_Hi, local2base);
// Set up Semi-major axis line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(hyp->hyp_A, a, b, c);
VSCALE(hyp->hyp_A, hyp->hyp_A, local2base);
// Set up Semi-minor length line
read_params_line_incr;
sscanf(lc, "%lf", &a);
hyp->hyp_b = a * local2base;
// Set up Ratio of Neck to Base line
read_params_line_incr;
sscanf(lc, "%lf", &a);
hyp->hyp_bnr = a;
// Cleanup
bu_free(wc, "wc");
*hyp = candidate;
return BRLCAD_OK;
}
+8 -1
View File
@@ -1732,7 +1732,7 @@ rt_hyp_volume(fastf_t *volume, const struct rt_db_internal *ip)
C_DECL int
rt_hyp_labels(struct rt_point_labels *pl, int pl_max, const mat_t xform, const struct rt_db_internal *ip, const struct bn_tol *UNUSED(tol))
{
int lcnt = 4;
int lcnt = 5;
if (!pl || pl_max < lcnt || !ip)
return 0;
@@ -1767,6 +1767,13 @@ rt_hyp_labels(struct rt_point_labels *pl, int pl_max, const mat_t xform, const s
MAT4X3PNT(pos_view, xform, work);
POINT_LABEL(pos_view, 'B');
/* hyp_bnr is the neck-to-base ratio. Show it at the neck's
* semi-major-axis point, halfway along H. */
VJOIN2(work, hyp->hyp_Vi, 0.5, hyp->hyp_Hi,
hyp->hyp_bnr, hyp->hyp_A);
MAT4X3PNT(pos_view, xform, work);
POINT_LABEL(pos_view, 'c');
return lcnt;
}
+47 -1
View File
@@ -676,6 +676,52 @@ nmg_edit_target_point(point_t target, const struct rt_edit *s, const point_t loc
VMOVE(target, base_point);
}
static int
nmg_edit_move_edge(struct rt_edit *s, const point_t new_pt)
{
struct rt_nmg_edit *n = (struct rt_nmg_edit *)s->ipe_ptr;
struct model *original = (struct model *)s->es_int.idb_ptr;
struct model *candidate;
struct nmg_struct_counts counts;
uint32_t **candidate_structs;
struct edgeuse *candidate_eu;
int result;
if (nmg_find_model(&n->es_eu->l.magic) != original)
return 1;
/* A wire move cannot fail after it starts changing geometry. */
if (!nmg_find_fu_of_eu(n->es_eu))
return nmg_move_edge_thru_pnt(n->es_eu, new_pt, s->tol);
candidate = nmg_clone_model(original);
if (!candidate)
return 1;
candidate_structs = nmg_m_struct_count(&counts, candidate);
candidate_eu = (struct edgeuse *)candidate_structs[n->es_eu->index];
NMG_CK_EDGEUSE(candidate_eu);
result = nmg_move_edge_thru_pnt(candidate_eu, new_pt, s->tol);
if (result) {
bu_free(candidate_structs, "NMG candidate structures");
nmg_km(candidate);
return result;
}
/* Clone indices identify the corresponding selections in the new model. */
n->es_eu = candidate_eu;
if (n->es_v && nmg_find_model(&n->es_v->magic) == original)
n->es_v = (struct vertex *)candidate_structs[n->es_v->index];
if (n->es_fu && nmg_find_model(&n->es_fu->l.magic) == original)
n->es_fu = (struct faceuse *)candidate_structs[n->es_fu->index];
if (n->es_s && nmg_find_model(&n->es_s->l.magic) == original)
n->es_s = (struct shell *)candidate_structs[n->es_s->index];
bu_free(candidate_structs, "NMG candidate structures");
s->es_int.idb_ptr = (void *)candidate;
nmg_km(original);
return 0;
}
static int
ecmd_nmg_emove(struct rt_edit *s)
{
@@ -747,7 +793,7 @@ ecmd_nmg_emove(struct rt_edit *s)
}
}
if (nmg_move_edge_thru_pnt(n->es_eu, new_pt, s->tol) < 0) {
if (nmg_edit_move_edge(s, new_pt)) {
bu_vls_printf(s->log_str, "Edge Move: Unable to hit (%g %g %g)\n",
V3ARGS(new_pt));
return BRLCAD_ERROR;
+12 -8
View File
@@ -234,7 +234,14 @@ rt_edit_rec_edit(struct rt_edit *s)
tgc = (struct rt_tgc_internal *)s->es_int.idb_ptr;
RT_TGC_CK_MAGIC(tgc);
if (s->e_inpara == 3) {
VSCALE(tgc->v, s->e_para, s->local2base);
point_t new_v;
VSCALE(new_v, s->e_para, s->local2base);
if (!isfinite(new_v[X]) || !isfinite(new_v[Y]) ||
!isfinite(new_v[Z])) {
bu_vls_printf(s->log_str, "ERROR: base center must be finite\n");
return BRLCAD_ERROR;
}
VMOVE(tgc->v, new_v);
}
return BRLCAD_OK;
@@ -244,11 +251,8 @@ rt_edit_rec_edit(struct rt_edit *s)
if (s->e_inpara == 3) {
vect_t new_h;
VSCALE(new_h, s->e_para, s->local2base);
if (MAGNITUDE(new_h) < SMALL_FASTF) {
bu_vls_printf(s->log_str,
"ERROR: H vector must have non-zero length\n");
if (edit_validate_height(s, new_h) != BRLCAD_OK)
return BRLCAD_ERROR;
}
mag_a = MAGNITUDE(tgc->a);
mag_b = MAGNITUDE(tgc->b);
@@ -281,7 +285,7 @@ rt_edit_rec_edit(struct rt_edit *s)
RT_TGC_CK_MAGIC(tgc);
if (s->e_inpara == 1) {
r = s->e_para[0] * s->local2base;
if (r < SMALL_FASTF) {
if (!isfinite(r) || r <= 0.0) {
bu_vls_printf(s->log_str,
"ERROR: radius must be positive\n");
return BRLCAD_ERROR;
@@ -298,7 +302,7 @@ rt_edit_rec_edit(struct rt_edit *s)
RT_TGC_CK_MAGIC(tgc);
if (s->e_inpara == 1) {
r = s->e_para[0] * s->local2base;
if (r < SMALL_FASTF) {
if (!isfinite(r) || r <= 0.0) {
bu_vls_printf(s->log_str,
"ERROR: radius must be positive\n");
return BRLCAD_ERROR;
@@ -315,7 +319,7 @@ rt_edit_rec_edit(struct rt_edit *s)
RT_TGC_CK_MAGIC(tgc);
if (s->e_inpara == 1) {
r = s->e_para[0] * s->local2base;
if (r < SMALL_FASTF) {
if (!isfinite(r) || r <= 0.0) {
bu_vls_printf(s->log_str,
"ERROR: radius must be positive\n");
return BRLCAD_ERROR;
+10 -69
View File
@@ -222,16 +222,6 @@ rt_edit_rhc_write_params(
bu_vls_printf(p, "Dist_to_asymptotes: %.9f\n", rhc->rhc_c * base2local);
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_rhc_read_params(
struct rt_db_internal *ip,
@@ -240,68 +230,19 @@ rt_edit_rhc_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_rhc_internal *rhc = (struct rt_rhc_internal *)ip->idb_ptr;
RT_RHC_CK_MAGIC(rhc);
if (!fc)
struct rt_rhc_internal candidate = *rhc;
const struct edit_param_field fields[] = {
{"Vertex", candidate.rhc_V, ELEMENTS_PER_VECT, local2base},
{"Height", candidate.rhc_H, ELEMENTS_PER_VECT, local2base},
{"Breadth", candidate.rhc_B, ELEMENTS_PER_VECT, local2base},
{"Half-width", &candidate.rhc_r, 1, local2base},
{"Dist_to_asymptotes", &candidate.rhc_c, 1, local2base}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK)
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (Vertex)
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(rhc->rhc_V, a, b, c);
VSCALE(rhc->rhc_V, rhc->rhc_V, local2base);
// Set up Height line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(rhc->rhc_H, a, b, c);
VSCALE(rhc->rhc_H, rhc->rhc_H, local2base);
// Set up Breadth line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(rhc->rhc_B, a, b, c);
VSCALE(rhc->rhc_B, rhc->rhc_B, local2base);
// Set up Half-width line
read_params_line_incr;
sscanf(lc, "%lf", &a);
rhc->rhc_r = a * local2base;
// Set up distance to asymptotes line
read_params_line_incr;
sscanf(lc, "%lf", &a);
rhc->rhc_c = a * local2base;
// Cleanup
bu_free(wc, "wc");
*rhc = candidate;
return BRLCAD_OK;
}
+9 -63
View File
@@ -195,16 +195,6 @@ rt_edit_rpc_write_params(
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_rpc_read_params(
struct rt_db_internal *ip,
@@ -213,62 +203,18 @@ rt_edit_rpc_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_rpc_internal *rpc = (struct rt_rpc_internal *)ip->idb_ptr;
RT_RPC_CK_MAGIC(rpc);
if (!fc)
struct rt_rpc_internal candidate = *rpc;
const struct edit_param_field fields[] = {
{"Vertex", candidate.rpc_V, ELEMENTS_PER_VECT, local2base},
{"Height", candidate.rpc_H, ELEMENTS_PER_VECT, local2base},
{"Breadth", candidate.rpc_B, ELEMENTS_PER_VECT, local2base},
{"Half-width", &candidate.rpc_r, 1, local2base}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK)
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (Vertex)
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(rpc->rpc_V, a, b, c);
VSCALE(rpc->rpc_V, rpc->rpc_V, local2base);
// Set up Height line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(rpc->rpc_H, a, b, c);
VSCALE(rpc->rpc_H, rpc->rpc_H, local2base);
// Set up Breadth line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(rpc->rpc_B, a, b, c);
VSCALE(rpc->rpc_B, rpc->rpc_B, local2base);
// Set up Half-width line
read_params_line_incr;
sscanf(lc, "%lf", &a);
rpc->rpc_r = a * local2base;
// Cleanup
bu_free(wc, "wc");
*rpc = candidate;
return BRLCAD_OK;
}
+32 -96
View File
@@ -460,16 +460,6 @@ rt_edit_tgc_write_params(
bu_vls_printf(p, "D: %.9f %.9f %.9f\n", V3BASE2LOCAL(tgc->d));
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_tgc_read_params(
struct rt_db_internal *ip,
@@ -478,77 +468,20 @@ rt_edit_tgc_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
struct rt_tgc_internal *tgc = (struct rt_tgc_internal *)ip->idb_ptr;
RT_TGC_CK_MAGIC(tgc);
if (!fc)
struct rt_tgc_internal candidate = *tgc;
const struct edit_param_field fields[] = {
{"Vertex", candidate.v, ELEMENTS_PER_VECT, local2base},
{"Height", candidate.h, ELEMENTS_PER_VECT, local2base},
{"A", candidate.a, ELEMENTS_PER_VECT, local2base},
{"B", candidate.b, ELEMENTS_PER_VECT, local2base},
{"C", candidate.c, ELEMENTS_PER_VECT, local2base},
{"D", candidate.d, ELEMENTS_PER_VECT, local2base}
};
if (edit_param_read_fields(fc, fields, sizeof(fields) / sizeof(fields[0])) != BRLCAD_OK)
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (Vertex)
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(tgc->v, a, b, c);
VSCALE(tgc->v, tgc->v, local2base);
// Set up Height line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(tgc->h, a, b, c);
VSCALE(tgc->h, tgc->h, local2base);
// Set up A line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(tgc->a, a, b, c);
VSCALE(tgc->a, tgc->a, local2base);
// Set up B line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(tgc->b, a, b, c);
VSCALE(tgc->b, tgc->b, local2base);
// Set up C line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(tgc->c, a, b, c);
VSCALE(tgc->c, tgc->c, local2base);
// Set up D line
read_params_line_incr;
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(tgc->d, a, b, c);
VSCALE(tgc->d, tgc->d, local2base);
// Cleanup
bu_free(wc, "wc");
*tgc = candidate;
return BRLCAD_OK;
}
@@ -751,7 +684,7 @@ int
ecmd_tgc_mv_h(struct rt_edit *s)
{
float la, lb, lc, ld; /* TGC: length of vectors */
vect_t work;
vect_t work, height;
point_t model_point;
struct rt_tgc_internal *tgc =
(struct rt_tgc_internal *)s->es_int.idb_ptr;
@@ -759,6 +692,7 @@ ecmd_tgc_mv_h(struct rt_edit *s)
void *d = NULL;
RT_TGC_CK_MAGIC(tgc);
VMOVE(height, tgc->h);
if (s->e_inpara) {
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str, "ERROR: three arguments needed\n");
@@ -771,22 +705,21 @@ ecmd_tgc_mv_h(struct rt_edit *s)
if (s->mv_context) {
/* apply s->e_invmat to convert to real model coordinates */
MAT4X3PNT(work, s->e_invmat, model_point);
VSUB2(tgc->h, work, tgc->v);
VSUB2(height, work, tgc->v);
} else {
VSUB2(tgc->h, model_point, tgc->v);
VSUB2(height, model_point, tgc->v);
}
}
/* check for zero H vector */
if (MAGNITUDE(tgc->h) <= SQRT_SMALL_FASTF) {
bu_vls_printf(s->log_str, "Zero H vector not allowed, resetting to +Z\n");
if (edit_validate_height(s, height) != BRLCAD_OK) {
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
VSET(tgc->h, 0.0, 0.0, 1.0);
return BRLCAD_ERROR;
}
VMOVE(tgc->h, height);
/* have new height vector -- redefine rest of tgc */
la = MAGNITUDE(tgc->a);
lb = MAGNITUDE(tgc->b);
@@ -814,7 +747,7 @@ ecmd_tgc_mv_h(struct rt_edit *s)
int
ecmd_tgc_mv_hh(struct rt_edit *s)
{
vect_t work;
vect_t work, height;
point_t model_point;
struct rt_tgc_internal *tgc =
(struct rt_tgc_internal *)s->es_int.idb_ptr;
@@ -822,6 +755,7 @@ ecmd_tgc_mv_hh(struct rt_edit *s)
void *d = NULL;
RT_TGC_CK_MAGIC(tgc);
VMOVE(height, tgc->h);
if (s->e_inpara) {
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str, "ERROR: three arguments needed\n");
@@ -834,22 +768,20 @@ ecmd_tgc_mv_hh(struct rt_edit *s)
if (s->mv_context) {
/* apply s->e_invmat to convert to real model coordinates */
MAT4X3PNT(work, s->e_invmat, model_point);
VSUB2(tgc->h, work, tgc->v);
VSUB2(height, work, tgc->v);
} else {
VSUB2(tgc->h, model_point, tgc->v);
VSUB2(height, model_point, tgc->v);
}
}
/* check for zero H vector */
if (MAGNITUDE(tgc->h) <= SQRT_SMALL_FASTF) {
bu_vls_printf(s->log_str, "Zero H vector not allowed, resetting to +Z\n");
if (edit_validate_height(s, height) != BRLCAD_OK) {
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
VSET(tgc->h, 0.0, 0.0, 1.0);
return BRLCAD_ERROR;
}
VMOVE(tgc->h, height);
return 0;
}
@@ -1005,7 +937,7 @@ ecmd_tgc_rot_ab(struct rt_edit *s)
}
/* Use mouse to change location of point V+H */
void
static int
ecmd_tgc_mv_h_mousevec(struct rt_edit *s, const vect_t mousevec)
{
struct rt_tgc_internal *tgc =
@@ -1021,8 +953,13 @@ ecmd_tgc_mv_h_mousevec(struct rt_edit *s, const vect_t mousevec)
/* Do NOT change pos_view[Z] ! */
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(tr_temp, s->e_invmat, temp);
VSUB2(tgc->h, tr_temp, tgc->v);
vect_t height;
VSUB2(height, tr_temp, tgc->v);
if (edit_validate_height(s, height) != BRLCAD_OK)
return BRLCAD_ERROR;
VMOVE(tgc->h, height);
edit_abs_tra(s, pos_view);
return BRLCAD_OK;
}
static int
@@ -1188,8 +1125,7 @@ rt_edit_tgc_edit_xy(
break;
case ECMD_TGC_MV_H:
case ECMD_TGC_MV_HH:
ecmd_tgc_mv_h_mousevec(s, mousevec);
return BRLCAD_OK;
return ecmd_tgc_mv_h_mousevec(s, mousevec);
case ECMD_TGC_ROT_H:
case ECMD_TGC_ROT_AB:
bu_vls_printf(s->log_str, "%s: XY edit undefined in solid edit mode %d\n", EDOBJ[ip->idb_type].ft_label, s->edit_flag);
+82 -14
View File
@@ -198,11 +198,11 @@ public:
const char *skip_setting = std::getenv("LIBRT_RTCMP_SKIP_MISSES");
primitive_mode = primitive_setting && bu_strcmp(primitive_setting, "1") == 0;
skip_misses = skip_setting && bu_strcmp(skip_setting, "1") == 0;
enabled.store(true, std::memory_order_release);
healthy.store(true, std::memory_order_release);
try {
worker = std::thread(&CaptureWriter::write, this);
} catch (const std::exception &e) {
enabled.store(false, std::memory_order_release);
healthy.store(false, std::memory_order_release);
bu_log("RT_DEBUG_RTCMP: cannot start writer: %s\n", e.what());
std::fclose(file);
file = nullptr;
@@ -224,8 +224,24 @@ public:
bu_log("RT_DEBUG_RTCMP: closing output failed\n");
}
bool active() const { return enabled.load(std::memory_order_acquire); }
bool active() const
{
return healthy.load(std::memory_order_acquire);
}
bool capturing() const
{
return active() && recording.load(std::memory_order_acquire);
}
bool primitives() const { return primitive_mode; }
int set_recording(bool requested)
{
std::lock_guard<std::mutex> guard(mutex);
if (requested && !active()) return -1;
recording.store(requested, std::memory_order_release);
if (!requested) space.notify_all();
return 0;
}
bool omit_misses() const { return skip_misses; }
int flush()
@@ -244,11 +260,11 @@ public:
{
std::unique_lock<std::mutex> guard(mutex);
space.wait(guard, [&] {
return !active() || queued.empty() ||
return !capturing() || queued.empty() ||
(line.size() <= MAX_QUEUED_BYTES &&
queued_bytes <= MAX_QUEUED_BYTES - line.size());
});
if (!active()) return;
if (!capturing()) return;
if (queued.empty()) deadline = std::chrono::steady_clock::now() + FLUSH_DELAY;
queued_bytes += line.size();
@@ -260,7 +276,8 @@ public:
void disable(const char *reason)
{
if (!enabled.exchange(false, std::memory_order_acq_rel)) return;
if (!healthy.exchange(false, std::memory_order_acq_rel)) return;
recording.store(false, std::memory_order_release);
bu_log("RT_DEBUG_RTCMP: %s; capture disabled\n", reason);
ready.notify_all();
space.notify_all();
@@ -305,7 +322,8 @@ private:
}
}
std::atomic<bool> enabled{false};
std::atomic<bool> healthy{false};
std::atomic<bool> recording{false};
std::mutex mutex;
std::condition_variable ready;
std::condition_variable space;
@@ -323,23 +341,71 @@ private:
bool stopping = false;
};
std::atomic<CaptureWriter *> &
initialized_writer()
{
static std::atomic<CaptureWriter *> capture{nullptr};
return capture;
}
struct WriterHolder {
CaptureWriter capture;
WriterHolder()
{
initialized_writer().store(&capture, std::memory_order_release);
}
~WriterHolder()
{
initialized_writer().store(nullptr, std::memory_order_release);
}
};
CaptureWriter &
writer()
{
static CaptureWriter instance;
return instance;
static WriterHolder holder;
return holder.capture;
}
} // namespace
extern "C" int
rt_rtcmp_capture_set_enabled(int enabled)
{
if (!enabled) {
CaptureWriter *capture =
initialized_writer().load(std::memory_order_acquire);
return capture ? capture->set_recording(false) : 0;
}
if (!(RT_G_DEBUG & RT_DEBUG_RTCMP)) return -1;
try {
return writer().set_recording(true);
} catch (const std::exception &e) {
bu_log("RT_DEBUG_RTCMP: cannot enable capture: %s\n", e.what());
return -1;
}
}
extern "C" int
_rt_rtcmp_capture_ready(void)
{
try {
return writer().capturing();
} catch (const std::exception &e) {
bu_log("RT_DEBUG_RTCMP: cannot initialize capture: %s\n",
e.what());
return 0;
}
}
extern "C" void *
_rt_rtcmp_capture_begin(void)
{
const char *mode = std::getenv("LIBRT_RTCMP_PRIMITIVES");
if (!mode || bu_strcmp(mode, "1") != 0) return nullptr;
try {
CaptureWriter &capture = writer();
return capture.active() && capture.primitives() ? new CaptureState : nullptr;
return capture.primitives() ? new CaptureState : nullptr;
} catch (const std::exception &e) {
writer().disable(e.what());
return nullptr;
@@ -364,7 +430,8 @@ _rt_rtcmp_capture_finish(void *state, const struct application *ap, const struct
std::unique_ptr<CaptureState> shot(static_cast<CaptureState *>(state));
try {
CaptureWriter &capture = writer();
if (!capture.active()) return;
if (!(RT_G_DEBUG & RT_DEBUG_RTCMP) ||
!capture.capturing()) return;
if (capture.omit_misses() && shot->segments.empty() && parts->pt_forw == parts) return;
capture.submit(format_shot(ap, parts, &shot->segments));
} catch (const std::exception &e) {
@@ -385,7 +452,8 @@ _rt_rtcmp_capture(const struct application *ap, const struct partition *parts)
try {
CaptureWriter &capture = writer();
if (capture.active()) capture.submit(format_shot(ap, parts));
if ((RT_G_DEBUG & RT_DEBUG_RTCMP) && capture.capturing())
capture.submit(format_shot(ap, parts));
} catch (const std::exception &e) {
writer().disable(e.what());
}
+7 -3
View File
@@ -545,6 +545,7 @@ rt_shootray(register struct application *ap)
struct resource *resp;
struct rt_i *rtip;
const int debug_shoot = RT_G_DEBUG & RT_DEBUG_SHOOT;
int rtcmp_capture = 0;
void *rtcmp_state = NULL;
fastf_t pending_hit = 0; /* dist of closest odd hit pending */
@@ -600,8 +601,11 @@ rt_shootray(register struct application *ap)
BU_LIST_INIT(&waiting_segs.l);
BU_LIST_INIT(&finished_segs.l);
ap->a_finished_segs_hdp = &finished_segs;
if (RT_G_DEBUG & RT_DEBUG_RTCMP)
rtcmp_state = _rt_rtcmp_capture_begin();
if (RT_G_DEBUG & RT_DEBUG_RTCMP) {
rtcmp_capture = _rt_rtcmp_capture_ready();
if (rtcmp_capture)
rtcmp_state = _rt_rtcmp_capture_begin();
}
if (!BU_LIST_IS_INITIALIZED(&resp->re_parthead)) {
/* XXX This shouldn't happen any more */
@@ -968,7 +972,7 @@ hitit:
if (rtcmp_state)
_rt_rtcmp_capture_finish(rtcmp_state, ap, &FinalPart);
else if (RT_G_DEBUG & RT_DEBUG_RTCMP)
else if (rtcmp_capture)
_rt_rtcmp_capture(ap, &FinalPart);
/* Before recursing, release storage for unused Initial
+20 -14
View File
@@ -72,10 +72,15 @@ brlcad_add_test(NAME rt_tree_prune COMMAND rt_tree_prune)
brlcad_addexec(rt_reprep_prune reprep_prune.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_addexec(rt_rtcmp_capture rtcmp_capture.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_rtcmp_capture COMMAND rt_rtcmp_capture)
brlcad_add_test(NAME rt_rtcmp_capture_primitives COMMAND rt_rtcmp_capture primitives)
brlcad_add_test(NAME rt_reprep_prune COMMAND rt_reprep_prune)
# Note - this feature is not available in a BRLCAD_ENABLE_RUNTIME_DEBUG=OFF
# build, since it has an unavoidable performance overhead in the hot loop to
# check if the setting is enabled.
if (BRLCAD_ENABLE_RUNTIME_DEBUG)
brlcad_addexec(rt_rtcmp_capture rtcmp_capture.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_rtcmp_capture COMMAND rt_rtcmp_capture)
brlcad_add_test(NAME rt_rtcmp_capture_primitives COMMAND rt_rtcmp_capture primitives)
brlcad_add_test(NAME rt_reprep_prune COMMAND rt_reprep_prune)
endif()
brlcad_addexec(rt_submodel_xform submodel_xform.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_submodel_xform COMMAND rt_submodel_xform)
@@ -212,36 +217,37 @@ brlcad_add_test(NAME rt_metaball_adjust COMMAND rt_metaball_adjust)
set(
distcheck_files
CMakeLists.txt
ars_inside.c
arb_intersect.g
ars_inside.c
binary_attribute.c
binunif.c
brep_io.cpp
bound_internal.c
brep_boolean_tests.g
brep_io.cpp
crofton.c
cyclic_tests.g
datum_annot_semantics.c
db_name.c
default_object.c
datum_annot_semantics.c
dsp_raytrace_check.c
dsp_sa_vol_check.c
primitive_brep.cpp
reprep_prune.c
brep_boolean_tests.g
cyclic_tests.g
extreme_ssi_test.g
invalid_inmem.c
lcomb_large.c
matrix_tests.g
metaball_adjust.c
nurbs_surfaces.g
pnts_bounds.c
prim_tess.c
primitive_brep.cpp
reprep_prune.c
rt_datum.c
rt_perturb.c
rtcmp_capture.c
search_stress.c
static_init.cpp
sketch.g
static_init.cpp
submodel_xform.c
prim_tess.c
pnts_bounds.c
tess_timing.c
tree_prune.c
vds_tree.c
+63
View File
@@ -37,6 +37,7 @@ main(int UNUSED(argc), const char **argv)
point_t bmin, bmax;
point_t expected_min = {-1.0, -1.0, -1.0};
point_t expected_max = { 1.0, 1.0, 1.0};
mat_t translated;
int ret = 1;
bu_setprogname(argv[0]);
@@ -75,6 +76,68 @@ main(int UNUSED(argc), const char **argv)
if (!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
dp = db_lookup(dbip, "left.s", LOOKUP_QUIET);
if (dp == RT_DIR_NULL || rt_bound_internal(dbip, dp, bmin, bmax))
goto done;
if (!VNEAR_EQUAL(bmin, expected_min, BN_TOL_DIST) ||
!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
BU_LIST_INIT(&wm.l);
if (mk_comb(wdbp, "empty.c", &wm.l, 0, NULL, NULL, NULL,
0, 0, 0, 0, 0, 0, 0))
goto done;
dp = db_lookup(dbip, "empty.c", LOOKUP_QUIET);
if (dp == RT_DIR_NULL ||
rt_bound_internal(dbip, dp, bmin, bmax) == 0)
goto done;
BU_LIST_INIT(&wm.l);
if (!mk_addmember("empty.c", &wm.l, NULL, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "nested.c", &wm, 0, NULL, NULL, NULL, 0))
goto done;
dp = db_lookup(dbip, "nested.c", LOOKUP_QUIET);
if (dp == RT_DIR_NULL ||
rt_bound_internal(dbip, dp, bmin, bmax) == 0)
goto done;
MAT_IDN(translated);
MAT_DELTAS(translated, 20.0, 0.0, 0.0);
BU_LIST_INIT(&wm.l);
if (!mk_addmember("left.s", &wm.l, NULL, WMOP_UNION) ||
!mk_addmember("left.s", &wm.l, translated, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "instanced.r", &wm, 1, NULL, NULL, NULL, 0))
goto done;
dp = db_lookup(dbip, "instanced.r", LOOKUP_QUIET);
if (dp == RT_DIR_NULL || rt_bound_internal(dbip, dp, bmin, bmax))
goto done;
VSET(expected_max, 21.0, 1.0, 1.0);
if (!VNEAR_EQUAL(bmin, expected_min, BN_TOL_DIST) ||
!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
BU_LIST_INIT(&wm.l);
if (!mk_addmember("left.s", &wm.l, translated, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "child.c", &wm, 0, NULL, NULL, NULL, 0))
goto done;
MAT_DELTAS(translated, 10.0, 0.0, 0.0);
BU_LIST_INIT(&wm.l);
if (!mk_addmember("child.c", &wm.l, translated, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "parent.r", &wm, 1, NULL, NULL, NULL, 0))
goto done;
dp = db_lookup(dbip, "parent.r", LOOKUP_QUIET);
if (dp == RT_DIR_NULL || rt_bound_internal(dbip, dp, bmin, bmax))
goto done;
VSET(expected_min, 29.0, -1.0, -1.0);
VSET(expected_max, 31.0, 1.0, 1.0);
if (!VNEAR_EQUAL(bmin, expected_min, BN_TOL_DIST) ||
!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
ret = 0;
done:
+1
View File
@@ -57,6 +57,7 @@ foreach(edit_case IN LISTS edit_cases)
endforeach()
configure_file(cases.inc.in "${CMAKE_CURRENT_BINARY_DIR}/cases.inc" @ONLY)
distclean("${CMAKE_CURRENT_BINARY_DIR}/cases.inc")
brlcad_addexec(rt_edit_test "${edit_sources}" "librt;libnmg;libwdb;libbrep;${WINSOCK_LIB}" TEST)
# The cases have translation-unit-local fixture helpers with shared names.
+163
View File
@@ -343,6 +343,79 @@ test_arb_geometry_helpers(struct rt_edit *s, struct rt_arb_internal *arb,
}
static void
nonplanar_arb8_reset(struct rt_edit *s, struct rt_arb_internal *arb,
struct rt_arb8_edit *a, const struct bn_tol *tol)
{
arb8_reset(s, arb, a);
arb->pt[6][Z] = 1.25;
int issues = 0;
(void)rt_arb_validate(NULL, arb, tol, &issues);
if (!(issues & RT_ARB_VALIDATE_NONCOPLANAR))
bu_exit(1, "ERROR: nonplanar ARB8 test fixture is planar\n");
MAT_IDN(s->e_mat);
MAT_IDN(s->e_invmat);
MAT_IDN(s->model_changes);
s->mv_context = 0;
}
static void
test_nonplanar_whole_solid_edits(struct rt_edit *s,
struct rt_arb_internal *arb, struct rt_arb8_edit *a,
const struct bn_tol *tol)
{
nonplanar_arb8_reset(s, arb, a, tol);
rt_edit_set_edflag(s, RT_PARAMS_EDIT_SCALE);
s->es_scale = 2.0;
VSETALL(s->e_keypoint, 0.0);
if (rt_edit_process(s) != BRLCAD_OK) {
bu_exit(1, "ERROR: nonplanar ARB8 whole-solid scale was rejected\n");
}
point_t scaled = {2.0, 2.0, 2.5};
if (!VNEAR_EQUAL(arb->pt[6], scaled, VUNITIZE_TOL))
bu_exit(1, "ERROR: nonplanar ARB8 whole-solid scale failed\n");
nonplanar_arb8_reset(s, arb, a, tol);
rt_edit_set_edflag(s, RT_PARAMS_EDIT_TRANS);
s->e_inpara = 3;
VSET(s->e_para, 2.0, 3.0, 4.0);
VSETALL(s->e_keypoint, 0.0);
if (rt_edit_process(s) != BRLCAD_OK) {
bu_exit(1, "ERROR: nonplanar ARB8 whole-solid translation was rejected\n");
}
point_t translated = {3.0, 4.0, 5.25};
if (!VNEAR_EQUAL(arb->pt[6], translated, VUNITIZE_TOL))
bu_exit(1, "ERROR: nonplanar ARB8 whole-solid translation failed\n");
nonplanar_arb8_reset(s, arb, a, tol);
rt_edit_set_edflag(s, RT_PARAMS_EDIT_ROT);
s->e_inpara = 3;
VSET(s->e_para, 0.0, 0.0, 90.0);
VSETALL(s->e_keypoint, 0.0);
if (rt_edit_process(s) != BRLCAD_OK) {
bu_exit(1, "ERROR: nonplanar ARB8 whole-solid rotation was rejected\n");
}
point_t rotated = {-1.0, 1.0, 1.25};
if (!VNEAR_EQUAL(arb->pt[6], rotated, VUNITIZE_TOL))
bu_exit(1, "ERROR: nonplanar ARB8 whole-solid rotation failed\n");
nonplanar_arb8_reset(s, arb, a, tol);
struct rt_arb_internal before_face_move = *arb;
a->edit_menu = 0;
rt_edit_set_edflag(s, ECMD_ARB_MOVE_FACE);
s->e_inpara = 3;
VSET(s->e_para, 0.0, 0.0, 0.25);
bu_vls_trunc(s->log_str, 0);
if (rt_edit_process(s) != BRLCAD_ERROR ||
memcmp(arb->pt, before_face_move.pt, sizeof(arb->pt)) ||
!strstr(bu_vls_cstr(s->log_str), "valid face planes"))
bu_exit(1, "ERROR: nonplanar ARB8 face edit was not rejected cleanly\n");
bu_log("Nonplanar ARB8 whole-solid edits SUCCESS\n");
}
int
rt_edit_test_arb8(void)
{
@@ -778,6 +851,72 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
if (!edit_test_mouse_knobs_match(s, face_view_target))
bu_exit(1, "ERROR: ARB8 mouse face knobs missed cursor\n");
/* A rejected drag must not become the starting geometry for the next. */
struct rt_arb_internal valid_face_arb = *arb;
plane_t valid_face_planes[ARB8_FACE_COUNT];
memcpy(valid_face_planes, a->es_peqn, sizeof(valid_face_planes));
s->local2base = 25.4;
s->base2local = 1.0 / s->local2base;
VSET(mousevec, 1.0, 0.0, 0.0); /* Collapse the moved face onto the opposite face. */
if (EDOBJ[dp->d_minor_type].ft_edit_xy(s, mousevec) != BRLCAD_ERROR ||
memcmp(arb->pt, valid_face_arb.pt, sizeof(arb->pt)) ||
memcmp(a->es_peqn, valid_face_planes, sizeof(valid_face_planes)))
bu_exit(1, "ERROR: rejected ARB8 mouse face move changed edit state\n");
const fastf_t face_drags[] = {0.3, 0.4, 0.25};
const size_t drag_cycles = 32;
const size_t drag_positions = sizeof(face_drags) / sizeof(face_drags[0]);
for (size_t drag = 0; drag < drag_cycles * drag_positions; drag++) {
fastf_t target_x = face_drags[drag % drag_positions];
VSET(mousevec, target_x, 0.0, 0.0);
if (EDOBJ[dp->d_minor_type].ft_edit_xy(s, mousevec) != BRLCAD_OK ||
rt_edit_process(s) != BRLCAD_OK)
bu_exit(1, "ERROR: ARB8 mouse face move failed after rejected drag\n");
for (size_t i = 0; i < sizeof(face_points) / sizeof(face_points[0]); i++) {
if (!NEAR_EQUAL(arb->pt[face_points[i]][X], target_x, VUNITIZE_TOL))
bu_exit(1, "ERROR: repeated ARB8 mouse face move drifted\n");
}
}
struct rt_arb_internal before_rejected_calc = *arb;
plane_t rejected_planes[ARB8_FACE_COUNT];
memcpy(rejected_planes, a->es_peqn, sizeof(rejected_planes));
rejected_planes[a->edit_menu][W] = VDOT(rejected_planes[a->edit_menu], arb->pt[1]);
if (rt_arb_calc_points(arb, ARB8, (const plane_t *)rejected_planes, s->tol) >= 0 ||
memcmp(arb->pt, before_rejected_calc.pt, sizeof(arb->pt)))
bu_exit(1, "ERROR: rejected ARB point calculation changed geometry\n");
struct rt_arb_internal invalid_arb = *arb;
VMOVE(invalid_arb.pt[6], invalid_arb.pt[5]);
VMOVE(invalid_arb.pt[0], invalid_arb.pt[4]);
memcpy(rejected_planes, a->es_peqn, sizeof(rejected_planes));
plane_t unchanged_planes[ARB8_FACE_COUNT];
memcpy(unchanged_planes, rejected_planes, sizeof(unchanged_planes));
struct bu_vls rejected_plane_msg = BU_VLS_INIT_ZERO;
if (rt_arb_calc_planes(&rejected_plane_msg, &invalid_arb, ARB8,
rejected_planes, s->tol) >= 0 ||
memcmp(rejected_planes, unchanged_planes, sizeof(rejected_planes)))
bu_exit(1, "ERROR: rejected ARB plane calculation changed planes\n");
bu_vls_free(&rejected_plane_msg);
struct rt_arb_internal before_rejected_edge = *arb;
memcpy(rejected_planes, a->es_peqn, sizeof(rejected_planes));
VSET(mousevec, 0.0, 1.0, 0.0); /* Move edge 12 onto the opposite edge. */
if (rt_arb_edit(s->log_str, arb, NULL, ARB8, 0,
RT_ARB_EDIT_DEFAULT, mousevec, rejected_planes, s->tol) == 0 ||
memcmp(arb->pt, before_rejected_edge.pt, sizeof(arb->pt)) ||
memcmp(rejected_planes, a->es_peqn, sizeof(rejected_planes)))
bu_exit(1, "ERROR: rejected ARB edge edit changed geometry or planes\n");
memcpy(rejected_planes, a->es_peqn, sizeof(rejected_planes));
VSETALL(mousevec, 0.0);
if (rt_arb_edit(s->log_str, arb, NULL, ARB8, 0,
RT_ARB_EDIT_EDGE_DIR, mousevec, rejected_planes, s->tol) == 0 ||
!ZERO(MAGNITUDE(mousevec)) ||
memcmp(arb->pt, before_rejected_edge.pt, sizeof(arb->pt)) ||
memcmp(rejected_planes, a->es_peqn, sizeof(rejected_planes)))
bu_exit(1, "ERROR: rejected ARB edge direction changed input or edit state\n");
s->local2base = 1.0;
s->base2local = 1.0;
@@ -844,6 +983,29 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
ZERO(a->es_peqn[4][Y]))
bu_exit(1, "ERROR: ARB extended face rotation changed input or left plane unchanged\n");
arb8_reset(s, arb, a);
a->edit_menu = 0; /* Rotate the bottom plane onto a side plane. */
a->fixv = 0;
rt_edit_set_edflag(s, ECMD_ARB_ROTATE_FACE);
s->mv_context = 0;
s->e_inpara = 3;
VSET(s->e_para, 0.0, 90.0, 0.0);
MAT_IDN(s->acc_rot_sol);
MAT_IDN(s->model_changes);
MAT_IDN(s->incr_change);
if (rt_arb_calc_planes(s->log_str, arb, ARB8, a->es_peqn, s->tol))
bu_exit(1, "ERROR: ARB rejected-rotation setup failed\n");
struct rt_arb_internal before_rejected_rotation = *arb;
plane_t before_rotation_planes[ARB8_FACE_COUNT];
mat_t before_rotation_acc;
memcpy(before_rotation_planes, a->es_peqn, sizeof(before_rotation_planes));
MAT_COPY(before_rotation_acc, s->acc_rot_sol);
if (rt_edit_process(s) != BRLCAD_ERROR ||
memcmp(arb->pt, before_rejected_rotation.pt, sizeof(arb->pt)) ||
memcmp(a->es_peqn, before_rotation_planes, sizeof(before_rotation_planes)) ||
memcmp(s->acc_rot_sol, before_rotation_acc, sizeof(before_rotation_acc)))
bu_exit(1, "ERROR: rejected ARB face rotation changed edit state\n");
/* The same face operation must survive the interactive rotation knob
* adapter, which supplies an incremental matrix rather than parameters.
*/
@@ -864,6 +1026,7 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
bu_log("ECMD_ARB_ROTATE_FACE knob edit SUCCESS\n");
test_arb_geometry_helpers(s, arb, a, &tol);
test_nonplanar_whole_solid_edits(s, arb, a, &tol);
/* The public editing entry point must reject invalid type and menu indexes
* before using them to address the type-specific editing tables. */
+40
View File
@@ -402,6 +402,20 @@ rt_edit_test_bot(void)
V3ARGS(&bot->vertices[4*3]));
}
bot_reset(s, bot, b);
/* An edge selection can become stale after a topology change. */
bot->faces[0] = 0; bot->faces[1] = 2; bot->faces[2] = 3;
bot->faces[3] = 1; bot->faces[4] = 2; bot->faces[5] = 3;
int faces_before[12];
memcpy(faces_before, bot->faces, sizeof(faces_before));
b->bot_verts[0] = 0;
b->bot_verts[1] = 1;
EDOBJ[dp->d_minor_type].ft_set_edit_mode(s, ECMD_BOT_ESPLIT);
if (rt_edit_process(s) != BRLCAD_ERROR ||
bot->num_vertices != 4 || bot->num_faces != 4 ||
memcmp(bot->faces, faces_before, sizeof(faces_before)))
bu_exit(1, "ERROR: stale BOT edge split changed topology\n");
/* ================================================================
* ECMD_BOT_FSPLIT: split face {0,2,3} of a fresh tetrahedron
*
@@ -629,6 +643,32 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
be->bot_verts[0], be->bot_verts[1], be->bot_verts[2]);
}
/* The selected face is labeled at its centroid and marked with one line
* from the centroid to each vertex. */
{
struct rt_point_labels labels[3] = {RT_POINT_LABELS_INIT};
point_t lines[2 * 4];
mat_t identity;
int num_lines = 0;
MAT_IDN(identity);
EDOBJ[ID_BOT].ft_labels(&num_lines, lines, labels, 3, identity, s, &tol);
point_t centroid = {1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0};
point_t vertices[3] = {{1.0, 0.0, 0.0},
{0.0, 1.0, 0.0},
{0.0, 0.0, 1.0}};
if (num_lines != 3 || !BU_STR_EQUAL(labels[0].str, "face") ||
!VNEAR_EQUAL(labels[0].pt, centroid, VUNITIZE_TOL) ||
!BU_STR_EQUAL(labels[1].str, "pt"))
bu_exit(1, "ERROR: selected BOT face label is missing or misplaced\n");
for (int i = 0; i < num_lines; ++i) {
if (!VNEAR_EQUAL(lines[i * 2], centroid, VUNITIZE_TOL) ||
!VNEAR_EQUAL(lines[i * 2 + 1], vertices[i], VUNITIZE_TOL))
bu_exit(1, "ERROR: selected BOT face marker line %d is misplaced\n", i);
}
bu_log("ECMD_BOT_PICKT label SUCCESS: face centroid is marked\n");
}
/* ================================================================
* ECMD_BOT_MODE (descriptor path): set mode to RT_BOT_SOLID (2)
* ================================================================*/
+21
View File
@@ -258,6 +258,27 @@ rt_edit_test_cline(void)
rt_edit_process(s);
if (cline_diff("ECMD_CLINE_MOVE_H", cmp, edit_cline))
bu_exit(1, "ERROR: ECMD_CLINE_MOVE_H failed\n");
cline_reset(s, edit_cline, orig, cmp);
s->e_inpara = 3;
s->local2base = 25.4;
s->base2local = 1.0 / s->local2base;
VSCALE(s->e_para, orig->v, s->base2local);
if (rt_edit_process(s) != BRLCAD_ERROR ||
cline_diff("rejected zero-height move", cmp, edit_cline))
bu_exit(1, "ERROR: rejected CLINE height move changed geometry\n");
s->local2base = 1.0;
s->base2local = 1.0;
cline_reset(s, edit_cline, orig, cmp);
VMOVE(s->curr_e_axes_pos, orig->v);
point_t rejected_view_target;
MAT4X3PNT(rejected_view_target, v->gv_model2view, orig->v);
VMOVE(mousevec, rejected_view_target);
if (EDOBJ[dp->d_minor_type].ft_edit_xy(s, mousevec) != BRLCAD_ERROR ||
cline_diff("rejected mouse zero-height move", cmp, edit_cline))
bu_exit(1, "ERROR: rejected CLINE mouse height move changed geometry\n");
VADD2(s->curr_e_axes_pos, orig->v, orig->h);
bu_log("ECMD_CLINE_MOVE_H SUCCESS: h=%g,%g,%g\n", V3ARGS(edit_cline->h));
/* ================================================================
+21
View File
@@ -232,6 +232,27 @@ rt_edit_test_extrude(void)
rt_edit_process(s);
if (extr_diff("ECMD_EXTR_MOV_H", cmp, edit_extr))
bu_exit(1, "ERROR: ECMD_EXTR_MOV_H failed\n");
extr_reset(s, edit_extr, orig, cmp);
s->e_inpara = 3;
s->local2base = 25.4;
s->base2local = 1.0 / s->local2base;
VSCALE(s->e_para, orig->V, s->base2local);
if (rt_edit_process(s) != BRLCAD_ERROR ||
extr_diff("rejected zero-height move", cmp, edit_extr))
bu_exit(1, "ERROR: rejected EXTRUDE height move changed geometry\n");
s->local2base = 1.0;
s->base2local = 1.0;
extr_reset(s, edit_extr, orig, cmp);
VMOVE(s->curr_e_axes_pos, orig->V);
point_t rejected_view_target;
MAT4X3PNT(rejected_view_target, v->gv_model2view, orig->V);
VMOVE(mousevec, rejected_view_target);
if (EDOBJ[dp->d_minor_type].ft_edit_xy(s, mousevec) != BRLCAD_ERROR ||
extr_diff("rejected mouse zero-height move", cmp, edit_extr))
bu_exit(1, "ERROR: rejected EXTRUDE mouse height move changed geometry\n");
VADD2(s->curr_e_axes_pos, orig->V, orig->h);
bu_log("ECMD_EXTR_MOV_H SUCCESS: h=%g,%g,%g\n", V3ARGS(edit_extr->h));
/* ================================================================
+14
View File
@@ -178,6 +178,20 @@ rt_edit_test_hyp(void)
vect_t mousevec;
{
struct rt_point_labels labels[5] = {RT_POINT_LABELS_INIT};
mat_t identity;
MAT_IDN(identity);
int label_count = OBJ[ID_HYP].ft_labels(labels, 5, identity,
&s->es_int, &tol);
point_t expected_c = {2.0, 0.0, 5.0};
if (label_count != 5 || !BU_STR_EQUAL(labels[4].str, "c") ||
!VNEAR_EQUAL(labels[4].pt, expected_c, VUNITIZE_TOL))
bu_exit(1, "ERROR: HYP neck ratio label is missing or misplaced\n");
bu_log("HYP neck ratio label SUCCESS: c=%g,%g,%g\n",
V3ARGS(labels[4].pt));
}
/* ================================================================
* ECMD_HYP_H (scale Hi; note: e_para[0] is es_scale, not |Hi|)
* MGED: es_scale = e_para[0] (scale factor); Hi' = Hi * es_scale
+67 -5
View File
@@ -144,16 +144,66 @@ check_unit(fastf_t local2base, const char *unit)
rt_edit_checkpoint(edit) != BRLCAD_OK)
++failures;
uint8_t *saved_checkpoint = edit->es_ckpt.ext_buf;
size_t saved_size = edit->es_ckpt.ext_nbytes;
/* Force export failure without altering the saved checkpoint. */
edit->es_int.idb_type = -1;
int checkpoint_result = rt_edit_checkpoint(edit);
edit->es_int.idb_type = ID_ELL;
if (checkpoint_result != BRLCAD_ERROR ||
edit->es_ckpt.ext_buf != saved_checkpoint ||
edit->es_ckpt.ext_nbytes != saved_size || !same_ell(edit, 4))
++failures;
/* This placeholder has no importer; failed revert must preserve ELL. */
edit->es_int.idb_type = ID_UNUSED1;
int revert_result = rt_edit_revert(edit);
edit->es_int.idb_type = ID_ELL;
if (revert_result != BRLCAD_ERROR || !same_ell(edit, 4) ||
edit->es_ckpt.ext_buf != saved_checkpoint)
++failures;
bu_log("failed checkpoint/revert preserve state\t%s\t%s\n", unit,
failures ? "fail" : "pass");
rt_edit_set_edflag(edit, RT_PARAMS_EDIT_SCALE);
edit->update_views = 0;
edit->e_inpara = 2;
edit->e_mvalid = 1;
edit->es_scale = 2.0;
edit->e_para[0] = 2.0;
edit->e_para[1] = 3.0;
if (rt_edit_process(edit) != BRLCAD_ERROR ||
edit->e_inpara || edit->e_mvalid || !ZERO(edit->es_scale) ||
edit->update_views || !EQUAL(edit->e_para[0], 2.0) ||
!same_ell(edit, 4))
++failures;
rt_edit_set_edflag(edit, ECMD_ELL_SCALE_A);
if (rt_edit_process(edit) != BRLCAD_OK || !same_ell(edit, 4) ||
edit->update_views != 1)
++failures;
if (rt_edit_process(edit) != BRLCAD_OK || edit->update_views != 1)
++failures;
bu_log("failed edit consumes input\t%s\t%s\n", unit,
failures ? "fail" : "pass");
const fastf_t target_length = 2.0 * inch_to_mm;
rt_edit_set_edflag(edit, ECMD_ELL_SCALE_A);
edit->mv_context = 1;
edit->e_inpara = 1;
edit->e_para[0] = target_length / local2base;
if (rt_edit_process(edit) != BRLCAD_OK ||
!same_ell(edit, target_length) ||
!NEAR_EQUAL(edit->e_para[0], target_length / local2base,
VUNITIZE_TOL) ||
rt_edit_revert(edit) != BRLCAD_OK || !same_ell(edit, 4))
int edit_result = rt_edit_process(edit);
bool scaled = same_ell(edit, target_length) &&
NEAR_EQUAL(edit->e_para[0], target_length / local2base,
VUNITIZE_TOL);
edit->acc_sc_sol = 2.5;
VSET(edit->e_keypoint, -1, -1, -1);
point_t original_keypoint = {10, 5, 20};
if (edit_result != BRLCAD_OK || !scaled ||
rt_edit_revert(edit) != BRLCAD_OK || !same_ell(edit, 4) ||
!EQUAL(edit->acc_sc_sol, 1.0) ||
!VNEAR_EQUAL(edit->e_keypoint, original_keypoint, VUNITIZE_TOL))
++failures;
if (rt_edit_revert(edit) != BRLCAD_OK || !same_ell(edit, 4))
++failures;
bu_log("checkpoint/edit/revert\t%s\t%s\n", unit,
failures ? "fail" : "pass");
@@ -194,6 +244,18 @@ int
rt_edit_test_lifecycle_matrix(void)
{
int failures = check_map();
struct rt_edit *idle = rt_edit_create(NULL, NULL, NULL, NULL);
if (!idle)
return BRLCAD_ERROR;
idle->e_inpara = 1;
idle->e_mvalid = 1;
idle->es_scale = 2.0;
if (rt_edit_process(NULL) != BRLCAD_ERROR ||
rt_edit_process(idle) != BRLCAD_ERROR ||
idle->e_inpara || idle->e_mvalid || !ZERO(idle->es_scale) ||
idle->update_views)
++failures;
rt_edit_destroy(idle);
failures += check_unit(1.0, "mm");
failures += check_unit(inch_to_mm, "in");
return failures ? BRLCAD_ERROR : BRLCAD_OK;
+94
View File
@@ -282,6 +282,29 @@ nmg_tet_state(const struct rt_edit *edit,
nmg_points_match(edit, expected, NMG_TET_VERTEX_COUNT);
}
static bool
nmg_tet_face_planes(const struct model *model,
fastf_t planes[NMG_TET_FACE_COUNT][ELEMENTS_PER_PLANE])
{
size_t count = 0;
struct nmgregion *region;
for (BU_LIST_FOR(region, nmgregion, &model->r_hd)) {
struct shell *shell;
for (BU_LIST_FOR(shell, shell, &region->s_hd)) {
struct faceuse *face;
for (BU_LIST_FOR(face, faceuse, &shell->fu_hd)) {
if (face->orientation != OT_SAME)
continue;
if (count == NMG_TET_FACE_COUNT || !face->f_p->g.plane_p)
return false;
memcpy(planes[count++], face->f_p->g.plane_p->N,
sizeof(plane_t));
}
}
}
return count == NMG_TET_FACE_COUNT;
}
static int
nmg_tet_step(struct rt_edit *edit, const char *unit, const char *name,
int command, const fastf_t *params, int count,
@@ -354,6 +377,34 @@ nmg_operation_matrix_unit(fastf_t local2base, const char *unit)
ECMD_NMG_EKILL, NULL, 0, expected, -1, false, true);
failures += nmg_tet_step(edit, unit, "reject extrusion without loop",
ECMD_NMG_LEXTRU_DIR, extrude_direction, 4, expected, -1, false, true);
/* A target at the opposite face vertex makes its new plane degenerate. */
struct rt_nmg_edit *selection = (struct rt_nmg_edit *)edit->ipe_ptr;
struct model *model = (struct model *)edit->es_int.idb_ptr;
struct nmgregion *region = BU_LIST_FIRST(nmgregion, &model->r_hd);
struct shell *shell = BU_LIST_FIRST(shell, &region->s_hd);
struct faceuse *face = BU_LIST_FIRST(faceuse, &shell->fu_hd);
if (face->orientation != OT_SAME)
face = face->fumate_p;
struct loopuse *loop = BU_LIST_FIRST(loopuse, &face->lu_hd);
selection->es_eu = BU_LIST_FIRST(edgeuse, &loop->down_hd);
struct edgeuse *next = BU_LIST_PNEXT_CIRC(edgeuse, &selection->es_eu->l);
point_t degenerate_target;
VSCALE(degenerate_target, next->eumate_p->vu_p->v_p->vg_p->coord,
1.0 / local2base);
fastf_t before_planes[NMG_TET_FACE_COUNT][ELEMENTS_PER_PLANE];
fastf_t after_planes[NMG_TET_FACE_COUNT][ELEMENTS_PER_PLANE];
bool have_planes = nmg_tet_face_planes(model, before_planes);
failures += nmg_tet_step(edit, unit, "reject degenerate edge move",
ECMD_NMG_EMOVE, degenerate_target, 3, expected, -1, false, true);
if (!have_planes || !nmg_tet_face_planes(
(struct model *)edit->es_int.idb_ptr, after_planes) ||
memcmp(before_planes, after_planes, sizeof(before_planes))) {
bu_log("nmg\treject degenerate edge move planes\t%s\tfail\n", unit);
++failures;
}
selection->es_eu = NULL;
failures += nmg_tet_step(edit, unit, "reject fractional vertex index",
ECMD_NMG_VPICK, bad_pick, 1, expected, -1, false, true);
failures += nmg_tet_step(edit, unit, "pick vertex",
@@ -375,6 +426,45 @@ nmg_operation_matrix_unit(fastf_t local2base, const char *unit)
failures += nmg_tet_step(edit, unit, "move face",
ECMD_NMG_FMOVE, face_delta, 3, expected, 1, true);
/* A successful face-edge move must remap all live NMG selections. */
model = (struct model *)edit->es_int.idb_ptr;
region = BU_LIST_FIRST(nmgregion, &model->r_hd);
shell = BU_LIST_FIRST(shell, &region->s_hd);
face = BU_LIST_FIRST(faceuse, &shell->fu_hd);
if (face->orientation != OT_SAME)
face = face->fumate_p;
loop = BU_LIST_FIRST(loopuse, &face->lu_hd);
selection->es_eu = BU_LIST_FIRST(edgeuse, &loop->down_hd);
point_t edge_target;
VMOVE(edge_target, selection->es_eu->vu_p->v_p->vg_p->coord);
edge_target[Y] += 0.1;
edge_target[Z] += 0.1;
rt_edit_set_edflag(edit, ECMD_NMG_EMOVE);
edit->e_inpara = 3;
VSCALE(edit->e_para, edge_target, 1.0 / local2base);
int edge_result = rt_edit_process(edit);
struct model *moved_model = (struct model *)edit->es_int.idb_ptr;
if (edge_result != BRLCAD_OK || moved_model == model ||
nmg_find_model(&selection->es_eu->l.magic) != moved_model ||
nmg_find_model(&selection->es_v->magic) != moved_model ||
nmg_find_model(&selection->es_fu->l.magic) != moved_model) {
bu_log("nmg\tmove face edge and remap selections\t%s\tfail\n", unit);
++failures;
}
if (rt_edit_checkpoint(edit) != BRLCAD_OK) {
bu_log("nmg\tcheckpoint selected model\t%s\tfail\n", unit);
++failures;
} else {
selection->es_v->vg_p->coord[X] += 1.0;
if (rt_edit_revert(edit) != BRLCAD_OK || selection->es_eu ||
selection->es_v || selection->es_fu ||
!edit->es_int.idb_ptr) {
bu_log("nmg\trevert clears selected topology\t%s\tfail\n", unit);
++failures;
}
}
rt_edit_destroy(edit);
db_free_full_path(&path);
db_close(dbip);
@@ -702,6 +792,7 @@ rt_edit_test_nmg(void)
bu_list_len(&wire_loop->down_hd) != edge_count ||
!nmg_points_match(ws, wire_expected, NMG_WIRE_VERTEX_COUNT))
bu_exit(1, "ERROR: NMG accepted a nonfinite edge split\n");
ws->e_inpara = 3;
VSCALE(ws->e_para, edge_midpoint, 1.0 / local2base);
point_t split_input;
VMOVE(split_input, ws->e_para);
@@ -765,6 +856,7 @@ rt_edit_test_nmg(void)
if (rt_edit_process(ws) == BRLCAD_OK ||
!nmg_points_match(ws, wire_expected, NMG_WIRE_VERTEX_COUNT))
bu_exit(1, "ERROR: NMG accepted a zero extrusion direction\n");
ws->e_inpara = 4;
ws->e_para[0] = 0.0; /* dir X */
ws->e_para[1] = 0.0; /* dir Y */
ws->e_para[2] = 1.0; /* dir Z */
@@ -807,6 +899,7 @@ rt_edit_test_nmg(void)
!nmg_points_match(ws, wire_and_prism_expected,
NMG_WIRE_AND_PRISM_VERTEX_COUNT))
bu_exit(1, "ERROR: NMG invalid preview target changed the solid\n");
ws->e_inpara = 4;
ws->e_para[Z] = 1.0;
if (rt_edit_process(ws) != BRLCAD_OK)
bu_exit(1, "ERROR: NMG repeated extrusion failed\n");
@@ -834,6 +927,7 @@ rt_edit_test_nmg(void)
!nmg_points_match(ws, wire_and_prism_expected,
NMG_WIRE_AND_PRISM_VERTEX_COUNT))
bu_exit(1, "ERROR: NMG accepted a nonfinite extrusion distance\n");
ws->e_inpara = 1;
ws->e_para[0] = 1.0 / local2base;
if (rt_edit_process(ws) != BRLCAD_OK)
bu_exit(1, "ERROR: NMG scalar extrusion failed\n");
+70
View File
@@ -26,6 +26,7 @@
#include "common.h"
#include <string>
#include <string.h>
#include "bu/log.h"
@@ -262,6 +263,56 @@ same_arb_vertices(const struct rt_arb_internal *arb,
return true;
}
static int
check_atomic_param_rejection(const struct param_case *test,
struct rt_db_internal *ip, const struct bn_tol *tol,
fastf_t local2base, const char *output)
{
std::string truncated(output);
if (!truncated.empty() && truncated.back() == '\n')
truncated.pop_back();
size_t last_line = truncated.find_last_of('\n');
if (last_line == std::string::npos)
return 1;
truncated.erase(last_line + 1);
const std::string invalid[] = {
truncated,
truncated + "invalid\n",
std::string(output) + "unexpected: 1\n"
};
void *before = bu_malloc(test->parsed_size, "parameter reader snapshot");
memcpy(before, test->parsed, test->parsed_size);
int failures = 0;
for (const std::string &text : invalid) {
memcpy(test->parsed, before, test->parsed_size);
int result = EDOBJ[test->type].ft_read_params(ip, text.c_str(), tol,
local2base);
if (result != BRLCAD_ERROR || !test->same(test->parsed, before))
failures++;
}
memcpy(test->parsed, before, test->parsed_size);
bu_free(before, "parameter reader snapshot");
return failures;
}
static bool
check_atomic_reader_type(int type)
{
switch (type) {
case ID_EPA:
case ID_EHY:
case ID_ETO:
case ID_HYP:
case ID_RPC:
case ID_RHC:
case ID_TGC:
return true;
default:
return false;
}
}
static int
run_case(const struct param_case *test, fastf_t local2base, const char *unit)
{
@@ -296,6 +347,25 @@ run_case(const struct param_case *test, fastf_t local2base, const char *unit)
bu_log("%s\tparams\t%s\twrong reconstructed geometry\n", test->name, unit);
failed = 1;
}
if (check_atomic_reader_type(test->type)) {
std::string crlf;
for (const char *p = output; *p; ++p) {
if (*p == '\n')
crlf.push_back('\r');
crlf.push_back(*p);
}
if (edit->ft_read_params(&ip, crlf.c_str(), &tol, local2base) != BRLCAD_OK ||
!test->same(test->original, test->parsed)) {
bu_log("%s\tparams\t%s\tCRLF reconstruction failed\n", test->name, unit);
failed = 1;
}
}
if (check_atomic_reader_type(test->type) &&
check_atomic_param_rejection(test, &ip, &tol, local2base, output)) {
bu_log("%s\tparams\t%s\trejected edit changed geometry\n",
test->name, unit);
failed = 1;
}
if (!failed)
bu_log("%s\tparams\t%s\tpass\n", test->name, unit);
bu_vls_free(&text);
+11
View File
@@ -1305,6 +1305,17 @@ bu_log("RT_MATRIX_EDIT_TRANS_MODEL_XYZ SUCCESS: "
!NEAR_EQUAL(pipe_second(s)->pp_bendradius, 0.5 * inch, SMALL_FASTF))
bu_exit(1, "ERROR: inch whole-pipe bend could not be set from zero\n");
pipe_full_reset(s, pe);
if (rt_edit_checkpoint(s) != BRLCAD_OK)
bu_exit(1, "ERROR: pipe checkpoint failed\n");
pe->es_pipe_pnt = pipe_second(s);
pipe_first(s)->pp_coord[X] = inch;
VSET(s->e_keypoint, -1, -1, -1);
if (rt_edit_revert(s) != BRLCAD_OK || pe->es_pipe_pnt ||
!ZERO(pipe_first(s)->pp_coord[X]) ||
!VNEAR_ZERO(s->e_keypoint, VUNITIZE_TOL))
bu_exit(1, "ERROR: pipe revert retained stale geometry or selection\n");
int matrix_failures = pipe_transform_matrix(dbip, &fp, &tol, v);
matrix_failures += pipe_dimension_matrix(dbip, &fp, &tol);
matrix_failures += pipe_point_matrix(dbip, &fp, &tol);
+39
View File
@@ -24,6 +24,9 @@
#include "common.h"
#include <math.h>
#include <string.h>
#include "vmath.h"
#include "bu/log.h"
#include "bu/malloc.h"
@@ -156,6 +159,32 @@ check_rec_units(const fastf_t local2base)
!NEAR_EQUAL(MAGNITUDE(rec->b), 2.5 * inch, VUNITIZE_TOL))
bu_exit(1, "REC equal-radius edit did not use local units\n");
const struct rt_tgc_internal saved_rec = *rec;
EDOBJ[ID_REC].ft_set_edit_mode(s, ECMD_REC_SET_V);
s->e_inpara = 3;
VSET(s->e_para, NAN, 0.0, 0.0);
if (EDOBJ[ID_REC].ft_edit(s) != BRLCAD_ERROR ||
memcmp(rec, &saved_rec, sizeof(saved_rec)))
bu_exit(1, "REC accepted an invalid center or changed geometry\n");
EDOBJ[ID_REC].ft_set_edit_mode(s, ECMD_REC_SET_H);
VSET(s->e_para, 0.0, 0.0, tol.dist * 0.5 / local2base);
if (EDOBJ[ID_REC].ft_edit(s) != BRLCAD_ERROR ||
memcmp(rec, &saved_rec, sizeof(saved_rec)))
bu_exit(1, "REC accepted a near-zero height or changed geometry\n");
const int radius_commands[] = {
ECMD_REC_SCALE_R1, ECMD_REC_SCALE_R2, ECMD_REC_SCALE_R
};
for (size_t i = 0; i < sizeof(radius_commands) / sizeof(radius_commands[0]); i++) {
EDOBJ[ID_REC].ft_set_edit_mode(s, radius_commands[i]);
s->e_inpara = 1;
s->e_para[0] = NAN;
if (EDOBJ[ID_REC].ft_edit(s) != BRLCAD_ERROR ||
memcmp(rec, &saved_rec, sizeof(saved_rec)))
bu_exit(1, "REC accepted an invalid radius or changed geometry\n");
}
s->e_inpara = 1;
s->e_para[0] = 0.0;
if (EDOBJ[ID_REC].ft_edit(s) != BRLCAD_ERROR ||
@@ -175,6 +204,16 @@ check_rec_units(const fastf_t local2base)
EDOBJ[ID_REC].ft_edit_get_params(s, ECMD_REC_SCALE_R, NULL) >= 0)
bu_exit(1, "REC getter accepted invalid arguments\n");
point_t saved_center;
VMOVE(saved_center, rec->v);
if (rt_edit_checkpoint(s) != BRLCAD_OK)
bu_exit(1, "REC checkpoint failed\n");
rec->v[X] += inch;
if (rt_edit_revert(s) != BRLCAD_OK || s->es_int.idb_type != ID_REC ||
!VNEAR_EQUAL(((struct rt_tgc_internal *)s->es_int.idb_ptr)->v,
saved_center, VUNITIZE_TOL))
bu_exit(1, "REC revert lost its type or geometry\n");
rt_edit_destroy(s);
db_free_full_path(&fp);
db_close(dbip);
+10
View File
@@ -420,6 +420,16 @@ rt_edit_test_sph(void)
bu_exit(1, "SPH radius command did not restore degenerate axes\n");
}
point_t saved_center;
VMOVE(saved_center, ell->v);
if (rt_edit_checkpoint(s) != BRLCAD_OK)
bu_exit(1, "SPH checkpoint failed\n");
ell->v[X] += 1.0;
if (rt_edit_revert(s) != BRLCAD_OK || s->es_int.idb_type != ID_SPH ||
!VNEAR_EQUAL(((struct rt_ell_internal *)s->es_int.idb_ptr)->v,
saved_center, VUNITIZE_TOL))
bu_exit(1, "SPH revert lost its type or geometry\n");
rt_edit_destroy(s);
db_close(dbip);
return 0;
+28
View File
@@ -386,6 +386,34 @@ rt_edit_test_tgc(void)
bu_exit(1, "ERROR: ECMD_TGC_MV_HH failed\n");
bu_log("ECMD_TGC_MV_HH SUCCESS: h=%g,%g,%g\n", V3ARGS(edit_tgc->h));
const int height_moves[] = {ECMD_TGC_MV_H, ECMD_TGC_MV_HH};
for (size_t i = 0; i < sizeof(height_moves) / sizeof(height_moves[0]); i++) {
tgc_reset(s, edit_tgc, orig_tgc, cmp_tgc);
EDOBJ[dp->d_minor_type].ft_set_edit_mode(s, height_moves[i]);
s->local2base = 25.4;
s->base2local = 1.0 / s->local2base;
s->e_inpara = 3;
VSCALE(s->e_para, orig_tgc->v, s->base2local);
int result = rt_edit_process(s);
if (result != BRLCAD_ERROR ||
tgc_diff("rejected zero-height move", cmp_tgc, edit_tgc))
bu_exit(1, "ERROR: rejected TGC height move changed geometry (mode %d, result %d, h %g %g %g)\n",
height_moves[i], result, V3ARGS(edit_tgc->h));
s->local2base = 1.0;
s->base2local = 1.0;
}
tgc_reset(s, edit_tgc, orig_tgc, cmp_tgc);
EDOBJ[dp->d_minor_type].ft_set_edit_mode(s, ECMD_TGC_MV_H);
VMOVE(s->curr_e_axes_pos, orig_tgc->v);
point_t rejected_view_target;
MAT4X3PNT(rejected_view_target, v->gv_model2view, orig_tgc->v);
VMOVE(mousevec, rejected_view_target);
if (EDOBJ[dp->d_minor_type].ft_edit_xy(s, mousevec) != BRLCAD_ERROR ||
tgc_diff("rejected mouse zero-height move", cmp_tgc, edit_tgc))
bu_exit(1, "ERROR: rejected TGC mouse height move changed geometry\n");
VADD2(s->curr_e_axes_pos, orig_tgc->v, orig_tgc->h);
/* ================================================================
* ECMD_TGC_ROT_H (rotate H vector by (5,5,5) degrees about keypoint=v)
* h_new = R * h where R = bn_mat_angles(5,5,5)
+16
View File
@@ -240,6 +240,22 @@ rt_edit_test_vol(void)
bu_log("RT_PARAMS_EDIT_SCALE SUCCESS: mat[15]=%g (encodes scale=2)\n",
edit_vol->mat[15]);
vol_reset(s, edit_vol);
s->e_inpara = 2;
s->e_para[0] = 2.0;
s->e_para[1] = 3.0;
if (rt_edit_process(s) != BRLCAD_ERROR ||
!NEAR_EQUAL(edit_vol->mat[15], 1.0, VUNITIZE_TOL))
bu_exit(1, "ERROR: rejected generic VOL scale changed geometry or succeeded\n");
vol_reset(s, edit_vol);
s->e_inpara = 1;
s->e_para[0] = 0.0;
if (rt_edit_process(s) != BRLCAD_ERROR ||
!NEAR_EQUAL(edit_vol->mat[15], 1.0, VUNITIZE_TOL) ||
!NEAR_EQUAL(s->acc_sc_sol, 1.0, VUNITIZE_TOL))
bu_exit(1, "ERROR: rejected zero VOL scale changed edit state\n");
/* ================================================================
* RT_PARAMS_EDIT_TRANS (translate; keypoint (0,0,0) → e_para=(5,5,5))
*

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