Merge branch 'solvespace:master' into python
|
@ -1,27 +1,52 @@
|
|||
#!/bin/sh -xe
|
||||
|
||||
mkdir build || true
|
||||
cd build
|
||||
|
||||
OSX_TARGET="10.9"
|
||||
|
||||
ENABLE_SANITIZERS="OFF"
|
||||
if [ "$1" = "release" ]; then
|
||||
BUILD_TYPE=RelWithDebInfo
|
||||
cmake \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_TARGET}" \
|
||||
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
|
||||
-DENABLE_OPENMP="ON" \
|
||||
-DENABLE_LTO="ON" \
|
||||
..
|
||||
BUILD_TYPE="RelWithDebInfo"
|
||||
ENABLE_LTO="ON"
|
||||
else
|
||||
BUILD_TYPE=Debug
|
||||
cmake \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_TARGET}" \
|
||||
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
|
||||
-DENABLE_OPENMP="ON" \
|
||||
-DENABLE_SANITIZERS="ON" \
|
||||
..
|
||||
BUILD_TYPE="Debug"
|
||||
ENABLE_LTO="OFF"
|
||||
fi
|
||||
|
||||
cmake --build . --config "${BUILD_TYPE}" -- -j$(nproc)
|
||||
make -j$(nproc) test_solvespace
|
||||
# this is an option for our Github CI only, since it doesn't have a macos arm64 image yet
|
||||
CMAKE_GENERATOR="Unix Makefiles"
|
||||
CMAKE_PREFIX_PATH=""
|
||||
if [ "$2" = "arm64" ]; then
|
||||
OSX_ARCHITECTURE="arm64"
|
||||
CMAKE_PREFIX_PATH=$(find /tmp/libomp-arm64/libomp -depth 1)
|
||||
git apply cmake/libpng-macos-arm64.patch || echo "Could not apply patch, probably already patched..."
|
||||
mkdir build-arm64 || true
|
||||
cd build-arm64
|
||||
elif [ "$2" = "x86_64" ]; then
|
||||
OSX_ARCHITECTURE="x86_64"
|
||||
CMAKE_PREFIX_PATH=$(find /tmp/libomp-x86_64/libomp -depth 1)
|
||||
mkdir build || true
|
||||
cd build
|
||||
else
|
||||
mkdir build || true
|
||||
cd build
|
||||
fi
|
||||
|
||||
if [ "$3" = "xcode" ]; then
|
||||
CMAKE_GENERATOR="Xcode"
|
||||
fi
|
||||
|
||||
cmake \
|
||||
-G "${CMAKE_GENERATOR}" \
|
||||
-D CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}" \
|
||||
-D CMAKE_OSX_ARCHITECTURES="${OSX_ARCHITECTURE}" \
|
||||
-D CMAKE_BUILD_TYPE="${BUILD_TYPE}" \
|
||||
-D ENABLE_OPENMP="ON" \
|
||||
-D ENABLE_SANITIZERS="${ENABLE_SANITIZERS}" \
|
||||
-D ENABLE_LTO="${ENABLE_LTO}" \
|
||||
..
|
||||
|
||||
if [ "$3" = "xcode" ]; then
|
||||
open solvespace.xcodeproj
|
||||
else
|
||||
cmake --build . --config "${BUILD_TYPE}" -j$(sysctl -n hw.logicalcpu)
|
||||
if [ $(uname -m) = "$2" ]; then
|
||||
make -j$(sysctl -n hw.logicalcpu) test_solvespace
|
||||
fi
|
||||
fi
|
|
@ -1,4 +1,16 @@
|
|||
#!/bin/sh -xe
|
||||
|
||||
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
|
||||
git submodule update --init
|
||||
fi
|
||||
|
||||
git submodule update --init extlib/cairo extlib/freetype extlib/libdxfrw extlib/libpng extlib/mimalloc extlib/pixman extlib/zlib
|
||||
|
|
|
@ -1,8 +1,29 @@
|
|||
#!/bin/bash -xe
|
||||
|
||||
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
|
||||
|
||||
openmp="bin/SolveSpace.app/Contents/Resources/lib/libomp.dylib"
|
||||
openmp="bin/SolveSpace.app/Contents/Resources/libomp.dylib"
|
||||
app="bin/SolveSpace.app"
|
||||
dmg="bin/SolveSpace.dmg"
|
||||
bundle_id="com.solvespace.solvespace"
|
||||
|
|
|
@ -9,7 +9,16 @@ on:
|
|||
- created
|
||||
|
||||
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:
|
||||
needs: [cancel_previous_runs]
|
||||
runs-on: ubuntu-18.04
|
||||
name: Test Ubuntu
|
||||
steps:
|
||||
|
@ -20,6 +29,7 @@ jobs:
|
|||
run: .github/scripts/build-ubuntu.sh
|
||||
|
||||
test_windows:
|
||||
needs: [cancel_previous_runs]
|
||||
runs-on: windows-2019
|
||||
name: Test Windows
|
||||
steps:
|
||||
|
@ -32,14 +42,15 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
test_macos:
|
||||
runs-on: macos-latest
|
||||
needs: [cancel_previous_runs]
|
||||
runs-on: macos-10.15
|
||||
name: Test macOS
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: .github/scripts/install-macos.sh
|
||||
run: .github/scripts/install-macos.sh ci
|
||||
- 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:
|
||||
needs: [test_ubuntu, test_windows, test_macos]
|
||||
|
@ -80,13 +91,13 @@ jobs:
|
|||
build_release_macos:
|
||||
needs: [test_ubuntu, test_windows, test_macos]
|
||||
name: Build Release macOS
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: .github/scripts/install-macos.sh
|
||||
run: .github/scripts/install-macos.sh ci
|
||||
- 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
|
||||
run: .github/scripts/sign-macos.sh
|
||||
env:
|
||||
|
@ -107,6 +118,8 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Fetch Tags
|
||||
run: git fetch --force --tags
|
||||
- name: Set Up Source
|
||||
run: rsync --filter=":- .gitignore" -r ./ pkg/snap/solvespace-snap-src
|
||||
- name: Build Snap
|
||||
|
@ -135,7 +148,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
image: tonistiigi/binfmt@sha256:df15403e06a03c2f461c1f7938b171fda34a5849eb63a70e2a2109ed5a778bde
|
||||
- uses: actions/checkout@v2
|
||||
- name: Fetch Tags
|
||||
run: git fetch --force --tags
|
||||
- name: Set Up Source
|
||||
run: rsync --filter=":- .gitignore" -r ./ pkg/snap/solvespace-snap-src
|
||||
- name: Build Snap
|
||||
|
@ -159,37 +176,10 @@ jobs:
|
|||
snap: ${{ steps.build.outputs.snap }}
|
||||
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:
|
||||
name: Upload Release Assets
|
||||
needs: [build_release_windows, build_release_windows_openmp, build_release_macos, update_edge_release]
|
||||
if: always()
|
||||
needs: [build_release_windows, build_release_windows_openmp, build_release_macos]
|
||||
if: "!cancelled() && github.event_name == 'release'"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download All Workflow Artifacts
|
||||
|
@ -197,15 +187,9 @@ jobs:
|
|||
- name: Get Release Upload URL
|
||||
id: get_upload_url
|
||||
env:
|
||||
event_name: ${{ github.event_name }}
|
||||
event: ${{ toJson(github.event) }}
|
||||
edge_upload_url: ${{ needs.update_edge_release.outputs.upload_url }}
|
||||
run: |
|
||||
if [ "$event_name" = "release" ]; then
|
||||
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 "Upload URL: $upload_url"
|
||||
- name: Upload solvespace.exe
|
||||
|
|
|
@ -34,11 +34,11 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
test_macos:
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-10.15
|
||||
name: Test macOS
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: .github/scripts/install-macos.sh
|
||||
run: .github/scripts/install-macos.sh ci
|
||||
- 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
|
||||
|
|
60
CHANGELOG.md
|
@ -1,10 +1,42 @@
|
|||
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
|
||||
---
|
||||
|
||||
New sketch features:
|
||||
|
||||
* New intersection boolean operation for solid models.
|
||||
* New groups, revolution and helical extrusion.
|
||||
* Extrude, lathe, translate and rotate groups can use the "assembly"
|
||||
|
@ -31,8 +63,10 @@ New sketch features:
|
|||
intersecting) that would propagate in subsequent groups.
|
||||
* It is now possible to press "g" to toggle construction on new objects while
|
||||
they are still being drawn.
|
||||
* Allow right click to end sketching of all entities.
|
||||
|
||||
New constraint features:
|
||||
|
||||
* When dragging an arc or rectangle point, it will be automatically
|
||||
constrained to other points with a click.
|
||||
* When selecting a constraint, the requests it constraints can be selected
|
||||
|
@ -55,24 +89,22 @@ New constraint features:
|
|||
degrees.
|
||||
|
||||
New export/import features:
|
||||
|
||||
* Link IDF circuit boards in an assembly (.emn files)
|
||||
* Three.js: allow configuring projection for exported model, and initially
|
||||
use the current viewport projection.
|
||||
* Wavefront OBJ: a material file is exported alongside the model, containing
|
||||
mesh color information.
|
||||
* 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).
|
||||
* Export 2d section: custom styled entities that lie in the same
|
||||
plane as the exported section are included.
|
||||
* Added ExportBackgroundColor in configuration for EPS, PDF, and SVG files.
|
||||
* STEP export includes object colors and transparency.
|
||||
* Default "line styles" have a new "export these objects" option.
|
||||
|
||||
New rendering features:
|
||||
|
||||
* The "Show/hide hidden lines" button is now a tri-state button that allows
|
||||
showing all lines (on top of shaded mesh), stippling occluded lines
|
||||
or not drawing them at all.
|
||||
|
@ -82,6 +114,7 @@ New rendering features:
|
|||
s000d-#def-dim-solid style applied to them).
|
||||
|
||||
New measurement/analysis features:
|
||||
|
||||
* New choice for base unit, meters.
|
||||
* New command for measuring total length of selected entities,
|
||||
"Analyze → Measure Perimeter".
|
||||
|
@ -96,11 +129,13 @@ New measurement/analysis features:
|
|||
workplane is displayed.
|
||||
|
||||
Other new features:
|
||||
|
||||
* Improvements to the text window for selected entities and constraints.
|
||||
* Ambient light source added in text window to allow flat shaded renderings.
|
||||
* New command-line interface, for batch exporting and more.
|
||||
* 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 link to match the on-screen size of the sketch with its actual size,
|
||||
"view → set to full scale".
|
||||
|
@ -124,9 +159,10 @@ Other new features:
|
|||
* New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes
|
||||
to enable support for multi-threading and link-time optimization.
|
||||
* "Shift+Scroll" for ten times finer zoom.
|
||||
* Chinese translation
|
||||
* Translations: Chinese, French, German, Russian, Turkish, Ukrainian.
|
||||
|
||||
Bugs fixed:
|
||||
|
||||
* Fixed broken --view options for command line thumbnail image creation.
|
||||
* Some errors in Triangulation of surfaces.
|
||||
* Some NURNS boolean operations that failed particularly on surfaces
|
||||
|
@ -157,6 +193,7 @@ Bugs fixed:
|
|||
---
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Do not crash when changing an unconstrained lathe group between
|
||||
union and difference modes.
|
||||
|
||||
|
@ -164,6 +201,7 @@ Bug fixes:
|
|||
---
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Do not crash when applying a symmetry constraint to two points.
|
||||
* Fix TTF font metrics again (properly this time).
|
||||
* Fix the "draw back faces in red" option.
|
||||
|
@ -174,10 +212,12 @@ Bug fixes:
|
|||
---
|
||||
|
||||
Other new features:
|
||||
|
||||
* OS X: support 3Dconnexion devices (SpaceMouse, SpaceNavigator, etc).
|
||||
* GTK: files with uppercase extensions can be opened.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* Do not remove autosaves after successfully opening a file, preventing
|
||||
data loss in case of two abnormal terminations in a row.
|
||||
* Do not crash when changing autosave interval.
|
||||
|
@ -200,6 +240,7 @@ Bug fixes:
|
|||
---
|
||||
|
||||
New sketch features:
|
||||
|
||||
* Lathe groups create circle and face entities.
|
||||
* New toolbar button for creating lathe groups.
|
||||
* Chord tolerance is separated into two: display chord tolerance (specified
|
||||
|
@ -222,6 +263,7 @@ New sketch features:
|
|||
"points not all coplanar".
|
||||
|
||||
New constraint features:
|
||||
|
||||
* Height of the font used for drawing constraint labels can be changed.
|
||||
* New constraint, length difference, placed with J.
|
||||
(Patch by Peter Ruevski)
|
||||
|
@ -239,11 +281,13 @@ New constraint features:
|
|||
* Extension lines are drawn for point-line distance constraints.
|
||||
|
||||
New solver features:
|
||||
|
||||
* Sketches with redundant and unsolvable constraints are distinguished.
|
||||
* New group setting, "allow redundant constraints". Note that it makes
|
||||
the solver less stable.
|
||||
|
||||
New rendering and styling features:
|
||||
|
||||
* New line style parameter: stippling, based on ISO 128.
|
||||
* Outlines of solids can be drawn in a particular style (by default, thick
|
||||
lines) controlled by the "Show outline of solid model" button.
|
||||
|
@ -252,6 +296,7 @@ New rendering and styling features:
|
|||
* Solids can be made transparent.
|
||||
|
||||
New export/import features:
|
||||
|
||||
* The old "import" command (for .slvs files) is renamed to "link".
|
||||
* If a linked .slvs file is not found, first the relative path recorded
|
||||
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.
|
||||
|
||||
Other new features:
|
||||
|
||||
* Native Linux (GTK 2 and GTK 3) and Mac OS X ports.
|
||||
* Automatically save and then restore sketches if SolveSpace crashes.
|
||||
(Patch by Marc Britten)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# cmake configuration
|
||||
cmake_minimum_required(VERSION 3.9...3.19)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||
message(FATAL_ERROR
|
||||
|
@ -7,17 +8,10 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
|||
" mkdir build && cd build && cmake ..")
|
||||
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}
|
||||
"${CMAKE_SOURCE_DIR}/cmake/")
|
||||
|
||||
cmake_policy(SET CMP0048 OLD)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
|
||||
|
@ -27,10 +21,15 @@ set(CMAKE_USER_MAKE_RULES_OVERRIDE
|
|||
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX
|
||||
"${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++")
|
||||
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
|
||||
|
||||
# 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:
|
||||
# set(GIT_COMMIT_HASH 0000000000000000000000000000000000000000)
|
||||
|
||||
project(solvespace)
|
||||
set(solvespace_VERSION_MAJOR 3)
|
||||
set(solvespace_VERSION_MINOR 0)
|
||||
string(SUBSTRING "${GIT_COMMIT_HASH}" 0 8 solvespace_GIT_HASH)
|
||||
project(solvespace LANGUAGES C CXX ASM)
|
||||
|
||||
set(ENABLE_GUI ON CACHE BOOL
|
||||
"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>)
|
||||
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_VERSION VERSION_LESS 5.0)
|
||||
# GCC 4.8/4.9 ship with broken but present <regex>. meh.
|
||||
|
@ -82,12 +77,14 @@ endif()
|
|||
# common compiler flags
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if (NOT APPLE)
|
||||
set(FILE_PREFIX_MAP "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.")
|
||||
check_cxx_compiler_flag("${FILE_PREFIX_MAP}" HAS_FILE_PREFIX_MAP)
|
||||
if(HAS_FILE_PREFIX_MAP)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FILE_PREFIX_MAP}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FILE_PREFIX_MAP}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
|
||||
|
@ -210,6 +207,12 @@ if(WIN32 OR APPLE)
|
|||
find_vendored_package(PNG libpng
|
||||
SKIP_INSTALL_ALL ON
|
||||
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)
|
||||
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)
|
||||
|
||||
message(STATUS "Using in-tree pixman")
|
||||
add_vendored_subdirectory(extlib/pixman)
|
||||
set(PIXMAN_FOUND YES)
|
||||
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)
|
||||
list(APPEND PIXMAN_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/pixman/pixman)
|
||||
add_vendored_subdirectory(extlib/pixman)
|
||||
|
||||
message(STATUS "Using in-tree cairo")
|
||||
add_vendored_subdirectory(extlib/cairo)
|
||||
|
@ -275,6 +281,7 @@ if(ENABLE_GUI)
|
|||
elseif(APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_library(APPKIT_LIBRARY AppKit REQUIRED)
|
||||
set(util_LIBRARIES ${APPKIT_LIBRARY})
|
||||
else()
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(SpaceWare)
|
||||
|
|
23
README.md
|
@ -37,16 +37,19 @@ the SolveSpace maintainers for each stable release.
|
|||
|
||||
### 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)
|
||||
|
||||
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
|
||||
|
@ -58,6 +61,20 @@ and cannot guarantee their functionality.
|
|||
|
||||
[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
|
||||
|
||||
See below.
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
diff --git a/extlib/libpng/CMakeLists.txt b/extlib/libpng/CMakeLists.txt
|
||||
index 42ff0f9025..6834ea332e 100644
|
||||
--- a/extlib/libpng/CMakeLists.txt
|
||||
+++ b/extlib/libpng/CMakeLists.txt
|
||||
@@ -65,11 +65,22 @@ option(PNG_HARDWARE_OPTIMIZATIONS "Enable hardware optimizations" ON)
|
||||
set(PNG_PREFIX "" CACHE STRING "Prefix to add to the API function names")
|
||||
set(DFA_XTRA "" CACHE FILEPATH "File containing extra configuration settings")
|
||||
|
||||
+# CMake currently sets CMAKE_SYSTEM_PROCESSOR to one of x86_64 or arm64 on macOS,
|
||||
+# based upon the OS architecture, not the target architecture. As such, we need
|
||||
+# to check CMAKE_OSX_ARCHITECTURES to identify which hardware-specific flags to
|
||||
+# enable. Note that this will fail if you attempt to build a universal binary in
|
||||
+# a single cmake invocation.
|
||||
+if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
+ set(TARGET_ARCH ${CMAKE_OSX_ARCHITECTURES})
|
||||
+else()
|
||||
+ set(TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR})
|
||||
+endif()
|
||||
+
|
||||
if(PNG_HARDWARE_OPTIMIZATIONS)
|
||||
|
||||
# Set definitions and sources for ARM.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
|
||||
+if(TARGET_ARCH MATCHES "^arm" OR
|
||||
+ TARGET_ARCH MATCHES "^aarch64")
|
||||
set(PNG_ARM_NEON_POSSIBLE_VALUES check on off)
|
||||
set(PNG_ARM_NEON "check"
|
||||
CACHE STRING "Enable ARM NEON optimizations: check|on|off; check is default")
|
||||
@@ -95,8 +106,8 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
|
||||
endif()
|
||||
|
||||
# Set definitions and sources for PowerPC.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64*")
|
||||
+if(TARGET_ARCH MATCHES "^powerpc*" OR
|
||||
+ TARGET_ARCH MATCHES "^ppc64*")
|
||||
set(PNG_POWERPC_VSX_POSSIBLE_VALUES on off)
|
||||
set(PNG_POWERPC_VSX "on"
|
||||
CACHE STRING "Enable POWERPC VSX optimizations: on|off; on is default")
|
||||
@@ -118,8 +129,8 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR
|
||||
endif()
|
||||
|
||||
# Set definitions and sources for Intel.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64*")
|
||||
+if(TARGET_ARCH MATCHES "^i?86" OR
|
||||
+ TARGET_ARCH MATCHES "^x86_64*")
|
||||
set(PNG_INTEL_SSE_POSSIBLE_VALUES on off)
|
||||
set(PNG_INTEL_SSE "on"
|
||||
CACHE STRING "Enable INTEL_SSE optimizations: on|off; on is default")
|
||||
@@ -141,8 +152,8 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR
|
||||
endif()
|
||||
|
||||
# Set definitions and sources for MIPS.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "mips64el*")
|
||||
+if(TARGET_ARCH MATCHES "mipsel*" OR
|
||||
+ TARGET_ARCH MATCHES "mips64el*")
|
||||
set(PNG_MIPS_MSA_POSSIBLE_VALUES on off)
|
||||
set(PNG_MIPS_MSA "on"
|
||||
CACHE STRING "Enable MIPS_MSA optimizations: on|off; on is default")
|
||||
@@ -166,26 +177,26 @@ endif()
|
||||
else(PNG_HARDWARE_OPTIMIZATIONS)
|
||||
|
||||
# Set definitions and sources for ARM.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
|
||||
+if(TARGET_ARCH MATCHES "^arm" OR
|
||||
+ TARGET_ARCH MATCHES "^aarch64")
|
||||
add_definitions(-DPNG_ARM_NEON_OPT=0)
|
||||
endif()
|
||||
|
||||
# Set definitions and sources for PowerPC.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^powerpc*" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "^ppc64*")
|
||||
+if(TARGET_ARCH MATCHES "^powerpc*" OR
|
||||
+ TARGET_ARCH MATCHES "^ppc64*")
|
||||
add_definitions(-DPNG_POWERPC_VSX_OPT=0)
|
||||
endif()
|
||||
|
||||
# Set definitions and sources for Intel.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i?86" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "^x86_64*")
|
||||
+if(TARGET_ARCH MATCHES "^i?86" OR
|
||||
+ TARGET_ARCH MATCHES "^x86_64*")
|
||||
add_definitions(-DPNG_INTEL_SSE_OPT=0)
|
||||
endif()
|
||||
|
||||
# Set definitions and sources for MIPS.
|
||||
-if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR
|
||||
- CMAKE_SYSTEM_PROCESSOR MATCHES "mips64el*")
|
||||
+if(TARGET_ARCH MATCHES "mipsel*" OR
|
||||
+ TARGET_ARCH MATCHES "mips64el*")
|
||||
add_definitions(-DPNG_MIPS_MSA_OPT=0)
|
||||
endif()
|
||||
|
||||
@@ -412,19 +412,11 @@ else()
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/scripts/checksym.awk"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.def")
|
||||
|
||||
- add_custom_target(symbol-check
|
||||
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/scripts/symbols.chk")
|
||||
-
|
||||
generate_copy("${CMAKE_CURRENT_BINARY_DIR}/scripts/sym.out"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/libpng.sym")
|
||||
generate_copy("${CMAKE_CURRENT_BINARY_DIR}/scripts/vers.out"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/libpng.vers")
|
||||
|
||||
- add_custom_target(genvers
|
||||
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libpng.vers")
|
||||
- add_custom_target(gensym
|
||||
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libpng.sym")
|
||||
-
|
||||
add_custom_target("genprebuilt"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"-DOUTPUT=scripts/pnglibconf.h.prebuilt"
|
|
@ -0,0 +1,49 @@
|
|||
Some notes about Entities, Entity IDs and the IdList structure
|
||||
==============================================================
|
||||
Sketch entities in SolveSpace are all of the same type without use of language
|
||||
support for polymorphism. The entity class is defined in sketch.h. That class
|
||||
contains an enum for each entity to define its type (line, arc, etc...) and some
|
||||
other members that can be used to store different things depending on the entity
|
||||
type. This means all entities are the same size, so some data may be reference
|
||||
by pointers from the entity (font, extra points, etc...)
|
||||
|
||||
Entities in a sketch are kept in a global array (IdList) referenced by a unique
|
||||
Id (handle) and can be looked up by Id in log(n) time via binary search. In
|
||||
order to use binary search the array must be kept in order sorted by Id. One
|
||||
problem is that insertion takes O(n) time because half the list (on average)
|
||||
must be shifted to make room for a new item.
|
||||
|
||||
The IdList class is a template and is used for more than entities.
|
||||
|
||||
EntityMap:
|
||||
==========
|
||||
Another important structure is the EntityMap and EntityKey defined in sketch.h
|
||||
This is what allows SovleSpace to update groups when earlier groups in the
|
||||
sketch are changed. If a rectangle is extruded to a box and items are
|
||||
constrained to entities on that box, the user can go back to the sketch and
|
||||
modify it. Entities can be added, modified an even deleted. So long as the
|
||||
entities that are later used to build upon are kept the later extrude group will
|
||||
pick up the changes from the 2D sketch and anything build on it will remain.
|
||||
|
||||
The way this works is that each group has a member called remap, which is one of
|
||||
these maps. This is where my understanding is fuzzy. At the end of Group.cpp is
|
||||
a function called Group::CopyEntity() which is used to make new sketch entities
|
||||
when a group is created. These are generally copies of entities in the previous
|
||||
group, but there are exceptions. A point will be used to generate a line when
|
||||
extruding a 2D sketch. A point will also be "copied" to a circle for a Lathe
|
||||
group. For this reason, the entity key is derived by combining its previous key
|
||||
with something often called the CopyNumber or just remap (unfortunate).
|
||||
|
||||
When a group is regenerated (the first time, or after a previous one is
|
||||
modified) entities are copied from the old group to the new one. For Step
|
||||
Translating and Rotating there may be many copies, and the copy number is
|
||||
literally N for the Nth copy except for the last one which gets an enum - it is
|
||||
common to constrain the last item, so it gets a large unique number so that
|
||||
constraints still refer to it if the number of copies changes. When an entity is
|
||||
copied like this a new handle is created unless there is already an entity in
|
||||
Remap that was created the same way. This is how constructions are preserved
|
||||
across underlying changes.
|
||||
|
||||
There are some hard limits used in the hash table for the remap mechanism which
|
||||
limit the number of entities in a group (but not the global sketch).
|
||||
|
|
@ -46,7 +46,7 @@ POINT_N_ROT_TRANS: Rotates a point via quaternion param[3],param[4],param[5],par
|
|||
|
||||
POINT_N_COPY: A non-transformed copy of a point - numeric copy?
|
||||
|
||||
POINT_N_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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
When creating a new entity transformation, the first thing to do is define the
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit e9c3d83d5a04835806287f1e8c0f2d3a962d6673
|
||||
Subproject commit dbe3e0c43e549a1602286144d94b0666549b18e6
|
|
@ -1 +1 @@
|
|||
Subproject commit 27627843648ef84aee1621976f25bee5946e6bda
|
||||
Subproject commit 4e643b6d3178e0ea2a093b7e14fe621631a91e4b
|
|
@ -113,6 +113,10 @@ typedef struct {
|
|||
#define SLVS_C_WHERE_DRAGGED 100031
|
||||
#define SLVS_C_CURVE_CURVE_TANGENT 100032
|
||||
#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 {
|
||||
Slvs_hConstraint h;
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
{
|
||||
"app-id": "com.solvespace.SolveSpace",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "3.30",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"runtime": "org.freedesktop.Platform",
|
||||
"runtime-version": "20.08",
|
||||
"sdk": "org.freedesktop.Sdk",
|
||||
"finish-args": [
|
||||
/* 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 */
|
||||
"--filesystem=home"
|
||||
],
|
||||
"cleanup": [
|
||||
"/include", "/lib/*/include",
|
||||
"*.a", "*.la", "*.m4", "/lib/libslvs*.so*", "/lib/libglibmm_generate_extra_defs*.so*",
|
||||
"/share/pkgconfig", "*.pc",
|
||||
"/share/man", "/share/doc",
|
||||
"/include",
|
||||
"/lib/*/include",
|
||||
"*.a",
|
||||
"*.la",
|
||||
"*.m4",
|
||||
"/lib/libslvs*.so*",
|
||||
"/lib/libglibmm_generate_extra_defs*.so*",
|
||||
"/share/pkgconfig",
|
||||
"*.pc",
|
||||
"/share/man",
|
||||
"/share/doc",
|
||||
"/share/aclocal",
|
||||
/* mm-common junk */
|
||||
"/bin/mm-common-prepare",
|
||||
|
@ -26,8 +36,8 @@
|
|||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/mm-common/0.9/mm-common-0.9.12.tar.xz",
|
||||
"sha256": "ceffdcce1e5b52742884c233ec604bf6fded12eea9da077ce7a62c02c87e7c0b"
|
||||
"url": "https://download.gnome.org/sources/mm-common/1.0/mm-common-1.0.2.tar.xz",
|
||||
"sha256": "a2a99f3fa943cf662f189163ed39a2cfc19a428d906dd4f92b387d3659d1641d"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -39,21 +49,20 @@
|
|||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/libsigc++/2.10/libsigc++-2.10.1.tar.xz",
|
||||
"sha256": "c9a25f26178c6cbb147f9904d8c533b5a5c5111a41ac2eb781eb734eea446003"
|
||||
"url": "https://download.gnome.org/sources/libsigc++/2.10/libsigc%2B%2B-2.10.6.tar.xz",
|
||||
"sha256": "dda176dc4681bda9d5a2ac1bc55273bdd381662b7a6d49e918267d13e8774e1b"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "glibmm",
|
||||
"config-opts": [
|
||||
"--disable-documentation"
|
||||
],
|
||||
"config-opts": [],
|
||||
"buildsystem": "meson",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/glibmm/2.58/glibmm-2.58.1.tar.xz",
|
||||
"sha256": "6e5fe03bdf1e220eeffd543e017fd2fb15bcec9235f0ffd50674aff9362a85f0"
|
||||
"url": "https://download.gnome.org/sources/glibmm/2.64/glibmm-2.64.5.tar.xz",
|
||||
"sha256": "508fc86e2c9141198aa16c225b16fd6b911917c0d3817602652844d0973ea386"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -98,14 +107,13 @@
|
|||
},
|
||||
{
|
||||
"name": "gtkmm",
|
||||
"config-opts": [
|
||||
"--disable-documentation"
|
||||
],
|
||||
"config-opts": [],
|
||||
"buildsystem": "meson",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "http://ftp.gnome.org/pub/GNOME/sources/gtkmm/3.24/gtkmm-3.24.1.tar.xz",
|
||||
"sha256": "ddfe42ed2458a20a34de252854bcf4b52d3f0c671c045f56b42aa27c7542d2fd"
|
||||
"url": "https://download.gnome.org/sources/gtkmm/3.24/gtkmm-3.24.4.tar.xz",
|
||||
"sha256": "9beb71c3e90cfcfb790396b51e3f5e7169966751efd4f3ef9697114be3be6743"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -113,6 +121,7 @@
|
|||
"name": "libjson-c",
|
||||
"sources": [
|
||||
{
|
||||
/* 0.15-nodoc doesn't build */
|
||||
"type": "archive",
|
||||
"url": "https://s3.amazonaws.com/json-c_releases/releases/json-c-0.13.1-nodoc.tar.gz",
|
||||
"sha256": "94a26340c0785fcff4f46ff38609cf84ebcd670df0c8efd75d039cc951d80132"
|
||||
|
@ -125,8 +134,8 @@
|
|||
"name": "SolveSpace",
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"path": "/home/whitequark/Projects/solvespace"
|
||||
"type": "dir",
|
||||
"path": "../.."
|
||||
}
|
||||
],
|
||||
"buildsystem": "cmake",
|
||||
|
|
|
@ -31,7 +31,7 @@ if(WIN32)
|
|||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${source}")
|
||||
endfunction()
|
||||
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)
|
||||
|
||||
function(add_resource name)
|
||||
|
@ -134,6 +134,13 @@ else()
|
|||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications
|
||||
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
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
|
||||
RENAME com.solvespace.SolveSpace-slvs.xml)
|
||||
|
@ -176,6 +183,13 @@ else()
|
|||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freedesktop/solvespace.desktop
|
||||
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
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/mime/packages
|
||||
RENAME solvespace-slvs.xml)
|
||||
|
@ -185,7 +199,7 @@ else()
|
|||
RENAME solvespace.svg)
|
||||
install(FILES freedesktop/solvespace-scalable.svg
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/mimetypes
|
||||
RENAME application.x-solvespace.svg)
|
||||
RENAME application-x-solvespace.svg)
|
||||
|
||||
foreach(SIZE 16x16 24x24 32x32 48x48)
|
||||
install(FILES freedesktop/solvespace-${SIZE}.png
|
||||
|
@ -193,12 +207,7 @@ else()
|
|||
RENAME solvespace.png)
|
||||
install(FILES freedesktop/solvespace-${SIZE}.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}/mimetypes
|
||||
RENAME application.x-solvespace.png)
|
||||
endforeach()
|
||||
|
||||
foreach(SIZE 16x16 24x24 32x32 48x48)
|
||||
install(FILES freedesktop/solvespace-${SIZE}.xpm
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pixmaps)
|
||||
RENAME application-x-solvespace.png)
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
@ -257,6 +266,8 @@ add_resources(
|
|||
locales/en_US.po
|
||||
locales/fr_FR.po
|
||||
locales/uk_UA.po
|
||||
locales/es_AR.po
|
||||
locales/tr_TR.po
|
||||
locales/ru_RU.po
|
||||
locales/zh_CN.po
|
||||
fonts/unifont.hex.gz
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.solvespace.SolveSpace</id>
|
||||
|
||||
<name>SolveSpace</name>
|
||||
<summary>A free (GPLv3) parametric 3d CAD tool</summary>
|
||||
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0-or-later</project_license>
|
||||
<update_contact>ryan_AT_ryanpavlik.com</update_contact>
|
||||
|
||||
<categories>
|
||||
<category>Graphics</category>
|
||||
<category>3DGraphics</category>
|
||||
<category>Engineering</category>
|
||||
</categories>
|
||||
<description>
|
||||
<p>
|
||||
SolveSpace is a free (GPLv3) parametric 3d CAD tool. Applications include:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Modeling 3d parts — draw with extrudes, revolves, and Boolean operations</li>
|
||||
<li>Modeling 2d parts — draw the part as a single section, and export; use 3d assembly to verify fit</li>
|
||||
<li>Modeling 3d-printed parts — export the STL or other triangle mesh expected by most slicers</li>
|
||||
<li>Preparing 2D CAM data — export 2d vector art for a waterjet machine or laser cutter</li>
|
||||
<li>Mechanism design — use the constraint solver to simulate planar or spatial linkages</li>
|
||||
<li>Plane and solid geometry — replace hand-solved trigonometry with a live dimensioned drawing</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="homepage">https://solvespace.com</url>
|
||||
<url type="bugtracker">https://github.com/solvespace/solvespace/issues</url>
|
||||
|
||||
<launchable type="desktop-id">@DESKTOP_FILE_NAME@</launchable>
|
||||
<provides>
|
||||
<mediatype>application/x-solvespace</mediatype>
|
||||
</provides>
|
||||
|
||||
<content_rating type="oars-1.0" />
|
||||
|
||||
<releases>
|
||||
<release version="3.0" date="2021-04-18" type="stable">
|
||||
<description>
|
||||
<p>Major new stable release. Includes new intersection boolean operation,
|
||||
new types of groups, solid model suppression, usability improvements
|
||||
(especially regarding redundant constraints and automatic constraints),
|
||||
and more. Also includes performance and scalability improvements.</p>
|
||||
</description>
|
||||
<url>https://github.com/solvespace/solvespace/releases/tag/v3.0</url>
|
||||
</release>
|
||||
|
||||
<release version="3.0~rc2" date="2021-01-17" type="development">
|
||||
<description>
|
||||
<p>Second release candidate for the 3.0 stable release.</p>
|
||||
</description>
|
||||
<url>https://github.com/solvespace/solvespace/releases/tag/v3.0.rc2</url>
|
||||
</release>
|
||||
|
||||
<release version="3.0~rc1" date="2020-11-18" type="development">
|
||||
<description>
|
||||
<p>First release candidate for the 3.0 stable release.</p>
|
||||
</description>
|
||||
<url>https://github.com/solvespace/solvespace/releases/tag/v3.0.rc1</url>
|
||||
</release>
|
||||
|
||||
<release version="2.3" date="2016-12-23" type="stable">
|
||||
<description>
|
||||
<p>Bug-fix release in the 2.x series, fixing some crashes.</p>
|
||||
</description>
|
||||
<url>https://github.com/solvespace/solvespace/releases/tag/v2.3</url>
|
||||
</release>
|
||||
|
||||
<release version="2.2" date="2016-10-16" type="stable">
|
||||
<description>
|
||||
<p>Bug-fix release, including performance improvements.</p>
|
||||
</description>
|
||||
<url>https://github.com/solvespace/solvespace/releases/tag/v2.2</url>
|
||||
</release>
|
||||
|
||||
<release version="2.1" date="2016-06-11" type="stable">
|
||||
<description>
|
||||
<p>Introduced *nix compatibility, internationalization, technical drawing mode, improved import and export, and other features and fixes.</p>
|
||||
</description>
|
||||
<url>https://github.com/solvespace/solvespace/releases/tag/v2.1</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
</component>
|
|
@ -1,27 +0,0 @@
|
|||
/* XPM */
|
||||
static char *solvespace_16x16[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"16 16 5 1 ",
|
||||
" c black",
|
||||
". c #1ED500",
|
||||
"X c #DE00D6",
|
||||
"o c #CBCBCB",
|
||||
"O c None",
|
||||
/* pixels */
|
||||
"OOO OOOOOOOOOOOO",
|
||||
"OOO OOOOOOOOOOOO",
|
||||
"OOO OOOOOOOOOOOO",
|
||||
"OOO OOOOOXOOOOOO",
|
||||
"OOO OOOOOXoOOOOO",
|
||||
"OOO OOOOOXoOOOOO",
|
||||
"OOO OOOOOXoOOOOO",
|
||||
"OOO OOOOOXoOOOOO",
|
||||
"OOO OOOOOXoOOOOO",
|
||||
"OOO OOXXXXXXXOOO",
|
||||
"OOO OOOoooooooOO",
|
||||
"OO...OOOOOOOOOOO",
|
||||
" ... ",
|
||||
"OO...OOOOOOOOOOO",
|
||||
"OOO OOOOOOOOOOOO",
|
||||
"OOO OOOOOOOOOOOO"
|
||||
};
|
|
@ -1,35 +0,0 @@
|
|||
/* XPM */
|
||||
static char *solvespace_24x24[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"24 24 5 1 ",
|
||||
" c black",
|
||||
". c #1ED500",
|
||||
"X c #DE00D6",
|
||||
"o c #CBCBCB",
|
||||
"O c None",
|
||||
/* pixels */
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOXOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
||||
"OOOOOOO OOOOOXoOOOOOOOOO",
|
||||
"OOOOOOO OOXXXXXXXOOOOOOO",
|
||||
"OOOOOOO OOOoooooooOOOOOO",
|
||||
"OOOOOO...OOOOOOOOOOOOOOO",
|
||||
"OOOO ... OOOO",
|
||||
"OOOOOO...OOOOOOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
||||
"OOOOOOO OOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOO"
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
/* XPM */
|
||||
static char *solvespace_32x32[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"32 32 5 1 ",
|
||||
" c black",
|
||||
". c #1ED500",
|
||||
"X c #DE00D6",
|
||||
"o c #CBCBCB",
|
||||
"O c None",
|
||||
/* pixels */
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOXXoOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
|
||||
"OOOOOO OOOOXXXXXXXXXXXXOOOOOOOO",
|
||||
"OOOOOO OOOOOooooooooooooOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
||||
" ...... ",
|
||||
" ...... ",
|
||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOO......OOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOO OOOOOOOOOOOOOOOOOOOOOOOO"
|
||||
};
|
|
@ -1,59 +0,0 @@
|
|||
/* XPM */
|
||||
static char *solvespace_48x48[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"48 48 5 1 ",
|
||||
" c black",
|
||||
". c #1ED500",
|
||||
"X c #DE00D6",
|
||||
"o c #CBCBCB",
|
||||
"O c None",
|
||||
/* pixels */
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOXXoOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOXXXXXXXXXXXXOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOooooooooooooOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOO ...... OOOOOOOO",
|
||||
"OOOOOOOO ...... OOOOOOOO",
|
||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOO......OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO",
|
||||
"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO"
|
||||
};
|
|
@ -6,5 +6,5 @@ Exec=${CMAKE_INSTALL_FULL_BINDIR}/solvespace
|
|||
MimeType=application/x-solvespace
|
||||
Icon=com.solvespace.SolveSpace
|
||||
Type=Application
|
||||
Categories=Graphics
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=parametric;cad;2d;3d;
|
||||
|
|
|
@ -6,5 +6,5 @@ Exec=solvespace
|
|||
MimeType=application/x-solvespace
|
||||
Icon=${SNAP}/meta/icons/hicolor/scalable/apps/snap.solvespace.svg
|
||||
Type=Application
|
||||
Categories=Graphics
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=parametric;cad;2d;3d;
|
||||
|
|
|
@ -6,5 +6,5 @@ Exec=${CMAKE_INSTALL_FULL_BINDIR}/solvespace
|
|||
MimeType=application/x-solvespace
|
||||
Icon=solvespace
|
||||
Type=Application
|
||||
Categories=Graphics
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=parametric;cad;2d;3d;
|
||||
|
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 216 B |
Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 260 B |
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 264 B |
|
@ -3,6 +3,8 @@
|
|||
de-DE,0407,Deutsch
|
||||
en-US,0409,English (US)
|
||||
fr-FR,040C,Français
|
||||
es-AR,2C0A,español (AR)
|
||||
ru-RU,0419,Русский
|
||||
tr-TR,041F,Türkçe
|
||||
uk-UA,0422,Українська
|
||||
zh-CN,0804,简体中文
|
||||
|
|
1137
res/locales/uk_UA.po
659
res/messages.pot
|
@ -117,6 +117,7 @@ if(WIN32)
|
|||
${SPACEWARE_LIBRARIES})
|
||||
elseif(APPLE)
|
||||
add_compile_options(
|
||||
-DGL_SILENCE_DEPRECATION
|
||||
-fobjc-arc)
|
||||
|
||||
list(APPEND platform_SOURCES
|
||||
|
@ -176,6 +177,7 @@ set(solvespace_core_SOURCES
|
|||
groupmesh.cpp
|
||||
importdxf.cpp
|
||||
importidf.cpp
|
||||
importmesh.cpp
|
||||
mesh.cpp
|
||||
modify.cpp
|
||||
mouse.cpp
|
||||
|
@ -338,7 +340,10 @@ if(ENABLE_GUI)
|
|||
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
|
||||
elseif(APPLE)
|
||||
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()
|
||||
|
||||
|
@ -401,17 +406,30 @@ endif()
|
|||
# solvespace macOS package
|
||||
|
||||
if(APPLE)
|
||||
set(bundle SolveSpace)
|
||||
set(bundle_bin ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/MacOS)
|
||||
set(bundle_resources ${EXECUTABLE_OUTPUT_PATH}/${bundle}.app/Contents/Resources/lib)
|
||||
execute_process(
|
||||
COMMAND mkdir -p ${bundle_resources}
|
||||
COMMAND cp -p /usr/local/opt/libomp/lib/libomp.dylib ${bundle_resources}/libomp.dylib
|
||||
)
|
||||
set(LIBOMP_LIB_PATH ${OpenMP_CXX_INCLUDE_DIRS}/../lib/libomp.dylib)
|
||||
set(LIBOMP_LINK_PATH "@executable_path/../Resources/libomp.dylib")
|
||||
set(LIBOMP_LINK_PATH_UTILS "@executable_path/SolveSpace.app/Contents/Resources/libomp.dylib")
|
||||
if(ENABLE_GUI)
|
||||
add_custom_command(TARGET solvespace POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${bundle_bin}
|
||||
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}
|
||||
COMMAND cp -r ${CMAKE_BINARY_DIR}/Resources $<TARGET_BUNDLE_CONTENT_DIR:solvespace>
|
||||
)
|
||||
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"
|
||||
VERBATIM)
|
||||
endif()
|
||||
endif()
|
|
@ -138,18 +138,17 @@ void GraphicsWindow::CopySelection() {
|
|||
}
|
||||
}
|
||||
|
||||
Constraint *c;
|
||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
||||
if(!SS.clipboard.ContainsEntity(c->ptA) ||
|
||||
!SS.clipboard.ContainsEntity(c->ptB) ||
|
||||
!SS.clipboard.ContainsEntity(c->entityA) ||
|
||||
!SS.clipboard.ContainsEntity(c->entityB) ||
|
||||
!SS.clipboard.ContainsEntity(c->entityC) ||
|
||||
!SS.clipboard.ContainsEntity(c->entityD) ||
|
||||
c->type == Constraint::Type::COMMENT) {
|
||||
for(Constraint &c : SK.constraint) {
|
||||
if(!SS.clipboard.ContainsEntity(c.ptA) ||
|
||||
!SS.clipboard.ContainsEntity(c.ptB) ||
|
||||
!SS.clipboard.ContainsEntity(c.entityA) ||
|
||||
!SS.clipboard.ContainsEntity(c.entityB) ||
|
||||
!SS.clipboard.ContainsEntity(c.entityC) ||
|
||||
!SS.clipboard.ContainsEntity(c.entityD) ||
|
||||
c.type == Constraint::Type::COMMENT) {
|
||||
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::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(c.type == Constraint::Type::HORIZONTAL) {
|
||||
c.type = Constraint::Type::VERTICAL;
|
||||
|
|
|
@ -9,24 +9,6 @@
|
|||
#include <omp.h>
|
||||
#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) {
|
||||
SS.TW.ShowEditControlWithColorPicker(13, SS.modelColor[v]);
|
||||
|
||||
|
@ -58,13 +40,8 @@ void TextWindow::ScreenChangeExportMaxSegments(int link, uint32_t v) {
|
|||
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) {
|
||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.gridSpacing));
|
||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.gridSpacing, true));
|
||||
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) {
|
||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.exportOffset));
|
||||
SS.TW.ShowEditControl(3, SS.MmToString(SS.exportOffset, true));
|
||||
SS.TW.edit.meaning = Edit::EXPORT_OFFSET;
|
||||
}
|
||||
|
||||
|
@ -171,7 +148,7 @@ void TextWindow::ScreenChangeCanvasSize(int link, uint32_t v) {
|
|||
}
|
||||
int col = 13;
|
||||
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.i = v;
|
||||
}
|
||||
|
@ -181,7 +158,12 @@ void TextWindow::ScreenChangeGCodeParameter(int link, uint32_t v) {
|
|||
switch(link) {
|
||||
case 'd':
|
||||
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;
|
||||
|
||||
case 's':
|
||||
|
@ -191,12 +173,12 @@ void TextWindow::ScreenChangeGCodeParameter(int link, uint32_t v) {
|
|||
|
||||
case 'F':
|
||||
SS.TW.edit.meaning = Edit::G_CODE_FEED;
|
||||
buf += SS.MmToString(SS.gCode.feed);
|
||||
buf += SS.MmToString(SS.gCode.feed, true);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
SS.TW.edit.meaning = Edit::G_CODE_PLUNGE_FEED;
|
||||
buf += SS.MmToString(SS.gCode.plungeFeed);
|
||||
buf += SS.MmToString(SS.gCode.plungeFeed, true);
|
||||
break;
|
||||
}
|
||||
SS.TW.ShowEditControl(14, buf);
|
||||
|
@ -227,20 +209,6 @@ void TextWindow::ShowConfiguration() {
|
|||
&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, "%Ft chord tolerance (in percents)%E");
|
||||
Printf(false, "%Ba %@ %% %Fl%Ll%f%D[change]%E; %@ mm, %d triangles",
|
||||
|
@ -262,11 +230,6 @@ void TextWindow::ShowConfiguration() {
|
|||
SS.exportMaxSegments,
|
||||
&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, "%Ba %s %Fl%Ll%f%D[change]%E",
|
||||
SS.MmToString(SS.gridSpacing).c_str(),
|
||||
|
@ -461,6 +424,11 @@ bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
|
|||
SS.GW.Invalidate();
|
||||
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: {
|
||||
int v = atoi(s.c_str());
|
||||
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);
|
||||
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: {
|
||||
Expr *e = Expr::From(s, /*popUpError=*/true);
|
||||
if(e) SS.gCode.passes = (int)(e->Eval());
|
||||
|
|
|
@ -22,7 +22,11 @@ std::string Constraint::DescriptionString() const {
|
|||
case Type::EQ_LEN_PT_LINE_D: s = C_("constr-name", "eq-length-and-pt-ln-dist"); break;
|
||||
case Type::EQ_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::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::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_HORIZ: s = C_("constr-name", "symmetric-h"); 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.entityA = gs.entity[0];
|
||||
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 {
|
||||
Error(_("Bad selection for length ratio constraint. This "
|
||||
"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;
|
||||
}
|
||||
|
||||
|
@ -401,10 +422,27 @@ void Constraint::MenuConstrain(Command id) {
|
|||
c.type = Type::LENGTH_DIFFERENCE;
|
||||
c.entityA = gs.entity[0];
|
||||
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 {
|
||||
Error(_("Bad selection for length difference constraint. This "
|
||||
"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;
|
||||
}
|
||||
|
||||
|
@ -767,10 +805,19 @@ void Constraint::MenuConstrain(Command id) {
|
|||
break;
|
||||
|
||||
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.command = Command::COMMENT;
|
||||
SS.GW.pending.description = _("click center of comment text");
|
||||
SS.ScheduleShowTW();
|
||||
}
|
||||
break;
|
||||
|
||||
default: ssassert(false, "Unexpected menu ID");
|
||||
|
|
|
@ -18,7 +18,11 @@ bool ConstraintBase::HasLabel() const {
|
|||
case Type::PROJ_PT_DISTANCE:
|
||||
case Type::DIAMETER:
|
||||
case Type::LENGTH_RATIO:
|
||||
case Type::ARC_ARC_LEN_RATIO:
|
||||
case Type::ARC_LINE_LEN_RATIO:
|
||||
case Type::LENGTH_DIFFERENCE:
|
||||
case Type::ARC_ARC_DIFFERENCE:
|
||||
case Type::ARC_LINE_DIFFERENCE:
|
||||
case Type::ANGLE:
|
||||
case Type::COMMENT:
|
||||
return true;
|
||||
|
@ -39,7 +43,11 @@ bool ConstraintBase::IsProjectible() const {
|
|||
case Type::EQ_PT_LN_DISTANCES:
|
||||
case Type::EQUAL_ANGLE:
|
||||
case Type::LENGTH_RATIO:
|
||||
case Type::ARC_ARC_LEN_RATIO:
|
||||
case Type::ARC_LINE_LEN_RATIO:
|
||||
case Type::LENGTH_DIFFERENCE:
|
||||
case Type::ARC_ARC_DIFFERENCE:
|
||||
case Type::ARC_LINE_DIFFERENCE:
|
||||
case Type::SYMMETRIC:
|
||||
case Type::SYMMETRIC_HORIZ:
|
||||
case Type::SYMMETRIC_VERT:
|
||||
|
@ -335,6 +343,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
|
|||
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: {
|
||||
EntityBase *a = SK.GetEntity(entityA);
|
||||
EntityBase *b = SK.GetEntity(entityB);
|
||||
|
@ -344,6 +456,110 @@ void ConstraintBase::GenerateEquations(IdList<Equation,hEquation> *l,
|
|||
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: {
|
||||
EntityBase *circle = SK.GetEntity(entityA);
|
||||
Expr *r = circle->CircleGetRadiusExpr();
|
||||
|
|
17
src/draw.cpp
|
@ -210,16 +210,15 @@ void GraphicsWindow::SelectByMarquee() {
|
|||
BBox marqueeBBox = BBox::From(Vector::From(marqueePoint.x, marqueePoint.y, VERY_NEGATIVE),
|
||||
Vector::From(orig.mouse.x, orig.mouse.y, VERY_POSITIVE));
|
||||
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group != SS.GW.activeGroup) continue;
|
||||
if(e->IsFace() || e->IsDistance()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
for(Entity &e : SK.entity) {
|
||||
if(e.group != SS.GW.activeGroup) continue;
|
||||
if(e.IsFace() || e.IsDistance()) continue;
|
||||
if(!e.IsVisible()) continue;
|
||||
|
||||
bool entityHasBBox;
|
||||
BBox entityBBox = e->GetOrGenerateScreenBBox(&entityHasBBox);
|
||||
BBox entityBBox = e.GetOrGenerateScreenBBox(&entityHasBBox);
|
||||
if(entityHasBBox && entityBBox.Overlaps(marqueeBBox)) {
|
||||
MakeSelected(e->h);
|
||||
MakeSelected(e.h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -412,8 +411,8 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
|
|||
cached.projRight = projRight;
|
||||
cached.projUp = projUp;
|
||||
cached.scale = scale;
|
||||
for(Entity *e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
e->screenBBoxValid = false;
|
||||
for(Entity &e : SK.entity) {
|
||||
e.screenBBoxValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ std::string Constraint::Label() const {
|
|||
std::string result;
|
||||
if(type == Type::ANGLE) {
|
||||
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);
|
||||
} else if(type == Type::COMMENT) {
|
||||
result = comment;
|
||||
|
@ -267,7 +267,7 @@ void Constraint::DoEqualRadiusTicks(Canvas *canvas, Canvas::hStroke hcs,
|
|||
const Camera &camera = canvas->GetCamera();
|
||||
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();
|
||||
Quaternion q = circ->Normal()->NormalGetNum();
|
||||
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,
|
||||
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();
|
||||
double pixels = 1.0 / camera.scale;
|
||||
|
@ -305,6 +306,9 @@ void Constraint::DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
|||
db = db.ProjectVectorInto(workplane);
|
||||
}
|
||||
|
||||
a0 = a0.Plus(explodeOffset);
|
||||
b0 = b0.Plus(explodeOffset);
|
||||
|
||||
Vector a1 = a0.Plus(da);
|
||||
Vector b1 = b0.Plus(db);
|
||||
|
||||
|
@ -534,6 +538,15 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
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);
|
||||
if(refs) refs->push_back(ref);
|
||||
|
||||
|
@ -548,6 +561,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
dp = (bp.Minus(ap)),
|
||||
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);
|
||||
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_PLANE_DISTANCE: {
|
||||
Vector pt = SK.GetEntity(ptA)->PointGetNum();
|
||||
Vector pt = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||
Entity *enta = SK.GetEntity(entityA);
|
||||
Vector n, p;
|
||||
if(type == Type::PT_PLANE_DISTANCE) {
|
||||
|
@ -590,7 +616,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
}
|
||||
|
||||
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);
|
||||
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
|
||||
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
|
||||
|
@ -602,6 +629,19 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
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
|
||||
Vector closest = pt.ClosestPointOnLine(lA, dl);
|
||||
|
||||
|
@ -655,7 +695,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
|
||||
case Type::DIAMETER: {
|
||||
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();
|
||||
Vector n = q.RotationN().WithMagnitude(1);
|
||||
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 d = camera.projUp.ScaledBy((2-a)/camera.scale);
|
||||
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);
|
||||
canvas->DrawQuad(p.Plus (r).Plus (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_IN_PLANE: {
|
||||
double s = 8/camera.scale;
|
||||
Vector p = SK.GetEntity(ptA)->PointGetNum();
|
||||
Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||
if(refs) refs->push_back(p);
|
||||
Vector r, d;
|
||||
if(type == Type::PT_ON_FACE) {
|
||||
|
@ -740,7 +780,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
}
|
||||
|
||||
case Type::WHERE_DRAGGED: {
|
||||
Vector p = SK.GetEntity(ptA)->PointGetNum();
|
||||
Vector p = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||
if(refs) refs->push_back(p);
|
||||
Vector u = p.Plus(gu.WithMagnitude(8/camera.scale)).Plus(
|
||||
gr.WithMagnitude(8/camera.scale)),
|
||||
|
@ -797,10 +837,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return;
|
||||
|
@ -820,7 +860,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
}
|
||||
|
||||
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);
|
||||
if(refs) refs->push_back(ref);
|
||||
return;
|
||||
|
@ -855,7 +895,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
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);
|
||||
DoLine(canvas, hcs, s, s.Plus(v));
|
||||
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) {
|
||||
Entity *arc = SK.GetEntity(entityA);
|
||||
Entity *norm = SK.GetEntity(arc->normal);
|
||||
Vector c = SK.GetEntity(arc->point[0])->PointGetNum();
|
||||
Vector c = SK.GetEntity(arc->point[0])->PointGetDrawNum();
|
||||
Vector p =
|
||||
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetNum();
|
||||
SK.GetEntity(arc->point[other ? 2 : 1])->PointGetDrawNum();
|
||||
Vector r = p.Minus(c);
|
||||
textAt = p.Plus(r.WithMagnitude(14/camera.scale));
|
||||
u = norm->NormalU();
|
||||
|
@ -896,6 +936,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
Entity *cubic = SK.GetEntity(entityA);
|
||||
Vector p = other ? cubic->CubicGetFinishNum() :
|
||||
cubic->CubicGetStartNum();
|
||||
p = p.Plus(cubic->ExplodeOffset());
|
||||
Vector dir = SK.GetEntity(entityB)->VectorGetNum();
|
||||
Vector out = n.Cross(dir);
|
||||
textAt = p.Plus(out.WithMagnitude(14/camera.scale));
|
||||
|
@ -905,12 +946,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
u = wn->NormalU();
|
||||
v = wn->NormalV();
|
||||
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
|
||||
// at the shared point, which could be from either a cubic
|
||||
// or an arc.
|
||||
if(other) {
|
||||
textAt = eA->EndpointFinish();
|
||||
textAt = eA->EndpointFinish().Plus(eA->ExplodeOffset());
|
||||
if(eA->type == Entity::Type::CUBIC) {
|
||||
dir = eA->CubicGetFinishTangentNum();
|
||||
} else {
|
||||
|
@ -919,7 +960,7 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
dir = n.Cross(dir);
|
||||
}
|
||||
} else {
|
||||
textAt = eA->EndpointStart();
|
||||
textAt = eA->EndpointStart().Plus(eA->ExplodeOffset());
|
||||
if(eA->type == Entity::Type::CUBIC) {
|
||||
dir = eA->CubicGetStartTangentNum();
|
||||
} else {
|
||||
|
@ -947,6 +988,10 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
Vector u = (gn.Cross(n)).WithMagnitude(4/camera.scale);
|
||||
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.Minus(u), p.Minus(u).Plus(n));
|
||||
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);
|
||||
Vector ref;
|
||||
DoEqualLenTicks(canvas, hcs,
|
||||
SK.GetEntity(line->point[0])->PointGetNum(),
|
||||
SK.GetEntity(line->point[1])->PointGetNum(),
|
||||
SK.GetEntity(line->point[0])->PointGetDrawNum(),
|
||||
SK.GetEntity(line->point[1])->PointGetDrawNum(),
|
||||
gn, &ref);
|
||||
if(refs) refs->push_back(ref);
|
||||
DoEqualRadiusTicks(canvas, hcs, entityB, &ref);
|
||||
|
@ -990,6 +1035,12 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
DoProjectedPoint(canvas, hcs, &b);
|
||||
}
|
||||
|
||||
if(ShouldDrawExploded()) {
|
||||
Vector offset = e->ExplodeOffset();
|
||||
a = a.Plus(offset);
|
||||
b = b.Plus(offset);
|
||||
}
|
||||
|
||||
Vector ref;
|
||||
DoEqualLenTicks(canvas, hcs, a, b, gn, &ref);
|
||||
if(refs) refs->push_back(ref);
|
||||
|
@ -1000,6 +1051,41 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
}
|
||||
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: {
|
||||
Entity *forLen = SK.GetEntity(entityA);
|
||||
|
@ -1009,6 +1095,11 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
DoProjectedPoint(canvas, hcs, &a);
|
||||
DoProjectedPoint(canvas, hcs, &b);
|
||||
}
|
||||
if(ShouldDrawExploded()) {
|
||||
Vector offset = forLen->ExplodeOffset();
|
||||
a = a.Plus(offset);
|
||||
b = b.Plus(offset);
|
||||
}
|
||||
Vector refa;
|
||||
DoEqualLenTicks(canvas, hcs, a, b, gn, &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));
|
||||
if(ShouldDrawExploded()) {
|
||||
Vector offset = SK.GetEntity(ptA)->ExplodeOffset();
|
||||
pt = pt.Plus(offset);
|
||||
closest = closest.Plus(offset);
|
||||
}
|
||||
DoLine(canvas, hcs, pt, closest);
|
||||
Vector 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));
|
||||
if(ShouldDrawExploded()) {
|
||||
Vector offset = pte->ExplodeOffset();
|
||||
pt = pt.Plus(offset);
|
||||
closest = closest.Plus(offset);
|
||||
}
|
||||
DoLine(canvas, hcs, pt, closest);
|
||||
|
||||
Vector ref;
|
||||
|
@ -1075,8 +1176,8 @@ void Constraint::DoLayout(DrawAs how, Canvas *canvas,
|
|||
goto s;
|
||||
}
|
||||
s:
|
||||
Vector a = SK.GetEntity(ptA)->PointGetNum();
|
||||
Vector b = SK.GetEntity(ptB)->PointGetNum();
|
||||
Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||
Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
|
||||
|
||||
for(int i = 0; i < 2; i++) {
|
||||
Vector tail = (i == 0) ? a : b;
|
||||
|
@ -1113,8 +1214,8 @@ s:
|
|||
}
|
||||
// For "at midpoint", this branch is always taken.
|
||||
Entity *e = SK.GetEntity(entityA);
|
||||
Vector a = SK.GetEntity(e->point[0])->PointGetNum();
|
||||
Vector b = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
Vector a = SK.GetEntity(e->point[0])->PointGetDrawNum();
|
||||
Vector b = SK.GetEntity(e->point[1])->PointGetDrawNum();
|
||||
Vector m = (a.ScaledBy(0.5)).Plus(b.ScaledBy(0.5));
|
||||
Vector offset = (a.Minus(b)).Cross(n);
|
||||
offset = offset.WithMagnitude(textHeight);
|
||||
|
@ -1138,8 +1239,8 @@ s:
|
|||
r.WithMagnitude(1), u.WithMagnitude(1), hcs);
|
||||
if(refs) refs->push_back(o);
|
||||
} else {
|
||||
Vector a = SK.GetEntity(ptA)->PointGetNum();
|
||||
Vector b = SK.GetEntity(ptB)->PointGetNum();
|
||||
Vector a = SK.GetEntity(ptA)->PointGetDrawNum();
|
||||
Vector b = SK.GetEntity(ptB)->PointGetDrawNum();
|
||||
|
||||
Entity *w = SK.GetEntity(workplane);
|
||||
Vector cu = w->Normal()->NormalU();
|
||||
|
@ -1189,8 +1290,13 @@ s:
|
|||
}
|
||||
hcs = canvas->GetStroke(stroke);
|
||||
}
|
||||
DoLabel(canvas, hcs, disp.offset, labelPos, u, v);
|
||||
if(refs) refs->push_back(disp.offset);
|
||||
Vector ref = 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;
|
||||
}
|
||||
}
|
||||
|
@ -1238,7 +1344,11 @@ bool Constraint::HasLabel() const {
|
|||
case Type::PT_FACE_DISTANCE:
|
||||
case Type::PROJ_PT_DISTANCE:
|
||||
case Type::LENGTH_RATIO:
|
||||
case Type::ARC_ARC_LEN_RATIO:
|
||||
case Type::ARC_LINE_LEN_RATIO:
|
||||
case Type::LENGTH_DIFFERENCE:
|
||||
case Type::ARC_ARC_DIFFERENCE:
|
||||
case Type::ARC_LINE_DIFFERENCE:
|
||||
case Type::DIAMETER:
|
||||
case Type::ANGLE:
|
||||
return true;
|
||||
|
@ -1247,3 +1357,7 @@ bool Constraint::HasLabel() const {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Constraint::ShouldDrawExploded() const {
|
||||
return SK.GetGroup(group)->ShouldDrawExploded();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ void Entity::GenerateEdges(SEdgeList *el) {
|
|||
List<Vector> lv = {};
|
||||
sb->MakePwlInto(&lv);
|
||||
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();
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
|
|||
case Type::POINT_N_ROT_AXIS_TRANS:
|
||||
case Type::POINT_IN_3D:
|
||||
case Type::POINT_IN_2D:
|
||||
refs->push_back(PointGetNum());
|
||||
refs->push_back(PointGetDrawNum());
|
||||
break;
|
||||
|
||||
case Type::NORMAL_N_COPY:
|
||||
|
@ -103,12 +103,12 @@ void Entity::GetReferencePoints(std::vector<Vector> *refs) {
|
|||
case Type::CUBIC_PERIODIC:
|
||||
case Type::TTF_TEXT:
|
||||
case Type::IMAGE:
|
||||
refs->push_back(SK.GetEntity(point[0])->PointGetNum());
|
||||
refs->push_back(SK.GetEntity(point[0])->PointGetDrawNum());
|
||||
break;
|
||||
|
||||
case Type::LINE_SEGMENT: {
|
||||
Vector a = SK.GetEntity(point[0])->PointGetNum(),
|
||||
b = SK.GetEntity(point[1])->PointGetNum();
|
||||
Vector a = SK.GetEntity(point[0])->PointGetDrawNum(),
|
||||
b = SK.GetEntity(point[1])->PointGetDrawNum();
|
||||
refs->push_back(b.Plus(a.Minus(b).ScaledBy(0.5)));
|
||||
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) {
|
||||
if(!(how == DrawAs::HOVERED || how == DrawAs::SELECTED) &&
|
||||
!IsVisible()) return;
|
||||
|
@ -557,16 +577,17 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
|||
pointStroke.unit = Canvas::Unit::PX;
|
||||
Canvas::hStroke hcsPoint = canvas->GetStroke(pointStroke);
|
||||
|
||||
Vector p = PointGetDrawNum();
|
||||
if(free) {
|
||||
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
|
||||
analyzeStroke.width = 14.0;
|
||||
analyzeStroke.layer = Canvas::Layer::FRONT;
|
||||
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
|
||||
|
||||
canvas->DrawPoint(PointGetNum(), hcsAnalyze);
|
||||
canvas->DrawPoint(p, hcsAnalyze);
|
||||
}
|
||||
|
||||
canvas->DrawPoint(PointGetNum(), hcsPoint);
|
||||
canvas->DrawPoint(p, hcsPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -621,7 +642,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
|||
tail = camera.projRight.ScaledBy(w/s).Plus(
|
||||
camera.projUp. ScaledBy(h/s)).Minus(camera.offset);
|
||||
} else {
|
||||
tail = SK.GetEntity(point[0])->PointGetNum();
|
||||
tail = SK.GetEntity(point[0])->PointGetDrawNum();
|
||||
}
|
||||
tail = camera.AlignToPixelGrid(tail);
|
||||
|
||||
|
@ -709,8 +730,32 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
|||
case Type::TTF_TEXT: {
|
||||
// Generate the rational polynomial curves, then piecewise linearize
|
||||
// them, and display those.
|
||||
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcs)) {
|
||||
canvas->DrawEdges(*GetOrGenerateEdges(), hcs);
|
||||
// Calculating the draw offset, if necessary.
|
||||
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) {
|
||||
Entity *dist = SK.GetEntity(distance);
|
||||
|
@ -720,12 +765,14 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
|||
Canvas::Stroke analyzeStroke = Style::Stroke(Style::ANALYZE);
|
||||
analyzeStroke.layer = Canvas::Layer::FRONT;
|
||||
Canvas::hStroke hcsAnalyze = canvas->GetStroke(analyzeStroke);
|
||||
if(!canvas->DrawBeziers(*GetOrGenerateBezierCurves(), hcsAnalyze)) {
|
||||
canvas->DrawEdges(*GetOrGenerateEdges(), hcsAnalyze);
|
||||
if(!canvas->DrawBeziers(*beziers, hcsAnalyze)) {
|
||||
canvas->DrawEdges(*edges, hcsAnalyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
offsetBeziers.Clear();
|
||||
offsetEdges.Clear();
|
||||
return;
|
||||
}
|
||||
case Type::IMAGE: {
|
||||
|
@ -757,7 +804,7 @@ void Entity::Draw(DrawAs how, Canvas *canvas) {
|
|||
Canvas::hFill hf = canvas->GetFill(fill);
|
||||
Vector v[4] = {};
|
||||
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 iv = v[1].Minus(v[0]);
|
||||
|
|
258
src/dsc.h
|
@ -10,6 +10,7 @@
|
|||
#include "solvespace.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
/// Trait indicating which types are handle types and should get the associated operators.
|
||||
/// 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
|
||||
template <class T, class H>
|
||||
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
|
||||
|
@ -387,148 +401,152 @@ struct CompareId {
|
|||
// id.
|
||||
template <class T, class H>
|
||||
class IdList {
|
||||
T *elem = nullptr;
|
||||
int elemsAllocated = 0;
|
||||
std::vector<T> elemstore;
|
||||
std::vector<int> elemidx;
|
||||
std::vector<int> freelist;
|
||||
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>;
|
||||
|
||||
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 {
|
||||
return n == 0;
|
||||
}
|
||||
|
||||
void AllocForOneMore() {
|
||||
if(n >= elemsAllocated) {
|
||||
ReserveMore((elemsAllocated + 32)*2 - n);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MaximumId() {
|
||||
if(IsEmpty()) {
|
||||
return 0;
|
||||
} else {
|
||||
return Last()->h.v;
|
||||
return elemstore[elemidx.back()].h.v;
|
||||
}
|
||||
}
|
||||
|
||||
H AddAndAssignId(T *t) {
|
||||
t->h.v = (MaximumId() + 1);
|
||||
AllocForOneMore();
|
||||
|
||||
// Copy-construct at the end of the list.
|
||||
new(&elem[n]) T(*t);
|
||||
// Add at the end of the list.
|
||||
elemstore.push_back(*t);
|
||||
elemidx.push_back(elemstore.size()-1);
|
||||
++n;
|
||||
|
||||
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) {
|
||||
if(n + howMuch > elemsAllocated) {
|
||||
elemsAllocated = n + howMuch;
|
||||
T *newElem = (T *)::operator new[]((size_t)elemsAllocated*sizeof(T));
|
||||
for(int i = 0; i < n; i++) {
|
||||
new(&newElem[i]) T(std::move(elem[i]));
|
||||
elem[i].~T();
|
||||
}
|
||||
::operator delete[](elem);
|
||||
elem = newElem;
|
||||
}
|
||||
elemstore.reserve(elemstore.size() + howMuch);
|
||||
elemidx.reserve(elemidx.size() + howMuch);
|
||||
// freelist.reserve(freelist.size() + howMuch); // PAR@@@@ maybe we should - not much more RAM
|
||||
}
|
||||
|
||||
void Add(T *t) {
|
||||
AllocForOneMore();
|
||||
|
||||
// Look to see if we already have something with the same handle value.
|
||||
ssassert(FindByIdNoOops(t->h) == nullptr, "Handle isn't unique");
|
||||
|
||||
// Copy-construct at the end of the list.
|
||||
new(&elem[n]) T(*t);
|
||||
// Find out where the added element should be.
|
||||
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;
|
||||
// The item we just added is trivially sorted, so "merge"
|
||||
std::inplace_merge(begin(), end() - 1, end(), Compare());
|
||||
}
|
||||
|
||||
T *FindById(H h) {
|
||||
T *t = FindByIdNoOops(h);
|
||||
ssassert(t != NULL, "Cannot find handle");
|
||||
ssassert(t != nullptr, "Cannot find handle");
|
||||
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) {
|
||||
if(IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto it = LowerBound(h);
|
||||
if (it == nullptr || it == end()) {
|
||||
auto it = std::lower_bound(elemidx.begin(), elemidx.end(), h, Compare(this));
|
||||
if(it == elemidx.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
if(elemstore[*it].h.v != h.v) {
|
||||
return nullptr;
|
||||
}
|
||||
if (it->h.v == h.v) {
|
||||
return it;
|
||||
return &elemstore[*it];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T *First() {
|
||||
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 &Get(size_t i) { return elemstore[elemidx[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]; }
|
||||
T *end() { return IsEmpty() ? nullptr : &elem[0] + n; }
|
||||
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(); }
|
||||
iterator begin() { return IsEmpty() ? nullptr : iterator(this); }
|
||||
iterator end() { return IsEmpty() ? nullptr : iterator(this, elemidx.size()); }
|
||||
|
||||
void ClearTags() {
|
||||
for(auto &elt : *this) { elt.tag = 0; }
|
||||
|
@ -545,22 +563,23 @@ public:
|
|||
int src, dest;
|
||||
dest = 0;
|
||||
for(src = 0; src < n; src++) {
|
||||
if(elem[src].tag) {
|
||||
if(elemstore[elemidx[src]].tag) {
|
||||
// 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 {
|
||||
if(src != dest) {
|
||||
elem[dest] = elem[src];
|
||||
elemidx[dest] = elemidx[src];
|
||||
}
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
for(int i = dest; i < n; i++)
|
||||
elem[i].~T();
|
||||
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();
|
||||
FindById(h)->tag = 1;
|
||||
RemoveTagged();
|
||||
|
@ -568,28 +587,35 @@ public:
|
|||
|
||||
void MoveSelfInto(IdList<T,H> *l) {
|
||||
l->Clear();
|
||||
std::swap(l->elem, elem);
|
||||
std::swap(l->elemsAllocated, elemsAllocated);
|
||||
std::swap(l->elemstore, elemstore);
|
||||
std::swap(l->elemidx, elemidx);
|
||||
std::swap(l->freelist, freelist);
|
||||
std::swap(l->n, n);
|
||||
}
|
||||
|
||||
void DeepCopyInto(IdList<T,H> *l) {
|
||||
l->Clear();
|
||||
l->elem = (T *)::operator new[](elemsAllocated * sizeof(elem[0]));
|
||||
for(int i = 0; i < n; i++)
|
||||
new(&l->elem[i]) T(elem[i]);
|
||||
l->elemsAllocated = elemsAllocated;
|
||||
|
||||
for(auto const &it : elemstore) {
|
||||
l->elemstore.push_back(it);
|
||||
}
|
||||
|
||||
for(auto const &it : elemidx) {
|
||||
l->elemidx.push_back(it);
|
||||
}
|
||||
|
||||
l->n = n;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
for(int i = 0; i < n; i++) {
|
||||
elem[i].Clear();
|
||||
elem[i].~T();
|
||||
for(auto &it : elemidx) {
|
||||
elemstore[it].Clear();
|
||||
// elemstore[it].~T(); // clear below calls the destructors
|
||||
}
|
||||
if(elem) ::operator delete[](elem);
|
||||
elem = NULL;
|
||||
elemsAllocated = n = 0;
|
||||
freelist.clear();
|
||||
elemidx.clear();
|
||||
elemstore.clear();
|
||||
n = 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -366,9 +366,9 @@ void SolveSpaceUI::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *s
|
|||
|
||||
// And calculate lighting for the triangle
|
||||
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[1])*(n.Dot(l1)));
|
||||
max(0.0, (SS.lightIntensity[1])*(n.Dot(l1))));
|
||||
double r = min(1.0, tt.meta.color.redF() * lighting),
|
||||
g = min(1.0, tt.meta.color.greenF() * lighting),
|
||||
b = min(1.0, tt.meta.color.blueF() * lighting);
|
||||
|
|
|
@ -353,22 +353,21 @@ void StepFileWriter::ExportSurfacesTo(const Platform::Path &filename) {
|
|||
|
||||
advancedFaces = {};
|
||||
|
||||
SSurface *ss;
|
||||
for(ss = shell->surface.First(); ss; ss = shell->surface.NextAfter(ss)) {
|
||||
if(ss->trim.IsEmpty())
|
||||
for(SSurface &ss : shell->surface) {
|
||||
if(ss.trim.IsEmpty())
|
||||
continue;
|
||||
|
||||
// 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
|
||||
// the piecewise linearization of those loops in xyz space.
|
||||
SBezierList sbl = {};
|
||||
ss->MakeSectionEdgesInto(shell, NULL, &sbl);
|
||||
ss.MakeSectionEdgesInto(shell, NULL, &sbl);
|
||||
|
||||
// Apply the export scale factor.
|
||||
ss->ScaleSelfBy(1.0/SS.exportScale);
|
||||
ss.ScaleSelfBy(1.0/SS.exportScale);
|
||||
sbl.ScaleSelfBy(1.0/SS.exportScale);
|
||||
|
||||
ExportSurface(ss, &sbl);
|
||||
ExportSurface(&ss, &sbl);
|
||||
|
||||
sbl.Clear();
|
||||
}
|
||||
|
|
|
@ -170,22 +170,21 @@ public:
|
|||
}
|
||||
|
||||
if(writer->constraint) {
|
||||
Constraint *c;
|
||||
for(c = writer->constraint->First(); c; c = writer->constraint->NextAfter(c)) {
|
||||
if(!writer->NeedToOutput(c)) continue;
|
||||
switch(c->type) {
|
||||
for(Constraint &c : *writer->constraint) {
|
||||
if(!writer->NeedToOutput(&c)) continue;
|
||||
switch(c.type) {
|
||||
case Constraint::Type::PT_PT_DISTANCE: {
|
||||
Vector ap = SK.GetEntity(c->ptA)->PointGetNum();
|
||||
Vector bp = SK.GetEntity(c->ptB)->PointGetNum();
|
||||
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(c->disp.offset);
|
||||
Vector ap = SK.GetEntity(c.ptA)->PointGetNum();
|
||||
Vector bp = SK.GetEntity(c.ptB)->PointGetNum();
|
||||
Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(c.disp.offset);
|
||||
writeAlignedDimension(xfrm(ap), xfrm(bp), xfrm(ref),
|
||||
xfrm(ref), c->Label(), c->GetStyle(), c->valA);
|
||||
xfrm(ref), c.Label(), c.GetStyle(), c.valA);
|
||||
break;
|
||||
}
|
||||
|
||||
case Constraint::Type::PT_LINE_DISTANCE: {
|
||||
Vector pt = SK.GetEntity(c->ptA)->PointGetNum();
|
||||
Entity *line = SK.GetEntity(c->entityA);
|
||||
Vector pt = SK.GetEntity(c.ptA)->PointGetNum();
|
||||
Entity *line = SK.GetEntity(c.entityA);
|
||||
Vector lA = SK.GetEntity(line->point[0])->PointGetNum();
|
||||
Vector lB = SK.GetEntity(line->point[1])->PointGetNum();
|
||||
Vector dl = lB.Minus(lA);
|
||||
|
@ -194,7 +193,7 @@ public:
|
|||
|
||||
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);
|
||||
|
||||
double ddl = dl.Dot(dl);
|
||||
|
@ -209,54 +208,54 @@ public:
|
|||
|
||||
Vector xdl = xfrm(lB).Minus(xfrm(lA));
|
||||
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,
|
||||
c->GetStyle(), c->valA);
|
||||
c.GetStyle(), c.valA);
|
||||
break;
|
||||
}
|
||||
|
||||
case Constraint::Type::DIAMETER: {
|
||||
Entity *circle = SK.GetEntity(c->entityA);
|
||||
Entity *circle = SK.GetEntity(c.entityA);
|
||||
Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
|
||||
Quaternion q = SK.GetEntity(circle->normal)->NormalGetNum();
|
||||
Vector n = q.RotationN().WithMagnitude(1);
|
||||
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.
|
||||
ref = ref.Minus(n.ScaledBy(n.Dot(ref) - n.Dot(center)));
|
||||
|
||||
Vector rad = ref.Minus(center).WithMagnitude(r);
|
||||
if(/*isRadius*/c->other) {
|
||||
if(/*isRadius*/c.other) {
|
||||
writeRadialDimension(
|
||||
xfrm(center), xfrm(center.Plus(rad)),
|
||||
xfrm(ref), c->Label(), c->GetStyle(), c->valA);
|
||||
xfrm(ref), c.Label(), c.GetStyle(), c.valA);
|
||||
} else {
|
||||
writeDiametricDimension(
|
||||
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;
|
||||
}
|
||||
|
||||
case Constraint::Type::ANGLE: {
|
||||
Entity *a = SK.GetEntity(c->entityA);
|
||||
Entity *b = SK.GetEntity(c->entityB);
|
||||
Entity *a = SK.GetEntity(c.entityA);
|
||||
Entity *b = SK.GetEntity(c.entityB);
|
||||
|
||||
Vector a0 = a->VectorGetStartPoint();
|
||||
Vector b0 = b->VectorGetStartPoint();
|
||||
Vector da = a->VectorGetNum();
|
||||
Vector db = b->VectorGetNum();
|
||||
if(/*otherAngle*/c->other) {
|
||||
if(/*otherAngle*/c.other) {
|
||||
a0 = a0.Plus(da);
|
||||
da = da.ScaledBy(-1);
|
||||
}
|
||||
|
||||
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),
|
||||
&skew);
|
||||
if(!skew) ref = pi.Plus(c->disp.offset);
|
||||
if(!skew) ref = pi.Plus(c.disp.offset);
|
||||
|
||||
Vector norm = da.Cross(db);
|
||||
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(
|
||||
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.
|
||||
a0 = a->VectorGetStartPoint();
|
||||
|
@ -287,15 +286,15 @@ public:
|
|||
|
||||
writeAngularDimension(
|
||||
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;
|
||||
}
|
||||
|
||||
case Constraint::Type::COMMENT: {
|
||||
Style *st = SK.style.FindById(c->GetStyle());
|
||||
writeText(xfrm(c->disp.offset), c->Label(),
|
||||
Style::TextHeight(c->GetStyle()) / SS.GW.scale,
|
||||
st->textAngle, st->textOrigin, c->GetStyle());
|
||||
Style *st = SK.style.FindById(c.GetStyle());
|
||||
writeText(xfrm(c.disp.offset), c.Label(),
|
||||
Style::TextHeight(c.GetStyle()) / SS.GW.scale,
|
||||
st->textAngle, st->textOrigin, c.GetStyle());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1309,9 +1308,9 @@ void GCodeFileWriter::FinishAndCloseFile() {
|
|||
SS.MmToString(pt->p.x).c_str(), SS.MmToString(pt->p.y).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",
|
||||
SS.MmToString(SS.gCode.depth < 0 ? +5 : -5).c_str());
|
||||
SS.MmToString(SS.gCode.safeHeight).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
34
src/file.cpp
|
@ -354,19 +354,18 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
|
|||
}
|
||||
|
||||
SShell *s = &g->runningShell;
|
||||
SSurface *srf;
|
||||
for(srf = s->surface.First(); srf; srf = s->surface.NextAfter(srf)) {
|
||||
for(SSurface &srf : s->surface) {
|
||||
fprintf(fh, "Surface %08x %08x %08x %d %d\n",
|
||||
srf->h.v, srf->color.ToPackedInt(), srf->face, srf->degm, srf->degn);
|
||||
for(i = 0; i <= srf->degm; i++) {
|
||||
for(j = 0; j <= srf->degn; j++) {
|
||||
srf.h.v, srf.color.ToPackedInt(), srf.face, srf.degm, srf.degn);
|
||||
for(i = 0; i <= srf.degm; i++) {
|
||||
for(j = 0; j <= srf.degn; j++) {
|
||||
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;
|
||||
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",
|
||||
stb->curve.v, stb->backwards ? 1 : 0,
|
||||
CO(stb->start), CO(stb->finish));
|
||||
|
@ -374,21 +373,20 @@ bool SolveSpaceUI::SaveToFile(const Platform::Path &filename) {
|
|||
|
||||
fprintf(fh, "AddSurface\n");
|
||||
}
|
||||
SCurve *sc;
|
||||
for(sc = s->curve.First(); sc; sc = s->curve.NextAfter(sc)) {
|
||||
for(SCurve &sc : s->curve) {
|
||||
fprintf(fh, "Curve %08x %d %d %08x %08x\n",
|
||||
sc->h.v,
|
||||
sc->isExact ? 1 : 0, sc->exact.deg,
|
||||
sc->surfA.v, sc->surfB.v);
|
||||
sc.h.v,
|
||||
sc.isExact ? 1 : 0, sc.exact.deg,
|
||||
sc.surfA.v, sc.surfB.v);
|
||||
|
||||
if(sc->isExact) {
|
||||
for(i = 0; i <= sc->exact.deg; i++) {
|
||||
if(sc.isExact) {
|
||||
for(i = 0; i <= sc.exact.deg; i++) {
|
||||
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;
|
||||
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",
|
||||
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) {
|
||||
return LinkIDF(filename, le, m, sh);
|
||||
} else if(strcmp(filename.Extension().c_str(), "stl")==0) {
|
||||
return LinkStl(filename, le, m, sh);
|
||||
} else {
|
||||
return LoadEntitiesFromSlvs(filename, le, m, sh);
|
||||
}
|
||||
|
@ -909,6 +909,8 @@ try_again:
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if(g.IsTriangleMeshAssembly())
|
||||
g.forceToMesh = true;
|
||||
} else if(linkMap.count(g.linkFile) == 0) {
|
||||
dbp("Missing file for group: %s", g.name.c_str());
|
||||
// The file was moved; prompt the user for its new location.
|
||||
|
|
|
@ -224,9 +224,11 @@ void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox)
|
|||
if(PruneGroups(hg))
|
||||
goto pruned;
|
||||
|
||||
int groupRequestIndex = 0;
|
||||
for(auto &req : SK.request) {
|
||||
Request *r = &req;
|
||||
if(r->group != hg) continue;
|
||||
r->groupRequestIndex = groupRequestIndex++;
|
||||
|
||||
r->Generate(&(SK.entity), &(SK.param));
|
||||
}
|
||||
|
|
|
@ -94,10 +94,12 @@ const MenuEntry Menu[] = {
|
|||
{ 1, N_("Show Snap &Grid"), Command::SHOW_GRID, '>', KC, mView },
|
||||
{ 1, N_("Darken Inactive Solids"), Command::DIM_SOLID_MODEL, 0, 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 },
|
||||
{ 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 &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, N_("Show &Toolbar"), Command::SHOW_TOOLBAR, 0, 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, 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_("Length Ra&tio"), Command::RATIO, 'z', KN, mCon },
|
||||
{ 1, N_("Length Diff&erence"), Command::DIFFERENCE, 'j', KN, mCon },
|
||||
{ 1, N_("Length / Arc Ra&tio"), Command::RATIO, 'z', 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_("S&ymmetric"), Command::SYMMETRIC, 'y', KN, mCon },
|
||||
{ 1, N_("Para&llel / Tangent"), Command::PARALLEL, 'l', KN, mCon },
|
||||
|
@ -317,6 +319,8 @@ void GraphicsWindow::PopulateMainMenu() {
|
|||
dimSolidModelMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::PERSPECTIVE_PROJ) {
|
||||
perspectiveProjMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::EXPLODE_SKETCH) {
|
||||
explodeMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::SHOW_TOOLBAR) {
|
||||
showToolbarMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::SHOW_TEXT_WND) {
|
||||
|
@ -329,6 +333,8 @@ void GraphicsWindow::PopulateMainMenu() {
|
|||
unitsMetersMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::UNITS_INCHES) {
|
||||
unitsInchesMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::UNITS_FEET_INCHES) {
|
||||
unitsFeetInchesMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::SEL_WORKPLANE) {
|
||||
inWorkplaneMenuItem = menuItem;
|
||||
} else if(Menu[i].cmd == Command::FREE_IN_3D) {
|
||||
|
@ -369,8 +375,11 @@ static void PopulateMenuWithPathnames(Platform::MenuRef menu,
|
|||
|
||||
void GraphicsWindow::PopulateRecentFiles() {
|
||||
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;
|
||||
SS.Load(path);
|
||||
SS.Load(pathCopy);
|
||||
});
|
||||
|
||||
PopulateMenuWithPathnames(linkRecentMenu, SS.recentFiles, [](const Platform::Path &path) {
|
||||
|
@ -404,6 +413,8 @@ void GraphicsWindow::Init() {
|
|||
showEdges = true;
|
||||
showMesh = false;
|
||||
showOutlines = false;
|
||||
showFacesDrawing = false;
|
||||
showFacesNonDrawing = true;
|
||||
drawOccludedAs = DrawOccludedAs::INVISIBLE;
|
||||
|
||||
showTextWindow = true;
|
||||
|
@ -419,7 +430,7 @@ void GraphicsWindow::Init() {
|
|||
using namespace std::placeholders;
|
||||
// Do this first, so that if it causes an onRender event we don't try to paint without
|
||||
// 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->onRender = std::bind(&GraphicsWindow::Paint, this);
|
||||
window->onKeyboardEvent = std::bind(&GraphicsWindow::KeyboardEvent, this, _1);
|
||||
|
@ -745,6 +756,12 @@ void GraphicsWindow::MenuView(Command id) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Command::EXPLODE_SKETCH:
|
||||
SS.explode = !SS.explode;
|
||||
SS.GW.EnsureValidActives();
|
||||
SS.MarkGroupDirty(SS.GW.activeGroup, true);
|
||||
break;
|
||||
|
||||
case Command::ONTO_WORKPLANE:
|
||||
if(SS.GW.LockedInWorkplane()) {
|
||||
SS.GW.AnimateOntoWorkplane();
|
||||
|
@ -838,6 +855,12 @@ void GraphicsWindow::MenuView(Command id) {
|
|||
SS.GW.EnsureValidActives();
|
||||
break;
|
||||
|
||||
case Command::UNITS_FEET_INCHES:
|
||||
SS.viewUnits = Unit::FEET_INCHES;
|
||||
SS.ScheduleShowTW();
|
||||
SS.GW.EnsureValidActives();
|
||||
break;
|
||||
|
||||
case Command::UNITS_MM:
|
||||
SS.viewUnits = Unit::MM;
|
||||
SS.ScheduleShowTW();
|
||||
|
@ -920,6 +943,7 @@ void GraphicsWindow::EnsureValidActives() {
|
|||
case Unit::MM:
|
||||
case Unit::METERS:
|
||||
case Unit::INCHES:
|
||||
case Unit::FEET_INCHES:
|
||||
break;
|
||||
default:
|
||||
SS.viewUnits = Unit::MM;
|
||||
|
@ -928,6 +952,7 @@ void GraphicsWindow::EnsureValidActives() {
|
|||
unitsMmMenuItem->SetActive(SS.viewUnits == Unit::MM);
|
||||
unitsMetersMenuItem->SetActive(SS.viewUnits == Unit::METERS);
|
||||
unitsInchesMenuItem->SetActive(SS.viewUnits == Unit::INCHES);
|
||||
unitsFeetInchesMenuItem->SetActive(SS.viewUnits == Unit::FEET_INCHES);
|
||||
|
||||
if(SS.TW.window) SS.TW.window->SetVisible(SS.GW.showTextWindow);
|
||||
showTextWndMenuItem->SetActive(SS.GW.showTextWindow);
|
||||
|
@ -935,6 +960,7 @@ void GraphicsWindow::EnsureValidActives() {
|
|||
showGridMenuItem->SetActive(SS.GW.showSnapGrid);
|
||||
dimSolidModelMenuItem->SetActive(SS.GW.dimSolidModel);
|
||||
perspectiveProjMenuItem->SetActive(SS.usePerspectiveProj);
|
||||
explodeMenuItem->SetActive(SS.explode);
|
||||
showToolbarMenuItem->SetActive(SS.showToolbar);
|
||||
fullScreenMenuItem->SetActive(SS.GW.window->IsFullScreen());
|
||||
|
||||
|
@ -965,20 +991,19 @@ void GraphicsWindow::ForceTextWindowShown() {
|
|||
}
|
||||
|
||||
void GraphicsWindow::DeleteTaggedRequests() {
|
||||
Request *r;
|
||||
// Delete any requests that were affected by this deletion.
|
||||
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
|
||||
if(r->workplane == Entity::FREE_IN_3D) continue;
|
||||
if(!r->workplane.isFromRequest()) continue;
|
||||
Request *wrkpl = SK.GetRequest(r->workplane.request());
|
||||
for(Request &r : SK.request) {
|
||||
if(r.workplane == Entity::FREE_IN_3D) continue;
|
||||
if(!r.workplane.isFromRequest()) continue;
|
||||
Request *wrkpl = SK.GetRequest(r.workplane.request());
|
||||
if(wrkpl->tag)
|
||||
r->tag = 1;
|
||||
r.tag = 1;
|
||||
}
|
||||
// Rewrite any point-coincident constraints that were affected by this
|
||||
// deletion.
|
||||
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
|
||||
if(!r->tag) continue;
|
||||
FixConstraintsForRequestBeingDeleted(r->h);
|
||||
for(Request &r : SK.request) {
|
||||
if(!r.tag) continue;
|
||||
FixConstraintsForRequestBeingDeleted(r.h);
|
||||
}
|
||||
// and then delete the tagged requests.
|
||||
SK.request.RemoveTagged();
|
||||
|
@ -1042,9 +1067,8 @@ void GraphicsWindow::MenuEdit(Command id) {
|
|||
SS.centerOfMass.draw = false;
|
||||
// This clears the marks drawn to indicate which points are
|
||||
// still free to drag.
|
||||
Param *p;
|
||||
for(p = SK.param.First(); p; p = SK.param.NextAfter(p)) {
|
||||
p->free = false;
|
||||
for(Param &p : SK.param) {
|
||||
p.free = false;
|
||||
}
|
||||
if(SS.exportMode) {
|
||||
SS.exportMode = false;
|
||||
|
@ -1054,13 +1078,12 @@ void GraphicsWindow::MenuEdit(Command id) {
|
|||
break;
|
||||
|
||||
case Command::SELECT_ALL: {
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group != SS.GW.activeGroup) continue;
|
||||
if(e->IsFace() || e->IsDistance()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
for(Entity &e : SK.entity) {
|
||||
if(e.group != SS.GW.activeGroup) continue;
|
||||
if(e.IsFace() || e.IsDistance()) continue;
|
||||
if(!e.IsVisible()) continue;
|
||||
|
||||
SS.GW.MakeSelected(e->h);
|
||||
SS.GW.MakeSelected(e.h);
|
||||
}
|
||||
SS.GW.Invalidate();
|
||||
SS.ScheduleShowTW();
|
||||
|
@ -1068,24 +1091,23 @@ void GraphicsWindow::MenuEdit(Command id) {
|
|||
}
|
||||
|
||||
case Command::SELECT_CHAIN: {
|
||||
Entity *e;
|
||||
int newlySelected = 0;
|
||||
bool didSomething;
|
||||
do {
|
||||
didSomething = false;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group != SS.GW.activeGroup) continue;
|
||||
if(!e->HasEndpoints()) continue;
|
||||
if(!e->IsVisible()) continue;
|
||||
for(Entity &e : SK.entity) {
|
||||
if(e.group != SS.GW.activeGroup) continue;
|
||||
if(!e.HasEndpoints()) continue;
|
||||
if(!e.IsVisible()) continue;
|
||||
|
||||
Vector st = e->EndpointStart(),
|
||||
fi = e->EndpointFinish();
|
||||
Vector st = e.EndpointStart(),
|
||||
fi = e.EndpointFinish();
|
||||
|
||||
bool onChain = false, alreadySelected = false;
|
||||
List<Selection> *ls = &(SS.GW.selection);
|
||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
||||
if(!s->entity.v) continue;
|
||||
if(s->entity == e->h) {
|
||||
if(s->entity == e.h) {
|
||||
alreadySelected = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -1102,7 +1124,7 @@ void GraphicsWindow::MenuEdit(Command id) {
|
|||
}
|
||||
}
|
||||
if(onChain && !alreadySelected) {
|
||||
SS.GW.MakeSelected(e->h);
|
||||
SS.GW.MakeSelected(e.h);
|
||||
newlySelected++;
|
||||
didSomething = true;
|
||||
}
|
||||
|
@ -1367,6 +1389,14 @@ void GraphicsWindow::ToggleBool(bool *v) {
|
|||
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);
|
||||
SS.ScheduleShowTW();
|
||||
}
|
||||
|
|
|
@ -138,12 +138,24 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
|
|||
g.predef.q = wrkplg->predef.q;
|
||||
} 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 {
|
||||
Error(_("Bad selection for new sketch in workplane. This "
|
||||
"group can be created with:\n\n"
|
||||
" * a point (through the point, orthogonal to coordinate axes)\n"
|
||||
" * a point and two line segments (through the point, "
|
||||
"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"));
|
||||
return;
|
||||
}
|
||||
|
@ -392,7 +404,11 @@ bool Group::IsForcedToMeshBySource() 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() {
|
||||
|
@ -404,11 +420,10 @@ std::string Group::DescriptionString() {
|
|||
}
|
||||
|
||||
void Group::Activate() {
|
||||
if(type == Type::EXTRUDE || type == Type::LINKED || type == Type::LATHE ||
|
||||
type == Type::REVOLVE || type == Type::HELIX || type == Type::TRANSLATE || type == Type::ROTATE) {
|
||||
SS.GW.showFaces = true;
|
||||
if(type == Type::DRAWING_WORKPLANE || type == Type::DRAWING_3D) {
|
||||
SS.GW.showFaces = SS.GW.showFacesDrawing;
|
||||
} else {
|
||||
SS.GW.showFaces = false;
|
||||
SS.GW.showFaces = SS.GW.showFacesNonDrawing;
|
||||
}
|
||||
SS.MarkGroupDirty(h); // for good measure; shouldn't be needed
|
||||
SS.ScheduleShowTW();
|
||||
|
@ -440,7 +455,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
|
|||
if(predef.negateU) u = u.ScaledBy(-1);
|
||||
if(predef.negateV) v = v.ScaledBy(-1);
|
||||
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.
|
||||
q = predef.q;
|
||||
} else ssassert(false, "Unexpected workplane subtype");
|
||||
|
@ -448,6 +463,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
|
|||
Entity normal = {};
|
||||
normal.type = Entity::Type::NORMAL_N_COPY;
|
||||
normal.numNormal = q;
|
||||
|
||||
normal.point[0] = h.entity(2);
|
||||
normal.group = h;
|
||||
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);
|
||||
#undef EC
|
||||
#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) {
|
||||
if(predef.entityB != Entity::FREE_IN_3D) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
bool Group::ShouldDrawExploded() const {
|
||||
return SS.explode && h == SS.GW.activeGroup && type == Type::DRAWING_WORKPLANE && !SS.exportMode;
|
||||
}
|
||||
|
|
|
@ -83,13 +83,12 @@ void Group::GenerateLoops() {
|
|||
}
|
||||
|
||||
void SShell::RemapFaces(Group *g, int remap) {
|
||||
SSurface *ss;
|
||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)){
|
||||
hEntity face = { ss->face };
|
||||
for(SSurface &ss : surface){
|
||||
hEntity face = { ss.face };
|
||||
if(face == Entity::NO_ENTITY) continue;
|
||||
|
||||
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
|
||||
if(ss->degm != 1 || ss->degn != 1) continue;
|
||||
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->group != opA) continue;
|
||||
if(e->type != Entity::Type::LINE_SEGMENT) continue;
|
||||
for(Entity &e : SK.entity) {
|
||||
if(e.group != opA) continue;
|
||||
if(e.type != Entity::Type::LINE_SEGMENT) continue;
|
||||
|
||||
Vector a = SK.GetEntity(e->point[0])->PointGetNum(),
|
||||
b = SK.GetEntity(e->point[1])->PointGetNum();
|
||||
Vector a = SK.GetEntity(e.point[0])->PointGetNum(),
|
||||
b = SK.GetEntity(e.point[1])->PointGetNum();
|
||||
a = a.Plus(ttop);
|
||||
b = b.Plus(ttop);
|
||||
// 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])) ||
|
||||
(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;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ static std::vector <std::string> splitString(const std::string line) {
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Functions for linking an IDF file - we need to create entites that
|
||||
// Functions for linking an IDF file - we need to create entities that
|
||||
// get remapped into a linked group similar to linking .slvs files
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -77,7 +77,7 @@ static hEntity newPoint(EntityList *el, int *id, Vector p, bool visible = true)
|
|||
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 = {};
|
||||
en.type = Entity::Type::LINE_SEGMENT;
|
||||
en.point[0] = p0;
|
||||
|
@ -85,8 +85,8 @@ static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1) {
|
|||
en.extraPoints = 0;
|
||||
en.timesApplied = 0;
|
||||
en.group.v = 493;
|
||||
en.construction = false;
|
||||
en.style.v = Style::ACTIVE_GRP;
|
||||
en.construction = keepout;
|
||||
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
|
||||
en.actVisible = true;
|
||||
en.forceHidden = false;
|
||||
|
||||
|
@ -117,7 +117,7 @@ static hEntity newNormal(EntityList *el, int *id, Quaternion normal) {
|
|||
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 = {};
|
||||
en.type = Entity::Type::ARC_OF_CIRCLE;
|
||||
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.timesApplied = 0;
|
||||
en.group.v = 403;
|
||||
en.construction = false;
|
||||
en.style.v = Style::ACTIVE_GRP;
|
||||
en.construction = keepout;
|
||||
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
|
||||
en.actVisible = true;
|
||||
en.forceHidden = false; *id = *id+1;
|
||||
|
||||
|
@ -158,7 +158,7 @@ static hEntity newDistance(EntityList *el, int *id, double distance) {
|
|||
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 = {};
|
||||
en.type = Entity::Type::CIRCLE;
|
||||
en.point[0] = p0;
|
||||
|
@ -167,8 +167,8 @@ static hEntity newCircle(EntityList *el, int *id, hEntity p0, hEntity hdist, hEn
|
|||
en.extraPoints = 0;
|
||||
en.timesApplied = 0;
|
||||
en.group.v = 399;
|
||||
en.construction = false;
|
||||
en.style.v = Style::ACTIVE_GRP;
|
||||
en.construction = keepout;
|
||||
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
|
||||
en.actVisible = true;
|
||||
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
|
||||
// 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,
|
||||
Vector p0, Vector p1, double angle) {
|
||||
Vector p0, Vector p1, double angle, bool keepout) {
|
||||
if (angle == 0.0) {
|
||||
//line
|
||||
if(p0.Equals(p1)) return;
|
||||
|
||||
newLine(el, id, h0, h1);
|
||||
newLine(el, id, h0, h1, keepout);
|
||||
|
||||
} else if(angle == 360.0) {
|
||||
// circle
|
||||
double d = p1.Minus(p0).Magnitude();
|
||||
hEntity hd = newDistance(el, id, d);
|
||||
newCircle(el, id, h1, hd, hnorm);
|
||||
newCircle(el, id, h1, hd, hnorm, keepout);
|
||||
|
||||
} else {
|
||||
// arc
|
||||
|
@ -226,7 +226,7 @@ static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntit
|
|||
}
|
||||
Vector c = m.Minus(perp.ScaledBy(dist));
|
||||
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 {
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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) {
|
||||
|
@ -355,10 +355,9 @@ bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *s
|
|||
} else if (line.find(".BOARD_OUTLINE") == 0) {
|
||||
section = board_outline;
|
||||
record_number = 1;
|
||||
// no keepouts for now - they should also be shown as construction?
|
||||
// } else if (line.find(".ROUTE_KEEPOUT") == 0) {
|
||||
// section = routing_keepout;
|
||||
// record_number = 1;
|
||||
} else if (line.find(".ROUTE_KEEPOUT") == 0) {
|
||||
section = routing_keepout;
|
||||
record_number = 1;
|
||||
} else if(line.find(".DRILLED_HOLES") == 0) {
|
||||
section = drilled_holes;
|
||||
record_number = 1;
|
||||
|
@ -433,13 +432,15 @@ bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *s
|
|||
bool vis = (ang == 360.0);
|
||||
if (bottomEntities) {
|
||||
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;
|
||||
hprev = hp;
|
||||
}
|
||||
if (topEntities) {
|
||||
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;
|
||||
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);
|
||||
hEntity hcent = newPoint(el, &entityCount, cent);
|
||||
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
|
||||
Vector cTop = Vector::From(x,y,board_thickness);
|
||||
hcent = newPoint(el, &entityCount, cTop);
|
||||
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
|
||||
Vector pt = Vector::From(x+d/2, y, 0.0);
|
||||
MakeBeziersForArcs(&sbl, cent, pt, pt, normal, 360.0);
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Triangle mesh file reader. Reads an STL file triangle mesh and creates
|
||||
// a SovleSpace SMesh from it. Supports only Linking, not import.
|
||||
//
|
||||
// Copyright 2020 Paul Kahler.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "solvespace.h"
|
||||
#include "sketch.h"
|
||||
#include <vector>
|
||||
|
||||
#define MIN_POINT_DISTANCE 0.001
|
||||
|
||||
// we will check for duplicate verticies and keep all their normals
|
||||
class vertex {
|
||||
public:
|
||||
Vector p;
|
||||
std::vector<Vector> normal;
|
||||
};
|
||||
|
||||
static bool isEdgeVertex(vertex &v) {
|
||||
unsigned int i,j;
|
||||
bool result = false;
|
||||
for(i=0;i<v.normal.size();i++) {
|
||||
for(j=i;j<v.normal.size();j++) {
|
||||
if(v.normal[i].Dot(v.normal[j]) < 0.9) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// This function has poor performance, used inside a loop it is O(n**2)
|
||||
static void addUnique(std::vector<vertex> &lv, Vector &p, Vector &n) {
|
||||
unsigned int i;
|
||||
for(i=0; i<lv.size(); i++) {
|
||||
if(lv[i].p.Equals(p, MIN_POINT_DISTANCE)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(i==lv.size()) {
|
||||
vertex v;
|
||||
v.p = p;
|
||||
lv.push_back(v);
|
||||
}
|
||||
// we could improve a little by only storing unique normals
|
||||
lv[i].normal.push_back(n);
|
||||
};
|
||||
|
||||
// Make a new point - type doesn't matter since we will make a copy later
|
||||
static hEntity newPoint(EntityList *el, int id, Vector p) {
|
||||
Entity en = {};
|
||||
en.type = Entity::Type::POINT_N_COPY;
|
||||
en.extraPoints = 0;
|
||||
en.timesApplied = 0;
|
||||
en.group.v = 462;
|
||||
en.actPoint = p;
|
||||
en.construction = false;
|
||||
en.style.v = Style::DATUM;
|
||||
en.actVisible = true;
|
||||
en.forceHidden = false;
|
||||
|
||||
en.h.v = id + en.group.v*65536;
|
||||
el->Add(&en);
|
||||
return en.h;
|
||||
}
|
||||
|
||||
// check if a vertex is unique and add it via newPoint if it is.
|
||||
static void addVertex(EntityList *el, Vector v) {
|
||||
if(el->n < 15000) {
|
||||
int id = el->n+2;
|
||||
newPoint(el, id, v);
|
||||
}
|
||||
}
|
||||
|
||||
static hEntity newLine(EntityList *el, int id, hEntity p0, hEntity p1) {
|
||||
Entity en = {};
|
||||
en.type = Entity::Type::LINE_SEGMENT;
|
||||
en.point[0] = p0;
|
||||
en.point[1] = p1;
|
||||
en.extraPoints = 0;
|
||||
en.timesApplied = 0;
|
||||
en.group.v = 493;
|
||||
en.construction = true;
|
||||
en.style.v = Style::CONSTRUCTION;
|
||||
en.actVisible = true;
|
||||
en.forceHidden = false;
|
||||
|
||||
en.h.v = id + en.group.v*65536;
|
||||
el->Add(&en);
|
||||
return en.h;
|
||||
}
|
||||
|
||||
namespace SolveSpace {
|
||||
|
||||
bool LinkStl(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
|
||||
dbp("\nLink STL triangle mesh.");
|
||||
el->Clear();
|
||||
std::string data;
|
||||
if(!ReadFile(filename, &data)) {
|
||||
Error("Couldn't read from '%s'", filename.raw.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream f(data);
|
||||
|
||||
char str[80] = {};
|
||||
f.read(str, 80);
|
||||
|
||||
uint32_t n;
|
||||
uint32_t color;
|
||||
|
||||
f.read((char*)&n, 4);
|
||||
dbp("%d triangles", n);
|
||||
|
||||
float x,y,z;
|
||||
float xn,yn,zn;
|
||||
|
||||
//add the STL origin as an entity
|
||||
addVertex(el, Vector::From(0.0, 0.0, 0.0));
|
||||
|
||||
std::vector<vertex> verts = {};
|
||||
|
||||
for(uint32_t i = 0; i<n; i++) {
|
||||
STriangle tr = {};
|
||||
|
||||
// read the triangle normal
|
||||
f.read((char*)&xn, 4);
|
||||
f.read((char*)&yn, 4);
|
||||
f.read((char*)&zn, 4);
|
||||
tr.an = Vector::From(xn,yn,zn);
|
||||
tr.bn = tr.an;
|
||||
tr.cn = tr.an;
|
||||
|
||||
f.read((char*)&x, 4);
|
||||
f.read((char*)&y, 4);
|
||||
f.read((char*)&z, 4);
|
||||
tr.a.x = x;
|
||||
tr.a.y = y;
|
||||
tr.a.z = z;
|
||||
|
||||
f.read((char*)&x, 4);
|
||||
f.read((char*)&y, 4);
|
||||
f.read((char*)&z, 4);
|
||||
tr.b.x = x;
|
||||
tr.b.y = y;
|
||||
tr.b.z = z;
|
||||
|
||||
f.read((char*)&x, 4);
|
||||
f.read((char*)&y, 4);
|
||||
f.read((char*)&z, 4);
|
||||
tr.c.x = x;
|
||||
tr.c.y = y;
|
||||
tr.c.z = z;
|
||||
|
||||
f.read((char*)&color,2);
|
||||
if(color & 0x8000) {
|
||||
tr.meta.color.red = (color >> 7) & 0xf8;
|
||||
tr.meta.color.green = (color >> 2) & 0xf8;
|
||||
tr.meta.color.blue = (color << 3);
|
||||
tr.meta.color.alpha = 255;
|
||||
} else {
|
||||
tr.meta.color.red = 90;
|
||||
tr.meta.color.green = 120;
|
||||
tr.meta.color.blue = 140;
|
||||
tr.meta.color.alpha = 255;
|
||||
}
|
||||
|
||||
m->AddTriangle(&tr);
|
||||
Vector normal = tr.Normal().WithMagnitude(1.0);
|
||||
addUnique(verts, tr.a, normal);
|
||||
addUnique(verts, tr.b, normal);
|
||||
addUnique(verts, tr.c, normal);
|
||||
}
|
||||
dbp("%d verticies", verts.size());
|
||||
|
||||
BBox box = {};
|
||||
box.minp = verts[0].p;
|
||||
box.maxp = verts[0].p;
|
||||
|
||||
// determine the bounding box for all vertexes
|
||||
for(unsigned int i=1; i<verts.size(); i++) {
|
||||
box.Include(verts[i].p);
|
||||
}
|
||||
|
||||
hEntity p[8];
|
||||
int id = el->n+2;
|
||||
p[0] = newPoint(el, id++, Vector::From(box.minp.x, box.minp.y, box.minp.z));
|
||||
p[1] = newPoint(el, id++, Vector::From(box.maxp.x, box.minp.y, box.minp.z));
|
||||
p[2] = newPoint(el, id++, Vector::From(box.minp.x, box.maxp.y, box.minp.z));
|
||||
p[3] = newPoint(el, id++, Vector::From(box.maxp.x, box.maxp.y, box.minp.z));
|
||||
p[4] = newPoint(el, id++, Vector::From(box.minp.x, box.minp.y, box.maxp.z));
|
||||
p[5] = newPoint(el, id++, Vector::From(box.maxp.x, box.minp.y, box.maxp.z));
|
||||
p[6] = newPoint(el, id++, Vector::From(box.minp.x, box.maxp.y, box.maxp.z));
|
||||
p[7] = newPoint(el, id++, Vector::From(box.maxp.x, box.maxp.y, box.maxp.z));
|
||||
|
||||
newLine(el, id++, p[0], p[1]);
|
||||
newLine(el, id++, p[0], p[2]);
|
||||
newLine(el, id++, p[3], p[1]);
|
||||
newLine(el, id++, p[3], p[2]);
|
||||
|
||||
newLine(el, id++, p[4], p[5]);
|
||||
newLine(el, id++, p[4], p[6]);
|
||||
newLine(el, id++, p[7], p[5]);
|
||||
newLine(el, id++, p[7], p[6]);
|
||||
|
||||
newLine(el, id++, p[0], p[4]);
|
||||
newLine(el, id++, p[1], p[5]);
|
||||
newLine(el, id++, p[2], p[6]);
|
||||
newLine(el, id++, p[3], p[7]);
|
||||
|
||||
for(unsigned int i=0; i<verts.size(); i++) {
|
||||
// create point entities for edge vertexes
|
||||
if(isEdgeVertex(verts[i])) {
|
||||
addVertex(el, verts[i].p);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -131,11 +131,15 @@ case SLVS_C_PT_ON_LINE: t = Constraint::Type::PT_ON_LINE; break;
|
|||
case SLVS_C_PT_ON_FACE: t = Constraint::Type::PT_ON_FACE; break;
|
||||
case SLVS_C_EQUAL_LENGTH_LINES: t = Constraint::Type::EQUAL_LENGTH_LINES; 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_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_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_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_HORIZ: t = Constraint::Type::SYMMETRIC_HORIZ; break;
|
||||
case SLVS_C_SYMMETRIC_VERT: t = Constraint::Type::SYMMETRIC_VERT; break;
|
||||
|
|
|
@ -50,38 +50,36 @@ void GraphicsWindow::FixConstraintsForRequestBeingDeleted(hRequest hr) {
|
|||
Request *r = SK.GetRequest(hr);
|
||||
if(r->group != SS.GW.activeGroup) return;
|
||||
|
||||
Entity *e;
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(!(e->h.isFromRequest())) continue;
|
||||
if(e->h.request() != hr) continue;
|
||||
for(Entity &e : SK.entity) {
|
||||
if(!(e.h.isFromRequest())) continue;
|
||||
if(e.h.request() != hr) continue;
|
||||
|
||||
if(e->type != Entity::Type::POINT_IN_2D &&
|
||||
e->type != Entity::Type::POINT_IN_3D)
|
||||
if(e.type != Entity::Type::POINT_IN_2D &&
|
||||
e.type != Entity::Type::POINT_IN_3D)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is a point generated by the request being deleted; so fix
|
||||
// the constraints for that.
|
||||
FixConstraintsForPointBeingDeleted(e->h);
|
||||
FixConstraintsForPointBeingDeleted(e.h);
|
||||
}
|
||||
}
|
||||
void GraphicsWindow::FixConstraintsForPointBeingDeleted(hEntity hpt) {
|
||||
List<hEntity> ld = {};
|
||||
|
||||
Constraint *c;
|
||||
SK.constraint.ClearTags();
|
||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
||||
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||
if(c->group != SS.GW.activeGroup) continue;
|
||||
for(Constraint &c : SK.constraint) {
|
||||
if(c.type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||
if(c.group != SS.GW.activeGroup) continue;
|
||||
|
||||
if(c->ptA == hpt) {
|
||||
ld.Add(&(c->ptB));
|
||||
c->tag = 1;
|
||||
if(c.ptA == hpt) {
|
||||
ld.Add(&(c.ptB));
|
||||
c.tag = 1;
|
||||
}
|
||||
if(c->ptB == hpt) {
|
||||
ld.Add(&(c->ptA));
|
||||
c->tag = 1;
|
||||
if(c.ptB == hpt) {
|
||||
ld.Add(&(c.ptA));
|
||||
c.tag = 1;
|
||||
}
|
||||
}
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
void GraphicsWindow::ParametricCurve::ConstrainPointIfCoincident(hEntity hpt) {
|
||||
Entity *e, *pt;
|
||||
Entity *pt;
|
||||
pt = SK.GetEntity(hpt);
|
||||
Vector ev, ptv;
|
||||
ptv = pt->PointGetNum();
|
||||
|
||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
||||
if(e->h == pt->h) continue;
|
||||
if(!e->IsPoint()) continue;
|
||||
if(e->group != pt->group) continue;
|
||||
if(e->workplane != pt->workplane) continue;
|
||||
for(Entity &e : SK.entity) {
|
||||
if(e.h == pt->h) continue;
|
||||
if(!e.IsPoint()) continue;
|
||||
if(e.group != pt->group) continue;
|
||||
if(e.workplane != pt->workplane) continue;
|
||||
|
||||
ev = e->PointGetNum();
|
||||
ev = e.PointGetNum();
|
||||
if(!ev.Equals(ptv)) continue;
|
||||
|
||||
Constraint::ConstrainCoincident(hpt, e->h);
|
||||
Constraint::ConstrainCoincident(hpt, e.h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ void GraphicsWindow::UpdateDraggedPoint(hEntity hp, double mx, double my) {
|
|||
Vector pos = p->PointGetNum();
|
||||
UpdateDraggedNum(&pos, mx, my);
|
||||
p->PointForceTo(pos);
|
||||
|
||||
SS.ScheduleShowTW();
|
||||
}
|
||||
|
||||
void GraphicsWindow::UpdateDraggedNum(Vector *pos, double mx, double my) {
|
||||
|
@ -101,7 +103,10 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
|||
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)) {
|
||||
hover.Clear();
|
||||
return;
|
||||
|
@ -188,32 +193,23 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
|||
hEntity dragEntity = ChooseFromHoverToDrag().entity;
|
||||
if(dragEntity.v) e = SK.GetEntity(dragEntity);
|
||||
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) {
|
||||
// The user clicked an unselected entity, which
|
||||
// means they're dragging just the hovered thing,
|
||||
// not the full selection. So clear all the selection
|
||||
// except that entity.
|
||||
ClearSelection();
|
||||
MakeSelected(e->h);
|
||||
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();
|
||||
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();
|
||||
pending.operation = Pending::DRAGGING_POINTS;
|
||||
}
|
||||
|
@ -425,6 +421,7 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
|||
SK.GetEntity(circle->distance)->DistanceForceTo(r);
|
||||
|
||||
SS.MarkGroupDirtyByEntity(pending.circle);
|
||||
SS.ScheduleShowTW();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -526,11 +523,16 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
|||
}
|
||||
|
||||
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
|
||||
// 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();
|
||||
return;
|
||||
}
|
||||
|
@ -714,11 +716,12 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
|||
|
||||
if(gs.points == 1) {
|
||||
Entity *p = SK.GetEntity(gs.point[0]);
|
||||
Constraint *c;
|
||||
Constraint *c = nullptr;
|
||||
IdList<Constraint,hConstraint> *lc = &(SK.constraint);
|
||||
for(c = lc->First(); c; c = lc->NextAfter(c)) {
|
||||
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||
if(c->ptA == p->h || c->ptB == p->h) {
|
||||
for(Constraint &ci : *lc) {
|
||||
if(ci.type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||
if(ci.ptA == p->h || ci.ptB == p->h) {
|
||||
c = &ci;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -728,11 +731,10 @@ void GraphicsWindow::MouseRightUp(double x, double y) {
|
|||
|
||||
SS.UndoRemember();
|
||||
SK.constraint.ClearTags();
|
||||
Constraint *c;
|
||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
||||
if(c->type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||
if(c->ptA == p->h || c->ptB == p->h) {
|
||||
c->tag = 1;
|
||||
for(Constraint &c : SK.constraint) {
|
||||
if(c.type != Constraint::Type::POINTS_COINCIDENT) continue;
|
||||
if(c.ptA == p->h || c.ptB == p->h) {
|
||||
c.tag = 1;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
orig.mouseDown = false;
|
||||
hoverWasSelectedOnMousedown = false;
|
||||
|
||||
switch(pending.operation) {
|
||||
case Pending::DRAGGING_POINTS:
|
||||
SS.extraLine.draw = false;
|
||||
// fall through
|
||||
case Pending::DRAGGING_CONSTRAINT:
|
||||
case Pending::DRAGGING_NORMAL:
|
||||
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();
|
||||
Invalidate();
|
||||
break;
|
||||
|
@ -1369,12 +1376,12 @@ void GraphicsWindow::EditConstraint(hConstraint constraint) {
|
|||
value /= 2;
|
||||
|
||||
// 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);
|
||||
} else if(c->type == Constraint::Type::ANGLE) {
|
||||
editValue = SS.DegreeToString(value);
|
||||
} else {
|
||||
editValue = SS.MmToString(value);
|
||||
editValue = SS.MmToString(value, true);
|
||||
value /= SS.MmPerUnit();
|
||||
}
|
||||
// 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_FACE_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
|
||||
// distance internally. To flip the sign, the user enters a
|
||||
// negative distance.
|
||||
|
@ -1444,6 +1453,8 @@ void GraphicsWindow::EditControlDone(const std::string &s) {
|
|||
}
|
||||
case Constraint::Type::ANGLE:
|
||||
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
|
||||
// they're always positive
|
||||
c->valA = fabs(e->Eval());
|
||||
|
|
|
@ -86,8 +86,10 @@ std::vector<FileFilter> SolveSpaceModelFileFilters = {
|
|||
};
|
||||
|
||||
std::vector<FileFilter> SolveSpaceLinkFileFilters = {
|
||||
{ CN_("file-type", "ALL"), { "slvs", "emn", "stl" } },
|
||||
{ CN_("file-type", "SolveSpace models"), { "slvs" } },
|
||||
{ CN_("file-type", "IDF circuit board"), { "emn" } },
|
||||
{ CN_("file-type", "STL triangle mesh"), { "stl" } },
|
||||
};
|
||||
|
||||
std::vector<FileFilter> RasterFileFilters = {
|
||||
|
|
|
@ -33,8 +33,19 @@
|
|||
|
||||
#if defined(HAVE_SPACEWARE)
|
||||
# include <spnav.h>
|
||||
# include <gdk/gdk.h>
|
||||
# if defined(GDK_WINDOWING_X11)
|
||||
# include <gdk/gdkx.h>
|
||||
# endif
|
||||
# if defined(GDK_WINDOWING_WAYLAND)
|
||||
# include <gdk/gdkwayland.h>
|
||||
# endif
|
||||
# if GTK_CHECK_VERSION(3, 20, 0)
|
||||
# include <gdkmm/seat.h>
|
||||
# else
|
||||
# include <gdkmm/devicemanager.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "solvespace.h"
|
||||
|
||||
|
@ -217,6 +228,10 @@ public:
|
|||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -1038,16 +1053,8 @@ WindowRef CreateWindow(Window::Kind kind, WindowRef parentWindow) {
|
|||
void Open3DConnexion() {}
|
||||
void Close3DConnexion() {}
|
||||
|
||||
#if defined(HAVE_SPACEWARE) && defined(GDK_WINDOWING_X11)
|
||||
static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent, gpointer data) {
|
||||
XEvent *xEvent = (XEvent *)gdkXEvent;
|
||||
WindowImplGtk *window = (WindowImplGtk *)data;
|
||||
|
||||
spnav_event spnavEvent;
|
||||
if(!spnav_x11_event(xEvent, &spnavEvent)) {
|
||||
return GDK_FILTER_CONTINUE;
|
||||
}
|
||||
|
||||
#if defined(HAVE_SPACEWARE) && (defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND))
|
||||
static void ProcessSpnavEvent(WindowImplGtk *window, const spnav_event &spnavEvent, bool shiftDown, bool controlDown) {
|
||||
switch(spnavEvent.type) {
|
||||
case SPNAV_EVENT_MOTION: {
|
||||
SixDofEvent event = {};
|
||||
|
@ -1058,8 +1065,8 @@ static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent,
|
|||
event.rotationX = (double)spnavEvent.motion.rx * 0.001;
|
||||
event.rotationY = (double)spnavEvent.motion.ry * 0.001;
|
||||
event.rotationZ = (double)spnavEvent.motion.rz * -0.001;
|
||||
event.shiftDown = xEvent->xmotion.state & ShiftMask;
|
||||
event.controlDown = xEvent->xmotion.state & ControlMask;
|
||||
event.shiftDown = shiftDown;
|
||||
event.controlDown = controlDown;
|
||||
if(window->onSixDofEvent) {
|
||||
window->onSixDofEvent(event);
|
||||
}
|
||||
|
@ -1075,30 +1082,80 @@ static GdkFilterReturn GdkSpnavFilter(GdkXEvent *gdkXEvent, GdkEvent *gdkEvent,
|
|||
}
|
||||
switch(spnavEvent.button.bnum) {
|
||||
case 0: event.button = SixDofEvent::Button::FIT; break;
|
||||
default: return GDK_FILTER_REMOVE;
|
||||
default: return;
|
||||
}
|
||||
event.shiftDown = xEvent->xmotion.state & ShiftMask;
|
||||
event.controlDown = xEvent->xmotion.state & ControlMask;
|
||||
event.shiftDown = shiftDown;
|
||||
event.controlDown = controlDown;
|
||||
if(window->onSixDofEvent) {
|
||||
window->onSixDofEvent(event);
|
||||
}
|
||||
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_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) {
|
||||
std::shared_ptr<WindowImplGtk> windowImpl =
|
||||
std::static_pointer_cast<WindowImplGtk>(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(spnav_x11_open(gdk_x11_get_default_xdisplay(),
|
||||
gdk_x11_window_get_xid(gdkWindow->gobj())) != -1) {
|
||||
gdkWindow->add_filter(GdkSpnavFilter, windowImpl.get());
|
||||
spnav_x11_open(gdk_x11_get_default_xdisplay(),
|
||||
gdk_x11_window_get_xid(gdkWindow->gobj()));
|
||||
} else if(spnav_open() != -1) {
|
||||
g_io_add_watch(g_io_channel_unix_new(spnav_fd()), G_IO_IN,
|
||||
ConsumeSpnavQueue, windowImpl.get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(GDK_WINDOWING_WAYLAND)
|
||||
if(GDK_IS_WAYLAND_DISPLAY(gdkWindow->get_display()->gobj())) {
|
||||
if(spnav_open() != -1) {
|
||||
g_io_add_watch(g_io_channel_unix_new(spnav_fd()), G_IO_IN,
|
||||
ConsumeSpnavQueue, windowImpl.get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
#else
|
||||
void Request3DConnexionEventsForWindow(WindowRef window) {}
|
||||
#endif
|
||||
|
@ -1303,7 +1360,7 @@ public:
|
|||
return;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,7 +286,8 @@ public:
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -358,18 +359,25 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
- (void)didEdit:(NSString *)text;
|
||||
|
||||
@property double scrollerMin;
|
||||
@property double scrollerMax;
|
||||
@property double scrollerSize;
|
||||
@property double pageSize;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SSView
|
||||
{
|
||||
NSTrackingArea *trackingArea;
|
||||
NSTextField *editor;
|
||||
double magnificationGestureCurrentZ;
|
||||
double rotationGestureCurrent;
|
||||
Point2d trackpadPositionShift;
|
||||
bool inTrackpadScrollGesture;
|
||||
Platform::Window::Kind kind;
|
||||
}
|
||||
|
||||
@synthesize acceptsFirstResponder;
|
||||
|
||||
- (id)initWithFrame:(NSRect)frameRect {
|
||||
- (id)initWithKind:(Platform::Window::Kind)aKind {
|
||||
NSOpenGLPixelFormatAttribute attrs[] = {
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
|
@ -377,7 +385,7 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
0
|
||||
};
|
||||
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.wantsLayer = YES;
|
||||
editor = [[NSTextField alloc] init];
|
||||
|
@ -387,6 +395,18 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
editor.bezeled = NO;
|
||||
editor.target = self;
|
||||
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;
|
||||
}
|
||||
|
@ -427,9 +447,9 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
- (Platform::MouseEvent)convertMouseEvent:(NSEvent *)nsEvent {
|
||||
Platform::MouseEvent event = {};
|
||||
|
||||
NSPoint nsPoint = [self convertPoint:nsEvent.locationInWindow fromView:self];
|
||||
NSPoint nsPoint = [self convertPoint:nsEvent.locationInWindow fromView:nil];
|
||||
event.x = nsPoint.x;
|
||||
event.y = self.bounds.size.height - nsPoint.y;
|
||||
event.y = nsPoint.y;
|
||||
|
||||
NSUInteger nsFlags = [nsEvent modifierFlags];
|
||||
if(nsFlags & NSEventModifierFlagShift) event.shiftDown = true;
|
||||
|
@ -553,15 +573,58 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
using Platform::MouseEvent;
|
||||
|
||||
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;
|
||||
|
||||
bool isPrecise = [nsEvent hasPreciseScrollingDeltas];
|
||||
event.scrollDelta = [nsEvent scrollingDeltaY] / (isPrecise ? 50 : 5);
|
||||
|
||||
if(receiver->onMouseEvent) {
|
||||
receiver->onMouseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent *)nsEvent {
|
||||
using Platform::MouseEvent;
|
||||
|
@ -638,6 +701,50 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
[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;
|
||||
|
||||
- (void)startEditing:(NSString *)text at:(NSPoint)origin withHeight:(double)fontHeight
|
||||
|
@ -698,11 +805,27 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) {
|
|||
}
|
||||
|
||||
@synthesize scrollerMin;
|
||||
@synthesize scrollerMax;
|
||||
@synthesize scrollerSize;
|
||||
@synthesize pageSize;
|
||||
|
||||
- (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) {
|
||||
double pos = scrollerMin + [sender doubleValue] * (scrollerMax - scrollerMin);
|
||||
receiver->onScrollbarAdjusted(pos);
|
||||
}
|
||||
}
|
||||
|
@ -769,7 +892,7 @@ public:
|
|||
NSString *nsToolTip;
|
||||
|
||||
WindowImplCocoa(Window::Kind kind, std::shared_ptr<WindowImplCocoa> parentWindow) {
|
||||
ssView = [[SSView alloc] init];
|
||||
ssView = [[SSView alloc] initWithKind:kind];
|
||||
ssView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
ssView.receiver = this;
|
||||
|
||||
|
@ -962,21 +1085,22 @@ public:
|
|||
|
||||
void ConfigureScrollbar(double min, double max, double pageSize) override {
|
||||
ssView.scrollerMin = min;
|
||||
ssView.scrollerMax = max - pageSize;
|
||||
[nsScroller setKnobProportion:(pageSize / (ssView.scrollerMax - ssView.scrollerMin))];
|
||||
ssView.scrollerSize = max + 1 - min;
|
||||
ssView.pageSize = pageSize;
|
||||
nsScroller.knobProportion = pageSize / ssView.scrollerSize;
|
||||
nsScroller.hidden = pageSize >= ssView.scrollerSize;
|
||||
}
|
||||
|
||||
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 +
|
||||
[nsScroller doubleValue] * (ssView.scrollerMax - ssView.scrollerMin);
|
||||
nsScroller.doubleValue * (ssView.scrollerSize - ssView.pageSize);
|
||||
}
|
||||
|
||||
void SetScrollbarPosition(double pos) override {
|
||||
if(pos > ssView.scrollerMax)
|
||||
pos = ssView.scrollerMax;
|
||||
if(GetScrollbarPosition() == pos)
|
||||
return;
|
||||
[nsScroller setDoubleValue:(pos / (ssView.scrollerMax - ssView.scrollerMin))];
|
||||
nsScroller.doubleValue = (pos - ssView.scrollerMin) / ( ssView.scrollerSize - ssView.pageSize);
|
||||
}
|
||||
|
||||
void Invalidate() override {
|
||||
|
@ -1426,9 +1550,22 @@ void OpenInBrowser(const std::string &url) {
|
|||
- (IBAction)preferences:(id)sender;
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||
|
||||
@property BOOL exiting;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SSApplicationDelegate
|
||||
|
||||
@synthesize exiting;
|
||||
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
self.exiting = false;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)preferences:(id)sender {
|
||||
if (!SS.GW.showTextWindow) {
|
||||
SolveSpace::SS.GW.MenuView(SolveSpace::Command::SHOW_TEXT_WND);
|
||||
|
@ -1443,12 +1580,27 @@ void OpenInBrowser(const std::string &url) {
|
|||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
[[[NSApp mainWindow] delegate] windowShouldClose:[NSApp mainWindow]];
|
||||
return NSTerminateCancel;
|
||||
if(!SS.unsaved) {
|
||||
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 {
|
||||
SolveSpace::SS.MenuFile(SolveSpace::Command::EXIT);
|
||||
[NSApp replyToApplicationShouldTerminate:SS.OkayToStartNewFile()];
|
||||
}
|
||||
@end
|
||||
|
||||
|
@ -1469,6 +1621,14 @@ std::vector<std::string> InitGui(int argc, char **argv) {
|
|||
ssDelegate = [[SSApplicationDelegate alloc] init];
|
||||
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];
|
||||
|
||||
NSArray *languages = NSLocale.preferredLanguages;
|
||||
|
@ -1487,9 +1647,11 @@ void RunGui() {
|
|||
}
|
||||
|
||||
void ExitGui() {
|
||||
[NSApp setDelegate:nil];
|
||||
if(!ssDelegate.exiting) {
|
||||
ssDelegate.exiting = true;
|
||||
[NSApp terminate:nil];
|
||||
}
|
||||
}
|
||||
|
||||
void ClearGui() {}
|
||||
|
||||
|
|
|
@ -1229,7 +1229,7 @@ public:
|
|||
sscheck(GetMonitorInfo(MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST), &mi));
|
||||
|
||||
// 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;
|
||||
|
||||
RECT mrc = mi.rcMonitor;
|
||||
|
@ -1583,11 +1583,6 @@ public:
|
|||
ofn.nMaxFile = sizeof(filenameWC) / sizeof(wchar_t);
|
||||
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY |
|
||||
OFN_OVERWRITEPROMPT;
|
||||
if(isSaveDialog) {
|
||||
SetTitle(C_("title", "Save File"));
|
||||
} else {
|
||||
SetTitle(C_("title", "Open File"));
|
||||
}
|
||||
}
|
||||
|
||||
void SetTitle(std::string title) override {
|
||||
|
@ -1640,13 +1635,14 @@ public:
|
|||
}
|
||||
|
||||
bool RunModal() override {
|
||||
if(isSaveDialog) {
|
||||
SetTitle(C_("title", "Save File"));
|
||||
if(GetFilename().IsEmpty()) {
|
||||
SetFilename(Path::From(_("untitled")));
|
||||
}
|
||||
|
||||
if(isSaveDialog) {
|
||||
return GetSaveFileNameW(&ofn) == TRUE;
|
||||
} else {
|
||||
SetTitle(C_("title", "Open File"));
|
||||
return GetOpenFileNameW(&ofn) == TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,11 +190,11 @@ void SurfaceRenderer::DrawFaces(const SMesh &m, const std::vector<uint32_t> &fac
|
|||
void SurfaceRenderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
|
||||
const Vector &o, const Vector &u, const Vector &v,
|
||||
const Point2d &ta, const Point2d &tb, hFill hcf) {
|
||||
ssassert(false, "Not implemented");
|
||||
dbp("Not implemented");
|
||||
}
|
||||
|
||||
void SurfaceRenderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
|
||||
ssassert(false, "Not implemented");
|
||||
dbp("Not implemented");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
26
src/sketch.h
|
@ -198,6 +198,9 @@ public:
|
|||
// For drawings in 2d
|
||||
WORKPLANE_BY_POINT_ORTHO = 6000,
|
||||
WORKPLANE_BY_LINE_SEGMENTS = 6001,
|
||||
WORKPLANE_BY_POINT_NORMAL = 6002,
|
||||
//WORKPLANE_BY_POINT_FACE = 6003,
|
||||
//WORKPLANE_BY_FACE = 6004,
|
||||
// For extrudes, translates, and rotates
|
||||
ONE_SIDED = 7000,
|
||||
TWO_SIDED = 7001
|
||||
|
@ -266,6 +269,7 @@ public:
|
|||
void Generate(EntityList *entity, ParamList *param);
|
||||
bool IsSolvedOkay();
|
||||
void TransformImportedBy(Vector t, Quaternion q);
|
||||
bool IsTriangleMeshAssembly() const;
|
||||
bool IsForcedToMeshBySource() const;
|
||||
bool IsForcedToMesh() const;
|
||||
// When a request generates entities from entities, and the source
|
||||
|
@ -323,6 +327,7 @@ public:
|
|||
void DrawPolyError(Canvas *canvas);
|
||||
void DrawFilledPaths(Canvas *canvas);
|
||||
void DrawContourAreaLabels(Canvas *canvas);
|
||||
bool ShouldDrawExploded() const;
|
||||
|
||||
SPolygon GetPolygon();
|
||||
|
||||
|
@ -368,6 +373,7 @@ public:
|
|||
std::string font;
|
||||
Platform::Path file;
|
||||
double aspectRatio;
|
||||
int groupRequestIndex;
|
||||
|
||||
static hParam AddParam(ParamList *param, hParam hp);
|
||||
void Generate(EntityList *entity, ParamList *param);
|
||||
|
@ -591,6 +597,10 @@ public:
|
|||
beziers.l.Clear();
|
||||
edges.l.Clear();
|
||||
}
|
||||
|
||||
bool ShouldDrawExploded() const;
|
||||
Vector ExplodeOffset() const;
|
||||
Vector PointGetDrawNum() const;
|
||||
};
|
||||
|
||||
class EntReqTable {
|
||||
|
@ -673,7 +683,10 @@ public:
|
|||
CURVE_CURVE_TANGENT = 125,
|
||||
EQUAL_RADIUS = 130,
|
||||
WHERE_DRAGGED = 200,
|
||||
|
||||
ARC_ARC_LEN_RATIO = 210,
|
||||
ARC_LINE_LEN_RATIO = 211,
|
||||
ARC_ARC_DIFFERENCE = 212,
|
||||
ARC_LINE_DIFFERENCE = 213,
|
||||
COMMENT = 1000
|
||||
};
|
||||
|
||||
|
@ -757,7 +770,7 @@ public:
|
|||
Vector p0, Vector p1, Vector pt, double salient);
|
||||
void DoArcForAngle(Canvas *canvas, Canvas::hStroke hcs,
|
||||
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,
|
||||
Vector p, Vector dir, Vector n, double width, double angle, double da);
|
||||
void DoLineWithArrows(Canvas *canvas, Canvas::hStroke hcs,
|
||||
|
@ -779,6 +792,8 @@ public:
|
|||
|
||||
std::string DescriptionString() const;
|
||||
|
||||
bool ShouldDrawExploded() const;
|
||||
|
||||
static hConstraint AddConstraint(Constraint *c, bool rememberForUndo = true);
|
||||
static void MenuConstrain(Command id);
|
||||
static void DeleteAllConstraintsFor(Constraint::Type type, hEntity entityA, hEntity ptA);
|
||||
|
@ -884,11 +899,14 @@ public:
|
|||
double width;
|
||||
int zIndex;
|
||||
bool exportable;
|
||||
StipplePattern stippleType;
|
||||
} Default;
|
||||
static const Default Defaults[];
|
||||
|
||||
static std::string CnfColor(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 CnfPrefixToName(const std::string &prefix);
|
||||
static std::string CnfExportable(const std::string &prefix);
|
||||
|
@ -918,7 +936,11 @@ public:
|
|||
static bool Exportable(int hs);
|
||||
static hStyle ForEntity(hEntity he);
|
||||
static StipplePattern PatternType(hStyle hs);
|
||||
static double StippleScale(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;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ void SolveSpaceUI::Init() {
|
|||
Platform::SettingsRef settings = Platform::GetSettings();
|
||||
|
||||
SS.tangentArcRadius = 10.0;
|
||||
SS.explodeDistance = 1.0;
|
||||
|
||||
// Then, load the registry settings.
|
||||
// Default list of colors for the model material
|
||||
|
@ -104,6 +105,7 @@ void SolveSpaceUI::Init() {
|
|||
exportCanvas.dy = settings->ThawFloat("ExportCanvas_Dy", 5.0);
|
||||
// Extra parameters when exporting G code
|
||||
gCode.depth = settings->ThawFloat("GCode_Depth", 10.0);
|
||||
gCode.safeHeight = settings->ThawFloat("GCode_SafeHeight", 5.0);
|
||||
gCode.passes = settings->ThawInt("GCode_Passes", 1);
|
||||
gCode.feed = settings->ThawFloat("GCode_Feed", 10.0);
|
||||
gCode.plungeFeed = settings->ThawFloat("GCode_PlungeFeed", 10.0);
|
||||
|
@ -315,6 +317,7 @@ void SolveSpaceUI::ScheduleAutosave() {
|
|||
double SolveSpaceUI::MmPerUnit() {
|
||||
switch(viewUnits) {
|
||||
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::MM: return 1.0;
|
||||
}
|
||||
|
@ -323,14 +326,47 @@ double SolveSpaceUI::MmPerUnit() {
|
|||
const char *SolveSpaceUI::UnitName() {
|
||||
switch(viewUnits) {
|
||||
case Unit::INCHES: return "in";
|
||||
case Unit::FEET_INCHES: return "in";
|
||||
case Unit::METERS: return "m";
|
||||
case Unit::MM: return "mm";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string SolveSpaceUI::MmToString(double v) {
|
||||
std::string SolveSpaceUI::MmToString(double v, bool editable) {
|
||||
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();
|
||||
double minimum = 0.5 * pow(10,-digits);
|
||||
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) {
|
||||
// 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) {
|
||||
case 0:
|
||||
case 1:
|
||||
|
@ -394,17 +430,22 @@ std::string SolveSpaceUI::MmToStringSI(double v, int dim) {
|
|||
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)));
|
||||
std::string unit;
|
||||
if(fabs(v) > 0.0) {
|
||||
int sdeg = 0;
|
||||
std::tie(sdeg, unit) =
|
||||
(viewUnits == Unit::INCHES)
|
||||
inches
|
||||
? SelectSIPrefixInch(vdeg/dim)
|
||||
: SelectSIPrefixMm(vdeg, 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));
|
||||
return ssprintf("%.*g%s%s%s", pdeg + UnitDigitsAfterDecimal(), v,
|
||||
compact ? "" : " ", unit.c_str(), DimToString(dim));
|
||||
|
@ -434,10 +475,11 @@ int SolveSpaceUI::GetMaxSegments() {
|
|||
return maxSegments;
|
||||
}
|
||||
int SolveSpaceUI::UnitDigitsAfterDecimal() {
|
||||
return (viewUnits == Unit::INCHES) ? afterDecimalInch : afterDecimalMm;
|
||||
return (viewUnits == Unit::INCHES || viewUnits == Unit::FEET_INCHES) ?
|
||||
afterDecimalInch : afterDecimalMm;
|
||||
}
|
||||
void SolveSpaceUI::SetUnitDigitsAfterDecimal(int v) {
|
||||
if(viewUnits == Unit::INCHES) {
|
||||
if(viewUnits == Unit::INCHES || viewUnits == Unit::FEET_INCHES) {
|
||||
afterDecimalInch = v;
|
||||
} else {
|
||||
afterDecimalMm = v;
|
||||
|
@ -764,7 +806,11 @@ void SolveSpaceUI::MenuAnalyze(Command id) {
|
|||
SS.TW.stepDim.isDistance =
|
||||
(c->type != Constraint::Type::ANGLE) &&
|
||||
(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.screen = TextWindow::Screen::STEP_DIMENSION;
|
||||
|
||||
|
@ -1026,12 +1072,14 @@ void SolveSpaceUI::Clear() {
|
|||
GW.showGridMenuItem = NULL;
|
||||
GW.dimSolidModelMenuItem = NULL;
|
||||
GW.perspectiveProjMenuItem = NULL;
|
||||
GW.explodeMenuItem = NULL;
|
||||
GW.showToolbarMenuItem = NULL;
|
||||
GW.showTextWndMenuItem = NULL;
|
||||
GW.fullScreenMenuItem = NULL;
|
||||
GW.unitsMmMenuItem = NULL;
|
||||
GW.unitsMetersMenuItem = NULL;
|
||||
GW.unitsInchesMenuItem = NULL;
|
||||
GW.unitsFeetInchesMenuItem = NULL;
|
||||
GW.inWorkplaneMenuItem = NULL;
|
||||
GW.in3dMenuItem = NULL;
|
||||
GW.undoMenuItem = NULL;
|
||||
|
|
|
@ -138,7 +138,8 @@ enum class Command : uint32_t;
|
|||
enum class Unit : uint32_t {
|
||||
MM = 0,
|
||||
INCHES,
|
||||
METERS
|
||||
METERS,
|
||||
FEET_INCHES
|
||||
};
|
||||
|
||||
template<class Key, class T>
|
||||
|
@ -597,6 +598,7 @@ public:
|
|||
} exportCanvas;
|
||||
struct {
|
||||
double depth;
|
||||
double safeHeight;
|
||||
int passes;
|
||||
double feed;
|
||||
double plungeFeed;
|
||||
|
@ -608,8 +610,10 @@ public:
|
|||
int afterDecimalDegree;
|
||||
bool useSIPrefixes;
|
||||
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 DegreeToString(double v);
|
||||
double ExprToMm(Expr *e);
|
||||
|
@ -812,6 +816,7 @@ public:
|
|||
void ImportDxf(const Platform::Path &file);
|
||||
void ImportDwg(const Platform::Path &file);
|
||||
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 Sketch SK;
|
||||
|
|
|
@ -29,7 +29,7 @@ void SCurve::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) const {
|
|||
}
|
||||
}
|
||||
|
||||
// We will be inserting other curve verticies into our curves to split them.
|
||||
// We will be inserting other curve vertices into our curves to split them.
|
||||
// This is helpful when curved surfaces become tangent along a trim and the
|
||||
// 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
|
||||
|
@ -521,20 +521,19 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
|
|||
SEdgeList inter = {};
|
||||
|
||||
SSurface *ss;
|
||||
SCurve *sc;
|
||||
for(sc = into->curve.First(); sc; sc = into->curve.NextAfter(sc)) {
|
||||
if(sc->source != SCurve::Source::INTERSECTION) continue;
|
||||
for(SCurve &sc : into->curve) {
|
||||
if(sc.source != SCurve::Source::INTERSECTION) continue;
|
||||
if(opA) {
|
||||
if(sc->surfA != h) continue;
|
||||
ss = shb->surface.FindById(sc->surfB);
|
||||
if(sc.surfA != h) continue;
|
||||
ss = shb->surface.FindById(sc.surfB);
|
||||
} else {
|
||||
if(sc->surfB != h) continue;
|
||||
ss = sha->surface.FindById(sc->surfA);
|
||||
if(sc.surfB != h) continue;
|
||||
ss = sha->surface.FindById(sc.surfA);
|
||||
}
|
||||
int i;
|
||||
for(i = 1; i < sc->pts.n; i++) {
|
||||
Vector a = sc->pts[i-1].p,
|
||||
b = sc->pts[i].p;
|
||||
for(i = 1; i < sc.pts.n; i++) {
|
||||
Vector a = sc.pts[i-1].p,
|
||||
b = sc.pts[i].p;
|
||||
|
||||
Point2d auv, buv;
|
||||
ss->ClosestPointTo(a, &(auv.x), &(auv.y));
|
||||
|
@ -560,9 +559,9 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *parent,
|
|||
bkwds = !bkwds;
|
||||
}
|
||||
if(bkwds) {
|
||||
inter.AddEdge(tb, ta, sc->h.v, 1);
|
||||
inter.AddEdge(tb, ta, sc.h.v, 1);
|
||||
} 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++) {
|
||||
SSurface *sa = &surface[i];
|
||||
|
||||
SSurface *sb;
|
||||
for(sb = agnst->surface.First(); sb; sb = agnst->surface.NextAfter(sb)){
|
||||
for(SSurface &sb : agnst->surface){
|
||||
// Intersect every surface from our shell against every surface
|
||||
// from agnst; this will add zero or more curves to the curve
|
||||
// list for into.
|
||||
sa->IntersectAgainst(sb, this, agnst, into);
|
||||
sa->IntersectAgainst(&sb, this, agnst, into);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SShell::CleanupAfterBoolean() {
|
||||
SSurface *ss;
|
||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
||||
ss->edges.Clear();
|
||||
for(SSurface &ss : surface) {
|
||||
ss.edges.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -734,10 +731,9 @@ void SShell::CleanupAfterBoolean() {
|
|||
// by their new IDs.
|
||||
//-----------------------------------------------------------------------------
|
||||
void SShell::RewriteSurfaceHandlesForCurves(SShell *a, SShell *b) {
|
||||
SCurve *sc;
|
||||
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
|
||||
sc->surfA = sc->GetSurfaceA(a, b)->newH,
|
||||
sc->surfB = sc->GetSurfaceB(a, b)->newH;
|
||||
for(SCurve &sc : curve) {
|
||||
sc.surfA = sc.GetSurfaceA(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
|
||||
// came from, but assign it a new ID.
|
||||
curve.ReserveMore(a->curve.n + b->curve.n);
|
||||
SCurve *c, cn;
|
||||
SCurve cn;
|
||||
for(i = 0; i < 2; i++) {
|
||||
ab = (i == 0) ? a : b;
|
||||
for(c = ab->curve.First(); c; c = ab->curve.NextAfter(c)) {
|
||||
cn = SCurve::FromTransformationOf(c, t, q, 1.0);
|
||||
for(SCurve &c : ab->curve) {
|
||||
cn = SCurve::FromTransformationOf(&c, t, q, 1.0);
|
||||
cn.source = (i == 0) ? SCurve::Source::A : SCurve::Source::B;
|
||||
// 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.
|
||||
c->newH = curve.AddAndAssignId(&cn);
|
||||
c.newH = curve.AddAndAssignId(&cn);
|
||||
}
|
||||
}
|
||||
|
||||
// Likewise copy over all the surfaces.
|
||||
surface.ReserveMore(a->surface.n + b->surface.n);
|
||||
SSurface *s, sn;
|
||||
SSurface sn;
|
||||
for(i = 0; i < 2; i++) {
|
||||
ab = (i == 0) ? a : b;
|
||||
for(s = ab->surface.First(); s; s = ab->surface.NextAfter(s)) {
|
||||
sn = SSurface::FromTransformationOf(s, t, q, 1.0, /*includingTrims=*/true);
|
||||
for(SSurface &s : ab->surface) {
|
||||
sn = SSurface::FromTransformationOf(&s, t, q, 1.0, /*includingTrims=*/true);
|
||||
// All the trim curve IDs get rewritten; we know the new handles
|
||||
// to the curves since we recorded them in the previous step.
|
||||
STrimBy *stb;
|
||||
for(stb = sn.trim.First(); stb; stb = sn.trim.NextAfter(stb)) {
|
||||
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);
|
||||
|
||||
// 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.
|
||||
a->CopyCurvesSplitAgainst(/*opA=*/true, b, 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).
|
||||
a->MakeIntersectionCurvesAgainst(b, this);
|
||||
|
||||
SCurve *sc;
|
||||
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
|
||||
SSurface *srfA = sc->GetSurfaceA(a, b),
|
||||
*srfB = sc->GetSurfaceB(a, b);
|
||||
for(SCurve &sc : curve) {
|
||||
SSurface *srfA = sc.GetSurfaceA(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
|
||||
|
|
|
@ -817,7 +817,7 @@ void SCurve::RemoveShortSegments(SSurface *srfA, SSurface *srfB) {
|
|||
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)
|
||||
tprev = t = tnext = 0;
|
||||
if (isExact) {
|
||||
|
|
|
@ -58,10 +58,9 @@ void SShell::MergeCoincidentSurfaces() {
|
|||
|
||||
// All the references to this surface get replaced with the
|
||||
// new srf
|
||||
SCurve *sc;
|
||||
for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) {
|
||||
if(sc->surfA == sj->h) sc->surfA = si->h;
|
||||
if(sc->surfB == sj->h) sc->surfB = si->h;
|
||||
for(SCurve &sc : curve) {
|
||||
if(sc.surfA == sj->h) sc.surfA = si->h;
|
||||
if(sc.surfB == sj->h) sc.surfB = si->h;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -381,9 +381,8 @@ void SShell::AllPointsIntersecting(Vector a, Vector b,
|
|||
List<SInter> *il,
|
||||
bool asSegment, bool trimmed, bool inclTangent)
|
||||
{
|
||||
SSurface *ss;
|
||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
||||
ss->AllPointsIntersecting(a, b, il,
|
||||
for(SSurface &ss : surface) {
|
||||
ss.AllPointsIntersecting(a, b, il,
|
||||
asSegment, trimmed, inclTangent);
|
||||
}
|
||||
}
|
||||
|
@ -434,11 +433,10 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
|
|||
// First, check for edge-on-edge
|
||||
int edge_inters = 0;
|
||||
Vector inter_surf_n[2], inter_edge_n[2];
|
||||
SSurface *srf;
|
||||
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
|
||||
if(srf->LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
||||
for(SSurface &srf : surface) {
|
||||
if(srf.LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
||||
|
||||
SEdgeList *sel = &(srf->edges);
|
||||
SEdgeList *sel = &(srf.edges);
|
||||
SEdge *se;
|
||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||
if((ea.Equals(se->a) && eb.Equals(se->b)) ||
|
||||
|
@ -448,9 +446,9 @@ bool SShell::ClassifyEdge(Class *indir, Class *outdir,
|
|||
if(edge_inters < 2) {
|
||||
// Edge-on-edge case
|
||||
Point2d pm;
|
||||
srf->ClosestPointTo(p, &pm, /*mustConverge=*/false);
|
||||
srf.ClosestPointTo(p, &pm, /*mustConverge=*/false);
|
||||
// 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
|
||||
// intersecting surface) at the intersection point, pointing
|
||||
// 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
|
||||
// the additional error from the line intersection.
|
||||
|
||||
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
|
||||
if(srf->LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
||||
for(SSurface &srf : surface) {
|
||||
if(srf.LineEntirelyOutsideBbox(ea, eb, /*asSegment=*/true)) continue;
|
||||
|
||||
Point2d puv;
|
||||
srf->ClosestPointTo(p, &(puv.x), &(puv.y), /*mustConverge=*/false);
|
||||
Vector pp = srf->PointAt(puv);
|
||||
srf.ClosestPointTo(p, &(puv.x), &(puv.y), /*mustConverge=*/false);
|
||||
Vector pp = srf.PointAt(puv);
|
||||
|
||||
if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue;
|
||||
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;
|
||||
|
||||
// Edge-on-face (unless edge-on-edge above superceded)
|
||||
Point2d pin, pout;
|
||||
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_in), &pin, /*mustConverge=*/false);
|
||||
srf.ClosestPointTo(p.Plus(edge_n_out), &pout, /*mustConverge=*/false);
|
||||
|
||||
Vector surf_n_in = srf->NormalAt(pin),
|
||||
surf_n_out = srf->NormalAt(pout);
|
||||
Vector surf_n_in = srf.NormalAt(pin),
|
||||
surf_n_out = srf.NormalAt(pout);
|
||||
|
||||
*indir = ClassifyRegion(edge_n_in, surf_n_in, surf_n);
|
||||
*outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n);
|
||||
|
|
|
@ -1038,35 +1038,31 @@ void SShell::MakeFromTransformationOf(SShell *a,
|
|||
{
|
||||
booleanFailed = false;
|
||||
surface.ReserveMore(a->surface.n);
|
||||
SSurface *s;
|
||||
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
|
||||
for(SSurface &s : a->surface) {
|
||||
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
|
||||
}
|
||||
|
||||
curve.ReserveMore(a->curve.n);
|
||||
SCurve *c;
|
||||
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
|
||||
for(SCurve &c : a->curve) {
|
||||
SCurve n;
|
||||
n = SCurve::FromTransformationOf(c, t, q, scale);
|
||||
n = SCurve::FromTransformationOf(&c, t, q, scale);
|
||||
curve.Add(&n); // keeping the old ID
|
||||
}
|
||||
}
|
||||
|
||||
void SShell::MakeEdgesInto(SEdgeList *sel) {
|
||||
SSurface *s;
|
||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
||||
s->MakeEdgesInto(this, sel, SSurface::MakeAs::XYZ);
|
||||
for(SSurface &s : surface) {
|
||||
s.MakeEdgesInto(this, sel, SSurface::MakeAs::XYZ);
|
||||
}
|
||||
}
|
||||
|
||||
void SShell::MakeSectionEdgesInto(Vector n, double d, SEdgeList *sel, SBezierList *sbl)
|
||||
{
|
||||
SSurface *s;
|
||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
||||
if(s->CoincidentWithPlane(n, d)) {
|
||||
s->MakeSectionEdgesInto(this, sel, sbl);
|
||||
for(SSurface &s : surface) {
|
||||
if(s.CoincidentWithPlane(n, d)) {
|
||||
s.MakeSectionEdgesInto(this, sel, sbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1088,15 +1084,13 @@ bool SShell::IsEmpty() const {
|
|||
}
|
||||
|
||||
void SShell::Clear() {
|
||||
SSurface *s;
|
||||
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
||||
s->Clear();
|
||||
for(SSurface &s : surface) {
|
||||
s.Clear();
|
||||
}
|
||||
surface.Clear();
|
||||
|
||||
SCurve *c;
|
||||
for(c = curve.First(); c; c = curve.NextAfter(c)) {
|
||||
c->Clear();
|
||||
for(SCurve &c : curve) {
|
||||
c.Clear();
|
||||
}
|
||||
curve.Clear();
|
||||
}
|
||||
|
|
|
@ -23,20 +23,20 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
|
|||
// Now we have to piecewise linearize the curve. If there's already an
|
||||
// identical curve in the shell, then follow that pwl exactly, otherwise
|
||||
// calculate from scratch.
|
||||
SCurve split, *existing = NULL, *se;
|
||||
SCurve split, *existing = NULL;
|
||||
SBezier sbrev = *sb;
|
||||
sbrev.Reverse();
|
||||
bool backwards = false;
|
||||
#pragma omp critical(into)
|
||||
{
|
||||
for(se = into->curve.First(); se; se = into->curve.NextAfter(se)) {
|
||||
if(se->isExact) {
|
||||
if(sb->Equals(&(se->exact))) {
|
||||
existing = se;
|
||||
for(SCurve &se : into->curve) {
|
||||
if(se.isExact) {
|
||||
if(sb->Equals(&(se.exact))) {
|
||||
existing = &se;
|
||||
break;
|
||||
}
|
||||
if(sbrev.Equals(&(se->exact))) {
|
||||
existing = se;
|
||||
if(sbrev.Equals(&(se.exact))) {
|
||||
existing = &se;
|
||||
backwards = true;
|
||||
break;
|
||||
}
|
||||
|
@ -332,15 +332,14 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
|
|||
shext = agnstA;
|
||||
}
|
||||
bool foundExact = false;
|
||||
SCurve *sc;
|
||||
for(sc = shext->curve.First(); sc; sc = shext->curve.NextAfter(sc)) {
|
||||
if(sc->source == SCurve::Source::INTERSECTION) continue;
|
||||
if(!sc->isExact) continue;
|
||||
if((sc->surfA != sext->h) && (sc->surfB != sext->h)) continue;
|
||||
for(SCurve &sc : shext->curve) {
|
||||
if(sc.source == SCurve::Source::INTERSECTION) continue;
|
||||
if(!sc.isExact) continue;
|
||||
if((sc.surfA != sext->h) && (sc.surfB != sext->h)) continue;
|
||||
// we have a curve belonging to the curved surface and not the plane.
|
||||
// does it lie completely in the plane?
|
||||
if(splane->ContainsPlaneCurve(sc)) {
|
||||
SBezier bezier = sc->exact;
|
||||
if(splane->ContainsPlaneCurve(&sc)) {
|
||||
SBezier bezier = sc.exact;
|
||||
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
|
||||
foundExact = true;
|
||||
}
|
||||
|
@ -571,10 +570,9 @@ bool SSurface::ContainsPlaneCurve(SCurve *sc) const {
|
|||
void SShell::MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
|
||||
SEdgeList *el, SShell *useCurvesFrom)
|
||||
{
|
||||
SSurface *ss;
|
||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
||||
if(proto->CoincidentWith(ss, sameNormal)) {
|
||||
ss->MakeEdgesInto(this, el, SSurface::MakeAs::XYZ, useCurvesFrom);
|
||||
for(SSurface &ss : surface) {
|
||||
if(proto->CoincidentWith(&ss, sameNormal)) {
|
||||
ss.MakeEdgesInto(this, el, SSurface::MakeAs::XYZ, useCurvesFrom);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -426,7 +426,7 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
|
|||
if (i == l.n-1) {
|
||||
end = true;
|
||||
}
|
||||
if (end) { // triangulate the fan and tag the verticies
|
||||
if (end) { // triangulate the fan and tag the vertices
|
||||
if (j > 3) {
|
||||
Vector center = l[pstart+1].p.Plus(l[pstart+j-1].p).ScaledBy(0.5);
|
||||
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);
|
||||
|
||||
// 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.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.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
|
||||
holes.AddEdge(a, b);
|
||||
} else {
|
||||
if (prev_flag) // add our left neighbots right edge
|
||||
if (prev_flag) // add our left neighbors right edge
|
||||
holes.AddEdge(a, d);
|
||||
if (bottom[j]) // add our bottom neighbors top edge
|
||||
holes.AddEdge(b, a);
|
||||
|
|
114
src/style.cpp
|
@ -8,22 +8,22 @@
|
|||
#include "solvespace.h"
|
||||
|
||||
const Style::Default Style::Defaults[] = {
|
||||
{ { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4, true },
|
||||
{ { CONSTRUCTION }, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, 0, false },
|
||||
{ { INACTIVE_GRP }, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, 3, true },
|
||||
{ { DATUM }, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, 0, true },
|
||||
{ { SOLID_EDGE }, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, 2, true },
|
||||
{ { CONSTRAINT }, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, 0, true },
|
||||
{ { SELECTED }, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, 0, true },
|
||||
{ { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0, true },
|
||||
{ { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0, true },
|
||||
{ { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0, true },
|
||||
{ { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0, true },
|
||||
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0, true },
|
||||
{ { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0, true },
|
||||
{ { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1, true },
|
||||
{ { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5, true },
|
||||
{ { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0, 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, StipplePattern::CONTINUOUS },
|
||||
{ { 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, StipplePattern::CONTINUOUS },
|
||||
{ { 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, StipplePattern::CONTINUOUS },
|
||||
{ { 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, StipplePattern::CONTINUOUS },
|
||||
{ { 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, StipplePattern::CONTINUOUS },
|
||||
{ { 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, StipplePattern::CONTINUOUS },
|
||||
{ { 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, StipplePattern::DASH },
|
||||
{ { 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, StipplePattern::CONTINUOUS }
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
s->filled = false;
|
||||
s->fillColor = RGBf(0.3, 0.3, 0.3);
|
||||
s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? StipplePattern::DASH
|
||||
: StipplePattern::CONTINUOUS;
|
||||
s->stippleScale = 15.0;
|
||||
s->stippleType = (factory)
|
||||
? d->stippleType
|
||||
: 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;
|
||||
}
|
||||
|
||||
|
@ -125,6 +136,8 @@ void Style::FreezeDefaultStyles(Platform::SettingsRef settings) {
|
|||
for(d = &(Defaults[0]); d->h.v; d++) {
|
||||
settings->FreezeColor(CnfColor(d->cnfPrefix), Color(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->FreezeBool(CnfExportable(d->cnfPrefix), Exportable(d->h.v));
|
||||
}
|
||||
|
@ -353,11 +366,62 @@ hStyle Style::ForEntity(hEntity he) {
|
|||
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) {
|
||||
Style *s = Get(hs);
|
||||
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) {
|
||||
Style *s = Get(hs);
|
||||
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) {
|
||||
Style::LoadFactoryDefaults();
|
||||
SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
|
||||
SS.GW.persistentDirty = true;
|
||||
}
|
||||
|
||||
void TextWindow::ScreenCreateCustomStyle(int link, uint32_t v) {
|
||||
|
@ -405,14 +470,13 @@ void TextWindow::ShowListOfStyles() {
|
|||
Printf(true, "%Ft color style-name");
|
||||
|
||||
bool darkbg = false;
|
||||
Style *s;
|
||||
for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) {
|
||||
for(Style &s : SK.style) {
|
||||
Printf(false, "%Bp %Bz %Bp %Fl%Ll%f%D%s%E",
|
||||
darkbg ? 'd' : 'a',
|
||||
&s->color,
|
||||
&s.color,
|
||||
darkbg ? 'd' : 'a',
|
||||
ScreenShowStyleInfo, s->h.v,
|
||||
s->DescriptionString().c_str());
|
||||
ScreenShowStyleInfo, s.h.v,
|
||||
s.DescriptionString().c_str());
|
||||
|
||||
darkbg = !darkbg;
|
||||
}
|
||||
|
@ -499,7 +563,7 @@ void TextWindow::ScreenChangeStyleMetric(int link, uint32_t v) {
|
|||
if(units == Style::UnitsAs::PIXELS) {
|
||||
edit_value = ssprintf("%.2f", val);
|
||||
} else {
|
||||
edit_value = SS.MmToString(val);
|
||||
edit_value = SS.MmToString(val, true);
|
||||
}
|
||||
SS.TW.ShowEditControl(col, edit_value);
|
||||
SS.TW.edit.style = hs;
|
||||
|
|
|
@ -123,13 +123,18 @@ void TextWindow::ShowListOfGroups() {
|
|||
sprintf(sdof, "%-3d", dof);
|
||||
}
|
||||
}
|
||||
std::string suffix;
|
||||
if(g->forceToMesh || g->IsTriangleMeshAssembly()) {
|
||||
suffix = " (∆)";
|
||||
}
|
||||
|
||||
bool ref = (g->h == Group::HGROUP_REFERENCES);
|
||||
Printf(false,
|
||||
"%Bp%Fd "
|
||||
"%Ft%s%Fb%D%f%Ll%s%E "
|
||||
"%Fb%s%D%f%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
|
||||
backgroundParity ? 'd' : 'a',
|
||||
// Link that activates the group
|
||||
|
@ -146,7 +151,9 @@ void TextWindow::ShowListOfGroups() {
|
|||
ok ? ((warn && SS.checkClosedContour) ? "err" : sdof) : "",
|
||||
ok ? "" : "ERR",
|
||||
// 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;
|
||||
backgroundParity = !backgroundParity;
|
||||
|
@ -299,6 +306,23 @@ void TextWindow::ScreenChangeGroupScale(int link, uint32_t v) {
|
|||
SS.TW.edit.meaning = Edit::GROUP_SCALE;
|
||||
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) {
|
||||
SS.UndoRemember();
|
||||
|
||||
|
@ -398,6 +422,26 @@ void TextWindow::ShowGroupInfo() {
|
|||
}
|
||||
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 ||
|
||||
g->type == Group::Type::REVOLVE || g->type == Group::Type::LINKED ||
|
||||
g->type == Group::Type::HELIX) {
|
||||
|
@ -451,7 +495,7 @@ void TextWindow::ShowGroupInfo() {
|
|||
&TextWindow::ScreenChangeGroupOption,
|
||||
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",
|
||||
&TextWindow::ScreenChangeGroupOption,
|
||||
g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
|
||||
|
@ -579,7 +623,7 @@ void TextWindow::ShowGroupSolveInfo() {
|
|||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -603,7 +647,7 @@ void TextWindow::ScreenStepDimFinish(int link, uint32_t v) {
|
|||
SS.TW.edit.meaning = Edit::STEP_DIM_FINISH;
|
||||
std::string edit_value;
|
||||
if(SS.TW.stepDim.isDistance) {
|
||||
edit_value = SS.MmToString(SS.TW.stepDim.finish);
|
||||
edit_value = SS.MmToString(SS.TW.stepDim.finish, true);
|
||||
} else {
|
||||
edit_value = ssprintf("%.3f", SS.TW.stepDim.finish);
|
||||
}
|
||||
|
@ -690,7 +734,7 @@ void TextWindow::ScreenChangeTangentArc(int link, uint32_t v) {
|
|||
switch(link) {
|
||||
case 'r': {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -789,6 +833,15 @@ void TextWindow::EditControlDone(std::string s) {
|
|||
}
|
||||
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: {
|
||||
Vector rgb;
|
||||
if(sscanf(s.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
|
||||
|
|
|
@ -203,7 +203,7 @@ const TextWindow::Color TextWindow::fgColors[] = {
|
|||
{ 'r', RGBi( 0, 0, 0) }, // Reverse : black
|
||||
{ 'x', RGBi(255, 20, 20) }, // Error : red
|
||||
{ 'i', RGBi( 0, 255, 255) }, // Info : cyan
|
||||
{ 'g', RGBi(160, 160, 160) },
|
||||
{ 'g', RGBi(128, 128, 128) }, // Disabled : gray
|
||||
{ 'b', RGBi(200, 200, 200) },
|
||||
{ 0, RGBi( 0, 0, 0) }
|
||||
};
|
||||
|
@ -348,8 +348,8 @@ void TextWindow::ClearScreen() {
|
|||
rows = 0;
|
||||
}
|
||||
|
||||
// This message was addded when someone had too many fonts for the text window
|
||||
// Scrolling seemed to be broken, but was actaully at the MAX_ROWS.
|
||||
// This message was added when someone had too many fonts for the text window
|
||||
// Scrolling seemed to be broken, but was actually at the MAX_ROWS.
|
||||
static const char* endString = " **** End of Text Screen ****";
|
||||
|
||||
void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
|
||||
|
|
|
@ -153,11 +153,18 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my, UiCanvas *canvas,
|
|||
double 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 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;
|
||||
|
||||
bool withinToolbar =
|
||||
|
|
27
src/ui.h
|
@ -82,6 +82,7 @@ enum class Command : uint32_t {
|
|||
SHOW_GRID,
|
||||
DIM_SOLID_MODEL,
|
||||
PERSPECTIVE_PROJ,
|
||||
EXPLODE_SKETCH,
|
||||
ONTO_WORKPLANE,
|
||||
NEAREST_ORTHO,
|
||||
NEAREST_ISO,
|
||||
|
@ -89,6 +90,7 @@ enum class Command : uint32_t {
|
|||
SHOW_TOOLBAR,
|
||||
SHOW_TEXT_WND,
|
||||
UNITS_INCHES,
|
||||
UNITS_FEET_INCHES,
|
||||
UNITS_MM,
|
||||
UNITS_METERS,
|
||||
FULL_SCREEN,
|
||||
|
@ -312,12 +314,14 @@ public:
|
|||
EXPORT_OFFSET = 110,
|
||||
CANVAS_SIZE = 111,
|
||||
G_CODE_DEPTH = 112,
|
||||
G_CODE_PASSES = 113,
|
||||
G_CODE_FEED = 114,
|
||||
G_CODE_PLUNGE_FEED = 115,
|
||||
AUTOSAVE_INTERVAL = 116,
|
||||
LIGHT_AMBIENT = 117,
|
||||
FIND_CONSTRAINT_TIMEOUT = 118,
|
||||
G_CODE_SAFE_HEIGHT = 113,
|
||||
G_CODE_PASSES = 114,
|
||||
G_CODE_FEED = 115,
|
||||
G_CODE_PLUNGE_FEED = 116,
|
||||
AUTOSAVE_INTERVAL = 117,
|
||||
LIGHT_AMBIENT = 118,
|
||||
FIND_CONSTRAINT_TIMEOUT = 119,
|
||||
EXPLODE_DISTANCE = 120,
|
||||
// For TTF text
|
||||
TTF_TEXT = 300,
|
||||
// For the step dimension screen
|
||||
|
@ -342,7 +346,9 @@ public:
|
|||
VIEW_PROJ_RIGHT = 702,
|
||||
VIEW_PROJ_UP = 703,
|
||||
// For tangent arc
|
||||
TANGENT_ARC_RADIUS = 800
|
||||
TANGENT_ARC_RADIUS = 800,
|
||||
// For helix pitch
|
||||
HELIX_PITCH = 802
|
||||
};
|
||||
struct {
|
||||
bool showAgain;
|
||||
|
@ -473,6 +479,8 @@ public:
|
|||
static void ScreenChangeExprA(int link, uint32_t v);
|
||||
static void ScreenChangeGroupName(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 ScreenChangeLightIntensity(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 ScreenChangeCameraTangent(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 ScreenChangeDigitsAfterDecimalDegree(int link, uint32_t v);
|
||||
static void ScreenChangeUseSIPrefixes(int link, uint32_t v);
|
||||
|
@ -535,6 +544,7 @@ public:
|
|||
Platform::MenuItemRef showGridMenuItem;
|
||||
Platform::MenuItemRef dimSolidModelMenuItem;
|
||||
Platform::MenuItemRef perspectiveProjMenuItem;
|
||||
Platform::MenuItemRef explodeMenuItem;
|
||||
Platform::MenuItemRef showToolbarMenuItem;
|
||||
Platform::MenuItemRef showTextWndMenuItem;
|
||||
Platform::MenuItemRef fullScreenMenuItem;
|
||||
|
@ -542,6 +552,7 @@ public:
|
|||
Platform::MenuItemRef unitsMmMenuItem;
|
||||
Platform::MenuItemRef unitsMetersMenuItem;
|
||||
Platform::MenuItemRef unitsInchesMenuItem;
|
||||
Platform::MenuItemRef unitsFeetInchesMenuItem;
|
||||
|
||||
Platform::MenuItemRef inWorkplaneMenuItem;
|
||||
Platform::MenuItemRef in3dMenuItem;
|
||||
|
@ -798,6 +809,8 @@ public:
|
|||
bool showEdges;
|
||||
bool showOutlines;
|
||||
bool showFaces;
|
||||
bool showFacesDrawing;
|
||||
bool showFacesNonDrawing;
|
||||
bool showMesh;
|
||||
void ToggleBool(bool *v);
|
||||
|
||||
|
|
59
src/view.cpp
|
@ -35,8 +35,29 @@ void TextWindow::ShowEditView() {
|
|||
Printf(false, "%Ba %Ftout%E (%3, %3, %3)", CO(n));
|
||||
Printf(false, "");
|
||||
|
||||
Printf(false, "The perspective may be changed in the");
|
||||
Printf(false, "configuration screen.");
|
||||
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, "");
|
||||
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) {
|
||||
|
@ -51,9 +72,9 @@ void TextWindow::ScreenChangeViewToFullScale(int link, uint32_t v) {
|
|||
void TextWindow::ScreenChangeViewOrigin(int link, uint32_t v) {
|
||||
std::string edit_value =
|
||||
ssprintf("%s, %s, %s",
|
||||
SS.MmToString(-SS.GW.offset.x).c_str(),
|
||||
SS.MmToString(-SS.GW.offset.y).c_str(),
|
||||
SS.MmToString(-SS.GW.offset.z).c_str());
|
||||
SS.MmToString(-SS.GW.offset.x, true).c_str(),
|
||||
SS.MmToString(-SS.GW.offset.y, true).c_str(),
|
||||
SS.MmToString(-SS.GW.offset.z, true).c_str());
|
||||
|
||||
SS.TW.edit.meaning = Edit::VIEW_ORIGIN;
|
||||
SS.TW.ShowEditControl(3, edit_value);
|
||||
|
@ -66,6 +87,34 @@ void TextWindow::ScreenChangeViewProjection(int link, uint32_t v) {
|
|||
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) {
|
||||
switch(edit.meaning) {
|
||||
case Edit::VIEW_SCALE: {
|
||||
|
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
|
@ -119,7 +119,7 @@ Param.val=-5.00000000000000000000
|
|||
AddParam
|
||||
|
||||
Param.h.v.=00040011
|
||||
Param.val=5.00000000000000000000
|
||||
Param.val=5.00000000000000088818
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00040013
|
||||
|
@ -147,7 +147,7 @@ Param.val=10.00000000000000000000
|
|||
AddParam
|
||||
|
||||
Param.h.v.=00060010
|
||||
Param.val=10.00000000000000000000
|
||||
Param.val=10.29878739785912600269
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00060011
|
||||
|
@ -155,7 +155,7 @@ Param.val=10.00000000000000000000
|
|||
AddParam
|
||||
|
||||
Param.h.v.=00060013
|
||||
Param.val=5.00000000000000000000
|
||||
Param.val=5.29878739785912422633
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00060014
|
||||
|
@ -163,7 +163,7 @@ Param.val=5.00000000000000000000
|
|||
AddParam
|
||||
|
||||
Param.h.v.=00070010
|
||||
Param.val=5.00000000000000000000
|
||||
Param.val=5.29878739785912422633
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00070011
|
||||
|
@ -171,7 +171,7 @@ Param.val=5.00000000000000000000
|
|||
AddParam
|
||||
|
||||
Param.h.v.=00070013
|
||||
Param.val=10.00000000000000000000
|
||||
Param.val=10.29878739785912600269
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00070014
|
||||
|
@ -310,7 +310,7 @@ Entity.type=2001
|
|||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=-5.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000088818
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
|
@ -363,7 +363,7 @@ Entity.h.v=00060001
|
|||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=10.00000000000000000000
|
||||
Entity.actPoint.x=10.29878739785912600269
|
||||
Entity.actPoint.y=10.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
@ -372,7 +372,7 @@ Entity.h.v=00060002
|
|||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=5.00000000000000000000
|
||||
Entity.actPoint.x=5.29878739785912422633
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
@ -390,7 +390,7 @@ Entity.h.v=00070001
|
|||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=5.00000000000000000000
|
||||
Entity.actPoint.x=5.29878739785912422633
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
@ -399,7 +399,7 @@ Entity.h.v=00070002
|
|||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=10.00000000000000000000
|
||||
Entity.actPoint.x=10.29878739785912600269
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
|
|
@ -0,0 +1,463 @@
|
|||
±²³SolveSpaceREVa
|
||||
|
||||
|
||||
Group.h.v=00000001
|
||||
Group.type=5000
|
||||
Group.name=#references
|
||||
Group.color=ff000000
|
||||
Group.skipFirst=0
|
||||
Group.predef.swapUV=0
|
||||
Group.predef.negateU=0
|
||||
Group.predef.negateV=0
|
||||
Group.visible=1
|
||||
Group.suppress=0
|
||||
Group.relaxConstraints=0
|
||||
Group.allowRedundant=0
|
||||
Group.allDimsReference=0
|
||||
Group.scale=1.00000000000000000000
|
||||
Group.remap={
|
||||
}
|
||||
AddGroup
|
||||
|
||||
Group.h.v=00000002
|
||||
Group.type=5001
|
||||
Group.order=1
|
||||
Group.name=sketch-in-plane
|
||||
Group.activeWorkplane.v=80020000
|
||||
Group.color=ff000000
|
||||
Group.subtype=6000
|
||||
Group.skipFirst=0
|
||||
Group.predef.q.w=1.00000000000000000000
|
||||
Group.predef.origin.v=00010001
|
||||
Group.predef.swapUV=0
|
||||
Group.predef.negateU=0
|
||||
Group.predef.negateV=0
|
||||
Group.visible=1
|
||||
Group.suppress=0
|
||||
Group.relaxConstraints=0
|
||||
Group.allowRedundant=0
|
||||
Group.allDimsReference=0
|
||||
Group.scale=1.00000000000000000000
|
||||
Group.remap={
|
||||
}
|
||||
AddGroup
|
||||
|
||||
Param.h.v.=00010010
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00010011
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00010012
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00010020
|
||||
Param.val=1.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00010021
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00010022
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00010023
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020010
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020011
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020012
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020020
|
||||
Param.val=0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020021
|
||||
Param.val=0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020022
|
||||
Param.val=0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00020023
|
||||
Param.val=0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030010
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030011
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030012
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030020
|
||||
Param.val=0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030021
|
||||
Param.val=-0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030022
|
||||
Param.val=-0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00030023
|
||||
Param.val=-0.50000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00040010
|
||||
Param.val=-5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00040011
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00040013
|
||||
Param.val=-10.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00040014
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00050010
|
||||
Param.val=-10.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00050011
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00050013
|
||||
Param.val=-5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00050014
|
||||
Param.val=10.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00060010
|
||||
Param.val=10.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00060011
|
||||
Param.val=10.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00060013
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00060014
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00070010
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00070011
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00070013
|
||||
Param.val=10.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Param.h.v.=00070014
|
||||
Param.val=5.00000000000000000000
|
||||
AddParam
|
||||
|
||||
Request.h.v=00000001
|
||||
Request.type=100
|
||||
Request.group.v=00000001
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Request.h.v=00000002
|
||||
Request.type=100
|
||||
Request.group.v=00000001
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Request.h.v=00000003
|
||||
Request.type=100
|
||||
Request.group.v=00000001
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Request.h.v=00000004
|
||||
Request.type=200
|
||||
Request.workplane.v=80020000
|
||||
Request.group.v=00000002
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Request.h.v=00000005
|
||||
Request.type=200
|
||||
Request.workplane.v=80020000
|
||||
Request.group.v=00000002
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Request.h.v=00000006
|
||||
Request.type=200
|
||||
Request.workplane.v=80020000
|
||||
Request.group.v=00000002
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Request.h.v=00000007
|
||||
Request.type=200
|
||||
Request.workplane.v=80020000
|
||||
Request.group.v=00000002
|
||||
Request.construction=0
|
||||
AddRequest
|
||||
|
||||
Entity.h.v=00010000
|
||||
Entity.type=10000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00010001
|
||||
Entity.normal.v=00010020
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00010001
|
||||
Entity.type=2000
|
||||
Entity.construction=1
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00010020
|
||||
Entity.type=3000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00010001
|
||||
Entity.actNormal.w=1.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00020000
|
||||
Entity.type=10000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00020001
|
||||
Entity.normal.v=00020020
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00020001
|
||||
Entity.type=2000
|
||||
Entity.construction=1
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00020020
|
||||
Entity.type=3000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00020001
|
||||
Entity.actNormal.w=0.50000000000000000000
|
||||
Entity.actNormal.vx=0.50000000000000000000
|
||||
Entity.actNormal.vy=0.50000000000000000000
|
||||
Entity.actNormal.vz=0.50000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00030000
|
||||
Entity.type=10000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00030001
|
||||
Entity.normal.v=00030020
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00030001
|
||||
Entity.type=2000
|
||||
Entity.construction=1
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00030020
|
||||
Entity.type=3000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00030001
|
||||
Entity.actNormal.w=0.50000000000000000000
|
||||
Entity.actNormal.vx=-0.50000000000000000000
|
||||
Entity.actNormal.vy=-0.50000000000000000000
|
||||
Entity.actNormal.vz=-0.50000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00040000
|
||||
Entity.type=11000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00040001
|
||||
Entity.point[1].v=00040002
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00040001
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=-5.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00040002
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=-10.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00050000
|
||||
Entity.type=11000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00050001
|
||||
Entity.point[1].v=00050002
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00050001
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=-10.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00050002
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=-5.00000000000000000000
|
||||
Entity.actPoint.y=10.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00060000
|
||||
Entity.type=11000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00060001
|
||||
Entity.point[1].v=00060002
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00060001
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=10.00000000000000000000
|
||||
Entity.actPoint.y=10.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00060002
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=5.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00070000
|
||||
Entity.type=11000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=00070001
|
||||
Entity.point[1].v=00070002
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00070001
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=5.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=00070002
|
||||
Entity.type=2001
|
||||
Entity.construction=0
|
||||
Entity.workplane.v=80020000
|
||||
Entity.actPoint.x=10.00000000000000000000
|
||||
Entity.actPoint.y=5.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=80020000
|
||||
Entity.type=10000
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=80020002
|
||||
Entity.normal.v=80020001
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=80020001
|
||||
Entity.type=3010
|
||||
Entity.construction=0
|
||||
Entity.point[0].v=80020002
|
||||
Entity.actNormal.w=1.00000000000000000000
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Entity.h.v=80020002
|
||||
Entity.type=2012
|
||||
Entity.construction=1
|
||||
Entity.actVisible=1
|
||||
AddEntity
|
||||
|
||||
Constraint.h.v=00000001
|
||||
Constraint.type=20
|
||||
Constraint.group.v=00000002
|
||||
Constraint.workplane.v=80020000
|
||||
Constraint.ptA.v=00040002
|
||||
Constraint.ptB.v=00050001
|
||||
Constraint.other=0
|
||||
Constraint.other2=0
|
||||
Constraint.reference=0
|
||||
AddConstraint
|
||||
|
||||
Constraint.h.v=00000002
|
||||
Constraint.type=20
|
||||
Constraint.group.v=00000002
|
||||
Constraint.workplane.v=80020000
|
||||
Constraint.ptA.v=00060002
|
||||
Constraint.ptB.v=00070001
|
||||
Constraint.other=0
|
||||
Constraint.other2=0
|
||||
Constraint.reference=0
|
||||
AddConstraint
|
||||
|
||||
Constraint.h.v=00000003
|
||||
Constraint.type=54
|
||||
Constraint.group.v=00000002
|
||||
Constraint.workplane.v=80020000
|
||||
Constraint.entityA.v=00040000
|
||||
Constraint.entityB.v=00050000
|
||||
Constraint.entityC.v=00070000
|
||||
Constraint.entityD.v=00060000
|
||||
Constraint.other=0
|
||||
Constraint.other2=0
|
||||
Constraint.reference=0
|
||||
AddConstraint
|
||||
|
|
@ -8,12 +8,12 @@ TEST_CASE(normal_roundtrip) {
|
|||
|
||||
TEST_CASE(normal_migrate_from_v20) {
|
||||
CHECK_LOAD("normal_v20.slvs");
|
||||
CHECK_SAVE("normal.slvs");
|
||||
CHECK_SAVE("normal_old_version.slvs");
|
||||
}
|
||||
|
||||
TEST_CASE(normal_migrate_from_v22) {
|
||||
CHECK_LOAD("normal_v22.slvs");
|
||||
CHECK_SAVE("normal.slvs");
|
||||
CHECK_SAVE("normal_old_version.slvs");
|
||||
}
|
||||
|
||||
TEST_CASE(other_roundtrip) {
|
||||
|
|
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.2 KiB |
|
@ -2,13 +2,12 @@
|
|||
|
||||
TEST_CASE(normal_roundtrip) {
|
||||
CHECK_LOAD("normal.slvs");
|
||||
// Can't render images through cairo for now.
|
||||
// CHECK_RENDER("normal.png");
|
||||
CHECK_RENDER("normal.png");
|
||||
CHECK_SAVE("normal.slvs");
|
||||
}
|
||||
|
||||
TEST_CASE(linked_roundtrip) {
|
||||
CHECK_LOAD("linked.slvs");
|
||||
// CHECK_RENDER("linked.png");
|
||||
CHECK_RENDER("linked.png");
|
||||
CHECK_SAVE("linked.slvs");
|
||||
}
|
||||
|
|