Compare commits

...

624 Commits

Author SHA1 Message Date
Rick Companje
41be8dcfd1 Update package.json 2021-05-27 00:30:18 +02:00
casper
a54a716d6d update css 2021-05-24 17:05:55 +02:00
casper
c472739baa update refs 2021-05-22 17:37:41 +02:00
casper
8e01a5c2e2 reify code formatting fix 2021-05-22 15:23:52 +02:00
casper
06efd35119 Fix install
need to install with `npm i --force` now
2021-05-16 13:22:05 +02:00
Casper Lamboo
3b440b346a change font bold 2019-02-21 20:54:59 +01:00
Casper Lamboo
8015cd780d disable selecting titles 2019-02-21 20:54:48 +01:00
Casper Lamboo
01f40eb5f8 Display settings inside accordion element 2019-02-21 20:39:05 +01:00
Rick Companje
49b4a2be59 separate Download and Print buttons 2019-02-21 20:13:31 +01:00
Rick Companje
7df13f6db0 remove Doodle3D Printer, move Duplicator i3 Mini 2019-02-21 20:11:54 +01:00
Rick Companje
766cadcd44 fix point sorting 2018-11-27 15:47:34 +01:00
Rick Companje
cbc439b4dc Update printer.yml 2018-11-27 14:14:39 +01:00
Rick Companje
7da0b2fa17 Merge branch 'master' of https://github.com/Doodle3D/Doodle3D-Slicer 2018-11-27 14:13:25 +01:00
Rick Companje
5cb91e6c5a Revert "printer duplicator i3 mini"
This reverts commit 92593cef14d97461d507b57321a62816736004a9.
2018-11-27 14:13:08 +01:00
Rick Companje
92593cef14 printer duplicator i3 mini 2018-11-27 14:12:29 +01:00
Casper Lamboo
ebbc985d67 remove react on touch tap 2018-06-26 16:08:33 +02:00
Casper Lamboo
246f1627c5 Merge branch 'develop' 2018-05-29 11:36:43 +02:00
Casper Lamboo
6971c3c4b5 Merge branch 'feature/comb' into develop 2018-05-28 15:17:33 +02:00
Casper Lamboo
fc3dc7355c reformat 2018-05-28 13:53:53 +02:00
Casper Lamboo
3944202a83 add tau constant 2018-05-28 13:53:32 +02:00
Casper Lamboo
fd6e5cebbd remove normals 2018-05-28 13:53:09 +02:00
Casper Lamboo
ead6be081f Merge branch 'develop' into feature/comb 2018-05-28 11:57:24 +02:00
Casper Lamboo
d63754dbd0 fix slicing single walled paths 2018-05-28 11:57:16 +02:00
Casper Lamboo
2044929808 turn of combing by default 2018-05-24 16:27:22 +02:00
Casper Lamboo
7dceeda291 update combing 2018-05-24 16:14:03 +02:00
Casper Lamboo
86eed64255 remove eruct 2018-05-24 16:13:56 +02:00
Casper Lamboo
caf36a5505 remove unused imports 2018-05-24 16:13:44 +02:00
Casper Lamboo
1493ae3536 add extra option within containLineInPath 2018-05-05 10:19:30 +02:00
Casper Lamboo
5900bbcc50 remove unused imports 2018-05-05 10:18:20 +02:00
Casper Lamboo
2c953496f7 share functions in comb.js 2018-05-02 17:42:31 +02:00
Casper Lamboo
c642375295 update containLineInPath func 2018-05-02 17:39:27 +02:00
Casper Lamboo
942addf8d7 Merge branch 'develop' into feature/comb 2018-05-02 17:18:54 +02:00
Casper Lamboo
b7269da172 update example 2018-05-02 17:18:48 +02:00
Casper Lamboo
50ff72a037 change distance check to 3 2018-05-02 16:54:19 +02:00
Casper Lamboo
60fb966ccb add hash to scripts 2018-05-02 16:34:35 +02:00
Casper Lamboo
0f70855989 update example 2018-05-02 16:29:44 +02:00
Casper Lamboo
e966bc89b2 performance 2018-05-02 16:27:54 +02:00
Casper Lamboo
6c8b8e9d44 implement new combing 2018-05-02 15:12:45 +02:00
Casper Lamboo
79e4acd3d1 add demo 2018-05-02 15:07:03 +02:00
Casper Lamboo
59681e8023 reverse order of commands 2018-04-23 14:22:40 +02:00
Casper Lamboo
f109147e62 only request doodle printers when adding or managing printers 2018-04-23 12:27:16 +02:00
Casper Lamboo
0119c91001 make case sensitive 2018-04-23 11:57:48 +02:00
Casper Lamboo
8467da3894 Merge branch 'master' into develop 2018-04-23 11:56:52 +02:00
Casper Lamboo
808d585f2a remove whole line for heated bed instead of making comment 2018-04-23 11:56:41 +02:00
Rick Companje
0804dd5282
fix {if heatedBed} 2018-04-23 11:46:26 +02:00
Casper Lamboo
0957d53b41 fix font loading
@companje
2018-04-23 10:55:11 +02:00
Casper Lamboo
0be1ee6d51 comply with linter 2018-04-17 14:17:28 +02:00
Casper Lamboo
5a40c7c647 update web pack config 2018-04-17 11:44:51 +02:00
Casper Lamboo
141c38d878 setting up linter 2018-04-17 11:29:17 +02:00
Casper Lamboo
97cb6e062d fix percentage of malyan printer 2018-04-16 21:03:15 +02:00
Casper Lamboo
264ea9ff00 make filename configurable through query 2018-04-16 16:26:44 +02:00
Casper Lamboo
374fc4a32e remove old path when joining two paths
#42
2018-04-12 11:06:47 +02:00
Casper Lamboo
396502948b Fix bug where some shapes are skipped 2018-04-03 21:33:34 +02:00
Casper Lamboo
a0b9cd1306 don't trim 2018-04-03 16:07:49 +02:00
Casper Lamboo
75c3e9d632 Merge branch 'master' into develop 2018-04-03 15:22:10 +02:00
Casper Lamboo
06ce0b0d6b bump version 2018-04-03 14:02:58 +02:00
Casper Lamboo
74ed9e58e4 Merge branch 'develop' 2018-04-03 14:02:36 +02:00
Casper Lamboo
6be1291923 cleanup 2018-04-03 11:38:49 +02:00
Casper Lamboo
701d736cc0 store gcode as blob instead of string
Less prone to crashing
2018-04-03 11:38:40 +02:00
Casper Lamboo
61722a6985 change order of multiplying matrix 2018-03-29 15:37:32 +02:00
Rick Companje
ef7ceb7849
Update printer.yml 2018-03-28 09:15:42 +02:00
Rick Companje
5302857b5e
Update printer.yml 2018-03-28 09:14:57 +02:00
Rick Companje
5a51ddf0f6
Update default.yml 2018-03-28 09:11:04 +02:00
Rick Companje
14c33c0225
Update infill.yml 2018-03-28 09:02:56 +02:00
Casper Lamboo
5a6468baef fix orientation of printed doodles
#43
2018-03-26 10:28:46 +02:00
Rick Companje
763c6e08e8
Update printer.yml 2018-03-19 16:50:01 +01:00
Casper Lamboo
5b2c3bb80a fix key property of map function 2018-03-15 14:26:04 +01:00
Casper Lamboo
f65ab470bb fix heatedBed
@companje
2018-03-15 14:25:53 +01:00
Casper Lamboo
991586dc4c add missing break 2018-03-07 21:25:15 +01:00
Casper Lamboo
5e88cd8c17 fix unknown target 2018-03-07 19:13:05 +01:00
Casper Lamboo
38ab39f7de add custom actions 2018-03-07 18:21:44 +01:00
Rick Companje
df6e084503
Update default.yml 2018-03-07 16:53:45 +01:00
Casper Lamboo
c149667409 Only change value of form onblur 2018-03-07 14:41:42 +01:00
Casper Lamboo
d341cc28c9 use flat shading 2018-03-07 11:52:42 +01:00
Casper Lamboo
e1c4e2c1d4 open printer popup with ip if printer is unknown 2018-03-07 11:46:50 +01:00
Casper Lamboo
bc67cab75f Add selected printer query param
@companje
2018-03-07 11:14:02 +01:00
Casper Lamboo
229929e4e8 update docs 2018-03-06 17:49:39 +01:00
Casper Lamboo
a3f5c398da fix ordering connect points 2018-03-06 17:44:01 +01:00
Casper Lamboo
cd8687d148 fix filled shapes 2018-03-06 17:35:03 +01:00
Casper Lamboo
e056b37677 rename vars 2018-03-06 17:26:32 +01:00
Casper Lamboo
9a8f96a844 update web pack config 2018-03-06 17:17:05 +01:00
Casper Lamboo
b29198edd4 fix rounding errors
#38
2018-03-06 17:16:33 +01:00
Casper Lamboo
8bb97527f4 Fix incorrect order of points
#41
2018-03-06 17:01:52 +01:00
Casper Lamboo
16e6e11e2f Fix ignored last line 2018-03-06 12:41:00 +01:00
Casper Lamboo
c4d8d1136a use flat shading 2018-03-06 12:25:49 +01:00
Casper Lamboo
34a2b6cafc remove comma 2018-02-19 18:02:57 +01:00
Casper Lamboo
f032d8c267 Fix support
#39
2018-02-19 16:26:13 +01:00
Casper Lamboo
1107353290 lock doodle3d core version 2018-02-19 13:53:44 +01:00
Casper Lamboo
3d308e2533 fix intersections to shapes step 2018-02-19 13:53:18 +01:00
Casper Lamboo
793f8100fb change order of axis in dimensions preview 2018-02-19 13:48:55 +01:00
casperlamboo
120b49dfb7 fix some edge cases in intersections to shapes 2018-02-13 16:37:50 +01:00
casperlamboo
1434006f95 fix mapping to object 2018-02-13 00:01:41 +01:00
casperlamboo
8313982342 slice in worker 2018-02-12 23:58:12 +01:00
casperlamboo
e087fadd80 cleanup 2018-02-12 17:36:07 +01:00
casperlamboo
2b4941eefb allow adding doodle3d printer without ip 2018-02-12 15:34:46 +01:00
casperlamboo
c835ea12b2 don't export internal checkbox 2018-02-12 14:20:50 +01:00
casperlamboo
caf5e655da calculate face normals in slice process 2018-02-12 11:41:51 +01:00
casperlamboo
ca886afa25 rename file 2018-02-12 11:13:50 +01:00
casperlamboo
dbc01167e5 fix line preview 2018-02-12 11:07:06 +01:00
casperlamboo
45b0f541b1 send typed array instead of string across worker 2018-02-12 10:28:42 +01:00
casperlamboo
4352293e95 don't combine layerpoints and layer face indexes into objects 2018-02-12 00:36:05 +01:00
casperlamboo
4b17325c3f don't send instance of geometry to slicer
making importing three obsolete, reducing te worker from 700kb to 200kb
2018-02-12 00:10:44 +01:00
casperlamboo
03f95b7570 rename vectorutils 2018-02-11 23:50:57 +01:00
casperlamboo
5944153de7 disable line priview 2018-02-11 23:28:25 +01:00
casperlamboo
774289895e update doodle box url 2018-02-06 17:28:25 +01:00
casperlamboo
4bf14d3a1d rename cancel close 2018-02-06 17:21:39 +01:00
casperlamboo
34147a918e Unable to close add printer popup when no printer is added 2018-02-06 11:21:40 +01:00
casperlamboo
78764383e1 allow for empty ip in add doodle printer 2018-02-06 11:16:41 +01:00
casperlamboo
a08223f243 print can now parse urls with credentials 2018-02-05 14:38:43 +01:00
casperlamboo
89d4a659db reduce tolerance of min combing distance to 1mm 2018-02-01 18:29:20 +01:00
casperlamboo
0b6a4a1588 don't use inverse 2018-02-01 18:19:45 +01:00
casperlamboo
bc06f703ac move casting to string in slice.js 2018-02-01 18:13:55 +01:00
casperlamboo
6a63077b55 don't use three.js in worker 2018-02-01 17:57:20 +01:00
casperlamboo
c927c7bec1 typo 2018-02-01 16:59:35 +01:00
casperlamboo
0ef85cc918 fix intersecties to shapes code 2018-02-01 16:13:04 +01:00
casperlamboo
4fc34d0272 updated startcode
@companje
2018-02-01 14:40:26 +01:00
casperlamboo
aad4c1564c can connect to both start and end in one cycle 2018-02-01 14:39:14 +01:00
casperlamboo
8f475195b8 clean up create lines 2018-02-01 12:23:34 +01:00
casperlamboo
64d28affe6 improve intersections to shapes step
#36
2018-02-01 12:11:57 +01:00
casperlamboo
491067b070 use blob instead of file
for iOS 9
2018-01-31 14:17:21 +01:00
casperlamboo
3b99a3c494 update doodle3d api 2018-01-31 14:04:08 +01:00
casperlamboo
4cea3b3086 use blob instead of file so it can be used on ios9 2018-01-31 13:32:16 +01:00
casperlamboo
dd007b8bbf Link to connect.doodle3d.com
#37
2018-01-31 11:46:03 +01:00
casperlamboo
4d93ee2e9d add favicon 2018-01-30 19:41:30 +01:00
casperlamboo
8fde65a78a remove unused import 2018-01-30 17:01:54 +01:00
casperlamboo
fded534a10 remove log 2018-01-30 14:32:31 +01:00
casperlamboo
9cc64f44a5 fix component will unmount 2018-01-30 12:01:37 +01:00
casperlamboo
07f8255a37 move styles of logo to jss 2018-01-30 11:07:52 +01:00
Rick Companje
f809c28d26 added logo 2018-01-30 00:19:05 +01:00
casperlamboo
9eb01f2f21 decrease z offset 2018-01-29 17:17:11 +01:00
casperlamboo
377088fa34 fix wegpack config 2018-01-29 17:13:19 +01:00
casperlamboo
47e44dfed6 Trim extra zero in gcode 2018-01-29 17:13:19 +01:00
Rick Companje
602b5b35fe sliceInfo 2018-01-29 17:12:27 +01:00
casperlamboo
13cc1238f1 Fix infill settings 2018-01-29 16:21:12 +01:00
Rick Companje
4a3a5d832c Merge branch 'develop' of https://github.com/Doodle3D/Doodle3D-Slicer into develop 2018-01-29 16:14:44 +01:00
Rick Companje
a422956146 adding infill settings 2018-01-29 16:14:28 +01:00
casperlamboo
556d2f3eab Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	package-lock.json
2018-01-29 16:13:45 +01:00
casperlamboo
a88db15c96 fix building dist 2018-01-29 16:11:22 +01:00
Rick Companje
407d3355c0 adding infill settings 2018-01-29 16:11:19 +01:00
casperlamboo
4482bf1f73 compensate for higher layer height 2018-01-29 15:15:27 +01:00
casperlamboo
0c7f08735f implement printing to ultimaker2go 2018-01-29 13:59:29 +01:00
casperlamboo
cd2b0322d8 fix setting state in unmounted 2018-01-29 12:30:05 +01:00
casperlamboo
635e01fd01 update fetch progress code 2018-01-29 12:03:22 +01:00
casperlamboo
67b4084e55 add idle check for doodle3d printer 2018-01-29 12:03:01 +01:00
casperlamboo
21bd6c4714 decrease autoupdate interval 2018-01-29 11:04:08 +01:00
casperlamboo
cdb12f4d78 remove double setting 2018-01-26 09:31:49 +01:00
casperlamboo
a3c028a71c configure web pack for doodle3d api 2018-01-25 18:03:39 +01:00
casperlamboo
61b3d61f8a disable malign and wifi box controls 2018-01-25 18:03:39 +01:00
casperlamboo
abf426c5f2 Add export to wifi box 2018-01-25 18:03:39 +01:00
casperlamboo
fb06bfb135 add comment 2018-01-25 18:03:39 +01:00
Rick Companje
e289fd848a z offset 2018-01-25 17:47:42 +01:00
Rick Companje
8c3d444eed design 2018-01-25 17:47:36 +01:00
casperlamboo
0067c7a9e1 update scripts 2018-01-25 12:17:59 +01:00
casperlamboo
109cd0898c move malyan controls next to print button 2018-01-25 12:17:14 +01:00
casperlamboo
e091f425a1 simplify progress updating
by @companje
2018-01-25 12:13:11 +01:00
casperlamboo
e09ed8012c use fetch no-cors mode to make form posts to malyan
so we can upload from cross origin
because fetch has no progress we fake malyan upload progress by
updating 20kb every second

@companje
2018-01-25 12:12:47 +01:00
casperlamboo
56929c6af7 concat doodle3d printer and the doodle3d wifi box into one target 2018-01-24 16:06:39 +01:00
casperlamboo
de2acfe6be Add malyan controls
not styled yet
2018-01-24 16:03:44 +01:00
casperlamboo
6628b9cf13 Merge branch 'feature/support' into develop 2018-01-24 14:29:28 +01:00
casperlamboo
5cef2777fb save precision squared in const 2018-01-24 14:00:49 +01:00
casperlamboo
70aa39d89f support infill is always bidirectional 2018-01-24 13:59:39 +01:00
casperlamboo
1af76e6ef1 Merge branch 'develop' into feature/support 2018-01-24 12:50:47 +01:00
casperlamboo
88291ba549 implement support
#33
2018-01-24 12:50:41 +01:00
casperlamboo
5b934f0e71 rename infill density to density 2018-01-24 12:39:56 +01:00
casperlamboo
7906bfe43d remove weird code 2018-01-24 12:39:34 +01:00
casperlamboo
5d49ee0c74 send code "M563 S4" before uploading 2018-01-23 16:58:28 +01:00
casperlamboo
67981872aa implement print to malyan 2018-01-23 16:35:04 +01:00
casperlamboo
e384c74f8e fix typo 2018-01-23 12:36:05 +01:00
casperlamboo
d5ea670967 add ip in settings config for the doodle3d printer 2018-01-23 12:19:43 +01:00
casperlamboo
4dc5e4849e update fetch progress 2018-01-22 18:12:33 +01:00
casperlamboo
f28722aec5 rename percentage to density 2018-01-18 16:32:54 +01:00
casperlamboo
485f741077 change min area 2018-01-18 15:33:12 +01:00
casperlamboo
2f04aa9c50 fix threshold area function
#18
2018-01-18 15:30:08 +01:00
casperlamboo
ca3718e492 Remove tiny holes
#18
2018-01-18 15:27:08 +01:00
casperlamboo
0bb646a5ac move z offset to constant 2018-01-18 14:24:01 +01:00
casperlamboo
60c70cdbd5 difference line shapes with fill shapes 2018-01-18 14:02:34 +01:00
casperlamboo
198ca783f7 update package lock 2018-01-18 13:59:42 +01:00
casperlamboo
961337138b fix grid size calculation 2018-01-18 12:38:35 +01:00
casperlamboo
a892d6ff89 update package lock 2018-01-18 12:24:20 +01:00
casperlamboo
d4743ef867 improve bundle size 2018-01-18 12:06:14 +01:00
casperlamboo
46408e0668 add analyse script 2018-01-18 11:40:57 +01:00
casperlamboo
bbd8bc529d clean up 2018-01-18 11:14:21 +01:00
casperlamboo
0f7da85453 Revert "Fix brim"
This reverts commit eee2682f7064a64093c3025fdb3629291561e2e9.
2018-01-17 23:53:49 +01:00
casperlamboo
082329b810 fix loading mesh 2018-01-17 23:53:40 +01:00
casperlamboo
b562d3c2e2 allow for drag and dropping files 2018-01-17 17:42:58 +01:00
casperlamboo
a568a79ede don't require a model for the slicer interface 2018-01-17 17:18:28 +01:00
casperlamboo
742783e4db add margin to percentage container 2018-01-17 16:52:14 +01:00
casperlamboo
ac85bbc6d5 use primary buttons 2018-01-17 16:15:13 +01:00
casperlamboo
53e961b9bb fix min and max props 2018-01-17 16:10:22 +01:00
casperlamboo
11222aaa82 close url dialog by default 2018-01-17 15:55:21 +01:00
casperlamboo
1f4ca15442 Show dialog for closed popups
#30
2018-01-17 15:53:03 +01:00
casperlamboo
222a27d5b5 fix catching errors in the worker 2018-01-17 15:27:42 +01:00
casperlamboo
9f1958563d remove primary color 3 2018-01-17 14:58:36 +01:00
casperlamboo
a8b3d68845 order advanced settings based on most use 2018-01-17 14:21:59 +01:00
casperlamboo
2f4adbbb47 use mui theme for colors 2018-01-17 13:26:30 +01:00
casperlamboo
2fdc5ca16b disable selecting for title and details 2018-01-17 12:23:03 +01:00
casperlamboo
291e11fecf update style 2018-01-17 12:21:36 +01:00
casperlamboo
89e67882a0 cast number as string 2018-01-17 12:00:19 +01:00
casperlamboo
137f95fdba Add constrains to fields
#32
2018-01-17 11:56:42 +01:00
casperlamboo
9b78f4e2c8 move ultimaker 2go 2018-01-17 11:22:50 +01:00
casperlamboo
879667fa05 add manage printer dialog 2018-01-17 11:04:22 +01:00
casperlamboo
a48768e268 replace clear icon with refresh icon 2018-01-17 10:23:23 +01:00
casperlamboo
5fbd7f50ec Update title 2018-01-17 09:11:15 +01:00
casperlamboo
e06281667a change html title 2018-01-17 09:02:50 +01:00
casperlamboo
d190625f14 Fix form element 2018-01-17 09:01:40 +01:00
casperlamboo
9764e0a374 Add dimensions details to 3d panel 2018-01-17 08:40:48 +01:00
casperlamboo
e1d833d4f3 syntax 2018-01-16 18:52:08 +01:00
casperlamboo
f20f5b95b8 Change infill gridsize to infill percentage
#31
2018-01-16 18:52:03 +01:00
casperlamboo
7b59ba1108 implement local storage 2018-01-16 17:57:34 +01:00
casperlamboo
9d47e8dc23 change default brim size 2018-01-15 17:47:50 +01:00
casperlamboo
eee2682f70 Fix brim
brim now always prints the most outer layer first
2018-01-15 16:41:06 +01:00
casperlamboo
1ebbe7fc6a implement brim 2018-01-15 16:30:20 +01:00
casperlamboo
9a37e8a928 Revert "lower z offset"
This reverts commit 55eadc73debcd10969f640ba0e7fd9a8d5d2910c.
2018-01-15 15:55:48 +01:00
casperlamboo
da3ab2b0e6 Added default line to gcode 2018-01-15 15:43:58 +01:00
casperlamboo
700b27e6e0 add combing setting 2018-01-15 15:29:40 +01:00
casperlamboo
55eadc73de lower z offset 2018-01-15 15:27:12 +01:00
casperlamboo
03b9570014 change default bed temperature 2018-01-15 15:24:09 +01:00
casperlamboo
457f110dd2 slicer can now fetch d3 files from cloud 2018-01-15 15:17:38 +01:00
casperlamboo
0d64b62f12 update bed temperature of doodle3d printer 2018-01-15 14:40:52 +01:00
casperlamboo
212075e306 pack scene state in scene object 2018-01-15 14:21:42 +01:00
casperlamboo
ecc37273ca better error throwing 2018-01-15 13:47:16 +01:00
casperlamboo
10fb3714c7 add support for start and end code 2018-01-15 13:44:59 +01:00
casperlamboo
43af4e05ab add doodle3d printer 2018-01-15 13:43:53 +01:00
casperlamboo
2aee317d42 move ultimaker 2 go 2018-01-15 13:09:31 +01:00
casperlamboo
6f735b5de4 Merge branch 'master' into develop 2018-01-15 11:53:48 +01:00
casperlamboo
246522ee5f Add download g-code button 2017-12-24 17:18:33 +01:00
casperlamboo
c1cbe4f280 clean up 2017-12-24 14:53:34 +01:00
casperlamboo
d9edfe9bde disable control buttons instead of hiding them while slicing 2017-12-24 14:52:24 +01:00
casperlamboo
54811b27e9 rename base to default 2017-12-24 14:46:00 +01:00
casperlamboo
745c675f67 add transform-class-properties 2017-12-21 12:49:44 +01:00
casperlamboo
9fc1ba1cb8 babel rc 2017-12-19 16:23:45 +01:00
casperlamboo
68df877e1d babelrc 2017-12-19 16:16:48 +01:00
casperlamboo
346256ff47 babel rc 2017-12-19 16:11:17 +01:00
casperlamboo
a7b2852c6e Revert "remove babelrc"
This reverts commit 175689bd646b87f125f1610e65da1ef9c2dac4b7.
2017-12-19 16:03:42 +01:00
casperlamboo
175689bd64 remove babelrc 2017-12-19 16:03:28 +01:00
casperlamboo
c91ee0a5e9 fix babel rc 2017-12-19 16:00:27 +01:00
casperlamboo
5e959fa634 fix babelrc 2017-12-19 15:56:48 +01:00
casperlamboo
264ed096a4 Add layer height property 2017-12-19 14:37:43 +01:00
casperlamboo
b830cc611b edit margins 2017-12-19 14:34:58 +01:00
casperlamboo
6e55ca7a79 slice async 2017-12-19 13:42:48 +01:00
casperlamboo
a4d8e255cc mesh now slices geometry again
No need for doodle3d-core import
2017-12-19 12:38:58 +01:00
casperlamboo
7b57d5c7b0 version bumb 2017-12-18 16:38:00 +01:00
casperlamboo
b85781620e Slicer now slices d3sketch files instead of stl's
Easier to differentiate between open and closed shapes
2017-12-18 16:37:03 +01:00
casperlamboo
db0d82c396 remove save code 2017-12-14 15:19:09 +01:00
casperlamboo
6c02343da3 update css 2017-12-14 11:55:14 +01:00
casperlamboo
6b84572931 use three as modules 2017-12-06 11:54:09 +01:00
Rick Companje
5f1e628952 add 'name' to slice function and fix gcode object/string 2017-12-05 13:03:04 +01:00
casperlamboo
a1b4a9c454 update material 2017-12-05 11:20:06 +01:00
casperlamboo
bcf0bb254d add focus function 2017-12-05 11:10:38 +01:00
casperlamboo
d2c70f3b2f change rendering 2017-12-05 11:10:31 +01:00
casperlamboo
95ba0cfeb1 change rorate code 2017-12-05 11:10:17 +01:00
casperlamboo
84e28bc598 add on cancel 2017-12-04 19:31:15 +01:00
casperlamboo
31073e7122 remove unused code 2017-12-04 19:31:09 +01:00
casperlamboo
3dfce6a610 add title 2017-12-04 17:51:56 +01:00
casperlamboo
a79dd30abc update copy 2017-12-04 17:45:28 +01:00
casperlamboo
9d14e40c21 update tabs 2017-12-04 17:44:08 +01:00
casperlamboo
65d44db405 Make changes to UI 2017-12-04 15:08:29 +01:00
casperlamboo
bc9f0e673e add info to all printers
we should really add info like dimensions to all printers
2017-11-17 00:07:50 +01:00
casperlamboo
ed8ccd3f68 more flexibele code 2017-11-16 23:54:05 +01:00
casperlamboo
8c546e31b3 fix z offset 2017-11-16 23:32:50 +01:00
casperlamboo
8e5e000a12 semi z offset
isn’t real fix
2017-11-16 23:30:53 +01:00
casperlamboo
994a1caa98 update settings 2017-11-16 23:24:30 +01:00
casperlamboo
b47e98c005 add font family 2017-11-16 22:51:29 +01:00
casperlamboo
2aa72566db remove primary 2017-11-16 22:42:11 +01:00
casperlamboo
2d0f628743 move example 2017-11-16 22:40:39 +01:00
casperlamboo
2c2bbeda53 add z offset 2017-11-16 22:39:54 +01:00
casperlamboo
5bc7d09e8d add super() 2017-11-16 15:06:19 +01:00
casperlamboo
e33c967934 fix props 2017-11-16 15:05:28 +01:00
casperlamboo
5f1a7e3e74 simplify calculating center 2017-11-16 14:54:55 +01:00
casperlamboo
72c7c91b27 mov static functions 2017-11-16 14:54:47 +01:00
casperlamboo
aef67db205 remove height offseting 2017-11-16 14:54:33 +01:00
casperlamboo
b9b1f59af2 update displaying progress 2017-11-14 11:22:24 +01:00
casperlamboo
22298c9cb6 remove canvas width height updating 2017-11-14 11:21:58 +01:00
casperlamboo
1f7b48662a rename var printer to printers 2017-11-13 15:47:19 +01:00
casperlamboo
3221278853 rename var 2017-11-13 15:41:11 +01:00
casperlamboo
aa339fda3a fix last commit 2017-11-13 15:32:23 +01:00
casperlamboo
6e43994305 pass more data 2017-11-13 15:12:59 +01:00
casperlamboo
f4eaac16ff add browser target 2017-11-13 14:18:18 +01:00
casperlamboo
9f7242e0e4 change padding 2017-11-13 13:37:36 +01:00
casperlamboo
9e28b3b317 enable backface culling 2017-11-13 13:32:06 +01:00
casperlamboo
4e291b2370 force react v16 2017-11-13 13:21:36 +01:00
casperlamboo
d60b8d1ddb Merge branch 'develop' 2017-11-13 12:53:59 +01:00
casperlamboo
71c271bb59 Merge branch 'feature/interface' into develop 2017-11-13 12:53:32 +01:00
casperlamboo
1eedb88f0b fix building 2017-11-13 12:53:21 +01:00
casperlamboo
597ec406de set antialiasing to true 2017-11-13 12:50:45 +01:00
casperlamboo
23087af9fc hack to disable control 2017-11-13 12:44:03 +01:00
casperlamboo
8e985669ed better resize handling 2017-11-13 12:42:35 +01:00
casperlamboo
81d842cc8c move default props 2017-11-13 11:26:52 +01:00
casperlamboo
fcfe7f7bc6 update size when props change 2017-11-13 11:15:00 +01:00
casperlamboo
10055824aa space buttons 2017-11-13 11:01:57 +01:00
casperlamboo
83d96d88ec move on change to context 2017-11-13 10:45:23 +01:00
casperlamboo
45e0f02936 typo 2017-11-13 10:43:42 +01:00
casperlamboo
cd737da6d9 make settings passable 2017-11-13 10:40:58 +01:00
casperlamboo
53d2023047 rename to example 2017-11-13 03:05:29 +01:00
casperlamboo
6bdcb6cb23 also pass settings to callback 2017-11-13 03:05:06 +01:00
casperlamboo
057fd4e094 styles 2017-11-13 03:04:53 +01:00
casperlamboo
6768810fd6 remove example 2017-11-13 03:04:37 +01:00
casperlamboo
dcd3dc1614 settings now work 2017-11-13 02:47:53 +01:00
casperlamboo
6cd899f32b make settings editable 2017-11-13 02:09:39 +01:00
casperlamboo
c1117a8ce5 move static outside class 2017-11-12 19:12:32 +01:00
casperlamboo
88e056aece add basic settings drop downs 2017-11-12 18:41:00 +01:00
casperlamboo
dc6c1d7575 replace with material ui 2017-11-12 16:58:59 +01:00
casperlamboo
9c233b1ab6 don't convert geometry 2017-11-12 13:50:49 +01:00
casperlamboo
c65a5a3df4 make interface fullscreen 2017-11-12 13:08:51 +01:00
casperlamboo
3a09b93f46 set enabled of control
doesn’t do anything
2017-11-12 12:58:19 +01:00
casperlamboo
16158a3e3c add slice logging 2017-11-12 12:34:50 +01:00
casperlamboo
245e1b705a construct geometry in worker 2017-11-12 11:53:45 +01:00
casperlamboo
b6f94f6edb add draw range slider 2017-11-12 11:28:32 +01:00
casperlamboo
a5547ac070 simplify place on ground 2017-11-12 01:51:41 +01:00
casperlamboo
2c2b547ea2 focus camera 2017-11-12 01:51:33 +01:00
casperlamboo
9f49bb7b8c add border 2017-11-12 01:41:09 +01:00
casperlamboo
40d505d754 use printer from state 2017-11-12 01:41:05 +01:00
casperlamboo
ccc676ebd1 add download code 2017-11-12 01:15:38 +01:00
casperlamboo
3b4df148ed add slice again button 2017-11-12 01:04:26 +01:00
casperlamboo
849f3f893a move line preview to slice 2017-11-12 00:57:28 +01:00
casperlamboo
4654858c3e hide buttons bar when sliced 2017-11-12 00:47:06 +01:00
casperlamboo
624a178bb2 fix setting dimensions 2017-11-12 00:46:39 +01:00
casperlamboo
2b783e2889 beter type checking 2017-11-12 00:46:30 +01:00
casperlamboo
bacca4099c add comment 2017-11-12 00:46:12 +01:00
casperlamboo
c30bd0440e position geometry 2017-11-12 00:46:00 +01:00
casperlamboo
cd0406f0a9 add code preview 2017-11-12 00:11:05 +01:00
casperlamboo
ae24974e31 create basic component 2017-11-11 20:23:45 +01:00
casperlamboo
3f3408747c Merge branch 'develop'
# Conflicts:
#	src/settings/printer.yml
2017-10-17 12:07:52 +02:00
Rick Companje
dcfd1bf318 added ultimaker2+ and wanhao_duplicator_i3+ 2017-10-17 11:47:55 +02:00
casperlamboo
574072c1bd temporarily disable cleaning on open lines 2017-10-05 14:19:57 +02:00
casperlamboo
abde14fb4a set correct dimensions of ultimaker2 2017-09-19 14:18:51 +02:00
casperlamboo
65034af93b update 2017-09-15 10:49:30 +02:00
casperlamboo
b6d9cffbbb Merge branch 'develop' 2017-09-15 10:49:07 +02:00
casperlamboo
43aed5c3a2 add test model 2017-09-15 00:17:44 +02:00
casperlamboo
50d1efc7ea remove combing test 2017-09-15 00:17:35 +02:00
casperlamboo
8ea613e36e Always clean with clean delta 1
Making constant clean delta obsolete because it is already implied by
the const precision
2017-09-14 23:27:07 +02:00
casperlamboo
8db1feecb4 Merge branch 'master' into develop 2017-09-14 14:40:03 +02:00
casperlamboo
c9536d857a Merge branch 'develop' 2017-09-13 13:57:46 +02:00
casperlamboo
3422b1283f version bumb 2017-09-13 13:57:39 +02:00
casperlamboo
935e4eca98 Fix error in slices to gcode 2017-09-13 13:56:19 +02:00
casperlamboo
ef687769a1 update clipper-js 2017-09-13 12:29:28 +02:00
casperlamboo
c3eb42ee2e Version bumb 2017-09-13 11:45:15 +02:00
casperlamboo
f857a2f76f add clean to open shapes
Is now possible because of updated clipper-lib
2017-09-13 11:43:51 +02:00
casperlamboo
f3c1ca885a update clipper-js 2017-09-13 11:43:25 +02:00
Peter Uithoven
d3038d87d7 Adding polyfills 2017-09-07 12:55:56 +02:00
casperlamboo
24a100fbb1 Merge branch 'develop' 2017-08-25 15:36:52 +02:00
casperlamboo
d0f3137335 Merge branch 'master' into develop 2017-08-25 15:36:33 +02:00
casperlamboo
740d9d53ca fix combing 2017-08-25 15:35:38 +02:00
casperlamboo
b0341ec8db add combing test example 2017-08-24 10:55:58 +02:00
casperlamboo
cb61566c08 update comb code 2017-08-24 10:55:36 +02:00
casperlamboo
12a0eadaf4 update example 2017-08-17 16:13:30 +02:00
casperlamboo
1afbe54dc7 update combing 2017-08-17 16:13:21 +02:00
casperlamboo
65472890eb basic implementation of comb 2017-08-11 18:54:04 +02:00
casperlamboo
4ededa4dfe add duration and filament usage 2017-08-03 00:17:34 +02:00
Simon Voordouw
7e55b95822 Merge branch 'develop' 2017-08-01 14:36:44 +02:00
casperlamboo
9363fe58af actually edit parts array 2017-08-01 14:24:15 +02:00
casperlamboo
b566a387c6 remove parts that don't contain shells 2017-08-01 13:58:44 +02:00
peteruithoven
0c727dd16c Disable public publish to npm 2017-07-31 10:31:26 +02:00
casperlamboo
48f2cef965 Merge branch 'develop' 2017-07-28 15:13:01 +02:00
casperlamboo
484a6413a3 Merge branch 'feature/updated-settings' into develop 2017-07-28 15:11:09 +02:00
casperlamboo
ab4427e818 differentiate between open lines and closed lines for non filled paths 2017-07-28 15:02:24 +02:00
casperlamboo
53bd335d7f fix settings 2017-07-28 12:17:40 +02:00
casperlamboo
72db7105fb combine interlines and outline into shell 2017-07-28 12:13:53 +02:00
casperlamboo
d380db1d7a remove log 2017-07-28 11:21:51 +02:00
casperlamboo
39f29eb489 separate inner fill and outer fill 2017-07-28 11:05:48 +02:00
casperlamboo
a19ffbffab merge vertices of example 2017-07-28 10:48:46 +02:00
casperlamboo
f390f1cc7c Merge branch 'develop' 2017-07-27 18:53:37 +02:00
casperlamboo
138a1faa7e Merge branch 'develop' into feature/updated-settings
# Conflicts:
#	src/sliceActions/helpers/GCode.js
2017-07-27 18:33:52 +02:00
casperlamboo
9805991d9f use new settings
#25
2017-07-27 18:33:25 +02:00
casperlamboo
bcf62c71ec remove settings from gcode class 2017-07-27 18:20:08 +02:00
casperlamboo
9bc832fcdd remove infillOverlap refference 2017-07-27 18:05:09 +02:00
casperlamboo
eb6747a49c Merge branch 'develop' 2017-07-27 16:48:34 +02:00
casperlamboo
b8b67e664b Fix extruder calculation 2017-07-27 16:47:36 +02:00
casperlamboo
34f6a91db9 remove unnecessary check 2017-07-27 14:48:15 +02:00
casperlamboo
bcfbe99579 remove overlap setting 2017-07-27 12:42:14 +02:00
casperlamboo
ccc88bdb77 pre calculate bounding boxes 2017-07-27 10:35:30 +02:00
casperlamboo
4304f7373d add comment
@peteruithoven I was wrong about the less then :(
2017-07-25 14:36:26 +02:00
casperlamboo
2304560a13 Revert "Fix generate inner lines"
This reverts commit b62e3b5cb8a95ae0e0093be0f56190c26f626efc.
2017-07-25 14:34:12 +02:00
casperlamboo
ee83f69647 Merge branch 'develop' 2017-07-25 14:20:33 +02:00
casperlamboo
b62e3b5cb8 Fix generate inner lines
Use correct number of shells
2017-07-25 14:19:26 +02:00
casperlamboo
1ee1321859 Merge branch 'master' into develop 2017-07-24 17:57:37 +02:00
casperlamboo
78c1718e6e upgrade version 2017-07-24 17:56:59 +02:00
casperlamboo
23948d9ddc remove old clipper-js 2017-07-24 17:56:32 +02:00
casperlamboo
2abbd6d325 Merge branch 'develop' 2017-07-24 17:50:05 +02:00
casperlamboo
4f50ccb088 version bumb 2017-07-24 17:47:13 +02:00
casperlamboo
a00773a2c9 use @doodle3d/clipper-js 2017-07-24 17:47:03 +02:00
casperlamboo
3c08a4f47c Merge branch 'master' into develop 2017-07-24 17:15:33 +02:00
peteruithoven
0f078614b1 Merge remote-tracking branch 'origin/master' 2017-07-24 16:00:41 +02:00
peteruithoven
e39d02abbd Merge branch 'develop' 2017-07-24 16:00:15 +02:00
peteruithoven
d1849cb8af version bump 2017-07-24 16:00:02 +02:00
peteruithoven
e723db92a0 Merge branch 'develop' 2017-07-24 15:58:27 +02:00
peteruithoven
b366861326 Merge branch 'babel' into develop 2017-07-24 15:52:27 +02:00
peteruithoven
46dd12d6ec Merge remote-tracking branch 'origin/develop' into develop 2017-07-24 15:50:44 +02:00
casperlamboo
0ad8de8c80 simplify code 2017-07-24 15:45:18 +02:00
casperlamboo
4a61164af9 Terminate web worker on error 2017-07-24 15:40:54 +02:00
casperlamboo
cb465b9eee Throw error when provided mesh is empty
@peteruithoven
2017-07-24 15:40:39 +02:00
casperlamboo
8b475d8e4b typo 2017-07-24 13:01:22 +02:00
casperlamboo
c07a0b81c2 update git ignore 2017-07-24 13:01:16 +02:00
peteruithoven
f039b92802 Prepare script 2017-07-20 22:34:36 +02:00
peteruithoven
45a3c54112 Adding webpack-dev-server dependency 2017-07-20 16:59:33 +02:00
peteruithoven
d718646e77 Using better yml-loader 2017-07-20 16:58:42 +02:00
peteruithoven
1a002fce9d removed jspm.config.js 2017-07-20 16:32:55 +02:00
peteruithoven
6744df1779 Fixed simple example 2017-07-20 16:32:48 +02:00
peteruithoven
0f4434330b Lowercasing slicer.js 2017-07-20 16:25:02 +02:00
peteruithoven
541d205a2f Merge branch 'develop' into babel 2017-07-20 16:24:14 +02:00
peteruithoven
788cd3d476 Merge remote-tracking branch 'origin/develop' into develop 2017-07-20 16:22:11 +02:00
peteruithoven
9b979acbd0 Ignore higher .babelrc file in example 2017-07-20 16:14:01 +02:00
peteruithoven
8d8be75dd6 Removing doodle3d-slicer dep from example 2017-07-20 16:13:46 +02:00
peteruithoven
c99489e5d0 Publish under doodle3d npm org 2017-07-20 16:13:20 +02:00
peteruithoven
167c2c67ca simple example fixed 2017-07-20 12:12:25 +02:00
peteruithoven
566c80f6e8 Adding three dependency 2017-07-20 12:09:23 +02:00
peteruithoven
fa1ae069bf Removing jspm specific loader specification 2017-07-20 11:54:49 +02:00
peteruithoven
f330aad040 Clipper-js dependency 2017-07-20 11:51:48 +02:00
peteruithoven
a0747d6db2 Include settings in build step 2017-07-20 11:42:46 +02:00
peteruithoven
02b1f080fb Assumming package user configured loaders 2017-07-20 11:34:47 +02:00
peteruithoven
cfcd839baa Switching to babel 2017-07-20 11:20:43 +02:00
casperlamboo
30645f634a Updated API
@peteruithoven @mith

I think this is a somewhat nicer API for the slicer. I completely
removed the Slicer class and instead added two function: sliceGeometry
and sliceMesh
2017-07-20 10:29:33 +02:00
casperlamboo
fb69ad850d Merge branch 'develop' 2017-07-20 09:30:30 +02:00
casperlamboo
ed05c6fda9 update save example 2017-07-20 00:10:29 +02:00
casperlamboo
0564f3e7bc fix onProgress handling in slice function 2017-07-20 00:06:19 +02:00
casperlamboo
a79308dc8f better error handling 2017-07-20 00:05:50 +02:00
peteruithoven
580b41413f Merge remote-tracking branch 'origin/develop' into develop 2017-07-19 21:40:19 +02:00
casperlamboo
281b037f9e Merge branch 'develop' 2017-07-19 20:55:55 +02:00
casperlamboo
d7a525f75b camel casing 2017-07-19 20:54:39 +02:00
casperlamboo
e4880106f1 remove progress promise
@mith @peteruithoven
2017-07-19 20:54:21 +02:00
peteruithoven
2356136a3d Merge remote-tracking branch 'origin/develop' into develop 2017-07-19 17:54:35 +02:00
casperlamboo
c390cc335a increase total stages because step is added 2017-07-19 17:37:40 +02:00
peteruithoven
2668c16500 Merge remote-tracking branch 'origin/develop' into develop 2017-07-19 17:36:25 +02:00
casperlamboo
8c57e16998 cleanup Slicer 2017-07-19 17:36:20 +02:00
casperlamboo
3f140d7f03 don't catch result of void detectOpenClosed function 2017-07-19 17:36:03 +02:00
casperlamboo
8e45881a45 pre calculate outer lines 2017-07-19 17:33:55 +02:00
casperlamboo
44197dd5e4 remove unnecessary new key word 2017-07-19 11:50:23 +02:00
casperlamboo
fdbb1469ee format settings deconstructing 2017-07-19 11:04:13 +02:00
casperlamboo
3b4dbc0d4a fix settings deconstructing 2017-07-19 11:03:16 +02:00
casperlamboo
1e16c741a0 move helper classes to helper folder 2017-07-19 11:02:42 +02:00
casperlamboo
73f0433e00 wrap progress in object
So data could be extended in the future
2017-07-19 11:02:14 +02:00
casperlamboo
83f543b62b fix isFlat check 2017-07-18 16:55:27 +02:00
casperlamboo
65f1e96ed8 Merge branch 'master' into develop 2017-07-18 14:23:37 +02:00
casperlamboo
6c7c2b1708 remove printer_settings.json file 2017-07-18 14:05:25 +02:00
casperlamboo
9c04511f94 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	settings/printer_settings.json
2017-07-18 14:05:09 +02:00
casperlamboo
a5384b3de1 Merge branch 'develop' 2017-07-18 14:02:45 +02:00
casperlamboo
365680a693 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	jspm.config.js
#	package.json
2017-07-18 12:53:59 +02:00
casperlamboo
e675cf200a update readme 2017-07-18 12:43:27 +02:00
casperlamboo
1d2a9ba965 use npm version of three.js 2017-07-18 12:38:03 +02:00
casperlamboo
e54d077db0 update example 2017-07-18 12:30:56 +02:00
casperlamboo
0a041ddae0 update progress api 2017-07-18 12:26:30 +02:00
casperlamboo
4a238f9089 remove settings class 2017-07-18 11:39:38 +02:00
casperlamboo
622be82706 Merge remote-tracking branch 'origin/feature/split-settings' into develop
# Conflicts:
#	example/save.js
#	example/viewer.js
#	jspm.config.js
#	package.json
#	src/GCode.js
#	src/Slicer.js
#	src/index.js
#	src/sliceActions/addBrim.js
#	src/sliceActions/calculateLayersIntersections.js
#	src/sliceActions/createLines.js
#	src/sliceActions/detectOpenClosed.js
#	src/sliceActions/generateInfills.js
#	src/sliceActions/generateInnerLines.js
#	src/sliceActions/generateSupport.js
#	src/sliceActions/intersectionsToShapes.js
#	src/sliceActions/removePrecision.js
#	src/sliceActions/slice.js
2017-07-18 11:26:11 +02:00
casperlamboo
4e38acd9bd Merge branch 'feature/improved-open-closed-detection' into develop 2017-07-18 10:42:04 +02:00
casperlamboo
94181d6660 update save example 2017-07-18 10:34:20 +02:00
casperlamboo
0f579b8055 update detect open closed shapes logic 2017-07-18 10:23:16 +02:00
casperlamboo
b92a35a0c1 remove stl's and add json files 2017-07-18 10:22:26 +02:00
casperlamboo
8988bc4368 remove stl loader 2017-07-18 10:21:37 +02:00
casperlamboo
8b9b789c56 remove unused three js imports 2017-07-17 16:18:44 +02:00
casperlamboo
87f7f2f906 Fix geometry type check
#21
2017-07-17 15:22:13 +02:00
Rick Companje
f3c0960512 set default top thickness to 1.2 2017-07-13 18:12:45 +02:00
Rick Companje
a216ee0e94 fixed typo 'high' and added fill.gridSize:0.15 2017-07-13 17:56:57 +02:00
Rick Companje
cd3c2d8cb4 fixed typo in title of Renkforce RF100 printer 2017-07-13 15:59:07 +02:00
Rick Companje
f374139238 added custom dimensions for Renkforce printer 2017-07-12 12:54:20 +02:00
Rick Companje
8cf264b437 renamed 'title' of 'Ultimaker' to 'Ultimaker Original' 2017-07-12 10:45:20 +02:00
peteruithoven
7a111371e9 Fixed code in readme 2017-07-07 18:11:37 +02:00
peteruithoven
94c21360b2 Added simple example 2017-07-07 18:10:29 +02:00
Peter Uithoven
b517ad222b Added version to package.json 2017-07-03 14:47:34 +02:00
Peter Uithoven
d8fdfd3e11 Fixed package.json syntax error 2017-07-03 14:45:11 +02:00
Peter Uithoven
c7f4553b4e Added name to package.json 2017-07-03 14:44:40 +02:00
Rick Companje
ba141a54a5 fixed typo in printer.type for ultimaker2go 2017-06-22 11:13:26 +02:00
casperlamboo
16d2468ced make variables private in GCode.js 2017-06-22 10:24:09 +02:00
casperlamboo
f4b9d565a7 clean up 2017-06-22 10:21:01 +02:00
casperlamboo
ef97f362e3 typo 2017-06-22 10:20:44 +02:00
casperlamboo
3d52fc9139 properly check for undefined 2017-06-22 10:19:15 +02:00
casperlamboo
68f1cd847d update add brim code 2017-05-26 17:35:30 +02:00
casperlamboo
8bf771b0f9 use includes instead of indexOf 2017-05-26 17:14:14 +02:00
casperlamboo
d92f6bb056 move merge vertices and compute normals to slice actions 2017-05-26 17:12:01 +02:00
casperlamboo
846ddcd97a Better detection of open closed shapes
when a ‘single walled’,  ‘closed’ shape is sliced the shape will appear
to be closed by the slicer. This happens because the start and endpoint
of the 2d shape are connected. This commits fixes this.

In the new approach al parts of the geometry are split up into shapes.
Then the 3d shapes are places into two categories (closed and open
geometries).

Based on weather the 3d geometry is open or closed the 2d shape will be
open or closed

@mith @peteruithoven
2017-05-26 17:11:38 +02:00
casperlamboo
5f5b1254ad re enable applying and removing precision for open shapes 2017-05-26 16:56:05 +02:00
casperlamboo
07d059d456 fix setting replace code 2017-05-19 12:34:57 +02:00
casperlamboo
721b75b1f5 Don't scale up or scale down open shapes 2017-05-19 11:56:53 +02:00
casperlamboo
e88e8804d2 always start first layer on Z0.2 2017-05-19 11:43:47 +02:00
casperlamboo
a0aec2f4ad Don't clean open shapes 2017-05-19 10:51:34 +02:00
casperlamboo
0d3ac6525c store calculation in const 2017-05-19 10:49:26 +02:00
casperlamboo
93c9040170 remove unused variable 2017-05-19 10:49:10 +02:00
casperlamboo
c5a6f3cf14 simplify getOutline function 2017-05-16 10:44:48 +02:00
casperlamboo
c9cbd1fc49 Scale down open paths 2017-05-16 10:38:40 +02:00
casperlamboo
bb213547e2 increase clean delta 2017-05-15 16:01:51 +02:00
casperlamboo
501f154569 remove unused import 2017-05-14 16:09:15 +02:00
casperlamboo
0ba712c049 update castle model 2017-05-14 13:45:21 +02:00
casperlamboo
a7a8ffa5f9 update slicer viewer 2017-05-14 12:57:55 +02:00
casperlamboo
f80ff22076 remove unused app.js 2017-05-13 15:44:41 +02:00
casperlamboo
183622e143 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	example/app.js
#	package.json
2017-05-13 15:41:46 +02:00
casperlamboo
0f10629a3c add save example 2017-05-13 15:39:45 +02:00
casperlamboo
1ec4f55c4b normalize cleanDelta with precision 2017-05-13 15:26:46 +02:00
casperlamboo
ff9ef8690a clean after simplify 2017-05-13 15:26:04 +02:00
casperlamboo
e6269bd581 fix construction of clipper paths 2017-05-13 15:25:58 +02:00
casperlamboo
229f194def replace tabs with spaces 2017-05-13 14:48:48 +02:00
Simon Voordouw
da8a54550a update dependencies and fix Three.js imports 2017-05-12 16:32:05 +02:00
casperlamboo
b7b2eff61b use simplify('pftNonZero') instead of deprecated removeOverlap function 2017-05-12 13:44:55 +02:00
casperlamboo
e9c2f653bf update three.js 2017-01-18 13:30:57 +01:00
casperlamboo
bbda9089d2 don't calculate 1 - alpha twice 2017-01-18 12:05:21 +01:00
casperlamboo
7f2b8da15f add stl loader 2016-10-16 11:10:39 +02:00
casperlamboo
0d5587a9c9 implement better viewer as example 2016-10-13 16:33:40 +02:00
casperlamboo
c34be4595a prettify 2016-10-13 14:24:53 +02:00
casperlamboo
fdff42b4a5 fix indenting 2016-10-13 14:24:12 +02:00
casperlamboo
83febd6aa0 remove enter 2016-10-13 14:22:54 +02:00
casperlamboo
45514218af rename sliceWorker to slicerWorker 2016-10-13 14:22:04 +02:00
casperlamboo
868748ee2c add all optional arguments in Shape constructor 2016-08-27 10:08:57 +02:00
casperlamboo
8d2ccc2c86 add break property to switch 2016-08-27 10:02:17 +02:00
casperlamboo
b8b13b2a6b use mapToLower to get path data 2016-08-27 10:01:50 +02:00
casperlamboo
bf2a9c512c added map to lower argument 2016-08-27 09:55:59 +02:00
casperlamboo
91fc46621f update config 2016-08-26 21:43:46 +02:00
casperlamboo
178e88f76b splice slice sync and slice async 2016-08-19 15:12:20 +02:00
casperlamboo
0ae8ef33b9 make example non-blocking 2016-08-19 14:54:10 +02:00
casperlamboo
4c42bd8147 update read me 2016-08-19 14:47:15 +02:00
casperlamboo
bf9d98611a implement async slicing 2016-08-19 14:46:02 +02:00
casperlamboo
ce1a0b760f reorganize folders and make Slicer.slice promise 2016-08-19 14:09:51 +02:00
casperlamboo
84f3656580 remove event dispatcher 2016-08-19 14:09:25 +02:00
casperlamboo
5a5ded1b5a add jspm dependency 2016-08-19 14:05:30 +02:00
casperlamboo
2ee97f9590 add capital to constructor functions 2/2 2016-08-19 13:50:41 +02:00
casperlamboo
4b5a49c829 add capital to constructor functions 1/2 2016-08-19 13:50:06 +02:00
casperlamboo
cbff9134f2 change settings 2016-07-19 14:45:17 +02:00
casperlamboo
5f2a3a6328 replace event dispatcher 2016-07-19 14:32:48 +02:00
casperlamboo
d016aa345b Merge branch 'restructure' into develop 2016-07-19 14:28:20 +02:00
casperlamboo
b8d567aabe remove spaces
comply with linter
2016-07-19 14:28:03 +02:00
casperlamboo
b6350871be instance check instead of type check 2016-07-19 14:26:06 +02:00
casperlamboo
782c61ecdb better error handling 2016-07-19 14:25:48 +02:00
casperlamboo
1b6a5966ca update config handling 2016-07-19 14:23:23 +02:00
casperlamboo
ab3b241fcb update to jspm 0.17 2016-07-18 18:28:47 +02:00
casperlamboo
74e0a5d0ef remove clone function in example 2016-05-09 11:42:23 +02:00
casperlamboo
08adf33139 remove line breaks 2016-05-09 11:41:21 +02:00
casperlamboo
47e5090444 remove line breaks 2016-05-09 11:40:54 +02:00
casperlamboo
0d4f6fbe5b remove line breaks 2016-05-09 11:17:49 +02:00
casperlamboo
481de15e75 update example 2016-05-08 11:12:35 +02:00
casperlamboo
5b9931531b include settings in lib 2016-05-08 11:09:34 +02:00
casperlamboo
ecce63f63c fix subsitute variables 2016-05-08 10:31:29 +02:00
casperlamboo
06c61a3cf6 move set settings to constructor 2016-05-08 10:31:00 +02:00
casperlamboo
d9fca74653 fix code 2016-05-08 10:26:47 +02:00
casperlamboo
5a46e138af use deconstion in code 2016-05-08 10:26:41 +02:00
casperlamboo
7517f522d0 use single quotes 2016-05-07 23:46:08 +02:00
casperlamboo
7360fc86bc use deconstruction 2016-05-07 23:45:58 +02:00
casperlamboo
0eb2a3994f use deconstruction in move 2016-05-07 23:31:06 +02:00
casperlamboo
caf62e58d5 remove worker 2016-05-07 23:06:56 +02:00
casperlamboo
8b9b1af612 update settings 2016-05-07 22:59:20 +02:00
casperlamboo
b65541ff54 remove slicer worker from index 2016-05-07 22:58:07 +02:00
casperlamboo
edeb24545e remove unused code 2016-05-07 22:45:36 +02:00
casperlamboo
fdb5e96b6d update config 2016-05-07 22:33:20 +02:00
casperlamboo
5374a2d9c0 implement optimize paths 2016-05-07 22:33:15 +02:00
casperlamboo
0d1a9881db use destructure in add brim 2016-05-06 22:46:32 +02:00
casperlamboo
c8f97389ce add action log 2016-05-06 20:13:30 +02:00
casperlamboo
53d5d0a375 move file 2016-05-06 20:12:49 +02:00
casperlamboo
896eae7185 remove comment 2016-05-06 20:04:12 +02:00
casperlamboo
44e05bed28 remove line break 2016-05-06 20:03:15 +02:00
casperlamboo
5f5fac3180 remove constructor 2016-05-06 20:00:39 +02:00
casperlamboo
6c18b44827 remove update progress code
want to implement this in a different way
2016-05-06 19:59:49 +02:00
casperlamboo
1388bb2c44 move support enabled check 2016-05-06 19:58:39 +02:00
casperlamboo
065b43572d typo 2016-05-06 19:56:14 +02:00
casperlamboo
26ed8df38d fix action log 2016-05-06 19:56:08 +02:00
casperlamboo
457eaa1bef move scale up and scale down to separate function 2016-05-06 19:54:25 +02:00
casperlamboo
e16a5dda90 move brim code to separate function 2016-05-06 19:52:31 +02:00
casperlamboo
c8150c3bf7 use default dimensionsZ name 2016-05-06 19:49:07 +02:00
casperlamboo
9d828e3fda move down path to code 2016-05-06 19:48:41 +02:00
casperlamboo
bf0f504c34 comment optimize path code 2016-05-06 19:47:42 +02:00
casperlamboo
51412b5599 remove line break 2016-05-06 19:46:56 +02:00
casperlamboo
4237195b5e update create lines 2016-05-06 19:45:03 +02:00
casperlamboo
626867e554 cleanup calculate layers intersections 2016-04-23 09:56:47 +02:00
casperlamboo
7042ebf588 cleanup slicer 2016-04-23 09:56:36 +02:00
casperlamboo
b21db81f12 cleanup slice 2016-04-23 09:56:25 +02:00
casperlamboo
9e4109a3f8 cleanup slices to geode 2016-04-23 09:56:15 +02:00
casperlamboo
ebbe75ef2e implement constants 2016-04-23 00:24:01 +02:00
casperlamboo
9d17df0cf3 single quotes 2016-04-22 21:15:39 +02:00
casperlamboo
b9a2fdcc81 else on one line 2016-04-22 21:15:33 +02:00
casperlamboo
1cddce9e69 replace vars with const and let 2016-04-22 21:14:21 +02:00
casperlamboo
6891e09b54 fix infills 2016-04-22 19:38:06 +02:00
casperlamboo
fb691f175c replace var with const 2016-04-22 19:38:00 +02:00
casperlamboo
09bd688b88 fix get fill template 2016-04-22 19:37:34 +02:00
casperlamboo
00c94f5d67 fix join 2016-04-21 22:50:02 +02:00
casperlamboo
386a53849f remove unused imports 2016-04-21 22:22:59 +02:00
casperlamboo
4edb1985c8 rafacter 2016-04-21 22:14:22 +02:00
casperlamboo
2b10f388f7 simplified add gcode 2016-03-29 15:56:32 +02:00
casperlamboo
bd910a270c geplakt var with const or let in createLines 2016-03-29 09:53:48 +02:00
casperlamboo
bd511c3509 remove var for const or let in calculateLayerIntersections.js 2016-03-29 09:53:12 +02:00
casperlamboo
aa9de5ec47 use deconstructs 2016-03-29 08:49:35 +02:00
casperlamboo
e573a0662b move adeline to separate file 2016-03-29 08:15:30 +02:00
casperlamboo
3531d64dbf use s6 string 2016-03-29 08:01:07 +02:00
casperlamboo
ef74eebf8a implemented event dispatcher 2016-03-29 00:35:53 +02:00
casperlamboo
02e406f018 remove spaces 2016-03-29 00:27:06 +02:00
casperlamboo
3db03cd873 separate into actions into different files 2016-03-29 00:26:58 +02:00
casperlamboo
7ae57e0bda added detection for inside-out models 2015-10-16 14:28:10 +02:00
casperlamboo
0f6c73e93f updated to three.js r72 2015-10-14 17:11:29 +02:00
casperlamboo
fa38d8117e fixed clipper alert error 2015-08-26 18:27:56 +02:00
casperlamboo
aa71eb5b67 cloned outline
cloned outline so pointers get lost
2015-08-05 11:26:10 +02:00
casperlamboo
d3af71bc22 fixed https://github.com/Doodle3D/Doodle3D-Slicer/issues/12
fixed https://github.com/Doodle3D/Doodle3D-Slicer/issues/12
2015-08-04 09:54:02 +02:00
casperlamboo
1ac87e6f0c implemented better hole detection system 2015-08-03 12:09:46 +02:00
casperlamboo
0c557172e0 fixed bug in shell thickness 2015-07-30 14:31:55 +02:00
casperlamboo
0dd19d060a Fixed bug in constucting low/high fill areas 2015-07-30 11:36:20 +02:00
casperlamboo
2bc9d8fb35 fixed closed lines detected as open lines
fixed https://github.com/Doodle3D/Doodle3D-Slicer/issues/10
2015-07-29 20:59:34 +02:00
casperlamboo
d4f6776aa1 updated webworkers 2015-07-29 16:07:52 +02:00
casperlamboo
adafc44dc1 improved slicing algoritm 2015-07-29 11:18:18 +02:00
casperlamboo
bde4a8a907 fixed bug in open lines 2015-07-28 12:34:57 +02:00
casperlamboo
3b455377ba Improved hole detection algorithm 2015-07-28 12:28:08 +02:00
Casper Lamboo
13c1333ce8 Update README.md 2015-07-26 15:38:48 +02:00
Casper Lamboo
4ae00e00fd Update README.md 2015-07-26 15:37:44 +02:00
Casper Lamboo
5a56818279 Update README.md 2015-07-26 15:37:10 +02:00
Casper Lamboo
1b48683738 Create README.md 2015-07-26 15:34:57 +02:00
casperlamboo
7304a95276 Moved To ES6 2015-07-26 15:32:10 +02:00
casperlamboo
c541d70157 fixed closing line bug 2015-07-26 15:24:35 +02:00
casperlamboo
cd71f7a378 better names 2015-07-10 18:07:21 +02:00
casperlamboo
f8be250815 added support for non closing parts 2015-07-10 18:04:10 +02:00
casperlamboo
ce50b84010 improves slicing algorithm
fixed https://github.com/Doodle3D/Doodle3D-Slicer/issues/4
2015-07-10 15:06:51 +02:00
casperlamboo
bb42335353 added setMesh and setGeometry 2015-07-10 12:59:50 +02:00
casperlamboo
7064058464 Fix progress bug 2015-07-10 09:16:03 +02:00
casperlamboo
151dee5ee9 Fixed error with handling clipper
no longer need to comment clipper error alerts
2015-07-06 22:59:58 +02:00
casperlamboo
fee357b910 move THREE normal function to utils 2015-07-01 14:51:41 +02:00
casperlamboo
ffb8d337cb added printer outline for editor 2015-06-17 21:26:49 +02:00
casperlamboo
18b71b92f1 made some functions and variables private with "_" 2015-06-17 09:36:01 +02:00
casperlamboo
d99e302c38 added doodle_app and fixed https://github.com/Doodle3D/Doodle3D-Slicer/issues/6 2015-06-16 18:37:26 +02:00
casperlamboo
44994f61a8 slicer didn't fill top anymore; fixed 2015-06-16 12:37:51 +02:00
casperlamboo
f9ef14822b edited viable names and removed bug 2015-06-16 10:28:26 +02:00
casperlamboo
68e16a94a4 added infill overlap 2015-06-16 07:23:31 +02:00
casperlamboo
f25e6493b2 didn't thing about the difference in size when lines are diagonal 2015-06-16 01:38:33 +02:00
casperlamboo
89b07ff341 made diagonal infill 2015-06-16 01:25:06 +02:00
casperlamboo
a70ba60854 fixed bug https://github.com/Doodle3D/Doodle3D-Slicer/issues/3 2015-06-15 17:22:36 +02:00
casperlamboo
f7e9309e6d Fixed major bug 2015-06-15 11:40:19 +02:00
casperlamboo
06528f6d0f cleaner code 2015-06-15 10:21:05 +02:00
casperlamboo
74a83f9c3b fixed minor bugs 2015-06-13 20:09:44 +02:00
casperlamboo
67093d047d fixed spelling errors 2015-06-12 21:19:56 +02:00
casperlamboo
99b2b24d88 added three js editor + slicer 2015-06-12 15:58:26 +02:00
casperlamboo
14c5d85318 changed format settings 2015-06-11 15:42:38 +02:00
casperlamboo
090bdf248a Added slice abstraction to Slice class 2015-06-11 14:34:30 +02:00
casperlamboo
3bacee2072 improved index page
index page now checks if doodle boxes are alive before displaying them
2015-06-11 11:08:56 +02:00
casperlamboo
81ec036973 improved network error handling
can now reconnect when connecting failed
2015-06-11 10:28:21 +02:00
74 changed files with 24562 additions and 188506 deletions

26
.babelrc Normal file
View File

@ -0,0 +1,26 @@
{
"env": {
"module": {
"presets": [
["env", {
"targets": {
"node": "6",
"browsers": ["last 2 versions", "safari >= 7", "not ie < 11"]
},
"modules": false
}],
"stage-0",
"react"
]
},
"main": {
"presets": ["env", "stage-0", "react"]
}
},
"plugins": [
"transform-class-properties",
"transform-object-rest-spread",
"transform-runtime",
"transform-es2015-classes"
]
}

33
.eslintrc Normal file
View File

@ -0,0 +1,33 @@
{
"extends": "eslint-config-airbnb",
"parser": "babel-eslint",
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"modules": true,
"jsx": true
},
"rules": {
"comma-dangle": [1, "never"],
"no-else-return": 0,
"no-use-before-define": [2, "nofunc"],
"no-param-reassign": 0,
"no-var": 1,
"no-labels": 0,
"guard-for-in": 0,
"prefer-const": 0,
"no-unused-vars": 1,
"key-spacing": [1, {"beforeColon": false, "afterColon": true, "mode": "minimum"}],
"no-loop-func": 1,
"react/sort-comp": [0],
"max-len": [1, 110, 4],
"camelcase": 1,
"new-cap": 0
},
"env": {
"browser": true,
"es6": true
},
"globals": {
"THREE": false
}
}

8
.gitignore vendored
View File

@ -1,8 +1,8 @@
*.DS_Store
jspm_packages/*
node_modules
node_modules/*
bundle.js
lib
module
dist

2
.npmignore Normal file
View File

@ -0,0 +1,2 @@
node_modules
jspm_packages

136
DOCS.md Normal file
View File

@ -0,0 +1,136 @@
# Doodle3D Slicer
This document explains how the slice process works.
In this slicer Z is the "up" vector.
Requisites
- 2D Vector math
- 3D Vector math
- 2D Boolean operations (union, difference)
- 2D Path offsetting
### Step 0: Preparation
The first step is to prepare the data for slicing. Most of the model data is mapped into `typed arrays`. This way they can be send to the worker very efficiently (due to the transferable nature of typed arrays).
```
Vertices: Float32Array
Faces: Uint32Array
ObjectIndexes: UInt8Array
OpenObjectIndexes: [...Int]
Settings:
startCode: String
endcode: String
dimensions:
x: Number
y: Number
z: Number
heatedBed: Bool
nozzleDiameter: Number
filamentThickness: Number
temperature: Number
bedTemperature: Number
layerHeight: Number
combing: Bool
thickness:
top: Number
bottom: Number
shell: Number
retraction:
enabled: Bool
amount: Number
speed: Number
minDistance: Number
travel:
speed: Number
support:
enabled: Bool
minArea: Number
distanceY: Number
density: Number
margin: Number
flowRate: Number
speed: Number
innerShell:
flowRate: Number
speed: Number
outerShell:
flowRate: Number
speed: Number
innerInfill:
flowRate: Number
speed: Number
density: Number
outerInfill:
flowRate: Number
speed: Number
brim:
size: Number
flowRate: Number
speed: Number
firstLayer:
flowRate: Number
speed: Number
```
- Vertices: List of points in 3d
- Faces: Indexes refering to points in the vertices list that make a triangular surface
- ObjectIndexes: Describes of what object each face is part of (important for the generating of 2d shapes)
- OpenObjectIndexes: Determines weather a object is open or closed (important for the generating of 2d shapes)
- Settings: object containing all the settings for slicing. We go in depth in this object when it's needed
### Step 1: Creating lines
In this we take the 3d model and look at each surface to extract all individual lines. Note some lines are part of multiple surfaces. In addition we also add some additional data to each line, like the surfaces it is part of we'll also store the 2d normal.
```
function calculateNormal(vertices, a, b, c) {
a = getVertex(vertices, a);
b = getVertex(vertices, b);
c = getVertex(vertices, c);
const cb = vector3.subtract(c, b);
const ab = vector3.subtract(a, b);
const normal = vector3.normalize(vector3.cross(cb, ab));
return normal;
}
```
In order to extract all unique lines from the model we'll loop through each face of the model.
### Step 2: Calculate Layers Intersections
This is a fairly straight forward step. We take the lines and calculate on what layers that line will be intersecting. Additinally we calculate the coordinates where the line intersects each layer.
### Step 3: Intersections To Shapes
### Step 4: Shapes To Slices
### Step 5: Generate Inner Lines
### Step 6: Generate Outlines
### Step 7: Generate Infills
### Step 8: Generate Support
### Step 9: AddBrim
```
let {
brim: { size: brimSize },
nozzleDiameter
} = settings;
nozzleDiameter /= PRECISION;
brimSize /= PRECISION;
const nozzleRadius = nozzleDiameter / 2;
const [firstLayer] = slices;
const brim = firstLayer.parts.reduce((brim, { shape }) => (
brim.join(shape.offset(nozzleRadius, {
endType: shape.closed ? 'etClosedPolygon' : 'etOpenRound'
}))
), new Shape([], true)).simplify('pftNonZero');
firstLayer.brim = new Shape([], true);
for (let offset = 0; offset < brimSize; offset += nozzleDiameter) {
const brimPart = brim.offset(offset, OFFSET_OPTIONS);
firstLayer.brim = firstLayer.brim.join(brimPart);
}
```
### Step 10: Optimize Paths
### Step 11: Slices To GCode

View File

@ -1,22 +1,62 @@
# Doodle3D-Slicer
JavaScript gcode slicer, Intended to use with the Doodle3D WiFi-Box
# Usage
```javascript
import THREE from 'three.js';
import * as SLICER from 'doodle3d-slicer';
import * as THREE from 'three';
import { defaultSettings, sliceGeometry } from 'Doodle3D/Doodle3D-Slicer';
const settings = new SLICER.Settings({
...SLICER.printerSettings['ultimaker2go'],
...SLICER.userSettings
});
const settings = {
...defaultSettings.default,
...defaultSettings.material.pla,
...defaultSettings.printer.ultimaker2go,
...defaultSettings.quality.high
};
const geometry = new THREE.TorusGeometry(20, 10, 30, 30);
const geometry = new THREE.TorusGeometry(20, 10, 30, 30).clone();
const slicer = new SLICER.Slicer();
slicer.setGeometry(geometry);
slicer.slice(settings, false).then(gcode => {
document.getElementById('gcode').innerHTML = gcode.replace(/(?:\r\n|\r|\n)/g, '<br />');
});
const gcode = await sliceGeometry(settings, geometry);
```
# API
**Settings**
```javascript
import { defaultSettings } from 'Doodle3D/Doodle3D-Slicer';
const settings = {
...defaultSettings.default,
...defaultSettings.material.pla,
...defaultSettings.printer.ultimaker2go,
...defaultSettings.quality.high
};
```
Create settings object to be used by the slicer
**Slice Mesh**
```javascript
import { sliceMesh } from 'Doodle3D/Doodle3D-Slicer';
GCode: String = sliceMesh(settings: Object, mesh: THREE.Mesh, [sync: Boolean = false, onProgress: Func ])
```
Slice function that accepts Meshes
- Settings: settings object (see [settings](#settings))
- Mesh: THREE.Mesh instance that contains the geometry
- Sync: determines if the slicing progress will be sync (blocking) or async (non-blocking). A webworker is used to slice async
- onProgress: progress callback
**Slice Geometry**
```javascript
import { sliceGeometry } from 'Doodle3D/Doodle3D-Slicer';
GCode: String = sliceGeometry(settings: Object, geometry: THREE.Geometry | THREE.BufferGeometry, [matrix: THREE.Matrix, sync: Boolean = false, onProgress: Func ])
```
Slice function that accepts Geometry
- Settings: settings object (see [settings](#settings))
- Geometry: THREE.Geometry instance
- matrix: matrix that can control the scale, rotation and position of the model
- Sync: determines if the slicing progress will be sync (blocking) or async (non-blocking). A webworker is used to slice async
- onProgress: progress callback

97
comb.js Normal file
View File

@ -0,0 +1,97 @@
import comb from './src/sliceActions/helpers/comb.js';
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.width = 800;
canvas.height = 800;
const context = canvas.getContext('2d');
context.lineJoin = 'bevel';
function circle(radius = 10, x = 0, y = 0, clockWise = true, segments = 40) {
const shape = [];
for (let rad = 0; rad < Math.PI * 2; rad += Math.PI * 2 / segments) {
if (clockWise) {
shape.push({ x: Math.cos(rad) * radius + x, y: Math.sin(rad) * radius + y });
} else {
shape.push({ x: Math.cos(rad) * radius + x, y: -Math.sin(rad) * radius + y });
}
}
return shape;
}
const START = { x: 200, y: 400 };
const END = { x: 400, y: 300 };
const POLYGON = [[
{ x: 10, y: 10 },
{ x: 600, y: 10 },
{ x: 500, y: 200 },
{ x: 600, y: 600 },
{ x: 10, y: 600 }
], [
{ x: 160, y: 120 },
{ x: 120, y: 400 },
{ x: 400, y: 400 }
]];
// const POLYGON = [
// circle(300, 305, 305, true, 4),
// circle(40, 305, 105, false, 4),
// circle(40, 305, 205, false, 4),
// circle(40, 305, 305, false, 4),
// circle(40, 305, 405, false, 4),
// circle(40, 305, 505, false, 4)
// ];
canvas.onmousedown = (event) => {
START.x = event.offsetX;
START.y = event.offsetY;
compute();
};
canvas.onmousemove = (event) => {
END.x = event.offsetX;
END.y = event.offsetY;
compute();
};
compute();
function compute() {
const path = comb(POLYGON, START, END);
// draw
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
for (const shape of POLYGON) {
let first = true;
for (const { x, y } of shape) {
if (first) {
context.moveTo(x, y);
} else {
context.lineTo(x, y);
}
first = false;
}
}
context.closePath();
context.fillStyle = 'lightgray';
context.fill();
context.beginPath();
for (const { x, y } of path) {
context.lineTo(x, y);
}
context.lineWidth = 2;
context.stroke();
context.beginPath();
context.arc(START.x, START.y, 3, 0, Math.PI * 2);
context.fillStyle = 'blue';
context.fill();
context.beginPath();
context.arc(END.x, END.y, 3, 0, Math.PI * 2);
context.fillStyle = 'red';
context.fill();
}

BIN
data/bunny.stl Normal file

Binary file not shown.

View File

@ -1,128 +0,0 @@
import React from 'react';
import { PRECISION } from '../src/constants.js';
export default class SlicerViewer extends React.Component {
state = {
layer: 0,
render: {
renderIntersectionPoints: false,
renderShape1: false,
renderShape2: true,
renderOuterLine: true,
renderInnerLine: true,
renderFill: true
}
};
changeSlider = (event) => {
this.setState({
layer: parseInt(event.target.value)
});
};
onControl = (event) => {
const section = event.target.value;
this.setState({
render: {
...this.state.render,
[section]: !this.state.render[section]
}
});
};
render() {
const { layer, render } = this.state;
const { layerIntersectionPoints, settings, layerShapes, slices } = this.props;
const numLayers = settings.dimensionsZ / settings.layerHeight;
const intersectionPoints = layerIntersectionPoints[layer + 1];
const shape = layerShapes[layer];
const slice = slices[layer];
return (
<div>
<svg viewBox={`-20 -20 ${settings.dimensionsX + 40} ${settings.dimensionsX + 40}`}>
<rect
width={settings.dimensionsX}
height={settings.dimensionsY}
fill="lightGrey"
/>
{render.renderIntersectionPoints && intersectionPoints.map(({ x, y }, i) => (
<circle key={i} cx={x} cy={y} r="0.3"/>
))}
{render.renderShape1 && shape && shape.closedShapes.map((closedShape, i) => (
<polygon
key={i}
points={closedShape.map(({ x, y }) => `${x} ${y}`).join(' ')}
fill="rgba(255, 0, 0, 0.5)"
/>
))}
{slice && slice.parts.map((slicePart, i) => (
<g key={i}>
{render.renderShape2 && <ClipperShapeSVG
shape={slicePart.shape}
scale={PRECISION}
color="rgba(0, 0, 0, 0.5)"
fill
/>}
{render.renderOuterLine && <ClipperShapeSVG
shape={slicePart.outerLine}
scale={1.0}
color="blue"
strokeWidth={settings.nozzleDiameter * 0.9}
/>}
{render.renderInnerLine && slicePart.innerLines.map((innerLine, i) => (
<ClipperShapeSVG
key={i}
shape={innerLine}
scale={1.0}
color="red"
strokeWidth={settings.nozzleDiameter * 0.9}
/>
))}
{render.renderFill && <ClipperShapeSVG
shape={slicePart.fill}
scale={1.0}
color="yellow"
strokeWidth={settings.nozzleDiameter * 0.9}
/>}
</g>
))}
</svg>
<div id="controls">
<input onChange={this.changeSlider} value={layer} type="range" min="0" max={numLayers} />
<p>Layer: {layer}</p>
<p><label><input type="checkbox" value="renderIntersectionPoints" onChange={this.onControl} checked={render.renderIntersectionPoints} />Render Intersection Points</label></p>
<p><label><input type="checkbox" value="renderShape1" onChange={this.onControl} checked={render.renderShape1} />Render Shape 1</label></p>
<p><label><input type="checkbox" value="renderShape2" onChange={this.onControl} checked={render.renderShape2} />Render Shape 2</label></p>
<p><label><input type="checkbox" value="renderOuterLine" onChange={this.onControl} checked={render.renderOuterLine} />Render Out Line</label></p>
<p><label><input type="checkbox" value="renderInnerLine" onChange={this.onControl} checked={render.renderInnerLine} />Render Inner Lines</label></p>
<p><label><input type="checkbox" value="renderFill" onChange={this.onControl} checked={render.renderFill} />Render Fill</label></p>
</div>
</div>
);
}
}
class ClipperShapeSVG extends React.Component {
render() {
const { shape, color = 'black', strokeWidth, scale, fill } = this.props;
const data = shape.paths.map(path => {
const pathData = path.map(({ X, Y }, i) => `${i === 0 ? 'M' : 'L '}${X * scale} ${Y * scale}`);
if (shape.closed) pathData.push('Z');
return pathData.join(' ');
}).join(' ');
return (
<path
d={data}
strokeWidth={typeof strokeWidth === 'number' ? strokeWidth : 1.0}
vectorEffect={typeof strokeWidth === 'number' ? 'none' : 'non-scaling-stroke'}
fill={fill ? color : 'none'}
stroke={!fill ? color : 'none'}
/>
);
}
}

View File

@ -1,54 +0,0 @@
import calculateLayersIntersections from 'src/sliceActions/calculateLayersIntersections.js';
import createLines from 'src/sliceActions/createLines.js';
import generateInfills from 'src/sliceActions/generateInfills.js';
import generateInnerLines from 'src/sliceActions/generateInnerLines.js';
import generateSupport from 'src/sliceActions/generateSupport.js';
import intersectionsToShapes from 'src/sliceActions/intersectionsToShapes.js';
import addBrim from 'src/sliceActions/addBrim.js';
import optimizePaths from 'src/sliceActions/optimizePaths.js';
import shapesToSlices from 'src/sliceActions/shapesToSlices.js';
import slicesToGCode from 'src/sliceActions/slicesToGCode.js';
import applyPrecision from 'src/sliceActions/applyPrecision.js';
import removePrecision from 'src/sliceActions/removePrecision.js';
export default function generateRawData(geometry, settings) {
const rawData = {};
const lines = createLines(geometry, settings);
const {
layerIntersectionIndexes,
layerIntersectionPoints
} = calculateLayersIntersections(lines, settings);
rawData.layerIntersectionPoints = layerIntersectionPoints
.map(intersectionPoints => intersectionPoints.map(intersectionPoint => intersectionPoint.clone()));
const layerShapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
rawData.layerShapes = layerShapes
.map(({ closedShapes, openShapes }) => ({
closedShapes: closedShapes.map(closedShape => closedShape.map(vector => vector.clone())),
openShapes: openShapes.map(openShape => openShape.map(vector => vector.clone()))
}));
applyPrecision(layerShapes);
const slices = shapesToSlices(layerShapes, settings);
generateInnerLines(slices, settings);
generateInfills(slices, settings);
generateSupport(slices, settings);
addBrim(slices, settings);
optimizePaths(slices, settings);
removePrecision(slices);
rawData.slices = slices;
const gcode = slicesToGCode(slices, settings);
rawData.gcode = gcode;
return rawData;
}

View File

@ -1,11 +0,0 @@
<!DOCTYPE>
<html>
<head>
<title>Doodle3D Slicer</title>
</head>
<body>
<p><a href="./viewer.html">Viewer</a></p>
<p><a href="./save.html">Save</a></p>
</body>
</html>

View File

@ -1,21 +0,0 @@
* {
margin: 0;
padding: 0;
}
#container {
position: relative;
}
#container, svg {
width: 100%;
height: 100%;
}
svg, #controls {
position: absolute;
}
input[type=range] {
width: 100%;
}

View File

@ -1,19 +0,0 @@
<!DOCTYPE>
<html>
<head>
<title>Doodle3D Slicer - Save</title>
<script type="text/javascript" src="../jspm_packages/system.js"></script>
<script type="text/javascript" src="../jspm.config.js"></script>
<script type="text/javascript">
System.import('example/save.js');
</script>
</head>
<body>
</body>
</html>

View File

@ -1,25 +0,0 @@
import 'three.js';
import 'three.js/loaders/STLLoader';
import { Settings, printerSettings, userSettings, Slicer } from 'src/index.js';
import { saveAs } from 'file-saver';
const settings = new Settings({
...printerSettings['ultimaker2go'],
...userSettings
});
const stlLoader = new THREE.STLLoader();
stlLoader.load('stl/traktor.stl', async (geometry) => {
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.1, 50)));
geometry.mergeVertices();
geometry.computeFaceNormals();
const slicer = new Slicer().setGeometry(geometry);
const gcode = await slicer.slice(settings);
const file = new File([gcode], 'traktor.gcode', { type: 'text/plain' });
saveAs(file);
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
<!DOCTYPE>
<html>
<head>
<title>Doodle3D Slicer - Viewer</title>
<style>
#gcode {
font-family: monospace;
}
</style>
<script type="text/javascript" src="../jspm_packages/system.js"></script>
<script type="text/javascript" src="../jspm.config.js"></script>
<link href="main.css" rel="stylesheet"/>
<script type="text/javascript">
System.import('example/viewer.js');
</script>
</head>
<body>
<div id="container"></div>
</body>
</html>

View File

@ -1,35 +0,0 @@
import * as THREE from 'three.js';
import stlLoader from 'three.js/loaders/STLLoader';
import React from 'react';
import ReactDOM, { render } from 'react-dom';
import * as SLICER from 'src/index.js';
import generateRawData from './generateRawData.js';
import SlicerViewer from './SlicerViewer.js';
const settings = new SLICER.Settings({
...SLICER.printerSettings['ultimaker2go'],
...SLICER.userSettings
});
const stlLoader = new THREE.STLLoader();
stlLoader.load('stl/Airplane.stl', (geometry) => {
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
geometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI / -2));
geometry.applyMatrix(new THREE.Matrix4().setPosition(new THREE.Vector3(50, -0.1, 50)));
// geometry.applyMatrix(new THREE.Matrix4().scale(0.8));
geometry.mergeVertices();
geometry.computeFaceNormals();
const rawData = generateRawData(geometry, settings);
render(
<SlicerViewer
layerIntersectionPoints={rawData.layerIntersectionPoints}
layerShapes={rawData.layerShapes}
slices={rawData.slices}
settings={settings.config}
/>,
document.getElementById('container')
);
});

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

47
index.js Normal file
View File

@ -0,0 +1,47 @@
import 'babel-polyfill';
import React from 'react';
import { Interface } from './src/index.js';
import { render } from 'react-dom';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import jss from 'jss';
import preset from 'jss-preset-default';
import normalize from 'normalize-jss';
import queryString from 'query-string';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { grey400, blue500, blue700 } from 'material-ui/styles/colors';
import bunny_url from './data/bunny.stl';
import * as THREE from 'three';
import 'three/examples/js/loaders/STLLoader.js';
import fileSaver from 'file-saver';
const muiTheme = getMuiTheme({
palette: {
primary1Color: blue500,
primary2Color: blue700,
accent1Color: blue500,
}
});
jss.setup(preset());
jss.createStyleSheet(normalize).attach();
jss.createStyleSheet({
'@global': {
'*': { margin: 0, padding: 0 },
'#app, body, html': { height: '100%', fontFamily: 'sans-serif' },
body: { overflow: 'auto' },
html: { overflow: 'hidden' }
}
}).attach();
new THREE.STLLoader().load(bunny_url, geometry => {
const material = new THREE.MeshPhongMaterial({ color: 0xff5533, specular: 0x111111, shininess: 200 });
const mesh = new THREE.Mesh(geometry, material);
render((
<MuiThemeProvider muiTheme={muiTheme}>
<Interface
mesh={mesh}
onSliceSucces={({ gcode }) => fileSaver.saveAs(gcode, 'bunny.gcode')}
/>
</MuiThemeProvider>
), document.getElementById('app'));
});

View File

@ -1,438 +0,0 @@
SystemJS.config({
paths: {
"github:": "jspm_packages/github/",
"npm:": "jspm_packages/npm/",
"example/": "example/",
"slicer/": "src/"
},
browserConfig: {
"baseURL": "/"
},
devConfig: {
"map": {
"babel-runtime": "npm:babel-runtime@5.8.38",
"core-js": "npm:core-js@1.2.7",
"plugin-babel": "npm:systemjs-plugin-babel@0.0.12",
"react": "npm:react@15.3.2",
"domain": "npm:jspm-nodelibs-domain@0.2.0",
"zlib": "npm:jspm-nodelibs-zlib@0.2.0",
"https": "npm:jspm-nodelibs-https@0.2.0",
"react-dom": "npm:react-dom@15.3.2",
"babel-plugin-transform-react-jsx": "npm:babel-plugin-transform-react-jsx@6.8.0",
"three.js/loaders/STLLoader": "github:mrdoob/three.js@r83/examples/js/loaders/STLLoader.js",
"file-saver": "npm:file-saver@1.3.3"
},
"packages": {
"npm:babel-runtime@5.8.38": {
"map": {}
},
"npm:react@15.3.2": {
"map": {
"object-assign": "npm:object-assign@4.1.0",
"loose-envify": "npm:loose-envify@1.2.0",
"fbjs": "npm:fbjs@0.8.5"
}
},
"npm:fbjs@0.8.5": {
"map": {
"loose-envify": "npm:loose-envify@1.2.0",
"object-assign": "npm:object-assign@4.1.0",
"promise": "npm:promise@7.1.1",
"isomorphic-fetch": "npm:isomorphic-fetch@2.2.1",
"ua-parser-js": "npm:ua-parser-js@0.7.10",
"immutable": "npm:immutable@3.8.1",
"core-js": "npm:core-js@1.2.7"
}
},
"npm:loose-envify@1.2.0": {
"map": {
"js-tokens": "npm:js-tokens@1.0.3"
}
},
"npm:promise@7.1.1": {
"map": {
"asap": "npm:asap@2.0.5"
}
},
"npm:isomorphic-fetch@2.2.1": {
"map": {
"whatwg-fetch": "npm:whatwg-fetch@1.0.0",
"node-fetch": "npm:node-fetch@1.6.3"
}
},
"npm:node-fetch@1.6.3": {
"map": {
"encoding": "npm:encoding@0.1.12",
"is-stream": "npm:is-stream@1.1.0"
}
},
"npm:encoding@0.1.12": {
"map": {
"iconv-lite": "npm:iconv-lite@0.4.13"
}
},
"npm:browserify-zlib@0.1.4": {
"map": {
"readable-stream": "npm:readable-stream@2.1.5",
"pako": "npm:pako@0.2.9"
}
},
"npm:babel-plugin-transform-react-jsx@6.8.0": {
"map": {
"babel-helper-builder-react-jsx": "npm:babel-helper-builder-react-jsx@6.9.0",
"babel-plugin-syntax-jsx": "npm:babel-plugin-syntax-jsx@6.13.0",
"babel-runtime": "npm:babel-runtime@6.11.6"
}
},
"npm:babel-helper-builder-react-jsx@6.9.0": {
"map": {
"babel-runtime": "npm:babel-runtime@6.11.6",
"esutils": "npm:esutils@2.0.2",
"babel-types": "npm:babel-types@6.16.0",
"lodash": "npm:lodash@4.16.4"
}
},
"npm:babel-runtime@6.11.6": {
"map": {
"core-js": "npm:core-js@2.4.1",
"regenerator-runtime": "npm:regenerator-runtime@0.9.5"
}
},
"npm:babel-types@6.16.0": {
"map": {
"lodash": "npm:lodash@4.16.4",
"babel-runtime": "npm:babel-runtime@6.11.6",
"esutils": "npm:esutils@2.0.2",
"to-fast-properties": "npm:to-fast-properties@1.0.2"
}
},
"npm:jspm-nodelibs-zlib@0.2.0": {
"map": {
"zlib-browserify": "npm:browserify-zlib@0.1.4"
}
},
"npm:jspm-nodelibs-domain@0.2.0": {
"map": {
"domain-browserify": "npm:domain-browser@1.1.7"
}
}
}
},
transpiler: "plugin-babel",
packages: {
"slicer": {
"main": "index.js"
},
"example": {
"main": "example/index.js",
"format": "esm",
"meta": {
"*.js": {
"loader": "plugin-babel",
"babelOptions": {
"stage1": true,
"plugins": [
"babel-plugin-transform-react-jsx"
]
}
}
}
}
},
meta: {
"three.js/loaders/STLLoader": {
"deps": [
"three.js"
]
}
},
map: {
"babel": "npm:babel-core@5.8.38",
"three.js/loaders/STLLoader": "github:mrdoob/three.js@r75/examples/js/loaders/STLLoader.js"
}
});
SystemJS.config({
packageConfigPaths: [
"npm:@*/*.json",
"npm:*.json",
"github:*/*.json"
],
map: {
"js-yaml": "npm:js-yaml@3.9.0",
"clipper-js": "github:Doodle3D/clipper-js@1.0.2",
"three.js": "github:mrdoob/three.js@r83",
"assert": "npm:jspm-nodelibs-assert@0.2.0",
"buffer": "npm:jspm-nodelibs-buffer@0.2.0",
"child_process": "npm:jspm-nodelibs-child_process@0.2.0",
"constants": "npm:jspm-nodelibs-constants@0.2.0",
"crypto": "npm:jspm-nodelibs-crypto@0.2.0",
"events": "npm:jspm-nodelibs-events@0.2.0",
"fs": "npm:jspm-nodelibs-fs@0.2.0",
"http": "npm:jspm-nodelibs-http@0.2.0",
"json": "github:systemjs/plugin-json@0.1.2",
"Doodle3D/clipper-js": "github:Doodle3D/clipper-js@master",
"module": "npm:jspm-nodelibs-module@0.2.0",
"os": "npm:jspm-nodelibs-os@0.2.0",
"path": "npm:jspm-nodelibs-path@0.2.0",
"process": "npm:jspm-nodelibs-process@0.2.0",
"stream": "npm:jspm-nodelibs-stream@0.2.0",
"string_decoder": "npm:jspm-nodelibs-string_decoder@0.2.0",
"tty": "npm:jspm-nodelibs-tty@0.2.0",
"url": "npm:jspm-nodelibs-url@0.2.0",
"util": "npm:jspm-nodelibs-util@0.2.0",
"vm": "npm:jspm-nodelibs-vm@0.2.0",
"worker": "github:casperlamboo/plugin-worker@master"
},
packages: {
"github:Doodle3D/clipper-js@master": {
"map": {
"clipper-lib": "npm:clipper-lib@1.0.0"
}
},
"npm:clipper-lib@1.0.0": {
"map": {}
},
"npm:stream-browserify@2.0.1": {
"map": {
"inherits": "npm:inherits@2.0.3",
"readable-stream": "npm:readable-stream@2.1.5"
}
},
"npm:buffer@4.9.1": {
"map": {
"base64-js": "npm:base64-js@1.2.0",
"isarray": "npm:isarray@1.0.0",
"ieee754": "npm:ieee754@1.1.8"
}
},
"npm:url@0.11.0": {
"map": {
"querystring": "npm:querystring@0.2.0",
"punycode": "npm:punycode@1.3.2"
}
},
"npm:readable-stream@2.1.5": {
"map": {
"string_decoder": "npm:string_decoder@0.10.31",
"inherits": "npm:inherits@2.0.3",
"isarray": "npm:isarray@1.0.0",
"buffer-shims": "npm:buffer-shims@1.0.0",
"core-util-is": "npm:core-util-is@1.0.2",
"process-nextick-args": "npm:process-nextick-args@1.0.7",
"util-deprecate": "npm:util-deprecate@1.0.2"
}
},
"npm:crypto-browserify@3.11.0": {
"map": {
"inherits": "npm:inherits@2.0.3",
"browserify-cipher": "npm:browserify-cipher@1.0.0",
"create-hash": "npm:create-hash@1.1.2",
"browserify-sign": "npm:browserify-sign@4.0.0",
"pbkdf2": "npm:pbkdf2@3.0.9",
"public-encrypt": "npm:public-encrypt@4.0.0",
"randombytes": "npm:randombytes@2.0.3",
"diffie-hellman": "npm:diffie-hellman@5.0.2",
"create-ecdh": "npm:create-ecdh@4.0.0",
"create-hmac": "npm:create-hmac@1.1.4"
}
},
"npm:browserify-sign@4.0.0": {
"map": {
"create-hash": "npm:create-hash@1.1.2",
"inherits": "npm:inherits@2.0.3",
"create-hmac": "npm:create-hmac@1.1.4",
"parse-asn1": "npm:parse-asn1@5.0.0",
"elliptic": "npm:elliptic@6.3.2",
"bn.js": "npm:bn.js@4.11.6",
"browserify-rsa": "npm:browserify-rsa@4.0.1"
}
},
"npm:create-hash@1.1.2": {
"map": {
"inherits": "npm:inherits@2.0.3",
"ripemd160": "npm:ripemd160@1.0.1",
"cipher-base": "npm:cipher-base@1.0.3",
"sha.js": "npm:sha.js@2.4.5"
}
},
"npm:public-encrypt@4.0.0": {
"map": {
"randombytes": "npm:randombytes@2.0.3",
"create-hash": "npm:create-hash@1.1.2",
"parse-asn1": "npm:parse-asn1@5.0.0",
"bn.js": "npm:bn.js@4.11.6",
"browserify-rsa": "npm:browserify-rsa@4.0.1"
}
},
"npm:diffie-hellman@5.0.2": {
"map": {
"randombytes": "npm:randombytes@2.0.3",
"bn.js": "npm:bn.js@4.11.6",
"miller-rabin": "npm:miller-rabin@4.0.0"
}
},
"npm:create-hmac@1.1.4": {
"map": {
"create-hash": "npm:create-hash@1.1.2",
"inherits": "npm:inherits@2.0.3"
}
},
"npm:browserify-cipher@1.0.0": {
"map": {
"browserify-des": "npm:browserify-des@1.0.0",
"evp_bytestokey": "npm:evp_bytestokey@1.0.0",
"browserify-aes": "npm:browserify-aes@1.0.6"
}
},
"npm:create-ecdh@4.0.0": {
"map": {
"elliptic": "npm:elliptic@6.3.2",
"bn.js": "npm:bn.js@4.11.6"
}
},
"npm:parse-asn1@5.0.0": {
"map": {
"create-hash": "npm:create-hash@1.1.2",
"pbkdf2": "npm:pbkdf2@3.0.9",
"browserify-aes": "npm:browserify-aes@1.0.6",
"evp_bytestokey": "npm:evp_bytestokey@1.0.0",
"asn1.js": "npm:asn1.js@4.8.1"
}
},
"npm:browserify-des@1.0.0": {
"map": {
"inherits": "npm:inherits@2.0.3",
"cipher-base": "npm:cipher-base@1.0.3",
"des.js": "npm:des.js@1.0.0"
}
},
"npm:evp_bytestokey@1.0.0": {
"map": {
"create-hash": "npm:create-hash@1.1.2"
}
},
"npm:browserify-aes@1.0.6": {
"map": {
"create-hash": "npm:create-hash@1.1.2",
"evp_bytestokey": "npm:evp_bytestokey@1.0.0",
"inherits": "npm:inherits@2.0.3",
"cipher-base": "npm:cipher-base@1.0.3",
"buffer-xor": "npm:buffer-xor@1.0.3"
}
},
"npm:sha.js@2.4.5": {
"map": {
"inherits": "npm:inherits@2.0.3"
}
},
"npm:browserify-rsa@4.0.1": {
"map": {
"bn.js": "npm:bn.js@4.11.6",
"randombytes": "npm:randombytes@2.0.3"
}
},
"npm:miller-rabin@4.0.0": {
"map": {
"bn.js": "npm:bn.js@4.11.6",
"brorand": "npm:brorand@1.0.6"
}
},
"npm:des.js@1.0.0": {
"map": {
"inherits": "npm:inherits@2.0.3",
"minimalistic-assert": "npm:minimalistic-assert@1.0.0"
}
},
"npm:hash.js@1.0.3": {
"map": {
"inherits": "npm:inherits@2.0.3"
}
},
"npm:pbkdf2@3.0.9": {
"map": {
"create-hmac": "npm:create-hmac@1.1.4"
}
},
"npm:elliptic@6.3.2": {
"map": {
"bn.js": "npm:bn.js@4.11.6",
"inherits": "npm:inherits@2.0.3",
"brorand": "npm:brorand@1.0.6",
"hash.js": "npm:hash.js@1.0.3"
}
},
"npm:cipher-base@1.0.3": {
"map": {
"inherits": "npm:inherits@2.0.3"
}
},
"npm:asn1.js@4.8.1": {
"map": {
"bn.js": "npm:bn.js@4.11.6",
"inherits": "npm:inherits@2.0.3",
"minimalistic-assert": "npm:minimalistic-assert@1.0.0"
}
},
"npm:stream-http@2.4.0": {
"map": {
"inherits": "npm:inherits@2.0.3",
"readable-stream": "npm:readable-stream@2.1.5",
"to-arraybuffer": "npm:to-arraybuffer@1.0.1",
"builtin-status-codes": "npm:builtin-status-codes@2.0.0",
"xtend": "npm:xtend@4.0.1"
}
},
"npm:jspm-nodelibs-crypto@0.2.0": {
"map": {
"crypto-browserify": "npm:crypto-browserify@3.11.0"
}
},
"npm:jspm-nodelibs-http@0.2.0": {
"map": {
"http-browserify": "npm:stream-http@2.4.0"
}
},
"npm:jspm-nodelibs-os@0.2.0": {
"map": {
"os-browserify": "npm:os-browserify@0.2.1"
}
},
"npm:jspm-nodelibs-buffer@0.2.0": {
"map": {
"buffer-browserify": "npm:buffer@4.9.1"
}
},
"npm:jspm-nodelibs-stream@0.2.0": {
"map": {
"stream-browserify": "npm:stream-browserify@2.0.1"
}
},
"npm:jspm-nodelibs-string_decoder@0.2.0": {
"map": {
"string_decoder-browserify": "npm:string_decoder@0.10.31"
}
},
"npm:jspm-nodelibs-url@0.2.0": {
"map": {
"url-browserify": "npm:url@0.11.0"
}
},
"github:Doodle3D/clipper-js@1.0.2": {
"map": {
"Breush/clipper-lib": "github:Breush/clipper-lib@patch-1"
}
},
"npm:js-yaml@3.9.0": {
"map": {
"argparse": "npm:argparse@1.0.9",
"esprima": "npm:esprima@4.0.0"
}
},
"npm:argparse@1.0.9": {
"map": {
"sprintf-js": "npm:sprintf-js@1.0.3"
}
}
}
});

1
models/Doodle.d3sketch Normal file
View File

@ -0,0 +1 @@
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":9.266873708001008,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,26.586102719033242,0,1,-4.229607250755304]},\"z\":10.733126291998994,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":false,\"star\":{\"rays\":5,\"innerRadius\":20.54380664652568,\"outerRadius\":40.48338368580059},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-12.688821752265852,0,1,-12.68882175226588]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":20.54380664652568,\"outerRadius\":40.48338368580059},\"color\":6873597,\"type\":\"STAR\"}]}]}","appVersion":"0.17.4"}

1
models/Doodle.json Normal file
View File

@ -0,0 +1 @@
{"vertices":[47.12482452392578,42.35624313354492,-2.593571992041396e-15,-7.293127536773682,29.7335205078125,-1.820653115308456e-15,-4.207573413848877,-19.915847778320312,1.219494014914264e-15,54.97896194458008,-14.586255073547363,8.931505683412107e-16,53.85694122314453,-42.07573699951172,2.576395944332666e-15,-35.624122619628906,-25.245441436767578,1.5458375198830296e-15,-38.99018096923828,51.6129035949707,-3.1603789970181283e-15,47.12482452392578,61.71107864379883,-3.7787139115382e-15,47.12482452392578,42.35624313354492,19.999999999999996,-7.293127536773682,29.7335205078125,19.999999999999996,-4.207573413848877,-19.915847778320312,20,54.97896194458008,-14.586255073547362,20,53.85694122314453,-42.07573699951172,20.000000000000004,-35.624122619628906,-25.245441436767578,20,-38.99018096923828,51.6129035949707,19.999999999999996,47.12482452392578,61.71107864379883,19.999999999999996],"normals":[0,-6.123234262925839e-17,-1,0,6.123234262925839e-17,1,0.22595957126202507,-0.9741366804278966,5.964867098368936e-17,0.22595959003447755,-0.9741366760734609,5.964867071705706e-17,0.9980744575480833,0.062027229424660574,-3.798072564474434e-18,0.9980744575147102,0.06202722996166134,-3.798072597356249e-18,-0.08968451372047671,0.995970224453885,-6.098559003229967e-17,-0.0896845139633491,0.9959702244320148,-6.098559003096051e-17,0.9991680515439634,-0.04078240765133586,2.4972023585526864e-18,0.9991680513997995,-0.040782411183346925,2.4972025748259983e-18,-0.18484654771165182,-0.9827673955718536,6.017714989051966e-17,-0.1848465343760269,-0.9827673980801217,6.017715004410679e-17,-0.9990423495327988,-0.04375367230285246,2.679139853736555e-18,-0.9990423494957035,-0.04375367314986121,2.6791399056008846e-18,-0.11646581097902166,0.993194701391927,-6.081563825319445e-17,-0.11646581460851489,0.9931947009663187,-6.081563822713346e-17,1,0,0],"faces":[50,7,0,1,0,0,0,0,0,50,2,3,4,0,0,0,0,0,50,6,7,1,0,0,0,0,0,50,2,4,5,0,0,0,0,0,50,5,6,1,0,0,0,0,0,50,1,2,5,0,0,0,0,0,50,13,10,9,0,1,1,1,1,50,9,14,13,0,1,1,1,1,50,13,12,10,0,1,1,1,1,50,9,15,14,0,1,1,1,1,50,12,11,10,0,1,1,1,1,50,9,8,15,0,1,1,1,1,50,0,8,1,0,2,3,3,3,50,8,9,1,0,2,3,3,3,50,1,9,2,0,4,5,5,5,50,9,10,2,0,4,5,5,5,50,2,10,3,0,6,7,7,7,50,10,11,3,0,6,7,7,7,50,3,11,4,0,8,9,9,9,50,11,12,4,0,8,9,9,9,50,4,12,5,0,10,11,11,11,50,12,13,5,0,10,11,11,11,50,5,13,6,0,12,13,13,13,50,13,14,6,0,12,13,13,13,50,6,14,7,0,14,15,15,15,50,14,15,7,0,14,15,15,15,50,7,15,0,0,16,16,16,16,50,15,8,0,0,16,16,16,16]}

1
models/Doodle_2.d3sketch Normal file
View File

@ -0,0 +1 @@
{"data":"{\"spaces\":[{\"matrix\":{\"metadata\":{\"type\":\"Matrix4\",\"library\":\"three.js\"},\"elements\":[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},\"objects\":[{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-32.27848101265822,0,1,5.3797468354430436]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":false,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":10,\"outerRadius\":25},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,47.784810126582286,0,1,0.6329113924050631]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":true,\"star\":{\"rays\":5,\"innerRadius\":22.468354430379748,\"outerRadius\":25.9493670886076},\"color\":6873597,\"type\":\"STAR\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-46.83544303797467,0,1,9.810126582278485]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":false,\"solid\":false,\"rectSize\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Vector\"},\"x\":120.8860759493671,\"y\":34.49367088607595},\"color\":6873597,\"type\":\"RECT\"},{\"height\":20,\"transform\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Matrix\"},\"matrix\":[1,0,-47.1518987341772,0,1,-37.341772151898724]},\"z\":0,\"sculpt\":[{\"pos\":0,\"scale\":1},{\"pos\":1,\"scale\":1}],\"twist\":0,\"fill\":true,\"solid\":false,\"rectSize\":{\"metadata\":{\"library\":\"CAL\",\"type\":\"Vector\"},\"x\":120.8860759493671,\"y\":34.49367088607595},\"color\":6873597,\"type\":\"RECT\"}]}]}","appVersion":"0.17.4"}

1
models/combingtest.json Normal file

File diff suppressed because one or more lines are too long

1
models/error.json Normal file

File diff suppressed because one or more lines are too long

1
models/shape.json Normal file

File diff suppressed because one or more lines are too long

1
models/test.json Normal file

File diff suppressed because one or more lines are too long

21158
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,87 +1,81 @@
{
"jspm": {
"name": "slicer",
"main": "index.js",
"directories": {
"lib": "src"
},
"dependencies": {
"Doodle3D/clipper-js": "github:Doodle3D/clipper-js@master",
"clipper-js": "github:Doodle3D/clipper-js@1.0.2",
"js-yaml": "npm:js-yaml@^3.9.0",
"json": "github:systemjs/plugin-json@^0.1.2",
"three.js": "github:mrdoob/three.js@r83",
"worker": "github:casperlamboo/plugin-worker@master"
},
"devDependencies": {
"babel-plugin-transform-react-jsx": "npm:babel-plugin-transform-react-jsx@^6.8.0",
"babel-runtime": "npm:babel-runtime@^5.1.13",
"core-js": "npm:core-js@^1.2.0",
"domain": "npm:jspm-nodelibs-domain@^0.2.0",
"https": "npm:jspm-nodelibs-https@^0.2.0",
"file-saver": "npm:file-saver@^1.3.3",
"plugin-babel": "npm:systemjs-plugin-babel@^0.0.12",
"react": "npm:react@^15.3.2",
"react-dom": "npm:react-dom@^15.3.2",
"zlib": "npm:jspm-nodelibs-zlib@^0.2.0"
},
"peerDependencies": {
"assert": "npm:jspm-nodelibs-assert@^0.2.0",
"buffer": "npm:jspm-nodelibs-buffer@^0.2.0",
"child_process": "npm:jspm-nodelibs-child_process@^0.2.0",
"constants": "npm:jspm-nodelibs-constants@^0.2.0",
"crypto": "npm:jspm-nodelibs-crypto@^0.2.0",
"events": "npm:jspm-nodelibs-events@^0.2.0",
"fs": "npm:jspm-nodelibs-fs@^0.2.0",
"http": "npm:jspm-nodelibs-http@^0.2.0",
"module": "npm:jspm-nodelibs-module@^0.2.0",
"os": "npm:jspm-nodelibs-os@^0.2.0",
"path": "npm:jspm-nodelibs-path@^0.2.0",
"process": "npm:jspm-nodelibs-process@^0.2.0",
"stream": "npm:jspm-nodelibs-stream@^0.2.0",
"string_decoder": "npm:jspm-nodelibs-string_decoder@^0.2.0",
"tty": "npm:jspm-nodelibs-tty@^0.2.0",
"url": "npm:jspm-nodelibs-url@^0.2.0",
"util": "npm:jspm-nodelibs-util@^0.2.0",
"vm": "npm:jspm-nodelibs-vm@^0.2.0"
},
"overrides": {
"github:mrdoob/three.js@r83": {
"format": "global"
},
"npm:babel-runtime@5.8.38": {
"main": false,
"dependencies": {},
"optionalDependencies": {
"core-js": "^1.2.0"
}
},
"npm:browserify-zlib@0.1.4": {
"dependencies": {
"readable-stream": "^2.0.2",
"pako": "~0.2.0"
},
"map": {
"_stream_transform": "readable-stream/transform"
}
},
"npm:inherits@2.0.3": {
"ignore": [
"test.js"
]
},
"npm:lodash@4.16.4": {
"map": {
"buffer": "@empty",
"process": "@empty"
}
}
}
"name": "@doodle3d/doodle3d-slicer",
"version": "0.0.18",
"description": "JavaScript gcode slicer for Doodle3D Transform",
"main": "lib/index.js",
"module": "module/index.js",
"esnext": "src/index.js",
"scripts": {
"start": "webpack-dev-server -w",
"dist": "NODE_ENV=production webpack -p",
"lint": "eslint src",
"prepare": "npm run build",
"upload": "npm run dist && scp -r dist/* doodle3d.com:/domains/doodle3d.com/print",
"analyze": "NODE_ENV=production ANALYZE_BUNDLE=true webpack -p",
"build": "npm run build:main && npm run build:main:settings && npm run build:module && npm run build:module:settings ",
"build:main": "BABEL_ENV=main babel src -s -d lib",
"build:module": "BABEL_ENV=module babel src -s -d module",
"build:main:settings": "cp -r src/settings lib",
"build:module:settings": "cp -r src/settings module"
},
"dependencies": {
"js-yaml": "^3.9.0"
"@doodle3d/clipper-js": "^1.0.10",
"lodash": "^4.17.4",
"material-ui": "^0.19.4",
"material-ui-icons": "^1.0.0-beta.17",
"material-ui-textfield-icon": "^0.2.2-1",
"proptypes": "^1.1.0",
"react": "^16.0.0",
"react-addons-update": "^15.6.2",
"react-dom": "^16.0.0",
"react-jss": "^7.2.0",
"react-resize-detector": "^1.1.0",
"shortid": "^2.2.8",
"three": "^0.88.0",
"validate-ip": "^1.0.1"
},
"devDependencies": {
"jspm": "^0.17.0-beta.28"
}
"file-saver": "^1.3.3",
"babel-cli": "6.24.1",
"babel-eslint": "^5.0.4",
"babel-loader": "7.0.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"eslint": "^1.10.3",
"eslint-config-airbnb": "^3.1.0",
"eslint-plugin-react": "^3.16.1",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^2.29.0",
"html-webpack-template": "^6.0.2",
"query-string": "^5.0.1",
"image-webpack-loader": "^4.2.0",
"imports-loader": "^0.7.1",
"material-ui": "^0.19.4",
"normalize-jss": "^4.0.0",
"raw-loader": "^0.5.1",
"webpack": "^3.8.1",
"webpack-bundle-analyzer": "^2.9.2",
"webpack-dev-server": "^2.5.1",
"worker-loader": "^0.8.1",
"yml-loader": "^2.1.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Doodle3D/Doodle3D-Slicer.git"
},
"author": "Casper @Doodle3D",
"license": "MIT",
"private": false,
"bugs": {
"url": "https://github.com/Doodle3D/Doodle3D-Slicer/issues"
},
"homepage": "https://github.com/Doodle3D/Doodle3D-Slicer#readme"
}

View File

@ -1,182 +0,0 @@
import * as THREE from 'three.js';
const MOVE = 'G';
const M_COMMAND = 'M';
const FAN_SPEED = 'S';
const SPEED = 'F';
const EXTRUDER = 'E';
const POSITION_X = 'X';
const POSITION_Y = 'Y';
const POSITION_Z = 'Z';
export default class {
constructor(settings) {
this._gcode = '';
this._currentValues = {};
this._settings = settings;
this._nozzlePosition = new THREE.Vector2(0, 0);
this._extruder = 0.0;
this._isRetracted = false;
this._isFanOn = false;
this.bottom = true;
}
_addGCode(command) {
let str = '';
let first = true;
for (const action in command) {
const value = command[action];
const currentValue = this._currentValues[action];
if (first) {
str = action + value;
first = false;
} else if (currentValue !== value) {
str += ` ${action}${value}`;
this._currentValues[action] = value;
}
}
this._gcode += `${str}\n`;
}
turnFanOn(fanSpeed) {
this._isFanOn = true;
const gcode = { [M_COMMAND]: 106 }
if (typeof fanSpeed !== 'undefined') gcode[FAN_SPEED] = fanSpeed;
this._addGCode(gcode);
return this;
}
turnFanOff() {
this._isFanOn = false;
this._addGCode({ [M_COMMAND]: 107 });
return this;
}
moveTo(x, y, layer) {
const {
layerHeight,
travelSpeed
} = this._settings.config;
const z = layer * layerHeight + 0.2;
const speed = travelSpeed * 60;
this._addGCode({
[MOVE]: 0,
[POSITION_X]: x.toFixed(3),
[POSITION_Y]: y.toFixed(3),
[POSITION_Z]: z.toFixed(3),
[SPEED]: speed.toFixed(3)
});
this._nozzlePosition.set(x, y);
return this;
}
lineTo(x, y, layer, type) {
const newNozzlePosition = new THREE.Vector2(x, y);
const {
layerHeight,
nozzleDiameter,
filamentThickness,
travelSpeed
} = this._settings.config;
const profile = this._settings.config[(this.bottom ? 'bottom' : type)];
let {
speed,
flowRate
} = profile;
speed *= 60;
const z = layer * layerHeight + 0.2;
const lineLength = this._nozzlePosition.distanceTo(newNozzlePosition);
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
this._extruder += lineLength * nozzleDiameter * layerHeight / filamentSurfaceArea * flowRate;
this._addGCode({
[MOVE]: 1,
[POSITION_X]: x.toFixed(3),
[POSITION_Y]: y.toFixed(3),
[POSITION_Z]: z.toFixed(3),
[SPEED]: speed.toFixed(3),
[EXTRUDER]: this._extruder.toFixed(3)
});
this._nozzlePosition.copy(newNozzlePosition);
return this;
}
unRetract() {
const {
retraction: {
enabled: retractionEnabled,
minDistance: retractionMinDistance,
speed: retractionSpeed
}
} = this._settings.config;
if (this._isRetracted && retractionEnabled) {
this._isRetracted = false;
const speed = retractionSpeed * 60;
if (this._extruder > retractionMinDistance) {
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: this._extruder.toFixed(3),
[SPEED]: speed.toFixed(3)
});
}
}
return this;
}
retract() {
const {
retraction: {
amount: retractionAmount,
enabled: retractionEnabled,
minDistance: retractionMinDistance,
speed: retractionSpeed
}
} = this._settings.config;
if (!this._isRetracted && retractionEnabled) {
this._isRetracted = true;
const speed = retractionSpeed * 60;
if (this._extruder > retractionMinDistance && retractionEnabled) {
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: (this._extruder - retractionAmount).toFixed(3),
[SPEED]: speed.toFixed(3)
});
}
}
return this;
}
getGCode() {
return this._settings.startCode() + this._gcode + this._settings.endCode();
}
}

View File

@ -1,53 +0,0 @@
export default class {
constructor(config = {}) {
this.config = config;
}
updateConfig(config) {
this.config = { ...this.config, ...config };
return this;
}
startCode() {
const { startCode } = this.config;
const gcode = this._subsituteVariables(startCode);
return gcode;
}
endCode() {
const { endCode } = this.config;
const gcode = this._subsituteVariables(endCode);
return gcode;
}
_subsituteVariables(gcode) {
let {
temperature,
bedTemperature,
heatTemperature,
heatBedTemperature,
travelSpeed,
printerType,
heatedBed
} = this.config;
travelSpeed *= 60;
switch (printerType) {
case 'makerbot_replicator2': printerType = 'r2'; break;
case 'makerbot_replicator2x': printerType = 'r2x'; break;
case 'makerbot_thingomatic': printerType = 't6'; break;
case 'makerbot_generic': printerType = 'r2'; break;
case '_3Dison_plus': printerType = 'r2'; break;
}
const heatedBedReplacement = heatedBed ? '' : ';';
gcode = gcode.replace(/{printingTemp}/gi, temperature);
gcode = gcode.replace(/{printingBedTemp}/gi, bedTemperature);
gcode = gcode.replace(/{preheatTemp}/gi, heatTemperature);
gcode = gcode.replace(/{preheatBedTemp}/gi, heatBedTemperature);
gcode = gcode.replace(/{printerType}/gi, printerType);
gcode = gcode.replace(/{travelSpeed}/gi, travelSpeed);
gcode = gcode.replace(/{if heatedBed}/gi, heatedBedReplacement);
return gcode;
}
}

View File

@ -1,24 +0,0 @@
import Shape from 'Doodle3D/clipper-js';
export default class {
constructor() {
this.parts = [];
}
getOutline() {
return this.parts.reduce((shape, part) => {
if (part.outerLine) shape.join(part.outerLine);
return shape;
}, new Shape([], true));
}
add(shape) {
const part = { shape };
if (shape.closed) {
part.innerLines = [];
part.outerLine = new Shape([], true);
part.fill = new Shape([], false);
}
this.parts.push(part);
}
}

View File

@ -1,65 +0,0 @@
import * as THREE from 'three.js';
import Settings from './Settings.js';
import slice from './sliceActions/slice.js';
import SlicerWorker from './slicerWorker.js!worker';
export default class {
setMesh(mesh) {
mesh.updateMatrix();
this.setGeometry(mesh.geometry, mesh.matrix);
return this;
}
setGeometry(geometry, matrix) {
if (geometry.type === 'BufferGeometry') {
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
} else if (geometry.type === 'Geometry') {
geometry = geometry.clone();
} else {
throw new Error('Geometry is not an instance of BufferGeometry or Geometry');
}
if (matrix) {
geometry.applyMatrix(matrix);
}
this.geometry = geometry;
return this;
}
sliceSync(settings) {
return slice(this.geometry, new Settings(settings));
}
slice(settings) {
const slicerWorker = new SlicerWorker();
const geometry = this.geometry.toJSON();
const { config } = new Settings(settings);
return new Promise((resolve, reject) => {
slicerWorker.onerror = reject;
slicerWorker.addEventListener('message', (event) => {
const { message, data } = event.data;
switch (message) {
case 'SLICE': {
slicerWorker.terminate();
resolve(data.gcode);
break;
}
case 'PROGRESS': {
if (this.onprogress) {
this.onprogress(data);
}
}
}
});
slicerWorker.postMessage({
message: 'SLICE',
data: { geometry, config }
});
});
}
}

View File

@ -1,2 +1,5 @@
export const CLEAN_DELTA = 0.05;
export const PRECISION = 0.01;
export const VERSION = '0.0.19';
export const LOCAL_STORAGE_KEY = 'PRINTER_SETTINGS';
export const MIN_AREA = 1; // holes smaller as 1mm2 get removed
export const Z_OFFSET = 0.2;

View File

@ -1,18 +1,22 @@
import Slicer from './Slicer.js';
import defaultSettings from './settings/default.yml!text';
import printerSettings from './settings/printer.yml!text';
import materialSettings from './settings/material.yml!text';
import qualitySettings from './settings/quality.yml!text';
import yaml from 'js-yaml';
import { sliceGeometry, sliceMesh } from './slicer.js';
import Interface from './interface/index.js';
import _defaultSettings from './settings/default.yml';
import printerSettings from './settings/printer.yml';
import materialSettings from './settings/material.yml';
import qualitySettings from './settings/quality.yml';
import infillSettings from './settings/infill.yml';
const ds = {
base: yaml.safeLoad(defaultSettings),
printer: yaml.safeLoad(printerSettings),
material: yaml.safeLoad(materialSettings),
quality: yaml.safeLoad(qualitySettings)
const defaultSettings = {
default: _defaultSettings,
printer: printerSettings,
material: materialSettings,
quality: qualitySettings,
infill: infillSettings
};
export {
Slicer,
ds as defaultSettings
export {
sliceGeometry,
sliceMesh,
Interface,
defaultSettings
};

View File

@ -0,0 +1,65 @@
import React from 'react';
import PropTypes from 'proptypes';
import injectSheet from 'react-jss';
import ExpandIcon from 'material-ui-icons/ExpandMore';
const styles = {
button: {
cursor: 'pointer'
},
body: {
overflow: 'hidden'
},
closed: {
maxHeight: '0px'
},
title: {
userSelect: 'none',
display: 'flex',
alignItems: 'flex-end'
}
};
class Accordion extends React.Component {
static propTypes = {
elements: PropTypes.arrayOf(PropTypes.shape({ body: PropTypes.node, title: PropTypes.string })),
classes: PropTypes.objectOf(PropTypes.string)
};
static defaultProps: {
elements: []
};
state = {
openAccordion: null
};
changeAccordion = (name) => {
const { openAccordion } = this.state;
if (openAccordion === name) {
this.setState({ openAccordion: null });
} else {
this.setState({ openAccordion: name });
}
};
render() {
const { openAccordion } = this.state;
const { elements, classes } = this.props;
return elements.map(({ body, title }, i) => (
<span key={i}>
<span onClick={() => this.changeAccordion(title)} className={classes.title}>
<ExpandIcon />
<p style={{
fontWeight: openAccordion === title ? 'bold' : 'normal'
}} className={classes.button}>{title}</p>
</span>
<div className={`${classes.body} ${openAccordion === title ? '' : classes.closed}`}>
{body}
</div>
</span>
));
}
}
export default injectSheet(styles)(Accordion);

View File

@ -0,0 +1,104 @@
import React from 'react';
import PropTypes from 'proptypes';
import _ from 'lodash';
import MaterialUISelectField from 'material-ui/SelectField';
import MaterialUICheckbox from 'material-ui/Checkbox';
import TextFieldIcon from 'material-ui-textfield-icon';
import RefreshIcon from 'material-ui-icons/Refresh';
import muiThemeable from 'material-ui/styles/muiThemeable';
export const contextTypes = {
settings: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired,
addPrinter: PropTypes.object.isRequired,
managePrinter: PropTypes.object.isRequired,
advancedFields: PropTypes.array.isRequired,
activePrinter: PropTypes.string
};
const propTypes = {
name: PropTypes.string.isRequired,
muiTheme: PropTypes.object.isRequired
};
export const _SelectField = ({ name, muiTheme, ...props }, context) => (
<MaterialUISelectField
{...props}
disabled={context.disabled}
value={_.get(context, name)}
onChange={(event, index, value) => context.onChange(name, value)}
/>
);
_SelectField.contextTypes = contextTypes;
_SelectField.propTypes = propTypes;
export const SelectField = muiThemeable()(_SelectField);
const _TextField = ({ name, muiTheme: { palette }, ...props }, context) => (
<TextFieldIcon
{...props}
icon={context.advancedFields.includes(name) && <RefreshIcon
style={{ fill: palette.textColor }}
onClick={() => context.onChange(name, null)}
/>}
floatingLabelStyle={{
color: context.advancedFields.includes(name) ? palette.primary1Color : palette.primary3Color
}}
disabled={context.disabled}
value={_.get(context, name)}
onChange={(event, value) => context.onChange(name, value)}
/>
);
_TextField.contextTypes = contextTypes;
_TextField.propTypes = propTypes;
export const TextField = muiThemeable()(_TextField);
const _NumberField = ({ name, min, max, muiTheme: { palette }, ...props }, context) => (
<TextFieldIcon
{...props}
type="number"
icon={context.advancedFields.includes(name) && <RefreshIcon
style={{ fill: palette.textColor }}
onClick={() => context.onChange(name, null)}
/>}
floatingLabelStyle={{
color: context.advancedFields.includes(name) ? palette.primary1Color : palette.primary3Color
}}
disabled={context.disabled}
value={_.get(context, name.toString())}
onChange={(event, value) => {
value = parseFloat(value);
context.onChange(name, value);
}}
onBlur={() => {
const value = _.get(context, name.toString());
let newValue = value;
if (typeof min === 'number') newValue = Math.max(newValue, min);
if (typeof max === 'number') newValue = Math.min(newValue, max);
if (newValue !== value) context.onChange(name, newValue);
}}
/>
);
_NumberField.contextTypes = contextTypes;
_NumberField.propTypes = propTypes;
export const NumberField = muiThemeable()(_NumberField);
const _Checkbox = ({ name, muiTheme: { palette }, ...props }, context) => (
<span style={{ display: 'flex', position: 'relative' }}>
<MaterialUICheckbox
{...props}
style={{ display: 'block' }}
iconStyle={{
fill: context.advancedFields.includes(name) ? palette.primary1Color : palette.primary3Color
}}
disabled={context.disabled}
checked={_.get(context, name)}
onCheck={(event, value) => context.onChange(name, value)}
/>
{context.advancedFields.includes(name) && <RefreshIcon
onClick={() => context.onChange(name, null)}
/>}
</span>
);
_Checkbox.contextTypes = contextTypes;
_Checkbox.propTypes = propTypes;
export const Checkbox = muiThemeable()(_Checkbox);

568
src/interface/Settings.js Normal file
View File

@ -0,0 +1,568 @@
import React from 'react';
import PropTypes from 'proptypes';
import _ from 'lodash';
import { Tabs, Tab } from 'material-ui/Tabs';
import MenuItem from 'material-ui/MenuItem';
import injectSheet from 'react-jss';
import { SelectField, TextField, NumberField, Checkbox } from './FormComponents.js';
import { grey800, red500 } from 'material-ui/styles/colors';
import Divider from 'material-ui/Divider';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import RaisedButton from 'material-ui/RaisedButton';
import { LOCAL_STORAGE_KEY } from '../constants.js';
import shortid from 'shortid';
import defaultSettings from '../settings/default.yml';
import printerSettings from '../settings/printer.yml';
import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import infillSettings from '../settings/infill.yml';
import update from 'react-addons-update';
import SettingsIcon from 'material-ui-icons/Settings';
import ExitToAppIcon from 'material-ui-icons/ExitToApp';
import validateIp from 'validate-ip';
import Accordion from './Accordion.js';
const styles = {
textFieldRow: {
display: 'flex',
alignItems: 'center'
},
container: {
width: '100%',
flexGrow: 1,
overflowY: 'auto',
'& p': {
// fontWeight: 'bold',
margin: '30px 0 0 0'
},
'& h3': {
fontWeight: 'bold',
marginTop: '20px',
marginBottom: '20px'
}
},
error: {
color: red500
}
};
const updateLocalStorage = (localStorage) => {
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(localStorage));
};
const getLocalStorage = () => {
let localStorage = window.localStorage.getItem(LOCAL_STORAGE_KEY);
if (!localStorage) {
localStorage = { printers: {}, active: null };
updateLocalStorage(localStorage);
} else {
localStorage = JSON.parse(localStorage);
}
return localStorage;
};
class Settings extends React.Component {
static propTypes = {
selectedPrinter: PropTypes.string,
classes: PropTypes.objectOf(PropTypes.string),
onChange: PropTypes.func,
disabled: PropTypes.bool.isRequired
};
static defaultProps: {
disabled: false
};
static childContextTypes = {
settings: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
disabled: PropTypes.bool.isRequired,
addPrinter: PropTypes.object.isRequired,
managePrinter: PropTypes.object.isRequired,
activePrinter: PropTypes.string,
advancedFields: PropTypes.array.isRequired
};
state = {
localStorage: getLocalStorage(),
addPrinter: {
open: false,
name: '',
printer: '',
ip: '',
error: null
},
managePrinter: {
open: false
}
};
componentDidMount() {
const { onChange, selectedPrinter } = this.props;
const { localStorage } = this.state;
if (selectedPrinter && localStorage.active) {
const activePrinter = selectedPrinter && Object.values(localStorage.printers)
.find(({ ip }) => ip === selectedPrinter);
if (activePrinter) {
const state = this.changeSettings('activePrinter', activePrinter.key);
if (onChange) onChange(this.constructSettings(state.localStorage));
} else {
this.openAddPrinterDialog({ ip: selectedPrinter });
}
} else if (!selectedPrinter && localStorage.active) {
if (onChange) onChange(this.constructSettings(localStorage));
} else if (selectedPrinter && !localStorage.active) {
this.openAddPrinterDialog({ ip: selectedPrinter });
} else if (!selectedPrinter && !localStorage.active) {
this.openAddPrinterDialog();
}
}
changeSettings = (fieldName, value) => {
const { onChange } = this.props;
const { localStorage } = this.state;
let state = _.cloneDeep(this.state);
switch (fieldName) {
case 'managePrinter.printer':
case 'managePrinter.name':
case 'managePrinter.ip':
state = _.set(state, fieldName, value);
state = update(state, { managePrinter: { error: { $set: null } } });
break;
case 'addPrinter.printer':
case 'addPrinter.name':
case 'addPrinter.ip':
state = _.set(state, fieldName, value);
if (fieldName === 'addPrinter.printer') {
state = update(state, { addPrinter: { name: { $set: printerSettings[value].title } } });
}
state = update(state, { addPrinter: { error: { $set: null } } });
break;
case 'activePrinter':
if (value !== 'add_printer') state = update(state, { localStorage: { active: { $set: value } } });
break;
case 'settings.infill':
case 'settings.quality':
case 'settings.material':
if (!localStorage.active) return this.openAddPrinterDialog();
state = _.set(state, `localStorage.printers[${localStorage.active}].${fieldName}`, value);
break;
case 'settings.layerHeight':
case 'settings.dimensions.x':
case 'settings.dimensions.y':
case 'settings.dimensions.z':
case 'settings.nozzleDiameter':
case 'settings.bedTemperature':
case 'settings.heatedBed':
case 'settings.filamentThickness':
case 'settings.temperature':
case 'settings.thickness.top':
case 'settings.thickness.bottom':
case 'settings.thickness.shell':
case 'settings.retraction.enabled':
case 'settings.retraction.amount':
case 'settings.retraction.speed':
case 'settings.retraction.minDistance':
case 'settings.travel.speed':
case 'settings.combing':
case 'settings.innerShell.speed':
case 'settings.innerShell.flowRate':
case 'settings.outerShell.speed':
case 'settings.outerShell.flowRate':
case 'settings.innerInfill.density':
case 'settings.innerInfill.speed':
case 'settings.innerInfill.flowRate':
case 'settings.outerInfill.speed':
case 'settings.outerInfill.flowRate':
case 'settings.brim.size':
case 'settings.brim.speed':
case 'settings.brim.flowRate':
case 'settings.firstLayer.speed':
case 'settings.firstLayer.flowRate':
case 'settings.support.enabled':
case 'settings.support.speed':
case 'settings.support.distanceY':
case 'settings.support.density':
case 'settings.support.minArea':
case 'settings.support.margin':
case 'settings.support.flowRate':
if (!localStorage.active) return this.openAddPrinterDialog();
if (value === null) {
const advanced = { ...state.localStorage.printers[localStorage.active].settings.advanced };
delete advanced[fieldName];
state = update(state, { localStorage: { printers: { [localStorage.active]: { settings: { advanced: { $set: advanced } } } } } });
} else {
state = _.set(state, `localStorage.printers[${localStorage.active}].settings.advanced[${JSON.stringify(fieldName)}]`, value);
}
break;
default:
break;
}
this.setState(state);
if (localStorage.active) {
if (onChange) onChange(this.constructSettings(state.localStorage));
updateLocalStorage(state.localStorage);
}
return state;
}
getChildContext() {
const { localStorage, addPrinter, managePrinter } = this.state;
return {
addPrinter,
managePrinter,
activePrinter: localStorage.active,
advancedFields: localStorage.active ? Object.keys(localStorage.printers[localStorage.active].settings.advanced) : [],
settings: this.constructSettings(localStorage),
onChange: this.changeSettings,
disabled: this.props.disabled
};
}
constructSettings(localStorage) {
if (!localStorage.active) return defaultSettings;
const { ip, settings: { printer, material, quality, infill, advanced } } = localStorage.printers[localStorage.active];
let settings = {
...defaultSettings,
printer,
material,
quality,
infill,
ip
};
settings = _.merge({}, settings, printerSettings[printer]);
settings = _.merge({}, settings, qualitySettings[quality]);
settings = _.merge({}, settings, infillSettings[infill]);
settings = _.merge({}, settings, materialSettings[material]);
for (const key in advanced) {
const value = advanced[key];
settings = _.set(_.cloneDeep(settings), key.replace('settings.', ''), value);
}
return settings;
}
addPrinter = () => {
const { name, printer, ip } = this.state.addPrinter;
if (!name || !printer) {
this.setState(update(this.state, { addPrinter: { error: { $set: 'Please enter a name and printer' } } }));
return;
}
if (printer === 'doodle3d_printer' && ip !== '' && !validateIp(ip)) {
this.setState(update(this.state, { addPrinter: { error: { $set: 'Please enter a valid IP adress' } } }));
return;
}
const id = shortid.generate();
const localStorage = {
active: id,
printers: {
...this.state.localStorage.printers,
[id]: { name, ip, settings: { printer, material: 'pla', infill: '20pct', quality: 'medium', advanced: {} } }
}
};
this.setState({ localStorage });
updateLocalStorage(localStorage);
this.closeAddPrinterDialog();
const { onChange } = this.props;
if (onChange) onChange(this.constructSettings(localStorage));
};
editPrinter = () => {
const { localStorage: { active }, managePrinter: { printer, name, ip } } = this.state;
if (!name) {
this.setState(update(this.state, {
managePrinter: {
error: { $set: 'Please enter a name' }
}
}));
return;
}
if (printer === 'doodle3d_printer' && !validateIp(ip)) {
this.setState(update(this.state, {
managePrinter: {
error: { $set: 'Please enter a valid IP adress' }
}
}));
return;
}
const localStorage = update(this.state.localStorage, {
printers: {
[active]: {
name: { $set: name },
ip: { $set: ip },
settings: {
printer: { $set: printer }
}
}
}
});
this.closeManagePrinterDialog();
this.setState({ localStorage });
updateLocalStorage(localStorage);
const { onChange } = this.props;
if (onChange) onChange(this.constructSettings(localStorage));
};
removeActivePrinter = () => {
let { localStorage: { active, printers } } = this.state;
if (!active) return;
printers = { ...printers };
delete printers[active];
active = Object.keys(printers)[0] || null;
const localStorage = { active, printers };
this.closeManagePrinterDialog();
this.setState({ localStorage });
updateLocalStorage(localStorage);
const { onChange } = this.props;
if (onChange) onChange(this.constructSettings(localStorage));
};
closeAddPrinterDialog = (override) => this.setAddPrinterDialog(false, override);
openAddPrinterDialog = (override) => this.setAddPrinterDialog(true, override);
setAddPrinterDialog = (open, override = {}) => {
this.setState({
addPrinter: {
ip: '',
name: '',
printer: '',
error: null,
open,
...override
}
});
};
closeManagePrinterDialog = () => this.setManagePrinterDialog(false);
openManagePrinterDialog = () => this.setManagePrinterDialog(true);
setManagePrinterDialog = (open) => {
const { localStorage: { active, printers } } = this.state;
this.setState({
managePrinter: {
open,
name: printers[active].name,
ip: printers[active].ip,
printer: printers[active].settings.printer,
error: null
}
});
}
render() {
const { addPrinter, managePrinter, localStorage } = this.state;
const { classes } = this.props;
return (
<div className={classes.container}>
<div className={classes.textFieldRow}>
<SelectField name="activePrinter" floatingLabelText="Printer" fullWidth>
{Object.entries(localStorage.printers).map(([id, { name }]) => (
<MenuItem key={id} value={id} primaryText={name} />
))}
<Divider />
<MenuItem onClick={this.openAddPrinterDialog} value="add_printer" primaryText="Add Printer" />
</SelectField>
{localStorage.active && <SettingsIcon
onClick={this.openManagePrinterDialog}
style={{ fill: grey800, marginLeft: '10px', cursor: 'pointer' }}
/>}
</div>
<SelectField name="settings.material" floatingLabelText="Material" fullWidth>
{Object.entries(materialSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
<h3>Print Setup</h3>
<Tabs>
<Tab buttonStyle={{ color: grey800, backgroundColor: 'white' }} label="Basic">
<div>
<SelectField name="settings.quality" floatingLabelText="Quality" fullWidth>
{Object.entries(qualitySettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
<SelectField name="settings.infill" floatingLabelText="Infill" fullWidth>
{Object.entries(infillSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
</div>
</Tab>
<Tab buttonStyle={{ color: grey800, backgroundColor: 'white' }} label="Advanced">
<div>
<Accordion elements={[{
title: 'Layer',
body: (<NumberField name="settings.layerHeight" min={0.05} max={3} fullWidth floatingLabelText="Height" />)
}, {
title: 'Thickness',
body: (<span>
<NumberField name="settings.thickness.top" min={0} fullWidth floatingLabelText="top" />
<NumberField name="settings.thickness.bottom" min={0} fullWidth floatingLabelText="bottom" />
<NumberField name="settings.thickness.shell" min={0} fullWidth floatingLabelText="shell" />
</span>)
}, {
title: 'Material',
body: (<span>
<NumberField name="settings.filamentThickness" min={0.1} max={10} fullWidth floatingLabelText="Thickness" />
<NumberField name="settings.temperature" min={100} max={400} fullWidth floatingLabelText="Temperature" />
</span>)
}, {
title: 'Bed',
body: (<span>
<NumberField name="settings.bedTemperature" min={30} max={150} fullWidth floatingLabelText="Temperature" />
<Checkbox name="settings.heatedBed" label="Heated" />
</span>)
}, {
title: 'Brim',
body: (<span>
<NumberField name="settings.brim.size" min={0} max={20} fullWidth floatingLabelText="Size" />
<NumberField name="settings.brim.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.brim.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'Support',
body: (<span>
<Checkbox name="settings.support.enabled" label="Enabled" />
<NumberField name="settings.support.distanceY" min={0.1} fullWidth floatingLabelText="Distance Y" />
<NumberField name="settings.support.density" min={0} max={100} fullWidth floatingLabelText="Density" />
<NumberField name="settings.support.margin" min={0.1} fullWidth floatingLabelText="Margin" />
<NumberField name="settings.support.minArea" min={1} fullWidth floatingLabelText="Min Area" />
<NumberField name="settings.support.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.support.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'First layer',
body: (<span>
<NumberField name="settings.firstLayer.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.firstLayer.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'Inner shell',
body: (<span>
<NumberField name="settings.innerShell.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.innerShell.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'Outer shell',
body: (<span>
<NumberField name="settings.outerShell.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.outerShell.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'Inner infill',
body: (<span>
<NumberField name="settings.innerInfill.density" min={0} max={100} fullWidth floatingLabelText="Density" />
<NumberField name="settings.innerInfill.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.innerInfill.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'Outer infill',
body: (<span>
<NumberField name="settings.outerInfill.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.outerInfill.flowRate" min={0.1} max={4} fullWidth floatingLabelText="Flow rate" />
</span>)
}, {
title: 'Travel',
body: (<span>
<NumberField name="settings.travel.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<Checkbox name="settings.combing" label="Combing" />
</span>)
}, {
title: 'Retraction',
body: (<span>
<Checkbox name="settings.retraction.enabled" label="Enabled" />
<NumberField name="settings.retraction.amount" min={0} max={10} fullWidth floatingLabelText="Amount" />
<NumberField name="settings.retraction.speed" min={10} max={200} fullWidth floatingLabelText="Speed" />
<NumberField name="settings.retraction.minDistance" min={0} fullWidth floatingLabelText="Min distance" />
</span>)
}, {
title: 'Printer dimensions',
body: (<span>
<div className={classes.textFieldRow}>
<NumberField name="settings.dimensions.x" min={1} fullWidth floatingLabelText="X" />
<NumberField name="settings.dimensions.y" min={1} fullWidth floatingLabelText="Y" />
<NumberField name="settings.dimensions.z" min={1} fullWidth floatingLabelText="Z" />
</div>
</span>)
}, {
title: 'Nozzle',
body: (<span>
<NumberField name="settings.nozzleDiameter" min={0.1} max={5} fullWidth floatingLabelText="Diameter" />
</span>)
}]} />
</div>
</Tab>
</Tabs>
{printDialog(this.props, this.state, 'Add Printer', 'addPrinter', 'Add', addPrinter, localStorage.active && this.closeAddPrinterDialog, null, this.addPrinter)}
{printDialog(this.props, this.state, 'Manage Printer', 'managePrinter', 'Save', managePrinter, this.closeManagePrinterDialog, this.removeActivePrinter, this.editPrinter)}
</div>
);
}
}
function printDialog(props, state, title, form, submitText, data, closeDialog, removeActivePrinter, save) {
const { classes } = props;
return (
<Dialog
title={title}
open={data.open}
onRequestClose={closeDialog ? closeDialog : null}
contentStyle={{ maxWidth: '400px' }}
autoScrollBodyContent
actions={[
closeDialog && <FlatButton
label="Close"
onClick={closeDialog}
/>,
removeActivePrinter && <FlatButton
label="Remove Printer"
onClick={removeActivePrinter}
/>,
<RaisedButton
label={submitText}
primary
onClick={save}
/>
]}
>
<SelectField name={`${form}.printer`} floatingLabelText="Printer" fullWidth>
{Object.entries(printerSettings).map(([value, { title }]) => (
<MenuItem key={value} value={value} primaryText={title} />
))}
</SelectField>
{data.error && <p className={classes.error}>{data.error}</p>}
</Dialog>
);
}
printDialog.propTypes = {
classes: PropTypes.objectOf(PropTypes.string)
};
export default injectSheet(styles)(Settings);

377
src/interface/index.js Normal file
View File

@ -0,0 +1,377 @@
import * as THREE from 'three';
import React from 'react';
import PropTypes from 'proptypes';
import { centerGeometry, placeOnGround, createScene, slice, TabTemplate } from './utils.js';
import injectSheet from 'react-jss';
import RaisedButton from 'material-ui/RaisedButton';
import LinearProgress from 'material-ui/LinearProgress';
import { grey50, grey300, grey800, red500 } from 'material-ui/styles/colors';
import Popover from 'material-ui/Popover/Popover';
import Menu from 'material-ui/Menu';
import MenuItem from 'material-ui/MenuItem';
import { Tabs, Tab } from 'material-ui/Tabs';
import Settings from './Settings.js';
import ReactResizeDetector from 'react-resize-detector';
import muiThemeable from 'material-ui/styles/muiThemeable';
import logo from '../../img/logo.png';
const MAX_FULLSCREEN_WIDTH = 720;
const styles = {
container: {
position: 'relative',
display: 'flex',
height: '100%',
backgroundColor: grey50,
color: grey800,
overflow: 'hidden',
fontFamily: 'roboto, sans-serif'
},
controlBar: {
position: 'absolute',
bottom: '10px',
left: '10px'
},
d3View: {
flexGrow: 1,
flexBasis: 0
},
canvas: {
position: 'absolute'
},
settingsBar: {
display: 'flex',
flexDirection: 'column',
maxWidth: '320px',
boxSizing: 'border-box',
padding: '10px 20px',
backgroundColor: 'white',
overflowY: 'auto',
borderLeft: `1px solid ${grey300}`
},
sliceActions: {
flexShrink: 0
},
sliceInfo: {
margin: '10px 0',
'& p': {
marginBottom: '5px',
fontSize: '11px'
}
},
sliceButtons: {
justifyContent: 'flex-end',
display: 'flex'
},
button: {
margin: '5px 0 5px 5px'
},
controlButton: {
marginRight: '5px'
},
buttonContainer: {
width: '100%',
padding: '10px'
},
error: {
color: red500
},
title: {
userSelect: 'none',
position: 'absolute',
left: '10px'
},
detail: {
userSelect: 'none',
marginTop: '10px',
marginBottom: '10px'
},
logo: {
position: 'absolute',
left: '20px',
top: '20px',
width: '150px',
height: '51px'
}
};
class Interface extends React.Component {
static propTypes = {
selectedPrinter: PropTypes.string,
mesh: PropTypes.shape({ isMesh: PropTypes.oneOf([true]) }),
classes: PropTypes.objectOf(PropTypes.string),
pixelRatio: PropTypes.number.isRequired,
onCancel: PropTypes.func,
onSliceSucces: PropTypes.func.isRequired,
muiTheme: PropTypes.object.isRequired
};
static defaultProps = {
pixelRatio: 1
};
constructor(props) {
super(props);
this.canvasElement = React.createRef();
const scene = createScene(this.props);
this.state = {
scene,
settings: null,
showFullScreen: window.innerWidth > MAX_FULLSCREEN_WIDTH,
isSlicing: false,
error: null,
mesh: null,
objectDimensions: '0x0x0mm',
popover: { open: false, element: null }
};
}
componentDidMount() {
const { scene } = this.state;
scene.updateCanvas(this.canvasElement.current);
const { mesh } = this.props;
if (mesh) {
this.updateMesh(mesh, scene);
}
}
updateMesh(mesh, scene = this.state.scene) {
scene.mesh.geometry = mesh.geometry;
centerGeometry(scene.mesh);
placeOnGround(scene.mesh);
this.calculateDimensions();
scene.render();
this.setState({ mesh });
}
componentWillUnmount() {
const { scene: { editorControls, mesh: { material }, renderer } } = this.state;
editorControls.dispose();
material.dispose();
renderer.dispose();
}
resetMesh = () => {
const { scene: { mesh, render }, isSlicing } = this.state;
if (isSlicing) return;
if (mesh) {
mesh.position.set(0, 0, 0);
mesh.scale.set(1, 1, 1);
mesh.rotation.set(0, 0, 0);
mesh.updateMatrix();
placeOnGround(mesh);
this.calculateDimensions();
render();
}
};
scaleUp = () => this.scaleMesh(0.9);
scaleDown = () => this.scaleMesh(1.0 / 0.9);
scaleMesh = (factor) => {
const { scene: { mesh, render }, isSlicing } = this.state;
if (isSlicing) return;
if (mesh) {
mesh.scale.multiplyScalar(factor);
mesh.updateMatrix();
placeOnGround(mesh);
this.calculateDimensions();
render();
}
};
rotateX = () => this.rotate(new THREE.Vector3(0, 0, 1), Math.PI / 2.0);
rotateY = () => this.rotate(new THREE.Vector3(1, 0, 0), Math.PI / 2.0);
rotateZ = () => this.rotate(new THREE.Vector3(0, 1, 0), Math.PI / 2.0);
rotate = (axis, angle) => {
const { scene: { mesh, render }, isSlicing } = this.state;
if (isSlicing) return;
if (mesh) {
mesh.rotateOnWorldAxis(axis, angle);
placeOnGround(mesh);
this.calculateDimensions();
render();
}
};
slice = async () => {
const { isSlicing, settings, mesh, scene: { mesh: { matrix } } } = this.state;
const { onSliceSucces } = this.props;
if (isSlicing) return;
if (!settings) {
this.setState({ error: 'please select a printer first' });
return;
}
if (!mesh) {
this.setState({ error: 'there is no file to slice' });
return;
}
this.closePopover();
this.setState({ isSlicing: true, progress: { action: '', percentage: 0, step: 0 }, error: null });
const exportMesh = new THREE.Mesh(mesh.geometry, mesh.material);
exportMesh.applyMatrix(matrix);
try {
const updateProgres = progress => this.setState({ progress: { ...this.state.progress, ...progress } });
const sliceResults = await slice(exportMesh, settings, updateProgres);
onSliceSucces(sliceResults);
} catch (error) {
this.setState({ error: error.message });
throw error;
} finally {
this.setState({ isSlicing: false });
}
};
openPopover = (event) => {
event.preventDefault();
this.setState({
popover: {
element: event.currentTarget,
open: true
}
});
};
closePopover = () => {
this.setState({
popover: {
element: null,
open: false
}
});
};
componentDidUpdate() {
const { scene: { updateCanvas } } = this.state;
if (updateCanvas && this.canvasElement.current) updateCanvas(this.canvasElement.current);
}
onResize3dView = (width, height) => {
window.requestAnimationFrame(() => {
const { scene: { setSize } } = this.state;
const { pixelRatio } = this.props;
if (setSize) setSize(width, height, pixelRatio);
});
};
onResizeContainer = (width) => {
this.setState({ showFullScreen: width > MAX_FULLSCREEN_WIDTH });
};
onChangeSettings = (settings) => {
const { scene: { box, render } } = this.state;
let changed = false;
if (!this.state.settings || this.state.settings.dimensions !== settings.dimensions) {
box.scale.set(settings.dimensions.y, settings.dimensions.z, settings.dimensions.x);
box.updateMatrix();
changed = true;
}
if (changed) render();
this.setState({ settings, error: null });
};
calculateDimensions = () => {
const { scene: { mesh } } = this.state;
const { x, y, z } = new THREE.Box3().setFromObject(mesh).getSize();
this.setState({ objectDimensions: `${Math.round(y)}x${Math.round(z)}x${Math.round(x)}mm` });
};
render() {
const { classes, onCancel, selectedPrinter } = this.props;
const { isSlicing, settings, progress, showFullScreen, error, objectDimensions } = this.state;
const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) };
const settingsPanel = (
<div className={classes.settingsBar} style={style}>
<Settings
selectedPrinter={selectedPrinter}
disabled={isSlicing}
onChange={this.onChangeSettings}
/>
<div className={classes.sliceActions}>
<div className={classes.sliceInfo}>
{error && <p className={classes.error}>{error}</p>}
{isSlicing && <p>{progress.action}</p>}
{isSlicing && <LinearProgress mode="determinate" value={progress.percentage * 100.0} />}
</div>
<div className={classes.sliceButtons}>
{onCancel && <RaisedButton
label="Close"
className={`${classes.button}`}
onClick={onCancel}
/>}
<RaisedButton
label="Download GCODE"
ref="button"
primary
className={`${classes.button}`}
disabled={isSlicing}
onClick={() => this.slice()}
/>
</div>
</div>
</div>
);
const d3Panel = (
<div className={classes.d3View}>
<ReactResizeDetector handleWidth handleHeight onResize={this.onResize3dView} />
<canvas className={classes.canvas} ref={this.canvasElement} />
<div className={classes.controlBar}>
<div className={classes.detail}>
<p>Dimensions: {objectDimensions}</p>
</div>
<RaisedButton disabled={isSlicing} className={classes.controlButton} onClick={this.resetMesh} label="reset" />
<RaisedButton disabled={isSlicing} className={classes.controlButton} onClick={this.scaleUp} label="scale down" />
<RaisedButton disabled={isSlicing} className={classes.controlButton} onClick={this.scaleDown} label="scale up" />
<RaisedButton disabled={isSlicing} className={classes.controlButton} onClick={this.rotateX} label="rotate x" />
<RaisedButton disabled={isSlicing} className={classes.controlButton} onClick={this.rotateY} label="rotate y" />
<RaisedButton disabled={isSlicing} className={classes.controlButton} onClick={this.rotateZ} label="rotate z" />
</div>
</div>
);
if (showFullScreen) {
return (
<div className={classes.container}>
<ReactResizeDetector handleWidth handleHeight onResize={this.onResizeContainer} />
<img src={logo} className={classes.logo} />
{d3Panel}
{settingsPanel}
</div>
);
} else {
return (
<div className={classes.container}>
<ReactResizeDetector handleWidth handleHeight onResize={this.onResizeContainer} />
<Tabs
style={{ width: '100%', display: 'flex', flexDirection: 'column' }}
tabItemContainerStyle={{ flexShrink: 0 }}
contentContainerStyle={{ flexGrow: 1, display: 'flex' }}
tabTemplateStyle={{ display: 'flex' }}
tabTemplate={TabTemplate}
>
<Tab label="Settings">
{settingsPanel}
</Tab>
<Tab label="Edit Model">
{d3Panel}
</Tab>
</Tabs>
</div>
);
}
}
}
export default muiThemeable()(injectSheet(styles)(Interface));

143
src/interface/utils.js Normal file
View File

@ -0,0 +1,143 @@
import * as THREE from 'three';
import 'three/examples/js/controls/EditorControls';
import printerSettings from '../settings/printer.yml';
import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import { sliceGeometry } from '../slicer.js';
import React from 'react';
import PropTypes from 'prop-types';
export function placeOnGround(mesh) {
const boundingBox = new THREE.Box3().setFromObject(mesh);
mesh.position.y -= boundingBox.min.y;
mesh.updateMatrix();
}
export function centerGeometry(mesh) {
// center geometry
mesh.geometry.computeBoundingBox();
const center = mesh.geometry.boundingBox.getCenter();
mesh.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(-center.x, -center.y, -center.z));
}
export function createScene({ muiTheme }) {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, 1, 1, 10000);
camera.position.set(0, 400, 300);
camera.lookAt(new THREE.Vector3(0, 0, 0));
const directionalLightA = new THREE.DirectionalLight(0xa2a2a2);
directionalLightA.position.set(1, 1, 1);
scene.add(directionalLightA);
const directionalLightB = new THREE.DirectionalLight(0xa2a2a2);
directionalLightB.position.set(-1, 1, -1);
scene.add(directionalLightB);
const light = new THREE.AmbientLight(0x656565);
scene.add(light);
const material = new THREE.MeshPhongMaterial({ color: muiTheme.palette.primary2Color, side: THREE.DoubleSide, specular: 0xc5c5c5, shininess: 5, flatShading: false });
const mesh = new THREE.Mesh(new THREE.Geometry(), material);
scene.add(mesh);
const box = new THREE.BoxHelper(new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1).applyMatrix(new THREE.Matrix4().makeTranslation(0, 0.5, 0))), muiTheme.palette.primary2Color);
scene.add(box);
let renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
let editorControls = new THREE.EditorControls(camera, renderer.domElement);
box.scale.set(1, 1, 1);
box.updateMatrix();
const render = () => renderer.render(scene, camera);
const setSize = (width, height, pixelRatio = 1) => {
renderer.setSize(width, height);
renderer.setPixelRatio(pixelRatio);
camera.aspect = width / height;
camera.updateProjectionMatrix();
render();
};
const updateCanvas = (canvas) => {
if (!renderer || renderer.domElement !== canvas) {
if (renderer) renderer.dispose();
renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
renderer.setClearColor(0xffffff, 0);
}
if (!editorControls || editorControls.domElement !== canvas) {
if (editorControls) editorControls.dispose();
editorControls = new THREE.EditorControls(camera, canvas);
editorControls.addEventListener('change', render);
}
render();
};
const focus = () => editorControls.focus(mesh);
return { editorControls, scene, mesh, camera, renderer, render, box, setSize, updateCanvas, focus };
}
export function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
export async function slice(mesh, settings, updateProgress) {
let steps = 1;
let currentStep = 0;
const { dimensions } = settings;
const centerX = dimensions.x / 2;
const centerY = dimensions.y / 2;
const matrix = new THREE.Matrix4().makeTranslation(centerY, 0, centerX)
.multiply(new THREE.Matrix4().makeRotationY(-Math.PI / 2.0))
.multiply(mesh.matrix);
const sliceResult = await sliceGeometry({
...settings,
printer: { type: settings.printers, title: printerSettings[settings.printer].title },
material: { type: settings.material, title: materialSettings[settings.material].title },
quality: { type: settings.quality, title: qualitySettings[settings.quality].title }
}, mesh.geometry, mesh.material, matrix, false, false, ({ progress }) => {
updateProgress({
action: progress.action,
percentage: (currentStep + progress.done / progress.total) / steps
});
}).catch(error => {
throw { message: `error during slicing: ${error.message}`, code: 2 };
});
currentStep ++;
return sliceResult;
}
export const TabTemplate = ({ children, selected, style }) => {
const templateStyle = {
width: '100%',
position: 'relative',
textAlign: 'initial',
...style,
...(selected ? {} : {
height: 0,
width: 0,
overflow: 'hidden'
})
};
return (
<div style={templateStyle}>
{children}
</div>
);
};
TabTemplate.propTypes = {
children: PropTypes.node,
selected: PropTypes.bool,
style: PropTypes.object
};

View File

@ -1,51 +1,77 @@
startCode: |-
M109 S{temperature} ;set target temperature
{if heatedBed}M190 S{bedTemperature} ;set target bed temperature
G21 ;metric values
M107 ;start with the fan off
G28 X0 Y0 ;move X/Y to min endstops
G28 Z0 ;move Z to min endstops
G1 Z15 F9000 ;move the platform down 15mm
G92 E0 ;zero the extruded length
G91 ;relative positioning
G1 F200 E10 ;extrude 10mm of feed stock
G92 E0 ;zero the extruded length again
G92 E0 ;zero the extruded length again
G1 F9000
G90 ;absolute positioning
M117 Printing Doodle...
endCode: |-
M107 ;fan off
G91 ;relative positioning
G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure
G1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more
G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way
M84 ;disable axes / steppers
G90 ;absolute positioning
M104 S0
{if heatedBed}M140 S0
M117 Done
dimensions:
x: 200
y: 200
z: 200
temperature: 210
bedTemperature: 70
# heatBedTemperature: 20
# heatTemperature: 20
# heatupEnabled: true
travelSpeed: 200.0
layerHeight: 0.15
heatedBed: false
nozzleDiameter: 0.4
filamentThickness: 2.85
temperature: 210
bedTemperature: 50
layerHeight: 0.15
combing: false
thickness:
top: 0.45
bottom: 0.45
shell: 0.8
retraction:
amount: 3.0
enabled: true
amount: 3.0
speed: 50.0
minDistance: 0.0
travel:
speed: 200.0
support:
acceptanceMargin: 1.5
distanceY: 0.4
enabled: false
gridSize: 6.0
minArea: 2
distanceY: 0.4
density: 5.0
margin: 2.0
plateSize: 4.0
flowRate: 0.8
speed: 40.0
outerLine:
innerShell:
flowRate: 1.0
speed: 50.0
outerShell:
flowRate: 1.0
speed: 40.0
innerLine:
innerInfill:
flowRate: 1.0
speed: 80.0
density: 20.0
outerInfill:
flowRate: 1.0
speed: 50.0
fill:
flowRate: 1.0
speed: 50.0
overlap: 0.0
gridSize: 5.0
brim:
size: 8.0
flowRate: 1.0
speed: 40.0
offset: 4.0
top:
thickness: 0.8
bottom:
firstLayer:
flowRate: 1.2
speed: 40.0
thickness: 0.4
shell:
thickness: 0.4

21
src/settings/infill.yml Normal file
View File

@ -0,0 +1,21 @@
0pct:
title: Hollow (0%)
innerInfill:
density: 0.0
10pct:
title: Light (10%)
innerInfill:
density: 10.0
20pct:
title: Normal (20%)
innerInfill:
density: 20.0
50pct:
title: Dense (50%)
innerInfill:
density: 50.0
100pct:
title: Solid (100%)
innerInfill:
density: 100.0

View File

@ -1,40 +1,79 @@
_3Dison_plus:
title: 3Dison plus
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 227
y: 147
z: 150
bigbuilder3d:
title: Big Builder 3D
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 200
y: 200
z: 200
builder3d:
title: Builder 3D
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 200
y: 200
z: 200
bukobot:
title: Bukobot
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 200
y: 200
z: 200
cartesio:
title: Cartesio
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 200
y: 200
z: 200
colido_2_0_plus:
title: ColiDo 2.0 Plus
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 230
y: 150
z: 140
colido_compact:
title: ColiDo Compact
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 130
y: 130
z: 115
colido_diy:
title: ColiDo DIY
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 200
y: 200
z: 170
colido_m2020:
title: ColiDo M2020
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 200
y: 200
z: 200
colido_x3045:
title: ColiDo X3045
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 300
y: 300
@ -42,23 +81,27 @@ colido_x3045:
craftbot_plus:
title: CraftBot PLUS
heatedBed: true
filamentThickness: 1.75
filamentThickness: 2.85
dimensions:
x: 250
y: 200
z: 200
cyrus:
title: Cyrus
heatedBed: false
dimensions:
x: 195
y: 195
z: 200
delta_rostockmax:
title: Delta RostockMax
dimensions:
x: 0
y: 0
heatedBed: false
deltamaker:
title: Deltamaker
dimensions:
x: 0
y: 0
heatedBed: false
doodle_dream:
title: Doodle Dream
heatedBed: false
filamentThickness: 1.75
dimensions:
x: 120
@ -66,88 +109,211 @@ doodle_dream:
z: 80
eventorbot:
title: EventorBot
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 203
y: 250
z: 150
felix:
title: Felix
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 237
y: 244
z: 235
gigabot:
title: Gigabot
heatedBed: false
filamentThickness: 2.85
kossel:
title: Kossel
dimensions:
x: 0
y: 0
heatedBed: false
filamentThickness: 2.85
leapfrog_creatr:
title: LeapFrog Creatr
heatedBed: false
filamentThickness: 2.85
lulzbot_aO_101:
title: LulzBot AO-101
heatedBed: false
filamentThickness: 2.85
lulzbot_taz_4:
title: LulzBot TAZ 4
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 298
y: 275
z: 250
heatedBed: true
makerbot_generic:
title: Generic Makerbot Printer
heatedBed: false
filamentThickness: 2.85
makerbot_replicator2:
title: MakerBot Replicator2
heatedBed: false
filamentThickness: 2.85
makerbot_replicator2x:
title: MakerBot Replicator2x
heatedBed: true
filamentThickness: 2.85
makerbot_thingomatic:
title: MakerBot Thing-o-matic
heatedBed: false
filamentThickness: 2.85
makergear_m2:
title: MakerGear M2
heatedBed: false
filamentThickness: 2.85
makergear_prusa:
title: MakerGear Prusa
heatedBed: false
filamentThickness: 2.85
makibox:
title: Makibox
heatedBed: false
filamentThickness: 2.85
mamba3d:
title: Mamba3D
heatedBed: false
filamentThickness: 2.85
marlin_generic:
title: Generic Marlin Printer
heatedBed: false
filamentThickness: 2.85
minifactory:
title: miniFactory
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 150
y: 150
z: 155
heatedBed: true
orca_0_3:
title: Orca 0.3
heatedBed: false
filamentThickness: 2.85
ord_bot_hadron:
title: ORD Bot Hadron
heatedBed: false
filamentThickness: 2.85
printrbot:
title: Printrbot
heatedBed: false
filamentThickness: 2.85
printxel_3d:
title: Printxel 3D
heatedBed: false
filamentThickness: 2.85
prusa_i3:
title: Prusa I3
heatedBed: false
filamentThickness: 2.85
prusa_iteration_2:
title: Prusa Iteration 2
heatedBed: false
filamentThickness: 2.85
rapman:
title: RapMan
heatedBed: false
filamentThickness: 2.85
renkforce_rf100:
title: Renkforce RF10
title: Renkforce RF100
heatedBed: false
filamentThickness: 1.75
dimensions:
x: 100
y: 100
z: 100
reprappro_huxley:
title: RepRapPro Huxley
heatedBed: false
filamentThickness: 2.85
reprappro_mendel:
title: RepRapPro Mendel
heatedBed: false
filamentThickness: 2.85
rigidbot:
title: Rigidbot
heatedBed: false
filamentThickness: 2.85
robo_3d_printer:
title: RoBo 3D Printer
heatedBed: false
filamentThickness: 2.85
shapercube:
title: ShaperCube
heatedBed: false
filamentThickness: 2.85
tantillus:
title: Tantillus
heatedBed: false
filamentThickness: 2.85
ultimaker:
title: Ultimaker
title: Ultimaker Original
heatedBed: false
filamentThickness: 2.85
ultimaker2:
title: Ultimaker 2
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 223
y: 223
z: 205
ultimaker2_plus:
title: Ultimaker 2+
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 223
y: 223
z: 205
ultimaker2_plus_extended:
title: Ultimaker 2+ Extended
heatedBed: true
filamentThickness: 2.85
dimensions:
x: 223
y: 223
z: 305
ultimaker2go:
startCode: |-
M10000
M10000
M10001 X8 Y28 SDoodle3D heat up...
M109 S{temperature} ;set target temperature
{if heatedBed}M190 S{bedTemperature} ;set target bed temperature
G21 ;metric values
G90 ;absolute positioning
M107 ;start with the fan off
G28 ; home to endstops
G1 Z15 F9000 ;move the platform down 15mm
G92 E0 ;zero the extruded length
G1 F200 E10 ;extrude 10mm of feed stock
G92 E0 ;zero the extruded length again
G1 F9000
M10000
M10000
M10001 X8 Y28 SDoodle3D printing...
endCode: |-
M10000
M10000
M10001 X20 Y28 SDoodle3D done!
M107 ;fan off
G91 ;relative positioning
G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure
G1 Z+5.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more
G28 ;home the printer
M84 ;disable axes / steppers
G90 ;absolute positioning
M104 S0
{if heatedBed}M140 S0
title: Ultimaker 2 Go
heatedBed: false
filamentThickness: 2.85
dimensions:
x: 120
y: 120
@ -155,13 +321,47 @@ ultimaker2go:
ultimaker_original_plus:
title: Ultimaker Original Plus
heatedBed: true
filamentThickness: 2.85
vision_3d_printer:
title: Vision 3D Printer
heatedBed: false
filamentThickness: 2.85
wanhao_duplicator4:
title: Wanhao Duplicator 4
filamentThickness: 1.75
heatedBed: true
filamentThickness: 1.75
dimensions:
x: 210
y: 140
z: 140
wanhao_duplicator_i3_plus:
title: Wanhao Duplicator i3 Plus
heatedBed: false
filamentThickness: 1.75
wanhao_duplicator_i3_mini:
duplicator_i3_mini:
startCode: |-
M104 S{temperature}
G28
M109 S{temperature}
G90
M82
G1 Z10.0 F6000
G92 E0
G1 F200 E3
G92 E0
endCode: |-
M104 S0
G92 E1
G1 E-1 F300
G28 X0 Y0
M84
M82
M104 S0
title: Wanhao Duplicator i3 Mini
heatedBed: false
filamentThickness: 1.75
dimensions:
x: 120
y: 135
z: 120

View File

@ -1,9 +1,54 @@
low:
title: "Low"
thickness:
top: 0.30
bottom: 0.30
shell: 0.4
layerHeight: .2
innerShell:
speed: 80.0
outerShell:
speed: 70.0
outerInfill:
speed: 80.0
firstLayer:
speed: 70.0
innerInfill:
speed: 80.0
density: 10.0
medium:
title: "Medium"
layerHeight: .15
height:
thickness:
top: 0.45
bottom: 0.45
shell: 0.8
innerShell:
speed: 50.0
outerShell:
speed: 40.0
outerInfill:
speed: 50.0
firstLayer:
speed: 40.0
innerInfill:
speed: 80.0
density: 10.0
high:
title: "High"
thickness:
top: 0.60
bottom: 0.60
shell: 1.2
layerHeight: .1
innerShell:
speed: 40.0
outerShell:
speed: 30.0
outerInfill:
speed: 40.0
firstLayer:
speed: 30.0
innerInfill:
speed: 70.0
density: 20.0

View File

@ -1,26 +1,36 @@
import Shape from 'Doodle3D/clipper-js';
import * as THREE from 'three.js';
import Shape from '@doodle3d/clipper-js';
import { PRECISION } from '../constants.js';
const offsetOptions = {
const OFFSET_OPTIONS = {
jointType: 'jtRound',
miterLimit: 2.0,
roundPrecision: 0.25
roundPrecision: 0.25,
endType: 'etClosedPolygon'
};
export default function addBrim(slices, settings) {
console.log('add brim');
let {
brim: { size: brimSize },
nozzleDiameter
} = settings;
let { brim: { offset: brimOffset } } = settings.config;
brimOffset /= PRECISION;
nozzleDiameter /= PRECISION;
brimSize /= PRECISION;
const nozzleRadius = nozzleDiameter / 2;
const [firstLayer] = slices;
firstLayer.brim = firstLayer.parts.reduce((brim, { shape }) => {
brim.join(shape.offset(brimOffset, {
...offsetOptions,
const brim = firstLayer.parts.reduce((_brim, { shape }) => (
_brim.join(shape.offset(nozzleRadius, {
...OFFSET_OPTIONS,
endType: shape.closed ? 'etClosedPolygon' : 'etOpenRound'
}));
return brim;
}, new Shape([], true)).simplify('pftNonZero');
}))
), new Shape([], true)).simplify('pftNonZero');
firstLayer.brim = new Shape([], true);
for (let offset = 0; offset < brimSize; offset += nozzleDiameter) {
const brimPart = brim.offset(offset, OFFSET_OPTIONS);
firstLayer.brim = firstLayer.brim.join(brimPart);
}
}

View File

@ -1,11 +1,13 @@
import { PRECISION } from '../constants.js'
import { PRECISION } from '../constants.js';
import { divide } from './helpers/vector2.js';
export default function applyPrecision(shapes) {
for (let i = 0; i < shapes.length; i ++) {
const { closedShapes, openShapes } = shapes[i];
export default function applyPrecision(layers) {
for (let layer = 0; layer < layers.length; layer ++) {
const { fillShapes, lineShapesOpen, lineShapesClosed } = layers[layer];
scaleUpShape(closedShapes);
scaleUpShape(openShapes);
scaleUpShape(fillShapes);
scaleUpShape(lineShapesOpen);
scaleUpShape(lineShapesClosed);
}
}
@ -14,9 +16,7 @@ function scaleUpShape(shape) {
const path = shape[i];
for (let i = 0; i < path.length; i ++) {
const point = path[i];
point.copy(point.divideScalar(PRECISION));
path[i] = divide(path[i], PRECISION);
}
}
}

View File

@ -1,29 +1,28 @@
import * as THREE from 'three.js';
import { Z_OFFSET } from '../constants.js';
export default function calculateLayersIntersections(lines, settings) {
console.log('calculating layer intersections');
const {
dimensions: { z: dimensionsZ },
layerHeight
} = settings;
const { layerHeight, dimensions: { z: dimensionsZ } } = settings.config;
const numLayers = Math.floor((dimensionsZ - Z_OFFSET) / layerHeight);
const numLayers = Math.floor(dimensionsZ / layerHeight);
const layerIntersectionIndexes = Array.from(Array(numLayers)).map(() => []);
const layerIntersectionPoints = Array.from(Array(numLayers)).map(() => []);
const layerPoints = Array.from(Array(numLayers)).map(() => ({}));
const layerFaceIndexes = Array.from(Array(numLayers)).map(() => []);
for (let lineIndex = 0; lineIndex < lines.length; lineIndex ++) {
const line = lines[lineIndex].line;
const { line, faces } = lines[lineIndex];
const min = Math.ceil(Math.min(line.start.y, line.end.y) / layerHeight);
const max = Math.floor(Math.max(line.start.y, line.end.y) / layerHeight);
const min = Math.ceil((Math.min(line.start.y, line.end.y) - Z_OFFSET) / layerHeight);
const max = Math.floor((Math.max(line.start.y, line.end.y) - Z_OFFSET) / layerHeight);
for (let layerIndex = min; layerIndex <= max; layerIndex ++) {
if (layerIndex >= 0 && layerIndex < numLayers) {
const y = layerIndex * layerHeight + Z_OFFSET;
layerIntersectionIndexes[layerIndex].push(lineIndex);
const y = layerIndex * layerHeight;
let x, z;
let x;
let z;
if (line.start.y === line.end.y) {
x = line.start.x;
z = line.start.z;
@ -34,10 +33,14 @@ export default function calculateLayersIntersections(lines, settings) {
z = line.end.z * alpha + line.start.z * alpha1;
}
layerIntersectionPoints[layerIndex][lineIndex] = new THREE.Vector2(z, x);
layerPoints[layerIndex][lineIndex] = { x: z, y: x };
for (const faceIndex of faces) {
const layerFaceIndex = layerFaceIndexes[layerIndex];
if (!layerFaceIndex.includes(faceIndex)) layerFaceIndex.push(faceIndex);
}
}
}
}
return { layerIntersectionIndexes, layerIntersectionPoints };
return { layerPoints, layerFaceIndexes };
}

View File

@ -1,56 +1,70 @@
import * as THREE from 'three.js';
function addLine(geometry, lineLookup, lines, a, b) {
const index = lines.length;
lineLookup[`${a}_${b}`] = index;
lines.push({
line: new THREE.Line3(geometry.vertices[a], geometry.vertices[b]),
connects: [],
normals: [],
open: false
});
return index;
}
export default function createLines(geometry, settings, openClosed) {
console.log('constructing unique lines from geometry');
import * as vector2 from './helpers/vector2.js';
import * as vector3 from './helpers/vector3.js';
export default function createLines(geometry) {
const faces = [];
const lines = [];
const lineLookup = {};
for (let i = 0; i < geometry.faces.length; i ++) {
const face = geometry.faces[i];
const open = openClosed[i];
for (let i = 0; i < geometry.objectIndexes.length; i ++) {
const objectIndex = geometry.objectIndexes[i];
const { x: a, y: b, z: c } = getVertex(geometry.faces, i);
const normal = calculateNormal(geometry.vertices, a, b, c);
if (face.normal.y !== 1 && face.normal.y !== -1) {
const normal = new THREE.Vector2(face.normal.z, face.normal.x).normalize();
const lookupA = lineLookup[`${face.b}_${face.a}`];
const lookupB = lineLookup[`${face.c}_${face.b}`];
const lookupC = lineLookup[`${face.a}_${face.c}`];
// only add unique lines
// returns index of said line
const indexA = typeof lookupA !== 'undefined' ? lookupA : addLine(geometry, lineLookup, lines, face.a, face.b);
const indexB = typeof lookupB !== 'undefined' ? lookupB : addLine(geometry, lineLookup, lines, face.b, face.c);
const indexC = typeof lookupC !== 'undefined' ? lookupC : addLine(geometry, lineLookup, lines, face.c, face.a);
// set connecting lines (based on face)
lines[indexA].connects.push(indexB, indexC);
lines[indexB].connects.push(indexC, indexA);
lines[indexC].connects.push(indexA, indexB);
lines[indexA].normals.push(normal);
lines[indexB].normals.push(normal);
lines[indexC].normals.push(normal);
lines[indexA].open = open;
lines[indexB].open = open;
lines[indexC].open = open;
// skip faces that point up or down
if (normal.y > 0.999 || normal.y < -0.999) {
faces.push(null);
continue;
}
const indexA = addLine(geometry.vertices, lineLookup, lines, a, b, i);
const indexB = addLine(geometry.vertices, lineLookup, lines, b, c, i);
const indexC = addLine(geometry.vertices, lineLookup, lines, c, a, i);
const flatNormal = vector2.normalize({ x: normal.z, y: normal.x });
const lineIndexes = [indexA, indexB, indexC];
faces.push({ lineIndexes, flatNormal, objectIndex });
}
return lines;
return { lines, faces };
}
function addLine(vertices, lineLookup, lines, a, b, faceIndex) {
let index;
if (typeof lineLookup[`${b}_${a}`] !== 'undefined') {
index = lineLookup[`${b}_${a}`];
} else {
const start = getVertex(vertices, a);
const end = getVertex(vertices, b);
const line = { start, end };
const faces = [];
index = lines.length;
lineLookup[`${a}_${b}`] = index;
lines.push({ line, faces });
}
lines[index].faces.push(faceIndex);
return index;
}
function calculateNormal(vertices, a, b, c) {
a = getVertex(vertices, a);
b = getVertex(vertices, b);
c = getVertex(vertices, c);
const cb = vector3.subtract(c, b);
const ab = vector3.subtract(a, b);
const normal = vector3.normalize(vector3.cross(cb, ab));
return normal;
}
function getVertex(vertices, i) {
const i3 = i * 3;
return {
x: vertices[i3],
y: vertices[i3 + 1],
z: vertices[i3 + 2]
};
}

View File

@ -1,71 +0,0 @@
export default function detectOpenClosed(geometry) {
console.log('detecting open and closed lines');
const pools = getPools(geometry);
const openVertices = getOpenVertices(geometry);
const openFaces = [];
for (let i = 0; i < pools.length; i ++) {
const pool = pools[i];
const isOpen = pool.some(face => openVertices[face.a] || openVertices[face.b] || openVertices[face.c]);
if (isOpen) openFaces.splice(openFaces.length, 0, ...pool);
}
return geometry.faces.map(face => openFaces.includes(face));
}
function findPool(pools, faceA) {
for (let i = 0; i < pools.length; i ++) {
const pool = pools[i];
if (pool.find(faceB => faceA.a === faceB.a || faceA.a === faceB.b || faceA.a === faceB.c)) {
return pool;
}
}
const pool = [];
pools.push(pool);
return pool;
}
function getPools(geometry) {
const pools = [];
for (let i = 0; i < geometry.faces.length; i ++) {
const face = geometry.faces[i];
const pool = findPool(pools, face);
pool.push(face);
}
for (let i = 0; i < pools.length; i ++) {
const poolA = pools[i];
for (let j = i + 1; j < pools.length; j ++) {
const poolB = pools[j];
for (let k = 0; k < poolA.length; k ++) {
const faceA = poolA[k];
if (poolB.find(faceB => faceA.a === faceB.a || faceA.a === faceB.b || faceA.a === faceB.c)) {
poolA.splice(poolA.length, 0, ...poolB);
poolB.splice(0, poolB.length);
}
}
}
}
return pools.filter(pool => pool.length > 0);
}
function getOpenVertices(geometry) {
const vertices = Array(geometry.vertices.length).fill(0);
for (let i = 0; i < geometry.faces.length; i ++) {
const face = geometry.faces[i];
vertices[face.a] ++;
vertices[face.b] ++;
vertices[face.c] ++;
}
return vertices.map(numFaces => numFaces < 4);
}

View File

@ -1,80 +1,70 @@
import { PRECISION } from '../constants.js'
import { PRECISION } from '../constants.js';
import getFillTemplate from './getFillTemplate.js';
import Shape from 'Doodle3D/clipper-js';
export default function generateInfills(slices, settings) {
console.log('generating infills');
let {
layerHeight,
fill: { gridSize: fillGridSize },
bottom: { thickness: bottomThickness },
top: { thickness: topThickness },
nozzleDiameter,
fill: { overlap: infillOverlap }
} = settings.config;
innerInfill: { density },
thickness: {
top: topThickness,
bottom: bottomThickness
},
nozzleDiameter
} = settings;
fillGridSize /= PRECISION;
density /= 100;
nozzleDiameter /= PRECISION;
infillOverlap /= PRECISION;
const bottomSkinCount = Math.ceil(bottomThickness/layerHeight);
const topSkinCount = Math.ceil(topThickness/layerHeight);
const bidirectionalInfill = density < 0.8;
const infillGridSize = nozzleDiameter * (bidirectionalInfill ? 2 : 1) / density;
const bottomSkinCount = Math.ceil(bottomThickness / layerHeight);
const topSkinCount = Math.ceil(topThickness / layerHeight);
const nozzleRadius = nozzleDiameter / 2;
const hightemplateSize = Math.sqrt(2 * Math.pow(nozzleDiameter, 2));
const outerFillTemplateSize = nozzleDiameter;
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
let surroundingLayer;
if (layer - bottomSkinCount >= 0 && layer + topSkinCount < slices.length) {
const downSkin = slices[layer - bottomSkinCount].getOutline();
const upSkin = slices[layer + topSkinCount].getOutline();
const downSkin = slices[layer - bottomSkinCount].outline;
const upSkin = slices[layer + topSkinCount].outline;
surroundingLayer = upSkin.intersect(downSkin);
}
for (let i = 0; i < slice.parts.length; i ++) {
const even = (layer % 2 === 0);
const part = slice.parts[i];
if (!part.shape.closed) {
continue;
if (!part.closed) continue;
const innerShell = part.shell[part.shell.length - 1];
if (innerShell.paths.length === 0) continue;
const fillArea = innerShell.offset(-nozzleRadius);
let innerFillArea;
let outerFillArea;
if (surroundingLayer) {
outerFillArea = fillArea.difference(surroundingLayer).intersect(fillArea);
innerFillArea = fillArea.difference(outerFillArea);
} else {
outerFillArea = fillArea;
}
const outerLine = part.outerLine;
if (innerFillArea && innerFillArea.paths.length > 0) {
const bounds = innerFillArea.shapeBounds();
const innerFillTemplate = getFillTemplate(bounds, infillGridSize, bidirectionalInfill || even, bidirectionalInfill || !even);
if (outerLine.paths.length > 0) {
const inset = (part.innerLines.length > 0) ? part.innerLines[part.innerLines.length - 1] : outerLine;
part.innerFill.join(innerFillTemplate.intersect(innerFillArea));
}
const fillArea = inset.offset(-nozzleRadius);
let lowFillArea;
let highFillArea;
if (surroundingLayer) {
highFillArea = fillArea.difference(surroundingLayer);
if (outerFillArea.paths.length > 0) {
const bounds = outerFillArea.shapeBounds();
const outerFillTemplate = getFillTemplate(bounds, outerFillTemplateSize, even, !even);
if (infillOverlap > 0) {
highFillArea = highFillArea.offset(infillOverlap);
}
highFillArea = highFillArea.intersect(fillArea);
lowFillArea = fillArea.difference(highFillArea);
} else {
highFillArea = fillArea;
}
if (lowFillArea && lowFillArea.paths.length > 0) {
const bounds = lowFillArea.shapeBounds();
const lowFillTemplate = getFillTemplate(bounds, fillGridSize, true, true);
part.fill.join(lowFillTemplate.intersect(lowFillArea));
}
if (highFillArea.paths.length > 0) {
const bounds = highFillArea.shapeBounds();
const even = (layer % 2 === 0);
const highFillTemplate = getFillTemplate(bounds, hightemplateSize, even, !even);
part.fill.join(highFillTemplate.intersect(highFillArea));
}
part.outerFill.join(outerFillTemplate.intersect(outerFillArea));
}
}
}

View File

@ -1,6 +1,6 @@
import { PRECISION } from '../constants.js'
import { PRECISION } from '../constants.js';
const offsetOptions = {
const OFFSET_OPTIONS = {
jointType: 'jtSquare',
endType: 'etClosedPolygon',
miterLimit: 2.0,
@ -8,14 +8,17 @@ const offsetOptions = {
};
export default function generateInnerLines(slices, settings) {
console.log('generating outer lines and inner lines');
// need to scale up everything because of clipper rounding errors
let { layerHeight, nozzleDiameter, shell: { thickness: shellThickness } } = settings.config;
let {
nozzleDiameter,
thickness: { shell: shellThickness }
} = settings;
nozzleDiameter /= PRECISION;
shellThickness /= PRECISION;
const nozzleRadius = nozzleDiameter / 2;
const shells = Math.round(shellThickness / nozzleDiameter);
const numShells = Math.round(shellThickness / nozzleDiameter);
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
@ -23,25 +26,28 @@ export default function generateInnerLines(slices, settings) {
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (!part.shape.closed) continue;
if (!part.closed) continue;
const outerLine = part.shape.offset(-nozzleRadius, offsetOptions);
const outerLine = part.shape.offset(-nozzleRadius, OFFSET_OPTIONS);
if (outerLine.paths.length > 0) {
part.outerLine.join(outerLine);
if (outerLine.paths.length === 0) continue;
for (let shell = 1; shell < shells; shell += 1) {
const offset = shell * nozzleDiameter;
part.shell.push(outerLine);
const innerLine = outerLine.offset(-offset, offsetOptions);
// start with 1 because outerLine is the 1st (0) shell
for (let inset = 1; inset < numShells; inset += 1) {
const offset = inset * nozzleDiameter;
if (innerLine.paths.length > 0) {
part.innerLines.push(innerLine);
} else {
break;
}
const shell = outerLine.offset(-offset, OFFSET_OPTIONS);
if (shell.paths.length === 0) {
break;
} else {
part.shell.push(shell);
}
}
}
slice.parts = slice.parts.filter(part => !part.closed || part.shell.length !== 0);
}
}

View File

@ -0,0 +1,15 @@
import Shape from '@doodle3d/clipper-js';
export default function calculateOutlines(slices) {
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
slice.outline = slice.parts.reduce((shape, part) => {
if (part.closed) {
const [outerLine] = part.shell;
shape.join(outerLine);
}
return shape;
}, new Shape([], true));
}
}

View File

@ -1,78 +1,41 @@
import getFillTemplate from './getFillTemplate.js';
import Shape from 'Doodle3D/clipper-js';
import Shape from '@doodle3d/clipper-js';
import { PRECISION } from '../constants.js';
export default function generateSupport(slices, settings) {
console.log('generating support');
const PRECISION_SQUARED = Math.pow(PRECISION, 2);
if (!settings.config.support.enabled) return;
export default function generateSupport(slices, settings) {
if (!settings.support.enabled) return;
let {
layerHeight,
support: {
gridSize: supportGridSize,
margin: AcceptanceMargin,
plateSize: plateSize,
distanceY: DistanceY
},
support: { density, margin, minArea, distanceY },
nozzleDiameter
} = settings.config;
} = settings;
supportGridSize /= PRECISION;
supportMargin /= PRECISION;
plateSize /= PRECISION;
density /= 100;
margin /= PRECISION;
nozzleDiameter /= PRECISION;
var supportDistanceLayers = Math.max(Math.ceil(supportDistanceY / layerHeight), 1);
var supportAreas = new Shape([], true);
const infillGridSize = nozzleDiameter * 2 / density;
const supportDistanceLayers = Math.max(Math.ceil(distanceY / layerHeight), 1);
for (var layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
var currentSlice = slices[layer];
let supportArea = new Shape([], true);
if (supportAreas.length > 0) {
for (let layer = slices.length - 1 - supportDistanceLayers; layer >= 0; layer --) {
const currentLayer = slices[layer + supportDistanceLayers - 1];
const upSkin = slices[layer + supportDistanceLayers];
const downSkin = slices[layer - supportDistanceLayers];
if (layer >= supportDistanceLayers) {
var sliceSkin = slices[layer - supportDistanceLayers].getOutline();
sliceSkin = sliceSkin;
const neededSupportArea = upSkin.outline.difference(currentLayer.outline.offset(margin));
var supportAreasSlimmed = supportAreas.difference(sliceSkin.offset(supportMargin));
if (supportAreasSlimmed.area() < 100.0) {
supportAreas = supportAreas.difference(sliceSkin);
}
else {
supportAreas = supportAreasSlimmed;
}
}
if (neededSupportArea.totalArea() * PRECISION_SQUARED > minArea) supportArea = supportArea.union(neededSupportArea);
if (downSkin) supportArea = supportArea.difference(downSkin.outline.offset(margin));
var supportTemplate = getFillTemplate(supportAreas.bounds(), supportGridSize, true, true);
var supportFill = supportTemplate.intersect(supportAreas);
if (supportFill.length === 0) {
currentSlice.support = supportAreas.clone();
}
else {
currentSlice.support = supportFill;
}
}
const bounds = supportArea.shapeBounds();
const innerFillTemplate = getFillTemplate(bounds, infillGridSize, true, true);
var supportSkin = slices[layer + supportDistanceLayers - 1].getOutline();
var slice = slices[layer + supportDistanceLayers];
for (var i = 0; i < slice.parts.length; i ++) {
var slicePart = slice.parts[i];
if (slicePart.intersect.closed) {
var outerLine = slicePart.outerLine;
}
else {
var outerLine = slicePart.intersect.offset(supportAcceptanceMargin);
}
var overlap = supportSkin.offset(supportAcceptanceMargin).intersect(outerLine);
var overhang = outerLine.difference(overlap);
if (overlap.length === 0 || overhang.length > 0) {
supportAreas = supportAreas.join(overhang);
}
}
slices[layer].support = supportArea.clone().join(supportArea.intersect(innerFillTemplate));
slices[layer].supportOutline = supportArea;
}
}

View File

@ -1,8 +1,10 @@
import Shape from 'Doodle3D/clipper-js';
import Shape from '@doodle3d/clipper-js';
export default function getFillTemplate(bounds, size, even, uneven) {
export default function getFillTemplate(bounds, gridSize, even, uneven) {
const paths = [];
const size = Math.sqrt(2 * Math.pow(gridSize, 2));
const left = Math.floor(bounds.left / size) * size;
const right = Math.ceil(bounds.right / size) * size;
const top = Math.floor(bounds.top / size) * size;

View File

@ -0,0 +1,149 @@
import { distanceTo } from './vector2.js';
import { VERSION } from '../../constants.js';
export const MOVE = 'G';
export const M_COMMAND = 'M';
export const FAN_SPEED = 'S';
export const SPEED = 'F';
export const EXTRUDER = 'E';
export const POSITION_X = 'X';
export const POSITION_Y = 'Y';
export const POSITION_Z = 'Z';
export default class GCode {
constructor(settings) {
this._nozzleToFilamentRatio = 1;
this._gcode = [
`; ${JSON.stringify(settings)}`,
`; Generated with Doodle3D Slicer V${VERSION}`
];
this._currentValues = {};
this._nozzlePosition = { x: 0, y: 0 };
this._extruder = 0.0;
this._duration = 0.0;
this._isRetracted = false;
this._isFanOn = false;
}
_addGCode(command) {
this._gcode.push(command);
}
updateLayerHeight(layerHeight, nozzleDiameter, filamentThickness) {
const filamentSurfaceArea = Math.pow((filamentThickness / 2), 2) * Math.PI;
const lineSurfaceArea = nozzleDiameter * layerHeight;
this._nozzleToFilamentRatio = lineSurfaceArea / filamentSurfaceArea;
}
turnFanOn(fanSpeed) {
this._isFanOn = true;
const gcode = { [M_COMMAND]: 106 };
if (typeof fanSpeed !== 'undefined') gcode[FAN_SPEED] = fanSpeed;
this._addGCode(gcode);
return this;
}
turnFanOff() {
this._isFanOn = false;
this._addGCode({ [M_COMMAND]: 107 });
return this;
}
moveTo(x, y, z, { speed }) {
const newNozzlePosition = { x, y };
const lineLength = distanceTo(this._nozzlePosition, newNozzlePosition);
this._duration += lineLength / speed;
this._addGCode({
[MOVE]: 0,
[POSITION_X]: newNozzlePosition.x,
[POSITION_Y]: newNozzlePosition.y,
[POSITION_Z]: z,
[SPEED]: speed * 60
});
this._nozzlePosition = newNozzlePosition;
return this;
}
lineTo(x, y, z, { speed, flowRate }) {
const newNozzlePosition = { x, y };
const lineLength = distanceTo(this._nozzlePosition, newNozzlePosition);
this._extruder += this._nozzleToFilamentRatio * lineLength * flowRate;
this._duration += lineLength / speed;
this._addGCode({
[MOVE]: 1,
[POSITION_X]: newNozzlePosition.x,
[POSITION_Y]: newNozzlePosition.y,
[POSITION_Z]: z,
[SPEED]: speed * 60,
[EXTRUDER]: this._extruder
});
this._nozzlePosition = newNozzlePosition;
return this;
}
unRetract({ enabled, speed, minDistance, amount }) {
if (this._isRetracted && enabled) {
this._isRetracted = false;
if (this._extruder > minDistance) {
this._duration += amount / speed;
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: this._extruder,
[SPEED]: speed * 60
});
}
}
return this;
}
retract({ enabled, speed, minDistance, amount }) {
if (!this._isRetracted && enabled) {
this._isRetracted = true;
if (this._extruder > minDistance) {
this._duration += amount / speed;
this._addGCode({
[MOVE]: 0,
[EXTRUDER]: this._extruder - amount,
[SPEED]: speed * 60
});
}
}
return this;
}
addGCode(gcode, { temperature, bedTemperature, heatedBed }) {
gcode = gcode
.replace(/{temperature}/g, temperature)
.replace(/{if heatedBed}.*?\n/g, str => heatedBed ? str.replace(/{if heatedBed}/g, '') : '')
.replace(/{bedTemperature}/g, bedTemperature);
this._addGCode(gcode);
}
getGCode() {
return {
gcode: this._gcode,
duration: this._duration,
filament: this._extruder
};
}
}

View File

@ -0,0 +1,18 @@
import Shape from '@doodle3d/clipper-js';
export default class Slice {
constructor() {
this.parts = [];
}
add(shape, closed) {
const part = { shape, closed };
if (closed) {
part.shell = [];
part.innerFill = new Shape([], false);
part.outerFill = new Shape([], false);
}
this.parts.push(part);
}
}

View File

@ -0,0 +1,26 @@
export function hslToRgb(h, s, l) {
let r;
let g;
let b;
if (s === 0) {
r = g = b = l;
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hueToRgb(p, q, h + 1 / 3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1 / 3);
}
return [r, g, b];
}
function hueToRgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}

View File

@ -0,0 +1,197 @@
import { angle, subtract, distanceTo } from './vector2.js';
const graphs = new WeakMap();
export default function comb(polygons, start, end) {
if (!graphs.has(polygons)) graphs.set(polygons, createGraph(polygons));
let { edges, graph, points } = graphs.get(polygons);
points = [...points, start, end];
graph = [...graph];
const startNode = createNode(graph, points, edges, start);
const endNode = createNode(graph, points, edges, end);
let result;
if (graph[startNode].some(node => node.to === endNode)) {
result = [start, end];
} else {
const path = shortestPath(graph, startNode, endNode);
if (path) {
result = path.map(index => points[index]);
} else {
result = [start, end];
}
}
return result;
}
function createGraph(polygons) {
const points = [];
const edges = [];
const nextPoints = new WeakMap();
const previousPoints = new WeakMap();
for (let i = 0; i < polygons.length; i ++) {
const polygon = polygons[i];
for (let j = 0; j < polygon.length; j ++) {
const point = polygon[j];
const nextPoint = polygon[(j + 1) % polygon.length];
const previousPoint = polygon[(j - 1 + polygon.length) % polygon.length];
points.push(point);
edges.push([point, nextPoint]);
nextPoints.set(point, nextPoint);
previousPoints.set(point, previousPoint);
}
}
const graph = points.map(() => ([]));
for (let i = 0; i < points.length; i ++) {
const a = points[i];
for (let j = i + 1; j < points.length; j ++) {
const b = points[j];
const nextPoint = nextPoints.get(a);
const previousPoint = previousPoints.get(a);
if (!lineIsVisible(previousPoint, nextPoint, edges, a, b)) continue;
const distance = distanceTo(a, b);
const connectNodeA = graph[i];
connectNodeA.push({ to: j, distance });
const connectNodeB = graph[j];
connectNodeB.push({ to: i, distance });
}
}
return { graph, edges, points };
}
function createNode(graph, points, edges, point) {
const node = [];
const to = graph.length;
graph.push(node);
let previousPoint;
let nextPoint;
for (let j = 0; j < edges.length; j ++) {
const edge = edges[j];
if (pointOnLine(edge, point)) [previousPoint, nextPoint] = edge;
}
for (let i = 0; i < graph.length; i ++) {
const b = points[i];
if (!lineIsVisible(previousPoint, nextPoint, edges, point, b)) continue;
const distance = distanceTo(point, b);
node.push({ to: i, distance });
graph[i] = [...graph[i], { to, distance }];
}
return to;
}
function lineIsVisible(previousPoint, nextPoint, edges, a, b) {
if (b === nextPoint || b === previousPoint) return true;
if (previousPoint && nextPoint) {
const angleLine = angle(subtract(b, a));
const anglePrevious = angle(subtract(previousPoint, a));
const angleNext = angle(subtract(nextPoint, a));
if (betweenAngles(angleLine, anglePrevious, angleNext)) return false;
}
if (lineCrossesEdges(edges, a, b)) return false;
return true;
}
function lineCrossesEdges(edges, a, b) {
for (let i = 0; i < edges.length; i ++) {
const [c, d] = edges[i];
if (lineSegmentsCross(a, b, c, d)) return true;
}
return false;
}
function lineSegmentsCross(a, b, c, d) {
const denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x));
if (denominator === 0.0) return false;
const numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y));
const numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
if (numerator1 === 0.0 || numerator2 === 0.0) return false;
const r = numerator1 / denominator;
const s = numerator2 / denominator;
return (r > 0.0 && r < 1.0) && (s >= 0.0 && s <= 1.0);
}
const TAU = Math.PI * 2.0;
function normalizeAngle(a) {
a %= TAU;
return a > 0.0 ? a : a + TAU;
}
function betweenAngles(n, a, b) {
n = normalizeAngle(n);
a = normalizeAngle(a);
b = normalizeAngle(b);
return a < b ? a <= n && n <= b : a <= n || n <= b;
}
// dijkstra's algorithm
function shortestPath(graph, start, end) {
const distances = graph.map(() => Infinity);
distances[start] = 0;
const traverse = [];
const queue = [];
for (let i = 0; i < distances.length; i ++) {
queue.push(i);
}
while (queue.length > 0) {
let queueIndex;
let minDistance = Infinity;
for (let index = 0; index < queue.length; index ++) {
const nodeIndex = queue[index];
const distance = distances[nodeIndex];
if (distances[nodeIndex] < minDistance) {
queueIndex = index;
minDistance = distance;
}
}
const [nodeIndex] = queue.splice(queueIndex, 1);
const node = graph[nodeIndex];
for (let i = 0; i < node.length; i ++) {
const child = node[i];
const distance = distances[nodeIndex] + child.distance;
if (distance < distances[child.to]) {
distances[child.to] = distance;
traverse[child.to] = nodeIndex;
}
}
}
if (!traverse.hasOwnProperty(end)) return null;
const path = [end];
let nodeIndex = end;
do {
nodeIndex = traverse[nodeIndex];
path.push(nodeIndex);
} while (nodeIndex !== start);
return path.reverse();
}
function pointOnLine([a, b], point) {
return (a.x - point.x) * (a.y - point.y) === (b.x - point.x) * (b.y - point.y);
}

View File

@ -0,0 +1,34 @@
export const subtract = (a, b) => ({
x: a.x - b.x,
y: a.y - b.y
});
export const add = (a, b) => ({
x: a.x + b.x,
y: a.y + b.y
});
export const scale = (v, factor) => ({
x: v.x * factor,
y: v.y * factor
});
export const divide = (v, factor) => ({
x: v.x / factor,
y: v.y / factor
});
export const normal = (v) => ({
x: -v.y,
y: v.x
});
export const equals = (a, b) => a.x === b.x && a.y === b.y;
export const almostEquals = (a, b) => Math.abs(a.x - b.x) < 0.001 && Math.abs(a.y - b.y) < 0.001;
export const dot = (a, b) => a.x * b.x + a.y * b.y;
export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y);
export const distanceTo = (a, b) => length(subtract(a, b));
export const angle = (v) => Math.atan2(v.y, v.x);
export const normalize = (v) => {
const l = length(v);
return {
x: v.x / l,
y: v.y / l
};
};

View File

@ -0,0 +1,38 @@
export const subtract = (a, b) => ({
x: a.x - b.x,
y: a.y - b.y,
z: a.z - b.z
});
export const add = (a, b) => ({
x: a.x + b.x,
y: a.y + b.y,
z: a.z + b.z
});
export const scale = (v, factor) => ({
x: v.x * factor,
y: v.y * factor,
z: v.z * factor
});
export const divide = (v, factor) => ({
x: v.x / factor,
y: v.y / factor,
z: v.z / factor
});
export const cross = (a, b) => ({
x: a.y * b.z - a.z * b.y,
y: a.z * b.x - a.x * b.z,
z: a.x * b.y - a.y * b.x
});
export const equals = (a, b) => a.x === b.x && a.y === b.y && a.z === b.z;
export const almostEquals = (a, b) => Math.abs(a.x - b.x) < 0.001 && Math.abs(a.y - b.y) < 0.001 && Math.abs(a.z - b.z) < 0.001;
export const length = (v) => Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
export const distanceTo = (a, b) => length(subtract(a, b));
export const normalize = (v) => {
const l = length(v);
return {
x: v.x / l,
y: v.y / l,
z: v.z / l
};
};

View File

@ -1,122 +1,163 @@
import * as THREE from 'three.js';
import Shape from 'Doodle3D/clipper-js';
export default function intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings) {
console.log('generating slices');
import { subtract, normal, normalize, dot, almostEquals } from './helpers/vector2.js';
export default function intersectionsToShapes(layerPoints, layerFaceIndexes, faces, openObjectIndexes) {
const layers = [];
for (let layer = 1; layer < layerIntersectionIndexes.length; layer ++) {
const intersectionIndexes = layerIntersectionIndexes[layer];
const intersectionPoints = layerIntersectionPoints[layer];
for (let layer = 0; layer < layerPoints.length; layer ++) {
const fillShapes = [];
const lineShapesOpen = [];
const lineShapesClosed = [];
if (intersectionIndexes.length === 0) continue;
const points = layerPoints[layer];
const faceIndexes = layerFaceIndexes[layer];
const closedShapes = [];
const openShapes = [];
for (let i = 0; i < intersectionIndexes.length; i ++) {
let index = intersectionIndexes[i];
if (faceIndexes.length === 0) continue;
if (typeof intersectionPoints[index] === 'undefined') continue;
const shapes = {};
const shape = [];
const startConnects = {};
const endConnects = {};
const firstPoints = [index];
const { open: openGeometry } = lines[index];
let isFirstPoint = true;
let openShape = true;
for (let i = 0; i < faceIndexes.length; i ++) {
const faceIndex = faceIndexes[i];
const { lineIndexes, flatNormal, objectIndex } = faces[faceIndex];
while (index !== -1) {
const intersection = intersectionPoints[index];
// uppercase X and Y because clipper vector
shape.push(intersection);
const a = lineIndexes[0];
const b = lineIndexes[1];
const c = lineIndexes[2];
delete intersectionPoints[index];
const connects = lines[index].connects;
const faceNormals = lines[index].normals;
for (let i = 0; i < connects.length; i ++) {
index = connects[i];
if (firstPoints.includes(index) && shape.length > 2) {
openShape = false;
index = -1;
break;
}
// Check if index has an intersection or is already used
if (typeof intersectionPoints[index] !== 'undefined') {
const faceNormal = faceNormals[Math.floor(i / 2)];
const a = new THREE.Vector2(intersection.x, intersection.y);
const b = new THREE.Vector2(intersectionPoints[index].x, intersectionPoints[index].y);
// can't calculate normal between points if distance is smaller as 0.0001
if ((faceNormal.x === 0 && faceNormal.y === 0) || a.distanceTo(b) < 0.0001) {
if (isFirstPoint) {
firstPoints.push(index);
}
delete intersectionPoints[index];
connects.push(...lines[index].connects);
faceNormals.push(...lines[index].normals);
index = -1;
} else {
// make sure the path goes the right direction
// THREE.Vector2.normal is not yet implimented
// const normal = a.sub(b).normal().normalize();
const normal = a.sub(b);
normal.set(-normal.y, normal.x).normalize();
if (normal.dot(faceNormal) > 0) {
break;
} else {
index = -1;
}
}
} else {
index = -1;
}
}
isFirstPoint = false;
}
if (openShape) {
index = firstPoints[0];
while (index !== -1) {
if (!firstPoints.includes(index)) {
const intersection = intersectionPoints[index];
shape.unshift(intersection);
delete intersectionPoints[index];
}
const connects = lines[index].connects;
for (let i = 0; i < connects.length; i ++) {
index = connects[i];
if (typeof intersectionPoints[index] !== 'undefined') {
break;
} else {
index = -1;
}
}
}
}
if (openGeometry) {
if (!openShape) shape.push(shape[0].clone());
openShapes.push(shape);
let pointA;
let pointB;
if (points[a] && points[b]) {
pointA = a;
pointB = b;
} else if (points[b] && points[c]) {
pointA = b;
pointB = c;
} else if (points[c] && points[a]) {
pointA = c;
pointB = a;
} else {
closedShapes.push(shape);
// should never happen
continue;
}
const segmentNormal = normalize(normal(subtract(points[pointA], points[pointB])));
if (dot(segmentNormal, flatNormal) < 0) {
const temp = pointB;
pointB = pointA;
pointA = temp;
}
if (endConnects[pointA]) {
const lineSegment = endConnects[pointA];
delete endConnects[pointA];
if (startConnects[pointB]) {
if (startConnects[pointB] === lineSegment) {
delete startConnects[pointB];
lineSegment.push(pointB);
} else {
lineSegment.push(...startConnects[pointB]);
endConnects[lineSegment[lineSegment.length - 1]] = lineSegment;
shapes[objectIndex].splice(shapes[objectIndex].indexOf(startConnects[pointB]), 1);
}
} else {
lineSegment.push(pointB);
endConnects[pointB] = lineSegment;
}
} else if (startConnects[pointB]) {
const lineSegment = startConnects[pointB];
delete startConnects[pointB];
if (endConnects[pointA]) {
lineSegment.unshift(...endConnects[pointA]);
startConnects[lineSegment[0]] = lineSegment;
shapes[objectIndex].splice(shapes[objectIndex].indexOf(endConnects[pointA]), 1);
} else {
lineSegment.unshift(pointA);
startConnects[pointA] = lineSegment;
}
} else {
const lineSegment = [pointA, pointB];
startConnects[pointA] = lineSegment;
endConnects[pointB] = lineSegment;
if (!shapes[objectIndex]) shapes[objectIndex] = [];
shapes[objectIndex].push(lineSegment);
}
}
layers.push({ closedShapes, openShapes });
for (const objectIndex in shapes) {
const shape = shapes[objectIndex]
.map(lineSegment => lineSegment.map(pointIndex => points[pointIndex]))
.filter(lineSegment => lineSegment.some(point => !almostEquals(lineSegment[0], point)));
const openShape = openObjectIndexes[objectIndex];
const connectPoints = [];
for (let pathIndex = 0; pathIndex < shape.length; pathIndex ++) {
const path = shape[pathIndex];
if (almostEquals(path[0], path[path.length - 1])) {
if (openShape) {
lineShapesClosed.push(path);
} else {
fillShapes.push(path);
}
continue;
}
let shapeStartPoint = path[0];
const connectNext = connectPoints.find(({ point }) => almostEquals(point, shapeStartPoint));
if (connectNext) {
connectNext.next = pathIndex;
} else {
connectPoints.push({ point: shapeStartPoint, next: pathIndex, previous: -1 });
}
let shapeEndPoint = path[path.length - 1];
const connectPrevious = connectPoints.find(({ point }) => almostEquals(point, shapeEndPoint));
if (connectPrevious) {
connectPrevious.previous = pathIndex;
} else {
connectPoints.push({ point: shapeEndPoint, next: -1, previous: pathIndex });
}
}
connectPoints.sort((a, b) => b.previous - a.previous);
while (connectPoints.length !== 0) {
let { next, previous } = connectPoints.pop();
const line = [];
if (previous !== -1) line.push(...shape[previous]);
while (true) {
const pointIndex = connectPoints.findIndex(point => point.previous === next);
if (pointIndex === -1) break;
const point = connectPoints[pointIndex];
line.push(...shape[point.previous]);
connectPoints.splice(pointIndex, 1);
if (point.next === -1) break;
if (point.next === previous) break;
next = point.next;
}
if (openShape) {
if (almostEquals(line[0], line[line.length - 1])) {
lineShapesClosed.push(line);
} else {
lineShapesOpen.push(line);
}
} else {
fillShapes.push(line);
}
}
}
layers.push({ fillShapes, lineShapesOpen, lineShapesClosed });
}
return layers;

View File

@ -1,20 +1,27 @@
import * as THREE from 'three.js';
import Shape from 'Doodle3D/clipper-js';
import { distanceTo } from './helpers/vector2.js';
import Shape from '@doodle3d/clipper-js';
export default function optimizePaths(slices, settings) {
console.log('optimize paths');
const start = new THREE.Vector2(0, 0);
export default function optimizePaths(slices) {
let start = { x: 0, y: 0 };
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
if (typeof slice.brim !== 'undefined' && slice.brim.paths.length > 0) {
slice.brim = optimizeShape(slice.brim, start);
start.copy(slice.brim.lastPoint(true));
start = slice.brim.lastPoint(true);
}
const parts = [];
const boundingBoxes = new WeakMap();
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
const shape = part.closed ? part.shell[0] : part.shape;
const bounds = shape.shapeBounds();
boundingBoxes.set(part, bounds);
}
while (slice.parts.length > 0) {
let closestDistance = Infinity;
@ -22,20 +29,14 @@ export default function optimizePaths(slices, settings) {
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
const bounds = boundingBoxes.get(part);
let bounds;
if (part.shape.closed) {
bounds = part.outerLine.shapeBounds();
} else {
bounds = part.shape.shapeBounds();
}
const topDistance = bounds.top - start.y;
const bottomDistance = start.y - bounds.bottom;
const leftDistance = bounds.left - start.x;
const rightDistance = start.x - bounds.right;
const top = bounds.top - start.y;
const bottom = start.y - bounds.bottom;
const left = bounds.left - start.x;
const right = start.x - bounds.right;
const distance = Math.max(top, bottom, left, right);
const distance = Math.max(topDistance, bottomDistance, leftDistance, rightDistance);
if (distance < closestDistance) {
closestDistance = distance;
@ -43,52 +44,50 @@ export default function optimizePaths(slices, settings) {
}
}
const part = slice.parts.splice(closestPart, 1)[0];
const [part] = slice.parts.splice(closestPart, 1);
parts.push(part);
if (part.shape.closed) {
if (part.outerLine.paths.length > 0) {
part.outerLine = optimizeShape(part.outerLine, start);
start.copy(part.outerLine.lastPoint(true));
if (part.closed) {
for (let i = 0; i < part.shell.length; i ++) {
const shell = part.shell[i];
if (shell.paths.length === 0) continue;
part.shell[i] = optimizeShape(shell, start);
start = part.shell[i].lastPoint(true);
}
for (let i = 0; i < part.innerLines.length; i ++) {
const innerLine = part.innerLines[i];
if (innerLine.paths.length > 0) {
part.innerLines[i] = optimizeShape(innerLine, start);
start.copy(part.innerLines[i].lastPoint(true));
}
if (part.outerFill.paths.length > 0) {
part.outerFill = optimizeShape(part.outerFill, start);
start = part.outerFill.lastPoint(true);
}
if (part.fill.paths.length > 0) {
part.fill = optimizeShape(part.fill, start);
start.copy(part.fill.lastPoint(true));
if (part.innerFill.paths.length > 0) {
part.innerFill = optimizeShape(part.innerFill, start);
start = part.innerFill.lastPoint(true);
}
} else {
part.shape = optimizeShape(part.shape, start);
start.copy(part.shape.lastPoint(true));
start = part.shape.lastPoint(true);
}
}
slice.parts = parts;
if (typeof slice.support !== 'undefined' && slice.support.length > 0) {
if (typeof slice.support !== 'undefined' && slice.support.paths.length > 0) {
slice.support = optimizeShape(slice.support, start);
start.copy(slice.support.lastPoint(true));
start = slice.support.lastPoint(true);
}
}
}
function optimizeShape(shape, start) {
start = start.clone();
const inputPaths = shape.mapToLower();
const inputPaths = shape.mapToLower().filter(path => path.length > 0);
const optimizedPaths = [];
const donePaths = [];
while (optimizedPaths.length !== inputPaths.length) {
let minLength = false;
let minLength = Infinity;
let reverse;
let minPath;
let offset;
@ -101,9 +100,8 @@ function optimizeShape(shape, start) {
if (shape.closed) {
for (let j = 0; j < path.length; j += 1) {
const point = new THREE.Vector2().copy(path[j]);
const length = point.sub(start).length();
if (minLength === false || length < minLength) {
const length = distanceTo(path[j], start);
if (length < minLength) {
minPath = path;
minLength = length;
offset = j;
@ -111,17 +109,15 @@ function optimizeShape(shape, start) {
}
}
} else {
const startPoint = new THREE.Vector2().copy(path[0]);
const lengthToStart = startPoint.sub(start).length();
if (minLength === false || lengthToStart < minLength) {
const lengthToStart = distanceTo(path[0], start);
if (lengthToStart < minLength) {
minPath = path;
minLength = lengthToStart;
reverse = false;
pathIndex = i;
}
const endPoint = new THREE.Vector2().copy(path[path.length - 1]);
const lengthToEnd = endPoint.sub(start).length();
const lengthToEnd = distanceTo(path[path.length - 1], start);
if (lengthToEnd < minLength) {
minPath = path;
minLength = lengthToEnd;
@ -131,20 +127,15 @@ function optimizeShape(shape, start) {
}
}
let point;
if (shape.closed) {
minPath = minPath.concat(minPath.splice(0, offset));
point = minPath[0];
start = minPath[0];
} else {
if (reverse) {
minPath.reverse();
}
point = minPath[minPath.length - 1];
if (reverse) minPath.reverse();
start = minPath[minPath.length - 1];
}
donePaths.push(pathIndex);
start.copy(point);
optimizedPaths.push(minPath);
}

View File

@ -1,24 +1,21 @@
import * as THREE from 'three.js';
import { PRECISION } from '../constants.js';
const inversePrecision = 1 / PRECISION;
export default function removePrecision(slices) {
console.log('remove precision');
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (part.shape.closed) {
part.outerLine.scaleDown(inversePrecision);
for (let i = 0; i < part.innerLines.length; i ++) {
const innerLine = part.innerLines[i];
if (part.closed) {
for (let i = 0; i < part.shell.length; i ++) {
const innerLine = part.shell[i];
innerLine.scaleDown(inversePrecision);
}
part.fill.scaleDown(inversePrecision);
part.innerFill.scaleDown(inversePrecision);
part.outerFill.scaleDown(inversePrecision);
} else {
part.shape.scaleDown(inversePrecision);
}

View File

@ -1,42 +1,43 @@
import Shape from 'Doodle3D/clipper-js';
import Slice from '../Slice.js';
import Shape from '@doodle3d/clipper-js';
import Slice from './helpers/Slice.js';
import { CLEAN_DELTA, PRECISION } from '../constants.js';
import { PRECISION, MIN_AREA } from '../constants.js';
const cleanDelta = CLEAN_DELTA / PRECISION;
export default function shapesToSlices(shapes, settings) {
export default function shapesToSlices(shapes) {
const sliceLayers = [];
for (let layer = 0; layer < shapes.length; layer ++) {
let { closedShapes, openShapes } = shapes[layer];
let { fillShapes, lineShapesOpen, lineShapesClosed } = shapes[layer];
closedShapes = new Shape(closedShapes, true, true, true, true)
fillShapes = new Shape(fillShapes, true, true, true, true)
.fixOrientation()
.simplify('pftNonZero')
.clean(cleanDelta)
.seperateShapes();
.clean(1)
.thresholdArea(MIN_AREA / Math.pow(PRECISION, 2))
.separateShapes();
openShapes = new Shape(openShapes, false, true, true, true);
// .clean(cleanDelta);
lineShapesClosed = new Shape(lineShapesClosed, true, true, true, true)
.clean(1);
lineShapesOpen = new Shape(lineShapesOpen, false, true, true, true);
// .clean(1);
// TODO
// Cleaning is actually wanted here but there is a bug in the clean function
// https://sourceforge.net/p/jsclipper/tickets/16/
// Enable cleaning when https://sourceforge.net/p/jsclipper/tickets/24/ is fixed
const slice = new Slice();
for (let i = 0; i < closedShapes.length; i ++) {
const closedShape = closedShapes[i];
slice.add(closedShape);
for (let i = 0; i < fillShapes.length; i ++) {
const fillShape = fillShapes[i];
if (fillShape.paths.length === 0) continue;
// if (openShapes.path.length > 0) {
// openShapes = openShapes.difference(closedShape);
// }
slice.add(fillShape, true);
if (lineShapesClosed.paths.length > 0) lineShapesClosed = lineShapesClosed.difference(fillShape);
if (lineShapesOpen.paths.length > 0) lineShapesOpen = lineShapesOpen.difference(fillShape);
}
if (openShapes.paths.length > 0) {
slice.add(openShapes);
}
if (lineShapesClosed.paths.length > 0) slice.add(lineShapesClosed, false);
if (lineShapesOpen.paths.length > 0) slice.add(lineShapesOpen, false);
sliceLayers.push(slice);
}

View File

@ -2,64 +2,121 @@ import calculateLayersIntersections from './calculateLayersIntersections.js';
import createLines from './createLines.js';
import generateInfills from './generateInfills.js';
import generateInnerLines from './generateInnerLines.js';
import generateOutlines from './generateOutlines.js';
import generateSupport from './generateSupport.js';
import intersectionsToShapes from './intersectionsToShapes.js';
import addBrim from './addBrim.js';
import optimizePaths from './optimizePaths.js';
import shapesToSlices from './shapesToSlices.js';
import slicesToGCode from './slicesToGCode.js';
import detectOpenClosed from './detectOpenClosed.js';
import applyPrecision from './applyPrecision.js';
import { hslToRgb } from './helpers/color.js';
import removePrecision from './removePrecision.js';
export default function(geometry, settings) {
const totalStages = 15;
let current = 0;
const progressMessage = () => {
current++;
postMessage({ message: 'PROGRESS', data: { done: current, total: totalStages } });
export default function slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
const total = 11;
let done = -1;
const updateProgress = action => {
done ++;
if (onProgress) onProgress({ progress: { done, total, action } });
};
geometry.mergeVertices();
progressMessage();
geometry.computeFaceNormals();
progressMessage();
// get unique lines from geometry;
const openClosed = detectOpenClosed(geometry);
progressMessage();
const lines = createLines(geometry, settings, openClosed);
progressMessage();
updateProgress('Constructing unique lines from geometry');
const { lines, faces } = createLines(geometry, settings);
const {
layerIntersectionIndexes,
layerIntersectionPoints
} = calculateLayersIntersections(lines, settings);
progressMessage();
updateProgress('Calculating layer intersections');
const { layerPoints, layerFaceIndexes } = calculateLayersIntersections(lines, settings);
const shapes = intersectionsToShapes(layerIntersectionIndexes, layerIntersectionPoints, lines, settings);
progressMessage();
updateProgress('Constructing shapes from intersections');
const shapes = intersectionsToShapes(layerPoints, layerFaceIndexes, faces, openObjectIndexes, settings);
applyPrecision(shapes);
progressMessage();
updateProgress('Constructing slices from shapes');
const slices = shapesToSlices(shapes, settings);
progressMessage();
updateProgress('Generating inner lines');
generateInnerLines(slices, settings);
progressMessage();
updateProgress('Generating out lines');
generateOutlines(slices, settings);
updateProgress('Generating infills');
generateInfills(slices, settings);
progressMessage();
updateProgress('Generating support');
generateSupport(slices, settings);
progressMessage();
updateProgress('Adding brim');
addBrim(slices, settings);
progressMessage();
updateProgress('Optimizing paths');
optimizePaths(slices, settings);
progressMessage();
removePrecision(slices);
progressMessage();
removePrecision(slices);
updateProgress('Constructing gcode');
const gcode = slicesToGCode(slices, settings);
progressMessage();
updateProgress('Finished');
if (constructLinePreview) gcode.linePreview = createGcodeGeometry(gcode.gcode);
gcode.gcode = new Blob([gcodeToString(gcode.gcode)], { type: 'text/plain' });
return gcode;
}
const PRECISION = 1000;
function toFixedTrimmed(value) {
return (Math.round(value * PRECISION) / PRECISION).toString();
}
function gcodeToString(gcode) {
const currentValues = {};
return gcode.reduce((string, command) => {
if (typeof command === 'string') {
string += command;
} else {
let first = true;
for (const action in command) {
const value = toFixedTrimmed(command[action]);
const currentValue = currentValues[action];
if (first) {
string += `${action}${value}`;
first = false;
} else if (currentValue !== value) {
string += ` ${action}${value}`;
currentValues[action] = value;
}
}
}
string += '\n';
return string;
}, '');
}
const MAX_SPEED = 100 * 60;
function createGcodeGeometry(gcode) {
const positions = [];
const colors = [];
let lastPoint = [0, 0, 0];
for (let i = 0; i < gcode.length; i ++) {
const command = gcode[i];
if (typeof command === 'string') continue;
const { G, F, X, Y, Z } = command;
if (X || Y || Z) {
if (G === 1) {
positions.push(lastPoint.Y, lastPoint.Z, lastPoint.X);
positions.push(Y, Z, X);
const color = (G === 0) ? [0, 1, 0] : hslToRgb(F / MAX_SPEED, 0.5, 0.5);
colors.push(...color, ...color);
}
lastPoint = { X, Y, Z };
}
}
return {
positions: new Float32Array(positions),
colors: new Float32Array(colors)
};
}

View File

@ -1,49 +1,88 @@
import GCode from '../GCode.js';
import GCode from './helpers/GCode.js';
import comb from './helpers/comb.js';
import { Z_OFFSET } from '../constants.js';
const PROFILE_TYPES = ['support', 'innerShell', 'outerShell', 'innerInfill', 'outerInfill', 'brim'];
export default function slicesToGCode(slices, settings) {
console.log('slices to gcode');
const {
layerHeight,
filamentThickness,
nozzleDiameter,
retraction,
travel,
combing
} = settings;
const gcode = new GCode(settings);
gcode.updateLayerHeight(Z_OFFSET, nozzleDiameter, filamentThickness);
if (settings.startCode) gcode.addGCode(settings.startCode, settings);
const defaultProfile = {
travelProfile: travel,
retractionProfile: retraction
};
let isFirstLayer = true;
for (let layer = 0; layer < slices.length; layer ++) {
const slice = slices[layer];
const z = layer * layerHeight + Z_OFFSET;
if (layer === 1) {
gcode.updateLayerHeight(layerHeight, nozzleDiameter, filamentThickness);
gcode.turnFanOn();
gcode.bottom = false;
isFirstLayer = false;
}
const profiles = PROFILE_TYPES.reduce((_profiles, profileType) => {
_profiles[profileType] = {
...defaultProfile,
lineProfile: isFirstLayer ? settings.firstLayer : settings[profileType]
};
return _profiles;
}, {});
if (typeof slice.brim !== 'undefined') {
pathToGCode(gcode, slice.brim, true, true, layer, 'brim');
pathToGCode(null, false, gcode, slice.brim, true, true, z, profiles.brim);
}
for (let i = 0; i < slice.parts.length; i ++) {
const part = slice.parts[i];
if (part.shape.closed) {
pathToGCode(gcode, part.outerLine, false, true, layer, 'outerLine');
if (part.closed) {
const outline = part.shell[0].mapToLower();
for (let i = 0; i < part.innerLines.length; i ++) {
const innerLine = part.innerLines[i];
pathToGCode(gcode, innerLine, false, false, layer, 'innerLine');
for (let i = 0; i < part.shell.length; i ++) {
const shell = part.shell[i];
const isOuterShell = i === 0;
const unRetract = isOuterShell;
const profile = isOuterShell ? profiles.outerShell : profiles.innerShell;
pathToGCode(outline, combing, gcode, shell, false, unRetract, z, profile);
}
pathToGCode(gcode, part.fill, true, false, layer, 'fill');
pathToGCode(outline, combing, gcode, part.outerFill, false, false, z, profiles.outerInfill);
pathToGCode(outline, combing, gcode, part.innerFill, true, false, z, profiles.innerInfill);
} else {
const retract = !(slice.parts.length === 1 && typeof slice.support === 'undefined');
pathToGCode(gcode, part.shape, retract, retract, layer, 'outerLine');
pathToGCode(null, false, gcode, part.shape, retract, retract, z, profiles.outerShell);
}
}
if (typeof slice.support !== 'undefined') {
pathToGCode(gcode, slice.support, true, true, layer, 'support');
const supportOutline = slice.supportOutline.mapToLower();
pathToGCode(supportOutline, combing, gcode, slice.support, true, true, z, profiles.support);
}
}
if (settings.endCode) gcode.addGCode(settings.endCode, settings);
return gcode.getGCode();
}
function pathToGCode(gcode, shape, retract, unRetract, layer, type) {
function pathToGCode(outline, combing, gcode, shape, retract, unRetract, z, profiles) {
const { lineProfile, travelProfile, retractionProfile } = profiles;
const { closed } = shape;
const paths = shape.mapToLower();
@ -55,20 +94,26 @@ function pathToGCode(gcode, shape, retract, unRetract, layer, type) {
const point = line[i % line.length];
if (i === 0) {
// TODO
// moveTo should impliment combing
gcode.moveTo(point.x, point.y, layer);
if (combing) {
const combPath = comb(outline, gcode._nozzlePosition, point);
for (let i = 0; i < combPath.length; i ++) {
const combPoint = combPath[i];
gcode.moveTo(combPoint.x, combPoint.y, z, travelProfile);
}
} else {
gcode.moveTo(point.x, point.y, z, travelProfile);
}
if (unRetract) {
gcode.unRetract();
gcode.unRetract(retractionProfile);
}
} else {
gcode.lineTo(point.x, point.y, layer, type);
gcode.lineTo(point.x, point.y, z, lineProfile);
}
}
}
if (retract) {
gcode.retract();
gcode.retract(retractionProfile);
}
}

127
src/slicer.js Normal file
View File

@ -0,0 +1,127 @@
import * as THREE from 'three';
import slice from './sliceActions/slice.js';
import SlicerWorker from './slicer.worker.js';
export function sliceMesh(settings, mesh, sync = false, constructLinePreview = false, onProgress) {
if (!mesh || !mesh.isMesh) {
throw new Error('Provided mesh is not intance of THREE.Mesh');
}
mesh.updateMatrix();
const { geometry, matrix, material } = mesh;
return sliceGeometry(settings, geometry, material, matrix, sync, constructLinePreview, onProgress);
}
export function sliceGeometry(settings, geometry, materials, matrix, sync = false, constructLinePreview = false, onProgress) {
if (!geometry) {
throw new Error('Missing required geometry argument');
} else if (geometry.isBufferGeometry) {
geometry = new THREE.Geometry().fromBufferGeometry(geometry);
} else if (geometry.isGeometry) {
geometry = geometry.clone();
} else {
throw new Error('Geometry is not an instance of BufferGeometry or Geometry');
}
if (matrix && matrix.isMatrix4) geometry.applyMatrix(matrix);
const vertices = geometry.vertices.reduce((array, { x, y, z }, i) => {
const i3 = i * 3;
array[i3] = x;
array[i3 + 1] = y;
array[i3 + 2] = z;
return array;
}, new Float32Array(geometry.vertices.length * 3));
const faces = geometry.faces.reduce((array, { a, b, c }, i) => {
const i3 = i * 3;
array[i3] = a;
array[i3 + 1] = b;
array[i3 + 2] = c;
return array;
}, new Uint32Array(geometry.faces.length * 3));
const objectIndexes = geometry.faces.reduce((array, { materialIndex }, i) => {
array[i] = materialIndex;
return array;
}, new Uint8Array(geometry.faces.length));
if (faces.length === 0) throw new Error('Geometry does not contain any data');
geometry = { vertices, faces, objectIndexes };
const openObjectIndexes = materials instanceof Array ? materials.map(({ side }) => {
switch (side) {
case THREE.FrontSide:
return false;
case THREE.DoubleSide:
return true;
default:
return false;
}
}) : [false];
if (sync) {
return sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
} else {
return sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
}
}
function sliceSync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
const gcode = slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
if (gcode.linePreview) gcode.linePreview = constructLineGeometry(gcode.linePreview);
return gcode;
}
function sliceAsync(settings, geometry, openObjectIndexes, constructLinePreview, onProgress) {
return new Promise((resolve, reject) => {
// create the slicer worker
const slicerWorker = new SlicerWorker();
slicerWorker.addEventListener('error', event => {
slicerWorker.terminate();
reject(event);
});
// listen to messages send from worker
slicerWorker.addEventListener('message', (event) => {
const { message, data } = event.data;
switch (message) {
case 'SLICE': {
slicerWorker.terminate();
const { gcode } = data;
if (gcode.linePreview) gcode.linePreview = constructLineGeometry(gcode.linePreview);
resolve(gcode);
break;
}
case 'PROGRESS': {
if (typeof onProgress !== 'undefined') onProgress(data);
break;
}
default:
break;
}
});
const { vertices, faces, objectIndexes } = geometry;
const buffers = [vertices.buffer, faces.buffer, objectIndexes.buffer];
slicerWorker.postMessage({
message: 'SLICE',
data: { settings, geometry, openObjectIndexes, constructLinePreview }
}, buffers);
});
}
function constructLineGeometry(linePreview) {
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(linePreview.positions), 3));
geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(linePreview.colors), 3));
const material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
const mesh = new THREE.LineSegments(geometry, material);
return mesh;
}

34
src/slicer.worker.js Normal file
View File

@ -0,0 +1,34 @@
import 'core-js'; // polyfills
import slice from './sliceActions/slice.js';
const onProgress = progress => {
self.postMessage({
message: 'PROGRESS',
data: progress
});
};
self.addEventListener('message', (event) => {
const { message, data } = event.data;
switch (message) {
case 'SLICE': {
const { settings, geometry, constructLinePreview, openObjectIndexes } = data;
const gcode = slice(settings, geometry, openObjectIndexes, constructLinePreview, onProgress);
const buffers = [];
if (gcode.linePreview) {
buffers.push(gcode.linePreview.positions.buffer);
buffers.push(gcode.linePreview.colors.buffer);
}
self.postMessage({
message: 'SLICE',
data: { gcode }
}, buffers);
break;
}
default:
break;
}
}, false);

View File

@ -1,25 +0,0 @@
import Settings from './Settings.js';
import slice from './sliceActions/slice.js';
import * as THREE from 'three.js';
const loader = new THREE.JSONLoader();
self.addEventListener('message', (event) => {
const { message, data } = event.data;
switch (message) {
case 'SLICE': {
const { geometry: JSONGeometry, config } = data;
const { geometry } = new loader.parse(JSONGeometry.data);
const settings = new Settings(config);
const gcode = slice(geometry, settings);
self.postMessage({
message: 'SLICE',
data: { gcode }
});
break;
}
}
}, false);

92
webpack.config.js Normal file
View File

@ -0,0 +1,92 @@
const path = require('path');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const devMode = process.env.NODE_ENV !== 'production';
const analyzeBundle = process.env.ANALYZE_BUNDLE;
const babelLoader = {
loader: 'babel-loader',
options: {
presets: [
require('babel-preset-env'),
require('babel-preset-stage-0'),
require('babel-preset-react')
],
plugins: [
require('babel-plugin-transform-class-properties'),
require('babel-plugin-transform-object-rest-spread'),
require('babel-plugin-transform-runtime'),
require('babel-plugin-transform-es2015-classes')
],
babelrc: false
}
};
module.exports = {
entry: './index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: babelLoader
}, { // make THREE global available to three.js examples
test: /three\/examples\/.+\.js/,
use: 'imports-loader?THREE=three'
}, {
test: /\.yml$/,
use: 'yml-loader'
}, {
test: /\.worker\.js$/,
use: [{
loader: 'worker-loader',
options: {
inline: false,
name: '[name].js'
}
}, babelLoader]
}, {
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'file-loader',
options: { name: '[path][name].[ext]' }
},
...(!devMode ? [{
loader: 'image-webpack-loader',
options: {
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: false },
pngquant: { quality: '65-90', speed: 4 }
}
}] : [])]
}, {
test: /\.stl$/,
use: {
loader: 'file-loader'
}
}, {
test: /\.glsl$/,
use: ['raw-loader']
}
]
},
plugins: analyzeBundle ? [new BundleAnalyzerPlugin()] : [
new HTMLWebpackPlugin({
favicon: 'favicon.ico',
title: 'Doodle3D Slicer',
template: require('html-webpack-template'),
inject: false,
hash: !devMode,
appMountId: 'app'
}),
],
devtool: devMode ? 'source-map' : false,
devServer: {
contentBase: 'dist'
}
};