Merge branch 'solvespace:master' into python
|
@ -1,27 +1,52 @@
|
||||||
#!/bin/sh -xe
|
#!/bin/sh -xe
|
||||||
|
|
||||||
mkdir build || true
|
ENABLE_SANITIZERS="OFF"
|
||||||
cd build
|
|
||||||
|
|
||||||
OSX_TARGET="10.9"
|
|
||||||
|
|
||||||
if [ "$1" = "release" ]; then
|
if [ "$1" = "release" ]; then
|
||||||
BUILD_TYPE=RelWithDebInfo
|
BUILD_TYPE="RelWithDebInfo"
|
||||||
cmake \
|
ENABLE_LTO="ON"
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_TARGET}" \
|
|
||||||
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
|
|
||||||
-DENABLE_OPENMP="ON" \
|
|
||||||
-DENABLE_LTO="ON" \
|
|
||||||
..
|
|
||||||
else
|
else
|
||||||
BUILD_TYPE=Debug
|
BUILD_TYPE="Debug"
|
||||||
cmake \
|
ENABLE_LTO="OFF"
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_TARGET}" \
|
|
||||||
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
|
|
||||||
-DENABLE_OPENMP="ON" \
|
|
||||||
-DENABLE_SANITIZERS="ON" \
|
|
||||||
..
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cmake --build . --config "${BUILD_TYPE}" -- -j$(nproc)
|
# this is an option for our Github CI only, since it doesn't have a macos arm64 image yet
|
||||||
make -j$(nproc) test_solvespace
|
CMAKE_GENERATOR="Unix Makefiles"
|
||||||
|
CMAKE_PREFIX_PATH=""
|
||||||
|
if [ "$2" = "arm64" ]; then
|
||||||
|
OSX_ARCHITECTURE="arm64"
|
||||||
|
CMAKE_PREFIX_PATH=$(find /tmp/libomp-arm64/libomp -depth 1)
|
||||||
|
git apply cmake/libpng-macos-arm64.patch || echo "Could not apply patch, probably already patched..."
|
||||||
|
mkdir build-arm64 || true
|
||||||
|
cd build-arm64
|
||||||
|
elif [ "$2" = "x86_64" ]; then
|
||||||
|
OSX_ARCHITECTURE="x86_64"
|
||||||
|
CMAKE_PREFIX_PATH=$(find /tmp/libomp-x86_64/libomp -depth 1)
|
||||||
|
mkdir build || true
|
||||||
|
cd build
|
||||||
|
else
|
||||||
|
mkdir build || true
|
||||||
|
cd build
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$3" = "xcode" ]; then
|
||||||
|
CMAKE_GENERATOR="Xcode"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cmake \
|
||||||
|
-G "${CMAKE_GENERATOR}" \
|
||||||
|
-D CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}" \
|
||||||
|
-D CMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURE}" \
|
||||||
|
-D CMAKE_BUILD_TYPE="${BUILD_TYPE}" \
|
||||||
|
-D ENABLE_OPENMP="ON" \
|
||||||
|
-D ENABLE_SANITIZERS="${ENABLE_SANITIZERS}" \
|
||||||
|
-D ENABLE_LTO="${ENABLE_LTO}" \
|
||||||
|
..
|
||||||
|
|
||||||
|
if [ "$3" = "xcode" ]; then
|
||||||
|
open solvespace.xcodeproj
|
||||||
|
else
|
||||||
|
cmake --build . --config "${BUILD_TYPE}" -j$(sysctl -n hw.logicalcpu)
|
||||||
|
if [ $(uname -m) = "$2" ]; then
|
||||||
|
make -j$(sysctl -n hw.logicalcpu) test_solvespace
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -1,4 +1,16 @@
|
||||||
#!/bin/sh -xe
|
#!/bin/sh -xe
|
||||||
|
|
||||||
brew install libomp
|
if [ "$1" = "ci" ]; then
|
||||||
git submodule update --init
|
armloc=$(brew fetch --bottle-tag=arm64_big_sur libomp | grep -i downloaded | grep tar.gz | cut -f2 -d:)
|
||||||
|
x64loc=$(brew fetch --bottle-tag=big_sur libomp | grep -i downloaded | grep tar.gz | cut -f2 -d:)
|
||||||
|
cp $armloc /tmp/libomp-arm64.tar.gz
|
||||||
|
mkdir /tmp/libomp-arm64 || true
|
||||||
|
tar -xzvf /tmp/libomp-arm64.tar.gz -C /tmp/libomp-arm64
|
||||||
|
cp $x64loc /tmp/libomp-x86_64.tar.gz
|
||||||
|
mkdir /tmp/libomp-x86_64 || true
|
||||||
|
tar -xzvf /tmp/libomp-x86_64.tar.gz -C /tmp/libomp-x86_64
|
||||||
|
else
|
||||||
|
brew install libomp
|
||||||
|
fi
|
||||||
|
|
||||||
|
git submodule update --init extlib/cairo extlib/freetype extlib/libdxfrw extlib/libpng extlib/mimalloc extlib/pixman extlib/zlib
|
||||||
|
|
|
@ -1,8 +1,29 @@
|
||||||
#!/bin/bash -xe
|
#!/bin/bash -xe
|
||||||
|
|
||||||
|
lipo \
|
||||||
|
-create \
|
||||||
|
build/bin/SolveSpace.app/Contents/Resources/libomp.dylib \
|
||||||
|
build-arm64/bin/SolveSpace.app/Contents/Resources/libomp.dylib \
|
||||||
|
-output \
|
||||||
|
build/bin/SolveSpace.app/Contents/Resources/libomp.dylib
|
||||||
|
|
||||||
|
lipo \
|
||||||
|
-create \
|
||||||
|
build/bin/SolveSpace.app/Contents/MacOS/SolveSpace \
|
||||||
|
build-arm64/bin/SolveSpace.app/Contents/MacOS/SolveSpace \
|
||||||
|
-output \
|
||||||
|
build/bin/SolveSpace.app/Contents/MacOS/SolveSpace
|
||||||
|
|
||||||
|
lipo \
|
||||||
|
-create \
|
||||||
|
build/bin/SolveSpace.app/Contents/MacOS/solvespace-cli \
|
||||||
|
build-arm64/bin/SolveSpace.app/Contents/MacOS/solvespace-cli \
|
||||||
|
-output \
|
||||||
|
build/bin/SolveSpace.app/Contents/MacOS/solvespace-cli
|
||||||
|
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
openmp="bin/SolveSpace.app/Contents/Resources/lib/libomp.dylib"
|
openmp="bin/SolveSpace.app/Contents/Resources/libomp.dylib"
|
||||||
app="bin/SolveSpace.app"
|
app="bin/SolveSpace.app"
|
||||||
dmg="bin/SolveSpace.dmg"
|
dmg="bin/SolveSpace.dmg"
|
||||||
bundle_id="com.solvespace.solvespace"
|
bundle_id="com.solvespace.solvespace"
|
||||||
|
|
|
@ -9,17 +9,27 @@ on:
|
||||||
- created
|
- created
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
cancel_previous_runs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Cancel Previous Runs
|
||||||
|
steps:
|
||||||
|
- uses: styfle/cancel-workflow-action@0.8.0
|
||||||
|
with:
|
||||||
|
access_token: ${{ github.token }}
|
||||||
|
|
||||||
test_ubuntu:
|
test_ubuntu:
|
||||||
runs-on: ubuntu-18.04
|
needs: [cancel_previous_runs]
|
||||||
name: Test Ubuntu
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
name: Test Ubuntu
|
||||||
- uses: actions/checkout@v2
|
steps:
|
||||||
- name: Install Dependencies
|
- uses: actions/checkout@v2
|
||||||
run: .github/scripts/install-ubuntu.sh
|
- name: Install Dependencies
|
||||||
- name: Build & Test
|
run: .github/scripts/install-ubuntu.sh
|
||||||
run: .github/scripts/build-ubuntu.sh
|
- name: Build & Test
|
||||||
|
run: .github/scripts/build-ubuntu.sh
|
||||||
|
|
||||||
test_windows:
|
test_windows:
|
||||||
|
needs: [cancel_previous_runs]
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
name: Test Windows
|
name: Test Windows
|
||||||
steps:
|
steps:
|
||||||
|
@ -32,14 +42,15 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test_macos:
|
test_macos:
|
||||||
runs-on: macos-latest
|
needs: [cancel_previous_runs]
|
||||||
|
runs-on: macos-10.15
|
||||||
name: Test macOS
|
name: Test macOS
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: .github/scripts/install-macos.sh
|
run: .github/scripts/install-macos.sh ci
|
||||||
- name: Build & Test
|
- name: Build & Test
|
||||||
run: .github/scripts/build-macos.sh
|
run: .github/scripts/build-macos.sh debug arm64 && .github/scripts/build-macos.sh debug x86_64
|
||||||
|
|
||||||
build_release_windows:
|
build_release_windows:
|
||||||
needs: [test_ubuntu, test_windows, test_macos]
|
needs: [test_ubuntu, test_windows, test_macos]
|
||||||
|
@ -80,13 +91,13 @@ jobs:
|
||||||
build_release_macos:
|
build_release_macos:
|
||||||
needs: [test_ubuntu, test_windows, test_macos]
|
needs: [test_ubuntu, test_windows, test_macos]
|
||||||
name: Build Release macOS
|
name: Build Release macOS
|
||||||
runs-on: macos-latest
|
runs-on: macos-10.15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: .github/scripts/install-macos.sh
|
run: .github/scripts/install-macos.sh ci
|
||||||
- name: Build & Test
|
- name: Build & Test
|
||||||
run: .github/scripts/build-macos.sh release
|
run: .github/scripts/build-macos.sh release arm64 && .github/scripts/build-macos.sh release x86_64
|
||||||
- name: Sign Build
|
- name: Sign Build
|
||||||
run: .github/scripts/sign-macos.sh
|
run: .github/scripts/sign-macos.sh
|
||||||
env:
|
env:
|
||||||
|
@ -100,13 +111,15 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
path: build/bin/SolveSpace.dmg
|
path: build/bin/SolveSpace.dmg
|
||||||
|
|
||||||
deploy_snap_amd64:
|
deploy_snap_amd64:
|
||||||
needs: [test_ubuntu, test_windows, test_macos]
|
needs: [test_ubuntu, test_windows, test_macos]
|
||||||
name: Deploy AMD64 Snap
|
name: Deploy AMD64 Snap
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- name: Fetch Tags
|
||||||
|
run: git fetch --force --tags
|
||||||
- name: Set Up Source
|
- name: Set Up Source
|
||||||
run: rsync --filter=":- .gitignore" -r ./ pkg/snap/solvespace-snap-src
|
run: rsync --filter=":- .gitignore" -r ./ pkg/snap/solvespace-snap-src
|
||||||
- name: Build Snap
|
- name: Build Snap
|
||||||
|
@ -128,14 +141,18 @@ jobs:
|
||||||
store_login: ${{ secrets.SNAPSTORE_LOGIN }}
|
store_login: ${{ secrets.SNAPSTORE_LOGIN }}
|
||||||
snap: ${{ steps.build.outputs.snap }}
|
snap: ${{ steps.build.outputs.snap }}
|
||||||
release: edge,beta
|
release: edge,beta
|
||||||
|
|
||||||
deploy_snap_arm64:
|
deploy_snap_arm64:
|
||||||
needs: [test_ubuntu, test_windows, test_macos]
|
needs: [test_ubuntu, test_windows, test_macos]
|
||||||
name: Deploy ARM64 Snap
|
name: Deploy ARM64 Snap
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: docker/setup-qemu-action@v1
|
- uses: docker/setup-qemu-action@v1
|
||||||
|
with:
|
||||||
|
image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- name: Fetch Tags
|
||||||
|
run: git fetch --force --tags
|
||||||
- name: Set Up Source
|
- name: Set Up Source
|
||||||
run: rsync --filter=":- .gitignore" -r ./ pkg/snap/solvespace-snap-src
|
run: rsync --filter=":- .gitignore" -r ./ pkg/snap/solvespace-snap-src
|
||||||
- name: Build Snap
|
- name: Build Snap
|
||||||
|
@ -158,38 +175,11 @@ jobs:
|
||||||
store_login: ${{ secrets.SNAPSTORE_LOGIN }}
|
store_login: ${{ secrets.SNAPSTORE_LOGIN }}
|
||||||
snap: ${{ steps.build.outputs.snap }}
|
snap: ${{ steps.build.outputs.snap }}
|
||||||
release: edge,beta
|
release: edge,beta
|
||||||
|
|
||||||
update_edge_release:
|
|
||||||
name: Update Edge Release
|
|
||||||
needs: [build_release_windows, build_release_windows_openmp, build_release_macos]
|
|
||||||
if: always() && github.event_name == 'push'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
steps:
|
|
||||||
- name: Delete Old Edge Release
|
|
||||||
uses: dev-drprasad/delete-tag-and-release@v0.1.2
|
|
||||||
with:
|
|
||||||
delete_release: true
|
|
||||||
tag_name: edge
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Create New Edge Release
|
|
||||||
id: create_release
|
|
||||||
uses: actions/create-release@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
tag_name: edge
|
|
||||||
release_name: Edge
|
|
||||||
prerelease: true
|
|
||||||
draft: false
|
|
||||||
body: ${{ github.event.head_commit.message }}
|
|
||||||
|
|
||||||
upload_release_assets:
|
upload_release_assets:
|
||||||
name: Upload Release Assets
|
name: Upload Release Assets
|
||||||
needs: [build_release_windows, build_release_windows_openmp, build_release_macos, update_edge_release]
|
needs: [build_release_windows, build_release_windows_openmp, build_release_macos]
|
||||||
if: always()
|
if: "!cancelled() && github.event_name == 'release'"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Download All Workflow Artifacts
|
- name: Download All Workflow Artifacts
|
||||||
|
@ -197,15 +187,9 @@ jobs:
|
||||||
- name: Get Release Upload URL
|
- name: Get Release Upload URL
|
||||||
id: get_upload_url
|
id: get_upload_url
|
||||||
env:
|
env:
|
||||||
event_name: ${{ github.event_name }}
|
|
||||||
event: ${{ toJson(github.event) }}
|
event: ${{ toJson(github.event) }}
|
||||||
edge_upload_url: ${{ needs.update_edge_release.outputs.upload_url }}
|
|
||||||
run: |
|
run: |
|
||||||
if [ "$event_name" = "release" ]; then
|
upload_url=$(echo "$event" | jq -r ".release.upload_url")
|
||||||
upload_url=$(echo "$event" | jq -r ".release.upload_url")
|
|
||||||
else
|
|
||||||
upload_url="$edge_upload_url"
|
|
||||||
fi
|
|
||||||
echo "::set-output name=upload_url::$upload_url"
|
echo "::set-output name=upload_url::$upload_url"
|
||||||
echo "Upload URL: $upload_url"
|
echo "Upload URL: $upload_url"
|
||||||
- name: Upload solvespace.exe
|
- name: Upload solvespace.exe
|
||||||
|
|
|
@ -34,11 +34,11 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
test_macos:
|
test_macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-10.15
|
||||||
name: Test macOS
|
name: Test macOS
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: .github/scripts/install-macos.sh
|
run: .github/scripts/install-macos.sh ci
|
||||||
- name: Build & Test
|
- name: Build & Test
|
||||||
run: .github/scripts/build-macos.sh
|
run: .github/scripts/build-macos.sh debug arm64 && .github/scripts/build-macos.sh debug x86_64
|
||||||
|
|
506
CHANGELOG.md
|
@ -1,284 +1,330 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
3.x - since the 3.0 release, only available in edge builds
|
||||||
|
---
|
||||||
|
|
||||||
|
Constraints:
|
||||||
|
|
||||||
|
* Arcs length ratio and difference.
|
||||||
|
* Arc & Line length ratio and difference.
|
||||||
|
* Allow comments to be associated with point entities.
|
||||||
|
|
||||||
|
Sketching:
|
||||||
|
|
||||||
|
* Support for pan, zoom and rotate trackpad gestures on macOS
|
||||||
|
* Add "exploded view" to sketches via "\\" key. Shows sketch elements separated
|
||||||
|
by a configurable distance perpendicular to the sketch plane.
|
||||||
|
* Added Feet-Inches as a unit of measure. Inputs are still in inches.
|
||||||
|
But the display shows feet, inches, and fraction of an inch.
|
||||||
|
* Added an optional "pitch" parameter to helix extrusions (in the text window)
|
||||||
|
* Allow use of Point & Normal to define "sketch-in-new-workplane".
|
||||||
|
* Update "Property Browser" live while dragging the sketch.
|
||||||
|
|
||||||
|
MISC:
|
||||||
|
|
||||||
|
* Add a "∆" suffix to groups which have "force to triangle mesh" ticked
|
||||||
|
* Gray the group name in the text window for groups with suppressed solid model.
|
||||||
|
* Added the ability to Link STL files.
|
||||||
|
* When linking circuit boards (IDF .emn files) show keepout regions as construction entities.
|
||||||
|
|
||||||
|
Performance:
|
||||||
|
|
||||||
|
* More changes to the ID list implementation.
|
||||||
|
|
||||||
3.0
|
3.0
|
||||||
---
|
---
|
||||||
|
|
||||||
New sketch features:
|
New sketch features:
|
||||||
* New intersection boolean operation for solid models.
|
|
||||||
* New groups, revolution and helical extrusion.
|
* New intersection boolean operation for solid models.
|
||||||
* Extrude, lathe, translate and rotate groups can use the "assembly"
|
* New groups, revolution and helical extrusion.
|
||||||
boolean operation, to increase performance.
|
* Extrude, lathe, translate and rotate groups can use the "assembly"
|
||||||
* The solid model of extrude and lathe groups can be suppressed,
|
boolean operation, to increase performance.
|
||||||
for splitting a single model in multiple parts to export,
|
* The solid model of extrude and lathe groups can be suppressed,
|
||||||
or if only the generated entities are desired, without the mesh.
|
for splitting a single model in multiple parts to export,
|
||||||
* Translate and rotate groups can create n-dimensional arrays using
|
or if only the generated entities are desired, without the mesh.
|
||||||
the "difference" and "assembly" boolean operations.
|
* Translate and rotate groups can create n-dimensional arrays using
|
||||||
* A new sketch in workplane group can be created based on existing workplane.
|
the "difference" and "assembly" boolean operations.
|
||||||
* TTF text request has two additional points on the right side, which allow
|
* A new sketch in workplane group can be created based on existing workplane.
|
||||||
constraining the width of text.
|
* TTF text request has two additional points on the right side, which allow
|
||||||
* Image requests can now be created, similar to TTF text requests.
|
constraining the width of text.
|
||||||
This replaces the "style → background image" feature.
|
* Image requests can now be created, similar to TTF text requests.
|
||||||
* Irrelevant points (e.g. arc center point) are not counted when estimating
|
This replaces the "style → background image" feature.
|
||||||
the bounding box used to compute chord tolerance.
|
* Irrelevant points (e.g. arc center point) are not counted when estimating
|
||||||
* When adding a constraint which has a label and is redundant with another
|
the bounding box used to compute chord tolerance.
|
||||||
constraint, the constraint is added as a reference, avoiding an error.
|
* When adding a constraint which has a label and is redundant with another
|
||||||
* Datum points can be copied and pasted.
|
constraint, the constraint is added as a reference, avoiding an error.
|
||||||
* "Split Curves at Intersection" can now split curves at point lying on curve,
|
* Datum points can be copied and pasted.
|
||||||
not just at intersection of two curves.
|
* "Split Curves at Intersection" can now split curves at point lying on curve,
|
||||||
* Property browser now shows amount of degrees of freedom in group list.
|
not just at intersection of two curves.
|
||||||
It also shows a yellow "err" if the sketch has problems (e.g. self
|
* Property browser now shows amount of degrees of freedom in group list.
|
||||||
intersecting) that would propagate in subsequent groups.
|
It also shows a yellow "err" if the sketch has problems (e.g. self
|
||||||
* It is now possible to press "g" to toggle construction on new objects while
|
intersecting) that would propagate in subsequent groups.
|
||||||
they are still being drawn.
|
* It is now possible to press "g" to toggle construction on new objects while
|
||||||
|
they are still being drawn.
|
||||||
|
* Allow right click to end sketching of all entities.
|
||||||
|
|
||||||
New constraint features:
|
New constraint features:
|
||||||
* When dragging an arc or rectangle point, it will be automatically
|
|
||||||
constrained to other points with a click.
|
* When dragging an arc or rectangle point, it will be automatically
|
||||||
* When selecting a constraint, the requests it constraints can be selected
|
constrained to other points with a click.
|
||||||
in the text window.
|
* When selecting a constraint, the requests it constraints can be selected
|
||||||
* When selecting an entity, the constraints applied to it can be selected
|
in the text window.
|
||||||
in the text window.
|
* When selecting an entity, the constraints applied to it can be selected
|
||||||
* Distance constraint labels can now be formatted to use SI prefixes.
|
in the text window.
|
||||||
Values are edited in the configured unit regardless of label format.
|
* Distance constraint labels can now be formatted to use SI prefixes.
|
||||||
* When creating a constraint, if an exactly identical constraint already
|
Values are edited in the configured unit regardless of label format.
|
||||||
exists, it is now selected instead of adding a redundant constraint.
|
* When creating a constraint, if an exactly identical constraint already
|
||||||
* It is now possible to turn off automatic creation of horizontal/vertical
|
exists, it is now selected instead of adding a redundant constraint.
|
||||||
constraints on line segments.
|
* It is now possible to turn off automatic creation of horizontal/vertical
|
||||||
* Automatic creation of constraints no longer happens if the constraint
|
constraints on line segments.
|
||||||
would have been redundant with other ones.
|
* Automatic creation of constraints no longer happens if the constraint
|
||||||
* New option to open the constraint editor for newly created constraints
|
would have been redundant with other ones.
|
||||||
with a value.
|
* New option to open the constraint editor for newly created constraints
|
||||||
* New "redundant constraint timeout (in ms)" option to prevent UI freeze
|
with a value.
|
||||||
when looking for redundant constraints.
|
* New "redundant constraint timeout (in ms)" option to prevent UI freeze
|
||||||
* Swap vertical and horizontal constraints when pasting rotated by 90/270
|
when looking for redundant constraints.
|
||||||
degrees.
|
* Swap vertical and horizontal constraints when pasting rotated by 90/270
|
||||||
|
degrees.
|
||||||
|
|
||||||
New export/import features:
|
New export/import features:
|
||||||
* Link IDF circuit boards in an assembly (.emn files)
|
|
||||||
* Three.js: allow configuring projection for exported model, and initially
|
* Link IDF circuit boards in an assembly (.emn files)
|
||||||
use the current viewport projection.
|
* Three.js: allow configuring projection for exported model, and initially
|
||||||
* Wavefront OBJ: a material file is exported alongside the model, containing
|
use the current viewport projection.
|
||||||
mesh color information.
|
* Wavefront OBJ: a material file is exported alongside the model, containing
|
||||||
* DXF/DWG: 3D DXF files are imported as construction entities, in 3d.
|
mesh color information.
|
||||||
* [ADDED 2019-02-25](https://github.com/solvespace/solvespace/pull/384) and [REMOVED 2020-11-13](https://github.com/solvespace/solvespace/issues/795):
|
* DXF/DWG: 3D DXF files are imported as construction entities, in 3d.
|
||||||
Q3D: [Q3D](https://github.com/q3k/q3d/) triangle meshes can now be
|
* VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
|
||||||
exported. This format allows to easily hack on triangle mesh data created
|
* Export 2d section: custom styled entities that lie in the same
|
||||||
in SolveSpace, supports colour information and is more space efficient than
|
plane as the exported section are included.
|
||||||
most other formats.
|
* Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
|
||||||
* VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
|
* STEP export includes object colors and transparency.
|
||||||
* Export 2d section: custom styled entities that lie in the same
|
* Default "line styles" have a new "export these objects" option.
|
||||||
plane as the exported section are included.
|
|
||||||
* Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
|
|
||||||
* STEP export includes object colors and transparency.
|
|
||||||
|
|
||||||
New rendering features:
|
New rendering features:
|
||||||
* The "Show/hide hidden lines" button is now a tri-state button that allows
|
|
||||||
showing all lines (on top of shaded mesh), stippling occluded lines
|
* The "Show/hide hidden lines" button is now a tri-state button that allows
|
||||||
or not drawing them at all.
|
showing all lines (on top of shaded mesh), stippling occluded lines
|
||||||
* The "Show/hide outlines" button is now independent from "Show/hide edges".
|
or not drawing them at all.
|
||||||
* "View | Darken Inactive Solids" added. When turned off and a "sketch in plane"
|
* The "Show/hide outlines" button is now independent from "Show/hide edges".
|
||||||
group is active solids form previous groups will not be "darkened" (have the
|
* "View | Darken Inactive Solids" added. When turned off and a "sketch in plane"
|
||||||
s000d-#def-dim-solid style applied to them).
|
group is active solids form previous groups will not be "darkened" (have the
|
||||||
|
s000d-#def-dim-solid style applied to them).
|
||||||
|
|
||||||
New measurement/analysis features:
|
New measurement/analysis features:
|
||||||
* New choice for base unit, meters.
|
|
||||||
* New command for measuring total length of selected entities,
|
* New choice for base unit, meters.
|
||||||
"Analyze → Measure Perimeter".
|
* New command for measuring total length of selected entities,
|
||||||
* New command for measuring center of mass, with live updates as the sketch
|
"Analyze → Measure Perimeter".
|
||||||
changes, "Analyze → Center of Mass".
|
* New command for measuring center of mass, with live updates as the sketch
|
||||||
* New option for displaying areas of closed contours.
|
changes, "Analyze → Center of Mass".
|
||||||
* When calculating volume of the mesh, volume of the solid from the current
|
* New option for displaying areas of closed contours.
|
||||||
group is now shown alongside total volume of all solids.
|
* When calculating volume of the mesh, volume of the solid from the current
|
||||||
* When calculating area, and faces are selected, calculate area of those faces
|
group is now shown alongside total volume of all solids.
|
||||||
instead of the closed contour in the sketch.
|
* When calculating area, and faces are selected, calculate area of those faces
|
||||||
* When selecting a point and a line, projected distance to current
|
instead of the closed contour in the sketch.
|
||||||
workplane is displayed.
|
* When selecting a point and a line, projected distance to current
|
||||||
|
workplane is displayed.
|
||||||
|
|
||||||
Other new features:
|
Other new features:
|
||||||
* Improvements to the text window for selected entities and constraints.
|
|
||||||
* Ambient light source added in text window to allow flat shaded renderings.
|
* Improvements to the text window for selected entities and constraints.
|
||||||
* New command-line interface, for batch exporting and more.
|
* Ambient light source added in text window to allow flat shaded renderings.
|
||||||
* The graphical interface now supports HiDPI screens on every OS.
|
* New command-line interface, for batch exporting and more.
|
||||||
* New option to lock Z axis to be always vertical, like in SketchUp.
|
* The graphical interface now supports HiDPI screens on every OS.
|
||||||
* New button to hide all construction entities.
|
* New option to lock Z axis to be always vertical when rotating the view,
|
||||||
* New link to match the on-screen size of the sketch with its actual size,
|
a.k.a. "turntable navigation".
|
||||||
"view → set to full scale".
|
* New button to hide all construction entities.
|
||||||
* When zooming to fit, constraints are also considered.
|
* New link to match the on-screen size of the sketch with its actual size,
|
||||||
* Ctrl-clicking entities now deselects them, as the inverse of clicking.
|
"view → set to full scale".
|
||||||
* When clicking on an entity that shares a place with other entities,
|
* When zooming to fit, constraints are also considered.
|
||||||
the entity from the current group is selected.
|
* Ctrl-clicking entities now deselects them, as the inverse of clicking.
|
||||||
* When dragging an entity that shares a place with other entities,
|
* When clicking on an entity that shares a place with other entities,
|
||||||
the entity from a request is selected. For example, dragging a point on
|
the entity from the current group is selected.
|
||||||
a face of an extrusion coincident with the source sketch plane will
|
* When dragging an entity that shares a place with other entities,
|
||||||
drag the point from the source sketch.
|
the entity from a request is selected. For example, dragging a point on
|
||||||
* The default font for TTF text is now Bitstream Vera Sans, which is
|
a face of an extrusion coincident with the source sketch plane will
|
||||||
included in the resources such that it is available on any OS.
|
drag the point from the source sketch.
|
||||||
* In expressions, numbers can contain the digit group separator, "_".
|
* The default font for TTF text is now Bitstream Vera Sans, which is
|
||||||
* The "=" key is bound to "Zoom In", like "+" key.
|
included in the resources such that it is available on any OS.
|
||||||
* The numpad decimal separator key is bound to "." regardless of locale.
|
* In expressions, numbers can contain the digit group separator, "_".
|
||||||
* On Windows, full-screen mode is implemented.
|
* The "=" key is bound to "Zoom In", like "+" key.
|
||||||
* On Linux, native file chooser dialog can be used.
|
* The numpad decimal separator key is bound to "." regardless of locale.
|
||||||
* New edit menu items "Line Styles", "View Projection" and "Configuration"
|
* On Windows, full-screen mode is implemented.
|
||||||
that are shortcuts to the respective configuration screens.
|
* On Linux, native file chooser dialog can be used.
|
||||||
* New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes
|
* New edit menu items "Line Styles", "View Projection" and "Configuration"
|
||||||
to enable support for multi-threading and link-time optimization.
|
that are shortcuts to the respective configuration screens.
|
||||||
* "Shift+Scroll" for ten times finer zoom.
|
* New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes
|
||||||
* Chinese translation
|
to enable support for multi-threading and link-time optimization.
|
||||||
|
* "Shift+Scroll" for ten times finer zoom.
|
||||||
|
* Translations: Chinese, French, German, Russian, Turkish, Ukrainian.
|
||||||
|
|
||||||
Bugs fixed:
|
Bugs fixed:
|
||||||
* Fixed broken --view options for command line thumbnail image creation.
|
|
||||||
* Some errors in Triangulation of surfaces.
|
* Fixed broken --view options for command line thumbnail image creation.
|
||||||
* Some NURNS boolean operations that failed particularly on surfaces
|
* Some errors in Triangulation of surfaces.
|
||||||
created with Lathe, Revolve, or Helix.
|
* Some NURNS boolean operations that failed particularly on surfaces
|
||||||
* Segfault in Remove Spline Point context menu.
|
created with Lathe, Revolve, or Helix.
|
||||||
* A point in 3d constrained to any line whose length is free no longer
|
* Segfault in Remove Spline Point context menu.
|
||||||
causes the line length to collapse.
|
* A point in 3d constrained to any line whose length is free no longer
|
||||||
* Curve-line constraints (in 3d), parallel constraints (in 3d), and
|
causes the line length to collapse.
|
||||||
same orientation constraints are more robust.
|
* Curve-line constraints (in 3d), parallel constraints (in 3d), and
|
||||||
* Adding some constraints (vertical, midpoint, etc) twice errors out
|
same orientation constraints are more robust.
|
||||||
immediately, instead of later and in a confusing way.
|
* Adding some constraints (vertical, midpoint, etc) twice errors out
|
||||||
* Constraining a newly placed point to a hovered entity does not cause
|
immediately, instead of later and in a confusing way.
|
||||||
spurious changes in the sketch.
|
* Constraining a newly placed point to a hovered entity does not cause
|
||||||
* Points highlighted with "Analyze → Show Degrees of Freedom" are drawn
|
spurious changes in the sketch.
|
||||||
on top of all other geometry.
|
* Points highlighted with "Analyze → Show Degrees of Freedom" are drawn
|
||||||
* A step rotate/translate group using a group forced to triangle mesh
|
on top of all other geometry.
|
||||||
as a source group also gets forced to triangle mesh.
|
* A step rotate/translate group using a group forced to triangle mesh
|
||||||
* Paste Transformed with a negative scale does not invert arcs.
|
as a source group also gets forced to triangle mesh.
|
||||||
* The tangent arc now modifies the original entities instead of deleting
|
* Paste Transformed with a negative scale does not invert arcs.
|
||||||
them, such that their constraints are retained.
|
* The tangent arc now modifies the original entities instead of deleting
|
||||||
* When linking a sketch file, missing custom styles are now imported from
|
them, such that their constraints are retained.
|
||||||
the linked file.
|
* When linking a sketch file, missing custom styles are now imported from
|
||||||
* 3Dconnexion SpaceMouse should now work (on Windows and macOS X).
|
the linked file.
|
||||||
* Improved NURBS boolean operations on curved surfaces in some cases.
|
* 3Dconnexion SpaceMouse should now work (on Windows and macOS X).
|
||||||
* Show only usable fonts in the font selector.
|
* Improved NURBS boolean operations on curved surfaces in some cases.
|
||||||
|
* Show only usable fonts in the font selector.
|
||||||
|
|
||||||
2.x
|
2.x
|
||||||
---
|
---
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
* Do not crash when changing an unconstrained lathe group between
|
|
||||||
union and difference modes.
|
* Do not crash when changing an unconstrained lathe group between
|
||||||
|
union and difference modes.
|
||||||
|
|
||||||
2.3
|
2.3
|
||||||
---
|
---
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
* Do not crash when applying a symmetry constraint to two points.
|
|
||||||
* Fix TTF font metrics again (properly this time).
|
* Do not crash when applying a symmetry constraint to two points.
|
||||||
* Fix the "draw back faces in red" option.
|
* Fix TTF font metrics again (properly this time).
|
||||||
* Fix export of wireframe as 3D DXF.
|
* Fix the "draw back faces in red" option.
|
||||||
* Various minor crashes.
|
* Fix export of wireframe as 3D DXF.
|
||||||
|
* Various minor crashes.
|
||||||
|
|
||||||
2.2
|
2.2
|
||||||
---
|
---
|
||||||
|
|
||||||
Other new features:
|
Other new features:
|
||||||
* OS X: support 3Dconnexion devices (SpaceMouse, SpaceNavigator, etc).
|
|
||||||
* GTK: files with uppercase extensions can be opened.
|
* OS X: support 3Dconnexion devices (SpaceMouse, SpaceNavigator, etc).
|
||||||
|
* GTK: files with uppercase extensions can be opened.
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
* Do not remove autosaves after successfully opening a file, preventing
|
|
||||||
data loss in case of two abnormal terminations in a row.
|
* Do not remove autosaves after successfully opening a file, preventing
|
||||||
* Do not crash when changing autosave interval.
|
data loss in case of two abnormal terminations in a row.
|
||||||
* Unbreak the "Show degrees of freedom" command.
|
* Do not crash when changing autosave interval.
|
||||||
* Three.js: correctly respond to controls when browser zoom is used.
|
* Unbreak the "Show degrees of freedom" command.
|
||||||
* OS X: do not completely hide main window when defocused.
|
* Three.js: correctly respond to controls when browser zoom is used.
|
||||||
* GTK: unbreak 3Dconnexion support.
|
* OS X: do not completely hide main window when defocused.
|
||||||
* When pasting transformed entities, multiply constraint values by scale.
|
* GTK: unbreak 3Dconnexion support.
|
||||||
* Fix TTF font metrics (restore the behavior from version 2.0).
|
* When pasting transformed entities, multiply constraint values by scale.
|
||||||
* Forcibly show the current group once we start a drawing operation.
|
* Fix TTF font metrics (restore the behavior from version 2.0).
|
||||||
* DXF export: always declare layers before using them.
|
* Forcibly show the current group once we start a drawing operation.
|
||||||
* Do not truncate operations on selections to first 32 selected entities.
|
* DXF export: always declare layers before using them.
|
||||||
* Translate and rotate groups inherit the "suppress solid model" setting.
|
* Do not truncate operations on selections to first 32 selected entities.
|
||||||
* DXF: files with paths containing non-ASCII or spaces can be exported
|
* Translate and rotate groups inherit the "suppress solid model" setting.
|
||||||
or imported.
|
* DXF: files with paths containing non-ASCII or spaces can be exported
|
||||||
* Significantly improved performance when dragging an entity.
|
or imported.
|
||||||
* Various crashes and minor glitches.
|
* Significantly improved performance when dragging an entity.
|
||||||
|
* Various crashes and minor glitches.
|
||||||
|
|
||||||
2.1
|
2.1
|
||||||
---
|
---
|
||||||
|
|
||||||
New sketch features:
|
New sketch features:
|
||||||
* Lathe groups create circle and face entities.
|
|
||||||
* New toolbar button for creating lathe groups.
|
* Lathe groups create circle and face entities.
|
||||||
* Chord tolerance is separated into two: display chord tolerance (specified
|
* New toolbar button for creating lathe groups.
|
||||||
in percents, relative to model bounding box), and export chord tolerance
|
* Chord tolerance is separated into two: display chord tolerance (specified
|
||||||
(specified in millimeters as absolute value).
|
in percents, relative to model bounding box), and export chord tolerance
|
||||||
* Bezier spline points can be added and removed after the spline is created.
|
(specified in millimeters as absolute value).
|
||||||
* When an unconstrained extrusion is switched between "union" and
|
* Bezier spline points can be added and removed after the spline is created.
|
||||||
"difference", its normal is flipped.
|
* When an unconstrained extrusion is switched between "union" and
|
||||||
* Groups can be added in the middle of the stack. Note that this results
|
"difference", its normal is flipped.
|
||||||
in files incompatible with version 2.0.
|
* Groups can be added in the middle of the stack. Note that this results
|
||||||
* Active group can be removed.
|
in files incompatible with version 2.0.
|
||||||
* Removing an imported group does not cause all subsequent groups to also
|
* Active group can be removed.
|
||||||
be removed.
|
* Removing an imported group does not cause all subsequent groups to also
|
||||||
* When a new group with a solid is created, the color is taken from
|
be removed.
|
||||||
a previous group with a solid, if any.
|
* When a new group with a solid is created, the color is taken from
|
||||||
* Entities in a newly active group do not become visible.
|
a previous group with a solid, if any.
|
||||||
* When entities are selected, "Zoom to fit" zooms to fit only these
|
* Entities in a newly active group do not become visible.
|
||||||
entities and not the entire sketch.
|
* When entities are selected, "Zoom to fit" zooms to fit only these
|
||||||
* Zero-length edges are reported with a "zero-length error", not
|
entities and not the entire sketch.
|
||||||
"points not all coplanar".
|
* Zero-length edges are reported with a "zero-length error", not
|
||||||
|
"points not all coplanar".
|
||||||
|
|
||||||
New constraint features:
|
New constraint features:
|
||||||
* Height of the font used for drawing constraint labels can be changed.
|
|
||||||
* New constraint, length difference, placed with J.
|
* Height of the font used for drawing constraint labels can be changed.
|
||||||
(Patch by Peter Ruevski)
|
* New constraint, length difference, placed with J.
|
||||||
* Horizontal/vertical constraints are automatically added if a line segment
|
(Patch by Peter Ruevski)
|
||||||
is close enough to being horizontal/vertical. This can be disabled by
|
* Horizontal/vertical constraints are automatically added if a line segment
|
||||||
holding Ctrl.
|
is close enough to being horizontal/vertical. This can be disabled by
|
||||||
* Reference dimensions and angles can be placed with Shift+D and Shift+N.
|
holding Ctrl.
|
||||||
* Copying and pasting entities duplicates any constraints that only involve
|
* Reference dimensions and angles can be placed with Shift+D and Shift+N.
|
||||||
entities in the clipboard, as well as selected comments.
|
* Copying and pasting entities duplicates any constraints that only involve
|
||||||
* Diameter constraints can be shown as radius.
|
entities in the clipboard, as well as selected comments.
|
||||||
* The "pi" identifier can be used in expressions.
|
* Diameter constraints can be shown as radius.
|
||||||
* Constraint labels can be snapped to grid.
|
* The "pi" identifier can be used in expressions.
|
||||||
* Integer angles are displayed without trailing zeroes.
|
* Constraint labels can be snapped to grid.
|
||||||
* Angle constraints have proper reference lines and arrowheads.
|
* Integer angles are displayed without trailing zeroes.
|
||||||
* Extension lines are drawn for point-line distance constraints.
|
* Angle constraints have proper reference lines and arrowheads.
|
||||||
|
* Extension lines are drawn for point-line distance constraints.
|
||||||
|
|
||||||
New solver features:
|
New solver features:
|
||||||
* Sketches with redundant and unsolvable constraints are distinguished.
|
|
||||||
* New group setting, "allow redundant constraints". Note that it makes
|
* Sketches with redundant and unsolvable constraints are distinguished.
|
||||||
the solver less stable.
|
* New group setting, "allow redundant constraints". Note that it makes
|
||||||
|
the solver less stable.
|
||||||
|
|
||||||
New rendering and styling features:
|
New rendering and styling features:
|
||||||
* New line style parameter: stippling, based on ISO 128.
|
|
||||||
* Outlines of solids can be drawn in a particular style (by default, thick
|
* New line style parameter: stippling, based on ISO 128.
|
||||||
lines) controlled by the "Show outline of solid model" button.
|
* Outlines of solids can be drawn in a particular style (by default, thick
|
||||||
* Occluded edges can be drawn in a particular style (by default, stippled
|
lines) controlled by the "Show outline of solid model" button.
|
||||||
with short dashes) controlled by the "Show hidden lines" button.
|
* Occluded edges can be drawn in a particular style (by default, stippled
|
||||||
* Solids can be made transparent.
|
with short dashes) controlled by the "Show hidden lines" button.
|
||||||
|
* Solids can be made transparent.
|
||||||
|
|
||||||
New export/import features:
|
New export/import features:
|
||||||
* The old "import" command (for .slvs files) is renamed to "link".
|
|
||||||
* If a linked .slvs file is not found, first the relative path recorded
|
* The old "import" command (for .slvs files) is renamed to "link".
|
||||||
in the .slvs file is checked and then the absolute path; this is
|
* If a linked .slvs file is not found, first the relative path recorded
|
||||||
an inversion of the previously used order. If it is still not found,
|
in the .slvs file is checked and then the absolute path; this is
|
||||||
a dialog appears offering to locate it.
|
an inversion of the previously used order. If it is still not found,
|
||||||
* DXF and DWG files can be imported, with point-coincident, horizontal and
|
a dialog appears offering to locate it.
|
||||||
vertical constraints automatically inferred from geometry, and distance
|
* DXF and DWG files can be imported, with point-coincident, horizontal and
|
||||||
and angle constraints created when a dimension placed against geometry
|
vertical constraints automatically inferred from geometry, and distance
|
||||||
exists.
|
and angle constraints created when a dimension placed against geometry
|
||||||
* Triangle mesh can be exported for viewing in the browser through WebGL.
|
exists.
|
||||||
* Export dialogs remember the last file format used, and preselect it.
|
* Triangle mesh can be exported for viewing in the browser through WebGL.
|
||||||
* Exported DXF files have exact circles, arcs and splines instead of
|
* Export dialogs remember the last file format used, and preselect it.
|
||||||
a piecewise linear approximation (unless hidden line removal was needed).
|
* Exported DXF files have exact circles, arcs and splines instead of
|
||||||
* Exported DXF files preserve color and line thickness.
|
a piecewise linear approximation (unless hidden line removal was needed).
|
||||||
* In exported DXF files, constraints are represented as DXF dimensions,
|
* Exported DXF files preserve color and line thickness.
|
||||||
instead of piecewise linear geometry.
|
* In exported DXF files, constraints are represented as DXF dimensions,
|
||||||
* When exporting 2d views, overlapping lines are removed.
|
instead of piecewise linear geometry.
|
||||||
|
* When exporting 2d views, overlapping lines are removed.
|
||||||
|
|
||||||
Other new features:
|
Other new features:
|
||||||
* Native Linux (GTK 2 and GTK 3) and Mac OS X ports.
|
|
||||||
* Automatically save and then restore sketches if SolveSpace crashes.
|
* Native Linux (GTK 2 and GTK 3) and Mac OS X ports.
|
||||||
(Patch by Marc Britten)
|
* Automatically save and then restore sketches if SolveSpace crashes.
|
||||||
* Unicode is supported everywhere (filenames, group names, TTF text,
|
(Patch by Marc Britten)
|
||||||
comments), although RTL scripts and scripts making heavy use of ligatures
|
* Unicode is supported everywhere (filenames, group names, TTF text,
|
||||||
are not rendered correctly.
|
comments), although RTL scripts and scripts making heavy use of ligatures
|
||||||
* The vector font is grid-fitted when rendered on screen to make it easier
|
are not rendered correctly.
|
||||||
to read regardless of its size.
|
* The vector font is grid-fitted when rendered on screen to make it easier
|
||||||
|
to read regardless of its size.
|
||||||
|
|
||||||
2.0
|
2.0
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# cmake configuration
|
# cmake configuration
|
||||||
|
cmake_minimum_required(VERSION 3.9...3.19)
|
||||||
|
|
||||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
|
@ -7,17 +8,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||||
" mkdir build && cd build && cmake ..")
|
" mkdir build && cd build && cmake ..")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.7.2 FATAL_ERROR)
|
|
||||||
if(NOT CMAKE_VERSION VERSION_LESS 3.11.0)
|
|
||||||
cmake_policy(VERSION 3.11.0)
|
|
||||||
endif()
|
|
||||||
if(NOT CMAKE_VERSION VERSION_LESS 3.9)
|
|
||||||
# LTO/IPO with non-Intel compilers on Linux requires policy CMP0069 to be set to NEW.
|
|
||||||
# Set it explicitly until cmake_minimum_required is raised to >= 3.9.
|
|
||||||
cmake_policy(SET CMP0069 NEW)
|
|
||||||
endif()
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||||
"${CMAKE_SOURCE_DIR}/cmake/")
|
"${CMAKE_SOURCE_DIR}/cmake/")
|
||||||
|
|
||||||
|
cmake_policy(SET CMP0048 OLD)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||||
|
|
||||||
|
@ -27,10 +21,15 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE
|
||||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX
|
||||||
"${CMAKE_SOURCE_DIR}/cmake/cxx_flag_overrides.cmake")
|
"${CMAKE_SOURCE_DIR}/cmake/cxx_flag_overrides.cmake")
|
||||||
|
|
||||||
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
# Docs say this must be set before the first project() call
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12" CACHE STRING "macOS minimum supported version")
|
||||||
|
endif()
|
||||||
|
|
||||||
# project
|
# project
|
||||||
|
|
||||||
# NOTE TO PACKAGERS: The embedded git commit hash is critical for rapid bug triage when the builds
|
# NOTE TO PACKAGERS: The embedded git commit hash is critical for rapid bug triage when the builds
|
||||||
|
@ -40,10 +39,10 @@ include(GetGitCommitHash)
|
||||||
# and instead uncomment the following, adding the complete git hash of the checkout you are using:
|
# and instead uncomment the following, adding the complete git hash of the checkout you are using:
|
||||||
# set(GIT_COMMIT_HASH 0000000000000000000000000000000000000000)
|
# set(GIT_COMMIT_HASH 0000000000000000000000000000000000000000)
|
||||||
|
|
||||||
project(solvespace)
|
|
||||||
set(solvespace_VERSION_MAJOR 3)
|
set(solvespace_VERSION_MAJOR 3)
|
||||||
set(solvespace_VERSION_MINOR 0)
|
set(solvespace_VERSION_MINOR 0)
|
||||||
string(SUBSTRING "${GIT_COMMIT_HASH}" 0 8 solvespace_GIT_HASH)
|
string(SUBSTRING "${GIT_COMMIT_HASH}" 0 8 solvespace_GIT_HASH)
|
||||||
|
project(solvespace LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
set(ENABLE_GUI ON CACHE BOOL
|
set(ENABLE_GUI ON CACHE BOOL
|
||||||
"Whether the graphical interface is enabled")
|
"Whether the graphical interface is enabled")
|
||||||
|
@ -68,10 +67,6 @@ if("${CMAKE_GENERATOR}" STREQUAL "Xcode")
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_BINARY_DIR}/bin>)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_BINARY_DIR}/bin>)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
|
|
||||||
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||||
# GCC 4.8/4.9 ship with broken but present <regex>. meh.
|
# GCC 4.8/4.9 ship with broken but present <regex>. meh.
|
||||||
|
@ -82,11 +77,13 @@ endif()
|
||||||
# common compiler flags
|
# common compiler flags
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.")
|
if (NOT APPLE)
|
||||||
check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP)
|
set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.")
|
||||||
if(HAS_FILE_PREFIX_MAP)
|
check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}")
|
if(HAS_FILE_PREFIX_MAP)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
|
@ -210,6 +207,12 @@ if(WIN32 OR APPLE)
|
||||||
find_vendored_package(PNG libpng
|
find_vendored_package(PNG libpng
|
||||||
SKIP_INSTALL_ALL ON
|
SKIP_INSTALL_ALL ON
|
||||||
PNG_LIBRARY png_static
|
PNG_LIBRARY png_static
|
||||||
|
PNG_ARM_NEON "off"
|
||||||
|
PNG_SHARED OFF
|
||||||
|
PNG_STATIC ON
|
||||||
|
PNG_EXECUTABLES OFF
|
||||||
|
PNG_TESTS OFF
|
||||||
|
PNG_FRAMEWORK OFF
|
||||||
PNG_PNG_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/libpng)
|
PNG_PNG_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/libpng)
|
||||||
list(APPEND PNG_PNG_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/libpng)
|
list(APPEND PNG_PNG_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/libpng)
|
||||||
|
|
||||||
|
@ -222,11 +225,14 @@ if(WIN32 OR APPLE)
|
||||||
FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/freetype/include)
|
FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/freetype/include)
|
||||||
|
|
||||||
message(STATUS "Using in-tree pixman")
|
message(STATUS "Using in-tree pixman")
|
||||||
add_vendored_subdirectory(extlib/pixman)
|
|
||||||
set(PIXMAN_FOUND YES)
|
set(PIXMAN_FOUND YES)
|
||||||
set(PIXMAN_LIBRARY pixman)
|
set(PIXMAN_LIBRARY pixman)
|
||||||
|
set(PIXMAN_BUILD_TESTS OFF CACHE BOOL "")
|
||||||
|
set(PIXMAN_BUILD_DEMOS OFF CACHE BOOL "")
|
||||||
|
|
||||||
set(PIXMAN_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/pixman/pixman)
|
set(PIXMAN_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/pixman/pixman)
|
||||||
list(APPEND PIXMAN_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/pixman/pixman)
|
list(APPEND PIXMAN_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/pixman/pixman)
|
||||||
|
add_vendored_subdirectory(extlib/pixman)
|
||||||
|
|
||||||
message(STATUS "Using in-tree cairo")
|
message(STATUS "Using in-tree cairo")
|
||||||
add_vendored_subdirectory(extlib/cairo)
|
add_vendored_subdirectory(extlib/cairo)
|
||||||
|
@ -275,6 +281,7 @@ if(ENABLE_GUI)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_library(APPKIT_LIBRARY AppKit REQUIRED)
|
find_library(APPKIT_LIBRARY AppKit REQUIRED)
|
||||||
|
set(util_LIBRARIES ${APPKIT_LIBRARY})
|
||||||
else()
|
else()
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(SpaceWare)
|
find_package(SpaceWare)
|
||||||
|
|
23
README.md
|
@ -37,16 +37,19 @@ the SolveSpace maintainers for each stable release.
|
||||||
|
|
||||||
### Via Snap Store
|
### Via Snap Store
|
||||||
|
|
||||||
Builds from master are automatically released to the `edge` channel in the Snap Store. Those packages contain the latest improvements, but receive less testing than release builds.
|
Official releases can be installed from the `stable` channel.
|
||||||
|
|
||||||
Future official releases will appear in the `stable` channel.
|
Builds from master are automatically released to the `edge` channel in the Snap Store. Those packages contain the latest improvements, but receive less testing than release builds.
|
||||||
|
|
||||||
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/solvespace)
|
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/solvespace)
|
||||||
|
|
||||||
Or install from a terminal:
|
Or install from a terminal:
|
||||||
|
|
||||||
```
|
```
|
||||||
snap install --edge solvespace
|
# for the latest stable release:
|
||||||
|
snap install solvespace
|
||||||
|
# for the bleeding edge builds from master:
|
||||||
|
snap install solvespace --edge
|
||||||
```
|
```
|
||||||
|
|
||||||
### Via third-party binary packages
|
### Via third-party binary packages
|
||||||
|
@ -58,6 +61,20 @@ and cannot guarantee their functionality.
|
||||||
|
|
||||||
[notesalexp]: https://notesalexp.org/packages/en/source/solvespace/
|
[notesalexp]: https://notesalexp.org/packages/en/source/solvespace/
|
||||||
|
|
||||||
|
### Via automated edge builds
|
||||||
|
|
||||||
|
> :warning: **Edge builds might be unstable or contain severe bugs!**
|
||||||
|
> They are intended for experienced users to test new features or verify bugfixes.
|
||||||
|
|
||||||
|
Cutting edge builds from the latest master commit are available as zip archives from the
|
||||||
|
following links:
|
||||||
|
|
||||||
|
- [macOS](https://nightly.link/solvespace/solvespace/workflows/cd/master/macos.zip)
|
||||||
|
- [Windows](https://nightly.link/solvespace/solvespace/workflows/cd/master/windows.zip)
|
||||||
|
- [Windows with OpenMP enabled](https://nightly.link/solvespace/solvespace/workflows/cd/master/windows-openmp.zip)
|
||||||
|
|
||||||
|
Extract the downloaded archive and install or execute the contained file as is appropriate for your platform.
|
||||||
|
|
||||||
### Via source code
|
### Via source code
|
||||||
|
|
||||||
See below.
|
See below.
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
diff --git a/extlib/libpng/CMakeLists.txt b/extlib/libpng/CMakeLists.txt
|
||||||
|
index 42ff0f9025..6834ea332e 100644
|
||||||
|
--- a/extlib/libpng/CMakeLists.txt
|
||||||
|
+++ b/extlib/libpng/CMakeLists.txt
|
||||||
|
@@ -65,11 +65,22 @@ option(PNG_HARDWARE_OPTIMIZATIONS "Enable hardware optimizations" ON)
|
||||||
|
set(PNG_PREFIX "" CACHE STRING "Prefix to add to the API function names")
|
||||||
|
set(DFA_XTRA "" CACHE FILEPATH "File containing extra configuration settings")
|
||||||
|
|
||||||
|
+# CMake currently sets CMAKE_SYSTEM_PROCESSOR to one of x86_64 or arm64 on macOS,
|
||||||
|
+# based upon the OS architecture, not the target architecture. As such, we need
|
||||||
|
+# to check CMAKE_OSX_ARCHITECTURES to identify which hardware-specific flags to
|
||||||
|
+# enable. Note that this will fail if you attempt to build a universal binary in
|
||||||
|
+# a single cmake invocation.
|
||||||
|
+if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||||
|
+ set(TARGET_ARCH ${CMAKE_OSX_ARCHITECTURES})
|
||||||
|
+else()
|
||||||
|
+ set(TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
|
||||||
|
+endif()
|
||||||
|
+
|
||||||
|
if(PNG_HARDWARE_OPTIMIZATIONS)
|
||||||
|
|
||||||
|
# Set definitions and sources for ARM.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
|
||||||
|
+if(TARGET_ARCH MATCHES "^arm" OR
|
||||||
|
+ TARGET_ARCH MATCHES "^aarch64")
|
||||||
|
set(PNG_ARM_NEON_POSSIBLE_VALUES check on off)
|
||||||
|
set(PNG_ARM_NEON "check"
|
||||||
|
CACHE STRING "Enable ARM NEON optimizations: check|on|off; check is default")
|
||||||
|
@@ -95,8 +106,8 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set definitions and sources for PowerPC.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64*")
|
||||||
|
+if(TARGET_ARCH MATCHES "^powerpc*" OR
|
||||||
|
+ TARGET_ARCH MATCHES "^ppc64*")
|
||||||
|
set(PNG_POWERPC_VSX_POSSIBLE_VALUES on off)
|
||||||
|
set(PNG_POWERPC_VSX "on"
|
||||||
|
CACHE STRING "Enable POWERPC VSX optimizations: on|off; on is default")
|
||||||
|
@@ -118,8 +129,8 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set definitions and sources for Intel.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64*")
|
||||||
|
+if(TARGET_ARCH MATCHES "^i?86" OR
|
||||||
|
+ TARGET_ARCH MATCHES "^x86_64*")
|
||||||
|
set(PNG_INTEL_SSE_POSSIBLE_VALUES on off)
|
||||||
|
set(PNG_INTEL_SSE "on"
|
||||||
|
CACHE STRING "Enable INTEL_SSE optimizations: on|off; on is default")
|
||||||
|
@@ -141,8 +152,8 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set definitions and sources for MIPS.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "mips64el*")
|
||||||
|
+if(TARGET_ARCH MATCHES "mipsel*" OR
|
||||||
|
+ TARGET_ARCH MATCHES "mips64el*")
|
||||||
|
set(PNG_MIPS_MSA_POSSIBLE_VALUES on off)
|
||||||
|
set(PNG_MIPS_MSA "on"
|
||||||
|
CACHE STRING "Enable MIPS_MSA optimizations: on|off; on is default")
|
||||||
|
@@ -166,26 +177,26 @@ endif()
|
||||||
|
else(PNG_HARDWARE_OPTIMIZATIONS)
|
||||||
|
|
||||||
|
# Set definitions and sources for ARM.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
|
||||||
|
+if(TARGET_ARCH MATCHES "^arm" OR
|
||||||
|
+ TARGET_ARCH MATCHES "^aarch64")
|
||||||
|
add_definitions(-DPNG_ARM_NEON_OPT=0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set definitions and sources for PowerPC.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64*")
|
||||||
|
+if(TARGET_ARCH MATCHES "^powerpc*" OR
|
||||||
|
+ TARGET_ARCH MATCHES "^ppc64*")
|
||||||
|
add_definitions(-DPNG_POWERPC_VSX_OPT=0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set definitions and sources for Intel.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64*")
|
||||||
|
+if(TARGET_ARCH MATCHES "^i?86" OR
|
||||||
|
+ TARGET_ARCH MATCHES "^x86_64*")
|
||||||
|
add_definitions(-DPNG_INTEL_SSE_OPT=0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set definitions and sources for MIPS.
|
||||||
|
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR
|
||||||
|
- CMAKE_SYSTEM_PROCESSOR MATCHES "mips64el*")
|
||||||
|
+if(TARGET_ARCH MATCHES "mipsel*" OR
|
||||||
|
+ TARGET_ARCH MATCHES "mips64el*")
|
||||||
|
add_definitions(-DPNG_MIPS_MSA_OPT=0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@@ -412,19 +412,11 @@ else()
|
||||||
|
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/checksym.awk"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.def")
|
||||||
|
|
||||||
|
- add_custom_target(symbol-check
|
||||||
|
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.chk")
|
||||||
|
-
|
||||||
|
generate_copy("${CMAKE_CURRENT_BINARY_DIR}/scripts/sym.out"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/libpng.sym")
|
||||||
|
generate_copy("${CMAKE_CURRENT_BINARY_DIR}/scripts/vers.out"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/libpng.vers")
|
||||||
|
|
||||||
|
- add_custom_target(genvers
|
||||||
|
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libpng.vers")
|
||||||
|
- add_custom_target(gensym
|
||||||
|
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libpng.sym")
|
||||||
|
-
|
||||||
|
add_custom_target("genprebuilt"
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
"-DOUTPUT=scripts/pnglibconf.h.prebuilt"
|
|
@ -0,0 +1,49 @@
|
||||||
|
Some notes about Entities, Entity IDs and the IdList structure
|
||||||
|
==============================================================
|
||||||
|
Sketch entities in SolveSpace are all of the same type without use of language
|
||||||
|
support for polymorphism. The entity class is defined in sketch.h. That class
|
||||||
|
contains an enum for each entity to define its type (line, arc, etc...) and some
|
||||||
|
other members that can be used to store different things depending on the entity
|
||||||
|
type. This means all entities are the same size, so some data may be reference
|
||||||
|
by pointers from the entity (font, extra points, etc...)
|
||||||
|
|
||||||
|
Entities in a sketch are kept in a global array (IdList) referenced by a unique
|
||||||
|
Id (handle) and can be looked up by Id in log(n) time via binary search. In
|
||||||
|
order to use binary search the array must be kept in order sorted by Id. One
|
||||||
|
problem is that insertion takes O(n) time because half the list (on average)
|
||||||
|
must be shifted to make room for a new item.
|
||||||
|
|
||||||
|
The IdList class is a template and is used for more than entities.
|
||||||
|
|
||||||
|
EntityMap:
|
||||||
|
==========
|
||||||
|
Another important structure is the EntityMap and EntityKey defined in sketch.h
|
||||||
|
This is what allows SovleSpace to update groups when earlier groups in the
|
||||||
|
sketch are changed. If a rectangle is extruded to a box and items are
|
||||||
|
constrained to entities on that box, the user can go back to the sketch and
|
||||||
|
modify it. Entities can be added, modified an even deleted. So long as the
|
||||||
|
entities that are later used to build upon are kept the later extrude group will
|
||||||
|
pick up the changes from the 2D sketch and anything build on it will remain.
|
||||||
|
|
||||||
|
The way this works is that each group has a member called remap, which is one of
|
||||||
|
these maps. This is where my understanding is fuzzy. At the end of Group.cpp is
|
||||||
|
a function called Group::CopyEntity() which is used to make new sketch entities
|
||||||
|
when a group is created. These are generally copies of entities in the previous
|
||||||
|
group, but there are exceptions. A point will be used to generate a line when
|
||||||
|
extruding a 2D sketch. A point will also be "copied" to a circle for a Lathe
|
||||||
|
group. For this reason, the entity key is derived by combining its previous key
|
||||||
|
with something often called the CopyNumber or just remap (unfortunate).
|
||||||
|
|
||||||
|
When a group is regenerated (the first time, or after a previous one is
|
||||||
|
modified) entities are copied from the old group to the new one. For Step
|
||||||
|
Translating and Rotating there may be many copies, and the copy number is
|
||||||
|
literally N for the Nth copy except for the last one which gets an enum - it is
|
||||||
|
common to constrain the last item, so it gets a large unique number so that
|
||||||
|
constraints still refer to it if the number of copies changes. When an entity is
|
||||||
|
copied like this a new handle is created unless there is already an entity in
|
||||||
|
Remap that was created the same way. This is how constructions are preserved
|
||||||
|
across underlying changes.
|
||||||
|
|
||||||
|
There are some hard limits used in the hash table for the remap mechanism which
|
||||||
|
limit the number of entities in a group (but not the global sketch).
|
||||||
|
|
|
@ -46,7 +46,7 @@ POINT_N_ROT_TRANS: Rotates a point via quaternion param[3],param[4],param[5],par
|
||||||
|
|
||||||
POINT_N_COPY: A non-transformed copy of a point - numeric copy?
|
POINT_N_COPY: A non-transformed copy of a point - numeric copy?
|
||||||
|
|
||||||
POINT_N_ROT_AA: A point rotated arount point param[0],param[1],param[2] Where the
|
POINT_N_ROT_AA: A point rotated around point param[0],param[1],param[2] Where the
|
||||||
angle is given by param[3]*timesApplied (times 2?) and the axis
|
angle is given by param[3]*timesApplied (times 2?) and the axis
|
||||||
of rotation defined by param[4],param[5],param[6]
|
of rotation defined by param[4],param[5],param[6]
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ the entity itself.
|
||||||
The ForceTo() functions are shortcuts for using the solver. They are passed the
|
The ForceTo() functions are shortcuts for using the solver. They are passed the
|
||||||
desired location of a point (or orientation of a normal...) and have the opportunity
|
desired location of a point (or orientation of a normal...) and have the opportunity
|
||||||
to back-calculate what the group parameters should be to place it there. This is
|
to back-calculate what the group parameters should be to place it there. This is
|
||||||
used for mouse dragging of copied entites. It is notable that the constraints will
|
used for mouse dragging of copied entities. It is notable that the constraints will
|
||||||
still be applied afterward, but this is a good shortcut.
|
still be applied afterward, but this is a good shortcut.
|
||||||
|
|
||||||
When creating a new entity transformation, the first thing to do is define the
|
When creating a new entity transformation, the first thing to do is define the
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e9c3d83d5a04835806287f1e8c0f2d3a962d6673
|
Subproject commit dbe3e0c43e549a1602286144d94b0666549b18e6
|
|
@ -1 +1 @@
|
||||||
Subproject commit 27627843648ef84aee1621976f25bee5946e6bda
|
Subproject commit 4e643b6d3178e0ea2a093b7e14fe621631a91e4b
|
|
@ -113,6 +113,10 @@ typedef struct {
|
||||||
#define SLVS_C_WHERE_DRAGGED 100031
|
#define SLVS_C_WHERE_DRAGGED 100031
|
||||||
#define SLVS_C_CURVE_CURVE_TANGENT 100032
|
#define SLVS_C_CURVE_CURVE_TANGENT 100032
|
||||||
#define SLVS_C_LENGTH_DIFFERENCE 100033
|
#define SLVS_C_LENGTH_DIFFERENCE 100033
|
||||||
|
#define SLVS_C_ARC_ARC_LEN_RATIO 100034
|
||||||
|
#define SLVS_C_ARC_LINE_LEN_RATIO 100035
|
||||||
|
#define SLVS_C_ARC_ARC_DIFFERENCE 100036
|
||||||
|
#define SLVS_C_ARC_LINE_DIFFERENCE 100037
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Slvs_hConstraint h;
|
Slvs_hConstraint h;
|
||||||
|
|
|
@ -1,19 +1,29 @@
|
||||||
{
|
{
|
||||||
"app-id": "com.solvespace.SolveSpace",
|
"app-id": "com.solvespace.SolveSpace",
|
||||||
"runtime": "org.gnome.Platform",
|
"runtime": "org.freedesktop.Platform",
|
||||||
"runtime-version": "3.30",
|
"runtime-version": "20.08",
|
||||||
"sdk": "org.gnome.Sdk",
|
"sdk": "org.freedesktop.Sdk",
|
||||||
"finish-args": [
|
"finish-args": [
|
||||||
/* Access to display server and OpenGL */
|
/* Access to display server and OpenGL */
|
||||||
"--share=ipc", "--socket=fallback-x11", "--socket=wayland", "--device=dri",
|
"--share=ipc",
|
||||||
|
"--socket=fallback-x11",
|
||||||
|
"--socket=wayland",
|
||||||
|
"--device=dri",
|
||||||
/* Access to save files */
|
/* Access to save files */
|
||||||
"--filesystem=home"
|
"--filesystem=home"
|
||||||
],
|
],
|
||||||
"cleanup": [
|
"cleanup": [
|
||||||
"/include", "/lib/*/include",
|
"/include",
|
||||||
"*.a", "*.la", "*.m4", "/lib/libslvs*.so*", "/lib/libglibmm_generate_extra_defs*.so*",
|
"/lib/*/include",
|
||||||
"/share/pkgconfig", "*.pc",
|
"*.a",
|
||||||
"/share/man", "/share/doc",
|
"*.la",
|
||||||
|
"*.m4",
|
||||||
|
"/lib/libslvs*.so*",
|
||||||
|
"/lib/libglibmm_generate_extra_defs*.so*",
|
||||||
|
"/share/pkgconfig",
|
||||||
|
"*.pc",
|
||||||
|
"/share/man",
|
||||||
|
"/share/doc",
|
||||||
"/share/aclocal",
|
"/share/aclocal",
|
||||||
/* mm-common junk */
|
/* mm-common junk */
|
||||||
"/bin/mm-common-prepare",
|
"/bin/mm-common-prepare",
|
||||||
|
@ -26,8 +36,8 @@
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/mm-common/0.9/mm-common-0.9.12.tar.xz",
|
"url": "https://download.gnome.org/sources/mm-common/1.0/mm-common-1.0.2.tar.xz",
|
||||||
"sha256": "ceffdcce1e5b52742884c233ec604bf6fded12eea9da077ce7a62c02c87e7c0b"
|
"sha256": "a2a99f3fa943cf662f189163ed39a2cfc19a428d906dd4f92b387d3659d1641d"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -39,21 +49,20 @@
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/libsigc++/2.10/libsigc++-2.10.1.tar.xz",
|
"url": "https://download.gnome.org/sources/libsigc++/2.10/libsigc%2B%2B-2.10.6.tar.xz",
|
||||||
"sha256": "c9a25f26178c6cbb147f9904d8c533b5a5c5111a41ac2eb781eb734eea446003"
|
"sha256": "dda176dc4681bda9d5a2ac1bc55273bdd381662b7a6d49e918267d13e8774e1b"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glibmm",
|
"name": "glibmm",
|
||||||
"config-opts": [
|
"config-opts": [],
|
||||||
"--disable-documentation"
|
"buildsystem": "meson",
|
||||||
],
|
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/glibmm/2.58/glibmm-2.58.1.tar.xz",
|
"url": "https://download.gnome.org/sources/glibmm/2.64/glibmm-2.64.5.tar.xz",
|
||||||
"sha256": "6e5fe03bdf1e220eeffd543e017fd2fb15bcec9235f0ffd50674aff9362a85f0"
|
"sha256": "508fc86e2c9141198aa16c225b16fd6b911917c0d3817602652844d0973ea386"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -98,14 +107,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "gtkmm",
|
"name": "gtkmm",
|
||||||
"config-opts": [
|
"config-opts": [],
|
||||||
"--disable-documentation"
|
"buildsystem": "meson",
|
||||||
],
|
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/gtkmm/3.24/gtkmm-3.24.1.tar.xz",
|
"url": "https://download.gnome.org/sources/gtkmm/3.24/gtkmm-3.24.4.tar.xz",
|
||||||
"sha256": "ddfe42ed2458a20a34de252854bcf4b52d3f0c671c045f56b42aa27c7542d2fd"
|
"sha256": "9beb71c3e90cfcfb790396b51e3f5e7169966751efd4f3ef9697114be3be6743"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -113,6 +121,7 @@
|
||||||
"name": "libjson-c",
|
"name": "libjson-c",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
|
/* 0.15-nodoc doesn't build */
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://s3.amazonaws.com/json-c_releases/releases/json-c-0.13.1-nodoc.tar.gz",
|
"url": "https://s3.amazonaws.com/json-c_releases/releases/json-c-0.13.1-nodoc.tar.gz",
|
||||||
"sha256": "94a26340c0785fcff4f46ff38609cf84ebcd670df0c8efd75d039cc951d80132"
|
"sha256": "94a26340c0785fcff4f46ff38609cf84ebcd670df0c8efd75d039cc951d80132"
|
||||||
|
@ -125,8 +134,8 @@
|
||||||
"name": "SolveSpace",
|
"name": "SolveSpace",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "dir",
|
||||||
"path": "/home/whitequark/Projects/solvespace"
|
"path": "../.."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"buildsystem": "cmake",
|
"buildsystem": "cmake",
|
||||||
|
|
|
@ -31,7 +31,7 @@ if(WIN32)
|
||||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
|
||||||
endfunction()
|
endfunction()
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set(app_resource_dir ${CMAKE_BINARY_DIR}/bin/SolveSpace.app/Contents/Resources)
|
set(app_resource_dir ${CMAKE_BINARY_DIR}/Resources)
|
||||||
set(cli_resource_dir ${CMAKE_BINARY_DIR}/res)
|
set(cli_resource_dir ${CMAKE_BINARY_DIR}/res)
|
||||||
|
|
||||||
function(add_resource name)
|
function(add_resource name)
|
||||||
|
@ -134,6 +134,13 @@ else()
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications
|
||||||
RENAME com.solvespace.SolveSpace.desktop)
|
RENAME com.solvespace.SolveSpace.desktop)
|
||||||
|
|
||||||
|
set(DESKTOP_FILE_NAME com.solvespace.SolveSpace.desktop)
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/com.solvespace.SolveSpace.metainfo.xml.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/freedesktop/com.solvespace.SolveSpace.metainfo.xml)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/com.solvespace.SolveSpace.metainfo.xml
|
||||||
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo)
|
||||||
|
|
||||||
install(FILES freedesktop/solvespace-flatpak-mime.xml
|
install(FILES freedesktop/solvespace-flatpak-mime.xml
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
|
||||||
RENAME com.solvespace.SolveSpace-slvs.xml)
|
RENAME com.solvespace.SolveSpace-slvs.xml)
|
||||||
|
@ -176,6 +183,13 @@ else()
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace.desktop
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace.desktop
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
||||||
|
|
||||||
|
set(DESKTOP_FILE_NAME solvespace.desktop)
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/freedesktop/com.solvespace.SolveSpace.metainfo.xml.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/freedesktop/com.solvespace.SolveSpace.metainfo.xml)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/com.solvespace.SolveSpace.metainfo.xml
|
||||||
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo)
|
||||||
|
|
||||||
install(FILES freedesktop/solvespace-mime.xml
|
install(FILES freedesktop/solvespace-mime.xml
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
|
||||||
RENAME solvespace-slvs.xml)
|
RENAME solvespace-slvs.xml)
|
||||||
|
@ -185,7 +199,7 @@ else()
|
||||||
RENAME solvespace.svg)
|
RENAME solvespace.svg)
|
||||||
install(FILES freedesktop/solvespace-scalable.svg
|
install(FILES freedesktop/solvespace-scalable.svg
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
|
||||||
RENAME application.x-solvespace.svg)
|
RENAME application-x-solvespace.svg)
|
||||||
|
|
||||||
foreach(SIZE 16x16 24x24 32x32 48x48)
|
foreach(SIZE 16x16 24x24 32x32 48x48)
|
||||||
install(FILES freedesktop/solvespace-${SIZE}.png
|
install(FILES freedesktop/solvespace-${SIZE}.png
|
||||||
|
@ -193,12 +207,7 @@ else()
|
||||||
RENAME solvespace.png)
|
RENAME solvespace.png)
|
||||||
install(FILES freedesktop/solvespace-${SIZE}.png
|
install(FILES freedesktop/solvespace-${SIZE}.png
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
|
||||||
RENAME application.x-solvespace.png)
|
RENAME application-x-solvespace.png)
|
||||||
endforeach()
|
|
||||||
|
|
||||||
foreach(SIZE 16x16 24x24 32x32 48x48)
|
|
||||||
install(FILES freedesktop/solvespace-${SIZE}.xpm
|
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps)
|
|
||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -257,6 +266,8 @@ add_resources(
|
||||||
locales/en_US.po
|
locales/en_US.po
|
||||||
locales/fr_FR.po
|
locales/fr_FR.po
|
||||||
locales/uk_UA.po
|
locales/uk_UA.po
|
||||||
|
locales/es_AR.po
|
||||||
|
locales/tr_TR.po
|
||||||
locales/ru_RU.po
|
locales/ru_RU.po
|
||||||
locales/zh_CN.po
|
locales/zh_CN.po
|
||||||
fonts/unifont.hex.gz
|
fonts/unifont.hex.gz
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>com.solvespace.SolveSpace</id>
|
||||||
|
|
||||||
|
<name>SolveSpace</name>
|
||||||
|
<summary>A free (GPLv3) parametric 3d CAD tool</summary>
|
||||||
|
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0-or-later</project_license>
|
||||||
|
<update_contact>ryan_AT_ryanpavlik.com</update_contact>
|
||||||
|
|
||||||
|
<categories>
|
||||||
|
<category>Graphics</category>
|
||||||
|
<category>3DGraphics</category>
|
||||||
|
<category>Engineering</category>
|
||||||
|
</categories>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
SolveSpace is a free (GPLv3) parametric 3d CAD tool. Applications include:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Modeling 3d parts — draw with extrudes, revolves, and Boolean operations</li>
|
||||||
|
<li>Modeling 2d parts — draw the part as a single section, and export; use 3d assembly to verify fit</li>
|
||||||
|
<li>Modeling 3d-printed parts — export the STL or other triangle mesh expected by most slicers</li>
|
||||||
|
<li>Preparing 2D CAM data — export 2d vector art for a waterjet machine or laser cutter</li>
|
||||||
|
<li>Mechanism design — use the constraint solver to simulate planar or spatial linkages</li>
|
||||||
|
<li>Plane and solid geometry — replace hand-solved trigonometry with a live dimensioned drawing</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
<url type="homepage">https://solvespace.com</url>
|
||||||
|
<url type="bugtracker">https://github.com/solvespace/solvespace/issues</url>
|
||||||
|
|
||||||
|
<launchable type="desktop-id">@DESKTOP_FILE_NAME@</launchable>
|
||||||
|
<provides>
|
||||||
|
<mediatype>application/x-solvespace</mediatype>
|
||||||
|
</provides>
|
||||||
|
|
||||||
|
<content_rating type="oars-1.0" />
|
||||||
|
|
||||||
|
<releases>
|
||||||
|
<release version="3.0" date="2021-04-18" type="stable">
|
||||||
|
<description>
|
||||||
|
<p>Major new stable release. Includes new intersection boolean operation,
|
||||||
|
new types of groups, solid model suppression, usability improvements
|
||||||
|
(especially regarding redundant constraints and automatic constraints),
|
||||||
|
and more. Also includes performance and scalability improvements.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/solvespace/solvespace/releases/tag/v3.0</url>
|
||||||
|
</release>
|
||||||
|
|
||||||
|
<release version="3.0~rc2" date="2021-01-17" type="development">
|
||||||
|
<description>
|
||||||
|
<p>Second release candidate for the 3.0 stable release.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/solvespace/solvespace/releases/tag/v3.0.rc2</url>
|
||||||
|
</release>
|
||||||
|
|
||||||
|
<release version="3.0~rc1" date="2020-11-18" type="development">
|
||||||
|
<description>
|
||||||
|
<p>First release candidate for the 3.0 stable release.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/solvespace/solvespace/releases/tag/v3.0.rc1</url>
|
||||||
|
</release>
|
||||||
|
|
||||||
|
<release version="2.3" date="2016-12-23" type="stable">
|
||||||
|
<description>
|
||||||
|
<p>Bug-fix release in the 2.x series, fixing some crashes.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/solvespace/solvespace/releases/tag/v2.3</url>
|
||||||
|
</release>
|
||||||
|
|
||||||
|
<release version="2.2" date="2016-10-16" type="stable">
|
||||||
|
<description>
|
||||||
|
<p>Bug-fix release, including performance improvements.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/solvespace/solvespace/releases/tag/v2.2</url>
|
||||||
|
</release>
|
||||||
|
|
||||||
|
<release version="2.1" date="2016-06-11" type="stable">
|
||||||
|
<description>
|
||||||
|
<p>Introduced *nix compatibility, internationalization, technical drawing mode, improved import and export, and other features and fixes.</p>
|
||||||
|
</description>
|
||||||
|
<url>https://github.com/solvespace/solvespace/releases/tag/v2.1</url>
|
||||||
|
</release>
|
||||||
|
</releases>
|
||||||
|
|
||||||
|
</component>
|
|
@ -1,27 +0,0 @@
|
||||||
/* XPM */
|
|
||||||
static char *solvespace_16x16[] = {
|
|
||||||
/* columns rows colors chars-per-pixel */
|
|
||||||
"16 16 5 1 ",
|
|
||||||
" c black",
|
|
||||||
". c #1ED500",
|
|
||||||
"X c #DE00D6",
|
|
||||||
"o c #CBCBCB",
|
|
||||||
"O c None",
|
|
||||||
/* pixels */
|
|
||||||
"OOO OOOOOOOOOOOO",
|
|
||||||
"OOO OOOOOOOOOOOO",
|
|
||||||
"OOO OOOOOOOOOOOO",
|
|
||||||
"OOO OOOOOXOOOOOO",
|
|
||||||
"OOO OOOOOXoOOOOO",
|
|
||||||
"OOO OOOOOXoOOOOO",
|
|
||||||
"OOO OOOOOXoOOOOO",
|
|
||||||
"OOO OOOOOXoOOOOO",
|
|
||||||
"OOO OOOOOXoOOOOO",
|
|
||||||
"OOO OOXXXXXXXOOO",
|
|
||||||
"OOO OOOoooooooOO",
|
|
||||||
"OO...OOOOOOOOOOO",
|
|
||||||
" ... ",
|
|
||||||
"OO...OOOOOOOOOOO",
|
|
||||||
"OOO OOOOOOOOOOOO",
|
|
||||||
"OOO OOOOOOOOOOOO"
|
|
||||||
};
|
|
|
@ -1,35 +0,0 @@
|
||||||
/* XPM */
|
|
||||||
static char *solvespace_24x24[] = {
|
|
||||||
/* columns rows colors chars-per-pixel */
|
|
||||||
"24 24 5 1 ",
|
|
||||||
" c black",
|
|
||||||
". c #1ED500",
|
|
||||||
"X c #DE00D6",
|
|
||||||
"o c #CBCBCB",
|
|
||||||
"O c None",
|
|
||||||
/* pixels */
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOXOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
|
||||||
"OOOOOOO OOXXXXXXXOOOOOOO",
|
|
||||||
"OOOOOOO OOOoooooooOOOOOO",
|
|
||||||
"OOOOOO...OOOOOOOOOOOOOOO",
|
|
||||||
"OOOO ... OOOO",
|
|
||||||
"OOOOOO...OOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOO"
|
|
||||||
};
|
|
|
@ -1,43 +0,0 @@
|
||||||
/* XPM */
|
|
||||||
static char *solvespace_32x32[] = {
|
|
||||||
/* columns rows colors chars-per-pixel */
|
|
||||||
"32 32 5 1 ",
|
|
||||||
" c black",
|
|
||||||
". c #1ED500",
|
|
||||||
"X c #DE00D6",
|
|
||||||
"o c #CBCBCB",
|
|
||||||
"O c None",
|
|
||||||
/* pixels */
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
|
|
||||||
"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOooooooooooooOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
" ...... ",
|
|
||||||
" ...... ",
|
|
||||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO"
|
|
||||||
};
|
|
|
@ -1,59 +0,0 @@
|
||||||
/* XPM */
|
|
||||||
static char *solvespace_48x48[] = {
|
|
||||||
/* columns rows colors chars-per-pixel */
|
|
||||||
"48 48 5 1 ",
|
|
||||||
" c black",
|
|
||||||
". c #1ED500",
|
|
||||||
"X c #DE00D6",
|
|
||||||
"o c #CBCBCB",
|
|
||||||
"O c None",
|
|
||||||
/* pixels */
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOooooooooooooOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOO ...... OOOOOOOO",
|
|
||||||
"OOOOOOOO ...... OOOOOOOO",
|
|
||||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
|
||||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
|
|
||||||
};
|
|
|
@ -6,5 +6,5 @@ Exec=${CMAKE_INSTALL_FULL_BINDIR}/solvespace
|
||||||
MimeType=application/x-solvespace
|
MimeType=application/x-solvespace
|
||||||
Icon=com.solvespace.SolveSpace
|
Icon=com.solvespace.SolveSpace
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Graphics
|
Categories=Graphics;3DGraphics;Engineering;
|
||||||
Keywords=parametric;cad;2d;3d;
|
Keywords=parametric;cad;2d;3d;
|
||||||
|
|
|
@ -6,5 +6,5 @@ Exec=solvespace
|
||||||
MimeType=application/x-solvespace
|
MimeType=application/x-solvespace
|
||||||
Icon=${SNAP}/meta/icons/hicolor/scalable/apps/snap.solvespace.svg
|
Icon=${SNAP}/meta/icons/hicolor/scalable/apps/snap.solvespace.svg
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Graphics
|
Categories=Graphics;3DGraphics;Engineering;
|
||||||
Keywords=parametric;cad;2d;3d;
|
Keywords=parametric;cad;2d;3d;
|
||||||
|
|
|
@ -6,5 +6,5 @@ Exec=${CMAKE_INSTALL_FULL_BINDIR}/solvespace
|
||||||
MimeType=application/x-solvespace
|
MimeType=application/x-solvespace
|
||||||
Icon=solvespace
|
Icon=solvespace
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Graphics
|
Categories=Graphics;3DGraphics;Engineering;
|
||||||
Keywords=parametric;cad;2d;3d;
|
Keywords=parametric;cad;2d;3d;
|
||||||
|
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 216 B |
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 260 B |
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 264 B |
|
@ -3,6 +3,8 @@
|
||||||
de-DE,0407,Deutsch
|
de-DE,0407,Deutsch
|
||||||
en-US,0409,English (US)
|
en-US,0409,English (US)
|
||||||
fr-FR,040C,Français
|
fr-FR,040C,Français
|
||||||
|
es-AR,2C0A,español (AR)
|
||||||
ru-RU,0419,Русский
|
ru-RU,0419,Русский
|
||||||
|
tr-TR,041F,Türkçe
|
||||||
uk-UA,0422,Українська
|
uk-UA,0422,Українська
|
||||||
zh-CN,0804,简体中文
|
zh-CN,0804,简体中文
|
||||||
|
|
1137
res/locales/uk_UA.po
659
res/messages.pot
|
@ -117,6 +117,7 @@ if(WIN32)
|
||||||
${SPACEWARE_LIBRARIES})
|
${SPACEWARE_LIBRARIES})
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
|
-DGL_SILENCE_DEPRECATION
|
||||||
-fobjc-arc)
|
-fobjc-arc)
|
||||||
|
|
||||||
list(APPEND platform_SOURCES
|
list(APPEND platform_SOURCES
|
||||||
|
@ -176,6 +177,7 @@ set(solvespace_core_SOURCES
|
||||||
groupmesh.cpp
|
groupmesh.cpp
|
||||||
importdxf.cpp
|
importdxf.cpp
|
||||||
importidf.cpp
|
importidf.cpp
|
||||||
|
importmesh.cpp
|
||||||
mesh.cpp
|
mesh.cpp
|
||||||
modify.cpp
|
modify.cpp
|
||||||
mouse.cpp
|
mouse.cpp
|
||||||
|
@ -338,7 +340,10 @@ if(ENABLE_GUI)
|
||||||
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
|
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set_target_properties(solvespace PROPERTIES
|
set_target_properties(solvespace PROPERTIES
|
||||||
OUTPUT_NAME SolveSpace)
|
OUTPUT_NAME SolveSpace
|
||||||
|
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME "YES"
|
||||||
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.solvespace"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -401,17 +406,30 @@ endif()
|
||||||
# solvespace macOS package
|
# solvespace macOS package
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(bundle SolveSpace)
|
set(LIBOMP_LIB_PATH ${OpenMP_CXX_INCLUDE_DIRS}/../lib/libomp.dylib)
|
||||||
set(bundle_bin ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/MacOS)
|
set(LIBOMP_LINK_PATH "@executable_path/../Resources/libomp.dylib")
|
||||||
set(bundle_resources ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/Resources/lib)
|
set(LIBOMP_LINK_PATH_UTILS "@executable_path/SolveSpace.app/Contents/Resources/libomp.dylib")
|
||||||
execute_process(
|
if(ENABLE_GUI)
|
||||||
COMMAND mkdir -p ${bundle_resources}
|
add_custom_command(TARGET solvespace POST_BUILD
|
||||||
COMMAND cp -p /usr/local/opt/libomp/lib/libomp.dylib ${bundle_resources}/libomp.dylib
|
COMMAND cp -r ${CMAKE_BINARY_DIR}/Resources $<TARGET_BUNDLE_CONTENT_DIR:solvespace>
|
||||||
)
|
)
|
||||||
add_custom_command(TARGET solvespace POST_BUILD
|
if(ENABLE_OPENMP)
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${bundle_bin}
|
execute_process(COMMAND install_name_tool -id ${LIBOMP_LINK_PATH} ${LIBOMP_LIB_PATH})
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:solvespace-cli> ${bundle_bin}
|
message("FROM " ${${LIBOMP_LIB_PATH}} "TO" $<TARGET_BUNDLE_CONTENT_DIR:solvespace>/Resources/libomp.dylib)
|
||||||
COMMAND install_name_tool -change /usr/local/opt/libomp/lib/libomp.dylib "@executable_path/../Resources/lib/libomp.dylib" ${bundle_bin}/${bundle}
|
add_custom_command(TARGET solvespace POST_BUILD
|
||||||
COMMENT "Bundling executable solvespace-cli"
|
COMMAND ${CMAKE_COMMAND} -E copy ${LIBOMP_LIB_PATH} $<TARGET_BUNDLE_CONTENT_DIR:solvespace>/Resources/libomp.dylib
|
||||||
VERBATIM)
|
COMMAND install_name_tool -change ${LIBOMP_LINK_PATH} ${LIBOMP_LINK_PATH_UTILS} $<TARGET_FILE:solvespace-debugtool>
|
||||||
endif()
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(ENABLE_TESTS AND ENABLE_OPENMP)
|
||||||
|
add_custom_command(TARGET solvespace POST_BUILD
|
||||||
|
COMMAND install_name_tool -change ${LIBOMP_LINK_PATH} ${LIBOMP_LINK_PATH_UTILS} $<TARGET_FILE:solvespace-testsuite>)
|
||||||
|
endif()
|
||||||
|
if(ENABLE_CLI)
|
||||||
|
add_custom_command(TARGET solvespace POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:solvespace-cli> $<TARGET_FILE_DIR:solvespace>
|
||||||
|
COMMENT "Bundling executable solvespace-cli"
|
||||||
|
VERBATIM)
|
||||||
|
endif()
|
||||||
|
endif()
|
|
@ -138,18 +138,17 @@ void GraphicsWindow::CopySelection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Constraint *c;
|
for(Constraint &c : SK.constraint) {
|
||||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
if(!SS.clipboard.ContainsEntity(c.ptA) ||
|
||||||
if(!SS.clipboard.ContainsEntity(c->ptA) ||
|
!SS.clipboard.ContainsEntity(c.ptB) ||
|
||||||
!SS.clipboard.ContainsEntity(c->ptB) ||
|
!SS.clipboard.ContainsEntity(c.entityA) ||
|
||||||
!SS.clipboard.ContainsEntity(c->entityA) ||
|
!SS.clipboard.ContainsEntity(c.entityB) ||
|
||||||
!SS.clipboard.ContainsEntity(c->entityB) ||
|
!SS.clipboard.ContainsEntity(c.entityC) ||
|
||||||
!SS.clipboard.ContainsEntity(c->entityC) ||
|
!SS.clipboard.ContainsEntity(c.entityD) ||
|
||||||
!SS.clipboard.ContainsEntity(c->entityD) ||
|
c.type == Constraint::Type::COMMENT) {
|
||||||
c->type == Constraint::Type::COMMENT) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SS.clipboard.c.Add(c);
|
SS.clipboard.c.Add(&c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +281,7 @@ void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
|
||||||
}
|
}
|
||||||
case Constraint::Type::HORIZONTAL:
|
case Constraint::Type::HORIZONTAL:
|
||||||
case Constraint::Type::VERTICAL:
|
case Constraint::Type::VERTICAL:
|
||||||
// When rotating 90 or 270 degrees, swap the vertical / horizontal constaints
|
// When rotating 90 or 270 degrees, swap the vertical / horizontal constraints
|
||||||
if (EXACT(fmod(theta + (PI/2), PI) == 0)) {
|
if (EXACT(fmod(theta + (PI/2), PI) == 0)) {
|
||||||
if(c.type == Constraint::Type::HORIZONTAL) {
|
if(c.type == Constraint::Type::HORIZONTAL) {
|
||||||
c.type = Constraint::Type::VERTICAL;
|
c.type = Constraint::Type::VERTICAL;
|
||||||
|
|
|
@ -9,24 +9,6 @@
|
||||||
#include <omp.h>
|
#include <omp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void TextWindow::ScreenChangeLightDirection(int link, uint32_t v) {
|
|
||||||
SS.TW.ShowEditControl(8, ssprintf("%.2f, %.2f, %.2f", CO(SS.lightDir[v])));
|
|
||||||
SS.TW.edit.meaning = Edit::LIGHT_DIRECTION;
|
|
||||||
SS.TW.edit.i = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeLightIntensity(int link, uint32_t v) {
|
|
||||||
SS.TW.ShowEditControl(31, ssprintf("%.2f", SS.lightIntensity[v]));
|
|
||||||
SS.TW.edit.meaning = Edit::LIGHT_INTENSITY;
|
|
||||||
SS.TW.edit.i = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeLightAmbient(int link, uint32_t v) {
|
|
||||||
SS.TW.ShowEditControl(31, ssprintf("%.2f", SS.ambientIntensity));
|
|
||||||
SS.TW.edit.meaning = Edit::LIGHT_AMBIENT;
|
|
||||||
SS.TW.edit.i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeColor(int link, uint32_t v) {
|
void TextWindow::ScreenChangeColor(int link, uint32_t v) {
|
||||||
SS.TW.ShowEditControlWithColorPicker(13, SS.modelColor[v]);
|
SS.TW.ShowEditControlWithColorPicker(13, SS.modelColor[v]);
|
||||||
|
|
||||||
|
@ -58,13 +40,8 @@ void TextWindow::ScreenChangeExportMaxSegments(int link, uint32_t v) {
|
||||||
SS.TW.edit.i = 1;
|
SS.TW.edit.i = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::ScreenChangeCameraTangent(int link, uint32_t v) {
|
|
||||||
SS.TW.ShowEditControl(3, ssprintf("%.3f", 1000*SS.cameraTangent));
|
|
||||||
SS.TW.edit.meaning = Edit::CAMERA_TANGENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeGridSpacing(int link, uint32_t v) {
|
void TextWindow::ScreenChangeGridSpacing(int link, uint32_t v) {
|
||||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.gridSpacing));
|
SS.TW.ShowEditControl(3, SS.MmToString(SS.gridSpacing, true));
|
||||||
SS.TW.edit.meaning = Edit::GRID_SPACING;
|
SS.TW.edit.meaning = Edit::GRID_SPACING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +66,7 @@ void TextWindow::ScreenChangeExportScale(int link, uint32_t v) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::ScreenChangeExportOffset(int link, uint32_t v) {
|
void TextWindow::ScreenChangeExportOffset(int link, uint32_t v) {
|
||||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.exportOffset));
|
SS.TW.ShowEditControl(3, SS.MmToString(SS.exportOffset, true));
|
||||||
SS.TW.edit.meaning = Edit::EXPORT_OFFSET;
|
SS.TW.edit.meaning = Edit::EXPORT_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +148,7 @@ void TextWindow::ScreenChangeCanvasSize(int link, uint32_t v) {
|
||||||
}
|
}
|
||||||
int col = 13;
|
int col = 13;
|
||||||
if(v < 10) col = 11;
|
if(v < 10) col = 11;
|
||||||
SS.TW.ShowEditControl(col, SS.MmToString(d));
|
SS.TW.ShowEditControl(col, SS.MmToString(d, true));
|
||||||
SS.TW.edit.meaning = Edit::CANVAS_SIZE;
|
SS.TW.edit.meaning = Edit::CANVAS_SIZE;
|
||||||
SS.TW.edit.i = v;
|
SS.TW.edit.i = v;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +158,12 @@ void TextWindow::ScreenChangeGCodeParameter(int link, uint32_t v) {
|
||||||
switch(link) {
|
switch(link) {
|
||||||
case 'd':
|
case 'd':
|
||||||
SS.TW.edit.meaning = Edit::G_CODE_DEPTH;
|
SS.TW.edit.meaning = Edit::G_CODE_DEPTH;
|
||||||
buf += SS.MmToString(SS.gCode.depth);
|
buf += SS.MmToString(SS.gCode.depth, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
SS.TW.edit.meaning = Edit::G_CODE_SAFE_HEIGHT;
|
||||||
|
buf += SS.MmToString(SS.gCode.safeHeight, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
|
@ -191,12 +173,12 @@ void TextWindow::ScreenChangeGCodeParameter(int link, uint32_t v) {
|
||||||
|
|
||||||
case 'F':
|
case 'F':
|
||||||
SS.TW.edit.meaning = Edit::G_CODE_FEED;
|
SS.TW.edit.meaning = Edit::G_CODE_FEED;
|
||||||
buf += SS.MmToString(SS.gCode.feed);
|
buf += SS.MmToString(SS.gCode.feed, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
SS.TW.edit.meaning = Edit::G_CODE_PLUNGE_FEED;
|
SS.TW.edit.meaning = Edit::G_CODE_PLUNGE_FEED;
|
||||||
buf += SS.MmToString(SS.gCode.plungeFeed);
|
buf += SS.MmToString(SS.gCode.plungeFeed, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SS.TW.ShowEditControl(14, buf);
|
SS.TW.ShowEditControl(14, buf);
|
||||||
|
@ -227,20 +209,6 @@ void TextWindow::ShowConfiguration() {
|
||||||
&ScreenChangeColor, i);
|
&ScreenChangeColor, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft light direction intensity");
|
|
||||||
for(i = 0; i < 2; i++) {
|
|
||||||
Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E "
|
|
||||||
"%2 %Fl%D%f%Ll[c]%E",
|
|
||||||
(i & 1) ? 'd' : 'a', i,
|
|
||||||
CO(SS.lightDir[i]), i, &ScreenChangeLightDirection,
|
|
||||||
SS.lightIntensity[i], i, &ScreenChangeLightIntensity);
|
|
||||||
}
|
|
||||||
Printf(false, "%Bp ambient lighting "
|
|
||||||
"%2 %Fl%D%f%Ll[c]%E",
|
|
||||||
(i & 1) ? 'd' : 'a', i,
|
|
||||||
SS.ambientIntensity, &ScreenChangeLightAmbient);
|
|
||||||
|
|
||||||
Printf(false, "");
|
Printf(false, "");
|
||||||
Printf(false, "%Ft chord tolerance (in percents)%E");
|
Printf(false, "%Ft chord tolerance (in percents)%E");
|
||||||
Printf(false, "%Ba %@ %% %Fl%Ll%f%D[change]%E; %@ mm, %d triangles",
|
Printf(false, "%Ba %@ %% %Fl%Ll%f%D[change]%E; %@ mm, %d triangles",
|
||||||
|
@ -262,11 +230,6 @@ void TextWindow::ShowConfiguration() {
|
||||||
SS.exportMaxSegments,
|
SS.exportMaxSegments,
|
||||||
&ScreenChangeExportMaxSegments);
|
&ScreenChangeExportMaxSegments);
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft perspective factor (0 for parallel)%E");
|
|
||||||
Printf(false, "%Ba %# %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.cameraTangent*1000,
|
|
||||||
&ScreenChangeCameraTangent, 0);
|
|
||||||
Printf(false, "%Ft snap grid spacing%E");
|
Printf(false, "%Ft snap grid spacing%E");
|
||||||
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
||||||
SS.MmToString(SS.gridSpacing).c_str(),
|
SS.MmToString(SS.gridSpacing).c_str(),
|
||||||
|
@ -461,6 +424,11 @@ bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
|
||||||
SS.GW.Invalidate();
|
SS.GW.Invalidate();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Edit::EXPLODE_DISTANCE: {
|
||||||
|
SS.explodeDistance = min(1e4, max(-1e4, SS.StringToMm(s)));
|
||||||
|
SS.MarkGroupDirty(SS.GW.activeGroup, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Edit::DIGITS_AFTER_DECIMAL: {
|
case Edit::DIGITS_AFTER_DECIMAL: {
|
||||||
int v = atoi(s.c_str());
|
int v = atoi(s.c_str());
|
||||||
if(v < 0 || v > 8) {
|
if(v < 0 || v > 8) {
|
||||||
|
@ -529,6 +497,11 @@ bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
|
||||||
if(e) SS.gCode.depth = (float)SS.ExprToMm(e);
|
if(e) SS.gCode.depth = (float)SS.ExprToMm(e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Edit::G_CODE_SAFE_HEIGHT: {
|
||||||
|
Expr *e = Expr::From(s, /*popUpError=*/true);
|
||||||
|
if(e) SS.gCode.safeHeight = (float)SS.ExprToMm(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Edit::G_CODE_PASSES: {
|
case Edit::G_CODE_PASSES: {
|
||||||
Expr *e = Expr::From(s, /*popUpError=*/true);
|
Expr *e = Expr::From(s, /*popUpError=*/true);
|
||||||
if(e) SS.gCode.passes = (int)(e->Eval());
|
if(e) SS.gCode.passes = (int)(e->Eval());
|
||||||
|
|
|
@ -22,7 +22,11 @@ std::string Constraint::DescriptionString() const {
|
||||||
case Type::EQ_LEN_PT_LINE_D: s = C_("constr-name", "eq-length-and-pt-ln-dist"); break;
|
case Type::EQ_LEN_PT_LINE_D: s = C_("constr-name", "eq-length-and-pt-ln-dist"); break;
|
||||||
case Type::EQ_PT_LN_DISTANCES: s = C_("constr-name", "eq-pt-line-distances"); break;
|
case Type::EQ_PT_LN_DISTANCES: s = C_("constr-name", "eq-pt-line-distances"); break;
|
||||||
case Type::LENGTH_RATIO: s = C_("constr-name", "length-ratio"); break;
|
case Type::LENGTH_RATIO: s = C_("constr-name", "length-ratio"); break;
|
||||||
|
case Type::ARC_ARC_LEN_RATIO: s = C_("constr-name", "arc-arc-length-ratio"); break;
|
||||||
|
case Type::ARC_LINE_LEN_RATIO: s = C_("constr-name", "arc-line-length-ratio"); break;
|
||||||
case Type::LENGTH_DIFFERENCE: s = C_("constr-name", "length-difference"); break;
|
case Type::LENGTH_DIFFERENCE: s = C_("constr-name", "length-difference"); break;
|
||||||
|
case Type::ARC_ARC_DIFFERENCE: s = C_("constr-name", "arc-arc-len-difference"); break;
|
||||||
|
case Type::ARC_LINE_DIFFERENCE: s = C_("constr-name", "arc-line-len-difference"); break;
|
||||||
case Type::SYMMETRIC: s = C_("constr-name", "symmetric"); break;
|
case Type::SYMMETRIC: s = C_("constr-name", "symmetric"); break;
|
||||||
case Type::SYMMETRIC_HORIZ: s = C_("constr-name", "symmetric-h"); break;
|
case Type::SYMMETRIC_HORIZ: s = C_("constr-name", "symmetric-h"); break;
|
||||||
case Type::SYMMETRIC_VERT: s = C_("constr-name", "symmetric-v"); break;
|
case Type::SYMMETRIC_VERT: s = C_("constr-name", "symmetric-v"); break;
|
||||||
|
@ -384,10 +388,27 @@ void Constraint::MenuConstrain(Command id) {
|
||||||
c.type = Type::LENGTH_RATIO;
|
c.type = Type::LENGTH_RATIO;
|
||||||
c.entityA = gs.entity[0];
|
c.entityA = gs.entity[0];
|
||||||
c.entityB = gs.entity[1];
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
|
else if(gs.arcs == 2 && gs.n == 2) {
|
||||||
|
c.type = Type::ARC_ARC_LEN_RATIO;
|
||||||
|
c.entityA = gs.entity[0];
|
||||||
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
|
else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
|
||||||
|
c.type = Type::ARC_LINE_LEN_RATIO;
|
||||||
|
if(SK.GetEntity(gs.entity[0])->type == Entity::Type::ARC_OF_CIRCLE) {
|
||||||
|
c.entityA = gs.entity[1];
|
||||||
|
c.entityB = gs.entity[0];
|
||||||
|
} else {
|
||||||
|
c.entityA = gs.entity[0];
|
||||||
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Error(_("Bad selection for length ratio constraint. This "
|
Error(_("Bad selection for length ratio constraint. This "
|
||||||
"constraint can apply to:\n\n"
|
"constraint can apply to:\n\n"
|
||||||
" * two line segments\n"));
|
" * two line segments\n"
|
||||||
|
" * two arcs\n"
|
||||||
|
" * one arc and one line segment\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,10 +422,27 @@ void Constraint::MenuConstrain(Command id) {
|
||||||
c.type = Type::LENGTH_DIFFERENCE;
|
c.type = Type::LENGTH_DIFFERENCE;
|
||||||
c.entityA = gs.entity[0];
|
c.entityA = gs.entity[0];
|
||||||
c.entityB = gs.entity[1];
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
|
else if(gs.arcs == 2 && gs.n == 2) {
|
||||||
|
c.type = Type::ARC_ARC_DIFFERENCE;
|
||||||
|
c.entityA = gs.entity[0];
|
||||||
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
|
else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
|
||||||
|
c.type = Type::ARC_LINE_DIFFERENCE;
|
||||||
|
if(SK.GetEntity(gs.entity[0])->type == Entity::Type::ARC_OF_CIRCLE) {
|
||||||
|
c.entityA = gs.entity[1];
|
||||||
|
c.entityB = gs.entity[0];
|
||||||
|
} else {
|
||||||
|
c.entityA = gs.entity[0];
|
||||||
|
c.entityB = gs.entity[1];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Error(_("Bad selection for length difference constraint. This "
|
Error(_("Bad selection for length difference constraint. This "
|
||||||
"constraint can apply to:\n\n"
|
"constraint can apply to:\n\n"
|
||||||
" * two line segments\n"));
|
" * two line segments\n"
|
||||||
|
" * two arcs\n"
|
||||||
|
" * one arc and one line segment\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,10 +805,19 @@ void Constraint::MenuConstrain(Command id) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Command::COMMENT:
|
case Command::COMMENT:
|
||||||
SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
|
if(gs.points == 1 && gs.n == 1) {
|
||||||
SS.GW.pending.command = Command::COMMENT;
|
c.type = Type::COMMENT;
|
||||||
SS.GW.pending.description = _("click center of comment text");
|
c.ptA = gs.point[0];
|
||||||
SS.ScheduleShowTW();
|
c.group = SS.GW.activeGroup;
|
||||||
|
c.workplane = SS.GW.ActiveWorkplane();
|
||||||
|
c.comment = _("NEW COMMENT -- DOUBLE-CLICK TO EDIT");
|
||||||
|
AddConstraint(&c);
|
||||||
|
} else {
|
||||||
|
SS.GW.pending.operation = GraphicsWindow::Pending::COMMAND;
|
||||||
|
SS.GW.pending.command = Command::COMMENT;
|
||||||
|
SS.GW.pending.description = _("click center of comment text");
|
||||||
|
SS.ScheduleShowTW();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: ssassert(false, "Unexpected menu ID");
|
default: ssassert(false, "Unexpected menu ID");
|
||||||
|
|
|
@ -18,7 +18,11 @@ bool ConstraintBase::HasLabel() const {
|
||||||
case Type::PROJ_PT_DISTANCE:
|
case Type::PROJ_PT_DISTANCE:
|
||||||
case Type::DIAMETER:
|
case Type::DIAMETER:
|
||||||
case Type::LENGTH_RATIO:
|
case Type::LENGTH_RATIO:
|
||||||
|
case Type::ARC_ARC_LEN_RATIO:
|
||||||
|
case Type::ARC_LINE_LEN_RATIO:
|
||||||
case Type::LENGTH_DIFFERENCE:
|
case Type::LENGTH_DIFFERENCE:
|
||||||
|
case Type::ARC_ARC_DIFFERENCE:
|
||||||
|
case Type::ARC_LINE_DIFFERENCE:
|
||||||
case Type::ANGLE:
|
case Type::ANGLE:
|
||||||
case Type::COMMENT:
|
case Type::COMMENT:
|
||||||
return true;
|
return true;
|
||||||
|
@ -39,7 +43,11 @@ bool ConstraintBase::IsProjectible() const {
|
||||||
case Type::EQ_PT_LN_DISTANCES:
|
case Type::EQ_PT_LN_DISTANCES:
|
||||||
case Type::EQUAL_ANGLE:
|
case Type::EQUAL_ANGLE:
|
||||||
case Type::LENGTH_RATIO:
|
case Type::LENGTH_RATIO:
|
||||||
|
case Type::ARC_ARC_LEN_RATIO:
|
||||||
|
case Type::ARC_LINE_LEN_RATIO:
|
||||||
case Type::LENGTH_DIFFERENCE:
|
case Type::LENGTH_DIFFERENCE:
|
||||||
|
case Type::ARC_ARC_DIFFERENCE:
|
||||||
|
case Type::ARC_LINE_DIFFERENCE:
|
||||||
case Type::SYMMETRIC:
|
case Type::SYMMETRIC:
|
||||||
case Type::SYMMETRIC_HORIZ:
|
case Type::SYMMETRIC_HORIZ:
|
||||||
case Type::SYMMETRIC_VERT:
|
case Type::SYMMETRIC_VERT:
|
||||||
|
@ -334,6 +342,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
|
||||||
AddEq(l, (la->Div(lb))->Minus(exA), 0);
|
AddEq(l, (la->Div(lb))->Minus(exA), 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Type::ARC_ARC_LEN_RATIO: {
|
||||||
|
EntityBase *arc1 = SK.GetEntity(entityA),
|
||||||
|
*arc2 = SK.GetEntity(entityB);
|
||||||
|
|
||||||
|
// And get the arc1 radius, and the cosine of its angle
|
||||||
|
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
|
||||||
|
*as1 = SK.GetEntity(arc1->point[1]),
|
||||||
|
*af1 = SK.GetEntity(arc1->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
|
||||||
|
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
|
||||||
|
Expr *r1 = aof1.Magnitude();
|
||||||
|
|
||||||
|
ExprVector n1 = arc1->Normal()->NormalExprsN();
|
||||||
|
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v1 = n1.Cross(u1);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
|
||||||
|
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
|
||||||
|
|
||||||
|
double thetas1, thetaf1, dtheta1;
|
||||||
|
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
|
||||||
|
Expr *theta1;
|
||||||
|
if(dtheta1 < 3*PI/4) {
|
||||||
|
theta1 = costheta1->ACos();
|
||||||
|
} else if(dtheta1 < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta1 is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
|
||||||
|
} else {
|
||||||
|
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
|
||||||
|
}
|
||||||
|
|
||||||
|
// And get the arc2 radius, and the cosine of its angle
|
||||||
|
EntityBase *ao2 = SK.GetEntity(arc2->point[0]),
|
||||||
|
*as2 = SK.GetEntity(arc2->point[1]),
|
||||||
|
*af2 = SK.GetEntity(arc2->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos2 = (as2->PointGetExprs()).Minus(ao2->PointGetExprs()),
|
||||||
|
aof2 = (af2->PointGetExprs()).Minus(ao2->PointGetExprs());
|
||||||
|
Expr *r2 = aof2.Magnitude();
|
||||||
|
|
||||||
|
ExprVector n2 = arc2->Normal()->NormalExprsN();
|
||||||
|
ExprVector u2 = aos2.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v2 = n2.Cross(u2);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta2 = aof2.Dot(u2)->Div(r2);
|
||||||
|
Expr *sintheta2 = aof2.Dot(v2)->Div(r2);
|
||||||
|
|
||||||
|
double thetas2, thetaf2, dtheta2;
|
||||||
|
arc2->ArcGetAngles(&thetas2, &thetaf2, &dtheta2);
|
||||||
|
Expr *theta2;
|
||||||
|
if(dtheta2 < 3*PI/4) {
|
||||||
|
theta2 = costheta2->ACos();
|
||||||
|
} else if(dtheta2 < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta2 is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta2 = Expr::From(PI)->Minus(sintheta2->ASin());
|
||||||
|
} else {
|
||||||
|
theta2 = (Expr::From(2*PI))->Minus(costheta2->ACos());
|
||||||
|
}
|
||||||
|
// And write the equation; (r1*theta1) / ( r2*theta2) = some ratio
|
||||||
|
AddEq(l, (r1->Times(theta1))->Div(r2->Times(theta2))->Minus(exA), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type::ARC_LINE_LEN_RATIO: {
|
||||||
|
EntityBase *line = SK.GetEntity(entityA),
|
||||||
|
*arc1 = SK.GetEntity(entityB);
|
||||||
|
|
||||||
|
Expr *ll = Distance(workplane, line->point[0], line->point[1]);
|
||||||
|
|
||||||
|
// And get the arc1 radius, and the cosine of its angle
|
||||||
|
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
|
||||||
|
*as1 = SK.GetEntity(arc1->point[1]),
|
||||||
|
*af1 = SK.GetEntity(arc1->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
|
||||||
|
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
|
||||||
|
Expr *r1 = aof1.Magnitude();
|
||||||
|
ExprVector n1 = arc1->Normal()->NormalExprsN();
|
||||||
|
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v1 = n1.Cross(u1);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
|
||||||
|
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
|
||||||
|
|
||||||
|
double thetas1, thetaf1, dtheta1;
|
||||||
|
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
|
||||||
|
Expr *theta1;
|
||||||
|
if(dtheta1 < 3*PI/4) {
|
||||||
|
theta1 = costheta1->ACos();
|
||||||
|
} else if(dtheta1 < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta1 is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
|
||||||
|
} else {
|
||||||
|
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
|
||||||
|
}
|
||||||
|
// And write the equation; (r1*theta1) / ( length) = some ratio
|
||||||
|
AddEq(l, (r1->Times(theta1))->Div(ll)->Minus(exA), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case Type::LENGTH_DIFFERENCE: {
|
case Type::LENGTH_DIFFERENCE: {
|
||||||
EntityBase *a = SK.GetEntity(entityA);
|
EntityBase *a = SK.GetEntity(entityA);
|
||||||
|
@ -343,7 +455,111 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
|
||||||
AddEq(l, (la->Minus(lb))->Minus(exA), 0);
|
AddEq(l, (la->Minus(lb))->Minus(exA), 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Type::ARC_ARC_DIFFERENCE: {
|
||||||
|
EntityBase *arc1 = SK.GetEntity(entityA),
|
||||||
|
*arc2 = SK.GetEntity(entityB);
|
||||||
|
|
||||||
|
// And get the arc1 radius, and the cosine of its angle
|
||||||
|
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
|
||||||
|
*as1 = SK.GetEntity(arc1->point[1]),
|
||||||
|
*af1 = SK.GetEntity(arc1->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
|
||||||
|
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
|
||||||
|
Expr *r1 = aof1.Magnitude();
|
||||||
|
|
||||||
|
ExprVector n1 = arc1->Normal()->NormalExprsN();
|
||||||
|
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v1 = n1.Cross(u1);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
|
||||||
|
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
|
||||||
|
|
||||||
|
double thetas1, thetaf1, dtheta1;
|
||||||
|
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
|
||||||
|
Expr *theta1;
|
||||||
|
if(dtheta1 < 3*PI/4) {
|
||||||
|
theta1 = costheta1->ACos();
|
||||||
|
} else if(dtheta1 < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta1 is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
|
||||||
|
} else {
|
||||||
|
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
|
||||||
|
}
|
||||||
|
|
||||||
|
// And get the arc2 radius, and the cosine of its angle
|
||||||
|
EntityBase *ao2 = SK.GetEntity(arc2->point[0]),
|
||||||
|
*as2 = SK.GetEntity(arc2->point[1]),
|
||||||
|
*af2 = SK.GetEntity(arc2->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos2 = (as2->PointGetExprs()).Minus(ao2->PointGetExprs()),
|
||||||
|
aof2 = (af2->PointGetExprs()).Minus(ao2->PointGetExprs());
|
||||||
|
Expr *r2 = aof2.Magnitude();
|
||||||
|
|
||||||
|
ExprVector n2 = arc2->Normal()->NormalExprsN();
|
||||||
|
ExprVector u2 = aos2.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v2 = n2.Cross(u2);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta2 = aof2.Dot(u2)->Div(r2);
|
||||||
|
Expr *sintheta2 = aof2.Dot(v2)->Div(r2);
|
||||||
|
|
||||||
|
double thetas2, thetaf2, dtheta2;
|
||||||
|
arc2->ArcGetAngles(&thetas2, &thetaf2, &dtheta2);
|
||||||
|
Expr *theta2;
|
||||||
|
if(dtheta2 < 3*PI/4) {
|
||||||
|
theta2 = costheta2->ACos();
|
||||||
|
} else if(dtheta2 < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta2 is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta2 = Expr::From(PI)->Minus(sintheta2->ASin());
|
||||||
|
} else {
|
||||||
|
theta2 = (Expr::From(2*PI))->Minus(costheta2->ACos());
|
||||||
|
}
|
||||||
|
// And write the equation; (r1*theta1) - ( r2*theta2) = some difference
|
||||||
|
AddEq(l, (r1->Times(theta1))->Minus(r2->Times(theta2))->Minus(exA), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Type::ARC_LINE_DIFFERENCE: {
|
||||||
|
EntityBase *line = SK.GetEntity(entityA),
|
||||||
|
*arc1 = SK.GetEntity(entityB);
|
||||||
|
|
||||||
|
Expr *ll = Distance(workplane, line->point[0], line->point[1]);
|
||||||
|
|
||||||
|
// And get the arc1 radius, and the cosine of its angle
|
||||||
|
EntityBase *ao1 = SK.GetEntity(arc1->point[0]),
|
||||||
|
*as1 = SK.GetEntity(arc1->point[1]),
|
||||||
|
*af1 = SK.GetEntity(arc1->point[2]);
|
||||||
|
|
||||||
|
ExprVector aos1 = (as1->PointGetExprs()).Minus(ao1->PointGetExprs()),
|
||||||
|
aof1 = (af1->PointGetExprs()).Minus(ao1->PointGetExprs());
|
||||||
|
Expr *r1 = aof1.Magnitude();
|
||||||
|
ExprVector n1 = arc1->Normal()->NormalExprsN();
|
||||||
|
ExprVector u1 = aos1.WithMagnitude(Expr::From(1.0));
|
||||||
|
ExprVector v1 = n1.Cross(u1);
|
||||||
|
// so in our new csys, we start at (1, 0, 0)
|
||||||
|
Expr *costheta1 = aof1.Dot(u1)->Div(r1);
|
||||||
|
Expr *sintheta1 = aof1.Dot(v1)->Div(r1);
|
||||||
|
|
||||||
|
double thetas1, thetaf1, dtheta1;
|
||||||
|
arc1->ArcGetAngles(&thetas1, &thetaf1, &dtheta1);
|
||||||
|
Expr *theta1;
|
||||||
|
if(dtheta1 < 3*PI/4) {
|
||||||
|
theta1 = costheta1->ACos();
|
||||||
|
} else if(dtheta1 < 5*PI/4) {
|
||||||
|
// As the angle crosses pi, cos theta1 is not invertible;
|
||||||
|
// so use the sine to stop blowing up
|
||||||
|
theta1 = Expr::From(PI)->Minus(sintheta1->ASin());
|
||||||
|
} else {
|
||||||
|
theta1 = (Expr::From(2*PI))->Minus(costheta1->ACos());
|
||||||
|
}
|
||||||
|
// And write the equation; (r1*theta1) - ( length) = some difference
|
||||||
|
AddEq(l, (r1->Times(theta1))->Minus(ll)->Minus(exA), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case Type::DIAMETER: {
|
case Type::DIAMETER: {
|
||||||
EntityBase *circle = SK.GetEntity(entityA);
|
EntityBase *circle = SK.GetEntity(entityA);
|
||||||
Expr *r = circle->CircleGetRadiusExpr();
|
Expr *r = circle->CircleGetRadiusExpr();
|
||||||
|
@ -511,7 +727,7 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case Type::SYMMETRIC:
|
case Type::SYMMETRIC:
|
||||||
if(workplane == EntityBase::FREE_IN_3D) {
|
if(workplane == EntityBase::FREE_IN_3D) {
|
||||||
EntityBase *plane = SK.GetEntity(entityA);
|
EntityBase *plane = SK.GetEntity(entityA);
|
||||||
|
|
17
src/draw.cpp
|
@ -210,16 +210,15 @@ void GraphicsWindow::SelectByMarquee() {
|
||||||
BBox marqueeBBox = BBox::From(Vector::From(marqueePoint.x, marqueePoint.y, VERY_NEGATIVE),
|
BBox marqueeBBox = BBox::From(Vector::From(marqueePoint.x, marqueePoint.y, VERY_NEGATIVE),
|
||||||
Vector::From(orig.mouse.x, orig.mouse.y, VERY_POSITIVE));
|
Vector::From(orig.mouse.x, orig.mouse.y, VERY_POSITIVE));
|
||||||
|
|
||||||
Entity *e;
|
for(Entity &e : SK.entity) {
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
if(e.group != SS.GW.activeGroup) continue;
|
||||||
if(e->group != SS.GW.activeGroup) continue;
|
if(e.IsFace() || e.IsDistance()) continue;
|
||||||
if(e->IsFace() || e->IsDistance()) continue;
|
if(!e.IsVisible()) continue;
|
||||||
if(!e->IsVisible()) continue;
|
|
||||||
|
|
||||||
bool entityHasBBox;
|
bool entityHasBBox;
|
||||||
BBox entityBBox = e->GetOrGenerateScreenBBox(&entityHasBBox);
|
BBox entityBBox = e.GetOrGenerateScreenBBox(&entityHasBBox);
|
||||||
if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) {
|
if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) {
|
||||||
MakeSelected(e->h);
|
MakeSelected(e.h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,8 +411,8 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
|
||||||
cached.projRight = projRight;
|
cached.projRight = projRight;
|
||||||
cached.projUp = projUp;
|
cached.projUp = projUp;
|
||||||
cached.scale = scale;
|
cached.scale = scale;
|
||||||
for(Entity *e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
for(Entity &e : SK.entity) {
|
||||||
e->screenBBoxValid = false;
|
e.screenBBoxValid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ std::string Constraint::Label() const {
|
||||||
std::string result;
|
std::string result;
|
||||||
if(type == Type::ANGLE) {
|
if(type == Type::ANGLE) {
|
||||||
result = SS.DegreeToString(valA) + "°";
|
result = SS.DegreeToString(valA) + "°";
|
||||||
} else if(type == Type::LENGTH_RATIO) {
|
} else if(type == Type::LENGTH_RATIO || type == Type::ARC_ARC_LEN_RATIO || type == Type::ARC_LINE_LEN_RATIO) {
|
||||||
result = ssprintf("%.3f:1", valA);
|
result = ssprintf("%.3f:1", valA);
|
||||||
} else if(type == Type::COMMENT) {
|
} else if(type == Type::COMMENT) {
|
||||||
result = comment;
|
result = comment;
|
||||||
|
@ -267,7 +267,7 @@ void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
const Camera &camera = canvas->GetCamera();
|
const Camera &camera = canvas->GetCamera();
|
||||||
Entity *circ = SK.GetEntity(he);
|
Entity *circ = SK.GetEntity(he);
|
||||||
|
|
||||||
Vector center = SK.GetEntity(circ->point[0])->PointGetNum();
|
Vector center = SK.GetEntity(circ->point[0])->PointGetDrawNum();
|
||||||
double r = circ->CircleGetRadiusNum();
|
double r = circ->CircleGetRadiusNum();
|
||||||
Quaternion q = circ->Normal()->NormalGetNum();
|
Quaternion q = circ->Normal()->NormalGetNum();
|
||||||
Vector u = q.RotationU(), v = q.RotationV();
|
Vector u = q.RotationU(), v = q.RotationV();
|
||||||
|
@ -291,7 +291,8 @@ void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
|
|
||||||
void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
Vector a0, Vector da, Vector b0, Vector db,
|
Vector a0, Vector da, Vector b0, Vector db,
|
||||||
Vector offset, Vector *ref, bool trim)
|
Vector offset, Vector *ref, bool trim,
|
||||||
|
Vector explodeOffset)
|
||||||
{
|
{
|
||||||
const Camera &camera = canvas->GetCamera();
|
const Camera &camera = canvas->GetCamera();
|
||||||
double pixels = 1.0 / camera.scale;
|
double pixels = 1.0 / camera.scale;
|
||||||
|
@ -305,6 +306,9 @@ void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
db = db.ProjectVectorInto(workplane);
|
db = db.ProjectVectorInto(workplane);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a0 = a0.Plus(explodeOffset);
|
||||||
|
b0 = b0.Plus(explodeOffset);
|
||||||
|
|
||||||
Vector a1 = a0.Plus(da);
|
Vector a1 = a0.Plus(da);
|
||||||
Vector b1 = b0.Plus(db);
|
Vector b1 = b0.Plus(db);
|
||||||
|
|
||||||
|
@ -534,6 +538,15 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
DoProjectedPoint(canvas, hcs, &bp);
|
DoProjectedPoint(canvas, hcs, &bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
// Offset A and B by the same offset so the constraint is drawn
|
||||||
|
// in the plane of one of the exploded points (rather than at an
|
||||||
|
// angle)
|
||||||
|
Vector offset = SK.GetEntity(ptA)->ExplodeOffset();
|
||||||
|
ap = ap.Plus(offset);
|
||||||
|
bp = bp.Plus(offset);
|
||||||
|
}
|
||||||
|
|
||||||
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
|
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
|
|
||||||
|
@ -548,6 +561,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
dp = (bp.Minus(ap)),
|
dp = (bp.Minus(ap)),
|
||||||
pp = SK.GetEntity(entityA)->VectorGetNum();
|
pp = SK.GetEntity(entityA)->VectorGetNum();
|
||||||
|
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
// explode for whichever point is in the workplane (or the first if both are)
|
||||||
|
Entity *pt = SK.GetEntity(ptA);
|
||||||
|
if(pt->group != group) {
|
||||||
|
pt = SK.GetEntity(ptB);
|
||||||
|
}
|
||||||
|
if(pt->group == group) {
|
||||||
|
Vector offset = pt->ExplodeOffset();
|
||||||
|
ap = ap.Plus(offset);
|
||||||
|
bp = bp.Plus(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
|
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset);
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
|
|
||||||
|
@ -564,7 +590,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
|
|
||||||
case Type::PT_FACE_DISTANCE:
|
case Type::PT_FACE_DISTANCE:
|
||||||
case Type::PT_PLANE_DISTANCE: {
|
case Type::PT_PLANE_DISTANCE: {
|
||||||
Vector pt = SK.GetEntity(ptA)->PointGetNum();
|
Vector pt = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||||
Entity *enta = SK.GetEntity(entityA);
|
Entity *enta = SK.GetEntity(entityA);
|
||||||
Vector n, p;
|
Vector n, p;
|
||||||
if(type == Type::PT_PLANE_DISTANCE) {
|
if(type == Type::PT_PLANE_DISTANCE) {
|
||||||
|
@ -590,7 +616,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
case Type::PT_LINE_DISTANCE: {
|
case Type::PT_LINE_DISTANCE: {
|
||||||
Vector pt = SK.GetEntity(ptA)->PointGetNum();
|
Entity *ptEntity = SK.GetEntity(ptA);
|
||||||
|
Vector pt = ptEntity->PointGetNum();
|
||||||
Entity *line = SK.GetEntity(entityA);
|
Entity *line = SK.GetEntity(entityA);
|
||||||
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
|
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
|
||||||
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
|
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
|
||||||
|
@ -602,6 +629,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
DoProjectedPoint(canvas, hcs, &pt);
|
DoProjectedPoint(canvas, hcs, &pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only explode if the point and line are in the same group (and that group is a sketch
|
||||||
|
// with explode enabled) otherwise it's too visually confusing to figure out what the
|
||||||
|
// correct projections should be.
|
||||||
|
bool shouldExplode = ShouldDrawExploded()
|
||||||
|
&& ptEntity->group == group
|
||||||
|
&& line->group == group;
|
||||||
|
if(shouldExplode) {
|
||||||
|
Vector explodeOffset = ptEntity->ExplodeOffset();
|
||||||
|
pt = pt.Plus(explodeOffset);
|
||||||
|
lA = lA.Plus(explodeOffset);
|
||||||
|
lB = lB.Plus(explodeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
// Find the closest point on the line
|
// Find the closest point on the line
|
||||||
Vector closest = pt.ClosestPointOnLine(lA, dl);
|
Vector closest = pt.ClosestPointOnLine(lA, dl);
|
||||||
|
|
||||||
|
@ -655,7 +695,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
|
|
||||||
case Type::DIAMETER: {
|
case Type::DIAMETER: {
|
||||||
Entity *circle = SK.GetEntity(entityA);
|
Entity *circle = SK.GetEntity(entityA);
|
||||||
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
Vector center = SK.GetEntity(circle->point[0])->PointGetDrawNum();
|
||||||
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
|
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
|
||||||
Vector n = q.RotationN().WithMagnitude(1);
|
Vector n = q.RotationN().WithMagnitude(1);
|
||||||
double r = circle->CircleGetRadiusNum();
|
double r = circle->CircleGetRadiusNum();
|
||||||
|
@ -697,7 +737,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
Vector r = camera.projRight.ScaledBy((a+1)/camera.scale);
|
Vector r = camera.projRight.ScaledBy((a+1)/camera.scale);
|
||||||
Vector d = camera.projUp.ScaledBy((2-a)/camera.scale);
|
Vector d = camera.projUp.ScaledBy((2-a)/camera.scale);
|
||||||
for(int i = 0; i < 2; i++) {
|
for(int i = 0; i < 2; i++) {
|
||||||
Vector p = SK.GetEntity(i == 0 ? ptA : ptB)-> PointGetNum();
|
Vector p = SK.GetEntity(i == 0 ? ptA : ptB)->PointGetDrawNum();
|
||||||
if(refs) refs->push_back(p);
|
if(refs) refs->push_back(p);
|
||||||
canvas->DrawQuad(p.Plus (r).Plus (d),
|
canvas->DrawQuad(p.Plus (r).Plus (d),
|
||||||
p.Plus (r).Minus(d),
|
p.Plus (r).Minus(d),
|
||||||
|
@ -715,7 +755,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
case Type::PT_ON_FACE:
|
case Type::PT_ON_FACE:
|
||||||
case Type::PT_IN_PLANE: {
|
case Type::PT_IN_PLANE: {
|
||||||
double s = 8/camera.scale;
|
double s = 8/camera.scale;
|
||||||
Vector p = SK.GetEntity(ptA)->PointGetNum();
|
Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||||
if(refs) refs->push_back(p);
|
if(refs) refs->push_back(p);
|
||||||
Vector r, d;
|
Vector r, d;
|
||||||
if(type == Type::PT_ON_FACE) {
|
if(type == Type::PT_ON_FACE) {
|
||||||
|
@ -740,7 +780,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
case Type::WHERE_DRAGGED: {
|
case Type::WHERE_DRAGGED: {
|
||||||
Vector p = SK.GetEntity(ptA)->PointGetNum();
|
Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||||
if(refs) refs->push_back(p);
|
if(refs) refs->push_back(p);
|
||||||
Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus(
|
Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus(
|
||||||
gr.WithMagnitude(8/camera.scale)),
|
gr.WithMagnitude(8/camera.scale)),
|
||||||
|
@ -797,10 +837,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
DoArcForAngle(canvas, hcs, a0, da, b0, db,
|
DoArcForAngle(canvas, hcs, a0, da, b0, db,
|
||||||
da.WithMagnitude(40/camera.scale), &ref, /*trim=*/false);
|
da.WithMagnitude(40/camera.scale), &ref, /*trim=*/false, a->ExplodeOffset());
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
DoArcForAngle(canvas, hcs, c0, dc, d0, dd,
|
DoArcForAngle(canvas, hcs, c0, dc, d0, dd,
|
||||||
dc.WithMagnitude(40/camera.scale), &ref, /*trim=*/false);
|
dc.WithMagnitude(40/camera.scale), &ref, /*trim=*/false, c->ExplodeOffset());
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -820,7 +860,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector ref;
|
Vector ref;
|
||||||
DoArcForAngle(canvas, hcs, a0, da, b0, db, disp.offset, &ref, /*trim=*/true);
|
DoArcForAngle(canvas, hcs, a0, da, b0, db, disp.offset, &ref, /*trim=*/true, a->ExplodeOffset());
|
||||||
DoLabel(canvas, hcs, ref, labelPos, gr, gu);
|
DoLabel(canvas, hcs, ref, labelPos, gr, gu);
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
return;
|
return;
|
||||||
|
@ -855,7 +895,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
if(u.Dot(ru) < 0) u = u.ScaledBy(-1);
|
if(u.Dot(ru) < 0) u = u.ScaledBy(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector p = e->VectorGetRefPoint();
|
Vector p = e->VectorGetRefPoint().Plus(e->ExplodeOffset());
|
||||||
Vector s = p.Plus(u).Plus(v);
|
Vector s = p.Plus(u).Plus(v);
|
||||||
DoLine(canvas, hcs, s, s.Plus(v));
|
DoLine(canvas, hcs, s, s.Plus(v));
|
||||||
Vector m = s.Plus(v.ScaledBy(0.5));
|
Vector m = s.Plus(v.ScaledBy(0.5));
|
||||||
|
@ -873,9 +913,9 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
if(type == Type::ARC_LINE_TANGENT) {
|
if(type == Type::ARC_LINE_TANGENT) {
|
||||||
Entity *arc = SK.GetEntity(entityA);
|
Entity *arc = SK.GetEntity(entityA);
|
||||||
Entity *norm = SK.GetEntity(arc->normal);
|
Entity *norm = SK.GetEntity(arc->normal);
|
||||||
Vector c = SK.GetEntity(arc->point[0])->PointGetNum();
|
Vector c = SK.GetEntity(arc->point[0])->PointGetDrawNum();
|
||||||
Vector p =
|
Vector p =
|
||||||
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetNum();
|
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetDrawNum();
|
||||||
Vector r = p.Minus(c);
|
Vector r = p.Minus(c);
|
||||||
textAt = p.Plus(r.WithMagnitude(14/camera.scale));
|
textAt = p.Plus(r.WithMagnitude(14/camera.scale));
|
||||||
u = norm->NormalU();
|
u = norm->NormalU();
|
||||||
|
@ -896,6 +936,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
Entity *cubic = SK.GetEntity(entityA);
|
Entity *cubic = SK.GetEntity(entityA);
|
||||||
Vector p = other ? cubic->CubicGetFinishNum() :
|
Vector p = other ? cubic->CubicGetFinishNum() :
|
||||||
cubic->CubicGetStartNum();
|
cubic->CubicGetStartNum();
|
||||||
|
p = p.Plus(cubic->ExplodeOffset());
|
||||||
Vector dir = SK.GetEntity(entityB)->VectorGetNum();
|
Vector dir = SK.GetEntity(entityB)->VectorGetNum();
|
||||||
Vector out = n.Cross(dir);
|
Vector out = n.Cross(dir);
|
||||||
textAt = p.Plus(out.WithMagnitude(14/camera.scale));
|
textAt = p.Plus(out.WithMagnitude(14/camera.scale));
|
||||||
|
@ -905,12 +946,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
u = wn->NormalU();
|
u = wn->NormalU();
|
||||||
v = wn->NormalV();
|
v = wn->NormalV();
|
||||||
n = wn->NormalN();
|
n = wn->NormalN();
|
||||||
EntityBase *eA = SK.GetEntity(entityA);
|
Entity *eA = SK.GetEntity(entityA);
|
||||||
// Big pain; we have to get a vector tangent to the curve
|
// Big pain; we have to get a vector tangent to the curve
|
||||||
// at the shared point, which could be from either a cubic
|
// at the shared point, which could be from either a cubic
|
||||||
// or an arc.
|
// or an arc.
|
||||||
if(other) {
|
if(other) {
|
||||||
textAt = eA->EndpointFinish();
|
textAt = eA->EndpointFinish().Plus(eA->ExplodeOffset());
|
||||||
if(eA->type == Entity::Type::CUBIC) {
|
if(eA->type == Entity::Type::CUBIC) {
|
||||||
dir = eA->CubicGetFinishTangentNum();
|
dir = eA->CubicGetFinishTangentNum();
|
||||||
} else {
|
} else {
|
||||||
|
@ -919,7 +960,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
dir = n.Cross(dir);
|
dir = n.Cross(dir);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
textAt = eA->EndpointStart();
|
textAt = eA->EndpointStart().Plus(eA->ExplodeOffset());
|
||||||
if(eA->type == Entity::Type::CUBIC) {
|
if(eA->type == Entity::Type::CUBIC) {
|
||||||
dir = eA->CubicGetStartTangentNum();
|
dir = eA->CubicGetStartTangentNum();
|
||||||
} else {
|
} else {
|
||||||
|
@ -947,6 +988,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale);
|
Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale);
|
||||||
Vector p = e->VectorGetRefPoint();
|
Vector p = e->VectorGetRefPoint();
|
||||||
|
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
p = p.Plus(e->ExplodeOffset());
|
||||||
|
}
|
||||||
|
|
||||||
DoLine(canvas, hcs, p.Plus(u), p.Plus(u).Plus(n));
|
DoLine(canvas, hcs, p.Plus(u), p.Plus(u).Plus(n));
|
||||||
DoLine(canvas, hcs, p.Minus(u), p.Minus(u).Plus(n));
|
DoLine(canvas, hcs, p.Minus(u), p.Minus(u).Plus(n));
|
||||||
if(refs) refs->push_back(p.Plus(n.ScaledBy(0.5)));
|
if(refs) refs->push_back(p.Plus(n.ScaledBy(0.5)));
|
||||||
|
@ -967,8 +1012,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
Entity *line = SK.GetEntity(entityA);
|
Entity *line = SK.GetEntity(entityA);
|
||||||
Vector ref;
|
Vector ref;
|
||||||
DoEqualLenTicks(canvas, hcs,
|
DoEqualLenTicks(canvas, hcs,
|
||||||
SK.GetEntity(line->point[0])->PointGetNum(),
|
SK.GetEntity(line->point[0])->PointGetDrawNum(),
|
||||||
SK.GetEntity(line->point[1])->PointGetNum(),
|
SK.GetEntity(line->point[1])->PointGetDrawNum(),
|
||||||
gn, &ref);
|
gn, &ref);
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
|
DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
|
||||||
|
@ -990,6 +1035,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
DoProjectedPoint(canvas, hcs, &b);
|
DoProjectedPoint(canvas, hcs, &b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
Vector offset = e->ExplodeOffset();
|
||||||
|
a = a.Plus(offset);
|
||||||
|
b = b.Plus(offset);
|
||||||
|
}
|
||||||
|
|
||||||
Vector ref;
|
Vector ref;
|
||||||
DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
|
DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
|
||||||
if(refs) refs->push_back(ref);
|
if(refs) refs->push_back(ref);
|
||||||
|
@ -1000,7 +1051,42 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case Type::ARC_ARC_LEN_RATIO:
|
||||||
|
case Type::ARC_ARC_DIFFERENCE: {
|
||||||
|
Entity *circle = SK.GetEntity(entityA);
|
||||||
|
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
||||||
|
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
|
||||||
|
Vector n = q.RotationN().WithMagnitude(1);
|
||||||
|
|
||||||
|
Vector ref2;
|
||||||
|
DoEqualRadiusTicks(canvas, hcs, entityA, &ref2);
|
||||||
|
DoEqualRadiusTicks(canvas, hcs, entityB, &ref2);
|
||||||
|
|
||||||
|
Vector ref = center.Plus(disp.offset);
|
||||||
|
// Force the label into the same plane as the circle.
|
||||||
|
ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center)));
|
||||||
|
if(refs) refs->push_back(ref);
|
||||||
|
Vector topLeft;
|
||||||
|
DoLabel(canvas, hcs, ref, &topLeft, gr, gu);
|
||||||
|
if(labelPos) *labelPos = topLeft;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Type::ARC_LINE_LEN_RATIO:
|
||||||
|
case Type::ARC_LINE_DIFFERENCE: {
|
||||||
|
Vector a, b = Vector::From(0, 0, 0);
|
||||||
|
Vector ref;
|
||||||
|
Entity *e = SK.GetEntity(entityA);
|
||||||
|
a = SK.GetEntity(e->point[0])->PointGetNum();
|
||||||
|
b = SK.GetEntity(e->point[1])->PointGetNum();
|
||||||
|
DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
|
||||||
|
if(refs) refs->push_back(ref);
|
||||||
|
DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
|
||||||
|
if(refs) refs->push_back(ref);
|
||||||
|
ref = ((a.Plus(b)).ScaledBy(0.5)).Plus(disp.offset);
|
||||||
|
DoLabel(canvas, hcs, ref, labelPos, gr, gu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
case Type::EQ_LEN_PT_LINE_D: {
|
case Type::EQ_LEN_PT_LINE_D: {
|
||||||
Entity *forLen = SK.GetEntity(entityA);
|
Entity *forLen = SK.GetEntity(entityA);
|
||||||
Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(),
|
Vector a = SK.GetEntity(forLen->point[0])->PointGetNum(),
|
||||||
|
@ -1009,6 +1095,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
DoProjectedPoint(canvas, hcs, &a);
|
DoProjectedPoint(canvas, hcs, &a);
|
||||||
DoProjectedPoint(canvas, hcs, &b);
|
DoProjectedPoint(canvas, hcs, &b);
|
||||||
}
|
}
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
Vector offset = forLen->ExplodeOffset();
|
||||||
|
a = a.Plus(offset);
|
||||||
|
b = b.Plus(offset);
|
||||||
|
}
|
||||||
Vector refa;
|
Vector refa;
|
||||||
DoEqualLenTicks(canvas, hcs, a, b, gn, &refa);
|
DoEqualLenTicks(canvas, hcs, a, b, gn, &refa);
|
||||||
if(refs) refs->push_back(refa);
|
if(refs) refs->push_back(refa);
|
||||||
|
@ -1024,6 +1115,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
|
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
Vector offset = SK.GetEntity(ptA)->ExplodeOffset();
|
||||||
|
pt = pt.Plus(offset);
|
||||||
|
closest = closest.Plus(offset);
|
||||||
|
}
|
||||||
DoLine(canvas, hcs, pt, closest);
|
DoLine(canvas, hcs, pt, closest);
|
||||||
Vector refb;
|
Vector refb;
|
||||||
DoEqualLenTicks(canvas, hcs, pt, closest, gn, &refb);
|
DoEqualLenTicks(canvas, hcs, pt, closest, gn, &refb);
|
||||||
|
@ -1046,6 +1142,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
|
Vector closest = pt.ClosestPointOnLine(la, lb.Minus(la));
|
||||||
|
if(ShouldDrawExploded()) {
|
||||||
|
Vector offset = pte->ExplodeOffset();
|
||||||
|
pt = pt.Plus(offset);
|
||||||
|
closest = closest.Plus(offset);
|
||||||
|
}
|
||||||
DoLine(canvas, hcs, pt, closest);
|
DoLine(canvas, hcs, pt, closest);
|
||||||
|
|
||||||
Vector ref;
|
Vector ref;
|
||||||
|
@ -1075,8 +1176,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
||||||
goto s;
|
goto s;
|
||||||
}
|
}
|
||||||
s:
|
s:
|
||||||
Vector a = SK.GetEntity(ptA)->PointGetNum();
|
Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||||
Vector b = SK.GetEntity(ptB)->PointGetNum();
|
Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
|
||||||
|
|
||||||
for(int i = 0; i < 2; i++) {
|
for(int i = 0; i < 2; i++) {
|
||||||
Vector tail = (i == 0) ? a : b;
|
Vector tail = (i == 0) ? a : b;
|
||||||
|
@ -1113,8 +1214,8 @@ s:
|
||||||
}
|
}
|
||||||
// For "at midpoint", this branch is always taken.
|
// For "at midpoint", this branch is always taken.
|
||||||
Entity *e = SK.GetEntity(entityA);
|
Entity *e = SK.GetEntity(entityA);
|
||||||
Vector a = SK.GetEntity(e->point[0])->PointGetNum();
|
Vector a = SK.GetEntity(e->point[0])->PointGetDrawNum();
|
||||||
Vector b = SK.GetEntity(e->point[1])->PointGetNum();
|
Vector b = SK.GetEntity(e->point[1])->PointGetDrawNum();
|
||||||
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
|
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
|
||||||
Vector offset = (a.Minus(b)).Cross(n);
|
Vector offset = (a.Minus(b)).Cross(n);
|
||||||
offset = offset.WithMagnitude(textHeight);
|
offset = offset.WithMagnitude(textHeight);
|
||||||
|
@ -1138,8 +1239,8 @@ s:
|
||||||
r.WithMagnitude(1), u.WithMagnitude(1), hcs);
|
r.WithMagnitude(1), u.WithMagnitude(1), hcs);
|
||||||
if(refs) refs->push_back(o);
|
if(refs) refs->push_back(o);
|
||||||
} else {
|
} else {
|
||||||
Vector a = SK.GetEntity(ptA)->PointGetNum();
|
Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||||
Vector b = SK.GetEntity(ptB)->PointGetNum();
|
Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
|
||||||
|
|
||||||
Entity *w = SK.GetEntity(workplane);
|
Entity *w = SK.GetEntity(workplane);
|
||||||
Vector cu = w->Normal()->NormalU();
|
Vector cu = w->Normal()->NormalU();
|
||||||
|
@ -1189,8 +1290,13 @@ s:
|
||||||
}
|
}
|
||||||
hcs = canvas->GetStroke(stroke);
|
hcs = canvas->GetStroke(stroke);
|
||||||
}
|
}
|
||||||
DoLabel(canvas, hcs, disp.offset, labelPos, u, v);
|
Vector ref = disp.offset;
|
||||||
if(refs) refs->push_back(disp.offset);
|
if(ptA.v) {
|
||||||
|
Vector a = SK.GetEntity(ptA)->PointGetNum();
|
||||||
|
ref = a.Plus(disp.offset);
|
||||||
|
}
|
||||||
|
DoLabel(canvas, hcs, ref, labelPos, u, v);
|
||||||
|
if(refs) refs->push_back(ref);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1238,7 +1344,11 @@ bool Constraint::HasLabel() const {
|
||||||
case Type::PT_FACE_DISTANCE:
|
case Type::PT_FACE_DISTANCE:
|
||||||
case Type::PROJ_PT_DISTANCE:
|
case Type::PROJ_PT_DISTANCE:
|
||||||
case Type::LENGTH_RATIO:
|
case Type::LENGTH_RATIO:
|
||||||
|
case Type::ARC_ARC_LEN_RATIO:
|
||||||
|
case Type::ARC_LINE_LEN_RATIO:
|
||||||
case Type::LENGTH_DIFFERENCE:
|
case Type::LENGTH_DIFFERENCE:
|
||||||
|
case Type::ARC_ARC_DIFFERENCE:
|
||||||
|
case Type::ARC_LINE_DIFFERENCE:
|
||||||
case Type::DIAMETER:
|
case Type::DIAMETER:
|
||||||
case Type::ANGLE:
|
case Type::ANGLE:
|
||||||
return true;
|
return true;
|
||||||
|
@ -1247,3 +1357,7 @@ bool Constraint::HasLabel() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Constraint::ShouldDrawExploded() const {
|
||||||
|
return SK.GetGroup(group)->ShouldDrawExploded();
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ void Entity::GenerateEdges(SEdgeList *el) {
|
||||||
List<Vector> lv = {};
|
List<Vector> lv = {};
|
||||||
sb->MakePwlInto(&lv);
|
sb->MakePwlInto(&lv);
|
||||||
for(int j = 1; j < lv.n; j++) {
|
for(int j = 1; j < lv.n; j++) {
|
||||||
el->AddEdge(lv[j-1], lv[j], style.v, i);
|
el->AddEdge(lv[j-1], lv[j], Style::ForEntity(h).v, i);
|
||||||
}
|
}
|
||||||
lv.Clear();
|
lv.Clear();
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
|
||||||
case Type::POINT_N_ROT_AXIS_TRANS:
|
case Type::POINT_N_ROT_AXIS_TRANS:
|
||||||
case Type::POINT_IN_3D:
|
case Type::POINT_IN_3D:
|
||||||
case Type::POINT_IN_2D:
|
case Type::POINT_IN_2D:
|
||||||
refs->push_back(PointGetNum());
|
refs->push_back(PointGetDrawNum());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::NORMAL_N_COPY:
|
case Type::NORMAL_N_COPY:
|
||||||
|
@ -103,12 +103,12 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
|
||||||
case Type::CUBIC_PERIODIC:
|
case Type::CUBIC_PERIODIC:
|
||||||
case Type::TTF_TEXT:
|
case Type::TTF_TEXT:
|
||||||
case Type::IMAGE:
|
case Type::IMAGE:
|
||||||
refs->push_back(SK.GetEntity(point[0])->PointGetNum());
|
refs->push_back(SK.GetEntity(point[0])->PointGetDrawNum());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type::LINE_SEGMENT: {
|
case Type::LINE_SEGMENT: {
|
||||||
Vector a = SK.GetEntity(point[0])->PointGetNum(),
|
Vector a = SK.GetEntity(point[0])->PointGetDrawNum(),
|
||||||
b = SK.GetEntity(point[1])->PointGetNum();
|
b = SK.GetEntity(point[1])->PointGetDrawNum();
|
||||||
refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
|
refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -466,6 +466,26 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entity::ShouldDrawExploded() const {
|
||||||
|
return SK.GetGroup(group)->ShouldDrawExploded();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector Entity::ExplodeOffset() const {
|
||||||
|
if(ShouldDrawExploded() && workplane.v != 0) {
|
||||||
|
int requestIdx = SK.GetRequest(h.request())->groupRequestIndex;
|
||||||
|
double offset = SS.explodeDistance * (requestIdx + 1);
|
||||||
|
return SK.GetEntity(workplane)->Normal()->NormalN().ScaledBy(offset);
|
||||||
|
} else {
|
||||||
|
return Vector::From(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector Entity::PointGetDrawNum() const {
|
||||||
|
// As per EntityBase::PointGetNum but specifically for when drawing/rendering the point
|
||||||
|
// (and not when solving), so we can potentially draw it somewhere different
|
||||||
|
return PointGetNum().Plus(ExplodeOffset());
|
||||||
|
}
|
||||||
|
|
||||||
void Entity::Draw(DrawAs how, Canvas *canvas) {
|
void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
|
if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
|
||||||
!IsVisible()) return;
|
!IsVisible()) return;
|
||||||
|
@ -557,16 +577,17 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
pointStroke.unit = Canvas::Unit::PX;
|
pointStroke.unit = Canvas::Unit::PX;
|
||||||
Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
|
Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
|
||||||
|
|
||||||
|
Vector p = PointGetDrawNum();
|
||||||
if(free) {
|
if(free) {
|
||||||
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
|
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
|
||||||
analyzeStroke.width = 14.0;
|
analyzeStroke.width = 14.0;
|
||||||
analyzeStroke.layer = Canvas::Layer::FRONT;
|
analyzeStroke.layer = Canvas::Layer::FRONT;
|
||||||
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
|
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
|
||||||
|
|
||||||
canvas->DrawPoint(PointGetNum(), hcsAnalyze);
|
canvas->DrawPoint(p, hcsAnalyze);
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas->DrawPoint(PointGetNum(), hcsPoint);
|
canvas->DrawPoint(p, hcsPoint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,7 +642,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
tail = camera.projRight.ScaledBy(w/s).Plus(
|
tail = camera.projRight.ScaledBy(w/s).Plus(
|
||||||
camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
|
camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
|
||||||
} else {
|
} else {
|
||||||
tail = SK.GetEntity(point[0])->PointGetNum();
|
tail = SK.GetEntity(point[0])->PointGetDrawNum();
|
||||||
}
|
}
|
||||||
tail = camera.AlignToPixelGrid(tail);
|
tail = camera.AlignToPixelGrid(tail);
|
||||||
|
|
||||||
|
@ -709,8 +730,32 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
case Type::TTF_TEXT: {
|
case Type::TTF_TEXT: {
|
||||||
// Generate the rational polynomial curves, then piecewise linearize
|
// Generate the rational polynomial curves, then piecewise linearize
|
||||||
// them, and display those.
|
// them, and display those.
|
||||||
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcs)) {
|
// Calculating the draw offset, if necessary.
|
||||||
canvas->DrawEdges(*GetOrGenerateEdges(), hcs);
|
const bool shouldExplode = ShouldDrawExploded();
|
||||||
|
Vector explodeOffset;
|
||||||
|
SBezierList offsetBeziers = {};
|
||||||
|
SBezierList *beziers = GetOrGenerateBezierCurves();
|
||||||
|
if(shouldExplode) {
|
||||||
|
explodeOffset = ExplodeOffset();
|
||||||
|
for(const SBezier& b : beziers->l) {
|
||||||
|
SBezier offset = b.TransformedBy(explodeOffset, Quaternion::IDENTITY, 1.0);
|
||||||
|
offsetBeziers.l.Add(&offset);
|
||||||
|
}
|
||||||
|
beziers = &offsetBeziers;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEdgeList *edges = nullptr;
|
||||||
|
SEdgeList offsetEdges = {};
|
||||||
|
|
||||||
|
if(!canvas->DrawBeziers(*beziers, hcs)) {
|
||||||
|
edges = GetOrGenerateEdges();
|
||||||
|
if(shouldExplode) {
|
||||||
|
for(const SEdge &e : edges->l) {
|
||||||
|
offsetEdges.AddEdge(e.a.Plus(explodeOffset), e.b.Plus(explodeOffset), e.auxA, e.auxB, e.tag);
|
||||||
|
}
|
||||||
|
edges = &offsetEdges;
|
||||||
|
}
|
||||||
|
canvas->DrawEdges(*edges, hcs);
|
||||||
}
|
}
|
||||||
if(type == Type::CIRCLE) {
|
if(type == Type::CIRCLE) {
|
||||||
Entity *dist = SK.GetEntity(distance);
|
Entity *dist = SK.GetEntity(distance);
|
||||||
|
@ -720,12 +765,14 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
|
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
|
||||||
analyzeStroke.layer = Canvas::Layer::FRONT;
|
analyzeStroke.layer = Canvas::Layer::FRONT;
|
||||||
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
|
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
|
||||||
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcsAnalyze)) {
|
if(!canvas->DrawBeziers(*beziers, hcsAnalyze)) {
|
||||||
canvas->DrawEdges(*GetOrGenerateEdges(), hcsAnalyze);
|
canvas->DrawEdges(*edges, hcsAnalyze);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
offsetBeziers.Clear();
|
||||||
|
offsetEdges.Clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Type::IMAGE: {
|
case Type::IMAGE: {
|
||||||
|
@ -757,7 +804,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
||||||
Canvas::hFill hf = canvas->GetFill(fill);
|
Canvas::hFill hf = canvas->GetFill(fill);
|
||||||
Vector v[4] = {};
|
Vector v[4] = {};
|
||||||
for(int i = 0; i < 4; i++) {
|
for(int i = 0; i < 4; i++) {
|
||||||
v[i] = SK.GetEntity(point[i])->PointGetNum();
|
v[i] = SK.GetEntity(point[i])->PointGetDrawNum();
|
||||||
}
|
}
|
||||||
Vector iu = v[3].Minus(v[0]);
|
Vector iu = v[3].Minus(v[0]);
|
||||||
Vector iv = v[1].Minus(v[0]);
|
Vector iv = v[1].Minus(v[0]);
|
||||||
|
|
260
src/dsc.h
|
@ -10,6 +10,7 @@
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/// Trait indicating which types are handle types and should get the associated operators.
|
/// Trait indicating which types are handle types and should get the associated operators.
|
||||||
/// Specialize for each handle type and inherit from std::true_type.
|
/// Specialize for each handle type and inherit from std::true_type.
|
||||||
|
@ -371,15 +372,28 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class T, class H> class IdList;
|
||||||
|
|
||||||
// Comparison functor used by IdList and related classes
|
// Comparison functor used by IdList and related classes
|
||||||
template <class T, class H>
|
template <class T, class H>
|
||||||
struct CompareId {
|
struct CompareId {
|
||||||
bool operator()(T const& lhs, T const& rhs) const {
|
|
||||||
return lhs.h.v < rhs.h.v;
|
CompareId(const IdList<T, H> *list) {
|
||||||
|
idlist = list;
|
||||||
}
|
}
|
||||||
bool operator()(T const& lhs, H rhs) const {
|
|
||||||
return lhs.h.v < rhs.v;
|
bool operator()(int lhs, T const& rhs) const {
|
||||||
|
return idlist->elemstore[lhs].h.v < rhs.h.v;
|
||||||
}
|
}
|
||||||
|
bool operator()(int lhs, H rhs) const {
|
||||||
|
return idlist->elemstore[lhs].h.v < rhs.v;
|
||||||
|
}
|
||||||
|
bool operator()(T *lhs, int rhs) const {
|
||||||
|
return lhs->h.v < idlist->elemstore[rhs].h.v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const IdList<T, H> *idlist;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A list, where each element has an integer identifier. The list is kept
|
// A list, where each element has an integer identifier. The list is kept
|
||||||
|
@ -387,148 +401,152 @@ struct CompareId {
|
||||||
// id.
|
// id.
|
||||||
template <class T, class H>
|
template <class T, class H>
|
||||||
class IdList {
|
class IdList {
|
||||||
T *elem = nullptr;
|
std::vector<T> elemstore;
|
||||||
int elemsAllocated = 0;
|
std::vector<int> elemidx;
|
||||||
|
std::vector<int> freelist;
|
||||||
public:
|
public:
|
||||||
int n = 0;
|
int n = 0; // PAR@@@@@ make this private to see all interesting and suspicious places in SoveSpace ;-)
|
||||||
|
|
||||||
|
friend struct CompareId<T, H>;
|
||||||
using Compare = CompareId<T, H>;
|
using Compare = CompareId<T, H>;
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
typedef std::random_access_iterator_tag iterator_category;
|
||||||
|
typedef T value_type;
|
||||||
|
typedef int difference_type;
|
||||||
|
typedef T *pointer;
|
||||||
|
typedef T &reference;
|
||||||
|
|
||||||
|
public:
|
||||||
|
T &operator*() const noexcept { return *elem; }
|
||||||
|
const T *operator->() const noexcept { return elem; }
|
||||||
|
|
||||||
|
bool operator==(const iterator &p) const { return p.position == position; }
|
||||||
|
bool operator!=(const iterator &p) const { return !operator==(p); }
|
||||||
|
|
||||||
|
iterator &operator++() {
|
||||||
|
++position;
|
||||||
|
if(position >= (int)list->elemidx.size()) {
|
||||||
|
elem = nullptr; // PAR@@@@ Remove just debugging
|
||||||
|
} else if(0 <= position) {
|
||||||
|
elem = &(list->elemstore[list->elemidx[position]]);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed for std:find_if of gcc used in entity.cpp GenerateEquations
|
||||||
|
difference_type operator-(const iterator &rhs) const noexcept {
|
||||||
|
return position - rhs.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator(IdList<T, H> *l) : position(0), list(l) {
|
||||||
|
if(list) {
|
||||||
|
if(list->elemstore.size() && list->elemidx.size()) {
|
||||||
|
elem = &(list->elemstore[list->elemidx[position]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
iterator(IdList<T, H> *l, int pos) : position(pos), list(l) {
|
||||||
|
if(position >= (int)list->elemidx.size()) {
|
||||||
|
elem = nullptr;
|
||||||
|
} else if(0 <= position) {
|
||||||
|
elem = &((list->elemstore)[list->elemidx[position]]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
int position;
|
||||||
|
T *elem;
|
||||||
|
IdList<T, H> *list;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
bool IsEmpty() const {
|
bool IsEmpty() const {
|
||||||
return n == 0;
|
return n == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AllocForOneMore() {
|
|
||||||
if(n >= elemsAllocated) {
|
|
||||||
ReserveMore((elemsAllocated + 32)*2 - n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t MaximumId() {
|
uint32_t MaximumId() {
|
||||||
if(IsEmpty()) {
|
if(IsEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return Last()->h.v;
|
return elemstore[elemidx.back()].h.v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
H AddAndAssignId(T *t) {
|
H AddAndAssignId(T *t) {
|
||||||
t->h.v = (MaximumId() + 1);
|
t->h.v = (MaximumId() + 1);
|
||||||
AllocForOneMore();
|
|
||||||
|
|
||||||
// Copy-construct at the end of the list.
|
// Add at the end of the list.
|
||||||
new(&elem[n]) T(*t);
|
elemstore.push_back(*t);
|
||||||
|
elemidx.push_back(elemstore.size()-1);
|
||||||
++n;
|
++n;
|
||||||
|
|
||||||
return t->h;
|
return t->h;
|
||||||
}
|
}
|
||||||
|
|
||||||
T * LowerBound(T const& t) {
|
|
||||||
if(IsEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto it = std::lower_bound(begin(), end(), t, Compare());
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
T * LowerBound(H const& h) {
|
|
||||||
if(IsEmpty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto it = std::lower_bound(begin(), end(), h, Compare());
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LowerBoundIndex(T const& t) {
|
|
||||||
if(IsEmpty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
auto it = LowerBound(t);
|
|
||||||
auto idx = std::distance(begin(), it);
|
|
||||||
auto i = static_cast<int>(idx);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
void ReserveMore(int howMuch) {
|
void ReserveMore(int howMuch) {
|
||||||
if(n + howMuch > elemsAllocated) {
|
elemstore.reserve(elemstore.size() + howMuch);
|
||||||
elemsAllocated = n + howMuch;
|
elemidx.reserve(elemidx.size() + howMuch);
|
||||||
T *newElem = (T *)::operator new[]((size_t)elemsAllocated*sizeof(T));
|
// freelist.reserve(freelist.size() + howMuch); // PAR@@@@ maybe we should - not much more RAM
|
||||||
for(int i = 0; i < n; i++) {
|
|
||||||
new(&newElem[i]) T(std::move(elem[i]));
|
|
||||||
elem[i].~T();
|
|
||||||
}
|
|
||||||
::operator delete[](elem);
|
|
||||||
elem = newElem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Add(T *t) {
|
void Add(T *t) {
|
||||||
AllocForOneMore();
|
|
||||||
|
|
||||||
// Look to see if we already have something with the same handle value.
|
// Look to see if we already have something with the same handle value.
|
||||||
ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique");
|
ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique");
|
||||||
|
|
||||||
// Copy-construct at the end of the list.
|
// Find out where the added element should be.
|
||||||
new(&elem[n]) T(*t);
|
auto pos = std::lower_bound(elemidx.begin(), elemidx.end(), *t, Compare(this));
|
||||||
|
|
||||||
|
if(freelist.empty()) { // Add a new element to the store
|
||||||
|
elemstore.push_back(*t);
|
||||||
|
// Insert a pointer to the element at the correct position
|
||||||
|
if(elemidx.empty()) {
|
||||||
|
// The list is empty so pos, begin and end are all null.
|
||||||
|
// insert does not work in this case.
|
||||||
|
elemidx.push_back(elemstore.size()-1);
|
||||||
|
} else {
|
||||||
|
elemidx.insert(pos, elemstore.size() - 1);
|
||||||
|
}
|
||||||
|
} else { // Use the last element from the freelist
|
||||||
|
// Insert an index to the element at the correct position
|
||||||
|
elemidx.insert(pos, freelist.back());
|
||||||
|
// Remove the element from the freelist
|
||||||
|
freelist.pop_back();
|
||||||
|
|
||||||
|
// Copy-construct to the element storage.
|
||||||
|
elemstore[*pos] = T(*t);
|
||||||
|
// *elemptr[pos] = *t; // PAR@@@@@@ maybe this?
|
||||||
|
}
|
||||||
|
|
||||||
++n;
|
++n;
|
||||||
// The item we just added is trivially sorted, so "merge"
|
|
||||||
std::inplace_merge(begin(), end() - 1, end(), Compare());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
T *FindById(H h) {
|
T *FindById(H h) {
|
||||||
T *t = FindByIdNoOops(h);
|
T *t = FindByIdNoOops(h);
|
||||||
ssassert(t != NULL, "Cannot find handle");
|
ssassert(t != nullptr, "Cannot find handle");
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IndexOf(H h) {
|
|
||||||
if(IsEmpty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
auto it = LowerBound(h);
|
|
||||||
auto idx = std::distance(begin(), it);
|
|
||||||
if (idx < n) {
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
T *FindByIdNoOops(H h) {
|
T *FindByIdNoOops(H h) {
|
||||||
if(IsEmpty()) {
|
if(IsEmpty()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto it = LowerBound(h);
|
auto it = std::lower_bound(elemidx.begin(), elemidx.end(), h, Compare(this));
|
||||||
if (it == nullptr || it == end()) {
|
if(it == elemidx.end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
if(elemstore[*it].h.v != h.v) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &elemstore[*it];
|
||||||
}
|
}
|
||||||
if (it->h.v == h.v) {
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
T *First() {
|
T &Get(size_t i) { return elemstore[elemidx[i]]; }
|
||||||
return (IsEmpty()) ? NULL : &(elem[0]);
|
|
||||||
}
|
|
||||||
T *Last() {
|
|
||||||
return (IsEmpty()) ? NULL : &(elem[n-1]);
|
|
||||||
}
|
|
||||||
T *NextAfter(T *prev) {
|
|
||||||
if(IsEmpty() || !prev) return NULL;
|
|
||||||
if(prev - First() == (n - 1)) return NULL;
|
|
||||||
return prev + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
T &Get(size_t i) { return elem[i]; }
|
|
||||||
T const &Get(size_t i) const { return elem[i]; }
|
|
||||||
T &operator[](size_t i) { return Get(i); }
|
T &operator[](size_t i) { return Get(i); }
|
||||||
T const &operator[](size_t i) const { return Get(i); }
|
|
||||||
|
|
||||||
T *begin() { return IsEmpty() ? nullptr : &elem[0]; }
|
iterator begin() { return IsEmpty() ? nullptr : iterator(this); }
|
||||||
T *end() { return IsEmpty() ? nullptr : &elem[0] + n; }
|
iterator end() { return IsEmpty() ? nullptr : iterator(this, elemidx.size()); }
|
||||||
const T *begin() const { return IsEmpty() ? nullptr : &elem[0]; }
|
|
||||||
const T *end() const { return IsEmpty() ? nullptr : &elem[0] + n; }
|
|
||||||
const T *cbegin() const { return begin(); }
|
|
||||||
const T *cend() const { return end(); }
|
|
||||||
|
|
||||||
void ClearTags() {
|
void ClearTags() {
|
||||||
for(auto &elt : *this) { elt.tag = 0; }
|
for(auto &elt : *this) { elt.tag = 0; }
|
||||||
|
@ -545,22 +563,23 @@ public:
|
||||||
int src, dest;
|
int src, dest;
|
||||||
dest = 0;
|
dest = 0;
|
||||||
for(src = 0; src < n; src++) {
|
for(src = 0; src < n; src++) {
|
||||||
if(elem[src].tag) {
|
if(elemstore[elemidx[src]].tag) {
|
||||||
// this item should be deleted
|
// this item should be deleted
|
||||||
elem[src].Clear();
|
elemstore[elemidx[src]].Clear();
|
||||||
|
// elemstore[elemidx[src]].~T(); // Clear below calls the destructors
|
||||||
|
freelist.push_back(elemidx[src]);
|
||||||
|
elemidx[src] = 0xDEADBEEF; // PAR@@@@@ just for debugging, not needed, remove later
|
||||||
} else {
|
} else {
|
||||||
if(src != dest) {
|
if(src != dest) {
|
||||||
elem[dest] = elem[src];
|
elemidx[dest] = elemidx[src];
|
||||||
}
|
}
|
||||||
dest++;
|
dest++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(int i = dest; i < n; i++)
|
|
||||||
elem[i].~T();
|
|
||||||
n = dest;
|
n = dest;
|
||||||
// and elemsAllocated is untouched, because we didn't resize
|
elemidx.resize(n); // Clear left over elements at the end.
|
||||||
}
|
}
|
||||||
void RemoveById(H h) {
|
void RemoveById(H h) { // PAR@@@@@ this can be optimized
|
||||||
ClearTags();
|
ClearTags();
|
||||||
FindById(h)->tag = 1;
|
FindById(h)->tag = 1;
|
||||||
RemoveTagged();
|
RemoveTagged();
|
||||||
|
@ -568,28 +587,35 @@ public:
|
||||||
|
|
||||||
void MoveSelfInto(IdList<T,H> *l) {
|
void MoveSelfInto(IdList<T,H> *l) {
|
||||||
l->Clear();
|
l->Clear();
|
||||||
std::swap(l->elem, elem);
|
std::swap(l->elemstore, elemstore);
|
||||||
std::swap(l->elemsAllocated, elemsAllocated);
|
std::swap(l->elemidx, elemidx);
|
||||||
|
std::swap(l->freelist, freelist);
|
||||||
std::swap(l->n, n);
|
std::swap(l->n, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeepCopyInto(IdList<T,H> *l) {
|
void DeepCopyInto(IdList<T,H> *l) {
|
||||||
l->Clear();
|
l->Clear();
|
||||||
l->elem = (T *)::operator new[](elemsAllocated * sizeof(elem[0]));
|
|
||||||
for(int i = 0; i < n; i++)
|
for(auto const &it : elemstore) {
|
||||||
new(&l->elem[i]) T(elem[i]);
|
l->elemstore.push_back(it);
|
||||||
l->elemsAllocated = elemsAllocated;
|
}
|
||||||
|
|
||||||
|
for(auto const &it : elemidx) {
|
||||||
|
l->elemidx.push_back(it);
|
||||||
|
}
|
||||||
|
|
||||||
l->n = n;
|
l->n = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear() {
|
void Clear() {
|
||||||
for(int i = 0; i < n; i++) {
|
for(auto &it : elemidx) {
|
||||||
elem[i].Clear();
|
elemstore[it].Clear();
|
||||||
elem[i].~T();
|
// elemstore[it].~T(); // clear below calls the destructors
|
||||||
}
|
}
|
||||||
if(elem) ::operator delete[](elem);
|
freelist.clear();
|
||||||
elem = NULL;
|
elemidx.clear();
|
||||||
elemsAllocated = n = 0;
|
elemstore.clear();
|
||||||
|
n = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -366,9 +366,9 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
|
||||||
|
|
||||||
// And calculate lighting for the triangle
|
// And calculate lighting for the triangle
|
||||||
Vector n = tt.Normal().WithMagnitude(1);
|
Vector n = tt.Normal().WithMagnitude(1);
|
||||||
double lighting = SS.ambientIntensity +
|
double lighting = min(1.0, SS.ambientIntensity +
|
||||||
max(0.0, (SS.lightIntensity[0])*(n.Dot(l0))) +
|
max(0.0, (SS.lightIntensity[0])*(n.Dot(l0))) +
|
||||||
max(0.0, (SS.lightIntensity[1])*(n.Dot(l1)));
|
max(0.0, (SS.lightIntensity[1])*(n.Dot(l1))));
|
||||||
double r = min(1.0, tt.meta.color.redF() * lighting),
|
double r = min(1.0, tt.meta.color.redF() * lighting),
|
||||||
g = min(1.0, tt.meta.color.greenF() * lighting),
|
g = min(1.0, tt.meta.color.greenF() * lighting),
|
||||||
b = min(1.0, tt.meta.color.blueF() * lighting);
|
b = min(1.0, tt.meta.color.blueF() * lighting);
|
||||||
|
|
|
@ -353,22 +353,21 @@ void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
|
||||||
|
|
||||||
advancedFaces = {};
|
advancedFaces = {};
|
||||||
|
|
||||||
SSurface *ss;
|
for(SSurface &ss : shell->surface) {
|
||||||
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
|
if(ss.trim.IsEmpty())
|
||||||
if(ss->trim.IsEmpty())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Get all of the loops of Beziers that trim our surface (with each
|
// Get all of the loops of Beziers that trim our surface (with each
|
||||||
// Bezier split so that we use the section as t goes from 0 to 1), and
|
// Bezier split so that we use the section as t goes from 0 to 1), and
|
||||||
// the piecewise linearization of those loops in xyz space.
|
// the piecewise linearization of those loops in xyz space.
|
||||||
SBezierList sbl = {};
|
SBezierList sbl = {};
|
||||||
ss->MakeSectionEdgesInto(shell, NULL, &sbl);
|
ss.MakeSectionEdgesInto(shell, NULL, &sbl);
|
||||||
|
|
||||||
// Apply the export scale factor.
|
// Apply the export scale factor.
|
||||||
ss->ScaleSelfBy(1.0/SS.exportScale);
|
ss.ScaleSelfBy(1.0/SS.exportScale);
|
||||||
sbl.ScaleSelfBy(1.0/SS.exportScale);
|
sbl.ScaleSelfBy(1.0/SS.exportScale);
|
||||||
|
|
||||||
ExportSurface(ss, &sbl);
|
ExportSurface(&ss, &sbl);
|
||||||
|
|
||||||
sbl.Clear();
|
sbl.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,22 +170,21 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(writer->constraint) {
|
if(writer->constraint) {
|
||||||
Constraint *c;
|
for(Constraint &c : *writer->constraint) {
|
||||||
for(c = writer->constraint->First(); c; c = writer->constraint->NextAfter(c)) {
|
if(!writer->NeedToOutput(&c)) continue;
|
||||||
if(!writer->NeedToOutput(c)) continue;
|
switch(c.type) {
|
||||||
switch(c->type) {
|
|
||||||
case Constraint::Type::PT_PT_DISTANCE: {
|
case Constraint::Type::PT_PT_DISTANCE: {
|
||||||
Vector ap = SK.GetEntity(c->ptA)->PointGetNum();
|
Vector ap = SK.GetEntity(c.ptA)->PointGetNum();
|
||||||
Vector bp = SK.GetEntity(c->ptB)->PointGetNum();
|
Vector bp = SK.GetEntity(c.ptB)->PointGetNum();
|
||||||
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(c->disp.offset);
|
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(c.disp.offset);
|
||||||
writeAlignedDimension(xfrm(ap), xfrm(bp), xfrm(ref),
|
writeAlignedDimension(xfrm(ap), xfrm(bp), xfrm(ref),
|
||||||
xfrm(ref), c->Label(), c->GetStyle(), c->valA);
|
xfrm(ref), c.Label(), c.GetStyle(), c.valA);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Constraint::Type::PT_LINE_DISTANCE: {
|
case Constraint::Type::PT_LINE_DISTANCE: {
|
||||||
Vector pt = SK.GetEntity(c->ptA)->PointGetNum();
|
Vector pt = SK.GetEntity(c.ptA)->PointGetNum();
|
||||||
Entity *line = SK.GetEntity(c->entityA);
|
Entity *line = SK.GetEntity(c.entityA);
|
||||||
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
|
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
|
||||||
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
|
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
|
||||||
Vector dl = lB.Minus(lA);
|
Vector dl = lB.Minus(lA);
|
||||||
|
@ -194,7 +193,7 @@ public:
|
||||||
|
|
||||||
if(pt.Equals(closest)) break;
|
if(pt.Equals(closest)) break;
|
||||||
|
|
||||||
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(c->disp.offset);
|
Vector ref = ((closest.Plus(pt)).ScaledBy(0.5)).Plus(c.disp.offset);
|
||||||
Vector refClosest = ref.ClosestPointOnLine(lA, dl);
|
Vector refClosest = ref.ClosestPointOnLine(lA, dl);
|
||||||
|
|
||||||
double ddl = dl.Dot(dl);
|
double ddl = dl.Dot(dl);
|
||||||
|
@ -209,54 +208,54 @@ public:
|
||||||
|
|
||||||
Vector xdl = xfrm(lB).Minus(xfrm(lA));
|
Vector xdl = xfrm(lB).Minus(xfrm(lA));
|
||||||
writeLinearDimension(xfrm(pt), xfrm(refClosest), xfrm(ref),
|
writeLinearDimension(xfrm(pt), xfrm(refClosest), xfrm(ref),
|
||||||
xfrm(ref), c->Label(),
|
xfrm(ref), c.Label(),
|
||||||
atan2(xdl.y, xdl.x) / PI * 180.0 + 90.0, 0.0,
|
atan2(xdl.y, xdl.x) / PI * 180.0 + 90.0, 0.0,
|
||||||
c->GetStyle(), c->valA);
|
c.GetStyle(), c.valA);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Constraint::Type::DIAMETER: {
|
case Constraint::Type::DIAMETER: {
|
||||||
Entity *circle = SK.GetEntity(c->entityA);
|
Entity *circle = SK.GetEntity(c.entityA);
|
||||||
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
||||||
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
|
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
|
||||||
Vector n = q.RotationN().WithMagnitude(1);
|
Vector n = q.RotationN().WithMagnitude(1);
|
||||||
double r = circle->CircleGetRadiusNum();
|
double r = circle->CircleGetRadiusNum();
|
||||||
|
|
||||||
Vector ref = center.Plus(c->disp.offset);
|
Vector ref = center.Plus(c.disp.offset);
|
||||||
// Force the label into the same plane as the circle.
|
// Force the label into the same plane as the circle.
|
||||||
ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center)));
|
ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center)));
|
||||||
|
|
||||||
Vector rad = ref.Minus(center).WithMagnitude(r);
|
Vector rad = ref.Minus(center).WithMagnitude(r);
|
||||||
if(/*isRadius*/c->other) {
|
if(/*isRadius*/c.other) {
|
||||||
writeRadialDimension(
|
writeRadialDimension(
|
||||||
xfrm(center), xfrm(center.Plus(rad)),
|
xfrm(center), xfrm(center.Plus(rad)),
|
||||||
xfrm(ref), c->Label(), c->GetStyle(), c->valA);
|
xfrm(ref), c.Label(), c.GetStyle(), c.valA);
|
||||||
} else {
|
} else {
|
||||||
writeDiametricDimension(
|
writeDiametricDimension(
|
||||||
xfrm(center.Minus(rad)), xfrm(center.Plus(rad)),
|
xfrm(center.Minus(rad)), xfrm(center.Plus(rad)),
|
||||||
xfrm(ref), c->Label(), c->GetStyle(), c->valA);
|
xfrm(ref), c.Label(), c.GetStyle(), c.valA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Constraint::Type::ANGLE: {
|
case Constraint::Type::ANGLE: {
|
||||||
Entity *a = SK.GetEntity(c->entityA);
|
Entity *a = SK.GetEntity(c.entityA);
|
||||||
Entity *b = SK.GetEntity(c->entityB);
|
Entity *b = SK.GetEntity(c.entityB);
|
||||||
|
|
||||||
Vector a0 = a->VectorGetStartPoint();
|
Vector a0 = a->VectorGetStartPoint();
|
||||||
Vector b0 = b->VectorGetStartPoint();
|
Vector b0 = b->VectorGetStartPoint();
|
||||||
Vector da = a->VectorGetNum();
|
Vector da = a->VectorGetNum();
|
||||||
Vector db = b->VectorGetNum();
|
Vector db = b->VectorGetNum();
|
||||||
if(/*otherAngle*/c->other) {
|
if(/*otherAngle*/c.other) {
|
||||||
a0 = a0.Plus(da);
|
a0 = a0.Plus(da);
|
||||||
da = da.ScaledBy(-1);
|
da = da.ScaledBy(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skew = false;
|
bool skew = false;
|
||||||
Vector ref = c->disp.offset;
|
Vector ref = c.disp.offset;
|
||||||
Vector pi = Vector::AtIntersectionOfLines(a0, a0.Plus(da), b0, b0.Plus(db),
|
Vector pi = Vector::AtIntersectionOfLines(a0, a0.Plus(da), b0, b0.Plus(db),
|
||||||
&skew);
|
&skew);
|
||||||
if(!skew) ref = pi.Plus(c->disp.offset);
|
if(!skew) ref = pi.Plus(c.disp.offset);
|
||||||
|
|
||||||
Vector norm = da.Cross(db);
|
Vector norm = da.Cross(db);
|
||||||
Vector dna = norm.Cross(da).WithMagnitude(1.0);
|
Vector dna = norm.Cross(da).WithMagnitude(1.0);
|
||||||
|
@ -277,7 +276,7 @@ public:
|
||||||
Vector bisect = da.WithMagnitude(1.0).ScaledBy(cos(thetaf / 2.0)).Plus(
|
Vector bisect = da.WithMagnitude(1.0).ScaledBy(cos(thetaf / 2.0)).Plus(
|
||||||
dna.ScaledBy(sin(thetaf / 2.0)));
|
dna.ScaledBy(sin(thetaf / 2.0)));
|
||||||
|
|
||||||
ref = pi.Plus(bisect.WithMagnitude(c->disp.offset.Magnitude()));
|
ref = pi.Plus(bisect.WithMagnitude(c.disp.offset.Magnitude()));
|
||||||
|
|
||||||
// Get lines again to write exact line.
|
// Get lines again to write exact line.
|
||||||
a0 = a->VectorGetStartPoint();
|
a0 = a->VectorGetStartPoint();
|
||||||
|
@ -287,15 +286,15 @@ public:
|
||||||
|
|
||||||
writeAngularDimension(
|
writeAngularDimension(
|
||||||
xfrm(a0), xfrm(a0.Plus(da)), xfrm(b0), xfrm(b0.Plus(db)), xfrm(ref),
|
xfrm(a0), xfrm(a0.Plus(da)), xfrm(b0), xfrm(b0.Plus(db)), xfrm(ref),
|
||||||
xfrm(ref), c->Label(), c->GetStyle(), c->valA);
|
xfrm(ref), c.Label(), c.GetStyle(), c.valA);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Constraint::Type::COMMENT: {
|
case Constraint::Type::COMMENT: {
|
||||||
Style *st = SK.style.FindById(c->GetStyle());
|
Style *st = SK.style.FindById(c.GetStyle());
|
||||||
writeText(xfrm(c->disp.offset), c->Label(),
|
writeText(xfrm(c.disp.offset), c.Label(),
|
||||||
Style::TextHeight(c->GetStyle()) / SS.GW.scale,
|
Style::TextHeight(c.GetStyle()) / SS.GW.scale,
|
||||||
st->textAngle, st->textOrigin, c->GetStyle());
|
st->textAngle, st->textOrigin, c.GetStyle());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1103,7 +1102,7 @@ void SvgFileWriter::StartFile() {
|
||||||
fprintf(f, "stroke-dasharray:%s;\r\n", patternStr.c_str());
|
fprintf(f, "stroke-dasharray:%s;\r\n", patternStr.c_str());
|
||||||
}
|
}
|
||||||
if(s->filled) {
|
if(s->filled) {
|
||||||
fprintf(f, "fill:#%02x%02x%02x;\r\n", fillRgb.red, fillRgb.green, fillRgb.blue);
|
fprintf(f, "fill:#%02x%02x%02x;\r\n", fillRgb.red, fillRgb.green, fillRgb.blue);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fprintf(f, "fill:none;\r\n");
|
fprintf(f, "fill:none;\r\n");
|
||||||
|
@ -1309,9 +1308,9 @@ void GCodeFileWriter::FinishAndCloseFile() {
|
||||||
SS.MmToString(pt->p.x).c_str(), SS.MmToString(pt->p.y).c_str(),
|
SS.MmToString(pt->p.x).c_str(), SS.MmToString(pt->p.y).c_str(),
|
||||||
SS.MmToString(SS.gCode.feed).c_str());
|
SS.MmToString(SS.gCode.feed).c_str());
|
||||||
}
|
}
|
||||||
// Move up to a clearance plane 5mm above the work.
|
// Move up to a clearance plane above the work.
|
||||||
fprintf(f, "G00 Z%s\r\n",
|
fprintf(f, "G00 Z%s\r\n",
|
||||||
SS.MmToString(SS.gCode.depth < 0 ? +5 : -5).c_str());
|
SS.MmToString(SS.gCode.safeHeight).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/file.cpp
|
@ -354,19 +354,18 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SShell *s = &g->runningShell;
|
SShell *s = &g->runningShell;
|
||||||
SSurface *srf;
|
for(SSurface &srf : s->surface) {
|
||||||
for(srf = s->surface.First(); srf; srf = s->surface.NextAfter(srf)) {
|
|
||||||
fprintf(fh, "Surface %08x %08x %08x %d %d\n",
|
fprintf(fh, "Surface %08x %08x %08x %d %d\n",
|
||||||
srf->h.v, srf->color.ToPackedInt(), srf->face, srf->degm, srf->degn);
|
srf.h.v, srf.color.ToPackedInt(), srf.face, srf.degm, srf.degn);
|
||||||
for(i = 0; i <= srf->degm; i++) {
|
for(i = 0; i <= srf.degm; i++) {
|
||||||
for(j = 0; j <= srf->degn; j++) {
|
for(j = 0; j <= srf.degn; j++) {
|
||||||
fprintf(fh, "SCtrl %d %d %.20f %.20f %.20f Weight %20.20f\n",
|
fprintf(fh, "SCtrl %d %d %.20f %.20f %.20f Weight %20.20f\n",
|
||||||
i, j, CO(srf->ctrl[i][j]), srf->weight[i][j]);
|
i, j, CO(srf.ctrl[i][j]), srf.weight[i][j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STrimBy *stb;
|
STrimBy *stb;
|
||||||
for(stb = srf->trim.First(); stb; stb = srf->trim.NextAfter(stb)) {
|
for(stb = srf.trim.First(); stb; stb = srf.trim.NextAfter(stb)) {
|
||||||
fprintf(fh, "TrimBy %08x %d %.20f %.20f %.20f %.20f %.20f %.20f\n",
|
fprintf(fh, "TrimBy %08x %d %.20f %.20f %.20f %.20f %.20f %.20f\n",
|
||||||
stb->curve.v, stb->backwards ? 1 : 0,
|
stb->curve.v, stb->backwards ? 1 : 0,
|
||||||
CO(stb->start), CO(stb->finish));
|
CO(stb->start), CO(stb->finish));
|
||||||
|
@ -374,21 +373,20 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
|
||||||
|
|
||||||
fprintf(fh, "AddSurface\n");
|
fprintf(fh, "AddSurface\n");
|
||||||
}
|
}
|
||||||
SCurve *sc;
|
for(SCurve &sc : s->curve) {
|
||||||
for(sc = s->curve.First(); sc; sc = s->curve.NextAfter(sc)) {
|
|
||||||
fprintf(fh, "Curve %08x %d %d %08x %08x\n",
|
fprintf(fh, "Curve %08x %d %d %08x %08x\n",
|
||||||
sc->h.v,
|
sc.h.v,
|
||||||
sc->isExact ? 1 : 0, sc->exact.deg,
|
sc.isExact ? 1 : 0, sc.exact.deg,
|
||||||
sc->surfA.v, sc->surfB.v);
|
sc.surfA.v, sc.surfB.v);
|
||||||
|
|
||||||
if(sc->isExact) {
|
if(sc.isExact) {
|
||||||
for(i = 0; i <= sc->exact.deg; i++) {
|
for(i = 0; i <= sc.exact.deg; i++) {
|
||||||
fprintf(fh, "CCtrl %d %.20f %.20f %.20f Weight %.20f\n",
|
fprintf(fh, "CCtrl %d %.20f %.20f %.20f Weight %.20f\n",
|
||||||
i, CO(sc->exact.ctrl[i]), sc->exact.weight[i]);
|
i, CO(sc.exact.ctrl[i]), sc.exact.weight[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SCurvePt *scpt;
|
SCurvePt *scpt;
|
||||||
for(scpt = sc->pts.First(); scpt; scpt = sc->pts.NextAfter(scpt)) {
|
for(scpt = sc.pts.First(); scpt; scpt = sc.pts.NextAfter(scpt)) {
|
||||||
fprintf(fh, "CurvePt %d %.20f %.20f %.20f\n",
|
fprintf(fh, "CurvePt %d %.20f %.20f %.20f\n",
|
||||||
scpt->vertex ? 1 : 0, CO(scpt->p));
|
scpt->vertex ? 1 : 0, CO(scpt->p));
|
||||||
}
|
}
|
||||||
|
@ -712,7 +710,9 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityLi
|
||||||
SMesh *m, SShell *sh)
|
SMesh *m, SShell *sh)
|
||||||
{
|
{
|
||||||
if(strcmp(filename.Extension().c_str(), "emn")==0) {
|
if(strcmp(filename.Extension().c_str(), "emn")==0) {
|
||||||
return LinkIDF(filename, le, m, sh);
|
return LinkIDF(filename, le, m, sh);
|
||||||
|
} else if(strcmp(filename.Extension().c_str(), "stl")==0) {
|
||||||
|
return LinkStl(filename, le, m, sh);
|
||||||
} else {
|
} else {
|
||||||
return LoadEntitiesFromSlvs(filename, le, m, sh);
|
return LoadEntitiesFromSlvs(filename, le, m, sh);
|
||||||
}
|
}
|
||||||
|
@ -909,6 +909,8 @@ try_again:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(g.IsTriangleMeshAssembly())
|
||||||
|
g.forceToMesh = true;
|
||||||
} else if(linkMap.count(g.linkFile) == 0) {
|
} else if(linkMap.count(g.linkFile) == 0) {
|
||||||
dbp("Missing file for group: %s", g.name.c_str());
|
dbp("Missing file for group: %s", g.name.c_str());
|
||||||
// The file was moved; prompt the user for its new location.
|
// The file was moved; prompt the user for its new location.
|
||||||
|
|
|
@ -224,9 +224,11 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
|
||||||
if(PruneGroups(hg))
|
if(PruneGroups(hg))
|
||||||
goto pruned;
|
goto pruned;
|
||||||
|
|
||||||
|
int groupRequestIndex = 0;
|
||||||
for(auto &req : SK.request) {
|
for(auto &req : SK.request) {
|
||||||
Request *r = &req;
|
Request *r = &req;
|
||||||
if(r->group != hg) continue;
|
if(r->group != hg) continue;
|
||||||
|
r->groupRequestIndex = groupRequestIndex++;
|
||||||
|
|
||||||
r->Generate(&(SK.entity), &(SK.param));
|
r->Generate(&(SK.entity), &(SK.param));
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,10 +94,12 @@ const MenuEntry Menu[] = {
|
||||||
{ 1, N_("Show Snap &Grid"), Command::SHOW_GRID, '>', KC, mView },
|
{ 1, N_("Show Snap &Grid"), Command::SHOW_GRID, '>', KC, mView },
|
||||||
{ 1, N_("Darken Inactive Solids"), Command::DIM_SOLID_MODEL, 0, KC, mView },
|
{ 1, N_("Darken Inactive Solids"), Command::DIM_SOLID_MODEL, 0, KC, mView },
|
||||||
{ 1, N_("Use &Perspective Projection"), Command::PERSPECTIVE_PROJ, '`', KC, mView },
|
{ 1, N_("Use &Perspective Projection"), Command::PERSPECTIVE_PROJ, '`', KC, mView },
|
||||||
|
{ 1, N_("Show E&xploded View"), Command::EXPLODE_SKETCH, '\\', KC, mView },
|
||||||
{ 1, N_("Dimension &Units"), Command::NONE, 0, KN, NULL },
|
{ 1, N_("Dimension &Units"), Command::NONE, 0, KN, NULL },
|
||||||
{ 2, N_("Dimensions in &Millimeters"), Command::UNITS_MM, 0, KR, mView },
|
{ 2, N_("Dimensions in &Millimeters"), Command::UNITS_MM, 0, KR, mView },
|
||||||
{ 2, N_("Dimensions in M&eters"), Command::UNITS_METERS, 0, KR, mView },
|
{ 2, N_("Dimensions in M&eters"), Command::UNITS_METERS, 0, KR, mView },
|
||||||
{ 2, N_("Dimensions in &Inches"), Command::UNITS_INCHES, 0, KR, mView },
|
{ 2, N_("Dimensions in &Inches"), Command::UNITS_INCHES, 0, KR, mView },
|
||||||
|
{ 2, N_("Dimensions in &Feet and Inches"), Command::UNITS_FEET_INCHES, 0, KR, mView },
|
||||||
{ 1, NULL, Command::NONE, 0, KN, NULL },
|
{ 1, NULL, Command::NONE, 0, KN, NULL },
|
||||||
{ 1, N_("Show &Toolbar"), Command::SHOW_TOOLBAR, 0, KC, mView },
|
{ 1, N_("Show &Toolbar"), Command::SHOW_TOOLBAR, 0, KC, mView },
|
||||||
{ 1, N_("Show Property Bro&wser"), Command::SHOW_TEXT_WND, '\t', KC, mView },
|
{ 1, N_("Show Property Bro&wser"), Command::SHOW_TEXT_WND, '\t', KC, mView },
|
||||||
|
@ -153,8 +155,8 @@ const MenuEntry Menu[] = {
|
||||||
{ 1, NULL, Command::NONE, 0, KN, NULL },
|
{ 1, NULL, Command::NONE, 0, KN, NULL },
|
||||||
{ 1, N_("&On Point / Curve / Plane"), Command::ON_ENTITY, 'o', KN, mCon },
|
{ 1, N_("&On Point / Curve / Plane"), Command::ON_ENTITY, 'o', KN, mCon },
|
||||||
{ 1, N_("E&qual Length / Radius / Angle"), Command::EQUAL, 'q', KN, mCon },
|
{ 1, N_("E&qual Length / Radius / Angle"), Command::EQUAL, 'q', KN, mCon },
|
||||||
{ 1, N_("Length Ra&tio"), Command::RATIO, 'z', KN, mCon },
|
{ 1, N_("Length / Arc Ra&tio"), Command::RATIO, 'z', KN, mCon },
|
||||||
{ 1, N_("Length Diff&erence"), Command::DIFFERENCE, 'j', KN, mCon },
|
{ 1, N_("Length / Arc Diff&erence"), Command::DIFFERENCE, 'j', KN, mCon },
|
||||||
{ 1, N_("At &Midpoint"), Command::AT_MIDPOINT, 'm', KN, mCon },
|
{ 1, N_("At &Midpoint"), Command::AT_MIDPOINT, 'm', KN, mCon },
|
||||||
{ 1, N_("S&ymmetric"), Command::SYMMETRIC, 'y', KN, mCon },
|
{ 1, N_("S&ymmetric"), Command::SYMMETRIC, 'y', KN, mCon },
|
||||||
{ 1, N_("Para&llel / Tangent"), Command::PARALLEL, 'l', KN, mCon },
|
{ 1, N_("Para&llel / Tangent"), Command::PARALLEL, 'l', KN, mCon },
|
||||||
|
@ -317,6 +319,8 @@ void GraphicsWindow::PopulateMainMenu() {
|
||||||
dimSolidModelMenuItem = menuItem;
|
dimSolidModelMenuItem = menuItem;
|
||||||
} else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) {
|
} else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) {
|
||||||
perspectiveProjMenuItem = menuItem;
|
perspectiveProjMenuItem = menuItem;
|
||||||
|
} else if(Menu[i].cmd == Command::EXPLODE_SKETCH) {
|
||||||
|
explodeMenuItem = menuItem;
|
||||||
} else if(Menu[i].cmd == Command::SHOW_TOOLBAR) {
|
} else if(Menu[i].cmd == Command::SHOW_TOOLBAR) {
|
||||||
showToolbarMenuItem = menuItem;
|
showToolbarMenuItem = menuItem;
|
||||||
} else if(Menu[i].cmd == Command::SHOW_TEXT_WND) {
|
} else if(Menu[i].cmd == Command::SHOW_TEXT_WND) {
|
||||||
|
@ -329,6 +333,8 @@ void GraphicsWindow::PopulateMainMenu() {
|
||||||
unitsMetersMenuItem = menuItem;
|
unitsMetersMenuItem = menuItem;
|
||||||
} else if(Menu[i].cmd == Command::UNITS_INCHES) {
|
} else if(Menu[i].cmd == Command::UNITS_INCHES) {
|
||||||
unitsInchesMenuItem = menuItem;
|
unitsInchesMenuItem = menuItem;
|
||||||
|
} else if(Menu[i].cmd == Command::UNITS_FEET_INCHES) {
|
||||||
|
unitsFeetInchesMenuItem = menuItem;
|
||||||
} else if(Menu[i].cmd == Command::SEL_WORKPLANE) {
|
} else if(Menu[i].cmd == Command::SEL_WORKPLANE) {
|
||||||
inWorkplaneMenuItem = menuItem;
|
inWorkplaneMenuItem = menuItem;
|
||||||
} else if(Menu[i].cmd == Command::FREE_IN_3D) {
|
} else if(Menu[i].cmd == Command::FREE_IN_3D) {
|
||||||
|
@ -369,8 +375,11 @@ static void PopulateMenuWithPathnames(Platform::MenuRef menu,
|
||||||
|
|
||||||
void GraphicsWindow::PopulateRecentFiles() {
|
void GraphicsWindow::PopulateRecentFiles() {
|
||||||
PopulateMenuWithPathnames(openRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
|
PopulateMenuWithPathnames(openRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
|
||||||
|
// OkayToStartNewFile could mutate recentFiles, which will invalidate path (which is a
|
||||||
|
// reference into the recentFiles vector), so take a copy of it here.
|
||||||
|
Platform::Path pathCopy(path);
|
||||||
if(!SS.OkayToStartNewFile()) return;
|
if(!SS.OkayToStartNewFile()) return;
|
||||||
SS.Load(path);
|
SS.Load(pathCopy);
|
||||||
});
|
});
|
||||||
|
|
||||||
PopulateMenuWithPathnames(linkRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
|
PopulateMenuWithPathnames(linkRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
|
||||||
|
@ -404,6 +413,8 @@ void GraphicsWindow::Init() {
|
||||||
showEdges = true;
|
showEdges = true;
|
||||||
showMesh = false;
|
showMesh = false;
|
||||||
showOutlines = false;
|
showOutlines = false;
|
||||||
|
showFacesDrawing = false;
|
||||||
|
showFacesNonDrawing = true;
|
||||||
drawOccludedAs = DrawOccludedAs::INVISIBLE;
|
drawOccludedAs = DrawOccludedAs::INVISIBLE;
|
||||||
|
|
||||||
showTextWindow = true;
|
showTextWindow = true;
|
||||||
|
@ -419,7 +430,7 @@ void GraphicsWindow::Init() {
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
// Do this first, so that if it causes an onRender event we don't try to paint without
|
// Do this first, so that if it causes an onRender event we don't try to paint without
|
||||||
// a canvas.
|
// a canvas.
|
||||||
window->SetMinContentSize(720, 670);
|
window->SetMinContentSize(720, /*ToolbarDrawOrHitTest 636*/ 32 * 18 + 3 * 16 + 8 + 4);
|
||||||
window->onClose = std::bind(&SolveSpaceUI::MenuFile, Command::EXIT);
|
window->onClose = std::bind(&SolveSpaceUI::MenuFile, Command::EXIT);
|
||||||
window->onRender = std::bind(&GraphicsWindow::Paint, this);
|
window->onRender = std::bind(&GraphicsWindow::Paint, this);
|
||||||
window->onKeyboardEvent = std::bind(&GraphicsWindow::KeyboardEvent, this, _1);
|
window->onKeyboardEvent = std::bind(&GraphicsWindow::KeyboardEvent, this, _1);
|
||||||
|
@ -745,6 +756,12 @@ void GraphicsWindow::MenuView(Command id) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::EXPLODE_SKETCH:
|
||||||
|
SS.explode = !SS.explode;
|
||||||
|
SS.GW.EnsureValidActives();
|
||||||
|
SS.MarkGroupDirty(SS.GW.activeGroup, true);
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::ONTO_WORKPLANE:
|
case Command::ONTO_WORKPLANE:
|
||||||
if(SS.GW.LockedInWorkplane()) {
|
if(SS.GW.LockedInWorkplane()) {
|
||||||
SS.GW.AnimateOntoWorkplane();
|
SS.GW.AnimateOntoWorkplane();
|
||||||
|
@ -838,6 +855,12 @@ void GraphicsWindow::MenuView(Command id) {
|
||||||
SS.GW.EnsureValidActives();
|
SS.GW.EnsureValidActives();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::UNITS_FEET_INCHES:
|
||||||
|
SS.viewUnits = Unit::FEET_INCHES;
|
||||||
|
SS.ScheduleShowTW();
|
||||||
|
SS.GW.EnsureValidActives();
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::UNITS_MM:
|
case Command::UNITS_MM:
|
||||||
SS.viewUnits = Unit::MM;
|
SS.viewUnits = Unit::MM;
|
||||||
SS.ScheduleShowTW();
|
SS.ScheduleShowTW();
|
||||||
|
@ -920,6 +943,7 @@ void GraphicsWindow::EnsureValidActives() {
|
||||||
case Unit::MM:
|
case Unit::MM:
|
||||||
case Unit::METERS:
|
case Unit::METERS:
|
||||||
case Unit::INCHES:
|
case Unit::INCHES:
|
||||||
|
case Unit::FEET_INCHES:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
SS.viewUnits = Unit::MM;
|
SS.viewUnits = Unit::MM;
|
||||||
|
@ -928,6 +952,7 @@ void GraphicsWindow::EnsureValidActives() {
|
||||||
unitsMmMenuItem->SetActive(SS.viewUnits == Unit::MM);
|
unitsMmMenuItem->SetActive(SS.viewUnits == Unit::MM);
|
||||||
unitsMetersMenuItem->SetActive(SS.viewUnits == Unit::METERS);
|
unitsMetersMenuItem->SetActive(SS.viewUnits == Unit::METERS);
|
||||||
unitsInchesMenuItem->SetActive(SS.viewUnits == Unit::INCHES);
|
unitsInchesMenuItem->SetActive(SS.viewUnits == Unit::INCHES);
|
||||||
|
unitsFeetInchesMenuItem->SetActive(SS.viewUnits == Unit::FEET_INCHES);
|
||||||
|
|
||||||
if(SS.TW.window) SS.TW.window->SetVisible(SS.GW.showTextWindow);
|
if(SS.TW.window) SS.TW.window->SetVisible(SS.GW.showTextWindow);
|
||||||
showTextWndMenuItem->SetActive(SS.GW.showTextWindow);
|
showTextWndMenuItem->SetActive(SS.GW.showTextWindow);
|
||||||
|
@ -935,6 +960,7 @@ void GraphicsWindow::EnsureValidActives() {
|
||||||
showGridMenuItem->SetActive(SS.GW.showSnapGrid);
|
showGridMenuItem->SetActive(SS.GW.showSnapGrid);
|
||||||
dimSolidModelMenuItem->SetActive(SS.GW.dimSolidModel);
|
dimSolidModelMenuItem->SetActive(SS.GW.dimSolidModel);
|
||||||
perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj);
|
perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj);
|
||||||
|
explodeMenuItem->SetActive(SS.explode);
|
||||||
showToolbarMenuItem->SetActive(SS.showToolbar);
|
showToolbarMenuItem->SetActive(SS.showToolbar);
|
||||||
fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen());
|
fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen());
|
||||||
|
|
||||||
|
@ -965,20 +991,19 @@ void GraphicsWindow::ForceTextWindowShown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::DeleteTaggedRequests() {
|
void GraphicsWindow::DeleteTaggedRequests() {
|
||||||
Request *r;
|
|
||||||
// Delete any requests that were affected by this deletion.
|
// Delete any requests that were affected by this deletion.
|
||||||
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
|
for(Request &r : SK.request) {
|
||||||
if(r->workplane == Entity::FREE_IN_3D) continue;
|
if(r.workplane == Entity::FREE_IN_3D) continue;
|
||||||
if(!r->workplane.isFromRequest()) continue;
|
if(!r.workplane.isFromRequest()) continue;
|
||||||
Request *wrkpl = SK.GetRequest(r->workplane.request());
|
Request *wrkpl = SK.GetRequest(r.workplane.request());
|
||||||
if(wrkpl->tag)
|
if(wrkpl->tag)
|
||||||
r->tag = 1;
|
r.tag = 1;
|
||||||
}
|
}
|
||||||
// Rewrite any point-coincident constraints that were affected by this
|
// Rewrite any point-coincident constraints that were affected by this
|
||||||
// deletion.
|
// deletion.
|
||||||
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
|
for(Request &r : SK.request) {
|
||||||
if(!r->tag) continue;
|
if(!r.tag) continue;
|
||||||
FixConstraintsForRequestBeingDeleted(r->h);
|
FixConstraintsForRequestBeingDeleted(r.h);
|
||||||
}
|
}
|
||||||
// and then delete the tagged requests.
|
// and then delete the tagged requests.
|
||||||
SK.request.RemoveTagged();
|
SK.request.RemoveTagged();
|
||||||
|
@ -1042,9 +1067,8 @@ void GraphicsWindow::MenuEdit(Command id) {
|
||||||
SS.centerOfMass.draw = false;
|
SS.centerOfMass.draw = false;
|
||||||
// This clears the marks drawn to indicate which points are
|
// This clears the marks drawn to indicate which points are
|
||||||
// still free to drag.
|
// still free to drag.
|
||||||
Param *p;
|
for(Param &p : SK.param) {
|
||||||
for(p = SK.param.First(); p; p = SK.param.NextAfter(p)) {
|
p.free = false;
|
||||||
p->free = false;
|
|
||||||
}
|
}
|
||||||
if(SS.exportMode) {
|
if(SS.exportMode) {
|
||||||
SS.exportMode = false;
|
SS.exportMode = false;
|
||||||
|
@ -1054,13 +1078,12 @@ void GraphicsWindow::MenuEdit(Command id) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Command::SELECT_ALL: {
|
case Command::SELECT_ALL: {
|
||||||
Entity *e;
|
for(Entity &e : SK.entity) {
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
if(e.group != SS.GW.activeGroup) continue;
|
||||||
if(e->group != SS.GW.activeGroup) continue;
|
if(e.IsFace() || e.IsDistance()) continue;
|
||||||
if(e->IsFace() || e->IsDistance()) continue;
|
if(!e.IsVisible()) continue;
|
||||||
if(!e->IsVisible()) continue;
|
|
||||||
|
|
||||||
SS.GW.MakeSelected(e->h);
|
SS.GW.MakeSelected(e.h);
|
||||||
}
|
}
|
||||||
SS.GW.Invalidate();
|
SS.GW.Invalidate();
|
||||||
SS.ScheduleShowTW();
|
SS.ScheduleShowTW();
|
||||||
|
@ -1068,24 +1091,23 @@ void GraphicsWindow::MenuEdit(Command id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case Command::SELECT_CHAIN: {
|
case Command::SELECT_CHAIN: {
|
||||||
Entity *e;
|
|
||||||
int newlySelected = 0;
|
int newlySelected = 0;
|
||||||
bool didSomething;
|
bool didSomething;
|
||||||
do {
|
do {
|
||||||
didSomething = false;
|
didSomething = false;
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
for(Entity &e : SK.entity) {
|
||||||
if(e->group != SS.GW.activeGroup) continue;
|
if(e.group != SS.GW.activeGroup) continue;
|
||||||
if(!e->HasEndpoints()) continue;
|
if(!e.HasEndpoints()) continue;
|
||||||
if(!e->IsVisible()) continue;
|
if(!e.IsVisible()) continue;
|
||||||
|
|
||||||
Vector st = e->EndpointStart(),
|
Vector st = e.EndpointStart(),
|
||||||
fi = e->EndpointFinish();
|
fi = e.EndpointFinish();
|
||||||
|
|
||||||
bool onChain = false, alreadySelected = false;
|
bool onChain = false, alreadySelected = false;
|
||||||
List<Selection> *ls = &(SS.GW.selection);
|
List<Selection> *ls = &(SS.GW.selection);
|
||||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||||
if(!s->entity.v) continue;
|
if(!s->entity.v) continue;
|
||||||
if(s->entity == e->h) {
|
if(s->entity == e.h) {
|
||||||
alreadySelected = true;
|
alreadySelected = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1124,7 @@ void GraphicsWindow::MenuEdit(Command id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(onChain && !alreadySelected) {
|
if(onChain && !alreadySelected) {
|
||||||
SS.GW.MakeSelected(e->h);
|
SS.GW.MakeSelected(e.h);
|
||||||
newlySelected++;
|
newlySelected++;
|
||||||
didSomething = true;
|
didSomething = true;
|
||||||
}
|
}
|
||||||
|
@ -1367,6 +1389,14 @@ void GraphicsWindow::ToggleBool(bool *v) {
|
||||||
SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
|
SS.GenerateAll(SolveSpaceUI::Generate::UNTIL_ACTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(v == &showFaces) {
|
||||||
|
if(g->type == Group::Type::DRAWING_WORKPLANE || g->type == Group::Type::DRAWING_3D) {
|
||||||
|
showFacesDrawing = showFaces;
|
||||||
|
} else {
|
||||||
|
showFacesNonDrawing = showFaces;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Invalidate(/*clearPersistent=*/true);
|
Invalidate(/*clearPersistent=*/true);
|
||||||
SS.ScheduleShowTW();
|
SS.ScheduleShowTW();
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,12 +138,24 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
|
||||||
g.predef.q = wrkplg->predef.q;
|
g.predef.q = wrkplg->predef.q;
|
||||||
} else ssassert(false, "Unexpected workplane subtype");
|
} else ssassert(false, "Unexpected workplane subtype");
|
||||||
}
|
}
|
||||||
|
} else if(gs.anyNormals == 1 && gs.points == 1 && gs.n == 2) {
|
||||||
|
g.subtype = Subtype::WORKPLANE_BY_POINT_NORMAL;
|
||||||
|
g.predef.q = SK.GetEntity(gs.anyNormal[0])->NormalGetNum();
|
||||||
|
g.predef.origin = gs.point[0];
|
||||||
|
//} else if(gs.faces == 1 && gs.points == 1 && gs.n == 2) {
|
||||||
|
// g.subtype = Subtype::WORKPLANE_BY_POINT_FACE;
|
||||||
|
// g.predef.q = SK.GetEntity(gs.face[0])->NormalGetNum();
|
||||||
|
// g.predef.origin = gs.point[0];
|
||||||
} else {
|
} else {
|
||||||
Error(_("Bad selection for new sketch in workplane. This "
|
Error(_("Bad selection for new sketch in workplane. This "
|
||||||
"group can be created with:\n\n"
|
"group can be created with:\n\n"
|
||||||
" * a point (through the point, orthogonal to coordinate axes)\n"
|
" * a point (through the point, orthogonal to coordinate axes)\n"
|
||||||
" * a point and two line segments (through the point, "
|
" * a point and two line segments (through the point, "
|
||||||
"parallel to the lines)\n"
|
"parallel to the lines)\n"
|
||||||
|
" * a point and a normal (through the point, "
|
||||||
|
"orthogonal to the normal)\n"
|
||||||
|
/*" * a point and a face (through the point, "
|
||||||
|
"parallel to the face)\n"*/
|
||||||
" * a workplane (copy of the workplane)\n"));
|
" * a workplane (copy of the workplane)\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -392,7 +404,11 @@ bool Group::IsForcedToMeshBySource() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Group::IsForcedToMesh() const {
|
bool Group::IsForcedToMesh() const {
|
||||||
return forceToMesh || IsForcedToMeshBySource();
|
return forceToMesh || IsTriangleMeshAssembly() || IsForcedToMeshBySource();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Group::IsTriangleMeshAssembly() const {
|
||||||
|
return type == Type::LINKED && linkFile.Extension() == "stl";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Group::DescriptionString() {
|
std::string Group::DescriptionString() {
|
||||||
|
@ -404,11 +420,10 @@ std::string Group::DescriptionString() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Group::Activate() {
|
void Group::Activate() {
|
||||||
if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE ||
|
if(type == Type::DRAWING_WORKPLANE || type == Type::DRAWING_3D) {
|
||||||
type == Type::REVOLVE || type == Type::HELIX || type == Type::TRANSLATE || type == Type::ROTATE) {
|
SS.GW.showFaces = SS.GW.showFacesDrawing;
|
||||||
SS.GW.showFaces = true;
|
|
||||||
} else {
|
} else {
|
||||||
SS.GW.showFaces = false;
|
SS.GW.showFaces = SS.GW.showFacesNonDrawing;
|
||||||
}
|
}
|
||||||
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
|
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
|
||||||
SS.ScheduleShowTW();
|
SS.ScheduleShowTW();
|
||||||
|
@ -440,7 +455,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
|
||||||
if(predef.negateU) u = u.ScaledBy(-1);
|
if(predef.negateU) u = u.ScaledBy(-1);
|
||||||
if(predef.negateV) v = v.ScaledBy(-1);
|
if(predef.negateV) v = v.ScaledBy(-1);
|
||||||
q = Quaternion::From(u, v);
|
q = Quaternion::From(u, v);
|
||||||
} else if(subtype == Subtype::WORKPLANE_BY_POINT_ORTHO) {
|
} else if(subtype == Subtype::WORKPLANE_BY_POINT_ORTHO || subtype == Subtype::WORKPLANE_BY_POINT_NORMAL /*|| subtype == Subtype::WORKPLANE_BY_POINT_FACE*/) {
|
||||||
// Already given, numerically.
|
// Already given, numerically.
|
||||||
q = predef.q;
|
q = predef.q;
|
||||||
} else ssassert(false, "Unexpected workplane subtype");
|
} else ssassert(false, "Unexpected workplane subtype");
|
||||||
|
@ -448,6 +463,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
|
||||||
Entity normal = {};
|
Entity normal = {};
|
||||||
normal.type = Entity::Type::NORMAL_N_COPY;
|
normal.type = Entity::Type::NORMAL_N_COPY;
|
||||||
normal.numNormal = q;
|
normal.numNormal = q;
|
||||||
|
|
||||||
normal.point[0] = h.entity(2);
|
normal.point[0] = h.entity(2);
|
||||||
normal.group = h;
|
normal.group = h;
|
||||||
normal.h = h.entity(1);
|
normal.h = h.entity(1);
|
||||||
|
@ -800,6 +816,12 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
|
||||||
AddEq(l, (EC(axis.z))->Minus(EP(6)), 5);
|
AddEq(l, (EC(axis.z))->Minus(EP(6)), 5);
|
||||||
#undef EC
|
#undef EC
|
||||||
#undef EP
|
#undef EP
|
||||||
|
if(type == Type::HELIX) {
|
||||||
|
if(valB != 0.0) {
|
||||||
|
AddEq(l, Expr::From(h.param(7))->Times(Expr::From(PI))->
|
||||||
|
Minus(Expr::From(h.param(3))->Times(Expr::From(valB))), 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if(type == Type::EXTRUDE) {
|
} else if(type == Type::EXTRUDE) {
|
||||||
if(predef.entityB != Entity::FREE_IN_3D) {
|
if(predef.entityB != Entity::FREE_IN_3D) {
|
||||||
// The extrusion path is locked along a line, normal to the
|
// The extrusion path is locked along a line, normal to the
|
||||||
|
@ -1172,3 +1194,6 @@ void Group::CopyEntity(IdList<Entity,hEntity> *el,
|
||||||
el->Add(&en);
|
el->Add(&en);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Group::ShouldDrawExploded() const {
|
||||||
|
return SS.explode && h == SS.GW.activeGroup && type == Type::DRAWING_WORKPLANE && !SS.exportMode;
|
||||||
|
}
|
||||||
|
|
|
@ -83,13 +83,12 @@ void Group::GenerateLoops() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SShell::RemapFaces(Group *g, int remap) {
|
void SShell::RemapFaces(Group *g, int remap) {
|
||||||
SSurface *ss;
|
for(SSurface &ss : surface){
|
||||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){
|
hEntity face = { ss.face };
|
||||||
hEntity face = { ss->face };
|
|
||||||
if(face == Entity::NO_ENTITY) continue;
|
if(face == Entity::NO_ENTITY) continue;
|
||||||
|
|
||||||
face = g->Remap(face, remap);
|
face = g->Remap(face, remap);
|
||||||
ss->face = face.v;
|
ss.face = face.v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,13 +291,12 @@ void Group::GenerateShellAndMesh() {
|
||||||
// So these are the sides
|
// So these are the sides
|
||||||
if(ss->degm != 1 || ss->degn != 1) continue;
|
if(ss->degm != 1 || ss->degn != 1) continue;
|
||||||
|
|
||||||
Entity *e;
|
for(Entity &e : SK.entity) {
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
if(e.group != opA) continue;
|
||||||
if(e->group != opA) continue;
|
if(e.type != Entity::Type::LINE_SEGMENT) continue;
|
||||||
if(e->type != Entity::Type::LINE_SEGMENT) continue;
|
|
||||||
|
|
||||||
Vector a = SK.GetEntity(e->point[0])->PointGetNum(),
|
Vector a = SK.GetEntity(e.point[0])->PointGetNum(),
|
||||||
b = SK.GetEntity(e->point[1])->PointGetNum();
|
b = SK.GetEntity(e.point[1])->PointGetNum();
|
||||||
a = a.Plus(ttop);
|
a = a.Plus(ttop);
|
||||||
b = b.Plus(ttop);
|
b = b.Plus(ttop);
|
||||||
// Could get taken backwards, so check all cases.
|
// Could get taken backwards, so check all cases.
|
||||||
|
@ -307,7 +305,7 @@ void Group::GenerateShellAndMesh() {
|
||||||
(a.Equals(ss->ctrl[0][1]) && b.Equals(ss->ctrl[1][1])) ||
|
(a.Equals(ss->ctrl[0][1]) && b.Equals(ss->ctrl[1][1])) ||
|
||||||
(b.Equals(ss->ctrl[0][1]) && a.Equals(ss->ctrl[1][1])))
|
(b.Equals(ss->ctrl[0][1]) && a.Equals(ss->ctrl[1][1])))
|
||||||
{
|
{
|
||||||
face = Remap(e->h, REMAP_LINE_TO_FACE);
|
face = Remap(e.h, REMAP_LINE_TO_FACE);
|
||||||
ss->face = face.v;
|
ss->face = face.v;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ static std::vector <std::string> splitString(const std::string line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// Functions for linking an IDF file - we need to create entites that
|
// Functions for linking an IDF file - we need to create entities that
|
||||||
// get remapped into a linked group similar to linking .slvs files
|
// get remapped into a linked group similar to linking .slvs files
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ static hEntity newPoint(EntityList *el, int *id, Vector p, bool visible = true)
|
||||||
return en.h;
|
return en.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1) {
|
static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1, bool keepout) {
|
||||||
Entity en = {};
|
Entity en = {};
|
||||||
en.type = Entity::Type::LINE_SEGMENT;
|
en.type = Entity::Type::LINE_SEGMENT;
|
||||||
en.point[0] = p0;
|
en.point[0] = p0;
|
||||||
|
@ -85,8 +85,8 @@ static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1) {
|
||||||
en.extraPoints = 0;
|
en.extraPoints = 0;
|
||||||
en.timesApplied = 0;
|
en.timesApplied = 0;
|
||||||
en.group.v = 493;
|
en.group.v = 493;
|
||||||
en.construction = false;
|
en.construction = keepout;
|
||||||
en.style.v = Style::ACTIVE_GRP;
|
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
|
||||||
en.actVisible = true;
|
en.actVisible = true;
|
||||||
en.forceHidden = false;
|
en.forceHidden = false;
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ static hEntity newNormal(EntityList *el, int *id, Quaternion normal) {
|
||||||
return en.h;
|
return en.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hEntity newArc(EntityList *el, int *id, hEntity p0, hEntity p1, hEntity pc, hEntity hnorm) {
|
static hEntity newArc(EntityList *el, int *id, hEntity p0, hEntity p1, hEntity pc, hEntity hnorm, bool keepout) {
|
||||||
Entity en = {};
|
Entity en = {};
|
||||||
en.type = Entity::Type::ARC_OF_CIRCLE;
|
en.type = Entity::Type::ARC_OF_CIRCLE;
|
||||||
en.point[0] = pc;
|
en.point[0] = pc;
|
||||||
|
@ -127,8 +127,8 @@ static hEntity newArc(EntityList *el, int *id, hEntity p0, hEntity p1, hEntity p
|
||||||
en.extraPoints = 0;
|
en.extraPoints = 0;
|
||||||
en.timesApplied = 0;
|
en.timesApplied = 0;
|
||||||
en.group.v = 403;
|
en.group.v = 403;
|
||||||
en.construction = false;
|
en.construction = keepout;
|
||||||
en.style.v = Style::ACTIVE_GRP;
|
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
|
||||||
en.actVisible = true;
|
en.actVisible = true;
|
||||||
en.forceHidden = false; *id = *id+1;
|
en.forceHidden = false; *id = *id+1;
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ static hEntity newDistance(EntityList *el, int *id, double distance) {
|
||||||
return en.h;
|
return en.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hEntity newCircle(EntityList *el, int *id, hEntity p0, hEntity hdist, hEntity hnorm) {
|
static hEntity newCircle(EntityList *el, int *id, hEntity p0, hEntity hdist, hEntity hnorm, bool keepout) {
|
||||||
Entity en = {};
|
Entity en = {};
|
||||||
en.type = Entity::Type::CIRCLE;
|
en.type = Entity::Type::CIRCLE;
|
||||||
en.point[0] = p0;
|
en.point[0] = p0;
|
||||||
|
@ -167,8 +167,8 @@ static hEntity newCircle(EntityList *el, int *id, hEntity p0, hEntity hdist, hEn
|
||||||
en.extraPoints = 0;
|
en.extraPoints = 0;
|
||||||
en.timesApplied = 0;
|
en.timesApplied = 0;
|
||||||
en.group.v = 399;
|
en.group.v = 399;
|
||||||
en.construction = false;
|
en.construction = keepout;
|
||||||
en.style.v = Style::ACTIVE_GRP;
|
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
|
||||||
en.actVisible = true;
|
en.actVisible = true;
|
||||||
en.forceHidden = false;
|
en.forceHidden = false;
|
||||||
|
|
||||||
|
@ -196,18 +196,18 @@ static Vector ArcCenter(Vector p0, Vector p1, double angle) {
|
||||||
// Positive angles are counter clockwise, negative are clockwise. An angle of 360
|
// Positive angles are counter clockwise, negative are clockwise. An angle of 360
|
||||||
// indicates a circle centered at x1,y1 passing through x2,y2 and is a complete loop.
|
// indicates a circle centered at x1,y1 passing through x2,y2 and is a complete loop.
|
||||||
static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntity hnorm,
|
static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntity hnorm,
|
||||||
Vector p0, Vector p1, double angle) {
|
Vector p0, Vector p1, double angle, bool keepout) {
|
||||||
if (angle == 0.0) {
|
if (angle == 0.0) {
|
||||||
//line
|
//line
|
||||||
if(p0.Equals(p1)) return;
|
if(p0.Equals(p1)) return;
|
||||||
|
|
||||||
newLine(el, id, h0, h1);
|
newLine(el, id, h0, h1, keepout);
|
||||||
|
|
||||||
} else if(angle == 360.0) {
|
} else if(angle == 360.0) {
|
||||||
// circle
|
// circle
|
||||||
double d = p1.Minus(p0).Magnitude();
|
double d = p1.Minus(p0).Magnitude();
|
||||||
hEntity hd = newDistance(el, id, d);
|
hEntity hd = newDistance(el, id, d);
|
||||||
newCircle(el, id, h1, hd, hnorm);
|
newCircle(el, id, h1, hd, hnorm, keepout);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// arc
|
// arc
|
||||||
|
@ -226,7 +226,7 @@ static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntit
|
||||||
}
|
}
|
||||||
Vector c = m.Minus(perp.ScaledBy(dist));
|
Vector c = m.Minus(perp.ScaledBy(dist));
|
||||||
hEntity hc = newPoint(el, id, c, /*visible=*/false);
|
hEntity hc = newPoint(el, id, c, /*visible=*/false);
|
||||||
newArc(el, id, h0, h1, hc, hnorm);
|
newArc(el, id, h0, h1, hc, hnorm, keepout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,9 +291,9 @@ static void MakeBeziersForArcs(SBezierList *sbl, Vector center, Vector pa, Vecto
|
||||||
namespace SolveSpace {
|
namespace SolveSpace {
|
||||||
|
|
||||||
// Here we read the important section of an IDF file. SolveSpace Entities are directly created by
|
// Here we read the important section of an IDF file. SolveSpace Entities are directly created by
|
||||||
// the funcions above, which is only OK because of the way linking works. For example points do
|
// the functions above, which is only OK because of the way linking works. For example points do
|
||||||
// not have handles for solver parameters (coordinates), they only have their actPoint values
|
// not have handles for solver parameters (coordinates), they only have their actPoint values
|
||||||
// set (or actNormal or actDistance). These are incompete entites and would be a problem if
|
// set (or actNormal or actDistance). These are incomplete entities and would be a problem if
|
||||||
// they were part of the sketch, but they are not. After making a list of them here, a new group
|
// they were part of the sketch, but they are not. After making a list of them here, a new group
|
||||||
// gets created from copies of these. Those copies are complete and part of the sketch group.
|
// gets created from copies of these. Those copies are complete and part of the sketch group.
|
||||||
bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
|
bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
|
||||||
|
@ -355,10 +355,9 @@ bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *s
|
||||||
} else if (line.find(".BOARD_OUTLINE") == 0) {
|
} else if (line.find(".BOARD_OUTLINE") == 0) {
|
||||||
section = board_outline;
|
section = board_outline;
|
||||||
record_number = 1;
|
record_number = 1;
|
||||||
// no keepouts for now - they should also be shown as construction?
|
} else if (line.find(".ROUTE_KEEPOUT") == 0) {
|
||||||
// } else if (line.find(".ROUTE_KEEPOUT") == 0) {
|
section = routing_keepout;
|
||||||
// section = routing_keepout;
|
record_number = 1;
|
||||||
// record_number = 1;
|
|
||||||
} else if(line.find(".DRILLED_HOLES") == 0) {
|
} else if(line.find(".DRILLED_HOLES") == 0) {
|
||||||
section = drilled_holes;
|
section = drilled_holes;
|
||||||
record_number = 1;
|
record_number = 1;
|
||||||
|
@ -433,13 +432,15 @@ bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *s
|
||||||
bool vis = (ang == 360.0);
|
bool vis = (ang == 360.0);
|
||||||
if (bottomEntities) {
|
if (bottomEntities) {
|
||||||
hEntity hp = newPoint(el, &entityCount, point, /*visible=*/vis);
|
hEntity hp = newPoint(el, &entityCount, point, /*visible=*/vis);
|
||||||
CreateEntity(el, &entityCount, hprev, hp, hnorm, pprev, point, ang);
|
CreateEntity(el, &entityCount, hprev, hp, hnorm, pprev, point, ang,
|
||||||
|
(section == routing_keepout) );
|
||||||
pprev = point;
|
pprev = point;
|
||||||
hprev = hp;
|
hprev = hp;
|
||||||
}
|
}
|
||||||
if (topEntities) {
|
if (topEntities) {
|
||||||
hEntity hp = newPoint(el, &entityCount, pTop, /*visible=*/vis);
|
hEntity hp = newPoint(el, &entityCount, pTop, /*visible=*/vis);
|
||||||
CreateEntity(el, &entityCount, hprevTop, hp, hnorm, pprevTop, pTop, ang);
|
CreateEntity(el, &entityCount, hprevTop, hp, hnorm, pprevTop, pTop,
|
||||||
|
ang, (section == routing_keepout) );
|
||||||
pprevTop = pTop;
|
pprevTop = pTop;
|
||||||
hprevTop = hp;
|
hprevTop = hp;
|
||||||
}
|
}
|
||||||
|
@ -467,12 +468,12 @@ bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *s
|
||||||
Vector cent = Vector::From(x,y,0.0);
|
Vector cent = Vector::From(x,y,0.0);
|
||||||
hEntity hcent = newPoint(el, &entityCount, cent);
|
hEntity hcent = newPoint(el, &entityCount, cent);
|
||||||
hEntity hdist = newDistance(el, &entityCount, d/2);
|
hEntity hdist = newDistance(el, &entityCount, d/2);
|
||||||
newCircle(el, &entityCount, hcent, hdist, hnorm);
|
newCircle(el, &entityCount, hcent, hdist, hnorm, false);
|
||||||
// and again for the top
|
// and again for the top
|
||||||
Vector cTop = Vector::From(x,y,board_thickness);
|
Vector cTop = Vector::From(x,y,board_thickness);
|
||||||
hcent = newPoint(el, &entityCount, cTop);
|
hcent = newPoint(el, &entityCount, cTop);
|
||||||
hdist = newDistance(el, &entityCount, d/2);
|
hdist = newDistance(el, &entityCount, d/2);
|
||||||
newCircle(el, &entityCount, hcent, hdist, hnorm);
|
newCircle(el, &entityCount, hcent, hdist, hnorm, false);
|
||||||
// create the curves for the extrusion
|
// create the curves for the extrusion
|
||||||
Vector pt = Vector::From(x+d/2, y, 0.0);
|
Vector pt = Vector::From(x+d/2, y, 0.0);
|
||||||
MakeBeziersForArcs(&sbl, cent, pt, pt, normal, 360.0);
|
MakeBeziersForArcs(&sbl, cent, pt, pt, normal, 360.0);
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Triangle mesh file reader. Reads an STL file triangle mesh and creates
|
||||||
|
// a SovleSpace SMesh from it. Supports only Linking, not import.
|
||||||
|
//
|
||||||
|
// Copyright 2020 Paul Kahler.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "solvespace.h"
|
||||||
|
#include "sketch.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define MIN_POINT_DISTANCE 0.001
|
||||||
|
|
||||||
|
// we will check for duplicate verticies and keep all their normals
|
||||||
|
class vertex {
|
||||||
|
public:
|
||||||
|
Vector p;
|
||||||
|
std::vector<Vector> normal;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool isEdgeVertex(vertex &v) {
|
||||||
|
unsigned int i,j;
|
||||||
|
bool result = false;
|
||||||
|
for(i=0;i<v.normal.size();i++) {
|
||||||
|
for(j=i;j<v.normal.size();j++) {
|
||||||
|
if(v.normal[i].Dot(v.normal[j]) < 0.9) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// This function has poor performance, used inside a loop it is O(n**2)
|
||||||
|
static void addUnique(std::vector<vertex> &lv, Vector &p, Vector &n) {
|
||||||
|
unsigned int i;
|
||||||
|
for(i=0; i<lv.size(); i++) {
|
||||||
|
if(lv[i].p.Equals(p, MIN_POINT_DISTANCE)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i==lv.size()) {
|
||||||
|
vertex v;
|
||||||
|
v.p = p;
|
||||||
|
lv.push_back(v);
|
||||||
|
}
|
||||||
|
// we could improve a little by only storing unique normals
|
||||||
|
lv[i].normal.push_back(n);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make a new point - type doesn't matter since we will make a copy later
|
||||||
|
static hEntity newPoint(EntityList *el, int id, Vector p) {
|
||||||
|
Entity en = {};
|
||||||
|
en.type = Entity::Type::POINT_N_COPY;
|
||||||
|
en.extraPoints = 0;
|
||||||
|
en.timesApplied = 0;
|
||||||
|
en.group.v = 462;
|
||||||
|
en.actPoint = p;
|
||||||
|
en.construction = false;
|
||||||
|
en.style.v = Style::DATUM;
|
||||||
|
en.actVisible = true;
|
||||||
|
en.forceHidden = false;
|
||||||
|
|
||||||
|
en.h.v = id + en.group.v*65536;
|
||||||
|
el->Add(&en);
|
||||||
|
return en.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a vertex is unique and add it via newPoint if it is.
|
||||||
|
static void addVertex(EntityList *el, Vector v) {
|
||||||
|
if(el->n < 15000) {
|
||||||
|
int id = el->n+2;
|
||||||
|
newPoint(el, id, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static hEntity newLine(EntityList *el, int id, hEntity p0, hEntity p1) {
|
||||||
|
Entity en = {};
|
||||||
|
en.type = Entity::Type::LINE_SEGMENT;
|
||||||
|
en.point[0] = p0;
|
||||||
|
en.point[1] = p1;
|
||||||
|
en.extraPoints = 0;
|
||||||
|
en.timesApplied = 0;
|
||||||
|
en.group.v = 493;
|
||||||
|
en.construction = true;
|
||||||
|
en.style.v = Style::CONSTRUCTION;
|
||||||
|
en.actVisible = true;
|
||||||
|
en.forceHidden = false;
|
||||||
|
|
||||||
|
en.h.v = id + en.group.v*65536;
|
||||||
|
el->Add(&en);
|
||||||
|
return en.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SolveSpace {
|
||||||
|
|
||||||
|
bool LinkStl(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
|
||||||
|
dbp("\nLink STL triangle mesh.");
|
||||||
|
el->Clear();
|
||||||
|
std::string data;
|
||||||
|
if(!ReadFile(filename, &data)) {
|
||||||
|
Error("Couldn't read from '%s'", filename.raw.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream f(data);
|
||||||
|
|
||||||
|
char str[80] = {};
|
||||||
|
f.read(str, 80);
|
||||||
|
|
||||||
|
uint32_t n;
|
||||||
|
uint32_t color;
|
||||||
|
|
||||||
|
f.read((char*)&n, 4);
|
||||||
|
dbp("%d triangles", n);
|
||||||
|
|
||||||
|
float x,y,z;
|
||||||
|
float xn,yn,zn;
|
||||||
|
|
||||||
|
//add the STL origin as an entity
|
||||||
|
addVertex(el, Vector::From(0.0, 0.0, 0.0));
|
||||||
|
|
||||||
|
std::vector<vertex> verts = {};
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i<n; i++) {
|
||||||
|
STriangle tr = {};
|
||||||
|
|
||||||
|
// read the triangle normal
|
||||||
|
f.read((char*)&xn, 4);
|
||||||
|
f.read((char*)&yn, 4);
|
||||||
|
f.read((char*)&zn, 4);
|
||||||
|
tr.an = Vector::From(xn,yn,zn);
|
||||||
|
tr.bn = tr.an;
|
||||||
|
tr.cn = tr.an;
|
||||||
|
|
||||||
|
f.read((char*)&x, 4);
|
||||||
|
f.read((char*)&y, 4);
|
||||||
|
f.read((char*)&z, 4);
|
||||||
|
tr.a.x = x;
|
||||||
|
tr.a.y = y;
|
||||||
|
tr.a.z = z;
|
||||||
|
|
||||||
|
f.read((char*)&x, 4);
|
||||||
|
f.read((char*)&y, 4);
|
||||||
|
f.read((char*)&z, 4);
|
||||||
|
tr.b.x = x;
|
||||||
|
tr.b.y = y;
|
||||||
|
tr.b.z = z;
|
||||||
|
|
||||||
|
f.read((char*)&x, 4);
|
||||||
|
f.read((char*)&y, 4);
|
||||||
|
f.read((char*)&z, 4);
|
||||||
|
tr.c.x = x;
|
||||||
|
tr.c.y = y;
|
||||||
|
tr.c.z = z;
|
||||||
|
|
||||||
|
f.read((char*)&color,2);
|
||||||
|
if(color & 0x8000) {
|
||||||
|
tr.meta.color.red = (color >> 7) & 0xf8;
|
||||||
|
tr.meta.color.green = (color >> 2) & 0xf8;
|
||||||
|
tr.meta.color.blue = (color << 3);
|
||||||
|
tr.meta.color.alpha = 255;
|
||||||
|
} else {
|
||||||
|
tr.meta.color.red = 90;
|
||||||
|
tr.meta.color.green = 120;
|
||||||
|
tr.meta.color.blue = 140;
|
||||||
|
tr.meta.color.alpha = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
m->AddTriangle(&tr);
|
||||||
|
Vector normal = tr.Normal().WithMagnitude(1.0);
|
||||||
|
addUnique(verts, tr.a, normal);
|
||||||
|
addUnique(verts, tr.b, normal);
|
||||||
|
addUnique(verts, tr.c, normal);
|
||||||
|
}
|
||||||
|
dbp("%d verticies", verts.size());
|
||||||
|
|
||||||
|
BBox box = {};
|
||||||
|
box.minp = verts[0].p;
|
||||||
|
box.maxp = verts[0].p;
|
||||||
|
|
||||||
|
// determine the bounding box for all vertexes
|
||||||
|
for(unsigned int i=1; i<verts.size(); i++) {
|
||||||
|
box.Include(verts[i].p);
|
||||||
|
}
|
||||||
|
|
||||||
|
hEntity p[8];
|
||||||
|
int id = el->n+2;
|
||||||
|
p[0] = newPoint(el, id++, Vector::From(box.minp.x, box.minp.y, box.minp.z));
|
||||||
|
p[1] = newPoint(el, id++, Vector::From(box.maxp.x, box.minp.y, box.minp.z));
|
||||||
|
p[2] = newPoint(el, id++, Vector::From(box.minp.x, box.maxp.y, box.minp.z));
|
||||||
|
p[3] = newPoint(el, id++, Vector::From(box.maxp.x, box.maxp.y, box.minp.z));
|
||||||
|
p[4] = newPoint(el, id++, Vector::From(box.minp.x, box.minp.y, box.maxp.z));
|
||||||
|
p[5] = newPoint(el, id++, Vector::From(box.maxp.x, box.minp.y, box.maxp.z));
|
||||||
|
p[6] = newPoint(el, id++, Vector::From(box.minp.x, box.maxp.y, box.maxp.z));
|
||||||
|
p[7] = newPoint(el, id++, Vector::From(box.maxp.x, box.maxp.y, box.maxp.z));
|
||||||
|
|
||||||
|
newLine(el, id++, p[0], p[1]);
|
||||||
|
newLine(el, id++, p[0], p[2]);
|
||||||
|
newLine(el, id++, p[3], p[1]);
|
||||||
|
newLine(el, id++, p[3], p[2]);
|
||||||
|
|
||||||
|
newLine(el, id++, p[4], p[5]);
|
||||||
|
newLine(el, id++, p[4], p[6]);
|
||||||
|
newLine(el, id++, p[7], p[5]);
|
||||||
|
newLine(el, id++, p[7], p[6]);
|
||||||
|
|
||||||
|
newLine(el, id++, p[0], p[4]);
|
||||||
|
newLine(el, id++, p[1], p[5]);
|
||||||
|
newLine(el, id++, p[2], p[6]);
|
||||||
|
newLine(el, id++, p[3], p[7]);
|
||||||
|
|
||||||
|
for(unsigned int i=0; i<verts.size(); i++) {
|
||||||
|
// create point entities for edge vertexes
|
||||||
|
if(isEdgeVertex(verts[i])) {
|
||||||
|
addVertex(el, verts[i].p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -131,11 +131,15 @@ case SLVS_C_PT_ON_LINE: t = Constraint::Type::PT_ON_LINE; break;
|
||||||
case SLVS_C_PT_ON_FACE: t = Constraint::Type::PT_ON_FACE; break;
|
case SLVS_C_PT_ON_FACE: t = Constraint::Type::PT_ON_FACE; break;
|
||||||
case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::Type::EQUAL_LENGTH_LINES; break;
|
case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::Type::EQUAL_LENGTH_LINES; break;
|
||||||
case SLVS_C_LENGTH_RATIO: t = Constraint::Type::LENGTH_RATIO; break;
|
case SLVS_C_LENGTH_RATIO: t = Constraint::Type::LENGTH_RATIO; break;
|
||||||
|
case SLVS_C_ARC_ARC_LEN_RATIO: t = Constraint::Type::ARC_ARC_LEN_RATIO; break;
|
||||||
|
case SLVS_C_ARC_LINE_LEN_RATIO: t = Constraint::Type::ARC_LINE_LEN_RATIO; break;
|
||||||
case SLVS_C_EQ_LEN_PT_LINE_D: t = Constraint::Type::EQ_LEN_PT_LINE_D; break;
|
case SLVS_C_EQ_LEN_PT_LINE_D: t = Constraint::Type::EQ_LEN_PT_LINE_D; break;
|
||||||
case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::Type::EQ_PT_LN_DISTANCES; break;
|
case SLVS_C_EQ_PT_LN_DISTANCES: t = Constraint::Type::EQ_PT_LN_DISTANCES; break;
|
||||||
case SLVS_C_EQUAL_ANGLE: t = Constraint::Type::EQUAL_ANGLE; break;
|
case SLVS_C_EQUAL_ANGLE: t = Constraint::Type::EQUAL_ANGLE; break;
|
||||||
case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::Type::EQUAL_LINE_ARC_LEN; break;
|
case SLVS_C_EQUAL_LINE_ARC_LEN: t = Constraint::Type::EQUAL_LINE_ARC_LEN; break;
|
||||||
case SLVS_C_LENGTH_DIFFERENCE: t = Constraint::Type::LENGTH_DIFFERENCE; break;
|
case SLVS_C_LENGTH_DIFFERENCE: t = Constraint::Type::LENGTH_DIFFERENCE; break;
|
||||||
|
case SLVS_C_ARC_ARC_DIFFERENCE: t = Constraint::Type::ARC_ARC_DIFFERENCE; break;
|
||||||
|
case SLVS_C_ARC_LINE_DIFFERENCE:t = Constraint::Type::ARC_LINE_DIFFERENCE; break;
|
||||||
case SLVS_C_SYMMETRIC: t = Constraint::Type::SYMMETRIC; break;
|
case SLVS_C_SYMMETRIC: t = Constraint::Type::SYMMETRIC; break;
|
||||||
case SLVS_C_SYMMETRIC_HORIZ: t = Constraint::Type::SYMMETRIC_HORIZ; break;
|
case SLVS_C_SYMMETRIC_HORIZ: t = Constraint::Type::SYMMETRIC_HORIZ; break;
|
||||||
case SLVS_C_SYMMETRIC_VERT: t = Constraint::Type::SYMMETRIC_VERT; break;
|
case SLVS_C_SYMMETRIC_VERT: t = Constraint::Type::SYMMETRIC_VERT; break;
|
||||||
|
|
|
@ -50,38 +50,36 @@ void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
||||||
Request *r = SK.GetRequest(hr);
|
Request *r = SK.GetRequest(hr);
|
||||||
if(r->group != SS.GW.activeGroup) return;
|
if(r->group != SS.GW.activeGroup) return;
|
||||||
|
|
||||||
Entity *e;
|
for(Entity &e : SK.entity) {
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
if(!(e.h.isFromRequest())) continue;
|
||||||
if(!(e->h.isFromRequest())) continue;
|
if(e.h.request() != hr) continue;
|
||||||
if(e->h.request() != hr) continue;
|
|
||||||
|
|
||||||
if(e->type != Entity::Type::POINT_IN_2D &&
|
if(e.type != Entity::Type::POINT_IN_2D &&
|
||||||
e->type != Entity::Type::POINT_IN_3D)
|
e.type != Entity::Type::POINT_IN_3D)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a point generated by the request being deleted; so fix
|
// This is a point generated by the request being deleted; so fix
|
||||||
// the constraints for that.
|
// the constraints for that.
|
||||||
FixConstraintsForPointBeingDeleted(e->h);
|
FixConstraintsForPointBeingDeleted(e.h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
|
void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
|
||||||
List<hEntity> ld = {};
|
List<hEntity> ld = {};
|
||||||
|
|
||||||
Constraint *c;
|
|
||||||
SK.constraint.ClearTags();
|
SK.constraint.ClearTags();
|
||||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
for(Constraint &c : SK.constraint) {
|
||||||
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
|
if(c.type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||||
if(c->group != SS.GW.activeGroup) continue;
|
if(c.group != SS.GW.activeGroup) continue;
|
||||||
|
|
||||||
if(c->ptA == hpt) {
|
if(c.ptA == hpt) {
|
||||||
ld.Add(&(c->ptB));
|
ld.Add(&(c.ptB));
|
||||||
c->tag = 1;
|
c.tag = 1;
|
||||||
}
|
}
|
||||||
if(c->ptB == hpt) {
|
if(c.ptB == hpt) {
|
||||||
ld.Add(&(c->ptA));
|
ld.Add(&(c.ptA));
|
||||||
c->tag = 1;
|
c.tag = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove constraints without waiting for regeneration; this way
|
// Remove constraints without waiting for regeneration; this way
|
||||||
|
@ -225,21 +223,21 @@ void GraphicsWindow::ParametricCurve::CreateRequestTrimmedTo(double t,
|
||||||
// happens to exist, then constrain that point coincident to hpt.
|
// happens to exist, then constrain that point coincident to hpt.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
|
void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
|
||||||
Entity *e, *pt;
|
Entity *pt;
|
||||||
pt = SK.GetEntity(hpt);
|
pt = SK.GetEntity(hpt);
|
||||||
Vector ev, ptv;
|
Vector ev, ptv;
|
||||||
ptv = pt->PointGetNum();
|
ptv = pt->PointGetNum();
|
||||||
|
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
for(Entity &e : SK.entity) {
|
||||||
if(e->h == pt->h) continue;
|
if(e.h == pt->h) continue;
|
||||||
if(!e->IsPoint()) continue;
|
if(!e.IsPoint()) continue;
|
||||||
if(e->group != pt->group) continue;
|
if(e.group != pt->group) continue;
|
||||||
if(e->workplane != pt->workplane) continue;
|
if(e.workplane != pt->workplane) continue;
|
||||||
|
|
||||||
ev = e->PointGetNum();
|
ev = e.PointGetNum();
|
||||||
if(!ev.Equals(ptv)) continue;
|
if(!ev.Equals(ptv)) continue;
|
||||||
|
|
||||||
Constraint::ConstrainCoincident(hpt, e->h);
|
Constraint::ConstrainCoincident(hpt, e.h);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ void GraphicsWindow::UpdateDraggedPoint(hEntity hp, double mx, double my) {
|
||||||
Vector pos = p->PointGetNum();
|
Vector pos = p->PointGetNum();
|
||||||
UpdateDraggedNum(&pos, mx, my);
|
UpdateDraggedNum(&pos, mx, my);
|
||||||
p->PointForceTo(pos);
|
p->PointForceTo(pos);
|
||||||
|
|
||||||
|
SS.ScheduleShowTW();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::UpdateDraggedNum(Vector *pos, double mx, double my) {
|
void GraphicsWindow::UpdateDraggedNum(Vector *pos, double mx, double my) {
|
||||||
|
@ -101,7 +103,10 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
shiftDown = !shiftDown;
|
shiftDown = !shiftDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SS.showToolbar) {
|
// Not passing right-button and middle-button drags to the toolbar avoids
|
||||||
|
// some cosmetic issues with trackpad pans/rotates implemented with
|
||||||
|
// simulated right-button drag events causing spurious hover events.
|
||||||
|
if(SS.showToolbar && !middleDown) {
|
||||||
if(ToolbarMouseMoved((int)x, (int)y)) {
|
if(ToolbarMouseMoved((int)x, (int)y)) {
|
||||||
hover.Clear();
|
hover.Clear();
|
||||||
return;
|
return;
|
||||||
|
@ -188,32 +193,23 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
hEntity dragEntity = ChooseFromHoverToDrag().entity;
|
hEntity dragEntity = ChooseFromHoverToDrag().entity;
|
||||||
if(dragEntity.v) e = SK.GetEntity(dragEntity);
|
if(dragEntity.v) e = SK.GetEntity(dragEntity);
|
||||||
if(e && e->type != Entity::Type::WORKPLANE) {
|
if(e && e->type != Entity::Type::WORKPLANE) {
|
||||||
Entity *e = SK.GetEntity(dragEntity);
|
if(!hoverWasSelectedOnMousedown) {
|
||||||
|
// The user clicked an unselected entity, which
|
||||||
|
// means they're dragging just the hovered thing,
|
||||||
|
// not the full selection. So clear all the selection
|
||||||
|
// except that entity.
|
||||||
|
ClearSelection();
|
||||||
|
MakeSelected(dragEntity);
|
||||||
|
}
|
||||||
if(e->type == Entity::Type::CIRCLE && selection.n <= 1) {
|
if(e->type == Entity::Type::CIRCLE && selection.n <= 1) {
|
||||||
// Drag the radius.
|
// Drag the radius.
|
||||||
ClearSelection();
|
|
||||||
pending.circle = dragEntity;
|
pending.circle = dragEntity;
|
||||||
pending.operation = Pending::DRAGGING_RADIUS;
|
pending.operation = Pending::DRAGGING_RADIUS;
|
||||||
} else if(e->IsNormal()) {
|
} else if(e->IsNormal()) {
|
||||||
ClearSelection();
|
|
||||||
pending.normal = dragEntity;
|
pending.normal = dragEntity;
|
||||||
pending.operation = Pending::DRAGGING_NORMAL;
|
pending.operation = Pending::DRAGGING_NORMAL;
|
||||||
} else {
|
} else {
|
||||||
if(!hoverWasSelectedOnMousedown) {
|
|
||||||
// The user clicked an unselected entity, which
|
|
||||||
// means they're dragging just the hovered thing,
|
|
||||||
// not the full selection. So clear all the selection
|
|
||||||
// except that entity.
|
|
||||||
ClearSelection();
|
|
||||||
MakeSelected(e->h);
|
|
||||||
}
|
|
||||||
StartDraggingBySelection();
|
StartDraggingBySelection();
|
||||||
if(!hoverWasSelectedOnMousedown) {
|
|
||||||
// And then clear the selection again, since they
|
|
||||||
// probably didn't want that selected if they just
|
|
||||||
// were dragging it.
|
|
||||||
ClearSelection();
|
|
||||||
}
|
|
||||||
hover.Clear();
|
hover.Clear();
|
||||||
pending.operation = Pending::DRAGGING_POINTS;
|
pending.operation = Pending::DRAGGING_POINTS;
|
||||||
}
|
}
|
||||||
|
@ -425,6 +421,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
||||||
SK.GetEntity(circle->distance)->DistanceForceTo(r);
|
SK.GetEntity(circle->distance)->DistanceForceTo(r);
|
||||||
|
|
||||||
SS.MarkGroupDirtyByEntity(pending.circle);
|
SS.MarkGroupDirtyByEntity(pending.circle);
|
||||||
|
SS.ScheduleShowTW();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,11 +523,16 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pending.operation == Pending::DRAGGING_NEW_LINE_POINT ||
|
if(pending.operation == Pending::DRAGGING_NEW_LINE_POINT ||
|
||||||
pending.operation == Pending::DRAGGING_NEW_CUBIC_POINT)
|
pending.operation == Pending::DRAGGING_NEW_CUBIC_POINT ||
|
||||||
|
pending.operation == Pending::DRAGGING_NEW_ARC_POINT ||
|
||||||
|
pending.operation == Pending::DRAGGING_NEW_RADIUS ||
|
||||||
|
pending.operation == Pending::DRAGGING_NEW_POINT
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Special case; use a right click to stop drawing lines, since
|
// Special case; use a right click to stop drawing lines, since
|
||||||
// a left click would draw another one. This is quicker and more
|
// a left click would draw another one. This is quicker and more
|
||||||
// intuitive than hitting escape. Likewise for new cubic segments.
|
// intuitive than hitting escape. Likewise for other entities
|
||||||
|
// for consistency.
|
||||||
ClearPending();
|
ClearPending();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -714,11 +716,12 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||||
|
|
||||||
if(gs.points == 1) {
|
if(gs.points == 1) {
|
||||||
Entity *p = SK.GetEntity(gs.point[0]);
|
Entity *p = SK.GetEntity(gs.point[0]);
|
||||||
Constraint *c;
|
Constraint *c = nullptr;
|
||||||
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
|
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
|
||||||
for(c = lc->First(); c; c = lc->NextAfter(c)) {
|
for(Constraint &ci : *lc) {
|
||||||
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
|
if(ci.type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||||
if(c->ptA == p->h || c->ptB == p->h) {
|
if(ci.ptA == p->h || ci.ptB == p->h) {
|
||||||
|
c = &ci;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -728,11 +731,10 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
||||||
|
|
||||||
SS.UndoRemember();
|
SS.UndoRemember();
|
||||||
SK.constraint.ClearTags();
|
SK.constraint.ClearTags();
|
||||||
Constraint *c;
|
for(Constraint &c : SK.constraint) {
|
||||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
if(c.type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||||
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
|
if(c.ptA == p->h || c.ptB == p->h) {
|
||||||
if(c->ptA == p->h || c->ptB == p->h) {
|
c.tag = 1;
|
||||||
c->tag = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SK.constraint.RemoveTagged();
|
SK.constraint.RemoveTagged();
|
||||||
|
@ -1306,15 +1308,20 @@ void GraphicsWindow::MouseLeftDown(double mx, double my, bool shiftDown, bool ct
|
||||||
|
|
||||||
void GraphicsWindow::MouseLeftUp(double mx, double my, bool shiftDown, bool ctrlDown) {
|
void GraphicsWindow::MouseLeftUp(double mx, double my, bool shiftDown, bool ctrlDown) {
|
||||||
orig.mouseDown = false;
|
orig.mouseDown = false;
|
||||||
hoverWasSelectedOnMousedown = false;
|
|
||||||
|
|
||||||
switch(pending.operation) {
|
switch(pending.operation) {
|
||||||
case Pending::DRAGGING_POINTS:
|
case Pending::DRAGGING_POINTS:
|
||||||
SS.extraLine.draw = false;
|
|
||||||
// fall through
|
|
||||||
case Pending::DRAGGING_CONSTRAINT:
|
case Pending::DRAGGING_CONSTRAINT:
|
||||||
case Pending::DRAGGING_NORMAL:
|
case Pending::DRAGGING_NORMAL:
|
||||||
case Pending::DRAGGING_RADIUS:
|
case Pending::DRAGGING_RADIUS:
|
||||||
|
if(!hoverWasSelectedOnMousedown) {
|
||||||
|
// And then clear the selection again, since they
|
||||||
|
// probably didn't want that selected if they just
|
||||||
|
// were dragging it.
|
||||||
|
ClearSelection();
|
||||||
|
}
|
||||||
|
hoverWasSelectedOnMousedown = false;
|
||||||
|
SS.extraLine.draw = false;
|
||||||
ClearPending();
|
ClearPending();
|
||||||
Invalidate();
|
Invalidate();
|
||||||
break;
|
break;
|
||||||
|
@ -1369,12 +1376,12 @@ void GraphicsWindow::EditConstraint(hConstraint constraint) {
|
||||||
value /= 2;
|
value /= 2;
|
||||||
|
|
||||||
// Try showing value with default number of digits after decimal first.
|
// Try showing value with default number of digits after decimal first.
|
||||||
if(c->type == Constraint::Type::LENGTH_RATIO) {
|
if(c->type == Constraint::Type::LENGTH_RATIO || c->type == Constraint::Type::ARC_ARC_LEN_RATIO || c->type == Constraint::Type::ARC_LINE_LEN_RATIO) {
|
||||||
editValue = ssprintf("%.3f", value);
|
editValue = ssprintf("%.3f", value);
|
||||||
} else if(c->type == Constraint::Type::ANGLE) {
|
} else if(c->type == Constraint::Type::ANGLE) {
|
||||||
editValue = SS.DegreeToString(value);
|
editValue = SS.DegreeToString(value);
|
||||||
} else {
|
} else {
|
||||||
editValue = SS.MmToString(value);
|
editValue = SS.MmToString(value, true);
|
||||||
value /= SS.MmPerUnit();
|
value /= SS.MmPerUnit();
|
||||||
}
|
}
|
||||||
// If that's not enough to represent it exactly, show the value with as many
|
// If that's not enough to represent it exactly, show the value with as many
|
||||||
|
@ -1430,7 +1437,9 @@ void GraphicsWindow::EditControlDone(const std::string &s) {
|
||||||
case Constraint::Type::PT_LINE_DISTANCE:
|
case Constraint::Type::PT_LINE_DISTANCE:
|
||||||
case Constraint::Type::PT_FACE_DISTANCE:
|
case Constraint::Type::PT_FACE_DISTANCE:
|
||||||
case Constraint::Type::PT_PLANE_DISTANCE:
|
case Constraint::Type::PT_PLANE_DISTANCE:
|
||||||
case Constraint::Type::LENGTH_DIFFERENCE: {
|
case Constraint::Type::LENGTH_DIFFERENCE:
|
||||||
|
case Constraint::Type::ARC_ARC_DIFFERENCE:
|
||||||
|
case Constraint::Type::ARC_LINE_DIFFERENCE: {
|
||||||
// The sign is not displayed to the user, but this is a signed
|
// The sign is not displayed to the user, but this is a signed
|
||||||
// distance internally. To flip the sign, the user enters a
|
// distance internally. To flip the sign, the user enters a
|
||||||
// negative distance.
|
// negative distance.
|
||||||
|
@ -1444,6 +1453,8 @@ void GraphicsWindow::EditControlDone(const std::string &s) {
|
||||||
}
|
}
|
||||||
case Constraint::Type::ANGLE:
|
case Constraint::Type::ANGLE:
|
||||||
case Constraint::Type::LENGTH_RATIO:
|
case Constraint::Type::LENGTH_RATIO:
|
||||||
|
case Constraint::Type::ARC_ARC_LEN_RATIO:
|
||||||
|
case Constraint::Type::ARC_LINE_LEN_RATIO:
|
||||||
// These don't get the units conversion for distance, and
|
// These don't get the units conversion for distance, and
|
||||||
// they're always positive
|
// they're always positive
|
||||||
c->valA = fabs(e->Eval());
|
c->valA = fabs(e->Eval());
|
||||||
|
|
|
@ -86,8 +86,10 @@ std::vector<FileFilter> SolveSpaceModelFileFilters = {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<FileFilter> SolveSpaceLinkFileFilters = {
|
std::vector<FileFilter> SolveSpaceLinkFileFilters = {
|
||||||
|
{ CN_("file-type", "ALL"), { "slvs", "emn", "stl" } },
|
||||||
{ CN_("file-type", "SolveSpace models"), { "slvs" } },
|
{ CN_("file-type", "SolveSpace models"), { "slvs" } },
|
||||||
{ CN_("file-type", "IDF circuit board"), { "emn" } },
|
{ CN_("file-type", "IDF circuit board"), { "emn" } },
|
||||||
|
{ CN_("file-type", "STL triangle mesh"), { "stl" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<FileFilter> RasterFileFilters = {
|
std::vector<FileFilter> RasterFileFilters = {
|
||||||
|
|
|
@ -33,7 +33,18 @@
|
||||||
|
|
||||||
#if defined(HAVE_SPACEWARE)
|
#if defined(HAVE_SPACEWARE)
|
||||||
# include <spnav.h>
|
# include <spnav.h>
|
||||||
# include <gdk/gdkx.h>
|
# include <gdk/gdk.h>
|
||||||
|
# if defined(GDK_WINDOWING_X11)
|
||||||
|
# include <gdk/gdkx.h>
|
||||||
|
# endif
|
||||||
|
# if defined(GDK_WINDOWING_WAYLAND)
|
||||||
|
# include <gdk/gdkwayland.h>
|
||||||
|
# endif
|
||||||
|
# if GTK_CHECK_VERSION(3, 20, 0)
|
||||||
|
# include <gdkmm/seat.h>
|
||||||
|
# else
|
||||||
|
# include <gdkmm/devicemanager.h>
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
@ -217,6 +228,10 @@ public:
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
// Note: asan warnings about new-delete-type-mismatch are false positives here:
|
||||||
|
// https://gitlab.gnome.org/GNOME/gtkmm/-/issues/65
|
||||||
|
// Pass new_delete_type_mismatch=0 to ASAN_OPTIONS to disable those warnings.
|
||||||
|
// Unfortunately they won't go away until upgrading to gtkmm4
|
||||||
_connection = Glib::signal_timeout().connect(handler, milliseconds);
|
_connection = Glib::signal_timeout().connect(handler, milliseconds);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1038,16 +1053,8 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
|
||||||
void Open3DConnexion() {}
|
void Open3DConnexion() {}
|
||||||
void Close3DConnexion() {}
|
void Close3DConnexion() {}
|
||||||
|
|
||||||
#if defined(HAVE_SPACEWARE) && defined(GDK_WINDOWING_X11)
|
#if defined(HAVE_SPACEWARE) && (defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND))
|
||||||
static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent, gpointer data) {
|
static void ProcessSpnavEvent(WindowImplGtk *window, const spnav_event &spnavEvent, bool shiftDown, bool controlDown) {
|
||||||
XEvent *xEvent = (XEvent *)gdkXEvent;
|
|
||||||
WindowImplGtk *window = (WindowImplGtk *)data;
|
|
||||||
|
|
||||||
spnav_event spnavEvent;
|
|
||||||
if(!spnav_x11_event(xEvent, &spnavEvent)) {
|
|
||||||
return GDK_FILTER_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(spnavEvent.type) {
|
switch(spnavEvent.type) {
|
||||||
case SPNAV_EVENT_MOTION: {
|
case SPNAV_EVENT_MOTION: {
|
||||||
SixDofEvent event = {};
|
SixDofEvent event = {};
|
||||||
|
@ -1058,8 +1065,8 @@ static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent,
|
||||||
event.rotationX = (double)spnavEvent.motion.rx * 0.001;
|
event.rotationX = (double)spnavEvent.motion.rx * 0.001;
|
||||||
event.rotationY = (double)spnavEvent.motion.ry * 0.001;
|
event.rotationY = (double)spnavEvent.motion.ry * 0.001;
|
||||||
event.rotationZ = (double)spnavEvent.motion.rz * -0.001;
|
event.rotationZ = (double)spnavEvent.motion.rz * -0.001;
|
||||||
event.shiftDown = xEvent->xmotion.state & ShiftMask;
|
event.shiftDown = shiftDown;
|
||||||
event.controlDown = xEvent->xmotion.state & ControlMask;
|
event.controlDown = controlDown;
|
||||||
if(window->onSixDofEvent) {
|
if(window->onSixDofEvent) {
|
||||||
window->onSixDofEvent(event);
|
window->onSixDofEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -1075,17 +1082,52 @@ static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent,
|
||||||
}
|
}
|
||||||
switch(spnavEvent.button.bnum) {
|
switch(spnavEvent.button.bnum) {
|
||||||
case 0: event.button = SixDofEvent::Button::FIT; break;
|
case 0: event.button = SixDofEvent::Button::FIT; break;
|
||||||
default: return GDK_FILTER_REMOVE;
|
default: return;
|
||||||
}
|
}
|
||||||
event.shiftDown = xEvent->xmotion.state & ShiftMask;
|
event.shiftDown = shiftDown;
|
||||||
event.controlDown = xEvent->xmotion.state & ControlMask;
|
event.controlDown = controlDown;
|
||||||
if(window->onSixDofEvent) {
|
if(window->onSixDofEvent) {
|
||||||
window->onSixDofEvent(event);
|
window->onSixDofEvent(event);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GDK_FILTER_REMOVE;
|
static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent, gpointer data) {
|
||||||
|
XEvent *xEvent = (XEvent *)gdkXEvent;
|
||||||
|
WindowImplGtk *window = (WindowImplGtk *)data;
|
||||||
|
bool shiftDown = (xEvent->xmotion.state & ShiftMask) != 0;
|
||||||
|
bool controlDown = (xEvent->xmotion.state & ControlMask) != 0;
|
||||||
|
|
||||||
|
spnav_event spnavEvent;
|
||||||
|
if(spnav_x11_event(xEvent, &spnavEvent)) {
|
||||||
|
ProcessSpnavEvent(window, spnavEvent, shiftDown, controlDown);
|
||||||
|
return GDK_FILTER_REMOVE;
|
||||||
|
}
|
||||||
|
return GDK_FILTER_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean ConsumeSpnavQueue(GIOChannel *, GIOCondition, gpointer data) {
|
||||||
|
WindowImplGtk *window = (WindowImplGtk *)data;
|
||||||
|
Glib::RefPtr<Gdk::Window> gdkWindow = window->gtkWindow.get_window();
|
||||||
|
|
||||||
|
// We don't get modifier state through the socket.
|
||||||
|
int x, y;
|
||||||
|
Gdk::ModifierType mask{};
|
||||||
|
#if GTK_CHECK_VERSION(3, 20, 0)
|
||||||
|
Glib::RefPtr<Gdk::Device> device = gdkWindow->get_display()->get_default_seat()->get_pointer();
|
||||||
|
#else
|
||||||
|
Glib::RefPtr<Gdk::Device> device = gdkWindow->get_display()->get_device_manager()->get_client_pointer();
|
||||||
|
#endif
|
||||||
|
gdkWindow->get_device_position(device, x, y, mask);
|
||||||
|
bool shiftDown = (mask & Gdk::SHIFT_MASK) != 0;
|
||||||
|
bool controlDown = (mask & Gdk::CONTROL_MASK) != 0;
|
||||||
|
|
||||||
|
spnav_event spnavEvent;
|
||||||
|
while(spnav_poll_event(&spnavEvent)) {
|
||||||
|
ProcessSpnavEvent(window, spnavEvent, shiftDown, controlDown);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request3DConnexionEventsForWindow(WindowRef window) {
|
void Request3DConnexionEventsForWindow(WindowRef window) {
|
||||||
|
@ -1093,11 +1135,26 @@ void Request3DConnexionEventsForWindow(WindowRef window) {
|
||||||
std::static_pointer_cast<WindowImplGtk>(window);
|
std::static_pointer_cast<WindowImplGtk>(window);
|
||||||
|
|
||||||
Glib::RefPtr<Gdk::Window> gdkWindow = windowImpl->gtkWindow.get_window();
|
Glib::RefPtr<Gdk::Window> gdkWindow = windowImpl->gtkWindow.get_window();
|
||||||
|
#if defined(GDK_WINDOWING_X11)
|
||||||
if(GDK_IS_X11_DISPLAY(gdkWindow->get_display()->gobj())) {
|
if(GDK_IS_X11_DISPLAY(gdkWindow->get_display()->gobj())) {
|
||||||
gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get());
|
if(spnav_x11_open(gdk_x11_get_default_xdisplay(),
|
||||||
spnav_x11_open(gdk_x11_get_default_xdisplay(),
|
gdk_x11_window_get_xid(gdkWindow->gobj())) != -1) {
|
||||||
gdk_x11_window_get_xid(gdkWindow->gobj()));
|
gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get());
|
||||||
|
} else if(spnav_open() != -1) {
|
||||||
|
g_io_add_watch(g_io_channel_unix_new(spnav_fd()), G_IO_IN,
|
||||||
|
ConsumeSpnavQueue, windowImpl.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(GDK_WINDOWING_WAYLAND)
|
||||||
|
if(GDK_IS_WAYLAND_DISPLAY(gdkWindow->get_display()->gobj())) {
|
||||||
|
if(spnav_open() != -1) {
|
||||||
|
g_io_add_watch(g_io_channel_unix_new(spnav_fd()), G_IO_IN,
|
||||||
|
ConsumeSpnavQueue, windowImpl.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void Request3DConnexionEventsForWindow(WindowRef window) {}
|
void Request3DConnexionEventsForWindow(WindowRef window) {}
|
||||||
|
@ -1303,7 +1360,7 @@ public:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Platform::Path path = GetFilename();
|
Platform::Path path = GetFilename();
|
||||||
if(gtkChooser->get_action() != GTK_FILE_CHOOSER_ACTION_OPEN) {
|
if(gtkChooser->get_action() != Gtk::FILE_CHOOSER_ACTION_OPEN) {
|
||||||
SetCurrentName(path.WithExtension(extension).FileName());
|
SetCurrentName(path.WithExtension(extension).FileName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,7 +286,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopUp() override {
|
void PopUp() override {
|
||||||
[NSMenu popUpContextMenu:nsMenu withEvent:[NSApp currentEvent] forView:nil];
|
NSEvent* event = [NSApp currentEvent];
|
||||||
|
[NSMenu popUpContextMenu:nsMenu withEvent:event forView:event.window.contentView];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear() override {
|
void Clear() override {
|
||||||
|
@ -358,18 +359,25 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
- (void)didEdit:(NSString *)text;
|
- (void)didEdit:(NSString *)text;
|
||||||
|
|
||||||
@property double scrollerMin;
|
@property double scrollerMin;
|
||||||
@property double scrollerMax;
|
@property double scrollerSize;
|
||||||
|
@property double pageSize;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation SSView
|
@implementation SSView
|
||||||
{
|
{
|
||||||
NSTrackingArea *trackingArea;
|
NSTrackingArea *trackingArea;
|
||||||
NSTextField *editor;
|
NSTextField *editor;
|
||||||
|
double magnificationGestureCurrentZ;
|
||||||
|
double rotationGestureCurrent;
|
||||||
|
Point2d trackpadPositionShift;
|
||||||
|
bool inTrackpadScrollGesture;
|
||||||
|
Platform::Window::Kind kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize acceptsFirstResponder;
|
@synthesize acceptsFirstResponder;
|
||||||
|
|
||||||
- (id)initWithFrame:(NSRect)frameRect {
|
- (id)initWithKind:(Platform::Window::Kind)aKind {
|
||||||
NSOpenGLPixelFormatAttribute attrs[] = {
|
NSOpenGLPixelFormatAttribute attrs[] = {
|
||||||
NSOpenGLPFADoubleBuffer,
|
NSOpenGLPFADoubleBuffer,
|
||||||
NSOpenGLPFAColorSize, 24,
|
NSOpenGLPFAColorSize, 24,
|
||||||
|
@ -377,7 +385,7 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
||||||
if(self = [super initWithFrame:frameRect pixelFormat:pixelFormat]) {
|
if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0) pixelFormat:pixelFormat]) {
|
||||||
self.wantsBestResolutionOpenGLSurface = YES;
|
self.wantsBestResolutionOpenGLSurface = YES;
|
||||||
self.wantsLayer = YES;
|
self.wantsLayer = YES;
|
||||||
editor = [[NSTextField alloc] init];
|
editor = [[NSTextField alloc] init];
|
||||||
|
@ -387,6 +395,18 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
editor.bezeled = NO;
|
editor.bezeled = NO;
|
||||||
editor.target = self;
|
editor.target = self;
|
||||||
editor.action = @selector(didEdit:);
|
editor.action = @selector(didEdit:);
|
||||||
|
|
||||||
|
inTrackpadScrollGesture = false;
|
||||||
|
kind = aKind;
|
||||||
|
if(kind == Platform::Window::Kind::TOPLEVEL) {
|
||||||
|
NSGestureRecognizer *mag = [[NSMagnificationGestureRecognizer alloc] initWithTarget:self
|
||||||
|
action:@selector(magnifyGesture:)];
|
||||||
|
[self addGestureRecognizer:mag];
|
||||||
|
|
||||||
|
NSRotationGestureRecognizer* rot = [[NSRotationGestureRecognizer alloc] initWithTarget:self
|
||||||
|
action:@selector(rotateGesture:)];
|
||||||
|
[self addGestureRecognizer:rot];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -427,9 +447,9 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
- (Platform::MouseEvent)convertMouseEvent:(NSEvent *)nsEvent {
|
- (Platform::MouseEvent)convertMouseEvent:(NSEvent *)nsEvent {
|
||||||
Platform::MouseEvent event = {};
|
Platform::MouseEvent event = {};
|
||||||
|
|
||||||
NSPoint nsPoint = [self convertPoint:nsEvent.locationInWindow fromView:self];
|
NSPoint nsPoint = [self convertPoint:nsEvent.locationInWindow fromView:nil];
|
||||||
event.x = nsPoint.x;
|
event.x = nsPoint.x;
|
||||||
event.y = self.bounds.size.height - nsPoint.y;
|
event.y = nsPoint.y;
|
||||||
|
|
||||||
NSUInteger nsFlags = [nsEvent modifierFlags];
|
NSUInteger nsFlags = [nsEvent modifierFlags];
|
||||||
if(nsFlags & NSEventModifierFlagShift) event.shiftDown = true;
|
if(nsFlags & NSEventModifierFlagShift) event.shiftDown = true;
|
||||||
|
@ -553,14 +573,57 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
using Platform::MouseEvent;
|
using Platform::MouseEvent;
|
||||||
|
|
||||||
MouseEvent event = [self convertMouseEvent:nsEvent];
|
MouseEvent event = [self convertMouseEvent:nsEvent];
|
||||||
|
if(nsEvent.subtype == NSEventSubtypeTabletPoint && kind == Platform::Window::Kind::TOPLEVEL) {
|
||||||
|
// This is how Cocoa represents 2 finger trackpad drag gestures, rather than going via
|
||||||
|
// NSPanGestureRecognizer which is how you might expect this to work... We complicate this
|
||||||
|
// further by also handling shift-two-finger-drag to mean rotate. Fortunately we're using
|
||||||
|
// shift in the same way as right-mouse-button MouseEvent does (to converts a pan to a
|
||||||
|
// rotate) so we get the rotate support for free. It's a bit ugly having to fake mouse
|
||||||
|
// events and track the deviation from the actual mouse cursor with trackpadPositionShift,
|
||||||
|
// but in lieu of an event API that allows us to request a rotate/pan with relative
|
||||||
|
// coordinates, it's the best we can do.
|
||||||
|
event.button = MouseEvent::Button::RIGHT;
|
||||||
|
// Make sure control (actually cmd) isn't passed through, ctrl-right-click-drag has special
|
||||||
|
// meaning as rotate which we don't want to inadvertently trigger.
|
||||||
|
event.controlDown = false;
|
||||||
|
if(nsEvent.scrollingDeltaX == 0 && nsEvent.scrollingDeltaY == 0) {
|
||||||
|
// Cocoa represents the point where the user lifts their fingers off (and any inertial
|
||||||
|
// scrolling has finished) by an event with scrollingDeltaX and scrollingDeltaY both 0.
|
||||||
|
// Sometimes you also get a zero scroll at the start of a two-finger-rotate (probably
|
||||||
|
// reflecting the internal implementation of that being a cancelled possible pan
|
||||||
|
// gesture), which is why this conditional is structured the way it is.
|
||||||
|
if(inTrackpadScrollGesture) {
|
||||||
|
event.x += trackpadPositionShift.x;
|
||||||
|
event.y += trackpadPositionShift.y;
|
||||||
|
event.type = MouseEvent::Type::RELEASE;
|
||||||
|
receiver->onMouseEvent(event);
|
||||||
|
inTrackpadScrollGesture = false;
|
||||||
|
trackpadPositionShift = Point2d::From(0, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if(!inTrackpadScrollGesture) {
|
||||||
|
inTrackpadScrollGesture = true;
|
||||||
|
trackpadPositionShift = Point2d::From(0, 0);
|
||||||
|
event.type = MouseEvent::Type::PRESS;
|
||||||
|
receiver->onMouseEvent(event);
|
||||||
|
// And drop through
|
||||||
|
}
|
||||||
|
|
||||||
|
trackpadPositionShift.x += nsEvent.scrollingDeltaX;
|
||||||
|
trackpadPositionShift.y += nsEvent.scrollingDeltaY;
|
||||||
|
event.type = MouseEvent::Type::MOTION;
|
||||||
|
event.x += trackpadPositionShift.x;
|
||||||
|
event.y += trackpadPositionShift.y;
|
||||||
|
receiver->onMouseEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
event.type = MouseEvent::Type::SCROLL_VERT;
|
event.type = MouseEvent::Type::SCROLL_VERT;
|
||||||
|
|
||||||
bool isPrecise = [nsEvent hasPreciseScrollingDeltas];
|
bool isPrecise = [nsEvent hasPreciseScrollingDeltas];
|
||||||
event.scrollDelta = [nsEvent scrollingDeltaY] / (isPrecise ? 50 : 5);
|
event.scrollDelta = [nsEvent scrollingDeltaY] / (isPrecise ? 50 : 5);
|
||||||
|
|
||||||
if(receiver->onMouseEvent) {
|
receiver->onMouseEvent(event);
|
||||||
receiver->onMouseEvent(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseExited:(NSEvent *)nsEvent {
|
- (void)mouseExited:(NSEvent *)nsEvent {
|
||||||
|
@ -638,6 +701,50 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
[super keyUp:nsEvent];
|
[super keyUp:nsEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)magnifyGesture:(NSMagnificationGestureRecognizer *)gesture {
|
||||||
|
// The onSixDofEvent API doesn't allow us to specify the scaling's origin, so for expediency
|
||||||
|
// we fake out a scrollwheel MouseEvent with a suitably-scaled scrollDelta with a bit of
|
||||||
|
// absolute-to-relative positioning conversion tracked using magnificationGestureCurrentZ.
|
||||||
|
|
||||||
|
if(gesture.state == NSGestureRecognizerStateBegan) {
|
||||||
|
magnificationGestureCurrentZ = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Magic number to make gesture.magnification align roughly with what scrollDelta expects
|
||||||
|
constexpr double kScale = 10.0;
|
||||||
|
double z = ((double)gesture.magnification * kScale);
|
||||||
|
double zdelta = z - magnificationGestureCurrentZ;
|
||||||
|
magnificationGestureCurrentZ = z;
|
||||||
|
|
||||||
|
using Platform::MouseEvent;
|
||||||
|
MouseEvent event = {};
|
||||||
|
event.type = MouseEvent::Type::SCROLL_VERT;
|
||||||
|
NSPoint nsPoint = [gesture locationInView:self];
|
||||||
|
event.x = nsPoint.x;
|
||||||
|
event.y = nsPoint.y;
|
||||||
|
event.scrollDelta = zdelta;
|
||||||
|
if(receiver->onMouseEvent) {
|
||||||
|
receiver->onMouseEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)rotateGesture:(NSRotationGestureRecognizer *)gesture {
|
||||||
|
if(gesture.state == NSGestureRecognizerStateBegan) {
|
||||||
|
rotationGestureCurrent = 0.0;
|
||||||
|
}
|
||||||
|
double rotation = gesture.rotation;
|
||||||
|
double rotationDelta = rotation - rotationGestureCurrent;
|
||||||
|
rotationGestureCurrent = rotation;
|
||||||
|
|
||||||
|
using Platform::SixDofEvent;
|
||||||
|
SixDofEvent event = {};
|
||||||
|
event.type = SixDofEvent::Type::MOTION;
|
||||||
|
event.rotationZ = rotationDelta;
|
||||||
|
if(receiver->onSixDofEvent) {
|
||||||
|
receiver->onSixDofEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@synthesize editing;
|
@synthesize editing;
|
||||||
|
|
||||||
- (void)startEditing:(NSString *)text at:(NSPoint)origin withHeight:(double)fontHeight
|
- (void)startEditing:(NSString *)text at:(NSPoint)origin withHeight:(double)fontHeight
|
||||||
|
@ -698,11 +805,27 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize scrollerMin;
|
@synthesize scrollerMin;
|
||||||
@synthesize scrollerMax;
|
@synthesize scrollerSize;
|
||||||
|
@synthesize pageSize;
|
||||||
|
|
||||||
- (void)didScroll:(NSScroller *)sender {
|
- (void)didScroll:(NSScroller *)sender {
|
||||||
|
double pos;
|
||||||
|
switch(sender.hitPart) {
|
||||||
|
case NSScrollerKnob:
|
||||||
|
case NSScrollerKnobSlot:
|
||||||
|
pos = receiver->GetScrollbarPosition();
|
||||||
|
break;
|
||||||
|
case NSScrollerDecrementPage:
|
||||||
|
pos = receiver->GetScrollbarPosition() - pageSize;
|
||||||
|
break;
|
||||||
|
case NSScrollerIncrementPage:
|
||||||
|
pos = receiver->GetScrollbarPosition() + pageSize;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(receiver->onScrollbarAdjusted) {
|
if(receiver->onScrollbarAdjusted) {
|
||||||
double pos = scrollerMin + [sender doubleValue] * (scrollerMax - scrollerMin);
|
|
||||||
receiver->onScrollbarAdjusted(pos);
|
receiver->onScrollbarAdjusted(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -769,7 +892,7 @@ public:
|
||||||
NSString *nsToolTip;
|
NSString *nsToolTip;
|
||||||
|
|
||||||
WindowImplCocoa(Window::Kind kind, std::shared_ptr<WindowImplCocoa> parentWindow) {
|
WindowImplCocoa(Window::Kind kind, std::shared_ptr<WindowImplCocoa> parentWindow) {
|
||||||
ssView = [[SSView alloc] init];
|
ssView = [[SSView alloc] initWithKind:kind];
|
||||||
ssView.translatesAutoresizingMaskIntoConstraints = NO;
|
ssView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||||
ssView.receiver = this;
|
ssView.receiver = this;
|
||||||
|
|
||||||
|
@ -962,21 +1085,22 @@ public:
|
||||||
|
|
||||||
void ConfigureScrollbar(double min, double max, double pageSize) override {
|
void ConfigureScrollbar(double min, double max, double pageSize) override {
|
||||||
ssView.scrollerMin = min;
|
ssView.scrollerMin = min;
|
||||||
ssView.scrollerMax = max - pageSize;
|
ssView.scrollerSize = max + 1 - min;
|
||||||
[nsScroller setKnobProportion:(pageSize / (ssView.scrollerMax - ssView.scrollerMin))];
|
ssView.pageSize = pageSize;
|
||||||
|
nsScroller.knobProportion = pageSize / ssView.scrollerSize;
|
||||||
|
nsScroller.hidden = pageSize >= ssView.scrollerSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
double GetScrollbarPosition() override {
|
double GetScrollbarPosition() override {
|
||||||
|
// Platform::Window scrollbar positions are in the range [min, max+1 - pageSize] inclusive,
|
||||||
|
// and Cocoa scrollbars are from 0.0 to 1.0 inclusive, so we have to apply some scaling and
|
||||||
|
// transforming. (scrollerSize is max+1-min, see ConfigureScrollbar above)
|
||||||
return ssView.scrollerMin +
|
return ssView.scrollerMin +
|
||||||
[nsScroller doubleValue] * (ssView.scrollerMax - ssView.scrollerMin);
|
nsScroller.doubleValue * (ssView.scrollerSize - ssView.pageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetScrollbarPosition(double pos) override {
|
void SetScrollbarPosition(double pos) override {
|
||||||
if(pos > ssView.scrollerMax)
|
nsScroller.doubleValue = (pos - ssView.scrollerMin) / ( ssView.scrollerSize - ssView.pageSize);
|
||||||
pos = ssView.scrollerMax;
|
|
||||||
if(GetScrollbarPosition() == pos)
|
|
||||||
return;
|
|
||||||
[nsScroller setDoubleValue:(pos / (ssView.scrollerMax - ssView.scrollerMin))];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Invalidate() override {
|
void Invalidate() override {
|
||||||
|
@ -1426,9 +1550,22 @@ void OpenInBrowser(const std::string &url) {
|
||||||
- (IBAction)preferences:(id)sender;
|
- (IBAction)preferences:(id)sender;
|
||||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
|
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
|
||||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||||
|
|
||||||
|
@property BOOL exiting;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation SSApplicationDelegate
|
@implementation SSApplicationDelegate
|
||||||
|
|
||||||
|
@synthesize exiting;
|
||||||
|
|
||||||
|
- (id)init {
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.exiting = false;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)preferences:(id)sender {
|
- (IBAction)preferences:(id)sender {
|
||||||
if (!SS.GW.showTextWindow) {
|
if (!SS.GW.showTextWindow) {
|
||||||
SolveSpace::SS.GW.MenuView(SolveSpace::Command::SHOW_TEXT_WND);
|
SolveSpace::SS.GW.MenuView(SolveSpace::Command::SHOW_TEXT_WND);
|
||||||
|
@ -1443,12 +1580,27 @@ void OpenInBrowser(const std::string &url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||||
[[[NSApp mainWindow] delegate] windowShouldClose:[NSApp mainWindow]];
|
if(!SS.unsaved) {
|
||||||
return NSTerminateCancel;
|
return NSTerminateNow;
|
||||||
|
} else {
|
||||||
|
[self performSelectorOnMainThread:@selector(applicationTerminatePrompt) withObject:nil
|
||||||
|
waitUntilDone:NO modes:@[NSDefaultRunLoopMode, NSModalPanelRunLoopMode]];
|
||||||
|
return NSTerminateLater;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillTerminate:(NSNotification *)notification {
|
||||||
|
if(!exiting) {
|
||||||
|
// Prevent the Platform::ExitGui() call from SolveSpaceUI::Exit()
|
||||||
|
// triggering another terminate
|
||||||
|
exiting = true;
|
||||||
|
// Now let SS save settings etc
|
||||||
|
SS.Exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationTerminatePrompt {
|
- (void)applicationTerminatePrompt {
|
||||||
SolveSpace::SS.MenuFile(SolveSpace::Command::EXIT);
|
[NSApp replyToApplicationShouldTerminate:SS.OkayToStartNewFile()];
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -1469,6 +1621,14 @@ std::vector<std::string> InitGui(int argc, char **argv) {
|
||||||
ssDelegate = [[SSApplicationDelegate alloc] init];
|
ssDelegate = [[SSApplicationDelegate alloc] init];
|
||||||
NSApplication.sharedApplication.delegate = ssDelegate;
|
NSApplication.sharedApplication.delegate = ssDelegate;
|
||||||
|
|
||||||
|
// Setting this prevents "Show Tab Bar" and "Show All Tabs" items from being
|
||||||
|
// automagically added to the View menu
|
||||||
|
NSWindow.allowsAutomaticWindowTabbing = NO;
|
||||||
|
|
||||||
|
// And this prevents the duplicate "Enter Full Screen" menu item, see
|
||||||
|
// https://stackoverflow.com/questions/52154977/how-to-get-rid-of-enter-full-screen-menu-item
|
||||||
|
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
|
||||||
|
|
||||||
[NSBundle.mainBundle loadNibNamed:@"MainMenu" owner:nil topLevelObjects:nil];
|
[NSBundle.mainBundle loadNibNamed:@"MainMenu" owner:nil topLevelObjects:nil];
|
||||||
|
|
||||||
NSArray *languages = NSLocale.preferredLanguages;
|
NSArray *languages = NSLocale.preferredLanguages;
|
||||||
|
@ -1487,8 +1647,10 @@ void RunGui() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitGui() {
|
void ExitGui() {
|
||||||
[NSApp setDelegate:nil];
|
if(!ssDelegate.exiting) {
|
||||||
[NSApp terminate:nil];
|
ssDelegate.exiting = true;
|
||||||
|
[NSApp terminate:nil];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearGui() {}
|
void ClearGui() {}
|
||||||
|
|
|
@ -1229,7 +1229,7 @@ public:
|
||||||
sscheck(GetMonitorInfo(MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST), &mi));
|
sscheck(GetMonitorInfo(MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST), &mi));
|
||||||
|
|
||||||
// If it somehow ended up off-screen, then put it back.
|
// If it somehow ended up off-screen, then put it back.
|
||||||
// and make it visible by at least this portion of the scrren
|
// and make it visible by at least this portion of the screen
|
||||||
const LONG movein = 40;
|
const LONG movein = 40;
|
||||||
|
|
||||||
RECT mrc = mi.rcMonitor;
|
RECT mrc = mi.rcMonitor;
|
||||||
|
@ -1583,11 +1583,6 @@ public:
|
||||||
ofn.nMaxFile = sizeof(filenameWC) / sizeof(wchar_t);
|
ofn.nMaxFile = sizeof(filenameWC) / sizeof(wchar_t);
|
||||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
|
||||||
OFN_OVERWRITEPROMPT;
|
OFN_OVERWRITEPROMPT;
|
||||||
if(isSaveDialog) {
|
|
||||||
SetTitle(C_("title", "Save File"));
|
|
||||||
} else {
|
|
||||||
SetTitle(C_("title", "Open File"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTitle(std::string title) override {
|
void SetTitle(std::string title) override {
|
||||||
|
@ -1640,13 +1635,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RunModal() override {
|
bool RunModal() override {
|
||||||
if(GetFilename().IsEmpty()) {
|
|
||||||
SetFilename(Path::From(_("untitled")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isSaveDialog) {
|
if(isSaveDialog) {
|
||||||
|
SetTitle(C_("title", "Save File"));
|
||||||
|
if(GetFilename().IsEmpty()) {
|
||||||
|
SetFilename(Path::From(_("untitled")));
|
||||||
|
}
|
||||||
return GetSaveFileNameW(&ofn) == TRUE;
|
return GetSaveFileNameW(&ofn) == TRUE;
|
||||||
} else {
|
} else {
|
||||||
|
SetTitle(C_("title", "Open File"));
|
||||||
return GetOpenFileNameW(&ofn) == TRUE;
|
return GetOpenFileNameW(&ofn) == TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,11 +190,11 @@ void SurfaceRenderer::DrawFaces(const SMesh &m, const std::vector<uint32_t> &fac
|
||||||
void SurfaceRenderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
|
void SurfaceRenderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
|
||||||
const Vector &o, const Vector &u, const Vector &v,
|
const Vector &o, const Vector &u, const Vector &v,
|
||||||
const Point2d &ta, const Point2d &tb, hFill hcf) {
|
const Point2d &ta, const Point2d &tb, hFill hcf) {
|
||||||
ssassert(false, "Not implemented");
|
dbp("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceRenderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
|
void SurfaceRenderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
|
||||||
ssassert(false, "Not implemented");
|
dbp("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
26
src/sketch.h
|
@ -198,6 +198,9 @@ public:
|
||||||
// For drawings in 2d
|
// For drawings in 2d
|
||||||
WORKPLANE_BY_POINT_ORTHO = 6000,
|
WORKPLANE_BY_POINT_ORTHO = 6000,
|
||||||
WORKPLANE_BY_LINE_SEGMENTS = 6001,
|
WORKPLANE_BY_LINE_SEGMENTS = 6001,
|
||||||
|
WORKPLANE_BY_POINT_NORMAL = 6002,
|
||||||
|
//WORKPLANE_BY_POINT_FACE = 6003,
|
||||||
|
//WORKPLANE_BY_FACE = 6004,
|
||||||
// For extrudes, translates, and rotates
|
// For extrudes, translates, and rotates
|
||||||
ONE_SIDED = 7000,
|
ONE_SIDED = 7000,
|
||||||
TWO_SIDED = 7001
|
TWO_SIDED = 7001
|
||||||
|
@ -266,6 +269,7 @@ public:
|
||||||
void Generate(EntityList *entity, ParamList *param);
|
void Generate(EntityList *entity, ParamList *param);
|
||||||
bool IsSolvedOkay();
|
bool IsSolvedOkay();
|
||||||
void TransformImportedBy(Vector t, Quaternion q);
|
void TransformImportedBy(Vector t, Quaternion q);
|
||||||
|
bool IsTriangleMeshAssembly() const;
|
||||||
bool IsForcedToMeshBySource() const;
|
bool IsForcedToMeshBySource() const;
|
||||||
bool IsForcedToMesh() const;
|
bool IsForcedToMesh() const;
|
||||||
// When a request generates entities from entities, and the source
|
// When a request generates entities from entities, and the source
|
||||||
|
@ -323,6 +327,7 @@ public:
|
||||||
void DrawPolyError(Canvas *canvas);
|
void DrawPolyError(Canvas *canvas);
|
||||||
void DrawFilledPaths(Canvas *canvas);
|
void DrawFilledPaths(Canvas *canvas);
|
||||||
void DrawContourAreaLabels(Canvas *canvas);
|
void DrawContourAreaLabels(Canvas *canvas);
|
||||||
|
bool ShouldDrawExploded() const;
|
||||||
|
|
||||||
SPolygon GetPolygon();
|
SPolygon GetPolygon();
|
||||||
|
|
||||||
|
@ -368,6 +373,7 @@ public:
|
||||||
std::string font;
|
std::string font;
|
||||||
Platform::Path file;
|
Platform::Path file;
|
||||||
double aspectRatio;
|
double aspectRatio;
|
||||||
|
int groupRequestIndex;
|
||||||
|
|
||||||
static hParam AddParam(ParamList *param, hParam hp);
|
static hParam AddParam(ParamList *param, hParam hp);
|
||||||
void Generate(EntityList *entity, ParamList *param);
|
void Generate(EntityList *entity, ParamList *param);
|
||||||
|
@ -591,6 +597,10 @@ public:
|
||||||
beziers.l.Clear();
|
beziers.l.Clear();
|
||||||
edges.l.Clear();
|
edges.l.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShouldDrawExploded() const;
|
||||||
|
Vector ExplodeOffset() const;
|
||||||
|
Vector PointGetDrawNum() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EntReqTable {
|
class EntReqTable {
|
||||||
|
@ -673,7 +683,10 @@ public:
|
||||||
CURVE_CURVE_TANGENT = 125,
|
CURVE_CURVE_TANGENT = 125,
|
||||||
EQUAL_RADIUS = 130,
|
EQUAL_RADIUS = 130,
|
||||||
WHERE_DRAGGED = 200,
|
WHERE_DRAGGED = 200,
|
||||||
|
ARC_ARC_LEN_RATIO = 210,
|
||||||
|
ARC_LINE_LEN_RATIO = 211,
|
||||||
|
ARC_ARC_DIFFERENCE = 212,
|
||||||
|
ARC_LINE_DIFFERENCE = 213,
|
||||||
COMMENT = 1000
|
COMMENT = 1000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -757,7 +770,7 @@ public:
|
||||||
Vector p0, Vector p1, Vector pt, double salient);
|
Vector p0, Vector p1, Vector pt, double salient);
|
||||||
void DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
void DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
Vector a0, Vector da, Vector b0, Vector db,
|
Vector a0, Vector da, Vector b0, Vector db,
|
||||||
Vector offset, Vector *ref, bool trim);
|
Vector offset, Vector *ref, bool trim, Vector explodeOffset);
|
||||||
void DoArrow(Canvas *canvas, Canvas::hStroke hcs,
|
void DoArrow(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
Vector p, Vector dir, Vector n, double width, double angle, double da);
|
Vector p, Vector dir, Vector n, double width, double angle, double da);
|
||||||
void DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs,
|
void DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs,
|
||||||
|
@ -779,6 +792,8 @@ public:
|
||||||
|
|
||||||
std::string DescriptionString() const;
|
std::string DescriptionString() const;
|
||||||
|
|
||||||
|
bool ShouldDrawExploded() const;
|
||||||
|
|
||||||
static hConstraint AddConstraint(Constraint *c, bool rememberForUndo = true);
|
static hConstraint AddConstraint(Constraint *c, bool rememberForUndo = true);
|
||||||
static void MenuConstrain(Command id);
|
static void MenuConstrain(Command id);
|
||||||
static void DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA);
|
static void DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA);
|
||||||
|
@ -884,11 +899,14 @@ public:
|
||||||
double width;
|
double width;
|
||||||
int zIndex;
|
int zIndex;
|
||||||
bool exportable;
|
bool exportable;
|
||||||
|
StipplePattern stippleType;
|
||||||
} Default;
|
} Default;
|
||||||
static const Default Defaults[];
|
static const Default Defaults[];
|
||||||
|
|
||||||
static std::string CnfColor(const std::string &prefix);
|
static std::string CnfColor(const std::string &prefix);
|
||||||
static std::string CnfWidth(const std::string &prefix);
|
static std::string CnfWidth(const std::string &prefix);
|
||||||
|
static std::string CnfStippleType(const std::string &prefix);
|
||||||
|
static std::string CnfStippleScale(const std::string &prefix);
|
||||||
static std::string CnfTextHeight(const std::string &prefix);
|
static std::string CnfTextHeight(const std::string &prefix);
|
||||||
static std::string CnfPrefixToName(const std::string &prefix);
|
static std::string CnfPrefixToName(const std::string &prefix);
|
||||||
static std::string CnfExportable(const std::string &prefix);
|
static std::string CnfExportable(const std::string &prefix);
|
||||||
|
@ -918,7 +936,11 @@ public:
|
||||||
static bool Exportable(int hs);
|
static bool Exportable(int hs);
|
||||||
static hStyle ForEntity(hEntity he);
|
static hStyle ForEntity(hEntity he);
|
||||||
static StipplePattern PatternType(hStyle hs);
|
static StipplePattern PatternType(hStyle hs);
|
||||||
|
static double StippleScale(hStyle hs);
|
||||||
static double StippleScaleMm(hStyle hs);
|
static double StippleScaleMm(hStyle hs);
|
||||||
|
static std::string StipplePatternName(hStyle hs);
|
||||||
|
static std::string StipplePatternName(StipplePattern stippleType);
|
||||||
|
static StipplePattern StipplePatternFromString(std::string name);
|
||||||
|
|
||||||
std::string DescriptionString() const;
|
std::string DescriptionString() const;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ void SolveSpaceUI::Init() {
|
||||||
Platform::SettingsRef settings = Platform::GetSettings();
|
Platform::SettingsRef settings = Platform::GetSettings();
|
||||||
|
|
||||||
SS.tangentArcRadius = 10.0;
|
SS.tangentArcRadius = 10.0;
|
||||||
|
SS.explodeDistance = 1.0;
|
||||||
|
|
||||||
// Then, load the registry settings.
|
// Then, load the registry settings.
|
||||||
// Default list of colors for the model material
|
// Default list of colors for the model material
|
||||||
|
@ -104,6 +105,7 @@ void SolveSpaceUI::Init() {
|
||||||
exportCanvas.dy = settings->ThawFloat("ExportCanvas_Dy", 5.0);
|
exportCanvas.dy = settings->ThawFloat("ExportCanvas_Dy", 5.0);
|
||||||
// Extra parameters when exporting G code
|
// Extra parameters when exporting G code
|
||||||
gCode.depth = settings->ThawFloat("GCode_Depth", 10.0);
|
gCode.depth = settings->ThawFloat("GCode_Depth", 10.0);
|
||||||
|
gCode.safeHeight = settings->ThawFloat("GCode_SafeHeight", 5.0);
|
||||||
gCode.passes = settings->ThawInt("GCode_Passes", 1);
|
gCode.passes = settings->ThawInt("GCode_Passes", 1);
|
||||||
gCode.feed = settings->ThawFloat("GCode_Feed", 10.0);
|
gCode.feed = settings->ThawFloat("GCode_Feed", 10.0);
|
||||||
gCode.plungeFeed = settings->ThawFloat("GCode_PlungeFeed", 10.0);
|
gCode.plungeFeed = settings->ThawFloat("GCode_PlungeFeed", 10.0);
|
||||||
|
@ -315,6 +317,7 @@ void SolveSpaceUI::ScheduleAutosave() {
|
||||||
double SolveSpaceUI::MmPerUnit() {
|
double SolveSpaceUI::MmPerUnit() {
|
||||||
switch(viewUnits) {
|
switch(viewUnits) {
|
||||||
case Unit::INCHES: return 25.4;
|
case Unit::INCHES: return 25.4;
|
||||||
|
case Unit::FEET_INCHES: return 25.4; // The 'unit' is still inches
|
||||||
case Unit::METERS: return 1000.0;
|
case Unit::METERS: return 1000.0;
|
||||||
case Unit::MM: return 1.0;
|
case Unit::MM: return 1.0;
|
||||||
}
|
}
|
||||||
|
@ -323,14 +326,47 @@ double SolveSpaceUI::MmPerUnit() {
|
||||||
const char *SolveSpaceUI::UnitName() {
|
const char *SolveSpaceUI::UnitName() {
|
||||||
switch(viewUnits) {
|
switch(viewUnits) {
|
||||||
case Unit::INCHES: return "in";
|
case Unit::INCHES: return "in";
|
||||||
|
case Unit::FEET_INCHES: return "in";
|
||||||
case Unit::METERS: return "m";
|
case Unit::METERS: return "m";
|
||||||
case Unit::MM: return "mm";
|
case Unit::MM: return "mm";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SolveSpaceUI::MmToString(double v) {
|
std::string SolveSpaceUI::MmToString(double v, bool editable) {
|
||||||
v /= MmPerUnit();
|
v /= MmPerUnit();
|
||||||
|
// The syntax 2' 6" for feet and inches is not something we can (currently)
|
||||||
|
// parse back from a string so if editable is true, we treat FEET_INCHES the
|
||||||
|
// same as INCHES and just return the unadorned decimal number of inches.
|
||||||
|
if(viewUnits == Unit::FEET_INCHES && !editable) {
|
||||||
|
// Now convert v from inches to 64'ths of an inch, to make rounding easier.
|
||||||
|
v = floor((v + (1.0 / 128.0)) * 64.0);
|
||||||
|
int feet = (int)(v / (12.0 * 64.0));
|
||||||
|
v = v - (feet * 12.0 * 64.0);
|
||||||
|
// v is now the feet-less remainder in 1/64 inches
|
||||||
|
int inches = (int)(v / 64.0);
|
||||||
|
int numerator = (int)(v - ((double)inches * 64.0));
|
||||||
|
int denominator = 64;
|
||||||
|
// Divide down to smallest denominator where the numerator is still a whole number
|
||||||
|
while ((numerator != 0) && ((numerator & 1) == 0)) {
|
||||||
|
numerator /= 2;
|
||||||
|
denominator /= 2;
|
||||||
|
}
|
||||||
|
std::ostringstream str;
|
||||||
|
if(feet != 0) {
|
||||||
|
str << feet << "'-";
|
||||||
|
}
|
||||||
|
// For something like 0.5, show 1/2" rather than 0 1/2"
|
||||||
|
if(!(feet == 0 && inches == 0 && numerator != 0)) {
|
||||||
|
str << inches;
|
||||||
|
}
|
||||||
|
if(numerator != 0) {
|
||||||
|
str << " " << numerator << "/" << denominator;
|
||||||
|
}
|
||||||
|
str << "\"";
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
int digits = UnitDigitsAfterDecimal();
|
int digits = UnitDigitsAfterDecimal();
|
||||||
double minimum = 0.5 * pow(10,-digits);
|
double minimum = 0.5 * pow(10,-digits);
|
||||||
while ((v < minimum) && (v > LENGTH_EPS)) {
|
while ((v < minimum) && (v > LENGTH_EPS)) {
|
||||||
|
@ -349,7 +385,7 @@ static const char *DimToString(int dim) {
|
||||||
}
|
}
|
||||||
static std::pair<int, std::string> SelectSIPrefixMm(int ord, int dim) {
|
static std::pair<int, std::string> SelectSIPrefixMm(int ord, int dim) {
|
||||||
// decide what units to use depending on the order of magnitude of the
|
// decide what units to use depending on the order of magnitude of the
|
||||||
// measure in meters and the dimmension (1,2,3 lenear, area, volume)
|
// measure in meters and the dimension (1,2,3 lenear, area, volume)
|
||||||
switch(dim) {
|
switch(dim) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -378,7 +414,7 @@ static std::pair<int, std::string> SelectSIPrefixMm(int ord, int dim) {
|
||||||
default:
|
default:
|
||||||
dbp ("dimensions over 3 not supported");
|
dbp ("dimensions over 3 not supported");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return {0, "m"};
|
return {0, "m"};
|
||||||
}
|
}
|
||||||
static std::pair<int, std::string> SelectSIPrefixInch(int deg) {
|
static std::pair<int, std::string> SelectSIPrefixInch(int deg) {
|
||||||
|
@ -394,17 +430,22 @@ std::string SolveSpaceUI::MmToStringSI(double v, int dim) {
|
||||||
dim = 1;
|
dim = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
v /= pow((viewUnits == Unit::INCHES) ? 25.4 : 1000, dim);
|
bool inches = (viewUnits == Unit::INCHES) || (viewUnits == Unit::FEET_INCHES);
|
||||||
|
v /= pow(inches ? 25.4 : 1000, dim);
|
||||||
int vdeg = (int)(log10(fabs(v)));
|
int vdeg = (int)(log10(fabs(v)));
|
||||||
std::string unit;
|
std::string unit;
|
||||||
if(fabs(v) > 0.0) {
|
if(fabs(v) > 0.0) {
|
||||||
int sdeg = 0;
|
int sdeg = 0;
|
||||||
std::tie(sdeg, unit) =
|
std::tie(sdeg, unit) =
|
||||||
(viewUnits == Unit::INCHES)
|
inches
|
||||||
? SelectSIPrefixInch(vdeg/dim)
|
? SelectSIPrefixInch(vdeg/dim)
|
||||||
: SelectSIPrefixMm(vdeg, dim);
|
: SelectSIPrefixMm(vdeg, dim);
|
||||||
v /= pow(10.0, sdeg * dim);
|
v /= pow(10.0, sdeg * dim);
|
||||||
}
|
}
|
||||||
|
if(viewUnits == Unit::FEET_INCHES && fabs(v) > pow(12.0, dim)) {
|
||||||
|
unit = "ft";
|
||||||
|
v /= pow(12.0, dim);
|
||||||
|
}
|
||||||
int pdeg = (int)ceil(log10(fabs(v) + 1e-10));
|
int pdeg = (int)ceil(log10(fabs(v) + 1e-10));
|
||||||
return ssprintf("%.*g%s%s%s", pdeg + UnitDigitsAfterDecimal(), v,
|
return ssprintf("%.*g%s%s%s", pdeg + UnitDigitsAfterDecimal(), v,
|
||||||
compact ? "" : " ", unit.c_str(), DimToString(dim));
|
compact ? "" : " ", unit.c_str(), DimToString(dim));
|
||||||
|
@ -434,10 +475,11 @@ int SolveSpaceUI::GetMaxSegments() {
|
||||||
return maxSegments;
|
return maxSegments;
|
||||||
}
|
}
|
||||||
int SolveSpaceUI::UnitDigitsAfterDecimal() {
|
int SolveSpaceUI::UnitDigitsAfterDecimal() {
|
||||||
return (viewUnits == Unit::INCHES) ? afterDecimalInch : afterDecimalMm;
|
return (viewUnits == Unit::INCHES || viewUnits == Unit::FEET_INCHES) ?
|
||||||
|
afterDecimalInch : afterDecimalMm;
|
||||||
}
|
}
|
||||||
void SolveSpaceUI::SetUnitDigitsAfterDecimal(int v) {
|
void SolveSpaceUI::SetUnitDigitsAfterDecimal(int v) {
|
||||||
if(viewUnits == Unit::INCHES) {
|
if(viewUnits == Unit::INCHES || viewUnits == Unit::FEET_INCHES) {
|
||||||
afterDecimalInch = v;
|
afterDecimalInch = v;
|
||||||
} else {
|
} else {
|
||||||
afterDecimalMm = v;
|
afterDecimalMm = v;
|
||||||
|
@ -764,7 +806,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
|
||||||
SS.TW.stepDim.isDistance =
|
SS.TW.stepDim.isDistance =
|
||||||
(c->type != Constraint::Type::ANGLE) &&
|
(c->type != Constraint::Type::ANGLE) &&
|
||||||
(c->type != Constraint::Type::LENGTH_RATIO) &&
|
(c->type != Constraint::Type::LENGTH_RATIO) &&
|
||||||
(c->type != Constraint::Type::LENGTH_DIFFERENCE);
|
(c->type != Constraint::Type::ARC_ARC_LEN_RATIO) &&
|
||||||
|
(c->type != Constraint::Type::ARC_LINE_LEN_RATIO) &&
|
||||||
|
(c->type != Constraint::Type::LENGTH_DIFFERENCE) &&
|
||||||
|
(c->type != Constraint::Type::ARC_ARC_DIFFERENCE) &&
|
||||||
|
(c->type != Constraint::Type::ARC_LINE_DIFFERENCE) ;
|
||||||
SS.TW.shown.constraint = c->h;
|
SS.TW.shown.constraint = c->h;
|
||||||
SS.TW.shown.screen = TextWindow::Screen::STEP_DIMENSION;
|
SS.TW.shown.screen = TextWindow::Screen::STEP_DIMENSION;
|
||||||
|
|
||||||
|
@ -1026,12 +1072,14 @@ void SolveSpaceUI::Clear() {
|
||||||
GW.showGridMenuItem = NULL;
|
GW.showGridMenuItem = NULL;
|
||||||
GW.dimSolidModelMenuItem = NULL;
|
GW.dimSolidModelMenuItem = NULL;
|
||||||
GW.perspectiveProjMenuItem = NULL;
|
GW.perspectiveProjMenuItem = NULL;
|
||||||
|
GW.explodeMenuItem = NULL;
|
||||||
GW.showToolbarMenuItem = NULL;
|
GW.showToolbarMenuItem = NULL;
|
||||||
GW.showTextWndMenuItem = NULL;
|
GW.showTextWndMenuItem = NULL;
|
||||||
GW.fullScreenMenuItem = NULL;
|
GW.fullScreenMenuItem = NULL;
|
||||||
GW.unitsMmMenuItem = NULL;
|
GW.unitsMmMenuItem = NULL;
|
||||||
GW.unitsMetersMenuItem = NULL;
|
GW.unitsMetersMenuItem = NULL;
|
||||||
GW.unitsInchesMenuItem = NULL;
|
GW.unitsInchesMenuItem = NULL;
|
||||||
|
GW.unitsFeetInchesMenuItem = NULL;
|
||||||
GW.inWorkplaneMenuItem = NULL;
|
GW.inWorkplaneMenuItem = NULL;
|
||||||
GW.in3dMenuItem = NULL;
|
GW.in3dMenuItem = NULL;
|
||||||
GW.undoMenuItem = NULL;
|
GW.undoMenuItem = NULL;
|
||||||
|
|
|
@ -138,7 +138,8 @@ enum class Command : uint32_t;
|
||||||
enum class Unit : uint32_t {
|
enum class Unit : uint32_t {
|
||||||
MM = 0,
|
MM = 0,
|
||||||
INCHES,
|
INCHES,
|
||||||
METERS
|
METERS,
|
||||||
|
FEET_INCHES
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Key, class T>
|
template<class Key, class T>
|
||||||
|
@ -597,6 +598,7 @@ public:
|
||||||
} exportCanvas;
|
} exportCanvas;
|
||||||
struct {
|
struct {
|
||||||
double depth;
|
double depth;
|
||||||
|
double safeHeight;
|
||||||
int passes;
|
int passes;
|
||||||
double feed;
|
double feed;
|
||||||
double plungeFeed;
|
double plungeFeed;
|
||||||
|
@ -608,8 +610,10 @@ public:
|
||||||
int afterDecimalDegree;
|
int afterDecimalDegree;
|
||||||
bool useSIPrefixes;
|
bool useSIPrefixes;
|
||||||
int autosaveInterval; // in minutes
|
int autosaveInterval; // in minutes
|
||||||
|
bool explode;
|
||||||
|
double explodeDistance;
|
||||||
|
|
||||||
std::string MmToString(double v);
|
std::string MmToString(double v, bool editable=false);
|
||||||
std::string MmToStringSI(double v, int dim = 0);
|
std::string MmToStringSI(double v, int dim = 0);
|
||||||
std::string DegreeToString(double v);
|
std::string DegreeToString(double v);
|
||||||
double ExprToMm(Expr *e);
|
double ExprToMm(Expr *e);
|
||||||
|
@ -812,6 +816,7 @@ public:
|
||||||
void ImportDxf(const Platform::Path &file);
|
void ImportDxf(const Platform::Path &file);
|
||||||
void ImportDwg(const Platform::Path &file);
|
void ImportDwg(const Platform::Path &file);
|
||||||
bool LinkIDF(const Platform::Path &filename, EntityList *le, SMesh *m, SShell *sh);
|
bool LinkIDF(const Platform::Path &filename, EntityList *le, SMesh *m, SShell *sh);
|
||||||
|
bool LinkStl(const Platform::Path &filename, EntityList *le, SMesh *m, SShell *sh);
|
||||||
|
|
||||||
extern SolveSpaceUI SS;
|
extern SolveSpaceUI SS;
|
||||||
extern Sketch SK;
|
extern Sketch SK;
|
||||||
|
|
|
@ -29,7 +29,7 @@ void SCurve::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We will be inserting other curve verticies into our curves to split them.
|
// We will be inserting other curve vertices into our curves to split them.
|
||||||
// This is helpful when curved surfaces become tangent along a trim and the
|
// This is helpful when curved surfaces become tangent along a trim and the
|
||||||
// usual tests for curve-surface intersection don't split the curve at a vertex.
|
// usual tests for curve-surface intersection don't split the curve at a vertex.
|
||||||
// This is faster than the previous version that split at surface corners and
|
// This is faster than the previous version that split at surface corners and
|
||||||
|
@ -521,20 +521,19 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
|
||||||
SEdgeList inter = {};
|
SEdgeList inter = {};
|
||||||
|
|
||||||
SSurface *ss;
|
SSurface *ss;
|
||||||
SCurve *sc;
|
for(SCurve &sc : into->curve) {
|
||||||
for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) {
|
if(sc.source != SCurve::Source::INTERSECTION) continue;
|
||||||
if(sc->source != SCurve::Source::INTERSECTION) continue;
|
|
||||||
if(opA) {
|
if(opA) {
|
||||||
if(sc->surfA != h) continue;
|
if(sc.surfA != h) continue;
|
||||||
ss = shb->surface.FindById(sc->surfB);
|
ss = shb->surface.FindById(sc.surfB);
|
||||||
} else {
|
} else {
|
||||||
if(sc->surfB != h) continue;
|
if(sc.surfB != h) continue;
|
||||||
ss = sha->surface.FindById(sc->surfA);
|
ss = sha->surface.FindById(sc.surfA);
|
||||||
}
|
}
|
||||||
int i;
|
int i;
|
||||||
for(i = 1; i < sc->pts.n; i++) {
|
for(i = 1; i < sc.pts.n; i++) {
|
||||||
Vector a = sc->pts[i-1].p,
|
Vector a = sc.pts[i-1].p,
|
||||||
b = sc->pts[i].p;
|
b = sc.pts[i].p;
|
||||||
|
|
||||||
Point2d auv, buv;
|
Point2d auv, buv;
|
||||||
ss->ClosestPointTo(a, &(auv.x), &(auv.y));
|
ss->ClosestPointTo(a, &(auv.x), &(auv.y));
|
||||||
|
@ -560,9 +559,9 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
|
||||||
bkwds = !bkwds;
|
bkwds = !bkwds;
|
||||||
}
|
}
|
||||||
if(bkwds) {
|
if(bkwds) {
|
||||||
inter.AddEdge(tb, ta, sc->h.v, 1);
|
inter.AddEdge(tb, ta, sc.h.v, 1);
|
||||||
} else {
|
} else {
|
||||||
inter.AddEdge(ta, tb, sc->h.v, 0);
|
inter.AddEdge(ta, tb, sc.h.v, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,20 +710,18 @@ void SShell::MakeIntersectionCurvesAgainst(SShell *agnst, SShell *into) {
|
||||||
for(int i = 0; i< surface.n; i++) {
|
for(int i = 0; i< surface.n; i++) {
|
||||||
SSurface *sa = &surface[i];
|
SSurface *sa = &surface[i];
|
||||||
|
|
||||||
SSurface *sb;
|
for(SSurface &sb : agnst->surface){
|
||||||
for(sb = agnst->surface.First(); sb; sb = agnst->surface.NextAfter(sb)){
|
|
||||||
// Intersect every surface from our shell against every surface
|
// Intersect every surface from our shell against every surface
|
||||||
// from agnst; this will add zero or more curves to the curve
|
// from agnst; this will add zero or more curves to the curve
|
||||||
// list for into.
|
// list for into.
|
||||||
sa->IntersectAgainst(sb, this, agnst, into);
|
sa->IntersectAgainst(&sb, this, agnst, into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SShell::CleanupAfterBoolean() {
|
void SShell::CleanupAfterBoolean() {
|
||||||
SSurface *ss;
|
for(SSurface &ss : surface) {
|
||||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
ss.edges.Clear();
|
||||||
ss->edges.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,10 +731,9 @@ void SShell::CleanupAfterBoolean() {
|
||||||
// by their new IDs.
|
// by their new IDs.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void SShell::RewriteSurfaceHandlesForCurves(SShell *a, SShell *b) {
|
void SShell::RewriteSurfaceHandlesForCurves(SShell *a, SShell *b) {
|
||||||
SCurve *sc;
|
for(SCurve &sc : curve) {
|
||||||
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
|
sc.surfA = sc.GetSurfaceA(a, b)->newH,
|
||||||
sc->surfA = sc->GetSurfaceA(a, b)->newH,
|
sc.surfB = sc.GetSurfaceB(a, b)->newH;
|
||||||
sc->surfB = sc->GetSurfaceB(a, b)->newH;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,32 +755,32 @@ void SShell::MakeFromAssemblyOf(SShell *a, SShell *b) {
|
||||||
// First, copy over all the curves. Note which shell (a or b) each curve
|
// First, copy over all the curves. Note which shell (a or b) each curve
|
||||||
// came from, but assign it a new ID.
|
// came from, but assign it a new ID.
|
||||||
curve.ReserveMore(a->curve.n + b->curve.n);
|
curve.ReserveMore(a->curve.n + b->curve.n);
|
||||||
SCurve *c, cn;
|
SCurve cn;
|
||||||
for(i = 0; i < 2; i++) {
|
for(i = 0; i < 2; i++) {
|
||||||
ab = (i == 0) ? a : b;
|
ab = (i == 0) ? a : b;
|
||||||
for(c = ab->curve.First(); c; c = ab->curve.NextAfter(c)) {
|
for(SCurve &c : ab->curve) {
|
||||||
cn = SCurve::FromTransformationOf(c, t, q, 1.0);
|
cn = SCurve::FromTransformationOf(&c, t, q, 1.0);
|
||||||
cn.source = (i == 0) ? SCurve::Source::A : SCurve::Source::B;
|
cn.source = (i == 0) ? SCurve::Source::A : SCurve::Source::B;
|
||||||
// surfA and surfB are wrong now, and we can't fix them until
|
// surfA and surfB are wrong now, and we can't fix them until
|
||||||
// we've assigned IDs to the surfaces. So we'll get that later.
|
// we've assigned IDs to the surfaces. So we'll get that later.
|
||||||
c->newH = curve.AddAndAssignId(&cn);
|
c.newH = curve.AddAndAssignId(&cn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Likewise copy over all the surfaces.
|
// Likewise copy over all the surfaces.
|
||||||
surface.ReserveMore(a->surface.n + b->surface.n);
|
surface.ReserveMore(a->surface.n + b->surface.n);
|
||||||
SSurface *s, sn;
|
SSurface sn;
|
||||||
for(i = 0; i < 2; i++) {
|
for(i = 0; i < 2; i++) {
|
||||||
ab = (i == 0) ? a : b;
|
ab = (i == 0) ? a : b;
|
||||||
for(s = ab->surface.First(); s; s = ab->surface.NextAfter(s)) {
|
for(SSurface &s : ab->surface) {
|
||||||
sn = SSurface::FromTransformationOf(s, t, q, 1.0, /*includingTrims=*/true);
|
sn = SSurface::FromTransformationOf(&s, t, q, 1.0, /*includingTrims=*/true);
|
||||||
// All the trim curve IDs get rewritten; we know the new handles
|
// All the trim curve IDs get rewritten; we know the new handles
|
||||||
// to the curves since we recorded them in the previous step.
|
// to the curves since we recorded them in the previous step.
|
||||||
STrimBy *stb;
|
STrimBy *stb;
|
||||||
for(stb = sn.trim.First(); stb; stb = sn.trim.NextAfter(stb)) {
|
for(stb = sn.trim.First(); stb; stb = sn.trim.NextAfter(stb)) {
|
||||||
stb->curve = ab->curve.FindById(stb->curve)->newH;
|
stb->curve = ab->curve.FindById(stb->curve)->newH;
|
||||||
}
|
}
|
||||||
s->newH = surface.AddAndAssignId(&sn);
|
s.newH = surface.AddAndAssignId(&sn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -800,7 +796,7 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type) {
|
||||||
b->MakeClassifyingBsps(NULL);
|
b->MakeClassifyingBsps(NULL);
|
||||||
|
|
||||||
// Copy over all the original curves, splitting them so that a
|
// Copy over all the original curves, splitting them so that a
|
||||||
// piecwise linear segment never crosses a surface from the other
|
// piecewise linear segment never crosses a surface from the other
|
||||||
// shell.
|
// shell.
|
||||||
a->CopyCurvesSplitAgainst(/*opA=*/true, b, this);
|
a->CopyCurvesSplitAgainst(/*opA=*/true, b, this);
|
||||||
b->CopyCurvesSplitAgainst(/*opA=*/false, a, this);
|
b->CopyCurvesSplitAgainst(/*opA=*/false, a, this);
|
||||||
|
@ -809,12 +805,11 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, SSurface::CombineAs type) {
|
||||||
// the surfaces in B (which is all of the intersection curves).
|
// the surfaces in B (which is all of the intersection curves).
|
||||||
a->MakeIntersectionCurvesAgainst(b, this);
|
a->MakeIntersectionCurvesAgainst(b, this);
|
||||||
|
|
||||||
SCurve *sc;
|
for(SCurve &sc : curve) {
|
||||||
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
|
SSurface *srfA = sc.GetSurfaceA(a, b),
|
||||||
SSurface *srfA = sc->GetSurfaceA(a, b),
|
*srfB = sc.GetSurfaceB(a, b);
|
||||||
*srfB = sc->GetSurfaceB(a, b);
|
|
||||||
|
|
||||||
sc->RemoveShortSegments(srfA, srfB);
|
sc.RemoveShortSegments(srfA, srfB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// And clean up the piecewise linear things we made as a calculation aid
|
// And clean up the piecewise linear things we made as a calculation aid
|
||||||
|
|
|
@ -817,7 +817,7 @@ void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the curve is exact and points are >0.05 appart wrt t, point is there
|
// if the curve is exact and points are >0.05 apart wrt t, point is there
|
||||||
// deliberately regardless of chord tolerance (ex: small circles)
|
// deliberately regardless of chord tolerance (ex: small circles)
|
||||||
tprev = t = tnext = 0;
|
tprev = t = tnext = 0;
|
||||||
if (isExact) {
|
if (isExact) {
|
||||||
|
|
|
@ -58,10 +58,9 @@ void SShell::MergeCoincidentSurfaces() {
|
||||||
|
|
||||||
// All the references to this surface get replaced with the
|
// All the references to this surface get replaced with the
|
||||||
// new srf
|
// new srf
|
||||||
SCurve *sc;
|
for(SCurve &sc : curve) {
|
||||||
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
|
if(sc.surfA == sj->h) sc.surfA = si->h;
|
||||||
if(sc->surfA == sj->h) sc->surfA = si->h;
|
if(sc.surfB == sj->h) sc.surfB = si->h;
|
||||||
if(sc->surfB == sj->h) sc->surfB = si->h;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,9 +381,8 @@ void SShell::AllPointsIntersecting(Vector a, Vector b,
|
||||||
List<SInter> *il,
|
List<SInter> *il,
|
||||||
bool asSegment, bool trimmed, bool inclTangent)
|
bool asSegment, bool trimmed, bool inclTangent)
|
||||||
{
|
{
|
||||||
SSurface *ss;
|
for(SSurface &ss : surface) {
|
||||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
ss.AllPointsIntersecting(a, b, il,
|
||||||
ss->AllPointsIntersecting(a, b, il,
|
|
||||||
asSegment, trimmed, inclTangent);
|
asSegment, trimmed, inclTangent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,11 +433,10 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
|
||||||
// First, check for edge-on-edge
|
// First, check for edge-on-edge
|
||||||
int edge_inters = 0;
|
int edge_inters = 0;
|
||||||
Vector inter_surf_n[2], inter_edge_n[2];
|
Vector inter_surf_n[2], inter_edge_n[2];
|
||||||
SSurface *srf;
|
for(SSurface &srf : surface) {
|
||||||
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
|
if(srf.LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
||||||
if(srf->LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
|
||||||
|
|
||||||
SEdgeList *sel = &(srf->edges);
|
SEdgeList *sel = &(srf.edges);
|
||||||
SEdge *se;
|
SEdge *se;
|
||||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
if((ea.Equals(se->a) && eb.Equals(se->b)) ||
|
if((ea.Equals(se->a) && eb.Equals(se->b)) ||
|
||||||
|
@ -448,9 +446,9 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
|
||||||
if(edge_inters < 2) {
|
if(edge_inters < 2) {
|
||||||
// Edge-on-edge case
|
// Edge-on-edge case
|
||||||
Point2d pm;
|
Point2d pm;
|
||||||
srf->ClosestPointTo(p, &pm, /*mustConverge=*/false);
|
srf.ClosestPointTo(p, &pm, /*mustConverge=*/false);
|
||||||
// A vector normal to the surface, at the intersection point
|
// A vector normal to the surface, at the intersection point
|
||||||
inter_surf_n[edge_inters] = srf->NormalAt(pm);
|
inter_surf_n[edge_inters] = srf.NormalAt(pm);
|
||||||
// A vector normal to the intersecting edge (but within the
|
// A vector normal to the intersecting edge (but within the
|
||||||
// intersecting surface) at the intersection point, pointing
|
// intersecting surface) at the intersection point, pointing
|
||||||
// out.
|
// out.
|
||||||
|
@ -520,25 +518,25 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
|
||||||
// are on surface) and for numerical stability, so we don't pick up
|
// are on surface) and for numerical stability, so we don't pick up
|
||||||
// the additional error from the line intersection.
|
// the additional error from the line intersection.
|
||||||
|
|
||||||
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
|
for(SSurface &srf : surface) {
|
||||||
if(srf->LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
if(srf.LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
||||||
|
|
||||||
Point2d puv;
|
Point2d puv;
|
||||||
srf->ClosestPointTo(p, &(puv.x), &(puv.y), /*mustConverge=*/false);
|
srf.ClosestPointTo(p, &(puv.x), &(puv.y), /*mustConverge=*/false);
|
||||||
Vector pp = srf->PointAt(puv);
|
Vector pp = srf.PointAt(puv);
|
||||||
|
|
||||||
if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue;
|
if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue;
|
||||||
Point2d dummy = { 0, 0 };
|
Point2d dummy = { 0, 0 };
|
||||||
SBspUv::Class c = (srf->bsp) ? srf->bsp->ClassifyPoint(puv, dummy, srf) : SBspUv::Class::OUTSIDE;
|
SBspUv::Class c = (srf.bsp) ? srf.bsp->ClassifyPoint(puv, dummy, &srf) : SBspUv::Class::OUTSIDE;
|
||||||
if(c == SBspUv::Class::OUTSIDE) continue;
|
if(c == SBspUv::Class::OUTSIDE) continue;
|
||||||
|
|
||||||
// Edge-on-face (unless edge-on-edge above superceded)
|
// Edge-on-face (unless edge-on-edge above superceded)
|
||||||
Point2d pin, pout;
|
Point2d pin, pout;
|
||||||
srf->ClosestPointTo(p.Plus(edge_n_in), &pin, /*mustConverge=*/false);
|
srf.ClosestPointTo(p.Plus(edge_n_in), &pin, /*mustConverge=*/false);
|
||||||
srf->ClosestPointTo(p.Plus(edge_n_out), &pout, /*mustConverge=*/false);
|
srf.ClosestPointTo(p.Plus(edge_n_out), &pout, /*mustConverge=*/false);
|
||||||
|
|
||||||
Vector surf_n_in = srf->NormalAt(pin),
|
Vector surf_n_in = srf.NormalAt(pin),
|
||||||
surf_n_out = srf->NormalAt(pout);
|
surf_n_out = srf.NormalAt(pout);
|
||||||
|
|
||||||
*indir = ClassifyRegion(edge_n_in, surf_n_in, surf_n);
|
*indir = ClassifyRegion(edge_n_in, surf_n_in, surf_n);
|
||||||
*outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n);
|
*outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n);
|
||||||
|
|
|
@ -1038,35 +1038,31 @@ void SShell::MakeFromTransformationOf(SShell *a,
|
||||||
{
|
{
|
||||||
booleanFailed = false;
|
booleanFailed = false;
|
||||||
surface.ReserveMore(a->surface.n);
|
surface.ReserveMore(a->surface.n);
|
||||||
SSurface *s;
|
for(SSurface &s : a->surface) {
|
||||||
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
|
|
||||||
SSurface n;
|
SSurface n;
|
||||||
n = SSurface::FromTransformationOf(s, t, q, scale, /*includingTrims=*/true);
|
n = SSurface::FromTransformationOf(&s, t, q, scale, /*includingTrims=*/true);
|
||||||
surface.Add(&n); // keeping the old ID
|
surface.Add(&n); // keeping the old ID
|
||||||
}
|
}
|
||||||
|
|
||||||
curve.ReserveMore(a->curve.n);
|
curve.ReserveMore(a->curve.n);
|
||||||
SCurve *c;
|
for(SCurve &c : a->curve) {
|
||||||
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
|
|
||||||
SCurve n;
|
SCurve n;
|
||||||
n = SCurve::FromTransformationOf(c, t, q, scale);
|
n = SCurve::FromTransformationOf(&c, t, q, scale);
|
||||||
curve.Add(&n); // keeping the old ID
|
curve.Add(&n); // keeping the old ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SShell::MakeEdgesInto(SEdgeList *sel) {
|
void SShell::MakeEdgesInto(SEdgeList *sel) {
|
||||||
SSurface *s;
|
for(SSurface &s : surface) {
|
||||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
s.MakeEdgesInto(this, sel, SSurface::MakeAs::XYZ);
|
||||||
s->MakeEdgesInto(this, sel, SSurface::MakeAs::XYZ);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SShell::MakeSectionEdgesInto(Vector n, double d, SEdgeList *sel, SBezierList *sbl)
|
void SShell::MakeSectionEdgesInto(Vector n, double d, SEdgeList *sel, SBezierList *sbl)
|
||||||
{
|
{
|
||||||
SSurface *s;
|
for(SSurface &s : surface) {
|
||||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
if(s.CoincidentWithPlane(n, d)) {
|
||||||
if(s->CoincidentWithPlane(n, d)) {
|
s.MakeSectionEdgesInto(this, sel, sbl);
|
||||||
s->MakeSectionEdgesInto(this, sel, sbl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1088,15 +1084,13 @@ bool SShell::IsEmpty() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SShell::Clear() {
|
void SShell::Clear() {
|
||||||
SSurface *s;
|
for(SSurface &s : surface) {
|
||||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
s.Clear();
|
||||||
s->Clear();
|
|
||||||
}
|
}
|
||||||
surface.Clear();
|
surface.Clear();
|
||||||
|
|
||||||
SCurve *c;
|
for(SCurve &c : curve) {
|
||||||
for(c = curve.First(); c; c = curve.NextAfter(c)) {
|
c.Clear();
|
||||||
c->Clear();
|
|
||||||
}
|
}
|
||||||
curve.Clear();
|
curve.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,20 +23,20 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
|
||||||
// Now we have to piecewise linearize the curve. If there's already an
|
// Now we have to piecewise linearize the curve. If there's already an
|
||||||
// identical curve in the shell, then follow that pwl exactly, otherwise
|
// identical curve in the shell, then follow that pwl exactly, otherwise
|
||||||
// calculate from scratch.
|
// calculate from scratch.
|
||||||
SCurve split, *existing = NULL, *se;
|
SCurve split, *existing = NULL;
|
||||||
SBezier sbrev = *sb;
|
SBezier sbrev = *sb;
|
||||||
sbrev.Reverse();
|
sbrev.Reverse();
|
||||||
bool backwards = false;
|
bool backwards = false;
|
||||||
#pragma omp critical(into)
|
#pragma omp critical(into)
|
||||||
{
|
{
|
||||||
for(se = into->curve.First(); se; se = into->curve.NextAfter(se)) {
|
for(SCurve &se : into->curve) {
|
||||||
if(se->isExact) {
|
if(se.isExact) {
|
||||||
if(sb->Equals(&(se->exact))) {
|
if(sb->Equals(&(se.exact))) {
|
||||||
existing = se;
|
existing = &se;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(sbrev.Equals(&(se->exact))) {
|
if(sbrev.Equals(&(se.exact))) {
|
||||||
existing = se;
|
existing = &se;
|
||||||
backwards = true;
|
backwards = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -332,15 +332,14 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
|
||||||
shext = agnstA;
|
shext = agnstA;
|
||||||
}
|
}
|
||||||
bool foundExact = false;
|
bool foundExact = false;
|
||||||
SCurve *sc;
|
for(SCurve &sc : shext->curve) {
|
||||||
for(sc = shext->curve.First(); sc; sc = shext->curve.NextAfter(sc)) {
|
if(sc.source == SCurve::Source::INTERSECTION) continue;
|
||||||
if(sc->source == SCurve::Source::INTERSECTION) continue;
|
if(!sc.isExact) continue;
|
||||||
if(!sc->isExact) continue;
|
if((sc.surfA != sext->h) && (sc.surfB != sext->h)) continue;
|
||||||
if((sc->surfA != sext->h) && (sc->surfB != sext->h)) continue;
|
|
||||||
// we have a curve belonging to the curved surface and not the plane.
|
// we have a curve belonging to the curved surface and not the plane.
|
||||||
// does it lie completely in the plane?
|
// does it lie completely in the plane?
|
||||||
if(splane->ContainsPlaneCurve(sc)) {
|
if(splane->ContainsPlaneCurve(&sc)) {
|
||||||
SBezier bezier = sc->exact;
|
SBezier bezier = sc.exact;
|
||||||
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
|
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
|
||||||
foundExact = true;
|
foundExact = true;
|
||||||
}
|
}
|
||||||
|
@ -571,10 +570,9 @@ bool SSurface::ContainsPlaneCurve(SCurve *sc) const {
|
||||||
void SShell::MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
|
void SShell::MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
|
||||||
SEdgeList *el, SShell *useCurvesFrom)
|
SEdgeList *el, SShell *useCurvesFrom)
|
||||||
{
|
{
|
||||||
SSurface *ss;
|
for(SSurface &ss : surface) {
|
||||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
if(proto->CoincidentWith(&ss, sameNormal)) {
|
||||||
if(proto->CoincidentWith(ss, sameNormal)) {
|
ss.MakeEdgesInto(this, el, SSurface::MakeAs::XYZ, useCurvesFrom);
|
||||||
ss->MakeEdgesInto(this, el, SSurface::MakeAs::XYZ, useCurvesFrom);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -426,7 +426,7 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
|
||||||
if (i == l.n-1) {
|
if (i == l.n-1) {
|
||||||
end = true;
|
end = true;
|
||||||
}
|
}
|
||||||
if (end) { // triangulate the fan and tag the verticies
|
if (end) { // triangulate the fan and tag the vertices
|
||||||
if (j > 3) {
|
if (j > 3) {
|
||||||
Vector center = l[pstart+1].p.Plus(l[pstart+j-1].p).ScaledBy(0.5);
|
Vector center = l[pstart+1].p.Plus(l[pstart+j-1].p).ScaledBy(0.5);
|
||||||
for (int x=0; x<j; x++) {
|
for (int x=0; x<j; x++) {
|
||||||
|
@ -597,11 +597,11 @@ void SPolygon::UvGridTriangulateInto(SMesh *mesh, SSurface *srf) {
|
||||||
srf->MakeTriangulationGridInto(&lj, 0, 1, /*swapped=*/false, 0);
|
srf->MakeTriangulationGridInto(&lj, 0, 1, /*swapped=*/false, 0);
|
||||||
|
|
||||||
// force 2nd order grid to have at least 4 segments in each direction
|
// force 2nd order grid to have at least 4 segments in each direction
|
||||||
if ((li.n < 5) && (srf->degm>1)) { // 4 segments minimun
|
if ((li.n < 5) && (srf->degm>1)) { // 4 segments minimum
|
||||||
li.Clear();
|
li.Clear();
|
||||||
li.Add(&v[0]);li.Add(&v[1]);li.Add(&v[2]);li.Add(&v[3]);li.Add(&v[4]);
|
li.Add(&v[0]);li.Add(&v[1]);li.Add(&v[2]);li.Add(&v[3]);li.Add(&v[4]);
|
||||||
}
|
}
|
||||||
if ((lj.n < 5) && (srf->degn>1)) { // 4 segments minimun
|
if ((lj.n < 5) && (srf->degn>1)) { // 4 segments minimum
|
||||||
lj.Clear();
|
lj.Clear();
|
||||||
lj.Add(&v[0]);lj.Add(&v[1]);lj.Add(&v[2]);lj.Add(&v[3]);lj.Add(&v[4]);
|
lj.Add(&v[0]);lj.Add(&v[1]);lj.Add(&v[2]);lj.Add(&v[3]);lj.Add(&v[4]);
|
||||||
}
|
}
|
||||||
|
@ -681,7 +681,7 @@ void SPolygon::UvGridTriangulateInto(SMesh *mesh, SSurface *srf) {
|
||||||
if (!bottom[j]) // add our own bottom edge
|
if (!bottom[j]) // add our own bottom edge
|
||||||
holes.AddEdge(a, b);
|
holes.AddEdge(a, b);
|
||||||
} else {
|
} else {
|
||||||
if (prev_flag) // add our left neighbots right edge
|
if (prev_flag) // add our left neighbors right edge
|
||||||
holes.AddEdge(a, d);
|
holes.AddEdge(a, d);
|
||||||
if (bottom[j]) // add our bottom neighbors top edge
|
if (bottom[j]) // add our bottom neighbors top edge
|
||||||
holes.AddEdge(b, a);
|
holes.AddEdge(b, a);
|
||||||
|
|
114
src/style.cpp
|
@ -8,22 +8,22 @@
|
||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
|
||||||
const Style::Default Style::Defaults[] = {
|
const Style::Default Style::Defaults[] = {
|
||||||
{ { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4, true },
|
{ { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4, true, StipplePattern::CONTINUOUS },
|
||||||
{ { CONSTRUCTION }, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, 0, false },
|
{ { CONSTRUCTION }, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, 0, false, StipplePattern::CONTINUOUS },
|
||||||
{ { INACTIVE_GRP }, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, 3, true },
|
{ { INACTIVE_GRP }, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, 3, true, StipplePattern::CONTINUOUS },
|
||||||
{ { DATUM }, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, 0, true },
|
{ { DATUM }, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { SOLID_EDGE }, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, 2, true },
|
{ { SOLID_EDGE }, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, 2, true, StipplePattern::CONTINUOUS },
|
||||||
{ { CONSTRAINT }, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, 0, true },
|
{ { CONSTRAINT }, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { SELECTED }, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, 0, true },
|
{ { SELECTED }, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0, true },
|
{ { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0, true },
|
{ { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0, true },
|
{ { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0, true },
|
{ { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0, true },
|
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0, true },
|
{ { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0, true, StipplePattern::CONTINUOUS },
|
||||||
{ { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1, true },
|
{ { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1, true, StipplePattern::DASH },
|
||||||
{ { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5, true },
|
{ { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5, true, StipplePattern::CONTINUOUS },
|
||||||
{ { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0, true }
|
{ { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0, true, StipplePattern::CONTINUOUS }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string Style::CnfColor(const std::string &prefix) {
|
std::string Style::CnfColor(const std::string &prefix) {
|
||||||
|
@ -32,6 +32,12 @@ std::string Style::CnfColor(const std::string &prefix) {
|
||||||
std::string Style::CnfWidth(const std::string &prefix) {
|
std::string Style::CnfWidth(const std::string &prefix) {
|
||||||
return "Style_" + prefix + "_Width";
|
return "Style_" + prefix + "_Width";
|
||||||
}
|
}
|
||||||
|
std::string Style::CnfStippleType(const std::string &prefix) {
|
||||||
|
return "Style_" + prefix + "_StippleType";
|
||||||
|
}
|
||||||
|
std::string Style::CnfStippleScale(const std::string &prefix) {
|
||||||
|
return "Style_" + prefix + "_StippleScale";
|
||||||
|
}
|
||||||
std::string Style::CnfTextHeight(const std::string &prefix) {
|
std::string Style::CnfTextHeight(const std::string &prefix) {
|
||||||
return "Style_" + prefix + "_TextHeight";
|
return "Style_" + prefix + "_TextHeight";
|
||||||
}
|
}
|
||||||
|
@ -105,9 +111,14 @@ void Style::FillDefaultStyle(Style *s, const Default *d, bool factory) {
|
||||||
: settings->ThawBool(CnfExportable(d->cnfPrefix), d->exportable);
|
: settings->ThawBool(CnfExportable(d->cnfPrefix), d->exportable);
|
||||||
s->filled = false;
|
s->filled = false;
|
||||||
s->fillColor = RGBf(0.3, 0.3, 0.3);
|
s->fillColor = RGBf(0.3, 0.3, 0.3);
|
||||||
s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? StipplePattern::DASH
|
s->stippleType = (factory)
|
||||||
: StipplePattern::CONTINUOUS;
|
? d->stippleType
|
||||||
s->stippleScale = 15.0;
|
: Style::StipplePatternFromString(
|
||||||
|
settings->ThawString(CnfStippleType(d->cnfPrefix),
|
||||||
|
StipplePatternName(d->stippleType)));
|
||||||
|
s->stippleScale = (factory)
|
||||||
|
? 15.0
|
||||||
|
: settings->ThawFloat(CnfStippleScale(d->cnfPrefix), 15.0);
|
||||||
s->zIndex = d->zIndex;
|
s->zIndex = d->zIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +136,8 @@ void Style::FreezeDefaultStyles(Platform::SettingsRef settings) {
|
||||||
for(d = &(Defaults[0]); d->h.v; d++) {
|
for(d = &(Defaults[0]); d->h.v; d++) {
|
||||||
settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h));
|
settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h));
|
||||||
settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h));
|
settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h));
|
||||||
|
settings->FreezeString(CnfStippleType(d->cnfPrefix), StipplePatternName(d->h));
|
||||||
|
settings->FreezeFloat(CnfStippleScale(d->cnfPrefix), (float)StippleScale(d->h));
|
||||||
settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h));
|
settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h));
|
||||||
settings->FreezeBool(CnfExportable(d->cnfPrefix), Exportable(d->h.v));
|
settings->FreezeBool(CnfExportable(d->cnfPrefix), Exportable(d->h.v));
|
||||||
}
|
}
|
||||||
|
@ -353,11 +366,62 @@ hStyle Style::ForEntity(hEntity he) {
|
||||||
return hs;
|
return hs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StipplePattern Style::StipplePatternFromString(std::string name) {
|
||||||
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||||
|
if(name == "continuous") {
|
||||||
|
return StipplePattern::CONTINUOUS;
|
||||||
|
} else if(name == "shortdash") {
|
||||||
|
return StipplePattern::SHORT_DASH;
|
||||||
|
} else if(name == "dash") {
|
||||||
|
return StipplePattern::DASH;
|
||||||
|
} else if(name == "longdash") {
|
||||||
|
return StipplePattern::LONG_DASH;
|
||||||
|
} else if(name == "dashdot") {
|
||||||
|
return StipplePattern::DASH_DOT;
|
||||||
|
} else if(name == "dashdotdot") {
|
||||||
|
return StipplePattern::DASH_DOT_DOT;
|
||||||
|
} else if(name == "dot") {
|
||||||
|
return StipplePattern::DOT;
|
||||||
|
} else if(name == "freehand") {
|
||||||
|
return StipplePattern::FREEHAND;
|
||||||
|
} else if(name == "zigzag") {
|
||||||
|
return StipplePattern::ZIGZAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StipplePattern::CONTINUOUS;
|
||||||
|
}
|
||||||
|
|
||||||
StipplePattern Style::PatternType(hStyle hs) {
|
StipplePattern Style::PatternType(hStyle hs) {
|
||||||
Style *s = Get(hs);
|
Style *s = Get(hs);
|
||||||
return s->stippleType;
|
return s->stippleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Style::StipplePatternName(hStyle hs) {
|
||||||
|
Style *s = Get(hs);
|
||||||
|
return StipplePatternName(s->stippleType);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Style::StipplePatternName(StipplePattern stippleType) {
|
||||||
|
switch(stippleType) {
|
||||||
|
case StipplePattern::CONTINUOUS: return "Continuous";
|
||||||
|
case StipplePattern::SHORT_DASH: return "ShortDash";
|
||||||
|
case StipplePattern::DASH: return "Dash";
|
||||||
|
case StipplePattern::LONG_DASH: return "LongDash";
|
||||||
|
case StipplePattern::DASH_DOT: return "DashDot";
|
||||||
|
case StipplePattern::DASH_DOT_DOT: return "DashDotDot";
|
||||||
|
case StipplePattern::DOT: return "Dot";
|
||||||
|
case StipplePattern::FREEHAND: return "FreeHand";
|
||||||
|
case StipplePattern::ZIGZAG: return "ZigZag";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Continuous";
|
||||||
|
}
|
||||||
|
|
||||||
|
double Style::StippleScale(hStyle hs) {
|
||||||
|
Style *s = Get(hs);
|
||||||
|
return s->stippleScale;
|
||||||
|
}
|
||||||
|
|
||||||
double Style::StippleScaleMm(hStyle hs) {
|
double Style::StippleScaleMm(hStyle hs) {
|
||||||
Style *s = Get(hs);
|
Style *s = Get(hs);
|
||||||
if(s->widthAs == UnitsAs::MM) {
|
if(s->widthAs == UnitsAs::MM) {
|
||||||
|
@ -389,6 +453,7 @@ void TextWindow::ScreenShowStyleInfo(int link, uint32_t v) {
|
||||||
void TextWindow::ScreenLoadFactoryDefaultStyles(int link, uint32_t v) {
|
void TextWindow::ScreenLoadFactoryDefaultStyles(int link, uint32_t v) {
|
||||||
Style::LoadFactoryDefaults();
|
Style::LoadFactoryDefaults();
|
||||||
SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
|
SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
|
||||||
|
SS.GW.persistentDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::ScreenCreateCustomStyle(int link, uint32_t v) {
|
void TextWindow::ScreenCreateCustomStyle(int link, uint32_t v) {
|
||||||
|
@ -405,14 +470,13 @@ void TextWindow::ShowListOfStyles() {
|
||||||
Printf(true, "%Ft color style-name");
|
Printf(true, "%Ft color style-name");
|
||||||
|
|
||||||
bool darkbg = false;
|
bool darkbg = false;
|
||||||
Style *s;
|
for(Style &s : SK.style) {
|
||||||
for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) {
|
|
||||||
Printf(false, "%Bp %Bz %Bp %Fl%Ll%f%D%s%E",
|
Printf(false, "%Bp %Bz %Bp %Fl%Ll%f%D%s%E",
|
||||||
darkbg ? 'd' : 'a',
|
darkbg ? 'd' : 'a',
|
||||||
&s->color,
|
&s.color,
|
||||||
darkbg ? 'd' : 'a',
|
darkbg ? 'd' : 'a',
|
||||||
ScreenShowStyleInfo, s->h.v,
|
ScreenShowStyleInfo, s.h.v,
|
||||||
s->DescriptionString().c_str());
|
s.DescriptionString().c_str());
|
||||||
|
|
||||||
darkbg = !darkbg;
|
darkbg = !darkbg;
|
||||||
}
|
}
|
||||||
|
@ -499,7 +563,7 @@ void TextWindow::ScreenChangeStyleMetric(int link, uint32_t v) {
|
||||||
if(units == Style::UnitsAs::PIXELS) {
|
if(units == Style::UnitsAs::PIXELS) {
|
||||||
edit_value = ssprintf("%.2f", val);
|
edit_value = ssprintf("%.2f", val);
|
||||||
} else {
|
} else {
|
||||||
edit_value = SS.MmToString(val);
|
edit_value = SS.MmToString(val, true);
|
||||||
}
|
}
|
||||||
SS.TW.ShowEditControl(col, edit_value);
|
SS.TW.ShowEditControl(col, edit_value);
|
||||||
SS.TW.edit.style = hs;
|
SS.TW.edit.style = hs;
|
||||||
|
|
|
@ -123,13 +123,18 @@ void TextWindow::ShowListOfGroups() {
|
||||||
sprintf(sdof, "%-3d", dof);
|
sprintf(sdof, "%-3d", dof);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::string suffix;
|
||||||
|
if(g->forceToMesh || g->IsTriangleMeshAssembly()) {
|
||||||
|
suffix = " (∆)";
|
||||||
|
}
|
||||||
|
|
||||||
bool ref = (g->h == Group::HGROUP_REFERENCES);
|
bool ref = (g->h == Group::HGROUP_REFERENCES);
|
||||||
Printf(false,
|
Printf(false,
|
||||||
"%Bp%Fd "
|
"%Bp%Fd "
|
||||||
"%Ft%s%Fb%D%f%Ll%s%E "
|
"%Ft%s%Fb%D%f%Ll%s%E "
|
||||||
"%Fb%s%D%f%Ll%s%E "
|
"%Fb%s%D%f%Ll%s%E "
|
||||||
"%Fp%D%f%s%Ll%s%E "
|
"%Fp%D%f%s%Ll%s%E "
|
||||||
"%Fl%Ll%D%f%s",
|
"%Fp%Ll%D%f%s%E%s",
|
||||||
// Alternate between light and dark backgrounds, for readability
|
// Alternate between light and dark backgrounds, for readability
|
||||||
backgroundParity ? 'd' : 'a',
|
backgroundParity ? 'd' : 'a',
|
||||||
// Link that activates the group
|
// Link that activates the group
|
||||||
|
@ -146,7 +151,9 @@ void TextWindow::ShowListOfGroups() {
|
||||||
ok ? ((warn && SS.checkClosedContour) ? "err" : sdof) : "",
|
ok ? ((warn && SS.checkClosedContour) ? "err" : sdof) : "",
|
||||||
ok ? "" : "ERR",
|
ok ? "" : "ERR",
|
||||||
// Link to a screen that gives more details on the group
|
// Link to a screen that gives more details on the group
|
||||||
g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str());
|
g->suppress ? 'g' : 'l',
|
||||||
|
g->h.v, (&TextWindow::ScreenSelectGroup), s.c_str(),
|
||||||
|
suffix.c_str());
|
||||||
|
|
||||||
if(active) afterActive = true;
|
if(active) afterActive = true;
|
||||||
backgroundParity = !backgroundParity;
|
backgroundParity = !backgroundParity;
|
||||||
|
@ -299,6 +306,23 @@ void TextWindow::ScreenChangeGroupScale(int link, uint32_t v) {
|
||||||
SS.TW.edit.meaning = Edit::GROUP_SCALE;
|
SS.TW.edit.meaning = Edit::GROUP_SCALE;
|
||||||
SS.TW.edit.group.v = v;
|
SS.TW.edit.group.v = v;
|
||||||
}
|
}
|
||||||
|
void TextWindow::ScreenChangeHelixPitch(int link, uint32_t v) {
|
||||||
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
||||||
|
double pitch = g->valB/SS.MmPerUnit();
|
||||||
|
SS.TW.ShowEditControl(3, ssprintf("%.8f", pitch));
|
||||||
|
SS.TW.edit.meaning = Edit::HELIX_PITCH;
|
||||||
|
SS.TW.edit.group.v = v;
|
||||||
|
}
|
||||||
|
void TextWindow::ScreenChangePitchOption(int link, uint32_t v) {
|
||||||
|
Group *g = SK.GetGroup(SS.TW.shown.group);
|
||||||
|
if(g->valB == 0.0) {
|
||||||
|
g->valB = SK.GetParam(g->h.param(7))->val * PI /
|
||||||
|
(SK.GetParam(g->h.param(3))->val);
|
||||||
|
} else {
|
||||||
|
g->valB = 0.0;
|
||||||
|
}
|
||||||
|
SS.GW.Invalidate();
|
||||||
|
}
|
||||||
void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
|
void TextWindow::ScreenDeleteGroup(int link, uint32_t v) {
|
||||||
SS.UndoRemember();
|
SS.UndoRemember();
|
||||||
|
|
||||||
|
@ -398,6 +422,26 @@ void TextWindow::ShowGroupInfo() {
|
||||||
}
|
}
|
||||||
Printf(false, "");
|
Printf(false, "");
|
||||||
|
|
||||||
|
if(g->type == Group::Type::HELIX) {
|
||||||
|
Printf(false, "%Ft pitch - length per turn%E");
|
||||||
|
|
||||||
|
if (fabs(g->valB) != 0.0) {
|
||||||
|
Printf(false, " %Ba %# %Fl%Ll%f%D[change]%E",
|
||||||
|
g->valB / SS.MmPerUnit(),
|
||||||
|
&TextWindow::ScreenChangeHelixPitch, g->h.v);
|
||||||
|
} else {
|
||||||
|
Printf(false, " %Ba %# %E",
|
||||||
|
SK.GetParam(g->h.param(7))->val * PI /
|
||||||
|
( (SK.GetParam(g->h.param(3))->val) * SS.MmPerUnit() ),
|
||||||
|
&TextWindow::ScreenChangeHelixPitch, g->h.v);
|
||||||
|
}
|
||||||
|
Printf(false, " %Fd%f%LP%s fixed",
|
||||||
|
&TextWindow::ScreenChangePitchOption,
|
||||||
|
g->valB != 0 ? CHECK_TRUE : CHECK_FALSE);
|
||||||
|
|
||||||
|
Printf(false, ""); // blank line
|
||||||
|
}
|
||||||
|
|
||||||
if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
|
if(g->type == Group::Type::EXTRUDE || g->type == Group::Type::LATHE ||
|
||||||
g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
|
g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
|
||||||
g->type == Group::Type::HELIX) {
|
g->type == Group::Type::HELIX) {
|
||||||
|
@ -451,7 +495,7 @@ void TextWindow::ShowGroupInfo() {
|
||||||
&TextWindow::ScreenChangeGroupOption,
|
&TextWindow::ScreenChangeGroupOption,
|
||||||
g->visible ? CHECK_TRUE : CHECK_FALSE);
|
g->visible ? CHECK_TRUE : CHECK_FALSE);
|
||||||
|
|
||||||
if(!g->IsForcedToMeshBySource()) {
|
if(!g->IsForcedToMeshBySource() && !g->IsTriangleMeshAssembly()) {
|
||||||
Printf(false, " %f%Lf%Fd%s force NURBS surfaces to triangle mesh",
|
Printf(false, " %f%Lf%Fd%s force NURBS surfaces to triangle mesh",
|
||||||
&TextWindow::ScreenChangeGroupOption,
|
&TextWindow::ScreenChangeGroupOption,
|
||||||
g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
|
g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
|
||||||
|
@ -579,7 +623,7 @@ void TextWindow::ShowGroupSolveInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(g->solved.timeout) {
|
if(g->solved.timeout) {
|
||||||
Printf(true, "%FxSome items in list have been ommitted%Fd");
|
Printf(true, "%FxSome items in list have been omitted%Fd");
|
||||||
Printf(false, "%Fxbecause the operation timed out.%Fd");
|
Printf(false, "%Fxbecause the operation timed out.%Fd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,7 +647,7 @@ void TextWindow::ScreenStepDimFinish(int link, uint32_t v) {
|
||||||
SS.TW.edit.meaning = Edit::STEP_DIM_FINISH;
|
SS.TW.edit.meaning = Edit::STEP_DIM_FINISH;
|
||||||
std::string edit_value;
|
std::string edit_value;
|
||||||
if(SS.TW.stepDim.isDistance) {
|
if(SS.TW.stepDim.isDistance) {
|
||||||
edit_value = SS.MmToString(SS.TW.stepDim.finish);
|
edit_value = SS.MmToString(SS.TW.stepDim.finish, true);
|
||||||
} else {
|
} else {
|
||||||
edit_value = ssprintf("%.3f", SS.TW.stepDim.finish);
|
edit_value = ssprintf("%.3f", SS.TW.stepDim.finish);
|
||||||
}
|
}
|
||||||
|
@ -690,7 +734,7 @@ void TextWindow::ScreenChangeTangentArc(int link, uint32_t v) {
|
||||||
switch(link) {
|
switch(link) {
|
||||||
case 'r': {
|
case 'r': {
|
||||||
SS.TW.edit.meaning = Edit::TANGENT_ARC_RADIUS;
|
SS.TW.edit.meaning = Edit::TANGENT_ARC_RADIUS;
|
||||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.tangentArcRadius));
|
SS.TW.ShowEditControl(3, SS.MmToString(SS.tangentArcRadius, true));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,6 +833,15 @@ void TextWindow::EditControlDone(std::string s) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Edit::HELIX_PITCH: // stored in valB
|
||||||
|
if(Expr *e = Expr::From(s, /*popUpError=*/true)) {
|
||||||
|
double ev = e->Eval();
|
||||||
|
Group *g = SK.GetGroup(edit.group);
|
||||||
|
g->valB = ev * SS.MmPerUnit();
|
||||||
|
SS.MarkGroupDirty(g->h);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Edit::GROUP_COLOR: {
|
case Edit::GROUP_COLOR: {
|
||||||
Vector rgb;
|
Vector rgb;
|
||||||
if(sscanf(s.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
|
if(sscanf(s.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
|
||||||
|
|
|
@ -203,7 +203,7 @@ const TextWindow::Color TextWindow::fgColors[] = {
|
||||||
{ 'r', RGBi( 0, 0, 0) }, // Reverse : black
|
{ 'r', RGBi( 0, 0, 0) }, // Reverse : black
|
||||||
{ 'x', RGBi(255, 20, 20) }, // Error : red
|
{ 'x', RGBi(255, 20, 20) }, // Error : red
|
||||||
{ 'i', RGBi( 0, 255, 255) }, // Info : cyan
|
{ 'i', RGBi( 0, 255, 255) }, // Info : cyan
|
||||||
{ 'g', RGBi(160, 160, 160) },
|
{ 'g', RGBi(128, 128, 128) }, // Disabled : gray
|
||||||
{ 'b', RGBi(200, 200, 200) },
|
{ 'b', RGBi(200, 200, 200) },
|
||||||
{ 0, RGBi( 0, 0, 0) }
|
{ 0, RGBi( 0, 0, 0) }
|
||||||
};
|
};
|
||||||
|
@ -348,8 +348,8 @@ void TextWindow::ClearScreen() {
|
||||||
rows = 0;
|
rows = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This message was addded when someone had too many fonts for the text window
|
// This message was added when someone had too many fonts for the text window
|
||||||
// Scrolling seemed to be broken, but was actaully at the MAX_ROWS.
|
// Scrolling seemed to be broken, but was actually at the MAX_ROWS.
|
||||||
static const char* endString = " **** End of Text Screen ****";
|
static const char* endString = " **** End of Text Screen ****";
|
||||||
|
|
||||||
void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
|
void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
|
||||||
|
|
|
@ -153,11 +153,18 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
|
||||||
double width, height;
|
double width, height;
|
||||||
window->GetContentSize(&width, &height);
|
window->GetContentSize(&width, &height);
|
||||||
|
|
||||||
int x = 17, y = (int)(height - 52);
|
int x = 17, y = (int)(height - 21); // 20 is the menu bar height
|
||||||
|
|
||||||
// When changing these values, also change the asReference drawing code in drawentity.cpp.
|
// When changing these values, also change the asReference drawing code in drawentity.cpp
|
||||||
|
// as well as the "window->SetMinContentSize(720, 636);" in graphicswin.cpp
|
||||||
int fudge = 8;
|
int fudge = 8;
|
||||||
int h = 32*18 + 3*16 + fudge;
|
int h = 32*18 + 3*16 + fudge; // Toolbar height = 18 icons * 32 pixels + 3 dividers * 16 pixels + fudge
|
||||||
|
|
||||||
|
if(h < y) {
|
||||||
|
// If there is enough vertical space leave up to 32 pixels between the menu bar and the toolbar.
|
||||||
|
y -= ((y - h) < 32) ? y - h : 32;
|
||||||
|
}
|
||||||
|
|
||||||
int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h;
|
int aleft = 0, aright = 66, atop = y+16+fudge/2, abot = y+16-h;
|
||||||
|
|
||||||
bool withinToolbar =
|
bool withinToolbar =
|
||||||
|
|
27
src/ui.h
|
@ -82,6 +82,7 @@ enum class Command : uint32_t {
|
||||||
SHOW_GRID,
|
SHOW_GRID,
|
||||||
DIM_SOLID_MODEL,
|
DIM_SOLID_MODEL,
|
||||||
PERSPECTIVE_PROJ,
|
PERSPECTIVE_PROJ,
|
||||||
|
EXPLODE_SKETCH,
|
||||||
ONTO_WORKPLANE,
|
ONTO_WORKPLANE,
|
||||||
NEAREST_ORTHO,
|
NEAREST_ORTHO,
|
||||||
NEAREST_ISO,
|
NEAREST_ISO,
|
||||||
|
@ -89,6 +90,7 @@ enum class Command : uint32_t {
|
||||||
SHOW_TOOLBAR,
|
SHOW_TOOLBAR,
|
||||||
SHOW_TEXT_WND,
|
SHOW_TEXT_WND,
|
||||||
UNITS_INCHES,
|
UNITS_INCHES,
|
||||||
|
UNITS_FEET_INCHES,
|
||||||
UNITS_MM,
|
UNITS_MM,
|
||||||
UNITS_METERS,
|
UNITS_METERS,
|
||||||
FULL_SCREEN,
|
FULL_SCREEN,
|
||||||
|
@ -312,12 +314,14 @@ public:
|
||||||
EXPORT_OFFSET = 110,
|
EXPORT_OFFSET = 110,
|
||||||
CANVAS_SIZE = 111,
|
CANVAS_SIZE = 111,
|
||||||
G_CODE_DEPTH = 112,
|
G_CODE_DEPTH = 112,
|
||||||
G_CODE_PASSES = 113,
|
G_CODE_SAFE_HEIGHT = 113,
|
||||||
G_CODE_FEED = 114,
|
G_CODE_PASSES = 114,
|
||||||
G_CODE_PLUNGE_FEED = 115,
|
G_CODE_FEED = 115,
|
||||||
AUTOSAVE_INTERVAL = 116,
|
G_CODE_PLUNGE_FEED = 116,
|
||||||
LIGHT_AMBIENT = 117,
|
AUTOSAVE_INTERVAL = 117,
|
||||||
FIND_CONSTRAINT_TIMEOUT = 118,
|
LIGHT_AMBIENT = 118,
|
||||||
|
FIND_CONSTRAINT_TIMEOUT = 119,
|
||||||
|
EXPLODE_DISTANCE = 120,
|
||||||
// For TTF text
|
// For TTF text
|
||||||
TTF_TEXT = 300,
|
TTF_TEXT = 300,
|
||||||
// For the step dimension screen
|
// For the step dimension screen
|
||||||
|
@ -342,7 +346,9 @@ public:
|
||||||
VIEW_PROJ_RIGHT = 702,
|
VIEW_PROJ_RIGHT = 702,
|
||||||
VIEW_PROJ_UP = 703,
|
VIEW_PROJ_UP = 703,
|
||||||
// For tangent arc
|
// For tangent arc
|
||||||
TANGENT_ARC_RADIUS = 800
|
TANGENT_ARC_RADIUS = 800,
|
||||||
|
// For helix pitch
|
||||||
|
HELIX_PITCH = 802
|
||||||
};
|
};
|
||||||
struct {
|
struct {
|
||||||
bool showAgain;
|
bool showAgain;
|
||||||
|
@ -473,6 +479,8 @@ public:
|
||||||
static void ScreenChangeExprA(int link, uint32_t v);
|
static void ScreenChangeExprA(int link, uint32_t v);
|
||||||
static void ScreenChangeGroupName(int link, uint32_t v);
|
static void ScreenChangeGroupName(int link, uint32_t v);
|
||||||
static void ScreenChangeGroupScale(int link, uint32_t v);
|
static void ScreenChangeGroupScale(int link, uint32_t v);
|
||||||
|
static void ScreenChangeHelixPitch(int link, uint32_t v);
|
||||||
|
static void ScreenChangePitchOption(int link, uint32_t v);
|
||||||
static void ScreenChangeLightDirection(int link, uint32_t v);
|
static void ScreenChangeLightDirection(int link, uint32_t v);
|
||||||
static void ScreenChangeLightIntensity(int link, uint32_t v);
|
static void ScreenChangeLightIntensity(int link, uint32_t v);
|
||||||
static void ScreenChangeLightAmbient(int link, uint32_t v);
|
static void ScreenChangeLightAmbient(int link, uint32_t v);
|
||||||
|
@ -483,6 +491,7 @@ public:
|
||||||
static void ScreenChangeExportMaxSegments(int link, uint32_t v);
|
static void ScreenChangeExportMaxSegments(int link, uint32_t v);
|
||||||
static void ScreenChangeCameraTangent(int link, uint32_t v);
|
static void ScreenChangeCameraTangent(int link, uint32_t v);
|
||||||
static void ScreenChangeGridSpacing(int link, uint32_t v);
|
static void ScreenChangeGridSpacing(int link, uint32_t v);
|
||||||
|
static void ScreenChangeExplodeDistance(int link, uint32_t v);
|
||||||
static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v);
|
static void ScreenChangeDigitsAfterDecimal(int link, uint32_t v);
|
||||||
static void ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v);
|
static void ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v);
|
||||||
static void ScreenChangeUseSIPrefixes(int link, uint32_t v);
|
static void ScreenChangeUseSIPrefixes(int link, uint32_t v);
|
||||||
|
@ -535,6 +544,7 @@ public:
|
||||||
Platform::MenuItemRef showGridMenuItem;
|
Platform::MenuItemRef showGridMenuItem;
|
||||||
Platform::MenuItemRef dimSolidModelMenuItem;
|
Platform::MenuItemRef dimSolidModelMenuItem;
|
||||||
Platform::MenuItemRef perspectiveProjMenuItem;
|
Platform::MenuItemRef perspectiveProjMenuItem;
|
||||||
|
Platform::MenuItemRef explodeMenuItem;
|
||||||
Platform::MenuItemRef showToolbarMenuItem;
|
Platform::MenuItemRef showToolbarMenuItem;
|
||||||
Platform::MenuItemRef showTextWndMenuItem;
|
Platform::MenuItemRef showTextWndMenuItem;
|
||||||
Platform::MenuItemRef fullScreenMenuItem;
|
Platform::MenuItemRef fullScreenMenuItem;
|
||||||
|
@ -542,6 +552,7 @@ public:
|
||||||
Platform::MenuItemRef unitsMmMenuItem;
|
Platform::MenuItemRef unitsMmMenuItem;
|
||||||
Platform::MenuItemRef unitsMetersMenuItem;
|
Platform::MenuItemRef unitsMetersMenuItem;
|
||||||
Platform::MenuItemRef unitsInchesMenuItem;
|
Platform::MenuItemRef unitsInchesMenuItem;
|
||||||
|
Platform::MenuItemRef unitsFeetInchesMenuItem;
|
||||||
|
|
||||||
Platform::MenuItemRef inWorkplaneMenuItem;
|
Platform::MenuItemRef inWorkplaneMenuItem;
|
||||||
Platform::MenuItemRef in3dMenuItem;
|
Platform::MenuItemRef in3dMenuItem;
|
||||||
|
@ -798,6 +809,8 @@ public:
|
||||||
bool showEdges;
|
bool showEdges;
|
||||||
bool showOutlines;
|
bool showOutlines;
|
||||||
bool showFaces;
|
bool showFaces;
|
||||||
|
bool showFacesDrawing;
|
||||||
|
bool showFacesNonDrawing;
|
||||||
bool showMesh;
|
bool showMesh;
|
||||||
void ToggleBool(bool *v);
|
void ToggleBool(bool *v);
|
||||||
|
|
||||||
|
|
59
src/view.cpp
|
@ -35,8 +35,29 @@ void TextWindow::ShowEditView() {
|
||||||
Printf(false, "%Ba %Ftout%E (%3, %3, %3)", CO(n));
|
Printf(false, "%Ba %Ftout%E (%3, %3, %3)", CO(n));
|
||||||
Printf(false, "");
|
Printf(false, "");
|
||||||
|
|
||||||
Printf(false, "The perspective may be changed in the");
|
Printf(false, "%Ft perspective factor (0 for parallel)%E");
|
||||||
Printf(false, "configuration screen.");
|
Printf(false, "%Ba %# %Fl%Ll%f%D[change]%E",
|
||||||
|
SS.cameraTangent*1000,
|
||||||
|
&ScreenChangeCameraTangent, 0);
|
||||||
|
|
||||||
|
Printf(false, "");
|
||||||
|
Printf(false, "%Ft light direction intensity");
|
||||||
|
for(int i = 0; i < 2; i++) {
|
||||||
|
Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E "
|
||||||
|
"%2 %Fl%D%f%Ll[c]%E",
|
||||||
|
(i & 1) ? 'd' : 'a', i,
|
||||||
|
CO(SS.lightDir[i]), i, &ScreenChangeLightDirection,
|
||||||
|
SS.lightIntensity[i], i, &ScreenChangeLightIntensity);
|
||||||
|
}
|
||||||
|
Printf(false, "%Ba ambient lighting %2 %Fl%f%Ll[c]%E",
|
||||||
|
SS.ambientIntensity, &ScreenChangeLightAmbient);
|
||||||
|
|
||||||
|
Printf(false, "");
|
||||||
|
Printf(false, "%Ft explode distance%E");
|
||||||
|
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
||||||
|
SS.MmToString(SS.explodeDistance).c_str(),
|
||||||
|
&ScreenChangeExplodeDistance, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::ScreenChangeViewScale(int link, uint32_t v) {
|
void TextWindow::ScreenChangeViewScale(int link, uint32_t v) {
|
||||||
|
@ -51,9 +72,9 @@ void TextWindow::ScreenChangeViewToFullScale(int link, uint32_t v) {
|
||||||
void TextWindow::ScreenChangeViewOrigin(int link, uint32_t v) {
|
void TextWindow::ScreenChangeViewOrigin(int link, uint32_t v) {
|
||||||
std::string edit_value =
|
std::string edit_value =
|
||||||
ssprintf("%s, %s, %s",
|
ssprintf("%s, %s, %s",
|
||||||
SS.MmToString(-SS.GW.offset.x).c_str(),
|
SS.MmToString(-SS.GW.offset.x, true).c_str(),
|
||||||
SS.MmToString(-SS.GW.offset.y).c_str(),
|
SS.MmToString(-SS.GW.offset.y, true).c_str(),
|
||||||
SS.MmToString(-SS.GW.offset.z).c_str());
|
SS.MmToString(-SS.GW.offset.z, true).c_str());
|
||||||
|
|
||||||
SS.TW.edit.meaning = Edit::VIEW_ORIGIN;
|
SS.TW.edit.meaning = Edit::VIEW_ORIGIN;
|
||||||
SS.TW.ShowEditControl(3, edit_value);
|
SS.TW.ShowEditControl(3, edit_value);
|
||||||
|
@ -66,6 +87,34 @@ void TextWindow::ScreenChangeViewProjection(int link, uint32_t v) {
|
||||||
SS.TW.ShowEditControl(10, edit_value);
|
SS.TW.ShowEditControl(10, edit_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenChangeLightDirection(int link, uint32_t v) {
|
||||||
|
SS.TW.ShowEditControl(8, ssprintf("%.2f, %.2f, %.2f", CO(SS.lightDir[v])));
|
||||||
|
SS.TW.edit.meaning = Edit::LIGHT_DIRECTION;
|
||||||
|
SS.TW.edit.i = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenChangeLightIntensity(int link, uint32_t v) {
|
||||||
|
SS.TW.ShowEditControl(31, ssprintf("%.2f", SS.lightIntensity[v]));
|
||||||
|
SS.TW.edit.meaning = Edit::LIGHT_INTENSITY;
|
||||||
|
SS.TW.edit.i = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenChangeLightAmbient(int link, uint32_t v) {
|
||||||
|
SS.TW.ShowEditControl(31, ssprintf("%.2f", SS.ambientIntensity));
|
||||||
|
SS.TW.edit.meaning = Edit::LIGHT_AMBIENT;
|
||||||
|
SS.TW.edit.i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenChangeCameraTangent(int link, uint32_t v) {
|
||||||
|
SS.TW.ShowEditControl(3, ssprintf("%.3f", 1000*SS.cameraTangent));
|
||||||
|
SS.TW.edit.meaning = Edit::CAMERA_TANGENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextWindow::ScreenChangeExplodeDistance(int link, uint32_t v) {
|
||||||
|
SS.TW.ShowEditControl(3, SS.MmToString(SS.explodeDistance, true));
|
||||||
|
SS.TW.edit.meaning = Edit::EXPLODE_DISTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
bool TextWindow::EditControlDoneForView(const std::string &s) {
|
bool TextWindow::EditControlDoneForView(const std::string &s) {
|
||||||
switch(edit.meaning) {
|
switch(edit.meaning) {
|
||||||
case Edit::VIEW_SCALE: {
|
case Edit::VIEW_SCALE: {
|
||||||
|
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
@ -119,7 +119,7 @@ Param.val=-5.00000000000000000000
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00040011
|
Param.h.v.=00040011
|
||||||
Param.val=5.00000000000000000000
|
Param.val=5.00000000000000088818
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00040013
|
Param.h.v.=00040013
|
||||||
|
@ -147,7 +147,7 @@ Param.val=10.00000000000000000000
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00060010
|
Param.h.v.=00060010
|
||||||
Param.val=10.00000000000000000000
|
Param.val=10.29878739785912600269
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00060011
|
Param.h.v.=00060011
|
||||||
|
@ -155,7 +155,7 @@ Param.val=10.00000000000000000000
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00060013
|
Param.h.v.=00060013
|
||||||
Param.val=5.00000000000000000000
|
Param.val=5.29878739785912422633
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00060014
|
Param.h.v.=00060014
|
||||||
|
@ -163,7 +163,7 @@ Param.val=5.00000000000000000000
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00070010
|
Param.h.v.=00070010
|
||||||
Param.val=5.00000000000000000000
|
Param.val=5.29878739785912422633
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00070011
|
Param.h.v.=00070011
|
||||||
|
@ -171,7 +171,7 @@ Param.val=5.00000000000000000000
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00070013
|
Param.h.v.=00070013
|
||||||
Param.val=10.00000000000000000000
|
Param.val=10.29878739785912600269
|
||||||
AddParam
|
AddParam
|
||||||
|
|
||||||
Param.h.v.=00070014
|
Param.h.v.=00070014
|
||||||
|
@ -310,7 +310,7 @@ Entity.type=2001
|
||||||
Entity.construction=0
|
Entity.construction=0
|
||||||
Entity.workplane.v=80020000
|
Entity.workplane.v=80020000
|
||||||
Entity.actPoint.x=-5.00000000000000000000
|
Entity.actPoint.x=-5.00000000000000000000
|
||||||
Entity.actPoint.y=5.00000000000000000000
|
Entity.actPoint.y=5.00000000000000088818
|
||||||
Entity.actVisible=1
|
Entity.actVisible=1
|
||||||
AddEntity
|
AddEntity
|
||||||
|
|
||||||
|
@ -363,7 +363,7 @@ Entity.h.v=00060001
|
||||||
Entity.type=2001
|
Entity.type=2001
|
||||||
Entity.construction=0
|
Entity.construction=0
|
||||||
Entity.workplane.v=80020000
|
Entity.workplane.v=80020000
|
||||||
Entity.actPoint.x=10.00000000000000000000
|
Entity.actPoint.x=10.29878739785912600269
|
||||||
Entity.actPoint.y=10.00000000000000000000
|
Entity.actPoint.y=10.00000000000000000000
|
||||||
Entity.actVisible=1
|
Entity.actVisible=1
|
||||||
AddEntity
|
AddEntity
|
||||||
|
@ -372,7 +372,7 @@ Entity.h.v=00060002
|
||||||
Entity.type=2001
|
Entity.type=2001
|
||||||
Entity.construction=0
|
Entity.construction=0
|
||||||
Entity.workplane.v=80020000
|
Entity.workplane.v=80020000
|
||||||
Entity.actPoint.x=5.00000000000000000000
|
Entity.actPoint.x=5.29878739785912422633
|
||||||
Entity.actPoint.y=5.00000000000000000000
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
Entity.actVisible=1
|
Entity.actVisible=1
|
||||||
AddEntity
|
AddEntity
|
||||||
|
@ -390,7 +390,7 @@ Entity.h.v=00070001
|
||||||
Entity.type=2001
|
Entity.type=2001
|
||||||
Entity.construction=0
|
Entity.construction=0
|
||||||
Entity.workplane.v=80020000
|
Entity.workplane.v=80020000
|
||||||
Entity.actPoint.x=5.00000000000000000000
|
Entity.actPoint.x=5.29878739785912422633
|
||||||
Entity.actPoint.y=5.00000000000000000000
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
Entity.actVisible=1
|
Entity.actVisible=1
|
||||||
AddEntity
|
AddEntity
|
||||||
|
@ -399,7 +399,7 @@ Entity.h.v=00070002
|
||||||
Entity.type=2001
|
Entity.type=2001
|
||||||
Entity.construction=0
|
Entity.construction=0
|
||||||
Entity.workplane.v=80020000
|
Entity.workplane.v=80020000
|
||||||
Entity.actPoint.x=10.00000000000000000000
|
Entity.actPoint.x=10.29878739785912600269
|
||||||
Entity.actPoint.y=5.00000000000000000000
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
Entity.actVisible=1
|
Entity.actVisible=1
|
||||||
AddEntity
|
AddEntity
|
||||||
|
|
|
@ -0,0 +1,463 @@
|
||||||
|
±²³SolveSpaceREVa
|
||||||
|
|
||||||
|
|
||||||
|
Group.h.v=00000001
|
||||||
|
Group.type=5000
|
||||||
|
Group.name=#references
|
||||||
|
Group.color=ff000000
|
||||||
|
Group.skipFirst=0
|
||||||
|
Group.predef.swapUV=0
|
||||||
|
Group.predef.negateU=0
|
||||||
|
Group.predef.negateV=0
|
||||||
|
Group.visible=1
|
||||||
|
Group.suppress=0
|
||||||
|
Group.relaxConstraints=0
|
||||||
|
Group.allowRedundant=0
|
||||||
|
Group.allDimsReference=0
|
||||||
|
Group.scale=1.00000000000000000000
|
||||||
|
Group.remap={
|
||||||
|
}
|
||||||
|
AddGroup
|
||||||
|
|
||||||
|
Group.h.v=00000002
|
||||||
|
Group.type=5001
|
||||||
|
Group.order=1
|
||||||
|
Group.name=sketch-in-plane
|
||||||
|
Group.activeWorkplane.v=80020000
|
||||||
|
Group.color=ff000000
|
||||||
|
Group.subtype=6000
|
||||||
|
Group.skipFirst=0
|
||||||
|
Group.predef.q.w=1.00000000000000000000
|
||||||
|
Group.predef.origin.v=00010001
|
||||||
|
Group.predef.swapUV=0
|
||||||
|
Group.predef.negateU=0
|
||||||
|
Group.predef.negateV=0
|
||||||
|
Group.visible=1
|
||||||
|
Group.suppress=0
|
||||||
|
Group.relaxConstraints=0
|
||||||
|
Group.allowRedundant=0
|
||||||
|
Group.allDimsReference=0
|
||||||
|
Group.scale=1.00000000000000000000
|
||||||
|
Group.remap={
|
||||||
|
}
|
||||||
|
AddGroup
|
||||||
|
|
||||||
|
Param.h.v.=00010010
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00010011
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00010012
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00010020
|
||||||
|
Param.val=1.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00010021
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00010022
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00010023
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020010
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020011
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020012
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020020
|
||||||
|
Param.val=0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020021
|
||||||
|
Param.val=0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020022
|
||||||
|
Param.val=0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00020023
|
||||||
|
Param.val=0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030010
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030011
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030012
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030020
|
||||||
|
Param.val=0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030021
|
||||||
|
Param.val=-0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030022
|
||||||
|
Param.val=-0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00030023
|
||||||
|
Param.val=-0.50000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00040010
|
||||||
|
Param.val=-5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00040011
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00040013
|
||||||
|
Param.val=-10.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00040014
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00050010
|
||||||
|
Param.val=-10.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00050011
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00050013
|
||||||
|
Param.val=-5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00050014
|
||||||
|
Param.val=10.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00060010
|
||||||
|
Param.val=10.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00060011
|
||||||
|
Param.val=10.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00060013
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00060014
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00070010
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00070011
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00070013
|
||||||
|
Param.val=10.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Param.h.v.=00070014
|
||||||
|
Param.val=5.00000000000000000000
|
||||||
|
AddParam
|
||||||
|
|
||||||
|
Request.h.v=00000001
|
||||||
|
Request.type=100
|
||||||
|
Request.group.v=00000001
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Request.h.v=00000002
|
||||||
|
Request.type=100
|
||||||
|
Request.group.v=00000001
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Request.h.v=00000003
|
||||||
|
Request.type=100
|
||||||
|
Request.group.v=00000001
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Request.h.v=00000004
|
||||||
|
Request.type=200
|
||||||
|
Request.workplane.v=80020000
|
||||||
|
Request.group.v=00000002
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Request.h.v=00000005
|
||||||
|
Request.type=200
|
||||||
|
Request.workplane.v=80020000
|
||||||
|
Request.group.v=00000002
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Request.h.v=00000006
|
||||||
|
Request.type=200
|
||||||
|
Request.workplane.v=80020000
|
||||||
|
Request.group.v=00000002
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Request.h.v=00000007
|
||||||
|
Request.type=200
|
||||||
|
Request.workplane.v=80020000
|
||||||
|
Request.group.v=00000002
|
||||||
|
Request.construction=0
|
||||||
|
AddRequest
|
||||||
|
|
||||||
|
Entity.h.v=00010000
|
||||||
|
Entity.type=10000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00010001
|
||||||
|
Entity.normal.v=00010020
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00010001
|
||||||
|
Entity.type=2000
|
||||||
|
Entity.construction=1
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00010020
|
||||||
|
Entity.type=3000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00010001
|
||||||
|
Entity.actNormal.w=1.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00020000
|
||||||
|
Entity.type=10000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00020001
|
||||||
|
Entity.normal.v=00020020
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00020001
|
||||||
|
Entity.type=2000
|
||||||
|
Entity.construction=1
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00020020
|
||||||
|
Entity.type=3000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00020001
|
||||||
|
Entity.actNormal.w=0.50000000000000000000
|
||||||
|
Entity.actNormal.vx=0.50000000000000000000
|
||||||
|
Entity.actNormal.vy=0.50000000000000000000
|
||||||
|
Entity.actNormal.vz=0.50000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00030000
|
||||||
|
Entity.type=10000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00030001
|
||||||
|
Entity.normal.v=00030020
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00030001
|
||||||
|
Entity.type=2000
|
||||||
|
Entity.construction=1
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00030020
|
||||||
|
Entity.type=3000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00030001
|
||||||
|
Entity.actNormal.w=0.50000000000000000000
|
||||||
|
Entity.actNormal.vx=-0.50000000000000000000
|
||||||
|
Entity.actNormal.vy=-0.50000000000000000000
|
||||||
|
Entity.actNormal.vz=-0.50000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00040000
|
||||||
|
Entity.type=11000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00040001
|
||||||
|
Entity.point[1].v=00040002
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00040001
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=-5.00000000000000000000
|
||||||
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00040002
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=-10.00000000000000000000
|
||||||
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00050000
|
||||||
|
Entity.type=11000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00050001
|
||||||
|
Entity.point[1].v=00050002
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00050001
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=-10.00000000000000000000
|
||||||
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00050002
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=-5.00000000000000000000
|
||||||
|
Entity.actPoint.y=10.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00060000
|
||||||
|
Entity.type=11000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00060001
|
||||||
|
Entity.point[1].v=00060002
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00060001
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=10.00000000000000000000
|
||||||
|
Entity.actPoint.y=10.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00060002
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=5.00000000000000000000
|
||||||
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00070000
|
||||||
|
Entity.type=11000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=00070001
|
||||||
|
Entity.point[1].v=00070002
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00070001
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=5.00000000000000000000
|
||||||
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=00070002
|
||||||
|
Entity.type=2001
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.workplane.v=80020000
|
||||||
|
Entity.actPoint.x=10.00000000000000000000
|
||||||
|
Entity.actPoint.y=5.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=80020000
|
||||||
|
Entity.type=10000
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=80020002
|
||||||
|
Entity.normal.v=80020001
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=80020001
|
||||||
|
Entity.type=3010
|
||||||
|
Entity.construction=0
|
||||||
|
Entity.point[0].v=80020002
|
||||||
|
Entity.actNormal.w=1.00000000000000000000
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Entity.h.v=80020002
|
||||||
|
Entity.type=2012
|
||||||
|
Entity.construction=1
|
||||||
|
Entity.actVisible=1
|
||||||
|
AddEntity
|
||||||
|
|
||||||
|
Constraint.h.v=00000001
|
||||||
|
Constraint.type=20
|
||||||
|
Constraint.group.v=00000002
|
||||||
|
Constraint.workplane.v=80020000
|
||||||
|
Constraint.ptA.v=00040002
|
||||||
|
Constraint.ptB.v=00050001
|
||||||
|
Constraint.other=0
|
||||||
|
Constraint.other2=0
|
||||||
|
Constraint.reference=0
|
||||||
|
AddConstraint
|
||||||
|
|
||||||
|
Constraint.h.v=00000002
|
||||||
|
Constraint.type=20
|
||||||
|
Constraint.group.v=00000002
|
||||||
|
Constraint.workplane.v=80020000
|
||||||
|
Constraint.ptA.v=00060002
|
||||||
|
Constraint.ptB.v=00070001
|
||||||
|
Constraint.other=0
|
||||||
|
Constraint.other2=0
|
||||||
|
Constraint.reference=0
|
||||||
|
AddConstraint
|
||||||
|
|
||||||
|
Constraint.h.v=00000003
|
||||||
|
Constraint.type=54
|
||||||
|
Constraint.group.v=00000002
|
||||||
|
Constraint.workplane.v=80020000
|
||||||
|
Constraint.entityA.v=00040000
|
||||||
|
Constraint.entityB.v=00050000
|
||||||
|
Constraint.entityC.v=00070000
|
||||||
|
Constraint.entityD.v=00060000
|
||||||
|
Constraint.other=0
|
||||||
|
Constraint.other2=0
|
||||||
|
Constraint.reference=0
|
||||||
|
AddConstraint
|
||||||
|
|
|
@ -8,12 +8,12 @@ TEST_CASE(normal_roundtrip) {
|
||||||
|
|
||||||
TEST_CASE(normal_migrate_from_v20) {
|
TEST_CASE(normal_migrate_from_v20) {
|
||||||
CHECK_LOAD("normal_v20.slvs");
|
CHECK_LOAD("normal_v20.slvs");
|
||||||
CHECK_SAVE("normal.slvs");
|
CHECK_SAVE("normal_old_version.slvs");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(normal_migrate_from_v22) {
|
TEST_CASE(normal_migrate_from_v22) {
|
||||||
CHECK_LOAD("normal_v22.slvs");
|
CHECK_LOAD("normal_v22.slvs");
|
||||||
CHECK_SAVE("normal.slvs");
|
CHECK_SAVE("normal_old_version.slvs");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(other_roundtrip) {
|
TEST_CASE(other_roundtrip) {
|
||||||
|
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
TEST_CASE(normal_roundtrip) {
|
TEST_CASE(normal_roundtrip) {
|
||||||
CHECK_LOAD("normal.slvs");
|
CHECK_LOAD("normal.slvs");
|
||||||
// Can't render images through cairo for now.
|
CHECK_RENDER("normal.png");
|
||||||
// CHECK_RENDER("normal.png");
|
|
||||||
CHECK_SAVE("normal.slvs");
|
CHECK_SAVE("normal.slvs");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(linked_roundtrip) {
|
TEST_CASE(linked_roundtrip) {
|
||||||
CHECK_LOAD("linked.slvs");
|
CHECK_LOAD("linked.slvs");
|
||||||
// CHECK_RENDER("linked.png");
|
CHECK_RENDER("linked.png");
|
||||||
CHECK_SAVE("linked.slvs");
|
CHECK_SAVE("linked.slvs");
|
||||||
}
|
}
|
||||||
|
|