Compare commits

...
412 Commits
Author SHA1 Message Date
Emmett Lalish 798d83c8d7 v3.3.2 (#1428)
* try and fix build_wheels

* bump oneTBB version

* prod SDist
2025-11-15 00:36:54 -08:00
Tony Thompson 652a95320b Fix more npm package issues. (#1427)
* Pack all the things.

* Isolated smoke tests
2025-11-14 18:32:55 -08:00
Emmett Lalish a06d4502cd v3.3.1 (#1426) 2025-11-14 13:44:56 -08:00
Tony Thompson aea74b56ff Correct CLI and add tests. (#1425) 2025-11-14 12:38:30 -08:00
Emmett Lalish b51858f648 fix gear examples (#1423) 2025-11-14 01:15:26 -08:00
Tony Thompson b479c67510 Run npm install before npm publish (#1421) 2025-11-13 23:37:35 -08:00
Emmett Lalish 3b589b94cb v3.3.0 (#1419) 2025-11-13 11:00:23 -08:00
Tony Thompson 84f9a8ce76 Loading types should not create new scripts. (#1420) 2025-11-13 10:58:39 -08:00
Tony Thompson 75390fe5cb Update tests. (#1417)
* Support .ts, .mjs or .js extensions for examples.

* fsPromises.glob is only in node22+

* Add tests for recent examples.
2025-11-12 11:16:06 -08:00
Emmett Lalish 7054c63029 updated involute gear library and exmaple (#1416)
* updated involute gear library and exmaple

* disable gersemi
2025-11-12 01:42:01 -08:00
Tony Thompson 55776ac3b5 Assume virtual files are typescript (#1410) 2025-11-08 21:17:06 +13:00
Tony Thompson 9e127e455d Remove GlobalDefaults object. (#1408)
* Remove GlobalDefaults object.

On the way, this involved:

  * Added `lib/level-of-detail.ts`
  * Refactored `lib/manifoldCAD.ts` and `types/manifoldCAD.d.ts` to use re-exports.
  * Improved documentation.
    * Extracted GLTFNode into a module.
    * Added @internal to hide functions that aren't really public facing.
  * Updated examples to match.

* Fix tests.
2025-11-06 11:14:02 -08:00
Tony Thompson 6726ad2eeb Refactor stack trace handling. (#1403)
* Refactor stack trace handling.

* Bundler errors, too.
2025-11-02 01:00:58 -07:00
Tony Thompson d4acf92ced Ensure tabs are 2 spaces on manifoldCAD.org (#1402) 2025-11-02 08:03:14 +13:00
Tony Thompson e93f95dc04 Require explicit imports in/of manifoldCAD (#1401) 2025-11-01 01:13:55 -07:00
Tony Thompson fcd67c97b1 Small bundler improvements. (#1400)
Small changes to make packaging easier.
2025-10-31 02:10:00 -07:00
Tony Thompson b72a676caf Fetch remote types. (#1399)
* Fetch remote types (and use npm monaco)

* Path to polyfill.

* Local imports now validating.
2025-10-31 02:06:32 -07:00
Tony Thompson c181da63e5 Add missing declarations. (#1398) 2025-10-31 02:00:12 -07:00
Tony Thompson 3087302edc Automagically bundle manifoldCAD.d.ts (#1397) 2025-10-29 22:54:41 -07:00
Tony Thompson 097d487e96 Add specific error messages in wasm bindings. (#1396)
* Add specific error messages

  * When glMatrix is mentioned in a ReferenceError
  * When GLTF nodes are created at the top level,
    yet no geometry is exported to manifoldCAD

* Treat bundled code as typescript by default.

* Better wording.

* Add TypeScript syntax test.

* It has been [0] days since the last linter error.
2025-10-30 08:17:42 +13:00
Tony Thompson 8604c48a2f Copy wasm artifacts before building docs. (#1394) 2025-10-27 17:48:29 -07:00
Tony Thompson ff0162907d Isomorphic Javascript Libraries (#1392)
* Script import tests and infrastructure

* Almost working.

* Cleaned up esbuild plugin

* Exports in package.json

* Better errors through source mapping

* Consistent wasm initialization

* Bundler options and context types

* Massive refactor! WIP so PR comments not fully addressed (yet).

  * Evaluator is gone.  Most of it is in `garbage-collector` and `worker`.
  * Models now have to `export default` rather than set `result`.
  * Examples have been updated accordingly, and moved to `test/examples`.

* It has been [0] days since the last linter error.

* Bundle worker for easier inclusion in other projects.

Slightly more verbose worker.

* esbuild worker fix

* Format...

* More tests.

* CLI updated to match.

* Small tweaks.

* Now, with less gl-matrix

* Added involute gear example

* Enable CDN tests.

* Free of side effects.

* Involute fix.

* Tacks that may be brass in colour.

* Tracking GLTF nodes for fun and profit.

* Documentation update.

* bundle -> bundler in package.json
2025-10-28 12:28:49 +13:00
surrimandsurrim f6005ffa83 using static_cast<float> in ImportMesh to support assimp with double-precision (#1391)
using aiVector3t<float> in ImportMesh to support assimp with double-precision

Co-authored-by: surrim <root@surrim.org>
2025-10-21 08:21:51 +13:00
Tony Thompson 5cb7e6444e Transform static imports to dynamic (#1387)
* Transform static imports to dynamic

...when called from a web worker.  This allows use of `import x from 'npm'`
By default, packages will be loaded from https://esm.run/.

* Tests and broader syntax support.
2025-10-16 13:18:08 -07:00
Tony Thompson 50548dcc25 Fall back to packaged type files if not built. (#1389) 2025-10-16 13:16:54 -07:00
Tony Thompson bd68fe6d78 Set extension on worker.ts imports. (#1384)
Set extension on worker imports.
2025-10-15 01:09:43 -07:00
pca006132 54d0a2e210 fix csg tree bug (#1383)
* fix csg tree bug

* fix formatting
2025-10-14 11:13:08 +08:00
Tony Thompson 533fc6a86e Fix bad path preventing documentation build. (#1382)
Fix paths. Again.
2025-10-13 08:03:51 +13:00
Tony Thompson edd20e03f5 Refactor worker.ts (#1380)
* Promote web worker out of examples.

* Addressed review comments.
2025-10-11 17:31:05 -07:00
pca006132 f0747c3420 make MANIFOLD_USE_BUILTIN_TBB opt-in (#1379)
* make MANIFOLD_USE_BUILTIN_TBB opt-in

* use builtin-tbb for most CI actions
2025-10-10 15:29:57 +08:00
Tony Thompson 64c7d229bc Remediate top level async calls. (#1375)
* Remediate top level async calls.

* Prefer null over undefined.
2025-10-09 09:30:52 +13:00
Tony Thompson 34da5f0629 Make evaluator asynchronous, and other small TS tweaks. (#1360)
* Make evaluator asynchronous

* Renamed euler2quat.ts to math.ts

* Better function names.  More documentation.

* Move glMatrix out of evaluator.
2025-09-23 22:19:53 +12:00
Mike f18a4c7c38 Add BREP.io link to README (#1357) 2025-09-12 20:46:02 +12:00
Tony Thompson 557b320729 Fix paths for CI/CD. Again. (#1353)
Fix paths for CI/CD.
2025-09-05 20:29:59 +12:00
Tony Thompson e93393bcdc Refactor WASM state and export. (#1351)
* Isolated GLTF code without manifold dependencies.

* Split ManifoldCAD state.

* Clean up after code review.

* Renamed scene builder.
2025-09-04 13:26:15 -07:00
Trzeth ccece0bf69 fix single point import (#1349)
* fix

* fix empty

* update

* fix
2025-09-02 01:58:11 -07:00
Tony Thompson 5fe1e365fb Fix github pages CI/CD issue. (#1346)
Use package level type definitions.
2025-08-26 02:25:35 -07:00
Emmett Lalish ca375ea079 added test from #1309 (#1345) 2025-08-26 02:24:16 -07:00
Tony Thompson 59361d8d99 Repackage WASM dynamic evaluation and mesh export (#1329)
* Extract evaluation and export from worker

* Refactor evaluator to be object oriented.

This involved extracting some of the context stuff back into the
`worker.ts`; the evaluator doesn't really need to know how to set up the
exporter.

* Exporter does not need `module`.

...As there's now an easier way to add methods to the evaluation context.

* Now working with nodejs.

* exportModel now returns a Blob.

* /examples/lib moved to /lib

* Add npm to wasm build.

* Evaluate documentation and tweaks

* Documentation and copyright updates.

* Update vite config.
2025-08-25 01:58:08 -07:00
pca006132 818f7b3024 Fix install with static link and using builtin dependencies (#1341)
* static link needs find_package

* build static lib in CI

* fix subdir

* only test pkg-config for shared lib
2025-08-23 10:30:15 +08:00
Trzeth e047ba6362 fix Build error on Debian 11 (#1336)
* fix

* move priority to vec.h

* reset to default
2025-08-16 00:01:14 +08:00
Esteban Dugueperoux ae57329b67 Add missing header to build on mac osx with clang (#1332)
* Check if manifold compile with C++17 since clipper dependency is requiring C++17

See https://github.com/AngusJohnson/Clipper2/blob/main/CPP/CMakeLists.txt#L5

* Add missing header to build on MacOS with clang compiler

* Format code
2025-08-08 19:49:37 +08:00
darix 3124ad45e4 Use Python_EXECUTABLE from FindPython instead of hardcoding the command (#1328)
on many linux distributions /usr/bin/python would refer to python 2
which no longer exists. python 3.x is usually /usr/bin/python3.

The finder handles this for us.

Fixes #1327
2025-08-06 18:41:34 +08:00
pca006132 3a29e9566f v3.2.1 (#1326) 2025-08-06 14:09:39 +08:00
pca006132 7d5d78b957 disable async free when asan is enabled (#1325) 2025-08-06 10:27:33 +08:00
pca006132 bf93b97221 Simplify intel macos (#1322)
* Revert "adjust threshold for Manifold.Simplify test (#1311)"

This reverts commit 56f2b6730b.

* add intel macos
2025-08-06 08:55:43 +08:00
pca006132 34afe21c31 fix odr violation (#1323)
* fix odr violation

* fix python binding docstring name
2025-08-06 08:55:21 +08:00
pca006132 b4e0dbb763 deterministic batch boolean (#1321) 2025-08-06 08:53:43 +08:00
pca006132 4b4eb9a8ec Fix #1309 (#1319)
* tighten manifoldness check

* avoid matching removed halfedge

* fix

* remove commented code
2025-08-03 17:47:13 +08:00
Jiří Hon 9457a4e889 Fix faceID documentation in wasm bindings (#1317) 2025-07-30 02:57:38 -07:00
Tomas Franzén f1962a723c Add Cadova to Users in README (#1312)
Add Cadova to the Users table in README
2025-07-22 13:28:34 -07:00
Trzeth 56f2b6730b adjust threshold for Manifold.Simplify test (#1311)
* disable Manifold.Simplify test

* update threshold

* add comments
2025-07-22 13:25:31 -07:00
Trzeth e4c8defdff add old version gcc to CI (#1308)
* add ubuntu-22.04 with gcc 11

* fix typo

* fix linalg print

* move back operator<< to linalg.h
2025-07-21 02:30:43 -07:00
Trzeth dad5deeccc add MANIFOLD_FOUND cmake variable (#1307) 2025-07-18 20:44:47 -07:00
pca006132 76208dc02b Fix downstream openscad build (#1302)
add missing header
2025-07-16 00:57:56 +08:00
pca006132 6754a3cf7c v3.2.0 (#1298)
3.2.0
2025-07-15 23:24:26 +08:00
pca006132 73f3afdfc2 add cmake check for fp related options (#1300) 2025-07-15 22:50:19 +08:00
Henk 39e3cd3f6e Add JoinType.Bevel (#1299)
* Add JoinType.Bevel to Python binding

* Implement JoinType.Bevel

* Add JoinType.Bevel to other bindings

* Fix typo and test and formatting

* Fix formatting
2025-07-15 22:11:50 +08:00
Trzethandpca006132 c18032c31e fix stubgen path (#1301)
* add cmake check for fp related options

* fix

* update

* Revert "add cmake check for fp related options"

This reverts commit 4045cf3653f9376c42e16b58adc61fd494afb11f.

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2025-07-15 22:07:23 +08:00
pca006132 83ddd27063 skip halfedges that are removed (#1296) 2025-07-15 16:26:29 +08:00
pca006132 8610651942 Fuzzing (#1214)
* fix compilation error

* compile fuzzing test in github action

* use pthread

* add macos CI

* fix for macos

* fix polygon fuzz

* format

* update fuzztest version

* remove unused code

* set environment variable

* update README

* update instruction

* add back ASAN option
2025-07-14 23:01:49 +08:00
Trzeth da4997bd29 fix pyi vert_properties type (#1291)
* fix

* update doc

* fix pylance

* python postbuild

* fmt

* fmt

* update workflows to windows-2025 since windows-2019 deprecated

* fin
2025-07-02 15:44:31 +08:00
Trzeth 52c5ef592e update Clipper2 to 1.5.4 and fix CrossSection::Offset (#1292)
* update Clipper2 to 1.5.4
fix CrossSection::Offset arc_tol

* update to lastest commit
update flake.lock
2025-06-27 08:31:42 +08:00
sorashin 8c63b864d8 Add Bento3D to README.md (#1288)
Add Bento3D to README Uses section
2025-06-15 21:49:13 +12:00
Emmett Lalish 87b8167659 fixed meshID regression (#1287)
* fixed meshID regression

* fix compiler
2025-06-12 00:31:33 -07:00
Philip Rideout 982c339abc Add Arcol to README.md (#1286)
We use manifold to slice up buildings!
2025-06-11 14:54:28 -07:00
Jonathan Hogg 3a83b83ca3 Add missing Manifold.simplify() method to Python API (#1282)
Add missing Manifold.simplify() method to Python API (#1281)

* add binding to manifold3d.cpp

* add test call in all_apis.py
2025-06-10 17:10:34 +08:00
pca006132 75dc3219fa faster halfedge pairing for large meshes (#1279)
* improve locality by sorting smaller arrays

* simple optimizations for triangulation

* address comments

* use Next3
2025-06-10 15:45:17 +08:00
pca006132 90cab6e2bb Make GCC floating-point operations standard conforming (#1273)
* disable fp-contract for gcc

* avoid excess precision as well
2025-06-05 08:03:36 +08:00
pca006132 186dc69f52 use cmake4 (#1274) 2025-06-04 20:06:13 +08:00
pca006132 d013ddec32 Use union-find for winding number (#1271)
* use union-find for winding number

* address comments

* fix comparison signs

* cleanup

* remove cfind, not needed when lock-free

* move DisjointSets to its own header with copyright info

* fix naming and casts
2025-06-02 14:05:38 +08:00
pca006132 182188b805 misc performance improvements (#1269)
* allow collider to take a function for queries

* async free for large arrays

* use named constant for threshold
2025-06-01 22:18:08 +08:00
pca006132 2f4741e0b1 v3.1.1 (#1272) 2025-06-01 15:48:47 +08:00
Kongfa Waroros 023bb59be9 Fix crashing on an empty MeshGL with one runIndex element (#1261) 2025-05-29 02:43:17 -07:00
Tomas Franzén 2984626cb2 Add test for Simplify adding cracks (#1263)
* Add test CurvedWallSimplify

* Change code formatting to appease check_format

* Fix two remaining format issues

* Move CurvedWallSimplify to boolean_test and call it DISABLED_SimplifyCracks
2025-05-27 15:24:37 -07:00
Emmett Lalishandpca006132 551e1a12cf v3.1 (#1259)
* bump version

* bindings: include string in manifold3d

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2025-05-14 12:43:54 -07:00
Emmett Lalish 2cf3dfd0ec default offset to round (#1257) 2025-05-14 02:17:50 -07:00
pca006132 b252782489 wasm: use string status (#1256) 2025-05-13 14:59:05 +08:00
Nicolas Mattia 39934bca55 Clarify WASM objects to delete (#1254) 2025-05-12 16:29:41 +08:00
Emmett Lalish 1d18e60d74 fixed coplanar property collapse (#1255)
* fixed coplanar property collapse

* fix CI

* fix CI more
2025-05-11 03:15:29 -07:00
Ben Lubar 061c5e7a48 Add include for cstdint (#1253)
Add include for cstdint.
2025-05-07 16:50:13 +08:00
Trzeth e3ff2a0019 Add test for CrossSection::BatchBoolean (#1249)
* init

* use Area instead

* remove print
2025-05-03 16:12:41 +08:00
Tomas Franzén 3027282259 Fix intersection behavior in CrossSection::BatchBoolean (#1247)
* Add special case for intersections in CrossSection::BatchBoolean

* Fix code formatting
2025-05-02 23:21:49 +08:00
Emmett Lalish 1ec20369ca added simplify to WASM bindings (#1246) 2025-05-01 02:48:05 -07:00
Emmett Lalish cd57073179 revert to old, robust decimator (#1243) 2025-05-01 01:53:54 -07:00
Daniel Engberg 3ba3175f2c Update README.md (#1245)
Add "make test" to building instructions
2025-05-01 01:49:28 -07:00
Daniel Engberg 5145d3422b Add test target to CMake (#1244)
Add manifold_test as test target for unit testing
2025-05-01 01:49:01 -07:00
Emmett Lalish 41d8d97d7c use input faceID (#1241)
* use input faceID

* addressing feedback
2025-04-30 03:19:42 -07:00
Emmett Lalish 54baa62b4a Remove triProperties (#1232)
* started adding halfedge prop support

* fixed bugs

* attempt to push props through triangulation

* fixed a few bugs

* fixed MANIFOLD_ASSERT

* fixed bug

* updated edge_op

* fixed sort

* fixed compactProps

* more updates

* a few more

* fixed a bug

* fixed old bug

* fixed bug

* fixed edge swap

* small WASM update

* updated boolean

* updated smoothing

* removed triProperties

* reduce test memory

* format

* moved numProp out of meshRelation

* moved properties out of meshRelation

* harmonize includes

* remove unused

* update test to check simplify with properties

* fix CI

* more CI fixes

* undo no-unused req

* renamed internal faceID to coplanarID

* fixed tests

* updated quad short path
2025-04-28 03:14:04 -07:00
Emmett Lalish f3a6a477da Don't simplify unless requested (#1185)
* don't simplify unless requested

* small fixes
2025-04-25 03:45:41 -07:00
Emmett Lalish 994814921e Update README.md (#1237) 2025-04-24 11:06:07 +12:00
Robin 138815a127 Update ts definitions (#1236)
* Accept readonly arrays

* Use tuple return type

* Format
2025-04-23 14:04:56 -07:00
Trzeth 85b0cd211c Fix determinism (#1234)
* fix determinism

* Add determinism test

* fmt
2025-04-23 01:12:55 +08:00
JimandEmmett Lalish 9756892b09 Update README.md (#1228)
Adding Conversation to users of Manifold.

Co-authored-by: Emmett Lalish <elalish@gmail.com>
2025-04-20 03:24:03 -07:00
Gianni MarianiandGianni Mariani 3015a81c07 Add PythonOpenSCAD and AnchorSCAD as 'users'. (#1231)
Co-authored-by: Gianni Mariani <owebeeone@gmail.com>
2025-04-20 02:24:45 -07:00
Emmett Lalish 272a22439f bump CI ubuntu version (#1230) 2025-04-18 22:10:15 -07:00
Clifford Yapp 4ba67b8fcf Fix MANIFOLD_DEBUG guards for Read/Write OBJ api (#1226) 2025-04-12 22:52:07 -07:00
Emmett Lalish 09fb03cff2 added MANIFOLD_ASSERT (#1225)
* added MANIFOLD_ASSERT and rearranged includes

* fixes

* fix format and compile

* uniform format

* update clang-format
2025-04-12 02:18:42 -07:00
Clifford Yapp b8dd573a9c Remove per-error hacks in favor of a "disable Werror" option (#1223)
* Remove per-error hacks in favor of a "disable Werror" option

For users who are just looking to build and aren't interested in failing the
build due to warnings, provide a MANIFOLD_STRICT option to control it.

By default it will remain on, since developers will generally want to fix
errors found, but for user projects or those working with older compilers
setting -DMANIFOLD_STRICT=OFF will allow them to avoid build failure when
warnings are spurious.

Also should eliminate most of the motivation to suppress specific warnings
per-file, although that may still be desirable in some specific instances if
the newest compilers are the ones generating verifiably incorrect/unavoidable
errors.

* formatting
2025-04-11 03:58:31 -07:00
Clifford Yapp 63a36e678a Make API for high precision OBJ I/O method (#1219)
* Make a quick try at turning Dump into a public Read/Write API not dependent on MANIFOLD_EXPORT being set.

* Try to add Read/Write for MeshGLP, in addition to Manifold.

* OBJ indexes faces starting at 1, not 0

* Go with fixed output precision to make sure we clearly always have the expected number of printed digits

* Adjust comments - we are writing OBJ files, just putting more info in comments.

* Adjust names to be less general

These are not (and are not intended to be) general purpose/format I/O methods.
While we're at it, make clear that the format itself is not intended to be
guaranteed stable across releases.

* Formatting, see if we can also skip Windows CR line ending chars.

* more formatting

* Move ReadOBJ/WriteOBJ implementation to .cpp file

* formatting

* Remove MeshGL* ReadOBJ/WriteOBJ support, guard Manifold versions with MANIFOLD_IO option.

* Whoops, shared_ptr needs <memory>

* Add header guard, remove const char methods, use const std::string param.

* formatting

* MANIFOLD_IO -> MANIFOLD_DEBUG, reduce to just stream API

* formatting

* See if std::filesystem helps Windows find files correctly.

* Various cleanups, disable (I think) the Offset tests having trouble in WASM

* guard iostream include

* Remove unneeded headers
2025-04-09 22:56:06 -07:00
Clifford Yapp 7c2b7b407b Suppress quickhull.cpp warnings with g++ compiler (#1218)
* Suppress quickhull.cpp warnings with g++ compiler

Observed with gcc 12 (array-bounds) and 13 (stringop-overflow):

inlined from ‘void manifold::QuickHull::createConvexHalfedgeMesh()’ at /home/user/manifold/src/quickhull.cpp:442:40:
/usr/include/c++/13/bits/stl_algobase.h:931:18: error: ‘void* __builtin_memset(void*, int, long unsigned int)’ specified bound 18446744073709551608 exceeds maximum object size 9223372036854775807 [-Werror=stringop-overflow=]
  931 |         *__first = __tmp;
      |         ~~~~~~~~~^~~~~~~

inlined from ‘void manifold::QuickHull::createConvexHalfedgeMesh()’ at /home/user/manifold/src/quickhull.cpp:442:40:
/usr/include/c++/12/bits/stl_algobase.h:922:18: error: ‘void* __builtin_memset(void*, int, long unsigned int)’ pointer overflow between offset 0 and size 18446744073709551608 [-Werror=array-bounds]
  922 |         *__first = __tmp;
      |         ~~~~~~~~~^~~~~~~

* fix formatting
2025-04-03 21:11:00 +02:00
Jiří Hon 29d2b1dbe9 Fix triangulate js bindings (#1216)
* fix triangulate js bindings

* format
2025-04-03 02:26:48 -07:00
Clifford Yapp 90f83a98be In-loop Status calls were a work-around - see if #1205 solved it. (#1212)
In-loop Status calls were a hacky work-around - see if #1205 had cleared the issue.
2025-03-31 21:58:30 -07:00
Emmett Lalish ca7d6c326c updated status doc (#1210)
updated comment
2025-03-31 02:29:44 -07:00
Emmett Lalish 00a589dafe Fix triangulator handling of splinters (#1205)
* added tests

* relax convexity check

* un-disabled tests

* updated minimize_testcase

* added minimized test

* fix keyhole
2025-03-31 02:04:46 -07:00
Michael Faulkner 7c8fbe186a Fix Cross Section Mirror and add Test (#1206)
* Fix Cross Section Mirror and add Test

* Updated Docs and Simplified Test
2025-03-29 23:03:10 -07:00
pca006132 4fabfc327b use more precise acos function (#1201)
* use more precise acos function

* fix compilation error

* fix msvc
2025-03-29 21:14:36 +01:00
pca006132 75b592f38c set cmake version in macos action (#1207)
set cmake version
2025-03-30 03:40:14 +08:00
Emmett Lalish 444399393a Fix Revolve clipping (#1200)
fixed sign error
2025-03-28 08:53:45 +13:00
pca006132 2a4e9bae06 Fix empty slice (#1199)
* early return for empty collider

* added empty slice test

* changed test
2025-03-27 19:58:07 +08:00
Scott RichmondandScott Richmond bdb0b880c9 Changed the delete memory operation to a destructure in the Merge functions as Manifold doesn't own this memory (#1196)
* Changed the delete memory operation to a destructure in the Merge functions as Manifold doesn't own this memory.

* Fix code format error.

---------

Co-authored-by: Scott Richmond <strich@users.noreply.github.com>
2025-03-25 10:05:19 +08:00
pca006132 33a69413c2 fix a bunch of warnings (#1194)
* fix a bunch of warnings

* fix build error

* rename variable
2025-03-21 14:30:00 +08:00
pca006132 4edd442297 Fix face removal (#1191) (#1192)
* fix opposite face removal

* added testcase

* fix test genus

* handle halfedgeTangent and faceNormal

* address comment

* fix CreateFaces

* set triPriority

* use named constant

* capture for msvc

* format
2025-03-15 20:43:56 +08:00
pca006132 beb9ca6122 misc changes (#1190)
* removed use of acos and fma in boolean

* add mesh debug io function

* format

* add comment

* remove another fma
2025-03-15 17:00:06 +08:00
Johnny fab833e7e1 ARM Wheels (#1180)
Update build_wheels.yml
2025-03-13 20:39:32 +08:00
pca006132 1f188036d1 parallel CreateHalfedges (#1188)
* parallel CreateHalfedges

* add const
2025-03-13 15:25:33 +08:00
pca006132 808c261b33 make boolean deterministic (#1187)
* make edge collapse order deterministic

* reorder halfedges

* update simplify result

* deterministic vertex normal calculation

* fix compilation error

* reduce code duplication

* test for determinism in ember test

* fix nondeterminism in topology fixup

* format

* fix compilation error

* fix nondeterminism in EdgePos vector

* make batch boolean deterministic

* test determinism with some samples

* don't stress test when wasm is used

* disable CondensedMatter64 for wasm?
2025-03-13 15:25:16 +08:00
pca006132 bc70f58990 avoid sparse indices (#1186)
* ugly removal

* remove more

* avoid sort

* avoid intermediate sparse indices

* make analyze_ember_tests accept optional parameter

to run with different benchmark.csv files

* remove p0q2 computation

* fix race?

* properly isolate stuff

* added failing triangulator case

* cleanup dead code

* slight cleanup

* add comment

* avoid writing to SparseIndices

* fix

* remove sparse indices

* fix compilation error

* sort results

* simplify collider recorder code

* fix naming

* fix normalP

* add documentation

* Revert "fix naming"

This reverts commit 79a2526cbde751aa7bba108b6aacff64a1791939.
2025-03-13 14:59:32 +08:00
Howard Trickeyandpca006132 d4439bccec Ember tests (#1184)
* Add tests for performance like in the EMBER paper.

See the README for details.

* Trimming outliers didn't work. Trim plot range instead.

* cleanup

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2025-03-09 17:20:49 +08:00
Emmett Lalish 8e34f97194 improved MeshGL docs (#1178)
* added docs

* addressing feedback
2025-03-07 13:12:21 -08:00
Emmett Lalishandpca006132 954df798b2 New decimator (#1175)
* merging

* mostly working

* implemented, but crashes

* runs serially

* fix compile issues

* fixed empty meshes

* fix parallel error dumping

* fixed parallelization

* fix compiler

* run black

* improved triangulation

* remove unreferenced verts

* addressing feedback

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2025-03-05 20:42:39 -08:00
pca006132 0451671bd3 unified ManifoldParams and PolygonParams (#1167)
* unified ManifoldParams and PolygonParams

* disable failing test

* made verbose param an int
2025-02-27 17:53:51 +08:00
pca006132 cba9e35bb1 Early return when result size too large (#1166)
early return when result size too large
2025-02-25 19:25:11 +08:00
pca006132 5399bd4760 Misc changes (#1162)
* avoid uploading to PyPI for manual trigger

* update dependencies

* tweak tests

* update to dump full precision

* updated RELEASE_CHECKLIST
2025-02-23 23:31:12 +08:00
pca006132 06b10e7149 kdtree (2D) for collider (#1160)
* kdtree

* remove verify code

* address comments

* try to add the CI failing case

* fix build

* set precision

* format

* fix
2025-02-23 14:59:14 +08:00
pca006132 f178cd1d73 parallelize mesh fixup (#1148)
* parallelize mesh fixup

* avoid quadratic behavior

* documentation

* allocation optimization

* format

* fix gcc warning

* reduce temporary allocations in std::function

* rename

* format

* split DedupeEdges into its own function

* avoid undefined behavior
2025-02-21 08:21:47 +08:00
pca006132 5ae22f0d57 edge_op: quit CollapseEdge early if possible (#1152) 2025-02-20 10:34:29 -08:00
pca006132 a97b150bb5 fix linalg.h (#1155) 2025-02-20 20:53:54 +08:00
Emmett Lalish c16b521193 improved decimation (#1147)
* improved decimation

* only collapse intersected Boolean edges

* ensure retains verts are not removed

* collapse all short edges

* fix compile

* gotten part

* cleanup

* pca's patch
2025-02-19 01:47:41 -08:00
Emmett Lalish c149dd5088 fix default JS parameter binding for simplify (#1146)
fix default parameter binding
2025-02-16 01:06:15 -08:00
pca006132 a857f008dd Fix misc (#1143)
* update nix actions

* misc fix

* use modern c++
2025-02-13 01:04:42 -08:00
Emmett Lalishandpca006132 f2da5eab66 Better faces (#1040)
* refactor

* separate propVert from createFaces

* working, but very slow

* no longer slow

* added test

* loosen test

* merging

* update nix actions

* fix

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2025-02-13 00:51:48 -08:00
pca006132 447921cf97 Faster import (#1139)
* avoid fill after resize if possible

* speed up simplification

* fix FlagEdge

* avoids calling SplitPinchedVerts again

* triProperties should be initialized

* add comments

* fix

* check for non-finite input
2025-02-03 17:23:01 +08:00
Tomas Franzén ac82767927 Add Manifold-Swift to Bindings & Packages in README (#1133)
Add Manifold-Swift to README.md
2025-01-14 17:27:51 +08:00
Emmett Lalish 56fec978c6 update author info (#1130) 2025-01-04 21:47:44 -08:00
Tomas Franzén ae813ddecb Add const keyword to CrossSection::Compose parameter (#1129)
* Add const keyword to CrossSection::Compose parameter

* Fix code formatting
2024-12-30 10:44:22 -08:00
pca006132 1b3dbc29e2 python: add ninja build dependency (#1128)
* python: add ninja build dependency

* change ninja version setting

* CI: install ninja for SDist

* fix package name
2024-12-29 21:59:36 +08:00
Emmett Lalish 98b8142519 v3.0.1 (#1126)
bump version
2024-12-27 12:03:06 -08:00
pca006132 b7a02755aa csg_tree: avoid recursive drop (#1127)
* csg_tree: avoid recursive drop

* don't need to lock before checking use count

* no need for SharedPtrGuard::UseCount
2024-12-27 10:23:08 -08:00
pca006132 be1dec2ef0 CI updates (#1123)
* CI updates

* CI: naming

* cmake: try to fix msvc dll output directory

* cmake: exportlib for samples

* CI: fix bash

* try to copy the dll files?
2024-12-26 09:03:57 -08:00
pca006132 3a0c251b0e wasm: update package-lock (#1124)
* wasm: update package-lock

* fix typescript
2024-12-24 12:50:03 +08:00
pca006132 209350fd1e setup dependabot (#1121) 2024-12-24 08:45:49 +08:00
pca006132 1917ce0844 python: update nanobind version requirement (#1120)
python: relax nanobind version requirement
2024-12-23 21:14:43 +08:00
Marius Kintel f48111c43c Allow Clipper2 to be supplied externally when using Manifold as a submodule (#1115)
* Remove duplicate target code for Clipper2

* Don't look for Clipper2 if it was already found upstream

* Put back pkg-config setup for Clipper2
2024-12-18 22:14:42 -08:00
pca006132 3db0969d1d update fuzztest (#1112)
* update fuzztest

* fix build

* add manifold fuzz

* use cube instead

* fix comment

* moved regression test out

* disable test
2024-12-19 01:07:02 +08:00
Emmett Lalish c50b0f80ff Fixed unreferenced vert seg fault (#1110)
* added test

* update test

* fixed unreferenced verts

* no repro locally anymore...

* fix facing triangle removal, simplify unreferenced vert removal

* remove unreferenced verts less often
2024-12-17 11:14:07 -08:00
Jeffrey Wardman 25ce1b1d53 add vcpkg to readme (#1108)
* add vcpkg to readme

* move vcpkg in readme from building to bindings and packages

* vcpkg externally maintained
2024-12-14 17:18:21 -08:00
pca006132 926d2e7a7d boolean debugging (#1104)
* dump debug information

* simple self-intersecting case

* impl.h: define ImportMeshGL64 when MANIFOLD_EXPORT is defined

* try to fix compilation

* address comments

* better checks

* updated test

* some minor updates

* better debug output

* fix includes

* disable test

* smaller example

* allow setting epsilon

* revert an accidental change

* fix models

* address comments
2024-12-11 12:56:08 +08:00
Nick Ufer af5d95ec1b refactor: change return type to size_t for cross section vertex/contour count (#1106) 2024-12-08 16:34:14 -08:00
pca006132 ec0bbca3d9 csg-tree: fix subtraction (#1103)
* csg-tree: fix subtraction

* csg-tree: fix comment
2024-12-07 15:39:14 +08:00
Nick Ufer aacf37aa4d refactor(1089): update index types from int to size_t (#1090) 2024-12-04 12:28:08 -08:00
Emmett Lalish 847016c069 fix meshIO alpha type (#1101) 2024-12-04 12:27:38 -08:00
Emmett Lalish 517a224f44 finer timing in ManifoldCAD.org (#1099)
split working time and export time
2024-12-04 11:11:36 -08:00
Emmett Lalish a13ceab979 add user bitbybit (#1098)
add bitbybit
2024-12-03 21:37:33 -08:00
pca006132 36035428bc run tests for nix (#1094)
* run tests for nix

* avoid building latest tbb
2024-12-04 09:07:42 +08:00
Emmett Lalish f1a8693016 Fix DedupePropVerts (#1092)
* added test

* fixed dedupePropVerts

* size_t
2024-12-03 15:19:41 -08:00
pca006132 fb29e8586e avoid recursion in csg tree (#1086)
* csg_tree.cpp: avoid recursion

* fix cache transform

* fix compiler error

* cleanup

* fixed simple bug

* disable flaky CondensedMatter16

* disable properly

* documentation and cleanup

* address some comments

* make finalize explicit

* rename children to destination

* some simplification

* added recursive pseudocode

* simplify
2024-12-04 01:34:02 +08:00
Nick Ufer 8556840558 fix: change manifold_calculate_normals min_sharp_angle type to double (#1085) 2024-12-01 21:24:40 +08:00
pca006132 2162d8d91f wasm-tbb: increase stack size and add CI (#1079)
* update dependencies and run wasm-tbb build

* update stack size...

* update

* format

* fix compilation error

* format

* fix

* idk what happened

* change emscripten version back

* don't test for wasm-tbb

* readme changes
2024-11-30 23:27:45 -08:00
pca006132 11387fb871 make format run in parallel (#1080) 2024-11-28 16:04:36 -08:00
Nick Ufer ddc0a18d87 feat: add C binding for Manifold::NumProp() (#1078)
Introduced a new function to the C bindings: `manifold_num_prop`. This function retrieves the number of properties associated with a Manifold object. Added corresponding tests to validate its integration.
2024-11-27 11:26:22 -08:00
pca006132 f8c9dc9c8b Enable emscripten build with parallelization (#1045)
* port tbb changes

* format
2024-11-28 00:41:40 +08:00
Jeffrey WardmanandJeffrey Wardman 3ea3e7e5e5 casting issue in debug mode (#1077)
Co-authored-by: Jeffrey Wardman <jeffrey.w@zydex.com>
2024-11-27 10:34:49 +08:00
Reza Ali bc08d41dc0 Added Valence 3D to Users in README.md (#1074)
Update README.md

Added Valence 3D to Users in README.md
2024-11-26 15:27:42 +08:00
Emmett Lalish 560e37e985 fix bug JS binding for project() (#1071)
fix bug, add test
2024-11-23 22:23:14 -08:00
Emmett Lalish db08c23988 Try to restrict codecov again (#1069)
* restrict again

* switch from remove to extract
2024-11-23 10:53:24 -08:00
Emmett Lalish 7d27a437ab update lcov excludes (#1068)
* update lcov excludes

* try double quotes

* add cross section, remove headers

* fix mac CI?

* remove pkg-config
2024-11-23 09:39:42 -08:00
pca006132 b77abde57b cancel in-progress runs upon new push (#1065) 2024-11-21 14:48:11 +08:00
Emmett Lalish 70abe63963 fix codecov (#1032)
* fix codecov

* try verbose

* update to v5
2024-11-20 09:22:17 -08:00
pca006132 75f42f9c53 update cibuildwheels version (#1063) 2024-11-19 13:36:31 +08:00
Emmett Lalish 978f84107f fix download-artifact (#1062) 2024-11-18 15:32:00 -08:00
Clifford Yapp 7d6c285b3e parallel.h is private again, so we can make iters.h private as well. (#1061)
* parallel.h is private again, so we can make iters.h private as well.

* Per elalish, don't need to document non-public features in Doxygen.
2024-11-18 13:00:44 -08:00
Emmett Lalish 78915ae28d Update README.md (#1060)
Turns out Babylon.js uses Manifold too!
2024-11-18 10:48:51 -08:00
pca006132 a557225c29 Fix action (#1059)
* archs default to native, no need to specify x86_64

* limit artifact search and add dependency
2024-11-19 00:41:42 +08:00
Emmett Lalishandpca006132 f22435f1e5 Fix publishing (#1056)
* try to fix npm artifacts

* fix python SDist

* cibuildwheel: build universal binary for macos

* try to build all binaries

* skip builds on incompatible architecture

* again

* typo...

* typo?

* update

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2024-11-18 18:01:00 +08:00
Emmett Lalish 5d127e57fb v3.0 (#1024)
* nanobind link

* update READMEs

* bump version

* update npm README
2024-11-17 21:27:58 -08:00
pca006132 125dbd41cd generate version.h for emscripten build (#1055)
* generate version.h for emscripten build

* fix

* typo
2024-11-18 12:31:03 +08:00
Henner Zeller 182adae00a Provide a numeric version number to compare against. (#1054)
Including simple example in version.h.in and checking
in the test-{cmake,pkgconfig}.sh scripts.
2024-11-17 19:01:25 -08:00
pca006132andEmmett Lalish 7f298aa9db cmake changes (#1048)
* enable emscripten build with tbb enabled

* nix: fix emscripten build, add tbb build

* browser

* fix python build

* minor

* no parallel sdf for js

* disable test for emscripten-tbb

* add cache for nix python build

* cmake changes

* fix

* update

* update

* update

* update

* move python dependency handling

* more changes

* format

* fix broken format

* fix

* simplify

* allow emscripten with cbind

* build fetched dependencies as static libs

* fix static build

* fix rebuild

* fix extras

* remove parallel.h from public API

* update tests

* make dependencies private

* move parallel.h

* missing utils.h change

* fix

* fix...

* fix wasm tbb

* add more cmake consumer tests

* fix consumer

* add rpath

* Revert "add rpath"

This reverts commit 2e8a96c90b26f7044617953ccf0f6935e4de8fdf.

* rpath specific to macos

* use override

* forgot to set rpath...

* remove #1045 related changes

* integrate #1052

* allow version override

* minor improvements

* msvc changes

* fix typo

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-11-17 12:00:36 -08:00
Emmett Lalish 18101057b5 fixed properties (#1051)
* fixed properties

* fix formatting

* fix compile

* consolidate debug functions
2024-11-16 21:26:14 -08:00
pca006132 7de71d4c93 manifold alias (#1049) 2024-11-16 10:01:51 -08:00
pca006132 d9bc83a7ad free temporary vectors earlier (#1047)
* free temporary vectors earlier

* clear some more

doesn't seem very effective though
2024-11-16 08:27:11 -08:00
Jimandpca006132 ee32b3025d Add C bindings for Triangulate() (#1034)
* Adds C bindings for Triangulate()

* Triangulate() returns a std::vector<ivec3>, which I tried to respect.
  Since ivec3 is 3 int's, I have
  manifold_triangulation_tri_verts(void *mem, ...)
  copy assuming mem has space for 3 * num_tri int's

* NOTE: manifoldc.cpp now includes "manifold/polygon.h"

* NOTE: I couldn't figure out manifold_destruct_triangulation(...)
  Also, I don't understand the use case(s) of the destruct functions
  (literally don't understand; I am sure they exist)

* Usage code:

  ```
  ManifoldTriangulation *triangulation = manifold_triangulate(manifold_alloc_triangulation(), polygons, epsilon);
  size_t num_tri = manifold_triangulation_num_tri(triangulation);
  int *tri_verts = (int *) manifold_triangulation_tri_verts(malloc(num_tri * 3 * sizeof(int)), triangulation);
  manifold_delete_triangulation(triangulation);
  ```

* fixing formatting

* fix destruct

* add triangulation CBIND test

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2024-11-17 00:15:57 +08:00
Emmett Lalishandpca006132 e7e0780114 Disable exceptions by default (#1044)
* removed ASSERT

* removed MANIFOLD_EXCEPTIONS

* fix CI

* fix performance regression

* avoid copying in Is2Manifold

* back to two assert flavors

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2024-11-15 11:54:26 -08:00
Emmett Lalish 0b957bf8df added linalg docs (#1043) 2024-11-14 15:34:08 -08:00
pca006132 b26007e76c remove oneDPL (#1042) 2024-11-15 04:04:57 +08:00
Emmett Lalish 0f7cecaf1a SDF boundary cleanup (#1037)
* tighten test

* fixed SDF eggcrate

* addressing feedback
2024-11-13 22:08:45 -08:00
Emmett Lalish a41a361123 remove deterministic flag (#1033)
faster > deterministic
2024-11-13 21:15:35 -08:00
pca006132 74f340a8e7 always use 64-bit integer for MeshGL64 (#1038)
* always use 64-bit integer for MeshGL64

* fix 2

* c binding fixes

* c binding fix 2

* fix
2024-11-13 09:57:19 -08:00
pca006132 12b43f50c4 fix atomic exchange template deduction (#1035) 2024-11-12 09:13:05 -08:00
Chris Mayo 66bca6e3fd Fix target include directory when building as a submodule (#1029) 2024-11-08 12:44:21 -08:00
Emmett Lalish 55722a0ce3 Update Readme (#1025)
* nanobind link

* addessing feedback

* added users and packages

* dependency table

* fix merge docs

* fix build

* added docs
2024-11-07 11:19:28 -08:00
Emmett Lalish 2b29dca503 Update bindings (#1023)
* add setTolerance to WASM bindings

* update manifold_revolve C API

* fix formatting
2024-11-05 14:40:25 -08:00
Emmett Lalish 82140d3479 Remove GetProperties() (#1022)
* organize member docs

* remove GetProperties

* updated WASM bindings
2024-11-05 13:34:45 -08:00
Emmett Lalish 8e9bdb2614 updated linalg docs (#1020)
* updated linalg docs

* add missing icon, update readme deps
2024-11-04 00:04:36 -08:00
pca006132 4076b14dd6 fix data races (#1019) 2024-11-04 13:29:43 +08:00
pca006132 f11ed8da6c bindings update (#1018)
* bindings: add MeshGL64 C bindings

* bindings: fix leak and address sanitizer issue

* bindings: add Mesh64 to python

* bindings: fix c binding 64 bit index type

* bindings: fix python 64 bit index type

* bindings: fix python again

* bindings: formatting
2024-11-02 13:59:12 +08:00
Emmett Lalish a8a90c7d34 switched property indexing from vector to scalar (#1016)
* switched property indexing from vector to scalar

* updated WASM bindings

* update python bindings
2024-11-01 11:45:32 -07:00
Emmett Lalish 649b69a140 Update README (#1015)
* add forgotten images

* update README
2024-10-29 15:54:16 -07:00
Emmett Lalish 97cb8dbffa Doxygen updates (#1014)
* reorganize docs

* further organization

* fixed mismatched names

* fix clang-format 19 diff

* fix clang-format 19 diff again
2024-10-29 15:19:36 -07:00
Emmett Lalish d1a8de2e00 update tolerance defaults (#1007)
* update tolerance defaults

* remove kFloatTolerance

* update test

* quick fix

* added tolerance test
2024-10-29 09:42:54 -07:00
Emmett Lalish a0d2c3ab25 remove CGAL comparisons (#1009)
* remove CGAL

* fix format
2024-10-22 22:09:50 -07:00
Emmett Lalish 80221a3511 Precision renaming (#1008)
* rename refineToPrecision

* rename kTolerance

* the precision rename
2024-10-22 15:28:09 -07:00
pca006132 5ba3c24112 [WIP] Add tolerance value (#997)
* renaming precision to uncertainty, and hiding

* add tolerance

* rename uncertainty to epsilon

* use epsilon instead of tolerance

* guard simplify topology for SetEpsilon

* format

* remove conflict markers

* fix builds

* use epsilon for barycentric

* prefer using epsilon

* update python bindings

* address comments

* format

* remove SetTolerance

Tolerance value is set before the mesh is created, when SimplfiyTopology
cannot be called.

* read/write MeshGL tolerance field
2024-10-22 08:29:11 -07:00
Clifford Yapp 85675f2723 Need to fix compilation flag lost in CMake refactor (#1005)
Looks like we lost the O3 flag always being on.  This was good for some
fun trying to figure out why performance had dropped off so dramatically
- turned out it was simply because prior builds had NEVER been without
O3, so all builds - Debug or Release - were optimized.

On the other hand, there are arguments for and against O3 with debug
builds.  The optimizations complicate/obscure debugging, but it's also
an order of magnitude slower running.  Rather than be completely either
or, this PR defines a MANIFOLD_OPTIMIZED variable that a user can
specify to ON explicitly.  Build types Release and RelWithDebInfo will
always have O3, and for other cases it'll be up to whether the user has
explicitly requested optimized building with MANIFOLD_OPTIMIZED.
2024-10-22 08:22:59 -07:00
Emmett Lalish e647093bbe removed RotateUp and uint (#1000)
* removed RotateUp and uint

* avoid new API

* simplify

* change error reporting

* fixed error reporting

* fix compile

* addressing feedback
2024-10-20 21:48:26 -07:00
Emmett Lalish 54517b7546 Separate coplanarity from faceID (#991)
* separated faceID

* clean up tests

* fix AsOriginal

* reenable CreateFaces and keep mesh relation intact

* fix compilation

* updates docs
2024-10-19 10:04:08 -07:00
Chris Mayo e45df5dd3a Support googletest with the CMake package name GTest (#1002) 2024-10-19 10:01:45 -07:00
Clifford Yapp 97423a07f9 Clean up some unused variables (#1001) 2024-10-19 10:00:31 -07:00
Clifford Yapp 0a51ae2a0c Define a test for simple offset booleans (#998)
* Define a test for simple offset booleans

This is intended to check for performance regressions - the BRL-CAD
logic performing this operation using v2.4.5 ran considerably faster.

Note - removing in-loop assertions for triangle count > 0 with the
intermediate forms greatly increases the speed, but they are placed
there deliberately to simulate what BRL-CAD does to validate the output
of the booleans on an incremental basis.  In the event of a boolean
failure or invalid output being produced for any reason, we want to be
able to capture the *exact* inputs responsible - which means we need to
check after each boolean operation to validate the results and catch
any bad intermediate states at the time they are produced.

* formatting

* More formatting

* Per suggestion from elalish, simplify edge pair setup

* Switch to a Status() check - appears to have the same slowing effect.

* Move the checks out of the loops

* Use initializer lists for better compactness

* Restore in-loop checks with explanation

* Simplify with += syntax.

* Revert "Simplify with += syntax."

Getting CI failure, see if this had an impact somehow...

This reverts commit 55ca9a9dea3f3fddad14ea7858603e35a4a2f629.

* Revert "Use initializer lists for better compactness"

Still not passing?? Revert another commit.

This reverts commit 89065dd7833d55933c8367f4734091ae43b4517e.

* Reapply "Simplify with += syntax."

OK, starting from a passing state, see if this works.

This reverts commit ba6027e003e4d6d64deda35eed8c540bff4a0f80.

* Break the initialization changes down - start with faces array.

* Initialize pts array

* Ah ha. There it is. Indexing problem with second half of pnts setup.

* formatting

* Test

* Revert "Test"

This reverts commit c137a4825824c43a56ba51ea32b073072c9af20e.

* See if the failure triggers if we only check once at the end.

* Revert "See if the failure triggers if we only check once at the end."

This reverts commit 35f0eb0c96685defbfb44910f75bf5974b5c754e.
2024-10-18 21:11:55 -07:00
pca006132 e718288cbf fix python binding (#987)
* python: fix type casters and warp batch

* python: allow docstring override

* update nanobind version

* python: add docstring_override.txt

* python: fix example

* version limit for nanobind
2024-10-19 01:07:18 +08:00
pca006132 005102c1a3 CI: remove glm (#992)
CI: removed glm and change naming
2024-10-16 23:08:20 +08:00
Clifford Yapp 6ade357e4b Make sure _MSC_VER is defined before checking (#990)
Avoids the following:

error: "_MSC_VER" is not defined, evaluates to 0 [-Werror=undef]
   59 | #if _MSC_VER <= 1900
      |     ^~~~~~~~
2024-10-16 13:13:11 +08:00
Emmett Lalishandpca006132 e06765346e Replace GLM with linalg (#984)
* added linalg

* removed GLM

* fix matrix multiplication

* more matrix constructors

* fixed more functions

* fixed matrix notation

* more fixes

* fix ostream

* further fixes

* added more rotation functions

* compiles

* some fixes

* fix2

* fix build

* another warning...

* fix install

* for wasm?

* no warning for unknown options

* proper fix?

* remove legacy warning flags

* format

* misc constexpr

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2024-10-15 11:22:03 -07:00
pca006132 bd52d301ff Minor cleanup (#988)
* documentation update

* remove some todo

* format
2024-10-16 00:58:49 +08:00
Chris Mayo 370375ebf0 CI: libthrust-dev dependency no longer needed (#986) 2024-10-14 10:20:03 +08:00
Taylor Holliday 9d3fd48b01 Fix comment (#985)
`Mesh` was removed in #950
2024-10-12 13:38:40 +08:00
pca006132 451829f1d1 Collider optimization (#978)
* vec changes

* collider optimization

* fix tracy

* slight optimization for polygon

* cleanup

* cleanup

* CMAKE_CXX_FLAGS should also be a string

* slightly optimize polygon

* should be lowest instead of min

* address comments

* update MANIFOLD_PAR guard

* try to fix black CI
2024-10-09 09:05:04 -07:00
Clifford Yapp 3febd48dcd Always check in the code for explicit parallel/serial choice (#980)
* Check at compile time that MANIFOLD_PAR is set to either P (parallel) or S (series) mode.

* Switch from 'P' and 'S' to 1 and -1

* fix formatting

* Put the MANIFOLD_PAR poison pill in an internal header.

We want this check for Manifold compilation as a cross check on our CMake
logic, but this way client codes will work by default without being forced to
set it explicitly.
2024-10-07 13:00:57 -07:00
pca006132 658d3e037a format cmake files (#975)
* format cmake files

* update format.sh

* fix linker flags for emscripten

* update readme

* use gersemi

* actual format

* should be public instead of interface

* make test work without download

* simplification

* format
2024-10-05 00:36:24 +08:00
pca006132 cc1e97442a simple cleanup (#973)
* cleanup files

* hide some public APIs

* merged CMakeLists.txt for headers

* fix build

* include cleanup

* include cleanup again

* address #944

* python cmake install

* iostream is still needed

* fix nix
2024-10-03 00:10:07 +08:00
Nick Ufer 5719926db8 feat: exposes allocation functions via C bindings (#968)
* feat: exposes allocation functions via C bindings

* test(manifoldc): replaces malloc calls with alloc function calls
2024-09-30 17:19:42 -07:00
Clifford Yapp 3c8d486e36 Try to restore C bindings as a separate library build. (#967)
* Try to restore C bindings as a separate library build.

* Doesn't look like we need this define here now.

* meshIO isn't a separate library

* Right - manifoldc.h is in a different location
2024-09-29 21:27:31 -07:00
Brandon 5a157a07fe Wasm: update CrossSection.toPolygons() return value to match type (#963)
* Update return value to match type

* Format
2024-09-29 21:12:53 -07:00
Clifford Yapp acb4f8c5e4 Whoops - looks like Doxyfile needs to be top level (#965) 2024-09-29 10:58:55 -07:00
Clifford Yapp 5cab42f165 Attempt at simplifying the file structure and CMake build (#961)
* Initial file moves, stage logic for src/CMakeLists.txt rework

* Start working on getting reorganized files to build, as well as doing some rework on the CMake logic

* remove unused libs, propagate dependency includes

* Ignore common temporary files and build directories

* Just build the C bindings, since this is already a C++ compile - reduces complexity.

* Fixes for python binding building

* Require C++17, don't use extensions

* Remove C binding CMake file

* Remove extra libraries from link line

* Try to set up the manifoldConfig file to just find the required dependencies, without downloading

* Go looking for glm and Clipper2 installed with Manifold first (untested)

* Use a bundled Clipper2 copy instead of downloading

This preserves the ability to disable cross_section (and thus the use of
clipper) but replaces the Download-and-Build fetch with a bundled source
copy to avoid the network connection.  If we go this route, we're also
building Clipper2 directly into Manifold so client libraries don't have
to worry about linking it in.

* Remove option and directories no longer present

* Remove option no longer present.

* Put back the default .gitignore

* Add a few more patterns

* Revert "Use a bundled Clipper2 copy instead of downloading"

Feedback is we don't want to bundle clipper, so revert inclusion

This reverts commit 68e8e3ccceb35c87fc84626e28a95468781be8e3.

* Just build polygon as part of the main library

* Correct some library linkages

* Tweak exported target file install location - this seems to be what's expected on Linux, but will it work for other platforms?

* clang format fixes

* Just include FetchContent up front

* Fix include for minimize_testcase

* formatting

* Add sstream header

* Need to disable more code when cross_section is off

* Don't try to build the bindings unless MANIFOLD_CROSS_SECTION is on.

* formatting

* manifoldc folded into libmanifold

* Fix location of test-cmake.sh script

* Try using previous install dir for Apple/MSVC manifoldConfig.cmake file

Per https://cmake.org/cmake/help/latest/command/find_package.html it
looks like the search is different on different platforms.  Need to look
at the available patterns more closely, but for now try using what's
working for the tests in master branch.

* Fix conditional test, add namespace

* Fix header install

* Remove unused bindings filter from lcov line, Remove unused MANIFOLD_CBIND option.

* Don't do tests if downloads are off, since they require googletest.

* elalish spotted that this isn't a public header - fix.

* Try out CMake intenting based on vim + file footer with settings.

* Rename sh directory to scripts per suggestion from pca006132

* Per pca006132, remove unnecessary find_package call for oneDPL.

* Simplify MANIFOLD_PAR to an OFF/ON setting

Per suggestion from pca006132, changed CMake logic to be ON/OFF, and
simplified the C++ defines to test for defined/undefined vs. checking
for a specific value.

* conv.h is also a private header

* Hmm - looks like flake naming was coming from MANIFOLD_PAR - try translating the settings.

* Whoops - need to translate the active value not the cached value.

* OFF is still defined, so make sure we catch that case too

* Explain multiple calls to get_filename_component

* Move parallel.h to src

* Move iters.h to src

* Place optional components in subdirectories.

* Correct path in gen_docs.py

* Fix the file location for python bindings.

* Revert "Move iters.h to src"

parallel.h was apparently specifically public - undo hiding of iter.h

This reverts commit ce3e45a9a6af42393fbb4b220e9bb2c8ebabd901.

* Revert "Move parallel.h to src"

parallel.h was apparently specifically public

This reverts commit 08fbcdf8e95d6afd1eb3617eed9c90caa6744b0d.

* Footers, indenting adjustments for other CMake files
2024-09-29 10:23:42 -07:00
Nick Ufer ea91395878 feat: exposes Quality::ResetToDefaults() via C bindings (#964) 2024-09-29 09:53:34 -07:00
Emmett Lalish 4d7613bd4f Add RefineToPrecision (#938)
* added RefineToPrecision, but needs work

* fixed 1-d triangulations

* added test of precision

* fixed approximation

* added bindings

* cleanup

* forgot to include
2024-09-27 17:19:29 -07:00
Emmett Lalish 9f62dbb048 Remove mesh (#950)
* removed a bunch of mesh

* compiles

* fix test

* cleanup

* fix compile issues
2024-09-27 16:46:55 -07:00
pca006132 31d6da0973 use nanobind builtin stubgen (#949)
* use nanobind builtin stubgen

* fix formatting

* python format

* update nix for newer nanobind

* remove recursive clone flags

* fix extrude.py

* fix install

* add version checks for stubgen

* exclude_from_all

* fix python

* disable nix js variant for CI
2024-09-27 08:02:56 -07:00
Taylor Holliday 359274d01b use std::swap (#956) 2024-09-27 01:24:05 +08:00
Taylor Holliday a24cca1f06 remove face index on HalfEdge (#955)
* remove face index on HalfEdge

doesn't appear to actually be used

* remove face index on HalfEdge
2024-09-26 14:07:11 +08:00
Taylor Holliday c1d96a6404 Fix comment (#954) 2024-09-26 10:58:02 +08:00
Chris Mayo a8e533c790 Use CMAKE_INSTALL_LIBDIR when setting installed rpath (#948) 2024-09-24 10:39:07 -07:00
Chris Mayo fa6a632545 Install manifold.pc under CMAKE_INSTALL_LIBDIR (#946) 2024-09-23 07:53:28 -07:00
pca006132 cff8d903e8 Misc. changes (#940)
* propagate errors in boolean operands

* propagate error for transform and compose

* use 64-bit integers for MeshGL64

* use size_t for public APIs

* address comments

* fix signed compare errors

* fix
2024-09-21 09:37:39 -07:00
pca006132 fcc707585b removed face from halfedge (#939)
* removed face from halfedge

* address comments and format
2024-09-19 10:07:05 +08:00
Emmett Lalish 456b8ceb3b fix deployment (#937) 2024-09-18 14:57:16 +08:00
Neil PankeyandEmmett Lalish 9257734c0f wasm: fix CrossSection slice and project bindings (#930)
* wasm: fix CrossSection slice and project bindings

* wasm: SwissCheese example to demonstrate `slice`

* format.sh: ignore node_modules

* replace swiss cheese with auger

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-09-17 23:24:30 -07:00
Emmett Lalish ced53e2109 Make create faces optional (#935)
* avoid createFaces calls, need to separate relation from coplanarity

* simplified SetProperties

* fixed merge test

* simplified calculateCurvature

* fixed WASM + cleanup

* more cleanup
2024-09-17 14:59:05 -07:00
pca006132 a017f0e614 Normal calculation optimization (#934)
* normal calculation optimization

* move test around
2024-09-17 11:15:36 -07:00
pca006132andEmmett Lalish 22c66051df add MeshGL64 (#927)
* add MeshGL64

* allow SetProperties to zero property channel

* address comments

* minor test updates

* fixed scallop

* address comments

* fix test

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-09-16 12:35:55 +08:00
pca006132 4760ba3a33 use mxe to cross compile to windows (#924)
* MXE warning flags

* actually use mxe

* try again

* don't warn format when on mxe

* fix mxe

* disable c binding as well

* we can't run test here

* find threads first

* try harder
2024-09-13 01:26:18 +08:00
Emmett Lalish c75f068a03 update README links (#920) 2024-09-10 13:20:10 -07:00
MarkoandEmmett Lalish 51ee91e356 implemented typedoc documentation (#907)
* implemented typedoc documentation

* exclude docs output dir

* added bindings/wasm/docs dir to git ignore list

* Added js docs to GitHub deploy workflow

* include manifold global types

* js docs - added missing comments

* fixed formatting

* added missing comments

* added missing docs

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-09-10 11:55:15 -07:00
Henner Zeller 2e41fef203 Fix test-{cmake,pkgconfig}.sh to create, not append, to test files. (#919)
The test.cpp file and the CMakeLists.txt/Makefile were created with
the '>>' append in shell instead of '>'. Which means these files were
growing on multiple invocations if *-consumer was not deleted
between calls (which means that the test would fail the second time
around).

While at it, add the manifold/parallel.h include also to the
pkg-config test.

Make all includes 'system-header' style with angle brackets, as this
is what users of the library would use.
2024-09-11 00:27:49 +08:00
pca006132 9124a68032 change directory structure (#918)
* change directory structure

* expose parallel.h

* format

* missing functional

* format

* fix install
2024-09-10 07:56:43 -07:00
Henner Zeller 6f009ca13f Make cross_section an installable library. (#915) 2024-09-07 13:48:21 +08:00
pca006132 5b2a95e6d2 use custom file format for polygon tests (#913)
* use custom format

* try fix windows

* emscripten stuff

* address comments

* remove test filter

* fix format
2024-09-05 07:56:50 -07:00
Brandon e9c065bd5e Fix missing 'this' for property access in wasm bindings (#914)
* Fix missing 'this' for property access

* fix formatting
2024-09-05 07:47:39 -07:00
reinux b95b88f026 Enable MeshIO/MANIFOLD_EXPORT in flake (#912) 2024-09-04 16:10:16 +08:00
pca006132andEmmett Lalish 492c3a8b23 Double precision (#895)
* added configuration summary

* use custom math type

* use std function if possible

* build success

* fix js binding

* format

* use better primitives

* use old precision for polygon tests

* fix polygon CCW check

* temp-fix tests

* add simpler failing test case

* refactored CutKeyhole

* fixed tests

* more keyholing fixes

* truncate long debug output

* shrink SDF blobs test

* fixed WASM and disabled zebra

* fix hull

* cleanup a bit

* fix wasm

* CI cleanuo

* reenable zebra

* upped precision and fixed tests

* fix format

* disable zebra test on windows

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-09-03 10:19:11 -07:00
Kushal-Shah-03 d0d5106add Added DisabledFaceTest (#908)
* Added the DisabledFaceTest

* Added IsEmpty() check
2024-09-02 08:19:48 -07:00
Emmett Lalish fbf07bf92a SDF collapse (#894)
* refactor to collapse verts

* fixed missing triangles

* tests pass

* cleanup, docs

* Moved LevelSet from MeshGL to Manifold

* update bindings

* minor fixes

* more python fixes

* update clang-format version

* updated sinesurface test

* revert unneeded change

* fixed WASM

* update Python

* remove opposed triangles

* update format version

* fix format

* remove unreferenced verts

* fix 4-manifolds

* reduce blob resolution

* avoid collapsing thin surfaces

* cleanup, tighten tests

* compromise heuristic

* update test

* slight heuristic tweak and update docs

* loosen genus test
2024-09-01 15:52:47 -07:00
Kushal-Shah-03 a0e4d1b558 Cleaning Hull Algorithm Code (#904)
* Removing redundant code (structs and function calls)

* Removed unecessary comments

* Removed ConvexHull, CCW and getConvexHullAsMesh

* Moved Impl::Hull to quickhull.cpp

* Modified some comments
2024-08-24 18:11:45 -07:00
Emmett Lalish db4470c3f3 fix accidentally disabled tests (#902) 2024-08-20 10:36:38 -07:00
Kushal-Shah-03andpca006132 fd485f62d5 Using Inbuilt Datastructures (#893)
* Removing VertexDatSource structure and Replacing it with Vec

* Integrated HalfEdge datastructure of Manifold (in progress)

* Changed QuickHull Class to accomodate VecView

* Formatting changes and modified erase

* Made the suggested changes

* Impl Hull function working

* Formatting

* Adding comments and making code more readable

* Made changes, added manifold namespace, removed multiple lines to add halfEdge

* Added the fix and corrected face indices to now match the expected indices (i.e edge index/3)

* Formatting

* Added comment as TODO and replaced std::cout with DEBUG_ASSERT

* move things around

* remove custom HalfEdge data structure

* Added MANIFOLD_EXCEPTIONS

* Revert "Added MANIFOLD_EXCEPTIONS"

This reverts commit b46e7cec59ae95254723a5b87e32d3c008c8a23f.

* fix include

* format

* Made the suggested changes

* Fixed : processing disabled faces bug

* Removed unecessary comments

* Trying atomic for j

* Suggested fix

* Atomic Add should work

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2024-08-20 09:26:35 -07:00
Henner Zeller 74e15b1574 Add *.pc file for easier use in non-cmake environments. (#901)
pkg-config is the common way how to find cflags and library flags
for projects not using cmake.
2024-08-19 10:04:06 +08:00
Chris Mayo d4fb82d4d1 CI: Cancel definition of NDEBUG in TBB build jobs (#899)
NDEBUG disables assert from cassert, as used by GLM.
2024-08-16 11:56:56 -07:00
Chris Mayo 952546b761 Fix hull_test isMeshConvex() vertices check (#898) 2024-08-14 12:17:31 -07:00
Chris Mayo e26e01a231 Add a cmake option for CrossSection (#888)
* Add a cmake option for CrossSection

* Add MANIFOLD_CROSS_SECTION to manifoldConfig.cmake

* Document MANIFOLD_CROSS_SECTION in README

* Fix default MANIFOLD_JSBIND value

* Enable tests that run without CrossSection

* Remove cross_section.h from tests that don't use it

* Add ifdef to tests using CrossSection

* CI: Build without CrossSection

* CI: Separate Python bindings test
2024-08-13 16:03:07 -07:00
Emmett Lalish 844334380a Fix smoothing regression (#897)
* tighten test to catch coplanar violation

* fixed regression
2024-08-13 11:09:29 -07:00
Chris Mayo e01d31611e Remove quickhull from README (#892)
Not used since:
af2304d ("Removing QuickHull dependency (#881)", 2024-08-08)
2024-08-10 11:33:46 -07:00
Kushal-Shah-03 af2304d305 Removing QuickHull dependency (#881)
* Removed QuickHull Dependency

* Replaced Vector3 with glm::vec3 and VertexDataSource (and formatting)

* Revert nanobind

* Removing Templates (since using glm::vec3 so only need float)

* Formatting

* Converted glm::vec3 to glm::vec3d and float to double

* Fixed Formatting and Removed third_party

* Made requested Changes (m_,comments,removing unnecessary code)

* Resolving Conflict

* Fixed std::move warning (Shouldn't affect performance, ideally)

* Reverted third_party directory in bindings

* Reverting std::move

* Corrected std::move and added glm::dot to calculate distance squared

* Reverted m_epsilon due to epsilon and m_epsilon

* Fixed some Names

* Formatting

* Removed all unecessary m_

* Fixed manifold.yaml

* Added headers and pragma once

* Modified License

* Formatting

* Activated the two failing tests and added the fix for the algorithm

* Modified gitmodules to remove third_party

* Removed nanobind
2024-08-07 14:30:57 -07:00
Kushal-Shah-03 75c340f4f4 Testing Framework for Hull (#875)
* Testing Framework for Hull

* Fixed Formatting

* Made Changes Based on the Review

* Fixed Formatting

* Fixed Formatting Using clang-16

* Reduced NumSegs for Sphere to fit within memory

* Reducing NumSegs Further

* Reducing SphereSegs Further

* Fixed Formatting

* Check for Valid Convex Mesh

* Added Check for Valid Convex Hull, and two failing cases with reduced points

* Added DISABLED in tests and isMeshConvex check in hull_tests.cpp
2024-08-07 08:18:57 -07:00
Jonathan Hogg 7ad7703964 C bindings are required to build Python package (#883)
* Don't attempt to build C bindings for Python package

* Add workflow stage to do a test build of the SDist
2024-08-03 20:40:45 +08:00
Chris Mayo a37d59ef5b Use system nanobind if installed (#880)
* Use system nanobind if installed

* Add a pyproject.toml build-time dependency on nanobind

* Add nanobind to Nix buildInputs

* Replace nanobind submodule with FetchContent

* Remove nanobind directories from tool.scikit-build sdist.exclude

* Use Nix python3.withPackages for nanobind instead of buildInputs
2024-08-03 12:03:34 +08:00
Emmett Lalish 3bf7f4b33d SDF Refactor (#876)
* added static LevelSet

* removed Morton codes

* store voxels

* new levelset working

* cleanup

* moved LevelSet to MeshGL

* small updates

* removed sdf library

* updated WASM bindings

* cleanup FindSurface

* updated test

* fix formatting

* loosen test slightly

* off-by-one

* fix sizing

* tweak test
2024-07-26 09:17:51 -07:00
pca006132 a93c0c6ca1 make collider header-only (#871)
* pch related changes

* weird error

* try fix error

* disable pch for msvc

* header-only collider

* fix

* fix format

* cmake changes
2024-07-26 01:30:03 +08:00
Ben FrantzDale ccb682fc44 Rename operator""_z to _uz per P0330R8, and move into namespace manifold (#869)
Rename operator""_z to _uz per P0330R8, and move into namespace manifold.
2024-07-18 13:38:46 -07:00
Emmett Lalish 0617d344bc fix shadowing warning (#867) 2024-07-16 11:10:09 -07:00
Emmett Lalish a6ee198b88 SDF interpolation (#866)
* start of test

* started FindSurface

* beautiful smoothing

* implemented ITP root finding

* improved ITP

* added precision to SDF API

* test comparison to linear interpolation

* fixed quad 4-manifold edge

* fixed bug

* optimized ITP

* fix NaN tangents
2024-07-16 10:22:59 -07:00
pca006132 dad73e1bf4 Fix debug mode msvc build error (#863)
use std::vector for list iter
2024-07-13 13:46:07 +08:00
pca006132 610fd3a073 custom parallel algorithms based on tbb (#857)
* most of the algorithms done

* conditional compile tbb stuff

* format

* optimize a bit

* unique implementation

* tiny optimizations

* remaining ones

* update clang-format

* don't check version here

* radix sort

* fix compiler error

* fix another compiler error

* fix again

* again

* another fix

* add benchmark

* fix

* some changes

* format

* fix

* fix msvc

* misc

* remove uninitialized_fill and copy

* infer policy automatically

* tuned better?

* signedness thing

* format

* proper iter

* documentation and checks

* address comments

* fixed comments
2024-07-13 13:04:17 +08:00
Emmett Lalish 46f199c200 update to emscripten 3.1.61 (#860) 2024-07-10 12:40:54 -07:00
Clifford Yapp fa1a342e1c Avoid Wundef trigger (#861) 2024-07-09 20:50:37 -07:00
pca006132andEmmett Lalish e46938b498 Bye-bye thrust (#856)
* transform iterator

* strided range

* fix pointer/reference to strided range

* fix iter

* avoid thrust

* removed copy_if with stencil

* get rid of transform_reduce

* done!

* use parallel unique

* address comments

* trying to get OneDPL working on Mac

* running fast on mac again

* try fix mac oneDPL build

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-07-08 09:28:02 -07:00
Emmett Lalishandpca006132 ddb3cbeb3a Remove fancy iterators (#852)
* removed zip from collider

* removed zip from boolean result

* removed zip from boolean3

* refactored constructors

* refactored mesh_fixes

* refactored impl

* ≈removed reduce_by_key

* more zip removal

* refactored properties

* refactored shared

* refactored smoothing

* refactored sort

* refactored subdivision

* refactored polygon

* removed zip completely

* cleanup

* transform iterator

* strided range

* format

* fix pointer/reference to strided range

* avoid thrust copy

* fix iter

* correct location

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2024-07-07 08:37:43 -07:00
pca006132 b817d07a9d chores (#833)
* fix comparison between integers with different signedness and implicit narrowing

* fix compilation error and formatting

* allow build with exception disabled

* CI for building without exception

* format

* another fix

* msvc stuff

* fix python bindings

* update nix

* formatting

* old llvm somehow likes this better

* fix bug

* rename ASSERT into DEBUG_ASSERT

* remove unused file

* use ASSERT and remove obsolete limits

* polygon: change to size_t

* format

* fix sign compare

* install optional_assert.h

* address comments

* fix

* fix

* fix

* fix format
2024-06-28 09:36:54 -07:00
Emmett Lalish 85d71f5cef removed cross_section dependency from manifold (#850)
* removed cross_section dependency from manifold

* fix compile errors

* updated JS bindings

* fixed python bindings
2024-06-27 12:19:31 -07:00
Emmett Lalish ab1a96a96a Fix another zebra triangulation (#851)
* added test

* fix bug, reorganize polygon tests
2024-06-25 09:51:54 -07:00
Chris Mayo 7b10590d89 [test] Smooth.SineSurface fails - the difference between prop3.volume and 8.43 exceeds 0.01 (#848)
Adjust test Smooth.SineSurface prop3.volume to 8.44
2024-06-18 11:42:41 -07:00
Emmett Lalish 5e7a576cea v2.5.1 (#847) 2024-06-14 13:48:05 -07:00
Emmett Lalish 80a5f2e0c5 fixed property smoothing (#845) 2024-06-14 11:41:26 -07:00
Emmett Lalish 67c4647464 Several smoothing fixes (#844)
* fixed several bugs

* fixed average rotation
2024-06-13 15:26:57 -07:00
Emmett Lalish edd149e753 fixed missing default params in JS bindings (#843) 2024-06-13 09:48:06 -07:00
Emmett Lalish b5c175162a fixed folded normals smoothing (#842) 2024-06-13 09:32:11 -07:00
Clifford Yapp 1df22bee48 Clear compiler warning from BSD. (#841)
* Clear compiler warning from BSD.

* Fix formatting per Github CI
2024-06-13 07:53:23 -07:00
Emmett Lalish d5c0418309 removed animation from roundedFrame (#839) 2024-06-12 14:28:35 -07:00
Emmett Lalish 7a5d5421d4 Fix PyPI again (#838)
* test all OSes

* try older windows

* updated release docs
2024-06-12 13:44:16 -07:00
Emmett Lalish 69437096d0 update cibuildwheels (#837) 2024-06-12 10:33:29 -07:00
Emmett Lalish 3931b3296d v2.5 (#836)
update to v2.5.0
2024-06-12 09:22:41 -07:00
Emmett Lalish 62d0f25ce0 Flair normals (#835)
* added comments

* flaired normals working

* updated tests
2024-06-11 11:38:49 -07:00
Emmett Lalish 1bd16f4c02 added convex polygon triangulation fast path (#829)
* added convex polygon triangulation fast path

* fixed transform_reduce

* fix sliver convexity test
2024-06-10 14:01:35 -07:00
Emmett Lalish 3afe846570 Fixed backward zebra polygon (#834)
fixed two more bugs
2024-06-08 17:31:33 -07:00
Emmett Lalish 12b9494fdd Fix the zebra polygon (#832)
* add Zebra polygon stress test

* fixed Zebra polygon - 4 bugs

* fix MSVC build

* try MSVC fix
2024-06-08 13:00:26 -07:00
Emmett Lalish 0577aabe7f Optimize triangulator with collider (#830)
* implemented - weird linker error

* fix linker

* fixed empty collider

* improved perf and cleanup

* add comments

* more docs
2024-06-05 10:28:59 -07:00
Emmett Lalish bdae9dd11a Improve smooth (#828)
* add max curvature tests

* back to radial basis functions

* fix longWay slerp

* cusps eliminated

* cleanup
2024-06-04 09:04:47 -07:00
Chris Mayo 5f4059ab1f Create a release file containing submodules (#825) 2024-05-31 13:30:51 -07:00
Emmett Lalish 9cb3969f70 Clean up test files (#826)
* match names to files

* GLB naming

* tighten matter tests

* partial revert

* stop skipping my job

* stop cancelling my jobs

* revert CI changes
2024-05-30 16:18:00 -07:00
Hiroshi OgawaandEmmett Lalish 4fb3520287 refactor(wasm/examples): support export models without web worker (#822)
* refactor(wasm/examples): support `evaluateCADToModel` without web worker

* test: copy test

* chore: update vitest

* chore: revert some

* chore: revert more

* refactor: simplify worker-wrapper.ts

* chore: revert format

* test: fix cleanup

* refactor: single module

* chore: format

* chore: revert .vscode/settings.json

* chore: format

* test: tweak

* chore: format

* chore: rename

* refactor: minor

* chore: remove wrong lock file

* added resetToCircularDefaults

* cleanup

* added headers

* removed vitest/web-worker

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-05-28 13:50:25 -07:00
Marius KintelandEmmett Lalish 376d317f14 Add polygon test showing bad tessellation result + fix (#824)
* Add polygon test showing bad tessellation result

* fixed ear cost

---------

Co-authored-by: Emmett Lalish <elalish@google.com>
2024-05-28 11:21:14 -07:00
Emmett Lalish bf18c9f759 Improve smoothing of flat faces (#821)
* Improve SmoothByNormals

* improving SmoothByNormals

* parallelize

* simplify SmoothByNormals

* added DistributeTangents

* fixed NaN bug

* use normals for sharp smoothing

* remove LinearizeTangents

* distrubute tangents for SmoothOut

* Revert "remove LinearizeTangents"

This reverts commit 7276609dc245c26b9a720b5bda1f018d64639954.

* fixed SmoothOut and added tests

* expanded test

* update wasm test
2024-05-28 11:06:27 -07:00
Emmett Lalish cdd1475fe1 added project test (#818)
* added failing test

* update test
2024-05-28 08:46:58 -07:00
Chris Mayo 55a0221f9b CI: use GCC 14 in Linux build (#816)
* CI: use GCC 14 in Linux build

Ubuntu 24.04 packages GCC 13. GitHub add GCC 14 to their runner.

* Ignore lcov mismatch errors

Processing ./test/CMakeFiles/manifold_test.dir/smooth_test.cpp.gcno
geninfo: ERROR: mismatched end line for _ZN23Smooth_Tetrahedron_Test8TestBodyEv at /home/runner/work/manifold/manifold/test/smooth_test.cpp:26: 26 -> 39
	(use "geninfo --ignore-errors mismatch ..." to bypass this error)
2024-05-27 19:02:47 -07:00
pca006132 d78b1e2042 update clipper2 (#817)
* update clipper2

* test

* test2

* test3
2024-05-25 00:41:31 +08:00
Chris Mayo 562ff02232 Update glm to 1.0.1 (#819)
* Update glm to 1.0.1

* Define GLM_ENABLE_EXPERIMENTAL for glm/gtx/quaternion.hpp

With GLM 1:

In file included from manifold/manifold/src/manifold/src/smoothing.cpp:15:
manifold/manifold/build/_deps/glm-src/glm/gtx/quaternion.hpp:24:3: error: "GLM: GLM_GTX_quaternion is an experimental extension
  and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it."
2024-05-23 11:56:14 -07:00
Peter Harman 8e2b1dd485 Add manifold_meshgl_merge to c bindings (#820)
Add manifold_meshgl_merge to call MeshGL::Merge() on a duplicate mesh via the c bindings
2024-05-24 00:18:47 +08:00
Ian McInerney 5ef09daf45 Set import library for Clipper2 target when using system version (#815)
On Windows, the import library is also needed to link against the
Clipper2 DLL, and that must be specified using a different target
property.
2024-05-21 09:42:28 -07:00
Albert Siddhartha Slawinski 6d932a2c7f Fix Manifold.prototype.{split,splitByPlane,rotate} JavaScript bindings functions (#811)
* Fix the JS function to use the correct WASM function names

* Add a simple test

* Update tests and examples based on PR review
2024-05-15 10:14:43 -07:00
Daso Jung fd81cd6172 Split manifold tests (#800)
* Split Boolean test

* Fix name

* formatting

* move cubes test

* formatting

* format

* move tests

* update complex_boolean

* Split manifold test

* Update CMakeLists.txt

* fix typo

* move mingap to properties

* fix conflict

* Update CMakeLists.txt

* organize tests

* fix format

* Update properties.cpp
2024-05-13 09:37:50 -07:00
Emmett Lalish 6b4a4ed841 Update examples from JS -> TS (#807)
* put types back

* updated MV example to TS

* fixed TS errors

* added docs

* switch make-manifold to TS

* fix TS errors

* added docs

* update MV

* fix TS build

* forgot declaration
2024-05-10 16:43:57 -07:00
Emmett Lalish 887f631b8c Make runIndex easier to use (#806)
* handled unsorted groups

* forgot types
2024-05-10 12:52:19 -07:00
Emmett Lalish 443863d09e Runs example (#805)
* switched three example to TS

* use merge

* cleanup

* material groups

* added groups and ids

* working

* added docs
2024-05-09 22:46:16 -07:00
Peter Harman 5301542254 C bindings: pass context to set_properties and warp operation callbacks (#795)
* Fix for #786 by setting numProps in GatherFaces method. Added test for Decompose with properties, and ExpectProperties function in test_main in order to check properties in a mesh match those set by SetProperties.

* Correct formatting of ExpectProperties

* Correct more formatting

* Add x_context variants of set_properties and warp functions in cbindings, along with tests

* Revert "Correct more formatting"

This reverts commit 6e409cf0a9e2c000bddd70ba41667d3209410d4a.

* Revert "Correct formatting of ExpectProperties"

This reverts commit 4d5ee88d9c52e380c6722f347bcd032a8a0d2492.

* Revert "Fix for #786 by setting numProps in GatherFaces method. Added test for Decompose with properties, and ExpectProperties function in test_main in order to check properties in a mesh match those set by SetProperties."

This reverts commit 03848fac198851ad3617de35348513ebfdf00791.

* Remove _context variants of functions and make context argument the default for all callbacks. Add bounds checks to warp test and volume and surface area checks to level set test

* Correct formatting

* Use M_PI not M_PIf as Windows/Mac don't have it

* Use glm not C math

* Mac build sensitive to glm::pow<float,float> vs glm::pow<float>

* Use std::pow for pow
2024-05-03 09:26:56 -07:00
Emmett Lalish a122bde778 Quaternion smoothing (#801)
* revert smoothness change

* updated to quaternions - not right yet

* fixed triangles, mostly

* fixed numerical stability problem

* fixed more nans

* fixed several bugs

* refactor bezier2D

* improved circularity

* added test

* updated tests

* fixed mirror test

* updated manual smooth model

* updated JS test

* cleanup
2024-04-30 21:41:32 -07:00
Peter Harman c9810fd517 Ensure properties maintained through Decompose operation (#794)
* Fix for #786 by setting numProps in GatherFaces method. Added test for Decompose with properties, and ExpectProperties function in test_main in order to check properties in a mesh match those set by SetProperties.

* Correct formatting of ExpectProperties

* Correct more formatting

* Removed ExpectProperties and instead use existing test methods

* Call RelatedGL for each Decomposed Manifold
2024-04-26 08:09:02 -07:00
Emmett Lalish 4c57a56355 split subdivision out of smoothing (#797) 2024-04-25 22:25:08 -07:00
Emmett Lalish cdcbd329e6 Smoothing fixes (#796)
* fixed nan normals

* re-identify flat faces after refine

* fix color interpolation

* fix build
2024-04-25 16:35:35 -07:00
Emmett Lalish c738f1eea1 Smooth quads (#793)
* switched to ivec4, tests still pass

* added partition quad support

* added quad support for property interp

* forming quad tangents

* convex barycentric

* fix interpolation

* added linear quad interp

* added quad smoothing

* fixed vec out of range

* all tests pass

* fixed quad tangents

* tightened quad test

* cleanup

* avoid dome duplicate work

* added docs

* update test
2024-04-25 08:23:48 -07:00
Daso Jung a8b8bf71b1 Split boolean tests (#792)
* Split Boolean test

* Fix name

* formatting

* move cubes test

* formatting

* format

* move tests

* update complex_boolean
2024-04-25 08:10:12 -07:00
Emmett LalishandJohn Collins c60df1b704 Use forVert (#789)
* updated a bunch of do loops to ForVert

* more cleanup

* Hull failure case

* test cleanup

* test helper cleanup

* fixed edge_op

---------

Co-authored-by: John Collins <jmicahc@gmail.com>
2024-04-16 15:23:29 -07:00
Emmett Lalish 8d67ba3a3a Revert "Use ForVert" (#788)
Revert "Use ForVert (#778)"

This reverts commit 32eefe9157.
2024-04-12 21:31:08 -07:00
Marcell Leleszi 3258152e2c Add MinGap function (#765)
* Add tests for intersection

* Add POC MinGap implementation and tests

* Add tests where closest point falls on the triangle's face or on one of the edges

* Create TriangleDistance declaration

* Delete intersect tests

* Add stub MinGap definition

* Add triangle distance impl

* Add comments

* Fix tests

* Fix build

* Use collider

* Collider exploration

* Collider impl, cleanup

* Use par transform

* Revert settings.json, add comments

* Add comments

* Move tri_dist to utils

* Delete unnecessary include

* Create lambda for bounding box expansion

* Move duplicate logic to lambda

* Cleanup

* Use floats instead of doubles, use glm::clamp instead of own implementation

* User ternaries

* Remove DistanceTriangleTriangleSquared out params

* Move MinGap impl to Impl

* Use std::array instead of C-style

* Modify MinGap test cases for clarity, add overlapping tri_dist test

* Add c, wasm, python bindings

* Fix formatting

* Clean up comments

* Test whether faceboxes need to be sorted

* Add min_gap example to all_apis.py

* Fix formatting

* Clean up python binding

* Fix edge to edge test case

* Test BVH culling

* Remove sorting of other manifold's boxes

* Use collider_ member, remove sorting

* Move tests to manifold_test.cpp

* Remove whitespace

* Rename some tests for clarity

* Remove redundant test, modify test for edge to edge test for clarity

* Fix comments

* Parallelize narrow phase

* Reduce sphere res in test case to not cause out of memory error

* Add complex test case

* Remove unnecessary cast

* Refactor functor is lambda
2024-04-02 09:32:06 -07:00
pca006132 78729599a5 fix glm install (#782)
* fix glm install

* try again?

* proper C binding install

* fix glm install
2024-03-30 13:27:09 +08:00
Emmett Lalish 32eefe9157 Use ForVert (#778)
* updated a bunch of do loops to ForVert

* more cleanup
2024-03-26 13:15:26 -07:00
Emmett Lalish 3e05284250 Smooth By Normals (#776)
* SmoothByNormals compiles

* added ForVert

* tests pass

* added bindings

* stupid formatting...

* fix typo

* small fix

* aligned weights
2024-03-26 09:30:13 -07:00
Emmett Lalish c9ec16969d CalculateNormals and Smooth (#771)
* started implementation

* update to Vec

* refactor

* snap normals to faces

* added flat face sharpening

* added Smooth member

* Smooth working

* refactor SetNormals

* calculateNormals runs

* CalculateNormals works

* update test

* add bindings

* fix python bindings

* fix format

* Smooth -> SmoothOut

* a few fixes

* weird format bug

* addressing feedback

* update docs
2024-03-19 10:29:02 -07:00
John Collins 9b1d255931 Java bindings pointer in README (#769) 2024-03-11 12:48:19 -07:00
Emmett Lalish 22d94d5688 v2.4.5 (#762)
remove skip condition
2024-03-04 19:58:15 -08:00
Emmett Lalish e073141f14 v2.4.4 (#761) 2024-03-04 17:57:15 -08:00
Emmett Lalish f54e535c6a v2.4.3 (#760)
fix artifacts
2024-03-04 17:11:17 -08:00
Emmett Lalish c6801c82ea v2.4.2 (#759) 2024-03-04 15:04:57 -08:00
Emmett Lalish a67aa13133 v2.4.1 (#758)
update actions
2024-03-04 14:28:52 -08:00
Emmett Lalish d43709715d v2.4 (#757)
* v2.4.0

* fix cmake version
2024-03-04 13:24:41 -08:00
Emmett Lalish 2ddbd0a1a0 add docs about vertex properties, etc. (#755)
add docs
2024-03-02 17:32:07 -08:00
Emmett Lalish dc4afa950b Refine To Length (#741)
* sketched out implementation

* started PartitionQuad

* added edge directions

* compiles

* first test passing

* minor progress

* simplify

* refactor

* fixed interior verts

* getting manifold results

* improved triangulation

* finished algorithm

* fixed fans

* a few fixes

* fixed 3-2-2

* handle reversed triangles

* use Vec

* fixed another bug

* refine working

* replaced old subdivide

* fix allocation error

* fixed sphere

* fix cache

* fixed undefined behavior

* fix rounding error

* updated test

* cleanup

* add lock

* added properties support

* tightened test

* fixed edge colors

* fixed corners

* only duplicate props when necessary

* fix boolean props

* fixed manifoldness error

* fixed some narrow triangles

* fix compile

* added bindings

* fix formatting

* addressing feedback

* revert edge divisions change
2024-03-02 16:15:44 -08:00
pca006132 c03a5ac3ad prefer find_package for tbb (#747) 2024-02-19 13:03:37 +08:00
pca006132 cae5a01af1 README: change API documentation URL (#746) 2024-02-18 20:06:26 +08:00
pca006132 d59b9b93bd Fix sparseindices (#742)
* accept size_t for Vec

* Prepare a test case for "SparseIndices too large" core dump

* fix python docstring deps

* fix compilation errors

* limit sdf hashtable size

* another fix to hashtable

* log2(0) is undefined

* fix formatting

* Revert "fix compilation errors"

This reverts commit adfb86c18fb8a3f44f138f8b66d583b3c1c81193.

* fix size_t stuff

* remove processOverlap setting in test

* fix hashtable

* vertex number limit

* address comments
2024-02-16 08:45:43 -08:00
pca006132 f3c5ada9f0 test windows dll build (#743)
* test windows dll build

* export all symbols on windows

* skip manifoldc_test on emscripten

* try fix windows build

* fix

* fix2

* fix manifoldc on emscripten

* fix

* fix manifoldc

* fixes

* fix windows ci

* fix python flags

* document windows shenanigans

* I hate windows

* tell users we are downloading thrust from source
2024-02-15 15:28:41 -08:00
Kyle Finn 4395ab622c [python] add MeshGL::Merge() to python API (#739)
* [python] add MeshGL::Merge() to python API

* [python] mesh.merge in all_apis.py
2024-02-13 09:08:50 -08:00
Emmett Lalish 994956d7c3 switch share to hash (#736) 2024-02-08 17:19:47 -08:00
Emmett Lalish 79f735c59a update manifold, fix startup bug (#735) 2024-02-08 15:31:34 -08:00
Emmett Lalish dd67e992bd Editor buttons (#733)
* added undo, redo, format

* share button works

* cleanup

* reset scroll

* addressing feedback
2024-02-06 08:21:57 -08:00
briansturgill 5bdb8bc233 Added test for hull problem fixed in #730. (#732) 2024-02-05 10:47:22 -08:00
briansturgill c9ae400647 Fix to 2D HullImpl problem. (#730)
* Fixed 2D Hull problem

* Ran document formatting.

* Using public.h CCW, refactored as requested.

* Decided that precision value should 0.
2024-02-05 08:13:35 -08:00
Chris Mayo 9c1517e54e Install headers into manifold subdirectory (#729) 2024-02-05 08:12:57 -08:00
pca006132 3b8282e1d5 fix cube performance (#717)
* fix cube performance

* fix test
2024-01-29 13:46:55 +08:00
pca006132 1635864cbc optimize triangulator for msvc (#722)
optimize for msvc
2024-01-29 12:31:35 +08:00
Chris Mayo 65b2ef8198 Fix manifold3d module being built again in the install phase (#725)
With add_custom_target() "the target has no output file and is always
considered out of date".
2024-01-29 01:54:48 +08:00
Chris Mayo a9055a8abf python/gen_docs.py tidy-up (#724)
* python/gen_docs: Use raw strings in method_key()

Resolve Python 3.12 invalid escape sequence warnings.

* python/gen_docs: Remove unused f-string and md5 import
2024-01-29 01:53:38 +08:00
Chris Mayo 0b7ee87489 GLM 1.0 compatibility (#721)
Using glm/gtx/compatibility.hpp without GLM_ENABLE_EXPERIMENTAL has
become an error:

error: #error "GLM: GLM_GTX_compatibility is an experimental extension
               and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL
	       before including it, if you really want to use it."
2024-01-28 08:04:12 +08:00
pca006132 9690305b8d optimize triangulator performance (#718) 2024-01-27 18:30:33 +08:00
pca006132 aa88959899 Update manifoldDeps.cmake (#716) 2024-01-23 15:29:51 +13:00
Emmett LalishandClifford Yapp dbb0ca76b4 Fixed float overflow (#714)
* Set up test for subtraction of a very large cuboid

Test case for https://github.com/elalish/manifold/issues/712 - these glb
inputs were generated from manifolds that succeeded in producing a
Boolean output, but (on one platform but not another) the Status() call
on the output boolean manifold crashed.

(A test for calling Status() on the output captured on the crashing
platform (in case there was something specifically bad about that
particular output on that platform) was tried, but the result was simply
a successful Status() call even with that specific input - hence, that
additional test is not included in this commit.)

* fixed 0 * inf

* switch to ASSERT

* tighten test

---------

Co-authored-by: Clifford Yapp <238416+starseeker@users.noreply.github.com>
2024-01-20 15:16:50 +13:00
Chris Mayo 9018b2ac45 Install Python module into site-packages (#704)
Python_SITEARCH is not writeable by build_nix (tbb) job.
2024-01-17 09:22:27 +13:00
briansturgill f407e43e5b Added BatchBoolean to Python API, both CS & M. (#707) 2024-01-16 15:13:33 +08:00
pca006132 d7cbcb8c2e fix sdist submodule (#709) 2024-01-16 15:13:12 +08:00
pca006132 e27c9c100f cmake updates (#696)
* cmake packaging

* compatibility

* update

* update

* tbb

* fix rpath stuff

* update tbb

* fix nix

* fix bugs

* fix emscripten

* PIC

* CI stuff

* only publish if on master

* assimp dependency for meshIO

* add cmake consumer test

* remove THRUST_HOST_SYSTEM flag

* fix CI permission issue

* cmake minimum required

* install without admin privilege on mac

* fix install directory

* update RELEASE_CHECKLIST.md
2024-01-14 23:31:28 +13:00
briansturgill 8ad5f8e427 Extrude and Resolve should be static on Manifold. (#692)
* Extrude and Resolve should be static on Manifold.

* Fixed unit test for extrude/revolve issue.

* More test issues fixed.

* Clean up formatting.

* Placed extrude and revolve on both CrossSection and Manifold.

* Formatting.
2024-01-14 23:06:11 +13:00
briansturgill afc64f248a CrossSection::Compose was accidently left out of Python API. (#700) 2024-01-14 05:41:36 +08:00
pca006132 c0fb3766e4 update nanobind to v1.8.0 (#699) 2024-01-14 02:52:51 +08:00
Emmett Lalish 887a5021af bump package versions to 2.3.1 (#689)
bump version
2024-01-08 23:26:16 +13:00
Emmett Lalish e83b9bd158 v2.3.1 (#688)
* add missing pytest

* fix npm publish
2024-01-08 22:28:44 +13:00
Emmett Lalish 2d0c705839 v2.3 (#685)
bumped version
2024-01-08 21:06:58 +13:00
Emmett Lalish b36ec2ce0e fix model-viewer example (#686)
updated glTF extension name in GLBs
2024-01-07 22:59:19 +13:00
Emmett Lalish b8d143dd72 fix default partial revolve segments (#684)
* fixed revolve

* correct default segments
2024-01-07 21:27:02 +13:00
Emmett Lalish be022a58e4 fixed revolve (#682) 2024-01-06 23:30:03 +13:00
Emmett Lalish 30e0af942a remove simplify from warp (#677)
remove simplify
2023-12-29 10:24:07 +13:00
Kyle Finn 8c77a6aa05 [python] fix warp callback (#674)
* [python] fix warp callback

I recently broke this making it a no-op due to
the vec reference becoming a by-value copy in python
callback. This fix provides a simple adapter like
we had before allowing python to return modified
values from the callback.

* update torus_knot.py demo to expose both warp modes

* add torus_knot.py to pytest

* use pytest in github CI

* add pytest to flake.nix
2023-12-28 11:54:54 +13:00
Kyle Finn 54fa822408 Python auto doc (#665)
* python autogen docstrings

* use function signature hashes in docstring key

* remove sys.path debug code

* docstrings use param names

* autogen docstrings during cmake

* document gen_docs.py

* more readme, remap mesh_gl references

* readme about verify python docstrings, autogen depend on sources
2023-12-27 09:04:01 +13:00
Kyle Finn 7d564cfbc1 Python more numpy (#656)
* [python] numpy updates and cleanup

Also add CrossSection::Bounds()

* cleanup - const and reference args in py bind

* fix unnamed args

* all_apis.py more coverage

* fixup - more param renaming, docstring fixes

* fixup - run black

* fixup - move const to left side

* fixup extrude.py get_volume

* fix python examples for api update
2023-12-19 08:34:40 +08:00
Kyle Finn bc0bded294 add WarpBatch - array process warp with single callback (#651)
Add WarpBatch - array process warp with single callback
2023-12-17 22:15:28 +13:00
pca006132 d52decc4f1 example url (#643)
* example url

* update

* format

* address comments

* fix formatting
2023-12-16 18:25:45 +08:00
pca006132 8060376e12 add formatting script (#660)
* update formatting

* update formatter
2023-12-16 18:15:53 +08:00
Kyle Finn eba866d266 [python] manifold3d.triangulate and CrossSection.to_polygons_numpy (#650) 2023-12-14 21:51:48 +13:00
Emmett Lalish cc4966f062 added Colab Python example (#648)
added colab example
2023-12-14 07:33:50 +13:00
Emmett Lalish 3216a24be8 added Slice() (#644)
* added slice

* added test, addressed feedback

* added JS bindings

* remove code oddity

* added python bindings

* added C bindings
2023-12-13 13:48:50 +13:00
Kyle Finn 5e566c8c36 [python] fix JoinType.Round and simplify(epsilon) (#647) 2023-12-12 23:04:01 +13:00
Kyle Finn 80edfee978 [python] add revolveDegrees to revolve() API (#646) 2023-12-12 23:02:19 +13:00
pca006132 0e0d1c4f69 change extrude behavior (#642) 2023-12-12 13:17:07 +08:00
Emmett Lalish e15f4cf375 Added Project() (#638)
* added Project

* updated Clipper2 to 1.3.0

* fix simplify to remove more cruft

* update docs
2023-12-10 19:33:27 +08:00
pca006132 c9322ca63a Misc (#640)
* update tracy

* fix warnings

* remove dead code and warnings

* add comment
2023-12-09 15:40:34 +08:00
Emmett Lalish 45826d6c47 Added morph animation to ManifoldCAD.org (#637)
* added morph animation

* update test
2023-12-06 22:45:25 +13:00
Emmett Lalish dd781cdbdf Fix precision (#635)
* fixed empty collider

* disable test

* added svd

* use glm::mat3

* cleanup

* fix casing

* fixed typo
2023-12-06 10:28:08 +13:00
Emmett Lalish 638badaf9b added fuzzer cases (#633)
* added fuzzer cases

* not infinite looping anymore

* fixed loop check

* fixed bound calc
2023-12-04 15:14:33 +13:00
Emmett Lalish 0149e482bc EXT_manifold -> EXT_mesh_manifold (#630) 2023-11-27 16:18:05 -08:00
pca006132 f683e8c9c0 added triangulator fuzzer (#620)
* added triangulator fuzzer

* added easy to use minimizer script

* update readme

* added rounding
2023-11-25 21:51:07 +08:00
Chun Kit LAM 1f8e8037f1 real fix for 3MF export (#627)
* real fix

* formatting

* updated jscadui/3mf-export
2023-11-23 02:28:11 +08:00
Chun Kit LAM 10a15c1dd4 add thumbnail file to 3mf output (#626)
add _rel to 3mf output
2023-11-22 08:14:37 +08:00
Emmett Lalish 6e1629ddce updated tet puzzle example (#625)
* updated tet puzzle example

* updated description
2023-11-19 23:01:33 -08:00
Emmett Lalish e273f6b09d Add animation to manifoldCAD (#624)
* added global config

* translation animation works

* single times accessor

* animations working

* animation controls working

* add icons
2023-11-19 10:49:29 -08:00
Emmett Lalish 67c468211d maybe fix release actions (#623) 2023-11-18 21:50:02 -08:00
Emmett Lalish 01c38e42ca V2.2.2 (#622)
* bumped version

* added npm publishing automation
2023-11-17 10:20:16 -08:00
Chun Kit LAM 1991232eb7 fix performance regression for CreateProperties (#621)
* fix performance regression for CreateProperties

* added SparseIndices size check

* faster CreateProperties

* fix

* rollback and fix

* applied suggestion
2023-11-17 07:58:33 -08:00
Chun Kit LAM 4551288f95 Use black formatter for python format checking (#618)
* added black formatter to ci

* format gyroid_module.py
2023-11-16 20:44:07 +08:00
Emmett Lalishandpca006132 2e29784449 Update python bindings (#615)
* updated python bindings

* added scallop example

* fix python binding and example

* apply black formatting

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2023-11-15 22:51:51 -08:00
Johnathon Selstad 2a2cb6f88e [FEAT] Add LevelSet Function to Python Bindings (#616)
* Commit LevelSet Python Binding

* Address PR Comments

* Standardize Indentation
2023-11-15 22:24:45 -08:00
Rob McDonald 1a91d8db2a RFC to make SimplifyTopology() steps optional (#603)
Add option to skip cleaning up triangles in SimplifyTopology()
2023-11-14 15:23:51 -08:00
Emmett Lalish 82b98d9353 Improve gltf-io and manifold-gltf (#612)
* fixed build copy for npm

* update deps

* fixed glTF validator errors

* fixed bug and Vite

* clamped color

* converted manifold-gltf to TS

* converted gltf-io to TS

* fixed TS build

* avoid usage warnings

* update MV

* clean up types

* update gltf-io API

* fixed bugs

* fixed disposal

* fixed example

* a few fixes and cleanup
2023-11-14 15:23:25 -08:00
Emmett Lalish 7273c9bf0f Fix odd number halfedges (#610)
* fixed odd number of halfedges

* both tests fixed

* reenable tests

* wrong isfinite
2023-11-12 16:09:42 -08:00
4ff3f7cf68 Apparent infinite loop case for boolean operation (#608)
* Apparent infinite loop case for boolean operation

This comes from the FAA Generic Twin example model included with BRL-CAD
- the glb files are written out from the input data structures that
triggered the infinite loop behavior.

* Fix formatting

* disabled GenericTwinBooleanTest7863

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
Co-authored-by: Emmett Lalish <elalish@gmail.com>
2023-11-12 15:23:37 -08:00
Clifford Yappandpca006132 fafe4792da Prepare a test input with failing boolean (#609)
* Prepare a test input with failing boolean

Encountered this case when testing Manifold with the BRL-CAD example
file - specifically, the r.glass8 region.  C++ exception "Vec out of
range" is thrown.  Both inputs are the outputs of previous Manifold
boolean evaluations.

* disabled Havocglass8Bool test

---------

Co-authored-by: pca006132 <john.lck40@gmail.com>
2023-11-12 14:30:26 -08:00
Emmett Lalish b439603eb9 Fix glTF validator errors (#596)
* fixed build copy for npm

* update deps

* fixed glTF validator errors

* fixed bug and Vite
2023-11-07 16:10:52 -08:00
Chun Kit LAM b6eea01021 fix cmake build error with thrust installed (#595) 2023-11-07 22:49:57 +08:00
Johnathon Selstad b9f7228064 [FIX] Packaged Split() output to Tuple (#593)
* Fix the Split Function

* Fix Formatting

* Also update split_by_plane

* Add Cube Splitting Example
2023-11-07 12:18:27 +08:00
Chun Kit LAM 0ef5f70434 fix wasm cmake problems (#591)
* fix cmake problems

* update emsdk version
2023-11-07 09:38:06 +08:00
Chun Kit LAM bb6acf1f6e V2.2.1 (#589)
* added notes about twistDegree and nDivisions

* update version

* removed perf comparison figure

it is pretty outdated considering we don't have the GPU backend anymore
and performance is different now.
2023-11-02 22:00:27 -07:00
Emmett Lalish 5930b5c09d Fix NaN halfedgeTangents (#588)
fixed CreateTangents
2023-11-02 12:35:44 -07:00
Emmett Lalish a842de97c6 Fix properties (#587)
* fix testing

* added test

* add comments

* tests pass

* cleanup
2023-11-02 12:07:58 -07:00
Chun Kit LAM 2942229b49 push to pypi (#583)
* push to pypi

* update action name
2023-10-31 11:13:02 +08:00
315 changed files with 252451 additions and 29867 deletions
+3
View File
@@ -1 +1,4 @@
BasedOnStyle: Google
PointerAlignment: Left
ReferenceAlignment: Left
DerivePointerAlignment: false
+2
View File
@@ -0,0 +1,2 @@
line_length: 80
indent: 2
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
+49 -7
View File
@@ -1,6 +1,9 @@
name: build_wheels
on: [release, workflow_dispatch]
on:
release:
types: [published]
workflow_dispatch:
jobs:
build_wheels:
@@ -8,17 +11,56 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-latest, macos-latest]
# macos-13: x86-64
# macos-14: arm64
os: [ubuntu-22.04, ubuntu-22.04-arm, windows-2025, macos-14, macos-13]
steps:
- run: |
git config --global submodule.fetchJobs 8
git config --global core.longpaths true
- uses: actions/checkout@v3
with:
submodules: recursive
- if: matrix.os == 'macos-14'
run: echo "CIBW_ARCHS=arm64 universal2" >> "$GITHUB_ENV"
- uses: actions/checkout@v4
- name: Build wheels
uses: pypa/cibuildwheel@v2.15.0
- uses: actions/upload-artifact@v3
uses: pypa/cibuildwheel@v2.23.0
- uses: actions/upload-artifact@v4
with:
name: python-${{ matrix.os }}
path: ./wheelhouse/*.whl
make_sdist:
name: Make SDist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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
with:
name: python-sdist
path: dist/*.tar.gz
pypi-publish:
name: Upload release to PyPI
needs: [build_wheels, make_sdist]
runs-on: ubuntu-latest
environment:
name: pypi
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
with:
pattern: python-*
merge-multiple: true
path: dist
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
+16 -4
View File
@@ -8,14 +8,26 @@ on:
jobs:
check_format:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: DoozyX/clang-format-lint-action@v0.14
- uses: actions/checkout@v4
- uses: DoozyX/clang-format-lint-action@v0.20
with:
source: '.'
exclude: '*/third_party'
extensions: 'h,cpp,js,ts,html'
clangFormatVersion: 12
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
+23 -9
View File
@@ -1,19 +1,21 @@
name: Deploy documentation
on:
push:
branches:
- master
workflow_run:
workflows: [CI]
types:
- completed
branches: [master]
jobs:
deploy_github_pages:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Doxygen Action
uses: mattnotmitt/doxygen-action@v1
uses: mattnotmitt/doxygen-action@edge
- name: Move deployables
run: |
@@ -22,14 +24,26 @@ jobs:
mv ./docs ./public
- name: Download built examples
uses: dawidd6/action-download-artifact@v2
uses: dawidd6/action-download-artifact@v6
with:
workflow: manifold.yml
workflow_conclusion: completed
pr: ${{github.event.pull_request.number}}
# specific to the triggering workflow
run_id: ${{github.event.workflow_run.id}}
# do not download from old run
check_artifacts: true
name: wasm
path: ./public
- name: Deploy Javascript Docs to Github Pages
run: |
cd bindings/wasm
cp ../../public/manifold.js .
cp ../../public/manifold.wasm .
npm install
npm run docs
mv ./docs ../../public/jsdocs
- name: Deploy to Github Pages
uses: peaceiris/actions-gh-pages@v3
with:
@@ -37,4 +51,4 @@ jobs:
publish_branch: gh-pages
force_orphan: true
cname: manifoldcad.org
publish_dir: ./public
publish_dir: ./public
+250 -107
View File
@@ -10,225 +10,368 @@ on:
pull_request:
branches: [ master ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
build:
name: GCC ${{matrix.gcc}} (CrossSection:${{matrix.cross_section}}, TBB:${{matrix.parallelization}})
timeout-minutes: 45
strategy:
matrix:
parallel_backend: [NONE, TBB]
runs-on: ubuntu-22.04
if: github.event.pull_request.draft == false
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 }}
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- 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
sudo apt-get update
sudo apt-get install libgtest-dev libassimp-dev git libtbb-dev pkg-config libpython3-dev lcov
python -m pip install -U trimesh pytest
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
with:
submodules: recursive
- uses: jwlawson/actions-setup-cmake@v1.12
- name: Build ${{matrix.backend}}
if: matrix.parallel_backend != 'NONE'
cmake-version: 4.0.2
- name: Build ${{matrix.parallelization}}
if: matrix.parallelization != 'OFF'
run: |
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 ${{matrix.parallel_backend}}
if: matrix.parallel_backend != 'NONE'
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_PYBIND=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_ASSERT=ON -DMANIFOLD_FLAGS=-UNDEBUG -DMANIFOLD_CROSS_SECTION=${{matrix.cross_section}} -DMANIFOLD_EXPORT=ON -DMANIFOLD_PAR=${{matrix.parallelization}} .. && make
- name: Test ${{matrix.parallelization}}
if: matrix.parallelization != 'OFF'
run: |
export PYTHONPATH=$PYTHONPATH:$(pwd)/build/bindings/python
cd build/test
./manifold_test
cd ../../
- name: Test Python bindings ${{matrix.parallelization}}
if: matrix.parallelization != 'OFF' && matrix.cross_section == 'ON'
run: |
export PYTHONPATH=$PYTHONPATH:$(pwd)/build/bindings/python
python3 bindings/python/examples/run_all.py -e
python3 -m pytest
- 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'
if: matrix.parallelization == 'OFF'
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
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_CROSS_SECTION=${{matrix.cross_section}} -DMANIFOLD_PAR=${{matrix.parallelization}} -DCODE_COVERAGE=ON .. && make
lcov --capture --gcov-tool gcov-${{ matrix.gcc }} --ignore-errors mismatch --initial --directory . --output-file ./code_coverage_init.info
cd test
./manifold_test
cd ../
lcov --capture --directory . --output-file ./code_coverage_test.info
lcov --capture --gcov-tool gcov-${{ matrix.gcc }} --ignore-errors mismatch --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 --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'
lcov --extract ./code_coverage_total.info "/*/manifold/src/*" --output-file ./code_coverage.info
cd ../
- uses: codecov/codecov-action@v5
if: matrix.parallelization == 'OFF'
with:
disable_search: true
files: build/code_coverage.info
fail_ci_if_error: false
name: ${{matrix.parallel_backend}}
build_cbind:
timeout-minutes: 30
runs-on: ubuntu-22.04
if: github.event.pull_request.draft == false
steps:
- name: Install dependencies
name: ${{matrix.parallelization}}
token: ${{secrets.CODECOV_TOKEN}}
verbose: true
- name: test cmake consumer
run: |
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
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.*
sudo cmake --install .
cd ..
./scripts/test-cmake.sh
build_wasm:
name: WASM (TBB:${{matrix.parallelization}}, DEBUG:${{matrix.debug}})
timeout-minutes: 30
runs-on: ubuntu-22.04
if: github.event.pull_request.draft == false
strategy:
matrix:
parallelization: [OFF, ON]
debug: [OFF, ON]
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
DEBIAN_FRONTEND=noninteractive sudo apt install -y nodejs
- uses: actions/checkout@v4
- 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
./emsdk install 3.1.64
./emsdk activate 3.1.64
- uses: jwlawson/actions-setup-cmake@v2
- name: Build WASM ${{matrix.parallelization}}
run: |
source ./emsdk/emsdk_env.sh
mkdir build
cd build
emcmake cmake -DCMAKE_BUILD_TYPE=Release .. && emmake make
emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DMANIFOLD_PAR=${{matrix.parallelization}} -DMANIFOLD_USE_BUILTIN_TBB=ON -DMANIFOLD_DEBUG=${{matrix.debug}} -DMANIFOLD_ASSERT=${{matrix.debug}} .. && emmake make
- name: Test WASM
if: matrix.parallelization == 'OFF'
run: |
cd build/test
node ./manifold_test.js
- name: Test examples
- name: Run unit tests
if: matrix.parallelization == 'OFF'
run: |
cd bindings/wasm
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 examples
if: matrix.parallelization == 'OFF'
run: |
cd bindings/wasm/examples
npm ci
npm run build
npm test
cp ../manifold.* ./dist/
- name: Upload WASM files
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: github.event_name == 'push' && matrix.parallelization == 'OFF' && matrix.debug == 'OFF'
with:
name: wasm
path: bindings/wasm/examples/dist/
retention-days: 90
overwrite: true
build_fuzzer:
name: fuzzer
timeout-minutes: 30
runs-on: ubuntu-24.04
steps:
- name: Install dependencies
run: |
sudo apt-get -y update
DEBIAN_FRONTEND=noninteractive sudo apt install -y libgtest-dev clang
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
- name: Build
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMANIFOLD_DEBUG=ON -DMANIFOLD_FUZZ=ON -DMANIFOLD_PYBIND=OFF -DCMAKE_CXX_COMPILER=clang++ ..
make
build_fuzzer_mac:
name: fuzzer mac
timeout-minutes: 30
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install common dependencies
run: |
brew install googletest
- uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: 3.31.6
- name: Build
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMANIFOLD_DEBUG=ON -DMANIFOLD_FUZZ=ON -DMANIFOLD_PYBIND=OFF -DCMAKE_CXX_COMPILER=clang++ -DMANIFOLD_PAR=OFF ..
ASAN_OPTIONS=detect_container_overflow=0 make
build_windows:
name: Windows (TBB:${{matrix.parallelization}}, SHARED:${{matrix.shared}})
timeout-minutes: 30
strategy:
matrix:
parallel_backend: [NONE, TBB]
max-parallel: 1
runs-on: windows-2019
if: github.event.pull_request.draft == false
parallelization: [OFF, ON]
shared: [OFF, ON]
runs-on: windows-2025
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
with:
submodules: recursive
- uses: jwlawson/actions-setup-cmake@v1.12
cmake-version: 4.0.2
- uses: ilammy/msvc-dev-cmd@v1
- name: Build ${{matrix.backend}}
- name: Build ${{matrix.parallelization}}
shell: powershell
run: |
cmake . -DCMAKE_BUILD_TYPE=Release -B build -DMANIFOLD_DEBUG=ON -DPYBIND11_FINDPYTHON=OFF -DMANIFOLD_PAR=${{matrix.parallel_backend}} -A x64
cmake . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=${{matrix.shared}} -DMANIFOLD_USE_BUILTIN_TBB=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_ASSERT=ON -DMANIFOLD_PAR=${{matrix.parallelization}} -A x64 -B build
cd build
cmake --build . --target ALL_BUILD --config Release
- name: Test ${{matrix.parallel_backend}}
- name: Test dll location
if: matrix.shared == 'ON'
shell: bash
run: |
cd build/test
./Release/manifold_test.exe
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
cd build/bin/Release
./manifold_test.exe
cd ../../
- name: test cmake consumer
run: |
cd build
cmake --install .
cd ..
./scripts/test-cmake.sh
build_mxe:
name: MXE (TBB:${{matrix.parallelization == 'ON'}})
timeout-minutes: 30
strategy:
matrix:
parallel_backend: [NONE, TBB]
max-parallel: 1
parallelization: [OFF, ON]
runs-on: ubuntu-latest
container: openscad/mxe-x86_64-gui:latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/checkout@v4
- name: Build
run: |
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
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_PYBIND=OFF -DMANIFOLD_CBIND=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_PAR=${{matrix.parallelization}} .. && make
build_mac:
name: MacOS (TBB:${{matrix.parallelization == 'ON'}}, shared lib:${{matrix.shared == 'ON'}}, ${{matrix.os}})
timeout-minutes: 30
strategy:
matrix:
parallel_backend: [NONE, TBB]
runs-on: macos-latest
if: github.event.pull_request.draft == false
parallelization: [OFF, ON]
shared: [OFF, ON]
# macos-13: intel
# macos-latest: arm
os: [macos-13, macos-latest]
runs-on: ${{matrix.os}}
steps:
- name: Install common dependencies
run: |
brew install pkg-config googletest assimp
pip install trimesh
brew install googletest assimp
pip install trimesh pytest
- name: Install TBB
if: matrix.parallel_backend == 'TBB'
if: matrix.parallelization == 'ON'
run: brew install tbb
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
with:
submodules: recursive
- uses: jwlawson/actions-setup-cmake@v1.12
cmake-version: 4.0.2
- name: Build
run: |
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
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=${{matrix.shared}} -DMANIFOLD_DEBUG=ON -DMANIFOLD_ASSERT=ON -DMANIFOLD_PYBIND=ON -DMANIFOLD_EXPORT=ON -DMANIFOLD_PAR=${{matrix.parallelization}} .. && make
- name: Test
run: |
cd build/test
./manifold_test
cd ../../
- name: test cmake consumer
run: |
cd build
sudo cmake --install .
cd ..
./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
runs-on: macos-latest
steps:
- name: Install common dependencies
run: |
brew install googletest
pip install trimesh pytest
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: 4.0.2
- name: Build
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_USE_BUILTIN_TBB=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_ASSERT=ON -DMANIFOLD_PYBIND=OFF -DMANIFOLD_PAR=ON .. && make
- name: Test
run: |
cd build/test
./manifold_test
cd ../../
- name: test cmake consumer
run: |
cd build
sudo cmake --install .
cd ..
./scripts/test-cmake.sh
LDFLAGS=-Wl,-rpath,/usr/local/lib ./scripts/test-pkgconfig.sh
build_mac_builtin_tbb_subdir:
timeout-minutes: 30
runs-on: macos-latest
steps:
- name: Install common dependencies
run: |
brew install googletest
pip install trimesh pytest
- uses: actions/checkout@v4
- uses: jwlawson/actions-setup-cmake@v2
with:
cmake-version: 4.0.2
- name: test cmake consumer
run: |
cd ..
./manifold/scripts/test-cmake-subdir.sh
build_nix:
timeout-minutes: 30
strategy:
matrix:
variant: [none, tbb, js]
variant: [manifold-none, manifold-tbb, manifold-js, manifold-js-tbb, manifold3d]
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#manifold-${{matrix.variant}}'
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- run: nix build -L '.?submodules=1#${{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'
+37
View File
@@ -0,0 +1,37 @@
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@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
- 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 install
npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+31
View File
@@ -0,0 +1,31 @@
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}}
+17 -2
View File
@@ -9,7 +9,6 @@
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
@@ -112,11 +111,27 @@ 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
.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
-9
View File
@@ -1,9 +0,0 @@
[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 -4
View File
@@ -6,12 +6,13 @@
"configurations": [
{
"name": "manifold test",
"type": "cppdbg",
"type": "lldb-dap",
"MIMode": "lldb",
"request": "launch",
"program": "${workspaceFolder}/build/test/manifold_test",
"args": [
"--gtest_break_on_failure",
"--gtest_filter=Manifold.GetMesh"
"--gtest_catch_exceptions=0",
"--gtest_filter=Manifold.Simplify"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/test",
@@ -20,7 +21,8 @@
"name": "MALLOC_CHECK_",
"value": "2"
}
]
],
"externalConsole": false
}
]
}
+22 -4
View File
@@ -108,17 +108,29 @@
"latch": "cpp",
"semaphore": "cpp",
"span": "cpp",
"__sso_allocator": "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"
},
"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_USE_CUDA=OFF",
"-DMANIFOLD_PAR=TBB",
"-DMANIFOLD_ASSERT=ON",
"-DMANIFOLD_PAR=ON",
"-DMANIFOLD_FUZZ=OFF",
"-DCODE_COVERAGE=OFF",
"-DCMAKE_CXX_FLAGS=''" //'-fsanitize=address,undefined'"
],
@@ -137,7 +149,9 @@
"halfedge",
"halfedges",
"Tris",
"Verts"
"Verts",
"Voxel",
"voxels"
],
"javascript.format.semicolons": "insert",
"[python]": {
@@ -145,4 +159,8 @@
},
"python.formatting.provider": "none",
"clang-format.executable": "clang-format",
"clangd.arguments": [
"--header-insertion=never"
],
"editor.defaultFormatter": "xaver.clang-format",
}
+276 -48
View File
@@ -15,83 +15,311 @@
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)
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)
include(CTest)
enable_testing()
include(GNUInstallDirs)
include(CMakeDependentOption)
# Define Manifold version
set(MANIFOLD_VERSION_MAJOR 3)
set(MANIFOLD_VERSION_MINOR 3)
set(MANIFOLD_VERSION_PATCH 2)
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" ON)
option(
MANIFOLD_DOWNLOADS
"Allow Manifold build to download missing dependencies"
ON
)
option(MANIFOLD_EXPORT "Build mesh export (via assimp) utility library" OFF)
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)
option(MANIFOLD_PYBIND "Build python bindings" ON)
option(MANIFOLD_CBIND "Build C (FFI) bindings" OFF)
option(MANIFOLD_JSBIND "Build js binding" ${EMSCRIPTEN})
# 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_FLAGS "" CACHE STRING "Manifold compiler flags")
option(TRACY_ENABLE "Use tracy profiling" OFF)
# Development options
option(TRACY_ENABLE "Use tracy profiling" OFF)
option(TRACY_MEMORY_USAGE "Track memory allocation with tracy (expensive)" OFF)
option(BUILD_TEST_CGAL "Build CGAL performance comparisons" OFF)
option(MANIFOLD_FUZZ "Enable fuzzing tests" OFF)
mark_as_advanced(TRACY_ENABLE)
mark_as_advanced(TRACY_MEMORY_USAGE)
mark_as_advanced(MANIFOLD_FUZZ)
# 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()
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")
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)
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)
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_PYBIND OFF)
set(BUILD_SHARED_LIBS OFF)
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)
set(MANIFOLD_FLAGS ${MANIFOLD_FLAGS} /DNOMINMAX)
if(MSVC)
list(APPEND MANIFOLD_FLAGS /DNOMINMAX /bigobj)
else()
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)
list(
APPEND
WARNING_FLAGS
-Wall
-Wno-unknown-warning-option
-Wno-unused
-Wno-shorten-64-to-32
)
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()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# disable fp math optimizations, e.g. FMA for supported architectures, as
# this changes floating-point results
# clang defaults to -ffp-contract=off so we don't have to set that
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()
endif()
if(CODE_COVERAGE)
list(
APPEND
MANIFOLD_FLAGS
-coverage
-fno-inline-small-functions
-fkeep-inline-functions
-fkeep-static-functions
)
add_link_options(-coverage)
endif()
set(MANIFOLD_FLAGS ${MANIFOLD_FLAGS} ${WARNING_FLAGS} -O3)
endif()
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()
# 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")
include(GNUInstallDirs)
include(${PROJECT_SOURCE_DIR}/cmake/manifoldDeps.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/configHelper.cmake)
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>
$<$<BOOL:${MANIFOLD_EXPORT}>:meshIO.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
)
+789 -437
View File
File diff suppressed because it is too large Load Diff
+147 -49
View File
@@ -1,58 +1,92 @@
# About Manifold
[![codecov](https://codecov.io/github/elalish/manifold/branch/master/graph/badge.svg?token=IIA8G5HVS7)](https://codecov.io/github/elalish/manifold)
[![PyPI version](https://badge.fury.io/py/manifold3d.svg)](https://badge.fury.io/py/manifold3d)
[![npm version](https://badge.fury.io/js/manifold-3d.svg)](https://badge.fury.io/js/manifold-3d)
[![twitter](https://img.shields.io/twitter/follow/manifoldcad?style=social&logo=twitter)](https://twitter.com/intent/follow?screen_name=manifoldcad)
[**C++ Documentation**](https://manifoldcad.org/docs/html/classmanifold_1_1_manifold.html) | [**TS Documentation**](https://manifoldcad.org/jsdocs) | [**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
[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.
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.
For example, here is a log-log plot of Manifold's performance vs. earlier OpenSCAD geometry backends:
| | | |
| --- | --- | --- |
| [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) | [Cadova](https://github.com/tomasf/Cadova) |
| [BREP.io](https://github.com/mmiscool/BREP) |
### 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
![A metallic Menger sponge](https://elalish.github.io/manifold/samples/models/mengerSponge3.webp "A metallic Menger sponge")
### [ManifoldCAD.org](https://manifoldcad.org)
### Note for Firefox users
If you like OpenSCAD / JSCAD, you might also like ManifoldCAD - 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.
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.
### [Python Colab Example](https://colab.research.google.com/drive/1VxrFYHPSHZgUbl9TeWzCeovlpXrPQ5J5?usp=sharing)
# Manifold
If you prefer Python to JS/TS, make your own copy of the example notebook above. 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.
[**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)
![A metallic Menger sponge](https://manifoldcad.org/samples/models/mengerSponge192.png "A metallic Menger sponge")
[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).
## 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.
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`)
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.
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.
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.
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.
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.
> Note: OMP and CUDA backends are now removed
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.
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) |
| [`Assimp`](https://github.com/assimp/assimp) | `MANIFOLD_EXPORT=ON` | Basic Mesh I/O |
| [`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 |
### 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 an optional [`MeshIO`](https://manifoldcad.org/docs/html/group___mesh_i_o.html) component based on [Assimp](https://assimp.org/), 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.
## 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.
@@ -64,29 +98,61 @@ cd manifold
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON .. && make
test/manifold_test
make test
```
CMake flags (usage e.g. `-DMANIFOLD_DEBUG=ON`):
- `MANIFOLD_JSBIND=[OFF, <ON>]`: Build js binding when using emscripten.
- `MANIFOLD_JSBIND=[OFF, <ON>]`: Build js binding (when using the emscripten toolchain).
- `MANIFOLD_CBIND=[<OFF>, ON]`: Build C FFI binding.
- `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.
- `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_EXPORT=[<OFF>, ON]`: Enables `MeshIO` and GLB export of 3D models from the tests, requires `assimp`.
- `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`.
- `TRACY_ENABLE=[<OFF>, ON]`: Enable integration with tracy profiler.
See profiling section below.
- `BUILD_TEST_CGAL=[<OFF>, ON]`: Builds a CGAL-based performance [comparison](https://github.com/elalish/manifold/tree/master/extras), requires `libcgal-dev`.
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.
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`.
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):
@@ -108,8 +174,9 @@ Then build:
cd manifold
mkdir buildWASM
cd buildWASM
emcmake cmake -DCMAKE_BUILD_TYPE=Release .. && emmake make
node test/manifold_test.js
emcmake cmake -DCMAKE_BUILD_TYPE=MinSizeRel .. && emmake make
cd test
node ./manifold_test.js
```
### Python
@@ -120,7 +187,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
pip install trimesh pytest
python3 run_all.py -e
```
@@ -134,15 +201,46 @@ 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!
## Profiling
### 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
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/). 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).
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).
+14
View File
@@ -0,0 +1,14 @@
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.
+8 -4
View File
@@ -12,14 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
if(NOT EMSCRIPTEN AND MANIFOLD_CBIND)
add_subdirectory(c)
if(NOT MANIFOLD_CROSS_SECTION)
return()
endif()
if(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()
+40 -23
View File
@@ -1,33 +1,50 @@
project(manifoldc)
# 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.
add_library(${PROJECT_NAME} manifoldc.cpp conv.cpp)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/../../include
)
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()
add_library(
manifoldc
manifoldc.cpp
conv.cpp
box.cpp
cross.cpp
rect.cpp
)
exportlib(manifoldc)
if(MANIFOLD_EXPORT)
target_link_libraries(${PROJECT_NAME} PRIVATE meshIO)
target_compile_options(${PROJECT_NAME} PUBLIC -DMANIFOLD_EXPORT)
target_sources(manifoldc PRIVATE meshIOc.cpp)
target_compile_options(manifoldc PUBLIC -DMANIFOLD_EXPORT)
endif()
target_link_libraries(
${PROJECT_NAME}
PRIVATE manifold sdf cross_section
)
target_link_libraries(manifoldc PRIVATE manifold)
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_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(
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
FILES include/manifold/manifoldc.h include/manifold/types.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/manifold
)
+38 -33
View File
@@ -13,86 +13,91 @@
// limitations under the License.
#include "conv.h"
#include "manifold.h"
#include "public.h"
#include "types.h"
#include "manifold/common.h"
#include "manifold/types.h"
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);
#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);
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};
}
float manifold_box_scale(ManifoldBox *b) { return from_c(b)->Scale(); }
double manifold_box_scale(ManifoldBox* b) { return from_c(b)->Scale(); }
int manifold_box_contains_pt(ManifoldBox *b, float x, float y, float z) {
auto p = glm::vec3(x, y, z);
int manifold_box_contains_pt(ManifoldBox* b, double x, double y, double z) {
auto p = 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, float x, float y, float z) {
void manifold_box_include_pt(ManifoldBox* b, double x, double y, double z) {
auto box = *from_c(b);
auto p = glm::vec3(x, y, z);
auto p = 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, 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);
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});
auto transformed = from_c(b)->Transform(mat);
return to_c(new (mem) Box(transformed));
}
ManifoldBox *manifold_box_translate(void *mem, ManifoldBox *b, float x, float y,
float z) {
auto p = glm::vec3(x, y, z);
ManifoldBox* manifold_box_translate(void* mem, ManifoldBox* b, double x,
double y, double z) {
auto p = 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, float x, float y,
float z) {
auto p = glm::vec3(x, y, z);
ManifoldBox* manifold_box_mul(void* mem, ManifoldBox* b, double x, double y,
double z) {
auto p = 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, float x, float y, float z) {
auto p = glm::vec3(x, y, z);
int manifold_box_does_overlap_pt(ManifoldBox* b, double x, double y, double z) {
auto p = 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(); }
int manifold_box_is_finite(ManifoldBox* b) { return from_c(b)->IsFinite(); }
#ifdef __cplusplus
}
#endif
+72 -63
View File
@@ -16,42 +16,41 @@
#include <vector>
#include "cross_section.h"
#include "manifold.h"
#include "public.h"
#include "sdf.h"
#include "types.h"
#include "manifold/common.h"
#include "manifold/cross_section.h"
#include "manifold/manifold.h"
#include "manifold/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);
}
ManifoldMesh *to_c(manifold::Mesh *m) {
return reinterpret_cast<ManifoldMesh *>(m);
ManifoldMeshGL* to_c(manifold::MeshGL* m) {
return reinterpret_cast<ManifoldMeshGL*>(m);
}
ManifoldMeshGL *to_c(manifold::MeshGL *m) {
return reinterpret_cast<ManifoldMeshGL *>(m);
ManifoldMeshGL64* to_c(manifold::MeshGL64* m) {
return reinterpret_cast<ManifoldMeshGL64*>(m);
}
ManifoldOpType to_c(manifold::OpType optype) {
@@ -107,58 +106,61 @@ 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(glm::vec2 v) { return {v.x, v.y}; }
ManifoldVec2 to_c(vec2 v) { return {v.x, v.y}; }
ManifoldVec3 to_c(glm::vec3 v) { return {v.x, v.y, v.z}; }
ManifoldVec3 to_c(vec3 v) { return {v.x, v.y, v.z}; }
ManifoldIVec3 to_c(glm::ivec3 v) { return {v.x, v.y, v.z}; }
ManifoldIVec3 to_c(ivec3 v) { return {v.x, v.y, v.z}; }
ManifoldProperties to_c(manifold::Properties p) {
return {p.surfaceArea, p.volume};
ManifoldTriangulation* to_c(std::vector<ivec3>* m) {
return reinterpret_cast<ManifoldTriangulation*>(m);
}
const manifold::Manifold *from_c(ManifoldManifold *m) {
return reinterpret_cast<manifold::Manifold const *>(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);
}
const 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);
}
const manifold::SimplePolygon *from_c(ManifoldSimplePolygon *m) {
return reinterpret_cast<manifold::SimplePolygon const *>(m);
const manifold::SimplePolygon* from_c(ManifoldSimplePolygon* m) {
return reinterpret_cast<manifold::SimplePolygon const*>(m);
}
const manifold::Polygons *from_c(ManifoldPolygons *m) {
return reinterpret_cast<manifold::Polygons const *>(m);
const manifold::Polygons* from_c(ManifoldPolygons* m) {
return reinterpret_cast<manifold::Polygons const*>(m);
}
const manifold::Mesh *from_c(ManifoldMesh *m) {
return reinterpret_cast<manifold::Mesh const *>(m);
const manifold::MeshGL* from_c(ManifoldMeshGL* m) {
return reinterpret_cast<manifold::MeshGL const*>(m);
}
const manifold::MeshGL *from_c(ManifoldMeshGL *m) {
return reinterpret_cast<manifold::MeshGL const *>(m);
const manifold::MeshGL64* from_c(ManifoldMeshGL64* m) {
return reinterpret_cast<manifold::MeshGL64 const*>(m);
}
OpType from_c(ManifoldOpType optype) {
@@ -205,45 +207,52 @@ 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;
}
const manifold::Box *from_c(ManifoldBox *m) {
return reinterpret_cast<manifold::Box const *>(m);
const manifold::Box* from_c(ManifoldBox* m) {
return reinterpret_cast<manifold::Box const*>(m);
}
const manifold::Rect *from_c(ManifoldRect *m) {
return reinterpret_cast<manifold::Rect const *>(m);
const manifold::Rect* from_c(ManifoldRect* m) {
return reinterpret_cast<manifold::Rect const*>(m);
}
glm::vec2 from_c(ManifoldVec2 v) { return glm::vec2(v.x, v.y); }
vec2 from_c(ManifoldVec2 v) { return vec2(v.x, v.y); }
glm::vec3 from_c(ManifoldVec3 v) { return glm::vec3(v.x, v.y, v.z); }
vec3 from_c(ManifoldVec3 v) { return vec3(v.x, v.y, v.z); }
glm::ivec3 from_c(ManifoldIVec3 v) { return glm::ivec3(v.x, v.y, v.z); }
ivec3 from_c(ManifoldIVec3 v) { return ivec3(v.x, v.y, v.z); }
glm::vec4 from_c(ManifoldVec4 v) { return glm::vec4(v.x, v.y, v.z, v.w); }
vec4 from_c(ManifoldVec4 v) { return vec4(v.x, v.y, v.z, v.w); }
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) {
const std::vector<ivec3>* from_c(ManifoldTriangulation* m) {
return reinterpret_cast<std::vector<ivec3> const*>(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) {
vec.push_back(from_c(vs[i]));
}
return vec;
}
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) {
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) {
vec.push_back(from_c(vs[i]));
}
return vec;
}
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) {
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) {
vec.push_back(from_c(vs[i]));
}
return vec;
+81
View File
@@ -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 "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);
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::MeshGL* from_c(ManifoldMeshGL* m);
const manifold::MeshGL64* from_c(ManifoldMeshGL64* 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);
vec2 from_c(ManifoldVec2 v);
vec3 from_c(ManifoldVec3 v);
ivec3 from_c(ManifoldIVec3 v);
vec4 from_c(ManifoldVec4 v);
const 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;
}
+95 -88
View File
@@ -12,235 +12,242 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include "conv.h"
#include "cross_section.h"
#include "manifold.h"
#include "public.h"
#include "types.h"
#include "manifold/common.h"
#include "manifold/cross_section.h"
#include "manifold/types.h"
ManifoldCrossSection *manifold_cross_section_empty(void *mem) {
#ifdef __cplusplus
extern "C" {
#endif
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, int idx) {
ManifoldCrossSection* manifold_cross_section_vec_get(
void* mem, ManifoldCrossSectionVec* csv, size_t idx) {
auto cs = (*from_c(csv))[idx];
return to_c(new (mem) CrossSection(cs));
}
void manifold_cross_section_vec_set(ManifoldCrossSectionVec *csv, int idx,
ManifoldCrossSection *cs) {
void manifold_cross_section_vec_set(ManifoldCrossSectionVec* csv, size_t 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, float x, float y,
int center) {
auto cs = CrossSection::Square(glm::vec2(x, y), center);
ManifoldCrossSection* manifold_cross_section_square(void* mem, double x,
double y, int center) {
auto cs = CrossSection::Square(vec2(x, y), center);
return to_c(new (mem) CrossSection(cs));
}
ManifoldCrossSection *manifold_cross_section_circle(void *mem, float radius,
ManifoldCrossSection* manifold_cross_section_circle(void* mem, double 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,
float x, float y) {
auto translated = from_c(cs)->Translate(glm::vec2(x, y));
ManifoldCrossSection* manifold_cross_section_translate(void* mem,
ManifoldCrossSection* cs,
double x, double y) {
auto translated = from_c(cs)->Translate(vec2(x, y));
return to_c(new (mem) CrossSection(translated));
}
ManifoldCrossSection *manifold_cross_section_rotate(void *mem,
ManifoldCrossSection *cs,
float deg) {
ManifoldCrossSection* manifold_cross_section_rotate(void* mem,
ManifoldCrossSection* cs,
double deg) {
auto rotated = from_c(cs)->Rotate(deg);
return to_c(new (mem) CrossSection(rotated));
}
ManifoldCrossSection *manifold_cross_section_scale(void *mem,
ManifoldCrossSection *cs,
float x, float y) {
auto scaled = from_c(cs)->Scale(glm::vec2(x, y));
ManifoldCrossSection* manifold_cross_section_scale(void* mem,
ManifoldCrossSection* cs,
double x, double y) {
auto scaled = from_c(cs)->Scale(vec2(x, y));
return to_c(new (mem) CrossSection(scaled));
}
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));
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));
return to_c(new (mem) CrossSection(mirrored));
}
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);
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});
auto transformed = from_c(cs)->Transform(mat);
return to_c(new (mem) CrossSection(transformed));
}
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));
ManifoldCrossSection* manifold_cross_section_warp(
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));
};
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();
}
int manifold_cross_section_num_vert(ManifoldCrossSection *cs) {
size_t manifold_cross_section_num_vert(ManifoldCrossSection* cs) {
return from_c(cs)->NumVert();
}
int manifold_cross_section_num_contour(ManifoldCrossSection *cs) {
size_t 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
-81
View File
@@ -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 "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;
}
+514
View File
@@ -0,0 +1,514 @@
// 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_get_meshgl(void* mem, ManifoldManifold* m);
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_get_meshgl64(void* mem, ManifoldManifold* m);
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);
// 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(
void* mem, ManifoldCrossSection* cs, ManifoldVec2 (*fun)(double, double));
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
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);
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_curvature_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
#ifdef MANIFOLD_EXPORT
ManifoldMaterial* manifold_material(void* mem);
void manifold_material_set_roughness(ManifoldMaterial* mat, double roughness);
void manifold_material_set_metalness(ManifoldMaterial* mat, double metalness);
void manifold_material_set_color(ManifoldMaterial* mat, ManifoldVec3 color);
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
@@ -23,10 +23,11 @@ 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;
@@ -41,14 +42,14 @@ typedef struct ManifoldManifoldPair {
} ManifoldManifoldPair;
typedef struct ManifoldVec2 {
float x;
float y;
double x;
double y;
} ManifoldVec2;
typedef struct ManifoldVec3 {
float x;
float y;
float z;
double x;
double y;
double z;
} ManifoldVec3;
typedef struct ManifoldIVec3 {
@@ -58,15 +59,15 @@ typedef struct ManifoldIVec3 {
} ManifoldIVec3;
typedef struct ManifoldVec4 {
float x;
float y;
float z;
float w;
double x;
double y;
double z;
double w;
} ManifoldVec4;
typedef struct ManifoldProperties {
float surface_area;
float volume;
double surface_area;
double volume;
} ManifoldProperties;
// enums
@@ -90,6 +91,7 @@ typedef enum ManifoldError {
MANIFOLD_RUN_INDEX_WRONG_LENGTH,
MANIFOLD_FACE_ID_WRONG_LENGTH,
MANIFOLD_INVALID_CONSTRUCTION,
MANIFOLD_RESULT_TOO_LARGE,
} ManifoldError;
typedef enum ManifoldFillRule {
@@ -103,4 +105,8 @@ 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*);
-428
View File
@@ -1,428 +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 "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
+425 -215
View File
File diff suppressed because it is too large Load Diff
+111
View File
@@ -0,0 +1,111 @@
// 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 "conv.h"
#include "manifold/meshIO.h"
#include "manifold/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, double roughness) {
from_c(mat)->roughness = roughness;
}
void manifold_material_set_metalness(ManifoldMaterial* mat, double metalness) {
from_c(mat)->metalness = metalness;
}
void manifold_material_set_color(ManifoldMaterial* mat, ManifoldVec3 color) {
from_c(mat)->color = from_c(color);
}
// 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) 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
-117
View File
@@ -1,117 +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 "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
+36 -32
View File
@@ -13,88 +13,92 @@
// limitations under the License.
#include "conv.h"
#include "cross_section.h"
#include "manifold.h"
#include "public.h"
#include "types.h"
#include "manifold/common.h"
#include "manifold/types.h"
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);
#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);
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};
}
float manifold_rect_scale(ManifoldRect *r) { return from_c(r)->Scale(); }
double manifold_rect_scale(ManifoldRect* r) { return from_c(r)->Scale(); }
int manifold_rect_contains_pt(ManifoldRect *r, float x, float y) {
int manifold_rect_contains_pt(ManifoldRect* r, double x, double y) {
auto rect = *from_c(r);
auto p = glm::vec2(x, y);
auto p = 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, float x, float y) {
void manifold_rect_include_pt(ManifoldRect* r, double x, double y) {
auto rect = *from_c(r);
auto p = glm::vec2(x, y);
auto p = 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, float x1,
float y1, float x2, float y2, float x3,
float y3) {
auto mat = glm::mat3x2(x1, y1, x2, y2, x3, y3);
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});
auto transformed = from_c(r)->Transform(mat);
return to_c(new (mem) Rect(transformed));
}
ManifoldRect *manifold_rect_translate(void *mem, ManifoldRect *r, float x,
float y) {
auto rect = *from_c(r);
auto p = glm::vec2(x, y);
ManifoldRect* manifold_rect_translate(void* mem, ManifoldRect* r, double x,
double y) {
auto p = vec2(x, y);
auto translated = (*from_c(r)) + p;
return to_c(new (mem) Rect(translated));
}
ManifoldRect *manifold_rect_mul(void *mem, ManifoldRect *r, float x, float y) {
auto rect = *from_c(r);
auto p = glm::vec2(x, y);
ManifoldRect* manifold_rect_mul(void* mem, ManifoldRect* r, double x,
double y) {
auto p = 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(); }
int manifold_rect_is_finite(ManifoldRect* r) { return from_c(r)->IsFinite(); }
#ifdef __cplusplus
}
#endif
+62 -15
View File
@@ -12,28 +12,75 @@
# See the License for the specific language governing permissions and
# limitations under the License.
project(python)
nanobind_add_module(manifold3d NB_STATIC STABLE_ABI LTO autogen_docstrings.inl
manifold3d.cpp
)
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()
add_subdirectory(third_party)
nanobind_add_module(
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)
target_compile_options(
manifold3d
PRIVATE ${MANIFOLD_FLAGS} -DMODULE_NAME=manifold3d
)
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)
install(
TARGETS manifold3d
LIBRARY DESTINATION ${SKBUILD_PLATLIB_DIR}
COMPONENT bindings
)
set(MANIFOLD_PYBIND_LIBDIR ${SKBUILD_PLATLIB_DIR})
else()
set(MANIFOLD_PYBIND_LIBDIR ${Python_SITEARCH})
endif()
install(
TARGETS manifold3d
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
COMPONENT bindings
LIBRARY DESTINATION ${MANIFOLD_PYBIND_LIBDIR} COMPONENT bindings
)
if(MANIFOLD_PYBIND_STUBGEN)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/manifold3d.pyi
DESTINATION ${MANIFOLD_PYBIND_LIBDIR}
COMPONENT bindings
)
endif()
+39
View File
@@ -0,0 +1,39 @@
# 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.
+51
View File
@@ -0,0 +1,51 @@
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.
+133
View File
@@ -0,0 +1,133 @@
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()
+63 -41
View File
@@ -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,60 +20,82 @@ 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 Manifold(bricks)
bricks = [
brick().translate([(brick_length + mortar_gap) * x, 0, 0])
for x in range(length)
]
return sum(bricks, Manifold())
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 Manifold(bricks)
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())
def walls(length, width, height):
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),
])
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(),
)
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 Manifold(results)
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())
def run(width=10, length=10, height=10):
return walls(length, width, height) + floor(length, width)
+21 -18
View File
@@ -1,30 +1,33 @@
"""
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
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)
return a - 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 - sum(spheres, Manifold())
+18 -10
View File
@@ -1,4 +1,4 @@
from manifold3d import CrossSection
from manifold3d import CrossSection, Manifold
def run():
@@ -10,26 +10,34 @@ def run():
cross_section = CrossSection(polygons_points)
polygons = cross_section.to_polygons()
polygon = polygons[0]
if set(polygon) != set(polygon_points):
raise Exception(f"polygon={polygon} differs from polygon_points={polygon_points}")
if set([tuple(p) for p in polygon]) != set(polygon_points):
raise Exception(
f"polygon={polygon} differs from polygon_points={polygon_points}"
)
# extrude a polygon to create a manifold
extruded_polygon = cross_section.extrude(10.0)
extruded_polygon = Manifold.extrude(cross_section, 10.0)
eps = 0.001
observed_volume = extruded_polygon.get_volume()
observed_volume = extruded_polygon.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.get_surface_area()
raise Exception(
f"observed_volume={observed_volume} differs from expected_volume={expected_volume}"
)
observed_surface_area = extruded_polygon.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
+57
View File
@@ -0,0 +1,57 @@
"""
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)
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -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,10 +24,9 @@ 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)
@@ -41,9 +40,10 @@ 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}")
+81
View File
@@ -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
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)
)
+23
View File
@@ -0,0 +1,23 @@
"""
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]
+19 -14
View File
@@ -4,26 +4,27 @@ 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 [1 - p / 2 for p in pos] + [1.0]
return [-p + 0.5 for p in pos] + [1.0]
def run(n=1):
@@ -37,4 +38,8 @@ 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)
return (
result.trim_by_plane([1, 1, 1], 0)
.set_properties(4, posColors)
.scale([100, 100, 100])
)
@@ -0,0 +1,97 @@
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()
+2 -1
View File
@@ -1,7 +1,8 @@
from manifold3d import Manifold
def run():
# for some reason this causes collider error
obj = Manifold.cube()
obj += Manifold([Manifold.cube()]).rotate(0, 0, 45+180)
obj += Manifold.cube().rotate([0, 0, 45 + 180])
return obj
+121
View File
@@ -0,0 +1,121 @@
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")
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
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]]
+13
View File
@@ -0,0 +1,13 @@
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)
-1
View File
@@ -1 +0,0 @@
add_subdirectory(nanobind)
+10
View File
@@ -0,0 +1,10 @@
manifold.js
manifold.wasm
manifold-3d-*.tgz
lib/*.js
lib/*.js.map
lib/*.d.ts
lib/*.d.ts.map
dist/
coverage/
test/results
+39 -21
View File
@@ -12,35 +12,53 @@
# 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 manifold sdf cross_section polygon)
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>
)
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
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/)
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}
# copy WASM build and TS declarations for Vite to package into ManifoldCAD.org
COMMAND
${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/manifold*
${CMAKE_CURRENT_SOURCE_DIR}/examples/built/
# copy TS declarations to public so they can be accessed by our editor
COMMAND
${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/manifold*.d.ts
${CMAKE_CURRENT_SOURCE_DIR}/examples/public/
)
+37 -12
View File
@@ -1,31 +1,56 @@
# About Manifold
[![codecov](https://codecov.io/github/elalish/manifold/branch/master/graph/badge.svg?token=IIA8G5HVS7)](https://codecov.io/github/elalish/manifold)
[![npm version](https://badge.fury.io/js/manifold-3d.svg)](https://badge.fury.io/js/manifold-3d)
[![twitter](https://img.shields.io/twitter/follow/manifoldcad?style=social&logo=twitter)](https://twitter.com/intent/follow?screen_name=manifoldcad)
[**TS Documentation**](https://manifoldcad.org/jsdocs) | [**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) | [**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.
## [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.
![A metallic Menger sponge](https://elalish.github.io/manifold/samples/models/mengerSponge3.webp "A metallic Menger sponge")
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.
# Manifold
![A metallic Menger sponge](https://manifoldcad.org/samples/models/mengerSponge192.png "A metallic Menger sponge")
[**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)
## Manifold Library
[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).
## 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.
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.
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.
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.
## Note on memory management
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).
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
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).
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_mesh_manifold](https://github.com/KhronosGroup/glTF/pull/2286).
## Command Line Interface
ManifoldCAD has a [command line interface](./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.
This CLI is written in relatively plain JavaScript and serves as an example of how to use Manifold in a `node.js` environment.
## 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.
## About the author
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).
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).
@@ -0,0 +1,58 @@
{
"$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"
}
},
"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"
}
}
}
}
@@ -0,0 +1,61 @@
{
"$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"
}
},
"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"
}
}
}
}
+67
View File
@@ -0,0 +1,67 @@
#!/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, writeFile} from 'node:fs/promises';
import {extname} from 'node:path';
import {Command} from 'commander';
import {bundleFile} from '../lib/bundler.js';
import {Export3MF} from '../lib/export-3mf.js';
import {ExportGLTF} from '../lib/export-gltf.js';
import * as worker from '../lib/worker.js';
const exporters = [
new Export3MF(),
new ExportGLTF()
];
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.
const exporter = exporters.find(ex => ex.extensions.map(ext => `.${ext}`).includes(extname(outfile)));
if (!exporter) {
const extensions = exporters
.reduce((acc, cur) => ([... acc.extensions, ... cur.extensions ]))
.map(ext => `.${ext}`)
.reduceRight((prev, cur, index) => cur + (index ? ', or ' : ', ') + prev);
console.error(`Output file must be one of ${extensions}`);
process.exit(1);
}
// 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.
const binary = await exporter.asBlob(doc);
await writeFile(outfile, binary.stream());
});
program.parseAsync(process.argv);
+72 -43
View File
@@ -15,37 +15,55 @@
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <vector>
#include "cross_section.h"
#include "helpers.cpp"
#include "manifold.h"
#include "polygon.h"
#include "sdf.h"
#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
}
using namespace emscripten;
using namespace manifold;
EMSCRIPTEN_BINDINGS(whatever) {
value_object<glm::vec2>("vec2")
.field("x", &glm::vec2::x)
.field("y", &glm::vec2::y);
value_object<vec2>("vec2").field("x", &vec2::x).field("y", &vec2::y);
value_object<glm::ivec3>("ivec3")
.field("0", &glm::ivec3::x)
.field("1", &glm::ivec3::y)
.field("2", &glm::ivec3::z);
value_object<ivec3>("ivec3")
.field("0", &ivec3::x)
.field("1", &ivec3::y)
.field("2", &ivec3::z);
value_object<glm::vec3>("vec3")
.field("x", &glm::vec3::x)
.field("y", &glm::vec3::y)
.field("z", &glm::vec3::z);
value_object<vec3>("vec3")
.field("x", &vec3::x)
.field("y", &vec3::y)
.field("z", &vec3::z);
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);
value_object<vec4>("vec4")
.field("x", &vec4::x)
.field("y", &vec4::y)
.field("z", &vec4::z)
.field("w", &vec4::w);
enum_<Manifold::Error>("status")
.value("NoError", Manifold::Error::NoError)
@@ -72,7 +90,8 @@ EMSCRIPTEN_BINDINGS(whatever) {
enum_<CrossSection::JoinType>("jointype")
.value("Square", CrossSection::JoinType::Square)
.value("Round", CrossSection::JoinType::Round)
.value("Miter", CrossSection::JoinType::Miter);
.value("Miter", CrossSection::JoinType::Miter)
.value("Bevel", CrossSection::JoinType::Bevel);
value_object<Rect>("rect").field("min", &Rect::min).field("max", &Rect::max);
value_object<Box>("box").field("min", &Box::min).field("max", &Box::max);
@@ -81,19 +100,15 @@ EMSCRIPTEN_BINDINGS(whatever) {
.field("halfedge", &Smoothness::halfedge)
.field("smoothness", &Smoothness::smoothness);
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<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");
register_vector<CrossSection>("Vector_crossSection");
register_vector<Manifold>("Vector_manifold");
register_vector<Smoothness>("Vector_smoothness");
register_vector<glm::vec4>("Vector_vec4");
register_vector<vec4>("Vector_vec4");
class_<CrossSection>("CrossSection")
.constructor(&cross_js::OfPolygons)
@@ -112,7 +127,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",
@@ -126,9 +141,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<glm::vec2>)>(
&CrossSection::Hull));
function(
"_crossSectionHullPoints",
select_overload<CrossSection(std::vector<vec2>)>(&CrossSection::Hull));
class_<Manifold>("Manifold")
.constructor(&man_js::FromMeshJS)
@@ -138,9 +153,15 @@ EMSCRIPTEN_BINDINGS(whatever) {
.function("_Split", &man_js::Split)
.function("_SplitByPlane", &man_js::SplitByPlane)
.function("_TrimByPlane", &Manifold::TrimByPlane)
.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)
@@ -151,17 +172,22 @@ EMSCRIPTEN_BINDINGS(whatever) {
.function("_Decompose", select_overload<std::vector<Manifold>() const>(
&Manifold::Decompose))
.function("isEmpty", &Manifold::IsEmpty)
.function("status", &Manifold::Status)
.function("status", &man_js::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("precision", &Manifold::Precision)
.function("tolerance", &Manifold::GetTolerance)
.function("setTolerance", &Manifold::SetTolerance)
.function("simplify", &Manifold::Simplify)
.function("genus", &Manifold::Genus)
.function("getProperties", &Manifold::GetProperties)
.function("volume", &Manifold::Volume)
.function("surfaceArea", &Manifold::SurfaceArea)
.function("minGap", &Manifold::MinGap)
.function("calculateCurvature", &Manifold::CalculateCurvature)
.function("_CalculateNormals", &Manifold::CalculateNormals)
.function("originalID", &Manifold::OriginalID)
.function("asOriginal", &Manifold::AsOriginal);
@@ -182,13 +208,16 @@ EMSCRIPTEN_BINDINGS(whatever) {
function("_manifoldDifferenceN", &man_js::DifferenceN);
function("_manifoldIntersectionN", &man_js::IntersectionN);
function("_manifoldCollectVertices", &man_js::CollectVertices);
function("_manifoldHullPoints",
select_overload<Manifold(const std::vector<glm::vec3>&)>(
&Manifold::Hull));
function(
"_manifoldHullPoints",
select_overload<Manifold(const std::vector<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);
}
+89 -51
View File
@@ -17,6 +17,9 @@ 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) {
@@ -128,12 +131,12 @@ Module.setup = function() {
Module.CrossSection.prototype.warp = function(func) {
const wasmFuncPtr = addFunction(function(vec2Ptr) {
const x = getValue(vec2Ptr, 'float');
const y = getValue(vec2Ptr + 4, 'float');
const x = getValue(vec2Ptr, 'double');
const y = getValue(vec2Ptr + 8, 'double');
const vert = [x, y];
func(vert);
setValue(vec2Ptr, vert[0], 'float');
setValue(vec2Ptr + 4, vert[1], 'float');
setValue(vec2Ptr, vert[0], 'double');
setValue(vec2Ptr + 8, vert[1], 'double');
}, 'vi');
const out = this._Warp(wasmFuncPtr);
removeFunction(wasmFuncPtr);
@@ -156,23 +159,28 @@ Module.setup = function() {
};
Module.CrossSection.prototype.offset = function(
delta, joinType = 'Square', miterLimit = 2.0, circularSegments = 0) {
delta, joinType = 'Round', 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, height, nDivisions, twistDegrees, scaleTop);
const man = Module._Extrude(
this._ToPolygons(), 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, circularSegments, revolveDegrees);
return Module._Revolve(
this._ToPolygons(), circularSegments, revolveDegrees);
};
Module.CrossSection.prototype.add = function(other) {
@@ -189,54 +197,64 @@ Module.setup = function() {
Module.CrossSection.prototype.toPolygons = function() {
const vec = this._ToPolygons();
const result = vec2polygons(vec);
const result = vec2polygons(vec, v => [v.x, v.y]);
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, 'float');
const y = getValue(vec3Ptr + 4, 'float');
const z = getValue(vec3Ptr + 8, 'float');
const x = getValue(vec3Ptr, 'double');
const y = getValue(vec3Ptr + 8, 'double');
const z = getValue(vec3Ptr + 16, 'double');
const vert = [x, y, z];
func(vert);
setValue(vec3Ptr, vert[0], 'float');
setValue(vec3Ptr + 4, vert[1], 'float');
setValue(vec3Ptr + 8, vert[2], 'float');
setValue(vec3Ptr, vert[0], 'double');
setValue(vec3Ptr + 8, vert[1], 'double');
setValue(vec3Ptr + 16, vert[2], 'double');
}, 'vi');
const out = this._Warp(wasmFuncPtr);
removeFunction(wasmFuncPtr);
const status = out.status();
if (status.value !== 0) {
throw new Module.ManifoldError(status.value);
if (status !== 'NoError') {
throw new Module.ManifoldError(status);
}
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 + 4 * i, 'float');
newProp[i] = getValue(newPtr + 8 * i, 'double');
}
const pos = [];
for (let i = 0; i < 3; ++i) {
pos[i] = getValue(vec3Ptr + 4 * i, 'float');
pos[i] = getValue(vec3Ptr + 8 * i, 'double');
}
const oldProp = [];
for (let i = 0; i < oldNumProp; ++i) {
oldProp[i] = getValue(oldPtr + 4 * i, 'float');
oldProp[i] = getValue(oldPtr + 8 * i, 'double');
}
func(newProp, pos, oldProp);
for (let i = 0; i < numProp; ++i) {
setValue(newPtr + 4 * i, newProp[i], 'float');
setValue(newPtr + 8 * i, newProp[i], 'double');
}
}, 'viii');
const out = this._SetProperties(numProp, wasmFuncPtr);
@@ -248,8 +266,12 @@ Module.setup = function() {
return this._Translate(vararg2vec3(vec));
};
Module.Manifold.prototype.rotate = function(vec) {
return this._Rotate(...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.scale = function(vec) {
@@ -268,15 +290,29 @@ 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;
@@ -345,12 +381,13 @@ Module.setup = function() {
}
position(vert) {
return this.vertProperties.subarray(numProp * vert, numProp * vert + 3);
return this.vertProperties.subarray(
this.numProp * vert, this.numProp * vert + 3);
}
extras(vert) {
return this.vertProperties.subarray(
numProp * vert + 3, numProp * (vert + 1));
this.numProp * vert + 3, this.numProp * (vert + 1));
}
tangent(halfedge) {
@@ -371,45 +408,43 @@ Module.setup = function() {
Module.Mesh = Mesh;
Module.Manifold.prototype.getMesh = function(normalIdx = [0, 0, 0]) {
if (normalIdx instanceof Array)
normalIdx = {0: normalIdx[0], 1: normalIdx[1], 2: normalIdx[2]};
Module.Manifold.prototype.getMesh = function(normalIdx = -1) {
return new Mesh(this._GetMeshJS(normalIdx));
};
Module.ManifoldError = function ManifoldError(code, ...args) {
let message = 'Unknown error';
switch (code) {
case Module.status.NonFiniteVertex.value:
case 'NonFiniteVertex':
message = 'Non-finite vertex';
break;
case Module.status.NotManifold.value:
case 'NotManifold':
message = 'Not manifold';
break;
case Module.status.VertexOutOfBounds.value:
case 'VertexOutOfBounds':
message = 'Vertex index out of bounds';
break;
case Module.status.PropertiesWrongLength.value:
case 'PropertiesWrongLength':
message = 'Properties have wrong length';
break;
case Module.status.MissingPositionProperties.value:
case 'MissingPositionProperties':
message = 'Less than three properties';
break;
case Module.status.MergeVectorsDifferentLengths.value:
case 'MergeVectorsDifferentLengths':
message = 'Merge vectors have different lengths';
break;
case Module.status.MergeIndexOutOfBounds.value:
case 'MergeIndexOutOfBounds':
message = 'Merge index out of bounds';
break;
case Module.status.TransformWrongLength.value:
case 'TransformWrongLength':
message = 'Transform vector has wrong length';
break;
case Module.status.RunIndexWrongLength.value:
case 'RunIndexWrongLength':
message = 'Run index vector has wrong length';
break;
case Module.status.FaceIDWrongLength.value:
case 'FaceIDWrongLength':
message = 'Face ID vector has wrong length';
case Module.status.InvalidConstruction.value:
case 'InvalidConstruction':
message = 'Manifold constructed with invalid parameters';
}
@@ -519,8 +554,8 @@ Module.setup = function() {
const manifold = new ManifoldCtor(mesh);
const status = manifold.status();
if (status.value !== 0) {
throw new Module.ManifoldError(status.value);
if (status !== 'NoError') {
throw new Module.ManifoldError(status);
}
return manifold;
@@ -609,19 +644,21 @@ Module.setup = function() {
Module.Manifold.difference = manifoldBatchbool('Difference');
Module.Manifold.intersection = manifoldBatchbool('Intersection');
Module.Manifold.levelSet = function(sdf, bounds, edgeLength, level = 0) {
Module.Manifold.levelSet = function(
sdf, bounds, edgeLength, level = 0, tolerance = -1) {
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, 'float');
const y = getValue(vec3Ptr + 4, 'float');
const z = getValue(vec3Ptr + 8, 'float');
const x = getValue(vec3Ptr, 'double');
const y = getValue(vec3Ptr + 8, 'double');
const z = getValue(vec3Ptr + 16, 'double');
const vert = [x, y, z];
return sdf(vert);
}, 'fi');
const out = Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level);
}, 'di');
const out =
Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level, tolerance);
removeFunction(wasmFuncPtr);
return out;
};
@@ -665,10 +702,11 @@ Module.setup = function() {
// Top-level functions
Module.triangulate = function(polygons, precision = -1) {
Module.triangulate = function(polygons, epsilon = -1, allowConvex = true) {
const polygonsVec = polygons2vec(polygons);
const result = fromVec(
Module._Triangulate(polygonsVec, precision), (x) => [x[0], x[1], x[2]]);
Module._Triangulate(polygonsVec, epsilon, allowConvex),
(x) => [x[0], x[1], x[2]]);
disposePolygons(polygonsVec);
return result;
};
+33
View File
@@ -0,0 +1,33 @@
# Get Started
## Installation
In your project root folder run:
```bash
npm i 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);
```
## Next steps
In order to visualize Manifold mesh using Three.js library please check out our example [here](https://github.com/elalish/manifold/blob/master/bindings/wasm/examples/three.ts)
+14
View File
@@ -0,0 +1,14 @@
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', 'module',
]
});
+32
View File
@@ -0,0 +1,32 @@
import auger from '../test/examples/auger.mjs?raw';
import gearBearing from '../test/examples/gear-bearing.ts?raw';
import gyroidModule from '../test/examples/gyroid-module.mjs?raw';
import heart from '../test/examples/heart.mjs?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);
if (typeof self !== 'undefined') {
self.examples = examples;
}
+102 -6
View File
@@ -65,7 +65,7 @@ button:hover {
filter: brightness(0.9);
}
input {
.name {
border: none;
height: 36px;
background-color: transparent;
@@ -77,12 +77,12 @@ input {
-webkit-font-smoothing: antialiased;
}
input:focus {
.name:focus {
outline: none;
}
p {
margin: 1em;
margin: 18px;
text-align: left;
}
@@ -130,7 +130,7 @@ model-viewer {
.header .margin {
font-size: 16px;
margin: 1em 0.5em;
margin: 18px 8px;
}
.dropdown {
@@ -168,6 +168,29 @@ 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;
@@ -180,6 +203,22 @@ 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);
}
.cancel {
background-image: url(/icons/close.png);
background-size: 20px;
@@ -196,10 +235,22 @@ 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);
}
@@ -221,7 +272,52 @@ model-viewer {
width: 125px;
}
@media only screen and (max-width: 600px) {
#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;
}
@media only screen and (max-width: 820px) {
.container {
flex-direction: column;
width: 100%;
@@ -256,4 +352,4 @@ model-viewer {
width: calc(50% - 16px);
margin: 8px;
}
}
}
+263 -91
View File
@@ -12,10 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import ManifoldWorker from './worker?worker';
// '?url' is vite convention to reference a static asset.
// vite will package the asset and provide a proper URL.
import esbuildWasmUrl from 'esbuild-wasm/esbuild.wasm?url';
import {AutoTypings, 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';
import ManifoldWorker from '../lib/worker?worker';
import manifoldWasmUrl from '../manifold.wasm?url';
const CODE_START = '<code>';
// Loaded globally by examples.js
const exampleFunctions = self.examples.functionBodies;
const exampleFunctions = self.examples;
if (navigator.serviceWorker) {
navigator.serviceWorker.register(
@@ -24,6 +35,28 @@ if (navigator.serviceWorker) {
let editor = undefined;
// 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');
@@ -71,6 +104,28 @@ function nthKey(n) {
}
}
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;
@@ -90,10 +145,17 @@ function switchTo(scriptName) {
switching = true;
currentFileElement.textContent = scriptName;
setScript('currentName', scriptName);
const code =
exampleFunctions.get(scriptName) ?? getScript(scriptName) ?? '';
isExample = exampleFunctions.get(scriptName) != null;
editor.setValue(code);
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);
}
}
@@ -146,6 +208,7 @@ function addEdit(button) {
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);
@@ -203,16 +266,16 @@ function addEdit(button) {
}
const newButton = document.querySelector('#new');
function newItem(code) {
const name = uniqueName('New Script');
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);
nextButton.click();
return {button: nextButton, name};
};
newButton.onclick = function() {
newItem('');
newItem('').button.click();
};
const runButton = document.querySelector('#compile');
@@ -225,58 +288,60 @@ function initializeRun() {
if (autoExecute) {
runButton.click();
} else {
poster.textContent = 'Auto-run disabled due to prior failure';
poster.textContent = 'Auto-run disabled';
}
}
// 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 getDTS(url) {
const response = await fetch(url);
const text = await response.text();
return text.replace(/^export \{ }.*$/gm, '').replace(/^export /gm, '');
}
async function getEditorDTS() {
const global = await fetch('/editor.d.ts').then(response => response.text());
return `${global.replace(/^import.*$/gm, '')}`;
}
async function createEditor() {
self.MonacoEnvironment = {
getWorker: (_, label) => {
if (label === 'typescript' || label === 'javascript') {
return new tsWorker();
} else {
return new editorWorker();
}
}
};
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});
editor = monaco.editor.create(document.getElementById('editor'), {
language: 'typescript',
automaticLayout: true,
});
// Make sure `manifold-3d/manifoldCAD` types are available for import.
const wrap = (module, content) =>
`declare module '${module}' {\n${content}\n};`
const manifoldCadDTS =
wrap('manifold-3d/manifoldCAD', await getDTS('/manifoldCAD.d.ts'));
monaco.languages.typescript.typescriptDefaults.addExtraLib(manifoldCadDTS);
// Types for the global namespace.
const globalsDTS = await getDTS('/manifoldCADGlobals.d.ts');
monaco.languages.typescript.typescriptDefaults.addExtraLib(globalsDTS);
// 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.
self.window.typecache = new LocalStorageCache();
const autoTypings = await AutoTypings.create(editor, {
sourceCache: self.window.typecache,
onError: e => {console.error(e)},
onUpdate: (update, text) => {console.debug(text)},
onUpdateVersions: (versions) => {
console.debug(versions)
}
});
for (const [name] of exampleFunctions) {
const button = createDropdownItem(name);
@@ -298,30 +363,109 @@ require(['vs/editor/editor.main'], async function() {
addEdit(button);
}
}
switchTo(currentName);
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 => {
runButton.disabled = false;
if (switching) {
switching = false;
// monaco-editor-auto-typings loaded types. Do nothing.
if (autoTypings.isResolving && e.changes.isFlush) {
return;
}
// The user switched models.
if (switching) {
switching = false;
editor.setScrollTop(0);
runButton.disabled = false;
return;
}
// The user edited an example.
// Copy it into a new script.
if (isExample) {
const cursor = editor.getPosition();
newItem(editor.getValue());
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;
@@ -345,19 +489,13 @@ function disableCancel() {
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
@@ -366,41 +504,70 @@ let manifoldWorker = null;
function createWorker() {
manifoldWorker = new ManifoldWorker();
manifoldWorker.onmessage = function(e) {
if (e.data == null) {
manifoldWorker.onmessage = (e) => {
const message = e.data;
if (message?.type === 'ready') {
if (tsWorker != null && !manifoldInitialized) {
initializeRun();
}
manifoldInitialized = true;
return;
}
if (e.data.log != null) {
consoleElement.textContent += e.data.log + '\r\n';
} 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;
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';
// 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 {
} else if (message?.type === 'log') {
consoleElement.textContent += message.message + '\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();
@@ -411,9 +578,14 @@ async function run() {
enableCancel();
clearConsole();
console.log('Running...');
const output = await tsWorker.getEmitOutput(editor.getModel().uri.toString());
manifoldWorker.postMessage(output.outputFiles[0].text);
t0 = performance.now();
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, jsCDN: 'jsDelivr', files});
}
function cancel() {
-285
View File
@@ -1,285 +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.
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()));
}
+17 -6
View File
@@ -33,14 +33,21 @@
self.ModelViewerElement = self.ModelViewerElement || {};
self.ModelViewerElement.modelCacheSize = 0;
</script>
<script type="module" src="/examples.js"></script>
<script type="module" src="/editor-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 </p>
<div class="margin" id="file-margin" >
<p style="flex: 2;"><a target="_blank" href="https://github.com/elalish/manifold">Manifold</a>CAD
&nbsp;&nbsp;<span class="top-edit blue">
<button type="button" id="undo" class="top-icon undo squish"></button>
<button type="button" id="redo" class="top-icon redo squish"></button>
<button type="button" id="format" class="top-icon star squish"></button>
<button type="button" id="share" class="top-icon share"></button>
</span>
</p>
<div class="margin" id="file-margin">
<button type="button" id="file" class="blue item"><b class="uparrow"></b>&nbsp;&nbsp;<span
id="current">Intro</span></button>
<div class="dropdown" id="fileDropdown">
@@ -66,10 +73,14 @@
<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">
shadow-intensity="1" tone-mapping="neutral" 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="console"></div>
</div>
@@ -84,6 +95,6 @@
integrity="sha512-iQEIc0rsSDujsfjtD+lfyJ1W23Bh/lbgriubKDAym6VlEIDRj9rrbSIyJRyshOrl8s0yRcQ0+gyrZfSLyjJGWQ=="
crossorigin="anonymous" referrerpolicy="no-referrer">
<script type="module" src="editor.js"></script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.1.1/model-viewer.min.js"></script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.5.0/model-viewer.min.js"></script>
</html>
</html>
+4 -128
View File
@@ -46,7 +46,7 @@
<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
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
@@ -55,135 +55,11 @@
<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">
<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" 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>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.5.0/model-viewer.min.js"></script>
<script type="module" src="make-manifold.ts"></script>
</html>
+151
View File
@@ -0,0 +1,151 @@
// 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
//
// 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 {Document, WebIO} from '@gltf-transform/core';
import {KHRONOS_EXTENSIONS} from '@gltf-transform/extensions';
import {prune} from '@gltf-transform/functions';
import {SimpleDropzone} from 'simple-dropzone';
import {disposeMesh, Properties, readMesh, setupIO, writeMesh} from '../lib/gltf-io';
import Module from './built/manifold.js';
// Set up gltf-transform
const io = setupIO(new WebIO());
io.registerExtensions(KHRONOS_EXTENSIONS);
// Set up Manifold WASM library
const wasm = await Module();
wasm.setup();
const {Manifold, Mesh} = wasm;
// UX elements
const mv = document.querySelector('model-viewer');
const inputEl = document.querySelector('#input') as HTMLInputElement;
const downloadButton = document.querySelector('#download') as HTMLButtonElement;
const checkbox = document.querySelector('#viewFinal') as HTMLInputElement;
const dropCtrl = new SimpleDropzone(mv, inputEl) as any;
// glTF objects in memory
let inputGLBurl = '';
let outputGLBurl = '';
// Status of manifoldness of all meshes
let allManifold = true;
let anyManifold = false;
// The processing is run when a glTF is drag-and-dropped onto this element.
dropCtrl.on('drop', async ({files}: {files: Map<string, File>}) => {
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;
}
}
});
// UI functions
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 as any).src = checkbox.checked ? outputGLBurl : inputGLBurl;
downloadButton.disabled = !checkbox.checked;
};
downloadButton.onclick = () => {
const link = document.createElement('a');
link.download = 'manifold.glb';
link.href = outputGLBurl;
link.click();
};
// Write output glTF using gltf-transform, which contains only the meshes that
// are manifold, and using the EXT_mesh_manifold extension.
async function writeGLB(doc: Document): Promise<void> {
URL.revokeObjectURL(outputGLBurl);
if (!anyManifold) {
return;
}
const glb = await io.writeBinary(doc);
const blob = new Blob(
[glb as Uint8Array<ArrayBuffer>], {type: 'application/octet-stream'});
outputGLBurl = URL.createObjectURL(blob);
}
// Read the glTF ObjectURL and return a gltf-transform document with all the
// non-manifold meshes stripped out.
async function readGLB(url: string): Promise<Document> {
allManifold = false;
anyManifold = false;
updateUI();
allManifold = true;
const docIn = await io.read(url);
const nodes = docIn.getRoot().listNodes();
for (const node of nodes) {
const mesh = node.getMesh();
if (!mesh) continue;
const tmp = readMesh(mesh);
if (!tmp) continue;
const id2properties = new Map<number, Properties>();
const numID = tmp.runProperties.length;
const firstID = Manifold.reserveIDs(numID);
tmp.mesh.runOriginalID = new Uint32Array(numID);
for (let i = 0; i < numID; ++i) {
tmp.mesh.runOriginalID[i] = firstID + i;
id2properties.set(firstID + i, tmp.runProperties[i]);
}
const manifoldMesh = new Mesh(tmp.mesh);
disposeMesh(mesh);
// Make the mesh manifold if it's close.
manifoldMesh.merge();
try {
// Test manifoldness - will throw if not.
const manifold = new Manifold(manifoldMesh);
// Replace the mesh with a manifold version
node.setMesh(writeMesh(docIn, manifold.getMesh(), id2properties));
manifold.delete();
anyManifold = true;
} catch (e) {
console.log(mesh.getName(), e);
allManifold = false;
}
}
// Prune the leftovers after non-manifold mesh removal.
await docIn.transform(prune());
return docIn;
}
@@ -0,0 +1,126 @@
// 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
//
// 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 {Document, WebIO} from '@gltf-transform/core';
import {clearNodeTransform, flatten, mergeDocuments, prune} from '@gltf-transform/functions';
import {disposeMesh, Properties, readMesh, setupIO, writeMesh} from '../lib/gltf-io';
import Module, {Manifold, Mesh} from './built/manifold';
// Set up gltf-transform
const io = setupIO(new WebIO());
const doc = new Document();
// Set up Manifold WASM library
const wasm = await Module();
wasm.setup();
const {Manifold, Mesh} = wasm;
// Map of OriginalID to glTF material and attributes
const id2properties = new Map<number, Properties>();
// Wrapper for gltf-io readMesh() that processes a whole glTF and stores the
// properties in the above map. This function is simplified and intended only
// for reading single-object glTFs, as it simply unions any extra meshes
// together rather than returning a scene hierarchy.
async function readGLB(url: string) {
const manifolds = Array<Manifold>();
const docIn = await io.read(url);
await docIn.transform(flatten());
const nodes = docIn.getRoot().listNodes();
const ids = Array<number>();
for (const node of nodes) {
clearNodeTransform(node);
const gltfMesh = node.getMesh();
if (gltfMesh == null) continue;
const tmp = readMesh(gltfMesh);
if (tmp == null) continue;
const numID = tmp.runProperties.length;
const firstID = Manifold.reserveIDs(numID);
tmp.mesh.runOriginalID = new Uint32Array(numID);
for (let i = 0; i < numID; ++i) {
tmp.mesh.runOriginalID[i] = firstID + i;
ids.push(firstID + i);
id2properties.set(firstID + i, tmp.runProperties[i]);
}
manifolds.push(new Manifold(new Mesh(tmp.mesh)));
}
// pull in materials, TODO: replace with transfer() when available
const startIdx = doc.getRoot().listMaterials().length;
mergeDocuments(doc, docIn);
doc.getRoot().listScenes().forEach((s) => s.dispose());
doc.getRoot().listBuffers().forEach((s) => s.dispose());
doc.getRoot().listAccessors().forEach((s) => s.dispose());
for (const [i, id] of ids.entries()) {
const material = doc.getRoot().listMaterials()[startIdx + i];
id2properties.get(id)!.material = material;
}
return Manifold.union(manifolds);
}
// Read static input glTFs
const space = await readGLB('/models/space.glb');
const moon = await readGLB('/models/moon.glb');
const node = doc.createNode();
doc.createScene().addChild(node);
// Set up UI for operations
type BooleanOp = 'union'|'difference'|'intersection';
function csg(operation: BooleanOp) {
push2MV(Manifold[operation](space, moon));
}
csg('difference');
const selectElement = document.querySelector('select')!;
selectElement.onchange = function() {
csg(selectElement.value as BooleanOp);
};
// The resulting glTF
let objectURL = '';
// Set up download UI
const downloadButton = document.querySelector('#download') as HTMLButtonElement;
downloadButton.onclick = function() {
const link = document.createElement('a');
link.download = 'manifold.glb';
link.href = objectURL;
link.click();
};
// <model-viewer> element for rendering resulting glTF.
const mv = document.querySelector('model-viewer');
// Use gltf-io and gltf-transform to convert the resulting Manifold to a glTF
// and display it with <model-viewer>.
async function push2MV(manifold: Manifold) {
disposeMesh(node.getMesh()!);
const mesh = writeMesh(doc, manifold.getMesh(), id2properties);
node.setMesh(mesh);
await doc.transform(prune());
const glb = await io.writeBinary(doc);
const blob = new Blob(
[glb as Uint8Array<ArrayBuffer>], {type: 'application/octet-stream'});
URL.revokeObjectURL(objectURL);
objectURL = URL.createObjectURL(blob);
(mv as any).src = objectURL;
}
+12 -105
View File
@@ -35,117 +35,24 @@
</head>
<body>
<p>This example demonstrates feeding Manifold's output into <a
href="https://modelviewer.dev/">&lt;model-viewer&gt;</a> via <a
href="https://gltf-transform.donmccurdy.com/index.html">gltf-transform</a>. It also shows how to pass mesh
properties like UV coordinates through Manifold and how to re-associate textures after operations. A more basic
three.js example is <a href="three.html">here</a>. </p>
<p>This example demonstrates reading and writing glTF models using <a
href="https://gltf-transform.donmccurdy.com/index.html">gltf-transform</a> and using this to feed Manifold's
output into <a href="https://modelviewer.dev/">&lt;model-viewer&gt;</a>. It also shows how to pass mesh properties
like UV coordinates through Manifold and how to re-associate textures and materials after operations, made simple
using our `gltf-io.ts` library. The resulting glTF includes the EXT_mesh_manifold extension, using our
`manifold-gltf.ts` extension to gltf-transform, which ensures lossless data transfer.</p>
<p>Please open dev tools to inspect our source code - everything is source mapped back to our TS files and commented
thoroughly. A more basic three.js example is <a href="three.html">here</a>. </p>
<select>
<option value="difference" selected>Difference</option>
<option value="intersection">Intersection</option>
<option value="union">Union</option>
</select>
<button id="download">Download manifold GLB</button>
<model-viewer camera-controls shadow-intensity="1" alt="Output of mesh Boolean operation"></model-viewer>
<model-viewer camera-controls shadow-intensity="1" tone-mapping="neutral"
alt="Output of mesh Boolean operation"></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 { Document, WebIO } from '@gltf-transform/core';
import { clearNodeTransform, flatten, prune } from '@gltf-transform/functions';
import { loadTexture, readMesh, writeMesh, disposeMesh, setupIO } from './gltf-io.js';
import Module from './built/manifold.js';
const io = setupIO(new WebIO());
const doc = new Document();
const wasm = await Module();
wasm.setup();
const { Manifold, Mesh } = wasm;
const attributes = ['POSITION', 'TEXCOORD_0'];
const materialMap = new Map();
async function readGLB(url, attributes, materialMap) {
const manifolds = [];
const docIn = await io.read(url);
await docIn.transform(flatten());
const nodes = docIn.getRoot().listNodes();
const ids = [];
for (const node of nodes) {
clearNodeTransform(node);
const materials = [];
const manifoldMesh = readMesh(node.getMesh(), attributes, materials);
manifoldMesh.runOriginalID = [];
const firstID = Manifold.reserveIDs(materials.length);
for (let i = 0; i < materials.length; ++i) {
manifoldMesh.runOriginalID.push(firstID + i);
ids.push(firstID + i);
}
manifolds.push(Manifold(new Mesh(manifoldMesh)));
}
// pull in materials, TODO: replace with transfer() when available
const startIdx = doc.getRoot().listMaterials().length;
doc.merge(docIn);
doc.getRoot().listScenes().forEach((s) => s.dispose());
doc.getRoot().listBuffers().forEach((s) => s.dispose());
for (const [i, id] of ids.entries()) {
const material = doc.getRoot().listMaterials()[startIdx + i];
materialMap.set(id, material);
}
return Manifold.union(manifolds);
}
const space = await readGLB('/models/space.glb', attributes, materialMap);
const moon = await readGLB('/models/moon.glb', attributes, materialMap);
const node = doc.createNode();
doc.createScene().addChild(node);
function csg(operation) {
push2MV(Manifold[operation](space, moon));
};
document.querySelector('select').onchange = function (event) {
csg(event.target.value);
};
csg('difference');
const mv = document.querySelector('model-viewer');
let objectURL = null;
const downloadButton = document.querySelector('#download');
downloadButton.onclick = function () {
const link = document.createElement('a');
link.download = 'manifold.glb';
link.href = objectURL;
link.click();
};
async function push2MV(manifold) {
// From Z-up to Y-up (glTF)
const manifoldMesh = manifold.rotate([-90, 0, 0]).getMesh();
disposeMesh(node.getMesh());
const materials = [];
const matAttributes = [];
for (const id of manifoldMesh.runOriginalID) {
materials.push(materialMap.get(id));
matAttributes.push(attributes);
}
const mesh = writeMesh(doc, manifoldMesh, matAttributes, materials);
node.setMesh(mesh);
await doc.transform(prune());
const glb = await io.writeBinary(doc);
const blob = new Blob([glb], { type: 'application/octet-stream' });
URL.revokeObjectURL(objectURL);
objectURL = URL.createObjectURL(blob);
mv.src = objectURL;
}
</script>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.5.0/model-viewer.min.js"></script>
<script type="module" src="model-viewer-script.ts"></script>
</html>
+1691 -1744
View File
File diff suppressed because it is too large Load Diff
+18 -17
View File
@@ -7,26 +7,27 @@
"dev": "vite",
"build": "tsc --noEmit && vite build",
"build:watch": "vite build --watch",
"preview": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run"
"prebuild": "cd .. && npm run build",
"preinstall": "cd .. && npm ci",
"preview": "vite preview"
},
"dependencies": {
"@gltf-transform/core": "^3.2.1",
"@gltf-transform/extensions": "^3.2.1",
"@gltf-transform/functions": "^3.2.1",
"@jscadui/3mf-export": "^0.4.2",
"fflate": "^0.8.0",
"gl-matrix": "^3.4.3",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@gltf-transform/core": "^4.2.0",
"@gltf-transform/extensions": "^4.2.0",
"@gltf-transform/functions": "^4.2.0",
"@jscadui/3mf-export": "^0.5.0",
"@types/three": "^0.179.0",
"esbuild-wasm": "^0.25.11",
"monaco-editor": "^0.54.0",
"monaco-editor-auto-typings": "^0.4.6",
"simple-dropzone": "0.8.3",
"three": "0.151.2"
"three": "0.179.1"
},
"devDependencies": {
"@vitest/ui": "^0.31.1",
"@vitest/web-worker": "^0.31.1",
"typescript": "5.0.4",
"vite": "^4.3.2",
"vitest": "^0.31.1"
"@types/node": "^24.2.1",
"typescript": "^5.9.2",
"vite": "^6.3.5",
"vite-plugin-static-copy": "^3.1.3"
}
}
}
File diff suppressed because it is too large Load Diff
-531
View File
@@ -1,531 +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.
export const examples = {
functions: {
Intro: function() {
// Write code in JavaScript or TypeScript and this editor will show the
// API docs. Type e.g. "box." to see the Manifold API. Type
// "CrossSection." or "Manifold." to list the 2D and 3D constructors,
// respectively. Type "module." to see the static API - these functions
// can also be used bare. Use console.log() to print output (lower-right).
// This editor defines Z as up and units of mm.
const {cube, sphere} = Manifold;
const box = cube([100, 100, 100], true);
const ball = sphere(60, 100);
// You must name your final output "result", or create at least one
// GLTFNode - see Menger Sponge and Gyroid Module examples.
const result = box.subtract(ball);
// For visual debug, wrap any shape with show() and it and all of its
// copies will be shown in transparent red, akin to # in OpenSCAD. Or try
// only() to ghost out everything else, akin to * in OpenSCAD.
// All changes are automatically saved and restored between sessions.
// This PWA is purely local - there is no server communication.
// This means it will work equally well offline once loaded.
// Consider installing it (icon in the search bar) for easy access.
// See the script drop-down above ("Intro") for usage examples. The
// gl-matrix package from npm is automatically imported for convenience -
// its API is available in the top-level glMatrix object.
// Use GLTFNode for disjoint manifolds rather than compose(), as this will
// keep them better organized in the GLB. This will also allow you to
// specify material properties, and even vertex colors via
// setProperties(). See Tetrahedron Puzzle example.
return result;
},
TetrahedronPuzzle: function() {
// A tetrahedron cut into two identical halves that can screw together as
// a puzzle. This only outputs one of the halves. This demonstrates how
// redundant points along a polygon can be used to make twisted extrusions
// smoother. Based on the screw puzzle by George Hart:
// https://www.thingiverse.com/thing:186372
const edgeLength = 50; // Length of each edge of the overall tetrahedron.
const gap = 0.2; // Spacing between the two halves to allow sliding.
const nDivisions = 50; // Divisions (both ways) in the screw surface.
const scale = edgeLength / (2 * Math.sqrt(2));
const tet = Manifold.tetrahedron().scale(scale);
const box = [];
box.push([1, -1], [1, 1]);
for (let i = 0; i <= nDivisions; ++i) {
box.push([gap / (4 * scale), 1 - i * 2 / nDivisions]);
}
const cyan = [0, 1, 1];
const magenta = [1, 0, 1];
const fade = (color, pos) => {
for (let i = 0; i < 3; ++i) {
color[i] = cyan[i] * pos[2] + magenta[i] * (1 - pos[2]);
}
};
// setProperties(3, fade) creates three channels of vertex properties
// according to the above fade function. setMaterial assigns these
// channels as colors, and sets the factor to white, since our default is
// yellow.
const screw = setMaterial(
Manifold.extrude(box, 1, nDivisions, 270).setProperties(3, fade),
{baseColorFactor: [1, 1, 1], attributes: ['COLOR_0']});
const result = tet.intersect(
screw.rotate([0, 0, -45]).translate([0, 0, -0.5]).scale(2 * scale));
// Assigned materials are only applied to a GLTFNode. Note that material
// definitions cascade, applying recursively to all child surfaces, but
// overridden by any materials defined lower down. Our default material:
// {
// roughness = 0.2,
// metallic = 1,
// baseColorFactor = [1, 1, 0],
// alpha = 1,
// unlit = false,
// name = ''
// }
const node = new GLTFNode();
node.manifold = result;
return result;
},
RoundedFrame: function() {
// Demonstrates how at 90-degree intersections, the sphere and cylinder
// facets match up perfectly, for any choice of global resolution
// parameters.
const {sphere, cylinder, union} = Manifold;
function roundedFrame(edgeLength, radius, circularSegments = 0) {
const edge = cylinder(edgeLength, radius, -1, circularSegments);
const corner = sphere(radius, circularSegments);
const edge1 = union(corner, edge).rotate([-90, 0, 0]).translate([
-edgeLength / 2, -edgeLength / 2, 0
]);
const edge2 = union(
union(edge1, edge1.rotate([0, 0, 180])),
edge.translate([-edgeLength / 2, -edgeLength / 2, 0]));
const edge4 = union(edge2, edge2.rotate([0, 0, 90])).translate([
0, 0, -edgeLength / 2
]);
return union(edge4, edge4.rotate([180, 0, 0]));
}
setMinCircularAngle(3);
setMinCircularEdgeLength(0.5);
const result = roundedFrame(100, 10);
return result;
},
Heart: function() {
// Smooth, complex manifolds can be created using the warp() function.
// This example recreates the Exploitable Heart by Emmett Lalish:
// https://www.thingiverse.com/thing:6190
const func = (v) => {
const x2 = v[0] * v[0];
const y2 = v[1] * v[1];
const z = v[2];
const z2 = z * z;
const a = x2 + 9 / 4 * y2 + z2;
const b = z * z2 * (x2 + 9 / 80 * y2);
const a2 = a * a;
const a3 = a * a2;
const step = (r) => {
const r2 = r * r;
const r4 = r2 * r2;
// Taubin's function: https://mathworld.wolfram.com/HeartSurface.html
const f = a3 * r4 * r2 - b * r4 * r - 3 * a2 * r4 + 3 * a * r2 - 1;
// Derivative
const df =
6 * a3 * r4 * r - 5 * b * r4 - 12 * a2 * r2 * r + 6 * a * r;
return f / df;
};
// Newton's method for root finding
let r = 1.5;
let dr = 1;
while (Math.abs(dr) > 0.0001) {
dr = step(r);
r -= dr;
}
// Update radius
v[0] *= r;
v[1] *= r;
v[2] *= r;
};
const ball = Manifold.sphere(1, 200);
const heart = ball.warp(func);
const box = heart.boundingBox();
const result = heart.scale(100 / (box.max[0] - box.min[0]));
return result;
},
Scallop: function() {
// 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
const height = 10;
const radius = 30;
const offset = 20;
const wiggles = 12;
const sharpness = 0.8;
const n = 50;
const positions = [];
const triangles = [];
positions.push(-offset, 0, height, -offset, 0, -height);
const sharpenedEdges = [];
const delta = 3.14159 / wiggles;
for (let i = 0; i < 2 * wiggles; ++i) {
const theta = (i - wiggles) * delta;
const amp = 0.5 * height * Math.max(Math.cos(0.8 * theta), 0);
positions.push(
radius * Math.cos(theta), radius * Math.sin(theta),
amp * (i % 2 == 0 ? 1 : -1));
let j = i + 1;
if (j == 2 * wiggles) j = 0;
const smoothness = 1 - sharpness * Math.cos((theta + delta / 2) / 2);
let halfedge = triangles.length + 1;
sharpenedEdges.push({halfedge, smoothness});
triangles.push(0, 2 + i, 2 + j);
halfedge = triangles.length + 1;
sharpenedEdges.push({halfedge, smoothness});
triangles.push(1, 2 + j, 2 + i);
}
const triVerts = Uint32Array.from(triangles);
const vertProperties = Float32Array.from(positions);
const scallop = new Mesh({numProp: 3, triVerts, vertProperties});
const colorCurvature = (color, pos, oldProp) => {
const a = Math.max(0, Math.min(1, oldProp[0] / 3 + 0.5));
const b = a * a * (3 - 2 * a);
const red = [1, 0, 0];
const blue = [0, 0, 1];
for (let i = 0; i < 3; ++i) {
color[i] = (1 - b) * blue[i] + b * red[i];
}
};
const result = Manifold.smooth(scallop, sharpenedEdges)
.refine(n)
.calculateCurvature(-1, 0)
.setProperties(3, colorCurvature);
const node = new GLTFNode();
node.manifold = result;
node.material = {
baseColorFactor: [1, 1, 1],
metallic: 0,
attributes: ['COLOR_0']
};
return result;
},
TorusKnot: function() {
// 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.
// @param p The number of times the thread passes through the donut hole.
// @param q The number of times the thread circles the donut.
// @param majorRadius Radius of the interior of the imaginary donut.
// @param minorRadius Radius of the small cross-section of the imaginary
// donut.
// @param threadRadius Radius of the small cross-section of the actual
// object.
// @param circularSegments Number of linear segments making up the
// threadRadius circle. Default is getCircularSegments(threadRadius).
// @param linearSegments Number of segments along the length of the knot.
// Default makes roughly square facets.
function torusKnot(
p, q, majorRadius, minorRadius, threadRadius, circularSegments = 0,
linearSegments = 0) {
const {vec3} = glMatrix;
function gcd(a, b) {
return b == 0 ? a : gcd(b, a % b);
}
const kLoops = gcd(p, q);
p /= kLoops;
q /= kLoops;
const n = circularSegments > 2 ? circularSegments :
getCircularSegments(threadRadius);
const m = linearSegments > 2 ? linearSegments :
n * q * majorRadius / threadRadius;
const offset = 2
const circle = CrossSection.circle(1, n).translate([offset, 0]);
const func = (v) => {
const psi = q * Math.atan2(v[0], v[1]);
const theta = psi * p / q;
const x1 = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
const phi = Math.atan2(x1 - offset, v[2]);
vec3.set(
v, threadRadius * Math.cos(phi), 0, threadRadius * Math.sin(phi));
const center = vec3.fromValues(0, 0, 0);
const r = majorRadius + minorRadius * Math.cos(theta);
vec3.rotateX(v, v, center, -Math.atan2(p * minorRadius, q * r));
v[0] += minorRadius;
vec3.rotateY(v, v, center, theta);
v[0] += majorRadius;
vec3.rotateZ(v, v, center, psi);
};
let knot = Manifold.revolve(circle, m).warp(func);
if (kLoops > 1) {
const knots = [];
for (let k = 0; k < kLoops; ++k) {
knots.push(knot.rotate([0, 0, 360 * (k / kLoops) * (q / p)]));
}
knot = Manifold.compose(knots);
}
return knot;
}
// This recreates Matlab Knot by Emmett Lalish:
// https://www.thingiverse.com/thing:7080
const result = torusKnot(1, 3, 25, 10, 3.75);
return result;
},
MengerSponge: function() {
// This example demonstrates how symbolic perturbation correctly creates
// holes even though the subtracted objects are exactly coplanar.
const {vec2} = glMatrix;
function fractal(holes, hole, w, position, depth, maxDepth) {
w /= 3;
holes.push(
hole.scale([w, w, 1.0]).translate([position[0], position[1], 0.0]));
if (depth == maxDepth) return;
const offsets = [
vec2.fromValues(-w, -w), vec2.fromValues(-w, 0.0),
vec2.fromValues(-w, w), vec2.fromValues(0.0, w),
vec2.fromValues(w, w), vec2.fromValues(w, 0.0),
vec2.fromValues(w, -w), vec2.fromValues(0.0, -w)
];
for (let offset of offsets)
fractal(
holes, hole, w, vec2.add(offset, position, offset), depth + 1,
maxDepth);
}
function mengerSponge(n) {
let result = Manifold.cube([1, 1, 1], true);
const holes = [];
fractal(holes, result, 1.0, [0.0, 0.0], 1, n);
const hole = Manifold.compose(holes);
result = Manifold.difference(
result,
hole,
hole.rotate([90, 0, 0]),
hole.rotate([0, 90, 0]),
);
return result;
}
const posColors = (newProp, pos) => {
for (let i = 0; i < 3; ++i) {
newProp[i] = (1 - pos[i]) / 2;
}
};
const result = mengerSponge(3)
.trimByPlane([1, 1, 1], 0)
.setProperties(3, posColors)
.scale(100);
const node = new GLTFNode();
node.manifold = result;
node.material = {baseColorFactor: [1, 1, 1], attributes: ['COLOR_0']};
return result;
},
StretchyBracelet: function() {
// Recreates Stretchy Bracelet by Emmett Lalish:
// https://www.thingiverse.com/thing:13505
const {vec2} = glMatrix;
function base(
width, radius, decorRadius, twistRadius, nDecor, innerRadius,
outerRadius, cut, nCut, nDivision) {
let b = Manifold.cylinder(width, radius + twistRadius / 2);
const circle = [];
const dPhiDeg = 180 / nDivision;
for (let i = 0; i < 2 * nDivision; ++i) {
circle.push([
decorRadius * Math.cos(dPhiDeg * i * Math.PI / 180) + twistRadius,
decorRadius * Math.sin(dPhiDeg * i * Math.PI / 180)
]);
}
let decor = Manifold.extrude(circle, width, nDivision, 180)
.scale([1, 0.5, 1])
.translate([0, radius, 0]);
for (let i = 0; i < nDecor; i++)
b = b.add(decor.rotate([0, 0, (360.0 / nDecor) * i]));
const stretch = [];
const dPhiRad = 2 * Math.PI / nCut;
const o = vec2.fromValues(0, 0);
const p0 = vec2.fromValues(outerRadius, 0);
const p1 = vec2.fromValues(innerRadius, -cut);
const p2 = vec2.fromValues(innerRadius, cut);
for (let i = 0; i < nCut; ++i) {
stretch.push(vec2.rotate([0, 0], p0, o, dPhiRad * i));
stretch.push(vec2.rotate([0, 0], p1, o, dPhiRad * i));
stretch.push(vec2.rotate([0, 0], p2, o, dPhiRad * i));
stretch.push(vec2.rotate([0, 0], p0, o, dPhiRad * i));
}
const result =
Manifold.intersection(Manifold.extrude(stretch, width), b);
return result;
}
function stretchyBracelet(
radius = 30, height = 8, width = 15, thickness = 0.4, nDecor = 20,
nCut = 27, nDivision = 30) {
const twistRadius = Math.PI * radius / nDecor;
const decorRadius = twistRadius * 1.5;
const outerRadius = radius + (decorRadius + twistRadius) * 0.5;
const innerRadius = outerRadius - height;
const cut = 0.5 * (Math.PI * 2 * innerRadius / nCut - thickness);
const adjThickness = 0.5 * thickness * height / cut;
return Manifold.difference(
base(
width, radius, decorRadius, twistRadius, nDecor,
innerRadius + thickness, outerRadius + adjThickness,
cut - adjThickness, nCut, nDivision),
base(
width, radius - thickness, decorRadius, twistRadius, nDecor,
innerRadius, outerRadius + 3 * adjThickness, cut, nCut,
nDivision));
}
const result = stretchyBracelet();
return result;
},
GyroidModule: function() {
// Recreates Modular Gyroid Puzzle by Emmett Lalish:
// https://www.thingiverse.com/thing:25477. This sample demonstrates the
// use of a Signed Distance Function (SDF) to create smooth, complex
// manifolds.
const {vec3} = glMatrix;
// number of modules along pyramid edge (use 1 for print orientation)
const m = 4;
// module size
const size = 20;
// SDF resolution
const n = 20;
const pi = 3.14159;
function gyroid(p) {
const x = p[0] - pi / 4;
const y = p[1] - pi / 4;
const z = p[2] - pi / 4;
return Math.cos(x) * Math.sin(y) + Math.cos(y) * Math.sin(z) +
Math.cos(z) * Math.sin(x);
}
function gyroidOffset(level) {
const period = 2 * pi;
const box = {
min: vec3.fromValues(-period, -period, -period),
max: vec3.fromValues(period, period, period)
};
return Manifold.levelSet(gyroid, box, period / n, level)
.scale(size / period);
};
function rhombicDodecahedron() {
const box = Manifold.cube([1, 1, 2], true).scale(size * Math.sqrt(2));
const result =
box.rotate([90, 45, 0]).intersect(box.rotate([90, 45, 90]));
return result.intersect(box.rotate([0, 0, 45]));
}
const gyroidModule = rhombicDodecahedron()
.intersect(gyroidOffset(-0.4))
.subtract(gyroidOffset(0.4));
if (m > 1) {
for (let i = 0; i < m; ++i) {
for (let j = i; j < m; ++j) {
for (let k = j; k < m; ++k) {
const node = new GLTFNode();
node.manifold = gyroidModule;
node.translation =
[(k + i - j) * size, (k - i) * size, (-j) * size];
node.material = {
baseColorFactor:
[(k + i - j + 1) / m, (k - i + 1) / m, (j + 1) / m]
};
}
}
}
}
const result = gyroidModule.rotate([-45, 0, 90]).translate([
0, 0, size / Math.sqrt(2)
]);
return result;
}
},
functionBodies: new Map()
};
for (const [func, code] of Object.entries(examples.functions)) {
const whole = code.toString();
const lines = whole.split('\n');
lines.splice(0, 1); // remove first line
lines.splice(-2, 2); // remove last two lines
// remove first six leading spaces
const body = '\n' + lines.map(l => l.slice(6)).join('\n');
const name =
func.replace(/([a-z])([A-Z])/g, '$1 $2'); // Add spaces between words
examples.functionBodies.set(name, body);
};
if (typeof self !== 'undefined') {
self.examples = examples;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
declare module 'simple-dropzone';
+11 -73
View File
@@ -35,83 +35,21 @@
</head>
<body>
<p>This demonstrates the minimum code to connect Manifold to a three.js rendering. A slightly more complex example
involving GLTF export and &lt;model-viewer&gt; can be found <a href="model-viewer.html">here</a>. </p>
<p>This example demonstrates interop between Manifold and Three.js with minimal code - please open dev tools to
inspect the source. Here we generate two multi-material Three.js meshes and convert them to Manifolds while building
a mapping from material to Manifold ID. Then Boolean operations are performed on the Manifolds and the result is
converted back to a Three.js mesh using the material mapping. The input cube has half red faces and half
normal-shaded, while the icosahedron has half blue faces and half normal-shaded. The resulting mesh has three
materials, since one (normal-shaded) was common between the input models.</p>
<p>A slightly more complex example involving our library for GLTF import/export and &lt;model-viewer&gt; can be found
<a href="model-viewer.html">here</a>. </p>
<select>
<option value="difference" selected>Difference</option>
<option value="union" selected>Union</option>
<option value="difference">Difference</option>
<option value="intersection">Intersection</option>
<option value="union">Union</option>
</select><br />
<canvas id="output"></canvas>
</body>
<script type="module">
import * as THREE from 'three';
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
import Module from './built/manifold.js';
const wasm = await Module();
wasm.setup();
const { Manifold, Mesh } = wasm;
// we have manifold module, let's do some three.js
const camera = new THREE.PerspectiveCamera(30, 1, 0.01, 10);
camera.position.z = 1;
const scene = new THREE.Scene();
const mesh = new THREE.Mesh(undefined, new THREE.MeshNormalMaterial({
flatShading: true
}));
scene.add(mesh);
const icosahedron = simplify(new THREE.IcosahedronGeometry(0.16));
const manifold_1 = Manifold.cube([0.2, 0.2, 0.2], true);
const manifold_2 = new Manifold(geometry2mesh(icosahedron));
const csg = function (operation) {
mesh.geometry?.dispose();
mesh.geometry = mesh2geometry(Manifold[operation](manifold_1, manifold_2).getMesh());
};
document.querySelector('select').onchange = function (event) {
csg(event.target.value);
};
csg('difference');
const output = document.querySelector("#output");
const renderer = new THREE.WebGLRenderer({ canvas: output, antialias: true });
const dim = output.getBoundingClientRect();
renderer.setSize(dim.width, dim.height);
renderer.setAnimationLoop(function (time) {
mesh.rotation.x = time / 2000;
mesh.rotation.y = time / 1000;
renderer.render(scene, camera);
});
// functions to convert between three.js and wasm
function geometry2mesh(geometry) {
const vertProperties = geometry.attributes.position.array;
const triVerts = geometry.index.array;
return new Mesh({ numProp: 3, vertProperties, triVerts });
}
function mesh2geometry(mesh) {
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(mesh.vertProperties, 3));
geometry.setIndex(new THREE.BufferAttribute(mesh.triVerts, 1));
return geometry;
}
// most of three.js geometries aren't manifolds, so...
function simplify(geometry) {
delete geometry.attributes.normal;
delete geometry.attributes.uv;
const simplified = mergeVertices(geometry);
simplified.computeVertexNormals();
return simplified;
}
</script>
<script type="module" src="three.ts"></script>
</html>
+148
View File
@@ -0,0 +1,148 @@
// 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
//
// 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 {BoxGeometry, BufferAttribute, BufferGeometry, IcosahedronGeometry, Mesh as ThreeMesh, MeshLambertMaterial, MeshNormalMaterial, PerspectiveCamera, PointLight, Scene, WebGLRenderer} from 'three';
import Module, {Mesh} from './built/manifold';
// Load Manifold WASM library
const wasm = await Module();
wasm.setup();
const {Manifold, Mesh} = wasm;
// Define our set of materials
const materials = [
new MeshNormalMaterial({flatShading: true}),
new MeshLambertMaterial({color: 'red', flatShading: true}),
new MeshLambertMaterial({color: 'blue', flatShading: true})
];
const result = new ThreeMesh(undefined, materials);
// Set up Manifold IDs corresponding to materials
const firstID = Manifold.reserveIDs(materials.length);
// ids vector is parallel to materials vector - same indexing
const ids = [...Array<number>(materials.length)].map((_, idx) => firstID + idx);
// Build a mapping to get back from ID to material index
const id2matIndex = new Map();
ids.forEach((id, idx) => id2matIndex.set(id, idx));
// Set up Three.js scene
const scene = new Scene();
const camera = new PerspectiveCamera(30, 1, 0.01, 10);
camera.position.z = 1;
camera.add(new PointLight(0xffffff, 1));
scene.add(camera);
scene.add(result);
// Set up Three.js renderer
const output = document.querySelector('#output')!;
const renderer = new WebGLRenderer({canvas: output, antialias: true});
const dim = output.getBoundingClientRect();
renderer.setSize(dim.width, dim.height);
renderer.setAnimationLoop(function(time: number) {
result.rotation.x = time / 2000;
result.rotation.y = time / 1000;
renderer.render(scene, camera);
});
// Create input meshes in Three.js
const cube = new BoxGeometry(0.2, 0.2, 0.2);
cube.clearGroups();
cube.addGroup(0, 18, 0); // First 6 faces colored by normal
cube.addGroup(18, Infinity, 1); // Rest of faces are red
const icosahedron = new IcosahedronGeometry(0.16);
icosahedron.clearGroups();
icosahedron.addGroup(30, Infinity, 2); // Last faces are blue
icosahedron.addGroup(0, 30, 0); // First 10 faces colored by normal
// The above groups are in reversed order to demonstrate the need for sorting.
// Convert Three.js input meshes to Manifolds
const manifoldCube = new Manifold(geometry2mesh(cube));
const manifoldIcosahedron = new Manifold(geometry2mesh(icosahedron));
// Set up UI for operations
type BooleanOp = 'union'|'difference'|'intersection';
function csg(operation: BooleanOp) {
result.geometry?.dispose();
result.geometry = mesh2geometry(
Manifold[operation](manifoldCube, manifoldIcosahedron).getMesh());
}
csg('union');
const selectElement = document.querySelector('select')!;
selectElement.onchange = function() {
csg(selectElement.value as BooleanOp);
};
// Convert Three.js BufferGeometry to Manifold Mesh
function geometry2mesh(geometry: BufferGeometry) {
// Only using position in this sample for simplicity. Can interleave any other
// desired attributes here such as UV, normal, etc.
const vertProperties = geometry.attributes.position.array as Float32Array;
// Manifold only uses indexed geometry, so generate an index if necessary.
const triVerts = geometry.index != null ?
geometry.index.array as Uint32Array :
new Uint32Array(vertProperties.length / 3).map((_, idx) => idx);
// Create a triangle run for each group (material) - akin to a draw call.
const starts = [...Array(geometry.groups.length)].map(
(_, idx) => geometry.groups[idx].start);
// Map the materials to ID.
const originalIDs = [...Array(geometry.groups.length)].map(
(_, idx) => ids[geometry.groups[idx].materialIndex!]);
// List the runs in sequence.
const indices = Array.from(starts.keys())
indices.sort((a, b) => starts[a] - starts[b])
const runIndex = new Uint32Array(indices.map(i => starts[i]));
const runOriginalID = new Uint32Array(indices.map(i => originalIDs[i]));
// Create the MeshGL for I/O with Manifold library.
const mesh =
new Mesh({numProp: 3, vertProperties, triVerts, runIndex, runOriginalID});
// Automatically merge vertices with nearly identical positions to create a
// Manifold. This only fills in the mergeFromVert and mergeToVert vectors -
// these are automatically filled in for any mesh returned by Manifold. These
// are necessary because GL drivers require duplicate verts when any
// properties change, e.g. a UV boundary or sharp corner.
mesh.merge();
return mesh;
}
// Convert Manifold Mesh to Three.js BufferGeometry
function mesh2geometry(mesh: Mesh) {
const geometry = new BufferGeometry();
// Assign buffers
geometry.setAttribute(
'position', new BufferAttribute(mesh.vertProperties, 3));
geometry.setIndex(new BufferAttribute(mesh.triVerts, 1));
// Create a group (material) for each ID. Note that there may be multiple
// triangle runs returned with the same ID, though these will always be
// sequential since they are sorted by ID. In this example there are two runs
// for the MeshNormalMaterial, one corresponding to each input mesh that had
// this ID. This allows runTransform to return the total transformation matrix
// applied to each triangle run from its input mesh - even after many
// consecutive operations.
let id = mesh.runOriginalID[0];
let start = mesh.runIndex[0];
for (let run = 0; run < mesh.numRun; ++run) {
const nextID = mesh.runOriginalID[run + 1];
if (nextID !== id) {
const end = mesh.runIndex[run + 1];
geometry.addGroup(start, end - start, id2matIndex.get(id));
id = nextID;
start = end;
}
}
return geometry;
}
+8 -3
View File
@@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"moduleResolution": "Bundler",
"lib": [
"es2022",
"dom"
@@ -16,9 +16,14 @@
"noFallthroughCasesInSwitch": true,
"declaration": true,
"skipLibCheck": true,
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
},
"include": [
"./*.ts"
"./*.ts",
"../lib/*.ts",
"../lib/gltf-transform/*.ts"
],
"exclude": []
}
}
@@ -0,0 +1,48 @@
// https://gist.github.com/jamsinclair/6ad148d0590291077a4ce389c2b274ea
import {createFilter} from 'vite';
function isEmscriptenFile(code) {
return /var\s+Module\s*=|WebAssembly\.instantiate/.test(code) &&
/var\s+workerOptions\s*=/.test(code);
}
/**
* Vite plugin that replaces Emscripten workerOptions with static object literal
* to fix error with Vite See project issue:
* https://github.com/emscripten-core/emscripten/issues/22394
*
* Defaults to running for all .js and .ts files. If there are any issues you
* can use the include/exclude options.
*
* @param {Object} options
* @property {string[]} [include] - Glob patterns to include
* @property {string[]} [exclude] - Glob patterns to exclude
* @returns {import('vite').Plugin}
*/
export default function emscriptenStaticWorkerOptions(options = {}) {
const filter = createFilter(options.include || /\.[jt]s$/, options.exclude);
return {
name: 'emscripten-static-worker-options',
enforce: 'pre',
transform(code, id) {
if (!filter(id)) return null;
if (!isEmscriptenFile(code)) return null;
const workerOptionsMatch =
code.match(/var\s+workerOptions\s*=\s*({[^}]+})/);
if (!workerOptionsMatch) return null;
const optionsObjectStr = workerOptionsMatch[1];
const optionsDeclarationStr = workerOptionsMatch[0];
const modifiedCode =
code.replace(optionsDeclarationStr, '')
.replace(
new RegExp('workerOptions(?![\\w$])', 'g'), optionsObjectStr);
return {code: modifiedCode, map: null};
}
};
}
+50 -4
View File
@@ -1,14 +1,60 @@
// vite.config.js
import {resolve} from 'path'
import {defineConfig} from 'vite'
import {viteStaticCopy} from 'vite-plugin-static-copy'
import emscriptenStaticWorkerOptions from './vite-fixup-plugin.js'
export default defineConfig({
test: {testTimeout: 15000},
worker: {
format: 'es',
worker: {format: 'es', plugins: () => ([emscriptenStaticWorkerOptions])},
server: {
headers: {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
},
fs: {allow: [resolve(__dirname, '..')]}
},
plugins: [viteStaticCopy({
targets: [
{
src: '../dist/manifoldCAD.d.ts',
dest: './', // Targets are relative to 'dist'.
},
{
src: '../dist/manifoldCADGlobals.d.ts',
dest: './',
},
// If 'dist/manifold-encapsulated-types.d.ts' and
// 'dist/manifold-global-types.d.ts' are missing, the web editor can't
// load them, and type validation won't work for our core modules.
//
// They are generated when building the wasm module. But we don't
// really need to build the whole thing when working up here in JS/TS
// territory. Additionally, they rarely change. In the case where the
// binding files are not present, grab the checked in copies, and use
// those instead. But if they _are_ present, don't overwrite them.
{
src: '../manifold-encapsulated-types.d.ts',
dest: './',
overwrite: false,
},
{
src: '../manifold-global-types.d.ts',
dest: './',
overwrite: false,
}
],
})],
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'),
@@ -21,6 +67,6 @@ export default defineConfig({
chunkFileNames: `assets/[name].js`,
assetFileNames: `assets/[name].[ext]`
}
},
}
},
})
-152
View File
@@ -1,152 +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.
import '@vitest/web-worker';
import {WebIO} from '@gltf-transform/core';
import {expect, suite, test} from 'vitest';
import Module from './built/manifold.js';
import {readMesh, setupIO} from './gltf-io.js';
import {examples} from './public/examples.js';
import ManifoldWorker from './worker?worker';
const io = setupIO(new WebIO());
const wasm = await Module();
wasm.setup();
function initialized(worker) {
return new Promise((resolve) => {
worker.onmessage = function(e) {
if (e.data == null) {
resolve();
} else {
reject();
}
};
});
}
let glbURL = null;
async function runExample(name) {
const worker = new ManifoldWorker();
await initialized(worker);
return new Promise((resolve, reject) => {
worker.onmessageerror = function({data}) {
reject(data);
};
worker.onerror = function(e) {
reject(e);
};
worker.onmessage = async function(e) {
try {
URL.revokeObjectURL(glbURL);
glbURL = e.data.glbURL;
if (glbURL == null) {
reject('no glbURL)');
}
const docIn = await io.read(glbURL);
const nodes = docIn.getRoot().listNodes();
for (const node of nodes) {
const mesh = node.getMesh();
if (!mesh) {
continue;
}
const attributes = [];
const materials = [];
const manifoldMesh = readMesh(mesh, attributes, materials);
const manifold = wasm.Manifold(manifoldMesh);
const prop = manifold.getProperties();
const genus = manifold.genus();
console.log(genus);
manifold.delete();
resolve({...prop, genus});
}
} catch (e) {
reject(e);
}
};
worker.postMessage(examples.functionBodies.get(name));
});
}
suite('Examples', () => {
test('Intro', async () => {
const result = await runExample('Intro');
expect(result.genus).to.equal(5, 'Genus');
expect(result.volume).to.be.closeTo(203164, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(62046, 1, 'Surface Area');
});
test('Tetrahedron Puzzle', async () => {
const result = await runExample('Tetrahedron Puzzle');
expect(result.genus).to.equal(0, 'Genus');
expect(result.volume).to.be.closeTo(7297, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(3303, 1, 'Surface Area');
});
test('Rounded Frame', async () => {
const result = await runExample('Rounded Frame');
expect(result.genus).to.equal(5, 'Genus');
expect(result.volume).to.be.closeTo(353706, 10, 'Volume');
expect(result.surfaceArea).to.be.closeTo(68454, 1, 'Surface Area');
});
test('Heart', async () => {
const result = await runExample('Heart');
expect(result.genus).to.equal(0, 'Genus');
expect(result.volume).to.be.closeTo(282743, 10, 'Volume');
expect(result.surfaceArea).to.be.closeTo(22187, 1, 'Surface Area');
});
test('Scallop', async () => {
const result = await runExample('Scallop');
expect(result.genus).to.equal(0, 'Genus');
expect(result.volume).to.be.closeTo(41284, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(7810, 1, 'Surface Area');
});
test('Torus Knot', async () => {
const result = await runExample('Torus Knot');
expect(result.genus).to.equal(1, 'Genus');
expect(result.volume).to.be.closeTo(20786, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(11177, 1, 'Surface Area');
});
test('Menger Sponge', async () => {
const result = await runExample('Menger Sponge');
expect(result.genus).to.equal(729, 'Genus');
expect(result.volume).to.be.closeTo(203222, 10, 'Volume');
expect(result.surfaceArea).to.be.closeTo(130475, 10, 'Surface Area');
});
test('Stretchy Bracelet', async () => {
const result = await runExample('Stretchy Bracelet');
expect(result.genus).to.equal(1, 'Genus');
expect(result.volume).to.be.closeTo(3992, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(22267, 1, 'Surface Area');
});
test('Gyroid Module', async () => {
const result = await runExample('Gyroid Module');
expect(result.genus).to.equal(15, 'Genus');
expect(result.volume).to.be.closeTo(4167, 1, 'Volume');
expect(result.surfaceArea).to.be.closeTo(5642, 1, 'Surface Area');
});
});
-566
View File
@@ -1,566 +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.
import {Document, mat4, Material, Node, WebIO} from '@gltf-transform/core';
import {KHRMaterialsUnlit, KHRONOS_EXTENSIONS} from '@gltf-transform/extensions';
import {fileForContentTypes, to3dmodel} from '@jscadui/3mf-export';
import {strToU8, Zippable, zipSync} from 'fflate'
import * as glMatrix from 'gl-matrix';
import Module from './built/manifold';
//@ts-ignore
import {setupIO, writeMesh} from './gltf-io';
import type {GLTFMaterial, Quat} from './public/editor';
import type {CrossSection, Manifold, ManifoldToplevel, Mesh, Vec3} from './public/manifold';
interface WorkerStatic extends ManifoldToplevel {
GLTFNode: typeof GLTFNode;
show(manifold: Manifold): Manifold;
only(manifold: Manifold): Manifold;
setMaterial(manifold: Manifold, material: GLTFMaterial): void;
cleanup(): void;
}
const module = await Module() as unknown as WorkerStatic;
module.setup();
// Faster on modern browsers than Float32Array
glMatrix.glMatrix.setMatrixArrayType(Array);
const io = setupIO(new WebIO());
io.registerExtensions(KHRONOS_EXTENSIONS);
// manifold static methods (that return a new manifold)
const manifoldStaticFunctions = [
'cube', 'cylinder', 'sphere', 'tetrahedron', 'extrude', 'revolve', 'compose',
'union', 'difference', 'intersection', 'levelSet', 'smooth', 'ofMesh', 'hull'
];
// manifold member functions (that return a new manifold)
const manifoldMemberFunctions = [
'add', 'subtract', 'intersect', 'decompose', 'warp', 'transform', 'translate',
'rotate', 'scale', 'mirror', 'refine', 'setProperties', 'asOriginal',
'trimByPlane', 'split', 'splitByPlane', 'hull'
];
// CrossSection static methods (that return a new cross-section)
const crossSectionStaticFunctions = [
'square', 'circle', 'union', 'difference', 'intersection', 'compose',
'ofPolygons', 'hull'
];
// CrossSection member functions (that return a new cross-section)
const crossSectionMemberFunctions = [
'add', 'subtract', 'intersect', 'rectClip', 'decompose', 'transform',
'translate', 'rotate', 'scale', 'mirror', 'simplify', 'offset', 'hull'
];
// top level functions that construct a new manifold/mesh
const toplevelConstructors = ['show', 'only', 'setMaterial'];
const toplevel = [
'setMinCircularAngle', 'setMinCircularEdgeLength', 'setCircularSegments',
'getCircularSegments', 'Mesh', 'GLTFNode', 'Manifold', 'CrossSection'
];
const exposedFunctions = toplevelConstructors.concat(toplevel);
// Setup memory management, such that users don't have to care about
// calling `delete` manually.
// Note that this only fixes memory leak across different runs: the memory
// will only be freed when the compilation finishes.
const memoryRegistry = new Array<Manifold|CrossSection>();
function addMembers(
className: string, methodNames: Array<string>, areStatic: boolean) {
//@ts-ignore
const cls = module[className];
const obj = areStatic ? cls : cls.prototype;
for (const name of methodNames) {
if (name != 'cylinder') {
const originalFn = obj[name];
obj[name] = function(...args: any) {
//@ts-ignore
const result = originalFn(...args);
memoryRegistry.push(result);
return result;
};
}
}
}
addMembers('Manifold', manifoldMemberFunctions, false);
addMembers('Manifold', manifoldStaticFunctions, true);
addMembers('CrossSection', crossSectionMemberFunctions, false);
addMembers('CrossSection', crossSectionStaticFunctions, true);
for (const name of toplevelConstructors) {
//@ts-ignore
const originalFn = module[name];
//@ts-ignore
module[name] = function(...args: any) {
const result = originalFn(...args);
memoryRegistry.push(result);
return result;
};
}
module.cleanup = function() {
for (const obj of memoryRegistry) {
// decompose result is an array of manifolds
if (obj instanceof Array)
for (const elem of obj) elem.delete();
else
obj.delete();
}
memoryRegistry.length = 0;
};
// Debug setup to show source meshes
let ghost = false;
const shown = new Map<number, Mesh>();
const singles = new Map<number, Mesh>();
const SHOW = {
baseColorFactor: [1, 0, 0],
alpha: 0.25,
roughness: 1,
metallic: 0
} as GLTFMaterial;
const GHOST = {
baseColorFactor: [0.5, 0.5, 0.5],
alpha: 0.25,
roughness: 1,
metallic: 0
} as GLTFMaterial;
const nodes = new Array<GLTFNode>();
const id2material = new Map<number, GLTFMaterial>();
const materialCache = new Map<GLTFMaterial, Material>();
let nextGlobalID = 0;
const object2globalID = new Map<GLTFNode|Manifold, number>();
function cleanup() {
ghost = false;
shown.clear();
singles.clear();
nodes.length = 0;
id2material.clear();
materialCache.clear();
object2globalID.clear();
nextGlobalID = 0;
}
interface Mesh3MF {
id: string;
vertices: Float32Array;
indices: Uint32Array;
name?: string;
}
interface Child3MF {
objectID: string;
transform?: mat4;
}
interface Component3MF {
id: string;
children: Array<Child3MF>;
name?: string;
}
interface Header {
unit?: 'micron'|'millimeter'|'centimeter'|'inch'|'foot'|'meter';
title?: string;
author?: string;
description?: string;
application?: string;
creationDate?: string;
license?: string;
modificationDate?: string;
}
interface To3MF {
meshes: Array<Mesh3MF>;
components: Array<Component3MF>;
items: Array<Child3MF>;
precision: number;
header: Header;
}
class GLTFNode {
private _parent?: GLTFNode;
manifold?: Manifold;
translation?: Vec3;
rotation?: Vec3;
scale?: Vec3;
material?: GLTFMaterial;
name?: string;
constructor(parent?: GLTFNode) {
this._parent = parent;
nodes.push(this);
}
clone(parent?: GLTFNode) {
const copy = {...this};
copy._parent = parent;
nodes.push(copy);
return copy;
}
get parent() {
return this._parent;
}
}
module.GLTFNode = GLTFNode;
module.setMaterial = (manifold: Manifold, material: GLTFMaterial): Manifold => {
const out = manifold.asOriginal();
id2material.set(out.originalID(), material);
return out;
};
function debug(manifold: Manifold, map: Map<number, Mesh>) {
let result = manifold.asOriginal();
map.set(result.originalID(), result.getMesh());
return result;
};
module.show = (manifold) => {
return debug(manifold, shown);
};
module.only = (manifold) => {
ghost = true;
return debug(manifold, singles);
};
// Setup complete
self.postMessage(null);
if (self.console) {
const oldLog = self.console.log;
self.console.log = function(...args) {
let message = '';
for (const arg of args) {
if (arg == null) {
message += 'undefined';
} else if (typeof arg == 'object') {
message += JSON.stringify(arg, null, 4);
} else {
message += arg.toString();
}
}
self.postMessage({log: message});
oldLog(...args);
};
}
// Swallow informational logs in testing framework
function log(...args: any[]) {
if (self.console) {
self.console.log(...args);
}
}
self.onmessage = async (e) => {
const content = e.data +
'\nreturn exportModels(typeof result === "undefined" ? undefined : result);\n';
try {
const f = new Function(
'exportModels', 'glMatrix', 'module', ...exposedFunctions, content);
await f(
exportModels, glMatrix, module, //@ts-ignore
...exposedFunctions.map(name => module[name]));
} catch (error: any) {
console.log(error.toString());
self.postMessage({objectURL: null});
} finally {
module.cleanup();
cleanup();
}
};
function createGLTFnode(doc: Document, node: GLTFNode) {
const out = doc.createNode(node.name);
if (node.translation) {
out.setTranslation(node.translation);
}
if (node.rotation) {
const {quat} = glMatrix;
const deg2rad = Math.PI / 180;
const q = quat.create() as Quat;
quat.rotateX(q, q, deg2rad * node.rotation[0]);
quat.rotateY(q, q, deg2rad * node.rotation[1]);
quat.rotateZ(q, q, deg2rad * node.rotation[2]);
out.setRotation(q);
}
if (node.scale) {
out.setScale(node.scale);
}
return out;
}
function getBackupMaterial(node?: GLTFNode): GLTFMaterial {
if (node == null) {
return {};
}
if (node.material == null) {
node.material = getBackupMaterial(node.parent);
}
return node.material;
}
function makeDefaultedMaterial(doc: Document, {
roughness = 0.2,
metallic = 1,
baseColorFactor = [1, 1, 0],
alpha = 1,
unlit = false,
name = ''
}: GLTFMaterial = {}): Material {
const material = doc.createMaterial(name);
if (unlit) {
const unlit = doc.createExtension(KHRMaterialsUnlit).createUnlit();
material.setExtension('KHR_materials_unlit', unlit);
}
if (alpha < 1) {
material.setAlphaMode(Material.AlphaMode.BLEND).setDoubleSided(true);
}
return material.setRoughnessFactor(roughness)
.setMetallicFactor(metallic)
.setBaseColorFactor([...baseColorFactor, alpha]);
}
function getCachedMaterial(doc: Document, matDef: GLTFMaterial): Material {
if (!materialCache.has(matDef)) {
materialCache.set(matDef, makeDefaultedMaterial(doc, matDef));
}
return materialCache.get(matDef)!;
}
function addMesh(
doc: Document, to3mf: To3MF, node: Node, manifold: Manifold,
backupMaterial: GLTFMaterial = {}) {
const numTri = manifold.numTri();
if (numTri == 0) {
log('Empty manifold, skipping.');
return;
}
log(`Triangles: ${numTri.toLocaleString()}`);
const box = manifold.boundingBox();
const size = [0, 0, 0];
for (let i = 0; i < 3; i++) {
size[i] = Math.round((box.max[i] - box.min[i]) * 10) / 10;
}
log(`Bounding Box: X = ${size[0].toLocaleString()} mm, Y = ${
size[1].toLocaleString()} mm, Z = ${size[2].toLocaleString()} mm`);
const volume = Math.round(manifold.getProperties().volume / 10);
log(`Genus: ${manifold.genus().toLocaleString()}, Volume: ${
(volume / 100).toLocaleString()} cm^3`);
// From Z-up to Y-up (glTF)
const manifoldMesh = manifold.getMesh();
const materials = new Array<GLTFMaterial>();
const attributes = new Array<Array<string>>();
for (const id of manifoldMesh.runOriginalID!) {
const material = id2material.get(id) || backupMaterial;
materials.push(material);
attributes.push(['POSITION', ...material.attributes ?? []]);
}
const gltfMaterials = materials.map((matDef) => {
return getCachedMaterial(doc, ghost ? GHOST : matDef);
});
node.setMesh(writeMesh(doc, manifoldMesh, attributes, gltfMaterials));
const vertices = manifoldMesh.numProp === 3 ?
manifoldMesh.vertProperties :
new Float32Array(manifoldMesh.numVert * 3);
if (manifoldMesh.numProp > 3) {
for (let i = 0; i < manifoldMesh.numVert; ++i) {
for (let j = 0; j < 3; ++j)
vertices[i * 3 + j] =
manifoldMesh.vertProperties[i * manifoldMesh.numProp + j];
}
}
object2globalID.set(manifold, nextGlobalID);
to3mf.meshes.push(
{vertices, indices: manifoldMesh.triVerts, id: `${nextGlobalID++}`});
for (const [run, id] of manifoldMesh.runOriginalID!.entries()) {
let inMesh = shown.get(id);
let single = false;
if (inMesh == null) {
single = true;
inMesh = singles.get(id);
}
if (inMesh == null) {
continue;
}
const mat = single ? materials[run] : SHOW;
const debugNode =
doc.createNode('debug')
.setMesh(writeMesh(
doc, inMesh, attributes, [getCachedMaterial(doc, mat)]))
.setMatrix(manifoldMesh.transform(run));
node.addChild(debugNode);
}
}
function cloneNode(toNode: Node, fromNode: Node) {
toNode.setMesh(fromNode.getMesh());
fromNode.listChildren().forEach((child) => {
const clone = child.clone();
toNode.addChild(clone);
});
}
function cloneNodeNewMaterial(
doc: Document, toNode: Node, fromNode: Node, backupMaterial: Material,
oldBackupMaterial: Material) {
cloneNode(toNode, fromNode);
const mesh = doc.createMesh();
toNode.setMesh(mesh);
fromNode.getMesh()!.listPrimitives().forEach((primitive) => {
const newPrimitive = primitive.clone();
if (primitive.getMaterial() === oldBackupMaterial) {
newPrimitive.setMaterial(backupMaterial);
}
mesh.addPrimitive(newPrimitive);
});
}
function createNodeFromCache(
doc: Document, to3MF: To3MF, nodeDef: GLTFNode,
manifold2node: Map<Manifold, Map<GLTFMaterial, Node>>): Node {
const node = createGLTFnode(doc, nodeDef);
const {manifold} = nodeDef;
if (manifold != null) {
const backupMaterial = getBackupMaterial(nodeDef);
const cachedNodes = manifold2node.get(manifold);
if (cachedNodes == null) {
addMesh(doc, to3MF, node, manifold, backupMaterial);
const cache = new Map<GLTFMaterial, Node>();
cache.set(backupMaterial, node);
manifold2node.set(manifold, cache);
} else {
const cachedNode = cachedNodes.get(backupMaterial);
if (cachedNode == null) {
const [oldBackupMaterial, oldNode] = cachedNodes.entries().next().value;
cloneNodeNewMaterial(
doc, node, oldNode, getCachedMaterial(doc, backupMaterial),
getCachedMaterial(doc, oldBackupMaterial));
cachedNodes.set(backupMaterial, node);
} else {
cloneNode(node, cachedNode);
}
}
}
object2globalID.set(nodeDef, nextGlobalID);
to3MF.components.push({
id: `${nextGlobalID++}`,
name: nodeDef.name,
children:
manifold == null ? [] : [{objectID: `${object2globalID.get(manifold)}`}]
});
return node;
}
async function exportModels(manifold?: Manifold) {
const doc = new Document();
const halfRoot2 = Math.sqrt(2) / 2;
const mm2m = 1 / 1000;
const wrapper = doc.createNode('wrapper')
.setRotation([-halfRoot2, 0, 0, halfRoot2])
.setScale([mm2m, mm2m, mm2m]);
doc.createScene().addChild(wrapper);
const to3mf = {
meshes: [],
components: [],
items: [],
precision: 7,
header: {
unit: 'millimeter',
title: 'ManifoldCAD.org model',
description: 'ManifoldCAD.org model',
application: 'ManifoldCAD.org',
}
} as To3MF;
if (nodes.length > 0) {
const node2gltf = new Map<GLTFNode, Node>();
const manifold2node = new Map<Manifold, Map<GLTFMaterial, Node>>();
let leafNodes = 0;
for (const nodeDef of nodes) {
node2gltf.set(
nodeDef, createNodeFromCache(doc, to3mf, nodeDef, manifold2node));
if (nodeDef.manifold) {
++leafNodes;
}
}
for (const nodeDef of nodes) {
const gltfNode = node2gltf.get(nodeDef)!;
const child = {
objectID: `${object2globalID.get(nodeDef)}`,
transform: gltfNode.getMatrix()
};
const {parent} = nodeDef;
if (parent == null) {
wrapper.addChild(gltfNode);
to3mf.items.push(child);
} else {
node2gltf.get(parent)!.addChild(gltfNode);
const parent3mf = to3mf.components.find(
(comp) => comp.id == `${object2globalID.get(parent)}`)!;
parent3mf.children.push(child);
}
}
log('Total glTF nodes: ', nodes.length,
', Total mesh references: ', leafNodes);
} else {
if (manifold == null) {
log('No output because "result" is undefined and no "GLTFNode"s were created.');
return;
}
const node = doc.createNode();
addMesh(doc, to3mf, node, manifold);
wrapper.addChild(node);
to3mf.items.push({objectID: `${object2globalID.get(manifold)}`});
}
const glb = await io.writeBinary(doc);
const blobGLB = new Blob([glb], {type: 'application/octet-stream'});
const model = to3dmodel(to3mf);
const files: Zippable = {};
files['3D/3dmodel.model'] = strToU8(model);
files[fileForContentTypes.name] = strToU8(fileForContentTypes.content);
const zipFile = zipSync(files);
const blob3MF = new Blob(
[zipFile],
{type: 'application/vnd.ms-package.3dmanufacturing-3dmodel+xml'});
self.postMessage({
glbURL: URL.createObjectURL(blobGLB),
threeMFURL: URL.createObjectURL(blob3MF)
});
}
+59 -27
View File
@@ -3,10 +3,9 @@
#include <vector>
#include "cross_section.h"
#include "manifold.h"
#include "polygon.h"
#include "sdf.h"
#include "manifold/cross_section.h"
#include "manifold/manifold.h"
#include "manifold/polygon.h"
using namespace emscripten;
using namespace manifold;
@@ -84,7 +83,7 @@ MeshGL MeshJS2GL(const val& mesh) {
return out;
}
val GetMeshJS(const Manifold& manifold, const glm::ivec3& normalIdx) {
val GetMeshJS(const Manifold& manifold, int normalIdx) {
MeshGL mesh = manifold.GetMeshGL(normalIdx);
return MeshGL2JS(mesh);
}
@@ -106,7 +105,7 @@ Manifold Smooth(const val& mesh,
} // namespace js
namespace cross_js {
CrossSection OfPolygons(std::vector<std::vector<glm::vec2>> polygons,
CrossSection OfPolygons(std::vector<std::vector<vec2>> polygons,
int fill_rule) {
auto fr = fill_rule == 0 ? CrossSection::FillRule::EvenOdd
: fill_rule == 1 ? CrossSection::FillRule::NonZero
@@ -140,15 +139,15 @@ CrossSection IntersectionN(const std::vector<CrossSection>& cross_sections) {
}
CrossSection Transform(CrossSection& cross_section, const val& mat) {
std::vector<float> array = convertJSArrayToNumberVector<float>(mat);
glm::mat3x2 matrix;
std::vector<double> array = convertJSArrayToNumberVector<double>(mat);
mat2x3 matrix;
for (const int col : {0, 1, 2})
for (const int row : {0, 1}) matrix[col][row] = array[col * 3 + row];
return cross_section.Transform(matrix);
}
CrossSection Warp(CrossSection& cross_section, uintptr_t funcPtr) {
void (*f)(glm::vec2&) = reinterpret_cast<void (*)(glm::vec2&)>(funcPtr);
void (*f)(vec2&) = reinterpret_cast<void (*)(vec2&)>(funcPtr);
return cross_section.Warp(f);
}
@@ -156,11 +155,12 @@ CrossSection Offset(CrossSection& cross_section, double delta, int join_type,
double miter_limit, double arc_tolerance) {
auto jt = join_type == 0 ? CrossSection::JoinType::Square
: join_type == 1 ? CrossSection::JoinType::Round
: CrossSection::JoinType::Miter;
: join_type == 2 ? CrossSection::JoinType::Miter
: CrossSection::JoinType::Bevel;
return cross_section.Offset(delta, jt, miter_limit, arc_tolerance);
}
void CollectVertices(std::vector<glm::vec2>& verts, const CrossSection& cs) {
void CollectVertices(std::vector<vec2>& verts, const CrossSection& cs) {
auto polys = cs.ToPolygons();
for (auto poly : polys) verts.insert(verts.end(), poly.begin(), poly.end());
}
@@ -188,30 +188,59 @@ Manifold IntersectionN(const std::vector<Manifold>& manifolds) {
}
Manifold Transform(Manifold& manifold, const val& mat) {
std::vector<float> array = convertJSArrayToNumberVector<float>(mat);
glm::mat4x3 matrix;
std::vector<double> array = convertJSArrayToNumberVector<double>(mat);
mat3x4 matrix;
for (const int col : {0, 1, 2, 3})
for (const int row : {0, 1, 2}) matrix[col][row] = array[col * 4 + row];
return manifold.Transform(matrix);
}
Manifold Warp(Manifold& manifold, uintptr_t funcPtr) {
void (*f)(glm::vec3&) = reinterpret_cast<void (*)(glm::vec3&)>(funcPtr);
void (*f)(vec3&) = reinterpret_cast<void (*)(vec3&)>(funcPtr);
return manifold.Warp(f);
}
Manifold SetProperties(Manifold& manifold, int numProp, uintptr_t funcPtr) {
void (*f)(float*, glm::vec3, const float*) =
reinterpret_cast<void (*)(float*, glm::vec3, const float*)>(funcPtr);
void (*f)(double*, vec3, const double*) =
reinterpret_cast<void (*)(double*, vec3, const double*)>(funcPtr);
return manifold.SetProperties(numProp, f);
}
Manifold LevelSet(uintptr_t funcPtr, Box bounds, float edgeLength,
float level) {
float (*f)(const glm::vec3&) =
reinterpret_cast<float (*)(const glm::vec3&)>(funcPtr);
Mesh m = LevelSet(f, bounds, edgeLength, level);
return Manifold(m);
Manifold LevelSet(uintptr_t funcPtr, Box bounds, double edgeLength,
double level, double tolerance) {
double (*f)(const vec3&) = reinterpret_cast<double (*)(const vec3&)>(funcPtr);
return Manifold::LevelSet(f, bounds, edgeLength, level, tolerance, false);
}
std::string Status(Manifold& manifold) {
switch (manifold.Status()) {
case Manifold::Error::NoError:
return "NoError";
case Manifold::Error::NonFiniteVertex:
return "NonFiniteVertex";
case Manifold::Error::NotManifold:
return "NotManifold";
case Manifold::Error::VertexOutOfBounds:
return "VertexOutOfBounds";
case Manifold::Error::PropertiesWrongLength:
return "PropertiesWrongLength";
case Manifold::Error::MissingPositionProperties:
return "MissingPositionProperties";
case Manifold::Error::MergeVectorsDifferentLengths:
return "MergeVectorsDifferentLengths";
case Manifold::Error::MergeIndexOutOfBounds:
return "MergeIndexOutOfBounds";
case Manifold::Error::TransformWrongLength:
return "TransformWrongLength";
case Manifold::Error::RunIndexWrongLength:
return "RunIndexWrongLength";
case Manifold::Error::FaceIDWrongLength:
return "FaceIDWrongLength";
case Manifold::Error::InvalidConstruction:
return "InvalidConstruction";
default:
return "UnknownError";
}
}
std::vector<Manifold> Split(Manifold& a, Manifold& b) {
@@ -219,14 +248,17 @@ std::vector<Manifold> Split(Manifold& a, Manifold& b) {
return {r1, r2};
}
std::vector<Manifold> SplitByPlane(Manifold& m, glm::vec3 normal,
float originOffset) {
std::vector<Manifold> SplitByPlane(Manifold& m, vec3 normal,
double originOffset) {
auto [a, b] = m.SplitByPlane(normal, originOffset);
return {a, b};
}
void CollectVertices(std::vector<glm::vec3>& verts, const Manifold& manifold) {
Mesh mesh = manifold.GetMesh();
verts.insert(verts.end(), mesh.vertPos.begin(), mesh.vertPos.end());
void CollectVertices(std::vector<vec3>& verts, const Manifold& manifold) {
const MeshGL64 mesh = manifold.GetMeshGL64();
const auto numVert = mesh.NumVert();
for (size_t v = 0; v < numVert; ++v) {
verts.push_back(mesh.GetVertPos(v));
}
}
} // namespace man_js
+1
View File
@@ -0,0 +1 @@
declare module '@jscadui/3mf-export';
+319
View File
@@ -0,0 +1,319 @@
// Copyright 2022-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 {Accessor, Animation, AnimationSampler, Document, Mesh as GLTFMesh, Node} from '@gltf-transform/core';
import {Manifold, Mesh} from '../manifold-encapsulated-types';
import {Vec3} from '../manifold-global-types';
import {GLTFNode} from './gltf-node.ts';
import {euler2quat} from './math.ts';
/**
* @inline
*/
export type AnimationMode = 'loop'|'ping-pong';
let animationMode: AnimationMode = 'loop';
let animationDuration: number = 1;
let animationFPS: number = 30;
/**
* Set the animation repeat mode.
*
* @param mode 'loop' or 'ping-pong'
* @group Global Settings
*/
export const setAnimationMode = (mode: AnimationMode): void => {
animationMode = mode
};
/**
* Get the current animation repeat mode.
* @group Global Settings
*/
export const getAnimationMode = (): AnimationMode => animationMode;
/**
* Set the duration of the animation, in seconds.
*
* @param duration in seconds.
* @group Global Settings
*/
export const setAnimationDuration = (duration: number): void => {
animationDuration = duration;
};
/**
* Get the current duruation of the animation, in seconds.
* @group Global Settings
*/
export const getAnimationDuration = (): number => animationDuration;
/**
* Set the animation frame rate.
*
* @param fps in frames per second.
* @group Global Settings
*/
export const setAnimationFPS = (fps: number): void => {
animationFPS = fps;
};
/**
* Get the current animation frame rate.
* @group Global Settings
*/
export const getAnimationFPS = (): number => animationFPS;
/**
*
* @internal
*/
export interface Morph {
start?: (v: Vec3) => void;
end?: (v: Vec3) => void;
}
const manifold2morph = new Map<Manifold, Morph>();
let animation: Animation;
let timesAccessor: Accessor;
let weightsAccessor: Accessor;
let weightsSampler: AnimationSampler;
let hasAnimation: boolean;
export function cleanup() {
manifold2morph.clear();
animationMode = 'loop';
animationDuration = 1;
animationFPS = 30;
}
/**
*
* @internal
*/
export function addMotion(
doc: Document, type: 'translation'|'rotation'|'scale', node: GLTFNode,
out: Node): Vec3|null {
const motion = node[type];
if (motion == null) {
return null;
}
if (typeof motion !== 'function') {
return motion;
}
const nFrames = timesAccessor.getCount();
const nEl = type == 'rotation' ? 4 : 3;
const frames = new Float32Array(nEl * nFrames);
for (let i = 0; i < nFrames; ++i) {
const x = i / (nFrames - 1);
const m = motion(
animationMode !== 'ping-pong' ? x :
(1 - Math.cos(x * 2 * Math.PI)) / 2);
frames.set(nEl === 4 ? euler2quat(m) : m, nEl * i);
}
const framesAccessor =
doc.createAccessor(node.name + ' ' + type + ' frames')
.setBuffer(doc.getRoot().listBuffers()[0])
.setArray(frames)
.setType(nEl === 4 ? Accessor.Type.VEC4 : Accessor.Type.VEC3);
const sampler = doc.createAnimationSampler()
.setInput(timesAccessor)
.setOutput(framesAccessor)
.setInterpolation('LINEAR');
const channel = doc.createAnimationChannel()
.setTargetPath(type)
.setTargetNode(out)
.setSampler(sampler);
animation.addSampler(sampler);
animation.addChannel(channel);
hasAnimation = true;
return motion(0);
}
/**
*
* @internal
*/
export function setMorph(doc: Document, node: Node, manifold: Manifold) {
if (manifold2morph.has(manifold)) {
const channel = doc.createAnimationChannel()
.setTargetPath('weights')
.setTargetNode(node)
.setSampler(weightsSampler);
animation.addChannel(channel);
hasAnimation = true;
}
}
/**
*
* @internal
*/
export const getMorph = (manifold: Manifold) => manifold2morph.get(manifold)
/**
*
* @internal
*/
export function morphStart(manifoldMesh: Mesh, morph?: Morph):
number[] {
const inputPositions: number[] = [];
if (morph == null) {
return inputPositions;
}
for (let i = 0; i < manifoldMesh.numVert; ++i) {
for (let j = 0; j < 3; ++j)
inputPositions[i * 3 + j] =
manifoldMesh.vertProperties[i * manifoldMesh.numProp + j];
}
if (morph.start) {
for (let i = 0; i < manifoldMesh.numVert; ++i) {
const vertProp = manifoldMesh.vertProperties;
const offset = i * manifoldMesh.numProp;
const pos = inputPositions.slice(offset, offset + 3) as Vec3;
morph.start(pos);
for (let j = 0; j < 3; ++j) vertProp[offset + j] = pos[j];
}
}
return inputPositions;
}
/**
*
* @internal
*/
export function morphEnd(
doc: Document, manifoldMesh: Mesh, mesh: GLTFMesh, inputPositions: number[],
morph?: Morph) {
if (morph == null) {
return;
}
mesh.setWeights([0]);
mesh.listPrimitives().forEach((primitive, i) => {
if (morph.end) {
for (let i = 0; i < manifoldMesh.numVert; ++i) {
const pos = inputPositions.slice(3 * i, 3 * (i + 1)) as Vec3;
morph.end(pos);
inputPositions.splice(3 * i, 3, ...pos);
}
}
const startPosition = primitive.getAttribute('POSITION')!.getArray()!;
const array = new Float32Array(startPosition.length);
const offset = manifoldMesh.runIndex[i];
for (let j = 0; j < array.length; ++j) {
array[j] = inputPositions[offset + j] - startPosition[j];
}
const morphAccessor = doc.createAccessor(mesh.getName() + ' morph target')
.setBuffer(doc.getRoot().listBuffers()[0])
.setArray(array)
.setType(Accessor.Type.VEC3);
const morphTarget =
doc.createPrimitiveTarget().setAttribute('POSITION', morphAccessor);
primitive.addTarget(morphTarget);
});
}
/**
* Apply a morphing animation to the input manifold. Specify the start
* function which will be applied to the vertex positions of the first frame and
* linearly interpolated across the length of the overall animation. This
* animation will only be shown if this manifold is used directly on a GLTFNode.
*
* @group Modelling Functions
* @param manifold The object to add morphing animation to.
* @param func A warping function to apply to the first animation frame.
*/
export const setMorphStart =
(manifold: Manifold, func: (v: Vec3) => void): void => {
const morph = manifold2morph.get(manifold);
if (morph != null) {
morph.start = func;
} else {
manifold2morph.set(manifold, {start: func});
}
};
/**
* Apply a morphing animation to the input manifold. Specify the end
* function which will be applied to the vertex positions of the last frame and
* linearly interpolated across the length of the overall animation. This
* animation will only be shown if this manifold is used directly on a GLTFNode.
*
* @group Modelling Functions
* @param manifold The object to add morphing animation to.
* @param func A warping function to apply to the last animation frame.
*/
export const setMorphEnd =
(manifold: Manifold, func: (v: Vec3) => void): void => {
const morph = manifold2morph.get(manifold);
if (morph != null) {
morph.end = func;
} else {
manifold2morph.set(manifold, {end: func});
}
};
/**
*
* @internal
*/
export function addAnimationToDoc(doc: Document) {
animation = doc.createAnimation('');
hasAnimation = false;
const nFrames = Math.round(animationDuration * animationFPS) + 1;
const times = new Float32Array(nFrames);
const weights = new Float32Array(nFrames);
for (let i = 0; i < nFrames; ++i) {
const x = i / (nFrames - 1);
times[i] = x * animationDuration;
weights[i] =
animationMode !== 'ping-pong' ? x : (1 - Math.cos(x * 2 * Math.PI)) / 2;
}
timesAccessor = doc.createAccessor('animation times')
.setBuffer(doc.createBuffer())
.setArray(times)
.setType(Accessor.Type.SCALAR);
weightsAccessor = doc.createAccessor('animation weights')
.setBuffer(doc.getRoot().listBuffers()[0])
.setArray(weights)
.setType(Accessor.Type.SCALAR);
weightsSampler = doc.createAnimationSampler()
.setInput(timesAccessor)
.setOutput(weightsAccessor)
.setInterpolation('LINEAR');
animation.addSampler(weightsSampler);
}
/**
*
* @internal
*/
export function cleanupAnimationInDoc() {
if (!hasAnimation) {
timesAccessor.dispose();
weightsAccessor.dispose();
weightsSampler.dispose();
animation.dispose();
}
}
+305
View File
@@ -0,0 +1,305 @@
// 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 type {BuildFailure} from 'esbuild';
import * as esbuild from 'esbuild-wasm';
import {BundlerError} from './error.ts';
import {isNode} from './util.ts';
let esbuildWasmUrl: string|null = null;
let esbuildHasOwnWorker: boolean = false;
export const setWasmUrl = (url: string) => {
esbuildWasmUrl = url;
};
export const setHasOwnWorker = (x: boolean) => {
esbuildHasOwnWorker = x;
};
/**
* These content delivery networks provide NPM modules as ES modules,
* whether they were published that way or not.
*/
export const cdnUrlHelpers: {[key: string]: (specifier: string) => string} = {
'esm.sh': (specifier) => `https://esm.sh/${specifier}`,
'jsDelivr': (specifier) => `https://cdn.jsdelivr.net/npm/${specifier}/+esm`,
'skypack': (specifier) => `https://cdn.skypack.dev/${specifier}`
};
const cdnUrl = (specifier: string, jsCDN?: string) => {
if (!jsCDN) return specifier;
const helper = cdnUrlHelpers[jsCDN];
return helper ? helper(specifier) : `${jsCDN}${specifier}`;
};
export interface BundlerOptions {
jsCDN?: string;
fetchRemotePackages?: boolean;
filename?: string;
files?: Record<string, string>;
}
// Swallow informational logs in testing framework
function log(...args: any[]) {
if (typeof self !== 'undefined' && self.console) {
self.console.log(...args);
}
}
/**
* This is a plugin for esbuild that has three functions:
* * It resolves NPM packages to urls served by various CDNs.
* * It fetches imports from http/https urls.
* * It provides evaluation context to npm packages.
*
* ManifoldCAD libraries can include `manifold-3d/manifoldCAD` to get access to
* the evaluator context. Outside of the evaluator, this involves importing a
* module. Inside the evaluator, imports are in the global namespace. The
* imported module will not be able to see the actual evaluator context. Here,
* we will swap it out for an object we will inject at evaluation time.
*/
export const esbuildManifoldPlugin = (options: BundlerOptions = {}):
esbuild.Plugin => ({
name: 'esbuild-manifold-plugin',
async setup(build) {
let manifoldCADExportPath: string|null = null;
const manifoldCADExportSpecifier = 'manifold-3d/manifoldCAD'
const ManifoldCADExportMatch = /^manifold-3d\/manifoldCAD(.ts|.js)?$/
const manifoldCADExportNames = [
// Manifold classes.
'Mesh', 'Manifold', 'CrossSection',
// Manifold methods.
'setMinCircularAngle', 'setMinCircularEdgeLength', 'setCircularSegments',
'getCircularSegments', 'resetToCircularDefaults', 'triangulate',
// Scene builder exports.
'show', 'only', 'setMaterial', 'setMorphStart', 'setMorphEnd',
// GLTFNode and utilities.
'GLTFNode', 'getGLTFNodes', 'resetGLTFNodes',
// ManifoldCAD specific exports.
'isManifoldCAD'
];
if (isNode()) {
// We only need to check against the local manifoldCAD context on disk if
// we happen to be running in node. Truthfully, this is really only
// necessary in development. End users will almost always import
// `manifold-3d/manifoldCAD` instead of `some/path/to/manifoldCAD`.
(async () => {
const {resolve, dirname} = await import('node:path');
const {fileURLToPath} = await import('node:url');
const dir = ('string' == typeof __dirname && __dirname) ||
('string' == typeof import.meta?.dirname && import.meta.dirname) ||
dirname(fileURLToPath(import.meta.url));
manifoldCADExportPath = resolve(dir, './manifoldCAD.ts');
})();
}
const skipResolve = {};
// Try to resolve local files. If we can't, blindly resolve them to a CDN.
build.onResolve({filter: /.*/}, async (args) => {
// skipResolve used to avoid loops.
// https://github.com/evanw/esbuild/issues/2198#issuecomment-1104566397
if (args.pluginData === skipResolve) return null;
// Skip a few cases handled elsewhere.
if (args.namespace === 'http-url') return null;
if (args.path.match(/^https?:\/\//)) return null;
// Is this a manifoldCAD context import?
const pluginData = {
toplevel:
args.importer === options.filename || args.importer === '<stdin>'
};
if (args.path.match(ManifoldCADExportMatch)) {
return {namespace: 'manifold-cad-globals', path: args.path, pluginData};
}
// Is this a virtual file?
if (options.files && Object.keys(options.files).includes(args.path)) {
return {namespace: 'virtual-file', path: args.path};
}
// Try esbuilds' resolver first.
const result = await build.resolve(args.path, {
resolveDir: args.resolveDir,
kind: 'import-statement',
pluginData: skipResolve
});
// We found a local file!
if (result.errors.length === 0) {
if (manifoldCADExportPath && manifoldCADExportPath === result.path) {
// It resolved to our local manifoldCAD context.
return {
namespace: 'manifold-cad-globals',
path: args.path,
pluginData
};
}
return result;
}
// Built in resolver failed. Are we fetching remote packages?
if (options.fetchRemotePackages !== false && options.jsCDN) {
return {
path: cdnUrl(args.path, options.jsCDN),
namespace: 'http-url',
};
}
// Okay fine. I give up.
return null;
});
// Inject context.
build.onLoad(
{filter: /.*/, namespace: 'manifold-cad-globals'},
(args): esbuild.OnLoadResult => {
// This is a string replace.
const globals = args.pluginData?.toplevel ?
'_manifold_cad_top_level' :
'_manifold_cad_library';
return {
// Type hinting isn't necessary. Only esbuild will see the swap,
// and it doesn't do type validation.
contents: `export const {${manifoldCADExportNames}} = ${globals};`,
};
});
// Virtual files.
build.onLoad(
{filter: /.*/, namespace: 'virtual-file'},
(args): esbuild.OnLoadResult => {
let loader: esbuild.Loader = 'ts';
if (args.path.match(/\.js$/)) loader = 'js';
return {contents: (options.files!)[args.path], loader};
});
// Unless disabled, handle HTTP/HTTPs urls.
if (options.fetchRemotePackages !== false) {
// Resolve absolute urls.
build.onResolve({filter: /^https?:\/\//}, args => {
return {path: args.path, namespace: 'http-url'};
});
// Resolve relative http urls into absolute urls.
build.onResolve({filter: /.*/, namespace: 'http-url'}, args => {
const path = new URL(args.path, args.importer).toString();
// Is this a manifoldCAD context import from a remote package?
// e.g.: `/npm/manifold-3d/manifoldCAD/+esm`
if (path === cdnUrl(manifoldCADExportSpecifier, options.jsCDN)) {
const response = {
path,
namespace: 'manifold-cad-globals',
pluginData: {toplevel: false}
};
return response;
}
return {path, namespace: 'http-url'};
});
// Fetch urls.
build.onLoad({filter: /.*/, namespace: 'http-url'}, async (args) => {
const response = await fetch(args.path);
if (response.ok) {
log(`Fetching ${args.path}.`);
return {contents: await response.text()};
} else {
return {errors: [{text: await response.text()}]};
}
});
}
},
});
let esbuild_initialized: boolean = false;
const getEsbuildConfig =
async(options: BundlerOptions = {}): Promise<esbuild.BuildOptions> => {
if (!esbuild_initialized) {
const esbuildOptions: esbuild.InitializeOptions = {};
if (typeof esbuildWasmUrl === 'string' && esbuildWasmUrl) {
esbuildOptions.wasmURL = esbuildWasmUrl;
esbuildOptions.worker = esbuildHasOwnWorker === true;
}
await esbuild.initialize(esbuildOptions);
esbuild_initialized = true;
}
return {
// Create a bundle in memory.
bundle: true,
write: false,
platform: 'neutral',
treeShaking: false,
sourcemap: 'inline',
sourcesContent: false, // We have the source handy already.
format: 'cjs',
logLevel: 'silent',
plugins: [
esbuildManifoldPlugin(options),
],
// Some CDN imports will check import.meta.env. This is only present when
// generating an ESM bundle. In other cases, it generates log noise, so
// let's drop it down a log level.
logOverride: {'empty-import-meta': 'info'}
};
};
export const bundleFile = async(
entrypoint: string, options: BundlerOptions = {}): Promise<string> => {
try {
const built = await esbuild.build({
...(await getEsbuildConfig({...options, filename: entrypoint})),
entryPoints: [entrypoint],
});
return built.outputFiles![0].text;
} catch (error) {
if ((error as any).errors?.length) {
throw new BundlerError(error as BuildFailure);
} else {
throw error;
}
}
};
export const bundleCode =
async(code: string, options: BundlerOptions = {}): Promise<string> => {
try {
let resolveDir: string|undefined;
if (isNode() && options.filename) {
const {dirname} = await import('node:path');
resolveDir = dirname(options.filename);
}
const built = await esbuild.build({
...(await getEsbuildConfig(options)),
stdin: {
contents: code,
sourcefile: options.filename,
resolveDir,
loader: 'ts',
}
});
return built.outputFiles![0].text;
} catch (error) {
if ((error as any).errors?.length) {
throw new BundlerError(error as BuildFailure);
} else {
throw error;
}
}
};
+153
View File
@@ -0,0 +1,153 @@
// Copyright 2022-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 {Document} from '@gltf-transform/core';
import {Manifold, Mesh} from '../manifold-encapsulated-types';
import {Properties, writeMesh} from './gltf-io.ts';
import {GLTFMaterial} from './gltf-node.ts';
import {getCachedMaterial, getMaterialByID as getOriginalMaterialByID} from './material.ts';
// Debug setup to show source meshes
let ghost = false;
const shown = new Map<number, Mesh>();
const singles = new Map<number, Mesh>();
const SHOW = {
baseColorFactor: [1, 0, 0],
alpha: 0.25,
roughness: 1,
metallic: 0
} as GLTFMaterial;
const GHOST = {
baseColorFactor: [0.5, 0.5, 0.5],
alpha: 0.25,
roughness: 1,
metallic: 0
} as GLTFMaterial;
/**
* @internal
*/
export function cleanup() {
ghost = false;
shown.clear();
singles.clear();
}
const getDebugMeshByID = (id: number):
Mesh|undefined => {
return shown.has(id) ? shown.get(id) : singles.get(id);
}
/**
* Material for a debug visualization.
*
* There are two conditions:
*
* * This is a mesh that has been flagged with `show`.
* It will be highlighted with the SHOW material.
* * This is a mesh that has been flagged with `only`.
* Any other mesh will have the GHOST material, while
* this one gets it's natural material.
*
* @internal
* @param id The `originalID` of the mesh.
*/
const getDebugMaterialByID = (id: number):
GLTFMaterial|undefined => {
const show = shown.has(id);
const inMesh = show ? shown.get(id) : singles.get(id);
if (show && inMesh) {
return SHOW;
}
return getOriginalMaterialByID(id);
}
/**
* Override materials when debugging.
*
* When a mesh is flagged with `only`, we set the `ghost` global.
* Everything gets rendered in the GHOST material, while the flagged
* mesh is added as a debug node.
*
* @internal
* @param id The `originalID` of the mesh.
*/
export const getMaterialByID = (id: number): GLTFMaterial|undefined =>
ghost ? GHOST : getOriginalMaterialByID(id);
const debug = (manifold: Manifold, map: Map<number, Mesh>) => {
let result = manifold.asOriginal();
map.set(result.originalID(), result.getMesh());
return result;
};
/**
* Wrap any shape object with this method to display it and any copies in
* transparent red. This is particularly useful for debugging `subtract()` as it
* will allow you find the object even if it doesn't currently intersect the
* result.
*
* @group Modelling Functions
* @param manifold The object to show - returned for chaining.
*/
export const show = (manifold: Manifold) => {
return debug(manifold, shown);
};
/**
* Wrap any shape object with this method to display it and any copies as the
* result, while ghosting out the final result in transparent gray. Helpful for
* debugging as it allows you to see objects that may be hidden in the interior
* of the result. Multiple objects marked `only()` will all be shown.
*
* @group Modelling Functions
* @param manifold The object to show - returned for chaining.
*/
export const only = (manifold: Manifold) => {
ghost = true;
return debug(manifold, singles);
};
/**
*
* @internal
*/
export const getDebugGLTFMesh =
(doc: Document, manifoldMesh: Mesh, id2properties: Map<number, Properties>,
backupMaterial: GLTFMaterial = {}) => {
const debugNodes = [];
for (const [run, id] of manifoldMesh.runOriginalID!.entries()) {
const debugMesh = getDebugMeshByID(id);
if (!debugMesh) {
continue;
}
// Here, we'll get back either a debug material (like SHOW),
// or the original mesh material.
const material = getDebugMaterialByID(id) || backupMaterial;
id2properties.get(id)!.material = getCachedMaterial(doc, material);
const debugNode = doc.createNode('debug')
.setMesh(writeMesh(doc, debugMesh, id2properties))
.setMatrix(manifoldMesh.transform(run));
debugNodes.push(debugNode);
}
return debugNodes;
};
+55
View File
@@ -0,0 +1,55 @@
// 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 type {BuildFailure, Location, Message} from 'esbuild';
export class BundlerError extends Error {
location?: Location;
error: Message;
manifoldStack?: string;
constructor(failure: BuildFailure, options?: ErrorOptions) {
super(undefined, options);
this.cause = failure;
this.error = failure.errors[0];
if (this.error.location) {
const {file, line, column} = this.error.location!;
this.manifoldStack =
`${this.toString()}\n at ${file}:${line}:${column}`;
}
}
get name(): string {
return 'BundlerError';
}
get message(): string {
return this.error.text;
}
};
export class RuntimeError extends Error {
manifoldStack?: string;
cause: Error;
constructor(cause: Error, message?: string, options?: ErrorOptions) {
super(message ?? cause.message, options);
this.cause = cause;
}
get name(): string {
return this.cause.name;
}
}
+216
View File
@@ -0,0 +1,216 @@
// Copyright 2023-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 {Document, mat4, Mesh, Node} from '@gltf-transform/core';
import {fileForContentTypes, FileForRelThumbnail, to3dmodel} from '@jscadui/3mf-export';
import {strToU8, Zippable, zipSync} from 'fflate';
import {ManifoldPrimitive} from './manifold-gltf';
interface Mesh3MF {
id: string;
vertices: Float32Array;
indices: Uint32Array;
name?: string;
}
interface Child3MF {
objectID: string;
transform?: mat4|Array<string>;
}
interface Component3MF {
id: string;
children: Array<Child3MF>;
name?: string;
transform?: mat4|Array<string>;
}
interface Header {
unit?: 'micron'|'millimeter'|'centimeter'|'inch'|'foot'|'meter';
title?: string;
author?: string;
description?: string;
application?: string;
creationDate?: string;
license?: string;
modificationDate?: string;
}
interface To3MF {
meshes: Array<Mesh3MF>;
components: Array<Component3MF>;
items: Array<Child3MF>;
precision: number;
header: Header;
}
/**
* Object to convert GLTF documents to a 3MF binary.
*
* @property unit
* @property title
* @property author
* @property description
* @property application
* @property creationDate
* @property license
* @property modificationDate
*/
export class Export3MF {
extensions: Array<string> = ['3mf'];
unit: 'micron'|'millimeter'|'centimeter'|'inch'|'foot'|'meter' = 'millimeter';
title?: string = 'ManifoldCAD.org model';
author?: string;
description?: string = 'ManifoldCAD.org model';
application?: string = 'ManifoldCAD.org';
creationDate?: string;
license?: string;
modificationDate?: string;
/**
* Convert a GLTF-Transform document to a 3MF model.
*
* @param doc The GLTF document to convert.
* @returns A blob containing the converted model.
*/
async asBlob(doc: Document) {
const to3mf = {
meshes: [],
components: [],
items: [],
precision: 7,
header: {
unit: this.unit,
title: this.title,
description: this.description,
application: this.application,
creationDate: this.creationDate,
license: this.license,
modificationDate: this.modificationDate
}
} as To3MF;
// GLTF references by array index.
// 3MF references by ID.
let nextGlobalID = 1;
const object2globalID = new Map<Node|Mesh, string>();
const getObjectID = (obj: Node|Mesh) => `${object2globalID.get(obj)}`;
const getMeshID = (mesh: Mesh) => {
// If a mesh has been cloned with a different material, find
// the original mesh. This isn't a general GLTF feature; this is set
// by the ManifoldCAD GLTF exporter.
const {clonedFrom} = mesh.getExtras();
if (clonedFrom) {
return object2globalID.get(clonedFrom as Mesh)
}
return object2globalID.get(mesh);
};
const setObjectID =
(obj: Node|Mesh) => {
const objectID = `${nextGlobalID++}`;
object2globalID.set(obj, objectID);
return objectID
}
// Get meshes in place first.
for (const mesh of doc.getRoot().listMeshes()) {
const manifoldPrimitive =
mesh.getExtension('EXT_mesh_manifold') as ManifoldPrimitive;
if (manifoldPrimitive) {
// This mesh has a list of triangle vertices already.
const indices = manifoldPrimitive.getIndices();
const positionAccessor =
mesh.listPrimitives()[0].getAttribute('POSITION')!;
const objectID = setObjectID(mesh)
to3mf.meshes.push({
vertices: positionAccessor.getArray()! as Float32Array,
indices: indices.getArray()! as Uint32Array,
id: objectID
});
}
const {clonedFrom} = mesh.getExtras();
if (!manifoldPrimitive && clonedFrom) {
// GLTF Mesh, instance of another mesh.
// getMeshID will find this when adding it to components.
continue;
}
if (!manifoldPrimitive && !clonedFrom) {
// GLTF Mesh, no manifold primitive,
// not an instance of another mesh.
// We should handle this case, but for now we do not.
console.log('skipping non-ManifoldCAD mesh')
}
}
// Some 3MF parsers (like PrusaSlicer) expect child nodes
// to be defined before their parents.
const nodes = doc.getRoot().listNodes().reverse()
for (const node of nodes) {
const meshID = node.getMesh() && getMeshID(node.getMesh()!);
to3mf.components.push({
id: setObjectID(node),
name: node.getName(),
children: meshID ? [{objectID: meshID}] : [],
transform: node.getMatrix().map(n => n.toFixed(to3mf.precision))
});
}
// Now we can work out our node hierarchy.
for (const node of doc.getRoot().listNodes()) {
const objectID = getObjectID(node)
if (!objectID) {
console.log(`Could not find object ID for ${node.getName()}`)
continue;
}
const child = {
objectID,
// Most 3MF parsers will not accept a number in scientific notation.
// Transforms are serialized to a string, containing 12 numbers
// separated by spaces. If we force a number to a string here,
// 3mf-export passes it through.
transform: node.getMatrix().map(n => n.toFixed(to3mf?.precision ?? 7))
};
const parent = node.getParentNode();
if (parent) {
// This is a child node, add it to its parent.
const parentID = getObjectID(parent);
const parent3mf = to3mf.components.find((comp) => comp.id == parentID)!;
parent3mf.children.push(child);
} else {
// This is a root node.
// Add it to the build list.
to3mf.items.push({objectID})
}
}
const fileForRelThumbnail = new FileForRelThumbnail();
fileForRelThumbnail.add3dModel('3D/3dmodel.model')
const model = to3dmodel(to3mf as any);
const files: Zippable = {};
files['3D/3dmodel.model'] = strToU8(model);
files[fileForContentTypes.name] = strToU8(fileForContentTypes.content);
files[fileForRelThumbnail.name] = strToU8(fileForRelThumbnail.content);
const zipFile = zipSync(files);
return new Blob(
[zipFile as Uint8Array<ArrayBuffer>],
{type: 'application/vnd.ms-package.3dmanufacturing-3dmodel+xml'});
}
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2023-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 {Document, WebIO} from '@gltf-transform/core';
import {KHRONOS_EXTENSIONS} from '@gltf-transform/extensions';
import {setupIO} from './gltf-io.ts';
/**
* Object to convert GLTF documents.
*
*/
export class ExportGLTF {
extensions: Array<string> = ['glb', 'gltf'];
/**
* Convert a GLTF-Transform document to a blob.
*
* @param doc The GLTF document to convert.
* @returns A blob containing the converted model.
*/
async asBlob(doc: Document) {
const io = setupIO(new WebIO());
io.registerExtensions(KHRONOS_EXTENSIONS);
const glb = await io.writeBinary(doc);
return new Blob(
[glb as Uint8Array<ArrayBuffer>], {type: 'application/octet-stream'});
}
}

Some files were not shown because too many files have changed in this diff Show More