Merge branch 'solvespace:master' into python

pull/493/head
Yuan Chang 2021-12-02 09:18:36 +08:00 committed by GitHub
commit 91d3477314
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 10607 additions and 3619 deletions

View File

@ -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

View File

@ -1,4 +1,16 @@
#!/bin/sh -xe #!/bin/sh -xe
if [ "$1" = "ci" ]; then
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 brew install libomp
git submodule update --init fi
git submodule update --init extlib/cairo extlib/freetype extlib/libdxfrw extlib/libpng extlib/mimalloc extlib/pixman extlib/zlib

View File

@ -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"

View File

@ -9,7 +9,16 @@ 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:
needs: [cancel_previous_runs]
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
name: Test Ubuntu name: Test Ubuntu
steps: steps:
@ -20,6 +29,7 @@ jobs:
run: .github/scripts/build-ubuntu.sh 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:
@ -107,6 +118,8 @@ jobs:
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
@ -135,7 +148,11 @@ jobs:
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
@ -159,37 +176,10 @@ jobs:
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

View File

@ -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

View File

@ -1,10 +1,42 @@
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 intersection boolean operation for solid models.
* New groups, revolution and helical extrusion. * New groups, revolution and helical extrusion.
* Extrude, lathe, translate and rotate groups can use the "assembly" * Extrude, lathe, translate and rotate groups can use the "assembly"
@ -31,8 +63,10 @@ New sketch features:
intersecting) that would propagate in subsequent groups. intersecting) that would propagate in subsequent groups.
* It is now possible to press "g" to toggle construction on new objects while * It is now possible to press "g" to toggle construction on new objects while
they are still being drawn. 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 * When dragging an arc or rectangle point, it will be automatically
constrained to other points with a click. constrained to other points with a click.
* When selecting a constraint, the requests it constraints can be selected * When selecting a constraint, the requests it constraints can be selected
@ -55,24 +89,22 @@ New constraint features:
degrees. degrees.
New export/import features: New export/import features:
* Link IDF circuit boards in an assembly (.emn files) * Link IDF circuit boards in an assembly (.emn files)
* Three.js: allow configuring projection for exported model, and initially * Three.js: allow configuring projection for exported model, and initially
use the current viewport projection. use the current viewport projection.
* Wavefront OBJ: a material file is exported alongside the model, containing * Wavefront OBJ: a material file is exported alongside the model, containing
mesh color information. mesh color information.
* DXF/DWG: 3D DXF files are imported as construction entities, in 3d. * DXF/DWG: 3D DXF files are imported as construction entities, in 3d.
* [ADDED 2019-02-25](https://github.com/solvespace/solvespace/pull/384) and [REMOVED 2020-11-13](https://github.com/solvespace/solvespace/issues/795):
Q3D: [Q3D](https://github.com/q3k/q3d/) triangle meshes can now be
exported. This format allows to easily hack on triangle mesh data created
in SolveSpace, supports colour information and is more space efficient than
most other formats.
* VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org). * VRML (WRL) triangle meshes can now be exported, useful for e.g. [KiCAD](http://kicad.org).
* Export 2d section: custom styled entities that lie in the same * Export 2d section: custom styled entities that lie in the same
plane as the exported section are included. plane as the exported section are included.
* Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files. * Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
* STEP export includes object colors and transparency. * STEP export includes object colors and transparency.
* Default "line styles" have a new "export these objects" option.
New rendering features: New rendering features:
* The "Show/hide hidden lines" button is now a tri-state button that allows * 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 showing all lines (on top of shaded mesh), stippling occluded lines
or not drawing them at all. or not drawing them at all.
@ -82,6 +114,7 @@ New rendering features:
s000d-#def-dim-solid style applied to them). s000d-#def-dim-solid style applied to them).
New measurement/analysis features: New measurement/analysis features:
* New choice for base unit, meters. * New choice for base unit, meters.
* New command for measuring total length of selected entities, * New command for measuring total length of selected entities,
"Analyze → Measure Perimeter". "Analyze → Measure Perimeter".
@ -96,11 +129,13 @@ New measurement/analysis features:
workplane is displayed. workplane is displayed.
Other new features: Other new features:
* Improvements to the text window for selected entities and constraints. * Improvements to the text window for selected entities and constraints.
* Ambient light source added in text window to allow flat shaded renderings. * Ambient light source added in text window to allow flat shaded renderings.
* New command-line interface, for batch exporting and more. * New command-line interface, for batch exporting and more.
* The graphical interface now supports HiDPI screens on every OS. * The graphical interface now supports HiDPI screens on every OS.
* New option to lock Z axis to be always vertical, like in SketchUp. * New option to lock Z axis to be always vertical when rotating the view,
a.k.a. "turntable navigation".
* New button to hide all construction entities. * New button to hide all construction entities.
* New link to match the on-screen size of the sketch with its actual size, * New link to match the on-screen size of the sketch with its actual size,
"view → set to full scale". "view → set to full scale".
@ -124,9 +159,10 @@ Other new features:
* New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes * New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes
to enable support for multi-threading and link-time optimization. to enable support for multi-threading and link-time optimization.
* "Shift+Scroll" for ten times finer zoom. * "Shift+Scroll" for ten times finer zoom.
* Chinese translation * Translations: Chinese, French, German, Russian, Turkish, Ukrainian.
Bugs fixed: Bugs fixed:
* Fixed broken --view options for command line thumbnail image creation. * Fixed broken --view options for command line thumbnail image creation.
* Some errors in Triangulation of surfaces. * Some errors in Triangulation of surfaces.
* Some NURNS boolean operations that failed particularly on surfaces * Some NURNS boolean operations that failed particularly on surfaces
@ -157,6 +193,7 @@ Bugs fixed:
--- ---
Bug fixes: Bug fixes:
* Do not crash when changing an unconstrained lathe group between * Do not crash when changing an unconstrained lathe group between
union and difference modes. union and difference modes.
@ -164,6 +201,7 @@ Bug fixes:
--- ---
Bug fixes: Bug fixes:
* Do not crash when applying a symmetry constraint to two points. * Do not crash when applying a symmetry constraint to two points.
* Fix TTF font metrics again (properly this time). * Fix TTF font metrics again (properly this time).
* Fix the "draw back faces in red" option. * Fix the "draw back faces in red" option.
@ -174,10 +212,12 @@ Bug fixes:
--- ---
Other new features: Other new features:
* OS X: support 3Dconnexion devices (SpaceMouse, SpaceNavigator, etc). * OS X: support 3Dconnexion devices (SpaceMouse, SpaceNavigator, etc).
* GTK: files with uppercase extensions can be opened. * GTK: files with uppercase extensions can be opened.
Bug fixes: Bug fixes:
* Do not remove autosaves after successfully opening a file, preventing * Do not remove autosaves after successfully opening a file, preventing
data loss in case of two abnormal terminations in a row. data loss in case of two abnormal terminations in a row.
* Do not crash when changing autosave interval. * Do not crash when changing autosave interval.
@ -200,6 +240,7 @@ Bug fixes:
--- ---
New sketch features: New sketch features:
* Lathe groups create circle and face entities. * Lathe groups create circle and face entities.
* New toolbar button for creating lathe groups. * New toolbar button for creating lathe groups.
* Chord tolerance is separated into two: display chord tolerance (specified * Chord tolerance is separated into two: display chord tolerance (specified
@ -222,6 +263,7 @@ New sketch features:
"points not all coplanar". "points not all coplanar".
New constraint features: New constraint features:
* Height of the font used for drawing constraint labels can be changed. * Height of the font used for drawing constraint labels can be changed.
* New constraint, length difference, placed with J. * New constraint, length difference, placed with J.
(Patch by Peter Ruevski) (Patch by Peter Ruevski)
@ -239,11 +281,13 @@ New constraint features:
* Extension lines are drawn for point-line distance constraints. * Extension lines are drawn for point-line distance constraints.
New solver features: New solver features:
* Sketches with redundant and unsolvable constraints are distinguished. * Sketches with redundant and unsolvable constraints are distinguished.
* New group setting, "allow redundant constraints". Note that it makes * New group setting, "allow redundant constraints". Note that it makes
the solver less stable. the solver less stable.
New rendering and styling features: New rendering and styling features:
* New line style parameter: stippling, based on ISO 128. * New line style parameter: stippling, based on ISO 128.
* Outlines of solids can be drawn in a particular style (by default, thick * Outlines of solids can be drawn in a particular style (by default, thick
lines) controlled by the "Show outline of solid model" button. lines) controlled by the "Show outline of solid model" button.
@ -252,6 +296,7 @@ New rendering and styling features:
* Solids can be made transparent. * Solids can be made transparent.
New export/import features: New export/import features:
* The old "import" command (for .slvs files) is renamed to "link". * The old "import" command (for .slvs files) is renamed to "link".
* If a linked .slvs file is not found, first the relative path recorded * If a linked .slvs file is not found, first the relative path recorded
in the .slvs file is checked and then the absolute path; this is in the .slvs file is checked and then the absolute path; this is
@ -271,6 +316,7 @@ New export/import features:
* When exporting 2d views, overlapping lines are removed. * 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. * Native Linux (GTK 2 and GTK 3) and Mac OS X ports.
* Automatically save and then restore sketches if SolveSpace crashes. * Automatically save and then restore sketches if SolveSpace crashes.
(Patch by Marc Britten) (Patch by Marc Britten)

View File

@ -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,12 +77,14 @@ endif()
# common compiler flags # common compiler flags
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
if (NOT APPLE)
set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.") set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.")
check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP) check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP)
if(HAS_FILE_PREFIX_MAP) if(HAS_FILE_PREFIX_MAP)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}")
endif() endif()
endif()
if(MINGW) if(MINGW)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
@ -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)

View File

@ -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.

View File

@ -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"

View File

@ -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).

View File

@ -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

View File

@ -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;

View File

@ -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",

View File

@ -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

Binary file not shown.

View File

@ -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>

View File

@ -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"
};

View File

@ -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"
};

View File

@ -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"
};

View File

@ -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"
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 B

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 264 B

View File

@ -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,简体中文

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2276
res/locales/es_AR.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2265
res/locales/tr_TR.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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}
COMMAND cp -p /usr/local/opt/libomp/lib/libomp.dylib ${bundle_resources}/libomp.dylib
)
add_custom_command(TARGET solvespace POST_BUILD add_custom_command(TARGET solvespace POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${bundle_bin} COMMAND cp -r ${CMAKE_BINARY_DIR}/Resources $<TARGET_BUNDLE_CONTENT_DIR:solvespace>
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:solvespace-cli> ${bundle_bin} )
COMMAND install_name_tool -change /usr/local/opt/libomp/lib/libomp.dylib "@executable_path/../Resources/lib/libomp.dylib" ${bundle_bin}/${bundle} if(ENABLE_OPENMP)
execute_process(COMMAND install_name_tool -id ${LIBOMP_LINK_PATH} ${LIBOMP_LIB_PATH})
message("FROM " ${${LIBOMP_LIB_PATH}} "TO" $<TARGET_BUNDLE_CONTENT_DIR:solvespace>/Resources/libomp.dylib)
add_custom_command(TARGET solvespace POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${LIBOMP_LIB_PATH} $<TARGET_BUNDLE_CONTENT_DIR:solvespace>/Resources/libomp.dylib
COMMAND install_name_tool -change ${LIBOMP_LINK_PATH} ${LIBOMP_LINK_PATH_UTILS} $<TARGET_FILE:solvespace-debugtool>
)
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" COMMENT "Bundling executable solvespace-cli"
VERBATIM) VERBATIM)
endif() endif()
endif()

View File

@ -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;

View File

@ -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());

View File

@ -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:
if(gs.points == 1 && gs.n == 1) {
c.type = Type::COMMENT;
c.ptA = gs.point[0];
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.operation = GraphicsWindow::Pending::COMMAND;
SS.GW.pending.command = Command::COMMENT; SS.GW.pending.command = Command::COMMENT;
SS.GW.pending.description = _("click center of comment text"); SS.GW.pending.description = _("click center of comment text");
SS.ScheduleShowTW(); SS.ScheduleShowTW();
}
break; break;
default: ssassert(false, "Unexpected menu ID"); default: ssassert(false, "Unexpected menu ID");

View File

@ -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:
@ -335,6 +343,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
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);
EntityBase *b = SK.GetEntity(entityB); EntityBase *b = SK.GetEntity(entityB);
@ -344,6 +456,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
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();

View File

@ -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;
} }
} }

View File

@ -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,6 +1051,41 @@ 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);
@ -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();
}

View File

@ -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]);

258
src/dsc.h
View File

@ -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;
} else {
if(elemstore[*it].h.v != h.v) {
return nullptr; return nullptr;
} }
if (it->h.v == h.v) { return &elemstore[*it];
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;
} }
}; };

View File

@ -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);

View File

@ -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();
} }

View File

@ -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;
} }
@ -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());
} }
} }

View File

@ -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));
} }
@ -713,6 +711,8 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityLi
{ {
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.

View File

@ -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));
} }

View File

@ -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();
} }

View File

@ -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;
}

View File

@ -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;
} }

View File

@ -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);

221
src/importmesh.cpp Normal file
View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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(e->type == Entity::Type::CIRCLE && selection.n <= 1) {
// Drag the radius.
ClearSelection();
pending.circle = dragEntity;
pending.operation = Pending::DRAGGING_RADIUS;
} else if(e->IsNormal()) {
ClearSelection();
pending.normal = dragEntity;
pending.operation = Pending::DRAGGING_NORMAL;
} else {
if(!hoverWasSelectedOnMousedown) { if(!hoverWasSelectedOnMousedown) {
// The user clicked an unselected entity, which // The user clicked an unselected entity, which
// means they're dragging just the hovered thing, // means they're dragging just the hovered thing,
// not the full selection. So clear all the selection // not the full selection. So clear all the selection
// except that entity. // except that entity.
ClearSelection(); ClearSelection();
MakeSelected(e->h); MakeSelected(dragEntity);
} }
if(e->type == Entity::Type::CIRCLE && selection.n <= 1) {
// Drag the radius.
pending.circle = dragEntity;
pending.operation = Pending::DRAGGING_RADIUS;
} else if(e->IsNormal()) {
pending.normal = dragEntity;
pending.operation = Pending::DRAGGING_NORMAL;
} else {
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());

View File

@ -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 = {

View File

@ -33,8 +33,19 @@
#if defined(HAVE_SPACEWARE) #if defined(HAVE_SPACEWARE)
# include <spnav.h> # include <spnav.h>
# include <gdk/gdk.h>
# if defined(GDK_WINDOWING_X11)
# include <gdk/gdkx.h> # include <gdk/gdkx.h>
# endif # 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
#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,30 +1082,80 @@ 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;
} }
}
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_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) {
std::shared_ptr<WindowImplGtk> windowImpl = std::shared_ptr<WindowImplGtk> windowImpl =
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())) {
if(spnav_x11_open(gdk_x11_get_default_xdisplay(),
gdk_x11_window_get_xid(gdkWindow->gobj())) != -1) {
gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get()); gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get());
spnav_x11_open(gdk_x11_get_default_xdisplay(), } else if(spnav_open() != -1) {
gdk_x11_window_get_xid(gdkWindow->gobj())); 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) {}
#endif #endif
@ -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());
} }
} }

View File

@ -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,15 +573,58 @@ 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 {
using Platform::MouseEvent; using Platform::MouseEvent;
@ -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,9 +1647,11 @@ void RunGui() {
} }
void ExitGui() { void ExitGui() {
[NSApp setDelegate:nil]; if(!ssDelegate.exiting) {
ssDelegate.exiting = true;
[NSApp terminate:nil]; [NSApp terminate:nil];
} }
}
void ClearGui() {} void ClearGui() {}

View File

@ -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(isSaveDialog) {
SetTitle(C_("title", "Save File"));
if(GetFilename().IsEmpty()) { if(GetFilename().IsEmpty()) {
SetFilename(Path::From(_("untitled"))); SetFilename(Path::From(_("untitled")));
} }
if(isSaveDialog) {
return GetSaveFileNameW(&ofn) == TRUE; return GetSaveFileNameW(&ofn) == TRUE;
} else { } else {
SetTitle(C_("title", "Open File"));
return GetOpenFileNameW(&ofn) == TRUE; return GetOpenFileNameW(&ofn) == TRUE;
} }
} }

View File

@ -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");
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -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;

View File

@ -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:
@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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();
} }

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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, ...) {

View File

@ -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 =

View File

@ -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);

View File

@ -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: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -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

View File

@ -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

View File

@ -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) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -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");
} }