Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fca04823f4 | ||
|
|
d8de9d6f76 | ||
|
|
7b33277245 | ||
|
|
2e2ed36968 | ||
|
|
0d9abcd72a | ||
|
|
10ecdf6ab6 | ||
|
|
6178ce30a9 | ||
|
|
87f5fec565 | ||
|
|
37e5b64440 | ||
|
|
15224126a4 | ||
|
|
d00dbe1d48 | ||
|
|
81e94c86c5 | ||
|
|
35dec2cfdb | ||
|
|
2021a3ab06 | ||
|
|
d38122c5ab | ||
|
|
969b1417af | ||
|
|
c50001b82d | ||
|
|
ff42ddc885 | ||
|
|
422ab6fcee | ||
|
|
d62e3b0979 | ||
|
|
f2b7582bd9 | ||
|
|
cf4a15ef58 | ||
|
|
26fe6d8c49 | ||
|
|
e2adc05696 | ||
|
|
895bfc230f | ||
|
|
8bffb52101 | ||
|
|
8d674676f5 | ||
|
|
e5f6201dfb | ||
|
|
697418b8b9 | ||
|
|
681fec46b3 | ||
|
|
090bdaca33 | ||
|
|
bf47ebccc6 | ||
|
|
ecc145f477 | ||
|
|
0a68eae6ce | ||
|
|
72d6137b2e | ||
|
|
9008e157a1 | ||
|
|
db042ab153 | ||
|
|
324bec7bd7 | ||
|
|
c463210f74 | ||
|
|
3fe31a1d09 | ||
|
|
b27d8a5e26 | ||
|
|
ae2dbdb2fb | ||
|
|
40cf0bdaf1 | ||
|
|
8ba18a0417 | ||
|
|
cc8a7f66d7 | ||
|
|
5b29f45db9 | ||
|
|
37125da090 | ||
|
|
760d1a04b8 | ||
|
|
e02471d211 | ||
|
|
df1143e596 | ||
|
|
9f01818d63 | ||
|
|
f77233c520 | ||
|
|
2c98e8b96a | ||
|
|
beb681b080 | ||
|
|
541c33bd2a | ||
|
|
9719c34993 | ||
|
|
eefe895846 | ||
|
|
8cab4b607c | ||
|
|
bd56861de0 | ||
|
|
22571eeae7 | ||
|
|
74ea61bd73 | ||
|
|
77d7b65804 | ||
|
|
0d5b52cbaf | ||
|
|
51f178f012 | ||
|
|
8606a8281d | ||
|
|
73c687839a | ||
|
|
bb3f31ff7b | ||
|
|
35cb773018 | ||
|
|
2acc4831f6 | ||
|
|
649f4e29d9 | ||
|
|
fc198acd00 | ||
|
|
f49107d7db | ||
|
|
07ad5c24a4 | ||
|
|
3dac1c4fd4 | ||
|
|
3ce9622b85 | ||
|
|
e73c0781ac | ||
|
|
5f95a3ac0e | ||
|
|
a8ee9b87f3 | ||
|
|
65943caaab | ||
|
|
90e55118eb | ||
|
|
58b328a98a | ||
|
|
7e88b62f6c | ||
|
|
f702191d68 | ||
|
|
396bc5841d | ||
|
|
8b173db61a | ||
|
|
7c092e76db | ||
|
|
8d28267ca1 | ||
|
|
adaf64c4a7 | ||
|
|
f575cf647a | ||
|
|
f756a74e7d | ||
|
|
e7e39f042a | ||
|
|
eae153d628 | ||
|
|
78f15825f4 | ||
|
|
d093da8424 | ||
|
|
076c704c4f | ||
|
|
c5f72130b9 | ||
|
|
97df55ba1d | ||
|
|
cfe7b563ad | ||
|
|
a74d946271 | ||
|
|
ee966fb920 | ||
|
|
56110f65bd | ||
|
|
3f7926be7a | ||
|
|
802b065e77 | ||
|
|
79524cfbd6 | ||
|
|
1b23a9db33 | ||
|
|
f9c0b2cd75 | ||
|
|
842479e238 | ||
|
|
abbf20c590 | ||
|
|
929d9928bc | ||
|
|
8a27b6028d | ||
|
|
93c1b4bf3c | ||
|
|
a503665378 | ||
|
|
d88e295085 | ||
|
|
d93943fc7e | ||
|
|
b38ca7c0c3 | ||
|
|
d96c6ff26c | ||
|
|
7b048b89a4 | ||
|
|
dddb86dbf9 | ||
|
|
3cebb542cd | ||
|
|
a51916383a | ||
|
|
40eaa20567 | ||
|
|
4613509768 | ||
|
|
dbdae103e6 | ||
|
|
ca986a46b7 | ||
|
|
023c91935c | ||
|
|
fb0aa7e4d7 | ||
|
|
4faa8316d3 | ||
|
|
31afd71d17 | ||
|
|
7255c27646 | ||
|
|
98c317fff8 | ||
|
|
ca7a6c01a3 | ||
|
|
a8f28ab6a8 | ||
|
|
2e00278d58 | ||
|
|
f3d121b95d | ||
|
|
50724930ff | ||
|
|
91d700b639 | ||
|
|
0b527ea45a | ||
|
|
4a56b0f9f2 | ||
|
|
5d21276946 | ||
|
|
842f58f1e4 | ||
|
|
5a1fcb9963 | ||
|
|
cf01c7bee2 | ||
|
|
ec936a4614 | ||
|
|
4d42d54406 | ||
|
|
c8ec82a494 | ||
|
|
5e434a07d6 | ||
|
|
d7c19d7b30 | ||
|
|
19eee42716 | ||
|
|
5805355b93 | ||
|
|
9c4f4d75df | ||
|
|
ffe6c02952 | ||
|
|
af6f131a49 | ||
|
|
330b475d92 | ||
|
|
9363fc7b89 | ||
|
|
bd55a1f066 | ||
|
|
9266a94859 | ||
|
|
bf2a603432 | ||
|
|
b722d7f4f8 | ||
|
|
552103eec8 | ||
|
|
7332399d5e | ||
|
|
6c9da566cc | ||
|
|
aa0dc12da8 | ||
|
|
1f6d1dbad2 | ||
|
|
46e8a6dc86 | ||
|
|
3a799fa5a5 | ||
|
|
9e1fa6d544 | ||
|
|
61c4af291c | ||
|
|
0bfc0d64f8 | ||
|
|
747559170a | ||
|
|
497cfb5d0b | ||
|
|
cbcf7dc0c8 | ||
|
|
fa6be648b7 | ||
|
|
93bea2834f | ||
|
|
e2c39536fd | ||
|
|
b5e9bfeb8e | ||
|
|
8b27763c8f | ||
|
|
896bdf5ac4 | ||
|
|
634aee5fe0 | ||
|
|
db58939b58 | ||
|
|
6860502494 | ||
|
|
d5c0e6c29e | ||
|
|
a850f73d51 | ||
|
|
301c165a20 | ||
|
|
539f58d325 | ||
|
|
1d44fa463d | ||
|
|
cadec11542 | ||
|
|
bf7031f2ea | ||
|
|
213bcc5bd1 | ||
|
|
8732952b5f | ||
|
|
e4ccce3c03 | ||
|
|
6c05639c1b | ||
|
|
34e3e1bbf3 | ||
|
|
2e3f0e6272 | ||
|
|
31214804c9 | ||
|
|
38e143d8e0 | ||
|
|
b918447ad1 | ||
|
|
b2c6ddd2e3 | ||
|
|
71c9c59f1c | ||
|
|
e031fc471c | ||
|
|
3b00ea4ace | ||
|
|
18fb6d6315 | ||
|
|
7ea0c707a9 | ||
|
|
58034ad362 | ||
|
|
d315ce22f5 | ||
|
|
337e13fba0 | ||
|
|
3a96fcc80f | ||
|
|
704540c873 | ||
|
|
499347bb9f | ||
|
|
628037b26e | ||
|
|
750f92aee2 | ||
|
|
0560cd043a | ||
|
|
29c3e3b194 | ||
|
|
04f210d112 | ||
|
|
50fcc28a55 | ||
|
|
5c90c777bc | ||
|
|
526280ef20 | ||
|
|
7f037e5189 | ||
|
|
cf3b517dfa | ||
|
|
db18317e42 | ||
|
|
04cb690671 | ||
|
|
798d83c8d7 | ||
|
|
652a95320b | ||
|
|
a06d4502cd | ||
|
|
aea74b56ff | ||
|
|
b51858f648 | ||
|
|
b479c67510 | ||
|
|
3b589b94cb | ||
|
|
84f9a8ce76 | ||
|
|
75390fe5cb | ||
|
|
7054c63029 | ||
|
|
55776ac3b5 | ||
|
|
9e127e455d | ||
|
|
6726ad2eeb | ||
|
|
d4acf92ced | ||
|
|
e93f95dc04 | ||
|
|
fcd67c97b1 | ||
|
|
b72a676caf | ||
|
|
c181da63e5 | ||
|
|
3087302edc | ||
|
|
097d487e96 | ||
|
|
8604c48a2f | ||
|
|
ff0162907d | ||
|
|
f6005ffa83 | ||
|
|
5cb7e6444e | ||
|
|
50548dcc25 | ||
|
|
bd68fe6d78 | ||
|
|
54d0a2e210 | ||
|
|
533fc6a86e | ||
|
|
edd20e03f5 | ||
|
|
f0747c3420 | ||
|
|
64c7d229bc | ||
|
|
34da5f0629 | ||
|
|
f18a4c7c38 | ||
|
|
557b320729 | ||
|
|
e93393bcdc | ||
|
|
ccece0bf69 | ||
|
|
5fe1e365fb | ||
|
|
ca375ea079 | ||
|
|
59361d8d99 | ||
|
|
818f7b3024 | ||
|
|
e047ba6362 | ||
|
|
ae57329b67 | ||
|
|
3124ad45e4 | ||
|
|
3a29e9566f | ||
|
|
7d5d78b957 | ||
|
|
bf93b97221 | ||
|
|
34afe21c31 | ||
|
|
b4e0dbb763 | ||
|
|
4b4eb9a8ec | ||
|
|
9457a4e889 | ||
|
|
f1962a723c | ||
|
|
56f2b6730b | ||
|
|
e4c8defdff | ||
|
|
dad5deeccc | ||
|
|
76208dc02b | ||
|
|
6754a3cf7c | ||
|
|
73f3afdfc2 | ||
|
|
39e3cd3f6e | ||
|
|
c18032c31e | ||
|
|
83ddd27063 | ||
|
|
8610651942 | ||
|
|
da4997bd29 | ||
|
|
52c5ef592e | ||
|
|
8c63b864d8 | ||
|
|
87b8167659 | ||
|
|
982c339abc | ||
|
|
3a83b83ca3 | ||
|
|
75dc3219fa | ||
|
|
90cab6e2bb | ||
|
|
186dc69f52 | ||
|
|
d013ddec32 | ||
|
|
182188b805 | ||
|
|
2f4741e0b1 | ||
|
|
023bb59be9 | ||
|
|
2984626cb2 | ||
|
|
551e1a12cf | ||
|
|
2cf3dfd0ec | ||
|
|
b252782489 | ||
|
|
39934bca55 | ||
|
|
1d18e60d74 | ||
|
|
061c5e7a48 | ||
|
|
e3ff2a0019 | ||
|
|
3027282259 | ||
|
|
1ec20369ca | ||
|
|
cd57073179 | ||
|
|
3ba3175f2c | ||
|
|
5145d3422b | ||
|
|
41d8d97d7c | ||
|
|
54baa62b4a | ||
|
|
f3a6a477da | ||
|
|
994814921e | ||
|
|
138815a127 | ||
|
|
85b0cd211c | ||
|
|
9756892b09 | ||
|
|
3015a81c07 | ||
|
|
272a22439f | ||
|
|
4ba67b8fcf | ||
|
|
09fb03cff2 | ||
|
|
b8dd573a9c | ||
|
|
63a36e678a | ||
|
|
7c2b7b407b | ||
|
|
29d2b1dbe9 | ||
|
|
90f83a98be | ||
|
|
ca7d6c326c | ||
|
|
00a589dafe | ||
|
|
7c8fbe186a | ||
|
|
4fabfc327b | ||
|
|
75b592f38c | ||
|
|
444399393a | ||
|
|
2a4e9bae06 | ||
|
|
bdb0b880c9 | ||
|
|
33a69413c2 | ||
|
|
4edd442297 | ||
|
|
beb9ca6122 | ||
|
|
fab833e7e1 | ||
|
|
1f188036d1 | ||
|
|
808c261b33 | ||
|
|
bc70f58990 | ||
|
|
d4439bccec | ||
|
|
8e34f97194 | ||
|
|
954df798b2 | ||
|
|
0451671bd3 | ||
|
|
cba9e35bb1 | ||
|
|
5399bd4760 | ||
|
|
06b10e7149 | ||
|
|
f178cd1d73 | ||
|
|
5ae22f0d57 | ||
|
|
a97b150bb5 | ||
|
|
c16b521193 | ||
|
|
c149dd5088 | ||
|
|
a857f008dd | ||
|
|
f2da5eab66 | ||
|
|
447921cf97 | ||
|
|
ac82767927 | ||
|
|
56fec978c6 | ||
|
|
ae813ddecb | ||
|
|
1b3dbc29e2 | ||
|
|
98b8142519 | ||
|
|
b7a02755aa | ||
|
|
be1dec2ef0 | ||
|
|
3a0c251b0e | ||
|
|
209350fd1e | ||
|
|
1917ce0844 | ||
|
|
f48111c43c | ||
|
|
3db0969d1d | ||
|
|
c50b0f80ff | ||
|
|
25ce1b1d53 | ||
|
|
926d2e7a7d | ||
|
|
af5d95ec1b | ||
|
|
ec0bbca3d9 | ||
|
|
aacf37aa4d | ||
|
|
847016c069 | ||
|
|
517a224f44 | ||
|
|
a13ceab979 | ||
|
|
36035428bc | ||
|
|
f1a8693016 | ||
|
|
fb29e8586e | ||
|
|
8556840558 | ||
|
|
2162d8d91f | ||
|
|
11387fb871 | ||
|
|
ddc0a18d87 | ||
|
|
f8c9dc9c8b | ||
|
|
3ea3e7e5e5 | ||
|
|
bc08d41dc0 | ||
|
|
560e37e985 | ||
|
|
db08c23988 | ||
|
|
7d27a437ab | ||
|
|
b77abde57b | ||
|
|
70abe63963 | ||
|
|
75f42f9c53 | ||
|
|
978f84107f | ||
|
|
7d6c285b3e | ||
|
|
78915ae28d | ||
|
|
a557225c29 | ||
|
|
f22435f1e5 | ||
|
|
5d127e57fb | ||
|
|
125dbd41cd | ||
|
|
182adae00a | ||
|
|
7f298aa9db | ||
|
|
18101057b5 | ||
|
|
7de71d4c93 | ||
|
|
d9bc83a7ad | ||
|
|
ee32b3025d | ||
|
|
e7e0780114 | ||
|
|
0b957bf8df | ||
|
|
b26007e76c | ||
|
|
0f7cecaf1a | ||
|
|
a41a361123 | ||
|
|
74f340a8e7 | ||
|
|
12b43f50c4 | ||
|
|
66bca6e3fd | ||
|
|
55722a0ce3 | ||
|
|
2b29dca503 | ||
|
|
82140d3479 | ||
|
|
8e9bdb2614 | ||
|
|
4076b14dd6 | ||
|
|
f11ed8da6c | ||
|
|
a8a90c7d34 | ||
|
|
649b69a140 | ||
|
|
97cb8dbffa | ||
|
|
d1a8de2e00 | ||
|
|
a0d2c3ab25 | ||
|
|
80221a3511 | ||
|
|
5ba3c24112 | ||
|
|
85675f2723 | ||
|
|
e647093bbe | ||
|
|
54517b7546 | ||
|
|
e45df5dd3a | ||
|
|
97423a07f9 | ||
|
|
0a51ae2a0c | ||
|
|
e718288cbf | ||
|
|
005102c1a3 | ||
|
|
6ade357e4b | ||
|
|
e06765346e | ||
|
|
bd52d301ff | ||
|
|
370375ebf0 | ||
|
|
9d3fd48b01 | ||
|
|
451829f1d1 | ||
|
|
3febd48dcd | ||
|
|
658d3e037a | ||
|
|
cc1e97442a | ||
|
|
5719926db8 | ||
|
|
3c8d486e36 | ||
|
|
5a157a07fe | ||
|
|
acb4f8c5e4 | ||
|
|
5cab42f165 | ||
|
|
ea91395878 | ||
|
|
4d7613bd4f | ||
|
|
9f62dbb048 | ||
|
|
31d6da0973 | ||
|
|
359274d01b | ||
|
|
a24cca1f06 | ||
|
|
c1d96a6404 | ||
|
|
a8e533c790 | ||
|
|
fa6a632545 | ||
|
|
cff8d903e8 | ||
|
|
fcc707585b | ||
|
|
456b8ceb3b | ||
|
|
9257734c0f | ||
|
|
ced53e2109 | ||
|
|
a017f0e614 | ||
|
|
22c66051df | ||
|
|
4760ba3a33 | ||
|
|
c75f068a03 | ||
|
|
51ee91e356 | ||
|
|
2e41fef203 | ||
|
|
9124a68032 | ||
|
|
6f009ca13f | ||
|
|
5b2a95e6d2 | ||
|
|
e9c065bd5e | ||
|
|
b95b88f026 | ||
|
|
492c3a8b23 | ||
|
|
d0d5106add | ||
|
|
fbf07bf92a | ||
|
|
a0e4d1b558 | ||
|
|
db4470c3f3 | ||
|
|
fd485f62d5 | ||
|
|
74e15b1574 | ||
|
|
d4fb82d4d1 | ||
|
|
952546b761 | ||
|
|
e26e01a231 | ||
|
|
844334380a | ||
|
|
e01d31611e | ||
|
|
af2304d305 | ||
|
|
75c340f4f4 | ||
|
|
7ad7703964 | ||
|
|
a37d59ef5b | ||
|
|
3bf7f4b33d | ||
|
|
a93c0c6ca1 | ||
|
|
ccb682fc44 | ||
|
|
0617d344bc | ||
|
|
a6ee198b88 | ||
|
|
dad73e1bf4 | ||
|
|
610fd3a073 | ||
|
|
46f199c200 | ||
|
|
fa1a342e1c | ||
|
|
e46938b498 | ||
|
|
ddb3cbeb3a | ||
|
|
b817d07a9d | ||
|
|
85d71f5cef | ||
|
|
ab1a96a96a | ||
|
|
7b10590d89 | ||
|
|
5e7a576cea | ||
|
|
80a5f2e0c5 | ||
|
|
67c4647464 | ||
|
|
edd149e753 | ||
|
|
b5c175162a | ||
|
|
1df22bee48 | ||
|
|
d5c0418309 | ||
|
|
7a5d5421d4 | ||
|
|
69437096d0 | ||
|
|
3931b3296d | ||
|
|
62d0f25ce0 | ||
|
|
1bd16f4c02 | ||
|
|
3afe846570 | ||
|
|
12b9494fdd | ||
|
|
0577aabe7f | ||
|
|
bdae9dd11a | ||
|
|
5f4059ab1f | ||
|
|
9cb3969f70 | ||
|
|
4fb3520287 | ||
|
|
376d317f14 | ||
|
|
bf18c9f759 | ||
|
|
cdd1475fe1 | ||
|
|
55a0221f9b | ||
|
|
d78b1e2042 | ||
|
|
562ff02232 | ||
|
|
8e2b1dd485 | ||
|
|
5ef09daf45 | ||
|
|
6d932a2c7f | ||
|
|
fd81cd6172 | ||
|
|
6b4a4ed841 | ||
|
|
887f631b8c | ||
|
|
443863d09e | ||
|
|
5301542254 | ||
|
|
a122bde778 | ||
|
|
c9810fd517 | ||
|
|
4c57a56355 | ||
|
|
cdcbd329e6 | ||
|
|
c738f1eea1 | ||
|
|
a8b8bf71b1 | ||
|
|
c60df1b704 | ||
|
|
8d67ba3a3a | ||
|
|
3258152e2c | ||
|
|
78729599a5 | ||
|
|
32eefe9157 | ||
|
|
3e05284250 | ||
|
|
c9ec16969d | ||
|
|
9b1d255931 | ||
|
|
22d94d5688 | ||
|
|
e073141f14 | ||
|
|
f54e535c6a | ||
|
|
c6801c82ea | ||
|
|
a67aa13133 | ||
|
|
d43709715d | ||
|
|
2ddbd0a1a0 | ||
|
|
dc4afa950b | ||
|
|
c03a5ac3ad | ||
|
|
cae5a01af1 | ||
|
|
d59b9b93bd | ||
|
|
f3c5ada9f0 | ||
|
|
4395ab622c | ||
|
|
994956d7c3 | ||
|
|
79f735c59a | ||
|
|
dd67e992bd | ||
|
|
5bdb8bc233 | ||
|
|
c9ae400647 | ||
|
|
9c1517e54e | ||
|
|
3b8282e1d5 | ||
|
|
1635864cbc | ||
|
|
65b2ef8198 | ||
|
|
a9055a8abf | ||
|
|
0b7ee87489 | ||
|
|
9690305b8d | ||
|
|
aa88959899 | ||
|
|
dbb0ca76b4 | ||
|
|
9018b2ac45 | ||
|
|
f407e43e5b | ||
|
|
d7cbcb8c2e | ||
|
|
e27c9c100f | ||
|
|
8ad5f8e427 | ||
|
|
afc64f248a | ||
|
|
c0fb3766e4 | ||
|
|
887a5021af | ||
|
|
e83b9bd158 | ||
|
|
2d0c705839 | ||
|
|
b36ec2ce0e | ||
|
|
b8d143dd72 | ||
|
|
be022a58e4 | ||
|
|
30e0af942a | ||
|
|
8c77a6aa05 | ||
|
|
54fa822408 | ||
|
|
7d564cfbc1 | ||
|
|
bc0bded294 | ||
|
|
d52decc4f1 | ||
|
|
8060376e12 | ||
|
|
eba866d266 | ||
|
|
cc4966f062 | ||
|
|
3216a24be8 | ||
|
|
5e566c8c36 | ||
|
|
80edfee978 | ||
|
|
0e0d1c4f69 | ||
|
|
e15f4cf375 | ||
|
|
c9322ca63a | ||
|
|
45826d6c47 | ||
|
|
dd781cdbdf | ||
|
|
638badaf9b | ||
|
|
0149e482bc | ||
|
|
f683e8c9c0 | ||
|
|
1f8e8037f1 | ||
|
|
10a15c1dd4 | ||
|
|
6e1629ddce | ||
|
|
e273f6b09d | ||
|
|
67c468211d | ||
|
|
01c38e42ca | ||
|
|
1991232eb7 | ||
|
|
4551288f95 | ||
|
|
2e29784449 | ||
|
|
2a2cb6f88e | ||
|
|
1a91d8db2a | ||
|
|
82b98d9353 | ||
|
|
7273c9bf0f | ||
|
|
4ff3f7cf68 | ||
|
|
fafe4792da | ||
|
|
b439603eb9 | ||
|
|
b6eea01021 | ||
|
|
b9f7228064 | ||
|
|
0ef5f70434 | ||
|
|
bb6acf1f6e | ||
|
|
5930b5c09d | ||
|
|
a842de97c6 | ||
|
|
2942229b49 | ||
|
|
0856539bfc | ||
|
|
621835683f | ||
|
|
bec8164120 | ||
|
|
062399c1c7 | ||
|
|
d51ee709c2 | ||
|
|
206871ac11 | ||
|
|
2055b9c412 | ||
|
|
5e103931f1 | ||
|
|
77051ead35 | ||
|
|
83b78f8356 | ||
|
|
6c6193c25b | ||
|
|
e79d343bec | ||
|
|
1d6a89ac87 | ||
|
|
8651bf8bd5 | ||
|
|
1514c15184 | ||
|
|
cc83e1b7aa | ||
|
|
111350f1ef | ||
|
|
b4f583fcae | ||
|
|
8011fb6072 | ||
|
|
9290d420b0 | ||
|
|
fca66380f5 | ||
|
|
8497e44915 | ||
|
|
a7eac92087 | ||
|
|
15399474fa | ||
|
|
2d513446f7 | ||
|
|
abff7e8ed3 | ||
|
|
7c454dc7a4 | ||
|
|
ffdd97d4d3 | ||
|
|
7facd0ecc0 | ||
|
|
59760b3369 | ||
|
|
5f9b484b6c | ||
|
|
18edefc6c4 | ||
|
|
93e06a2de8 | ||
|
|
047159e2c4 | ||
|
|
a8626f907c | ||
|
|
2f22b92352 | ||
|
|
a3d7991563 | ||
|
|
2aef6e4728 | ||
|
|
c167a48d36 | ||
|
|
da3f323113 | ||
|
|
8b7bf4231b | ||
|
|
ac2cfd857f | ||
|
|
285bb0754b | ||
|
|
ba5fead6d5 | ||
|
|
96c57c0591 | ||
|
|
4b65ff3afe | ||
|
|
709021d9d2 | ||
|
|
6cec951aed | ||
|
|
dd3445ddb1 | ||
|
|
37db0d0182 | ||
|
|
1b9243df2a | ||
|
|
8b4c79c485 | ||
|
|
7a197af83d | ||
|
|
a65fedb2f9 | ||
|
|
6ae9dee640 | ||
|
|
79daba1200 | ||
|
|
2f8e3174f7 | ||
|
|
cce35aa5a0 | ||
|
|
e727b115c0 | ||
|
|
f6431112a8 | ||
|
|
2643c291ed | ||
|
|
e024edf103 | ||
|
|
cbfeed1672 | ||
|
|
9dac47a76a | ||
|
|
eb41b9d36a | ||
|
|
72935213e8 | ||
|
|
e1ebc66425 | ||
|
|
ef52b0cce2 | ||
|
|
812ca5c30f | ||
|
|
f064b2a5fd | ||
|
|
18abd3ffcf | ||
|
|
1615b72e83 | ||
|
|
58fb7e3300 | ||
|
|
1959a30560 | ||
|
|
4bdb761d5e | ||
|
|
ba042ee8db | ||
|
|
97317888bc | ||
|
|
a9bf836574 | ||
|
|
29dc753513 | ||
|
|
3a517a490e | ||
|
|
aeba26760b | ||
|
|
85d74b4ccf | ||
|
|
a5f92e2522 |
@@ -1 +1,4 @@
|
||||
BasedOnStyle: Google
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
DerivePointerAlignment: false
|
||||
@@ -0,0 +1,2 @@
|
||||
line_length: 80
|
||||
indent: 2
|
||||
@@ -0,0 +1,4 @@
|
||||
# Carry-patches must keep LF line endings even when checked out on
|
||||
# Windows with default core.autocrlf=true; otherwise `git apply`
|
||||
# refuses them on EOL mismatch with the (non-CRLF) target tree.
|
||||
*.patch text eol=lf
|
||||
@@ -0,0 +1,41 @@
|
||||
name: download and cache git repo
|
||||
description: this simply dumps it in the current directory...
|
||||
inputs:
|
||||
name:
|
||||
required: true
|
||||
url:
|
||||
required: true
|
||||
rev:
|
||||
default: ''
|
||||
tag:
|
||||
default: ''
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: setup cache
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{inputs.name}}
|
||||
key: ${{inputs.url}}@${{inputs.rev}}${{inputs.tag}}
|
||||
|
||||
- name: download rev
|
||||
if: steps.cache.outputs.cache-hit != 'true' && inputs.rev != ''
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ${{inputs.name}}
|
||||
cd ${{inputs.name}}
|
||||
git init
|
||||
git remote add origin ${{inputs.url}}
|
||||
git fetch --depth 1 origin ${{inputs.rev}}
|
||||
git checkout ${{inputs.rev}}
|
||||
git submodule update --init --recursive
|
||||
|
||||
- name: download tag
|
||||
if: steps.cache.outputs.cache-hit != 'true' && inputs.tag != ''
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch ${{inputs.tag}} --depth 1 ${{inputs.url}} ${{inputs.name}}
|
||||
cd ${{inputs.name}}
|
||||
git submodule update --init --recursive
|
||||
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
@@ -0,0 +1,68 @@
|
||||
name: build_wheels
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheels on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
# macos-15-intel: x86-64
|
||||
# macos-15: arm64
|
||||
os: [ubuntu-22.04, ubuntu-22.04-arm, windows-2025, windows-11-arm, macos-15-intel, macos-15]
|
||||
steps:
|
||||
- run: |
|
||||
git config --global submodule.fetchJobs 8
|
||||
git config --global core.longpaths true
|
||||
- if: matrix.os == 'macos-15'
|
||||
run: echo "CIBW_ARCHS=arm64 universal2" >> "$GITHUB_ENV"
|
||||
- if: matrix.os == 'windows-11-arm'
|
||||
run: echo "CIBW_BUILD=cp311-* cp312-* cp313-* cp314-*" >> $env:GITHUB_ENV
|
||||
- uses: actions/checkout@v6
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v3.4.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@v6
|
||||
- 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
|
||||
@@ -8,14 +8,25 @@ 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@v6
|
||||
- uses: DoozyX/clang-format-lint-action@v0.20
|
||||
with:
|
||||
source: '.'
|
||||
exclude: '*/third_party'
|
||||
extensions: 'h,cpp'
|
||||
clangFormatVersion: 12
|
||||
extensions: 'h,cpp,js,ts,html'
|
||||
clangFormatVersion: 20
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
- uses: psf/black@stable
|
||||
with:
|
||||
options: "--check --verbose"
|
||||
src: "./bindings/python/examples"
|
||||
# - name: "gersemi cmake check"
|
||||
# run: |
|
||||
# pip3 install gersemi
|
||||
# ./scripts/gersemi-check.sh
|
||||
|
||||
|
||||
@@ -1,41 +1,95 @@
|
||||
name: Deploy documentation
|
||||
name: Deploy documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
release:
|
||||
types: [published]
|
||||
workflow_run:
|
||||
workflows: [CI]
|
||||
types: [completed]
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Branch or tag to build docs from (e.g. v3.4.0)'
|
||||
required: true
|
||||
destination_dir:
|
||||
description: 'Subdirectory to deploy to on gh-pages (e.g. v3.4.0)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
deploy_github_pages:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
# Skip when the CI run that triggered us failed.
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ (github.event_name == 'workflow_dispatch' && inputs.ref) || (github.event_name == 'workflow_run' && github.event.workflow_run.head_sha) || github.ref }}
|
||||
|
||||
# github.sha is the ref the workflow was launched from, which on a manual
|
||||
# dispatch is not the ref being deployed. Use what checkout landed on so
|
||||
# the artifact matches the code.
|
||||
- name: Resolve deployed commit
|
||||
id: deployed
|
||||
run: echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Doxygen Action
|
||||
uses: mattnotmitt/doxygen-action@v1
|
||||
uses: mattnotmitt/doxygen-action@edge
|
||||
|
||||
- name: Move deployables
|
||||
run: |
|
||||
mkdir public
|
||||
mv ./samples ./public
|
||||
mv ./docs ./public
|
||||
mv ./bindings/wasm/examples/* ./public
|
||||
mv ./dashboard ./public
|
||||
|
||||
- name: Download WASM files
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
# No continue-on-error: a missing artifact means the editor, examples and
|
||||
# TypeScript docs would be absent, and publishing that silently is worse
|
||||
# than failing.
|
||||
- name: Download manifoldCAD editor, examples and documentation
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: manifold.yml
|
||||
workflow_conclusion: completed
|
||||
pr: ${{github.event.pull_request.number}}
|
||||
name: wasm
|
||||
workflow_conclusion: success
|
||||
# Pin to the artifact from the CI run that triggered us
|
||||
commit: ${{ (github.event_name != 'workflow_run' && steps.deployed.outputs.sha) || '' }}
|
||||
run_id: ${{ (github.event_name == 'workflow_run' && github.event.workflow_run.id) || '' }}
|
||||
check_artifacts: true
|
||||
name: wasmpublic
|
||||
path: ./public
|
||||
|
||||
- name: Deploy to Github Pages
|
||||
# unreleased build for testing, not a version anyone should pin to.
|
||||
- name: Deploy master to alpha/
|
||||
if: github.event_name == 'workflow_run'
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: gh-pages
|
||||
force_orphan: true
|
||||
publish_dir: ./public
|
||||
destination_dir: alpha
|
||||
|
||||
- name: Deploy to vX.Y.Z/
|
||||
if: github.event_name != 'workflow_run'
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: gh-pages
|
||||
publish_dir: ./public
|
||||
destination_dir: ${{ (github.event_name == 'release' && github.ref_name) || inputs.destination_dir }}
|
||||
|
||||
# A release also publishes to the site root, so manifoldcad.org always
|
||||
# serves the current release without a redirect.(it is the latest release)
|
||||
#
|
||||
# keep_files is load-bearing here and must not be removed. With no
|
||||
# destination_dir the action's cleanup runs at the branch root, so
|
||||
# without this it would delete alpha/ and every vX.Y.Z/ directory.
|
||||
- name: Deploy to gh-pages root
|
||||
if: github.event_name == 'release'
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: gh-pages
|
||||
publish_dir: ./public
|
||||
keep_files: true
|
||||
cname: manifoldcad.org
|
||||
publish_dir: ./public
|
||||
@@ -10,222 +10,648 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
build_windows:
|
||||
name: Windows (TBB:${{matrix.parallelization}}, SHARED:${{matrix.shared}})
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
cuda_support: [ON, OFF]
|
||||
parallel_backend: [NONE, OMP, TBB]
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event.pull_request.draft == false
|
||||
container:
|
||||
image: docker://nvidia/cuda:12.0.1-devel-ubuntu22.04
|
||||
parallelization: [OFF, ON]
|
||||
shared: [OFF, ON]
|
||||
runs-on: windows-2025
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get -y update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y libomp-dev libassimp-dev git libtbb-dev pkg-config libpython3-dev python3 python3-distutils python3-pip lcov
|
||||
pip install trimesh
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: lukka/get-cmake@v4.3.3
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- name: Build ${{matrix.backend}}
|
||||
cmakeVersion: 4.3.3
|
||||
- &clone-tbb
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: tbb
|
||||
url: https://github.com/oneapi-src/oneTBB.git
|
||||
tag: v2022.3.0
|
||||
- &clone-nanobind
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: nanobind
|
||||
url: https://github.com/wjakob/nanobind.git
|
||||
tag: v2.12.0
|
||||
- &clone-gtest
|
||||
uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: gtest
|
||||
url: https://github.com/google/googletest.git
|
||||
tag: v1.17.0
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Build ${{matrix.parallelization}}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_EXPORT=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} -DMANIFOLD_USE_CUDA=${{matrix.cuda_support}} .. && make
|
||||
- name: Test ${{matrix.parallel_backend}} with CUDA ${{matrix.cuda_support}}
|
||||
# note that the test for CUDA backend does not really test CUDA, as we
|
||||
# don't have CUDA GPU on GitHub Action
|
||||
cmake . \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=${{matrix.shared}} \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_NANOBIND=nanobind \
|
||||
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=gtest \
|
||||
-A x64 -B build
|
||||
cmake --build build --target ALL_BUILD --config Release
|
||||
- name: Test dll location
|
||||
if: matrix.shared == 'ON'
|
||||
shell: bash
|
||||
run: |
|
||||
ls ./build/lib/Release
|
||||
[ -f ./build/lib/Release/manifold.dll ]
|
||||
[ -f ./build/lib/Release/manifoldc.dll ]
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
cp ./build/lib/Release/* ./build/bin/Release
|
||||
./build/bin/Release/manifold_test.exe
|
||||
- name: Export determinism meshes
|
||||
if: matrix.parallelization == 'OFF' && matrix.shared == 'OFF'
|
||||
shell: bash
|
||||
env:
|
||||
MANIFOLD_OBJ_HEX_FLOAT: 1
|
||||
run: |
|
||||
cp ./build/lib/Release/* ./build/bin/Release
|
||||
bash ./scripts/export_determinism_meshes.sh build/bin/Release ./manifold_test.exe meshes-windows
|
||||
- name: Upload determinism meshes (Windows)
|
||||
if: matrix.parallelization == 'OFF' && matrix.shared == 'OFF'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: meshes-windows
|
||||
path: meshes-windows/
|
||||
build:
|
||||
name: ${{matrix.name}}
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: GCC 11 (TBB:ON)
|
||||
gcc: 11
|
||||
parallelization: ON
|
||||
os: ubuntu-22.04
|
||||
std: 17
|
||||
- name: GCC 13 (TBB:OFF)
|
||||
gcc: 13
|
||||
parallelization: OFF
|
||||
os: ubuntu-24.04
|
||||
std: 17
|
||||
- name: GCC 13 (TBB:ON)
|
||||
gcc: 13
|
||||
parallelization: ON
|
||||
os: ubuntu-24.04
|
||||
std: 17
|
||||
- name: GCC 14 (TBB:OFF)
|
||||
gcc: 14
|
||||
parallelization: OFF
|
||||
os: ubuntu-24.04
|
||||
std: 17
|
||||
- name: GCC 14 (TBB:ON)
|
||||
gcc: 14
|
||||
parallelization: ON
|
||||
os: ubuntu-24.04
|
||||
std: 17
|
||||
# One strict lane at C++20, to catch what it deprecates or redefines.
|
||||
- name: GCC 14 (TBB:ON, C++20)
|
||||
gcc: 14
|
||||
parallelization: ON
|
||||
os: ubuntu-24.04
|
||||
std: 20
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CC: gcc-${{ matrix.gcc }}
|
||||
CXX: g++-${{ matrix.gcc }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- &setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: 'pip'
|
||||
- run: pip install -U trimesh pytest
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libgtest-dev libassimp-dev git libtbb-dev pkg-config cmake
|
||||
- *clone-nanobind
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PYBIND=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DFETCHCONTENT_SOURCE_DIR_NANOBIND=nanobind \
|
||||
-DASSIMP_ENABLE=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Test
|
||||
run: |
|
||||
./build/test/manifold_test
|
||||
# python tests
|
||||
export PYTHONPATH=$PYTHONPATH:$(pwd)/build/bindings/python
|
||||
cd build/test
|
||||
./manifold_test
|
||||
cd ../../
|
||||
python3 bindings/python/examples/run_all.py
|
||||
python3 bindings/python/examples/run_all.py -e
|
||||
python3 -m pytest
|
||||
- name: Export determinism meshes
|
||||
if: matrix.parallelization == 'OFF' && matrix.gcc == 14 && matrix.os == 'ubuntu-24.04'
|
||||
env:
|
||||
MANIFOLD_OBJ_HEX_FLOAT: 1
|
||||
run: |
|
||||
bash ./scripts/export_determinism_meshes.sh build/test ./manifold_test meshes-linux
|
||||
- name: Upload determinism meshes (Linux)
|
||||
if: matrix.parallelization == 'OFF' && matrix.gcc == 14 && matrix.os == 'ubuntu-24.04'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: meshes-linux
|
||||
path: meshes-linux/
|
||||
- name: test cmake consumer
|
||||
run: |
|
||||
sudo cmake --install build
|
||||
./scripts/test-cmake.sh
|
||||
|
||||
build_no_iostream:
|
||||
name: NO_IOSTREAM (clang)
|
||||
timeout-minutes: 20
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: cmake clang libgtest-dev
|
||||
- name: Configure + build with MANIFOLD_NO_IOSTREAM=ON, MANIFOLD_TEST=ON
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DMANIFOLD_NO_IOSTREAM=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_TEST=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Run manifold_test (iostream-using TESTs are gated out)
|
||||
run: ./build/test/manifold_test
|
||||
- name: Verify conflict guard fires (NO_IOSTREAM + DEBUG)
|
||||
run: |
|
||||
if cmake -DMANIFOLD_NO_IOSTREAM=ON -DMANIFOLD_DEBUG=ON \
|
||||
. -B /tmp/conflict-debug 2>&1; then
|
||||
echo "FAIL: configure should have failed for NO_IOSTREAM + DEBUG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
build_libcxx_hardened:
|
||||
name: Libc++ hardened (clang, EXTENSIVE, TBB:${{matrix.parallelization}})
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Match nixpkgs hardening profile (the trigger for #1735), plus
|
||||
# EXTENSIVE checks to catch latent preconditions beyond bounds.
|
||||
CXXFLAGS: "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -stdlib=libc++"
|
||||
LDFLAGS: "-stdlib=libc++"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
# tbb/gtest are built from the cloned sources below under the
|
||||
# lane's libc++ flags so they match the test binary's ABI; the distro
|
||||
# packages are libstdc++.
|
||||
packages: cmake clang libc++-dev libc++abi-dev
|
||||
- *clone-tbb
|
||||
- *clone-gtest
|
||||
- name: Configure + build with libc++ and EXTENSIVE hardening
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_C_COMPILER=clang \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_TEST=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=gtest \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Run manifold_test
|
||||
run: ./build/test/manifold_test
|
||||
|
||||
build_gcc_codecov:
|
||||
name: code coverage
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
# parallelization: [OFF, ON]
|
||||
parallelization: [OFF]
|
||||
env:
|
||||
CC: gcc-14
|
||||
CXX: g++-14
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libgtest-dev git libtbb-dev pkg-config lcov cmake
|
||||
- name: build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DCODE_COVERAGE=ON \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- 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' && matrix.cuda_support == 'OFF'
|
||||
run: |
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} -DMANIFOLD_USE_CUDA=${{matrix.cuda_support}} -DCODE_COVERAGE=ON .. && make
|
||||
lcov --capture --initial --directory . --output-file ./code_coverage_init.info
|
||||
cd test
|
||||
./manifold_test
|
||||
cd ../
|
||||
lcov --capture --directory . --output-file ./code_coverage_test.info
|
||||
lcov --capture --gcov-tool gcov-14 --ignore-errors mismatch --initial --directory . --output-file ./code_coverage_init.info
|
||||
test/manifold_test
|
||||
lcov --capture --gcov-tool gcov-14 --ignore-errors mismatch --directory . --output-file ./code_coverage_test.info
|
||||
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' && matrix.cuda_support == 'OFF'
|
||||
lcov --extract ./code_coverage_total.info "/*/manifold/src/*" --output-file ./code_coverage.info
|
||||
- uses: codecov/codecov-action@v5
|
||||
with:
|
||||
disable_search: true
|
||||
files: build/code_coverage.info
|
||||
fail_ci_if_error: false
|
||||
name: ${{matrix.parallel_backend}}-${{matrix.cuda_support}}
|
||||
|
||||
build_cbind:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event.pull_request.draft == false
|
||||
container:
|
||||
image: docker://nvidia/cuda:12.0.1-devel-ubuntu22.04
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get -y update
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y libomp-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 OMP and CUDA
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_CBIND=ON -DMANIFOLD_PAR=OMP -DMANIFOLD_USE_CUDA=ON .. && make
|
||||
- name: Test ${{matrix.parallel_backend}} with CUDA ${{matrix.cuda_support}}
|
||||
# note that the test for CUDA backend does not really test CUDA, as we
|
||||
# don't have CUDA GPU on GitHub Action
|
||||
run: |
|
||||
cd build/test
|
||||
./manifold_test --gtest_filter=CBIND.*
|
||||
name: ${{matrix.parallelization}}
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
verbose: true
|
||||
|
||||
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
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Setup WASM
|
||||
node-version: 24
|
||||
cache: 'npm'
|
||||
cache-dependency-path: bindings/**/package-lock.json
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: cmake
|
||||
version: 1
|
||||
- name: Setup EMSDK
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 5.0.2
|
||||
- *clone-tbb
|
||||
- *clone-gtest
|
||||
- name: Build WASM ${{matrix.parallelization}}
|
||||
run: |
|
||||
# setup emscripten
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
cd emsdk
|
||||
./emsdk install latest
|
||||
./emsdk activate latest
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- name: Build WASM
|
||||
run: |
|
||||
source ./emsdk/emsdk_env.sh
|
||||
mkdir build
|
||||
emcmake cmake \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=${{matrix.debug}} \
|
||||
-DMANIFOLD_ASSERT=${{matrix.debug}} \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=gtest \
|
||||
. -B build
|
||||
cd build
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=Release .. && emmake make
|
||||
emmake make
|
||||
- name: Test WASM
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd build/test
|
||||
node ./manifold_test.js
|
||||
cd ../bindings/wasm
|
||||
npm install
|
||||
- name: Run unit tests
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm
|
||||
npm ci
|
||||
npm run build
|
||||
npm test
|
||||
- name: Upload WASM files
|
||||
uses: actions/upload-artifact@v3
|
||||
- name: Isolated npm smoke test
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
set -ev -o pipefail
|
||||
cd bindings/wasm
|
||||
wasmpath=$(pwd)
|
||||
npmtestpath=../npmtest
|
||||
# Create an npm package.
|
||||
npm ci && npm pack
|
||||
# Install it in an empty directory.
|
||||
mkdir -p ${npmtestpath} && cd ${npmtestpath}
|
||||
npm install ${wasmpath}/manifold-3d*.tgz
|
||||
cp ${wasmpath}/test/fixtures/npm/* .
|
||||
# Positive case smoke test.
|
||||
npx manifold-cad ./unitSphere.mjs ./unitSphere.glb && echo "Positive smoke test succeeded"
|
||||
# Negative case smoke test.
|
||||
npx manifold-cad ./unitSphereNoManifoldImport.mjs ./unitSphereNoManifoldImport.glb || echo "Negative smoke test failed as expected"
|
||||
- name: Build manifoldCAD editor
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm
|
||||
npm run ci:editor
|
||||
npm run build:editor
|
||||
- name: Build WASM examples
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm/examples
|
||||
npm run ci:examples
|
||||
npm run build:examples
|
||||
- name: Build WASM documentation and site
|
||||
if: matrix.parallelization == 'OFF'
|
||||
run: |
|
||||
cd bindings/wasm
|
||||
npm run docs
|
||||
npm run public
|
||||
- name: Upload manifoldCAD editor, examples and documentation
|
||||
uses: actions/upload-artifact@v4
|
||||
if: github.event_name == 'push' && matrix.parallelization == 'OFF' && matrix.debug == 'OFF'
|
||||
with:
|
||||
name: wasmpublic
|
||||
path: bindings/wasm/public/
|
||||
retention-days: 90
|
||||
overwrite: true
|
||||
- name: Upload complete wasm build.
|
||||
uses: actions/upload-artifact@v4
|
||||
if: github.event_name == 'push' && matrix.parallelization == 'OFF' && matrix.debug == 'OFF'
|
||||
with:
|
||||
name: wasm
|
||||
path: |
|
||||
build/bindings/wasm/manifold.js
|
||||
build/bindings/wasm/manifold.wasm
|
||||
build/bindings/wasm/manifold.d.ts
|
||||
build/bindings/wasm/manifold-global-types.d.ts
|
||||
build/bindings/wasm/manifold-encapsulated-types.d.ts
|
||||
path: bindings/wasm/
|
||||
retention-days: 90
|
||||
overwrite: true
|
||||
|
||||
build_windows:
|
||||
build_fuzzer:
|
||||
name: fuzzer
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallel_backend: [NONE]
|
||||
cuda_support: [OFF]
|
||||
max-parallel: 1
|
||||
runs-on: windows-2019
|
||||
if: github.event.pull_request.draft == false
|
||||
os: [ubuntu-24.04, macos-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install apt packages
|
||||
if: matrix.os == 'ubuntu-24.04'
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Install CUDA
|
||||
if: matrix.backend == 'CUDA'
|
||||
env:
|
||||
cuda: "11.3.0"
|
||||
visual_studio: "Visual Studio 16 2019"
|
||||
shell: powershell
|
||||
packages: libgtest-dev clang cmake
|
||||
version: 1
|
||||
- name: Install brew packages
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
Invoke-RestMethod https://raw.githubusercontent.com/ptheywood/cuda-cmake-github-actions/21811d232fc775fa8d44dddf7f8c5ce2cf8d2b81/scripts/actions/install_cuda_windows.ps1 -OutFile install_cuda_windows.ps1
|
||||
.\install_cuda_windows.ps1
|
||||
- name: nvcc check
|
||||
if: matrix.backend == 'CUDA'
|
||||
shell: powershell
|
||||
run: |
|
||||
nvcc -V
|
||||
ls $env:CUDA_PATH
|
||||
ls $env:CUDA_PATH\bin
|
||||
ls $env:CUDA_PATH\include
|
||||
- name: Build ${{matrix.backend}}
|
||||
shell: powershell
|
||||
run: |
|
||||
cmake . -DCMAKE_BUILD_TYPE=Release -B build -DMANIFOLD_DEBUG=ON -DPYBIND11_FINDPYTHON=OFF -DMANIFOLD_PAR=${{matrix.parallel_backend}} -DMANIFOLD_USE_CUDA=${{matrix.cuda_support}} -A x64
|
||||
cd build
|
||||
cmake --build . --target ALL_BUILD --config Release
|
||||
- name: Test ${{matrix.parallel_backend}} with CUDA ${{matrix.cuda_support}}
|
||||
shell: bash
|
||||
run: |
|
||||
cd build/test
|
||||
./Release/manifold_test.exe
|
||||
|
||||
build_mac:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallel_backend: [NONE, TBB]
|
||||
runs-on: macos-latest
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- name: Install common dependencies
|
||||
run: |
|
||||
brew install pkg-config
|
||||
pip install trimesh
|
||||
- name: Install OpenMP
|
||||
if: matrix.parallel_backend == 'OMP'
|
||||
run: brew install libomp
|
||||
- name: Install TBB
|
||||
if: matrix.parallel_backend == 'TBB'
|
||||
run: brew install tbb
|
||||
- uses: actions/checkout@v3
|
||||
brew install googletest cmake
|
||||
- uses: ./.github/actions/git-clone
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: jwlawson/actions-setup-cmake@v1.12
|
||||
name: fuzztest
|
||||
url: https://github.com/google/fuzztest.git
|
||||
rev: f1e26613f66997aa09d3026762e275de22b2daae
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DMANIFOLD_DEBUG=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} .. && make
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_FUZZ=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DFETCHCONTENT_SOURCE_DIR_FUZZTEST=fuzztest \
|
||||
. -B build
|
||||
ASAN_OPTIONS=detect_container_overflow=0 cmake --build build
|
||||
|
||||
sanitizer:
|
||||
uses: ./.github/workflows/sanitizer.yml
|
||||
with:
|
||||
subset: core
|
||||
retention-days: 1
|
||||
|
||||
build_mxe:
|
||||
name: MXE (TBB:${{matrix.parallelization == 'ON'}})
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
runs-on: ubuntu-latest
|
||||
container: openscad/mxe-x86_64-gui:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Build
|
||||
run: |
|
||||
export MXEDIR=/mxe
|
||||
export MXE_TARGETS=x86_64-w64-mingw32.static.posix
|
||||
export MXEDIR=$HOME/openscad_deps/mxe
|
||||
export MXETARGETDIR=$MXEDIR/usr/$MXE_TARGETS
|
||||
export PATH=/mxe/usr/bin:$PATH
|
||||
export CMAKE=$MXE_TARGETS-cmake
|
||||
$CMAKE \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_CBIND=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
. -B build
|
||||
$CMAKE --build build
|
||||
|
||||
build_mac:
|
||||
name: MacOS (TBB:${{matrix.parallelization == 'ON'}}, shared lib:${{matrix.shared == 'ON'}}, ${{matrix.os}})
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
parallelization: [OFF, ON]
|
||||
shared: [OFF, ON]
|
||||
# macos-15-intel: intel
|
||||
# macos-latest: arm
|
||||
os: [macos-15-intel, macos-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: 'pip'
|
||||
- name: Install common dependencies
|
||||
run: |
|
||||
brew install googletest assimp cmake
|
||||
pip install trimesh pytest
|
||||
- name: Install TBB
|
||||
if: matrix.parallelization == 'ON'
|
||||
run: brew install tbb
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=${{matrix.shared}} \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PYBIND=ON \
|
||||
-DASSIMP_ENABLE=ON \
|
||||
-DMANIFOLD_PAR=${{matrix.parallelization}} \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Test
|
||||
run: |
|
||||
cd build/test
|
||||
./manifold_test
|
||||
build/test/manifold_test
|
||||
- name: Export determinism meshes
|
||||
if: matrix.parallelization == 'OFF' && matrix.shared == 'OFF' && matrix.os == 'macos-latest'
|
||||
env:
|
||||
MANIFOLD_OBJ_HEX_FLOAT: 1
|
||||
run: |
|
||||
bash ./scripts/export_determinism_meshes.sh build/test ./manifold_test meshes-mac
|
||||
- name: Upload determinism meshes (macOS)
|
||||
if: matrix.parallelization == 'OFF' && matrix.shared == 'OFF' && matrix.os == 'macos-latest'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: meshes-mac
|
||||
path: meshes-mac/
|
||||
- name: test cmake consumer
|
||||
run: |
|
||||
sudo cmake --install build
|
||||
./scripts/test-cmake.sh
|
||||
- name: test pkg-config consumer
|
||||
if: matrix.shared == 'ON'
|
||||
run: |
|
||||
LDFLAGS=-Wl,-rpath,/usr/local/lib ./scripts/test-pkgconfig.sh
|
||||
|
||||
build_mac_builtin_tbb:
|
||||
timeout-minutes: 30
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
cache: 'pip'
|
||||
- name: Install common dependencies
|
||||
run: |
|
||||
brew install googletest cmake
|
||||
pip install trimesh pytest
|
||||
- *clone-tbb
|
||||
- name: test cmake consumer subdir
|
||||
run: |
|
||||
export TBB=$(pwd)/tbb
|
||||
cd ..
|
||||
./manifold/scripts/test-cmake-subdir.sh
|
||||
- name: Build
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=ON \
|
||||
-DMANIFOLD_ASSERT=ON \
|
||||
-DMANIFOLD_PYBIND=OFF \
|
||||
-DMANIFOLD_PAR=ON \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
. -B build
|
||||
cmake --build build
|
||||
- name: Test
|
||||
run: |
|
||||
./build/test/manifold_test
|
||||
- name: test cmake consumer
|
||||
run: |
|
||||
sudo cmake --install build
|
||||
./scripts/test-cmake.sh
|
||||
LDFLAGS=-Wl,-rpath,/usr/local/lib ./scripts/test-pkgconfig.sh
|
||||
|
||||
build_nix:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
variant: [none, omp, tbb, none-cuda, omp-cuda, tbb-cuda, 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
|
||||
- uses: actions/checkout@v6
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- run: nix build -L '.?submodules=1#${{matrix.variant}}'
|
||||
|
||||
determinism_check:
|
||||
name: Cross-platform determinism check
|
||||
needs: [build, build_windows, build_mac]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Download Linux meshes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: cachix/install-nix-action@v15
|
||||
- run: |
|
||||
nix build -L '.?submodules=1#manifold-${{matrix.variant}}'
|
||||
name: meshes-linux
|
||||
path: meshes/linux
|
||||
- name: Download macOS meshes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: meshes-mac
|
||||
path: meshes/mac
|
||||
- name: Download Windows meshes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: meshes-windows
|
||||
path: meshes/windows
|
||||
- name: Compare meshes across platforms
|
||||
run: |
|
||||
bash ./scripts/compare_artifact.sh meshes
|
||||
|
||||
benchmark_guard:
|
||||
name: perfTest base-vs-head guard
|
||||
if: github.event_name == 'pull_request'
|
||||
timeout-minutes: 45
|
||||
runs-on: macos-15
|
||||
env:
|
||||
PERF_REPEATS: 3
|
||||
REGRESSION_WARN_PERCENT: 20
|
||||
REGRESSION_WARN_ABS_MS: 10
|
||||
MEMORY_WARN_PERCENT: 20
|
||||
MEMORY_WARN_ABS_MB: 50
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install brew packages
|
||||
run: brew install googletest cmake
|
||||
- name: Benchmark base and head
|
||||
run: >
|
||||
python3 ./scripts/benchmark_pr_perf_guard.py
|
||||
--base-sha "${{ github.event.pull_request.base.sha }}"
|
||||
--head-sha "${{ github.event.pull_request.head.sha }}"
|
||||
--repeats "${PERF_REPEATS}"
|
||||
- name: Compare benchmark results
|
||||
run: >
|
||||
python3 ./scripts/compare_pr_perf_guard.py
|
||||
--base-dir ./bench/base
|
||||
--head-dir ./bench/head
|
||||
--warn-percent "${REGRESSION_WARN_PERCENT}"
|
||||
--warn-abs-ms "${REGRESSION_WARN_ABS_MS}"
|
||||
--memory-warn-percent "${MEMORY_WARN_PERCENT}"
|
||||
--memory-warn-abs-mb "${MEMORY_WARN_ABS_MB}"
|
||||
--markdown-out ./bench/summary.md
|
||||
--json-out ./bench/result.json
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
name: Prepare release
|
||||
|
||||
# Bumps the version everywhere it appears and opens a PR with the result.
|
||||
# Run this before tagging, so the commit you tag already carries the right
|
||||
# version. Deriving it here rather than at publish time means what ships
|
||||
# always corresponds to a real commit in history.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release version, without a leading v (e.g. 3.6.0)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
prepare_release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
# Nothing is built or tested here; that all happens on the resulting PR.
|
||||
# The lockfiles are edited in place rather than regenerated with npm
|
||||
# install, which keeps this fast and limits the commit to the version.
|
||||
- name: Set the version
|
||||
run: python3 scripts/set_version.py "${{ inputs.version }}"
|
||||
|
||||
# gh rather than a third-party action; release_file.yml already uses it.
|
||||
- name: Commit and open a pull request
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -e
|
||||
if git diff --quiet; then
|
||||
echo "Already at $VERSION; nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git checkout -b "release/v$VERSION"
|
||||
git commit -am "Set version to $VERSION"
|
||||
# Force, so a re-run replaces an earlier attempt's branch rather than
|
||||
# being rejected. The branch is generated, so there is nothing to lose.
|
||||
git push --force origin "release/v$VERSION"
|
||||
# Heredoc so the body is not indented by the YAML block scalar,
|
||||
# which markdown would render as a code block.
|
||||
cat > /tmp/pr-body.md <<EOF
|
||||
Generated by the Prepare release workflow.
|
||||
|
||||
Sets the version to \`$VERSION\` across CMakeLists.txt, pyproject.toml,
|
||||
flake.nix, bindings/wasm/package.json and scripts/test-cmake.sh, and
|
||||
refreshes the package-lock files.
|
||||
|
||||
Once merged, tag the merge commit \`v$VERSION\` and publish the release.
|
||||
The tagged commit then carries the matching version, which is what
|
||||
publish_npm reads.
|
||||
EOF
|
||||
sed -i 's/^ //' /tmp/pr-body.md
|
||||
# --base is required. Without it gh targets the repository's default
|
||||
# branch, so a backport release dispatched against v3.5.x would open
|
||||
# a PR into master carrying the whole branch difference.
|
||||
gh pr create \
|
||||
--base "$GITHUB_REF_NAME" \
|
||||
--title "Release v$VERSION" \
|
||||
--body-file /tmp/pr-body.md
|
||||
@@ -0,0 +1,78 @@
|
||||
name: publish_npm
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Branch or tag to publish from (e.g. v3.5.2)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
publish_npm:
|
||||
# Matches the WASM build environment in manifold.yml.
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
id-token: write # Required for OIDC
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ (github.event_name == 'workflow_dispatch' && inputs.ref) || github.ref }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
registry-url: 'https://registry.npmjs.org/'
|
||||
|
||||
- name: Update npm # Ensure npm 11.5.1 or later for trusted publishing
|
||||
run: npm install -g npm@latest
|
||||
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: cmake
|
||||
version: 1
|
||||
|
||||
- name: Setup EMSDK
|
||||
uses: mymindstorm/setup-emsdk@v14
|
||||
with:
|
||||
version: 5.0.2
|
||||
|
||||
- uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: tbb
|
||||
url: https://github.com/oneapi-src/oneTBB.git
|
||||
tag: v2022.3.0
|
||||
|
||||
- uses: ./.github/actions/git-clone
|
||||
with:
|
||||
name: gtest
|
||||
url: https://github.com/google/googletest.git
|
||||
tag: v1.17.0
|
||||
|
||||
# Built here rather than downloaded from a CI artifact, so what ships is
|
||||
# always the checked-out commit.
|
||||
- name: Build WASM
|
||||
run: |
|
||||
emcmake cmake \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DMANIFOLD_STRICT=ON \
|
||||
-DMANIFOLD_PAR=OFF \
|
||||
-DMANIFOLD_USE_BUILTIN_TBB=ON \
|
||||
-DMANIFOLD_DEBUG=OFF \
|
||||
-DMANIFOLD_ASSERT=OFF \
|
||||
-DFETCHCONTENT_SOURCE_DIR_TBB=tbb \
|
||||
-DFETCHCONTENT_SOURCE_DIR_GOOGLETEST=gtest \
|
||||
. -B build
|
||||
cd build
|
||||
emmake make
|
||||
|
||||
- name: Publish to npm
|
||||
run: |
|
||||
cd ./bindings/wasm/
|
||||
npm ci
|
||||
npm publish
|
||||
@@ -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@v6
|
||||
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}}
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Sanitizer
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
subset:
|
||||
description: Which gtest subset to run - core or extended.
|
||||
type: string
|
||||
required: true
|
||||
retention-days:
|
||||
description: >
|
||||
Retention for the shared sanitizer build artifact. It only exists to
|
||||
hand the build from the build job to the test job, so this is short.
|
||||
type: number
|
||||
default: 1
|
||||
test-timeout-sec:
|
||||
description: Per-run timeout applied inside sanitizer_test.sh.
|
||||
type: number
|
||||
default: 1200
|
||||
outputs:
|
||||
build-result:
|
||||
description: Result of the sanitizer build job.
|
||||
value: ${{ jobs.build_sanitizer.result }}
|
||||
test-result:
|
||||
description: Result of the sanitizer test job.
|
||||
value: ${{ jobs.test_sanitizer.result }}
|
||||
|
||||
jobs:
|
||||
build_sanitizer:
|
||||
name: Sanitizer build (ASan+UBSan)
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libgtest-dev cmake
|
||||
version: 1
|
||||
- name: Build (asan/ubsan)
|
||||
run: ./scripts/ci/sanitizer_build.sh
|
||||
- name: Upload sanitizer build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sanitizer-build
|
||||
path: build/
|
||||
retention-days: ${{ inputs.retention-days }}
|
||||
|
||||
test_sanitizer:
|
||||
name: Sanitizer ${{ inputs.subset }} subset (ASan+UBSan)
|
||||
needs: build_sanitizer
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
ASAN_OPTIONS: detect_container_overflow=0:strict_init_order=1
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
SANITIZER_TEST_TIMEOUT_SEC: ${{ inputs.test-timeout-sec }}
|
||||
SANITIZER_SUBSET: ${{ inputs.subset }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Download sanitizer build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: sanitizer-build
|
||||
path: build
|
||||
- name: Test sanitizer subset
|
||||
shell: bash
|
||||
run: bash ./scripts/ci/sanitizer_test.sh
|
||||
@@ -0,0 +1,93 @@
|
||||
name: TSAN
|
||||
|
||||
# Run ThreadSanitizer against the test suite using a TBB built with
|
||||
# TBB_SANITIZE=thread (so TBB's internal fences are swapped for acquire/release
|
||||
# ops TSAN can model — otherwise stock libtbb produces thousands of spurious
|
||||
# warnings). On a cold TBB build run takes ~22 min on CI; intent is to move
|
||||
# off per-PR onto a nightly/on-demand trigger once validated. For now it
|
||||
# runs per-PR to prove out the plumbing.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
jobs:
|
||||
tsan:
|
||||
name: TSAN (GCC 14, TBB_SANITIZE=thread)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
env:
|
||||
CC: gcc-14
|
||||
CXX: g++-14
|
||||
TBB_TAG: v2022.3.0
|
||||
TBB_PREFIX: ${{ github.workspace }}/.tbb-tsan
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install apt packages
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
|
||||
with:
|
||||
packages: libgtest-dev libassimp-dev pkg-config cmake
|
||||
|
||||
- name: Cache TBB (TSAN build)
|
||||
id: cache-tbb
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.TBB_PREFIX }}
|
||||
key: tbb-tsan-${{ env.TBB_TAG }}-gcc14-ubuntu-24.04-v1
|
||||
|
||||
- name: Build oneTBB with TBB_SANITIZE=thread
|
||||
if: steps.cache-tbb.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth 1 --branch "$TBB_TAG" https://github.com/uxlfoundation/oneTBB.git tbb-src
|
||||
# -Wno-tsan: GCC 14's -Wtsan fires on TBB's own atomic_thread_fence
|
||||
# usage (which TBB_SANITIZE=thread mode replaces internally anyway).
|
||||
# oneTBB master adds this suppression in its own sanitize.cmake but
|
||||
# it didn't land until after v2022.3.0.
|
||||
cmake -S tbb-src -B tbb-build \
|
||||
-DTBB_TEST=OFF \
|
||||
-DTBB_SANITIZE=thread \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS=-Wno-tsan \
|
||||
-DCMAKE_INSTALL_PREFIX="$TBB_PREFIX"
|
||||
cmake --build tbb-build -j$(nproc)
|
||||
cmake --install tbb-build
|
||||
|
||||
- name: Configure manifold with TSAN
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DMANIFOLD_CBIND=ON \
|
||||
-DMANIFOLD_TEST=ON \
|
||||
-DMANIFOLD_PAR=ON \
|
||||
-DCMAKE_PREFIX_PATH="$TBB_PREFIX" \
|
||||
-DCMAKE_CXX_FLAGS=-fsanitize=thread \
|
||||
-DCMAKE_EXE_LINKER_FLAGS=-fsanitize=thread \
|
||||
-DCMAKE_SHARED_LINKER_FLAGS=-fsanitize=thread \
|
||||
. -B build
|
||||
|
||||
- name: Build manifold
|
||||
run: cmake --build build -j$(nproc)
|
||||
|
||||
- name: Run tests under TSAN
|
||||
env:
|
||||
LD_LIBRARY_PATH: ${{ env.TBB_PREFIX }}/lib
|
||||
TSAN_OPTIONS: halt_on_error=1 report_thread_leaks=0
|
||||
run: |
|
||||
set -o pipefail
|
||||
./build/test/manifold_test 2>&1 | tee tsan.log
|
||||
|
||||
- name: Upload TSAN log
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tsan-log
|
||||
path: tsan.log
|
||||
@@ -0,0 +1,77 @@
|
||||
name: Weekly Benchmarks
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: "0 2 * * 0"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: weekly-benchmark
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
sanitizer:
|
||||
uses: ./.github/workflows/sanitizer.yml
|
||||
with:
|
||||
subset: extended
|
||||
retention-days: 1
|
||||
|
||||
weekly_benchmark:
|
||||
name: Weekly benchmarks
|
||||
needs: sanitizer
|
||||
if: ${{ !cancelled() }}
|
||||
timeout-minutes: 60
|
||||
runs-on: macos-15
|
||||
env:
|
||||
WEEKLY_BENCHMARK_REPEATS: 5
|
||||
WEEKLY_BENCHMARK_THREADS: 1
|
||||
CMAKE_OSX_ARCHITECTURES: arm64
|
||||
DATA_BRANCH: benchmark-data
|
||||
RELEASE_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || '' }}
|
||||
SANITIZER_SUMMARY_SUBSET: extended
|
||||
SANITIZER_SUMMARY_RUNNER: ubuntu-24.04
|
||||
SANITIZER_SUMMARY_OS: Linux
|
||||
SANITIZER_BUILD_RESULT: ${{ needs.sanitizer.outputs.build-result }}
|
||||
SANITIZER_TEST_RESULT: ${{ needs.sanitizer.outputs.test-result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install brew packages
|
||||
run: brew install googletest assimp tbb cmake
|
||||
- name: Run weekly benchmarks
|
||||
shell: bash
|
||||
run: python3 ./scripts/run_weekly_benchmarks.py . ./bench/weekly "${WEEKLY_BENCHMARK_REPEATS}"
|
||||
- name: Report weekly benchmarks
|
||||
run: python3 ./scripts/report_weekly_benchmarks.py . ./bench/weekly "${WEEKLY_BENCHMARK_REPEATS}"
|
||||
- name: Publish weekly benchmark data
|
||||
if: github.event_name == 'release' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
|
||||
shell: bash
|
||||
run: |
|
||||
# github-actions[bot]'s identity for committing with the built-in
|
||||
# token, see https://github.com/actions/checkout#push-a-commit-using-the-built-in-token
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git fetch origin "${DATA_BRANCH}"
|
||||
git worktree add benchmark-data "origin/${DATA_BRANCH}"
|
||||
|
||||
python3 ./scripts/publish_weekly_benchmark_results.py \
|
||||
./bench/weekly ./benchmark-data \
|
||||
--release-tag "${RELEASE_TAG}" \
|
||||
--sanitizer-subset "${SANITIZER_SUMMARY_SUBSET}" \
|
||||
--sanitizer-build-result "${SANITIZER_BUILD_RESULT}" \
|
||||
--sanitizer-test-result "${SANITIZER_TEST_RESULT}" \
|
||||
--sanitizer-runner "${SANITIZER_SUMMARY_RUNNER}" \
|
||||
--sanitizer-os "${SANITIZER_SUMMARY_OS}"
|
||||
|
||||
git -C benchmark-data add weekly
|
||||
git -C benchmark-data commit -m "add weekly benchmark result ${GITHUB_RUN_ID}"
|
||||
git -C benchmark-data push origin "HEAD:${DATA_BRANCH}"
|
||||
@@ -9,7 +9,6 @@
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
@@ -109,8 +108,33 @@ build
|
||||
buildWASM
|
||||
docs/html
|
||||
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
|
||||
.cache
|
||||
CMakeUserPresets.json
|
||||
CMakeSettings.json
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
|
||||
# Temporary files
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-v][a-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Benchmark stl files
|
||||
extras/ember_tests/testfiles/raw_meshes/*
|
||||
|
||||
#Eclipse project files
|
||||
/.project
|
||||
/.cproject
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[submodule "third_party/thrust"]
|
||||
path = src/third_party/thrust
|
||||
url = https://github.com/NVIDIA/thrust
|
||||
[submodule "third_party/glm"]
|
||||
path = src/third_party/glm
|
||||
url = https://github.com/g-truc/glm
|
||||
[submodule "third_party/google_test"]
|
||||
path = test/third_party/google_test
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "third_party/pybind11"]
|
||||
path = bindings/python/third_party/pybind11
|
||||
url = https://github.com/pybind/pybind11
|
||||
branch = stable
|
||||
[submodule "src/third_party/clipper2"]
|
||||
path = src/third_party/clipper2
|
||||
url = https://github.com/AngusJohnson/clipper2
|
||||
@@ -1,26 +1,28 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "manifold test",
|
||||
"type": "lldb-dap",
|
||||
"MIMode": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/test/manifold_test",
|
||||
"args": [
|
||||
"--gtest_catch_exceptions=0",
|
||||
"--gtest_filter=Smooth.Csaszar"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/test",
|
||||
"environment": [
|
||||
{
|
||||
"name": "manifold test",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/test/manifold_test",
|
||||
"args": [
|
||||
"--gtest_break_on_failure",
|
||||
"--gtest_filter=Manifold.GetMesh"
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build/test",
|
||||
"environment": [
|
||||
{
|
||||
"name": "MALLOC_CHECK_",
|
||||
"value": "2"
|
||||
}
|
||||
]
|
||||
"name": "MALLOC_CHECK_",
|
||||
"value": "2"
|
||||
}
|
||||
]
|
||||
],
|
||||
"externalConsole": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -98,16 +98,39 @@
|
||||
"concepts": "cpp",
|
||||
"queue": "cpp",
|
||||
"stack": "cpp",
|
||||
"__hash_table": "cpp"
|
||||
"__hash_table": "cpp",
|
||||
"__std_stream": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"any": "cpp",
|
||||
"barrier": "cpp",
|
||||
"charconv": "cpp",
|
||||
"coroutine": "cpp",
|
||||
"latch": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"span": "cpp",
|
||||
"__sso_allocator": "cpp",
|
||||
"source_location": "cpp",
|
||||
"format": "cpp",
|
||||
"numbers": "cpp",
|
||||
"ranges": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"execution": "cpp",
|
||||
"csetjmp": "cpp",
|
||||
"cuchar": "cpp",
|
||||
"propagate_const": "cpp",
|
||||
"scoped_allocator": "cpp"
|
||||
},
|
||||
"C_Cpp.clang_format_fallbackStyle": "google",
|
||||
"editor.formatOnSave": true,
|
||||
"cmake.configureArgs": [
|
||||
"-DBUILD_SHARED_LIBS=ON",
|
||||
"-DMANIFOLD_EXPORT=OFF",
|
||||
"-DMANIFOLD_CBIND=ON",
|
||||
"-DMANIFOLD_PYBIND=ON",
|
||||
"-DMANIFOLD_EXPORT=ON",
|
||||
"-DMANIFOLD_DEBUG=ON",
|
||||
"-DMANIFOLD_USE_CUDA=OFF",
|
||||
"-DMANIFOLD_PAR=NONE",
|
||||
"-DMANIFOLD_ASSERT=ON",
|
||||
"-DMANIFOLD_PAR=ON",
|
||||
"-DMANIFOLD_FUZZ=OFF",
|
||||
"-DCODE_COVERAGE=OFF",
|
||||
"-DCMAKE_CXX_FLAGS=''" //'-fsanitize=address,undefined'"
|
||||
],
|
||||
@@ -120,12 +143,25 @@
|
||||
"editor.detectIndentation": false,
|
||||
"cSpell.words": [
|
||||
"Bary",
|
||||
"Coplanarity",
|
||||
"csaszar",
|
||||
"Gyroid",
|
||||
"halfedge",
|
||||
"halfedges",
|
||||
"Minkowski",
|
||||
"Tris",
|
||||
"Verts"
|
||||
"Verts",
|
||||
"Voxel",
|
||||
"voxels"
|
||||
],
|
||||
"javascript.format.semicolons": "insert",
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||
},
|
||||
"python.formatting.provider": "none",
|
||||
"clang-format.executable": "clang-format",
|
||||
"clangd.arguments": [
|
||||
"--header-insertion=never"
|
||||
],
|
||||
"editor.defaultFormatter": "xaver.clang-format",
|
||||
}
|
||||
@@ -15,82 +15,319 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(manifold LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
# C++17 by default, guarded so -DCMAKE_CXX_STANDARD and parent projects work.
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
if(NOT DEFINED CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 17)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
option(MANIFOLD_EXPORT "Build mesh export (via assimp) utility library" OFF)
|
||||
option(MANIFOLD_TEST "Enable testing suite" ON)
|
||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
# Define Manifold version
|
||||
set(MANIFOLD_VERSION_MAJOR 3)
|
||||
set(MANIFOLD_VERSION_MINOR 5)
|
||||
set(MANIFOLD_VERSION_PATCH 1)
|
||||
set(
|
||||
MANIFOLD_VERSION
|
||||
"${MANIFOLD_VERSION_MAJOR}.${MANIFOLD_VERSION_MINOR}.${MANIFOLD_VERSION_PATCH}"
|
||||
)
|
||||
|
||||
# Primary user facing options
|
||||
option(MANIFOLD_DEBUG "Enable debug tracing/timing" OFF)
|
||||
option(MANIFOLD_USE_CUDA "Enable GPU support via CUDA" OFF)
|
||||
option(MANIFOLD_PYBIND "Build python bindings" ON)
|
||||
option(MANIFOLD_CBIND "Build C (FFI) bindings" OFF)
|
||||
set(MANIFOLD_PAR "NONE" CACHE STRING "Parallel backend, either \"TBB\" or \"OpenMP\" or \"NONE\"")
|
||||
set(MANIFOLD_FLAGS -O3)
|
||||
option(MANIFOLD_ASSERT "Enable assertions - requires MANIFOLD_DEBUG" OFF)
|
||||
option(MANIFOLD_TIMING "Enable Boolean3 phase timing without debug overhead" OFF)
|
||||
option(MANIFOLD_STRICT "Treat compile warnings as fatal build errors" OFF)
|
||||
option(
|
||||
MANIFOLD_NO_IOSTREAM
|
||||
"Strip iostream- and filesystem-using bits from the public API and tests; useful for freestanding/embedded builds. When ON, defines both MANIFOLD_NO_IOSTREAM and MANIFOLD_NO_FILESYSTEM."
|
||||
OFF
|
||||
)
|
||||
option(
|
||||
MANIFOLD_DOWNLOADS
|
||||
"Allow Manifold build to download missing dependencies"
|
||||
ON
|
||||
)
|
||||
option(MANIFOLD_PAR "Enable Parallel backend" OFF)
|
||||
option(
|
||||
MANIFOLD_OPTIMIZED
|
||||
"Force optimized build, even with debugging enabled"
|
||||
OFF
|
||||
)
|
||||
option(MANIFOLD_TEST "Enable testing suite" ON)
|
||||
option(MANIFOLD_PYBIND "Build python bindings" OFF)
|
||||
option(MANIFOLD_CBIND "Build C (FFI) bindings" ON)
|
||||
# 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_NANOBIND "Use builtin nanobind" OFF)
|
||||
|
||||
# default to Release build
|
||||
option(CMAKE_BUILD_TYPE "Build type" Release)
|
||||
# default to building shared library
|
||||
option(BUILD_SHARED_LIBS "Build shared library" ON)
|
||||
# Set some option values in the CMake cache
|
||||
set(MANIFOLD_FLAGS "" CACHE STRING "Manifold compiler flags")
|
||||
|
||||
# Development options
|
||||
option(TRACY_ENABLE "Use tracy profiling" OFF)
|
||||
option(TRACY_MEMORY_USAGE "Track memory allocation with tracy (expensive)" OFF)
|
||||
option(MANIFOLD_FUZZ "Enable fuzzing tests" OFF)
|
||||
option(ASSIMP_ENABLE "Enable ASSIMP for utilities in extras" OFF)
|
||||
mark_as_advanced(TRACY_ENABLE)
|
||||
mark_as_advanced(TRACY_MEMORY_USAGE)
|
||||
mark_as_advanced(MANIFOLD_FUZZ)
|
||||
mark_as_advanced(ASSIMP_ENABLE)
|
||||
|
||||
# MANIFOLD_NO_IOSTREAM strips iostream- and filesystem-using bits
|
||||
# from the public API and from the test files that use them.
|
||||
# Incompatible with options that emit diagnostic output via iostream:
|
||||
# MANIFOLD_DEBUG and MANIFOLD_TIMING (both use std::cout).
|
||||
# MANIFOLD_TEST=ON is supported — iostream-using TEST(...) blocks
|
||||
# in manifold_test/polygon_test/manifoldc_test are gated out under
|
||||
# the macro; the test executable still builds and runs the rest.
|
||||
if(MANIFOLD_NO_IOSTREAM AND (MANIFOLD_DEBUG OR MANIFOLD_TIMING))
|
||||
message(FATAL_ERROR
|
||||
"MANIFOLD_NO_IOSTREAM is incompatible with MANIFOLD_DEBUG / MANIFOLD_TIMING "
|
||||
"(those options use std::cout for diagnostic output).")
|
||||
endif()
|
||||
|
||||
# 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)
|
||||
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)
|
||||
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()
|
||||
|
||||
option(PYBIND11_FINDPYTHON "Enable PyBind to perform FindPython for you" ON)
|
||||
|
||||
option(BUILD_TEST_CGAL "Build CGAL performance comparisons" OFF)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build dynamic/shared libraries" OFF)
|
||||
set(PYBIND11_DIR ${PROJECT_SOURCE_DIR}/bindings/python/third_party/pybind11)
|
||||
set(THRUST_INC_DIR ${PROJECT_SOURCE_DIR}/src/third_party/thrust)
|
||||
|
||||
if(MANIFOLD_CBIND)
|
||||
set(MANIFOLD_EXPORT ON)
|
||||
if(CMAKE_EXPORT_COMPILE_COMMANDS AND NOT EMSCRIPTEN)
|
||||
# for nixos
|
||||
set(
|
||||
CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
|
||||
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BUILD_SHARED_LIBS OR MANIFOLD_CBIND)
|
||||
# Allow shared libraries to be relocatable (add Place Independent Code flag).
|
||||
# Also include when statically linking C bindings to avoid issues with bundling
|
||||
# artefacts in host languages using them for FFI.
|
||||
add_compile_options(-fPIC)
|
||||
if(MSVC)
|
||||
list(APPEND MANIFOLD_FLAGS /DNOMINMAX /bigobj /fp:precise)
|
||||
if(MANIFOLD_STRICT)
|
||||
list(APPEND MANIFOLD_FLAGS /WX)
|
||||
endif()
|
||||
else()
|
||||
list(
|
||||
APPEND
|
||||
WARNING_FLAGS
|
||||
-Wall
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-unused
|
||||
-Wno-shorten-64-to-32
|
||||
-Wno-sign-compare
|
||||
-Wno-alloc-size-larger-than
|
||||
)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
list(APPEND WARNING_FLAGS -Wno-format)
|
||||
endif()
|
||||
elseif(PROJECT_IS_TOP_LEVEL)
|
||||
# only do -Werror if we are the top level project and
|
||||
# MANIFOLD_STRICT is on
|
||||
if(MANIFOLD_STRICT)
|
||||
list(APPEND WARNING_FLAGS -Werror)
|
||||
endif()
|
||||
endif()
|
||||
list(APPEND MANIFOLD_FLAGS ${WARNING_FLAGS})
|
||||
if(
|
||||
MANIFOLD_OPTIMIZED
|
||||
OR "${CMAKE_BUILD_TYPE}" STREQUAL "Release"
|
||||
OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"
|
||||
)
|
||||
list(APPEND MANIFOLD_FLAGS -O3)
|
||||
endif()
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||
list(APPEND MANIFOLD_FLAGS -fno-omit-frame-pointer)
|
||||
endif()
|
||||
# disable fp math optimizations, e.g. FMA for supported architectures, as
|
||||
# this changes floating-point results
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-ffp-contract=off HAS_FP_CONTRACT)
|
||||
check_cxx_compiler_flag(-fexcess-precision=standard HAS_FP_PRECISION)
|
||||
if(HAS_FP_CONTRACT)
|
||||
list(APPEND MANIFOLD_FLAGS -ffp-contract=off)
|
||||
endif()
|
||||
if(HAS_FP_PRECISION)
|
||||
list(APPEND MANIFOLD_FLAGS -fexcess-precision=standard)
|
||||
endif()
|
||||
if(CODE_COVERAGE)
|
||||
list(
|
||||
APPEND
|
||||
MANIFOLD_FLAGS
|
||||
-coverage
|
||||
-fno-inline-small-functions
|
||||
-fkeep-inline-functions
|
||||
-fkeep-static-functions
|
||||
)
|
||||
add_link_options(-coverage)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MANIFOLD_USE_CUDA)
|
||||
enable_language(CUDA)
|
||||
find_package(CUDA REQUIRED)
|
||||
# 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")
|
||||
|
||||
# we cannot set THRUST_INC_DIR when building with CUDA, otherwise the
|
||||
# compiler will not use the system CUDA headers which causes incompatibility
|
||||
# clear THRUST_INC_DIR, we use the one from nvcc
|
||||
set(THRUST_INC_DIR "")
|
||||
set(MANIFOLD_NVCC_RELEASE_FLAGS -O3 -lineinfo)
|
||||
set(MANIFOLD_NVCC_DEBUG_FLAGS -G)
|
||||
set(MANIFOLD_NVCC_FLAGS -Xcudafe --diag_suppress=esa_on_defaulted_function_ignored --extended-lambda --expt-relaxed-constexpr
|
||||
"$<$<CONFIG:RELEASE>:${MANIFOLD_NVCC_RELEASE_FLAGS}>"
|
||||
"$<$<CONFIG:DEBUG>:${MANIFOLD_NVCC_DEBUG_FLAGS}>")
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
set(WARNING_FLAGS -Werror -Wall -Wno-sign-compare -Wno-unused)
|
||||
add_compile_options(
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${WARNING_FLAGS}>"
|
||||
"$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=${WARNING_FLAGS}>")
|
||||
endif()
|
||||
|
||||
if(CODE_COVERAGE AND NOT MSVC)
|
||||
set(COVERAGE_FLAGS -coverage -fno-inline-small-functions -fkeep-inline-functions -fkeep-static-functions)
|
||||
add_compile_options(
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${COVERAGE_FLAGS}>"
|
||||
"$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=-coverage>")
|
||||
add_link_options("-coverage")
|
||||
endif()
|
||||
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
|
||||
)
|
||||
|
||||
# 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
|
||||
math.h
|
||||
mesh.h
|
||||
linalg.h
|
||||
manifold.h
|
||||
optional_assert.h
|
||||
polygon.h
|
||||
vec_view.h
|
||||
cross_section.h
|
||||
)
|
||||
list(TRANSFORM MANIFOLD_PUBLIC_HDRS PREPEND include/manifold/)
|
||||
list(
|
||||
APPEND
|
||||
MANIFOLD_PUBLIC_HDRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/manifold/version.h
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${MANIFOLD_PUBLIC_HDRS}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/manifold
|
||||
)
|
||||
|
||||
# PkgConfig file
|
||||
if(MANIFOLD_PAR)
|
||||
set(TEMPLATE_OPTIONAL_TBB "tbb")
|
||||
endif()
|
||||
configure_file(
|
||||
cmake/manifold.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifold.pc
|
||||
@ONLY
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/manifold.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
@@ -3,19 +3,6 @@
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement (CLA). You (or your employer) retain the copyright to your
|
||||
contribution; this simply gives us permission to use and redistribute your
|
||||
contributions as part of the project. Head over to
|
||||
<https://cla.developers.google.com/> to see your current agreements on file or
|
||||
to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code Reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
@@ -26,4 +13,4 @@ information on using pull requests.
|
||||
## Community Guidelines
|
||||
|
||||
This project follows
|
||||
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
||||
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
||||
|
||||
@@ -1,47 +1,106 @@
|
||||
# About Manifold
|
||||
|
||||
[](https://codecov.io/github/elalish/manifold)
|
||||
[](https://badge.fury.io/py/manifold3d)
|
||||
[](https://badge.fury.io/js/manifold-3d)
|
||||
[](https://twitter.com/intent/follow?screen_name=manifoldcad)
|
||||
|
||||
[**C++ Documentation**](https://manifoldcad.org/docs/html/classmanifold_1_1_manifold.html) | [**ManifoldCAD User Guide**](https://manifoldcad.org/docs/jsuser/) | [**JS/TS/WASM API**](https://manifoldcad.org/docs/jsapi/) | [**Algorithm Documentation**](https://github.com/elalish/manifold/wiki/Manifold-Library) | [**Blog Posts**](https://elalish.blogspot.com/search/label/Manifold) | [**Web Examples**](https://manifoldcad.org/model-viewer.html)
|
||||
|
||||
[Manifold](https://github.com/elalish/manifold) is a geometry library dedicated to creating and operating on manifold triangle meshes. A [manifold mesh](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness) is a mesh that represents a solid object, and so is very important in manufacturing, CAD, structural analysis, etc. Manifold also supports arbitrary vertex properties and enables mapping of materials for rendering use-cases. Our primary goal is reliability: guaranteed manifold output without caveats or edge cases. Our secondary goal is performance: efficient algorithms that make extensive use of parallelization, or pipelining when only a single thread is available.
|
||||
|
||||
## Users
|
||||
|
||||
[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) | [SKÅPA](https://skapa.build) |
|
||||
| [Cadova](https://github.com/tomasf/Cadova) | [BREP.io](https://github.com/mmiscool/BREP) | [Otterplans](https://otterplans.com) |
|
||||
| [Bracket Engineer](https://bracket.engineer) | [Nodillo](https://nodillo3d.com) | [CaDoodle CAD](https://cadoodlecad.com/) |
|
||||
| [Bridge Designer](https://www.asce.org/career-growth/pre-college-outreach/bridge-designer) |[AdaShape](https://adashape.com)| [PyVista](https://github.com/pyvista/pyvista-manifold) |
|
||||
| [NASSCAD](https://www.nasscad.com) — Free browser-based parametric CAD in a single HTML file. Boolean CSG, 18 watertight primitives, offline-ready. ([GitHub](https://github.com/Nx-Nass/Nasscad_4.2.7)) | [SolidSKeleton](https://github.com/FerroIT/SolidSKeleton) | [Piecad (Python-based)](https://github.com/briansturgill/Piecad) |
|
||||
|
||||
<img src="https://elalish.github.io/manifold/samples/models/perfSpheres.png" width="350px"/>
|
||||
### Bindings & Packages
|
||||
|
||||
## [ManifoldCAD.org](https://manifoldcad.org)
|
||||
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). There is also a native Rust port, [manifold-rust](https://github.com/larsbrubaker/manifold-rust), which passes Manifold's full test suite with identical results and has no C++ dependency — from a user's perspective it works like a binding.
|
||||
|
||||
If you like OpenSCAD / JSCAD, you might also like ManifoldCAD - our own solid modelling web app. Our WASM is not GPU-accelerated, but it's still quite fast and a good way to test out our Manifold library.
|
||||
| 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 | Maven | [manifold3d](https://github.com/CommonWealthRobotics/manifold3d-java/blob/development/bindings/java/README.md) | 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 |
|
||||
| Rust | crates.io | [manifold-csg](https://github.com/zmerlynn/manifold-csg) | external |
|
||||
| Rust (native port) | crates.io | [manifold-rust](https://crates.io/crates/manifold-rust) | external |
|
||||
| Swift | SPM | [Manifold-Swift](https://github.com/tomasf/manifold-swift) | external |
|
||||
|
||||

|
||||
## Frontend Sandboxes
|
||||
|
||||
# Manifold
|
||||
[ManifoldCAD.org]: https://manifoldcad.org
|
||||
[Python Colab Example]: https://colab.research.google.com/drive/1VxrFYHPSHZgUbl9TeWzCeovlpXrPQ5J5?usp=sharing
|
||||
|
||||
[**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)
|
||||
### [ManifoldCAD.org]
|
||||
|
||||
[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).
|
||||
If you like OpenSCAD / JSCAD, you might also like [ManifoldCAD][ManifoldCAD.org] - our own solid modelling web app where you script in JS/TS. This uses our npm package, [manifold-3d](https://www.npmjs.com/package/manifold-3d), built via WASM. It's not quite as fast as our raw C++, but it's hard to beat for interoperability.
|
||||
|
||||
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.
|
||||
### [Python Colab Example]
|
||||
|
||||
We have four core dependencies, making use of submodules to ensure compatibility:
|
||||
- `graphlite`: connected components algorithm
|
||||
- `Clipper2`: provides our 2D subsystem
|
||||
- `GLM`: a compact vector library
|
||||
- `Thrust`: Nvidia's parallel algorithms library (basically a superset of C++17 std::parallel_algorithms)
|
||||
If you prefer Python to JS/TS, make your own copy of [the notebook][Python Colab Example]. It demonstrates interop between our [`manifold3d`](https://pypi.org/project/manifold3d/) PyPI library and the popular [`trimesh`](https://pypi.org/project/trimesh/) library, including showing the interactive model right in the notebook and saving 3D model output.
|
||||
|
||||
## 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.
|
||||
## Manifold Library
|
||||
|
||||
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 CUDA, OMP, 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 for CUDA, the code will still run without a GPU, falling back to the serial version of the algorithms. 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.
|
||||
|
||||
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.
|
||||
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 bear 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 |
|
||||
| [`Nanobind`](https://github.com/wjakob/nanobind) | `MANIFOLD_PYBIND=ON` | Python bindings |
|
||||
| [`Emscripten`](https://github.com/emscripten-core/emscripten) | `MANIFOLD_JSBIND=ON` | JS bindings via WASM |
|
||||
| [`GTest`](https://github.com/google/googletest/) | `MANIFOLD_TEST=ON` | Testing framework |
|
||||
| [`Assimp`](https://github.com/assimp/assimp) | `ASSIMP_ENABLE=ON` | Utilities in `extras` |
|
||||
| [`Tracy`](https://github.com/wolfpld/tracy) | `TRACY_ENABLE=ON` | Performance analysis |
|
||||
|
||||
|
||||
### 3D Formats
|
||||
|
||||
Please avoid saving to STL files! They are lossy and inefficient - when saving a manifold mesh to STL there is no guarantee that the re-imported mesh will still be manifold, as the topology is lost. Please consider using [3MF](https://3mf.io/) instead, as this format was designed from the beginning for manifold meshes representing solid objects.
|
||||
|
||||
If you use vertex properties for things like interpolated normals or texture UV coordinates, [glTF](https://www.khronos.org/Gltf) is recommended, specifically using the [`EXT_mesh_manifold`](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_mesh_manifold/README.md) extension. This allows for the lossless and efficient transmission of manifoldness even with property boundaries. Try our [make-manifold](https://manifoldcad.org/make-manifold) page to add this extension to your existing glTF/GLB models.
|
||||
|
||||
Manifold provides high precision OBJ file IO, but it is limited in functionality and is primarily to aid in testing. If you are using our npm module, we have a much more capable [gltf-io.ts](https://github.com/elalish/manifold/blob/master/bindings/wasm/examples/gltf-io.ts) you can use instead. For other languages we strongly recommend using existing packages that focus on 3D file I/O, e.g. [trimesh](https://trimesh.org/) for Python, particularly when using vertex properties or materials.
|
||||
Example for integrating with [Assimp](https://github.com/assimp/assimp) is in `extras/meshIO.cpp`, which is used by files such as `extras/convert_file.cpp`.
|
||||
|
||||
## Building
|
||||
|
||||
Only CMake, a C++ compiler, and Python are required to be installed and set up to build this library (it has been tested with GCC, LLVM, MSVC). However, a variety of optional dependencies can bring in more functionality, see below.
|
||||
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.
|
||||
|
||||
Build and test (Ubuntu or similar):
|
||||
```
|
||||
@@ -50,19 +109,79 @@ 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_USE_CUDA=ON`):
|
||||
- `MANIFOLD_USE_CUDA=[<OFF>, ON]`: Provides Nvidia GPU parallelization, requires CUDA Toolkit and its nvcc compiler.
|
||||
- `MANIFOLD_PAR=[<NONE>, OMP, TBB]`: Provides multi-thread parallelization, requires `libomp-dev` or `libtbb-dev`.
|
||||
- `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.
|
||||
- `BUILD_TEST_CGAL=[<OFF>, ON]`: Builds a CGAL-based performance [comparison](https://github.com/elalish/manifold/tree/master/extras), requires `libcgal-dev`.
|
||||
CMake flags (usage e.g. `-DMANIFOLD_DEBUG=ON`):
|
||||
- `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, requires `nanobind`.
|
||||
- `MANIFOLD_PAR=[<OFF>, ON]`: Enables multi-thread parallelization, requires `tbb`.
|
||||
- `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.
|
||||
- `ASSIMP_ENABLE=[<OFF>, ON]`: Enable integration with assimp, which is needed for some of the utilities in `extras`.
|
||||
- `MANIFOLD_STRICT=[<OFF>, ON]`: Treat compile warnings as fatal build errors.
|
||||
- `MANIFOLD_NO_IOSTREAM=[<OFF>, ON]`: Strip iostream- and filesystem-using
|
||||
bits from the public API and tests; useful for freestanding/embedded
|
||||
builds (e.g., `wasm32-unknown-unknown`). Defines both
|
||||
`MANIFOLD_NO_IOSTREAM` and `MANIFOLD_NO_FILESYSTEM` as PUBLIC compile
|
||||
definitions. The test suite still builds + runs — iostream-using
|
||||
TESTs in `manifold_test`/`polygon_test`/`manifoldc_test` are gated
|
||||
out under the macro. Incompatible with `MANIFOLD_DEBUG` /
|
||||
`MANIFOLD_TIMING` (which use `std::cout` for diagnostic output).
|
||||
|
||||
Dependency version override:
|
||||
- `MANIFOLD_USE_BUILTIN_TBB=[<OFF>, ON]`: Use builtin version of tbb.
|
||||
- `MANIFOLD_USE_BUILTIN_NANOBIND=[<OFF>, ON]`: Use builtin version of nanobind.
|
||||
|
||||
> Note: These two 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_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.
|
||||
|
||||
#### Sanitizer CI Reproduction
|
||||
|
||||
To reproduce the sanitizer CI test locally from repo root:
|
||||
|
||||
```bash
|
||||
bash ./scripts/ci/sanitizer_build.sh
|
||||
source ./scripts/sanitizer_cases.sh
|
||||
ASAN_OPTIONS=detect_container_overflow=0:strict_init_order=1 \
|
||||
UBSAN_OPTIONS=print_stacktrace=1 \
|
||||
./build/test/manifold_test --gtest_filter="${SANITIZER_GTEST_FILTER_CORE}"
|
||||
```
|
||||
|
||||
### 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):
|
||||
@@ -84,19 +203,20 @@ 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
|
||||
|
||||
The CMake script will build the python binding `pymanifold` automatically. To
|
||||
use the extension, please add `$BUILD_DIR/tools` to your `PYTHONPATH`, where
|
||||
The CMake script will build the python binding `manifold3d` automatically. To
|
||||
use the extension, please add `$BUILD_DIR/bindings/python` to your `PYTHONPATH`, where
|
||||
`$BUILD_DIR` is the build directory for CMake. Examples using the python binding
|
||||
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
|
||||
```
|
||||
|
||||
@@ -104,16 +224,52 @@ Run the following code in the interpreter for
|
||||
python binding documentation:
|
||||
|
||||
```
|
||||
>>> import pymanifold
|
||||
>>> help(pymanifold)
|
||||
>>> import manifold3d
|
||||
>>> help(manifold3d)
|
||||
```
|
||||
|
||||
For more detailed documentation, please refer to the C++ API.
|
||||
|
||||
### Windows Shenanigans
|
||||
|
||||
Windows users should build with `-DBUILD_SHARED_LIBS=OFF`, as enabling shared
|
||||
libraries in general makes things very complicated.
|
||||
|
||||
The DLL file for manifoldc (C FFI bindings) when built with msvc is in `${CMAKE_BINARY_DIR}/bin/${BUILD_TYPE}/manifoldc.dll`.
|
||||
For example, for the following command, the path relative to the project root directory is `build/bin/Release/manifoldc.dll`.
|
||||
```sh
|
||||
cmake . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DMANIFOLD_DEBUG=ON -DMANIFOLD_PAR=${{matrix.parallel_backend}} -A x64 -B build
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! A lower barrier contribution is to simply make a PR that adds a test, especially if it repros an issue you've found. Simply name it prepended with DISABLED_, so that it passes the CI. That will be a very strong signal to me to fix your issue. However, if you know how to fix it yourself, then including the fix in your PR would be much appreciated!
|
||||
|
||||
### Formatting
|
||||
|
||||
There is a formatting script `format.sh` that automatically formats everything.
|
||||
It requires clang-format, black formatter for python and [gersemi](https://github.com/BlankSpruce/gersemi) for formatting cmake files.
|
||||
|
||||
Note that our script can run with clang-format older than 18, but the GitHub
|
||||
action check may fail due to slight differences between different versions of
|
||||
clang-format. In that case, either update your clang-format version or apply the
|
||||
patch from the GitHub action log.
|
||||
|
||||
### Profiling
|
||||
|
||||
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).
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
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. Run the Prepare release workflow from the Actions tab with the new version number. It sets the version in CMakeLists.txt, pyproject.toml, flake.nix, package.json and test-cmake.sh, refreshes the package-lock files, and opens a PR.
|
||||
1. Verify tests pass on that PR, 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. See [Deploying manually](#deploying-manually) for the site deploy in particular.
|
||||
|
||||
## Where the site is published
|
||||
|
||||
`manifoldcad.org` is served from the `gh-pages` branch, which holds several
|
||||
builds side by side:
|
||||
|
||||
| Path | Updated | Contents |
|
||||
| --- | --- | --- |
|
||||
| `/` (root) | each published release | the current release, served at manifoldcad.org |
|
||||
| `alpha/` | every master commit that passes CI | unreleased build, for testing |
|
||||
| `vX.Y.Z/` | once, when that release is published | a frozen snapshot of that version |
|
||||
|
||||
Each is a complete copy of the site: the ManifoldCAD editor, the examples,
|
||||
the benchmark dashboard, and both the C++ and TypeScript docs. A release
|
||||
publishes twice, once to its own `vX.Y.Z/` directory and once to the root,
|
||||
so manifoldcad.org always serves the current release directly.
|
||||
|
||||
Subdirectory deploys erase their target before writing, so each holds exactly
|
||||
one build. The root deploy cannot, because clearing it would take the
|
||||
subdirectories with it, so there it overwrites in place instead.
|
||||
|
||||
## Deploying manually
|
||||
|
||||
The Deploy documentation workflow can be run by hand from the Actions tab for
|
||||
a quick fix or a docs-only update, without cutting a release. It takes two
|
||||
inputs:
|
||||
|
||||
- **ref** - the branch or tag to build from, e.g. `v3.4.0`
|
||||
- **destination_dir** - the directory on `gh-pages` to publish into, e.g. `v3.4.0`
|
||||
|
||||
The target directory is erased and rebuilt, so the result is exactly what the
|
||||
chosen ref produces. Nothing outside that directory is affected.
|
||||
|
||||
Keep `ref` and `destination_dir` describing the same version. Publishing
|
||||
master into `v3.4.0/` would leave that directory claiming to be a release it
|
||||
is not.
|
||||
|
||||
Typical uses:
|
||||
|
||||
- **Fix a published version.** Branch from its tag, commit the fix, then
|
||||
deploy that branch into the version's own directory. Useful for correcting
|
||||
documentation without a patch release.
|
||||
- **Backfill an old release.** Deploy its tag into a matching directory so the
|
||||
version has a browsable copy of the site.
|
||||
- **Preview a branch.** Deploy it somewhere disposable rather than over a
|
||||
directory that matters.
|
||||
|
||||
Builds from before the versioned layout existed assume they are served from
|
||||
the site root and will load the wrong assets from a subdirectory. Deploying
|
||||
such a tag needs the relative-path fix applied on top first.
|
||||
@@ -12,14 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
if(NOT EMSCRIPTEN AND MANIFOLD_CBIND)
|
||||
add_subdirectory(c)
|
||||
if(MANIFOLD_CBIND)
|
||||
add_subdirectory(c)
|
||||
endif()
|
||||
|
||||
if(NOT EMSCRIPTEN AND MANIFOLD_PYBIND)
|
||||
add_subdirectory(python)
|
||||
add_subdirectory(python)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
add_subdirectory(wasm)
|
||||
if(EMSCRIPTEN AND MANIFOLD_JSBIND)
|
||||
add_subdirectory(wasm)
|
||||
endif()
|
||||
|
||||
@@ -1,18 +1,45 @@
|
||||
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(manifoldc manifoldc.cpp conv.cpp)
|
||||
|
||||
if(MANIFOLD_USE_CUDA)
|
||||
set_source_files_properties(manifoldc.cpp conv.cpp PROPERTIES LANGUAGE CUDA)
|
||||
set_property(TARGET manifoldc PROPERTY CUDA_ARCHITECTURES 61)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
manifoldc
|
||||
PRIVATE manifold sdf meshIO graphlite cross_section
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include
|
||||
)
|
||||
|
||||
target_include_directories(manifoldc PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||
add_library(
|
||||
manifoldc
|
||||
manifoldc.cpp
|
||||
conv.cpp
|
||||
box.cpp
|
||||
cross.cpp
|
||||
rect.cpp
|
||||
)
|
||||
exportlib(manifoldc)
|
||||
|
||||
target_link_libraries(manifoldc PRIVATE manifold)
|
||||
|
||||
target_include_directories(
|
||||
manifoldc
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
)
|
||||
target_compile_options(manifoldc PRIVATE ${MANIFOLD_FLAGS})
|
||||
target_compile_features(manifoldc PRIVATE cxx_std_17)
|
||||
|
||||
install(TARGETS manifoldc EXPORT manifoldTargets)
|
||||
install(
|
||||
FILES include/manifold/manifoldc.h include/manifold/types.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/manifold
|
||||
)
|
||||
|
||||
@@ -1,85 +1,103 @@
|
||||
#include <conv.h>
|
||||
#include <manifold.h>
|
||||
#include <public.h>
|
||||
// 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 "types.h"
|
||||
#include "conv.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) {
|
||||
auto box = *from_c(b);
|
||||
auto p = glm::vec3(x, y, z);
|
||||
void manifold_box_include_pt(ManifoldBox* b, double x, double y, double z) {
|
||||
auto& box = *from_c(b);
|
||||
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
|
||||
|
||||
@@ -1,49 +1,64 @@
|
||||
#include <conv.h>
|
||||
#include <manifold.h>
|
||||
#include <meshIO.h>
|
||||
#include <sdf.h>
|
||||
// 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 <vector>
|
||||
|
||||
#include "cross_section.h"
|
||||
#include "include/types.h"
|
||||
#include "public.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);
|
||||
ManifoldRayHitVec* to_c(RayHitVec* v) {
|
||||
return reinterpret_cast<ManifoldRayHitVec*>(v);
|
||||
}
|
||||
|
||||
ManifoldPolygons *to_c(manifold::Polygons *m) {
|
||||
return reinterpret_cast<ManifoldPolygons *>(m);
|
||||
ManifoldExecutionContext* to_c(ExecutionContext* ctx) {
|
||||
return reinterpret_cast<ManifoldExecutionContext*>(ctx);
|
||||
}
|
||||
|
||||
ManifoldMesh *to_c(manifold::Mesh *m) {
|
||||
return reinterpret_cast<ManifoldMesh *>(m);
|
||||
ManifoldSimplePolygon* to_c(manifold::SimplePolygon* m) {
|
||||
return reinterpret_cast<ManifoldSimplePolygon*>(m);
|
||||
}
|
||||
|
||||
ManifoldMeshGL *to_c(manifold::MeshGL *m) {
|
||||
return reinterpret_cast<ManifoldMeshGL *>(m);
|
||||
ManifoldPolygons* to_c(manifold::Polygons* m) {
|
||||
return reinterpret_cast<ManifoldPolygons*>(m);
|
||||
}
|
||||
|
||||
ManifoldCurvature *to_c(manifold::Curvature *m) {
|
||||
return reinterpret_cast<ManifoldCurvature *>(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) {
|
||||
@@ -99,70 +114,75 @@ 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;
|
||||
case Manifold::Error::InvalidTangents:
|
||||
e = MANIFOLD_INVALID_TANGENTS;
|
||||
break;
|
||||
case Manifold::Error::Cancelled:
|
||||
e = MANIFOLD_CANCELLED;
|
||||
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);
|
||||
}
|
||||
|
||||
ManifoldMaterial *to_c(manifold::Material *m) {
|
||||
return reinterpret_cast<ManifoldMaterial *>(m);
|
||||
ManifoldVec2 to_c(vec2 v) { return {v.x, v.y}; }
|
||||
|
||||
ManifoldVec3 to_c(vec3 v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
ManifoldIVec3 to_c(ivec3 v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
ManifoldTriangulation* to_c(std::vector<ivec3>* m) {
|
||||
return reinterpret_cast<ManifoldTriangulation*>(m);
|
||||
}
|
||||
|
||||
ManifoldExportOptions *to_c(manifold::ExportOptions *m) {
|
||||
return reinterpret_cast<ManifoldExportOptions *>(m);
|
||||
manifold::Manifold* from_c(ManifoldManifold* m) {
|
||||
return reinterpret_cast<manifold::Manifold*>(m);
|
||||
}
|
||||
|
||||
ManifoldVec2 to_c(glm::vec2 v) { return {v.x, v.y}; }
|
||||
|
||||
ManifoldVec3 to_c(glm::vec3 v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
ManifoldIVec3 to_c(glm::ivec3 v) { return {v.x, v.y, v.z}; }
|
||||
|
||||
ManifoldProperties to_c(manifold::Properties p) {
|
||||
return {p.surfaceArea, p.volume};
|
||||
ManifoldVec* from_c(ManifoldManifoldVec* ms) {
|
||||
return reinterpret_cast<ManifoldVec*>(ms);
|
||||
}
|
||||
|
||||
const manifold::Manifold *from_c(ManifoldManifold *m) {
|
||||
return reinterpret_cast<manifold::Manifold const *>(m);
|
||||
manifold::CrossSection* from_c(ManifoldCrossSection* cs) {
|
||||
return reinterpret_cast<manifold::CrossSection*>(cs);
|
||||
}
|
||||
|
||||
ManifoldVec *from_c(ManifoldManifoldVec *ms) {
|
||||
return reinterpret_cast<ManifoldVec *>(ms);
|
||||
CrossSectionVec* from_c(ManifoldCrossSectionVec* csv) {
|
||||
return reinterpret_cast<CrossSectionVec*>(csv);
|
||||
}
|
||||
|
||||
const manifold::CrossSection *from_c(ManifoldCrossSection *cs) {
|
||||
return reinterpret_cast<manifold::CrossSection *>(cs);
|
||||
RayHitVec* from_c(ManifoldRayHitVec* v) {
|
||||
return reinterpret_cast<RayHitVec*>(v);
|
||||
}
|
||||
|
||||
CrossSectionVec *from_c(ManifoldCrossSectionVec *csv) {
|
||||
return reinterpret_cast<CrossSectionVec *>(csv);
|
||||
ExecutionContext* from_c(ManifoldExecutionContext* ctx) {
|
||||
return reinterpret_cast<ExecutionContext*>(ctx);
|
||||
}
|
||||
|
||||
const manifold::SimplePolygon *from_c(ManifoldSimplePolygon *m) {
|
||||
return reinterpret_cast<manifold::SimplePolygon const *>(m);
|
||||
manifold::SimplePolygon* from_c(ManifoldSimplePolygon* m) {
|
||||
return reinterpret_cast<manifold::SimplePolygon*>(m);
|
||||
}
|
||||
|
||||
const manifold::Polygons *from_c(ManifoldPolygons *m) {
|
||||
return reinterpret_cast<manifold::Polygons const *>(m);
|
||||
manifold::Polygons* from_c(ManifoldPolygons* m) {
|
||||
return reinterpret_cast<manifold::Polygons*>(m);
|
||||
}
|
||||
|
||||
const manifold::Mesh *from_c(ManifoldMesh *m) {
|
||||
return reinterpret_cast<manifold::Mesh const *>(m);
|
||||
manifold::MeshGL* from_c(ManifoldMeshGL* m) {
|
||||
return reinterpret_cast<manifold::MeshGL*>(m);
|
||||
}
|
||||
|
||||
const manifold::MeshGL *from_c(ManifoldMeshGL *m) {
|
||||
return reinterpret_cast<manifold::MeshGL const *>(m);
|
||||
}
|
||||
|
||||
const manifold::Curvature *from_c(ManifoldCurvature *c) {
|
||||
return reinterpret_cast<manifold::Curvature const *>(c);
|
||||
manifold::MeshGL64* from_c(ManifoldMeshGL64* m) {
|
||||
return reinterpret_cast<manifold::MeshGL64*>(m);
|
||||
}
|
||||
|
||||
OpType from_c(ManifoldOpType optype) {
|
||||
@@ -180,24 +200,6 @@ OpType from_c(ManifoldOpType optype) {
|
||||
return op;
|
||||
}
|
||||
|
||||
CrossSection::FillRule from_c(ManifoldFillRule fillrule) {
|
||||
auto fr = CrossSection::FillRule::EvenOdd;
|
||||
switch (fillrule) {
|
||||
case MANIFOLD_FILL_RULE_EVEN_ODD:
|
||||
break;
|
||||
case MANIFOLD_FILL_RULE_NON_ZERO:
|
||||
fr = CrossSection::FillRule::NonZero;
|
||||
break;
|
||||
case MANIFOLD_FILL_RULE_POSITIVE:
|
||||
fr = CrossSection::FillRule::Positive;
|
||||
break;
|
||||
case MANIFOLD_FILL_RULE_NEGATIVE:
|
||||
fr = CrossSection::FillRule::Negative;
|
||||
break;
|
||||
};
|
||||
return fr;
|
||||
}
|
||||
|
||||
CrossSection::JoinType from_c(ManifoldJoinType join_type) {
|
||||
auto jt = CrossSection::JoinType::Square;
|
||||
switch (join_type) {
|
||||
@@ -209,53 +211,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);
|
||||
manifold::Box* from_c(ManifoldBox* m) {
|
||||
return reinterpret_cast<manifold::Box*>(m);
|
||||
}
|
||||
|
||||
const manifold::Rect *from_c(ManifoldRect *m) {
|
||||
return reinterpret_cast<manifold::Rect const *>(m);
|
||||
manifold::Rect* from_c(ManifoldRect* m) {
|
||||
return reinterpret_cast<manifold::Rect*>(m);
|
||||
}
|
||||
|
||||
manifold::Material *from_c(ManifoldMaterial *mat) {
|
||||
return reinterpret_cast<manifold::Material *>(mat);
|
||||
vec2 from_c(ManifoldVec2 v) { return vec2(v.x, v.y); }
|
||||
|
||||
vec3 from_c(ManifoldVec3 v) { return vec3(v.x, v.y, v.z); }
|
||||
|
||||
ivec3 from_c(ManifoldIVec3 v) { return ivec3(v.x, v.y, v.z); }
|
||||
|
||||
vec4 from_c(ManifoldVec4 v) { return vec4(v.x, v.y, v.z, v.w); }
|
||||
|
||||
std::vector<ivec3>* from_c(ManifoldTriangulation* m) {
|
||||
return reinterpret_cast<std::vector<ivec3>*>(m);
|
||||
}
|
||||
|
||||
manifold::ExportOptions *from_c(ManifoldExportOptions *options) {
|
||||
return reinterpret_cast<manifold::ExportOptions *>(options);
|
||||
}
|
||||
|
||||
glm::vec2 from_c(ManifoldVec2 v) { return glm::vec2(v.x, v.y); }
|
||||
|
||||
glm::vec3 from_c(ManifoldVec3 v) { return glm::vec3(v.x, v.y, v.z); }
|
||||
|
||||
glm::ivec3 from_c(ManifoldIVec3 v) { return glm::ivec3(v.x, v.y, v.z); }
|
||||
|
||||
glm::vec4 from_c(ManifoldVec4 v) { return glm::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) {
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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>;
|
||||
using RayHitVec = std::vector<RayHit>;
|
||||
|
||||
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);
|
||||
ManifoldRayHitVec* to_c(RayHitVec* v);
|
||||
ManifoldExecutionContext* to_c(ExecutionContext* ctx);
|
||||
|
||||
manifold::Manifold* from_c(ManifoldManifold* m);
|
||||
ManifoldVec* from_c(ManifoldManifoldVec* ms);
|
||||
manifold::CrossSection* from_c(ManifoldCrossSection* cs);
|
||||
CrossSectionVec* from_c(ManifoldCrossSectionVec* csv);
|
||||
manifold::SimplePolygon* from_c(ManifoldSimplePolygon* m);
|
||||
manifold::Polygons* from_c(ManifoldPolygons* m);
|
||||
manifold::MeshGL* from_c(ManifoldMeshGL* m);
|
||||
manifold::MeshGL64* from_c(ManifoldMeshGL64* m);
|
||||
OpType from_c(ManifoldOpType op);
|
||||
CrossSection::JoinType from_c(ManifoldJoinType jt);
|
||||
manifold::Box* from_c(ManifoldBox* m);
|
||||
manifold::Rect* from_c(ManifoldRect* r);
|
||||
vec2 from_c(ManifoldVec2 v);
|
||||
vec3 from_c(ManifoldVec3 v);
|
||||
ivec3 from_c(ManifoldIVec3 v);
|
||||
vec4 from_c(ManifoldVec4 v);
|
||||
std::vector<ivec3>* from_c(ManifoldTriangulation* m);
|
||||
RayHitVec* from_c(ManifoldRayHitVec* v);
|
||||
ExecutionContext* from_c(ManifoldExecutionContext* ctx);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,216 +1,266 @@
|
||||
#include <conv.h>
|
||||
#include <manifold.h>
|
||||
#include <public.h>
|
||||
// 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 <vector>
|
||||
#include "conv.h"
|
||||
#include "manifold/common.h"
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/types.h"
|
||||
|
||||
#include "cross_section.h"
|
||||
#include "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) {
|
||||
return to_c(new (mem) CrossSection(*from_c(p), from_c(fr)));
|
||||
ManifoldCrossSection* manifold_cross_section_of_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* p) {
|
||||
return to_c(new (mem) CrossSection(*from_c(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)));
|
||||
ManifoldCrossSection* manifold_cross_section_of_polygons(void* mem,
|
||||
ManifoldPolygons* p) {
|
||||
return to_c(new (mem) CrossSection(*from_c(p)));
|
||||
}
|
||||
|
||||
ManifoldCrossSectionVec *manifold_cross_section_empty_vec(void *mem) {
|
||||
ManifoldCrossSection* manifold_cross_section_even_odd_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* p) {
|
||||
return to_c(new (mem) CrossSection(CrossSection::EvenOdd(*from_c(p))));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_even_odd_polygons(
|
||||
void* mem, ManifoldPolygons* p) {
|
||||
return to_c(new (mem) CrossSection(CrossSection::EvenOdd(*from_c(p))));
|
||||
}
|
||||
|
||||
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_rect_clip(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
ManifoldRect *r) {
|
||||
auto clipped = from_c(cs)->RectClip(*from_c(r));
|
||||
return to_c(new (mem) CrossSection(clipped));
|
||||
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_translate(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
float x, float y) {
|
||||
auto translated = from_c(cs)->Translate(glm::vec2(x, y));
|
||||
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) {
|
||||
auto hulled = CrossSection::Hull(*from_c(ps));
|
||||
return to_c(new (mem) CrossSection(hulled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_hull_polygons(
|
||||
void* mem, ManifoldPolygons* ps) {
|
||||
auto hulled = CrossSection::Hull(*from_c(ps));
|
||||
return to_c(new (mem) CrossSection(hulled));
|
||||
}
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_translate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y) {
|
||||
auto translated = from_c(cs)->Translate(vec2(x, y));
|
||||
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_context(
|
||||
void* mem, ManifoldCrossSection* cs,
|
||||
ManifoldVec2 (*fun)(double, double, void*), void* ctx) {
|
||||
// Bind function with context argument to one without
|
||||
using namespace std::placeholders;
|
||||
std::function<ManifoldVec2(double, double)> f2 = std::bind(fun, _1, _2, ctx);
|
||||
std::function<void(vec2 & v)> warp = [f2](vec2& v) {
|
||||
v = from_c(f2(v.x, v.y));
|
||||
};
|
||||
auto warped = from_c(cs)->Warp(warp);
|
||||
return to_c(new (mem) CrossSection(warped));
|
||||
}
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_simplify(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
double epsilon) {
|
||||
auto simplified = from_c(cs)->Simplify(epsilon);
|
||||
ManifoldCrossSection* manifold_cross_section_simplify(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double tolerance) {
|
||||
auto simplified = from_c(cs)->Simplify(tolerance);
|
||||
return to_c(new (mem) CrossSection(simplified));
|
||||
}
|
||||
|
||||
ManifoldCrossSection *manifold_cross_section_offset(
|
||||
void *mem, ManifoldCrossSection *cs, double delta, ManifoldJoinType jt,
|
||||
double miter_limit, double arc_tolerance) {
|
||||
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, arc_tolerance);
|
||||
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) {
|
||||
auto cs = CrossSection::Compose(*from_c(csv));
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
double manifold_cross_section_get_tolerance(ManifoldCrossSection* cs) {
|
||||
return from_c(cs)->GetTolerance();
|
||||
}
|
||||
|
||||
ManifoldCrossSectionVec *manifold_cross_section_decompose(
|
||||
void *mem, ManifoldCrossSection *cs) {
|
||||
ManifoldCrossSection* manifold_cross_section_set_tolerance(
|
||||
void* mem, ManifoldCrossSection* cs, double tolerance) {
|
||||
auto out = from_c(cs)->SetTolerance(tolerance);
|
||||
return to_c(new (mem) CrossSection(out));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
#include <manifold.h>
|
||||
#include <meshIO.h>
|
||||
#include <public.h>
|
||||
#include <sdf.h>
|
||||
#include <types.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cross_section.h"
|
||||
|
||||
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);
|
||||
ManifoldCurvature *to_c(manifold::Curvature *m);
|
||||
ManifoldError to_c(manifold::Manifold::Error error);
|
||||
ManifoldMaterial *to_c(manifold::Material *m);
|
||||
ManifoldExportOptions *to_c(manifold::ExportOptions *m);
|
||||
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);
|
||||
const manifold::Curvature *from_c(ManifoldCurvature *m);
|
||||
manifold::Material *from_c(ManifoldMaterial *mat);
|
||||
manifold::ExportOptions *from_c(ManifoldExportOptions *options);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,601 @@
|
||||
// Copyright 2023 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "manifold/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Polygons
|
||||
|
||||
ManifoldSimplePolygon* manifold_simple_polygon(void* mem, ManifoldVec2* ps,
|
||||
size_t length);
|
||||
ManifoldPolygons* manifold_polygons(void* mem, ManifoldSimplePolygon** ps,
|
||||
size_t length);
|
||||
size_t manifold_simple_polygon_length(ManifoldSimplePolygon* p);
|
||||
size_t manifold_polygons_length(ManifoldPolygons* ps);
|
||||
size_t manifold_polygons_simple_length(ManifoldPolygons* ps, size_t idx);
|
||||
ManifoldVec2 manifold_simple_polygon_get_point(ManifoldSimplePolygon* p,
|
||||
size_t idx);
|
||||
ManifoldSimplePolygon* manifold_polygons_get_simple(void* mem,
|
||||
ManifoldPolygons* ps,
|
||||
size_t idx);
|
||||
ManifoldVec2 manifold_polygons_get_point(ManifoldPolygons* ps,
|
||||
size_t simple_idx, size_t pt_idx);
|
||||
|
||||
// Mesh Construction
|
||||
|
||||
ManifoldMeshGL* manifold_meshgl(void* mem, float* vert_props, size_t n_verts,
|
||||
size_t n_props, uint32_t* tri_verts,
|
||||
size_t n_tris);
|
||||
|
||||
ManifoldMeshGL* manifold_meshgl_w_tangents(void* mem, float* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint32_t* tri_verts, size_t n_tris,
|
||||
float* halfedge_tangent);
|
||||
ManifoldMeshGL* manifold_meshgl_w_options(void* mem, float* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint32_t* tri_verts, size_t n_tris,
|
||||
ManifoldMeshGLOptions* options);
|
||||
ManifoldMeshGL* manifold_get_meshgl(void* mem, ManifoldManifold* m);
|
||||
ManifoldMeshGL* manifold_get_meshgl_w_normals(void* mem, ManifoldManifold* m,
|
||||
int32_t normalIdx);
|
||||
ManifoldMeshGL* manifold_meshgl_copy(void* mem, ManifoldMeshGL* m);
|
||||
ManifoldMeshGL* manifold_meshgl_merge(void* mem, ManifoldMeshGL* m);
|
||||
|
||||
ManifoldMeshGL64* manifold_meshgl64(void* mem, double* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint64_t* tri_verts, size_t n_tris);
|
||||
|
||||
ManifoldMeshGL64* manifold_meshgl64_w_tangents(void* mem, double* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint64_t* tri_verts,
|
||||
size_t n_tris,
|
||||
double* halfedge_tangent);
|
||||
ManifoldMeshGL64* manifold_meshgl64_w_options(void* mem, double* vert_props,
|
||||
size_t n_verts, size_t n_props,
|
||||
uint64_t* tri_verts,
|
||||
size_t n_tris,
|
||||
ManifoldMeshGL64Options* options);
|
||||
ManifoldMeshGL64* manifold_get_meshgl64(void* mem, ManifoldManifold* m);
|
||||
ManifoldMeshGL64* manifold_get_meshgl64_w_normals(void* mem,
|
||||
ManifoldManifold* m,
|
||||
int32_t normalIdx);
|
||||
ManifoldMeshGL64* manifold_meshgl64_copy(void* mem, ManifoldMeshGL64* m);
|
||||
ManifoldMeshGL64* manifold_meshgl64_merge(void* mem, ManifoldMeshGL64* m);
|
||||
|
||||
// SDF
|
||||
// By default, the execution policy (sequential or parallel) of
|
||||
// manifold_level_set will be chosen automatically depending on the size of the
|
||||
// job and whether Manifold has been compiled with a PAR backend. If you are
|
||||
// using these bindings from a language that has a runtime lock preventing the
|
||||
// parallel execution of closures, then you should use manifold_level_set_seq to
|
||||
// force sequential execution.
|
||||
ManifoldManifold* manifold_level_set(void* mem, ManifoldSdf sdf,
|
||||
ManifoldBox* bounds, double edge_length,
|
||||
double level, double tolerance, void* ctx);
|
||||
ManifoldManifold* manifold_level_set_seq(void* mem, ManifoldSdf sdf,
|
||||
ManifoldBox* bounds,
|
||||
double edge_length, double level,
|
||||
double tolerance, void* ctx);
|
||||
|
||||
// Manifold Vectors
|
||||
|
||||
ManifoldManifoldVec* manifold_manifold_empty_vec(void* mem);
|
||||
ManifoldManifoldVec* manifold_manifold_vec(void* mem, size_t sz);
|
||||
void manifold_manifold_vec_reserve(ManifoldManifoldVec* ms, size_t sz);
|
||||
size_t manifold_manifold_vec_length(ManifoldManifoldVec* ms);
|
||||
ManifoldManifold* manifold_manifold_vec_get(void* mem, ManifoldManifoldVec* ms,
|
||||
size_t idx);
|
||||
void manifold_manifold_vec_set(ManifoldManifoldVec* ms, size_t idx,
|
||||
ManifoldManifold* m);
|
||||
void manifold_manifold_vec_push_back(ManifoldManifoldVec* ms,
|
||||
ManifoldManifold* m);
|
||||
|
||||
// Manifold Booleans
|
||||
|
||||
ManifoldManifold* manifold_boolean(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b, ManifoldOpType op);
|
||||
ManifoldManifold* manifold_batch_boolean(void* mem, ManifoldManifoldVec* ms,
|
||||
ManifoldOpType op);
|
||||
ManifoldManifold* manifold_union(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifold* manifold_difference(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifold* manifold_intersection(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifoldPair manifold_split(void* mem_first, void* mem_second,
|
||||
ManifoldManifold* a, ManifoldManifold* b);
|
||||
ManifoldManifoldPair manifold_split_by_plane(void* mem_first, void* mem_second,
|
||||
ManifoldManifold* m,
|
||||
double normal_x, double normal_y,
|
||||
double normal_z, double offset);
|
||||
ManifoldManifold* manifold_trim_by_plane(void* mem, ManifoldManifold* m,
|
||||
double normal_x, double normal_y,
|
||||
double normal_z, double offset);
|
||||
ManifoldManifold* manifold_minkowski_sum(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
ManifoldManifold* manifold_minkowski_difference(void* mem, ManifoldManifold* a,
|
||||
ManifoldManifold* b);
|
||||
|
||||
// 3D to 2D
|
||||
|
||||
ManifoldPolygons* manifold_slice(void* mem, ManifoldManifold* m, double height);
|
||||
ManifoldPolygons* manifold_project(void* mem, ManifoldManifold* m);
|
||||
|
||||
// Convex Hulls
|
||||
|
||||
ManifoldManifold* manifold_hull(void* mem, ManifoldManifold* m);
|
||||
ManifoldManifold* manifold_batch_hull(void* mem, ManifoldManifoldVec* ms);
|
||||
ManifoldManifold* manifold_hull_pts(void* mem, ManifoldVec3* ps, size_t length);
|
||||
|
||||
// Manifold Transformations
|
||||
|
||||
ManifoldManifold* manifold_translate(void* mem, ManifoldManifold* m, double x,
|
||||
double y, double z);
|
||||
ManifoldManifold* manifold_rotate(void* mem, ManifoldManifold* m, double x,
|
||||
double y, double z);
|
||||
ManifoldManifold* manifold_scale(void* mem, ManifoldManifold* m, double x,
|
||||
double y, double z);
|
||||
ManifoldManifold* manifold_transform(void* mem, ManifoldManifold* m, double x1,
|
||||
double y1, double z1, double x2, double y2,
|
||||
double z2, double x3, double y3, double z3,
|
||||
double x4, double y4, double z4);
|
||||
ManifoldManifold* manifold_mirror(void* mem, ManifoldManifold* m, double nx,
|
||||
double ny, double nz);
|
||||
ManifoldManifold* manifold_warp(void* mem, ManifoldManifold* m,
|
||||
ManifoldVec3 (*fun)(double, double, double,
|
||||
void*),
|
||||
void* ctx);
|
||||
ManifoldManifold* manifold_smooth_by_normals(void* mem, ManifoldManifold* m,
|
||||
int normalIdx);
|
||||
ManifoldManifold* manifold_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);
|
||||
ManifoldManifold* manifold_set_tolerance(void* mem, ManifoldManifold* m,
|
||||
double tolerance);
|
||||
ManifoldManifold* manifold_simplify(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_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);
|
||||
// Returns a copy of the manifold with the given ExecutionContext attached.
|
||||
// The attachment is consumed by the next eager op invoked on the result —
|
||||
// manifold_status, or one of the manifold_refine* family. Deferred ops
|
||||
// (Boolean operators, transforms, batch ops) ignore any attached ctx and
|
||||
// produce a result with no attached ctx. See ExecutionContext in common.h
|
||||
// for the full model.
|
||||
ManifoldManifold* manifold_with_context(void* mem, ManifoldManifold* m,
|
||||
ManifoldExecutionContext* ctx);
|
||||
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);
|
||||
double manifold_get_tolerance(ManifoldManifold* m);
|
||||
size_t manifold_num_prop_vert(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);
|
||||
|
||||
// Ray Casting
|
||||
ManifoldRayHitVec* manifold_ray_cast(void* mem, ManifoldManifold* m,
|
||||
double origin_x, double origin_y,
|
||||
double origin_z, double end_x,
|
||||
double end_y, double end_z);
|
||||
size_t manifold_ray_hit_vec_length(ManifoldRayHitVec* v);
|
||||
ManifoldRayHit manifold_ray_hit_vec_get(ManifoldRayHitVec* v, size_t idx);
|
||||
|
||||
// Point containment
|
||||
int manifold_winding_number(ManifoldManifold* m, double x, double y, double z);
|
||||
|
||||
// ExecutionContext: observe progress and request cancellation of a
|
||||
// long-running Manifold evaluation. Attach to a manifold via
|
||||
// manifold_with_context; subsequent operations on that manifold observe
|
||||
// the context. Safe to read/write from any thread. See the ExecutionContext
|
||||
// class in common.h for full semantics (sticky cancel, per-boolean
|
||||
// granularity).
|
||||
ManifoldExecutionContext* manifold_execution_context(void* mem);
|
||||
void manifold_execution_context_cancel(ManifoldExecutionContext* ctx);
|
||||
int manifold_execution_context_cancelled(ManifoldExecutionContext* ctx);
|
||||
double manifold_execution_context_progress(ManifoldExecutionContext* ctx);
|
||||
|
||||
// ctx-aware static factories. These ops have no source manifold to attach via
|
||||
// manifold_with_context, so they run on the ExecutionContext directly to report
|
||||
// progress / observe cancellation. Mirror manifold_level_set /
|
||||
// manifold_of_meshgl; `sdf_context` is the SDF callback's
|
||||
// user-data.
|
||||
ManifoldManifold* manifold_execution_context_level_set(
|
||||
void* mem, ManifoldExecutionContext* ec, ManifoldSdf sdf,
|
||||
ManifoldBox* bounds, double edge_length, double level, double tolerance,
|
||||
void* sdf_context);
|
||||
ManifoldManifold* manifold_execution_context_level_set_seq(
|
||||
void* mem, ManifoldExecutionContext* ec, ManifoldSdf sdf,
|
||||
ManifoldBox* bounds, double edge_length, double level, double tolerance,
|
||||
void* sdf_context);
|
||||
ManifoldManifold* manifold_execution_context_of_meshgl(
|
||||
void* mem, ManifoldExecutionContext* ec, ManifoldMeshGL* mesh);
|
||||
ManifoldManifold* manifold_execution_context_of_meshgl64(
|
||||
void* mem, ManifoldExecutionContext* ec, ManifoldMeshGL64* mesh);
|
||||
|
||||
// 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);
|
||||
ManifoldCrossSection* manifold_cross_section_of_polygons(void* mem,
|
||||
ManifoldPolygons* p);
|
||||
ManifoldCrossSection* manifold_cross_section_even_odd_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* p);
|
||||
ManifoldCrossSection* manifold_cross_section_even_odd_polygons(
|
||||
void* mem, ManifoldPolygons* p);
|
||||
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);
|
||||
ManifoldCrossSectionVec* manifold_cross_section_decompose(
|
||||
void* mem, ManifoldCrossSection* cs);
|
||||
|
||||
// CrossSection Vectors
|
||||
|
||||
ManifoldCrossSectionVec* manifold_cross_section_empty_vec(void* mem);
|
||||
ManifoldCrossSectionVec* manifold_cross_section_vec(void* mem, size_t sz);
|
||||
void manifold_cross_section_vec_reserve(ManifoldCrossSectionVec* csv,
|
||||
size_t sz);
|
||||
size_t manifold_cross_section_vec_length(ManifoldCrossSectionVec* csv);
|
||||
ManifoldCrossSection* manifold_cross_section_vec_get(
|
||||
void* mem, ManifoldCrossSectionVec* csv, size_t idx);
|
||||
void manifold_cross_section_vec_set(ManifoldCrossSectionVec* csv, size_t idx,
|
||||
ManifoldCrossSection* cs);
|
||||
void manifold_cross_section_vec_push_back(ManifoldCrossSectionVec* csv,
|
||||
ManifoldCrossSection* cs);
|
||||
|
||||
// CrossSection Booleans
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_boolean(void* mem,
|
||||
ManifoldCrossSection* a,
|
||||
ManifoldCrossSection* b,
|
||||
ManifoldOpType op);
|
||||
ManifoldCrossSection* manifold_cross_section_batch_boolean(
|
||||
void* mem, ManifoldCrossSectionVec* csv, ManifoldOpType op);
|
||||
ManifoldCrossSection* manifold_cross_section_union(void* mem,
|
||||
ManifoldCrossSection* a,
|
||||
ManifoldCrossSection* b);
|
||||
ManifoldCrossSection* manifold_cross_section_difference(
|
||||
void* mem, ManifoldCrossSection* a, ManifoldCrossSection* b);
|
||||
ManifoldCrossSection* manifold_cross_section_intersection(
|
||||
void* mem, ManifoldCrossSection* a, ManifoldCrossSection* b);
|
||||
|
||||
// CrossSection Convex Hulls
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_hull(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
ManifoldCrossSection* manifold_cross_section_batch_hull(
|
||||
void* mem, ManifoldCrossSectionVec* css);
|
||||
ManifoldCrossSection* manifold_cross_section_hull_simple_polygon(
|
||||
void* mem, ManifoldSimplePolygon* ps);
|
||||
ManifoldCrossSection* manifold_cross_section_hull_polygons(
|
||||
void* mem, ManifoldPolygons* ps);
|
||||
|
||||
// CrossSection Transformation
|
||||
|
||||
ManifoldCrossSection* manifold_cross_section_translate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y);
|
||||
ManifoldCrossSection* manifold_cross_section_rotate(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double deg);
|
||||
ManifoldCrossSection* manifold_cross_section_scale(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x, double y);
|
||||
ManifoldCrossSection* manifold_cross_section_mirror(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double ax_x, double ax_y);
|
||||
ManifoldCrossSection* manifold_cross_section_transform(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double x1, double y1,
|
||||
double x2, double y2,
|
||||
double x3, double y3);
|
||||
ManifoldCrossSection* manifold_cross_section_warp_context(
|
||||
void* mem, ManifoldCrossSection* cs,
|
||||
ManifoldVec2 (*fun)(double, double, void*), void* ctx);
|
||||
ManifoldCrossSection* manifold_cross_section_simplify(void* mem,
|
||||
ManifoldCrossSection* cs,
|
||||
double tolerance);
|
||||
ManifoldCrossSection* manifold_cross_section_set_tolerance(
|
||||
void* mem, ManifoldCrossSection* cs, double tolerance);
|
||||
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);
|
||||
double manifold_cross_section_get_tolerance(ManifoldCrossSection* cs);
|
||||
size_t manifold_cross_section_num_vert(ManifoldCrossSection* cs);
|
||||
size_t manifold_cross_section_num_contour(ManifoldCrossSection* cs);
|
||||
int manifold_cross_section_is_empty(ManifoldCrossSection* cs);
|
||||
ManifoldRect* manifold_cross_section_bounds(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
ManifoldPolygons* manifold_cross_section_to_polygons(void* mem,
|
||||
ManifoldCrossSection* cs);
|
||||
|
||||
// Rectangle
|
||||
|
||||
ManifoldRect* manifold_rect(void* mem, double x1, double y1, double x2,
|
||||
double y2);
|
||||
ManifoldVec2 manifold_rect_min(ManifoldRect* r);
|
||||
ManifoldVec2 manifold_rect_max(ManifoldRect* r);
|
||||
ManifoldVec2 manifold_rect_dimensions(ManifoldRect* r);
|
||||
ManifoldVec2 manifold_rect_center(ManifoldRect* r);
|
||||
double manifold_rect_scale(ManifoldRect* r);
|
||||
int manifold_rect_contains_pt(ManifoldRect* r, double x, double y);
|
||||
int manifold_rect_contains_rect(ManifoldRect* a, ManifoldRect* b);
|
||||
void manifold_rect_include_pt(ManifoldRect* r, double x, double y);
|
||||
ManifoldRect* manifold_rect_union(void* mem, ManifoldRect* a, ManifoldRect* b);
|
||||
ManifoldRect* manifold_rect_transform(void* mem, ManifoldRect* r, double x1,
|
||||
double y1, double x2, double y2,
|
||||
double x3, double y3);
|
||||
ManifoldRect* manifold_rect_translate(void* mem, ManifoldRect* r, double x,
|
||||
double y);
|
||||
ManifoldRect* manifold_rect_mul(void* mem, ManifoldRect* r, double x, double y);
|
||||
int manifold_rect_does_overlap_rect(ManifoldRect* a, ManifoldRect* r);
|
||||
int manifold_rect_is_empty(ManifoldRect* r);
|
||||
int manifold_rect_is_finite(ManifoldRect* r);
|
||||
|
||||
// Bounding Box
|
||||
|
||||
ManifoldBox* manifold_box(void* mem, double x1, double y1, double z1, double x2,
|
||||
double y2, double z2);
|
||||
ManifoldVec3 manifold_box_min(ManifoldBox* b);
|
||||
ManifoldVec3 manifold_box_max(ManifoldBox* b);
|
||||
ManifoldVec3 manifold_box_dimensions(ManifoldBox* b);
|
||||
ManifoldVec3 manifold_box_center(ManifoldBox* b);
|
||||
double manifold_box_scale(ManifoldBox* b);
|
||||
int manifold_box_contains_pt(ManifoldBox* b, double x, double y, double z);
|
||||
int manifold_box_contains_box(ManifoldBox* a, ManifoldBox* b);
|
||||
void manifold_box_include_pt(ManifoldBox* b, double x, double y, double z);
|
||||
ManifoldBox* manifold_box_union(void* mem, ManifoldBox* a, ManifoldBox* b);
|
||||
ManifoldBox* manifold_box_transform(void* mem, ManifoldBox* b, double x1,
|
||||
double y1, double z1, double x2, double y2,
|
||||
double z2, double x3, double y3, double z3,
|
||||
double x4, double y4, double z4);
|
||||
ManifoldBox* manifold_box_translate(void* mem, ManifoldBox* b, double x,
|
||||
double y, double z);
|
||||
ManifoldBox* manifold_box_mul(void* mem, ManifoldBox* b, double x, double y,
|
||||
double z);
|
||||
int manifold_box_does_overlap_pt(ManifoldBox* b, double x, double y, double z);
|
||||
int manifold_box_does_overlap_box(ManifoldBox* a, ManifoldBox* b);
|
||||
int manifold_box_is_finite(ManifoldBox* b);
|
||||
|
||||
// Static Quality Globals
|
||||
|
||||
void manifold_set_min_circular_angle(double degrees);
|
||||
void manifold_set_min_circular_edge_length(double length);
|
||||
void manifold_set_circular_segments(int number);
|
||||
void manifold_reset_to_circular_defaults();
|
||||
|
||||
// Manifold Mesh Extraction
|
||||
|
||||
size_t manifold_meshgl_num_prop(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_num_vert(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_num_tri(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_vert_properties_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_tri_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_merge_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_index_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_original_id_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_transform_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_face_id_length(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_tangent_length(ManifoldMeshGL* m);
|
||||
float* manifold_meshgl_vert_properties(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_tri_verts(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_merge_from_vert(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_merge_to_vert(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_run_index(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_run_original_id(void* mem, ManifoldMeshGL* m);
|
||||
float* manifold_meshgl_run_transform(void* mem, ManifoldMeshGL* m);
|
||||
uint32_t* manifold_meshgl_face_id(void* mem, ManifoldMeshGL* m);
|
||||
float* manifold_meshgl_halfedge_tangent(void* mem, ManifoldMeshGL* m);
|
||||
float manifold_meshgl_tolerance(ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_run_flags_length(ManifoldMeshGL* m);
|
||||
uint8_t* manifold_meshgl_run_flags(void* mem, ManifoldMeshGL* m);
|
||||
size_t manifold_meshgl_num_run(ManifoldMeshGL* m);
|
||||
int manifold_meshgl_backside(ManifoldMeshGL* m, size_t run);
|
||||
int manifold_meshgl_has_normals(ManifoldMeshGL* m, size_t run);
|
||||
|
||||
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);
|
||||
double manifold_meshgl64_tolerance(ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_run_flags_length(ManifoldMeshGL64* m);
|
||||
uint8_t* manifold_meshgl64_run_flags(void* mem, ManifoldMeshGL64* m);
|
||||
size_t manifold_meshgl64_num_run(ManifoldMeshGL64* m);
|
||||
int manifold_meshgl64_backside(ManifoldMeshGL64* m, size_t run);
|
||||
int manifold_meshgl64_has_normals(ManifoldMeshGL64* m, size_t run);
|
||||
|
||||
// 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_ray_hit_vec_size();
|
||||
size_t manifold_simple_polygon_size();
|
||||
size_t manifold_polygons_size();
|
||||
size_t manifold_manifold_pair_size();
|
||||
size_t manifold_meshgl_size();
|
||||
size_t manifold_meshgl64_size();
|
||||
size_t manifold_box_size();
|
||||
size_t manifold_rect_size();
|
||||
size_t manifold_triangulation_size();
|
||||
size_t manifold_execution_context_size();
|
||||
|
||||
// allocation
|
||||
|
||||
ManifoldManifold* manifold_alloc_manifold();
|
||||
ManifoldManifoldVec* manifold_alloc_manifold_vec();
|
||||
ManifoldCrossSection* manifold_alloc_cross_section();
|
||||
ManifoldCrossSectionVec* manifold_alloc_cross_section_vec();
|
||||
ManifoldRayHitVec* manifold_alloc_ray_hit_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();
|
||||
ManifoldExecutionContext* manifold_alloc_execution_context();
|
||||
|
||||
// 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_ray_hit_vec(ManifoldRayHitVec* v);
|
||||
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);
|
||||
void manifold_destruct_execution_context(ManifoldExecutionContext* ctx);
|
||||
|
||||
// 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_ray_hit_vec(ManifoldRayHitVec* v);
|
||||
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);
|
||||
void manifold_delete_execution_context(ManifoldExecutionContext* ctx);
|
||||
|
||||
#ifndef MANIFOLD_NO_IOSTREAM
|
||||
// MeshIO / Export
|
||||
|
||||
// Import a manifold from a Wavefront obj file.
|
||||
// The obj_file parameter is the content of the obj file, not the filename,
|
||||
// and should be null-terminated.
|
||||
ManifoldManifold* manifold_read_obj(void* mem, char* obj_file);
|
||||
// Import a meshgl from a Wavefront obj file.
|
||||
// The obj_file parameter is the content of the obj file, not the filename,
|
||||
// and should be null-terminated.
|
||||
ManifoldMeshGL64* manifold_meshgl64_read_obj(void* mem, char* obj_file);
|
||||
// Export a manifold to a Wavefront obj file.
|
||||
// The callback accepts two parameters:
|
||||
// 1. Temporary null-terminated string buffer, containing the content of the
|
||||
// file. This buffer will be freed automatically after returning from the
|
||||
// callback.
|
||||
// 2. An arg value (the third parameter in the manifold_write_obj function), for
|
||||
// passing additional data into the callback.
|
||||
void manifold_write_obj(ManifoldManifold* manifold,
|
||||
void (*callback)(char*, void*), void* args);
|
||||
// Export a MeshGL64 to a Wavefront obj file.
|
||||
// The callback accepts two parameters:
|
||||
// 1. Temporary null-terminated string buffer, containing the content of the
|
||||
// file. This buffer will be freed automatically after returning from the
|
||||
// callback.
|
||||
// 2. An arg value (the third parameter in the manifold_write_obj function), for
|
||||
// passing additional data into the callback.
|
||||
void manifold_meshgl64_write_obj(ManifoldMeshGL64* mesh,
|
||||
void (*callback)(char*, void*), void* args);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
// 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// opaque pointers
|
||||
|
||||
typedef struct ManifoldManifold ManifoldManifold;
|
||||
typedef struct ManifoldManifoldVec ManifoldManifoldVec;
|
||||
typedef struct ManifoldCrossSection ManifoldCrossSection;
|
||||
typedef struct ManifoldCrossSectionVec ManifoldCrossSectionVec;
|
||||
typedef struct ManifoldRayHitVec ManifoldRayHitVec;
|
||||
typedef struct ManifoldSimplePolygon ManifoldSimplePolygon;
|
||||
typedef struct ManifoldPolygons ManifoldPolygons;
|
||||
typedef struct ManifoldMeshGL ManifoldMeshGL;
|
||||
typedef struct ManifoldMeshGL64 ManifoldMeshGL64;
|
||||
typedef struct ManifoldBox ManifoldBox;
|
||||
typedef struct ManifoldRect ManifoldRect;
|
||||
typedef struct ManifoldTriangulation ManifoldTriangulation;
|
||||
typedef struct ManifoldExecutionContext ManifoldExecutionContext;
|
||||
|
||||
// structs
|
||||
|
||||
typedef struct ManifoldManifoldPair {
|
||||
ManifoldManifold* first;
|
||||
ManifoldManifold* second;
|
||||
} ManifoldManifoldPair;
|
||||
|
||||
typedef struct ManifoldVec2 {
|
||||
double x;
|
||||
double y;
|
||||
} ManifoldVec2;
|
||||
|
||||
typedef struct ManifoldVec3 {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
} ManifoldVec3;
|
||||
|
||||
typedef struct ManifoldIVec3 {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
} ManifoldIVec3;
|
||||
|
||||
typedef struct ManifoldVec4 {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
double w;
|
||||
} ManifoldVec4;
|
||||
|
||||
typedef struct ManifoldProperties {
|
||||
double surface_area;
|
||||
double volume;
|
||||
} ManifoldProperties;
|
||||
|
||||
typedef struct ManifoldMeshGLOptions {
|
||||
uint32_t* run_indices;
|
||||
size_t run_indices_length;
|
||||
uint32_t* run_original_ids;
|
||||
size_t run_original_ids_length;
|
||||
uint32_t* merge_from_vert;
|
||||
uint32_t* merge_to_vert;
|
||||
size_t merge_verts_length;
|
||||
float* halfedge_tangents;
|
||||
} ManifoldMeshGLOptions;
|
||||
|
||||
typedef struct ManifoldMeshGL64Options {
|
||||
uint64_t* run_indices;
|
||||
size_t run_indices_length;
|
||||
uint32_t* run_original_ids;
|
||||
size_t run_original_ids_length;
|
||||
uint64_t* merge_from_vert;
|
||||
uint64_t* merge_to_vert;
|
||||
size_t merge_verts_length;
|
||||
double* halfedge_tangents;
|
||||
} ManifoldMeshGL64Options;
|
||||
|
||||
typedef struct ManifoldRayHit {
|
||||
uint64_t face_id;
|
||||
double distance;
|
||||
ManifoldVec3 position;
|
||||
ManifoldVec3 normal;
|
||||
} ManifoldRayHit;
|
||||
|
||||
// enums
|
||||
|
||||
typedef enum ManifoldOpType {
|
||||
MANIFOLD_ADD,
|
||||
MANIFOLD_SUBTRACT,
|
||||
MANIFOLD_INTERSECT
|
||||
} ManifoldOpType;
|
||||
|
||||
typedef enum ManifoldError {
|
||||
MANIFOLD_NO_ERROR,
|
||||
MANIFOLD_NON_FINITE_VERTEX,
|
||||
MANIFOLD_NOT_MANIFOLD,
|
||||
MANIFOLD_VERTEX_INDEX_OUT_OF_BOUNDS,
|
||||
MANIFOLD_PROPERTIES_WRONG_LENGTH,
|
||||
MANIFOLD_MISSING_POSITION_PROPERTIES,
|
||||
MANIFOLD_MERGE_VECTORS_DIFFERENT_LENGTHS,
|
||||
MANIFOLD_MERGE_INDEX_OUT_OF_BOUNDS,
|
||||
MANIFOLD_TRANSFORM_WRONG_LENGTH,
|
||||
MANIFOLD_RUN_INDEX_WRONG_LENGTH,
|
||||
MANIFOLD_FACE_ID_WRONG_LENGTH,
|
||||
MANIFOLD_INVALID_CONSTRUCTION,
|
||||
MANIFOLD_RESULT_TOO_LARGE,
|
||||
MANIFOLD_INVALID_TANGENTS,
|
||||
MANIFOLD_CANCELLED,
|
||||
} ManifoldError;
|
||||
|
||||
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*);
|
||||
@@ -1,373 +0,0 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
ManifoldCurvature *manifold_get_curvature(void *mem, ManifoldManifold *m);
|
||||
ManifoldCurvatureBounds manifold_curvature_bounds(ManifoldCurvature *curv);
|
||||
size_t manifold_curvature_vert_length(ManifoldCurvature *curv);
|
||||
float *manifold_curvature_vert_mean(void *mem, ManifoldCurvature *curv);
|
||||
float *manifold_curvature_vert_gaussian(void *mem, ManifoldCurvature *curv);
|
||||
int manifold_get_circular_segments(float radius);
|
||||
int manifold_original_id(ManifoldManifold *m);
|
||||
|
||||
// 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);
|
||||
ManifoldCrossSection *manifold_cross_section_rect_clip(void *mem,
|
||||
ManifoldCrossSection *cs,
|
||||
ManifoldRect *r);
|
||||
|
||||
// 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, double arc_tolerance);
|
||||
|
||||
// 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);
|
||||
ManifoldCrossSection *manifold_rect_as_cross_section(void *mem,
|
||||
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);
|
||||
|
||||
// MeshIO / 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);
|
||||
|
||||
// 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();
|
||||
size_t manifold_material_size();
|
||||
size_t manifold_export_options_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);
|
||||
void manifold_destruct_curvature(ManifoldCurvature *c);
|
||||
void manifold_destruct_material(ManifoldMaterial *m);
|
||||
void manifold_destruct_export_options(ManifoldExportOptions *options);
|
||||
|
||||
// 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);
|
||||
void manifold_delete_curvature(ManifoldCurvature *c);
|
||||
void manifold_delete_material(ManifoldMaterial *m);
|
||||
void manifold_delete_export_options(ManifoldExportOptions *options);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,91 +0,0 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct ManifoldManifold ManifoldManifold;
|
||||
typedef struct ManifoldManifoldVec ManifoldManifoldVec;
|
||||
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 ManifoldCurvature ManifoldCurvature;
|
||||
typedef struct ManifoldBox ManifoldBox;
|
||||
typedef struct ManifoldRect ManifoldRect;
|
||||
typedef struct ManifoldMaterial ManifoldMaterial;
|
||||
typedef struct ManifoldExportOptions ManifoldExportOptions;
|
||||
|
||||
typedef struct ManifoldManifoldPair {
|
||||
ManifoldManifold* first;
|
||||
ManifoldManifold* second;
|
||||
} ManifoldManifoldPair;
|
||||
|
||||
typedef struct ManifoldVec2 {
|
||||
float x;
|
||||
float y;
|
||||
} ManifoldVec2;
|
||||
|
||||
typedef struct ManifoldVec3 {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
} ManifoldVec3;
|
||||
|
||||
typedef struct ManifoldIVec3 {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
} ManifoldIVec3;
|
||||
|
||||
typedef struct ManifoldVec4 {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
} ManifoldVec4;
|
||||
|
||||
typedef struct ManifoldProperties {
|
||||
float surface_area;
|
||||
float volume;
|
||||
} ManifoldProperties;
|
||||
|
||||
typedef struct ManifoldCurvatureBounds {
|
||||
float max_mean_curvature;
|
||||
float min_mean_curvature;
|
||||
float max_gaussian_curvature;
|
||||
float min_gaussian_curvature;
|
||||
} ManifoldCurvatureBounds;
|
||||
|
||||
typedef enum ManifoldOpType {
|
||||
MANIFOLD_ADD,
|
||||
MANIFOLD_SUBTRACT,
|
||||
MANIFOLD_INTERSECT
|
||||
} ManifoldOpType;
|
||||
|
||||
typedef enum ManifoldError {
|
||||
MANIFOLD_NO_ERROR,
|
||||
MANIFOLD_NON_FINITE_VERTEX,
|
||||
MANIFOLD_NOT_MANIFOLD,
|
||||
MANIFOLD_VERTEX_INDEX_OUT_OF_BOUNDS,
|
||||
MANIFOLD_PROPERTIES_WRONG_LENGTH,
|
||||
MANIFOLD_MISSING_POSITION_PROPERTIES,
|
||||
MANIFOLD_MERGE_VECTORS_DIFFERENT_LENGTHS,
|
||||
MANIFOLD_MERGE_INDEX_OUT_OF_BOUNDS,
|
||||
MANIFOLD_TRANSFORM_WRONG_LENGTH,
|
||||
MANIFOLD_RUN_INDEX_WRONG_LENGTH,
|
||||
MANIFOLD_FACE_ID_WRONG_LENGTH,
|
||||
MANIFOLD_INVALID_CONSTRUCTION,
|
||||
} ManifoldError;
|
||||
|
||||
typedef enum ManifoldFillRule {
|
||||
MANIFOLD_FILL_RULE_EVEN_ODD,
|
||||
MANIFOLD_FILL_RULE_NON_ZERO,
|
||||
MANIFOLD_FILL_RULE_POSITIVE,
|
||||
MANIFOLD_FILL_RULE_NEGATIVE
|
||||
} ManifoldFillRule;
|
||||
|
||||
typedef enum ManifoldJoinType {
|
||||
MANIFOLD_JOIN_TYPE_SQUARE,
|
||||
MANIFOLD_JOIN_TYPE_ROUND,
|
||||
MANIFOLD_JOIN_TYPE_MITER,
|
||||
} ManifoldJoinType;
|
||||
@@ -1,72 +0,0 @@
|
||||
#include "meshIO.h"
|
||||
|
||||
#include <conv.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void manifold_export_meshgl(const char *filename, ManifoldMeshGL *mesh,
|
||||
ManifoldExportOptions *options) {
|
||||
manifold::ExportMesh(std::string(filename), *from_c(mesh), *from_c(options));
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -1,93 +1,104 @@
|
||||
#include <conv.h>
|
||||
#include <manifold.h>
|
||||
#include <public.h>
|
||||
// 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 "cross_section.h"
|
||||
#include "types.h"
|
||||
#include "conv.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) {
|
||||
auto rect = *from_c(r);
|
||||
auto p = glm::vec2(x, y);
|
||||
void manifold_rect_include_pt(ManifoldRect* r, double x, double y) {
|
||||
auto& rect = *from_c(r);
|
||||
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(); }
|
||||
|
||||
ManifoldCrossSection *manifold_rect_as_cross_section(void *mem,
|
||||
ManifoldRect *r) {
|
||||
auto cs = from_c(r)->AsCrossSection();
|
||||
return to_c(new (mem) CrossSection(cs));
|
||||
int manifold_rect_is_finite(ManifoldRect* r) { return from_c(r)->IsFinite(); }
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,13 +12,75 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
project(python)
|
||||
|
||||
add_subdirectory(third_party)
|
||||
pybind11_add_module(pymanifold pymanifold.cpp)
|
||||
target_link_libraries(pymanifold PRIVATE manifold)
|
||||
target_compile_options(pymanifold PRIVATE ${MANIFOLD_FLAGS})
|
||||
target_compile_features(pymanifold PUBLIC cxx_std_17)
|
||||
target_include_directories(pymanifold
|
||||
PRIVATE ${PYBIND11_DIR}/include
|
||||
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()
|
||||
|
||||
target_link_libraries(manifold3d PRIVATE manifold)
|
||||
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.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/polygon.cpp
|
||||
${PROJECT_SOURCE_DIR}/include/manifold/common.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gen_docs.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/docstring_override.txt
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT autogen_docstrings.inl
|
||||
DEPENDS ${DOCSTRING_DEPS}
|
||||
COMMAND ${Python_EXECUTABLE}
|
||||
ARGS ${CMAKE_CURRENT_SOURCE_DIR}/gen_docs.py
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
target_include_directories(manifold3d PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(SKBUILD)
|
||||
set(MANIFOLD_PYBIND_LIBDIR ${SKBUILD_PLATLIB_DIR})
|
||||
else()
|
||||
set(MANIFOLD_PYBIND_LIBDIR ${Python_SITEARCH})
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS manifold3d
|
||||
LIBRARY DESTINATION ${MANIFOLD_PYBIND_LIBDIR} COMPONENT bindings
|
||||
)
|
||||
if(MANIFOLD_PYBIND_STUBGEN)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/manifold3d.pyi
|
||||
DESTINATION ${MANIFOLD_PYBIND_LIBDIR}
|
||||
COMPONENT bindings
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,26 @@
|
||||
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.
|
||||
@@ -0,0 +1,164 @@
|
||||
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.even_odd([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.batch_boolean([c, c.translate((1, 0))], OpType.Add)
|
||||
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()
|
||||
t = c.get_tolerance()
|
||||
c = c.set_tolerance(t)
|
||||
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)
|
||||
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.sphere(1)
|
||||
m, n = m.split(m.translate((1, 0, 0)))
|
||||
m, n = m.split_by_plane((0, 0, 1), 0)
|
||||
e = m.status()
|
||||
ctx = ExecutionContext()
|
||||
_ = ctx.cancelled()
|
||||
_ = ctx.progress()
|
||||
ctx.cancel()
|
||||
m = m.with_context(ctx)
|
||||
# Real cancel assertion: a pre-cancelled ctx on a CsgOpNode returns
|
||||
# Cancelled (not NoError). Leaf nodes are trivially evaluated so use a
|
||||
# fresh union to ensure an actual CSG tree is traversed.
|
||||
cancelled_ctx = ExecutionContext()
|
||||
cancelled_ctx.cancel()
|
||||
tree = (Manifold.cube() + Manifold.sphere(1)).with_context(cancelled_ctx)
|
||||
assert tree.status() == Error.Cancelled
|
||||
# ctx-aware static factories: these ops have no source Manifold to attach a
|
||||
# context to via with_context, so they run on the context directly.
|
||||
ingest_ctx = ExecutionContext()
|
||||
assert ingest_ctx.from_mesh(Manifold.cube().to_mesh()).status() == Error.NoError
|
||||
assert ingest_ctx.progress() == 1.0
|
||||
sdf_ctx = ExecutionContext()
|
||||
sphere = sdf_ctx.level_set(
|
||||
lambda x, y, z: 1 - (x * x + y * y + z * z) ** 0.5,
|
||||
[-1.1, -1.1, -1.1, 1.1, 1.1, 1.1],
|
||||
0.2,
|
||||
)
|
||||
assert sphere.status() == Error.NoError
|
||||
# A pre-cancelled ctx propagates through a factory; this proves the ctx is
|
||||
# wired, since NoError + progress==1 also hold for an untouched ctx.
|
||||
cancel_factory_ctx = ExecutionContext()
|
||||
cancel_factory_ctx.cancel()
|
||||
cancelled = cancel_factory_ctx.from_mesh(Manifold.cube().to_mesh())
|
||||
assert cancelled.status() == Error.Cancelled
|
||||
m = Manifold.tetrahedron()
|
||||
mesh = m.to_mesh()
|
||||
ok = mesh.merge()
|
||||
m = m.transform([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])
|
||||
m = m.translate((0, 0, 0))
|
||||
m = m.trim_by_plane((0, 0, 1), 0)
|
||||
m = m.warp(lambda p: (p[0] + 1, p[1] / 2, p[2] * 2))
|
||||
m = m.warp_batch(lambda ps: ps * 2 + [1, 0, 0])
|
||||
m = Manifold.cube()
|
||||
m2 = Manifold.cube().translate([2, 0, 0])
|
||||
d = m.min_gap(m2, 2)
|
||||
mesh2 = m.to_mesh64()
|
||||
ok = mesh.merge()
|
||||
|
||||
|
||||
def run():
|
||||
all_root_level()
|
||||
all_cross_section()
|
||||
all_manifold()
|
||||
return Manifold()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -1,79 +1,101 @@
|
||||
"""
|
||||
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 pymanifold import Manifold
|
||||
from manifold3d import Manifold
|
||||
|
||||
# https://gist.github.com/deckar01/ef11def51de7e71d9f288c6e5819fdb7
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
from pymanifold import Manifold
|
||||
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())
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
from manifold3d import CrossSection, Manifold
|
||||
|
||||
|
||||
def run():
|
||||
# create a polygon
|
||||
polygon_points = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]
|
||||
polygons_points = [polygon_points]
|
||||
|
||||
# create a cross-section
|
||||
cross_section = CrossSection(polygons_points)
|
||||
polygons = cross_section.to_polygons()
|
||||
polygon = polygons[0]
|
||||
if set([tuple(p) for p in polygon]) != set(polygon_points):
|
||||
raise Exception(
|
||||
f"polygon={polygon} differs from polygon_points={polygon_points}"
|
||||
)
|
||||
|
||||
# extrude a polygon to create a manifold
|
||||
extruded_polygon = Manifold.extrude(cross_section, 10.0)
|
||||
eps = 0.001
|
||||
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.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}"
|
||||
)
|
||||
|
||||
# get bounding box from manifold
|
||||
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}"
|
||||
)
|
||||
|
||||
return extruded_polygon
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -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)
|
||||
@@ -0,0 +1,22 @@
|
||||
import numpy as np
|
||||
from manifold3d import Manifold
|
||||
|
||||
|
||||
def run():
|
||||
small_cube = Manifold.cube([0.1, 0.1, 0.1], True)
|
||||
cube_vertices = small_cube.to_mesh().vert_properties[:, :3]
|
||||
star = Manifold.as_original(small_cube)
|
||||
for offset in [
|
||||
[[0.2, 0.0, 0.0]],
|
||||
[[-0.2, 0.0, 0.0]],
|
||||
[[0.0, 0.2, 0.0]],
|
||||
[[0.0, -0.2, 0.0]],
|
||||
[[0.0, 0.0, 0.2]],
|
||||
[[0.0, 0.0, -0.2]],
|
||||
]:
|
||||
star += Manifold.hull_points(np.concatenate((cube_vertices, offset), axis=0))
|
||||
|
||||
sphere = Manifold.sphere(0.6, 15)
|
||||
cube = Manifold.cube([1.0, 1.0, 1.0], True)
|
||||
sphereless_cube = cube - sphere
|
||||
return sphereless_cube.minkowski_sum(star)
|
||||
@@ -1,32 +1,32 @@
|
||||
"""
|
||||
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
|
||||
import importlib
|
||||
import trimesh
|
||||
import numpy as np
|
||||
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)
|
||||
@@ -34,9 +34,16 @@ if __name__ == "__main__":
|
||||
model = module.run()
|
||||
mesh = model.to_mesh()
|
||||
if export_models:
|
||||
if mesh.vert_properties.shape[1] > 3:
|
||||
vertices = mesh.vert_properties[:, :3]
|
||||
colors = (mesh.vert_properties[:, 3:] * 255).astype(np.uint8)
|
||||
else:
|
||||
vertices = mesh.vert_properties
|
||||
colors = None
|
||||
meshOut = trimesh.Trimesh(
|
||||
vertices=mesh.vert_pos, faces=mesh.tri_verts)
|
||||
trimesh.exchange.export.export_mesh(meshOut, f'{f}.glb', 'glb')
|
||||
print(f'Exported model to {f}.glb')
|
||||
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}")
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
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 manual smoothing by assigning tangents to
|
||||
# halfedges. Use refine() before export to see the curvature.
|
||||
|
||||
height = 10
|
||||
radius = 30
|
||||
offset = 20
|
||||
wiggles = 12
|
||||
lean = 0.3
|
||||
frontal_sharpness = 1
|
||||
n = 50
|
||||
|
||||
def lerp(a, b, t):
|
||||
return np.array(a) + (np.array(b) - np.array(a)) * t
|
||||
|
||||
triangles = []
|
||||
positions = [[-offset, 0, height], [-offset, 0, -height]]
|
||||
halfedge_tangent = []
|
||||
|
||||
len_ = np.pi * radius / (2 * wiggles)
|
||||
top_normal = np.array([-lean, 0, np.sqrt(1 - lean * lean)])
|
||||
delta = np.pi / wiggles
|
||||
center_tangents = [None] * (2 * wiggles)
|
||||
edge_tangents = [None] * (2 * wiggles)
|
||||
|
||||
for i in range(2 * wiggles):
|
||||
theta = i * delta
|
||||
amp = 0.5 * height * (np.cos(theta) + 1) / 2
|
||||
v = np.array(
|
||||
[
|
||||
radius * np.cos(theta),
|
||||
radius * np.sin(theta),
|
||||
amp * (1 if i % 2 == 0 else -1),
|
||||
],
|
||||
dtype=np.float64,
|
||||
)
|
||||
|
||||
positions.append(v.tolist())
|
||||
|
||||
center_tan = np.array([(v[0] + offset) / 2, v[1] / 2, (v[2] - height) / 2])
|
||||
center_tangents[i] = center_tan - np.dot(center_tan, top_normal) * top_normal
|
||||
edge_tangents[i] = np.array([-len_ * np.sin(theta), len_ * np.cos(theta), 0.0])
|
||||
|
||||
for i in range(2 * wiggles):
|
||||
next_i = 0 if i + 1 == 2 * wiggles else i + 1
|
||||
|
||||
radial = center_tangents[i]
|
||||
next_radial = center_tangents[next_i]
|
||||
edge = edge_tangents[i]
|
||||
next_edge = -edge_tangents[next_i]
|
||||
sharpness = frontal_sharpness * (np.cos(i * delta) + 1) / 4
|
||||
up = lerp(
|
||||
np.array([0, 0, len_]),
|
||||
np.array([next_edge[1], -next_edge[0], 0]),
|
||||
sharpness,
|
||||
)
|
||||
down = lerp(
|
||||
np.array([0, 0, -len_]), np.array([-edge[1], edge[0], 0]), sharpness
|
||||
)
|
||||
|
||||
triangles.append([0, 2 + i, 2 + next_i])
|
||||
halfedge_tangent.extend([*radial.tolist(), 1])
|
||||
halfedge_tangent.extend([*edge.tolist(), 1])
|
||||
halfedge_tangent.extend([*up.tolist(), 1])
|
||||
|
||||
triangles.append([1, 2 + next_i, 2 + i])
|
||||
halfedge_tangent.extend([next_radial[0], next_radial[1], -next_radial[2], 1])
|
||||
halfedge_tangent.extend([*next_edge.tolist(), 1])
|
||||
halfedge_tangent.extend([*down.tolist(), 1])
|
||||
|
||||
scallop = Mesh(
|
||||
tri_verts=np.array(triangles, np.int32),
|
||||
vert_properties=np.array(positions, np.float32),
|
||||
halfedge_tangent=np.array(halfedge_tangent, np.float32).reshape(-1, 3, 4),
|
||||
)
|
||||
|
||||
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)]
|
||||
|
||||
return (
|
||||
Manifold(scallop)
|
||||
.refine(n)
|
||||
.calculate_curvature(-1, 0)
|
||||
.set_properties(3, colorCurvature)
|
||||
)
|
||||
@@ -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]
|
||||
@@ -0,0 +1,45 @@
|
||||
from manifold3d import Manifold
|
||||
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]))
|
||||
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],
|
||||
]
|
||||
)
|
||||
for offset in offsets:
|
||||
fractal(holes, hole, w, position + offset, depth + 1, maxDepth)
|
||||
|
||||
|
||||
def posColors(pos, _):
|
||||
return [-p + 0.5 for p in pos] + [1.0]
|
||||
|
||||
|
||||
def run(n=1):
|
||||
result = Manifold.cube([1, 1, 1], True)
|
||||
holes = []
|
||||
fractal(holes, result, 1.0, np.array([0.0, 0.0]), 1, n)
|
||||
|
||||
hole = Manifold.compose(holes)
|
||||
|
||||
result -= hole
|
||||
result -= hole.rotate([90, 0, 0])
|
||||
result -= hole.rotate([0, 90, 0])
|
||||
|
||||
return (
|
||||
result.trim_by_plane([1, 1, 1], 0)
|
||||
.set_properties(4, posColors)
|
||||
.scale([100, 100, 100])
|
||||
)
|
||||
@@ -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()
|
||||
@@ -1,7 +1,8 @@
|
||||
from pymanifold import Manifold
|
||||
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
|
||||
|
||||
@@ -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.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")
|
||||
@@ -0,0 +1,921 @@
|
||||
// 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.
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "autogen_docstrings.inl" // generated in build folder
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/manifold.h"
|
||||
#include "manifold/polygon.h"
|
||||
#include "nanobind/nanobind.h"
|
||||
#include "nanobind/ndarray.h"
|
||||
#include "nanobind/operators.h"
|
||||
#include "nanobind/stl/function.h"
|
||||
#include "nanobind/stl/optional.h"
|
||||
#include "nanobind/stl/pair.h"
|
||||
#include "nanobind/stl/tuple.h"
|
||||
#include "nanobind/stl/vector.h"
|
||||
|
||||
namespace nb = nanobind;
|
||||
using namespace manifold;
|
||||
|
||||
template <class T>
|
||||
struct la_name {};
|
||||
template <>
|
||||
struct la_name<vec3> {
|
||||
static constexpr char const name[] = "Doublex3";
|
||||
static constexpr char const multi_name[] = "DoubleNx3";
|
||||
};
|
||||
template <>
|
||||
struct la_name<vec2> {
|
||||
static constexpr char const name[] = "Doublex2";
|
||||
static constexpr char const multi_name[] = "DoubleNx2";
|
||||
};
|
||||
template <>
|
||||
struct la_name<ivec3> {
|
||||
static constexpr char const name[] = "Intx3";
|
||||
static constexpr char const multi_name[] = "IntNx3";
|
||||
};
|
||||
template <>
|
||||
struct la_name<mat3x4> {
|
||||
static constexpr char const name[] = "Double3x4";
|
||||
};
|
||||
template <>
|
||||
struct la_name<mat2x3> {
|
||||
static constexpr char const name[] = "Double2x3";
|
||||
};
|
||||
|
||||
// handle la::vecN
|
||||
template <class T, int N>
|
||||
struct nb::detail::type_caster<la::vec<T, N>> {
|
||||
using la_type = la::vec<T, N>;
|
||||
NB_TYPE_CASTER(la_type, const_name(la_name<la_type>::name));
|
||||
|
||||
bool from_python(handle src, uint8_t flags, cleanup_list* cleanup) noexcept {
|
||||
int size = PyObject_Size(src.ptr()); // negative on failure
|
||||
if (size != N) return false;
|
||||
make_caster<T> t_cast;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!t_cast.from_python(src[i], flags, cleanup)) return false;
|
||||
value[i] = t_cast.value;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static handle from_cpp(la_type vec, rv_policy policy,
|
||||
cleanup_list* cleanup) noexcept {
|
||||
nb::list out;
|
||||
for (int i = 0; i < N; i++) out.append(vec[i]);
|
||||
return out.release();
|
||||
}
|
||||
};
|
||||
|
||||
// handle la::matMxN
|
||||
template <class T, int C, int R>
|
||||
struct nb::detail::type_caster<la::mat<T, R, C>> {
|
||||
using la_type = la::mat<T, R, C>;
|
||||
using numpy_type = nb::ndarray<nb::numpy, T, nb::shape<R, C>>;
|
||||
NB_TYPE_CASTER(la_type, const_name(la_name<la_type>::name));
|
||||
|
||||
bool from_python(handle src, uint8_t flags, cleanup_list* cleanup) noexcept {
|
||||
int rows = PyObject_Size(src.ptr()); // negative on failure
|
||||
if (rows != R) return false;
|
||||
for (int i = 0; i < R; i++) {
|
||||
const nb::object& slice = src[i];
|
||||
int cols = PyObject_Size(slice.ptr()); // negative on failure
|
||||
if (cols != C) return false;
|
||||
for (int j = 0; j < C; j++) {
|
||||
make_caster<T> t_cast;
|
||||
if (!t_cast.from_python(slice[j], flags, cleanup)) return false;
|
||||
value[j][i] = t_cast.value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static handle from_cpp(la_type mat, rv_policy policy,
|
||||
cleanup_list* cleanup) noexcept {
|
||||
std::array<T, R * C> buffer;
|
||||
for (int i = 0; i < R; i++) {
|
||||
for (int j = 0; j < C; j++) {
|
||||
// py is (Rows, Cols), la is (Cols, Rows)
|
||||
buffer[i * C + j] = mat[j][i];
|
||||
}
|
||||
}
|
||||
numpy_type arr{buffer, {R, C}, nb::handle()};
|
||||
// we must copy the underlying data
|
||||
return make_caster<numpy_type>::from_cpp(arr, rv_policy::copy, cleanup);
|
||||
}
|
||||
};
|
||||
|
||||
// handle std::vector<la::vecN>
|
||||
template <class T, int N>
|
||||
struct nb::detail::type_caster<std::vector<la::vec<T, N>>> {
|
||||
using la_type = la::vec<T, N>;
|
||||
using numpy_type = nb::ndarray<nb::numpy, T, nb::shape<-1, N>>;
|
||||
NB_TYPE_CASTER(std::vector<la_type>,
|
||||
const_name(la_name<la_type>::multi_name));
|
||||
|
||||
bool from_python(handle src, uint8_t flags, cleanup_list* cleanup) noexcept {
|
||||
make_caster<numpy_type> arr_cast;
|
||||
if (arr_cast.from_python(src, flags, cleanup)) {
|
||||
size_t num_vec = arr_cast.value.shape(0);
|
||||
value.resize(num_vec);
|
||||
for (size_t i = 0; i < num_vec; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
value[i][j] = arr_cast.value(i, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t num_vec = PyObject_Size(src.ptr()); // negative on failure
|
||||
if (num_vec == static_cast<size_t>(-1)) return false;
|
||||
value.resize(num_vec);
|
||||
for (size_t i = 0; i < num_vec; i++) {
|
||||
make_caster<la_type> vec_cast;
|
||||
if (!vec_cast.from_python(src[i], flags, cleanup)) return false;
|
||||
value[i] = vec_cast.value;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static handle from_cpp(Value vec, rv_policy policy,
|
||||
cleanup_list* cleanup) noexcept {
|
||||
size_t num_vec = vec.size();
|
||||
T* buffer = new T[num_vec * N];
|
||||
nb::capsule mem_mgr(buffer, [](void* p) noexcept { delete[] (T*)p; });
|
||||
for (size_t i = 0; i < num_vec; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
buffer[i * N + j] = vec[i][j];
|
||||
}
|
||||
}
|
||||
numpy_type arr{buffer, {num_vec, N}, std::move(mem_mgr)};
|
||||
// we can just do a move because we already did the copying
|
||||
return make_caster<numpy_type>::from_cpp(arr, rv_policy::move, cleanup);
|
||||
}
|
||||
};
|
||||
|
||||
// handle VecView<la::vecN>
|
||||
template <class T, int N>
|
||||
struct nb::detail::type_caster<manifold::VecView<la::vec<T, N>>> {
|
||||
using la_type = la::vec<T, N>;
|
||||
using numpy_type = nb::ndarray<nb::numpy, T, nb::shape<-1, N>>;
|
||||
NB_TYPE_CASTER(manifold::VecView<la_type>,
|
||||
const_name(la_name<la_type>::multi_name));
|
||||
|
||||
static handle from_cpp(Value vec, rv_policy policy,
|
||||
cleanup_list* cleanup) noexcept {
|
||||
size_t num_vec = vec.size();
|
||||
// assume packed struct
|
||||
static_assert(alignof(la::vec<T, N>) <= (N * sizeof(T)),
|
||||
"VecView -> numpy requires packed structs");
|
||||
static_assert(sizeof(la::vec<T, N>) == (N * sizeof(T)),
|
||||
"VecView -> numpy requires packed structs");
|
||||
numpy_type arr{vec.data(), {num_vec, N}, nb::handle()};
|
||||
// we must copy the underlying data
|
||||
return make_caster<numpy_type>::from_cpp(arr, rv_policy::copy, cleanup);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> toVector(const T* arr, size_t size) {
|
||||
return std::vector<T>(arr, arr + size);
|
||||
}
|
||||
|
||||
using namespace manifold_docstrings;
|
||||
|
||||
// strip original :params: and replace with ours
|
||||
const std::string manifold__rotate_xyz =
|
||||
manifold__rotate__x_degrees__y_degrees__z_degrees;
|
||||
const std::string manifold__rotate__v =
|
||||
manifold__rotate_xyz.substr(0, manifold__rotate_xyz.find(":param")) +
|
||||
":param v: [X, Y, Z] rotation in degrees.";
|
||||
|
||||
NB_MODULE(manifold3d, m) {
|
||||
m.doc() = "Python binding for the Manifold library.";
|
||||
|
||||
m.def("set_min_circular_angle", Quality::SetMinCircularAngle,
|
||||
nb::arg("angle"), quality__set_min_circular_angle__angle);
|
||||
|
||||
m.def("set_min_circular_edge_length", Quality::SetMinCircularEdgeLength,
|
||||
nb::arg("length"), quality__set_min_circular_edge_length__length);
|
||||
|
||||
m.def("set_circular_segments", Quality::SetCircularSegments,
|
||||
nb::arg("number"), quality__set_circular_segments__number);
|
||||
|
||||
m.def("get_circular_segments", Quality::GetCircularSegments,
|
||||
nb::arg("radius"), quality__get_circular_segments__radius);
|
||||
|
||||
m.def("triangulate", &Triangulate, nb::arg("polygons"),
|
||||
nb::arg("epsilon") = -1, nb::arg("allow_convex") = true,
|
||||
triangulate__polygons__epsilon__allow_convex);
|
||||
|
||||
nb::class_<Manifold>(m, "Manifold")
|
||||
.def(nb::init<>(), manifold__manifold)
|
||||
.def(nb::init<const MeshGL&>(), nb::arg("mesh"),
|
||||
manifold__manifold__mesh_gl)
|
||||
.def(nb::init<const MeshGL64&>(), nb::arg("mesh"),
|
||||
manifold__manifold__mesh_gl64)
|
||||
.def(nb::self + nb::self, manifold__operator_plus__q)
|
||||
.def(nb::self - nb::self, manifold__operator_minus__q)
|
||||
.def(nb::self ^ nb::self, manifold__operator_xor__q)
|
||||
.def(
|
||||
"hull", [](const Manifold& self) { return self.Hull(); },
|
||||
manifold__hull)
|
||||
.def_static(
|
||||
"batch_hull",
|
||||
[](std::vector<Manifold> ms) { return Manifold::Hull(ms); },
|
||||
nb::arg("manifolds"), manifold__hull__manifolds)
|
||||
.def_static(
|
||||
"hull_points",
|
||||
[](std::vector<vec3> pts) { return Manifold::Hull(pts); },
|
||||
nb::arg("pts"), manifold__hull__pts)
|
||||
.def("transform", &Manifold::Transform, nb::arg("m"),
|
||||
manifold__transform__m)
|
||||
.def("translate", &Manifold::Translate, nb::arg("t"),
|
||||
manifold__translate__v)
|
||||
.def("scale", &Manifold::Scale, nb::arg("v"), manifold__scale__v)
|
||||
.def(
|
||||
"scale",
|
||||
[](const Manifold& self, double s) { self.Scale({s, s, s}); },
|
||||
nb::arg("s"),
|
||||
"Scale this Manifold in space. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily.\n\n"
|
||||
":param s: The scalar to multiply every vertex by component.")
|
||||
.def("mirror", &Manifold::Mirror, nb::arg("v"), manifold__mirror__normal)
|
||||
.def(
|
||||
"rotate",
|
||||
[](const Manifold& self, vec3 v) {
|
||||
return self.Rotate(v.x, v.y, v.z);
|
||||
},
|
||||
nb::arg("v"), manifold__rotate__v.c_str())
|
||||
.def(
|
||||
"warp",
|
||||
[](const Manifold& self, std::function<vec3(vec3)> warp_func) {
|
||||
// need a wrapper because python cant modify a reference in-place
|
||||
return self.Warp([&warp_func](vec3& v) { v = warp_func(v); });
|
||||
},
|
||||
nb::arg("warp_func"), manifold__warp__warp_func)
|
||||
.def(
|
||||
"warp_batch",
|
||||
[](const Manifold& self,
|
||||
std::function<nb::object(VecView<vec3>)> warp_func) {
|
||||
// need a wrapper because python cant modify a reference in-place
|
||||
return self.WarpBatch([&warp_func](VecView<vec3> v) {
|
||||
auto tmp = warp_func(v);
|
||||
nb::ndarray<double, nb::shape<-1, 3>, nanobind::c_contig> tmpnd;
|
||||
if (!nb::try_cast(tmp, tmpnd) || tmpnd.ndim() != 2)
|
||||
throw std::runtime_error(
|
||||
"Invalid vector shape, expected (:, 3)");
|
||||
std::copy(tmpnd.data(), tmpnd.data() + v.size() * 3,
|
||||
&v.data()->x);
|
||||
});
|
||||
},
|
||||
nb::arg("warp_func"), manifold__warp_batch__warp_func)
|
||||
.def(
|
||||
"set_properties",
|
||||
[](const Manifold& self, int newNumProp,
|
||||
const std::function<nb::object(
|
||||
vec3, const nb::ndarray<nb::numpy, const double,
|
||||
nb::c_contig>&)>& f) {
|
||||
const int oldNumProp = self.NumProp();
|
||||
return self.SetProperties(newNumProp, [newNumProp, oldNumProp, &f](
|
||||
double* newProps, vec3 v,
|
||||
const double* oldProps) {
|
||||
auto result =
|
||||
f(v, nb::ndarray<nb::numpy, const double, nb::c_contig>(
|
||||
oldProps, {static_cast<unsigned long>(oldNumProp)},
|
||||
nb::handle()));
|
||||
nb::ndarray<double, nb::shape<-1>> array;
|
||||
std::vector<double> vec;
|
||||
if (nb::try_cast(result, array)) {
|
||||
if (array.ndim() != 1 ||
|
||||
array.shape(0) != static_cast<size_t>(newNumProp))
|
||||
throw std::runtime_error("Invalid vector shape, expected (" +
|
||||
std::to_string(newNumProp) + ")");
|
||||
for (int i = 0; i < newNumProp; i++) newProps[i] = array(i);
|
||||
} else if (nb::try_cast(result, vec)) {
|
||||
for (int i = 0; i < newNumProp; i++) newProps[i] = vec[i];
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"Callback in set_properties should return an array");
|
||||
}
|
||||
});
|
||||
},
|
||||
nb::arg("new_num_prop"), nb::arg("f"),
|
||||
manifold__set_properties__num_prop__prop_func)
|
||||
.def("calculate_curvature", &Manifold::CalculateCurvature,
|
||||
nb::arg("gaussian_idx"), nb::arg("mean_idx"),
|
||||
manifold__calculate_curvature__gaussian_idx__mean_idx)
|
||||
.def("min_gap", &Manifold::MinGap, nb::arg("other"),
|
||||
nb::arg("search_length"),
|
||||
"Returns the minimum gap between two manifolds."
|
||||
"Returns a double between 0 and searchLength.")
|
||||
.def("ray_cast", &Manifold::RayCast, nb::arg("origin"),
|
||||
nb::arg("endpoint"),
|
||||
"Cast a ray segment, returning all hits sorted by distance.")
|
||||
.def("winding_number", &Manifold::WindingNumber, nb::arg("points"),
|
||||
"Returns the winding number of this manifold around each of the "
|
||||
"given points. 0 means outside, non-zero means inside.")
|
||||
.def("calculate_normals", &Manifold::CalculateNormals,
|
||||
nb::arg("normal_idx") = 0, nb::arg("min_sharp_angle") = 52.5,
|
||||
manifold__calculate_normals__normal_idx__min_sharp_angle)
|
||||
.def("smooth_by_normals", &Manifold::SmoothByNormals,
|
||||
nb::arg("normal_idx") = 0, manifold__smooth_by_normals__normal_idx)
|
||||
.def("refine", &Manifold::Refine, nb::arg("n"), manifold__refine__n)
|
||||
.def("refine_to_length", &Manifold::RefineToLength, nb::arg("length"),
|
||||
manifold__refine_to_length__length)
|
||||
.def("refine_to_tolerance", &Manifold::RefineToTolerance,
|
||||
nb::arg("tolerance"), manifold__refine_to_tolerance__tolerance)
|
||||
.def("to_mesh", &Manifold::GetMeshGL, nb::arg("normal_idx") = -1,
|
||||
manifold__get_mesh_gl__normal_idx)
|
||||
.def("to_mesh64", &Manifold::GetMeshGL64, nb::arg("normal_idx") = -1,
|
||||
manifold__get_mesh_gl64__normal_idx)
|
||||
.def("num_vert", &Manifold::NumVert, manifold__num_vert)
|
||||
.def("num_edge", &Manifold::NumEdge, manifold__num_edge)
|
||||
.def("num_tri", &Manifold::NumTri, manifold__num_tri)
|
||||
.def("num_prop", &Manifold::NumProp, manifold__num_prop)
|
||||
.def("num_prop_vert", &Manifold::NumPropVert, manifold__num_prop_vert)
|
||||
.def("genus", &Manifold::Genus, manifold__genus)
|
||||
.def(
|
||||
"volume", [](const Manifold& self) { return self.Volume(); },
|
||||
"Get the volume of the manifold\n This is clamped to zero for a "
|
||||
"given face if they are within the Epsilon().")
|
||||
.def(
|
||||
"surface_area",
|
||||
[](const Manifold& self) { return self.SurfaceArea(); },
|
||||
"Get the surface area of the manifold\n This is clamped to zero for "
|
||||
"a given face if they are within the Epsilon().")
|
||||
.def("original_id", &Manifold::OriginalID, manifold__original_id)
|
||||
.def("get_tolerance", &Manifold::GetTolerance, manifold__get_tolerance)
|
||||
.def("set_tolerance", &Manifold::SetTolerance,
|
||||
manifold__set_tolerance__tolerance)
|
||||
.def("simplify", &Manifold::Simplify, manifold__simplify__tolerance)
|
||||
.def("as_original", &Manifold::AsOriginal, manifold__as_original)
|
||||
.def("is_empty", &Manifold::IsEmpty, manifold__is_empty)
|
||||
.def("decompose", &Manifold::Decompose, manifold__decompose)
|
||||
.def("split", &Manifold::Split, nb::arg("cutter"),
|
||||
manifold__split__cutter)
|
||||
.def("split_by_plane", &Manifold::SplitByPlane, nb::arg("normal"),
|
||||
nb::arg("origin_offset"),
|
||||
manifold__split_by_plane__normal__origin_offset)
|
||||
.def("trim_by_plane", &Manifold::TrimByPlane, nb::arg("normal"),
|
||||
nb::arg("origin_offset"),
|
||||
manifold__trim_by_plane__normal__origin_offset)
|
||||
.def("minkowski_sum", &Manifold::MinkowskiSum, nb::arg("other"),
|
||||
manifold__minkowski_sum__other)
|
||||
.def("minkowski_difference", &Manifold::MinkowskiDifference,
|
||||
nb::arg("other"), manifold__minkowski_difference__other)
|
||||
.def(
|
||||
"slice",
|
||||
[](const Manifold& self, double height) {
|
||||
return CrossSection(self.Slice(height));
|
||||
},
|
||||
nb::arg("height"), manifold__slice__height)
|
||||
.def(
|
||||
"project",
|
||||
[](const Manifold& self) {
|
||||
return CrossSection(self.Project()).Simplify(self.GetEpsilon());
|
||||
},
|
||||
manifold__project)
|
||||
.def("status", &Manifold::Status, manifold__status)
|
||||
.def("with_context", &Manifold::WithContext, nb::arg("ctx"),
|
||||
manifold__with_context__ctx)
|
||||
.def(
|
||||
"bounding_box",
|
||||
[](const Manifold& self) {
|
||||
Box b = self.BoundingBox();
|
||||
return nb::make_tuple(b.min[0], b.min[1], b.min[2], b.max[0],
|
||||
b.max[1], b.max[2]);
|
||||
},
|
||||
"Gets the manifold bounding box as a tuple "
|
||||
"(xmin, ymin, zmin, xmax, ymax, zmax).")
|
||||
.def_static("batch_boolean", &Manifold::BatchBoolean,
|
||||
nb::arg("manifolds"), nb::arg("op"),
|
||||
manifold__batch_boolean__manifolds__op)
|
||||
.def_static(
|
||||
"compose",
|
||||
[](const std::vector<Manifold>& manifolds) {
|
||||
return Manifold::BatchBoolean(manifolds, OpType::Add);
|
||||
},
|
||||
nb::arg("manifolds"), manifold__compose__manifolds)
|
||||
.def_static("tetrahedron", &Manifold::Tetrahedron, manifold__tetrahedron)
|
||||
.def_static("cube", &Manifold::Cube,
|
||||
nb::arg("size") = std::make_tuple(1.0, 1.0, 1.0),
|
||||
nb::arg("center") = false, manifold__cube__size__center)
|
||||
.def_static(
|
||||
"extrude",
|
||||
[](const CrossSection& crossSection, double height, int nDivisions,
|
||||
double twistDegrees, vec2 scaleTop) {
|
||||
return Manifold::Extrude(crossSection.ToPolygons(), height,
|
||||
nDivisions, twistDegrees, scaleTop);
|
||||
},
|
||||
nb::arg("crossSection"), nb::arg("height"),
|
||||
nb::arg("n_divisions") = 0, nb::arg("twist_degrees") = 0.0f,
|
||||
nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f),
|
||||
manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top)
|
||||
.def_static(
|
||||
"revolve",
|
||||
[](const CrossSection& crossSection, int circularSegments,
|
||||
double revolveDegrees) {
|
||||
return Manifold::Revolve(crossSection.ToPolygons(),
|
||||
circularSegments, revolveDegrees);
|
||||
},
|
||||
nb::arg("crossSection"), nb::arg("circular_segments") = 0,
|
||||
nb::arg("revolve_degrees") = 360.0,
|
||||
manifold__revolve__cross_section__circular_segments__revolve_degrees)
|
||||
.def_static(
|
||||
"level_set",
|
||||
[](const std::function<double(double, double, double)>& f,
|
||||
std::vector<double> bounds, double edgeLength, double level = 0.0,
|
||||
double tolerance = -1) {
|
||||
// Same format as Manifold.bounding_box
|
||||
Box bound = {vec3(bounds[0], bounds[1], bounds[2]),
|
||||
vec3(bounds[3], bounds[4], bounds[5])};
|
||||
|
||||
std::function<double(vec3)> cppToPython = [&f](vec3 v) {
|
||||
return f(v.x, v.y, v.z);
|
||||
};
|
||||
return Manifold::LevelSet(cppToPython, bound, edgeLength, level,
|
||||
tolerance, false);
|
||||
},
|
||||
nb::arg("f"), nb::arg("bounds"), nb::arg("edgeLength"),
|
||||
nb::arg("level") = 0.0, nb::arg("tolerance") = -1,
|
||||
manifold__level_set__sdf__bounds__edge_length__level__tolerance__can_parallel)
|
||||
.def_static(
|
||||
"cylinder", &Manifold::Cylinder, nb::arg("height"),
|
||||
nb::arg("radius_low"), nb::arg("radius_high") = -1.0f,
|
||||
nb::arg("circular_segments") = 0, nb::arg("center") = false,
|
||||
manifold__cylinder__height__radius_low__radius_high__circular_segments__center)
|
||||
.def_static("sphere", &Manifold::Sphere, nb::arg("radius"),
|
||||
nb::arg("circular_segments") = 0,
|
||||
manifold__sphere__radius__circular_segments)
|
||||
.def_static("reserve_ids", Manifold::ReserveIDs, nb::arg("n"),
|
||||
manifold__reserve_ids__n);
|
||||
|
||||
nb::class_<MeshGL>(m, "Mesh")
|
||||
.def(
|
||||
// note that reshape requires mutable ndarray, but this will not
|
||||
// affect the original array passed into the function
|
||||
"__init__",
|
||||
[](MeshGL* self,
|
||||
nb::ndarray<float, nb::shape<-1, -1>, nb::c_contig>& vertProp,
|
||||
nb::ndarray<uint32_t, nb::shape<-1, 3>, nb::c_contig>& triVerts,
|
||||
const std::optional<nb::ndarray<uint32_t, nb::shape<-1>,
|
||||
nb::c_contig>>& mergeFromVert,
|
||||
const std::optional<nb::ndarray<uint32_t, nb::shape<-1>,
|
||||
nb::c_contig>>& mergeToVert,
|
||||
const std::optional<
|
||||
nb::ndarray<uint32_t, nb::shape<-1>, nb::c_contig>>& runIndex,
|
||||
const std::optional<nb::ndarray<uint32_t, nb::shape<-1>,
|
||||
nb::c_contig>>& runOriginalID,
|
||||
std::optional<nb::ndarray<float, nb::shape<-1, 4, 3>,
|
||||
nb::c_contig>>& runTransform,
|
||||
const std::optional<
|
||||
nb::ndarray<uint8_t, nb::shape<-1>, nb::c_contig>>& runFlags,
|
||||
const std::optional<
|
||||
nb::ndarray<uint32_t, nb::shape<-1>, nb::c_contig>>& faceID,
|
||||
const std::optional<nb::ndarray<float, nb::shape<-1, 3, 4>,
|
||||
nb::c_contig>>& halfedgeTangent,
|
||||
float tolerance) {
|
||||
new (self) MeshGL();
|
||||
MeshGL& out = *self;
|
||||
out.numProp = vertProp.shape(1);
|
||||
out.vertProperties = toVector(vertProp.data(), vertProp.size());
|
||||
|
||||
out.triVerts = toVector(triVerts.data(), triVerts.size());
|
||||
|
||||
if (mergeFromVert.has_value())
|
||||
out.mergeFromVert =
|
||||
toVector(mergeFromVert->data(), mergeFromVert->size());
|
||||
|
||||
if (mergeToVert.has_value())
|
||||
out.mergeToVert =
|
||||
toVector(mergeToVert->data(), mergeToVert->size());
|
||||
|
||||
if (runIndex.has_value())
|
||||
out.runIndex = toVector(runIndex->data(), runIndex->size());
|
||||
|
||||
if (runOriginalID.has_value())
|
||||
out.runOriginalID =
|
||||
toVector(runOriginalID->data(), runOriginalID->size());
|
||||
|
||||
if (runTransform.has_value()) {
|
||||
out.runTransform =
|
||||
toVector(runTransform->data(), runTransform->size());
|
||||
}
|
||||
|
||||
if (runFlags.has_value()) {
|
||||
out.runFlags = toVector(runFlags->data(), runFlags->size());
|
||||
}
|
||||
|
||||
if (faceID.has_value())
|
||||
out.faceID = toVector(faceID->data(), faceID->size());
|
||||
|
||||
if (halfedgeTangent.has_value()) {
|
||||
out.halfedgeTangent =
|
||||
toVector(halfedgeTangent->data(), halfedgeTangent->size());
|
||||
}
|
||||
},
|
||||
nb::arg("vert_properties"), nb::arg("tri_verts"),
|
||||
nb::arg("merge_from_vert") = nb::none(),
|
||||
nb::arg("merge_to_vert") = nb::none(),
|
||||
nb::arg("run_index") = nb::none(),
|
||||
nb::arg("run_original_id") = nb::none(),
|
||||
nb::arg("run_transform") = nb::none(),
|
||||
nb::arg("run_flags") = nb::none(), nb::arg("face_id") = nb::none(),
|
||||
nb::arg("halfedge_tangent") = nb::none(), nb::arg("tolerance") = 0)
|
||||
.def_prop_ro(
|
||||
"vert_properties",
|
||||
[](const MeshGL& self) {
|
||||
return nb::ndarray<nb::numpy, const float, nb::c_contig>(
|
||||
self.vertProperties.data(),
|
||||
{self.vertProperties.size() / self.numProp, self.numProp},
|
||||
nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_prop_ro(
|
||||
"tri_verts",
|
||||
[](const MeshGL& self) {
|
||||
return nb::ndarray<nb::numpy, const int, nb::c_contig>(
|
||||
self.triVerts.data(), {self.triVerts.size() / 3, 3},
|
||||
nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_prop_ro(
|
||||
"run_transform",
|
||||
[](const MeshGL& self) {
|
||||
return nb::ndarray<nb::numpy, const float, nb::c_contig>(
|
||||
self.runTransform.data(), {self.runTransform.size() / 12, 4, 3},
|
||||
nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_prop_ro(
|
||||
"halfedge_tangent",
|
||||
[](const MeshGL& self) {
|
||||
return nb::ndarray<nb::numpy, const float, nb::c_contig>(
|
||||
self.halfedgeTangent.data(),
|
||||
{self.halfedgeTangent.size() / 12, 3, 4}, nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_ro("merge_from_vert", &MeshGL::mergeFromVert)
|
||||
.def_ro("merge_to_vert", &MeshGL::mergeToVert)
|
||||
.def_ro("run_index", &MeshGL::runIndex)
|
||||
.def_ro("run_original_id", &MeshGL::runOriginalID)
|
||||
.def_ro("run_flags", &MeshGL::runFlags)
|
||||
.def_ro("face_id", &MeshGL::faceID)
|
||||
.def("backside", &MeshGL::Backside, nb::arg("run"))
|
||||
.def("has_normals", &MeshGL::HasNormals, nb::arg("run"))
|
||||
.def("merge", &MeshGL::Merge, mesh_gl__merge);
|
||||
|
||||
nb::class_<MeshGL64>(m, "Mesh64")
|
||||
.def(
|
||||
// note that reshape requires mutable ndarray, but this will not
|
||||
// affect the original array passed into the function
|
||||
"__init__",
|
||||
[](MeshGL64* self,
|
||||
nb::ndarray<double, nb::shape<-1, -1>, nb::c_contig>& vertProp,
|
||||
nb::ndarray<uint64_t, nb::shape<-1, 3>, nb::c_contig>& triVerts,
|
||||
const std::optional<nb::ndarray<uint64_t, nb::shape<-1>,
|
||||
nb::c_contig>>& mergeFromVert,
|
||||
const std::optional<nb::ndarray<uint64_t, nb::shape<-1>,
|
||||
nb::c_contig>>& mergeToVert,
|
||||
const std::optional<
|
||||
nb::ndarray<uint64_t, nb::shape<-1>, nb::c_contig>>& runIndex,
|
||||
const std::optional<nb::ndarray<uint32_t, nb::shape<-1>,
|
||||
nb::c_contig>>& runOriginalID,
|
||||
std::optional<nb::ndarray<double, nb::shape<-1, 4, 3>,
|
||||
nb::c_contig>>& runTransform,
|
||||
const std::optional<
|
||||
nb::ndarray<uint8_t, nb::shape<-1>, nb::c_contig>>& runFlags,
|
||||
const std::optional<
|
||||
nb::ndarray<uint64_t, nb::shape<-1>, nb::c_contig>>& faceID,
|
||||
const std::optional<nb::ndarray<double, nb::shape<-1, 3, 4>,
|
||||
nb::c_contig>>& halfedgeTangent,
|
||||
float tolerance) {
|
||||
new (self) MeshGL64();
|
||||
MeshGL64& out = *self;
|
||||
out.numProp = vertProp.shape(1);
|
||||
out.vertProperties = toVector(vertProp.data(), vertProp.size());
|
||||
|
||||
out.triVerts = toVector(triVerts.data(), triVerts.size());
|
||||
|
||||
if (mergeFromVert.has_value())
|
||||
out.mergeFromVert =
|
||||
toVector(mergeFromVert->data(), mergeFromVert->size());
|
||||
|
||||
if (mergeToVert.has_value())
|
||||
out.mergeToVert =
|
||||
toVector(mergeToVert->data(), mergeToVert->size());
|
||||
|
||||
if (runIndex.has_value())
|
||||
out.runIndex = toVector(runIndex->data(), runIndex->size());
|
||||
|
||||
if (runOriginalID.has_value())
|
||||
out.runOriginalID =
|
||||
toVector(runOriginalID->data(), runOriginalID->size());
|
||||
|
||||
if (runTransform.has_value()) {
|
||||
out.runTransform =
|
||||
toVector(runTransform->data(), runTransform->size());
|
||||
}
|
||||
|
||||
if (runFlags.has_value()) {
|
||||
out.runFlags = toVector(runFlags->data(), runFlags->size());
|
||||
}
|
||||
|
||||
if (faceID.has_value())
|
||||
out.faceID = toVector(faceID->data(), faceID->size());
|
||||
|
||||
if (halfedgeTangent.has_value()) {
|
||||
out.halfedgeTangent =
|
||||
toVector(halfedgeTangent->data(), halfedgeTangent->size());
|
||||
}
|
||||
},
|
||||
nb::arg("vert_properties"), nb::arg("tri_verts"),
|
||||
nb::arg("merge_from_vert") = nb::none(),
|
||||
nb::arg("merge_to_vert") = nb::none(),
|
||||
nb::arg("run_index") = nb::none(),
|
||||
nb::arg("run_original_id") = nb::none(),
|
||||
nb::arg("run_transform") = nb::none(),
|
||||
nb::arg("run_flags") = nb::none(), nb::arg("face_id") = nb::none(),
|
||||
nb::arg("halfedge_tangent") = nb::none(), nb::arg("tolerance") = 0)
|
||||
.def_prop_ro(
|
||||
"vert_properties",
|
||||
[](const MeshGL64& self) {
|
||||
return nb::ndarray<nb::numpy, const double, nb::c_contig>(
|
||||
self.vertProperties.data(),
|
||||
{self.vertProperties.size() / self.numProp, self.numProp},
|
||||
nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_prop_ro(
|
||||
"tri_verts",
|
||||
[](const MeshGL64& self) {
|
||||
return nb::ndarray<nb::numpy, const uint64_t, nb::c_contig>(
|
||||
self.triVerts.data(), {self.triVerts.size() / 3, 3},
|
||||
nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_prop_ro(
|
||||
"run_transform",
|
||||
[](const MeshGL64& self) {
|
||||
return nb::ndarray<nb::numpy, const double, nb::c_contig>(
|
||||
self.runTransform.data(), {self.runTransform.size() / 12, 4, 3},
|
||||
nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_prop_ro(
|
||||
"halfedge_tangent",
|
||||
[](const MeshGL64& self) {
|
||||
return nb::ndarray<nb::numpy, const double, nb::c_contig>(
|
||||
self.halfedgeTangent.data(),
|
||||
{self.halfedgeTangent.size() / 12, 3, 4}, nb::handle());
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def_ro("merge_from_vert", &MeshGL64::mergeFromVert)
|
||||
.def_ro("merge_to_vert", &MeshGL64::mergeToVert)
|
||||
.def_ro("run_index", &MeshGL64::runIndex)
|
||||
.def_ro("run_original_id", &MeshGL64::runOriginalID)
|
||||
.def_ro("run_flags", &MeshGL64::runFlags)
|
||||
.def_ro("face_id", &MeshGL64::faceID)
|
||||
.def("backside", &MeshGL64::Backside, nb::arg("run"))
|
||||
.def("has_normals", &MeshGL64::HasNormals, nb::arg("run"))
|
||||
.def("merge", &MeshGL64::Merge, mesh_gl__merge);
|
||||
|
||||
nb::class_<RayHit>(m, "RayHit")
|
||||
.def_ro("face_id", &RayHit::faceID)
|
||||
.def_ro("distance", &RayHit::distance)
|
||||
.def_ro("position", &RayHit::position)
|
||||
.def_ro("normal", &RayHit::normal);
|
||||
|
||||
nb::class_<ExecutionContext>(m, "ExecutionContext")
|
||||
.def(nb::init<>())
|
||||
.def("cancel", &ExecutionContext::Cancel)
|
||||
.def("cancelled", &ExecutionContext::Cancelled)
|
||||
.def("progress", &ExecutionContext::Progress)
|
||||
// ctx-aware static factories: like Manifold.from_mesh / level_set, but
|
||||
// run under this context so progress / cancellation are observed (these
|
||||
// ops have no source Manifold to attach via with_context).
|
||||
.def(
|
||||
"from_mesh",
|
||||
[](ExecutionContext& ctx, const MeshGL& mesh) {
|
||||
return ctx.FromMeshGL(mesh);
|
||||
},
|
||||
nb::arg("mesh"),
|
||||
"Ingest a MeshGL into a Manifold under this context, so the ingest "
|
||||
"reports progress and observes cancellation.")
|
||||
.def(
|
||||
"from_mesh",
|
||||
[](ExecutionContext& ctx, const MeshGL64& mesh) {
|
||||
return ctx.FromMeshGL(mesh);
|
||||
},
|
||||
nb::arg("mesh"),
|
||||
"Ingest a MeshGL64 into a Manifold under this context, so the ingest "
|
||||
"reports progress and observes cancellation.")
|
||||
.def(
|
||||
"level_set",
|
||||
[](ExecutionContext& ctx,
|
||||
const std::function<double(double, double, double)>& f,
|
||||
std::vector<double> bounds, double edgeLength, double level,
|
||||
double tolerance) {
|
||||
Box bound = {vec3(bounds[0], bounds[1], bounds[2]),
|
||||
vec3(bounds[3], bounds[4], bounds[5])};
|
||||
std::function<double(vec3)> cppToPython = [&f](vec3 v) {
|
||||
return f(v.x, v.y, v.z);
|
||||
};
|
||||
return ctx.LevelSet(cppToPython, bound, edgeLength, level,
|
||||
tolerance, false);
|
||||
},
|
||||
nb::arg("f"), nb::arg("bounds"), nb::arg("edgeLength"),
|
||||
nb::arg("level") = 0.0, nb::arg("tolerance") = -1,
|
||||
"Like Manifold.level_set, run under this context so the level-set "
|
||||
"evaluation reports progress and observes cancellation.");
|
||||
|
||||
nb::enum_<Manifold::Error>(m, "Error")
|
||||
.value("NoError", Manifold::Error::NoError)
|
||||
.value("NonFiniteVertex", Manifold::Error::NonFiniteVertex)
|
||||
.value("NotManifold", Manifold::Error::NotManifold)
|
||||
.value("VertexOutOfBounds", Manifold::Error::VertexOutOfBounds)
|
||||
.value("PropertiesWrongLength", Manifold::Error::PropertiesWrongLength)
|
||||
.value("MissingPositionProperties",
|
||||
Manifold::Error::MissingPositionProperties)
|
||||
.value("MergeVectorsDifferentLengths",
|
||||
Manifold::Error::MergeVectorsDifferentLengths)
|
||||
.value("MergeIndexOutOfBounds", Manifold::Error::MergeIndexOutOfBounds)
|
||||
.value("TransformWrongLength", Manifold::Error::TransformWrongLength)
|
||||
.value("RunIndexWrongLength", Manifold::Error::RunIndexWrongLength)
|
||||
.value("FaceIDWrongLength", Manifold::Error::FaceIDWrongLength)
|
||||
.value("InvalidConstruction", Manifold::Error::InvalidConstruction)
|
||||
.value("ResultTooLarge", Manifold::Error::ResultTooLarge)
|
||||
.value("InvalidTangents", Manifold::Error::InvalidTangents)
|
||||
.value("Cancelled", Manifold::Error::Cancelled);
|
||||
|
||||
nb::enum_<CrossSection::JoinType>(m, "JoinType")
|
||||
.value("Square", CrossSection::JoinType::Square,
|
||||
"Squaring is applied uniformly at all joins where the internal "
|
||||
"join angle is less that 90 degrees. The squared edge will be at "
|
||||
"exactly the offset distance from the join vertex.")
|
||||
.value("Round", CrossSection::JoinType::Round,
|
||||
"Rounding is applied to all joins that have convex external "
|
||||
"angles, and it maintains the exact offset distance from the join "
|
||||
"vertex.")
|
||||
.value(
|
||||
"Miter", CrossSection::JoinType::Miter,
|
||||
"There's a necessary limit to mitered joins (to avoid narrow "
|
||||
"angled joins producing excessively long and narrow "
|
||||
"[spikes](http://www.angusj.com/clipper2/Docs/Units/Clipper.Offset/"
|
||||
"Classes/ClipperOffset/Properties/MiterLimit.htm)). So where "
|
||||
"mitered joins would exceed a given maximum miter distance "
|
||||
"(relative to the offset distance), these are 'squared' instead.")
|
||||
.value("Bevel", CrossSection::JoinType::Bevel,
|
||||
"Bevelled joins are similar to 'squared' joins except that "
|
||||
"squaring won't occur at a fixed distance. While bevelled joins "
|
||||
"may not be as pretty as squared joins, bevelling is much easier "
|
||||
"(ie faster) than squaring. And perhaps this is why bevelling "
|
||||
"rather than squaring is preferred in numerous graphics display "
|
||||
"formats (including [SVG](https://developer.mozilla.org/en-US/"
|
||||
"docs/Web/SVG/Attribute/stroke-linejoin) and [PDF]"
|
||||
"(https://helpx.adobe.com/indesign/using/"
|
||||
"applying-line-stroke-settings.html) document formats).");
|
||||
|
||||
nb::enum_<OpType>(m, "OpType", "Operation types for batch_boolean")
|
||||
.value("Add", OpType::Add)
|
||||
.value("Subtract", OpType::Subtract)
|
||||
.value("Intersect", OpType::Intersect);
|
||||
|
||||
nb::class_<CrossSection>(
|
||||
m, "CrossSection",
|
||||
"Two-dimensional cross sections guaranteed to be without "
|
||||
"self-intersections, or overlaps between polygons (from construction "
|
||||
"onwards). Polygon clipping (boolean) and offsetting use Manifold's own "
|
||||
"robust floating-point predicates.")
|
||||
.def(nb::init<>(), cross_section__cross_section)
|
||||
.def(nb::init<std::vector<std::vector<vec2>>>(), nb::arg("contours"),
|
||||
cross_section__cross_section__contours)
|
||||
.def_static("even_odd",
|
||||
nb::overload_cast<const Polygons&>(&CrossSection::EvenOdd),
|
||||
nb::arg("contours"), cross_section__even_odd__contours)
|
||||
.def("area", &CrossSection::Area, cross_section__area)
|
||||
.def("num_vert", &CrossSection::NumVert, cross_section__num_vert)
|
||||
.def("num_contour", &CrossSection::NumContour, cross_section__num_contour)
|
||||
.def("is_empty", &CrossSection::IsEmpty, cross_section__is_empty)
|
||||
.def(
|
||||
"bounds",
|
||||
[](const CrossSection& self) {
|
||||
Rect r = self.Bounds();
|
||||
return nb::make_tuple(r.min[0], r.min[1], r.max[0], r.max[1]);
|
||||
},
|
||||
"Return bounding box of CrossSection as tuple("
|
||||
"min_x, min_y, max_x, max_y)")
|
||||
.def("translate", &CrossSection::Translate, nb::arg("v"),
|
||||
cross_section__translate__v)
|
||||
.def("rotate", &CrossSection::Rotate, nb::arg("degrees"),
|
||||
cross_section__rotate__degrees)
|
||||
.def("scale", &CrossSection::Scale, nb::arg("scale"),
|
||||
cross_section__scale__scale)
|
||||
.def(
|
||||
"scale",
|
||||
[](const CrossSection& self, double s) { self.Scale({s, s}); },
|
||||
nb::arg("s"),
|
||||
"Scale this CrossSection in space. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param s: The scalar to multiply every vertex by per component.")
|
||||
.def("mirror", &CrossSection::Mirror, nb::arg("ax"),
|
||||
cross_section__mirror__ax)
|
||||
.def("transform", &CrossSection::Transform, nb::arg("m"),
|
||||
cross_section__transform__m)
|
||||
.def(
|
||||
"warp",
|
||||
[](const CrossSection& self, std::function<vec2(vec2)> warp_func) {
|
||||
// need a wrapper because python cant modify a reference in-place
|
||||
return self.Warp([&warp_func](vec2& v) { v = warp_func(v); });
|
||||
},
|
||||
nb::arg("warp_func"), cross_section__warp__warp_func)
|
||||
.def("warp_batch", &CrossSection::WarpBatch, nb::arg("warp_func"),
|
||||
cross_section__warp_batch__warp_func)
|
||||
|
||||
.def(
|
||||
"warp_batch",
|
||||
[](const CrossSection& self,
|
||||
std::function<nb::object(VecView<vec2>)> warp_func) {
|
||||
// need a wrapper because python cant modify a reference in-place
|
||||
return self.WarpBatch([&warp_func](VecView<vec2> v) {
|
||||
auto tmp = warp_func(v);
|
||||
nb::ndarray<double, nb::shape<-1, 2>, nanobind::c_contig> tmpnd;
|
||||
if (!nb::try_cast(tmp, tmpnd) || tmpnd.ndim() != 2)
|
||||
throw std::runtime_error(
|
||||
"Invalid vector shape, expected (:, 2)");
|
||||
std::copy(tmpnd.data(), tmpnd.data() + v.size() * 2,
|
||||
&v.data()->x);
|
||||
});
|
||||
},
|
||||
nb::arg("warp_func"), cross_section__warp_batch__warp_func)
|
||||
.def("simplify", &CrossSection::Simplify, nb::arg("tolerance") = 0,
|
||||
cross_section__simplify__tolerance)
|
||||
.def("get_tolerance", &CrossSection::GetTolerance,
|
||||
cross_section__get_tolerance)
|
||||
.def("set_tolerance", &CrossSection::SetTolerance, nb::arg("tolerance"),
|
||||
cross_section__set_tolerance__tolerance)
|
||||
.def(
|
||||
"offset", &CrossSection::Offset, nb::arg("delta"),
|
||||
nb::arg("join_type") = CrossSection::JoinType::Round,
|
||||
nb::arg("miter_limit") = 2.0, nb::arg("circular_segments") = 0,
|
||||
cross_section__offset__delta__jointype__miter_limit__circular_segments)
|
||||
.def(nb::self + nb::self, cross_section__operator_plus__q)
|
||||
.def(nb::self - nb::self, cross_section__operator_minus__q)
|
||||
.def(nb::self ^ nb::self, cross_section__operator_xor__q)
|
||||
.def(
|
||||
"hull", [](const CrossSection& self) { return self.Hull(); },
|
||||
cross_section__hull)
|
||||
.def_static(
|
||||
"batch_hull",
|
||||
[](std::vector<CrossSection> cs) { return CrossSection::Hull(cs); },
|
||||
nb::arg("cross_sections"), cross_section__hull__cross_sections)
|
||||
.def_static(
|
||||
"hull_points",
|
||||
[](std::vector<vec2> pts) { return CrossSection::Hull(pts); },
|
||||
nb::arg("pts"), cross_section__hull__pts)
|
||||
.def("decompose", &CrossSection::Decompose, cross_section__decompose)
|
||||
.def_static("batch_boolean", &CrossSection::BatchBoolean,
|
||||
nb::arg("cross_sections"), nb::arg("op"),
|
||||
cross_section__batch_boolean__cross_sections__op)
|
||||
.def("to_polygons", &CrossSection::ToPolygons, cross_section__to_polygons)
|
||||
.def(
|
||||
"extrude",
|
||||
[](const CrossSection& self, double height, int nDivisions,
|
||||
double twistDegrees, vec2 scaleTop) {
|
||||
return Manifold::Extrude(self.ToPolygons(), height, nDivisions,
|
||||
twistDegrees, scaleTop);
|
||||
},
|
||||
nb::arg("height"), nb::arg("n_divisions") = 0,
|
||||
nb::arg("twist_degrees") = 0.0f,
|
||||
nb::arg("scale_top") = std::make_tuple(1.0f, 1.0f),
|
||||
manifold__extrude__cross_section__height__n_divisions__twist_degrees__scale_top)
|
||||
.def(
|
||||
"revolve",
|
||||
[](const CrossSection& self, int circularSegments,
|
||||
double revolveDegrees) {
|
||||
return Manifold::Revolve(self.ToPolygons(), circularSegments,
|
||||
revolveDegrees);
|
||||
},
|
||||
nb::arg("circular_segments") = 0, nb::arg("revolve_degrees") = 360.0,
|
||||
manifold__revolve__cross_section__circular_segments__revolve_degrees)
|
||||
|
||||
.def_static("square", &CrossSection::Square, nb::arg("size"),
|
||||
nb::arg("center") = false,
|
||||
cross_section__square__size__center)
|
||||
.def_static("circle", &CrossSection::Circle, nb::arg("radius"),
|
||||
nb::arg("circular_segments") = 0,
|
||||
cross_section__circle__radius__circular_segments);
|
||||
}
|
||||
@@ -1,738 +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.
|
||||
|
||||
#include "cross_section.h"
|
||||
#include "manifold.h"
|
||||
#include "pybind11/functional.h"
|
||||
#include "pybind11/numpy.h"
|
||||
#include "pybind11/operators.h"
|
||||
#include "pybind11/pybind11.h"
|
||||
#include "pybind11/stl.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
using namespace manifold;
|
||||
|
||||
typedef std::tuple<float, float> Float2;
|
||||
typedef std::tuple<float, float, float> Float3;
|
||||
|
||||
struct PolygonsWrapper {
|
||||
std::unique_ptr<Polygons> polygons;
|
||||
};
|
||||
|
||||
PYBIND11_MODULE(pymanifold, m) {
|
||||
m.doc() =
|
||||
"Python binding for the manifold library. Please check the C++ "
|
||||
"documentation for APIs.\n"
|
||||
"This binding will perform copying to make the API more familiar to "
|
||||
"OpenSCAD users.";
|
||||
|
||||
m.def("set_min_circular_angle", Quality::SetMinCircularAngle,
|
||||
"Sets an angle constraint the default number of circular segments for "
|
||||
"the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), "
|
||||
"and Manifold::Revolve() constructors. The number of segments will be "
|
||||
"rounded up to the nearest factor of four."
|
||||
"\n\n"
|
||||
"@param angle The minimum angle in degrees between consecutive "
|
||||
"segments. The angle will increase if the the segments hit the minimum "
|
||||
"edge length.\n"
|
||||
"Default is 10 degrees.");
|
||||
|
||||
m.def("set_min_circular_edge_length", Quality::SetMinCircularEdgeLength,
|
||||
"Sets a length constraint the default number of circular segments for "
|
||||
"the CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), "
|
||||
"and Manifold::Revolve() constructors. The number of segments will be "
|
||||
"rounded up to the nearest factor of four."
|
||||
"\n\n"
|
||||
"@param length The minimum length of segments. The length will "
|
||||
"increase if the the segments hit the minimum angle. Default is 1.0.");
|
||||
|
||||
m.def("set_circular_segments", Quality::SetCircularSegments,
|
||||
"Sets the default number of circular segments for the "
|
||||
"CrossSection::Circle(), Manifold::Cylinder(), Manifold::Sphere(), and "
|
||||
"Manifold::Revolve() constructors. Overrides the edge length and angle "
|
||||
"constraints and sets the number of segments to exactly this value."
|
||||
"\n\n"
|
||||
"@param number Number of circular segments. Default is 0, meaning no "
|
||||
"constraint is applied.");
|
||||
|
||||
m.def(
|
||||
"get_circular_segments", Quality::GetCircularSegments,
|
||||
"Determine the result of the SetMinCircularAngle(), "
|
||||
"SetMinCircularEdgeLength(), and SetCircularSegments() defaults."
|
||||
"\n\n"
|
||||
"@param radius For a given radius of circle, determine how many default");
|
||||
|
||||
py::class_<Manifold>(m, "Manifold")
|
||||
.def(py::init<>())
|
||||
.def(py::init([](std::vector<Manifold> &manifolds) {
|
||||
Manifold result;
|
||||
if (manifolds.size() >= 1) {
|
||||
// for some reason using Manifold() as the initial object
|
||||
// will cause failure for python specifically
|
||||
// unable to reproduce with c++ directly
|
||||
Manifold first = manifolds[0];
|
||||
for (int i = 1; i < manifolds.size(); i++) first += manifolds[i];
|
||||
return first;
|
||||
} else {
|
||||
return Manifold();
|
||||
}
|
||||
}),
|
||||
"Construct manifold as the union of a set of manifolds.")
|
||||
.def(py::self + py::self, "Boolean union.")
|
||||
.def(py::self - py::self, "Boolean difference.")
|
||||
.def(py::self ^ py::self, "Boolean intersection.")
|
||||
.def(
|
||||
"transform",
|
||||
[](Manifold self, py::array_t<float> &mat) {
|
||||
auto mat_view = mat.unchecked<2>();
|
||||
if (mat_view.shape(0) != 3 || mat_view.shape(1) != 4)
|
||||
throw std::runtime_error("Invalid matrix shape");
|
||||
glm::mat4x3 mat_glm;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
mat_glm[j][i] = mat_view(i, j);
|
||||
}
|
||||
}
|
||||
return self.Transform(mat_glm);
|
||||
},
|
||||
py::arg("m"),
|
||||
"Transform this Manifold in space. The first three columns form a "
|
||||
"3x3 matrix transform and the last is a translation vector. This "
|
||||
"operation can be chained. Transforms are combined and applied "
|
||||
"lazily.\n"
|
||||
"\n\n"
|
||||
":param m: The affine transform matrix to apply to all the vertices.")
|
||||
.def(
|
||||
"translate",
|
||||
[](Manifold self, float x = 0.0f, float y = 0.0f, float z = 0.0f) {
|
||||
return self.Translate(glm::vec3(x, y, z));
|
||||
},
|
||||
py::arg("x") = 0.0f, py::arg("y") = 0.0f, py::arg("z") = 0.0f,
|
||||
"Move this Manifold in space. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param x: X axis translation. (default 0.0).\n"
|
||||
":param y: Y axis translation. (default 0.0).\n"
|
||||
":param z: Z axis translation. (default 0.0).")
|
||||
.def(
|
||||
"translate",
|
||||
[](Manifold self, py::array_t<float> &t) {
|
||||
auto t_view = t.unchecked<1>();
|
||||
if (t.shape(0) != 3)
|
||||
throw std::runtime_error("Invalid vector shape");
|
||||
return self.Translate(glm::vec3(t_view(0), t_view(1), t_view(2)));
|
||||
},
|
||||
py::arg("t"),
|
||||
"Move this Manifold in space. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param v: The vector to add to every vertex.")
|
||||
.def("scale",
|
||||
static_cast<Manifold (*)(Manifold, float)>(
|
||||
[](Manifold self, float scale) {
|
||||
return self.Scale(glm::vec3(scale));
|
||||
}),
|
||||
py::arg("scale"),
|
||||
"Scale this Manifold in space. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param scale: The scalar multiplier for each component of every "
|
||||
"vertices.")
|
||||
.def(
|
||||
"scale",
|
||||
[](Manifold self, py::array_t<float> &scale) {
|
||||
auto scale_view = scale.unchecked<1>();
|
||||
if (scale_view.shape(0) != 3)
|
||||
throw std::runtime_error("Invalid vector shape");
|
||||
glm::vec3 v(scale_view(0), scale_view(1), scale_view(2));
|
||||
return self.Scale(v);
|
||||
},
|
||||
py::arg("v"),
|
||||
"Scale this Manifold in space. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param v: The vector to multiply every vertex by component.")
|
||||
.def(
|
||||
"rotate",
|
||||
[](Manifold self, py::array_t<float> &v) {
|
||||
auto v_view = v.unchecked<1>();
|
||||
if (v_view.shape(0) != 3)
|
||||
throw std::runtime_error("Invalid vector shape");
|
||||
return self.Rotate(v_view(0), v_view(1), v_view(2));
|
||||
},
|
||||
py::arg("v"),
|
||||
"Applies an Euler angle rotation to the manifold, first about the X "
|
||||
"axis, then Y, then Z, in degrees. We use degrees so that we can "
|
||||
"minimize rounding error, and eliminate it completely for any "
|
||||
"multiples of 90 degrees. Additionally, more efficient code paths "
|
||||
"are used to update the manifold when the transforms only rotate by "
|
||||
"multiples of 90 degrees. This operation can be chained. Transforms "
|
||||
"are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param v: [X, Y, Z] rotation in degrees.")
|
||||
.def(
|
||||
"rotate",
|
||||
[](Manifold self, float xDegrees = 0.0f, float yDegrees = 0.0f,
|
||||
float zDegrees = 0.0f) {
|
||||
return self.Rotate(xDegrees, yDegrees, zDegrees);
|
||||
},
|
||||
py::arg("x_degrees") = 0.0f, py::arg("y_degrees") = 0.0f,
|
||||
py::arg("z_degrees") = 0.0f,
|
||||
"Applies an Euler angle rotation to the manifold, first about the X "
|
||||
"axis, then Y, then Z, in degrees. We use degrees so that we can "
|
||||
"minimize rounding error, and eliminate it completely for any "
|
||||
"multiples of 90 degrees. Additionally, more efficient code paths "
|
||||
"are used to update the manifold when the transforms only rotate by "
|
||||
"multiples of 90 degrees. This operation can be chained. "
|
||||
"Transforms are combined and applied lazily."
|
||||
"\n\n"
|
||||
":param x: X rotation in degrees. (default 0.0).\n"
|
||||
":param y: Y rotation in degrees. (default 0.0).\n"
|
||||
":param z: Z rotation in degrees. (default 0.0).")
|
||||
.def(
|
||||
"warp",
|
||||
[](Manifold self, const std::function<Float3(Float3)> &f) {
|
||||
return self.Warp([&f](glm::vec3 &v) {
|
||||
Float3 fv = f(std::make_tuple(v.x, v.y, v.z));
|
||||
v.x = std::get<0>(fv);
|
||||
v.y = std::get<1>(fv);
|
||||
v.z = std::get<2>(fv);
|
||||
});
|
||||
},
|
||||
py::arg("f"))
|
||||
.def(
|
||||
"refine", [](Manifold self, int n) { return self.Refine(n); },
|
||||
py::arg("n"),
|
||||
"Increase the density of the mesh by splitting every edge into n "
|
||||
"pieces. For instance, with n = 2, each triangle will be split into "
|
||||
"4 triangles. These will all be coplanar (and will not be "
|
||||
"immediately collapsed) unless the Mesh/Manifold has "
|
||||
"halfedgeTangents specified (e.g. from the Smooth() constructor), "
|
||||
"in which case the new vertices will be moved to the interpolated "
|
||||
"surface according to their barycentric coordinates.\n"
|
||||
"\n"
|
||||
":param n: The number of pieces to split every edge into. Must be > "
|
||||
"1.")
|
||||
.def("to_mesh", &Manifold::GetMesh)
|
||||
.def("num_vert", &Manifold::NumVert,
|
||||
"The number of vertices in the Manifold.")
|
||||
.def("num_edge", &Manifold::NumEdge,
|
||||
"The number of edges in the Manifold.")
|
||||
.def("num_tri", &Manifold::NumTri,
|
||||
"The number of triangles in the Manifold.")
|
||||
.def("num_prop", &Manifold::NumProp,
|
||||
"The number of properties per vertex in the Manifold")
|
||||
.def("num_prop_vert", &Manifold::NumPropVert,
|
||||
"The number of property vertices in the Manifold. This will always "
|
||||
"be >= NumVert, as some physical vertices may be duplicated to "
|
||||
"account for different properties on different neighboring "
|
||||
"triangles.")
|
||||
.def("precision", &Manifold::Precision,
|
||||
"Returns the precision of this Manifold's vertices, which tracks "
|
||||
"the approximate rounding error over all the transforms and "
|
||||
"operations that have led to this state. Any triangles that are "
|
||||
"colinear within this precision are considered degenerate and "
|
||||
"removed. This is the value of ε defining "
|
||||
"[ε-valid](https://github.com/elalish/manifold/wiki/"
|
||||
"Manifold-Library#definition-of-%CE%B5-valid).")
|
||||
.def("genus", &Manifold::Genus,
|
||||
"The genus is a topological property of the manifold, representing "
|
||||
"the number of \"handles\". A sphere is 0, torus 1, etc. It is only "
|
||||
"meaningful for a single mesh, so it is best to call Decompose() "
|
||||
"first.")
|
||||
.def(
|
||||
"get_volume",
|
||||
[](Manifold self) { return self.GetProperties().volume; },
|
||||
"Get the volume of the manifold\n This is clamped to zero for a "
|
||||
"given face if they are within the Precision().")
|
||||
.def(
|
||||
"get_surface_area",
|
||||
[](Manifold self) { return self.GetProperties().surfaceArea; },
|
||||
"Get the surface area of the manifold\n This is clamped to zero for "
|
||||
"a given face if they are within the Precision().")
|
||||
.def("original_id", &Manifold::OriginalID,
|
||||
"If this mesh is an original, this returns its meshID that can be "
|
||||
"referenced by product manifolds' MeshRelation. If this manifold is "
|
||||
"a product, this returns -1.")
|
||||
.def("as_original", &Manifold::AsOriginal,
|
||||
"This function condenses all coplanar faces in the relation, and "
|
||||
"collapses those edges. In the process the relation to ancestor "
|
||||
"meshes is lost and this new Manifold is marked an original. "
|
||||
"Properties are preserved, so if they do not match across an edge, "
|
||||
"that edge will be kept.")
|
||||
.def("is_empty", &Manifold::IsEmpty,
|
||||
"Does the Manifold have any triangles?")
|
||||
.def(
|
||||
"decompose", [](Manifold self) { return self.Decompose(); },
|
||||
" This operation returns a vector of Manifolds that are "
|
||||
"topologically disconnected. If everything is connected, the vector "
|
||||
"is length one, containing a copy of the original. It is the inverse "
|
||||
"operation of Compose().")
|
||||
.def("split", &Manifold::Split,
|
||||
"Split cuts this manifold in two using the cutter manifold. The "
|
||||
"first result is the intersection, second is the difference. This "
|
||||
"is more efficient than doing them separately.")
|
||||
.def(
|
||||
"split_by_plane",
|
||||
[](Manifold self, Float3 normal, float originOffset) {
|
||||
return self.SplitByPlane(
|
||||
{std::get<0>(normal), std::get<1>(normal), std::get<2>(normal)},
|
||||
originOffset);
|
||||
},
|
||||
py::arg("normal"), py::arg("origin_offset"),
|
||||
"Convenient version of Split() for a half-space."
|
||||
"\n\n"
|
||||
"@param normal This vector is normal to the cutting plane and its "
|
||||
"length does not matter. The first result is in the direction of "
|
||||
"this vector, the second result is on the opposite side.\n"
|
||||
"@param originOffset The distance of the plane from the origin in "
|
||||
"the direction of the normal vector.")
|
||||
.def(
|
||||
"trim_by_plane",
|
||||
[](Manifold self, Float3 normal, float originOffset) {
|
||||
return self.TrimByPlane(
|
||||
{std::get<0>(normal), std::get<1>(normal), std::get<2>(normal)},
|
||||
originOffset);
|
||||
},
|
||||
py::arg("normal"), py::arg("origin_offset"),
|
||||
"Identical to SplitByPlane(), but calculating and returning only the "
|
||||
"first result."
|
||||
"\n\n"
|
||||
"@param normal This vector is normal to the cutting plane and its "
|
||||
"length does not matter. The result is in the direction of this "
|
||||
"vector from the plane.\n"
|
||||
"@param originOffset The distance of the plane from the origin in "
|
||||
"the direction of the normal vector.")
|
||||
.def_static(
|
||||
"smooth", [](const Mesh &mesh) { return Manifold::Smooth(mesh); },
|
||||
"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."
|
||||
"\n\n"
|
||||
"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."
|
||||
"\n\n"
|
||||
":param mesh: input Mesh.\n"
|
||||
":param sharpenedEdges: 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)."
|
||||
"\n\n"
|
||||
"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.")
|
||||
.def_static(
|
||||
"from_mesh", [](const Mesh &mesh) { return Manifold(mesh); },
|
||||
py::arg("mesh"))
|
||||
.def_static(
|
||||
"tetrahedron", []() { return Manifold::Tetrahedron(); },
|
||||
"Constructs a tetrahedron centered at the origin with one vertex at "
|
||||
"(1,1,1) and the rest at similarly symmetric points.")
|
||||
.def_static(
|
||||
"cube",
|
||||
[](Float3 size, bool center = false) {
|
||||
return Manifold::Cube(
|
||||
glm::vec3(std::get<0>(size), std::get<1>(size),
|
||||
std::get<2>(size)),
|
||||
center);
|
||||
},
|
||||
py::arg("size") = std::make_tuple(1.0f, 1.0f, 1.0f),
|
||||
py::arg("center") = false,
|
||||
"Constructs a unit cube (edge lengths all one), by default in the "
|
||||
"first octant, touching the origin."
|
||||
"\n\n"
|
||||
":param size: The X, Y, and Z dimensions of the box.\n"
|
||||
":param center: Set to true to shift the center to the origin.")
|
||||
.def_static(
|
||||
"cube",
|
||||
[](py::array_t<float> &size, bool center = false) {
|
||||
auto size_view = size.unchecked<1>();
|
||||
if (size_view.shape(0) != 3)
|
||||
throw std::runtime_error("Invalid vector shape");
|
||||
return Manifold::Cube(
|
||||
glm::vec3(size_view(0), size_view(1), size_view(2)), center);
|
||||
},
|
||||
py::arg("size"), py::arg("center") = false,
|
||||
"Constructs a unit cube (edge lengths all one), by default in the "
|
||||
"first octant, touching the origin."
|
||||
"\n\n"
|
||||
":param size: The X, Y, and Z dimensions of the box.\n"
|
||||
":param center: Set to true to shift the center to the origin.")
|
||||
.def_static(
|
||||
"cube",
|
||||
[](float x, float y, float z, bool center = false) {
|
||||
return Manifold::Cube(glm::vec3(x, y, z), center);
|
||||
},
|
||||
py::arg("x"), py::arg("y"), py::arg("z"), py::arg("center") = false,
|
||||
"Constructs a unit cube (edge lengths all one), by default in the "
|
||||
"first octant, touching the origin."
|
||||
"\n\n"
|
||||
":param x: The X dimensions of the box.\n"
|
||||
":param y: The Y dimensions of the box.\n"
|
||||
":param z: The Z dimensions of the box.\n"
|
||||
":param center: Set to true to shift the center to the origin.")
|
||||
.def_static(
|
||||
"cylinder",
|
||||
[](float height, float radiusLow, float radiusHigh = -1.0f,
|
||||
int circularSegments = 0, bool center = false) {
|
||||
return Manifold::Cylinder(height, radiusLow, radiusHigh,
|
||||
circularSegments, center);
|
||||
},
|
||||
py::arg("height"), py::arg("radius_low"),
|
||||
py::arg("radius_high") = -1.0f, py::arg("circular_segments") = 0,
|
||||
py::arg("center") = false,
|
||||
"A convenience constructor for the common case of extruding a "
|
||||
"circle. Can also form cones if both radii are specified."
|
||||
"\n\n"
|
||||
":param height: Z-extent\n"
|
||||
":param radiusLow: Radius of bottom circle. Must be positive.\n"
|
||||
":param radiusHigh: Radius of top circle. Can equal zero. Default "
|
||||
"(-1) is equal to radiusLow.\n"
|
||||
":param circularSegments: How many line segments to use around the "
|
||||
"circle. Default (-1) is calculated by the static Defaults.\n"
|
||||
":param center: Set to true to shift the center to the origin. "
|
||||
"Default is origin at the bottom.")
|
||||
.def_static(
|
||||
"sphere",
|
||||
[](float radius, int circularSegments = 0) {
|
||||
return Manifold::Sphere(radius, circularSegments);
|
||||
},
|
||||
py::arg("radius"), py::arg("circular_segments") = 0,
|
||||
"Constructs a geodesic sphere of a given radius.\n"
|
||||
"\n"
|
||||
":param radius: Radius of the sphere. Must be positive.\n"
|
||||
":param circularSegments: Number of segments along its diameter. "
|
||||
"This number will always be rounded up to the nearest factor of "
|
||||
"four, as this sphere is constructed by refining an octahedron. This "
|
||||
"means there are a circle of vertices on all three of the axis "
|
||||
"planes. Default is calculated by the static Defaults.")
|
||||
.def_static("reserve_ids", Manifold::ReserveIDs, py::arg("n"),
|
||||
"Returns the first of n sequential new unique mesh IDs for "
|
||||
"marking sets of triangles that can be looked up after "
|
||||
"further operations. Assign to MeshGL.runOriginalID vector");
|
||||
|
||||
py::class_<PolygonsWrapper>(m, "Polygons")
|
||||
.def(py::init([](std::vector<std::vector<Float2>> &polygons) {
|
||||
std::vector<SimplePolygon> simplePolygons(polygons.size());
|
||||
for (int i = 0; i < polygons.size(); i++) {
|
||||
std::vector<glm::vec2> vertices(polygons[i].size());
|
||||
for (int j = 0; j < polygons[i].size(); j++) {
|
||||
vertices[j] = {std::get<0>(polygons[i][j]),
|
||||
std::get<1>(polygons[i][j])};
|
||||
}
|
||||
simplePolygons[i] = {vertices};
|
||||
}
|
||||
return PolygonsWrapper{std::make_unique<Polygons>(simplePolygons)};
|
||||
}),
|
||||
py::arg("polygons"),
|
||||
"Construct the Polygons object from a list of simple polygon, "
|
||||
"where each simple polygon is a list of points (pair of floats).")
|
||||
.def(
|
||||
"extrude",
|
||||
[](PolygonsWrapper &self, float height, int nDivisions = 0,
|
||||
float twistDegrees = 0.0f,
|
||||
Float2 scaleTop = std::make_tuple(1.0f, 1.0f)) {
|
||||
glm::vec2 scaleTopVec(std::get<0>(scaleTop), std::get<1>(scaleTop));
|
||||
return Manifold::Extrude(*self.polygons, height, nDivisions,
|
||||
twistDegrees, scaleTopVec);
|
||||
},
|
||||
py::arg("height"), py::arg("n_divisions") = 0,
|
||||
py::arg("twist_degrees") = 0.0f,
|
||||
py::arg("scale_top") = std::make_tuple(1.0f, 1.0f),
|
||||
"Constructs a manifold from the set of polygons by extruding them "
|
||||
"along the Z-axis.\n"
|
||||
"\n"
|
||||
":param height: Z-extent of extrusion.\n"
|
||||
":param nDivisions: Number of extra copies of the crossSection to "
|
||||
"insert into the shape vertically; especially useful in combination "
|
||||
"with twistDegrees to avoid interpolation artifacts. Default is "
|
||||
"none.\n"
|
||||
":param twistDegrees: Amount to twist the top crossSection relative "
|
||||
"to the bottom, interpolated linearly for the divisions in between.\n"
|
||||
":param scaleTop: Amount to scale the top (independently in X and "
|
||||
"Y). If the scale is (0, 0), a pure cone is formed with only a "
|
||||
"single vertex at the top. Default (1, 1).")
|
||||
.def(
|
||||
"revolve",
|
||||
[](PolygonsWrapper &self, int circularSegments = 0) {
|
||||
return Manifold::Revolve(*self.polygons, circularSegments);
|
||||
},
|
||||
py::arg("circular_segments") = 0,
|
||||
"Constructs a manifold from the set of polygons by revolving this "
|
||||
"cross-section around its Y-axis and then setting this as the Z-axis "
|
||||
"of the resulting manifold. If the polygons cross the Y-axis, only "
|
||||
"the part on the positive X side is used. Geometrically valid input "
|
||||
"will result in geometrically valid output.\n"
|
||||
"\n"
|
||||
":param circularSegments: Number of segments along its diameter. "
|
||||
"Default is calculated by the static Defaults.");
|
||||
|
||||
py::class_<Mesh>(m, "Mesh")
|
||||
.def(py::init([](py::array_t<float> &vertPos, py::array_t<int> &triVerts,
|
||||
py::array_t<float> &vertNormal,
|
||||
py::array_t<float> &halfedgeTangent) {
|
||||
auto vertPos_view = vertPos.unchecked<2>();
|
||||
auto triVerts_view = triVerts.unchecked<2>();
|
||||
auto vertNormal_view = vertNormal.unchecked<2>();
|
||||
auto halfedgeTangent_view = halfedgeTangent.unchecked<2>();
|
||||
if (vertPos_view.shape(1) != 3)
|
||||
throw std::runtime_error("Invalid vert_pos shape");
|
||||
if (triVerts_view.shape(1) != 3)
|
||||
throw std::runtime_error("Invalid tri_verts shape");
|
||||
if (vertNormal_view.shape(0) != 0) {
|
||||
if (vertNormal_view.shape(1) != 3)
|
||||
throw std::runtime_error("Invalid vert_normal shape");
|
||||
if (vertNormal_view.shape(0) != vertPos_view.shape(0))
|
||||
throw std::runtime_error(
|
||||
"vert_normal must have the same length as vert_pos");
|
||||
}
|
||||
if (halfedgeTangent_view.shape(0) != 0) {
|
||||
if (halfedgeTangent_view.shape(1) != 4)
|
||||
throw std::runtime_error("Invalid halfedge_tangent shape");
|
||||
if (halfedgeTangent_view.shape(0) != triVerts_view.shape(0) * 3)
|
||||
throw std::runtime_error(
|
||||
"halfedge_tangent must be three times as long as "
|
||||
"tri_verts");
|
||||
}
|
||||
std::vector<glm::vec3> vertPos_vec(vertPos_view.shape(0));
|
||||
std::vector<glm::ivec3> triVerts_vec(triVerts_view.shape(0));
|
||||
std::vector<glm::vec3> vertNormal_vec(vertNormal_view.shape(0));
|
||||
std::vector<glm::vec4> halfedgeTangent_vec(
|
||||
halfedgeTangent_view.shape(0));
|
||||
for (int i = 0; i < vertPos_view.shape(0); i++)
|
||||
for (const int j : {0, 1, 2})
|
||||
vertPos_vec[i][j] = vertPos_view(i, j);
|
||||
for (int i = 0; i < triVerts_view.shape(0); i++)
|
||||
for (const int j : {0, 1, 2})
|
||||
triVerts_vec[i][j] = triVerts_view(i, j);
|
||||
for (int i = 0; i < vertNormal_view.shape(0); i++)
|
||||
for (const int j : {0, 1, 2})
|
||||
vertNormal_vec[i][j] = vertNormal_view(i, j);
|
||||
for (int i = 0; i < halfedgeTangent_view.shape(0); i++)
|
||||
for (const int j : {0, 1, 2, 3})
|
||||
halfedgeTangent_vec[i][j] = halfedgeTangent_view(i, j);
|
||||
return Mesh({vertPos_vec, triVerts_vec, vertNormal_vec,
|
||||
halfedgeTangent_vec});
|
||||
}),
|
||||
py::arg("vert_pos"), py::arg("tri_verts"), py::arg("vert_normal"),
|
||||
py::arg("halfedge_tangent"))
|
||||
.def_property_readonly("vert_pos",
|
||||
[](Mesh &self) {
|
||||
const int numVert = self.vertPos.size();
|
||||
py::array_t<float> vert_pos({numVert, 3});
|
||||
auto vert_pos_view =
|
||||
vert_pos.mutable_unchecked<2>();
|
||||
|
||||
for (int i = 0; i < numVert; ++i)
|
||||
for (const int j : {0, 1, 2})
|
||||
vert_pos_view(i, j) = self.vertPos[i][j];
|
||||
return vert_pos;
|
||||
})
|
||||
.def_property_readonly("tri_verts",
|
||||
[](Mesh &self) {
|
||||
const int numTri = self.triVerts.size();
|
||||
py::array_t<int> tri_verts({numTri, 3});
|
||||
auto tri_verts_view =
|
||||
tri_verts.mutable_unchecked<2>();
|
||||
|
||||
for (int i = 0; i < numTri; ++i)
|
||||
for (const int j : {0, 1, 2})
|
||||
tri_verts_view(i, j) = self.triVerts[i][j];
|
||||
return tri_verts;
|
||||
})
|
||||
.def_property_readonly(
|
||||
"vert_normal",
|
||||
[](Mesh &self) {
|
||||
const int numVert = self.vertNormal.size();
|
||||
py::array_t<float> vert_normal({numVert, 3});
|
||||
auto vert_normal_view = vert_normal.mutable_unchecked<2>();
|
||||
|
||||
for (int i = 0; i < numVert; ++i)
|
||||
for (const int j : {0, 1, 2})
|
||||
vert_normal_view(i, j) = self.vertNormal[i][j];
|
||||
return vert_normal;
|
||||
})
|
||||
.def_property_readonly("halfedge_tangent", [](Mesh &self) {
|
||||
const int numEdge = self.halfedgeTangent.size();
|
||||
py::array_t<float> halfedge_tangent({numEdge, 4});
|
||||
auto halfedge_tangent_view = halfedge_tangent.mutable_unchecked<2>();
|
||||
|
||||
for (int i = 0; i < numEdge; ++i)
|
||||
for (const int j : {0, 1, 2, 3})
|
||||
halfedge_tangent_view(i, j) = self.halfedgeTangent[i][j];
|
||||
return halfedge_tangent;
|
||||
});
|
||||
|
||||
py::enum_<CrossSection::FillRule>(m, "FillRule")
|
||||
.value("EvenOdd", CrossSection::FillRule::EvenOdd,
|
||||
"Only odd numbered sub-regions are filled.")
|
||||
.value("NonZero", CrossSection::FillRule::NonZero,
|
||||
"Only non-zero sub-regions are filled.")
|
||||
.value("Positive", CrossSection::FillRule::Positive,
|
||||
"Only sub-regions with winding counts > 0 are filled.")
|
||||
.value("Negative", CrossSection::FillRule::Negative,
|
||||
"Only sub-regions with winding counts < 0 are filled.");
|
||||
|
||||
py::enum_<CrossSection::JoinType>(m, "JoinType")
|
||||
.value("Square", CrossSection::JoinType::Square,
|
||||
"Squaring is applied uniformly at all joins where the internal "
|
||||
"join angle is less that 90 degrees. The squared edge will be at "
|
||||
"exactly the offset distance from the join vertex.")
|
||||
.value(
|
||||
"Round", CrossSection::JoinType::Square,
|
||||
"Rounding is applied to all joins that have convex external angles, "
|
||||
"and it maintains the exact offset distance from the join vertex.")
|
||||
.value(
|
||||
"Miter", CrossSection::JoinType::Miter,
|
||||
"There's a necessary limit to mitered joins (to avoid narrow angled "
|
||||
"joins producing excessively long and narrow "
|
||||
"[spikes](http://www.angusj.com/clipper2/Docs/Units/Clipper.Offset/"
|
||||
"Classes/ClipperOffset/Properties/MiterLimit.htm)). So where mitered "
|
||||
"joins would exceed a given maximum miter distance (relative to the "
|
||||
"offset distance), these are 'squared' instead.");
|
||||
|
||||
py::class_<CrossSection>(
|
||||
m, "CrossSection",
|
||||
"Two-dimensional cross sections guaranteed to be without "
|
||||
"self-intersections, or overlaps between polygons (from construction "
|
||||
"onwards). This class makes use of the "
|
||||
"[Clipper2](http://www.angusj.com/clipper2/Docs/Overview.htm) library "
|
||||
"for polygon clipping (boolean) and offsetting operations.")
|
||||
.def(py::init<>())
|
||||
.def(py::init(
|
||||
[](PolygonsWrapper &contours, CrossSection::FillRule fillrule) {
|
||||
return CrossSection(*contours.polygons, fillrule);
|
||||
}),
|
||||
py::arg("contours"),
|
||||
py::arg("fillrule") = CrossSection::FillRule::Positive)
|
||||
.def("area", &CrossSection::Area)
|
||||
.def("num_vert", &CrossSection::NumVert)
|
||||
.def("num_contour", &CrossSection::NumContour)
|
||||
.def("is_empty", &CrossSection::IsEmpty)
|
||||
.def("translate",
|
||||
[](CrossSection self, Float2 v) {
|
||||
return self.Translate({std::get<0>(v), std::get<1>(v)});
|
||||
})
|
||||
.def("rotate", &CrossSection::Rotate)
|
||||
.def("scale",
|
||||
[](CrossSection self, Float2 s) {
|
||||
return self.Scale({std::get<0>(s), std::get<1>(s)});
|
||||
})
|
||||
.def("mirror",
|
||||
[](CrossSection self, Float2 ax) {
|
||||
return self.Mirror({std::get<0>(ax), std::get<1>(ax)});
|
||||
})
|
||||
.def("transform",
|
||||
[](CrossSection self, py::array_t<float> &mat) {
|
||||
auto mat_view = mat.unchecked<2>();
|
||||
if (mat_view.shape(0) != 2 || mat_view.shape(1) != 3)
|
||||
throw std::runtime_error("Invalid matrix shape");
|
||||
glm::mat3x2 mat_glm;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
mat_glm[j][i] = mat_view(i, j);
|
||||
}
|
||||
}
|
||||
return self.Transform(mat_glm);
|
||||
})
|
||||
.def(
|
||||
"warp",
|
||||
[](CrossSection self, const std::function<Float2(Float2)> &f) {
|
||||
return self.Warp([&f](glm::vec2 &v) {
|
||||
Float2 fv = f(std::make_tuple(v.x, v.y));
|
||||
v.x = std::get<0>(fv);
|
||||
v.y = std::get<1>(fv);
|
||||
});
|
||||
},
|
||||
py::arg("f"))
|
||||
.def("simplify", &CrossSection::Simplify)
|
||||
.def("offset", &CrossSection::Offset, py::arg("delta"),
|
||||
py::arg("join_type"), py::arg("miter_limit") = 2.0,
|
||||
py::arg("arc_tolerance") = 0.0)
|
||||
.def(py::self + py::self, "Boolean union.")
|
||||
.def(py::self - py::self, "Boolean difference.")
|
||||
.def(py::self ^ py::self, "Boolean intersection.")
|
||||
.def("decompose", &CrossSection::Decompose)
|
||||
.def("to_polygons",
|
||||
[](CrossSection self) {
|
||||
return PolygonsWrapper{
|
||||
std::make_unique<Polygons>(self.ToPolygons())};
|
||||
})
|
||||
.def(
|
||||
"extrude",
|
||||
[](CrossSection self, float height, int nDivisions = 0,
|
||||
float twistDegrees = 0.0f,
|
||||
Float2 scaleTop = std::make_tuple(1.0f, 1.0f)) {
|
||||
glm::vec2 scaleTopVec(std::get<0>(scaleTop), std::get<1>(scaleTop));
|
||||
return Manifold::Extrude(self, height, nDivisions, twistDegrees,
|
||||
scaleTopVec);
|
||||
},
|
||||
py::arg("height"), py::arg("n_divisions") = 0,
|
||||
py::arg("twist_degrees") = 0.0f,
|
||||
py::arg("scale_top") = std::make_tuple(1.0f, 1.0f),
|
||||
"Constructs a manifold from the set of polygons by extruding them "
|
||||
"along the Z-axis.\n"
|
||||
"\n"
|
||||
":param height: Z-extent of extrusion.\n"
|
||||
":param nDivisions: Number of extra copies of the crossSection to "
|
||||
"insert into the shape vertically; especially useful in combination "
|
||||
"with twistDegrees to avoid interpolation artifacts. Default is "
|
||||
"none.\n"
|
||||
":param twistDegrees: Amount to twist the top crossSection relative "
|
||||
"to the bottom, interpolated linearly for the divisions in between.\n"
|
||||
":param scaleTop: Amount to scale the top (independently in X and "
|
||||
"Y). If the scale is (0, 0), a pure cone is formed with only a "
|
||||
"single vertex at the top. Default (1, 1).")
|
||||
.def(
|
||||
"revolve",
|
||||
[](CrossSection self, int circularSegments = 0) {
|
||||
return Manifold::Revolve(self, circularSegments);
|
||||
},
|
||||
py::arg("circular_segments") = 0,
|
||||
"Constructs a manifold from the set of polygons by revolving this "
|
||||
"cross-section around its Y-axis and then setting this as the Z-axis "
|
||||
"of the resulting manifold. If the polygons cross the Y-axis, only "
|
||||
"the part on the positive X side is used. Geometrically valid input "
|
||||
"will result in geometrically valid output.\n"
|
||||
"\n"
|
||||
":param circularSegments: Number of segments along its diameter. "
|
||||
"Default is calculated by the static Defaults.")
|
||||
.def_static(
|
||||
"square",
|
||||
[](Float2 dims, bool center) {
|
||||
return CrossSection::Square({std::get<0>(dims), std::get<1>(dims)},
|
||||
center);
|
||||
},
|
||||
py::arg("dims"), py::arg("center") = false)
|
||||
.def_static(
|
||||
"circle",
|
||||
[](float radius, int circularSegments) {
|
||||
return CrossSection::Circle(radius, circularSegments);
|
||||
},
|
||||
py::arg("radius"), py::arg("circularSegments") = 0);
|
||||
}
|
||||
@@ -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]]
|
||||
@@ -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 +0,0 @@
|
||||
add_subdirectory(pybind11)
|
||||
@@ -0,0 +1,16 @@
|
||||
/manifold.js
|
||||
/manifold.wasm
|
||||
/manifold-3d-*.tgz
|
||||
/manifold.d.ts
|
||||
/lib/*.js
|
||||
/lib/*.js.map
|
||||
/lib/*.d.ts
|
||||
/lib/*.d.ts.map
|
||||
/test/*.js
|
||||
/test/*.js.map
|
||||
/test/*.d.ts
|
||||
/test/*.d.ts.map
|
||||
/test/results
|
||||
/coverage
|
||||
/public
|
||||
dist
|
||||
@@ -12,27 +12,43 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
project(wasm)
|
||||
|
||||
add_executable(manifoldjs bindings.cpp)
|
||||
|
||||
# make sure that we recompile the wasm when bindings.js is being modified
|
||||
set_source_files_properties(bindings.cpp OBJECT_DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/bindings.js)
|
||||
target_link_libraries(manifoldjs manifold sdf cross_section polygon)
|
||||
target_compile_options(manifoldjs PRIVATE ${MANIFOLD_FLAGS} -fexceptions)
|
||||
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)
|
||||
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_compile_features(manifoldjs PUBLIC cxx_std_17)
|
||||
set_target_properties(manifoldjs PROPERTIES OUTPUT_NAME "manifold")
|
||||
|
||||
file(COPY examples;test;. DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS examples;test)
|
||||
# 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_command(
|
||||
TARGET manifoldjs POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifold.*
|
||||
${CMAKE_CURRENT_BINARY_DIR}/manifold-*.d.ts
|
||||
${CMAKE_CURRENT_BINARY_DIR}/examples/)
|
||||
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}
|
||||
)
|
||||
|
||||
@@ -1,27 +1,46 @@
|
||||
[](https://codecov.io/github/elalish/manifold)
|
||||
[](https://badge.fury.io/js/manifold-3d)
|
||||
[](https://twitter.com/intent/follow?screen_name=manifoldcad)
|
||||
|
||||
[**ManifoldCAD User Guide**](https://manifoldcad.org/docs/jsuser/) | [**JS/TS/WASM API**](https://manifoldcad.org/docs/jsapi/) | [**C++ Documentation**](https://manifoldcad.org/docs/html/classmanifold_1_1_manifold.html) | [**Algorithm Documentation**](https://github.com/elalish/manifold/wiki/Manifold-Library) | [**Blog Posts**](https://elalish.blogspot.com/search/label/Manifold)
|
||||
|
||||
<!-- #region IncludeInUserGuide -->
|
||||
|
||||
A [manifold mesh](https://github.com/elalish/manifold/wiki/Manifold-Library#manifoldness) is a mesh that represents a solid object, and so is very important in manufacturing, CAD, structural analysis, etc. 'Manifold' implies that there are no gaps or tears, and that all faces are oriented outwards.
|
||||
|
||||
## Manifold Library
|
||||
|
||||
[ [Using Manifold](https://manifoldcad.org/docs/jsapi/documents/Using_Manifold.html) | [Examples](https://manifoldcad.org/docs/jsapi/documents/Manifold_Examples.html) | [Manifold WASM Developer Guide](https://manifoldcad.org/docs/jsapi/) ]
|
||||
|
||||
[Manifold](https://github.com/elalish/manifold) is a geometry library dedicated to creating and operating on manifold triangle meshes. It is available as a WASM module that runs in any modern browser.
|
||||
|
||||
Our primary goal is reliability: guaranteed manifold output without caveats or edge cases. Our secondary goal is performance: efficient algorithms that make extensive use of parallelization, or pipelining when only a single thread is available.
|
||||
|
||||
## [ManifoldCAD.org](https://manifoldcad.org)
|
||||
If you like OpenSCAD / OpenJSCAD, you might also like ManifoldCAD - our own solid modelling web app based on this package.
|
||||
|
||||

|
||||
<img alt="A metallic Menger sponge" src="https://manifoldcad.org/samples/models/mengerSponge192.png" style="float:right">
|
||||
|
||||
# Manifold
|
||||
[ [ManifoldCAD](https://manifoldcad.org) | [ManifoldCAD User Guide](https://manifoldcad.org/docs/jsuser/) ]
|
||||
|
||||
[**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)
|
||||
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 bear 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](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).
|
||||
manifoldCAD = manifold + [TypeScript](https://www.typescriptlang.org/) + [glTF](https://www.khronos.org/gltf/)
|
||||
|
||||
## What's here
|
||||
<div style="clear:both"></div>
|
||||
|
||||
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.
|
||||
<!-- #endregion IncludeInUserGuide -->
|
||||
<!-- #region NoStlPlease -->
|
||||
|
||||
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.
|
||||
## 3D Formats
|
||||
|
||||
## Note on memory management
|
||||
[ [EXT_mesh_manifold](https://manifoldcad.org/docs/jsapi/modules/manifold-gltf.html) ]
|
||||
|
||||
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 https://github.com/elalish/manifold/discussions/256#discussioncomment-3944287.
|
||||
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.
|
||||
|
||||
<!-- #endregion NoStlPlease -->
|
||||
|
||||
## 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,76 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./manifold-root.d.ts",
|
||||
"compiler": {
|
||||
"overrideTsconfig": {
|
||||
"include": [
|
||||
"./manifold-global-types.d.ts",
|
||||
"./manifold-encapsulated-types.d.ts",
|
||||
"./manifold-root.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./manifold.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-incompatible-release-tags": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-internal-missing-underscore": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"tsdoc-malformed-html-name": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-param-tag-missing-hyphen": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-right-brace": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-greater-than": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-malformed-inline-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-undefined-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-reference-missing-hash": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./types/manifoldCAD.d.ts",
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./dist/manifoldCAD.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-incompatible-release-tags": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-internal-missing-underscore": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"tsdoc-malformed-html-name": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-param-tag-missing-hyphen": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-right-brace": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-greater-than": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-malformed-inline-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-undefined-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-reference-missing-hash": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./types/manifoldCADGlobals.d.ts",
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./dist/manifoldCADGlobals.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-incompatible-release-tags": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-internal-missing-underscore": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"tsdoc-malformed-html-name": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-param-tag-missing-hyphen": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-right-brace": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-escape-greater-than": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-malformed-inline-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-undefined-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"tsdoc-reference-missing-hash": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env node
|
||||
// Copyright 2025 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import {Command} from 'commander';
|
||||
|
||||
import {bundleFile} from '../lib/bundler.js';
|
||||
import * as worker from '../lib/worker.js';
|
||||
import * as exportModel from '../lib/export-model.js';
|
||||
|
||||
const program = new Command();
|
||||
program.name('manifold-cad')
|
||||
.description('Compile a ManifoldCAD program into a mesh.')
|
||||
.argument('<infile.[js|ts]>', 'Input file')
|
||||
.argument('<outfile>', 'Output file')
|
||||
.option('--jscdn <string>', 'Fetch npm packages from a CDN', 'jsDelivr')
|
||||
|
||||
// True means '--bundle' is implied by default, and '--no-bundle'
|
||||
// must be specified to disable bundling.
|
||||
.option('--no-bundle', 'Do not bundle input file before building model', true)
|
||||
.action(async(infile, outfile, options) => {
|
||||
// Choose an exporter. It will throw if we cannot match the extension.
|
||||
exportModel.supports(outfile, true);
|
||||
|
||||
// Build the model.
|
||||
let bundle = '';
|
||||
if (options.bundle) {
|
||||
bundle = await bundleFile(infile, {jsCDN: options.jscdn});
|
||||
} else {
|
||||
bundle = await readFile(infile, 'utf-8');
|
||||
}
|
||||
const doc = await worker.evaluate(bundle, {doNotBundle:true});
|
||||
|
||||
// Convert it and write it to disk.
|
||||
await exportModel.writeFile(outfile, doc);
|
||||
});
|
||||
|
||||
program.parseAsync(process.argv);
|
||||
@@ -17,177 +17,55 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.cpp"
|
||||
#include "manifold/cross_section.h"
|
||||
#include "manifold/manifold.h"
|
||||
|
||||
#if (MANIFOLD_PAR == 1)
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
// https://github.com/oneapi-src/oneTBB/blob/master/WASM_Support.md#limitations
|
||||
void initTBB() {
|
||||
#if (MANIFOLD_PAR == 1)
|
||||
int num_threads = tbb::this_task_arena::max_concurrency();
|
||||
std::atomic<int> barrier{num_threads};
|
||||
tbb::parallel_for(
|
||||
0, num_threads,
|
||||
[&barrier](int) {
|
||||
barrier--;
|
||||
while (barrier > 0) {
|
||||
// Send browser thread to event loop
|
||||
std::this_thread::yield();
|
||||
}
|
||||
},
|
||||
tbb::static_partitioner{});
|
||||
#endif
|
||||
}
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
#include <manifold.h>
|
||||
#include <polygon.h>
|
||||
#include <sdf.h>
|
||||
|
||||
using namespace manifold;
|
||||
|
||||
Manifold Union(const Manifold& a, const Manifold& b) { return a + b; }
|
||||
|
||||
Manifold Difference(const Manifold& a, const Manifold& b) { return a - b; }
|
||||
|
||||
Manifold Intersection(const Manifold& a, const Manifold& b) { return a ^ b; }
|
||||
|
||||
Manifold UnionN(const std::vector<Manifold>& manifolds) {
|
||||
return Manifold::BatchBoolean(manifolds, OpType::Add);
|
||||
}
|
||||
|
||||
Manifold DifferenceN(const std::vector<Manifold>& manifolds) {
|
||||
return Manifold::BatchBoolean(manifolds, OpType::Subtract);
|
||||
}
|
||||
|
||||
Manifold IntersectionN(const std::vector<Manifold>& manifolds) {
|
||||
return Manifold::BatchBoolean(manifolds, OpType::Intersect);
|
||||
}
|
||||
|
||||
std::vector<SimplePolygon> ToPolygon(
|
||||
std::vector<std::vector<glm::vec2>>& polygons) {
|
||||
std::vector<SimplePolygon> simplePolygons(polygons.size());
|
||||
for (int i = 0; i < polygons.size(); i++) {
|
||||
std::vector<glm::vec2> vertices(polygons[i].size());
|
||||
for (int j = 0; j < polygons[i].size(); j++) {
|
||||
vertices[j] = polygons[i][j];
|
||||
}
|
||||
simplePolygons[i] = {vertices};
|
||||
}
|
||||
return simplePolygons;
|
||||
}
|
||||
|
||||
val GetMeshJS(const Manifold& manifold, const glm::ivec3& normalIdx) {
|
||||
MeshGL mesh = manifold.GetMeshGL(normalIdx);
|
||||
val meshJS = val::object();
|
||||
|
||||
meshJS.set("numProp", mesh.numProp);
|
||||
meshJS.set("triVerts",
|
||||
val(typed_memory_view(mesh.triVerts.size(), mesh.triVerts.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("vertProperties",
|
||||
val(typed_memory_view(mesh.vertProperties.size(),
|
||||
mesh.vertProperties.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("mergeFromVert", val(typed_memory_view(mesh.mergeFromVert.size(),
|
||||
mesh.mergeFromVert.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("mergeToVert", val(typed_memory_view(mesh.mergeToVert.size(),
|
||||
mesh.mergeToVert.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("runIndex",
|
||||
val(typed_memory_view(mesh.runIndex.size(), mesh.runIndex.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("runOriginalID", val(typed_memory_view(mesh.runOriginalID.size(),
|
||||
mesh.runOriginalID.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("faceID",
|
||||
val(typed_memory_view(mesh.faceID.size(), mesh.faceID.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("halfedgeTangent",
|
||||
val(typed_memory_view(mesh.halfedgeTangent.size(),
|
||||
mesh.halfedgeTangent.data()))
|
||||
.call<val>("slice"));
|
||||
meshJS.set("runTransform", val(typed_memory_view(mesh.runTransform.size(),
|
||||
mesh.runTransform.data()))
|
||||
.call<val>("slice"));
|
||||
|
||||
return meshJS;
|
||||
}
|
||||
|
||||
MeshGL MeshJS2GL(const val& mesh) {
|
||||
MeshGL out;
|
||||
out.numProp = mesh["numProp"].as<int>();
|
||||
out.triVerts = convertJSArrayToNumberVector<uint32_t>(mesh["triVerts"]);
|
||||
out.vertProperties =
|
||||
convertJSArrayToNumberVector<float>(mesh["vertProperties"]);
|
||||
if (mesh["mergeFromVert"] != val::undefined()) {
|
||||
out.mergeFromVert =
|
||||
convertJSArrayToNumberVector<uint32_t>(mesh["mergeFromVert"]);
|
||||
}
|
||||
if (mesh["mergeToVert"] != val::undefined()) {
|
||||
out.mergeToVert =
|
||||
convertJSArrayToNumberVector<uint32_t>(mesh["mergeToVert"]);
|
||||
}
|
||||
if (mesh["runIndex"] != val::undefined()) {
|
||||
out.runIndex = convertJSArrayToNumberVector<uint32_t>(mesh["runIndex"]);
|
||||
}
|
||||
if (mesh["runOriginalID"] != val::undefined()) {
|
||||
out.runOriginalID =
|
||||
convertJSArrayToNumberVector<uint32_t>(mesh["runOriginalID"]);
|
||||
}
|
||||
if (mesh["faceID"] != val::undefined()) {
|
||||
out.faceID = convertJSArrayToNumberVector<uint32_t>(mesh["faceID"]);
|
||||
}
|
||||
if (mesh["halfedgeTangent"] != val::undefined()) {
|
||||
out.halfedgeTangent =
|
||||
convertJSArrayToNumberVector<float>(mesh["halfedgeTangent"]);
|
||||
}
|
||||
if (mesh["runTransform"] != val::undefined()) {
|
||||
out.runTransform =
|
||||
convertJSArrayToNumberVector<float>(mesh["runTransform"]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Manifold FromMeshJS(const val& mesh) { return Manifold(MeshJS2GL(mesh)); }
|
||||
|
||||
Manifold Smooth(const val& mesh,
|
||||
const std::vector<Smoothness>& sharpenedEdges = {}) {
|
||||
return Manifold::Smooth(MeshJS2GL(mesh), sharpenedEdges);
|
||||
}
|
||||
|
||||
Manifold Extrude(std::vector<std::vector<glm::vec2>>& polygons, float height,
|
||||
int nDivisions, float twistDegrees, glm::vec2 scaleTop) {
|
||||
return Manifold::Extrude(ToPolygon(polygons), height, nDivisions,
|
||||
twistDegrees, scaleTop);
|
||||
}
|
||||
|
||||
Manifold Revolve(std::vector<std::vector<glm::vec2>>& polygons,
|
||||
int circularSegments) {
|
||||
return Manifold::Revolve(ToPolygon(polygons), circularSegments);
|
||||
}
|
||||
|
||||
Manifold Transform(Manifold& manifold, const val& mat) {
|
||||
std::vector<float> array = convertJSArrayToNumberVector<float>(mat);
|
||||
glm::mat4x3 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);
|
||||
return manifold.Warp(f);
|
||||
}
|
||||
|
||||
Manifold LevelSetJs(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);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -203,45 +81,104 @@ EMSCRIPTEN_BINDINGS(whatever) {
|
||||
.value("TransformWrongLength", Manifold::Error::TransformWrongLength)
|
||||
.value("RunIndexWrongLength", Manifold::Error::RunIndexWrongLength)
|
||||
.value("FaceIDWrongLength", Manifold::Error::FaceIDWrongLength)
|
||||
.value("InvalidConstruction", Manifold::Error::InvalidConstruction);
|
||||
.value("InvalidConstruction", Manifold::Error::InvalidConstruction)
|
||||
.value("ResultTooLarge", Manifold::Error::ResultTooLarge)
|
||||
.value("InvalidTangents", Manifold::Error::InvalidTangents)
|
||||
.value("Cancelled", Manifold::Error::Cancelled);
|
||||
|
||||
enum_<CrossSection::JoinType>("jointype")
|
||||
.value("Square", CrossSection::JoinType::Square)
|
||||
.value("Round", CrossSection::JoinType::Round)
|
||||
.value("Miter", CrossSection::JoinType::Miter)
|
||||
.value("Bevel", CrossSection::JoinType::Bevel);
|
||||
|
||||
value_object<Rect>("rect").field("min", &Rect::min).field("max", &Rect::max);
|
||||
value_object<Box>("box").field("min", &Box::min).field("max", &Box::max);
|
||||
|
||||
value_object<Smoothness>("smoothness")
|
||||
.field("halfedge", &Smoothness::halfedge)
|
||||
.field("smoothness", &Smoothness::smoothness);
|
||||
value_object<RayHit>("rayHit")
|
||||
.field("faceID", &RayHit::faceID)
|
||||
.field("distance", &RayHit::distance)
|
||||
.field("position", &RayHit::position)
|
||||
.field("normal", &RayHit::normal);
|
||||
|
||||
value_object<Properties>("properties")
|
||||
.field("surfaceArea", &Properties::surfaceArea)
|
||||
.field("volume", &Properties::volume);
|
||||
class_<ExecutionContext>("ExecutionContext")
|
||||
.constructor<>()
|
||||
.function("cancel", &ExecutionContext::Cancel)
|
||||
.function("cancelled", &ExecutionContext::Cancelled)
|
||||
.function("progress", &ExecutionContext::Progress)
|
||||
.function("_FromMesh", &man_js::ExecutionContextFromMesh)
|
||||
.function("_LevelSet", &man_js::ExecutionContextLevelSet);
|
||||
|
||||
value_object<Curvature>("curvature")
|
||||
.field("maxMeanCurvature", &Curvature::maxMeanCurvature)
|
||||
.field("minMeanCurvature", &Curvature::minMeanCurvature)
|
||||
.field("maxGaussianCurvature", &Curvature::maxGaussianCurvature)
|
||||
.field("minGaussianCurvature", &Curvature::minGaussianCurvature)
|
||||
.field("vertMeanCurvature", &Curvature::vertMeanCurvature)
|
||||
.field("vertGaussianCurvature", &Curvature::vertGaussianCurvature);
|
||||
|
||||
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<RayHit>("Vector_rayHit");
|
||||
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<int>("Vector_i32");
|
||||
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)
|
||||
.function("_add", &cross_js::Union)
|
||||
.function("_subtract", &cross_js::Difference)
|
||||
.function("_intersect", &cross_js::Intersection)
|
||||
.function("_Warp", &cross_js::Warp)
|
||||
.function("transform", &cross_js::Transform)
|
||||
.function("_Translate", &CrossSection::Translate)
|
||||
.function("rotate", &CrossSection::Rotate)
|
||||
.function("_Scale", &CrossSection::Scale)
|
||||
.function("_Mirror", &CrossSection::Mirror)
|
||||
.function("_Decompose", &CrossSection::Decompose)
|
||||
.function("isEmpty", &CrossSection::IsEmpty)
|
||||
.function("area", &CrossSection::Area)
|
||||
.function("numVert", &CrossSection::NumVert)
|
||||
.function("numContour", &CrossSection::NumContour)
|
||||
.function("_Bounds", &CrossSection::Bounds)
|
||||
.function("_Simplify", &CrossSection::Simplify)
|
||||
.function("tolerance", &CrossSection::GetTolerance)
|
||||
.function("setTolerance", &CrossSection::SetTolerance)
|
||||
.function("_Offset", &cross_js::Offset)
|
||||
.function("_ToPolygons", &CrossSection::ToPolygons)
|
||||
.function("hull",
|
||||
select_overload<CrossSection() const>(&CrossSection::Hull));
|
||||
|
||||
// CrossSection Static Methods
|
||||
function("_EvenOdd", &cross_js::EvenOdd);
|
||||
function("_Square", &CrossSection::Square);
|
||||
function("_Circle", &CrossSection::Circle);
|
||||
function("_crossSectionUnionN", &cross_js::UnionN);
|
||||
function("_crossSectionDifferenceN", &cross_js::DifferenceN);
|
||||
function("_crossSectionIntersectionN", &cross_js::IntersectionN);
|
||||
function("_crossSectionCollectVertices", &cross_js::CollectVertices);
|
||||
function("_crossSectionHullPoints",
|
||||
select_overload<CrossSection(const std::vector<vec2>&)>(
|
||||
&CrossSection::Hull));
|
||||
|
||||
class_<Manifold>("Manifold")
|
||||
.constructor(&FromMeshJS)
|
||||
.function("add", &Union)
|
||||
.function("subtract", &Difference)
|
||||
.function("intersect", &Intersection)
|
||||
.constructor(&man_js::FromMeshJS)
|
||||
.function("add", &man_js::Union)
|
||||
.function("subtract", &man_js::Difference)
|
||||
.function("intersect", &man_js::Intersection)
|
||||
.function("_Split", &man_js::Split)
|
||||
.function("_SplitByPlane", &man_js::SplitByPlane)
|
||||
.function("_TrimByPlane", &Manifold::TrimByPlane)
|
||||
.function("_GetMeshJS", &GetMeshJS)
|
||||
.function("minkowskiSum", &Manifold::MinkowskiSum)
|
||||
.function("minkowskiDifference", &Manifold::MinkowskiDifference)
|
||||
.function("_Slice", &Manifold::Slice)
|
||||
.function("_Project", &Manifold::Project)
|
||||
.function("hull", select_overload<Manifold() const>(&Manifold::Hull))
|
||||
.function("_GetMeshJS", &js::GetMeshJS)
|
||||
.function("refine", &Manifold::Refine)
|
||||
.function("_Warp", &Warp)
|
||||
.function("transform", &Transform)
|
||||
.function("refineToLength", &Manifold::RefineToLength)
|
||||
.function("refineToTolerance", &Manifold::RefineToTolerance)
|
||||
.function("_SmoothByNormals", &Manifold::SmoothByNormals)
|
||||
.function("_Warp", &man_js::Warp)
|
||||
.function("_WarpBatch", &man_js::WarpBatch)
|
||||
.function("_SetProperties", &man_js::SetProperties)
|
||||
.function("transform", &man_js::Transform)
|
||||
.function("_Translate", &Manifold::Translate)
|
||||
.function("_Rotate", &Manifold::Rotate)
|
||||
.function("_Scale", &Manifold::Scale)
|
||||
@@ -249,36 +186,54 @@ 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("withContext", &Manifold::WithContext)
|
||||
.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("_getCurvature", &Manifold::GetCurvature)
|
||||
.function("volume", &Manifold::Volume)
|
||||
.function("surfaceArea", &Manifold::SurfaceArea)
|
||||
.function("minGap", &Manifold::MinGap)
|
||||
.function("_RayCast", &man_js::RayCast)
|
||||
.function("_WindingNumber", &Manifold::WindingNumber)
|
||||
.function("calculateCurvature", &Manifold::CalculateCurvature)
|
||||
.function("_CalculateNormals", &Manifold::CalculateNormals)
|
||||
.function("originalID", &Manifold::OriginalID)
|
||||
.function("asOriginal", &Manifold::AsOriginal);
|
||||
|
||||
// Manifold Static Methods
|
||||
function("_Cube", &Manifold::Cube);
|
||||
function("_Cylinder", &Manifold::Cylinder);
|
||||
function("_Sphere", &Manifold::Sphere);
|
||||
function("tetrahedron", &Manifold::Tetrahedron);
|
||||
function("_Smooth", &Smooth);
|
||||
function("_Extrude", &Extrude);
|
||||
function("_Tetrahedron", &Manifold::Tetrahedron);
|
||||
function("_Extrude", &Manifold::Extrude);
|
||||
function("_Triangulate", &Triangulate);
|
||||
function("_Revolve", &Revolve);
|
||||
function("_LevelSet", &LevelSetJs);
|
||||
|
||||
function("_unionN", &UnionN);
|
||||
function("_differenceN", &DifferenceN);
|
||||
function("_intersectionN", &IntersectionN);
|
||||
function("_Compose", &Manifold::Compose);
|
||||
function("_Revolve", &Manifold::Revolve);
|
||||
function("_LevelSet", &man_js::LevelSet);
|
||||
function("_Merge", &js::Merge);
|
||||
function("_ReserveIDs", &Manifold::ReserveIDs);
|
||||
function("_manifoldCompose", &man_js::UnionN);
|
||||
function("_manifoldUnionN", &man_js::UnionN);
|
||||
function("_manifoldDifferenceN", &man_js::DifferenceN);
|
||||
function("_manifoldIntersectionN", &man_js::IntersectionN);
|
||||
function("_manifoldCollectVertices", &man_js::CollectVertices);
|
||||
function(
|
||||
"_manifoldHullPoints",
|
||||
select_overload<Manifold(const std::vector<vec3>&)>(&Manifold::Hull));
|
||||
|
||||
// Quality Globals
|
||||
function("setMinCircularAngle", &Quality::SetMinCircularAngle);
|
||||
function("setMinCircularEdgeLength", &Quality::SetMinCircularEdgeLength);
|
||||
function("setCircularSegments", &Quality::SetCircularSegments);
|
||||
function("getCircularSegments", &Quality::GetCircularSegments);
|
||||
function("reserveIDs", &Manifold::ReserveIDs);
|
||||
function("resetToCircularDefaults", &Quality::ResetToDefaults);
|
||||
|
||||
function("initTBB", &initTBB);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@ 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) {
|
||||
if (list) {
|
||||
for (let x of list) {
|
||||
@@ -33,6 +38,21 @@ Module.setup = function() {
|
||||
return result;
|
||||
}
|
||||
|
||||
function vec2polygons(vec, f = x => x) {
|
||||
const result = [];
|
||||
const nPoly = vec.size();
|
||||
for (let i = 0; i < nPoly; i++) {
|
||||
const v = vec.get(i);
|
||||
const nPts = v.size();
|
||||
const poly = [];
|
||||
for (let j = 0; j < nPts; j++) {
|
||||
poly.push(f(v.get(j)));
|
||||
}
|
||||
result.push(poly);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function polygons2vec(polygons) {
|
||||
if (polygons[0].length < 3) {
|
||||
polygons = [polygons];
|
||||
@@ -50,7 +70,15 @@ Module.setup = function() {
|
||||
polygonsVec.delete();
|
||||
}
|
||||
|
||||
function vararg2vec(vec) {
|
||||
function vararg2vec2(vec) {
|
||||
if (vec[0] instanceof Array) return {x: vec[0][0], y: vec[0][1]};
|
||||
if (typeof (vec[0]) == 'number')
|
||||
// default to 0
|
||||
return {x: vec[0] || 0, y: vec[1] || 0};
|
||||
return vec[0];
|
||||
}
|
||||
|
||||
function vararg2vec3(vec) {
|
||||
if (vec[0] instanceof Array)
|
||||
return {x: vec[0][0], y: vec[0][1], z: vec[0][2]};
|
||||
if (typeof (vec[0]) == 'number')
|
||||
@@ -59,43 +87,275 @@ Module.setup = function() {
|
||||
return vec[0];
|
||||
}
|
||||
|
||||
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 vert = [x, y, z];
|
||||
function joinTypeToInt(joinType) {
|
||||
return joinType == 'Round' ? 1 :
|
||||
joinType == 'Miter' ? 2 :
|
||||
joinType == 'Bevel' ? 3 :
|
||||
/* Square */ 0;
|
||||
}
|
||||
|
||||
// CrossSection methods
|
||||
|
||||
const CrossSectionCtor = Module.CrossSection;
|
||||
|
||||
function cross(polygons) {
|
||||
if (polygons instanceof CrossSectionCtor) {
|
||||
return polygons;
|
||||
} else {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const cs = new CrossSectionCtor(polygonsVec);
|
||||
disposePolygons(polygonsVec);
|
||||
return cs;
|
||||
}
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.translate = function(...vec) {
|
||||
return this._Translate(vararg2vec2(vec));
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.scale = function(vec) {
|
||||
// if only one factor provided, scale both x and y with it
|
||||
if (typeof vec == 'number') {
|
||||
return this._Scale({x: vec, y: vec});
|
||||
}
|
||||
return this._Scale(vararg2vec2([vec]));
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.mirror = function(vec) {
|
||||
return this._Mirror(vararg2vec2([vec]));
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.warp = function(func) {
|
||||
const wasmFuncPtr = addFunction(function(vec2Ptr) {
|
||||
const x = getValue(vec2Ptr, 'double');
|
||||
const y = getValue(vec2Ptr + 8, 'double');
|
||||
const vert = [x, y];
|
||||
func(vert);
|
||||
setValue(vec3Ptr, vert[0], 'float');
|
||||
setValue(vec3Ptr + 4, vert[1], 'float');
|
||||
setValue(vec3Ptr + 8, vert[2], 'float');
|
||||
setValue(vec2Ptr, vert[0], 'double');
|
||||
setValue(vec2Ptr + 8, vert[1], 'double');
|
||||
}, 'vi');
|
||||
const out = this._Warp(wasmFuncPtr);
|
||||
removeFunction(wasmFuncPtr);
|
||||
return out;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.translate = function(...vec) {
|
||||
return this._Translate(vararg2vec(vec));
|
||||
Module.CrossSection.prototype.decompose = function() {
|
||||
const vec = this._Decompose();
|
||||
const result = fromVec(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.rotate = function(vec) {
|
||||
return this._Rotate(...vec);
|
||||
Module.CrossSection.prototype.bounds = function() {
|
||||
const result = this._Bounds();
|
||||
return {
|
||||
min: ['x', 'y'].map(f => result.min[f]),
|
||||
max: ['x', 'y'].map(f => result.max[f]),
|
||||
};
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.offset = function(
|
||||
delta, joinType = 'Round', miterLimit = 2.0, circularSegments = 0) {
|
||||
return this._Offset(
|
||||
delta, joinTypeToInt(joinType), miterLimit, circularSegments);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.simplify = function(tolerance = 0) {
|
||||
return this._Simplify(tolerance);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.extrude = function(
|
||||
height, nDivisions = 0, twistDegrees = 0.0, scaleTop = [1.0, 1.0],
|
||||
center = false) {
|
||||
scaleTop = vararg2vec2([scaleTop]);
|
||||
const man = Module._Extrude(
|
||||
this._ToPolygons(), height, nDivisions, twistDegrees, scaleTop);
|
||||
return (center ? man.translate([0., 0., -height / 2.]) : man);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.revolve = function(
|
||||
circularSegments = 0, revolveDegrees = 360.0) {
|
||||
return Module._Revolve(
|
||||
this._ToPolygons(), circularSegments, revolveDegrees);
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.add = function(other) {
|
||||
return this._add(cross(other));
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.subtract = function(other) {
|
||||
return this._subtract(cross(other));
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.intersect = function(other) {
|
||||
return this._intersect(cross(other));
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype.toPolygons = function() {
|
||||
const vec = this._ToPolygons();
|
||||
const result = vec2polygons(vec, v => [v.x, v.y]);
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
// Manifold methods
|
||||
|
||||
Module.Manifold.prototype.warp = function(func) {
|
||||
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
||||
const x = getValue(vec3Ptr, 'double');
|
||||
const y = getValue(vec3Ptr + 8, 'double');
|
||||
const z = getValue(vec3Ptr + 16, 'double');
|
||||
const vert = [x, y, z];
|
||||
func(vert);
|
||||
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 !== 'NoError') {
|
||||
throw new Module.ManifoldError(status);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.warpBatch = function(func) {
|
||||
const wasmFuncPtr = addFunction(function(ptr, count) {
|
||||
const heapF64 = Module.HEAPF64 ?? HEAPF64;
|
||||
if (!heapF64) {
|
||||
throw new Error('WASM heap is not initialized (HEAPF64 unavailable)');
|
||||
}
|
||||
const verts = new Float64Array(heapF64.buffer, ptr, count * 3);
|
||||
|
||||
func(verts, count);
|
||||
}, 'vii');
|
||||
|
||||
const out = this._WarpBatch(wasmFuncPtr);
|
||||
removeFunction(wasmFuncPtr);
|
||||
|
||||
const status = out.status();
|
||||
if (status !== 'NoError') {
|
||||
throw new Module.ManifoldError(status);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.calculateNormals = function(
|
||||
normalIdx = 0, minSharpAngle = 52.5) {
|
||||
return this._CalculateNormals(normalIdx, minSharpAngle);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.smoothByNormals = function(normalIdx = 0) {
|
||||
return this._SmoothByNormals(normalIdx);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.setProperties = function(numProp, func) {
|
||||
const oldNumProp = this.numProp();
|
||||
const wasmFuncPtr = addFunction(function(newPtr, vec3Ptr, oldPtr) {
|
||||
const newProp = [];
|
||||
for (let i = 0; i < numProp; ++i) {
|
||||
newProp[i] = getValue(newPtr + 8 * i, 'double');
|
||||
}
|
||||
const pos = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
pos[i] = getValue(vec3Ptr + 8 * i, 'double');
|
||||
}
|
||||
const oldProp = [];
|
||||
for (let i = 0; i < oldNumProp; ++i) {
|
||||
oldProp[i] = getValue(oldPtr + 8 * i, 'double');
|
||||
}
|
||||
|
||||
func(newProp, pos, oldProp);
|
||||
|
||||
for (let i = 0; i < numProp; ++i) {
|
||||
setValue(newPtr + 8 * i, newProp[i], 'double');
|
||||
}
|
||||
}, 'viii');
|
||||
const out = this._SetProperties(numProp, wasmFuncPtr);
|
||||
removeFunction(wasmFuncPtr);
|
||||
return out;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.translate = function(...vec) {
|
||||
return this._Translate(vararg2vec3(vec));
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.rotate = function(xOrVec, y, z) {
|
||||
if (Array.isArray(xOrVec)) {
|
||||
return this._Rotate(...xOrVec);
|
||||
} else {
|
||||
return this._Rotate(xOrVec, y || 0, z || 0);
|
||||
}
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.scale = function(vec) {
|
||||
// if only one factor provided, scale all three dimensions (xyz) with it
|
||||
if (typeof vec == 'number') {
|
||||
return this._Scale({x: vec, y: vec, z: vec});
|
||||
}
|
||||
return this._Scale(vararg2vec([vec]));
|
||||
return this._Scale(vararg2vec3([vec]));
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.mirror = function(vec) {
|
||||
return this._Mirror(vararg2vec([vec]));
|
||||
return this._Mirror(vararg2vec3([vec]));
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.trimByPlane = function(normal, offset) {
|
||||
return this._TrimByPlane(vararg2vec([normal]), offset);
|
||||
Module.Manifold.prototype.trimByPlane = function(normal, offset = 0.) {
|
||||
return this._TrimByPlane(vararg2vec3([normal]), offset);
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.slice = function(height = 0.) {
|
||||
const polygonsVec = this._Slice(height);
|
||||
const result = new CrossSectionCtor(polygonsVec);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.project = function() {
|
||||
const polygonsVec = this._Project();
|
||||
const result = new CrossSectionCtor(polygonsVec);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.windingNumber = function(points) {
|
||||
const inVec = new Module.Vector_vec3();
|
||||
for (const p of points) inVec.push_back(vararg2vec3([p]));
|
||||
const outVec = this._WindingNumber(inVec);
|
||||
inVec.delete();
|
||||
const result = fromVec(outVec);
|
||||
outVec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.rayCast = function(origin, endpoint) {
|
||||
const vec = this._RayCast(vararg2vec3([origin]), vararg2vec3([endpoint]));
|
||||
const result =
|
||||
fromVec(vec, hit => ({
|
||||
faceID: hit.faceID,
|
||||
distance: hit.distance,
|
||||
position: ['x', 'y', 'z'].map(f => hit.position[f]),
|
||||
normal: ['x', 'y', 'z'].map(f => hit.normal[f]),
|
||||
}));
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
Module.Manifold.prototype.split = function(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 result = fromVec(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.decompose = function() {
|
||||
@@ -105,15 +365,16 @@ Module.setup = function() {
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.getCurvature = function() {
|
||||
const result = this._getCurvature();
|
||||
const oldMeanCurvature = result.vertMeanCurvature;
|
||||
const oldGaussianCurvature = result.vertGaussianCurvature;
|
||||
result.vertMeanCurvature = fromVec(oldMeanCurvature);
|
||||
result.vertGaussianCurvature = fromVec(oldGaussianCurvature);
|
||||
oldMeanCurvature.delete();
|
||||
oldGaussianCurvature.delete();
|
||||
return result;
|
||||
Module.Manifold.prototype.boundingBox = function() {
|
||||
const result = this._boundingBox();
|
||||
return {
|
||||
min: ['x', 'y', 'z'].map(f => result.min[f]),
|
||||
max: ['x', 'y', 'z'].map(f => result.max[f]),
|
||||
};
|
||||
};
|
||||
|
||||
Module.Manifold.prototype.simplify = function(tolerance = 0) {
|
||||
return this._Simplify(tolerance);
|
||||
};
|
||||
|
||||
class Mesh {
|
||||
@@ -127,7 +388,9 @@ Module.setup = function() {
|
||||
runOriginalID,
|
||||
faceID,
|
||||
halfedgeTangent,
|
||||
runTransform
|
||||
runTransform,
|
||||
runFlags,
|
||||
tolerance = 0
|
||||
} = {}) {
|
||||
this.numProp = numProp;
|
||||
this.triVerts = triVerts;
|
||||
@@ -139,6 +402,8 @@ Module.setup = function() {
|
||||
this.faceID = faceID;
|
||||
this.halfedgeTangent = halfedgeTangent;
|
||||
this.runTransform = runTransform;
|
||||
this.runFlags = runFlags;
|
||||
this.tolerance = tolerance;
|
||||
}
|
||||
|
||||
get numTri() {
|
||||
@@ -153,17 +418,24 @@ Module.setup = function() {
|
||||
return this.runOriginalID.length;
|
||||
}
|
||||
|
||||
merge() {
|
||||
const {changed, mesh} = Module._Merge(this);
|
||||
Object.assign(this, {...mesh});
|
||||
return changed;
|
||||
}
|
||||
|
||||
verts(tri) {
|
||||
return this.triVerts.subarray(3 * tri, 3 * (tri + 1));
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -180,58 +452,66 @@ Module.setup = function() {
|
||||
mat4[15] = 1;
|
||||
return mat4;
|
||||
}
|
||||
|
||||
backside(run) {
|
||||
return this.runFlags != null && run < this.runFlags.length &&
|
||||
(this.runFlags[run] & 1) !== 0;
|
||||
}
|
||||
|
||||
hasNormals(run) {
|
||||
return this.runFlags != null && run < this.runFlags.length &&
|
||||
(this.runFlags[run] & 2) !== 0;
|
||||
}
|
||||
}
|
||||
|
||||
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.Manifold.prototype.boundingBox = function() {
|
||||
const result = this._boundingBox();
|
||||
return {
|
||||
min: ['x', 'y', 'z'].map(f => result.min[f]),
|
||||
max: ['x', 'y', 'z'].map(f => result.max[f]),
|
||||
};
|
||||
};
|
||||
|
||||
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:
|
||||
break;
|
||||
case 'InvalidConstruction':
|
||||
message = 'Manifold constructed with invalid parameters';
|
||||
break;
|
||||
case 'ResultTooLarge':
|
||||
message = 'Result exceeds maximum size';
|
||||
break;
|
||||
case 'InvalidTangents':
|
||||
message = 'Invalid halfedge tangents';
|
||||
break;
|
||||
}
|
||||
|
||||
const base = Error.apply(this, [message, ...args]);
|
||||
@@ -246,113 +526,273 @@ Module.setup = function() {
|
||||
{value: Module.ManifoldError, writable: true, configurable: true}
|
||||
});
|
||||
|
||||
// CrossSection Constructors
|
||||
|
||||
Module.CrossSection = function(polygons) {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const cs = new CrossSectionCtor(polygonsVec);
|
||||
disposePolygons(polygonsVec);
|
||||
return cs;
|
||||
};
|
||||
|
||||
Module.CrossSection.ofPolygons = function(polygons) {
|
||||
return new Module.CrossSection(polygons);
|
||||
};
|
||||
|
||||
Module.CrossSection.evenOdd = function(polygons) {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const cs = Module._EvenOdd(polygonsVec);
|
||||
disposePolygons(polygonsVec);
|
||||
return cs;
|
||||
};
|
||||
|
||||
Module.CrossSection.square = function(...args) {
|
||||
let size = undefined;
|
||||
if (args.length == 0)
|
||||
size = {x: 1, y: 1};
|
||||
else if (typeof args[0] == 'number')
|
||||
size = {x: args[0], y: args[0]};
|
||||
else
|
||||
size = vararg2vec2(args);
|
||||
const center = args[1] || false;
|
||||
return Module._Square(size, center);
|
||||
};
|
||||
|
||||
Module.CrossSection.circle = function(radius, circularSegments = 0) {
|
||||
return Module._Circle(radius, circularSegments);
|
||||
};
|
||||
|
||||
// allows args to be either CrossSection or polygons (constructed with
|
||||
// Positive fill)
|
||||
function crossSectionBatchbool(name) {
|
||||
return function(...args) {
|
||||
if (args.length == 1) args = args[0];
|
||||
const v = new Module.Vector_crossSection();
|
||||
for (const cs of args) v.push_back(cross(cs));
|
||||
const result = Module['_crossSection' + name](v);
|
||||
v.delete();
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
Module.CrossSection.union = crossSectionBatchbool('UnionN');
|
||||
Module.CrossSection.difference = crossSectionBatchbool('DifferenceN');
|
||||
Module.CrossSection.intersection = crossSectionBatchbool('IntersectionN');
|
||||
|
||||
function pushVec2(vec, ps) {
|
||||
toVec(vec, ps, p => {
|
||||
if (p instanceof Array) return {x: p[0], y: p[1]};
|
||||
return p;
|
||||
})
|
||||
}
|
||||
|
||||
Module.CrossSection.hull = function(...args) {
|
||||
if (args.length == 1) args = args[0];
|
||||
let pts = new Module.Vector_vec2();
|
||||
for (const cs of args) {
|
||||
if (cs instanceof CrossSectionCtor) {
|
||||
Module._crossSectionCollectVertices(pts, cs);
|
||||
} else if (
|
||||
cs instanceof Array && cs.length == 2 && typeof cs[0] == 'number') {
|
||||
pts.push_back({x: cs[0], y: cs[1]});
|
||||
} else if (cs.x) {
|
||||
pts.push_back(cs);
|
||||
} else {
|
||||
const wrap =
|
||||
((cs[0].length == 2 && typeof cs[0][0] == 'number') || cs[0].x);
|
||||
const polys = wrap ? [cs] : cs;
|
||||
for (const poly of polys) pushVec2(pts, poly);
|
||||
}
|
||||
}
|
||||
const result = Module._crossSectionHullPoints(pts);
|
||||
pts.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.CrossSection.prototype = Object.create(CrossSectionCtor.prototype);
|
||||
|
||||
// Because the constructor and prototype are being replaced, instanceof will
|
||||
// not work as desired unless we refer back to the original like this
|
||||
Object.defineProperty(Module.CrossSection, Symbol.hasInstance, {
|
||||
get: () => (t) => {
|
||||
return (t instanceof CrossSectionCtor);
|
||||
}
|
||||
});
|
||||
|
||||
// Manifold Constructors
|
||||
|
||||
const ManifoldCtor = Module.Manifold;
|
||||
Module.Manifold = function(mesh) {
|
||||
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;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype = Object.create(ManifoldCtor.prototype);
|
||||
Module.Manifold.ofMesh = function(mesh) {
|
||||
return new Module.Manifold(mesh);
|
||||
};
|
||||
|
||||
Module.cube = function(...args) {
|
||||
Module.Manifold.tetrahedron = function() {
|
||||
return Module._Tetrahedron();
|
||||
};
|
||||
|
||||
Module.Manifold.cube = function(...args) {
|
||||
let size = undefined;
|
||||
if (args.length == 0)
|
||||
size = {x: 1, y: 1, z: 1};
|
||||
else if (typeof args[0] == 'number')
|
||||
size = {x: args[0], y: args[0], z: args[0]};
|
||||
else
|
||||
size = vararg2vec(args);
|
||||
size = vararg2vec3(args);
|
||||
const center = args[1] || false;
|
||||
return Module._Cube(size, center);
|
||||
};
|
||||
|
||||
Module.cylinder = function(
|
||||
Module.Manifold.cylinder = function(
|
||||
height, radiusLow, radiusHigh = -1.0, circularSegments = 0,
|
||||
center = false) {
|
||||
return Module._Cylinder(
|
||||
height, radiusLow, radiusHigh, circularSegments, center);
|
||||
};
|
||||
|
||||
Module.sphere = function(radius, circularSegments = 0) {
|
||||
Module.Manifold.sphere = function(radius, circularSegments = 0) {
|
||||
return Module._Sphere(radius, circularSegments);
|
||||
};
|
||||
|
||||
Module.smooth = function(mesh, sharpenedEdges = []) {
|
||||
const sharp = new Module.Vector_smoothness();
|
||||
toVec(sharp, sharpenedEdges);
|
||||
const result = Module._Smooth(mesh, sharp);
|
||||
sharp.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.extrude = function(
|
||||
Module.Manifold.extrude = function(
|
||||
polygons, height, nDivisions = 0, twistDegrees = 0.0,
|
||||
scaleTop = [1.0, 1.0]) {
|
||||
if (scaleTop instanceof Array) scaleTop = {x: scaleTop[0], y: scaleTop[1]};
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const result = Module._Extrude(
|
||||
polygonsVec, height, nDivisions, twistDegrees, scaleTop);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
scaleTop = [1.0, 1.0], center = false) {
|
||||
const cs = (polygons instanceof CrossSectionCtor) ?
|
||||
polygons :
|
||||
Module.CrossSection(polygons);
|
||||
return cs.extrude(height, nDivisions, twistDegrees, scaleTop, center);
|
||||
};
|
||||
|
||||
Module.triangulate = function(polygons, precision = -1) {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const result = fromVec(Module._Triangulate(polygonsVec, precision), (x) => [x[0], x[1], x[2]]);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
}
|
||||
|
||||
Module.revolve = function(polygons, circularSegments = 0) {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const result = Module._Revolve(polygonsVec, circularSegments);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
Module.Manifold.revolve = function(
|
||||
polygons, circularSegments = 0, revolveDegrees = 360.0) {
|
||||
const cs = (polygons instanceof CrossSectionCtor) ?
|
||||
polygons :
|
||||
Module.CrossSection(polygons);
|
||||
return cs.revolve(circularSegments, revolveDegrees);
|
||||
};
|
||||
|
||||
Module.compose = function(manifolds) {
|
||||
const vec = new Module.Vector_manifold();
|
||||
toVec(vec, manifolds);
|
||||
const result = Module._Compose(vec);
|
||||
vec.delete();
|
||||
return result;
|
||||
Module.Manifold.reserveIDs = function(n) {
|
||||
return Module._ReserveIDs(n);
|
||||
};
|
||||
|
||||
Module.levelSet = function(sdf, bounds, edgeLength, level = 0) {
|
||||
const bounds2 = {
|
||||
min: {x: bounds.min[0], y: bounds.min[1], z: bounds.min[2]},
|
||||
max: {x: bounds.max[0], y: bounds.max[1], z: bounds.max[2]},
|
||||
};
|
||||
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
||||
const x = getValue(vec3Ptr, 'float');
|
||||
const y = getValue(vec3Ptr + 4, 'float');
|
||||
const z = getValue(vec3Ptr + 8, 'float');
|
||||
const vert = [x, y, z];
|
||||
return sdf(vert);
|
||||
}, 'fi');
|
||||
const out = Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level);
|
||||
removeFunction(wasmFuncPtr);
|
||||
return out;
|
||||
};
|
||||
|
||||
function batchbool(name) {
|
||||
function manifoldBatchbool(name) {
|
||||
return function(...args) {
|
||||
if (args.length == 1) args = args[0];
|
||||
const v = new Module.Vector_manifold();
|
||||
for (const m of args) v.push_back(m);
|
||||
const result = Module['_' + name + 'N'](v);
|
||||
const result = Module['_manifold' + name + 'N'](v);
|
||||
v.delete();
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
Module.union = batchbool('union');
|
||||
Module.difference = batchbool('difference');
|
||||
Module.intersection = batchbool('intersection');
|
||||
Module.Manifold.union = manifoldBatchbool('Union');
|
||||
// Aliasing compose to union.
|
||||
// Native compose has some issues, and is deprecated.
|
||||
Module.Manifold.compose = Module.Manifold.union;
|
||||
|
||||
Module.Manifold.difference = manifoldBatchbool('Difference');
|
||||
Module.Manifold.intersection = manifoldBatchbool('Intersection');
|
||||
|
||||
Module.Manifold.levelSet = function(
|
||||
sdf, bounds, edgeLength, level = 0, tolerance = -1) {
|
||||
const bounds2 = {
|
||||
min: {x: bounds.min[0], y: bounds.min[1], z: bounds.min[2]},
|
||||
max: {x: bounds.max[0], y: bounds.max[1], z: bounds.max[2]},
|
||||
};
|
||||
const wasmFuncPtr = addFunction(function(vec3Ptr) {
|
||||
const x = getValue(vec3Ptr, 'double');
|
||||
const y = getValue(vec3Ptr + 8, 'double');
|
||||
const z = getValue(vec3Ptr + 16, 'double');
|
||||
const vert = [x, y, z];
|
||||
return sdf(vert);
|
||||
}, 'di');
|
||||
const out =
|
||||
Module._LevelSet(wasmFuncPtr, bounds2, edgeLength, level, tolerance);
|
||||
removeFunction(wasmFuncPtr);
|
||||
return out;
|
||||
};
|
||||
|
||||
// ctx-aware static factories: mirror Manifold.ofMesh / levelSet but
|
||||
// run under this ExecutionContext so progress/cancellation are observed.
|
||||
Module.ExecutionContext.prototype.fromMesh = function(mesh) {
|
||||
return this._FromMesh(mesh);
|
||||
};
|
||||
|
||||
Module.ExecutionContext.prototype.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, 'double');
|
||||
const y = getValue(vec3Ptr + 8, 'double');
|
||||
const z = getValue(vec3Ptr + 16, 'double');
|
||||
const vert = [x, y, z];
|
||||
return sdf(vert);
|
||||
}, 'di');
|
||||
const out =
|
||||
this._LevelSet(wasmFuncPtr, bounds2, edgeLength, level, tolerance);
|
||||
removeFunction(wasmFuncPtr);
|
||||
return out;
|
||||
};
|
||||
|
||||
function pushVec3(vec, ps) {
|
||||
toVec(vec, ps, p => {
|
||||
if (p instanceof Array) return {x: p[0], y: p[1], z: p[2]};
|
||||
return p;
|
||||
})
|
||||
}
|
||||
|
||||
Module.Manifold.hull = function(...args) {
|
||||
if (args.length == 1) args = args[0];
|
||||
let pts = new Module.Vector_vec3();
|
||||
for (const m of args) {
|
||||
if (m instanceof ManifoldCtor) {
|
||||
Module._manifoldCollectVertices(pts, m);
|
||||
} else if (
|
||||
m instanceof Array && m.length == 3 && typeof m[0] == 'number') {
|
||||
pts.push_back({x: m[0], y: m[1], z: m[2]});
|
||||
} else if (m.x) {
|
||||
pts.push_back(m);
|
||||
} else {
|
||||
pushVec3(pts, m);
|
||||
}
|
||||
}
|
||||
const result = Module._manifoldHullPoints(pts);
|
||||
pts.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Module.Manifold.prototype = Object.create(ManifoldCtor.prototype);
|
||||
|
||||
// Because the constructor and prototype are being replaced, instanceof will
|
||||
// not work as desired unless we refer back to the original like this
|
||||
Object.defineProperty(Module.Manifold, Symbol.hasInstance, {
|
||||
get: () => (t) => {
|
||||
return (t instanceof ManifoldCtor);
|
||||
}
|
||||
});
|
||||
|
||||
// Top-level functions
|
||||
|
||||
Module.triangulate = function(polygons, epsilon = -1, allowConvex = true) {
|
||||
const polygonsVec = polygons2vec(polygons);
|
||||
const result = fromVec(
|
||||
Module._Triangulate(polygonsVec, epsilon, allowConvex),
|
||||
(x) => [x[0], x[1], x[2]]);
|
||||
disposePolygons(polygonsVec);
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: Manifold Examples
|
||||
group: none
|
||||
---
|
||||
# Manifold Examples
|
||||
|
||||
## Interoperation with three.js
|
||||
|
||||
{@include ../examples/three/README.md#summary}
|
||||
|
||||
## <model-viewer>
|
||||
|
||||
{@include ../examples/model-viewer/README.md#summary}
|
||||
|
||||
## Manifoldness and EXT_mesh_manifold
|
||||
|
||||
{@include ../examples/make-manifold/README.md#summary}
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Using Manifold
|
||||
group: none
|
||||
---
|
||||
# Using Manifold
|
||||
|
||||
## Installation
|
||||
|
||||
In your project root folder run:
|
||||
|
||||
```bash
|
||||
npm install --save manifold-3d
|
||||
```
|
||||
|
||||
To start using Manifold, import it and initialize it:
|
||||
|
||||
```js
|
||||
import Module from 'manifold-3d';
|
||||
|
||||
const wasm = await Module();
|
||||
wasm.setup();
|
||||
const { Manifold } = wasm;
|
||||
```
|
||||
|
||||
Intro example
|
||||
|
||||
```js
|
||||
const {cube, sphere} = Manifold;
|
||||
const box = cube([100, 100, 100], true);
|
||||
const ball = sphere(60, 100);
|
||||
const result = box.subtract(ball);
|
||||
```
|
||||
|
||||
## Memory Management
|
||||
|
||||
> [!WARNING]
|
||||
> Since Manifold is a WASM module, it does not automatically garbage-collect like regular JavaScript.
|
||||
|
||||
You must manually `delete()` each object constructed by your scripts (both `Manifold` and `CrossSection`).
|
||||
See [discussion](https://github.com/elalish/manifold/discussions/256#discussioncomment-3944287).
|
||||
|
||||
## Examples
|
||||
|
||||
See [Examples](./bindings-examples.md).
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Contributing
|
||||
group: none
|
||||
category: none
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
To debug the WASM build directly in Chrome dev tools, simply build in debug mode:
|
||||
```
|
||||
emcmake cmake -DCMAKE_BUILD_TYPE=Debug .. && emmake make
|
||||
```
|
||||
and install the [DWARF](goo.gle/wasm-debugging-extension) Chrome extension.
|
||||
|
||||
When testing [ManifoldCAD.org](https://manifoldcad.org/) (either locally or the
|
||||
deployed version) note that it uses a service worker for faster loading. This
|
||||
means you need to open the page twice to see updates (the first time loads the
|
||||
old version and caches the new one, the second time loads the new version from
|
||||
cache). To see changes on each reload, open Chrome dev tools, go to the
|
||||
Application tab and check "update on reload".
|
||||
|
||||
## Documentation
|
||||
|
||||
This guide and the ManifoldCAD User Guide are built using [TypeDoc](https://typedoc.org).
|
||||
|
||||
### Building Documentation
|
||||
|
||||
Documentation can be built using NPM:
|
||||
|
||||
```bash
|
||||
cd bindings/wasm
|
||||
npm install
|
||||
npm run build
|
||||
npm run docs
|
||||
```
|
||||
|
||||
After this, the User and Developer docs will be in `docs/jsuser` and `docs/jsapi` respectively.
|
||||
|
||||
### Writing for the User Guide
|
||||
|
||||
The ManifoldCAD User Guide is generated from the declaration files present in `types/`. These mostly re-export declarations, allowing the User Guide to inherit from the Developer Guide.
|
||||
|
||||
TypeDoc reflections (comments) flagged as `@internal` will _not_ be visible in the User Guide, but will be present in the Developer Guide. Reflections tagged as `@hidden` will not be visible in either guide.
|
||||
|
||||
### WASM Bindings
|
||||
|
||||
The manifold library itself is documented in three files:
|
||||
|
||||
* `manifold-global-types.d.ts`: Defines lower level types and interfaces like {@link manifold.Vec2 | Vec2}, {@link manifold.Mat3 | Mat3}, &c.
|
||||
* `manifold-encapsulated-types.d.ts`: Declares objects and types that are implemented in C++, and compiled to WASM. Definitions of classes like {@link manifold.Manifold | Manifold} and {@link manifold.CrossSection | CrossSection} can be found here.
|
||||
* `manifold-root.d.ts`: Defines the {@link manifold.ManifoldToplevel | ManifoldToplevel} interface, and re-exports the above two files.
|
||||
|
||||
These three files are consolidated into `manifold.d.ts` using [API Extractor](https://api-extractor.com/). This is automatically run before each build as an npm lifecycle hook. It can be run manually as `npm run prebuild`.
|
||||
|
||||
### Terminology
|
||||
|
||||
These are general guidelines, not hard rules. The aim of this section is to encourage consistent documentation, not to institute brand standards.
|
||||
|
||||
* When referring to the library, manifold should generally be lowercase. Manifold can be capitalized when grammatically correct, for example at the start of a sentence. If unclear, `manifold` can be marked up as code to distinguish it from the manifold property.
|
||||
* The Manifold class should generally be capitalized. Where appropriate, it can be linked directly to the {@link manifold.Manifold | Manifold} class documentation.
|
||||
* ManifoldCAD should be capitalized. [ManifoldCAD.org](https://manifoldcad.org) should be a link.
|
||||
@@ -0,0 +1,6 @@
|
||||
@layer typedoc {
|
||||
:root {
|
||||
--light-color-background-secondary: #c0c000;
|
||||
--dark-color-background-secondary: #606000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Embedding ManifoldCAD
|
||||
group: ManifoldCAD
|
||||
---
|
||||
# Embedding ManifoldCAD
|
||||
|
||||
There are three ways to use manifold in your own projects:
|
||||
|
||||
1. Using the ManifoldCAD worker. The worker adds overhead, but it will sandbox scripts. It also makes it possible to include packages at runtime.
|
||||
1. Using ManifoldCAD libraries directly. ManifoldCAD implements importers, exporters, and garbage collection amongst other utilities. These modules are reasonably light and can be used outside of manifoldCAD.
|
||||
1. Using the [Manifold WASM module directly](./bindings.md) without any of the above.
|
||||
|
||||
## Using the ManifoldCAD worker
|
||||
|
||||
The worker is a code evaluation sandbox intended to run as a [web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). It will bundle a script, include any dependencies, and even fetch those dependencies from a CDN if required. At run time, it will evaluate the script as a [dynamically created function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function).
|
||||
|
||||
See:
|
||||
|
||||
* {@link lib/worker | worker API}
|
||||
* [manifold-cad](https://github.com/elalish/manifold/blob/master/bindings/wasm/bin/manifold-cad) CLI
|
||||
|
||||
### Detecting the worker
|
||||
|
||||
> [!NOTE]
|
||||
> There are slight behaviour differences between scripts running inside the manifoldCAD worker, and those outside.
|
||||
>
|
||||
> See our [import integration tests](https://github.com/elalish/manifold/blob/master/bindings/wasm/test/import.test.ts) for examples.
|
||||
|
||||
When evaluated through the worker, {@link manifoldCAD!isManifoldCAD | isManifoldCAD()} will return true. Otherwise it returns false.
|
||||
|
||||
## Using the ManifoldCAD runtime directly
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: Tips and Tricks
|
||||
---
|
||||
## Handling Precision Issues
|
||||
|
||||
> [!WARNING]
|
||||
> Geometry where two manifolds are meant to share a face exactly is called **marginal geometry** and should generally be avoided. Manifold uses symbolic perturbation, which means multiple topologically valid results may be within rounding error of each other — so even small imprecisions can produce unexpected output.
|
||||
|
||||
Two common sources of trouble:
|
||||
|
||||
- **Floating point drift** — JavaScript uses IEEE 754 binary floating point, so `0.1 + 0.2` evaluates to `0.30000000000000004`. Manifold does not compensate for this in its transform pipeline. A bounding box maximum of `0.30000000000000004` instead of `0.3` is enough to prevent a union from closing properly.
|
||||
- **Different triangulations** — even with floating-point identical coordinates, if two coplanar faces are triangulated differently, that can also lead to gaps and slivers.
|
||||
|
||||
Where marginal geometry is unavoidable, the recommended workaround for floating point drift is to **work in larger, integer-friendly units**:
|
||||
|
||||
1. Scale coordinates up by a round factor (e.g., 1000) so your values are whole numbers
|
||||
2. Use `Math.round()` after any arithmetic to eliminate accumulated drift
|
||||
3. Perform all Manifold operations
|
||||
4. Scale the result back down if needed
|
||||
|
||||
```js
|
||||
const scale = 1000;
|
||||
|
||||
// Instead of cube([0.1, 0.1, 0.1]) translated by [0.2, 0.2, 0.2]:
|
||||
const size = Math.round(0.1 * scale); // 100
|
||||
const offset = Math.round(0.2 * scale); // 200
|
||||
|
||||
const { cube } = Manifold;
|
||||
const box = cube([size, size, size]).translate([offset, offset, offset]);
|
||||
```
|
||||
|
||||
See [discussion #1135](https://github.com/elalish/manifold/discussions/1135) for more context.
|
||||
|
||||
## Degrees vs. Radians
|
||||
|
||||
Manifold's rotation API takes **degrees**, not radians. This is a deliberate design choice: Manifold can eliminate floating point error entirely for multiples of 90°, and uses more efficient code paths when it detects such rotations.
|
||||
|
||||
```js
|
||||
// Pass degrees directly
|
||||
box.rotate([90, 0, 0]);
|
||||
box.rotate([0, 45, 0]);
|
||||
```
|
||||
|
||||
If your own code works in radians (e.g. when using `Math.atan2`), convert to degrees before passing to Manifold:
|
||||
|
||||
```js
|
||||
const toDeg = (rad) => rad * (180 / Math.PI);
|
||||
|
||||
const angle = Math.atan2(y, x); // radians
|
||||
box.rotate([0, 0, toDeg(angle)]);
|
||||
```
|
||||
|
||||
For common angles, always prefer the exact degree value (`90`, `45`, `30`, etc.) over a computed approximation to get the precision guarantee. See [issue #1262](https://github.com/elalish/manifold/issues/1262) for a real-world example where a rotation of `-89.999999999999` instead of `-90` caused mesh cracks.
|
||||
@@ -0,0 +1,12 @@
|
||||
@layer typedoc {
|
||||
:root {
|
||||
--light-color-background-secondary: #A288B3;
|
||||
--dark-color-background-secondary: #675772;
|
||||
}
|
||||
/* Hide right-hand navigation bar.
|
||||
* It is redundant for such a small site.
|
||||
*/
|
||||
.tsd-page-navigation {
|
||||
display: none
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Using ManifoldCAD
|
||||
---
|
||||
## Running ManifoldCAD
|
||||
|
||||
### With a Browser
|
||||
|
||||
Just go to [ManifoldCAD.org](https://manifoldcad.org), and check out the examples!
|
||||
|
||||
### On the Command Line
|
||||
|
||||
ManifoldCAD has a [command line interface](https://github.com/elalish/manifold/blob/master/bindings/wasm/bin/manifold-cad). It can be run directly as `./bin/manifold-cad`. It can also be run via `npx`. If Manifold is not already present, use `npx manifold-3d manifold-cad` or `npx manifold-3d`. If Manifold is already installed, `npx manifold-cad` will suffice.
|
||||
|
||||
```
|
||||
Usage: manifold-cad [options] <infile.js> <outfile>
|
||||
```
|
||||
|
||||
The output file can be in either `.glb` or `.3mf` format, determined by extension.
|
||||
|
||||
## Coordinate Systems
|
||||
|
||||
ManifoldCAD uses a right-handed coordinate system where '+Z' is up. Length units are assumed to be millimetres. This is slightly at odds with glTF, which also uses a right handed coordinate system, but specifies '+Y' as up, and length units are in metres.
|
||||
|
||||
ManifoldCAD will scale and rotate automatically when importing or exporting glTF models. This means that an arrow pointing in the '+Z' direction in will point in the '+Y' direction after export. However, that arrow will still point up, and will still be the same size.
|
||||
@@ -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', 'node:fs', 'node:module', 'module',
|
||||
]
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
manifold*
|
||||
@@ -1,224 +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.
|
||||
*/
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
padding: 0 1em;
|
||||
cursor: pointer;
|
||||
height: 36px;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
text-align: left;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a:link,
|
||||
a:visited {
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:active {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
color: black;
|
||||
background-color: #eee !important;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
input {
|
||||
border: none;
|
||||
height: 36px;
|
||||
background-color: transparent;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
text-align: left;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
model-viewer {
|
||||
position: relative;
|
||||
flex: 3;
|
||||
width: 100%;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
#console {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-height: 25vh;
|
||||
overflow-y: scroll;
|
||||
background-color: #eee;
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.col {
|
||||
position: relative;
|
||||
width: 50vw;
|
||||
}
|
||||
|
||||
.center {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
|
||||
.header .margin {
|
||||
font-size: 16px;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#file {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 250px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.uparrow {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border: solid black;
|
||||
border-width: 0 3px 3px 0;
|
||||
padding: 3px;
|
||||
transform: rotate(-135deg);
|
||||
}
|
||||
|
||||
.down {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
width: 36px;
|
||||
top: 0px;
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background-image: url(icons/close.png);
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.edit {
|
||||
background-image: url(icons/pencil.png);
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.trash {
|
||||
background-image: url(icons/trash.png);
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.blue {
|
||||
background-color: rgb(148, 221, 255);
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: rgb(131, 255, 131);
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: rgb(255, 177, 177);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -1,417 +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 {examples} from './examples.js';
|
||||
const exampleFunctions = examples.functionBodies;
|
||||
|
||||
if (navigator.serviceWorker) {
|
||||
navigator.serviceWorker.register('service-worker.js');
|
||||
}
|
||||
|
||||
let editor = undefined;
|
||||
|
||||
// File UI ------------------------------------------------------------
|
||||
const fileButton = document.querySelector('#file');
|
||||
const currentElement = document.querySelector('#current');
|
||||
const arrow = document.querySelector('.uparrow');
|
||||
const dropdown = document.querySelector('.dropdown');
|
||||
|
||||
const hideDropdown = function() {
|
||||
dropdown.classList.remove('show');
|
||||
arrow.classList.remove('down');
|
||||
};
|
||||
const toggleDropdown = function(event) {
|
||||
event.stopPropagation();
|
||||
dropdown.classList.toggle('show');
|
||||
arrow.classList.toggle('down');
|
||||
};
|
||||
fileButton.onclick = toggleDropdown;
|
||||
document.body.onclick = hideDropdown;
|
||||
|
||||
const prefix = 'ManifoldCAD';
|
||||
function getScript(name) {
|
||||
return window.localStorage.getItem(prefix + name);
|
||||
}
|
||||
function setScript(name, code) {
|
||||
window.localStorage.setItem(prefix + name, code);
|
||||
}
|
||||
function removeScript(name) {
|
||||
window.localStorage.removeItem(prefix + name);
|
||||
}
|
||||
function nthKey(n) {
|
||||
if (n >= window.localStorage.length) return;
|
||||
const key = window.localStorage.key(n);
|
||||
if (key.startsWith(prefix)) {
|
||||
return key.slice(prefix.length);
|
||||
}
|
||||
}
|
||||
|
||||
function saveCurrent() {
|
||||
if (editor) {
|
||||
const currentName = currentElement.textContent;
|
||||
if (!exampleFunctions.get(currentName)) {
|
||||
setScript(currentName, editor.getValue());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onpagehide = saveCurrent;
|
||||
window.beforeunload = saveCurrent;
|
||||
|
||||
let switching = false;
|
||||
let isExample = true;
|
||||
function switchTo(scriptName) {
|
||||
if (editor) {
|
||||
switching = true;
|
||||
currentElement.textContent = scriptName;
|
||||
setScript('currentName', scriptName);
|
||||
const code =
|
||||
exampleFunctions.get(scriptName) ?? getScript(scriptName) ?? '';
|
||||
isExample = exampleFunctions.get(scriptName) != null;
|
||||
editor.setValue(code);
|
||||
}
|
||||
}
|
||||
|
||||
function createDropdownItem(name) {
|
||||
const container = document.createElement('div');
|
||||
container.classList.add('item');
|
||||
const button = document.createElement('button');
|
||||
container.appendChild(button);
|
||||
button.type = 'button';
|
||||
button.classList.add('blue', 'item');
|
||||
const label = document.createElement('span');
|
||||
button.appendChild(label);
|
||||
label.textContent = name;
|
||||
|
||||
button.onclick = function() {
|
||||
saveCurrent();
|
||||
switchTo(label.textContent);
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
||||
function addIcon(button) {
|
||||
const icon = document.createElement('button');
|
||||
icon.classList.add('icon');
|
||||
button.parentElement.appendChild(icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
function uniqueName(name) {
|
||||
let num = 1;
|
||||
let newName = name;
|
||||
while (getScript(newName) != null || exampleFunctions.get(newName) != null) {
|
||||
newName = name + ' ' + num++;
|
||||
}
|
||||
return newName;
|
||||
}
|
||||
|
||||
function addEdit(button) {
|
||||
const label = button.firstChild;
|
||||
const edit = addIcon(button);
|
||||
edit.classList.add('edit');
|
||||
|
||||
edit.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
const oldName = label.textContent;
|
||||
const code = getScript(oldName);
|
||||
const form = document.createElement('form');
|
||||
const inputElement = document.createElement('input');
|
||||
inputElement.value = oldName;
|
||||
label.textContent = '';
|
||||
button.appendChild(form);
|
||||
form.appendChild(inputElement);
|
||||
inputElement.focus();
|
||||
inputElement.setSelectionRange(0, oldName.length);
|
||||
|
||||
function rename() {
|
||||
const input = inputElement.value;
|
||||
inputElement.blur();
|
||||
if (!input) return;
|
||||
const newName = uniqueName(input);
|
||||
label.textContent = newName;
|
||||
if (currentElement.textContent == oldName) {
|
||||
currentElement.textContent = newName;
|
||||
}
|
||||
removeScript(oldName);
|
||||
setScript(newName, code);
|
||||
}
|
||||
|
||||
form.onsubmit = rename;
|
||||
inputElement.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
inputElement.onblur = function() {
|
||||
button.removeChild(form);
|
||||
label.textContent = oldName;
|
||||
};
|
||||
};
|
||||
|
||||
const trash = addIcon(button);
|
||||
trash.classList.add('trash');
|
||||
let lastClick = 0;
|
||||
|
||||
trash.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
if (button.classList.contains('blue')) {
|
||||
lastClick = performance.now();
|
||||
button.classList.remove('blue');
|
||||
button.classList.add('red');
|
||||
document.body.addEventListener('click', function() {
|
||||
button.classList.add('blue');
|
||||
button.classList.remove('red');
|
||||
}, {once: true});
|
||||
} else if (performance.now() - lastClick > 500) {
|
||||
removeScript(label.textContent);
|
||||
if (currentElement.textContent == label.textContent) {
|
||||
switchTo('Intro');
|
||||
}
|
||||
const container = button.parentElement;
|
||||
container.parentElement.removeChild(container);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const newButton = document.querySelector('#new');
|
||||
function newItem(code) {
|
||||
const name = uniqueName('New Script');
|
||||
setScript(name, code);
|
||||
const nextButton = createDropdownItem(name);
|
||||
newButton.insertAdjacentElement('afterend', nextButton.parentElement);
|
||||
addEdit(nextButton);
|
||||
nextButton.click();
|
||||
};
|
||||
newButton.onclick = function() {
|
||||
newItem('');
|
||||
};
|
||||
|
||||
const runButton = document.querySelector('#compile');
|
||||
const poster = document.querySelector('#poster');
|
||||
let manifoldInitialized = false;
|
||||
let autoExecute = true;
|
||||
|
||||
function initializeRun() {
|
||||
runButton.disabled = false;
|
||||
if (autoExecute) {
|
||||
runButton.click();
|
||||
} else {
|
||||
poster.textContent = 'Auto-run disabled due to prior failure';
|
||||
}
|
||||
}
|
||||
|
||||
// Editor ------------------------------------------------------------
|
||||
let tsWorker = undefined;
|
||||
|
||||
async function getManifoldDTS() {
|
||||
const global = await fetch('manifold-global-types.d.ts')
|
||||
.then(response => response.text());
|
||||
|
||||
const encapsulated = await fetch('manifold-encapsulated-types.d.ts')
|
||||
.then(response => response.text());
|
||||
|
||||
return `
|
||||
${global.replaceAll('export', '')}
|
||||
${encapsulated.replace(/^import.*$/gm, '').replaceAll('export', 'declare')}
|
||||
declare interface ManifoldStatic {
|
||||
cube: typeof cube;
|
||||
cylinder: typeof cylinder;
|
||||
sphere: typeof sphere;
|
||||
smooth: typeof smooth;
|
||||
tetrahedron: typeof tetrahedron;
|
||||
extrude: typeof extrude;
|
||||
revolve: typeof revolve;
|
||||
union: typeof union;
|
||||
difference: typeof difference;
|
||||
intersection: typeof intersection;
|
||||
compose: typeof compose;
|
||||
levelSet: typeof levelSet;
|
||||
setMinCircularAngle: typeof setMinCircularAngle;
|
||||
setMinCircularEdgeLength: typeof setMinCircularEdgeLength;
|
||||
setCircularSegments: typeof setCircularSegments;
|
||||
getCircularSegments: typeof getCircularSegments;
|
||||
reserveIDs: typeof reserveIDs;
|
||||
}
|
||||
declare const module: ManifoldStatic;
|
||||
`;
|
||||
}
|
||||
|
||||
require.config({
|
||||
paths:
|
||||
{vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs'}
|
||||
});
|
||||
require(['vs/editor/editor.main'], async function() {
|
||||
async function addTypes(uri) {
|
||||
const content = await fetch(uri).then(response => response.text());
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(content);
|
||||
}
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
await getManifoldDTS());
|
||||
await addTypes('editor.d.ts');
|
||||
editor = monaco.editor.create(
|
||||
document.getElementById('editor'),
|
||||
{language: 'typescript', automaticLayout: true});
|
||||
const w = await monaco.languages.typescript.getTypeScriptWorker();
|
||||
tsWorker = await w(editor.getModel().uri);
|
||||
editor.getModel().updateOptions({tabSize: 2});
|
||||
|
||||
for (const [name] of exampleFunctions) {
|
||||
const button = createDropdownItem(name);
|
||||
dropdown.appendChild(button.parentElement);
|
||||
}
|
||||
|
||||
let currentName = currentElement.textContent;
|
||||
|
||||
for (let i = 0; i < window.localStorage.length; i++) {
|
||||
const key = nthKey(i);
|
||||
if (!key) continue;
|
||||
if (key === 'currentName') {
|
||||
currentName = getScript(key);
|
||||
} else if (key === 'safe') {
|
||||
autoExecute = getScript(key) !== 'false';
|
||||
} else {
|
||||
const button = createDropdownItem(key);
|
||||
newButton.insertAdjacentElement('afterend', button.parentElement);
|
||||
addEdit(button);
|
||||
}
|
||||
}
|
||||
switchTo(currentName);
|
||||
|
||||
if (manifoldInitialized) {
|
||||
initializeRun();
|
||||
}
|
||||
|
||||
editor.onDidChangeModelContent(e => {
|
||||
runButton.disabled = false;
|
||||
if (switching) {
|
||||
switching = false;
|
||||
return;
|
||||
}
|
||||
if (isExample) {
|
||||
const cursor = editor.getPosition();
|
||||
newItem(editor.getValue());
|
||||
editor.setPosition(cursor);
|
||||
}
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
editor.layout({});
|
||||
};
|
||||
});
|
||||
|
||||
// Execution ------------------------------------------------------------
|
||||
const consoleElement = document.querySelector('#console');
|
||||
const oldLog = console.log;
|
||||
console.log = function(message) {
|
||||
consoleElement.textContent += message + '\r\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
oldLog(message);
|
||||
};
|
||||
|
||||
function clearConsole() {
|
||||
consoleElement.textContent = '';
|
||||
}
|
||||
|
||||
function enableCancel() {
|
||||
runButton.firstChild.style.visibility = 'hidden';
|
||||
runButton.classList.add('red', 'cancel');
|
||||
}
|
||||
|
||||
function disableCancel() {
|
||||
runButton.firstChild.style.visibility = 'visible';
|
||||
runButton.classList.remove('red', 'cancel');
|
||||
}
|
||||
|
||||
let t0 = performance.now();
|
||||
|
||||
function finishRun() {
|
||||
disableCancel();
|
||||
const t1 = performance.now();
|
||||
const log = consoleElement.textContent;
|
||||
// Remove "Running..."
|
||||
consoleElement.textContent = log.substring(log.indexOf('\n') + 1);
|
||||
console.log(
|
||||
`Took ${(Math.round((t1 - t0) / 10) / 100).toLocaleString()} seconds`);
|
||||
}
|
||||
|
||||
const mv = document.querySelector('model-viewer');
|
||||
let objectURL = null;
|
||||
let manifoldWorker = null;
|
||||
|
||||
function createWorker() {
|
||||
manifoldWorker = new Worker('worker.js', {type: 'module'});
|
||||
manifoldWorker.onmessage = function(e) {
|
||||
if (e.data == null) {
|
||||
if (tsWorker != null && !manifoldInitialized) {
|
||||
initializeRun();
|
||||
}
|
||||
manifoldInitialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.data.log != null) {
|
||||
consoleElement.textContent += e.data.log + '\r\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
return;
|
||||
}
|
||||
|
||||
finishRun();
|
||||
runButton.disabled = true;
|
||||
setScript('safe', 'true');
|
||||
|
||||
URL.revokeObjectURL(objectURL);
|
||||
objectURL = e.data.objectURL;
|
||||
mv.src = objectURL;
|
||||
if (objectURL == null) {
|
||||
mv.showPoster();
|
||||
poster.textContent = 'Error';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createWorker();
|
||||
|
||||
async function run() {
|
||||
saveCurrent();
|
||||
setScript('safe', 'false');
|
||||
enableCancel();
|
||||
clearConsole();
|
||||
console.log('Running...');
|
||||
const output = await tsWorker.getEmitOutput(editor.getModel().uri.toString());
|
||||
manifoldWorker.postMessage(output.outputFiles[0].text);
|
||||
t0 = performance.now();
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
manifoldWorker.terminate();
|
||||
createWorker();
|
||||
finishRun();
|
||||
console.log('Run canceled');
|
||||
}
|
||||
|
||||
runButton.onclick = function() {
|
||||
if (runButton.classList.contains('cancel')) {
|
||||
cancel();
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
};
|
||||
|
||||
const downloadButton = document.querySelector('#download');
|
||||
downloadButton.onclick = function() {
|
||||
const link = document.createElement('a');
|
||||
link.download = 'manifold.glb';
|
||||
link.href = objectURL;
|
||||
link.click();
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
## Local development
|
||||
|
||||
First, follow the directions in the root README to get your C++ build environment set up and working for WASM.
|
||||
|
||||
To develop the manifoldCAD.org editor as well as our other example pages, run
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
which will serve the pages, watch for changes, and automatically rebuild and refresh. This build step doesn't do TS type-checking, so to verify everything is correct (beyond VSCode's TS linting), run
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
See `package.json` for other useful scripts.
|
||||
|
||||
## Bundle Analyzer
|
||||
|
||||
To generate a bundle analysis report, run:
|
||||
|
||||
On Windows PowerShell:
|
||||
$env:ANALYZE=1; npm run build
|
||||
|
||||
On Linux/macOS:
|
||||
ANALYZE=1 npm run build
|
||||
|
||||
This will include the analyzer in the build and output a report.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import auger from '../../test/examples/auger.mjs?raw';
|
||||
import gearBearing from '../../test/examples/gear-bearing.ts?raw';
|
||||
import godelEscherBach from '../../test/examples/godel-escher-bach.ts?raw';
|
||||
import gyroidModule from '../../test/examples/gyroid-module.ts?raw';
|
||||
import heart from '../../test/examples/heart.mjs?raw';
|
||||
import importManifold from '../../test/examples/import-manifold.ts?raw';
|
||||
import importModel from '../../test/examples/import-model.ts?raw';
|
||||
import intro from '../../test/examples/intro.mjs?raw';
|
||||
import involuteGearLibrary from '../../test/examples/involute-gear-library.ts?raw';
|
||||
import mengerSponge from '../../test/examples/menger-sponge.mjs?raw';
|
||||
import roundedFrame from '../../test/examples/rounded-frame.mjs?raw';
|
||||
import scallop from '../../test/examples/scallop.mjs?raw';
|
||||
import stretchyBracelet from '../../test/examples/stretchy-bracelet.mjs?raw';
|
||||
import tetrahedronPuzzle from '../../test/examples/tetrahedron-puzzle.mjs?raw';
|
||||
import torusKnot from '../../test/examples/torus-knot.mjs?raw';
|
||||
import voronoi from '../../test/examples/voronoi.mjs?raw';
|
||||
|
||||
const examples = new Map();
|
||||
examples.set('Intro', intro);
|
||||
examples.set('Auger', auger);
|
||||
examples.set('Tetrahedron Puzzle', tetrahedronPuzzle);
|
||||
examples.set('Rounded Frame', roundedFrame);
|
||||
examples.set('Heart', heart);
|
||||
examples.set('Scallop', scallop);
|
||||
examples.set('Torus Knot', torusKnot);
|
||||
examples.set('Menger Sponge', mengerSponge);
|
||||
examples.set('Stretchy Bracelet', stretchyBracelet);
|
||||
examples.set('Gyroid Module', gyroidModule);
|
||||
examples.set('Gear Bearing', gearBearing);
|
||||
examples.set('involute-gear-library', involuteGearLibrary);
|
||||
examples.set('Voronoi', voronoi);
|
||||
examples.set('Import Model', importModel);
|
||||
examples.set('Import Manifold', importManifold);
|
||||
examples.set('Gödel, Escher, Bach', godelEscherBach)
|
||||
|
||||
if (typeof self !== 'undefined') {
|
||||
self.examples = examples;
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 24px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
padding: 0 1em;
|
||||
cursor: pointer;
|
||||
height: 36px;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
text-align: left;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a:link,
|
||||
a:visited {
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover,
|
||||
a:active {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
color: black;
|
||||
background-color: #eee !important;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.name {
|
||||
border: none;
|
||||
height: 36px;
|
||||
background-color: transparent;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 800;
|
||||
text-align: left;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.name:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
model-viewer {
|
||||
position: relative;
|
||||
flex: 3;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#console {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
/* draggable pane dividers */
|
||||
max-height: none;
|
||||
/* draggable pane dividers-end */
|
||||
overflow-y: scroll;
|
||||
background-color: #fff;
|
||||
white-space: pre-wrap;
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
line-height: 20px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.page {
|
||||
/* draggable pane dividers */
|
||||
--left-pane: 50%;
|
||||
--viewer-pane: 75%;
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
/* draggable pane dividers */
|
||||
.workbench {
|
||||
min-height: 0;
|
||||
}
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.col {
|
||||
position: relative;
|
||||
width: 50vw;
|
||||
}
|
||||
/* draggable pane dividers-start */
|
||||
.pane-left {
|
||||
width: auto;
|
||||
flex: 0 0 var(--left-pane);
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.pane-right {
|
||||
width: auto;
|
||||
flex: 1 1 auto;
|
||||
min-width: 220px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
flex: 0 0 var(--viewer-pane);
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
.splitter {
|
||||
flex: 0 0 8px;
|
||||
background: #d8c5e5;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.splitter:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.splitter-x {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.splitter-y {
|
||||
cursor: row-resize;
|
||||
}
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
|
||||
.center {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #A288B3;
|
||||
}
|
||||
|
||||
.header .margin {
|
||||
font-size: 16px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
max-height: 85dvh;
|
||||
overflow: auto;
|
||||
/* Scrollbar styles for Firefox */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #777 transparent;
|
||||
}
|
||||
|
||||
/* Minimal, borderless scrollbar for dropdown (Windows/Chrome/Edge/Safari) */
|
||||
.dropdown::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropdown::-webkit-scrollbar-thumb {
|
||||
background: #777;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropdown::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.monaco-editor .monaco-scrollable-element > .scrollbar {
|
||||
z-index: 0 !important;
|
||||
}
|
||||
|
||||
.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.uparrow {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border: solid black;
|
||||
border-width: 0 3px 3px 0;
|
||||
padding: 3px;
|
||||
transform: rotate(-135deg);
|
||||
}
|
||||
|
||||
.down {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#fileDropdown .item > button.item {
|
||||
/* Keep room on the right for the trash icon. */
|
||||
width: 100%;
|
||||
padding-right: 42px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#fileDropdown .item > button.item > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.top-edit {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 36px;
|
||||
top: 18px;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
-webkit-border-radius: 18px;
|
||||
-moz-border-radius: 18px;
|
||||
-ms-border-radius: 18px;
|
||||
-o-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: -8px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: inline-block;
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
width: 36px;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.undo {
|
||||
background-image: url(/icons/undo.png);
|
||||
}
|
||||
|
||||
.redo {
|
||||
background-image: url(/icons/redo.png);
|
||||
}
|
||||
|
||||
.star {
|
||||
background-image: url(/icons/star.png);
|
||||
}
|
||||
|
||||
.share {
|
||||
background-image: url(/icons/share.png);
|
||||
}
|
||||
|
||||
.docs {
|
||||
background-image: url(/icons/docs.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
-webkit-border-radius: 18px;
|
||||
-moz-border-radius: 18px;
|
||||
-ms-border-radius: 18px;
|
||||
-o-border-radius: 18px;
|
||||
}
|
||||
|
||||
.edges {
|
||||
background-image: url(/icons/edges.png);
|
||||
}
|
||||
|
||||
.cancel {
|
||||
background-image: url(/icons/close.png);
|
||||
background-size: 20px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.edit {
|
||||
background-image: url(/icons/pencil.png);
|
||||
}
|
||||
|
||||
.trash {
|
||||
background-image: url(/icons/trash.png);
|
||||
}
|
||||
|
||||
.play {
|
||||
background-image: url(/icons/play.png);
|
||||
}
|
||||
|
||||
.pause {
|
||||
background-image: url(/icons/pause.png);
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
box-shadow: inset 0 0 0 100px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.top-icon:hover {
|
||||
box-shadow: inset 0 0 0 100px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.blue {
|
||||
background-color: rgb(148, 221, 255);
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: rgb(131, 255, 131);
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: rgb(255, 177, 177);
|
||||
}
|
||||
|
||||
#file-margin {
|
||||
position: relative;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
#file {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#current {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#current.renaming {
|
||||
/* While typing, show full text (no three dots). */
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
#save {
|
||||
width: 125px;
|
||||
}
|
||||
|
||||
#animation {
|
||||
display: none;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
#play {
|
||||
position: relative;
|
||||
background-color: #0004;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
margin-right: 16px;
|
||||
background: #0002;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
background: #0004;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
background: #ccc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* draggable pane dividers */
|
||||
@media only screen and (max-width: 820px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
.splitter {
|
||||
display: none;
|
||||
}
|
||||
.col {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
.pane-left,
|
||||
.pane-right {
|
||||
min-width: 0;
|
||||
}
|
||||
/* draggable pane dividers-end */
|
||||
|
||||
|
||||
#file-margin {
|
||||
width: calc(100% - 32px);
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: .2em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header .margin {
|
||||
margin: 0.2em 16px;
|
||||
}
|
||||
|
||||
#button-container {
|
||||
flex-direction: row;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
width: calc(100% - 16px);
|
||||
}
|
||||
|
||||
#button-container .margin {
|
||||
width: calc(50% - 16px);
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>ManifoldCAD</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description"
|
||||
content="Fast, reliable, parametric solid modeling web app. Programmatic 3D design with JavaScript, inspired by and improving upon OpenSCAD & JSCAD. Demonstrates a new GPU-parallel, open-source geometry kernel: Manifold.">
|
||||
<meta name="theme-color" content="#A288B3">
|
||||
<meta property="og:title" content="ManifoldCAD">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://manifoldcad.org/" />
|
||||
<meta property="og:image" content="https://elalish.github.io/manifold/samples/models/mengerSpongeSquare.png">
|
||||
<meta property="og:image:width" content="850">
|
||||
<meta property="og:image:height" content="850">
|
||||
<link rel="shortcut icon" type="image/png" href="./icons/ManifoldIcon.png">
|
||||
<link rel="manifest" href="./manifest.json">
|
||||
<link type="text/css" href="editor.css" rel="stylesheet">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebApplication",
|
||||
"name": "ManifoldCAD",
|
||||
"applicationCategory": "DesignApplication",
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
self.ModelViewerElement = self.ModelViewerElement || {};
|
||||
self.ModelViewerElement.modelCacheSize = 0;
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="container header">
|
||||
<p style="flex: 2; margin-left: 16px;"><a target="_blank" href="https://github.com/elalish/manifold">Manifold</a>CAD
|
||||
<span class="top-edit blue">
|
||||
<button type="button" id="undo" class="top-icon undo squish" title="Undo"></button>
|
||||
<button type="button" id="redo" class="top-icon redo squish" title="Redo"></button>
|
||||
<button type="button" id="format" class="top-icon star squish" title="Format Code"></button>
|
||||
<button type="button" id="share" class="top-icon share squish" title="Copy Shareable Link"></button>
|
||||
<a target="_blank" href="./docs/jsuser/" class="top-icon docs squish" title="Documentation"></a>
|
||||
<button type="button" id="edgesToggle" class="top-icon edges" title="Toggle Edges" aria-pressed="false"></button>
|
||||
</span>
|
||||
<span id="type-indicator" style="font-size: 14px; margin-left: 12px; font-weight: normal; opacity: 0.8;"></span>
|
||||
</p>
|
||||
<div class="margin" id="file-margin">
|
||||
<button type="button" id="file" class="blue item"><b class="uparrow"></b> <span id="current">Intro</span><button
|
||||
id="current-edit" class="icon edit" aria-hidden="true"></button></button>
|
||||
<div class="dropdown" id="fileDropdown">
|
||||
<button type="button" class="green item" id="new">New</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" id="button-container">
|
||||
<button type="button" id="compile" class="margin green" disabled><span>Run</span></button>
|
||||
<div id="save" class="margin" style="position: relative; ">
|
||||
<div class="item">
|
||||
<button type="button" id="glb" class="blue item">Save GLB</button>
|
||||
<button type="button" class="icon"><b class="uparrow" style="right: 2px;"></b></button>
|
||||
</div>
|
||||
<div class="dropdown" id="saveDropdown">
|
||||
<div class="item">
|
||||
<button type="button" id="threemf" class="blue item">Save 3MF</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Draggable pane dividers-start -->
|
||||
<div id="workbench" class="container workbench" style="flex: 2;">
|
||||
<div id="editor" class="col pane-left"></div>
|
||||
<div id="split-x" class="splitter splitter-x" aria-hidden="true"></div>
|
||||
<div id="rightPane" class="col container pane-right" style="flex-direction: column;">
|
||||
<model-viewer id="viewer" camera-controls min-camera-orbit="auto 0deg auto" max-camera-orbit="auto 180deg auto"
|
||||
min-field-of-view="1deg" 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="split-y" class="splitter splitter-y" aria-hidden="true"></div>
|
||||
<!-- Draggable pane dividers-end -->
|
||||
<div id="console"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="module" src="editor-examples.js"></script>
|
||||
<script type="module" src="editor.js"></script>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "manifold-editor",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc --noEmit && vite build",
|
||||
"build:watch": "vite build --watch",
|
||||
"clean": "rm -fr ./dist",
|
||||
"preview": "vite preview",
|
||||
"public": "cp -rv dist/* ../../public/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"esbuild-wasm": "^0.27.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
|
||||
"@google/model-viewer": "^4.2.0",
|
||||
"manifold-3d": "file:../..",
|
||||
"monaco-editor": "^0.53.0",
|
||||
"monaco-editor-auto-typings": "^0.4.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.0.9",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "7.3.1",
|
||||
"vite-plugin-static-copy": "^3.1.5",
|
||||
"vite-bundle-analyzer": "1.3.8"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 539 B After Width: | Height: | Size: 539 B |
|
After Width: | Height: | Size: 330 B |
|
After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 704 KiB After Width: | Height: | Size: 704 KiB |
|
Before Width: | Height: | Size: 952 KiB After Width: | Height: | Size: 952 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 11 KiB |