Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31470287a1 | ||
|
|
342d8a9152 | ||
|
|
9e030edd60 | ||
|
|
b5bf1683a2 | ||
|
|
eb7e9cb38c | ||
|
|
84be85a74d | ||
|
|
3fd9ec8f4b | ||
|
|
7d2392d754 | ||
|
|
757bb76203 | ||
|
|
c70bab52c4 | ||
|
|
7929a747ad | ||
|
|
eac0f4d265 | ||
|
|
d6ae424de0 | ||
|
|
27ba3c4880 | ||
|
|
7ade8db130 | ||
|
|
e91023f354 | ||
|
|
303b90f7f1 | ||
|
|
875091f78a | ||
|
|
0716e4c842 | ||
|
|
ee6e1bebdc | ||
|
|
9d4f5bb124 | ||
|
|
21f442a4ad | ||
|
|
24d6f6f4cb | ||
|
|
d3e037a760 | ||
|
|
5a38423677 | ||
|
|
38113b1de7 | ||
|
|
fec9d07d43 | ||
|
|
4b09d6227f | ||
|
|
eab1e82de2 | ||
|
|
f7a8d5b406 | ||
|
|
85de3929e5 | ||
|
|
0a8c6d8bc2 | ||
|
|
a00efd15c6 | ||
|
|
06c8ab6204 | ||
|
|
cd55562c46 | ||
|
|
6d142b62e7 | ||
|
|
da2f503367 | ||
|
|
a9ac66788a | ||
|
|
4cc9f9cf01 | ||
|
|
7b1898d9fd | ||
|
|
e4d297893e | ||
|
|
0173dd8c40 | ||
|
|
cf344d773e | ||
|
|
8c51c1d527 | ||
|
|
6e48ea9ddd | ||
|
|
488f538040 | ||
|
|
17cc9f3a5a | ||
|
|
bf2c508826 | ||
|
|
f2e68b1a2a | ||
|
|
5e873e5081 | ||
|
|
3beed70eb8 | ||
|
|
73258d4264 | ||
|
|
8ae7482918 | ||
|
|
917ce5554c | ||
|
|
58ab0d5496 | ||
|
|
13b0699349 | ||
|
|
acd3a9800d | ||
|
|
03b36c1b61 | ||
|
|
b714d88185 | ||
|
|
eff3fea479 | ||
|
|
7358e90b54 | ||
|
|
3c2b0de9d2 | ||
|
|
a384f838fb | ||
|
|
1053594e9a | ||
|
|
72cde263cc | ||
|
|
b670a19aba | ||
|
|
2d9fa5c033 | ||
|
|
8be0f0ae85 | ||
|
|
00ffa4bd80 | ||
|
|
e5e0905d03 | ||
|
|
10d13793b0 | ||
|
|
70d5180438 | ||
|
|
50fe626694 | ||
|
|
ca403c92ed | ||
|
|
bab959474f | ||
|
|
27b522c155 | ||
|
|
875231c335 | ||
|
|
89810b06ca | ||
|
|
90e512b5d6 | ||
|
|
19fe382208 | ||
|
|
994c56b05f | ||
|
|
db76164ea8 | ||
|
|
58c5ed3681 | ||
|
|
05bd5c7470 | ||
|
|
2276f4ef8c | ||
|
|
188e72eb32 | ||
|
|
03306043ea | ||
|
|
28038fe0ea | ||
|
|
5b5cb8c538 | ||
|
|
1c20d18156 | ||
|
|
97920fbc90 | ||
|
|
37fb7d928a | ||
|
|
2056b359bd | ||
|
|
b4cf00292a | ||
|
|
6748dd37ab | ||
|
|
ef35c1dca1 | ||
|
|
8cdedd8752 | ||
|
|
e4ba0b166c | ||
|
|
586ac06c7e |
@@ -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
|
||||
|
||||
@@ -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() }}
|
||||
|
||||
+36
-20
@@ -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.
|
||||
@@ -678,8 +679,13 @@ endif(MSVC)
|
||||
# before use so platforms without GNU/LLVM-style linker options are skipped.
|
||||
brlcad_check_linker_flag(C "LINKER:--no-undefined" BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND)
|
||||
brlcad_check_linker_flag(CXX "LINKER:--no-undefined" BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND)
|
||||
if(BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND AND BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND)
|
||||
if(BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND AND BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND
|
||||
AND NOT BRLCAD_ENABLE_ADDRESS_SANITIZER)
|
||||
set(BRLCAD_LINKER_NO_UNDEFINED_FLAG "LINKER:--no-undefined" CACHE STRING "Linker flag requiring resolved shared library symbols" FORCE)
|
||||
else()
|
||||
# Clang links the AddressSanitizer runtime into executables, leaving its
|
||||
# shared-library instrumentation symbols unresolved until then.
|
||||
set(BRLCAD_LINKER_NO_UNDEFINED_FLAG "" CACHE STRING "Linker flag requiring resolved shared library symbols" FORCE)
|
||||
endif()
|
||||
brlcad_check_linker_flags(C BRLCAD_LINKER_WHOLE_ARCHIVE_GROUP_C_FLAG_FOUND "LINKER:--whole-archive" "LINKER:--no-whole-archive")
|
||||
brlcad_check_linker_flags(CXX BRLCAD_LINKER_WHOLE_ARCHIVE_GROUP_CXX_FLAG_FOUND "LINKER:--whole-archive" "LINKER:--no-whole-archive")
|
||||
@@ -1642,21 +1648,31 @@ if(NOT MSVC)
|
||||
endif(BRLCAD_WARNINGS OR BRLCAD_ENABLE_STRICT)
|
||||
|
||||
if(BRLCAD_ENABLE_COVERAGE)
|
||||
# TODO: These seem to GCC specific flags - should probably set up for clang
|
||||
# as well if that's our compiler...
|
||||
# https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
|
||||
# If we need different settings for different tools, should
|
||||
# encapsulate each tool's setup in a function
|
||||
check_c_flag(ftest-coverage)
|
||||
check_cxx_flag(ftest-coverage)
|
||||
if(FTEST_COVERAGE_C_FLAG_FOUND)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
else(FTEST_COVERAGE_C_FLAG_FOUND)
|
||||
message(SEND_ERROR "Building with coverage is not supported by BRL-CAD on this platform.")
|
||||
endif(FTEST_COVERAGE_C_FLAG_FOUND)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
check_c_flag(fprofile-instr-generate)
|
||||
check_cxx_flag(fprofile-instr-generate)
|
||||
check_c_flag(fcoverage-mapping)
|
||||
check_cxx_flag(fcoverage-mapping)
|
||||
if(NOT FPROFILE_INSTR_GENERATE_C_FLAG_FOUND OR
|
||||
NOT FPROFILE_INSTR_GENERATE_CXX_FLAG_FOUND OR
|
||||
NOT FCOVERAGE_MAPPING_C_FLAG_FOUND OR
|
||||
NOT FCOVERAGE_MAPPING_CXX_FLAG_FOUND)
|
||||
message(FATAL_ERROR "Clang source-based coverage is not supported by this toolchain.")
|
||||
endif()
|
||||
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
|
||||
add_link_options(-fprofile-instr-generate)
|
||||
else()
|
||||
check_c_flag(ftest-coverage)
|
||||
check_cxx_flag(ftest-coverage)
|
||||
if(FTEST_COVERAGE_C_FLAG_FOUND AND FTEST_COVERAGE_CXX_FLAG_FOUND)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
|
||||
else()
|
||||
message(FATAL_ERROR "GCC coverage is not supported by this toolchain.")
|
||||
endif()
|
||||
endif()
|
||||
endif(BRLCAD_ENABLE_COVERAGE)
|
||||
|
||||
if(BRLCAD_ENABLE_STRICT)
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
@@ -10,78 +10,107 @@ descriptive text that emphasizes or further describes significant
|
||||
changes made. See document footer for additional details.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
--- 20XX-XX-XX Release 7.46.0 ---
|
||||
--- 2026-10-12 Release 7.46.0 ---
|
||||
----------------------------------------------------------------------
|
||||
|
||||
* updated MGED and Archer for Tcl 8.6.18 and Itcl/Itk 4 - Cliff Yapp
|
||||
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
|
||||
* improved pixscale & bwscale scaling and image piping - Sean Morrison
|
||||
* restored pixdiff behavior on .pix, added -b for .bw - Sean Morrison
|
||||
* 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
|
||||
@@ -103,26 +132,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
|
||||
@@ -131,7 +168,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
|
||||
@@ -142,14 +179,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
|
||||
@@ -161,7 +200,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
|
||||
@@ -270,7 +308,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
|
||||
@@ -542,6 +580,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
|
||||
@@ -580,6 +619,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,348 +0,0 @@
|
||||
# GQA sampling-method comparison
|
||||
|
||||
## Executive result
|
||||
|
||||
The new methods are a substantial practical improvement, but Crofton is not a
|
||||
uniform numerical improvement over grids.
|
||||
|
||||
- **Crofton QMC is the best general-purpose stochastic method.** It is far
|
||||
more accurate than ordinary random Crofton at the same ray count, avoids the
|
||||
severe orientation bias seen in the legacy grid on thin or oblique models,
|
||||
supports uncertainty intervals, and reached a 1% volume-interval target on
|
||||
every bounded `share/db` case in the 10-second study.
|
||||
- **Rotated grid is the strongest deterministic control.** It converges very
|
||||
quickly on the analytic fixtures and usually agrees closely with Crofton on
|
||||
complex-model volume, centroid, and inertia. It is not uniformly reliable:
|
||||
the full Generic Twin surface-area result remains about 6.5% above the two
|
||||
Crofton results.
|
||||
- **Legacy axis-aligned GQA remains useful for compatibility, not truth.** It
|
||||
can be excellent on aligned or symmetric geometry, but agreement among its
|
||||
three axial views does not detect shared grid bias. Havoc volume is 18.2%
|
||||
above the new-method consensus at the finest practical legacy grid tested.
|
||||
- **Ordinary random Crofton is primarily a control.** Its observed error falls
|
||||
close to the expected inverse-square-root rate, but QMC needs substantially
|
||||
fewer rays for the same precision on these tests.
|
||||
|
||||
The recommended experimental default is therefore time-first QMC with an
|
||||
explicit accuracy status, retaining rotated grid and random Crofton as
|
||||
diagnostic controls. Legacy output should remain available as a compatibility
|
||||
and comparison control. The experimental path now supplies conservative
|
||||
bounds, traditional Plot3 files, bounded representative plots, and incremental
|
||||
MGED visualization.
|
||||
|
||||
## Test protocol
|
||||
|
||||
- Analytic fixtures: a 1000 mm cube and radius-1000 mm sphere.
|
||||
- Solid density: synthetic 1 g/cm^3. Consequently mass relative error equals
|
||||
volume relative error. The air fixture uses 0.001 g/cm^3 for modeled air.
|
||||
- Random and QMC convergence values are medians across ten fixed outer seeds.
|
||||
- Comparable-effort analytic table: 76,800 legacy rays, about 60,000 rotated
|
||||
rays, and 100,000 random/QMC rays.
|
||||
- Complex-model comparison: the finest practical legacy grid, the deepest
|
||||
completed rotated grid, and fixed high-ray Crofton runs. Those are
|
||||
stabilization comparisons, not strictly equal-work comparisons.
|
||||
- The four complex models are Havoc, the M35 cab, wrapped `Goliath.c`, and the
|
||||
complete Generic Twin `all` hierarchy.
|
||||
- Complex models have no analytic truth. “Consensus” below is the arithmetic
|
||||
mean of the three new-method values and is only a comparison reference.
|
||||
|
||||
For uncertainty, the implementation uses independent randomized replications,
|
||||
following the design guidance in A. B. Owen, [“Error estimation for quasi-Monte
|
||||
Carlo” (2025), section 5](https://arxiv.org/abs/2501.00150v3). The pilot
|
||||
forecast and its safety factor are engineering heuristics, not results
|
||||
attributed to Owen. Only fresh production replications determine the reported
|
||||
interval.
|
||||
|
||||
## Analytic measurement accuracy
|
||||
|
||||
All entries are errors; lower is better. Experimental inertia tensors were
|
||||
translated from the model origin to the object centroid before comparison with
|
||||
legacy output.
|
||||
|
||||
| Output | Fixture | Legacy axis grid | Rotated grid | Crofton random | Crofton QMC | Result |
|
||||
|---|---|---:|---:|---:|---:|---|
|
||||
| Volume | Cube | **0%** | 0.00392% | 0.18574% | 0.00674% | Legacy wins this specially aligned case |
|
||||
| Volume | Sphere | 0.01170% | 0.00679% | 0.24152% | **0.00211%** | QMC improves on legacy by 82.0% |
|
||||
| Mass | Cube | **0%** | 0.00392% | 0.18574% | 0.00674% | Same relative result as volume |
|
||||
| Mass | Sphere | 0.01170% | 0.00679% | 0.24152% | **0.00211%** | Same relative result as volume |
|
||||
| Surface area | Cube | unavailable | **0.00468%** | 0.12256% | 0.03220% | New capability; rotated wins |
|
||||
| Surface area | Sphere | unavailable | 0.01267% | 0.39150% | **0.00200%** | New capability; QMC wins |
|
||||
| Centroid | Cube | 3.6079 mm | **0.0102 mm** | 1.1650 mm | 0.1952 mm | Rotated improves on legacy by 99.7% |
|
||||
| Centroid | Sphere | **approximately 0** | 0.0261 mm | 3.0817 mm | 0.1139 mm | Axial symmetry gives legacy exact cancellation |
|
||||
| Inertia | Cube | 0.00276% | **0.00224%** | 0.32629% | 0.02884% | Rotated improves on legacy by 18.8% |
|
||||
| Inertia | Sphere | 0.01534% | **0.01222%** | 0.42519% | 0.01539% | QMC is effectively tied; rotated wins |
|
||||
|
||||
Selected relative changes make the tradeoff clearer:
|
||||
|
||||
| Comparison at comparable effort | Change relative to legacy error |
|
||||
|---|---:|
|
||||
| Sphere volume, rotated | 41.9% lower |
|
||||
| Sphere volume, QMC | 82.0% lower |
|
||||
| Sphere volume, random | 1,964% higher |
|
||||
| Cube centroid, rotated | 99.7% lower |
|
||||
| Cube centroid, QMC | 94.6% lower |
|
||||
| Cube inertia, QMC | 944% higher |
|
||||
| Cube inertia, random | 11,713% higher |
|
||||
| Sphere inertia, rotated | 20.4% lower |
|
||||
|
||||
The apparent contradictions are real sampling effects. An aligned grid can
|
||||
integrate an aligned box volume exactly while placing its estimated centroid
|
||||
at cell centers. Symmetric axial views can cancel sphere centroid error. That
|
||||
does not make axis-aligned sampling uniformly more accurate on arbitrary
|
||||
geometry.
|
||||
|
||||
## Convergence rates
|
||||
|
||||
The fitted relation is `error ~ rays^slope`; a more negative slope indicates
|
||||
faster observed error reduction. Fits span the measured sampling ladders and
|
||||
should not be extrapolated as guarantees.
|
||||
|
||||
| Method | Volume slope | Area slope | Centroid slope | Inertia slope |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Legacy axis grid | -1.025 on sphere; cube exact | unavailable | -0.500 on cube; sphere cancels | -1.000 to -1.037 |
|
||||
| Rotated grid | -0.650 to -0.918 | -0.673 to -0.681 | -0.934 to -1.118 | -0.863 to -0.898 |
|
||||
| Crofton random | -0.460 to -0.462 | -0.480 to -0.487 | -0.487 to -0.531 | -0.480 to -0.546 |
|
||||
| Crofton QMC | -0.576 to -0.696 | -0.573 to -0.753 | -0.786 to -0.844 | -0.757 to -0.834 |
|
||||
|
||||
Random Crofton behaves close to inverse square root. QMC improves every
|
||||
measured random-Crofton slope. Grid convergence is faster on these smooth
|
||||
fixtures, but its result can plateau around an orientation-specific bias.
|
||||
|
||||
The next table reports the first tested sampling rung meeting each threshold
|
||||
on **both** analytic fixtures. A dash means the threshold was not reached by
|
||||
the largest tested rung.
|
||||
|
||||
| Method | Volume 1% / 0.1% / 0.01% | Area 1% / 0.1% / 0.01% | Centroid 1 mm / 0.1 mm | Inertia 1% / 0.1% / 0.01% |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Legacy axis grid | 1,200 / 19,200 / 307,200 | — | 4,915,200 / — | 1,200 / 19,200 / 307,200 |
|
||||
| Rotated grid | 2,940 / 14,373 / 239,005 | 2,940 / 59,501 / 954,145 | 2,940 / 59,501 | 2,940 / 14,373 / 239,005 |
|
||||
| Crofton random | 25,000 / 1,600,000 / — | 25,000 / 800,000 / — | 800,000 / — | 25,000 / 3,200,000 / — |
|
||||
| Crofton QMC | 12,500 / 12,500 / 100,000 | 12,500 / 50,000 / 1,600,000 | 25,000 / 400,000 | 12,500 / 25,000 / 800,000 |
|
||||
|
||||
## Time-first and adaptive behavior on `share/db`
|
||||
|
||||
### Ten seconds or a 1% interval
|
||||
|
||||
The implemented pilot/forecast/production planner was run on all 138 bounded,
|
||||
non-half-containing roots from the original corpus. The requested result was
|
||||
a pointwise 95% volume-interval half-width no larger than 1%, with a 10-second
|
||||
deadline and no ray cap.
|
||||
|
||||
| Outcome | Result |
|
||||
|---|---:|
|
||||
| Target met | **138 / 138** |
|
||||
| Median rays | 300,000 |
|
||||
| 90th-percentile rays | 1,863,584 |
|
||||
| Maximum rays | 45,943,072 |
|
||||
| Median time | 0.11 s |
|
||||
| 90th-percentile time | 1.41 s |
|
||||
| 95th-percentile time | 2.53 s |
|
||||
| Maximum time | 8.40 s |
|
||||
| Median relative half-width | 0.368% |
|
||||
| 90th-percentile relative half-width | 0.652% |
|
||||
| Maximum relative half-width | 0.896% |
|
||||
|
||||
This replaces the misleading earlier 100,000-total-ray result, where only
|
||||
49/138 met 1% because the cap also paid for a discarded pilot and often left
|
||||
only 25,000–75,000 production rays. Ray count varies by more than two orders
|
||||
of magnitude across the corpus, while all wall times remained under the
|
||||
declared deadline. That strongly supports time as the primary user-facing
|
||||
resource control.
|
||||
|
||||
### Automatic dimensional stability with a 60-second default
|
||||
|
||||
The dimensional stability rule used a 0.00005 mm target, scaled downward for
|
||||
very small objects, and required two stable doubling comparisons.
|
||||
|
||||
| Outcome | Result |
|
||||
|---|---:|
|
||||
| Stopped stable before deadline | 23 / 138 |
|
||||
| Reached 60-second limit | 115 / 138 |
|
||||
| Mean rays | 104.2 million |
|
||||
| Within the independent adaptive result's 95% interval | 129 / 138 |
|
||||
| Median difference from adaptive point estimate | 0.100% |
|
||||
| 90th-percentile difference | 0.334% |
|
||||
| 95th-percentile difference | 0.454% |
|
||||
| Maximum difference | 1.204% |
|
||||
|
||||
The independent adaptive interval is not analytic truth, so the interval count
|
||||
is an agreement check rather than a coverage claim. The practical conclusion
|
||||
is still clear: the modeling-distance stability floor is much stricter than a
|
||||
1% answer requirement for most corpus objects. It is valuable as an absolute
|
||||
terminal criterion, but using it alone makes the default consume the full
|
||||
minute in 83.3% of cases.
|
||||
|
||||
### Legacy comparison over the corpus
|
||||
|
||||
At the earlier 100,000-ray cap, 123 roots produced paired legacy and Crofton
|
||||
volumes. Median relative difference was 0.544%, the 90th percentile was
|
||||
3.573%, and 113/123 were within 5%. Fifteen legacy runs exceeded 120 seconds;
|
||||
all had fast Crofton answers. Analytic checks on notable disagreements favor
|
||||
the new estimator:
|
||||
|
||||
| Model | Analytic volume (mm^3) | Legacy error | Crofton error |
|
||||
|---|---:|---:|---:|
|
||||
| `pic.g/box.r` | 1.17368243 | -10.126% | -0.175% |
|
||||
| `cube.g/light.r` | 6.08899047 | +1.940% | -0.0146% |
|
||||
| `lgt-test.g/box.r` | 19,233.2255 | +2.496% | -0.175% |
|
||||
|
||||
## Large complex models
|
||||
|
||||
Volumes are in billions of mm^3. Legacy uses its finest practical tested grid;
|
||||
Havoc, M35, and Goliath use rotated level 3 and 3.2 million Crofton rays. The
|
||||
full Generic Twin uses rotated level 5 and 12.8 million Crofton rays.
|
||||
|
||||
| Model | Legacy | Rotated | Random | QMC | Legacy vs new-method consensus |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| Havoc | 2.864720 | 2.420378 | 2.422244 | 2.426672 | **+18.226%** |
|
||||
| M35 cab | 0.547483 | 0.537888 | 0.538267 | 0.537940 | +1.757% |
|
||||
| Goliath | 0.708337 | 0.709737 | 0.709080 | 0.709610 | -0.161% |
|
||||
| Generic Twin, full | 29.415300 | 29.725600 | 29.830135 | 29.801455 | -1.244% |
|
||||
|
||||
The legacy finest-grid centroids differ from the new-method centroid mean by
|
||||
about 547 mm on Havoc, 11.1 mm on M35, 2.1 mm on Goliath, and 8.1 mm on the full
|
||||
Generic Twin. Havoc is the clearest evidence of axis-aligned aliasing in a
|
||||
real model.
|
||||
|
||||
Cross-method spread among the three new methods summarizes consistency for all
|
||||
numeric outputs. Volume and mass have the same relative spread because of the
|
||||
uniform synthetic density. Inertia spread uses tensor trace about the model
|
||||
origin; centroid reports the maximum pairwise distance.
|
||||
|
||||
| Model | Volume/mass spread | Area spread | Centroid separation | Inertia-trace spread |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Havoc | 0.260% | 0.336% | 12.31 mm | 0.203% |
|
||||
| M35 cab | 0.070% | 0.113% | 1.73 mm | 0.107% |
|
||||
| Goliath | 0.093% | 0.0097% | 0.68 mm | 0.204% |
|
||||
| Generic Twin, full | 0.351% | **6.445%** | 23.75 mm | 0.344% |
|
||||
|
||||
The Generic Twin area disagreement is the main unresolved answer regression.
|
||||
Random and QMC agree with each other to 0.108%, while rotated grid is 6.5%
|
||||
higher. This points to orientation/boundary-count behavior in the rotated
|
||||
area estimator rather than Crofton noise.
|
||||
|
||||
The declared 10-second, 1%-interval mode also succeeded on all four complex
|
||||
models after the half-space correction:
|
||||
|
||||
| Model | Volume (10^9 mm^3) | Relative 95% half-width | Rays | Internal time | Difference from deep new-method consensus |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| Havoc | 2.430949 | 0.600% | 3,119,616 | 1.691 s | +0.324% |
|
||||
| M35 cab | 0.536708 | 0.352% | 1,360,416 | 0.407 s | -0.246% |
|
||||
| Goliath | 0.710126 | 0.221% | 300,000 | 1.221 s | +0.092% |
|
||||
| Generic Twin, full | 29.871938 | 0.463% | 3,746,112 | 1.526 s | +0.289% |
|
||||
|
||||
Goliath illustrates why time and rays are separate concerns: only 300,000 rays
|
||||
were needed statistically, but its geometry makes each ray much more expensive
|
||||
than in the other models.
|
||||
|
||||
## Modeled-air consistency
|
||||
|
||||
Expected volume is 2.0 billion mm^3 with air included and 1.0 billion mm^3
|
||||
without it. Expected included mass is 1,001,000 g and excluded mass is
|
||||
1,000,000 g.
|
||||
|
||||
| Output | Legacy | Rotated | Random | QMC |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Included volume error | **0%** | +0.00339% | +0.39436% | +0.00286% |
|
||||
| Excluded volume error | **0%** | -0.00289% | +0.02053% | +0.00081% |
|
||||
| Included mass error | **0%** | -0.00288% | +0.02127% | +0.00081% |
|
||||
| Included centroid error | 14.43 mm | **0.052 mm** | 1.01 mm | 0.184 mm |
|
||||
| Excluded centroid error | 14.43 mm | **0.052 mm** | 1.01 mm | 0.184 mm |
|
||||
|
||||
All methods apply air inclusion consistently. The legacy exact totals again
|
||||
coexist with a cell-center centroid bias.
|
||||
|
||||
## Diagnostic outputs
|
||||
|
||||
Diagnostics are sampled observations, not proofs that unobserved problems are
|
||||
absent. “Largest” values are therefore directional extreme samples and
|
||||
generally increase as more favorable rays are found.
|
||||
|
||||
| Check | Legacy | Rotated | Random | QMC | Assessment |
|
||||
|---|---|---|---|---|---|
|
||||
| Overlap | Found; max 8,000 mm | Found; 2,888.6 mm | Found; 9,714.2 mm | Found; 10,185.5 mm | Analytic directional maximum is 11,357.8 mm; QMC is closest |
|
||||
| Gaps/internal voids | Found | Found | Found | Found | New output distinguishes `internal-void` and both region orders |
|
||||
| Adjacent air | Legacy stand-alone run crashed without a plot prefix | Found at 0 mm | Found at 0 mm | Found at 0 mm | Existing legacy defect; experimental modes are clean |
|
||||
| Exposed air | Found; max 9,000 mm | Found; 12,122.8 mm | Found; 12,765.4 mm | Found; 12,406.1 mm | Analytic box diagonal is 14,456 mm; random is closest at this budget |
|
||||
|
||||
Crofton has much better angular coverage than three fixed views for overlap and
|
||||
exposed-air extremes. Rotating only one triad does not guarantee that it will
|
||||
sample the direction of a narrow or maximal defect.
|
||||
|
||||
## Output and interface coverage
|
||||
|
||||
| Capability | Legacy | Rotated grid | Crofton random/QMC | Status |
|
||||
|---|---|---|---|---|
|
||||
| Volume | yes | yes | yes | Comparable |
|
||||
| Mass/weight | yes | yes | yes | Comparable with density data |
|
||||
| Centroid | yes | yes | yes | Comparable |
|
||||
| Moments/products of inertia | yes, about centroid | yes, about model origin | yes, about model origin | Convention mismatch must remain explicit |
|
||||
| Surface area | no | yes | yes | Experimental improvement |
|
||||
| Overlaps | yes | yes | yes | Crofton negative result remains probabilistic |
|
||||
| Gaps/internal voids | yes | yes | yes | Experimental labeling is clearer |
|
||||
| Adjacent air | yes, but tested crash | yes | yes | Legacy defect exposed |
|
||||
| Exposed air | yes | yes | yes | Better angular coverage experimentally |
|
||||
| Modeled-air include/exclude | yes | yes | yes | Consistent |
|
||||
| Per-object/per-region results | text | JSON/text | JSON/text | Experimental output is easier to consume |
|
||||
| Bounding-box report | yes | yes | yes | Experimental bounds need no sampling rays and reject unbounded results |
|
||||
| Plot3 diagnostic output | yes | all or representative | all or representative | Experimental modes also support incremental MGED display |
|
||||
| Progress, replay, uncertainty | no | progress | progress, invalid-ray replay, intervals | Experimental improvement |
|
||||
|
||||
## Half-space behavior
|
||||
|
||||
Half-spaces must remain in ray tracing when they clip finite geometry; simply
|
||||
removing them changes Boolean meaning. The corrected implementation derives
|
||||
conservative sampling bounds from prepared region trees and ignores infinite
|
||||
solids only when constructing the Crofton sampling sphere.
|
||||
|
||||
- A cube intersected by a half-space and the complementary subtraction each
|
||||
report the expected 500,000,000 mm^3.
|
||||
- The combined split reports the original 1,000,000,000 mm^3 in both grid and
|
||||
Crofton tests.
|
||||
- The complete Generic Twin, whose fuel regions use half-space clipping, now
|
||||
runs successfully and meets the adaptive 1% target in 1.526 seconds.
|
||||
- `cube.g/cloud.r.bak` is another bounded half-containing root and now runs.
|
||||
- A standalone half-space, or a selected union containing an independently
|
||||
unbounded region, is rejected with an explicit unbounded-region diagnostic.
|
||||
- Unboundedness is now propagated through `SUBMODEL`. This prevents the prior
|
||||
`primitives.g/other` infinite-hit crash while preserving bounded submodels
|
||||
whose internal half-space is clipped by finite geometry.
|
||||
|
||||
Of the twelve original direct or indirect half-containing corpus roots, two
|
||||
are bounded and now analyzable; ten are genuinely unbounded selections and are
|
||||
rejected before sampling.
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. **Use QMC for the experimental accuracy-oriented default.** Keep a time
|
||||
ceiling as the primary resource control, report progress, and always expose
|
||||
`target_met`, `target_not_met`, or `time_limit` with the achieved interval.
|
||||
2. **Retain the geometry-scale stability rule as an absolute floor, not the
|
||||
sole ordinary stopping target.** It consumed the full minute on 115/138
|
||||
cases even though the independent comparison was already subpercent for
|
||||
almost all of them.
|
||||
3. **Keep rotated grid as a deterministic control.** It is excellent on the
|
||||
analytic fixtures and gives users a non-statistical alternative, but it
|
||||
should not replace QMC until the Generic Twin area discrepancy is resolved.
|
||||
4. **Investigate rotated-grid surface-area accumulation next.** Test multiple
|
||||
independent triads, internal-interface counting, clipped boundary-cell
|
||||
weights, and convergence under orientation changes. The Crofton pair's
|
||||
0.108% agreement gives a strong reference, though not analytic truth.
|
||||
5. **Keep ordinary random Crofton for validation and debugging.** It is an
|
||||
important unbiased control for QMC, but should not be the performance
|
||||
default at the tested precision levels.
|
||||
6. **Keep representative plotting as the interactive default.** The `all`
|
||||
policy is valuable for exact legacy-style debugging but grows directly with
|
||||
ray count. Continue using bounded spatial representatives for ordinary
|
||||
live viewing, and clearly document inertia reference conversion when
|
||||
presenting legacy and experimental tensors side by side.
|
||||
7. **Expand interval calibration.** Repeat the 10-second corpus with multiple
|
||||
independent outer seeds and analytic fixtures to measure empirical coverage,
|
||||
especially for area, centroid, inertia, near-zero results, and
|
||||
`--accuracy-scope all`.
|
||||
8. **Retain half-space/submodel regressions.** Test finite intersection,
|
||||
subtraction, bounded submodels, standalone unbounded regions, and nested
|
||||
unbounded submodels. Do not filter half-spaces out of Boolean evaluation.
|
||||
9. **Treat diagnostic negatives conservatively.** Report sampling evidence
|
||||
and event-probability bounds, never “no overlap exists,” unless a separate
|
||||
exhaustive geometric proof is implemented.
|
||||
@@ -4,7 +4,6 @@ set(
|
||||
api_reference.adoc
|
||||
bool_eval_development.adoc
|
||||
getting_started_as_contributor.adoc
|
||||
gqa_analysis.adoc
|
||||
iterate_geometry.adoc
|
||||
libged_command_authoring.adoc
|
||||
raytracing_pipeline.adoc
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
= GQA Analysis Design
|
||||
|
||||
The experimental `gqa --analyze` path separates geometric observations from
|
||||
reporting. Ray callbacks accumulate region-attributed raw moments, boundary
|
||||
crossings, and diagnostic events; the command layer applies units, object
|
||||
rollups, and schema-versioned output. The engine is exposed through the
|
||||
C-compatible `analyze_gqa` libanalyze API; libged supplies only its open
|
||||
database, result string, and density-loader adapter. The reusable sampling
|
||||
interval calculation is separately exposed as
|
||||
`analyze_sampling_interval95`; paired ratio estimates use
|
||||
`analyze_sampling_ratio_interval95`.
|
||||
|
||||
== Measurement conventions
|
||||
|
||||
Lengths use millimetres internally, mass uses grams, surface area counts region
|
||||
partition boundaries, and inertia is about the model origin. Modeled air is
|
||||
included by default and can be excluded explicitly. Grid boundary cells are
|
||||
clipped to the model bounds. These choices are written to JSON so a consumer
|
||||
does not have to infer them from command options.
|
||||
|
||||
The optional `bounds` measurement is not sampled. Conservative bounds are
|
||||
combined from the prepared region Boolean trees rather than directly unioning
|
||||
raw primitive boxes. This keeps finite intersections and subtractions
|
||||
involving half-spaces usable while rejecting a result whose region-tree bound
|
||||
is unbounded. As elsewhere in librt, subtraction does not shrink this
|
||||
conservative box. The same bounds define the sampling domain and the
|
||||
automatic plot scale.
|
||||
|
||||
== Grid sampling
|
||||
|
||||
The experimental grid implementation has two modes. The `grid` mode is an
|
||||
axis-aligned three-view control. The `grid-rotated` mode constructs an
|
||||
orthonormal direction triad from an azimuth and elevation, projects all eight
|
||||
corners of the tight model bounding box into each perpendicular view plane,
|
||||
and traces the resulting rectangular grids. Both modes clip boundary cells
|
||||
to their projection intervals and weight each chord by its cell's actual
|
||||
area. Rotated-cell covariance is included when accumulating mass moments;
|
||||
otherwise an oblique cell would incorrectly be treated as having only
|
||||
axis-aligned transverse extent.
|
||||
|
||||
The grid worker also records the clipped width and orientation of every
|
||||
sampled cell. These are visualization metadata only: volume and moment
|
||||
accumulation continues to use the same clipped area and covariance.
|
||||
|
||||
The rotated mode changes the two-dimensional grid phase between refinements.
|
||||
The general shifted-lattice technique is described by R. Cranley and T. N. L.
|
||||
Patterson, _Randomization of Number Theoretic Methods for Multiple
|
||||
Integration_ (1976), https://doi.org/10.1137/0713071. GQA uses deterministic
|
||||
irrational shifts for reproducibility, so they are an anti-aliasing measure,
|
||||
not the randomized construction or an uncertainty estimate from that paper.
|
||||
|
||||
Grid rays execute in parallel using one librt resource and one GQA accumulator
|
||||
per worker. Region metrics, problem observations, and material errors are
|
||||
merged only after a batch completes. This preserves the existing serial
|
||||
bookkeeping semantics without locking every partition callback. A refinement
|
||||
is publishable only after all three views finish; rays from an incomplete pass
|
||||
at the deadline are counted and reported as discarded rather than mixed into
|
||||
the preceding complete estimate.
|
||||
|
||||
Grid convergence uses dimensional quantities. The change between successive
|
||||
area and volume estimates is expressed as the corresponding sphere-radius
|
||||
change, and the maximum-minus-minimum directional volume radius measures
|
||||
view-dependent aliasing. Both must be below the requested distance for two
|
||||
successive refinements. Stability across levels alone is insufficient:
|
||||
axis-aligned geometry can produce a nearly unchanged average while one view
|
||||
remains anomalous. When a half-spacing pass will not fit the remaining time,
|
||||
the planner may choose an intermediate spacing that is still at least 1.05
|
||||
times finer; it stops instead of starting a refinement too small to add useful
|
||||
resolution.
|
||||
|
||||
== Crofton sampling
|
||||
|
||||
An oriented line needs four uniform coordinates: two for direction and two for
|
||||
the perpendicular offset. The default QMC implementation uses a randomized
|
||||
rank-one lattice when OpenQMC is available; ordinary pseudorandom sampling is
|
||||
also available as a diagnostic cross-check. A ray offset continues one point
|
||||
set. A seed and stream identifier select a separately randomized point set.
|
||||
The distinction matters because consecutive points from one QMC sequence are
|
||||
dependent and cannot be treated as independent error replicates.
|
||||
|
||||
The implementation follows Art Owen's _Error estimation for quasi-Monte Carlo_
|
||||
(2025), especially section 5:
|
||||
https://arxiv.org/abs/2501.00150v3. Owen recommends independent randomizations,
|
||||
at least ten replications, and a Student-t interval across replicate estimates.
|
||||
GQA uses 16 replications. Libanalyze uses the conservative 95 percent
|
||||
Student-t critical value for nine degrees of freedom for any accepted replicate
|
||||
count, avoiding intervals that are too narrow because a normal critical value
|
||||
was used for a small replication count.
|
||||
|
||||
The reported intervals are approximate and pointwise. Selecting
|
||||
`--accuracy-scope all` requires each selected model, unambiguous object, and
|
||||
region metric to pass, but does not claim simultaneous 95 percent family-wise
|
||||
coverage.
|
||||
|
||||
== Adaptive accuracy
|
||||
|
||||
Adaptive accuracy has two statistically separate stages:
|
||||
|
||||
. A pilot set of 16 randomized replications estimates the production budget.
|
||||
The normal deadline-driven pilot uses 100000 rays. A root-n squared-error
|
||||
scaling and an empirically calibrated safety factor are used only as planning
|
||||
heuristics.
|
||||
. The pilot estimates are discarded. Sixteen fresh streams produce the
|
||||
published estimates, intervals, and pass/fail result.
|
||||
|
||||
Fresh validation prevents the stopping decision and the reported interval from
|
||||
using the same favorable random fluctuation. QMC convergence need not follow a
|
||||
stable power law, so the production interval is authoritative: an optimistic
|
||||
pilot can reduce work, but it cannot assert success. This separation, the use
|
||||
of independently randomized replications, and the Student-t interval follow
|
||||
A. B. Owen, _Error estimation for quasi-Monte Carlo_ (2025), section 5:
|
||||
https://arxiv.org/abs/2501.00150v3. The root-n forecast and safety factor are
|
||||
engineering policy, not claims attributed to Owen.
|
||||
|
||||
When an adaptive target is combined with `--time` and no explicit `--rays`,
|
||||
the pilot also measures throughput. The forecast is clamped to 80 percent of
|
||||
the ray count expected to fit in the remaining wall time, reserving headroom
|
||||
for variation and reporting. An explicit ray count remains an additional hard
|
||||
cap. A deadline-constrained production estimate that misses its target is
|
||||
reported with `time_limit`; it is not promoted to success.
|
||||
|
||||
The 16 streams execute serially through one reusable Crofton worker session.
|
||||
Each stream's rays use the session's all-CPU worker pool. Worker-local events
|
||||
are merged and delivered to libanalyze callbacks serially, preserving GQA's
|
||||
per-region bookkeeping without launching competing all-CPU replications.
|
||||
|
||||
== Default resource and stability policy
|
||||
|
||||
Grid and Crofton measurement-only runs use elapsed time as their default
|
||||
resource limit. Unless the caller supplies the sampler's explicit work limit
|
||||
(`--refine` or `--rays`) or `--time`, the analysis deadline is 60000 ms. An
|
||||
explicit ray count remains available as a Crofton hard cap for reproducibility
|
||||
and specialized testing, but ray count is not the ordinary user-facing
|
||||
resource control.
|
||||
|
||||
For non-uncertainty measurement runs without an explicit stability target,
|
||||
the automatic equivalent-radius target is
|
||||
|
||||
`min(0.1 * BN_TOL_DIST, 0.001 * smallest_positive_tight_model_span)`.
|
||||
|
||||
The first term asks for stability an order of magnitude below librt's normal
|
||||
0.0005 mm modeling tolerance. The scale-relative term begins tightening the
|
||||
target for models below 0.05 mm, before the fixed tolerance becomes large
|
||||
relative to the selected geometry. Finite region-tree bounds are used because
|
||||
the prepared model RPP is deliberately rounded outward and raw half-space
|
||||
bounds are infinite. This stopping rule is an
|
||||
engineering convergence heuristic and is reported as `stable`; it does not
|
||||
claim the pointwise coverage of the independent-replication intervals.
|
||||
|
||||
For Crofton sampling, only estimates needed by the selected measurements
|
||||
participate in the test: area requests stabilize the surface-area radius,
|
||||
while volume, mass, centroid, and moment requests stabilize the volume radius.
|
||||
Stability is evaluated at 20,000 rays and then whenever the cumulative ray
|
||||
count doubles. Two successive doubling intervals must meet the target.
|
||||
Comparing fixed 2,000-ray increments would be misleading because each
|
||||
increment becomes a vanishing fraction of the cumulative estimate and its
|
||||
apparent change eventually tends to zero even when sampling error remains
|
||||
material.
|
||||
|
||||
Grid sampling applies the selected area or volume-radius refinement change and
|
||||
always checks directional volume-radius spread as an aliasing diagnostic.
|
||||
|
||||
The Crofton progress callback runs serially after completed parallel ray
|
||||
batches. GQA throttles those callbacks to approximately one report per second
|
||||
and includes elapsed time, total rays, throughput, the current volume estimate,
|
||||
and observed versus requested stability. A callback never reads worker-local
|
||||
state while rays are in flight.
|
||||
|
||||
== Plot publication and live views
|
||||
|
||||
Plotting is downstream of sampling. Grid workers append immutable sample
|
||||
records to worker-local accumulators, and Crofton visitors append records
|
||||
while librt is in its serialized delivery phase. Libanalyze publishes only
|
||||
after a parallel batch completes. Consequently Plot3 output and live-view
|
||||
output consume the same ordered line batches and cannot alter ray selection,
|
||||
partition bookkeeping, or numerical estimates.
|
||||
|
||||
The `all` policy preserves every chord and diagnostic line, including rays
|
||||
from successive complete grid refinements. It is useful for exact debugging
|
||||
but its storage and display cost grows with sample count. The default
|
||||
`representative` policy quantizes sample centers in model space and retains a
|
||||
deterministic one-element min-hash reservoir for each category, direction
|
||||
class, region pair, and occupied cell. The reservoir construction follows
|
||||
the mergeable fixed-size sampling idea in J. S. Vitter, _Random Sampling with
|
||||
a Reservoir_ (1985), https://doi.org/10.1145/3147.3165. Severity wins first
|
||||
for diagnostic observations so the most significant issue in a cell remains
|
||||
visible; deterministic priority resolves equal-severity samples.
|
||||
|
||||
Representative samples are rendered as finite-support glyphs. Grid glyphs
|
||||
use the exact clipped pixel footprint. Crofton glyphs use the representative
|
||||
side length corresponding to their average `pi R^2/N` transverse sampling
|
||||
area, where `R` is the bounding-sphere radius and `N` is the ray count. The
|
||||
spatial-cell scale independently limits how many glyphs are retained. This
|
||||
follows the visual principle of surfels, which draw
|
||||
sample support instead of infinitesimal points: H. Pfister et al., _Surfels:
|
||||
Surface Elements as Rendering Primitives_ (2000),
|
||||
https://doi.org/10.1145/344779.344936. Bounding the number of displayed
|
||||
samples by occupied spatial cells applies the fixed-detail motivation of S.
|
||||
Rusinkiewicz and M. Levoy, _QSplat: A Multiresolution Point Rendering System
|
||||
for Large Meshes_ (2000), https://doi.org/10.1145/344779.344940, without
|
||||
claiming to implement its hierarchy.
|
||||
|
||||
An incomplete grid refinement never replaces the last complete
|
||||
representative snapshot. Adjacent-air samples use an interface-centered
|
||||
footprint and a bounded directional tick; the full following air chord would
|
||||
overstate the location of the transition.
|
||||
|
||||
The libanalyze callback contains plain immutable model-coordinate lines and
|
||||
has no display-system dependency. MGED copies each batch, synchronously
|
||||
marshals the update through its existing GUI-event bridge, rebuilds the named
|
||||
`gqa::analysis` view object on the display-owning thread, and requests a
|
||||
refresh. This is the same ownership rule used for other asynchronous GED
|
||||
work: worker threads calculate data, while all view-list mutation remains on
|
||||
the GUI thread.
|
||||
|
||||
Relative and absolute tolerances may be combined. For a scalar estimate
|
||||
`theta`, the accepted half-width is the larger of
|
||||
`relative_error * abs(theta)` and the applicable absolute tolerance. This
|
||||
lets absolute tolerances govern quantities near zero. A centroid point
|
||||
estimate is the ratio of the replicate-mean first moment to replicate-mean
|
||||
mass. Its interval uses paired first-moment and mass residuals in a
|
||||
first-order delta calculation, retaining their covariance; zero-mass
|
||||
centroids remain unavailable.
|
||||
|
||||
== Diagnostic evidence
|
||||
|
||||
Overlap, gap, and modeled-air results are sampled observations, not proofs of
|
||||
absence. Crofton overlap-only runs first find positive-volume intersections of
|
||||
region-instance bounding boxes and divide rays evenly among those candidates.
|
||||
Raw overlap events are clipped to their candidate box and filtered by the
|
||||
requested depth tolerance. For an ordinary randomized sequence with zero
|
||||
events, the report includes the exact one-sided 95 percent binomial upper bound
|
||||
`1 - 0.05^(1/N)` on the per-ray event probability.
|
||||
|
||||
Invalid partitions are rejected deliberately and may be written to a separate
|
||||
replay file. Publication uses temporary sibling files followed by rename so a
|
||||
failed write does not leave a new partial report at the requested path.
|
||||
|
||||
== Reproducibility and schema
|
||||
|
||||
The seed, resolved sequence, total pilot and production ray counts, unused
|
||||
budget, measurement conventions, and every interval are recorded in JSON
|
||||
schema version 2. Each diagnostic observation retains its actual ray ID,
|
||||
origin, and direction. Invalid-ray files can be replayed by `nirt --replay`.
|
||||
@@ -37,179 +37,6 @@ For error and interface calculations, the grid is refined until an error is foun
|
||||
When multiple calculations are performed, the most extensive path is taken. For example, if weight and overlap calculations are performed, then the grid will be refined until the refinement limit is reached, or an overlap is detected.
|
||||
|
||||
|
||||
[[experimental_analysis]]
|
||||
== EXPERIMENTAL ANALYSIS
|
||||
|
||||
The separate `--analyze` interface preserves the behavior of existing *gqa*
|
||||
invocations. It reports a concise summary and can write all region results and
|
||||
individual problem observations as structured JSON. The JSON includes
|
||||
per-object rollups when every region belongs unambiguously to one requested
|
||||
object. This interface requires an explicit object list, including when used
|
||||
inside MGED.
|
||||
|
||||
[source]
|
||||
----
|
||||
gqa --analyze [analysis options] model.g object [object ...]
|
||||
----
|
||||
|
||||
`--measure` accepts a comma-separated selection from
|
||||
`volume,mass,area,centroid,moments,bounds`. With no measurement or check selection,
|
||||
the default is `volume,area`. A check-only selection does not require density
|
||||
data. Centroid and moments require mass. `--check` accepts
|
||||
`overlaps,gaps,adjacent-air,exposed-air` and defaults to no checks.
|
||||
The model totals and region measurements use mm, mm^3^, mm^2^, and grams.
|
||||
Mass needs a density table, selected with `--density file` or found using the
|
||||
normal gqa density search.
|
||||
The `bounds` measure reports the finite, conservative region-tree bounding box
|
||||
and its three face areas without firing sampling rays. It can be combined
|
||||
with the sampled measures. Subtraction does not shrink the reported box. A
|
||||
bounded intersection or subtraction involving a half-space is supported; a
|
||||
selected region whose region-tree bound is unbounded is rejected.
|
||||
|
||||
`--sampler grid`, the experimental interface's default, is the axis-aligned
|
||||
control corresponding to the traditional three-view method. `--sampler
|
||||
grid-rotated` uses a non-axis-aligned orthonormal view triad. Its first
|
||||
direction defaults to 35 degrees azimuth and 25 degrees elevation and may be
|
||||
set with `--azimuth degrees` and
|
||||
`--elevation degrees`; the other two directions are derived orthogonally.
|
||||
Rotated refinements use deterministic plane-grid shifts so successive passes
|
||||
do not repeatedly sample the same alignment. These shifts reduce aliasing but
|
||||
do not make grid results statistical confidence estimates.
|
||||
|
||||
Both grid modes use `--spacing mm`. When it is omitted, the initial spacing
|
||||
is the larger of 50 mm and one 128th of the tight model bounding-box diagonal.
|
||||
This keeps the first pass tractable for exceptionally large models while
|
||||
preserving the familiar 50 mm start for ordinary models. `--refine levels`
|
||||
sets the maximum number of additional grid passes and disables the default
|
||||
time control when used alone. Each normal refinement halves the spacing;
|
||||
under a time limit, a final intermediate spacing may be selected so a complete
|
||||
three-view pass fits the remaining budget. Boundary cells are clipped and
|
||||
weighted by their actual projected area rather than counted as full cells.
|
||||
|
||||
`--sampler crofton` uses isotropic rays. For a measurement-only run with
|
||||
either grid or Crofton sampling and no explicit resource control, sampling has
|
||||
a 60000 ms budget and stops sooner when its automatic equivalent-radius
|
||||
stability target is reached. The planner may stop before the exact deadline
|
||||
when there is not enough time for another complete sampling checkpoint.
|
||||
For Crofton sampling, `--rays count` replaces that time-first default with an
|
||||
explicit hard ray cap; `--sequence random|qmc` chooses an explicit sequence.
|
||||
If omitted, the standard librt sequence selection is used. A QMC request
|
||||
requires a build with OpenQMC.
|
||||
`--seed value` makes the selected randomized sequence reproducible.
|
||||
`--stability mm` requests equivalent-radius convergence. For a grid, both the
|
||||
change from the preceding refinement and the spread among its three
|
||||
directional volume estimates must meet the target for two successive passes.
|
||||
For Crofton sampling, two successive doubling checkpoints must meet the
|
||||
target. `--time ms` sets an internal wall-clock budget. A time-controlled run
|
||||
reports progress at approximately one-second intervals. Grid progress
|
||||
includes the level, spacing, completed rays, throughput, and directional
|
||||
volumes; Crofton progress includes the current volume and stability when
|
||||
available.
|
||||
|
||||
The automatic stability target for either measurement sampler is the smaller
|
||||
of `BN_TOL_DIST / 10` (0.00005 mm) and 0.1 percent of the smallest positive
|
||||
tight model-bounding-box span. The scale-dependent term prevents the fixed
|
||||
modeling tolerance from becoming coarse relative to a small valid object.
|
||||
This is a convergence heuristic, not a confidence interval. It applies only
|
||||
to estimates needed by the requested measurements and requires two stable
|
||||
comparisons at doubling ray-count checkpoints for Crofton sampling. Grid
|
||||
refinement applies the same two-interval policy to complete three-view passes
|
||||
and also requires its directional spread to meet the target. The result
|
||||
distinguishes `stable`, `time_limit`, and an explicitly requested ray or
|
||||
refinement limit stopping condition.
|
||||
`--tolerance mm` ignores overlap and gap depths at or below the threshold
|
||||
(default 0). Crofton diagnostic checks use the observed ray partition paths
|
||||
and therefore remain sampling evidence rather than exhaustive proofs. Gaps
|
||||
between two partitions of the same region are labeled `internal-void` in the
|
||||
summary.
|
||||
When Crofton is used for an overlap-only request (that is, `--check overlaps`
|
||||
with no `--measure`), the experimental path first indexes finite region-instance
|
||||
bounding boxes and samples only their intersecting candidate boxes. The summary
|
||||
and JSON record report the number of candidate boxes considered. This is a
|
||||
broad-phase acceleration and does not establish that two solids actually
|
||||
intersect; the targeted rays still provide the overlap evidence.
|
||||
|
||||
For targeted overlap checks, `--stability mm` compares successive equivalent-radius
|
||||
estimates of each candidate box, using the same rays that test the geometry. The
|
||||
known box volume and surface area provide calibration checks, and the sampled rays
|
||||
must cover all three dominant direction classes. A candidate stops only when the
|
||||
radius change and these calibration checks meet the requested target, subject to
|
||||
the ray budget. JSON records the calibration errors and whether the target was
|
||||
reached in `box_stability_reached` and `box_calibration_reached`.
|
||||
|
||||
For randomized sequences, a candidate with no observed overlap event also reports
|
||||
a 95 percent upper bound on the per-ray event probability. This quantifies the
|
||||
strength of a sampling-negative result; it is not an exhaustive proof of absence.
|
||||
|
||||
The measurements are
|
||||
sampled estimates, and diagnostic checks can miss problems that no ray hits.
|
||||
|
||||
`--plot-prefix prefix` writes the selected observations as binary Plot3 files
|
||||
named `prefixvolume.plot3`, `prefixgaps.plot3`, `prefixoverlaps.plot3`,
|
||||
`prefixadj_air.plot3`, and `prefixexp_air.plot3`. Only files corresponding to
|
||||
requested measurements or checks are created. `--plot-mode all` writes every
|
||||
sampled chord, matching the legacy plotting intent and retaining all grid
|
||||
refinement passes. The default, `--plot-mode representative`, keeps one
|
||||
deterministic sample per occupied spatial cell, direction class, category, and
|
||||
region pair. It draws the actual projected grid-cell footprint; a Crofton
|
||||
glyph represents its average `pi R^2/N` transverse sampling area. This bounded-detail
|
||||
mode preserves visible coverage as refinement proceeds instead of making the
|
||||
plot grow in direct proportion to the ray count. `--plot-resolution mm`
|
||||
overrides the automatic representative cell size of one 128th of the model
|
||||
diagonal. Plot selection never changes the rays or numerical estimates.
|
||||
|
||||
Inside MGED, `--plot-view` displays the same output in the active view as
|
||||
`gqa::analysis` and refreshes it incrementally at approximately one-second
|
||||
intervals. Representative mode replaces the overlay with its latest bounded
|
||||
snapshot; all mode appends new rays and can become expensive. Adjacent-air
|
||||
snapshots mark the sampled interface with a footprint and a short directional
|
||||
tick, avoiding an invisible zero-length line without implying that the whole
|
||||
air chord is defective. `--plot-view` is unavailable to standalone *gqa*.
|
||||
|
||||
`--air include|exclude` controls whether modeled air contributes to
|
||||
measurement totals; the default is `include`. Diagnostic air checks still
|
||||
trace modeled air when it is excluded from measurements. Surface area counts
|
||||
region partition boundaries, including internal interfaces, and moments of
|
||||
inertia are reported about the model origin.
|
||||
|
||||
`--uncertainty` divides the Crofton ray budget among 16 independently
|
||||
randomized replications and reports approximate pointwise 95 percent
|
||||
Student-t intervals. It applies only to measurement-only Crofton runs.
|
||||
`--relative-error fraction` requests adaptive accuracy and
|
||||
`--absolute-error measure=value[,measure=value...]` sets absolute targets for
|
||||
`volume` (mm^3^), `mass` (g), `area` (mm^2^), `centroid` (mm), or
|
||||
`moments` (g mm^2^). When both forms apply, the larger tolerance is used.
|
||||
`--accuracy-scope model|all` selects model totals only or model, object, and
|
||||
region results. The `all` intervals remain pointwise rather than a
|
||||
simultaneous family-wise confidence statement.
|
||||
|
||||
Adaptive accuracy uses a pilot stage to forecast a production ray count, then
|
||||
discards the pilot estimates and evaluates the target with fresh independent
|
||||
production streams. With `--time ms` and no explicit `--rays`, the deadline
|
||||
is the resource limit: the pilot measures ray throughput and the production
|
||||
forecast is restricted to fit the remaining time with scheduling headroom.
|
||||
When both are specified, the ray count is also a hard cap. When neither is
|
||||
specified, adaptive accuracy uses the default 60000 ms deadline. A bare
|
||||
`--uncertainty` request without an accuracy target retains the 100000-ray
|
||||
budget because it has no accuracy condition with which to plan a deadline.
|
||||
|
||||
The result explicitly reports `target_met`, `target_not_met`, `time_limit`, or
|
||||
`insufficient_evidence`. `time_limit` means the deadline constrained a
|
||||
production result that did not meet the requested interval width; the estimate
|
||||
and its achieved interval are still reported. The pilot, forecast, production
|
||||
count and timing, planning safety factor, deadline state, and actual interval
|
||||
are recorded in JSON. The planning heuristic and safety factor do not alter
|
||||
the published interval: only the fresh production replications determine it.
|
||||
|
||||
`--json file` writes a schema-version 2 JSON record with model and region
|
||||
measurements and every observed problem, including full region paths and ray
|
||||
information.
|
||||
`--invalid-rays file` writes a separate replay-oriented JSON file for rejected
|
||||
Crofton partitions. Each record includes the ray ID, origin, direction, hit
|
||||
distances, thickness, region path, and rejection reason.
|
||||
Without `--json`, the command prints only the summary. Reported surface area
|
||||
counts region partition boundaries, including internal interfaces.
|
||||
|
||||
[[volume_and_weight_calculation]]
|
||||
== VOLUME AND WEIGHT CALCULATION
|
||||
|
||||
|
||||
@@ -19,20 +19,6 @@ nirt [options] model.g [objects]...
|
||||
|
||||
*nirt* (Natalie's Interactive Ray-Tracer) traces rays through the specified _objects_ in the database _model.g_, using *librt*(3), and produces an ASCII report of what each ray encounters. Commands are read from standard input. By default the user interacts with *nirt*, repeatedly specifying ray origination points and directions and controlling the format and destination of the reports. Locations may be input and output in either model coordinates (_x_, _y_, _z_) or view (a.k.a. grid-plane) coordinates (_h_, _v_, _d_), and direction may be expressed either as a vector in model coordinates or as azimuth and elevation angles.
|
||||
|
||||
Replay invalid-ray records generated by analysis tools with `--replay` and
|
||||
`--ray-id`. The selected record's origin and direction are restored at JSON
|
||||
precision, and one shot is performed before *nirt* exits:
|
||||
|
||||
[source]
|
||||
----
|
||||
nirt --replay invalid_rays.json --ray-id 18432 model.g object
|
||||
----
|
||||
|
||||
Replay files use schema version 1 or 2 and contain a `rays` array. Each record
|
||||
has a unique nonnegative integer `ray_id`, a finite numeric `origin_mm`
|
||||
three-vector, and a finite nonzero `direction` three-vector. Both options are
|
||||
required. The `--replay=FILE` and `--ray-id=N` forms are also accepted.
|
||||
|
||||
When standard input is not a terminal (for example, when it is redirected from a file or a pipe), *nirt* defaults to silent mode, making it convenient for scripted ray queries.
|
||||
|
||||
[[options]]
|
||||
|
||||
@@ -4,38 +4,54 @@
|
||||
:manmanual: BRL-CAD
|
||||
|
||||
== NAME
|
||||
pixdiff - highlight samples which differ between two pix or bw files
|
||||
pixdiff - highlight differences between two pix or bw images
|
||||
|
||||
== SYNOPSIS
|
||||
|
||||
[source]
|
||||
----
|
||||
pixdiff _file1.pix_ _file2.pix_ > _out.pix_
|
||||
pixdiff _file1.bw_ _file2.bw_ > _out.bw_
|
||||
pixdiff -b _file1.bw_ _file2.bw_ > _out.bw_
|
||||
----
|
||||
|
||||
|
||||
[[description]]
|
||||
== DESCRIPTION
|
||||
|
||||
*pixdiff* compares two raw *pix*(5) or *bw*(5) images byte by byte and
|
||||
writes a difference image of the same size to standard output. Bytes that
|
||||
differ by +/-1 are written at intensity 192, while larger differences are
|
||||
written at full intensity 255. Matching bytes are written at half their
|
||||
original intensity, providing visual context for locating differences.
|
||||
By default, *pixdiff* compares raw *pix*(5) images as RGB pixels and writes
|
||||
a PIX difference image to standard output. When all three channels of a
|
||||
pixel match, it writes a dim grayscale version of that pixel to all three
|
||||
output channels. When any channel differs, each matching channel is written
|
||||
as zero, each channel differing by one is written as 192, and each channel
|
||||
differing by more than one is written as 255.
|
||||
|
||||
Because the comparison does not interpret pixel structure, no format option
|
||||
is needed and *pixdiff* is independent of image resolution. The two inputs
|
||||
must be the same size in bytes; otherwise the program reports the mismatch
|
||||
and exits. Either input file may be given as
|
||||
With *-b*, *pixdiff* compares raw *bw*(5) images one byte at a time and
|
||||
writes a BW difference image. Differing samples use the same 192 and 255
|
||||
intensities; matching samples are written at half their original intensity.
|
||||
|
||||
No image dimensions are needed. The two inputs must be the same size in
|
||||
bytes, and PIX input must contain complete three-byte pixels. Otherwise,
|
||||
*pixdiff* reports an error. Either input file may be given as
|
||||
*-* to read from standard input. Output must be redirected; *pixdiff*
|
||||
refuses to write a raw image stream to a terminal.
|
||||
|
||||
Upon completion, the count of bytes that matched, that were off by 1, and
|
||||
that were off by many is reported on standard error.
|
||||
Upon completion, *pixdiff* reports matching, off-by-one, and larger-difference
|
||||
channel or sample counts on standard error.
|
||||
|
||||
Also see *bwdiff*(1), which can also operate on *pix*(5) files and can
|
||||
produce other kinds of difference output.
|
||||
*bwdiff -n -b* produces a similar BW highlight image, but writes 255 rather
|
||||
than 192 for off-by-one differences. The default *bwdiff*(1) output instead
|
||||
shows signed differences around a neutral gray.
|
||||
|
||||
|
||||
[[options]]
|
||||
== OPTIONS
|
||||
|
||||
*-b*::
|
||||
Treat both inputs and the output as one-byte-per-pixel BW images. Without
|
||||
this option, use three-byte-per-pixel PIX images.
|
||||
|
||||
*-h*::
|
||||
Show usage information.
|
||||
|
||||
|
||||
[[examples]]
|
||||
@@ -59,10 +75,10 @@ on standard error give a quick quantitative sense of how much changed.
|
||||
====
|
||||
[source,bash]
|
||||
----
|
||||
pixdiff depth_a.bw depth_b.bw > depth_diff.bw
|
||||
pixdiff -b depth_a.bw depth_b.bw > depth_diff.bw
|
||||
----
|
||||
Writes a grayscale highlight image using the same byte comparison and
|
||||
statistics as the color example; no format option is needed.
|
||||
Writes a grayscale highlight image using byte-wise comparison. Matching
|
||||
samples retain half their input intensity as context.
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -200,6 +200,56 @@ ____
|
||||
|
||||
The LIBRT_DEBUG environment variable may be set to the hexadecimal bit vector as described by the DEBUG_* flags listed in the raytrace.h header. Similarly, many LIBRT applications provide a "-x #" command-line option which will take precedence over the environment variable.
|
||||
|
||||
The RT_DEBUG_RTCMP flag (0x10000000) (set with LIBRT_DEBUG=10000000)
|
||||
sets up rtcmp capture, but recording starts disabled. Set
|
||||
LIBRT_RTCMP_FILE before enabling the flag, then call
|
||||
rt_rtcmp_capture_set_enabled(1) from the application or a debugger to
|
||||
record selected rays. Call rt_rtcmp_capture_set_enabled(0) to stop
|
||||
recording. Enabling returns -1 if the debug flag is unset or the output
|
||||
writer failed to initialize; disabling returns 0 even before setup. A
|
||||
ray must begin while recording is enabled, and leaving recording
|
||||
disabled when it finishes suppresses its record. The debug flag must be
|
||||
set before concurrent ray shooting; use the setter to change recording
|
||||
state while rays are running.
|
||||
|
||||
Captured hits append one high-precision rtcmp JSON record per ray with
|
||||
final partitions. Misses produce no record. librt never clears the
|
||||
output file; the caller must clear or rotate it before a new comparison
|
||||
as needed. Separate processes must not write to the same file
|
||||
concurrently. Pending records are flushed within two seconds under
|
||||
normal operation and during normal process shutdown; call
|
||||
rt_rtcmp_capture_flush() to wait for pending records when using a
|
||||
debugger. Disabling recording does not discard records already queued. A
|
||||
process terminated abnormally may lose pending records. rtcmp comparison
|
||||
files should use the same hit-only capture mode on both sides; its
|
||||
generation mode also includes misses. The output can be expanded to also
|
||||
include primitive segments if LIBRT_RTCMP_PRIMITIVES is set to 1 before
|
||||
capture is set up.
|
||||
|
||||
For example, run a single-threaded ray tracing application in a debugger
|
||||
and stop at `rt_shootray` for the suspect ray. Capture just that call,
|
||||
then compare it with a capture of the same ray from a reference build:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
$ LIBRT_DEBUG=10000000 LIBRT_RTCMP_FILE=trial.json \
|
||||
gdb --args ./ray_application model.g
|
||||
(gdb) break rt_shootray
|
||||
(gdb) run
|
||||
(gdb) call rt_rtcmp_capture_set_enabled(1)
|
||||
(gdb) finish
|
||||
(gdb) call rt_rtcmp_capture_set_enabled(0)
|
||||
(gdb) call rt_rtcmp_capture_flush()
|
||||
(gdb) quit
|
||||
$ rtcmp -c reference.json trial.json
|
||||
----
|
||||
|
||||
Continue to the suspect ray before enabling capture if the breakpoint
|
||||
first stops on another ray. Produce `reference.json` with the same ray
|
||||
and the same capture mode; set LIBRT_RTCMP_PRIMITIVES=1 for both runs
|
||||
when primitive segments are needed to locate a difference before Boolean
|
||||
evaluation.
|
||||
|
||||
The LIBRT_V4FLIP environment variable may be set to a true or false value to respectively force or disable endianness interpretation of v4 geometry database files. The default behavior is to automatically detect whether flipping the endian interpretation will help LIBRT read a binary-incompatible v4 geometry database file. Setting LIBRT_V4FLIP will override automatic detection.
|
||||
|
||||
|
||||
|
||||
@@ -38,108 +38,6 @@ region. Overlaps and other geometry errors invalidate reported volume and
|
||||
mass properties.
|
||||
|
||||
|
||||
[[experimental_analysis]]
|
||||
== EXPERIMENTAL ANALYSIS
|
||||
|
||||
The separate `--analyze` interface preserves the behavior of existing *gqa*
|
||||
invocations. It reports a concise summary and can write all region results and
|
||||
individual problem observations as structured JSON. The JSON includes
|
||||
per-object rollups when every region belongs unambiguously to one requested
|
||||
object. This interface requires an explicit object list, including when used
|
||||
inside MGED.
|
||||
|
||||
[source]
|
||||
----
|
||||
gqa --analyze [analysis options] object [object ...]
|
||||
----
|
||||
|
||||
`--measure` accepts a comma-separated selection from
|
||||
`volume,mass,area,centroid,moments,bounds`. With no measurement or check selection,
|
||||
the default is `volume,area`. A check-only selection does not require density
|
||||
data. Centroid and moments require mass. `--check` accepts
|
||||
`overlaps,gaps,adjacent-air,exposed-air` and defaults to no checks.
|
||||
The model totals and region measurements use mm, mm^3^, mm^2^, and grams.
|
||||
Mass needs a density table, selected with `--density file` or found using the
|
||||
normal gqa density search.
|
||||
The `bounds` measure reports the finite, conservative region-tree bounding box
|
||||
and its three face areas without firing rays. Subtraction does not shrink the
|
||||
reported box. Bounded results involving half-spaces are supported; regions
|
||||
whose region-tree bounds are unbounded are rejected.
|
||||
|
||||
`--sampler grid`, the experimental interface's default, is the axis-aligned
|
||||
three-view control. `--sampler grid-rotated` uses a non-axis-aligned
|
||||
orthonormal view triad and deterministically shifts the plane grids between
|
||||
refinements to reduce alignment aliasing. Its first direction defaults to 35
|
||||
degrees azimuth and 25 degrees elevation and may be changed with `--azimuth`
|
||||
and `--elevation`.
|
||||
Both grid modes use `--spacing mm`; when omitted, the initial spacing is the
|
||||
larger of 50 mm and one 128th of the tight model diagonal. `--refine levels`
|
||||
sets a maximum number of additional passes. `--sampler crofton` uses
|
||||
isotropic rays. Measurement-only runs without an explicit resource control
|
||||
use a 60000 ms budget and stop sooner when the automatic stability target is
|
||||
reached. The planner may stop before the exact deadline when there is not
|
||||
enough time for another complete sampling checkpoint. `--rays count`
|
||||
supplies a Crofton hard ray cap;
|
||||
`--sequence random|qmc` chooses an explicit sequence. A QMC request requires
|
||||
a build with OpenQMC. `--seed value` makes randomized sampling reproducible.
|
||||
`--stability mm` requests equivalent-radius convergence. A grid must meet the
|
||||
target for both refinement change and three-view directional spread twice in
|
||||
succession. `--time ms` sets an internal wall-clock budget.
|
||||
The automatic target is the smaller of `BN_TOL_DIST / 10` (0.00005 mm)
|
||||
and 0.1 percent of the smallest positive tight model span, allowing valid small
|
||||
objects to use a proportionally smaller target. Only estimates needed by the
|
||||
requested measurements participate in Crofton convergence, and two
|
||||
comparisons at doubling ray-count checkpoints must be stable. Time-controlled
|
||||
runs report progress at approximately one-second intervals. The summary and
|
||||
JSON identify `stable`, `time_limit`, or an explicitly requested ray or
|
||||
refinement limit as appropriate.
|
||||
`--tolerance mm` ignores overlap and gap depths at or below the threshold
|
||||
(default 0). Gaps between two
|
||||
partitions of the same region are labeled `internal-void` in the summary.
|
||||
The measurements are
|
||||
sampled estimates, and diagnostic checks can miss problems that no ray hits.
|
||||
|
||||
`--plot-prefix prefix` writes selected results to the traditional
|
||||
`volume.plot3`, `gaps.plot3`, `overlaps.plot3`, `adj_air.plot3`, and
|
||||
`exp_air.plot3` suffixes. `--plot-mode all` records every sampled chord and
|
||||
all grid refinements. The default `representative` mode keeps one
|
||||
deterministic sample per occupied spatial cell, direction, category, and
|
||||
region pair, and draws finite sample footprints so the plot remains visually
|
||||
useful as refinement increases. `--plot-resolution mm` overrides the default
|
||||
cell size of one 128th of the model diagonal. Plotting does not change the
|
||||
sampling calculation.
|
||||
|
||||
In MGED, `--plot-view` incrementally refreshes a `gqa::analysis` overlay. The
|
||||
representative overlay is replaced by each bounded snapshot; all mode appends
|
||||
rays and can be expensive. Adjacent-air snapshots show an interface footprint
|
||||
and a short directional tick. Standalone *gqa* does not provide a live view.
|
||||
|
||||
`--air include|exclude` controls whether modeled air contributes to
|
||||
measurements; inclusion is the default. Surface area counts region partition
|
||||
boundaries and inertia is about the model origin.
|
||||
|
||||
`--uncertainty` reports approximate pointwise 95 percent intervals from 16
|
||||
independently randomized Crofton replications.
|
||||
`--relative-error fraction` and
|
||||
`--absolute-error measure=value[,measure=value...]` request adaptive accuracy;
|
||||
`--accuracy-scope model|all` controls which reported measurements must meet
|
||||
the target. A pilot estimates the required production budget, but only fresh
|
||||
independent production streams determine the reported accuracy status.
|
||||
With `--time ms` and no explicit `--rays`, measured throughput limits the
|
||||
production plan to the remaining time. If both are specified, the ray count
|
||||
is also a hard cap. With neither specified, the default deadline is 60000 ms.
|
||||
Results distinguish `target_met`, `target_not_met`,
|
||||
`time_limit`, and `insufficient_evidence`, and JSON records the pilot,
|
||||
forecast, production timing, deadline state, and achieved interval.
|
||||
|
||||
`--json file` writes a versioned JSON record with model and region measurements
|
||||
and every observed problem, including full region paths and ray information.
|
||||
`--invalid-rays file` writes a separate replay-oriented JSON file for rejected
|
||||
Crofton partitions. Each record includes the ray ID, origin, direction, hit
|
||||
distances, thickness, region path, and rejection reason.
|
||||
Without `--json`, the command prints only the summary. Reported surface area
|
||||
counts region partition boundaries, including internal interfaces.
|
||||
|
||||
[[how_gqa_works]]
|
||||
== HOW GQA WORKS
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+2
-4
@@ -35,16 +35,14 @@
|
||||
#include "analyze/debug.h"
|
||||
#include "analyze/diff.h"
|
||||
#include "analyze/density.h"
|
||||
#include "analyze/gqa.h"
|
||||
#include "analyze/grid.h"
|
||||
#include "analyze/heal.h"
|
||||
#include "analyze/info.h"
|
||||
#include "analyze/nirt.h"
|
||||
#include "analyze/pnts.h"
|
||||
#include "analyze/polygonize.h"
|
||||
#include "analyze/sampling.h"
|
||||
#include "analyze/voxelize.h"
|
||||
#include "analyze/nirt.h"
|
||||
#include "analyze/worker.h"
|
||||
#include "analyze/voxelize.h"
|
||||
|
||||
#endif /* ANALYZE_H */
|
||||
|
||||
|
||||
@@ -5,14 +5,12 @@ set(
|
||||
defines.h
|
||||
density.h
|
||||
diff.h
|
||||
gqa.h
|
||||
grid.h
|
||||
heal.h
|
||||
info.h
|
||||
nirt.h
|
||||
pnts.h
|
||||
polygonize.h
|
||||
sampling.h
|
||||
voxelize.h
|
||||
worker.h
|
||||
)
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/* G Q A . H
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this file; see the file named COPYING for more
|
||||
* information.
|
||||
*/
|
||||
/** @addtogroup libanalyze */
|
||||
/** @{ */
|
||||
/** @file analyze/gqa.h */
|
||||
|
||||
#ifndef ANALYZE_GQA_H
|
||||
#define ANALYZE_GQA_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "analyze/defines.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
struct analyze_densities;
|
||||
struct bu_vls;
|
||||
struct db_i;
|
||||
|
||||
/**
|
||||
* Load the density table selected by @p filename, or the caller's normal
|
||||
* default density source when it is NULL. On success, ownership of both
|
||||
* returned allocations passes to analyze_gqa(); @p source must be allocated
|
||||
* with bu_malloc() and @p densities must be releasable by
|
||||
* analyze_densities_destroy().
|
||||
*/
|
||||
typedef int (*analyze_gqa_density_loader)(
|
||||
struct analyze_densities **densities,
|
||||
char **source,
|
||||
const char *filename,
|
||||
void *data);
|
||||
|
||||
/** Receive an informational progress line during a long analysis. */
|
||||
typedef void (*analyze_gqa_progress_handler)(const char *message, void *data);
|
||||
|
||||
/** Categories used to separate quantitative-analysis plot output. */
|
||||
enum analyze_gqa_plot_category {
|
||||
ANALYZE_GQA_PLOT_VOLUME = 0,
|
||||
ANALYZE_GQA_PLOT_GAP,
|
||||
ANALYZE_GQA_PLOT_OVERLAP,
|
||||
ANALYZE_GQA_PLOT_ADJACENT_AIR,
|
||||
ANALYZE_GQA_PLOT_EXPOSED_AIR,
|
||||
ANALYZE_GQA_PLOT_CATEGORY_COUNT
|
||||
};
|
||||
|
||||
/** Whether a plot batch extends or replaces the preceding visualization. */
|
||||
enum analyze_gqa_plot_action {
|
||||
ANALYZE_GQA_PLOT_APPEND = 0,
|
||||
ANALYZE_GQA_PLOT_REPLACE
|
||||
};
|
||||
|
||||
/** One colored line in model coordinates. */
|
||||
struct analyze_gqa_plot_line {
|
||||
int category;
|
||||
unsigned char color[3];
|
||||
double start[3];
|
||||
double end[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* Immutable plot update. Storage is valid only for the duration of the
|
||||
* callback. An empty replacement clears a previous visualization.
|
||||
*/
|
||||
struct analyze_gqa_plot_batch {
|
||||
int action;
|
||||
const struct analyze_gqa_plot_line *lines;
|
||||
size_t line_count;
|
||||
};
|
||||
|
||||
/** Receive a serialized plot update after a completed ray batch. */
|
||||
typedef void (*analyze_gqa_plot_handler)(
|
||||
const struct analyze_gqa_plot_batch *batch,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Invocation-local services needed by the quantitative analysis engine.
|
||||
*
|
||||
* The database and result string remain caller-owned. A density loader is
|
||||
* required only for analyses that request mass-dependent measurements. The
|
||||
* progress and plot handlers are optional. Plot handlers run serially after
|
||||
* completed ray batches and must not call back into the active analysis.
|
||||
*/
|
||||
struct analyze_gqa_context {
|
||||
struct db_i *dbip;
|
||||
struct bu_vls *result;
|
||||
analyze_gqa_density_loader load_densities;
|
||||
void *density_data;
|
||||
analyze_gqa_progress_handler report_progress;
|
||||
void *progress_data;
|
||||
analyze_gqa_plot_handler report_plot;
|
||||
void *plot_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Locate the model filename in a standalone @c gqa @c --analyze command line.
|
||||
* This lets a caller open the database before invoking analyze_gqa() without
|
||||
* duplicating the engine's option-arity rules.
|
||||
*
|
||||
* @return the zero-based argument index, or -1 for malformed input.
|
||||
*/
|
||||
ANALYZE_EXPORT extern int analyze_gqa_model_argument(
|
||||
int argc,
|
||||
const char *argv[]);
|
||||
|
||||
/**
|
||||
* Run the versioned GQA analysis interface.
|
||||
*
|
||||
* @p argv uses the command form accepted by @c gqa @c --analyze; argv[0] is
|
||||
* the command name and argv[1] is @c --analyze. The selected objects follow
|
||||
* all options. Results and diagnostics replace context->result.
|
||||
*
|
||||
* The function does not retain any context or geometry pointers after return.
|
||||
*
|
||||
* @return ANALYZE_OK on success; ANALYZE_ERROR on invalid input or failure.
|
||||
*/
|
||||
ANALYZE_EXPORT extern int analyze_gqa(
|
||||
const struct analyze_gqa_context *context,
|
||||
int argc,
|
||||
const char *argv[]);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* ANALYZE_GQA_H */
|
||||
|
||||
/** @} */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -1,80 +0,0 @@
|
||||
/* S A M P L I N G . H
|
||||
* BRL-CAD
|
||||
*
|
||||
* Copyright (c) 2026 United States Government as represented by
|
||||
* the U.S. Army Research Laboratory.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*/
|
||||
/** @addtogroup libanalyze */
|
||||
/** @{ */
|
||||
/** @file analyze/sampling.h */
|
||||
|
||||
#ifndef ANALYZE_SAMPLING_H
|
||||
#define ANALYZE_SAMPLING_H
|
||||
|
||||
#include "common.h"
|
||||
#include "analyze/defines.h"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/** Minimum independent replications accepted by the interval estimator. */
|
||||
#define ANALYZE_SAMPLING_MIN_REPLICATES 10u
|
||||
|
||||
struct analyze_sampling_interval {
|
||||
double estimate; /**< arithmetic mean of the replicate estimates */
|
||||
double half_width; /**< approximate two-sided 95% half-width */
|
||||
};
|
||||
|
||||
/**
|
||||
* Estimate a pointwise two-sided 95% sampling interval from independent
|
||||
* randomized replicate estimates. At least ANALYZE_SAMPLING_MIN_REPLICATES
|
||||
* finite values are required. The implementation uses the conservative
|
||||
* 95% Student-t critical value for nine degrees of freedom for every accepted
|
||||
* replicate count.
|
||||
*
|
||||
* This API is suitable for ordinary Monte Carlo or independently randomized
|
||||
* QMC replications. Consecutive points from one deterministic or randomized
|
||||
* QMC sequence are not independent replications.
|
||||
*
|
||||
* @return ANALYZE_OK on success; ANALYZE_ERROR for invalid input.
|
||||
*/
|
||||
ANALYZE_EXPORT extern int analyze_sampling_interval95(
|
||||
struct analyze_sampling_interval *interval,
|
||||
const double *replicate_estimates,
|
||||
size_t replicate_count);
|
||||
|
||||
/**
|
||||
* Estimate a pointwise two-sided 95% interval for a ratio of replicate means.
|
||||
* Numerator and denominator values must be paired independent replications.
|
||||
* The point estimate is mean(numerator) / mean(denominator), and the standard
|
||||
* error uses their paired first-order delta-method residuals. This retains
|
||||
* numerator/denominator covariance and is appropriate for derived quantities
|
||||
* such as a mass centroid.
|
||||
*
|
||||
* @return ANALYZE_OK on success; ANALYZE_ERROR for invalid input or a zero
|
||||
* denominator mean.
|
||||
*/
|
||||
ANALYZE_EXPORT extern int analyze_sampling_ratio_interval95(
|
||||
struct analyze_sampling_interval *interval,
|
||||
const double *replicate_numerators,
|
||||
const double *replicate_denominators,
|
||||
size_t replicate_count);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* ANALYZE_SAMPLING_H */
|
||||
|
||||
/** @} */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "bg/clip.h"
|
||||
#include "bg/lseg.h"
|
||||
#include "bg/obr.h"
|
||||
#include "bg/pca.h"
|
||||
#include "bg/plane.h"
|
||||
#include "bg/polygon.h"
|
||||
#include "bg/sat.h"
|
||||
|
||||
+4
-4
@@ -50,12 +50,12 @@ BG_EXPORT extern int bg_lseg_clip(fastf_t *xp1, fastf_t *yp1, fastf_t *xp2, fast
|
||||
* The RPP has faces parallel to the coordinate planes and is defined
|
||||
* by a minimum point and a maximum point.
|
||||
*
|
||||
* FIXME: the function name implies this takes a point,dir for a,b but
|
||||
* it actually takes a line segment going from points a to b!
|
||||
* Despite its historical name, this function clips the finite line segment
|
||||
* from a to b.
|
||||
*
|
||||
* Returns -
|
||||
* 0 if ray does not hit RPP,
|
||||
* !0 if ray hits RPP.
|
||||
* 0 if the segment does not hit the RPP,
|
||||
* !0 if the segment hits the RPP.
|
||||
*
|
||||
* Implicit Return -
|
||||
* if !0 was returned, "a" and "b" have been clipped to the RPP.
|
||||
|
||||
+6
-3
@@ -126,9 +126,12 @@ BG_EXPORT extern int bg_3d_coplanar_obr(point_t *center,
|
||||
* \endverbatim
|
||||
*
|
||||
*
|
||||
* @param[out] pnts eight points of oriented bounding box
|
||||
* @param points_3d array of 3D points
|
||||
* @param pnt_cnt number of points in pnts array
|
||||
* @param[out] pnts eight non-NULL, caller-provided point_t storage
|
||||
* locations for the oriented bounding box vertices
|
||||
* @param[in] points_3d flat array containing 3 * pnt_cnt coordinate values
|
||||
* @param[in] pnt_cnt number of points in points_3d
|
||||
*
|
||||
* @return 0 on success and -1 for invalid input or when no box can be fit
|
||||
*/
|
||||
BG_EXPORT extern int bg_3d_obb(point_t **pnts,
|
||||
const fastf_t *points_3d,
|
||||
|
||||
+4
-4
@@ -21,7 +21,7 @@
|
||||
/*----------------------------------------------------------------------*/
|
||||
/** @addtogroup bg_pca
|
||||
*
|
||||
* Principle Component Analysis
|
||||
* Principal Component Analysis
|
||||
*
|
||||
* Calculates an XYZ coordinate system such that it aligns with the largest
|
||||
* variations in the supplied data. Intuitively, it "aligns" with the
|
||||
@@ -50,7 +50,7 @@
|
||||
* point_t p;
|
||||
* for (size_t i = 0; i < pntcnt; i++) {
|
||||
* MAT4X3PNT(p, RT, pnts[i]);
|
||||
* VMOVE(pnts[i], v);
|
||||
* VMOVE(pnts[i], p);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
@@ -69,7 +69,7 @@ __BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Perform a Principle Component Analysis on a set of points.
|
||||
* Perform a Principal Component Analysis on a set of points.
|
||||
*
|
||||
* Outputs are a center point and XYZ vectors for the coordinate system.
|
||||
*
|
||||
@@ -88,7 +88,7 @@ BG_EXPORT extern int bg_pca(point_t *c, vect_t *xaxis, vect_t *yaxis, vect_t *za
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* BG_PLANE_H */
|
||||
#endif /* BG_PCA_H */
|
||||
/** @} */
|
||||
/*
|
||||
* Local Variables:
|
||||
|
||||
+7
-4
@@ -1171,9 +1171,10 @@ BG_EXPORT extern int bg_isect_planes(point_t pt,
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Given an origin and a normal, create a plane_t.
|
||||
* Given an origin and a normal, create a plane_t with a unit normal.
|
||||
* The input point and normal are not modified. A zero-length normal fails.
|
||||
*/
|
||||
BG_EXPORT extern int bg_plane_pt_nrml(plane_t *p, point_t pt, vect_t nrml);
|
||||
BG_EXPORT extern int bg_plane_pt_nrml(plane_t *p, const point_t pt, const vect_t nrml);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
@@ -1188,13 +1189,15 @@ BG_EXPORT extern int bg_fit_plane(point_t *c, vect_t *n, size_t npnts, point_t *
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Find the closest U,V point on the plane p to 3d point pt.
|
||||
* Find the closest U,V point on the plane p to 3d point pt. The plane normal
|
||||
* need not be unit length.
|
||||
*/
|
||||
BG_EXPORT extern int bg_plane_closest_pt(fastf_t *u, fastf_t *v, plane_t *p, point_t *pt);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Return the 3D point on the plane at parametric coordinates u, v.
|
||||
* Return the 3D point on the plane at parametric coordinates u, v. The plane
|
||||
* normal need not be unit length.
|
||||
*/
|
||||
BG_EXPORT extern int bg_plane_pt_at(point_t *pt, plane_t *p, fastf_t u, fastf_t v);
|
||||
|
||||
|
||||
@@ -293,6 +293,7 @@ BG_EXPORT extern int bg_3d_polygon_area(fastf_t *area, size_t npts, const point_
|
||||
* @param[in] npts Number of point_ts, stored in pts
|
||||
* @param[in] pts all points of the polygon, sorted counter-clockwise.
|
||||
* The array mustn't contain duplicated points or non-coplanar points.
|
||||
* A zero-area polygon fails.
|
||||
*
|
||||
* @return 0 if calculation was successful
|
||||
* @return 1 if calculation failed, e.g. because one in-parameter is a NULL-pointer
|
||||
@@ -307,7 +308,7 @@ BG_EXPORT extern int bg_3d_polygon_centroid(point_t *cent, size_t npts, const po
|
||||
*@param[in] npts Number of points, pts contains
|
||||
*@param pts Array of point_ts, building a convex polygon. Duplicated points
|
||||
*aren't allowed. The points in the array will be sorted counter-clockwise.
|
||||
*@param[in] cmp Plane equation of the polygon
|
||||
*@param[in] cmp Plane equation of the polygon. Its normal must be nonzero.
|
||||
*
|
||||
*@return 0 if calculation was successful
|
||||
*@return 1 if calculation failed, e.g. because pts is a NULL-pointer
|
||||
|
||||
+20
-8
@@ -39,7 +39,9 @@
|
||||
__BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Test for an intersection between a line and an Axis-Aligned Bounding Box (AABB).
|
||||
* Test for an intersection between an infinite line and an Axis-Aligned
|
||||
* Bounding Box (AABB). ldir need not be unit length, but must be nonzero.
|
||||
* aabb_extent supplies the positive half extents along the coordinate axes.
|
||||
*
|
||||
* Returns 1 if they intersect, 0 otherwise.
|
||||
*/
|
||||
@@ -50,7 +52,10 @@ bg_sat_line_aabb(
|
||||
);
|
||||
|
||||
/**
|
||||
* Test for an intersection between a line and an Oriented Bounding Box (OBB).
|
||||
* Test for an intersection between an infinite line and an Oriented Bounding
|
||||
* Box (OBB). ldir need not be unit length, but must be nonzero. Each OBB
|
||||
* extent vector points from the center to a face center; nonzero vectors must
|
||||
* be mutually perpendicular, and a zero vector represents a degenerate axis.
|
||||
*
|
||||
* Returns 1 if they intersect, 0 otherwise.
|
||||
*/
|
||||
@@ -61,7 +66,9 @@ bg_sat_line_obb(
|
||||
);
|
||||
|
||||
/**
|
||||
* Test for an intersection between a triangle and an Axis-Aligned Bounding Box (AABB).
|
||||
* Test for an intersection between a triangle and an Axis-Aligned Bounding
|
||||
* Box (AABB). extent supplies the positive half extents along the coordinate
|
||||
* axes.
|
||||
*
|
||||
* Returns 1 if they intersect, 0 otherwise.
|
||||
*/
|
||||
@@ -72,7 +79,10 @@ bg_sat_tri_aabb(
|
||||
);
|
||||
|
||||
/**
|
||||
* Test for an intersection between a triangle and an Oriented Bounding Box (OBB).
|
||||
* Test for an intersection between a triangle and an Oriented Bounding Box
|
||||
* (OBB). Each extent vector points from the center to a face center; nonzero
|
||||
* vectors must be mutually perpendicular, and a zero vector represents a
|
||||
* degenerate axis.
|
||||
*
|
||||
* Returns 1 if they intersect, 0 otherwise.
|
||||
*/
|
||||
@@ -85,8 +95,9 @@ bg_sat_tri_obb(
|
||||
/**
|
||||
* Test for an intersection between an Axis-Aligned Bounding Box (AABB) and an
|
||||
* Oriented Bounding Box (OBB). The latter is defined by a center point and
|
||||
* three perpendicular vectors from the center to the centers of the various
|
||||
* faces.
|
||||
* three extent vectors from the center to the centers of the various faces.
|
||||
* Nonzero extent vectors must be mutually perpendicular; a zero vector
|
||||
* represents a degenerate axis.
|
||||
*
|
||||
* Returns 1 if they intersect, 0 otherwise.
|
||||
*/
|
||||
@@ -98,8 +109,9 @@ bg_sat_aabb_obb(
|
||||
|
||||
/**
|
||||
* Test for an intersection between two Oriented Bounding Boxes (OBBs). The
|
||||
* boxes are defined by a center point and three perpendicular vectors from the
|
||||
* center to the centers of the various faces.
|
||||
* boxes are defined by a center point and three extent vectors from the center
|
||||
* to the centers of the various faces. Nonzero extent vectors must be
|
||||
* mutually perpendicular; a zero vector represents a degenerate axis.
|
||||
*
|
||||
* Returns 1 if they intersect, 0 otherwise.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ BG_EXPORT extern double bg_tri_closest_pt(
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* BG_TRI_TRI_H */
|
||||
#endif /* BG_TRI_PT_H */
|
||||
/** @} */
|
||||
/*
|
||||
* Local Variables:
|
||||
|
||||
@@ -54,7 +54,7 @@ BG_EXPORT extern int bg_isect_tri_line(point_t *isect,
|
||||
* If isect is non-NULL, it is written only when an intersection is found.
|
||||
*/
|
||||
BG_EXPORT extern int bg_isect_tri_ray(const point_t orig,
|
||||
const point_t dir,
|
||||
const vect_t dir,
|
||||
const point_t vert0,
|
||||
const point_t vert1,
|
||||
const point_t vert2,
|
||||
|
||||
@@ -87,7 +87,11 @@ BG_EXPORT extern int bg_trimesh_oriented(int vcnt, int fcnt, fastf_t *v, int *f)
|
||||
/**
|
||||
* Check if a mesh is topologically solid. Returns 1 if the mesh is NOT SOLID
|
||||
* and 0 if the mesh is SOLID. A SOLID (0) outcome indicates the mesh satisfies
|
||||
* all three criteria: Closed, Manifold, Oriented
|
||||
* all three criteria: Closed, Manifold, Oriented. If @p bedges is non-NULL,
|
||||
* it is set to NULL when no edge diagnostics are present, or to a bu_calloc()
|
||||
* allocated array of vertex-index pairs for unmatched, misoriented, and excess
|
||||
* edges. The caller must bu_free() a non-NULL result. Use
|
||||
* bg_trimesh_solid2() when structured diagnostics and their counts are needed.
|
||||
*/
|
||||
BG_EXPORT extern int bg_trimesh_solid(int vcnt, int fcnt, fastf_t *v, int *f, int **bedges);
|
||||
|
||||
|
||||
+15
-12
@@ -214,8 +214,8 @@ BN_EXPORT extern void bn_mat_ae(mat_t m,
|
||||
double elev);
|
||||
|
||||
/**
|
||||
* Find the azimuth and elevation angles that correspond to the
|
||||
* direction (not including twist) given by a direction vector.
|
||||
* Find the azimuth and elevation angles, in degrees, that correspond
|
||||
* to the direction (not including twist) given by a direction vector.
|
||||
*/
|
||||
BN_EXPORT extern void bn_ae_vec(fastf_t *azp,
|
||||
fastf_t *elp,
|
||||
@@ -236,14 +236,16 @@ BN_EXPORT extern void bn_aet_vec(fastf_t *az,
|
||||
fastf_t accuracy);
|
||||
|
||||
/**
|
||||
* Find a unit vector from the origin given azimuth and elevation.
|
||||
* Find a unit vector from the origin given azimuth and elevation in
|
||||
* radians.
|
||||
*/
|
||||
BN_EXPORT extern void bn_vec_ae(vect_t vec,
|
||||
fastf_t az,
|
||||
fastf_t el);
|
||||
|
||||
/**
|
||||
* Find a vector from the origin given azimuth, elevation, and distance.
|
||||
* Find a vector from the origin given azimuth and elevation in radians,
|
||||
* and distance.
|
||||
*/
|
||||
BN_EXPORT extern void bn_vec_aed(vect_t vec,
|
||||
fastf_t az,
|
||||
@@ -310,8 +312,10 @@ BN_EXPORT extern void bn_vec_perp(vect_t new_vec,
|
||||
* space by the angle between the two. There are many candidate
|
||||
* matrices.
|
||||
*
|
||||
* The input 'from' and 'to' vectors need not be unit length.
|
||||
* MAT4X3VEC(to, m, from) is the identity that is created.
|
||||
* The input 'from' and 'to' vectors need not be unit length. They are
|
||||
* normalized internally. Near-parallel inputs are handled using the
|
||||
* angular tolerance in 'tol'. If either input cannot be normalized,
|
||||
* the result is the identity matrix.
|
||||
*
|
||||
*/
|
||||
BN_EXPORT extern void bn_mat_fromto(mat_t m,
|
||||
@@ -344,12 +348,11 @@ BN_EXPORT extern void bn_mat_zrot(mat_t m,
|
||||
double cosz);
|
||||
|
||||
/**
|
||||
* Given a direction vector D of unit length, product a matrix which
|
||||
* rotates that vector D onto the -Z axis. This matrix will be
|
||||
* suitable for use as a "model2view" matrix.
|
||||
*
|
||||
* XXX This routine will fail if the vector is already more or less
|
||||
* aligned with the Z axis.
|
||||
* Given a nonzero direction vector D, produce a matrix which
|
||||
* rotates D onto the -Z axis. D is normalized internally. Z-axis
|
||||
* directions use a deterministic roll, and a zero direction produces
|
||||
* the identity matrix. This matrix will be suitable for use as a
|
||||
* model2view matrix.
|
||||
*
|
||||
* This is done in several steps.
|
||||
*
|
||||
|
||||
@@ -37,11 +37,42 @@ __BEGIN_DECLS
|
||||
|
||||
typedef struct bn_multipoly {
|
||||
uint32_t magic;
|
||||
/**< Number of coefficients in the first polynomial variable. */
|
||||
int dgrs;
|
||||
/**< Number of coefficients in the second polynomial variable. */
|
||||
int dgrt;
|
||||
/**< Coefficient of S^s * T^t is cf[s][t]. */
|
||||
double **cf;
|
||||
} bn_multipoly_t;
|
||||
|
||||
/**
|
||||
* Allocate a zero-valued bivariate polynomial with the specified
|
||||
* coefficient counts. Both counts must be positive.
|
||||
*/
|
||||
BN_EXPORT extern struct bn_multipoly *bn_multipoly_new(int dgrs, int dgrt);
|
||||
|
||||
/** Release a bivariate polynomial allocated by bn_multipoly_new(). */
|
||||
BN_EXPORT extern void bn_multipoly_free(struct bn_multipoly *poly);
|
||||
|
||||
/** Grow a polynomial to at least the specified coefficient counts. */
|
||||
BN_EXPORT extern struct bn_multipoly *bn_multipoly_grow(struct bn_multipoly *poly,
|
||||
int dgrs,
|
||||
int dgrt);
|
||||
|
||||
/** Set the coefficient of S^s * T^t, growing the polynomial as needed. */
|
||||
BN_EXPORT extern struct bn_multipoly *bn_multipoly_set(struct bn_multipoly *poly,
|
||||
int s,
|
||||
int t,
|
||||
double value);
|
||||
|
||||
/** Return the sum of two bivariate polynomials. */
|
||||
BN_EXPORT extern struct bn_multipoly *bn_multipoly_add(const struct bn_multipoly *p1,
|
||||
const struct bn_multipoly *p2);
|
||||
|
||||
/** Return the product of two bivariate polynomials. */
|
||||
BN_EXPORT extern struct bn_multipoly *bn_multipoly_mul(const struct bn_multipoly *p1,
|
||||
const struct bn_multipoly *p2);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* BN_MULTIPOLY_H */
|
||||
|
||||
+20
-4
@@ -45,7 +45,10 @@ __BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Polynomial data type
|
||||
* bn_poly->cf[n] corresponds to X^n
|
||||
*
|
||||
* Coefficients are stored from the highest to the lowest degree:
|
||||
* `cf[0]` is the coefficient of `X^dgr` and `cf[dgr]` is the
|
||||
* constant coefficient.
|
||||
*/
|
||||
typedef struct bn_poly {
|
||||
uint32_t magic;
|
||||
@@ -63,6 +66,9 @@ typedef struct bn_poly {
|
||||
*
|
||||
* @brief
|
||||
* multiply two polynomials
|
||||
*
|
||||
* The output may alias either input. If the result exceeds
|
||||
* BN_MAX_POLY_DEGREE, returns BN_POLY_NULL without modifying product.
|
||||
*/
|
||||
BN_EXPORT extern struct bn_poly *bn_poly_mul(struct bn_poly *product,
|
||||
const struct bn_poly *m1,
|
||||
@@ -81,6 +87,8 @@ BN_EXPORT extern struct bn_poly *bn_poly_scale(struct bn_poly *eqn,
|
||||
* bn_poly_add
|
||||
* @brief
|
||||
* add two polynomials
|
||||
*
|
||||
* The output may alias either input.
|
||||
*/
|
||||
BN_EXPORT extern struct bn_poly *bn_poly_add(struct bn_poly *sum,
|
||||
const struct bn_poly *poly1,
|
||||
@@ -90,6 +98,8 @@ BN_EXPORT extern struct bn_poly *bn_poly_add(struct bn_poly *sum,
|
||||
* bn_poly_sub
|
||||
* @brief
|
||||
* subtract two polynomials
|
||||
*
|
||||
* The output may alias either input.
|
||||
*/
|
||||
BN_EXPORT extern struct bn_poly *bn_poly_sub(struct bn_poly *diff,
|
||||
const struct bn_poly *poly1,
|
||||
@@ -98,7 +108,10 @@ BN_EXPORT extern struct bn_poly *bn_poly_sub(struct bn_poly *diff,
|
||||
/**
|
||||
* @brief
|
||||
* Divides any polynomial into any other polynomial using synthetic
|
||||
* division. Both polynomials must have real coefficients.
|
||||
* division. Both polynomials must have real coefficients and the
|
||||
* divisor's leading coefficient must be nonzero. The quotient and
|
||||
* remainder outputs must be distinct; the quotient may alias the
|
||||
* dividend. All other output/input pairs must be distinct.
|
||||
*/
|
||||
BN_EXPORT extern void bn_poly_synthetic_division(struct bn_poly *quo,
|
||||
struct bn_poly *rem,
|
||||
@@ -108,7 +121,8 @@ BN_EXPORT extern void bn_poly_synthetic_division(struct bn_poly *quo,
|
||||
/**
|
||||
*@brief
|
||||
* Uses the quadratic formula to find the roots (in `complex' form) of
|
||||
* any quadratic equation with real coefficients.
|
||||
* any quadratic equation with real coefficients. A nonzero leading
|
||||
* coefficient need not be one.
|
||||
*
|
||||
* @return 1 for success
|
||||
* @return 0 for fail.
|
||||
@@ -144,6 +158,7 @@ BN_EXPORT extern int bn_poly_quadratic_roots(struct bn_complex roots[],
|
||||
* If D < 0, there will be three unequal real roots.
|
||||
*
|
||||
* Returns 1 for success, 0 for fail.
|
||||
* A nonzero leading coefficient need not be one.
|
||||
*/
|
||||
BN_EXPORT extern int bn_poly_cubic_roots(struct bn_complex roots[],
|
||||
const struct bn_poly *eqn);
|
||||
@@ -151,7 +166,8 @@ BN_EXPORT extern int bn_poly_cubic_roots(struct bn_complex roots[],
|
||||
/**
|
||||
*@brief
|
||||
* Uses the quartic formula to find the roots (in `complex' form)
|
||||
* of any quartic equation with real coefficients.
|
||||
* of any quartic equation with real coefficients. A nonzero leading
|
||||
* coefficient need not be one.
|
||||
*
|
||||
* @return 1 for success
|
||||
* @return 0 for fail.
|
||||
|
||||
+4
-3
@@ -100,8 +100,8 @@ BN_EXPORT extern void quat_double(quat_t qout,
|
||||
/**
|
||||
*@brief Gives the bisector of quaternions q1 and q2.
|
||||
*
|
||||
* (Could be done with quat_slerp and factor 0.5)
|
||||
* [I believe they must be unit quaternions this to work]
|
||||
* Inputs must be unit quaternions. Antipodal inputs use a valid
|
||||
* great-circle midpoint.
|
||||
*/
|
||||
BN_EXPORT extern void quat_bisect(quat_t qout,
|
||||
const quat_t q1,
|
||||
@@ -161,7 +161,8 @@ BN_EXPORT extern void quat_exp(quat_t out,
|
||||
|
||||
/**
|
||||
*@brief
|
||||
* Take the natural logarithm of a unit quaternion.
|
||||
* Take the natural logarithm of a unit quaternion. The identity
|
||||
* quaternion maps to the zero quaternion.
|
||||
* Code by Ken Shoemake.
|
||||
*/
|
||||
BN_EXPORT extern void quat_log(quat_t out,
|
||||
|
||||
+16
-9
@@ -205,23 +205,30 @@ BU_EXPORT extern int bu_sscanf(const char *src, const char *fmt, ...) _BU_ATTR_S
|
||||
/** Routines for scanning certain kinds of data. */
|
||||
|
||||
/**
|
||||
* Scans a sequence of fastf_t numbers from a string or stdin
|
||||
* Scans a sequence of fastf_t numbers from a string or stdin.
|
||||
*
|
||||
* Scanning fastf_t numbers with bu_sscanf() is difficult, because
|
||||
* doing so requires scanning to some intermediate type like double
|
||||
* and then assigning to the fastf_t variable to convert the value to
|
||||
* whatever type fastf_t really is. This function makes it possible
|
||||
* to scan a series of fastf_t numbers separated by some character(s)
|
||||
* easily, by doing the required conversion internally to the
|
||||
* functions. As series of delimiter characters will be skipped,
|
||||
* empty scan fields are not supported (e.g., "0.0,,0.0,1.0" will scan
|
||||
* as 3 fields, not 4 with the 2nd skipped).
|
||||
* to scan a series of fastf_t numbers easily, by doing the required
|
||||
* conversion internally. Delimiters form a non-empty character set:
|
||||
* one or more delimiter characters must separate values, and repeated
|
||||
* delimiters are skipped. Empty scan fields are therefore not
|
||||
* supported (e.g., "0.0,,0.0,1.0" scans as 3 fields, not 4 with the
|
||||
* second skipped). A trailing delimiter is not consumed after the
|
||||
* final requested value.
|
||||
*
|
||||
* @param[out] c Returns number of characters scanned by the function
|
||||
* @param[out] c Optional destination for the number of input characters
|
||||
* consumed. It is set to zero before scanning and remains zero for
|
||||
* invalid arguments or when no value is scanned.
|
||||
* @param[in] src A source string to scan from, or NULL to read from stdin
|
||||
* @param[in] delim Any delimiter character(s) to skip between scan values
|
||||
* @param[in] delim Non-empty set of delimiter characters to skip between
|
||||
* scan values
|
||||
* @param[in] n Number of fastf_t values to scan from the src input string
|
||||
* @param[out] ... Pointers to fastf_t for storing scanned values (optional)
|
||||
* @param[out] ... Exactly n fastf_t pointers for storing scanned values;
|
||||
* individual pointers may be NULL
|
||||
* @return Number of values scanned
|
||||
*
|
||||
*/
|
||||
BU_EXPORT extern int bu_scan_fastf_t(int *c, const char *src, const char *delim, size_t n, ...);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
45
|
||||
46
|
||||
|
||||
@@ -82,10 +82,6 @@ typedef int (*ged_func_ptr)(struct ged *, int, const char *[]);
|
||||
/* Callback related definitions */
|
||||
typedef void (*ged_io_func_t)(void *, int);
|
||||
typedef void (*ged_refresh_func_t)(void *);
|
||||
typedef void (*ged_main_thread_callback_t)(void *);
|
||||
/** Run callback synchronously on the display-owning thread. */
|
||||
typedef void (*ged_run_on_main_thread_func_t)(void *,
|
||||
ged_main_thread_callback_t, void *);
|
||||
typedef void (*ged_create_vlist_solid_func_t)(void *, struct bv_scene_obj *);
|
||||
typedef void (*ged_create_vlist_display_list_func_t)(void *, struct display_list *);
|
||||
typedef void (*ged_destroy_vlist_func_t)(void *, unsigned int, int);
|
||||
@@ -239,8 +235,6 @@ struct ged {
|
||||
struct ged_callback_state *ged_cbs;
|
||||
void (*ged_refresh_handler)(void *); /**< @brief function for handling refresh requests */
|
||||
void *ged_refresh_clientdata; /**< @brief client data passed to refresh handler */
|
||||
ged_run_on_main_thread_func_t ged_run_on_main_thread; /**< @brief run display-affine work on the owning thread */
|
||||
void *ged_main_thread_clientdata; /**< @brief client data passed to the main-thread handler */
|
||||
void (*ged_output_handler)(struct ged *, char *); /**< @brief function for handling output */
|
||||
void (*ged_create_vlist_scene_obj_callback)(void *, struct bv_scene_obj *); /**< @brief function to call after creating a vlist to create display list for solid */
|
||||
void (*ged_create_vlist_display_list_callback)(void *, struct display_list *); /**< @brief function to call after all vlist created that loops through creating display list for each solid */
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ __BEGIN_DECLS
|
||||
#define RT_DEBUG_HF 0x04000000 /**< @brief 27 Height Field solids */
|
||||
|
||||
#define RT_DEBUG_MESHING 0x08000000 /**< @brief 28 Print meshing/triangulation details */
|
||||
#define RT_DEBUG_UNUSED_3 0x10000000 /**< @brief 29 Unassigned */
|
||||
#define RT_DEBUG_RTCMP 0x10000000 /**< @brief 29 Set up rtcmp capture; recording uses a runtime toggle. If LIBRT_RTCMP_PRIMITIVES=1 includes primitive segments. */
|
||||
#define RT_DEBUG_UNUSED_4 0x20000000 /**< @brief 30 Unassigned */
|
||||
|
||||
/* Options which will cause the library to write binary debugging output */
|
||||
@@ -96,7 +96,7 @@ __BEGIN_DECLS
|
||||
"\040PL_BOX" \
|
||||
"\037PL_SOLIDS" \
|
||||
"\036UNUSED_4" \
|
||||
"\035UNUSED_3" \
|
||||
"\035RTCMP" \
|
||||
"\034UNUSED_2" \
|
||||
"\033HF" \
|
||||
"\032EBM" \
|
||||
|
||||
+23
-6
@@ -465,6 +465,17 @@ rt_get_solid_keypoint(struct rt_edit *s, point_t *pt, const char **strp, fastf_t
|
||||
RT_EXPORT extern void
|
||||
rt_edit_set_edflag(struct rt_edit *s, int edflag);
|
||||
|
||||
/**
|
||||
* Set a parameter-edit translation target expressed in model base units.
|
||||
*
|
||||
* rt_edit's parameter-edit handlers consume e_para in database local units,
|
||||
* while keypoints and edit axes are maintained in model base units. This
|
||||
* helper performs the boundary conversion for callers that have computed a
|
||||
* target from a keypoint, axis, or view transform.
|
||||
*/
|
||||
RT_EXPORT extern void
|
||||
rt_edit_set_translation_target(struct rt_edit *s, const point_t target);
|
||||
|
||||
RT_EXPORT extern int
|
||||
rt_edit_knob_cmd_process(
|
||||
struct rt_edit *s,
|
||||
@@ -499,8 +510,10 @@ rt_knob_edit_sca(
|
||||
int matrix_edit);
|
||||
|
||||
/* Equivalent to sedit - run editing logic after input data is set in
|
||||
* rt_edit container */
|
||||
RT_EXPORT extern void
|
||||
* rt_edit container. Transient input flags are consumed whether the edit
|
||||
* succeeds or fails; parameter values remain available to the caller.
|
||||
* Returns BRLCAD_ERROR if the primitive edit fails. */
|
||||
RT_EXPORT extern int
|
||||
rt_edit_process(struct rt_edit *s);
|
||||
|
||||
/**
|
||||
@@ -521,7 +534,7 @@ rt_edit_snap_point(point2d_t pt, const struct rt_edit *s);
|
||||
* restored later with rt_edit_revert().
|
||||
*
|
||||
* The snapshot is stored inside the rt_edit struct. Calling this
|
||||
* function again overwrites any previous snapshot (single-level undo).
|
||||
* function again replaces the previous snapshot only on success.
|
||||
*
|
||||
* @return BRLCAD_OK on success, BRLCAD_ERROR if the export failed.
|
||||
*/
|
||||
@@ -532,8 +545,8 @@ rt_edit_checkpoint(struct rt_edit *s);
|
||||
* Restore primitive parameters from the snapshot saved by
|
||||
* rt_edit_checkpoint().
|
||||
*
|
||||
* If no snapshot has been saved (or the last snapshot was already
|
||||
* consumed) this function logs a message and returns BRLCAD_ERROR.
|
||||
* The snapshot remains available for repeated restores. If none has
|
||||
* been saved, this function logs a message and returns BRLCAD_ERROR.
|
||||
*
|
||||
* @return BRLCAD_OK on success, BRLCAD_ERROR otherwise.
|
||||
*/
|
||||
@@ -697,7 +710,11 @@ enum rt_constraint_edit_op_kind {
|
||||
RT_CONSTRAINT_EDIT_OP_SET_BEND,
|
||||
RT_CONSTRAINT_EDIT_OP_SCALE_OD,
|
||||
RT_CONSTRAINT_EDIT_OP_SCALE_ID,
|
||||
RT_CONSTRAINT_EDIT_OP_SCALE_BEND
|
||||
RT_CONSTRAINT_EDIT_OP_SCALE_BEND,
|
||||
/* Set every point when an all-zero dimension has no scale reference. */
|
||||
RT_CONSTRAINT_EDIT_OP_SET_ALL_OD,
|
||||
RT_CONSTRAINT_EDIT_OP_SET_ALL_ID,
|
||||
RT_CONSTRAINT_EDIT_OP_SET_ALL_BEND
|
||||
};
|
||||
|
||||
struct rt_constraint_edit_param_ref {
|
||||
|
||||
+11
-270
@@ -196,12 +196,12 @@ RT_EXPORT extern int rt_obj_prep_serialize(struct soltab *stp, const struct rt_d
|
||||
/**
|
||||
* Stopping-criteria parameters for rt_crofton_shoot() and rt_crofton_sample().
|
||||
*
|
||||
* All stopping fields default to zero. Behaviour when all three are zero
|
||||
* (or the pointer is NULL) is identical to the historical default: fire
|
||||
* 2 000 rays and stop when two successive 1 %-threshold iterations agree.
|
||||
* All fields default to zero. Behaviour when all three are zero (or the
|
||||
* pointer is NULL) is identical to the historical default: fire 2 000 rays
|
||||
* and stop when two successive 1 %-threshold iterations agree.
|
||||
*
|
||||
* When one or more non-zero stopping fields are provided, sampling continues
|
||||
* until the FIRST criterion that is met:
|
||||
* When one or more non-zero fields are provided, sampling continues until
|
||||
* the FIRST criterion that is met:
|
||||
*
|
||||
* n_rays > 0 Stop once this many rays have been fired in total.
|
||||
*
|
||||
@@ -211,65 +211,30 @@ RT_EXPORT extern int rt_obj_prep_serialize(struct soltab *stp, const struct rt_d
|
||||
* iterations: r_sa = sqrt(SA / (4*pi)) for surface area
|
||||
* and r_v = cbrt(3*V / (4*pi)) for volume. Sampling
|
||||
* stops when both |Δr_sa| and |Δr_v| (for whichever
|
||||
* outputs are requested) remain <= stability_mm for
|
||||
* outputs are requested) remain < stability_mm for
|
||||
* consecutive stable windows.
|
||||
*
|
||||
* time_ms > 0 Stop once this many wall-clock milliseconds have
|
||||
* elapsed, returning the best estimate accumulated so far.
|
||||
*
|
||||
* When multiple stopping fields are non-zero, the first criterion to fire
|
||||
* wins, giving callers fine control over the accuracy / speed trade-off.
|
||||
* When multiple fields are non-zero the first criterion to fire wins,
|
||||
* giving callers fine control over the accuracy / speed trade-off.
|
||||
*
|
||||
* @c stability_metrics selects which estimates must meet @c stability_mm.
|
||||
* The default derives them from the requested output arguments; visitor APIs
|
||||
* return both statistics and therefore consider both unless told otherwise.
|
||||
*
|
||||
* The optional progress callback runs serially after completed ray batches.
|
||||
* It is informational and must not call back into the active Crofton
|
||||
* session.
|
||||
*/
|
||||
typedef void (*rt_crofton_progress_fn)(size_t ray_count,
|
||||
size_t crossing_count,
|
||||
double surface_area,
|
||||
double volume,
|
||||
double stability_mm,
|
||||
int stability_evaluated,
|
||||
double elapsed_ms,
|
||||
void *data);
|
||||
|
||||
/** Estimates considered by stability-based stopping. */
|
||||
enum rt_crofton_stability_metric {
|
||||
RT_CROFTON_STABILITY_DEFAULT = 0,
|
||||
RT_CROFTON_STABILITY_SURFACE_AREA = 1,
|
||||
RT_CROFTON_STABILITY_VOLUME = 2,
|
||||
RT_CROFTON_STABILITY_ALL =
|
||||
RT_CROFTON_STABILITY_SURFACE_AREA | RT_CROFTON_STABILITY_VOLUME
|
||||
};
|
||||
|
||||
struct rt_crofton_params {
|
||||
size_t n_rays; /**< max total rays; 0 = no limit via this criterion */
|
||||
double stability_mm; /**< equivalent-radius stability target (mm); 0 = disabled */
|
||||
double time_ms; /**< wall-clock time budget (ms); 0 = disabled */
|
||||
unsigned int stability_metrics; /**< rt_crofton_stability_metric bitmask */
|
||||
rt_crofton_progress_fn progress; /**< optional serial progress callback */
|
||||
void *progress_data; /**< caller data for progress */
|
||||
};
|
||||
|
||||
/** One solid partition observed by a Crofton ray. */
|
||||
struct rt_crofton_segment {
|
||||
point_t ray_origin; /**< sampled line origin */
|
||||
vect_t ray_direction; /**< sampled unit line direction */
|
||||
point_t in_point;
|
||||
vect_t in_normal;
|
||||
point_t out_point;
|
||||
vect_t out_normal;
|
||||
fastf_t in_distance; /**< distance from ray_origin to in_point */
|
||||
fastf_t out_distance; /**< distance from ray_origin to out_point */
|
||||
fastf_t thickness;
|
||||
size_t ray_id;
|
||||
const struct region *region; /**< owning region, valid while rtip lives */
|
||||
const struct soltab *in_solid; /**< solid producing the entry hit */
|
||||
const struct soltab *out_solid; /**< solid producing the exit hit */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -285,83 +250,9 @@ struct rt_crofton_result {
|
||||
double volume;
|
||||
};
|
||||
|
||||
/** Initialize an rt_crofton_result before first use. */
|
||||
#define RT_CROFTON_RESULT_INIT { NULL, 0, 0, 0, 0.0, 0.0 }
|
||||
|
||||
/** Explicit sequence selection for experimental Crofton callers. */
|
||||
enum rt_crofton_sequence {
|
||||
RT_CROFTON_SEQUENCE_DEFAULT = 0,
|
||||
RT_CROFTON_SEQUENCE_RANDOM,
|
||||
RT_CROFTON_SEQUENCE_QMC
|
||||
};
|
||||
|
||||
/** Criterion that ended a Crofton sampling call. */
|
||||
enum rt_crofton_stop_reason {
|
||||
RT_CROFTON_STOP_NONE = 0,
|
||||
RT_CROFTON_STOP_RAYS,
|
||||
RT_CROFTON_STOP_STABILITY,
|
||||
RT_CROFTON_STOP_TIME
|
||||
};
|
||||
|
||||
/** Estimates from a Crofton run that does not retain every segment. */
|
||||
struct rt_crofton_stats {
|
||||
size_t ray_count;
|
||||
size_t crossing_count;
|
||||
double surface_area;
|
||||
double volume;
|
||||
double stability_mm; /**< last observed equivalent-radius change */
|
||||
int stability_evaluated; /**< non-zero when stability_mm was observed */
|
||||
size_t invalid_partition_count; /**< rejected non-finite partitions */
|
||||
enum rt_crofton_stop_reason stop_reason; /**< criterion that ended sampling */
|
||||
};
|
||||
|
||||
/** Opaque reusable worker session for serial Crofton sampling calls. */
|
||||
struct rt_crofton_session;
|
||||
|
||||
/** Called serially after each ray batch; the segment is borrowed. */
|
||||
typedef void (*rt_crofton_segment_fn)(
|
||||
const struct rt_crofton_segment *segment, void *data);
|
||||
|
||||
/** A complete sampled ray and its valid positive-length partitions. */
|
||||
struct rt_crofton_ray {
|
||||
size_t ray_id;
|
||||
point_t origin;
|
||||
vect_t direction;
|
||||
const struct rt_crofton_segment *segments;
|
||||
size_t segment_count;
|
||||
};
|
||||
|
||||
/** Called serially after each ray batch; the ray and segments are borrowed. */
|
||||
typedef void (*rt_crofton_ray_fn)(const struct rt_crofton_ray *ray, void *data);
|
||||
|
||||
/** A raw overlap event observed before boolean overlap resolution. */
|
||||
struct rt_crofton_overlap {
|
||||
size_t ray_id;
|
||||
point_t origin;
|
||||
vect_t direction;
|
||||
point_t in_point;
|
||||
point_t out_point;
|
||||
fastf_t depth;
|
||||
const struct region *first_region;
|
||||
const struct region *second_region;
|
||||
};
|
||||
|
||||
/** Called serially after each ray batch for each raw overlap event. */
|
||||
typedef void (*rt_crofton_overlap_fn)(const struct rt_crofton_overlap *overlap,
|
||||
void *data);
|
||||
|
||||
struct rt_crofton_invalid_ray {
|
||||
size_t ray_id;
|
||||
point_t origin;
|
||||
vect_t direction;
|
||||
fastf_t in_distance;
|
||||
fastf_t out_distance;
|
||||
fastf_t thickness;
|
||||
const struct region *region;
|
||||
const char *reason;
|
||||
};
|
||||
|
||||
typedef void (*rt_crofton_invalid_fn)(const struct rt_crofton_invalid_ray *ray,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Run the Cauchy-Crofton ray-sampling estimator on an already-prepared
|
||||
@@ -393,26 +284,6 @@ typedef void (*rt_crofton_invalid_fn)(const struct rt_crofton_invalid_ray *ray,
|
||||
* bound fit failure. A return
|
||||
* value of 0 means no geometry was intersected by the sampler.
|
||||
*
|
||||
* @section crofton_sampler Sampling method
|
||||
*
|
||||
* When BRL-CAD is built with BRLCAD_ENABLE_QMC, Crofton sampling defaults to
|
||||
* OpenQMC's randomized rank-one lattice. It maps four uniformly distributed
|
||||
* coordinates directly to the two direction and two perpendicular-offset
|
||||
* coordinates of an oriented line. Directions are isotropic, and the offset
|
||||
* radius uses R*sqrt(u) so lines are uniform by area across the bounding disk,
|
||||
* not concentrated toward its center. This is the same invariant line
|
||||
* measure used by the independent-random-endpoint construction, so the
|
||||
* Crofton area and volume formulas and stopping criteria do not change.
|
||||
*
|
||||
* The lattice improves finite-sample coverage but its samples are correlated,
|
||||
* unlike ordinary Monte Carlo samples. OpenQMC shuffles indices and applies
|
||||
* toroidal shifts to mitigate alignment artifacts; a fixed pattern schedule
|
||||
* keeps runs reproducible. Set the LIBRT_CROFTON_USE_PRNG environment
|
||||
* variable to a value accepted by bu_str_true() to force the original
|
||||
* independent-PRNG generator. That path is useful for compatibility and for
|
||||
* checking whether an unexpected result is specific to lattice correlation.
|
||||
*
|
||||
*
|
||||
* @section crofton_near_tol Near-tolerance sliver geometry: CSG vs BoT divergence
|
||||
*
|
||||
* The Cauchy-Crofton formula is mathematically exact for any well-defined
|
||||
@@ -508,136 +379,6 @@ RT_EXPORT extern int rt_crofton_collect(struct rt_crofton_result *result,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max);
|
||||
|
||||
/** Whether the librt build supports the QMC Crofton sequence. */
|
||||
RT_EXPORT extern int rt_crofton_qmc_available(void);
|
||||
|
||||
/** Resolve DEFAULT to the sequence selected by this process. */
|
||||
RT_EXPORT extern enum rt_crofton_sequence rt_crofton_resolve_sequence(
|
||||
enum rt_crofton_sequence sequence);
|
||||
|
||||
/**
|
||||
* Visit region-attributed segments without retaining the full sample set.
|
||||
* The callback runs serially between bounded ray batches. It receives every
|
||||
* positive-length partition; a normal may be zero when the raytracer cannot
|
||||
* calculate one. Borrowed pointers are valid only during the callback,
|
||||
* except for the region pointer, which remains valid while @p rtip is alive.
|
||||
*/
|
||||
RT_EXPORT extern int rt_crofton_visit(struct rt_crofton_stats *stats,
|
||||
struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max,
|
||||
enum rt_crofton_sequence sequence,
|
||||
rt_crofton_segment_fn visitor,
|
||||
rt_crofton_invalid_fn invalid_visitor,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Seeded form of rt_crofton_visit(). A zero seed selects the library default;
|
||||
* distinct stream identifiers select reproducible randomized replications.
|
||||
* Independent replications, rather than offsets within one point set, are
|
||||
* required when estimating randomized-QMC sampling error.
|
||||
*/
|
||||
RT_EXPORT extern int rt_crofton_visit_seeded(
|
||||
struct rt_crofton_stats *stats,
|
||||
struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max,
|
||||
enum rt_crofton_sequence sequence,
|
||||
uint64_t seed,
|
||||
uint64_t stream_id,
|
||||
rt_crofton_segment_fn visitor,
|
||||
rt_crofton_invalid_fn invalid_visitor,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Create a reusable Crofton worker session for one prepared raytrace instance.
|
||||
*
|
||||
* A session retains its parallel worker pool and per-CPU raytrace resources
|
||||
* across calls. Calls using a session must be serialized, and @p rtip must
|
||||
* remain alive and unchanged until rt_crofton_session_destroy() returns.
|
||||
* This facility avoids repeatedly starting an all-CPU worker pool when a
|
||||
* caller evaluates several independent randomized replications.
|
||||
*/
|
||||
RT_EXPORT extern struct rt_crofton_session *rt_crofton_session_create(
|
||||
struct rt_i *rtip);
|
||||
|
||||
/** Release a reusable Crofton worker session and its raytrace resources. */
|
||||
RT_EXPORT extern void rt_crofton_session_destroy(
|
||||
struct rt_crofton_session *session);
|
||||
|
||||
/**
|
||||
* Session-backed form of rt_crofton_visit_seeded().
|
||||
*
|
||||
* The seed and stream semantics are identical to
|
||||
* rt_crofton_visit_seeded(). Keeping replications on distinct stream
|
||||
* identifiers is required for randomized-QMC error estimation; reusing the
|
||||
* worker session does not combine or otherwise correlate their samples.
|
||||
*/
|
||||
RT_EXPORT extern int rt_crofton_session_visit_seeded(
|
||||
struct rt_crofton_session *session,
|
||||
struct rt_crofton_stats *stats,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max,
|
||||
enum rt_crofton_sequence sequence,
|
||||
uint64_t seed,
|
||||
uint64_t stream_id,
|
||||
rt_crofton_segment_fn visitor,
|
||||
rt_crofton_invalid_fn invalid_visitor,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Visit complete rays, including rays with no valid partitions. Segment
|
||||
* pointers are borrowed and valid only during the callback. The ray record
|
||||
* contains only positive-length partitions; rejected partitions are reported
|
||||
* through @p invalid_visitor when supplied.
|
||||
*/
|
||||
RT_EXPORT extern int rt_crofton_visit_rays(struct rt_crofton_stats *stats,
|
||||
struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max,
|
||||
enum rt_crofton_sequence sequence,
|
||||
rt_crofton_ray_fn ray_visitor,
|
||||
rt_crofton_invalid_fn invalid_visitor,
|
||||
void *data);
|
||||
|
||||
/** Extended ray visitor with raw overlap events. */
|
||||
RT_EXPORT extern int rt_crofton_visit_rays_ex(struct rt_crofton_stats *stats,
|
||||
struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max,
|
||||
enum rt_crofton_sequence sequence,
|
||||
rt_crofton_ray_fn ray_visitor,
|
||||
rt_crofton_overlap_fn overlap_visitor,
|
||||
rt_crofton_invalid_fn invalid_visitor,
|
||||
void *data);
|
||||
|
||||
/** Seeded form of rt_crofton_visit_rays_ex(). */
|
||||
RT_EXPORT extern int rt_crofton_visit_rays_seeded_ex(
|
||||
struct rt_crofton_stats *stats,
|
||||
struct rt_i *rtip,
|
||||
const struct rt_crofton_params *params,
|
||||
size_t ray_offset,
|
||||
const fastf_t *bbox_min,
|
||||
const fastf_t *bbox_max,
|
||||
enum rt_crofton_sequence sequence,
|
||||
uint64_t seed,
|
||||
uint64_t stream_id,
|
||||
rt_crofton_ray_fn ray_visitor,
|
||||
rt_crofton_overlap_fn overlap_visitor,
|
||||
rt_crofton_invalid_fn invalid_visitor,
|
||||
void *data);
|
||||
|
||||
|
||||
/** Release storage owned by a structured Crofton result. */
|
||||
RT_EXPORT extern void rt_crofton_result_free(
|
||||
struct rt_crofton_result *result);
|
||||
@@ -646,8 +387,8 @@ RT_EXPORT extern void rt_crofton_result_free(
|
||||
/**
|
||||
* Cauchy-Crofton surface-area and/or volume estimator for a primitive.
|
||||
*
|
||||
* Creates a temporary in-memory raytrace of @p ip and samples Crofton lines
|
||||
* until the stopping criteria in @p params are satisfied (or all
|
||||
* Creates a temporary in-memory raytrace of @p ip and fires random chord
|
||||
* rays until the stopping criteria in @p params are satisfied (or all
|
||||
* criteria are zero / params is NULL, in which case the 2 000-ray default
|
||||
* is used). Stores the estimated surface area in @p *area (if non-NULL)
|
||||
* and the estimated volume in @p *vol (if non-NULL).
|
||||
|
||||
@@ -55,14 +55,11 @@ struct clt_tri_specific {
|
||||
};
|
||||
#endif
|
||||
|
||||
#define RT_BOT_SURFNO_INTERIOR (-1)
|
||||
|
||||
/* Shared between bot and ars at the moment */
|
||||
struct bot_specific {
|
||||
unsigned char bot_mode;
|
||||
unsigned char bot_orientation;
|
||||
unsigned char bot_flags;
|
||||
unsigned char bot_parity_shot;
|
||||
size_t bot_ntri;
|
||||
fastf_t *bot_thickness;
|
||||
struct bu_bitv *bot_facemode;
|
||||
|
||||
@@ -41,6 +41,7 @@ struct rt_nmg_edit {
|
||||
struct loopuse *lu_copy; /* copy of loop to be extruded */
|
||||
plane_t lu_pl; /* plane equation for loop to be extruded */
|
||||
struct shell *es_s; /* Shell where extrusion is to end up */
|
||||
int es_s_from_extrusion; /* Replace this edit's prior preview */
|
||||
point_t lu_keypoint; /* keypoint of lu_copy for extrusion */
|
||||
struct vertex *es_v; /* Currently selected NMG vertex (ECMD_NMG_VPICK) */
|
||||
struct faceuse *es_fu; /* Currently selected NMG faceuse (ECMD_NMG_FPICK) */
|
||||
|
||||
@@ -55,6 +55,18 @@
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define RT_RTCMP_CAPTURE_RUNTIME_TOGGLE 1
|
||||
|
||||
/** Set RT_DEBUG_RTCMP recording: nonzero enables, zero disables.
|
||||
* Enabling requires the debug flag and a usable output writer.
|
||||
* Return 0 on success and -1 on failure. Disabling succeeds even
|
||||
* before setup.
|
||||
*/
|
||||
RT_EXPORT extern int rt_rtcmp_capture_set_enabled(int enabled);
|
||||
|
||||
/** Wait until pending RT_DEBUG_RTCMP records have reached the output file. */
|
||||
RT_EXPORT extern int rt_rtcmp_capture_flush(void);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Shoot a ray
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -2200,7 +2205,11 @@ macro(find_package_opencv)
|
||||
set(OpenCV_DIR_TMP "${OpenCV_DIR}")
|
||||
set(OpenCV_DIR "${CMAKE_BINARY_DIR}/${LIB_DIR}/cmake/opencv4")
|
||||
set(OpenCV_ROOT ${CMAKE_BINARY_DIR})
|
||||
find_package(OpenCV COMPONENTS core features2d imgproc highgui)
|
||||
find_package(OpenCV CONFIG COMPONENTS core imgproc imgcodecs highgui)
|
||||
if(OpenCV_FOUND AND OpenCV_VERSION_MAJOR GREATER_EQUAL 5)
|
||||
# OpenCV 5 moved boundingRect from imgproc to geometry.
|
||||
find_package(OpenCV CONFIG COMPONENTS geometry)
|
||||
endif()
|
||||
unset(OpenCV_ROOT)
|
||||
|
||||
# If no bundled copy, see what the system has
|
||||
|
||||
@@ -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
|
||||
@@ -258,7 +258,6 @@ function(BRLCAD_Summary)
|
||||
ext_report("Netpbm" NETPBM_STATUS)
|
||||
ext_report("OpenCV" OPENCV_STATUS)
|
||||
ext_report("OpenNURBS" OPENNURBS_STATUS)
|
||||
ext_report("OpenQMC" OPENQMC_STATUS REQUIRED_VARS "BRLCAD_ENABLE_QMC")
|
||||
ext_report("OSMesa" OSMESA_STATUS)
|
||||
ext_report("Portable Network Graphics" PNG_STATUS)
|
||||
|
||||
|
||||
@@ -308,10 +308,6 @@ if(BRLCAD_ENABLE_OPENCL)
|
||||
endif(NOT OPENCL_FOUND)
|
||||
endif(BRLCAD_ENABLE_OPENCL)
|
||||
|
||||
# Enable quasi-Monte Carlo sampling where supported
|
||||
option(BRLCAD_ENABLE_QMC "Enable quasi-Monte Carlo sampling" OFF)
|
||||
mark_as_advanced(BRLCAD_ENABLE_QMC)
|
||||
|
||||
# Enable features requiring OpenVDB
|
||||
option(BRLCAD_ENABLE_OPENVDB "Enable features requiring OpenVDB" OFF)
|
||||
mark_as_advanced(BRLCAD_ENABLE_OPENVDB)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
|
||||
@@ -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.\
|
||||
\
|
||||
|
||||
@@ -117,6 +117,20 @@ if(TARGET libged)
|
||||
|
||||
endif(TARGET libged)
|
||||
|
||||
if(TARGET librt AND FUZZER_SUPPORTED)
|
||||
set(RT_EDIT_FUZZ_DIR "${CMAKE_SOURCE_DIR}/src/librt/tests/edit")
|
||||
brlcad_addexec(
|
||||
fuzz_rt_edit_test
|
||||
"${RT_EDIT_FUZZ_DIR}/fuzz.cpp;${RT_EDIT_FUZZ_DIR}/ars.cpp;${RT_EDIT_FUZZ_DIR}/bot.cpp"
|
||||
"libbu;librt;libnmg;libwdb;libbrep"
|
||||
TEST
|
||||
)
|
||||
set_target_properties(fuzz_rt_edit_test PROPERTIES UNITY_BUILD OFF)
|
||||
target_compile_options(fuzz_rt_edit_test PRIVATE ${FUZZER_EXE_COMPILE_OPTIONS})
|
||||
target_link_options(fuzz_rt_edit_test PRIVATE ${FUZZER_LINK_OPTIONS})
|
||||
BRLCAD_ADD_TEST(NAME fuzz_rt_edit_smoke COMMAND fuzz_rt_edit_test -runs=1 -max_total_time=5)
|
||||
endif()
|
||||
|
||||
cmakefiles(
|
||||
CMakeLists.txt
|
||||
fuzz_ged.cpp
|
||||
|
||||
+13
-17
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -300,9 +300,7 @@ crofton_estimate(const char *gfile, const char *obj_name,
|
||||
rt_prep_parallel(rtip, 1);
|
||||
|
||||
/* REGRESS_CROFTON_SAMPLES rays, REGRESS_CROFTON_PCT% convergence threshold */
|
||||
struct rt_crofton_params p = {
|
||||
REGRESS_CROFTON_SAMPLES, REGRESS_CROFTON_STABLE_TARGET, 0.0,
|
||||
RT_CROFTON_STABILITY_DEFAULT, NULL, NULL};
|
||||
struct rt_crofton_params p = { REGRESS_CROFTON_SAMPLES, REGRESS_CROFTON_STABLE_TARGET, 0.0};
|
||||
int ret = rt_crofton_shoot(sa, vol, NULL, NULL, NULL, NULL, NULL,
|
||||
rtip, &p, NULL, NULL);
|
||||
rt_i_destroy(rtip);
|
||||
|
||||
@@ -4,10 +4,10 @@ if(TARGET gqa AND TARGET bview_plot3 AND TARGET plot3stat)
|
||||
brlcad_regression_test(regress-gqa_plot "bview_plot3;gqa;plot3stat" TEST_SCRIPT "${TSCRIPT}" EXEC gqa VEXEC bview_plot3)
|
||||
endif(TARGET gqa AND TARGET bview_plot3 AND TARGET plot3stat)
|
||||
|
||||
if(SH_EXEC AND TARGET mged AND TARGET gqa AND TARGET nirt)
|
||||
if(SH_EXEC AND TARGET mged AND TARGET gqa)
|
||||
brlcad_add_test(NAME regress-gqa COMMAND ${SH_EXEC} "${CMAKE_CURRENT_SOURCE_DIR}/gqa.sh" ${CMAKE_SOURCE_DIR})
|
||||
brlcad_regression_test(regress-gqa "gqa;mged;nirt" TEST_DEFINED)
|
||||
endif(SH_EXEC AND TARGET mged AND TARGET gqa AND TARGET nirt)
|
||||
brlcad_regression_test(regress-gqa "gqa;mged" TEST_DEFINED)
|
||||
endif(SH_EXEC AND TARGET mged AND TARGET gqa)
|
||||
|
||||
# list of temporary files
|
||||
set(
|
||||
@@ -18,62 +18,14 @@ set(
|
||||
gqa.g
|
||||
gqa.gaps.plot3
|
||||
gqa.log
|
||||
gqa.matrix.g
|
||||
gqa.matrix.mged
|
||||
gqa.modern.options.out
|
||||
gqa.modern.invalid_value.out
|
||||
gqa.modern.unknown_option.out
|
||||
gqa.modern.grid.out
|
||||
gqa.modern.grid.json
|
||||
gqa.modern.collision.out
|
||||
gqa.modern.alias_collision.out
|
||||
gqa.alias.json
|
||||
gqa.modern.grid_edge.out
|
||||
gqa.modern.rotated.out
|
||||
gqa.modern.rotated.json
|
||||
gqa.modern.grid_defaults.out
|
||||
gqa.modern.grid_defaults.json
|
||||
gqa.modern.axis_angle.out
|
||||
gqa.modern.refine.out
|
||||
gqa.modern.refine.json
|
||||
gqa.modern.air_excluded.out
|
||||
gqa.modern.matrix.out
|
||||
gqa.modern.random.out
|
||||
gqa.modern.uncertainty.out
|
||||
gqa.modern.uncertainty.json
|
||||
gqa.modern.centroid.out
|
||||
gqa.modern.centroid.json
|
||||
gqa.modern.accuracy.out
|
||||
gqa.modern.accuracy.json
|
||||
gqa.modern.absolute.out
|
||||
gqa.modern.absolute.json
|
||||
gqa.modern.seed_a.out
|
||||
gqa.modern.seed_b.out
|
||||
gqa.modern.issues.out
|
||||
gqa.modern.issues.json
|
||||
gqa.modern.crofton.out
|
||||
gqa.modern.crofton.json
|
||||
gqa.modern.tolerance.out
|
||||
gqa.modern.stability_cap.out
|
||||
gqa.modern.defaults.out
|
||||
gqa.modern.defaults.json
|
||||
gqa.modern.deadline.out
|
||||
gqa.modern.deadline.json
|
||||
gqa.modern.time_limit.out
|
||||
gqa.modern.time_limit.json
|
||||
gqa.modern.gaps.out
|
||||
gqa.modern.gaps.json
|
||||
gqa.modern.air.out
|
||||
gqa.modern.air.json
|
||||
gqa.mass_air.am.out
|
||||
gqa.mass_air.aw.out
|
||||
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.replay.json
|
||||
gqa.replay.out
|
||||
gqa.sphere_adaptive.out
|
||||
gqa.volume.plot3
|
||||
ovlp_overlaps.plot3
|
||||
|
||||
+24
-390
@@ -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"
|
||||
@@ -112,11 +130,6 @@ if test ! -f "$GQABIN" ; then
|
||||
log "Unable to find gqa, aborting"
|
||||
exit 1
|
||||
fi
|
||||
NIRT="`ensearch nirt`"
|
||||
if test ! -f "$NIRT" ; then
|
||||
log "Unable to find nirt, aborting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f density_table.txt
|
||||
echo "5 1 stuff" > density_table.txt
|
||||
@@ -174,23 +187,6 @@ in mass_solid.s rpp 0 1 0 1 0 1
|
||||
r mass_solid.r u mass_solid.s
|
||||
adjust mass_solid.r GIFTmater 5
|
||||
|
||||
# Half-spaces may split finite geometry but do not independently define a
|
||||
# measurable volume.
|
||||
in split.s half 1 0 0 0.5
|
||||
r split_intersection.r u mass_solid.s + split.s
|
||||
r split_subtraction.r u mass_solid.s - split.s
|
||||
g split.g split_intersection.r split_subtraction.r
|
||||
r unbounded.r u split.s
|
||||
in bounded_submodel.s submodel split_intersection.r 0 ""
|
||||
r bounded_submodel.r u bounded_submodel.s
|
||||
in unbounded_submodel.s submodel unbounded.r 0 ""
|
||||
r unbounded_submodel.r u unbounded_submodel.s
|
||||
|
||||
# A 0.01 mm solid verifies that automatic stability scales below
|
||||
# BN_TOL_DIST rather than treating sub-tolerance geometry as ordinary size.
|
||||
in tiny.s rpp 0 0.00001 0 0.00001 0 0.00001
|
||||
r tiny.r u tiny.s
|
||||
|
||||
in mass_air.s rpp 1 2 0 1 0 1
|
||||
r mass_air.r u mass_air.s
|
||||
adjust mass_air.r air 2
|
||||
@@ -199,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
|
||||
@@ -213,19 +213,6 @@ $MGED -c gqa.g <<EOF >> $LOGFILE 2>&1
|
||||
`cat gqa.mged`
|
||||
EOF
|
||||
|
||||
cat > gqa.matrix.mged <<EOF
|
||||
units m
|
||||
in box.s rpp 0 1 0 1 0 1
|
||||
r left.r u box.s
|
||||
r right.r u box.s
|
||||
comb moved.c u right.r
|
||||
arced moved.c/right.r matrix rarc xlate 0.5 0 0
|
||||
g model left.r moved.c
|
||||
q
|
||||
EOF
|
||||
rm -f gqa.matrix.g
|
||||
$MGED -c gqa.matrix.g < gqa.matrix.mged >> $LOGFILE 2>&1
|
||||
|
||||
GQA="$GQABIN -u m,m^3,kg -g 250mm-50mm -p gqa."
|
||||
STATUS=0
|
||||
|
||||
@@ -278,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`"
|
||||
@@ -292,363 +280,9 @@ 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"
|
||||
|
||||
|
||||
# The experimental interface must remain separate from the legacy options.
|
||||
run_capture gqa.modern.grid.out $GQABIN --analyze --measure volume,mass,area --spacing 50 --refine 0 --json gqa.modern.grid.json gqa.g mass_solid.r
|
||||
MODERN_GRID_VOLUME="`extract_last_number 'Volume:' gqa.modern.grid.out`"
|
||||
MODERN_GRID_MASS="`extract_last_number 'Mass:' gqa.modern.grid.out`"
|
||||
assert_close 1000000000 "$MODERN_GRID_VOLUME" 1000 "experimental grid cube volume"
|
||||
assert_close 1000000 "$MODERN_GRID_MASS" 1 "experimental grid cube mass"
|
||||
if test ! -s gqa.modern.grid.json || ! grep -q '"schema_version": 2' gqa.modern.grid.json || ! grep -q '"path": "/mass_solid.r"' gqa.modern.grid.json ; then
|
||||
log "FAIL: experimental JSON region record"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.bounds.out $GQABIN --analyze --measure bounds --json gqa.modern.bounds.json gqa.g mass_solid.r
|
||||
if ! grep -q '^gqa analysis (grid), 0 rays$' gqa.modern.bounds.out ||
|
||||
! grep -q '^Bounding box: 0 0 0 1000 1000 1000 mm$' gqa.modern.bounds.out ||
|
||||
! grep -q '"face_areas_mm2":' gqa.modern.bounds.json ; then
|
||||
log "FAIL: experimental Boolean bounds report"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.plot_all.out $GQABIN --analyze --measure volume --spacing 50 --refine 1 --plot-prefix gqa.modern.plot.all. --plot-mode all gqa.g mass_solid.r
|
||||
run_capture gqa.modern.plot_representative.out $GQABIN --analyze --measure volume --spacing 50 --refine 1 --plot-prefix gqa.modern.plot.representative. --plot-mode representative --plot-resolution 250 gqa.g mass_solid.r
|
||||
ALL_PLOT_SIZE="`wc -c < gqa.modern.plot.all.volume.plot3 | tr -d ' '`"
|
||||
REPRESENTATIVE_PLOT_SIZE="`wc -c < gqa.modern.plot.representative.volume.plot3 | tr -d ' '`"
|
||||
if ! cmp -s gqa.modern.plot_all.out gqa.modern.plot_representative.out ||
|
||||
test ! -s gqa.modern.plot.all.volume.plot3 ||
|
||||
test ! -s gqa.modern.plot.representative.volume.plot3 ||
|
||||
test "$REPRESENTATIVE_PLOT_SIZE" -ge "$ALL_PLOT_SIZE" ; then
|
||||
log "FAIL: experimental all and representative volume plots"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.plot_overlap.out $GQABIN --analyze --check overlaps --spacing 250 --refine 0 --plot-prefix gqa.modern.plot.overlap. gqa.g overlaps
|
||||
run_capture gqa.modern.plot_gap.out $GQABIN --analyze --check gaps --spacing 50 --refine 0 --plot-prefix gqa.modern.plot.gap. gqa.g gap.g
|
||||
run_capture gqa.modern.plot_adj_air.out $GQABIN --analyze --check adjacent-air --spacing 250 --refine 0 --plot-prefix gqa.modern.plot.air. gqa.g adj_air.g
|
||||
run_capture gqa.modern.plot_exp_air.out $GQABIN --analyze --check exposed-air --spacing 250 --refine 0 --plot-prefix gqa.modern.plot.air. gqa.g exposed_air.g
|
||||
if test ! -s gqa.modern.plot.overlap.overlaps.plot3 ||
|
||||
test ! -s gqa.modern.plot.gap.gaps.plot3 ||
|
||||
test ! -s gqa.modern.plot.air.adj_air.plot3 ||
|
||||
test ! -s gqa.modern.plot.air.exp_air.plot3 ; then
|
||||
log "FAIL: experimental diagnostic Plot3 output"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
if $GQABIN --analyze --measure volume --spacing 50 --refine 0 --plot-view gqa.g mass_solid.r > gqa.modern.plot_view.out 2>&1 ; then
|
||||
log "FAIL: standalone gqa accepted --plot-view"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
$MGED -c gqa.g > gqa.modern.mged_plot_view.out 2>&1 <<EOF
|
||||
gqa --analyze --measure volume --spacing 100 --refine 0 --plot-view mass_solid.r
|
||||
view objs
|
||||
quit
|
||||
EOF
|
||||
cat gqa.modern.mged_plot_view.out >> "$LOGFILE"
|
||||
if ! grep -q '^Volume: 1e+09 mm\^3$' gqa.modern.mged_plot_view.out ||
|
||||
! grep -q '^gqa::analysis$' gqa.modern.mged_plot_view.out ; then
|
||||
log "FAIL: MGED live analysis plot"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
GQA_DB_CHECKSUM_BEFORE="`cksum gqa.g`"
|
||||
if $GQABIN --analyze --measure volume --json gqa.g gqa.g mass_solid.r > gqa.modern.collision.out 2>&1 ; then
|
||||
log "FAIL: analysis accepted the database as its JSON output"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
GQA_DB_CHECKSUM_AFTER="`cksum gqa.g`"
|
||||
if test "x$GQA_DB_CHECKSUM_BEFORE" != "x$GQA_DB_CHECKSUM_AFTER" ; then
|
||||
log "FAIL: rejected output collision modified the database"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
if $GQABIN --analyze --measure volume --json gqa.alias.json --invalid-rays ./gqa.alias.json gqa.g mass_solid.r > gqa.modern.alias_collision.out 2>&1 ; then
|
||||
log "FAIL: analysis accepted aliased JSON and invalid-ray output paths"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
if test -e gqa.alias.json ; then
|
||||
log "FAIL: rejected aliased output paths created an output file"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
# The shared bu_opt parser must recognize assignment syntax and options on
|
||||
# either side of positional database and object arguments.
|
||||
run_capture gqa.modern.options.out $GQABIN --analyze --measure=volume gqa.g --sampler=crofton --sequence=random mass_solid.r --rays=1000
|
||||
if ! grep -q '^gqa analysis (crofton, random), 1000 rays$' gqa.modern.options.out ||
|
||||
! grep -q '^Stopping condition: ray_limit$' gqa.modern.options.out ; then
|
||||
log "FAIL: experimental bu_opt syntax and positional argument handling"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
# Exercise QMC when this build provides it. A failed probe is a valid skip
|
||||
# only when gqa explicitly reports that QMC support is unavailable.
|
||||
log "... checking for QMC support"
|
||||
QMC_STATUS=0
|
||||
$GQABIN --analyze --measure=volume --sampler=crofton --sequence=qmc --rays=1000 gqa.g mass_solid.r > gqa.modern.qmc.out 2>&1 || QMC_STATUS=$?
|
||||
cat gqa.modern.qmc.out >> "$LOGFILE"
|
||||
if test $QMC_STATUS -eq 0 ; then
|
||||
if ! grep -q '^gqa analysis (crofton, qmc), 1000 rays$' gqa.modern.qmc.out ||
|
||||
! grep -q '^Stopping condition: ray_limit$' gqa.modern.qmc.out ; then
|
||||
log "FAIL: experimental QMC analysis"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
else
|
||||
if ! grep -q '^QMC is unavailable in this build\.$' gqa.modern.qmc.out ; then
|
||||
log "FAIL: experimental QMC availability check"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $GQABIN --analyze --measure=invalid gqa.g mass_solid.r > gqa.modern.invalid_value.out 2>&1 ; then
|
||||
log "FAIL: analysis accepted an invalid bu_opt value"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
if $GQABIN --analyze --unknown-option gqa.g mass_solid.r > gqa.modern.unknown_option.out 2>&1 ; then
|
||||
log "FAIL: analysis accepted an unknown bu_opt option"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.grid_edge.out $GQABIN --analyze --measure volume,area --spacing 600 --refine 0 gqa.g mass_solid.r
|
||||
MODERN_GRID_EDGE_VOLUME="`extract_last_number 'Volume:' gqa.modern.grid_edge.out`"
|
||||
MODERN_GRID_EDGE_AREA="`extract_last_number 'Area:' gqa.modern.grid_edge.out`"
|
||||
assert_close 1000000000 "$MODERN_GRID_EDGE_VOLUME" 1000 "experimental clipped grid-cell volume"
|
||||
assert_close 6000000 "$MODERN_GRID_EDGE_AREA" 1 "experimental clipped grid-cell area"
|
||||
|
||||
run_capture gqa.modern.half_intersection.out $GQABIN --analyze --measure volume --spacing 50 --refine 0 gqa.g split_intersection.r
|
||||
run_capture gqa.modern.half_subtraction.out $GQABIN --analyze --measure volume --spacing 50 --refine 0 gqa.g split_subtraction.r
|
||||
run_capture gqa.modern.half_split.out $GQABIN --analyze --measure volume --spacing 50 --refine 0 gqa.g split.g
|
||||
HALF_INTERSECTION_VOLUME="`extract_last_number 'Volume:' gqa.modern.half_intersection.out`"
|
||||
HALF_SUBTRACTION_VOLUME="`extract_last_number 'Volume:' gqa.modern.half_subtraction.out`"
|
||||
HALF_SPLIT_VOLUME="`extract_last_number 'Volume:' gqa.modern.half_split.out`"
|
||||
assert_close 500000000 "$HALF_INTERSECTION_VOLUME" 1000 "experimental half-space intersection volume"
|
||||
assert_close 500000000 "$HALF_SUBTRACTION_VOLUME" 1000 "experimental half-space subtraction volume"
|
||||
assert_close 1000000000 "$HALF_SPLIT_VOLUME" 1000 "experimental half-space split volume"
|
||||
|
||||
run_capture gqa.modern.half_crofton.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --seed 42 --rays 20000 gqa.g split.g
|
||||
HALF_CROFTON_VOLUME="`extract_last_number 'Volume:' gqa.modern.half_crofton.out`"
|
||||
assert_close 1000000000 "$HALF_CROFTON_VOLUME" 50000000 "Crofton half-space split volume"
|
||||
|
||||
run_capture gqa.modern.half_submodel.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --seed 42 --rays 20000 gqa.g bounded_submodel.r
|
||||
HALF_SUBMODEL_VOLUME="`extract_last_number 'Volume:' gqa.modern.half_submodel.out`"
|
||||
assert_close 500000000 "$HALF_SUBMODEL_VOLUME" 25000000 "Crofton submodel half-space clipping volume"
|
||||
|
||||
if $GQABIN --analyze --measure volume --spacing 50 --refine 0 gqa.g unbounded.r > gqa.modern.unbounded.out 2>&1 ; then
|
||||
log "FAIL: experimental analysis accepted an unbounded region"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
if ! grep -q '^Cannot analyze unbounded region /unbounded.r\.$' gqa.modern.unbounded.out ; then
|
||||
log "FAIL: experimental unbounded-region diagnostic"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
if $GQABIN --analyze --measure volume --sampler crofton --rays 100 gqa.g unbounded_submodel.r > gqa.modern.unbounded_submodel.out 2>&1 ; then
|
||||
log "FAIL: experimental analysis accepted an unbounded submodel"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
if ! grep -q '^Cannot analyze unbounded region /unbounded_submodel.r\.$' gqa.modern.unbounded_submodel.out ; then
|
||||
log "FAIL: experimental unbounded-submodel diagnostic"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.refine.out $GQABIN --analyze --measure volume,area --spacing 600 --refine 1 --json gqa.modern.refine.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Final refinement volume change:' gqa.modern.refine.out ||
|
||||
! grep -q '"level": 1' gqa.modern.refine.json ; then
|
||||
log "FAIL: experimental grid refinement evidence"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.rotated.out $GQABIN --analyze --measure volume,mass,area,centroid,moments --density density_table.txt --sampler grid-rotated --spacing 50 --refine 2 --azimuth 35 --elevation 25 --json gqa.modern.rotated.json gqa.g mass_solid.r
|
||||
MODERN_ROTATED_VOLUME="`extract_last_number 'Volume:' gqa.modern.rotated.out`"
|
||||
MODERN_ROTATED_MASS="`extract_last_number 'Mass:' gqa.modern.rotated.out`"
|
||||
MODERN_ROTATED_AREA="`extract_last_number 'Area:' gqa.modern.rotated.out`"
|
||||
assert_close 1000000000 "$MODERN_ROTATED_VOLUME" 1000000 "experimental rotated-grid cube volume"
|
||||
assert_close 1000000 "$MODERN_ROTATED_MASS" 1000 "experimental rotated-grid cube mass"
|
||||
assert_close 6000000 "$MODERN_ROTATED_AREA" 10000 "experimental rotated-grid cube area"
|
||||
if ! grep -q '^gqa analysis (grid-rotated),' gqa.modern.rotated.out ||
|
||||
! grep -q '^Centroid:' gqa.modern.rotated.out ||
|
||||
! grep -q '^Inertia tensor' gqa.modern.rotated.out ||
|
||||
! grep -q '^Grid directional spread:' gqa.modern.rotated.out ||
|
||||
! grep -q '"sampler": "grid-rotated"' gqa.modern.rotated.json ||
|
||||
! grep -q '"centroid_mm":' gqa.modern.rotated.json ||
|
||||
! grep -q '"inertia_tensor_g_mm2":' gqa.modern.rotated.json ||
|
||||
! grep -q '"grid_view_directions":' gqa.modern.rotated.json ||
|
||||
! grep -q '"phase_u":' gqa.modern.rotated.json ; then
|
||||
log "FAIL: experimental rotated-grid evidence"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.grid_defaults.out $GQABIN --analyze --measure volume --json gqa.modern.grid_defaults.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Stopping condition: stable$' gqa.modern.grid_defaults.out ||
|
||||
! grep -q '"sampler": "grid"' gqa.modern.grid_defaults.json ||
|
||||
! grep -q '"spacing_defaulted": true' gqa.modern.grid_defaults.json ||
|
||||
! grep -q '"stability_defaulted": true' gqa.modern.grid_defaults.json ||
|
||||
! grep -q '"time_defaulted": true' gqa.modern.grid_defaults.json ||
|
||||
! grep -q '"time_ms": 60000.0' gqa.modern.grid_defaults.json ; then
|
||||
log "FAIL: grid time-first automatic stability defaults"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
if $GQABIN --analyze --measure volume --sampler grid --azimuth 35 --refine 0 gqa.g mass_solid.r > gqa.modern.axis_angle.out 2>&1 ; then
|
||||
log "FAIL: axis-aligned grid accepted a rotated-grid orientation"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.air_excluded.out $GQABIN --analyze --measure volume,mass --air exclude --spacing 50 --refine 0 gqa.g mass_air_mix.g
|
||||
MODERN_SOLID_VOLUME="`extract_last_number 'Volume:' gqa.modern.air_excluded.out`"
|
||||
MODERN_SOLID_MASS="`extract_last_number 'Mass:' gqa.modern.air_excluded.out`"
|
||||
assert_close 1000000000 "$MODERN_SOLID_VOLUME" 1000 "experimental air-excluded volume"
|
||||
assert_close 1000000 "$MODERN_SOLID_MASS" 1 "experimental air-excluded mass"
|
||||
|
||||
run_capture gqa.modern.random.out $GQABIN --analyze --measure volume,area --sampler crofton --sequence random --rays 20000 gqa.g mass_solid.r
|
||||
MODERN_RANDOM_VOLUME="`extract_last_number 'Volume:' gqa.modern.random.out`"
|
||||
assert_close 1000000000 "$MODERN_RANDOM_VOLUME" 50000000 "experimental random Crofton cube volume"
|
||||
|
||||
run_capture gqa.modern.uncertainty.out $GQABIN --analyze --measure volume,area --sampler crofton --sequence random --rays 3200 --uncertainty --accuracy-scope all --json gqa.modern.uncertainty.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Approximate 95% sampling intervals (16 independent replicates):' gqa.modern.uncertainty.out ||
|
||||
! grep -q '"replicates": 16' gqa.modern.uncertainty.json ||
|
||||
! grep -q '"metric": "object:mass_solid.r.volume_mm3"' gqa.modern.uncertainty.json ||
|
||||
! grep -q '"metric": "region:/mass_solid.r#' gqa.modern.uncertainty.json ||
|
||||
! grep -q '"half_width":' gqa.modern.uncertainty.json ; then
|
||||
log "FAIL: Crofton independent-replicate uncertainty"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.centroid.out $GQABIN --analyze --measure mass,centroid --density density_table.txt --sampler crofton --sequence random --rays 3200 --uncertainty --json gqa.modern.centroid.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Centroid:' gqa.modern.centroid.out ||
|
||||
! grep -q '"metric": "model.centroid_x_mm"' gqa.modern.centroid.json ||
|
||||
! grep -q '"centroid_mm":' gqa.modern.centroid.json ; then
|
||||
log "FAIL: Crofton paired-ratio centroid uncertainty"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.absolute.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --rays 6400 --absolute-error volume=100000000 --json gqa.modern.absolute.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Accuracy target:' gqa.modern.absolute.out ||
|
||||
! grep -q '"volume": 100000000' gqa.modern.absolute.json ||
|
||||
! grep -q '"target_tolerance": 100000000' gqa.modern.absolute.json ; then
|
||||
log "FAIL: Crofton absolute accuracy target"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.seed_a.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --seed 42 --rays 1000 gqa.g mass_solid.r
|
||||
run_capture gqa.modern.seed_b.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --seed 42 --rays 1000 gqa.g mass_solid.r
|
||||
if ! cmp -s gqa.modern.seed_a.out gqa.modern.seed_b.out ; then
|
||||
log "FAIL: seeded Crofton sampling is not reproducible"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.defaults.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --seed 42 --json gqa.modern.defaults.json gqa.g tiny.r
|
||||
if ! grep -q '^Stopping condition: stable' gqa.modern.defaults.out ||
|
||||
! grep -q '"rays": null' gqa.modern.defaults.json ||
|
||||
! grep -q '"stability_defaulted": true' gqa.modern.defaults.json ||
|
||||
! grep -q '"stability_mm": 1e-05' gqa.modern.defaults.json ||
|
||||
! grep -q '"time_defaulted": true' gqa.modern.defaults.json ||
|
||||
! grep -q '"time_ms": 60000.0' gqa.modern.defaults.json ; then
|
||||
log "FAIL: Crofton time-first automatic stability defaults"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.accuracy.out $GQABIN --analyze --measure volume,area --sampler crofton --sequence random --rays 6400 --uncertainty --relative-error 0.1 --json gqa.modern.accuracy.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Accuracy target:' gqa.modern.accuracy.out ||
|
||||
! grep -q '"pilot_rays":' gqa.modern.accuracy.json ||
|
||||
! grep -q '"production_rays":' gqa.modern.accuracy.json ; then
|
||||
log "FAIL: Crofton pilot and production accuracy evaluation"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.deadline.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --uncertainty --relative-error 0.1 --time 1000 --json gqa.modern.deadline.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Accuracy target: target_met' gqa.modern.deadline.out ||
|
||||
! grep -q '^Adaptive plan:' gqa.modern.deadline.out ||
|
||||
! grep -q '^Deadline:' gqa.modern.deadline.out ||
|
||||
! grep -q '"rays": null' gqa.modern.deadline.json ||
|
||||
! grep -q '"forecast_production_rays":' gqa.modern.deadline.json ||
|
||||
! grep -q '"planning_safety_factor": 2' gqa.modern.deadline.json ||
|
||||
! grep -q '"deadline_hit": false' gqa.modern.deadline.json ; then
|
||||
log "FAIL: Crofton deadline-driven adaptive accuracy"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.time_limit.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --uncertainty --relative-error 0.000000001 --time 500 --json gqa.modern.time_limit.json gqa.g mass_solid.r
|
||||
if ! grep -q '^Accuracy target: time_limit' gqa.modern.time_limit.out ||
|
||||
! grep -q '"accuracy_status": "time_limit"' gqa.modern.time_limit.json ||
|
||||
! grep -q '"deadline_limited": true' gqa.modern.time_limit.json ; then
|
||||
log "FAIL: Crofton adaptive deadline status"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.issues.out $GQABIN --analyze --measure volume --check overlaps --spacing 250 --json gqa.modern.issues.json gqa.g overlaps
|
||||
if ! grep -q '^overlap: ' gqa.modern.issues.out ||
|
||||
! grep -q '"type": "overlap"' gqa.modern.issues.json ||
|
||||
! grep -q '"start_point_mm"' gqa.modern.issues.json ||
|
||||
! grep -q '"end_point_mm"' gqa.modern.issues.json ; then
|
||||
log "FAIL: experimental overlap report"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.matrix.out $GQABIN --analyze --measure volume --check overlaps --spacing 100 gqa.matrix.g model
|
||||
if ! grep -q '/model/moved.c/right.r' gqa.modern.matrix.out ||
|
||||
grep -q '^Area:' gqa.modern.matrix.out ; then
|
||||
log "FAIL: transformed overlap path must remain complete"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
# Crofton diagnostics use complete ray partitions and must report the same
|
||||
# intentionally constructed overlap, gap, and air cases as the grid path.
|
||||
run_capture gqa.modern.crofton.out $GQABIN --analyze --sampler crofton --sequence random --rays 12000 --check overlaps --json gqa.modern.crofton.json gqa.g overlaps
|
||||
if ! grep -q 'Overlap candidate boxes: ' gqa.modern.crofton.out ||
|
||||
! grep -q '^overlap: ' gqa.modern.crofton.out ||
|
||||
! grep -q '"targeted_overlap": true' gqa.modern.crofton.json ||
|
||||
! grep -q '"event_probability_upper_bound_95"' gqa.modern.crofton.json ||
|
||||
! grep -q '"type": "overlap"' gqa.modern.crofton.json ; then
|
||||
log "FAIL: Crofton targeted overlap report"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
CROFTON_EVENT_COUNT="`grep -c '"ray_id":' gqa.modern.crofton.json`"
|
||||
CROFTON_RAY_COUNT="`grep '"ray_id":' gqa.modern.crofton.json | sort -u | wc -l | tr -d ' '`"
|
||||
if test "$CROFTON_EVENT_COUNT" -ne "$CROFTON_RAY_COUNT" ||
|
||||
test "$CROFTON_RAY_COUNT" -le 1 ; then
|
||||
log "FAIL: Crofton overlap events must be delivered once with their own ray IDs"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.tolerance.out $GQABIN --analyze --sampler crofton --sequence random --rays 1000 --check overlaps --tolerance 1000000000 gqa.g overlaps
|
||||
if grep -q '^overlap: ' gqa.modern.tolerance.out ; then
|
||||
log "FAIL: Crofton overlap tolerance"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.stability_cap.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --rays 100 --stability 1 gqa.g mass_solid.r
|
||||
if ! grep -q '), 100 rays$' gqa.modern.stability_cap.out ; then
|
||||
log "FAIL: Crofton stability must honor the ray limit"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.gaps.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --rays 5000 --check gaps --json gqa.modern.gaps.json gqa.g gap.g
|
||||
if ! grep -q '^gap: ' gqa.modern.gaps.out ||
|
||||
! grep -q '"type": "gap"' gqa.modern.gaps.json ; then
|
||||
log "FAIL: Crofton gap report"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
run_capture gqa.modern.air.out $GQABIN --analyze --measure volume --sampler crofton --sequence random --rays 5000 --check adjacent-air,exposed-air --json gqa.modern.air.json gqa.g adj_air.g exposed_air.g
|
||||
if ! grep -q '"sampler": "crofton"' gqa.modern.air.json ||
|
||||
! grep -q '"ray_count": 5000' gqa.modern.air.json ; then
|
||||
log "FAIL: Crofton air diagnostic run"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
cat > gqa.replay.json <<EOF
|
||||
{"schema_version":2,"model":["mass_solid.r"],"rays":[{"ray_id":7,"origin_mm":[1500,500,500],"direction":[-1,0,0]}]}
|
||||
EOF
|
||||
run_capture gqa.replay.out $NIRT --replay gqa.replay.json --ray-id 7 gqa.g mass_solid.r
|
||||
if ! grep -q 'Replaying ray 7' gqa.replay.out ||
|
||||
! grep -q 'mass_solid.r' gqa.replay.out ; then
|
||||
log "FAIL: nirt replay of analysis ray record"
|
||||
STATUS="`expr $STATUS + 1`"
|
||||
fi
|
||||
|
||||
if [ $STATUS = 0 ] ; then
|
||||
log "-> gqa.sh succeeded"
|
||||
else
|
||||
|
||||
@@ -68,6 +68,7 @@ set(
|
||||
sca_view.mged
|
||||
search_exec.mged
|
||||
sed.mged
|
||||
sed_reset.mged
|
||||
set_perspective.mged
|
||||
setview.mged
|
||||
size.mged
|
||||
@@ -100,6 +101,7 @@ if(TARGET mged)
|
||||
continue()
|
||||
endif(TNAME STREQUAL "")
|
||||
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${testfile}")
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/${testfile}" TEST_CONTENTS)
|
||||
if(TNAME STREQUAL "search_exec")
|
||||
string(CONFIGURE "${TEST_CONTENTS}" TEST_CONTENTS @ONLY)
|
||||
@@ -126,6 +128,12 @@ if(TARGET mged)
|
||||
elseif(TNAME STREQUAL "arb")
|
||||
set(TEST_EXPECT "'arb' testing completed")
|
||||
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
|
||||
elseif(TNAME MATCHES "^(extrude|facedef|mirface|permute)$")
|
||||
set(TEST_EXPECT "'${TNAME}' testing completed")
|
||||
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
|
||||
elseif(TNAME STREQUAL "edgedir")
|
||||
set(TEST_EXPECT "'edarb edgedir' testing completed")
|
||||
set(TEST_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/regress_${testfile}")
|
||||
elseif(TNAME STREQUAL "killtree")
|
||||
set(TEST_EXPECT "KILL Solid: killtree_dup.s")
|
||||
set(TEST_REJECT "db_lookup(killtree_dup.s) failed")
|
||||
@@ -138,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")
|
||||
|
||||
@@ -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 ***"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -43,4 +43,37 @@ foreach {arb_type face expected_type} $arb_extrude_cases {
|
||||
arb_standard_storage_check $expected_type $prim
|
||||
}
|
||||
|
||||
# A one-inch extrusion must extend a face by 25.4 base millimeters through
|
||||
# both the direct libged command and MGED's interactive solid editor.
|
||||
set inch_in_mm 25.4
|
||||
set tolerance 0.001
|
||||
foreach mode {direct interactive} {
|
||||
set suffix "_inch_$mode"
|
||||
in_arb8 extrude $suffix
|
||||
set prim "extrude_arb8${suffix}.s"
|
||||
set base_x [lindex [db get $prim V1] 0]
|
||||
|
||||
units in
|
||||
if {$mode eq "direct"} {
|
||||
if {[catch {edarb extrude $prim 1234 1} result]} {
|
||||
error "inch edarb extrusion failed: $result"
|
||||
}
|
||||
} else {
|
||||
Z
|
||||
e $prim
|
||||
sed $prim
|
||||
if {[catch {extrude 1234 1} result]} {
|
||||
error "inch MGED extrusion failed: $result"
|
||||
}
|
||||
accept
|
||||
Z
|
||||
}
|
||||
units mm
|
||||
|
||||
set new_x [lindex [db get $prim V5] 0]
|
||||
if {abs($new_x - $base_x - $inch_in_mm) > $tolerance} {
|
||||
error "inch $mode extrusion moved V5 by [expr {$new_x - $base_x}] mm"
|
||||
}
|
||||
}
|
||||
|
||||
puts "*** 'extrude' testing completed ***"
|
||||
|
||||
@@ -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) ---"
|
||||
|
||||
@@ -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"
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
Vendored
+1
-1
@@ -752,7 +752,7 @@ tessellate_part(struct creo_conv_info *cinfo, ProMdl model, struct bu_vls **snam
|
||||
}
|
||||
|
||||
/* Check solidity */
|
||||
success = bg_trimesh_solid((int)vert_tree->curr_vert, (size_t)faces.size() / 3,
|
||||
success = !bg_trimesh_solid((int)vert_tree->curr_vert, (size_t)faces.size() / 3,
|
||||
vert_tree->the_array, &faces[0], NULL);
|
||||
|
||||
/* If it's not solid and we're testing solidity, keep trying... */
|
||||
|
||||
@@ -9,7 +9,7 @@ if (TARGET ged-check)
|
||||
endif()
|
||||
brlcad_addexec(gex "gex.cpp" "libbu;librt")
|
||||
brlcad_addexec(glint glint.cpp "librt;${M_LIBRARY}")
|
||||
brlcad_addexec(gqa gqa.c "libged;libanalyze;librt")
|
||||
brlcad_addexec(gqa gqa.c "libged;librt")
|
||||
|
||||
brlcad_addexec(gtransfer gtransfer.c "librt;libpkg" NO_INSTALL)
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
|
||||
// OpenCV header files
|
||||
#include <opencv2/opencv.hpp>
|
||||
#if CV_VERSION_MAJOR >= 5
|
||||
#include <opencv2/geometry.hpp>
|
||||
#endif
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include "opencv2/highgui/highgui.hpp"
|
||||
|
||||
|
||||
+5
-27
@@ -21,6 +21,8 @@
|
||||
*
|
||||
* Perform quantitative analysis checks on geometry.
|
||||
*
|
||||
* XXX need to look at gap computation
|
||||
*
|
||||
* plot the points where overlaps start/stop
|
||||
*
|
||||
* Designed to be a framework for 3d sampling of the geometry volume.
|
||||
@@ -28,7 +30,6 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "analyze/gqa.h"
|
||||
#include "bu/app.h"
|
||||
#include "bu/cmd.h"
|
||||
#include "bu/getopt.h"
|
||||
@@ -42,6 +43,7 @@ main(int argc, char *argv[])
|
||||
int i, j;
|
||||
int db_index;
|
||||
int c;
|
||||
int ret;
|
||||
const char **av;
|
||||
struct ged *gedp;
|
||||
|
||||
@@ -50,30 +52,6 @@ main(int argc, char *argv[])
|
||||
bu_opterr = 0;
|
||||
bu_optind = 1;
|
||||
|
||||
if (argc > 1 && bu_strcmp(argv[1], "--analyze") == 0) {
|
||||
int model_index = analyze_gqa_model_argument(argc,
|
||||
(const char **)argv);
|
||||
if (model_index < 0 || model_index + 1 >= argc)
|
||||
bu_exit(1, "Usage: %s --analyze [options] model object [objects...]\n", argv[0]);
|
||||
av = (const char **)bu_calloc(argc, sizeof(char *), "analysis argv");
|
||||
for (i = j = 0; i < argc; i++) {
|
||||
if (i != model_index)
|
||||
av[j++] = argv[i];
|
||||
}
|
||||
av[0] = "gqa";
|
||||
gedp = ged_open("db", argv[model_index], 1);
|
||||
if (gedp == GED_NULL) {
|
||||
bu_free((void *)av, "analysis argv");
|
||||
bu_exit(1, "Cannot open %s\n", argv[model_index]);
|
||||
}
|
||||
c = 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, "analysis argv");
|
||||
return c == BRLCAD_OK ? 0 : 1;
|
||||
}
|
||||
|
||||
/* Get past command line options. */
|
||||
while ((c = bu_getopt(argc, argv, "A:a:de:f:g:Gn:N:p:P:qrS:t:U:u:vV:W:h?")) != -1) {
|
||||
switch (c) {
|
||||
@@ -126,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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ set(
|
||||
contour.cpp
|
||||
density.cpp
|
||||
globals.c
|
||||
gqa.cpp
|
||||
inside.c
|
||||
mass.c
|
||||
moments.c
|
||||
@@ -43,7 +42,6 @@ set(
|
||||
overlaps.c
|
||||
polygonizer.c
|
||||
raydiff.c
|
||||
sampling.c
|
||||
#find_subtracted_shapes.cpp
|
||||
surf_area.c
|
||||
util.cpp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,108 +0,0 @@
|
||||
/* S A M P L I N G . C
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "analyze/sampling.h"
|
||||
|
||||
/*
|
||||
* Owen recommends at least ten independent randomized-QMC replications and
|
||||
* an ordinary Student-t interval across their estimates. See A. B. Owen,
|
||||
* "Error estimation for quasi-Monte Carlo" (2025), section 5,
|
||||
* https://arxiv.org/abs/2501.00150v3.
|
||||
*
|
||||
* Using t(0.975, 9) for all R >= 10 is conservative relative to the smaller
|
||||
* critical value for R - 1 degrees of freedom and avoids making the public
|
||||
* API depend on a special-functions package.
|
||||
*/
|
||||
#define ANALYZE_STUDENT_T_95_DF9 2.2621571627409915
|
||||
|
||||
int
|
||||
analyze_sampling_interval95(struct analyze_sampling_interval *interval,
|
||||
const double *replicate_estimates, size_t replicate_count)
|
||||
{
|
||||
if (!interval || !replicate_estimates ||
|
||||
replicate_count < ANALYZE_SAMPLING_MIN_REPLICATES)
|
||||
return ANALYZE_ERROR;
|
||||
|
||||
double sum = 0.0;
|
||||
for (size_t i = 0; i < replicate_count; i++) {
|
||||
if (!isfinite(replicate_estimates[i])) return ANALYZE_ERROR;
|
||||
sum += replicate_estimates[i];
|
||||
}
|
||||
|
||||
interval->estimate = sum / (double)replicate_count;
|
||||
double squared_deviation = 0.0;
|
||||
for (size_t i = 0; i < replicate_count; i++) {
|
||||
const double deviation = replicate_estimates[i] - interval->estimate;
|
||||
squared_deviation += deviation * deviation;
|
||||
}
|
||||
const double variance = squared_deviation / (double)(replicate_count - 1);
|
||||
interval->half_width = ANALYZE_STUDENT_T_95_DF9 *
|
||||
sqrt(variance / (double)replicate_count);
|
||||
|
||||
return isfinite(interval->estimate) && isfinite(interval->half_width) ?
|
||||
ANALYZE_OK : ANALYZE_ERROR;
|
||||
}
|
||||
|
||||
int
|
||||
analyze_sampling_ratio_interval95(struct analyze_sampling_interval *interval,
|
||||
const double *replicate_numerators,
|
||||
const double *replicate_denominators, size_t replicate_count)
|
||||
{
|
||||
if (!interval || !replicate_numerators || !replicate_denominators ||
|
||||
replicate_count < ANALYZE_SAMPLING_MIN_REPLICATES)
|
||||
return ANALYZE_ERROR;
|
||||
|
||||
double numerator_sum = 0.0;
|
||||
double denominator_sum = 0.0;
|
||||
for (size_t i = 0; i < replicate_count; i++) {
|
||||
if (!isfinite(replicate_numerators[i]) ||
|
||||
!isfinite(replicate_denominators[i]))
|
||||
return ANALYZE_ERROR;
|
||||
numerator_sum += replicate_numerators[i];
|
||||
denominator_sum += replicate_denominators[i];
|
||||
}
|
||||
const double mean_numerator = numerator_sum / (double)replicate_count;
|
||||
const double mean_denominator = denominator_sum / (double)replicate_count;
|
||||
if (!isfinite(mean_numerator) || !isfinite(mean_denominator) ||
|
||||
fabs(mean_denominator) <= DBL_MIN)
|
||||
return ANALYZE_ERROR;
|
||||
|
||||
interval->estimate = mean_numerator / mean_denominator;
|
||||
double squared_residual = 0.0;
|
||||
for (size_t i = 0; i < replicate_count; i++) {
|
||||
/* Linearization of Xbar/Ybar retains the paired X,Y covariance. */
|
||||
const double residual = (replicate_numerators[i] -
|
||||
interval->estimate * replicate_denominators[i]) / mean_denominator;
|
||||
squared_residual += residual * residual;
|
||||
}
|
||||
const double variance = squared_residual /
|
||||
(double)(replicate_count - 1);
|
||||
interval->half_width = ANALYZE_STUDENT_T_95_DF9 *
|
||||
sqrt(variance / (double)replicate_count);
|
||||
|
||||
return isfinite(interval->estimate) && isfinite(interval->half_width) ?
|
||||
ANALYZE_OK : ANALYZE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
@@ -4,7 +4,6 @@ brlcad_add_test(NAME analyze_contour COMMAND analyze_contour)
|
||||
brlcad_addexec(analyze_raydiff raydiff.c "libanalyze;librt;libbu" TEST)
|
||||
brlcad_addexec(analyze_sp solid_partitions.c "libanalyze;librt;libbu" TEST)
|
||||
brlcad_addexec(analyze_nhit nhit.cpp "libanalyze;librt;libbu" TEST_USESDATA)
|
||||
brlcad_addexec(analyze_sampling sampling_test.c "libanalyze;libbn" TEST)
|
||||
|
||||
#####################################
|
||||
# analyze_densities testing #
|
||||
@@ -13,14 +12,12 @@ brlcad_addexec(analyze_densities density.cpp "libanalyze;libbu" TEST)
|
||||
|
||||
brlcad_add_test(NAME analyze_densities_null COMMAND analyze_densities)
|
||||
brlcad_add_test(NAME analyze_densities_std COMMAND analyze_densities std)
|
||||
brlcad_add_test(NAME analyze_sampling_interval COMMAND analyze_sampling)
|
||||
|
||||
cmakefiles(
|
||||
CMakeLists.txt
|
||||
arbs.g
|
||||
contour.cpp
|
||||
raydiff.g
|
||||
sampling_test.c
|
||||
)
|
||||
|
||||
# Local Variables:
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/* S A M P L I N G _ T E S T . C
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "analyze/gqa.h"
|
||||
#include "analyze/sampling.h"
|
||||
#include "bu/app.h"
|
||||
#include "vmath.h"
|
||||
|
||||
|
||||
int
|
||||
main(int UNUSED(argc), char *argv[])
|
||||
{
|
||||
static const double estimates[ANALYZE_SAMPLING_MIN_REPLICATES] = {
|
||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
|
||||
struct analyze_sampling_interval interval;
|
||||
static const double ratio_numerators[ANALYZE_SAMPLING_MIN_REPLICATES] = {
|
||||
1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0, 81.0, 100.0};
|
||||
static const double zero_denominators[ANALYZE_SAMPLING_MIN_REPLICATES] = {0.0};
|
||||
|
||||
bu_setprogname(argv[0]);
|
||||
|
||||
if (analyze_sampling_interval95(&interval, estimates,
|
||||
ANALYZE_SAMPLING_MIN_REPLICATES) != ANALYZE_OK ||
|
||||
!NEAR_EQUAL(interval.estimate, 5.5, SMALL_FASTF) ||
|
||||
!(interval.half_width > 0.0) || !(interval.half_width < DBL_MAX)) {
|
||||
fprintf(stderr, "valid sampling interval was rejected\n");
|
||||
return 1;
|
||||
}
|
||||
if (analyze_sampling_ratio_interval95(&interval, ratio_numerators,
|
||||
estimates, ANALYZE_SAMPLING_MIN_REPLICATES) != ANALYZE_OK ||
|
||||
!NEAR_EQUAL(interval.estimate, 7.0, SMALL_FASTF) ||
|
||||
!(interval.half_width > 0.0) || !(interval.half_width < DBL_MAX)) {
|
||||
fprintf(stderr, "valid ratio sampling interval was rejected\n");
|
||||
return 1;
|
||||
}
|
||||
if (analyze_sampling_ratio_interval95(&interval, ratio_numerators,
|
||||
zero_denominators, ANALYZE_SAMPLING_MIN_REPLICATES) != ANALYZE_ERROR) {
|
||||
fprintf(stderr, "zero-denominator ratio interval was accepted\n");
|
||||
return 1;
|
||||
}
|
||||
if (analyze_sampling_interval95(&interval, estimates,
|
||||
ANALYZE_SAMPLING_MIN_REPLICATES - 1) != ANALYZE_ERROR) {
|
||||
fprintf(stderr, "undersized replicate set was accepted\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
const char *arguments[] = {
|
||||
"gqa", "--analyze", "--uncertainty", "--rays", "1600",
|
||||
"model.g", "object"};
|
||||
if (analyze_gqa_model_argument(7, arguments) != 5) {
|
||||
fprintf(stderr, "analysis model argument was not located\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
{
|
||||
const char *arguments[] = {
|
||||
"gqa", "--analyze", "--rays", "model.g"};
|
||||
if (analyze_gqa_model_argument(4, arguments) >= 0) {
|
||||
fprintf(stderr, "missing analysis option value was accepted\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* tab-width: 8
|
||||
* mode: C
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "stroustrup"
|
||||
* End:
|
||||
* ex: shiftwidth=4 tabstop=8
|
||||
*/
|
||||
+4
-4
@@ -113,10 +113,10 @@ bg_lseg_clip(fastf_t *xp1, fastf_t *yp1, fastf_t *xp2, fastf_t *yp2, fastf_t cli
|
||||
int
|
||||
bg_ray_vclip(point_t a, point_t b, const fastf_t *min_pt, const fastf_t *max_pt)
|
||||
{
|
||||
static vect_t diff;
|
||||
static double sv;
|
||||
static double st;
|
||||
static double mindist, maxdist;
|
||||
vect_t diff;
|
||||
double sv;
|
||||
double st;
|
||||
double mindist, maxdist;
|
||||
fastf_t *pt = &a[0];
|
||||
fastf_t *dir = &diff[0];
|
||||
int i;
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@
|
||||
/** @file libbg/pca.cpp
|
||||
*
|
||||
* @brief
|
||||
* Principle Component Analysis.
|
||||
* Principal Component Analysis.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -74,7 +74,7 @@ bg_pca(point_t *c, vect_t *xa, vect_t *ya, vect_t *za, size_t npnts, const point
|
||||
}
|
||||
|
||||
// 3. Perform SVD
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeThinU);
|
||||
Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeFullU);
|
||||
|
||||
// 4. Extract the vectors from the U matrix
|
||||
vect_t xaxis, yaxis, zaxis;
|
||||
|
||||
+44
-73
@@ -2620,20 +2620,50 @@ bg_lseg3_lseg3_parallel(const point_t sg1pt1, const point_t sg1pt2,
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
bg_plane_pt_nrml(plane_t *p, point_t pt, vect_t nrml)
|
||||
bg_plane_pt_nrml(plane_t *p, const point_t pt, const vect_t nrml)
|
||||
{
|
||||
if (!p)
|
||||
vect_t normal;
|
||||
fastf_t normal_length;
|
||||
|
||||
if (!p || !pt || !nrml)
|
||||
return -1;
|
||||
vect_t unrml;
|
||||
VMOVE(unrml, nrml);
|
||||
VUNITIZE(nrml);
|
||||
(*p)[0] = unrml[0];
|
||||
(*p)[1] = unrml[1];
|
||||
(*p)[2] = unrml[2];
|
||||
(*p)[3] = unrml[0]*pt[0] + unrml[1]*pt[1] + unrml[2]*pt[2];
|
||||
|
||||
VMOVE(normal, nrml);
|
||||
normal_length = MAGNITUDE(normal);
|
||||
if (normal_length <= SMALL_FASTF)
|
||||
return -1;
|
||||
|
||||
VSCALE(normal, normal, 1.0 / normal_length);
|
||||
(*p)[X] = normal[X];
|
||||
(*p)[Y] = normal[Y];
|
||||
(*p)[Z] = normal[Z];
|
||||
(*p)[H] = VDOT(normal, pt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
plane_coordinate_frame(point_t origin, vect_t xaxis, vect_t yaxis, const plane_t *p)
|
||||
{
|
||||
vect_t normal;
|
||||
fastf_t normal_length;
|
||||
|
||||
VSET(normal, (*p)[X], (*p)[Y], (*p)[Z]);
|
||||
normal_length = MAGNITUDE(normal);
|
||||
if (normal_length <= SMALL_FASTF)
|
||||
return -1;
|
||||
|
||||
VSCALE(normal, normal, 1.0 / normal_length);
|
||||
VSCALE(origin, normal, (*p)[H] / normal_length);
|
||||
bn_vec_perp(xaxis, normal);
|
||||
VUNITIZE(xaxis);
|
||||
VCROSS(yaxis, normal, xaxis);
|
||||
VUNITIZE(yaxis);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Use PCA fit a plane to vertex points
|
||||
extern "C" int
|
||||
bg_fit_plane(point_t *c, vect_t *n, size_t npnts, point_t *pnts)
|
||||
@@ -2664,40 +2694,11 @@ bg_plane_closest_pt(fastf_t *u, fastf_t *v, plane_t *p, point_t *pt)
|
||||
return -1;
|
||||
|
||||
point_t origin;
|
||||
vect_t xaxis, yaxis, zaxis;
|
||||
VSET(zaxis, (*p)[0], (*p)[1], (*p)[2]);
|
||||
fastf_t x, y, z, d;
|
||||
x = fabs((*p)[0]);
|
||||
y = fabs((*p)[1]);
|
||||
z = fabs((*p)[2]);
|
||||
d = 0.0;
|
||||
if ( y >= x && y >= z ) {
|
||||
d = x;
|
||||
x = y;
|
||||
y = d;
|
||||
} else if ( z >= x && z >= y ) {
|
||||
d = x;
|
||||
x = z;
|
||||
z = d;
|
||||
}
|
||||
if ( x > SMALL_FASTF ) {
|
||||
y /= x;
|
||||
z /= x;
|
||||
d = x*sqrt(1.0 + y*y + z*z);
|
||||
} else if ( x > 0.0 && x < MAX_FASTF ) {
|
||||
d = x;
|
||||
}
|
||||
vect_t xaxis, yaxis;
|
||||
|
||||
if (d < 0 || NEAR_ZERO(d, SMALL_FASTF))
|
||||
if (plane_coordinate_frame(origin, xaxis, yaxis, p))
|
||||
return -1;
|
||||
|
||||
VMOVE(origin, zaxis);
|
||||
VSCALE(origin, origin, 1/d*(*p)[3]);
|
||||
VUNITIZE(zaxis);
|
||||
bn_vec_perp(xaxis, zaxis);
|
||||
VUNITIZE(xaxis);
|
||||
VCROSS(yaxis, zaxis, xaxis);
|
||||
VUNITIZE(yaxis);
|
||||
vect_t vc;
|
||||
VSUB2(vc, *pt, origin);
|
||||
|
||||
@@ -2711,45 +2712,15 @@ bg_plane_closest_pt(fastf_t *u, fastf_t *v, plane_t *p, point_t *pt)
|
||||
extern "C" int
|
||||
bg_plane_pt_at(point_t *pt, plane_t *p, fastf_t u, fastf_t v)
|
||||
{
|
||||
if (!pt)
|
||||
if (!pt || !p)
|
||||
return -1;
|
||||
|
||||
point_t origin;
|
||||
vect_t xaxis, yaxis, zaxis;
|
||||
VSET(zaxis, (*p)[0], (*p)[1], (*p)[2]);
|
||||
fastf_t x, y, z, d;
|
||||
x = fabs((*p)[0]);
|
||||
y = fabs((*p)[1]);
|
||||
z = fabs((*p)[2]);
|
||||
d = 0.0;
|
||||
if ( y >= x && y >= z ) {
|
||||
d = x;
|
||||
x = y;
|
||||
y = d;
|
||||
} else if ( z >= x && z >= y ) {
|
||||
d = x;
|
||||
x = z;
|
||||
z = d;
|
||||
}
|
||||
if ( x > SMALL_FASTF ) {
|
||||
y /= x;
|
||||
z /= x;
|
||||
d = x*sqrt(1.0 + y*y + z*z);
|
||||
} else if ( x > 0.0 && x < MAX_FASTF ) {
|
||||
d = x;
|
||||
}
|
||||
vect_t xaxis, yaxis;
|
||||
|
||||
if (d < 0 || NEAR_ZERO(d, SMALL_FASTF))
|
||||
if (plane_coordinate_frame(origin, xaxis, yaxis, p))
|
||||
return -1;
|
||||
|
||||
VMOVE(origin, zaxis);
|
||||
VSCALE(origin, origin, 1/d*(*p)[3]);
|
||||
VUNITIZE(zaxis);
|
||||
bn_vec_perp(xaxis, zaxis);
|
||||
VUNITIZE(xaxis);
|
||||
VCROSS(yaxis, zaxis, xaxis);
|
||||
VUNITIZE(yaxis);
|
||||
|
||||
VSCALE(xaxis, xaxis, u);
|
||||
VSCALE(yaxis, yaxis, v);
|
||||
VADD2(origin, origin, xaxis);
|
||||
|
||||
+51
-72
@@ -83,7 +83,7 @@ bg_polygon_view_bbox(point2d_t *bmin, point2d_t *bmax, struct bg_polygon *p, mat
|
||||
// contours. ONLY considering positive contour points.
|
||||
for (size_t i = 0; i < p->num_contours; i++) {
|
||||
struct bg_poly_contour *c = &p->contour[i];
|
||||
if (!c->num_points)
|
||||
if ((p->hole && p->hole[i]) || !c->num_points)
|
||||
continue;
|
||||
for (size_t j = 0; j < c->num_points; j++) {
|
||||
point_t vpoint;
|
||||
@@ -143,64 +143,45 @@ bg_3d_polygon_area(fastf_t *area, size_t npts, const point_t *pts)
|
||||
int
|
||||
bg_3d_polygon_centroid(point_t *cent, size_t npts, const point_t *pts)
|
||||
{
|
||||
size_t i;
|
||||
fastf_t x_0 = 0.0;
|
||||
fastf_t x_1 = 0.0;
|
||||
fastf_t y_0 = 0.0;
|
||||
fastf_t y_1 = 0.0;
|
||||
fastf_t z_0 = 0.0;
|
||||
fastf_t z_1 = 0.0;
|
||||
fastf_t a = 0.0;
|
||||
fastf_t signedArea = 0.0;
|
||||
point_t centroid = VINIT_ZERO;
|
||||
vect_t normal = VINIT_ZERO;
|
||||
vect_t edge0, edge1, cross;
|
||||
fastf_t signed_area = 0.0;
|
||||
|
||||
if (!pts || !cent || npts < 3)
|
||||
return 1;
|
||||
/* Calculate Centroid projection for face for x-y-plane */
|
||||
for (i = 0; i < npts-1; i++) {
|
||||
x_0 = pts[i][0];
|
||||
y_0 = pts[i][1];
|
||||
x_1 = pts[i+1][0];
|
||||
y_1 = pts[i+1][1];
|
||||
a = x_0 *y_1 - x_1*y_0;
|
||||
signedArea += a;
|
||||
*cent[0] += (x_0 + x_1)*a;
|
||||
*cent[1] += (y_0 + y_1)*a;
|
||||
|
||||
for (size_t i = 1; i + 1 < npts; i++) {
|
||||
VSUB2(edge0, pts[i], pts[0]);
|
||||
VSUB2(edge1, pts[i + 1], pts[0]);
|
||||
VCROSS(cross, edge0, edge1);
|
||||
VADD2(normal, normal, cross);
|
||||
}
|
||||
x_0 = pts[i][0];
|
||||
y_0 = pts[i][1];
|
||||
x_1 = pts[0][0];
|
||||
y_1 = pts[0][1];
|
||||
a = x_0 *y_1 - x_1*y_0;
|
||||
signedArea += a;
|
||||
*cent[0] += (x_0 + x_1)*a;
|
||||
*cent[1] += (y_0 + y_1)*a;
|
||||
if (MAGNITUDE(normal) <= SMALL_FASTF)
|
||||
return 1;
|
||||
VUNITIZE(normal);
|
||||
|
||||
signedArea *= 0.5;
|
||||
*cent[0] /= (6.0*signedArea);
|
||||
*cent[1] /= (6.0*signedArea);
|
||||
for (size_t i = 1; i + 1 < npts; i++) {
|
||||
point_t triangle_centroid;
|
||||
fastf_t triangle_area;
|
||||
|
||||
/* calculate Centroid projection for face for x-z-plane */
|
||||
|
||||
signedArea = 0.0;
|
||||
for (i = 0; i < npts-1; i++) {
|
||||
x_0 = pts[i][0];
|
||||
z_0 = pts[i][2];
|
||||
x_1 = pts[i+1][0];
|
||||
z_1 = pts[i+1][2];
|
||||
a = x_0 *z_1 - x_1*z_0;
|
||||
signedArea += a;
|
||||
*cent[2] += (z_0 + z_1)*a;
|
||||
VSUB2(edge0, pts[i], pts[0]);
|
||||
VSUB2(edge1, pts[i + 1], pts[0]);
|
||||
VCROSS(cross, edge0, edge1);
|
||||
triangle_area = VDOT(cross, normal);
|
||||
signed_area += triangle_area;
|
||||
VADD2(triangle_centroid, pts[0], pts[i]);
|
||||
VADD2(triangle_centroid, triangle_centroid, pts[i + 1]);
|
||||
VSCALE(triangle_centroid, triangle_centroid, triangle_area / 3.0);
|
||||
VADD2(centroid, centroid, triangle_centroid);
|
||||
}
|
||||
x_0 = pts[i][0];
|
||||
z_0 = pts[i][2];
|
||||
x_1 = pts[0][0];
|
||||
z_1 = pts[0][2];
|
||||
a = x_0 *z_1 - x_1*z_0;
|
||||
signedArea += a;
|
||||
*cent[2] += (z_0 + z_1)*a;
|
||||
|
||||
signedArea *= 0.5;
|
||||
*cent[2] /= (6.0*signedArea);
|
||||
if (NEAR_ZERO(signed_area, SMALL_FASTF))
|
||||
return 1;
|
||||
|
||||
VSCALE(centroid, centroid, 1.0 / signed_area);
|
||||
VMOVE(*cent, centroid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -263,6 +244,7 @@ bg_3d_polygon_make_pnts_planes(size_t *npts, point_t **pts, size_t neqs, const p
|
||||
|
||||
|
||||
struct sort_ccw_data {
|
||||
point_t centroid;
|
||||
vect_t x_axis;
|
||||
vect_t y_axis;
|
||||
};
|
||||
@@ -272,20 +254,26 @@ static int
|
||||
sort_ccw_3d(const void *left, const void *right, void *context)
|
||||
{
|
||||
const struct sort_ccw_data *data = (const struct sort_ccw_data *)context;
|
||||
const fastf_t *left_point = (const fastf_t *)left;
|
||||
const fastf_t *right_point = (const fastf_t *)right;
|
||||
double left_angle = atan2(VDOT(left_point, data->y_axis),
|
||||
VDOT(left_point, data->x_axis));
|
||||
double right_angle = atan2(VDOT(right_point, data->y_axis),
|
||||
VDOT(right_point, data->x_axis));
|
||||
const point_t *left_point = (const point_t *)left;
|
||||
const point_t *right_point = (const point_t *)right;
|
||||
vect_t left_offset, right_offset;
|
||||
double left_angle, right_angle;
|
||||
double left_radius, right_radius;
|
||||
|
||||
VSUB2(left_offset, *left_point, data->centroid);
|
||||
VSUB2(right_offset, *right_point, data->centroid);
|
||||
left_angle = atan2(VDOT(left_offset, data->y_axis),
|
||||
VDOT(left_offset, data->x_axis));
|
||||
right_angle = atan2(VDOT(right_offset, data->y_axis),
|
||||
VDOT(right_offset, data->x_axis));
|
||||
|
||||
if (left_angle < right_angle)
|
||||
return -1;
|
||||
if (left_angle > right_angle)
|
||||
return 1;
|
||||
|
||||
double left_radius = MAGSQ(left_point);
|
||||
double right_radius = MAGSQ(right_point);
|
||||
left_radius = MAGSQ(left_offset);
|
||||
right_radius = MAGSQ(right_offset);
|
||||
if (left_radius < right_radius)
|
||||
return -1;
|
||||
if (left_radius > right_radius)
|
||||
@@ -298,11 +286,10 @@ int
|
||||
bg_3d_polygon_sort_ccw(size_t npts, point_t *pts, plane_t cmp)
|
||||
{
|
||||
size_t i;
|
||||
point_t centroid;
|
||||
vect_t normal;
|
||||
struct sort_ccw_data data;
|
||||
|
||||
if (!pts || npts < 3)
|
||||
if (!pts || !cmp || npts < 3)
|
||||
return 1;
|
||||
if (MAGNITUDE(cmp) < VDIVIDE_TOL)
|
||||
return 1;
|
||||
@@ -312,21 +299,13 @@ bg_3d_polygon_sort_ccw(size_t npts, point_t *pts, plane_t cmp)
|
||||
bn_vec_ortho(data.x_axis, normal);
|
||||
VCROSS(data.y_axis, normal, data.x_axis);
|
||||
|
||||
/* Angular ordering is about the polygon center, not the model origin.
|
||||
* Translate temporarily so the comparator only needs the plane axes. */
|
||||
VSETALL(centroid, 0.0);
|
||||
VSETALL(data.centroid, 0.0);
|
||||
for (i = 0; i < npts; i++)
|
||||
VADD2(centroid, centroid, pts[i]);
|
||||
VSCALE(centroid, centroid, 1.0 / (double)npts);
|
||||
|
||||
for (i = 0; i < npts; i++)
|
||||
VSUB2(pts[i], pts[i], centroid);
|
||||
VADD2(data.centroid, data.centroid, pts[i]);
|
||||
VSCALE(data.centroid, data.centroid, 1.0 / (fastf_t)npts);
|
||||
|
||||
bu_sort(pts, npts, sizeof(point_t), sort_ccw_3d, &data);
|
||||
|
||||
for (i = 0; i < npts; i++)
|
||||
VADD2(pts[i], pts[i], centroid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ brlcad_add_test(NAME bg_plane_mk_plane_3pts_succeed COMMAND bg_test pla
|
||||
#
|
||||
# A-D are plane equation parameters, and P is the test point.
|
||||
brlcad_add_test(NAME bg_plane_pt_project_1 COMMAND bg_test plane_pt 6 0,0,1,1 2,2,1)
|
||||
brlcad_add_test(NAME bg_plane_pt_project_2 COMMAND bg_test plane_pt 6 0.737397,0.461098,0.493593,236.3 77.526016919831704,185.674010580054585,189.465011325663767)
|
||||
brlcad_add_test(NAME bg_plane_pt_project_2 COMMAND bg_test plane_pt 6 0.737397,0.461098,0.493593,236.3 77.526016919831704,185.674010580054585,189.465070961146)
|
||||
|
||||
# For function #7 (bg_coplanar_pts), the <args> format is as follows:
|
||||
#
|
||||
@@ -310,7 +310,24 @@ brlcad_addexec(bg_line_distance line_distance.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
brlcad_add_test(NAME bg_line_distance COMMAND bg_line_distance)
|
||||
|
||||
# ************ plane coordinate tests ***********
|
||||
|
||||
brlcad_addexec(bg_plane_coordinates plane_coordinates.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
brlcad_add_test(NAME bg_plane_coordinates COMMAND bg_plane_coordinates)
|
||||
|
||||
# ************ pca.c tests ***********
|
||||
|
||||
brlcad_addexec(bg_pca pca.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
brlcad_add_test(NAME bg_pca COMMAND bg_pca)
|
||||
|
||||
# ************ polygon.c tests ***********
|
||||
|
||||
brlcad_addexec(bg_polygon polygon.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
brlcad_add_test(NAME bg_polygon COMMAND bg_polygon)
|
||||
|
||||
brlcad_addexec(bg_polygon_triangulate polygon_triangulate.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
brlcad_add_test(NAME bg_polygon_triangulate COMMAND bg_polygon_triangulate)
|
||||
@@ -452,6 +469,10 @@ brlcad_addexec(bg_trimesh_area trimesh_area.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
#BRLCAD_ADD_TEST(NAME bg_trimesh_area COMMAND bg_trimesh_area)
|
||||
|
||||
brlcad_addexec(bg_trimesh_solid trimesh_solid.c "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
brlcad_add_test(NAME bg_trimesh_solid COMMAND bg_trimesh_solid)
|
||||
|
||||
# ************ trimesh_repair tests ***********
|
||||
|
||||
brlcad_addexec(bg_trimesh_repair trimesh_repair.cpp "${BG_TEST_LIBS}" TEST)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/* P C A . C
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bn.h"
|
||||
#include "bu.h"
|
||||
#include "bg.h"
|
||||
|
||||
|
||||
static int
|
||||
orthonormal_axes(const vect_t xaxis, const vect_t yaxis, const vect_t zaxis)
|
||||
{
|
||||
return !NEAR_EQUAL(MAGSQ(xaxis), 1.0, BN_TOL_DIST)
|
||||
|| !NEAR_EQUAL(MAGSQ(yaxis), 1.0, BN_TOL_DIST)
|
||||
|| !NEAR_EQUAL(MAGSQ(zaxis), 1.0, BN_TOL_DIST)
|
||||
|| !NEAR_ZERO(VDOT(xaxis, yaxis), BN_TOL_DIST)
|
||||
|| !NEAR_ZERO(VDOT(xaxis, zaxis), BN_TOL_DIST)
|
||||
|| !NEAR_ZERO(VDOT(yaxis, zaxis), BN_TOL_DIST);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const point_t one_point[] = {{1.0, 2.0, 3.0}};
|
||||
const point_t two_points[] = {{1.0, 2.0, 3.0}, {5.0, 2.0, 3.0}};
|
||||
const point_t one_center = {1.0, 2.0, 3.0};
|
||||
const point_t two_center = {3.0, 2.0, 3.0};
|
||||
const vect_t xaxis_expected = {1.0, 0.0, 0.0};
|
||||
point_t center;
|
||||
vect_t xaxis, yaxis, zaxis;
|
||||
fastf_t alignment;
|
||||
int failed = 0;
|
||||
|
||||
bu_setprogname(argv[0]);
|
||||
if (argc != 1)
|
||||
return 1;
|
||||
|
||||
if (bg_pca(¢er, &xaxis, &yaxis, &zaxis, 1, one_point) != BRLCAD_OK ||
|
||||
!VNEAR_EQUAL(center, one_center, BN_TOL_DIST) ||
|
||||
orthonormal_axes(xaxis, yaxis, zaxis)) {
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
if (bg_pca(¢er, &xaxis, &yaxis, &zaxis, 2, two_points) != BRLCAD_OK ||
|
||||
!VNEAR_EQUAL(center, two_center, BN_TOL_DIST) ||
|
||||
orthonormal_axes(xaxis, yaxis, zaxis)) {
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
alignment = VDOT(xaxis, xaxis_expected);
|
||||
if (!NEAR_EQUAL(alignment * alignment, 1.0, BN_TOL_DIST))
|
||||
failed = 1;
|
||||
|
||||
return failed;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* P L A N E _ C O O R D I N A T E S . C
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bn.h"
|
||||
#include "bu.h"
|
||||
#include "bg.h"
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const point_t plane_point = {0.0, 0.0, 3.0};
|
||||
const vect_t original_normal = {0.0, 0.0, 2.0};
|
||||
const vect_t expected_normal = {0.0, 0.0, 1.0};
|
||||
vect_t normal;
|
||||
vect_t zero_normal = VINIT_ZERO;
|
||||
plane_t plane;
|
||||
plane_t unnormalized_plane = {0.0, 0.0, 2.0, 6.0};
|
||||
point_t query = {2.0, 2.0, 3.0};
|
||||
point_t off_plane_query = {2.0, 2.0, 5.0};
|
||||
const point_t expected_projection = {2.0, 2.0, 3.0};
|
||||
point_t reconstructed = VINIT_ZERO;
|
||||
fastf_t u, v;
|
||||
int failed = 0;
|
||||
|
||||
bu_setprogname(argv[0]);
|
||||
if (argc != 1)
|
||||
return 1;
|
||||
|
||||
VMOVE(normal, original_normal);
|
||||
if (bg_plane_pt_nrml(&plane, plane_point, normal) ||
|
||||
!VNEAR_EQUAL(plane, expected_normal, BN_TOL_DIST) ||
|
||||
!NEAR_EQUAL(plane[H], 3.0, BN_TOL_DIST) ||
|
||||
!VNEAR_EQUAL(normal, original_normal, BN_TOL_DIST)) {
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
if (bg_plane_pt_nrml(&plane, plane_point, zero_normal) != -1)
|
||||
failed = 1;
|
||||
|
||||
if (bg_plane_closest_pt(&u, &v, &unnormalized_plane, &query) ||
|
||||
bg_plane_pt_at(&reconstructed, &unnormalized_plane, u, v) ||
|
||||
!VNEAR_EQUAL(reconstructed, query, BN_TOL_DIST)) {
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
if (bg_plane_closest_pt(&u, &v, &unnormalized_plane, &off_plane_query) ||
|
||||
bg_plane_pt_at(&reconstructed, &unnormalized_plane, u, v) ||
|
||||
!VNEAR_EQUAL(reconstructed, expected_projection, BN_TOL_DIST)) {
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
return failed;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/* P O L Y G O N . C
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bn.h"
|
||||
#include "bu.h"
|
||||
#include "bg.h"
|
||||
|
||||
|
||||
static int
|
||||
test_view_bbox(void)
|
||||
{
|
||||
point_t outer[] = {
|
||||
{0.0, 0.0, 0.0},
|
||||
{2.0, 0.0, 0.0},
|
||||
{2.0, 2.0, 0.0},
|
||||
{0.0, 2.0, 0.0}
|
||||
};
|
||||
point_t hole[] = {
|
||||
{100.0, 100.0, 0.0},
|
||||
{101.0, 100.0, 0.0},
|
||||
{101.0, 101.0, 0.0},
|
||||
{100.0, 101.0, 0.0}
|
||||
};
|
||||
struct bg_poly_contour contours[] = {
|
||||
{4, outer, 0},
|
||||
{4, hole, 0}
|
||||
};
|
||||
int holes[] = {0, 1};
|
||||
struct bg_polygon polygon = BG_POLYGON_NULL;
|
||||
mat_t model2view;
|
||||
point2d_t bmin, bmax;
|
||||
|
||||
polygon.num_contours = 2;
|
||||
polygon.hole = holes;
|
||||
polygon.contour = contours;
|
||||
MAT_IDN(model2view);
|
||||
bg_polygon_view_bbox(&bmin, &bmax, &polygon, model2view);
|
||||
|
||||
return !NEAR_EQUAL(bmin[X], 0.0, BN_TOL_DIST)
|
||||
|| !NEAR_EQUAL(bmin[Y], 0.0, BN_TOL_DIST)
|
||||
|| !NEAR_EQUAL(bmax[X], 2.0, BN_TOL_DIST)
|
||||
|| !NEAR_EQUAL(bmax[Y], 2.0, BN_TOL_DIST);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_centroid(void)
|
||||
{
|
||||
const point_t rectangle[] = {
|
||||
{5.0, 0.0, 0.0},
|
||||
{5.0, 4.0, 0.0},
|
||||
{5.0, 4.0, 2.0},
|
||||
{5.0, 0.0, 2.0}
|
||||
};
|
||||
const point_t expected = {5.0, 2.0, 1.0};
|
||||
point_t output[] = {
|
||||
{11.0, 12.0, 13.0},
|
||||
{0.0, 0.0, 0.0},
|
||||
{17.0, 18.0, 19.0},
|
||||
{23.0, 24.0, 25.0}
|
||||
};
|
||||
const point_t before = {11.0, 12.0, 13.0};
|
||||
const point_t after = {17.0, 18.0, 19.0};
|
||||
const point_t farther_after = {23.0, 24.0, 25.0};
|
||||
|
||||
return bg_3d_polygon_centroid(&output[1], 4, rectangle)
|
||||
|| !VNEAR_EQUAL(output[1], expected, BN_TOL_DIST)
|
||||
|| !VNEAR_EQUAL(output[0], before, BN_TOL_DIST)
|
||||
|| !VNEAR_EQUAL(output[2], after, BN_TOL_DIST)
|
||||
|| !VNEAR_EQUAL(output[3], farther_after, BN_TOL_DIST);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_ccw_sort(void)
|
||||
{
|
||||
point_t points[] = {
|
||||
{10.0, 10.0, 0.0},
|
||||
{10.1, 10.1, 0.0},
|
||||
{10.1, 10.0, 0.0},
|
||||
{10.0, 10.1, 0.0}
|
||||
};
|
||||
plane_t plane = {0.0, 0.0, 1.0, 0.0};
|
||||
vect_t total = VINIT_ZERO;
|
||||
vect_t cross;
|
||||
|
||||
if (bg_3d_polygon_sort_ccw(4, points, plane))
|
||||
return 1;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
size_t next = i + 1 == 4 ? 0 : i + 1;
|
||||
VCROSS(cross, points[i], points[next]);
|
||||
VADD2(total, total, cross);
|
||||
}
|
||||
|
||||
return total[Z] <= SMALL_FASTF;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
bu_setprogname(argv[0]);
|
||||
if (argc != 1)
|
||||
return 1;
|
||||
|
||||
return test_view_bbox() || test_centroid() || test_ccw_sort();
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/* T R I M E S H _ S O L I D . C
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "bu.h"
|
||||
#include "bg.h"
|
||||
|
||||
|
||||
static int
|
||||
test_solid_diagnostics(void)
|
||||
{
|
||||
fastf_t vertices[][3] = {
|
||||
{0.0, 0.0, 0.0},
|
||||
{1.0, 0.0, 0.0},
|
||||
{0.0, 1.0, 0.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
{1.0, 1.0, 1.0}
|
||||
};
|
||||
int solid_faces[] = {
|
||||
0, 2, 1,
|
||||
0, 1, 3,
|
||||
0, 3, 2,
|
||||
1, 2, 3
|
||||
};
|
||||
int reversed_faces[] = {
|
||||
0, 1, 2,
|
||||
0, 1, 3,
|
||||
0, 3, 2,
|
||||
1, 2, 3
|
||||
};
|
||||
int mixed_error_faces[] = {
|
||||
0, 1, 2,
|
||||
0, 1, 3,
|
||||
0, 3, 4,
|
||||
1, 2, 3
|
||||
};
|
||||
int *bad_edges = NULL;
|
||||
struct bg_trimesh_solid_errors errors = BG_TRIMESH_SOLID_ERRORS_INIT_NULL;
|
||||
int failed = 0;
|
||||
|
||||
if (bg_trimesh_solid(4, 4, &vertices[0][0], solid_faces, &bad_edges) || bad_edges)
|
||||
failed = 1;
|
||||
|
||||
if (bad_edges) {
|
||||
bu_free(bad_edges, "bad edges");
|
||||
bad_edges = NULL;
|
||||
}
|
||||
|
||||
if (bg_trimesh_solid(4, 4, &vertices[0][0], reversed_faces, &bad_edges) != 1
|
||||
|| !bad_edges) {
|
||||
failed = 1;
|
||||
}
|
||||
|
||||
if (bad_edges)
|
||||
bu_free(bad_edges, "bad edges");
|
||||
|
||||
if (!bg_trimesh_solid2(5, 4, &vertices[0][0], mixed_error_faces, &errors)
|
||||
|| !errors.unmatched.count || !errors.misoriented.count) {
|
||||
failed = 1;
|
||||
}
|
||||
bg_free_trimesh_solid_errors(&errors);
|
||||
|
||||
return failed;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_free_helpers(void)
|
||||
{
|
||||
struct bg_trimesh_edges edges = BG_TRIMESH_EDGES_INIT_NULL;
|
||||
struct bg_trimesh_faces faces = BG_TRIMESH_FACES_INIT_NULL;
|
||||
|
||||
edges.count = 1;
|
||||
edges.edges = (int *)bu_malloc(2 * sizeof(int), "test edges");
|
||||
faces.count = 1;
|
||||
faces.faces = (int *)bu_malloc(sizeof(int), "test faces");
|
||||
bg_free_trimesh_edges(&edges);
|
||||
bg_free_trimesh_faces(&faces);
|
||||
|
||||
return edges.count != 0 || edges.edges || faces.count != 0 || faces.faces;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
bu_setprogname(argv[0]);
|
||||
if (argc != 1)
|
||||
return 1;
|
||||
|
||||
return test_solid_diagnostics() || test_free_helpers();
|
||||
}
|
||||
@@ -88,7 +88,7 @@ intersect_triangle(const point_t origin, const vect_t direction,
|
||||
extern "C" {
|
||||
|
||||
int
|
||||
bg_isect_tri_ray(const point_t orig, const point_t dir,
|
||||
bg_isect_tri_ray(const point_t orig, const vect_t dir,
|
||||
const point_t vert0, const point_t vert1, const point_t vert2,
|
||||
point_t *isect)
|
||||
{
|
||||
|
||||
+41
-21
@@ -285,10 +285,20 @@ bg_trimesh_solid2(int vcnt, int fcnt, fastf_t *v, int *f, struct bg_trimesh_soli
|
||||
|
||||
bad_edge_func = errors ? bg_trimesh_edge_continue : bg_trimesh_edge_exit;
|
||||
|
||||
if ((unmatched_edges = bg_trimesh_unmatched_edges(num_edges, edge_list, bad_edge_func, NULL)) ||
|
||||
(misoriented_edges = bg_trimesh_misoriented_edges(num_edges, edge_list, bad_edge_func, NULL)) ||
|
||||
(excess_edges = bg_trimesh_excess_edges(num_edges, edge_list, bad_edge_func, NULL)))
|
||||
{
|
||||
unmatched_edges = bg_trimesh_unmatched_edges(num_edges, edge_list, bad_edge_func, NULL);
|
||||
if (!errors && unmatched_edges) {
|
||||
bu_free(edge_list, "edge_list");
|
||||
return 1;
|
||||
}
|
||||
|
||||
misoriented_edges = bg_trimesh_misoriented_edges(num_edges, edge_list, bad_edge_func, NULL);
|
||||
if (!errors && misoriented_edges) {
|
||||
bu_free(edge_list, "edge_list");
|
||||
return 1;
|
||||
}
|
||||
|
||||
excess_edges = bg_trimesh_excess_edges(num_edges, edge_list, bad_edge_func, NULL);
|
||||
if (unmatched_edges || misoriented_edges || excess_edges) {
|
||||
if (!errors) {
|
||||
bu_free(edge_list, "edge_list");
|
||||
return 1;
|
||||
@@ -329,6 +339,7 @@ bg_free_trimesh_edges(struct bg_trimesh_edges *edges)
|
||||
if (!edges) return;
|
||||
|
||||
bu_free(edges->edges, "bg trimesh edges");
|
||||
edges->edges = NULL;
|
||||
edges->count = 0;
|
||||
}
|
||||
|
||||
@@ -338,6 +349,7 @@ bg_free_trimesh_faces(struct bg_trimesh_faces *faces)
|
||||
if (!faces) return;
|
||||
|
||||
bu_free(faces->faces, "bg trimesh faces");
|
||||
faces->faces = NULL;
|
||||
faces->count = 0;
|
||||
}
|
||||
|
||||
@@ -355,34 +367,42 @@ bg_free_trimesh_solid_errors(struct bg_trimesh_solid_errors *errors)
|
||||
int
|
||||
bg_trimesh_solid(int vcnt, int fcnt, fastf_t *v, int *f, int **bedges)
|
||||
{
|
||||
int bedge_cnt = 0;
|
||||
int not_solid;
|
||||
|
||||
if (bedges) {
|
||||
int copy_cnt = 0;
|
||||
int copy_count = 0;
|
||||
int edge_count;
|
||||
struct bg_trimesh_solid_errors errors = BG_TRIMESH_SOLID_ERRORS_INIT_NULL;
|
||||
|
||||
bedge_cnt = bg_trimesh_solid2(vcnt, fcnt, v, f, &errors);
|
||||
*bedges = NULL;
|
||||
not_solid = bg_trimesh_solid2(vcnt, fcnt, v, f, &errors);
|
||||
edge_count = errors.unmatched.count + errors.misoriented.count + errors.excess.count;
|
||||
if (edge_count) {
|
||||
*bedges = (int *)bu_calloc(edge_count * 2, sizeof(int), "bad edges");
|
||||
|
||||
*bedges = (int *)bu_calloc(bedge_cnt * 2, sizeof(int), "bad edges");
|
||||
|
||||
if (!errors.unmatched.edges || !errors.unmatched.count)
|
||||
return 0;
|
||||
|
||||
memcpy(*bedges, errors.unmatched.edges, errors.unmatched.count * 2 * sizeof(int));
|
||||
copy_cnt += errors.unmatched.count * 2;
|
||||
|
||||
memcpy(*bedges + copy_cnt, errors.misoriented.edges, errors.misoriented.count * 2 * sizeof(int));
|
||||
copy_cnt += errors.misoriented.count * 2;
|
||||
|
||||
memcpy(*bedges + copy_cnt, errors.excess.edges, errors.excess.count * 2 * sizeof(int));
|
||||
if (errors.unmatched.count) {
|
||||
memcpy(*bedges + copy_count, errors.unmatched.edges,
|
||||
errors.unmatched.count * 2 * sizeof(int));
|
||||
copy_count += errors.unmatched.count * 2;
|
||||
}
|
||||
if (errors.misoriented.count) {
|
||||
memcpy(*bedges + copy_count, errors.misoriented.edges,
|
||||
errors.misoriented.count * 2 * sizeof(int));
|
||||
copy_count += errors.misoriented.count * 2;
|
||||
}
|
||||
if (errors.excess.count) {
|
||||
memcpy(*bedges + copy_count, errors.excess.edges,
|
||||
errors.excess.count * 2 * sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
bg_free_trimesh_solid_errors(&errors);
|
||||
} else {
|
||||
bedge_cnt = bg_trimesh_solid2(vcnt, fcnt, v, f, NULL);
|
||||
not_solid = bg_trimesh_solid2(vcnt, fcnt, v, f, NULL);
|
||||
}
|
||||
|
||||
/* returns true when not solid */
|
||||
return (bedge_cnt > 0) ? 1 : 0;
|
||||
return not_solid ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
+38
-98
@@ -662,87 +662,6 @@ bn_vec_perp(vect_t new_vec, const vect_t old_vec)
|
||||
VCROSS(new_vec, another_vec, old_vec);
|
||||
}
|
||||
|
||||
static int
|
||||
bn_lseg3_lseg3_parallel(const point_t sg1pt1, const point_t sg1pt2,
|
||||
const point_t sg2pt1, const point_t sg2pt2,
|
||||
const struct bn_tol *tol)
|
||||
{
|
||||
vect_t e_dif[2] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
|
||||
vect_t e_dif_a[2] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
|
||||
fastf_t e_rr[2][3] = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
|
||||
char e_sc[2][3] = {{0, 0, 0}, {0, 0, 0}};
|
||||
fastf_t dist = tol->dist;
|
||||
fastf_t tmp;
|
||||
int i, j;
|
||||
|
||||
VSUB2(e_dif[0], sg1pt2, sg1pt1);
|
||||
VSUB2(e_dif[1], sg2pt2, sg2pt1);
|
||||
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
for ( j = 0 ; j < 3 ; j++ ) {
|
||||
e_dif_a[i][j] = fabs(e_dif[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0 ; i < 2 ; i++ ) {
|
||||
if ((e_dif_a[i][X] < dist) && (e_dif_a[i][Y] > dist)) {
|
||||
e_sc[i][0] = 1;
|
||||
} else if ((e_dif_a[i][X] > dist) && (e_dif_a[i][Y] < dist)) {
|
||||
e_sc[i][0] = 2;
|
||||
} else if ((e_dif_a[i][X] < dist) && (e_dif_a[i][Y] < dist)) {
|
||||
e_sc[i][0] = 3;
|
||||
} else {
|
||||
e_rr[i][0] = e_dif[i][Y] / e_dif[i][X];
|
||||
e_sc[i][0] = 0;
|
||||
}
|
||||
if ((e_dif_a[i][X] < dist) && (e_dif_a[i][Z] > dist)) {
|
||||
e_sc[i][1] = 1;
|
||||
} else if ((e_dif_a[i][X] > dist) && (e_dif_a[i][Z] < dist)) {
|
||||
e_sc[i][1] = 2;
|
||||
} else if ((e_dif_a[i][X] < dist) && (e_dif_a[i][Z] < dist)) {
|
||||
e_sc[i][1] = 3;
|
||||
} else {
|
||||
e_rr[i][1] = e_dif[i][Z] / e_dif[i][X];
|
||||
e_sc[i][1] = 0;
|
||||
}
|
||||
if ((e_dif_a[i][X] < dist) && (e_dif_a[i][Z] > dist)) {
|
||||
e_sc[i][1] = 1;
|
||||
} else if ((e_dif_a[i][X] > dist) && (e_dif_a[i][Z] < dist)) {
|
||||
e_sc[i][1] = 2;
|
||||
} else if ((e_dif_a[i][X] < dist) && (e_dif_a[i][Z] < dist)) {
|
||||
e_sc[i][1] = 3;
|
||||
} else {
|
||||
e_rr[i][1] = e_dif[i][Z] / e_dif[i][X];
|
||||
e_sc[i][1] = 0;
|
||||
}
|
||||
if ((e_dif_a[i][Y] < dist) && (e_dif_a[i][Z] > dist)) {
|
||||
e_sc[i][2] = 1;
|
||||
} else if ((e_dif_a[i][Y] > dist) && (e_dif_a[i][Z] < dist)) {
|
||||
e_sc[i][2] = 2;
|
||||
} else if ((e_dif_a[i][Y] < dist) && (e_dif_a[i][Z] < dist)) {
|
||||
e_sc[i][2] = 3;
|
||||
} else {
|
||||
e_rr[i][2] = e_dif[i][Z] / e_dif[i][Y];
|
||||
e_sc[i][2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* loop thru (rise/run) ratios from xy, xz and yz planes */
|
||||
for ( i = 0 ; i < 3 ; i++ ) {
|
||||
if (e_sc[0][i] != e_sc[1][i]) {
|
||||
return 0;
|
||||
}
|
||||
if (e_sc[0][i] == 0) {
|
||||
tmp = e_rr[0][i] - e_rr[1][i];
|
||||
if (fabs(tmp) > dist) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
bn_mat_fromto(
|
||||
mat_t m,
|
||||
@@ -753,7 +672,6 @@ bn_mat_fromto(
|
||||
vect_t test_to;
|
||||
vect_t unit_from, unit_to;
|
||||
fastf_t dot;
|
||||
point_t origin = VINIT_ZERO;
|
||||
|
||||
/**
|
||||
* The method used here is from Graphics Gems, A. Glassner, ed.
|
||||
@@ -767,18 +685,24 @@ bn_mat_fromto(
|
||||
vect_t N, M;
|
||||
vect_t w_prime; /* image of "to" ("w") in Qt */
|
||||
|
||||
BN_CK_TOL(tol);
|
||||
|
||||
VMOVE(unit_from, from);
|
||||
VUNITIZE(unit_from); /* aka "v" */
|
||||
VMOVE(unit_to, to);
|
||||
VUNITIZE(unit_to); /* aka "w" */
|
||||
|
||||
/* If from and to are the same or opposite, special handling is
|
||||
* needed, because the cross product isn't defined. asin(0.00001)
|
||||
* = 0.0005729 degrees (1/2000 degree)
|
||||
if (VZERO(unit_from) || VZERO(unit_to)) {
|
||||
MAT_IDN(m);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Same and opposite directions need special handling because their
|
||||
* cross product does not define a rotation axis.
|
||||
*/
|
||||
if (bn_lseg3_lseg3_parallel(origin, from, origin, to, tol)) {
|
||||
dot = VDOT(from, to);
|
||||
if (dot > SMALL_FASTF) {
|
||||
dot = VDOT(unit_from, unit_to);
|
||||
if (BN_VECT_ARE_PARALLEL(dot, tol)) {
|
||||
if (dot > 0.0) {
|
||||
MAT_IDN(m);
|
||||
return;
|
||||
} else {
|
||||
@@ -812,7 +736,7 @@ bn_mat_fromto(
|
||||
|
||||
/* Verify that it worked */
|
||||
MAT4X3VEC(test_to, m, unit_from);
|
||||
if (UNLIKELY(!bn_lseg3_lseg3_parallel(origin, unit_to, origin, test_to, tol))) {
|
||||
if (UNLIKELY(!BN_VECT_ARE_PARALLEL(VDOT(unit_to, test_to), tol))) {
|
||||
dot = VDOT(unit_to, test_to);
|
||||
bu_log("bn_mat_fromto() ERROR! from (%g, %g, %g) to (%g, %g, %g) went to (%g, %g, %g), dot=%g?\n",
|
||||
V3ARGS(from), V3ARGS(to), V3ARGS(test_to), dot);
|
||||
@@ -899,16 +823,32 @@ bn_mat_lookat(mat_t rot, const vect_t dir, int yflip)
|
||||
vect_t x;
|
||||
vect_t z;
|
||||
vect_t t1;
|
||||
vect_t unit_dir;
|
||||
fastf_t hypot_xy;
|
||||
vect_t xproj;
|
||||
vect_t zproj;
|
||||
|
||||
VMOVE(unit_dir, dir);
|
||||
VUNITIZE(unit_dir);
|
||||
if (VZERO(unit_dir)) {
|
||||
MAT_IDN(rot);
|
||||
return;
|
||||
}
|
||||
|
||||
/* First, rotate D around Z axis to match +X axis (azimuth) */
|
||||
hypot_xy = hypot(dir[X], dir[Y]);
|
||||
bn_mat_zrot(first, -dir[Y] / hypot_xy, dir[X] / hypot_xy);
|
||||
hypot_xy = hypot(unit_dir[X], unit_dir[Y]);
|
||||
if (hypot_xy < VDIVIDE_TOL) {
|
||||
if (unit_dir[Z] < 0.0) {
|
||||
MAT_IDN(rot);
|
||||
} else {
|
||||
bn_mat_yrot(rot, 0.0, -1.0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bn_mat_zrot(first, -unit_dir[Y] / hypot_xy, unit_dir[X] / hypot_xy);
|
||||
|
||||
/* Next, rotate D around Y axis to match -Z axis (elevation) */
|
||||
bn_mat_yrot(second, -hypot_xy, -dir[Z]);
|
||||
bn_mat_yrot(second, -hypot_xy, -unit_dir[Z]);
|
||||
bn_mat_mul(prod12, second, first);
|
||||
|
||||
/* Produce twist correction, by re-orienting projection of X axis */
|
||||
@@ -937,7 +877,7 @@ bn_mat_lookat(mat_t rot, const vect_t dir, int yflip)
|
||||
}
|
||||
|
||||
/* Check the final results */
|
||||
MAT4X3VEC(t1, rot, dir);
|
||||
MAT4X3VEC(t1, rot, unit_dir);
|
||||
if (t1[Z] > -0.98) {
|
||||
bu_log("Error: bn_mat_lookat final= (%g, %g, %g)\n", V3ARGS(t1));
|
||||
}
|
||||
@@ -1277,10 +1217,10 @@ bn_mat_is_non_unif(const mat_t m)
|
||||
void
|
||||
bn_wrt_point_direc(mat_t out, const mat_t change, const mat_t in, const point_t point, const vect_t direc, const struct bn_tol *tol)
|
||||
{
|
||||
static mat_t t1;
|
||||
static mat_t pt_to_origin, origin_to_pt;
|
||||
static mat_t d_to_zaxis, zaxis_to_d;
|
||||
static vect_t zaxis;
|
||||
mat_t t1;
|
||||
mat_t pt_to_origin, origin_to_pt;
|
||||
mat_t d_to_zaxis, zaxis_to_d;
|
||||
vect_t zaxis;
|
||||
|
||||
/* build "point to origin" matrix */
|
||||
MAT_IDN(pt_to_origin);
|
||||
@@ -1288,7 +1228,7 @@ bn_wrt_point_direc(mat_t out, const mat_t change, const mat_t in, const point_t
|
||||
|
||||
/* build "origin to point" matrix */
|
||||
MAT_IDN(origin_to_pt);
|
||||
MAT_DELTAS_VEC_NEG(origin_to_pt, point);
|
||||
MAT_DELTAS_VEC(origin_to_pt, point);
|
||||
|
||||
/* build "direc to zaxis" matrix */
|
||||
VSET(zaxis, 0.0, 0.0, 1.0);
|
||||
|
||||
+86
-43
@@ -28,21 +28,11 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "bu/interrupt.h"
|
||||
#include "bu/malloc.h"
|
||||
#include "vmath.h"
|
||||
#include "bn/multipoly.h"
|
||||
|
||||
|
||||
#define FAILSTR "failure in multipoly.c"
|
||||
/* the Max and Min macros in vmath.h are destructive and not suited for here, should this be
|
||||
* added somewhere else? */
|
||||
#define Max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define Min(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
/**
|
||||
* bn_multipoly_new
|
||||
*
|
||||
@@ -52,25 +42,41 @@ struct bn_multipoly *
|
||||
bn_multipoly_new(int dgrs, int dgrt)
|
||||
{
|
||||
struct bn_multipoly *newmp;
|
||||
int i, s, t;
|
||||
int i;
|
||||
|
||||
if (dgrs <= 0 || dgrt <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BU_ALLOC(newmp, struct bn_multipoly);
|
||||
newmp->cf = (double **)bu_malloc(dgrs * sizeof(double *), FAILSTR);
|
||||
newmp->cf = (double **)bu_calloc(dgrs, sizeof(double *), FAILSTR);
|
||||
|
||||
for (i = 0; i < dgrs; i++) {
|
||||
newmp->cf[i] = (double *)bu_malloc(dgrt * sizeof(double), FAILSTR);
|
||||
newmp->cf[i] = (double *)bu_calloc(dgrt, sizeof(double), FAILSTR);
|
||||
}
|
||||
|
||||
newmp->dgrs = dgrs;
|
||||
newmp->dgrt = dgrt;
|
||||
for (s = 0; s < dgrs; s++) {
|
||||
for (t = 0; t < dgrt; t++) {
|
||||
newmp->cf[s][t] = 0;
|
||||
}
|
||||
}
|
||||
return newmp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bn_multipoly_free(struct bn_multipoly *poly)
|
||||
{
|
||||
int s;
|
||||
|
||||
if (!poly) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (s = 0; s < poly->dgrs; s++) {
|
||||
bu_free(poly->cf[s], "bn_multipoly coefficients");
|
||||
}
|
||||
bu_free(poly->cf, "bn_multipoly coefficient rows");
|
||||
bu_free(poly, "bn_multipoly");
|
||||
}
|
||||
|
||||
/**
|
||||
* bn_multipoly_grow
|
||||
*
|
||||
@@ -79,24 +85,35 @@ bn_multipoly_new(int dgrs, int dgrt)
|
||||
struct bn_multipoly *
|
||||
bn_multipoly_grow(register struct bn_multipoly *P, int dgrs, int dgrt)
|
||||
{
|
||||
int i, j;
|
||||
if (dgrs > P->dgrs) {
|
||||
P->cf = (double **)bu_realloc(P->cf, dgrs * sizeof(double *), FAILSTR);
|
||||
for (i = P->dgrs; i < dgrs; i++) {
|
||||
P->cf[i] = (double *)bu_malloc(Max(P->dgrt, dgrt) * sizeof(double), FAILSTR);
|
||||
for (j = 0; j < Max(P->dgrt, dgrt); j++) {
|
||||
P->cf[i][j] = 0;
|
||||
}
|
||||
}
|
||||
double **coefficients;
|
||||
int new_dgrs, new_dgrt;
|
||||
int s, t;
|
||||
|
||||
if (!P) {
|
||||
return NULL;
|
||||
}
|
||||
if (dgrt > P->dgrt) {
|
||||
for (i = 0; i < P->dgrt; i++) {
|
||||
P->cf[i] = (double *)bu_realloc(P->cf, dgrt * sizeof(double *), FAILSTR);
|
||||
for (j = P->dgrt; j < dgrt; j++) {
|
||||
P->cf[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
new_dgrs = (dgrs > P->dgrs) ? dgrs : P->dgrs;
|
||||
new_dgrt = (dgrt > P->dgrt) ? dgrt : P->dgrt;
|
||||
if (new_dgrs == P->dgrs && new_dgrt == P->dgrt) {
|
||||
return P;
|
||||
}
|
||||
|
||||
coefficients = (double **)bu_calloc(new_dgrs, sizeof(double *), FAILSTR);
|
||||
for (s = 0; s < new_dgrs; s++) {
|
||||
coefficients[s] = (double *)bu_calloc(new_dgrt, sizeof(double), FAILSTR);
|
||||
}
|
||||
for (s = 0; s < P->dgrs; s++) {
|
||||
for (t = 0; t < P->dgrt; t++) {
|
||||
coefficients[s][t] = P->cf[s][t];
|
||||
}
|
||||
bu_free(P->cf[s], "bn_multipoly coefficients");
|
||||
}
|
||||
bu_free(P->cf, "bn_multipoly coefficient rows");
|
||||
|
||||
P->cf = coefficients;
|
||||
P->dgrs = new_dgrs;
|
||||
P->dgrt = new_dgrt;
|
||||
return P;
|
||||
}
|
||||
|
||||
@@ -108,7 +125,12 @@ bn_multipoly_grow(register struct bn_multipoly *P, int dgrs, int dgrt)
|
||||
struct bn_multipoly *
|
||||
bn_multipoly_set(register struct bn_multipoly *P, int s, int t, double val)
|
||||
{
|
||||
bn_multipoly_grow(P, s + 1, t + 1);
|
||||
if (!P || s < 0 || t < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (!bn_multipoly_grow(P, s + 1, t + 1)) {
|
||||
return NULL;
|
||||
}
|
||||
P->cf[s][t] = val;
|
||||
return P;
|
||||
}
|
||||
@@ -119,13 +141,26 @@ bn_multipoly_set(register struct bn_multipoly *P, int s, int t, double val)
|
||||
* @brief add two polynomials
|
||||
*/
|
||||
struct bn_multipoly *
|
||||
bn_multipoly_add(register struct bn_multipoly *p1, register struct bn_multipoly *p2)
|
||||
bn_multipoly_add(register const struct bn_multipoly *p1, register const struct bn_multipoly *p2)
|
||||
{
|
||||
struct bn_multipoly *sum = bn_multipoly_new(Max(p1->dgrs, p2->dgrs), Max(p1->dgrt, p2->dgrs));
|
||||
struct bn_multipoly *sum;
|
||||
int s, t;
|
||||
|
||||
if (!p1 || !p2 || p1->dgrs <= 0 || p1->dgrt <= 0 ||
|
||||
p2->dgrs <= 0 || p2->dgrt <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sum = bn_multipoly_new((p1->dgrs > p2->dgrs) ? p1->dgrs : p2->dgrs,
|
||||
(p1->dgrt > p2->dgrt) ? p1->dgrt : p2->dgrt);
|
||||
for (s = 0; s < sum->dgrs; s++) {
|
||||
for (t = 0; t < sum->dgrt; t++) {
|
||||
sum->cf[s][t] = p1->cf[s][t] + p2->cf[s][t];
|
||||
if (s < p1->dgrs && t < p1->dgrt) {
|
||||
sum->cf[s][t] += p1->cf[s][t];
|
||||
}
|
||||
if (s < p2->dgrs && t < p2->dgrt) {
|
||||
sum->cf[s][t] += p2->cf[s][t];
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
@@ -138,15 +173,23 @@ bn_multipoly_add(register struct bn_multipoly *p1, register struct bn_multipoly
|
||||
*/
|
||||
|
||||
struct bn_multipoly *
|
||||
bn_multipoly_mul(register struct bn_multipoly *p1, register struct bn_multipoly *p2)
|
||||
bn_multipoly_mul(register const struct bn_multipoly *p1, register const struct bn_multipoly *p2)
|
||||
{
|
||||
struct bn_multipoly *product = bn_multipoly_new(p1->dgrs + p2->dgrs, p1->dgrt + p2->dgrt);
|
||||
int s1,s2,t1,t2;
|
||||
struct bn_multipoly *product;
|
||||
int s1, s2, t1, t2;
|
||||
|
||||
if (!p1 || !p2 || p1->dgrs <= 0 || p1->dgrt <= 0 ||
|
||||
p2->dgrs <= 0 || p2->dgrt <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
product = bn_multipoly_new(p1->dgrs + p2->dgrs - 1,
|
||||
p1->dgrt + p2->dgrt - 1);
|
||||
for (s1 = 0; s1 < p1->dgrs; s1++) {
|
||||
for (t1 = 0; t1 < p1->dgrt; t1++) {
|
||||
for (s2 = 0; s2 < p2->dgrs; s2++) {
|
||||
for (t2 = 0; t2 < p2->dgrt; p2++) {
|
||||
product->cf[s1 + s2][t1 + t2] = p1->cf[s1][t1] * p2->cf[s2][t2];
|
||||
for (t2 = 0; t2 < p2->dgrt; t2++) {
|
||||
product->cf[s1 + s2][t1 + t2] += p1->cf[s1][t1] * p2->cf[s2][t2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+64
-39
@@ -28,7 +28,6 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdlib.h> /* for abs */
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
@@ -49,7 +48,24 @@ static const struct bn_poly bn_Zero_poly = { BN_POLY_MAGIC, 0, {0.0} };
|
||||
struct bn_poly *
|
||||
bn_poly_mul(register struct bn_poly *product, register const struct bn_poly *m1, register const struct bn_poly *m2)
|
||||
{
|
||||
if (m1->dgr > BN_MAX_POLY_DEGREE ||
|
||||
m2->dgr > BN_MAX_POLY_DEGREE ||
|
||||
m1->dgr > BN_MAX_POLY_DEGREE - m2->dgr) {
|
||||
return BN_POLY_NULL;
|
||||
}
|
||||
|
||||
if (product == m1 || product == m2) {
|
||||
struct bn_poly result;
|
||||
|
||||
if (!bn_poly_mul(&result, m1, m2)) {
|
||||
return BN_POLY_NULL;
|
||||
}
|
||||
*product = result;
|
||||
return product;
|
||||
}
|
||||
|
||||
if (m1->dgr == 1 && m2->dgr == 1) {
|
||||
product->magic = BN_POLY_MAGIC;
|
||||
product->dgr = 2;
|
||||
product->cf[0] = m1->cf[0] * m2->cf[0];
|
||||
product->cf[1] = m1->cf[0] * m2->cf[1] +
|
||||
@@ -58,6 +74,7 @@ bn_poly_mul(register struct bn_poly *product, register const struct bn_poly *m1,
|
||||
return product;
|
||||
}
|
||||
if (m1->dgr == 2 && m2->dgr == 2) {
|
||||
product->magic = BN_POLY_MAGIC;
|
||||
product->dgr = 4;
|
||||
product->cf[0] = m1->cf[0] * m2->cf[0];
|
||||
product->cf[1] = m1->cf[0] * m2->cf[1] +
|
||||
@@ -77,12 +94,7 @@ bn_poly_mul(register struct bn_poly *product, register const struct bn_poly *m1,
|
||||
|
||||
*product = bn_Zero_poly;
|
||||
|
||||
/* If the degree of the product will be larger than the
|
||||
* maximum size allowed in "polyno.h", then return a null
|
||||
* pointer to indicate failure.
|
||||
*/
|
||||
if ((product->dgr = m1->dgr + m2->dgr) > BN_MAX_POLY_DEGREE)
|
||||
return BN_POLY_NULL;
|
||||
product->dgr = m1->dgr + m2->dgr;
|
||||
|
||||
for (ct1=0; ct1 <= m1->dgr; ++ct1) {
|
||||
for (ct2=0; ct2 <= m2->dgr; ++ct2) {
|
||||
@@ -110,28 +122,26 @@ bn_poly_scale(register struct bn_poly *eqn, double factor)
|
||||
struct bn_poly *
|
||||
bn_poly_add(register struct bn_poly *sum, register const struct bn_poly *poly1, register const struct bn_poly *poly2)
|
||||
{
|
||||
struct bn_poly tmp;
|
||||
struct bn_poly result;
|
||||
register size_t i, offset;
|
||||
|
||||
offset = labs((long)poly1->dgr - (long)poly2->dgr);
|
||||
|
||||
tmp = bn_Zero_poly;
|
||||
offset = (poly1->dgr >= poly2->dgr) ?
|
||||
poly1->dgr - poly2->dgr : poly2->dgr - poly1->dgr;
|
||||
|
||||
if (poly1->dgr >= poly2->dgr) {
|
||||
*sum = *poly1;
|
||||
result = *poly1;
|
||||
for (i=0; i <= poly2->dgr; ++i) {
|
||||
tmp.cf[i+offset] = poly2->cf[i];
|
||||
result.cf[i+offset] += poly2->cf[i];
|
||||
}
|
||||
} else {
|
||||
*sum = *poly2;
|
||||
result = *poly2;
|
||||
for (i=0; i <= poly1->dgr; ++i) {
|
||||
tmp.cf[i+offset] = poly1->cf[i];
|
||||
result.cf[i+offset] += poly1->cf[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i <= sum->dgr; ++i) {
|
||||
sum->cf[i] += tmp.cf[i];
|
||||
}
|
||||
result.magic = BN_POLY_MAGIC;
|
||||
*sum = result;
|
||||
return sum;
|
||||
}
|
||||
|
||||
@@ -139,30 +149,30 @@ bn_poly_add(register struct bn_poly *sum, register const struct bn_poly *poly1,
|
||||
struct bn_poly *
|
||||
bn_poly_sub(register struct bn_poly *diff, register const struct bn_poly *poly1, register const struct bn_poly *poly2)
|
||||
{
|
||||
struct bn_poly tmp;
|
||||
struct bn_poly result;
|
||||
register size_t i, offset;
|
||||
|
||||
offset = labs((long)poly1->dgr - (long)poly2->dgr);
|
||||
|
||||
*diff = bn_Zero_poly;
|
||||
tmp = bn_Zero_poly;
|
||||
offset = (poly1->dgr >= poly2->dgr) ?
|
||||
poly1->dgr - poly2->dgr : poly2->dgr - poly1->dgr;
|
||||
|
||||
if (poly1->dgr >= poly2->dgr) {
|
||||
*diff = *poly1;
|
||||
result = *poly1;
|
||||
for (i=0; i <= poly2->dgr; ++i) {
|
||||
tmp.cf[i+offset] = poly2->cf[i];
|
||||
result.cf[i+offset] -= poly2->cf[i];
|
||||
}
|
||||
} else {
|
||||
diff->dgr = poly2->dgr;
|
||||
result = bn_Zero_poly;
|
||||
result.dgr = poly2->dgr;
|
||||
for (i=0; i <= poly1->dgr; ++i) {
|
||||
diff->cf[i+offset] = poly1->cf[i];
|
||||
result.cf[i+offset] = poly1->cf[i];
|
||||
}
|
||||
for (i=0; i <= poly2->dgr; ++i) {
|
||||
result.cf[i] -= poly2->cf[i];
|
||||
}
|
||||
tmp = *poly2;
|
||||
}
|
||||
|
||||
for (i=0; i <= diff->dgr; ++i) {
|
||||
diff->cf[i] -= tmp.cf[i];
|
||||
}
|
||||
result.magic = BN_POLY_MAGIC;
|
||||
*diff = result;
|
||||
return diff;
|
||||
}
|
||||
|
||||
@@ -177,12 +187,20 @@ bn_poly_synthetic_division(struct bn_poly *quo, struct bn_poly *rem, const struc
|
||||
*rem = bn_Zero_poly; /* struct copy */
|
||||
|
||||
if (dvsor->dgr > dvdend->dgr) {
|
||||
quo->dgr = -1;
|
||||
} else {
|
||||
quo->dgr = dvdend->dgr - dvsor->dgr;
|
||||
*rem = *dvdend;
|
||||
*quo = bn_Zero_poly;
|
||||
return;
|
||||
}
|
||||
if ((rem->dgr = dvsor->dgr - 1) > dvdend->dgr)
|
||||
rem->dgr = dvdend->dgr;
|
||||
|
||||
if (dvsor->dgr == 0) {
|
||||
for (n=0; n <= quo->dgr; ++n) {
|
||||
quo->cf[n] /= dvsor->cf[0];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
quo->dgr = dvdend->dgr - dvsor->dgr;
|
||||
rem->dgr = dvsor->dgr - 1;
|
||||
|
||||
for (n=0; n <= quo->dgr; ++n) {
|
||||
quo->cf[n] /= dvsor->cf[0];
|
||||
@@ -200,10 +218,11 @@ bn_poly_synthetic_division(struct bn_poly *quo, struct bn_poly *rem, const struc
|
||||
int
|
||||
bn_poly_quadratic_roots(register struct bn_complex *roots, register const struct bn_poly *quadrat)
|
||||
{
|
||||
struct bn_poly monic;
|
||||
fastf_t discrim, denom, rad;
|
||||
const fastf_t small = SMALL_FASTF;
|
||||
|
||||
if (NEAR_ZERO(quadrat->cf[0], small)) {
|
||||
if (ZERO(quadrat->cf[0]) && fpclassify(quadrat->cf[0]) == FP_ZERO) {
|
||||
/* root = -cf[2] / cf[1] */
|
||||
if (NEAR_ZERO(quadrat->cf[1], small)) {
|
||||
/* No solution. Now what? */
|
||||
@@ -216,6 +235,12 @@ bn_poly_quadratic_roots(register struct bn_complex *roots, register const struct
|
||||
return 1; /* OK - repeated root */
|
||||
}
|
||||
|
||||
if (!NEAR_EQUAL(quadrat->cf[0], 1.0, small)) {
|
||||
monic = *quadrat;
|
||||
bn_poly_scale(&monic, 1.0 / monic.cf[0]);
|
||||
quadrat = &monic;
|
||||
}
|
||||
|
||||
discrim = quadrat->cf[1]*quadrat->cf[1] - 4.0* quadrat->cf[0]*quadrat->cf[2];
|
||||
denom = 0.5 / quadrat->cf[0];
|
||||
|
||||
@@ -264,7 +289,7 @@ bn_poly_cubic_roots(register struct bn_complex *roots, register const struct bn_
|
||||
fastf_t a, b, c1, c1_3rd, delta;
|
||||
register int i;
|
||||
|
||||
if (ZERO(eqn->cf[0])) return 0;
|
||||
if (ZERO(eqn->cf[0]) && fpclassify(eqn->cf[0]) == FP_ZERO) return 0;
|
||||
|
||||
/* Cardano's formula below is expressed for a monic polynomial. */
|
||||
if (!NEAR_EQUAL(eqn->cf[0], 1.0, SMALL_FASTF)) {
|
||||
@@ -358,7 +383,7 @@ bn_poly_quartic_roots(register struct bn_complex *roots, register const struct b
|
||||
|
||||
#define Max3(a, b, c) ((c)>((a)>(b)?(a):(b)) ? (c) : ((a)>(b)?(a):(b)))
|
||||
|
||||
if (ZERO(eqn->cf[0])) return 0;
|
||||
if (ZERO(eqn->cf[0]) && fpclassify(eqn->cf[0]) == FP_ZERO) return 0;
|
||||
|
||||
/* Ferrari's formula below is expressed for a monic polynomial. */
|
||||
if (!NEAR_EQUAL(eqn->cf[0], 1.0, SMALL_FASTF)) {
|
||||
|
||||
+12
-2
@@ -174,7 +174,14 @@ quat_double(quat_t qout, const quat_t q1, const quat_t q2)
|
||||
void
|
||||
quat_bisect(quat_t qout, const quat_t q1, const quat_t q2)
|
||||
{
|
||||
QADD2( qout, q1, q2 );
|
||||
quat_t qsum;
|
||||
|
||||
QADD2(qsum, q1, q2);
|
||||
if (QMAGSQ(qsum) < VDIVIDE_TOL * VDIVIDE_TOL) {
|
||||
quat_slerp(qout, q1, q2, 0.5);
|
||||
return;
|
||||
}
|
||||
QMOVE(qout, qsum);
|
||||
QUNITIZE( qout );
|
||||
}
|
||||
|
||||
@@ -302,9 +309,12 @@ quat_log(quat_t out, const quat_t in)
|
||||
fastf_t theta;
|
||||
fastf_t scale;
|
||||
|
||||
if ( (scale = MAGNITUDE(in)) > VDIVIDE_TOL ) {
|
||||
scale = MAGNITUDE(in);
|
||||
if (scale > VDIVIDE_TOL) {
|
||||
theta = atan2( scale, in[W] );
|
||||
scale = theta/scale;
|
||||
} else {
|
||||
scale = 0.0;
|
||||
}
|
||||
|
||||
VSCALE( out, in, scale );
|
||||
|
||||
@@ -33,6 +33,22 @@ vect_close_or_neg(const vect_t a, const vect_t b, double tol)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
lookat_is_valid(const vect_t direction)
|
||||
{
|
||||
vect_t expected = {0.0, 0.0, -1.0};
|
||||
vect_t output = VINIT_ZERO;
|
||||
mat_t matrix = MAT_INIT_ZERO;
|
||||
|
||||
bn_mat_lookat(matrix, direction, 0);
|
||||
MAT4X3VEC(output, matrix, direction);
|
||||
VUNITIZE(output);
|
||||
|
||||
return finite_vec(output) && vect_close(output, expected, 1.0e-12)
|
||||
&& orthonormal_rotation(matrix, 1.0e-12);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_mat_basic(void)
|
||||
{
|
||||
@@ -799,6 +815,13 @@ test_mat_orientation(void)
|
||||
int failures = 0;
|
||||
const char *test = "mat_orientation";
|
||||
struct bn_tol tol = BN_TOL_INIT_TOL;
|
||||
struct bn_tol angular_tol = BN_TOL_INIT_TOL;
|
||||
static const vect_t lookat_directions[] = {
|
||||
{0.0, 0.0, -1.0},
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.0, 0.0, -5.0},
|
||||
{0.0, 0.0, 5.0}
|
||||
};
|
||||
vect_t input = {1.0, 2.0, 3.0};
|
||||
vect_t perp = VINIT_ZERO;
|
||||
vect_t expected_perp = {0.0, 0.0, 1.0};
|
||||
@@ -806,9 +829,14 @@ test_mat_orientation(void)
|
||||
vect_t to = {0.0, 1.0, 0.0};
|
||||
vect_t opposite = {-1.0, 0.0, 0.0};
|
||||
vect_t dir = {1.0, 1.0, -2.0};
|
||||
vect_t near_from = {1.0, 0.0, 0.0};
|
||||
vect_t near_to = {1.0, 0.1, 0.0};
|
||||
vect_t near_to_unit = VINIT_ZERO;
|
||||
vect_t zero_dir = VINIT_ZERO;
|
||||
vect_t out = VINIT_ZERO;
|
||||
hvect_t homogeneous = HINIT_ZERO;
|
||||
mat_t m = MAT_INIT_ZERO;
|
||||
size_t i;
|
||||
|
||||
if (!HZERO(homogeneous)) {
|
||||
report_failure(test, "HZERO failed for a zero homogeneous vector");
|
||||
@@ -844,6 +872,18 @@ test_mat_orientation(void)
|
||||
failures++;
|
||||
}
|
||||
|
||||
angular_tol.dist = 1.0;
|
||||
angular_tol.dist_sq = 1.0;
|
||||
bn_mat_fromto(m, near_from, near_to, &angular_tol);
|
||||
MAT4X3VEC(out, m, near_from);
|
||||
VUNITIZE(out);
|
||||
VMOVE(near_to_unit, near_to);
|
||||
VUNITIZE(near_to_unit);
|
||||
if (VDOT(out, near_to_unit) < 1.0 - 1.0e-12 || !orthonormal_rotation(m, 1.0e-12)) {
|
||||
report_failure(test, "bn_mat_fromto used distance tolerance for angular classification");
|
||||
failures++;
|
||||
}
|
||||
|
||||
VUNITIZE(dir);
|
||||
bn_mat_lookat(m, dir, 0);
|
||||
MAT4X3VEC(out, m, dir);
|
||||
@@ -854,6 +894,19 @@ test_mat_orientation(void)
|
||||
failures++;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(lookat_directions) / sizeof(lookat_directions[0]); i++) {
|
||||
if (!lookat_is_valid(lookat_directions[i])) {
|
||||
report_failure(test, "bn_mat_lookat failed for an axial direction");
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
bn_mat_lookat(m, zero_dir, 0);
|
||||
if (!mat_close(m, bn_mat_identity, 0.0)) {
|
||||
report_failure(test, "bn_mat_lookat did not return identity for a zero direction");
|
||||
failures++;
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
@@ -864,6 +917,7 @@ test_mat_transform(void)
|
||||
int failures = 0;
|
||||
const char *test = "mat_transform";
|
||||
const double legacy_tol = BN_TOL_DIST;
|
||||
struct bn_tol tol = BN_TOL_INIT_TOL;
|
||||
static const struct {
|
||||
double sinv;
|
||||
double cosv;
|
||||
@@ -919,8 +973,14 @@ test_mat_transform(void)
|
||||
point_t p = {2.0, 2.0, 3.0};
|
||||
point_t axis_point = {1.0, 2.0, 10.0};
|
||||
point_t expected = {1.0, 3.0, 3.0};
|
||||
point_t pivot = {1.0, 2.0, 3.0};
|
||||
point_t pivot_offset = {2.0, 2.0, 3.0};
|
||||
point_t pivot_expected = {1.0, 3.0, 3.0};
|
||||
point_t out = VINIT_ZERO;
|
||||
vect_t pivot_direction = {0.0, 0.0, 1.0};
|
||||
mat_t m = MAT_INIT_ZERO;
|
||||
mat_t change = MAT_INIT_ZERO;
|
||||
mat_t input = MAT_INIT_ZERO;
|
||||
int i;
|
||||
|
||||
bn_mat_arb_rot(m, anchor, axis, M_PI_2);
|
||||
@@ -936,6 +996,20 @@ test_mat_transform(void)
|
||||
failures++;
|
||||
}
|
||||
|
||||
bn_mat_zrot(change, 1.0, 0.0);
|
||||
MAT_IDN(input);
|
||||
bn_wrt_point_direc(m, change, input, pivot, pivot_direction, &tol);
|
||||
MAT4X3PNT(out, m, pivot);
|
||||
if (!vect_close(out, pivot, 1.0e-12)) {
|
||||
report_failure(test, "bn_wrt_point_direc moved its pivot point");
|
||||
failures++;
|
||||
}
|
||||
MAT4X3PNT(out, m, pivot_offset);
|
||||
if (!vect_close(out, pivot_expected, 1.0e-12)) {
|
||||
report_failure(test, "bn_wrt_point_direc did not rotate around its pivot point");
|
||||
failures++;
|
||||
}
|
||||
|
||||
MAT_IDN(m);
|
||||
if (bn_mat_is_non_unif(m) != 0) {
|
||||
report_failure(test, "bn_mat_is_non_unif misclassified the identity matrix");
|
||||
|
||||
@@ -199,6 +199,153 @@ test_poly_ops(void)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_poly_edge_cases(void)
|
||||
{
|
||||
int failures = 0;
|
||||
const char *test = "poly_edge_cases";
|
||||
const double linear_coeffs[] = {1.0, -1.0};
|
||||
const double quadratic_coeffs[] = {1.0, 0.0, -4.0};
|
||||
const double aliased_sum_coeffs[] = {1.0, 1.0, -5.0};
|
||||
const double aliased_difference_coeffs[] = {-1.0, 1.0, 3.0};
|
||||
const double aliased_product_coeffs[] = {1.0, -1.0, -4.0, 4.0};
|
||||
const double dividend_coeffs[] = {1.0, -3.0, 2.0};
|
||||
const double constant_divisor_coeffs[] = {2.0};
|
||||
const double constant_quotient_coeffs[] = {0.5, -1.5, 1.0};
|
||||
const double higher_divisor_coeffs[] = {1.0, 0.0, 1.0};
|
||||
const double degree_six_coeffs[] = {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
||||
const double untouched_product_coeffs[] = {42.0};
|
||||
const double zero_coeffs[] = {0.0};
|
||||
const double scaled_quadratic_coeffs[] = {1.0e-12, -5.0e-12, 6.0e-12};
|
||||
const double scaled_cubic_coeffs[] = {1.0e-12, -6.0e-12, 11.0e-12, -6.0e-12};
|
||||
const double scaled_quartic_coeffs[] = {1.0e-12, 0.0, -10.0e-12, 0.0, 9.0e-12};
|
||||
bn_poly_t linear = poly_from_coeffs(1, linear_coeffs);
|
||||
bn_poly_t quadratic = poly_from_coeffs(2, quadratic_coeffs);
|
||||
bn_poly_t dividend = poly_from_coeffs(2, dividend_coeffs);
|
||||
bn_poly_t constant_divisor = poly_from_coeffs(0, constant_divisor_coeffs);
|
||||
bn_poly_t higher_divisor = poly_from_coeffs(2, higher_divisor_coeffs);
|
||||
bn_poly_t degree_six = poly_from_coeffs(6, degree_six_coeffs);
|
||||
bn_poly_t rejected_product = poly_from_coeffs(0, untouched_product_coeffs);
|
||||
bn_poly_t quotient = BN_POLY_INIT_ZERO;
|
||||
bn_poly_t remainder = BN_POLY_INIT_ZERO;
|
||||
bn_poly_t scaled_quadratic = poly_from_coeffs(2, scaled_quadratic_coeffs);
|
||||
bn_poly_t scaled_cubic = poly_from_coeffs(3, scaled_cubic_coeffs);
|
||||
bn_poly_t scaled_quartic = poly_from_coeffs(4, scaled_quartic_coeffs);
|
||||
bn_complex_t roots[4];
|
||||
|
||||
if (!bn_poly_add(&linear, &linear, &quadratic) ||
|
||||
!poly_close(&linear, 2, aliased_sum_coeffs, 0.0)) {
|
||||
report_failure(test, "bn_poly_add did not support output aliasing a lower-degree input");
|
||||
failures++;
|
||||
}
|
||||
|
||||
linear = poly_from_coeffs(1, linear_coeffs);
|
||||
if (!bn_poly_sub(&linear, &linear, &quadratic) ||
|
||||
!poly_close(&linear, 2, aliased_difference_coeffs, 0.0)) {
|
||||
report_failure(test, "bn_poly_sub did not support output aliasing a lower-degree input");
|
||||
failures++;
|
||||
}
|
||||
|
||||
linear = poly_from_coeffs(1, linear_coeffs);
|
||||
if (!bn_poly_mul(&linear, &linear, &quadratic) ||
|
||||
!poly_close(&linear, 3, aliased_product_coeffs, 0.0)) {
|
||||
report_failure(test, "bn_poly_mul did not support output aliasing an input");
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (bn_poly_mul(&rejected_product, °ree_six, &quadratic) ||
|
||||
!poly_close(&rejected_product, 0, untouched_product_coeffs, 0.0)) {
|
||||
report_failure(test, "bn_poly_mul modified its output after a degree-limit failure");
|
||||
failures++;
|
||||
}
|
||||
|
||||
linear = poly_from_coeffs(1, linear_coeffs);
|
||||
bn_poly_synthetic_division("ient, &remainder, ÷nd, &constant_divisor);
|
||||
if (!poly_close("ient, 2, constant_quotient_coeffs, 0.0) ||
|
||||
!poly_close(&remainder, 0, zero_coeffs, 0.0)) {
|
||||
report_failure(test, "bn_poly_synthetic_division failed for a constant divisor");
|
||||
failures++;
|
||||
}
|
||||
|
||||
bn_poly_synthetic_division("ient, &remainder, &linear, &higher_divisor);
|
||||
if (!poly_close("ient, 0, zero_coeffs, 0.0) ||
|
||||
!poly_close(&remainder, 1, linear_coeffs, 0.0)) {
|
||||
report_failure(test, "bn_poly_synthetic_division mishandled a higher-degree divisor");
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (!bn_poly_quadratic_roots(roots, &scaled_quadratic) ||
|
||||
!has_real_root(roots, 2, 2.0, 1.0e-10) ||
|
||||
!has_real_root(roots, 2, 3.0, 1.0e-10)) {
|
||||
report_failure(test, "bn_poly_quadratic_roots rejected a scaled quadratic");
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (!bn_poly_cubic_roots(roots, &scaled_cubic) ||
|
||||
!has_real_root(roots, 3, 1.0, 1.0e-10) ||
|
||||
!has_real_root(roots, 3, 2.0, 1.0e-10) ||
|
||||
!has_real_root(roots, 3, 3.0, 1.0e-10)) {
|
||||
report_failure(test, "bn_poly_cubic_roots rejected a scaled cubic");
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (!bn_poly_quartic_roots(roots, &scaled_quartic) ||
|
||||
!has_real_root(roots, 4, -3.0, 1.0e-8) ||
|
||||
!has_real_root(roots, 4, -1.0, 1.0e-8) ||
|
||||
!has_real_root(roots, 4, 1.0, 1.0e-8) ||
|
||||
!has_real_root(roots, 4, 3.0, 1.0e-8)) {
|
||||
report_failure(test, "bn_poly_quartic_roots rejected a scaled quartic");
|
||||
failures++;
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_multipoly_ops(void)
|
||||
{
|
||||
int failures = 0;
|
||||
const char *test = "multipoly_ops";
|
||||
struct bn_multipoly *p1 = bn_multipoly_new(1, 1);
|
||||
struct bn_multipoly *p2 = bn_multipoly_new(3, 2);
|
||||
struct bn_multipoly *sum;
|
||||
struct bn_multipoly *product;
|
||||
|
||||
if (!p1 || !p2 || !bn_multipoly_set(p1, 1, 2, 3.0) ||
|
||||
!bn_multipoly_set(p2, 0, 0, 2.0) ||
|
||||
!bn_multipoly_set(p2, 2, 1, 5.0)) {
|
||||
report_failure(test, "could not construct bivariate polynomial operands");
|
||||
bn_multipoly_free(p1);
|
||||
bn_multipoly_free(p2);
|
||||
return failures + 1;
|
||||
}
|
||||
|
||||
sum = bn_multipoly_add(p1, p2);
|
||||
product = bn_multipoly_mul(p1, p2);
|
||||
if (!sum || sum->dgrs != 3 || sum->dgrt != 3 ||
|
||||
!scalar_close(sum->cf[0][0], 2.0, 0.0) ||
|
||||
!scalar_close(sum->cf[1][2], 3.0, 0.0) ||
|
||||
!scalar_close(sum->cf[2][1], 5.0, 0.0)) {
|
||||
report_failure(test, "bn_multipoly_add did not preserve differently sized operands");
|
||||
failures++;
|
||||
}
|
||||
|
||||
if (!product || product->dgrs != 4 || product->dgrt != 4 ||
|
||||
!scalar_close(product->cf[1][2], 6.0, 0.0) ||
|
||||
!scalar_close(product->cf[3][3], 15.0, 0.0)) {
|
||||
report_failure(test, "bn_multipoly_mul did not accumulate coefficient products");
|
||||
failures++;
|
||||
}
|
||||
|
||||
bn_multipoly_free(product);
|
||||
bn_multipoly_free(sum);
|
||||
bn_multipoly_free(p2);
|
||||
bn_multipoly_free(p1);
|
||||
return failures;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test_poly_legacy(void)
|
||||
{
|
||||
@@ -553,6 +700,8 @@ test_poly_roots(void)
|
||||
|
||||
static const struct bn_api_case poly_cases[] = {
|
||||
{"ops", test_poly_ops},
|
||||
{"edge", test_poly_edge_cases},
|
||||
{"multipoly", test_multipoly_ops},
|
||||
{"legacy", test_poly_legacy},
|
||||
{"roots", test_poly_roots},
|
||||
{NULL, NULL}
|
||||
|
||||
@@ -244,6 +244,12 @@ test_qmath_interp(void)
|
||||
failures++;
|
||||
}
|
||||
|
||||
quat_bisect(out, qid, qanti);
|
||||
if (!quat_is_unit_finite(out, 1.0e-12)) {
|
||||
report_failure(test, "quat_bisect antipodal branch failed to return a finite unit quaternion");
|
||||
failures++;
|
||||
}
|
||||
|
||||
quat_double(exact, qdouble_same_in, qdouble_same_in);
|
||||
if (!hvect_close(exact, qdouble_same_expected, 1.0e-3)) {
|
||||
report_failure(test, "quat_double legacy same-input case failed");
|
||||
@@ -326,6 +332,8 @@ test_qmath_logexp(void)
|
||||
quat_t v = {0.2, -0.1, 0.3, 0.0};
|
||||
quat_t q = HINIT_ZERO;
|
||||
quat_t out = HINIT_ZERO;
|
||||
quat_t qidentity = {0.0, 0.0, 0.0, 1.0};
|
||||
quat_t qzero = HINIT_ZERO;
|
||||
quat_t qin = {0.3, 0.4, 0.1, 0.85};
|
||||
int i;
|
||||
|
||||
@@ -347,6 +355,12 @@ test_qmath_logexp(void)
|
||||
failures++;
|
||||
}
|
||||
|
||||
quat_log(out, qidentity);
|
||||
if (!hvect_close(out, qzero, 0.0)) {
|
||||
report_failure(test, "quat_log did not map identity to the zero quaternion");
|
||||
failures++;
|
||||
}
|
||||
|
||||
for (i = 0; i < (int)(sizeof(exp_cases) / sizeof(exp_cases[0])); i++) {
|
||||
quat_exp(q, exp_cases[i].in);
|
||||
if (!hvect_close(q, exp_cases[i].expected, 1.0e-3)) {
|
||||
|
||||
@@ -250,22 +250,28 @@ brep_translate_scv(
|
||||
return -1;
|
||||
}
|
||||
|
||||
double *cv = NULL;
|
||||
if (nurbsSurface) {
|
||||
cv = nurbsSurface->CV(i, j);
|
||||
} else {
|
||||
if (!nurbsSurface)
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (cv) {
|
||||
ON_3dPoint newPt;
|
||||
newPt.x = cv[X] + dx;
|
||||
newPt.y = cv[Y] + dy;
|
||||
newPt.z = cv[Z] + dz;
|
||||
nurbsSurface->SetCV(i, j, newPt);
|
||||
} else {
|
||||
ON_3dPoint point;
|
||||
if (!nurbsSurface->GetCV(i, j, point))
|
||||
return -3;
|
||||
|
||||
point.x += dx;
|
||||
point.y += dy;
|
||||
point.z += dz;
|
||||
|
||||
bool updated;
|
||||
if (nurbsSurface->IsRational()) {
|
||||
const double weight = nurbsSurface->Weight(i, j);
|
||||
updated = nurbsSurface->SetCV(i, j,
|
||||
ON_4dPoint(point.x * weight, point.y * weight,
|
||||
point.z * weight, weight));
|
||||
} else {
|
||||
updated = nurbsSurface->SetCV(i, j, point);
|
||||
}
|
||||
if (!updated)
|
||||
return -3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user