Compare commits

...
30 Commits
Author SHA1 Message Date
Clifford Yapp ad3cf7ecf0 proc-db: initialize gear pitch radii
The active array elements are initialized before use, but GCC PGO
analysis cannot prove the clamped gear count keeps every indexed value
within that initialized range.

Zero-initialize the fixed array so all possible elements have a defined
value without changing the computed radii.
2026-10-06 16:48:12 +00:00
Clifford Yapp 5075668363 g-x3d: suppress false LTO setjmp warning
GCC can report an anonymous value as maybe uninitialized when LTO
examines the BU_SETJUMP setjmp and longjmp control flow.  The diagnostic
is emitted during linking, so source pragmas cannot affect it.

Check for the linker option and suppress this warning only for the
g-x3d LTO link.  Normal compilation continues to treat the warning as
an error, leaving genuine uninitialized uses visible.
2026-10-06 16:47:58 +00:00
Clifford Yapp d09b59863a step: avoid GCC vector insertion false positive
GCC 14 lowers vector range insertion to a bulk move and can infer an
impossible negative size during PGO, reporting stringop-overflow.

Append the known positions individually.  This preserves their order
and complexity while avoiding the invalid range analysis.
2026-10-06 16:47:43 +00:00
Clifford Yapp 685d63dd71 build: use conventional calloc argument order
GCC 14 diagnoses calls with sizeof in the element-count position as
-Wcalloc-transposed-args.  Since multiplication is commutative, these
calls allocate the intended size, but their ordering obscures intent
and fails PGO and LTO builds with -Werror.

Pass the element count first and the element size second.
2026-10-06 16:47:32 +00:00
Clifford Yapp 00e61a90aa librt: let optimizer choose local inlining
GCC PGO decisions can reject explicit inline requests for cold call
sites, and -Winline then fails the build under -Werror.

Keep these helpers private with static linkage but remove inline.
Optimized builds remain free to inline profitable calls.  Remove the
metaball declaration that is no longer externally visible.
2026-10-06 16:47:19 +00:00
Clifford Yapp 07b2237074 libbu: keep UUID namespace optional
The namespace UUID is optional, but the array parameter syntax tells
GCC the pointer is nonnull.  PGO and LTO propagate that contract and
diagnose the existing null check as contradictory.

Declare the namespace as a pointer.  Array parameters already adjust to
pointers in function types, so this preserves the ABI while accurately
describing the accepted input.
2026-10-06 16:47:05 +00:00
Clifford Yapp 5d4f1f3d01 libbu: batch SHA-1 final padding
GCC interprocedural analysis reports a string overread when
SHA1Final repeatedly calls SHA1Update with a one-byte padding
variable.  The direct block loop is unreachable for those calls,
but PGO and LTO do not retain enough range information to prove it.

Define the SHA-1 block size once and submit the complete final padding
in one update.  This avoids the false object-size path, reduces
repeated update calls, and preserves the required one- or two-block
SHA-1 finalization.
2026-10-06 15:24:32 +00:00
Clifford Yapp 0d4dfb570d Stash (not yet enabling) STABLE brancy autosyncing 2026-10-06 14:56:43 +00:00
Clifford Yapp ea81a17325 Test CI PGO building, as well as MSVC version of PGO 2026-10-06 14:56:43 +00:00
Christopher Sean Morrison e7c61497df new doc on representations 2026-10-06 10:51:33 -04:00
Christopher Sean Morrison 08da151a6e add new documentation explaining the differences between geometry representation schemes with examples 2026-10-06 10:50:13 -04:00
Clifford Yapp 7929a747ad Fix Archer saving window sizes before they're ready 2026-10-06 01:42:31 -04:00
Clifford Yapp eac0f4d265 Adjustments for Archer/Itcl 2026-10-06 00:57:13 -04:00
Clifford Yapp d6ae424de0 Correct Archer plugin setup 2026-10-06 00:47:05 -04:00
Clifford Yapp 27ba3c4880 Be more aggressive about making sure the install rpath setup is our standard one. 2026-10-05 21:29:34 +00:00
Clifford Yapp 7ade8db130 Got the test backwards 2026-10-05 15:30:29 -04:00
Clifford Yapp e91023f354 Disable rtcmp tests for a BRLCAD_ENABLE_RUNTIME_DEBUG=OFF build
rtcmp logging in librt is in the hot loop for rt_shootray, so
even the enabled/disabled conditional check imposes about 1.5%
performance overhead in an optimized build configuration.  Since
that isn't acceptable when we're disabling debugging support for
maximum performance, disable the tests in that particular case.
2026-10-05 13:50:34 -04:00
Clifford Yapp 303b90f7f1 librt: handle empty and instanced trees during bounds traversal
rt_gettree can succeed for an empty combination with no primitives,
leaving its imported tree null.  The old traversal reached
RT_CK_TREE.  With runtime checks enabled, that bombs and facetize's
setjmp catches it.  Builds with runtime checks disabled compile out
the magic check and dereference null instead.  This explains the
different outcomes across build configurations.

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

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

Added item to source commit mapping:

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

Corrections to existing items:

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

Reorder the existing facetize and pnts export entries to retain the
newest-first organization of the release section.
2026-10-02 19:10:21 -04:00
53 changed files with 2324 additions and 280 deletions
+44 -2
View File
@@ -384,7 +384,7 @@ jobs:
osx:
name: macOS Clang
needs: [linux]
runs-on: macos-14
runs-on: macos-15
strategy:
fail-fast: true
steps:
@@ -403,7 +403,7 @@ jobs:
- name: Prepare bext dependencies
uses: ./brlcad/.github/actions/prepare-bext
with:
platform-channel: macos-14-${{ runner.arch }}-release
platform-channel: macos-15-${{ runner.arch }}-release
source-dir: ${{ github.workspace }}/brlcad
output-dir: ${{ runner.temp }}/bext_output
build-type: Release
@@ -447,3 +447,45 @@ jobs:
export PATH=$GITHUB_WORKSPACE:$PATH
make package
working-directory: ${{ github.workspace }}/build_osx
# BRL-CAD's STABLE branch is intended to represent the most recent
# SHA1 that has successfully passed our primary tests on our CI
# platforms. This job is intended to execute that update if the
# prior tasks succeed. It's "needs" section should contain every
# test that is not already the dependency of another tests.
# sync-stable-branch:
# name: Sync STABLE branch
# runs-on: ubuntu-latest
# needs: [archives, componentbext, windows_std_tools, osx]
# # This job should only run on a successful push to the main branch
# if: github.ref == 'refs/heads/main' && github.event_name == 'push'
# steps:
# # 1. Generate a short-lived token using the GitHub App
# - name: Generate GitHub App Token
# id: generate_token
# uses: tibdex/github-app-token@v2
# with:
# app_id: ${{ secrets.STABLESYNC_APP_ID }}
# private_key: ${{ secrets.STABLESYNC_APP_PRIVATE_KEY }}
# # 2. Checkout the repository using the App Token
# - name: Checkout Code
# uses: actions/checkout@v4
# with:
# token: ${{ steps.generate_token.outputs.token }}
# fetch-depth: 0 # Fetches all history to safely move branches
# # 3. Securely update and push the STABLE branch pointer
# - name: Update STABLE Branch Pointer
# run: |
# # Configure Git to identify as the GitHub Actions bot
# git config --global user.name "github-actions[bot]"
# git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
# echo "Updating STABLE branch to point to the latest successfully CI tested main SHA: ${{ github.sha }}"
# # Force the local STABLE branch to point directly to the current main SHA
# git branch -f STABLE ${{ github.sha }}
# # Push the updated stable branch reference securely back to origin
# git push origin STABLE --force
+353 -22
View File
@@ -15,16 +15,24 @@ on:
schedule:
- cron: '0 3 * * *' # daily at 03:00 UTC
workflow_dispatch:
inputs:
branch:
description: 'The branch ref to run the conditional jobs on (e.g., refs/heads/pgo)'
required: true
default: 'refs/heads/main'
type: string
env:
DEBIAN_FRONTEND: noninteractive
CLANG_VERSION: 18
jobs:
linux_bext:
name: Linux - BRL-CAD bext build
runs-on: ubuntu-latest
runs-on: &ubuntu_runner ubuntu-24.04 # Define the ubuntu runner anchor here
strategy:
fail-fast: true
env:
DEBIAN_FRONTEND: noninteractive
steps:
- name: Setup - CMake
uses: lukka/get-cmake@latest
@@ -177,7 +185,7 @@ jobs:
osx_bext:
name: macOS - BRL-CAD bext build
needs: [linux_bext]
runs-on: macos-14
runs-on: macos-15
strategy:
fail-fast: true
if: ${{ !cancelled() }}
@@ -225,12 +233,13 @@ jobs:
linux_static:
name: Clang Static Analyser
needs: [linux_bext]
runs-on: ubuntu-24.04
runs-on: *ubuntu_runner # Use the ubuntu runner alias
strategy:
fail-fast: true
env:
DEBIAN_FRONTEND: noninteractive
if: ${{ !cancelled() && github.ref == 'refs/heads/main' }}
if: |
!cancelled() &&
(github.event_name == 'workflow_dispatch' && github.ref == inputs.branch ||
github.event_name == 'schedule' && github.ref == 'refs/heads/main')
steps:
- name: Setup - CMake
uses: lukka/get-cmake@latest
@@ -239,19 +248,19 @@ jobs:
- name: Clang Static Analyzer Setup
run: |
sudo apt-get update
sudo apt-get install clang-18 clang-tools-18
sudo apt-get install -y clang-${{ env.CLANG_VERSION }} clang-tools-${{ env.CLANG_VERSION }}
# Install X/OpenGL dev pkgs
sudo apt-get install xserver-xorg-dev libx11-dev libxi-dev libxext-dev libglu1-mesa-dev libfontconfig-dev
sudo apt-get install -y xserver-xorg-dev libx11-dev libxi-dev libxext-dev libglu1-mesa-dev libfontconfig-dev
# Install tools
sudo apt-get install astyle re2c xsltproc libxml2-utils
sudo apt-get install -y astyle re2c xsltproc libxml2-utils
# Install dependency dev pkgs
sudo apt-get install zlib1g-dev libpng-dev libjpeg-dev libtiff-dev libeigen3-dev libgdal-dev libassimp-dev libopencv-dev tcl-dev tk-dev
sudo apt-get install -y zlib1g-dev libpng-dev libjpeg-dev libtiff-dev libeigen3-dev libgdal-dev libassimp-dev libopencv-dev tcl-dev tk-dev
# Install XCB/OpenGL dev pkgs for Qt - see:
# https://wiki.qt.io/Building_Qt_6_from_Git
# https://doc.qt.io/qt-6/linux-requirements.html
sudo apt-get install libfontconfig1-dev libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-cursor-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-util-dev libxcb-xinerama0-dev libxcb-xkb-dev libxcb-xinput-dev libxcb-xrm-dev libxkbcommon-dev libxkbcommon-x11-dev
sudo apt-get install libgl-dev
sudo apt-get install libinput-dev # Cleanup
sudo apt-get install -y libfontconfig1-dev libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-cursor-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-util-dev libxcb-xinerama0-dev libxcb-xkb-dev libxcb-xinput-dev libxcb-xrm-dev libxkbcommon-dev libxkbcommon-x11-dev
sudo apt-get install -y libgl-dev
sudo apt-get install -y libinput-dev
- name: Checkout
uses: actions/checkout@v6
@@ -261,7 +270,7 @@ jobs:
- name: Prepare bext dependencies
uses: ./brlcad/.github/actions/prepare-bext
with:
platform-channel: ubuntu-24.04-${{ runner.arch }}-release
platform-channel: ubuntu-*ubuntu_version-${{ runner.arch }}-release
source-dir: ${{ github.workspace }}/brlcad
output-dir: ${{ runner.temp }}/bext_output
build-type: Release
@@ -278,7 +287,7 @@ jobs:
env:
BRLCAD_EXT_DIR: ${{ runner.temp }}/bext_output
run: |
export PATH=$GITHUB_WORKSPACE:/usr/share/clang/scan-build-18/bin:/usr/share/clang/scan-build-18/libexec:$PATH
# The script will now receive the version number and find the tools itself
cp $GITHUB_WORKSPACE/brlcad/misc/clang-static-analyzer-run.sh .
./clang-static-analyzer-run.sh
working-directory: ${{ github.workspace }}/brlcad/build_clangstatic
@@ -290,17 +299,339 @@ jobs:
html2text scan-reports-all/*/index.html
working-directory: ${{ github.workspace }}/brlcad/build_clangstatic
linux_pgo:
name: GCC Profile-Guided Optimization
needs: [linux_bext]
runs-on: *ubuntu_runner # Use the ubuntu runner alias
timeout-minutes: 180
strategy:
fail-fast: true
env:
CMAKE_BUILD_PARALLEL_LEVEL: "2"
PGO_PATH: ${{ github.workspace }}/pgo-profile
PGO_BENCHMARK_TARGETS: >-
rt pixcmp benchmark_cp BENCHMARK_PIX_cp PIX_LOG_cp
bldg391.g m35.g moss.g sphflake.g star.g world.g
PGO_TRAINING_TARGETS: >-
havoc.g Generic_Twin.g NIST_MBE_PMI_2.g
PGO_TRAINING_SIZE: "512"
PGO_TRAINING_AZIMUTH: "35"
PGO_TRAINING_ELEVATION: "25"
PGO_BENCHMARK_TIMEFRAME: "8"
PGO_BENCHMARK_MAXTIME: "60"
PGO_BENCHMARK_AVERAGE: "3"
PGO_BENCHMARK_DEVIATION: "3"
PGO_MIN_SPEEDUP: "1.0"
if: |
!cancelled() &&
(github.event_name == 'workflow_dispatch' && github.ref == inputs.branch ||
github.event_name == 'schedule' && github.ref == 'refs/heads/main')
steps:
- name: Setup - CMake
uses: lukka/get-cmake@latest
- name: Install apt dependencies
run: |
sudo apt-get update
# Install X/OpenGL dev pkgs
sudo apt-get install xserver-xorg-dev libx11-dev libxi-dev libxext-dev libglu1-mesa-dev libfontconfig-dev
# Install tools
sudo apt-get install astyle re2c xsltproc libxml2-utils
# Install dependency dev pkgs
sudo apt-get install zlib1g-dev libpng-dev libjpeg-dev libtiff-dev libeigen3-dev libgdal-dev libassimp-dev libopencv-dev tcl-dev tk-dev
# Install XCB/OpenGL dev pkgs for a fallback bext build
sudo apt-get install libfontconfig1-dev libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-cursor-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-util-dev libxcb-xinerama0-dev libxcb-xkb-dev libxcb-xinput-dev libxcb-xrm-dev libxkbcommon-dev libxkbcommon-x11-dev
sudo apt-get install libgl-dev libinput-dev
- name: Checkout
uses: actions/checkout@v6
with:
path: brlcad
- name: Prepare bext dependencies
uses: ./brlcad/.github/actions/prepare-bext
with:
platform-channel: ubuntu-*ubuntu_version-${{ runner.arch }}-release
source-dir: ${{ github.workspace }}/brlcad
output-dir: ${{ runner.temp }}/bext_output
build-type: Release
artifact-token: ${{ secrets.BEXT_ARTIFACT_TOKEN }}
- name: Directory setup
run: |
set -euo pipefail
cmake -E make_directory $HOME/.cache/BRL-CAD
if [[ -e "$PGO_PATH" ]]; then
echo "::error::PGO profile path unexpectedly exists before the generation build"
exit 1
fi
- name: Configure baseline build
run: |
cmake -S brlcad -B build_pgo_baseline -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_TESTING=ON \
-DBRLCAD_EXT_DIR=${{ runner.temp }}/bext_output \
-DBRLCAD_ENABLE_QT=OFF \
-DBRLCAD_ENABLE_STEP=ON \
-DBRLCAD_ENABLE_STRICT=ON
- name: Build baseline
run: cmake --build build_pgo_baseline --target $PGO_BENCHMARK_TARGETS
- name: Configure profile-generation build
run: |
cmake -S brlcad -B build_pgo -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_TESTING=ON \
-DBRLCAD_EXT_DIR=${{ runner.temp }}/bext_output \
-DBRLCAD_ENABLE_QT=OFF \
-DBRLCAD_ENABLE_STEP=ON \
-DBRLCAD_ENABLE_STRICT=ON \
-DBRLCAD_PGO=ON
grep -q -- '-fprofile-generate=' build_pgo/compile_commands.json
- name: Build profile-generation binaries
run: cmake --build build_pgo --target $PGO_BENCHMARK_TARGETS $PGO_TRAINING_TARGETS
- name: Generate profile data
run: |
cmake \
-DPGO_MODE=TRAIN \
-DPGO_BUILD_DIR="$GITHUB_WORKSPACE/build_pgo" \
-DPGO_RESULTS_DIR="$GITHUB_WORKSPACE/pgo-training" \
-DPGO_PROFILE_DIR="$PGO_PATH" \
-DPGO_TIMEFRAME="$PGO_BENCHMARK_TIMEFRAME" \
-DPGO_MAXTIME="$PGO_BENCHMARK_MAXTIME" \
-DPGO_AVERAGE="$PGO_BENCHMARK_AVERAGE" \
-DPGO_DEVIATION="$PGO_BENCHMARK_DEVIATION" \
-DPGO_TRAINING_SIZE="$PGO_TRAINING_SIZE" \
-DPGO_TRAINING_AZIMUTH="$PGO_TRAINING_AZIMUTH" \
-DPGO_TRAINING_ELEVATION="$PGO_TRAINING_ELEVATION" \
-P brlcad/misc/CMake/scripts/pgo_benchmark.cmake
- name: Configure profile-use build
run: |
# GCC incorporates the build path into profile filenames, so both
# compilation passes must use the same build directory.
cmake -E rm -rf build_pgo
cmake -S brlcad -B build_pgo -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_TESTING=ON \
-DBRLCAD_EXT_DIR=${{ runner.temp }}/bext_output \
-DBRLCAD_ENABLE_QT=OFF \
-DBRLCAD_ENABLE_STEP=ON \
-DBRLCAD_ENABLE_STRICT=ON \
-DBRLCAD_PGO=ON
grep -q -- '-fprofile-use=' build_pgo/compile_commands.json
- name: Build profile-guided binaries
run: cmake --build build_pgo --target $PGO_BENCHMARK_TARGETS
- name: Benchmark and compare profile-guided build
run: |
cmake \
-DPGO_MODE=COMPARE \
-DPGO_BASELINE_BUILD_DIR="$GITHUB_WORKSPACE/build_pgo_baseline" \
-DPGO_OPTIMIZED_BUILD_DIR="$GITHUB_WORKSPACE/build_pgo" \
-DPGO_RESULTS_DIR="$GITHUB_WORKSPACE/pgo-results" \
-DPGO_TIMEFRAME="$PGO_BENCHMARK_TIMEFRAME" \
-DPGO_MAXTIME="$PGO_BENCHMARK_MAXTIME" \
-DPGO_AVERAGE="$PGO_BENCHMARK_AVERAGE" \
-DPGO_DEVIATION="$PGO_BENCHMARK_DEVIATION" \
-DPGO_MIN_SPEEDUP="$PGO_MIN_SPEEDUP" \
-P brlcad/misc/CMake/scripts/pgo_benchmark.cmake
windows_pgo:
name: MSVC Profile-Guided Optimization
needs: [windows_bext]
runs-on: windows-latest
timeout-minutes: 180
strategy:
fail-fast: true
env:
CMAKE_BUILD_PARALLEL_LEVEL: "2"
PGO_BENCHMARK_TARGETS: >-
rt pixcmp benchmark_cp BENCHMARK_PIX_cp PIX_LOG_cp
bldg391.g m35.g moss.g sphflake.g star.g world.g
PGO_TRAINING_TARGETS: >-
havoc.g Generic_Twin.g NIST_MBE_PMI_2.g
PGO_TRAINING_SIZE: "512"
PGO_TRAINING_AZIMUTH: "35"
PGO_TRAINING_ELEVATION: "25"
PGO_BENCHMARK_TIMEFRAME: "8"
PGO_BENCHMARK_MAXTIME: "60"
PGO_BENCHMARK_AVERAGE: "3"
PGO_BENCHMARK_DEVIATION: "3"
PGO_MIN_SPEEDUP: "1.0"
if: |
!cancelled() &&
(github.event_name == 'workflow_dispatch' && github.ref == inputs.branch ||
github.event_name == 'schedule' && github.ref == 'refs/heads/main')
steps:
- name: Setup - CMake
uses: lukka/get-cmake@latest
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Add cl.exe to PATH
uses: ilammy/msvc-dev-cmd@v1
- name: Checkout
uses: actions/checkout@v6
with:
path: brlcad
- name: Prepare bext dependencies
uses: ./brlcad/.github/actions/prepare-bext
with:
platform-channel: windows-2025-${{ runner.arch }}-release
source-dir: ${{ github.workspace }}/brlcad
output-dir: ${{ runner.temp }}/bext_output
build-type: Release
artifact-token: ${{ secrets.BEXT_ARTIFACT_TOKEN }}
- name: Set up PGO paths
shell: pwsh
run: |
# Short build roots keep STEP-generated paths below Windows MAX_PATH.
$paths = @{
PGO_PATH = Join-Path $env:RUNNER_TEMP 'pgo-d'
PGO_BASELINE_BUILD_DIR = Join-Path $env:RUNNER_TEMP 'pgo-b'
PGO_GENERATE_BUILD_DIR = Join-Path $env:RUNNER_TEMP 'pgo-g'
PGO_OPTIMIZED_BUILD_DIR = Join-Path $env:RUNNER_TEMP 'pgo-u'
PGO_TRAINING_RESULTS_DIR = Join-Path $env:RUNNER_TEMP 'pgo-t'
PGO_RESULTS_DIR = Join-Path $env:RUNNER_TEMP 'pgo-r'
}
if (Test-Path -LiteralPath $paths.PGO_PATH) {
throw "PGO profile path unexpectedly exists before the generation build: $($paths.PGO_PATH)"
}
foreach ($path in $paths.GetEnumerator()) {
Add-Content -Path $env:GITHUB_ENV -Value "$($path.Key)=$($path.Value)"
}
- name: Configure baseline build
shell: pwsh
run: |
cmake -S brlcad -B "$env:PGO_BASELINE_BUILD_DIR" -G Ninja `
-DCMAKE_C_COMPILER=cl.exe `
-DCMAKE_CXX_COMPILER=cl.exe `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_TESTING=ON `
"-DBRLCAD_EXT_DIR=${{ runner.temp }}/bext_output" `
-DBRLCAD_ENABLE_QT=OFF `
-DBRLCAD_ENABLE_STEP=ON `
-DBRLCAD_ENABLE_STRICT=ON
- name: Build baseline
shell: pwsh
run: |
$targets = $env:PGO_BENCHMARK_TARGETS -split '\s+'
$arguments = @('--build', $env:PGO_BASELINE_BUILD_DIR, '--target') + $targets
cmake @arguments
- name: Configure profile-generation build
shell: pwsh
run: |
cmake -S brlcad -B "$env:PGO_GENERATE_BUILD_DIR" -G Ninja `
-DCMAKE_C_COMPILER=cl.exe `
-DCMAKE_CXX_COMPILER=cl.exe `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_TESTING=ON `
"-DBRLCAD_EXT_DIR=${{ runner.temp }}/bext_output" `
-DBRLCAD_ENABLE_QT=OFF `
-DBRLCAD_ENABLE_STEP=ON `
-DBRLCAD_ENABLE_STRICT=ON `
-DBRLCAD_PGO=ON
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
if (-not (Select-String -Path "$env:PGO_GENERATE_BUILD_DIR/build.ninja" -SimpleMatch '/GENPROFILE:PGD=' -Quiet)) {
throw 'The profile-generation build does not contain /GENPROFILE.'
}
- name: Build profile-generation binaries
shell: pwsh
run: |
$targets = ($env:PGO_BENCHMARK_TARGETS + ' ' + $env:PGO_TRAINING_TARGETS) -split '\s+'
$arguments = @('--build', $env:PGO_GENERATE_BUILD_DIR, '--target') + $targets
cmake @arguments
- name: Generate profile data
shell: pwsh
run: |
cmake `
-DPGO_MODE=TRAIN `
"-DPGO_BUILD_DIR=$env:PGO_GENERATE_BUILD_DIR" `
"-DPGO_RESULTS_DIR=$env:PGO_TRAINING_RESULTS_DIR" `
"-DPGO_PROFILE_DIR=$env:PGO_PATH" `
"-DPGO_TIMEFRAME=$env:PGO_BENCHMARK_TIMEFRAME" `
"-DPGO_MAXTIME=$env:PGO_BENCHMARK_MAXTIME" `
"-DPGO_AVERAGE=$env:PGO_BENCHMARK_AVERAGE" `
"-DPGO_DEVIATION=$env:PGO_BENCHMARK_DEVIATION" `
"-DPGO_TRAINING_SIZE=$env:PGO_TRAINING_SIZE" `
"-DPGO_TRAINING_AZIMUTH=$env:PGO_TRAINING_AZIMUTH" `
"-DPGO_TRAINING_ELEVATION=$env:PGO_TRAINING_ELEVATION" `
-P brlcad/misc/CMake/scripts/pgo_benchmark.cmake
- name: Configure profile-use build
shell: pwsh
run: |
cmake -S brlcad -B "$env:PGO_OPTIMIZED_BUILD_DIR" -G Ninja `
-DCMAKE_C_COMPILER=cl.exe `
-DCMAKE_CXX_COMPILER=cl.exe `
-DCMAKE_BUILD_TYPE=Release `
-DBUILD_TESTING=ON `
"-DBRLCAD_EXT_DIR=${{ runner.temp }}/bext_output" `
-DBRLCAD_ENABLE_QT=OFF `
-DBRLCAD_ENABLE_STEP=ON `
-DBRLCAD_ENABLE_STRICT=ON `
-DBRLCAD_PGO=ON
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
if (-not (Select-String -Path "$env:PGO_OPTIMIZED_BUILD_DIR/build.ninja" -SimpleMatch '/USEPROFILE:PGD=' -Quiet)) {
throw 'The profile-use build does not contain /USEPROFILE.'
}
- name: Build profile-guided binaries
shell: pwsh
run: |
$targets = $env:PGO_BENCHMARK_TARGETS -split '\s+'
$arguments = @('--build', $env:PGO_OPTIMIZED_BUILD_DIR, '--target') + $targets
cmake @arguments
- name: Benchmark and compare profile-guided build
shell: pwsh
run: |
cmake `
-DPGO_MODE=COMPARE `
"-DPGO_BASELINE_BUILD_DIR=$env:PGO_BASELINE_BUILD_DIR" `
"-DPGO_OPTIMIZED_BUILD_DIR=$env:PGO_OPTIMIZED_BUILD_DIR" `
"-DPGO_RESULTS_DIR=$env:PGO_RESULTS_DIR" `
"-DPGO_TIMEFRAME=$env:PGO_BENCHMARK_TIMEFRAME" `
"-DPGO_MAXTIME=$env:PGO_BENCHMARK_MAXTIME" `
"-DPGO_AVERAGE=$env:PGO_BENCHMARK_AVERAGE" `
"-DPGO_DEVIATION=$env:PGO_BENCHMARK_DEVIATION" `
"-DPGO_MIN_SPEEDUP=$env:PGO_MIN_SPEEDUP" `
-P brlcad/misc/CMake/scripts/pgo_benchmark.cmake
# See https://app.codecov.io/gh/BRL-CAD/brlcad for analysis results
linux_cov:
name: LCOV Coverage testing
needs: [linux_bext]
runs-on: ubuntu-24.04
runs-on: *ubuntu_runner # Use the ubuntu runner alias
strategy:
fail-fast: true
env:
DEBIAN_FRONTEND: noninteractive
if: ${{ !cancelled() && github.ref == 'refs/heads/main' }}
if: |
!cancelled() &&
(github.event_name == 'workflow_dispatch' && github.ref == inputs.branch ||
github.event_name == 'schedule' && github.ref == 'refs/heads/main')
steps:
- name: Setup - CMake
uses: lukka/get-cmake@latest
@@ -331,7 +662,7 @@ jobs:
- name: Prepare bext dependencies
uses: ./brlcad/.github/actions/prepare-bext
with:
platform-channel: ubuntu-24.04-${{ runner.arch }}-release
platform-channel: ubuntu-*ubuntu_version-${{ runner.arch }}-release
source-dir: ${{ github.workspace }}/brlcad
output-dir: ${{ runner.temp }}/bext_output
build-type: Release
+109 -10
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(0682795bcfe34644c17e5a4bdd0225c76f6bdc98)
brlcad_bext_init(5ccf9b577dbccef244e2fea7e2741027f0c83555)
#---------------------------------------------------------------------
# Define various utilities.
@@ -352,9 +352,10 @@ set(CMAKE_SKIP_BUILD_RPATH FALSE)
# it should only be set to the installation value when actually installed.
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
# Add the automatically determined parts of the RPATH which point to
# directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# Installed runtime paths are defined explicitly below. Do not append
# directories discovered on the build host, since those paths may not exist
# or may resolve to unrelated libraries on the installed system.
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
# The following logic is what allows binaries to run successfully in the build
# directory AND install directory.
@@ -1337,6 +1338,90 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
brlcad_deferred_define("BRLCAD_HAS_BIG_STACK 1")
endif()
# Profile-Guided Optimization uses a shared profile location for both compiler
# families. A path that does not exist selects the instrumentation pass; an
# existing path selects the profile-use pass in a fresh build tree.
if("$ENV{PGO_PATH}" STREQUAL "")
set(BRLCAD_PGO_PATH "${CMAKE_BINARY_DIR}/profiling")
else()
file(TO_CMAKE_PATH "$ENV{PGO_PATH}" BRLCAD_PGO_PATH)
endif()
# MSVC creates one profile database per linked image. The target-dependent
# generator expression gives every executable and DLL a distinct database,
# while /GL also covers code contributed through object and static libraries.
if(MSVC AND BRLCAD_PGO)
if(DEFINED BRLCAD_MSVC_PGO_PHASE)
if(NOT "${BRLCAD_MSVC_PGO_PATH}" STREQUAL "${BRLCAD_PGO_PATH}")
message(
FATAL_ERROR
"PGO_PATH changed from '${BRLCAD_MSVC_PGO_PATH}' to '${BRLCAD_PGO_PATH}'. "
"Use a fresh build directory when changing PGO phases or profile locations."
)
endif()
elseif(EXISTS "${BRLCAD_PGO_PATH}")
set(BRLCAD_MSVC_PGO_PHASE USE)
else()
set(BRLCAD_MSVC_PGO_PHASE GENERATE)
endif()
if(NOT BRLCAD_MSVC_PGO_PHASE MATCHES "^(GENERATE|USE)$")
message(FATAL_ERROR "Invalid internal MSVC PGO phase '${BRLCAD_MSVC_PGO_PHASE}'")
endif()
set(BRLCAD_MSVC_PGO_PHASE "${BRLCAD_MSVC_PGO_PHASE}" CACHE INTERNAL "MSVC PGO build phase" FORCE)
set(BRLCAD_MSVC_PGO_PATH "${BRLCAD_PGO_PATH}" CACHE INTERNAL "MSVC PGO profile path" FORCE)
if(BRLCAD_MSVC_PGO_PHASE STREQUAL "GENERATE")
# link.exe requires the PGD parent directory to exist before it links the
# instrumented image. Cache the phase above so an in-place CMake rerun
# does not mistake this newly created directory for completed profiles.
file(MAKE_DIRECTORY "${BRLCAD_PGO_PATH}")
set(_brlcad_msvc_pgo_link_mode GENPROFILE)
message(
STATUS
"MSVC PGO generation enabled; set VCPROFILE_PATH=${BRLCAD_PGO_PATH} while running training workloads"
)
distclean("${BRLCAD_PGO_PATH}")
else()
message(STATUS "MSVC PGO profile use enabled from ${BRLCAD_PGO_PATH}")
endif()
add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:/GL>")
add_link_options(/LTCG)
if(_brlcad_msvc_pgo_link_mode)
add_link_options(
"/${_brlcad_msvc_pgo_link_mode}:PGD=${BRLCAD_PGO_PATH}/$<TARGET_PROPERTY:NAME>.pgd"
)
endif()
unset(_brlcad_msvc_pgo_link_mode)
endif()
# Apply profile-use only to images exercised during training. link.exe emits
# C4961 for an untrained PGD, and strict builds promote that warning to an
# error during LTCG. The generation pass names PGC files after the target's
# PGD, so this check also works when OUTPUT_NAME changes the binary name.
function(_BRLCAD_MSVC_PGO_APPLY_PROFILES directory)
get_property(_brlcad_pgo_targets DIRECTORY "${directory}" PROPERTY BUILDSYSTEM_TARGETS)
foreach(_brlcad_pgo_target IN LISTS _brlcad_pgo_targets)
get_target_property(_brlcad_pgo_target_type ${_brlcad_pgo_target} TYPE)
if(_brlcad_pgo_target_type MATCHES "^(EXECUTABLE|SHARED_LIBRARY|MODULE_LIBRARY)$")
set(_brlcad_pgo_database "${BRLCAD_PGO_PATH}/${_brlcad_pgo_target}.pgd")
file(GLOB _brlcad_pgo_counts LIST_DIRECTORIES FALSE "${BRLCAD_PGO_PATH}/${_brlcad_pgo_target}![0-9]*.pgc")
if(EXISTS "${_brlcad_pgo_database}" AND _brlcad_pgo_counts)
target_link_options(${_brlcad_pgo_target} PRIVATE "/USEPROFILE:PGD=${_brlcad_pgo_database}")
set_property(GLOBAL APPEND PROPERTY BRLCAD_MSVC_PGO_TRAINED_TARGETS ${_brlcad_pgo_target})
endif()
unset(_brlcad_pgo_counts)
unset(_brlcad_pgo_database)
endif()
endforeach()
get_property(_brlcad_pgo_subdirectories DIRECTORY "${directory}" PROPERTY SUBDIRECTORIES)
foreach(_brlcad_pgo_subdirectory IN LISTS _brlcad_pgo_subdirectories)
_brlcad_msvc_pgo_apply_profiles("${_brlcad_pgo_subdirectory}")
endforeach()
endfunction()
# The following tests are almost entirely for gcc/llvm style flags, so
# we don't unnecessarily slow down the Windows build.
if(NOT MSVC)
@@ -1399,11 +1484,6 @@ if(NOT MSVC)
# variable set to a temp folder
set(pgo_flags_found "")
if("$ENV{PGO_PATH}" STREQUAL "")
set(BRLCAD_PGO_PATH "${CMAKE_BINARY_DIR}/profiling")
else(NOT "$ENV{PGO_PATH}" STREQUAL "")
set(BRLCAD_PGO_PATH "$ENV{PGO_PATH}")
endif("$ENV{PGO_PATH}" STREQUAL "")
if(BRLCAD_PGO AND NOT EXISTS "${BRLCAD_PGO_PATH}")
if("${CMAKE_C_FLAGS}" MATCHES ".*fprofile-use.*")
@@ -1449,7 +1529,8 @@ if(NOT MSVC)
# gcc-style USE
if("${pgo_flags_found}" STREQUAL "")
cmake_push_check_state()
set(flags "fprofile-use=\"${BRLCAD_PGO_PATH}\" -Wno-error=coverage-mismatch -fprofile-correction")
set(flags "fprofile-use=\"${BRLCAD_PGO_PATH}\" -Wno-error=coverage-mismatch")
string(APPEND flags " -Wno-error=missing-profile -fprofile-correction")
set(CMAKE_REQUIRED_FLAGS "-${flags}")
check_c_flag("${flags}" GROUPS OPTIMIZE_C_FLAGS VARS pgo_flags_found)
check_cxx_flag("${flags}" GROUPS OPTIMIZE_CXX_FLAGS)
@@ -2972,6 +3053,24 @@ verbose_add_subdirectory("" doc)
# NOTE: misc/tools is added separately (earlier)
verbose_add_subdirectory("" misc)
if(MSVC AND BRLCAD_PGO AND BRLCAD_MSVC_PGO_PHASE STREQUAL "USE")
set_property(GLOBAL PROPERTY BRLCAD_MSVC_PGO_TRAINED_TARGETS)
_brlcad_msvc_pgo_apply_profiles("${CMAKE_SOURCE_DIR}")
get_property(_brlcad_msvc_pgo_targets GLOBAL PROPERTY BRLCAD_MSVC_PGO_TRAINED_TARGETS)
list(LENGTH _brlcad_msvc_pgo_targets _brlcad_msvc_pgo_target_count)
if(_brlcad_msvc_pgo_target_count EQUAL 0)
message(
FATAL_ERROR
"No MSVC PGO counter files were found in ${BRLCAD_PGO_PATH}. "
"Set VCPROFILE_PATH to that directory while running the training workloads "
"before configuring the profile-use build."
)
endif()
message(STATUS "MSVC PGO will use training data for ${_brlcad_msvc_pgo_target_count} linked targets")
unset(_brlcad_msvc_pgo_target_count)
unset(_brlcad_msvc_pgo_targets)
endif()
#------------------------------------------------------------------------------
# CMake package configuration files for modern find_package(BRLCAD CONFIG)
#
+85 -48
View File
@@ -10,82 +10,108 @@ descriptive text that emphasizes or further describes significant
changes made. See document footer for additional details.
----------------------------------------------------------------------
--- 20XX-XX-XX Release 7.46.0 ---
--- 2026-10-0X Release 7.46.0 ---
----------------------------------------------------------------------
This release expands geometry conversion and improves modeling
reliability. STEP support adds AP203e2, AP214, and AP242
import/export, native CSG round trips, and AP242 product and
manufacturing information (PMI) import. IGES and JT conversion is
expanded, and new OpenSCAD CSG import/export tools are available.
Facetization is more reliable, NMG solid ray tracing is 10-20 times
faster, CSG ray tracing is faster, and BoT, DSP, and point-cloud ray
intersections are corrected. MGED gains numerous fixes to primitive,
sketch, and combination editing, drawing, dialogs, and preferences.
New tools support object nesting with arrange, building generation
with mkbuilding, annotation editing, and primitive repair. Geometry
analysis, image utilities, Windows support, documentation, and bundled
dependencies also receive substantial updates.
* corrected Archer logic for saving window size - Cliff Yapp
* fixed inside BoT rays, DSP surface normals, PNTS bounds - Cliff Yapp
* improved mged fallback HTML manual viewer - Cliff Yapp
* fixed gqa undefined densities and exit status - Chris McGregor
* added ARBN support to the mged inside command - Cliff Yapp
* improved HYP neck-ratio labels and BoT face selection - Cliff Yapp
* fixed mged view panning, edit rotation, & ray queries - Cliff Yapp
* preserved ARB, CLINE, TGC, & extrusion geometry edits - Cliff Yapp
* restored mged command prompt focus at GUI startup - Cliff Yapp
* new documentation on geometry representation schemes - Sean Morrison
* improved pixscale & bwscale scaling and image piping - Sean Morrison
* restored pixdiff behavior on .pix, added -b for .bw - Sean Morrison
* updated MGED and Archer for Tcl 8.6.18 and Itcl/Itk 4 - Cliff Yapp
* improved facetize fallback methods and reliability - Cliff Yapp
* updated mged and Archer for Tcl 8.6.18 and Itcl/Itk 4 - Cliff Yapp
* fixed writes to fileless geometry databases - Cliff Yapp
* fixed nastran-g handling of large property IDs - Rupesh, Cliff Yapp
* fixed dxf-g invalid polyface index crashes - Meghna45-code, Cliff Yapp
* fixed Archer primitive editors under Itcl 4 - Cliff Yapp
* fixed Archer shutdown menu cleanup errors - Cliff Yapp
* fixed MGED Build Pattern dialog under Itcl 4 - Cliff Yapp
* fixed dxf-g invalid polyface idx crash - Meghna45-code, Cliff Yapp
* fixed TclCAD framebuffer listing and deletion - Cliff Yapp
* fixed MGED Build Pattern progress updates - Cliff Yapp
* fixed MGED search -exec reentry and Ctrl-C crashes - Cliff Yapp
* fixed MGED asynchronous commands and clone progress - Cliff Yapp
* accelerated large and repeated MGED path drawing - Cliff Yapp
* fixed mged Build Pattern progress updates - Cliff Yapp
* fixed mged search -exec reentry and Ctrl-C crashes - Cliff Yapp
* fixed mged asynchronous commands and clone progress - Cliff Yapp
* accelerated large and repeated mged path drawing - Cliff Yapp
* fixed ARB7 inside solid construction - Cliff Yapp
* fixed direct ARB face and edge-direction editing - Cliff Yapp
* fixed rtwizard startup with framebuffer contention - Cliff Yapp
* fixed Archer annotation line display updates - Cliff Yapp
* fixed Archer sketch scale drift in non-mm databases - Cliff Yapp
* fixed toggle arrow callbacks during setup - Cliff Yapp
* fixed Tcl dialogs with multi-word titles and messages - Cliff Yapp
* fixed Tcl dialogs with multi-word titles and msgs - Cliff Yapp
* fixed Archer Tank Wizard gun generation - Cliff Yapp
* fixed MGED and Archer sketch arc tangency editing - Cliff Yapp
* fixed mged and Archer sketch arc tangency editing - Cliff Yapp
* fixed Archer ARB4 point 3 editing - Cliff Yapp
* fixed ARB4 storage from make and mk_arb4 - Cliff Yapp
* fixed ARB4 and ARB6 center point calculations - Cliff Yapp
* fixed concurrent MGED and rtwizard framebuffer use - Cliff Yapp
* fixed MGED ShotVis editing and reload failures - Cliff Yapp
* fixed concurrent mged and rtwizard framebuffer use - Cliff Yapp
* fixed mged ShotVis editing and reload failures - Cliff Yapp
* fixed Tcl widget geometry and selection defects - Cliff Yapp
* fixed MGED BoT editor property handling - Cliff Yapp
* fixed MGED LOD dialog and adaptive BoT wireframes - Cliff Yapp
* fixed MGED Plot, PostScript, and manual launch tools - Cliff Yapp
* fixed MGED Overlap Tool multi-object result handling - Cliff Yapp
* fixed mged BoT editor property handling - Cliff Yapp
* fixed mged LOD dialog and adaptive BoT wireframes - Cliff Yapp
* fixed mged Plot, PostScript, and manual launch tools - Cliff Yapp
* fixed mged Overlap Tool multi-object result handling - Cliff Yapp
* eliminated 16-fold redundant gchecker grid analysis - Cliff Yapp
* fixed Archer tree handling for object names with spaces - Cliff Yapp
* fixed submodel transforms & relative database paths - Sean Morrison
* fixed Archer tree handling for names with spaces - Cliff Yapp
* fixed RtWizard multi-object edge occlusion handling - Cliff Yapp
* fixed RtWizard title, About, and preference handling - Cliff Yapp
* fixed MGED startup file false legacy-syntax warnings - Cliff Yapp
* fixed MGED saved-view deletion and collaboration sync - Cliff Yapp
* fixed MGED font preferences apply and persistence - Cliff Yapp
* fixed mged startup file false legacy-syntax warnings - Cliff Yapp
* fixed mged saved-view deletion and collaboration sync - Cliff Yapp
* fixed mged font preferences apply and persistence - Cliff Yapp
* fixed Archer combination edits changing Boolean trees - Cliff Yapp
* fixed Archer command-line help startup crash - Cliff Yapp
* fixed MGED Geometry Browser menu and close behavior - Cliff Yapp
* fixed MGED collaboration view unshare crash - Cliff Yapp
* added an override for Archer's preference file location - Cliff Yapp
* fixed mged Geometry Browser menu and close behavior - Cliff Yapp
* fixed mged collaboration view unshare crash - Cliff Yapp
* added override for Archer's preference file location - Cliff Yapp
* fixed embedded Archer display and mode menus - Cliff Yapp
* added Archer -h and --help command-line options - Cliff Yapp
* fixed Archer Help opening the documentation menu - Sean Morrison
* 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
* added arrange nest command for packing db objects - Sean Morrison
* added mkbuilding for parametric/OSM/CityJSON models - Sean Morrison
* cross-platform BINUNIF and DSP byte-order handling - Sean Morrison
* improved several cv default behaviors (s, 32, 64, n) - Sean Morrison
* improved several cv default behaviors (s,32,64,n) - Sean Morrison
* fixed invalid bot handling bug applying matrices - Sean Morrison
* improved glTF import robustness via gcv - Sean Morrison
* fixed bot dump glTF/GLB export; -F is now rejected - Cliff Yapp
* faster NMG solid ray tracing (10-20x faster) - Sean Morrison
* preserved primitive colors in shaded BoT and B-Rep drawing - Cliff Yapp
* preserved B-Rep attributes through in-place geometry edits - Cliff Yapp
* fixed gcv filter arguments containing one-character values - Cliff Yapp
* preserved prim colors in shaded BoT and B-Rep drawing - Cliff Yapp
* preserved B-Rep attrs through in-place geometry edits - Cliff Yapp
* fixed gcv filter arguments w/ one-character values - Cliff Yapp
* improved iges-g importer robustness - Cliff Yapp
* expanded g-iges native CSG and B-Rep export support - Cliff Yapp
* added IGES import and export support to gcv - Cliff Yapp
* rt -b query option now supports a single value index - Sean Morrison
* rt -b query option now supports a single value idx - Sean Morrison
* fixed .dpix file format portability and icv support - Sean Morrison
* expanded pixscale support for piped image data - Sean Morrison
* fixed HYP volume and ARBN totals reported by analyze - Cliff Yapp
* corrected rtarea sampling and exposed-area calculations - Cliff Yapp
* fixed VOL data-file lookup and missing-data errors - Chris McGregor
* corrected rtarea sampling/exposed-area calculations - Cliff Yapp
* bot split now groups results and preserves mesh data - Cliff Yapp
* bot decimate now preserves normals, UVs, and plate data - Cliff Yapp
* bot decimate now preserves nrmls, UVs, and plate data - Cliff Yapp
* added raytracing support for annotation objects - Cliff Yapp
* fixed MGED X embedded framebuffer display manager crash - Cliff Yapp
* fixed Windows subprocess quoting and empty arguments - Cliff Yapp
* fixed mged X embedded framebuffer display manager crash - Cliff Yapp
* fixed Windows drive-letter path handling - Cliff Yapp
* added annotate command for managing annotation objects - Cliff Yapp
* added repair command for supported invalid primitives - Cliff Yapp
* changed default new-region material code from 1 to 0 - Keith Bowman
@@ -107,26 +133,34 @@ changes made. See document footer for additional details.
* added option validation to image & framebuffer tools - Sean Morrison
* added contributor, API, command, & raytracing guides - Sean Morrison
* added and updated command and library manual pages - Sean Morrison
* added pnts write -f xyz export option - Chris McGregor
* fixed mged ARB5 point editing - Cliff Yapp
* fixed mged gqa and rtcheck stale overlap plots - Cliff Yapp
* fixed procedural tree generator crashes - Sean Morrison, Cliff Yapp
* added gaia Earth models with day/dusk/night textures - Sean Morrison
* fixed fbcolor ramps & Windows input - Cliff Yapp, Sean Morrison
* added parashot gravity trajectory path generator - Sean Morrison
* added metaball point scaling (via metaball_move_pnt) - Sean Morrison
* added metaball pnt scaling (via metaball_move_pnt) - Sean Morrison
* all raytracing apps now report object prep failures - Sean Morrison
* improved fast4-g robustness and error reporting - Cliff Yapp
* improved robustness of g-vrml plate mode handling - Cliff Yapp
* fixed MGED ARB5 point, pipe, and sketch editing - Cliff Yapp
* added pnts write -f xyz export option - Chris McGregor
* added support for tick drawing on MGED model axis - Cliff Yapp
* added support for tick drawing on mged model axis - Cliff Yapp
* added naca456 left and paired wing generation - Cliff Yapp
* added patch-g -j option, specify object thicknesses - Sean Morrison
* fixed fbcolor color ramp bug drawing the last color - Sean Morrison
* corrected bwstat median and input error handling - Sean Morrison
* fixed gist -Q quick preview option - Sean Morrison
* rt preserves eye_pt setting in command scripts - Sean Morrison
* removed trailing whitespace from nirt output - Sean Morrison
* rt -l8 heat graphs now measure CPU time - Sean Morrison
* documented gqa centroid & moments of inertia options - Sean Morrison
* pixdiff now calculates pixel differences on bw files - Sean Morrison
* fixed bwcrop single-axis crops and pixembed edge fill - Sean Morrison
* fixed bwcrop single-axis crops & pixembed edge fill - Sean Morrison
* fixed isize in non-mm databases - Josh Baker
* fixed metaball adjust clearing points on errors - Sean Morrison
* fixed benchmark+pixcmp correctness validation bug - Sean Morrison
* fixed mged gqa option and value parsing - Sean Morrison
* improved gist hierarchy overflow rendering - Chris McGregor
* fixed rtxray -o grayscale/BW output - Sean Morrison
* fbhelp writes help to stdout and rest to stderr - Sean Morrison
* FB now supports /dev/stdout and /dev/stderr devices - Sean Morrison
* fixed mged rt -C + -o background color to file bug - Sean Morrison
@@ -135,7 +169,7 @@ changes made. See document footer for additional details.
* added rtsil -o support for bw output - Sean Morrison
* added cutting plane animation generation to rtwizard - Cliff Yapp
* fixed pixdiff bug on Windows - Sean Morrison
* fixed MGED startup window placement on Windows - Chris McGregor
* fixed mged startup window placement on Windows - Chris McGregor
* fixed pixborder utility right-side edge bug - Sean Morrison
* fixed 'wavelet' image processing tool on Windows - Sean Morrison
* quieted mged killtree spurious lookup failure output - Sean Morrison
@@ -146,14 +180,16 @@ changes made. See document footer for additional details.
* new doc guide, a visual overview of BRL-CAD objects - Sean Morrison
* improved triangle quality produced by bot remesh - Cliff Yapp
* updated numerous manpage command references - Josh Baker
* fixed submodel paths relative to parent databases - Sean Morrison
* improved performance making huge combinations - Sean Morrison
* added PNTS get, adjust, form, & g2asc/asc2g support - Chris McGregor
* fixed ANNOT get, adjust, & g2asc/asc2g support - Chris McGregor
* added make -t primitive type listing - Chris McGregor
* added gcv import conversion support for vrml/wrl 1.0 - Sean Morrison
* fixed gcv appending to existing geometry databases - Sean Morrison
* added CSS names, hex, HSL/HSV to libbu color parsing - Sean Morrison
* improved OpenNURBS B-Rep import and adjustment safety - Cliff Yapp
* improved Debian application menus and MIME associations - Jordi Sayol
* improved Debian application menus/MIME associations - Jordi Sayol
* updated Tcl/Tk dependency to v8.6.18 using bundled Itcl - Cliff Yapp
* updated Clipper2 dependency to v1.5.4 - Cliff Yapp
* updated Eigen dependency to v5.0.1 - Cliff Yapp
* updated GDAL dependency to v3.13.3 - Cliff Yapp
@@ -165,7 +201,6 @@ changes made. See document footer for additional details.
* updated OpenNURBS dependency to v8.34.26223.11001 - Cliff Yapp
* updated PROJ dependency to v9.8.1 - Cliff Yapp
* updated PoissonRecon dependency to v18.75 - Cliff Yapp
* updated Tcl/Tk dependency to v8.6.18, using bundled Itcl - Cliff Yapp
* updated libjpeg-turbo dependency to v3.2.0 - Cliff Yapp
* updated libosmesa dependency to SHA1 bfa90de - Cliff Yapp
* updated libpng dependency to v1.6.58 - Cliff Yapp
@@ -274,7 +309,7 @@ and extensively improving existing docs.
* added metadata-aware image diff & nirt analysis - Cliff Yapp
* added basic PNTS point-cloud ray tracing - Sean Morrison
* improved nirt behavior in Windows bash consoles - Cliff Yapp
* added g2asc/asc2g support for PNTS, DSP, and EBM - Sean Morrison
* added g2asc/asc2g support for DSP and EBM - Sean Morrison
* updated gct mesh decimation dependency to mmesh - Cliff Yapp
* added procedural wing, propeller & tree generators - Cliff Yapp
* added ISO tire dimensions & named tread presets - Cliff Yapp
@@ -546,6 +581,7 @@ for more information.
----------------------------------------------------------------------
--- 2023-11-06 Release 7.38.0 ---
----------------------------------------------------------------------
This major release of BRL-CAD introduces significant improvements to
the robustness of the facetize command by leveraging the Manifold
library for boolean evaluation of meshes. Facetization is an operation
@@ -584,6 +620,7 @@ processing high level assemblies in BRL-CAD's example geometry files:
----------------------------------------------------------------------
--- 2023-07-26 Release 7.36.0 ---
----------------------------------------------------------------------
This major release of BRL-CAD introduces significant updates to
geometry conversion support. The update includes modernization for
NURBS importing and interaction, BoT manipulation, and incorporates
+1 -1
View File
@@ -1,5 +1,5 @@
BRL-CAD
Release 7.44.2
Release 7.46.0
http://brlcad.org/
BRL-CAD is a powerful cross-platform open source combinatorial
-9
View File
@@ -2668,15 +2668,6 @@ DOCUMENTATION
Comparison of how BRL-CAD overlaps feature-wise with various
major commercial CAD systems.
* Solid Geometry Representation Comparisons
Describe the implications, tradeoffs, and conversion issues
involved when converting between different geometry
representations. Includes describing implicit geometry,
explicit polygonal boundary representations, explicit spline
surface representations, volumetric models, wireframes,
boolean operations, feature edit operations, and parametric
modeling.
* BRL-CAD Open Source
An overview of how BRL-CAD is organized as an open source
project and community describing the developer meritocracy,
+62
View File
@@ -20,6 +20,7 @@ set(
procedural_geometry.adoc
projection_shader.adoc
renderer_showcase.adoc
solid_geometry_comparison.adoc
taxonomy.adoc
tire.adoc
)
@@ -227,6 +228,17 @@ set(
images/renderer_gallery_rthide.png
images/renderer_gallery_rtdepth.png
images/renderer_gallery_rtxray.png
images/solid_rep_csg.png
images/solid_rep_brep_boolean.png
images/solid_rep_brep_evaluated.png
images/solid_rep_polygonal_boolean.png
images/solid_rep_nmg.png
images/solid_rep_bot.png
images/solid_rep_revolve.png
images/solid_rep_voxel_cells.png
images/solid_rep_volume.png
images/solid_rep_point_cloud.png
images/solid_rep_wireframe.png
images/adrt_castle_path.png
images/adrt_castle_rt.png
images/adrt_castle_rt_ao.png
@@ -409,6 +421,54 @@ if(
set_target_properties(renderer-gallery-snapshot PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
# Solid geometry comparison -- opt-in regeneration of committed snapshots.
#
# The article and solid_rep_*.png images are normal committed documentation.
# This target rebuilds the common pawn, derives each representation with the
# built tools, and refreshes the images for review.
option(
BRLCAD_GENERATE_SOLID_REPRESENTATION_COMPARISON
"Regenerate the solid geometry representation comparison snapshots"
OFF
)
if(
BRLCAD_GENERATE_SOLID_REPRESENTATION_COMPARISON
AND TARGET mged
AND TARGET rt
AND TARGET g-voxel
AND TARGET asc2pix
AND TARGET fbclear
AND TARGET plot3-fb
AND TARGET fb-png
)
add_custom_target(
solid-representation-comparison-snapshot
COMMAND
${CMAKE_COMMAND}
-DMGED=$<TARGET_FILE:mged>
-DRT=$<TARGET_FILE:rt>
-DGVOXEL=$<TARGET_FILE:g-voxel>
-DASC2PIX=$<TARGET_FILE:asc2pix>
-DFBCLEAR=$<TARGET_FILE:fbclear>
-DPLOT3FB=$<TARGET_FILE:plot3-fb>
-DFBPNG=$<TARGET_FILE:fb-png>
-DMODEL_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/solid_geometry_comparison/create_pawn.tcl
-DIMGDIR=${CMAKE_CURRENT_SOURCE_DIR}/images
-DWORKDIR=${CMAKE_CURRENT_BINARY_DIR}/solid_representation_comparison_work
-DSIZE=512
-P ${CMAKE_CURRENT_SOURCE_DIR}/solid_geometry_comparison/generate_comparison.cmake
DEPENDS mged rt g-voxel asc2pix fbclear plot3-fb fb-png
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Regenerating solid geometry representation comparison snapshots"
VERBATIM
USES_TERMINAL
)
set_target_properties(
solid-representation-comparison-snapshot
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD 1
)
endif()
# ADRT path-tracing gallery -- opt-in regeneration of the committed snapshot.
#
# Like the galleries above, the adrt_pathtrace.adoc article is hand-written and
@@ -464,6 +524,8 @@ cmakefiles(
gallery/generate_gallery.cmake
renderer_gallery/renderer_gallery_manifest.cmake
renderer_gallery/generate_renderer_gallery.cmake
solid_geometry_comparison/create_pawn.tcl
solid_geometry_comparison/generate_comparison.cmake
adrt_gallery/generate_adrt_gallery.cmake
gallery/inputs/pipe.stdin
gallery/inputs/wire.stdin
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

@@ -0,0 +1,294 @@
= Solid Geometry Representation Comparisons
:doctype: article
:toc:
:toclevels: 2
The same shape can be stored in very different ways. This comparison uses a
purpose-built chess pawn to show which information BRL-CAD retains, evaluates,
or samples when moving among its principal geometry representations. It is a
guide to semantics and conversion choices, not a claim that one representation
is universally best.
== Common model and method
The reference pawn is a region containing seven overlapping ellipsoids. Their
union is non-trivial enough to exercise curved surfaces, overlapping interiors,
and several component boundaries while remaining reliable across the
conversions shown here. The sketch-and-revolve version independently follows
the same nominal profile; it is not an exact conversion of the seven-piece CSG
model.
Every shaded image uses the same orthographic view, 512x512 image size,
material, and single-threaded renderer. The wireframe uses the same view and
image size. The faceting and sampling settings are intentionally coarse enough
to remain visible. A similar silhouette therefore does not mean that two
database objects have the same structure or numerical boundary.
NOTE: This article and its images are a committed snapshot. Developers can
regenerate the figures from built BRL-CAD tools by configuring with
`-DBRLCAD_GENERATE_SOLID_REPRESENTATION_COMPARISON=ON` and building the
`solid-representation-comparison-snapshot` target.
== At a glance
[cols="2,2,1,2,4",options="header"]
|===
|Form
|BRL-CAD storage
|Solid?
|Boolean state
|Editing and conversion consequences
|Analytic CSG
|Seven `ell` leaves in a combination
|Yes
|Retained
|Compact primitive parameters and tree edits preserve construction intent.
|B-rep tree
|Closed `brep` leaves in a combination
|Yes
|Retained
|NURBS surfaces and trims replace analytic leaves, but the assembly tree remains.
|Evaluated B-rep
|One closed `brep`
|Yes
|Evaluated
|Direct face, edge, trim, and control-point editing is possible; original
features and hierarchy are gone.
|Polygonal tree
|Solid-mode `bot` leaves in a combination
|Yes
|Retained
|Leaf tessellation is approximate, but components and their union tree remain
independently replaceable.
|Evaluated NMG
|One `nmg`
|Yes
|Evaluated
|Stores explicit, topology-rich polygonal boundaries and can record
non-manifold relationships.
|Evaluated triangle mesh
|One solid-mode `bot`
|Yes
|Evaluated
|Portable triangle connectivity, but curved geometry and construction history
have been sampled away.
|Sketch and revolve
|One `sketch` referenced by one `revolve`
|Yes
|Not needed here
|The profile, axis, and sweep angle are compact, meaningful parameters.
|Voxel cells
|A region containing many `arb8` boxes
|Yes (sampled)
|Cell union retained
|Individual cells are ordinary CSG geometry; size grows rapidly as cells
shrink.
|Sampled volume
|A file-backed `vol` scalar grid
|Yes (thresholded)
|None
|Compact regular-grid occupancy; resolution and threshold determine the boundary.
|Point cloud
|One `pnts` object with positions and normals
|No pawn interior
|None
|Useful as measured or sampled evidence, but it has no inherent connected
boundary or inside/outside rule.
|Wireframe
|Derived line display of the CSG object
|No
|Uses source tree
|A view-dependent drawing aid, not an independent solid representation.
|===
"Solid" in this table means that the stored object supplies usable
inside/outside semantics for the pawn. An arbitrary B-rep or mesh is not
automatically a solid: its boundary must be closed and consistently oriented.
The point size used to render `pnts` makes each sample a small sphere, but those
spheres do not reconstruct the pawn interior. A wireframe likewise does not
become a solid merely because its curves appear closed in one view.
== Implicit and parametric forms
[cols="1,1"]
[%noheader]
|===
|image:images/solid_rep_csg.png[Analytic CSG pawn,512]
|image:images/solid_rep_revolve.png[Sketch and revolve pawn,512]
|*Analytic CSG.* Native primitives retain their equations and dimensions; the
region evaluates their union during ray tracing.
|*Sketch plus revolve.* A closed line-segment profile is swept through 360
degrees. The stepped profile is intentional and remains editable in two
dimensions.
|===
Analytic CSG is an implicit representation: intersections with ideal primitive
surfaces are computed as needed rather than read from a stored boundary mesh.
It is usually the most compact form here and makes edits such as changing the
head radius or stem proportions direct. Its Boolean hierarchy also records why the
shape was assembled.
`sketch` plus `revolve` is another procedural, parameterized description. It
works especially well for an axisymmetric part, although a sketch with only
line segments produces conical and cylindrical bands rather than the pawn's
original ellipsoidal surfaces. BRL-CAD supports primitive parameters, placement
matrices, sketches, and sweeps; that should not be confused with a general
constraint solver or an associative feature-history system.
== Spline boundary representations
[cols="1,1"]
[%noheader]
|===
|image:images/solid_rep_brep_boolean.png[B-rep leaves with Boolean tree,512]
|image:images/solid_rep_brep_evaluated.png[Single evaluated B-rep,512]
|*B-rep with Booleans.* Each analytic leaf becomes a closed B-rep whose
boundaries are trimmed NURBS surfaces. The original union tree is retained.
|*B-rep without Booleans.* Boolean evaluation produces one explicit boundary;
operand identities and the construction tree no longer exist as editable
features.
|===
A B-rep stores faces, trims, edges, vertices, and their topology explicitly.
Rational NURBS can represent the source quadrics exactly as surfaces, but that
does not make every CSG-to-B-rep conversion exact: intersections, trimming,
topology classification, and repair use numerical tolerances. Keeping a tree
of B-rep leaves isolates conversion to each primitive and retains high-level
editing. Evaluating the tree yields a more self-contained boundary for
exchange and face-level work, at the cost of that history. This snapshot's
evaluated result passes both `brep ... valid` and `brep ... solid`; conversion
results should be checked rather than assumed to be closed.
== Polygonal boundaries
[cols="1,1,1"]
[%noheader]
|===
|image:images/solid_rep_polygonal_boolean.png[BoT leaves with Boolean tree,512]
|image:images/solid_rep_nmg.png[Evaluated NMG pawn,512]
|image:images/solid_rep_bot.png[Evaluated BoT pawn,512]
|*Polygonal with Booleans.* Each leaf is independently tessellated to a BoT,
then placed in the original union tree.
|*Evaluated NMG.* One non-manifold geometry object records the evaluated
polygonal boundary and its richer topology.
|*Evaluated triangle mesh.* One solid, counter-clockwise BoT stores 982
vertices and 1,960 triangular faces in this snapshot.
|===
All three images expose the same coarse tessellation. Their similar appearance
hides an important distinction: the first still has seven editable operands,
whereas the NMG and single BoT are evaluated results. NMG can represent
polygonal faces and non-manifold adjacency; BoT faces are triangles only.
Neither recreates analytic curvature when converted back. Finer tessellation
reduces visible error but increases storage and processing cost.
== Sampled forms
[cols="1,1,1"]
[%noheader]
|===
|image:images/solid_rep_voxel_cells.png[Pawn represented by CSG voxel cells,512]
|image:images/solid_rep_volume.png[Pawn represented by a VOL grid,512]
|image:images/solid_rep_point_cloud.png[Pawn represented by surface points,512]
|*Explicit voxel cells.* `voxelize` tests 3 mm cells and creates a CSG union of
699 boxes for cells exceeding the occupancy threshold.
|*Sampled volume.* `g-voxel` samples a 2 mm grid; a file-backed `vol` interprets
the resulting 17x17x30 byte array through an occupancy window.
|*Point cloud.* `pnts gen` samples the analytic surface on a 2 mm grid. Small
rendered spheres make the otherwise zero-area sample locations visible.
|===
The two voxel images are deliberately separate. The `voxelize` command emits
ordinary box primitives and a combination, while `vol` reads scalar samples on
a regular grid and derives a threshold boundary at ray-trace time. Both are
volumetric approximations whose accuracy, memory, and feature size depend on
cell spacing. A point cloud samples only the surface and has no connectivity;
surface reconstruction requires additional assumptions and may invent or omit
geometry.
== Wireframe display
image::images/solid_rep_wireframe.png[Wireframe display of the analytic CSG pawn,512]
This wireframe is a view-plane plot of MGED's unevaluated display of the
analytic CSG pawn; hidden lines are not removed. Wireframes are useful for
selection, orientation, and seeing component curves, but they are ambiguous:
curves alone do not say which side is material, which loops bound faces, or
which surfaces enclose volume. Keep the solid that generated the display
rather than treating the drawing as an equivalent conversion target.
== Conversion and editing consequences
A conversion command is not an equivalence guarantee. Before replacing source
data, check at least these effects:
* *Tolerance and resolution:* B-rep Boolean evaluation uses intersection and
topology tolerances; tessellation uses chord and angular tolerances; voxels
and points use sample spacing and thresholds. Each can erase a feature
smaller than its controlling tolerance.
* *Construction intent:* evaluating a Boolean tree discards operand identity,
primitive parameters, and ordering. Converting leaves while retaining the
tree keeps more of that intent.
* *Topology:* a visually plausible B-rep or mesh may contain gaps, duplicate
faces, self-intersections, or inconsistent orientation. Validate closure
and ray behavior, not only a shaded image.
* *Attributes:* collapsing a hierarchy may change where region identifiers,
materials, colors, and other attributes reside. Verify them separately
from geometry.
* *Round trips:* CSG to mesh to B-rep does not recover the original equations;
points to a surface requires reconstruction; resampling a volume compounds
discretization. Retain the highest-level trustworthy source.
For parameter changes and repeated design work, prefer analytic primitives,
sketch sweeps, and preserved combination trees. Prefer a validated B-rep when
trimmed-surface topology or exchange requires it. Use NMG for topology-rich
polygonal processing and BoT for triangle-oriented interchange or algorithms.
Use voxels for occupancy operations and point clouds for measured or sampled
data, not as silent substitutes for a watertight solid.
BRL-CAD also supports specialized forms such as DSP height fields, EBM
extrusions, ARS surfaces, metaballs, and legacy `poly` and `bspline` objects.
They are not shown as interchangeable pawn encodings: DSP and EBM are
domain-specific, metaballs change the modeling method, and BoT and B-rep are the
preferred general successors to the legacy polygon and spline forms.
== Reproducing the conversions
The snapshot generator creates the database and runs these principal MGED
operations (names shortened here only where noted):
[source]
----
brep comparison.csg.r brep --no-evaluation
brep comparison.csg.r brep comparison.brep_evaluated.s
facetize -n comparison.csg.r comparison.nmg.s
facetize --methods NMG comparison.csg.r comparison.bot.s
facetize --methods NMG base.s base.bot # repeated for all seven leaves
voxelize -s "3 3 3" -d 4 -t 0.45 comparison.voxel_cells.r comparison.csg.r
pnts gen -t 2 --surface --grid comparison.csg.r comparison.samples
pnts write comparison.samples comparison.xyz
pnts read -f xyzijk --size 0.45 comparison.xyz comparison.point_cloud.s
----
The generator also runs `g-voxel` and expands its sparse occupied-cell report
into the x-fastest byte array used by `vol`. It then renders every shaded
object with `rt -B -M -P1` and the saved view; MGED's `plot -2d`, `plot3-fb`,
and `fb-png` produce the wireframe image. See
`solid_geometry_comparison/generate_comparison.cmake` and
`solid_geometry_comparison/create_pawn.tcl` for the complete reproducible
inputs.
@@ -0,0 +1,24 @@
# Create the shared pawn used by the solid-representation comparison.
title {Solid geometry representation comparison pawn}
units mm
# Deliberate overlaps keep the pawn connected while exercising curved-surface
# intersections during Boolean evaluation.
db put base.s ell V {0 0 4.5} A {17 0 0} B {0 17 0} C {0 0 4.5}
db put base_bead.s ell V {0 0 9.5} A {14 0 0} B {0 14 0} C {0 0 5}
db put body.s ell V {0 0 18} A {12 0 0} B {0 12 0} C {0 0 9}
db put stem.s ell V {0 0 29} A {7 0 0} B {0 7 0} C {0 0 13}
db put collar.s ell V {0 0 39} A {9 0 0} B {0 9 0} C {0 0 3.5}
db put neck.s ell V {0 0 43} A {6.5 0 0} B {0 6.5 0} C {0 0 5}
db put head.s ell V {0 0 50} A {9 0 0} B {0 9 0} C {0 0 9}
db put comparison.csg.r comb region yes id 1000 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {u {u {u {l base.s} {l base_bead.s}} {u {l body.s} {l stem.s}}} {u {u {l collar.s} {l neck.s}} {l head.s}}}
# The revolve uses a deliberately independent outline. Its single closed
# sketch approximates the same pawn silhouette without copying the CSG tree.
db put comparison.profile sketch V {0 0 0} A {1 0 0} B {0 0 1} VL {{0 0} {17 0} {17 2} {16.3 4} {15 6} {14 8} {15.4 7} {16 9} {15.4 11} {13.5 12.5} {11.2 14} {12 18} {11 21} {9.5 23} {8 26} {7.5 32} {6.5 36} {7.5 37} {9 39} {7.5 41} {6.5 42} {6.5 43} {7.5 44} {8.5 46} {9 50} {8.5 54} {6.7 56.5} {0 59}} SL {{line S 0 E 1} {line S 1 E 2} {line S 2 E 3} {line S 3 E 4} {line S 4 E 5} {line S 5 E 6} {line S 6 E 7} {line S 7 E 8} {line S 8 E 9} {line S 9 E 10} {line S 10 E 11} {line S 11 E 12} {line S 12 E 13} {line S 13 E 14} {line S 14 E 15} {line S 15 E 16} {line S 16 E 17} {line S 17 E 18} {line S 18 E 19} {line S 19 E 20} {line S 20 E 21} {line S 21 E 22} {line S 22 E 23} {line S 23 E 24} {line S 24 E 25} {line S 25 E 26} {line S 26 E 27} {line S 27 E 0}}
db put comparison.revolve.s revolve V {0 0 0} axis {0 0 1} R {1 0 0} ang 6.283185307179586 sk_name comparison.profile
db put comparison.revolve.r comb region yes id 1001 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.revolve.s}
q
@@ -0,0 +1,372 @@
# Regenerate the solid geometry representation comparison snapshots.
#
# The article is hand-written. This script builds its common pawn model,
# derives each representation using BRL-CAD tools, and replaces the committed
# images. All databases and sidecar data remain in WORKDIR.
cmake_minimum_required(VERSION 3.20)
foreach(v MGED RT GVOXEL ASC2PIX FBCLEAR PLOT3FB FBPNG
MODEL_SCRIPT IMGDIR WORKDIR)
if(NOT DEFINED ${v})
message(FATAL_ERROR "generate_comparison: -D${v}=... is required")
endif()
endforeach()
if(NOT DEFINED SIZE)
set(SIZE 512)
endif()
set(RENDER_TIMEOUT 1800)
set(PAWN_DB "${WORKDIR}/solid_representation_pawn.g")
set(VIEW_SCRIPT "${WORKDIR}/pawn.view")
set(MGED_SCRIPT "${WORKDIR}/assemble_variants.tcl")
set(VOXELIZE_SCRIPT "${WORKDIR}/voxelize.tcl")
set(VOXEL_LIST "${WORKDIR}/voxels1.txt")
set(VOLUME_ASCII "${WORKDIR}/comparison_volume.asc")
set(VOLUME_FILE "${WORKDIR}/comparison.vol")
file(REMOVE_RECURSE "${WORKDIR}")
file(MAKE_DIRECTORY "${WORKDIR}")
file(MAKE_DIRECTORY "${IMGDIR}")
function(run_process label)
execute_process(
COMMAND ${ARGN}
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
RESULT_VARIABLE process_result
OUTPUT_VARIABLE process_stdout
ERROR_VARIABLE process_stderr
)
if(NOT process_result EQUAL 0)
message(
FATAL_ERROR
"${label} failed (exit ${process_result})\n${process_stdout}${process_stderr}"
)
endif()
message(STATUS "${label}")
endfunction()
function(require_brep_property object property expected_output)
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}" brep "${object}" "${property}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
RESULT_VARIABLE property_result
OUTPUT_VARIABLE property_stdout
ERROR_VARIABLE property_stderr
)
set(property_output "${property_stdout}${property_stderr}")
if(NOT property_result EQUAL 0 OR NOT property_output MATCHES "${expected_output}")
message(
FATAL_ERROR
"B-rep ${object} is not ${property}\n${property_output}"
)
endif()
message(STATUS "Validated B-rep ${object}: ${property}")
endfunction()
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${MODEL_SCRIPT}"
RESULT_VARIABLE create_result
OUTPUT_VARIABLE create_stdout
ERROR_VARIABLE create_stderr
)
if(NOT create_result EQUAL 0 OR NOT EXISTS "${PAWN_DB}")
message(
FATAL_ERROR
"Creating the comparison pawn failed (exit ${create_result})\n${create_stdout}${create_stderr}"
)
endif()
# Preserve one Boolean hierarchy as B-rep leaves, and evaluate another into a
# single B-rep. The default suffix is intentional: supplying an explicit
# suffix changes parsing in older MGED versions.
set(PAWN_LEAVES base base_bead body stem collar neck head)
run_process(
"Created Boolean tree of B-rep leaves"
"${MGED}" --no-rc -c "${PAWN_DB}" brep comparison.csg.r brep --no-evaluation
)
foreach(leaf IN LISTS PAWN_LEAVES)
require_brep_property("${leaf}.s.brep" solid "brep is solid")
endforeach()
run_process(
"Created evaluated B-rep"
"${MGED}" --no-rc -c "${PAWN_DB}" brep comparison.csg.r brep comparison.brep_evaluated.s
)
require_brep_property(comparison.brep_evaluated.s valid "brep is valid")
require_brep_property(comparison.brep_evaluated.s solid "brep is solid")
# Generate both the topology-rich NMG form and the triangle-only BoT form.
run_process(
"Created evaluated NMG"
"${MGED}" --no-rc -c "${PAWN_DB}" facetize -n comparison.csg.r comparison.nmg.s
)
run_process(
"Created evaluated BoT"
"${MGED}" --no-rc -c "${PAWN_DB}" facetize --methods NMG comparison.csg.r comparison.bot.s
)
# Tessellating leaves independently preserves the editable Boolean hierarchy.
foreach(leaf IN LISTS PAWN_LEAVES)
run_process(
"Tessellated ${leaf}.s"
"${MGED}" --no-rc -c "${PAWN_DB}" facetize --methods NMG "${leaf}.s" "${leaf}.bot"
)
endforeach()
file(WRITE "${VOXELIZE_SCRIPT}" "voxelize -s {3 3 3} -d 4 -t 0.45 comparison.voxel_cells.r comparison.csg.r\nq\n")
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${VOXELIZE_SCRIPT}"
RESULT_VARIABLE voxelize_result
OUTPUT_VARIABLE voxelize_stdout
ERROR_VARIABLE voxelize_stderr
)
if(NOT voxelize_result EQUAL 0)
message(
FATAL_ERROR
"Creating explicit voxel cells failed (exit ${voxelize_result})\n${voxelize_stdout}${voxelize_stderr}"
)
endif()
message(STATUS "Created explicit voxel cells")
# PNTS stores point size at import time. The generator first writes all
# sampled fields (xyzijk), then reads them back with a visible point diameter.
run_process(
"Sampled pawn surface"
"${MGED}" --no-rc -c "${PAWN_DB}" pnts gen -t 2 --surface --grid
comparison.csg.r comparison.samples
)
run_process(
"Exported pawn point samples"
"${MGED}" --no-rc -c "${PAWN_DB}" pnts write comparison.samples comparison.xyz
)
run_process(
"Created visible point cloud"
"${MGED}" --no-rc -c "${PAWN_DB}" pnts read -f xyzijk --size 0.45
comparison.xyz comparison.point_cloud.s
)
# g-voxel reports occupied cells. Expand that sparse report into the regular,
# x-fastest byte array required by VOL. The pawn bounds are [-17,17] in X/Y
# and [0,60] in Z; 2 mm cells therefore form a 17 x 17 x 30 grid.
file(REMOVE "${VOXEL_LIST}")
run_process(
"Sampled VOL grid"
"${GVOXEL}" -s "2 2 2" -d 4 -t 0.5 "${PAWN_DB}" comparison.csg.r
)
if(NOT EXISTS "${VOXEL_LIST}")
message(FATAL_ERROR "g-voxel did not create ${VOXEL_LIST}")
endif()
file(STRINGS "${VOXEL_LIST}" voxel_lines)
list(LENGTH voxel_lines occupied_voxels)
if(occupied_voxels EQUAL 0)
message(FATAL_ERROR "g-voxel reported no occupied cells")
endif()
foreach(line IN LISTS voxel_lines)
string(
REGEX MATCH
"^\\((-?[0-9]+)\\.0+, (-?[0-9]+)\\.0+, (-?[0-9]+)\\.0+\\)[ \t]+[^ \t]+[ \t]+[0-9.]+$"
parsed "${line}"
)
if(NOT parsed)
message(FATAL_ERROR "Cannot parse g-voxel output: ${line}")
endif()
set(x_coord "${CMAKE_MATCH_1}")
set(y_coord "${CMAKE_MATCH_2}")
set(z_coord "${CMAKE_MATCH_3}")
math(EXPR x_index "(${x_coord} + 16) / 2")
math(EXPR y_index "(${y_coord} + 16) / 2")
math(EXPR z_index "(${z_coord} - 1) / 2")
if(x_index LESS 0 OR x_index GREATER 16 OR
y_index LESS 0 OR y_index GREATER 16 OR
z_index LESS 0 OR z_index GREATER 29)
message(FATAL_ERROR "g-voxel coordinate outside the expected pawn grid: ${line}")
endif()
set("voxel_${x_index}_${y_index}_${z_index}" 255)
endforeach()
set(volume_text "")
foreach(z_index RANGE 0 29)
foreach(y_index RANGE 0 16)
foreach(x_index RANGE 0 16)
if(DEFINED voxel_${x_index}_${y_index}_${z_index})
set(sample ff)
else()
set(sample 00)
endif()
string(APPEND volume_text "${sample}\n")
endforeach()
endforeach()
endforeach()
file(WRITE "${VOLUME_ASCII}" "${volume_text}")
execute_process(
COMMAND "${ASC2PIX}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${VOLUME_ASCII}"
OUTPUT_FILE "${VOLUME_FILE}"
RESULT_VARIABLE asc2pix_result
ERROR_VARIABLE asc2pix_error
)
if(NOT asc2pix_result EQUAL 0)
message(FATAL_ERROR "asc2pix failed (exit ${asc2pix_result})\n${asc2pix_error}")
endif()
file(SIZE "${VOLUME_FILE}" volume_size)
if(NOT volume_size EQUAL 8670)
message(FATAL_ERROR "VOL sidecar is ${volume_size} bytes; expected 8670")
endif()
message(STATUS "Encoded ${occupied_voxels} occupied VOL cells")
# Wrap single primitives in regions so every shaded comparison uses the same
# material. The polygonal tree repeats the original CSG union hierarchy with
# BoT leaves. VOL references a sidecar basename resolved beside the database.
file(WRITE "${MGED_SCRIPT}" [=[
db put comparison.brep_evaluated.r comb region yes id 1010 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.brep_evaluated.s}
db put comparison.nmg.r comb region yes id 1020 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.nmg.s}
db put comparison.bot.r comb region yes id 1030 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.bot.s}
db put comparison.polygonal_boolean.r comb region yes id 1040 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {u {u {u {l base.bot} {l base_bead.bot}} {u {l body.bot} {l stem.bot}}} {u {u {l collar.bot} {l neck.bot}} {l head.bot}}}
attr set comparison.voxel_cells.r region R region_id 1050 material_id 1 los 100 shader plastic color 235/190/90
db put comparison.point_cloud.r comb region yes id 1060 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.point_cloud.s}
db put comparison.volume.s vol file {comparison.vol} name {comparison.vol} src f w 17 n 17 d 30 lo 1 hi 255 size {2 2 2} mat {1 0 0 -17 0 1 0 -17 0 0 1 0 0 0 0 1}
db put comparison.volume.r comb region yes id 1070 los 100 GIFTmater 1 rgb {235 190 90} shader {plastic} tree {l comparison.volume.s}
q
]=])
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${MGED_SCRIPT}"
RESULT_VARIABLE assemble_result
OUTPUT_VARIABLE assemble_stdout
ERROR_VARIABLE assemble_stderr
)
if(NOT assemble_result EQUAL 0)
message(
FATAL_ERROR
"Assembling comparison regions failed (exit ${assemble_result})\n${assemble_stdout}${assemble_stderr}"
)
endif()
file(WRITE "${VIEW_SCRIPT}" "viewsize 8.000000000000000e+01;\n")
file(APPEND "${VIEW_SCRIPT}" "orientation 2.480970000000000e-01 4.765910000000000e-01 7.480970000000000e-01 3.894350000000000e-01;\n")
file(APPEND "${VIEW_SCRIPT}" "eye_pt 4.199070000000000e+01 2.940060000000000e+01 5.329140000000000e+01;\n")
file(APPEND "${VIEW_SCRIPT}" "start 0; clean;\n")
file(APPEND "${VIEW_SCRIPT}" "end;\n")
function(copy_snapshot local_image)
if(NOT EXISTS "${local_image}")
message(FATAL_ERROR "Expected image was not created: ${local_image}")
endif()
file(SIZE "${local_image}" image_size)
if(image_size EQUAL 0)
message(FATAL_ERROR "Rendered image is empty: ${local_image}")
endif()
file(COPY "${local_image}" DESTINATION "${IMGDIR}")
get_filename_component(image_name "${local_image}" NAME)
file(
CHMOD "${IMGDIR}/${image_name}"
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
)
endfunction()
function(render_snapshot name object)
set(filename "solid_rep_${name}.png")
set(local_image "${WORKDIR}/${filename}")
file(REMOVE "${local_image}")
execute_process(
COMMAND "${RT}" -B -M -W "-s${SIZE}" -P1 -o "${filename}" "${PAWN_DB}" "${object}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${VIEW_SCRIPT}"
RESULT_VARIABLE render_result
OUTPUT_VARIABLE render_stdout
ERROR_VARIABLE render_stderr
)
if(NOT render_result EQUAL 0)
message(
FATAL_ERROR
"Rendering ${name} failed (exit ${render_result})\n${render_stdout}${render_stderr}"
)
endif()
copy_snapshot("${local_image}")
message(STATUS "Rendered ${filename}")
endfunction()
render_snapshot(csg comparison.csg.r)
render_snapshot(brep_boolean comparison.csg.r.brep)
render_snapshot(brep_evaluated comparison.brep_evaluated.r)
render_snapshot(polygonal_boolean comparison.polygonal_boolean.r)
render_snapshot(nmg comparison.nmg.r)
render_snapshot(bot comparison.bot.r)
render_snapshot(revolve comparison.revolve.r)
render_snapshot(voxel_cells comparison.voxel_cells.r)
render_snapshot(volume comparison.volume.r)
render_snapshot(point_cloud comparison.point_cloud.r)
# MGED exports its current wireframe display as view-plane plot3. Rasterize
# that plot through a disk framebuffer so regeneration needs no display.
set(WIREFRAME_PLOT "${WORKDIR}/solid_rep_wireframe.plot3")
set(WIREFRAME_PNG "${WORKDIR}/solid_rep_wireframe.png")
set(WIREFRAME_FB_NAME "solid_rep_wireframe.fb")
set(WIREFRAME_SCRIPT "${WORKDIR}/wireframe.tcl")
file(REMOVE "${WIREFRAME_PLOT}" "${WORKDIR}/${WIREFRAME_FB_NAME}" "${WIREFRAME_PNG}")
file(WRITE "${WIREFRAME_SCRIPT}" [=[
draw comparison.csg.r
size 80
orientation 0.248097 0.476591 0.748097 0.389435
eye_pt 41.9907 29.4006 53.2914
plot -2d solid_rep_wireframe.plot3
q
]=])
execute_process(
COMMAND "${MGED}" --no-rc -c "${PAWN_DB}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${WIREFRAME_SCRIPT}"
RESULT_VARIABLE wireframe_result
OUTPUT_VARIABLE wireframe_stdout
ERROR_VARIABLE wireframe_stderr
)
if(NOT wireframe_result EQUAL 0 OR NOT EXISTS "${WIREFRAME_PLOT}")
message(
FATAL_ERROR
"MGED wireframe export failed (exit ${wireframe_result})\n${wireframe_stdout}${wireframe_stderr}"
)
endif()
message(STATUS "Exported wireframe plot")
run_process(
"Cleared wireframe framebuffer"
"${FBCLEAR}" -c -F "${WIREFRAME_FB_NAME}" -w "${SIZE}" -n "${SIZE}" 24 24 24
)
execute_process(
COMMAND "${PLOT3FB}" -F "${WIREFRAME_FB_NAME}" -o -w "${SIZE}" -n "${SIZE}"
WORKING_DIRECTORY "${WORKDIR}"
TIMEOUT ${RENDER_TIMEOUT}
INPUT_FILE "${WIREFRAME_PLOT}"
RESULT_VARIABLE plot_result
OUTPUT_VARIABLE plot_stdout
ERROR_VARIABLE plot_stderr
)
if(NOT plot_result EQUAL 0)
message(FATAL_ERROR "plot3-fb failed (exit ${plot_result})\n${plot_stdout}${plot_stderr}")
endif()
run_process(
"Rasterized wireframe snapshot"
"${FBPNG}" -F "${WIREFRAME_FB_NAME}" -w "${SIZE}" -n "${SIZE}"
"solid_rep_wireframe.png"
)
copy_snapshot("${WIREFRAME_PNG}")
message(STATUS "Solid representation comparison: 11 snapshots regenerated")
+1
View File
@@ -22,4 +22,5 @@ Task-focused guides and how-to articles.
* link:articles/procedural_geometry.html[BRL-CAD Procedural Geometry Gallery]
* link:articles/projection_shader.html[Using the Projection Shader]
* link:articles/renderer_showcase.html[BRL-CAD Renderer Showcase]
* link:articles/solid_geometry_comparison.html[Solid Geometry Representation Comparisons]
* link:articles/tire.html[Vehicle Tire and Wheel Creation in BRL-CAD]
+1 -1
View File
@@ -55,7 +55,7 @@ __BEGIN_DECLS
* version 5 UUID suitable for repeatable identifier hashing.
*/
BU_EXPORT int
bu_uuid_create(uint8_t uuid[STATIC_ARRAY(16)], size_t nbytes, const uint8_t *bytes, const uint8_t namespace_uuid[STATIC_ARRAY(16)]);
bu_uuid_create(uint8_t uuid[STATIC_ARRAY(16)], size_t nbytes, const uint8_t *bytes, const uint8_t *namespace_uuid);
/**
* This is a convenience UUID comparison routine compatible with
+20 -15
View File
@@ -1108,9 +1108,9 @@ function(brlcad_install_strclear_replace stamp_dir strclear source install_file
endif()
endfunction()
function(brlcad_install_binary_postprocess stamp_dir strclear source install_file install_type mode rpath_tool install_rpath build_lib_path rel_rpath use_selective_rpath verbose)
function(brlcad_install_binary_postprocess stamp_dir strclear source install_file install_type mode rpath_tool install_rpath build_lib_path rel_rpath verbose)
set(_brlcad_physical_file "$ENV{DESTDIR}${install_file}")
set(_brlcad_signature "binary-postprocess|${install_type}|${mode}|${rpath_tool}|${install_rpath}|${build_lib_path}|${rel_rpath}|${use_selective_rpath}|${strclear}")
set(_brlcad_signature "binary-postprocess|${install_type}|${mode}|${rpath_tool}|${install_rpath}|${build_lib_path}|${rel_rpath}|${strclear}")
_brlcad_postprocess_needed(_brlcad_needed "${stamp_dir}" "${source}" "${_brlcad_physical_file}" "${_brlcad_signature}")
if(NOT _brlcad_needed)
return()
@@ -1121,32 +1121,41 @@ function(brlcad_install_binary_postprocess stamp_dir strclear source install_fil
message(FATAL_ERROR "Post-install copy did not create ${_brlcad_physical_file}")
endif()
set(_brlcad_result 0)
set(_brlcad_rpath_output)
set(_brlcad_rpath_error)
if("${mode}" STREQUAL "RPATH_TOOL")
set(_brlcad_selective_rpath_args)
if(use_selective_rpath)
set(_brlcad_selective_rpath_args --set-rpath-if-needed --set-rpath-if-needed-prepend --stale-rpath-prefix "${build_lib_path}")
endif()
# Install the exact project-defined search order. A selective update can
# leave an existing relative-only RPATH without the canonical install path.
execute_process(
COMMAND "${rpath_tool}" --set-rpath "${install_rpath}" ${_brlcad_selective_rpath_args} "${_brlcad_physical_file}"
COMMAND "${rpath_tool}" --set-rpath "${install_rpath}" "${_brlcad_physical_file}"
RESULT_VARIABLE _brlcad_result
OUTPUT_VARIABLE _brlcad_rpath_output
ERROR_VARIABLE _brlcad_rpath_error
)
elseif("${mode}" STREQUAL "APPLE")
execute_process(
COMMAND install_name_tool -delete_rpath "${build_lib_path}" "${_brlcad_physical_file}"
RESULT_VARIABLE _brlcad_result
OUTPUT_VARIABLE _brlcad_output
ERROR_VARIABLE _brlcad_error
OUTPUT_VARIABLE _brlcad_rpath_output
ERROR_VARIABLE _brlcad_rpath_error
)
if(_brlcad_result EQUAL 0)
execute_process(
COMMAND install_name_tool -add_rpath "${rel_rpath}" "${_brlcad_physical_file}"
RESULT_VARIABLE _brlcad_result
OUTPUT_VARIABLE _brlcad_rpath_output
ERROR_VARIABLE _brlcad_rpath_error
)
endif()
endif()
if(NOT _brlcad_result EQUAL 0)
message(WARNING "Post-install RPATH update failed for ${_brlcad_physical_file}")
message(
WARNING
"Post-install RPATH update failed for ${_brlcad_physical_file}\n"
"stdout: ${_brlcad_rpath_output}\n"
"stderr: ${_brlcad_rpath_error}"
)
return()
endif()
@@ -1840,13 +1849,9 @@ endfunction()
elseif(APPLE)
set(_brlcad_install_postprocess_mode "APPLE")
endif(P_RPATH_EXECUTABLE)
set(_brlcad_install_use_selective_rpath FALSE)
if(P_RPATH_SUPPORTS_SET_IF_NEEDED_PREPEND)
set(_brlcad_install_use_selective_rpath TRUE)
endif(P_RPATH_SUPPORTS_SET_IF_NEEDED_PREPEND)
install(
CODE
"include(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_SCRIPT}\")\nbrlcad_install_binary_postprocess(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_STAMP_DIR}\" \"${STRCLEAR_EXECUTABLE}\" \"${CMAKE_BINARY_DIR}/${tf}\" \"\${CMAKE_INSTALL_PREFIX}/${tf}\" \"PROGRAM\" \"${_brlcad_install_postprocess_mode}\" \"${_brlcad_install_postprocess_tool}\" \"${_brlcad_install_postprocess_rpath}\" \"${CMAKE_BINARY_DIR}/${LIB_DIR}\" \"${REL_RPATH}\" \"${_brlcad_install_use_selective_rpath}\" \"${BRLCAD_VERBOSE}\")"
"include(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_SCRIPT}\")\nbrlcad_install_binary_postprocess(\"${BRLCAD_EXT_INSTALL_POSTPROCESS_STAMP_DIR}\" \"${STRCLEAR_EXECUTABLE}\" \"${CMAKE_BINARY_DIR}/${tf}\" \"\${CMAKE_INSTALL_PREFIX}/${tf}\" \"PROGRAM\" \"${_brlcad_install_postprocess_mode}\" \"${_brlcad_install_postprocess_tool}\" \"${_brlcad_install_postprocess_rpath}\" \"${CMAKE_BINARY_DIR}/${LIB_DIR}\" \"${REL_RPATH}\" \"${BRLCAD_VERBOSE}\")"
)
continue()
endif("${tf}" IN_LIST ALL_BINARY_FILES)
+7 -5
View File
@@ -471,11 +471,13 @@ if(BRLCAD_VERBOSE)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif(BRLCAD_VERBOSE)
# Build with profile-guided optimization support. this requires a two-pass
# compile, once with BRLCAD_PGO=ON on a location that did not exist beforehand
# (specified via the PGO_PATH environment variable), and again to use profiling
# metrics captured on "typical" operations and data. By default, path is
# BUILDDIR/profiling
# Build with profile-guided optimization support. This requires a two-pass
# compile, once with BRLCAD_PGO=ON and a profile location that did not exist
# beforehand (specified via the PGO_PATH environment variable), and again in a
# fresh build directory to use metrics captured on "typical" operations and
# data. By default, the path is BUILDDIR/profiling. For MSVC, set
# VCPROFILE_PATH to PGO_PATH while running the instrumented programs so their
# per-binary .pgc files are available beside the corresponding .pgd files.
option(BRLCAD_PGO "Enable profile-guided optimization (set PGO_PATH environment variable)")
mark_as_advanced(BRLCAD_PGO)
+1
View File
@@ -108,6 +108,7 @@ set(
probe_validation/README.md
probe_validation/CMakeLists.txt
probe_validation/probe_validation.cpp
scripts/pgo_benchmark.cmake
scripts/printtime.cmake
scripts/tcl_mkindex.cmake
scripts/timestamp.cmake
+435
View File
@@ -0,0 +1,435 @@
# P G O _ B E N C H M A R K . C M A K E
# BRL-CAD
#
# Copyright (c) 2026 United States Government as represented by
# the U.S. Army Research Laboratory.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. The name of the author may not be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.22)
# TRAIN runs the standard benchmark and additional geometry, then verifies
# that compiler profile counters were produced. COMPARE runs the standard
# benchmark from baseline and profile-use build trees and requires its
# equal-weight mean speedup to exceed PGO_MIN_SPEEDUP. The training model
# list accepts build-relative or absolute paths, so external model sets can be
# supplied without changing this script.
#
# Required inputs:
# TRAIN: PGO_BUILD_DIR, PGO_RESULTS_DIR, PGO_PROFILE_DIR
# COMPARE: PGO_BASELINE_BUILD_DIR, PGO_OPTIMIZED_BUILD_DIR,
# PGO_RESULTS_DIR
set(_pgo_valid_modes TRAIN COMPARE)
if(NOT DEFINED PGO_MODE)
message(FATAL_ERROR "PGO_MODE must be TRAIN or COMPARE")
endif()
string(TOUPPER "${PGO_MODE}" PGO_MODE)
if(NOT PGO_MODE IN_LIST _pgo_valid_modes)
message(FATAL_ERROR "PGO_MODE must be TRAIN or COMPARE, not '${PGO_MODE}'")
endif()
set(_pgo_default_models moss world star bldg391 m35 sphflake)
set(
_pgo_default_training_models
share/db/havoc.g
share/db/faa/Generic_Twin.g
share/db/nist/NIST_MBE_PMI_2.g
)
if(NOT DEFINED PGO_MODELS)
set(PGO_MODELS ${_pgo_default_models})
endif()
if(NOT DEFINED PGO_TRAINING_MODELS)
set(PGO_TRAINING_MODELS ${_pgo_default_training_models})
endif()
if(NOT DEFINED PGO_TIMEFRAME)
set(PGO_TIMEFRAME 8)
endif()
if(NOT DEFINED PGO_MAXTIME)
set(PGO_MAXTIME 60)
endif()
if(NOT DEFINED PGO_AVERAGE)
set(PGO_AVERAGE 3)
endif()
if(NOT DEFINED PGO_DEVIATION)
set(PGO_DEVIATION 3)
endif()
if(NOT DEFINED PGO_TRAINING_SIZE)
set(PGO_TRAINING_SIZE 512)
endif()
if(NOT DEFINED PGO_TRAINING_AZIMUTH)
set(PGO_TRAINING_AZIMUTH 35)
endif()
if(NOT DEFINED PGO_TRAINING_ELEVATION)
set(PGO_TRAINING_ELEVATION 25)
endif()
if(NOT DEFINED PGO_MIN_SPEEDUP)
set(PGO_MIN_SPEEDUP 1.0)
endif()
function(_pgo_require variable_name)
if(NOT DEFINED "${variable_name}" OR "${${variable_name}}" STREQUAL "")
message(FATAL_ERROR "${variable_name} is required for PGO_MODE=${PGO_MODE}")
endif()
endfunction()
function(_pgo_absolute_path input_path output_variable)
get_filename_component(_pgo_path "${input_path}" ABSOLUTE)
file(TO_CMAKE_PATH "${_pgo_path}" _pgo_path)
set("${output_variable}" "${_pgo_path}" PARENT_SCOPE)
endfunction()
function(_pgo_cache_value build_directory variable_name output_variable)
set(_pgo_cache_file "${build_directory}/CMakeCache.txt")
if(NOT EXISTS "${_pgo_cache_file}")
message(FATAL_ERROR "CMake cache does not exist: ${_pgo_cache_file}")
endif()
file(
STRINGS
"${_pgo_cache_file}"
_pgo_cache_entry
LIMIT_COUNT 1
REGEX "^${variable_name}(:[^=]*)?="
)
if(_pgo_cache_entry)
string(REGEX REPLACE "^[^=]*=" "" _pgo_value "${_pgo_cache_entry}")
else()
set(_pgo_value "")
endif()
set("${output_variable}" "${_pgo_value}" PARENT_SCOPE)
endfunction()
function(_pgo_validate_build build_directory expected_pgo expected_msvc_phase)
_pgo_cache_value("${build_directory}" BRLCAD_PGO _pgo_enabled)
if(expected_pgo AND NOT _pgo_enabled)
message(FATAL_ERROR "BRLCAD_PGO is not enabled in ${build_directory}")
elseif(NOT expected_pgo AND _pgo_enabled)
message(FATAL_ERROR "The baseline build unexpectedly enables BRLCAD_PGO: ${build_directory}")
endif()
_pgo_cache_value("${build_directory}" BRLCAD_MSVC_PGO_PHASE _pgo_msvc_phase)
if(_pgo_msvc_phase AND NOT _pgo_msvc_phase STREQUAL expected_msvc_phase)
message(
FATAL_ERROR
"Expected MSVC PGO phase ${expected_msvc_phase} in ${build_directory}, found ${_pgo_msvc_phase}"
)
endif()
endfunction()
function(_pgo_find_build_file build_directory file_name output_variable)
set(_pgo_candidates "${build_directory}/bin/${file_name}")
if(DEFINED PGO_CONFIG AND NOT "${PGO_CONFIG}" STREQUAL "")
list(
APPEND
_pgo_candidates
"${build_directory}/bin/${PGO_CONFIG}/${file_name}"
"${build_directory}/${PGO_CONFIG}/bin/${file_name}"
)
endif()
if(CMAKE_HOST_WIN32)
set(_pgo_executable_candidates "")
foreach(_pgo_candidate IN LISTS _pgo_candidates)
list(APPEND _pgo_executable_candidates "${_pgo_candidate}.exe")
endforeach()
list(APPEND _pgo_candidates ${_pgo_executable_candidates})
endif()
foreach(_pgo_candidate IN LISTS _pgo_candidates)
if(EXISTS "${_pgo_candidate}")
set("${output_variable}" "${_pgo_candidate}" PARENT_SCOPE)
return()
endif()
endforeach()
message(FATAL_ERROR "Could not find ${file_name} under ${build_directory}/bin")
endfunction()
function(_pgo_find_shell build_directory output_variable)
if(DEFINED PGO_SHELL AND NOT "${PGO_SHELL}" STREQUAL "")
set(_pgo_shell "${PGO_SHELL}")
elseif(EXISTS "${build_directory}/CMakeCache.txt")
file(
STRINGS
"${build_directory}/CMakeCache.txt"
_pgo_shell_cache_entry
LIMIT_COUNT 1
REGEX "^SH_EXEC(:[^=]*)?="
)
if(_pgo_shell_cache_entry)
string(REGEX REPLACE "^[^=]*=" "" _pgo_shell "${_pgo_shell_cache_entry}")
endif()
endif()
if(NOT DEFINED _pgo_shell OR "${_pgo_shell}" STREQUAL "" OR NOT EXISTS "${_pgo_shell}")
unset(_pgo_shell)
unset(_pgo_shell CACHE)
find_program(_pgo_shell NAMES sh bash)
endif()
if(NOT _pgo_shell)
message(FATAL_ERROR "The BRL-CAD benchmark requires a POSIX shell; set PGO_SHELL to its path")
endif()
set("${output_variable}" "${_pgo_shell}" PARENT_SCOPE)
endfunction()
function(_pgo_run_benchmark build_directory result_directory)
_pgo_find_build_file("${build_directory}" benchmark _pgo_benchmark)
_pgo_find_build_file("${build_directory}" rt _pgo_rt)
_pgo_find_build_file("${build_directory}" pixcmp _pgo_pixcmp)
_pgo_find_shell("${build_directory}" _pgo_shell)
file(MAKE_DIRECTORY "${result_directory}")
message(STATUS "Running the BRL-CAD benchmark from ${build_directory}")
execute_process(
COMMAND
"${_pgo_shell}" "${_pgo_benchmark}" run
"TIMEFRAME=${PGO_TIMEFRAME}"
"MAXTIME=${PGO_MAXTIME}"
"AVERAGE=${PGO_AVERAGE}"
"DEVIATION=${PGO_DEVIATION}"
"RT=${_pgo_rt}"
"CMP=${_pgo_pixcmp}"
-P1
WORKING_DIRECTORY "${result_directory}"
RESULT_VARIABLE _pgo_benchmark_result
)
if(NOT "${_pgo_benchmark_result}" STREQUAL "0")
message(FATAL_ERROR "The benchmark from ${build_directory} failed with status ${_pgo_benchmark_result}")
endif()
endfunction()
function(_pgo_extract_rate log_file output_text output_integer)
if(NOT EXISTS "${log_file}")
message(FATAL_ERROR "Benchmark log does not exist: ${log_file}")
endif()
file(READ "${log_file}" _pgo_log)
string(REGEX MATCHALL "[0-9]+(\\.[0-9]+)? rays/sec \\(RTFM\\)" _pgo_rate_lines "${_pgo_log}")
if(NOT _pgo_rate_lines)
message(FATAL_ERROR "No RTFM result was found in ${log_file}")
endif()
list(GET _pgo_rate_lines -1 _pgo_rate_line)
string(REGEX REPLACE " rays/sec.*$" "" _pgo_rate "${_pgo_rate_line}")
if(NOT _pgo_rate MATCHES "^([0-9]+)(\\.([0-9]+))?$")
message(FATAL_ERROR "Invalid RTFM result '${_pgo_rate}' in ${log_file}")
endif()
set(_pgo_whole "${CMAKE_MATCH_1}")
set(_pgo_fraction "${CMAKE_MATCH_3}00")
string(SUBSTRING "${_pgo_fraction}" 0 2 _pgo_fraction)
string(REGEX REPLACE "^0+" "" _pgo_whole "${_pgo_whole}")
string(REGEX REPLACE "^0+" "" _pgo_fraction "${_pgo_fraction}")
if("${_pgo_whole}" STREQUAL "")
set(_pgo_whole 0)
endif()
if("${_pgo_fraction}" STREQUAL "")
set(_pgo_fraction 0)
endif()
math(EXPR _pgo_rate_integer "${_pgo_whole} * 100 + ${_pgo_fraction}")
if(_pgo_rate_integer LESS_EQUAL 0)
message(FATAL_ERROR "Non-positive RTFM result '${_pgo_rate}' in ${log_file}")
endif()
set("${output_text}" "${_pgo_rate}" PARENT_SCOPE)
set("${output_integer}" "${_pgo_rate_integer}" PARENT_SCOPE)
endfunction()
function(_pgo_decimal_to_thousandths value output_variable)
if(NOT "${value}" MATCHES "^([0-9]+)(\\.([0-9]+))?$")
message(FATAL_ERROR "Expected a non-negative decimal number, not '${value}'")
endif()
set(_pgo_whole "${CMAKE_MATCH_1}")
set(_pgo_fraction "${CMAKE_MATCH_3}000")
string(SUBSTRING "${_pgo_fraction}" 0 3 _pgo_fraction)
string(REGEX REPLACE "^0+" "" _pgo_whole "${_pgo_whole}")
string(REGEX REPLACE "^0+" "" _pgo_fraction "${_pgo_fraction}")
if("${_pgo_whole}" STREQUAL "")
set(_pgo_whole 0)
endif()
if("${_pgo_fraction}" STREQUAL "")
set(_pgo_fraction 0)
endif()
math(EXPR _pgo_value "${_pgo_whole} * 1000 + ${_pgo_fraction}")
set("${output_variable}" "${_pgo_value}" PARENT_SCOPE)
endfunction()
function(_pgo_format_thousandths value output_variable)
math(EXPR _pgo_whole "${value} / 1000")
math(EXPR _pgo_fraction "${value} % 1000")
set(_pgo_fraction "00${_pgo_fraction}")
string(LENGTH "${_pgo_fraction}" _pgo_fraction_length)
math(EXPR _pgo_fraction_offset "${_pgo_fraction_length} - 3")
string(SUBSTRING "${_pgo_fraction}" ${_pgo_fraction_offset} 3 _pgo_fraction)
set("${output_variable}" "${_pgo_whole}.${_pgo_fraction}" PARENT_SCOPE)
endfunction()
if(PGO_MODE STREQUAL "TRAIN")
_pgo_require(PGO_BUILD_DIR)
_pgo_require(PGO_RESULTS_DIR)
_pgo_require(PGO_PROFILE_DIR)
_pgo_absolute_path("${PGO_BUILD_DIR}" PGO_BUILD_DIR)
_pgo_absolute_path("${PGO_RESULTS_DIR}" PGO_RESULTS_DIR)
_pgo_absolute_path("${PGO_PROFILE_DIR}" PGO_PROFILE_DIR)
if(NOT IS_DIRECTORY "${PGO_BUILD_DIR}")
message(FATAL_ERROR "PGO_BUILD_DIR does not exist: ${PGO_BUILD_DIR}")
endif()
_pgo_validate_build("${PGO_BUILD_DIR}" TRUE GENERATE)
# MSVC's profile runtime uses this location for each image's PGC files.
# GCC ignores it and writes its GCDA files to the compiled-in PGO path.
file(MAKE_DIRECTORY "${PGO_PROFILE_DIR}")
set(ENV{VCPROFILE_PATH} "${PGO_PROFILE_DIR}")
_pgo_run_benchmark("${PGO_BUILD_DIR}" "${PGO_RESULTS_DIR}/benchmark")
_pgo_find_build_file("${PGO_BUILD_DIR}" rt _pgo_rt)
if(CMAKE_HOST_WIN32)
set(_pgo_null_device NUL)
else()
set(_pgo_null_device /dev/null)
endif()
set(_pgo_training_index 0)
foreach(_pgo_model IN LISTS PGO_TRAINING_MODELS)
math(EXPR _pgo_training_index "${_pgo_training_index} + 1")
if(IS_ABSOLUTE "${_pgo_model}")
set(_pgo_model_path "${_pgo_model}")
else()
set(_pgo_model_path "${PGO_BUILD_DIR}/${_pgo_model}")
endif()
if(NOT EXISTS "${_pgo_model_path}")
message(FATAL_ERROR "Training model does not exist: ${_pgo_model_path}")
endif()
get_filename_component(_pgo_log_name "${_pgo_model_path}" NAME_WE)
string(REGEX REPLACE "[^A-Za-z0-9_.-]" "_" _pgo_log_name "${_pgo_log_name}")
set(_pgo_log_file "${PGO_RESULTS_DIR}/${_pgo_training_index}-${_pgo_log_name}.log")
message(STATUS "Training with ${_pgo_model_path}")
execute_process(
COMMAND
"${_pgo_rt}" -B -P1 "-s${PGO_TRAINING_SIZE}"
"-a${PGO_TRAINING_AZIMUTH}" "-e${PGO_TRAINING_ELEVATION}"
-o "${_pgo_null_device}" "${_pgo_model_path}"
RESULT_VARIABLE _pgo_rt_result
OUTPUT_VARIABLE _pgo_rt_output
ERROR_VARIABLE _pgo_rt_error
)
file(WRITE "${_pgo_log_file}" "${_pgo_rt_output}${_pgo_rt_error}")
if(NOT "${_pgo_rt_result}" STREQUAL "0")
message(FATAL_ERROR "Training ${_pgo_model} failed with status ${_pgo_rt_result}; see ${_pgo_log_file}")
endif()
string(REGEX MATCH "[^\r\n]*rays/sec \\(RTFM\\)" _pgo_rtfm_line "${_pgo_rt_output}${_pgo_rt_error}")
if(NOT _pgo_rtfm_line)
message(FATAL_ERROR "Training ${_pgo_model} did not complete a raytrace; see ${_pgo_log_file}")
endif()
message(STATUS "${_pgo_model}: ${_pgo_rtfm_line}")
endforeach()
file(GLOB_RECURSE _pgo_gcda_files LIST_DIRECTORIES FALSE "${PGO_PROFILE_DIR}/*.gcda")
file(GLOB_RECURSE _pgo_pgc_files LIST_DIRECTORIES FALSE "${PGO_PROFILE_DIR}/*.pgc")
list(LENGTH _pgo_gcda_files _pgo_gcda_count)
list(LENGTH _pgo_pgc_files _pgo_pgc_count)
math(EXPR _pgo_profile_count "${_pgo_gcda_count} + ${_pgo_pgc_count}")
if(_pgo_profile_count EQUAL 0)
message(FATAL_ERROR "Training did not generate GCC GCDA or MSVC PGC data under ${PGO_PROFILE_DIR}")
endif()
message(STATUS "Training generated ${_pgo_profile_count} profile counter files under ${PGO_PROFILE_DIR}")
else()
_pgo_require(PGO_BASELINE_BUILD_DIR)
_pgo_require(PGO_OPTIMIZED_BUILD_DIR)
_pgo_require(PGO_RESULTS_DIR)
_pgo_absolute_path("${PGO_BASELINE_BUILD_DIR}" PGO_BASELINE_BUILD_DIR)
_pgo_absolute_path("${PGO_OPTIMIZED_BUILD_DIR}" PGO_OPTIMIZED_BUILD_DIR)
_pgo_absolute_path("${PGO_RESULTS_DIR}" PGO_RESULTS_DIR)
_pgo_validate_build("${PGO_BASELINE_BUILD_DIR}" FALSE "")
_pgo_validate_build("${PGO_OPTIMIZED_BUILD_DIR}" TRUE USE)
_pgo_run_benchmark("${PGO_BASELINE_BUILD_DIR}" "${PGO_RESULTS_DIR}/baseline")
_pgo_run_benchmark("${PGO_OPTIMIZED_BUILD_DIR}" "${PGO_RESULTS_DIR}/optimized")
set(_pgo_report "### Profile-guided optimization results\n\n")
string(APPEND _pgo_report "| Model | Baseline rays/sec | PGO rays/sec | Speedup |\n")
string(APPEND _pgo_report "| --- | ---: | ---: | ---: |\n")
set(_pgo_speedup_sum 0)
set(_pgo_model_count 0)
set(_pgo_tsv "model\tbaseline_rays_per_second\tpgo_rays_per_second\tspeedup\n")
foreach(_pgo_model IN LISTS PGO_MODELS)
_pgo_extract_rate("${PGO_RESULTS_DIR}/baseline/${_pgo_model}.log" _pgo_baseline_rate _pgo_baseline_integer)
_pgo_extract_rate("${PGO_RESULTS_DIR}/optimized/${_pgo_model}.log" _pgo_optimized_rate _pgo_optimized_integer)
math(
EXPR
_pgo_speedup
"(${_pgo_optimized_integer} * 1000 + ${_pgo_baseline_integer} / 2) / ${_pgo_baseline_integer}"
)
_pgo_format_thousandths("${_pgo_speedup}" _pgo_speedup_text)
math(EXPR _pgo_speedup_sum "${_pgo_speedup_sum} + ${_pgo_speedup}")
math(EXPR _pgo_model_count "${_pgo_model_count} + 1")
string(
APPEND
_pgo_report
"| `${_pgo_model}` | ${_pgo_baseline_rate} | ${_pgo_optimized_rate} | ${_pgo_speedup_text}x |\n"
)
string(
APPEND
_pgo_tsv
"${_pgo_model}\t${_pgo_baseline_rate}\t${_pgo_optimized_rate}\t${_pgo_speedup_text}\n"
)
endforeach()
if(_pgo_model_count EQUAL 0)
message(FATAL_ERROR "PGO_MODELS must name at least one benchmark model")
endif()
math(EXPR _pgo_mean_speedup "(${_pgo_speedup_sum} + ${_pgo_model_count} / 2) / ${_pgo_model_count}")
_pgo_format_thousandths("${_pgo_mean_speedup}" _pgo_mean_speedup_text)
string(APPEND _pgo_report "\nArithmetic mean speedup: ${_pgo_mean_speedup_text}x\n")
file(MAKE_DIRECTORY "${PGO_RESULTS_DIR}")
file(WRITE "${PGO_RESULTS_DIR}/results.tsv" "${_pgo_tsv}")
if(DEFINED PGO_SUMMARY_FILE AND NOT "${PGO_SUMMARY_FILE}" STREQUAL "")
_pgo_absolute_path("${PGO_SUMMARY_FILE}" _pgo_summary_file)
elseif(NOT "$ENV{GITHUB_STEP_SUMMARY}" STREQUAL "")
set(_pgo_summary_file "$ENV{GITHUB_STEP_SUMMARY}")
endif()
if(DEFINED _pgo_summary_file)
file(APPEND "${_pgo_summary_file}" "${_pgo_report}")
endif()
message("${_pgo_report}")
_pgo_decimal_to_thousandths("${PGO_MIN_SPEEDUP}" _pgo_min_speedup)
if(NOT _pgo_mean_speedup GREATER _pgo_min_speedup)
message(
FATAL_ERROR
"PGO raytracing speedup ${_pgo_mean_speedup_text}x did not exceed ${PGO_MIN_SPEEDUP}x"
)
endif()
endif()
# Local Variables:
# tab-width: 8
# mode: cmake
# indent-tabs-mode: t
# End:
# ex: shiftwidth=2 tabstop=8
+35 -19
View File
@@ -7,14 +7,25 @@
set -e
set -o pipefail
# Note: we set these variables to tell the ccc-analyzer script which compilers
# to use, but it is not always enough - when some of the sub-builds are
# triggered, they do not seem to inherit these settings and revert to the
# default compiler. The only way so far to avoid this issue reliably seems to
# be to edit the ccc-analyzer script to use the compiler we want as the default
# compiler.
export CCC_CC=clang
export CCC_CXX=clang++
# CLANG_VERSION should be set by User or CI environments to avoid hard-coding
# a particular version into the script
if [ -z "$CLANG_VERSION" ]; then
echo "Error: CLANG_VERSION environment variable is not set." >&2
exit 1
fi
# Derive tool paths from the version
SCAN_BUILD_DIR="/usr/share/clang/scan-build-${CLANG_VERSION}"
SCAN_BUILD_BIN="${SCAN_BUILD_DIR}/bin"
SCAN_BUILD_LIBEXEC="${SCAN_BUILD_DIR}/libexec"
CLANG_ANALYZER_BIN="/usr/bin/clang-${CLANG_VERSION}"
# Add scan-build to the PATH
export PATH="${SCAN_BUILD_BIN}:${SCAN_BUILD_LIBEXEC}:${PATH}"
# Set compilers for the analyzer scripts
export CCC_CC="clang-${CLANG_VERSION}"
export CCC_CXX="clang++-${CLANG_VERSION}"
# This appears to be a workable way to enable the new Z3 static analyzer
# support, but at least as of 2017-12 it greatly slows the testing (by orders
@@ -45,19 +56,24 @@ fi
# Encapsulate the logic to do a scan build
function runtest {
echo "$1"
scan-build --use-analyzer=/usr/bin/$CCC_CC -o ./scan-reports-$1 make -j12 $1
if [ "$(ls -A ./scan-reports-$1)" ]; then
report_cnt=$(ls -l ./scan-reports-$1/*/report* | wc -l)
failure=$(($failure + $report_cnt))
else
rm -rf ./scan-reports-$1
fi
echo "$1"
scan-build --use-analyzer="${CLANG_ANALYZER_BIN}" -o "./scan-reports-$1" make -j12 "$1"
if [ -d "./scan-reports-$1" ] && [ -n "$(ls -A "./scan-reports-$1")" ]; then
report_cnt=$(find "./scan-reports-$1" -name 'report-*.html' | wc -l)
failure=$((failure + report_cnt))
else
rm -rf "./scan-reports-$1"
fi
}
# configure using the correct compiler and values. We don't
# need this report, so clear it after configure is done
scan-build --use-analyzer=/usr/bin/$CCC_CC -o ./scan-reports-config cmake .. -DBRLCAD_EXTRADOCS=OFF -DBRLCAD_ENABLE_QT=ON -DBRLCAD_LTO_MODE=OFF -DBRLCAD_EXT_DIR="$bext_dir" -DCMAKE_C_COMPILER=ccc-analyzer -DCMAKE_CXX_COMPILER=c++-analyzer
# Configure using the correct compiler and values.
scan-build --use-analyzer="${CLANG_ANALYZER_BIN}" -o ./scan-reports-config cmake .. \
-DBRLCAD_EXTRADOCS=OFF \
-DBRLCAD_ENABLE_QT=ON \
-DBRLCAD_LTO_MODE=OFF \
-DBRLCAD_EXT_DIR="$bext_dir" \
-DCMAKE_C_COMPILER=ccc-analyzer \
-DCMAKE_CXX_COMPILER=c++-analyzer
# clear out any old reports
rm -rfv ./scan-reports-*
+13
View File
@@ -144,6 +144,18 @@ if(BRLCAD_X11_PROVIDER_RESOLVED STREQUAL "XMIN" AND
)
brlcad_regression_test(regress-archer-embedded-xmin-gui
"archer;bwish" STAND_ALONE TEST_DEFINED)
brlcad_add_gui_test(
NAME regress-archer-geometry-xmin-gui
TIMEOUT 120
ENVIRONMENT ${_gui_smoke_environment}
"GUI_TEST_LIBRARY=${_gui_test_library}"
COMMAND "${SH_EXEC}" "${_gui_tcl_runner}" archer-geometry
$<TARGET_FILE:bwish>
"${CMAKE_CURRENT_SOURCE_DIR}/archer_geometry_xmin_gui.tcl"
)
brlcad_regression_test(regress-archer-geometry-xmin-gui
"archer;bwish" STAND_ALONE TEST_DEFINED)
endif()
endif()
@@ -201,6 +213,7 @@ cmakefiles(
archer_action.sh
archer_smoke.sh
archer_embedded_xmin_gui.tcl
archer_geometry_xmin_gui.tcl
archer_menu_manifest.txt
archer_xmin_gui.tcl
qged_action.sh
+134
View File
@@ -0,0 +1,134 @@
# A R C H E R _ G E O M E T R Y _ X M I N _ G U I . T C L
# BRL-CAD
#
# Copyright (c) 2026 United States Government as represented by
# the U.S. Army Research Laboratory.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# version 2.1 as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this file; see the file named COPYING for more
# information.
#
###
if {![info exists ::env(GUI_TEST_LIBRARY)]} {
puts stderr "GUI_TEST_LIBRARY is required"
exit 2
}
source $::env(GUI_TEST_LIBRARY)
set ::env(ARCHER_PREFS_FILE) [file join $::env(GUI_TEST_DIR) .archerrc]
proc archer_geometry_preferences {{geometry ""}} {
set channel [open $::env(ARCHER_PREFS_FILE) w]
if {$geometry ne ""} {
puts $channel [list set mWindowGeometry $geometry]
}
close $channel
}
proc archer_geometry_saved {} {
set channel [open $::env(ARCHER_PREFS_FILE) r]
set contents [read $channel]
close $channel
if {[regexp -line {^set mWindowGeometry (.+)$} $contents -> geometry]} {
return $geometry
}
return ""
}
proc archer_geometry_settle {} {
# Allow MapNotify and the deferred geometry restore to reach the event loop.
set settle_delay_ms 100
after $settle_delay_ms [list set ::archer_geometry_settled 1]
vwait ::archer_geometry_settled
update
::gui::test::require_no_background_errors
}
if {[catch {
package require Archer 1.0
::gui::test::capture_background_errors
wm withdraw .
set saved_geometry 900x650+120+90
set startup_geometry 1000x700+20+30
set updated_geometry 950x680+60+70
set default_geometry 920x660+40+50
archer_geometry_preferences $saved_geometry
Archer .unmapped
::gui::test::require {[wm state .unmapped] eq "withdrawn"} \
"startup fixture unexpectedly showed its window"
::itcl::delete object .unmapped
archer_geometry_settle
::gui::test::require {[archer_geometry_saved] eq $saved_geometry} \
"closing an unmapped Archer replaced its saved geometry"
archer_geometry_preferences
Archer .unmapped_new
::itcl::delete object .unmapped_new
archer_geometry_settle
::gui::test::require {[archer_geometry_saved] eq ""} \
"closing an unmapped Archer saved an unfinished layout size"
archer_geometry_preferences $saved_geometry
Archer .restore
.restore configure -geometry $startup_geometry
wm geometry .restore $startup_geometry
event generate [.restore component hpane] <Map>
archer_geometry_settle
::gui::test::require {[wm geometry .restore] eq $startup_geometry} \
"a child Map event restored the main window's geometry prematurely"
wm deiconify .restore
archer_geometry_settle
::gui::test::require {[wm geometry .restore] eq $saved_geometry} \
"mapping the main window did not restore its saved geometry"
wm geometry .restore $updated_geometry
event generate [.restore component hpane] <Map>
archer_geometry_settle
::gui::test::require {[wm geometry .restore] eq $updated_geometry} \
"a later child Map event reset the resized main window"
wm withdraw .restore
::itcl::delete object .restore
archer_geometry_settle
::gui::test::require {[archer_geometry_saved] eq $updated_geometry} \
"closing a previously mapped window did not save its latest geometry"
archer_geometry_preferences
Archer .default
wm geometry .default $default_geometry
wm deiconify .default
archer_geometry_settle
::gui::test::require {[wm geometry .default] eq $default_geometry} \
"an empty geometry option reset the window to its layout size"
wm withdraw .default
::itcl::delete object .default
archer_geometry_settle
} message options]} {
puts stderr $message
if {[dict exists $options -errorinfo]} {
puts stderr [dict get $options -errorinfo]
}
puts "FAIL: Archer window geometry regression"
exit 1
}
puts "PASS: Archer window geometry restoration and startup cancellation"
exit 0
# Local Variables:
# tab-width: 8
# mode: Tcl
# tcl-indent-level: 4
# indent-tabs-mode: t
# End:
# ex: shiftwidth=4 tabstop=8
+7 -9
View File
@@ -72,17 +72,15 @@ set(archer_wizard_FILES
)
if(BRLCAD_ENABLE_TCL)
brlcad_adddata(archer_utility_FILES plugins/archer/Utility)
brlcad_adddata(archer_wizard_FILES plugins/archer/Wizards)
# Archer scans only the plugin category directories for registration scripts.
# Keep class definitions in their subdirectories so they are not sourced twice.
foreach(plugin_file IN LISTS archer_utility_FILES archer_wizard_FILES)
get_filename_component(plugin_dir "${plugin_file}" DIRECTORY)
file(RELATIVE_PATH plugin_dir "${CMAKE_CURRENT_SOURCE_DIR}/plugins" "${CMAKE_CURRENT_SOURCE_DIR}/${plugin_dir}")
brlcad_adddata("${plugin_file}" "plugins/archer/${plugin_dir}")
endforeach()
brlcad_adddata(plugins/Commands/README plugins/archer/Command)
brlcad_adddata(plugins/Core/README plugins/archer/Core)
brlcad_adddata(plugins/Utility/attrGroupsDisplayUtilityP/AttrGroupsDisplayUtilityP.tcl plugins/archer/Utility/attrGroupsDisplayUtilityP)
brlcad_adddata(plugins/Utility/botUtilityP/BotUtilityP.tcl plugins/archer/Utility/botUtilityP)
brlcad_adddata(plugins/Utility/lodUtilityP/LODUtilityP.tcl plugins/archer/Utility/lodUtilityP)
brlcad_adddata(plugins/Wizards/humanwizard/HumanWizard.tcl plugins/archer/Wizards/humanwizard)
brlcad_adddata(plugins/Wizards/tankwizard/TankWizard.tcl plugins/archer/Wizards/tankwizard)
brlcad_adddata(plugins/Wizards/tankwizard/images/tank.png plugins/archer/Wizards/tankwizardIA/images)
brlcad_adddata(plugins/Wizards/tirewizard/TireWizard.tcl plugins/archer/Wizards/tirewizard)
endif(BRLCAD_ENABLE_TCL)
set(
+10
View File
@@ -109,6 +109,16 @@ brlcad_addexec(g-var g-var.c "librt;libbu;${M_LIBRARY}" FOLDER Conv)
brlcad_addexec(g-vrml g-vrml.c "librt;libnmg;libbu;${M_LIBRARY}" FOLDER Conv)
brlcad_addexec(g-x3d g-x3d.c "librt;libnmg;libbu;${M_LIBRARY}" FOLDER Conv)
# GCC cannot model BU_SETJUMP's longjmp path during LTO and reports an
# anonymous value as potentially uninitialized.
check_linker_flag(
"C"
"-Wno-error=maybe-uninitialized"
G_X3D_LINKER_WNO_MAYBE_UNINITIALIZED
)
if(G_X3D_LINKER_WNO_MAYBE_UNINITIALIZED)
target_link_options(g-x3d PRIVATE -Wno-maybe-uninitialized)
endif()
brlcad_addexec(nastran-g nastran-g.c "libwdb;librt;libnmg;libbu;${M_LIBRARY}" FOLDER Conv)
+6 -6
View File
@@ -326,7 +326,7 @@ SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) {
/* -------------------------------------------------------------------- */
/* Initialize the info structure. */
/* -------------------------------------------------------------------- */
SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(sizeof(SHPInfo),1));
SHPHandle psSHP = STATIC_CAST(SHPHandle, calloc(1,sizeof(SHPInfo)));
psSHP->bUpdated = FALSE;
memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) );
@@ -1176,7 +1176,7 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts,
psObject->nParts = MAX(1,nParts);
psObject->panPartStart = STATIC_CAST(int *,
calloc(sizeof(int), psObject->nParts));
calloc(psObject->nParts, sizeof(int)));
psObject->panPartType = STATIC_CAST(int *,
malloc(sizeof(int) * psObject->nParts));
@@ -1205,13 +1205,13 @@ SHPCreateObject( int nSHPType, int nShapeId, int nParts,
{
const size_t nSize = sizeof(double) * nVertices;
psObject->padfX = STATIC_CAST(double *, padfX ? malloc(nSize) :
calloc(sizeof(double),nVertices));
calloc(nVertices,sizeof(double)));
psObject->padfY = STATIC_CAST(double *, padfY ? malloc(nSize) :
calloc(sizeof(double),nVertices));
calloc(nVertices,sizeof(double)));
psObject->padfZ = STATIC_CAST(double *, padfZ && bHasZ ? malloc(nSize) :
calloc(sizeof(double),nVertices));
calloc(nVertices,sizeof(double)));
psObject->padfM = STATIC_CAST(double *, padfM && bHasM ? malloc(nSize) :
calloc(sizeof(double),nVertices));
calloc(nVertices,sizeof(double)));
if( padfX != SHPLIB_NULLPTR )
memcpy(psObject->padfX, padfX, nSize);
if( padfY != SHPLIB_NULLPTR )
+2 -2
View File
@@ -5545,8 +5545,8 @@ repair_ambiguous_singular_periodic_loop_branches(ON_Brep *brep,
std::vector<int> affected_positions;
affected_positions.reserve(arc_positions.size() + 2);
affected_positions.push_back(first_position);
affected_positions.insert(affected_positions.end(),
arc_positions.begin(), arc_positions.end());
for (const int position : arc_positions)
affected_positions.push_back(position);
affected_positions.push_back(second_position);
for (int shift_sign = -1; shift_sign <= 1 && !repaired;
+20 -16
View File
@@ -50,16 +50,16 @@
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
void SHA1Transform(uint32_t state[5], const unsigned char buffer[SHA1_BLOCK_SIZE])
{
uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
unsigned char c[SHA1_BLOCK_SIZE];
uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
memcpy(block, buffer, 64);
memcpy(block, buffer, sizeof(block));
#else
/* The following had better never be used because it causes the
* pointer-to-const buffer to be cast into a pointer to non-const.
@@ -131,11 +131,11 @@ void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1] += (len>>29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
j = (j >> 3) & (SHA1_BLOCK_SIZE - 1);
if ((j + len) >= SHA1_BLOCK_SIZE) {
memcpy(&context->buffer[j], data, (i = SHA1_BLOCK_SIZE-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
for ( ; i + SHA1_BLOCK_SIZE - 1 < len; i += SHA1_BLOCK_SIZE) {
SHA1Transform(context->state, &data[i]);
}
j = 0;
@@ -149,22 +149,26 @@ void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
unsigned i;
static const unsigned char padding[SHA1_BLOCK_SIZE] = {0x80};
unsigned char finalcount[8];
unsigned char c;
const uint32_t block_size = (uint32_t)sizeof(context->buffer);
const uint32_t length_offset = block_size - (uint32_t)sizeof(finalcount);
uint32_t buffer_used;
uint32_t padding_length;
unsigned i;
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
c = 0200;
SHA1Update(context, &c, 1);
while ((context->count[0] & 504) != 448) {
c = 0000;
SHA1Update(context, &c, 1);
}
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
buffer_used = (context->count[0] >> 3) & (block_size - 1);
if (buffer_used < length_offset)
padding_length = length_offset - buffer_used;
else
padding_length = block_size + length_offset - buffer_used;
SHA1Update(context, padding, padding_length);
SHA1Update(context, finalcount, (uint32_t)sizeof(finalcount));
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+4 -2
View File
@@ -7,15 +7,17 @@
#include "common.h"
#define SHA1_BLOCK_SIZE 64
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
unsigned char buffer[SHA1_BLOCK_SIZE];
} SHA1_CTX;
__BEGIN_DECLS
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Transform(uint32_t state[5], const unsigned char buffer[SHA1_BLOCK_SIZE]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+1 -1
View File
@@ -46,7 +46,7 @@ extern double drand48(void);
int
bu_uuid_create(uint8_t uuid[STATIC_ARRAY(16)], size_t nbytes, const uint8_t *bytes, const uint8_t namespace_uuid[STATIC_ARRAY(16)])
bu_uuid_create(uint8_t uuid[STATIC_ARRAY(16)], size_t nbytes, const uint8_t *bytes, const uint8_t *namespace_uuid)
{
int type = 4; /* random */
+4 -4
View File
@@ -934,7 +934,7 @@ x24_setup(struct fb *ifp, int width, int height)
case FLG_VP8:
case FLG_VS8:
case FLG_VG8:
if ((xi->xi_pix = (unsigned char *) calloc(sizeof(uint8_t), width*height)) == NULL) {
if ((xi->xi_pix = (unsigned char *) calloc(width*height, sizeof(uint8_t))) == NULL) {
fb_log("X24_open: pix8 malloc failed\n");
return -1;
}
@@ -950,7 +950,7 @@ x24_setup(struct fb *ifp, int width, int height)
xi->xi_visual, xi->xi_depth, XYBitmap, 0,
NULL, width, height, 32, 0);
if ((xi->xi_pix = (unsigned char *) calloc(sizeof(uint8_t), xi->xi_image->bytes_per_line * height)) == NULL) {
if ((xi->xi_pix = (unsigned char *) calloc(xi->xi_image->bytes_per_line * height, sizeof(uint8_t))) == NULL) {
fb_log("X24_open: pix1 malloc failed\n");
return -1;
}
@@ -2653,7 +2653,7 @@ X24_configureWindow(struct fb *ifp, int width, int height)
XDestroyImage(xi->xi_image);
/* Make new buffers and new image */
if ((xi->xi_pix = (unsigned char *)calloc(sizeof(uint8_t), xi->xi_xwidth * xi->xi_xheight)) == NULL) {
if ((xi->xi_pix = (unsigned char *)calloc(xi->xi_xwidth * xi->xi_xheight, sizeof(uint8_t))) == NULL) {
fb_log("X24: pix8 malloc failed in resize!\n");
return 1;
}
@@ -2671,7 +2671,7 @@ X24_configureWindow(struct fb *ifp, int width, int height)
xi->xi_visual, xi->xi_depth, XYBitmap, 0,
NULL, xi->xi_xwidth, xi->xi_xheight, 32, 0);
if ((xi->xi_pix = (unsigned char *)calloc(sizeof(uint8_t), xi->xi_image->bytes_per_line * xi->xi_xheight)) == NULL) {
if ((xi->xi_pix = (unsigned char *)calloc(xi->xi_image->bytes_per_line * xi->xi_xheight, sizeof(uint8_t))) == NULL) {
fb_log("X24: pix1 malloc failed in resize!\n");
return 1;
}
+2 -2
View File
@@ -422,12 +422,12 @@ fb_open(const char *file, int width, int height)
if (width < 0 || height < 0)
return FB_NULL;
ifp = (struct fb *)calloc(sizeof(struct fb), 1);
ifp = (struct fb *)calloc(1, sizeof(struct fb));
if (ifp == FB_NULL) {
Malloc_Bomb(sizeof(struct fb));
return FB_NULL;
}
ifp->i = (struct fb_impl *)calloc(sizeof(struct fb_impl), 1);
ifp->i = (struct fb_impl *)calloc(1, sizeof(struct fb_impl));
if (file == NULL || *file == '\0') {
/* No name given, check environment variable first. */
file = (const char *)getenv("FB_FILE");
+2 -2
View File
@@ -114,8 +114,8 @@ struct fb *
fb_open_existing(const char *file, int width, int height, struct fb_platform_specific *fb_p)
{
struct fb *ifp;
ifp = (struct fb *)calloc(sizeof(struct fb), 1);
ifp->i = (struct fb_impl *) calloc(sizeof(struct fb_impl), 1);
ifp = (struct fb *)calloc(1, sizeof(struct fb));
ifp->i = (struct fb_impl *) calloc(1, sizeof(struct fb_impl));
fb_set_interface(ifp, file);
fb_set_magic(ifp, FB_MAGIC);
fb_setup_existing(ifp, width, height, fb_p);
+90 -70
View File
@@ -247,19 +247,41 @@ rt_in_rpp(struct xray *rp,
}
/* A name alone cannot distinguish transformed instances of a solid. */
static struct soltab *
rt_find_solid_with_mat(const struct rt_i *rtip,
const struct directory *dp, const mat_t mat)
{
struct soltab *stp;
RT_VISIT_ALL_SOLTABS_START(stp, (struct rt_i *)rtip) {
if (stp->st_dp != dp)
continue;
const fastf_t *st_mat = stp->st_matp ?
stp->st_matp : bn_mat_identity;
if (bn_mat_is_equal(mat, st_mat, &rtip->rti_tol))
return stp;
} RT_VISIT_ALL_SOLTABS_END;
return NULL;
}
/**
* Traverse the passed tree using rt_db_internals to show the way
* This function supports rt_bound_internal and is internal to librt
* Traverse the original database tree while tracking member transforms.
*
* Returns -
* 0 success
* -1 failure (tree_min and tree_max may have been altered)
* Returns 0 on success, -1 if no bounds can be computed.
*/
int
rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fastf_t *tree_max)
static int
rt_traverse_tree_mat(struct rt_i *rtip, const union tree *tp,
fastf_t *tree_min, fastf_t *tree_max,
const mat_t mat)
{
vect_t r_min, r_max; /* rpp for right side of tree */
if (!tp)
return -1;
RT_CK_TREE(tp);
switch (tp->tr_op) {
@@ -269,6 +291,8 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
const struct soltab *stp;
stp = tp->tr_a.tu_stp;
if (!stp)
return -1;
RT_CK_SOLTAB(stp);
if (stp->st_aradius <= 0) {
bu_log("rt_traverse_tree: encountered dead solid '%s'\n",
@@ -293,16 +317,20 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
case OP_XOR:
case OP_UNION:
/* BINARY type -- expand to contain both */
if (rt_traverse_tree(rtip, tp->tr_b.tb_left, tree_min, tree_max) < 0 ||
rt_traverse_tree(rtip, tp->tr_b.tb_right, r_min, r_max) < 0)
if (rt_traverse_tree_mat(rtip, tp->tr_b.tb_left,
tree_min, tree_max, mat) < 0 ||
rt_traverse_tree_mat(rtip, tp->tr_b.tb_right,
r_min, r_max, mat) < 0)
return -1;
VMIN(tree_min, r_min);
VMAX(tree_max, r_max);
break;
case OP_INTERSECT:
/* BINARY type -- find common area only */
if (rt_traverse_tree(rtip, tp->tr_b.tb_left, tree_min, tree_max) < 0 ||
rt_traverse_tree(rtip, tp->tr_b.tb_right, r_min, r_max) < 0)
if (rt_traverse_tree_mat(rtip, tp->tr_b.tb_left,
tree_min, tree_max, mat) < 0 ||
rt_traverse_tree_mat(rtip, tp->tr_b.tb_right,
r_min, r_max, mat) < 0)
return -1;
/* min = largest min, max = smallest max */
VMAX(tree_min, r_min);
@@ -313,7 +341,8 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
* rt_gettree's subtractor shaker may have already discarded a
* disjoint B (and its soltab) from the prepared tree, while this
* routine may be walking the original database tree. */
if (rt_traverse_tree(rtip, tp->tr_b.tb_left, tree_min, tree_max) < 0)
if (rt_traverse_tree_mat(rtip, tp->tr_b.tb_left,
tree_min, tree_max, mat) < 0)
return -1;
break;
@@ -324,6 +353,8 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
case OP_DB_LEAF:
{
const struct soltab *stp;
struct directory *dp;
mat_t leaf_mat;
if (rtip == NULL) {
bu_log("rt_traverse_tree: A valid rtip was not passed for calculating bounds of '%s'\n",
@@ -331,60 +362,44 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
return -1;
}
/* Good to go */
MAT_COPY(leaf_mat, mat);
if (tp->tr_l.tl_mat)
bn_mat_mul(leaf_mat, mat, tp->tr_l.tl_mat);
/* Attempt to get a solid pointer, will fail for combs */
stp = rt_find_solid(rtip, tp->tr_l.tl_name);
dp = db_lookup(rtip->rti_dbip, tp->tr_l.tl_name,
LOOKUP_QUIET);
if (dp == RT_DIR_NULL)
return -1;
stp = rt_find_solid_with_mat(rtip, dp, leaf_mat);
if (stp == NULL) {
/* It was a comb! get an internal format and
* repeat the whole thing that got us here in the
* 1st place
*/
/* Recurse through combinations using their original
* trees. A primitive without a matching prepared
* instance has no valid bounds. */
struct rt_db_internal intern;
struct directory *dp;
struct rt_comb_internal *combp;
/* Get the directory pointer */
if ((dp=db_lookup(rtip->rti_dbip, tp->tr_l.tl_name, LOOKUP_QUIET)) == RT_DIR_NULL) {
bu_log("rt_traverse_tree: db_lookup(%s) failed", tp->tr_l.tl_name);
return -1;
}
/* Why does recursion work with the internal
* format ? The internal format does have the
* boolean op for a comb in the tr_a.tu_op field
* in the root node, even though it has no prims
* at the leaves. So recursive calls to load the
* prim further down the tree, will return the
* correct bb as we are going through the proper
* switch case in each step down the tree
*/
if (!rt_db_lookup_internal(rtip->rti_dbip, tp->tr_l.tl_name, &dp, &intern, LOOKUP_NOISY)) {
bu_log("rt_traverse_tree: rt_db_lookup_internal(%s) failed to get the internal form",
tp->tr_l.tl_name);
return -1;
}
/* The passed rt_db_internal should be a comb,
* prepare a rt_comb_internal
*/
if (intern.idb_minor_type == ID_COMBINATION) {
combp = (struct rt_comb_internal *)intern.idb_ptr;
} else {
/* if it's not a comb, then something else is
* cooking.
*/
bu_log("rt_traverse_tree: WARNING : rt_db_lookup_internal(%s) got the internal form of a primitive when it should not, the bounds may not be correct", tp->tr_l.tl_name);
if (intern.idb_minor_type != ID_COMBINATION) {
bu_log("rt_traverse_tree: no prepared instance"
" for '%s'\n", tp->tr_l.tl_name);
rt_db_free_internal(&intern);
return -1;
}
combp = (struct rt_comb_internal *)intern.idb_ptr;
RT_CK_COMB(combp);
/* further down the rabbit hole */
if (rt_traverse_tree(rtip, combp->tree, tree_min, tree_max)) {
bu_log("rt_traverse_tree: rt_bound_tree() failed\n");
int ret = rt_traverse_tree_mat(rtip, combp->tree,
tree_min, tree_max, leaf_mat);
rt_db_free_internal(&intern);
if (ret)
return -1;
}
} else {
/* Got a solid pointer, get bounds and return */
RT_CK_SOLTAB(stp);
@@ -408,13 +423,24 @@ rt_traverse_tree(struct rt_i *rtip, const union tree *tp, fastf_t *tree_min, fas
}
case OP_NOP:
/* Implies that this tree has nothing in it */
break;
/* An empty tree has no bounds to return. */
return -1;
}
return 0;
}
int
rt_traverse_tree(struct rt_i *rtip, const union tree *tp,
fastf_t *tree_min, fastf_t *tree_max)
{
mat_t mat;
MAT_IDN(mat);
return rt_traverse_tree_mat(rtip, tp, tree_min, tree_max, mat);
}
int
rt_bound_instance(point_t *bmin, point_t *bmax,
struct directory *dp,
@@ -472,7 +498,8 @@ rt_bound_internal(struct db_i *dbip, struct directory *dp,
struct rt_db_internal intern;
struct rt_comb_internal *combp;
vect_t tree_min, tree_max;
union tree *tp;
const union tree *tp;
union tree primitive_tree;
/* Initialize RPP bounds */
VSETALL(rpp_min, MAX_FASTF);
@@ -494,7 +521,8 @@ rt_bound_internal(struct db_i *dbip, struct directory *dp,
if (!rt_db_lookup_internal(dbip, dp->d_namep, &dp, &intern, LOOKUP_NOISY)) {
bu_exit(1, "rt_bound_internal: rt_db_lookup_internal(%s) failed to get the internal form", dp->d_namep);
bu_log("rt_bound_internal: lookup failed for '%s'\n",
dp->d_namep);
rt_i_destroy(rtip);
return -1;
}
@@ -504,26 +532,18 @@ rt_bound_internal(struct db_i *dbip, struct directory *dp,
*/
if (intern.idb_minor_type == ID_COMBINATION) {
combp = (struct rt_comb_internal *)intern.idb_ptr;
RT_CK_COMB(combp);
tp = combp->tree;
} else {
/* A primitive was passed, construct a struct rt_comb_internal
* with a single leaf node.
*/
BU_ALLOC(combp, struct rt_comb_internal);
RT_COMB_INTERNAL_INIT(combp);
combp->region_flag = 0;
BU_GET(tp, union tree);
RT_TREE_INIT(tp);
tp->tr_l.tl_op = OP_SOLID;
tp->tr_l.tl_name = bu_strdup("dummy");
tp->tr_l.tl_mat = (matp_t)NULL;
tp->tr_a.tu_stp = rt_find_solid(rtip, dp->d_namep);
combp->tree = tp;
/* A primitive uses its prepared solid directly. */
RT_TREE_INIT(&primitive_tree);
primitive_tree.tr_l.tl_op = OP_SOLID;
primitive_tree.tr_a.tu_stp = rt_find_solid(rtip, dp->d_namep);
tp = &primitive_tree;
}
RT_CK_COMB(combp);
if (rt_traverse_tree(rtip, combp->tree, tree_min, tree_max)) {
bu_log("rt_bound_internal: rt_bound_tree() failed\n");
if (rt_traverse_tree(rtip, tp, tree_min, tree_max)) {
bu_log("rt_bound_internal: tree traversal failed\n");
rt_db_free_internal(&intern);
rt_i_destroy(rtip);
return -1;
+2 -2
View File
@@ -111,7 +111,7 @@ hlbvh_init_pool(size_t n_primatives) {
}
/* utility functions */
static inline uint32_t left_shift3(uint32_t x)
static uint32_t left_shift3(uint32_t x)
{
BU_ASSERT(x <= (1 << 10));
if (x == (1 << 10)) --x;
@@ -299,7 +299,7 @@ maximum_extent(const fastf_t b[6])
return 2;
}
static inline int
static int
pred(const struct bvh_build_node *node, fastf_t *centroid_bounds,
uint8_t dim, long min_cost_split_bucket)
{
+1 -1
View File
@@ -730,7 +730,7 @@ bot_makesegs_specific(hit_da *hits,
/* r_min can describe a spatial partition cell that begins inside this
* solid. Start BVH traversal just before the ray enters the BoT's own
* root box so entrance triangles behind the ray origin remain visible. */
static inline void
static void
bot_backed_origin(point_t b_pt, const struct xray *rp, const struct bvh_flat_node *root,
const vect_t inv_dir, const struct soltab *stp)
{
+1 -1
View File
@@ -517,7 +517,7 @@ rt_metaball_vshot(struct soltab *stp[], struct xray *rp[], struct seg *segp, int
}
inline void
static void
rt_metaball_norm_internal(vect_t *n, point_t *p, struct rt_metaball_internal *mb)
{
struct wdb_metaball_pnt *mbpt;
-1
View File
@@ -27,7 +27,6 @@ __BEGIN_DECLS
int rt_metaball_bbox(struct rt_db_internal *ip, point_t *min, point_t *max, const struct bn_tol *tol);
fastf_t rt_metaball_get_bounding_sphere(point_t *center, fastf_t threshold, struct rt_metaball_internal *mb);
int rt_metaball_find_intersection(point_t *intersect, const struct rt_metaball_internal *mb, const point_t *a, const point_t *b, fastf_t step, const fastf_t finalstep);
void rt_metaball_norm_internal(vect_t *n, point_t *p, struct rt_metaball_internal *mb);
__END_DECLS
#endif /* LIBRT_PRIMITIVES_METABALL_METABALL_H */
+1 -1
View File
@@ -33,7 +33,7 @@
#define PART_SPHERE_HEIGHT_RATIO 0.001
#define PART_CYLINDER_RADIUS_RATIO 0.001
static inline int
static int
part_update_type(struct rt_part_internal *part)
{
if (!part || !isfinite(part->part_vrad) ||
+20 -14
View File
@@ -72,10 +72,15 @@ brlcad_add_test(NAME rt_tree_prune COMMAND rt_tree_prune)
brlcad_addexec(rt_reprep_prune reprep_prune.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_addexec(rt_rtcmp_capture rtcmp_capture.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_rtcmp_capture COMMAND rt_rtcmp_capture)
brlcad_add_test(NAME rt_rtcmp_capture_primitives COMMAND rt_rtcmp_capture primitives)
brlcad_add_test(NAME rt_reprep_prune COMMAND rt_reprep_prune)
# Note - this feature is not available in a BRLCAD_ENABLE_RUNTIME_DEBUG=OFF
# build, since it has an unavoidable performance overhead in the hot loop to
# check if the setting is enabled.
if (BRLCAD_ENABLE_RUNTIME_DEBUG)
brlcad_addexec(rt_rtcmp_capture rtcmp_capture.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_rtcmp_capture COMMAND rt_rtcmp_capture)
brlcad_add_test(NAME rt_rtcmp_capture_primitives COMMAND rt_rtcmp_capture primitives)
brlcad_add_test(NAME rt_reprep_prune COMMAND rt_reprep_prune)
endif()
brlcad_addexec(rt_submodel_xform submodel_xform.c "librt;libwdb;libbu;${M_LIBRARY}" TEST)
brlcad_add_test(NAME rt_submodel_xform COMMAND rt_submodel_xform)
@@ -212,36 +217,37 @@ brlcad_add_test(NAME rt_metaball_adjust COMMAND rt_metaball_adjust)
set(
distcheck_files
CMakeLists.txt
ars_inside.c
arb_intersect.g
ars_inside.c
binary_attribute.c
binunif.c
brep_io.cpp
bound_internal.c
brep_boolean_tests.g
brep_io.cpp
crofton.c
cyclic_tests.g
datum_annot_semantics.c
db_name.c
default_object.c
datum_annot_semantics.c
dsp_raytrace_check.c
dsp_sa_vol_check.c
primitive_brep.cpp
reprep_prune.c
brep_boolean_tests.g
cyclic_tests.g
extreme_ssi_test.g
invalid_inmem.c
lcomb_large.c
matrix_tests.g
metaball_adjust.c
nurbs_surfaces.g
pnts_bounds.c
prim_tess.c
primitive_brep.cpp
reprep_prune.c
rt_datum.c
rt_perturb.c
rtcmp_capture.c
search_stress.c
static_init.cpp
sketch.g
static_init.cpp
submodel_xform.c
prim_tess.c
pnts_bounds.c
tess_timing.c
tree_prune.c
vds_tree.c
+63
View File
@@ -37,6 +37,7 @@ main(int UNUSED(argc), const char **argv)
point_t bmin, bmax;
point_t expected_min = {-1.0, -1.0, -1.0};
point_t expected_max = { 1.0, 1.0, 1.0};
mat_t translated;
int ret = 1;
bu_setprogname(argv[0]);
@@ -75,6 +76,68 @@ main(int UNUSED(argc), const char **argv)
if (!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
dp = db_lookup(dbip, "left.s", LOOKUP_QUIET);
if (dp == RT_DIR_NULL || rt_bound_internal(dbip, dp, bmin, bmax))
goto done;
if (!VNEAR_EQUAL(bmin, expected_min, BN_TOL_DIST) ||
!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
BU_LIST_INIT(&wm.l);
if (mk_comb(wdbp, "empty.c", &wm.l, 0, NULL, NULL, NULL,
0, 0, 0, 0, 0, 0, 0))
goto done;
dp = db_lookup(dbip, "empty.c", LOOKUP_QUIET);
if (dp == RT_DIR_NULL ||
rt_bound_internal(dbip, dp, bmin, bmax) == 0)
goto done;
BU_LIST_INIT(&wm.l);
if (!mk_addmember("empty.c", &wm.l, NULL, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "nested.c", &wm, 0, NULL, NULL, NULL, 0))
goto done;
dp = db_lookup(dbip, "nested.c", LOOKUP_QUIET);
if (dp == RT_DIR_NULL ||
rt_bound_internal(dbip, dp, bmin, bmax) == 0)
goto done;
MAT_IDN(translated);
MAT_DELTAS(translated, 20.0, 0.0, 0.0);
BU_LIST_INIT(&wm.l);
if (!mk_addmember("left.s", &wm.l, NULL, WMOP_UNION) ||
!mk_addmember("left.s", &wm.l, translated, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "instanced.r", &wm, 1, NULL, NULL, NULL, 0))
goto done;
dp = db_lookup(dbip, "instanced.r", LOOKUP_QUIET);
if (dp == RT_DIR_NULL || rt_bound_internal(dbip, dp, bmin, bmax))
goto done;
VSET(expected_max, 21.0, 1.0, 1.0);
if (!VNEAR_EQUAL(bmin, expected_min, BN_TOL_DIST) ||
!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
BU_LIST_INIT(&wm.l);
if (!mk_addmember("left.s", &wm.l, translated, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "child.c", &wm, 0, NULL, NULL, NULL, 0))
goto done;
MAT_DELTAS(translated, 10.0, 0.0, 0.0);
BU_LIST_INIT(&wm.l);
if (!mk_addmember("child.c", &wm.l, translated, WMOP_UNION))
goto done;
if (mk_lcomb(wdbp, "parent.r", &wm, 1, NULL, NULL, NULL, 0))
goto done;
dp = db_lookup(dbip, "parent.r", LOOKUP_QUIET);
if (dp == RT_DIR_NULL || rt_bound_internal(dbip, dp, bmin, bmax))
goto done;
VSET(expected_min, 29.0, -1.0, -1.0);
VSET(expected_max, 31.0, 1.0, 1.0);
if (!VNEAR_EQUAL(bmin, expected_min, BN_TOL_DIST) ||
!VNEAR_EQUAL(bmax, expected_max, BN_TOL_DIST))
goto done;
ret = 0;
done:
+64
View File
@@ -85,6 +85,69 @@ proc exercise_object_lifecycle {class object args} {
}
}
proc exercise_panedwindow_bindings {} {
set pane_size 200
set drag_distance 20
set native_motion_binding [bind Panedwindow <Motion>]
toplevel .panes
foreach orientation {horizontal vertical} {
set pane .panes.$orientation
iwidgets::panedwindow $pane -orient $orientation \
-width $pane_size -height $pane_size -showhandle 0
$pane add first -minimum 0 -margin 0
$pane add second -minimum 0 -margin 0
pack $pane -side left -fill both -expand yes
}
panedwindow .panes.native -width $pane_size -height $pane_size
foreach name {first second} {
frame .panes.native.$name
.panes.native add .panes.native.$name
}
pack .panes.native -side left -fill both -expand yes
update
# Hull events must not invoke native Tk handlers on Iwidgets objects.
foreach pane {.panes.horizontal .panes.vertical .panes.native} {
foreach event {
<Motion> <Leave>
<ButtonPress-1> <B1-Motion> <ButtonRelease-1>
<ButtonPress-2> <B2-Motion> <ButtonRelease-2>
} {
event generate $pane $event -x 1 -y 1
}
update
assert_equal "$pane pointer events" $::background_errors {}
}
assert_equal "native panedwindow motion binding" \
[bind Panedwindow <Motion>] $native_motion_binding
# Removing the hull's class tag must retain Iwidgets' sash dragging.
foreach orientation {horizontal vertical} {
set pane .panes.$orientation
set before [$pane fraction]
set sash [$pane component sash1]
set x 0
set y 0
if {$orientation eq "horizontal"} {
set y $drag_distance
} else {
set x $drag_distance
}
event generate $sash <ButtonPress-1> -x 0 -y 0
event generate $sash <B1-Motion> -x $x -y $y
event generate $sash <B1-ButtonRelease-1> -x $x -y $y
update
if {[lindex [$pane fraction] 0] <= [lindex $before 0]} {
fail "$orientation panedwindow sash did not move"
}
assert_equal "$orientation sash events" $::background_errors {}
}
destroy .panes
update
}
proc require_at_least {package minimum} {
set version [package require $package $minimum]
if {[package vcompare $version $minimum] < 0} {
@@ -102,6 +165,7 @@ set tk_version [require_at_least Tk 8.6]
set itcl_version [require_at_least Itcl 4.3.0]
set itk_version [require_at_least Itk 4.2.3]
set iwidgets_version [require_at_least Iwidgets 4.1.1]
exercise_panedwindow_bindings
interp alias {} Hierarchy {} ::iwidgets::Hierarchy
interp alias {} scrolledlistbox {} ::iwidgets::scrolledlistbox
+1 -1
View File
@@ -337,7 +337,7 @@ main(int ac, char *av[])
unsigned char ground_rgb[3];
unsigned char light_rgb[3];
double pitch_r[MAX_GEARS];
double pitch_r[MAX_GEARS] = {0.0};
double center_x[MAX_GEARS];
double total_span;
double max_outer;
+29 -10
View File
@@ -226,7 +226,7 @@ package provide Archer 1.0
method buildViewAxesPreferences {}
method doAboutArcher {}
method doarcherHelp {}
method handleMap {}
method handleMap {_window}
method handleBindingModeChange {_name1 _name2 _op}
method overrideBindingMode {_mode}
method updateOverrideBindingMode {_keysym}
@@ -427,6 +427,7 @@ package provide Archer 1.0
private {
variable mInstanceInit 1
variable mWindowMapped 0
}
}
@@ -528,7 +529,7 @@ package provide Archer 1.0
}
# resize and position window after it's drawn
bind [namespace tail $this] <Map> [::itcl::code $this handleMap]
bind [namespace tail $this] <Map> [::itcl::code $this handleMap %W]
}
@@ -4263,18 +4264,26 @@ proc title_node_handler {node} {
}
::itcl::body Archer::handleMap {} {
if {$mWindowGeometry != ""} {
after idle "wm geometry [namespace tail $this] $mWindowGeometry"
} else {
after idle "wm geometry [namespace tail $this] $itk_option(-geometry)"
::itcl::body Archer::handleMap {_window} {
# Child windows also dispatch Map events through the toplevel's tag.
if {$_window ne [namespace tail $this]} {
return
}
set mWindowMapped 1
set geometry $mWindowGeometry
if {$geometry eq ""} {
set geometry $itk_option(-geometry)
}
if {$geometry ne ""} {
after idle [list wm geometry $_window $geometry]
}
if {$mSeparateCommandWindow && $mCmdWindowGeometry != ""} {
after idle [::itcl::code wm geometry $itk_component(sepcmdT) $mCmdWindowGeometry]
after idle [list wm geometry $itk_component(sepcmdT) $mCmdWindowGeometry]
}
bind [namespace tail $this] <Map> {}
bind $_window <Map> {}
}
::itcl::body Archer::handleDisplayEscape {_dm} {
@@ -9351,7 +9360,17 @@ proc title_node_handler {node} {
puts $_pfile "set mVPaneToggle3 $mVPaneToggle3"
puts $_pfile "set mVPaneToggle5 $mVPaneToggle5"
puts $_pfile "set mWindowGeometry [winfo geometry [namespace tail $this]]"
# Before the first map, the hull's size only reflects unfinished layout.
if {$mWindowMapped} {
if {$ArcherCore::inheritFromToplevel} {
set mWindowGeometry [wm geometry [namespace tail $this]]
} else {
set mWindowGeometry [winfo geometry [namespace tail $this]]
}
}
if {$mWindowGeometry ne ""} {
puts $_pfile [list set mWindowGeometry $mWindowGeometry]
}
if {$mSeparateCommandWindow} {
puts $_pfile "set mCmdWindowGeometry [winfo geometry $itk_component(sepcmdT)]"
+1 -2
View File
@@ -165,8 +165,7 @@ proc secErrorDialog {root title text default args} {
update idletasks
# First frame consists of error icon and message
frame $base.frame\#1 \
-class Panedwindow
frame $base.frame\#1
pack $base.frame\#1 -fill x -anchor w
canvas $base.frame\#1.bitmap \
+2 -1
View File
@@ -47,10 +47,11 @@ if { [info exists tk_strictMotif] == 0 } {
# MGED html manual directory search order precedence should be:
# MGED_HTML_DIR
# [bu_dir data]/html/manuals/mged
# [bu_dir doc]/html/manuals/mged
if ![info exists mged_default(html_dir)] {
set mged_default(html_dir) [file normalize [file join [bu_dir doc] html manuals mged]]
set mged_default(html_dir) [file normalize [file join [bu_dir data] html manuals mged]]
if {![file exists $mged_default(html_dir)]} {
set mged_default(html_dir) [file normalize [file join [bu_dir doc] html manuals mged]]
}