Compare commits
732 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
911c67b2d8 | ||
![]() |
acc25ecd57 | ||
![]() |
ca2aad7fea | ||
![]() |
5744d1d599 | ||
![]() |
e2e74762f4 | ||
![]() |
335c217114 | ||
![]() |
60f85f5a39 | ||
![]() |
7e2b1b1d8d | ||
![]() |
15f349049c | ||
![]() |
27b59f601e | ||
![]() |
dbf66639aa | ||
![]() |
f2f37aeed8 | ||
![]() |
9ab37c2ebf | ||
![]() |
61fe8badf2 | ||
![]() |
8e27a5e34b | ||
![]() |
91574254fe | ||
![]() |
dea573e156 | ||
![]() |
3bcc376224 | ||
![]() |
6bc2ed9771 | ||
![]() |
3c36d8f887 | ||
![]() |
312184505c | ||
![]() |
41794dbadb | ||
![]() |
e6ed36f739 | ||
![]() |
f29d9d7a34 | ||
![]() |
1871efa02b | ||
![]() |
2e15f60ef6 | ||
![]() |
d77f617dc4 | ||
![]() |
6cf5409cd7 | ||
![]() |
cd26256675 | ||
![]() |
97e71856b3 | ||
![]() |
ced42440e7 | ||
![]() |
80c7296316 | ||
![]() |
4260be2445 | ||
![]() |
baf9dc0aae | ||
![]() |
4465bc0270 | ||
![]() |
51b88280e5 | ||
![]() |
279424290b | ||
![]() |
6e88eaef69 | ||
![]() |
d4b052d34d | ||
![]() |
3fc85b7934 | ||
![]() |
27a5f6f9bd | ||
![]() |
67a916d19c | ||
![]() |
ff0af6fdba | ||
![]() |
495a7ac166 | ||
![]() |
bb2cc4aa56 | ||
![]() |
669e6f0dcb | ||
![]() |
119b17ac75 | ||
![]() |
86f229c5e6 | ||
![]() |
db75e06ecc | ||
![]() |
068191bf28 | ||
![]() |
8749a175a6 | ||
![]() |
e388d9fd16 | ||
![]() |
5d9356a90a | ||
![]() |
4aae22929a | ||
![]() |
3e668079b5 | ||
![]() |
8d0e226722 | ||
![]() |
572fbc7463 | ||
![]() |
04e86d9b36 | ||
![]() |
67146f6ab2 | ||
![]() |
8370382a33 | ||
![]() |
5b2ad9b5f1 | ||
![]() |
6931979b8e | ||
![]() |
5c34b3f6ef | ||
![]() |
12a1a35784 | ||
![]() |
fba88859e1 | ||
![]() |
ec07516580 | ||
![]() |
29e43e67ea | ||
![]() |
cde86a0626 | ||
![]() |
6b67cfe63f | ||
![]() |
6de5133609 | ||
![]() |
022d012a44 | ||
![]() |
43db2201fd | ||
![]() |
c00ab25740 | ||
![]() |
855de072be | ||
![]() |
55ae10b5b8 | ||
![]() |
96476ca2e5 | ||
![]() |
fb667fb8bb | ||
![]() |
33c9ffb5ca | ||
![]() |
c12672be66 | ||
![]() |
aeebc3c395 | ||
![]() |
0eb33decd1 | ||
![]() |
984f74d271 | ||
![]() |
4fda1e4361 | ||
![]() |
da7c74af2e | ||
![]() |
00dda08917 | ||
![]() |
27ac3c7b5f | ||
![]() |
529e1bfd63 | ||
![]() |
4f04406121 | ||
![]() |
387c5c5144 | ||
![]() |
934f33f4c0 | ||
![]() |
c385740087 | ||
![]() |
9f697af138 | ||
![]() |
6b8f937894 | ||
![]() |
d8932772ef | ||
![]() |
02ab358bd9 | ||
![]() |
daf3c7b070 | ||
![]() |
f1d4c4a50c | ||
![]() |
2eb934243b | ||
![]() |
e127b28a2d | ||
![]() |
9253e5f229 | ||
![]() |
6bb73a162c | ||
![]() |
7dbbd75969 | ||
![]() |
25631d4fb2 | ||
![]() |
d99a133982 | ||
![]() |
9148d0cb91 | ||
![]() |
10251c6484 | ||
![]() |
727c0a97dc | ||
![]() |
5763972ed8 | ||
![]() |
626ffeab1f | ||
![]() |
d6e2ac8328 | ||
![]() |
11adaf20b7 | ||
![]() |
15571ef58e | ||
![]() |
3dd2fc0093 | ||
![]() |
435682729e | ||
![]() |
374d1b5e7f | ||
![]() |
6fcf1bbe79 | ||
![]() |
4a0b4fd8d3 | ||
![]() |
679a1f0ded | ||
![]() |
d1ddc6ee07 | ||
![]() |
6989a3d068 | ||
![]() |
3858cbe249 | ||
![]() |
b975380493 | ||
![]() |
47244c5e89 | ||
![]() |
dbc567ed89 | ||
![]() |
9301dec98d | ||
![]() |
7681f6df74 | ||
![]() |
d37f53e190 | ||
![]() |
7758844f96 | ||
![]() |
f5485cbf24 | ||
![]() |
78d141cf9c | ||
![]() |
3d6d873906 | ||
![]() |
cc07058e48 | ||
![]() |
8f93136d8a | ||
![]() |
41365c5f9f | ||
![]() |
aaab8a09d4 | ||
![]() |
43629549c1 | ||
![]() |
37defcbc33 | ||
![]() |
01ae452507 | ||
![]() |
61f248899a | ||
![]() |
8dc60f61c1 | ||
![]() |
d3f2ac7513 | ||
![]() |
369b0a378c | ||
![]() |
683ac78ca2 | ||
![]() |
0ae0343fc7 | ||
![]() |
9a0f2c3601 | ||
![]() |
73844f7202 | ||
![]() |
c8ff17f4a2 | ||
![]() |
9db50ed077 | ||
![]() |
156fe73bee | ||
![]() |
92773a5770 | ||
![]() |
52557ee979 | ||
![]() |
6d2c2aecff | ||
![]() |
f8824e1fb2 | ||
![]() |
e56630e71d | ||
![]() |
c97ba6b928 | ||
![]() |
ea0a1b743a | ||
![]() |
802d092b13 | ||
![]() |
e681ba3218 | ||
![]() |
3c451586d7 | ||
![]() |
c7277521f3 | ||
![]() |
489ae66e96 | ||
![]() |
fdd08cbead | ||
![]() |
3b241392d4 | ||
![]() |
1ec36fc657 | ||
![]() |
9673225648 | ||
![]() |
a0d7f1dadb | ||
![]() |
13d4a1bb6a | ||
![]() |
7b9d730a23 | ||
![]() |
f5e955a015 | ||
![]() |
afecbccb0f | ||
![]() |
d151eb56fc | ||
![]() |
b846401c73 | ||
![]() |
74cb1f589c | ||
![]() |
23feb4cf8f | ||
![]() |
7bd6adefd8 | ||
![]() |
b8bec35a0c | ||
![]() |
0e72c606ab | ||
![]() |
505f503cc3 | ||
![]() |
600c39db91 | ||
![]() |
cdd6174cfa | ||
![]() |
071a7acc9d | ||
![]() |
592bea8109 | ||
![]() |
f619a4b85e | ||
![]() |
345c982b3b | ||
![]() |
90987d3ee4 | ||
![]() |
3a585ea7c2 | ||
![]() |
b2092eaea9 | ||
![]() |
8e329ca2b3 | ||
![]() |
b37aba00e2 | ||
![]() |
47288e9a4c | ||
![]() |
186911a51a | ||
![]() |
d2c250324b | ||
![]() |
ea52fcbce1 | ||
![]() |
62f5f690c1 | ||
![]() |
2ccf5954d4 | ||
![]() |
6658b1fa2b | ||
![]() |
5462fc6b3c | ||
![]() |
90f10ccfa6 | ||
![]() |
b5f5e05336 | ||
![]() |
b0363802d8 | ||
![]() |
291e16e549 | ||
![]() |
363f5c1ab8 | ||
![]() |
af226b2437 | ||
![]() |
9f97e9aad4 | ||
![]() |
a5c7fc6ad9 | ||
![]() |
df83ee4c8f | ||
![]() |
1108a6f37d | ||
![]() |
e80a3a0a71 | ||
![]() |
ffa104602c | ||
![]() |
f4e85127d6 | ||
![]() |
aa958504af | ||
![]() |
b10e621fce | ||
![]() |
456c987218 | ||
![]() |
e9725bd350 | ||
![]() |
9febc572a3 | ||
![]() |
93da88369f | ||
![]() |
66ed417d45 | ||
![]() |
476860acbb | ||
![]() |
2371068d01 | ||
![]() |
0e66eafe5a | ||
![]() |
3ff236c494 | ||
![]() |
9bcba4b92c | ||
![]() |
a8e723381c | ||
![]() |
f5a37ae2fd | ||
![]() |
4537444db5 | ||
![]() |
21a4305ee1 | ||
![]() |
2f64f18095 | ||
![]() |
2f4c6a6b0e | ||
![]() |
da2d035200 | ||
![]() |
c43a1988ae | ||
![]() |
fbd89a7e30 | ||
![]() |
819c4c5742 | ||
![]() |
7787923d05 | ||
![]() |
d12bf047b4 | ||
![]() |
fb87431ba5 | ||
![]() |
58db06d845 | ||
![]() |
fd54e5ac27 | ||
![]() |
06a188cc62 | ||
![]() |
cd5b5b2d7c | ||
![]() |
e7edc39b34 | ||
![]() |
ffd9c6241a | ||
![]() |
b9d375652c | ||
![]() |
a1e18ae4a6 | ||
![]() |
991909d794 | ||
![]() |
f998293760 | ||
![]() |
4c30c09792 | ||
![]() |
efd358d734 | ||
![]() |
6e860fb148 | ||
![]() |
8ce2922902 | ||
![]() |
e2e9167210 | ||
![]() |
0bf6167e16 | ||
![]() |
e381c70842 | ||
![]() |
35a20ae260 | ||
![]() |
065a0a8b00 | ||
![]() |
b2ab15c2e1 | ||
![]() |
cf9c3310fc | ||
![]() |
8bf55b3c62 | ||
![]() |
fbe2705ebe | ||
![]() |
415a6d36ff | ||
![]() |
068ed2816a | ||
![]() |
f8b6c33290 | ||
![]() |
e462387fca | ||
![]() |
4b4944264b | ||
![]() |
ba1cd83d9e | ||
![]() |
f2633e4a57 | ||
![]() |
8e7d2eaa84 | ||
![]() |
ff23a4a471 | ||
![]() |
9a1ceaa5c8 | ||
![]() |
fd9ee94437 | ||
![]() |
4f49a8a9d4 | ||
![]() |
a2a50927e9 | ||
![]() |
42d3ec9917 | ||
![]() |
2fdbabc13c | ||
![]() |
5e63d8301e | ||
![]() |
977a0b8e6d | ||
![]() |
65e2cccde0 | ||
![]() |
6963c7e25b | ||
![]() |
8960ee365a | ||
![]() |
8af3a933cf | ||
![]() |
7f0083aa1d | ||
![]() |
14cf0e09df | ||
![]() |
c83421a2b5 | ||
![]() |
d0e32849b2 | ||
![]() |
50b2b8adfd | ||
![]() |
613aa8c579 | ||
![]() |
803665404e | ||
![]() |
6d5d88f01e | ||
![]() |
6846010416 | ||
![]() |
0c90cd799d | ||
![]() |
bcd8c3e790 | ||
![]() |
6607a48357 | ||
![]() |
7265121b24 | ||
![]() |
216091a366 | ||
![]() |
7f411d1593 | ||
![]() |
e7c8c1c8f2 | ||
![]() |
bd2da7fe3f | ||
![]() |
89da072427 | ||
![]() |
b0d37c1e78 | ||
![]() |
a4a121694c | ||
![]() |
6e17780e01 | ||
![]() |
ad2371cfae | ||
![]() |
8ccc9fe56c | ||
![]() |
8747745a14 | ||
![]() |
33e292a6bc | ||
![]() |
cbf84a9d2b | ||
![]() |
25575b14c7 | ||
![]() |
0b999f4165 | ||
![]() |
737ff51893 | ||
![]() |
5a2eb9fb50 | ||
![]() |
d0a0a0f8cf | ||
![]() |
32a2a4dbd9 | ||
![]() |
da1fc3fd70 | ||
![]() |
131acc5e56 | ||
![]() |
21179c2e30 | ||
![]() |
a1589401b6 | ||
![]() |
ced5b78420 | ||
![]() |
a4e487d298 | ||
![]() |
5c754bd994 | ||
![]() |
51f4f27b2b | ||
![]() |
3a34f63415 | ||
![]() |
85cd44df3c | ||
![]() |
e0283b2d2e | ||
![]() |
fb62e0494b | ||
![]() |
251948bdbd | ||
![]() |
a98cdeeb16 | ||
![]() |
0066f93fb4 | ||
![]() |
3c887d30e3 | ||
![]() |
1dba594949 | ||
![]() |
afafa5ec2e | ||
![]() |
44223ea332 | ||
![]() |
a425395fe3 | ||
![]() |
f8f5095b3d | ||
![]() |
de6be52293 | ||
![]() |
215b8e4537 | ||
![]() |
122920d3ee | ||
![]() |
14cbd0bbee | ||
![]() |
66746d151f | ||
![]() |
682bfa2732 | ||
![]() |
a4e7dea9e3 | ||
![]() |
faa84c61cf | ||
![]() |
73d82a6347 | ||
![]() |
5791310bb1 | ||
![]() |
8aab0160d3 | ||
![]() |
3cd9c28ebc | ||
![]() |
1249f8496e | ||
![]() |
4128a5d8d4 | ||
![]() |
f33ddc94fb | ||
![]() |
3103728c15 | ||
![]() |
7bda30aca4 | ||
![]() |
274233fe08 | ||
![]() |
23ff9fa8d1 | ||
![]() |
8372154384 | ||
![]() |
68c4d6f704 | ||
![]() |
e2916e3e4a | ||
![]() |
20d87d93c5 | ||
![]() |
91e18eed73 | ||
![]() |
ddc0dd7194 | ||
![]() |
9c08398f51 | ||
![]() |
def73ec118 | ||
![]() |
4c318f09c4 | ||
![]() |
bb0eef2b96 | ||
![]() |
457ff78849 | ||
![]() |
bbca4cc224 | ||
![]() |
ad4a204edf | ||
![]() |
4415f5bb91 | ||
![]() |
1b52c46b81 | ||
![]() |
ab418b827e | ||
![]() |
193477e2da | ||
![]() |
d37d77a257 | ||
![]() |
63398ef3ba | ||
![]() |
45514849e2 | ||
![]() |
b55e096fef | ||
![]() |
432e7680a4 | ||
![]() |
4b0dc5819b | ||
![]() |
fbc5bfc27f | ||
![]() |
69b8a3b3b3 | ||
![]() |
56e2d451f4 | ||
![]() |
fc79642788 | ||
![]() |
645c2d90ac | ||
![]() |
a525f03371 | ||
![]() |
fa546af28f | ||
![]() |
4c01461316 | ||
![]() |
e36ee32def | ||
![]() |
a6b6d98a94 | ||
![]() |
f4c01f670c | ||
![]() |
b2cdbe8c8d | ||
![]() |
1e2f199633 | ||
![]() |
0d7aa0a1a3 | ||
![]() |
3bdaa53725 | ||
![]() |
ae60187322 | ||
![]() |
11919bf0c1 | ||
![]() |
63abcc379d | ||
![]() |
7b8e8b0b41 | ||
![]() |
e129755d66 | ||
![]() |
005ff7e31b | ||
![]() |
ab8fdcb273 | ||
![]() |
4a8675c120 | ||
![]() |
8f515f3b90 | ||
![]() |
e969bc94ad | ||
![]() |
ee30fa2b0d | ||
![]() |
12c9858d06 | ||
![]() |
a44a4665a8 | ||
![]() |
febe0f5282 | ||
![]() |
d05e9a938b | ||
![]() |
7da5cfbaae | ||
![]() |
a21a327a97 | ||
![]() |
70d84b30e8 | ||
![]() |
a8465cbc8a | ||
![]() |
affc88f342 | ||
![]() |
e1f614101f | ||
![]() |
a75bf6e216 | ||
![]() |
f5d8b1dc6b | ||
![]() |
ba4cb28251 | ||
![]() |
7f79461d5d | ||
![]() |
e61bac2797 | ||
![]() |
52af725606 | ||
![]() |
2fed0587ea | ||
![]() |
c6747438e0 | ||
![]() |
ab710f7ed3 | ||
![]() |
ddbb041995 | ||
![]() |
c2373e7b3a | ||
![]() |
1c51205a11 | ||
![]() |
04a79b4308 | ||
![]() |
dabd57847e | ||
![]() |
d2b21666e1 | ||
![]() |
2edc61d072 | ||
![]() |
c17f1160dc | ||
![]() |
1142f85ff5 | ||
![]() |
6cb6a2cf27 | ||
![]() |
a61544ea9c | ||
![]() |
accd73fe02 | ||
![]() |
efb9fa3d69 | ||
![]() |
b3df595769 | ||
![]() |
a4a353f01b | ||
![]() |
3b7e7289c8 | ||
![]() |
92f5bd450c | ||
![]() |
07f3ab95e4 | ||
![]() |
1098e598ae | ||
![]() |
6b123f2d34 | ||
![]() |
a7f4d0fffc | ||
![]() |
e74ccb3010 | ||
![]() |
bd6c4c0cbd | ||
![]() |
0128b86796 | ||
![]() |
bcb484e941 | ||
![]() |
7a01c840d3 | ||
![]() |
bd51a9edac | ||
![]() |
23dc36da9b | ||
![]() |
d17771064a | ||
![]() |
0f304b4c64 | ||
![]() |
e7057418df | ||
![]() |
24fc65a71c | ||
![]() |
d1a2eb6d18 | ||
![]() |
d55300232f | ||
![]() |
1bf7777815 | ||
![]() |
7e6a11c958 | ||
![]() |
7c60be8203 | ||
![]() |
16ea824456 | ||
![]() |
77d8291216 | ||
![]() |
2d25bdb51f | ||
![]() |
98e0a30a98 | ||
![]() |
83f5b60228 | ||
![]() |
09f59ddbc0 | ||
![]() |
11565e081d | ||
![]() |
27b403faf5 | ||
![]() |
f88cb1195b | ||
![]() |
d88149e705 | ||
![]() |
94cba11f2a | ||
![]() |
a6f7200092 | ||
![]() |
55e3162a05 | ||
![]() |
29296447a9 | ||
![]() |
d72b250a64 | ||
![]() |
e19a2f4f35 | ||
![]() |
1e0fcf1e6c | ||
![]() |
46b67d5457 | ||
![]() |
e17a24814b | ||
![]() |
73f28b9731 | ||
![]() |
614902ebdd | ||
![]() |
3d334d153d | ||
![]() |
6f67ec2d48 | ||
![]() |
fb0aee91ea | ||
![]() |
63d48e7840 | ||
![]() |
c061fb9416 | ||
![]() |
762af7d2ad | ||
![]() |
7c2200584a | ||
![]() |
b76d7026e0 | ||
![]() |
4376f178fc | ||
![]() |
e87e787d3f | ||
![]() |
6e56b00b9a | ||
![]() |
bda2835e9f | ||
![]() |
e99eedd7a3 | ||
![]() |
b054b9682a | ||
![]() |
a0576e2a50 | ||
![]() |
96344c85a6 | ||
![]() |
1f0649d1bb | ||
![]() |
1814cf3c0f | ||
![]() |
1170a91875 | ||
![]() |
171f208cfb | ||
![]() |
c9a2092b9c | ||
![]() |
a886746e71 | ||
![]() |
df0a1d64e4 | ||
![]() |
f87152e8c0 | ||
![]() |
e377eb8851 | ||
![]() |
3fdfca10f6 | ||
![]() |
c469af6600 | ||
![]() |
20a041e0ef | ||
![]() |
a71d5894aa | ||
![]() |
b3fa8dca37 | ||
![]() |
27d0dedbd1 | ||
![]() |
c9648805ea | ||
![]() |
df87ac6e6f | ||
![]() |
d4ecf155f6 | ||
![]() |
bdd02ac3a8 | ||
![]() |
cd6d891100 | ||
![]() |
923374b305 | ||
![]() |
5c15cbf5f6 | ||
![]() |
a1a624da12 | ||
![]() |
575146b975 | ||
![]() |
7c2ec8f80e | ||
![]() |
f20a044837 | ||
![]() |
cf38bdfebd | ||
![]() |
dd5feb5724 | ||
![]() |
29ad1acdfe | ||
![]() |
d43bd93060 | ||
![]() |
57fb3bf3dc | ||
![]() |
784f3e5548 | ||
![]() |
e5294eef9d | ||
![]() |
f82767ae79 | ||
![]() |
34a5d87011 | ||
![]() |
fc68804f65 | ||
![]() |
89eb208660 | ||
![]() |
139dd80b48 | ||
![]() |
1e2a899ba2 | ||
![]() |
8c83a4a212 | ||
![]() |
062344e40d | ||
![]() |
f3b232238c | ||
![]() |
293eedc85e | ||
![]() |
b28fa34e4a | ||
![]() |
2f734d9cfa | ||
![]() |
76d582720a | ||
![]() |
fd0b7fbc29 | ||
![]() |
b7409b8ad6 | ||
![]() |
3af4e1c5b0 | ||
![]() |
46ab541444 | ||
![]() |
2b388e7da4 | ||
![]() |
804761da88 | ||
![]() |
c011444045 | ||
![]() |
55cde18c5a | ||
![]() |
30d9bb0479 | ||
![]() |
ef5db2132e | ||
![]() |
5e5ef3be3e | ||
![]() |
28a6e04f33 | ||
![]() |
54d8957bfe | ||
![]() |
86315b2b1f | ||
![]() |
750842610c | ||
![]() |
259d8e0d38 | ||
![]() |
310fa9a817 | ||
![]() |
1160b5d335 | ||
![]() |
6dced8052b | ||
![]() |
9c4d1cb9b0 | ||
![]() |
c5082f73a4 | ||
![]() |
84a6511cfc | ||
![]() |
158c66b1b4 | ||
![]() |
818436c57d | ||
![]() |
869404a8ec | ||
![]() |
b8e8da0c0b | ||
![]() |
11f29b1231 | ||
![]() |
86f0439521 | ||
![]() |
9d2a035a71 | ||
![]() |
31fd64af0a | ||
![]() |
ba10a75a7d | ||
![]() |
97a9b4743e | ||
![]() |
5c9c32cfc7 | ||
![]() |
32383d22bf | ||
![]() |
3ffbac38c6 | ||
![]() |
c84a3b6de3 | ||
![]() |
97fa6355cc | ||
![]() |
45f056c852 | ||
![]() |
02c30e6f87 | ||
![]() |
f76f76ff60 | ||
![]() |
b23336b589 | ||
![]() |
8c2e94a01a | ||
![]() |
e40929afca | ||
![]() |
4b02bf1e81 | ||
![]() |
3c917ebac9 | ||
![]() |
c6194d439c | ||
![]() |
ef3ee55d42 | ||
![]() |
0933bbf687 | ||
![]() |
09c072fdfd | ||
![]() |
cdc384e44d | ||
![]() |
7c3c20161b | ||
![]() |
922a6260d7 | ||
![]() |
28166e6200 | ||
![]() |
063dfc4f98 | ||
![]() |
1b6cd0d632 | ||
![]() |
4d5a92e9b2 | ||
![]() |
fdc39edfd8 | ||
![]() |
06be0d1157 | ||
![]() |
1422d3abd2 | ||
![]() |
4260a91a99 | ||
![]() |
68f85587cb | ||
![]() |
0159a87a8a | ||
![]() |
5e7c7fce7e | ||
![]() |
75a3936b64 | ||
![]() |
1b69032d99 | ||
![]() |
f62e95d7b6 | ||
![]() |
69c509064c | ||
![]() |
f89020ad26 | ||
![]() |
46db5378dc | ||
![]() |
71b7ad7f99 | ||
![]() |
22d43d5b83 | ||
![]() |
d585f0d1ff | ||
![]() |
636b20bfa9 | ||
![]() |
d0c0a7ae5a | ||
![]() |
bbe4999033 | ||
![]() |
4b86fb89f8 | ||
![]() |
d76d59ea64 | ||
![]() |
95ab80d0ee | ||
![]() |
5d7a5bf3a7 | ||
![]() |
0807c2fdfe | ||
![]() |
d3dbf23725 | ||
![]() |
2ce12155bf | ||
![]() |
88e4163970 | ||
![]() |
e27c6b56c3 | ||
![]() |
622c9efadb | ||
![]() |
2c39f259db | ||
![]() |
b68631ee56 | ||
![]() |
5db5f1e152 | ||
![]() |
c5364fe7a8 | ||
![]() |
7c7ac17e4b | ||
![]() |
3a42a8d8cb | ||
![]() |
62af550c0b | ||
![]() |
024a26bf57 | ||
![]() |
14690e6e42 | ||
![]() |
13afcb310e | ||
![]() |
80046672ef | ||
![]() |
84f2a59993 | ||
![]() |
ffd2df9494 | ||
![]() |
1bbf61b8a2 | ||
![]() |
604c4bbea3 | ||
![]() |
1cd2e2be2f | ||
![]() |
55e2d6b06d | ||
![]() |
5894efcfe5 | ||
![]() |
ba8202d54a | ||
![]() |
403d3c04d2 | ||
![]() |
044a4ea8fc | ||
![]() |
9c9a0d8c28 | ||
![]() |
3b587d022a | ||
![]() |
48880f6fe9 | ||
![]() |
6c4eacf237 | ||
![]() |
8401dbd84a | ||
![]() |
f2cbdd36a9 | ||
![]() |
a9590dc271 | ||
![]() |
790b241f56 | ||
![]() |
40552a0066 | ||
![]() |
8d0c2f77ec | ||
![]() |
f5e4e02bac | ||
![]() |
f5de3faedb | ||
![]() |
9b68bf8f7b | ||
![]() |
ae37fb783e | ||
![]() |
413ab5f578 | ||
![]() |
2622684ed5 | ||
![]() |
3b4897c833 | ||
![]() |
afdf3145b2 | ||
![]() |
6cfa884406 | ||
![]() |
252f4128b7 | ||
![]() |
023f404475 | ||
![]() |
ce77197dac | ||
![]() |
396b1c63a4 | ||
![]() |
1bfba0df5f | ||
![]() |
0f334cc040 | ||
![]() |
12a29ddce0 | ||
![]() |
8996833989 | ||
![]() |
e587d0ebee | ||
![]() |
f5297093c0 | ||
![]() |
01213bcafb | ||
![]() |
f4916f9ee4 | ||
![]() |
1bc73c4a75 | ||
![]() |
f9f321ca84 | ||
![]() |
3cc520e98c | ||
![]() |
fd9dc19a34 | ||
![]() |
6c68294249 | ||
![]() |
174ed76ef9 | ||
![]() |
4dffdb20e2 | ||
![]() |
bb3e5e9bb1 | ||
![]() |
0a24cf40f0 | ||
![]() |
5429295492 | ||
![]() |
442197c907 | ||
![]() |
3a46c97fd0 | ||
![]() |
511c513035 | ||
![]() |
f09ae15586 | ||
![]() |
c60e3dd34e | ||
![]() |
7ca137f5fe | ||
![]() |
873811d865 | ||
![]() |
c6203678e1 | ||
![]() |
0afb5618ce | ||
![]() |
8bc322eb47 | ||
![]() |
a5176f4545 | ||
![]() |
dd168ad22c | ||
![]() |
505e3d869c | ||
![]() |
e7f3a3c0db | ||
![]() |
fb766d1cf9 | ||
![]() |
3cfe6b6da1 | ||
![]() |
2499a02f2f | ||
![]() |
4a2711476c | ||
![]() |
9da2a3a6c7 | ||
![]() |
37063840db | ||
![]() |
66758b9595 | ||
![]() |
07b128b877 | ||
![]() |
30ca4ec8ac | ||
![]() |
16179f34cd | ||
![]() |
42a46e83fa | ||
![]() |
70b6bad551 | ||
![]() |
718d411699 | ||
![]() |
93145387f4 | ||
![]() |
44a3981fbf | ||
![]() |
66315d5eea | ||
![]() |
bcb6c7b19b | ||
![]() |
5cca524c62 | ||
![]() |
a2a78b923f | ||
![]() |
74b65e14f9 | ||
![]() |
398e09fe9e | ||
![]() |
0f7a34ccca | ||
![]() |
e256956d12 | ||
![]() |
66f46b7b67 | ||
![]() |
7715fd1bb8 | ||
![]() |
1b00c8c3ab | ||
![]() |
dcc963aa4b | ||
![]() |
a72575d04e | ||
![]() |
8913d11fa5 | ||
![]() |
df6125efee | ||
![]() |
02776ea535 | ||
![]() |
affbeafc6c |
10
.gitattributes
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* text=auto
|
||||||
|
*.cpp text
|
||||||
|
*.h text
|
||||||
|
*.txt text
|
||||||
|
*.gz binary
|
||||||
|
*.ico binary
|
||||||
|
*.jpg binary
|
||||||
|
*.lib binary
|
||||||
|
*.png binary
|
||||||
|
*.slvs binary
|
18
.github/ISSUE_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
### System information
|
||||||
|
|
||||||
|
SolveSpace version: (e.g. 3.0~3dd2fc00; go to Help → About...)
|
||||||
|
|
||||||
|
Operating system: (e.g. Debian testing)
|
||||||
|
|
||||||
|
### Expected behavior
|
||||||
|
|
||||||
|
What should have happened?
|
||||||
|
|
||||||
|
### Actual behavior
|
||||||
|
|
||||||
|
What actually happened?
|
||||||
|
|
||||||
|
### Additional information
|
||||||
|
|
||||||
|
For bugs, please attach a savefile that shows the problematic behavior.
|
||||||
|
You can attach `.slvs` files by archiving them into a `.zip` first.
|
15
.gitignore
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/CMakeCache.txt
|
||||||
|
/build*/
|
||||||
|
/test/**/*.diff.*
|
||||||
|
/test/**/*.curr.*
|
||||||
|
*.trace
|
||||||
|
/debian/tmp/
|
||||||
|
/debian/*.log
|
||||||
|
/debian/*.substvars
|
||||||
|
/debian/*.debhelper
|
||||||
|
/debian/files
|
||||||
|
/debian/solvespace/
|
||||||
|
/debian/libslvs1/
|
||||||
|
/debian/libslvs1-dev/
|
||||||
|
/obj-*/
|
||||||
|
/*.slvs
|
22
.gitmodules
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[submodule "extlib/zlib"]
|
||||||
|
path = extlib/zlib
|
||||||
|
url = https://github.com/madler/zlib
|
||||||
|
ignore = dirty
|
||||||
|
[submodule "extlib/libpng"]
|
||||||
|
path = extlib/libpng
|
||||||
|
url = https://github.com/glennrp/libpng
|
||||||
|
[submodule "extlib/freetype"]
|
||||||
|
path = extlib/freetype
|
||||||
|
url = http://git.sv.nongnu.org/r/freetype/freetype2.git
|
||||||
|
[submodule "extlib/libdxfrw"]
|
||||||
|
path = extlib/libdxfrw
|
||||||
|
url = https://github.com/solvespace/libdxfrw.git
|
||||||
|
[submodule "extlib/pixman"]
|
||||||
|
path = extlib/pixman
|
||||||
|
url = https://github.com/solvespace/pixman
|
||||||
|
[submodule "extlib/cairo"]
|
||||||
|
path = extlib/cairo
|
||||||
|
url = https://github.com/solvespace/cairo
|
||||||
|
[submodule "extlib/angle"]
|
||||||
|
path = extlib/angle
|
||||||
|
url = https://github.com/solvespace/angle
|
29
.travis.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
language: c
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
osx_image: xcode8.2
|
||||||
|
install:
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/install-debian.sh; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/install-macos.sh; fi
|
||||||
|
script:
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./.travis/build-debian.sh; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ./.travis/build-macos.sh; fi
|
||||||
|
deploy:
|
||||||
|
- provider: releases
|
||||||
|
api_key:
|
||||||
|
secure: dDlkIawHcODlW9B/20/cQCtzeoocvs0hKuNngRKXKqzXLWTRq33oq/B7+39tAixWbmv6exTpijiKrRNFiSCW5Z4iwHLwaRD4XJznxw63e/Hus/dxg2Tvqx7XFpkCz8mT1Z+gZQE5YxAngeZPpI/sZbZtF1UO3yH5eLeeokZ15p26ZskQUPoYuzrTgTzYL3XfpG3F+20rNBawH1ycsCTVD/08/n31d2m3CrKAsbW7er92ek6w4fzKr7NW8WeXjrPJETVpw5fQg1Od3pRGW8dPQaJcvKQEogMp8Mm0ETYd0qigg89/giBz7QwOgmAWQ4dH+DfZH4Ojl//127QztBolMvyDMQBykWrtJoGcij05sT6K2IJr2FHeUBO12MAEdjiVvhQj3DtTzjPiZAHHDBSLWxLKWWhlhHE4pq7g1MQhqXkaAHI2BLNzwLmaowbMT0bECf9yfz6xx18h6XPQFX44oOktraobVALFlyHqeKa8zdcUt22LF6uAL1m5dxL0tny3eXCIPE4UH/RZgua/cHV9G3cUvKQa/QnFSLRhvWVSbGB+7YsHouBJcsUOOW1gmd5442XuC7mpppccRldh+GSxUk6TBJRAx7TeQ0ybDUaoco9MUqp2twv3KreR2+8Q12PDaAhfQVNEGdF3wTm1sShImjCN4VN3eSLlBEbve1QRQXM=
|
||||||
|
skip_cleanup: true
|
||||||
|
file: build/solvespace.dmg
|
||||||
|
on:
|
||||||
|
repo: solvespace/solvespace
|
||||||
|
tags: true
|
||||||
|
condition: "$TRAVIS_OS_NAME == osx"
|
||||||
|
notifications:
|
||||||
|
irc:
|
||||||
|
channels:
|
||||||
|
- "chat.freenode.net#solvespace"
|
||||||
|
use_notice: true
|
||||||
|
skip_join: true
|
13
.travis/build-debian.sh
Executable file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/sh -xe
|
||||||
|
|
||||||
|
if echo $TRAVIS_TAG | grep ^v; then BUILD_TYPE=RelWithDebInfo; else BUILD_TYPE=Debug; fi
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
# We build without the GUI until Travis updates to an Ubuntu version with GTK 3.22.
|
||||||
|
cmake .. -DCMAKE_C_COMPILER=clang-3.9 -DCMAKE_CXX_COMPILER=clang++-3.9 \
|
||||||
|
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||||
|
-DENABLE_GUI=OFF \
|
||||||
|
-DENABLE_SANITIZERS=ON
|
||||||
|
make VERBOSE=1
|
||||||
|
make test_solvespace
|
9
.travis/build-macos.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh -xe
|
||||||
|
|
||||||
|
if echo $TRAVIS_TAG | grep ^v; then BUILD_TYPE=RelWithDebInfo; else BUILD_TYPE=Debug; fi
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.7 -DCMAKE_BUILD_TYPE=$BUILD_TYPE ..
|
||||||
|
make VERBOSE=1
|
||||||
|
make test_solvespace
|
10
.travis/install-debian.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh -xe
|
||||||
|
|
||||||
|
wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||||
|
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||||
|
sudo add-apt-repository -y 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main'
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -q -y \
|
||||||
|
cmake cmake-data libpng12-dev zlib1g-dev libjson0-dev libfontconfig1-dev \
|
||||||
|
libgtkmm-3.0-dev libpangomm-1.4-dev libcairo2-dev libgl1-mesa-dev libglu-dev \
|
||||||
|
libfreetype6-dev dpkg-dev libstdc++-5-dev clang-3.9 clang++-3.9 lcov
|
4
.travis/install-macos.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh -xe
|
||||||
|
|
||||||
|
brew update
|
||||||
|
brew install freetype cairo
|
203
CHANGELOG.md
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
3.0
|
||||||
|
---
|
||||||
|
|
||||||
|
New sketch features:
|
||||||
|
* Extrude, lathe, translate and rotate groups can now use the "assembly"
|
||||||
|
boolean operation, to increase performance.
|
||||||
|
* Translate and rotate groups can create n-dimensional arrays using
|
||||||
|
the "difference" and "assembly" boolean operations.
|
||||||
|
* A new sketch in workplane group can be created based on existing workplane.
|
||||||
|
* TTF text request has two additional points on the right side, which allow
|
||||||
|
constraining the width of text.
|
||||||
|
* Image requests can now be created, similar to TTF text requests.
|
||||||
|
This replaces the "style → background image" feature.
|
||||||
|
* Irrelevant points (e.g. arc center point) are not counted when estimating
|
||||||
|
the bounding box used to compute chord tolerance.
|
||||||
|
* When adding a constraint which has a label and is redundant with another
|
||||||
|
constraint, the constraint is added as a reference, avoiding an error.
|
||||||
|
* Datum points can be copied and pasted.
|
||||||
|
|
||||||
|
New constraint features:
|
||||||
|
* When dragging an arc or rectangle point, it will be automatically
|
||||||
|
constrained to other points with a click.
|
||||||
|
* When selecting a constraint, the requests it constraints can be selected
|
||||||
|
in the text window.
|
||||||
|
* When selecting an entity, the constraints applied to it can be selected
|
||||||
|
in the text window.
|
||||||
|
|
||||||
|
New export/import features:
|
||||||
|
* Three.js: allow configuring projection for exported model, and initially
|
||||||
|
use the current viewport projection.
|
||||||
|
* Wavefront OBJ: a material file is exported alongside the model, containing
|
||||||
|
mesh color information.
|
||||||
|
* DXF/DWG: 3D DXF files are imported as construction entities, in 3d.
|
||||||
|
|
||||||
|
New rendering features:
|
||||||
|
* The "Show/hide hidden lines" button is now a tri-state button that allows
|
||||||
|
showing all lines (on top of shaded mesh), stippling occluded lines
|
||||||
|
or not drawing them at all.
|
||||||
|
* The "Show/hide outlines" button is now independent from "Show/hide edges".
|
||||||
|
|
||||||
|
New measurement/analysis features:
|
||||||
|
* New command for measuring total length of selected entities,
|
||||||
|
"Analyze → Measure Perimeter".
|
||||||
|
* New command for measuring center of mass, with live updates as the sketch
|
||||||
|
changes, "Analyze → Center of Mass".
|
||||||
|
* When selecting a point and a line, projected distance to to current
|
||||||
|
workplane is displayed.
|
||||||
|
|
||||||
|
Other new features:
|
||||||
|
* New command-line interface, for batch exporting and more.
|
||||||
|
* New link to match the on-screen size of the sketch with its actual size,
|
||||||
|
"view → set to full scale".
|
||||||
|
* When zooming to fit, constraints are also considered.
|
||||||
|
* When clicking on an entity that shares a place with other entities,
|
||||||
|
the entity from the current group is selected.
|
||||||
|
* When dragging an entity that shares a place with other entities,
|
||||||
|
the entity from a request is selected. For example, dragging a point on
|
||||||
|
a face of an extrusion coincident with the source sketch plane will
|
||||||
|
drag the point from the source sketch.
|
||||||
|
* In expressions, numbers can contain the digit group separator, "_".
|
||||||
|
* The "=" key is bound to "Zoom In", like "+" key.
|
||||||
|
* The numpad decimal separator key is bound to "." regardless of locale.
|
||||||
|
* On Windows, full-screen mode is implemented.
|
||||||
|
|
||||||
|
Bugs fixed:
|
||||||
|
* A point in 3d constrained to any line whose length is free no longer
|
||||||
|
causes the line length to collapse.
|
||||||
|
* Curve-line constraints (in 3d), parallel constraints (in 3d), and
|
||||||
|
same orientation constraints are more robust.
|
||||||
|
* Adding some constraints (vertical, midpoint, etc) twice errors out
|
||||||
|
immediately, instead of later and in a confusing way.
|
||||||
|
* Constraining a newly placed point to a hovered entity does not cause
|
||||||
|
spurious changes in the sketch.
|
||||||
|
* Points highlighted with "Analyze → Show Degrees of Freedom" are drawn
|
||||||
|
on top of all other geometry.
|
||||||
|
|
||||||
|
2.3
|
||||||
|
---
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
* Do not crash when applying a symmetry constraint to two points.
|
||||||
|
* Fix TTF font metrics again (properly this time).
|
||||||
|
* Fix the "draw back faces in red" option.
|
||||||
|
* Fix export of wireframe as 3D DXF.
|
||||||
|
* Various minor crashes.
|
||||||
|
|
||||||
|
2.2
|
||||||
|
---
|
||||||
|
|
||||||
|
Other new features:
|
||||||
|
* OS X: support 3Dconnexion devices (SpaceMouse, SpaceNavigator, etc).
|
||||||
|
* GTK: files with uppercase extensions can be opened.
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
* Do not remove autosaves after successfully opening a file, preventing
|
||||||
|
data loss in case of two abnormal terminations in a row.
|
||||||
|
* Do not crash when changing autosave interval.
|
||||||
|
* Unbreak the "Show degrees of freedom" command.
|
||||||
|
* Three.js: correctly respond to controls when browser zoom is used.
|
||||||
|
* OS X: do not completely hide main window when defocused.
|
||||||
|
* GTK: unbreak 3Dconnexion support.
|
||||||
|
* When pasting transformed entities, multiply constraint values by scale.
|
||||||
|
* Fix TTF font metrics (restore the behavior from version 2.0).
|
||||||
|
* Forcibly show the current group once we start a drawing operation.
|
||||||
|
* DXF export: always declare layers before using them.
|
||||||
|
* Do not truncate operations on selections to first 32 selected entities.
|
||||||
|
* Translate and rotate groups inherit the "suppress solid model" setting.
|
||||||
|
* DXF: files with paths containing non-ASCII or spaces can be exported
|
||||||
|
or imported.
|
||||||
|
* Significantly improved performance when dragging an entity.
|
||||||
|
* Various crashes and minor glitches.
|
||||||
|
|
||||||
|
2.1
|
||||||
|
---
|
||||||
|
|
||||||
|
New sketch features:
|
||||||
|
* Lathe groups create circle and face entities.
|
||||||
|
* New toolbar button for creating lathe groups.
|
||||||
|
* Chord tolerance is separated into two: display chord tolerance (specified
|
||||||
|
in percents, relative to model bounding box), and export chord tolerance
|
||||||
|
(specified in millimeters as absolute value).
|
||||||
|
* Bezier spline points can be added and removed after the spline is created.
|
||||||
|
* When an unconstrained extrusion is switched between "union" and
|
||||||
|
"difference", its normal is flipped.
|
||||||
|
* Groups can be added in the middle of the stack. Note that this results
|
||||||
|
in files incompatible with version 2.0.
|
||||||
|
* Active group can be removed.
|
||||||
|
* Removing an imported group does not cause all subsequent groups to also
|
||||||
|
be removed.
|
||||||
|
* When a new group with a solid is created, the color is taken from
|
||||||
|
a previous group with a solid, if any.
|
||||||
|
* Entities in a newly active group do not become visible.
|
||||||
|
* When entities are selected, "Zoom to fit" zooms to fit only these
|
||||||
|
entities and not the entire sketch.
|
||||||
|
* Zero-length edges are reported with a "zero-length error", not
|
||||||
|
"points not all coplanar".
|
||||||
|
|
||||||
|
New constraint features:
|
||||||
|
* Height of the font used for drawing constraint labels can be changed.
|
||||||
|
* New constraint, length difference, placed with J.
|
||||||
|
(Patch by Peter Ruevski)
|
||||||
|
* Horizontal/vertical constraints are automatically added if a line segment
|
||||||
|
is close enough to being horizontal/vertical. This can be disabled by
|
||||||
|
holding Ctrl.
|
||||||
|
* Reference dimensions and angles can be placed with Shift+D and Shift+N.
|
||||||
|
* Copying and pasting entities duplicates any constraints that only involve
|
||||||
|
entities in the clipboard, as well as selected comments.
|
||||||
|
* Diameter constraints can be shown as radius.
|
||||||
|
* The "pi" identifier can be used in expressions.
|
||||||
|
* Constraint labels can be snapped to grid.
|
||||||
|
* Integer angles are displayed without trailing zeroes.
|
||||||
|
* Angle constraints have proper reference lines and arrowheads.
|
||||||
|
* Extension lines are drawn for point-line distance constraints.
|
||||||
|
|
||||||
|
New solver features:
|
||||||
|
* Sketches with redundant and unsolvable constraints are distinguished.
|
||||||
|
* New group setting, "allow redundant constraints". Note that it makes
|
||||||
|
the solver less stable.
|
||||||
|
|
||||||
|
New rendering and styling features:
|
||||||
|
* New line style parameter: stippling, based on ISO 128.
|
||||||
|
* Outlines of solids can be drawn in a particular style (by default, thick
|
||||||
|
lines) controlled by the "Show outline of solid model" button.
|
||||||
|
* Occluded edges can be drawn in a particular style (by default, stippled
|
||||||
|
with short dashes) controlled by the "Show hidden lines" button.
|
||||||
|
* Solids can be made transparent.
|
||||||
|
|
||||||
|
New export/import features:
|
||||||
|
* The old "import" command (for .slvs files) is renamed to "link".
|
||||||
|
* If a linked .slvs file is not found, first the relative path recorded
|
||||||
|
in the .slvs file is checked and then the absolute path; this is
|
||||||
|
an inversion of the previously used order. If it is still not found,
|
||||||
|
a dialog appears offering to locate it.
|
||||||
|
* DXF and DWG files can be imported, with point-coincident, horizontal and
|
||||||
|
vertical constraints automatically inferred from geometry, and distance
|
||||||
|
and angle constraints created when a dimension placed against geometry
|
||||||
|
exists.
|
||||||
|
* Triangle mesh can be exported for viewing in the browser through WebGL.
|
||||||
|
* Export dialogs remember the last file format used, and preselect it.
|
||||||
|
* Exported DXF files have exact circles, arcs and splines instead of
|
||||||
|
a piecewise linear approximation (unless hidden line removal was needed).
|
||||||
|
* Exported DXF files preserve color and line thickness.
|
||||||
|
* In exported DXF files, constraints are represented as DXF dimensions,
|
||||||
|
instead of piecewise linear geometry.
|
||||||
|
* When exporting 2d views, overlapping lines are removed.
|
||||||
|
|
||||||
|
Other new features:
|
||||||
|
* Native Linux (GTK 2 and GTK 3) and Mac OS X ports.
|
||||||
|
* Automatically save and then restore sketches if SolveSpace crashes.
|
||||||
|
(Patch by Marc Britten)
|
||||||
|
* Unicode is supported everywhere (filenames, group names, TTF text,
|
||||||
|
comments), although RTL scripts and scripts making heavy use of ligatures
|
||||||
|
are not rendered correctly.
|
||||||
|
* The vector font is grid-fitted when rendered on screen to make it easier
|
||||||
|
to read regardless of its size.
|
||||||
|
|
||||||
|
2.0
|
||||||
|
---
|
||||||
|
|
||||||
|
Initial public release.
|
304
CMakeLists.txt
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
# cmake configuration
|
||||||
|
|
||||||
|
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"In-tree builds are not supported; please perform an out-of-tree build:\n"
|
||||||
|
" rm -rf CMakeCache.txt CMakeFiles/\n"
|
||||||
|
" mkdir build && cd build && cmake ..")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
|
||||||
|
cmake_policy(VERSION 3.1.0)
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||||
|
"${CMAKE_SOURCE_DIR}/cmake/")
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||||
|
|
||||||
|
# for /MT on MSVC
|
||||||
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE
|
||||||
|
"${CMAKE_SOURCE_DIR}/cmake/c_flag_overrides.cmake")
|
||||||
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX
|
||||||
|
"${CMAKE_SOURCE_DIR}/cmake/cxx_flag_overrides.cmake")
|
||||||
|
|
||||||
|
# project
|
||||||
|
|
||||||
|
# NOTE TO PACKAGERS: The embedded git commit hash is critical for rapid bug triage when the builds
|
||||||
|
# can come from a variety of sources. If you are mirroring the sources or otherwise build when
|
||||||
|
# the .git directory is not present, please comment the following line:
|
||||||
|
include(GetGitCommitHash)
|
||||||
|
# and instead uncomment the following, adding the complete git hash of the checkout you are using:
|
||||||
|
# set(GIT_COMMIT_HASH 0000000000000000000000000000000000000000)
|
||||||
|
|
||||||
|
project(solvespace)
|
||||||
|
set(solvespace_VERSION_MAJOR 3)
|
||||||
|
set(solvespace_VERSION_MINOR 0)
|
||||||
|
string(SUBSTRING "${GIT_COMMIT_HASH}" 0 8 solvespace_GIT_HASH)
|
||||||
|
|
||||||
|
set(ENABLE_GUI ON CACHE BOOL
|
||||||
|
"Whether the graphical interface is enabled (command line interface always is)")
|
||||||
|
set(ENABLE_TESTS ON CACHE BOOL
|
||||||
|
"Whether the test suite will be built and run")
|
||||||
|
set(ENABLE_COVERAGE OFF CACHE BOOL
|
||||||
|
"Whether code coverage information will be collected")
|
||||||
|
set(ENABLE_SANITIZERS OFF CACHE BOOL
|
||||||
|
"Whether to enable Clang's AddressSanitizer and UndefinedBehaviorSanitizer")
|
||||||
|
|
||||||
|
set(OPENGL 2 CACHE STRING "OpenGL version to use (one of: 1 2)")
|
||||||
|
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
if(NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
|
||||||
|
message(FATAL_ERROR "C and C++ compilers should be supplied by the same vendor")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
|
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||||
|
# GCC 4.8/4.9 ship with broken but present <regex>. meh.
|
||||||
|
message(FATAL_ERROR "GCC 5.0+ is required")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# common compiler flags
|
||||||
|
|
||||||
|
if(MINGW)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
|
||||||
|
|
||||||
|
if(TRIPLE STREQUAL "i686-w64-mingw32")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_SANITIZERS)
|
||||||
|
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
message(FATAL_ERROR "Sanitizers are only available when using Clang/Clang++")
|
||||||
|
endif()
|
||||||
|
set(SANITIZE_FLAGS "-O1 -fno-omit-frame-pointer -fno-optimize-sibling-calls")
|
||||||
|
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fsanitize=address,integer")
|
||||||
|
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-sanitize-recover=integer")
|
||||||
|
# We assume IEEE floats, which means DIV/0 is defined; but ubsan doesn't do so by default.
|
||||||
|
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-sanitize=float-divide-by-zero")
|
||||||
|
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-sanitize=unsigned-integer-overflow")
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZE_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZE_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
|
||||||
|
message(STATUS "Using in-tree libdxfrw")
|
||||||
|
add_subdirectory(extlib/libdxfrw)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
include(FindVendoredPackage)
|
||||||
|
include(AddVendoredSubdirectory)
|
||||||
|
|
||||||
|
find_vendored_package(Freetype freetype
|
||||||
|
WITH_ZLIB OFF
|
||||||
|
WITH_BZip2 OFF
|
||||||
|
WITH_PNG OFF
|
||||||
|
WITH_HarfBuzz OFF
|
||||||
|
FREETYPE_LIBRARY freetype
|
||||||
|
FREETYPE_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/freetype/include)
|
||||||
|
|
||||||
|
find_vendored_package(ZLIB zlib
|
||||||
|
ZLIB_LIBRARY zlibstatic
|
||||||
|
ZLIB_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/zlib)
|
||||||
|
list(APPEND ZLIB_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/zlib)
|
||||||
|
|
||||||
|
find_vendored_package(PNG libpng
|
||||||
|
SKIP_INSTALL_ALL ON
|
||||||
|
PNG_LIBRARY png_static
|
||||||
|
PNG_PNG_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/libpng)
|
||||||
|
list(APPEND PNG_PNG_INCLUDE_DIR ${CMAKE_BINARY_DIR}/extlib/libpng)
|
||||||
|
|
||||||
|
message(STATUS "Using in-tree pixman")
|
||||||
|
add_vendored_subdirectory(extlib/pixman)
|
||||||
|
set(PIXMAN_FOUND YES)
|
||||||
|
set(PIXMAN_LIBRARY pixman)
|
||||||
|
set(PIXMAN_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/pixman/pixman)
|
||||||
|
list(APPEND PIXMAN_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/pixman/pixman)
|
||||||
|
|
||||||
|
message(STATUS "Using in-tree cairo")
|
||||||
|
add_vendored_subdirectory(extlib/cairo)
|
||||||
|
set(CAIRO_FOUND YES)
|
||||||
|
set(CAIRO_LIBRARIES cairo)
|
||||||
|
set(CAIRO_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/extlib/cairo/src)
|
||||||
|
list(APPEND CAIRO_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/extlib/cairo/src)
|
||||||
|
|
||||||
|
if(ENABLE_GUI)
|
||||||
|
if(OPENGL STREQUAL "2")
|
||||||
|
message(STATUS "Using in-tree ANGLE")
|
||||||
|
set(ANGLE_STATIC ON CACHE INTERNAL "")
|
||||||
|
set(ANGLE_ENABLE_D3D9 ON CACHE INTERNAL "")
|
||||||
|
set(ANGLE_ENABLE_D3D11 ON CACHE INTERNAL "")
|
||||||
|
set(ANGLE_ENABLE_OPENGL OFF CACHE INTERNAL "")
|
||||||
|
set(ANGLE_ENABLE_ESSL OFF CACHE INTERNAL "")
|
||||||
|
set(ANGLE_ENABLE_GLSL OFF CACHE INTERNAL "")
|
||||||
|
set(ANGLE_ENABLE_HLSL ON CACHE INTERNAL "")
|
||||||
|
add_vendored_subdirectory(extlib/angle)
|
||||||
|
set(OPENGL_LIBRARIES EGL GLESv2)
|
||||||
|
set(OPENGL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/angle/include)
|
||||||
|
else()
|
||||||
|
find_package(OpenGL REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(MSVC AND ${CMAKE_SIZEOF_VOID_P} EQUAL 4)
|
||||||
|
message(STATUS "Using prebuilt SpaceWare")
|
||||||
|
set(SPACEWARE_FOUND TRUE)
|
||||||
|
set(SPACEWARE_INCLUDE_DIR
|
||||||
|
"${CMAKE_SOURCE_DIR}/extlib/si")
|
||||||
|
set(SPACEWARE_LIBRARIES
|
||||||
|
"${CMAKE_SOURCE_DIR}/extlib/si/siapp.lib")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||||
|
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
find_package(Freetype REQUIRED)
|
||||||
|
|
||||||
|
find_library(CAIRO_LIBRARIES cairo REQUIRED)
|
||||||
|
find_path(CAIRO_INCLUDE_DIRS cairo.h PATH_SUFFIXES cairo)
|
||||||
|
|
||||||
|
if(ENABLE_GUI)
|
||||||
|
find_package(OpenGL REQUIRED)
|
||||||
|
find_library(APPKIT_LIBRARY AppKit REQUIRED)
|
||||||
|
endif()
|
||||||
|
else() # Linux and compatible systems
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
find_package(Backtrace)
|
||||||
|
find_package(SpaceWare)
|
||||||
|
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
find_package(Freetype REQUIRED)
|
||||||
|
pkg_check_modules(CAIRO REQUIRED cairo)
|
||||||
|
|
||||||
|
if(ENABLE_GUI)
|
||||||
|
find_package(OpenGL REQUIRED)
|
||||||
|
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
|
||||||
|
pkg_check_modules(JSONC REQUIRED json-c)
|
||||||
|
pkg_check_modules(GTKMM REQUIRED gtkmm-3.0>=3.16 pangomm-1.4 x11)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_COVERAGE)
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||||
|
find_program(GCOV gcov)
|
||||||
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES Clang)
|
||||||
|
find_program(LLVM_COV llvm-cov)
|
||||||
|
|
||||||
|
if(LLVM_COV)
|
||||||
|
set(GCOV ${CMAKE_CURRENT_BINARY_DIR}/llvm-gcov.sh)
|
||||||
|
file(WRITE ${GCOV} "#!/bin/sh -e\n${LLVM_COV} gcov $*")
|
||||||
|
execute_process(COMMAND chmod +x ${GCOV})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_program(LCOV lcov)
|
||||||
|
find_program(GENHTML genhtml)
|
||||||
|
if(NOT GCOV OR NOT LCOV OR NOT GENHTML)
|
||||||
|
message(FATAL_ERROR "gcov/llvm-cov and lcov are required for producing coverage reports")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_program(XGETTEXT xgettext)
|
||||||
|
find_program(MSGINIT msginit)
|
||||||
|
find_program(MSGMERGE msgmerge)
|
||||||
|
if(XGETTEXT AND MSGINIT AND MSGMERGE)
|
||||||
|
set(HAVE_GETTEXT TRUE)
|
||||||
|
else()
|
||||||
|
message(WARNING "Gettext not found, translations will not be updated")
|
||||||
|
set(HAVE_GETTEXT FALSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# solvespace-only compiler flags
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
add_definitions(
|
||||||
|
-D_CRT_SECURE_NO_DEPRECATE
|
||||||
|
-D_CRT_SECURE_NO_WARNINGS
|
||||||
|
-D_SCL_SECURE_NO_WARNINGS
|
||||||
|
-DWINVER=0x0601
|
||||||
|
-D_WIN32_WINNT=0x0601
|
||||||
|
-D_WIN32_IE=_WIN32_WINNT
|
||||||
|
-DISOLATION_AWARE_ENABLED
|
||||||
|
-DWIN32
|
||||||
|
-DWIN32_LEAN_AND_MEAN
|
||||||
|
-DUNICODE
|
||||||
|
-D_UNICODE
|
||||||
|
-DNOMINMAX
|
||||||
|
-D_USE_MATH_DEFINES)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
# Many versions of MSVC do not have the (C99) inline keyword, instead
|
||||||
|
# they have their own __inline; this breaks `static inline` functions.
|
||||||
|
# We do not want to care and so we fix this with a definition.
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline")
|
||||||
|
# Same for the (C99) __func__ special variable; we use it only in C++ code.
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D__func__=__FUNCTION__")
|
||||||
|
|
||||||
|
# We rely on these /we flags. They correspond to the GNU-style flags below as
|
||||||
|
# follows: /w4062=-Wswitch
|
||||||
|
set(WARNING_FLAGS "${WARNING_FLAGS} /we4062")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(WARNING_FLAGS "-Wall -Wextra -Wno-unused-parameter")
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(WARNING_FLAGS "${WARNING_FLAGS} -Wfloat-conversion")
|
||||||
|
endif()
|
||||||
|
# We rely on these -Werror flags.
|
||||||
|
set(WARNING_FLAGS "${WARNING_FLAGS} -Werror=switch")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -l0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_COVERAGE)
|
||||||
|
if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
|
||||||
|
CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||||
|
message(FATAL_ERROR "Code coverage is only available on GCC and Clang")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
message(FATAL_ERROR "Code coverage produces reliable results only on Debug builds")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# With -fexceptions, every call becomes a branch. While technically accurate,
|
||||||
|
# this is not useful for us.
|
||||||
|
set(COVERAGE_FLAGS -fno-exceptions --coverage)
|
||||||
|
set(COVERAGE_LIBRARY --coverage)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# components
|
||||||
|
|
||||||
|
add_subdirectory(res)
|
||||||
|
add_subdirectory(src)
|
||||||
|
add_subdirectory(exposed)
|
||||||
|
if(ENABLE_TESTS)
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||||
|
add_subdirectory(bench)
|
||||||
|
else()
|
||||||
|
message(STATUS "Benchmarking disabled in debug builds.")
|
||||||
|
endif()
|
250
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
Contributing to SolveSpace
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Contributing bug reports
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Bug reports are always welcome! When reporting a bug, please include the following:
|
||||||
|
|
||||||
|
* The version of SolveSpace (use Help → About...);
|
||||||
|
* The operating system;
|
||||||
|
* The save file that reproduces the incorrect behavior, or, if trivial or impossible,
|
||||||
|
instructions for reproducing it.
|
||||||
|
|
||||||
|
GitHub does not allow attaching `*.slvs` files, but it does allow attaching `*.zip` files,
|
||||||
|
so any savefiles should first be archived.
|
||||||
|
|
||||||
|
Contributing code
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
SolveSpace is written in C++, and currently targets all compilers compliant with C++11.
|
||||||
|
This includes GCC 5 and later, Clang 3.3 and later, and Visual Studio 12 (2013) and later.
|
||||||
|
|
||||||
|
### High-level conventions
|
||||||
|
|
||||||
|
#### Portability
|
||||||
|
|
||||||
|
SolveSpace aims to consist of two general parts: a fully portable core, and platform-specific
|
||||||
|
UI and support code. Anything outside of `src/platform/` should only use standard C++11,
|
||||||
|
and rely on `src/platform/unixutil.cpp` and `src/platform/w32util.cpp` to interact with
|
||||||
|
the OS where this cannot be done through the C++11 standard library.
|
||||||
|
|
||||||
|
#### Libraries
|
||||||
|
|
||||||
|
SolveSpace primarily relies on the C++11 STL. STL has well-known drawbacks, but is also
|
||||||
|
widely supported, used, and understood. SolveSpace also includes a fair amount of use of
|
||||||
|
bespoke containers List and IdList; these provide STL iterators, and can be used when
|
||||||
|
convenient, such as when reusing other code.
|
||||||
|
|
||||||
|
One notable departure here is the STL I/O threads. SolveSpace does not use STL I/O threads
|
||||||
|
for two reasons: (i) the interface is borderline unusable, and (ii) on Windows it is not
|
||||||
|
possible to open files with Unicode paths through STL.
|
||||||
|
|
||||||
|
When using external libraries (other than to access platform features), the libraries
|
||||||
|
should satisfy the following conditions:
|
||||||
|
|
||||||
|
* Portable, and preferably not interacting with the platform at all;
|
||||||
|
* Can be included as a CMake subproject, to facilitate Windows, Android, etc. builds;
|
||||||
|
* Use a license less restrictive than GPL (BSD/MIT, Apache2, MPL, etc.)
|
||||||
|
|
||||||
|
#### String encoding
|
||||||
|
|
||||||
|
Internally, SolveSpace exclusively stores and uses UTF-8 for all purposes; any `std::string`
|
||||||
|
may be assumed to be encoded in UTF-8. On Windows, UTF-8 strings are converted to and from
|
||||||
|
wide strings at the boundary; see [UTF-8 Everywhere][utf8] for details.
|
||||||
|
|
||||||
|
[utf8]: http://utf8everywhere.org/
|
||||||
|
|
||||||
|
#### String formatting
|
||||||
|
|
||||||
|
For string formatting, a wrapper around `sprintf`, `ssprintf`, is used. A notable
|
||||||
|
pitfall when using it is trying to pass an `std::string` argument without first converting
|
||||||
|
it to a C string with `.c_str()`.
|
||||||
|
|
||||||
|
#### Filesystem access
|
||||||
|
|
||||||
|
For filesystem access, the C standard library is used. The `ssfopen` and `ssremove`
|
||||||
|
wrappers are provided that accept UTF-8 encoded paths.
|
||||||
|
|
||||||
|
#### Assertions
|
||||||
|
|
||||||
|
To ensure that internal invariants hold, the `ssassert` function is used, e.g.
|
||||||
|
`ssassert(!isFoo, "Unexpected foo condition");`. Unlike the standard `assert` function,
|
||||||
|
the `ssassert` function is always enabled, even in release builds. It is more valuable
|
||||||
|
to discover a bug through a crash than to silently generate incorrect results, and crashes
|
||||||
|
do not result in losing more than a few minutes of work thanks to the autosave feature.
|
||||||
|
|
||||||
|
### Use of C++ features
|
||||||
|
|
||||||
|
The conventions described in this section should be used for all new code, but there is a lot
|
||||||
|
of existing code in SolveSpace that does not use them. This is fine; don't touch it if it works,
|
||||||
|
but if you need to modify it anyway, might as well modernize it.
|
||||||
|
|
||||||
|
#### Exceptions
|
||||||
|
|
||||||
|
Exceptions are not used primarily because SolveSpace's testsuite uses measurement
|
||||||
|
of branch coverage, important for the critical parts such as the geometric kernel.
|
||||||
|
Every function call with exceptions enabled introduces a branch, making branch coverage
|
||||||
|
measurement useless.
|
||||||
|
|
||||||
|
#### Operator overloading
|
||||||
|
|
||||||
|
Operator overloading is not used primarily for historical reasons. Instead, method such
|
||||||
|
as `Plus` are used.
|
||||||
|
|
||||||
|
#### Member visibility
|
||||||
|
|
||||||
|
Member visibility is not used for implementation hiding. Every member field and function
|
||||||
|
is `public`.
|
||||||
|
|
||||||
|
#### Constructors
|
||||||
|
|
||||||
|
Constructors are not used for initialization, chiefly because indicating an error
|
||||||
|
in a constructor would require throwing an exception, nor does it use constructors for
|
||||||
|
blanket zero-initialization because of the performance impact of doing this for common
|
||||||
|
POD classes like `Vector`.
|
||||||
|
|
||||||
|
Instances can be zero-initialized using the aggregate-initialization syntax, e.g. `Foo foo = {};`.
|
||||||
|
This zero-initializes the POD members and default-initializes the non-POD members, generally
|
||||||
|
being an equivalent of `memset(&foo, 0, sizeof(foo));` but compatible with STL containers.
|
||||||
|
|
||||||
|
#### Input- and output-arguments
|
||||||
|
|
||||||
|
Functions accepting an input argument take it either by-value (`Vector v`) or
|
||||||
|
by-const-reference (`const Vector &v`). Generally, passing by-value is safer as the value
|
||||||
|
cannot be aliased by something else, but passing by-const-reference is faster, as a copy is
|
||||||
|
eliminated. Small values should always be passed by-value, and otherwise functions that do not
|
||||||
|
capture pointers into their arguments should take them by-const-reference. Use your judgement.
|
||||||
|
|
||||||
|
Functions accepting an output argument always take it by-pointer (`Vector *v`). This makes
|
||||||
|
it immediately visible at the call site as it is seen that the address is taken. Arguments
|
||||||
|
are never passed by-reference, except when needed for interoperability with STL, etc.
|
||||||
|
|
||||||
|
#### Iteration
|
||||||
|
|
||||||
|
`foreach`-style iteration is preferred for both STL and `List`/`IdList` containers as it indicates
|
||||||
|
intent clearly, as opposed to `for`-style.
|
||||||
|
|
||||||
|
#### Const correctness
|
||||||
|
|
||||||
|
Functions that do not mutate `this` should be marked as `const`; when iterating a collection
|
||||||
|
without mutating any of its elements, `for(const Foo &elem : collection)` is preferred to indicate
|
||||||
|
the intent.
|
||||||
|
|
||||||
|
### Coding style
|
||||||
|
|
||||||
|
Code is formatted by the following rules:
|
||||||
|
|
||||||
|
* Code is indented using 4 spaces, with no trailing spaces, and lines are wrapped
|
||||||
|
at 100 columns;
|
||||||
|
* Braces are placed at the end of the line with the declaration or control flow statement;
|
||||||
|
* Braces are used with every control flow statement, even if there is only one statement
|
||||||
|
in the body;
|
||||||
|
* There is no space after control flow keywords (`if`, `while`, etc.);
|
||||||
|
* Identifiers are formatted in camel case; variables start with a lowercase letter
|
||||||
|
(`exampleVariable`) and functions start with an uppercase letter (`ExampleFunction`).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
std::string SolveSpace::Dirname(std::string filename) {
|
||||||
|
int slash = filename.rfind(PATH_SEP);
|
||||||
|
if(slash >= 0) {
|
||||||
|
return filename.substr(0, slash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Debugging code
|
||||||
|
--------------
|
||||||
|
|
||||||
|
SolveSpace releases are throughly tested but sometimes they contain crash
|
||||||
|
bugs anyway. The reason for such crashes can be determined only if the executable
|
||||||
|
was built with debug information.
|
||||||
|
|
||||||
|
### Debugging a released version
|
||||||
|
|
||||||
|
The Linux distributions usually include separate debug information packages.
|
||||||
|
On a Debian derivative (e.g. Ubuntu), these can be installed with:
|
||||||
|
|
||||||
|
apt-get install solvespace-dbg
|
||||||
|
|
||||||
|
The macOS releases include the debug information, and no further action
|
||||||
|
is needed.
|
||||||
|
|
||||||
|
The Windows releases include the debug information on the GitHub
|
||||||
|
[release downloads page](https://github.com/solvespace/solvespace/releases).
|
||||||
|
|
||||||
|
### Debugging a custom build
|
||||||
|
|
||||||
|
If you are building SolveSpace yourself on a Unix-like platform,
|
||||||
|
configure or re-configure SolveSpace to produce a debug build, and
|
||||||
|
then re-build it:
|
||||||
|
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Debug [other cmake args...]
|
||||||
|
make
|
||||||
|
|
||||||
|
If you are building SolveSpace yourself using the Visual Studio IDE,
|
||||||
|
select Debug from the Solution Configurations list box on the toolbar,
|
||||||
|
and build the solution.
|
||||||
|
|
||||||
|
### Debugging with gdb
|
||||||
|
|
||||||
|
gdb is a debugger that is mostly used on Linux. First, run SolveSpace
|
||||||
|
under debugging:
|
||||||
|
|
||||||
|
gdb [path to solvespace executable]
|
||||||
|
(gdb) run
|
||||||
|
|
||||||
|
Then, reproduce the crash. After the crash, attach the output in
|
||||||
|
the console, as well as output of the following gdb commands to
|
||||||
|
a bug report:
|
||||||
|
|
||||||
|
(gdb) backtrace
|
||||||
|
(gdb) info locals
|
||||||
|
|
||||||
|
If the crash is not easy to reproduce, please generate a core file,
|
||||||
|
which you can use to resume the debugging session later, and provide
|
||||||
|
any other information that is requested:
|
||||||
|
|
||||||
|
(gdb) generate-core-file
|
||||||
|
|
||||||
|
This will generate a large file called like `core.1234` in the current
|
||||||
|
directory; it can be later re-loaded using `gdb --core core.1234`.
|
||||||
|
|
||||||
|
### Debugging with lldb
|
||||||
|
|
||||||
|
lldb is a debugger that is mostly used on macOS. First, run SolveSpace
|
||||||
|
under debugging:
|
||||||
|
|
||||||
|
lldb [path to solvespace executable]
|
||||||
|
(lldb) run
|
||||||
|
|
||||||
|
Then, reproduce the crash. After the crash, attach the output in
|
||||||
|
the console, as well as output of the following gdb commands to
|
||||||
|
a bug report:
|
||||||
|
|
||||||
|
(lldb) backtrace all
|
||||||
|
(lldb) frame variable
|
||||||
|
|
||||||
|
If the crash is not easy to reproduce, please generate a core file,
|
||||||
|
which you can use to resume the debugging session later, and provide
|
||||||
|
any other information that is requested:
|
||||||
|
|
||||||
|
(lldb) process save-core "core"
|
||||||
|
|
||||||
|
This will generate a large file called `core` in the current
|
||||||
|
directory; it can be later re-loaded using `lldb -c core`.
|
||||||
|
|
||||||
|
### Debugging GUI-related bugs on Linux
|
||||||
|
|
||||||
|
There are several environment variables available that make crashes
|
||||||
|
earlier and errors more informative. Before running SolveSpace, run
|
||||||
|
the following commands in your shell:
|
||||||
|
|
||||||
|
export G_DEBUG=fatal_warnings
|
||||||
|
export LIBGL_DEBUG=1
|
||||||
|
export MESA_DEBUG=1
|
102
Makefile
|
@ -1,102 +0,0 @@
|
||||||
DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32
|
|
||||||
# Use the multi-threaded static libc because libpng and zlib do; not sure if anything bad
|
|
||||||
# happens if those mix, but don't want to risk it.
|
|
||||||
CFLAGS = /W3 /nologo -MT -Iextlib -I..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /Zi /EHs # /O2
|
|
||||||
|
|
||||||
HEADERS = ..\common\win32\freeze.h ui.h solvespace.h dsc.h sketch.h expr.h polygon.h srf\surface.h
|
|
||||||
|
|
||||||
OBJDIR = obj
|
|
||||||
|
|
||||||
FREEZE = $(OBJDIR)\freeze.obj
|
|
||||||
|
|
||||||
W32OBJS = $(OBJDIR)\w32main.obj \
|
|
||||||
$(OBJDIR)\w32util.obj \
|
|
||||||
|
|
||||||
SSOBJS = $(OBJDIR)\solvespace.obj \
|
|
||||||
$(OBJDIR)\textwin.obj \
|
|
||||||
$(OBJDIR)\textscreens.obj \
|
|
||||||
$(OBJDIR)\confscreen.obj \
|
|
||||||
$(OBJDIR)\describescreen.obj \
|
|
||||||
$(OBJDIR)\graphicswin.obj \
|
|
||||||
$(OBJDIR)\modify.obj \
|
|
||||||
$(OBJDIR)\clipboard.obj \
|
|
||||||
$(OBJDIR)\view.obj \
|
|
||||||
$(OBJDIR)\util.obj \
|
|
||||||
$(OBJDIR)\style.obj \
|
|
||||||
$(OBJDIR)\entity.obj \
|
|
||||||
$(OBJDIR)\drawentity.obj \
|
|
||||||
$(OBJDIR)\group.obj \
|
|
||||||
$(OBJDIR)\groupmesh.obj \
|
|
||||||
$(OBJDIR)\request.obj \
|
|
||||||
$(OBJDIR)\glhelper.obj \
|
|
||||||
$(OBJDIR)\expr.obj \
|
|
||||||
$(OBJDIR)\constraint.obj \
|
|
||||||
$(OBJDIR)\constrainteq.obj \
|
|
||||||
$(OBJDIR)\mouse.obj \
|
|
||||||
$(OBJDIR)\draw.obj \
|
|
||||||
$(OBJDIR)\toolbar.obj \
|
|
||||||
$(OBJDIR)\drawconstraint.obj \
|
|
||||||
$(OBJDIR)\file.obj \
|
|
||||||
$(OBJDIR)\undoredo.obj \
|
|
||||||
$(OBJDIR)\system.obj \
|
|
||||||
$(OBJDIR)\polygon.obj \
|
|
||||||
$(OBJDIR)\mesh.obj \
|
|
||||||
$(OBJDIR)\bsp.obj \
|
|
||||||
$(OBJDIR)\ttf.obj \
|
|
||||||
$(OBJDIR)\generate.obj \
|
|
||||||
$(OBJDIR)\export.obj \
|
|
||||||
$(OBJDIR)\exportvector.obj \
|
|
||||||
$(OBJDIR)\exportstep.obj \
|
|
||||||
|
|
||||||
SRFOBJS = $(OBJDIR)\ratpoly.obj \
|
|
||||||
$(OBJDIR)\curve.obj \
|
|
||||||
$(OBJDIR)\surface.obj \
|
|
||||||
$(OBJDIR)\triangulate.obj \
|
|
||||||
$(OBJDIR)\boolean.obj \
|
|
||||||
$(OBJDIR)\surfinter.obj \
|
|
||||||
$(OBJDIR)\raycast.obj \
|
|
||||||
$(OBJDIR)\merge.obj \
|
|
||||||
|
|
||||||
|
|
||||||
RES = $(OBJDIR)\resource.res
|
|
||||||
|
|
||||||
|
|
||||||
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib opengl32.lib glu32.lib \
|
|
||||||
extlib\libpng.lib extlib\zlib.lib extlib\si\siapp.lib
|
|
||||||
|
|
||||||
all: $(OBJDIR)/solvespace.exe
|
|
||||||
@cp $(OBJDIR)/solvespace.exe .
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f obj/*
|
|
||||||
|
|
||||||
$(OBJDIR)/solvespace.exe: $(SRFOBJS) $(SSOBJS) $(W32OBJS) $(FREEZE) $(RES)
|
|
||||||
@$(CC) $(DEFINES) $(CFLAGS) -Fe$(OBJDIR)/solvespace.exe $(SSOBJS) $(SRFOBJS) $(W32OBJS) $(FREEZE) $(RES) $(LIBS)
|
|
||||||
editbin /nologo /STACK:8388608 $(OBJDIR)/solvespace.exe
|
|
||||||
@echo solvespace.exe
|
|
||||||
|
|
||||||
$(SSOBJS): $(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp
|
|
||||||
|
|
||||||
$(SRFOBJS): srf\$(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj srf\$(@B).cpp
|
|
||||||
|
|
||||||
$(W32OBJS): win32/$(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj win32/$(@B).cpp
|
|
||||||
|
|
||||||
$(FREEZE): ..\common\win32\$(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\common\win32\$(@B).cpp
|
|
||||||
|
|
||||||
$(RES): win32/$(@B).rc icon.ico
|
|
||||||
rc win32/$(@B).rc
|
|
||||||
mv win32/$(@B).res $(OBJDIR)/$(@B).res
|
|
||||||
|
|
||||||
toolbar.cpp: $(OBJDIR)/icons.h
|
|
||||||
|
|
||||||
textwin.cpp: $(OBJDIR)/icons.h
|
|
||||||
|
|
||||||
glhelper.cpp: bitmapfont.table font.table bitmapextra.table
|
|
||||||
|
|
||||||
$(OBJDIR)/icons.h: icons/* png2c.pl
|
|
||||||
perl png2c.pl $(OBJDIR)/icons.h $(OBJDIR)/icons-proto.h
|
|
||||||
|
|
164
README.md
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
SolveSpace
|
||||||
|
==========
|
||||||
|
|
||||||
|
This repository contains the source code of [SolveSpace][], a parametric
|
||||||
|
2d/3d CAD.
|
||||||
|
|
||||||
|
[solvespace]: http://solvespace.com
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
### macOS (>=10.6 64-bit), Windows (>=Vista 32-bit)
|
||||||
|
|
||||||
|
Binary packages for macOS and Windows are available via
|
||||||
|
[GitHub releases][rel].
|
||||||
|
|
||||||
|
[rel]: https://github.com/solvespace/solvespace/releases
|
||||||
|
|
||||||
|
### Other systems
|
||||||
|
|
||||||
|
See below.
|
||||||
|
|
||||||
|
Building on Linux
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
### Building for Linux
|
||||||
|
|
||||||
|
You will need CMake, zlib, libpng, cairo, freetype. To build the GUI, you will need
|
||||||
|
fontconfig, gtkmm 3.0 (version 3.16 or later), pangomm 1.4, OpenGL and OpenGL GLU, and
|
||||||
|
optionally, the Space Navigator client library.
|
||||||
|
On a Debian derivative (e.g. Ubuntu) these can be installed with:
|
||||||
|
|
||||||
|
apt-get install cmake zlib1g-dev libpng-dev libcairo2-dev libfreetype6-dev
|
||||||
|
apt-get install libjson-c-dev libfontconfig1-dev libgtkmm-3.0-dev libpangomm-1.4-dev \
|
||||||
|
libgl-dev libglu-dev libspnav-dev
|
||||||
|
|
||||||
|
Before building, check out the necessary submodules:
|
||||||
|
|
||||||
|
git submodule update --init extlib/libdxfrw
|
||||||
|
|
||||||
|
After that, build SolveSpace as following:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
The graphical interface is built as `build/bin/solvespace`, and the command-line interface
|
||||||
|
is built as `build/bin/solvespace-cli`. It is possible to build only the command-line interface
|
||||||
|
by passing the `-DENABLE_GUI=OFF` flag to the cmake invocation.
|
||||||
|
|
||||||
|
### Building for Windows
|
||||||
|
|
||||||
|
You will need CMake and a Windows cross-compiler.
|
||||||
|
On a Debian derivative (e.g. Ubuntu) these can be installed with:
|
||||||
|
|
||||||
|
apt-get install cmake mingw-w64
|
||||||
|
|
||||||
|
Before building, check out the necessary submodules:
|
||||||
|
|
||||||
|
git submodule update --init
|
||||||
|
|
||||||
|
After that, build 32-bit SolveSpace as following:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
make
|
||||||
|
|
||||||
|
Or, build 64-bit SolveSpace as following:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw64.cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
make
|
||||||
|
|
||||||
|
The graphical interface is built as `build/bin/solvespace.exe`, and the command-line interface
|
||||||
|
is built as `build/bin/solvespace-cli.exe`.
|
||||||
|
|
||||||
|
Space Navigator support will not be available.
|
||||||
|
|
||||||
|
Building on macOS
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
You will need XCode tools, CMake, libpng and Freetype. To build tests, you
|
||||||
|
will need cairo. Assuming you use
|
||||||
|
[homebrew][], these can be installed with:
|
||||||
|
|
||||||
|
brew install cmake libpng freetype cairo
|
||||||
|
|
||||||
|
XCode has to be installed via AppStore; it requires a free Apple ID.
|
||||||
|
|
||||||
|
Before building, check out the necessary submodules:
|
||||||
|
|
||||||
|
git submodule update --init extlib/libdxfrw
|
||||||
|
|
||||||
|
After that, build SolveSpace as following:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||||
|
make
|
||||||
|
|
||||||
|
The application is built in `build/bin/solvespace.app`, the graphical interface executable
|
||||||
|
is `build/bin/solvespace.app/Contents/MacOS/solvespace`, and the command-line interface executable
|
||||||
|
is `build/bin/solvespace.app/Contents/MacOS/solvespace-cli`.
|
||||||
|
|
||||||
|
[homebrew]: http://brew.sh/
|
||||||
|
|
||||||
|
Building on Windows
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
You will need [git][gitwin], [cmake][cmakewin] and Visual C++.
|
||||||
|
|
||||||
|
### Building with Visual Studio IDE
|
||||||
|
|
||||||
|
Check out the git submodules. Create a directory `build` in
|
||||||
|
the source tree and point cmake-gui to the source tree and that directory.
|
||||||
|
Press "Configure" and "Generate", then open `build\solvespace.sln` with
|
||||||
|
Visual C++ and build it.
|
||||||
|
|
||||||
|
### Building with Visual Studio in a command prompt
|
||||||
|
|
||||||
|
First, ensure that git and cl (the Visual C++ compiler driver) are in your
|
||||||
|
`%PATH%`; the latter is usually done by invoking `vcvarsall.bat` from your
|
||||||
|
Visual Studio install. Then, run the following in cmd or PowerShell:
|
||||||
|
|
||||||
|
git submodule update --init
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
|
||||||
|
nmake
|
||||||
|
|
||||||
|
### Building with MinGW
|
||||||
|
|
||||||
|
It is also possible to build SolveSpace using [MinGW][mingw], though
|
||||||
|
Space Navigator support will be disabled.
|
||||||
|
|
||||||
|
First, ensure that git and gcc are in your `$PATH`. Then, run the following
|
||||||
|
in bash:
|
||||||
|
|
||||||
|
git submodule update --init
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||||
|
make
|
||||||
|
|
||||||
|
[gitwin]: https://git-scm.com/download/win
|
||||||
|
[cmakewin]: http://www.cmake.org/download/#latest
|
||||||
|
[mingw]: http://www.mingw.org/
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
------------
|
||||||
|
|
||||||
|
See the [guide for contributors](CONTRIBUTING.md) for the best way to file issues, contribute code,
|
||||||
|
and debug SolveSpace.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
SolveSpace is distributed under the terms of the [GPL3 license](COPYING.txt).
|
30
appveyor.yml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
version: 3.0.{build}
|
||||||
|
clone_depth: 1
|
||||||
|
before_build:
|
||||||
|
- git submodule update --init
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- set tag=x%APPVEYOR_REPO_TAG_NAME%
|
||||||
|
- if %tag:~,2% == xv (set BUILD_TYPE=RelWithDebInfo) else (set BUILD_TYPE=Debug)
|
||||||
|
- cmake -G"Visual Studio 12" -T v120 ..
|
||||||
|
build_script:
|
||||||
|
- msbuild "src\solvespace.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
- msbuild "src\solvespace-cli.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
- msbuild "test\solvespace-testsuite.vcxproj" /verbosity:minimal /property:Configuration=%BUILD_TYPE% /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
test_script:
|
||||||
|
- bin\%BUILD_TYPE%\solvespace-testsuite.exe
|
||||||
|
artifacts:
|
||||||
|
- path: build\bin\%BUILD_TYPE%\solvespace.exe
|
||||||
|
name: solvespace.exe
|
||||||
|
- path: build\bin\%BUILD_TYPE%\solvespace-cli.exe
|
||||||
|
name: solvespace-cli.exe
|
||||||
|
- path: build\bin\%BUILD_TYPE%\solvespace.pdb
|
||||||
|
name: solvespace.pdb
|
||||||
|
deploy:
|
||||||
|
- provider: GitHub
|
||||||
|
auth_token:
|
||||||
|
secure: P9/pf2nM+jlWKe7pCjMp41HycBNP/+5AsmE/TETrDUoBOa/9WFHelqdVFrbRn9IC
|
||||||
|
description: ""
|
||||||
|
artifact: solvespace.exe,solvespace-cli.exe,solvespace.pdb
|
||||||
|
on:
|
||||||
|
appveyor_repo_tag: true
|
17
bench/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# benchmark runner
|
||||||
|
|
||||||
|
foreach(pkg_config_lib CAIRO)
|
||||||
|
include_directories(${${pkg_config_lib}_INCLUDE_DIRS})
|
||||||
|
link_directories(${${pkg_config_lib}_LIBRARY_DIRS})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_executable(solvespace-benchmark
|
||||||
|
harness.cpp
|
||||||
|
$<TARGET_PROPERTY:resources,EXTRA_SOURCES>)
|
||||||
|
|
||||||
|
target_link_libraries(solvespace-benchmark
|
||||||
|
solvespace-core
|
||||||
|
solvespace-headless)
|
||||||
|
|
||||||
|
add_dependencies(solvespace-benchmark
|
||||||
|
resources)
|
78
bench/harness.cpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Our harness for running benchmarks.
|
||||||
|
//
|
||||||
|
// Copyright 2016 whitequark
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
static bool RunBenchmark(std::function<void()> setupFn,
|
||||||
|
std::function<bool()> benchFn,
|
||||||
|
std::function<void()> teardownFn,
|
||||||
|
size_t minIter = 5, double minTime = 5.0) {
|
||||||
|
// Warmup
|
||||||
|
setupFn();
|
||||||
|
if(!benchFn()) {
|
||||||
|
fprintf(stderr, "Benchmark failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
teardownFn();
|
||||||
|
|
||||||
|
// Benchmark
|
||||||
|
size_t iter = 0;
|
||||||
|
double time = 0.0;
|
||||||
|
while(iter < minIter || time < minTime) {
|
||||||
|
setupFn();
|
||||||
|
auto testStartTime = std::chrono::steady_clock::now();
|
||||||
|
benchFn();
|
||||||
|
auto testEndTime = std::chrono::steady_clock::now();
|
||||||
|
teardownFn();
|
||||||
|
|
||||||
|
std::chrono::duration<double> testTime = testEndTime - testStartTime;
|
||||||
|
time += testTime.count();
|
||||||
|
iter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report
|
||||||
|
fprintf(stdout, "Iterations: %zd\n", iter);
|
||||||
|
fprintf(stdout, "Time: %.3f s\n", time);
|
||||||
|
fprintf(stdout, "Per iter.: %.3f s\n", time / (double)iter);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
std::vector<std::string> args = InitPlatform(argc, argv);
|
||||||
|
|
||||||
|
std::string mode;
|
||||||
|
Platform::Path filename;
|
||||||
|
if(args.size() == 3) {
|
||||||
|
mode = args[1];
|
||||||
|
filename = Platform::Path::From(args[2]);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Usage: %s [mode] [filename]\n", args[0].c_str());
|
||||||
|
fprintf(stderr, "Mode can be one of: load.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
if(mode == "load") {
|
||||||
|
result = RunBenchmark(
|
||||||
|
[] {
|
||||||
|
SS.Init();
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
if(!SS.LoadFromFile(filename))
|
||||||
|
return false;
|
||||||
|
SS.AfterNewFile();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[] {
|
||||||
|
SK.Clear();
|
||||||
|
SS.Clear();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown mode \"%s\"\n", mode.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result == true ? 0 : 1);
|
||||||
|
}
|
|
@ -1,68 +0,0 @@
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
|
|
2179
bitmapfont.table
680
bsp.cpp
|
@ -1,680 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Binary space partitioning tree, used to represent a volume in 3-space
|
|
||||||
// bounded by a triangle mesh. These are used to compute Boolean operations
|
|
||||||
// on meshes. These aren't used for anything relating to an SShell of
|
|
||||||
// ratpoly surfaces.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
|
|
||||||
SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
|
|
||||||
|
|
||||||
SBsp3 *SBsp3::FromMesh(SMesh *m) {
|
|
||||||
SBsp3 *bsp3 = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
SMesh mc; ZERO(&mc);
|
|
||||||
for(i = 0; i < m->l.n; i++) {
|
|
||||||
mc.AddTriangle(&(m->l.elem[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
srand(0); // Let's be deterministic, at least!
|
|
||||||
int n = mc.l.n;
|
|
||||||
while(n > 1) {
|
|
||||||
int k = rand() % n;
|
|
||||||
n--;
|
|
||||||
SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < mc.l.n; i++) {
|
|
||||||
bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.Clear();
|
|
||||||
return bsp3;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector SBsp3::IntersectionWith(Vector a, Vector b) {
|
|
||||||
double da = a.Dot(n) - d;
|
|
||||||
double db = b.Dot(n) - d;
|
|
||||||
if(da*db > 0) oops();
|
|
||||||
|
|
||||||
double dab = (db - da);
|
|
||||||
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp3::InsertInPlane(bool pos2, STriangle *tr, SMesh *m) {
|
|
||||||
Vector tc = ((tr->a).Plus(tr->b).Plus(tr->c)).ScaledBy(1.0/3);
|
|
||||||
|
|
||||||
bool onFace = false;
|
|
||||||
bool sameNormal;
|
|
||||||
double maxNormalMag = -1;
|
|
||||||
|
|
||||||
Vector lln, trn = tr->Normal();
|
|
||||||
|
|
||||||
SBsp3 *ll = this;
|
|
||||||
while(ll) {
|
|
||||||
if((ll->tri).ContainsPoint(tc)) {
|
|
||||||
onFace = true;
|
|
||||||
// If the mesh contains almost-zero-area triangles, and we're
|
|
||||||
// just on the edge of one of those, then don't trust its normal.
|
|
||||||
lln = (ll->tri).Normal();
|
|
||||||
if(lln.Magnitude() > maxNormalMag) {
|
|
||||||
sameNormal = trn.Dot(lln) > 0;
|
|
||||||
maxNormalMag = lln.Magnitude();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ll = ll->more;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m->flipNormal && ((!pos2 && !onFace) ||
|
|
||||||
(onFace && !sameNormal && m->keepCoplanar)))
|
|
||||||
{
|
|
||||||
m->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
|
|
||||||
} else if(!(m->flipNormal) && ((pos2 && !onFace) ||
|
|
||||||
(onFace && sameNormal && m->keepCoplanar)))
|
|
||||||
{
|
|
||||||
m->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
|
|
||||||
} else {
|
|
||||||
m->atLeastOneDiscarded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp3::InsertHow(int how, STriangle *tr, SMesh *instead) {
|
|
||||||
switch(how) {
|
|
||||||
case POS:
|
|
||||||
if(instead && !pos) goto alt;
|
|
||||||
pos = pos->Insert(tr, instead);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEG:
|
|
||||||
if(instead && !neg) goto alt;
|
|
||||||
neg = neg->Insert(tr, instead);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case COPLANAR: {
|
|
||||||
if(instead) goto alt;
|
|
||||||
SBsp3 *m = Alloc();
|
|
||||||
m->n = n;
|
|
||||||
m->d = d;
|
|
||||||
m->tri = *tr;
|
|
||||||
m->more = more;
|
|
||||||
more = m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
alt:
|
|
||||||
if(how == POS && !(instead->flipNormal)) {
|
|
||||||
instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
|
|
||||||
} else if(how == NEG && instead->flipNormal) {
|
|
||||||
instead->AddTriangle(tr->meta, tr->c, tr->b, tr->a);
|
|
||||||
} else if(how == COPLANAR) {
|
|
||||||
if(edges) {
|
|
||||||
edges->InsertTriangle(tr, instead, this);
|
|
||||||
} else {
|
|
||||||
// I suppose this actually is allowed to happen, if the coplanar
|
|
||||||
// face is the leaf, and all of its neighbors are earlier in tree?
|
|
||||||
InsertInPlane(false, tr, instead);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
instead->atLeastOneDiscarded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp3::InsertConvexHow(int how, STriMeta meta, Vector *vertex, int n,
|
|
||||||
SMesh *instead)
|
|
||||||
{
|
|
||||||
switch(how) {
|
|
||||||
case POS:
|
|
||||||
if(pos) {
|
|
||||||
pos = pos->InsertConvex(meta, vertex, n, instead);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEG:
|
|
||||||
if(neg) {
|
|
||||||
neg = neg->InsertConvex(meta, vertex, n, instead);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < n - 2; i++) {
|
|
||||||
STriangle tr = STriangle::From(meta,
|
|
||||||
vertex[0], vertex[i+1], vertex[i+2]);
|
|
||||||
InsertHow(how, &tr, instead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SBsp3 *SBsp3::InsertConvex(STriMeta meta, Vector *vertex, int cnt,
|
|
||||||
SMesh *instead)
|
|
||||||
{
|
|
||||||
Vector e01 = (vertex[1]).Minus(vertex[0]);
|
|
||||||
Vector e12 = (vertex[2]).Minus(vertex[1]);
|
|
||||||
Vector out = e01.Cross(e12);
|
|
||||||
|
|
||||||
#define MAX_VERTICES 50
|
|
||||||
if(cnt+1 >= MAX_VERTICES) goto triangulate;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
Vector on[2];
|
|
||||||
bool isPos[MAX_VERTICES];
|
|
||||||
bool isNeg[MAX_VERTICES];
|
|
||||||
bool isOn[MAX_VERTICES];
|
|
||||||
int posc = 0, negc = 0, onc = 0;
|
|
||||||
for(i = 0; i < cnt; i++) {
|
|
||||||
double dt = n.Dot(vertex[i]);
|
|
||||||
isPos[i] = isNeg[i] = isOn[i] = false;
|
|
||||||
if(fabs(dt - d) < LENGTH_EPS) {
|
|
||||||
isOn[i] = true;
|
|
||||||
if(onc < 2) {
|
|
||||||
on[onc] = vertex[i];
|
|
||||||
}
|
|
||||||
onc++;
|
|
||||||
} else if(dt > d) {
|
|
||||||
isPos[i] = true;
|
|
||||||
posc++;
|
|
||||||
} else {
|
|
||||||
isNeg[i] = true;
|
|
||||||
negc++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(onc != 2 && onc != 1 && onc != 0) goto triangulate;
|
|
||||||
|
|
||||||
if(onc == 2) {
|
|
||||||
if(!instead) {
|
|
||||||
SEdge se = SEdge::From(on[0], on[1]);
|
|
||||||
edges = edges->InsertEdge(&se, n, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(posc == 0) {
|
|
||||||
InsertConvexHow(NEG, meta, vertex, cnt, instead);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if(negc == 0) {
|
|
||||||
InsertConvexHow(POS, meta, vertex, cnt, instead);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector vpos[MAX_VERTICES];
|
|
||||||
Vector vneg[MAX_VERTICES];
|
|
||||||
int npos = 0, nneg = 0;
|
|
||||||
|
|
||||||
Vector inter[2];
|
|
||||||
int inters = 0;
|
|
||||||
|
|
||||||
for(i = 0; i < cnt; i++) {
|
|
||||||
int ip = WRAP((i + 1), cnt);
|
|
||||||
|
|
||||||
if(isPos[i]) {
|
|
||||||
vpos[npos++] = vertex[i];
|
|
||||||
}
|
|
||||||
if(isNeg[i]) {
|
|
||||||
vneg[nneg++] = vertex[i];
|
|
||||||
}
|
|
||||||
if(isOn[i]) {
|
|
||||||
vneg[nneg++] = vertex[i];
|
|
||||||
vpos[npos++] = vertex[i];
|
|
||||||
}
|
|
||||||
if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) {
|
|
||||||
Vector vi = IntersectionWith(vertex[i], vertex[ip]);
|
|
||||||
vpos[npos++] = vi;
|
|
||||||
vneg[nneg++] = vi;
|
|
||||||
|
|
||||||
if(inters >= 2) goto triangulate; // XXX shouldn't happen but does
|
|
||||||
inter[inters++] = vi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(npos > cnt + 1 || nneg > cnt + 1) oops();
|
|
||||||
|
|
||||||
if(!instead) {
|
|
||||||
if(inters == 2) {
|
|
||||||
SEdge se = SEdge::From(inter[0], inter[1]);
|
|
||||||
edges = edges->InsertEdge(&se, n, out);
|
|
||||||
} else if(inters == 1 && onc == 1) {
|
|
||||||
SEdge se = SEdge::From(inter[0], on[0]);
|
|
||||||
edges = edges->InsertEdge(&se, n, out);
|
|
||||||
} else if(inters == 0 && onc == 2) {
|
|
||||||
// We already handled this on-plane existing edge
|
|
||||||
} else {
|
|
||||||
goto triangulate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(nneg < 3 || npos < 3) goto triangulate; // XXX
|
|
||||||
|
|
||||||
InsertConvexHow(NEG, meta, vneg, nneg, instead);
|
|
||||||
InsertConvexHow(POS, meta, vpos, npos, instead);
|
|
||||||
return this;
|
|
||||||
|
|
||||||
triangulate:
|
|
||||||
// We don't handle the special case for this; do it as triangles
|
|
||||||
SBsp3 *r = this;
|
|
||||||
for(i = 0; i < cnt - 2; i++) {
|
|
||||||
STriangle tr = STriangle::From(meta,
|
|
||||||
vertex[0], vertex[i+1], vertex[i+2]);
|
|
||||||
r = r->Insert(&tr, instead);
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead) {
|
|
||||||
if(!this) {
|
|
||||||
if(instead) {
|
|
||||||
if(instead->flipNormal) {
|
|
||||||
instead->atLeastOneDiscarded = true;
|
|
||||||
} else {
|
|
||||||
instead->AddTriangle(tr->meta, tr->a, tr->b, tr->c);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Brand new node; so allocate for it, and fill us in.
|
|
||||||
SBsp3 *r = Alloc();
|
|
||||||
r->n = (tr->Normal()).WithMagnitude(1);
|
|
||||||
r->d = (tr->a).Dot(r->n);
|
|
||||||
r->tri = *tr;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
double dt[3] = { (tr->a).Dot(n), (tr->b).Dot(n), (tr->c).Dot(n) };
|
|
||||||
|
|
||||||
int inc = 0, posc = 0, negc = 0;
|
|
||||||
bool isPos[3], isNeg[3], isOn[3];
|
|
||||||
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
|
|
||||||
// Count vertices in the plane
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
if(fabs(dt[i] - d) < LENGTH_EPS) {
|
|
||||||
inc++;
|
|
||||||
isOn[i] = true;
|
|
||||||
} else if(dt[i] > d) {
|
|
||||||
posc++;
|
|
||||||
isPos[i] = true;
|
|
||||||
} else {
|
|
||||||
negc++;
|
|
||||||
isNeg[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All vertices in-plane
|
|
||||||
if(inc == 3) {
|
|
||||||
InsertHow(COPLANAR, tr, instead);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No split required
|
|
||||||
if(posc == 0 || negc == 0) {
|
|
||||||
if(inc == 2) {
|
|
||||||
Vector a, b;
|
|
||||||
if (!isOn[0]) { a = tr->b; b = tr->c; }
|
|
||||||
else if(!isOn[1]) { a = tr->c; b = tr->a; }
|
|
||||||
else if(!isOn[2]) { a = tr->a; b = tr->b; }
|
|
||||||
else oops();
|
|
||||||
if(!instead) {
|
|
||||||
SEdge se = SEdge::From(a, b);
|
|
||||||
edges = edges->InsertEdge(&se, n, tr->Normal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(posc > 0) {
|
|
||||||
InsertHow(POS, tr, instead);
|
|
||||||
} else {
|
|
||||||
InsertHow(NEG, tr, instead);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The polygon must be split into two pieces, one above, one below.
|
|
||||||
Vector a, b, c;
|
|
||||||
|
|
||||||
if(posc == 1 && negc == 1 && inc == 1) {
|
|
||||||
bool bpos;
|
|
||||||
// Standardize so that a is on the plane
|
|
||||||
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
|
|
||||||
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
|
|
||||||
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
|
|
||||||
} else oops();
|
|
||||||
|
|
||||||
Vector bPc = IntersectionWith(b, c);
|
|
||||||
STriangle btri = STriangle::From(tr->meta, a, b, bPc);
|
|
||||||
STriangle ctri = STriangle::From(tr->meta, c, a, bPc);
|
|
||||||
|
|
||||||
if(bpos) {
|
|
||||||
InsertHow(POS, &btri, instead);
|
|
||||||
InsertHow(NEG, &ctri, instead);
|
|
||||||
} else {
|
|
||||||
InsertHow(POS, &ctri, instead);
|
|
||||||
InsertHow(NEG, &btri, instead);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!instead) {
|
|
||||||
SEdge se = SEdge::From(a, bPc);
|
|
||||||
edges = edges->InsertEdge(&se, n, tr->Normal());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(posc == 2 && negc == 1) {
|
|
||||||
// Standardize so that a is on one side, and b and c are on the other.
|
|
||||||
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
|
|
||||||
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
|
|
||||||
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
|
|
||||||
} else oops();
|
|
||||||
|
|
||||||
} else if(posc == 1 && negc == 2) {
|
|
||||||
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
|
|
||||||
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
|
|
||||||
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
|
|
||||||
} else oops();
|
|
||||||
} else oops();
|
|
||||||
|
|
||||||
Vector aPb = IntersectionWith(a, b);
|
|
||||||
Vector cPa = IntersectionWith(c, a);
|
|
||||||
|
|
||||||
STriangle alone = STriangle::From(tr->meta, a, aPb, cPa);
|
|
||||||
Vector quad[4] = { aPb, b, c, cPa };
|
|
||||||
|
|
||||||
if(posc == 2 && negc == 1) {
|
|
||||||
InsertConvexHow(POS, tr->meta, quad, 4, instead);
|
|
||||||
InsertHow(NEG, &alone, instead);
|
|
||||||
} else {
|
|
||||||
InsertConvexHow(NEG, tr->meta, quad, 4, instead);
|
|
||||||
InsertHow(POS, &alone, instead);
|
|
||||||
}
|
|
||||||
if(!instead) {
|
|
||||||
SEdge se = SEdge::From(aPb, cPa);
|
|
||||||
edges = edges->InsertEdge(&se, n, alone.Normal());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp3::GenerateInPaintOrder(SMesh *m) {
|
|
||||||
if(!this) return;
|
|
||||||
|
|
||||||
// Doesn't matter which branch we take if the normal has zero z
|
|
||||||
// component, so don't need a separate case for that.
|
|
||||||
if(n.z < 0) {
|
|
||||||
pos->GenerateInPaintOrder(m);
|
|
||||||
} else {
|
|
||||||
neg->GenerateInPaintOrder(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
SBsp3 *flip = this;
|
|
||||||
while(flip) {
|
|
||||||
m->AddTriangle(&(flip->tri));
|
|
||||||
flip = flip->more;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(n.z < 0) {
|
|
||||||
neg->GenerateInPaintOrder(m);
|
|
||||||
} else {
|
|
||||||
pos->GenerateInPaintOrder(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp3::DebugDraw(void) {
|
|
||||||
if(!this) return;
|
|
||||||
|
|
||||||
pos->DebugDraw();
|
|
||||||
Vector norm = tri.Normal();
|
|
||||||
glNormal3d(norm.x, norm.y, norm.z);
|
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glEnable(GL_LIGHTING);
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
glxVertex3v(tri.a);
|
|
||||||
glxVertex3v(tri.b);
|
|
||||||
glxVertex3v(tri.c);
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
||||||
glxDepthRangeOffset(2);
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
glxVertex3v(tri.a);
|
|
||||||
glxVertex3v(tri.b);
|
|
||||||
glxVertex3v(tri.c);
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
glDisable(GL_LIGHTING);
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
|
|
||||||
glPointSize(10);
|
|
||||||
glxDepthRangeOffset(2);
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
glxVertex3v(tri.a);
|
|
||||||
glxVertex3v(tri.b);
|
|
||||||
glxVertex3v(tri.c);
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
||||||
|
|
||||||
more->DebugDraw();
|
|
||||||
neg->DebugDraw();
|
|
||||||
|
|
||||||
edges->DebugDraw(n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////
|
|
||||||
|
|
||||||
Vector SBsp2::IntersectionWith(Vector a, Vector b) {
|
|
||||||
double da = a.Dot(no) - d;
|
|
||||||
double db = b.Dot(no) - d;
|
|
||||||
if(da*db > 0) oops();
|
|
||||||
|
|
||||||
double dab = (db - da);
|
|
||||||
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
|
|
||||||
}
|
|
||||||
|
|
||||||
SBsp2 *SBsp2::InsertEdge(SEdge *nedge, Vector nnp, Vector out) {
|
|
||||||
if(!this) {
|
|
||||||
// Brand new node; so allocate for it, and fill us in.
|
|
||||||
SBsp2 *r = Alloc();
|
|
||||||
r->np = nnp;
|
|
||||||
r->no = ((r->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1);
|
|
||||||
if(out.Dot(r->no) < 0) {
|
|
||||||
r->no = (r->no).ScaledBy(-1);
|
|
||||||
}
|
|
||||||
r->d = (nedge->a).Dot(r->no);
|
|
||||||
r->edge = *nedge;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
double dt[2] = { (nedge->a).Dot(no), (nedge->b).Dot(no) };
|
|
||||||
|
|
||||||
bool isPos[2], isNeg[2], isOn[2];
|
|
||||||
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
|
|
||||||
for(int i = 0; i < 2; i++) {
|
|
||||||
if(fabs(dt[i] - d) < LENGTH_EPS) {
|
|
||||||
isOn[i] = true;
|
|
||||||
} else if(dt[i] > d) {
|
|
||||||
isPos[i] = true;
|
|
||||||
} else {
|
|
||||||
isNeg[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if((isPos[0] && isPos[1])||(isPos[0] && isOn[1])||(isOn[0] && isPos[1])) {
|
|
||||||
pos = pos->InsertEdge(nedge, nnp, out);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if((isNeg[0] && isNeg[1])||(isNeg[0] && isOn[1])||(isOn[0] && isNeg[1])) {
|
|
||||||
neg = neg->InsertEdge(nedge, nnp, out);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if(isOn[0] && isOn[1]) {
|
|
||||||
SBsp2 *m = Alloc();
|
|
||||||
|
|
||||||
m->np = nnp;
|
|
||||||
m->no = ((m->np).Cross((nedge->b).Minus(nedge->a))).WithMagnitude(1);
|
|
||||||
if(out.Dot(m->no) < 0) {
|
|
||||||
m->no = (m->no).ScaledBy(-1);
|
|
||||||
}
|
|
||||||
m->d = (nedge->a).Dot(m->no);
|
|
||||||
m->edge = *nedge;
|
|
||||||
|
|
||||||
m->more = more;
|
|
||||||
more = m;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
if((isPos[0] && isNeg[1]) || (isNeg[0] && isPos[1])) {
|
|
||||||
Vector aPb = IntersectionWith(nedge->a, nedge->b);
|
|
||||||
|
|
||||||
SEdge ea = SEdge::From(nedge->a, aPb);
|
|
||||||
SEdge eb = SEdge::From(aPb, nedge->b);
|
|
||||||
|
|
||||||
if(isPos[0]) {
|
|
||||||
pos = pos->InsertEdge(&ea, nnp, out);
|
|
||||||
neg = neg->InsertEdge(&eb, nnp, out);
|
|
||||||
} else {
|
|
||||||
neg = neg->InsertEdge(&ea, nnp, out);
|
|
||||||
pos = pos->InsertEdge(&eb, nnp, out);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
oops();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp2::InsertTriangleHow(int how, STriangle *tr, SMesh *m, SBsp3 *bsp3) {
|
|
||||||
switch(how) {
|
|
||||||
case POS:
|
|
||||||
if(pos) {
|
|
||||||
pos->InsertTriangle(tr, m, bsp3);
|
|
||||||
} else {
|
|
||||||
bsp3->InsertInPlane(true, tr, m);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEG:
|
|
||||||
if(neg) {
|
|
||||||
neg->InsertTriangle(tr, m, bsp3);
|
|
||||||
} else {
|
|
||||||
bsp3->InsertInPlane(false, tr, m);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp2::InsertTriangle(STriangle *tr, SMesh *m, SBsp3 *bsp3) {
|
|
||||||
double dt[3] = { (tr->a).Dot(no), (tr->b).Dot(no), (tr->c).Dot(no) };
|
|
||||||
|
|
||||||
bool isPos[3], isNeg[3], isOn[3];
|
|
||||||
int inc = 0, posc = 0, negc = 0;
|
|
||||||
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
if(fabs(dt[i] - d) < LENGTH_EPS) {
|
|
||||||
isOn[i] = true;
|
|
||||||
inc++;
|
|
||||||
} else if(dt[i] > d) {
|
|
||||||
isPos[i] = true;
|
|
||||||
posc++;
|
|
||||||
} else {
|
|
||||||
isNeg[i] = true;
|
|
||||||
negc++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(inc == 3) {
|
|
||||||
// All vertices on-line; so it's a degenerate triangle, to ignore.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No split required
|
|
||||||
if(posc == 0 || negc == 0) {
|
|
||||||
if(posc > 0) {
|
|
||||||
InsertTriangleHow(POS, tr, m, bsp3);
|
|
||||||
} else {
|
|
||||||
InsertTriangleHow(NEG, tr, m, bsp3);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The polygon must be split into two pieces, one above, one below.
|
|
||||||
Vector a, b, c;
|
|
||||||
|
|
||||||
if(posc == 1 && negc == 1 && inc == 1) {
|
|
||||||
bool bpos;
|
|
||||||
// Standardize so that a is on the plane
|
|
||||||
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
|
|
||||||
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
|
|
||||||
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
|
|
||||||
} else oops();
|
|
||||||
|
|
||||||
Vector bPc = IntersectionWith(b, c);
|
|
||||||
STriangle btri = STriangle::From(tr->meta, a, b, bPc);
|
|
||||||
STriangle ctri = STriangle::From(tr->meta, c, a, bPc);
|
|
||||||
|
|
||||||
if(bpos) {
|
|
||||||
InsertTriangleHow(POS, &btri, m, bsp3);
|
|
||||||
InsertTriangleHow(NEG, &ctri, m, bsp3);
|
|
||||||
} else {
|
|
||||||
InsertTriangleHow(POS, &ctri, m, bsp3);
|
|
||||||
InsertTriangleHow(NEG, &btri, m, bsp3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(posc == 2 && negc == 1) {
|
|
||||||
// Standardize so that a is on one side, and b and c are on the other.
|
|
||||||
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
|
|
||||||
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
|
|
||||||
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
|
|
||||||
} else oops();
|
|
||||||
|
|
||||||
} else if(posc == 1 && negc == 2) {
|
|
||||||
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
|
|
||||||
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
|
|
||||||
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
|
|
||||||
} else oops();
|
|
||||||
} else oops();
|
|
||||||
|
|
||||||
Vector aPb = IntersectionWith(a, b);
|
|
||||||
Vector cPa = IntersectionWith(c, a);
|
|
||||||
|
|
||||||
STriangle alone = STriangle::From(tr->meta, a, aPb, cPa);
|
|
||||||
STriangle quad1 = STriangle::From(tr->meta, aPb, b, c );
|
|
||||||
STriangle quad2 = STriangle::From(tr->meta, aPb, c, cPa);
|
|
||||||
|
|
||||||
if(posc == 2 && negc == 1) {
|
|
||||||
InsertTriangleHow(POS, &quad1, m, bsp3);
|
|
||||||
InsertTriangleHow(POS, &quad2, m, bsp3);
|
|
||||||
InsertTriangleHow(NEG, &alone, m, bsp3);
|
|
||||||
} else {
|
|
||||||
InsertTriangleHow(NEG, &quad1, m, bsp3);
|
|
||||||
InsertTriangleHow(NEG, &quad2, m, bsp3);
|
|
||||||
InsertTriangleHow(POS, &alone, m, bsp3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SBsp2::DebugDraw(Vector n, double d) {
|
|
||||||
if(!this) return;
|
|
||||||
|
|
||||||
if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops();
|
|
||||||
if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops();
|
|
||||||
|
|
||||||
glLineWidth(10);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(edge.a);
|
|
||||||
glxVertex3v(edge.b);
|
|
||||||
glEnd();
|
|
||||||
pos->DebugDraw(n, d);
|
|
||||||
neg->DebugDraw(n, d);
|
|
||||||
more->DebugDraw(n, d);
|
|
||||||
glLineWidth(1);
|
|
||||||
}
|
|
||||||
|
|
10
cmake/AddVendoredSubdirectory.cmake
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Equivalent to add_subdirectory(... EXCLUDE_FROM_ALL), but also disables
|
||||||
|
# all warnings.
|
||||||
|
|
||||||
|
include(DisableWarnings)
|
||||||
|
|
||||||
|
function(add_vendored_subdirectory PATH)
|
||||||
|
disable_warnings()
|
||||||
|
|
||||||
|
add_subdirectory(${PATH} EXCLUDE_FROM_ALL)
|
||||||
|
endfunction()
|
15
cmake/DisableWarnings.cmake
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Disables all warnings on MSVC and GNU-compatible compilers.
|
||||||
|
|
||||||
|
function(disable_warnings)
|
||||||
|
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w" PARENT_SCOPE)
|
||||||
|
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W0" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w" PARENT_SCOPE)
|
||||||
|
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
28
cmake/FindSpaceWare.cmake
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Find the libspnav library and header.
|
||||||
|
#
|
||||||
|
# Sets the usual variables expected for find_package scripts:
|
||||||
|
#
|
||||||
|
# SPACEWARE_INCLUDE_DIR - header location
|
||||||
|
# SPACEWARE_LIBRARIES - library to link against
|
||||||
|
# SPACEWARE_FOUND - true if pugixml was found.
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
|
||||||
|
find_path(SPACEWARE_INCLUDE_DIR
|
||||||
|
spnav.h)
|
||||||
|
|
||||||
|
find_library(SPACEWARE_LIBRARY
|
||||||
|
NAMES spnav libspnav)
|
||||||
|
|
||||||
|
# Support the REQUIRED and QUIET arguments, and set SPACEWARE_FOUND if found.
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SPACEWARE DEFAULT_MSG
|
||||||
|
SPACEWARE_LIBRARY SPACEWARE_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if(SPACEWARE_FOUND)
|
||||||
|
set(SPACEWARE_LIBRARIES ${SPACEWARE_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(SPACEWARE_LIBRARY SPACEWARE_INCLUDE_DIR)
|
||||||
|
|
||||||
|
endif()
|
54
cmake/FindVendoredPackage.cmake
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Find the given library in the system locations, or build in-tree if not found.
|
||||||
|
#
|
||||||
|
# Arguments:
|
||||||
|
# PKG_NAME - name of the package as passed to find_package
|
||||||
|
# PKG_PATH - name of the source tree relative to extlib/
|
||||||
|
#
|
||||||
|
# The rest of the arguments are VARIABLE VALUE pairs. If the library is not found,
|
||||||
|
# every VARIABLE will be set to VALUE and find_package will be rerun with the REQUIRED flag.
|
||||||
|
# Regardless of where the library was found, only the specfied VARIABLEs that start with
|
||||||
|
# ${PKG_NAME} will be set in the parent scope.
|
||||||
|
#
|
||||||
|
# All warnings in the in-tree package are disabled.
|
||||||
|
|
||||||
|
include(DisableWarnings)
|
||||||
|
|
||||||
|
function(find_vendored_package PKG_NAME PKG_PATH)
|
||||||
|
find_package(${PKG_NAME})
|
||||||
|
|
||||||
|
set(cfg_name)
|
||||||
|
foreach(item ${ARGN})
|
||||||
|
if(NOT cfg_name)
|
||||||
|
set(cfg_name ${item})
|
||||||
|
else()
|
||||||
|
set(${cfg_name} ${item} CACHE INTERNAL "")
|
||||||
|
set(cfg_name)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
disable_warnings()
|
||||||
|
|
||||||
|
string(TOUPPER ${PKG_NAME} VAR_NAME)
|
||||||
|
if(NOT ${VAR_NAME}_FOUND)
|
||||||
|
message(STATUS "Using in-tree ${PKG_PATH}")
|
||||||
|
set(${VAR_NAME}_IN_TREE YES CACHE INTERNAL "")
|
||||||
|
|
||||||
|
add_subdirectory(extlib/${PKG_PATH} EXCLUDE_FROM_ALL)
|
||||||
|
find_package(${PKG_NAME} REQUIRED)
|
||||||
|
elseif(${VAR_NAME}_IN_TREE)
|
||||||
|
add_subdirectory(extlib/${PKG_PATH} EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Now put everything we just discovered into the cache.
|
||||||
|
set(cfg_name)
|
||||||
|
foreach(item ${ARGN} ${VAR_NAME}_FOUND)
|
||||||
|
if(NOT cfg_name)
|
||||||
|
set(cfg_name ${item})
|
||||||
|
else()
|
||||||
|
if(cfg_name MATCHES "^${VAR_NAME}")
|
||||||
|
set(${cfg_name} "${${cfg_name}}" CACHE INTERNAL "")
|
||||||
|
endif()
|
||||||
|
set(cfg_name)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
35
cmake/GetGitCommitHash.cmake
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
function(get_git_commit_hash)
|
||||||
|
get_filename_component(GIT_DESCRIBE_CMAKE_DIR ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||||
|
get_filename_component(GIT_ROOT ${GIT_DESCRIBE_CMAKE_DIR} PATH)
|
||||||
|
set(GIT_DIR "${GIT_ROOT}/.git")
|
||||||
|
|
||||||
|
# Add a CMake configure dependency to the currently checked out revision.
|
||||||
|
set(GIT_DEPENDS ${GIT_DIR}/HEAD)
|
||||||
|
file(READ ${GIT_DIR}/HEAD HEAD_REF)
|
||||||
|
if(HEAD_REF MATCHES "ref: (.+)\n")
|
||||||
|
set(HEAD_REF ${CMAKE_MATCH_1})
|
||||||
|
if(EXISTS "${GIT_DIR}/${HEAD_REF}")
|
||||||
|
list(APPEND GIT_DEPENDS ${GIT_DIR}/${HEAD_REF})
|
||||||
|
file(READ ${GIT_DIR}/${HEAD_REF} HEAD_REF)
|
||||||
|
elseif(EXISTS "${GIT_DIR}/packed-refs")
|
||||||
|
list(APPEND GIT_DEPENDS ${GIT_DIR}/packed-refs)
|
||||||
|
file(READ "${GIT_DIR}/packed-refs" PACKED_REFS)
|
||||||
|
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||||
|
set(HEAD_REF ${CMAKE_MATCH_1})
|
||||||
|
else()
|
||||||
|
set(HEAD_REF "")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(HEAD_REF "")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${GIT_DEPENDS})
|
||||||
|
|
||||||
|
string(STRIP ${HEAD_REF} HEAD_REF)
|
||||||
|
if(HEAD_REF STREQUAL "")
|
||||||
|
message(WARNING "Cannot determine git HEAD")
|
||||||
|
else()
|
||||||
|
set(GIT_COMMIT_HASH ${HEAD_REF} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
get_git_commit_hash()
|
47
cmake/MacOSXBundleInfo.plist.in
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>English</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>solvespace</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>SolveSpace</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}~${solvespace_GIT_HASH}</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${solvespace_VERSION_MAJOR}.${solvespace_VERSION_MINOR}</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>© 2008-2016 Jonathan Westhues and other authors</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
<key>NSMainNibFile</key>
|
||||||
|
<string>MainMenu</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>AppIcon</string>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>slvs</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeIconFile</key>
|
||||||
|
<string>AppIcon.icns</string>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>SolveSpace sketch</string>
|
||||||
|
<key>CFBundleTypeOSTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>slvs</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
15
cmake/Toolchain-mingw32.cmake
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
|
||||||
|
set(TRIPLE i686-w64-mingw32)
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER ${TRIPLE}-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER ${TRIPLE}-g++)
|
||||||
|
set(CMAKE_RC_COMPILER ${TRIPLE}-windres)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH /usr/${TRIPLE})
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
||||||
|
set(ENV{PKG_CONFIG_LIBDIR} /usr/${TRIPLE}/lib/pkgconfig)
|
15
cmake/Toolchain-mingw64.cmake
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
|
||||||
|
set(TRIPLE x86_64-w64-mingw32)
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER ${TRIPLE}-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER ${TRIPLE}-g++)
|
||||||
|
set(CMAKE_RC_COMPILER ${TRIPLE}-windres)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH /usr/${TRIPLE})
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
||||||
|
set(ENV{PKG_CONFIG_LIBDIR} /usr/${TRIPLE}/lib/pkgconfig)
|
6
cmake/c_flag_overrides.cmake
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
if(MSVC)
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
|
||||||
|
set(CMAKE_C_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
|
||||||
|
set(CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
|
||||||
|
endif()
|
6
cmake/cxx_flag_overrides.cmake
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
if(MSVC)
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG_INIT "/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1")
|
||||||
|
set(CMAKE_CXX_FLAGS_MINSIZEREL_INIT "/MT /O1 /Ob1 /D NDEBUG")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE_INIT "/MT /O2 /Ob2 /D NDEBUG")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "/MT /Zi /O2 /Ob1 /D NDEBUG")
|
||||||
|
endif()
|
433
confscreen.cpp
|
@ -1,433 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// For the configuration screen, setup items that are not specific to the
|
|
||||||
// file being edited right now.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeLightDirection(int link, DWORD v) {
|
|
||||||
char str[1024];
|
|
||||||
sprintf(str, "%.2f, %.2f, %.2f", CO(SS.lightDir[v]));
|
|
||||||
SS.TW.ShowEditControl(29+2*v, 8, str);
|
|
||||||
SS.TW.edit.meaning = EDIT_LIGHT_DIRECTION;
|
|
||||||
SS.TW.edit.i = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeLightIntensity(int link, DWORD v) {
|
|
||||||
char str[1024];
|
|
||||||
sprintf(str, "%.2f", SS.lightIntensity[v]);
|
|
||||||
SS.TW.ShowEditControl(29+2*v, 31, str);
|
|
||||||
SS.TW.edit.meaning = EDIT_LIGHT_INTENSITY;
|
|
||||||
SS.TW.edit.i = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeColor(int link, DWORD v) {
|
|
||||||
SS.TW.ShowEditControlWithColorPicker(9+2*v, 13, SS.modelColor[v]);
|
|
||||||
|
|
||||||
SS.TW.edit.meaning = EDIT_COLOR;
|
|
||||||
SS.TW.edit.i = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeChordTolerance(int link, DWORD v) {
|
|
||||||
char str[1024];
|
|
||||||
sprintf(str, "%.2f", SS.chordTol);
|
|
||||||
SS.TW.ShowEditControl(37, 3, str);
|
|
||||||
SS.TW.edit.meaning = EDIT_CHORD_TOLERANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeMaxSegments(int link, DWORD v) {
|
|
||||||
char str[1024];
|
|
||||||
sprintf(str, "%d", SS.maxSegments);
|
|
||||||
SS.TW.ShowEditControl(41, 3, str);
|
|
||||||
SS.TW.edit.meaning = EDIT_MAX_SEGMENTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeCameraTangent(int link, DWORD v) {
|
|
||||||
char str[1024];
|
|
||||||
sprintf(str, "%.3f", 1000*SS.cameraTangent);
|
|
||||||
SS.TW.ShowEditControl(47, 3, str);
|
|
||||||
SS.TW.edit.meaning = EDIT_CAMERA_TANGENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeGridSpacing(int link, DWORD v) {
|
|
||||||
SS.TW.ShowEditControl(51, 3, SS.MmToString(SS.gridSpacing));
|
|
||||||
SS.TW.edit.meaning = EDIT_GRID_SPACING;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeDigitsAfterDecimal(int link, DWORD v) {
|
|
||||||
char buf[128];
|
|
||||||
sprintf(buf, "%d", SS.UnitDigitsAfterDecimal());
|
|
||||||
SS.TW.ShowEditControl(55, 3, buf);
|
|
||||||
SS.TW.edit.meaning = EDIT_DIGITS_AFTER_DECIMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeExportScale(int link, DWORD v) {
|
|
||||||
char str[1024];
|
|
||||||
sprintf(str, "%.3f", (double)SS.exportScale);
|
|
||||||
|
|
||||||
SS.TW.ShowEditControl(61, 5, str);
|
|
||||||
SS.TW.edit.meaning = EDIT_EXPORT_SCALE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeExportOffset(int link, DWORD v) {
|
|
||||||
SS.TW.ShowEditControl(65, 3, SS.MmToString(SS.exportOffset));
|
|
||||||
SS.TW.edit.meaning = EDIT_EXPORT_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeFixExportColors(int link, DWORD v) {
|
|
||||||
SS.fixExportColors = !SS.fixExportColors;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeBackFaces(int link, DWORD v) {
|
|
||||||
SS.drawBackFaces = !SS.drawBackFaces;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeCheckClosedContour(int link, DWORD v) {
|
|
||||||
SS.checkClosedContour = !SS.checkClosedContour;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeShadedTriangles(int link, DWORD v) {
|
|
||||||
SS.exportShadedTriangles = !SS.exportShadedTriangles;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangePwlCurves(int link, DWORD v) {
|
|
||||||
SS.exportPwlCurves = !SS.exportPwlCurves;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeCanvasSizeAuto(int link, DWORD v) {
|
|
||||||
if(link == 't') {
|
|
||||||
SS.exportCanvasSizeAuto = true;
|
|
||||||
} else {
|
|
||||||
SS.exportCanvasSizeAuto = false;
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeCanvasSize(int link, DWORD v) {
|
|
||||||
double d;
|
|
||||||
switch(v) {
|
|
||||||
case 0: d = SS.exportMargin.left; break;
|
|
||||||
case 1: d = SS.exportMargin.right; break;
|
|
||||||
case 2: d = SS.exportMargin.bottom; break;
|
|
||||||
case 3: d = SS.exportMargin.top; break;
|
|
||||||
|
|
||||||
case 10: d = SS.exportCanvas.width; break;
|
|
||||||
case 11: d = SS.exportCanvas.height; break;
|
|
||||||
case 12: d = SS.exportCanvas.dx; break;
|
|
||||||
case 13: d = SS.exportCanvas.dy; break;
|
|
||||||
|
|
||||||
default: return;
|
|
||||||
}
|
|
||||||
int row = 81, col;
|
|
||||||
if(v < 10) {
|
|
||||||
row += v*2;
|
|
||||||
col = 11;
|
|
||||||
} else {
|
|
||||||
row += (v - 10)*2;
|
|
||||||
col = 13;
|
|
||||||
}
|
|
||||||
SS.TW.ShowEditControl(row, col, SS.MmToString(d));
|
|
||||||
SS.TW.edit.meaning = EDIT_CANVAS_SIZE;
|
|
||||||
SS.TW.edit.i = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ScreenChangeGCodeParameter(int link, DWORD v) {
|
|
||||||
char buf[1024] = "";
|
|
||||||
int row = 93;
|
|
||||||
switch(link) {
|
|
||||||
case 'd':
|
|
||||||
SS.TW.edit.meaning = EDIT_G_CODE_DEPTH;
|
|
||||||
strcpy(buf, SS.MmToString(SS.gCode.depth));
|
|
||||||
row += 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
SS.TW.edit.meaning = EDIT_G_CODE_PASSES;
|
|
||||||
sprintf(buf, "%d", SS.gCode.passes);
|
|
||||||
row += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'F':
|
|
||||||
SS.TW.edit.meaning = EDIT_G_CODE_FEED;
|
|
||||||
strcpy(buf, SS.MmToString(SS.gCode.feed));
|
|
||||||
row += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'P':
|
|
||||||
SS.TW.edit.meaning = EDIT_G_CODE_PLUNGE_FEED;
|
|
||||||
strcpy(buf, SS.MmToString(SS.gCode.plungeFeed));
|
|
||||||
row += 6;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SS.TW.ShowEditControl(row, 14, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::ShowConfiguration(void) {
|
|
||||||
int i;
|
|
||||||
Printf(true, "%Ft user color (r, g, b)");
|
|
||||||
|
|
||||||
for(i = 0; i < SS.MODEL_COLORS; i++) {
|
|
||||||
Printf(false, "%Bp #%d: %Bp %Bp (%@, %@, %@) %f%D%Ll%Fl[change]%E",
|
|
||||||
(i & 1) ? 'd' : 'a',
|
|
||||||
i, 0x80000000 | SS.modelColor[i],
|
|
||||||
(i & 1) ? 'd' : 'a',
|
|
||||||
REDf(SS.modelColor[i]),
|
|
||||||
GREENf(SS.modelColor[i]),
|
|
||||||
BLUEf(SS.modelColor[i]),
|
|
||||||
&ScreenChangeColor, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft light direction intensity");
|
|
||||||
for(i = 0; i < 2; i++) {
|
|
||||||
Printf(false, "%Bp #%d (%2,%2,%2)%Fl%D%f%Ll[c]%E "
|
|
||||||
"%2 %Fl%D%f%Ll[c]%E",
|
|
||||||
(i & 1) ? 'd' : 'a', i,
|
|
||||||
CO(SS.lightDir[i]), i, &ScreenChangeLightDirection,
|
|
||||||
SS.lightIntensity[i], i, &ScreenChangeLightIntensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft chord tolerance (in screen pixels)%E");
|
|
||||||
Printf(false, "%Ba %@ %Fl%Ll%f%D[change]%E; now %d triangles",
|
|
||||||
SS.chordTol,
|
|
||||||
&ScreenChangeChordTolerance, 0,
|
|
||||||
SK.GetGroup(SS.GW.activeGroup)->displayMesh.l.n);
|
|
||||||
Printf(false, "%Ft max piecewise linear segments%E");
|
|
||||||
Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
|
|
||||||
SS.maxSegments,
|
|
||||||
&ScreenChangeMaxSegments);
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft perspective factor (0 for parallel)%E");
|
|
||||||
Printf(false, "%Ba %# %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.cameraTangent*1000,
|
|
||||||
&ScreenChangeCameraTangent, 0);
|
|
||||||
Printf(false, "%Ft snap grid spacing%E");
|
|
||||||
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.gridSpacing),
|
|
||||||
&ScreenChangeGridSpacing, 0);
|
|
||||||
Printf(false, "%Ft digits after decimal point to show%E");
|
|
||||||
Printf(false, "%Ba %d %Fl%Ll%f%D[change]%E (e.g. '%s')",
|
|
||||||
SS.UnitDigitsAfterDecimal(),
|
|
||||||
&ScreenChangeDigitsAfterDecimal, 0,
|
|
||||||
SS.MmToString(SS.StringToMm("1.23456789")));
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft export scale factor (1:1=mm, 1:25.4=inch)");
|
|
||||||
Printf(false, "%Ba 1:%# %Fl%Ll%f%D[change]%E",
|
|
||||||
(double)SS.exportScale,
|
|
||||||
&ScreenChangeExportScale, 0);
|
|
||||||
Printf(false, "%Ft cutter radius offset (0=no offset) ");
|
|
||||||
Printf(false, "%Ba %s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportOffset),
|
|
||||||
&ScreenChangeExportOffset, 0);
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, " %Fd%f%Ll%c export shaded 2d triangles%E",
|
|
||||||
&ScreenChangeShadedTriangles,
|
|
||||||
SS.exportShadedTriangles ? CHECK_TRUE : CHECK_FALSE);
|
|
||||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
|
||||||
Printf(false, " %Fd%c curves as piecewise linear%E "
|
|
||||||
"(since cutter radius is not zero)", CHECK_TRUE);
|
|
||||||
} else {
|
|
||||||
Printf(false, " %Fd%f%Ll%c export curves as piecewise linear%E",
|
|
||||||
&ScreenChangePwlCurves,
|
|
||||||
SS.exportPwlCurves ? CHECK_TRUE : CHECK_FALSE);
|
|
||||||
}
|
|
||||||
Printf(false, " %Fd%f%Ll%c fix white exported lines%E",
|
|
||||||
&ScreenChangeFixExportColors,
|
|
||||||
SS.fixExportColors ? CHECK_TRUE : CHECK_FALSE);
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft export canvas size: "
|
|
||||||
"%f%Fd%Lf%c fixed%E "
|
|
||||||
"%f%Fd%Lt%c auto%E",
|
|
||||||
&ScreenChangeCanvasSizeAuto,
|
|
||||||
!SS.exportCanvasSizeAuto ? RADIO_TRUE : RADIO_FALSE,
|
|
||||||
&ScreenChangeCanvasSizeAuto,
|
|
||||||
SS.exportCanvasSizeAuto ? RADIO_TRUE : RADIO_FALSE);
|
|
||||||
|
|
||||||
if(SS.exportCanvasSizeAuto) {
|
|
||||||
Printf(false, "%Ft (by margins around exported geometry)");
|
|
||||||
Printf(false, "%Ba%Ft left: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportMargin.left), &ScreenChangeCanvasSize, 0);
|
|
||||||
Printf(false, "%Bd%Ft right: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportMargin.right), &ScreenChangeCanvasSize, 1);
|
|
||||||
Printf(false, "%Ba%Ft bottom: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportMargin.bottom), &ScreenChangeCanvasSize, 2);
|
|
||||||
Printf(false, "%Bd%Ft top: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportMargin.top), &ScreenChangeCanvasSize, 3);
|
|
||||||
} else {
|
|
||||||
Printf(false, "%Ft (by absolute dimensions and offsets)");
|
|
||||||
Printf(false, "%Ba%Ft width: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportCanvas.width), &ScreenChangeCanvasSize, 10);
|
|
||||||
Printf(false, "%Bd%Ft height: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportCanvas.height), &ScreenChangeCanvasSize, 11);
|
|
||||||
Printf(false, "%Ba%Ft offset x: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportCanvas.dx), &ScreenChangeCanvasSize, 12);
|
|
||||||
Printf(false, "%Bd%Ft offset y: %Fd%s %Fl%Ll%f%D[change]%E",
|
|
||||||
SS.MmToString(SS.exportCanvas.dy), &ScreenChangeCanvasSize, 13);
|
|
||||||
}
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, "%Ft exported g code parameters");
|
|
||||||
Printf(false, "%Ba%Ft depth: %Fd%s %Fl%Ld%f[change]%E",
|
|
||||||
SS.MmToString(SS.gCode.depth), &ScreenChangeGCodeParameter);
|
|
||||||
Printf(false, "%Bd%Ft passes: %Fd%d %Fl%Ls%f[change]%E",
|
|
||||||
SS.gCode.passes, &ScreenChangeGCodeParameter);
|
|
||||||
Printf(false, "%Ba%Ft feed: %Fd%s %Fl%LF%f[change]%E",
|
|
||||||
SS.MmToString(SS.gCode.feed), &ScreenChangeGCodeParameter);
|
|
||||||
Printf(false, "%Bd%Ft plunge fd: %Fd%s %Fl%LP%f[change]%E",
|
|
||||||
SS.MmToString(SS.gCode.plungeFeed), &ScreenChangeGCodeParameter);
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, " %Fd%f%Ll%c draw triangle back faces in red%E",
|
|
||||||
&ScreenChangeBackFaces,
|
|
||||||
SS.drawBackFaces ? CHECK_TRUE : CHECK_FALSE);
|
|
||||||
Printf(false, " %Fd%f%Ll%c check sketch for closed contour%E",
|
|
||||||
&ScreenChangeCheckClosedContour,
|
|
||||||
SS.checkClosedContour ? CHECK_TRUE : CHECK_FALSE);
|
|
||||||
|
|
||||||
Printf(false, "");
|
|
||||||
Printf(false, " %Ftgl vendor %E%s", glGetString(GL_VENDOR));
|
|
||||||
Printf(false, " %Ft renderer %E%s", glGetString(GL_RENDERER));
|
|
||||||
Printf(false, " %Ft version %E%s", glGetString(GL_VERSION));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextWindow::EditControlDoneForConfiguration(char *s) {
|
|
||||||
switch(edit.meaning) {
|
|
||||||
case EDIT_LIGHT_INTENSITY:
|
|
||||||
SS.lightIntensity[edit.i] = min(1, max(0, atof(s)));
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EDIT_LIGHT_DIRECTION: {
|
|
||||||
double x, y, z;
|
|
||||||
if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) {
|
|
||||||
SS.lightDir[edit.i] = Vector::From(x, y, z);
|
|
||||||
} else {
|
|
||||||
Error("Bad format: specify coordinates as x, y, z");
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_COLOR: {
|
|
||||||
Vector rgb;
|
|
||||||
if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
|
|
||||||
rgb = rgb.ClampWithin(0, 1);
|
|
||||||
SS.modelColor[edit.i] = RGBf(rgb.x, rgb.y, rgb.z);
|
|
||||||
} else {
|
|
||||||
Error("Bad format: specify color as r, g, b");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_CHORD_TOLERANCE: {
|
|
||||||
SS.chordTol = min(10, max(0.1, atof(s)));
|
|
||||||
SS.GenerateAll(0, INT_MAX);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_MAX_SEGMENTS: {
|
|
||||||
SS.maxSegments = min(1000, max(7, atoi(s)));
|
|
||||||
SS.GenerateAll(0, INT_MAX);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_CAMERA_TANGENT: {
|
|
||||||
SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0;
|
|
||||||
if(!SS.usePerspectiveProj) {
|
|
||||||
Message("The perspective factor will have no effect until you "
|
|
||||||
"enable View -> Use Perspective Projection.");
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_GRID_SPACING: {
|
|
||||||
SS.gridSpacing = (float)min(1e4, max(1e-3, SS.StringToMm(s)));
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_DIGITS_AFTER_DECIMAL: {
|
|
||||||
int v = atoi(s);
|
|
||||||
if(v < 0 || v > 8) {
|
|
||||||
Error("Specify between 0 and 8 digits after the decimal.");
|
|
||||||
} else {
|
|
||||||
SS.SetUnitDigitsAfterDecimal(v);
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_EXPORT_SCALE: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(e) {
|
|
||||||
double ev = e->Eval();
|
|
||||||
if(fabs(ev) < 0.001 || isnan(ev)) {
|
|
||||||
Error("Export scale must not be zero!");
|
|
||||||
} else {
|
|
||||||
SS.exportScale = (float)ev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_EXPORT_OFFSET: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(e) {
|
|
||||||
double ev = SS.ExprToMm(e);
|
|
||||||
if(isnan(ev) || ev < 0) {
|
|
||||||
Error("Cutter radius offset must not be negative!");
|
|
||||||
} else {
|
|
||||||
SS.exportOffset = (float)ev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_CANVAS_SIZE: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(!e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
float d = (float)SS.ExprToMm(e);
|
|
||||||
switch(edit.i) {
|
|
||||||
case 0: SS.exportMargin.left = d; break;
|
|
||||||
case 1: SS.exportMargin.right = d; break;
|
|
||||||
case 2: SS.exportMargin.bottom = d; break;
|
|
||||||
case 3: SS.exportMargin.top = d; break;
|
|
||||||
|
|
||||||
case 10: SS.exportCanvas.width = d; break;
|
|
||||||
case 11: SS.exportCanvas.height = d; break;
|
|
||||||
case 12: SS.exportCanvas.dx = d; break;
|
|
||||||
case 13: SS.exportCanvas.dy = d; break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_G_CODE_DEPTH: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(e) SS.gCode.depth = (float)SS.ExprToMm(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_G_CODE_PASSES: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(e) SS.gCode.passes = (int)(e->Eval());
|
|
||||||
SS.gCode.passes = max(1, min(1000, SS.gCode.passes));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_G_CODE_FEED: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(e) SS.gCode.feed = (float)SS.ExprToMm(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EDIT_G_CODE_PLUNGE_FEED: {
|
|
||||||
Expr *e = Expr::From(s, true);
|
|
||||||
if(e) SS.gCode.plungeFeed = (float)SS.ExprToMm(e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
820
draw.cpp
|
@ -1,820 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// The root function to paint our graphics window, after setting up all the
|
|
||||||
// views and such appropriately. Also contains all the stuff to manage the
|
|
||||||
// selection.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
bool GraphicsWindow::Selection::Equals(Selection *b) {
|
|
||||||
if(entity.v != b->entity.v) return false;
|
|
||||||
if(constraint.v != b->constraint.v) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GraphicsWindow::Selection::IsEmpty(void) {
|
|
||||||
if(entity.v) return false;
|
|
||||||
if(constraint.v) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GraphicsWindow::Selection::IsStylable(void) {
|
|
||||||
if(entity.v) return true;
|
|
||||||
if(constraint.v) {
|
|
||||||
Constraint *c = SK.GetConstraint(constraint);
|
|
||||||
if(c->type == Constraint::COMMENT) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GraphicsWindow::Selection::HasEndpoints(void) {
|
|
||||||
if(!entity.v) return false;
|
|
||||||
Entity *e = SK.GetEntity(entity);
|
|
||||||
return e->HasEndpoints();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::Selection::Clear(void) {
|
|
||||||
entity.v = constraint.v = 0;
|
|
||||||
emphasized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::Selection::Draw(void) {
|
|
||||||
Vector refp;
|
|
||||||
if(entity.v) {
|
|
||||||
Entity *e = SK.GetEntity(entity);
|
|
||||||
e->Draw();
|
|
||||||
if(emphasized) refp = e->GetReferencePos();
|
|
||||||
}
|
|
||||||
if(constraint.v) {
|
|
||||||
Constraint *c = SK.GetConstraint(constraint);
|
|
||||||
c->Draw();
|
|
||||||
if(emphasized) refp = c->GetReferencePos();
|
|
||||||
}
|
|
||||||
if(emphasized && (constraint.v || entity.v)) {
|
|
||||||
// We want to emphasize this constraint or entity, by drawing a thick
|
|
||||||
// line from the top left corner of the screen to the reference point
|
|
||||||
// of that entity or constraint.
|
|
||||||
double s = 0.501/SS.GW.scale;
|
|
||||||
Vector topLeft = SS.GW.projRight.ScaledBy(-SS.GW.width*s);
|
|
||||||
topLeft = topLeft.Plus(SS.GW.projUp.ScaledBy(SS.GW.height*s));
|
|
||||||
topLeft = topLeft.Minus(SS.GW.offset);
|
|
||||||
|
|
||||||
glLineWidth(40);
|
|
||||||
DWORD rgb = Style::Color(Style::HOVERED);
|
|
||||||
glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), 0.2);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(topLeft);
|
|
||||||
glxVertex3v(refp);
|
|
||||||
glEnd();
|
|
||||||
glLineWidth(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::ClearSelection(void) {
|
|
||||||
selection.Clear();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::ClearNonexistentSelectionItems(void) {
|
|
||||||
bool change = false;
|
|
||||||
Selection *s;
|
|
||||||
selection.ClearTags();
|
|
||||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
|
||||||
if(s->constraint.v && !(SK.constraint.FindByIdNoOops(s->constraint))) {
|
|
||||||
s->tag = 1;
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
if(s->entity.v && !(SK.entity.FindByIdNoOops(s->entity))) {
|
|
||||||
s->tag = 1;
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selection.RemoveTagged();
|
|
||||||
if(change) InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Is this entity/constraint selected?
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
bool GraphicsWindow::IsSelected(hEntity he) {
|
|
||||||
Selection s;
|
|
||||||
ZERO(&s);
|
|
||||||
s.entity = he;
|
|
||||||
return IsSelected(&s);
|
|
||||||
}
|
|
||||||
bool GraphicsWindow::IsSelected(Selection *st) {
|
|
||||||
Selection *s;
|
|
||||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
|
||||||
if(s->Equals(st)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Unselect an item, if it is selected. We can either unselect just that item,
|
|
||||||
// or also unselect any coincident points. The latter is useful if the user
|
|
||||||
// somehow selects two coincident points (like with select all), because it
|
|
||||||
// would otherwise be impossible to de-select the lower of the two.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void GraphicsWindow::MakeUnselected(hEntity he, bool coincidentPointTrick) {
|
|
||||||
Selection stog;
|
|
||||||
ZERO(&stog);
|
|
||||||
stog.entity = he;
|
|
||||||
MakeUnselected(&stog, coincidentPointTrick);
|
|
||||||
}
|
|
||||||
void GraphicsWindow::MakeUnselected(Selection *stog, bool coincidentPointTrick){
|
|
||||||
if(stog->IsEmpty()) return;
|
|
||||||
|
|
||||||
Selection *s;
|
|
||||||
|
|
||||||
// If an item was selected, then we just un-select it.
|
|
||||||
bool wasSelected = false;
|
|
||||||
selection.ClearTags();
|
|
||||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
|
||||||
if(s->Equals(stog)) {
|
|
||||||
s->tag = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If two points are coincident, then it's impossible to hover one of
|
|
||||||
// them. But make sure to deselect both, to avoid mysterious seeming
|
|
||||||
// inability to deselect if the bottom one did somehow get selected.
|
|
||||||
if(stog->entity.v && coincidentPointTrick) {
|
|
||||||
Entity *e = SK.GetEntity(stog->entity);
|
|
||||||
if(e->IsPoint()) {
|
|
||||||
Vector ep = e->PointGetNum();
|
|
||||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
|
||||||
if(!s->entity.v) continue;
|
|
||||||
if(s->entity.v == stog->entity.v) continue;
|
|
||||||
Entity *se = SK.GetEntity(s->entity);
|
|
||||||
if(!se->IsPoint()) continue;
|
|
||||||
if(ep.Equals(se->PointGetNum())) {
|
|
||||||
s->tag = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selection.RemoveTagged();
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Select an item, if it isn't selected already.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void GraphicsWindow::MakeSelected(hEntity he) {
|
|
||||||
Selection stog;
|
|
||||||
ZERO(&stog);
|
|
||||||
stog.entity = he;
|
|
||||||
MakeSelected(&stog);
|
|
||||||
}
|
|
||||||
void GraphicsWindow::MakeSelected(Selection *stog) {
|
|
||||||
if(stog->IsEmpty()) return;
|
|
||||||
if(IsSelected(stog)) return;
|
|
||||||
|
|
||||||
if(stog->entity.v != 0 && SK.GetEntity(stog->entity)->IsFace()) {
|
|
||||||
// In the interest of speed for the triangle drawing code,
|
|
||||||
// only two faces may be selected at a time.
|
|
||||||
int c = 0;
|
|
||||||
Selection *s;
|
|
||||||
selection.ClearTags();
|
|
||||||
for(s = selection.First(); s; s = selection.NextAfter(s)) {
|
|
||||||
hEntity he = s->entity;
|
|
||||||
if(he.v != 0 && SK.GetEntity(he)->IsFace()) {
|
|
||||||
c++;
|
|
||||||
if(c >= 2) s->tag = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selection.RemoveTagged();
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.Add(stog);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Select everything that lies within the marquee view-aligned rectangle. For
|
|
||||||
// points, we test by the point location. For normals, we test by the normal's
|
|
||||||
// associated point. For anything else, we test by any piecewise linear edge.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void GraphicsWindow::SelectByMarquee(void) {
|
|
||||||
Point2d begin = ProjectPoint(orig.marqueePoint);
|
|
||||||
double xmin = min(orig.mouse.x, begin.x),
|
|
||||||
xmax = max(orig.mouse.x, begin.x),
|
|
||||||
ymin = min(orig.mouse.y, begin.y),
|
|
||||||
ymax = max(orig.mouse.y, begin.y);
|
|
||||||
|
|
||||||
Entity *e;
|
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
|
||||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
|
||||||
if(e->IsFace() || e->IsDistance()) continue;
|
|
||||||
if(!e->IsVisible()) continue;
|
|
||||||
|
|
||||||
if(e->IsPoint() || e->IsNormal()) {
|
|
||||||
Vector p = e->IsPoint() ? e->PointGetNum() :
|
|
||||||
SK.GetEntity(e->point[0])->PointGetNum();
|
|
||||||
Point2d pp = ProjectPoint(p);
|
|
||||||
if(pp.x >= xmin && pp.x <= xmax &&
|
|
||||||
pp.y >= ymin && pp.y <= ymax)
|
|
||||||
{
|
|
||||||
MakeSelected(e->h);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Use the 3d bounding box test routines, to avoid duplication;
|
|
||||||
// so let our bounding square become a bounding box that certainly
|
|
||||||
// includes the z = 0 plane.
|
|
||||||
Vector ptMin = Vector::From(xmin, ymin, -1),
|
|
||||||
ptMax = Vector::From(xmax, ymax, 1);
|
|
||||||
SEdgeList sel;
|
|
||||||
ZERO(&sel);
|
|
||||||
e->GenerateEdges(&sel, true);
|
|
||||||
SEdge *se;
|
|
||||||
for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) {
|
|
||||||
Point2d ppa = ProjectPoint(se->a),
|
|
||||||
ppb = ProjectPoint(se->b);
|
|
||||||
Vector ptA = Vector::From(ppa.x, ppa.y, 0),
|
|
||||||
ptB = Vector::From(ppb.x, ppb.y, 0);
|
|
||||||
if(Vector::BoundingBoxIntersectsLine(ptMax, ptMin,
|
|
||||||
ptA, ptB, true) ||
|
|
||||||
!ptA.OutsideAndNotOn(ptMax, ptMin) ||
|
|
||||||
!ptB.OutsideAndNotOn(ptMax, ptMin))
|
|
||||||
{
|
|
||||||
MakeSelected(e->h);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sel.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Sort the selection according to various critieria: the entities and
|
|
||||||
// constraints separately, counts of certain types of entities (circles,
|
|
||||||
// lines, etc.), and so on.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void GraphicsWindow::GroupSelection(void) {
|
|
||||||
memset(&gs, 0, sizeof(gs));
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < selection.n && i < MAX_SELECTED; i++) {
|
|
||||||
Selection *s = &(selection.elem[i]);
|
|
||||||
if(s->entity.v) {
|
|
||||||
(gs.n)++;
|
|
||||||
|
|
||||||
Entity *e = SK.entity.FindById(s->entity);
|
|
||||||
// A list of points, and a list of all entities that aren't points.
|
|
||||||
if(e->IsPoint()) {
|
|
||||||
gs.point[(gs.points)++] = s->entity;
|
|
||||||
} else {
|
|
||||||
gs.entity[(gs.entities)++] = s->entity;
|
|
||||||
(gs.stylables)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// And an auxiliary list of normals, including normals from
|
|
||||||
// workplanes.
|
|
||||||
if(e->IsNormal()) {
|
|
||||||
gs.anyNormal[(gs.anyNormals)++] = s->entity;
|
|
||||||
} else if(e->IsWorkplane()) {
|
|
||||||
gs.anyNormal[(gs.anyNormals)++] = e->Normal()->h;
|
|
||||||
}
|
|
||||||
|
|
||||||
// And of vectors (i.e., stuff with a direction to constrain)
|
|
||||||
if(e->HasVector()) {
|
|
||||||
gs.vector[(gs.vectors)++] = s->entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Faces (which are special, associated/drawn with triangles)
|
|
||||||
if(e->IsFace()) {
|
|
||||||
gs.face[(gs.faces)++] = s->entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(e->HasEndpoints()) {
|
|
||||||
(gs.withEndpoints)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// And some aux counts too
|
|
||||||
switch(e->type) {
|
|
||||||
case Entity::WORKPLANE: (gs.workplanes)++; break;
|
|
||||||
case Entity::LINE_SEGMENT: (gs.lineSegments)++; break;
|
|
||||||
case Entity::CUBIC: (gs.cubics)++; break;
|
|
||||||
case Entity::CUBIC_PERIODIC: (gs.periodicCubics)++; break;
|
|
||||||
|
|
||||||
case Entity::ARC_OF_CIRCLE:
|
|
||||||
(gs.circlesOrArcs)++;
|
|
||||||
(gs.arcs)++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Entity::CIRCLE: (gs.circlesOrArcs)++; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(s->constraint.v) {
|
|
||||||
gs.constraint[(gs.constraints)++] = s->constraint;
|
|
||||||
Constraint *c = SK.GetConstraint(s->constraint);
|
|
||||||
if(c->type == Constraint::COMMENT) {
|
|
||||||
(gs.stylables)++;
|
|
||||||
(gs.comments)++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
|
|
||||||
int i;
|
|
||||||
double d, dmin = 1e12;
|
|
||||||
Selection s;
|
|
||||||
ZERO(&s);
|
|
||||||
|
|
||||||
// Always do the entities; we might be dragging something that should
|
|
||||||
// be auto-constrained, and we need the hover for that.
|
|
||||||
for(i = 0; i < SK.entity.n; i++) {
|
|
||||||
Entity *e = &(SK.entity.elem[i]);
|
|
||||||
// Don't hover whatever's being dragged.
|
|
||||||
if(e->h.request().v == pending.point.request().v) {
|
|
||||||
// The one exception is when we're creating a new cubic; we
|
|
||||||
// want to be able to hover the first point, because that's
|
|
||||||
// how we turn it into a periodic spline.
|
|
||||||
if(!e->IsPoint()) continue;
|
|
||||||
if(!e->h.isFromRequest()) continue;
|
|
||||||
Request *r = SK.GetRequest(e->h.request());
|
|
||||||
if(r->type != Request::CUBIC) continue;
|
|
||||||
if(r->extraPoints < 2) continue;
|
|
||||||
if(e->h.v != r->h.entity(1).v) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
d = e->GetDistance(mp);
|
|
||||||
if(d < 10 && d < dmin) {
|
|
||||||
memset(&s, 0, sizeof(s));
|
|
||||||
s.entity = e->h;
|
|
||||||
dmin = d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The constraints and faces happen only when nothing's in progress.
|
|
||||||
if(pending.operation == 0) {
|
|
||||||
// Constraints
|
|
||||||
for(i = 0; i < SK.constraint.n; i++) {
|
|
||||||
d = SK.constraint.elem[i].GetDistance(mp);
|
|
||||||
if(d < 10 && d < dmin) {
|
|
||||||
memset(&s, 0, sizeof(s));
|
|
||||||
s.constraint = SK.constraint.elem[i].h;
|
|
||||||
dmin = d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Faces, from the triangle mesh; these are lowest priority
|
|
||||||
if(s.constraint.v == 0 && s.entity.v == 0 && showShaded && showFaces) {
|
|
||||||
Group *g = SK.GetGroup(activeGroup);
|
|
||||||
SMesh *m = &(g->displayMesh);
|
|
||||||
|
|
||||||
DWORD v = m->FirstIntersectionWith(mp);
|
|
||||||
if(v) {
|
|
||||||
s.entity.v = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!s.Equals(&hover)) {
|
|
||||||
hover = s;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Project a point in model space to screen space, exactly as gl would; return
|
|
||||||
// units are pixels.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
Point2d GraphicsWindow::ProjectPoint(Vector p) {
|
|
||||||
Vector p3 = ProjectPoint3(p);
|
|
||||||
Point2d p2 = { p3.x, p3.y };
|
|
||||||
return p2;
|
|
||||||
}
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Project a point in model space to screen space, exactly as gl would; return
|
|
||||||
// units are pixels. The z coordinate is also returned, also in pixels.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
Vector GraphicsWindow::ProjectPoint3(Vector p) {
|
|
||||||
double w;
|
|
||||||
Vector r = ProjectPoint4(p, &w);
|
|
||||||
return r.ScaledBy(scale/w);
|
|
||||||
}
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Project a point in model space halfway into screen space. The scale is
|
|
||||||
// not applied, and the perspective divide isn't applied; instead the w
|
|
||||||
// coordinate is returned separately.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
Vector GraphicsWindow::ProjectPoint4(Vector p, double *w) {
|
|
||||||
p = p.Plus(offset);
|
|
||||||
|
|
||||||
Vector r;
|
|
||||||
r.x = p.Dot(projRight);
|
|
||||||
r.y = p.Dot(projUp);
|
|
||||||
r.z = p.Dot(projUp.Cross(projRight));
|
|
||||||
|
|
||||||
*w = 1 + r.z*SS.CameraTangent()*scale;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Return a point in the plane parallel to the screen and through the offset,
|
|
||||||
// that projects onto the specified (x, y) coordinates.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
Vector GraphicsWindow::UnProjectPoint(Point2d p) {
|
|
||||||
Vector orig = offset.ScaledBy(-1);
|
|
||||||
|
|
||||||
// Note that we ignoring the effects of perspective. Since our returned
|
|
||||||
// point has the same component normal to the screen as the offset, it
|
|
||||||
// will have z = 0 after the rotation is applied, thus w = 1. So this is
|
|
||||||
// correct.
|
|
||||||
orig = orig.Plus(projRight.ScaledBy(p.x / scale)).Plus(
|
|
||||||
projUp. ScaledBy(p.y / scale));
|
|
||||||
return orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::NormalizeProjectionVectors(void) {
|
|
||||||
if(projRight.Magnitude() < LENGTH_EPS) {
|
|
||||||
projRight = Vector::From(1, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector norm = projRight.Cross(projUp);
|
|
||||||
// If projRight and projUp somehow ended up parallel, then pick an
|
|
||||||
// arbitrary projUp normal to projRight.
|
|
||||||
if(norm.Magnitude() < LENGTH_EPS) {
|
|
||||||
norm = projRight.Normal(0);
|
|
||||||
}
|
|
||||||
projUp = norm.Cross(projRight);
|
|
||||||
|
|
||||||
projUp = projUp.WithMagnitude(1);
|
|
||||||
projRight = projRight.WithMagnitude(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
|
|
||||||
Vector n = projRight.Cross(projUp);
|
|
||||||
|
|
||||||
Vector r = (projRight.ScaledBy(rightUpForward.x));
|
|
||||||
r = r.Plus(projUp.ScaledBy(rightUpForward.y));
|
|
||||||
r = r.Plus(n.ScaledBy(rightUpForward.z));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::Paint(void) {
|
|
||||||
int i;
|
|
||||||
havePainted = true;
|
|
||||||
|
|
||||||
int w, h;
|
|
||||||
GetGraphicsWindowSize(&w, &h);
|
|
||||||
width = w; height = h;
|
|
||||||
glViewport(0, 0, w, h);
|
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000);
|
|
||||||
|
|
||||||
double mat[16];
|
|
||||||
// Last thing before display is to apply the perspective
|
|
||||||
double clp = SS.CameraTangent()*scale;
|
|
||||||
MakeMatrix(mat, 1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, clp, 1);
|
|
||||||
glMultMatrixd(mat);
|
|
||||||
// Before that, we apply the rotation
|
|
||||||
Vector n = projUp.Cross(projRight);
|
|
||||||
MakeMatrix(mat, projRight.x, projRight.y, projRight.z, 0,
|
|
||||||
projUp.x, projUp.y, projUp.z, 0,
|
|
||||||
n.x, n.y, n.z, 0,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
glMultMatrixd(mat);
|
|
||||||
// And before that, the translation
|
|
||||||
MakeMatrix(mat, 1, 0, 0, offset.x,
|
|
||||||
0, 1, 0, offset.y,
|
|
||||||
0, 0, 1, offset.z,
|
|
||||||
0, 0, 0, 1);
|
|
||||||
glMultMatrixd(mat);
|
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
glLoadIdentity();
|
|
||||||
|
|
||||||
glShadeModel(GL_SMOOTH);
|
|
||||||
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
glEnable(GL_BLEND);
|
|
||||||
glEnable(GL_LINE_SMOOTH);
|
|
||||||
// don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards,
|
|
||||||
// drawn with leaks in the mesh
|
|
||||||
glEnable(GL_POLYGON_OFFSET_LINE);
|
|
||||||
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
|
||||||
glEnable(GL_NORMALIZE);
|
|
||||||
|
|
||||||
// At the same depth, we want later lines drawn over earlier.
|
|
||||||
glDepthFunc(GL_LEQUAL);
|
|
||||||
|
|
||||||
if(SS.AllGroupsOkay()) {
|
|
||||||
glClearColor(REDf(SS.backgroundColor),
|
|
||||||
GREENf(SS.backgroundColor),
|
|
||||||
BLUEf(SS.backgroundColor), 1.0f);
|
|
||||||
} else {
|
|
||||||
// Draw a different background whenever we're having solve problems.
|
|
||||||
DWORD rgb = Style::Color(Style::DRAW_ERROR);
|
|
||||||
glClearColor(0.4f*REDf(rgb), 0.4f*GREENf(rgb), 0.4f*BLUEf(rgb), 1.0f);
|
|
||||||
// And show the text window, which has info to debug it
|
|
||||||
ForceTextWindowShown();
|
|
||||||
}
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glClearDepth(1.0);
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
if(SS.bgImage.fromFile) {
|
|
||||||
// If a background image is loaded, then we draw it now as a texture.
|
|
||||||
// This handles the resizing for us nicely.
|
|
||||||
glBindTexture(GL_TEXTURE_2D, TEXTURE_BACKGROUND_IMG);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
|
|
||||||
SS.bgImage.rw, SS.bgImage.rh,
|
|
||||||
0,
|
|
||||||
GL_RGB, GL_UNSIGNED_BYTE,
|
|
||||||
SS.bgImage.fromFile);
|
|
||||||
|
|
||||||
double tw = ((double)SS.bgImage.w) / SS.bgImage.rw,
|
|
||||||
th = ((double)SS.bgImage.h) / SS.bgImage.rh;
|
|
||||||
|
|
||||||
double mmw = SS.bgImage.w / SS.bgImage.scale,
|
|
||||||
mmh = SS.bgImage.h / SS.bgImage.scale;
|
|
||||||
|
|
||||||
Vector origin = SS.bgImage.origin;
|
|
||||||
origin = origin.DotInToCsys(projRight, projUp, n);
|
|
||||||
// Place the depth of our origin at the point that corresponds to
|
|
||||||
// w = 1, so that it's unaffected by perspective.
|
|
||||||
origin.z = (offset.ScaledBy(-1)).Dot(n);
|
|
||||||
origin = origin.ScaleOutOfCsys(projRight, projUp, n);
|
|
||||||
|
|
||||||
// Place the background at the very back of the Z order, though, by
|
|
||||||
// mucking with the depth range.
|
|
||||||
glDepthRange(1, 1);
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glTexCoord2d(0, 0);
|
|
||||||
glxVertex3v(origin);
|
|
||||||
|
|
||||||
glTexCoord2d(0, th);
|
|
||||||
glxVertex3v(origin.Plus(projUp.ScaledBy(mmh)));
|
|
||||||
|
|
||||||
glTexCoord2d(tw, th);
|
|
||||||
glxVertex3v(origin.Plus(projRight.ScaledBy(mmw).Plus(
|
|
||||||
projUp. ScaledBy(mmh))));
|
|
||||||
|
|
||||||
glTexCoord2d(tw, 0);
|
|
||||||
glxVertex3v(origin.Plus(projRight.ScaledBy(mmw)));
|
|
||||||
glEnd();
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
}
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
|
|
||||||
// Nasty case when we're reloading the imported files; could be that
|
|
||||||
// we get an error, so a dialog pops up, and a message loop starts, and
|
|
||||||
// we have to get called to paint ourselves. If the sketch is screwed
|
|
||||||
// up, then we could trigger an oops trying to draw.
|
|
||||||
if(!SS.allConsistent) return;
|
|
||||||
|
|
||||||
// Let's use two lights, at the user-specified locations
|
|
||||||
GLfloat f;
|
|
||||||
glEnable(GL_LIGHT0);
|
|
||||||
f = (GLfloat)SS.lightIntensity[0];
|
|
||||||
GLfloat li0[] = { f, f, f, 1.0f };
|
|
||||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, li0);
|
|
||||||
glLightfv(GL_LIGHT0, GL_SPECULAR, li0);
|
|
||||||
|
|
||||||
glEnable(GL_LIGHT1);
|
|
||||||
f = (GLfloat)SS.lightIntensity[1];
|
|
||||||
GLfloat li1[] = { f, f, f, 1.0f };
|
|
||||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, li1);
|
|
||||||
glLightfv(GL_LIGHT1, GL_SPECULAR, li1);
|
|
||||||
|
|
||||||
Vector ld;
|
|
||||||
ld = VectorFromProjs(SS.lightDir[0]);
|
|
||||||
GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
|
|
||||||
glLightfv(GL_LIGHT0, GL_POSITION, ld0);
|
|
||||||
ld = VectorFromProjs(SS.lightDir[1]);
|
|
||||||
GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
|
|
||||||
glLightfv(GL_LIGHT1, GL_POSITION, ld1);
|
|
||||||
|
|
||||||
if(SS.drawBackFaces) {
|
|
||||||
// For debugging, draw the backs of the triangles in red, so that we
|
|
||||||
// notice when a shell is open
|
|
||||||
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1);
|
|
||||||
} else {
|
|
||||||
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLfloat ambient[4] = { (float)SS.ambientIntensity,
|
|
||||||
(float)SS.ambientIntensity,
|
|
||||||
(float)SS.ambientIntensity, 1 };
|
|
||||||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
|
|
||||||
|
|
||||||
glxUnlockColor();
|
|
||||||
|
|
||||||
if(showSnapGrid && LockedInWorkplane()) {
|
|
||||||
hEntity he = ActiveWorkplane();
|
|
||||||
EntityBase *wrkpl = SK.GetEntity(he),
|
|
||||||
*norm = wrkpl->Normal();
|
|
||||||
Vector wu, wv, wn, wp;
|
|
||||||
wp = SK.GetEntity(wrkpl->point[0])->PointGetNum();
|
|
||||||
wu = norm->NormalU();
|
|
||||||
wv = norm->NormalV();
|
|
||||||
wn = norm->NormalN();
|
|
||||||
|
|
||||||
double g = SS.gridSpacing;
|
|
||||||
|
|
||||||
double umin = VERY_POSITIVE, umax = VERY_NEGATIVE,
|
|
||||||
vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE;
|
|
||||||
int a;
|
|
||||||
for(a = 0; a < 4; a++) {
|
|
||||||
// Ideally, we would just do +/- half the width and height; but
|
|
||||||
// allow some extra slop for rounding.
|
|
||||||
Vector horiz = projRight.ScaledBy((0.6*width)/scale + 2*g),
|
|
||||||
vert = projUp. ScaledBy((0.6*height)/scale + 2*g);
|
|
||||||
if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1);
|
|
||||||
if(a == 1 || a == 3) vert = vert. ScaledBy(-1);
|
|
||||||
Vector tp = horiz.Plus(vert).Minus(offset);
|
|
||||||
|
|
||||||
// Project the point into our grid plane, normal to the screen
|
|
||||||
// (not to the grid plane). If the plane is on edge then this is
|
|
||||||
// impossible so don't try to draw the grid.
|
|
||||||
bool parallel;
|
|
||||||
Vector tpp = Vector::AtIntersectionOfPlaneAndLine(
|
|
||||||
wn, wn.Dot(wp),
|
|
||||||
tp, tp.Plus(n),
|
|
||||||
¶llel);
|
|
||||||
if(parallel) goto nogrid;
|
|
||||||
|
|
||||||
tpp = tpp.Minus(wp);
|
|
||||||
double uu = tpp.Dot(wu),
|
|
||||||
vv = tpp.Dot(wv);
|
|
||||||
|
|
||||||
umin = min(uu, umin);
|
|
||||||
umax = max(uu, umax);
|
|
||||||
vmin = min(vv, vmin);
|
|
||||||
vmax = max(vv, vmax);
|
|
||||||
}
|
|
||||||
|
|
||||||
int i, j, i0, i1, j0, j1;
|
|
||||||
|
|
||||||
i0 = (int)(umin / g);
|
|
||||||
i1 = (int)(umax / g);
|
|
||||||
j0 = (int)(vmin / g);
|
|
||||||
j1 = (int)(vmax / g);
|
|
||||||
|
|
||||||
if(i0 > i1 || i1 - i0 > 400) goto nogrid;
|
|
||||||
if(j0 > j1 || j1 - j0 > 400) goto nogrid;
|
|
||||||
|
|
||||||
glLineWidth(1);
|
|
||||||
glxColorRGBa(Style::Color(Style::DATUM), 0.3);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
for(i = i0 + 1; i < i1; i++) {
|
|
||||||
glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g)));
|
|
||||||
glxVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g)));
|
|
||||||
}
|
|
||||||
for(j = j0 + 1; j < j1; j++) {
|
|
||||||
glxVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g)));
|
|
||||||
glxVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g)));
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
// Clear the depth buffer, so that the grid is at the very back of
|
|
||||||
// the Z order.
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
|
||||||
nogrid:;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the active group; this does stuff like the mesh and edges.
|
|
||||||
(SK.GetGroup(activeGroup))->Draw();
|
|
||||||
|
|
||||||
// Now draw the entities
|
|
||||||
if(showHdnLines) glDisable(GL_DEPTH_TEST);
|
|
||||||
Entity::DrawAll();
|
|
||||||
|
|
||||||
// Draw filled paths in all groups, when those filled paths were requested
|
|
||||||
// specially by assigning a style with a fill color, or when the filled
|
|
||||||
// paths are just being filled by default. This should go last, to make
|
|
||||||
// the transparency work.
|
|
||||||
Group *g;
|
|
||||||
for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) {
|
|
||||||
if(!(g->IsVisible())) continue;
|
|
||||||
g->DrawFilledPaths();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
// Draw the constraints
|
|
||||||
for(i = 0; i < SK.constraint.n; i++) {
|
|
||||||
SK.constraint.elem[i].Draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the traced path, if one exists
|
|
||||||
glLineWidth(Style::Width(Style::ANALYZE));
|
|
||||||
glxColorRGB(Style::Color(Style::ANALYZE));
|
|
||||||
SContour *sc = &(SS.traced.path);
|
|
||||||
glBegin(GL_LINE_STRIP);
|
|
||||||
for(i = 0; i < sc->l.n; i++) {
|
|
||||||
glxVertex3v(sc->l.elem[i].p);
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
// And the naked edges, if the user did Analyze -> Show Naked Edges.
|
|
||||||
glLineWidth(Style::Width(Style::DRAW_ERROR));
|
|
||||||
glxColorRGB(Style::Color(Style::DRAW_ERROR));
|
|
||||||
glxDrawEdges(&(SS.nakedEdges), true);
|
|
||||||
|
|
||||||
// Then redraw whatever the mouse is hovering over, highlighted.
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
glxLockColorTo(Style::Color(Style::HOVERED));
|
|
||||||
hover.Draw();
|
|
||||||
|
|
||||||
// And finally draw the selection, same mechanism.
|
|
||||||
glxLockColorTo(Style::Color(Style::SELECTED));
|
|
||||||
for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) {
|
|
||||||
s->Draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
glxUnlockColor();
|
|
||||||
|
|
||||||
// If a marquee selection is in progress, then draw the selection
|
|
||||||
// rectangle, as an outline and a transparent fill.
|
|
||||||
if(pending.operation == DRAGGING_MARQUEE) {
|
|
||||||
Point2d begin = ProjectPoint(orig.marqueePoint);
|
|
||||||
double xmin = min(orig.mouse.x, begin.x),
|
|
||||||
xmax = max(orig.mouse.x, begin.x),
|
|
||||||
ymin = min(orig.mouse.y, begin.y),
|
|
||||||
ymax = max(orig.mouse.y, begin.y);
|
|
||||||
|
|
||||||
Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)),
|
|
||||||
tr = UnProjectPoint(Point2d::From(xmax, ymin)),
|
|
||||||
br = UnProjectPoint(Point2d::From(xmax, ymax)),
|
|
||||||
bl = UnProjectPoint(Point2d::From(xmin, ymax));
|
|
||||||
|
|
||||||
glLineWidth((GLfloat)1.3);
|
|
||||||
glxColorRGB(Style::Color(Style::HOVERED));
|
|
||||||
glBegin(GL_LINE_LOOP);
|
|
||||||
glxVertex3v(tl);
|
|
||||||
glxVertex3v(tr);
|
|
||||||
glxVertex3v(br);
|
|
||||||
glxVertex3v(bl);
|
|
||||||
glEnd();
|
|
||||||
glxColorRGBa(Style::Color(Style::HOVERED), 0.10);
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glxVertex3v(tl);
|
|
||||||
glxVertex3v(tr);
|
|
||||||
glxVertex3v(br);
|
|
||||||
glxVertex3v(bl);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
// An extra line, used to indicate the origin when rotating within the
|
|
||||||
// plane of the monitor.
|
|
||||||
if(SS.extraLine.draw) {
|
|
||||||
glLineWidth(1);
|
|
||||||
glxLockColorTo(Style::Color(Style::DATUM));
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(SS.extraLine.ptA);
|
|
||||||
glxVertex3v(SS.extraLine.ptB);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
// A note to indicate the origin in the just-exported file.
|
|
||||||
if(SS.justExportedInfo.draw) {
|
|
||||||
glxColorRGB(Style::Color(Style::DATUM));
|
|
||||||
Vector p = SS.justExportedInfo.pt,
|
|
||||||
u = SS.justExportedInfo.u,
|
|
||||||
v = SS.justExportedInfo.v;
|
|
||||||
|
|
||||||
glLineWidth(1.5);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(p.Plus(u.WithMagnitude(-15/scale)));
|
|
||||||
glxVertex3v(p.Plus(u.WithMagnitude(30/scale)));
|
|
||||||
glxVertex3v(p.Plus(v.WithMagnitude(-15/scale)));
|
|
||||||
glxVertex3v(p.Plus(v.WithMagnitude(30/scale)));
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
glxWriteText("(x, y) = (0, 0) for file just exported",
|
|
||||||
DEFAULT_TEXT_HEIGHT,
|
|
||||||
p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)),
|
|
||||||
u, v, NULL, NULL);
|
|
||||||
glxWriteText("press Esc to clear this message",
|
|
||||||
DEFAULT_TEXT_HEIGHT,
|
|
||||||
p.Plus(u.ScaledBy(40/scale)).Plus(
|
|
||||||
v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)),
|
|
||||||
u, v, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// And finally the toolbar.
|
|
||||||
if(SS.showToolbar) {
|
|
||||||
ToolbarDraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
1101
drawconstraint.cpp
648
drawentity.cpp
|
@ -1,648 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Draw a representation of an entity on-screen, in the case of curves up
|
|
||||||
// to our chord tolerance, or return the distance from the user's mouse pointer
|
|
||||||
// to the entity for selection.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
char *Entity::DescriptionString(void) {
|
|
||||||
if(h.isFromRequest()) {
|
|
||||||
Request *r = SK.GetRequest(h.request());
|
|
||||||
return r->DescriptionString();
|
|
||||||
} else {
|
|
||||||
Group *g = SK.GetGroup(h.group());
|
|
||||||
return g->DescriptionString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) {
|
|
||||||
if(dogd.drawing) {
|
|
||||||
// Draw lines from active group in front of those from previous
|
|
||||||
glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3);
|
|
||||||
// Narrow lines are drawn as lines, but fat lines must be drawn as
|
|
||||||
// filled polygons, to get the line join style right.
|
|
||||||
if(!maybeFat || dogd.lineWidth < 3) {
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(a);
|
|
||||||
glxVertex3v(b);
|
|
||||||
glEnd();
|
|
||||||
} else {
|
|
||||||
glxFatLine(a, b, dogd.lineWidth/SS.GW.scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
} else {
|
|
||||||
Point2d ap = SS.GW.ProjectPoint(a);
|
|
||||||
Point2d bp = SS.GW.ProjectPoint(b);
|
|
||||||
|
|
||||||
double d = dogd.mp.DistanceToLine(ap, bp.Minus(ap), true);
|
|
||||||
// A little bit easier to select in the active group
|
|
||||||
if(group.v == SS.GW.activeGroup.v) d -= 1;
|
|
||||||
dogd.dmin = min(dogd.dmin, d);
|
|
||||||
}
|
|
||||||
dogd.refp = (a.Plus(b)).ScaledBy(0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::DrawAll(void) {
|
|
||||||
// This handles points and line segments as a special case, because I
|
|
||||||
// seem to be able to get a huge speedup that way, by consolidating
|
|
||||||
// stuff to gl.
|
|
||||||
int i;
|
|
||||||
if(SS.GW.showPoints) {
|
|
||||||
double s = 3.5/SS.GW.scale;
|
|
||||||
Vector r = SS.GW.projRight.ScaledBy(s);
|
|
||||||
Vector d = SS.GW.projUp.ScaledBy(s);
|
|
||||||
glxColorRGB(Style::Color(Style::DATUM));
|
|
||||||
glxDepthRangeOffset(6);
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
for(i = 0; i < SK.entity.n; i++) {
|
|
||||||
Entity *e = &(SK.entity.elem[i]);
|
|
||||||
if(!e->IsPoint()) continue;
|
|
||||||
if(!(SK.GetGroup(e->group)->IsVisible())) continue;
|
|
||||||
if(e->forceHidden) continue;
|
|
||||||
|
|
||||||
Vector v = e->PointGetNum();
|
|
||||||
|
|
||||||
// If we're analyzing the sketch to show the degrees of freedom,
|
|
||||||
// then we draw big colored squares over the points that are
|
|
||||||
// free to move.
|
|
||||||
bool free = false;
|
|
||||||
if(e->type == POINT_IN_3D) {
|
|
||||||
Param *px = SK.GetParam(e->param[0]),
|
|
||||||
*py = SK.GetParam(e->param[1]),
|
|
||||||
*pz = SK.GetParam(e->param[2]);
|
|
||||||
|
|
||||||
free = (px->free) || (py->free) || (pz->free);
|
|
||||||
} else if(e->type == POINT_IN_2D) {
|
|
||||||
Param *pu = SK.GetParam(e->param[0]),
|
|
||||||
*pv = SK.GetParam(e->param[1]);
|
|
||||||
|
|
||||||
free = (pu->free) || (pv->free);
|
|
||||||
}
|
|
||||||
if(free) {
|
|
||||||
Vector re = r.ScaledBy(2.5), de = d.ScaledBy(2.5);
|
|
||||||
|
|
||||||
glxColorRGB(Style::Color(Style::ANALYZE));
|
|
||||||
glxVertex3v(v.Plus (re).Plus (de));
|
|
||||||
glxVertex3v(v.Plus (re).Minus(de));
|
|
||||||
glxVertex3v(v.Minus(re).Minus(de));
|
|
||||||
glxVertex3v(v.Minus(re).Plus (de));
|
|
||||||
glxColorRGB(Style::Color(Style::DATUM));
|
|
||||||
}
|
|
||||||
|
|
||||||
glxVertex3v(v.Plus (r).Plus (d));
|
|
||||||
glxVertex3v(v.Plus (r).Minus(d));
|
|
||||||
glxVertex3v(v.Minus(r).Minus(d));
|
|
||||||
glxVertex3v(v.Minus(r).Plus (d));
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < SK.entity.n; i++) {
|
|
||||||
Entity *e = &(SK.entity.elem[i]);
|
|
||||||
if(e->IsPoint())
|
|
||||||
{
|
|
||||||
continue; // already handled
|
|
||||||
}
|
|
||||||
e->Draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::Draw(void) {
|
|
||||||
hStyle hs = Style::ForEntity(h);
|
|
||||||
dogd.lineWidth = Style::Width(hs);
|
|
||||||
glLineWidth((float)dogd.lineWidth);
|
|
||||||
glxColorRGB(Style::Color(hs));
|
|
||||||
|
|
||||||
dogd.drawing = true;
|
|
||||||
DrawOrGetDistance();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::GenerateEdges(SEdgeList *el, bool includingConstruction) {
|
|
||||||
if(construction && !includingConstruction) return;
|
|
||||||
|
|
||||||
SBezierList sbl;
|
|
||||||
ZERO(&sbl);
|
|
||||||
GenerateBezierCurves(&sbl);
|
|
||||||
|
|
||||||
int i, j;
|
|
||||||
for(i = 0; i < sbl.l.n; i++) {
|
|
||||||
SBezier *sb = &(sbl.l.elem[i]);
|
|
||||||
|
|
||||||
List<Vector> lv;
|
|
||||||
ZERO(&lv);
|
|
||||||
sb->MakePwlInto(&lv);
|
|
||||||
for(j = 1; j < lv.n; j++) {
|
|
||||||
el->AddEdge(lv.elem[j-1], lv.elem[j], style.v);
|
|
||||||
}
|
|
||||||
lv.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
sbl.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
double Entity::GetDistance(Point2d mp) {
|
|
||||||
dogd.drawing = false;
|
|
||||||
dogd.mp = mp;
|
|
||||||
dogd.dmin = 1e12;
|
|
||||||
|
|
||||||
DrawOrGetDistance();
|
|
||||||
|
|
||||||
return dogd.dmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector Entity::GetReferencePos(void) {
|
|
||||||
dogd.drawing = false;
|
|
||||||
|
|
||||||
dogd.refp = SS.GW.offset.ScaledBy(-1);
|
|
||||||
DrawOrGetDistance();
|
|
||||||
|
|
||||||
return dogd.refp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entity::IsVisible(void) {
|
|
||||||
Group *g = SK.GetGroup(group);
|
|
||||||
|
|
||||||
if(g->h.v == Group::HGROUP_REFERENCES.v && IsNormal()) {
|
|
||||||
// The reference normals are always shown
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(!(g->IsVisible())) return false;
|
|
||||||
|
|
||||||
// Don't check if points are hidden; this gets called only for
|
|
||||||
// selected or hovered points, and those should always be shown.
|
|
||||||
if(IsNormal() && !SS.GW.showNormals) return false;
|
|
||||||
|
|
||||||
if(!SS.GW.showWorkplanes) {
|
|
||||||
if(IsWorkplane() && !h.isFromRequest()) {
|
|
||||||
if(g->h.v != SS.GW.activeGroup.v) {
|
|
||||||
// The group-associated workplanes are hidden outside
|
|
||||||
// their group.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(style.v) {
|
|
||||||
Style *s = Style::Get(style);
|
|
||||||
if(!s->visible) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(forceHidden) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::CalculateNumerical(bool forExport) {
|
|
||||||
if(IsPoint()) actPoint = PointGetNum();
|
|
||||||
if(IsNormal()) actNormal = NormalGetNum();
|
|
||||||
if(type == DISTANCE || type == DISTANCE_N_COPY) {
|
|
||||||
actDistance = DistanceGetNum();
|
|
||||||
}
|
|
||||||
if(IsFace()) {
|
|
||||||
actPoint = FaceGetPointNum();
|
|
||||||
Vector n = FaceGetNormalNum();
|
|
||||||
actNormal = Quaternion::From(0, n.x, n.y, n.z);
|
|
||||||
}
|
|
||||||
if(forExport) {
|
|
||||||
// Visibility in copied import entities follows source file
|
|
||||||
actVisible = IsVisible();
|
|
||||||
} else {
|
|
||||||
// Copied entities within a file are always visible
|
|
||||||
actVisible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Entity::PointIsFromReferences(void) {
|
|
||||||
return h.request().IsFromReferences();
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Compute a cubic, second derivative continuous, interpolating spline. Same
|
|
||||||
// routine for periodic splines (in a loop) or open splines (with specified
|
|
||||||
// end tangents).
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void Entity::ComputeInterpolatingSpline(SBezierList *sbl, bool periodic) {
|
|
||||||
static const int MAX_N = BandedMatrix::MAX_UNKNOWNS;
|
|
||||||
int ep = extraPoints;
|
|
||||||
|
|
||||||
// The number of unknowns to solve for.
|
|
||||||
int n = periodic ? 3 + ep : ep;
|
|
||||||
if(n >= MAX_N) oops();
|
|
||||||
// The number of on-curve points, one more than the number of segments.
|
|
||||||
int pts = periodic ? 4 + ep : 2 + ep;
|
|
||||||
|
|
||||||
int i, j, a;
|
|
||||||
|
|
||||||
// The starting and finishing control points that define our end tangents
|
|
||||||
// (if the spline isn't periodic), and the on-curve points.
|
|
||||||
Vector ctrl_s, ctrl_f, pt[MAX_N+4];
|
|
||||||
if(periodic) {
|
|
||||||
for(i = 0; i < ep + 3; i++) {
|
|
||||||
pt[i] = SK.GetEntity(point[i])->PointGetNum();
|
|
||||||
}
|
|
||||||
pt[i++] = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
} else {
|
|
||||||
ctrl_s = SK.GetEntity(point[1])->PointGetNum();
|
|
||||||
ctrl_f = SK.GetEntity(point[ep+2])->PointGetNum();
|
|
||||||
j = 0;
|
|
||||||
pt[j++] = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
for(i = 2; i <= ep + 1; i++) {
|
|
||||||
pt[j++] = SK.GetEntity(point[i])->PointGetNum();
|
|
||||||
}
|
|
||||||
pt[j++] = SK.GetEntity(point[ep+3])->PointGetNum();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The unknowns that we will be solving for, a set for each coordinate.
|
|
||||||
double Xx[MAX_N], Xy[MAX_N], Xz[MAX_N];
|
|
||||||
// For a cubic Bezier section f(t) as t goes from 0 to 1,
|
|
||||||
// f' (0) = 3*(P1 - P0)
|
|
||||||
// f' (1) = 3*(P3 - P2)
|
|
||||||
// f''(0) = 6*(P0 - 2*P1 + P2)
|
|
||||||
// f''(1) = 6*(P3 - 2*P2 + P1)
|
|
||||||
for(a = 0; a < 3; a++) {
|
|
||||||
BandedMatrix bm;
|
|
||||||
ZERO(&bm);
|
|
||||||
bm.n = n;
|
|
||||||
|
|
||||||
for(i = 0; i < n; i++) {
|
|
||||||
int im, it, ip;
|
|
||||||
if(periodic) {
|
|
||||||
im = WRAP(i - 1, n);
|
|
||||||
it = i;
|
|
||||||
ip = WRAP(i + 1, n);
|
|
||||||
} else {
|
|
||||||
im = i;
|
|
||||||
it = i + 1;
|
|
||||||
ip = i + 2;
|
|
||||||
}
|
|
||||||
// All of these are expressed in terms of a constant part, and
|
|
||||||
// of X[i-1], X[i], and X[i+1]; so let these be the four
|
|
||||||
// components of that vector;
|
|
||||||
Vector4 A, B, C, D, E;
|
|
||||||
// The on-curve interpolated point
|
|
||||||
C = Vector4::From((pt[it]).Element(a), 0, 0, 0);
|
|
||||||
// control point one back, C - X[i]
|
|
||||||
B = C.Plus(Vector4::From(0, 0, -1, 0));
|
|
||||||
// control point one forward, C + X[i]
|
|
||||||
D = C.Plus(Vector4::From(0, 0, 1, 0));
|
|
||||||
// control point two back
|
|
||||||
if(i == 0 && !periodic) {
|
|
||||||
A = Vector4::From(ctrl_s.Element(a), 0, 0, 0);
|
|
||||||
} else {
|
|
||||||
// pt[im] + X[i-1]
|
|
||||||
A = Vector4::From(pt[im].Element(a), 1, 0, 0);
|
|
||||||
}
|
|
||||||
// control point two forward
|
|
||||||
if(i == (n - 1) && !periodic) {
|
|
||||||
E = Vector4::From(ctrl_f.Element(a), 0, 0, 0);
|
|
||||||
} else {
|
|
||||||
// pt[ip] - X[i+1]
|
|
||||||
E = Vector4::From((pt[ip]).Element(a), 0, 0, -1);
|
|
||||||
}
|
|
||||||
// Write the second derivatives of each segment, dropping constant
|
|
||||||
Vector4 fprev_pp = (C.Minus(B.ScaledBy(2))).Plus(A),
|
|
||||||
fnext_pp = (C.Minus(D.ScaledBy(2))).Plus(E),
|
|
||||||
eq = fprev_pp.Minus(fnext_pp);
|
|
||||||
|
|
||||||
bm.B[i] = -eq.w;
|
|
||||||
if(periodic) {
|
|
||||||
bm.A[i][WRAP(i-2, n)] = eq.x;
|
|
||||||
bm.A[i][WRAP(i-1, n)] = eq.y;
|
|
||||||
bm.A[i][i] = eq.z;
|
|
||||||
} else {
|
|
||||||
// The wrapping would work, except when n = 1 and everything
|
|
||||||
// wraps to zero...
|
|
||||||
if(i > 0) bm.A[i][i - 1] = eq.x;
|
|
||||||
bm.A[i][i] = eq.y;
|
|
||||||
if(i < (n-1)) bm.A[i][i + 1] = eq.z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bm.Solve();
|
|
||||||
double *X = (a == 0) ? Xx :
|
|
||||||
(a == 1) ? Xy :
|
|
||||||
Xz;
|
|
||||||
memcpy(X, bm.X, n*sizeof(double));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < pts - 1; i++) {
|
|
||||||
Vector p0, p1, p2, p3;
|
|
||||||
if(periodic) {
|
|
||||||
p0 = pt[i];
|
|
||||||
int iw = WRAP(i - 1, n);
|
|
||||||
p1 = p0.Plus(Vector::From(Xx[iw], Xy[iw], Xz[iw]));
|
|
||||||
} else if(i == 0) {
|
|
||||||
p0 = pt[0];
|
|
||||||
p1 = ctrl_s;
|
|
||||||
} else {
|
|
||||||
p0 = pt[i];
|
|
||||||
p1 = p0.Plus(Vector::From(Xx[i-1], Xy[i-1], Xz[i-1]));
|
|
||||||
}
|
|
||||||
if(periodic) {
|
|
||||||
p3 = pt[i+1];
|
|
||||||
int iw = WRAP(i, n);
|
|
||||||
p2 = p3.Minus(Vector::From(Xx[iw], Xy[iw], Xz[iw]));
|
|
||||||
} else if(i == (pts - 2)) {
|
|
||||||
p3 = pt[pts-1];
|
|
||||||
p2 = ctrl_f;
|
|
||||||
} else {
|
|
||||||
p3 = pt[i+1];
|
|
||||||
p2 = p3.Minus(Vector::From(Xx[i], Xy[i], Xz[i]));
|
|
||||||
}
|
|
||||||
SBezier sb = SBezier::From(p0, p1, p2, p3);
|
|
||||||
sbl->l.Add(&sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::GenerateBezierCurves(SBezierList *sbl) {
|
|
||||||
SBezier sb;
|
|
||||||
|
|
||||||
int i = sbl->l.n;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case LINE_SEGMENT: {
|
|
||||||
Vector a = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
Vector b = SK.GetEntity(point[1])->PointGetNum();
|
|
||||||
sb = SBezier::From(a, b);
|
|
||||||
sbl->l.Add(&sb);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CUBIC:
|
|
||||||
ComputeInterpolatingSpline(sbl, false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CUBIC_PERIODIC:
|
|
||||||
ComputeInterpolatingSpline(sbl, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CIRCLE:
|
|
||||||
case ARC_OF_CIRCLE: {
|
|
||||||
Vector center = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
Quaternion q = SK.GetEntity(normal)->NormalGetNum();
|
|
||||||
Vector u = q.RotationU(), v = q.RotationV();
|
|
||||||
double r = CircleGetRadiusNum();
|
|
||||||
double thetaa, thetab, dtheta;
|
|
||||||
|
|
||||||
if(r < LENGTH_EPS) {
|
|
||||||
// If a circle or an arc gets dragged through zero radius,
|
|
||||||
// then we just don't generate anything.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(type == CIRCLE) {
|
|
||||||
thetaa = 0;
|
|
||||||
thetab = 2*PI;
|
|
||||||
dtheta = 2*PI;
|
|
||||||
} else {
|
|
||||||
ArcGetAngles(&thetaa, &thetab, &dtheta);
|
|
||||||
}
|
|
||||||
int i, n;
|
|
||||||
if(dtheta > (3*PI/2 + 0.01)) {
|
|
||||||
n = 4;
|
|
||||||
} else if(dtheta > (PI + 0.01)) {
|
|
||||||
n = 3;
|
|
||||||
} else if(dtheta > (PI/2 + 0.01)) {
|
|
||||||
n = 2;
|
|
||||||
} else {
|
|
||||||
n = 1;
|
|
||||||
}
|
|
||||||
dtheta /= n;
|
|
||||||
|
|
||||||
for(i = 0; i < n; i++) {
|
|
||||||
double s, c;
|
|
||||||
|
|
||||||
c = cos(thetaa);
|
|
||||||
s = sin(thetaa);
|
|
||||||
// The start point of the curve, and the tangent vector at
|
|
||||||
// that start point.
|
|
||||||
Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
|
|
||||||
t0 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
|
|
||||||
|
|
||||||
thetaa += dtheta;
|
|
||||||
|
|
||||||
c = cos(thetaa);
|
|
||||||
s = sin(thetaa);
|
|
||||||
Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
|
|
||||||
t2 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
|
|
||||||
|
|
||||||
// The control point must lie on both tangents.
|
|
||||||
Vector p1 = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
|
|
||||||
p2, p2.Plus(t2),
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
SBezier sb = SBezier::From(p0, p1, p2);
|
|
||||||
sb.weight[1] = cos(dtheta/2);
|
|
||||||
sbl->l.Add(&sb);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TTF_TEXT: {
|
|
||||||
Vector topLeft = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
Vector botLeft = SK.GetEntity(point[1])->PointGetNum();
|
|
||||||
Vector n = Normal()->NormalN();
|
|
||||||
Vector v = topLeft.Minus(botLeft);
|
|
||||||
Vector u = (v.Cross(n)).WithMagnitude(v.Magnitude());
|
|
||||||
|
|
||||||
SS.fonts.PlotString(font.str, str.str, 0, sbl, botLeft, u, v);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Not a problem, points and normals and such don't generate curves
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record our style for all of the Beziers that we just created.
|
|
||||||
for(; i < sbl->l.n; i++) {
|
|
||||||
sbl->l.elem[i].auxA = style.v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Entity::DrawOrGetDistance(void) {
|
|
||||||
if(!IsVisible()) return;
|
|
||||||
|
|
||||||
Group *g = SK.GetGroup(group);
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case POINT_N_COPY:
|
|
||||||
case POINT_N_TRANS:
|
|
||||||
case POINT_N_ROT_TRANS:
|
|
||||||
case POINT_N_ROT_AA:
|
|
||||||
case POINT_IN_3D:
|
|
||||||
case POINT_IN_2D: {
|
|
||||||
Vector v = PointGetNum();
|
|
||||||
dogd.refp = v;
|
|
||||||
|
|
||||||
if(dogd.drawing) {
|
|
||||||
double s = 3.5;
|
|
||||||
Vector r = SS.GW.projRight.ScaledBy(s/SS.GW.scale);
|
|
||||||
Vector d = SS.GW.projUp.ScaledBy(s/SS.GW.scale);
|
|
||||||
|
|
||||||
glxColorRGB(Style::Color(Style::DATUM));
|
|
||||||
glxDepthRangeOffset(6);
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glxVertex3v(v.Plus (r).Plus (d));
|
|
||||||
glxVertex3v(v.Plus (r).Minus(d));
|
|
||||||
glxVertex3v(v.Minus(r).Minus(d));
|
|
||||||
glxVertex3v(v.Minus(r).Plus (d));
|
|
||||||
glEnd();
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
} else {
|
|
||||||
Point2d pp = SS.GW.ProjectPoint(v);
|
|
||||||
dogd.dmin = pp.DistanceTo(dogd.mp) - 6;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NORMAL_N_COPY:
|
|
||||||
case NORMAL_N_ROT:
|
|
||||||
case NORMAL_N_ROT_AA:
|
|
||||||
case NORMAL_IN_3D:
|
|
||||||
case NORMAL_IN_2D: {
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < 2; i++) {
|
|
||||||
if(i == 0 && !SS.GW.showNormals) {
|
|
||||||
// When the normals are hidden, we will continue to show
|
|
||||||
// the coordinate axes at the bottom left corner, but
|
|
||||||
// not at the origin.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
hRequest hr = h.request();
|
|
||||||
// Always draw the x, y, and z axes in red, green, and blue;
|
|
||||||
// brighter for the ones at the bottom left of the screen,
|
|
||||||
// dimmer for the ones at the model origin.
|
|
||||||
int f = (i == 0 ? 100 : 255);
|
|
||||||
if(hr.v == Request::HREQUEST_REFERENCE_XY.v) {
|
|
||||||
glxColorRGB(RGB(0, 0, f));
|
|
||||||
} else if(hr.v == Request::HREQUEST_REFERENCE_YZ.v) {
|
|
||||||
glxColorRGB(RGB(f, 0, 0));
|
|
||||||
} else if(hr.v == Request::HREQUEST_REFERENCE_ZX.v) {
|
|
||||||
glxColorRGB(RGB(0, f, 0));
|
|
||||||
} else {
|
|
||||||
glxColorRGB(Style::Color(Style::NORMALS));
|
|
||||||
if(i > 0) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Quaternion q = NormalGetNum();
|
|
||||||
Vector tail;
|
|
||||||
if(i == 0) {
|
|
||||||
tail = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
glLineWidth(1);
|
|
||||||
} else {
|
|
||||||
// Draw an extra copy of the x, y, and z axes, that's
|
|
||||||
// always in the corner of the view and at the front.
|
|
||||||
// So those are always available, perhaps useful.
|
|
||||||
double s = SS.GW.scale;
|
|
||||||
double h = 60 - SS.GW.height/2;
|
|
||||||
double w = 60 - SS.GW.width/2;
|
|
||||||
tail = SS.GW.projRight.ScaledBy(w/s).Plus(
|
|
||||||
SS.GW.projUp. ScaledBy(h/s)).Minus(SS.GW.offset);
|
|
||||||
glxDepthRangeLockToFront(true);
|
|
||||||
glLineWidth(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector v = (q.RotationN()).WithMagnitude(50/SS.GW.scale);
|
|
||||||
Vector tip = tail.Plus(v);
|
|
||||||
LineDrawOrGetDistance(tail, tip);
|
|
||||||
|
|
||||||
v = v.WithMagnitude(12/SS.GW.scale);
|
|
||||||
Vector axis = q.RotationV();
|
|
||||||
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis, 0.6)));
|
|
||||||
LineDrawOrGetDistance(tip,tip.Minus(v.RotatedAbout(axis,-0.6)));
|
|
||||||
}
|
|
||||||
glxDepthRangeLockToFront(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case DISTANCE:
|
|
||||||
case DISTANCE_N_COPY:
|
|
||||||
// These are used only as data structures, nothing to display.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WORKPLANE: {
|
|
||||||
Vector p;
|
|
||||||
p = SK.GetEntity(point[0])->PointGetNum();
|
|
||||||
|
|
||||||
Vector u = Normal()->NormalU();
|
|
||||||
Vector v = Normal()->NormalV();
|
|
||||||
|
|
||||||
double s = (min(SS.GW.width, SS.GW.height))*0.45/SS.GW.scale;
|
|
||||||
|
|
||||||
Vector us = u.ScaledBy(s);
|
|
||||||
Vector vs = v.ScaledBy(s);
|
|
||||||
|
|
||||||
Vector pp = p.Plus (us).Plus (vs);
|
|
||||||
Vector pm = p.Plus (us).Minus(vs);
|
|
||||||
Vector mm = p.Minus(us).Minus(vs), mm2 = mm;
|
|
||||||
Vector mp = p.Minus(us).Plus (vs);
|
|
||||||
|
|
||||||
glLineWidth(1);
|
|
||||||
glxColorRGB(Style::Color(Style::NORMALS));
|
|
||||||
glEnable(GL_LINE_STIPPLE);
|
|
||||||
glLineStipple(3, 0x1111);
|
|
||||||
if(!h.isFromRequest()) {
|
|
||||||
mm = mm.Plus(v.ScaledBy(60/SS.GW.scale));
|
|
||||||
mm2 = mm2.Plus(u.ScaledBy(60/SS.GW.scale));
|
|
||||||
LineDrawOrGetDistance(mm2, mm);
|
|
||||||
}
|
|
||||||
LineDrawOrGetDistance(pp, pm);
|
|
||||||
LineDrawOrGetDistance(pm, mm2);
|
|
||||||
LineDrawOrGetDistance(mm, mp);
|
|
||||||
LineDrawOrGetDistance(mp, pp);
|
|
||||||
glDisable(GL_LINE_STIPPLE);
|
|
||||||
|
|
||||||
char *str = DescriptionString()+5;
|
|
||||||
double th = DEFAULT_TEXT_HEIGHT;
|
|
||||||
if(dogd.drawing) {
|
|
||||||
glxWriteText(str, th, mm2, u, v, NULL, NULL);
|
|
||||||
} else {
|
|
||||||
Vector pos = mm2.Plus(u.ScaledBy(glxStrWidth(str, th)/2)).Plus(
|
|
||||||
v.ScaledBy(glxStrHeight(th)/2));
|
|
||||||
Point2d pp = SS.GW.ProjectPoint(pos);
|
|
||||||
dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10);
|
|
||||||
// If a line lies in a plane, then select the line, not
|
|
||||||
// the plane.
|
|
||||||
dogd.dmin += 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case LINE_SEGMENT:
|
|
||||||
case CIRCLE:
|
|
||||||
case ARC_OF_CIRCLE:
|
|
||||||
case CUBIC:
|
|
||||||
case CUBIC_PERIODIC:
|
|
||||||
case TTF_TEXT:
|
|
||||||
// Nothing but the curve(s).
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FACE_NORMAL_PT:
|
|
||||||
case FACE_XPROD:
|
|
||||||
case FACE_N_ROT_TRANS:
|
|
||||||
case FACE_N_TRANS:
|
|
||||||
case FACE_N_ROT_AA:
|
|
||||||
// Do nothing; these are drawn with the triangle mesh
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
oops();
|
|
||||||
}
|
|
||||||
|
|
||||||
// And draw the curves; generate the rational polynomial curves for
|
|
||||||
// everything, then piecewise linearize them, and display those.
|
|
||||||
SEdgeList sel;
|
|
||||||
ZERO(&sel);
|
|
||||||
GenerateEdges(&sel, true);
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < sel.l.n; i++) {
|
|
||||||
SEdge *se = &(sel.l.elem[i]);
|
|
||||||
LineDrawOrGetDistance(se->a, se->b, true);
|
|
||||||
}
|
|
||||||
sel.Clear();
|
|
||||||
}
|
|
||||||
|
|
396
dsc.h
|
@ -1,396 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Data structures used frequently in the program, various kinds of vectors
|
|
||||||
// (of real numbers, not symbolic algebra stuff) and our templated lists.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef __DSC_H
|
|
||||||
#define __DSC_H
|
|
||||||
|
|
||||||
typedef unsigned long DWORD;
|
|
||||||
typedef unsigned char BYTE;
|
|
||||||
|
|
||||||
class Vector;
|
|
||||||
class Vector4;
|
|
||||||
class Point2d;
|
|
||||||
class hEntity;
|
|
||||||
class hParam;
|
|
||||||
|
|
||||||
class Quaternion {
|
|
||||||
public:
|
|
||||||
// a + (vx)*i + (vy)*j + (vz)*k
|
|
||||||
double w, vx, vy, vz;
|
|
||||||
|
|
||||||
static const Quaternion IDENTITY;
|
|
||||||
|
|
||||||
static Quaternion From(double w, double vx, double vy, double vz);
|
|
||||||
static Quaternion From(hParam w, hParam vx, hParam vy, hParam vz);
|
|
||||||
static Quaternion From(Vector u, Vector v);
|
|
||||||
static Quaternion From(Vector axis, double dtheta);
|
|
||||||
|
|
||||||
Quaternion Plus(Quaternion b);
|
|
||||||
Quaternion Minus(Quaternion b);
|
|
||||||
Quaternion ScaledBy(double s);
|
|
||||||
double Magnitude(void);
|
|
||||||
Quaternion WithMagnitude(double s);
|
|
||||||
|
|
||||||
// Call a rotation matrix [ u' v' n' ]'; this returns the first and
|
|
||||||
// second rows, where that matrix is generated by this quaternion
|
|
||||||
Vector RotationU(void);
|
|
||||||
Vector RotationV(void);
|
|
||||||
Vector RotationN(void);
|
|
||||||
Vector Rotate(Vector p);
|
|
||||||
|
|
||||||
Quaternion ToThe(double p);
|
|
||||||
Quaternion Inverse(void);
|
|
||||||
Quaternion Times(Quaternion b);
|
|
||||||
Quaternion Mirror(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Vector {
|
|
||||||
public:
|
|
||||||
double x, y, z;
|
|
||||||
|
|
||||||
static Vector From(double x, double y, double z);
|
|
||||||
static Vector From(hParam x, hParam y, hParam z);
|
|
||||||
static Vector AtIntersectionOfPlanes(Vector n1, double d1,
|
|
||||||
Vector n2, double d2);
|
|
||||||
static Vector AtIntersectionOfLines(Vector a0, Vector a1,
|
|
||||||
Vector b0, Vector b1,
|
|
||||||
bool *skew,
|
|
||||||
double *pa=NULL, double *pb=NULL);
|
|
||||||
static Vector AtIntersectionOfPlaneAndLine(Vector n, double d,
|
|
||||||
Vector p0, Vector p1,
|
|
||||||
bool *parallel);
|
|
||||||
static Vector AtIntersectionOfPlanes(Vector na, double da,
|
|
||||||
Vector nb, double db,
|
|
||||||
Vector nc, double dc, bool *parallel);
|
|
||||||
static void ClosestPointBetweenLines(Vector pa, Vector da,
|
|
||||||
Vector pb, Vector db,
|
|
||||||
double *ta, double *tb);
|
|
||||||
|
|
||||||
double Element(int i);
|
|
||||||
bool Equals(Vector v, double tol=LENGTH_EPS);
|
|
||||||
bool EqualsExactly(Vector v);
|
|
||||||
Vector Plus(Vector b);
|
|
||||||
Vector Minus(Vector b);
|
|
||||||
Vector Negated(void);
|
|
||||||
Vector Cross(Vector b);
|
|
||||||
double DirectionCosineWith(Vector b);
|
|
||||||
double Dot(Vector b);
|
|
||||||
Vector Normal(int which);
|
|
||||||
Vector RotatedAbout(Vector orig, Vector axis, double theta);
|
|
||||||
Vector RotatedAbout(Vector axis, double theta);
|
|
||||||
Vector DotInToCsys(Vector u, Vector v, Vector n);
|
|
||||||
Vector ScaleOutOfCsys(Vector u, Vector v, Vector n);
|
|
||||||
double DistanceToLine(Vector p0, Vector dp);
|
|
||||||
bool OnLineSegment(Vector a, Vector b, double tol=LENGTH_EPS);
|
|
||||||
Vector ClosestPointOnLine(Vector p0, Vector dp);
|
|
||||||
double Magnitude(void);
|
|
||||||
double MagSquared(void);
|
|
||||||
Vector WithMagnitude(double s);
|
|
||||||
Vector ScaledBy(double s);
|
|
||||||
Vector ProjectInto(hEntity wrkpl);
|
|
||||||
Vector ProjectVectorInto(hEntity wrkpl);
|
|
||||||
double DivPivoting(Vector delta);
|
|
||||||
Vector ClosestOrtho(void);
|
|
||||||
void MakeMaxMin(Vector *maxv, Vector *minv);
|
|
||||||
Vector ClampWithin(double minv, double maxv);
|
|
||||||
static bool BoundingBoxesDisjoint(Vector amax, Vector amin,
|
|
||||||
Vector bmax, Vector bmin);
|
|
||||||
static bool BoundingBoxIntersectsLine(Vector amax, Vector amin,
|
|
||||||
Vector p0, Vector p1, bool segment);
|
|
||||||
bool OutsideAndNotOn(Vector maxv, Vector minv);
|
|
||||||
Vector InPerspective(Vector u, Vector v, Vector n,
|
|
||||||
Vector origin, double cameraTan);
|
|
||||||
Point2d Project2d(Vector u, Vector v);
|
|
||||||
Point2d ProjectXy(void);
|
|
||||||
Vector4 Project4d(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Vector4 {
|
|
||||||
public:
|
|
||||||
double w, x, y, z;
|
|
||||||
|
|
||||||
static Vector4 From(double w, double x, double y, double z);
|
|
||||||
static Vector4 From(double w, Vector v3);
|
|
||||||
static Vector4 Blend(Vector4 a, Vector4 b, double t);
|
|
||||||
|
|
||||||
Vector4 Plus(Vector4 b);
|
|
||||||
Vector4 Minus(Vector4 b);
|
|
||||||
Vector4 ScaledBy(double s);
|
|
||||||
Vector PerspectiveProject(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Point2d {
|
|
||||||
public:
|
|
||||||
double x, y;
|
|
||||||
|
|
||||||
static Point2d From(double x, double y);
|
|
||||||
|
|
||||||
Point2d Plus(Point2d b);
|
|
||||||
Point2d Minus(Point2d b);
|
|
||||||
Point2d ScaledBy(double s);
|
|
||||||
double DivPivoting(Point2d delta);
|
|
||||||
double Dot(Point2d p);
|
|
||||||
double DistanceTo(Point2d p);
|
|
||||||
double DistanceToLine(Point2d p0, Point2d dp, bool segment);
|
|
||||||
double Magnitude(void);
|
|
||||||
double MagSquared(void);
|
|
||||||
Point2d WithMagnitude(double v);
|
|
||||||
Point2d Normal(void);
|
|
||||||
bool Equals(Point2d v, double tol=LENGTH_EPS);
|
|
||||||
};
|
|
||||||
|
|
||||||
// A simple list
|
|
||||||
template <class T>
|
|
||||||
class List {
|
|
||||||
public:
|
|
||||||
T *elem;
|
|
||||||
int n;
|
|
||||||
int elemsAllocated;
|
|
||||||
|
|
||||||
void AllocForOneMore(void) {
|
|
||||||
if(n >= elemsAllocated) {
|
|
||||||
elemsAllocated = (elemsAllocated + 32)*2;
|
|
||||||
elem = (T *)MemRealloc(elem, elemsAllocated*sizeof(elem[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Add(T *t) {
|
|
||||||
AllocForOneMore();
|
|
||||||
elem[n++] = *t;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddToBeginning(T *t) {
|
|
||||||
AllocForOneMore();
|
|
||||||
memmove(elem+1, elem, n*sizeof(elem[0]));
|
|
||||||
n++;
|
|
||||||
elem[0] = *t;
|
|
||||||
}
|
|
||||||
|
|
||||||
T *First(void) {
|
|
||||||
return (n == 0) ? NULL : &(elem[0]);
|
|
||||||
}
|
|
||||||
T *NextAfter(T *prev) {
|
|
||||||
if(!prev) return NULL;
|
|
||||||
if(prev - elem == (n - 1)) return NULL;
|
|
||||||
return prev + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearTags(void) {
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < n; i++) {
|
|
||||||
elem[i].tag = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear(void) {
|
|
||||||
if(elem) MemFree(elem);
|
|
||||||
elem = NULL;
|
|
||||||
n = elemsAllocated = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveTagged(void) {
|
|
||||||
int src, dest;
|
|
||||||
dest = 0;
|
|
||||||
for(src = 0; src < n; src++) {
|
|
||||||
if(elem[src].tag) {
|
|
||||||
// this item should be deleted
|
|
||||||
} else {
|
|
||||||
if(src != dest) {
|
|
||||||
elem[dest] = elem[src];
|
|
||||||
}
|
|
||||||
dest++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n = dest;
|
|
||||||
// and elemsAllocated is untouched, because we didn't resize
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveLast(int cnt) {
|
|
||||||
if(n < cnt) oops();
|
|
||||||
n -= cnt;
|
|
||||||
// and elemsAllocated is untouched, same as in RemoveTagged
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reverse(void) {
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < (n/2); i++) {
|
|
||||||
SWAP(T, elem[i], elem[(n-1)-i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A list, where each element has an integer identifier. The list is kept
|
|
||||||
// sorted by that identifier, and items can be looked up in log n time by
|
|
||||||
// id.
|
|
||||||
template <class T, class H>
|
|
||||||
class IdList {
|
|
||||||
public:
|
|
||||||
T *elem;
|
|
||||||
int n;
|
|
||||||
int elemsAllocated;
|
|
||||||
|
|
||||||
DWORD MaximumId(void) {
|
|
||||||
DWORD id = 0;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < n; i++) {
|
|
||||||
id = max(id, elem[i].h.v);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
H AddAndAssignId(T *t) {
|
|
||||||
t->h.v = (MaximumId() + 1);
|
|
||||||
Add(t);
|
|
||||||
|
|
||||||
return t->h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Add(T *t) {
|
|
||||||
if(n >= elemsAllocated) {
|
|
||||||
elemsAllocated = (elemsAllocated + 32)*2;
|
|
||||||
elem = (T *)MemRealloc(elem, elemsAllocated*sizeof(elem[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
int first = 0, last = n;
|
|
||||||
// We know that we must insert within the closed interval [first,last]
|
|
||||||
while(first != last) {
|
|
||||||
int mid = (first + last)/2;
|
|
||||||
H hm = elem[mid].h;
|
|
||||||
if(hm.v > t->h.v) {
|
|
||||||
last = mid;
|
|
||||||
} else if(hm.v < t->h.v) {
|
|
||||||
first = mid + 1;
|
|
||||||
} else {
|
|
||||||
dbp("can't insert in list; is handle %d not unique?", t->h.v);
|
|
||||||
oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int i = first;
|
|
||||||
|
|
||||||
memmove(elem+i+1, elem+i, (n-i)*sizeof(elem[0]));
|
|
||||||
elem[i] = *t;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
T *FindById(H h) {
|
|
||||||
T *t = FindByIdNoOops(h);
|
|
||||||
if(!t) {
|
|
||||||
dbp("failed to look up item %08x, searched %d items", h.v, n);
|
|
||||||
oops();
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
T *FindByIdNoOops(H h) {
|
|
||||||
int first = 0, last = n-1;
|
|
||||||
while(first <= last) {
|
|
||||||
int mid = (first + last)/2;
|
|
||||||
H hm = elem[mid].h;
|
|
||||||
if(hm.v > h.v) {
|
|
||||||
last = mid-1; // and first stays the same
|
|
||||||
} else if(hm.v < h.v) {
|
|
||||||
first = mid+1; // and last stays the same
|
|
||||||
} else {
|
|
||||||
return &(elem[mid]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
T *First(void) {
|
|
||||||
return (n == 0) ? NULL : &(elem[0]);
|
|
||||||
}
|
|
||||||
T *NextAfter(T *prev) {
|
|
||||||
if(!prev) return NULL;
|
|
||||||
if(prev - elem == (n - 1)) return NULL;
|
|
||||||
return prev + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearTags(void) {
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < n; i++) {
|
|
||||||
elem[i].tag = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tag(H h, int tag) {
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < n; i++) {
|
|
||||||
if(elem[i].h.v == h.v) {
|
|
||||||
elem[i].tag = tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveTagged(void) {
|
|
||||||
int src, dest;
|
|
||||||
dest = 0;
|
|
||||||
for(src = 0; src < n; src++) {
|
|
||||||
if(elem[src].tag) {
|
|
||||||
// this item should be deleted
|
|
||||||
} else {
|
|
||||||
if(src != dest) {
|
|
||||||
elem[dest] = elem[src];
|
|
||||||
}
|
|
||||||
dest++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n = dest;
|
|
||||||
// and elemsAllocated is untouched, because we didn't resize
|
|
||||||
}
|
|
||||||
void RemoveById(H h) {
|
|
||||||
ClearTags();
|
|
||||||
FindById(h)->tag = 1;
|
|
||||||
RemoveTagged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MoveSelfInto(IdList<T,H> *l) {
|
|
||||||
memcpy(l, this, sizeof(*this));
|
|
||||||
elemsAllocated = n = 0;
|
|
||||||
elem = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeepCopyInto(IdList<T,H> *l) {
|
|
||||||
l->elem = (T *)MemAlloc(elemsAllocated * sizeof(elem[0]));
|
|
||||||
memcpy(l->elem, elem, elemsAllocated * sizeof(elem[0]));
|
|
||||||
l->elemsAllocated = elemsAllocated;
|
|
||||||
l->n = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear(void) {
|
|
||||||
elemsAllocated = n = 0;
|
|
||||||
if(elem) MemFree(elem);
|
|
||||||
elem = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class NameStr {
|
|
||||||
public:
|
|
||||||
char str[64];
|
|
||||||
|
|
||||||
inline void strcpy(char *in) {
|
|
||||||
memcpy(str, in, min(strlen(in)+1, sizeof(str)));
|
|
||||||
str[sizeof(str)-1] = '\0';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class BandedMatrix {
|
|
||||||
public:
|
|
||||||
static const int MAX_UNKNOWNS = 16;
|
|
||||||
static const int RIGHT_OF_DIAG = 1;
|
|
||||||
static const int LEFT_OF_DIAG = 2;
|
|
||||||
|
|
||||||
double A[MAX_UNKNOWNS][MAX_UNKNOWNS];
|
|
||||||
double B[MAX_UNKNOWNS];
|
|
||||||
double X[MAX_UNKNOWNS];
|
|
||||||
int n;
|
|
||||||
|
|
||||||
void Solve(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
732
export.cpp
|
@ -1,732 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// The 2d vector output stuff that isn't specific to any particular file
|
|
||||||
// format: getting the appropriate lines and curves, performing hidden line
|
|
||||||
// removal, calculating bounding boxes, and so on. Also raster and triangle
|
|
||||||
// mesh output.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
#include <png.h>
|
|
||||||
|
|
||||||
void SolveSpace::ExportSectionTo(char *filename) {
|
|
||||||
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
|
||||||
gn = gn.WithMagnitude(1);
|
|
||||||
|
|
||||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
|
||||||
g->GenerateDisplayItems();
|
|
||||||
if(g->displayMesh.IsEmpty()) {
|
|
||||||
Error("No solid model present; draw one with extrudes and revolves, "
|
|
||||||
"or use Export 2d View to export bare lines and curves.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The plane in which the exported section lies; need this because we'll
|
|
||||||
// reorient from that plane into the xy plane before exporting.
|
|
||||||
Vector origin, u, v, n;
|
|
||||||
double d;
|
|
||||||
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
#define gs (SS.GW.gs)
|
|
||||||
if((gs.n == 0 && g->activeWorkplane.v != Entity::FREE_IN_3D.v)) {
|
|
||||||
Entity *wrkpl = SK.GetEntity(g->activeWorkplane);
|
|
||||||
origin = wrkpl->WorkplaneGetOffset();
|
|
||||||
n = wrkpl->Normal()->NormalN();
|
|
||||||
u = wrkpl->Normal()->NormalU();
|
|
||||||
v = wrkpl->Normal()->NormalV();
|
|
||||||
} else if(gs.n == 1 && gs.faces == 1) {
|
|
||||||
Entity *face = SK.GetEntity(gs.entity[0]);
|
|
||||||
origin = face->FaceGetPointNum();
|
|
||||||
n = face->FaceGetNormalNum();
|
|
||||||
if(n.Dot(gn) < 0) n = n.ScaledBy(-1);
|
|
||||||
u = n.Normal(0);
|
|
||||||
v = n.Normal(1);
|
|
||||||
} else if(gs.n == 3 && gs.vectors == 2 && gs.points == 1) {
|
|
||||||
Vector ut = SK.GetEntity(gs.entity[0])->VectorGetNum(),
|
|
||||||
vt = SK.GetEntity(gs.entity[1])->VectorGetNum();
|
|
||||||
ut = ut.WithMagnitude(1);
|
|
||||||
vt = vt.WithMagnitude(1);
|
|
||||||
|
|
||||||
if(fabs(SS.GW.projUp.Dot(vt)) < fabs(SS.GW.projUp.Dot(ut))) {
|
|
||||||
SWAP(Vector, ut, vt);
|
|
||||||
}
|
|
||||||
if(SS.GW.projRight.Dot(ut) < 0) ut = ut.ScaledBy(-1);
|
|
||||||
if(SS.GW.projUp. Dot(vt) < 0) vt = vt.ScaledBy(-1);
|
|
||||||
|
|
||||||
origin = SK.GetEntity(gs.point[0])->PointGetNum();
|
|
||||||
n = ut.Cross(vt);
|
|
||||||
u = ut.WithMagnitude(1);
|
|
||||||
v = (n.Cross(u)).WithMagnitude(1);
|
|
||||||
} else {
|
|
||||||
Error("Bad selection for export section. Please select:\n\n"
|
|
||||||
" * nothing, with an active workplane "
|
|
||||||
"(workplane is section plane)\n"
|
|
||||||
" * a face (section plane through face)\n"
|
|
||||||
" * a point and two line segments "
|
|
||||||
"(plane through point and parallel to lines)\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SS.GW.ClearSelection();
|
|
||||||
|
|
||||||
n = n.WithMagnitude(1);
|
|
||||||
d = origin.Dot(n);
|
|
||||||
|
|
||||||
SEdgeList el;
|
|
||||||
ZERO(&el);
|
|
||||||
SBezierList bl;
|
|
||||||
ZERO(&bl);
|
|
||||||
|
|
||||||
// If there's a mesh, then grab the edges from it.
|
|
||||||
g->runningMesh.MakeEdgesInPlaneInto(&el, n, d);
|
|
||||||
|
|
||||||
// If there's a shell, then grab the edges and possibly Beziers.
|
|
||||||
g->runningShell.MakeSectionEdgesInto(n, d,
|
|
||||||
&el,
|
|
||||||
(SS.exportPwlCurves || fabs(SS.exportOffset) > LENGTH_EPS) ? NULL : &bl);
|
|
||||||
|
|
||||||
// All of these are solid model edges, so use the appropriate style.
|
|
||||||
SEdge *se;
|
|
||||||
for(se = el.l.First(); se; se = el.l.NextAfter(se)) {
|
|
||||||
se->auxA = Style::SOLID_EDGE;
|
|
||||||
}
|
|
||||||
SBezier *sb;
|
|
||||||
for(sb = bl.l.First(); sb; sb = bl.l.NextAfter(sb)) {
|
|
||||||
sb->auxA = Style::SOLID_EDGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
el.CullExtraneousEdges();
|
|
||||||
bl.CullIdenticalBeziers();
|
|
||||||
|
|
||||||
// And write the edges.
|
|
||||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
|
||||||
if(out) {
|
|
||||||
// parallel projection (no perspective), and no mesh
|
|
||||||
ExportLinesAndMesh(&el, &bl, NULL,
|
|
||||||
u, v, n, origin, 0,
|
|
||||||
out);
|
|
||||||
}
|
|
||||||
el.Clear();
|
|
||||||
bl.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) {
|
|
||||||
int i;
|
|
||||||
SEdgeList edges;
|
|
||||||
ZERO(&edges);
|
|
||||||
SBezierList beziers;
|
|
||||||
ZERO(&beziers);
|
|
||||||
|
|
||||||
SMesh *sm = NULL;
|
|
||||||
if(SS.GW.showShaded) {
|
|
||||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
|
||||||
g->GenerateDisplayItems();
|
|
||||||
sm = &(g->displayMesh);
|
|
||||||
}
|
|
||||||
if(sm && sm->IsEmpty()) {
|
|
||||||
sm = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i = 0; i < SK.entity.n; i++) {
|
|
||||||
Entity *e = &(SK.entity.elem[i]);
|
|
||||||
if(!e->IsVisible()) continue;
|
|
||||||
if(e->construction) continue;
|
|
||||||
|
|
||||||
if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines) ||
|
|
||||||
fabs(SS.exportOffset) > LENGTH_EPS)
|
|
||||||
{
|
|
||||||
// We will be doing hidden line removal, which we can't do on
|
|
||||||
// exact curves; so we need things broken down to pwls. Same
|
|
||||||
// problem with cutter radius compensation.
|
|
||||||
e->GenerateEdges(&edges);
|
|
||||||
} else {
|
|
||||||
e->GenerateBezierCurves(&beziers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(SS.GW.showEdges) {
|
|
||||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
|
||||||
g->GenerateDisplayItems();
|
|
||||||
SEdgeList *selr = &(g->displayEdges);
|
|
||||||
SEdge *se;
|
|
||||||
for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) {
|
|
||||||
edges.AddEdge(se->a, se->b, Style::SOLID_EDGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(SS.GW.showConstraints) {
|
|
||||||
Constraint *c;
|
|
||||||
for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
|
|
||||||
c->GetEdges(&edges);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(wireframe) {
|
|
||||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
|
||||||
if(out) {
|
|
||||||
ExportWireframeCurves(&edges, &beziers, out);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Vector u = SS.GW.projRight,
|
|
||||||
v = SS.GW.projUp,
|
|
||||||
n = u.Cross(v),
|
|
||||||
origin = SS.GW.offset.ScaledBy(-1);
|
|
||||||
|
|
||||||
VectorFileWriter *out = VectorFileWriter::ForFile(filename);
|
|
||||||
if(out) {
|
|
||||||
ExportLinesAndMesh(&edges, &beziers, sm,
|
|
||||||
u, v, n, origin, SS.CameraTangent()*SS.GW.scale,
|
|
||||||
out);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(out && !out->HasCanvasSize()) {
|
|
||||||
// These file formats don't have a canvas size, so they just
|
|
||||||
// get exported in the raw coordinate system. So indicate what
|
|
||||||
// that was on-screen.
|
|
||||||
SS.justExportedInfo.draw = true;
|
|
||||||
SS.justExportedInfo.pt = origin;
|
|
||||||
SS.justExportedInfo.u = u;
|
|
||||||
SS.justExportedInfo.v = v;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
edges.Clear();
|
|
||||||
beziers.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::ExportWireframeCurves(SEdgeList *sel, SBezierList *sbl,
|
|
||||||
VectorFileWriter *out)
|
|
||||||
{
|
|
||||||
SBezierLoopSetSet sblss;
|
|
||||||
ZERO(&sblss);
|
|
||||||
SEdge *se;
|
|
||||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
|
||||||
SBezier sb = SBezier::From(
|
|
||||||
(se->a).ScaledBy(1.0 / SS.exportScale),
|
|
||||||
(se->b).ScaledBy(1.0 / SS.exportScale));
|
|
||||||
sblss.AddOpenPath(&sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
sbl->ScaleSelfBy(1.0/SS.exportScale);
|
|
||||||
SBezier *sb;
|
|
||||||
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
|
|
||||||
sblss.AddOpenPath(sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
out->Output(&sblss, NULL);
|
|
||||||
sblss.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm,
|
|
||||||
Vector u, Vector v, Vector n,
|
|
||||||
Vector origin, double cameraTan,
|
|
||||||
VectorFileWriter *out)
|
|
||||||
{
|
|
||||||
double s = 1.0 / SS.exportScale;
|
|
||||||
|
|
||||||
// Project into the export plane; so when we're done, z doesn't matter,
|
|
||||||
// and x and y are what goes in the DXF.
|
|
||||||
SEdge *e;
|
|
||||||
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
|
||||||
// project into the specified csys, and apply export scale
|
|
||||||
(e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
|
||||||
(e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
SBezier *b;
|
|
||||||
if(sbl) {
|
|
||||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
|
||||||
*b = b->InPerspective(u, v, n, origin, cameraTan);
|
|
||||||
int i;
|
|
||||||
for(i = 0; i <= b->deg; i++) {
|
|
||||||
b->ctrl[i] = (b->ctrl[i]).ScaledBy(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If cutter radius compensation is requested, then perform it now
|
|
||||||
if(fabs(SS.exportOffset) > LENGTH_EPS) {
|
|
||||||
// assemble those edges into a polygon, and clear the edge list
|
|
||||||
SPolygon sp;
|
|
||||||
ZERO(&sp);
|
|
||||||
sel->AssemblePolygon(&sp, NULL);
|
|
||||||
sel->Clear();
|
|
||||||
|
|
||||||
SPolygon compd;
|
|
||||||
ZERO(&compd);
|
|
||||||
sp.normal = Vector::From(0, 0, -1);
|
|
||||||
sp.FixContourDirections();
|
|
||||||
sp.OffsetInto(&compd, SS.exportOffset*s);
|
|
||||||
sp.Clear();
|
|
||||||
|
|
||||||
compd.MakeEdgesInto(sel);
|
|
||||||
compd.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now the triangle mesh; project, then build a BSP to perform
|
|
||||||
// occlusion testing and generated the shaded surfaces.
|
|
||||||
SMesh smp;
|
|
||||||
ZERO(&smp);
|
|
||||||
if(sm) {
|
|
||||||
Vector l0 = (SS.lightDir[0]).WithMagnitude(1),
|
|
||||||
l1 = (SS.lightDir[1]).WithMagnitude(1);
|
|
||||||
STriangle *tr;
|
|
||||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
|
||||||
STriangle tt = *tr;
|
|
||||||
tt.a = (tt.a).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
|
||||||
tt.b = (tt.b).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
|
||||||
tt.c = (tt.c).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s);
|
|
||||||
|
|
||||||
// And calculate lighting for the triangle
|
|
||||||
Vector n = tt.Normal().WithMagnitude(1);
|
|
||||||
double lighting = SS.ambientIntensity +
|
|
||||||
max(0, (SS.lightIntensity[0])*(n.Dot(l0))) +
|
|
||||||
max(0, (SS.lightIntensity[1])*(n.Dot(l1)));
|
|
||||||
double r = min(1, REDf (tt.meta.color)*lighting),
|
|
||||||
g = min(1, GREENf(tt.meta.color)*lighting),
|
|
||||||
b = min(1, BLUEf (tt.meta.color)*lighting);
|
|
||||||
tt.meta.color = RGBf(r, g, b);
|
|
||||||
smp.AddTriangle(&tt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the BSP routines to generate the split triangles in paint order.
|
|
||||||
SBsp3 *bsp = SBsp3::FromMesh(&smp);
|
|
||||||
SMesh sms;
|
|
||||||
ZERO(&sms);
|
|
||||||
bsp->GenerateInPaintOrder(&sms);
|
|
||||||
// And cull the back-facing triangles
|
|
||||||
STriangle *tr;
|
|
||||||
sms.l.ClearTags();
|
|
||||||
for(tr = sms.l.First(); tr; tr = sms.l.NextAfter(tr)) {
|
|
||||||
Vector n = tr->Normal();
|
|
||||||
if(n.z < 0) {
|
|
||||||
tr->tag = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sms.l.RemoveTagged();
|
|
||||||
|
|
||||||
// And now we perform hidden line removal if requested
|
|
||||||
SEdgeList hlrd;
|
|
||||||
ZERO(&hlrd);
|
|
||||||
if(sm && !SS.GW.showHdnLines) {
|
|
||||||
SKdNode *root = SKdNode::From(&smp);
|
|
||||||
|
|
||||||
// Generate the edges where a curved surface turns from front-facing
|
|
||||||
// to back-facing.
|
|
||||||
if(SS.GW.showEdges) {
|
|
||||||
root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES,
|
|
||||||
false, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
root->ClearTags();
|
|
||||||
int cnt = 1234;
|
|
||||||
|
|
||||||
SEdge *se;
|
|
||||||
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
|
||||||
if(se->auxA == Style::CONSTRAINT) {
|
|
||||||
// Constraints should not get hidden line removed; they're
|
|
||||||
// always on top.
|
|
||||||
hlrd.AddEdge(se->a, se->b, se->auxA);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEdgeList out;
|
|
||||||
ZERO(&out);
|
|
||||||
// Split the original edge against the mesh
|
|
||||||
out.AddEdge(se->a, se->b, se->auxA);
|
|
||||||
root->OcclusionTestLine(*se, &out, cnt);
|
|
||||||
// the occlusion test splits unnecessarily; so fix those
|
|
||||||
out.MergeCollinearSegments(se->a, se->b);
|
|
||||||
cnt++;
|
|
||||||
// And add the results to our output
|
|
||||||
SEdge *sen;
|
|
||||||
for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) {
|
|
||||||
hlrd.AddEdge(sen->a, sen->b, sen->auxA);
|
|
||||||
}
|
|
||||||
out.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
sel = &hlrd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We kept the line segments and Beziers separate until now; but put them
|
|
||||||
// all together, and also project everything into the xy plane, since not
|
|
||||||
// all export targets ignore the z component of the points.
|
|
||||||
for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) {
|
|
||||||
SBezier sb = SBezier::From(e->a, e->b);
|
|
||||||
sb.auxA = e->auxA;
|
|
||||||
sbl->l.Add(&sb);
|
|
||||||
}
|
|
||||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
|
||||||
for(int i = 0; i <= b->deg; i++) {
|
|
||||||
b->ctrl[i].z = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If possible, then we will assemble these output curves into loops. They
|
|
||||||
// will then get exported as closed paths.
|
|
||||||
SBezierLoopSetSet sblss;
|
|
||||||
ZERO(&sblss);
|
|
||||||
SBezierList leftovers;
|
|
||||||
ZERO(&leftovers);
|
|
||||||
SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0),
|
|
||||||
Vector::From(1, 0, 0),
|
|
||||||
Vector::From(0, 1, 0));
|
|
||||||
SPolygon spxyz;
|
|
||||||
ZERO(&spxyz);
|
|
||||||
bool allClosed;
|
|
||||||
SEdge notClosedAt;
|
|
||||||
sbl->l.ClearTags();
|
|
||||||
sblss.FindOuterFacesFrom(sbl, &spxyz, &srf,
|
|
||||||
SS.ChordTolMm()*s,
|
|
||||||
&allClosed, ¬ClosedAt,
|
|
||||||
NULL, NULL,
|
|
||||||
&leftovers);
|
|
||||||
for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) {
|
|
||||||
sblss.AddOpenPath(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now write the lines and triangles to the output file
|
|
||||||
out->Output(&sblss, &sms);
|
|
||||||
|
|
||||||
leftovers.Clear();
|
|
||||||
spxyz.Clear();
|
|
||||||
sblss.Clear();
|
|
||||||
smp.Clear();
|
|
||||||
sms.Clear();
|
|
||||||
hlrd.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
double VectorFileWriter::MmToPts(double mm) {
|
|
||||||
// 72 points in an inch
|
|
||||||
return (mm/25.4)*72;
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorFileWriter *VectorFileWriter::ForFile(char *filename) {
|
|
||||||
VectorFileWriter *ret;
|
|
||||||
if(StringEndsIn(filename, ".dxf")) {
|
|
||||||
static DxfFileWriter DxfWriter;
|
|
||||||
ret = &DxfWriter;
|
|
||||||
} else if(StringEndsIn(filename, ".ps") || StringEndsIn(filename, ".eps")) {
|
|
||||||
static EpsFileWriter EpsWriter;
|
|
||||||
ret = &EpsWriter;
|
|
||||||
} else if(StringEndsIn(filename, ".pdf")) {
|
|
||||||
static PdfFileWriter PdfWriter;
|
|
||||||
ret = &PdfWriter;
|
|
||||||
} else if(StringEndsIn(filename, ".svg")) {
|
|
||||||
static SvgFileWriter SvgWriter;
|
|
||||||
ret = &SvgWriter;
|
|
||||||
} else if(StringEndsIn(filename, ".plt")||StringEndsIn(filename, ".hpgl")) {
|
|
||||||
static HpglFileWriter HpglWriter;
|
|
||||||
ret = &HpglWriter;
|
|
||||||
} else if(StringEndsIn(filename, ".step")||StringEndsIn(filename, ".stp")) {
|
|
||||||
static Step2dFileWriter Step2dWriter;
|
|
||||||
ret = &Step2dWriter;
|
|
||||||
} else if(StringEndsIn(filename, ".txt")) {
|
|
||||||
static GCodeFileWriter GCodeWriter;
|
|
||||||
ret = &GCodeWriter;
|
|
||||||
} else {
|
|
||||||
Error("Can't identify output file type from file extension of "
|
|
||||||
"filename '%s'; try "
|
|
||||||
".step, .stp, .dxf, .svg, .plt, .hpgl, .pdf, .txt, "
|
|
||||||
".eps, or .ps.",
|
|
||||||
filename);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if(!f) {
|
|
||||||
Error("Couldn't write to '%s'", filename);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ret->f = f;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VectorFileWriter::Output(SBezierLoopSetSet *sblss, SMesh *sm) {
|
|
||||||
STriangle *tr;
|
|
||||||
SBezier *b;
|
|
||||||
|
|
||||||
// First calculate the bounding box.
|
|
||||||
ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
|
||||||
ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
|
|
||||||
if(sm) {
|
|
||||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
|
||||||
(tr->a).MakeMaxMin(&ptMax, &ptMin);
|
|
||||||
(tr->b).MakeMaxMin(&ptMax, &ptMin);
|
|
||||||
(tr->c).MakeMaxMin(&ptMax, &ptMin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(sblss) {
|
|
||||||
SBezierLoopSet *sbls;
|
|
||||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
|
||||||
SBezierLoop *sbl;
|
|
||||||
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
|
|
||||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
|
||||||
for(int i = 0; i <= b->deg; i++) {
|
|
||||||
(b->ctrl[i]).MakeMaxMin(&ptMax, &ptMin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// And now we compute the canvas size.
|
|
||||||
double s = 1.0 / SS.exportScale;
|
|
||||||
if(SS.exportCanvasSizeAuto) {
|
|
||||||
// It's based on the calculated bounding box; we grow it along each
|
|
||||||
// boundary by the specified amount.
|
|
||||||
ptMin.x -= s*SS.exportMargin.left;
|
|
||||||
ptMax.x += s*SS.exportMargin.right;
|
|
||||||
ptMin.y -= s*SS.exportMargin.bottom;
|
|
||||||
ptMax.y += s*SS.exportMargin.top;
|
|
||||||
} else {
|
|
||||||
ptMin.x = -(s*SS.exportCanvas.dx);
|
|
||||||
ptMin.y = -(s*SS.exportCanvas.dy);
|
|
||||||
ptMax.x = ptMin.x + (s*SS.exportCanvas.width);
|
|
||||||
ptMax.y = ptMin.y + (s*SS.exportCanvas.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
StartFile();
|
|
||||||
if(sm && SS.exportShadedTriangles) {
|
|
||||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
|
||||||
Triangle(tr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(sblss) {
|
|
||||||
SBezierLoopSet *sbls;
|
|
||||||
for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) {
|
|
||||||
SBezierLoop *sbl;
|
|
||||||
sbl = sbls->l.First();
|
|
||||||
if(!sbl) continue;
|
|
||||||
b = sbl->l.First();
|
|
||||||
if(!b || !Style::Exportable(b->auxA)) continue;
|
|
||||||
|
|
||||||
hStyle hs = { b->auxA };
|
|
||||||
Style *stl = Style::Get(hs);
|
|
||||||
double lineWidth = Style::WidthMm(b->auxA)*s;
|
|
||||||
DWORD strokeRgb = Style::Color(hs, true);
|
|
||||||
DWORD fillRgb = Style::FillColor(hs, true);
|
|
||||||
|
|
||||||
StartPath(strokeRgb, lineWidth, stl->filled, fillRgb);
|
|
||||||
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
|
|
||||||
for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) {
|
|
||||||
Bezier(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FinishPath(strokeRgb, lineWidth, stl->filled, fillRgb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FinishAndCloseFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VectorFileWriter::BezierAsPwl(SBezier *sb) {
|
|
||||||
List<Vector> lv;
|
|
||||||
ZERO(&lv);
|
|
||||||
sb->MakePwlInto(&lv, SS.ChordTolMm() / SS.exportScale);
|
|
||||||
int i;
|
|
||||||
for(i = 1; i < lv.n; i++) {
|
|
||||||
SBezier sb = SBezier::From(lv.elem[i-1], lv.elem[i]);
|
|
||||||
Bezier(&sb);
|
|
||||||
}
|
|
||||||
lv.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VectorFileWriter::BezierAsNonrationalCubic(SBezier *sb, int depth) {
|
|
||||||
Vector t0 = sb->TangentAt(0), t1 = sb->TangentAt(1);
|
|
||||||
// The curve is correct, and the first derivatives are correct, at the
|
|
||||||
// endpoints.
|
|
||||||
SBezier bnr = SBezier::From(
|
|
||||||
sb->Start(),
|
|
||||||
sb->Start().Plus(t0.ScaledBy(1.0/3)),
|
|
||||||
sb->Finish().Minus(t1.ScaledBy(1.0/3)),
|
|
||||||
sb->Finish());
|
|
||||||
|
|
||||||
double tol = SS.ChordTolMm() / SS.exportScale;
|
|
||||||
// Arbitrary choice, but make it a little finer than pwl tolerance since
|
|
||||||
// it should be easier to achieve that with the smooth curves.
|
|
||||||
tol /= 2;
|
|
||||||
|
|
||||||
bool closeEnough = true;
|
|
||||||
int i;
|
|
||||||
for(i = 1; i <= 3; i++) {
|
|
||||||
double t = i/4.0;
|
|
||||||
Vector p0 = sb->PointAt(t),
|
|
||||||
pn = bnr.PointAt(t);
|
|
||||||
double d = (p0.Minus(pn)).Magnitude();
|
|
||||||
if(d > tol) {
|
|
||||||
closeEnough = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(closeEnough || depth > 3) {
|
|
||||||
Bezier(&bnr);
|
|
||||||
} else {
|
|
||||||
SBezier bef, aft;
|
|
||||||
sb->SplitAt(0.5, &bef, &aft);
|
|
||||||
BezierAsNonrationalCubic(&bef, depth+1);
|
|
||||||
BezierAsNonrationalCubic(&aft, depth+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Export a triangle mesh, in the requested format.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void SolveSpace::ExportMeshTo(char *filename) {
|
|
||||||
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
|
||||||
if(m->IsEmpty()) {
|
|
||||||
Error("Active group mesh is empty; nothing to export.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if(!f) {
|
|
||||||
Error("Couldn't write to '%s'", filename);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(StringEndsIn(filename, ".stl")) {
|
|
||||||
ExportMeshAsStlTo(f, m);
|
|
||||||
} else if(StringEndsIn(filename, ".obj")) {
|
|
||||||
ExportMeshAsObjTo(f, m);
|
|
||||||
} else {
|
|
||||||
Error("Can't identify output file type from file extension of "
|
|
||||||
"filename '%s'; try .stl, .obj.", filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Export the mesh as an STL file; it should always be vertex-to-vertex and
|
|
||||||
// not self-intersecting, so not much to do.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void SolveSpace::ExportMeshAsStlTo(FILE *f, SMesh *sm) {
|
|
||||||
char str[80];
|
|
||||||
memset(str, 0, sizeof(str));
|
|
||||||
strcpy(str, "STL exported mesh");
|
|
||||||
fwrite(str, 1, 80, f);
|
|
||||||
|
|
||||||
DWORD n = sm->l.n;
|
|
||||||
fwrite(&n, 4, 1, f);
|
|
||||||
|
|
||||||
double s = SS.exportScale;
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < sm->l.n; i++) {
|
|
||||||
STriangle *tr = &(sm->l.elem[i]);
|
|
||||||
Vector n = tr->Normal().WithMagnitude(1);
|
|
||||||
float w;
|
|
||||||
w = (float)n.x; fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)n.y; fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)n.z; fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->a.x)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->a.y)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->a.z)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->b.x)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->b.y)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->b.z)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->c.x)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->c.y)/s); fwrite(&w, 4, 1, f);
|
|
||||||
w = (float)((tr->c.z)/s); fwrite(&w, 4, 1, f);
|
|
||||||
fputc(0, f);
|
|
||||||
fputc(0, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Export the mesh as Wavefront OBJ format. This requires us to reduce all the
|
|
||||||
// identical vertices to the same identifier, so do that first.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void SolveSpace::ExportMeshAsObjTo(FILE *f, SMesh *sm) {
|
|
||||||
SPointList spl;
|
|
||||||
ZERO(&spl);
|
|
||||||
STriangle *tr;
|
|
||||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
|
||||||
spl.IncrementTagFor(tr->a);
|
|
||||||
spl.IncrementTagFor(tr->b);
|
|
||||||
spl.IncrementTagFor(tr->c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output all the vertices.
|
|
||||||
SPoint *sp;
|
|
||||||
for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) {
|
|
||||||
fprintf(f, "v %.10f %.10f %.10f\r\n",
|
|
||||||
sp->p.x / SS.exportScale,
|
|
||||||
sp->p.y / SS.exportScale,
|
|
||||||
sp->p.z / SS.exportScale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// And now all the triangular faces, in terms of those vertices. The
|
|
||||||
// file format counts from 1, not 0.
|
|
||||||
for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) {
|
|
||||||
fprintf(f, "f %d %d %d\r\n",
|
|
||||||
spl.IndexForPoint(tr->a) + 1,
|
|
||||||
spl.IndexForPoint(tr->b) + 1,
|
|
||||||
spl.IndexForPoint(tr->c) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
spl.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Export a view of the model as an image; we just take a screenshot, by
|
|
||||||
// rendering the view in the usual way and then copying the pixels.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void SolveSpace::ExportAsPngTo(char *filename) {
|
|
||||||
int w = (int)SS.GW.width, h = (int)SS.GW.height;
|
|
||||||
// No guarantee that the back buffer contains anything valid right now,
|
|
||||||
// so repaint the scene. And hide the toolbar too.
|
|
||||||
int prevShowToolbar = SS.showToolbar;
|
|
||||||
SS.showToolbar = false;
|
|
||||||
SS.GW.Paint();
|
|
||||||
SS.showToolbar = prevShowToolbar;
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if(!f) goto err;
|
|
||||||
|
|
||||||
png_struct *png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
||||||
NULL, NULL, NULL);
|
|
||||||
if(!png_ptr) goto err;
|
|
||||||
|
|
||||||
png_info *info_ptr = png_create_info_struct(png_ptr);
|
|
||||||
if(!png_ptr) goto err;
|
|
||||||
|
|
||||||
if(setjmp(png_jmpbuf(png_ptr))) goto err;
|
|
||||||
|
|
||||||
png_init_io(png_ptr, f);
|
|
||||||
|
|
||||||
// glReadPixels wants to align things on 4-boundaries, and there's 3
|
|
||||||
// bytes per pixel. As long as the row width is divisible by 4, all
|
|
||||||
// works out.
|
|
||||||
w &= ~3; h &= ~3;
|
|
||||||
|
|
||||||
png_set_IHDR(png_ptr, info_ptr, w, h,
|
|
||||||
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
|
||||||
PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
|
|
||||||
|
|
||||||
png_write_info(png_ptr, info_ptr);
|
|
||||||
|
|
||||||
// Get the pixel data from the framebuffer
|
|
||||||
BYTE *pixels = (BYTE *)AllocTemporary(3*w*h);
|
|
||||||
BYTE **rowptrs = (BYTE **)AllocTemporary(h*sizeof(BYTE *));
|
|
||||||
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
|
|
||||||
|
|
||||||
int y;
|
|
||||||
for(y = 0; y < h; y++) {
|
|
||||||
// gl puts the origin at lower left, but png puts it top left
|
|
||||||
rowptrs[y] = pixels + ((h - 1) - y)*(3*w);
|
|
||||||
}
|
|
||||||
png_write_image(png_ptr, rowptrs);
|
|
||||||
|
|
||||||
png_write_end(png_ptr, info_ptr);
|
|
||||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
|
|
||||||
err:
|
|
||||||
Error("Error writing PNG file '%s'", filename);
|
|
||||||
if(f) fclose(f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
713
exportvector.cpp
|
@ -1,713 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// The file format-specific stuff for all of the 2d vector output formats.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines for DXF export
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void DxfFileWriter::StartFile(void) {
|
|
||||||
// Some software, like Adobe Illustrator, insists on a header.
|
|
||||||
fprintf(f,
|
|
||||||
" 999\r\n"
|
|
||||||
"file created by SolveSpace\r\n"
|
|
||||||
" 0\r\n"
|
|
||||||
"SECTION\r\n"
|
|
||||||
" 2\r\n"
|
|
||||||
"HEADER\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$ACADVER\r\n"
|
|
||||||
" 1\r\n"
|
|
||||||
"AC1006\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$ANGDIR\r\n"
|
|
||||||
" 70\r\n"
|
|
||||||
"0\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$AUNITS\r\n"
|
|
||||||
" 70\r\n"
|
|
||||||
"0\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$AUPREC\r\n"
|
|
||||||
" 70\r\n"
|
|
||||||
"0\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$INSBASE\r\n"
|
|
||||||
" 10\r\n"
|
|
||||||
"0.0\r\n"
|
|
||||||
" 20\r\n"
|
|
||||||
"0.0\r\n"
|
|
||||||
" 30\r\n"
|
|
||||||
"0.0\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$EXTMIN\r\n"
|
|
||||||
" 10\r\n"
|
|
||||||
"0.0\r\n"
|
|
||||||
" 20\r\n"
|
|
||||||
"0.0\r\n"
|
|
||||||
" 9\r\n"
|
|
||||||
"$EXTMAX\r\n"
|
|
||||||
" 10\r\n"
|
|
||||||
"10000.0\r\n"
|
|
||||||
" 20\r\n"
|
|
||||||
"10000.0\r\n"
|
|
||||||
" 0\r\n"
|
|
||||||
"ENDSEC\r\n");
|
|
||||||
|
|
||||||
// Then start the entities.
|
|
||||||
fprintf(f,
|
|
||||||
" 0\r\n"
|
|
||||||
"SECTION\r\n"
|
|
||||||
" 2\r\n"
|
|
||||||
"ENTITIES\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DxfFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void DxfFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DxfFileWriter::Triangle(STriangle *tr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void DxfFileWriter::Bezier(SBezier *sb) {
|
|
||||||
Vector c, n = Vector::From(0, 0, 1);
|
|
||||||
double r;
|
|
||||||
if(sb->deg == 1) {
|
|
||||||
fprintf(f,
|
|
||||||
" 0\r\n"
|
|
||||||
"LINE\r\n"
|
|
||||||
" 8\r\n" // Layer code
|
|
||||||
"%d\r\n"
|
|
||||||
" 10\r\n" // xA
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 20\r\n" // yA
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 30\r\n" // zA
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 11\r\n" // xB
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 21\r\n" // yB
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 31\r\n" // zB
|
|
||||||
"%.6f\r\n",
|
|
||||||
0,
|
|
||||||
sb->ctrl[0].x, sb->ctrl[0].y, sb->ctrl[0].z,
|
|
||||||
sb->ctrl[1].x, sb->ctrl[1].y, sb->ctrl[1].z);
|
|
||||||
} else if(sb->IsInPlane(n, 0) && sb->IsCircle(n, &c, &r)) {
|
|
||||||
double theta0 = atan2(sb->ctrl[0].y - c.y, sb->ctrl[0].x - c.x),
|
|
||||||
theta1 = atan2(sb->ctrl[2].y - c.y, sb->ctrl[2].x - c.x),
|
|
||||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
|
||||||
if(dtheta < 0) {
|
|
||||||
SWAP(double, theta0, theta1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f,
|
|
||||||
" 0\r\n"
|
|
||||||
"ARC\r\n"
|
|
||||||
" 8\r\n" // Layer code
|
|
||||||
"%d\r\n"
|
|
||||||
" 10\r\n" // x
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 20\r\n" // y
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 30\r\n" // z
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 40\r\n" // radius
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 50\r\n" // start angle
|
|
||||||
"%.6f\r\n"
|
|
||||||
" 51\r\n" // end angle
|
|
||||||
"%.6f\r\n",
|
|
||||||
0,
|
|
||||||
c.x, c.y, 0.0,
|
|
||||||
r,
|
|
||||||
theta0*180/PI, theta1*180/PI);
|
|
||||||
} else {
|
|
||||||
BezierAsPwl(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DxfFileWriter::FinishAndCloseFile(void) {
|
|
||||||
fprintf(f,
|
|
||||||
" 0\r\n"
|
|
||||||
"ENDSEC\r\n"
|
|
||||||
" 0\r\n"
|
|
||||||
"EOF\r\n" );
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines for EPS output
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void EpsFileWriter::StartFile(void) {
|
|
||||||
fprintf(f,
|
|
||||||
"%%!PS-Adobe-2.0\r\n"
|
|
||||||
"%%%%Creator: SolveSpace\r\n"
|
|
||||||
"%%%%Title: title\r\n"
|
|
||||||
"%%%%Pages: 0\r\n"
|
|
||||||
"%%%%PageOrder: Ascend\r\n"
|
|
||||||
"%%%%BoundingBox: 0 0 %d %d\r\n"
|
|
||||||
"%%%%HiResBoundingBox: 0 0 %.3f %.3f\r\n"
|
|
||||||
"%%%%EndComments\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"gsave\r\n"
|
|
||||||
"\r\n",
|
|
||||||
(int)ceil(MmToPts(ptMax.x - ptMin.x)),
|
|
||||||
(int)ceil(MmToPts(ptMax.y - ptMin.y)),
|
|
||||||
MmToPts(ptMax.x - ptMin.x),
|
|
||||||
MmToPts(ptMax.y - ptMin.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EpsFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
fprintf(f, "newpath\r\n");
|
|
||||||
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
|
||||||
}
|
|
||||||
void EpsFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
fprintf(f, " %.3f setlinewidth\r\n"
|
|
||||||
" %.3f %.3f %.3f setrgbcolor\r\n"
|
|
||||||
" 1 setlinejoin\r\n" // rounded
|
|
||||||
" 1 setlinecap\r\n" // rounded
|
|
||||||
" gsave stroke grestore\r\n",
|
|
||||||
MmToPts(lineWidth),
|
|
||||||
REDf(strokeRgb), GREENf(strokeRgb), BLUEf(strokeRgb));
|
|
||||||
if(filled) {
|
|
||||||
fprintf(f, " %.3f %.3f %.3f setrgbcolor\r\n"
|
|
||||||
" gsave fill grestore\r\n",
|
|
||||||
REDf(fillRgb), GREENf(fillRgb), BLUEf(fillRgb));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EpsFileWriter::MaybeMoveTo(Vector st, Vector fi) {
|
|
||||||
if(!prevPt.Equals(st)) {
|
|
||||||
fprintf(f, " %.3f %.3f moveto\r\n",
|
|
||||||
MmToPts(st.x - ptMin.x), MmToPts(st.y - ptMin.y));
|
|
||||||
}
|
|
||||||
prevPt = fi;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EpsFileWriter::Triangle(STriangle *tr) {
|
|
||||||
fprintf(f,
|
|
||||||
"%.3f %.3f %.3f setrgbcolor\r\n"
|
|
||||||
"newpath\r\n"
|
|
||||||
" %.3f %.3f moveto\r\n"
|
|
||||||
" %.3f %.3f lineto\r\n"
|
|
||||||
" %.3f %.3f lineto\r\n"
|
|
||||||
" closepath\r\n"
|
|
||||||
"gsave fill grestore\r\n",
|
|
||||||
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
|
||||||
MmToPts(tr->a.x - ptMin.x), MmToPts(tr->a.y - ptMin.y),
|
|
||||||
MmToPts(tr->b.x - ptMin.x), MmToPts(tr->b.y - ptMin.y),
|
|
||||||
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
|
|
||||||
|
|
||||||
// same issue with cracks, stroke it to avoid them
|
|
||||||
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
|
||||||
fprintf(f,
|
|
||||||
"1 setlinejoin\r\n"
|
|
||||||
"1 setlinecap\r\n"
|
|
||||||
"%.3f setlinewidth\r\n"
|
|
||||||
"gsave stroke grestore\r\n",
|
|
||||||
MmToPts(sw));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EpsFileWriter::Bezier(SBezier *sb) {
|
|
||||||
Vector c, n = Vector::From(0, 0, 1);
|
|
||||||
double r;
|
|
||||||
if(sb->deg == 1) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
|
|
||||||
fprintf(f, " %.3f %.3f lineto\r\n",
|
|
||||||
MmToPts(sb->ctrl[1].x - ptMin.x),
|
|
||||||
MmToPts(sb->ctrl[1].y - ptMin.y));
|
|
||||||
} else if(sb->IsCircle(n, &c, &r)) {
|
|
||||||
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
|
|
||||||
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
|
|
||||||
theta1 = atan2(p1.y - c.y, p1.x - c.x),
|
|
||||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
|
||||||
MaybeMoveTo(p0, p1);
|
|
||||||
fprintf(f,
|
|
||||||
" %.3f %.3f %.3f %.3f %.3f %s\r\n",
|
|
||||||
MmToPts(c.x - ptMin.x), MmToPts(c.y - ptMin.y),
|
|
||||||
MmToPts(r),
|
|
||||||
theta0*180/PI, theta1*180/PI,
|
|
||||||
dtheta < 0 ? "arcn" : "arc");
|
|
||||||
} else if(sb->deg == 3 && !sb->IsRational()) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
|
|
||||||
fprintf(f,
|
|
||||||
" %.3f %.3f %.3f %.3f %.3f %.3f curveto\r\n",
|
|
||||||
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y),
|
|
||||||
MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y),
|
|
||||||
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y));
|
|
||||||
} else {
|
|
||||||
BezierAsNonrationalCubic(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EpsFileWriter::FinishAndCloseFile(void) {
|
|
||||||
fprintf(f,
|
|
||||||
"\r\n"
|
|
||||||
"grestore\r\n"
|
|
||||||
"\r\n");
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines for PDF output, some extra complexity because we have to generate
|
|
||||||
// a correct xref table.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void PdfFileWriter::StartFile(void) {
|
|
||||||
if((ptMax.x - ptMin.x) > 200*25.4 ||
|
|
||||||
(ptMax.y - ptMin.y) > 200*25.4)
|
|
||||||
{
|
|
||||||
Message("PDF page size exceeds 200 by 200 inches; many viewers may "
|
|
||||||
"reject this file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f,
|
|
||||||
"%%PDF-1.1\r\n"
|
|
||||||
"%%%c%c%c%c\r\n",
|
|
||||||
0xe2, 0xe3, 0xcf, 0xd3);
|
|
||||||
|
|
||||||
xref[1] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"1 0 obj\r\n"
|
|
||||||
" << /Type /Catalog\r\n"
|
|
||||||
" /Outlines 2 0 R\r\n"
|
|
||||||
" /Pages 3 0 R\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
"endobj\r\n");
|
|
||||||
|
|
||||||
xref[2] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"2 0 obj\r\n"
|
|
||||||
" << /Type /Outlines\r\n"
|
|
||||||
" /Count 0\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
"endobj\r\n");
|
|
||||||
|
|
||||||
xref[3] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"3 0 obj\r\n"
|
|
||||||
" << /Type /Pages\r\n"
|
|
||||||
" /Kids [4 0 R]\r\n"
|
|
||||||
" /Count 1\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
"endobj\r\n");
|
|
||||||
|
|
||||||
xref[4] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"4 0 obj\r\n"
|
|
||||||
" << /Type /Page\r\n"
|
|
||||||
" /Parent 3 0 R\r\n"
|
|
||||||
" /MediaBox [0 0 %.3f %.3f]\r\n"
|
|
||||||
" /Contents 5 0 R\r\n"
|
|
||||||
" /Resources << /ProcSet 7 0 R\r\n"
|
|
||||||
" /Font << /F1 8 0 R >>\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
"endobj\r\n",
|
|
||||||
MmToPts(ptMax.x - ptMin.x),
|
|
||||||
MmToPts(ptMax.y - ptMin.y));
|
|
||||||
|
|
||||||
xref[5] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"5 0 obj\r\n"
|
|
||||||
" << /Length 6 0 R >>\r\n"
|
|
||||||
"stream\r\n");
|
|
||||||
bodyStart = ftell(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PdfFileWriter::FinishAndCloseFile(void) {
|
|
||||||
DWORD bodyEnd = ftell(f);
|
|
||||||
|
|
||||||
fprintf(f,
|
|
||||||
"endstream\r\n"
|
|
||||||
"endobj\r\n");
|
|
||||||
|
|
||||||
xref[6] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"6 0 obj\r\n"
|
|
||||||
" %d\r\n"
|
|
||||||
"endobj\r\n",
|
|
||||||
bodyEnd - bodyStart);
|
|
||||||
|
|
||||||
xref[7] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"7 0 obj\r\n"
|
|
||||||
" [/PDF /Text]\r\n"
|
|
||||||
"endobj\r\n");
|
|
||||||
|
|
||||||
xref[8] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"8 0 obj\r\n"
|
|
||||||
" << /Type /Font\r\n"
|
|
||||||
" /Subtype /Type1\r\n"
|
|
||||||
" /Name /F1\r\n"
|
|
||||||
" /BaseFont /Helvetica\r\n"
|
|
||||||
" /Encoding /WinAnsiEncoding\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
"endobj\r\n");
|
|
||||||
|
|
||||||
xref[9] = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"9 0 obj\r\n"
|
|
||||||
" << /Creator (SolveSpace)\r\n"
|
|
||||||
" >>\r\n");
|
|
||||||
|
|
||||||
DWORD xrefStart = ftell(f);
|
|
||||||
fprintf(f,
|
|
||||||
"xref\r\n"
|
|
||||||
"0 10\r\n"
|
|
||||||
"0000000000 65535 f\r\n");
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for(i = 1; i <= 9; i++) {
|
|
||||||
fprintf(f, "%010d %05d n\r\n", xref[i], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f,
|
|
||||||
"\r\n"
|
|
||||||
"trailer\r\n"
|
|
||||||
" << /Size 10\r\n"
|
|
||||||
" /Root 1 0 R\r\n"
|
|
||||||
" /Info 9 0 R\r\n"
|
|
||||||
" >>\r\n"
|
|
||||||
"startxref\r\n"
|
|
||||||
"%d\r\n"
|
|
||||||
"%%%%EOF\r\n",
|
|
||||||
xrefStart);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void PdfFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
fprintf(f, "1 J 1 j " // round endcaps and joins
|
|
||||||
"%.3f w "
|
|
||||||
"%.3f %.3f %.3f RG\r\n",
|
|
||||||
MmToPts(lineWidth),
|
|
||||||
REDf(strokeRgb), GREENf(strokeRgb), BLUEf(strokeRgb));
|
|
||||||
if(filled) {
|
|
||||||
fprintf(f, "%.3f %.3f %.3f rg\r\n",
|
|
||||||
REDf(fillRgb), GREENf(fillRgb), BLUEf(fillRgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
|
||||||
}
|
|
||||||
void PdfFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
if(filled) {
|
|
||||||
fprintf(f, "b\r\n");
|
|
||||||
} else {
|
|
||||||
fprintf(f, "S\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PdfFileWriter::MaybeMoveTo(Vector st, Vector fi) {
|
|
||||||
if(!prevPt.Equals(st)) {
|
|
||||||
fprintf(f, "%.3f %.3f m\r\n",
|
|
||||||
MmToPts(st.x - ptMin.x), MmToPts(st.y - ptMin.y));
|
|
||||||
}
|
|
||||||
prevPt = fi;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PdfFileWriter::Triangle(STriangle *tr) {
|
|
||||||
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
|
||||||
|
|
||||||
fprintf(f,
|
|
||||||
"1 J 1 j\r\n"
|
|
||||||
"%.3f %.3f %.3f RG\r\n"
|
|
||||||
"%.3f %.3f %.3f rg\r\n"
|
|
||||||
"%.3f w\r\n"
|
|
||||||
"%.3f %.3f m\r\n"
|
|
||||||
"%.3f %.3f l\r\n"
|
|
||||||
"%.3f %.3f l\r\n"
|
|
||||||
"b\r\n",
|
|
||||||
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
|
||||||
REDf(tr->meta.color), GREENf(tr->meta.color), BLUEf(tr->meta.color),
|
|
||||||
MmToPts(sw),
|
|
||||||
MmToPts(tr->a.x - ptMin.x), MmToPts(tr->a.y - ptMin.y),
|
|
||||||
MmToPts(tr->b.x - ptMin.x), MmToPts(tr->b.y - ptMin.y),
|
|
||||||
MmToPts(tr->c.x - ptMin.x), MmToPts(tr->c.y - ptMin.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PdfFileWriter::Bezier(SBezier *sb) {
|
|
||||||
if(sb->deg == 1) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
|
|
||||||
fprintf(f,
|
|
||||||
"%.3f %.3f l\r\n",
|
|
||||||
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y));
|
|
||||||
} else if(sb->deg == 3 && !sb->IsRational()) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
|
|
||||||
fprintf(f,
|
|
||||||
"%.3f %.3f %.3f %.3f %.3f %.3f c\r\n",
|
|
||||||
MmToPts(sb->ctrl[1].x - ptMin.x), MmToPts(sb->ctrl[1].y - ptMin.y),
|
|
||||||
MmToPts(sb->ctrl[2].x - ptMin.x), MmToPts(sb->ctrl[2].y - ptMin.y),
|
|
||||||
MmToPts(sb->ctrl[3].x - ptMin.x), MmToPts(sb->ctrl[3].y - ptMin.y));
|
|
||||||
} else {
|
|
||||||
BezierAsNonrationalCubic(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines for SVG output
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void SvgFileWriter::StartFile(void) {
|
|
||||||
fprintf(f,
|
|
||||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" "
|
|
||||||
"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\r\n"
|
|
||||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
|
||||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
|
|
||||||
"width='%.3fmm' height='%.3fmm' "
|
|
||||||
"viewBox=\"0 0 %.3f %.3f\">\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"<title>Exported SVG</title>\r\n"
|
|
||||||
"\r\n",
|
|
||||||
(ptMax.x - ptMin.x) + 1, (ptMax.y - ptMin.y) + 1,
|
|
||||||
(ptMax.x - ptMin.x) + 1, (ptMax.y - ptMin.y) + 1);
|
|
||||||
// A little bit of extra space for the stroke width.
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvgFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
fprintf(f, "<path d='");
|
|
||||||
prevPt = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
|
||||||
}
|
|
||||||
void SvgFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
char fill[100];
|
|
||||||
if(filled) {
|
|
||||||
sprintf(fill, "#%02x%02x%02x",
|
|
||||||
RED(fillRgb), GREEN(fillRgb), BLUE(fillRgb));
|
|
||||||
} else {
|
|
||||||
strcpy(fill, "none");
|
|
||||||
}
|
|
||||||
fprintf(f, "' stroke-width='%.3f' stroke='#%02x%02x%02x' "
|
|
||||||
"stroke-linecap='round' stroke-linejoin='round' "
|
|
||||||
"fill='%s' />\r\n",
|
|
||||||
lineWidth, RED(strokeRgb), GREEN(strokeRgb), BLUE(strokeRgb),
|
|
||||||
fill);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvgFileWriter::MaybeMoveTo(Vector st, Vector fi) {
|
|
||||||
// SVG uses a coordinate system with the origin at top left, +y down
|
|
||||||
if(!prevPt.Equals(st)) {
|
|
||||||
fprintf(f, "M%.3f %.3f ", (st.x - ptMin.x), (ptMax.y - st.y));
|
|
||||||
}
|
|
||||||
prevPt = fi;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvgFileWriter::Triangle(STriangle *tr) {
|
|
||||||
// crispEdges turns of anti-aliasing, which tends to cause hairline
|
|
||||||
// cracks between triangles; but there still is some cracking, so
|
|
||||||
// specify a stroke width too, hope for around a pixel
|
|
||||||
double sw = max(ptMax.x - ptMin.x, ptMax.y - ptMin.y) / 1000;
|
|
||||||
fprintf(f,
|
|
||||||
"<polygon points='%.3f,%.3f %.3f,%.3f %.3f,%.3f' "
|
|
||||||
"stroke='#%02x%02x%02x' stroke-width='%.3f' "
|
|
||||||
"style='fill:#%02x%02x%02x' shape-rendering='crispEdges'/>\r\n",
|
|
||||||
(tr->a.x - ptMin.x), (ptMax.y - tr->a.y),
|
|
||||||
(tr->b.x - ptMin.x), (ptMax.y - tr->b.y),
|
|
||||||
(tr->c.x - ptMin.x), (ptMax.y - tr->c.y),
|
|
||||||
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color),
|
|
||||||
sw,
|
|
||||||
RED(tr->meta.color), GREEN(tr->meta.color), BLUE(tr->meta.color));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvgFileWriter::Bezier(SBezier *sb) {
|
|
||||||
Vector c, n = Vector::From(0, 0, 1);
|
|
||||||
double r;
|
|
||||||
if(sb->deg == 1) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[1]);
|
|
||||||
fprintf(f, "L%.3f,%.3f ",
|
|
||||||
(sb->ctrl[1].x - ptMin.x), (ptMax.y - sb->ctrl[1].y));
|
|
||||||
} else if(sb->IsCircle(n, &c, &r)) {
|
|
||||||
Vector p0 = sb->ctrl[0], p1 = sb->ctrl[2];
|
|
||||||
double theta0 = atan2(p0.y - c.y, p0.x - c.x),
|
|
||||||
theta1 = atan2(p1.y - c.y, p1.x - c.x),
|
|
||||||
dtheta = WRAP_SYMMETRIC(theta1 - theta0, 2*PI);
|
|
||||||
// The arc must be less than 180 degrees, or else it couldn't have
|
|
||||||
// been represented as a single rational Bezier. So large-arc-flag
|
|
||||||
// must be false. sweep-flag is determined by the sign of dtheta.
|
|
||||||
// Note that clockwise and counter-clockwise are backwards in SVG's
|
|
||||||
// mirrored csys.
|
|
||||||
MaybeMoveTo(p0, p1);
|
|
||||||
fprintf(f, "A%.3f,%.3f 0 0,%d %.3f,%.3f ",
|
|
||||||
r, r,
|
|
||||||
(dtheta < 0) ? 1 : 0,
|
|
||||||
p1.x - ptMin.x, ptMax.y - p1.y);
|
|
||||||
} else if(!sb->IsRational()) {
|
|
||||||
if(sb->deg == 2) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[2]);
|
|
||||||
fprintf(f, "Q%.3f,%.3f %.3f,%.3f ",
|
|
||||||
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
|
|
||||||
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y);
|
|
||||||
} else if(sb->deg == 3) {
|
|
||||||
MaybeMoveTo(sb->ctrl[0], sb->ctrl[3]);
|
|
||||||
fprintf(f, "C%.3f,%.3f %.3f,%.3f %.3f,%.3f ",
|
|
||||||
sb->ctrl[1].x - ptMin.x, ptMax.y - sb->ctrl[1].y,
|
|
||||||
sb->ctrl[2].x - ptMin.x, ptMax.y - sb->ctrl[2].y,
|
|
||||||
sb->ctrl[3].x - ptMin.x, ptMax.y - sb->ctrl[3].y);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BezierAsNonrationalCubic(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SvgFileWriter::FinishAndCloseFile(void) {
|
|
||||||
fprintf(f, "\r\n</svg>\r\n");
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines for HPGL output
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
double HpglFileWriter::MmToHpglUnits(double mm) {
|
|
||||||
return mm*40;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HpglFileWriter::StartFile(void) {
|
|
||||||
fprintf(f, "IN;\r\n");
|
|
||||||
fprintf(f, "SP1;\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void HpglFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void HpglFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void HpglFileWriter::Triangle(STriangle *tr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void HpglFileWriter::Bezier(SBezier *sb) {
|
|
||||||
if(sb->deg == 1) {
|
|
||||||
fprintf(f, "PU%d,%d;\r\n",
|
|
||||||
(int)MmToHpglUnits(sb->ctrl[0].x),
|
|
||||||
(int)MmToHpglUnits(sb->ctrl[0].y));
|
|
||||||
fprintf(f, "PD%d,%d;\r\n",
|
|
||||||
(int)MmToHpglUnits(sb->ctrl[1].x),
|
|
||||||
(int)MmToHpglUnits(sb->ctrl[1].y));
|
|
||||||
} else {
|
|
||||||
BezierAsPwl(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HpglFileWriter::FinishAndCloseFile(void) {
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines for G Code output. Slightly complicated by our ability to generate
|
|
||||||
// multiple passes, and to specify the feeds and depth; those parameters get
|
|
||||||
// set in the configuration screen.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void GCodeFileWriter::StartFile(void) {
|
|
||||||
ZERO(&sel);
|
|
||||||
}
|
|
||||||
void GCodeFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void GCodeFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void GCodeFileWriter::Triangle(STriangle *tr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeFileWriter::Bezier(SBezier *sb) {
|
|
||||||
if(sb->deg == 1) {
|
|
||||||
sel.AddEdge(sb->ctrl[0], sb->ctrl[1]);
|
|
||||||
} else {
|
|
||||||
BezierAsPwl(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeFileWriter::FinishAndCloseFile(void) {
|
|
||||||
SPolygon sp;
|
|
||||||
ZERO(&sp);
|
|
||||||
sel.AssemblePolygon(&sp, NULL);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < SS.gCode.passes; i++) {
|
|
||||||
double depth = (SS.gCode.depth / SS.gCode.passes)*(i+1);
|
|
||||||
|
|
||||||
SContour *sc;
|
|
||||||
for(sc = sp.l.First(); sc; sc = sp.l.NextAfter(sc)) {
|
|
||||||
if(sc->l.n < 2) continue;
|
|
||||||
|
|
||||||
SPoint *pt = sc->l.First();
|
|
||||||
fprintf(f, "G00 X%s Y%s\r\n",
|
|
||||||
SS.MmToString(pt->p.x), SS.MmToString(pt->p.y));
|
|
||||||
fprintf(f, "G01 Z%s F%s\r\n",
|
|
||||||
SS.MmToString(depth), SS.MmToString(SS.gCode.plungeFeed));
|
|
||||||
|
|
||||||
pt = sc->l.NextAfter(pt);
|
|
||||||
for(; pt; pt = sc->l.NextAfter(pt)) {
|
|
||||||
fprintf(f, "G01 X%s Y%s F%s\r\n",
|
|
||||||
SS.MmToString(pt->p.x), SS.MmToString(pt->p.y),
|
|
||||||
SS.MmToString(SS.gCode.feed));
|
|
||||||
}
|
|
||||||
// Move up to a clearance plane 5mm above the work.
|
|
||||||
fprintf(f, "G00 Z%s\r\n",
|
|
||||||
SS.MmToString(SS.gCode.depth < 0 ? +5 : -5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sp.Clear();
|
|
||||||
sel.Clear();
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routine for STEP output; just a wrapper around the general STEP stuff that
|
|
||||||
// can also be used for surfaces or 3d curves.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void Step2dFileWriter::StartFile(void) {
|
|
||||||
ZERO(&sfw);
|
|
||||||
sfw.f = f;
|
|
||||||
sfw.WriteHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Step2dFileWriter::Triangle(STriangle *tr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Step2dFileWriter::StartPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void Step2dFileWriter::FinishPath(DWORD strokeRgb, double lineWidth,
|
|
||||||
bool filled, DWORD fillRgb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Step2dFileWriter::Bezier(SBezier *sb) {
|
|
||||||
int c = sfw.ExportCurve(sb);
|
|
||||||
sfw.curves.Add(&c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Step2dFileWriter::FinishAndCloseFile(void) {
|
|
||||||
sfw.WriteWireframe();
|
|
||||||
sfw.WriteFooter();
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
133
exposed/CDemo.c
|
@ -1,18 +1,23 @@
|
||||||
//-----------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------
|
||||||
// Some sample code for slvs.dll. We draw some geometric entities, provide
|
* Some sample code for slvs.dll. We draw some geometric entities, provide
|
||||||
// initial guesses for their positions, and then constrain them. The solver
|
* initial guesses for their positions, and then constrain them. The solver
|
||||||
// calculates their new positions, in order to satisfy the constraints.
|
* calculates their new positions, in order to satisfy the constraints.
|
||||||
//
|
*
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
* Copyright 2008-2013 Jonathan Westhues.
|
||||||
//-----------------------------------------------------------------------------
|
*---------------------------------------------------------------------------*/
|
||||||
#include <windows.h>
|
#ifdef WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "slvs.h"
|
#include <slvs.h>
|
||||||
|
|
||||||
Slvs_System sys;
|
static Slvs_System sys;
|
||||||
|
|
||||||
void *CheckMalloc(size_t n)
|
static void *CheckMalloc(size_t n)
|
||||||
{
|
{
|
||||||
void *r = malloc(n);
|
void *r = malloc(n);
|
||||||
if(!r) {
|
if(!r) {
|
||||||
|
@ -22,30 +27,30 @@ void *CheckMalloc(size_t n)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------
|
||||||
// An example of a constraint in 3d. We create a single group, with some
|
* An example of a constraint in 3d. We create a single group, with some
|
||||||
// entities and constraints.
|
* entities and constraints.
|
||||||
//-----------------------------------------------------------------------------
|
*---------------------------------------------------------------------------*/
|
||||||
void Example3d(void)
|
void Example3d()
|
||||||
{
|
{
|
||||||
// This will contain a single group, which will arbitrarily number 1.
|
/* This will contain a single group, which will arbitrarily number 1. */
|
||||||
int g = 1;
|
Slvs_hGroup g = 1;
|
||||||
|
|
||||||
// A point, initially at (x y z) = (10 10 10)
|
/* A point, initially at (x y z) = (10 10 10) */
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(1, g, 10.0);
|
sys.param[sys.params++] = Slvs_MakeParam(1, g, 10.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(2, g, 10.0);
|
sys.param[sys.params++] = Slvs_MakeParam(2, g, 10.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(3, g, 10.0);
|
sys.param[sys.params++] = Slvs_MakeParam(3, g, 10.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
|
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
|
||||||
// and a second point at (20 20 20)
|
/* and a second point at (20 20 20) */
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(4, g, 20.0);
|
sys.param[sys.params++] = Slvs_MakeParam(4, g, 20.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(5, g, 20.0);
|
sys.param[sys.params++] = Slvs_MakeParam(5, g, 20.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
|
sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
|
sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
|
||||||
// and a line segment connecting them.
|
/* and a line segment connecting them. */
|
||||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
||||||
SLVS_FREE_IN_3D, 101, 102);
|
SLVS_FREE_IN_3D, 101, 102);
|
||||||
|
|
||||||
// The distance between the points should be 30.0 units.
|
/* The distance between the points should be 30.0 units. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
1, g,
|
1, g,
|
||||||
SLVS_C_PT_PT_DISTANCE,
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
|
@ -53,13 +58,13 @@ void Example3d(void)
|
||||||
30.0,
|
30.0,
|
||||||
101, 102, 0, 0);
|
101, 102, 0, 0);
|
||||||
|
|
||||||
// Let's tell the solver to keep the second point as close to constant
|
/* Let's tell the solver to keep the second point as close to constant
|
||||||
// as possible, instead moving the first point.
|
* as possible, instead moving the first point. */
|
||||||
sys.dragged[0] = 4;
|
sys.dragged[0] = 4;
|
||||||
sys.dragged[1] = 5;
|
sys.dragged[1] = 5;
|
||||||
sys.dragged[2] = 6;
|
sys.dragged[2] = 6;
|
||||||
|
|
||||||
// Now that we have written our system, we solve.
|
/* Now that we have written our system, we solve. */
|
||||||
Slvs_Solve(&sys, g);
|
Slvs_Solve(&sys, g);
|
||||||
|
|
||||||
if(sys.result == SLVS_RESULT_OKAY) {
|
if(sys.result == SLVS_RESULT_OKAY) {
|
||||||
|
@ -73,25 +78,25 @@ void Example3d(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------
|
||||||
// An example of a constraint in 2d. In our first group, we create a workplane
|
* An example of a constraint in 2d. In our first group, we create a workplane
|
||||||
// along the reference frame's xy plane. In a second group, we create some
|
* along the reference frame's xy plane. In a second group, we create some
|
||||||
// entities in that group and dimension them.
|
* entities in that group and dimension them.
|
||||||
//-----------------------------------------------------------------------------
|
*---------------------------------------------------------------------------*/
|
||||||
void Example2d(void)
|
void Example2d()
|
||||||
{
|
{
|
||||||
int g;
|
Slvs_hGroup g;
|
||||||
double qw, qx, qy, qz;
|
double qw, qx, qy, qz;
|
||||||
|
|
||||||
g = 1;
|
g = 1;
|
||||||
// First, we create our workplane. Its origin corresponds to the origin
|
/* First, we create our workplane. Its origin corresponds to the origin
|
||||||
// of our base frame (x y z) = (0 0 0)
|
* of our base frame (x y z) = (0 0 0) */
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(1, g, 0.0);
|
sys.param[sys.params++] = Slvs_MakeParam(1, g, 0.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(2, g, 0.0);
|
sys.param[sys.params++] = Slvs_MakeParam(2, g, 0.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
|
sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
|
sys.entity[sys.entities++] = Slvs_MakePoint3d(101, g, 1, 2, 3);
|
||||||
// and it is parallel to the xy plane, so it has basis vectors (1 0 0)
|
/* and it is parallel to the xy plane, so it has basis vectors (1 0 0)
|
||||||
// and (0 1 0).
|
* and (0 1 0). */
|
||||||
Slvs_MakeQuaternion(1, 0, 0,
|
Slvs_MakeQuaternion(1, 0, 0,
|
||||||
0, 1, 0, &qw, &qx, &qy, &qz);
|
0, 1, 0, &qw, &qx, &qy, &qz);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(4, g, qw);
|
sys.param[sys.params++] = Slvs_MakeParam(4, g, qw);
|
||||||
|
@ -102,12 +107,12 @@ void Example2d(void)
|
||||||
|
|
||||||
sys.entity[sys.entities++] = Slvs_MakeWorkplane(200, g, 101, 102);
|
sys.entity[sys.entities++] = Slvs_MakeWorkplane(200, g, 101, 102);
|
||||||
|
|
||||||
// Now create a second group. We'll solve group 2, while leaving group 1
|
/* Now create a second group. We'll solve group 2, while leaving group 1
|
||||||
// constant; so the workplane that we've created will be locked down,
|
* constant; so the workplane that we've created will be locked down,
|
||||||
// and the solver can't move it.
|
* and the solver can't move it. */
|
||||||
g = 2;
|
g = 2;
|
||||||
// These points are represented by their coordinates (u v) within the
|
/* These points are represented by their coordinates (u v) within the
|
||||||
// workplane, so they need only two parameters each.
|
* workplane, so they need only two parameters each. */
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(11, g, 10.0);
|
sys.param[sys.params++] = Slvs_MakeParam(11, g, 10.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(12, g, 20.0);
|
sys.param[sys.params++] = Slvs_MakeParam(12, g, 20.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint2d(301, g, 200, 11, 12);
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(301, g, 200, 11, 12);
|
||||||
|
@ -116,11 +121,11 @@ void Example2d(void)
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(14, g, 10.0);
|
sys.param[sys.params++] = Slvs_MakeParam(14, g, 10.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);
|
||||||
|
|
||||||
// And we create a line segment with those endpoints.
|
/* And we create a line segment with those endpoints. */
|
||||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
||||||
200, 301, 302);
|
200, 301, 302);
|
||||||
|
|
||||||
// Now three more points.
|
/* Now three more points. */
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(15, g, 100.0);
|
sys.param[sys.params++] = Slvs_MakeParam(15, g, 100.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(16, g, 120.0);
|
sys.param[sys.params++] = Slvs_MakeParam(16, g, 120.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint2d(303, g, 200, 15, 16);
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(303, g, 200, 15, 16);
|
||||||
|
@ -133,12 +138,12 @@ void Example2d(void)
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(20, g, 115.0);
|
sys.param[sys.params++] = Slvs_MakeParam(20, g, 115.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint2d(305, g, 200, 19, 20);
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(305, g, 200, 19, 20);
|
||||||
|
|
||||||
// And arc, centered at point 303, starting at point 304, ending at
|
/* And arc, centered at point 303, starting at point 304, ending at
|
||||||
// point 305.
|
* point 305. */
|
||||||
sys.entity[sys.entities++] = Slvs_MakeArcOfCircle(401, g, 200, 102,
|
sys.entity[sys.entities++] = Slvs_MakeArcOfCircle(401, g, 200, 102,
|
||||||
303, 304, 305);
|
303, 304, 305);
|
||||||
|
|
||||||
// Now one more point, and a distance
|
/* Now one more point, and a distance */
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(21, g, 200.0);
|
sys.param[sys.params++] = Slvs_MakeParam(21, g, 200.0);
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(22, g, 200.0);
|
sys.param[sys.params++] = Slvs_MakeParam(22, g, 200.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakePoint2d(306, g, 200, 21, 22);
|
sys.entity[sys.entities++] = Slvs_MakePoint2d(306, g, 200, 21, 22);
|
||||||
|
@ -146,13 +151,13 @@ void Example2d(void)
|
||||||
sys.param[sys.params++] = Slvs_MakeParam(23, g, 30.0);
|
sys.param[sys.params++] = Slvs_MakeParam(23, g, 30.0);
|
||||||
sys.entity[sys.entities++] = Slvs_MakeDistance(307, g, 200, 23);
|
sys.entity[sys.entities++] = Slvs_MakeDistance(307, g, 200, 23);
|
||||||
|
|
||||||
// And a complete circle, centered at point 306 with radius equal to
|
/* And a complete circle, centered at point 306 with radius equal to
|
||||||
// distance 307. The normal is 102, the same as our workplane.
|
* distance 307. The normal is 102, the same as our workplane. */
|
||||||
sys.entity[sys.entities++] = Slvs_MakeCircle(402, g, 200,
|
sys.entity[sys.entities++] = Slvs_MakeCircle(402, g, 200,
|
||||||
306, 102, 307);
|
306, 102, 307);
|
||||||
|
|
||||||
|
|
||||||
// The length of our line segment is 30.0 units.
|
/* The length of our line segment is 30.0 units. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
1, g,
|
1, g,
|
||||||
SLVS_C_PT_PT_DISTANCE,
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
|
@ -160,45 +165,46 @@ void Example2d(void)
|
||||||
30.0,
|
30.0,
|
||||||
301, 302, 0, 0);
|
301, 302, 0, 0);
|
||||||
|
|
||||||
// And the distance from our line segment to the origin is 10.0 units.
|
/* And the distance from our line segment to the origin is 10.0 units. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
2, g,
|
2, g,
|
||||||
SLVS_C_PT_LINE_DISTANCE,
|
SLVS_C_PT_LINE_DISTANCE,
|
||||||
200,
|
200,
|
||||||
10.0,
|
10.0,
|
||||||
101, 0, 400, 0);
|
101, 0, 400, 0);
|
||||||
// And the line segment is vertical.
|
/* And the line segment is vertical. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
3, g,
|
3, g,
|
||||||
SLVS_C_VERTICAL,
|
SLVS_C_VERTICAL,
|
||||||
200,
|
200,
|
||||||
0.0,
|
0.0,
|
||||||
0, 0, 400, 0);
|
0, 0, 400, 0);
|
||||||
// And the distance from one endpoint to the origin is 15.0 units.
|
/* And the distance from one endpoint to the origin is 15.0 units. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
4, g,
|
4, g,
|
||||||
SLVS_C_PT_PT_DISTANCE,
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
200,
|
200,
|
||||||
15.0,
|
15.0,
|
||||||
301, 101, 0, 0);
|
301, 101, 0, 0);
|
||||||
/*
|
#if 0
|
||||||
// And same for the other endpoint; so if you add this constraint then
|
/* And same for the other endpoint; so if you add this constraint then
|
||||||
// the sketch is overconstrained and will signal an error.
|
* the sketch is overconstrained and will signal an error. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
5, g,
|
5, g,
|
||||||
SLVS_C_PT_PT_DISTANCE,
|
SLVS_C_PT_PT_DISTANCE,
|
||||||
200,
|
200,
|
||||||
18.0,
|
18.0,
|
||||||
302, 101, 0, 0); */
|
302, 101, 0, 0);
|
||||||
|
#endif /* 0 */
|
||||||
|
|
||||||
// The arc and the circle have equal radius.
|
/* The arc and the circle have equal radius. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
6, g,
|
6, g,
|
||||||
SLVS_C_EQUAL_RADIUS,
|
SLVS_C_EQUAL_RADIUS,
|
||||||
200,
|
200,
|
||||||
0.0,
|
0.0,
|
||||||
0, 0, 401, 402);
|
0, 0, 401, 402);
|
||||||
// The arc has radius 17.0 units.
|
/* The arc has radius 17.0 units. */
|
||||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||||
7, g,
|
7, g,
|
||||||
SLVS_C_DIAMETER,
|
SLVS_C_DIAMETER,
|
||||||
|
@ -206,11 +212,11 @@ void Example2d(void)
|
||||||
17.0*2,
|
17.0*2,
|
||||||
0, 0, 401, 0);
|
0, 0, 401, 0);
|
||||||
|
|
||||||
// If the solver fails, then ask it to report which constraints caused
|
/* If the solver fails, then ask it to report which constraints caused
|
||||||
// the problem.
|
* the problem. */
|
||||||
sys.calculateFaileds = 1;
|
sys.calculateFaileds = 1;
|
||||||
|
|
||||||
// And solve.
|
/* And solve. */
|
||||||
Slvs_Solve(&sys, g);
|
Slvs_Solve(&sys, g);
|
||||||
|
|
||||||
if(sys.result == SLVS_RESULT_OKAY) {
|
if(sys.result == SLVS_RESULT_OKAY) {
|
||||||
|
@ -243,9 +249,8 @@ void Example2d(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main()
|
||||||
{
|
{
|
||||||
memset(&sys, 0, sizeof(sys));
|
|
||||||
sys.param = CheckMalloc(50*sizeof(sys.param[0]));
|
sys.param = CheckMalloc(50*sizeof(sys.param[0]));
|
||||||
sys.entity = CheckMalloc(50*sizeof(sys.entity[0]));
|
sys.entity = CheckMalloc(50*sizeof(sys.entity[0]));
|
||||||
sys.constraint = CheckMalloc(50*sizeof(sys.constraint[0]));
|
sys.constraint = CheckMalloc(50*sizeof(sys.constraint[0]));
|
||||||
|
@ -253,7 +258,7 @@ int main(void)
|
||||||
sys.failed = CheckMalloc(50*sizeof(sys.failed[0]));
|
sys.failed = CheckMalloc(50*sizeof(sys.failed[0]));
|
||||||
sys.faileds = 50;
|
sys.faileds = 50;
|
||||||
|
|
||||||
// Example3d();
|
/*Example3d();*/
|
||||||
for(;;) {
|
for(;;) {
|
||||||
Example2d();
|
Example2d();
|
||||||
sys.params = sys.constraints = sys.entities = 0;
|
sys.params = sys.constraints = sys.entities = 0;
|
||||||
|
|
8
exposed/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
add_executable(CDemo
|
||||||
|
CDemo.c)
|
||||||
|
|
||||||
|
target_link_libraries(CDemo
|
||||||
|
slvs)
|
|
@ -313,6 +313,10 @@ SLVS_C_LENGTH_RATIO*
|
||||||
The length of line entityA divided by the length of line entityB is
|
The length of line entityA divided by the length of line entityB is
|
||||||
equal to valA.
|
equal to valA.
|
||||||
|
|
||||||
|
SLVS_C_LENGTH_DIFFERENCE*
|
||||||
|
|
||||||
|
The lengths of line entityA and line entityB differ by valA.
|
||||||
|
|
||||||
SLVS_C_EQ_LEN_PT_LINE_D*
|
SLVS_C_EQ_LEN_PT_LINE_D*
|
||||||
|
|
||||||
The length of the line entityA is equal to the distance from point
|
The length of the line entityA is equal to the distance from point
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
DEFINES = /D_WIN32_WINNT=0x500 /DISOLATION_AWARE_ENABLED /D_WIN32_IE=0x500 /DWIN32_LEAN_AND_MEAN /DWIN32 /DLIBRARY
|
|
||||||
# Use the multi-threaded static libc because libpng and zlib do; not sure if anything bad
|
|
||||||
# happens if those mix, but don't want to risk it.
|
|
||||||
CFLAGS = /W3 /nologo -MT -Iextlib -I..\..\common\win32 /D_DEBUG /D_CRT_SECURE_NO_WARNINGS /I. /I.. /Zi /EHs /O2 /GS-
|
|
||||||
|
|
||||||
HEADERS = ..\solvespace.h ..\dsc.h ..\sketch.h ..\expr.h slvs.h
|
|
||||||
|
|
||||||
OBJDIR = obj
|
|
||||||
|
|
||||||
SSOBJS = $(OBJDIR)\util.obj \
|
|
||||||
$(OBJDIR)\entity.obj \
|
|
||||||
$(OBJDIR)\expr.obj \
|
|
||||||
$(OBJDIR)\constrainteq.obj \
|
|
||||||
$(OBJDIR)\system.obj \
|
|
||||||
|
|
||||||
|
|
||||||
W32OBJS = $(OBJDIR)\w32util.obj \
|
|
||||||
|
|
||||||
|
|
||||||
LIBOBJS = $(OBJDIR)\lib.obj \
|
|
||||||
|
|
||||||
|
|
||||||
LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib shell32.lib
|
|
||||||
|
|
||||||
all: $(OBJDIR)/CDemo.exe
|
|
||||||
@cp $(OBJDIR)/CDemo.exe .
|
|
||||||
CDemo.exe
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f obj/*
|
|
||||||
|
|
||||||
$(OBJDIR)/slvs.dll: $(SSOBJS) $(LIBOBJS) $(W32OBJS)
|
|
||||||
@$(CC) /LD -Fe$(OBJDIR)/slvs.dll $(SSOBJS) $(LIBOBJS) $(W32OBJS) $(LIBS)
|
|
||||||
@cp $(OBJDIR)/slvs.dll .
|
|
||||||
@echo slvs.dll
|
|
||||||
|
|
||||||
$(OBJDIR)/CDemo.exe: CDemo.c $(OBJDIR)/slvs.dll
|
|
||||||
@$(CC) $(CFLAGS) -Fe$(OBJDIR)/CDemo.exe CDemo.c $(OBJDIR)/slvs.lib $(LIBS)
|
|
||||||
@echo CDemo.exe
|
|
||||||
|
|
||||||
$(SSOBJS): ..\$(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\$(@B).cpp
|
|
||||||
|
|
||||||
$(W32OBJS): ..\win32\$(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj ..\win32\$(@B).cpp
|
|
||||||
|
|
||||||
$(LIBOBJS): $(@B).cpp $(HEADERS)
|
|
||||||
@$(CC) $(CFLAGS) $(DEFINES) -c -Fo$(OBJDIR)/$(@B).obj $(@B).cpp
|
|
||||||
|
|
||||||
|
|
|
@ -511,6 +511,7 @@ Module VbDemo
|
||||||
Public Const SLVS_C_PROJ_PT_DISTANCE As Integer = 100030
|
Public Const SLVS_C_PROJ_PT_DISTANCE As Integer = 100030
|
||||||
Public Const SLVS_C_WHERE_DRAGGED As Integer = 100031
|
Public Const SLVS_C_WHERE_DRAGGED As Integer = 100031
|
||||||
Public Const SLVS_C_CURVE_CURVE_TANGENT As Integer = 100032
|
Public Const SLVS_C_CURVE_CURVE_TANGENT As Integer = 100032
|
||||||
|
Public Const SLVS_C_LENGTH_DIFFERENCE As Integer = 100033
|
||||||
|
|
||||||
<StructLayout(LayoutKind.Sequential)> Public Structure Slvs_Constraint
|
<StructLayout(LayoutKind.Sequential)> Public Structure Slvs_Constraint
|
||||||
Public h As UInteger
|
Public h As UInteger
|
||||||
|
|
794
expr.cpp
|
@ -1,794 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// The symbolic algebra system used to write our constraint equations;
|
|
||||||
// routines to build expressions in software or from a user-provided string,
|
|
||||||
// and to compute the partial derivatives that we'll use when write our
|
|
||||||
// Jacobian matrix.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
ExprVector ExprVector::From(Expr *x, Expr *y, Expr *z) {
|
|
||||||
ExprVector r = { x, y, z};
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::From(Vector vn) {
|
|
||||||
ExprVector ve;
|
|
||||||
ve.x = Expr::From(vn.x);
|
|
||||||
ve.y = Expr::From(vn.y);
|
|
||||||
ve.z = Expr::From(vn.z);
|
|
||||||
return ve;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::From(hParam x, hParam y, hParam z) {
|
|
||||||
ExprVector ve;
|
|
||||||
ve.x = Expr::From(x);
|
|
||||||
ve.y = Expr::From(y);
|
|
||||||
ve.z = Expr::From(z);
|
|
||||||
return ve;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::From(double x, double y, double z) {
|
|
||||||
ExprVector ve;
|
|
||||||
ve.x = Expr::From(x);
|
|
||||||
ve.y = Expr::From(y);
|
|
||||||
ve.z = Expr::From(z);
|
|
||||||
return ve;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::Minus(ExprVector b) {
|
|
||||||
ExprVector r;
|
|
||||||
r.x = x->Minus(b.x);
|
|
||||||
r.y = y->Minus(b.y);
|
|
||||||
r.z = z->Minus(b.z);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::Plus(ExprVector b) {
|
|
||||||
ExprVector r;
|
|
||||||
r.x = x->Plus(b.x);
|
|
||||||
r.y = y->Plus(b.y);
|
|
||||||
r.z = z->Plus(b.z);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *ExprVector::Dot(ExprVector b) {
|
|
||||||
Expr *r;
|
|
||||||
r = x->Times(b.x);
|
|
||||||
r = r->Plus(y->Times(b.y));
|
|
||||||
r = r->Plus(z->Times(b.z));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::Cross(ExprVector b) {
|
|
||||||
ExprVector r;
|
|
||||||
r.x = (y->Times(b.z))->Minus(z->Times(b.y));
|
|
||||||
r.y = (z->Times(b.x))->Minus(x->Times(b.z));
|
|
||||||
r.z = (x->Times(b.y))->Minus(y->Times(b.x));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::ScaledBy(Expr *s) {
|
|
||||||
ExprVector r;
|
|
||||||
r.x = x->Times(s);
|
|
||||||
r.y = y->Times(s);
|
|
||||||
r.z = z->Times(s);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprVector::WithMagnitude(Expr *s) {
|
|
||||||
Expr *m = Magnitude();
|
|
||||||
return ScaledBy(s->Div(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *ExprVector::Magnitude(void) {
|
|
||||||
Expr *r;
|
|
||||||
r = x->Square();
|
|
||||||
r = r->Plus(y->Square());
|
|
||||||
r = r->Plus(z->Square());
|
|
||||||
return r->Sqrt();
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector ExprVector::Eval(void) {
|
|
||||||
Vector r;
|
|
||||||
r.x = x->Eval();
|
|
||||||
r.y = y->Eval();
|
|
||||||
r.z = z->Eval();
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprQuaternion ExprQuaternion::From(hParam w, hParam vx, hParam vy, hParam vz) {
|
|
||||||
ExprQuaternion q;
|
|
||||||
q.w = Expr::From(w);
|
|
||||||
q.vx = Expr::From(vx);
|
|
||||||
q.vy = Expr::From(vy);
|
|
||||||
q.vz = Expr::From(vz);
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprQuaternion ExprQuaternion::From(Expr *w, Expr *vx, Expr *vy, Expr *vz)
|
|
||||||
{
|
|
||||||
ExprQuaternion q;
|
|
||||||
q.w = w;
|
|
||||||
q.vx = vx;
|
|
||||||
q.vy = vy;
|
|
||||||
q.vz = vz;
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprQuaternion ExprQuaternion::From(Quaternion qn) {
|
|
||||||
ExprQuaternion qe;
|
|
||||||
qe.w = Expr::From(qn.w);
|
|
||||||
qe.vx = Expr::From(qn.vx);
|
|
||||||
qe.vy = Expr::From(qn.vy);
|
|
||||||
qe.vz = Expr::From(qn.vz);
|
|
||||||
return qe;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprQuaternion::RotationU(void) {
|
|
||||||
ExprVector u;
|
|
||||||
Expr *two = Expr::From(2);
|
|
||||||
|
|
||||||
u.x = w->Square();
|
|
||||||
u.x = (u.x)->Plus(vx->Square());
|
|
||||||
u.x = (u.x)->Minus(vy->Square());
|
|
||||||
u.x = (u.x)->Minus(vz->Square());
|
|
||||||
|
|
||||||
u.y = two->Times(w->Times(vz));
|
|
||||||
u.y = (u.y)->Plus(two->Times(vx->Times(vy)));
|
|
||||||
|
|
||||||
u.z = two->Times(vx->Times(vz));
|
|
||||||
u.z = (u.z)->Minus(two->Times(w->Times(vy)));
|
|
||||||
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprQuaternion::RotationV(void) {
|
|
||||||
ExprVector v;
|
|
||||||
Expr *two = Expr::From(2);
|
|
||||||
|
|
||||||
v.x = two->Times(vx->Times(vy));
|
|
||||||
v.x = (v.x)->Minus(two->Times(w->Times(vz)));
|
|
||||||
|
|
||||||
v.y = w->Square();
|
|
||||||
v.y = (v.y)->Minus(vx->Square());
|
|
||||||
v.y = (v.y)->Plus(vy->Square());
|
|
||||||
v.y = (v.y)->Minus(vz->Square());
|
|
||||||
|
|
||||||
v.z = two->Times(w->Times(vx));
|
|
||||||
v.z = (v.z)->Plus(two->Times(vy->Times(vz)));
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprQuaternion::RotationN(void) {
|
|
||||||
ExprVector n;
|
|
||||||
Expr *two = Expr::From(2);
|
|
||||||
|
|
||||||
n.x = two->Times( w->Times(vy));
|
|
||||||
n.x = (n.x)->Plus (two->Times(vx->Times(vz)));
|
|
||||||
|
|
||||||
n.y = two->Times(vy->Times(vz));
|
|
||||||
n.y = (n.y)->Minus(two->Times( w->Times(vx)));
|
|
||||||
|
|
||||||
n.z = w->Square();
|
|
||||||
n.z = (n.z)->Minus(vx->Square());
|
|
||||||
n.z = (n.z)->Minus(vy->Square());
|
|
||||||
n.z = (n.z)->Plus (vz->Square());
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprVector ExprQuaternion::Rotate(ExprVector p) {
|
|
||||||
// Express the point in the new basis
|
|
||||||
return (RotationU().ScaledBy(p.x)).Plus(
|
|
||||||
RotationV().ScaledBy(p.y)).Plus(
|
|
||||||
RotationN().ScaledBy(p.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprQuaternion ExprQuaternion::Times(ExprQuaternion b) {
|
|
||||||
Expr *sa = w, *sb = b.w;
|
|
||||||
ExprVector va = { vx, vy, vz };
|
|
||||||
ExprVector vb = { b.vx, b.vy, b.vz };
|
|
||||||
|
|
||||||
ExprQuaternion r;
|
|
||||||
r.w = (sa->Times(sb))->Minus(va.Dot(vb));
|
|
||||||
ExprVector vr = vb.ScaledBy(sa).Plus(
|
|
||||||
va.ScaledBy(sb).Plus(
|
|
||||||
va.Cross(vb)));
|
|
||||||
r.vx = vr.x;
|
|
||||||
r.vy = vr.y;
|
|
||||||
r.vz = vr.z;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *ExprQuaternion::Magnitude(void) {
|
|
||||||
return ((w ->Square())->Plus(
|
|
||||||
(vx->Square())->Plus(
|
|
||||||
(vy->Square())->Plus(
|
|
||||||
(vz->Square())))))->Sqrt();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Expr *Expr::From(hParam p) {
|
|
||||||
Expr *r = AllocExpr();
|
|
||||||
r->op = PARAM;
|
|
||||||
r->x.parh = p;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *Expr::From(double v) {
|
|
||||||
Expr *r = AllocExpr();
|
|
||||||
r->op = CONSTANT;
|
|
||||||
r->x.v = v;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *Expr::AnyOp(int newOp, Expr *b) {
|
|
||||||
Expr *r = AllocExpr();
|
|
||||||
r->op = newOp;
|
|
||||||
r->a = this;
|
|
||||||
r->b = b;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Expr::Children(void) {
|
|
||||||
switch(op) {
|
|
||||||
case PARAM:
|
|
||||||
case PARAM_PTR:
|
|
||||||
case CONSTANT:
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case PLUS:
|
|
||||||
case MINUS:
|
|
||||||
case TIMES:
|
|
||||||
case DIV:
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
case NEGATE:
|
|
||||||
case SQRT:
|
|
||||||
case SQUARE:
|
|
||||||
case SIN:
|
|
||||||
case COS:
|
|
||||||
case ASIN:
|
|
||||||
case ACOS:
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Expr::Nodes(void) {
|
|
||||||
switch(Children()) {
|
|
||||||
case 0: return 1;
|
|
||||||
case 1: return 1 + a->Nodes();
|
|
||||||
case 2: return 1 + a->Nodes() + b->Nodes();
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *Expr::DeepCopy(void) {
|
|
||||||
Expr *n = AllocExpr();
|
|
||||||
*n = *this;
|
|
||||||
n->marker = 0;
|
|
||||||
int c = n->Children();
|
|
||||||
if(c > 0) n->a = a->DeepCopy();
|
|
||||||
if(c > 1) n->b = b->DeepCopy();
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *Expr::DeepCopyWithParamsAsPointers(IdList<Param,hParam> *firstTry,
|
|
||||||
IdList<Param,hParam> *thenTry)
|
|
||||||
{
|
|
||||||
Expr *n = AllocExpr();
|
|
||||||
if(op == PARAM) {
|
|
||||||
// A param that is referenced by its hParam gets rewritten to go
|
|
||||||
// straight in to the parameter table with a pointer, or simply
|
|
||||||
// into a constant if it's already known.
|
|
||||||
Param *p = firstTry->FindByIdNoOops(x.parh);
|
|
||||||
if(!p) p = thenTry->FindById(x.parh);
|
|
||||||
if(p->known) {
|
|
||||||
n->op = CONSTANT;
|
|
||||||
n->x.v = p->val;
|
|
||||||
} else {
|
|
||||||
n->op = PARAM_PTR;
|
|
||||||
n->x.parp = p;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
*n = *this;
|
|
||||||
int c = n->Children();
|
|
||||||
if(c > 0) n->a = a->DeepCopyWithParamsAsPointers(firstTry, thenTry);
|
|
||||||
if(c > 1) n->b = b->DeepCopyWithParamsAsPointers(firstTry, thenTry);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
double Expr::Eval(void) {
|
|
||||||
switch(op) {
|
|
||||||
case PARAM: return SK.GetParam(x.parh)->val;
|
|
||||||
case PARAM_PTR: return (x.parp)->val;
|
|
||||||
|
|
||||||
case CONSTANT: return x.v;
|
|
||||||
|
|
||||||
case PLUS: return a->Eval() + b->Eval();
|
|
||||||
case MINUS: return a->Eval() - b->Eval();
|
|
||||||
case TIMES: return a->Eval() * b->Eval();
|
|
||||||
case DIV: return a->Eval() / b->Eval();
|
|
||||||
|
|
||||||
case NEGATE: return -(a->Eval());
|
|
||||||
case SQRT: return sqrt(a->Eval());
|
|
||||||
case SQUARE: { double r = a->Eval(); return r*r; }
|
|
||||||
case SIN: return sin(a->Eval());
|
|
||||||
case COS: return cos(a->Eval());
|
|
||||||
case ACOS: return acos(a->Eval());
|
|
||||||
case ASIN: return asin(a->Eval());
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *Expr::PartialWrt(hParam p) {
|
|
||||||
Expr *da, *db;
|
|
||||||
|
|
||||||
switch(op) {
|
|
||||||
case PARAM_PTR: return From(p.v == x.parp->h.v ? 1 : 0);
|
|
||||||
case PARAM: return From(p.v == x.parh.v ? 1 : 0);
|
|
||||||
|
|
||||||
case CONSTANT: return From(0.0);
|
|
||||||
|
|
||||||
case PLUS: return (a->PartialWrt(p))->Plus(b->PartialWrt(p));
|
|
||||||
case MINUS: return (a->PartialWrt(p))->Minus(b->PartialWrt(p));
|
|
||||||
|
|
||||||
case TIMES:
|
|
||||||
da = a->PartialWrt(p);
|
|
||||||
db = b->PartialWrt(p);
|
|
||||||
return (a->Times(db))->Plus(b->Times(da));
|
|
||||||
|
|
||||||
case DIV:
|
|
||||||
da = a->PartialWrt(p);
|
|
||||||
db = b->PartialWrt(p);
|
|
||||||
return ((da->Times(b))->Minus(a->Times(db)))->Div(b->Square());
|
|
||||||
|
|
||||||
case SQRT:
|
|
||||||
return (From(0.5)->Div(a->Sqrt()))->Times(a->PartialWrt(p));
|
|
||||||
|
|
||||||
case SQUARE:
|
|
||||||
return (From(2.0)->Times(a))->Times(a->PartialWrt(p));
|
|
||||||
|
|
||||||
case NEGATE: return (a->PartialWrt(p))->Negate();
|
|
||||||
case SIN: return (a->Cos())->Times(a->PartialWrt(p));
|
|
||||||
case COS: return ((a->Sin())->Times(a->PartialWrt(p)))->Negate();
|
|
||||||
|
|
||||||
case ASIN:
|
|
||||||
return (From(1)->Div((From(1)->Minus(a->Square()))->Sqrt()))
|
|
||||||
->Times(a->PartialWrt(p));
|
|
||||||
case ACOS:
|
|
||||||
return (From(-1)->Div((From(1)->Minus(a->Square()))->Sqrt()))
|
|
||||||
->Times(a->PartialWrt(p));
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QWORD Expr::ParamsUsed(void) {
|
|
||||||
QWORD r = 0;
|
|
||||||
if(op == PARAM) r |= ((QWORD)1 << (x.parh.v % 61));
|
|
||||||
if(op == PARAM_PTR) r |= ((QWORD)1 << (x.parp->h.v % 61));
|
|
||||||
|
|
||||||
int c = Children();
|
|
||||||
if(c >= 1) r |= a->ParamsUsed();
|
|
||||||
if(c >= 2) r |= b->ParamsUsed();
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Expr::DependsOn(hParam p) {
|
|
||||||
if(op == PARAM) return (x.parh.v == p.v);
|
|
||||||
if(op == PARAM_PTR) return (x.parp->h.v == p.v);
|
|
||||||
|
|
||||||
int c = Children();
|
|
||||||
if(c == 1) return a->DependsOn(p);
|
|
||||||
if(c == 2) return a->DependsOn(p) || b->DependsOn(p);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Expr::Tol(double a, double b) {
|
|
||||||
return fabs(a - b) < 0.001;
|
|
||||||
}
|
|
||||||
Expr *Expr::FoldConstants(void) {
|
|
||||||
Expr *n = AllocExpr();
|
|
||||||
*n = *this;
|
|
||||||
|
|
||||||
int c = Children();
|
|
||||||
if(c >= 1) n->a = a->FoldConstants();
|
|
||||||
if(c >= 2) n->b = b->FoldConstants();
|
|
||||||
|
|
||||||
switch(op) {
|
|
||||||
case PARAM_PTR:
|
|
||||||
case PARAM:
|
|
||||||
case CONSTANT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MINUS:
|
|
||||||
case TIMES:
|
|
||||||
case DIV:
|
|
||||||
case PLUS:
|
|
||||||
// If both ops are known, then we can evaluate immediately
|
|
||||||
if(n->a->op == CONSTANT && n->b->op == CONSTANT) {
|
|
||||||
double nv = n->Eval();
|
|
||||||
n->op = CONSTANT;
|
|
||||||
n->x.v = nv;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// x + 0 = 0 + x = x
|
|
||||||
if(op == PLUS && n->b->op == CONSTANT && Tol(n->b->x.v, 0)) {
|
|
||||||
*n = *(n->a); break;
|
|
||||||
}
|
|
||||||
if(op == PLUS && n->a->op == CONSTANT && Tol(n->a->x.v, 0)) {
|
|
||||||
*n = *(n->b); break;
|
|
||||||
}
|
|
||||||
// 1*x = x*1 = x
|
|
||||||
if(op == TIMES && n->b->op == CONSTANT && Tol(n->b->x.v, 1)) {
|
|
||||||
*n = *(n->a); break;
|
|
||||||
}
|
|
||||||
if(op == TIMES && n->a->op == CONSTANT && Tol(n->a->x.v, 1)) {
|
|
||||||
*n = *(n->b); break;
|
|
||||||
}
|
|
||||||
// 0*x = x*0 = 0
|
|
||||||
if(op == TIMES && n->b->op == CONSTANT && Tol(n->b->x.v, 0)) {
|
|
||||||
n->op = CONSTANT; n->x.v = 0; break;
|
|
||||||
}
|
|
||||||
if(op == TIMES && n->a->op == CONSTANT && Tol(n->a->x.v, 0)) {
|
|
||||||
n->op = CONSTANT; n->x.v = 0; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SQRT:
|
|
||||||
case SQUARE:
|
|
||||||
case NEGATE:
|
|
||||||
case SIN:
|
|
||||||
case COS:
|
|
||||||
case ASIN:
|
|
||||||
case ACOS:
|
|
||||||
if(n->a->op == CONSTANT) {
|
|
||||||
double nv = n->Eval();
|
|
||||||
n->op = CONSTANT;
|
|
||||||
n->x.v = nv;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expr::Substitute(hParam oldh, hParam newh) {
|
|
||||||
if(op == PARAM_PTR) oops();
|
|
||||||
|
|
||||||
if(op == PARAM && x.parh.v == oldh.v) {
|
|
||||||
x.parh = newh;
|
|
||||||
}
|
|
||||||
int c = Children();
|
|
||||||
if(c >= 1) a->Substitute(oldh, newh);
|
|
||||||
if(c >= 2) b->Substitute(oldh, newh);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// If the expression references only one parameter that appears in pl, then
|
|
||||||
// return that parameter. If no param is referenced, then return NO_PARAMS.
|
|
||||||
// If multiple params are referenced, then return MULTIPLE_PARAMS.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
const hParam Expr::NO_PARAMS = { 0 };
|
|
||||||
const hParam Expr::MULTIPLE_PARAMS = { 1 };
|
|
||||||
hParam Expr::ReferencedParams(ParamList *pl) {
|
|
||||||
if(op == PARAM) {
|
|
||||||
if(pl->FindByIdNoOops(x.parh)) {
|
|
||||||
return x.parh;
|
|
||||||
} else {
|
|
||||||
return NO_PARAMS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(op == PARAM_PTR) oops();
|
|
||||||
|
|
||||||
int c = Children();
|
|
||||||
if(c == 0) {
|
|
||||||
return NO_PARAMS;
|
|
||||||
} else if(c == 1) {
|
|
||||||
return a->ReferencedParams(pl);
|
|
||||||
} else if(c == 2) {
|
|
||||||
hParam pa, pb;
|
|
||||||
pa = a->ReferencedParams(pl);
|
|
||||||
pb = b->ReferencedParams(pl);
|
|
||||||
if(pa.v == NO_PARAMS.v) {
|
|
||||||
return pb;
|
|
||||||
} else if(pb.v == NO_PARAMS.v) {
|
|
||||||
return pa;
|
|
||||||
} else if(pa.v == pb.v) {
|
|
||||||
return pa; // either, doesn't matter
|
|
||||||
} else {
|
|
||||||
return MULTIPLE_PARAMS;
|
|
||||||
}
|
|
||||||
} else oops();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Routines to pretty-print an expression. Mostly for debugging.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static char StringBuffer[4096];
|
|
||||||
void Expr::App(char *s, ...) {
|
|
||||||
va_list f;
|
|
||||||
va_start(f, s);
|
|
||||||
vsprintf(StringBuffer+strlen(StringBuffer), s, f);
|
|
||||||
}
|
|
||||||
char *Expr::Print(void) {
|
|
||||||
if(!this) return "0";
|
|
||||||
|
|
||||||
StringBuffer[0] = '\0';
|
|
||||||
PrintW();
|
|
||||||
return StringBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expr::PrintW(void) {
|
|
||||||
char c;
|
|
||||||
switch(op) {
|
|
||||||
case PARAM: App("param(%08x)", x.parh.v); break;
|
|
||||||
case PARAM_PTR: App("param(p%08x)", x.parp->h.v); break;
|
|
||||||
|
|
||||||
case CONSTANT: App("%.3f", x.v); break;
|
|
||||||
|
|
||||||
case PLUS: c = '+'; goto p;
|
|
||||||
case MINUS: c = '-'; goto p;
|
|
||||||
case TIMES: c = '*'; goto p;
|
|
||||||
case DIV: c = '/'; goto p;
|
|
||||||
p:
|
|
||||||
App("(");
|
|
||||||
a->PrintW();
|
|
||||||
App(" %c ", c);
|
|
||||||
b->PrintW();
|
|
||||||
App(")");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEGATE: App("(- "); a->PrintW(); App(")"); break;
|
|
||||||
case SQRT: App("(sqrt "); a->PrintW(); App(")"); break;
|
|
||||||
case SQUARE: App("(square "); a->PrintW(); App(")"); break;
|
|
||||||
case SIN: App("(sin "); a->PrintW(); App(")"); break;
|
|
||||||
case COS: App("(cos "); a->PrintW(); App(")"); break;
|
|
||||||
case ASIN: App("(asin "); a->PrintW(); App(")"); break;
|
|
||||||
case ACOS: App("(acos "); a->PrintW(); App(")"); break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// A parser; convert a string to an expression. Infix notation, with the
|
|
||||||
// usual shift/reduce approach. I had great hopes for user-entered eq
|
|
||||||
// constraints, but those don't seem very useful, so right now this is just
|
|
||||||
// to provide calculator type functionality wherever numbers are entered.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#define MAX_UNPARSED 1024
|
|
||||||
static Expr *Unparsed[MAX_UNPARSED];
|
|
||||||
static int UnparsedCnt, UnparsedP;
|
|
||||||
|
|
||||||
static Expr *Operands[MAX_UNPARSED];
|
|
||||||
static int OperandsP;
|
|
||||||
|
|
||||||
static Expr *Operators[MAX_UNPARSED];
|
|
||||||
static int OperatorsP;
|
|
||||||
|
|
||||||
void Expr::PushOperator(Expr *e) {
|
|
||||||
if(OperatorsP >= MAX_UNPARSED) throw "operator stack full!";
|
|
||||||
Operators[OperatorsP++] = e;
|
|
||||||
}
|
|
||||||
Expr *Expr::TopOperator(void) {
|
|
||||||
if(OperatorsP <= 0) throw "operator stack empty (get top)";
|
|
||||||
return Operators[OperatorsP-1];
|
|
||||||
}
|
|
||||||
Expr *Expr::PopOperator(void) {
|
|
||||||
if(OperatorsP <= 0) throw "operator stack empty (pop)";
|
|
||||||
return Operators[--OperatorsP];
|
|
||||||
}
|
|
||||||
void Expr::PushOperand(Expr *e) {
|
|
||||||
if(OperandsP >= MAX_UNPARSED) throw "operand stack full";
|
|
||||||
Operands[OperandsP++] = e;
|
|
||||||
}
|
|
||||||
Expr *Expr::PopOperand(void) {
|
|
||||||
if(OperandsP <= 0) throw "operand stack empty";
|
|
||||||
return Operands[--OperandsP];
|
|
||||||
}
|
|
||||||
Expr *Expr::Next(void) {
|
|
||||||
if(UnparsedP >= UnparsedCnt) return NULL;
|
|
||||||
return Unparsed[UnparsedP];
|
|
||||||
}
|
|
||||||
void Expr::Consume(void) {
|
|
||||||
if(UnparsedP >= UnparsedCnt) throw "no token to consume";
|
|
||||||
UnparsedP++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Expr::Precedence(Expr *e) {
|
|
||||||
if(e->op == ALL_RESOLVED) return -1; // never want to reduce this marker
|
|
||||||
if(e->op != BINARY_OP && e->op != UNARY_OP) oops();
|
|
||||||
|
|
||||||
switch(e->x.c) {
|
|
||||||
case 'q':
|
|
||||||
case 's':
|
|
||||||
case 'c':
|
|
||||||
case 'n': return 30;
|
|
||||||
|
|
||||||
case '*':
|
|
||||||
case '/': return 20;
|
|
||||||
|
|
||||||
case '+':
|
|
||||||
case '-': return 10;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expr::Reduce(void) {
|
|
||||||
Expr *a, *b;
|
|
||||||
|
|
||||||
Expr *op = PopOperator();
|
|
||||||
Expr *n;
|
|
||||||
int o;
|
|
||||||
switch(op->x.c) {
|
|
||||||
case '+': o = PLUS; goto c;
|
|
||||||
case '-': o = MINUS; goto c;
|
|
||||||
case '*': o = TIMES; goto c;
|
|
||||||
case '/': o = DIV; goto c;
|
|
||||||
c:
|
|
||||||
b = PopOperand();
|
|
||||||
a = PopOperand();
|
|
||||||
n = a->AnyOp(o, b);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'n': n = PopOperand()->Negate(); break;
|
|
||||||
case 'q': n = PopOperand()->Sqrt(); break;
|
|
||||||
case 's': n = (PopOperand()->Times(Expr::From(PI/180)))->Sin(); break;
|
|
||||||
case 'c': n = (PopOperand()->Times(Expr::From(PI/180)))->Cos(); break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
PushOperand(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expr::ReduceAndPush(Expr *n) {
|
|
||||||
while(Precedence(n) <= Precedence(TopOperator())) {
|
|
||||||
Reduce();
|
|
||||||
}
|
|
||||||
PushOperator(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expr::Parse(void) {
|
|
||||||
Expr *e = AllocExpr();
|
|
||||||
e->op = ALL_RESOLVED;
|
|
||||||
PushOperator(e);
|
|
||||||
|
|
||||||
for(;;) {
|
|
||||||
Expr *n = Next();
|
|
||||||
if(!n) throw "end of expression unexpected";
|
|
||||||
|
|
||||||
if(n->op == CONSTANT) {
|
|
||||||
PushOperand(n);
|
|
||||||
Consume();
|
|
||||||
} else if(n->op == PAREN && n->x.c == '(') {
|
|
||||||
Consume();
|
|
||||||
Parse();
|
|
||||||
n = Next();
|
|
||||||
if(n->op != PAREN || n->x.c != ')') throw "expected: )";
|
|
||||||
Consume();
|
|
||||||
} else if(n->op == UNARY_OP) {
|
|
||||||
PushOperator(n);
|
|
||||||
Consume();
|
|
||||||
continue;
|
|
||||||
} else if(n->op == BINARY_OP && n->x.c == '-') {
|
|
||||||
// The minus sign is special, because it might be binary or
|
|
||||||
// unary, depending on context.
|
|
||||||
n->op = UNARY_OP;
|
|
||||||
n->x.c = 'n';
|
|
||||||
PushOperator(n);
|
|
||||||
Consume();
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
throw "expected expression";
|
|
||||||
}
|
|
||||||
|
|
||||||
n = Next();
|
|
||||||
if(n && n->op == BINARY_OP) {
|
|
||||||
ReduceAndPush(n);
|
|
||||||
Consume();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while(TopOperator()->op != ALL_RESOLVED) {
|
|
||||||
Reduce();
|
|
||||||
}
|
|
||||||
PopOperator(); // discard the ALL_RESOLVED marker
|
|
||||||
}
|
|
||||||
|
|
||||||
void Expr::Lex(char *in) {
|
|
||||||
while(*in) {
|
|
||||||
if(UnparsedCnt >= MAX_UNPARSED) throw "too long";
|
|
||||||
|
|
||||||
char c = *in;
|
|
||||||
if(isdigit(c) || c == '.') {
|
|
||||||
// A number literal
|
|
||||||
char number[70];
|
|
||||||
int len = 0;
|
|
||||||
while((isdigit(*in) || *in == '.') && len < 30) {
|
|
||||||
number[len++] = *in;
|
|
||||||
in++;
|
|
||||||
}
|
|
||||||
number[len++] = '\0';
|
|
||||||
Expr *e = AllocExpr();
|
|
||||||
e->op = CONSTANT;
|
|
||||||
e->x.v = atof(number);
|
|
||||||
Unparsed[UnparsedCnt++] = e;
|
|
||||||
} else if(isalpha(c) || c == '_') {
|
|
||||||
char name[70];
|
|
||||||
int len = 0;
|
|
||||||
while(isforname(*in) && len < 30) {
|
|
||||||
name[len++] = *in;
|
|
||||||
in++;
|
|
||||||
}
|
|
||||||
name[len++] = '\0';
|
|
||||||
|
|
||||||
Expr *e = AllocExpr();
|
|
||||||
if(strcmp(name, "sqrt")==0) {
|
|
||||||
e->op = UNARY_OP;
|
|
||||||
e->x.c = 'q';
|
|
||||||
} else if(strcmp(name, "cos")==0) {
|
|
||||||
e->op = UNARY_OP;
|
|
||||||
e->x.c = 'c';
|
|
||||||
} else if(strcmp(name, "sin")==0) {
|
|
||||||
e->op = UNARY_OP;
|
|
||||||
e->x.c = 's';
|
|
||||||
} else {
|
|
||||||
throw "unknown name";
|
|
||||||
}
|
|
||||||
Unparsed[UnparsedCnt++] = e;
|
|
||||||
} else if(strchr("+-*/()", c)) {
|
|
||||||
Expr *e = AllocExpr();
|
|
||||||
e->op = (c == '(' || c == ')') ? PAREN : BINARY_OP;
|
|
||||||
e->x.c = c;
|
|
||||||
Unparsed[UnparsedCnt++] = e;
|
|
||||||
in++;
|
|
||||||
} else if(isspace(c)) {
|
|
||||||
// Ignore whitespace
|
|
||||||
in++;
|
|
||||||
} else {
|
|
||||||
// This is a lex error.
|
|
||||||
throw "unexpected characters";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *Expr::From(char *in, bool popUpError) {
|
|
||||||
UnparsedCnt = 0;
|
|
||||||
UnparsedP = 0;
|
|
||||||
OperandsP = 0;
|
|
||||||
OperatorsP = 0;
|
|
||||||
|
|
||||||
Expr *r;
|
|
||||||
try {
|
|
||||||
Lex(in);
|
|
||||||
Parse();
|
|
||||||
r = PopOperand();
|
|
||||||
} catch (char *e) {
|
|
||||||
dbp("exception: parse/lex error: %s", e);
|
|
||||||
if(popUpError) {
|
|
||||||
Error("Not a valid number or expression: '%s'", in);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
172
expr.h
|
@ -1,172 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// An expression in our symbolic algebra system, used to write, linearize,
|
|
||||||
// and solve our constraint equations.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifndef __EXPR_H
|
|
||||||
#define __EXPR_H
|
|
||||||
|
|
||||||
class Expr;
|
|
||||||
|
|
||||||
class Expr {
|
|
||||||
public:
|
|
||||||
DWORD marker;
|
|
||||||
|
|
||||||
// A parameter, by the hParam handle
|
|
||||||
static const int PARAM = 0;
|
|
||||||
// A parameter, by a pointer straight in to the param table (faster,
|
|
||||||
// if we know that the param table won't move around)
|
|
||||||
static const int PARAM_PTR = 1;
|
|
||||||
|
|
||||||
// These are used only for user-entered expressions.
|
|
||||||
static const int POINT = 10;
|
|
||||||
static const int ENTITY = 11;
|
|
||||||
|
|
||||||
static const int CONSTANT = 20;
|
|
||||||
|
|
||||||
static const int PLUS = 100;
|
|
||||||
static const int MINUS = 101;
|
|
||||||
static const int TIMES = 102;
|
|
||||||
static const int DIV = 103;
|
|
||||||
static const int NEGATE = 104;
|
|
||||||
static const int SQRT = 105;
|
|
||||||
static const int SQUARE = 106;
|
|
||||||
static const int SIN = 107;
|
|
||||||
static const int COS = 108;
|
|
||||||
static const int ASIN = 109;
|
|
||||||
static const int ACOS = 110;
|
|
||||||
|
|
||||||
// Special helpers for when we're parsing an expression from text.
|
|
||||||
// Initially, literals (like a constant number) appear in the same
|
|
||||||
// format as they will in the finished expression, but the operators
|
|
||||||
// are different until the parser fixes things up (and builds the
|
|
||||||
// tree from the flat list that the lexer outputs).
|
|
||||||
static const int ALL_RESOLVED = 1000;
|
|
||||||
static const int PAREN = 1001;
|
|
||||||
static const int BINARY_OP = 1002;
|
|
||||||
static const int UNARY_OP = 1003;
|
|
||||||
|
|
||||||
int op;
|
|
||||||
Expr *a;
|
|
||||||
Expr *b;
|
|
||||||
union {
|
|
||||||
double v;
|
|
||||||
hParam parh;
|
|
||||||
Param *parp;
|
|
||||||
hEntity entity;
|
|
||||||
|
|
||||||
// For use while parsing
|
|
||||||
char c;
|
|
||||||
} x;
|
|
||||||
|
|
||||||
static inline Expr *AllocExpr(void)
|
|
||||||
{ return (Expr *)AllocTemporary(sizeof(Expr)); }
|
|
||||||
|
|
||||||
static Expr *From(hParam p);
|
|
||||||
static Expr *From(double v);
|
|
||||||
|
|
||||||
Expr *AnyOp(int op, Expr *b);
|
|
||||||
inline Expr *Plus (Expr *b) { return AnyOp(PLUS, b); }
|
|
||||||
inline Expr *Minus(Expr *b) { return AnyOp(MINUS, b); }
|
|
||||||
inline Expr *Times(Expr *b) { return AnyOp(TIMES, b); }
|
|
||||||
inline Expr *Div (Expr *b) { return AnyOp(DIV, b); }
|
|
||||||
|
|
||||||
inline Expr *Negate(void) { return AnyOp(NEGATE, NULL); }
|
|
||||||
inline Expr *Sqrt (void) { return AnyOp(SQRT, NULL); }
|
|
||||||
inline Expr *Square(void) { return AnyOp(SQUARE, NULL); }
|
|
||||||
inline Expr *Sin (void) { return AnyOp(SIN, NULL); }
|
|
||||||
inline Expr *Cos (void) { return AnyOp(COS, NULL); }
|
|
||||||
inline Expr *ASin (void) { return AnyOp(ASIN, NULL); }
|
|
||||||
inline Expr *ACos (void) { return AnyOp(ACOS, NULL); }
|
|
||||||
|
|
||||||
Expr *PartialWrt(hParam p);
|
|
||||||
double Eval(void);
|
|
||||||
QWORD ParamsUsed(void);
|
|
||||||
bool DependsOn(hParam p);
|
|
||||||
static bool Tol(double a, double b);
|
|
||||||
Expr *FoldConstants(void);
|
|
||||||
void Substitute(hParam oldh, hParam newh);
|
|
||||||
|
|
||||||
static const hParam NO_PARAMS, MULTIPLE_PARAMS;
|
|
||||||
hParam ReferencedParams(ParamList *pl);
|
|
||||||
|
|
||||||
void ParamsToPointers(void);
|
|
||||||
|
|
||||||
void App(char *str, ...);
|
|
||||||
char *Print(void);
|
|
||||||
void PrintW(void); // worker
|
|
||||||
|
|
||||||
// number of child nodes: 0 (e.g. constant), 1 (sqrt), or 2 (+)
|
|
||||||
int Children(void);
|
|
||||||
// total number of nodes in the tree
|
|
||||||
int Nodes(void);
|
|
||||||
|
|
||||||
// Make a simple copy
|
|
||||||
Expr *DeepCopy(void);
|
|
||||||
// Make a copy, with the parameters (usually referenced by hParam)
|
|
||||||
// resolved to pointers to the actual value. This speeds things up
|
|
||||||
// considerably.
|
|
||||||
Expr *DeepCopyWithParamsAsPointers(IdList<Param,hParam> *firstTry,
|
|
||||||
IdList<Param,hParam> *thenTry);
|
|
||||||
|
|
||||||
static Expr *From(char *in, bool popUpError);
|
|
||||||
static void Lex(char *in);
|
|
||||||
static Expr *Next(void);
|
|
||||||
static void Consume(void);
|
|
||||||
|
|
||||||
static void PushOperator(Expr *e);
|
|
||||||
static Expr *PopOperator(void);
|
|
||||||
static Expr *TopOperator(void);
|
|
||||||
static void PushOperand(Expr *e);
|
|
||||||
static Expr *PopOperand(void);
|
|
||||||
|
|
||||||
static void Reduce(void);
|
|
||||||
static void ReduceAndPush(Expr *e);
|
|
||||||
static int Precedence(Expr *e);
|
|
||||||
|
|
||||||
static int Precedence(int op);
|
|
||||||
static void Parse(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExprVector {
|
|
||||||
public:
|
|
||||||
Expr *x, *y, *z;
|
|
||||||
|
|
||||||
static ExprVector From(Expr *x, Expr *y, Expr *z);
|
|
||||||
static ExprVector From(Vector vn);
|
|
||||||
static ExprVector From(hParam x, hParam y, hParam z);
|
|
||||||
static ExprVector From(double x, double y, double z);
|
|
||||||
|
|
||||||
ExprVector Plus(ExprVector b);
|
|
||||||
ExprVector Minus(ExprVector b);
|
|
||||||
Expr *Dot(ExprVector b);
|
|
||||||
ExprVector Cross(ExprVector b);
|
|
||||||
ExprVector ScaledBy(Expr *s);
|
|
||||||
ExprVector WithMagnitude(Expr *s);
|
|
||||||
Expr *Magnitude(void);
|
|
||||||
|
|
||||||
Vector Eval(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExprQuaternion {
|
|
||||||
public:
|
|
||||||
Expr *w, *vx, *vy, *vz;
|
|
||||||
|
|
||||||
static ExprQuaternion From(Expr *w, Expr *vx, Expr *vy, Expr *vz);
|
|
||||||
static ExprQuaternion From(Quaternion qn);
|
|
||||||
static ExprQuaternion From(hParam w, hParam vx, hParam vy, hParam vz);
|
|
||||||
|
|
||||||
ExprVector RotationU(void);
|
|
||||||
ExprVector RotationV(void);
|
|
||||||
ExprVector RotationN(void);
|
|
||||||
|
|
||||||
ExprVector Rotate(ExprVector p);
|
|
||||||
ExprQuaternion Times(ExprQuaternion b);
|
|
||||||
|
|
||||||
Expr *Magnitude(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
1
extlib/angle
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6fbcce0938caaccdbea44d826759aa2e587fe2f7
|
1
extlib/cairo
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit d4724ee921c4fa399ccbd0019c3d6917452e0ffd
|
1
extlib/freetype
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 069083cccd73d1d68da68116c8d050bb62cdfe0e
|
1
extlib/libdxfrw
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6f362317bf3f176b613be48512a88b78125c79f4
|
1
extlib/libpng
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit e9c3d83d5a04835806287f1e8c0f2d3a962d6673
|
1
extlib/pixman
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5561dfc3f7e992454076ff3f10a0554c6b407e19
|
3569
extlib/png.h
1481
extlib/pngconf.h
332
extlib/zconf.h
|
@ -1,332 +0,0 @@
|
||||||
/* zconf.h -- configuration of the zlib compression library
|
|
||||||
* Copyright (C) 1995-2005 Jean-loup Gailly.
|
|
||||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* @(#) $Id$ */
|
|
||||||
|
|
||||||
#ifndef ZCONF_H
|
|
||||||
#define ZCONF_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If you *really* need a unique prefix for all types and library functions,
|
|
||||||
* compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
|
|
||||||
*/
|
|
||||||
#ifdef Z_PREFIX
|
|
||||||
# define deflateInit_ z_deflateInit_
|
|
||||||
# define deflate z_deflate
|
|
||||||
# define deflateEnd z_deflateEnd
|
|
||||||
# define inflateInit_ z_inflateInit_
|
|
||||||
# define inflate z_inflate
|
|
||||||
# define inflateEnd z_inflateEnd
|
|
||||||
# define deflateInit2_ z_deflateInit2_
|
|
||||||
# define deflateSetDictionary z_deflateSetDictionary
|
|
||||||
# define deflateCopy z_deflateCopy
|
|
||||||
# define deflateReset z_deflateReset
|
|
||||||
# define deflateParams z_deflateParams
|
|
||||||
# define deflateBound z_deflateBound
|
|
||||||
# define deflatePrime z_deflatePrime
|
|
||||||
# define inflateInit2_ z_inflateInit2_
|
|
||||||
# define inflateSetDictionary z_inflateSetDictionary
|
|
||||||
# define inflateSync z_inflateSync
|
|
||||||
# define inflateSyncPoint z_inflateSyncPoint
|
|
||||||
# define inflateCopy z_inflateCopy
|
|
||||||
# define inflateReset z_inflateReset
|
|
||||||
# define inflateBack z_inflateBack
|
|
||||||
# define inflateBackEnd z_inflateBackEnd
|
|
||||||
# define compress z_compress
|
|
||||||
# define compress2 z_compress2
|
|
||||||
# define compressBound z_compressBound
|
|
||||||
# define uncompress z_uncompress
|
|
||||||
# define adler32 z_adler32
|
|
||||||
# define crc32 z_crc32
|
|
||||||
# define get_crc_table z_get_crc_table
|
|
||||||
# define zError z_zError
|
|
||||||
|
|
||||||
# define alloc_func z_alloc_func
|
|
||||||
# define free_func z_free_func
|
|
||||||
# define in_func z_in_func
|
|
||||||
# define out_func z_out_func
|
|
||||||
# define Byte z_Byte
|
|
||||||
# define uInt z_uInt
|
|
||||||
# define uLong z_uLong
|
|
||||||
# define Bytef z_Bytef
|
|
||||||
# define charf z_charf
|
|
||||||
# define intf z_intf
|
|
||||||
# define uIntf z_uIntf
|
|
||||||
# define uLongf z_uLongf
|
|
||||||
# define voidpf z_voidpf
|
|
||||||
# define voidp z_voidp
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__MSDOS__) && !defined(MSDOS)
|
|
||||||
# define MSDOS
|
|
||||||
#endif
|
|
||||||
#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
|
|
||||||
# define OS2
|
|
||||||
#endif
|
|
||||||
#if defined(_WINDOWS) && !defined(WINDOWS)
|
|
||||||
# define WINDOWS
|
|
||||||
#endif
|
|
||||||
#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
|
|
||||||
# ifndef WIN32
|
|
||||||
# define WIN32
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
|
|
||||||
# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
|
|
||||||
# ifndef SYS16BIT
|
|
||||||
# define SYS16BIT
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compile with -DMAXSEG_64K if the alloc function cannot allocate more
|
|
||||||
* than 64k bytes at a time (needed on systems with 16-bit int).
|
|
||||||
*/
|
|
||||||
#ifdef SYS16BIT
|
|
||||||
# define MAXSEG_64K
|
|
||||||
#endif
|
|
||||||
#ifdef MSDOS
|
|
||||||
# define UNALIGNED_OK
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __STDC_VERSION__
|
|
||||||
# ifndef STDC
|
|
||||||
# define STDC
|
|
||||||
# endif
|
|
||||||
# if __STDC_VERSION__ >= 199901L
|
|
||||||
# ifndef STDC99
|
|
||||||
# define STDC99
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
|
|
||||||
# define STDC
|
|
||||||
#endif
|
|
||||||
#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
|
|
||||||
# define STDC
|
|
||||||
#endif
|
|
||||||
#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
|
|
||||||
# define STDC
|
|
||||||
#endif
|
|
||||||
#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
|
|
||||||
# define STDC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
|
|
||||||
# define STDC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef STDC
|
|
||||||
# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
|
|
||||||
# define const /* note: need a more gentle solution here */
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Some Mac compilers merge all .h files incorrectly: */
|
|
||||||
#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
|
|
||||||
# define NO_DUMMY_DECL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Maximum value for memLevel in deflateInit2 */
|
|
||||||
#ifndef MAX_MEM_LEVEL
|
|
||||||
# ifdef MAXSEG_64K
|
|
||||||
# define MAX_MEM_LEVEL 8
|
|
||||||
# else
|
|
||||||
# define MAX_MEM_LEVEL 9
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Maximum value for windowBits in deflateInit2 and inflateInit2.
|
|
||||||
* WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
|
|
||||||
* created by gzip. (Files created by minigzip can still be extracted by
|
|
||||||
* gzip.)
|
|
||||||
*/
|
|
||||||
#ifndef MAX_WBITS
|
|
||||||
# define MAX_WBITS 15 /* 32K LZ77 window */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The memory requirements for deflate are (in bytes):
|
|
||||||
(1 << (windowBits+2)) + (1 << (memLevel+9))
|
|
||||||
that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
|
|
||||||
plus a few kilobytes for small objects. For example, if you want to reduce
|
|
||||||
the default memory requirements from 256K to 128K, compile with
|
|
||||||
make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
|
|
||||||
Of course this will generally degrade compression (there's no free lunch).
|
|
||||||
|
|
||||||
The memory requirements for inflate are (in bytes) 1 << windowBits
|
|
||||||
that is, 32K for windowBits=15 (default value) plus a few kilobytes
|
|
||||||
for small objects.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Type declarations */
|
|
||||||
|
|
||||||
#ifndef OF /* function prototypes */
|
|
||||||
# ifdef STDC
|
|
||||||
# define OF(args) args
|
|
||||||
# else
|
|
||||||
# define OF(args) ()
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The following definitions for FAR are needed only for MSDOS mixed
|
|
||||||
* model programming (small or medium model with some far allocations).
|
|
||||||
* This was tested only with MSC; for other MSDOS compilers you may have
|
|
||||||
* to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
|
|
||||||
* just define FAR to be empty.
|
|
||||||
*/
|
|
||||||
#ifdef SYS16BIT
|
|
||||||
# if defined(M_I86SM) || defined(M_I86MM)
|
|
||||||
/* MSC small or medium model */
|
|
||||||
# define SMALL_MEDIUM
|
|
||||||
# ifdef _MSC_VER
|
|
||||||
# define FAR _far
|
|
||||||
# else
|
|
||||||
# define FAR far
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# if (defined(__SMALL__) || defined(__MEDIUM__))
|
|
||||||
/* Turbo C small or medium model */
|
|
||||||
# define SMALL_MEDIUM
|
|
||||||
# ifdef __BORLANDC__
|
|
||||||
# define FAR _far
|
|
||||||
# else
|
|
||||||
# define FAR far
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(WINDOWS) || defined(WIN32)
|
|
||||||
/* If building or using zlib as a DLL, define ZLIB_DLL.
|
|
||||||
* This is not mandatory, but it offers a little performance increase.
|
|
||||||
*/
|
|
||||||
# ifdef ZLIB_DLL
|
|
||||||
# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
|
|
||||||
# ifdef ZLIB_INTERNAL
|
|
||||||
# define ZEXTERN extern __declspec(dllexport)
|
|
||||||
# else
|
|
||||||
# define ZEXTERN extern __declspec(dllimport)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# endif /* ZLIB_DLL */
|
|
||||||
/* If building or using zlib with the WINAPI/WINAPIV calling convention,
|
|
||||||
* define ZLIB_WINAPI.
|
|
||||||
* Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
|
|
||||||
*/
|
|
||||||
# ifdef ZLIB_WINAPI
|
|
||||||
# ifdef FAR
|
|
||||||
# undef FAR
|
|
||||||
# endif
|
|
||||||
# include <windows.h>
|
|
||||||
/* No need for _export, use ZLIB.DEF instead. */
|
|
||||||
/* For complete Windows compatibility, use WINAPI, not __stdcall. */
|
|
||||||
# define ZEXPORT WINAPI
|
|
||||||
# ifdef WIN32
|
|
||||||
# define ZEXPORTVA WINAPIV
|
|
||||||
# else
|
|
||||||
# define ZEXPORTVA FAR CDECL
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (__BEOS__)
|
|
||||||
# ifdef ZLIB_DLL
|
|
||||||
# ifdef ZLIB_INTERNAL
|
|
||||||
# define ZEXPORT __declspec(dllexport)
|
|
||||||
# define ZEXPORTVA __declspec(dllexport)
|
|
||||||
# else
|
|
||||||
# define ZEXPORT __declspec(dllimport)
|
|
||||||
# define ZEXPORTVA __declspec(dllimport)
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ZEXTERN
|
|
||||||
# define ZEXTERN extern
|
|
||||||
#endif
|
|
||||||
#ifndef ZEXPORT
|
|
||||||
# define ZEXPORT
|
|
||||||
#endif
|
|
||||||
#ifndef ZEXPORTVA
|
|
||||||
# define ZEXPORTVA
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FAR
|
|
||||||
# define FAR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(__MACTYPES__)
|
|
||||||
typedef unsigned char Byte; /* 8 bits */
|
|
||||||
#endif
|
|
||||||
typedef unsigned int uInt; /* 16 bits or more */
|
|
||||||
typedef unsigned long uLong; /* 32 bits or more */
|
|
||||||
|
|
||||||
#ifdef SMALL_MEDIUM
|
|
||||||
/* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
|
|
||||||
# define Bytef Byte FAR
|
|
||||||
#else
|
|
||||||
typedef Byte FAR Bytef;
|
|
||||||
#endif
|
|
||||||
typedef char FAR charf;
|
|
||||||
typedef int FAR intf;
|
|
||||||
typedef uInt FAR uIntf;
|
|
||||||
typedef uLong FAR uLongf;
|
|
||||||
|
|
||||||
#ifdef STDC
|
|
||||||
typedef void const *voidpc;
|
|
||||||
typedef void FAR *voidpf;
|
|
||||||
typedef void *voidp;
|
|
||||||
#else
|
|
||||||
typedef Byte const *voidpc;
|
|
||||||
typedef Byte FAR *voidpf;
|
|
||||||
typedef Byte *voidp;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
|
|
||||||
# include <sys/types.h> /* for off_t */
|
|
||||||
# include <unistd.h> /* for SEEK_* and off_t */
|
|
||||||
# ifdef VMS
|
|
||||||
# include <unixio.h> /* for off_t */
|
|
||||||
# endif
|
|
||||||
# define z_off_t off_t
|
|
||||||
#endif
|
|
||||||
#ifndef SEEK_SET
|
|
||||||
# define SEEK_SET 0 /* Seek from beginning of file. */
|
|
||||||
# define SEEK_CUR 1 /* Seek from current position. */
|
|
||||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
|
||||||
#endif
|
|
||||||
#ifndef z_off_t
|
|
||||||
# define z_off_t long
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__OS400__)
|
|
||||||
# define NO_vsnprintf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__MVS__)
|
|
||||||
# define NO_vsnprintf
|
|
||||||
# ifdef FAR
|
|
||||||
# undef FAR
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* MVS linker does not support external names larger than 8 bytes */
|
|
||||||
#if defined(__MVS__)
|
|
||||||
# pragma map(deflateInit_,"DEIN")
|
|
||||||
# pragma map(deflateInit2_,"DEIN2")
|
|
||||||
# pragma map(deflateEnd,"DEEND")
|
|
||||||
# pragma map(deflateBound,"DEBND")
|
|
||||||
# pragma map(inflateInit_,"ININ")
|
|
||||||
# pragma map(inflateInit2_,"ININ2")
|
|
||||||
# pragma map(inflateEnd,"INEND")
|
|
||||||
# pragma map(inflateSync,"INSY")
|
|
||||||
# pragma map(inflateSetDictionary,"INSEDI")
|
|
||||||
# pragma map(compressBound,"CMBND")
|
|
||||||
# pragma map(inflate_table,"INTABL")
|
|
||||||
# pragma map(inflate_fast,"INFA")
|
|
||||||
# pragma map(inflate_copyright,"INCOPY")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* ZCONF_H */
|
|
1
extlib/zlib
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 2fa463bacfff79181df1a5270fb67cc679a53e71
|
1357
extlib/zlib.h
BIN
extlib/zlib.lib
1774
font.table
607
glhelper.cpp
|
@ -1,607 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Helper functions that ultimately draw stuff with gl.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
// A public-domain Hershey vector font ("Simplex").
|
|
||||||
#include "font.table"
|
|
||||||
|
|
||||||
// A bitmap font.
|
|
||||||
#include "bitmapfont.table"
|
|
||||||
|
|
||||||
static bool ColorLocked;
|
|
||||||
static bool DepthOffsetLocked;
|
|
||||||
|
|
||||||
#define FONT_SCALE(h) ((h)/22.0)
|
|
||||||
double glxStrWidth(char *str, double h)
|
|
||||||
{
|
|
||||||
int w = 0;
|
|
||||||
for(; *str; str++) {
|
|
||||||
int c = *str;
|
|
||||||
if(c < 32 || c > 126) c = 32;
|
|
||||||
c -= 32;
|
|
||||||
|
|
||||||
w += Font[c].width;
|
|
||||||
}
|
|
||||||
return w*FONT_SCALE(h)/SS.GW.scale;
|
|
||||||
}
|
|
||||||
double glxStrHeight(double h)
|
|
||||||
{
|
|
||||||
// The characters have height ~22, as they appear in the table.
|
|
||||||
return 22.0*FONT_SCALE(h)/SS.GW.scale;
|
|
||||||
}
|
|
||||||
void glxWriteTextRefCenter(char *str, double h, Vector t, Vector u, Vector v,
|
|
||||||
glxLineFn *fn, void *fndata)
|
|
||||||
{
|
|
||||||
u = u.WithMagnitude(1);
|
|
||||||
v = v.WithMagnitude(1);
|
|
||||||
|
|
||||||
double scale = FONT_SCALE(h)/SS.GW.scale;
|
|
||||||
double fh = glxStrHeight(h);
|
|
||||||
double fw = glxStrWidth(str, h);
|
|
||||||
|
|
||||||
t = t.Plus(u.ScaledBy(-fw/2));
|
|
||||||
t = t.Plus(v.ScaledBy(-fh/2));
|
|
||||||
|
|
||||||
// Undo the (+5, +5) offset that glxWriteText applies.
|
|
||||||
t = t.Plus(u.ScaledBy(-5*scale));
|
|
||||||
t = t.Plus(v.ScaledBy(-5*scale));
|
|
||||||
|
|
||||||
glxWriteText(str, h, t, u, v, fn, fndata);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void LineDrawCallback(void *fndata, Vector a, Vector b)
|
|
||||||
{
|
|
||||||
glLineWidth(1);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(a);
|
|
||||||
glxVertex3v(b);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxWriteText(char *str, double h, Vector t, Vector u, Vector v,
|
|
||||||
glxLineFn *fn, void *fndata)
|
|
||||||
{
|
|
||||||
if(!fn) fn = LineDrawCallback;
|
|
||||||
u = u.WithMagnitude(1);
|
|
||||||
v = v.WithMagnitude(1);
|
|
||||||
|
|
||||||
double scale = FONT_SCALE(h)/SS.GW.scale;
|
|
||||||
int xo = 5;
|
|
||||||
int yo = 5;
|
|
||||||
|
|
||||||
for(; *str; str++) {
|
|
||||||
int c = *str;
|
|
||||||
if(c < 32 || c > 126) c = 32;
|
|
||||||
|
|
||||||
c -= 32;
|
|
||||||
|
|
||||||
int j;
|
|
||||||
Vector prevp = Vector::From(VERY_POSITIVE, 0, 0);
|
|
||||||
for(j = 0; j < Font[c].points; j++) {
|
|
||||||
int x = Font[c].coord[j*2];
|
|
||||||
int y = Font[c].coord[j*2+1];
|
|
||||||
|
|
||||||
if(x == PEN_UP && y == PEN_UP) {
|
|
||||||
prevp.x = VERY_POSITIVE;
|
|
||||||
} else {
|
|
||||||
Vector p = t;
|
|
||||||
p = p.Plus(u.ScaledBy((xo + x)*scale));
|
|
||||||
p = p.Plus(v.ScaledBy((yo + y)*scale));
|
|
||||||
if(prevp.x != VERY_POSITIVE) {
|
|
||||||
fn(fndata, prevp, p);
|
|
||||||
}
|
|
||||||
prevp = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xo += Font[c].width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxVertex3v(Vector u)
|
|
||||||
{
|
|
||||||
glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxAxisAlignedQuad(double l, double r, double t, double b)
|
|
||||||
{
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glVertex2d(l, t);
|
|
||||||
glVertex2d(l, b);
|
|
||||||
glVertex2d(r, b);
|
|
||||||
glVertex2d(r, t);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxAxisAlignedLineLoop(double l, double r, double t, double b)
|
|
||||||
{
|
|
||||||
glBegin(GL_LINE_LOOP);
|
|
||||||
glVertex2d(l, t);
|
|
||||||
glVertex2d(l, b);
|
|
||||||
glVertex2d(r, b);
|
|
||||||
glVertex2d(r, t);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FatLineEndcap(Vector p, Vector u, Vector v)
|
|
||||||
{
|
|
||||||
// A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10
|
|
||||||
static const double Circle[11][2] = {
|
|
||||||
{ 0.0000, 1.0000 },
|
|
||||||
{ -0.3090, 0.9511 },
|
|
||||||
{ -0.5878, 0.8090 },
|
|
||||||
{ -0.8090, 0.5878 },
|
|
||||||
{ -0.9511, 0.3090 },
|
|
||||||
{ -1.0000, 0.0000 },
|
|
||||||
{ -0.9511, -0.3090 },
|
|
||||||
{ -0.8090, -0.5878 },
|
|
||||||
{ -0.5878, -0.8090 },
|
|
||||||
{ -0.3090, -0.9511 },
|
|
||||||
{ 0.0000, -1.0000 },
|
|
||||||
};
|
|
||||||
glBegin(GL_TRIANGLE_FAN);
|
|
||||||
for(int i = 0; i <= 10; i++) {
|
|
||||||
double c = Circle[i][0], s = Circle[i][1];
|
|
||||||
glxVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s)));
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxFatLine(Vector a, Vector b, double width)
|
|
||||||
{
|
|
||||||
// The half-width of the line we're drawing.
|
|
||||||
double hw = width / 2;
|
|
||||||
Vector ab = b.Minus(a);
|
|
||||||
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
|
|
||||||
Vector abn = (ab.Cross(gn)).WithMagnitude(1);
|
|
||||||
abn = abn.Minus(gn.ScaledBy(gn.Dot(abn)));
|
|
||||||
// So now abn is normal to the projection of ab into the screen, so the
|
|
||||||
// line will always have constant thickness as the view is rotated.
|
|
||||||
|
|
||||||
abn = abn.WithMagnitude(hw);
|
|
||||||
ab = gn.Cross(abn);
|
|
||||||
ab = ab. WithMagnitude(hw);
|
|
||||||
|
|
||||||
// The body of a line is a quad
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glxVertex3v(a.Minus(abn));
|
|
||||||
glxVertex3v(b.Minus(abn));
|
|
||||||
glxVertex3v(b.Plus (abn));
|
|
||||||
glxVertex3v(a.Plus (abn));
|
|
||||||
glEnd();
|
|
||||||
// And the line has two semi-circular end caps.
|
|
||||||
FatLineEndcap(a, ab, abn);
|
|
||||||
FatLineEndcap(b, ab.ScaledBy(-1), abn);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void glxLockColorTo(DWORD rgb)
|
|
||||||
{
|
|
||||||
ColorLocked = false;
|
|
||||||
glColor3d(REDf(rgb), GREENf(rgb), BLUEf(rgb));
|
|
||||||
ColorLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxUnlockColor(void)
|
|
||||||
{
|
|
||||||
ColorLocked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxColorRGB(DWORD rgb)
|
|
||||||
{
|
|
||||||
// Is there a bug in some graphics drivers where this is not equivalent
|
|
||||||
// to glColor3d? There seems to be...
|
|
||||||
glxColorRGBa(rgb, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxColorRGBa(DWORD rgb, double a)
|
|
||||||
{
|
|
||||||
if(!ColorLocked) glColor4d(REDf(rgb), GREENf(rgb), BLUEf(rgb), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Stipple(BOOL forSel)
|
|
||||||
{
|
|
||||||
static BOOL Init;
|
|
||||||
const int BYTES = (32*32)/8;
|
|
||||||
static GLubyte HoverMask[BYTES];
|
|
||||||
static GLubyte SelMask[BYTES];
|
|
||||||
if(!Init) {
|
|
||||||
int x, y;
|
|
||||||
for(x = 0; x < 32; x++) {
|
|
||||||
for(y = 0; y < 32; y++) {
|
|
||||||
int i = y*4 + x/8, b = x % 8;
|
|
||||||
int ym = y % 4, xm = x % 4;
|
|
||||||
for(int k = 0; k < 2; k++) {
|
|
||||||
if(xm >= 1 && xm <= 2 && ym >= 1 && ym <= 2) {
|
|
||||||
(k == 0 ? SelMask : HoverMask)[i] |= (0x80 >> b);
|
|
||||||
}
|
|
||||||
ym = (ym + 2) % 4; xm = (xm + 2) % 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Init = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
glEnable(GL_POLYGON_STIPPLE);
|
|
||||||
if(forSel) {
|
|
||||||
glPolygonStipple(SelMask);
|
|
||||||
} else {
|
|
||||||
glPolygonStipple(HoverMask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void StippleTriangle(STriangle *tr, BOOL s, DWORD rgb)
|
|
||||||
{
|
|
||||||
glEnd();
|
|
||||||
glDisable(GL_LIGHTING);
|
|
||||||
glxColorRGB(rgb);
|
|
||||||
Stipple(s);
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
glxVertex3v(tr->a);
|
|
||||||
glxVertex3v(tr->b);
|
|
||||||
glxVertex3v(tr->c);
|
|
||||||
glEnd();
|
|
||||||
glEnable(GL_LIGHTING);
|
|
||||||
glDisable(GL_POLYGON_STIPPLE);
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxFillMesh(int specColor, SMesh *m, DWORD h, DWORD s1, DWORD s2)
|
|
||||||
{
|
|
||||||
DWORD rgbHovered = Style::Color(Style::HOVERED),
|
|
||||||
rgbSelected = Style::Color(Style::SELECTED);
|
|
||||||
|
|
||||||
glEnable(GL_NORMALIZE);
|
|
||||||
int prevColor = -1;
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
for(int i = 0; i < m->l.n; i++) {
|
|
||||||
STriangle *tr = &(m->l.elem[i]);
|
|
||||||
|
|
||||||
int color;
|
|
||||||
if(specColor < 0) {
|
|
||||||
color = tr->meta.color;
|
|
||||||
} else {
|
|
||||||
color = specColor;
|
|
||||||
}
|
|
||||||
if(color != prevColor) {
|
|
||||||
GLfloat mpf[] = { REDf(color), GREENf(color), BLUEf(color), 1.0 };
|
|
||||||
glEnd();
|
|
||||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf);
|
|
||||||
prevColor = color;
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 || tr->an.EqualsExactly(Vector::From(0, 0, 0))) {
|
|
||||||
// Compute the normal from the vertices
|
|
||||||
Vector n = tr->Normal();
|
|
||||||
glNormal3d(n.x, n.y, n.z);
|
|
||||||
glxVertex3v(tr->a);
|
|
||||||
glxVertex3v(tr->b);
|
|
||||||
glxVertex3v(tr->c);
|
|
||||||
} else {
|
|
||||||
// Use the exact normals that are specified
|
|
||||||
glNormal3d((tr->an).x, (tr->an).y, (tr->an).z);
|
|
||||||
glxVertex3v(tr->a);
|
|
||||||
|
|
||||||
glNormal3d((tr->bn).x, (tr->bn).y, (tr->bn).z);
|
|
||||||
glxVertex3v(tr->b);
|
|
||||||
|
|
||||||
glNormal3d((tr->cn).x, (tr->cn).y, (tr->cn).z);
|
|
||||||
glxVertex3v(tr->c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if((s1 != 0 && tr->meta.face == s1) ||
|
|
||||||
(s2 != 0 && tr->meta.face == s2))
|
|
||||||
{
|
|
||||||
StippleTriangle(tr, TRUE, rgbSelected);
|
|
||||||
}
|
|
||||||
if(h != 0 && tr->meta.face == h) {
|
|
||||||
StippleTriangle(tr, FALSE, rgbHovered);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GLX_CALLBACK Vertex(Vector *p)
|
|
||||||
{
|
|
||||||
glxVertex3v(*p);
|
|
||||||
}
|
|
||||||
void glxFillPolygon(SPolygon *p)
|
|
||||||
{
|
|
||||||
GLUtesselator *gt = gluNewTess();
|
|
||||||
gluTessCallback(gt, GLU_TESS_BEGIN, (glxCallbackFptr *)glBegin);
|
|
||||||
gluTessCallback(gt, GLU_TESS_END, (glxCallbackFptr *)glEnd);
|
|
||||||
gluTessCallback(gt, GLU_TESS_VERTEX, (glxCallbackFptr *)Vertex);
|
|
||||||
|
|
||||||
glxTesselatePolygon(gt, p);
|
|
||||||
|
|
||||||
gluDeleteTess(gt);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GLX_CALLBACK Combine(double coords[3], void *vertexData[4],
|
|
||||||
float weight[4], void **outData)
|
|
||||||
{
|
|
||||||
Vector *n = (Vector *)AllocTemporary(sizeof(Vector));
|
|
||||||
n->x = coords[0];
|
|
||||||
n->y = coords[1];
|
|
||||||
n->z = coords[2];
|
|
||||||
|
|
||||||
*outData = n;
|
|
||||||
}
|
|
||||||
void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
gluTessCallback(gt, GLU_TESS_COMBINE, (glxCallbackFptr *)Combine);
|
|
||||||
gluTessProperty(gt, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
|
|
||||||
|
|
||||||
Vector normal = p->normal;
|
|
||||||
glNormal3d(normal.x, normal.y, normal.z);
|
|
||||||
gluTessNormal(gt, normal.x, normal.y, normal.z);
|
|
||||||
|
|
||||||
gluTessBeginPolygon(gt, NULL);
|
|
||||||
for(i = 0; i < p->l.n; i++) {
|
|
||||||
SContour *sc = &(p->l.elem[i]);
|
|
||||||
gluTessBeginContour(gt);
|
|
||||||
for(j = 0; j < (sc->l.n-1); j++) {
|
|
||||||
SPoint *sp = &(sc->l.elem[j]);
|
|
||||||
double ap[3];
|
|
||||||
ap[0] = sp->p.x;
|
|
||||||
ap[1] = sp->p.y;
|
|
||||||
ap[2] = sp->p.z;
|
|
||||||
gluTessVertex(gt, ap, &(sp->p));
|
|
||||||
}
|
|
||||||
gluTessEndContour(gt);
|
|
||||||
}
|
|
||||||
gluTessEndPolygon(gt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxDebugPolygon(SPolygon *p)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
glLineWidth(2);
|
|
||||||
glPointSize(7);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
for(i = 0; i < p->l.n; i++) {
|
|
||||||
SContour *sc = &(p->l.elem[i]);
|
|
||||||
for(j = 0; j < (sc->l.n-1); j++) {
|
|
||||||
Vector a = (sc->l.elem[j]).p;
|
|
||||||
Vector b = (sc->l.elem[j+1]).p;
|
|
||||||
|
|
||||||
glxLockColorTo(RGB(0, 0, 255));
|
|
||||||
Vector d = (a.Minus(b)).WithMagnitude(-0);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(a.Plus(d));
|
|
||||||
glxVertex3v(b.Minus(d));
|
|
||||||
glEnd();
|
|
||||||
glxLockColorTo(RGB(255, 0, 0));
|
|
||||||
glBegin(GL_POINTS);
|
|
||||||
glxVertex3v(a.Plus(d));
|
|
||||||
glxVertex3v(b.Minus(d));
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxDrawEdges(SEdgeList *el, bool endpointsToo)
|
|
||||||
{
|
|
||||||
SEdge *se;
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
|
|
||||||
glxVertex3v(se->a);
|
|
||||||
glxVertex3v(se->b);
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
|
|
||||||
if(endpointsToo) {
|
|
||||||
glPointSize(12);
|
|
||||||
glBegin(GL_POINTS);
|
|
||||||
for(se = el->l.First(); se; se = el->l.NextAfter(se)) {
|
|
||||||
glxVertex3v(se->a);
|
|
||||||
glxVertex3v(se->b);
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxDebugMesh(SMesh *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
glLineWidth(1);
|
|
||||||
glPointSize(7);
|
|
||||||
glxDepthRangeOffset(1);
|
|
||||||
glxUnlockColor();
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
||||||
glxColorRGBa(RGB(0, 255, 0), 1.0);
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
for(i = 0; i < m->l.n; i++) {
|
|
||||||
STriangle *t = &(m->l.elem[i]);
|
|
||||||
if(t->tag) continue;
|
|
||||||
|
|
||||||
glxVertex3v(t->a);
|
|
||||||
glxVertex3v(t->b);
|
|
||||||
glxVertex3v(t->c);
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxMarkPolygonNormal(SPolygon *p)
|
|
||||||
{
|
|
||||||
Vector tail = Vector::From(0, 0, 0);
|
|
||||||
int i, j, cnt = 0;
|
|
||||||
// Choose some reasonable center point.
|
|
||||||
for(i = 0; i < p->l.n; i++) {
|
|
||||||
SContour *sc = &(p->l.elem[i]);
|
|
||||||
for(j = 0; j < (sc->l.n-1); j++) {
|
|
||||||
SPoint *sp = &(sc->l.elem[j]);
|
|
||||||
tail = tail.Plus(sp->p);
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(cnt == 0) return;
|
|
||||||
tail = tail.ScaledBy(1.0/cnt);
|
|
||||||
|
|
||||||
Vector gn = SS.GW.projRight.Cross(SS.GW.projUp);
|
|
||||||
Vector tip = tail.Plus((p->normal).WithMagnitude(40/SS.GW.scale));
|
|
||||||
Vector arrow = (p->normal).WithMagnitude(15/SS.GW.scale);
|
|
||||||
|
|
||||||
glColor3d(1, 1, 0);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glxVertex3v(tail);
|
|
||||||
glxVertex3v(tip);
|
|
||||||
glxVertex3v(tip);
|
|
||||||
glxVertex3v(tip.Minus(arrow.RotatedAbout(gn, 0.6)));
|
|
||||||
glxVertex3v(tip);
|
|
||||||
glxVertex3v(tip.Minus(arrow.RotatedAbout(gn, -0.6)));
|
|
||||||
glEnd();
|
|
||||||
glEnable(GL_LIGHTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxDepthRangeOffset(int units)
|
|
||||||
{
|
|
||||||
if(!DepthOffsetLocked) {
|
|
||||||
// The size of this step depends on the resolution of the Z buffer; for
|
|
||||||
// a 16-bit buffer, this should be fine.
|
|
||||||
double d = units/60000.0;
|
|
||||||
glDepthRange(0.1-d, 1-d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxDepthRangeLockToFront(bool yes)
|
|
||||||
{
|
|
||||||
if(yes) {
|
|
||||||
DepthOffsetLocked = true;
|
|
||||||
glDepthRange(0, 0);
|
|
||||||
} else {
|
|
||||||
DepthOffsetLocked = false;
|
|
||||||
glxDepthRangeOffset(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxCreateBitmapFont(void)
|
|
||||||
{
|
|
||||||
// Place the font in our texture in a two-dimensional grid; 1d would
|
|
||||||
// be simpler, but long skinny textures (256*16 = 4096 pixels wide)
|
|
||||||
// won't work.
|
|
||||||
static BYTE MappedTexture[4*16*64*16];
|
|
||||||
int a, i;
|
|
||||||
for(a = 0; a < 256; a++) {
|
|
||||||
int row = a / 4, col = a % 4;
|
|
||||||
|
|
||||||
for(i = 0; i < 16; i++) {
|
|
||||||
memcpy(MappedTexture + row*4*16*16 + col*16 + i*4*16,
|
|
||||||
FontTexture + a*16*16 + i*16,
|
|
||||||
16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, TEXTURE_BITMAP_FONT);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
|
|
||||||
16*4, 64*16,
|
|
||||||
0,
|
|
||||||
GL_ALPHA, GL_UNSIGNED_BYTE,
|
|
||||||
MappedTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxBitmapCharQuad(char c, double x, double y)
|
|
||||||
{
|
|
||||||
BYTE b = (BYTE)c;
|
|
||||||
int w, h;
|
|
||||||
|
|
||||||
if(b & 0x80) {
|
|
||||||
// Special character, like a checkbox or a radio button
|
|
||||||
w = h = 16;
|
|
||||||
x -= 3;
|
|
||||||
} else {
|
|
||||||
// Normal character from our font
|
|
||||||
w = SS.TW.CHAR_WIDTH,
|
|
||||||
h = SS.TW.CHAR_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(b != ' ' && b != 0) {
|
|
||||||
int row = b / 4, col = b % 4;
|
|
||||||
double s0 = col/4.0,
|
|
||||||
s1 = (col+1)/4.0,
|
|
||||||
t0 = row/64.0,
|
|
||||||
t1 = t0 + (w/16.0)/64;
|
|
||||||
|
|
||||||
glTexCoord2d(s1, t0);
|
|
||||||
glVertex2d(x, y);
|
|
||||||
|
|
||||||
glTexCoord2d(s1, t1);
|
|
||||||
glVertex2d(x + w, y);
|
|
||||||
|
|
||||||
glTexCoord2d(s0, t1);
|
|
||||||
glVertex2d(x + w, y - h);
|
|
||||||
|
|
||||||
glTexCoord2d(s0, t0);
|
|
||||||
glVertex2d(x, y - h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxBitmapText(char *str, Vector p)
|
|
||||||
{
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
while(*str) {
|
|
||||||
glxBitmapCharQuad(*str, p.x, p.y);
|
|
||||||
|
|
||||||
str++;
|
|
||||||
p.x += SS.TW.CHAR_WIDTH;
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
}
|
|
||||||
|
|
||||||
void glxDrawPixelsWithTexture(BYTE *data, int w, int h)
|
|
||||||
{
|
|
||||||
#define MAX_DIM 32
|
|
||||||
static BYTE Texture[MAX_DIM*MAX_DIM*3];
|
|
||||||
int i, j;
|
|
||||||
if(w > MAX_DIM || h > MAX_DIM) oops();
|
|
||||||
|
|
||||||
for(i = 0; i < w; i++) {
|
|
||||||
for(j = 0; j < h; j++) {
|
|
||||||
Texture[(j*MAX_DIM + i)*3 + 0] = data[(j*w + i)*3 + 0];
|
|
||||||
Texture[(j*MAX_DIM + i)*3 + 1] = data[(j*w + i)*3 + 1];
|
|
||||||
Texture[(j*MAX_DIM + i)*3 + 2] = data[(j*w + i)*3 + 2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, TEXTURE_DRAW_PIXELS);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
||||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MAX_DIM, MAX_DIM, 0,
|
|
||||||
GL_RGB, GL_UNSIGNED_BYTE, Texture);
|
|
||||||
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
glTexCoord2d(0, 0);
|
|
||||||
glVertex2d(0, h);
|
|
||||||
|
|
||||||
glTexCoord2d(((double)w)/MAX_DIM, 0);
|
|
||||||
glVertex2d(w, h);
|
|
||||||
|
|
||||||
glTexCoord2d(((double)w)/MAX_DIM, ((double)h)/MAX_DIM);
|
|
||||||
glVertex2d(w, 0);
|
|
||||||
|
|
||||||
glTexCoord2d(0, ((double)h)/MAX_DIM);
|
|
||||||
glVertex2d(0, 0);
|
|
||||||
glEnd();
|
|
||||||
glDisable(GL_TEXTURE_2D);
|
|
||||||
}
|
|
||||||
|
|
935
graphicswin.cpp
|
@ -1,935 +0,0 @@
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Top-level implementation of the program's main window, in which a graphical
|
|
||||||
// representation of the model is drawn and edited by the user.
|
|
||||||
//
|
|
||||||
// Copyright 2008-2013 Jonathan Westhues.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "solvespace.h"
|
|
||||||
|
|
||||||
#define mView (&GraphicsWindow::MenuView)
|
|
||||||
#define mEdit (&GraphicsWindow::MenuEdit)
|
|
||||||
#define mClip (&GraphicsWindow::MenuClipboard)
|
|
||||||
#define mReq (&GraphicsWindow::MenuRequest)
|
|
||||||
#define mCon (&Constraint::MenuConstrain)
|
|
||||||
#define mFile (&SolveSpace::MenuFile)
|
|
||||||
#define mGrp (&Group::MenuGroup)
|
|
||||||
#define mAna (&SolveSpace::MenuAnalyze)
|
|
||||||
#define mHelp (&SolveSpace::MenuHelp)
|
|
||||||
#define S 0x100
|
|
||||||
#define C 0x200
|
|
||||||
#define F(k) (0xf0+(k))
|
|
||||||
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
|
||||||
{ 0, "&File", 0, NULL },
|
|
||||||
{ 1, "&New\tCtrl+N", MNU_NEW, 'N'|C, mFile },
|
|
||||||
{ 1, "&Open...\tCtrl+O", MNU_OPEN, 'O'|C, mFile },
|
|
||||||
{10, "Open &Recent", MNU_OPEN_RECENT, 0, mFile },
|
|
||||||
{ 1, "&Save\tCtrl+S", MNU_SAVE, 'S'|C, mFile },
|
|
||||||
{ 1, "Save &As...", MNU_SAVE_AS, 0, mFile },
|
|
||||||
{ 1, NULL, 0, 0, NULL },
|
|
||||||
{ 1, "Export &Image...", MNU_EXPORT_PNG, 0, mFile },
|
|
||||||
{ 1, "Export 2d &View...", MNU_EXPORT_VIEW, 0, mFile },
|
|
||||||
{ 1, "Export 2d &Section...", MNU_EXPORT_SECTION, 0, mFile },
|
|
||||||
{ 1, "Export 3d &Wireframe...", MNU_EXPORT_WIREFRAME, 0, mFile },
|
|
||||||
{ 1, "Export Triangle &Mesh...", MNU_EXPORT_MESH, 0, mFile },
|
|
||||||
{ 1, "Export &Surfaces...", MNU_EXPORT_SURFACES,0, mFile },
|
|
||||||
{ 1, NULL, 0, 0, NULL },
|
|
||||||
{ 1, "E&xit", MNU_EXIT, 0, mFile },
|
|
||||||
|
|
||||||
{ 0, "&Edit", 0, NULL },
|
|
||||||
{ 1, "&Undo\tCtrl+Z", MNU_UNDO, 'Z'|C, mEdit },
|
|
||||||
{ 1, "&Redo\tCtrl+Y", MNU_REDO, 'Y'|C, mEdit },
|
|
||||||
{ 1, "Re&generate All\tSpace", MNU_REGEN_ALL, ' ', mEdit },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Snap Selection to &Grid\t.", MNU_SNAP_TO_GRID, '.', mEdit },
|
|
||||||
{ 1, "Rotate Imported &90°\t9", MNU_ROTATE_90, '9', mEdit },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Cu&t\tCtrl+X", MNU_CUT, 'X'|C, mClip },
|
|
||||||
{ 1, "&Copy\tCtrl+C", MNU_COPY, 'C'|C, mClip },
|
|
||||||
{ 1, "&Paste\tCtrl+V", MNU_PASTE, 'V'|C, mClip },
|
|
||||||
{ 1, "Paste &Transformed...\tCtrl+T", MNU_PASTE_TRANSFORM,'T'|C, mClip },
|
|
||||||
{ 1, "&Delete\tDel", MNU_DELETE, 127, mClip },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Select &Edge Chain\tCtrl+E", MNU_SELECT_CHAIN, 'E'|C, mEdit },
|
|
||||||
{ 1, "Select &All\tCtrl+A", MNU_SELECT_ALL, 'A'|C, mEdit },
|
|
||||||
{ 1, "&Unselect All\tEsc", MNU_UNSELECT_ALL, 27, mEdit },
|
|
||||||
|
|
||||||
{ 0, "&View", 0, NULL },
|
|
||||||
{ 1, "Zoom &In\t+", MNU_ZOOM_IN, '+', mView },
|
|
||||||
{ 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView },
|
|
||||||
{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Align View to &Workplane\tW", MNU_ONTO_WORKPLANE, 'W', mView },
|
|
||||||
{ 1, "Nearest &Ortho View\tF2", MNU_NEAREST_ORTHO, F(2), mView },
|
|
||||||
{ 1, "Nearest &Isometric View\tF3", MNU_NEAREST_ISO, F(3), mView },
|
|
||||||
{ 1, "&Center View At Point\tF4", MNU_CENTER_VIEW, F(4), mView },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Show Snap &Grid\t>", MNU_SHOW_GRID, '.'|S, mView },
|
|
||||||
{ 1, "Use &Perspective Projection\t`", MNU_PERSPECTIVE_PROJ,'`', mView },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Show Text &Window\tTab", MNU_SHOW_TEXT_WND, '\t', mView },
|
|
||||||
{ 1, "Show &Toolbar", MNU_SHOW_TOOLBAR, 0, mView },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Dimensions in &Inches", MNU_UNITS_INCHES, 0, mView },
|
|
||||||
{ 1, "Dimensions in &Millimeters", MNU_UNITS_MM, 0, mView },
|
|
||||||
|
|
||||||
{ 0, "&New Group", 0, 0, NULL },
|
|
||||||
{ 1, "Sketch In &3d\tShift+3", MNU_GROUP_3D, '3'|S, mGrp },
|
|
||||||
{ 1, "Sketch In New &Workplane\tShift+W", MNU_GROUP_WRKPL, 'W'|S, mGrp },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Step &Translating\tShift+T", MNU_GROUP_TRANS, 'T'|S, mGrp },
|
|
||||||
{ 1, "Step &Rotating\tShift+R", MNU_GROUP_ROT, 'R'|S, mGrp },
|
|
||||||
{ 1, NULL, 0, 0, NULL },
|
|
||||||
{ 1, "E&xtrude\tShift+X", MNU_GROUP_EXTRUDE, 'X'|S, mGrp },
|
|
||||||
{ 1, "&Lathe\tShift+L", MNU_GROUP_LATHE, 'L'|S, mGrp },
|
|
||||||
{ 1, NULL, 0, 0, NULL },
|
|
||||||
{ 1, "Import / Assemble...\tShift+I", MNU_GROUP_IMPORT, 'I'|S, mGrp },
|
|
||||||
{11, "Import Recent", MNU_GROUP_RECENT, 0, mGrp },
|
|
||||||
|
|
||||||
{ 0, "&Sketch", 0, NULL },
|
|
||||||
{ 1, "In &Workplane\t2", MNU_SEL_WORKPLANE, '2', mReq },
|
|
||||||
{ 1, "Anywhere In &3d\t3", MNU_FREE_IN_3D, '3', mReq },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq },
|
|
||||||
{ 1, "&Workplane", MNU_WORKPLANE, 0, mReq },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Line &Segment\tS", MNU_LINE_SEGMENT, 'S', mReq },
|
|
||||||
{ 1, "&Rectangle\tR", MNU_RECTANGLE, 'R', mReq },
|
|
||||||
{ 1, "&Circle\tC", MNU_CIRCLE, 'C', mReq },
|
|
||||||
{ 1, "&Arc of a Circle\tA", MNU_ARC, 'A', mReq },
|
|
||||||
{ 1, "&Bezier Cubic Spline\tB", MNU_CUBIC, 'B', mReq },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "&Text in TrueType Font\tT", MNU_TTF_TEXT, 'T', mReq },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "To&ggle Construction\tG", MNU_CONSTRUCTION, 'G', mReq },
|
|
||||||
{ 1, "Tangent &Arc at Point\tShift+A", MNU_TANGENT_ARC, 'A'|S, mReq },
|
|
||||||
{ 1, "Split Curves at &Intersection\tI", MNU_SPLIT_CURVES, 'I', mReq },
|
|
||||||
|
|
||||||
{ 0, "&Constrain", 0, NULL },
|
|
||||||
{ 1, "&Distance / Diameter\tD", MNU_DISTANCE_DIA, 'D', mCon },
|
|
||||||
{ 1, "A&ngle\tN", MNU_ANGLE, 'N', mCon },
|
|
||||||
{ 1, "Other S&upplementary Angle\tU", MNU_OTHER_ANGLE, 'U', mCon },
|
|
||||||
{ 1, "Toggle R&eference Dim\tE", MNU_REFERENCE, 'E', mCon },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "&Horizontal\tH", MNU_HORIZONTAL, 'H', mCon },
|
|
||||||
{ 1, "&Vertical\tV", MNU_VERTICAL, 'V', mCon },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "&On Point / Curve / Plane\tO", MNU_ON_ENTITY, 'O', mCon },
|
|
||||||
{ 1, "E&qual Length / Radius / Angle\tQ", MNU_EQUAL, 'Q', mCon },
|
|
||||||
{ 1, "Length Ra&tio\tZ", MNU_RATIO, 'Z', mCon },
|
|
||||||
{ 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon },
|
|
||||||
{ 1, "S&ymmetric\tY", MNU_SYMMETRIC, 'Y', mCon },
|
|
||||||
{ 1, "Para&llel / Tangent\tL", MNU_PARALLEL, 'L', mCon },
|
|
||||||
{ 1, "&Perpendicular\t[", MNU_PERPENDICULAR, '[', mCon },
|
|
||||||
{ 1, "Same Orient&ation\tX", MNU_ORIENTED_SAME, 'X', mCon },
|
|
||||||
{ 1, "Lock Point Where &Dragged\t]", MNU_WHERE_DRAGGED, ']', mCon },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Comment\t;", MNU_COMMENT, ';', mCon },
|
|
||||||
|
|
||||||
{ 0, "&Analyze", 0, NULL },
|
|
||||||
{ 1, "Measure &Volume\tCtrl+Shift+V", MNU_VOLUME, 'V'|S|C,mAna },
|
|
||||||
{ 1, "Measure &Area\tCtrl+Shift+A", MNU_AREA, 'A'|S|C,mAna },
|
|
||||||
{ 1, "Show &Interfering Parts\tCtrl+Shift+I", MNU_INTERFERENCE, 'I'|S|C,mAna },
|
|
||||||
{ 1, "Show &Naked Edges\tCtrl+Shift+N", MNU_NAKED_EDGES, 'N'|S|C,mAna },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "Show Degrees of &Freedom\tCtrl+Shift+F", MNU_SHOW_DOF, 'F'|S|C,mAna },
|
|
||||||
{ 1, NULL, 0, NULL },
|
|
||||||
{ 1, "&Trace Point\tCtrl+Shift+T", MNU_TRACE_PT, 'T'|S|C,mAna },
|
|
||||||
{ 1, "&Stop Tracing...\tCtrl+Shift+S", MNU_STOP_TRACING, 'S'|S|C,mAna },
|
|
||||||
{ 1, "Step &Dimension...\tCtrl+Shift+D", MNU_STEP_DIM, 'D'|S|C,mAna },
|
|
||||||
|
|
||||||
{ 0, "&Help", 0, 0, NULL },
|
|
||||||
{ 1, "&Website / Manual", MNU_WEBSITE, 0, mHelp },
|
|
||||||
{ 1, "&About", MNU_ABOUT, 0, mHelp },
|
|
||||||
{ -1 },
|
|
||||||
};
|
|
||||||
|
|
||||||
void GraphicsWindow::Init(void) {
|
|
||||||
memset(this, 0, sizeof(*this));
|
|
||||||
|
|
||||||
scale = 5;
|
|
||||||
offset = Vector::From(0, 0, 0);
|
|
||||||
projRight = Vector::From(1, 0, 0);
|
|
||||||
projUp = Vector::From(0, 1, 0);
|
|
||||||
|
|
||||||
// Make sure those are valid; could get a mouse move without a mouse
|
|
||||||
// down if someone depresses the button, then drags into our window.
|
|
||||||
orig.projRight = projRight;
|
|
||||||
orig.projUp = projUp;
|
|
||||||
|
|
||||||
// And with the last group active
|
|
||||||
activeGroup = SK.group.elem[SK.group.n-1].h;
|
|
||||||
SK.GetGroup(activeGroup)->Activate();
|
|
||||||
|
|
||||||
showWorkplanes = false;
|
|
||||||
showNormals = true;
|
|
||||||
showPoints = true;
|
|
||||||
showConstraints = true;
|
|
||||||
showHdnLines = false;
|
|
||||||
showShaded = true;
|
|
||||||
showEdges = true;
|
|
||||||
showMesh = false;
|
|
||||||
|
|
||||||
showTextWindow = true;
|
|
||||||
ShowTextWindow(showTextWindow);
|
|
||||||
|
|
||||||
showSnapGrid = false;
|
|
||||||
context.active = false;
|
|
||||||
|
|
||||||
// Do this last, so that all the menus get updated correctly.
|
|
||||||
EnsureValidActives();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::AnimateOntoWorkplane(void) {
|
|
||||||
if(!LockedInWorkplane()) return;
|
|
||||||
|
|
||||||
Entity *w = SK.GetEntity(ActiveWorkplane());
|
|
||||||
Quaternion quatf = w->Normal()->NormalGetNum();
|
|
||||||
Vector offsetf = (SK.GetEntity(w->point[0])->PointGetNum()).ScaledBy(-1);
|
|
||||||
|
|
||||||
AnimateOnto(quatf, offsetf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::AnimateOnto(Quaternion quatf, Vector offsetf) {
|
|
||||||
// Get our initial orientation and translation.
|
|
||||||
Quaternion quat0 = Quaternion::From(projRight, projUp);
|
|
||||||
Vector offset0 = offset;
|
|
||||||
|
|
||||||
// Make sure we take the shorter of the two possible paths.
|
|
||||||
double mp = (quatf.Minus(quat0)).Magnitude();
|
|
||||||
double mm = (quatf.Plus(quat0)).Magnitude();
|
|
||||||
if(mp > mm) {
|
|
||||||
quatf = quatf.ScaledBy(-1);
|
|
||||||
mp = mm;
|
|
||||||
}
|
|
||||||
double mo = (offset0.Minus(offsetf)).Magnitude()*scale;
|
|
||||||
|
|
||||||
// Animate transition, unless it's a tiny move.
|
|
||||||
SDWORD dt = (mp < 0.01 && mo < 10) ? (-20) :
|
|
||||||
(SDWORD)(100 + 1000*mp + 0.4*mo);
|
|
||||||
// Don't ever animate for longer than 2000 ms; we can get absurdly
|
|
||||||
// long translations (as measured in pixels) if the user zooms out, moves,
|
|
||||||
// and then zooms in again.
|
|
||||||
if(dt > 2000) dt = 2000;
|
|
||||||
SDWORD tn, t0 = GetMilliseconds();
|
|
||||||
double s = 0;
|
|
||||||
Quaternion dq = quatf.Times(quat0.Inverse());
|
|
||||||
do {
|
|
||||||
offset = (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
|
|
||||||
Quaternion quat = (dq.ToThe(s)).Times(quat0);
|
|
||||||
quat = quat.WithMagnitude(1);
|
|
||||||
|
|
||||||
projRight = quat.RotationU();
|
|
||||||
projUp = quat.RotationV();
|
|
||||||
PaintGraphics();
|
|
||||||
|
|
||||||
tn = GetMilliseconds();
|
|
||||||
s = (tn - t0)/((double)dt);
|
|
||||||
} while((tn - t0) < dt);
|
|
||||||
|
|
||||||
projRight = quatf.RotationU();
|
|
||||||
projUp = quatf.RotationV();
|
|
||||||
offset = offsetf;
|
|
||||||
InvalidateGraphics();
|
|
||||||
// If the view screen is open, then we need to refresh it.
|
|
||||||
SS.later.showTW = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::HandlePointForZoomToFit(Vector p,
|
|
||||||
Point2d *pmax, Point2d *pmin, double *wmin, bool div)
|
|
||||||
{
|
|
||||||
double w;
|
|
||||||
Vector pp = ProjectPoint4(p, &w);
|
|
||||||
// If div is true, then we calculate a perspective projection of the point.
|
|
||||||
// If not, then we do a parallel projection regardless of the current
|
|
||||||
// scale factor.
|
|
||||||
if(div) {
|
|
||||||
pp = pp.ScaledBy(1.0/w);
|
|
||||||
}
|
|
||||||
|
|
||||||
pmax->x = max(pmax->x, pp.x);
|
|
||||||
pmax->y = max(pmax->y, pp.y);
|
|
||||||
pmin->x = min(pmin->x, pp.x);
|
|
||||||
pmin->y = min(pmin->y, pp.y);
|
|
||||||
*wmin = min(*wmin, w);
|
|
||||||
}
|
|
||||||
void GraphicsWindow::LoopOverPoints(Point2d *pmax, Point2d *pmin, double *wmin,
|
|
||||||
bool div, bool includingInvisibles)
|
|
||||||
{
|
|
||||||
HandlePointForZoomToFit(Vector::From(0, 0, 0), pmax, pmin, wmin, div);
|
|
||||||
|
|
||||||
int i, j;
|
|
||||||
for(i = 0; i < SK.entity.n; i++) {
|
|
||||||
Entity *e = &(SK.entity.elem[i]);
|
|
||||||
if(!(e->IsVisible() || includingInvisibles)) continue;
|
|
||||||
if(e->IsPoint()) {
|
|
||||||
HandlePointForZoomToFit(e->PointGetNum(), pmax, pmin, wmin, div);
|
|
||||||
} else if(e->type == Entity::CIRCLE) {
|
|
||||||
// Lots of entities can extend outside the bbox of their points,
|
|
||||||
// but circles are particularly bad. We want to get things halfway
|
|
||||||
// reasonable without the mesh, because a zoom to fit is used to
|
|
||||||
// set the zoom level to set the chord tol.
|
|
||||||
double r = e->CircleGetRadiusNum();
|
|
||||||
Vector c = SK.GetEntity(e->point[0])->PointGetNum();
|
|
||||||
Quaternion q = SK.GetEntity(e->normal)->NormalGetNum();
|
|
||||||
for(j = 0; j < 4; j++) {
|
|
||||||
Vector p = (j == 0) ? (c.Plus(q.RotationU().ScaledBy( r))) :
|
|
||||||
(j == 1) ? (c.Plus(q.RotationU().ScaledBy(-r))) :
|
|
||||||
(j == 2) ? (c.Plus(q.RotationV().ScaledBy( r))) :
|
|
||||||
(c.Plus(q.RotationV().ScaledBy(-r)));
|
|
||||||
HandlePointForZoomToFit(p, pmax, pmin, wmin, div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Group *g = SK.GetGroup(activeGroup);
|
|
||||||
g->GenerateDisplayItems();
|
|
||||||
for(i = 0; i < g->displayMesh.l.n; i++) {
|
|
||||||
STriangle *tr = &(g->displayMesh.l.elem[i]);
|
|
||||||
HandlePointForZoomToFit(tr->a, pmax, pmin, wmin, div);
|
|
||||||
HandlePointForZoomToFit(tr->b, pmax, pmin, wmin, div);
|
|
||||||
HandlePointForZoomToFit(tr->c, pmax, pmin, wmin, div);
|
|
||||||
}
|
|
||||||
for(i = 0; i < g->polyLoops.l.n; i++) {
|
|
||||||
SContour *sc = &(g->polyLoops.l.elem[i]);
|
|
||||||
for(j = 0; j < sc->l.n; j++) {
|
|
||||||
HandlePointForZoomToFit(sc->l.elem[j].p, pmax, pmin, wmin, div);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void GraphicsWindow::ZoomToFit(bool includingInvisibles) {
|
|
||||||
// On the first run, ignore perspective.
|
|
||||||
Point2d pmax = { -1e12, -1e12 }, pmin = { 1e12, 1e12 };
|
|
||||||
double wmin = 1;
|
|
||||||
LoopOverPoints(&pmax, &pmin, &wmin, false, includingInvisibles);
|
|
||||||
|
|
||||||
double xm = (pmax.x + pmin.x)/2, ym = (pmax.y + pmin.y)/2;
|
|
||||||
double dx = pmax.x - pmin.x, dy = pmax.y - pmin.y;
|
|
||||||
|
|
||||||
offset = offset.Plus(projRight.ScaledBy(-xm)).Plus(
|
|
||||||
projUp. ScaledBy(-ym));
|
|
||||||
|
|
||||||
// And based on this, we calculate the scale and offset
|
|
||||||
if(dx == 0 && dy == 0) {
|
|
||||||
scale = 5;
|
|
||||||
} else {
|
|
||||||
double scalex = 1e12, scaley = 1e12;
|
|
||||||
if(dx != 0) scalex = 0.9*width /dx;
|
|
||||||
if(dy != 0) scaley = 0.9*height/dy;
|
|
||||||
scale = min(scalex, scaley);
|
|
||||||
|
|
||||||
scale = min(300, scale);
|
|
||||||
scale = max(0.003, scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then do another run, considering the perspective.
|
|
||||||
pmax.x = -1e12; pmax.y = -1e12;
|
|
||||||
pmin.x = 1e12; pmin.y = 1e12;
|
|
||||||
wmin = 1;
|
|
||||||
LoopOverPoints(&pmax, &pmin, &wmin, true, includingInvisibles);
|
|
||||||
|
|
||||||
// Adjust the scale so that no points are behind the camera
|
|
||||||
if(wmin < 0.1) {
|
|
||||||
double k = SS.CameraTangent();
|
|
||||||
// w = 1+k*scale*z
|
|
||||||
double zmin = (wmin - 1)/(k*scale);
|
|
||||||
// 0.1 = 1 + k*scale*zmin
|
|
||||||
// (0.1 - 1)/(k*zmin) = scale
|
|
||||||
scale = min(scale, (0.1 - 1)/(k*zmin));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::MenuView(int id) {
|
|
||||||
switch(id) {
|
|
||||||
case MNU_ZOOM_IN:
|
|
||||||
SS.GW.scale *= 1.2;
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_ZOOM_OUT:
|
|
||||||
SS.GW.scale /= 1.2;
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_ZOOM_TO_FIT:
|
|
||||||
SS.GW.ZoomToFit(false);
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_SHOW_GRID:
|
|
||||||
SS.GW.showSnapGrid = !SS.GW.showSnapGrid;
|
|
||||||
if(SS.GW.showSnapGrid && !SS.GW.LockedInWorkplane()) {
|
|
||||||
Message("No workplane is active, so the grid will not "
|
|
||||||
"appear.");
|
|
||||||
}
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_PERSPECTIVE_PROJ:
|
|
||||||
SS.usePerspectiveProj = !SS.usePerspectiveProj;
|
|
||||||
if(SS.cameraTangent < 1e-6) {
|
|
||||||
Error("The perspective factor is set to zero, so the view will "
|
|
||||||
"always be a parallel projection.\n\n"
|
|
||||||
"For a perspective projection, modify the perspective "
|
|
||||||
"factor in the configuration screen. A value around 0.3 "
|
|
||||||
"is typical.");
|
|
||||||
}
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_ONTO_WORKPLANE:
|
|
||||||
if(!SS.GW.LockedInWorkplane()) {
|
|
||||||
Error("No workplane is active.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SS.GW.AnimateOntoWorkplane();
|
|
||||||
SS.GW.ClearSuper();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_NEAREST_ORTHO:
|
|
||||||
case MNU_NEAREST_ISO: {
|
|
||||||
static const Vector ortho[3] = {
|
|
||||||
Vector::From(1, 0, 0),
|
|
||||||
Vector::From(0, 1, 0),
|
|
||||||
Vector::From(0, 0, 1)
|
|
||||||
};
|
|
||||||
double sqrt2 = sqrt(2.0), sqrt6 = sqrt(6.0);
|
|
||||||
Quaternion quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
|
|
||||||
Quaternion quatf = quat0;
|
|
||||||
double dmin = 1e10;
|
|
||||||
|
|
||||||
// There are 24 possible views; 3*2*2*2
|
|
||||||
int i, j, negi, negj;
|
|
||||||
for(i = 0; i < 3; i++) {
|
|
||||||
for(j = 0; j < 3; j++) {
|
|
||||||
if(i == j) continue;
|
|
||||||
for(negi = 0; negi < 2; negi++) {
|
|
||||||
for(negj = 0; negj < 2; negj++) {
|
|
||||||
Vector ou = ortho[i], ov = ortho[j];
|
|
||||||
if(negi) ou = ou.ScaledBy(-1);
|
|
||||||
if(negj) ov = ov.ScaledBy(-1);
|
|
||||||
Vector on = ou.Cross(ov);
|
|
||||||
|
|
||||||
Vector u, v;
|
|
||||||
if(id == MNU_NEAREST_ORTHO) {
|
|
||||||
u = ou;
|
|
||||||
v = ov;
|
|
||||||
} else {
|
|
||||||
u =
|
|
||||||
ou.ScaledBy(1/sqrt2).Plus(
|
|
||||||
on.ScaledBy(-1/sqrt2));
|
|
||||||
v =
|
|
||||||
ou.ScaledBy(-1/sqrt6).Plus(
|
|
||||||
ov.ScaledBy(2/sqrt6).Plus(
|
|
||||||
on.ScaledBy(-1/sqrt6)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Quaternion quatt = Quaternion::From(u, v);
|
|
||||||
double d = min(
|
|
||||||
(quatt.Minus(quat0)).Magnitude(),
|
|
||||||
(quatt.Plus(quat0)).Magnitude());
|
|
||||||
if(d < dmin) {
|
|
||||||
dmin = d;
|
|
||||||
quatf = quatt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SS.GW.AnimateOnto(quatf, SS.GW.offset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MNU_CENTER_VIEW:
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
|
||||||
Quaternion quat0;
|
|
||||||
// Offset is the selected point, quaternion is same as before
|
|
||||||
Vector pt = SK.GetEntity(SS.GW.gs.point[0])->PointGetNum();
|
|
||||||
quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
|
|
||||||
SS.GW.AnimateOnto(quat0, pt.ScaledBy(-1));
|
|
||||||
SS.GW.ClearSelection();
|
|
||||||
} else {
|
|
||||||
Error("Select a point; this point will become the center "
|
|
||||||
"of the view on screen.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_SHOW_TEXT_WND:
|
|
||||||
SS.GW.showTextWindow = !SS.GW.showTextWindow;
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_SHOW_TOOLBAR:
|
|
||||||
SS.showToolbar = !SS.showToolbar;
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_UNITS_MM:
|
|
||||||
SS.viewUnits = SolveSpace::UNIT_MM;
|
|
||||||
SS.later.showTW = true;
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_UNITS_INCHES:
|
|
||||||
SS.viewUnits = SolveSpace::UNIT_INCHES;
|
|
||||||
SS.later.showTW = true;
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::EnsureValidActives(void) {
|
|
||||||
bool change = false;
|
|
||||||
// The active group must exist, and not be the references.
|
|
||||||
Group *g = SK.group.FindByIdNoOops(activeGroup);
|
|
||||||
if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) {
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < SK.group.n; i++) {
|
|
||||||
if(SK.group.elem[i].h.v != Group::HGROUP_REFERENCES.v) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(i >= SK.group.n) {
|
|
||||||
// This can happen if the user deletes all the groups in the
|
|
||||||
// sketch. It's difficult to prevent that, because the last
|
|
||||||
// group might have been deleted automatically, because it failed
|
|
||||||
// a dependency. There needs to be something, so create a plane
|
|
||||||
// drawing group and activate that. They should never be able
|
|
||||||
// to delete the references, though.
|
|
||||||
activeGroup = SS.CreateDefaultDrawingGroup();
|
|
||||||
} else {
|
|
||||||
activeGroup = SK.group.elem[i].h;
|
|
||||||
}
|
|
||||||
SK.GetGroup(activeGroup)->Activate();
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The active coordinate system must also exist.
|
|
||||||
if(LockedInWorkplane()) {
|
|
||||||
Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane());
|
|
||||||
if(e) {
|
|
||||||
hGroup hgw = e->group;
|
|
||||||
if(hgw.v != activeGroup.v && SS.GroupsInOrder(activeGroup, hgw)) {
|
|
||||||
// The active workplane is in a group that comes after the
|
|
||||||
// active group; so any request or constraint will fail.
|
|
||||||
SetWorkplaneFreeIn3d();
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SetWorkplaneFreeIn3d();
|
|
||||||
change = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// And update the checked state for various menus
|
|
||||||
bool locked = LockedInWorkplane();
|
|
||||||
CheckMenuById(MNU_FREE_IN_3D, !locked);
|
|
||||||
CheckMenuById(MNU_SEL_WORKPLANE, locked);
|
|
||||||
|
|
||||||
SS.UndoEnableMenus();
|
|
||||||
|
|
||||||
switch(SS.viewUnits) {
|
|
||||||
case SolveSpace::UNIT_MM:
|
|
||||||
case SolveSpace::UNIT_INCHES:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SS.viewUnits = SolveSpace::UNIT_MM;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CheckMenuById(MNU_UNITS_MM, SS.viewUnits == SolveSpace::UNIT_MM);
|
|
||||||
CheckMenuById(MNU_UNITS_INCHES, SS.viewUnits == SolveSpace::UNIT_INCHES);
|
|
||||||
|
|
||||||
ShowTextWindow(SS.GW.showTextWindow);
|
|
||||||
CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow);
|
|
||||||
|
|
||||||
CheckMenuById(MNU_SHOW_TOOLBAR, SS.showToolbar);
|
|
||||||
CheckMenuById(MNU_PERSPECTIVE_PROJ, SS.usePerspectiveProj);
|
|
||||||
CheckMenuById(MNU_SHOW_GRID, SS.GW.showSnapGrid);
|
|
||||||
|
|
||||||
if(change) SS.later.showTW = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::SetWorkplaneFreeIn3d(void) {
|
|
||||||
SK.GetGroup(activeGroup)->activeWorkplane = Entity::FREE_IN_3D;
|
|
||||||
}
|
|
||||||
hEntity GraphicsWindow::ActiveWorkplane(void) {
|
|
||||||
Group *g = SK.group.FindByIdNoOops(activeGroup);
|
|
||||||
if(g) {
|
|
||||||
return g->activeWorkplane;
|
|
||||||
} else {
|
|
||||||
return Entity::FREE_IN_3D;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool GraphicsWindow::LockedInWorkplane(void) {
|
|
||||||
return (SS.GW.ActiveWorkplane().v != Entity::FREE_IN_3D.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::ForceTextWindowShown(void) {
|
|
||||||
if(!showTextWindow) {
|
|
||||||
showTextWindow = true;
|
|
||||||
CheckMenuById(MNU_SHOW_TEXT_WND, true);
|
|
||||||
ShowTextWindow(TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::DeleteTaggedRequests(void) {
|
|
||||||
// Rewrite any point-coincident constraints that were affected by this
|
|
||||||
// deletion.
|
|
||||||
Request *r;
|
|
||||||
for(r = SK.request.First(); r; r = SK.request.NextAfter(r)) {
|
|
||||||
if(!r->tag) continue;
|
|
||||||
FixConstraintsForRequestBeingDeleted(r->h);
|
|
||||||
}
|
|
||||||
// and then delete the tagged requests.
|
|
||||||
SK.request.RemoveTagged();
|
|
||||||
|
|
||||||
// An edit might be in progress for the just-deleted item. So
|
|
||||||
// now it's not.
|
|
||||||
HideGraphicsEditControl();
|
|
||||||
SS.TW.HideEditControl();
|
|
||||||
// And clear out the selection, which could contain that item.
|
|
||||||
ClearSuper();
|
|
||||||
// And regenerate to get rid of what it generates, plus anything
|
|
||||||
// that references it (since the regen code checks for that).
|
|
||||||
SS.GenerateAll(0, INT_MAX);
|
|
||||||
EnsureValidActives();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector GraphicsWindow::SnapToGrid(Vector p) {
|
|
||||||
if(!LockedInWorkplane()) return p;
|
|
||||||
|
|
||||||
EntityBase *wrkpl = SK.GetEntity(ActiveWorkplane()),
|
|
||||||
*norm = wrkpl->Normal();
|
|
||||||
Vector wo = SK.GetEntity(wrkpl->point[0])->PointGetNum(),
|
|
||||||
wu = norm->NormalU(),
|
|
||||||
wv = norm->NormalV(),
|
|
||||||
wn = norm->NormalN();
|
|
||||||
|
|
||||||
Vector pp = (p.Minus(wo)).DotInToCsys(wu, wv, wn);
|
|
||||||
pp.x = floor((pp.x / SS.gridSpacing) + 0.5)*SS.gridSpacing;
|
|
||||||
pp.y = floor((pp.y / SS.gridSpacing) + 0.5)*SS.gridSpacing;
|
|
||||||
pp.z = 0;
|
|
||||||
|
|
||||||
return pp.ScaleOutOfCsys(wu, wv, wn).Plus(wo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::MenuEdit(int id) {
|
|
||||||
switch(id) {
|
|
||||||
case MNU_UNSELECT_ALL:
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
// If there's nothing selected to de-select, and no operation
|
|
||||||
// to cancel, then perhaps they want to return to the home
|
|
||||||
// screen in the text window.
|
|
||||||
if(SS.GW.gs.n == 0 &&
|
|
||||||
SS.GW.gs.constraints == 0 &&
|
|
||||||
SS.GW.pending.operation == 0)
|
|
||||||
{
|
|
||||||
if(!(TextEditControlIsVisible() ||
|
|
||||||
GraphicsEditControlIsVisible()))
|
|
||||||
{
|
|
||||||
if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) {
|
|
||||||
SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES);
|
|
||||||
} else {
|
|
||||||
SS.TW.ClearSuper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SS.GW.ClearSuper();
|
|
||||||
SS.TW.HideEditControl();
|
|
||||||
SS.nakedEdges.Clear();
|
|
||||||
SS.justExportedInfo.draw = false;
|
|
||||||
// This clears the marks drawn to indicate which points are
|
|
||||||
// still free to drag.
|
|
||||||
Param *p;
|
|
||||||
for(p = SK.param.First(); p; p = SK.param.NextAfter(p)) {
|
|
||||||
p->free = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_SELECT_ALL: {
|
|
||||||
Entity *e;
|
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
|
||||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
|
||||||
if(e->IsFace() || e->IsDistance()) continue;
|
|
||||||
if(!e->IsVisible()) continue;
|
|
||||||
|
|
||||||
SS.GW.MakeSelected(e->h);
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MNU_SELECT_CHAIN: {
|
|
||||||
Entity *e;
|
|
||||||
int newlySelected = 0;
|
|
||||||
bool didSomething;
|
|
||||||
do {
|
|
||||||
didSomething = false;
|
|
||||||
for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
|
|
||||||
if(e->group.v != SS.GW.activeGroup.v) continue;
|
|
||||||
if(!e->HasEndpoints()) continue;
|
|
||||||
if(!e->IsVisible()) continue;
|
|
||||||
|
|
||||||
Vector st = e->EndpointStart(),
|
|
||||||
fi = e->EndpointFinish();
|
|
||||||
|
|
||||||
bool onChain = false, alreadySelected = false;
|
|
||||||
List<Selection> *ls = &(SS.GW.selection);
|
|
||||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
|
||||||
if(!s->entity.v) continue;
|
|
||||||
if(s->entity.v == e->h.v) {
|
|
||||||
alreadySelected = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Entity *se = SK.GetEntity(s->entity);
|
|
||||||
if(!se->HasEndpoints()) continue;
|
|
||||||
|
|
||||||
Vector sst = se->EndpointStart(),
|
|
||||||
sfi = se->EndpointFinish();
|
|
||||||
|
|
||||||
if(sst.Equals(st) || sst.Equals(fi) ||
|
|
||||||
sfi.Equals(st) || sfi.Equals(fi))
|
|
||||||
{
|
|
||||||
onChain = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(onChain && !alreadySelected) {
|
|
||||||
SS.GW.MakeSelected(e->h);
|
|
||||||
newlySelected++;
|
|
||||||
didSomething = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while(didSomething);
|
|
||||||
if(newlySelected == 0) {
|
|
||||||
Error("No additional entities share endpoints with the "
|
|
||||||
"selected entities.");
|
|
||||||
}
|
|
||||||
InvalidateGraphics();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MNU_ROTATE_90: {
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
Entity *e = NULL;
|
|
||||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
|
||||||
e = SK.GetEntity(SS.GW.gs.point[0]);
|
|
||||||
} else if(SS.GW.gs.n == 1 && SS.GW.gs.entities == 1) {
|
|
||||||
e = SK.GetEntity(SS.GW.gs.entity[0]);
|
|
||||||
}
|
|
||||||
SS.GW.ClearSelection();
|
|
||||||
|
|
||||||
hGroup hg = e ? e->group : SS.GW.activeGroup;
|
|
||||||
Group *g = SK.GetGroup(hg);
|
|
||||||
if(g->type != Group::IMPORTED) {
|
|
||||||
Error("To use this command, select a point or other "
|
|
||||||
"entity from an imported part, or make an import "
|
|
||||||
"group the active group.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SS.UndoRemember();
|
|
||||||
// Rotate by ninety degrees about the coordinate axis closest
|
|
||||||
// to the screen normal.
|
|
||||||
Vector norm = SS.GW.projRight.Cross(SS.GW.projUp);
|
|
||||||
norm = norm.ClosestOrtho();
|
|
||||||
norm = norm.WithMagnitude(1);
|
|
||||||
Quaternion qaa = Quaternion::From(norm, PI/2);
|
|
||||||
|
|
||||||
g->TransformImportedBy(Vector::From(0, 0, 0), qaa);
|
|
||||||
|
|
||||||
// and regenerate as necessary.
|
|
||||||
SS.MarkGroupDirty(hg);
|
|
||||||
SS.GenerateAll();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MNU_SNAP_TO_GRID: {
|
|
||||||
if(!SS.GW.LockedInWorkplane()) {
|
|
||||||
Error("No workplane is active. Select a workplane to define "
|
|
||||||
"the plane for the snap grid.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
if(SS.GW.gs.points == 0 && SS.GW.gs.comments == 0) {
|
|
||||||
Error("Can't snap these items to grid; select points or "
|
|
||||||
"text comments. To snap a line, select its endpoints.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SS.UndoRemember();
|
|
||||||
|
|
||||||
List<Selection> *ls = &(SS.GW.selection);
|
|
||||||
for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
|
|
||||||
if(s->entity.v) {
|
|
||||||
hEntity hp = s->entity;
|
|
||||||
Entity *ep = SK.GetEntity(hp);
|
|
||||||
if(!ep->IsPoint()) continue;
|
|
||||||
|
|
||||||
Vector p = ep->PointGetNum();
|
|
||||||
ep->PointForceTo(SS.GW.SnapToGrid(p));
|
|
||||||
SS.GW.pending.points.Add(&hp);
|
|
||||||
SS.MarkGroupDirty(ep->group);
|
|
||||||
} else if(s->constraint.v) {
|
|
||||||
Constraint *c = SK.GetConstraint(s->constraint);
|
|
||||||
if(c->type != Constraint::COMMENT) continue;
|
|
||||||
|
|
||||||
c->disp.offset = SS.GW.SnapToGrid(c->disp.offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Regenerate, with these points marked as dragged so that they
|
|
||||||
// get placed as close as possible to our snap grid.
|
|
||||||
SS.GenerateAll();
|
|
||||||
SS.GW.ClearPending();
|
|
||||||
|
|
||||||
SS.GW.ClearSelection();
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MNU_UNDO:
|
|
||||||
SS.UndoUndo();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_REDO:
|
|
||||||
SS.UndoRedo();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_REGEN_ALL:
|
|
||||||
SS.ReloadAllImported();
|
|
||||||
SS.GenerateAll(0, INT_MAX);
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::MenuRequest(int id) {
|
|
||||||
char *s;
|
|
||||||
switch(id) {
|
|
||||||
case MNU_SEL_WORKPLANE: {
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
|
||||||
|
|
||||||
if(SS.GW.gs.n == 1 && SS.GW.gs.workplanes == 1) {
|
|
||||||
// A user-selected workplane
|
|
||||||
g->activeWorkplane = SS.GW.gs.entity[0];
|
|
||||||
} else if(g->type == Group::DRAWING_WORKPLANE) {
|
|
||||||
// The group's default workplane
|
|
||||||
g->activeWorkplane = g->h.entity(0);
|
|
||||||
Message("No workplane selected. Activating default workplane "
|
|
||||||
"for this group.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!SS.GW.LockedInWorkplane()) {
|
|
||||||
Error("No workplane is selected, and the active group does "
|
|
||||||
"not have a default workplane. Try selecting a "
|
|
||||||
"workplane, or activating a sketch-in-new-workplane "
|
|
||||||
"group.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Align the view with the selected workplane
|
|
||||||
SS.GW.AnimateOntoWorkplane();
|
|
||||||
SS.GW.ClearSuper();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MNU_FREE_IN_3D:
|
|
||||||
SS.GW.SetWorkplaneFreeIn3d();
|
|
||||||
SS.GW.EnsureValidActives();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
InvalidateGraphics();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_TANGENT_ARC:
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
|
|
||||||
SS.GW.MakeTangentArc();
|
|
||||||
} else if(SS.GW.gs.n != 0) {
|
|
||||||
Error("Bad selection for tangent arc at point. Select a "
|
|
||||||
"single point, or select nothing to set up arc "
|
|
||||||
"parameters.");
|
|
||||||
} else {
|
|
||||||
SS.TW.GoToScreen(TextWindow::SCREEN_TANGENT_ARC);
|
|
||||||
SS.GW.ForceTextWindowShown();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c;
|
|
||||||
case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
|
|
||||||
case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
|
|
||||||
case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
|
|
||||||
case MNU_CIRCLE: s = "click center of circle"; goto c;
|
|
||||||
case MNU_WORKPLANE: s = "click origin of workplane"; goto c;
|
|
||||||
case MNU_RECTANGLE: s = "click one corner of rectangle"; goto c;
|
|
||||||
case MNU_TTF_TEXT: s = "click top left of text"; goto c;
|
|
||||||
c:
|
|
||||||
SS.GW.pending.operation = id;
|
|
||||||
SS.GW.pending.description = s;
|
|
||||||
SS.later.showTW = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MNU_CONSTRUCTION: {
|
|
||||||
SS.UndoRemember();
|
|
||||||
SS.GW.GroupSelection();
|
|
||||||
if(SS.GW.gs.entities == 0) {
|
|
||||||
Error("No entities are selected. Select entities before "
|
|
||||||
"trying to toggle their construction state.");
|
|
||||||
}
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < SS.GW.gs.entities; i++) {
|
|
||||||
hEntity he = SS.GW.gs.entity[i];
|
|
||||||
if(!he.isFromRequest()) continue;
|
|
||||||
Request *r = SK.GetRequest(he.request());
|
|
||||||
r->construction = !(r->construction);
|
|
||||||
SS.MarkGroupDirty(r->group);
|
|
||||||
}
|
|
||||||
SS.GW.ClearSelection();
|
|
||||||
SS.GenerateAll();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MNU_SPLIT_CURVES:
|
|
||||||
SS.GW.SplitLinesOrCurves();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: oops();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::ClearSuper(void) {
|
|
||||||
HideGraphicsEditControl();
|
|
||||||
ClearPending();
|
|
||||||
ClearSelection();
|
|
||||||
hover.Clear();
|
|
||||||
EnsureValidActives();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsWindow::ToggleBool(bool *v) {
|
|
||||||
*v = !*v;
|
|
||||||
|
|
||||||
// The faces are shown as special stippling on the shaded triangle mesh,
|
|
||||||
// so not meaningful to show them and hide the shaded.
|
|
||||||
if(!showShaded) showFaces = false;
|
|
||||||
|
|
||||||
// We might need to regenerate the mesh and edge list, since the edges
|
|
||||||
// wouldn't have been generated if they were previously hidden.
|
|
||||||
if(showEdges) (SK.GetGroup(activeGroup))->displayDirty = true;
|
|
||||||
|
|
||||||
SS.GenerateAll();
|
|
||||||
InvalidateGraphics();
|
|
||||||
SS.later.showTW = true;
|
|
||||||
}
|
|
||||||
|
|
BIN
icons/angle.png
Before Width: | Height: | Size: 27 KiB |
BIN
icons/arc.png
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/bezier.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/circle.png
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/edges.png
Before Width: | Height: | Size: 27 KiB |
BIN
icons/equal.png
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/faces.png
Before Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/horiz.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/in3d.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/length.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/line.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/mesh.png
Before Width: | Height: | Size: 28 KiB |
BIN
icons/normal.png
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/point.png
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/ref.png
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
icons/shaded.png
Before Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 27 KiB |
BIN
icons/text.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/trim.png
Before Width: | Height: | Size: 26 KiB |
BIN
icons/vert.png
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 395 B |
|
@ -1,32 +1,42 @@
|
||||||
//-----------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------
|
||||||
// Data structures and prototypes for slvs.lib, a geometric constraint solver.
|
* Data structures and prototypes for slvs.lib, a geometric constraint solver.
|
||||||
//
|
*
|
||||||
// See the comments in this file, the accompanying sample code that uses
|
* See the comments in this file, the accompanying sample code that uses
|
||||||
// this library, and the accompanying documentation (DOC.txt).
|
* this library, and the accompanying documentation (DOC.txt).
|
||||||
//
|
*
|
||||||
// Copyright 2009-2013 Jonathan Westhues.
|
* Copyright 2009-2013 Jonathan Westhues.
|
||||||
//-----------------------------------------------------------------------------
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#ifndef __SLVS_H
|
#ifndef __SLVS_H
|
||||||
#define __SLVS_H
|
#define __SLVS_H
|
||||||
|
|
||||||
#ifdef EXPORT_DLL
|
#ifdef WIN32
|
||||||
#define DLL __declspec( dllexport )
|
# ifdef EXPORT_DLL
|
||||||
|
# define DLL __declspec( dllexport )
|
||||||
|
# else
|
||||||
|
# define DLL __declspec( dllimport )
|
||||||
|
# endif
|
||||||
#else
|
#else
|
||||||
#define DLL __declspec( dllimport )
|
# define DLL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef DWORD Slvs_hParam;
|
#ifdef _MSC_VER
|
||||||
typedef DWORD Slvs_hEntity;
|
typedef unsigned __int32 uint32_t;
|
||||||
typedef DWORD Slvs_hConstraint;
|
#else
|
||||||
typedef DWORD Slvs_hGroup;
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// To obtain the 3d (not projected into a workplane) of a constraint or
|
typedef uint32_t Slvs_hParam;
|
||||||
// an entity, specify this instead of the workplane.
|
typedef uint32_t Slvs_hEntity;
|
||||||
|
typedef uint32_t Slvs_hConstraint;
|
||||||
|
typedef uint32_t Slvs_hGroup;
|
||||||
|
|
||||||
|
/* To obtain the 3d (not projected into a workplane) of a constraint or
|
||||||
|
* an entity, specify this instead of the workplane. */
|
||||||
#define SLVS_FREE_IN_3D 0
|
#define SLVS_FREE_IN_3D 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,9 +55,9 @@ typedef struct {
|
||||||
|
|
||||||
#define SLVS_E_DISTANCE 70000
|
#define SLVS_E_DISTANCE 70000
|
||||||
|
|
||||||
// The special point, normal, and distance types used for parametric step
|
/* The special point, normal, and distance types used for parametric step
|
||||||
// and repeat, extrude, and assembly are currently not exposed. Please
|
* and repeat, extrude, and assembly are currently not exposed. Please
|
||||||
// contact us if you are interested in using these.
|
* contact us if you are interested in using these. */
|
||||||
|
|
||||||
#define SLVS_E_WORKPLANE 80000
|
#define SLVS_E_WORKPLANE 80000
|
||||||
#define SLVS_E_LINE_SEGMENT 80001
|
#define SLVS_E_LINE_SEGMENT 80001
|
||||||
|
@ -102,6 +112,7 @@ typedef struct {
|
||||||
#define SLVS_C_PROJ_PT_DISTANCE 100030
|
#define SLVS_C_PROJ_PT_DISTANCE 100030
|
||||||
#define SLVS_C_WHERE_DRAGGED 100031
|
#define SLVS_C_WHERE_DRAGGED 100031
|
||||||
#define SLVS_C_CURVE_CURVE_TANGENT 100032
|
#define SLVS_C_CURVE_CURVE_TANGENT 100032
|
||||||
|
#define SLVS_C_LENGTH_DIFFERENCE 100033
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Slvs_hConstraint h;
|
Slvs_hConstraint h;
|
||||||
|
@ -125,16 +136,15 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
//// INPUT VARIABLES
|
/*** INPUT VARIABLES
|
||||||
//
|
*
|
||||||
// Here, we specify the parameters and their initial values, the entities,
|
* Here, we specify the parameters and their initial values, the entities,
|
||||||
// and the constraints. For example, param[] points to the array of
|
* and the constraints. For example, param[] points to the array of
|
||||||
// parameters, which has length params, so that the last valid element
|
* parameters, which has length params, so that the last valid element
|
||||||
// is param[params-1].
|
* is param[params-1].
|
||||||
//
|
*
|
||||||
// param[] is actually an in/out variable; if the solver is successful,
|
* param[] is actually an in/out variable; if the solver is successful,
|
||||||
// then the new values (that satisfy the constraints) are written to it.
|
* then the new values (that satisfy the constraints) are written to it. */
|
||||||
//
|
|
||||||
Slvs_Param *param;
|
Slvs_Param *param;
|
||||||
int params;
|
int params;
|
||||||
Slvs_Entity *entity;
|
Slvs_Entity *entity;
|
||||||
|
@ -142,39 +152,39 @@ typedef struct {
|
||||||
Slvs_Constraint *constraint;
|
Slvs_Constraint *constraint;
|
||||||
int constraints;
|
int constraints;
|
||||||
|
|
||||||
// If a parameter corresponds to a point (distance, normal, etc.) being
|
/* If a parameter corresponds to a point (distance, normal, etc.) being
|
||||||
// dragged, then specify it here. This will cause the solver to favor
|
* dragged, then specify it here. This will cause the solver to favor
|
||||||
// that parameter, and attempt to change it as little as possible even
|
* that parameter, and attempt to change it as little as possible even
|
||||||
// if that requires it to change other parameters more.
|
* if that requires it to change other parameters more.
|
||||||
//
|
*
|
||||||
// Unused members of this array should be set to zero.
|
* Unused members of this array should be set to zero. */
|
||||||
Slvs_hParam dragged[4];
|
Slvs_hParam dragged[4];
|
||||||
|
|
||||||
// If the solver fails, then it can determine which constraints are
|
/* If the solver fails, then it can determine which constraints are
|
||||||
// causing the problem. But this is a relatively slow process (for
|
* causing the problem. But this is a relatively slow process (for
|
||||||
// a system with n constraints, about n times as long as just solving).
|
* a system with n constraints, about n times as long as just solving).
|
||||||
// If calculateFaileds is true, then the solver will do so, otherwise
|
* If calculateFaileds is true, then the solver will do so, otherwise
|
||||||
// not.
|
* not. */
|
||||||
int calculateFaileds;
|
int calculateFaileds;
|
||||||
|
|
||||||
//// OUTPUT VARIABLES
|
/*** OUTPUT VARIABLES
|
||||||
//
|
*
|
||||||
// If the solver fails, then it can report which constraints are causing
|
* If the solver fails, then it can report which constraints are causing
|
||||||
// the problem. The caller should allocate the array failed[], and pass
|
* the problem. The caller should allocate the array failed[], and pass
|
||||||
// its size in faileds.
|
* its size in faileds.
|
||||||
//
|
*
|
||||||
// The solver will set faileds equal to the number of problematic
|
* The solver will set faileds equal to the number of problematic
|
||||||
// constraints, and write their Slvs_hConstraints into failed[]. To
|
* constraints, and write their Slvs_hConstraints into failed[]. To
|
||||||
// ensure that there is sufficient space for any possible set of
|
* ensure that there is sufficient space for any possible set of
|
||||||
// failing constraints, faileds should be greater than or equal to
|
* failing constraints, faileds should be greater than or equal to
|
||||||
// constraints.
|
* constraints. */
|
||||||
Slvs_hConstraint *failed;
|
Slvs_hConstraint *failed;
|
||||||
int faileds;
|
int faileds;
|
||||||
|
|
||||||
// The solver indicates the number of unconstrained degrees of freedom.
|
/* The solver indicates the number of unconstrained degrees of freedom. */
|
||||||
int dof;
|
int dof;
|
||||||
|
|
||||||
// The solver indicates whether the solution succeeded.
|
/* The solver indicates whether the solution succeeded. */
|
||||||
#define SLVS_RESULT_OKAY 0
|
#define SLVS_RESULT_OKAY 0
|
||||||
#define SLVS_RESULT_INCONSISTENT 1
|
#define SLVS_RESULT_INCONSISTENT 1
|
||||||
#define SLVS_RESULT_DIDNT_CONVERGE 2
|
#define SLVS_RESULT_DIDNT_CONVERGE 2
|
||||||
|
@ -185,12 +195,12 @@ typedef struct {
|
||||||
DLL void Slvs_Solve(Slvs_System *sys, Slvs_hGroup hg);
|
DLL void Slvs_Solve(Slvs_System *sys, Slvs_hGroup hg);
|
||||||
|
|
||||||
|
|
||||||
// Our base coordinate system has basis vectors
|
/* Our base coordinate system has basis vectors
|
||||||
// (1, 0, 0) (0, 1, 0) (0, 0, 1)
|
* (1, 0, 0) (0, 1, 0) (0, 0, 1)
|
||||||
// A unit quaternion defines a rotation to a new coordinate system with
|
* A unit quaternion defines a rotation to a new coordinate system with
|
||||||
// basis vectors
|
* basis vectors
|
||||||
// U V N
|
* U V N
|
||||||
// which these functions compute from the quaternion.
|
* which these functions compute from the quaternion. */
|
||||||
DLL void Slvs_QuaternionU(double qw, double qx, double qy, double qz,
|
DLL void Slvs_QuaternionU(double qw, double qx, double qy, double qz,
|
||||||
double *x, double *y, double *z);
|
double *x, double *y, double *z);
|
||||||
DLL void Slvs_QuaternionV(double qw, double qx, double qy, double qz,
|
DLL void Slvs_QuaternionV(double qw, double qx, double qy, double qz,
|
||||||
|
@ -198,18 +208,18 @@ DLL void Slvs_QuaternionV(double qw, double qx, double qy, double qz,
|
||||||
DLL void Slvs_QuaternionN(double qw, double qx, double qy, double qz,
|
DLL void Slvs_QuaternionN(double qw, double qx, double qy, double qz,
|
||||||
double *x, double *y, double *z);
|
double *x, double *y, double *z);
|
||||||
|
|
||||||
// Similarly, compute a unit quaternion in terms of two basis vectors.
|
/* Similarly, compute a unit quaternion in terms of two basis vectors. */
|
||||||
DLL void Slvs_MakeQuaternion(double ux, double uy, double uz,
|
DLL void Slvs_MakeQuaternion(double ux, double uy, double uz,
|
||||||
double vx, double vy, double vz,
|
double vx, double vy, double vz,
|
||||||
double *qw, double *qx, double *qy, double *qz);
|
double *qw, double *qx, double *qy, double *qz);
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------
|
/*-------------------------------------
|
||||||
// These are just convenience functions, to save you the trouble of filling
|
* These are just convenience functions, to save you the trouble of filling
|
||||||
// out the structures by hand. The code is included in the header file to
|
* out the structures by hand. The code is included in the header file to
|
||||||
// let the compiler inline them if possible.
|
* let the compiler inline them if possible. */
|
||||||
|
|
||||||
static Slvs_Param Slvs_MakeParam(Slvs_hParam h, Slvs_hGroup group, double val)
|
static inline Slvs_Param Slvs_MakeParam(Slvs_hParam h, Slvs_hGroup group, double val)
|
||||||
{
|
{
|
||||||
Slvs_Param r;
|
Slvs_Param r;
|
||||||
r.h = h;
|
r.h = h;
|
||||||
|
@ -217,7 +227,7 @@ static Slvs_Param Slvs_MakeParam(Slvs_hParam h, Slvs_hGroup group, double val)
|
||||||
r.val = val;
|
r.val = val;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakePoint2d(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakePoint2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl,
|
Slvs_hEntity wrkpl,
|
||||||
Slvs_hParam u, Slvs_hParam v)
|
Slvs_hParam u, Slvs_hParam v)
|
||||||
{
|
{
|
||||||
|
@ -231,7 +241,7 @@ static Slvs_Entity Slvs_MakePoint2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.param[1] = v;
|
r.param[1] = v;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakePoint3d(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakePoint3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hParam x, Slvs_hParam y, Slvs_hParam z)
|
Slvs_hParam x, Slvs_hParam y, Slvs_hParam z)
|
||||||
{
|
{
|
||||||
Slvs_Entity r;
|
Slvs_Entity r;
|
||||||
|
@ -245,8 +255,9 @@ static Slvs_Entity Slvs_MakePoint3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.param[2] = z;
|
r.param[2] = z;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeNormal3d(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeNormal3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hParam qw, Slvs_hParam qx, Slvs_hParam qy, Slvs_hParam qz)
|
Slvs_hParam qw, Slvs_hParam qx,
|
||||||
|
Slvs_hParam qy, Slvs_hParam qz)
|
||||||
{
|
{
|
||||||
Slvs_Entity r;
|
Slvs_Entity r;
|
||||||
memset(&r, 0, sizeof(r));
|
memset(&r, 0, sizeof(r));
|
||||||
|
@ -260,7 +271,7 @@ static Slvs_Entity Slvs_MakeNormal3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.param[3] = qz;
|
r.param[3] = qz;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeNormal2d(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeNormal2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl)
|
Slvs_hEntity wrkpl)
|
||||||
{
|
{
|
||||||
Slvs_Entity r;
|
Slvs_Entity r;
|
||||||
|
@ -271,7 +282,7 @@ static Slvs_Entity Slvs_MakeNormal2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.wrkpl = wrkpl;
|
r.wrkpl = wrkpl;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeDistance(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeDistance(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl, Slvs_hParam d)
|
Slvs_hEntity wrkpl, Slvs_hParam d)
|
||||||
{
|
{
|
||||||
Slvs_Entity r;
|
Slvs_Entity r;
|
||||||
|
@ -283,7 +294,7 @@ static Slvs_Entity Slvs_MakeDistance(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.param[0] = d;
|
r.param[0] = d;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeLineSegment(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeLineSegment(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl,
|
Slvs_hEntity wrkpl,
|
||||||
Slvs_hEntity ptA, Slvs_hEntity ptB)
|
Slvs_hEntity ptA, Slvs_hEntity ptB)
|
||||||
{
|
{
|
||||||
|
@ -297,7 +308,7 @@ static Slvs_Entity Slvs_MakeLineSegment(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.point[1] = ptB;
|
r.point[1] = ptB;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeCubic(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeCubic(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl,
|
Slvs_hEntity wrkpl,
|
||||||
Slvs_hEntity pt0, Slvs_hEntity pt1,
|
Slvs_hEntity pt0, Slvs_hEntity pt1,
|
||||||
Slvs_hEntity pt2, Slvs_hEntity pt3)
|
Slvs_hEntity pt2, Slvs_hEntity pt3)
|
||||||
|
@ -314,7 +325,7 @@ static Slvs_Entity Slvs_MakeCubic(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.point[3] = pt3;
|
r.point[3] = pt3;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeArcOfCircle(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeArcOfCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl,
|
Slvs_hEntity wrkpl,
|
||||||
Slvs_hEntity normal,
|
Slvs_hEntity normal,
|
||||||
Slvs_hEntity center,
|
Slvs_hEntity center,
|
||||||
|
@ -332,7 +343,7 @@ static Slvs_Entity Slvs_MakeArcOfCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.point[2] = end;
|
r.point[2] = end;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeCircle(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity wrkpl,
|
Slvs_hEntity wrkpl,
|
||||||
Slvs_hEntity center,
|
Slvs_hEntity center,
|
||||||
Slvs_hEntity normal, Slvs_hEntity radius)
|
Slvs_hEntity normal, Slvs_hEntity radius)
|
||||||
|
@ -348,7 +359,7 @@ static Slvs_Entity Slvs_MakeCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
r.distance = radius;
|
r.distance = radius;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
static Slvs_Entity Slvs_MakeWorkplane(Slvs_hEntity h, Slvs_hGroup group,
|
static inline Slvs_Entity Slvs_MakeWorkplane(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
Slvs_hEntity origin, Slvs_hEntity normal)
|
Slvs_hEntity origin, Slvs_hEntity normal)
|
||||||
{
|
{
|
||||||
Slvs_Entity r;
|
Slvs_Entity r;
|
||||||
|
@ -362,7 +373,7 @@ static Slvs_Entity Slvs_MakeWorkplane(Slvs_hEntity h, Slvs_hGroup group,
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Slvs_Constraint Slvs_MakeConstraint(Slvs_hConstraint h,
|
static inline Slvs_Constraint Slvs_MakeConstraint(Slvs_hConstraint h,
|
||||||
Slvs_hGroup group,
|
Slvs_hGroup group,
|
||||||
int type,
|
int type,
|
||||||
Slvs_hEntity wrkpl,
|
Slvs_hEntity wrkpl,
|