Compare commits

..
73 Commits
Author SHA1 Message Date
Clifford Yapp acd957aec3 Use Tk-native display selection across graphical tools
DISPLAY is an X11 connection setting, but MGED treated any inherited
value as an explicit Tk screen. Unix-like shells on Windows can export
placeholders, causing native Tk child windows and display managers to
fail even though Tk initialized successfully.

Let Tk select its platform backend and canonical screen when no -d
option is given. Preserve explicit X11 selection by applying DISPLAY
only for Tk initialization, restore the caller environment for Win32
and Aqua, and reject unsupported native screen requests clearly.

Use the initialized Tk screen for WGL and Tk software rendering, and
stop assigning X11 defaults to offscreen or Qt contexts. Remove the
same synthetic DISPLAY assumptions from MGED and Archer scripts so
Archer, RtWizard, BRLMan, and Tk ISST follow consistent startup rules.
2026-10-02 14:54:31 -04:00
Clifford Yapp f7a8d5b406 Fix Edit->Reset menu entry - uses reinit_edit_state() 2026-10-02 13:42:36 -04:00
Clifford Yapp 85de3929e5 Make sure zbuffer and lighting are set up when using share_dlist 2026-10-02 13:09:25 -04:00
Clifford Yapp 0a8c6d8bc2 Update Manual viewer in MGED to use tkhtml3 2026-10-01 15:43:07 -04:00
Clifford Yapp a00efd15c6 Fixes for man page viewer 2026-10-01 15:43:07 -04:00
f4alt 06c8ab6204 more uniform exiting
consolidate the call sites where we end up in bu_exit due to a failure.
Have a simple wrapper so we flush any results buffers that may be useful
for error context, and then call the bu_exit.
2026-10-01 14:34:31 -04:00
f4alt cd55562c46 bubble gqa return code 2026-10-01 14:34:31 -04:00
f4alt 6d142b62e7 validate material IDs are present in density table
previously, positive but not defined in the table values would silently
corrupt the calculation with a -1.0 for the density value and no
warning.
2026-10-01 14:34:31 -04:00
f4alt da2f503367 handle cache compression failures safely
ensure we can compress with bounds checks. If we got something failing
or unsupported, log and return instead of BU_ASSERT (bombing). This
allows the caller to appropriately free the external.
2026-10-01 14:34:31 -04:00
f4alt a9ac66788a guard logging behind debug flag 2026-10-01 14:34:31 -04:00
Clifford Yapp 4cc9f9cf01 Be more nuanced about what ARB edit ops we can support. 2026-10-01 12:10:33 -04:00
Clifford Yapp 7b1898d9fd Add ARBN inside logic. 2026-10-01 12:06:29 -04:00
Clifford Yapp e4d297893e label hyp neck-to-base ratio 2026-10-01 12:00:03 -04:00
Clifford Yapp 0173dd8c40 Correct bot face selection logic 2026-10-01 11:55:30 -04:00
Clifford Yapp cf344d773e Print result in mged (just assigning to interp isn't enough) 2026-10-01 11:14:39 -04:00
Clifford Yapp 8c51c1d527 Mention facetize improvements in NEWS 2026-10-01 08:55:26 -04:00
Clifford Yapp 6e48ea9ddd Center README toplevel text 2026-09-30 06:44:36 -04:00
Clifford Yapp 488f538040 Update bext SHA1 to latest 2026-09-28 08:46:34 -04:00
Clifford Yapp 17cc9f3a5a List file for distcheck 2026-09-25 17:31:09 -04:00
Clifford Yapp bf2c508826 List cases.inc for distclean 2026-09-25 15:55:30 -04:00
Clifford Yapp f2e68b1a2a Initialize 2026-09-25 15:42:29 -04:00
Clifford Yapp 5e873e5081 Fix MGED live view panning and solid edit lookup
View-mode mouse panning now honors view coordinates even when MGED is
set to object coordinates. Correct the solid-edit lookup so live
geometry queries work during dragging.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fix Creo's inverted solidity result, make segment clipping reentrant,
and clarify geometry API contracts.  Add focused plane, PCA, polygon,
and trimesh regressions.
2026-09-21 18:17:12 -04:00
Clifford Yapp 0d745fca35 Use bu_vls for VFONT paths
The VFONT loader used a 128-byte buffer when constructing the bundled
font path. Odd-pathname distcheck directories exceeded that limit in CI
environments, causing fblabel to reject the default font and all
dependent rtwizard tests to fail. Use a bu_vls path so the installed
font can be opened regardless of build prefix length, and release it on
all loader exits.
2026-09-18 16:39:52 -04:00
Clifford Yapp 5270e67abe List files for distcheck 2026-09-18 15:47:02 -04:00
229 changed files with 22287 additions and 7403 deletions
+32 -17
View File
@@ -231,7 +231,7 @@ set(CMAKE_FIND_USE_PACKAGE_REGISTRY FALSE)
# Prepare to manage and build BRL-CAD's external dependencies. Specifies a
# specific commit hash to use in the 'bext' repository.
include(BRLCAD_ExternalDeps)
brlcad_bext_init(12115e6c5922e2db27a9b88e00dd714af5315f69)
brlcad_bext_init(0682795bcfe34644c17e5a4bdd0225c76f6bdc98)
#---------------------------------------------------------------------
# Define various utilities.
@@ -678,8 +678,13 @@ endif(MSVC)
# before use so platforms without GNU/LLVM-style linker options are skipped.
brlcad_check_linker_flag(C "LINKER:--no-undefined" BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND)
brlcad_check_linker_flag(CXX "LINKER:--no-undefined" BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND)
if(BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND AND BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND)
if(BRLCAD_LINKER_NO_UNDEFINED_C_FLAG_FOUND AND BRLCAD_LINKER_NO_UNDEFINED_CXX_FLAG_FOUND
AND NOT BRLCAD_ENABLE_ADDRESS_SANITIZER)
set(BRLCAD_LINKER_NO_UNDEFINED_FLAG "LINKER:--no-undefined" CACHE STRING "Linker flag requiring resolved shared library symbols" FORCE)
else()
# Clang links the AddressSanitizer runtime into executables, leaving its
# shared-library instrumentation symbols unresolved until then.
set(BRLCAD_LINKER_NO_UNDEFINED_FLAG "" CACHE STRING "Linker flag requiring resolved shared library symbols" FORCE)
endif()
brlcad_check_linker_flags(C BRLCAD_LINKER_WHOLE_ARCHIVE_GROUP_C_FLAG_FOUND "LINKER:--whole-archive" "LINKER:--no-whole-archive")
brlcad_check_linker_flags(CXX BRLCAD_LINKER_WHOLE_ARCHIVE_GROUP_CXX_FLAG_FOUND "LINKER:--whole-archive" "LINKER:--no-whole-archive")
@@ -1642,21 +1647,31 @@ if(NOT MSVC)
endif(BRLCAD_WARNINGS OR BRLCAD_ENABLE_STRICT)
if(BRLCAD_ENABLE_COVERAGE)
# TODO: These seem to GCC specific flags - should probably set up for clang
# as well if that's our compiler...
# https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
# If we need different settings for different tools, should
# encapsulate each tool's setup in a function
check_c_flag(ftest-coverage)
check_cxx_flag(ftest-coverage)
if(FTEST_COVERAGE_C_FLAG_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
else(FTEST_COVERAGE_C_FLAG_FOUND)
message(SEND_ERROR "Building with coverage is not supported by BRL-CAD on this platform.")
endif(FTEST_COVERAGE_C_FLAG_FOUND)
if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
check_c_flag(fprofile-instr-generate)
check_cxx_flag(fprofile-instr-generate)
check_c_flag(fcoverage-mapping)
check_cxx_flag(fcoverage-mapping)
if(NOT FPROFILE_INSTR_GENERATE_C_FLAG_FOUND OR
NOT FPROFILE_INSTR_GENERATE_CXX_FLAG_FOUND OR
NOT FCOVERAGE_MAPPING_C_FLAG_FOUND OR
NOT FCOVERAGE_MAPPING_CXX_FLAG_FOUND)
message(FATAL_ERROR "Clang source-based coverage is not supported by this toolchain.")
endif()
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate)
else()
check_c_flag(ftest-coverage)
check_cxx_flag(ftest-coverage)
if(FTEST_COVERAGE_C_FLAG_FOUND AND FTEST_COVERAGE_CXX_FLAG_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov -ftest-coverage -fprofile-arcs -fprofile-update=atomic")
else()
message(FATAL_ERROR "GCC coverage is not supported by this toolchain.")
endif()
endif()
endif(BRLCAD_ENABLE_COVERAGE)
if(BRLCAD_ENABLE_STRICT)
+4
View File
@@ -13,6 +13,8 @@ changes made. See document footer for additional details.
--- 20XX-XX-XX Release 7.46.0 ---
----------------------------------------------------------------------
* improved pixscale & bwscale scaling and image piping - Sean Morrison
* restored pixdiff behavior on .pix, added -b for .bw - Sean Morrison
* updated MGED and Archer for Tcl 8.6.18 and Itcl/Itk 4 - Cliff Yapp
* fixed writes to fileless geometry databases - Cliff Yapp
* fixed nastran-g handling of large property IDs - Rupesh, Cliff Yapp
@@ -59,6 +61,8 @@ changes made. See document footer for additional details.
* 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
* improved fallback methodologies for facetize pipeline - Cliff Yapp
* improved facetize pipeline robustness - Cliff Yapp
* added arrange nest command for packing database objects - Sean Morrison
* new mkbuilding tool for parametric/OSM/CityJSON models - Sean Morrison
* cross-platform BINUNIF and DSP byte-order handling - Sean Morrison
+3 -3
View File
@@ -1,6 +1,6 @@
BRL-CAD
Release 7.44.2
http://brlcad.org/
BRL-CAD
Release 7.44.2
http://brlcad.org/
BRL-CAD is a powerful cross-platform open source combinatorial
solid modeling system that includes an interactive 3D solid geometry
+34 -18
View File
@@ -4,38 +4,54 @@
:manmanual: BRL-CAD
== NAME
pixdiff - highlight samples which differ between two pix or bw files
pixdiff - highlight differences between two pix or bw images
== SYNOPSIS
[source]
----
pixdiff _file1.pix_ _file2.pix_ > _out.pix_
pixdiff _file1.bw_ _file2.bw_ > _out.bw_
pixdiff -b _file1.bw_ _file2.bw_ > _out.bw_
----
[[description]]
== DESCRIPTION
*pixdiff* compares two raw *pix*(5) or *bw*(5) images byte by byte and
writes a difference image of the same size to standard output. Bytes that
differ by +/-1 are written at intensity 192, while larger differences are
written at full intensity 255. Matching bytes are written at half their
original intensity, providing visual context for locating differences.
By default, *pixdiff* compares raw *pix*(5) images as RGB pixels and writes
a PIX difference image to standard output. When all three channels of a
pixel match, it writes a dim grayscale version of that pixel to all three
output channels. When any channel differs, each matching channel is written
as zero, each channel differing by one is written as 192, and each channel
differing by more than one is written as 255.
Because the comparison does not interpret pixel structure, no format option
is needed and *pixdiff* is independent of image resolution. The two inputs
must be the same size in bytes; otherwise the program reports the mismatch
and exits. Either input file may be given as
With *-b*, *pixdiff* compares raw *bw*(5) images one byte at a time and
writes a BW difference image. Differing samples use the same 192 and 255
intensities; matching samples are written at half their original intensity.
No image dimensions are needed. The two inputs must be the same size in
bytes, and PIX input must contain complete three-byte pixels. Otherwise,
*pixdiff* reports an error. Either input file may be given as
*-* to read from standard input. Output must be redirected; *pixdiff*
refuses to write a raw image stream to a terminal.
Upon completion, the count of bytes that matched, that were off by 1, and
that were off by many is reported on standard error.
Upon completion, *pixdiff* reports matching, off-by-one, and larger-difference
channel or sample counts on standard error.
Also see *bwdiff*(1), which can also operate on *pix*(5) files and can
produce other kinds of difference output.
*bwdiff -n -b* produces a similar BW highlight image, but writes 255 rather
than 192 for off-by-one differences. The default *bwdiff*(1) output instead
shows signed differences around a neutral gray.
[[options]]
== OPTIONS
*-b*::
Treat both inputs and the output as one-byte-per-pixel BW images. Without
this option, use three-byte-per-pixel PIX images.
*-h*::
Show usage information.
[[examples]]
@@ -59,10 +75,10 @@ on standard error give a quick quantitative sense of how much changed.
====
[source,bash]
----
pixdiff depth_a.bw depth_b.bw > depth_diff.bw
pixdiff -b depth_a.bw depth_b.bw > depth_diff.bw
----
Writes a grayscale highlight image using the same byte comparison and
statistics as the color example; no format option is needed.
Writes a grayscale highlight image using byte-wise comparison. Matching
samples retain half their input intensity as context.
====
+50
View File
@@ -200,6 +200,56 @@ ____
The LIBRT_DEBUG environment variable may be set to the hexadecimal bit vector as described by the DEBUG_* flags listed in the raytrace.h header. Similarly, many LIBRT applications provide a "-x #" command-line option which will take precedence over the environment variable.
The RT_DEBUG_RTCMP flag (0x10000000) (set with LIBRT_DEBUG=10000000)
sets up rtcmp capture, but recording starts disabled. Set
LIBRT_RTCMP_FILE before enabling the flag, then call
rt_rtcmp_capture_set_enabled(1) from the application or a debugger to
record selected rays. Call rt_rtcmp_capture_set_enabled(0) to stop
recording. Enabling returns -1 if the debug flag is unset or the output
writer failed to initialize; disabling returns 0 even before setup. A
ray must begin while recording is enabled, and leaving recording
disabled when it finishes suppresses its record. The debug flag must be
set before concurrent ray shooting; use the setter to change recording
state while rays are running.
Captured hits append one high-precision rtcmp JSON record per ray with
final partitions. Misses produce no record. librt never clears the
output file; the caller must clear or rotate it before a new comparison
as needed. Separate processes must not write to the same file
concurrently. Pending records are flushed within two seconds under
normal operation and during normal process shutdown; call
rt_rtcmp_capture_flush() to wait for pending records when using a
debugger. Disabling recording does not discard records already queued. A
process terminated abnormally may lose pending records. rtcmp comparison
files should use the same hit-only capture mode on both sides; its
generation mode also includes misses. The output can be expanded to also
include primitive segments if LIBRT_RTCMP_PRIMITIVES is set to 1 before
capture is set up.
For example, run a single-threaded ray tracing application in a debugger
and stop at `rt_shootray` for the suspect ray. Capture just that call,
then compare it with a capture of the same ray from a reference build:
[source,shell]
----
$ LIBRT_DEBUG=10000000 LIBRT_RTCMP_FILE=trial.json \
gdb --args ./ray_application model.g
(gdb) break rt_shootray
(gdb) run
(gdb) call rt_rtcmp_capture_set_enabled(1)
(gdb) finish
(gdb) call rt_rtcmp_capture_set_enabled(0)
(gdb) call rt_rtcmp_capture_flush()
(gdb) quit
$ rtcmp -c reference.json trial.json
----
Continue to the suspect ray before enabling capture if the breakpoint
first stops on another ray. Produce `reference.json` with the same ray
and the same capture mode; set LIBRT_RTCMP_PRIMITIVES=1 for both runs
when primitive segments are needed to locate a difference before Boolean
evaluation.
The LIBRT_V4FLIP environment variable may be set to a true or false value to respectively force or disable endianness interpretation of v4 geometry database files. The default behavior is to automatically detect whether flipping the endian interpretation will help LIBRT read a binary-incompatible v4 geometry database file. Setting LIBRT_V4FLIP will override automatic detection.
-1
View File
@@ -61,5 +61,4 @@ Author: Paul Mackerras (paulus@cs.anu.edu.au),
file:/src/libtclcad/tkImgFmtPIX.c
file:/src/tclscripts/cad_clrpick.tcl
file:/src/tclscripts/html_library.tcl
+1
View File
@@ -55,6 +55,7 @@
#include "bg/clip.h"
#include "bg/lseg.h"
#include "bg/obr.h"
#include "bg/pca.h"
#include "bg/plane.h"
#include "bg/polygon.h"
#include "bg/sat.h"
+4 -4
View File
@@ -50,12 +50,12 @@ BG_EXPORT extern int bg_lseg_clip(fastf_t *xp1, fastf_t *yp1, fastf_t *xp2, fast
* The RPP has faces parallel to the coordinate planes and is defined
* by a minimum point and a maximum point.
*
* FIXME: the function name implies this takes a point,dir for a,b but
* it actually takes a line segment going from points a to b!
* Despite its historical name, this function clips the finite line segment
* from a to b.
*
* Returns -
* 0 if ray does not hit RPP,
* !0 if ray hits RPP.
* 0 if the segment does not hit the RPP,
* !0 if the segment hits the RPP.
*
* Implicit Return -
* if !0 was returned, "a" and "b" have been clipped to the RPP.
+6 -3
View File
@@ -126,9 +126,12 @@ BG_EXPORT extern int bg_3d_coplanar_obr(point_t *center,
* \endverbatim
*
*
* @param[out] pnts eight points of oriented bounding box
* @param points_3d array of 3D points
* @param pnt_cnt number of points in pnts array
* @param[out] pnts eight non-NULL, caller-provided point_t storage
* locations for the oriented bounding box vertices
* @param[in] points_3d flat array containing 3 * pnt_cnt coordinate values
* @param[in] pnt_cnt number of points in points_3d
*
* @return 0 on success and -1 for invalid input or when no box can be fit
*/
BG_EXPORT extern int bg_3d_obb(point_t **pnts,
const fastf_t *points_3d,
+4 -4
View File
@@ -21,7 +21,7 @@
/*----------------------------------------------------------------------*/
/** @addtogroup bg_pca
*
* Principle Component Analysis
* Principal Component Analysis
*
* Calculates an XYZ coordinate system such that it aligns with the largest
* variations in the supplied data. Intuitively, it "aligns" with the
@@ -50,7 +50,7 @@
* point_t p;
* for (size_t i = 0; i < pntcnt; i++) {
* MAT4X3PNT(p, RT, pnts[i]);
* VMOVE(pnts[i], v);
* VMOVE(pnts[i], p);
* }
* @endcode
*/
@@ -69,7 +69,7 @@ __BEGIN_DECLS
/**
* @brief
* Perform a Principle Component Analysis on a set of points.
* Perform a Principal Component Analysis on a set of points.
*
* Outputs are a center point and XYZ vectors for the coordinate system.
*
@@ -88,7 +88,7 @@ BG_EXPORT extern int bg_pca(point_t *c, vect_t *xaxis, vect_t *yaxis, vect_t *za
__END_DECLS
#endif /* BG_PLANE_H */
#endif /* BG_PCA_H */
/** @} */
/*
* Local Variables:
+7 -4
View File
@@ -1171,9 +1171,10 @@ BG_EXPORT extern int bg_isect_planes(point_t pt,
/**
* @brief
* Given an origin and a normal, create a plane_t.
* Given an origin and a normal, create a plane_t with a unit normal.
* The input point and normal are not modified. A zero-length normal fails.
*/
BG_EXPORT extern int bg_plane_pt_nrml(plane_t *p, point_t pt, vect_t nrml);
BG_EXPORT extern int bg_plane_pt_nrml(plane_t *p, const point_t pt, const vect_t nrml);
/**
* @brief
@@ -1188,13 +1189,15 @@ BG_EXPORT extern int bg_fit_plane(point_t *c, vect_t *n, size_t npnts, point_t *
/**
* @brief
* Find the closest U,V point on the plane p to 3d point pt.
* Find the closest U,V point on the plane p to 3d point pt. The plane normal
* need not be unit length.
*/
BG_EXPORT extern int bg_plane_closest_pt(fastf_t *u, fastf_t *v, plane_t *p, point_t *pt);
/**
* @brief
* Return the 3D point on the plane at parametric coordinates u, v.
* Return the 3D point on the plane at parametric coordinates u, v. The plane
* normal need not be unit length.
*/
BG_EXPORT extern int bg_plane_pt_at(point_t *pt, plane_t *p, fastf_t u, fastf_t v);
+2 -1
View File
@@ -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
View File
@@ -39,7 +39,9 @@
__BEGIN_DECLS
/**
* Test for an intersection between a line and an Axis-Aligned Bounding Box (AABB).
* Test for an intersection between an infinite line and an Axis-Aligned
* Bounding Box (AABB). ldir need not be unit length, but must be nonzero.
* aabb_extent supplies the positive half extents along the coordinate axes.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -50,7 +52,10 @@ bg_sat_line_aabb(
);
/**
* Test for an intersection between a line and an Oriented Bounding Box (OBB).
* Test for an intersection between an infinite line and an Oriented Bounding
* Box (OBB). ldir need not be unit length, but must be nonzero. Each OBB
* extent vector points from the center to a face center; nonzero vectors must
* be mutually perpendicular, and a zero vector represents a degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -61,7 +66,9 @@ bg_sat_line_obb(
);
/**
* Test for an intersection between a triangle and an Axis-Aligned Bounding Box (AABB).
* Test for an intersection between a triangle and an Axis-Aligned Bounding
* Box (AABB). extent supplies the positive half extents along the coordinate
* axes.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -72,7 +79,10 @@ bg_sat_tri_aabb(
);
/**
* Test for an intersection between a triangle and an Oriented Bounding Box (OBB).
* Test for an intersection between a triangle and an Oriented Bounding Box
* (OBB). Each extent vector points from the center to a face center; nonzero
* vectors must be mutually perpendicular, and a zero vector represents a
* degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -85,8 +95,9 @@ bg_sat_tri_obb(
/**
* Test for an intersection between an Axis-Aligned Bounding Box (AABB) and an
* Oriented Bounding Box (OBB). The latter is defined by a center point and
* three perpendicular vectors from the center to the centers of the various
* faces.
* three extent vectors from the center to the centers of the various faces.
* Nonzero extent vectors must be mutually perpendicular; a zero vector
* represents a degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
@@ -98,8 +109,9 @@ bg_sat_aabb_obb(
/**
* Test for an intersection between two Oriented Bounding Boxes (OBBs). The
* boxes are defined by a center point and three perpendicular vectors from the
* center to the centers of the various faces.
* boxes are defined by a center point and three extent vectors from the center
* to the centers of the various faces. Nonzero extent vectors must be
* mutually perpendicular; a zero vector represents a degenerate axis.
*
* Returns 1 if they intersect, 0 otherwise.
*/
+1 -1
View File
@@ -52,7 +52,7 @@ BG_EXPORT extern double bg_tri_closest_pt(
__END_DECLS
#endif /* BG_TRI_TRI_H */
#endif /* BG_TRI_PT_H */
/** @} */
/*
* Local Variables:
+1 -1
View File
@@ -54,7 +54,7 @@ BG_EXPORT extern int bg_isect_tri_line(point_t *isect,
* If isect is non-NULL, it is written only when an intersection is found.
*/
BG_EXPORT extern int bg_isect_tri_ray(const point_t orig,
const point_t dir,
const vect_t dir,
const point_t vert0,
const point_t vert1,
const point_t vert2,
+5 -1
View File
@@ -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
View File
@@ -214,8 +214,8 @@ BN_EXPORT extern void bn_mat_ae(mat_t m,
double elev);
/**
* Find the azimuth and elevation angles that correspond to the
* direction (not including twist) given by a direction vector.
* Find the azimuth and elevation angles, in degrees, that correspond
* to the direction (not including twist) given by a direction vector.
*/
BN_EXPORT extern void bn_ae_vec(fastf_t *azp,
fastf_t *elp,
@@ -236,14 +236,16 @@ BN_EXPORT extern void bn_aet_vec(fastf_t *az,
fastf_t accuracy);
/**
* Find a unit vector from the origin given azimuth and elevation.
* Find a unit vector from the origin given azimuth and elevation in
* radians.
*/
BN_EXPORT extern void bn_vec_ae(vect_t vec,
fastf_t az,
fastf_t el);
/**
* Find a vector from the origin given azimuth, elevation, and distance.
* Find a vector from the origin given azimuth and elevation in radians,
* and distance.
*/
BN_EXPORT extern void bn_vec_aed(vect_t vec,
fastf_t az,
@@ -310,8 +312,10 @@ BN_EXPORT extern void bn_vec_perp(vect_t new_vec,
* space by the angle between the two. There are many candidate
* matrices.
*
* The input 'from' and 'to' vectors need not be unit length.
* MAT4X3VEC(to, m, from) is the identity that is created.
* The input 'from' and 'to' vectors need not be unit length. They are
* normalized internally. Near-parallel inputs are handled using the
* angular tolerance in 'tol'. If either input cannot be normalized,
* the result is the identity matrix.
*
*/
BN_EXPORT extern void bn_mat_fromto(mat_t m,
@@ -344,12 +348,11 @@ BN_EXPORT extern void bn_mat_zrot(mat_t m,
double cosz);
/**
* Given a direction vector D of unit length, product a matrix which
* rotates that vector D onto the -Z axis. This matrix will be
* suitable for use as a "model2view" matrix.
*
* XXX This routine will fail if the vector is already more or less
* aligned with the Z axis.
* Given a nonzero direction vector D, produce a matrix which
* rotates D onto the -Z axis. D is normalized internally. Z-axis
* directions use a deterministic roll, and a zero direction produces
* the identity matrix. This matrix will be suitable for use as a
* model2view matrix.
*
* This is done in several steps.
*
+31
View File
@@ -37,11 +37,42 @@ __BEGIN_DECLS
typedef struct bn_multipoly {
uint32_t magic;
/**< Number of coefficients in the first polynomial variable. */
int dgrs;
/**< Number of coefficients in the second polynomial variable. */
int dgrt;
/**< Coefficient of S^s * T^t is cf[s][t]. */
double **cf;
} bn_multipoly_t;
/**
* Allocate a zero-valued bivariate polynomial with the specified
* coefficient counts. Both counts must be positive.
*/
BN_EXPORT extern struct bn_multipoly *bn_multipoly_new(int dgrs, int dgrt);
/** Release a bivariate polynomial allocated by bn_multipoly_new(). */
BN_EXPORT extern void bn_multipoly_free(struct bn_multipoly *poly);
/** Grow a polynomial to at least the specified coefficient counts. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_grow(struct bn_multipoly *poly,
int dgrs,
int dgrt);
/** Set the coefficient of S^s * T^t, growing the polynomial as needed. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_set(struct bn_multipoly *poly,
int s,
int t,
double value);
/** Return the sum of two bivariate polynomials. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_add(const struct bn_multipoly *p1,
const struct bn_multipoly *p2);
/** Return the product of two bivariate polynomials. */
BN_EXPORT extern struct bn_multipoly *bn_multipoly_mul(const struct bn_multipoly *p1,
const struct bn_multipoly *p2);
__END_DECLS
#endif /* BN_MULTIPOLY_H */
+20 -4
View File
@@ -45,7 +45,10 @@ __BEGIN_DECLS
/**
* Polynomial data type
* bn_poly->cf[n] corresponds to X^n
*
* Coefficients are stored from the highest to the lowest degree:
* `cf[0]` is the coefficient of `X^dgr` and `cf[dgr]` is the
* constant coefficient.
*/
typedef struct bn_poly {
uint32_t magic;
@@ -63,6 +66,9 @@ typedef struct bn_poly {
*
* @brief
* multiply two polynomials
*
* The output may alias either input. If the result exceeds
* BN_MAX_POLY_DEGREE, returns BN_POLY_NULL without modifying product.
*/
BN_EXPORT extern struct bn_poly *bn_poly_mul(struct bn_poly *product,
const struct bn_poly *m1,
@@ -81,6 +87,8 @@ BN_EXPORT extern struct bn_poly *bn_poly_scale(struct bn_poly *eqn,
* bn_poly_add
* @brief
* add two polynomials
*
* The output may alias either input.
*/
BN_EXPORT extern struct bn_poly *bn_poly_add(struct bn_poly *sum,
const struct bn_poly *poly1,
@@ -90,6 +98,8 @@ BN_EXPORT extern struct bn_poly *bn_poly_add(struct bn_poly *sum,
* bn_poly_sub
* @brief
* subtract two polynomials
*
* The output may alias either input.
*/
BN_EXPORT extern struct bn_poly *bn_poly_sub(struct bn_poly *diff,
const struct bn_poly *poly1,
@@ -98,7 +108,10 @@ BN_EXPORT extern struct bn_poly *bn_poly_sub(struct bn_poly *diff,
/**
* @brief
* Divides any polynomial into any other polynomial using synthetic
* division. Both polynomials must have real coefficients.
* division. Both polynomials must have real coefficients and the
* divisor's leading coefficient must be nonzero. The quotient and
* remainder outputs must be distinct; the quotient may alias the
* dividend. All other output/input pairs must be distinct.
*/
BN_EXPORT extern void bn_poly_synthetic_division(struct bn_poly *quo,
struct bn_poly *rem,
@@ -108,7 +121,8 @@ BN_EXPORT extern void bn_poly_synthetic_division(struct bn_poly *quo,
/**
*@brief
* Uses the quadratic formula to find the roots (in `complex' form) of
* any quadratic equation with real coefficients.
* any quadratic equation with real coefficients. A nonzero leading
* coefficient need not be one.
*
* @return 1 for success
* @return 0 for fail.
@@ -144,6 +158,7 @@ BN_EXPORT extern int bn_poly_quadratic_roots(struct bn_complex roots[],
* If D < 0, there will be three unequal real roots.
*
* Returns 1 for success, 0 for fail.
* A nonzero leading coefficient need not be one.
*/
BN_EXPORT extern int bn_poly_cubic_roots(struct bn_complex roots[],
const struct bn_poly *eqn);
@@ -151,7 +166,8 @@ BN_EXPORT extern int bn_poly_cubic_roots(struct bn_complex roots[],
/**
*@brief
* Uses the quartic formula to find the roots (in `complex' form)
* of any quartic equation with real coefficients.
* of any quartic equation with real coefficients. A nonzero leading
* coefficient need not be one.
*
* @return 1 for success
* @return 0 for fail.
+4 -3
View File
@@ -100,8 +100,8 @@ BN_EXPORT extern void quat_double(quat_t qout,
/**
*@brief Gives the bisector of quaternions q1 and q2.
*
* (Could be done with quat_slerp and factor 0.5)
* [I believe they must be unit quaternions this to work]
* Inputs must be unit quaternions. Antipodal inputs use a valid
* great-circle midpoint.
*/
BN_EXPORT extern void quat_bisect(quat_t qout,
const quat_t q1,
@@ -161,7 +161,8 @@ BN_EXPORT extern void quat_exp(quat_t out,
/**
*@brief
* Take the natural logarithm of a unit quaternion.
* Take the natural logarithm of a unit quaternion. The identity
* quaternion maps to the zero quaternion.
* Code by Ken Shoemake.
*/
BN_EXPORT extern void quat_log(quat_t out,
+16 -9
View File
@@ -205,23 +205,30 @@ BU_EXPORT extern int bu_sscanf(const char *src, const char *fmt, ...) _BU_ATTR_S
/** Routines for scanning certain kinds of data. */
/**
* Scans a sequence of fastf_t numbers from a string or stdin
* Scans a sequence of fastf_t numbers from a string or stdin.
*
* Scanning fastf_t numbers with bu_sscanf() is difficult, because
* doing so requires scanning to some intermediate type like double
* and then assigning to the fastf_t variable to convert the value to
* whatever type fastf_t really is. This function makes it possible
* to scan a series of fastf_t numbers separated by some character(s)
* easily, by doing the required conversion internally to the
* functions. As series of delimiter characters will be skipped,
* empty scan fields are not supported (e.g., "0.0,,0.0,1.0" will scan
* as 3 fields, not 4 with the 2nd skipped).
* to scan a series of fastf_t numbers easily, by doing the required
* conversion internally. Delimiters form a non-empty character set:
* one or more delimiter characters must separate values, and repeated
* delimiters are skipped. Empty scan fields are therefore not
* supported (e.g., "0.0,,0.0,1.0" scans as 3 fields, not 4 with the
* second skipped). A trailing delimiter is not consumed after the
* final requested value.
*
* @param[out] c Returns number of characters scanned by the function
* @param[out] c Optional destination for the number of input characters
* consumed. It is set to zero before scanning and remains zero for
* invalid arguments or when no value is scanned.
* @param[in] src A source string to scan from, or NULL to read from stdin
* @param[in] delim Any delimiter character(s) to skip between scan values
* @param[in] delim Non-empty set of delimiter characters to skip between
* scan values
* @param[in] n Number of fastf_t values to scan from the src input string
* @param[out] ... Pointers to fastf_t for storing scanned values (optional)
* @param[out] ... Exactly n fastf_t pointers for storing scanned values;
* individual pointers may be NULL
* @return Number of values scanned
*
*/
BU_EXPORT extern int bu_scan_fastf_t(int *c, const char *src, const char *delim, size_t n, ...);
+2 -2
View File
@@ -84,7 +84,7 @@ __BEGIN_DECLS
#define RT_DEBUG_HF 0x04000000 /**< @brief 27 Height Field solids */
#define RT_DEBUG_MESHING 0x08000000 /**< @brief 28 Print meshing/triangulation details */
#define RT_DEBUG_UNUSED_3 0x10000000 /**< @brief 29 Unassigned */
#define RT_DEBUG_RTCMP 0x10000000 /**< @brief 29 Set up rtcmp capture; recording uses a runtime toggle. If LIBRT_RTCMP_PRIMITIVES=1 includes primitive segments. */
#define RT_DEBUG_UNUSED_4 0x20000000 /**< @brief 30 Unassigned */
/* Options which will cause the library to write binary debugging output */
@@ -96,7 +96,7 @@ __BEGIN_DECLS
"\040PL_BOX" \
"\037PL_SOLIDS" \
"\036UNUSED_4" \
"\035UNUSED_3" \
"\035RTCMP" \
"\034UNUSED_2" \
"\033HF" \
"\032EBM" \
+23 -6
View File
@@ -465,6 +465,17 @@ rt_get_solid_keypoint(struct rt_edit *s, point_t *pt, const char **strp, fastf_t
RT_EXPORT extern void
rt_edit_set_edflag(struct rt_edit *s, int edflag);
/**
* Set a parameter-edit translation target expressed in model base units.
*
* rt_edit's parameter-edit handlers consume e_para in database local units,
* while keypoints and edit axes are maintained in model base units. This
* helper performs the boundary conversion for callers that have computed a
* target from a keypoint, axis, or view transform.
*/
RT_EXPORT extern void
rt_edit_set_translation_target(struct rt_edit *s, const point_t target);
RT_EXPORT extern int
rt_edit_knob_cmd_process(
struct rt_edit *s,
@@ -499,8 +510,10 @@ rt_knob_edit_sca(
int matrix_edit);
/* Equivalent to sedit - run editing logic after input data is set in
* rt_edit container */
RT_EXPORT extern void
* rt_edit container. Transient input flags are consumed whether the edit
* succeeds or fails; parameter values remain available to the caller.
* Returns BRLCAD_ERROR if the primitive edit fails. */
RT_EXPORT extern int
rt_edit_process(struct rt_edit *s);
/**
@@ -521,7 +534,7 @@ rt_edit_snap_point(point2d_t pt, const struct rt_edit *s);
* restored later with rt_edit_revert().
*
* The snapshot is stored inside the rt_edit struct. Calling this
* function again overwrites any previous snapshot (single-level undo).
* function again replaces the previous snapshot only on success.
*
* @return BRLCAD_OK on success, BRLCAD_ERROR if the export failed.
*/
@@ -532,8 +545,8 @@ rt_edit_checkpoint(struct rt_edit *s);
* Restore primitive parameters from the snapshot saved by
* rt_edit_checkpoint().
*
* If no snapshot has been saved (or the last snapshot was already
* consumed) this function logs a message and returns BRLCAD_ERROR.
* The snapshot remains available for repeated restores. If none has
* been saved, this function logs a message and returns BRLCAD_ERROR.
*
* @return BRLCAD_OK on success, BRLCAD_ERROR otherwise.
*/
@@ -697,7 +710,11 @@ enum rt_constraint_edit_op_kind {
RT_CONSTRAINT_EDIT_OP_SET_BEND,
RT_CONSTRAINT_EDIT_OP_SCALE_OD,
RT_CONSTRAINT_EDIT_OP_SCALE_ID,
RT_CONSTRAINT_EDIT_OP_SCALE_BEND
RT_CONSTRAINT_EDIT_OP_SCALE_BEND,
/* Set every point when an all-zero dimension has no scale reference. */
RT_CONSTRAINT_EDIT_OP_SET_ALL_OD,
RT_CONSTRAINT_EDIT_OP_SET_ALL_ID,
RT_CONSTRAINT_EDIT_OP_SET_ALL_BEND
};
struct rt_constraint_edit_param_ref {
+1 -11
View File
@@ -55,14 +55,11 @@ struct clt_tri_specific {
};
#endif
#define RT_BOT_SURFNO_INTERIOR (-1)
/* Shared between bot and ars at the moment */
struct bot_specific {
unsigned char bot_mode;
unsigned char bot_orientation;
unsigned char bot_flags;
unsigned char bot_parity_shot;
size_t bot_ntri;
fastf_t *bot_thickness;
struct bu_bitv *bot_facemode;
@@ -234,17 +231,10 @@ struct rt_bot_repair_info {
fastf_t max_hole_area_percent;
int strict;
/* OpenVDB repair controls. When force_openvdb is zero OpenVDB is used
* only as a fallback after the conventional repair path fails. */
int force_openvdb;
fastf_t openvdb_voxel_size;
// Info about generated output
int output_nonmanifold;
int output_lint_fail;
fastf_t output_volume;
int openvdb_available;
int used_openvdb;
unsigned int output_data_loss;
};
@@ -259,7 +249,7 @@ struct rt_bot_repair_info {
* tests. This isn't always desirable - sometimes manifold is enough even if
* the mesh is not otherwise well behaved - so it is an user settable param.
*/
#define RT_BOT_REPAIR_INFO_INIT {0.0, 5.0, 1, 0, 0.0, 0, 0, 0.0, 0, 0, 0};
#define RT_BOT_REPAIR_INFO_INIT {0.0, 5.0, 1, 0, 0, 0.0, 0};
/* Function to attempt repairing a non-manifold BoT. Returns 1 if ibot was
* already manifold (obot will contain NULL), 0 if a manifold BoT was created
+1
View File
@@ -41,6 +41,7 @@ struct rt_nmg_edit {
struct loopuse *lu_copy; /* copy of loop to be extruded */
plane_t lu_pl; /* plane equation for loop to be extruded */
struct shell *es_s; /* Shell where extrusion is to end up */
int es_s_from_extrusion; /* Replace this edit's prior preview */
point_t lu_keypoint; /* keypoint of lu_copy for extrusion */
struct vertex *es_v; /* Currently selected NMG vertex (ECMD_NMG_VPICK) */
struct faceuse *es_fu; /* Currently selected NMG faceuse (ECMD_NMG_FPICK) */
+12
View File
@@ -55,6 +55,18 @@
__BEGIN_DECLS
#define RT_RTCMP_CAPTURE_RUNTIME_TOGGLE 1
/** Set RT_DEBUG_RTCMP recording: nonzero enables, zero disables.
* Enabling requires the debug flag and a usable output writer.
* Return 0 on success and -1 on failure. Disabling succeeds even
* before setup.
*/
RT_EXPORT extern int rt_rtcmp_capture_set_enabled(int enabled);
/** Wait until pending RT_DEBUG_RTCMP records have reached the output file. */
RT_EXPORT extern int rt_rtcmp_capture_flush(void);
/**
* @brief
* Shoot a ray
+5 -1
View File
@@ -2200,7 +2200,11 @@ macro(find_package_opencv)
set(OpenCV_DIR_TMP "${OpenCV_DIR}")
set(OpenCV_DIR "${CMAKE_BINARY_DIR}/${LIB_DIR}/cmake/opencv4")
set(OpenCV_ROOT ${CMAKE_BINARY_DIR})
find_package(OpenCV COMPONENTS core features2d imgproc highgui)
find_package(OpenCV CONFIG COMPONENTS core imgproc imgcodecs highgui)
if(OpenCV_FOUND AND OpenCV_VERSION_MAJOR GREATER_EQUAL 5)
# OpenCV 5 moved boundingRect from imgproc to geometry.
find_package(OpenCV CONFIG COMPONENTS geometry)
endif()
unset(OpenCV_ROOT)
# If no bundled copy, see what the system has
+14
View File
@@ -117,6 +117,20 @@ if(TARGET libged)
endif(TARGET libged)
if(TARGET librt AND FUZZER_SUPPORTED)
set(RT_EDIT_FUZZ_DIR "${CMAKE_SOURCE_DIR}/src/librt/tests/edit")
brlcad_addexec(
fuzz_rt_edit_test
"${RT_EDIT_FUZZ_DIR}/fuzz.cpp;${RT_EDIT_FUZZ_DIR}/ars.cpp;${RT_EDIT_FUZZ_DIR}/bot.cpp"
"libbu;librt;libnmg;libwdb;libbrep"
TEST
)
set_target_properties(fuzz_rt_edit_test PROPERTIES UNITY_BUILD OFF)
target_compile_options(fuzz_rt_edit_test PRIVATE ${FUZZER_EXE_COMPILE_OPTIONS})
target_link_options(fuzz_rt_edit_test PRIVATE ${FUZZER_LINK_OPTIONS})
BRLCAD_ADD_TEST(NAME fuzz_rt_edit_smoke COMMAND fuzz_rt_edit_test -runs=1 -max_total_time=5)
endif()
cmakefiles(
CMakeLists.txt
fuzz_ged.cpp
+1
View File
@@ -23,6 +23,7 @@ set(
gqa.mass_mix.am.out
gqa.mass_mix.aw.out
gqa.mass_mix.u0.out
gqa.missing_density.out
gqa.mged
gqa.overlaps.plot3
gqa.sphere_adaptive.out
+24
View File
@@ -67,6 +67,11 @@ run_capture ( ) {
return $ret
}
expect_failure ( ) {
"$@"
test $? -ne 0
}
extract_last_number ( ) {
label="$1"
file="$2"
@@ -101,6 +106,19 @@ assert_close ( ) {
fi
}
assert_contains ( ) {
expected="$1"
file="$2"
desc="$3"
if grep -F -q "$expected" "$file" ; then
log "PASS: $desc"
else
log "FAIL: $desc"
STATUS="`expr $STATUS + 1`"
fi
}
MGED="`ensearch mged`"
if test ! -f "$MGED" ; then
log "Unable to find mged, aborting"
@@ -177,6 +195,10 @@ adjust mass_air.r GIFTmater 2
g mass_air_only.g mass_air.r
g mass_air_mix.g mass_solid.r mass_air.r
in missing_density.s rpp 2 3 0 1 0 1
r missing_density.r u missing_density.s
adjust missing_density.r GIFTmater 123
in sphere.s sph 0 0 0 1
r sphere.r u sphere.s
adjust sphere.r GIFTmater 5
@@ -243,6 +265,7 @@ run_capture gqa.mass_mix.aw.out $GQABIN -u m,m^3,kg -g 250mm-50mm -Aw gqa.g mass
run_capture gqa.mass_mix.am.out $GQABIN -u m,m^3,kg -g 250mm-50mm -Am gqa.g mass_air_mix.g
run_capture gqa.mass_mix.u0.out $GQABIN -u m,m^3,kg -g 250mm-50mm -U 0 -Aw gqa.g mass_air_mix.g
run_capture gqa.sphere_adaptive.out $GQABIN -u m,m^3,kg -q -Av -g 0.05m gqa.g sphere.r
run_capture gqa.missing_density.out expect_failure $GQABIN -u m,m^3,kg -g 250mm-50mm -f density_table.txt -Aw gqa.g missing_density.r
MASS_AIR_AW="`extract_last_number 'Average total weight:' gqa.mass_air.aw.out`"
MASS_AIR_AM="`extract_last_number 'Average total weight:' gqa.mass_air.am.out`"
@@ -257,6 +280,7 @@ assert_close 1001 "$MASS_MIX_AW" 0.001 "mixed solid+air modeled weight with -Aw"
assert_close "$MASS_MIX_AW" "$MASS_MIX_AM" 0.001 "-Aw and -Am agree on mixed modeled air"
assert_close 1000 "$MASS_MIX_U0" 0.001 "-U 0 excludes modeled air from weight"
assert_close 4.18879 "$SPHERE_VOLUME" 0.01 "adaptive sphere volume refines beyond symmetric first pass"
assert_contains "Density 123 on region /missing_density.r is not defined in the density table." gqa.missing_density.out "undefined density produces an error"
if [ $STATUS = 0 ] ; then
+13
View File
@@ -68,6 +68,7 @@ set(
sca_view.mged
search_exec.mged
sed.mged
sed_reset.mged
set_perspective.mged
setview.mged
size.mged
@@ -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")
@@ -187,6 +199,7 @@ distclean(${mged_outfiles})
cmakefiles(
CMakeLists.txt
csg_edit.mged
mged.tcl
regression_resources.tcl
search_exec_procs.tcl
+31
View File
@@ -119,4 +119,35 @@ if {![catch {get $invalid_inner}]} {
}
# ARBN uses one independent thickness per plane. Exercise unequal offsets so
# the test also verifies the plane ordering and inward normal convention.
set arbn_outer inside_arbn_outer.s
set arbn_inner inside_arbn_inner.s
set arbn_thicknesses {1 2 3 4 1.5 0.5}
in $arbn_outer arbn 6 \
1 0 0 10 -1 0 0 0 \
0 1 0 10 0 -1 0 0 \
0 0 1 10 0 0 -1 0
if {[catch {inside $arbn_outer $arbn_inner {*}$arbn_thicknesses} result]} {
error "inside failed for ARBN: $result"
}
set arbn_definition [get $arbn_inner]
set expected_offsets {9 -2 7 -4 8.5 -0.5}
for {set plane 0} {$plane < 6} {incr plane} {
set equation [lindex $arbn_definition [expr {4 + 2 * $plane}]]
set actual_offset [lindex $equation 3]
if {abs($actual_offset - [lindex $expected_offsets $plane]) > $plane_tolerance} {
error "ARBN inside plane $plane has offset $actual_offset: $arbn_definition"
}
}
facetize -n inside_arbn_inner_nmg.s $arbn_inner
set invalid_arbn_inner inside_arbn_invalid.s
if {![catch {inside $arbn_outer $invalid_arbn_inner {*}[lrepeat 6 6]}]} {
error "ARBN inside accepted thicknesses that collapse the solid"
}
if {![catch {get $invalid_arbn_inner}]} {
error "ARBN inside wrote an invalid collapsed solid"
}
puts "*** 'arb' testing completed ***"
+406
View File
@@ -962,8 +962,140 @@ foreach test [list \
csg_matrix_mouse_check {*}$test
}
puts "--- Repeated graphical object transforms ---"
proc csg_matrix_repeated_mouse_check {} {
global orig_gui transform csg_mouse_dm_pixels csg_mouse_view_size
global csg_drag_scale
set prim csg_matrix_repeat.s
set inner csg_matrix_repeat_inner.c
set outer csg_matrix_repeat_outer.c
set saved_orig_gui $orig_gui
set expected [csg_edit_identity_matrix]
set failed [catch {
in $prim sph 0 0 0 10
comb $inner u $prim
comb $outer u $inner
set orig_gui 0
for {set step 0} {$step < 16} {incr step} {
e $outer
center 0 0 0
size $csg_mouse_view_size
setview 0 0 0
dm size $csg_mouse_dm_pixels $csg_mouse_dm_pixels
_mged_press oill
_mged_ill -e -i 1 /$outer/$inner/$prim
_mged_matpick 2
if {[status state] ne "OBJ EDIT" || $transform ne "e"} {
error "step $step did not enter object editing"
}
set view_before [csg_edit_view_state]
if {$step % 2 == 0} {
press Rotate
dm con r z 100 100
set operation [csg_matrix_axis_rotation z 10]
set motion {dm motion 140 100}
} else {
press Scale
dm am s 100 100
set operation [csg_matrix_scale all $csg_drag_scale]
set motion {dm motion 164 100}
}
set motion_code [catch {uplevel #0 $motion} motion_result]
if {$motion_code != 0 && $motion_code != 2} {
error "step $step mouse motion failed: $motion_result"
}
dm idle
press accept
set expected [csg_matrix_multiply $operation $expected]
csg_edit_comb_leaf_matrix_at $inner $prim $expected
csg_edit_view_state_at $view_before
d $outer
}
} failure]
set orig_gui $saved_orig_gui
catch {press reject}
catch {Z}
foreach object [list $outer $inner $prim] {
catch {kill $object}
}
if {$failed} {
puts " FAIL: repeated object mouse rotations/scales ($failure)"
} else {
puts " PASS: repeated object mouse rotations/scales"
}
}
csg_matrix_repeated_mouse_check
puts "--- All ARB point, edge, and face states ---"
proc csg_edit_sed_point {attribute} {
set serialized [get_sed]
set index [lsearch -exact $serialized $attribute]
if {$index < 0 || $index + 1 >= [llength $serialized]} {
error "get_sed has no $attribute: $serialized"
}
return [lindex $serialized [expr {$index + 1}]]
}
proc csg_arb_repeated_mouse_check {} {
global orig_gui csg_bv_coord_half_range csg_mouse_dm_pixels
global csg_mouse_view_size
set source csg_lifecycle_arb5.s
set prim csg_arb_repeat.s
set saved_orig_gui $orig_gui
set expected {}
set failed [catch {
cp $source $prim
e $prim
center 0 0 0
size $csg_mouse_view_size
setview 0 0 0
dm size $csg_mouse_dm_pixels $csg_mouse_dm_pixels
set orig_gui 0
sed $prim
press {Move Edges}
press {Move Point 5}
set original_z [lindex [db get $source V5] 2]
for {set step 0} {$step < 64} {incr step} {
if {$step > 0 && $step % 8 == 0} {
press sedit
press {Move Edges}
press {Move Point 5}
}
set mouse_x [expr {150 + 2 * ($step % 5)}]
set mouse_y [expr {150 + 2 * (($step * 3) % 5)}]
M 1 $mouse_x $mouse_y
set expected [list \
[expr {$mouse_x / $csg_bv_coord_half_range * $csg_mouse_view_size / 2.0}] \
[expr {$mouse_y / $csg_bv_coord_half_range * $csg_mouse_view_size / 2.0}] \
$original_z]
set actual [csg_edit_sed_point V5]
if {[csg_edit_point_distance $actual $expected] > 0.000001} {
error "step $step expected V5 $expected, got $actual"
}
}
press accept
d $prim
if {[csg_edit_point_distance [db get $prim V5] $expected] > 0.000001} {
error "accepted V5 differs from the final mouse target"
}
foreach vertex {V1 V2 V3 V4} {
csg_edit_point_matches_source $source $vertex $prim
}
arb_standard_storage_check arb5 $prim
} failure]
set orig_gui $saved_orig_gui
catch {press reject}
catch {d $prim}
catch {kill $prim}
if {$failed} {
puts " FAIL: repeated ARB5 mouse movement ($failure)"
} else {
puts " PASS: repeated ARB5 mouse movement"
}
}
csg_arb_repeated_mouse_check
# Every ARB menu entry is checked independently. The variants do not share
# menu indices, so sampling one edge or face would miss precisely the class of
# integration failure this test is intended to catch. Move each selection a
@@ -1322,6 +1454,280 @@ csg_edit_sequence_check csg_lifecycle_dsp.s "Toggle Smooth" {
{press {Toggle Smooth}}
} [list csg_edit_scalar_at sm 1]
puts "--- Specialized mouse editing and menu inventory ---"
proc csg_edit_prepare_mouse_view {} {
global csg_dispatch_view_size
center 0 0 0
size $csg_dispatch_view_size
setview 0 0 0
}
array set csg_mouse_covered {}
proc csg_mouse_case {source menu_items label commands validator} {
global csg_mouse_covered
csg_edit_sequence_check $source $label $commands $validator
set type [lindex [db get $source] 0]
if {![info exists csg_mouse_covered($type)]} {
set csg_mouse_covered($type) {}
}
foreach item $menu_items {
if {[lsearch -exact $csg_mouse_covered($type) $item] < 0} {
lappend csg_mouse_covered($type) $item
}
}
}
proc csg_edit_menu_inventory_check {source ignored} {
global csg_mouse_covered
if {[catch {
Z
e $source
sed $source
set actual [mmenu_get 0]
set type [lindex [db get $source] 0]
set covered {}
if {[info exists csg_mouse_covered($type)]} {
set covered $csg_mouse_covered($type)
}
foreach item $actual {
set tested [expr {[lsearch -exact $covered $item] >= 0}]
set exempt [expr {[lsearch -exact $ignored $item] >= 0}]
if {$tested == $exempt} {
error "menu entry '$item' needs exactly one mouse-test or exclusion"
}
}
foreach item [concat $covered $ignored] {
if {[lsearch -exact $actual $item] < 0} {
error "recorded menu entry '$item' is absent from $actual"
}
}
press reject
d $source
} failure]} {
puts " FAIL: menu inventory $source ($failure)"
catch {press reject}
catch {Z}
} else {
puts " PASS: menu inventory $source"
}
}
csg_edit_prepare_mouse_view
set csg_special_mouse_target [list \
[expr {512.0 / $csg_bv_coord_half_range * $csg_dispatch_view_size / 2.0}] \
[expr {512.0 / $csg_bv_coord_half_range * $csg_dispatch_view_size / 2.0}] 0]
set csg_special_mouse_scale [expr {1.0 + 2047.0 / ($csg_bv_coord_half_range * 4.0)}]
set csg_special_mouse_shrink [expr {
1.0 / (1.0 + 1024.0 / ($csg_bv_coord_half_range * 4.0))}]
csg_mouse_case csg_lifecycle_pipe.s {{Move Point}} "mouse select and move point" {
{csg_edit_prepare_mouse_view}
{press {Select Point}}
{p 0 0 0}
{press {Move Point}}
{M 1 512 512}
} [list csg_edit_point_at V0 $csg_special_mouse_target]
in csg_mouse_pipe_topology.s pipe 4 \
0 0 0 1 2 3 \
0 0 30 1 2 3 \
30 40 80 1 2 3 \
80 80 100 1 2 3
csg_mouse_case csg_mouse_pipe_topology.s {{Select Point} {Move Point}} "mouse select point" {
{csg_edit_prepare_mouse_view}
{press {Select Point}}
{M 1 0 0}
{press {Move Point}}
{M 1 512 512}
} [list csg_edit_point_at V0 $csg_special_mouse_target]
csg_mouse_case csg_mouse_pipe_topology.s {{Append Point}} "mouse append point" {
{csg_edit_prepare_mouse_view}
{press {Append Point}}
{M 1 512 512}
} [list csg_edit_point_at V4 {5 5 0}]
csg_mouse_case csg_mouse_pipe_topology.s {{Prepend Point}} "mouse prepend point" {
{csg_edit_prepare_mouse_view}
{press {Prepend Point}}
{M 1 512 512}
} [list csg_edit_point_at V0 {5 5 0}]
csg_mouse_case csg_mouse_pipe_topology.s {{Split Segment}} "mouse split segment" {
{csg_edit_prepare_mouse_view}
{press {Select Point}}
{p 0 0 0}
{press {Split Segment}}
{M 1 512 512}
} [list csg_edit_point_at V1 {5 5 0}]
foreach {item attribute initial} {
{Set Point OD} O0 5
{Set Point ID} I0 3
{Set Point Bend} R0 6
} {
csg_mouse_case csg_lifecycle_pipe.s [list $item] "mouse $item" [list \
{csg_edit_prepare_mouse_view} \
{press {Select Point}} {p 0 0 0} [list press $item] \
{M 1 0 -1024}] \
[list csg_edit_scalar_at $attribute [expr {$initial * $csg_special_mouse_shrink}]]
}
foreach {item attributes initial} {
{Set Pipe OD} {O0 O1 O2 O3} {5 5 6 6}
{Set Pipe ID} {I0 I1 I2 I3} {3 3 2 0}
{Set Pipe Bend} {R0 R1 R2 R3} {6 7 10 8}
} {
set expected {}
foreach value $initial {
lappend expected [expr {$value * $csg_special_mouse_shrink}]
}
csg_mouse_case csg_lifecycle_pipe.s [list $item] "mouse $item" [list \
{csg_edit_prepare_mouse_view} \
[list press $item] {M 1 0 -1024}] \
[list csg_edit_scalar_values_at $attributes $expected]
}
proc csg_edit_bot_vertices_moved {source moved delta prim} {
set vertices [db get $source V]
for {set index 0} {$index < [llength $vertices]} {incr index} {
set expected [lindex $vertices $index]
if {[lsearch -exact $moved $index] >= 0} {
set translated {}
foreach value $expected shift $delta {
lappend translated [expr {$value + $shift}]
}
set expected $translated
}
csg_edit_list_value_at V $index $expected $prim
}
}
csg_mouse_case csg_lifecycle_bot.s {{Move Vertex}} "mouse move vertex" {
{csg_edit_prepare_mouse_view}
{press {Pick Vertex}}
{p 0}
{press {Move Vertex}}
{M 1 512 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0} \
$csg_special_mouse_target]
csg_mouse_case csg_lifecycle_bot.s {{Pick Vertex} {Move Vertex}} "mouse pick and move vertex" {
{csg_edit_prepare_mouse_view}
{press {Pick Vertex}}
{M 1 0 0}
{press {Move Vertex}}
{M 1 512 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0} \
$csg_special_mouse_target]
csg_mouse_case csg_lifecycle_bot.s {{Move Edge}} "mouse move edge" {
{csg_edit_prepare_mouse_view}
{press {Pick Edge}}
{p 0 1}
{press {Move Edge}}
{M 1 1024 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0 1} {10 5 0}]
csg_mouse_case csg_lifecycle_bot.s {{Pick Edge} {Move Edge}} "mouse pick and move edge" {
{csg_edit_prepare_mouse_view}
{press {Pick Edge}}
{M 1 512 512}
{press {Move Edge}}
{M 1 1024 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0 1} {10 5 0}]
csg_mouse_case csg_lifecycle_bot.s {{Move Triangle}} "mouse move triangle" {
{csg_edit_prepare_mouse_view}
{press {Pick Triangle}}
{p 0 1 2}
{press {Move Triangle}}
{M 1 512 512}
} [list csg_edit_bot_vertices_moved csg_lifecycle_bot.s {0 1 2} \
$csg_special_mouse_target]
csg_mouse_case csg_lifecycle_ebm.s {{Extrude Depth}} "mouse extrusion depth" {
{csg_edit_prepare_mouse_view}
{press {Extrude Depth}}
{M 1 0 2047}
} [list csg_edit_scalar_at H [expr {4.0 * $csg_special_mouse_scale}]]
csg_mouse_case csg_lifecycle_vol.s {{Threshold (low)}} "mouse low threshold" {
{csg_edit_prepare_mouse_view}
{press {Threshold (low)}}
{p 10}
{M 1 0 2047}
} [list csg_edit_scalar_at lo [expr {int(10.0 * $csg_special_mouse_scale)}]]
csg_mouse_case csg_lifecycle_vol.s {{Voxel Size (X Y Z)}} "mouse voxel size" {
{csg_edit_prepare_mouse_view}
{press {Voxel Size (X Y Z)}}
{M 1 0 2047}
} [list csg_edit_point_at size [list \
$csg_special_mouse_scale $csg_special_mouse_scale $csg_special_mouse_scale]]
csg_mouse_case csg_lifecycle_vol.s {{Threshold (hi)}} "mouse high threshold" {
{csg_edit_prepare_mouse_view}
{press {Threshold (hi)}}
{M 1 0 -1024}
} [list csg_edit_scalar_at hi [expr {int(255.0 * $csg_special_mouse_shrink)}]]
csg_mouse_case csg_lifecycle_dsp.s {{Set X}} "mouse X scale" {
{csg_edit_prepare_mouse_view}
{press {Set X}}
{M 1 0 2047}
} [list csg_edit_matrix_component_at stom 0 $csg_special_mouse_scale]
foreach {item index} {{Set Y} 5 {Set ALT} 10} {
csg_mouse_case csg_lifecycle_dsp.s [list $item] "mouse $item" [list \
{csg_edit_prepare_mouse_view} \
[list press $item] {M 1 0 2047}] \
[list csg_edit_matrix_component_at stom $index $csg_special_mouse_scale]
}
foreach type {arbn brep} {
set source csg_lifecycle_${type}.s
csg_edit_sequence_check $source "generic mouse translation" {
{csg_edit_prepare_mouse_view}
{keypoint 0 0 0}
{press sxy}
{M 1 512 512}
} [list csg_edit_bbox_translated $source $csg_special_mouse_target]
}
# Every specialized menu entry must be accounted for. Exclusions are menu
# headings, non-positional actions/settings, or the BOT triangle picker (tested
# under Xmin because it needs Tk). A new entry without a mouse case or
# explicit exclusion fails here.
foreach {source ignored} {
csg_lifecycle_pipe.s {
{PIPE MENU} {Next Point} {Previous Point} {Delete Point}
}
csg_lifecycle_bot.s {
{BOT MENU} {Pick Triangle} {Move Vertex List}
{Split Edge} {Split Face} {Fuse Vertices} {Fuse Faces}
{Delete Triangle} {Select Mode} {Select Orientation}
{Set flags} {Set Face Thickness} {Set Face Mode}
}
csg_lifecycle_dsp.s {
{DSP MENU} Name {Toggle Smooth} {Set Data Source}
}
csg_lifecycle_ebm.s {
{EBM MENU} {File Name} {File Size (W N)}
}
csg_lifecycle_vol.s {
{VOL MENU} {File Name} {File Size (X Y Z)}
}
csg_lifecycle_arbn.s {}
csg_lifecycle_brep.s {}
} {
csg_edit_menu_inventory_check $source $ignored
}
# A view-space request must remain view-space even when the editor's current
# coordinate system is object-relative. This is the fast path underlying the
# corresponding real mouse drag in the Xmin GUI test.
set csg_saved_coords [rset var coords]
set csg_saved_transform [rset var transform]
if {[catch {
Z
e csg_lifecycle_bot.s
sed csg_lifecycle_bot.s
center 0 0 0
rset var coords o
rset var transform v
knob -i -v aX 1
if {[csg_edit_point_distance [center] {-1 0 0}] > 0.000001} {
error "explicit view knob left center at [center]"
}
} csg_view_override_error]} {
puts " FAIL: view-space translation with object coordinates ($csg_view_override_error)"
} else {
puts " PASS: view-space translation with object coordinates"
}
catch {press reject}
catch {Z}
rset var coords $csg_saved_coords
rset var transform $csg_saved_transform
file delete -force $csg_data_file
file delete -force $csg_dsp_file
+33
View File
@@ -43,4 +43,37 @@ foreach {arb_type face expected_type} $arb_extrude_cases {
arb_standard_storage_check $expected_type $prim
}
# A one-inch extrusion must extend a face by 25.4 base millimeters through
# both the direct libged command and MGED's interactive solid editor.
set inch_in_mm 25.4
set tolerance 0.001
foreach mode {direct interactive} {
set suffix "_inch_$mode"
in_arb8 extrude $suffix
set prim "extrude_arb8${suffix}.s"
set base_x [lindex [db get $prim V1] 0]
units in
if {$mode eq "direct"} {
if {[catch {edarb extrude $prim 1234 1} result]} {
error "inch edarb extrusion failed: $result"
}
} else {
Z
e $prim
sed $prim
if {[catch {extrude 1234 1} result]} {
error "inch MGED extrusion failed: $result"
}
accept
Z
}
units mm
set new_x [lindex [db get $prim V5] 0]
if {abs($new_x - $base_x - $inch_in_mm) > $tolerance} {
error "inch $mode extrusion moved V5 by [expr {$new_x - $base_x}] mm"
}
}
puts "*** 'extrude' testing completed ***"
+23
View File
@@ -31,6 +31,7 @@ in tor_pe.s tor 0 0 0 0 0 1 20 2
prim_edit_check tor_pe.s "Set Radius 1" 30 r_a 30 -1
prim_edit_check tor_pe.s "Set Radius 2" 5 r_h 5 -1
prim_edit_mouse_check tor_pe.s "Set Radius 1" r_a 30 -1
prim_edit_mouse_check tor_pe.s "Set Radius 2" r_h 5 -1
kill tor_pe.s
puts "--- ELL (ellipsoid: A, B, C) ---"
@@ -39,6 +40,8 @@ prim_edit_check ell_pe.s "Set A" 8 A 8 0
prim_edit_check ell_pe.s "Set B" 6 B 6 1
prim_edit_check ell_pe.s "Set C" 4 C 4 2
prim_edit_mouse_check ell_pe.s "Set A" A 8 0
prim_edit_mouse_check ell_pe.s "Set B" B 6 1
prim_edit_mouse_check ell_pe.s "Set C" C 4 2
kill ell_pe.s
puts "--- RPC (right parabolic cylinder: H, r) ---"
@@ -46,6 +49,7 @@ in rpc_pe.s rpc 0 0 0 0 0 10 0 5 0 3
prim_edit_check rpc_pe.s "Set H" 20 H 20 2
prim_edit_check rpc_pe.s "Set r" 6 r 6 -1
prim_edit_mouse_check rpc_pe.s "Set H" H 20 2
prim_edit_mouse_check rpc_pe.s "Set r" r 6 -1
kill rpc_pe.s
puts "--- RHC (right hyperbolic cylinder: H, r, c) ---"
@@ -54,6 +58,8 @@ prim_edit_check rhc_pe.s "Set H" 20 H 20 2
prim_edit_check rhc_pe.s "Set r" 6 r 6 -1
prim_edit_check rhc_pe.s "Set c" 3 c 3 -1
prim_edit_mouse_check rhc_pe.s "Set r" r 6 -1
prim_edit_mouse_check rhc_pe.s "Set H" H 20 2
prim_edit_mouse_check rhc_pe.s "Set c" c 3 -1
kill rhc_pe.s
puts "--- EPA (elliptical paraboloid: H, r_1, r_2) ---"
@@ -62,6 +68,8 @@ prim_edit_check epa_pe.s "Set H" 20 H 20 2
prim_edit_check epa_pe.s "Set A" 8 r_1 8 -1
prim_edit_check epa_pe.s "Set B" 4 r_2 4 -1
prim_edit_mouse_check epa_pe.s "Set H" H 20 2
prim_edit_mouse_check epa_pe.s "Set A" r_1 8 -1
prim_edit_mouse_check epa_pe.s "Set B" r_2 4 -1
kill epa_pe.s
puts "--- EHY (elliptical hyperboloid: H, r_1, c) ---"
@@ -70,6 +78,8 @@ prim_edit_check ehy_pe.s "Set H" 20 H 20 2
prim_edit_check ehy_pe.s "Set A" 8 r_1 8 -1
prim_edit_check ehy_pe.s "Set c" 3 c 3 -1
prim_edit_mouse_check ehy_pe.s "Set A" r_1 8 -1
prim_edit_mouse_check ehy_pe.s "Set H" H 20 2
prim_edit_mouse_check ehy_pe.s "Set c" c 3 -1
kill ehy_pe.s
puts "--- PART (particle: H, r_v, r_h) ---"
@@ -78,6 +88,8 @@ prim_edit_check part_pe.s "Set H" 20 H 20 2
prim_edit_check part_pe.s "Set v" 4 r_v 4 -1
prim_edit_check part_pe.s "Set h" 3 r_h 3 -1
prim_edit_mouse_check part_pe.s "Set v" r_v 4 -1
prim_edit_mouse_check part_pe.s "Set H" H 20 2
prim_edit_mouse_check part_pe.s "Set h" r_h 3 -1
kill part_pe.s
puts "--- TGC (truncated general cone: H, A, B, C, D) ---"
@@ -91,6 +103,10 @@ prim_edit_check tgc_pe.s "Set B" 3 B 3 1
prim_edit_check tgc_pe.s "Set C" 3 C 3 0
prim_edit_check tgc_pe.s "Set D" 4 D 4 1
prim_edit_mouse_check tgc_pe.s "Set H" H 8 2
prim_edit_mouse_check tgc_pe.s "Set A" A 4 0
prim_edit_mouse_check tgc_pe.s "Set B" B 3 1
prim_edit_mouse_check tgc_pe.s "Set C" C 3 0
prim_edit_mouse_check tgc_pe.s "Set D" D 4 1
kill tgc_pe.s
puts "--- ETO (elliptical torus: r, C, D) ---"
@@ -100,6 +116,7 @@ prim_edit_check eto_pe.s "Set r" 8 r 8 -1
prim_edit_check eto_pe.s "Set D" 1.5 r_d 1.5 -1
prim_edit_mouse_check eto_pe.s "Set r" r 8 -1
prim_edit_mouse_check eto_pe.s "Set C" C 5 0
prim_edit_mouse_check eto_pe.s "Set D" r_d 1.5 -1
kill eto_pe.s
puts "--- HYP (one-sheet hyperboloid: H, A, B, c) ---"
@@ -111,6 +128,9 @@ prim_edit_check hyp_pe.s "Set A" 2 A 10 1
prim_edit_check hyp_pe.s "Set B" 2 b 4 -1
prim_edit_check hyp_pe.s "Set c" 0.5 bnr 0.25 -1
prim_edit_mouse_check hyp_pe.s "Set H" H 20 2
prim_edit_mouse_check hyp_pe.s "Set A" A 10 1
prim_edit_mouse_check hyp_pe.s "Set B" b 4 -1
prim_edit_mouse_check hyp_pe.s "Set c" bnr 0.25 -1
kill hyp_pe.s
puts "--- SUPERELL (superellipsoid: A, B, C) ---"
@@ -119,6 +139,8 @@ prim_edit_check superell_pe.s "Set A" 5 A 5 0
prim_edit_check superell_pe.s "Set B" 4 B 4 1
prim_edit_check superell_pe.s "Set C" 3 C 3 2
prim_edit_mouse_check superell_pe.s "Set A" A 5 0
prim_edit_mouse_check superell_pe.s "Set B" B 4 1
prim_edit_mouse_check superell_pe.s "Set C" C 3 2
kill superell_pe.s
puts "--- CLINE (cylinder line: H, R, plate thickness) ---"
@@ -129,6 +151,7 @@ prim_edit_check cline_pe.s "Set R" 4 R 4 -1
prim_edit_check cline_pe.s "Set plate thickness" 0.4 T 0.4 -1
prim_edit_mouse_check cline_pe.s "Set H" H 8 2
prim_edit_mouse_check cline_pe.s "Set R" R 4 -1
prim_edit_mouse_check cline_pe.s "Set plate thickness" T 0.4 -1
kill cline_pe.s
puts "--- NMG (non-manifold geometry: Pick Edge, Move Edge, Next EU) ---"
+64
View File
@@ -0,0 +1,64 @@
puts "*** Testing 'sed_reset' command ***"
proc sed_reset_check {primitive edit_commands} {
e $primitive
sed $primitive
if {[status state] ne "SOL EDIT"} {
error "$primitive entered [status state], not SOL EDIT"
}
set original [get_sed]
foreach command $edit_commands {
uplevel #0 $command
}
if {[get_sed] eq $original} {
error "$primitive edit did not change the working geometry"
}
sed_reset
if {[status state] ne "SOL EDIT"} {
error "$primitive reset entered [status state], not SOL EDIT"
}
if {[get_sed] ne $original} {
error "$primitive reset did not restore the original geometry"
}
press reject
if {[status state] ne "VIEWING"} {
error "$primitive reject entered [status state], not VIEWING"
}
d $primitive
}
if {[catch {
in sed_reset_arb8.s arb8 \
0 0 0 10 0 0 10 10 0 0 10 0 \
0 0 10 10 0 10 10 10 10 0 10 10
sed_reset_check sed_reset_arb8.s {
{press {Move Faces}}
{press {Move Face 1234}}
{p 0 0 -1}
}
} failure]} {
puts "FAIL: ARB8 face move reset ($failure)"
catch {press reject}
catch {Z}
} else {
puts "PASS: ARB8 face move reset"
}
if {[catch {
make sed_reset_cline.s cline
sed_reset_check sed_reset_cline.s {
{press {Move End H}}
{p 3 4 12}
}
} failure]} {
puts "FAIL: CLINE edit reset ($failure)"
catch {press reject}
catch {Z}
} else {
puts "PASS: CLINE edit reset"
}
puts "PASS: 'sed_reset' testing completed"
+4 -2
View File
@@ -93,6 +93,7 @@ if(BRLCAD_X11_PROVIDER_RESOLVED STREQUAL "XMIN" AND
"PLOT3_ASC_BIN=$<TARGET_FILE:plot3-asc>"
)
mged_add_xmin_gui_fixture(help "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(edit-interaction "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(bot "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(editors "mged;dm-tkswrast;m35.g")
mged_add_xmin_gui_fixture(query-pattern "mged;dm-tkswrast;m35.g")
@@ -212,18 +213,19 @@ cmakefiles(
mged/mged_xmin_gui.sh
mged/xmin_gui_bot.tcl
mged/xmin_gui_controls.tcl
mged/xmin_gui_edit.tcl
mged/xmin_gui_edit_interaction.tcl
mged/xmin_gui_editors.tcl
mged/xmin_gui_help.tcl
mged/xmin_gui_lod.tcl
mged/xmin_gui_overlaps.tcl
mged/xmin_gui_pipe_oracle.tcl
mged/xmin_gui_query_pattern.tcl
mged/xmin_gui_render_outputs.tcl
mged/xmin_gui_search_exec.tcl
mged/xmin_gui_shotvis.tcl
mged/xmin_gui_test.tcl
mged/xmin_gui_workflows.tcl
mged/xmin_gui_edit.tcl
mged/xmin_gui_pipe_oracle.tcl
)
# Local Variables:
File diff suppressed because it is too large Load Diff
+41 -1
View File
@@ -50,6 +50,18 @@ proc ::mged::xmin::help::exercise_command_manual {top} {
::gui::test::require {
[llength $selection] == 1 && [$toc get $selection] eq "rt"
} "man rt did not select and load the rt manual page"
event generate $toc <<ListboxSelect>>
::mged::gui::test::settle
man search
set html_view [$browser component browser].htmlview
set html_widget [$html_view html]
$html_view goto {#hierarchy_performance}
::mged::gui::test::settle
::gui::test::require {[lindex [$html_widget yview] 0] > 0.0} \
"manual browser did not follow an in-page hyperlink"
::gui::test::require {
[catch {man xmin_page_that_does_not_exist} message] &&
[string first "couldn't find manual page" $message] >= 0
@@ -151,10 +163,38 @@ proc ::mged::xmin::help::exercise_html_manual {top} {
set mged_browser [file join $::env(GUI_TEST_DIR) missing-browser]
::mged::gui::test::invoke $top {Help Manual}
set fallback $top.man
set viewer $fallback.html
::gui::test::require {
[winfo exists $fallback] && [winfo ismapped $fallback] &&
[string length [string trim [$fallback.text get 1.0 end]]] > 100
[winfo exists $viewer] && [$viewer title] eq "Table of Contents"
} "Manual did not load MGED's internal HTML viewer"
set special_path [file join [pwd] {manual path} {contents#1.html}]
::gui::test::require {
[::mged::manual::local_path \
[::mged::manual::file_uri $special_path]] eq \
[file normalize $special_path]
} "internal Manual viewer did not preserve special path characters"
set html [$viewer html]
set contents [$html text text]
::gui::test::require {
[string first "Preface" $contents] >= 0 &&
[string first "background-color" $contents] < 0
} "internal Manual viewer did not render its table of contents"
$viewer goto mged.html#csg
::mged::gui::test::settle
set contents [$html text text]
::gui::test::require {
[$viewer title] eq "Mged User's Manual" &&
[string first "Modeling With CSG" $contents] >= 0
} "internal Manual viewer did not follow its document links"
$fallback.f.back invoke
::mged::gui::test::settle
::gui::test::require {[$viewer title] eq "Table of Contents"} \
"internal Manual viewer Back button did not restore the prior page"
destroy $fallback
}
+17 -4
View File
@@ -53,12 +53,23 @@ endif(BRLCAD_ENABLE_TCL)
# Archer plugins
set(
archer_utility_FILES
plugins/Utility/attrGroupsDisplayUtilityP.tcl
plugins/Utility/botUtilityP.tcl
plugins/Utility/lodUtilityP.tcl
plugins/Utility/README
plugins/Utility/attrGroupsDisplayUtilityP.tcl
plugins/Utility/attrGroupsDisplayUtilityP/AttrGroupsDisplayUtilityP.tcl
plugins/Utility/botUtilityP.tcl
plugins/Utility/botUtilityP/BotUtilityP.tcl
plugins/Utility/lodUtilityP.tcl
plugins/Utility/lodUtilityP/LODUtilityP.tcl
)
set(archer_wizard_FILES
plugins/Wizards/humanwizard.tcl
plugins/Wizards/humanwizard/HumanWizard.tcl
plugins/Wizards/tankwizard.tcl
plugins/Wizards/tankwizard/TankWizard.tcl
plugins/Wizards/tankwizard/images/tank.png
plugins/Wizards/tirewizard.tcl
plugins/Wizards/tirewizard/TireWizard.tcl
)
set(archer_wizard_FILES plugins/Wizards/humanwizard.tcl plugins/Wizards/tankwizard.tcl plugins/Wizards/tirewizard.tcl)
if(BRLCAD_ENABLE_TCL)
brlcad_adddata(archer_utility_FILES plugins/archer/Utility)
@@ -77,6 +88,8 @@ endif(BRLCAD_ENABLE_TCL)
set(
archer_ignore_files
CMakeLists.txt
plugins/Commands/README
plugins/Core/README
${archer_utility_FILES}
${archer_wizard_FILES}
${archer_txt}
+1 -1
View File
@@ -752,7 +752,7 @@ tessellate_part(struct creo_conv_info *cinfo, ProMdl model, struct bu_vls **snam
}
/* Check solidity */
success = bg_trimesh_solid((int)vert_tree->curr_vert, (size_t)faces.size() / 3,
success = !bg_trimesh_solid((int)vert_tree->curr_vert, (size_t)faces.size() / 3,
vert_tree->the_array, &faces[0], NULL);
/* If it's not solid and we're testing solidity, keep trying... */
+30 -18
View File
@@ -23,10 +23,20 @@
#include "InformationGatherer.h"
#include "bu/log.h"
// flush results, if we have any, then exit
static void reportAndExit(const std::string &message, const std::string &result = "")
{
if (!result.empty())
bu_log("%s%s", result.c_str(), result.back() == '\n' ? "" : "\n");
bu_exit(BRLCAD_ERROR, "%s", message.c_str());
}
std::string getCmdPath(std::string exeDir, const char* cmd) {
char buf[MAXPATHLEN] = {0};
if (!bu_dir(buf, MAXPATHLEN, exeDir.c_str(), cmd, BU_DIR_EXT, NULL))
bu_exit(BRLCAD_ERROR, "Couldn't find %s, aborting.\n", cmd);
reportAndExit("Couldn't find " + std::string(cmd) + ", aborting.\n");
return std::string(buf);
}
@@ -58,13 +68,13 @@ getSurfaceArea(Options* opt, std::map<std::string, std::string> UNUSED(map), std
bu_process_create(&p, rtarea_av, BU_PROCESS_HIDE_WINDOW | BU_PROCESS_OUT_EQ_ERR);
if (bu_process_pid(p) <= 0) {
bu_exit(BRLCAD_ERROR, "Problem with getSurfaceArea, aborting\n");
reportAndExit("Problem with getSurfaceArea, aborting\n");
}
char buffer[128];
std::string result = "";
std::string result;
int read_cnt = 0;
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, 128-1, buffer)) > 0) {
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, sizeof(buffer) - 1, buffer)) > 0) {
/* NOTE: read does not ensure null-termination, thus buffersize-1 */
buffer[read_cnt] = '\0';
result += buffer;
@@ -80,7 +90,7 @@ getSurfaceArea(Options* opt, std::map<std::string, std::string> UNUSED(map), std
try {
surfArea += stod(result);
} catch (const std::invalid_argument& ia) {
bu_exit(BRLCAD_ERROR, "Invalid argument for getSurfaceArea. Got (%s), aborting.\n", ia.what());
reportAndExit("Invalid argument for getSurfaceArea. Got (" + std::string(ia.what()) + "), aborting.\n", result);
}
}
}
@@ -200,7 +210,7 @@ getVerificationData(struct ged* UNUSED(g), Options* opt, std::map<std::string, s
struct bu_process* p;
int read_cnt = 0;
char buffer[1024] = {0};
std::string result = "";
std::string result;
std::string ncpu(std::to_string(opt->getNCPU()));
std::string dFile(opt->getDensityFile());
@@ -233,21 +243,23 @@ getVerificationData(struct ged* UNUSED(g), Options* opt, std::map<std::string, s
bu_process_create(&p, const_cast<const char**>(gqa_av_vec.data()), BU_PROCESS_HIDE_WINDOW | BU_PROCESS_OUT_EQ_ERR);
if (bu_process_pid(p) <= 0) {
bu_exit(BRLCAD_ERROR, "Problem in getVerificationData gqa process creation, aborting\n");
reportAndExit("Problem in getVerificationData gqa process creation, aborting\n");
}
result = "";
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, 1024-1, buffer)) > 0) {
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, sizeof(buffer) - 1, buffer)) > 0) {
buffer[read_cnt] = '\0';
result += buffer;
}
if (bu_process_wait_n(&p, 0) != 0) {
bu_exit(BRLCAD_ERROR, "Problem collecting gqa volume and mass, aborting\n");
const int gqa_status = bu_process_wait_n(&p, 0);
// sanity - we should have clean status code and not the "no density found" message
const bool no_density_data = result.find(no_density_msg) != std::string::npos;
if (gqa_status != 0 && (!no_density_data || !dFile.empty())) {
reportAndExit("Problem collecting gqa volume and mass, aborting\n", result);
}
// make sure we did not get 'no density file' error message
if (result.find(no_density_msg) == std::string::npos) {
if (!no_density_data) {
// NOTE: tmp_val is zeroed at each parseLabeledNumber call
if (parseLabeledNumber(tmp_val, result, volume_label))
volume += tmp_val;
@@ -263,17 +275,17 @@ getVerificationData(struct ged* UNUSED(g), Options* opt, std::map<std::string, s
bu_process_create(&p, const_cast<const char**>(gqa_av_vec.data()), BU_PROCESS_HIDE_WINDOW | BU_PROCESS_OUT_EQ_ERR);
if (bu_process_pid(p) <= 0) {
bu_exit(BRLCAD_ERROR, "Problem in getVerificationData gqa process creation, aborting\n");
reportAndExit("Problem in getVerificationData gqa process creation, aborting\n");
}
result = "";
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, 1024-1, buffer)) > 0) {
result.clear();
while ((read_cnt = bu_process_read_n(p, BU_PROCESS_STDOUT, sizeof(buffer) - 1, buffer)) > 0) {
buffer[read_cnt] = '\0';
result += buffer;
}
if (bu_process_wait_n(&p, 0) != 0) {
bu_exit(BRLCAD_ERROR, "Problem collecting gqa volume, aborting\n");
reportAndExit("Problem collecting gqa volume, aborting\n", result);
}
if (parseLabeledNumber(tmp_val, result, volume_label))
@@ -381,7 +393,7 @@ InformationGatherer::getMainComp()
ged_exec_exists(g, 2, cmd);
std::string res = bu_vls_addr(g->ged_result_str);
if (res != "1") {
bu_exit(BRLCAD_ERROR, "Could not find component (%s), aborting.\n", topname);
reportAndExit("Could not find component (" + topcomp + "), aborting.\n");
}
int entities = getNumEntities(opt->getTopComp());
+3
View File
@@ -34,6 +34,9 @@
// OpenCV header files
#include <opencv2/opencv.hpp>
#if CV_VERSION_MAJOR >= 5
#include <opencv2/geometry.hpp>
#endif
#include <opencv2/core/core.hpp>
#include "opencv2/highgui/highgui.hpp"
+3 -2
View File
@@ -43,6 +43,7 @@ main(int argc, char *argv[])
int i, j;
int db_index;
int c;
int ret;
const char **av;
struct ged *gedp;
@@ -103,14 +104,14 @@ main(int argc, char *argv[])
bu_exit(1, usage, argv[0]);
}
(void)ged_exec_gqa(gedp, j, av);
ret = ged_exec_gqa(gedp, j, av);
if (bu_vls_strlen(gedp->ged_result_str) > 0)
bu_log("%s", bu_vls_addr(gedp->ged_result_str));
ged_close(gedp);
bu_free((void *)av, "av");
return 0;
return ret;
}
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+22 -1
View File
@@ -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)
+70
View File
@@ -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(&center, &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(&center, &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;
}
+69
View File
@@ -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;
}
+123
View File
@@ -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();
}
+106
View File
@@ -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();
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 );
+74
View File
@@ -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");
+149
View File
@@ -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, &degree_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(&quotient, &remainder, &dividend, &constant_divisor);
if (!poly_close(&quotient, 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(&quotient, &remainder, &linear, &higher_divisor);
if (!poly_close(&quotient, 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}
+14
View File
@@ -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)) {
+18 -12
View File
@@ -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;
}
+48 -43
View File
@@ -22,59 +22,54 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "vmath.h"
#include "bu/log.h"
#include "bu/malloc.h"
#include "bu/str.h"
int
bu_scan_fastf_t(int *c, const char *src, const char *delim, size_t n, ...)
{
va_list ap;
int offset = 0;
int current_n = 0, part_n = 0;
int len, delim_len;
int current_n = 0;
size_t i;
char *delim_fmt;
if (UNLIKELY(!delim || n < 1)) {
if (c) {
*c = 0;
}
if (UNLIKELY(!delim || !*delim || n < 1)) {
return 0;
}
va_start(ap, n);
/* TODO: we should just simply skip all occurrences of delim chars
* after reading a fastf_t, avoid dynamic memory allocation and
* avoid scanf/sscanf (we don't need to scan the delimiter, just
* skip chars in a loop until none are found.
*/
delim_len = (int)strlen(delim);
/* + 3 here to make room for the two characters '%' and 'n' as
* well as the terminating '\0'
*/
delim_fmt = (char *)bu_malloc(delim_len + 3, "bu_scan_fastf_t");
bu_strlcpy(delim_fmt, delim, delim_len + 1);
bu_strlcat(delim_fmt, "%n", delim_len + 3);
for (i = 0; i < n; i++) {
/* Read in the next fastf_t */
double scan = 0;
double scan = 0.0;
fastf_t *arg;
if (src)
part_n = sscanf(src + offset, "%lf%n", &scan, &len);
else
part_n = scanf("%lf%n", &scan, &len);
if (src) {
const char *input = src + offset;
char *end;
current_n += part_n;
offset += len;
if (part_n != 1) {
break;
scan = strtod(input, &end);
if (end == input) {
break;
}
offset += (int)(end - input);
} else {
int len = 0;
if (scanf("%lf%n", &scan, &len) != 1) {
break;
}
offset += len;
}
current_n++;
arg = va_arg(ap, fastf_t *);
if (arg) {
*arg = scan;
@@ -84,25 +79,35 @@ bu_scan_fastf_t(int *c, const char *src, const char *delim, size_t n, ...)
break;
}
/* Make sure that a delimiter is present */
if (src)
part_n = sscanf(src + offset, delim_fmt, &len);
else
part_n = scanf(delim_fmt, &len);
if (src) {
const char *input = src + offset;
offset += len;
while (*input && strchr(delim, *input)) {
input++;
}
if (input == src + offset) {
break;
}
offset += (int)(input - (src + offset));
} else {
int character;
int delimiter_count = 0;
/* as delim_fmt should only have a %n and that doesn't get
* counted in the scanf return, make sure the return is 0 as
* no values should be scanned.
*/
if (part_n != 0 || len != delim_len) {
break;
while ((character = fgetc(stdin)) != EOF) {
if (!strchr(delim, character)) {
(void)ungetc(character, stdin);
break;
}
delimiter_count++;
offset++;
}
if (!delimiter_count) {
break;
}
}
}
va_end(ap);
bu_free(delim_fmt, "bu_scan_fastf_t");
if (c) {
*c = offset;
+10 -1
View File
@@ -210,7 +210,7 @@ mkstemp(char *file_template)
for (i=(int)start; i>=(int)end; i--) {
file_template[i] = replace[(int)(replacelen * ((double)rand() / (double)RAND_MAX))];
}
fd = open(file_template, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
fd = open(file_template, O_CREAT | O_EXCL | O_TRUNC | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
} while ((fd == -1) && (counter++ < 1000));
return fd;
@@ -285,6 +285,15 @@ bu_temp_file(char *filepath, size_t len)
return NULL;
}
/* Ensure the descriptor is binary before associating it with a stream. */
if (UNLIKELY(setmode(fd, O_BINARY) == -1)) {
perror("setmode");
close(fd);
bu_file_delete(tempfile);
bu_log(_TF_FAIL);
return NULL;
}
if (filepath) {
if (UNLIKELY(len < strlen(tempfile))) {
bu_log("INTERNAL ERROR: bu_temp_file filepath buffer size is insufficient (%zu < %zu)\n", len, strlen(tempfile));
+3
View File
@@ -42,6 +42,7 @@ set(
test_process.c
test_ptbl.c
test_realpath.c
test_scan.c
test_semaphore.c
test_snooze.c
test_sort.c
@@ -127,6 +128,7 @@ brlcad_add_test(NAME bu_list_empty COMMAND bu_test test_list 0)
brlcad_add_test(NAME bu_list_single COMMAND bu_test test_list 1)
brlcad_add_test(NAME bu_list_many COMMAND bu_test test_list 16)
brlcad_add_test(NAME bu_sscanf_compare COMMAND bu_sscanf)
brlcad_add_test(NAME bu_scan_fastf_t COMMAND bu_test test_scan)
brlcad_add_test(NAME bu_gethostname_lookup COMMAND bu_gethostname)
@@ -198,6 +200,7 @@ brlcad_add_test(NAME bu_file_mime_png COMMAND bu_test test_file_mime png 3 BU_
# bu_temp_filename testing #
####################################
brlcad_add_test(NAME bu_temp_filename COMMAND bu_test test_temp_filename "single")
brlcad_add_test(NAME bu_temp_binary COMMAND bu_test test_temp_filename "binary")
#BRLCAD_ADD_TEST(NAME bu_temp_filename COMMAND bu_test test_temp_filename "parallel")
#
+173
View File
@@ -0,0 +1,173 @@
/* T E S T _ S C A 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; see the file named COPYING for more
* information.
*/
/** @file test_scan.c
*
*/
#include "common.h"
#include <string.h>
#include "test_api.h"
static int
scan_fastf_repeated_delimiters(void)
{
const char *input = "0.0,,0.0,1.0";
fastf_t first = -1.0;
fastf_t second = -1.0;
fastf_t third = -1.0;
int chars = -1;
return bu_scan_fastf_t(&chars, input, ",", 3,
&first, &second, &third) == 3
&& chars == (int)strlen(input)
&& ZERO(first)
&& ZERO(second)
&& EQUAL(third, 1.0);
}
static int
scan_fastf_mixed_delimiters(void)
{
const char *input = "1, ;2,, 3";
fastf_t first = -1.0;
fastf_t second = -1.0;
fastf_t third = -1.0;
int chars = -1;
return bu_scan_fastf_t(&chars, input, ",; ", 3,
&first, &second, &third) == 3
&& chars == (int)strlen(input)
&& EQUAL(first, 1.0)
&& EQUAL(second, 2.0)
&& EQUAL(third, 3.0);
}
static int
scan_fastf_percent_delimiter(void)
{
const char *input = "1%2%%3";
fastf_t first = -1.0;
fastf_t second = -1.0;
fastf_t third = -1.0;
int chars = -1;
return bu_scan_fastf_t(&chars, input, "%", 3,
&first, &second, &third) == 3
&& chars == (int)strlen(input)
&& EQUAL(first, 1.0)
&& EQUAL(second, 2.0)
&& EQUAL(third, 3.0);
}
static int
scan_fastf_reports_consumed_prefix(void)
{
fastf_t first = -1.0;
fastf_t second = -1.0;
int chars = -1;
return bu_scan_fastf_t(&chars, "1,not-a-number", ",", 2,
&first, &second) == 1
&& chars == 2
&& EQUAL(first, 1.0)
&& EQUAL(second, -1.0);
}
static int
scan_fastf_accepts_null_destinations(void)
{
fastf_t second = -1.0;
int chars = -1;
return bu_scan_fastf_t(&chars, "1,2", ",", 2,
NULL, &second) == 2
&& chars == 3
&& EQUAL(second, 2.0);
}
static int
scan_fastf_rejects_invalid_configuration(void)
{
fastf_t value = -1.0;
int chars = -1;
if (bu_scan_fastf_t(&chars, "1", NULL, 1, &value) != 0
|| chars != 0 || !EQUAL(value, -1.0))
return 0;
chars = -1;
if (bu_scan_fastf_t(&chars, "1", "", 1, &value) != 0
|| chars != 0 || !EQUAL(value, -1.0))
return 0;
chars = -1;
return bu_scan_fastf_t(&chars, "1", ",", 0) == 0 && chars == 0;
}
static int
test_scan(void)
{
int errors = 0;
TEST_API_CHECK(scan_fastf_repeated_delimiters(),
"bu_scan_fastf_t should skip repeated delimiters");
TEST_API_CHECK(scan_fastf_mixed_delimiters(),
"bu_scan_fastf_t should treat delim as a character set");
TEST_API_CHECK(scan_fastf_percent_delimiter(),
"bu_scan_fastf_t should treat percent as delimiter data");
TEST_API_CHECK(scan_fastf_reports_consumed_prefix(),
"bu_scan_fastf_t should report the deterministic consumed prefix");
TEST_API_CHECK(scan_fastf_accepts_null_destinations(),
"bu_scan_fastf_t should accept NULL value destinations");
TEST_API_CHECK(scan_fastf_rejects_invalid_configuration(),
"bu_scan_fastf_t should reset c for invalid configuration");
return errors ? BRLCAD_ERROR : BRLCAD_OK;
}
int
main(int UNUSED(argc), char *argv[])
{
if (bu_getprogname()[0] == '\0') {
bu_setprogname(argv[0]);
}
return test_scan();
}
/*
* Local Variables:
* tab-width: 8
* mode: C
* indent-tabs-mode: t
* c-file-style: "stroustrup"
* End:
* ex: shiftwidth=4 tabstop=8 cino=N-s
*/
+54
View File
@@ -35,6 +35,57 @@
#include "common.h"
#include <string.h>
#include "bio.h"
#include "bu/app.h"
#include "bu/file.h"
static int
temp_file_binary(void)
{
/* LF and DOS EOF expose text-mode translation on Windows. */
const unsigned char expected[] = {0x00, 0x0a, 0x1a, 0x0d, 0xff};
unsigned char actual[sizeof(expected) + 1] = {0};
char path[MAXPATHLEN] = {0};
size_t count;
FILE *fp = bu_temp_file(path, sizeof(path));
if (!fp) {
bu_log("temp_filename failure: cannot create temporary file\n");
return 1;
}
count = fwrite(expected, 1, sizeof(expected), fp);
if (fclose(fp) != 0 || count != sizeof(expected)) {
bu_log("temp_filename failure: cannot write binary data\n");
bu_file_delete(path);
return 1;
}
fp = fopen(path, "rb");
if (!fp) {
bu_log("temp_filename failure: cannot reopen temporary file\n");
bu_file_delete(path);
return 1;
}
count = fread(actual, 1, sizeof(actual), fp);
if (fclose(fp) != 0 || count != sizeof(expected) || memcmp(actual, expected, sizeof(expected))) {
bu_log("temp_filename failure: binary data changed (%zu bytes read, expected %zu)\n", count, sizeof(expected));
bu_file_delete(path);
return 1;
}
if (!bu_file_delete(path)) {
bu_log("temp_filename failure: cannot delete temporary file\n");
return 1;
}
return 0;
}
static void
temp_filename_thread(int cpu, void* ptr)
@@ -96,6 +147,9 @@ main(int argc, char *argv[])
if (strlen(under_size) != 4) /* buffer size -1 */
bu_exit(EXIT_FAILURE, "temp_filename failure: expected size [%d], got [%ld]]\n", 4, strlen(under_size));
} else if (!bu_strcmp(curr, "binary")) {
if (temp_file_binary())
return EXIT_FAILURE;
} else if (!bu_strcmp(curr, "parallel")) {
// test parallel / threaded -
uint8_t threads = bu_avail_cpus();
+10 -2
View File
@@ -891,8 +891,8 @@ Done:
int
ogl_share_dlist(struct dm *dmp1, struct dm *dmp2)
{
struct gl_vars *mvars = (struct gl_vars *)dmp1->i->m_vars;
struct pogl_vars *privars = (struct pogl_vars *)dmp1->i->dm_vars.priv_vars;
struct gl_vars *mvars;
struct pogl_vars *privars;
GLfloat backgnd[4];
GLfloat vf;
GLXContext old_glxContext;
@@ -900,6 +900,9 @@ ogl_share_dlist(struct dm *dmp1, struct dm *dmp2)
if (dmp1 == (struct dm *)NULL)
return BRLCAD_ERROR;
mvars = (struct gl_vars *)dmp1->i->m_vars;
privars = (struct pogl_vars *)dmp1->i->dm_vars.priv_vars;
if (dmp2 == (struct dm *)NULL) {
/* create a new graphics context for dmp1 with private display lists */
@@ -1053,6 +1056,11 @@ ogl_share_dlist(struct dm *dmp1, struct dm *dmp2)
glXDestroyContext(((struct dm_glxvars *)dmp2->i->dm_vars.pub_vars)->dpy, old_glxContext);
}
struct dm *target_dmp = dmp2 ? dmp2 : dmp1;
struct gl_vars *target_mvars = (struct gl_vars *)target_dmp->i->m_vars;
gl_setZBuffer(target_dmp, target_mvars->zbuffer_on);
gl_setLight(target_dmp, target_mvars->lighting_on);
return BRLCAD_OK;
}
+3 -9
View File
@@ -254,15 +254,9 @@ qtgl_open(void *ctx, void *UNUSED(interp), int argc, const char **argv)
if (bu_vls_strlen(&dmp->i->dm_pathName) == 0)
bu_vls_printf(&dmp->i->dm_pathName, ".dm_qtgl%d", count);
++count;
if (bu_vls_strlen(&dmp->i->dm_dName) == 0) {
char *dp;
dp = getenv("DISPLAY");
if (dp)
bu_vls_strcpy(&dmp->i->dm_dName, dp);
else
bu_vls_strcpy(&dmp->i->dm_dName, ":0.0");
}
/* The supplied QOpenGLWidget determines the Qt display. Keep dm_dName
* empty unless the caller supplied -d; DISPLAY is an X11 setting and
* cannot identify a Qt context on every supported platform. */
/* initialize dm specific variables */
pubvars->devmotionnotify = 0;
+3 -9
View File
@@ -246,15 +246,9 @@ swrast_open(void *ctx, void *UNUSED(interp), int argc, const char **argv)
if (bu_vls_strlen(&dmp->i->dm_pathName) == 0)
bu_vls_printf(&dmp->i->dm_pathName, ".dm_swrast%d", count);
++count;
if (bu_vls_strlen(&dmp->i->dm_dName) == 0) {
char *dp;
dp = getenv("DISPLAY");
if (dp)
bu_vls_strcpy(&dmp->i->dm_dName, dp);
else
bu_vls_strcpy(&dmp->i->dm_dName, ":0.0");
}
/* OSMesa is offscreen and has no display to discover. Leave dm_dName
* empty unless the caller supplied -d; a window-system wrapper can then
* select its own initialized display without inheriting X11 state. */
/* initialize dm specific variables */
pubvars->devmotionnotify = 0;
+10
View File
@@ -464,6 +464,16 @@ tkswrast_open(void *ctx, void *vinterp, int argc, const char **argv)
tv->orig_configureWin = dmp->i->dm_configureWin;
tv->orig_close = dmp->i->dm_close;
/* The wrapped offscreen backend deliberately has no default display.
* For this Tk presentation layer, use the display Tk actually opened.
* This remains correct for X11, Win32, and Aqua even when the process
* inherited an unrelated DISPLAY value. An explicit -d remains intact
* and is validated below by Tk_CreateWindowFromPath. */
if (bu_vls_strlen(&dmp->i->dm_dName) == 0) {
bu_vls_printf(&dmp->i->dm_dName, "%s.%d", Tk_DisplayName(tkwin),
Tk_ScreenNumber(tkwin));
}
char *cp = strrchr(bu_vls_addr(&dmp->i->dm_pathName), '.');
if (dmp->i->dm_top) {
tv->xtkwin = Tk_CreateWindowFromPath(interp, tkwin,
+13 -8
View File
@@ -88,11 +88,13 @@ wgl_share_dlist(struct dm *dmp1, struct dm *dmp2)
GLfloat backgnd[4];
GLfloat vf;
HGLRC old_glxContext;
struct gl_vars *mvars = (struct gl_vars *)dmp1->i->m_vars;
struct gl_vars *mvars;
if (dmp1 == (struct dm *)NULL)
return BRLCAD_ERROR;
mvars = (struct gl_vars *)dmp1->i->m_vars;
if (dmp2 == (struct dm *)NULL) {
/* create a new graphics context for dmp1 with private display lists */
@@ -244,6 +246,11 @@ wgl_share_dlist(struct dm *dmp1, struct dm *dmp2)
wglDeleteContext(old_glxContext);
}
struct dm *target_dmp = dmp2 ? dmp2 : dmp1;
struct gl_vars *target_mvars = (struct gl_vars *)target_dmp->i->m_vars;
gl_setZBuffer(target_dmp, target_mvars->zbuffer_on);
gl_setLight(target_dmp, target_mvars->lighting_on);
return BRLCAD_OK;
}
@@ -1033,13 +1040,11 @@ wgl_open(void *UNUSED(ctx), void *vinterp, int argc, const char *argv[])
++count;
if (bu_vls_strlen(&dmp->i->dm_dName) == 0) {
char *dp;
dp = getenv("DISPLAY");
if (dp)
bu_vls_strcpy(&dmp->i->dm_dName, dp);
else
bu_vls_strcpy(&dmp->i->dm_dName, ":0.0");
/* DISPLAY names an X server and is not meaningful to WGL. Unix-like
* shells on Windows may nevertheless export it, so use the screen of
* the Tk window that supplies the native HWND. */
bu_vls_printf(&dmp->i->dm_dName, "%s.%d", Tk_DisplayName(tkwin),
Tk_ScreenNumber(tkwin));
}
/* initialize dm specific variables */
+5 -5
View File
@@ -3,13 +3,11 @@ brlcad_find_package(manifold REQUIRED)
BRLCAD_Find_Package(TinyGLTF REQUIRED)
set(BOT_LIBS libged libbg libbu manifold::manifold tinygltf::tinygltf ${WINSOCK_LIB})
set(BOT_INTERNAL_INCLUDE_DIRS)
if(BRLCAD_ENABLE_OPENVDB)
set(BOT_LIBS ${BOT_LIBS} ${OpenVDB_LIBRARIES} ${IlmBase_LIBRARIES} ${Tbb_LIBRARIES})
set(BOT_LOCAL_INCLUDE_DIRS ${BOT_LOCAL_INCLUDE_DIRS} ${OpenVDB_INCLUDE_DIRS} ${Tbb_INCLUDE_DIRS} ${IlmBase_INCLUDE_DIRS})
list(APPEND BOT_INTERNAL_INCLUDE_DIRS ${BRLCAD_SOURCE_DIR}/src/librt/primitives/bot)
set(BOT_OPENVDB_DEFS HAVE_OPENVDB_BOT_EXTERIOR BRLCAD_OPENVDB)
endif(BRLCAD_ENABLE_OPENVDB)
@@ -56,11 +54,11 @@ if(BRLCAD_ENABLE_OPENVDB)
# PLUGIN_ONLY these flags are scoped to the plugin .so and do not
# pollute the main libged target.
set_source_files_properties(
remesh.cpp
bot_flood_exterior.cpp
bot_openvdb.cpp
PROPERTIES COMPILE_FLAGS "-w -fno-lto"
)
list(APPEND BOT_SRCS bot_flood_exterior.cpp)
list(APPEND BOT_SRCS bot_openvdb.cpp bot_flood_exterior.cpp)
endif(BRLCAD_ENABLE_OPENVDB)
set(
@@ -80,7 +78,7 @@ set(
ged_plugin_library(ged-bot
PLUGIN_ONLY
${BOT_SRCS}
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} ${BOT_INTERNAL_INCLUDE_DIRS}
INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}
SYSTEM_INCLUDES ${BOT_LOCAL_INCLUDE_DIRS}
LIBS ${BOT_LIBS}
DEFINITIONS "EIGEN_DONT_VECTORIZE" ${BOT_OPENVDB_DEFS}
@@ -89,6 +87,8 @@ ged_plugin_library(ged-bot
cmakefiles(
CMakeLists.txt
bot_flood_exterior.cpp
bot_openvdb.cpp
bot_openvdb.h
concurrentqueue.h
dump/ged_bot_dump.h
ged_bot.h
+2 -2
View File
@@ -28,7 +28,7 @@
*
* Algorithm
* ---------
* 1. Build an occupancy grid (solid / air) via rt_rtip_to_openvdb_occupancy
* 1. Build an occupancy grid (solid / air) via rt_rtip_to_occupancy_grid
* (implemented in bot_openvdb.cpp). Rays are shot in +X, +Y, +Z.
* A voxel is marked solid when any hit segment encloses its centre.
*
@@ -143,7 +143,7 @@ bot_flood_exterior_classify(struct rt_i *rtip,
/* Step 1: voxelise the model into a solid-occupancy grid. */
bu_log("bot flood exterior: voxel_size=%.4g\n", voxel_size);
openvdb::BoolGrid::Ptr solid = rt_rtip_to_openvdb_occupancy(rtip, voxel_size, &nx, &ny, &nz);
openvdb::BoolGrid::Ptr solid = rt_rtip_to_occupancy_grid(rtip, voxel_size, &nx, &ny, &nz);
bu_log("bot flood exterior: grid %dx%dx%d\n", nx, ny, nz);
/* Step 2: BFS flood fill to find exterior (water-reachable) voxels. */
@@ -1,7 +1,7 @@
/* B O T _ O P E N V D B . C P P
* BRL-CAD
*
* Copyright (c) 2024-2026 United States Government as represented by
* Copyright (c) 2024-2025 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* This library is free software; you can redistribute it and/or
@@ -17,24 +17,21 @@
* License along with this file; see the file named COPYING for more
* information.
*/
/** @file librt/primitives/bot/bot_openvdb.cpp
/** @file libged/bot/bot_openvdb.cpp
*
* Shared OpenVDB utilities for BoT processing.
* Shared OpenVDB utilities for the bot plugin.
*
* Four building blocks are implemented here:
* Three building blocks are implemented here:
*
* rt_bot_to_openvdb_sdf() — BoT → OpenVDB signed-distance field
* rt_bot_from_openvdb_sdf() — OpenVDB SDF → rt_bot_internal
* rt_bot_openvdb_repair() — OpenVDB level-set BoT repair
* rt_rtip_to_openvdb_occupancy() — raytrace a prepped rt_i into a BoolGrid
* bot_to_sdf() — BoT → OpenVDB signed-distance field
* sdf_to_bot() — OpenVDB SDF → rt_bot_internal
* rt_rtip_to_occupancy_grid() — raytrace a prepped rt_i into a BoolGrid
*
* See bot_openvdb.h for the public API documentation.
*/
#include "common.h"
#include "bot_openvdb.h"
#ifdef BRLCAD_OPENVDB
# include <cmath>
@@ -56,11 +53,8 @@
# include "rt/geom.h"
# include "raytrace.h"
int
rt_bot_openvdb_available(void)
{
return 1;
}
# include "bot_openvdb.h"
/* -----------------------------------------------------------------------
* botDataAdapter
@@ -90,10 +84,10 @@ struct botDataAdapter {
/* -----------------------------------------------------------------------
* rt_bot_to_openvdb_sdf
* bot_to_sdf
* ---------------------------------------------------------------------- */
openvdb::FloatGrid::Ptr
rt_bot_to_openvdb_sdf(struct rt_bot_internal *bot, double voxel_size)
bot_to_sdf(struct rt_bot_internal *bot, double voxel_size)
{
if (!bot || !bot->num_vertices || !bot->num_faces)
return openvdb::FloatGrid::Ptr();
@@ -134,7 +128,7 @@ rt_bot_to_openvdb_sdf(struct rt_bot_internal *bot, double voxel_size)
/* -----------------------------------------------------------------------
* rt_bot_from_openvdb_sdf
* sdf_to_bot
*
* Uses the class-based VolumeToMesh (rather than the volumeToMesh free
* function) so that relaxDisorientedTriangles=true is in effect. That
@@ -144,7 +138,7 @@ rt_bot_to_openvdb_sdf(struct rt_bot_internal *bot, double voxel_size)
* the same isovalue/adaptivity settings.
* ---------------------------------------------------------------------- */
struct rt_bot_internal *
rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity)
sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity)
{
if (!grid)
return NULL;
@@ -189,7 +183,7 @@ rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity)
obot->num_vertices = n_points;
obot->vertices = (fastf_t *)bu_malloc(n_points * 3 * sizeof(fastf_t),
"rt_bot_from_openvdb_sdf vertices");
"sdf_to_bot vertices");
for (size_t i = 0; i < n_points; i++) {
obot->vertices[i * 3 + X] = points[i].x();
obot->vertices[i * 3 + Y] = points[i].y();
@@ -197,7 +191,7 @@ rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity)
}
obot->num_faces = n_tris;
obot->faces = (int *)bu_malloc(n_tris * 3 * sizeof(int), "rt_bot_from_openvdb_sdf faces");
obot->faces = (int *)bu_malloc(n_tris * 3 * sizeof(int), "sdf_to_bot faces");
/* Walk each polygon pool, emitting triangles then splitting quads. */
size_t fidx = 0;
@@ -229,19 +223,9 @@ rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity)
return obot;
}
static void
free_openvdb_bot(struct rt_bot_internal *bot)
{
if (!bot)
return;
rt_bot_internal_free(bot);
BU_PUT(bot, struct rt_bot_internal);
}
/* -----------------------------------------------------------------------
* rt_bot_openvdb_repair
* bot_openvdb_repair
*
* Attempt to repair a non-manifold solid BoT using the OpenVDB level-set
* pipeline. Converts the mesh to a signed-distance field and extracts a
@@ -254,22 +238,19 @@ free_openvdb_bot(struct rt_bot_internal *bot)
* Caller is responsible for freeing the returned bot.
* ---------------------------------------------------------------------- */
struct rt_bot_internal *
rt_bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size, fastf_t *volume)
bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size)
{
if (!bot || bot->mode != RT_BOT_SOLID)
return NULL;
openvdb::FloatGrid::Ptr grid = rt_bot_to_openvdb_sdf(bot, voxel_size);
openvdb::FloatGrid::Ptr grid = bot_to_sdf(bot, voxel_size);
if (!grid) {
bu_log("rt_bot_openvdb_repair: meshToVolume failed\n");
bu_log("bot_openvdb_repair: meshToVolume failed\n");
return NULL;
}
/* Full voxel resolution (adaptivity=0) preserves shape best for repair. */
struct rt_bot_internal *cand = rt_bot_from_openvdb_sdf(grid, 0.0);
struct rt_bot_internal *cand = sdf_to_bot(grid, 0.0);
if (!cand || cand->num_faces == 0) {
bu_log("rt_bot_openvdb_repair: volumeToMesh produced no faces\n");
free_openvdb_bot(cand);
bu_log("bot_openvdb_repair: volumeToMesh produced no faces\n");
if (cand) { bu_free(cand->vertices, "verts"); bu_free(cand->faces, "faces"); BU_PUT(cand, struct rt_bot_internal); }
return NULL;
}
@@ -290,8 +271,9 @@ rt_bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size, fastf_t *v
}
manifold::Manifold mcheck(vcheck);
if (mcheck.Status() != manifold::Manifold::Error::NoError) {
bu_log("rt_bot_openvdb_repair: result is not manifold (unexpected)\n");
free_openvdb_bot(cand);
bu_log("bot_openvdb_repair: result is not manifold (unexpected)\n");
bu_free(cand->vertices, "verts"); bu_free(cand->faces, "faces");
BU_PUT(cand, struct rt_bot_internal);
return NULL;
}
@@ -301,34 +283,24 @@ rt_bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size, fastf_t *v
* and 2 of every triangle reverses the winding order globally and
* turns it into a valid outward-facing solid without touching the
* vertex data or rebuilding the mesh. */
fastf_t repair_volume = mcheck.Volume();
if (repair_volume == 0.0) {
bu_log("rt_bot_openvdb_repair: result has zero volume\n");
free_openvdb_bot(cand);
return NULL;
}
if (repair_volume < 0.0) {
bu_log("rt_bot_openvdb_repair: result is inside-out; flipping winding order\n");
if (mcheck.Volume() <= 0.0) {
bu_log("bot_openvdb_repair: result is inside-out; flipping winding order\n");
for (size_t i = 0; i < cand->num_faces; i++) {
int tmp = cand->faces[i * 3 + 1];
cand->faces[i * 3 + 1] = cand->faces[i * 3 + 2];
cand->faces[i * 3 + 2] = tmp;
}
repair_volume = -repair_volume;
}
if (volume)
*volume = repair_volume;
return cand;
}
/* -----------------------------------------------------------------------
* rt_rtip_to_openvdb_occupancy internals
* rt_rtip_to_occupancy_grid internals
*
* These hit/miss callbacks and the per-ray context are used only by
* rt_rtip_to_openvdb_occupancy below.
* rt_rtip_to_occupancy_grid below.
* ---------------------------------------------------------------------- */
struct OccRayCtx {
@@ -385,11 +357,11 @@ occ_hit(struct application *ap, struct partition *PartHeadp, struct seg *UNUSED(
/* -----------------------------------------------------------------------
* rt_rtip_to_openvdb_occupancy
* rt_rtip_to_occupancy_grid
* ---------------------------------------------------------------------- */
openvdb::BoolGrid::Ptr
rt_rtip_to_openvdb_occupancy(struct rt_i *rtip, double voxel_size,
int *nx, int *ny, int *nz)
rt_rtip_to_occupancy_grid(struct rt_i *rtip, double voxel_size,
int *nx, int *ny, int *nz)
{
int nx_m = (int)std::ceil((rtip->mdl_max[X] - rtip->mdl_min[X]) / voxel_size);
int ny_m = (int)std::ceil((rtip->mdl_max[Y] - rtip->mdl_min[Y]) / voxel_size);
@@ -478,20 +450,6 @@ rt_rtip_to_openvdb_occupancy(struct rt_i *rtip, double voxel_size,
return solid;
}
#else /* BRLCAD_OPENVDB */
int
rt_bot_openvdb_available(void)
{
return 0;
}
struct rt_bot_internal *
rt_bot_openvdb_repair(struct rt_bot_internal *UNUSED(bot), double UNUSED(voxel_size), fastf_t *UNUSED(volume))
{
return NULL;
}
#endif /* BRLCAD_OPENVDB */
@@ -1,7 +1,7 @@
/* B O T _ O P E N V D B . H
* BRL-CAD
*
* Copyright (c) 2024-2026 United States Government as represented by
* Copyright (c) 2024-2025 United States Government as represented by
* the U.S. Army Research Laboratory.
*
* This library is free software; you can redistribute it and/or
@@ -17,49 +17,31 @@
* License along with this file; see the file named COPYING for more
* information.
*/
/** @file librt/primitives/bot/bot_openvdb.h
/** @file libged/bot/bot_openvdb.h
*
* Shared OpenVDB utilities for BoT processing.
* Shared OpenVDB utilities for the bot plugin.
*
* Provides four building blocks used by remesh, repair, and the
* Provides three building blocks used by remesh, repair, and the
* flood-fill exterior classifier:
*
* rt_bot_to_openvdb_sdf() — BoT triangle mesh → FloatGrid SDF
* rt_bot_from_openvdb_sdf() — FloatGrid SDF → solid BoT
* rt_bot_openvdb_repair() — OpenVDB level-set BoT repair
* rt_rtip_to_openvdb_occupancy() — raytrace an rt_i into a BoolGrid
* bot_to_sdf() — BoT triangle mesh → OpenVDB FloatGrid SDF
* sdf_to_bot() — FloatGrid SDF → solid rt_bot_internal
* rt_rtip_to_occupancy_grid()— raytrace a prepped rt_i into a BoolGrid
*
* OpenVDB-specific declarations are guarded by BRLCAD_OPENVDB. Translation
* units using those declarations must be compiled with the OpenVDB headers
* and ABI definitions.
* All declarations are guarded by OPENVDB_ABI_VERSION_NUMBER so that
* the header is a no-op when compiled without OpenVDB. Every translation
* unit that includes this header is expected to be compiled with the
* OpenVDB ABI defines (i.e., BRLCAD_ENABLE_OPENVDB is ON).
*/
#ifndef LIBRT_BOT_OPENVDB_H
#define LIBRT_BOT_OPENVDB_H
#ifndef LIBGED_BOT_OPENVDB_H
#define LIBGED_BOT_OPENVDB_H
#include "common.h"
#include "rt/defines.h"
#include "rt/geom.h"
RT_EXPORT int rt_bot_openvdb_available(void);
/**
* Repair a non-manifold solid BoT using the OpenVDB level-set pipeline.
*
* @param bot Source BoT (read-only).
* @param voxel_size Voxel edge length in model units. Pass <= 0 to
* auto-size to bbox_diagonal / 100.
* @param volume Optional output for the absolute volume of the result.
* @return New rt_bot_internal on success, NULL on failure.
* Caller owns the result and must free it via
* rt_bot_internal_free() / BU_PUT().
*/
RT_EXPORT struct rt_bot_internal *rt_bot_openvdb_repair(struct rt_bot_internal *bot,
double voxel_size,
fastf_t *volume);
#ifdef BRLCAD_OPENVDB
# include "rt/geom.h"
# include "raytrace.h"
/* Suppress warnings from OpenVDB and its transitive headers. */
@@ -90,7 +72,7 @@ RT_EXPORT struct rt_bot_internal *rt_bot_openvdb_repair(struct rt_bot_internal *
* auto-size to bbox_diagonal / 100.
* @return FloatGrid SDF, or an empty Ptr on failure.
*/
RT_EXPORT openvdb::FloatGrid::Ptr rt_bot_to_openvdb_sdf(struct rt_bot_internal *bot, double voxel_size);
openvdb::FloatGrid::Ptr bot_to_sdf(struct rt_bot_internal *bot, double voxel_size);
/**
* Extract a solid BoT from an OpenVDB signed-distance field.
@@ -98,14 +80,31 @@ RT_EXPORT openvdb::FloatGrid::Ptr rt_bot_to_openvdb_sdf(struct rt_bot_internal *
* The output is always a closed 2-manifold (RT_BOT_SOLID, RT_BOT_CCW).
* Quads produced by volumeToMesh are split into two triangles each.
*
* @param grid Source SDF (e.g. from rt_bot_to_openvdb_sdf).
* @param grid Source SDF (e.g. from bot_to_sdf or meshToVolume).
* @param adaptivity Mesh simplification factor [0.0, 1.0].
* 0.0 = full voxel resolution; 1.0 = maximum merging.
* @return New rt_bot_internal on success, NULL on failure.
* Caller owns the result and must free it via
* rt_bot_internal_free() / BU_PUT().
*/
RT_EXPORT struct rt_bot_internal *rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Ptr grid, double adaptivity);
struct rt_bot_internal *sdf_to_bot(openvdb::FloatGrid::Ptr grid, double adaptivity);
/**
* Repair a non-manifold solid BoT using the OpenVDB level-set pipeline.
*
* Converts the mesh to a signed-distance field and extracts a
* guaranteed-manifold mesh from the 0-isovalue surface. The result is
* validated for manifoldness and positive volume (negative volume would
* indicate an inside-out reconstruction).
*
* @param bot Source BoT (read-only).
* @param voxel_size Voxel edge length in model units. Pass <= 0 to
* auto-size to bbox_diagonal / 100.
* @return New rt_bot_internal on success, NULL on failure.
* Caller owns the result and must free it via
* rt_bot_internal_free() / BU_PUT().
*/
struct rt_bot_internal *bot_openvdb_repair(struct rt_bot_internal *bot, double voxel_size);
/**
* Voxelize a prepped rt_i into a solid-occupancy BoolGrid.
@@ -120,12 +119,12 @@ RT_EXPORT struct rt_bot_internal *rt_bot_from_openvdb_sdf(openvdb::FloatGrid::Pt
* @param nx,ny,nz Output: grid dimensions including padding.
* @return BoolGrid with active voxels = solid interior cells.
*/
RT_EXPORT openvdb::BoolGrid::Ptr rt_rtip_to_openvdb_occupancy(struct rt_i *rtip,
double voxel_size,
int *nx, int *ny, int *nz);
openvdb::BoolGrid::Ptr rt_rtip_to_occupancy_grid(struct rt_i *rtip,
double voxel_size,
int *nx, int *ny, int *nz);
#endif /* BRLCAD_OPENVDB */
#endif /* LIBRT_BOT_OPENVDB_H */
#endif /* LIBGED_BOT_OPENVDB_H */
/*
* Local Variables:
+1 -1
View File
@@ -37,7 +37,7 @@
* eliminating the need for a separate global sampling phase.
*
* Flood fill (--flood-fill, requires OpenVDB)
* The model is voxelised via rt_rtip_to_openvdb_occupancy, then a
* The model is voxelised via rt_rtip_to_occupancy_grid, then a
* BFS flood-fill from the boundary finds all "water-reachable" voxels.
* A face is exterior when any of its six face-adjacent voxels is
* reachable.
+148 -87
View File
@@ -25,9 +25,11 @@
#include "common.h"
#ifdef BRLCAD_OPENVDB
# include "bot_openvdb.h"
#endif /* BRLCAD_OPENVDB */
#ifdef OPENVDB_ABI_VERSION_NUMBER
# include <openvdb/openvdb.h>
# include <openvdb/tools/VolumeToMesh.h>
# include <openvdb/tools/MeshToVolume.h>
#endif /* OPENVDB_ABI_VERSION_NUMBER */
#include "manifold/manifold.h"
@@ -47,52 +49,115 @@
#include "./ged_bot.h"
#ifdef BRLCAD_OPENVDB
#ifdef OPENVDB_ABI_VERSION_NUMBER
struct botDataAdapter {
struct rt_bot_internal *bot;
size_t polygonCount() const {
return bot->num_faces;
};
size_t pointCount() const {
return bot->num_vertices;
};
size_t vertexCount(size_t) const {
return 3;
};
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const {
int idx = bot->faces[(n*3)+v];
pos[X] = bot->vertices[(idx*3)+X];
pos[Y] = bot->vertices[(idx*3)+Y];
pos[Z] = bot->vertices[(idx*3)+Z];
return;
};
/* constructor */
botDataAdapter(struct rt_bot_internal *bip) : bot(bip) {}
};
static bool
bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal **obot,
struct rt_bot_internal *bot,
double voxel_size, double adaptivity)
bot_remesh_vdb(struct ged *UNUSED(gedp), struct rt_bot_internal *bot, double voxelSize)
{
if (bot->mode != RT_BOT_SOLID) {
bu_vls_printf(gedp->ged_result_str,
"ERROR: OpenVDB remesh requires a SOLID BoT.\n"
"Surface and plate-mode BoTs have no volume; "
"use Geogram remesh instead.\n");
return false;
const float exteriorBandWidth = 10.0;
const float interiorBandWidth = std::numeric_limits<float>::max();
struct botDataAdapter bda(bot);
openvdb::initialize();
bu_log("...voxelizing");
openvdb::math::Transform::Ptr xform = openvdb::math::Transform::createLinearTransform(voxelSize);
openvdb::FloatGrid::Ptr bot2vol = openvdb::tools::meshToVolume<openvdb::FloatGrid, botDataAdapter>(bda, *xform, exteriorBandWidth, interiorBandWidth);
#if 0
openvdb::io::File file("mesh.vdb");
openvdb::GridPtrVec grids;
grids.push_back(bot2vol);
file.write(grids);
file.close();
return false;
#endif
bu_log("...devoxelizing");
std::vector<openvdb::Vec3s> points;
std::vector<openvdb::Vec3I> triangles;
std::vector<openvdb::Vec4I> quadrilaterals;
openvdb::tools::volumeToMesh<openvdb::FloatGrid>(*bot2vol, points, triangles, quadrilaterals);
bu_log("...storing");
if (bot->vertices) {
bu_free(bot->vertices, "vertices");
bot->num_vertices = 0;
}
if (bot->faces) {
bu_free(bot->faces, "faces");
bot->num_faces = 0;
}
if (bot->normals) {
bu_free(bot->normals, "normals");
}
if (bot->face_normals) {
bu_free(bot->face_normals, "face normals");
}
bu_log("...voxelizing (voxel_size=%.4g, adaptivity=%.2f)\n",
voxel_size > 0.0 ? voxel_size : -1.0, adaptivity);
bot->num_vertices = points.size();
bot->vertices = (fastf_t *)bu_malloc(bot->num_vertices * ELEMENTS_PER_POINT * sizeof(fastf_t), "vertices");
for (size_t i = 0; i < points.size(); i++) {
bot->vertices[(i*3)+X] = points[i].x();
bot->vertices[(i*3)+Y] = points[i].y();
bot->vertices[(i*3)+Z] = points[i].z();
}
bot->num_faces = triangles.size() + (quadrilaterals.size() * 2);
bot->faces = (int *)bu_malloc(bot->num_faces * 3 * sizeof(int), "triangles");
for (size_t i = 0; i < triangles.size(); i++) {
bot->faces[(i*3)+X] = triangles[i].x();
bot->faces[(i*3)+Y] = triangles[i].y();
bot->faces[(i*3)+Z] = triangles[i].z();
}
size_t ntri = triangles.size();
for (size_t i = 0; i < quadrilaterals.size(); i++) {
bot->faces[((ntri+i)*3)+X] = quadrilaterals[i][0];
bot->faces[((ntri+i)*3)+Y] = quadrilaterals[i][1];
bot->faces[((ntri+i)*3)+Z] = quadrilaterals[i][2];
openvdb::FloatGrid::Ptr grid = rt_bot_to_openvdb_sdf(bot, voxel_size);
if (!grid) {
bu_vls_printf(gedp->ged_result_str, "ERROR: meshToVolume failed\n");
return false;
bot->faces[((ntri+i+1)*3)+X] = quadrilaterals[i][0];
bot->faces[((ntri+i+1)*3)+Y] = quadrilaterals[i][2];
bot->faces[((ntri+i+1)*3)+Z] = quadrilaterals[i][3];
}
bu_log("...extracting mesh\n");
bu_log("...done!\n");
struct rt_bot_internal *nbot = rt_bot_from_openvdb_sdf(grid, adaptivity);
if (!nbot || nbot->num_faces == 0) {
bu_vls_printf(gedp->ged_result_str, "ERROR: volumeToMesh produced no faces\n");
if (nbot) { bu_free(nbot->vertices, "verts"); bu_free(nbot->faces, "faces"); BU_PUT(nbot, struct rt_bot_internal); }
return false;
}
bu_log("...done! %zu vertices, %zu faces\n",
nbot->num_vertices, nbot->num_faces);
*obot = nbot;
return true;
return (points.size() > 0);
}
#else /* BRLCAD_OPENVDB */
#else /* OPENVDB_ABI_VERSION_NUMBER */
static bool
bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal **UNUSED(obot),
struct rt_bot_internal *UNUSED(bot),
double UNUSED(voxel_size), double UNUSED(adaptivity))
bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal *UNUSED(bot), double UNUSED(voxelSize))
{
bu_vls_printf(gedp->ged_result_str,
"WARNING: BoT remeshing is unavailable.\n"
@@ -101,7 +166,7 @@ bot_remesh_vdb(struct ged *gedp, struct rt_bot_internal **UNUSED(obot),
return false;
}
#endif /* BRLCAD_OPENVDB */
#endif /* OPENVDB_ABI_VERSION_NUMBER */
static int
bot_remesh_geogram(struct rt_bot_internal **obot, struct ged *gedp, struct rt_bot_internal *bot)
@@ -175,7 +240,7 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
struct _ged_bot_info *gb = (struct _ged_bot_info *)bs;
struct ged *gedp = gb->gedp;
const char *usage_string = "bot [options] remesh <objname> [output_bot]\n";
const char *usage_string = "bot [options] remesh <objname> <output_bot>\n";
const char *purpose_string = "Store a remeshed version of the BoT in object <output_bot>";
if (_bot_cmd_msgs(bs, argc, argv, usage_string, purpose_string)) {
return BRLCAD_OK;
@@ -192,17 +257,14 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
int print_help = 0;
int use_vdb = 0;
fastf_t vdb_voxel_size = 0.0; /* 0 = auto */
fastf_t vdb_adaptivity = 0.0; /* 0 = full voxel density */
struct bu_vls output_bot_name = BU_VLS_INIT_ZERO;
struct bu_opt_desc d[7];
BU_OPT(d[0], "h", "help", "", NULL, &print_help, "Print help");
BU_OPT(d[1], "o", "output", "oname", &bu_opt_vls, &output_bot_name, "Name to use for output BoT");
BU_OPT(d[2], "", "vdb", "", NULL, &use_vdb, "Use OpenVDB level-set remeshing (solid BoTs only)");
BU_OPT(d[3], "", "openvdb-voxel-size", "#", &bu_opt_fastf_t, &vdb_voxel_size, "OpenVDB voxel size in model units (default: bbox_diagonal/100)");
BU_OPT(d[4], "", "openvdb-adaptivity", "#", &bu_opt_fastf_t, &vdb_adaptivity, "OpenVDB mesh simplification [0.0=full density, 1.0=max simplification]");
BU_OPT_NULL(d[5]);
BU_OPT(d[0], "h", "help", "", NULL, &print_help, "Print help");
BU_OPT(d[1], "o", "output", "oname", &bu_opt_vls, &output_bot_name, "Name to use for output BoT");
BU_OPT(d[2], "", "vdb", "", NULL, &use_vdb, "Use OpenVDB based remeshing");
BU_OPT_NULL(d[3]);
argc--; argv++;
@@ -221,13 +283,15 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
}
const char *input_bot_name = gb->dp->d_namep;
if (gb->intern->idb_major_type != DB5_MAJORTYPE_BRLCAD ||
gb->intern->idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
if (gb->intern->idb_major_type != DB5_MAJORTYPE_BRLCAD || gb->intern->idb_minor_type != DB5_MINORTYPE_BRLCAD_BOT) {
bu_vls_printf(gedp->ged_result_str, "%s is not a BOT primitive\n", input_bot_name);
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
}
struct directory *dp_input = RT_DIR_NULL;
struct directory *dp_output = RT_DIR_NULL;
GED_CHECK_READ_ONLY(gedp, BRLCAD_ERROR);
if ((bu_vls_strlen(&output_bot_name) && argc > 1) || argc > 2) {
@@ -239,10 +303,11 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
if (!bu_vls_strlen(&output_bot_name) && argc == 2)
bu_vls_printf(&output_bot_name, "%s", argv[1]);
/* If no output name, overwrite the original. */
// If we've got no specified output, we're overwriting
if (!bu_vls_strlen(&output_bot_name))
bu_vls_printf(&output_bot_name, "%s", input_bot_name);
if (!BU_STR_EQUAL(input_bot_name, bu_vls_cstr(&output_bot_name))) {
GED_CHECK_EXISTS(gedp, bu_vls_cstr(&output_bot_name), LOOKUP_QUIET, BRLCAD_ERROR);
}
@@ -250,66 +315,61 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
struct rt_bot_internal *input_bot = (struct rt_bot_internal *)gb->intern->idb_ptr;
RT_BOT_CK_MAGIC(input_bot);
bu_log("INPUT BoT has %zu vertices and %zu faces\n",
input_bot->num_vertices, input_bot->num_faces);
bu_log("INPUT BoT has %zu vertices and %zu faces\n", input_bot->num_vertices, input_bot->num_faces);
struct rt_bot_internal *obot = NULL;
/* TODO: stash a backup if overwriting the original */
if (use_vdb) {
if (!bot_remesh_vdb(gedp, &obot, input_bot, vdb_voxel_size, vdb_adaptivity)) {
bool ok = bot_remesh_vdb(gedp, input_bot, 50);
if (!ok) {
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
}
} else {
struct rt_bot_internal *obot = NULL;
if (bot_remesh_geogram(&obot, gedp, input_bot) != BRLCAD_OK || !obot) {
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
}
}
if (!obot) {
bu_vls_printf(gedp->ged_result_str, "ERROR: remesh produced no output\n");
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
}
struct rt_db_internal intern;
RT_DB_INTERNAL_INIT(&intern);
intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
intern.idb_type = ID_BOT;
intern.idb_meth = &OBJ[ID_BOT];
intern.idb_ptr = (void *)obot;
bu_log("OUTPUT BoT has %zu vertices and %zu faces\n",
obot->num_vertices, obot->num_faces);
/* Write out the result. */
struct rt_db_internal intern;
RT_DB_INTERNAL_INIT(&intern);
intern.idb_major_type = DB5_MAJORTYPE_BRLCAD;
intern.idb_type = ID_BOT;
intern.idb_meth = &OBJ[ID_BOT];
intern.idb_ptr = (void *)obot;
const char *rname = bu_vls_cstr(&output_bot_name);
struct directory *dp;
if (BU_STR_EQUAL(input_bot_name, rname)) {
/* Overwrite in place. */
dp = gb->dp;
} else {
dp = db_diradd(gedp->dbip, rname, RT_DIR_PHONY_ADDR, 0,
RT_DIR_SOLID, (void *)&intern.idb_type);
const char *rname = bu_vls_cstr(&output_bot_name);
struct directory *dp = db_diradd(gedp->dbip, rname, RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, (void *)&intern.idb_type);
if (dp == RT_DIR_NULL) {
bu_vls_printf(gedp->ged_result_str, "Failed to create output BoT %s\n", rname);
bu_vls_printf(gedp->ged_result_str, "Failed to write out new BoT %s\n", rname);
rt_db_free_internal(&intern);
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
}
if (rt_db_put_internal(dp, gedp->dbip, &intern) < 0) {
bu_vls_printf(gedp->ged_result_str, "Failed to write out new BoT %s\n", rname);
rt_db_free_internal(&intern);
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
}
}
if (rt_db_put_internal(dp, gedp->dbip, &intern) < 0) {
bu_vls_printf(gedp->ged_result_str, "Failed to write output BoT %s\n", rname);
rt_db_free_internal(&intern);
bu_vls_free(&output_bot_name);
return BRLCAD_ERROR;
bu_vls_printf(gedp->ged_result_str, "Remesh complete\n");
return BRLCAD_OK;
}
rt_db_free_internal(&intern);
bu_vls_printf(gedp->ged_result_str, "Remesh complete\n");
bu_log("OUTPUT BoT has %zu vertices and %zu faces\n", input_bot->num_vertices, input_bot->num_faces);
if (BU_STR_EQUAL(input_bot_name, bu_vls_cstr(&output_bot_name))) {
dp_output = dp_input;
} else {
GED_DB_DIRADD(gedp, dp_output, bu_vls_cstr(&output_bot_name), RT_DIR_PHONY_ADDR, 0, RT_DIR_SOLID, (void *)&gb->intern->idb_type, BRLCAD_ERROR);
}
GED_DB_PUT_INTERN(gedp, dp_output, gb->intern, BRLCAD_ERROR);
bu_vls_free(&output_bot_name);
return BRLCAD_OK;
@@ -324,3 +384,4 @@ _bot_cmd_remesh(void *bs, int argc, const char **argv)
// c-file-style: "stroustrup"
// End:
// ex: shiftwidth=4 tabstop=8
+26 -13
View File
@@ -463,6 +463,9 @@ cmd_translate::exec(struct ged *gedp, void *u_data, int argc, const char **argv)
return _edit_xform_apply(ctx,
[&](struct rt_edit *s) -> int
{
vect_t to_vec_base;
VSCALE(to_vec_base, to_vec, s->local2base);
/* Resolve FROM position (k) */
point_t from_kp = VINIT_ZERO;
bool have_from = false;
@@ -514,7 +517,7 @@ cmd_translate::exec(struct ged *gedp, void *u_data, int argc, const char **argv)
}
} else if (do_abs) {
/* Absolute translate: to_vec IS the target position */
VMOVE(target, to_vec);
VMOVE(target, to_vec_base);
/* Keep unspecified axes at the current keypoint */
if (n_coord_flags > 0) {
if (!x_only) target[X] = s->e_keypoint[X];
@@ -532,17 +535,16 @@ cmd_translate::exec(struct ged *gedp, void *u_data, int argc, const char **argv)
/* Relative translate (default): to_vec is a delta */
vect_t delta = VINIT_ZERO;
if (n_coord_flags > 0) {
if (x_only) delta[X] = to_vec[X];
if (y_only) delta[Y] = to_vec[Y];
if (z_only) delta[Z] = to_vec[Z];
if (x_only) delta[X] = to_vec_base[X];
if (y_only) delta[Y] = to_vec_base[Y];
if (z_only) delta[Z] = to_vec_base[Z];
} else {
VMOVE(delta, to_vec);
VMOVE(delta, to_vec_base);
}
VADD2(target, s->e_keypoint, delta);
}
VMOVE(s->e_para, target);
s->e_inpara = 3;
rt_edit_set_translation_target(s, target);
rt_edit_set_edflag(s, RT_PARAMS_EDIT_TRANS);
rt_edit_process(s);
return BRLCAD_OK;
@@ -583,10 +585,11 @@ cmd_tra::exec(struct ged *gedp, void *u_data, int argc, const char **argv)
return _edit_xform_apply(ctx,
[&](struct rt_edit *s) -> int
{
vect_t target;
VADD2(target, s->e_keypoint, delta);
VMOVE(s->e_para, target);
s->e_inpara = 3;
vect_t delta_base;
point_t target;
VSCALE(delta_base, delta, s->local2base);
VADD2(target, s->e_keypoint, delta_base);
rt_edit_set_translation_target(s, target);
rt_edit_set_edflag(s, RT_PARAMS_EDIT_TRANS);
rt_edit_process(s);
return BRLCAD_OK;
@@ -618,6 +621,7 @@ _parse_pos_or_obj(point_t *pos, const char **argv, int argc, struct ged *gedp)
n++;
}
}
VSCALE(*pos, *pos, gedp->dbip->dbi_local2base);
return n;
}
@@ -1170,6 +1174,15 @@ cmd_scale::exec(struct ged *gedp, void *u_data, int argc, const char **argv)
}
}
/* Scale references define factors, so normalize model coordinates to
* the database-local numeric values used by this command. */
if (have_k)
VSCALE(k_vec, k_vec, gedp->dbip->dbi_base2local);
if (have_a)
VSCALE(a_vec, a_vec, gedp->dbip->dbi_base2local);
if (have_r)
VSCALE(r_vec, r_vec, gedp->dbip->dbi_base2local);
/* ---- Determine scale factors from whatever was provided -------- */
/* Helper: detect if the three vector components are all nearly equal */
auto vec_is_uniform = [](const vect_t v) -> bool {
@@ -2036,7 +2049,7 @@ _exec_desc_cmd_on_edit(struct rt_edit *s, struct ged *gedp,
s->e_inpara = _desc_cmd_inpara(cmd_desc);
rt_edit_set_edflag(s, cmd_desc->cmd_id);
rt_edit_process(s);
int edit_ret = rt_edit_process(s);
/* Forward any error message logged by the edit handler */
if (s->log_str && bu_vls_strlen(s->log_str) > 0) {
@@ -2046,7 +2059,7 @@ _exec_desc_cmd_on_edit(struct rt_edit *s, struct ged *gedp,
return BRLCAD_ERROR;
}
return BRLCAD_OK;
return edit_ret;
}
-2
View File
@@ -263,8 +263,6 @@ dp_tessellate(struct rt_bot_internal **obot, struct bu_vls *method_flag, struct
settings.strict = 0;
bu_vls_sprintf(method_flag, "REPAIR");
ret = rt_bot_repair(obot, bot, &settings);
if (ret == BRLCAD_OK && settings.used_openvdb)
bu_vls_sprintf(method_flag, "REPAIR_OPENVDB");
} else {
// Already a valid BoT - tessellate is a no-op.
*obot = NULL;
+1 -3
View File
@@ -701,7 +701,7 @@ facetize_collect_primitive_summary(struct _ged_facetize_state *s)
for (m_it = method_sets.begin(); m_it != method_sets.end(); ++m_it) {
total += m_it->second.size();
if (m_it->first == std::string("FAIL")) fail_cnt += m_it->second.size();
if (m_it->first == std::string("REPAIR") || m_it->first == std::string("REPAIR_OPENVDB")) repair_cnt += m_it->second.size();
if (m_it->first == std::string("REPAIR")) repair_cnt += m_it->second.size();
if (m_it->first == std::string("PLATE")) plate_cnt += m_it->second.size();
}
bu_vls_printf(s->primitive_summary, " %-43s %8zu\n", "Total solids evaluated", total);
@@ -712,8 +712,6 @@ facetize_collect_primitive_summary(struct _ged_facetize_state *s)
for (m_it = method_sets.begin(); m_it != method_sets.end(); ++m_it) {
if (m_it->first == std::string("REPAIR")) {
bu_vls_printf(s->primitive_summary, " %-41s %8zu\n", "bot repair", m_it->second.size());
} else if (m_it->first == std::string("REPAIR_OPENVDB")) {
bu_vls_printf(s->primitive_summary, " %-41s %8zu\n", "OpenVDB level-set repair", m_it->second.size());
} else if (m_it->first == std::string("PLATE")) {
bu_vls_printf(s->primitive_summary, " %-41s %8zu\n", "plate extrusion", m_it->second.size());
} else if (m_it->first == std::string("FAIL")) {
+12
View File
@@ -247,6 +247,18 @@ ged_cmd_same(const char *cmd1, const char *cmd2)
return (c1 == c2) ? 1 : 0;
}
extern "C" int
ged_cmd_valid(const char *cmd, const char *func)
{
if (!cmd || !ged_cmd_exists(cmd)) return 1;
if (!func) return 0;
if (!ged_cmd_exists(func)) return 1;
return ged_cmd_same(cmd, func) ? 0 : 2;
}
/* Edit distance lookup retained for help suggestions */
extern "C" int
ged_cmd_lookup(const char **ncmd, const char *cmd)
+115 -33
View File
@@ -138,30 +138,37 @@ static const char *p_nmgin[] = {
#define ARB_FACE_COUNT 6
#define ARB_MAX_PLANE_INTERSECTIONS 20
/* Verify that every plane bounds a non-degenerate convex volume. Translating
* the planes of an existing convex ARB cannot make their intersection
* the planes of an existing convex polyhedron cannot make their intersection
* unbounded, but excessive or unequal thicknesses can eliminate a face or
* collapse the volume. */
static int
arb_in_planes_valid(plane_t planes[ARB_FACE_COUNT], const struct bn_tol *tol)
in_planes_valid(const plane_t *planes, size_t plane_count,
const struct bn_tol *tol)
{
point_t vertices[ARB_MAX_PLANE_INTERSECTIONS];
int plane_used[ARB_FACE_COUNT] = {0};
size_t vertex_count = 0;
point_t basis[3];
plane_t basis_plane;
size_t basis_count = 0;
int have_volume = 0;
for (size_t i = 0; i < ARB_FACE_COUNT - 2; i++) {
for (size_t j = i + 1; j < ARB_FACE_COUNT - 1; j++) {
for (size_t k = j + 1; k < ARB_FACE_COUNT; k++) {
if (!planes || plane_count < 4)
return 0;
int *plane_used = (int *)bu_calloc(plane_count, sizeof(int),
"inside plane use flags");
for (size_t i = 0; i < plane_count - 2; i++) {
for (size_t j = i + 1; j < plane_count - 1; j++) {
for (size_t k = j + 1; k < plane_count; k++) {
point_t candidate;
int outside = 0;
if (bg_make_pnt_3planes(candidate, planes[i], planes[j], planes[k]) < 0)
continue;
for (size_t face = 0; face < ARB_FACE_COUNT; face++) {
for (size_t face = 0; face < plane_count; face++) {
if (DIST_PNT_PLANE(candidate, planes[face]) > tol->dist) {
outside = 1;
break;
@@ -173,35 +180,39 @@ arb_in_planes_valid(plane_t planes[ARB_FACE_COUNT], const struct bn_tol *tol)
plane_used[i] = 1;
plane_used[j] = 1;
plane_used[k] = 1;
VMOVE(vertices[vertex_count], candidate);
vertex_count++;
}
}
}
for (size_t face = 0; face < ARB_FACE_COUNT; face++)
if (!plane_used[face])
return 0;
/* At least four non-coplanar vertices are required for a volume. */
for (size_t i = 0; i + 2 < vertex_count; i++) {
for (size_t j = i + 1; j + 1 < vertex_count; j++) {
for (size_t k = j + 1; k < vertex_count; k++) {
plane_t vertex_plane;
if (bg_make_plane_3pnts(vertex_plane, vertices[i], vertices[j], vertices[k], tol) < 0)
if (have_volume)
continue;
for (size_t l = 0; l < vertex_count; l++) {
if (l == i || l == j || l == k)
continue;
if (!NEAR_ZERO(DIST_PNT_PLANE(vertices[l], vertex_plane), tol->dist))
return 1;
if (!basis_count) {
VMOVE(basis[0], candidate);
basis_count = 1;
} else if (basis_count == 1 &&
DIST_PNT_PNT_SQ(basis[0], candidate) > tol->dist_sq) {
VMOVE(basis[1], candidate);
basis_count = 2;
} else if (basis_count == 2 &&
bg_make_plane_3pnts(basis_plane, basis[0], basis[1],
candidate, tol) == 0) {
VMOVE(basis[2], candidate);
basis_count = 3;
} else if (basis_count == 3 &&
!NEAR_ZERO(DIST_PNT_PLANE(candidate, basis_plane),
tol->dist)) {
have_volume = 1;
}
}
}
}
return 0;
int all_planes_used = 1;
for (size_t face = 0; face < plane_count; face++) {
if (!plane_used[face]) {
all_planes_used = 0;
break;
}
}
bu_free(plane_used, "inside plane use flags");
return all_planes_used && have_volume;
}
@@ -227,7 +238,7 @@ arb7in(struct ged *gedp,
planes[i][W] -= thick[i];
}
if (!arb_in_planes_valid(planes, tol)) {
if (!in_planes_valid((const plane_t *)planes, ARB_FACE_COUNT, tol)) {
bu_vls_printf(gedp->ged_result_str,
"Cannot find a valid inside arb7: thicknesses eliminate a face or collapse the volume\n");
return BRLCAD_ERROR;
@@ -250,6 +261,43 @@ arb7in(struct ged *gedp,
}
/* Offset each outward-facing ARBN plane toward the interior. */
static int
arbnin(struct ged *gedp, struct rt_db_internal *ip, const fastf_t *thick,
const struct bn_tol *tol)
{
struct rt_arbn_internal *arbn =
(struct rt_arbn_internal *)ip->idb_ptr;
RT_ARBN_CK_MAGIC(arbn);
plane_t *planes = (plane_t *)bu_calloc(arbn->neqn, sizeof(plane_t),
"inside ARBN planes");
for (size_t i = 0; i < arbn->neqn; i++) {
fastf_t normal_length = MAGNITUDE(arbn->eqn[i]);
if (!isfinite(normal_length) || normal_length <= SQRT_SMALL_FASTF) {
bu_vls_printf(gedp->ged_result_str,
"Cannot find a valid inside ARBN: plane %zu has an invalid normal\n",
i);
bu_free(planes, "inside ARBN planes");
return BRLCAD_ERROR;
}
HSCALE(planes[i], arbn->eqn[i], 1.0 / normal_length);
planes[i][W] -= thick[i];
}
if (!in_planes_valid((const plane_t *)planes, arbn->neqn, tol)) {
bu_vls_printf(gedp->ged_result_str,
"Cannot find a valid inside ARBN: thicknesses eliminate a face or collapse the volume\n");
bu_free(planes, "inside ARBN planes");
return BRLCAD_ERROR;
}
bu_free(arbn->eqn, "arbn planes");
arbn->eqn = planes;
return BRLCAD_OK;
}
/* finds inside arbs */
static int
arbin(struct ged *gedp,
@@ -925,12 +973,22 @@ ged_inside_internal(struct ged *gedp, struct rt_db_internal *ip, int argc, const
if (ip->idb_type == ID_ARB8) {
/* find the comgeom arb type, & reorganize */
int uvec[8], svec[11];
int issues = 0;
struct bu_vls error_msg = BU_VLS_INIT_ZERO;
if (rt_arb_get_cgtype(&cgtype, (struct rt_arb_internal *)ip->idb_ptr, &wdbp->wdb_tol, uvec, svec) == 0) {
bu_vls_printf(gedp->ged_result_str, "%s: BAD ARB\n", o_name);
return BRLCAD_ERROR;
}
(void)rt_arb_validate(NULL,
(const struct rt_arb_internal *)ip->idb_ptr,
&wdbp->wdb_tol, &issues);
if (issues & RT_ARB_VALIDATE_CONCAVE) {
bu_vls_printf(gedp->ged_result_str,
"%s: inside requires a convex ARB; concave ARB8 solids are unsupported\n",
o_name);
return BRLCAD_ERROR;
}
/* must find new plane equations to account for
* any editing in the es_mat matrix or path to this solid.
@@ -1008,6 +1066,30 @@ ged_inside_internal(struct ged *gedp, struct rt_db_internal *ip, int argc, const
break;
}
case ID_ARBN: {
struct rt_arbn_internal *arbn =
(struct rt_arbn_internal *)ip->idb_ptr;
RT_ARBN_CK_MAGIC(arbn);
size_t supplied = argc > arg ? (size_t)(argc - arg) : 0;
if (supplied < arbn->neqn) {
bu_vls_printf(gedp->ged_result_str,
"Enter thickness for plane P%zu: ", supplied);
return GED_MORE;
}
fastf_t *arbn_thick = (fastf_t *)bu_calloc(arbn->neqn,
sizeof(fastf_t), "inside ARBN thicknesses");
for (i = 0; i < arbn->neqn; i++)
arbn_thick[i] = atof(argv[arg++]) * gedp->dbip->dbi_local2base;
int inside_result = arbnin(gedp, ip, arbn_thick, &wdbp->wdb_tol);
bu_free(arbn_thick, "inside ARBN thicknesses");
if (inside_result != BRLCAD_OK)
return BRLCAD_ERROR;
break;
}
case ID_TGC:
for (i = 0; i < 3; i++) {
if (argc < arg+1) {
+13
View File
@@ -1926,6 +1926,19 @@ densities_prep(struct ged *gedp, struct rt_i *rtip)
}
FOR_ALL_DIRECTORY_END;
}
// validation loop - all regions with a material ID should be in the density table
struct region *regp = REGION_NULL;
for (BU_LIST_FOR(regp, region, &(rtip->HeadRegion))) {
const long int material_id = regp->reg_gmater;
if (analyze_densities_density(_gd_densities, material_id) >= 0.0)
continue;
bu_vls_printf(gedp->ged_result_str,
"Error: Density %ld on region %s is not defined in the density table.\n", material_id, regp->reg_name);
analyze_densities_clear(_gd_densities);
return BRLCAD_ERROR;
}
}
return BRLCAD_OK;
+1
View File
@@ -227,6 +227,7 @@ ged_pipe_delete_pnt_core(struct ged *gedp, int argc, const char *argv[])
bu_vls_printf(gedp->ged_result_str, "%s: cannot delete pipe segment %d", argv[0], seg_i);
return BRLCAD_ERROR;
}
pipeip->pipe_count--;
GED_DB_PUT_INTERN(gedp, dp, &intern, BRLCAD_ERROR);
File diff suppressed because it is too large Load Diff
+42 -42
View File
@@ -52,9 +52,9 @@ union nmg_record {
};
#define NMG_CK_DISKMAGIC(_cp, _magic) \
if (ntohl(*(uint32_t*)_cp) != _magic) { \
if (BU_GLONG((const unsigned char *)_cp) != _magic) { \
bu_log("NMG_CK_DISKMAGIC: magic mismatch, got x%x, s/b x%x, file %s, line %d\n", \
ntohl(*(uint32_t*)_cp), _magic, __FILE__, __LINE__); \
BU_GLONG((const unsigned char *)_cp), _magic, __FILE__, __LINE__); \
bu_bomb("bad magic\n"); \
}
@@ -1184,7 +1184,7 @@ nmg_i2alloc(struct nmg_exp_counts *ecnt, unsigned char *cp, int *kind_counts)
for (i=0; i < nkind; i++) {
int ndouble;
NMG_CK_DISKMAGIC(cp + offset, DISK_DOUBLE_ARRAY_MAGIC);
ndouble = ntohl(*(uint32_t*)(cp + offset + 4));
ndouble = BU_GLONG((const unsigned char *)(cp + offset + 4));
ecnt[subscript].kind = NMG_KIND_DOUBLE_ARRAY;
/* Stored byte offset is from beginning of disk record */
ecnt[subscript].byte_offset = offset;
@@ -1215,9 +1215,9 @@ nmg_import4_fastf(const unsigned char *base, struct nmg_exp_counts *ecnt, long i
cp = base + ecnt[subscript].byte_offset;
if (ntohl(*(uint32_t*)cp) != DISK_DOUBLE_ARRAY_MAGIC) {
if (BU_GLONG((const unsigned char *)cp) != DISK_DOUBLE_ARRAY_MAGIC) {
bu_log("magic mismatch, got x%x, s/b x%x, file %s, line %d\n",
ntohl(*(uint32_t*)cp), DISK_DOUBLE_ARRAY_MAGIC, __FILE__, __LINE__);
BU_GLONG((const unsigned char *)cp), DISK_DOUBLE_ARRAY_MAGIC, __FILE__, __LINE__);
bu_log("subscript=%ld, byte_offset=%ld\n",
subscript, ecnt[subscript].byte_offset);
bu_bomb("nmg_import4_fastf() bad magic\n");
@@ -1226,7 +1226,7 @@ nmg_import4_fastf(const unsigned char *base, struct nmg_exp_counts *ecnt, long i
if (pt_type)
len *= RT_NURB_EXTRACT_COORDS(pt_type);
count = ntohl(*(uint32_t*)(cp + 4));
count = BU_GLONG((const unsigned char *)(cp + 4));
if (count != len || count < 0) {
bu_log("nmg_import4_fastf() subscript=%ld, expected len=%d, got=%d\n",
subscript, len, count);
@@ -1280,13 +1280,13 @@ nmg_import4_fastf(const unsigned char *base, struct nmg_exp_counts *ecnt, long i
*
* NOTE that the "< 0" test here is a comparison with DISK_INDEX_LISTHEAD.
*/
#define INDEX(o, i, ty, elem) (i)->elem = (struct ty *)ptrs[ntohl(*(uint32_t*)((o)->elem))]
#define INDEX(o, i, ty, elem) (i)->elem = (struct ty *)ptrs[BU_GLONG((const unsigned char *)((o)->elem))]
#define INDEXL_HD(oo, ii, elem, hd) { \
int sub; \
if ((sub = ntohl(*(uint32_t*)((oo)->elem.forw))) < 0) \
if ((sub = BU_GLONG((const unsigned char *)((oo)->elem.forw))) < 0) \
(ii)->elem.forw = &(hd); \
else (ii)->elem.forw = (struct bu_list *)ptrs[sub]; \
if ((sub = ntohl(*(uint32_t*)((oo)->elem.back))) < 0) \
if ((sub = BU_GLONG((const unsigned char *)((oo)->elem.back))) < 0) \
(ii)->elem.back = &(hd); \
else (ii)->elem.back = (struct bu_list *)ptrs[sub]; }
@@ -1295,14 +1295,14 @@ nmg_import4_fastf(const unsigned char *base, struct nmg_exp_counts *ecnt, long i
#define INDEXL_HD2(oo, ii, elem, hd) { \
int sub; \
struct edgeuse *eu2; \
if ((sub = ntohl(*(uint32_t*)((oo)->elem.forw))) < 0) { \
if ((sub = BU_GLONG((const unsigned char *)((oo)->elem.forw))) < 0) { \
(ii)->elem.forw = &(hd); \
} else { \
eu2 = (struct edgeuse *)ptrs[sub]; \
NMG_CK_EDGEUSE(eu2); \
(ii)->elem.forw = &eu2->l2; \
} \
if ((sub = ntohl(*(uint32_t*)((oo)->elem.back))) < 0) { \
if ((sub = BU_GLONG((const unsigned char *)((oo)->elem.back))) < 0) { \
(ii)->elem.back = &(hd); \
} else { \
eu2 = (struct edgeuse *)ptrs[sub]; \
@@ -1411,7 +1411,7 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
NMG_CK_DISKMAGIC(d->magic, DISK_FACEUSE_MAGIC);
INDEX(d, fu, shell, s_p);
INDEX(d, fu, faceuse, fumate_p);
fu->orientation = ntohl(*(uint32_t*)(d->orientation));
fu->orientation = BU_GLONG((const unsigned char *)(d->orientation));
INDEX(d, fu, face, f_p);
INDEXL_HD(d, fu, lu_hd, fu->lu_hd);
INDEXL_HD(d, fu, l, fu->s_p->fu_hd); /* after fu->s_p */
@@ -1428,9 +1428,9 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
NMG_CK_FACE(f);
NMG_CK_DISKMAGIC(d->magic, DISK_FACE_MAGIC);
INDEX(d, f, faceuse, fu_p);
g_index = ntohl(*(uint32_t*)(d->g));
g_index = BU_GLONG((const unsigned char *)(d->g));
f->g.magic_p = (uint32_t *)ptrs[g_index];
f->flip = ntohl(*(uint32_t*)(d->flip));
f->flip = BU_GLONG((const unsigned char *)(d->flip));
/* Enroll this face on fg's list of users */
NMG_CK_FACE_G_EITHER(f->g.magic_p);
INDEXL_HD(d, f, l, f->g.plane_p->f_hd); /* after fu->fg_p set */
@@ -1461,29 +1461,29 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
NMG_CK_FACE_G_SNURB(fg);
NMG_CK_DISKMAGIC(d->magic, DISK_FACE_G_SNURB_MAGIC);
INDEXL_HD(d, fg, f_hd, fg->f_hd);
fg->order[0] = ntohl(*(uint32_t*)(d->u_order));
fg->order[1] = ntohl(*(uint32_t*)(d->v_order));
fg->u.k_size = ntohl(*(uint32_t*)(d->u_size));
fg->order[0] = BU_GLONG((const unsigned char *)(d->u_order));
fg->order[1] = BU_GLONG((const unsigned char *)(d->v_order));
fg->u.k_size = BU_GLONG((const unsigned char *)(d->u_size));
fg->u.knots = nmg_import4_fastf(basep,
ecnt,
ntohl(*(uint32_t*)(d->u_knots)),
BU_GLONG((const unsigned char *)(d->u_knots)),
NULL,
fg->u.k_size,
0);
fg->v.k_size = ntohl(*(uint32_t*)(d->v_size));
fg->v.k_size = BU_GLONG((const unsigned char *)(d->v_size));
fg->v.knots = nmg_import4_fastf(basep,
ecnt,
ntohl(*(uint32_t*)(d->v_knots)),
BU_GLONG((const unsigned char *)(d->v_knots)),
NULL,
fg->v.k_size,
0);
fg->s_size[0] = ntohl(*(uint32_t*)(d->us_size));
fg->s_size[1] = ntohl(*(uint32_t*)(d->vs_size));
fg->pt_type = ntohl(*(uint32_t*)(d->pt_type));
fg->s_size[0] = BU_GLONG((const unsigned char *)(d->us_size));
fg->s_size[1] = BU_GLONG((const unsigned char *)(d->vs_size));
fg->pt_type = BU_GLONG((const unsigned char *)(d->pt_type));
/* Transform ctl_points by 'mat' */
fg->ctl_points = nmg_import4_fastf(basep,
ecnt,
ntohl(*(uint32_t*)(d->ctl_points)),
BU_GLONG((const unsigned char *)(d->ctl_points)),
(matp_t)mat,
fg->s_size[0] * fg->s_size[1],
fg->pt_type);
@@ -1498,10 +1498,10 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
d = &((struct disk_loopuse *)ip)[iindex];
NMG_CK_LOOPUSE(lu);
NMG_CK_DISKMAGIC(d->magic, DISK_LOOPUSE_MAGIC);
up_index = ntohl(*(uint32_t*)(d->up));
up_index = BU_GLONG((const unsigned char *)(d->up));
lu->up.magic_p = ptrs[up_index];
INDEX(d, lu, loopuse, lumate_p);
lu->orientation = ntohl(*(uint32_t*)(d->orientation));
lu->orientation = BU_GLONG((const unsigned char *)(d->orientation));
INDEX(d, lu, loop, l_p);
up_kind = ecnt[up_index].kind;
if (up_kind == NMG_KIND_FACEUSE) {
@@ -1555,12 +1555,12 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
d = &((struct disk_edgeuse *)ip)[iindex];
NMG_CK_EDGEUSE(eu);
NMG_CK_DISKMAGIC(d->magic, DISK_EDGEUSE_MAGIC);
up_index = ntohl(*(uint32_t*)(d->up));
up_index = BU_GLONG((const unsigned char *)(d->up));
eu->up.magic_p = ptrs[up_index];
INDEX(d, eu, edgeuse, eumate_p);
INDEX(d, eu, edgeuse, radial_p);
INDEX(d, eu, edge, e_p);
eu->orientation = ntohl(*(uint32_t*)(d->orientation));
eu->orientation = BU_GLONG((const unsigned char *)(d->orientation));
INDEX(d, eu, vertexuse, vu_p);
up_kind = ecnt[up_index].kind;
if (up_kind == NMG_KIND_LOOPUSE) {
@@ -1568,7 +1568,7 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
} else if (up_kind == NMG_KIND_SHELL) {
INDEXL_HD(d, eu, l, eu->up.s_p->eu_hd);
} else bu_log("bad edgeuse up, index=%d, kind=%d\n", up_index, up_kind);
eu->g.magic_p = ptrs[ntohl(*(uint32_t*)(d->g))];
eu->g.magic_p = ptrs[BU_GLONG((const unsigned char *)(d->g))];
NMG_CK_EDGE(eu->e_p);
NMG_CK_EDGEUSE(eu->eumate_p);
NMG_CK_EDGEUSE(eu->radial_p);
@@ -1591,7 +1591,7 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
d = &((struct disk_edge *)ip)[iindex];
NMG_CK_EDGE(e);
NMG_CK_DISKMAGIC(d->magic, DISK_EDGE_MAGIC);
e->is_real = ntohl(*(uint32_t*)(d->is_real));
e->is_real = BU_GLONG((const unsigned char *)(d->is_real));
INDEX(d, e, edgeuse, eu_p);
NMG_CK_EDGEUSE(e->eu_p);
}
@@ -1626,20 +1626,20 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
NMG_CK_EDGE_G_CNURB(eg);
NMG_CK_DISKMAGIC(d->magic, DISK_EDGE_G_CNURB_MAGIC);
INDEXL_HD2(d, eg, eu_hd2, eg->eu_hd2);
eg->order = ntohl(*(uint32_t*)(d->order));
eg->order = BU_GLONG((const unsigned char *)(d->order));
/* If order is zero, so is everything else */
if (eg->order == 0) return 0;
eg->k.k_size = ntohl(*(uint32_t*)(d->k_size));
eg->k.k_size = BU_GLONG((const unsigned char *)(d->k_size));
eg->k.knots = nmg_import4_fastf(basep,
ecnt,
ntohl(*(uint32_t*)(d->knots)),
BU_GLONG((const unsigned char *)(d->knots)),
NULL,
eg->k.k_size,
0);
eg->c_size = ntohl(*(uint32_t*)(d->c_size));
eg->pt_type = ntohl(*(uint32_t*)(d->pt_type));
eg->c_size = BU_GLONG((const unsigned char *)(d->c_size));
eg->pt_type = BU_GLONG((const unsigned char *)(d->pt_type));
/*
* The curve's control points are in parameter space.
* They do NOT get transformed!
@@ -1648,7 +1648,7 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
/* UV coords on snurb surface don't get xformed */
eg->ctl_points = nmg_import4_fastf(basep,
ecnt,
ntohl(*(uint32_t*)(d->ctl_points)),
BU_GLONG((const unsigned char *)(d->ctl_points)),
NULL,
eg->c_size,
eg->pt_type);
@@ -1656,7 +1656,7 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
/* XYZ coords on planar face DO get xformed */
eg->ctl_points = nmg_import4_fastf(basep,
ecnt,
ntohl(*(uint32_t*)(d->ctl_points)),
BU_GLONG((const unsigned char *)(d->ctl_points)),
(matp_t)mat,
eg->c_size,
eg->pt_type);
@@ -1669,9 +1669,9 @@ nmg_idisk(void *op, void *ip, struct nmg_exp_counts *ecnt, int idx, uint32_t **p
d = &((struct disk_vertexuse *)ip)[iindex];
NMG_CK_VERTEXUSE(vu);
NMG_CK_DISKMAGIC(d->magic, DISK_VERTEXUSE_MAGIC);
vu->up.magic_p = ptrs[ntohl(*(uint32_t*)(d->up))];
vu->up.magic_p = ptrs[BU_GLONG((const unsigned char *)(d->up))];
INDEX(d, vu, vertex, v_p);
vu->a.magic_p = ptrs[ntohl(*(uint32_t*)(d->a))];
vu->a.magic_p = ptrs[BU_GLONG((const unsigned char *)(d->a))];
NMG_CK_VERTEX(vu->v_p);
if (vu->a.magic_p)NMG_CK_VERTEXUSE_A_EITHER(vu->a.magic_p);
INDEXL_HD(d, vu, l, vu->v_p->vu_hd);
@@ -1776,7 +1776,7 @@ nmg_import4(const struct bu_external *ep, const fastf_t *mat)
/* Obtain counts of each kind of structure */
maxindex = 1;
for (kind = 0; kind < NMG_N_KINDS; kind++) {
kind_counts[kind] = ntohl(*(uint32_t*)(rp->nmg.N_structs+4*kind));
kind_counts[kind] = BU_GLONG((const unsigned char *)(rp->nmg.N_structs+4*kind));
maxindex += kind_counts[kind];
}
@@ -1863,7 +1863,7 @@ nmg_import(const struct bu_external *ep, const mat_t mat, int ver)
{
int version;
version = ntohl(*(uint32_t*)dp);
version = BU_GLONG((const unsigned char *)dp);
dp+= SIZEOF_NETWORK_LONG;
if (version != DISK_MODEL_VERSION) {
bu_log("nmg_import: expected NMG '.g' format version %d, got %d, aborting nmg solid import\n",
@@ -1873,7 +1873,7 @@ nmg_import(const struct bu_external *ep, const mat_t mat, int ver)
}
maxindex = 1;
for (kind =0; kind < NMG_N_KINDS; kind++) {
kind_counts[kind] = ntohl(*(uint32_t*)dp);
kind_counts[kind] = BU_GLONG((const unsigned char *)dp);
dp+= SIZEOF_NETWORK_LONG;
maxindex += kind_counts[kind];
}
+2 -19
View File
@@ -6,21 +6,6 @@ brlcad_find_package(OPENNURBS REQUIRED SYSPATTERN openNURBS)
brlcad_find_package(REGEX REQUIRED)
find_package_gte(REQUIRED)
set(LIBRT_OPENVDB_INCLUDE_DIRS)
set(LIBRT_OPENVDB_LIBS)
if(BRLCAD_ENABLE_OPENVDB)
set(LIBRT_OPENVDB_INCLUDE_DIRS ${OpenVDB_INCLUDE_DIRS} ${Tbb_INCLUDE_DIRS} ${IlmBase_INCLUDE_DIRS})
set(LIBRT_OPENVDB_LIBS ${OpenVDB_LIBRARIES} ${IlmBase_LIBRARIES} ${Tbb_LIBRARIES})
# Prevent warnings in OpenVDB/Boost headers and an LTO false positive
# involving OpenVDB's UINT64_MAX LeafManager sentinel.
set_source_files_properties(
primitives/bot/bot_openvdb.cpp
PROPERTIES
COMPILE_DEFINITIONS BRLCAD_OPENVDB
COMPILE_FLAGS "-w -fno-lto"
)
endif(BRLCAD_ENABLE_OPENVDB)
if(BRLCAD_ENABLE_OPENCL)
add_definitions(-DUSE_OPENCL)
endif(BRLCAD_ENABLE_OPENCL)
@@ -179,7 +164,6 @@ set(
primitives/bot/bot_edge.c
primitives/bot/bot_mirror.c
primitives/bot/bot_oriented_bbox.cpp
primitives/bot/bot_openvdb.cpp
primitives/bot/bot_plot.cpp
primitives/bot/btg.c
primitives/bot/btgf.c
@@ -316,6 +300,7 @@ set(
regionfix.c
roots.c
rt_init.cpp
rtcmp_capture.cpp
search.cpp
search_old.cpp
shoot.c
@@ -358,6 +343,7 @@ set(
primitives/edit_private.h
primitives/fixpt.h
primitives/metaball/metaball.h
primitives/part/part_private.h
primitives/revolve/revolve.h
primitives/rt_ecmd_scanner.cpp
primitives/sph/benchmark.sh
@@ -451,7 +437,6 @@ set(
${RPCRT_LIB}
${STDCXX_LIBRARIES}
manifold::manifold
${LIBRT_OPENVDB_LIBS}
)
# Include directories needed by librt users
@@ -465,7 +450,6 @@ set(
${BRLCAD_SOURCE_DIR}/src/libbu
${BRLCAD_SOURCE_DIR}/src/libbg
${REGEX_INCLUDE_DIRS}
${LIBRT_OPENVDB_INCLUDE_DIRS}
)
# FIXME: needs materialX
# ${CMAKE_CURRENT_SOURCE_DIR}/../../../MaterialXSource/source
@@ -585,7 +569,6 @@ cmakefiles(
bot2nurbs.txt
brep_csg_boolean.txt
kigumi_todo.txt
primitives/bot/bot_openvdb.h
vds/README
vds/COPYING
vds/vds.h
+17 -4
View File
@@ -341,7 +341,7 @@ cache_init(struct rt_cache *cache)
}
static void
static int
compress_external(const struct rt_cache *cache, struct bu_external *external)
{
int ret;
@@ -352,8 +352,16 @@ compress_external(const struct rt_cache *cache, struct bu_external *external)
BU_CK_EXTERNAL(external);
BU_ASSERT(external->ext_nbytes < INT_MAX);
if (external->ext_nbytes >= INT_MAX) {
CACHE_DEBUG("++++++ [%lu.%lu] Compression input is too large (%zu bytes)\n", bu_pid(), bu_parallel_id(), external->ext_nbytes);
return 0;
}
compressed_size = brl_LZ4_compressBound((int)external->ext_nbytes);
if (compressed_size <= 0) {
CACHE_DEBUG("++++++ [%lu.%lu] Compression input is unsupported (%zu bytes)\n", bu_pid(), bu_parallel_id(), external->ext_nbytes);
return 0;
}
/* buffer is uncompsize + maxcompsize + compressed_data */
buffer = (uint8_t *)bu_malloc((size_t)compressed_size + SIZEOF_NETWORK_LONG, "buffer");
@@ -367,12 +375,14 @@ compress_external(const struct rt_cache *cache, struct bu_external *external)
if (!compressed) {
CACHE_DEBUG("++++++ [%lu.%lu] Compression failed (ret %d, %zu bytes @ %p to %d bytes max)\n", bu_pid(), bu_parallel_id(), ret, external->ext_nbytes, (void *) external->ext_buf, compressed_size);
return;
bu_free(buffer, "buffer");
return 0;
}
bu_free(external->ext_buf, "ext_buf");
external->ext_nbytes = compressed_size + SIZEOF_NETWORK_LONG;
external->ext_buf = buffer;
return 1;
}
@@ -569,7 +579,10 @@ cache_try_store(struct rt_cache *cache, const char *name, const struct rt_db_int
return 0; /* can't serialize */
}
compress_external(cache, &data_external);
if (!compress_external(cache, &data_external)) {
bu_free_external(&data_external);
return 0;
}
{
struct bu_attribute_value_set attributes = BU_AVS_INIT_ZERO;
+14 -3
View File
@@ -159,10 +159,20 @@ comb_write_back(struct rt_edit *s)
return BRLCAD_ERROR;
}
if (rt_db_put_internal(dp, dbip, &s->es_int) < 0) {
int write_failed = (rt_db_put_internal(dp, dbip, &s->es_int) < 0);
/* rt_db_put_internal releases the internal even on failure. Keep the
* edit session usable for the caller and for subsequent commands. */
if (rt_db_get_internal(&s->es_int, dp, dbip, NULL) < 0) {
bu_vls_printf(s->log_str,
"ERROR: comb_write_back: failed to reload '%s'\n", dp->d_namep);
return BRLCAD_ERROR;
}
if (write_failed) {
bu_vls_printf(s->log_str,
"ERROR: comb_write_back: rt_db_put_internal failed for '%s'\n",
comb->src_objname);
dp->d_namep);
return BRLCAD_ERROR;
}
@@ -773,8 +783,9 @@ rt_edit_comb_prim_edit_create(struct rt_edit *s)
}
C_DECL void
rt_edit_comb_prim_edit_destroy(struct rt_comb_edit *ce)
rt_edit_comb_prim_edit_destroy(void *ptr)
{
struct rt_comb_edit *ce = (struct rt_comb_edit *)ptr;
if (!ce)
return;
bu_vls_free(&ce->es_name);
+82 -27
View File
@@ -254,6 +254,7 @@ rt_edit_reset(struct rt_edit *s)
MAT_IDN(s->e_invmat);
MAT_IDN(s->e_mat);
MAT_IDN(s->incr_change);
MAT_IDN(s->model2objview);
MAT_IDN(s->model_changes);
VSETALL(s->curr_e_axes_pos, 0.0);
VSETALL(s->e_axes_pos, 0.0);
@@ -291,9 +292,12 @@ rt_edit_reset(struct rt_edit *s)
s->mv_context = 0;
s->snap.enabled = 0;
s->snap.spacing = 1.0;
s->dbip = NULL;
s->tol = NULL;
s->u_ptr = NULL;
s->update_views = 0;
s->vlfree = NULL;
s->vp = NULL;
bu_vls_trunc(s->log_str, 0);
}
@@ -572,6 +576,16 @@ rt_edit_set_edflag(struct rt_edit *s, int edflag)
}
}
void
rt_edit_set_translation_target(struct rt_edit *s, const point_t target)
{
if (!s)
return;
VSCALE(s->e_para, target, s->base2local);
s->e_inpara = 3;
}
/* Processing of editing knob twists. */
int
rt_edit_knob_cmd_process(
@@ -967,8 +981,9 @@ rt_knob_edit_tran(struct rt_edit *s,
rt_edit_set_knob_edflag(s, RT_PARAMS_EDIT_TRANS);
VADD2(s->e_para, delta, s->curr_e_axes_pos);
s->e_inpara = 3;
point_t target;
VADD2(target, delta, s->curr_e_axes_pos);
rt_edit_set_translation_target(s, target);
rt_edit_process(s);
s->edit_flag = save_edflag;
s->edit_mode = save_mode;
@@ -1092,6 +1107,14 @@ rt_knob_edit_sca(struct rt_edit *s, int matrix_edit)
}
}
static void
rt_edit_clear_input(struct rt_edit *s)
{
s->e_inpara = 0;
s->e_mvalid = 0;
s->es_scale = 0.0;
}
/*
* A great deal of magic takes place here, to accomplish solid editing.
*
@@ -1101,13 +1124,26 @@ rt_knob_edit_sca(struct rt_edit *s, int matrix_edit)
* A lot of processing is deferred to here, so that the "p" command
* can operate on an equal footing to mouse events.
*/
void
int
rt_edit_process(struct rt_edit *s)
{
bu_clbk_t f = NULL;
void *d = NULL;
++s->update_views;
if (!s)
return BRLCAD_ERROR;
const int type = s->es_int.idb_type;
if (type <= ID_NULL || type > ID_MAX_SOLID ||
!s->es_int.idb_ptr || EDOBJ[type].magic != RT_FUNCTAB_MAGIC) {
bu_vls_printf(s->log_str, "rt_edit_process: no editable solid\n");
rt_edit_clear_input(s);
return BRLCAD_ERROR;
}
const int prior_update_views = s->update_views;
if (!s->update_views)
s->update_views = 1;
int had_method = 0;
const struct rt_db_internal *ip = &s->es_int;
@@ -1118,9 +1154,10 @@ rt_edit_process(struct rt_edit *s)
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_STR, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
bu_vls_trunc(s->log_str, 0);
}
return;
s->update_views = prior_update_views;
rt_edit_clear_input(s);
return BRLCAD_ERROR;
}
if (bu_vls_strlen(s->log_str)) {
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_STR, BU_CLBK_DURING);
@@ -1135,7 +1172,7 @@ rt_edit_process(struct rt_edit *s)
case RT_EDIT_IDLE:
/* do nothing more */
--s->update_views;
s->update_views = prior_update_views;
break;
default:
{
@@ -1152,6 +1189,9 @@ rt_edit_process(struct rt_edit *s)
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
s->update_views = prior_update_views;
rt_edit_clear_input(s);
return BRLCAD_ERROR;
}
}
@@ -1181,8 +1221,8 @@ rt_edit_process(struct rt_edit *s)
}
// Inputs processed, reset
s->e_inpara = 0;
s->e_mvalid = 0;
rt_edit_clear_input(s);
return BRLCAD_OK;
}
void
@@ -1204,15 +1244,15 @@ rt_edit_checkpoint(struct rt_edit *s)
RT_CK_DB_INTERNAL(&s->es_int);
/* Release any previous snapshot */
bu_free_external(&s->es_ckpt);
BU_EXTERNAL_INIT(&s->es_ckpt);
if (rt_obj_export(&s->es_ckpt, &s->es_int, 1.0, s->dbip) < 0) {
struct bu_external candidate = BU_EXTERNAL_INIT_ZERO;
if (rt_obj_export(&candidate, &s->es_int, 1.0, s->dbip) < 0) {
bu_free_external(&candidate);
bu_vls_printf(s->log_str, "rt_edit_checkpoint: export failed\n");
return BRLCAD_ERROR;
}
bu_free_external(&s->es_ckpt);
s->es_ckpt = candidate;
return BRLCAD_OK;
}
@@ -1228,26 +1268,41 @@ rt_edit_revert(struct rt_edit *s)
return BRLCAD_ERROR;
}
int type = s->es_int.idb_type;
/* Release current contents */
rt_db_free_internal(&s->es_int);
RT_DB_INTERNAL_INIT(&s->es_int);
/* rt_obj_import dispatches on ip->idb_minor_type, which RT_DB_INTERNAL_INIT
* resets to -1. Restore the saved type so the right ft_importN is called. */
s->es_int.idb_minor_type = type;
const int type = s->es_int.idb_type;
if (type <= ID_NULL || type > ID_MAX_SOLID || !s->es_int.idb_ptr) {
bu_vls_printf(s->log_str, "rt_edit_revert: no editable solid\n");
return BRLCAD_ERROR;
}
struct rt_db_internal restored;
RT_DB_INTERNAL_INIT(&restored);
restored.idb_minor_type = type;
mat_t identity;
MAT_IDN(identity);
if (rt_obj_import(&s->es_int, &s->es_ckpt, identity, s->dbip) < 0) {
int import_result = rt_obj_import(&restored, &s->es_ckpt, identity, s->dbip);
/* SPH and REC import through the ELL and TGC internal formats. */
bool compatible_type = restored.idb_type == type ||
(type == ID_SPH && restored.idb_type == ID_ELL) ||
(type == ID_REC && restored.idb_type == ID_TGC);
if (import_result < 0 || !compatible_type || !restored.idb_ptr) {
rt_db_free_internal(&restored);
bu_vls_printf(s->log_str, "rt_edit_revert: import failed\n");
return BRLCAD_ERROR;
}
/* If the type changed for some reason (shouldn't happen), keep the original */
if (s->es_int.idb_type != type)
s->es_int.idb_type = type;
restored.idb_type = type;
restored.idb_meth = &OBJ[type];
if (s->ipe_ptr && EDOBJ[type].ft_prim_edit_reset)
(*EDOBJ[type].ft_prim_edit_reset)(s);
rt_db_free_internal(&s->es_int);
s->es_int = restored;
rt_edit_clear_input(s);
s->acc_sc_sol = 1.0;
if (!s->e_keyfixed) {
s->e_keytag = "";
rt_get_solid_keypoint(s, &s->e_keypoint, &s->e_keytag, s->e_mat);
}
return BRLCAD_OK;
}
+8
View File
@@ -211,6 +211,14 @@ extern void flip_dbmat_mat(dbfloat_t *dbp, const fastf_t *ff);
*/
extern fastf_t ell_angle(fastf_t *p1, fastf_t a, fastf_t b, fastf_t dtol, fastf_t ntol);
int _rt_rtcmp_capture_ready(void);
/* Append an rtcmp record for a ray with final partitions. */
void _rt_rtcmp_capture(const struct application *ap, const struct partition *parts);
/* Optional per-ray primitive capture; state belongs to the caller until finish. */
void *_rt_rtcmp_capture_begin(void);
void _rt_rtcmp_capture_segment(void *state, const struct application *ap, const struct seg *segp);
void _rt_rtcmp_capture_finish(void *state, const struct application *ap, const struct partition *parts);
/**
* used by rt_shootray_bundle()
* FIXME: non-public API shouldn't be using rt_ prefix
+6 -4
View File
@@ -191,8 +191,10 @@ ecmd_annot_vert_move(struct rt_edit *s)
return;
}
aip->verts[vi][0] += s->e_para[1] * s->local2base;
aip->verts[vi][1] += s->e_para[2] * s->local2base;
/* Model-space UVs multiply the basis vectors; screen-space UVs are lengths. */
const fastf_t scale = (aip->flags & RT_ANNOT_MODEL_SPACE) ? 1.0 : s->local2base;
aip->verts[vi][0] += s->e_para[1] * scale;
aip->verts[vi][1] += s->e_para[2] * scale;
}
@@ -294,14 +296,14 @@ static const struct rt_edit_param_desc annot_pos_param[] = {
static const struct rt_edit_param_desc annot_txtsize_param[] = {
{ "txt_size", "Text size", RT_EDIT_PARAM_SCALAR, 0,
0.0, RT_EDIT_PARAM_NO_LIMIT, "length", 0, NULL, NULL, NULL }
0.0, RT_EDIT_PARAM_NO_LIMIT, "", 0, NULL, NULL, NULL }
};
static const struct rt_edit_param_desc annot_vert_move_params[] = {
{ "vert_index", "Vertex index", RT_EDIT_PARAM_SCALAR, 0,
0.0, RT_EDIT_PARAM_NO_LIMIT, "count", 0, NULL, NULL, NULL },
{ "delta", "2-D delta (du,dv)", RT_EDIT_PARAM_VECTOR, 1,
RT_EDIT_PARAM_NO_LIMIT, RT_EDIT_PARAM_NO_LIMIT, "length",
RT_EDIT_PARAM_NO_LIMIT, RT_EDIT_PARAM_NO_LIMIT, "",
0, NULL, NULL, NULL }
};
+9 -9
View File
@@ -3277,26 +3277,23 @@ int
rt_arb_calc_points(struct rt_arb_internal *arb, int cgtype, const plane_t planes[6], const struct bn_tol *tol)
{
int i;
point_t pt[8];
struct rt_arb_internal candidate;
RT_ARB_CK_MAGIC(arb);
candidate = *arb;
/* find new points for entire solid */
for (i = 0; i < 8; i++) {
if (rt_arb_3face_intersect(pt[i], planes, cgtype, i*3) < 0) {
if (rt_arb_3face_intersect(candidate.pt[i], planes, cgtype, i*3) < 0) {
bu_log("rt_arb_calc_points: Intersection of planes fails %d\n", i);
return -1; /* FAIL */
}
}
/* Move new points to arb tol->dist))*/
for (i = 0; i < 8; i++) {
VMOVE(arb->pt[i], pt[i]);
}
if (rt_arb_check_points(arb, cgtype, tol) < 0)
if (rt_arb_check_points(&candidate, cgtype, tol) < 0)
return -1;
memcpy(arb->pt, candidate.pt, sizeof(arb->pt));
return 0; /* success */
}
@@ -3397,6 +3394,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
const struct bn_tol *tol)
{
const int arb_faces[5][24] = rt_arb_faces;
plane_t candidate[6];
/* ARB4 at location 0, ARB5 at 1, etc. */
int type = cgtype - ARB4;
@@ -3405,6 +3403,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
RT_ARB_CK_MAGIC(arb);
BN_CK_TOL(tol);
memcpy(candidate, planes, sizeof(candidate));
for (int i = 0; i < 6; i++) {
if (arb_faces[type][i*4] == -1)
@@ -3414,7 +3413,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
int p2 = arb_faces[type][i*4+1];
int p3 = arb_faces[type][i*4+2];
if (bg_make_plane_3pnts(planes[i],
if (bg_make_plane_3pnts(candidate[i],
arb->pt[p1],
arb->pt[p2],
arb->pt[p3],
@@ -3425,6 +3424,7 @@ rt_arb_calc_planes(struct bu_vls *error_msg_ret,
}
}
memcpy(planes, candidate, sizeof(candidate));
return 0;
}
+308 -253
View File
@@ -47,6 +47,8 @@
#define ECMD_ARB_ROTATE_FACE 4015
#define ECMD_ARB_MOVE_EDGE 4036
enum { ARB8_FACE_COUNT = 6, ARB8_VERTEX_COUNT = 8, ARB8_EDGE_COUNT = 12 };
/* ------------------------------------------------------------------ */
/* ft_edit_desc descriptor for the ARB8 primitive */
/* ------------------------------------------------------------------ */
@@ -56,7 +58,7 @@
*
* move_face / move_edge / move_vertex:
* e_para[0] = integer index (face 0-5, edge 0-11, or vertex 0-7)
* e_para[1..3] = X Y Z (point/vector in model coords)
* e_para[1..3] = X Y Z (point in database local units)
* → total e_inpara = 4
*
* rotate_face:
@@ -72,7 +74,7 @@ static const struct rt_edit_param_desc arb_move_face_params[] = {
RT_EDIT_PARAM_INTEGER, /* type */
0, /* index */
0.0, /* range_min */
5.0, /* range_max */
ARB8_FACE_COUNT - 1, /* range_max */
NULL, /* units */
0, NULL, NULL, /* enum (unused) */
NULL /* prim_field */
@@ -147,7 +149,7 @@ static const struct rt_edit_param_desc arb_rotate_face_params[] = {
RT_EDIT_PARAM_INTEGER, /* type */
0, /* index */
0.0, /* range_min */
5.0, /* range_max */
ARB8_FACE_COUNT - 1, /* range_max */
NULL, /* units */
0, NULL, NULL, /* enum (unused) */
NULL /* prim_field */
@@ -158,7 +160,7 @@ static const struct rt_edit_param_desc arb_rotate_face_params[] = {
RT_EDIT_PARAM_INTEGER, /* type */
1, /* index */
0.0, /* range_min */
7.0, /* range_max */
ARB8_VERTEX_COUNT - 1, /* range_max */
NULL, /* units */
0, NULL, NULL, /* enum (unused) */
NULL /* prim_field */
@@ -205,7 +207,7 @@ static const struct rt_edit_cmd_desc arb_cmds[] = {
arb_move_vertex_params, /* params */
1, /* interactive */
30 /* display_order */,
"arb8,arb7,arb6,arb5,arb4" /* req_types */
"arb7,arb6,arb5,arb4" /* req_types */
},
{
ECMD_ARB_ROTATE_FACE, /* cmd_id */
@@ -253,8 +255,9 @@ rt_edit_arb_prim_edit_create(struct rt_edit *UNUSED(s))
}
C_DECL void
rt_edit_arb_prim_edit_destroy(struct rt_arb8_edit *a)
rt_edit_arb_prim_edit_destroy(void *ptr)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)ptr;
if (!a)
return;
BU_PUT(a, struct rt_arb8_edit);
@@ -964,16 +967,6 @@ rt_edit_arb_write_params(
}
}
#define read_params_line_incr \
lc = (ln) ? (ln + lcj) : NULL; \
if (!lc) { \
bu_free(wc, "wc"); \
return BRLCAD_ERROR; \
} \
ln = strchr(lc, tc); \
if (ln) *ln = '\0'; \
while (lc && strchr(lc, ':')) lc++
C_DECL int
rt_edit_arb_read_params(
struct rt_db_internal *ip,
@@ -982,72 +975,79 @@ rt_edit_arb_read_params(
fastf_t local2base
)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
static int uvec[8];
static int svec[11];
static int cgtype = 8;
struct rt_arb_internal *arb = (struct rt_arb_internal *)ip->idb_ptr;
RT_ARB_CK_MAGIC(arb);
rt_arb_get_cgtype(&cgtype, arb, tol, uvec, svec);
if (!fc)
if (!fc || !isfinite(local2base) || local2base <= 0.0)
return BRLCAD_ERROR;
// We're getting the file contents as a string, so we need to split it up
// to process lines. See https://stackoverflow.com/a/17983619
int uvec[8];
int svec[11];
int cgtype = ARB8;
for (int i = 0; i < ARB8_VERTEX_COUNT; ++i)
uvec[i] = -1;
if (!rt_arb_get_cgtype(&cgtype, arb, tol, uvec, svec))
return BRLCAD_ERROR;
// Figure out if we need to deal with Windows line endings
const char *crpos = strchr(fc, '\r');
int crlf = (crpos && crpos[1] == '\n') ? 1 : 0;
char tc = (crlf) ? '\r' : '\n';
// If we're CRLF jump ahead another character.
int lcj = (crlf) ? 2 : 1;
char *ln = NULL;
char *wc = bu_strdup(fc);
char *lc = wc;
// Set up initial line (pt[0])
ln = strchr(lc, tc);
if (ln) *ln = '\0';
// Trim off prefixes, if user left them in
while (lc && strchr(lc, ':')) lc++;
for (int i=0; i<8; i++) {
/* only read vertices that we wrote */
struct rt_arb_internal staged = *arb;
char *buffer = bu_strdup(fc);
char *cursor = buffer;
int expected_index = 1;
for (int i = 0; i < ARB8_VERTEX_COUNT; ++i) {
if (useThisVertex(i, uvec, svec)) {
if (i != 0) {
// Above sets up initial line - otherwise,
// we need to stage the next one.
read_params_line_incr;
char *line = edit_param_next_line(&cursor);
if (!line)
goto failure;
const char *coords = line;
int index = 0, prefix_length = 0;
if (!bu_strncmp(line, "pt[", 3)) {
if (sscanf(line, "pt[%d]: %n", &index,
&prefix_length) != 1 || !prefix_length ||
index != expected_index)
goto failure;
coords += prefix_length;
}
sscanf(lc, "%lf %lf %lf", &a, &b, &c);
VSET(arb->pt[i], a, b, c);
VSCALE(arb->pt[i], arb->pt[i], local2base);
double x, y, z;
int end = 0;
if (sscanf(coords, " %lf %lf %lf %n", &x, &y, &z, &end) != 3 ||
!end || coords[end] || !isfinite(x) || !isfinite(y) ||
!isfinite(z))
goto failure;
VSET(staged.pt[i], x * local2base, y * local2base,
z * local2base);
if (!isfinite(staged.pt[i][X]) || !isfinite(staged.pt[i][Y]) ||
!isfinite(staged.pt[i][Z]))
goto failure;
++expected_index;
}
}
/* fill in the duplicate vertices (based on rt_arb_get_cgtype call) */
if (svec[0] != -1) {
if (edit_param_next_line(&cursor))
goto failure;
/* Duplicate vertices retain the original ARB variant. */
if (svec[0] > 0) {
for (int i=1; i<svec[0]; i++) {
int start = 2;
VMOVE(arb->pt[svec[start+i]], arb->pt[svec[start]]);
VMOVE(staged.pt[svec[start+i]], staged.pt[svec[start]]);
}
}
if (svec[1] != -1) {
if (svec[1] > 0) {
int start = 2 + svec[0];
for (int i=1; i<svec[1]; i++) {
VMOVE(arb->pt[svec[start+i]], arb->pt[svec[start]]);
VMOVE(staged.pt[svec[start+i]], staged.pt[svec[start]]);
}
}
// Cleanup
bu_free(wc, "wc");
if (rt_arb_check_points(&staged, cgtype, tol))
goto failure;
memcpy(arb->pt, staged.pt, sizeof(staged.pt));
bu_free(buffer, "ARB parameter text");
return BRLCAD_OK;
failure:
bu_free(buffer, "ARB parameter text");
return BRLCAD_ERROR;
}
#define RT_ARB_EDIT_EDGE 0
@@ -1169,6 +1169,17 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
return 1;
}
/* Intersections and plane updates can fail after changing several points. */
struct rt_arb_internal *original_arb = arb;
plane_t *original_planes = planes;
struct rt_arb_internal candidate = *arb;
plane_t candidate_planes[6];
vect_t edit_position;
memcpy(candidate_planes, planes, sizeof(candidate_planes));
VMOVE(edit_position, pos_model);
arb = &candidate;
planes = candidate_planes;
/* set the pointer */
switch (arb_type) {
case ARB4:
@@ -1246,7 +1257,7 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
/* do the arb editing */
if (edit_class == RT_ARB_EDIT_POINT) {
/* moving a point - not an edge */
VMOVE(arb->pt[edit_type], pos_model);
VMOVE(arb->pt[edit_type], edit_position);
edptr += 4;
} else if (edit_class == RT_ARB_EDIT_EDGE) {
vect_t edge_dir;
@@ -1256,8 +1267,8 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
pt2 = *edptr++;
if (flags & RT_ARB_EDIT_EDGE_DIR) {
VMOVE(edge_dir, pos_model);
VMOVE(pos_model, arb->pt[pt1]);
VMOVE(edge_dir, edit_position);
VMOVE(edit_position, arb->pt[pt1]);
} else {
/* calculate edge direction */
VSUB2(edge_dir, arb->pt[pt2], arb->pt[pt1]);
@@ -1271,7 +1282,7 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
bp2 = *edptr++;
/* move the edge */
if (rt_arb_move_edge(error_msg_ret, arb, pos_model, bp1, bp2, pt1, pt2,
if (rt_arb_move_edge(error_msg_ret, arb, edit_position, bp1, bp2, pt1, pt2,
edge_dir, planes, tol))
goto err;
}
@@ -1377,6 +1388,8 @@ rt_arb_edit(struct bu_vls *error_msg_ret,
goto err;
}
memcpy(original_arb->pt, candidate.pt, sizeof(candidate.pt));
memcpy(original_planes, candidate_planes, sizeof(candidate_planes));
return 0; /* OK */
err:
@@ -1480,24 +1493,6 @@ ecmd_arb_specific_menu(struct rt_edit *s)
}
}
/*
* arb_unpack_index_param - descriptor-form helper
*
* When the descriptor passes an index + N further values, the caller
* stores them as e_para[0]=index, e_para[1..N]=data.
* Unpack: write the index into *idx_out, shift data[0..N-1] down to
* e_para[0..N-1], and reduce e_inpara by 1.
*/
static void
arb_unpack_index_param(struct rt_edit *s, int *idx_out)
{
int i;
*idx_out = (int)s->e_para[0];
for (i = 1; i < s->e_inpara; i++)
s->e_para[i - 1] = s->e_para[i];
s->e_inpara--;
}
static int
edarb_canonicalize(struct rt_edit *s, struct rt_arb_internal *arb)
{
@@ -1519,58 +1514,89 @@ edarb_canonicalize(struct rt_edit *s, struct rt_arb_internal *arb)
return BRLCAD_OK;
}
static int
arb_move_face_to_point(struct rt_edit *s, const point_t target)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
struct rt_arb_internal candidate;
plane_t planes[6];
struct bu_vls error_msg = BU_VLS_INIT_ZERO;
int arb_type;
RT_ARB_CK_MAGIC(arb);
if (a->edit_menu < 0 || a->edit_menu >= ARB8_FACE_COUNT) {
bu_vls_printf(s->log_str, "Invalid ARB face index %d\n", a->edit_menu);
return BRLCAD_ERROR;
}
candidate = *arb;
memcpy(planes, a->es_peqn, sizeof(planes));
planes[a->edit_menu][W] = VDOT(planes[a->edit_menu], target);
arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (!arb_type)
return BRLCAD_ERROR;
if (rt_arb_calc_points(&candidate, arb_type, (const plane_t *)planes, s->tol)) {
bu_vls_printf(s->log_str, "Cannot calculate ARB points\n");
return BRLCAD_ERROR;
}
if (edarb_canonicalize(s, &candidate) != BRLCAD_OK)
return BRLCAD_ERROR;
if (rt_arb_calc_planes(&error_msg, &candidate, arb_type, planes, s->tol)) {
bu_vls_printf(s->log_str, "%s", bu_vls_cstr(&error_msg));
bu_vls_free(&error_msg);
return BRLCAD_ERROR;
}
bu_vls_free(&error_msg);
memcpy(arb->pt, candidate.pt, sizeof(arb->pt));
memcpy(a->es_peqn, planes, sizeof(planes));
return BRLCAD_OK;
}
static int
arb_numeric_target(point_t target, struct rt_edit *s, int *edit_menu)
{
const fastf_t *coords = s->e_para;
point_t model_point;
if (s->e_inpara == 4) {
if (!(coords[0] >= 0.0 && coords[0] < ARB8_EDGE_COUNT) ||
!EQUAL(coords[0], (fastf_t)(int)coords[0])) {
bu_vls_printf(s->log_str, "ARB edit index must be an integer\n");
return BRLCAD_ERROR;
}
*edit_menu = (int)coords[0];
coords++;
} else if (s->e_inpara != 3) {
bu_vls_printf(s->log_str, "ERROR: three arguments needed\n");
s->e_inpara = 0;
return BRLCAD_ERROR;
}
VSCALE(model_point, coords, s->local2base);
if (s->mv_context)
MAT4X3PNT(target, s->e_invmat, model_point);
else
VMOVE(target, model_point);
return BRLCAD_OK;
}
int
ecmd_arb_move_face(struct rt_edit *s)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
/* move face through definite point */
if (s->e_inpara) {
if (!s->e_inpara)
return BRLCAD_OK;
/*
* Extended form: 4 parameters → e_para[0]=face_index, e_para[1..3]=point.
* Unpack the face index and shift the coord triple down.
*/
if (s->e_inpara == 4)
arb_unpack_index_param(s, &a->edit_menu);
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str, "ERROR: three arguments needed\n");
s->e_inpara = 0;
return BRLCAD_ERROR;
}
/* must convert to base units */
s->e_para[0] *= s->local2base;
s->e_para[1] *= s->local2base;
s->e_para[2] *= s->local2base;
vect_t work;
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
RT_ARB_CK_MAGIC(arb);
if (s->mv_context) {
/* apply s->e_invmat to convert to real model space */
MAT4X3PNT(work, s->e_invmat, s->e_para);
} else {
VMOVE(work, s->e_para);
}
/* change D of planar equation */
a->es_peqn[a->edit_menu][W]=VDOT(&a->es_peqn[a->edit_menu][0], work);
/* find new vertices, put in record in vector notation */
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
(void)rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol);
if (edarb_canonicalize(s, arb) != BRLCAD_OK) {
return BRLCAD_ERROR;
}
}
return 0;
point_t target;
if (arb_numeric_target(target, s, &a->edit_menu) != BRLCAD_OK)
return BRLCAD_ERROR;
return arb_move_face_to_point(s, target);
}
void
@@ -1620,6 +1646,8 @@ int
ecmd_arb_rotate_face(struct rt_edit *s)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
const fastf_t *angles = s->e_para;
int angle_count = s->e_inpara;
/* rotate a GENARB8 defining plane through a fixed vertex */
fastf_t *eqp;
@@ -1629,27 +1657,18 @@ ecmd_arb_rotate_face(struct rt_edit *s)
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
RT_ARB_CK_MAGIC(arb);
/*
* Extended form (from descriptor): 5 parameters:
* e_para[0] = face index → a->edit_menu
* e_para[1] = fixv index → a->fixv
* e_para[2..4] = Euler rotation angles X Y Z (degrees)
* Unpack and reduce to the standard 3-param case.
*/
if (s->e_inpara == 5) {
/*
* Extended form (from descriptor): 5 parameters:
* e_para[0] = face index → a->edit_menu
* e_para[1] = fixv index → a->fixv
* e_para[2..4] = Euler rotation angles X Y Z (degrees)
* Unpack the face index first, then unpack the fixv index.
*/
arb_unpack_index_param(s, &a->edit_menu); /* e_inpara now 4 */
{
int fixv_tmp;
arb_unpack_index_param(s, &fixv_tmp); /* e_inpara now 3 */
a->fixv = (short)fixv_tmp;
if (angle_count == 5) {
if (!(angles[0] >= 0.0 && angles[0] < ARB8_FACE_COUNT) ||
!(angles[1] >= 0.0 && angles[1] < ARB8_VERTEX_COUNT) ||
!EQUAL(angles[0], (fastf_t)(int)angles[0]) ||
!EQUAL(angles[1], (fastf_t)(int)angles[1])) {
bu_vls_printf(s->log_str, "ARB face and vertex indexes must be integers\n");
return BRLCAD_ERROR;
}
a->edit_menu = (int)angles[0];
a->fixv = (short)angles[1];
angles += 2;
angle_count = 3;
{
struct bu_vls error_msg = BU_VLS_INIT_ZERO;
int arb_type2 = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
@@ -1664,12 +1683,18 @@ ecmd_arb_rotate_face(struct rt_edit *s)
}
}
if (a->edit_menu < 0 || a->edit_menu >= ARB8_FACE_COUNT ||
a->fixv < 0 || a->fixv >= ARB8_VERTEX_COUNT) {
bu_vls_printf(s->log_str, "Invalid ARB face or fixed vertex index\n");
return BRLCAD_ERROR;
}
if (s->e_inpara) {
vect_t work;
static mat_t invsolr;
static vect_t tempvec;
static float rota, fb_a;
mat_t invsolr;
vect_t tempvec;
fastf_t rota, fb_a;
/*
* Keyboard parameters in degrees.
@@ -1681,14 +1706,14 @@ ecmd_arb_rotate_face(struct rt_edit *s)
VMOVE(work, eqp);
MAT4X3VEC(eqp, invsolr, work);
if (s->e_inpara == 3) {
if (angle_count == 3) {
/* 3 params: absolute X, Y, Z rotations */
/* Build completely new rotation change */
MAT_IDN(s->model_changes);
bn_mat_angles(s->model_changes,
s->e_para[0],
s->e_para[1],
s->e_para[2]);
angles[0],
angles[1],
angles[2]);
MAT_COPY(s->acc_rot_sol, s->model_changes);
/* Borrow s->incr_change matrix here */
@@ -1713,10 +1738,10 @@ ecmd_arb_rotate_face(struct rt_edit *s)
VMOVE(work, eqp);
MAT4X3VEC(eqp, s->model_changes, work);
}
} else if (s->e_inpara == 2) {
} else if (angle_count == 2) {
/* 2 parameters: rot, fb were given */
rota= s->e_para[0] * DEG2RAD;
fb_a = s->e_para[1] * DEG2RAD;
rota= angles[0] * DEG2RAD;
fb_a = angles[1] * DEG2RAD;
/* calculate normal vector (length = 1) from rot, struct fb */
a->es_peqn[a->edit_menu][0] = cos(fb_a) * cos(rota);
@@ -1762,24 +1787,51 @@ ecmd_arb_rotate_face(struct rt_edit *s)
if (arb_type == 0)
return BRLCAD_ERROR;
(void)rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol);
if (rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol)) {
bu_vls_printf(s->log_str, "Cannot calculate ARB points\n");
return BRLCAD_ERROR;
}
if (edarb_canonicalize(s, arb) != BRLCAD_OK) {
return BRLCAD_ERROR;
}
if (rt_arb_calc_planes(s->log_str, arb, arb_type, a->es_peqn, s->tol))
return BRLCAD_ERROR;
MAT_IDN(s->incr_change);
/* no need to calc_planes again */
f = NULL; d = NULL;
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_REPLOT_EDITING_SOLID, BU_CLBK_DURING);
if (f)
(*f)(0, NULL, d, NULL);
s->e_inpara = 0;
return 0;
}
struct arb_point_edit_index {
int arb_type;
int vertex;
int edit_menu;
};
static int
arb_point_edit_menu(int arb_type, int index, int vertex_index)
{
/* Parameter indices name vertices; the older menus name edit-table rows. */
static const struct arb_point_edit_index point_edits[] = {
{ARB4, 0, 0}, {ARB4, 1, 1}, {ARB4, 2, 2}, {ARB4, 4, 4},
{ARB4, 4, RT_ARB4_MOVE_POINT_4},
{ARB5, 4, RT_ARB5_MOVE_POINT_5},
{ARB6, 4, RT_ARB6_MOVE_POINT_5},
{ARB6, 6, RT_ARB6_MOVE_POINT_6},
{ARB7, 4, RT_ARB7_MOVE_POINT_5}
};
for (size_t i = 0; i < sizeof(point_edits) / sizeof(point_edits[0]); i++) {
const struct arb_point_edit_index *point = &point_edits[i];
if (point->arb_type == arb_type &&
(vertex_index ? point->vertex : point->edit_menu) == index)
return point->edit_menu;
}
return -1;
}
int
edit_arb_element(struct rt_edit *s)
{
@@ -1787,55 +1839,34 @@ edit_arb_element(struct rt_edit *s)
if (s->e_inpara) {
/*
* Extended form: 4 parameters → e_para[0]=index, e_para[1..3]=coords.
* Unpack the element index and shift the coord triple down.
*/
if (s->e_inpara == 4)
arb_unpack_index_param(s, &a->edit_menu);
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str, "ERROR: three arguments needed\n");
s->e_inpara = 0;
return BRLCAD_ERROR;
}
/* must convert to base units */
s->e_para[0] *= s->local2base;
s->e_para[1] *= s->local2base;
s->e_para[2] *= s->local2base;
vect_t work;
if (s->mv_context) {
/* apply s->e_invmat to convert to real model space */
MAT4X3PNT(work, s->e_invmat, s->e_para);
} else {
VMOVE(work, s->e_para);
int parameter_vertex = (s->edit_flag == PTARB && s->e_inpara == 4);
if (arb_numeric_target(work, s, &a->edit_menu) != BRLCAD_OK)
return BRLCAD_ERROR;
if (s->edit_flag == PTARB) {
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
int vertex = a->edit_menu;
if (arb_type == 0)
return BRLCAD_ERROR;
a->edit_menu = arb_point_edit_menu(arb_type, a->edit_menu,
parameter_vertex);
if (a->edit_menu < 0) {
bu_vls_printf(s->log_str, "ARB%d has no point edit for vertex %d\n",
arb_type, vertex);
return BRLCAD_ERROR;
}
}
editarb(s, work);
if (editarb(s, work) != BRLCAD_OK)
return BRLCAD_ERROR;
}
return 0;
}
void
arb_mv_pnt_to(struct rt_edit *s, const vect_t mousevec)
{
vect_t pos_view = VINIT_ZERO; /* Unrotated view space pos */
vect_t temp = VINIT_ZERO;
vect_t pos_model = VINIT_ZERO; /* Rotated screen space pos */
/* move an arb point to indicated point */
/* point is located at es_values[a->edit_menu*3] */
MAT4X3PNT(pos_view, s->vp->gv_model2view, s->curr_e_axes_pos);
pos_view[X] = mousevec[X];
pos_view[Y] = mousevec[Y];
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(pos_model, s->e_invmat, temp);
editarb(s, pos_model);
}
void
edarb_mousevec(struct rt_edit *s, const vect_t mousevec)
static int
arb_mouse_move_element(struct rt_edit *s, const vect_t mousevec)
{
vect_t pos_view = VINIT_ZERO; /* Unrotated view space pos */
vect_t temp = VINIT_ZERO;
@@ -1845,13 +1876,15 @@ edarb_mousevec(struct rt_edit *s, const vect_t mousevec)
pos_view[Y] = mousevec[Y];
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(pos_model, s->e_invmat, temp);
editarb(s, pos_model);
if (editarb(s, pos_model) != BRLCAD_OK)
return BRLCAD_ERROR;
edit_abs_tra(s, pos_view);
return BRLCAD_OK;
}
void
static int
edarb_move_face_mousevec(struct rt_edit *s, const vect_t mousevec)
{
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
vect_t pos_view = VINIT_ZERO; /* Unrotated view space pos */
vect_t temp = VINIT_ZERO;
vect_t pos_model = VINIT_ZERO; /* Rotated screen space pos */
@@ -1860,26 +1893,10 @@ edarb_move_face_mousevec(struct rt_edit *s, const vect_t mousevec)
pos_view[Y] = mousevec[Y];
MAT4X3PNT(temp, s->vp->gv_view2model, pos_view);
MAT4X3PNT(pos_model, s->e_invmat, temp);
/* change D of planar equation */
a->es_peqn[a->edit_menu][W]=VDOT(&a->es_peqn[a->edit_menu][0], pos_model);
/* calculate new vertices, put in record as vectors */
{
struct rt_arb_internal *arb=
(struct rt_arb_internal *)s->es_int.idb_ptr;
RT_ARB_CK_MAGIC(arb);
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return;
(void)rt_arb_calc_points(arb, arb_type, (const plane_t *)a->es_peqn, s->tol);
if (edarb_canonicalize(s, arb) != BRLCAD_OK) {
/* We don't have a return value for this function, so just return */
return;
}
}
if (arb_move_face_to_point(s, pos_model) != BRLCAD_OK)
return BRLCAD_ERROR;
edit_abs_tra(s, pos_view);
return BRLCAD_OK;
}
C_DECL int
@@ -1889,13 +1906,27 @@ rt_edit_arb_edit(struct rt_edit *s)
struct rt_arb_internal *arb = (struct rt_arb_internal *)s->es_int.idb_ptr;
struct rt_arb8_edit *a = (struct rt_arb8_edit *)s->ipe_ptr;
RT_ARB_CK_MAGIC(arb);
struct rt_arb_internal original_arb = *arb;
plane_t original_planes[6];
memcpy(original_planes, a->es_peqn, sizeof(original_planes));
int ret = 0;
int arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
if (rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol)) {
bu_vls_printf(s->log_str, "\nCannot calculate plane equations for ARB8\n");
int validation_issues = 0;
(void)rt_arb_validate(NULL, arb, s->tol, &validation_issues);
int planes_valid = !(validation_issues & RT_ARB_VALIDATE_NONCOPLANAR) &&
!rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol);
int planes_required = s->edit_flag == ECMD_ARB_MOVE_FACE ||
s->edit_flag == ECMD_ARB_SETUP_ROTFACE ||
s->edit_flag == ECMD_ARB_ROTATE_FACE ||
s->edit_flag == PTARB || s->edit_flag == EARB;
if (!planes_valid && planes_required) {
bu_vls_printf(s->log_str,
"Cannot edit ARB faces or edges without valid face planes: %s",
bu_vls_cstr(&error_msg));
bu_vls_free(&error_msg);
return BRLCAD_ERROR;
}
@@ -1917,24 +1948,38 @@ rt_edit_arb_edit(struct rt_edit *s)
break;
case ECMD_ARB_MAIN_MENU:
ecmd_arb_main_menu(s);
break;
return BRLCAD_OK;
case ECMD_ARB_SPECIFIC_MENU:
if (ecmd_arb_specific_menu(s) != BRLCAD_OK)
return -1;
break;
return BRLCAD_OK;
case ECMD_ARB_MOVE_FACE:
return ecmd_arb_move_face(s);
case ECMD_ARB_SETUP_ROTFACE:
ecmd_arb_setup_rotface(s);
break;
case ECMD_ARB_ROTATE_FACE:
{
mat_t original_acc_rot, original_model_changes, original_incr_change;
int original_menu = a->edit_menu;
int original_fixv = a->fixv;
MAT_COPY(original_acc_rot, s->acc_rot_sol);
MAT_COPY(original_model_changes, s->model_changes);
MAT_COPY(original_incr_change, s->incr_change);
ret = ecmd_arb_rotate_face(s);
if (ret)
if (ret) {
memcpy(arb->pt, original_arb.pt, sizeof(arb->pt));
memcpy(a->es_peqn, original_planes, sizeof(original_planes));
MAT_COPY(s->acc_rot_sol, original_acc_rot);
MAT_COPY(s->model_changes, original_model_changes);
MAT_COPY(s->incr_change, original_incr_change);
a->edit_menu = original_menu;
a->fixv = original_fixv;
return ret;
/* ecmd_arb_rotate_face handles plane calc and replot directly;
* return 1 to signal rt_edit_process to skip its post-dispatch
* switch (avoiding a redundant arb_planecalc and replot). */
return 1;
}
/* The plane is current; bypass arb_planecalc below. */
return BRLCAD_OK;
}
case PTARB: /* move an ARB point */
case EARB: /* edit an ARB edge */
return edit_arb_element(s);
@@ -1944,19 +1989,34 @@ rt_edit_arb_edit(struct rt_edit *s)
arb_planecalc:
if (ret != BRLCAD_OK)
goto arb_failed;
/* Rigid and uniform whole-solid transforms preserve an invalid ARB's
* shape. They must remain usable even when face-specific editing cannot
* construct a complete set of planes. */
if (!planes_valid)
return ret;
if (edarb_canonicalize(s, arb) != BRLCAD_OK) {
return BRLCAD_ERROR;
goto arb_failed;
}
/* must re-calculate the face plane equations for arbs */
arb_type = rt_arb_edit_type(s->log_str, s, arb, 0, s->tol);
if (arb_type == 0)
return BRLCAD_ERROR;
if (rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol) < 0)
goto arb_failed;
if (rt_arb_calc_planes(&error_msg, arb, arb_type, a->es_peqn, s->tol) < 0) {
bu_vls_printf(s->log_str, "%s", bu_vls_cstr(&error_msg));
bu_vls_free(&error_msg);
goto arb_failed;
}
bu_vls_free(&error_msg);
return ret;
arb_failed:
memcpy(arb->pt, original_arb.pt, sizeof(arb->pt));
memcpy(a->es_peqn, original_planes, sizeof(original_planes));
return BRLCAD_ERROR;
}
C_DECL int
@@ -1977,14 +2037,10 @@ rt_edit_arb_edit_xy(
edit_stra_xy(&pos_view, s, mousevec);
break;
case PTARB:
arb_mv_pnt_to(s, mousevec);
break;
case EARB:
edarb_mousevec(s, mousevec);
break;
return arb_mouse_move_element(s, mousevec);
case ECMD_ARB_MOVE_FACE:
edarb_move_face_mousevec(s, mousevec);
break;
return edarb_move_face_mousevec(s, mousevec);
default:
return edit_generic_xy(s, mousevec);
}
@@ -2632,9 +2688,8 @@ rt_edit_arb_repair(struct bu_vls *log_str, struct rt_db_internal *ip, const stru
BU_OPT(d[2], "", "options-json", "", NULL, &options_json, "Return JSON of supported options");
BU_OPT_NULL(d[3]);
if (argc > 0 && argv) {
bu_opt_parse(NULL, argc, argv, d);
}
if (edit_repair_parse_options(log_str, argc, argv, d) != BRLCAD_OK)
return -1;
if (options_json) {
if (log_str) {
+2 -2
View File
@@ -955,7 +955,7 @@ rt_arbn_import4(struct rt_db_internal *ip, const struct bu_external *ep, const f
aip = (struct rt_arbn_internal *)ip->idb_ptr;
aip->magic = RT_ARBN_INTERNAL_MAGIC;
aip->neqn = ntohl(*(uint32_t *)rp->n.n_neqn);
aip->neqn = BU_GLONG((const unsigned char *)rp->n.n_neqn);
if (aip->neqn <= 0) return -1;
aip->eqn = (plane_t *)bu_malloc(aip->neqn*sizeof(plane_t), "arbn plane eqn[]");
@@ -1117,7 +1117,7 @@ rt_arbn_import5(struct rt_db_internal *ip, const struct bu_external *ep, const f
BU_CK_EXTERNAL(ep);
if (dbip) RT_CK_DBI(dbip);
neqn = ntohl(*(uint32_t *)ep->ext_buf);
neqn = BU_GLONG((const unsigned char *)ep->ext_buf);
double_count = neqn * ELEMENTS_PER_PLANE;
byte_count = double_count * SIZEOF_NETWORK_DOUBLE;
+55 -53
View File
@@ -38,6 +38,7 @@
#include "common.h"
#include <limits.h>
#include <math.h>
#include <string.h>
@@ -82,7 +83,7 @@ _arbn_normalize_plane(plane_t eqn)
return 0;
}
static void
static int
ecmd_arbn_plane_select(struct rt_edit *s)
{
struct rt_arbn_internal *aip =
@@ -97,19 +98,22 @@ ecmd_arbn_plane_select(struct rt_edit *s)
"ECMD_ARBN_PLANE_SELECT: plane_index required\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
int idx = (int)s->e_para[0];
if (idx < 0 || (size_t)idx >= aip->neqn) {
fastf_t raw_index = s->e_para[0];
if (!isfinite(raw_index) || raw_index < 0.0 ||
raw_index >= (fastf_t)aip->neqn || raw_index > INT_MAX ||
!EQUAL(raw_index, (fastf_t)(int)raw_index)) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_SELECT: index %d out of range [0,%zu)\n",
idx, aip->neqn);
"ECMD_ARBN_PLANE_SELECT: invalid plane index %g\n",
raw_index);
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
int idx = (int)raw_index;
e->plane_index = idx;
bu_vls_printf(s->log_str,
@@ -119,9 +123,10 @@ ecmd_arbn_plane_select(struct rt_edit *s)
aip->eqn[idx][3]);
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return BRLCAD_OK;
}
static void
static int
ecmd_arbn_plane_set_dist(struct rt_edit *s)
{
struct rt_arbn_internal *aip =
@@ -136,20 +141,21 @@ ecmd_arbn_plane_set_dist(struct rt_edit *s)
"ECMD_ARBN_PLANE_SET_DIST: no plane selected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
if (s->e_inpara != 1) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_SET_DIST: distance required\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
aip->eqn[e->plane_index][3] = s->e_para[0] * s->local2base;
return BRLCAD_OK;
}
static void
static int
ecmd_arbn_plane_set_norm(struct rt_edit *s)
{
struct rt_arbn_internal *aip =
@@ -164,30 +170,33 @@ ecmd_arbn_plane_set_norm(struct rt_edit *s)
"ECMD_ARBN_PLANE_SET_NORM: no plane selected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_SET_NORM: nx ny nz required\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
plane_t *eq = &aip->eqn[e->plane_index];
(*eq)[0] = s->e_para[0];
(*eq)[1] = s->e_para[1];
(*eq)[2] = s->e_para[2];
plane_t candidate;
HSET(candidate, s->e_para[0], s->e_para[1], s->e_para[2], 0);
if (_arbn_normalize_plane(*eq) < 0) {
if (_arbn_normalize_plane(candidate) < 0) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_SET_NORM: zero-length normal rejected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return BRLCAD_ERROR;
}
candidate[3] = (*eq)[3];
HMOVE(*eq, candidate);
return BRLCAD_OK;
}
static void
static int
ecmd_arbn_plane_rotate(struct rt_edit *s)
{
struct rt_arbn_internal *aip =
@@ -202,14 +211,14 @@ ecmd_arbn_plane_rotate(struct rt_edit *s)
"ECMD_ARBN_PLANE_ROTATE: no plane selected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
if (s->e_inpara != 3) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_ROTATE: rx ry rz (degrees) required\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
mat_t rmat;
@@ -223,9 +232,10 @@ ecmd_arbn_plane_rotate(struct rt_edit *s)
(*eq)[0] = new_norm[0];
(*eq)[1] = new_norm[1];
(*eq)[2] = new_norm[2];
return BRLCAD_OK;
}
static void
static int
ecmd_arbn_plane_add(struct rt_edit *s)
{
struct rt_arbn_internal *aip =
@@ -240,33 +250,30 @@ ecmd_arbn_plane_add(struct rt_edit *s)
"ECMD_ARBN_PLANE_ADD: nx ny nz d required\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
plane_t candidate;
HSET(candidate, s->e_para[0], s->e_para[1], s->e_para[2],
s->e_para[3] * s->local2base);
if (_arbn_normalize_plane(candidate) < 0) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_ADD: zero-length normal rejected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return BRLCAD_ERROR;
}
size_t new_n = aip->neqn + 1;
aip->eqn = (plane_t *)bu_realloc(aip->eqn,
new_n * sizeof(plane_t), "arbn eqn[] grow");
aip->eqn[aip->neqn][0] = s->e_para[0];
aip->eqn[aip->neqn][1] = s->e_para[1];
aip->eqn[aip->neqn][2] = s->e_para[2];
aip->eqn[aip->neqn][3] = s->e_para[3] * s->local2base;
if (_arbn_normalize_plane(aip->eqn[aip->neqn]) < 0) {
/* roll back */
aip->eqn = (plane_t *)bu_realloc(aip->eqn,
aip->neqn * sizeof(plane_t), "arbn eqn[] rollback");
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_ADD: zero-length normal rejected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
}
HMOVE(aip->eqn[aip->neqn], candidate);
e->plane_index = (int)aip->neqn; /* select newly added plane */
aip->neqn = new_n;
return BRLCAD_OK;
}
static void
static int
ecmd_arbn_plane_del(struct rt_edit *s)
{
struct rt_arbn_internal *aip =
@@ -281,14 +288,14 @@ ecmd_arbn_plane_del(struct rt_edit *s)
"ECMD_ARBN_PLANE_DEL: no plane selected\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
if (aip->neqn <= 4) {
bu_vls_printf(s->log_str,
"ECMD_ARBN_PLANE_DEL: cannot reduce below 4 planes\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
if (f) (*f)(0, NULL, d, NULL);
return;
return BRLCAD_ERROR;
}
int idx = e->plane_index;
@@ -299,6 +306,7 @@ ecmd_arbn_plane_del(struct rt_edit *s)
aip->eqn = (plane_t *)bu_realloc(aip->eqn,
aip->neqn * sizeof(plane_t), "arbn eqn[] shrink");
e->plane_index = -1;
return BRLCAD_OK;
}
@@ -364,23 +372,17 @@ rt_edit_arbn_edit(struct rt_edit *s)
edit_srot(s);
break;
case ECMD_ARBN_PLANE_SELECT:
ecmd_arbn_plane_select(s);
break;
return ecmd_arbn_plane_select(s);
case ECMD_ARBN_PLANE_SET_DIST:
ecmd_arbn_plane_set_dist(s);
break;
return ecmd_arbn_plane_set_dist(s);
case ECMD_ARBN_PLANE_SET_NORM:
ecmd_arbn_plane_set_norm(s);
break;
return ecmd_arbn_plane_set_norm(s);
case ECMD_ARBN_PLANE_ROTATE:
ecmd_arbn_plane_rotate(s);
break;
return ecmd_arbn_plane_rotate(s);
case ECMD_ARBN_PLANE_ADD:
ecmd_arbn_plane_add(s);
break;
return ecmd_arbn_plane_add(s);
case ECMD_ARBN_PLANE_DEL:
ecmd_arbn_plane_del(s);
break;
return ecmd_arbn_plane_del(s);
default:
return edit_generic(s);
}
+121 -48
View File
@@ -28,6 +28,9 @@
#include "common.h"
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
@@ -35,6 +38,7 @@
#include "bnetwork.h"
#include "bu/cv.h"
#include "bu/opt.h"
#include "vmath.h"
#include "rt/db4.h"
#include "nmg.h"
@@ -293,7 +297,7 @@ rt_ars_mat(struct rt_db_internal *rop, const mat_t mat, const struct rt_db_inter
fp += ELEMENTS_PER_POINT;
ofp += ELEMENTS_PER_POINT;
}
VMOVE(fp, top->curves[i]); /* duplicate first point */
VMOVE(ofp, top->curves[i]); /* duplicate first point */
}
return BRLCAD_OK;
@@ -330,9 +334,9 @@ rt_ars_import5(struct rt_db_internal *ip, const struct bu_external *ep, const fa
ari->magic = RT_ARS_INTERNAL_MAGIC;
cp = (unsigned char *)ep->ext_buf;
ari->ncurves = ntohl(*(uint32_t *)cp);
ari->ncurves = BU_GLONG((const unsigned char *)cp);
cp += SIZEOF_NETWORK_LONG;
ari->pts_per_curve = ntohl(*(uint32_t *)cp);
ari->pts_per_curve = BU_GLONG((const unsigned char *)cp);
cp += SIZEOF_NETWORK_LONG;
/*
@@ -912,37 +916,102 @@ rt_ars_get(struct bu_vls *logstr, const struct rt_db_internal *intern, const cha
}
static int
ars_adjust_index(size_t *index, const char *text, const char **suffix)
{
char *end;
unsigned long long value;
if (!text || !isdigit((unsigned char)text[0]))
return BRLCAD_ERROR;
errno = 0;
value = strtoull(text, &end, 10);
if (errno == ERANGE || value > SIZE_MAX)
return BRLCAD_ERROR;
*index = (size_t)value;
if (suffix)
*suffix = end;
else if (*end != '\0')
return BRLCAD_ERROR;
return BRLCAD_OK;
}
static int
ars_adjust_coordinates(fastf_t *values, size_t count, const char *input)
{
char *copy;
const char **args = NULL;
int argc = 0;
int result = BRLCAD_OK;
if (!input || count > INT_MAX)
return BRLCAD_ERROR;
copy = bu_strdup(input);
for (char *ptr = copy; *ptr; ptr++) {
if (*ptr == '{' || *ptr == '}')
*ptr = ' ';
}
if (bu_argv_from_tcl_list(copy, &argc, &args) != 0 || argc != (int)count) {
result = BRLCAD_ERROR;
} else {
for (size_t i = 0; i < count; i++) {
if (bu_opt_fastf_t(NULL, 1, &args[i], &values[i]) != 1 ||
!isfinite(values[i])) {
result = BRLCAD_ERROR;
break;
}
}
}
if (args)
bu_free((void *)args, "ARS adjustment arguments");
bu_free(copy, "ARS adjustment string");
return result;
}
C_DECL int
rt_ars_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, const char **argv)
{
struct rt_ars_internal *ars;
size_t i, j, k;
int len;
fastf_t *array;
RT_CK_DB_INTERNAL(intern);
ars = (struct rt_ars_internal *)intern->idb_ptr;
RT_ARS_CK_MAGIC(ars);
if (argc % 2) {
bu_vls_printf(logstr, "ERROR: ARS adjustments require key/value pairs\n");
return BRLCAD_ERROR;
}
while (argc >= 2) {
if (BU_STR_EQUAL(argv[0], "NC")) {
/* change number of curves */
i = atoi(argv[1]);
if (ars_adjust_index(&i, argv[1], NULL) != BRLCAD_OK ||
i > SIZE_MAX / sizeof(fastf_t *) - 1) {
bu_vls_printf(logstr, "ERROR: Invalid ARS curve count\n");
return BRLCAD_ERROR;
}
if (i < ars->ncurves) {
for (j=i; j<ars->ncurves; j++)
bu_free((char *)ars->curves[j], "ars->curves[j]");
ars->curves = (fastf_t **)bu_realloc(ars->curves,
i*sizeof(fastf_t *), "ars->curves");
(i+1)*sizeof(fastf_t *), "ars->curves");
ars->ncurves = i;
} else if (i > ars->ncurves) {
ars->curves = (fastf_t **)bu_realloc(ars->curves,
i*sizeof(fastf_t *), "ars->curves");
(i+1)*sizeof(fastf_t *), "ars->curves");
if (ars->pts_per_curve) {
/* new curves are duplicates of the last */
for (j=ars->ncurves; j<i; j++) {
ars->curves[j] = (fastf_t *)bu_malloc(
ars->pts_per_curve * 3 * sizeof(fastf_t),
(ars->pts_per_curve + 1) * 3 * sizeof(fastf_t),
"ars->curves[j]");
for (k = 0; k < ars->pts_per_curve; k++) {
if (j) {
@@ -962,7 +1031,11 @@ rt_ars_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, co
}
} else if (BU_STR_EQUAL(argv[0], "PPC")) {
/* change the number of points per curve */
i = atoi(argv[1]);
if (ars_adjust_index(&i, argv[1], NULL) != BRLCAD_OK ||
i > INT_MAX / ELEMENTS_PER_POINT) {
bu_vls_printf(logstr, "ERROR: Invalid ARS points per curve\n");
return BRLCAD_ERROR;
}
if (i < 3) {
bu_vls_printf(logstr,
"ERROR: must have at least 3 points per curve\n");
@@ -971,14 +1044,14 @@ rt_ars_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, co
if (i < ars->pts_per_curve) {
for (j = 0; j < ars->ncurves; j++) {
ars->curves[j] = (fastf_t *)bu_realloc(ars->curves[j],
i * 3 * sizeof(fastf_t),
(i + 1) * 3 * sizeof(fastf_t),
"ars->curves[j]");
}
ars->pts_per_curve = i;
} else if (i > ars->pts_per_curve) {
for (j = 0; j < ars->ncurves; j++) {
ars->curves[j] = (fastf_t *)bu_realloc(ars->curves[j],
i * 3 * sizeof(fastf_t),
(i + 1) * 3 * sizeof(fastf_t),
"ars->curves[j]");
/* new points are duplicates of last */
for (k = ars->pts_per_curve; k < i; k++) {
@@ -993,50 +1066,50 @@ rt_ars_adjust(struct bu_vls *logstr, struct rt_db_internal *intern, int argc, co
ars->pts_per_curve = i;
}
} else if (argv[0][0] == 'C') {
if (isdigit((int)argv[0][1])) {
const char *ptr;
/* a specific curve */
ptr = strchr(argv[0], 'P');
if (ptr) {
/* a specific point on this curve */
i = atoi(&argv[0][1]);
j = atoi(ptr+1);
len = 3;
array = &ars->curves[i][j*3];
if (_rt_tcl_list_to_fastf_array(argv[1], &array, &len)!= len) {
bu_vls_printf(logstr, "WARNING: incorrect number of parameters provided for a point\n");
}
} else {
char *dupstr;
char *ptr2;
/* one complete curve */
i = atoi(&argv[0][1]);
len = (int)ars->pts_per_curve * 3;
dupstr = bu_strdup(argv[1]);
ptr2 = dupstr;
while (*ptr2) {
if (*ptr2 == '{' || *ptr2 == '}')
*ptr2 = ' ';
ptr2++;
}
if (!ars->curves[i]) {
ars->curves[i] = (fastf_t *)bu_calloc(ars->pts_per_curve * 3, sizeof(fastf_t), "ars->curves[i]");
}
if (_rt_tcl_list_to_fastf_array(dupstr, &ars->curves[i], &len) != len) {
bu_vls_printf(logstr, "WARNING: incorrect number of parameters provided for a curve\n");
}
bu_free(dupstr, "bu_strdup ars curve");
const char *suffix = NULL;
if (ars_adjust_index(&i, &argv[0][1], &suffix) != BRLCAD_OK ||
i >= ars->ncurves) {
bu_vls_printf(logstr, "ERROR: Invalid ARS curve index\n");
return BRLCAD_ERROR;
}
if (*suffix == 'P') {
if (ars_adjust_index(&j, suffix + 1, NULL) != BRLCAD_OK ||
j >= ars->pts_per_curve || !ars->curves[i]) {
bu_vls_printf(logstr, "ERROR: Invalid ARS point index\n");
return BRLCAD_ERROR;
}
fastf_t values[ELEMENTS_PER_POINT];
if (ars_adjust_coordinates(values, ELEMENTS_PER_POINT, argv[1]) != BRLCAD_OK) {
bu_vls_printf(logstr, "ERROR: Incorrect ARS point coordinates\n");
return BRLCAD_ERROR;
}
VMOVE(&ars->curves[i][j*ELEMENTS_PER_POINT], values);
} else if (*suffix == '\0') {
size_t count = ars->pts_per_curve * ELEMENTS_PER_POINT;
fastf_t *values = (fastf_t *)bu_calloc(
(ars->pts_per_curve + 1) * ELEMENTS_PER_POINT,
sizeof(fastf_t), "ARS curve adjustment");
if (ars_adjust_coordinates(values, count, argv[1]) != BRLCAD_OK) {
bu_free(values, "ARS curve adjustment");
bu_vls_printf(logstr, "ERROR: Incorrect ARS curve coordinates\n");
return BRLCAD_ERROR;
}
if (ars->curves[i])
bu_free(ars->curves[i], "old ARS curve");
ars->curves[i] = values;
} else {
bu_vls_printf(logstr, "ERROR: Illegal argument, must be NC, PPC, C#, or C#P#\n");
bu_vls_printf(logstr, "ERROR: Invalid ARS curve selector\n");
return BRLCAD_ERROR;
}
} else {
bu_vls_printf(logstr, "ERROR: Illegal argument, must be NC, PPC, C#, or C#P#\n");
return BRLCAD_ERROR;
}
/* Keep every completed pair valid if a later pair is rejected. */
for (j = 0; j < ars->ncurves; j++) {
if (ars->curves[j])
VMOVE(&ars->curves[j][ars->pts_per_curve * ELEMENTS_PER_POINT], ars->curves[j]);
}
argc -= 2;
argv += 2;
}
+36 -56
View File
@@ -87,8 +87,9 @@ rt_edit_ars_prim_edit_create(struct rt_edit *UNUSED(s))
}
C_DECL void
rt_edit_ars_prim_edit_destroy(struct rt_ars_edit *e)
rt_edit_ars_prim_edit_destroy(void *ptr)
{
struct rt_ars_edit *e = (struct rt_ars_edit *)ptr;
if (!e)
return;
@@ -363,6 +364,18 @@ rt_edit_ars_menu_str(struct bu_vls *mstr, const struct rt_db_internal *ip, const
return BRLCAD_OK;
}
/** Convert only keyboard coordinates; mouse points are already base. */
static void
ars_numeric_target(point_t target, struct rt_edit *s)
{
point_t model_point;
VSCALE(model_point, s->e_para, s->local2base);
if (s->mv_context)
MAT4X3PNT(target, s->e_invmat, model_point);
else
VMOVE(target, model_point);
}
void
ecmd_ars_pick(struct rt_edit *s)
{
@@ -379,20 +392,10 @@ ecmd_ars_pick(struct rt_edit *s)
RT_ARS_CK_MAGIC(ars);
/* must convert to base units */
s->e_para[0] *= s->local2base;
s->e_para[1] *= s->local2base;
s->e_para[2] *= s->local2base;
if (s->e_mvalid) {
VMOVE(pick_pt, s->e_mparam);
} else if (s->e_inpara == 3) {
if (s->mv_context) {
/* apply s->e_invmat to convert to real model space */
MAT4X3PNT(pick_pt, s->e_invmat, s->e_para);
} else {
VMOVE(pick_pt, s->e_para);
}
ars_numeric_target(pick_pt, s);
} else if (s->e_inpara && s->e_inpara != 3) {
bu_vls_printf(s->log_str, "x y z coordinates required for 'pick point'\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
@@ -564,7 +567,7 @@ ecmd_ars_dup_crv(struct rt_edit *s)
for (size_t i=0; i<ars->ncurves+1; i++) {
size_t j, k;
curves[i] = (fastf_t *)bu_malloc(ars->pts_per_curve * 3 * sizeof(fastf_t),
curves[i] = (fastf_t *)bu_malloc((ars->pts_per_curve + 1) * 3 * sizeof(fastf_t),
"new curves[i]");
if (i <= (size_t)a->es_ars_crv)
@@ -572,7 +575,7 @@ ecmd_ars_dup_crv(struct rt_edit *s)
else
k = i - 1;
for (j=0; j<ars->pts_per_curve*3; j++)
for (j=0; j<(ars->pts_per_curve + 1)*3; j++)
curves[i][j] = ars->curves[k][j];
}
@@ -605,7 +608,7 @@ ecmd_ars_dup_col(struct rt_edit *s)
for (size_t i=0; i<ars->ncurves; i++) {
size_t j, k;
curves[i] = (fastf_t *)bu_malloc((ars->pts_per_curve + 1) * 3 * sizeof(fastf_t),
curves[i] = (fastf_t *)bu_malloc((ars->pts_per_curve + 2) * 3 * sizeof(fastf_t),
"new curves[i]");
for (j=0; j<ars->pts_per_curve+1; j++) {
@@ -618,6 +621,7 @@ ecmd_ars_dup_col(struct rt_edit *s)
curves[i][j*3+1] = ars->curves[i][k*3+1];
curves[i][j*3+2] = ars->curves[i][k*3+2];
}
VMOVE(&curves[i][(ars->pts_per_curve + 1)*3], curves[i]);
}
for (size_t i=0; i<ars->ncurves; i++)
@@ -747,22 +751,22 @@ ecmd_ars_insert_crv(struct rt_edit *s)
for (size_t i = 0; i < ars->ncurves + 1; i++) {
curves[i] = (fastf_t *)bu_malloc(
ars->pts_per_curve * 3 * sizeof(fastf_t),
(ars->pts_per_curve + 1) * 3 * sizeof(fastf_t),
"ars insert curves[i]");
if ((int)i <= ins_after) {
/* before or at insertion point: copy as-is */
for (size_t j = 0; j < ars->pts_per_curve * 3; j++)
for (size_t j = 0; j < (ars->pts_per_curve + 1) * 3; j++)
curves[i][j] = ars->curves[i][j];
} else if ((int)i == ins_after + 1) {
/* the new interpolated curve */
fastf_t *c0 = ars->curves[ins_after];
fastf_t *c1 = ars->curves[next_crv];
for (size_t j = 0; j < ars->pts_per_curve * 3; j++)
for (size_t j = 0; j < (ars->pts_per_curve + 1) * 3; j++)
curves[i][j] = 0.5 * (c0[j] + c1[j]);
} else {
/* after insertion point: copy from i-1 */
for (size_t j = 0; j < ars->pts_per_curve * 3; j++)
for (size_t j = 0; j < (ars->pts_per_curve + 1) * 3; j++)
curves[i][j] = ars->curves[i - 1][j];
}
}
@@ -808,10 +812,10 @@ ecmd_ars_del_crv(struct rt_edit *s)
if (i == (size_t)a->es_ars_crv)
continue;
curves[k] = (fastf_t *)bu_malloc(ars->pts_per_curve * 3 * sizeof(fastf_t),
curves[k] = (fastf_t *)bu_malloc((ars->pts_per_curve + 1) * 3 * sizeof(fastf_t),
"new curves[k]");
for (j=0; j<ars->pts_per_curve*3; j++)
for (j=0; j<(ars->pts_per_curve + 1)*3; j++)
curves[k][j] = ars->curves[i][j];
k++;
@@ -843,7 +847,7 @@ ecmd_ars_del_col(struct rt_edit *s)
return;
}
if (a->es_ars_col == 0 || (size_t)a->es_ars_col == ars->ncurves - 1) {
if (a->es_ars_col == 0 || (size_t)a->es_ars_col == ars->pts_per_curve - 1) {
bu_log("Cannot delete first or last column\n");
return;
}
@@ -860,7 +864,7 @@ ecmd_ars_del_col(struct rt_edit *s)
size_t j, k;
curves[i] = (fastf_t *)bu_malloc((ars->pts_per_curve - 1) * 3 * sizeof(fastf_t),
curves[i] = (fastf_t *)bu_malloc(ars->pts_per_curve * 3 * sizeof(fastf_t),
"new curves[i]");
k = 0;
@@ -873,6 +877,7 @@ ecmd_ars_del_col(struct rt_edit *s)
curves[i][k*3+2] = ars->curves[i][j*3+2];
k++;
}
VMOVE(&curves[i][(ars->pts_per_curve - 1)*3], curves[i]);
}
for (size_t i=0; i<ars->ncurves; i++)
@@ -899,11 +904,6 @@ ecmd_ars_move_col(struct rt_edit *s)
RT_ARS_CK_MAGIC(ars);
/* must convert to base units */
s->e_para[0] *= s->local2base;
s->e_para[1] *= s->local2base;
s->e_para[2] *= s->local2base;
if (a->es_ars_crv < 0 || a->es_ars_col < 0) {
bu_log("No ARS point selected\n");
return;
@@ -926,12 +926,7 @@ ecmd_ars_move_col(struct rt_edit *s)
dist = DIST_PNT_PLANE(s->e_mparam, view_pl);
VJOIN1(new_pt, s->e_mparam, -dist, view_pl);
} else if (s->e_inpara == 3) {
if (s->mv_context) {
/* apply s->e_invmat to convert to real model space */
MAT4X3PNT(new_pt, s->e_invmat, s->e_para);
} else {
VMOVE(new_pt, s->e_para);
}
ars_numeric_target(new_pt, s);
} else if (s->e_inpara && s->e_inpara != 3) {
bu_vls_printf(s->log_str, "x y z coordinates required for point movement\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
@@ -963,11 +958,6 @@ ecmd_ars_move_crv(struct rt_edit *s)
RT_ARS_CK_MAGIC(ars);
/* must convert to base units */
s->e_para[0] *= s->local2base;
s->e_para[1] *= s->local2base;
s->e_para[2] *= s->local2base;
if (a->es_ars_crv < 0 || a->es_ars_col < 0) {
bu_log("No ARS point selected\n");
return;
@@ -990,12 +980,7 @@ ecmd_ars_move_crv(struct rt_edit *s)
dist = DIST_PNT_PLANE(s->e_mparam, view_pl);
VJOIN1(new_pt, s->e_mparam, -dist, view_pl);
} else if (s->e_inpara == 3) {
if (s->mv_context) {
/* apply s->e_invmat to convert to real model space */
MAT4X3PNT(new_pt, s->e_invmat, s->e_para);
} else {
VMOVE(new_pt, s->e_para);
}
ars_numeric_target(new_pt, s);
} else if (s->e_inpara && s->e_inpara != 3) {
bu_vls_printf(s->log_str, "x y z coordinates required for point movement\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
@@ -1026,11 +1011,6 @@ ecmd_ars_move_pt(struct rt_edit *s)
RT_ARS_CK_MAGIC(ars);
/* must convert to base units */
s->e_para[0] *= s->local2base;
s->e_para[1] *= s->local2base;
s->e_para[2] *= s->local2base;
if (a->es_ars_crv < 0 || a->es_ars_col < 0) {
bu_log("No ARS point selected\n");
return;
@@ -1053,12 +1033,7 @@ ecmd_ars_move_pt(struct rt_edit *s)
dist = DIST_PNT_PLANE(s->e_mparam, view_pl);
VJOIN1(new_pt, s->e_mparam, -dist, view_pl);
} else if (s->e_inpara == 3) {
if (s->mv_context) {
/* apply s->e_invmat to convert to real model space */
MAT4X3PNT(new_pt, s->e_invmat, s->e_para);
} else {
VMOVE(new_pt, s->e_para);
}
ars_numeric_target(new_pt, s);
} else if (s->e_inpara && s->e_inpara != 3) {
bu_vls_printf(s->log_str, "x y z coordinates required for point movement\n");
rt_edit_map_clbk_get(&f, &d, s->m, ECMD_PRINT_RESULTS, BU_CLBK_DURING);
@@ -1156,6 +1131,11 @@ rt_edit_ars_edit(struct rt_edit *s)
return edit_generic(s);
}
/* Native edits may move the first point or resize a curve. */
struct rt_ars_internal *ars = (struct rt_ars_internal *)s->es_int.idb_ptr;
for (size_t i = 0; i < ars->ncurves; i++)
VMOVE(&ars->curves[i][ars->pts_per_curve * 3], ars->curves[i]);
return 0;
}

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