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);
|
||||
}
|
||||
|
137
exposed/CDemo.c
|
@ -1,18 +1,23 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Some sample code for slvs.dll. We draw some geometric entities, provide
|
||||
// initial guesses for their positions, and then constrain them. The solver
|
||||
// calculates their new positions, in order to satisfy the constraints.
|
||||
//
|
||||
// Copyright 2008-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <windows.h>
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Some sample code for slvs.dll. We draw some geometric entities, provide
|
||||
* initial guesses for their positions, and then constrain them. The solver
|
||||
* calculates their new positions, in order to satisfy the constraints.
|
||||
*
|
||||
* Copyright 2008-2013 Jonathan Westhues.
|
||||
*---------------------------------------------------------------------------*/
|
||||
#ifdef WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
#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);
|
||||
if(!r) {
|
||||
|
@ -22,30 +27,30 @@ void *CheckMalloc(size_t n)
|
|||
return r;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// An example of a constraint in 3d. We create a single group, with some
|
||||
// entities and constraints.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Example3d(void)
|
||||
/*-----------------------------------------------------------------------------
|
||||
* An example of a constraint in 3d. We create a single group, with some
|
||||
* entities and constraints.
|
||||
*---------------------------------------------------------------------------*/
|
||||
void Example3d()
|
||||
{
|
||||
// This will contain a single group, which will arbitrarily number 1.
|
||||
int g = 1;
|
||||
/* This will contain a single group, which will arbitrarily number 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(2, g, 10.0);
|
||||
sys.param[sys.params++] = Slvs_MakeParam(3, g, 10.0);
|
||||
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(5, g, 20.0);
|
||||
sys.param[sys.params++] = Slvs_MakeParam(6, g, 20.0);
|
||||
sys.entity[sys.entities++] = Slvs_MakePoint3d(102, g, 4, 5, 6);
|
||||
// and a line segment connecting them.
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
||||
/* and a line segment connecting them. */
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(200, g,
|
||||
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(
|
||||
1, g,
|
||||
SLVS_C_PT_PT_DISTANCE,
|
||||
|
@ -53,13 +58,13 @@ void Example3d(void)
|
|||
30.0,
|
||||
101, 102, 0, 0);
|
||||
|
||||
// Let's tell the solver to keep the second point as close to constant
|
||||
// as possible, instead moving the first point.
|
||||
/* Let's tell the solver to keep the second point as close to constant
|
||||
* as possible, instead moving the first point. */
|
||||
sys.dragged[0] = 4;
|
||||
sys.dragged[1] = 5;
|
||||
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);
|
||||
|
||||
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
|
||||
// along the reference frame's xy plane. In a second group, we create some
|
||||
// entities in that group and dimension them.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Example2d(void)
|
||||
/*-----------------------------------------------------------------------------
|
||||
* 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
|
||||
* entities in that group and dimension them.
|
||||
*---------------------------------------------------------------------------*/
|
||||
void Example2d()
|
||||
{
|
||||
int g;
|
||||
Slvs_hGroup g;
|
||||
double qw, qx, qy, qz;
|
||||
|
||||
g = 1;
|
||||
// First, we create our workplane. Its origin corresponds to the origin
|
||||
// of our base frame (x y z) = (0 0 0)
|
||||
/* First, we create our workplane. Its origin corresponds to the origin
|
||||
* 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(2, g, 0.0);
|
||||
sys.param[sys.params++] = Slvs_MakeParam(3, g, 0.0);
|
||||
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 (0 1 0).
|
||||
/* and it is parallel to the xy plane, so it has basis vectors (1 0 0)
|
||||
* and (0 1 0). */
|
||||
Slvs_MakeQuaternion(1, 0, 0,
|
||||
0, 1, 0, &qw, &qx, &qy, &qz);
|
||||
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);
|
||||
|
||||
// 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,
|
||||
// and the solver can't move it.
|
||||
/* 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,
|
||||
* and the solver can't move it. */
|
||||
g = 2;
|
||||
// These points are represented by their coordinates (u v) within the
|
||||
// workplane, so they need only two parameters each.
|
||||
/* These points are represented by their coordinates (u v) within the
|
||||
* workplane, so they need only two parameters each. */
|
||||
sys.param[sys.params++] = Slvs_MakeParam(11, g, 10.0);
|
||||
sys.param[sys.params++] = Slvs_MakeParam(12, g, 20.0);
|
||||
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.entity[sys.entities++] = Slvs_MakePoint2d(302, g, 200, 13, 14);
|
||||
|
||||
// And we create a line segment with those endpoints.
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
||||
/* And we create a line segment with those endpoints. */
|
||||
sys.entity[sys.entities++] = Slvs_MakeLineSegment(400, g,
|
||||
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(16, g, 120.0);
|
||||
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.entity[sys.entities++] = Slvs_MakePoint2d(305, g, 200, 19, 20);
|
||||
|
||||
// And arc, centered at point 303, starting at point 304, ending at
|
||||
// point 305.
|
||||
/* And arc, centered at point 303, starting at point 304, ending at
|
||||
* point 305. */
|
||||
sys.entity[sys.entities++] = Slvs_MakeArcOfCircle(401, g, 200, 102,
|
||||
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(22, g, 200.0);
|
||||
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.entity[sys.entities++] = Slvs_MakeDistance(307, g, 200, 23);
|
||||
|
||||
// And a complete circle, centered at point 306 with radius equal to
|
||||
// distance 307. The normal is 102, the same as our workplane.
|
||||
/* And a complete circle, centered at point 306 with radius equal to
|
||||
* distance 307. The normal is 102, the same as our workplane. */
|
||||
sys.entity[sys.entities++] = Slvs_MakeCircle(402, g, 200,
|
||||
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(
|
||||
1, g,
|
||||
SLVS_C_PT_PT_DISTANCE,
|
||||
|
@ -160,45 +165,46 @@ void Example2d(void)
|
|||
30.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(
|
||||
2, g,
|
||||
SLVS_C_PT_LINE_DISTANCE,
|
||||
200,
|
||||
10.0,
|
||||
101, 0, 400, 0);
|
||||
// And the line segment is vertical.
|
||||
/* And the line segment is vertical. */
|
||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||
3, g,
|
||||
SLVS_C_VERTICAL,
|
||||
200,
|
||||
0.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(
|
||||
4, g,
|
||||
SLVS_C_PT_PT_DISTANCE,
|
||||
200,
|
||||
15.0,
|
||||
301, 101, 0, 0);
|
||||
/*
|
||||
// And same for the other endpoint; so if you add this constraint then
|
||||
// the sketch is overconstrained and will signal an error.
|
||||
#if 0
|
||||
/* And same for the other endpoint; so if you add this constraint then
|
||||
* the sketch is overconstrained and will signal an error. */
|
||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||
5, g,
|
||||
SLVS_C_PT_PT_DISTANCE,
|
||||
200,
|
||||
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(
|
||||
6, g,
|
||||
SLVS_C_EQUAL_RADIUS,
|
||||
200,
|
||||
0.0,
|
||||
0, 0, 401, 402);
|
||||
// The arc has radius 17.0 units.
|
||||
/* The arc has radius 17.0 units. */
|
||||
sys.constraint[sys.constraints++] = Slvs_MakeConstraint(
|
||||
7, g,
|
||||
SLVS_C_DIAMETER,
|
||||
|
@ -206,11 +212,11 @@ void Example2d(void)
|
|||
17.0*2,
|
||||
0, 0, 401, 0);
|
||||
|
||||
// If the solver fails, then ask it to report which constraints caused
|
||||
// the problem.
|
||||
/* If the solver fails, then ask it to report which constraints caused
|
||||
* the problem. */
|
||||
sys.calculateFaileds = 1;
|
||||
|
||||
// And solve.
|
||||
/* And solve. */
|
||||
Slvs_Solve(&sys, g);
|
||||
|
||||
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.entity = CheckMalloc(50*sizeof(sys.entity[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.faileds = 50;
|
||||
|
||||
// Example3d();
|
||||
/*Example3d();*/
|
||||
for(;;) {
|
||||
Example2d();
|
||||
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
|
||||
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*
|
||||
|
||||
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_WHERE_DRAGGED As Integer = 100031
|
||||
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
|
||||
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.
|
||||
//
|
||||
// See the comments in this file, the accompanying sample code that uses
|
||||
// this library, and the accompanying documentation (DOC.txt).
|
||||
//
|
||||
// Copyright 2009-2013 Jonathan Westhues.
|
||||
//-----------------------------------------------------------------------------
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Data structures and prototypes for slvs.lib, a geometric constraint solver.
|
||||
*
|
||||
* See the comments in this file, the accompanying sample code that uses
|
||||
* this library, and the accompanying documentation (DOC.txt).
|
||||
*
|
||||
* Copyright 2009-2013 Jonathan Westhues.
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef __SLVS_H
|
||||
#define __SLVS_H
|
||||
|
||||
#ifdef EXPORT_DLL
|
||||
#define DLL __declspec( dllexport )
|
||||
#ifdef WIN32
|
||||
# ifdef EXPORT_DLL
|
||||
# define DLL __declspec( dllexport )
|
||||
# else
|
||||
# define DLL __declspec( dllimport )
|
||||
# endif
|
||||
#else
|
||||
#define DLL __declspec( dllimport )
|
||||
# define DLL
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef DWORD Slvs_hParam;
|
||||
typedef DWORD Slvs_hEntity;
|
||||
typedef DWORD Slvs_hConstraint;
|
||||
typedef DWORD Slvs_hGroup;
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
// To obtain the 3d (not projected into a workplane) of a constraint or
|
||||
// an entity, specify this instead of the workplane.
|
||||
typedef uint32_t Slvs_hParam;
|
||||
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
|
||||
|
||||
|
||||
|
@ -45,9 +55,9 @@ typedef struct {
|
|||
|
||||
#define SLVS_E_DISTANCE 70000
|
||||
|
||||
// The special point, normal, and distance types used for parametric step
|
||||
// and repeat, extrude, and assembly are currently not exposed. Please
|
||||
// contact us if you are interested in using these.
|
||||
/* The special point, normal, and distance types used for parametric step
|
||||
* and repeat, extrude, and assembly are currently not exposed. Please
|
||||
* contact us if you are interested in using these. */
|
||||
|
||||
#define SLVS_E_WORKPLANE 80000
|
||||
#define SLVS_E_LINE_SEGMENT 80001
|
||||
|
@ -102,6 +112,7 @@ typedef struct {
|
|||
#define SLVS_C_PROJ_PT_DISTANCE 100030
|
||||
#define SLVS_C_WHERE_DRAGGED 100031
|
||||
#define SLVS_C_CURVE_CURVE_TANGENT 100032
|
||||
#define SLVS_C_LENGTH_DIFFERENCE 100033
|
||||
|
||||
typedef struct {
|
||||
Slvs_hConstraint h;
|
||||
|
@ -118,23 +129,22 @@ typedef struct {
|
|||
Slvs_hEntity entityB;
|
||||
Slvs_hEntity entityC;
|
||||
Slvs_hEntity entityD;
|
||||
|
||||
|
||||
int other;
|
||||
int other2;
|
||||
} Slvs_Constraint;
|
||||
|
||||
|
||||
typedef struct {
|
||||
//// INPUT VARIABLES
|
||||
//
|
||||
// Here, we specify the parameters and their initial values, the entities,
|
||||
// and the constraints. For example, param[] points to the array of
|
||||
// parameters, which has length params, so that the last valid element
|
||||
// is param[params-1].
|
||||
//
|
||||
// param[] is actually an in/out variable; if the solver is successful,
|
||||
// then the new values (that satisfy the constraints) are written to it.
|
||||
//
|
||||
/*** INPUT VARIABLES
|
||||
*
|
||||
* Here, we specify the parameters and their initial values, the entities,
|
||||
* and the constraints. For example, param[] points to the array of
|
||||
* parameters, which has length params, so that the last valid element
|
||||
* is param[params-1].
|
||||
*
|
||||
* param[] is actually an in/out variable; if the solver is successful,
|
||||
* then the new values (that satisfy the constraints) are written to it. */
|
||||
Slvs_Param *param;
|
||||
int params;
|
||||
Slvs_Entity *entity;
|
||||
|
@ -142,39 +152,39 @@ typedef struct {
|
|||
Slvs_Constraint *constraint;
|
||||
int constraints;
|
||||
|
||||
// If a parameter corresponds to a point (distance, normal, etc.) being
|
||||
// dragged, then specify it here. This will cause the solver to favor
|
||||
// that parameter, and attempt to change it as little as possible even
|
||||
// if that requires it to change other parameters more.
|
||||
//
|
||||
// Unused members of this array should be set to zero.
|
||||
/* If a parameter corresponds to a point (distance, normal, etc.) being
|
||||
* dragged, then specify it here. This will cause the solver to favor
|
||||
* that parameter, and attempt to change it as little as possible even
|
||||
* if that requires it to change other parameters more.
|
||||
*
|
||||
* Unused members of this array should be set to zero. */
|
||||
Slvs_hParam dragged[4];
|
||||
|
||||
// If the solver fails, then it can determine which constraints are
|
||||
// causing the problem. But this is a relatively slow process (for
|
||||
// a system with n constraints, about n times as long as just solving).
|
||||
// If calculateFaileds is true, then the solver will do so, otherwise
|
||||
// not.
|
||||
/* If the solver fails, then it can determine which constraints are
|
||||
* causing the problem. But this is a relatively slow process (for
|
||||
* a system with n constraints, about n times as long as just solving).
|
||||
* If calculateFaileds is true, then the solver will do so, otherwise
|
||||
* not. */
|
||||
int calculateFaileds;
|
||||
|
||||
//// OUTPUT VARIABLES
|
||||
//
|
||||
// If the solver fails, then it can report which constraints are causing
|
||||
// the problem. The caller should allocate the array failed[], and pass
|
||||
// its size in faileds.
|
||||
//
|
||||
// The solver will set faileds equal to the number of problematic
|
||||
// constraints, and write their Slvs_hConstraints into failed[]. To
|
||||
// ensure that there is sufficient space for any possible set of
|
||||
// failing constraints, faileds should be greater than or equal to
|
||||
// constraints.
|
||||
/*** OUTPUT VARIABLES
|
||||
*
|
||||
* If the solver fails, then it can report which constraints are causing
|
||||
* the problem. The caller should allocate the array failed[], and pass
|
||||
* its size in faileds.
|
||||
*
|
||||
* The solver will set faileds equal to the number of problematic
|
||||
* constraints, and write their Slvs_hConstraints into failed[]. To
|
||||
* ensure that there is sufficient space for any possible set of
|
||||
* failing constraints, faileds should be greater than or equal to
|
||||
* constraints. */
|
||||
Slvs_hConstraint *failed;
|
||||
int faileds;
|
||||
|
||||
// The solver indicates the number of unconstrained degrees of freedom.
|
||||
/* The solver indicates the number of unconstrained degrees of freedom. */
|
||||
int dof;
|
||||
|
||||
// The solver indicates whether the solution succeeded.
|
||||
/* The solver indicates whether the solution succeeded. */
|
||||
#define SLVS_RESULT_OKAY 0
|
||||
#define SLVS_RESULT_INCONSISTENT 1
|
||||
#define SLVS_RESULT_DIDNT_CONVERGE 2
|
||||
|
@ -185,12 +195,12 @@ typedef struct {
|
|||
DLL void Slvs_Solve(Slvs_System *sys, Slvs_hGroup hg);
|
||||
|
||||
|
||||
// Our base coordinate system has basis vectors
|
||||
// (1, 0, 0) (0, 1, 0) (0, 0, 1)
|
||||
// A unit quaternion defines a rotation to a new coordinate system with
|
||||
// basis vectors
|
||||
// U V N
|
||||
// which these functions compute from the quaternion.
|
||||
/* Our base coordinate system has basis vectors
|
||||
* (1, 0, 0) (0, 1, 0) (0, 0, 1)
|
||||
* A unit quaternion defines a rotation to a new coordinate system with
|
||||
* basis vectors
|
||||
* U V N
|
||||
* which these functions compute from the quaternion. */
|
||||
DLL void Slvs_QuaternionU(double qw, double qx, double qy, double qz,
|
||||
double *x, double *y, double *z);
|
||||
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,
|
||||
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,
|
||||
double vx, double vy, double vz,
|
||||
double *qw, double *qx, double *qy, double *qz);
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
// 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
|
||||
// let the compiler inline them if possible.
|
||||
/*-------------------------------------
|
||||
* 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
|
||||
* 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;
|
||||
r.h = h;
|
||||
|
@ -217,9 +227,9 @@ static Slvs_Param Slvs_MakeParam(Slvs_hParam h, Slvs_hGroup group, double val)
|
|||
r.val = val;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakePoint2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hParam u, Slvs_hParam v)
|
||||
static inline Slvs_Entity Slvs_MakePoint2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hParam u, Slvs_hParam v)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -231,8 +241,8 @@ static Slvs_Entity Slvs_MakePoint2d(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.param[1] = v;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakePoint3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hParam x, Slvs_hParam y, Slvs_hParam z)
|
||||
static inline Slvs_Entity Slvs_MakePoint3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hParam x, Slvs_hParam y, Slvs_hParam z)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -245,8 +255,9 @@ static Slvs_Entity Slvs_MakePoint3d(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.param[2] = z;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeNormal3d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hParam qw, Slvs_hParam qx, Slvs_hParam qy, Slvs_hParam qz)
|
||||
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_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -260,8 +271,8 @@ static Slvs_Entity Slvs_MakeNormal3d(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.param[3] = qz;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeNormal2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl)
|
||||
static inline Slvs_Entity Slvs_MakeNormal2d(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -271,8 +282,8 @@ static Slvs_Entity Slvs_MakeNormal2d(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.wrkpl = wrkpl;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeDistance(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl, Slvs_hParam d)
|
||||
static inline Slvs_Entity Slvs_MakeDistance(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl, Slvs_hParam d)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -283,9 +294,9 @@ static Slvs_Entity Slvs_MakeDistance(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.param[0] = d;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeLineSegment(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity ptA, Slvs_hEntity ptB)
|
||||
static inline Slvs_Entity Slvs_MakeLineSegment(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity ptA, Slvs_hEntity ptB)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -297,10 +308,10 @@ static Slvs_Entity Slvs_MakeLineSegment(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.point[1] = ptB;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeCubic(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity pt0, Slvs_hEntity pt1,
|
||||
Slvs_hEntity pt2, Slvs_hEntity pt3)
|
||||
static inline Slvs_Entity Slvs_MakeCubic(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity pt0, Slvs_hEntity pt1,
|
||||
Slvs_hEntity pt2, Slvs_hEntity pt3)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -314,11 +325,11 @@ static Slvs_Entity Slvs_MakeCubic(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.point[3] = pt3;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeArcOfCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity normal,
|
||||
Slvs_hEntity center,
|
||||
Slvs_hEntity start, Slvs_hEntity end)
|
||||
static inline Slvs_Entity Slvs_MakeArcOfCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity normal,
|
||||
Slvs_hEntity center,
|
||||
Slvs_hEntity start, Slvs_hEntity end)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -332,10 +343,10 @@ static Slvs_Entity Slvs_MakeArcOfCircle(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.point[2] = end;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity center,
|
||||
Slvs_hEntity normal, Slvs_hEntity radius)
|
||||
static inline Slvs_Entity Slvs_MakeCircle(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity wrkpl,
|
||||
Slvs_hEntity center,
|
||||
Slvs_hEntity normal, Slvs_hEntity radius)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -348,8 +359,8 @@ static Slvs_Entity Slvs_MakeCircle(Slvs_hEntity h, Slvs_hGroup group,
|
|||
r.distance = radius;
|
||||
return r;
|
||||
}
|
||||
static Slvs_Entity Slvs_MakeWorkplane(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity origin, Slvs_hEntity normal)
|
||||
static inline Slvs_Entity Slvs_MakeWorkplane(Slvs_hEntity h, Slvs_hGroup group,
|
||||
Slvs_hEntity origin, Slvs_hEntity normal)
|
||||
{
|
||||
Slvs_Entity r;
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
@ -362,15 +373,15 @@ static Slvs_Entity Slvs_MakeWorkplane(Slvs_hEntity h, Slvs_hGroup group,
|
|||
return r;
|
||||
}
|
||||
|
||||
static Slvs_Constraint Slvs_MakeConstraint(Slvs_hConstraint h,
|
||||
Slvs_hGroup group,
|
||||
int type,
|
||||
Slvs_hEntity wrkpl,
|
||||
double valA,
|
||||
Slvs_hEntity ptA,
|
||||
Slvs_hEntity ptB,
|
||||
Slvs_hEntity entityA,
|
||||
Slvs_hEntity entityB)
|
||||
static inline Slvs_Constraint Slvs_MakeConstraint(Slvs_hConstraint h,
|
||||
Slvs_hGroup group,
|
||||
int type,
|
||||
Slvs_hEntity wrkpl,
|
||||
double valA,
|
||||
Slvs_hEntity ptA,
|
||||
Slvs_hEntity ptB,
|
||||
Slvs_hEntity entityA,
|
||||
Slvs_hEntity entityB)
|
||||
{
|
||||
Slvs_Constraint r;
|
||||
memset(&r, 0, sizeof(r));
|