Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
324ff3f065 |
@@ -1,4 +1 @@
|
||||
BasedOnStyle: Google
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
DerivePointerAlignment: false
|
||||
@@ -1,2 +0,0 @@
|
||||
line_length: 80
|
||||
indent: 2
|
||||
@@ -1,41 +0,0 @@
|
||||
name: download and cache git repo
|
||||
description: this simply dumps it in the current directory...
|
||||
inputs:
|
||||
name:
|
||||
required: true
|
||||
url:
|
||||
required: true
|
||||
rev:
|
||||
default: ''
|
||||
tag:
|
||||
default: ''
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: setup cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{inputs.name}}
|
||||
key: ${{inputs.url}}@${{inputs.rev}}${{inputs.tag}}
|
||||
|
||||
- name: download rev
|
||||
if: steps.cache.outputs.cache-hit != 'true' && inputs.rev != ''
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ${{inputs.name}}
|
||||
cd ${{inputs.name}}
|
||||
git init
|
||||
git remote add origin ${{inputs.url}}
|
||||
git fetch --depth 1 origin ${{inputs.rev}}
|
||||
git checkout ${{inputs.rev}}
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: download tag
|
||||
if: steps.cache.outputs.cache-hit != 'true' && inputs.tag != ''
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch ${{inputs.tag}} --depth 1 ${{inputs.url}} ${{inputs.name}}
|
||||
cd ${{inputs.name}}
|
||||
git submodule update --init --recursive
|
||||
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -1,9 +1,6 @@
|
||||
name: build_wheels
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
on: [release, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
@@ -11,21 +8,18 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
# macos-15-intel: x86-64
|
||||
# macos-15: arm64
|
||||
os: [ubuntu-22.04, ubuntu-22.04-arm, windows-2025, macos-15-intel, macos-15]
|
||||
os: [ubuntu-22.04, windows-latest, macos-latest]
|
||||
steps:
|
||||
- run: |
|
||||
git config --global submodule.fetchJobs 8
|
||||
git config --global core.longpaths true
|
||||
- if: matrix.os == 'macos-15'
|
||||
run: echo "CIBW_ARCHS=arm64 universal2" >> "$GITHUB_ENV"
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v3.4.0
|
||||
- uses: actions/upload-artifact@v4
|
||||
uses: pypa/cibuildwheel@v2.15.0
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: python-${{ matrix.os }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
make_sdist:
|
||||
@@ -33,20 +27,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Optional, use if you use setuptools_scm
|
||||
submodules: true # Optional, use if you have submodules
|
||||
|
||||
- name: Build SDist
|
||||
run: pipx run build --sdist
|
||||
- name: Confirm SDist can be built
|
||||
run: |
|
||||
# FIXME: why doesn't scikit_build_core automatically download ninja?
|
||||
sudo apt-get update
|
||||
sudo apt-get install ninja-build libtbb-dev
|
||||
pip wheel dist/*.tar.gz
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: python-sdist
|
||||
path: dist/*.tar.gz
|
||||
|
||||
pypi-publish:
|
||||
pypi-publish-beta:
|
||||
name: Upload release to PyPI
|
||||
needs: [build_wheels, make_sdist]
|
||||
runs-on: ubuntu-latest
|
||||
@@ -55,12 +47,10 @@ jobs:
|
||||
url: https://pypi.org/p/manifold3d
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
pattern: python-*
|
||||
merge-multiple: true
|
||||
name: artifact
|
||||
path: dist
|
||||
- name: Publish package distributions to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
||||
@@ -8,25 +8,14 @@ on:
|
||||
|
||||
jobs:
|
||||
check_format:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DoozyX/clang-format-lint-action@v0.20
|
||||
- uses: actions/checkout@v3
|
||||
- uses: DoozyX/clang-format-lint-action@v0.14
|
||||
with:
|
||||
source: '.'
|
||||
exclude: '*/third_party'
|
||||
extensions: 'h,cpp,js,ts,html'
|
||||
clangFormatVersion: 20
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- uses: psf/black@stable
|
||||
with:
|
||||
options: "--check --verbose"
|
||||
src: "./bindings/python/examples"
|
||||
# - name: "gersemi cmake check"
|
||||
# run: |
|
||||
# pip3 install gersemi
|
||||
# ./scripts/gersemi-check.sh
|
||||
clangFormatVersion: 12
|
||||
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
name: Deploy documentation
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI]
|
||||
types:
|
||||
- completed
|
||||
branches: [master]
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
deploy_github_pages:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Doxygen Action
|
||||
uses: mattnotmitt/doxygen-action@edge
|
||||
uses: mattnotmitt/doxygen-action@v1
|
||||
|
||||
- name: Move deployables
|
||||
run: |
|
||||
@@ -23,16 +21,13 @@ jobs:
|
||||
mv ./samples ./public
|
||||
mv ./docs ./public
|
||||
|
||||
- name: Download manifoldCAD editor, examples and documentation
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
- name: Download built examples
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
workflow: manifold.yml
|
||||
workflow_conclusion: completed
|
||||
# specific to the triggering workflow
|
||||
run_id: ${{github.event.workflow_run.id}}
|
||||
# do not download from old run
|
||||
check_artifacts: true
|
||||
name: wasmpublic
|
||||
pr: ${{github.event.pull_request.number}}
|
||||
name: wasm
|
||||
path: ./public
|
||||
|
||||
- name: Deploy to Github Pages
|
||||
@@ -42,4 +37,4 @@ jobs:
|
||||
publish_branch: gh-pages
|
||||
force_orphan: true
|
||||
cname: manifoldcad.org
|
||||
publish_dir: ./public
|
||||
publish_dir: ./public
|
||||
@@ -10,484 +10,225 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
name: Windows (TBB:${{matrix.parallelization}}, SHARED:${{matrix.shared}})
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
shared: [OFF, ON]
|
||||
runs-on: windows-2025
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: lukka/get-cmake@v4.2.3
|
||||
with:
|
||||
cmakeVersion: 4.0.2
|
||||
- &clone-tbb
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: tbb
|
||||
url: https://github.com/oneapi-src/oneTBB.git
|
||||
tag: v2022.3.0
|
||||
- &clone-clipper2
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: clipper2
|
||||
url: https://github.com/AngusJohnson/Clipper2.git
|
||||
rev: 46f639177fe418f9689e8ddb74f08a870c71f5b4
|
||||
- &clone-nanobind
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: nanobind
|
||||
url: https://github.com/wjakob/nanobind.git
|
||||
tag: v2.12.0
|
||||
- &clone-gtest
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: gtest
|
||||
url: https://github.com/google/googletest.git
|
||||
tag: v1.17.0
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build ${{matrix.parallelization}}
|
||||
shell: bash
|
||||
run: |
|
||||
cmake . \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=${{matrix.shared}} \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
-DFETCHCONTENT_SOURCE_DIR_NANOBIND=nanobind \
|
||||
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=gtest \
|
||||
-A x64 -B build
|
||||
cmake --build build --target ALL_BUILD --config Release
|
||||
- name: Test dll location
|
||||
if: matrix.shared == 'ON'
|
||||
shell: bash
|
||||
run: |
|
||||
ls ./build/lib/Release
|
||||
[ -f ./build/lib/Release/manifold.dll ]
|
||||
[ -f ./build/lib/Release/manifoldc.dll ]
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
cp ./build/lib/Release/* ./build/bin/Release
|
||||
./build/bin/Release/manifold_test.exe
|
||||
|
||||
build:
|
||||
name: GCC ${{matrix.gcc}} (CrossSection:${{matrix.cross_section}}, TBB:${{matrix.parallelization}})
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
cross_section: [ON]
|
||||
parallelization: [OFF, ON]
|
||||
os: [ubuntu-24.04]
|
||||
gcc: [13, 14]
|
||||
include:
|
||||
- cross_section: OFF
|
||||
parallelization: ON
|
||||
os: ubuntu-24.04
|
||||
gcc: 14
|
||||
- cross_section: ON
|
||||
parallelization: ON
|
||||
os: ubuntu-22.04
|
||||
gcc: 11
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CC: gcc-${{ matrix.gcc }}
|
||||
CXX: g++-${{ matrix.gcc }}
|
||||
parallel_backend: [NONE, TBB]
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- &setup-python
|
||||
uses: actions/setup-python@v5
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt install -y libglm-dev libgtest-dev libassimp-dev git libtbb-dev pkg-config libpython3-dev python3 python3-distutils python3-pip lcov
|
||||
pip install trimesh
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: 'pip'
|
||||
- run: pip install -U trimesh pytest
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libgtest-dev libassimp-dev git libtbb-dev pkg-config cmake
|
||||
- *clone-clipper2
|
||||
- *clone-nanobind
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PYBIND=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
-DFETCHCONTENT_SOURCE_DIR_NANOBIND=nanobind \
|
||||
-DMANIFOLD_CROSS_SECTION=${{matrix.cross_section}} \
|
||||
-DASSIMP_ENABLE=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Test
|
||||
if: matrix.cross_section == 'ON'
|
||||
run: |
|
||||
./build/test/manifold_test
|
||||
# python tests
|
||||
export PYTHONPATH=$PYTHONPATH:$(pwd)/build/bindings/python
|
||||
python3 bindings/python/examples/run_all.py -e
|
||||
python3 -m pytest
|
||||
- name: test cmake consumer
|
||||
run: |
|
||||
sudo cmake --install build
|
||||
./scripts/test-cmake.sh
|
||||
|
||||
build_gcc_codecov:
|
||||
name: code coverage
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
# parallelization: [OFF, ON]
|
||||
parallelization: [OFF]
|
||||
env:
|
||||
CC: gcc-14
|
||||
CXX: g++-14
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libgtest-dev git libtbb-dev pkg-config lcov cmake
|
||||
- *clone-clipper2
|
||||
- name: build clipper2
|
||||
run: |
|
||||
cd clipper2/CPP
|
||||
cmake -DCMAKE_BUILD_TYPE=Release . -B build
|
||||
cmake --build build
|
||||
sudo cmake --install build
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_CROSS_SECTION=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DCODE_COVERAGE=ON \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Coverage Report
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- name: Build ${{matrix.backend}}
|
||||
if: matrix.parallel_backend != 'NONE'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
lcov --capture --gcov-tool gcov-14 --ignore-errors mismatch --initial --directory . --output-file ./code_coverage_init.info
|
||||
test/manifold_test
|
||||
lcov --capture --gcov-tool gcov-14 --ignore-errors mismatch --directory . --output-file ./code_coverage_test.info
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_EXPORT=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} .. && make
|
||||
- name: Test ${{matrix.parallel_backend}}
|
||||
if: matrix.parallel_backend != 'NONE'
|
||||
run: |
|
||||
export PYTHONPATH=$PYTHONPATH:$(pwd)/build/bindings/python
|
||||
cd build/test
|
||||
./manifold_test
|
||||
cd ../../
|
||||
python3 bindings/python/examples/run_all.py -e
|
||||
- name: Coverage Report
|
||||
# only do code coverage for default sequential backend, it seems that TBB
|
||||
# backend will cause failure
|
||||
# perhaps issue related to invalid memory access?
|
||||
if: matrix.parallel_backend == 'NONE'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} -DCODE_COVERAGE=ON .. && make
|
||||
lcov --capture --initial --directory . --output-file ./code_coverage_init.info
|
||||
cd test
|
||||
./manifold_test
|
||||
cd ../
|
||||
lcov --capture --directory . --output-file ./code_coverage_test.info
|
||||
lcov --add-tracefile ./code_coverage_init.info --add-tracefile ./code_coverage_test.info --output-file ./code_coverage_total.info
|
||||
lcov --extract ./code_coverage_total.info "/*/manifold/src/*" --output-file ./code_coverage.info
|
||||
- uses: codecov/codecov-action@v5
|
||||
lcov --remove ./code_coverage_total.info '/usr/*' '*/third_party/*' '*/test/*' '*/extras/*' '*/bindings/*' --output-file ./code_coverage.info
|
||||
- uses: codecov/codecov-action@v2
|
||||
if: matrix.parallel_backend == 'NONE'
|
||||
with:
|
||||
disable_search: true
|
||||
files: build/code_coverage.info
|
||||
fail_ci_if_error: false
|
||||
name: ${{matrix.parallelization}}
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
verbose: true
|
||||
name: ${{matrix.parallel_backend}}
|
||||
|
||||
build_wasm:
|
||||
name: WASM (TBB:${{matrix.parallelization}}, DEBUG:${{matrix.debug}})
|
||||
build_cbind:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
debug: [OFF, ON]
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 25
|
||||
cache: 'npm'
|
||||
cache-dependency-path: bindings/**/package-lock.json
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: cmake
|
||||
version: 1
|
||||
- name: Setup EMSDK
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 5.0.2
|
||||
- *clone-tbb
|
||||
- *clone-clipper2
|
||||
- *clone-gtest
|
||||
- name: Build WASM ${{matrix.parallelization}}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
emcmake cmake \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=${{matrix.debug}} \
|
||||
-DMANIFOLD_ASSERT=${{matrix.debug}} \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=gtest \
|
||||
. -B build
|
||||
sudo apt-get -y update
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt install -y libgtest-dev libglm-dev libassimp-dev git libtbb-dev pkg-config libpython3-dev python3 python3-distutils python3-pip
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- name: Build C bindings with TBB
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
emmake make
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_CBIND=ON -DMANIFOLD_PAR=TBB .. && make
|
||||
- name: Test ${{matrix.parallel_backend}}
|
||||
run: |
|
||||
cd build/test
|
||||
./manifold_test --gtest_filter=CBIND.*
|
||||
|
||||
build_wasm:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt install -y nodejs libglm-dev
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup WASM
|
||||
run: |
|
||||
# setup emscripten
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
cd emsdk
|
||||
./emsdk install latest
|
||||
./emsdk activate latest
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- name: Build WASM
|
||||
run: |
|
||||
source ./emsdk/emsdk_env.sh
|
||||
mkdir build
|
||||
cd build
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=Release .. && emmake make
|
||||
- name: Test WASM
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd build/test
|
||||
node ./manifold_test.js
|
||||
- name: Run unit tests
|
||||
if: matrix.parallelization == 'OFF'
|
||||
- name: Test examples
|
||||
run: |
|
||||
cd bindings/wasm
|
||||
cd bindings/wasm/examples
|
||||
npm ci
|
||||
npm run build
|
||||
npm test
|
||||
- name: Isolated npm smoke test
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
set -ev -o pipefail
|
||||
cd bindings/wasm
|
||||
wasmpath=$(pwd)
|
||||
npmtestpath=../npmtest
|
||||
# Create an npm package.
|
||||
npm ci && npm pack
|
||||
# Install it in an empty directory.
|
||||
mkdir -p ${npmtestpath} && cd ${npmtestpath}
|
||||
npm install ${wasmpath}/manifold-3d*.tgz
|
||||
cp ${wasmpath}/test/fixtures/npm/* .
|
||||
# Positive case smoke test.
|
||||
npx manifold-cad ./unitSphere.mjs ./unitSphere.glb && echo "Positive smoke test succeeded"
|
||||
# Negative case smoke test.
|
||||
npx manifold-cad ./unitSphereNoManifoldImport.mjs ./unitSphereNoManifoldImport.glb || echo "Negative smoke test failed as expected"
|
||||
- name: Build manifoldCAD editor
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm
|
||||
npm run ci:editor
|
||||
npm run build:editor
|
||||
- name: Build WASM examples
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm/examples
|
||||
npm run ci:examples
|
||||
npm run build:examples
|
||||
- name: Build WASM documentation and site
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm
|
||||
npm run docs
|
||||
npm run public
|
||||
- name: Upload manifoldCAD editor, examples and documentation
|
||||
uses: actions/upload-artifact@v4
|
||||
if: github.event_name == 'push' && matrix.parallelization == 'OFF' && matrix.debug == 'OFF'
|
||||
with:
|
||||
name: wasmpublic
|
||||
path: bindings/wasm/public/
|
||||
retention-days: 90
|
||||
overwrite: true
|
||||
- name: Upload complete wasm build.
|
||||
uses: actions/upload-artifact@v4
|
||||
if: github.event_name == 'push' && matrix.parallelization == 'OFF' && matrix.debug == 'OFF'
|
||||
- name: Upload WASM files
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wasm
|
||||
path: bindings/wasm/
|
||||
path: bindings/wasm/examples/dist/
|
||||
retention-days: 90
|
||||
overwrite: true
|
||||
|
||||
build_fuzzer:
|
||||
name: fuzzer
|
||||
build_windows:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-24.04, macos-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
parallel_backend: [NONE, TBB]
|
||||
max-parallel: 1
|
||||
runs-on: windows-2019
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install apt packages
|
||||
if: matrix.os == 'ubuntu-24.04'
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
packages: libgtest-dev clang cmake
|
||||
version: 1
|
||||
- name: Install brew packages
|
||||
if: matrix.os == 'macos-latest'
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Build ${{matrix.backend}}
|
||||
shell: powershell
|
||||
run: |
|
||||
brew install googletest cmake
|
||||
- *clone-clipper2
|
||||
- uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: fuzztest
|
||||
url: https://github.com/google/fuzztest.git
|
||||
rev: f1e26613f66997aa09d3026762e275de22b2daae
|
||||
- name: Build
|
||||
cmake . -DCMAKE_BUILD_TYPE=Release -B build -DMANIFOLD_DEBUG=ON -DPYBIND11_FINDPYTHON=OFF -DMANIFOLD_PAR=${{matrix.parallel_backend}} -A x64
|
||||
cd build
|
||||
cmake --build . --target ALL_BUILD --config Release
|
||||
- name: Test ${{matrix.parallel_backend}}
|
||||
shell: bash
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_FUZZ=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DMANIFOLD_CROSS_SECTION=ON \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
-DFETCHCONTENT_SOURCE_DIR_FUZZTEST=fuzztest \
|
||||
. -B build
|
||||
ASAN_OPTIONS=detect_container_overflow=0 cmake --build build
|
||||
cd build/test
|
||||
./Release/manifold_test.exe
|
||||
|
||||
build_mxe:
|
||||
name: MXE (TBB:${{matrix.parallelization == 'ON'}})
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
parallel_backend: [NONE, TBB]
|
||||
max-parallel: 1
|
||||
runs-on: ubuntu-latest
|
||||
container: openscad/mxe-x86_64-gui:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- *clone-clipper2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
run: |
|
||||
export MXEDIR=/mxe
|
||||
export MXE_TARGETS=x86_64-w64-mingw32.static.posix
|
||||
export MXEDIR=$HOME/openscad_deps/mxe
|
||||
export MXETARGETDIR=$MXEDIR/usr/$MXE_TARGETS
|
||||
export PATH=/mxe/usr/bin:$PATH
|
||||
export CMAKE=$MXE_TARGETS-cmake
|
||||
$CMAKE \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_CBIND=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
. -B build
|
||||
$CMAKE --build build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_PYBIND=OFF -DMANIFOLD_DEBUG=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} .. && make
|
||||
- name: Test
|
||||
run: |
|
||||
cd build/test
|
||||
./manifold_test
|
||||
|
||||
build_mac:
|
||||
name: MacOS (TBB:${{matrix.parallelization == 'ON'}}, shared lib:${{matrix.shared == 'ON'}}, ${{matrix.os}})
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
shared: [OFF, ON]
|
||||
# macos-15-intel: intel
|
||||
# macos-latest: arm
|
||||
os: [macos-15-intel, macos-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: 'pip'
|
||||
- name: Install common dependencies
|
||||
run: |
|
||||
brew install googletest assimp cmake
|
||||
pip install trimesh pytest
|
||||
- name: Install TBB
|
||||
if: matrix.parallelization == 'ON'
|
||||
run: brew install tbb
|
||||
- *clone-clipper2
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=${{matrix.shared}} \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PYBIND=ON \
|
||||
-DASSIMP_ENABLE=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Test
|
||||
run: |
|
||||
build/test/manifold_test
|
||||
- name: test cmake consumer
|
||||
run: |
|
||||
sudo cmake --install build
|
||||
./scripts/test-cmake.sh
|
||||
- name: test pkg-config consumer
|
||||
if: matrix.shared == 'ON'
|
||||
run: |
|
||||
LDFLAGS=-Wl,-rpath,/usr/local/lib ./scripts/test-pkgconfig.sh
|
||||
|
||||
build_mac_builtin_tbb:
|
||||
timeout-minutes: 30
|
||||
parallel_backend: [NONE, TBB]
|
||||
runs-on: macos-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: 'pip'
|
||||
- name: Install common dependencies
|
||||
run: |
|
||||
brew install googletest cmake
|
||||
pip install trimesh pytest
|
||||
- *clone-tbb
|
||||
- *clone-clipper2
|
||||
- name: test cmake consumer subdir
|
||||
run: |
|
||||
export CLIPPER2=$(pwd)/clipper2
|
||||
export TBB=$(pwd)/tbb
|
||||
cd ..
|
||||
./manifold/scripts/test-cmake-subdir.sh
|
||||
brew install pkg-config googletest assimp
|
||||
pip install trimesh
|
||||
- name: Install TBB
|
||||
if: matrix.parallel_backend == 'TBB'
|
||||
run: brew install tbb
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_PAR=ON \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_CLIPPER2=clipper2 \
|
||||
. -B build
|
||||
cmake --build build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_EXPORT=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} .. && make
|
||||
- name: Test
|
||||
run: |
|
||||
./build/test/manifold_test
|
||||
- name: test cmake consumer
|
||||
run: |
|
||||
sudo cmake --install build
|
||||
./scripts/test-cmake.sh
|
||||
LDFLAGS=-Wl,-rpath,/usr/local/lib ./scripts/test-pkgconfig.sh
|
||||
cd build/test
|
||||
./manifold_test
|
||||
|
||||
build_nix:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
variant: [manifold-none, manifold-tbb, manifold-js, manifold-js-tbb, manifold3d]
|
||||
variant: [none, tbb, js]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- run: nix build -L '.?submodules=1#${{matrix.variant}}'
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- run: nix build -L '.?submodules=1#manifold-${{matrix.variant}}'
|
||||
|
||||
build_nix_python:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- run: nix build -L '.?submodules=1#manifold3d'
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
name: publish_npm
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish_npm:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 25
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Update npm # Ensure npm 11.5.1 or later for trusted publishing
|
||||
run: npm install -g npm@latest
|
||||
|
||||
- name: Download built examples
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: manifold.yml
|
||||
workflow_conclusion: completed
|
||||
branch: master
|
||||
check_artifacts: true
|
||||
name: wasm
|
||||
path: ./bindings/wasm/
|
||||
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
cd ./bindings/wasm/
|
||||
npm ci
|
||||
npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
@@ -1,31 +0,0 @@
|
||||
name: Release File
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
upload_archive:
|
||||
name: Upload archive
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
ref=${{ github.ref_name }}
|
||||
echo "release_name=manifold-${ref#v}" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: ${{ env.release_name }}
|
||||
submodules: recursive
|
||||
- name: Build archive
|
||||
run: >
|
||||
tar --exclude=".git*" -cz ${{ env.release_name }} -f ${{ env.release_name }}.tar.gz
|
||||
- name: Log checksum
|
||||
run: >
|
||||
sha256sum ${{ env.release_name }}.tar.gz
|
||||
- name: Add file to release
|
||||
run: |
|
||||
cd ${{ env.release_name }} || exit 1
|
||||
gh release upload ${{ github.ref_name }} ../${{ env.release_name }}.tar.gz
|
||||
env:
|
||||
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
@@ -9,6 +9,7 @@
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
@@ -111,27 +112,11 @@ node_modules/
|
||||
bindings/wasm/examples/built
|
||||
bindings/wasm/examples/dist
|
||||
bindings/wasm/examples/public/manifold*
|
||||
bindings/wasm/docs
|
||||
__pycache__
|
||||
.vscode/c_cpp_properties.json
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.cache
|
||||
CMakeUserPresets.json
|
||||
CMakeSettings.json
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
|
||||
# Temporary files
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-v][a-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Benchmark stl files
|
||||
extras/ember_tests/testfiles/raw_meshes
|
||||
|
||||
.idea
|
||||
@@ -0,0 +1,9 @@
|
||||
[submodule "src/third_party/clipper2"]
|
||||
path = src/third_party/clipper2
|
||||
url = https://github.com/AngusJohnson/clipper2
|
||||
[submodule "src/third_party/quickhull"]
|
||||
path = src/third_party/quickhull
|
||||
url = https://github.com/akuukka/quickhull
|
||||
[submodule "bindings/python/third_party/nanobind"]
|
||||
path = bindings/python/third_party/nanobind
|
||||
url = https://github.com/wjakob/nanobind
|
||||
@@ -6,13 +6,12 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "manifold test",
|
||||
"type": "lldb-dap",
|
||||
"MIMode": "lldb",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/test/manifold_test",
|
||||
"args": [
|
||||
"--gtest_catch_exceptions=0",
|
||||
"--gtest_filter=SDF.SineSurface"
|
||||
"--gtest_break_on_failure",
|
||||
"--gtest_filter=Manifold.GetMesh"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/test",
|
||||
@@ -21,8 +20,7 @@
|
||||
"name": "MALLOC_CHECK_",
|
||||
"value": "2"
|
||||
}
|
||||
],
|
||||
"externalConsole": false
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -108,29 +108,17 @@
|
||||
"latch": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"span": "cpp",
|
||||
"__sso_allocator": "cpp",
|
||||
"source_location": "cpp",
|
||||
"format": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ranges": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"execution": "cpp",
|
||||
"csetjmp": "cpp",
|
||||
"cuchar": "cpp",
|
||||
"propagate_const": "cpp",
|
||||
"scoped_allocator": "cpp"
|
||||
"__sso_allocator": "cpp"
|
||||
},
|
||||
"C_Cpp.clang_format_fallbackStyle": "google",
|
||||
"editor.formatOnSave": true,
|
||||
"cmake.configureArgs": [
|
||||
"-DBUILD_SHARED_LIBS=ON",
|
||||
"-DMANIFOLD_CBIND=ON",
|
||||
"-DMANIFOLD_PYBIND=ON",
|
||||
"-DMANIFOLD_EXPORT=ON",
|
||||
"-DMANIFOLD_DEBUG=ON",
|
||||
"-DMANIFOLD_ASSERT=ON",
|
||||
"-DMANIFOLD_PAR=ON",
|
||||
"-DMANIFOLD_FUZZ=OFF",
|
||||
"-DMANIFOLD_USE_CUDA=OFF",
|
||||
"-DMANIFOLD_PAR=TBB",
|
||||
"-DCODE_COVERAGE=OFF",
|
||||
"-DCMAKE_CXX_FLAGS=''" //'-fsanitize=address,undefined'"
|
||||
],
|
||||
@@ -148,11 +136,8 @@
|
||||
"Gyroid",
|
||||
"halfedge",
|
||||
"halfedges",
|
||||
"Minkowski",
|
||||
"Tris",
|
||||
"Verts",
|
||||
"Voxel",
|
||||
"voxels"
|
||||
"Verts"
|
||||
],
|
||||
"javascript.format.semicolons": "insert",
|
||||
"[python]": {
|
||||
@@ -160,8 +145,4 @@
|
||||
},
|
||||
"python.formatting.provider": "none",
|
||||
"clang-format.executable": "clang-format",
|
||||
"clangd.arguments": [
|
||||
"--header-insertion=never"
|
||||
],
|
||||
"editor.defaultFormatter": "xaver.clang-format",
|
||||
}
|
||||
@@ -15,309 +15,83 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(manifold LANGUAGES CXX)
|
||||
|
||||
# Use C++17
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
# Define Manifold version
|
||||
set(MANIFOLD_VERSION_MAJOR 3)
|
||||
set(MANIFOLD_VERSION_MINOR 4)
|
||||
set(MANIFOLD_VERSION_PATCH 1)
|
||||
set(
|
||||
MANIFOLD_VERSION
|
||||
"${MANIFOLD_VERSION_MAJOR}.${MANIFOLD_VERSION_MINOR}.${MANIFOLD_VERSION_PATCH}"
|
||||
)
|
||||
|
||||
# Primary user facing options
|
||||
option(MANIFOLD_CROSS_SECTION "Build CrossSection for 2D support" ON)
|
||||
option(MANIFOLD_DEBUG "Enable debug tracing/timing" OFF)
|
||||
option(MANIFOLD_ASSERT "Enable assertions - requires MANIFOLD_DEBUG" OFF)
|
||||
option(MANIFOLD_STRICT "Treat compile warnings as fatal build errors" OFF)
|
||||
option(
|
||||
MANIFOLD_DOWNLOADS
|
||||
"Allow Manifold build to download missing dependencies"
|
||||
ON
|
||||
)
|
||||
option(MANIFOLD_PAR "Enable Parallel backend" OFF)
|
||||
option(
|
||||
MANIFOLD_OPTIMIZED
|
||||
"Force optimized build, even with debugging enabled"
|
||||
OFF
|
||||
)
|
||||
option(MANIFOLD_TEST "Enable testing suite" ON)
|
||||
option(MANIFOLD_PYBIND "Build python bindings" OFF)
|
||||
# MANIFOLD_CBIND is only available when MANIFOLD_CROSS_SECTION is enabled
|
||||
cmake_dependent_option(
|
||||
MANIFOLD_CBIND
|
||||
"Build C (FFI) bindings"
|
||||
ON
|
||||
"MANIFOLD_CROSS_SECTION"
|
||||
OFF
|
||||
)
|
||||
# MANIFOLD_JSBIND is only available when building with Emscripten
|
||||
cmake_dependent_option(
|
||||
MANIFOLD_JSBIND
|
||||
"Build js binding"
|
||||
ON
|
||||
"EMSCRIPTEN"
|
||||
OFF
|
||||
)
|
||||
# These three options can force the build to avoid using the system version of
|
||||
# the dependency
|
||||
# This will either use the provided source directory via
|
||||
# FETCHCONTENT_SOURCE_DIR_XXX, or fetch the source from GitHub.
|
||||
# Note that the dependency will be built as static dependency to avoid dynamic
|
||||
# library conflict.
|
||||
# When the system package is unavailable, the option will be automatically set
|
||||
# to true.
|
||||
option(MANIFOLD_USE_BUILTIN_TBB "Use builtin tbb" OFF)
|
||||
option(MANIFOLD_USE_BUILTIN_CLIPPER2 "Use builtin clipper2" OFF)
|
||||
option(MANIFOLD_USE_BUILTIN_NANOBIND "Use builtin nanobind" OFF)
|
||||
|
||||
# default to Release build
|
||||
option(CMAKE_BUILD_TYPE "Build type" Release)
|
||||
# default to building shared library
|
||||
option(BUILD_SHARED_LIBS "Build shared library" ON)
|
||||
# Set some option values in the CMake cache
|
||||
set(MANIFOLD_PAR "NONE" CACHE STRING "Parallel backend, either \"TBB\" or \"NONE\"")
|
||||
set(MANIFOLD_FLAGS "" CACHE STRING "Manifold compiler flags")
|
||||
option(MANIFOLD_EXPORT "Build mesh export (via assimp) utility library" OFF)
|
||||
option(MANIFOLD_TEST "Enable testing suite" ON)
|
||||
option(MANIFOLD_DEBUG "Enable debug tracing/timing" OFF)
|
||||
|
||||
# Development options
|
||||
option(TRACY_ENABLE "Use tracy profiling" OFF)
|
||||
option(MANIFOLD_PYBIND "Build python bindings" ON)
|
||||
option(MANIFOLD_CBIND "Build C (FFI) bindings" OFF)
|
||||
option(MANIFOLD_JSBIND "Build js binding" ${EMSCRIPTEN})
|
||||
|
||||
option(TRACY_ENABLE "Use tracy profiling" OFF)
|
||||
option(TRACY_MEMORY_USAGE "Track memory allocation with tracy (expensive)" OFF)
|
||||
option(MANIFOLD_FUZZ "Enable fuzzing tests" OFF)
|
||||
option(ASSIMP_ENABLE "Enable ASSIMP for utilities in extras" OFF)
|
||||
mark_as_advanced(TRACY_ENABLE)
|
||||
mark_as_advanced(TRACY_MEMORY_USAGE)
|
||||
mark_as_advanced(MANIFOLD_FUZZ)
|
||||
mark_as_advanced(ASSIMP_ENABLE)
|
||||
|
||||
# Always build position independent code for relocatability
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# Various Compiler Flags
|
||||
if(MANIFOLD_FUZZ)
|
||||
# we should enable debug checks
|
||||
set(MANIFOLD_DEBUG ON)
|
||||
# enable fuzztest fuzzing mode
|
||||
set(FUZZTEST_FUZZING_MODE ON)
|
||||
# address sanitizer required
|
||||
add_compile_options(-fsanitize=address)
|
||||
add_link_options(-fsanitize=address)
|
||||
endif()
|
||||
|
||||
option(BUILD_TEST_CGAL "Build CGAL performance comparisons" OFF)
|
||||
if(TRACY_ENABLE)
|
||||
option(CMAKE_BUILD_TYPE "Build type" RelWithDebInfo)
|
||||
if(TRACY_MEMORY_USAGE)
|
||||
set(MANIFOLD_FLAGS ${MANIFOLD_FLAGS} -DTRACY_MEMORY_USAGE)
|
||||
endif()
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
else()
|
||||
option(CMAKE_BUILD_TYPE "Build type" Release)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
message("Building for Emscripten")
|
||||
add_link_options(-sALLOW_MEMORY_GROWTH=1)
|
||||
add_link_options(-sMAXIMUM_MEMORY=4294967296)
|
||||
if(MANIFOLD_PAR)
|
||||
set(CMAKE_THREAD_LIBS_INIT "-pthread")
|
||||
add_compile_options(-pthread)
|
||||
# mimalloc is needed for good performance
|
||||
add_link_options(-sMALLOC=mimalloc)
|
||||
add_link_options(-sPTHREAD_POOL_SIZE=4)
|
||||
# The default stack size apparently causes problem when parallelization is
|
||||
# enabled.
|
||||
add_link_options(-sSTACK_SIZE=30MB)
|
||||
add_link_options(-sINITIAL_MEMORY=32MB)
|
||||
endif()
|
||||
if(MANIFOLD_DEBUG)
|
||||
list(APPEND MANIFOLD_FLAGS -fexceptions)
|
||||
add_link_options(-fexceptions)
|
||||
add_link_options(-sDISABLE_EXCEPTION_CATCHING=0)
|
||||
endif()
|
||||
set(MANIFOLD_FLAGS -fexceptions -D_LIBCUDACXX_HAS_THREAD_API_EXTERNAL -D_LIBCUDACXX_HAS_THREAD_API_CUDA)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sALLOW_MEMORY_GROWTH=1 -fexceptions -sDISABLE_EXCEPTION_CATCHING=0")
|
||||
set(MANIFOLD_PYBIND OFF)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
endif()
|
||||
|
||||
if(MANIFOLD_PYBIND)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
if(Python_VERSION VERSION_LESS 3.12)
|
||||
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
||||
else()
|
||||
find_package(Python COMPONENTS Interpreter Development.SABIModule REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_EXPORT_COMPILE_COMMANDS AND NOT EMSCRIPTEN)
|
||||
# for nixos
|
||||
set(
|
||||
CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
|
||||
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}
|
||||
)
|
||||
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
|
||||
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
list(APPEND MANIFOLD_FLAGS /DNOMINMAX /bigobj /fp:precise)
|
||||
if (MSVC)
|
||||
set(MANIFOLD_FLAGS ${MANIFOLD_FLAGS} /DNOMINMAX)
|
||||
else()
|
||||
list(
|
||||
APPEND
|
||||
WARNING_FLAGS
|
||||
-Wall
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-unused
|
||||
-Wno-shorten-64-to-32
|
||||
-Wno-alloc-size-larger-than
|
||||
)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
list(APPEND WARNING_FLAGS -Wno-format)
|
||||
endif()
|
||||
elseif(PROJECT_IS_TOP_LEVEL)
|
||||
# only do -Werror if we are the top level project and
|
||||
# MANIFOLD_STRICT is on
|
||||
if(MANIFOLD_STRICT)
|
||||
list(APPEND WARNING_FLAGS -Werror)
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND MANIFOLD_FLAGS ${WARNING_FLAGS})
|
||||
if(
|
||||
MANIFOLD_OPTIMIZED
|
||||
OR "${CMAKE_BUILD_TYPE}" STREQUAL "Release"
|
||||
OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"
|
||||
)
|
||||
list(APPEND MANIFOLD_FLAGS -O3)
|
||||
endif()
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||
list(APPEND MANIFOLD_FLAGS -fno-omit-frame-pointer)
|
||||
endif()
|
||||
# disable fp math optimizations, e.g. FMA for supported architectures, as
|
||||
# this changes floating-point results
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-ffp-contract=off HAS_FP_CONTRACT)
|
||||
check_cxx_compiler_flag(-fexcess-precision=standard HAS_FP_PRECISION)
|
||||
if(HAS_FP_CONTRACT)
|
||||
list(APPEND MANIFOLD_FLAGS -ffp-contract=off)
|
||||
endif()
|
||||
if(HAS_FP_PRECISION)
|
||||
list(APPEND MANIFOLD_FLAGS -fexcess-precision=standard)
|
||||
endif()
|
||||
if(CODE_COVERAGE)
|
||||
list(
|
||||
APPEND
|
||||
MANIFOLD_FLAGS
|
||||
-coverage
|
||||
-fno-inline-small-functions
|
||||
-fkeep-inline-functions
|
||||
-fkeep-static-functions
|
||||
)
|
||||
add_link_options(-coverage)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(WARNING_FLAGS -Werror -Wall -Wno-sign-compare -Wno-unused -Wno-array-bounds -Wno-stringop-overflow)
|
||||
else()
|
||||
set(WARNING_FLAGS -Werror -Wall -Wno-sign-compare -Wno-unused)
|
||||
endif()
|
||||
set(MANIFOLD_FLAGS ${MANIFOLD_FLAGS} ${WARNING_FLAGS} -O3)
|
||||
endif()
|
||||
|
||||
# RPath settings
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
list(
|
||||
FIND
|
||||
CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
|
||||
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}
|
||||
isSystemDir
|
||||
)
|
||||
if("${isSystemDir}" STREQUAL "-1")
|
||||
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
|
||||
endif("${isSystemDir}" STREQUAL "-1")
|
||||
if(CODE_COVERAGE AND NOT MSVC)
|
||||
set(COVERAGE_FLAGS -coverage -fno-inline-small-functions -fkeep-inline-functions -fkeep-static-functions)
|
||||
set(MANIFOLD_FLAGS ${MANIFOLD_FLAGS} ${COVERAGE_FLAGS})
|
||||
add_link_options("-coverage")
|
||||
endif()
|
||||
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/manifoldDeps.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/configHelper.cmake)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(bindings)
|
||||
|
||||
if(MANIFOLD_EXPORT)
|
||||
add_subdirectory(meshIO)
|
||||
endif()
|
||||
|
||||
if(MANIFOLD_TEST)
|
||||
add_subdirectory(samples)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(extras)
|
||||
endif()
|
||||
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/info.cmake)
|
||||
|
||||
# note that the path ${CMAKE_CURRENT_BINARY_DIR}/include is included when we
|
||||
# build the manifold target (as ${PROJECT_SOURCE_DIR}/include), so users can
|
||||
# include manifold/version.h without installing our library.
|
||||
configure_file(
|
||||
cmake/version.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/manifold/version.h
|
||||
@ONLY
|
||||
)
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/manifold/version.h
|
||||
PROPERTIES GENERATED TRUE
|
||||
)
|
||||
|
||||
# If it's an EMSCRIPTEN build, we're done
|
||||
if(EMSCRIPTEN)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# CMake exports
|
||||
configure_file(
|
||||
cmake/manifoldConfig.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifoldConfig.cmake
|
||||
@ONLY
|
||||
)
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/manifoldConfigVersion.cmake
|
||||
VERSION ${MANIFOLD_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# Location of inputs for CMake find_package - see:
|
||||
# https://cmake.org/cmake/help/latest/command/find_package.html
|
||||
set(EXPORT_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake)
|
||||
|
||||
install(
|
||||
EXPORT manifoldTargets
|
||||
NAMESPACE manifold::
|
||||
DESTINATION ${EXPORT_INSTALL_DIR}/manifold
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/manifoldConfigVersion.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifoldConfig.cmake
|
||||
DESTINATION ${EXPORT_INSTALL_DIR}/manifold
|
||||
)
|
||||
|
||||
# install public headers
|
||||
set(
|
||||
MANIFOLD_PUBLIC_HDRS
|
||||
common.h
|
||||
linalg.h
|
||||
manifold.h
|
||||
optional_assert.h
|
||||
polygon.h
|
||||
vec_view.h
|
||||
$<$<BOOL:${MANIFOLD_CROSS_SECTION}>:cross_section.h>
|
||||
)
|
||||
list(TRANSFORM MANIFOLD_PUBLIC_HDRS PREPEND include/manifold/)
|
||||
list(
|
||||
APPEND
|
||||
MANIFOLD_PUBLIC_HDRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/manifold/version.h
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${MANIFOLD_PUBLIC_HDRS}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/manifold
|
||||
)
|
||||
|
||||
# PkgConfig file
|
||||
if(MANIFOLD_PAR)
|
||||
set(TEMPLATE_OPTIONAL_TBB "tbb")
|
||||
endif()
|
||||
if(MANIFOLD_CROSS_SECTION)
|
||||
set(TEMPLATE_OPTIONAL_CLIPPER "Clipper2")
|
||||
endif()
|
||||
configure_file(
|
||||
cmake/manifold.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifold.pc
|
||||
@ONLY
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/manifold.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
@@ -3,6 +3,19 @@
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement (CLA). You (or your employer) retain the copyright to your
|
||||
contribution; this simply gives us permission to use and redistribute your
|
||||
contributions as part of the project. Head over to
|
||||
<https://cla.developers.google.com/> to see your current agreements on file or
|
||||
to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code Reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
@@ -13,4 +26,4 @@ information on using pull requests.
|
||||
## Community Guidelines
|
||||
|
||||
This project follows
|
||||
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
||||
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
||||
@@ -1,100 +1,58 @@
|
||||
# About Manifold
|
||||
|
||||
[](https://codecov.io/github/elalish/manifold)
|
||||
[](https://badge.fury.io/py/manifold3d)
|
||||
[](https://badge.fury.io/js/manifold-3d)
|
||||
[](https://twitter.com/intent/follow?screen_name=manifoldcad)
|
||||
|
||||
[**C++ Documentation**](https://manifoldcad.org/docs/html/classmanifold_1_1_manifold.html) | [**ManifoldCAD User Guide**](https://manifoldcad.org/docs/jsuser/) | [**JS/TS/WASM API**](https://manifoldcad.org/docs/jsapi/) | [**Algorithm Documentation**](https://github.com/elalish/manifold/wiki/Manifold-Library) | [**Blog Posts**](https://elalish.blogspot.com/search/label/Manifold) | [**Web Examples**](https://manifoldcad.org/model-viewer.html)
|
||||
|
||||
[Manifold](https://github.com/elalish/manifold) is a geometry library dedicated to creating and operating on manifold triangle meshes. A [manifold mesh](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness) is a mesh that represents a solid object, and so is very important in manufacturing, CAD, structural analysis, etc. Manifold also supports arbitrary vertex properties and enables mapping of materials for rendering use-cases. Our primary goal is reliability: guaranteed manifold output without caveats or edge cases. Our secondary goal is performance: efficient algorithms that make extensive use of parallelization, or pipelining when only a single thread is available.
|
||||
|
||||
## Users
|
||||
|
||||
Here is an incomplete list of our users, whose integrations may be anywhere from in-progress to released. Please feel free to send a PR to update this list with your own project - it's quite difficult for us to keep track.
|
||||
[OpenSCAD](https://openscad.org/), [IFCjs](https://ifcjs.github.io/info/), [Grid.Space](https://grid.space/), and [OCADml](https://github.com/OCADml/OManifold) have all integrated our Manifold geometry kernel! Why? Because its reliability is guaranteed and it's 1,000 times faster than other libraries. See our [usage](https://github.com/elalish/manifold/discussions/340) and [performance](https://github.com/elalish/manifold/discussions/383) discussions for all the latest and to add your own projects & analyses.
|
||||
|
||||
| | | |
|
||||
| --- | --- | --- |
|
||||
| [OpenSCAD](https://openscad.org/) | [Blender](https://www.blender.org/) | [IFCjs](https://ifcjs.github.io/info/) |
|
||||
| [Nomad Sculpt](https://apps.apple.com/us/app/id1519508653?mt=8&platform=ipad) | [Grid.Space](https://grid.space/) | [badcad](https://github.com/wrongbad/badcad) |
|
||||
| [Godot Engine](https://godotengine.org/) | [OCADml](https://github.com/OCADml/OManifold) | [Flitter](https://flitter.readthedocs.io/en/latest/) |
|
||||
| [BRL-CAD](https://brlcad.org/) | [PolygonJS](https://polygonjs.com/) | [Spherene](https://spherene.ch/) |
|
||||
| [Babylon.js](https://doc.babylonjs.com/features/featuresDeepDive/mesh/mergeMeshes#merging-meshes-with-constructive-solid-geometry) | [trimesh](https://trimesh.org/) | [Gypsum](https://github.com/playkostudios/gypsum) |
|
||||
| [Valence 3D](https://apps.apple.com/us/app/valence-3d/id6450967410?mt=8&platform=ipad) | [bitbybit.dev](https://bitbybit.dev) | [PythonOpenSCAD](https://github.com/owebeeone/pythonopenscad) |
|
||||
| [Conversation](https://james-bern.github.io/conversation.html) | [AnchorSCAD](https://github.com/owebeeone/anchorscad-core) | [Dactyl Web Configurator](https://github.com/rianadon/dactyl-configurator) |
|
||||
| [Arcol](https://arcol.io) | [Bento3D](https://bento3d.design) | [SKÅPA](https://skapa.build) |
|
||||
| [Cadova](https://github.com/tomasf/Cadova) | [BREP.io](https://github.com/mmiscool/BREP) | [Otterplans](https://otterplans.com) |
|
||||
| [Bracket Engineer](https://bracket.engineer) | [Nodillo](https://nodillo3d.com) | |
|
||||
For example, here is a log-log plot of Manifold's performance vs. earlier OpenSCAD geometry backends:
|
||||
|
||||
### Bindings & Packages
|
||||
<img src="https://elalish.github.io/manifold/samples/models/perfSpheres.png" width="350px"/>
|
||||
|
||||
Manifold has bindings to many other languages, some maintained in this repository, and others elsewhere. It can also be built in C++ via [vcpkg](https://github.com/microsoft/vcpkg.git).
|
||||
## [ManifoldCAD.org](https://manifoldcad.org)
|
||||
|
||||
| Language | Packager | Name | Maintenance |
|
||||
| --- | --- | --- | --- |
|
||||
| C | N/A | N/A | internal |
|
||||
| C++ | vcpkg | [manifold](https://github.com/microsoft/vcpkg/tree/master/ports/manifold) | external |
|
||||
| TS/JS | npm | [manifold-3d](https://www.npmjs.com/package/manifold-3d) | internal |
|
||||
| Python | PyPI | [manifold3d](https://pypi.org/project/manifold3d/) | internal |
|
||||
| Java | N/A | [manifold](https://github.com/SovereignShop/manifold) | external |
|
||||
| Clojure | N/A | [clj-manifold3d](https://github.com/SovereignShop/clj-manifold3d) | external |
|
||||
| C# | NuGet | [ManifoldNET](https://www.nuget.org/packages/ManifoldNET) | external |
|
||||
| Julia | Packages | [ManifoldBindings.jl](https://juliapackages.com/p/manifoldbindings) | external |
|
||||
| OCaml | N/A | [OManifold](https://ocadml.github.io/OManifold/OManifold/index.html) | external |
|
||||
| Swift | SPM | [Manifold-Swift](https://github.com/tomasf/manifold-swift) | external |
|
||||
If you like OpenSCAD / JSCAD, you might also like ManifoldCAD - our own solid modelling web app. Our WASM is not multithreaded yet, but it's still quite fast and a good way to test out our Manifold library.
|
||||
|
||||
## Frontend Sandboxes
|
||||

|
||||
|
||||
[ManifoldCAD.org]: https://manifoldcad.org
|
||||
[Python Colab Example]: https://colab.research.google.com/drive/1VxrFYHPSHZgUbl9TeWzCeovlpXrPQ5J5?usp=sharing
|
||||
### Note for Firefox users
|
||||
|
||||
### [ManifoldCAD.org]
|
||||
If you find the editor is stuck on **Loading...**, setting
|
||||
`dom.workers.modules.enabled: true` in your `about:config`, as mentioned in the
|
||||
discussion of the
|
||||
[issue#328](https://github.com/elalish/manifold/issues/328#issuecomment-1473847102)
|
||||
of this repository may solve the problem.
|
||||
|
||||
If you like OpenSCAD / JSCAD, you might also like [ManifoldCAD][ManifoldCAD.org] - our own solid modelling web app where you script in JS/TS. This uses our npm package, [manifold-3d](https://www.npmjs.com/package/manifold-3d), built via WASM. It's not quite as fast as our raw C++, but it's hard to beat for interoperability.
|
||||
# Manifold
|
||||
|
||||
### [Python Colab Example]
|
||||
[**API Documentation**](https://elalish.github.io/manifold/docs/html/modules.html) | [**Algorithm Documentation**](https://github.com/elalish/manifold/wiki/Manifold-Library) | [**Blog Posts**](https://elalish.blogspot.com/search/label/Manifold) | [**Web Examples**](https://elalish.github.io/manifold/model-viewer.html)
|
||||
|
||||
If you prefer Python to JS/TS, make your own copy of [the notebook][Python Colab Example]. It demonstrates interop between our [`manifold3d`](https://pypi.org/project/manifold3d/) PyPI library and the popular [`trimesh`](https://pypi.org/project/trimesh/) library, including showing the interactive model right in the notebook and saving 3D model output.
|
||||
[Manifold](https://github.com/elalish/manifold) is a geometry library dedicated to creating and operating on manifold triangle meshes. A [manifold mesh](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness) is a mesh that represents a solid object, and so is very important in manufacturing, CAD, structural analysis, etc. Further information can be found on the [wiki](https://github.com/elalish/manifold/wiki/Manifold-Library).
|
||||
|
||||

|
||||
This is a modern C++ library that Github's CI verifies builds and runs on a variety of platforms. Additionally, we build bindings for JavaScript ([manifold-3d](https://www.npmjs.com/package/manifold-3d) on npm), Python, and C to make this library more portable and easy to use.
|
||||
|
||||
## Manifold Library
|
||||
System Dependencies (note that we will automatically download the dependency if there is no such package on the system):
|
||||
- [`GLM`](https://github.com/g-truc/glm/): A compact header-only vector library.
|
||||
- [`Thrust`](https://github.com/NVIDIA/thrust): NVIDIA's parallel algorithms library (basically a superset of C++17 std::parallel_algorithms)
|
||||
- [`tbb`](https://github.com/oneapi-src/oneTBB/): Intel's thread building blocks library. (only when `MANIFOLD_PAR=TBB` is enabled)
|
||||
- [`gtest`](https://github.com/google/googletest/): Google test library (only when test is enabled, i.e. `MANIFOLD_TEST=ON`)
|
||||
|
||||
This library is fast with guaranteed manifold output. As such you need manifold meshes as input, which this library can create using constructors inspired by the OpenSCAD API, as well as a level set function for evaluating signed-distance functions (SDF) that improves significantly over Marching Cubes. You can also pass in your own mesh data, but you'll get an error status if the imported mesh isn't manifold. We provide a [`Merge`](https://manifoldcad.org/docs/html/structmanifold_1_1_mesh_g_l_p.html) function to fix slightly non-manifold meshes, but in general you may need one of the automated repair tools that exist mostly for 3D printing.
|
||||
Other dependencies:
|
||||
- [`Clipper2`](https://github.com/AngusJohnson/Clipper2): provides our 2D subsystem
|
||||
- [`quickhull`](https://github.com/akuukka/quickhull): 3D convex hull algorithm.
|
||||
|
||||
## What's here
|
||||
|
||||
This library is fast with guaranteed manifold output. As such you need manifold meshes as input, which this library can create using constructors inspired by the OpenSCAD API, as well as more advanced features like smoothing and signed-distance function (SDF) level sets. You can also pass in your own mesh data, but you'll get an error status if the imported mesh isn't manifold. Various automated repair tools exist online for fixing non manifold models, usually for 3D printing.
|
||||
|
||||
The most significant contribution here is a guaranteed-manifold [mesh Boolean](https://github.com/elalish/manifold/wiki/Manifold-Library#mesh-boolean) algorithm, which I believe is the first of its kind. If you know of another, please open a discussion - a mesh Boolean algorithm robust to edge cases has been an open problem for many years. Likewise, if the Boolean here ever fails you, please submit an issue! This Boolean forms the basis of a CAD kernel, as it allows simple shapes to be combined into more complex ones.
|
||||
|
||||
Manifold has full support for arbitrary vertex properties, and also has IDs that make it easy to keep track of materials and what surfaces belong to what input objects or faces. See our [web example](https://manifoldcad.org/model-viewer.html) for a simple demonstration of combining objects with unique textures.
|
||||
To aid in speed, this library makes extensive use of parallelization, generally through Nvidia's Thrust library. You can switch between the TBB, and serial C++ backends by setting a CMake flag. Not everything is so parallelizable, for instance a [polygon triangulation](https://github.com/elalish/manifold/wiki/Manifold-Library#polygon-triangulation) algorithm is included which is serial. Even if compiled with parallel backend, the code will still fall back to the serial version of the algorithms if the problem size is small. The WASM build is serial-only for now, but still fast.
|
||||
|
||||
Also included are a novel and powerful suite of refining functions for smooth mesh interpolation. They handle smoothing both triangles and quads, as well as keeping polygonal faces flat. You can easily create sharp or small-radius edges where desired, or even drive the curvature by normal vectors.
|
||||
|
||||
To aid in speed, this library makes extensive use of parallelization through TBB, if enabled. Not everything is so parallelizable, for instance a [polygon triangulation](https://github.com/elalish/manifold/wiki/Manifold-Library#polygon-triangulation) algorithm is included which is serial. Even if compiled with parallel backend, the code will still fall back to the serial version of the algorithms if the problem size is small. The WASM build is serial-only for now, but still fast.
|
||||
> Note: OMP and CUDA backends are now removed
|
||||
|
||||
Look in the [samples](https://github.com/elalish/manifold/tree/master/samples) directory for examples of how to use this library to make interesting 3D models. You may notice that some of these examples bare a certain resemblance to my OpenSCAD designs on [Thingiverse](https://www.thingiverse.com/emmett), which is no accident. Much as I love OpenSCAD, my library is dramatically faster and the code is more flexible.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Manifold no longer has **any** required dependencies! However, we do have several optional dependencies, of which the first two are strongly encouraged:
|
||||
| Name | CMake Flag | Provides |
|
||||
| --- | --- | --- |
|
||||
| [`TBB`](https://github.com/oneapi-src/oneTBB/) |`MANIFOLD_PAR=ON` | Parallel acceleration |
|
||||
| [`Clipper2`](https://github.com/AngusJohnson/Clipper2) | `MANIFOLD_CROSS_SECTION=ON` | 2D: [`CrossSection`](https://manifoldcad.org/docs/html/classmanifold_1_1_cross_section.html) |
|
||||
| [`Nanobind`](https://github.com/wjakob/nanobind) | `MANIFOLD_PYBIND=ON` | Python bindings |
|
||||
| [`Emscripten`](https://github.com/emscripten-core/emscripten) | `MANIFOLD_JSBIND=ON` | JS bindings via WASM |
|
||||
| [`GTest`](https://github.com/google/googletest/) | `MANIFOLD_TEST=ON` | Testing framework |
|
||||
| [`Assimp`](https://github.com/assimp/assimp) | `ASSIMP_ENABLE=ON` | Utilities in `extras` |
|
||||
| [`Tracy`](https://github.com/wolfpld/tracy) | `TRACY_ENABLE=ON` | Performance analysis |
|
||||
|
||||
|
||||
### 3D Formats
|
||||
|
||||
Please avoid saving to STL files! They are lossy and inefficient - when saving a manifold mesh to STL there is no guarantee that the re-imported mesh will still be manifold, as the topology is lost. Please consider using [3MF](https://3mf.io/) instead, as this format was designed from the beginning for manifold meshes representing solid objects.
|
||||
|
||||
If you use vertex properties for things like interpolated normals or texture UV coordinates, [glTF](https://www.khronos.org/Gltf) is recommended, specifically using the [`EXT_mesh_manifold`](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_mesh_manifold/README.md) extension. This allows for the lossless and efficient transmission of manifoldness even with property boundaries. Try our [make-manifold](https://manifoldcad.org/make-manifold) page to add this extension to your existing glTF/GLB models.
|
||||
|
||||
Manifold provides high precision OBJ file IO, but it is limited in functionality and is primarily to aid in testing. If you are using our npm module, we have a much more capable [gltf-io.ts](https://github.com/elalish/manifold/blob/master/bindings/wasm/examples/gltf-io.ts) you can use instead. For other languages we strongly recommend using existing packages that focus on 3D file I/O, e.g. [trimesh](https://trimesh.org/) for Python, particularly when using vertex properties or materials.
|
||||
Example for integrating with [Assimp](https://github.com/assimp/assimp) is in `extras/meshIO.cpp`, which is used by files such as `extras/convert_file.cpp`.
|
||||
|
||||
## Building
|
||||
|
||||
Only CMake, a C++ compiler, and Python are required to be installed and set up to build this library (it has been tested with GCC, LLVM, MSVC). However, a variety of optional dependencies can bring in more functionality, see below.
|
||||
@@ -106,62 +64,29 @@ cd manifold
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON .. && make
|
||||
make test
|
||||
test/manifold_test
|
||||
```
|
||||
|
||||
CMake flags (usage e.g. `-DMANIFOLD_DEBUG=ON`):
|
||||
- `MANIFOLD_JSBIND=[OFF, <ON>]`: Build js binding (when using the emscripten toolchain).
|
||||
- `MANIFOLD_JSBIND=[OFF, <ON>]`: Build js binding when using emscripten.
|
||||
- `MANIFOLD_CBIND=[<OFF>, ON]`: Build C FFI binding.
|
||||
- `MANIFOLD_PYBIND=[OFF, <ON>]`: Build python binding, requires `nanobind`.
|
||||
- `MANIFOLD_PAR=[<OFF>, ON]`: Enables multi-thread parallelization, requires `tbb`.
|
||||
- `MANIFOLD_CROSS_SECTION=[OFF, <ON>]`: Build CrossSection for 2D support (needed by language bindings), requires `Clipper2`.
|
||||
- `MANIFOLD_DEBUG=[<OFF>, ON]`: Enables exceptions, timing, verbosity, OBJ test dumps. Has almost no effect on its own, but enables further runtime parameters to dump various outputs.
|
||||
- `MANIFOLD_ASSERT=[<OFF>, ON]`: Enables internal assertions. This incurs around 20% runtime overhead. Requires MANIFOLD_DEBUG to work.
|
||||
- `MANIFOLD_TEST=[OFF, <ON>]`: Build unit tests, requires `GTest`.
|
||||
- `MANIFOLD_PYBIND=[OFF, <ON>]`: Build python binding.
|
||||
- `MANIFOLD_PAR=[<NONE>, TBB]`: Provides multi-thread parallelization, requires `libtbb-dev` if `TBB` backend is selected.
|
||||
- `MANIFOLD_EXPORT=[<OFF>, ON]`: Enables GLB export of 3D models from the tests, requires `libassimp-dev`.
|
||||
- `MANIFOLD_DEBUG=[<OFF>, ON]`: Enables internal assertions and exceptions.
|
||||
- `MANIFOLD_TEST=[OFF, <ON>]`: Build unittests.
|
||||
- `TRACY_ENABLE=[<OFF>, ON]`: Enable integration with tracy profiler.
|
||||
See profiling section below.
|
||||
- `ASSIMP_ENABLE=[<OFF>, ON]`: Enable integration with assimp, which is needed for some of the utilities in `extras`.
|
||||
- `MANIFOLD_STRICT=[<OFF>, ON]`: Treat compile warnings as fatal build errors.
|
||||
- `BUILD_TEST_CGAL=[<OFF>, ON]`: Builds a CGAL-based performance [comparison](https://github.com/elalish/manifold/tree/master/extras), requires `libcgal-dev`.
|
||||
|
||||
Dependency version override:
|
||||
- `MANIFOLD_USE_BUILTIN_TBB=[<OFF>, ON]`: Use builtin version of tbb.
|
||||
- `MANIFOLD_USE_BUILTIN_CLIPPER2=[<OFF>, ON]`: Use builtin version of clipper2.
|
||||
- `MANIFOLD_USE_BUILTIN_NANOBIND=[<OFF>, ON]`: Use builtin version of nanobind.
|
||||
|
||||
> Note: These three options can force the build to avoid using the system
|
||||
> version of the dependency. This will either use the provided source directory
|
||||
> via `FETCHCONTENT_SOURCE_DIR_*` (see below), or fetch the source from GitHub.
|
||||
> Note that the dependency will be built as static dependency to avoid dynamic
|
||||
> library conflict. When the system package is unavailable, the option will be
|
||||
> automatically set to true (except for tbb).
|
||||
|
||||
> WARNING: These packages are statically linked to the library, which may be
|
||||
> unexpected for other consumers of the library. In particular, for tbb, this
|
||||
> create two versions of tbb when another library also bring their own tbb,
|
||||
> which may cause performance issues or crash the system.
|
||||
> It is not recommended to install manifold compiled with builtin tbb, and this
|
||||
> option requires explicit opt-in now.
|
||||
|
||||
Offline building (with missing dependencies/dependency version override):
|
||||
- `MANIFOLD_DOWNLOADS=[OFF, <ON>]`: Automatically download missing dependencies.
|
||||
Need to set `FETCHCONTENT_SOURCE_DIR_*` if the dependency `*` is missing.
|
||||
- `FETCHCONTENT_SOURCE_DIR_TBB`: path to tbb source (if `MANIFOLD_PAR` is enabled).
|
||||
- `FETCHCONTENT_SOURCE_DIR_CLIPPER2`: path to tbb source (if `MANIFOLD_CROSS_SECTION` is enabled).
|
||||
- `FETCHCONTENT_SOURCE_DIR_NANOBIND`: path to nanobind source (if `MANIFOLD_PYBIND` is enabled).
|
||||
- `FETCHCONTENT_SOURCE_DIR_GOOGLETEST`: path to googletest source (if `MANIFOLD_TEST` is enabled).
|
||||
|
||||
> Note: When `FETCHCONTENT_SOURCE_DIR_*` is set, CMake will use the provided
|
||||
> source directly without downloading regardless of the value of
|
||||
> `MANIFOLD_DOWNLOADS`.
|
||||
Offline building:
|
||||
- `FETCHCONTENT_SOURCE_DIR_GLM`: path to glm source.
|
||||
- `FETCHCONTENT_SOURCE_DIR_GOOGLETEST`: path to googletest source.
|
||||
- `FETCHCONTENT_SOURCE_DIR_THRUST`: path to NVIDIA thrust source.
|
||||
|
||||
The build instructions used by our CI are in [manifold.yml](https://github.com/elalish/manifold/blob/master/.github/workflows/manifold.yml), which is a good source to check if something goes wrong and for instructions specific to other platforms, like Windows.
|
||||
|
||||
### WASM
|
||||
|
||||
> Note: While we support compiling with `MANIFOLD_PAR=ON` in recent emscripten
|
||||
> versions, this is not recommended as there can potentially be memory
|
||||
> corruption issues.
|
||||
|
||||
To build the JS WASM library, first install NodeJS and set up emscripten:
|
||||
|
||||
(on Mac):
|
||||
@@ -183,9 +108,8 @@ Then build:
|
||||
cd manifold
|
||||
mkdir buildWASM
|
||||
cd buildWASM
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel .. && emmake make
|
||||
cd test
|
||||
node ./manifold_test.js
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=Release .. && emmake make
|
||||
node test/manifold_test.js
|
||||
```
|
||||
|
||||
### Python
|
||||
@@ -196,7 +120,7 @@ use the extension, please add `$BUILD_DIR/bindings/python` to your `PYTHONPATH`,
|
||||
can be found in `bindings/python/examples`. To see exported samples, run:
|
||||
```
|
||||
sudo apt install pkg-config libpython3-dev python3 python3-distutils python3-pip
|
||||
pip install trimesh pytest
|
||||
pip install trimesh
|
||||
python3 run_all.py -e
|
||||
```
|
||||
|
||||
@@ -210,46 +134,15 @@ python binding documentation:
|
||||
|
||||
For more detailed documentation, please refer to the C++ API.
|
||||
|
||||
### Windows Shenanigans
|
||||
|
||||
Windows users should build with `-DBUILD_SHARED_LIBS=OFF`, as enabling shared
|
||||
libraries in general makes things very complicated.
|
||||
|
||||
The DLL file for manifoldc (C FFI bindings) when built with msvc is in `${CMAKE_BINARY_DIR}/bin/${BUILD_TYPE}/manifoldc.dll`.
|
||||
For example, for the following command, the path relative to the project root directory is `build/bin/Release/manifoldc.dll`.
|
||||
```sh
|
||||
cmake . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DMANIFOLD_DEBUG=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} -A x64 -B build
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! A lower barrier contribution is to simply make a PR that adds a test, especially if it repros an issue you've found. Simply name it prepended with DISABLED_, so that it passes the CI. That will be a very strong signal to me to fix your issue. However, if you know how to fix it yourself, then including the fix in your PR would be much appreciated!
|
||||
|
||||
### Formatting
|
||||
|
||||
There is a formatting script `format.sh` that automatically formats everything.
|
||||
It requires clang-format, black formatter for python and [gersemi](https://github.com/BlankSpruce/gersemi) for formatting cmake files.
|
||||
|
||||
Note that our script can run with clang-format older than 18, but the GitHub
|
||||
action check may fail due to slight differences between different versions of
|
||||
clang-format. In that case, either update your clang-format version or apply the
|
||||
patch from the GitHub action log.
|
||||
|
||||
### Profiling
|
||||
## Profiling
|
||||
|
||||
There is now basic support for the [Tracy profiler](https://github.com/wolfpld/tracy) for our tests.
|
||||
To enable tracing, compile with `-DTRACY_ENABLE=on` cmake option, and run the test with Tracy server running.
|
||||
To enable memory profiling in addition to tracing, compile with `-DTRACY_MEMORY_USAGE=ON` in addition to `-DTRACY_ENABLE=ON`.
|
||||
|
||||
### Fuzzing
|
||||
|
||||
To build with fuzzing support, you should set the following with CMake:
|
||||
|
||||
- Enable fuzzing by setting `-DMANIFOLD_FUZZ=ON`
|
||||
- Disable python bindings by setting `-DMANIFOLD_PYBIND=OFF`
|
||||
- Use `clang` for compiling by setting `-DCMAKE_CXX_COMPILER=clang++`
|
||||
- You may need to disable parallelization by setting `-DMANIFOLD_PAR=OFF`, and set `ASAN_OPTIONS=detect_container_overflow=0` when building the binary on MacOS.
|
||||
|
||||
## About the author
|
||||
|
||||
This library was started by [Emmett Lalish](https://elalish.blogspot.com/), currently a senior rendering engineer at Wētā FX. This was my 20% project when I was a Google employee, though my day job was maintaining [\<model-viewer\>](https://modelviewer.dev/). I was the first employee at a 3D video startup, [Omnivor](https://www.omnivor.io/), and before that I worked on 3D printing at Microsoft, including [3D Builder](https://www.microsoft.com/en-us/p/3d-builder/9wzdncrfj3t6?activetab=pivot%3Aoverviewtab). Originally an aerospace engineer, I started at a small DARPA contractor doing seedling projects, one of which became [Sea Hunter](https://en.wikipedia.org/wiki/Sea_Hunter). I earned my doctorate from the University of Washington in control theory and published some [papers](https://www.researchgate.net/scientific-contributions/75011026_Emmett_Lalish).
|
||||
This library was started by [Emmett Lalish](https://elalish.blogspot.com/). I am currently a Google employee and this is my 20% project, not an official Google project. At my day job I'm the maintainer of [\<model-viewer\>](https://modelviewer.dev/). I was the first employee at a 3D video startup, [Omnivor](https://www.omnivor.io/), and before that I worked on 3D printing at Microsoft, including [3D Builder](https://www.microsoft.com/en-us/p/3d-builder/9wzdncrfj3t6?activetab=pivot%3Aoverviewtab). Originally an aerospace engineer, I started at a small DARPA contractor doing seedling projects, one of which became [Sea Hunter](https://en.wikipedia.org/wiki/Sea_Hunter). I earned my doctorate from the University of Washington in control theory and published some [papers](https://www.researchgate.net/scientific-contributions/75011026_Emmett_Lalish).
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
1. Go through Github issues to verify bugs have been fixed and closed.
|
||||
1. Verify [ManifoldCAD.org](https://manifoldcad.org) - check a few examples, run them, download a GLB and a 3MF.
|
||||
1. Verify our three.js [example](https://manifoldcad.org/three) is functional.
|
||||
1. Verify our model-viewer [example](https://manifoldcad.org/model-viewer) is functional - select Union and Intersection.
|
||||
1. Verify [make-manifold](https://manifoldcad.org/make-manifold) is functional. Try dropping [DragonAttenuation.glb](https://github.com/KhronosGroup/glTF-Sample-Assets/blob/main/Models/DragonAttenuation/glTF-Binary/DragonAttenuation.glb) in and verify you can select "View Manifold GLB" and that the dragon is still present while the backdrop is removed. Download the GLB.
|
||||
1. Make a new branch called the version, e.g. v2.3.0.
|
||||
1. Use VSCode to search and replace the old version with the new - so far in test-cmake.sh, flake.nix, pyproject.toml, and package.json.
|
||||
1. Also update CMakeLists.txt version by searching for "set(MANIFOLD_VERSION_".
|
||||
1. Commit, push, open a PR, verify tests pass, manually trigger build_wheels CI, merge.
|
||||
1. On Github, draft a new release, make a new tag with the version number, add release notes, and publish.
|
||||
1. Check the Actions and verify that both PyPI and npm publishing actions ran successfully.
|
||||
1. Verify the npm [package](https://www.npmjs.com/package/manifold-3d?activeTab=code) looks good - unpacked size should be close to 1MB.
|
||||
1. Verify PyPI [package](https://pypi.org/project/manifold3d/#files) looks good - a bunch of built distributions ranging from ~600kB to ~1.1MB.
|
||||
1. If there's a problem with release deployment, the release workflows can be triggered separately, manually for any branch, under the Actions tab.
|
||||
@@ -12,18 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
if(NOT MANIFOLD_CROSS_SECTION)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(MANIFOLD_CBIND)
|
||||
add_subdirectory(c)
|
||||
if(NOT EMSCRIPTEN AND MANIFOLD_CBIND)
|
||||
add_subdirectory(c)
|
||||
endif()
|
||||
|
||||
if(NOT EMSCRIPTEN AND MANIFOLD_PYBIND)
|
||||
add_subdirectory(python)
|
||||
add_subdirectory(python)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN AND MANIFOLD_JSBIND)
|
||||
add_subdirectory(wasm)
|
||||
add_subdirectory(wasm)
|
||||
endif()
|
||||
|
||||
@@ -1,45 +1,33 @@
|
||||
# Copyright 2024 The Manifold Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
project(manifoldc)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include
|
||||
add_library(${PROJECT_NAME} manifoldc.cpp conv.cpp)
|
||||
|
||||
if(MANIFOLD_USE_CUDA)
|
||||
set_source_files_properties(manifoldc.cpp conv.cpp PROPERTIES LANGUAGE CUDA)
|
||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CUDA_ARCHITECTURES 61)
|
||||
endif()
|
||||
|
||||
if(MANIFOLD_EXPORT)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE meshIO)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC -DMANIFOLD_EXPORT)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE manifold sdf cross_section
|
||||
)
|
||||
|
||||
add_library(
|
||||
manifoldc
|
||||
manifoldc.cpp
|
||||
conv.cpp
|
||||
box.cpp
|
||||
cross.cpp
|
||||
rect.cpp
|
||||
)
|
||||
exportlib(manifoldc)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS})
|
||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
|
||||
|
||||
target_link_libraries(manifoldc PRIVATE manifold)
|
||||
|
||||
target_include_directories(
|
||||
manifoldc
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
)
|
||||
target_compile_options(manifoldc PRIVATE ${MANIFOLD_FLAGS})
|
||||
|
||||
install(TARGETS manifoldc EXPORT manifoldTargets)
|
||||
install(
|
||||
FILES include/manifold/manifoldc.h include/manifold/types.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/manifold
|
||||
TARGETS ${PROJECT_NAME}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
COMPONENT bindings
|
||||
)
|
||||
install(
|
||||
FILES include/manifoldc.h include/types.h
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/manifold
|
||||
COMPONENT devel
|
||||
)
|
||||
|
||||
@@ -13,91 +13,86 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "conv.h"
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/types.h"
|
||||
#include "manifold.h"
|
||||
#include "public.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
ManifoldBox* manifold_box(void* mem, double x1, double y1, double z1, double x2,
|
||||
double y2, double z2) {
|
||||
auto p1 = vec3(x1, y1, z1);
|
||||
auto p2 = vec3(x2, y2, z2);
|
||||
ManifoldBox *manifold_box(void *mem, float x1, float y1, float z1, float x2,
|
||||
float y2, float z2) {
|
||||
auto p1 = glm::vec3(x1, y1, z1);
|
||||
auto p2 = glm::vec3(x2, y2, z2);
|
||||
auto box = new (mem) Box(p1, p2);
|
||||
return to_c(box);
|
||||
}
|
||||
|
||||
ManifoldVec3 manifold_box_min(ManifoldBox* b) { return to_c((*from_c(b)).min); }
|
||||
ManifoldVec3 manifold_box_min(ManifoldBox *b) { return to_c((*from_c(b)).min); }
|
||||
|
||||
ManifoldVec3 manifold_box_max(ManifoldBox* b) { return to_c((*from_c(b)).max); }
|
||||
ManifoldVec3 manifold_box_max(ManifoldBox *b) { return to_c((*from_c(b)).max); }
|
||||
|
||||
ManifoldVec3 manifold_box_dimensions(ManifoldBox* b) {
|
||||
ManifoldVec3 manifold_box_dimensions(ManifoldBox *b) {
|
||||
auto v = from_c(b)->Size();
|
||||
return {v.x, v.y, v.z};
|
||||
}
|
||||
|
||||
ManifoldVec3 manifold_box_center(ManifoldBox* b) {
|
||||
ManifoldVec3 manifold_box_center(ManifoldBox *b) {
|
||||
auto v = from_c(b)->Center();
|
||||
return {v.x, v.y, v.z};
|
||||
}
|
||||
|
||||
double manifold_box_scale(ManifoldBox* b) { return from_c(b)->Scale(); }
|
||||
float manifold_box_scale(ManifoldBox *b) { return from_c(b)->Scale(); }
|
||||
|
||||
int manifold_box_contains_pt(ManifoldBox* b, double x, double y, double z) {
|
||||
auto p = vec3(x, y, z);
|
||||
int manifold_box_contains_pt(ManifoldBox *b, float x, float y, float z) {
|
||||
auto p = glm::vec3(x, y, z);
|
||||
return from_c(b)->Contains(p);
|
||||
}
|
||||
|
||||
int manifold_box_contains_box(ManifoldBox* a, ManifoldBox* b) {
|
||||
int manifold_box_contains_box(ManifoldBox *a, ManifoldBox *b) {
|
||||
auto outer = *from_c(a);
|
||||
auto inner = *from_c(b);
|
||||
return outer.Contains(inner);
|
||||
}
|
||||
|
||||
void manifold_box_include_pt(ManifoldBox* b, double x, double y, double z) {
|
||||
auto& box = *from_c(b);
|
||||
auto p = vec3(x, y, z);
|
||||
void manifold_box_include_pt(ManifoldBox *b, float x, float y, float z) {
|
||||
auto box = *from_c(b);
|
||||
auto p = glm::vec3(x, y, z);
|
||||
box.Union(p);
|
||||
}
|
||||
|
||||
ManifoldBox* manifold_box_union(void* mem, ManifoldBox* a, ManifoldBox* b) {
|
||||
ManifoldBox *manifold_box_union(void *mem, ManifoldBox *a, ManifoldBox *b) {
|
||||
auto box = from_c(a)->Union(*from_c(b));
|
||||
return to_c(new (mem) Box(box));
|
||||
}
|
||||
|
||||
ManifoldBox* manifold_box_transform(void* mem, ManifoldBox* b, double x1,
|
||||
double y1, double z1, double x2, double y2,
|
||||
double z2, double x3, double y3, double z3,
|
||||
double x4, double y4, double z4) {
|
||||
auto mat = mat3x4({x1, y1, z1}, {x2, y2, z2}, {x3, y3, z3}, {x4, y4, z4});
|
||||
ManifoldBox *manifold_box_transform(void *mem, ManifoldBox *b, float x1,
|
||||
float y1, float z1, float x2, float y2,
|
||||
float z2, float x3, float y3, float z3,
|
||||
float x4, float y4, float z4) {
|
||||
auto mat = glm::mat4x3(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4);
|
||||
auto transformed = from_c(b)->Transform(mat);
|
||||
return to_c(new (mem) Box(transformed));
|
||||
}
|
||||
|
||||
ManifoldBox* manifold_box_translate(void* mem, ManifoldBox* b, double x,
|
||||
double y, double z) {
|
||||
auto p = vec3(x, y, z);
|
||||
ManifoldBox *manifold_box_translate(void *mem, ManifoldBox *b, float x, float y,
|
||||
float z) {
|
||||
auto p = glm::vec3(x, y, z);
|
||||
auto translated = (*from_c(b)) + p;
|
||||
return to_c(new (mem) Box(translated));
|
||||
}
|
||||
|
||||
ManifoldBox* manifold_box_mul(void* mem, ManifoldBox* b, double x, double y,
|
||||
double z) {
|
||||
auto p = vec3(x, y, z);
|
||||
ManifoldBox *manifold_box_mul(void *mem, ManifoldBox *b, float x, float y,
|
||||
float z) {
|
||||
auto p = glm::vec3(x, y, z);
|
||||
auto scaled = (*from_c(b)) * p;
|
||||
return to_c(new (mem) Box(scaled));
|
||||
}
|
||||
|
||||
int manifold_box_does_overlap_pt(ManifoldBox* b, double x, double y, double z) {
|
||||
auto p = vec3(x, y, z);
|
||||
int manifold_box_does_overlap_pt(ManifoldBox *b, float x, float y, float z) {
|
||||
auto p = glm::vec3(x, y, z);
|
||||
return from_c(b)->DoesOverlap(p);
|
||||
}
|
||||
|
||||
int manifold_box_does_overlap_box(ManifoldBox* a, ManifoldBox* b) {
|
||||
int manifold_box_does_overlap_box(ManifoldBox *a, ManifoldBox *b) {
|
||||
return from_c(a)->DoesOverlap(*from_c(b));
|
||||
}
|
||||
|
||||
int manifold_box_is_finite(ManifoldBox* b) { return from_c(b)->IsFinite(); }
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
int manifold_box_is_finite(ManifoldBox *b) { return from_c(b)->IsFinite(); }
|
||||
|
||||
@@ -16,41 +16,42 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/manifold.h"
|
||||
#include "manifold/types.h"
|
||||
#include "cross_section.h"
|
||||
#include "manifold.h"
|
||||
#include "public.h"
|
||||
#include "sdf.h"
|
||||
#include "types.h"
|
||||
|
||||
ManifoldManifold* to_c(manifold::Manifold* m) {
|
||||
return reinterpret_cast<ManifoldManifold*>(m);
|
||||
ManifoldManifold *to_c(manifold::Manifold *m) {
|
||||
return reinterpret_cast<ManifoldManifold *>(m);
|
||||
}
|
||||
|
||||
ManifoldManifoldVec* to_c(ManifoldVec* ms) {
|
||||
return reinterpret_cast<ManifoldManifoldVec*>(ms);
|
||||
ManifoldManifoldVec *to_c(ManifoldVec *ms) {
|
||||
return reinterpret_cast<ManifoldManifoldVec *>(ms);
|
||||
}
|
||||
|
||||
ManifoldCrossSection* to_c(manifold::CrossSection* cs) {
|
||||
return reinterpret_cast<ManifoldCrossSection*>(cs);
|
||||
ManifoldCrossSection *to_c(manifold::CrossSection *cs) {
|
||||
return reinterpret_cast<ManifoldCrossSection *>(cs);
|
||||
}
|
||||
|
||||
ManifoldCrossSectionVec* to_c(CrossSectionVec* csv) {
|
||||
return reinterpret_cast<ManifoldCrossSectionVec*>(csv);
|
||||
ManifoldCrossSectionVec *to_c(CrossSectionVec *csv) {
|
||||
return reinterpret_cast<ManifoldCrossSectionVec *>(csv);
|
||||
}
|
||||
|
||||
ManifoldSimplePolygon* to_c(manifold::SimplePolygon* m) {
|
||||
return reinterpret_cast<ManifoldSimplePolygon*>(m);
|
||||
ManifoldSimplePolygon *to_c(manifold::SimplePolygon *m) {
|
||||
return reinterpret_cast<ManifoldSimplePolygon *>(m);
|
||||
}
|
||||
|
||||
ManifoldPolygons* to_c(manifold::Polygons* m) {
|
||||
return reinterpret_cast<ManifoldPolygons*>(m);
|
||||
ManifoldPolygons *to_c(manifold::Polygons *m) {
|
||||
return reinterpret_cast<ManifoldPolygons *>(m);
|
||||
}
|
||||
|
||||
ManifoldMeshGL* to_c(manifold::MeshGL* m) {
|
||||
return reinterpret_cast<ManifoldMeshGL*>(m);
|
||||
ManifoldMesh *to_c(manifold::Mesh *m) {
|
||||
return reinterpret_cast<ManifoldMesh *>(m);
|
||||
}
|
||||
|
||||
ManifoldMeshGL64* to_c(manifold::MeshGL64* m) {
|
||||
return reinterpret_cast<ManifoldMeshGL64*>(m);
|
||||
ManifoldMeshGL *to_c(manifold::MeshGL *m) {
|
||||
return reinterpret_cast<ManifoldMeshGL *>(m);
|
||||
}
|
||||
|
||||
ManifoldOpType to_c(manifold::OpType optype) {
|
||||
@@ -106,61 +107,58 @@ ManifoldError to_c(manifold::Manifold::Error error) {
|
||||
case Manifold::Error::InvalidConstruction:
|
||||
e = MANIFOLD_INVALID_CONSTRUCTION;
|
||||
break;
|
||||
case Manifold::Error::ResultTooLarge:
|
||||
e = MANIFOLD_RESULT_TOO_LARGE;
|
||||
break;
|
||||
};
|
||||
return e;
|
||||
}
|
||||
|
||||
ManifoldBox* to_c(manifold::Box* m) {
|
||||
return reinterpret_cast<ManifoldBox*>(m);
|
||||
ManifoldBox *to_c(manifold::Box *m) {
|
||||
return reinterpret_cast<ManifoldBox *>(m);
|
||||
}
|
||||
|
||||
ManifoldRect* to_c(manifold::Rect* m) {
|
||||
return reinterpret_cast<ManifoldRect*>(m);
|
||||
ManifoldRect *to_c(manifold::Rect *m) {
|
||||
return reinterpret_cast<ManifoldRect *>(m);
|
||||
}
|
||||
|
||||
ManifoldVec2 to_c(vec2 v) { return {v.x, v.y}; }
|
||||
ManifoldVec2 to_c(glm::vec2 v) { return {v.x, v.y}; }
|
||||
|
||||
ManifoldVec3 to_c(vec3 v) { return {v.x, v.y, v.z}; }
|
||||
ManifoldVec3 to_c(glm::vec3 v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
ManifoldIVec3 to_c(ivec3 v) { return {v.x, v.y, v.z}; }
|
||||
ManifoldIVec3 to_c(glm::ivec3 v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
ManifoldTriangulation* to_c(std::vector<ivec3>* m) {
|
||||
return reinterpret_cast<ManifoldTriangulation*>(m);
|
||||
ManifoldProperties to_c(manifold::Properties p) {
|
||||
return {p.surfaceArea, p.volume};
|
||||
}
|
||||
|
||||
manifold::Manifold* from_c(ManifoldManifold* m) {
|
||||
return reinterpret_cast<manifold::Manifold*>(m);
|
||||
const manifold::Manifold *from_c(ManifoldManifold *m) {
|
||||
return reinterpret_cast<manifold::Manifold const *>(m);
|
||||
}
|
||||
|
||||
ManifoldVec* from_c(ManifoldManifoldVec* ms) {
|
||||
return reinterpret_cast<ManifoldVec*>(ms);
|
||||
ManifoldVec *from_c(ManifoldManifoldVec *ms) {
|
||||
return reinterpret_cast<ManifoldVec *>(ms);
|
||||
}
|
||||
|
||||
manifold::CrossSection* from_c(ManifoldCrossSection* cs) {
|
||||
return reinterpret_cast<manifold::CrossSection*>(cs);
|
||||
const manifold::CrossSection *from_c(ManifoldCrossSection *cs) {
|
||||
return reinterpret_cast<manifold::CrossSection *>(cs);
|
||||
}
|
||||
|
||||
CrossSectionVec* from_c(ManifoldCrossSectionVec* csv) {
|
||||
return reinterpret_cast<CrossSectionVec*>(csv);
|
||||
CrossSectionVec *from_c(ManifoldCrossSectionVec *csv) {
|
||||
return reinterpret_cast<CrossSectionVec *>(csv);
|
||||
}
|
||||
|
||||
manifold::SimplePolygon* from_c(ManifoldSimplePolygon* m) {
|
||||
return reinterpret_cast<manifold::SimplePolygon*>(m);
|
||||
const manifold::SimplePolygon *from_c(ManifoldSimplePolygon *m) {
|
||||
return reinterpret_cast<manifold::SimplePolygon const *>(m);
|
||||
}
|
||||
|
||||
manifold::Polygons* from_c(ManifoldPolygons* m) {
|
||||
return reinterpret_cast<manifold::Polygons*>(m);
|
||||
const manifold::Polygons *from_c(ManifoldPolygons *m) {
|
||||
return reinterpret_cast<manifold::Polygons const *>(m);
|
||||
}
|
||||
|
||||
manifold::MeshGL* from_c(ManifoldMeshGL* m) {
|
||||
return reinterpret_cast<manifold::MeshGL*>(m);
|
||||
const manifold::Mesh *from_c(ManifoldMesh *m) {
|
||||
return reinterpret_cast<manifold::Mesh const *>(m);
|
||||
}
|
||||
|
||||
manifold::MeshGL64* from_c(ManifoldMeshGL64* m) {
|
||||
return reinterpret_cast<manifold::MeshGL64*>(m);
|
||||
const manifold::MeshGL *from_c(ManifoldMeshGL *m) {
|
||||
return reinterpret_cast<manifold::MeshGL const *>(m);
|
||||
}
|
||||
|
||||
OpType from_c(ManifoldOpType optype) {
|
||||
@@ -207,52 +205,45 @@ CrossSection::JoinType from_c(ManifoldJoinType join_type) {
|
||||
case MANIFOLD_JOIN_TYPE_MITER:
|
||||
jt = CrossSection::JoinType::Miter;
|
||||
break;
|
||||
case MANIFOLD_JOIN_TYPE_BEVEL:
|
||||
jt = CrossSection::JoinType::Bevel;
|
||||
break;
|
||||
};
|
||||
return jt;
|
||||
}
|
||||
|
||||
manifold::Box* from_c(ManifoldBox* m) {
|
||||
return reinterpret_cast<manifold::Box*>(m);
|
||||
const manifold::Box *from_c(ManifoldBox *m) {
|
||||
return reinterpret_cast<manifold::Box const *>(m);
|
||||
}
|
||||
|
||||
manifold::Rect* from_c(ManifoldRect* m) {
|
||||
return reinterpret_cast<manifold::Rect*>(m);
|
||||
const manifold::Rect *from_c(ManifoldRect *m) {
|
||||
return reinterpret_cast<manifold::Rect const *>(m);
|
||||
}
|
||||
|
||||
vec2 from_c(ManifoldVec2 v) { return vec2(v.x, v.y); }
|
||||
glm::vec2 from_c(ManifoldVec2 v) { return glm::vec2(v.x, v.y); }
|
||||
|
||||
vec3 from_c(ManifoldVec3 v) { return vec3(v.x, v.y, v.z); }
|
||||
glm::vec3 from_c(ManifoldVec3 v) { return glm::vec3(v.x, v.y, v.z); }
|
||||
|
||||
ivec3 from_c(ManifoldIVec3 v) { return ivec3(v.x, v.y, v.z); }
|
||||
glm::ivec3 from_c(ManifoldIVec3 v) { return glm::ivec3(v.x, v.y, v.z); }
|
||||
|
||||
vec4 from_c(ManifoldVec4 v) { return vec4(v.x, v.y, v.z, v.w); }
|
||||
glm::vec4 from_c(ManifoldVec4 v) { return glm::vec4(v.x, v.y, v.z, v.w); }
|
||||
|
||||
std::vector<ivec3>* from_c(ManifoldTriangulation* m) {
|
||||
return reinterpret_cast<std::vector<ivec3>*>(m);
|
||||
}
|
||||
|
||||
std::vector<vec3> vector_of_vec_array(ManifoldVec3* vs, size_t length) {
|
||||
auto vec = std::vector<vec3>();
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
std::vector<glm::vec3> vector_of_vec_array(ManifoldVec3 *vs, size_t length) {
|
||||
auto vec = std::vector<glm::vec3>();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
vec.push_back(from_c(vs[i]));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<ivec3> vector_of_vec_array(ManifoldIVec3* vs, size_t length) {
|
||||
auto vec = std::vector<ivec3>();
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
std::vector<glm::ivec3> vector_of_vec_array(ManifoldIVec3 *vs, size_t length) {
|
||||
auto vec = std::vector<glm::ivec3>();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
vec.push_back(from_c(vs[i]));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<vec4> vector_of_vec_array(ManifoldVec4* vs, size_t length) {
|
||||
auto vec = std::vector<vec4>();
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
std::vector<glm::vec4> vector_of_vec_array(ManifoldVec4 *vs, size_t length) {
|
||||
auto vec = std::vector<glm::vec4>();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
vec.push_back(from_c(vs[i]));
|
||||
}
|
||||
return vec;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/manifold.h"
|
||||
#include "manifold/types.h"
|
||||
|
||||
using namespace manifold;
|
||||
using ManifoldVec = std::vector<Manifold>;
|
||||
using CrossSectionVec = std::vector<CrossSection>;
|
||||
|
||||
ManifoldManifold* to_c(manifold::Manifold* m);
|
||||
ManifoldManifoldVec* to_c(ManifoldVec* ms);
|
||||
ManifoldCrossSection* to_c(manifold::CrossSection* cs);
|
||||
ManifoldCrossSectionVec* to_c(CrossSectionVec* csv);
|
||||
ManifoldSimplePolygon* to_c(manifold::SimplePolygon* p);
|
||||
ManifoldPolygons* to_c(manifold::Polygons* ps);
|
||||
ManifoldMeshGL* to_c(manifold::MeshGL* m);
|
||||
ManifoldMeshGL64* to_c(manifold::MeshGL64* m);
|
||||
ManifoldBox* to_c(manifold::Box* m);
|
||||
ManifoldRect* to_c(manifold::Rect* m);
|
||||
ManifoldError to_c(manifold::Manifold::Error error);
|
||||
ManifoldVec2 to_c(vec2 v);
|
||||
ManifoldVec3 to_c(vec3 v);
|
||||
ManifoldIVec3 to_c(ivec3 v);
|
||||
ManifoldTriangulation* to_c(std::vector<ivec3>* m);
|
||||
|
||||
manifold::Manifold* from_c(ManifoldManifold* m);
|
||||
ManifoldVec* from_c(ManifoldManifoldVec* ms);
|
||||
manifold::CrossSection* from_c(ManifoldCrossSection* cs);
|
||||
CrossSectionVec* from_c(ManifoldCrossSectionVec* csv);
|
||||
manifold::SimplePolygon* from_c(ManifoldSimplePolygon* m);
|
||||
manifold::Polygons* from_c(ManifoldPolygons* m);
|
||||
manifold::MeshGL* from_c(ManifoldMeshGL* m);
|
||||
manifold::MeshGL64* from_c(ManifoldMeshGL64* m);
|
||||
OpType from_c(ManifoldOpType op);
|
||||
CrossSection::FillRule from_c(ManifoldFillRule fillrule);
|
||||
CrossSection::JoinType from_c(ManifoldJoinType jt);
|
||||
manifold::Box* from_c(ManifoldBox* m);
|
||||
manifold::Rect* from_c(ManifoldRect* r);
|
||||
vec2 from_c(ManifoldVec2 v);
|
||||
vec3 from_c(ManifoldVec3 v);
|
||||
ivec3 from_c(ManifoldIVec3 v);
|
||||
vec4 from_c(ManifoldVec4 v);
|
||||
std::vector<ivec3>* from_c(ManifoldTriangulation* m);
|
||||
|
||||
std::vector<vec3> vector_of_vec_array(ManifoldVec3* vs, size_t length);
|
||||
std::vector<ivec3> vector_of_vec_array(ManifoldIVec3* vs, size_t length);
|
||||
std::vector<vec4> vector_of_vec_array(ManifoldVec4* vs, size_t length);
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> vector_of_array(T* ts, size_t length) {
|
||||
auto vec = std::vector<T>();
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
vec.push_back(ts[i]);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* copy_data(void* mem, std::vector<T> v) {
|
||||
T* ts = reinterpret_cast<T*>(mem);
|
||||
memcpy(ts, v.data(), sizeof(T) * v.size());
|
||||
return ts;
|
||||
}
|
||||
@@ -12,242 +12,235 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "conv.h"
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/types.h"
|
||||
#include <vector>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
ManifoldCrossSection* manifold_cross_section_empty(void* mem) {
|
||||
#include "conv.h"
|
||||
#include "cross_section.h"
|
||||
#include "manifold.h"
|
||||
#include "public.h"
|
||||
#include "types.h"
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_empty(void *mem) {
|
||||
return to_c(new (mem) CrossSection());
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_copy(void* mem,
|
||||
ManifoldCrossSection* cs) {
|
||||
ManifoldCrossSection *manifold_cross_section_copy(void *mem,
|
||||
ManifoldCrossSection *cs) {
|
||||
auto cross = *from_c(cs);
|
||||
return to_c(new (mem) CrossSection(cross));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_of_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* p, ManifoldFillRule fr) {
|
||||
ManifoldCrossSection *manifold_cross_section_of_simple_polygon(
|
||||
void *mem, ManifoldSimplePolygon *p, ManifoldFillRule fr) {
|
||||
return to_c(new (mem) CrossSection(*from_c(p), from_c(fr)));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_of_polygons(void* mem,
|
||||
ManifoldPolygons* p,
|
||||
ManifoldCrossSection *manifold_cross_section_of_polygons(void *mem,
|
||||
ManifoldPolygons *p,
|
||||
ManifoldFillRule fr) {
|
||||
return to_c(new (mem) CrossSection(*from_c(p), from_c(fr)));
|
||||
}
|
||||
|
||||
ManifoldCrossSectionVec* manifold_cross_section_empty_vec(void* mem) {
|
||||
ManifoldCrossSectionVec *manifold_cross_section_empty_vec(void *mem) {
|
||||
return to_c(new (mem) CrossSectionVec());
|
||||
}
|
||||
|
||||
ManifoldCrossSectionVec* manifold_cross_section_vec(void* mem, size_t sz) {
|
||||
ManifoldCrossSectionVec *manifold_cross_section_vec(void *mem, size_t sz) {
|
||||
return to_c(new (mem) CrossSectionVec(sz));
|
||||
}
|
||||
|
||||
void manifold_cross_section_vec_reserve(ManifoldCrossSectionVec* csv,
|
||||
void manifold_cross_section_vec_reserve(ManifoldCrossSectionVec *csv,
|
||||
size_t sz) {
|
||||
from_c(csv)->reserve(sz);
|
||||
}
|
||||
|
||||
size_t manifold_cross_section_vec_length(ManifoldCrossSectionVec* csv) {
|
||||
size_t manifold_cross_section_vec_length(ManifoldCrossSectionVec *csv) {
|
||||
return from_c(csv)->size();
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_vec_get(
|
||||
void* mem, ManifoldCrossSectionVec* csv, size_t idx) {
|
||||
ManifoldCrossSection *manifold_cross_section_vec_get(
|
||||
void *mem, ManifoldCrossSectionVec *csv, int idx) {
|
||||
auto cs = (*from_c(csv))[idx];
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
void manifold_cross_section_vec_set(ManifoldCrossSectionVec* csv, size_t idx,
|
||||
ManifoldCrossSection* cs) {
|
||||
void manifold_cross_section_vec_set(ManifoldCrossSectionVec *csv, int idx,
|
||||
ManifoldCrossSection *cs) {
|
||||
(*from_c(csv))[idx] = *from_c(cs);
|
||||
}
|
||||
|
||||
void manifold_cross_section_vec_push_back(ManifoldCrossSectionVec* csv,
|
||||
ManifoldCrossSection* cs) {
|
||||
void manifold_cross_section_vec_push_back(ManifoldCrossSectionVec *csv,
|
||||
ManifoldCrossSection *cs) {
|
||||
return from_c(csv)->push_back(*from_c(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_square(void* mem, double x,
|
||||
double y, int center) {
|
||||
auto cs = CrossSection::Square(vec2(x, y), center);
|
||||
ManifoldCrossSection *manifold_cross_section_square(void *mem, float x, float y,
|
||||
int center) {
|
||||
auto cs = CrossSection::Square(glm::vec2(x, y), center);
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_circle(void* mem, double radius,
|
||||
ManifoldCrossSection *manifold_cross_section_circle(void *mem, float radius,
|
||||
int circular_segments) {
|
||||
auto cs = CrossSection::Circle(radius, circular_segments);
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_boolean(void* mem,
|
||||
ManifoldCrossSection* a,
|
||||
ManifoldCrossSection* b,
|
||||
ManifoldCrossSection *manifold_cross_section_boolean(void *mem,
|
||||
ManifoldCrossSection *a,
|
||||
ManifoldCrossSection *b,
|
||||
ManifoldOpType op) {
|
||||
auto cs = from_c(a)->Boolean(*from_c(b), from_c(op));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_batch_boolean(
|
||||
void* mem, ManifoldCrossSectionVec* csv, ManifoldOpType op) {
|
||||
ManifoldCrossSection *manifold_cross_section_batch_boolean(
|
||||
void *mem, ManifoldCrossSectionVec *csv, ManifoldOpType op) {
|
||||
auto cs = CrossSection::BatchBoolean(*from_c(csv), from_c(op));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_union(void* mem,
|
||||
ManifoldCrossSection* a,
|
||||
ManifoldCrossSection* b) {
|
||||
ManifoldCrossSection *manifold_cross_section_union(void *mem,
|
||||
ManifoldCrossSection *a,
|
||||
ManifoldCrossSection *b) {
|
||||
auto cs = (*from_c(a)) + (*from_c(b));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_difference(
|
||||
void* mem, ManifoldCrossSection* a, ManifoldCrossSection* b) {
|
||||
ManifoldCrossSection *manifold_cross_section_difference(
|
||||
void *mem, ManifoldCrossSection *a, ManifoldCrossSection *b) {
|
||||
auto cs = (*from_c(a)) - (*from_c(b));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_intersection(
|
||||
void* mem, ManifoldCrossSection* a, ManifoldCrossSection* b) {
|
||||
ManifoldCrossSection *manifold_cross_section_intersection(
|
||||
void *mem, ManifoldCrossSection *a, ManifoldCrossSection *b) {
|
||||
auto cs = (*from_c(a)) ^ (*from_c(b));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_hull(void* mem,
|
||||
ManifoldCrossSection* cs) {
|
||||
ManifoldCrossSection *manifold_cross_section_hull(void *mem,
|
||||
ManifoldCrossSection *cs) {
|
||||
auto hulled = from_c(cs)->Hull();
|
||||
return to_c(new (mem) CrossSection(hulled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_batch_hull(
|
||||
void* mem, ManifoldCrossSectionVec* css) {
|
||||
ManifoldCrossSection *manifold_cross_section_batch_hull(
|
||||
void *mem, ManifoldCrossSectionVec *css) {
|
||||
auto hulled = CrossSection::Hull(*from_c(css));
|
||||
return to_c(new (mem) CrossSection(hulled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_hull_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* ps) {
|
||||
ManifoldCrossSection *manifold_cross_section_hull_simple_polygon(
|
||||
void *mem, ManifoldSimplePolygon *ps) {
|
||||
auto hulled = CrossSection::Hull(*from_c(ps));
|
||||
return to_c(new (mem) CrossSection(hulled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_hull_polygons(
|
||||
void* mem, ManifoldPolygons* ps) {
|
||||
ManifoldCrossSection *manifold_cross_section_hull_polygons(
|
||||
void *mem, ManifoldPolygons *ps) {
|
||||
auto hulled = CrossSection::Hull(*from_c(ps));
|
||||
return to_c(new (mem) CrossSection(hulled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_translate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y) {
|
||||
auto translated = from_c(cs)->Translate(vec2(x, y));
|
||||
ManifoldCrossSection *manifold_cross_section_translate(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x, float y) {
|
||||
auto translated = from_c(cs)->Translate(glm::vec2(x, y));
|
||||
return to_c(new (mem) CrossSection(translated));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_rotate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double deg) {
|
||||
ManifoldCrossSection *manifold_cross_section_rotate(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float deg) {
|
||||
auto rotated = from_c(cs)->Rotate(deg);
|
||||
return to_c(new (mem) CrossSection(rotated));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_scale(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y) {
|
||||
auto scaled = from_c(cs)->Scale(vec2(x, y));
|
||||
ManifoldCrossSection *manifold_cross_section_scale(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x, float y) {
|
||||
auto scaled = from_c(cs)->Scale(glm::vec2(x, y));
|
||||
return to_c(new (mem) CrossSection(scaled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_mirror(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double ax_x, double ax_y) {
|
||||
auto mirrored = from_c(cs)->Mirror(vec2(ax_x, ax_y));
|
||||
ManifoldCrossSection *manifold_cross_section_mirror(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float ax_x, float ax_y) {
|
||||
auto mirrored = from_c(cs)->Mirror(glm::vec2(ax_x, ax_y));
|
||||
return to_c(new (mem) CrossSection(mirrored));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_transform(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x1, double y1,
|
||||
double x2, double y2,
|
||||
double x3, double y3) {
|
||||
auto mat = mat2x3({x1, y1}, {x2, y2}, {x3, y3});
|
||||
ManifoldCrossSection *manifold_cross_section_transform(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3) {
|
||||
auto mat = glm::mat3x2(x1, y1, x2, y2, x3, y3);
|
||||
auto transformed = from_c(cs)->Transform(mat);
|
||||
return to_c(new (mem) CrossSection(transformed));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_warp_context(
|
||||
void* mem, ManifoldCrossSection* cs,
|
||||
ManifoldVec2 (*fun)(double, double, void*), void* ctx) {
|
||||
// Bind function with context argument to one without
|
||||
using namespace std::placeholders;
|
||||
std::function<ManifoldVec2(double, double)> f2 = std::bind(fun, _1, _2, ctx);
|
||||
std::function<void(vec2 & v)> warp = [f2](vec2& v) {
|
||||
v = from_c(f2(v.x, v.y));
|
||||
ManifoldCrossSection *manifold_cross_section_warp(
|
||||
void *mem, ManifoldCrossSection *cs, ManifoldVec2 (*fun)(float, float)) {
|
||||
std::function<void(glm::vec2 & v)> warp = [fun](glm::vec2 &v) {
|
||||
v = from_c(fun(v.x, v.y));
|
||||
};
|
||||
auto warped = from_c(cs)->Warp(warp);
|
||||
return to_c(new (mem) CrossSection(warped));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_simplify(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
ManifoldCrossSection *manifold_cross_section_simplify(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
double epsilon) {
|
||||
auto simplified = from_c(cs)->Simplify(epsilon);
|
||||
return to_c(new (mem) CrossSection(simplified));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_offset(
|
||||
void* mem, ManifoldCrossSection* cs, double delta, ManifoldJoinType jt,
|
||||
ManifoldCrossSection *manifold_cross_section_offset(
|
||||
void *mem, ManifoldCrossSection *cs, double delta, ManifoldJoinType jt,
|
||||
double miter_limit, int circular_segments) {
|
||||
auto offset =
|
||||
from_c(cs)->Offset(delta, from_c(jt), miter_limit, circular_segments);
|
||||
return to_c(new (mem) CrossSection(offset));
|
||||
}
|
||||
|
||||
double manifold_cross_section_area(ManifoldCrossSection* cs) {
|
||||
double manifold_cross_section_area(ManifoldCrossSection *cs) {
|
||||
return from_c(cs)->Area();
|
||||
}
|
||||
|
||||
size_t manifold_cross_section_num_vert(ManifoldCrossSection* cs) {
|
||||
int manifold_cross_section_num_vert(ManifoldCrossSection *cs) {
|
||||
return from_c(cs)->NumVert();
|
||||
}
|
||||
|
||||
size_t manifold_cross_section_num_contour(ManifoldCrossSection* cs) {
|
||||
int manifold_cross_section_num_contour(ManifoldCrossSection *cs) {
|
||||
return from_c(cs)->NumContour();
|
||||
}
|
||||
|
||||
int manifold_cross_section_is_empty(ManifoldCrossSection* cs) {
|
||||
int manifold_cross_section_is_empty(ManifoldCrossSection *cs) {
|
||||
return from_c(cs)->IsEmpty();
|
||||
}
|
||||
|
||||
ManifoldRect* manifold_cross_section_bounds(void* mem,
|
||||
ManifoldCrossSection* cs) {
|
||||
ManifoldRect *manifold_cross_section_bounds(void *mem,
|
||||
ManifoldCrossSection *cs) {
|
||||
auto rect = from_c(cs)->Bounds();
|
||||
return to_c(new (mem) Rect(rect));
|
||||
}
|
||||
|
||||
ManifoldPolygons* manifold_cross_section_to_polygons(void* mem,
|
||||
ManifoldCrossSection* cs) {
|
||||
ManifoldPolygons *manifold_cross_section_to_polygons(void *mem,
|
||||
ManifoldCrossSection *cs) {
|
||||
auto ps = from_c(cs)->ToPolygons();
|
||||
return to_c(new (mem) Polygons(ps));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_compose(
|
||||
void* mem, ManifoldCrossSectionVec* csv) {
|
||||
ManifoldCrossSection *manifold_cross_section_compose(
|
||||
void *mem, ManifoldCrossSectionVec *csv) {
|
||||
auto cs = CrossSection::Compose(*from_c(csv));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
}
|
||||
|
||||
ManifoldCrossSectionVec* manifold_cross_section_decompose(
|
||||
void* mem, ManifoldCrossSection* cs) {
|
||||
ManifoldCrossSectionVec *manifold_cross_section_decompose(
|
||||
void *mem, ManifoldCrossSection *cs) {
|
||||
auto comps = from_c(cs)->Decompose();
|
||||
return to_c(new (mem) CrossSectionVec(comps));
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "cross_section.h"
|
||||
#include "manifold.h"
|
||||
#include "public.h"
|
||||
#include "sdf.h"
|
||||
#include "types.h"
|
||||
|
||||
using namespace manifold;
|
||||
using ManifoldVec = std::vector<Manifold>;
|
||||
using CrossSectionVec = std::vector<CrossSection>;
|
||||
|
||||
ManifoldManifold *to_c(manifold::Manifold *m);
|
||||
ManifoldManifoldVec *to_c(ManifoldVec *ms);
|
||||
ManifoldCrossSection *to_c(manifold::CrossSection *cs);
|
||||
ManifoldCrossSectionVec *to_c(CrossSectionVec *csv);
|
||||
ManifoldSimplePolygon *to_c(manifold::SimplePolygon *p);
|
||||
ManifoldPolygons *to_c(manifold::Polygons *ps);
|
||||
ManifoldMesh *to_c(manifold::Mesh *m);
|
||||
ManifoldMeshGL *to_c(manifold::MeshGL *m);
|
||||
ManifoldBox *to_c(manifold::Box *m);
|
||||
ManifoldRect *to_c(manifold::Rect *m);
|
||||
ManifoldError to_c(manifold::Manifold::Error error);
|
||||
ManifoldVec2 to_c(glm::vec2 v);
|
||||
ManifoldVec3 to_c(glm::vec3 v);
|
||||
ManifoldIVec3 to_c(glm::ivec3 v);
|
||||
ManifoldProperties to_c(manifold::Properties p);
|
||||
|
||||
const manifold::Manifold *from_c(ManifoldManifold *m);
|
||||
ManifoldVec *from_c(ManifoldManifoldVec *ms);
|
||||
const manifold::CrossSection *from_c(ManifoldCrossSection *cs);
|
||||
CrossSectionVec *from_c(ManifoldCrossSectionVec *csv);
|
||||
const manifold::SimplePolygon *from_c(ManifoldSimplePolygon *m);
|
||||
const manifold::Polygons *from_c(ManifoldPolygons *m);
|
||||
const manifold::Mesh *from_c(ManifoldMesh *m);
|
||||
const manifold::MeshGL *from_c(ManifoldMeshGL *m);
|
||||
OpType from_c(ManifoldOpType op);
|
||||
CrossSection::FillRule from_c(ManifoldFillRule fillrule);
|
||||
CrossSection::JoinType from_c(ManifoldJoinType jt);
|
||||
const manifold::Box *from_c(ManifoldBox *m);
|
||||
const manifold::Rect *from_c(ManifoldRect *r);
|
||||
glm::vec2 from_c(ManifoldVec2 v);
|
||||
glm::vec3 from_c(ManifoldVec3 v);
|
||||
glm::ivec3 from_c(ManifoldIVec3 v);
|
||||
glm::vec4 from_c(ManifoldVec4 v);
|
||||
|
||||
std::vector<glm::vec3> vector_of_vec_array(ManifoldVec3 *vs, size_t length);
|
||||
std::vector<glm::ivec3> vector_of_vec_array(ManifoldIVec3 *vs, size_t length);
|
||||
std::vector<glm::vec4> vector_of_vec_array(ManifoldVec4 *vs, size_t length);
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> vector_of_array(T *ts, size_t length) {
|
||||
auto vec = std::vector<T>();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
vec.push_back(ts[i]);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *copy_data(void *mem, std::vector<T> v) {
|
||||
T *ts = reinterpret_cast<T *>(mem);
|
||||
memcpy(ts, v.data(), sizeof(T) * v.size());
|
||||
return ts;
|
||||
}
|
||||
@@ -1,530 +0,0 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "manifold/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Polygons
|
||||
|
||||
ManifoldSimplePolygon* manifold_simple_polygon(void* mem, ManifoldVec2* ps,
|
||||
size_t length);
|
||||
ManifoldPolygons* manifold_polygons(void* mem, ManifoldSimplePolygon** ps,
|
||||
size_t length);
|
||||
size_t manifold_simple_polygon_length(ManifoldSimplePolygon* p);
|
||||
size_t manifold_polygons_length(ManifoldPolygons* ps);
|
||||
size_t manifold_polygons_simple_length(ManifoldPolygons* ps, size_t idx);
|
||||
ManifoldVec2 manifold_simple_polygon_get_point(ManifoldSimplePolygon* p,
|
||||
size_t idx);
|
||||
ManifoldSimplePolygon* manifold_polygons_get_simple(void* mem,
|
||||
ManifoldPolygons* ps,
|
||||
size_t idx);
|
||||
ManifoldVec2 manifold_polygons_get_point(ManifoldPolygons* ps,
|
||||
size_t simple_idx, size_t pt_idx);
|
||||
|
||||
// Mesh Construction
|
||||
|
||||
ManifoldMeshGL* manifold_meshgl(void* mem, float* vert_props, size_t n_verts,
|
||||
size_t n_props, uint32_t* tri_verts,
|
||||
size_t n_tris);
|
||||
|
||||
ManifoldMeshGL* manifold_meshgl_w_tangents(void* mem, float* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint32_t* tri_verts, size_t n_tris,
|
||||
float* halfedge_tangent);
|
||||
ManifoldMeshGL* manifold_meshgl_w_options(void* mem, float* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint32_t* tri_verts, size_t n_tris,
|
||||
ManifoldMeshGLOptions* options);
|
||||
ManifoldMeshGL* manifold_get_meshgl(void* mem, ManifoldManifold* m);
|
||||
ManifoldMeshGL* manifold_get_meshgl_w_normals(void* mem, ManifoldManifold* m,
|
||||
int32_t normalIdx);
|
||||
ManifoldMeshGL* manifold_meshgl_copy(void* mem, ManifoldMeshGL* m);
|
||||
ManifoldMeshGL* manifold_meshgl_merge(void* mem, ManifoldMeshGL* m);
|
||||
|
||||
ManifoldMeshGL64* manifold_meshgl64(void* mem, double* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint64_t* tri_verts, size_t n_tris);
|
||||
|
||||
ManifoldMeshGL64* manifold_meshgl64_w_tangents(void* mem, double* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint64_t* tri_verts,
|
||||
size_t n_tris,
|
||||
double* halfedge_tangent);
|
||||
ManifoldMeshGL64* manifold_meshgl64_w_options(void* mem, double* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint64_t* tri_verts,
|
||||
size_t n_tris,
|
||||
ManifoldMeshGL64Options* options);
|
||||
ManifoldMeshGL64* manifold_get_meshgl64(void* mem, ManifoldManifold* m);
|
||||
ManifoldMeshGL64* manifold_get_meshgl64_w_normals(void* mem,
|
||||
ManifoldManifold* m,
|
||||
int32_t normalIdx);
|
||||
ManifoldMeshGL64* manifold_meshgl64_copy(void* mem, ManifoldMeshGL64* m);
|
||||
ManifoldMeshGL64* manifold_meshgl64_merge(void* mem, ManifoldMeshGL64* m);
|
||||
|
||||
// SDF
|
||||
// By default, the execution policy (sequential or parallel) of
|
||||
// manifold_level_set will be chosen automatically depending on the size of the
|
||||
// job and whether Manifold has been compiled with a PAR backend. If you are
|
||||
// using these bindings from a language that has a runtime lock preventing the
|
||||
// parallel execution of closures, then you should use manifold_level_set_seq to
|
||||
// force sequential execution.
|
||||
ManifoldManifold* manifold_level_set(void* mem, ManifoldSdf sdf,
|
||||
ManifoldBox* bounds, double edge_length,
|
||||
double level, double tolerance, void* ctx);
|
||||
ManifoldManifold* manifold_level_set_seq(void* mem, ManifoldSdf sdf,
|
||||
ManifoldBox* bounds,
|
||||
double edge_length, double level,
|
||||
double tolerance, void* ctx);
|
||||
|
||||
// Manifold Vectors
|
||||
|
||||
ManifoldManifoldVec* manifold_manifold_empty_vec(void* mem);
|
||||
ManifoldManifoldVec* manifold_manifold_vec(void* mem, size_t sz);
|
||||
void manifold_manifold_vec_reserve(ManifoldManifoldVec* ms, size_t sz);
|
||||
size_t manifold_manifold_vec_length(ManifoldManifoldVec* ms);
|
||||
ManifoldManifold* manifold_manifold_vec_get(void* mem, ManifoldManifoldVec* ms,
|
||||
size_t idx);
|
||||
void manifold_manifold_vec_set(ManifoldManifoldVec* ms, size_t idx,
|
||||
ManifoldManifold* m);
|
||||
void manifold_manifold_vec_push_back(ManifoldManifoldVec* ms,
|
||||
ManifoldManifold* m);
|
||||
|
||||
// Manifold Booleans
|
||||
|
||||
ManifoldManifold* manifold_boolean(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b, ManifoldOpType op);
|
||||
ManifoldManifold* manifold_batch_boolean(void* mem, ManifoldManifoldVec* ms,
|
||||
ManifoldOpType op);
|
||||
ManifoldManifold* manifold_union(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifold* manifold_difference(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifold* manifold_intersection(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifoldPair manifold_split(void* mem_first, void* mem_second,
|
||||
ManifoldManifold* a, ManifoldManifold* b);
|
||||
ManifoldManifoldPair manifold_split_by_plane(void* mem_first, void* mem_second,
|
||||
ManifoldManifold* m,
|
||||
double normal_x, double normal_y,
|
||||
double normal_z, double offset);
|
||||
ManifoldManifold* manifold_trim_by_plane(void* mem, ManifoldManifold* m,
|
||||
double normal_x, double normal_y,
|
||||
double normal_z, double offset);
|
||||
ManifoldManifold* manifold_minkowski_sum(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifold* manifold_minkowski_difference(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
|
||||
// 3D to 2D
|
||||
|
||||
ManifoldPolygons* manifold_slice(void* mem, ManifoldManifold* m, double height);
|
||||
ManifoldPolygons* manifold_project(void* mem, ManifoldManifold* m);
|
||||
|
||||
// Convex Hulls
|
||||
|
||||
ManifoldManifold* manifold_hull(void* mem, ManifoldManifold* m);
|
||||
ManifoldManifold* manifold_batch_hull(void* mem, ManifoldManifoldVec* ms);
|
||||
ManifoldManifold* manifold_hull_pts(void* mem, ManifoldVec3* ps, size_t length);
|
||||
|
||||
// Manifold Transformations
|
||||
|
||||
ManifoldManifold* manifold_translate(void* mem, ManifoldManifold* m, double x,
|
||||
double y, double z);
|
||||
ManifoldManifold* manifold_rotate(void* mem, ManifoldManifold* m, double x,
|
||||
double y, double z);
|
||||
ManifoldManifold* manifold_scale(void* mem, ManifoldManifold* m, double x,
|
||||
double y, double z);
|
||||
ManifoldManifold* manifold_transform(void* mem, ManifoldManifold* m, double x1,
|
||||
double y1, double z1, double x2, double y2,
|
||||
double z2, double x3, double y3, double z3,
|
||||
double x4, double y4, double z4);
|
||||
ManifoldManifold* manifold_mirror(void* mem, ManifoldManifold* m, double nx,
|
||||
double ny, double nz);
|
||||
ManifoldManifold* manifold_warp(void* mem, ManifoldManifold* m,
|
||||
ManifoldVec3 (*fun)(double, double, double,
|
||||
void*),
|
||||
void* ctx);
|
||||
ManifoldManifold* manifold_smooth_by_normals(void* mem, ManifoldManifold* m,
|
||||
int normalIdx);
|
||||
ManifoldManifold* manifold_smooth_out(void* mem, ManifoldManifold* m,
|
||||
double minSharpAngle,
|
||||
double minSmoothness);
|
||||
ManifoldManifold* manifold_refine(void* mem, ManifoldManifold* m, int refine);
|
||||
ManifoldManifold* manifold_refine_to_length(void* mem, ManifoldManifold* m,
|
||||
double length);
|
||||
ManifoldManifold* manifold_refine_to_tolerance(void* mem, ManifoldManifold* m,
|
||||
double tolerance);
|
||||
|
||||
// Manifold Shapes / Constructors
|
||||
|
||||
ManifoldManifold* manifold_empty(void* mem);
|
||||
ManifoldManifold* manifold_copy(void* mem, ManifoldManifold* m);
|
||||
ManifoldManifold* manifold_tetrahedron(void* mem);
|
||||
ManifoldManifold* manifold_cube(void* mem, double x, double y, double z,
|
||||
int center);
|
||||
ManifoldManifold* manifold_cylinder(void* mem, double height, double radius_low,
|
||||
double radius_high, int circular_segments,
|
||||
int center);
|
||||
ManifoldManifold* manifold_sphere(void* mem, double radius,
|
||||
int circular_segments);
|
||||
ManifoldManifold* manifold_of_meshgl(void* mem, ManifoldMeshGL* mesh);
|
||||
ManifoldManifold* manifold_of_meshgl64(void* mem, ManifoldMeshGL64* mesh);
|
||||
ManifoldManifold* manifold_smooth(void* mem, ManifoldMeshGL* mesh,
|
||||
size_t* half_edges, double* smoothness,
|
||||
size_t n_idxs);
|
||||
ManifoldManifold* manifold_smooth64(void* mem, ManifoldMeshGL64* mesh,
|
||||
size_t* half_edges, double* smoothness,
|
||||
size_t n_idxs);
|
||||
ManifoldManifold* manifold_extrude(void* mem, ManifoldPolygons* cs,
|
||||
double height, int slices,
|
||||
double twist_degrees, double scale_x,
|
||||
double scale_y);
|
||||
ManifoldManifold* manifold_revolve(void* mem, ManifoldPolygons* cs,
|
||||
int circular_segments,
|
||||
double revolve_degrees);
|
||||
ManifoldManifold* manifold_compose(void* mem, ManifoldManifoldVec* ms);
|
||||
ManifoldManifoldVec* manifold_decompose(void* mem, ManifoldManifold* m);
|
||||
|
||||
ManifoldManifold* manifold_as_original(void* mem, ManifoldManifold* m);
|
||||
|
||||
// Manifold Info
|
||||
|
||||
int manifold_is_empty(ManifoldManifold* m);
|
||||
ManifoldError manifold_status(ManifoldManifold* m);
|
||||
size_t manifold_num_vert(ManifoldManifold* m);
|
||||
size_t manifold_num_edge(ManifoldManifold* m);
|
||||
size_t manifold_num_tri(ManifoldManifold* m);
|
||||
size_t manifold_num_prop(ManifoldManifold* m);
|
||||
ManifoldBox* manifold_bounding_box(void* mem, ManifoldManifold* m);
|
||||
double manifold_epsilon(ManifoldManifold* m);
|
||||
int manifold_genus(ManifoldManifold* m);
|
||||
double manifold_surface_area(ManifoldManifold* m);
|
||||
double manifold_volume(ManifoldManifold* m);
|
||||
int manifold_get_circular_segments(double radius);
|
||||
int manifold_original_id(ManifoldManifold* m);
|
||||
uint32_t manifold_reserve_ids(uint32_t n);
|
||||
ManifoldManifold* manifold_set_properties(
|
||||
void* mem, ManifoldManifold* m, int num_prop,
|
||||
void (*fun)(double* new_prop, ManifoldVec3 position, const double* old_prop,
|
||||
void* ctx),
|
||||
void* ctx);
|
||||
ManifoldManifold* manifold_calculate_curvature(void* mem, ManifoldManifold* m,
|
||||
int gaussian_idx, int mean_idx);
|
||||
double manifold_min_gap(ManifoldManifold* m, ManifoldManifold* other,
|
||||
double searchLength);
|
||||
ManifoldManifold* manifold_calculate_normals(void* mem, ManifoldManifold* m,
|
||||
int normal_idx,
|
||||
double min_sharp_angle);
|
||||
|
||||
// CrossSection Shapes/Constructors
|
||||
ManifoldCrossSection* manifold_cross_section_empty(void* mem);
|
||||
ManifoldCrossSection* manifold_cross_section_copy(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
ManifoldCrossSection* manifold_cross_section_of_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* p, ManifoldFillRule fr);
|
||||
ManifoldCrossSection* manifold_cross_section_of_polygons(void* mem,
|
||||
ManifoldPolygons* p,
|
||||
ManifoldFillRule fr);
|
||||
ManifoldCrossSection* manifold_cross_section_square(void* mem, double x,
|
||||
double y, int center);
|
||||
ManifoldCrossSection* manifold_cross_section_circle(void* mem, double radius,
|
||||
int circular_segments);
|
||||
ManifoldCrossSection* manifold_cross_section_compose(
|
||||
void* mem, ManifoldCrossSectionVec* csv);
|
||||
ManifoldCrossSectionVec* manifold_cross_section_decompose(
|
||||
void* mem, ManifoldCrossSection* cs);
|
||||
|
||||
// CrossSection Vectors
|
||||
|
||||
ManifoldCrossSectionVec* manifold_cross_section_empty_vec(void* mem);
|
||||
ManifoldCrossSectionVec* manifold_cross_section_vec(void* mem, size_t sz);
|
||||
void manifold_cross_section_vec_reserve(ManifoldCrossSectionVec* csv,
|
||||
size_t sz);
|
||||
size_t manifold_cross_section_vec_length(ManifoldCrossSectionVec* csv);
|
||||
ManifoldCrossSection* manifold_cross_section_vec_get(
|
||||
void* mem, ManifoldCrossSectionVec* csv, size_t idx);
|
||||
void manifold_cross_section_vec_set(ManifoldCrossSectionVec* csv, size_t idx,
|
||||
ManifoldCrossSection* cs);
|
||||
void manifold_cross_section_vec_push_back(ManifoldCrossSectionVec* csv,
|
||||
ManifoldCrossSection* cs);
|
||||
|
||||
// CrossSection Booleans
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_boolean(void* mem,
|
||||
ManifoldCrossSection* a,
|
||||
ManifoldCrossSection* b,
|
||||
ManifoldOpType op);
|
||||
ManifoldCrossSection* manifold_cross_section_batch_boolean(
|
||||
void* mem, ManifoldCrossSectionVec* csv, ManifoldOpType op);
|
||||
ManifoldCrossSection* manifold_cross_section_union(void* mem,
|
||||
ManifoldCrossSection* a,
|
||||
ManifoldCrossSection* b);
|
||||
ManifoldCrossSection* manifold_cross_section_difference(
|
||||
void* mem, ManifoldCrossSection* a, ManifoldCrossSection* b);
|
||||
ManifoldCrossSection* manifold_cross_section_intersection(
|
||||
void* mem, ManifoldCrossSection* a, ManifoldCrossSection* b);
|
||||
|
||||
// CrossSection Convex Hulls
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_hull(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
ManifoldCrossSection* manifold_cross_section_batch_hull(
|
||||
void* mem, ManifoldCrossSectionVec* css);
|
||||
ManifoldCrossSection* manifold_cross_section_hull_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* ps);
|
||||
ManifoldCrossSection* manifold_cross_section_hull_polygons(
|
||||
void* mem, ManifoldPolygons* ps);
|
||||
|
||||
// CrossSection Transformation
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_translate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y);
|
||||
ManifoldCrossSection* manifold_cross_section_rotate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double deg);
|
||||
ManifoldCrossSection* manifold_cross_section_scale(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y);
|
||||
ManifoldCrossSection* manifold_cross_section_mirror(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double ax_x, double ax_y);
|
||||
ManifoldCrossSection* manifold_cross_section_transform(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x1, double y1,
|
||||
double x2, double y2,
|
||||
double x3, double y3);
|
||||
ManifoldCrossSection* manifold_cross_section_warp_context(
|
||||
void* mem, ManifoldCrossSection* cs,
|
||||
ManifoldVec2 (*fun)(double, double, void*), void* ctx);
|
||||
ManifoldCrossSection* manifold_cross_section_simplify(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double epsilon);
|
||||
ManifoldCrossSection* manifold_cross_section_offset(
|
||||
void* mem, ManifoldCrossSection* cs, double delta, ManifoldJoinType jt,
|
||||
double miter_limit, int circular_segments);
|
||||
|
||||
// CrossSection Info
|
||||
|
||||
double manifold_cross_section_area(ManifoldCrossSection* cs);
|
||||
size_t manifold_cross_section_num_vert(ManifoldCrossSection* cs);
|
||||
size_t manifold_cross_section_num_contour(ManifoldCrossSection* cs);
|
||||
int manifold_cross_section_is_empty(ManifoldCrossSection* cs);
|
||||
ManifoldRect* manifold_cross_section_bounds(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
ManifoldPolygons* manifold_cross_section_to_polygons(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
|
||||
// Rectangle
|
||||
|
||||
ManifoldRect* manifold_rect(void* mem, double x1, double y1, double x2,
|
||||
double y2);
|
||||
ManifoldVec2 manifold_rect_min(ManifoldRect* r);
|
||||
ManifoldVec2 manifold_rect_max(ManifoldRect* r);
|
||||
ManifoldVec2 manifold_rect_dimensions(ManifoldRect* r);
|
||||
ManifoldVec2 manifold_rect_center(ManifoldRect* r);
|
||||
double manifold_rect_scale(ManifoldRect* r);
|
||||
int manifold_rect_contains_pt(ManifoldRect* r, double x, double y);
|
||||
int manifold_rect_contains_rect(ManifoldRect* a, ManifoldRect* b);
|
||||
void manifold_rect_include_pt(ManifoldRect* r, double x, double y);
|
||||
ManifoldRect* manifold_rect_union(void* mem, ManifoldRect* a, ManifoldRect* b);
|
||||
ManifoldRect* manifold_rect_transform(void* mem, ManifoldRect* r, double x1,
|
||||
double y1, double x2, double y2,
|
||||
double x3, double y3);
|
||||
ManifoldRect* manifold_rect_translate(void* mem, ManifoldRect* r, double x,
|
||||
double y);
|
||||
ManifoldRect* manifold_rect_mul(void* mem, ManifoldRect* r, double x, double y);
|
||||
int manifold_rect_does_overlap_rect(ManifoldRect* a, ManifoldRect* r);
|
||||
int manifold_rect_is_empty(ManifoldRect* r);
|
||||
int manifold_rect_is_finite(ManifoldRect* r);
|
||||
|
||||
// Bounding Box
|
||||
|
||||
ManifoldBox* manifold_box(void* mem, double x1, double y1, double z1, double x2,
|
||||
double y2, double z2);
|
||||
ManifoldVec3 manifold_box_min(ManifoldBox* b);
|
||||
ManifoldVec3 manifold_box_max(ManifoldBox* b);
|
||||
ManifoldVec3 manifold_box_dimensions(ManifoldBox* b);
|
||||
ManifoldVec3 manifold_box_center(ManifoldBox* b);
|
||||
double manifold_box_scale(ManifoldBox* b);
|
||||
int manifold_box_contains_pt(ManifoldBox* b, double x, double y, double z);
|
||||
int manifold_box_contains_box(ManifoldBox* a, ManifoldBox* b);
|
||||
void manifold_box_include_pt(ManifoldBox* b, double x, double y, double z);
|
||||
ManifoldBox* manifold_box_union(void* mem, ManifoldBox* a, ManifoldBox* b);
|
||||
ManifoldBox* manifold_box_transform(void* mem, ManifoldBox* b, double x1,
|
||||
double y1, double z1, double x2, double y2,
|
||||
double z2, double x3, double y3, double z3,
|
||||
double x4, double y4, double z4);
|
||||
ManifoldBox* manifold_box_translate(void* mem, ManifoldBox* b, double x,
|
||||
double y, double z);
|
||||
ManifoldBox* manifold_box_mul(void* mem, ManifoldBox* b, double x, double y,
|
||||
double z);
|
||||
int manifold_box_does_overlap_pt(ManifoldBox* b, double x, double y, double z);
|
||||
int manifold_box_does_overlap_box(ManifoldBox* a, ManifoldBox* b);
|
||||
int manifold_box_is_finite(ManifoldBox* b);
|
||||
|
||||
// Static Quality Globals
|
||||
|
||||
void manifold_set_min_circular_angle(double degrees);
|
||||
void manifold_set_min_circular_edge_length(double length);
|
||||
void manifold_set_circular_segments(int number);
|
||||
void manifold_reset_to_circular_defaults();
|
||||
|
||||
// Manifold Mesh Extraction
|
||||
|
||||
size_t manifold_meshgl_num_prop(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_num_vert(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_num_tri(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_vert_properties_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_tri_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_merge_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_index_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_original_id_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_transform_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_face_id_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_tangent_length(ManifoldMeshGL* m);
|
||||
float* manifold_meshgl_vert_properties(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_tri_verts(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_merge_from_vert(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_merge_to_vert(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_run_index(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_run_original_id(void* mem, ManifoldMeshGL* m);
|
||||
float* manifold_meshgl_run_transform(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_face_id(void* mem, ManifoldMeshGL* m);
|
||||
float* manifold_meshgl_halfedge_tangent(void* mem, ManifoldMeshGL* m);
|
||||
|
||||
size_t manifold_meshgl64_num_prop(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_num_vert(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_num_tri(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_vert_properties_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_tri_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_merge_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_run_index_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_run_original_id_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_run_transform_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_face_id_length(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_tangent_length(ManifoldMeshGL64* m);
|
||||
double* manifold_meshgl64_vert_properties(void* mem, ManifoldMeshGL64* m);
|
||||
uint64_t* manifold_meshgl64_tri_verts(void* mem, ManifoldMeshGL64* m);
|
||||
uint64_t* manifold_meshgl64_merge_from_vert(void* mem, ManifoldMeshGL64* m);
|
||||
uint64_t* manifold_meshgl64_merge_to_vert(void* mem, ManifoldMeshGL64* m);
|
||||
uint64_t* manifold_meshgl64_run_index(void* mem, ManifoldMeshGL64* m);
|
||||
uint32_t* manifold_meshgl64_run_original_id(void* mem, ManifoldMeshGL64* m);
|
||||
double* manifold_meshgl64_run_transform(void* mem, ManifoldMeshGL64* m);
|
||||
uint64_t* manifold_meshgl64_face_id(void* mem, ManifoldMeshGL64* m);
|
||||
double* manifold_meshgl64_halfedge_tangent(void* mem, ManifoldMeshGL64* m);
|
||||
|
||||
// Triangulation
|
||||
|
||||
ManifoldTriangulation* manifold_triangulate(void* mem, ManifoldPolygons* ps,
|
||||
double epsilon);
|
||||
size_t manifold_triangulation_num_tri(ManifoldTriangulation* m);
|
||||
int* manifold_triangulation_tri_verts(void* mem, ManifoldTriangulation* m);
|
||||
|
||||
// memory size
|
||||
|
||||
size_t manifold_manifold_size();
|
||||
size_t manifold_manifold_vec_size();
|
||||
size_t manifold_cross_section_size();
|
||||
size_t manifold_cross_section_vec_size();
|
||||
size_t manifold_simple_polygon_size();
|
||||
size_t manifold_polygons_size();
|
||||
size_t manifold_manifold_pair_size();
|
||||
size_t manifold_meshgl_size();
|
||||
size_t manifold_meshgl64_size();
|
||||
size_t manifold_box_size();
|
||||
size_t manifold_rect_size();
|
||||
size_t manifold_triangulation_size();
|
||||
|
||||
// allocation
|
||||
|
||||
ManifoldManifold* manifold_alloc_manifold();
|
||||
ManifoldManifoldVec* manifold_alloc_manifold_vec();
|
||||
ManifoldCrossSection* manifold_alloc_cross_section();
|
||||
ManifoldCrossSectionVec* manifold_alloc_cross_section_vec();
|
||||
ManifoldSimplePolygon* manifold_alloc_simple_polygon();
|
||||
ManifoldPolygons* manifold_alloc_polygons();
|
||||
ManifoldMeshGL* manifold_alloc_meshgl();
|
||||
ManifoldMeshGL64* manifold_alloc_meshgl64();
|
||||
ManifoldBox* manifold_alloc_box();
|
||||
ManifoldRect* manifold_alloc_rect();
|
||||
ManifoldTriangulation* manifold_alloc_triangulation();
|
||||
|
||||
// destruction
|
||||
|
||||
void manifold_destruct_manifold(ManifoldManifold* m);
|
||||
void manifold_destruct_manifold_vec(ManifoldManifoldVec* ms);
|
||||
void manifold_destruct_cross_section(ManifoldCrossSection* m);
|
||||
void manifold_destruct_cross_section_vec(ManifoldCrossSectionVec* csv);
|
||||
void manifold_destruct_simple_polygon(ManifoldSimplePolygon* p);
|
||||
void manifold_destruct_polygons(ManifoldPolygons* p);
|
||||
void manifold_destruct_meshgl(ManifoldMeshGL* m);
|
||||
void manifold_destruct_meshgl64(ManifoldMeshGL64* m);
|
||||
void manifold_destruct_box(ManifoldBox* b);
|
||||
void manifold_destruct_rect(ManifoldRect* b);
|
||||
void manifold_destruct_triangulation(ManifoldTriangulation* M);
|
||||
|
||||
// pointer free + destruction
|
||||
|
||||
void manifold_delete_manifold(ManifoldManifold* m);
|
||||
void manifold_delete_manifold_vec(ManifoldManifoldVec* ms);
|
||||
void manifold_delete_cross_section(ManifoldCrossSection* cs);
|
||||
void manifold_delete_cross_section_vec(ManifoldCrossSectionVec* csv);
|
||||
void manifold_delete_simple_polygon(ManifoldSimplePolygon* p);
|
||||
void manifold_delete_polygons(ManifoldPolygons* p);
|
||||
void manifold_delete_meshgl(ManifoldMeshGL* m);
|
||||
void manifold_delete_meshgl64(ManifoldMeshGL64* m);
|
||||
void manifold_delete_box(ManifoldBox* b);
|
||||
void manifold_delete_rect(ManifoldRect* b);
|
||||
void manifold_delete_triangulation(ManifoldTriangulation* m);
|
||||
|
||||
// MeshIO / Export
|
||||
|
||||
// Import a manifold from a Wavefront obj file.
|
||||
// The obj_file parameter is the content of the obj file, not the filename,
|
||||
// and should be null-terminated.
|
||||
ManifoldManifold* manifold_read_obj(void* mem, char* obj_file);
|
||||
// Import a meshgl from a Wavefront obj file.
|
||||
// The obj_file parameter is the content of the obj file, not the filename,
|
||||
// and should be null-terminated.
|
||||
ManifoldMeshGL64* manifold_meshgl64_read_obj(void* mem, char* obj_file);
|
||||
// Export a manifold to a Wavefront obj file.
|
||||
// The callback accepts two parameters:
|
||||
// 1. Temporary null-terminated string buffer, containing the content of the
|
||||
// file. This buffer will be freed automatically after returning from the
|
||||
// callback.
|
||||
// 2. An arg value (the third parameter in the manifold_write_obj function), for
|
||||
// passing additional data into the callback.
|
||||
void manifold_write_obj(ManifoldManifold* manifold,
|
||||
void (*callback)(char*, void*), void* args);
|
||||
// Export a MeshGL64 to a Wavefront obj file.
|
||||
// The callback accepts two parameters:
|
||||
// 1. Temporary null-terminated string buffer, containing the content of the
|
||||
// file. This buffer will be freed automatically after returning from the
|
||||
// callback.
|
||||
// 2. An arg value (the third parameter in the manifold_write_obj function), for
|
||||
// passing additional data into the callback.
|
||||
void manifold_meshgl64_write_obj(ManifoldMeshGL64* mesh,
|
||||
void (*callback)(char*, void*), void* args);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,428 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Polygons
|
||||
|
||||
ManifoldSimplePolygon *manifold_simple_polygon(void *mem, ManifoldVec2 *ps,
|
||||
size_t length);
|
||||
ManifoldPolygons *manifold_polygons(void *mem, ManifoldSimplePolygon **ps,
|
||||
size_t length);
|
||||
size_t manifold_simple_polygon_length(ManifoldSimplePolygon *p);
|
||||
size_t manifold_polygons_length(ManifoldPolygons *ps);
|
||||
size_t manifold_polygons_simple_length(ManifoldPolygons *ps, int idx);
|
||||
ManifoldVec2 manifold_simple_polygon_get_point(ManifoldSimplePolygon *p,
|
||||
int idx);
|
||||
ManifoldSimplePolygon *manifold_polygons_get_simple(void *mem,
|
||||
ManifoldPolygons *ps,
|
||||
int idx);
|
||||
ManifoldVec2 manifold_polygons_get_point(ManifoldPolygons *ps, int simple_idx,
|
||||
int pt_idx);
|
||||
|
||||
// Mesh Construction
|
||||
|
||||
ManifoldMeshGL *manifold_meshgl(void *mem, float *vert_props, size_t n_verts,
|
||||
size_t n_props, uint32_t *tri_verts,
|
||||
size_t n_tris);
|
||||
|
||||
ManifoldMeshGL *manifold_meshgl_w_tangents(void *mem, float *vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint32_t *tri_verts, size_t n_tris,
|
||||
float *halfedge_tangent);
|
||||
ManifoldMeshGL *manifold_get_meshgl(void *mem, ManifoldManifold *m);
|
||||
ManifoldMeshGL *manifold_meshgl_copy(void *mem, ManifoldMeshGL *m);
|
||||
|
||||
// SDF
|
||||
// By default, the execution policy (sequential or parallel) of
|
||||
// manifold_level_set will be chosen automatically depending on the size of the
|
||||
// job and whether Manifold has been compiled with a PAR backend. If you are
|
||||
// using these bindings from a language that has a runtime lock preventing the
|
||||
// parallel execution of closures, then you should use manifold_level_set_seq to
|
||||
// force sequential execution.
|
||||
ManifoldMeshGL *manifold_level_set(void *mem, float (*sdf)(float, float, float),
|
||||
ManifoldBox *bounds, float edge_length,
|
||||
float level);
|
||||
ManifoldMeshGL *manifold_level_set_seq(void *mem,
|
||||
float (*sdf)(float, float, float),
|
||||
ManifoldBox *bounds, float edge_length,
|
||||
float level);
|
||||
// The _context variants of manifold_level_set allow a pointer to be passed
|
||||
// back to each invocation of the sdf function pointer, for languages that
|
||||
// need additional data.
|
||||
ManifoldMeshGL *manifold_level_set_context(
|
||||
void *mem, float (*sdf)(float, float, float, void *), ManifoldBox *bounds,
|
||||
float edge_length, float level, void *ctx);
|
||||
ManifoldMeshGL *manifold_level_set_seq_context(
|
||||
void *mem, float (*sdf)(float, float, float, void *), ManifoldBox *bounds,
|
||||
float edge_length, float level, void *ctx);
|
||||
|
||||
// Manifold Vectors
|
||||
|
||||
ManifoldManifoldVec *manifold_manifold_empty_vec(void *mem);
|
||||
ManifoldManifoldVec *manifold_manifold_vec(void *mem, size_t sz);
|
||||
void manifold_manifold_vec_reserve(ManifoldManifoldVec *ms, size_t sz);
|
||||
size_t manifold_manifold_vec_length(ManifoldManifoldVec *ms);
|
||||
ManifoldManifold *manifold_manifold_vec_get(void *mem, ManifoldManifoldVec *ms,
|
||||
int idx);
|
||||
void manifold_manifold_vec_set(ManifoldManifoldVec *ms, int idx,
|
||||
ManifoldManifold *m);
|
||||
void manifold_manifold_vec_push_back(ManifoldManifoldVec *ms,
|
||||
ManifoldManifold *m);
|
||||
|
||||
// Manifold Booleans
|
||||
|
||||
ManifoldManifold *manifold_boolean(void *mem, ManifoldManifold *a,
|
||||
ManifoldManifold *b, ManifoldOpType op);
|
||||
ManifoldManifold *manifold_batch_boolean(void *mem, ManifoldManifoldVec *ms,
|
||||
ManifoldOpType op);
|
||||
ManifoldManifold *manifold_union(void *mem, ManifoldManifold *a,
|
||||
ManifoldManifold *b);
|
||||
ManifoldManifold *manifold_difference(void *mem, ManifoldManifold *a,
|
||||
ManifoldManifold *b);
|
||||
ManifoldManifold *manifold_intersection(void *mem, ManifoldManifold *a,
|
||||
ManifoldManifold *b);
|
||||
ManifoldManifoldPair manifold_split(void *mem_first, void *mem_second,
|
||||
ManifoldManifold *a, ManifoldManifold *b);
|
||||
ManifoldManifoldPair manifold_split_by_plane(void *mem_first, void *mem_second,
|
||||
ManifoldManifold *m,
|
||||
float normal_x, float normal_y,
|
||||
float normal_z, float offset);
|
||||
ManifoldManifold *manifold_trim_by_plane(void *mem, ManifoldManifold *m,
|
||||
float normal_x, float normal_y,
|
||||
float normal_z, float offset);
|
||||
|
||||
// Convex Hulls
|
||||
|
||||
ManifoldManifold *manifold_hull(void *mem, ManifoldManifold *m);
|
||||
ManifoldManifold *manifold_batch_hull(void *mem, ManifoldManifoldVec *ms);
|
||||
ManifoldManifold *manifold_hull_pts(void *mem, ManifoldVec3 *ps, size_t length);
|
||||
|
||||
// Manifold Transformations
|
||||
|
||||
ManifoldManifold *manifold_translate(void *mem, ManifoldManifold *m, float x,
|
||||
float y, float z);
|
||||
ManifoldManifold *manifold_rotate(void *mem, ManifoldManifold *m, float x,
|
||||
float y, float z);
|
||||
ManifoldManifold *manifold_scale(void *mem, ManifoldManifold *m, float x,
|
||||
float y, float z);
|
||||
ManifoldManifold *manifold_transform(void *mem, ManifoldManifold *m, float x1,
|
||||
float y1, float z1, float x2, float y2,
|
||||
float z2, float x3, float y3, float z3,
|
||||
float x4, float y4, float z4);
|
||||
ManifoldManifold *manifold_mirror(void *mem, ManifoldManifold *m, float nx,
|
||||
float ny, float nz);
|
||||
ManifoldManifold *manifold_warp(void *mem, ManifoldManifold *m,
|
||||
ManifoldVec3 (*fun)(float, float, float));
|
||||
ManifoldManifold *manifold_refine(void *mem, ManifoldManifold *m, int refine);
|
||||
|
||||
// Manifold Shapes / Constructors
|
||||
|
||||
ManifoldManifold *manifold_empty(void *mem);
|
||||
ManifoldManifold *manifold_copy(void *mem, ManifoldManifold *m);
|
||||
ManifoldManifold *manifold_tetrahedron(void *mem);
|
||||
ManifoldManifold *manifold_cube(void *mem, float x, float y, float z,
|
||||
int center);
|
||||
ManifoldManifold *manifold_cylinder(void *mem, float height, float radius_low,
|
||||
float radius_high, int circular_segments,
|
||||
int center);
|
||||
ManifoldManifold *manifold_sphere(void *mem, float radius,
|
||||
int circular_segments);
|
||||
ManifoldManifold *manifold_of_meshgl(void *mem, ManifoldMeshGL *mesh);
|
||||
ManifoldManifold *manifold_smooth(void *mem, ManifoldMeshGL *mesh,
|
||||
int *half_edges, float *smoothness,
|
||||
int n_idxs);
|
||||
ManifoldManifold *manifold_extrude(void *mem, ManifoldCrossSection *cs,
|
||||
float height, int slices,
|
||||
float twist_degrees, float scale_x,
|
||||
float scale_y);
|
||||
ManifoldManifold *manifold_revolve(void *mem, ManifoldCrossSection *cs,
|
||||
int circular_segments);
|
||||
ManifoldManifold *manifold_compose(void *mem, ManifoldManifoldVec *ms);
|
||||
ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m);
|
||||
ManifoldManifold *manifold_as_original(void *mem, ManifoldManifold *m);
|
||||
|
||||
// Manifold Info
|
||||
|
||||
int manifold_is_empty(ManifoldManifold *m);
|
||||
ManifoldError manifold_status(ManifoldManifold *m);
|
||||
int manifold_num_vert(ManifoldManifold *m);
|
||||
int manifold_num_edge(ManifoldManifold *m);
|
||||
int manifold_num_tri(ManifoldManifold *m);
|
||||
ManifoldBox *manifold_bounding_box(void *mem, ManifoldManifold *m);
|
||||
float manifold_precision(ManifoldManifold *m);
|
||||
int manifold_genus(ManifoldManifold *m);
|
||||
ManifoldProperties manifold_get_properties(ManifoldManifold *m);
|
||||
int manifold_get_circular_segments(float radius);
|
||||
int manifold_original_id(ManifoldManifold *m);
|
||||
uint32_t manifold_reserve_ids(uint32_t n);
|
||||
ManifoldManifold *manifold_set_properties(
|
||||
void *mem, ManifoldManifold *m, int num_prop,
|
||||
void (*fun)(float *new_prop, ManifoldVec3 position, const float *old_prop));
|
||||
ManifoldManifold *manifold_calculate_curvature(void *mem, ManifoldManifold *m,
|
||||
int gaussian_idx, int mean_idx);
|
||||
|
||||
// CrossSection Shapes/Constructors
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_empty(void *mem);
|
||||
ManifoldCrossSection *manifold_cross_section_copy(void *mem,
|
||||
ManifoldCrossSection *cs);
|
||||
ManifoldCrossSection *manifold_cross_section_of_simple_polygon(
|
||||
void *mem, ManifoldSimplePolygon *p, ManifoldFillRule fr);
|
||||
ManifoldCrossSection *manifold_cross_section_of_polygons(void *mem,
|
||||
ManifoldPolygons *p,
|
||||
ManifoldFillRule fr);
|
||||
ManifoldCrossSection *manifold_cross_section_square(void *mem, float x, float y,
|
||||
int center);
|
||||
ManifoldCrossSection *manifold_cross_section_circle(void *mem, float radius,
|
||||
int circular_segments);
|
||||
ManifoldCrossSection *manifold_cross_section_compose(
|
||||
void *mem, ManifoldCrossSectionVec *csv);
|
||||
ManifoldCrossSectionVec *manifold_cross_section_decompose(
|
||||
void *mem, ManifoldCrossSection *cs);
|
||||
|
||||
// CrossSection Vectors
|
||||
|
||||
ManifoldCrossSectionVec *manifold_cross_section_empty_vec(void *mem);
|
||||
ManifoldCrossSectionVec *manifold_cross_section_vec(void *mem, size_t sz);
|
||||
void manifold_cross_section_vec_reserve(ManifoldCrossSectionVec *csv,
|
||||
size_t sz);
|
||||
size_t manifold_cross_section_vec_length(ManifoldCrossSectionVec *csv);
|
||||
ManifoldCrossSection *manifold_cross_section_vec_get(
|
||||
void *mem, ManifoldCrossSectionVec *csv, int idx);
|
||||
void manifold_cross_section_vec_set(ManifoldCrossSectionVec *csv, int idx,
|
||||
ManifoldCrossSection *cs);
|
||||
void manifold_cross_section_vec_push_back(ManifoldCrossSectionVec *csv,
|
||||
ManifoldCrossSection *cs);
|
||||
|
||||
// CrossSection Booleans
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_boolean(void *mem,
|
||||
ManifoldCrossSection *a,
|
||||
ManifoldCrossSection *b,
|
||||
ManifoldOpType op);
|
||||
ManifoldCrossSection *manifold_cross_section_batch_boolean(
|
||||
void *mem, ManifoldCrossSectionVec *csv, ManifoldOpType op);
|
||||
ManifoldCrossSection *manifold_cross_section_union(void *mem,
|
||||
ManifoldCrossSection *a,
|
||||
ManifoldCrossSection *b);
|
||||
ManifoldCrossSection *manifold_cross_section_difference(
|
||||
void *mem, ManifoldCrossSection *a, ManifoldCrossSection *b);
|
||||
ManifoldCrossSection *manifold_cross_section_intersection(
|
||||
void *mem, ManifoldCrossSection *a, ManifoldCrossSection *b);
|
||||
|
||||
// CrossSection Convex Hulls
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_hull(void *mem,
|
||||
ManifoldCrossSection *cs);
|
||||
ManifoldCrossSection *manifold_cross_section_batch_hull(
|
||||
void *mem, ManifoldCrossSectionVec *css);
|
||||
ManifoldCrossSection *manifold_cross_section_hull_simple_polygon(
|
||||
void *mem, ManifoldSimplePolygon *ps);
|
||||
ManifoldCrossSection *manifold_cross_section_hull_polygons(
|
||||
void *mem, ManifoldPolygons *ps);
|
||||
|
||||
// CrossSection Transformation
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_translate(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x, float y);
|
||||
ManifoldCrossSection *manifold_cross_section_rotate(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float deg);
|
||||
ManifoldCrossSection *manifold_cross_section_scale(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x, float y);
|
||||
ManifoldCrossSection *manifold_cross_section_mirror(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float ax_x, float ax_y);
|
||||
ManifoldCrossSection *manifold_cross_section_transform(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3);
|
||||
ManifoldCrossSection *manifold_cross_section_warp(
|
||||
void *mem, ManifoldCrossSection *cs, ManifoldVec2 (*fun)(float, float));
|
||||
ManifoldCrossSection *manifold_cross_section_simplify(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
double epsilon);
|
||||
ManifoldCrossSection *manifold_cross_section_offset(
|
||||
void *mem, ManifoldCrossSection *cs, double delta, ManifoldJoinType jt,
|
||||
double miter_limit, int circular_segments);
|
||||
|
||||
// CrossSection Info
|
||||
|
||||
double manifold_cross_section_area(ManifoldCrossSection *cs);
|
||||
int manifold_cross_section_num_vert(ManifoldCrossSection *cs);
|
||||
int manifold_cross_section_num_contour(ManifoldCrossSection *cs);
|
||||
int manifold_cross_section_is_empty(ManifoldCrossSection *cs);
|
||||
ManifoldRect *manifold_cross_section_bounds(void *mem,
|
||||
ManifoldCrossSection *cs);
|
||||
ManifoldPolygons *manifold_cross_section_to_polygons(void *mem,
|
||||
ManifoldCrossSection *cs);
|
||||
|
||||
// Rectangle
|
||||
|
||||
ManifoldRect *manifold_rect(void *mem, float x1, float y1, float x2, float y2);
|
||||
ManifoldVec2 manifold_rect_min(ManifoldRect *r);
|
||||
ManifoldVec2 manifold_rect_max(ManifoldRect *r);
|
||||
ManifoldVec2 manifold_rect_dimensions(ManifoldRect *r);
|
||||
ManifoldVec2 manifold_rect_center(ManifoldRect *r);
|
||||
float manifold_rect_scale(ManifoldRect *r);
|
||||
int manifold_rect_contains_pt(ManifoldRect *r, float x, float y);
|
||||
int manifold_rect_contains_rect(ManifoldRect *a, ManifoldRect *b);
|
||||
void manifold_rect_include_pt(ManifoldRect *r, float x, float y);
|
||||
ManifoldRect *manifold_rect_union(void *mem, ManifoldRect *a, ManifoldRect *b);
|
||||
ManifoldRect *manifold_rect_transform(void *mem, ManifoldRect *r, float x1,
|
||||
float y1, float x2, float y2, float x3,
|
||||
float y3);
|
||||
ManifoldRect *manifold_rect_translate(void *mem, ManifoldRect *r, float x,
|
||||
float y);
|
||||
ManifoldRect *manifold_rect_mul(void *mem, ManifoldRect *r, float x, float y);
|
||||
int manifold_rect_does_overlap_rect(ManifoldRect *a, ManifoldRect *r);
|
||||
int manifold_rect_is_empty(ManifoldRect *r);
|
||||
int manifold_rect_is_finite(ManifoldRect *r);
|
||||
|
||||
// Bounding Box
|
||||
|
||||
ManifoldBox *manifold_box(void *mem, float x1, float y1, float z1, float x2,
|
||||
float y2, float z2);
|
||||
ManifoldVec3 manifold_box_min(ManifoldBox *b);
|
||||
ManifoldVec3 manifold_box_max(ManifoldBox *b);
|
||||
ManifoldVec3 manifold_box_dimensions(ManifoldBox *b);
|
||||
ManifoldVec3 manifold_box_center(ManifoldBox *b);
|
||||
float manifold_box_scale(ManifoldBox *b);
|
||||
int manifold_box_contains_pt(ManifoldBox *b, float x, float y, float z);
|
||||
int manifold_box_contains_box(ManifoldBox *a, ManifoldBox *b);
|
||||
void manifold_box_include_pt(ManifoldBox *b, float x, float y, float z);
|
||||
ManifoldBox *manifold_box_union(void *mem, ManifoldBox *a, ManifoldBox *b);
|
||||
ManifoldBox *manifold_box_transform(void *mem, ManifoldBox *b, float x1,
|
||||
float y1, float z1, float x2, float y2,
|
||||
float z2, float x3, float y3, float z3,
|
||||
float x4, float y4, float z4);
|
||||
ManifoldBox *manifold_box_translate(void *mem, ManifoldBox *b, float x, float y,
|
||||
float z);
|
||||
ManifoldBox *manifold_box_mul(void *mem, ManifoldBox *b, float x, float y,
|
||||
float z);
|
||||
int manifold_box_does_overlap_pt(ManifoldBox *b, float x, float y, float z);
|
||||
int manifold_box_does_overlap_box(ManifoldBox *a, ManifoldBox *b);
|
||||
int manifold_box_is_finite(ManifoldBox *b);
|
||||
|
||||
// Static Quality Globals
|
||||
|
||||
void manifold_set_min_circular_angle(float degrees);
|
||||
void manifold_set_min_circular_edge_length(float length);
|
||||
void manifold_set_circular_segments(int number);
|
||||
|
||||
// Manifold Mesh Extraction
|
||||
|
||||
int manifold_meshgl_num_prop(ManifoldMeshGL *m);
|
||||
int manifold_meshgl_num_vert(ManifoldMeshGL *m);
|
||||
int manifold_meshgl_num_tri(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_vert_properties_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_tri_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_merge_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_run_index_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_run_original_id_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_run_transform_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_face_id_length(ManifoldMeshGL *m);
|
||||
size_t manifold_meshgl_tangent_length(ManifoldMeshGL *m);
|
||||
float *manifold_meshgl_vert_properties(void *mem, ManifoldMeshGL *m);
|
||||
uint32_t *manifold_meshgl_tri_verts(void *mem, ManifoldMeshGL *m);
|
||||
uint32_t *manifold_meshgl_merge_from_vert(void *mem, ManifoldMeshGL *m);
|
||||
uint32_t *manifold_meshgl_merge_to_vert(void *mem, ManifoldMeshGL *m);
|
||||
uint32_t *manifold_meshgl_run_index(void *mem, ManifoldMeshGL *m);
|
||||
uint32_t *manifold_meshgl_run_original_id(void *mem, ManifoldMeshGL *m);
|
||||
float *manifold_meshgl_run_transform(void *mem, ManifoldMeshGL *m);
|
||||
uint32_t *manifold_meshgl_face_id(void *mem, ManifoldMeshGL *m);
|
||||
float *manifold_meshgl_halfedge_tangent(void *mem, ManifoldMeshGL *m);
|
||||
|
||||
// memory size
|
||||
|
||||
size_t manifold_manifold_size();
|
||||
size_t manifold_manifold_vec_size();
|
||||
size_t manifold_cross_section_size();
|
||||
size_t manifold_cross_section_vec_size();
|
||||
size_t manifold_simple_polygon_size();
|
||||
size_t manifold_polygons_size();
|
||||
size_t manifold_manifold_pair_size();
|
||||
size_t manifold_meshgl_size();
|
||||
size_t manifold_box_size();
|
||||
size_t manifold_rect_size();
|
||||
size_t manifold_curvature_size();
|
||||
|
||||
// destruction
|
||||
|
||||
void manifold_destruct_manifold(ManifoldManifold *m);
|
||||
void manifold_destruct_manifold_vec(ManifoldManifoldVec *ms);
|
||||
void manifold_destruct_cross_section(ManifoldCrossSection *m);
|
||||
void manifold_destruct_cross_section_vec(ManifoldCrossSectionVec *csv);
|
||||
void manifold_destruct_simple_polygon(ManifoldSimplePolygon *p);
|
||||
void manifold_destruct_polygons(ManifoldPolygons *p);
|
||||
void manifold_destruct_meshgl(ManifoldMeshGL *m);
|
||||
void manifold_destruct_box(ManifoldBox *b);
|
||||
void manifold_destruct_rect(ManifoldRect *b);
|
||||
|
||||
// pointer free + destruction
|
||||
|
||||
void manifold_delete_manifold(ManifoldManifold *m);
|
||||
void manifold_delete_manifold_vec(ManifoldManifoldVec *ms);
|
||||
void manifold_delete_cross_section(ManifoldCrossSection *cs);
|
||||
void manifold_delete_cross_section_vec(ManifoldCrossSectionVec *csv);
|
||||
void manifold_delete_simple_polygon(ManifoldSimplePolygon *p);
|
||||
void manifold_delete_polygons(ManifoldPolygons *p);
|
||||
void manifold_delete_meshgl(ManifoldMeshGL *m);
|
||||
void manifold_delete_box(ManifoldBox *b);
|
||||
void manifold_delete_rect(ManifoldRect *b);
|
||||
|
||||
// MeshIO / Export
|
||||
|
||||
#ifdef MANIFOLD_EXPORT
|
||||
ManifoldMaterial *manifold_material(void *mem);
|
||||
void manifold_material_set_roughness(ManifoldMaterial *mat, float roughness);
|
||||
void manifold_material_set_metalness(ManifoldMaterial *mat, float metalness);
|
||||
void manifold_material_set_color(ManifoldMaterial *mat, ManifoldVec4 color);
|
||||
void manifold_material_set_vert_color(ManifoldMaterial *mat,
|
||||
ManifoldVec4 *vert_color, size_t n_vert);
|
||||
ManifoldExportOptions *manifold_export_options(void *mem);
|
||||
void manifold_export_options_set_faceted(ManifoldExportOptions *options,
|
||||
int faceted);
|
||||
void manifold_export_options_set_material(ManifoldExportOptions *options,
|
||||
ManifoldMaterial *mat);
|
||||
void manifold_export_meshgl(const char *filename, ManifoldMeshGL *mesh,
|
||||
ManifoldExportOptions *options);
|
||||
ManifoldMeshGL *manifold_import_meshgl(void *mem, const char *filename,
|
||||
int force_cleanup);
|
||||
|
||||
size_t manifold_material_size();
|
||||
size_t manifold_export_options_size();
|
||||
|
||||
void manifold_destruct_material(ManifoldMaterial *m);
|
||||
void manifold_destruct_export_options(ManifoldExportOptions *options);
|
||||
|
||||
void manifold_delete_material(ManifoldMaterial *m);
|
||||
void manifold_delete_export_options(ManifoldExportOptions *options);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// opaque pointers
|
||||
|
||||
@@ -24,11 +23,15 @@ typedef struct ManifoldCrossSection ManifoldCrossSection;
|
||||
typedef struct ManifoldCrossSectionVec ManifoldCrossSectionVec;
|
||||
typedef struct ManifoldSimplePolygon ManifoldSimplePolygon;
|
||||
typedef struct ManifoldPolygons ManifoldPolygons;
|
||||
typedef struct ManifoldMesh ManifoldMesh;
|
||||
typedef struct ManifoldMeshGL ManifoldMeshGL;
|
||||
typedef struct ManifoldMeshGL64 ManifoldMeshGL64;
|
||||
typedef struct ManifoldBox ManifoldBox;
|
||||
typedef struct ManifoldRect ManifoldRect;
|
||||
typedef struct ManifoldTriangulation ManifoldTriangulation;
|
||||
|
||||
#ifdef MANIFOLD_EXPORT
|
||||
typedef struct ManifoldMaterial ManifoldMaterial;
|
||||
typedef struct ManifoldExportOptions ManifoldExportOptions;
|
||||
#endif
|
||||
|
||||
// structs
|
||||
|
||||
@@ -38,14 +41,14 @@ typedef struct ManifoldManifoldPair {
|
||||
} ManifoldManifoldPair;
|
||||
|
||||
typedef struct ManifoldVec2 {
|
||||
double x;
|
||||
double y;
|
||||
float x;
|
||||
float y;
|
||||
} ManifoldVec2;
|
||||
|
||||
typedef struct ManifoldVec3 {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} ManifoldVec3;
|
||||
|
||||
typedef struct ManifoldIVec3 {
|
||||
@@ -55,39 +58,17 @@ typedef struct ManifoldIVec3 {
|
||||
} ManifoldIVec3;
|
||||
|
||||
typedef struct ManifoldVec4 {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double w;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
} ManifoldVec4;
|
||||
|
||||
typedef struct ManifoldProperties {
|
||||
double surface_area;
|
||||
double volume;
|
||||
float surface_area;
|
||||
float volume;
|
||||
} ManifoldProperties;
|
||||
|
||||
typedef struct ManifoldMeshGLOptions {
|
||||
uint32_t* run_indices;
|
||||
size_t run_indices_length;
|
||||
uint32_t* run_original_ids;
|
||||
size_t run_original_ids_length;
|
||||
uint32_t* merge_from_vert;
|
||||
uint32_t* merge_to_vert;
|
||||
size_t merge_verts_length;
|
||||
float* halfedge_tangents;
|
||||
} ManifoldMeshGLOptions;
|
||||
|
||||
typedef struct ManifoldMeshGL64Options {
|
||||
uint64_t* run_indices;
|
||||
size_t run_indices_length;
|
||||
uint32_t* run_original_ids;
|
||||
size_t run_original_ids_length;
|
||||
uint64_t* merge_from_vert;
|
||||
uint64_t* merge_to_vert;
|
||||
size_t merge_verts_length;
|
||||
double* halfedge_tangents;
|
||||
} ManifoldMeshGL64Options;
|
||||
|
||||
// enums
|
||||
|
||||
typedef enum ManifoldOpType {
|
||||
@@ -109,7 +90,6 @@ typedef enum ManifoldError {
|
||||
MANIFOLD_RUN_INDEX_WRONG_LENGTH,
|
||||
MANIFOLD_FACE_ID_WRONG_LENGTH,
|
||||
MANIFOLD_INVALID_CONSTRUCTION,
|
||||
MANIFOLD_RESULT_TOO_LARGE,
|
||||
} ManifoldError;
|
||||
|
||||
typedef enum ManifoldFillRule {
|
||||
@@ -123,8 +103,4 @@ typedef enum ManifoldJoinType {
|
||||
MANIFOLD_JOIN_TYPE_SQUARE,
|
||||
MANIFOLD_JOIN_TYPE_ROUND,
|
||||
MANIFOLD_JOIN_TYPE_MITER,
|
||||
MANIFOLD_JOIN_TYPE_BEVEL,
|
||||
} ManifoldJoinType;
|
||||
|
||||
// function pointer
|
||||
typedef double (*ManifoldSdf)(double, double, double, void*);
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "meshIO.h"
|
||||
|
||||
#include "conv.h"
|
||||
#include "types.h"
|
||||
|
||||
// C <-> C++ conversions
|
||||
|
||||
ManifoldMaterial *to_c(manifold::Material *m) {
|
||||
return reinterpret_cast<ManifoldMaterial *>(m);
|
||||
}
|
||||
|
||||
ManifoldExportOptions *to_c(manifold::ExportOptions *m) {
|
||||
return reinterpret_cast<ManifoldExportOptions *>(m);
|
||||
}
|
||||
|
||||
manifold::Material *from_c(ManifoldMaterial *mat) {
|
||||
return reinterpret_cast<manifold::Material *>(mat);
|
||||
}
|
||||
|
||||
manifold::ExportOptions *from_c(ManifoldExportOptions *options) {
|
||||
return reinterpret_cast<manifold::ExportOptions *>(options);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// material
|
||||
|
||||
ManifoldMaterial *manifold_material(void *mem) {
|
||||
return to_c(new (mem) manifold::Material());
|
||||
}
|
||||
|
||||
void manifold_material_set_roughness(ManifoldMaterial *mat, float roughness) {
|
||||
from_c(mat)->roughness = roughness;
|
||||
}
|
||||
|
||||
void manifold_material_set_metalness(ManifoldMaterial *mat, float metalness) {
|
||||
from_c(mat)->metalness = metalness;
|
||||
}
|
||||
|
||||
void manifold_material_set_color(ManifoldMaterial *mat, ManifoldVec4 color) {
|
||||
from_c(mat)->color = from_c(color);
|
||||
}
|
||||
|
||||
void manifold_material_set_vert_color(ManifoldMaterial *mat,
|
||||
ManifoldVec4 *vert_color, size_t n_vert) {
|
||||
from_c(mat)->vertColor = vector_of_vec_array(vert_color, n_vert);
|
||||
}
|
||||
|
||||
// export options
|
||||
|
||||
ManifoldExportOptions *manifold_export_options(void *mem) {
|
||||
return to_c(new (mem) manifold::ExportOptions());
|
||||
}
|
||||
|
||||
void manifold_export_options_set_faceted(ManifoldExportOptions *options,
|
||||
int faceted) {
|
||||
from_c(options)->faceted = faceted;
|
||||
}
|
||||
|
||||
void manifold_export_options_set_material(ManifoldExportOptions *options,
|
||||
ManifoldMaterial *mat) {
|
||||
from_c(options)->mat = *from_c(mat);
|
||||
}
|
||||
|
||||
// mesh IO
|
||||
|
||||
void manifold_export_meshgl(const char *filename, ManifoldMeshGL *mesh,
|
||||
ManifoldExportOptions *options) {
|
||||
manifold::ExportMesh(std::string(filename), *from_c(mesh), *from_c(options));
|
||||
}
|
||||
|
||||
ManifoldMeshGL *manifold_import_meshgl(void *mem, const char *filename,
|
||||
int force_cleanup) {
|
||||
auto m = manifold::ImportMesh(std::string(filename), force_cleanup);
|
||||
return to_c(new (mem) manifold::MeshGL(m));
|
||||
}
|
||||
|
||||
// memory size
|
||||
size_t manifold_material_size() { return sizeof(manifold::Material); }
|
||||
|
||||
size_t manifold_export_options_size() {
|
||||
return sizeof(manifold::ExportOptions);
|
||||
}
|
||||
|
||||
// memory free + destruction
|
||||
void manifold_delete_material(ManifoldMaterial *m) { delete (from_c(m)); }
|
||||
|
||||
void manifold_delete_export_options(ManifoldExportOptions *m) {
|
||||
delete (from_c(m));
|
||||
}
|
||||
|
||||
// destruction
|
||||
void manifold_destruct_material(ManifoldMaterial *m) { from_c(m)->~Material(); }
|
||||
|
||||
void manifold_destruct_export_options(ManifoldExportOptions *m) {
|
||||
from_c(m)->~ExportOptions();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -13,92 +13,88 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "conv.h"
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/types.h"
|
||||
#include "cross_section.h"
|
||||
#include "manifold.h"
|
||||
#include "public.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
ManifoldRect* manifold_rect(void* mem, double x1, double y1, double x2,
|
||||
double y2) {
|
||||
auto p1 = vec2(x1, y1);
|
||||
auto p2 = vec2(x2, y2);
|
||||
ManifoldRect *manifold_rect(void *mem, float x1, float y1, float x2, float y2) {
|
||||
auto p1 = glm::vec2(x1, y1);
|
||||
auto p2 = glm::vec2(x2, y2);
|
||||
auto rect = new (mem) Rect(p1, p2);
|
||||
return to_c(rect);
|
||||
}
|
||||
|
||||
ManifoldVec2 manifold_rect_min(ManifoldRect* r) {
|
||||
ManifoldVec2 manifold_rect_min(ManifoldRect *r) {
|
||||
return to_c((*from_c(r)).min);
|
||||
}
|
||||
|
||||
ManifoldVec2 manifold_rect_max(ManifoldRect* r) {
|
||||
ManifoldVec2 manifold_rect_max(ManifoldRect *r) {
|
||||
return to_c((*from_c(r)).max);
|
||||
}
|
||||
|
||||
ManifoldVec2 manifold_rect_dimensions(ManifoldRect* r) {
|
||||
ManifoldVec2 manifold_rect_dimensions(ManifoldRect *r) {
|
||||
auto v = from_c(r)->Size();
|
||||
return {v.x, v.y};
|
||||
}
|
||||
|
||||
ManifoldVec2 manifold_rect_center(ManifoldRect* r) {
|
||||
ManifoldVec2 manifold_rect_center(ManifoldRect *r) {
|
||||
auto v = from_c(r)->Center();
|
||||
return {v.x, v.y};
|
||||
}
|
||||
|
||||
double manifold_rect_scale(ManifoldRect* r) { return from_c(r)->Scale(); }
|
||||
float manifold_rect_scale(ManifoldRect *r) { return from_c(r)->Scale(); }
|
||||
|
||||
int manifold_rect_contains_pt(ManifoldRect* r, double x, double y) {
|
||||
int manifold_rect_contains_pt(ManifoldRect *r, float x, float y) {
|
||||
auto rect = *from_c(r);
|
||||
auto p = vec2(x, y);
|
||||
auto p = glm::vec2(x, y);
|
||||
return rect.Contains(p);
|
||||
}
|
||||
|
||||
int manifold_rect_contains_rect(ManifoldRect* a, ManifoldRect* b) {
|
||||
int manifold_rect_contains_rect(ManifoldRect *a, ManifoldRect *b) {
|
||||
auto outer = *from_c(a);
|
||||
auto inner = *from_c(b);
|
||||
return outer.Contains(inner);
|
||||
}
|
||||
|
||||
void manifold_rect_include_pt(ManifoldRect* r, double x, double y) {
|
||||
auto& rect = *from_c(r);
|
||||
auto p = vec2(x, y);
|
||||
void manifold_rect_include_pt(ManifoldRect *r, float x, float y) {
|
||||
auto rect = *from_c(r);
|
||||
auto p = glm::vec2(x, y);
|
||||
rect.Union(p);
|
||||
}
|
||||
|
||||
ManifoldRect* manifold_rect_union(void* mem, ManifoldRect* a, ManifoldRect* b) {
|
||||
ManifoldRect *manifold_rect_union(void *mem, ManifoldRect *a, ManifoldRect *b) {
|
||||
auto rect = from_c(a)->Union(*from_c(b));
|
||||
return to_c(new (mem) Rect(rect));
|
||||
}
|
||||
|
||||
ManifoldRect* manifold_rect_transform(void* mem, ManifoldRect* r, double x1,
|
||||
double y1, double x2, double y2,
|
||||
double x3, double y3) {
|
||||
auto mat = mat2x3({x1, y1}, {x2, y2}, {x3, y3});
|
||||
ManifoldRect *manifold_rect_transform(void *mem, ManifoldRect *r, float x1,
|
||||
float y1, float x2, float y2, float x3,
|
||||
float y3) {
|
||||
auto mat = glm::mat3x2(x1, y1, x2, y2, x3, y3);
|
||||
auto transformed = from_c(r)->Transform(mat);
|
||||
return to_c(new (mem) Rect(transformed));
|
||||
}
|
||||
|
||||
ManifoldRect* manifold_rect_translate(void* mem, ManifoldRect* r, double x,
|
||||
double y) {
|
||||
auto p = vec2(x, y);
|
||||
ManifoldRect *manifold_rect_translate(void *mem, ManifoldRect *r, float x,
|
||||
float y) {
|
||||
auto rect = *from_c(r);
|
||||
auto p = glm::vec2(x, y);
|
||||
auto translated = (*from_c(r)) + p;
|
||||
return to_c(new (mem) Rect(translated));
|
||||
}
|
||||
|
||||
ManifoldRect* manifold_rect_mul(void* mem, ManifoldRect* r, double x,
|
||||
double y) {
|
||||
auto p = vec2(x, y);
|
||||
ManifoldRect *manifold_rect_mul(void *mem, ManifoldRect *r, float x, float y) {
|
||||
auto rect = *from_c(r);
|
||||
auto p = glm::vec2(x, y);
|
||||
auto scaled = (*from_c(r)) * p;
|
||||
return to_c(new (mem) Rect(scaled));
|
||||
}
|
||||
|
||||
int manifold_rect_does_overlap_rect(ManifoldRect* a, ManifoldRect* r) {
|
||||
int manifold_rect_does_overlap_rect(ManifoldRect *a, ManifoldRect *r) {
|
||||
return from_c(a)->DoesOverlap(*from_c(r));
|
||||
}
|
||||
|
||||
int manifold_rect_is_empty(ManifoldRect* r) { return from_c(r)->IsEmpty(); }
|
||||
int manifold_rect_is_empty(ManifoldRect *r) { return from_c(r)->IsEmpty(); }
|
||||
|
||||
int manifold_rect_is_finite(ManifoldRect* r) { return from_c(r)->IsFinite(); }
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
int manifold_rect_is_finite(ManifoldRect *r) { return from_c(r)->IsFinite(); }
|
||||
|
||||
@@ -12,75 +12,28 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
nanobind_add_module(manifold3d NB_STATIC STABLE_ABI LTO autogen_docstrings.inl
|
||||
manifold3d.cpp
|
||||
)
|
||||
project(python)
|
||||
|
||||
message(Python_EXECUTABLE = ${Python_EXECUTABLE})
|
||||
|
||||
if(MANIFOLD_PYBIND_STUBGEN)
|
||||
nanobind_add_stub(
|
||||
manifold3d_stub
|
||||
MODULE manifold3d
|
||||
OUTPUT manifold3d.pyi
|
||||
PYTHON_PATH $<TARGET_FILE_DIR:manifold3d>
|
||||
DEPENDS manifold3d
|
||||
PATTERN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/stub_pattern.txt
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
TARGET manifold3d_stub
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
"${Python_EXECUTABLE}"
|
||||
"${PROJECT_SOURCE_DIR}/bindings/python/stub_postbuild.py"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/manifold3d.pyi"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(manifold3d PRIVATE manifold)
|
||||
target_compile_options(
|
||||
add_subdirectory(third_party)
|
||||
nanobind_add_module(
|
||||
manifold3d
|
||||
PRIVATE ${MANIFOLD_FLAGS} -DMODULE_NAME=manifold3d
|
||||
)
|
||||
NB_STATIC STABLE_ABI LTO
|
||||
manifold3d.cpp)
|
||||
target_link_libraries(manifold3d PRIVATE manifold)
|
||||
target_compile_options(manifold3d PRIVATE ${MANIFOLD_FLAGS} -DMODULE_NAME=manifold3d)
|
||||
target_compile_features(manifold3d PUBLIC cxx_std_17)
|
||||
set_target_properties(manifold3d PROPERTIES OUTPUT_NAME "manifold3d")
|
||||
|
||||
# ideally we should generate a dependency file from python...
|
||||
set(
|
||||
DOCSTRING_DEPS
|
||||
${PROJECT_SOURCE_DIR}/src/manifold.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/constructors.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/sort.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/cross_section/cross_section.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/polygon.cpp
|
||||
${PROJECT_SOURCE_DIR}/include/manifold/common.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gen_docs.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/docstring_override.txt
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT autogen_docstrings.inl
|
||||
DEPENDS ${DOCSTRING_DEPS}
|
||||
COMMAND ${Python_EXECUTABLE}
|
||||
ARGS ${CMAKE_CURRENT_SOURCE_DIR}/gen_docs.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
target_include_directories(manifold3d PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(SKBUILD)
|
||||
set(MANIFOLD_PYBIND_LIBDIR ${SKBUILD_PLATLIB_DIR})
|
||||
else()
|
||||
set(MANIFOLD_PYBIND_LIBDIR ${Python_SITEARCH})
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS manifold3d
|
||||
LIBRARY DESTINATION ${MANIFOLD_PYBIND_LIBDIR} COMPONENT bindings
|
||||
LIBRARY DESTINATION ${SKBUILD_PLATLIB_DIR}
|
||||
COMPONENT bindings
|
||||
)
|
||||
else()
|
||||
install(
|
||||
TARGETS manifold3d
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
COMPONENT bindings
|
||||
)
|
||||
if(MANIFOLD_PYBIND_STUBGEN)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/manifold3d.pyi
|
||||
DESTINATION ${MANIFOLD_PYBIND_LIBDIR}
|
||||
COMPONENT bindings
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# Python Bindings
|
||||
|
||||
## Autogenerated Doc-Strings
|
||||
|
||||
Doc-strings for the python API wrapper are generated by gen_docs.py
|
||||
The script is run automatically during the build to keep python
|
||||
doc strings fully up-to-date with c++ sources.
|
||||
|
||||
It scrapes documentation comments and c++ function signatures
|
||||
from the manifold c++ sources, in order to generate a c++ header file
|
||||
exposing the comments as string variables named by function names.
|
||||
This allows python bindings to re-use the c++ comments directly.
|
||||
|
||||
Some snake-casing of params is applied for python use case.
|
||||
|
||||
---
|
||||
|
||||
When modifying the Manifold C++ sources, you may need to update
|
||||
gen_docs.py. For example, top-level free functions are white-listed,
|
||||
so if you add a new one, you will need to add it in gen_docs.py.
|
||||
|
||||
Similarly, the list of source files to parse is also white listed,
|
||||
so if you define functions in new files that need python wrappers,
|
||||
you will also need to up gen_docs.py.
|
||||
|
||||
To verify that python docs are correct after changes, you can
|
||||
run the following commends from the manifold repo root:
|
||||
```
|
||||
pip install .
|
||||
python -c 'import manifold3d; help(manifold3d)'
|
||||
```
|
||||
|
||||
Alternatively you could generate stubs with nanobind python module
|
||||
```
|
||||
pip install -m nanobind
|
||||
python -m nanobind.stubgen -m manifold3d -o manifold3d.pyi -i=. -p=bindings/python/stub_pattern.txt
|
||||
```
|
||||
It will emit some warnings and write a file `manifold3d.pyi`
|
||||
which will show all the function signatures and docstrings.
|
||||
@@ -1,51 +0,0 @@
|
||||
cross_section__warp__warp_func:
|
||||
Move the vertices of this CrossSection (creating a new one) according to
|
||||
any arbitrary input function, followed by a union operation (with a
|
||||
Positive fill rule) that ensures any introduced intersections are not
|
||||
included in the result.
|
||||
:param warp_func: A function that takes the original vertex position and
|
||||
return the new position.
|
||||
cross_section__warp_batch__warp_func:
|
||||
Same as CrossSection::warp but calls warpFunc with
|
||||
an ndarray[n, 2] instead of processing only one vertex at a time.
|
||||
:param warp_func: A function that takes multiple vertex positions as an
|
||||
ndarray[n, 2] and returns the new vertex positions.
|
||||
manifold__warp__warp_func:
|
||||
This function does not change the topology, but allows the vertices to be
|
||||
moved according to any arbitrary input function. It is easy to create a
|
||||
function that warps a geometrically valid object into one which overlaps, but
|
||||
that is not checked here, so it is up to the user to choose their function
|
||||
with discretion.
|
||||
:param warp_func: A function that takes the original vertex position and
|
||||
return the new position.
|
||||
manifold__warp_batch__warp_func:
|
||||
Same as Manifold::warp but calls warpFunc with with
|
||||
an ndarray[n, 3] instead of processing only one vertex at a time.
|
||||
:param warp_func: A function that takes multiple vertex positions as an
|
||||
ndarray[n, 3] and returns the new vertex positions. The result should have the
|
||||
same shape as the input.
|
||||
manifold__smooth__mesh_gl__sharpened_edges:
|
||||
Constructs a smooth version of the input mesh by creating tangents; this
|
||||
method will throw if you have supplied tangents with your mesh already. The
|
||||
actual triangle resolution is unchanged; use the Refine() method to
|
||||
interpolate to a higher-resolution curve.
|
||||
By default, every edge is calculated for maximum smoothness (very much
|
||||
approximately), attempting to minimize the maximum mean Curvature magnitude.
|
||||
No higher-order derivatives are considered, as the interpolation is
|
||||
independent per triangle, only sharing constraints on their boundaries.
|
||||
:param mesh: input Mesh.
|
||||
:param sharpened_edges: If desired, you can supply a vector of sharpened
|
||||
halfedges, which should in general be a small subset of all halfedges. Order
|
||||
of entries doesn't matter, as each one specifies the desired smoothness
|
||||
(between zero and one, with one the default for all unspecified halfedges)
|
||||
and the halfedge index (3 * triangle index + [0,1,2] where 0 is the edge
|
||||
between triVert 0 and 1, etc).
|
||||
:param edge_smoothness: Smoothness values associated to each halfedge defined
|
||||
in sharpened_edges. At a smoothness value of zero, a sharp crease is made. The
|
||||
smoothness is interpolated along each edge, so the specified value should be
|
||||
thought of as an average. Where exactly two sharpened edges meet at a vertex,
|
||||
their tangents are rotated to be colinear so that the sharpened edge can be
|
||||
continuous. Vertices with only one sharpened edge are completely smooth,
|
||||
allowing sharpened edges to smoothly vanish at termination. A single vertex
|
||||
can be sharpened by sharping all edges that are incident on it, allowing cones
|
||||
to be formed.
|
||||
@@ -1,133 +0,0 @@
|
||||
from manifold3d import *
|
||||
import numpy as np
|
||||
|
||||
|
||||
def all_root_level():
|
||||
set_min_circular_angle(10)
|
||||
set_min_circular_edge_length(1)
|
||||
set_circular_segments(22)
|
||||
n = get_circular_segments(1)
|
||||
assert n == 22
|
||||
poly = [[0, 0], [1, 0], [1, 1]]
|
||||
tris = triangulate([poly])
|
||||
tris = triangulate([np.array(poly)])
|
||||
|
||||
|
||||
def all_cross_section():
|
||||
poly = [[0, 0], [1, 0], [1, 1]]
|
||||
c = CrossSection([np.array(poly)])
|
||||
c = CrossSection([poly])
|
||||
c = CrossSection() + c
|
||||
a = c.area()
|
||||
c = CrossSection.batch_boolean(
|
||||
[
|
||||
CrossSection.circle(1),
|
||||
CrossSection.square((3, 3)),
|
||||
CrossSection.circle(3).translate((1, 1)),
|
||||
],
|
||||
OpType.Add,
|
||||
)
|
||||
c = CrossSection.batch_hull([c, c.translate((1, 0))])
|
||||
b = c.bounds()
|
||||
c = CrossSection.circle(1)
|
||||
c = CrossSection.compose([c, c.translate((1, 0))])
|
||||
cs = c.decompose()
|
||||
m = c.extrude(1)
|
||||
c = c.hull()
|
||||
c = CrossSection.hull_points(poly)
|
||||
c = CrossSection.hull_points(np.array(poly))
|
||||
e = c.is_empty()
|
||||
c = c.mirror((0, 1))
|
||||
n = c.num_contour()
|
||||
n = c.num_vert()
|
||||
c = c.offset(1)
|
||||
m = c.revolve()
|
||||
c = c.rotate(90)
|
||||
c = c.scale((2, 2))
|
||||
c = c.simplify()
|
||||
c = CrossSection.square((1, 1))
|
||||
p = c.to_polygons()
|
||||
c = c.transform([[1, 0, 0], [0, 1, 0]])
|
||||
c = c.translate((1, 1))
|
||||
c = c.warp(lambda p: (p[0] + 1, p[1] / 2))
|
||||
c = c.warp_batch(lambda ps: ps * [1, 0.5] + [1, 0])
|
||||
|
||||
|
||||
def all_manifold():
|
||||
mesh = Manifold.sphere(1).to_mesh()
|
||||
m = Manifold(mesh)
|
||||
m = Manifold() + m
|
||||
m = m.as_original()
|
||||
m = Manifold.batch_boolean(
|
||||
[
|
||||
Manifold.cylinder(4, 1),
|
||||
Manifold.cube((3, 2, 1)),
|
||||
Manifold.cylinder(5, 3).translate((1, 1, 1)),
|
||||
],
|
||||
OpType.Add,
|
||||
)
|
||||
m = Manifold.batch_hull([m, m.translate((0, 0, 1))])
|
||||
b = m.bounding_box()
|
||||
m = m.calculate_curvature(4, 5)
|
||||
m = m.calculate_normals(0)
|
||||
m = m.smooth_by_normals(0)
|
||||
m = Manifold.compose([m, m.translate((5, 0, 0))])
|
||||
m = Manifold.cube((1, 1, 1))
|
||||
m = Manifold.cylinder(1, 1)
|
||||
ms = m.decompose()
|
||||
m = Manifold.extrude(CrossSection.circle(1), 1)
|
||||
m = Manifold.revolve(CrossSection.circle(1))
|
||||
g = m.genus()
|
||||
a = m.surface_area()
|
||||
v = m.volume()
|
||||
m = m.hull()
|
||||
m = m.hull_points(mesh.vert_properties)
|
||||
e = m.is_empty()
|
||||
m = m.mirror((0, 0, 1))
|
||||
n = m.num_edge()
|
||||
n = m.num_prop()
|
||||
n = m.num_prop_vert()
|
||||
n = m.num_tri()
|
||||
n = m.num_vert()
|
||||
i = m.original_id()
|
||||
p = m.get_tolerance()
|
||||
pp = m.set_tolerance(0.0001)
|
||||
m = m.simplify(0.001)
|
||||
c = m.project()
|
||||
m = m.refine(2)
|
||||
m = m.refine_to_length(0.1)
|
||||
m = m.refine_to_tolerance(0.01)
|
||||
m = m.smooth_out()
|
||||
i = Manifold.reserve_ids(1)
|
||||
m = m.scale((1, 2, 3))
|
||||
m = m.set_properties(3, lambda pos, prop: pos)
|
||||
c = m.slice(0.5)
|
||||
m = Manifold.smooth(mesh, [0], [0.5])
|
||||
m = Manifold.sphere(1)
|
||||
m, n = m.split(m.translate((1, 0, 0)))
|
||||
m, n = m.split_by_plane((0, 0, 1), 0)
|
||||
e = m.status()
|
||||
m = Manifold.tetrahedron()
|
||||
mesh = m.to_mesh()
|
||||
ok = mesh.merge()
|
||||
m = m.transform([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])
|
||||
m = m.translate((0, 0, 0))
|
||||
m = m.trim_by_plane((0, 0, 1), 0)
|
||||
m = m.warp(lambda p: (p[0] + 1, p[1] / 2, p[2] * 2))
|
||||
m = m.warp_batch(lambda ps: ps * 2 + [1, 0, 0])
|
||||
m = Manifold.cube()
|
||||
m2 = Manifold.cube().translate([2, 0, 0])
|
||||
d = m.min_gap(m2, 2)
|
||||
mesh2 = m.to_mesh64()
|
||||
ok = mesh.merge()
|
||||
|
||||
|
||||
def run():
|
||||
all_root_level()
|
||||
all_cross_section()
|
||||
all_manifold()
|
||||
return Manifold()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -1,18 +1,18 @@
|
||||
"""
|
||||
Copyright 2022 The Manifold Authors.
|
||||
Copyright 2022 The Manifold Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from manifold3d import Manifold
|
||||
|
||||
@@ -20,82 +20,60 @@ from manifold3d import Manifold
|
||||
|
||||
INCHES = 25.4
|
||||
|
||||
brick_depth = (3 + 5 / 8) * INCHES
|
||||
brick_height = (2 + 1 / 4) * INCHES
|
||||
brick_length = (7 + 5 / 8) * INCHES
|
||||
brick_depth = (3 + 5/8) * INCHES
|
||||
brick_height = (2 + 1/4) * INCHES
|
||||
brick_length = (7 + 5/8) * INCHES
|
||||
|
||||
mortar_gap = (3 / 8) * INCHES
|
||||
mortar_gap = (3/8) * INCHES
|
||||
|
||||
|
||||
def brick():
|
||||
return Manifold.cube([brick_length, brick_depth, brick_height])
|
||||
return Manifold.cube(brick_length, brick_depth, brick_height)
|
||||
|
||||
|
||||
def halfbrick():
|
||||
return Manifold.cube([(brick_length - mortar_gap) / 2, brick_depth, brick_height])
|
||||
return Manifold.cube((brick_length - mortar_gap)/2, brick_depth, brick_height)
|
||||
|
||||
|
||||
def row(length):
|
||||
bricks = [
|
||||
brick().translate([(brick_length + mortar_gap) * x, 0, 0])
|
||||
for x in range(length)
|
||||
]
|
||||
return sum(bricks, Manifold())
|
||||
bricks = [brick().translate((brick_length + mortar_gap)*x, 0, 0)
|
||||
for x in range(length)]
|
||||
return Manifold(bricks)
|
||||
|
||||
|
||||
def wall(length, height, alternate=0):
|
||||
bricks = [
|
||||
row(length).translate(
|
||||
[
|
||||
((z + alternate) % 2) * (brick_depth + mortar_gap),
|
||||
0,
|
||||
(brick_height + mortar_gap) * z,
|
||||
]
|
||||
)
|
||||
for z in range(height)
|
||||
]
|
||||
return sum(bricks, Manifold())
|
||||
bricks = [row(length).translate(
|
||||
((z+alternate) % 2)*(brick_depth+mortar_gap), 0,
|
||||
(brick_height+mortar_gap)*z) for z in range(height)]
|
||||
return Manifold(bricks)
|
||||
|
||||
|
||||
def walls(length, width, height):
|
||||
return sum(
|
||||
[
|
||||
wall(length, height),
|
||||
wall(width, height, 1).rotate([0, 0, 90]).translate([brick_depth, 0, 0]),
|
||||
wall(length, height, 1).translate(
|
||||
[0, (width) * (brick_length + mortar_gap), 0]
|
||||
),
|
||||
wall(width, height)
|
||||
.rotate([0, 0, 90])
|
||||
.translate(
|
||||
[(length + 0.5) * (brick_length + mortar_gap) - mortar_gap, 0, 0]
|
||||
),
|
||||
],
|
||||
Manifold(),
|
||||
)
|
||||
return Manifold([
|
||||
wall(length, height),
|
||||
wall(width, height, 1).rotate(0, 0, 90).translate(brick_depth, 0, 0),
|
||||
wall(length, height, 1).translate(
|
||||
0, (width)*(brick_length+mortar_gap), 0),
|
||||
wall(width, height).rotate(0, 0, 90)
|
||||
.translate((length+0.5)*(brick_length+mortar_gap) - mortar_gap, 0, 0),
|
||||
])
|
||||
|
||||
|
||||
def floor(length, width):
|
||||
results = [walls(length, width, 1)]
|
||||
if length > 1 and width > 1:
|
||||
results.append(
|
||||
floor(length - 1, width - 1).translate(
|
||||
[brick_depth + mortar_gap, brick_depth + mortar_gap, 0]
|
||||
)
|
||||
)
|
||||
results.append(floor(length-1, width-1).translate(brick_depth +
|
||||
mortar_gap, brick_depth+mortar_gap, 0))
|
||||
if length == 1 and width > 1:
|
||||
results.append(row(width - 1).rotate((0, 0, 90)))
|
||||
results.append(row(width-1).rotate(0, 0, 90))
|
||||
if width == 1 and length > 1:
|
||||
results.append(
|
||||
row(length - 1).translate(
|
||||
[2 * (brick_depth + mortar_gap), brick_depth + mortar_gap, 0]
|
||||
)
|
||||
)
|
||||
results.append(
|
||||
halfbrick().translate([brick_depth + mortar_gap, brick_depth + mortar_gap, 0])
|
||||
)
|
||||
return sum(results, Manifold())
|
||||
results.append(row(length-1).translate(2*(brick_depth +
|
||||
mortar_gap), brick_depth+mortar_gap, 0))
|
||||
results.append(halfbrick().translate(
|
||||
brick_depth+mortar_gap, brick_depth+mortar_gap, 0))
|
||||
return Manifold(results)
|
||||
|
||||
|
||||
def run(width=10, length=10, height=10):
|
||||
return walls(length, width, height) + floor(length, width)
|
||||
|
||||
|
||||
@@ -1,33 +1,30 @@
|
||||
"""
|
||||
Copyright 2022 The Manifold Authors.
|
||||
Copyright 2022 The Manifold Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from manifold3d import Manifold
|
||||
from functools import reduce
|
||||
|
||||
# https://gist.github.com/ochafik/2db96400e3c1f73558fcede990b8a355#file-cube-with-half-spheres-dents-scad
|
||||
|
||||
|
||||
def run(n=5, overlap=True):
|
||||
a = Manifold.cube([n, n, 0.5]).translate([-0.5, -0.5, -0.5])
|
||||
a = Manifold.cube(n, n, 0.5).translate(-0.5, -0.5, -0.5)
|
||||
|
||||
spheres = [
|
||||
Manifold.sphere(0.45 if overlap else 0.55, 50).translate([i, j, 0])
|
||||
for i in range(n)
|
||||
for j in range(n)
|
||||
]
|
||||
# spheres = reduce(lambda a, b: a + b, spheres)
|
||||
spheres = [Manifold.sphere(0.45 if overlap else 0.55, 50).translate(i, j, 0)
|
||||
for i in range(n) for j in range(n)]
|
||||
spheres = reduce(lambda a, b: a + b, spheres)
|
||||
|
||||
return a - spheres
|
||||
|
||||
return a - sum(spheres, Manifold())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from manifold3d import CrossSection, Manifold
|
||||
from manifold3d import CrossSection
|
||||
|
||||
|
||||
def run():
|
||||
@@ -10,34 +10,26 @@ def run():
|
||||
cross_section = CrossSection(polygons_points)
|
||||
polygons = cross_section.to_polygons()
|
||||
polygon = polygons[0]
|
||||
if set([tuple(p) for p in polygon]) != set(polygon_points):
|
||||
raise Exception(
|
||||
f"polygon={polygon} differs from polygon_points={polygon_points}"
|
||||
)
|
||||
if set(polygon) != set(polygon_points):
|
||||
raise Exception(f"polygon={polygon} differs from polygon_points={polygon_points}")
|
||||
|
||||
# extrude a polygon to create a manifold
|
||||
extruded_polygon = Manifold.extrude(cross_section, 10.0)
|
||||
extruded_polygon = cross_section.extrude(10.0)
|
||||
eps = 0.001
|
||||
observed_volume = extruded_polygon.volume()
|
||||
observed_volume = extruded_polygon.get_volume()
|
||||
expected_volume = 10.0
|
||||
if abs(observed_volume - expected_volume) > eps:
|
||||
raise Exception(
|
||||
f"observed_volume={observed_volume} differs from expected_volume={expected_volume}"
|
||||
)
|
||||
observed_surface_area = extruded_polygon.surface_area()
|
||||
raise Exception(f"observed_volume={observed_volume} differs from expected_volume={expected_volume}")
|
||||
observed_surface_area = extruded_polygon.get_surface_area()
|
||||
expected_surface_area = 42.0
|
||||
if abs(observed_surface_area - expected_surface_area) > eps:
|
||||
raise Exception(
|
||||
f"observed_surface_area={observed_surface_area} differs from expected_surface_area={expected_surface_area}"
|
||||
)
|
||||
raise Exception(f"observed_surface_area={observed_surface_area} differs from expected_surface_area={expected_surface_area}")
|
||||
|
||||
# get bounding box from manifold
|
||||
observed_bbox = extruded_polygon.bounding_box()
|
||||
observed_bbox = extruded_polygon.bounding_box
|
||||
expected_bbox = (0.0, 0.0, 0.0, 1.0, 1.0, 10.0)
|
||||
if observed_bbox != expected_bbox:
|
||||
raise Exception(
|
||||
f"observed_bbox={observed_bbox} differs from expected_bbox={expected_bbox}"
|
||||
)
|
||||
raise Exception(f"observed_bbox={observed_bbox} differs from expected_bbox={expected_bbox}")
|
||||
|
||||
return extruded_polygon
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
"""
|
||||
Copyright 2023 The Manifold Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
from manifold3d import Manifold
|
||||
|
||||
|
||||
def gyroid(x, y, z):
|
||||
xi = x - math.pi / 4.0
|
||||
yi = y - math.pi / 4.0
|
||||
zi = z - math.pi / 4.0
|
||||
return (
|
||||
math.cos(xi) * math.sin(yi)
|
||||
+ math.cos(yi) * math.sin(zi)
|
||||
+ math.cos(zi) * math.sin(xi)
|
||||
)
|
||||
|
||||
|
||||
def gyroid_levelset(level, period, size, n):
|
||||
return Manifold.level_set(
|
||||
gyroid,
|
||||
[-period, -period, -period, period, period, period],
|
||||
period / n,
|
||||
level,
|
||||
).scale([size / period] * 3)
|
||||
|
||||
|
||||
def rhombic_dodecahedron(size):
|
||||
box = Manifold.cube(size * math.sqrt(2.0) * np.array([1, 1, 2]), True)
|
||||
result = box.rotate([90, 45, 0]) ^ box.rotate([90, 45, 90])
|
||||
return result ^ box.rotate([0, 0, 45])
|
||||
|
||||
|
||||
def gyroid_module(size=20, n=15):
|
||||
period = math.pi * 2.0
|
||||
result = (
|
||||
gyroid_levelset(-0.4, period, size, n) ^ rhombic_dodecahedron(size)
|
||||
) - gyroid_levelset(0.4, period, size, n)
|
||||
return result.rotate([-45, 0, 90]).translate([0, 0, size / math.sqrt(2.0)])
|
||||
|
||||
|
||||
def run(size=20, n=15):
|
||||
return gyroid_module(size, n)
|
||||
@@ -1,22 +0,0 @@
|
||||
import numpy as np
|
||||
from manifold3d import Manifold
|
||||
|
||||
|
||||
def run():
|
||||
small_cube = Manifold.cube([0.1, 0.1, 0.1], True)
|
||||
cube_vertices = small_cube.to_mesh().vert_properties[:, :3]
|
||||
star = Manifold.as_original(small_cube)
|
||||
for offset in [
|
||||
[[0.2, 0.0, 0.0]],
|
||||
[[-0.2, 0.0, 0.0]],
|
||||
[[0.0, 0.2, 0.0]],
|
||||
[[0.0, -0.2, 0.0]],
|
||||
[[0.0, 0.0, 0.2]],
|
||||
[[0.0, 0.0, -0.2]],
|
||||
]:
|
||||
star += Manifold.hull_points(np.concatenate((cube_vertices, offset), axis=0))
|
||||
|
||||
sphere = Manifold.sphere(0.6, 15)
|
||||
cube = Manifold.cube([1.0, 1.0, 1.0], True)
|
||||
sphereless_cube = cube - sphere
|
||||
return sphereless_cube.minkowski_sum(star)
|
||||
@@ -1,18 +1,18 @@
|
||||
"""
|
||||
Copyright 2022 The Manifold Authors.
|
||||
Copyright 2022 The Manifold Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
@@ -24,9 +24,10 @@ from time import time
|
||||
if __name__ == "__main__":
|
||||
current_file = pathlib.Path(__file__)
|
||||
current_dir = current_file.parent
|
||||
files = [f.parts[-1][:-3] for f in current_dir.glob("*.py") if f != current_file]
|
||||
files = [f.parts[-1][:-3]
|
||||
for f in current_dir.glob('*.py') if f != current_file]
|
||||
|
||||
export_models = len(sys.argv) == 2 and sys.argv[-1] == "-e"
|
||||
export_models = len(sys.argv) == 2 and sys.argv[-1] == '-e'
|
||||
|
||||
for f in files:
|
||||
module = importlib.import_module(f)
|
||||
@@ -40,10 +41,9 @@ if __name__ == "__main__":
|
||||
else:
|
||||
vertices = mesh.vert_properties
|
||||
colors = None
|
||||
meshOut = trimesh.Trimesh(
|
||||
vertices=vertices, faces=mesh.tri_verts, vertex_colors=colors
|
||||
)
|
||||
trimesh.exchange.export.export_mesh(meshOut, f"{f}.glb", "glb")
|
||||
print(f"Exported model to {f}.glb")
|
||||
meshOut = trimesh.Trimesh(vertices=vertices, faces=mesh.tri_verts,
|
||||
vertex_colors=colors)
|
||||
trimesh.exchange.export.export_mesh(meshOut, f'{f}.glb', 'glb')
|
||||
print(f'Exported model to {f}.glb')
|
||||
t1 = time()
|
||||
print(f"Took {(t1-t0)*1000:.1f}ms for {f}")
|
||||
print(f'Took {(t1-t0)*1000:.1f}ms for {f}')
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
"""
|
||||
Copyright 2023 The Manifold Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from manifold3d import Manifold, Mesh
|
||||
import numpy as np
|
||||
|
||||
|
||||
def run():
|
||||
# A smoothed manifold demonstrating selective edge sharpening with
|
||||
# smooth() and refine(), see more details at:
|
||||
# https://elalish.blogspot.com/2022/03/smoothing-triangle-meshes.html
|
||||
|
||||
height = 10
|
||||
radius = 30
|
||||
offset = 20
|
||||
wiggles = 12
|
||||
sharpness = 0.8
|
||||
n = 50
|
||||
|
||||
triangles = []
|
||||
positions = [[-offset, 0, height], [-offset, 0, -height]]
|
||||
sharpenedEdges = []
|
||||
|
||||
delta = np.pi / wiggles
|
||||
for i in range(2 * wiggles):
|
||||
theta = (i - wiggles) * delta
|
||||
amp = 0.5 * height * max(np.cos(0.8 * theta), 0)
|
||||
|
||||
positions.append(
|
||||
[
|
||||
radius * np.cos(theta),
|
||||
radius * np.sin(theta),
|
||||
amp * (1 if i % 2 == 0 else -1),
|
||||
]
|
||||
)
|
||||
|
||||
j = i + 1
|
||||
if j == 2 * wiggles:
|
||||
j = 0
|
||||
|
||||
smoothness = 1 - sharpness * np.cos((theta + delta / 2) / 2)
|
||||
halfedge = 3 * len(triangles) + 1
|
||||
sharpenedEdges.append((halfedge, smoothness))
|
||||
triangles.append([0, 2 + i, 2 + j])
|
||||
|
||||
halfedge = 3 * len(triangles) + 1
|
||||
sharpenedEdges.append((halfedge, smoothness))
|
||||
triangles.append([1, 2 + j, 2 + i])
|
||||
|
||||
scallop = Mesh(
|
||||
tri_verts=np.array(triangles, np.int32),
|
||||
vert_properties=np.array(positions, np.float32),
|
||||
)
|
||||
|
||||
def colorCurvature(_pos, oldProp):
|
||||
a = max(0, min(1, oldProp[0] / 3 + 0.5))
|
||||
b = a * a * (3 - 2 * a)
|
||||
red = [1, 0, 0]
|
||||
blue = [0, 0, 1]
|
||||
return [(1 - b) * blue[i] + b * red[i] for i in range(3)]
|
||||
|
||||
edges, smoothing = zip(*sharpenedEdges)
|
||||
return (
|
||||
Manifold.smooth(scallop, edges, smoothing)
|
||||
.refine(n)
|
||||
.calculate_curvature(-1, 0)
|
||||
.set_properties(3, colorCurvature)
|
||||
)
|
||||
@@ -1,23 +0,0 @@
|
||||
"""
|
||||
Copyright 2022 The Manifold Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from manifold3d import Manifold
|
||||
|
||||
|
||||
def run():
|
||||
a = Manifold.cube([1.0, 1.0, 1.0])
|
||||
b = Manifold.cube([1.0, 1.0, 1.0]).rotate([45.0, 45.0, 45.0])
|
||||
return a.split(b)[0]
|
||||
@@ -4,27 +4,26 @@ import numpy as np
|
||||
|
||||
def fractal(holes, hole, w, position, depth, maxDepth):
|
||||
w /= 3
|
||||
holes.append(hole.scale([w, w, 1.0]).translate([position[0], position[1], 0.0]))
|
||||
holes.append(hole.scale([w, w, 1.0]).translate(
|
||||
[position[0], position[1], 0.0]))
|
||||
if depth == maxDepth:
|
||||
return
|
||||
offsets = np.array(
|
||||
[
|
||||
[-w, -w],
|
||||
[-w, 0.0],
|
||||
[-w, w],
|
||||
[0.0, w],
|
||||
[w, w],
|
||||
[w, 0.0],
|
||||
[w, -w],
|
||||
[0.0, -w],
|
||||
]
|
||||
)
|
||||
offsets = np.array([
|
||||
[-w, -w],
|
||||
[-w, 0.0],
|
||||
[-w, w],
|
||||
[0.0, w],
|
||||
[w, w],
|
||||
[w, 0.0],
|
||||
[w, -w],
|
||||
[0.0, -w],
|
||||
])
|
||||
for offset in offsets:
|
||||
fractal(holes, hole, w, position + offset, depth + 1, maxDepth)
|
||||
|
||||
|
||||
def posColors(pos, _):
|
||||
return [-p + 0.5 for p in pos] + [1.0]
|
||||
return [1 - p / 2 for p in pos] + [1.0]
|
||||
|
||||
|
||||
def run(n=1):
|
||||
@@ -38,8 +37,4 @@ def run(n=1):
|
||||
result -= hole.rotate([90, 0, 0])
|
||||
result -= hole.rotate([0, 90, 0])
|
||||
|
||||
return (
|
||||
result.trim_by_plane([1, 1, 1], 0)
|
||||
.set_properties(4, posColors)
|
||||
.scale([100, 100, 100])
|
||||
)
|
||||
return result.trim_by_plane([1, 1, 1], 0).set_properties(4, posColors).scale(100)
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
from manifold3d import *
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
# Creates a classic torus knot, defined as a string wrapping periodically
|
||||
# around the surface of an imaginary donut. If p and q have a common
|
||||
# factor then you will get multiple separate, interwoven knots. This is
|
||||
# an example of using the warp() method, thus avoiding any direct
|
||||
# handling of triangles.
|
||||
|
||||
|
||||
def run(warp_single=False):
|
||||
# The number of times the thread passes through the donut hole.
|
||||
p = 1
|
||||
# The number of times the thread circles the donut.
|
||||
q = 3
|
||||
# Radius of the interior of the imaginary donut.
|
||||
majorRadius = 25
|
||||
# Radius of the small cross-section of the imaginary donut.
|
||||
minorRadius = 10
|
||||
# Radius of the small cross-section of the actual object.
|
||||
threadRadius = 3.75
|
||||
# Number of linear segments making up the threadRadius circle. Default is
|
||||
# getCircularSegments(threadRadius).
|
||||
circularSegments = -1
|
||||
# Number of segments along the length of the knot. Default makes roughly
|
||||
# square facets.
|
||||
linearSegments = -1
|
||||
|
||||
# These default values recreate Matlab Knot by Emmett Lalish:
|
||||
# https://www.thingiverse.com/thing:7080
|
||||
|
||||
kLoops = np.gcd(p, q)
|
||||
pk = p / kLoops
|
||||
qk = q / kLoops
|
||||
n = (
|
||||
circularSegments
|
||||
if circularSegments > 2
|
||||
else get_circular_segments(threadRadius)
|
||||
)
|
||||
m = linearSegments if linearSegments > 2 else n * qk * majorRadius / threadRadius
|
||||
|
||||
offset = 2
|
||||
circle = CrossSection.circle(1, n).translate([offset, 0])
|
||||
|
||||
def ax_rotate(x, theta):
|
||||
a, b = (x + 1) % 3, (x + 2) % 3
|
||||
s, c = np.sin(theta), np.cos(theta)
|
||||
m = np.zeros((len(theta), 4, 4), dtype=np.float32)
|
||||
m[:, a, a], m[:, a, b] = c, s
|
||||
m[:, b, a], m[:, b, b] = -s, c
|
||||
m[:, x, x], m[:, 3, 3] = 1, 1
|
||||
return m
|
||||
|
||||
def func(pts):
|
||||
npts = pts.shape[0]
|
||||
x, y, z = pts[:, 0], pts[:, 1], pts[:, 2]
|
||||
psi = qk * np.arctan2(x, y)
|
||||
theta = psi * pk / qk
|
||||
x1 = np.sqrt(x * x + y * y)
|
||||
phi = np.arctan2(x1 - offset, z)
|
||||
|
||||
v = np.zeros((npts, 4), dtype=np.float32)
|
||||
v[:, 0] = threadRadius * np.cos(phi)
|
||||
v[:, 2] = threadRadius * np.sin(phi)
|
||||
v[:, 3] = 1
|
||||
r = majorRadius + minorRadius * np.cos(theta)
|
||||
|
||||
m1 = ax_rotate(0, -np.arctan2(pk * minorRadius, qk * r))
|
||||
m1[:, 3, 0] = minorRadius
|
||||
m2 = ax_rotate(1, theta)
|
||||
m2[:, 3, 0] = majorRadius
|
||||
m3 = ax_rotate(2, psi)
|
||||
|
||||
v = v[:, None, :] @ m1 @ m2 @ m3
|
||||
return v[:, 0, :3]
|
||||
|
||||
def func_single(v):
|
||||
pts = np.array([v])
|
||||
return func(pts)[0]
|
||||
|
||||
if warp_single:
|
||||
return Manifold.revolve(circle, int(m)).warp(func_single)
|
||||
else:
|
||||
return Manifold.revolve(circle, int(m)).warp_batch(func)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("warp_single", [True, False])
|
||||
def test_warp(warp_single):
|
||||
m = run(warp_single=warp_single)
|
||||
assert m.volume() == pytest.approx(20785.76)
|
||||
assert m.surface_area() == pytest.approx(11176.8)
|
||||
assert m.genus() == 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -1,8 +1,7 @@
|
||||
from manifold3d import Manifold
|
||||
|
||||
|
||||
def run():
|
||||
# for some reason this causes collider error
|
||||
obj = Manifold.cube()
|
||||
obj += Manifold.cube().rotate([0, 0, 45 + 180])
|
||||
obj += Manifold([Manifold.cube()]).rotate(0, 0, 45+180)
|
||||
return obj
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
from os.path import dirname
|
||||
import re
|
||||
|
||||
base = dirname(dirname(dirname(__file__)))
|
||||
|
||||
|
||||
def snake_case(name):
|
||||
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower()
|
||||
|
||||
|
||||
def python_param_modifier(comment):
|
||||
# p = f":{snake_case(m[0][1:])}:"
|
||||
comment = re.sub(r"@(param \w+)", lambda m: f":{snake_case(m[1])}:", comment)
|
||||
# python API renames `MeshGL` to `Mesh`
|
||||
comment = re.sub("mesh_gl", "mesh", comment)
|
||||
comment = re.sub("MeshGL", "Mesh", comment)
|
||||
return comment
|
||||
|
||||
|
||||
def method_key(name):
|
||||
name = re.sub(r"\+", "_plus", name)
|
||||
name = re.sub(r"\-", "_minus", name)
|
||||
name = re.sub(r"\^", "_xor", name)
|
||||
name = re.sub(r"\=", "_eq", name)
|
||||
name = re.sub(r"\:", "_", name)
|
||||
name = re.sub(r"\~", "destroy_", name)
|
||||
return name
|
||||
|
||||
|
||||
parens_re = re.compile(r"[^(]+\(([^(]*(\(.*\))*[^(]*\))", flags=re.DOTALL)
|
||||
args_re = re.compile(
|
||||
r"^[^,^\(^\)]*(\(.*\))*[^,^\(^\)]*[\s\&\*]([0-9\w]+)\s*[,\)]", flags=re.DOTALL
|
||||
)
|
||||
|
||||
|
||||
def parse_args(s):
|
||||
par = parens_re.match(s)
|
||||
if not par:
|
||||
return None
|
||||
out = []
|
||||
arg_str = par[1]
|
||||
while m := re.search(args_re, arg_str):
|
||||
out += [snake_case(m[2])]
|
||||
arg_str = arg_str[m.span()[1] :]
|
||||
return out
|
||||
|
||||
|
||||
def collect(fname, matcher, param_modifier=python_param_modifier):
|
||||
comment = ""
|
||||
with open(fname) as f:
|
||||
for line in f:
|
||||
line = line.lstrip()
|
||||
if line.startswith("/**"):
|
||||
comment = ""
|
||||
elif line.startswith("*/"):
|
||||
pass
|
||||
elif line.startswith("*") and comment is not None:
|
||||
comment += line[1:].lstrip()
|
||||
elif comment and (m := matcher(line)):
|
||||
while (args := parse_args(line)) is None:
|
||||
line += next(f)
|
||||
if len(line) > 500:
|
||||
break
|
||||
|
||||
method = method_key(snake_case(m[1]))
|
||||
# comment = re.sub(param_re, param_modifier, comment)
|
||||
comment = param_modifier(comment)
|
||||
method = "__".join([method, *args])
|
||||
assert method not in comments
|
||||
comments[method] = comment
|
||||
comment = ""
|
||||
|
||||
|
||||
comments = {}
|
||||
|
||||
method_re = re.compile(r"(\w+::[\w\-\+\^\=\:]+)\(")
|
||||
function_re = re.compile(r"([\w\-\+\^\=\:]+)\(")
|
||||
|
||||
|
||||
# we don't handle inline functions in classes properly
|
||||
# so instead just white-list functions we want
|
||||
def select_functions(s):
|
||||
m = function_re.search(s)
|
||||
if m and "Triangulate" in m[0]:
|
||||
return m
|
||||
if m and "Circular" in m[0]:
|
||||
return m
|
||||
return None
|
||||
|
||||
|
||||
collect(f"{base}/src/manifold.cpp", lambda s: method_re.search(s))
|
||||
collect(f"{base}/src/constructors.cpp", lambda s: method_re.search(s))
|
||||
collect(f"{base}/src/sort.cpp", lambda s: method_re.search(s))
|
||||
collect(f"{base}/src/sdf.cpp", lambda s: method_re.search(s))
|
||||
collect(f"{base}/src/cross_section/cross_section.cpp", lambda s: method_re.search(s))
|
||||
collect(f"{base}/src/polygon.cpp", select_functions)
|
||||
collect(f"{base}/include/manifold/common.h", select_functions)
|
||||
|
||||
comments = dict(sorted(comments.items()))
|
||||
|
||||
with open(f"{base}/bindings/python/docstring_override.txt") as f:
|
||||
key = ""
|
||||
for l in f:
|
||||
if l.startswith(" "):
|
||||
comments[key] += l[2:]
|
||||
else:
|
||||
key = l[:-2]
|
||||
if key not in comments.keys():
|
||||
print(f"Error, unknown docstring override key {key}")
|
||||
exit(-1)
|
||||
comments[key] = ""
|
||||
|
||||
gen_h = "autogen_docstrings.inl"
|
||||
with open(gen_h, "w") as f:
|
||||
f.write("#pragma once\n\n")
|
||||
f.write("// --- AUTO GENERATED ---\n")
|
||||
f.write("// gen_docs.py is run by cmake build\n\n")
|
||||
f.write("namespace manifold_docstrings {\n")
|
||||
for key, doc in comments.items():
|
||||
f.write(f'const char* {key} = R"___({doc.strip()})___";\n')
|
||||
f.write("} // namespace manifold_docs")
|
||||
@@ -1,13 +0,0 @@
|
||||
manifold3d.__prefix__:
|
||||
import numpy as np
|
||||
from typing import Literal, TypeVar, Union, Any
|
||||
|
||||
N = TypeVar('N', bound=int)
|
||||
DoubleNx2 = np.ndarray[tuple[N, Literal[2]], np.dtype[np.double]]
|
||||
Doublex2 = Union[np.ndarray[tuple[Literal[2]], np.dtype[np.double]], tuple[float, float], list[float]]
|
||||
Doublex3 = Union[np.ndarray[tuple[Literal[3]], np.dtype[np.double]], tuple[float, float, float], list[float]]
|
||||
Double2x3 = np.ndarray[tuple[Literal[2], Literal[3]], np.dtype[np.double]]
|
||||
Double3x4 = np.ndarray[tuple[Literal[3], Literal[4]], np.dtype[np.double]]
|
||||
DoubleNx3 = np.ndarray[tuple[N, Literal[3]], np.dtype[np.double]]
|
||||
Intx3 = Union[np.ndarray[tuple[Literal[3]], np.dtype[np.integer]], tuple[int, int, int], list[int]]
|
||||
IntNx3 = np.ndarray[tuple[N, Literal[3]], np.dtype[np.integer]]
|
||||
@@ -1,13 +0,0 @@
|
||||
import re, sys;
|
||||
|
||||
with open(sys.argv[1], 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
content = re.sub(r"Annotated\[ArrayLike, dict\(dtype='([^']*)'.*\)\]", r"np.ndarray[Any, np.dtype[np.\1]]", content)
|
||||
|
||||
# Remove ArrayLike and Annotated import
|
||||
content = re.sub(r"\s*from numpy.typing import ArrayLike\s", r"", content)
|
||||
content = re.sub(r"from typing import Annotated, overload", r"from typing import overload", content)
|
||||
|
||||
with open(sys.argv[1], 'w') as f:
|
||||
f.write(content)
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(nanobind)
|
||||
@@ -1,16 +0,0 @@
|
||||
/manifold.js
|
||||
/manifold.wasm
|
||||
/manifold-3d-*.tgz
|
||||
/manifold.d.ts
|
||||
/lib/*.js
|
||||
/lib/*.js.map
|
||||
/lib/*.d.ts
|
||||
/lib/*.d.ts.map
|
||||
/test/*.js
|
||||
/test/*.js.map
|
||||
/test/*.d.ts
|
||||
/test/*.d.ts.map
|
||||
/test/results
|
||||
/coverage
|
||||
/public
|
||||
dist
|
||||
@@ -12,43 +12,35 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
project(wasm)
|
||||
|
||||
add_executable(manifoldjs bindings.cpp)
|
||||
|
||||
set_source_files_properties(
|
||||
bindings.cpp
|
||||
PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings.js
|
||||
)
|
||||
target_link_libraries(
|
||||
manifoldjs
|
||||
PRIVATE manifold $<$<BOOL:${MANIFOLD_PAR}>:TBB::tbb>
|
||||
)
|
||||
set_source_files_properties(bindings.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings.js)
|
||||
target_link_libraries(manifoldjs manifold sdf cross_section polygon)
|
||||
target_compile_options(manifoldjs PRIVATE ${MANIFOLD_FLAGS})
|
||||
target_link_options(
|
||||
manifoldjs
|
||||
PUBLIC
|
||||
--pre-js
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bindings.js
|
||||
--bind
|
||||
-sALLOW_TABLE_GROWTH=1
|
||||
-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction
|
||||
-sMODULARIZE=1
|
||||
-sEXPORT_ES6=1
|
||||
)
|
||||
target_link_options(manifoldjs PUBLIC --pre-js ${CMAKE_CURRENT_SOURCE_DIR}/bindings.js --bind -sALLOW_TABLE_GROWTH=1
|
||||
-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction -sMODULARIZE=1 -sEXPORT_ES6=1)
|
||||
|
||||
target_compile_features(manifoldjs PUBLIC cxx_std_17)
|
||||
set_target_properties(manifoldjs PROPERTIES OUTPUT_NAME "manifold")
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/examples/built)
|
||||
|
||||
# ensure that interface files are copied over when modified
|
||||
add_custom_target(
|
||||
js_deps
|
||||
ALL
|
||||
DEPENDS manifoldjs ${CMAKE_CURRENT_SOURCE_DIR}/manifold*.d.ts
|
||||
)
|
||||
add_custom_target(js_deps ALL
|
||||
DEPENDS manifoldjs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/manifold*.d.ts)
|
||||
|
||||
add_custom_command(
|
||||
TARGET js_deps
|
||||
POST_BUILD
|
||||
# copy WASM build back here for publishing to npm
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/manifold.*
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
TARGET js_deps POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifold.*
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/manifold*.d.ts
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/built/)
|
||||
|
||||
add_custom_command(
|
||||
TARGET js_deps POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/manifold*.d.ts
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/examples/public/)
|
||||
|
||||
@@ -1,46 +1,31 @@
|
||||
[](https://codecov.io/github/elalish/manifold)
|
||||
[](https://badge.fury.io/js/manifold-3d)
|
||||
[](https://twitter.com/intent/follow?screen_name=manifoldcad)
|
||||
|
||||
[**ManifoldCAD User Guide**](https://manifoldcad.org/docs/jsuser/) | [**JS/TS/WASM API**](https://manifoldcad.org/docs/jsapi/) | [**C++ Documentation**](https://manifoldcad.org/docs/html/classmanifold_1_1_manifold.html) | [**Algorithm Documentation**](https://github.com/elalish/manifold/wiki/Manifold-Library) | [**Blog Posts**](https://elalish.blogspot.com/search/label/Manifold)
|
||||
|
||||
<!-- #region IncludeInUserGuide -->
|
||||
|
||||
A [manifold mesh](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness) is a mesh that represents a solid object, and so is very important in manufacturing, CAD, structural analysis, etc. 'Manifold' implies that there are no gaps or tears, and that all faces are oriented outwards.
|
||||
|
||||
## Manifold Library
|
||||
|
||||
[ [Using Manifold](https://manifoldcad.org/docs/jsapi/documents/Using_Manifold.html) | [Examples](https://manifoldcad.org/docs/jsapi/documents/Manifold_Examples.html) | [Manifold WASM Developer Guide](https://manifoldcad.org/docs/jsapi/) ]
|
||||
|
||||
[Manifold](https://github.com/elalish/manifold) is a geometry library dedicated to creating and operating on manifold triangle meshes. It is available as a WASM module that runs in any modern browser.
|
||||
|
||||
Our primary goal is reliability: guaranteed manifold output without caveats or edge cases. Our secondary goal is performance: efficient algorithms that make extensive use of parallelization, or pipelining when only a single thread is available.
|
||||
|
||||
## [ManifoldCAD.org](https://manifoldcad.org)
|
||||
If you like OpenSCAD / OpenJSCAD, you might also like ManifoldCAD - our own solid modelling web app based on this package.
|
||||
|
||||
<img alt="A metallic Menger sponge" src="https://manifoldcad.org/samples/models/mengerSponge192.png" style="float:right">
|
||||

|
||||
|
||||
[ [ManifoldCAD](https://manifoldcad.org) | [ManifoldCAD User Guide](https://manifoldcad.org/docs/jsuser/) ]
|
||||
# Manifold
|
||||
|
||||
If you like OpenSCAD / JSCAD, you might also like ManifoldCAD - our own solid modelling web app where you script in JS/TS and save a GLB or 3MF file. It contains several examples showing how to use our API to make interesting shapes. You may notice that some of these examples bare a certain resemblance to my OpenSCAD designs on [Thingiverse](https://www.thingiverse.com/emmett), which is no accident. Much as I love OpenSCAD, this library is dramatically faster and the code is more flexible.
|
||||
[**High-level Documentation**](https://elalish.blogspot.com/search/label/Manifold) | [**API Documentation**](https://elalish.github.io/manifold/docs/html/modules.html) | [**Algorithm Documentation**](https://github.com/elalish/manifold/wiki/Manifold-Library) | [**Web Examples**](https://elalish.github.io/manifold/model-viewer.html)
|
||||
|
||||
manifoldCAD = manifold + [TypeScript](https://www.typescriptlang.org/) + [glTF](https://www.khronos.org/gltf/)
|
||||
[Manifold](https://github.com/elalish/manifold) is a geometry library dedicated to creating and operating on manifold triangle meshes. A [manifold mesh](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness) is a mesh that represents a solid object, and so is very important in manufacturing, CAD, structural analysis, etc. Further information can be found on the [wiki](https://github.com/elalish/manifold/wiki/Manifold-Library).
|
||||
|
||||
<div style="clear:both"></div>
|
||||
## What's here
|
||||
|
||||
<!-- #endregion IncludeInUserGuide -->
|
||||
<!-- #region NoStlPlease -->
|
||||
This library is fast with guaranteed manifold output. As such you need manifold meshes as input, which this library can create using constructors inspired by the OpenSCAD API, as well as more advanced features like smoothing and signed-distance function (SDF) level sets. You can also pass in your own mesh data, but you'll get an error status if the imported mesh isn't manifold. Various automated repair tools exist online for fixing non manifold models, usually for 3D printing.
|
||||
|
||||
## 3D Formats
|
||||
The most significant contribution here is a guaranteed-manifold [mesh Boolean](https://github.com/elalish/manifold/wiki/Manifold-Library#mesh-boolean) algorithm, which I believe is the first of its kind. If you know of another, please open a discussion - a mesh Boolean algorithm robust to edge cases has been an open problem for many years. Likewise, if the Boolean here ever fails you, please submit an issue! This Boolean forms the basis of a CAD kernel, as it allows simple shapes to be combined into more complex ones.
|
||||
|
||||
[ [EXT_mesh_manifold](https://manifoldcad.org/docs/jsapi/modules/manifold-gltf.html) ]
|
||||
## Note on memory management
|
||||
|
||||
Please avoid saving to STL files! They are lossy and inefficient - when saving a manifold mesh to STL there is no guarantee that the re-imported mesh will still be manifold, as the topology is lost. Please consider using [3MF](https://3mf.io/) instead, as this format was designed from the beginning for manifold meshes representing solid objects.
|
||||
Since Manifold is a WASM module, it does not automatically garbage-collect like regular JavaScript. You must manually `delete()` each manifold object constructed by your scripts, see [discussion](https://github.com/elalish/manifold/discussions/256#discussioncomment-3944287).
|
||||
|
||||
If you use vertex properties for things like interpolated normals or texture UV coordinates, [glTF](https://www.khronos.org/Gltf) is recommended, specifically using the [`EXT_mesh_manifold`](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_mesh_manifold/README.md) extension. This allows for the lossless and efficient transmission of manifoldness even with property boundaries. Try our [make-manifold](https://manifoldcad.org/make-manifold) page to add this extension to your existing glTF/GLB models.
|
||||
## Examples
|
||||
|
||||
<!-- #endregion NoStlPlease -->
|
||||
Please see our usage [examples](https://github.com/elalish/manifold/tree/master/bindings/wasm/examples) to see how to interface this library with `three.js`, `<model-viewer>`, and `glTF`. Of particular note are the included libraries for lossless roundtrip of manifold meshes through glTF files, via a new extension: [EXT_manifold](https://github.com/KhronosGroup/glTF/pull/2286).
|
||||
|
||||
## About the author
|
||||
|
||||
This library was started by [Emmett Lalish](https://elalish.blogspot.com/), currently a senior rendering engineer at Wētā FX. This was my 20% project when I was a Google employee, though my day job was maintaining [\<model-viewer\>](https://modelviewer.dev/). I was the first employee at a 3D video startup, [Omnivor](https://www.omnivor.io/), and before that I worked on 3D printing at Microsoft, including [3D Builder](https://www.microsoft.com/en-us/p/3d-builder/9wzdncrfj3t6?activetab=pivot%3Aoverviewtab). Originally an aerospace engineer, I started at a small DARPA contractor doing seedling projects, one of which became [Sea Hunter](https://en.wikipedia.org/wiki/Sea_Hunter). I earned my doctorate from the University of Washington in control theory and published some [papers](https://www.researchgate.net/scientific-contributions/75011026_Emmett_Lalish).
|
||||
This library was started by [Emmett Lalish](https://elalish.blogspot.com/). I am currently a Google employee and this is my 20% project, not an official Google project. At my day job I'm the maintainer of [\<model-viewer\>](https://modelviewer.dev/). I was the first employee at a 3D video startup, [Omnivor](https://www.omnivor.io/), and before that I worked on 3D printing at Microsoft, including [3D Builder](https://www.microsoft.com/en-us/p/3d-builder/9wzdncrfj3t6?activetab=pivot%3Aoverviewtab). Originally an aerospace engineer, I started at a small DARPA contractor doing seedling projects, one of which became [Sea Hunter](https://en.wikipedia.org/wiki/Sea_Hunter). I earned my doctorate from the University of Washington in control theory and published some [papers](https://www.researchgate.net/scientific-contributions/75011026_Emmett_Lalish).
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./manifold-root.d.ts",
|
||||
"compiler": {
|
||||
"overrideTsconfig": {
|
||||
"include": [
|
||||
"./manifold-global-types.d.ts",
|
||||
"./manifold-encapsulated-types.d.ts",
|
||||
"./manifold-root.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./manifold.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-incompatible-release-tags": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-internal-missing-underscore": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"tsdoc-malformed-html-name": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-param-tag-missing-hyphen": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-right-brace": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-greater-than": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-malformed-inline-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-undefined-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-reference-missing-hash": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./types/manifoldCAD.d.ts",
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./dist/manifoldCAD.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-incompatible-release-tags": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-internal-missing-underscore": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"tsdoc-malformed-html-name": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-param-tag-missing-hyphen": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-right-brace": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-greater-than": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-malformed-inline-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-undefined-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-reference-missing-hash": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./types/manifoldCADGlobals.d.ts",
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./dist/manifoldCADGlobals.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-incompatible-release-tags": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-internal-missing-underscore": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"tsdoc-malformed-html-name": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-param-tag-missing-hyphen": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-right-brace": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-greater-than": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-malformed-inline-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-undefined-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-reference-missing-hash": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// Copyright 2025 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import {Command} from 'commander';
|
||||
|
||||
import {bundleFile} from '../lib/bundler.js';
|
||||
import * as worker from '../lib/worker.js';
|
||||
import * as exportModel from '../lib/export-model.js';
|
||||
|
||||
const program = new Command();
|
||||
program.name('manifold-cad')
|
||||
.description('Compile a ManifoldCAD program into a mesh.')
|
||||
.argument('<infile.[js|ts]>', 'Input file')
|
||||
.argument('<outfile>', 'Output file')
|
||||
.option('--jscdn <string>', 'Fetch npm packages from a CDN', 'jsDelivr')
|
||||
|
||||
// True means '--bundle' is implied by default, and '--no-bundle'
|
||||
// must be specified to disable bundling.
|
||||
.option('--no-bundle', 'Do not bundle input file before building model', true)
|
||||
.action(async(infile, outfile, options) => {
|
||||
// Choose an exporter. It will throw if we cannot match the extension.
|
||||
exportModel.supports(outfile, true);
|
||||
|
||||
// Build the model.
|
||||
let bundle = '';
|
||||
if (options.bundle) {
|
||||
bundle = await bundleFile(infile, {jsCDN: options.jscdn});
|
||||
} else {
|
||||
bundle = await readFile(infile, 'utf-8');
|
||||
}
|
||||
const doc = await worker.evaluate(bundle, {doNotBundle:true});
|
||||
|
||||
// Convert it and write it to disk.
|
||||
await exportModel.writeFile(outfile, doc);
|
||||
});
|
||||
|
||||
program.parseAsync(process.argv);
|
||||
@@ -17,55 +17,35 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cross_section.h"
|
||||
#include "helpers.cpp"
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/manifold.h"
|
||||
|
||||
#if (MANIFOLD_PAR == 1)
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
|
||||
void initTBB() {
|
||||
#if (MANIFOLD_PAR == 1)
|
||||
int num_threads = tbb::this_task_arena::max_concurrency();
|
||||
std::atomic<int> barrier{num_threads};
|
||||
tbb::parallel_for(
|
||||
0, num_threads,
|
||||
[&barrier](int) {
|
||||
barrier--;
|
||||
while (barrier > 0) {
|
||||
// Send browser thread to event loop
|
||||
std::this_thread::yield();
|
||||
}
|
||||
},
|
||||
tbb::static_partitioner{});
|
||||
#endif
|
||||
}
|
||||
#include "manifold.h"
|
||||
#include "polygon.h"
|
||||
#include "sdf.h"
|
||||
|
||||
using namespace emscripten;
|
||||
using namespace manifold;
|
||||
|
||||
EMSCRIPTEN_BINDINGS(whatever) {
|
||||
value_object<vec2>("vec2").field("x", &vec2::x).field("y", &vec2::y);
|
||||
value_object<glm::vec2>("vec2")
|
||||
.field("x", &glm::vec2::x)
|
||||
.field("y", &glm::vec2::y);
|
||||
|
||||
value_object<ivec3>("ivec3")
|
||||
.field("0", &ivec3::x)
|
||||
.field("1", &ivec3::y)
|
||||
.field("2", &ivec3::z);
|
||||
value_object<glm::ivec3>("ivec3")
|
||||
.field("0", &glm::ivec3::x)
|
||||
.field("1", &glm::ivec3::y)
|
||||
.field("2", &glm::ivec3::z);
|
||||
|
||||
value_object<vec3>("vec3")
|
||||
.field("x", &vec3::x)
|
||||
.field("y", &vec3::y)
|
||||
.field("z", &vec3::z);
|
||||
value_object<glm::vec3>("vec3")
|
||||
.field("x", &glm::vec3::x)
|
||||
.field("y", &glm::vec3::y)
|
||||
.field("z", &glm::vec3::z);
|
||||
|
||||
value_object<vec4>("vec4")
|
||||
.field("x", &vec4::x)
|
||||
.field("y", &vec4::y)
|
||||
.field("z", &vec4::z)
|
||||
.field("w", &vec4::w);
|
||||
value_object<glm::vec4>("vec4")
|
||||
.field("x", &glm::vec4::x)
|
||||
.field("y", &glm::vec4::y)
|
||||
.field("z", &glm::vec4::z)
|
||||
.field("w", &glm::vec4::w);
|
||||
|
||||
enum_<Manifold::Error>("status")
|
||||
.value("NoError", Manifold::Error::NoError)
|
||||
@@ -92,8 +72,7 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
enum_<CrossSection::JoinType>("jointype")
|
||||
.value("Square", CrossSection::JoinType::Square)
|
||||
.value("Round", CrossSection::JoinType::Round)
|
||||
.value("Miter", CrossSection::JoinType::Miter)
|
||||
.value("Bevel", CrossSection::JoinType::Bevel);
|
||||
.value("Miter", CrossSection::JoinType::Miter);
|
||||
|
||||
value_object<Rect>("rect").field("min", &Rect::min).field("max", &Rect::max);
|
||||
value_object<Box>("box").field("min", &Box::min).field("max", &Box::max);
|
||||
@@ -102,15 +81,19 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
.field("halfedge", &Smoothness::halfedge)
|
||||
.field("smoothness", &Smoothness::smoothness);
|
||||
|
||||
register_vector<ivec3>("Vector_ivec3");
|
||||
register_vector<vec3>("Vector_vec3");
|
||||
register_vector<vec2>("Vector_vec2");
|
||||
register_vector<std::vector<vec2>>("Vector2_vec2");
|
||||
register_vector<double>("Vector_f64");
|
||||
value_object<Properties>("properties")
|
||||
.field("surfaceArea", &Properties::surfaceArea)
|
||||
.field("volume", &Properties::volume);
|
||||
|
||||
register_vector<glm::ivec3>("Vector_ivec3");
|
||||
register_vector<glm::vec3>("Vector_vec3");
|
||||
register_vector<glm::vec2>("Vector_vec2");
|
||||
register_vector<std::vector<glm::vec2>>("Vector2_vec2");
|
||||
register_vector<float>("Vector_f32");
|
||||
register_vector<CrossSection>("Vector_crossSection");
|
||||
register_vector<Manifold>("Vector_manifold");
|
||||
register_vector<Smoothness>("Vector_smoothness");
|
||||
register_vector<vec4>("Vector_vec4");
|
||||
register_vector<glm::vec4>("Vector_vec4");
|
||||
|
||||
class_<CrossSection>("CrossSection")
|
||||
.constructor(&cross_js::OfPolygons)
|
||||
@@ -129,7 +112,7 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
.function("numVert", &CrossSection::NumVert)
|
||||
.function("numContour", &CrossSection::NumContour)
|
||||
.function("_Bounds", &CrossSection::Bounds)
|
||||
.function("_Simplify", &CrossSection::Simplify)
|
||||
.function("simplify", &CrossSection::Simplify)
|
||||
.function("_Offset", &cross_js::Offset)
|
||||
.function("_ToPolygons", &CrossSection::ToPolygons)
|
||||
.function("hull",
|
||||
@@ -143,9 +126,9 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
function("_crossSectionDifferenceN", &cross_js::DifferenceN);
|
||||
function("_crossSectionIntersectionN", &cross_js::IntersectionN);
|
||||
function("_crossSectionCollectVertices", &cross_js::CollectVertices);
|
||||
function(
|
||||
"_crossSectionHullPoints",
|
||||
select_overload<CrossSection(std::vector<vec2>)>(&CrossSection::Hull));
|
||||
function("_crossSectionHullPoints",
|
||||
select_overload<CrossSection(std::vector<glm::vec2>)>(
|
||||
&CrossSection::Hull));
|
||||
|
||||
class_<Manifold>("Manifold")
|
||||
.constructor(&man_js::FromMeshJS)
|
||||
@@ -155,17 +138,9 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
.function("_Split", &man_js::Split)
|
||||
.function("_SplitByPlane", &man_js::SplitByPlane)
|
||||
.function("_TrimByPlane", &Manifold::TrimByPlane)
|
||||
.function("minkowskiSum", &Manifold::MinkowskiSum)
|
||||
.function("minkowskiDifference", &Manifold::MinkowskiDifference)
|
||||
.function("_Slice", &Manifold::Slice)
|
||||
.function("_Project", &Manifold::Project)
|
||||
.function("hull", select_overload<Manifold() const>(&Manifold::Hull))
|
||||
.function("_GetMeshJS", &js::GetMeshJS)
|
||||
.function("refine", &Manifold::Refine)
|
||||
.function("refineToLength", &Manifold::RefineToLength)
|
||||
.function("refineToTolerance", &Manifold::RefineToTolerance)
|
||||
.function("smoothByNormals", &Manifold::SmoothByNormals)
|
||||
.function("_SmoothOut", &Manifold::SmoothOut)
|
||||
.function("_Warp", &man_js::Warp)
|
||||
.function("_SetProperties", &man_js::SetProperties)
|
||||
.function("transform", &man_js::Transform)
|
||||
@@ -176,22 +151,17 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
.function("_Decompose", select_overload<std::vector<Manifold>() const>(
|
||||
&Manifold::Decompose))
|
||||
.function("isEmpty", &Manifold::IsEmpty)
|
||||
.function("status", &man_js::Status)
|
||||
.function("status", &Manifold::Status)
|
||||
.function("numVert", &Manifold::NumVert)
|
||||
.function("numEdge", &Manifold::NumEdge)
|
||||
.function("numTri", &Manifold::NumTri)
|
||||
.function("numProp", &Manifold::NumProp)
|
||||
.function("numPropVert", &Manifold::NumPropVert)
|
||||
.function("_boundingBox", &Manifold::BoundingBox)
|
||||
.function("tolerance", &Manifold::GetTolerance)
|
||||
.function("setTolerance", &Manifold::SetTolerance)
|
||||
.function("_Simplify", &Manifold::Simplify)
|
||||
.function("precision", &Manifold::Precision)
|
||||
.function("genus", &Manifold::Genus)
|
||||
.function("volume", &Manifold::Volume)
|
||||
.function("surfaceArea", &Manifold::SurfaceArea)
|
||||
.function("minGap", &Manifold::MinGap)
|
||||
.function("getProperties", &Manifold::GetProperties)
|
||||
.function("calculateCurvature", &Manifold::CalculateCurvature)
|
||||
.function("_CalculateNormals", &Manifold::CalculateNormals)
|
||||
.function("originalID", &Manifold::OriginalID)
|
||||
.function("asOriginal", &Manifold::AsOriginal);
|
||||
|
||||
@@ -207,21 +177,18 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
function("_LevelSet", &man_js::LevelSet);
|
||||
function("_Merge", &js::Merge);
|
||||
function("_ReserveIDs", &Manifold::ReserveIDs);
|
||||
function("_manifoldCompose", &man_js::UnionN);
|
||||
function("_manifoldCompose", &Manifold::Compose);
|
||||
function("_manifoldUnionN", &man_js::UnionN);
|
||||
function("_manifoldDifferenceN", &man_js::DifferenceN);
|
||||
function("_manifoldIntersectionN", &man_js::IntersectionN);
|
||||
function("_manifoldCollectVertices", &man_js::CollectVertices);
|
||||
function(
|
||||
"_manifoldHullPoints",
|
||||
select_overload<Manifold(const std::vector<vec3>&)>(&Manifold::Hull));
|
||||
function("_manifoldHullPoints",
|
||||
select_overload<Manifold(const std::vector<glm::vec3>&)>(
|
||||
&Manifold::Hull));
|
||||
|
||||
// Quality Globals
|
||||
function("setMinCircularAngle", &Quality::SetMinCircularAngle);
|
||||
function("setMinCircularEdgeLength", &Quality::SetMinCircularEdgeLength);
|
||||
function("setCircularSegments", &Quality::SetCircularSegments);
|
||||
function("getCircularSegments", &Quality::GetCircularSegments);
|
||||
function("resetToCircularDefaults", &Quality::ResetToDefaults);
|
||||
|
||||
function("initTBB", &initTBB);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,6 @@ Module.setup = function() {
|
||||
if (_ManifoldInitialized) return;
|
||||
_ManifoldInitialized = true;
|
||||
|
||||
// warmup tbb for emscripten, according to
|
||||
// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
|
||||
Module.initTBB();
|
||||
// conversion utilities
|
||||
|
||||
function toVec(vec, list, f = x => x) {
|
||||
@@ -131,12 +128,12 @@ Module.setup = function() {
|
||||
|
||||
Module.CrossSection.prototype.warp = function(func) {
|
||||
const wasmFuncPtr = addFunction(function(vec2Ptr) {
|
||||
const x = getValue(vec2Ptr, 'double');
|
||||
const y = getValue(vec2Ptr + 8, 'double');
|
||||
const x = getValue(vec2Ptr, 'float');
|
||||
const y = getValue(vec2Ptr + 4, 'float');
|
||||
const vert = [x, y];
|
||||
func(vert);
|
||||
setValue(vec2Ptr, vert[0], 'double');
|
||||
setValue(vec2Ptr + 8, vert[1], 'double');
|
||||
setValue(vec2Ptr, vert[0], 'float');
|
||||
setValue(vec2Ptr + 4, vert[1], 'float');
|
||||
}, 'vi');
|
||||
const out = this._Warp(wasmFuncPtr);
|
||||
removeFunction(wasmFuncPtr);
|
||||
@@ -159,28 +156,23 @@ Module.setup = function() {
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.offset = function(
|
||||
delta, joinType = 'Round', miterLimit = 2.0, circularSegments = 0) {
|
||||
delta, joinType = 'Square', miterLimit = 2.0, circularSegments = 0) {
|
||||
return this._Offset(
|
||||
delta, joinTypeToInt(joinType), miterLimit, circularSegments);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.simplify = function(epsilon = 1e-6) {
|
||||
return this._Simplify(epsilon);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.extrude = function(
|
||||
height, nDivisions = 0, twistDegrees = 0.0, scaleTop = [1.0, 1.0],
|
||||
center = false) {
|
||||
scaleTop = vararg2vec2([scaleTop]);
|
||||
const man = Module._Extrude(
|
||||
this._ToPolygons(), height, nDivisions, twistDegrees, scaleTop);
|
||||
const man =
|
||||
Module._Extrude(this, height, nDivisions, twistDegrees, scaleTop);
|
||||
return (center ? man.translate([0., 0., -height / 2.]) : man);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.revolve = function(
|
||||
circularSegments = 0, revolveDegrees = 360.0) {
|
||||
return Module._Revolve(
|
||||
this._ToPolygons(), circularSegments, revolveDegrees);
|
||||
return Module._Revolve(this, circularSegments, revolveDegrees);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.add = function(other) {
|
||||
@@ -197,64 +189,54 @@ Module.setup = function() {
|
||||
|
||||
Module.CrossSection.prototype.toPolygons = function() {
|
||||
const vec = this._ToPolygons();
|
||||
const result = vec2polygons(vec, v => [v.x, v.y]);
|
||||
const result = vec2polygons(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
// Manifold methods
|
||||
|
||||
Module.Manifold.prototype.smoothOut = function(
|
||||
minSharpAngle = 60, minSmoothness = 0) {
|
||||
return this._SmoothOut(minSharpAngle, minSmoothness);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.warp = function(func) {
|
||||
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
||||
const x = getValue(vec3Ptr, 'double');
|
||||
const y = getValue(vec3Ptr + 8, 'double');
|
||||
const z = getValue(vec3Ptr + 16, 'double');
|
||||
const x = getValue(vec3Ptr, 'float');
|
||||
const y = getValue(vec3Ptr + 4, 'float');
|
||||
const z = getValue(vec3Ptr + 8, 'float');
|
||||
const vert = [x, y, z];
|
||||
func(vert);
|
||||
setValue(vec3Ptr, vert[0], 'double');
|
||||
setValue(vec3Ptr + 8, vert[1], 'double');
|
||||
setValue(vec3Ptr + 16, vert[2], 'double');
|
||||
setValue(vec3Ptr, vert[0], 'float');
|
||||
setValue(vec3Ptr + 4, vert[1], 'float');
|
||||
setValue(vec3Ptr + 8, vert[2], 'float');
|
||||
}, 'vi');
|
||||
const out = this._Warp(wasmFuncPtr);
|
||||
removeFunction(wasmFuncPtr);
|
||||
|
||||
const status = out.status();
|
||||
if (status !== 'NoError') {
|
||||
throw new Module.ManifoldError(status);
|
||||
if (status.value !== 0) {
|
||||
throw new Module.ManifoldError(status.value);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.calculateNormals = function(
|
||||
normalIdx, minSharpAngle = 60) {
|
||||
return this._CalculateNormals(normalIdx, minSharpAngle);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.setProperties = function(numProp, func) {
|
||||
const oldNumProp = this.numProp();
|
||||
const wasmFuncPtr = addFunction(function(newPtr, vec3Ptr, oldPtr) {
|
||||
const newProp = [];
|
||||
for (let i = 0; i < numProp; ++i) {
|
||||
newProp[i] = getValue(newPtr + 8 * i, 'double');
|
||||
newProp[i] = getValue(newPtr + 4 * i, 'float');
|
||||
}
|
||||
const pos = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
pos[i] = getValue(vec3Ptr + 8 * i, 'double');
|
||||
pos[i] = getValue(vec3Ptr + 4 * i, 'float');
|
||||
}
|
||||
const oldProp = [];
|
||||
for (let i = 0; i < oldNumProp; ++i) {
|
||||
oldProp[i] = getValue(oldPtr + 8 * i, 'double');
|
||||
oldProp[i] = getValue(oldPtr + 4 * i, 'float');
|
||||
}
|
||||
|
||||
func(newProp, pos, oldProp);
|
||||
|
||||
for (let i = 0; i < numProp; ++i) {
|
||||
setValue(newPtr + 8 * i, newProp[i], 'double');
|
||||
setValue(newPtr + 4 * i, newProp[i], 'float');
|
||||
}
|
||||
}, 'viii');
|
||||
const out = this._SetProperties(numProp, wasmFuncPtr);
|
||||
@@ -266,12 +248,8 @@ Module.setup = function() {
|
||||
return this._Translate(vararg2vec3(vec));
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.rotate = function(xOrVec, y, z) {
|
||||
if (Array.isArray(xOrVec)) {
|
||||
return this._Rotate(...xOrVec);
|
||||
} else {
|
||||
return this._Rotate(xOrVec, y || 0, z || 0);
|
||||
}
|
||||
Module.Manifold.prototype.rotate = function(vec) {
|
||||
return this._Rotate(...vec);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.scale = function(vec) {
|
||||
@@ -290,29 +268,15 @@ Module.setup = function() {
|
||||
return this._TrimByPlane(vararg2vec3([normal]), offset);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.slice = function(height = 0.) {
|
||||
const polygonsVec = this._Slice(height);
|
||||
const result = new CrossSectionCtor(polygonsVec, fillRuleToInt('Positive'));
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.project = function() {
|
||||
const polygonsVec = this._Project();
|
||||
const result = new CrossSectionCtor(polygonsVec, fillRuleToInt('Positive'));
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.split = function(manifold) {
|
||||
const vec = this._Split(manifold);
|
||||
const vec = this._split(manifold);
|
||||
const result = fromVec(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.splitByPlane = function(normal, offset = 0.) {
|
||||
const vec = this._SplitByPlane(vararg2vec3([normal]), offset);
|
||||
const vec = this._splitByPlane(vararg2vec3([normal]), offset);
|
||||
const result = fromVec(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
@@ -333,10 +297,6 @@ Module.setup = function() {
|
||||
};
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.simplify = function(tolerance = 0) {
|
||||
return this._Simplify(tolerance);
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
constructor({
|
||||
numProp = 3,
|
||||
@@ -348,8 +308,7 @@ Module.setup = function() {
|
||||
runOriginalID,
|
||||
faceID,
|
||||
halfedgeTangent,
|
||||
runTransform,
|
||||
tolerance = 0
|
||||
runTransform
|
||||
} = {}) {
|
||||
this.numProp = numProp;
|
||||
this.triVerts = triVerts;
|
||||
@@ -361,7 +320,6 @@ Module.setup = function() {
|
||||
this.faceID = faceID;
|
||||
this.halfedgeTangent = halfedgeTangent;
|
||||
this.runTransform = runTransform;
|
||||
this.tolerance = tolerance;
|
||||
}
|
||||
|
||||
get numTri() {
|
||||
@@ -387,13 +345,12 @@ Module.setup = function() {
|
||||
}
|
||||
|
||||
position(vert) {
|
||||
return this.vertProperties.subarray(
|
||||
this.numProp * vert, this.numProp * vert + 3);
|
||||
return this.vertProperties.subarray(numProp * vert, numProp * vert + 3);
|
||||
}
|
||||
|
||||
extras(vert) {
|
||||
return this.vertProperties.subarray(
|
||||
this.numProp * vert + 3, this.numProp * (vert + 1));
|
||||
numProp * vert + 3, numProp * (vert + 1));
|
||||
}
|
||||
|
||||
tangent(halfedge) {
|
||||
@@ -414,43 +371,45 @@ Module.setup = function() {
|
||||
|
||||
Module.Mesh = Mesh;
|
||||
|
||||
Module.Manifold.prototype.getMesh = function(normalIdx = -1) {
|
||||
Module.Manifold.prototype.getMesh = function(normalIdx = [0, 0, 0]) {
|
||||
if (normalIdx instanceof Array)
|
||||
normalIdx = {0: normalIdx[0], 1: normalIdx[1], 2: normalIdx[2]};
|
||||
return new Mesh(this._GetMeshJS(normalIdx));
|
||||
};
|
||||
|
||||
Module.ManifoldError = function ManifoldError(code, ...args) {
|
||||
let message = 'Unknown error';
|
||||
switch (code) {
|
||||
case 'NonFiniteVertex':
|
||||
case Module.status.NonFiniteVertex.value:
|
||||
message = 'Non-finite vertex';
|
||||
break;
|
||||
case 'NotManifold':
|
||||
case Module.status.NotManifold.value:
|
||||
message = 'Not manifold';
|
||||
break;
|
||||
case 'VertexOutOfBounds':
|
||||
case Module.status.VertexOutOfBounds.value:
|
||||
message = 'Vertex index out of bounds';
|
||||
break;
|
||||
case 'PropertiesWrongLength':
|
||||
case Module.status.PropertiesWrongLength.value:
|
||||
message = 'Properties have wrong length';
|
||||
break;
|
||||
case 'MissingPositionProperties':
|
||||
case Module.status.MissingPositionProperties.value:
|
||||
message = 'Less than three properties';
|
||||
break;
|
||||
case 'MergeVectorsDifferentLengths':
|
||||
case Module.status.MergeVectorsDifferentLengths.value:
|
||||
message = 'Merge vectors have different lengths';
|
||||
break;
|
||||
case 'MergeIndexOutOfBounds':
|
||||
case Module.status.MergeIndexOutOfBounds.value:
|
||||
message = 'Merge index out of bounds';
|
||||
break;
|
||||
case 'TransformWrongLength':
|
||||
case Module.status.TransformWrongLength.value:
|
||||
message = 'Transform vector has wrong length';
|
||||
break;
|
||||
case 'RunIndexWrongLength':
|
||||
case Module.status.RunIndexWrongLength.value:
|
||||
message = 'Run index vector has wrong length';
|
||||
break;
|
||||
case 'FaceIDWrongLength':
|
||||
case Module.status.FaceIDWrongLength.value:
|
||||
message = 'Face ID vector has wrong length';
|
||||
case 'InvalidConstruction':
|
||||
case Module.status.InvalidConstruction.value:
|
||||
message = 'Manifold constructed with invalid parameters';
|
||||
}
|
||||
|
||||
@@ -560,8 +519,8 @@ Module.setup = function() {
|
||||
const manifold = new ManifoldCtor(mesh);
|
||||
|
||||
const status = manifold.status();
|
||||
if (status !== 'NoError') {
|
||||
throw new Module.ManifoldError(status);
|
||||
if (status.value !== 0) {
|
||||
throw new Module.ManifoldError(status.value);
|
||||
}
|
||||
|
||||
return manifold;
|
||||
@@ -627,6 +586,14 @@ Module.setup = function() {
|
||||
return Module._ReserveIDs(n);
|
||||
};
|
||||
|
||||
Module.Manifold.compose = function(manifolds) {
|
||||
const vec = new Module.Vector_manifold();
|
||||
toVec(vec, manifolds);
|
||||
const result = Module._manifoldCompose(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
function manifoldBatchbool(name) {
|
||||
return function(...args) {
|
||||
if (args.length == 1) args = args[0];
|
||||
@@ -639,28 +606,22 @@ Module.setup = function() {
|
||||
}
|
||||
|
||||
Module.Manifold.union = manifoldBatchbool('Union');
|
||||
// Aliasing compose to union.
|
||||
// Native compose has some issues, and is deprecated.
|
||||
Module.Manifold.compose = Module.Manifold.union;
|
||||
|
||||
Module.Manifold.difference = manifoldBatchbool('Difference');
|
||||
Module.Manifold.intersection = manifoldBatchbool('Intersection');
|
||||
|
||||
Module.Manifold.levelSet = function(
|
||||
sdf, bounds, edgeLength, level = 0, tolerance = -1) {
|
||||
Module.Manifold.levelSet = function(sdf, bounds, edgeLength, level = 0) {
|
||||
const bounds2 = {
|
||||
min: {x: bounds.min[0], y: bounds.min[1], z: bounds.min[2]},
|
||||
max: {x: bounds.max[0], y: bounds.max[1], z: bounds.max[2]},
|
||||
};
|
||||
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
||||
const x = getValue(vec3Ptr, 'double');
|
||||
const y = getValue(vec3Ptr + 8, 'double');
|
||||
const z = getValue(vec3Ptr + 16, 'double');
|
||||
const x = getValue(vec3Ptr, 'float');
|
||||
const y = getValue(vec3Ptr + 4, 'float');
|
||||
const z = getValue(vec3Ptr + 8, 'float');
|
||||
const vert = [x, y, z];
|
||||
return sdf(vert);
|
||||
}, 'di');
|
||||
const out =
|
||||
Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level, tolerance);
|
||||
}, 'fi');
|
||||
const out = Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level);
|
||||
removeFunction(wasmFuncPtr);
|
||||
return out;
|
||||
};
|
||||
@@ -704,11 +665,10 @@ Module.setup = function() {
|
||||
|
||||
// Top-level functions
|
||||
|
||||
Module.triangulate = function(polygons, epsilon = -1, allowConvex = true) {
|
||||
Module.triangulate = function(polygons, precision = -1) {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const result = fromVec(
|
||||
Module._Triangulate(polygonsVec, epsilon, allowConvex),
|
||||
(x) => [x[0], x[1], x[2]]);
|
||||
Module._Triangulate(polygonsVec, precision), (x) => [x[0], x[1], x[2]]);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Manifold Examples
|
||||
group: none
|
||||
---
|
||||
# Manifold Examples
|
||||
|
||||
## Interoperation with three.js
|
||||
|
||||
{@include ../examples/three/README.md#summary}
|
||||
|
||||
## <model-viewer>
|
||||
|
||||
{@include ../examples/model-viewer/README.md#summary}
|
||||
|
||||
## Manifoldness and EXT_mesh_manifold
|
||||
|
||||
{@include ../examples/make-manifold/README.md#summary}
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
title: Using Manifold
|
||||
group: none
|
||||
---
|
||||
# Using Manifold
|
||||
|
||||
## Installation
|
||||
|
||||
In your project root folder run:
|
||||
|
||||
```bash
|
||||
npm install --save manifold-3d
|
||||
```
|
||||
|
||||
To start using Manifold, import it and initialize it:
|
||||
|
||||
```js
|
||||
import Module from 'manifold-3d';
|
||||
|
||||
const wasm = await Module();
|
||||
wasm.setup();
|
||||
const { Manifold } = wasm;
|
||||
```
|
||||
|
||||
Intro example
|
||||
|
||||
```js
|
||||
const {cube, sphere} = Manifold;
|
||||
const box = cube([100, 100, 100], true);
|
||||
const ball = sphere(60, 100);
|
||||
const result = box.subtract(ball);
|
||||
```
|
||||
|
||||
## Memory Management
|
||||
|
||||
> [!WARNING]
|
||||
> Since Manifold is a WASM module, it does not automatically garbage-collect like regular JavaScript.
|
||||
|
||||
You must manually `delete()` each object constructed by your scripts (both `Manifold` and `CrossSection`).
|
||||
See [discussion](https://github.com/elalish/manifold/discussions/256#discussioncomment-3944287).
|
||||
|
||||
## Examples
|
||||
|
||||
See [Examples](./bindings-examples.md).
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
title: Contributing
|
||||
group: none
|
||||
category: none
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
To debug the WASM build directly in Chrome dev tools, simply build in debug mode:
|
||||
```
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=Debug .. && emmake make
|
||||
```
|
||||
and install the [DWARF](goo.gle/wasm-debugging-extension) Chrome extension.
|
||||
|
||||
When testing [ManifoldCAD.org](https://manifoldcad.org/) (either locally or the
|
||||
deployed version) note that it uses a service worker for faster loading. This
|
||||
means you need to open the page twice to see updates (the first time loads the
|
||||
old version and caches the new one, the second time loads the new version from
|
||||
cache). To see changes on each reload, open Chrome dev tools, go to the
|
||||
Application tab and check "update on reload".
|
||||
|
||||
## Documentation
|
||||
|
||||
This guide and the ManifoldCAD User Guide are built using [TypeDoc](https://typedoc.org).
|
||||
|
||||
### Building Documentation
|
||||
|
||||
Documentation can be built using NPM:
|
||||
|
||||
```bash
|
||||
cd bindings/wasm
|
||||
npm install
|
||||
npm run build
|
||||
npm run docs
|
||||
```
|
||||
|
||||
After this, the User and Developer docs will be in `docs/jsuser` and `docs/jsapi` respectively.
|
||||
|
||||
### Writing for the User Guide
|
||||
|
||||
The ManifoldCAD User Guide is generated from the declaration files present in `types/`. These mostly re-export declarations, allowing the User Guide to inherit from the Developer Guide.
|
||||
|
||||
TypeDoc reflections (comments) flagged as `@internal` will _not_ be visible in the User Guide, but will be present in the Developer Guide. Reflections tagged as `@hidden` will not be visible in either guide.
|
||||
|
||||
### WASM Bindings
|
||||
|
||||
The manifold library itself is documented in three files:
|
||||
|
||||
* `manifold-global-types.d.ts`: Defines lower level types and interfaces like {@link manifold.Vec2 | Vec2}, {@link manifold.Mat3 | Mat3}, &c.
|
||||
* `manifold-encapsulated-types.d.ts`: Declares objects and types that are implemented in C++, and compiled to WASM. Definitions of classes like {@link manifold.Manifold | Manifold} and {@link manifold.CrossSection | CrossSection} can be found here.
|
||||
* `manifold-root.d.ts`: Defines the {@link manifold.ManifoldToplevel | ManifoldToplevel} interface, and re-exports the above two files.
|
||||
|
||||
These three files are consolidated into `manifold.d.ts` using [API Extractor](https://api-extractor.com/). This is automatically run before each build as an npm lifecycle hook. It can be run manually as `npm run prebuild`.
|
||||
|
||||
### Terminology
|
||||
|
||||
These are general guidelines, not hard rules. The aim of this section is to encourage consistent documentation, not to institute brand standards.
|
||||
|
||||
* When referring to the library, manifold should generally be lowercase. Manifold can be capitalized when grammatically correct, for example at the start of a sentence. If unclear, `manifold` can be marked up as code to distinguish it from the manifold property.
|
||||
* The Manifold class should generally be capitalized. Where appropriate, it can be linked directly to the {@link manifold.Manifold | Manifold} class documentation.
|
||||
* ManifoldCAD should be capitalized. [ManifoldCAD.org](https://manifoldcad.org) should be a link.
|
||||
@@ -1,6 +0,0 @@
|
||||
@layer typedoc {
|
||||
:root {
|
||||
--light-color-background-secondary: #c0c000;
|
||||
--dark-color-background-secondary: #606000;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
title: Embedding ManifoldCAD
|
||||
group: ManifoldCAD
|
||||
---
|
||||
# Embedding ManifoldCAD
|
||||
|
||||
There are three ways to use manifold in your own projects:
|
||||
|
||||
1. Using the ManifoldCAD worker. The worker adds overhead, but it will sandbox scripts. It also makes it possible to include packages at runtime.
|
||||
1. Using ManifoldCAD libraries directly. ManifoldCAD implements importers, exporters, and garbage collection amongst other utilities. These modules are reasonably light and can be used outside of manifoldCAD.
|
||||
1. Using the [Manifold WASM module directly](./bindings.md) without any of the above.
|
||||
|
||||
## Using the ManifoldCAD worker
|
||||
|
||||
The worker is a code evaluation sandbox intended to run as a [web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). It will bundle a script, include any dependencies, and even fetch those dependencies from a CDN if required. At run time, it will evaluate the script as a [dynamically created function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function).
|
||||
|
||||
See:
|
||||
|
||||
* {@link lib/worker | worker API}
|
||||
* [manifold-cad](https://github.com/elalish/manifold/blob/master/bindings/wasm/bin/manifold-cad) CLI
|
||||
|
||||
### Detecting the worker
|
||||
|
||||
> [!NOTE]
|
||||
> There are slight behaviour differences between scripts running inside the manifoldCAD worker, and those outside.
|
||||
>
|
||||
> See our [import integration tests](https://github.com/elalish/manifold/blob/master/bindings/wasm/test/import.test.ts) for examples.
|
||||
|
||||
When evaluated through the worker, {@link manifoldCAD!isManifoldCAD | isManifoldCAD()} will return true. Otherwise it returns false.
|
||||
|
||||
## Using the ManifoldCAD runtime directly
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
title: Tips and Tricks
|
||||
---
|
||||
## Handling Precision Issues
|
||||
|
||||
> [!WARNING]
|
||||
> Geometry where two manifolds are meant to share a face exactly is called **marginal geometry** and should generally be avoided. Manifold uses symbolic perturbation, which means multiple topologically valid results may be within rounding error of each other — so even small imprecisions can produce unexpected output.
|
||||
|
||||
Two common sources of trouble:
|
||||
|
||||
- **Floating point drift** — JavaScript uses IEEE 754 binary floating point, so `0.1 + 0.2` evaluates to `0.30000000000000004`. Manifold does not compensate for this in its transform pipeline. A bounding box maximum of `0.30000000000000004` instead of `0.3` is enough to prevent a union from closing properly.
|
||||
- **Different triangulations** — even with floating-point identical coordinates, if two coplanar faces are triangulated differently, that can also lead to gaps and slivers.
|
||||
|
||||
Where marginal geometry is unavoidable, the recommended workaround for floating point drift is to **work in larger, integer-friendly units**:
|
||||
|
||||
1. Scale coordinates up by a round factor (e.g., 1000) so your values are whole numbers
|
||||
2. Use `Math.round()` after any arithmetic to eliminate accumulated drift
|
||||
3. Perform all Manifold operations
|
||||
4. Scale the result back down if needed
|
||||
|
||||
```js
|
||||
const scale = 1000;
|
||||
|
||||
// Instead of cube([0.1, 0.1, 0.1]) translated by [0.2, 0.2, 0.2]:
|
||||
const size = Math.round(0.1 * scale); // 100
|
||||
const offset = Math.round(0.2 * scale); // 200
|
||||
|
||||
const { cube } = Manifold;
|
||||
const box = cube([size, size, size]).translate([offset, offset, offset]);
|
||||
```
|
||||
|
||||
See [discussion #1135](https://github.com/elalish/manifold/discussions/1135) for more context.
|
||||
|
||||
## Degrees vs. Radians
|
||||
|
||||
Manifold's rotation API takes **degrees**, not radians. This is a deliberate design choice: Manifold can eliminate floating point error entirely for multiples of 90°, and uses more efficient code paths when it detects such rotations.
|
||||
|
||||
```js
|
||||
// Pass degrees directly
|
||||
box.rotate([90, 0, 0]);
|
||||
box.rotate([0, 45, 0]);
|
||||
```
|
||||
|
||||
If your own code works in radians (e.g. when using `Math.atan2`), convert to degrees before passing to Manifold:
|
||||
|
||||
```js
|
||||
const toDeg = (rad) => rad * (180 / Math.PI);
|
||||
|
||||
const angle = Math.atan2(y, x); // radians
|
||||
box.rotate([0, 0, toDeg(angle)]);
|
||||
```
|
||||
|
||||
For common angles, always prefer the exact degree value (`90`, `45`, `30`, etc.) over a computed approximation to get the precision guarantee. See [issue #1262](https://github.com/elalish/manifold/issues/1262) for a real-world example where a rotation of `-89.999999999999` instead of `-90` caused mesh cracks.
|
||||
@@ -1,12 +0,0 @@
|
||||
@layer typedoc {
|
||||
:root {
|
||||
--light-color-background-secondary: #A288B3;
|
||||
--dark-color-background-secondary: #675772;
|
||||
}
|
||||
/* Hide right-hand navigation bar.
|
||||
* It is redundant for such a small site.
|
||||
*/
|
||||
.tsd-page-navigation {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: Using ManifoldCAD
|
||||
---
|
||||
## Running ManifoldCAD
|
||||
|
||||
### With a Browser
|
||||
|
||||
Just go to [ManifoldCAD.org](https://manifoldcad.org), and check out the examples!
|
||||
|
||||
### On the Command Line
|
||||
|
||||
ManifoldCAD has a [command line interface](https://github.com/elalish/manifold/blob/master/bindings/wasm/bin/manifold-cad). It can be run directly as `./bin/manifold-cad`. It can also be run via `npx`. If Manifold is not already present, use `npx manifold-3d manifold-cad` or `npx manifold-3d`. If Manifold is already installed, `npx manifold-cad` will suffice.
|
||||
|
||||
```
|
||||
Usage: manifold-cad [options] <infile.js> <outfile>
|
||||
```
|
||||
|
||||
The output file can be in either `.glb` or `.3mf` format, determined by extension.
|
||||
|
||||
## Coordinate Systems
|
||||
|
||||
ManifoldCAD uses a right-handed coordinate system where '+Z' is up. Length units are assumed to be millimetres. This is slightly at odds with glTF, which also uses a right handed coordinate system, but specifies '+Y' as up, and length units are in metres.
|
||||
|
||||
ManifoldCAD will scale and rotate automatically when importing or exporting glTF models. This means that an arrow pointing in the '+Z' direction in will point in the '+Y' direction after export. However, that arrow will still point up, and will still be the same size.
|
||||
@@ -1,14 +0,0 @@
|
||||
import * as esbuild from 'esbuild';
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints: ['lib/worker.ts'],
|
||||
bundle: true,
|
||||
outfile: 'dist/worker.bundled.js',
|
||||
format: 'esm',
|
||||
target: 'esnext',
|
||||
sourcemap: 'inline',
|
||||
sourcesContent: false,
|
||||
external: [
|
||||
'node:path', 'node:url', 'node:fs', 'node:module', 'module',
|
||||
]
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
manifold*
|
||||
@@ -0,0 +1,45 @@
|
||||
# WASM/JS Examples
|
||||
|
||||
This is the home of our editor, [ManifoldCAD.org](https://manifoldcad.org/), as well as our other public examples of integrations with `three.js`, `<model-viewer>`, and `glTF`. Included are `manifold-gltf.js` and `gltf-io.js`, which are intended to be fairly general-purpose libraries for interfacing between manifolds and glTF. We should probably make them into their own npm package at some point.
|
||||
|
||||
## Local development
|
||||
|
||||
First, follow the directions in the root README to get your C++ build environment set up and working for WASM. From this directory (`bindings/wasm/examples/`) you can test the JS bindings by running:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm test
|
||||
```
|
||||
|
||||
To develop the manifoldCAD.org editor as well as our other example pages, run
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
which will serve the pages, watch for changes, and automatically rebuild and refresh. This build step doesn't do TS type-checking, so to verify everything is correct (beyond VSCode's TS linting), run
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
See `package.json` for other useful scripts.
|
||||
|
||||
Note that the `emcmake` command automatically copies your WASM build into `built/`, (here, not just under the `buildWASM` directory) which is then packaged by Vite into `dist/assets/`.
|
||||
|
||||
To debug the WASM build directly in Chrome dev tools, simply build in debug mode:
|
||||
```
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=Debug .. && emmake make
|
||||
```
|
||||
and install the [DWARF](goo.gle/wasm-debugging-extension) Chrome extension.
|
||||
|
||||
When testing [ManifoldCAD.org](https://manifoldcad.org/) (either locally or the
|
||||
deployed version) note that it uses a service worker for faster loading. This
|
||||
means you need to open the page twice to see updates (the first time loads the
|
||||
old version and caches the new one, the second time loads the new version from
|
||||
cache). To see changes on each reload, open Chrome dev tools, go to the
|
||||
Application tab and check "update on reload".
|
||||
|
||||
### Note for firefox users
|
||||
|
||||
To use the manifoldCAD.org editor (`npm run dev`), you'll likely have to set
|
||||
`dom.workers.modules.enabled: true` in your `about:config`, as mentioned in the
|
||||
discussion of the
|
||||
[issue#328](https://github.com/elalish/manifold/issues/328#issuecomment-1473847102)
|
||||
of this repository.
|
||||
@@ -65,7 +65,7 @@ button:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.name {
|
||||
input {
|
||||
border: none;
|
||||
height: 36px;
|
||||
background-color: transparent;
|
||||
@@ -77,12 +77,12 @@ button:hover {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.name:focus {
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 18px;
|
||||
margin: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -96,9 +96,7 @@ model-viewer {
|
||||
#console {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
/* draggable pane dividers */
|
||||
max-height: none;
|
||||
/* draggable pane dividers-end */
|
||||
max-height: 25vh;
|
||||
overflow-y: scroll;
|
||||
background-color: #eee;
|
||||
white-space: pre-wrap;
|
||||
@@ -109,25 +107,11 @@ model-viewer {
|
||||
}
|
||||
|
||||
.page {
|
||||
/* draggable pane dividers */
|
||||
--left-pane: 50%;
|
||||
--viewer-pane: 75%;
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
/* draggable pane dividers */
|
||||
.workbench {
|
||||
min-height: 0;
|
||||
}
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
}
|
||||
@@ -136,45 +120,6 @@ model-viewer {
|
||||
position: relative;
|
||||
width: 50vw;
|
||||
}
|
||||
/* draggable pane dividers-start */
|
||||
.pane-left {
|
||||
width: auto;
|
||||
flex: 0 0 var(--left-pane);
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.pane-right {
|
||||
width: auto;
|
||||
flex: 1 1 auto;
|
||||
min-width: 220px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
flex: 0 0 var(--viewer-pane);
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
.splitter {
|
||||
flex: 0 0 8px;
|
||||
background: #d0d0d0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.splitter:hover {
|
||||
background: #bcbcbc;
|
||||
}
|
||||
|
||||
.splitter-x {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.splitter-y {
|
||||
cursor: row-resize;
|
||||
}
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
|
||||
.center {
|
||||
position: absolute;
|
||||
@@ -185,7 +130,7 @@ model-viewer {
|
||||
|
||||
.header .margin {
|
||||
font-size: 16px;
|
||||
margin: 18px 8px;
|
||||
margin: 1em 0.5em;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
@@ -223,29 +168,6 @@ model-viewer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top-edit {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 36px;
|
||||
top: 18px;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.top-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 36px;
|
||||
background-size: 24px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.squish {
|
||||
margin-right: -12px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
background-color: transparent;
|
||||
@@ -258,32 +180,6 @@ model-viewer {
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.undo {
|
||||
background-image: url(/icons/undo.png);
|
||||
}
|
||||
|
||||
.redo {
|
||||
background-image: url(/icons/redo.png);
|
||||
}
|
||||
|
||||
.star {
|
||||
background-image: url(/icons/star.png);
|
||||
}
|
||||
|
||||
.share {
|
||||
background-image: url(/icons/share.png);
|
||||
}
|
||||
|
||||
.docs {
|
||||
background-image: url(/icons/docs.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background-image: url(/icons/close.png);
|
||||
background-size: 20px;
|
||||
@@ -300,22 +196,10 @@ model-viewer {
|
||||
background-image: url(/icons/trash.png);
|
||||
}
|
||||
|
||||
.play {
|
||||
background-image: url(/icons/play.png);
|
||||
}
|
||||
|
||||
.pause {
|
||||
background-image: url(/icons/pause.png);
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.top-icon:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.blue {
|
||||
background-color: rgb(148, 221, 255);
|
||||
}
|
||||
@@ -337,70 +221,16 @@ model-viewer {
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
#animation {
|
||||
display: none;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
#play {
|
||||
position: relative;
|
||||
background-color: #ccc;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
margin-right: 16px;
|
||||
background: #ffffff55;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
background: #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
background: #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* draggable pane dividers */
|
||||
@media only screen and (max-width: 820px) {
|
||||
@media only screen and (max-width: 600px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
.splitter {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
.pane-left,
|
||||
.pane-right {
|
||||
min-width: 0;
|
||||
}
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
#file-margin {
|
||||
width: calc(100% - 32px);
|
||||
@@ -426,4 +256,4 @@ model-viewer {
|
||||
width: calc(50% - 16px);
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,454 @@
|
||||
// Copyright 2022 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import ManifoldWorker from './worker?worker';
|
||||
|
||||
// Loaded globally by examples.js
|
||||
const exampleFunctions = self.examples.functionBodies;
|
||||
|
||||
if (navigator.serviceWorker) {
|
||||
navigator.serviceWorker.register(
|
||||
'/service-worker.js', {scope: './index.html'});
|
||||
}
|
||||
|
||||
let editor = undefined;
|
||||
|
||||
// File UI ------------------------------------------------------------
|
||||
const fileButton = document.querySelector('#file');
|
||||
const currentFileElement = document.querySelector('#current');
|
||||
const fileArrow = document.querySelector('#file .uparrow');
|
||||
const fileDropdown = document.querySelector('#fileDropdown');
|
||||
const saveContainer = document.querySelector('#save');
|
||||
const saveDropdown = document.querySelector('#saveDropdown');
|
||||
const saveArrow = document.querySelector('#save .uparrow');
|
||||
|
||||
const hideDropdown = function() {
|
||||
fileDropdown.classList.remove('show');
|
||||
saveDropdown.classList.remove('show');
|
||||
fileArrow.classList.remove('down');
|
||||
saveArrow.classList.remove('down');
|
||||
};
|
||||
const toggleFileDropdown = function(event) {
|
||||
event.stopPropagation();
|
||||
fileDropdown.classList.toggle('show');
|
||||
fileArrow.classList.toggle('down');
|
||||
};
|
||||
const toggleSaveDropdown = function(event) {
|
||||
event.stopPropagation();
|
||||
saveDropdown.classList.toggle('show');
|
||||
saveArrow.classList.toggle('down');
|
||||
};
|
||||
fileButton.onclick = toggleFileDropdown;
|
||||
saveArrow.parentElement.onclick = toggleSaveDropdown;
|
||||
document.body.onclick = hideDropdown;
|
||||
|
||||
const prefix = 'ManifoldCAD';
|
||||
function getScript(name) {
|
||||
return window.localStorage.getItem(prefix + name);
|
||||
}
|
||||
function setScript(name, code) {
|
||||
window.localStorage.setItem(prefix + name, code);
|
||||
}
|
||||
function removeScript(name) {
|
||||
window.localStorage.removeItem(prefix + name);
|
||||
}
|
||||
function nthKey(n) {
|
||||
if (n >= window.localStorage.length) return;
|
||||
const key = window.localStorage.key(n);
|
||||
if (key.startsWith(prefix)) {
|
||||
return key.slice(prefix.length);
|
||||
}
|
||||
}
|
||||
|
||||
function saveCurrent() {
|
||||
if (editor) {
|
||||
const currentName = currentFileElement.textContent;
|
||||
if (!exampleFunctions.get(currentName)) {
|
||||
setScript(currentName, editor.getValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onpagehide = saveCurrent;
|
||||
window.beforeunload = saveCurrent;
|
||||
|
||||
let switching = false;
|
||||
let isExample = true;
|
||||
function switchTo(scriptName) {
|
||||
if (editor) {
|
||||
switching = true;
|
||||
currentFileElement.textContent = scriptName;
|
||||
setScript('currentName', scriptName);
|
||||
const code =
|
||||
exampleFunctions.get(scriptName) ?? getScript(scriptName) ?? '';
|
||||
isExample = exampleFunctions.get(scriptName) != null;
|
||||
editor.setValue(code);
|
||||
}
|
||||
}
|
||||
|
||||
function createDropdownItem(name) {
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('item');
|
||||
const button = document.createElement('button');
|
||||
container.appendChild(button);
|
||||
button.type = 'button';
|
||||
button.classList.add('blue', 'item');
|
||||
const label = document.createElement('span');
|
||||
button.appendChild(label);
|
||||
label.textContent = name;
|
||||
|
||||
button.onclick = function() {
|
||||
saveCurrent();
|
||||
switchTo(label.textContent);
|
||||
};
|
||||
// Stop text input spaces from triggering the button
|
||||
button.onkeyup = function(event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
||||
function addIcon(button) {
|
||||
const icon = document.createElement('button');
|
||||
icon.classList.add('icon');
|
||||
button.parentElement.appendChild(icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
function uniqueName(name) {
|
||||
let num = 1;
|
||||
let newName = name;
|
||||
while (getScript(newName) != null || exampleFunctions.get(newName) != null) {
|
||||
newName = name + ' ' + num++;
|
||||
}
|
||||
return newName;
|
||||
}
|
||||
|
||||
function addEdit(button) {
|
||||
const label = button.firstChild;
|
||||
const edit = addIcon(button);
|
||||
edit.classList.add('edit');
|
||||
|
||||
edit.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
const oldName = label.textContent;
|
||||
const code = getScript(oldName);
|
||||
const form = document.createElement('form');
|
||||
const inputElement = document.createElement('input');
|
||||
inputElement.value = oldName;
|
||||
label.textContent = '';
|
||||
button.appendChild(form);
|
||||
form.appendChild(inputElement);
|
||||
inputElement.focus();
|
||||
inputElement.setSelectionRange(0, oldName.length);
|
||||
|
||||
function rename() {
|
||||
const input = inputElement.value;
|
||||
inputElement.blur();
|
||||
if (!input) return;
|
||||
const newName = uniqueName(input);
|
||||
label.textContent = newName;
|
||||
if (currentFileElement.textContent == oldName) {
|
||||
currentFileElement.textContent = newName;
|
||||
}
|
||||
removeScript(oldName);
|
||||
setScript(newName, code);
|
||||
}
|
||||
|
||||
form.onsubmit = rename;
|
||||
inputElement.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
inputElement.onblur = function() {
|
||||
button.removeChild(form);
|
||||
label.textContent = oldName;
|
||||
};
|
||||
};
|
||||
|
||||
const trash = addIcon(button);
|
||||
trash.classList.add('trash');
|
||||
let lastClick = 0;
|
||||
|
||||
trash.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
if (button.classList.contains('blue')) {
|
||||
lastClick = performance.now();
|
||||
button.classList.remove('blue');
|
||||
button.classList.add('red');
|
||||
document.body.addEventListener('click', function() {
|
||||
button.classList.add('blue');
|
||||
button.classList.remove('red');
|
||||
}, {once: true});
|
||||
} else if (performance.now() - lastClick > 500) {
|
||||
removeScript(label.textContent);
|
||||
if (currentFileElement.textContent == label.textContent) {
|
||||
switchTo('Intro');
|
||||
}
|
||||
const container = button.parentElement;
|
||||
container.parentElement.removeChild(container);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const newButton = document.querySelector('#new');
|
||||
function newItem(code) {
|
||||
const name = uniqueName('New Script');
|
||||
setScript(name, code);
|
||||
const nextButton = createDropdownItem(name);
|
||||
newButton.insertAdjacentElement('afterend', nextButton.parentElement);
|
||||
addEdit(nextButton);
|
||||
nextButton.click();
|
||||
};
|
||||
newButton.onclick = function() {
|
||||
newItem('');
|
||||
};
|
||||
|
||||
const runButton = document.querySelector('#compile');
|
||||
const poster = document.querySelector('#poster');
|
||||
let manifoldInitialized = false;
|
||||
let autoExecute = true;
|
||||
|
||||
function initializeRun() {
|
||||
runButton.disabled = false;
|
||||
if (autoExecute) {
|
||||
runButton.click();
|
||||
} else {
|
||||
poster.textContent = 'Auto-run disabled due to prior failure';
|
||||
}
|
||||
}
|
||||
|
||||
// Editor ------------------------------------------------------------
|
||||
let tsWorker = undefined;
|
||||
|
||||
async function getManifoldDTS() {
|
||||
const global = await fetch('/manifold-global-types.d.ts')
|
||||
.then(response => response.text());
|
||||
|
||||
const encapsulated = await fetch('/manifold-encapsulated-types.d.ts')
|
||||
.then(response => response.text());
|
||||
|
||||
return `
|
||||
${global.replaceAll('export', '')}
|
||||
${encapsulated.replace(/^import.*$/gm, '').replaceAll('export', 'declare')}
|
||||
declare interface ManifoldToplevel {
|
||||
CrossSection: typeof T.CrossSection;
|
||||
Manifold: typeof T.Manifold;
|
||||
Mesh: typeof T.Mesh;
|
||||
triangulate: typeof T.triangulate;
|
||||
setMinCircularAngle: typeof T.setMinCircularAngle;
|
||||
setMinCircularEdgeLength: typeof T.setMinCircularEdgeLength;
|
||||
setCircularSegments: typeof T.setCircularSegments;
|
||||
getCircularSegments: typeof T.getCircularSegments;
|
||||
setup: () => void;
|
||||
}
|
||||
declare const module: ManifoldToplevel;
|
||||
`;
|
||||
}
|
||||
|
||||
async function getEditorDTS() {
|
||||
const global = await fetch('/editor.d.ts').then(response => response.text());
|
||||
return `${global.replace(/^import.*$/gm, '')}`;
|
||||
}
|
||||
|
||||
require.config({
|
||||
paths:
|
||||
{vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs'}
|
||||
});
|
||||
require(['vs/editor/editor.main'], async function() {
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
await getManifoldDTS());
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
await getEditorDTS());
|
||||
editor = monaco.editor.create(
|
||||
document.getElementById('editor'),
|
||||
{language: 'typescript', automaticLayout: true});
|
||||
const w = await monaco.languages.typescript.getTypeScriptWorker();
|
||||
tsWorker = await w(editor.getModel().uri);
|
||||
editor.getModel().updateOptions({tabSize: 2});
|
||||
|
||||
for (const [name] of exampleFunctions) {
|
||||
const button = createDropdownItem(name);
|
||||
fileDropdown.appendChild(button.parentElement);
|
||||
}
|
||||
|
||||
let currentName = currentFileElement.textContent;
|
||||
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = nthKey(i);
|
||||
if (!key) continue;
|
||||
if (key === 'currentName') {
|
||||
currentName = getScript(key);
|
||||
} else if (key === 'safe') {
|
||||
autoExecute = getScript(key) !== 'false';
|
||||
} else {
|
||||
const button = createDropdownItem(key);
|
||||
newButton.insertAdjacentElement('afterend', button.parentElement);
|
||||
addEdit(button);
|
||||
}
|
||||
}
|
||||
switchTo(currentName);
|
||||
|
||||
if (manifoldInitialized) {
|
||||
initializeRun();
|
||||
}
|
||||
|
||||
editor.onDidChangeModelContent(e => {
|
||||
runButton.disabled = false;
|
||||
if (switching) {
|
||||
switching = false;
|
||||
return;
|
||||
}
|
||||
if (isExample) {
|
||||
const cursor = editor.getPosition();
|
||||
newItem(editor.getValue());
|
||||
editor.setPosition(cursor);
|
||||
}
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
editor.layout({});
|
||||
};
|
||||
});
|
||||
|
||||
// Execution ------------------------------------------------------------
|
||||
const consoleElement = document.querySelector('#console');
|
||||
const oldLog = console.log;
|
||||
console.log = function(message) {
|
||||
consoleElement.textContent += message + '\r\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
oldLog(message);
|
||||
};
|
||||
|
||||
function clearConsole() {
|
||||
consoleElement.textContent = '';
|
||||
}
|
||||
|
||||
function enableCancel() {
|
||||
runButton.firstChild.style.visibility = 'hidden';
|
||||
runButton.classList.add('red', 'cancel');
|
||||
}
|
||||
|
||||
function disableCancel() {
|
||||
runButton.firstChild.style.visibility = 'visible';
|
||||
runButton.classList.remove('red', 'cancel');
|
||||
}
|
||||
|
||||
let t0 = performance.now();
|
||||
|
||||
function finishRun() {
|
||||
disableCancel();
|
||||
const t1 = performance.now();
|
||||
const log = consoleElement.textContent;
|
||||
// Remove "Running..."
|
||||
consoleElement.textContent = log.substring(log.indexOf('\n') + 1);
|
||||
console.log(
|
||||
`Took ${(Math.round((t1 - t0) / 10) / 100).toLocaleString()} seconds`);
|
||||
}
|
||||
|
||||
const mv = document.querySelector('model-viewer');
|
||||
const output = {
|
||||
glbURL: null,
|
||||
threeMFURL: null
|
||||
};
|
||||
let manifoldWorker = null;
|
||||
|
||||
function createWorker() {
|
||||
manifoldWorker = new ManifoldWorker();
|
||||
manifoldWorker.onmessage = function(e) {
|
||||
if (e.data == null) {
|
||||
if (tsWorker != null && !manifoldInitialized) {
|
||||
initializeRun();
|
||||
}
|
||||
manifoldInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.data.log != null) {
|
||||
consoleElement.textContent += e.data.log + '\r\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
return;
|
||||
}
|
||||
|
||||
finishRun();
|
||||
runButton.disabled = true;
|
||||
|
||||
if (output.threeMFURL != null) {
|
||||
URL.revokeObjectURL(output.threeMFURL);
|
||||
output.threeMFURL = null;
|
||||
}
|
||||
URL.revokeObjectURL(output.glbURL);
|
||||
output.glbURL = e.data.glbURL;
|
||||
output.threeMFURL = e.data.threeMFURL;
|
||||
threemfButton.disabled = output.threeMFURL == null;
|
||||
mv.src = output.glbURL;
|
||||
if (output.glbURL == null) {
|
||||
mv.showPoster();
|
||||
poster.textContent = 'Error';
|
||||
createWorker();
|
||||
} else {
|
||||
setScript('safe', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createWorker();
|
||||
|
||||
async function run() {
|
||||
saveCurrent();
|
||||
setScript('safe', 'false');
|
||||
enableCancel();
|
||||
clearConsole();
|
||||
console.log('Running...');
|
||||
const output = await tsWorker.getEmitOutput(editor.getModel().uri.toString());
|
||||
manifoldWorker.postMessage(output.outputFiles[0].text);
|
||||
t0 = performance.now();
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
manifoldWorker.terminate();
|
||||
createWorker();
|
||||
finishRun();
|
||||
console.log('Run canceled');
|
||||
}
|
||||
|
||||
runButton.onclick = function() {
|
||||
if (runButton.classList.contains('cancel')) {
|
||||
cancel();
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
};
|
||||
|
||||
function clickSave(saveButton, filename, outputName) {
|
||||
const container = saveButton.parentElement;
|
||||
return () => {
|
||||
const oldSave = saveContainer.firstElementChild;
|
||||
if (oldSave !== container) {
|
||||
saveDropdown.insertBefore(oldSave, saveDropdown.firstElementChild);
|
||||
saveContainer.insertBefore(container, saveDropdown);
|
||||
container.appendChild(saveArrow.parentElement);
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
link.download = filename;
|
||||
link.href = output[outputName];
|
||||
link.click();
|
||||
};
|
||||
}
|
||||
|
||||
const glbButton = document.querySelector('#glb');
|
||||
glbButton.onclick = clickSave(glbButton, 'manifold.glb', 'glbURL');
|
||||
|
||||
const threemfButton = document.querySelector('#threemf');
|
||||
threemfButton.onclick = clickSave(threemfButton, 'manifold.3mf', 'threeMFURL');
|
||||
@@ -1,14 +0,0 @@
|
||||
## Local development
|
||||
|
||||
First, follow the directions in the root README to get your C++ build environment set up and working for WASM.
|
||||
|
||||
To develop the manifoldCAD.org editor as well as our other example pages, run
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
which will serve the pages, watch for changes, and automatically rebuild and refresh. This build step doesn't do TS type-checking, so to verify everything is correct (beyond VSCode's TS linting), run
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
See `package.json` for other useful scripts.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import auger from '../../test/examples/auger.mjs?raw';
|
||||
import gearBearing from '../../test/examples/gear-bearing.ts?raw';
|
||||
import godelEscherBach from '../../test/examples/godel-escher-bach.ts?raw';
|
||||
import gyroidModule from '../../test/examples/gyroid-module.ts?raw';
|
||||
import heart from '../../test/examples/heart.mjs?raw';
|
||||
import importManifold from '../../test/examples/import-manifold.ts?raw';
|
||||
import importModel from '../../test/examples/import-model.ts?raw';
|
||||
import intro from '../../test/examples/intro.mjs?raw';
|
||||
import involuteGearLibrary from '../../test/examples/involute-gear-library.ts?raw';
|
||||
import mengerSponge from '../../test/examples/menger-sponge.mjs?raw';
|
||||
import roundedFrame from '../../test/examples/rounded-frame.mjs?raw';
|
||||
import scallop from '../../test/examples/scallop.mjs?raw';
|
||||
import stretchyBracelet from '../../test/examples/stretchy-bracelet.mjs?raw';
|
||||
import tetrahedronPuzzle from '../../test/examples/tetrahedron-puzzle.mjs?raw';
|
||||
import torusKnot from '../../test/examples/torus-knot.mjs?raw';
|
||||
import voronoi from '../../test/examples/voronoi.mjs?raw';
|
||||
|
||||
const examples = new Map();
|
||||
examples.set('Intro', intro);
|
||||
examples.set('Auger', auger);
|
||||
examples.set('Tetrahedron Puzzle', tetrahedronPuzzle);
|
||||
examples.set('Rounded Frame', roundedFrame);
|
||||
examples.set('Heart', heart);
|
||||
examples.set('Scallop', scallop);
|
||||
examples.set('Torus Knot', torusKnot);
|
||||
examples.set('Menger Sponge', mengerSponge);
|
||||
examples.set('Stretchy Bracelet', stretchyBracelet);
|
||||
examples.set('Gyroid Module', gyroidModule);
|
||||
examples.set('Gear Bearing', gearBearing);
|
||||
examples.set('involute-gear-library', involuteGearLibrary);
|
||||
examples.set('Voronoi', voronoi);
|
||||
examples.set('Import Model', importModel);
|
||||
examples.set('Import Manifold', importManifold);
|
||||
examples.set('Gödel, Escher, Bach', godelEscherBach)
|
||||
|
||||
if (typeof self !== 'undefined') {
|
||||
self.examples = examples;
|
||||
}
|
||||
@@ -1,861 +0,0 @@
|
||||
// Copyright 2022 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// '?url' is vite convention to reference a static asset.
|
||||
// vite will package the asset and provide a proper URL.
|
||||
import '@google/model-viewer';
|
||||
|
||||
import esbuildWasmUrl from 'esbuild-wasm/esbuild.wasm?url';
|
||||
import ManifoldWorker from 'manifold-3d/lib/worker.bundled.js?worker';
|
||||
import manifoldWasmUrl from 'manifold-3d/manifold.wasm?url';
|
||||
import {AutoTypings, JsDelivrSourceResolver, LocalStorageCache} from 'monaco-editor-auto-typings';
|
||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.main';
|
||||
// '?worker' is vite convention to load a module as a web worker.
|
||||
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
||||
|
||||
const CODE_START = '<code>';
|
||||
// Loaded globally by examples.js
|
||||
const exampleFunctions = self.examples;
|
||||
|
||||
if (navigator.serviceWorker) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const disableServiceWorker = params.has('no-sw');
|
||||
|
||||
if (window.caches) {
|
||||
window.caches.keys().then(keys => {
|
||||
keys.filter(
|
||||
key => key.startsWith('manifoldCAD-cache-') &&
|
||||
key !== 'manifoldCAD-cache-v4')
|
||||
.forEach(key => window.caches.delete(key));
|
||||
});
|
||||
}
|
||||
|
||||
if (disableServiceWorker) {
|
||||
// Explicit escape hatch for debugging cache-related issues.
|
||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
registrations.forEach(registration => registration.unregister());
|
||||
});
|
||||
} else {
|
||||
// Resolve against the current page URL so production asset paths don't
|
||||
// redirect registration into /assets.
|
||||
const serviceWorkerUrl =
|
||||
new URL('./service-worker.js', window.location.href);
|
||||
navigator.serviceWorker
|
||||
.register(serviceWorkerUrl, {scope: './', updateViaCache: 'none'})
|
||||
.then(async registration => {
|
||||
await navigator.serviceWorker.ready;
|
||||
if (!navigator.serviceWorker.controller) {
|
||||
const key = 'manifoldcad-sw-controller-refresh';
|
||||
if (!window.sessionStorage.getItem(key)) {
|
||||
// One-time marker for this tab to avoid a reload loop.
|
||||
window.sessionStorage.setItem(key, '1');
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
window.sessionStorage.removeItem(
|
||||
'manifoldcad-sw-controller-refresh');
|
||||
}
|
||||
registration.update();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Service worker registration failed:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let editor = undefined;
|
||||
|
||||
// Pane resizing - draggable pane dividers ---------------------
|
||||
|
||||
const LEFT_PANE_MIN_PERCENT = 20;
|
||||
const LEFT_PANE_MAX_PERCENT = 80;
|
||||
const VIEWER_PANE_MIN_PERCENT = 35;
|
||||
const VIEWER_PANE_MAX_PERCENT = 90;
|
||||
|
||||
// Keep percentages within practical bounds so panes stay usable.
|
||||
function clampToRange(value, minValue, maxValue) {
|
||||
return Math.min(maxValue, Math.max(minValue, value));
|
||||
}
|
||||
|
||||
// Attach pointer-drag behavior to a splitter element.
|
||||
// The callback receives pointer-move events and applies the layout update.
|
||||
function attachSplitterDrag(splitterElement, handleDragMove) {
|
||||
if (!splitterElement) return;
|
||||
|
||||
splitterElement.addEventListener('pointerdown', pointerDownEvent => {
|
||||
const isMobileLayout = window.matchMedia('(max-width: 820px)').matches;
|
||||
if (isMobileLayout) return;
|
||||
|
||||
pointerDownEvent.preventDefault();
|
||||
splitterElement.setPointerCapture(pointerDownEvent.pointerId);
|
||||
|
||||
const onPointerMove = moveEvent => handleDragMove(moveEvent);
|
||||
const onPointerEnd = endEvent => {
|
||||
splitterElement.releasePointerCapture(endEvent.pointerId);
|
||||
splitterElement.removeEventListener('pointermove', onPointerMove);
|
||||
splitterElement.removeEventListener('pointerup', onPointerEnd);
|
||||
splitterElement.removeEventListener('pointercancel', onPointerEnd);
|
||||
};
|
||||
|
||||
splitterElement.addEventListener('pointermove', onPointerMove);
|
||||
splitterElement.addEventListener('pointerup', onPointerEnd);
|
||||
splitterElement.addEventListener('pointercancel', onPointerEnd);
|
||||
});
|
||||
}
|
||||
|
||||
function setupPaneSplitters() {
|
||||
const pageElement = document.querySelector('.page');
|
||||
const workbenchElement = document.getElementById('workbench');
|
||||
const rightPaneElement = document.getElementById('rightPane');
|
||||
const horizontalSplitterElement = document.getElementById('split-x');
|
||||
const verticalSplitterElement = document.getElementById('split-y');
|
||||
const leftPaneStorageKey = 'ManifoldCAD:leftPanePercent';
|
||||
const viewerPaneStorageKey = 'ManifoldCAD:viewerPanePercent';
|
||||
|
||||
if (!pageElement || !workbenchElement || !rightPaneElement) return;
|
||||
|
||||
// Restore saved pane percentages on refresh when they are valid numbers.
|
||||
const savedLeftPane = Number(window.localStorage.getItem(leftPaneStorageKey));
|
||||
if (Number.isFinite(savedLeftPane)) {
|
||||
const clampedLeftPanePercent = clampToRange(
|
||||
savedLeftPane, LEFT_PANE_MIN_PERCENT, LEFT_PANE_MAX_PERCENT);
|
||||
pageElement.style.setProperty('--left-pane', `${clampedLeftPanePercent}%`);
|
||||
}
|
||||
|
||||
const savedViewerPane =
|
||||
Number(window.localStorage.getItem(viewerPaneStorageKey));
|
||||
if (Number.isFinite(savedViewerPane)) {
|
||||
const clampedViewerPanePercent = clampToRange(
|
||||
savedViewerPane, VIEWER_PANE_MIN_PERCENT, VIEWER_PANE_MAX_PERCENT);
|
||||
pageElement.style.setProperty(
|
||||
'--viewer-pane', `${clampedViewerPanePercent}%`);
|
||||
}
|
||||
|
||||
attachSplitterDrag(horizontalSplitterElement, moveEvent => {
|
||||
// Convert pointer X position to a percentage of the full workbench width.
|
||||
const workbenchBounds = workbenchElement.getBoundingClientRect();
|
||||
const leftPanePercent =
|
||||
((moveEvent.clientX - workbenchBounds.left) / workbenchBounds.width) *
|
||||
100;
|
||||
const clampedLeftPanePercent = clampToRange(
|
||||
leftPanePercent, LEFT_PANE_MIN_PERCENT, LEFT_PANE_MAX_PERCENT);
|
||||
pageElement.style.setProperty('--left-pane', `${clampedLeftPanePercent}%`);
|
||||
window.localStorage.setItem(leftPaneStorageKey, clampedLeftPanePercent);
|
||||
|
||||
// Monaco reacts to container size changes, but an explicit layout keeps
|
||||
// drag updates immediate and smooth.
|
||||
editor?.layout({});
|
||||
});
|
||||
|
||||
attachSplitterDrag(verticalSplitterElement, moveEvent => {
|
||||
// Convert pointer Y position to a percentage of the right pane height.
|
||||
const rightPaneBounds = rightPaneElement.getBoundingClientRect();
|
||||
const viewerPanePercent =
|
||||
((moveEvent.clientY - rightPaneBounds.top) / rightPaneBounds.height) *
|
||||
100;
|
||||
const clampedViewerPanePercent = clampToRange(
|
||||
viewerPanePercent, VIEWER_PANE_MIN_PERCENT, VIEWER_PANE_MAX_PERCENT);
|
||||
pageElement.style.setProperty(
|
||||
'--viewer-pane', `${clampedViewerPanePercent}%`);
|
||||
window.localStorage.setItem(viewerPaneStorageKey, clampedViewerPanePercent);
|
||||
});
|
||||
}
|
||||
|
||||
setupPaneSplitters();
|
||||
|
||||
// Edit UI ------------------------------------------------------------
|
||||
|
||||
const undoButton = document.querySelector('#undo');
|
||||
const redoButton = document.querySelector('#redo');
|
||||
const formatButton = document.querySelector('#format');
|
||||
const shareButton = document.querySelector('#share');
|
||||
|
||||
undoButton.onclick = () => editor.trigger('ignored', 'undo');
|
||||
redoButton.onclick = () => editor.trigger('ignored', 'redo');
|
||||
formatButton.onclick = () =>
|
||||
editor.trigger('ignored', 'editor.action.formatDocument');
|
||||
shareButton.onclick = () => {
|
||||
const url = new URL(window.location.toString());
|
||||
url.hash =
|
||||
'#' +
|
||||
encodeURIComponent(
|
||||
currentFileElement.textContent + CODE_START + editor.getValue());
|
||||
navigator.clipboard.writeText(url.toString());
|
||||
console.log('Shareable link copied to clipboard!');
|
||||
console.log('Consider shortening this URL using tinyURL.com');
|
||||
};
|
||||
|
||||
// File UI ------------------------------------------------------------
|
||||
const fileButton = document.querySelector('#file');
|
||||
const currentFileElement = document.querySelector('#current');
|
||||
const fileArrow = document.querySelector('#file .uparrow');
|
||||
const fileDropdown = document.querySelector('#fileDropdown');
|
||||
const saveContainer = document.querySelector('#save');
|
||||
const saveDropdown = document.querySelector('#saveDropdown');
|
||||
const saveArrow = document.querySelector('#save .uparrow');
|
||||
|
||||
const hideDropdown = function() {
|
||||
fileDropdown.classList.remove('show');
|
||||
saveDropdown.classList.remove('show');
|
||||
fileArrow.classList.remove('down');
|
||||
saveArrow.classList.remove('down');
|
||||
};
|
||||
const toggleFileDropdown = function(event) {
|
||||
event.stopPropagation();
|
||||
fileDropdown.classList.toggle('show');
|
||||
fileArrow.classList.toggle('down');
|
||||
};
|
||||
const toggleSaveDropdown = function(event) {
|
||||
event.stopPropagation();
|
||||
saveDropdown.classList.toggle('show');
|
||||
saveArrow.classList.toggle('down');
|
||||
};
|
||||
fileButton.onclick = toggleFileDropdown;
|
||||
saveArrow.parentElement.onclick = toggleSaveDropdown;
|
||||
document.body.onclick = hideDropdown;
|
||||
|
||||
const prefix = 'ManifoldCAD';
|
||||
function getScript(name) {
|
||||
return window.localStorage.getItem(prefix + name);
|
||||
}
|
||||
function setScript(name, code) {
|
||||
window.localStorage.setItem(prefix + name, code);
|
||||
}
|
||||
function removeScript(name) {
|
||||
window.localStorage.removeItem(prefix + name);
|
||||
}
|
||||
function nthKey(n) {
|
||||
if (n >= window.localStorage.length) return;
|
||||
const key = window.localStorage.key(n);
|
||||
if (key.startsWith(prefix)) {
|
||||
return key.slice(prefix.length);
|
||||
}
|
||||
}
|
||||
|
||||
function getAllScripts() {
|
||||
const files = {};
|
||||
for (const [name, contents] of exampleFunctions) {
|
||||
files[name] = contents;
|
||||
}
|
||||
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = nthKey(i);
|
||||
if (!key || key === 'currentName' || key === 'safe') continue;
|
||||
files[key] = getScript(key)
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function getModelForScript(filename) {
|
||||
const uri = monaco.Uri.parse(`inmemory://model/${filename}.ts`);
|
||||
const model = monaco.editor.getModel(uri) ||
|
||||
monaco.editor.createModel('', 'typescript', uri);
|
||||
model.updateOptions({tabSize: 2});
|
||||
return model;
|
||||
}
|
||||
|
||||
function saveCurrent() {
|
||||
if (editor) {
|
||||
const currentName = currentFileElement.textContent;
|
||||
if (!exampleFunctions.get(currentName)) {
|
||||
setScript(currentName, editor.getValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onpagehide = saveCurrent;
|
||||
window.beforeunload = saveCurrent;
|
||||
|
||||
let switching = false;
|
||||
let isExample = true;
|
||||
function switchTo(scriptName) {
|
||||
if (editor) {
|
||||
switching = true;
|
||||
currentFileElement.textContent = scriptName;
|
||||
setScript('currentName', scriptName);
|
||||
isExample = exampleFunctions.get(scriptName) != null;
|
||||
const code = isExample ? exampleFunctions.get(scriptName) :
|
||||
getScript(scriptName) ?? '';
|
||||
window.location.hash = '#' + scriptName;
|
||||
const model = getModelForScript(scriptName);
|
||||
editor.setModel(model);
|
||||
|
||||
// Either editor.setValue() or model.setValue() will trigger
|
||||
// onDidChangeModelContent. This will cause some UI updates, but will also
|
||||
// get monaco-editor-auto-typings to update types.
|
||||
model.setValue(code);
|
||||
}
|
||||
}
|
||||
|
||||
function createDropdownItem(name) {
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('item');
|
||||
const button = document.createElement('button');
|
||||
container.appendChild(button);
|
||||
button.type = 'button';
|
||||
button.classList.add('blue', 'item');
|
||||
const label = document.createElement('span');
|
||||
button.appendChild(label);
|
||||
label.textContent = name;
|
||||
|
||||
button.onclick = function() {
|
||||
saveCurrent();
|
||||
switchTo(label.textContent);
|
||||
};
|
||||
// Stop text input spaces from triggering the button
|
||||
button.onkeyup = function(event) {
|
||||
event.preventDefault();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
||||
function addIcon(button) {
|
||||
const icon = document.createElement('button');
|
||||
icon.classList.add('icon');
|
||||
button.parentElement.appendChild(icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
function uniqueName(name) {
|
||||
let num = 1;
|
||||
let newName = name;
|
||||
while (getScript(newName) != null || exampleFunctions.get(newName) != null) {
|
||||
newName = name + ' ' + num++;
|
||||
}
|
||||
return newName;
|
||||
}
|
||||
|
||||
function addEdit(button) {
|
||||
const label = button.firstChild;
|
||||
const edit = addIcon(button);
|
||||
edit.classList.add('edit');
|
||||
|
||||
edit.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
const oldName = label.textContent;
|
||||
const code = getScript(oldName);
|
||||
const form = document.createElement('form');
|
||||
const inputElement = document.createElement('input');
|
||||
inputElement.classList.add('name');
|
||||
inputElement.value = oldName;
|
||||
label.textContent = '';
|
||||
button.appendChild(form);
|
||||
form.appendChild(inputElement);
|
||||
inputElement.focus();
|
||||
inputElement.setSelectionRange(0, oldName.length);
|
||||
|
||||
function rename() {
|
||||
const input = inputElement.value;
|
||||
inputElement.blur();
|
||||
if (!input) return;
|
||||
const newName = uniqueName(input);
|
||||
label.textContent = newName;
|
||||
if (currentFileElement.textContent == oldName) {
|
||||
currentFileElement.textContent = newName;
|
||||
}
|
||||
removeScript(oldName);
|
||||
setScript(newName, code);
|
||||
}
|
||||
|
||||
form.onsubmit = rename;
|
||||
inputElement.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
inputElement.onblur = function() {
|
||||
button.removeChild(form);
|
||||
label.textContent = oldName;
|
||||
};
|
||||
};
|
||||
|
||||
const trash = addIcon(button);
|
||||
trash.classList.add('trash');
|
||||
let lastClick = 0;
|
||||
|
||||
trash.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
if (button.classList.contains('blue')) {
|
||||
lastClick = performance.now();
|
||||
button.classList.remove('blue');
|
||||
button.classList.add('red');
|
||||
document.body.addEventListener('click', function() {
|
||||
button.classList.add('blue');
|
||||
button.classList.remove('red');
|
||||
}, {once: true});
|
||||
} else if (performance.now() - lastClick > 500) {
|
||||
removeScript(label.textContent);
|
||||
if (currentFileElement.textContent == label.textContent) {
|
||||
switchTo('Intro');
|
||||
}
|
||||
const container = button.parentElement;
|
||||
container.parentElement.removeChild(container);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const newButton = document.querySelector('#new');
|
||||
function newItem(code, scriptName = undefined) {
|
||||
const name = uniqueName(scriptName ?? 'New Script');
|
||||
setScript(name, code);
|
||||
const nextButton = createDropdownItem(name);
|
||||
newButton.insertAdjacentElement('afterend', nextButton.parentElement);
|
||||
addEdit(nextButton);
|
||||
return {button: nextButton, name};
|
||||
};
|
||||
newButton.onclick = function() {
|
||||
newItem('').button.click();
|
||||
};
|
||||
|
||||
const runButton = document.querySelector('#compile');
|
||||
const poster = document.querySelector('#poster');
|
||||
let manifoldInitialized = false;
|
||||
let autoExecute = true;
|
||||
|
||||
function initializeRun() {
|
||||
runButton.disabled = false;
|
||||
if (autoExecute) {
|
||||
runButton.click();
|
||||
} else {
|
||||
poster.textContent = 'Auto-run disabled';
|
||||
}
|
||||
}
|
||||
|
||||
// Editor ------------------------------------------------------------
|
||||
|
||||
async function createEditor() {
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: (_, label) => {
|
||||
if (label === 'typescript' || label === 'javascript') {
|
||||
return new tsWorker();
|
||||
} else {
|
||||
return new editorWorker();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
editor = monaco.editor.create(document.getElementById('editor'), {
|
||||
language: 'typescript',
|
||||
automaticLayout: true,
|
||||
minimap: {enabled: false},
|
||||
|
||||
|
||||
// make monaco editor to wrap the content,and hide horizontal
|
||||
// scrollbar----start----:
|
||||
|
||||
// make text wrap to the next line when it exceeds the width of the editor:
|
||||
wordWrap: 'on',
|
||||
|
||||
// remove horizontal scrollbar:
|
||||
scrollbar: {
|
||||
horizontal: 'hidden',
|
||||
},
|
||||
// make monaco editor to wrap the content,and hide horizontal
|
||||
// scrollbar----end-------.
|
||||
|
||||
|
||||
});
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
module: monaco.languages.typescript.ScriptTarget.ESNext,
|
||||
moduleResolution: monaco.languages.typescript.ScriptTarget.NodeNext,
|
||||
allowNonTsExtensions: true,
|
||||
});
|
||||
|
||||
// Make sure `manifold-3d/manifoldCAD` types are available for import.
|
||||
const manifoldCADTypesUrl =
|
||||
new URL('./manifoldCAD.d.ts', window.location.href);
|
||||
const manifoldCADGlobalsTypesUrl =
|
||||
new URL('./manifoldCADGlobals.d.ts', window.location.href);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
await (await fetch(manifoldCADTypesUrl)).text(),
|
||||
'inmemory://model/node_modules/manifold-3d/manifoldCAD.d.ts');
|
||||
|
||||
// Types in the global namespace for top-level scripts.
|
||||
// This could be improved in the future. API-Extractor intentionally doesn't
|
||||
// global variables, so another tool may be a better fit.
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
(await (await fetch(manifoldCADGlobalsTypesUrl)).text())
|
||||
.replace(/^export /gm, ''));
|
||||
|
||||
// Load up all scripts so that monaco can check types of multi-file models.
|
||||
for (const [filename, content] of Object.entries(getAllScripts())) {
|
||||
getModelForScript(filename).setValue(content);
|
||||
}
|
||||
|
||||
// Initialize auto typing on monaco editor.
|
||||
const typeIndicator = document.querySelector('#type-indicator');
|
||||
let typeIndicatorFrame = 0;
|
||||
let autoTypings = undefined;
|
||||
|
||||
const syncTypeIndicator = () => {
|
||||
if (!typeIndicator || !autoTypings) return;
|
||||
typeIndicator.textContent =
|
||||
autoTypings.isResolving ? 'Fetching types...' : '';
|
||||
typeIndicatorFrame =
|
||||
autoTypings.isResolving ? requestAnimationFrame(syncTypeIndicator) : 0;
|
||||
};
|
||||
|
||||
const showTypeIndicator = () => {
|
||||
if (!typeIndicator) return;
|
||||
typeIndicator.textContent = 'Fetching types...';
|
||||
if (autoTypings && typeIndicatorFrame === 0) {
|
||||
typeIndicatorFrame = requestAnimationFrame(syncTypeIndicator);
|
||||
}
|
||||
};
|
||||
|
||||
self.window.typecache = new LocalStorageCache();
|
||||
|
||||
// We inject manifold-3d typings locally above, and text-shaper publishes
|
||||
// broken declaration re-exports to non-existent source files. Avoid CDN
|
||||
// probes for those packages to keep refreshes quiet.
|
||||
// This skip list only affects Monaco auto-typing CDN lookups, not runtime
|
||||
// imports.
|
||||
const jsDelivrResolver = new JsDelivrSourceResolver();
|
||||
const skippedTypingPackages =
|
||||
new Set(['manifold-3d', 'text-shaper', '@types/require']);
|
||||
const shouldSkipTypingPackage = packageName => {
|
||||
return skippedTypingPackages.has(packageName);
|
||||
};
|
||||
const sourceResolver = {
|
||||
resolvePackageJson: async (packageName, version, subPath) => {
|
||||
if (shouldSkipTypingPackage(packageName)) return '';
|
||||
return jsDelivrResolver.resolvePackageJson(packageName, version, subPath);
|
||||
},
|
||||
resolveSourceFile: async (packageName, version, path) => {
|
||||
if (shouldSkipTypingPackage(packageName)) return '';
|
||||
return jsDelivrResolver.resolveSourceFile(packageName, version, path);
|
||||
}
|
||||
};
|
||||
|
||||
autoTypings = await AutoTypings.create(editor, {
|
||||
sourceResolver,
|
||||
sourceCache: self.window.typecache,
|
||||
// Conservative limits: resolve shallow imports while avoiding deep fetch
|
||||
// fan-out that adds noise and slows editor/offline workflows.
|
||||
packageRecursionDepth: 1,
|
||||
fileRecursionDepth: 2,
|
||||
onUpdate: update => {
|
||||
if (update.type === 'ResolveNewImports') {
|
||||
showTypeIndicator();
|
||||
}
|
||||
},
|
||||
onError: e => {
|
||||
if (String(e?.message ?? e).includes('Not implemented yet')) {
|
||||
return;
|
||||
}
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
if (typeIndicator?.textContent) {
|
||||
syncTypeIndicator();
|
||||
}
|
||||
for (const [name] of exampleFunctions) {
|
||||
const button = createDropdownItem(name);
|
||||
fileDropdown.appendChild(button.parentElement);
|
||||
}
|
||||
|
||||
let currentName = currentFileElement.textContent;
|
||||
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = nthKey(i);
|
||||
if (!key) continue;
|
||||
if (key === 'currentName') {
|
||||
currentName = getScript(key);
|
||||
} else if (key === 'safe') {
|
||||
autoExecute = getScript(key) !== 'false';
|
||||
} else {
|
||||
const button = createDropdownItem(key);
|
||||
newButton.insertAdjacentElement('afterend', button.parentElement);
|
||||
addEdit(button);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.location.hash.length > 0) {
|
||||
const fragment = decodeURIComponent(window.location.hash.substring(1));
|
||||
const codeIdx = fragment.indexOf(CODE_START);
|
||||
if (codeIdx != -1) {
|
||||
autoExecute = true;
|
||||
const name = fragment.substring(0, codeIdx);
|
||||
switchTo(
|
||||
newItem(fragment.substring(codeIdx + CODE_START.length), name).name);
|
||||
} else {
|
||||
if (fragment != currentName) {
|
||||
autoExecute = true;
|
||||
}
|
||||
switchTo(fragment);
|
||||
}
|
||||
} else {
|
||||
switchTo(currentName);
|
||||
}
|
||||
|
||||
if (manifoldInitialized) {
|
||||
initializeRun();
|
||||
}
|
||||
|
||||
editor.onDidChangeModelContent(e => {
|
||||
const activeName = currentFileElement.textContent;
|
||||
|
||||
// The user switched models.
|
||||
if (switching) {
|
||||
switching = false;
|
||||
editor.setScrollTop(0);
|
||||
runButton.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// monaco-editor-auto-typings loaded types. Do nothing.
|
||||
if (autoTypings.isResolving && e.changes.isFlush) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The user edited an example.
|
||||
// Copy it into a new script.
|
||||
if (isExample && exampleFunctions.get(activeName) != editor.getValue()) {
|
||||
const cursor = editor.getPosition();
|
||||
newItem(editor.getValue()).button.click();
|
||||
editor.setPosition(cursor);
|
||||
runButton.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// And if we're here, the user made an edit.
|
||||
runButton.disabled = false;
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
editor.layout({});
|
||||
};
|
||||
};
|
||||
|
||||
createEditor();
|
||||
|
||||
// Animation ------------------------------------------------------------
|
||||
const mv = document.querySelector('model-viewer');
|
||||
const animationContainer = document.querySelector('#animation');
|
||||
const playButton = document.querySelector('#play');
|
||||
const scrubber = document.querySelector('#scrubber');
|
||||
let paused = false;
|
||||
|
||||
mv.addEventListener('load', () => {
|
||||
const hasAnimation = mv.availableAnimations.length > 0;
|
||||
animationContainer.style.display = hasAnimation ? 'flex' : 'none';
|
||||
if (hasAnimation) {
|
||||
play();
|
||||
}
|
||||
});
|
||||
|
||||
function play() {
|
||||
mv.play();
|
||||
playButton.classList.remove('play');
|
||||
playButton.classList.add('pause');
|
||||
paused = false;
|
||||
scrubber.classList.add('hide');
|
||||
}
|
||||
|
||||
function pause() {
|
||||
mv.pause();
|
||||
playButton.classList.remove('pause');
|
||||
playButton.classList.add('play');
|
||||
paused = true;
|
||||
scrubber.max = mv.duration;
|
||||
scrubber.value = mv.currentTime;
|
||||
scrubber.classList.remove('hide');
|
||||
}
|
||||
|
||||
playButton.onclick = function() {
|
||||
if (paused) {
|
||||
play();
|
||||
} else {
|
||||
pause();
|
||||
}
|
||||
};
|
||||
|
||||
scrubber.oninput = function() {
|
||||
mv.currentTime = scrubber.value;
|
||||
};
|
||||
|
||||
// Execution ------------------------------------------------------------
|
||||
const consoleElement = document.querySelector('#console');
|
||||
const oldLog = console.log;
|
||||
console.log = function(message) {
|
||||
consoleElement.textContent += message + '\r\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
oldLog(message);
|
||||
};
|
||||
|
||||
function clearConsole() {
|
||||
consoleElement.textContent = '';
|
||||
}
|
||||
|
||||
function enableCancel() {
|
||||
runButton.firstChild.style.visibility = 'hidden';
|
||||
runButton.classList.add('red', 'cancel');
|
||||
}
|
||||
|
||||
function disableCancel() {
|
||||
runButton.firstChild.style.visibility = 'visible';
|
||||
runButton.classList.remove('red', 'cancel');
|
||||
}
|
||||
|
||||
function finishRun() {
|
||||
disableCancel();
|
||||
const log = consoleElement.textContent;
|
||||
// Remove "Running..."
|
||||
consoleElement.textContent = log.substring(log.indexOf('\n') + 1);
|
||||
}
|
||||
|
||||
const output = {
|
||||
glbURL: null,
|
||||
threeMFURL: null
|
||||
};
|
||||
let manifoldWorker = null;
|
||||
|
||||
function createWorker() {
|
||||
manifoldWorker = new ManifoldWorker();
|
||||
|
||||
manifoldWorker.onmessage = (e) => {
|
||||
const message = e.data;
|
||||
|
||||
if (message?.type === 'ready') {
|
||||
if (tsWorker != null && !manifoldInitialized) {
|
||||
initializeRun();
|
||||
}
|
||||
manifoldInitialized = true;
|
||||
|
||||
} else if (message?.type === 'error') {
|
||||
// Clean up.
|
||||
setScript('safe', 'false');
|
||||
finishRun();
|
||||
|
||||
// Show errors. If the stack trace makes more sense, show that.
|
||||
const errorText = `${message.name}: ${message.message}`;
|
||||
if (message.stack && message.stack.startsWith(errorText)) {
|
||||
consoleElement.textContent += message.stack + '\r\n';
|
||||
} else {
|
||||
consoleElement.textContent += errorText + '\r\n';
|
||||
}
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
mv.showPoster();
|
||||
poster.textContent = 'Error';
|
||||
|
||||
// Clear models.
|
||||
if (output.glbURL) URL.revokeObjectURL(output.glbURL);
|
||||
if (output.threeMFURL) URL.revokeObjectURL(output.threeMFURL);
|
||||
output.glbURL = null;
|
||||
output.threeMFURL = null;
|
||||
threemfButton.disabled = true;
|
||||
|
||||
// Start all over again.
|
||||
createWorker();
|
||||
|
||||
} else if (message?.type === 'log') {
|
||||
const logMessage = String(message.message ?? '');
|
||||
// Hide noisy per-module CDN fetch traces, keep other worker logs.
|
||||
if (!logMessage.startsWith('Fetching http')) {
|
||||
consoleElement.textContent += logMessage + '\r\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
}
|
||||
|
||||
} else if (message?.type === 'done') {
|
||||
setScript('safe', 'true');
|
||||
manifoldWorker.postMessage({type: 'export', extension: 'glb'});
|
||||
manifoldWorker.postMessage({type: 'export', extension: '3mf'});
|
||||
|
||||
finishRun();
|
||||
runButton.disabled = true;
|
||||
|
||||
} else if (message?.type === 'blob') {
|
||||
if (message.extension === 'glb') {
|
||||
if (output.glbURL) URL.revokeObjectURL(output.glbURL);
|
||||
output.glbURL = message.blobURL;
|
||||
|
||||
mv.src = output.glbURL;
|
||||
} else if (message?.extension === '3mf') {
|
||||
if (output.threeMFURL) URL.revokeObjectURL(output.threeMFURL);
|
||||
output.threeMFURL = message.blobURL;
|
||||
threemfButton.disabled = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
manifoldWorker.postMessage(
|
||||
{type: 'initialize', esbuildWasmUrl, manifoldWasmUrl});
|
||||
}
|
||||
|
||||
createWorker();
|
||||
|
||||
async function run() {
|
||||
saveCurrent();
|
||||
setScript('safe', 'false');
|
||||
enableCancel();
|
||||
clearConsole();
|
||||
console.log('Running...');
|
||||
const files = {};
|
||||
for (const [filename, contents] of Object.entries(getAllScripts())) {
|
||||
files[`./${filename}`] = contents;
|
||||
}
|
||||
const filename = currentFileElement.textContent;
|
||||
const code = editor.getValue();
|
||||
manifoldWorker.postMessage({
|
||||
type: 'evaluate',
|
||||
code,
|
||||
filename,
|
||||
files,
|
||||
jsCDN: 'jsDelivr',
|
||||
baseUrl: window.location.href
|
||||
});
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
manifoldWorker.terminate();
|
||||
createWorker();
|
||||
finishRun();
|
||||
console.log('Run canceled');
|
||||
}
|
||||
|
||||
runButton.onclick = function() {
|
||||
if (runButton.classList.contains('cancel')) {
|
||||
cancel();
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
};
|
||||
|
||||
function clickSave(saveButton, extension, outputName) {
|
||||
const container = saveButton.parentElement;
|
||||
return () => {
|
||||
const oldSave = saveContainer.firstElementChild;
|
||||
if (oldSave !== container) {
|
||||
saveDropdown.insertBefore(oldSave, saveDropdown.firstElementChild);
|
||||
saveContainer.insertBefore(container, saveDropdown);
|
||||
container.appendChild(saveArrow.parentElement);
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
|
||||
link.download =
|
||||
`${currentFileElement.textContent.trim() || 'manifold'}.${extension}`;
|
||||
|
||||
link.href = output[outputName];
|
||||
link.click();
|
||||
};
|
||||
}
|
||||
|
||||
const glbButton = document.querySelector('#glb');
|
||||
glbButton.onclick = clickSave(glbButton, 'glb', 'glbURL');
|
||||
|
||||
const threemfButton = document.querySelector('#threemf');
|
||||
threemfButton.onclick = clickSave(threemfButton, '3mf', 'threeMFURL');
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "manifold-editor",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc --noEmit && vite build",
|
||||
"build:watch": "vite build --watch",
|
||||
"clean": "rm -fr ./dist",
|
||||
"preview": "vite preview",
|
||||
"public": "cp -rv dist/* ../../public/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"esbuild-wasm": "^0.27.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||
"@google/model-viewer": "^4.1.0",
|
||||
"manifold-3d": "file:../..",
|
||||
"monaco-editor": "^0.53.0",
|
||||
"monaco-editor-auto-typings": "^0.4.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.9",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "7.3.1",
|
||||
"vite-plugin-static-copy": "^3.1.5"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 330 B |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 326 B |
|
Before Width: | Height: | Size: 492 B |
|
Before Width: | Height: | Size: 619 B |
|
Before Width: | Height: | Size: 675 B |
|
Before Width: | Height: | Size: 471 B |
@@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting to the ManifoldCAD User Guide</title>
|
||||
<link rel="canonical" href="/docs/jsuser/">
|
||||
<meta http-equiv="refresh" content="0; URL=/docs/jsuser/">
|
||||
</head>
|
||||
<body><a href="/docs/jsuser/">Click here if you are not redirected automatically.</a></body>
|
||||
</html>
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Increment version when updating CDN URLs to clean up cache.
|
||||
const cacheName = 'manifoldCAD-cache-v4';
|
||||
|
||||
const appShellAssets = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/editor.css',
|
||||
'/editor.js',
|
||||
'/editor-examples.js',
|
||||
'/manifest.json',
|
||||
'/fonts/orbitron-black-webfont.ttf',
|
||||
'/icons/close.png',
|
||||
'/icons/docs.png',
|
||||
'/icons/manifoldCAD.png',
|
||||
'/icons/manifoldCADonly.png',
|
||||
'/icons/ManifoldIcon.png',
|
||||
'/icons/mengerSponge192.png',
|
||||
'/icons/mengerSponge512.png',
|
||||
'/icons/mengerSponge64.png',
|
||||
'/icons/pause.png',
|
||||
'/icons/pencil.png',
|
||||
'/icons/play.png',
|
||||
'/icons/redo.png',
|
||||
'/icons/share.png',
|
||||
'/icons/star.png',
|
||||
'/icons/trash.png',
|
||||
'/icons/undo.png'
|
||||
];
|
||||
|
||||
const shouldHandleRequest = request => {
|
||||
if (request.method !== 'GET') return false;
|
||||
|
||||
const url = new URL(request.url);
|
||||
if (url.origin !== self.location.origin) return false;
|
||||
|
||||
// Skip transient HTML proxy requests only.
|
||||
// Caching this can cause stale proxy-module lookup issues.
|
||||
if (url.searchParams.has('html-proxy')) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
self.addEventListener('install', e => {
|
||||
e.waitUntil((async () => {
|
||||
const cache = await caches.open(cacheName);
|
||||
await cache.addAll(appShellAssets);
|
||||
await self.skipWaiting();
|
||||
})());
|
||||
});
|
||||
|
||||
self.addEventListener('install', e => {
|
||||
e.waitUntil(self.skipWaiting());
|
||||
});
|
||||
|
||||
self.addEventListener('activate', e => {
|
||||
e.waitUntil(caches.keys()
|
||||
.then((keyList) => {
|
||||
return Promise.all(keyList.map((key) => {
|
||||
if (key !== cacheName) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
}));
|
||||
})
|
||||
.then(() => self.clients.claim()));
|
||||
});
|
||||
|
||||
// Serves from cache, then updates the cache in the background from the network,
|
||||
// if available. Update available on refresh.
|
||||
self.addEventListener('fetch', e => {
|
||||
if (!shouldHandleRequest(e.request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.respondWith((async () => {
|
||||
const cachedResponse = await caches.match(e.request);
|
||||
|
||||
try {
|
||||
const response = await fetch(e.request);
|
||||
|
||||
// Only cache successful basic and opaque responses.
|
||||
if (response.ok || response.type === 'opaque') {
|
||||
const cache = await caches.open(cacheName);
|
||||
await cache.put(e.request, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
console.debug('Service worker fetch failed:', e.request.url, error);
|
||||
return Response.error();
|
||||
}
|
||||
})());
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
// vite.config.js
|
||||
import {resolve} from 'path'
|
||||
import {defineConfig} from 'vite'
|
||||
import {viteStaticCopy} from 'vite-plugin-static-copy'
|
||||
|
||||
export default defineConfig({
|
||||
worker: {format: 'es'},
|
||||
server: {
|
||||
// `npm run dev`
|
||||
headers: {
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
},
|
||||
fs: {allow: [resolve(__dirname, '..')]}
|
||||
},
|
||||
preview: {
|
||||
// `npm run preview`
|
||||
headers: {
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
},
|
||||
},
|
||||
plugins: [viteStaticCopy({
|
||||
targets: [
|
||||
// If type declaration files are missing, the web editor can't
|
||||
// load them, and type validation won't work for our core modules.
|
||||
{
|
||||
src: './node_modules/manifold-3d/dist/manifoldCAD.d.ts',
|
||||
dest: './', // Targets are relative to 'dist'.
|
||||
},
|
||||
{
|
||||
src: './node_modules/manifold-3d/dist/manifoldCADGlobals.d.ts',
|
||||
dest: './',
|
||||
}
|
||||
],
|
||||
})],
|
||||
resolve: {
|
||||
alias: {
|
||||
path: resolve(
|
||||
__dirname,
|
||||
'./node_modules/rollup-plugin-node-polyfills/polyfills/path.js')
|
||||
}
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
sourcemap: 'hidden',
|
||||
rollupOptions: {
|
||||
input: {
|
||||
manifoldCAD: resolve(__dirname, 'index.html'),
|
||||
},
|
||||
output: {
|
||||
entryFileNames: `assets/[name].js`,
|
||||
chunkFileNames: `assets/[name].js`,
|
||||
assetFileNames: `assets/[name].[ext]`
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,285 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {Accessor} from '@gltf-transform/core';
|
||||
|
||||
import {EXTManifold} from './manifold-gltf.js';
|
||||
|
||||
export const attributeDefs = {
|
||||
'POSITION': {type: Accessor.Type.VEC3, components: 3},
|
||||
'NORMAL': {type: Accessor.Type.VEC3, components: 3},
|
||||
'TANGENT': {type: Accessor.Type.VEC4, components: 4},
|
||||
'TEXCOORD_0': {type: Accessor.Type.VEC2, components: 2},
|
||||
'TEXCOORD_1': {type: Accessor.Type.VEC2, components: 2},
|
||||
'COLOR_0': {type: Accessor.Type.VEC3, components: 3},
|
||||
'JOINTS_0': {type: Accessor.Type.VEC4, components: 4},
|
||||
'WEIGHTS_0': {type: Accessor.Type.VEC4, components: 4},
|
||||
'SKIP_1': {type: null, components: 1},
|
||||
'SKIP_2': {type: null, components: 2},
|
||||
'SKIP_3': {type: null, components: 3},
|
||||
'SKIP_4': {type: null, components: 4},
|
||||
};
|
||||
|
||||
export function setupIO(io) {
|
||||
return io.registerExtensions([EXTManifold]);
|
||||
}
|
||||
|
||||
function readPrimitive(primitive, numProp, attributes) {
|
||||
const position = primitive.getAttribute('POSITION');
|
||||
const numVert = position.getCount();
|
||||
const vertProperties = [];
|
||||
let offset = 0;
|
||||
for (const attribute of attributes) {
|
||||
const size = attributeDefs[attribute].components;
|
||||
if (attribute === 'SKIP') {
|
||||
offset += size;
|
||||
continue;
|
||||
}
|
||||
const accessor = primitive.getAttribute(attribute);
|
||||
if (accessor) {
|
||||
const array = accessor.getArray();
|
||||
for (let i = 0; i < numVert; ++i) {
|
||||
for (let j = 0; j < size; ++j) {
|
||||
vertProperties[numProp * i + offset + j] = array[i * size + j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < numVert; ++i) {
|
||||
for (let j = 0; j < size; ++j) {
|
||||
vertProperties[numProp * i + offset + j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
return vertProperties;
|
||||
}
|
||||
|
||||
export function readMesh(mesh, attributes, materials) {
|
||||
const primitives = mesh.listPrimitives();
|
||||
if (primitives.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (attributes.length === 0) {
|
||||
const attributeSet = new Set();
|
||||
for (const primitive of primitives) {
|
||||
const semantics = primitive.listSemantics();
|
||||
for (const semantic of semantics) {
|
||||
attributeSet.add(semantic);
|
||||
}
|
||||
}
|
||||
for (const semantic in attributeDefs) {
|
||||
if (attributeSet.has(semantic)) {
|
||||
attributes.push(semantic);
|
||||
attributeSet.delete(semantic);
|
||||
}
|
||||
}
|
||||
for (const semantic of attributeSet.keys()) {
|
||||
attributes.push(semantic);
|
||||
}
|
||||
}
|
||||
|
||||
if (attributes.length < 1 || attributes[0] !== 'POSITION')
|
||||
throw new Error('First attribute must be "POSITION".');
|
||||
|
||||
const numProp = attributes.map((def) => attributeDefs[def].components)
|
||||
.reduce((a, b) => a + b);
|
||||
|
||||
const manifoldPrimitive = mesh.getExtension('EXT_manifold');
|
||||
|
||||
let vertPropArray = [];
|
||||
let triVertArray = [];
|
||||
const runIndexArray = [0];
|
||||
const mergeFromVert = [];
|
||||
const mergeToVert = [];
|
||||
if (manifoldPrimitive != null) {
|
||||
vertPropArray = readPrimitive(primitives[0], numProp, attributes);
|
||||
for (const primitive of primitives) {
|
||||
triVertArray = [...triVertArray, ...primitive.getIndices().getArray()];
|
||||
runIndexArray.push(triVertArray.length);
|
||||
materials.push(primitive.getMaterial());
|
||||
}
|
||||
const mergeTriVert = manifoldPrimitive.getMergeIndices()?.getArray() ?? [];
|
||||
const mergeTo = manifoldPrimitive.getMergeValues()?.getArray() ?? [];
|
||||
const vert2merge = new Map();
|
||||
for (const [i, idx] of mergeTriVert.entries()) {
|
||||
vert2merge.set(triVertArray[idx], mergeTo[i]);
|
||||
}
|
||||
for (const [from, to] of vert2merge.entries()) {
|
||||
mergeFromVert.push(from);
|
||||
mergeToVert.push(to);
|
||||
}
|
||||
} else {
|
||||
for (const primitive of primitives) {
|
||||
const numVert = vertPropArray.length / numProp;
|
||||
vertPropArray =
|
||||
[...vertPropArray, ...readPrimitive(primitive, numProp, attributes)];
|
||||
triVertArray = [
|
||||
...triVertArray,
|
||||
...primitive.getIndices().getArray().map((i) => i + numVert)
|
||||
];
|
||||
runIndexArray.push(triVertArray.length);
|
||||
materials.push(primitive.getMaterial());
|
||||
}
|
||||
}
|
||||
const vertProperties = new Float32Array(vertPropArray);
|
||||
const triVerts = new Uint32Array(triVertArray);
|
||||
const runIndex = new Uint32Array(runIndexArray);
|
||||
|
||||
return {
|
||||
numProp,
|
||||
triVerts,
|
||||
vertProperties,
|
||||
runIndex,
|
||||
mergeFromVert,
|
||||
mergeToVert
|
||||
};
|
||||
}
|
||||
|
||||
export function writeMesh(doc, manifoldMesh, attributes, materials) {
|
||||
if (doc.getRoot().listBuffers().length === 0) {
|
||||
doc.createBuffer();
|
||||
}
|
||||
const buffer = doc.getRoot().listBuffers()[0];
|
||||
const manifoldExtension = doc.createExtension(EXTManifold);
|
||||
|
||||
const attributeUnion = [];
|
||||
for (const matAttributes of attributes) {
|
||||
matAttributes.forEach((attribute, i) => {
|
||||
if (i >= attributeUnion.length) {
|
||||
attributeUnion.push(attribute);
|
||||
} else {
|
||||
const size = attributeDefs[attribute].components;
|
||||
const unionSize = attributeDefs[attributeUnion[i]].components;
|
||||
if (size != unionSize) {
|
||||
throw new Error(
|
||||
'Attribute sizes do not correspond: ', attribute, ' and ',
|
||||
attributeUnion[i]);
|
||||
}
|
||||
if (attributeDefs[attributeUnion[i]].type == null) {
|
||||
attributeUnion[i] = attribute;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (attributeUnion.length < 1 || attributeUnion[0] !== 'POSITION')
|
||||
throw new Error('First attribute must be "POSITION".');
|
||||
|
||||
const mesh = doc.createMesh();
|
||||
const numPrimitive = manifoldMesh.runIndex.length - 1;
|
||||
for (let run = 0; run < numPrimitive; ++run) {
|
||||
const indices = doc.createAccessor('indices')
|
||||
.setBuffer(buffer)
|
||||
.setType(Accessor.Type.SCALAR)
|
||||
.setArray(new Uint32Array());
|
||||
const primitive = doc.createPrimitive().setIndices(indices);
|
||||
const material = materials[run];
|
||||
if (material) {
|
||||
primitive.setMaterial(material);
|
||||
}
|
||||
mesh.addPrimitive(primitive);
|
||||
}
|
||||
|
||||
const numVert = manifoldMesh.numVert;
|
||||
const numProp = manifoldMesh.numProp;
|
||||
let offset = 0;
|
||||
attributeUnion.forEach((attribute, aIdx) => {
|
||||
if (attribute === 'SKIP') {
|
||||
++offset;
|
||||
return;
|
||||
}
|
||||
const def = attributeDefs[attribute];
|
||||
if (def == null)
|
||||
throw new Error(attribute + ' is not a recognized attribute.');
|
||||
|
||||
const n = def.components;
|
||||
if (offset + n > numProp) throw new Error('Too many attribute channels.');
|
||||
|
||||
const array = new Float32Array(n * numVert);
|
||||
for (let v = 0; v < numVert; ++v) {
|
||||
for (let i = 0; i < n; ++i) {
|
||||
array[n * v + i] =
|
||||
manifoldMesh.vertProperties[numProp * v + offset + i];
|
||||
}
|
||||
}
|
||||
|
||||
const accessor =
|
||||
doc.createAccessor().setBuffer(buffer).setType(def.type).setArray(
|
||||
array);
|
||||
|
||||
mesh.listPrimitives().forEach((primitive, pIdx) => {
|
||||
if (attributes[pIdx].length > aIdx &&
|
||||
attributeDefs[attributes[pIdx][aIdx]].type != null) {
|
||||
primitive.setAttribute(attribute, accessor);
|
||||
}
|
||||
});
|
||||
offset += n;
|
||||
});
|
||||
|
||||
const manifoldPrimitive = manifoldExtension.createManifoldPrimitive();
|
||||
mesh.setExtension('EXT_manifold', manifoldPrimitive);
|
||||
|
||||
const indices = doc.createAccessor('indices')
|
||||
.setBuffer(buffer)
|
||||
.setType(Accessor.Type.SCALAR)
|
||||
.setArray(manifoldMesh.triVerts);
|
||||
manifoldPrimitive.setIndices(indices);
|
||||
manifoldPrimitive.setRunIndex(manifoldMesh.runIndex);
|
||||
|
||||
const vert2merge = [...Array(manifoldMesh.numVert).keys()];
|
||||
for (const [i, from] of manifoldMesh.mergeFromVert.entries()) {
|
||||
vert2merge[from] = manifoldMesh.mergeToVert[i];
|
||||
}
|
||||
const ind = [];
|
||||
const val = [];
|
||||
for (const [i, vert] of manifoldMesh.triVerts.entries()) {
|
||||
const newVert = vert2merge[vert];
|
||||
if (vert !== newVert) {
|
||||
ind.push(i);
|
||||
val.push(newVert);
|
||||
}
|
||||
}
|
||||
const indicesAccessor = doc.createAccessor()
|
||||
.setBuffer(buffer)
|
||||
.setType(Accessor.Type.SCALAR)
|
||||
.setArray(new Uint32Array(ind));
|
||||
const valuesAccessor = doc.createAccessor()
|
||||
.setBuffer(buffer)
|
||||
.setType(Accessor.Type.SCALAR)
|
||||
.setArray(new Uint32Array(val));
|
||||
manifoldPrimitive.setMerge(indicesAccessor, valuesAccessor);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
export function disposeMesh(mesh) {
|
||||
if (!mesh) return;
|
||||
const primitives = mesh.listPrimitives();
|
||||
for (const primitive of primitives) {
|
||||
primitive.getIndices()?.dispose();
|
||||
for (const accessor of primitive.listAttributes()) {
|
||||
accessor.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
mesh.dispose();
|
||||
}
|
||||
|
||||
export async function loadTexture(texture, uri) {
|
||||
const response = await fetch(uri);
|
||||
const blob = await response.blob();
|
||||
texture.setMimeType(blob.type);
|
||||
texture.setImage(new Uint8Array(await blob.arrayBuffer()));
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
<meta property="og:image" content="https://elalish.github.io/manifold/samples/models/mengerSpongeSquare.png">
|
||||
<meta property="og:image:width" content="850">
|
||||
<meta property="og:image:height" content="850">
|
||||
<link rel="shortcut icon" type="image/png" href="/icons/ManifoldIcon.png">
|
||||
<link rel="shortcut icon" type="image/png" href="https://elalish.github.io/manifold/samples/models/favicon.png">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link type="text/css" href="editor.css" rel="stylesheet">
|
||||
<script type="application/ld+json">
|
||||
@@ -33,22 +33,14 @@
|
||||
self.ModelViewerElement = self.ModelViewerElement || {};
|
||||
self.ModelViewerElement.modelCacheSize = 0;
|
||||
</script>
|
||||
<script type="module" src="/examples.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="container header" style="background-color: #A288B3;">
|
||||
<p style="flex: 2;"><a target="_blank" href="https://github.com/elalish/manifold">Manifold</a>CAD
|
||||
<span class="top-edit blue">
|
||||
<button type="button" id="undo" class="top-icon undo squish" title="Undo"></button>
|
||||
<button type="button" id="redo" class="top-icon redo squish" title="Redo"></button>
|
||||
<button type="button" id="format" class="top-icon star squish" title="Format Code"></button>
|
||||
<button type="button" id="share" class="top-icon share squish" title="Copy Shareable Link"></button>
|
||||
<a target="_blank" href="https://manifoldcad.org/docs/jsuser/" class="top-icon docs" title="Documentation"></a>
|
||||
</span>
|
||||
<span id="type-indicator" style="font-size: 14px; margin-left: 12px; font-weight: normal; opacity: 0.8;"></span>
|
||||
</p>
|
||||
<div class="margin" id="file-margin">
|
||||
<p style="flex: 2;"><a target="_blank" href="https://github.com/elalish/manifold">Manifold</a>CAD </p>
|
||||
<div class="margin" id="file-margin" >
|
||||
<button type="button" id="file" class="blue item"><b class="uparrow"></b> <span
|
||||
id="current">Intro</span></button>
|
||||
<div class="dropdown" id="fileDropdown">
|
||||
@@ -70,28 +62,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Draggable pane dividers-start -->
|
||||
<div id="workbench" class="container workbench" style="flex: 2;">
|
||||
<div id="editor" class="col pane-left"></div>
|
||||
<div id="split-x" class="splitter splitter-x" aria-hidden="true"></div>
|
||||
<div id="rightPane" class="col container pane-right" style="flex-direction: column;">
|
||||
<model-viewer id="viewer" camera-controls min-camera-orbit="auto 0deg auto" max-camera-orbit="auto 180deg auto"
|
||||
shadow-intensity="1" tone-mapping="neutral" interaction-prompt="none" alt="Editor 3D output">
|
||||
<div class="container" style="flex: 2;">
|
||||
<div id="editor" class="col"></div>
|
||||
<div class="col container" style="flex-direction: column;">
|
||||
<model-viewer camera-controls min-camera-orbit="auto 0deg auto" max-camera-orbit="auto 180deg auto"
|
||||
shadow-intensity="1" interaction-prompt="none" alt="Editor 3D output">
|
||||
<div class="center" slot="poster">
|
||||
<p id="poster" style="text-align: center; line-height: 36px;"> Loading... </p>
|
||||
</div>
|
||||
<div id="animation">
|
||||
<button type="button" id="play" class="icon pause"></button>
|
||||
<input type="range" min="0" max="1" step="0.01" value="0" class="slider" id="scrubber">
|
||||
</div>
|
||||
</model-viewer>
|
||||
<div id="split-y" class="splitter splitter-y" aria-hidden="true"></div>
|
||||
<!-- Draggable pane dividers-end -->
|
||||
<div id="console"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="module" src="editor-examples.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.min.js"
|
||||
integrity="sha512-6bIYsGqvLpAiEBXPdRQeFf5cueeBECtAKJjIHer3BhBZNTV3WLcLA8Tm3pDfxUwTMIS+kAZwTUvJ1IrMdX8C5w=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.min.css"
|
||||
integrity="sha512-iQEIc0rsSDujsfjtD+lfyJ1W23Bh/lbgriubKDAym6VlEIDRj9rrbSIyJRyshOrl8s0yRcQ0+gyrZfSLyjJGWQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<script type="module" src="editor.js"></script>
|
||||
</html>
|
||||
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.1.1/model-viewer.min.js"></script>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,189 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Make manifold glTF</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" type="image/png" href="https://elalish.github.io/manifold/samples/models/favicon.png" />
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: rgba(0, 0, 0, .87);
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 700px;
|
||||
margin: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
model-viewer {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
height: 100vw;
|
||||
max-width: 600px;
|
||||
max-height: 600px;
|
||||
border: 3px solid black;
|
||||
border-radius: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#poster {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Load a glTF/GLB model and our <a href="https://github.com/elalish/manifold">Manifold</a> library will attempt to
|
||||
merge it into a set of manifold objects. If the model is water-tight, you can download the new GLB which will have
|
||||
our <a href="https://github.com/KhronosGroup/glTF/pull/2286">EXT_manifold</a> extension, thus preserving the
|
||||
manifold data without losing any mesh properties.</p>
|
||||
<p> If the View Manifold GLB checkbox is enabled, then some meshes in the model have open edges and don't represent a
|
||||
solid object. Check this box to see only the manifold parts, which will also enable them to be downloaded. If the
|
||||
download button is still disabled, it means there were no manifold meshes found. </p>
|
||||
<input type="file" id="input">
|
||||
<button id="download" disabled>Download manifold GLB</button>
|
||||
<input type="checkbox" id="viewFinal" disabled>
|
||||
<label for="viewFinal">View Manifold GLB</label>
|
||||
<model-viewer camera-controls shadow-intensity="1" alt="Loaded GLB model">
|
||||
<span id="poster" slot="poster">Drop a GLB here</span>
|
||||
</model-viewer>
|
||||
</body>
|
||||
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/2.0.1/model-viewer.min.js"></script>
|
||||
<script type="module">
|
||||
import { WebIO } from '@gltf-transform/core';
|
||||
import { prune } from '@gltf-transform/functions';
|
||||
import { KHRONOS_EXTENSIONS } from '@gltf-transform/extensions';
|
||||
import { SimpleDropzone } from 'simple-dropzone';
|
||||
import { readMesh, writeMesh, setupIO } from './gltf-io.js';
|
||||
import Module from './built/manifold.js';
|
||||
|
||||
const io = setupIO(new WebIO());
|
||||
io.registerExtensions(KHRONOS_EXTENSIONS);
|
||||
|
||||
const wasm = await Module();
|
||||
wasm.setup();
|
||||
|
||||
const { Manifold, Mesh } = wasm;
|
||||
|
||||
const mv = document.querySelector('model-viewer');
|
||||
const inputEl = document.querySelector('#input');
|
||||
const downloadButton = document.querySelector('#download');
|
||||
const checkbox = document.querySelector('#viewFinal');
|
||||
const dropCtrl = new SimpleDropzone(mv, inputEl);
|
||||
|
||||
let inputGLBurl = null;
|
||||
let outputGLBurl = null;
|
||||
let allManifold = true;
|
||||
let anyManifold = false;
|
||||
|
||||
dropCtrl.on('drop', async ({ files }) => {
|
||||
for (const [path, file] of files) {
|
||||
const filename = file.name.toLowerCase();
|
||||
if (filename.match(/\.(gltf|glb)$/)) {
|
||||
URL.revokeObjectURL(inputGLBurl);
|
||||
inputGLBurl = URL.createObjectURL(file);
|
||||
await writeGLB(await readGLB(inputGLBurl));
|
||||
updateUI();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function updateUI() {
|
||||
if (allManifold) {
|
||||
checkbox.checked = true;
|
||||
checkbox.disabled = true;
|
||||
} else if (anyManifold) {
|
||||
checkbox.checked = false;
|
||||
checkbox.disabled = false;
|
||||
} else {
|
||||
checkbox.checked = false;
|
||||
checkbox.disabled = true;
|
||||
}
|
||||
onClick();
|
||||
}
|
||||
|
||||
checkbox.onclick = onClick;
|
||||
|
||||
function onClick() {
|
||||
mv.src = checkbox.checked ? outputGLBurl : inputGLBurl;
|
||||
downloadButton.disabled = !checkbox.checked;
|
||||
};
|
||||
|
||||
downloadButton.onclick = function () {
|
||||
const link = document.createElement('a');
|
||||
link.download = 'manifold.glb';
|
||||
link.href = outputGLBurl;
|
||||
link.click();
|
||||
};
|
||||
|
||||
async function writeGLB(doc) {
|
||||
URL.revokeObjectURL(outputGLBurl);
|
||||
if (!anyManifold) {
|
||||
return;
|
||||
}
|
||||
const glb = await io.writeBinary(doc);
|
||||
|
||||
const blob = new Blob([glb], { type: 'application/octet-stream' });
|
||||
outputGLBurl = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
async function readGLB(url) {
|
||||
allManifold = false;
|
||||
anyManifold = false;
|
||||
updateUI();
|
||||
allManifold = true;
|
||||
const docIn = await io.read(url);
|
||||
const nodes = docIn.getRoot().listNodes();
|
||||
for (const node of nodes) {
|
||||
const attributes = [];
|
||||
const mesh = node.getMesh();
|
||||
if (!mesh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const materials = [];
|
||||
const tmpMesh = readMesh(mesh, attributes, materials);
|
||||
tmpMesh.runOriginalID = [];
|
||||
const firstID = Manifold.reserveIDs(materials.length);
|
||||
for (let i = 0; i < materials.length; ++i) {
|
||||
tmpMesh.runOriginalID.push(firstID + i);
|
||||
}
|
||||
const manifoldMesh = new Mesh(tmpMesh);
|
||||
mesh.dispose();
|
||||
|
||||
const matAttributes = [];
|
||||
materials.forEach(() => matAttributes.push(attributes));
|
||||
|
||||
manifoldMesh.merge();
|
||||
|
||||
try {
|
||||
// Test manifoldness - will throw if not.
|
||||
const manifold = Manifold(manifoldMesh);
|
||||
node.setMesh(writeMesh(docIn, manifold.getMesh(), matAttributes, materials));
|
||||
manifold.delete();
|
||||
anyManifold = true;
|
||||
} catch (e) {
|
||||
console.log(mesh.getName(), e);
|
||||
allManifold = false;
|
||||
}
|
||||
}
|
||||
|
||||
await docIn.transform(prune());
|
||||
|
||||
return docIn;
|
||||
}
|
||||
</script>
|
||||
|
||||
</html>
|
||||
@@ -1,12 +0,0 @@
|
||||
## Manifoldness and EXT_mesh_manifold
|
||||
|
||||
<!-- #region summary -->
|
||||
<!-- This region will be included in the examples list at https://manifoldcad.org/docs/jsapi/documents/Manifold_Examples.html -->
|
||||
|
||||
[ [example](https://manifoldcad.org/make-manifold.html) | [source](https://github.com/elalish/manifold/blob/master/bindings/wasm/examples/make-manifold/) ]
|
||||
|
||||
This example will test a glTF model for manifoldness. Manifold models will be extended with [EXT_mesh_manifold](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_mesh_manifold/README.md), a glTF extension for lossless transmission of manifold meshes.
|
||||
|
||||
Our implementation of `EXT_mesh_manifold` is [manifold-gltf](https://manifoldcad.org/jsdocs/modules/manifold-gltf.html), an extension for [glTF Transform](https://gltf-transform.dev). Although this example uses manifold to test for manifoldness, `manifold-gltf` depends only on glTF Transform.
|
||||
|
||||
<!-- #endregion summary -->
|
||||
@@ -1,67 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Make manifold glTF</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" type="image/png" href="https://elalish.github.io/manifold/samples/models/favicon.png" />
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: rgba(0, 0, 0, .87);
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 700px;
|
||||
margin: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
model-viewer {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
height: 100vw;
|
||||
max-width: 600px;
|
||||
max-height: 600px;
|
||||
border: 3px solid black;
|
||||
border-radius: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#poster {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Load a glTF/GLB model and our <a href="https://github.com/elalish/manifold">Manifold</a> library will attempt to
|
||||
merge it into a set of manifold objects. If the model is water-tight, you can download the new GLB which will have
|
||||
our <a href="https://github.com/KhronosGroup/glTF/pull/2286">EXT_mesh_manifold</a> extension, thus preserving the
|
||||
manifold data without losing any mesh properties.</p>
|
||||
<p> If the View Manifold GLB checkbox is enabled, then some meshes in the model have open edges and don't represent a
|
||||
solid object. Check this box to see only the manifold parts, which will also enable them to be downloaded. If the
|
||||
download button is still disabled, it means there were no manifold meshes found. </p>
|
||||
<input type="file" id="input">
|
||||
<button id="download" disabled>Download manifold GLB</button>
|
||||
<input type="checkbox" id="viewFinal" disabled>
|
||||
<label for="viewFinal">View Manifold GLB</label>
|
||||
<model-viewer camera-controls shadow-intensity="1" tone-mapping="neutral" alt="Loaded GLB model">
|
||||
<span id="poster" slot="poster">Drop a GLB here</span>
|
||||
</model-viewer>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
import { ModelViewerElement } from '@google/model-viewer';
|
||||
</script>
|
||||
<script type="module" src="make-manifold.ts"></script>
|
||||
</html>
|
||||