├── .editorconfig ├── .gitignore ├── Dockerfile ├── README.md ├── agpl-3.0.txt ├── authors.txt ├── changelog.txt ├── config.js ├── i ├── css │ ├── bootstrap-notify.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.min.css │ ├── bootswatch.min.css │ ├── font-awesome.css │ ├── images │ │ ├── paypal.png │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_228ef1_256x240.png │ │ ├── ui-icons_ef8c08_256x240.png │ │ ├── ui-icons_ffd27a_256x240.png │ │ └── ui-icons_ffffff_256x240.png │ ├── jquery-ui.css │ └── screen.css ├── dxf │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── bower.json │ ├── dxf-parser.js │ ├── gcode │ │ ├── cp-eagle.js │ │ └── three2gcode.js │ ├── package.json │ ├── renderer.js │ ├── sample │ │ ├── data │ │ │ └── api-cw750-details.dxf │ │ ├── index.html │ │ ├── index.js │ │ ├── libs │ │ │ └── js │ │ │ │ ├── OrbitControls.js │ │ │ │ └── dxf-parser.js │ │ └── three-dxf.js │ ├── test.html │ └── three-dxf.js ├── favicon.ico ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── gcode-viewer │ ├── GridRect._OLD │ ├── gcode-parser.js │ ├── renderer.js │ ├── test.html │ └── ui.js ├── index.html ├── js │ ├── bootstrap-notify.js │ ├── bootstrap.min.js │ ├── clipper_unminified.js │ ├── jog.js │ ├── jquery-ui.js │ ├── jquery.min.js │ ├── jquery.min.js_old │ ├── justgage.js │ ├── paper-full.min.js │ └── raphael-2.1.4.min.js ├── main.js ├── millcrum │ ├── dxf.js │ ├── mc.js │ └── svg.js ├── mobile.html ├── openjscad │ ├── LaserWebapps.js │ ├── img │ │ ├── InternalGear_Small.png │ │ ├── RackAndPinion_Small.png │ │ └── RegularSpurGear_Small.png │ ├── lib │ │ ├── canvasrenderer.js │ │ └── projector.js │ ├── openjscad.css │ ├── src │ │ ├── csg.js │ │ ├── formats.js │ │ ├── openjscad.js │ │ ├── openscad.js │ │ └── threecsg.js │ └── test.html ├── openjscad_files │ ├── gear.jscad │ └── lasercut_box.jscad ├── raster │ ├── calibration.jpg │ ├── calibration.svg │ ├── laserraster.js │ ├── openbuilds.png │ ├── random grayscale pic from google images.jpg │ └── tiny.png ├── svg │ └── js │ │ ├── SVGReader.js │ │ ├── SVGRenderer.js │ │ ├── array.find.js │ │ ├── jquery.js │ │ ├── main.js │ │ ├── poly2tri.js │ │ ├── svg2gcode.js │ │ └── vec2.js └── threejs │ ├── GridRect.js │ ├── TrackballControls.js │ ├── orbitcontrols.js │ ├── three.min.js │ └── three.min.js_74 ├── machines ├── package.json ├── presets ├── rrw_timelapse.sh ├── screenshots ├── 50%.png ├── Scale Widget.png ├── Screenshot from 2016-01-22 15-24-02.png ├── controlmodal.PNG ├── ctrl+shift+i is debug logs.png ├── dxf.png ├── dxf1.png ├── hackaday.jpg ├── job modal.png ├── jog widget.png ├── laserweb.PNG ├── parametricgenerator.png ├── port selector.png ├── raster calibration.png ├── raster gcode viewer.png ├── raster.png ├── serial console.png ├── svg.png ├── terminal.png └── top toolbar.png ├── server.js ├── slBaseOpts.js └── update.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #laserweb 2 | machines 3 | presets 4 | 5 | # Compiled source # 6 | ################### 7 | *.com 8 | *.class 9 | *.dll 10 | *.exe 11 | *.o 12 | *.so 13 | 14 | # Packages # 15 | ############ 16 | # it's better to unpack these files and commit the raw source 17 | # git has its own built in compression methods 18 | *.7z 19 | *.dmg 20 | *.gz 21 | *.iso 22 | *.jar 23 | *.rar 24 | *.tar 25 | *.zip 26 | 27 | # Logs and databases # 28 | ###################### 29 | *.log 30 | *.sql 31 | *.sqlite 32 | 33 | # OS generated files # 34 | ###################### 35 | .DS_Store 36 | .DS_Store? 37 | ._* 38 | .Spotlight-V100 39 | .Trashes 40 | Icon? 41 | ehthumbs.db 42 | Thumbs.db 43 | *~ 44 | node_modules 45 | 46 | # Local settings # 47 | ################## 48 | .env 49 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:testing 2 | MAINTAINER joseph@cauldrondevelopment.com 3 | 4 | # Install prerequisites 5 | RUN apt-get update && \ 6 | apt-get install -y --no-install-recommends less debian-keyring \ 7 | debian-archive-keyring ca-certificates nodejs nodejs-legacy npm \ 8 | build-essential git 9 | 10 | # LaserWeb 11 | RUN git clone --depth=1 https://github.com/openhardwarecoza/LaserWeb.git 12 | RUN cd LaserWeb && npm install 13 | 14 | # Container config 15 | EXPOSE 8000 16 | WORKDIR /LaserWeb 17 | ENTRYPOINT nodejs server.js 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED! 2 | 3 | LASERWEB3 is available! See [https://github.com/openhardwarecoza/LaserWeb3](https://github.com/openhardwarecoza/LaserWeb3) for details! 4 | -------------------------------------------------------------------------------- /authors.txt: -------------------------------------------------------------------------------- 1 | Peter van der Walt 2 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | 7 April 2016: Please consider helping us test the beta of LaserWeb 2.0 at https://github.com/openhardwarecoza/LaserWeb2 2 | 1 Mar 2016: Completed switch to new DXF Library co-authored by @johnlauer @bzuillsmith and @openhardwarecoza ! Let the testing begin. Now supports Arcs and Circles too 3 | 28 Feb 2016: Fixed SVG Feedrate + Massive speed increase on 3D Renderer 4 | 17 Feb 2016: Fix for incorrectly evaluated boolean to enable/disable variable Raster Speed 5 | 01 Feb 2016: SVG: Added per-color (Stroke) power/speed setting 6 | 31 Jan 2016: Optimised whitespace in raster (Experimental - please report if issues) 7 | 30 Jan 2016: Made raster gcode output more Grbl compatible 8 | 29 Jan 2016: Fixed issues #94, #85, #70 9 | 28 Jan 2016: Fixed Serial Console Bug, added Websocket Server status indicator 10 | 26 Jan 2016: Project was featured on hackaday.com 11 | 22 Jan 2016: Fixed bug with Raster preview+gcode for Smoothieboards, added Progress Bars to Raster and 3D Viewer renders 12 | 21 Jan 2016: Closed several support Issues - fixed code for issues #68, #69, updated screenshots 13 | 20 Jan 2016: Added hardwareless debug option for developers who doesn't have hardware 14 | 18 Jan 2016: Added Estimated Job Time calculation 15 | 16 Jan 2016: Updated install instructions for Windows based users 16 | 12 Jan 2016: SVG support with scaling, laser power, feed and seek all live now! Go test! 17 | 11 Jan 2016: Working (but early) SVG cutting support is back 18 | 10 Jan 2016: Starting to port over to a new DXF library. Nothing users can use yet... 19 | 8 Jan 2016: Added Automatic Update to UI 20 | 8 Jan 2016: Massive code Cleanup, added Authors to various files, moved Changelog to seperate file (out of readme) 21 | 7 Jan 2016: Added Beta proof of concept 3D printer support 22 | 6 Jan 2016: Raster Engraving is live! Click the Raster button under the file open toolbar 23 | 4 Jan 2016: Added working S?? support to the 3D viewer's Gcode parser to show the laser intensity on the 3D viewer - screenshot below 24 | 31 Dec 2015: Working Javascript/PaperScript based Raster to GCODE code committed - long way from a fully support Raster Engraving solution, but thats the first most major step done: See detailed implementation log on https://github.com/openhardwarecoza/LaserWeb/issues/32 25 | 30 Dec 2014: Added a parametric Tabbed Box Generator based on http://ingegno.be/Manuals/openjscad/boxmaker.html 26 | 30 Dec 2015: Added a parametric Gear Generator based on hessmer.org/gears/InvoluteSpurGearBuilder.html 27 | 30 Dec 2015: Added a OpenJSCAD based engine to allow in-Laserweb apps like the ones above 28 | 27 Dec 2015: Added a little more instructions to readme 29 | 26 Dec 2015: Consider SVG broken for now. Please test DXF etc - we may need to switch to a different SVG Library - let me know if you can help 30 | 25 Dec 2015: Added per-layer (DXF) and per-path (SVG) Laser Power and Feedrate settings to the DXF/SVG Importing code 31 | 25 Dec 2015: Merry Christmas! And merged in @oxydum's Grbl-compatible mc.js code 32 | 24 Dec 2015: Added error handling for /dev/ttyS ports on linux, @Cinezaster contributed a fix for package.json, @oxydum contributed better mc.js code for Grbl lasers 33 | 23 Dec 2015: @danawoodman contributed code to handle serial port errors elegantly, allow overriding config using environment variables, and add some editor/gitignore to help the developers 34 | 22 Dec 2015: Added capability to run various controllers off one PC, all with different firmwares. Switching in the browser's dropdown between serial ports, changes the UI to match the firmware in use on that port! Groundbreaking! 35 | 22 Dec 2015: Added Grbl Support 36 | 21 Dec 2015: Added Lasersaur Support - Autodetect which firmware is loaded on the machine, and adapts Gcode dialect, UI elements, etc to suit the features of the attached machine 37 | 20 Dec 2015: Added a right-click context menu to 3D viewer 38 | 20 Dec 2015: Fixed Window resize didnt fix viewport bug 39 | 16 Dec 2015: Added Webcam Widget 40 | 15 Dec 2015: Added Scale, Translate, Flip functions 41 | 15 Dec 2015: Added Support for SmoothieBoard / SmoothieWare to Master branch 42 | 15 Dec 2015: Added Clear GCode button instead of clearing GCode automatically. Helps if you want to repeat same job a few times. 43 | 19 Nov 2015: Upgraded to latest socket.io and serialport versions (Socket.io 1.3.7 and SerialPort 2.0.5) since there are precompiled binaries for Windows - no more recompile required 44 | 18 Nov 2015: Upgraded to Bootstrap 3.3.5 and changed to a responsive layout 45 | 17 Nov 2015: Integrated Millcrum.com libraries to provide DXF, SVG and MILLCRUM support 46 | 10 Nov 2015: Added FontAwesome icons, moved Jogging to a Modal widget so its out of the way when not in use 47 | 8 Nov 2015: Project started 48 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').load({ silent: true }); 2 | 3 | var config = {}; 4 | 5 | config.webPort = process.env.WEB_PORT || 8000; 6 | config.serialBaudRate = process.env.SERIAL_BAUD_RATE || 115200; 7 | config.webcamPort = process.env.WEBCAM_PORT || 8080; // expects a webcam stream from mjpg_streamer 8 | config.xmax = process.env.X_MAX || 600 // Max length of X Axis in mm 9 | config.ymax = process.env.Y_MAX || 400 // Max length of Y Axis in mm 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /i/css/bootstrap-notify.css: -------------------------------------------------------------------------------- 1 | .notifications { 2 | position: fixed; 3 | z-index: 9999; 4 | } 5 | 6 | /* Positioning */ 7 | .notifications.top-right { 8 | right: 10px; 9 | top: 25px; 10 | } 11 | 12 | .notifications.top-left { 13 | left: 10px; 14 | top: 25px; 15 | } 16 | 17 | .notifications.bottom-left { 18 | left: 10px; 19 | bottom: 25px; 20 | } 21 | 22 | .notifications.bottom-right { 23 | right: 10px; 24 | bottom: 25px; 25 | } 26 | 27 | /* Notification Element */ 28 | .notifications > div { 29 | position: relative; 30 | margin: 5px 0px; 31 | } 32 | -------------------------------------------------------------------------------- /i/css/images/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/paypal.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /i/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /i/css/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /i/css/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /i/css/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /i/css/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /i/css/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/css/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /i/css/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | } 4 | 5 | textarea { 6 | outline: none; 7 | } 8 | 9 | #canvas-1 { 10 | border: 1px solid black; 11 | } 12 | 13 | #renderArea { 14 | cursor: pointer; 15 | background-color: #ffffff; 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | right: 0; 20 | bottom: 00; 21 | border: 1px #000; 22 | float: left; 23 | margin-right: 1%; 24 | } 25 | 26 | 27 | 28 | .btn-file { 29 | position: relative; 30 | overflow: hidden; 31 | } 32 | 33 | .btn-file input[type=file] { 34 | position: absolute; 35 | top: 0; 36 | right: 0; 37 | min-width: 100%; 38 | min-height: 100%; 39 | font-size: 100px; 40 | text-align: right; 41 | filter: alpha(opacity=0); 42 | opacity: 0; 43 | outline: none; 44 | background: white; 45 | cursor: inherit; 46 | display: block; 47 | } 48 | 49 | .xyzButton { 50 | height: 40px; 51 | width: 40px; 52 | padding: 0px; 53 | } 54 | 55 | #console { 56 | height: 200px; 57 | width: auto; 58 | overflow-y: auto; 59 | text-align: left; 60 | background-color: #eee; 61 | padding: 10px; 62 | font-size: 10px; 63 | } 64 | 65 | #gcodelines { 66 | height: 300px; 67 | width: auto; 68 | overflow-y: auto; 69 | text-align: left; 70 | background-color: #eee; 71 | padding: 00px; 72 | font-size: 10px; 73 | font-weight: 500; 74 | } 75 | 76 | .table-hover tbody tr:hover td, .table-hover tbody tr:hover th { 77 | background-color: #ffc299; 78 | color: #000; 79 | } 80 | 81 | 82 | #console p { 83 | margin-top: 0px; 84 | margin-bottom: 0px; 85 | } 86 | 87 | #mainStatus { 88 | width: auto; 89 | margin: 10px; 90 | } 91 | 92 | .pf { 93 | white-space: pre; 94 | } 95 | 96 | 97 | 98 | .spinner { 99 | width: 100px; 100 | } 101 | .spinner input { 102 | text-align: right; 103 | } 104 | .input-group-btn-vertical { 105 | position: relative; 106 | white-space: nowrap; 107 | width: 1%; 108 | vertical-align: middle; 109 | display: table-cell; 110 | } 111 | .input-group-btn-vertical > .btn { 112 | display: block; 113 | float: none; 114 | width: 100%; 115 | max-width: 100%; 116 | padding: 8px; 117 | margin-left: -1px; 118 | position: relative; 119 | border-radius: 0; 120 | } 121 | .input-group-btn-vertical > .btn:first-child { 122 | border-top-right-radius: 4px; 123 | } 124 | .input-group-btn-vertical > .btn:last-child { 125 | margin-top: -2px; 126 | border-bottom-right-radius: 4px; 127 | } 128 | .input-group-btn-vertical i{ 129 | position: absolute; 130 | top: 0; 131 | left: 4px; 132 | } 133 | 134 | .modal 135 | { 136 | overflow: hidden; 137 | } 138 | 139 | 140 | .modal-backdrop { 141 | display: none !important; 142 | } 143 | 144 | .simplebox { 145 | outline: none; 146 | border: none !important; 147 | -webkit-box-shadow: none !important; 148 | -moz-box-shadow: none !important; 149 | box-shadow: none !important; 150 | text-align:right; 151 | } 152 | 153 | /* navbar */ 154 | .navbar-primary .navbar { background:#666666; border-bottom:none; } 155 | .navbar-primary .navbar .nav > li > a {color: #501762;} 156 | .navbar-primary .navbar .nav > li > a:hover {color: #fff; background-color: #8e49a3;} 157 | .navbar-primary .navbar .nav .active > a,.navbar .nav .active > a:hover {color: #fff; background-color: #501762;} 158 | .navbar-primary .navbar .nav li > a .caret, .tnav .navbar .nav li > a:hover .caret {border-top-color: #fff;border-bottom-color: #fff;} 159 | .navbar-primary .navbar .nav > li.dropdown.open.active > a:hover {} 160 | .navbar-primary .navbar .nav > li.dropdown.open > a {color: #fff;background-color: #9f58b5;border-color: #fff;} 161 | .navbar-primary .navbar .nav > li.dropdown.open.active > a:hover .caret, .tnav .navbar .nav > li.dropdown.open > a .caret {border-top-color: #fff;} 162 | .navbar-primary .navbar .navbar-brand {color:#fff;} 163 | .navbar-primary .navbar .nav.pull-right {margin-left: 10px; margin-right: 0;} 164 | .navbar-xs .navbar-primary .navbar { min-height:28px; height: 28px; } 165 | .navbar-xs .navbar-primary .navbar .navbar-brand{ padding: 0px 12px;font-size: 16px;line-height: 28px; } 166 | .navbar-xs .navbar-primary .navbar .navbar-nav > li > a { padding-top: 0px; padding-bottom: 0px; line-height: 28px; } 167 | .navbar-sm .navbar-primary .navbar { min-height:28px; height: 28px; } 168 | .navbar-sm .navbar-primary .navbar .navbar-brand{ padding: 0px 12px;font-size: 16px;line-height: 28px; } 169 | .navbar-sm .navbar-primary .navbar .navbar-nav > li > a { padding-top: 0px; padding-bottom: 0px; line-height: 28px; } 170 | 171 | .navbar { 172 | min-height: 28px; 173 | margin-bottom: 23px; 174 | border: 1px solid transparent; 175 | } 176 | 177 | .navbar-brand { 178 | float: left; 179 | font-size: 17px; 180 | line-height: 23px; 181 | height: 28px; 182 | } 183 | 184 | #rasterProgressShroud { 185 | position: relative; 186 | top: 0; 187 | left: 0; 188 | right: 0; 189 | padding: 20px; 190 | background-color: white; 191 | z-index: 999; 192 | height: 100%; 193 | display: none; 194 | } 195 | 196 | #rasterProgressShroud .progress { 197 | position: relative; 198 | top: 0; 199 | bottom: 0; 200 | height: 25px; 201 | width: 300px; 202 | margin: auto; 203 | left: 0; 204 | right: 0 205 | -webkit-transition: none !important; 206 | transition: none !important;; 207 | } 208 | 209 | #rasterProgressShroud .progress-bar { 210 | -webkit-transition: none !important; 211 | transition: none !important;; 212 | } 213 | 214 | 215 | #rasterProgressShroud h1 { 216 | text-align: center; 217 | } 218 | 219 | #renderprogressholder { 220 | position: relative; 221 | top: 0; 222 | left: 0; 223 | right: 0; 224 | padding: 12px; 225 | background-color: white; 226 | z-index: 999; 227 | height: 100%; 228 | display: none; 229 | } 230 | 231 | #renderprogressholder .progress { 232 | position: relative; 233 | top: 0; 234 | bottom: 0; 235 | height: 26px; 236 | width: 300px; 237 | margin: auto; 238 | left: 0; 239 | right: 0 240 | -webkit-transition: none !important; 241 | transition: none !important;; 242 | } 243 | 244 | #renderprogressholder .progress-bar { 245 | -webkit-transition: none !important; 246 | transition: none !important;; 247 | } 248 | 249 | 250 | #renderprogressholder h1 { 251 | text-align: center; 252 | } 253 | -------------------------------------------------------------------------------- /i/dxf/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | private/ -------------------------------------------------------------------------------- /i/dxf/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 GDS Storefront Estimating 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /i/dxf/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Three-Dxf 3 | 4 | **Three-Dxf** is a javascript viewer for dxf files. It takes dxf objects produced from Dxf-Parser and renders them using 5 | three.js. 6 | 7 | #### Install 8 | ``` 9 | // Coming soon 10 | //bower install three-dxf 11 | ``` 12 | 13 | For now we recommend cloning the repo, and starting with our sample. See **Run Samples** below. 14 | 15 | #### Usage 16 | TODO 17 | 18 | #### Run Samples 19 | ``` 20 | > npm install -g http-server 21 | > cd sample 22 | > http-server . 23 | # use `http-server -c-1 .` to prevent caching 24 | ``` 25 | 26 | After performing the steps above, you can see the example at localhost:8080. You can use the dxf file included in the sample. 27 | 28 | #### Current Version v0.0.1 29 | Supports: 30 | * Headers 31 | * Most Simple entities (lines, polylines, circles, etc) 32 | * Layers 33 | * Some support for line types 34 | * Simple Text 35 | 36 | Does not yet support: 37 | * Attributes 38 | * 3DSolids 39 | * All types of Leaders 40 | * MText 41 | * other less common objects and entities. 42 | 43 | #### Run Tests 44 | TODO 45 | 46 | #### Contributors 47 | bzuillsmith@gdsestimating.com 48 | -------------------------------------------------------------------------------- /i/dxf/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-dxf", 3 | "main": "three-dxf.js", 4 | "version": "0.0.1", 5 | "authors": [ 6 | "GDS Storefront Estimating (gdsestimating.com)" 7 | ], 8 | "description": "A dxf viewer for the browser using three.js.", 9 | "keywords": [ 10 | "dxf", 11 | "cad", 12 | "viewer" 13 | ], 14 | "license": "MIT", 15 | "homepage": "https://github.com/gdsestimating/three-dxf", 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "test", 21 | "tests" 22 | ], 23 | "dependencies": { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /i/dxf/gcode/three2gcode.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | AUTHOR: John Lauer 4 | -- changes by AUTHOR: Peter van der Walt 5 | 6 | */ 7 | 8 | var options = {}; 9 | 10 | /** 11 | * Iterate over the text3d that was generated and create 12 | * Gcode to mill/cut the three.js object. 13 | */ 14 | function generateGcodeCallback(threeGroup, cutSpeed, laserPwr, rapidSpeed) { 15 | 16 | console.log('inside generateGcodeCallback') 17 | console.log('Group', threeGroup); 18 | console.log('CutSpeed', cutSpeed); 19 | console.log('Laser Power', laserPwr); 20 | console.log('RapidSpeed', rapidSpeed); 21 | // get settings 22 | //getSettings(); 23 | 24 | // Firmware specific Sxxx params 25 | if (firmware.indexOf('Grbl') == 0) { 26 | laserPwr = laserPwr * 2.55; 27 | laserPwr = laserPwr.toFixed(0); 28 | } else if (firmware.indexOf('Smooth') == 0) { 29 | laserPwr = laserPwr / 100; //.map(0, 100, 0, 1); 30 | laserPwr = laserPwr.toFixed(2); 31 | } else if (firmware.indexOf('Lasaur') == 0) { 32 | laserPwr = laserPwr * 2.55; 33 | laserPwr = laserPwr.toFixed(0); 34 | } else { 35 | laserPwr = laserPwr; 36 | } 37 | 38 | 39 | options["pointsperpath"] = 1; 40 | options["holes"] = 0; 41 | options["cut"] = 'solid'; 42 | options["dashPercent"] = 20; 43 | options["mode"] = 'laser'; 44 | options["laseron"] = 'M3'; 45 | options["lasersvalue"] = laserPwr; 46 | options["millclearanceheight"] = 5.00; 47 | options["milldepthcut"] = 3.00; 48 | options["millfeedrateplunge"] = 200.00; 49 | options["feedrate"] = cutSpeed; 50 | 51 | var g = ""; 52 | 53 | // get the THREE.Group() that is the txt3d 54 | var grp = threeGroup 55 | var txtGrp = threeGroup; 56 | 57 | var that = this; 58 | var isLaserOn = false; 59 | var isAtClearanceHeight = false; 60 | var isFeedrateSpecifiedAlready = false; 61 | subj_paths = []; 62 | subj_path2 = []; 63 | console.log(txtGrp); 64 | txtGrp.traverse( function(child) { 65 | if (child.type == "Line") { 66 | // let's create gcode for all points in line 67 | for (i = 0; i < child.geometry.vertices.length; i++) { 68 | 69 | var localPt = child.geometry.vertices[i]; 70 | var worldPt = grp.localToWorld(localPt.clone()); 71 | if (i == 0) { 72 | // first point in line where we start lasering/milling 73 | // move to point 74 | 75 | // if milling, we need to move to clearance height 76 | if (options.mode == "mill") { 77 | if (!isAtClearanceHeight) { 78 | g += "G0 Z" + options.millclearanceheight + "\n"; 79 | } 80 | } 81 | 82 | // move to start point 83 | g += "G0 X" + worldPt.x.toFixed(3) + 84 | " Y" + worldPt.y.toFixed(3) + " F" + rapidSpeed + "\n"; 85 | 86 | 87 | //subj_paths.push(worldPt.x.toFixed(3) +',' + worldPt.y.toFixed(3)); 88 | // if milling move back to depth cut 89 | if (options.mode == "mill") { 90 | var halfDistance = (options.millclearanceheight - options.milldepthcut) / 2; 91 | g += "G0 Z" + (options.millclearanceheight - halfDistance).toFixed(3) 92 | + "\n"; 93 | g += "G1 F" + options.millfeedrateplunge + 94 | " Z" + options.milldepthcut + "\n"; 95 | isAtClearanceHeight = false; 96 | } 97 | 98 | } 99 | else { 100 | 101 | // we are in a non-first line so this is normal moving 102 | 103 | // see if laser or milling 104 | if (options.mode == "laser") { 105 | 106 | // if the laser is not on, we need to turn it on 107 | if (firmware.indexOf('Grbl') == 0) { 108 | if (!isLaserOn) { 109 | if (options.laseron == "M3") { 110 | g += "M3 S" + options.lasersvalue; 111 | } else { 112 | g += options.laseron; 113 | } 114 | g += " ;laser on\n"; 115 | isLaserOn = true; 116 | } 117 | } 118 | } else { 119 | // this is milling. if we are not at depth cut 120 | // we need to get there 121 | 122 | 123 | } 124 | 125 | // do normal feedrate move 126 | var feedrate; 127 | if (isFeedrateSpecifiedAlready) { 128 | feedrate = ""; 129 | } else { 130 | feedrate = " F" + options.feedrate; 131 | isFeedrateSpecifiedAlready = true; 132 | } 133 | //console.log('World', worldPt); 134 | //console.log('Local', localPt); 135 | g += "G1" + feedrate; 136 | g += " X" + worldPt.x.toFixed(3); 137 | g += " Y" + worldPt.y.toFixed(3); 138 | g += " S" + options.lasersvalue + "\n"; 139 | //subj_paths.push(worldPt.x.toFixed(3) +',' + worldPt.y.toFixed(3)); 140 | var xpos = parseFloat(worldPt.x.toFixed(3)); 141 | var ypos = parseFloat(worldPt.y.toFixed(3)); 142 | subj_paths.push({X:xpos,Y:ypos}); 143 | 144 | 145 | } 146 | } 147 | 148 | // make feedrate have to get specified again on next line 149 | // if there is one 150 | isFeedrateSpecifiedAlready = false; 151 | 152 | // see if laser or milling 153 | if (options.mode == "laser") { 154 | // turn off laser at end of line 155 | isLaserOn = false; 156 | if (firmware.indexOf('Grbl') == 0) { 157 | if (options.laseron == "M3") 158 | g += "M5 ;laser off;\n"; 159 | else 160 | g += "M9 ;laser off;\n"; 161 | } 162 | } else { 163 | // milling. move back to clearance height 164 | g += "G0 Z" + options.millclearanceheight + "\n"; 165 | isAtClearanceHeight = true; 166 | } 167 | console.log('Input Path', subj_paths); 168 | subj_path2 = getInflatedPath(subj_paths, 5); 169 | console.log('Output Path', subj_path2); 170 | 171 | // var mesh = createClipperPathsAsMesh(subj_path2, 0xff0000, 0.2, subj_path2); 172 | // tool_offset = createClipperPathsAsLines(subj_path2); 173 | // tool_offset.translateX(laserxmax /2 * -1); 174 | // tool_offset.translateY(laserymax /2 * -1); 175 | // tool_offset.name = 'Mill Path'; 176 | //scene.add(tool_offset); // Plasma Mode! W.I.P 177 | 178 | 179 | 180 | } 181 | }); 182 | 183 | console.log("generated gcode. length:", g.length); 184 | 185 | isGcodeInRegeneratingState = false; 186 | 187 | // Remove DXF Preview 188 | if (typeof(dxfObject) !== 'undefined') { 189 | scene.remove(dxfObject); 190 | }; 191 | 192 | // Send to LaserWeb 193 | //document.getElementById("gcodepreview").value = g; 194 | return g; 195 | 196 | }; 197 | 198 | 199 | function getInflatedPath(paths, delta, joinType) { 200 | var scale = 10000; 201 | console.log('Inside getInflatedPath'); 202 | ClipperLib.JS.ScaleUpPath(paths, scale); 203 | console.log('Scaled Path: ', paths); 204 | var miterLimit = 2; 205 | var arcTolerance = 10; 206 | joinType = joinType ? joinType : ClipperLib.JoinType.jtRound 207 | var co = new ClipperLib.ClipperOffset(miterLimit, arcTolerance); 208 | co.AddPath(paths, joinType, ClipperLib.EndType.etClosedPolygon); 209 | //var delta = 0.0625; // 1/16 inch endmill 210 | var offsetted_paths = new ClipperLib.Path(); 211 | co.Execute(offsetted_paths, delta * scale); 212 | 213 | console.log('Offset Path: ', offsetted_paths); 214 | // scale back down 215 | for (var i = 0; i < offsetted_paths.length; i++) { 216 | for (var j = 0; j < offsetted_paths[i].length; j++) { 217 | offsetted_paths[i][j].X = offsetted_paths[i][j].X / scale; 218 | offsetted_paths[i][j].Y = offsetted_paths[i][j].Y / scale; 219 | } 220 | } 221 | ClipperLib.JS.ScaleDownPaths(paths, scale); 222 | return offsetted_paths; 223 | 224 | }; 225 | 226 | function createClipperPathsAsLines(paths) { 227 | 228 | var geometry = new THREE.Geometry(), 229 | color = 0xff0000, 230 | material, lineType, vertex, startPoint, endPoint, i, line; 231 | 232 | 233 | if (paths.length == 1) { 234 | var shape = new THREE.Shape(); 235 | var i = 0; 236 | for (var j = 0; j < paths[i].length; j++) { 237 | var pt = paths[i][j]; 238 | //if (j == 0) shape.moveTo(pt.X, pt.Y); 239 | //else shape.lineTo(pt.X, pt.Y); 240 | geometry.vertices.push(new THREE.Vector3(pt.X, pt.Y, 0)); 241 | } 242 | }; 243 | 244 | material = new THREE.LineBasicMaterial({ linewidth: 1, color: color, transparent: true }); 245 | 246 | millgeom = new THREE.Line(geometry, material); 247 | return millgeom; 248 | 249 | }; 250 | 251 | function createClipperPathsAsMesh(paths, color, opacity, holePath) { 252 | //console.log("createClipperPathsAsMesh. paths:", paths, "holePath:", holePath); 253 | if(color === undefined) 254 | color = 0xff0000; 255 | var mat = new THREE.MeshBasicMaterial({ 256 | color: color, 257 | transparent: true, 258 | opacity: opacity, 259 | side: THREE.DoubleSide, 260 | depthWrite: false 261 | }); 262 | 263 | 264 | if (paths.length == 1) { 265 | var shape = new THREE.Shape(); 266 | var i = 0; 267 | for (var j = 0; j < paths[i].length; j++) { 268 | var pt = paths[i][j]; 269 | if (j == 0) shape.moveTo(pt.X, pt.Y); 270 | else shape.lineTo(pt.X, pt.Y); 271 | } 272 | 273 | // see if asked to create hole 274 | // multiple holes supported now 275 | if (holePath !== undefined && holePath != null) { 276 | if (!(Array.isArray(holePath))) { 277 | holePath = [holePath]; 278 | } 279 | 280 | for (var hi = 0; hi < holePath.length; hi++) { 281 | var hp = holePath[hi]; 282 | console.log("adding hole:", hp); 283 | var hole = new THREE.Path(); 284 | //var i = 0; 285 | for (var j = 0; j < hp.length; j++) { 286 | var pt = hp[j]; 287 | if (j == 0) hole.moveTo(pt.X, pt.Y); 288 | else hole.lineTo(pt.X, pt.Y); 289 | } 290 | shape.holes.push(hole); 291 | } 292 | } 293 | 294 | var geometry = new THREE.ShapeGeometry( shape ); 295 | var shapeMesh = new THREE.Mesh(geometry, mat); 296 | 297 | //group.add(shapeMesh); 298 | return shapeMesh; 299 | } else { 300 | var group = new THREE.Object3D(); 301 | 302 | for (var i = 0; i < paths.length; i++) { 303 | var shape = new THREE.Shape(); 304 | for (var j = 0; j < paths[i].length; j++) { 305 | var pt = paths[i][j]; 306 | if (j == 0) shape.moveTo(pt.X, pt.Y); 307 | else shape.lineTo(pt.X, pt.Y); 308 | } 309 | 310 | // see if asked to create hole 311 | // multiple holes supported now 312 | if (holePath !== undefined && holePath != null) { 313 | if (!(Array.isArray(holePath))) { 314 | holePath = [holePath]; 315 | } 316 | 317 | for (var hi = 0; hi < holePath.length; hi++) { 318 | var hp = holePath[hi]; 319 | console.log("adding hole:", hp); 320 | var hole = new THREE.Path(); 321 | //var i = 0; 322 | for (var j = 0; j < hp.length; j++) { 323 | var pt = hp[j]; 324 | if (j == 0) hole.moveTo(pt.X, pt.Y); 325 | else hole.lineTo(pt.X, pt.Y); 326 | } 327 | shape.holes.push(hole); 328 | } 329 | } 330 | 331 | var geometry = new THREE.ShapeGeometry( shape ); 332 | var shapeMesh = new THREE.Mesh(geometry, mat); 333 | 334 | group.add(shapeMesh); 335 | } 336 | return group; 337 | } 338 | //this.sceneAdd(group); 339 | 340 | }; 341 | -------------------------------------------------------------------------------- /i/dxf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-dxf", 3 | "version": "0.1.0", 4 | "description": "A dxf viewer for the browser using three.js.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/gdsestimating/three-dxf" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/gdsestimating/three-dxf/issues", 15 | "email": "bzuillsmith@gmail.com" 16 | }, 17 | "homepage": "https://github.com/gdsestimating/three-dxf", 18 | "author": "GDS Storefront Estimating (gdsestimating.com)", 19 | "contributors": [ 20 | "Ben Zuill-Smith " 21 | ], 22 | "license": "MIT", 23 | "devDependencies": { 24 | }, 25 | "dependencies": { 26 | "dxf-parser": "^0.1.1" 27 | }, 28 | "keywords": [ 29 | "dxf", 30 | "cad", 31 | "viewer" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /i/dxf/renderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the angle in radians of the vector (p1,p2). In other words, imagine 3 | * putting the base of the vector at coordinates (0,0) and finding the angle 4 | * from vector (1,0) to (p1,p2). 5 | * @param {Object} p1 start point of the vector 6 | * @param {Object} p2 end point of the vector 7 | * @return {Number} the angle 8 | */ 9 | THREE.Math.angle2 = function(p1, p2) { 10 | var v1 = new THREE.Vector2(p1.x, p1.y); 11 | var v2 = new THREE.Vector2(p2.x, p2.y); 12 | v2.sub(v1); // sets v2 to be our chord 13 | v2.normalize(); // normalize because cos(theta) = 14 | // if(v2.y < 0) return Math.PI + (Math.PI - Math.acos(v2.x)); 15 | if(v2.y < 0) return -Math.acos(v2.x); 16 | return Math.acos(v2.x); 17 | }; 18 | 19 | 20 | THREE.Math.polar = function(point, distance, angle) { 21 | var result = {}; 22 | result.x = point.x + distance * Math.cos(angle); 23 | result.y = point.y + distance * Math.sin(angle); 24 | return result; 25 | }; 26 | 27 | /** 28 | * Calculates points for a curve between two points 29 | * @param startPoint - the starting point of the curve 30 | * @param endPoint - the ending point of the curve 31 | * @param bulge - a value indicating how much to curve 32 | * @param segments - number of segments between the two given points 33 | */ 34 | THREE.BulgeGeometry = function ( startPoint, endPoint, bulge, segments ) { 35 | 36 | var vertex, i, 37 | center, p0, p1, angle, 38 | radius, startAngle, 39 | thetaAngle; 40 | 41 | THREE.Geometry.call( this ); 42 | 43 | this.startPoint = p0 = startPoint ? new THREE.Vector2(startPoint.x, startPoint.y) : new THREE.Vector2(0,0); 44 | this.endPoint = p1 = endPoint ? new THREE.Vector2(endPoint.x, endPoint.y) : new THREE.Vector2(1,0); 45 | this.bulge = bulge = bulge || 1; 46 | 47 | angle = 4 * Math.atan(bulge); 48 | radius = p0.distanceTo(p1) / 2 / Math.sin(angle/2); 49 | center = THREE.Math.polar(startPoint, radius, THREE.Math.angle2(p0,p1) + (Math.PI / 2 - angle/2)); 50 | 51 | this.segments = segments = segments || Math.max( Math.abs(Math.ceil(angle/(Math.PI/18))), 6); // By default want a segment roughly every 10 degrees 52 | startAngle = THREE.Math.angle2(center, p0); 53 | thetaAngle = angle / segments; 54 | 55 | 56 | this.vertices.push(new THREE.Vector3(p0.x, p0.y, 0)); 57 | 58 | for(i = 1; i <= segments - 1; i++) { 59 | 60 | vertex = THREE.Math.polar(center, Math.abs(radius), startAngle + thetaAngle * i); 61 | 62 | this.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0)); 63 | 64 | } 65 | 66 | }; 67 | 68 | THREE.BulgeGeometry.prototype = Object.create( THREE.Geometry.prototype ); 69 | 70 | /** 71 | * Viewer class for a dxf object. 72 | * @param {Object} data - the dxf object 73 | * @param {Number} width - width of the rendering canvas in pixels 74 | * @param {Number} height - height of the rendering canvas in pixels 75 | * @constructor 76 | */ 77 | function processDXF(data) { 78 | //console.log(data); 79 | 80 | if (typeof(dxfObject) !== 'undefined') { 81 | scene.remove(dxfObject); 82 | }; 83 | dxfObject = new THREE.Group(); 84 | 85 | var i, entity; 86 | 87 | for(i = 0; i < data.entities.length; i++) { 88 | entity = data.entities[i]; 89 | 90 | if(entity.type === 'DIMENSION') { 91 | if(entity.block) { 92 | var block = data.blocks[entity.block]; 93 | for(j = 0; j < block.entities.length; j++) { 94 | drawEntity(block.entities[j], data, j); 95 | } 96 | } else { 97 | console.log('WARNING: No block for DIMENSION entity'); 98 | } 99 | } else { 100 | drawEntity(entity, data, 0); 101 | } 102 | 103 | scene.add(dxfObject); 104 | } 105 | } 106 | 107 | 108 | 109 | function drawEntity(index, entity) { 110 | //console.log('inside drawEntity: Entity ', entity, ' Index: ', index) 111 | if(entity.type === 'CIRCLE' || entity.type === 'ARC') { 112 | drawCircle(entity, index); 113 | } else if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE' || entity.type === 'POLYLINE') { 114 | drawLine(entity, index); 115 | } else if(entity.type === 'TEXT') { 116 | drawText(entity, index); 117 | } else if(entity.type === 'SOLID') { 118 | drawSolid(entity, index); 119 | } else if(entity.type === 'POINT') { 120 | drawPoint(entity, index); 121 | } 122 | } 123 | 124 | function drawLine(entity, index) { 125 | //console.log('inside drawLine ', entity, ' Index: ', index ) 126 | var geometry = new THREE.Geometry(), 127 | color = getDXFColor(entity), 128 | material, lineType, vertex, startPoint, endPoint, bulgeGeometry, 129 | bulge, i, line; 130 | 131 | // create geometry 132 | for(i = 0; i < entity.vertices.length; i++) { 133 | 134 | if(entity.vertices[i].bulge) { 135 | bulge = entity.vertices[i].bulge; 136 | startPoint = entity.vertices[i]; 137 | endPoint = i + 1 < entity.vertices.length ? entity.vertices[i + 1] : geometry.vertices[0]; 138 | 139 | bulgeGeometry = new THREE.BulgeGeometry(startPoint, endPoint, bulge); 140 | 141 | geometry.vertices.push.apply(geometry.vertices, bulgeGeometry.vertices); 142 | } else { 143 | vertex = entity.vertices[i]; 144 | geometry.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0)); 145 | } 146 | 147 | } 148 | if(entity.shape) geometry.vertices.push(geometry.vertices[0]); 149 | 150 | if(lineType && lineType.pattern && lineType.pattern.length !== 0) { 151 | material = new THREE.LineDashedMaterial({ color: color, gapSize: 4, dashSize: 4}); 152 | } else { 153 | material = new THREE.LineBasicMaterial({ linewidth: 1, color: color, transparent: true }); 154 | } 155 | 156 | window["dxfEntity" + index] = new THREE.Line(geometry, material); 157 | //window["dxfEntity" + index].translateX(laserxmax /2 * -1); 158 | //window["dxfEntity" + index].translateY(laserymax /2 * -1); 159 | dxfObject.add(window["dxfEntity" + index]); 160 | } 161 | 162 | function drawCircle(entity, index) { 163 | 164 | // Laserweb - calc and draw gcode 165 | var radius = entity.radius; 166 | //console.log('Radius:'+radius+' and Center '+entity.center.x+','+entity.center.y+','+entity.center.z+',\n'); // Radius:220 and Center 0,0,0, 167 | var arcTotalDeg = entity.startAngleDeg - entity.endAngleDeg; 168 | //console.log('Start Angle: '+entity.startAngleDeg+', End Angle: '+entity.endAngleDeg+', thus spanning '+arcTotalDeg+'deg' ); 169 | 170 | // Draw it since its cool to see (note this is a straigh three.js view of it, not via gcode. Can be used in the Cutting Params view by coloring object/layers to change params) 171 | var geometry, material, circle; 172 | 173 | geometry = new THREE.CircleGeometry(entity.radius, 128, entity.startAngle, entity.angleLength); 174 | geometry.vertices.shift(); 175 | 176 | material = new THREE.LineBasicMaterial({ color: getDXFColor(entity), transparent: true }); 177 | 178 | //circle = new THREE.Line(geometry, material); 179 | 180 | window["dxfEntity" + index] = new THREE.Line(geometry, material); 181 | window["dxfEntity" + index].translateX(entity.center.x); 182 | window["dxfEntity" + index].translateY(entity.center.y); 183 | window["dxfEntity" + index].translateZ(entity.center.z); 184 | //window["dxfEntity" + index].translateX(laserxmax /2 * -1); 185 | //window["dxfEntity" + index].translateY(laserymax /2 * -1); 186 | dxfObject.add(window["dxfEntity" + index]); 187 | } 188 | 189 | function drawSolid(entity, index) { 190 | var material, mesh, solid, verts; 191 | geometry = new THREE.Geometry(); 192 | 193 | verts = geometry.vertices; 194 | verts.push(new THREE.Vector3(entity.points[0].x, entity.points[0].y, entity.points[0].z)); 195 | verts.push(new THREE.Vector3(entity.points[1].x, entity.points[1].y, entity.points[1].z)); 196 | verts.push(new THREE.Vector3(entity.points[2].x, entity.points[2].y, entity.points[2].z)); 197 | verts.push(new THREE.Vector3(entity.points[3].x, entity.points[3].y, entity.points[3].z)); 198 | 199 | // Calculate which direction the points are facing (clockwise or counter-clockwise) 200 | var vector1 = new THREE.Vector3(); 201 | var vector2 = new THREE.Vector3(); 202 | vector1.subVectors(verts[1], verts[0]); 203 | vector2.subVectors(verts[2], verts[0]); 204 | vector1.cross(vector2); 205 | 206 | // If z < 0 then we must draw these in reverse order 207 | if(vector1.z < 0) { 208 | geometry.faces.push(new THREE.Face3(2, 1, 0)); 209 | geometry.faces.push(new THREE.Face3(2, 3, 0)); 210 | } else { 211 | geometry.faces.push(new THREE.Face3(0, 1, 2)); 212 | geometry.faces.push(new THREE.Face3(0, 3, 2)); 213 | } 214 | 215 | 216 | material = new THREE.MeshBasicMaterial({ color: getDXFColor(entity), transparent: true }); 217 | 218 | window["dxfEntity" + index] = new THREE.Mesh(geometry, material); 219 | //window["dxfEntity" + index].translateX(laserxmax /2 * -1); 220 | //window["dxfEntity" + index].translateY(laserymax /2 * -1); 221 | dxfObject.add(window["dxfEntity" + index]); 222 | 223 | } 224 | 225 | function drawText(entity, index) { 226 | var geometry, material, text; 227 | 228 | geometry = new THREE.TextGeometry(entity.text, { height: 0, size: entity.textHeight || 12 }); 229 | 230 | material = new THREE.MeshBasicMaterial({ color: getDXFColor(entity) }); 231 | 232 | window["dxfEntity" + index] = new THREE.Mesh(geometry, material); 233 | window["dxfEntity" + index].translateX(entity.startPoint.x); 234 | window["dxfEntity" + index].translateY(entity.startPoint.y); 235 | window["dxfEntity" + index].translateZ(entity.startPoint.z); 236 | //window["dxfEntity" + index].translateX(laserxmax /2 * -1); 237 | //window["dxfEntity" + index].translateY(laserymax /2 * -1); 238 | dxfObject.add(window["dxfEntity" + index]); 239 | } 240 | 241 | function drawPoint(entity, index) { 242 | var geometry, material, point; 243 | 244 | geometry = new THREE.Geometry(); 245 | 246 | geometry.vertices.push(new THREE.Vector3(entity.position.x, entity.position.y, entity.position.z)); 247 | 248 | // TODO: could be more efficient. PointCloud per layer? 249 | 250 | var numPoints = 1; 251 | 252 | var color = getDXFColor(entity); 253 | var colors = new Float32Array( numPoints*3 ); 254 | colors[0] = color.r; 255 | colors[1] = color.g; 256 | colors[2] = color.b; 257 | 258 | geometry.colors = colors; 259 | geometry.computeBoundingBox(); 260 | 261 | material = new THREE.PointCloudMaterial( { size: 0.05, vertexColors: THREE.VertexColors, transparent: true} ); 262 | 263 | window["dxfEntity" + index] = new THREE.PointCloud(geometry, material); 264 | //window["dxfEntity" + index].translateX(laserxmax /2 * -1); 265 | //window["dxfEntity" + index].translateY(laserymax /2 * -1); 266 | dxfObject.add(window["dxfEntity" + index]); 267 | } 268 | 269 | function getDXFColor(entity) { 270 | // var color = entity.color || data.tables.layers[entity.layer].color; 271 | // if(color === 0xffffff) { 272 | // color = 0x000000; 273 | // } 274 | return 0x000099; 275 | } 276 | 277 | 278 | function createLineTypeShaders(data) { 279 | var ltype, type; 280 | var ltypes = data.tables.lineTypes; 281 | 282 | for(type in ltypes) { 283 | ltype = ltypes[type]; 284 | if(!ltype.pattern) continue; 285 | ltype.material = createDashedLineShader(ltype.pattern); 286 | } 287 | } 288 | 289 | function createDashedLineShader(pattern) { 290 | var i, 291 | dashedLineShader = {}, 292 | totalLength = 0.0; 293 | 294 | for(i = 0; i < pattern.length; i++) { 295 | totalLength += Math.abs(pattern[i]); 296 | } 297 | 298 | dashedLineShader.uniforms = THREE.UniformsUtils.merge([ 299 | 300 | THREE.UniformsLib[ 'common' ], 301 | THREE.UniformsLib[ 'fog' ], 302 | 303 | { 304 | 'pattern': { type: 'fv1', value: pattern }, 305 | 'patternLength': { type: 'f', value: totalLength } 306 | } 307 | 308 | ]); 309 | 310 | dashedLineShader.vertexShader = [ 311 | 'attribute float lineDistance;', 312 | 313 | 'varying float vLineDistance;', 314 | 315 | THREE.ShaderChunk[ 'color_pars_vertex' ], 316 | 317 | 'void main() {', 318 | 319 | THREE.ShaderChunk[ 'color_vertex' ], 320 | 321 | 'vLineDistance = lineDistance;', 322 | 323 | 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', 324 | 325 | '}' 326 | ].join('\n'); 327 | 328 | dashedLineShader.fragmentShader = [ 329 | 'uniform vec3 diffuse;', 330 | 'uniform float opacity;', 331 | 332 | 'uniform float pattern[' + pattern.length + '];', 333 | 'uniform float patternLength;', 334 | 335 | 'varying float vLineDistance;', 336 | 337 | THREE.ShaderChunk[ 'color_pars_fragment' ], 338 | THREE.ShaderChunk[ 'fog_pars_fragment' ], 339 | 340 | 'void main() {', 341 | 342 | 'float pos = mod(vLineDistance, patternLength);', 343 | 344 | 'for ( int i = 0; i < ' + pattern.length + '; i++ ) {', 345 | 'pos = pos - abs(pattern[i]);', 346 | 'if( pos < 0.0 ) {', 347 | 'if( pattern[i] > 0.0 ) {', 348 | 'gl_FragColor = vec4(1.0, 0.0, 0.0, opacity );', 349 | 'break;', 350 | '}', 351 | 'discard;', 352 | '}', 353 | 354 | '}', 355 | 356 | THREE.ShaderChunk[ 'color_fragment' ], 357 | THREE.ShaderChunk[ 'fog_fragment' ], 358 | 359 | '}' 360 | ].join('\n'); 361 | 362 | return dashedLineShader; 363 | } 364 | -------------------------------------------------------------------------------- /i/dxf/sample/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 37 | 38 | 39 |
40 |
41 |
42 | 43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 | 51 |

While hovering over the canvas: Right click to pan. Mouse wheel to zoom in an out. The starting view is 52 | arbitrary at the moment so you may need to zoom out and pan around to find the drawing. This will be fixed 53 | in future versions.

54 | 55 |
56 |
57 | 58 |
59 |
60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /i/dxf/sample/index.js: -------------------------------------------------------------------------------- 1 | 2 | var progress = document.getElementById('file-progress-bar'); 3 | var $progress = $('.progress'); 4 | 5 | var $cadview = $('#cad-view'); 6 | var cadCanvas; 7 | 8 | // Setup the dnd listeners. 9 | var dropZone = $('.drop-zone'); 10 | dropZone.on('dragover', handleDragOver, false); 11 | dropZone.on('drop', onFileSelected, false); 12 | 13 | document.getElementById('dxf').addEventListener('change', onFileSelected, false); 14 | 15 | 16 | function onFileSelected(evt) { 17 | progress.style.width = '0%'; 18 | progress.textContent = '0%'; 19 | 20 | var file = evt.target.files[0]; 21 | var output = []; 22 | output.push('
  • ', encodeURI(file.name), ' (', file.type || 'n/a', ') - ', 23 | file.size, ' bytes, last modified: ', 24 | file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a', 25 | '
  • '); 26 | document.getElementById('file-description').innerHTML = ''; 27 | 28 | $progress.addClass('loading'); 29 | 30 | var reader = new FileReader(); 31 | reader.onprogress = updateProgress; 32 | reader.onloadend = onSuccess; 33 | reader.onabort = abortUpload; 34 | reader.onerror = errorHandler; 35 | reader.readAsText(file); 36 | } 37 | 38 | function abortUpload() { 39 | console.log('Aborted read!') 40 | } 41 | 42 | function errorHandler(evt) { 43 | switch(evt.target.error.code) { 44 | case evt.target.error.NOT_FOUND_ERR: 45 | alert('File Not Found!'); 46 | break; 47 | case evt.target.error.NOT_READABLE_ERR: 48 | alert('File is not readable'); 49 | break; 50 | case evt.target.error.ABORT_ERR: 51 | break; // noop 52 | default: 53 | alert('An error occurred reading this file.'); 54 | } 55 | } 56 | 57 | function updateProgress(evt) { 58 | console.log('progress'); 59 | console.log(Math.round((evt.loaded /evt.total) * 100)); 60 | if(evt.lengthComputable) { 61 | var percentLoaded = Math.round((evt.loaded /evt.total) * 100); 62 | if (percentLoaded < 100) { 63 | progress.style.width = percentLoaded + '%'; 64 | progress.textContent = percentLoaded + '%'; 65 | } 66 | } 67 | } 68 | 69 | function onSuccess(evt){ 70 | var fileReader = evt.target; 71 | if(fileReader.error) return console.log("error onloadend!?"); 72 | progress.style.width = '100%'; 73 | progress.textContent = '100%'; 74 | setTimeout(function() { $progress.removeClass('loading'); }, 2000); 75 | var parser = new window.DxfParser(); 76 | var dxf = parser.parseSync(fileReader.result); 77 | cadCanvas = new ThreeDxfViewer(dxf, document.getElementById('cad-view'), 400, 400); 78 | } 79 | 80 | function handleDragOver(evt) { 81 | evt.stopPropagation(); 82 | evt.preventDefault(); 83 | evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy. 84 | } 85 | -------------------------------------------------------------------------------- /i/dxf/sample/libs/js/OrbitControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author qiao / https://github.com/qiao 3 | * @author mrdoob / http://mrdoob.com 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author WestLangley / http://github.com/WestLangley 6 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | // 18 | // This is a drop-in replacement for (most) TrackballControls used in examples. 19 | // That is, include this js file and wherever you see: 20 | // controls = new THREE.TrackballControls( camera ); 21 | // controls.target.z = 150; 22 | // Simple substitute "OrbitControls" and the control should work as-is. 23 | 24 | THREE.OrbitControls = function ( object, domElement ) { 25 | 26 | this.object = object; 27 | this.domElement = ( domElement !== undefined ) ? domElement : document; 28 | 29 | // API 30 | 31 | // Set to false to disable this control 32 | this.enabled = true; 33 | 34 | // "target" sets the location of focus, where the control orbits around 35 | // and where it pans with respect to. 36 | this.target = new THREE.Vector3(); 37 | 38 | // center is old, deprecated; use "target" instead 39 | this.center = this.target; 40 | 41 | // This option actually enables dollying in and out; left as "zoom" for 42 | // backwards compatibility 43 | this.noZoom = false; 44 | this.zoomSpeed = 1.0; 45 | 46 | // Limits to how far you can dolly in and out 47 | this.minDistance = 0; 48 | this.maxDistance = Infinity; 49 | 50 | // Set to true to disable this control 51 | this.noRotate = false; 52 | this.rotateSpeed = 1.0; 53 | 54 | // Set to true to disable this control 55 | this.noPan = false; 56 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 57 | 58 | // Set to true to automatically rotate around the target 59 | this.autoRotate = false; 60 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 61 | 62 | // How far you can orbit vertically, upper and lower limits. 63 | // Range is 0 to Math.PI radians. 64 | this.minPolarAngle = 0; // radians 65 | this.maxPolarAngle = Math.PI; // radians 66 | 67 | // Set to true to disable use of the keys 68 | this.noKeys = false; 69 | 70 | // The four arrow keys 71 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 72 | 73 | //////////// 74 | // internals 75 | 76 | var scope = this; 77 | 78 | var EPS = 0.000001; 79 | 80 | var rotateStart = new THREE.Vector2(); 81 | var rotateEnd = new THREE.Vector2(); 82 | var rotateDelta = new THREE.Vector2(); 83 | 84 | var panStart = new THREE.Vector2(); 85 | var panEnd = new THREE.Vector2(); 86 | var panDelta = new THREE.Vector2(); 87 | var panOffset = new THREE.Vector3(); 88 | 89 | var offset = new THREE.Vector3(); 90 | 91 | var dollyStart = new THREE.Vector2(); 92 | var dollyEnd = new THREE.Vector2(); 93 | var dollyDelta = new THREE.Vector2(); 94 | 95 | var phiDelta = 0; 96 | var thetaDelta = 0; 97 | var scale = 1; 98 | var pan = new THREE.Vector3(); 99 | 100 | var lastPosition = new THREE.Vector3(); 101 | 102 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 103 | 104 | var state = STATE.NONE; 105 | 106 | // for reset 107 | 108 | this.target0 = this.target.clone(); 109 | this.position0 = this.object.position.clone(); 110 | 111 | // events 112 | 113 | var changeEvent = { type: 'change' }; 114 | var startEvent = { type: 'start'}; 115 | var endEvent = { type: 'end'}; 116 | 117 | this.rotateLeft = function ( angle ) { 118 | 119 | if ( angle === undefined ) { 120 | 121 | angle = getAutoRotationAngle(); 122 | 123 | } 124 | 125 | thetaDelta -= angle; 126 | 127 | }; 128 | 129 | this.rotateUp = function ( angle ) { 130 | 131 | if ( angle === undefined ) { 132 | 133 | angle = getAutoRotationAngle(); 134 | 135 | } 136 | 137 | phiDelta -= angle; 138 | 139 | }; 140 | 141 | // pass in distance in world space to move left 142 | this.panLeft = function ( distance ) { 143 | 144 | var te = this.object.matrix.elements; 145 | 146 | // get X column of matrix 147 | panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); 148 | panOffset.multiplyScalar( - distance ); 149 | 150 | pan.add( panOffset ); 151 | 152 | }; 153 | 154 | // pass in distance in world space to move up 155 | this.panUp = function ( distance ) { 156 | 157 | var te = this.object.matrix.elements; 158 | 159 | // get Y column of matrix 160 | panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); 161 | panOffset.multiplyScalar( distance ); 162 | 163 | pan.add( panOffset ); 164 | 165 | }; 166 | 167 | // pass in x,y of change desired in pixel space, 168 | // right and down are positive 169 | this.pan = function ( deltaX, deltaY ) { 170 | 171 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 172 | 173 | if ( scope.object.fov !== undefined ) { 174 | 175 | // perspective 176 | var position = scope.object.position; 177 | var offset = position.clone().sub( scope.target ); 178 | var targetDistance = offset.length(); 179 | 180 | // half of the fov is center to top of screen 181 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 182 | 183 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 184 | scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); 185 | scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); 186 | 187 | } else if ( scope.object.top !== undefined ) { 188 | 189 | // orthographic 190 | scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); 191 | scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); 192 | 193 | } else { 194 | 195 | // camera neither orthographic or perspective 196 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 197 | 198 | } 199 | 200 | }; 201 | 202 | this.dollyIn = function ( dollyScale ) { 203 | 204 | if ( dollyScale === undefined ) { 205 | 206 | dollyScale = getZoomScale(); 207 | 208 | } 209 | 210 | scale /= dollyScale; 211 | 212 | }; 213 | 214 | this.dollyOut = function ( dollyScale ) { 215 | 216 | if ( dollyScale === undefined ) { 217 | 218 | dollyScale = getZoomScale(); 219 | 220 | } 221 | 222 | scale *= dollyScale; 223 | 224 | }; 225 | 226 | this.update = function () { 227 | 228 | if(scope.object.top !== undefined) { 229 | this.object.top = (scale*this.object.top); 230 | this.object.bottom = (scale*this.object.bottom); 231 | this.object.left = (scale*this.object.left); 232 | this.object.right = (scale*this.object.right); 233 | 234 | this.object.updateProjectionMatrix(); 235 | 236 | } 237 | 238 | 239 | var position = this.object.position; 240 | 241 | offset.copy( position ).sub( this.target ); 242 | // 243 | // // angle from z-axis around y-axis 244 | // 245 | // var theta = Math.atan2( offset.x, offset.z ); 246 | // 247 | // // angle from y-axis 248 | // 249 | // var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 250 | // 251 | // if ( this.autoRotate ) { 252 | // 253 | // this.rotateLeft( getAutoRotationAngle() ); 254 | // 255 | // } 256 | // 257 | // theta += thetaDelta; 258 | // phi += phiDelta; 259 | // 260 | // // restrict phi to be between desired limits 261 | // phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 262 | // 263 | // // restrict phi to be betwee EPS and PI-EPS 264 | // phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 265 | // 266 | // var radius = offset.length() * scale; 267 | // 268 | // // restrict radius to be between desired limits 269 | // radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 270 | // 271 | // // move target to panned location 272 | this.target.add( pan ); 273 | // 274 | // offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 275 | // offset.y = radius * Math.cos( phi ); 276 | // offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 277 | // 278 | position.copy( this.target ).add( offset ); 279 | 280 | this.object.lookAt( this.target ); 281 | 282 | 283 | // if ( lastPosition.distanceTo( this.object.position ) > 0 || scale !== 1 ) { 284 | 285 | this.dispatchEvent( changeEvent ); 286 | 287 | // lastPosition.copy( this.object.position ); 288 | 289 | // } 290 | 291 | // thetaDelta = 0; 292 | // phiDelta = 0; 293 | scale = 1; 294 | pan.set( 0, 0, 0 ); 295 | 296 | }; 297 | 298 | 299 | this.reset = function () { 300 | 301 | state = STATE.NONE; 302 | 303 | this.target.copy( this.target0 ); 304 | this.object.position.copy( this.position0 ); 305 | 306 | this.update(); 307 | 308 | }; 309 | 310 | function getAutoRotationAngle() { 311 | 312 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 313 | 314 | } 315 | 316 | function getZoomScale() { 317 | 318 | return Math.pow( 0.95, scope.zoomSpeed ); 319 | 320 | } 321 | 322 | function onMouseDown( event ) { 323 | 324 | if ( scope.enabled === false ) return; 325 | event.preventDefault(); 326 | 327 | if ( event.button === 0 ) { 328 | if ( scope.noRotate === true ) return; 329 | 330 | state = STATE.ROTATE; 331 | 332 | rotateStart.set( event.clientX, event.clientY ); 333 | 334 | } else if ( event.button === 1 ) { 335 | if ( scope.noZoom === true ) return; 336 | 337 | state = STATE.DOLLY; 338 | 339 | dollyStart.set( event.clientX, event.clientY ); 340 | 341 | } else if ( event.button === 2 ) { 342 | if ( scope.noPan === true ) return; 343 | 344 | state = STATE.PAN; 345 | 346 | panStart.set( event.clientX, event.clientY ); 347 | 348 | } 349 | 350 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 351 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 352 | scope.dispatchEvent( startEvent ); 353 | 354 | } 355 | 356 | function onMouseMove( event ) { 357 | 358 | if ( scope.enabled === false ) return; 359 | 360 | event.preventDefault(); 361 | 362 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 363 | 364 | if ( state === STATE.ROTATE ) { 365 | 366 | if ( scope.noRotate === true ) return; 367 | 368 | rotateEnd.set( event.clientX, event.clientY ); 369 | rotateDelta.subVectors( rotateEnd, rotateStart ); 370 | 371 | // rotating across whole screen goes 360 degrees around 372 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 373 | 374 | // rotating up and down along whole screen attempts to go 360, but limited to 180 375 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 376 | 377 | rotateStart.copy( rotateEnd ); 378 | 379 | } else if ( state === STATE.DOLLY ) { 380 | 381 | if ( scope.noZoom === true ) return; 382 | 383 | dollyEnd.set( event.clientX, event.clientY ); 384 | dollyDelta.subVectors( dollyEnd, dollyStart ); 385 | 386 | if ( dollyDelta.y > 0 ) { 387 | 388 | scope.dollyIn(); 389 | 390 | } else { 391 | 392 | scope.dollyOut(); 393 | 394 | } 395 | 396 | dollyStart.copy( dollyEnd ); 397 | 398 | } else if ( state === STATE.PAN ) { 399 | 400 | if ( scope.noPan === true ) return; 401 | 402 | panEnd.set( event.clientX, event.clientY ); 403 | panDelta.subVectors( panEnd, panStart ); 404 | 405 | scope.pan( panDelta.x, panDelta.y ); 406 | 407 | panStart.copy( panEnd ); 408 | 409 | } 410 | 411 | scope.update(); 412 | 413 | } 414 | 415 | function onMouseUp( /* event */ ) { 416 | 417 | if ( scope.enabled === false ) return; 418 | 419 | scope.domElement.removeEventListener( 'mousemove', onMouseMove, false ); 420 | scope.domElement.removeEventListener( 'mouseup', onMouseUp, false ); 421 | scope.dispatchEvent( endEvent ); 422 | state = STATE.NONE; 423 | 424 | } 425 | 426 | function onMouseWheel( event ) { 427 | 428 | if ( scope.enabled === false || scope.noZoom === true ) return; 429 | 430 | event.preventDefault(); 431 | 432 | var delta = 0; 433 | 434 | if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 435 | 436 | delta = event.wheelDelta; 437 | 438 | } else if ( event.detail !== undefined ) { // Firefox 439 | 440 | delta = - event.detail; 441 | 442 | } 443 | 444 | if ( delta > 0 ) { 445 | 446 | scope.dollyOut(); 447 | 448 | } else { 449 | 450 | scope.dollyIn(); 451 | 452 | } 453 | 454 | scope.update(); 455 | scope.dispatchEvent( startEvent ); 456 | scope.dispatchEvent( endEvent ); 457 | 458 | } 459 | 460 | function onKeyDown( event ) { 461 | 462 | if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; 463 | 464 | switch ( event.keyCode ) { 465 | 466 | case scope.keys.UP: 467 | scope.pan( 0, scope.keyPanSpeed ); 468 | scope.update(); 469 | break; 470 | 471 | case scope.keys.BOTTOM: 472 | scope.pan( 0, - scope.keyPanSpeed ); 473 | scope.update(); 474 | break; 475 | 476 | case scope.keys.LEFT: 477 | scope.pan( scope.keyPanSpeed, 0 ); 478 | scope.update(); 479 | break; 480 | 481 | case scope.keys.RIGHT: 482 | scope.pan( - scope.keyPanSpeed, 0 ); 483 | scope.update(); 484 | break; 485 | 486 | } 487 | 488 | } 489 | 490 | function touchstart( event ) { 491 | 492 | if ( scope.enabled === false ) return; 493 | 494 | switch ( event.touches.length ) { 495 | 496 | case 1: // one-fingered touch: rotate 497 | 498 | if ( scope.noRotate === true ) return; 499 | 500 | state = STATE.TOUCH_ROTATE; 501 | 502 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 503 | break; 504 | 505 | case 2: // two-fingered touch: dolly 506 | 507 | if ( scope.noZoom === true ) return; 508 | 509 | state = STATE.TOUCH_DOLLY; 510 | 511 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 512 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 513 | var distance = Math.sqrt( dx * dx + dy * dy ); 514 | dollyStart.set( 0, distance ); 515 | break; 516 | 517 | case 3: // three-fingered touch: pan 518 | 519 | if ( scope.noPan === true ) return; 520 | 521 | state = STATE.TOUCH_PAN; 522 | 523 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 524 | break; 525 | 526 | default: 527 | 528 | state = STATE.NONE; 529 | 530 | } 531 | 532 | scope.dispatchEvent( startEvent ); 533 | 534 | } 535 | 536 | function touchmove( event ) { 537 | 538 | if ( scope.enabled === false ) return; 539 | 540 | event.preventDefault(); 541 | event.stopPropagation(); 542 | 543 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 544 | 545 | switch ( event.touches.length ) { 546 | 547 | case 1: // one-fingered touch: rotate 548 | 549 | if ( scope.noRotate === true ) return; 550 | if ( state !== STATE.TOUCH_ROTATE ) return; 551 | 552 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 553 | rotateDelta.subVectors( rotateEnd, rotateStart ); 554 | 555 | // rotating across whole screen goes 360 degrees around 556 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 557 | // rotating up and down along whole screen attempts to go 360, but limited to 180 558 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 559 | 560 | rotateStart.copy( rotateEnd ); 561 | 562 | scope.update(); 563 | break; 564 | 565 | case 2: // two-fingered touch: dolly 566 | 567 | if ( scope.noZoom === true ) return; 568 | if ( state !== STATE.TOUCH_DOLLY ) return; 569 | 570 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 571 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 572 | var distance = Math.sqrt( dx * dx + dy * dy ); 573 | 574 | dollyEnd.set( 0, distance ); 575 | dollyDelta.subVectors( dollyEnd, dollyStart ); 576 | 577 | if ( dollyDelta.y > 0 ) { 578 | 579 | scope.dollyOut(); 580 | 581 | } else { 582 | 583 | scope.dollyIn(); 584 | 585 | } 586 | 587 | dollyStart.copy( dollyEnd ); 588 | 589 | scope.update(); 590 | break; 591 | 592 | case 3: // three-fingered touch: pan 593 | 594 | if ( scope.noPan === true ) return; 595 | if ( state !== STATE.TOUCH_PAN ) return; 596 | 597 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 598 | panDelta.subVectors( panEnd, panStart ); 599 | 600 | scope.pan( panDelta.x, panDelta.y ); 601 | 602 | panStart.copy( panEnd ); 603 | 604 | scope.update(); 605 | break; 606 | 607 | default: 608 | 609 | state = STATE.NONE; 610 | 611 | } 612 | 613 | } 614 | 615 | function touchend( /* event */ ) { 616 | 617 | if ( scope.enabled === false ) return; 618 | 619 | scope.dispatchEvent( endEvent ); 620 | state = STATE.NONE; 621 | 622 | } 623 | 624 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 625 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 626 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 627 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 628 | 629 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 630 | this.domElement.addEventListener( 'touchend', touchend, false ); 631 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 632 | 633 | window.addEventListener( 'keydown', onKeyDown, false ); 634 | 635 | }; 636 | 637 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); -------------------------------------------------------------------------------- /i/dxf/sample/three-dxf.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Returns the angle in radians of the vector (p1,p2). In other words, imagine 4 | * putting the base of the vector at coordinates (0,0) and finding the angle 5 | * from vector (1,0) to (p1,p2). 6 | * @param {Object} p1 start point of the vector 7 | * @param {Object} p2 end point of the vector 8 | * @return {Number} the angle 9 | */ 10 | THREE.Math.angle2 = function(p1, p2) { 11 | var v1 = new THREE.Vector2(p1.x, p1.y); 12 | var v2 = new THREE.Vector2(p2.x, p2.y); 13 | v2.sub(v1); // sets v2 to be our chord 14 | v2.normalize(); // normalize because cos(theta) = 15 | // if(v2.y < 0) return Math.PI + (Math.PI - Math.acos(v2.x)); 16 | if(v2.y < 0) return -Math.acos(v2.x); 17 | return Math.acos(v2.x); 18 | }; 19 | 20 | 21 | THREE.Math.polar = function(point, distance, angle) { 22 | var result = {}; 23 | result.x = point.x + distance * Math.cos(angle); 24 | result.y = point.y + distance * Math.sin(angle); 25 | return result; 26 | }; 27 | 28 | /** 29 | * Calculates points for a curve between two points 30 | * @param startPoint - the starting point of the curve 31 | * @param endPoint - the ending point of the curve 32 | * @param bulge - a value indicating how much to curve 33 | * @param segments - number of segments between the two given points 34 | */ 35 | THREE.BulgeGeometry = function ( startPoint, endPoint, bulge, segments ) { 36 | 37 | var vertex, i, 38 | center, p0, p1, angle, 39 | radius, startAngle, 40 | thetaAngle; 41 | 42 | THREE.Geometry.call( this ); 43 | 44 | this.startPoint = p0 = startPoint ? new THREE.Vector2(startPoint.x, startPoint.y) : new THREE.Vector2(0,0); 45 | this.endPoint = p1 = endPoint ? new THREE.Vector2(endPoint.x, endPoint.y) : new THREE.Vector2(1,0); 46 | this.bulge = bulge = bulge || 1; 47 | 48 | angle = 4 * Math.atan(bulge); 49 | radius = p0.distanceTo(p1) / 2 / Math.sin(angle/2); 50 | center = THREE.Math.polar(startPoint, radius, THREE.Math.angle2(p0,p1) + (Math.PI / 2 - angle/2)); 51 | 52 | this.segments = segments = segments || Math.max( Math.abs(Math.ceil(angle/(Math.PI/18))), 6); // By default want a segment roughly every 10 degrees 53 | startAngle = THREE.Math.angle2(center, p0); 54 | thetaAngle = angle / segments; 55 | 56 | 57 | this.vertices.push(new THREE.Vector3(p0.x, p0.y, 0)); 58 | 59 | for(i = 1; i <= segments - 1; i++) { 60 | 61 | vertex = THREE.Math.polar(center, Math.abs(radius), startAngle + thetaAngle * i); 62 | 63 | this.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0)); 64 | 65 | } 66 | 67 | }; 68 | 69 | THREE.BulgeGeometry.prototype = Object.create( THREE.Geometry.prototype ); 70 | 71 | /** 72 | * Viewer class for a dxf object. 73 | * @param {Object} data - the dxf object 74 | * @param {Object} parent - the parent element to which we attach the rendering canvas 75 | * @param {Number} width - width of the rendering canvas in pixels 76 | * @param {Number} height - height of the rendering canvas in pixels 77 | * @constructor 78 | */ 79 | function ThreeDxfViewer(data, parent, width, height) { 80 | var self = this; 81 | var $parent = $(parent); 82 | 83 | var scene = new THREE.Scene(); 84 | width = width || $parent.innerWidth(); 85 | height = height || $parent.innerHeight(); 86 | var aspectRatio = width / height; 87 | var viewSize = 30; 88 | var camera = new THREE.OrthographicCamera(viewSize * aspectRatio / -2, viewSize * aspectRatio / 2, viewSize / 2, viewSize / -2, 1, 19); 89 | camera.position.z = 10; 90 | camera.position.x = 570; 91 | camera.position.y = 335; 92 | // camera.position.x = 0; 93 | // camera.position.y = 0; 94 | 95 | // var renderer = this.renderer = new THREE.CanvasRenderer(); 96 | var renderer = this.renderer = new THREE.WebGLRenderer(); 97 | renderer.setSize(width, height); 98 | renderer.setClearColor(0xfffffff, 1); 99 | 100 | $parent.append(renderer.domElement); 101 | $parent.show(); 102 | 103 | var controls = new THREE.OrbitControls(camera, parent); 104 | controls.target.x = camera.position.x; 105 | controls.target.y = camera.position.y; 106 | controls.target.z = 0; 107 | controls.zoomSpeed = 3; 108 | 109 | createLineTypeShaders(data); 110 | 111 | var i, entity; 112 | 113 | for(i = 0; i < data.entities.length; i++) { 114 | entity = data.entities[i]; 115 | 116 | if(entity.type === 'DIMENSION') { 117 | if(entity.block) { 118 | var block = data.blocks[entity.block]; 119 | for(j = 0; j < block.entities.length; j++) { 120 | drawEntity(block.entities[j], data); 121 | } 122 | } else { 123 | console.log('WARNING: No block for DIMENSION entity'); 124 | } 125 | } else { 126 | drawEntity(entity, data); 127 | } 128 | } 129 | 130 | this.render = function() { 131 | renderer.render(scene, camera); 132 | }; 133 | 134 | controls.addEventListener('change', this.render); 135 | this.render(); 136 | 137 | $parent.on('click', function(event) { 138 | var $el = $(renderer.domElement); 139 | 140 | var vector = new THREE.Vector3( 141 | ( (event.pageX - $el.offset().left) / $el.innerWidth() ) * 2 - 1, 142 | -( (event.pageY - $el.offset().top) / $el.innerHeight() ) * 2 + 1, 143 | 0.5); 144 | var projector = new THREE.Projector(); 145 | projector.unprojectVector(vector, camera); 146 | 147 | var dir = vector.sub(camera.position).normalize(); 148 | 149 | var distance = -camera.position.z / dir.z; 150 | 151 | var pos = camera.position.clone().add(dir.multiplyScalar(distance)); 152 | 153 | console.log(pos.x, pos.y); // Position in cad that is clicked 154 | }); 155 | 156 | this.resize = function(width, height) { 157 | var originalWidth = renderer.domElement.width; 158 | var originalHeight = renderer.domElement.height; 159 | 160 | var hscale = width / originalWidth; 161 | var vscale = height / originalHeight; 162 | 163 | 164 | camera.top = (vscale * camera.top); 165 | camera.bottom = (vscale * camera.bottom); 166 | camera.left = (hscale * camera.left); 167 | camera.right = (hscale * camera.right); 168 | 169 | // camera.updateProjectionMatrix(); 170 | 171 | renderer.setSize(width, height); 172 | renderer.setClearColor(0xfffffff, 1); 173 | this.render(); 174 | }; 175 | 176 | function drawEntity(entity, data) { 177 | if(entity.type === 'CIRCLE' || entity.type === 'ARC') { 178 | drawCircle(entity, data); 179 | } else if(entity.type === 'LWPOLYLINE' || entity.type === 'LINE') { 180 | drawLine(entity, data); 181 | } else if(entity.type === 'TEXT') { 182 | drawText(entity, data); 183 | } else if(entity.type === 'SOLID') { 184 | drawSolid(entity, data); 185 | } else if(entity.type === 'POINT') { 186 | drawPoint(entity, data); 187 | } 188 | } 189 | 190 | function drawLine(entity, data) { 191 | var geometry = new THREE.Geometry(), 192 | color = getColor(entity, data), 193 | material, lineType, vertex, startPoint, endPoint, bulgeGeometry, 194 | bulge, i, line; 195 | 196 | // create geometry 197 | for(i = 0; i < entity.vertices.length; i++) { 198 | 199 | if(entity.vertices[i].bulge) { 200 | bulge = entity.vertices[i].bulge; 201 | startPoint = entity.vertices[i]; 202 | endPoint = i + 1 < entity.vertices.length ? entity.vertices[i + 1] : geometry.vertices[0]; 203 | 204 | bulgeGeometry = new THREE.BulgeGeometry(startPoint, endPoint, bulge); 205 | 206 | geometry.vertices.push.apply(geometry.vertices, bulgeGeometry.vertices); 207 | } else { 208 | vertex = entity.vertices[i]; 209 | geometry.vertices.push(new THREE.Vector3(vertex.x, vertex.y, 0)); 210 | } 211 | 212 | } 213 | if(entity.shape) geometry.vertices.push(geometry.vertices[0]); 214 | 215 | 216 | // set material 217 | if(entity.lineType) { 218 | lineType = data.tables.lineTypes[entity.lineType]; 219 | } 220 | 221 | if(lineType && lineType.pattern && lineType.pattern.length !== 0) { 222 | material = new THREE.LineDashedMaterial({ color: color, gapSize: 4, dashSize: 4}); 223 | } else { 224 | material = new THREE.LineBasicMaterial({ linewidth: 1, color: color }); 225 | } 226 | 227 | // if(lineType && lineType.pattern && lineType.pattern.length !== 0) { 228 | 229 | // geometry.computeLineDistances(); 230 | 231 | // // Ugly hack to add diffuse to this. Maybe copy the uniforms object so we 232 | // // don't add diffuse to a material. 233 | // lineType.material.uniforms.diffuse = { type: 'c', value: new THREE.Color(color) }; 234 | 235 | // material = new THREE.ShaderMaterial({ 236 | // uniforms: lineType.material.uniforms, 237 | // vertexShader: lineType.material.vertexShader, 238 | // fragmentShader: lineType.material.fragmentShader 239 | // }); 240 | // }else { 241 | // material = new THREE.LineBasicMaterial({ linewidth: 1, color: color }); 242 | // } 243 | 244 | line = new THREE.Line(geometry, material); 245 | scene.add(line); 246 | } 247 | 248 | function drawCircle(entity, data) { 249 | var geometry, material, circle; 250 | 251 | geometry = new THREE.CircleGeometry(entity.radius, 32, entity.startAngle, entity.angleLength); 252 | geometry.vertices.shift(); 253 | 254 | material = new THREE.LineBasicMaterial({ color: getColor(entity, data) }); 255 | 256 | circle = new THREE.Line(geometry, material); 257 | circle.position.x = entity.center.x; 258 | circle.position.y = entity.center.y; 259 | circle.position.z = entity.center.z; 260 | 261 | scene.add(circle); 262 | } 263 | 264 | function drawSolid(entity, data) { 265 | var material, mesh, solid, verts; 266 | geometry = new THREE.Geometry(); 267 | 268 | verts = geometry.vertices; 269 | verts.push(new THREE.Vector3(entity.points[0].x, entity.points[0].y, entity.points[0].z)); 270 | verts.push(new THREE.Vector3(entity.points[1].x, entity.points[1].y, entity.points[1].z)); 271 | verts.push(new THREE.Vector3(entity.points[2].x, entity.points[2].y, entity.points[2].z)); 272 | verts.push(new THREE.Vector3(entity.points[3].x, entity.points[3].y, entity.points[3].z)); 273 | 274 | // Calculate which direction the points are facing (clockwise or counter-clockwise) 275 | var vector1 = new THREE.Vector3(); 276 | var vector2 = new THREE.Vector3(); 277 | vector1.subVectors(verts[1], verts[0]); 278 | vector2.subVectors(verts[2], verts[0]); 279 | vector1.cross(vector2); 280 | 281 | // If z < 0 then we must draw these in reverse order 282 | if(vector1.z < 0) { 283 | geometry.faces.push(new THREE.Face3(2, 1, 0)); 284 | geometry.faces.push(new THREE.Face3(2, 3, 0)); 285 | } else { 286 | geometry.faces.push(new THREE.Face3(0, 1, 2)); 287 | geometry.faces.push(new THREE.Face3(0, 3, 2)); 288 | } 289 | 290 | 291 | material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) }); 292 | 293 | mesh = new THREE.Mesh(geometry, material); 294 | scene.add(mesh); 295 | } 296 | 297 | function drawText(entity, data) { 298 | var geometry, material, text; 299 | 300 | geometry = new THREE.TextGeometry(entity.text, { height: 0, size: entity.textHeight || 12 }); 301 | 302 | material = new THREE.MeshBasicMaterial({ color: getColor(entity, data) }); 303 | 304 | text = new THREE.Mesh(geometry, material); 305 | text.position.x = entity.startPoint.x; 306 | text.position.y = entity.startPoint.y; 307 | text.position.z = entity.startPoint.z; 308 | 309 | scene.add(text); 310 | } 311 | 312 | function drawPoint(entity, data) { 313 | var geometry, material, point; 314 | 315 | geometry = new THREE.Geometry(); 316 | 317 | geometry.vertices.push(new THREE.Vector3(entity.position.x, entity.position.y, entity.position.z)); 318 | 319 | // TODO: could be more efficient. PointCloud per layer? 320 | 321 | var numPoints = 1; 322 | 323 | var color = getColor(entity, data); 324 | var colors = new Float32Array( numPoints*3 ); 325 | colors[0] = color.r; 326 | colors[1] = color.g; 327 | colors[2] = color.b; 328 | 329 | geometry.colors = colors; 330 | geometry.computeBoundingBox(); 331 | 332 | material = new THREE.PointCloudMaterial( { size: 0.05, vertexColors: THREE.VertexColors } ); 333 | point = new THREE.PointCloud(geometry, material); 334 | scene.add(point); 335 | } 336 | 337 | function getColor(entity, data) { 338 | var color = entity.color || data.tables.layers[entity.layer].color; 339 | if(color === 0xffffff) { 340 | color = 0x000000; 341 | } 342 | return color; 343 | } 344 | 345 | function createLineTypeShaders(data) { 346 | var ltype, type; 347 | var ltypes = data.tables.lineTypes; 348 | 349 | for(type in ltypes) { 350 | ltype = ltypes[type]; 351 | if(!ltype.pattern) continue; 352 | ltype.material = createDashedLineShader(ltype.pattern); 353 | } 354 | } 355 | 356 | function createDashedLineShader(pattern) { 357 | var i, 358 | dashedLineShader = {}, 359 | totalLength = 0.0; 360 | 361 | for(i = 0; i < pattern.length; i++) { 362 | totalLength += Math.abs(pattern[i]); 363 | } 364 | 365 | dashedLineShader.uniforms = THREE.UniformsUtils.merge([ 366 | 367 | THREE.UniformsLib[ 'common' ], 368 | THREE.UniformsLib[ 'fog' ], 369 | 370 | { 371 | 'pattern': { type: 'fv1', value: pattern }, 372 | 'patternLength': { type: 'f', value: totalLength } 373 | } 374 | 375 | ]); 376 | 377 | dashedLineShader.vertexShader = [ 378 | 'attribute float lineDistance;', 379 | 380 | 'varying float vLineDistance;', 381 | 382 | THREE.ShaderChunk[ 'color_pars_vertex' ], 383 | 384 | 'void main() {', 385 | 386 | THREE.ShaderChunk[ 'color_vertex' ], 387 | 388 | 'vLineDistance = lineDistance;', 389 | 390 | 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', 391 | 392 | '}' 393 | ].join('\n'); 394 | 395 | dashedLineShader.fragmentShader = [ 396 | 'uniform vec3 diffuse;', 397 | 'uniform float opacity;', 398 | 399 | 'uniform float pattern[' + pattern.length + '];', 400 | 'uniform float patternLength;', 401 | 402 | 'varying float vLineDistance;', 403 | 404 | THREE.ShaderChunk[ 'color_pars_fragment' ], 405 | THREE.ShaderChunk[ 'fog_pars_fragment' ], 406 | 407 | 'void main() {', 408 | 409 | 'float pos = mod(vLineDistance, patternLength);', 410 | 411 | 'for ( int i = 0; i < ' + pattern.length + '; i++ ) {', 412 | 'pos = pos - abs(pattern[i]);', 413 | 'if( pos < 0.0 ) {', 414 | 'if( pattern[i] > 0.0 ) {', 415 | 'gl_FragColor = vec4(1.0, 0.0, 0.0, opacity );', 416 | 'break;', 417 | '}', 418 | 'discard;', 419 | '}', 420 | 421 | '}', 422 | 423 | THREE.ShaderChunk[ 'color_fragment' ], 424 | THREE.ShaderChunk[ 'fog_fragment' ], 425 | 426 | '}' 427 | ].join('\n'); 428 | 429 | return dashedLineShader; 430 | } 431 | 432 | } -------------------------------------------------------------------------------- /i/dxf/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dxf-Parser - Browser Sample 5 | 6 | 7 | 8 | 9 |
    10 | 
    11 | 		
    12 | 13 | 14 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /i/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/favicon.ico -------------------------------------------------------------------------------- /i/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /i/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /i/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /i/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /i/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /i/gcode-viewer/GridRect._OLD: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | AUTHOR: Mr Doob 4 | 5 | */ 6 | 7 | // Code courtesy of https://github.com/mrdoob/three.js/issues/6549 8 | 9 | 10 | THREE.GridHelperRect = function ( sizeX, stepX, sizeZ, stepZ ) { 11 | 12 | var x = Math.round( sizeX / stepX ); 13 | var y = Math.round( sizeZ / stepZ ); 14 | 15 | sizeX = x * stepX; 16 | sizeZ = y * stepZ; 17 | 18 | // console.log( "Grid sizeX: " + sizeX * 2 ); 19 | //console.log( "Grid sizeZ: " + sizeZ * 2 ); 20 | 21 | var geometry = new THREE.Geometry(); 22 | var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); 23 | 24 | this.color1 = new THREE.Color( 0x444444 ); 25 | this.color2 = new THREE.Color( 0x888888 ); 26 | 27 | for ( var i = - 1 * sizeX; i <= sizeX; i += stepX ) { 28 | 29 | geometry.vertices.push( 30 | new THREE.Vector3( i, 0, - 1 * sizeZ ), //x Y z 31 | new THREE.Vector3( i, 0, sizeZ ) //x Y z 32 | ); 33 | 34 | var color = i === 0 ? this.color1 : this.color2; 35 | 36 | geometry.colors.push( color, color, color, color ); 37 | 38 | } 39 | 40 | for ( var i = - 1 * sizeZ; i <= sizeZ; i += stepZ ) { 41 | 42 | geometry.vertices.push( 43 | new THREE.Vector3( - 1 * sizeX, 0, i ), //x Y z 44 | new THREE.Vector3( sizeX, 0, i ) //x Y z 45 | ); 46 | 47 | var color = i === 0 ? this.color1 : this.color2; 48 | 49 | geometry.colors.push( color, color, color, color ); 50 | 51 | } 52 | 53 | THREE.Line.call( this, geometry, material, THREE.LinePieces ); 54 | 55 | }; 56 | 57 | THREE.GridHelperRect.prototype = Object.create( THREE.Line.prototype ); 58 | THREE.GridHelperRect.prototype.constructor = THREE.GridHelper; 59 | 60 | THREE.GridHelperRect.prototype.setColors = function( colorCenterLine, colorGrid ) { 61 | 62 | this.color1.set( colorCenterLine ); 63 | this.color2.set( colorGrid ); 64 | 65 | this.geometry.colorsNeedUpdate = true; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /i/gcode-viewer/renderer.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | AUTHOR: John Lauer 4 | 5 | */ 6 | 7 | function createScene(element) { 8 | 9 | 10 | var canvas = !! window.CanvasRenderingContext2D; 11 | var webgl = ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(); 12 | 13 | if (webgl) { 14 | $('#console').append('

    WebGL detected!
    Continuing with best Laserweb 3D Viewer Experience


    '); 15 | $('#console').scrollTop($("#console")[0].scrollHeight - $("#console").height()); 16 | 17 | renderer = new THREE.WebGLRenderer({ 18 | autoClearColor: true 19 | }); 20 | 21 | } else if (canvas) { 22 | 23 | $('#console').append('

    CRITICAL ERROR: No WebGL Support!
    Laserweb may not work on this computer! Try another computer with WebGL support


    Try the following:
    Sorry! :(
    '); 24 | $('#console').scrollTop($("#console")[0].scrollHeight - $("#console").height()); 25 | 26 | $( "#noWebGL" ).append('

    No WebGL Support found!

    CRITICAL ERROR:
    Laserweb may not work on this computer!
    Try another computer with WebGL support


    Try the following:
    Sorry! :(
    '); 27 | }; 28 | 29 | // renderer setup 30 | renderer = new THREE.WebGLRenderer({ 31 | autoClearColor: true 32 | }); 33 | 34 | renderer.setClearColor(0xffffff, 1); // Background color of viewer 35 | renderer.setSize(element.width(), (element.height() -30 )); 36 | element.append(renderer.domElement); 37 | renderer.clear(); 38 | 39 | // scene 40 | var scene = new THREE.Scene(); 41 | 42 | // lighting 43 | var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); 44 | directionalLight.position.set( 0, 1, 0 ); 45 | scene.add( directionalLight ); 46 | 47 | // camera 48 | var fov = 45, 49 | aspect = element.width() / element.height(), 50 | near = 1, 51 | far = 12000; 52 | camera = new THREE.PerspectiveCamera(fov, aspect, near, far); 53 | camera.position.z = 525; 54 | camera.position.x = 0; 55 | camera.position.y = 0; 56 | scene.add(camera); 57 | 58 | 59 | // Disabling mouse 60 | controls = new THREE.TrackballControls(camera, renderer.domElement); 61 | controls.noPan = false; 62 | controls.noZoom = false; 63 | controls.zoomSpeed = 1.2; 64 | controls.panSpeed = 1; 65 | controls.rotateSpeed = 1; 66 | 67 | 68 | 69 | // render 70 | function render() { 71 | controls.update(); 72 | renderer.render(scene, camera); 73 | requestAnimationFrame(render); 74 | } 75 | 76 | 77 | render(); 78 | 79 | // fix controls if window is resized. 80 | $(window).on('resize', function() { 81 | renderer.setSize(element.width(), element.height()); 82 | camera.aspect = element.width() / element.height(); 83 | camera.updateProjectionMatrix(); 84 | controls.screen.width = window.innerWidth; 85 | controls.screen.height = window.innerHeight; 86 | controls.reset(); 87 | }); 88 | 89 | 90 | 91 | 92 | 93 | return scene; 94 | } 95 | -------------------------------------------------------------------------------- /i/gcode-viewer/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GCode Viewer 6 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 |
    37 |
    38 |
    39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /i/gcode-viewer/ui.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | AUTHOR: John Lauer 4 | -- Significant UI changes by AUTHOR: Peter van der Walt 5 | 6 | */ 7 | 8 | 9 | var scene = null; 10 | var object = null; 11 | var cylinder = null; 12 | var axes = null; 13 | var axesgrp = null; 14 | var helper = null; 15 | 16 | 17 | var added = false; 18 | 19 | 20 | // Specific to your machine 21 | var laserxmax = 600 22 | var laserymax = 400 23 | var lineincrement = 50 24 | 25 | 26 | $(function() { 27 | 28 | scene = createScene($('#renderArea')); 29 | }); 30 | 31 | function createObject(gcode) { 32 | if (object) { 33 | scene.remove(object); 34 | } 35 | 36 | //Create the object 37 | createObjectFromGCode(gcode); 38 | // object = drawobject(); 39 | 40 | //object.translateX(laserxmax /2 * -1); 41 | //object.translateY(laserymax /2 * -1); 42 | 43 | //scene.add(object); 44 | //console.log('[VIEWER] - added Object'); 45 | if (cylinder) { 46 | scene.remove(cylinder); 47 | } 48 | // jogArrow Cylinder 49 | cylinder = new THREE.Mesh(new THREE.CylinderGeometry(0, 5, 35, 15, 1, false), new THREE.MeshBasicMaterial( {color: 0x005599} )); 50 | cylinder.overdraw = true; 51 | cylinder.rotation.x = -90 * Math.PI / 180; 52 | cylinder.material.opacity = 0.6; 53 | cylinder.material.transparent = true; 54 | cylinder.castShadow = false; 55 | cylinder.position.x = (laserxmax /2 * -1) 56 | cylinder.position.y = (laserymax /2 * -1) 57 | cylinder.position.z = 20; 58 | cylinder.needsUpdate = true; 59 | //console.log('[VIEWER] - added Cone'); 60 | scene.add(cylinder) 61 | 62 | 63 | if (helper) { 64 | scene.remove(helper); 65 | } 66 | 67 | helper = new THREE.GridHelperRect((laserxmax /2), 10, (laserymax /2), 10); 68 | helper.setColors(0x0000ff, 0x707070); 69 | helper.position.y = 0; 70 | helper.position.x = 0; 71 | helper.position.z = 0; 72 | helper.rotation.x = 90 * Math.PI / 180; 73 | helper.material.opacity = 0.15; 74 | helper.material.transparent = true; 75 | helper.receiveShadow = false; 76 | //console.log("helper grid:", helper); 77 | this.grid = helper; 78 | //this.sceneAdd(this.grid); 79 | //console.log('[VIEWER] - added Helpert'); 80 | scene.add(helper); 81 | 82 | 83 | 84 | if (axes) { 85 | scene.remove(axes); 86 | } 87 | 88 | if (axesgrp) { 89 | scene.remove(axesgrp); 90 | } 91 | axesgrp = new THREE.Object3D(); 92 | 93 | axes = new THREE.AxisHelper(120); 94 | 95 | axes.material.transparent = true; 96 | axes.material.opacity = 0.8; 97 | axes.material.depthWrite = false; 98 | axes.position.set(0,0,-0.0001); 99 | axes.translateX(laserxmax /2 * -1); 100 | axes.translateY(laserymax /2 * -1); 101 | 102 | //console.log('[VIEWER] - added Axes'); 103 | scene.add(axes); 104 | 105 | var x = []; 106 | var y = []; 107 | for (var i = 0; i < laserxmax ; i+=lineincrement) { 108 | 109 | x[i] = this.makeSprite(this.scene, "webgl", { 110 | x: i, 111 | y: -10, 112 | z: 0, 113 | text: i, 114 | color: "#ff0000" 115 | }); 116 | axesgrp.add(x[i]); 117 | } 118 | 119 | for (var i = 0; i < laserymax ; i+=lineincrement) { 120 | 121 | y[i] = this.makeSprite(this.scene, "webgl", { 122 | x: -10, 123 | y: i, 124 | z: 0, 125 | text: i, 126 | color: "#006600" 127 | }); 128 | axesgrp.add(y[i]); 129 | } 130 | // add axes labels 131 | var xlbl = this.makeSprite(this.scene, "webgl", { 132 | x: 125, 133 | y: 0, 134 | z: 0, 135 | text: "X", 136 | color: "#ff0000" 137 | }); 138 | var ylbl = this.makeSprite(this.scene, "webgl", { 139 | x: 0, 140 | y: 125, 141 | z: 0, 142 | text: "Y", 143 | color: "#006600" 144 | }); 145 | var zlbl = this.makeSprite(this.scene, "webgl", { 146 | x: 0, 147 | y: 0, 148 | z: 125, 149 | text: "Z", 150 | color: "#0000ff" 151 | }); 152 | 153 | 154 | axesgrp.add(xlbl); 155 | axesgrp.add(ylbl); 156 | axesgrp.add(zlbl); 157 | 158 | axesgrp.translateX(laserxmax /2 * -1); 159 | axesgrp.translateY(laserymax /2 * -1); 160 | //console.log('[VIEWER] - added Axesgrp'); 161 | scene.add(axesgrp); 162 | 163 | } 164 | 165 | function openGCodeFromText() { 166 | $('#renderprogressholder .progress-bar').width(0); 167 | //console.log('Starting Gcode Render'); 168 | var startTime = Date.now(); 169 | var gcode = $('#gcodepreview').val(); 170 | //if (document.hasFocus()) { 171 | createObject(gcode); 172 | //console.log('adding object with existing focus'); 173 | // } else { 174 | // wait for focus, then render 175 | //console.log('waiting for focus'); 176 | //$(window).bind('focus', function(event) { 177 | // createObject(gcode); 178 | // //console.log('focus exists'); 179 | // // unbind for next object load 180 | // $(this).unbind(event); 181 | // }); 182 | // } 183 | console.timeEnd("Process 3D View"); 184 | var currentTime = Date.now(); 185 | var elapsed = (currentTime - startTime); 186 | $('#console').append('

    3D Render completed in '+elapsed+' ms

    '); 187 | $('#console').scrollTop($("#console")[0].scrollHeight - $("#console").height()); 188 | 189 | // Pretty Gcode Viewer 190 | //$("#gcodelinestbody").empty(); 191 | 192 | // rest of gcodelinestbody is written by the doChunk on gcode-parser 193 | 194 | } 195 | 196 | 197 | function makeSprite(scene, rendererType, vals) { 198 | var canvas = document.createElement('canvas'), 199 | context = canvas.getContext('2d'), 200 | metrics = null, 201 | textHeight = 100, 202 | textWidth = 0, 203 | actualFontSize = 10; 204 | var txt = vals.text; 205 | if (vals.size) actualFontSize = vals.size; 206 | 207 | context.font = "normal " + textHeight + "px Arial"; 208 | metrics = context.measureText(txt); 209 | var textWidth = metrics.width; 210 | 211 | canvas.width = textWidth; 212 | canvas.height = textHeight; 213 | context.font = "normal " + textHeight + "px Arial"; 214 | context.textAlign = "center"; 215 | context.textBaseline = "middle"; 216 | //context.fillStyle = "#ff0000"; 217 | context.fillStyle = vals.color; 218 | 219 | context.fillText(txt, textWidth / 2, textHeight / 2); 220 | 221 | var texture = new THREE.Texture(canvas); 222 | texture.needsUpdate = true; 223 | texture.minFilter = THREE.LinearFilter; 224 | 225 | var material = new THREE.SpriteMaterial({ 226 | map: texture, 227 | useScreenCoordinates: false, 228 | transparent: true, 229 | opacity: 0.6 230 | }); 231 | material.transparent = true; 232 | //var textObject = new THREE.Sprite(material); 233 | var textObject = new THREE.Object3D(); 234 | textObject.position.x = vals.x; 235 | textObject.position.y = vals.y; 236 | textObject.position.z = vals.z; 237 | var sprite = new THREE.Sprite(material); 238 | textObject.textHeight = actualFontSize; 239 | textObject.textWidth = (textWidth / textHeight) * textObject.textHeight; 240 | if (rendererType == "2d") { 241 | sprite.scale.set(textObject.textWidth / textWidth, textObject.textHeight / textHeight, 1); 242 | } else { 243 | sprite.scale.set(textWidth / textHeight * actualFontSize, actualFontSize, 1); 244 | } 245 | 246 | textObject.add(sprite); 247 | 248 | //scene.add(textObject); 249 | return textObject; 250 | } 251 | -------------------------------------------------------------------------------- /i/js/bootstrap-notify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-notify.js v1.0 3 | * -- 4 | * Copyright 2012 Goodybag, Inc. 5 | * -- 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | (function ($) { 20 | var Notification = function (element, options) { 21 | // Element collection 22 | this.$element = $(element); 23 | this.$note = $('
    '); 24 | this.options = $.extend(true, {}, $.fn.notify.defaults, options); 25 | 26 | // Setup from options 27 | if(this.options.transition) { 28 | if(this.options.transition == 'fade') 29 | this.$note.addClass('in').addClass(this.options.transition); 30 | else 31 | this.$note.addClass(this.options.transition); 32 | } else 33 | this.$note.addClass('fade').addClass('in'); 34 | 35 | if(this.options.type) 36 | this.$note.addClass('alert-' + this.options.type); 37 | else 38 | this.$note.addClass('alert-success'); 39 | 40 | if(!this.options.message && this.$element.data("message") !== '') // dom text 41 | this.$note.html(this.$element.data("message")); 42 | else 43 | if(typeof this.options.message === 'object') { 44 | if(this.options.message.html) 45 | this.$note.html(this.options.message.html); 46 | else if(this.options.message.text) 47 | this.$note.text(this.options.message.text); 48 | } else 49 | this.$note.html(this.options.message); 50 | 51 | if(this.options.closable) { 52 | var link = $('×'); 53 | $(link).on('click', $.proxy(onClose, this)); 54 | this.$note.prepend(link); 55 | } 56 | 57 | return this; 58 | }; 59 | 60 | var onClose = function() { 61 | this.options.onClose(); 62 | $(this.$note).remove(); 63 | this.options.onClosed(); 64 | return false; 65 | }; 66 | 67 | Notification.prototype.show = function () { 68 | if(this.options.fadeOut.enabled) 69 | this.$note.delay(this.options.fadeOut.delay || 3000).fadeOut('slow', $.proxy(onClose, this)); 70 | 71 | this.$element.append(this.$note); 72 | this.$note.alert(); 73 | }; 74 | 75 | Notification.prototype.hide = function () { 76 | if(this.options.fadeOut.enabled) 77 | this.$note.delay(this.options.fadeOut.delay || 3000).fadeOut('slow', $.proxy(onClose, this)); 78 | else onClose.call(this); 79 | }; 80 | 81 | $.fn.notify = function (options) { 82 | return new Notification(this, options); 83 | }; 84 | 85 | $.fn.notify.defaults = { 86 | type: 'success', 87 | closable: true, 88 | transition: 'fade', 89 | fadeOut: { 90 | enabled: true, 91 | delay: 3000 92 | }, 93 | message: null, 94 | onClose: function () {}, 95 | onClosed: function () {} 96 | } 97 | })(window.jQuery); 98 | -------------------------------------------------------------------------------- /i/js/jog.js: -------------------------------------------------------------------------------- 1 | 2 | function initJog() { 3 | 4 | $('#xp').on('click', function() { 5 | var dist = $('#jogstp').val() 6 | console.log('Jog Distance', dist); 7 | sendGcode('G91\nG0 X'+ +'\nG90') { 8 | }; 9 | 10 | 11 | 12 | 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /i/openjscad/LaserWebapps.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | AUTHOR: Peter van der Walt 4 | 5 | */ 6 | // Show all exceptions to the user: 7 | OpenJsCad.AlertUserOfUncaughtExceptions(); 8 | 9 | function updateSolid() 10 | { 11 | gProcessor.setJsCad(document.getElementById('code').value); 12 | } 13 | 14 | function loadGeargen() 15 | { 16 | gProcessor = new OpenJsCad.Processor(document.getElementById("viewer")); 17 | getProg('openjscad_files/gear.jscad'); 18 | $('#JSHelp').empty(); 19 | $( "#JSHelp" ).append("Try Negative and Zero Teeth Count too: (works best on Gear 1)
    Tooth count < 0Tooth count = 0Tooth count > 0
    " 20 | ); 21 | } 22 | 23 | function loadBoxgen() 24 | { 25 | gProcessor = new OpenJsCad.Processor(document.getElementById("viewer")); 26 | getProg('openjscad_files/lasercut_box.jscad'); 27 | $('#JSHelp').empty(); 28 | $( "#JSHelp" ).append("Try the Cutouts features too!" 29 | ); 30 | } 31 | 32 | function getProg(name) { 33 | el = document.getElementById('code'); 34 | $.get(name, function(data) { 35 | el.value = data; 36 | updateSolid(); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /i/openjscad/img/InternalGear_Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/openjscad/img/InternalGear_Small.png -------------------------------------------------------------------------------- /i/openjscad/img/RackAndPinion_Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/openjscad/img/RackAndPinion_Small.png -------------------------------------------------------------------------------- /i/openjscad/img/RegularSpurGear_Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/openjscad/img/RegularSpurGear_Small.png -------------------------------------------------------------------------------- /i/openjscad/openjscad.css: -------------------------------------------------------------------------------- 1 | canvas { cursor: move; } 2 | 3 | .parametersdiv { 4 | border: 0px solid rgb(200,200,200); 5 | -moz-border-radius: 5px; 6 | -webkit-border-radius: 5px; 7 | border-radius: 5px; 8 | padding: 0px; 9 | } 10 | 11 | .parametersdiv .header { 12 | font-weight: bold; 13 | } 14 | 15 | .statusdiv { 16 | width: 500px; 17 | margin-bottom: 10px; 18 | } 19 | -------------------------------------------------------------------------------- /i/openjscad/src/formats.js: -------------------------------------------------------------------------------- 1 | /* 2 | ## License 3 | 4 | Copyright (c) 2014 bebbi (elghatta@gmail.com) 5 | Copyright (c) 2013 Eduard Bespalov (edwbes@gmail.com) 6 | Copyright (c) 2012 Joost Nieuwenhuijse (joost@newhouse.nl) 7 | Copyright (c) 2011 Evan Wallace (http://evanw.github.com/csg.js/) 8 | Copyright (c) 2012 Alexandre Girard (https://github.com/alx) 9 | 10 | All code released under MIT license 11 | 12 | */ 13 | 14 | if(typeof module !== 'undefined') { // used via nodejs 15 | CSG = require(lib+'csg.js').CSG; 16 | CAG = require(lib+'csg.js').CAG; 17 | } 18 | 19 | //////////////////////////////////////////// 20 | // X3D Export 21 | //////////////////////////////////////////// 22 | 23 | CSG.prototype.toX3D = function() { 24 | // materialPolygonLists 25 | // key: a color string (e.g. "0 1 1" for yellow) 26 | // value: an array of strings specifying polygons of this color 27 | // (as space-separated indices into vertexCoords) 28 | var materialPolygonLists = {}, 29 | // list of coordinates (as "x y z" strings) 30 | vertexCoords = [], 31 | // map to look up the index in vertexCoords of a given vertex 32 | vertexTagToCoordIndexMap = {}; 33 | 34 | this.polygons.map(function(p) { 35 | var red = 0, 36 | green = 0, 37 | blue = 1; // default color is blue 38 | if (p.shared && p.shared.color) { 39 | red = p.shared.color[0]; 40 | green = p.shared.color[1]; 41 | blue = p.shared.color[2]; 42 | } 43 | 44 | var polygonVertexIndices = [], 45 | numvertices = p.vertices.length, 46 | vertex; 47 | for (var i = 0; i < numvertices; i++) { 48 | vertex = p.vertices[i]; 49 | if (!(vertex.getTag() in vertexTagToCoordIndexMap)) { 50 | vertexCoords.push(vertex.pos._x.toString() + " " + 51 | vertex.pos._y.toString() + " " + 52 | vertex.pos._z.toString() 53 | ); 54 | vertexTagToCoordIndexMap[vertex.getTag()] = vertexCoords.length - 1; 55 | } 56 | polygonVertexIndices.push(vertexTagToCoordIndexMap[vertex.getTag()]); 57 | } 58 | 59 | var polygonString = polygonVertexIndices.join(" "); 60 | 61 | var colorString = red.toString() + " " + green.toString() + " " + blue.toString(); 62 | if (!(colorString in materialPolygonLists)) { 63 | materialPolygonLists[colorString] = []; 64 | } 65 | // add this polygonString to the list of colorString-colored polygons 66 | materialPolygonLists[colorString].push(polygonString); 67 | }); 68 | 69 | 70 | // create output document 71 | var docType = document.implementation.createDocumentType("X3D", 72 | 'ISO//Web3D//DTD X3D 3.1//EN" "http://www.web3d.org/specifications/x3d-3.1.dtd', null); 73 | var exportDoc = document.implementation.createDocument(null, "X3D", docType); 74 | exportDoc.insertBefore( 75 | exportDoc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"'), 76 | exportDoc.doctype); 77 | 78 | var exportRoot = exportDoc.getElementsByTagName("X3D")[0]; 79 | exportRoot.setAttribute("profile", "Interchange"); 80 | exportRoot.setAttribute("version", "3.1"); 81 | exportRoot.setAttribute("xsd:noNamespaceSchemaLocation", "http://www.web3d.org/specifications/x3d-3.1.xsd"); 82 | exportRoot.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance"); 83 | 84 | var exportScene = exportDoc.createElement("Scene"); 85 | exportRoot.appendChild(exportScene); 86 | 87 | /* 88 | For each color, create a shape made of an appropriately colored 89 | material which contains all polygons that are this color. 90 | 91 | The first shape will contain the definition of all vertices, 92 | (), which will be referenced by 93 | subsequent shapes. 94 | */ 95 | var coordsMeshDefined = false; 96 | for (var colorString in materialPolygonLists) { 97 | var polygonList = materialPolygonLists[colorString]; 98 | var shape = exportDoc.createElement("Shape"); 99 | exportScene.appendChild(shape); 100 | 101 | var appearance = exportDoc.createElement("Appearance"); 102 | shape.appendChild(appearance); 103 | 104 | var material = exportDoc.createElement("Material"); 105 | appearance.appendChild(material); 106 | material.setAttribute("diffuseColor", colorString); 107 | material.setAttribute("ambientIntensity", "1.0"); 108 | 109 | var ifs = exportDoc.createElement("IndexedFaceSet"); 110 | shape.appendChild(ifs); 111 | ifs.setAttribute("solid", "true"); 112 | ifs.setAttribute("coordIndex", polygonList.join(" -1 ") + " -1"); 113 | 114 | var coordinate = exportDoc.createElement("Coordinate"); 115 | ifs.appendChild(coordinate); 116 | if (coordsMeshDefined) { 117 | coordinate.setAttribute("USE", "coords_mesh"); 118 | } else { 119 | coordinate.setAttribute("DEF", "coords_mesh"); 120 | coordinate.setAttribute("point", vertexCoords.join(" ")); 121 | coordsMeshDefined = true; 122 | } 123 | } 124 | 125 | var x3dstring = (new XMLSerializer()).serializeToString(exportDoc); 126 | return new Blob([x3dstring], { 127 | type: "model/x3d+xml" 128 | }); 129 | }; 130 | 131 | //////////////////////////////////////////// 132 | // STL Binary Export 133 | //////////////////////////////////////////// 134 | 135 | // see http://en.wikipedia.org/wiki/STL_%28file_format%29#Binary_STL 136 | CSG.prototype.toStlBinary = function() { 137 | // first check if the host is little-endian: 138 | var buffer = new ArrayBuffer(4); 139 | var int32buffer = new Int32Array(buffer, 0, 1); 140 | var int8buffer = new Int8Array(buffer, 0, 4); 141 | int32buffer[0] = 0x11223344; 142 | if (int8buffer[0] != 0x44) { 143 | throw new Error("Binary STL output is currently only supported on little-endian (Intel) processors"); 144 | } 145 | 146 | var numtriangles = 0; 147 | this.polygons.map(function(p) { 148 | var numvertices = p.vertices.length; 149 | var thisnumtriangles = (numvertices >= 3) ? numvertices - 2 : 0; 150 | numtriangles += thisnumtriangles; 151 | }); 152 | var headerarray = new Uint8Array(80); 153 | for (var i = 0; i < 80; i++) { 154 | headerarray[i] = 65; 155 | } 156 | var ar1 = new Uint32Array(1); 157 | ar1[0] = numtriangles; 158 | // write the triangles to allTrianglesBuffer: 159 | var allTrianglesBuffer = new ArrayBuffer(50 * numtriangles); 160 | var allTrianglesBufferAsInt8 = new Int8Array(allTrianglesBuffer); 161 | // a tricky problem is that a Float32Array must be aligned at 4-byte boundaries (at least in certain browsers) 162 | // while each triangle takes 50 bytes. Therefore we write each triangle to a temporary buffer, and copy that 163 | // into allTrianglesBuffer: 164 | var triangleBuffer = new ArrayBuffer(50); 165 | var triangleBufferAsInt8 = new Int8Array(triangleBuffer); 166 | // each triangle consists of 12 floats: 167 | var triangleFloat32array = new Float32Array(triangleBuffer, 0, 12); 168 | // and one uint16: 169 | var triangleUint16array = new Uint16Array(triangleBuffer, 48, 1); 170 | var byteoffset = 0; 171 | this.polygons.map(function(p) { 172 | var numvertices = p.vertices.length; 173 | for (var i = 0; i < numvertices - 2; i++) { 174 | var normal = p.plane.normal; 175 | triangleFloat32array[0] = normal._x; 176 | triangleFloat32array[1] = normal._y; 177 | triangleFloat32array[2] = normal._z; 178 | var arindex = 3; 179 | for (var v = 0; v < 3; v++) { 180 | var vv = v + ((v > 0) ? i : 0); 181 | var vertexpos = p.vertices[vv].pos; 182 | triangleFloat32array[arindex++] = vertexpos._x; 183 | triangleFloat32array[arindex++] = vertexpos._y; 184 | triangleFloat32array[arindex++] = vertexpos._z; 185 | } 186 | triangleUint16array[0] = 0; 187 | // copy the triangle into allTrianglesBuffer: 188 | allTrianglesBufferAsInt8.set(triangleBufferAsInt8, byteoffset); 189 | byteoffset += 50; 190 | } 191 | }); 192 | return new Blob([headerarray.buffer, ar1.buffer, allTrianglesBuffer], { 193 | type: "application/sla" 194 | }); 195 | }; 196 | 197 | //////////////////////////////////////////// 198 | // STL String Export 199 | //////////////////////////////////////////// 200 | 201 | CSG.prototype.toStlString = function() { 202 | var result = "solid csg.js\n"; 203 | this.polygons.map(function(p) { 204 | result += p.toStlString(); 205 | }); 206 | result += "endsolid csg.js\n"; 207 | return result; 208 | }; 209 | 210 | CSG.Vector3D.prototype.toStlString = function() { 211 | return this._x + " " + this._y + " " + this._z; 212 | }; 213 | 214 | CSG.Vertex.prototype.toStlString = function() { 215 | return "vertex " + this.pos.toStlString() + "\n"; 216 | }; 217 | 218 | CSG.Polygon.prototype.toStlString = function() { 219 | var result = ""; 220 | if (this.vertices.length >= 3) // should be! 221 | { 222 | // STL requires triangular polygons. If our polygon has more vertices, create 223 | // multiple triangles: 224 | var firstVertexStl = this.vertices[0].toStlString(); 225 | for (var i = 0; i < this.vertices.length - 2; i++) { 226 | result += "facet normal " + this.plane.normal.toStlString() + "\nouter loop\n"; 227 | result += firstVertexStl; 228 | result += this.vertices[i + 1].toStlString(); 229 | result += this.vertices[i + 2].toStlString(); 230 | result += "endloop\nendfacet\n"; 231 | } 232 | } 233 | return result; 234 | }; 235 | 236 | //////////////////////////////////////////// 237 | // DXF Export 238 | //////////////////////////////////////////// 239 | 240 | CAG.PathsToDxf = function(paths) { 241 | var str = "999\nDXF generated by OpenJsCad\n"; 242 | str += " 0\nSECTION\n 2\nHEADER\n"; 243 | str += " 0\nENDSEC\n"; 244 | str += " 0\nSECTION\n 2\nTABLES\n"; 245 | str += " 0\nTABLE\n 2\nLTYPE\n 70\n1\n"; 246 | str += " 0\nLTYPE\n 2\nCONTINUOUS\n 3\nSolid Line\n 72\n65\n 73\n0\n 40\n0.0\n"; 247 | str += " 0\nENDTAB\n"; 248 | str += " 0\nTABLE\n 2\nLAYER\n 70\n1\n"; 249 | str += " 0\nLAYER\n 2\nOpenJsCad\n 62\n7\n 6\ncontinuous\n"; 250 | str += " 0\nENDTAB\n"; 251 | str += " 0\nTABLE\n 2\nSTYLE\n 70\n0\n 0\nENDTAB\n"; 252 | str += " 0\nTABLE\n 2\nVIEW\n 70\n0\n 0\nENDTAB\n"; 253 | str += " 0\nENDSEC\n"; 254 | str += " 0\nSECTION\n 2\nBLOCKS\n"; 255 | str += " 0\nENDSEC\n"; 256 | str += " 0\nSECTION\n 2\nENTITIES\n"; 257 | paths.map(function(path) { 258 | var numpoints_closed = path.points.length + (path.closed ? 1 : 0); 259 | str += " 0\nLWPOLYLINE\n 8\nOpenJsCad\n 90\n" + numpoints_closed + "\n 70\n" + (path.closed ? 1 : 0) + "\n"; 260 | for (var pointindex = 0; pointindex < numpoints_closed; pointindex++) { 261 | var pointindexwrapped = pointindex; 262 | if (pointindexwrapped >= path.points.length) pointindexwrapped -= path.points.length; 263 | var point = path.points[pointindexwrapped]; 264 | str += " 10\n" + point.x + "\n 20\n" + point.y + "\n 30\n0.0\n"; 265 | } 266 | }); 267 | str += " 0\nENDSEC\n 0\nEOF\n"; 268 | 269 | document.getElementById('dxf').value = str; 270 | 271 | document.getElementById('generatePreview').click(); 272 | return new Blob([str], { 273 | type: "application/dxf" 274 | }); 275 | 276 | }; 277 | 278 | CAG.prototype.toDxf = function() { 279 | var paths = this.getOutlinePaths(); 280 | return CAG.PathsToDxf(paths); 281 | }; 282 | 283 | //////////////////////////////////////////// 284 | // AMF Export 285 | //////////////////////////////////////////// 286 | 287 | CSG.prototype.toAMFString = function(m) { 288 | var result = "\n\n"; 289 | for(var k in m) { 290 | result += ""+m[k]+"\n"; 291 | } 292 | result += "\n\n\n"; 293 | 294 | this.polygons.map(function(p) { // first we dump all vertices of all polygons 295 | for(var i=0; i3) a = p.color[3]; 318 | colorSet = true; 319 | } 320 | result += ""+r+""+g+""+b+""+(a!==undefined?""+a+"":"")+""; 321 | 322 | for(var i=0; i"; 325 | result += "" + (n+i+1) + ""; 326 | result += "" + (n+i+2) + ""; 327 | result += "\n"; 328 | } 329 | n += p.vertices.length; 330 | result += "\n"; 331 | }); 332 | result += "\n\n"; 333 | result += "\n"; 334 | return result; 335 | }; 336 | 337 | CSG.Vector3D.prototype.toAMFString = function() { 338 | return "" + this._x + "" + this._y + "" + this._z + ""; 339 | }; 340 | 341 | CSG.Vertex.prototype.toAMFString = function() { 342 | return "" + this.pos.toAMFString() + "\n"; 343 | }; 344 | -------------------------------------------------------------------------------- /i/openjscad/src/threecsg.js: -------------------------------------------------------------------------------- 1 | /* 2 | adapted from THREE.CSG 3 | @author Chandler Prall http://chandler.prallfamily.com 4 | 5 | Wrapper for Evan Wallace's CSG library (https://github.com/evanw/csg.js/) 6 | Provides CSG capabilities for Three.js models. 7 | 8 | Provided under the MIT License 9 | 10 | */ 11 | THREE.CSG = { 12 | // convert CSG object to three.js mesh. 13 | fromCSG: function(csg, defaultColor) { 14 | 15 | var i, j, vertices, face, 16 | three_geometry = new THREE.Geometry(), 17 | polygons = csg.toPolygons(); 18 | 19 | if ( !CSG ) { 20 | throw 'CSG library not loaded. Please get a copy from https://github.com/evanw/csg.js'; 21 | } 22 | 23 | // dict of different THREE.Colors in mesh 24 | var colors = {}; 25 | // list of different opacities used by polygons 26 | var opacities = []; 27 | var materialIdx, opacity, colorKey, polyColor, color; 28 | 29 | polygons.forEach(function(polygon) { 30 | // polygon shared null? -> defaultColor, else extract color 31 | var vertices = polygon.vertices.map(function(vertex) { 32 | return this.getGeometryVertex(three_geometry, vertex.pos); 33 | }, this); 34 | 35 | if ( vertices[0] === vertices[vertices.length - 1] ) { 36 | vertices.pop( ); 37 | } 38 | 39 | polyColor = polygon.shared.color ? 40 | polygon.shared.color.slice() : 41 | defaultColor.slice(); 42 | opacity = polyColor.pop(); 43 | 44 | // one material per opacity (color not relevant) 45 | // collect different opacity values in opacities 46 | // point to current polygon opacity with materialIdx 47 | var opacityIdx = opacities.indexOf(opacity); 48 | if (opacityIdx > -1) { 49 | materialIdx = opacityIdx; 50 | } else { 51 | opacities.push(opacity); 52 | materialIdx = opacities.length - 1; 53 | } 54 | 55 | // for each different color, create a color object 56 | var colorKey = polyColor.join("_"); 57 | if (!(colorKey in colors)) { 58 | color = new THREE.Color(); 59 | color.setRGB.apply(color, polyColor); 60 | colors[colorKey] = color; 61 | } 62 | 63 | // create a mesh face using color (not opacity~material) 64 | for (var k = 2; k < vertices.length; k++) { 65 | face = new THREE.Face3( vertices[0], vertices[k-1], vertices[k], 66 | new THREE.Vector3().copy(polygon.plane.normal), 67 | colors[colorKey], materialIdx ); 68 | face.materialIdx = materialIdx; 69 | three_geometry.faces.push( face ); 70 | } 71 | }, this); 72 | 73 | // for each opacity in array, create a matching material 74 | // (color are on faces, not materials) 75 | var materials = opacities.map(function(opa) { 76 | // trigger wireframe if opa == 0 77 | var asWireframe = (opa == 0); 78 | // if opacity == 0, this is just a wireframe 79 | var phongMaterial = new THREE.MeshPhongMaterial({ 80 | opacity: opa || 1, 81 | wireframe: asWireframe, 82 | transparent: opa != 1 && opa != 0, 83 | vertexColors: THREE.FaceColors 84 | }); 85 | // (force black wireframe) 86 | // if (asWireframe) { 87 | // phongMaterial.color = 'black'; 88 | // } 89 | if (opa > 0 && opa < 1) { 90 | phongMaterial.depthWrite = false; 91 | } 92 | return phongMaterial; 93 | }); 94 | // now, materials is array of materials matching opacities - color not defined yet 95 | 96 | var colorMesh = new THREE.Mesh(three_geometry, new THREE.MeshFaceMaterial(materials)); 97 | 98 | // pass back bounding sphere radius (or 0 if empty object) 99 | three_geometry.computeBoundingSphere(); 100 | var boundLen = three_geometry.boundingSphere.radius + 101 | three_geometry.boundingSphere.center.length() || 0; 102 | 103 | var phongWireframeMaterial = new THREE.MeshPhongMaterial({ 104 | wireframe: true, 105 | transparent: false, 106 | color:'black' 107 | }); 108 | var wireframe = new THREE.Mesh(three_geometry, phongWireframeMaterial); 109 | 110 | // return result; 111 | return { 112 | colorMesh: colorMesh, 113 | wireframe: wireframe, 114 | boundLen: boundLen 115 | }; 116 | }, 117 | 118 | getGeometryVertex: function (geometry, vertex_position) { 119 | // var i; 120 | // // If Vertex already exists 121 | // // once required this should be done with the BSP info 122 | // for ( i = 0; i < geometry.vertices.length; i++ ) { 123 | // if ( geometry.vertices[i].x === vertex_position.x && 124 | // geometry.vertices[i].y === vertex_position.y && 125 | // geometry.vertices[i].z === vertex_position.z ) { 126 | // return i; 127 | // } 128 | // } 129 | geometry.vertices.push(new THREE.Vector3( vertex_position.x, vertex_position.y, vertex_position.z ) ); 130 | return geometry.vertices.length - 1; 131 | } 132 | }; -------------------------------------------------------------------------------- /i/openjscad/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 47 | 48 | 49 | 114 | 115 | 116 | 127 | 128 |
    129 |
    130 |
    Render to DXF
    131 |
    132 | 133 | 134 | 137 | 138 | 139 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /i/raster/calibration.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/raster/calibration.jpg -------------------------------------------------------------------------------- /i/raster/calibration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 64 | 71 | 78 | 85 | 92 | 99 | 106 | 113 | 120 | 127 | 10% 138 | 149 | 20% 160 | 30% 171 | 0% 186 | 40% 197 | 50% 208 | 60% 219 | 70% 230 | 80% 241 | 90% 252 | 100% 263 | 270 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /i/raster/laserraster.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | 5 | AUTHOR: Peter van der Walt 6 | Addional work by Nathaniel Stenzel and Sven Hecht 7 | 8 | LaserWeb Raster to GCODE Paperscript 9 | Copyright (C) 2015 Peter van der Walt 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | This program is free software: you can redistribute it and/or modify 20 | it under the terms of the GNU Affero General Public License as published by 21 | the Free Software Foundation, either version 3 of the License. 22 | 23 | This program is distributed in the hope that it will be useful, 24 | but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | GNU Affero General Public License for more details. 27 | 28 | You should have received a copy of the GNU Affero General Public License 29 | along with this program. If not, see . 30 | 31 | */ 32 | 33 | // add MAP function to the Numbers function 34 | Number.prototype.map = function (in_min, in_max, out_min, out_max) { 35 | return (this - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 36 | }; 37 | 38 | if (!String.prototype.format) { 39 | String.prototype.format = function() { 40 | var args = arguments; 41 | return this.replace(/{(\d+)}/g, function(match, number) { 42 | return typeof args[number] != 'undefined' 43 | ? args[number] 44 | : match 45 | ; 46 | }); 47 | }; 48 | } 49 | 50 | function Rasterizer(config) { 51 | 52 | this.config = config; 53 | 54 | console.log('[Rasterizer] Width: ' + this.config.imgwidth + ' Height: ' + this.config.imgheight); 55 | 56 | // Init some variables we'll be using in the process 57 | this.path = ''; 58 | this.intensity = ''; 59 | //this.gcodex = ''; 60 | 61 | this.moveCount = 0; // Keep count of Gcode lines so we can optimise, lower = better 62 | this.skip = 0; 63 | this.dir = 1; 64 | //this.lastPosx = -1; 65 | this.megaPixel = 0; 66 | this.x = 0; 67 | //this.endPosx = 0; 68 | this.grayLevel = 0; 69 | //this.gridSize = 1; 70 | this.startTime = 0; 71 | 72 | this.rasterIntervalTimer = null; 73 | 74 | // GCODE Header 75 | var useVariableSpeed = this.config.useVariableSpeed; 76 | this.result = [ 77 | '; GCODE generated by Laserweb', 78 | '; Firmware: {0}', 79 | '; Laser Min: {1}%', 80 | '; Laser Max: {2}%', 81 | '; Black Speed: {3}mm/min', 82 | '; White Speed: {4}mm/min', 83 | '; Laser Spot Size: {5}mm', 84 | '; Laser Feedrate: {6}mm/min \n', 85 | 'G21', 86 | 'G90', 87 | 'G1 F{6}\n' 88 | //'G0 F{7}' 89 | ].join('\n').format( 90 | this.config.firmware, 91 | this.config.minIntensity, 92 | this.config.maxIntensity, 93 | useVariableSpeed ? this.config.blackRate : 'N/A ', 94 | useVariableSpeed ? this.config.whiteRate : 'N/A ', 95 | this.config.spotSize1, 96 | this.config.feedRate, 97 | this.config.rapidRate); 98 | 99 | if (this.config.firmware.indexOf('Lasaur') == 0) { 100 | this.result += 'M80\n'; // Air Assist on 101 | } 102 | 103 | console.log('Variable Speed?: '+useVariableSpeed); 104 | } 105 | 106 | Rasterizer.prototype.figureIntensity = function() { 107 | var intensity = (1 - this.grayLevel) * 100; // Also add out Firmware specific mapping using intensity (which is 0-100) and map it between minIntensity and maxIntensity variables above * firmware specific multiplier (grbl 0-255, smoothie 0-1, etc) 108 | //Constraining Laser power between minIntensity and maxIntensity 109 | //console.log('Constraining'); 110 | 111 | if (parseFloat(intensity) > 0) { 112 | intensity = intensity.map(0, 100, parseInt(this.config.minIntensity, 10), parseInt(this.config.maxIntensity, 10)); 113 | } else { 114 | intensity = 0; 115 | } 116 | 117 | // Firmware Specific Gcode Output 118 | if (this.config.firmware.indexOf('Grbl') == 0) { 119 | intensity = intensity.map(0, 100, 0, 255); 120 | intensity = intensity.toFixed(0); 121 | } else if (this.config.firmware.indexOf('Smooth') == 0) { 122 | intensity = intensity / 100; 123 | //intensity = intensity.toFixed(1); 124 | } else if (this.config.firmware.indexOf('Lasaur') == 0) { 125 | intensity = intensity.map(0, 100, 0, 255); 126 | intensity = intensity.toFixed(0); 127 | } else { 128 | intensity = intensity.map(0, 100, 0, 100); 129 | intensity = intensity.toFixed(0); 130 | } 131 | 132 | return intensity; 133 | }; 134 | 135 | Rasterizer.prototype.figureSpeed = function(passedGrey) { 136 | var calcspeed = passedGrey * 100; 137 | //console.log('Figure speed for brightness'); 138 | 139 | calcspeed = calcspeed.map(0, 100, parseInt(this.config.blackRate, 10), parseInt(this.config.whiteRate, 10)); 140 | calcspeed = calcspeed.toFixed(0); 141 | 142 | return calcspeed; 143 | }; 144 | 145 | Rasterizer.prototype.init = function() { 146 | this.startTime = Date.now(); 147 | 148 | // Initialise 149 | project.clear(); 150 | 151 | // Create a raster item using the image tag 'origImage' 152 | this.raster = new Raster('origImage'); 153 | this.raster.visible = false; 154 | 155 | // Log it as a sanity check 156 | console.log('Constraining Laser power between {0}% and {1}%'.format(this.config.minIntensity, this.config.maxIntensity)); 157 | console.log('Height: {0}px, Width: {1}px'.format(this.config.imgheight, this.config.imgwidth)); 158 | console.log('Spot Size: {0}mm'.format(this.config.spotSize1)); 159 | console.log('Raster Width: {0} Height: {1}'.format(this.raster.width, this.raster.height)); 160 | console.log('G0: {0}mm/min, G1: {1}mm/min'.format(this.config.rapidRate, this.config.feedRate)); 161 | if (this.config.useVariableSpeed == "true") { 162 | console.log('Black speed: {0} Whitespeed: {1}'.format(this.config.blackRate, this.config.whiteRate)); 163 | } 164 | 165 | // As the web is asynchronous, we need to wait for the raster to load before we can perform any operation on its pixels. 166 | this.raster.on('load', this.onRasterLoaded.bind(this)); 167 | }; 168 | 169 | 170 | Rasterizer.prototype.rasterRow = function(y) { 171 | //console.log('[Rasterizer] rasterRow', y); 172 | 173 | // Calculate where to move to to start the first and next rows - G0 Yxx move between lines 174 | var posy = y; 175 | posy = (posy * this.config.spotSize1); 176 | posy = posy.toFixed(1); 177 | 178 | // Offset Y since Gcode runs from bottom left and paper.js runs from top left 179 | var gcodey = (this.config.imgheight * this.config.spotSize1) - posy; 180 | gcodey = gcodey.toFixed(1); 181 | this.result += 'G0 Y{0}\n'.format(gcodey); 182 | 183 | // Clear grayscale values on each line change 184 | var lastGrey = -1; 185 | var lastIntensity = -1; 186 | 187 | // Run the row: 188 | for(var px = 0; px <= this.raster.width ; px++) { 189 | var x; 190 | var posx; 191 | if (this.dir > 0) { // Forward 192 | x = px; 193 | posx = x; 194 | } else { // Backward 195 | x = this.raster.width - px - 1; 196 | posx = x + 1; 197 | } 198 | 199 | // Convert Pixel Position to millimeter position 200 | posx = (posx * this.config.spotSize1); 201 | posx = posx.toFixed(1); 202 | // Keep some stats of how many pixels we've processed 203 | this.megaPixel++; 204 | 205 | // Determine the grayscale of the pixel(x,y) we are looping over 206 | var color = this.raster.getPixel(x, y); 207 | this.grayLevel = color.gray.toFixed(1); // var grayLevel = color.gray.toFixed(2); // two decimal precision is plenty - for testing I will drop it to 1 decimal (10% increments) 208 | 209 | var speed = this.config.feedRate; 210 | if (lastGrey != this.grayLevel) { 211 | intensity = this.figureIntensity(); 212 | speed = this.figureSpeed(lastGrey); 213 | lastGrey = this.grayLevel; 214 | } 215 | 216 | // Can't miss the first pixel (; 217 | //if (px == 0) { this.lastPosx = posx; } 218 | 219 | //if we are on the last dot, force a chance for the last pixel while avoiding forcing a move with the laser off 220 | if ( px == this.raster.width ) { 221 | intensity = -1; 222 | } 223 | 224 | // If we dont match the grayscale, we need to write some gcode... 225 | if (intensity != lastIntensity) { 226 | this.moveCount++; 227 | 228 | //console.log('From: ' + this.lastPosx + ', ' + lastPosy + ' - To: ' + posx + ', ' + posy + ' at ' + lastIntensity + '%'); 229 | if (lastIntensity > 0) { 230 | if (this.config.useVariableSpeed == "true") { 231 | if (this.config.firmware.indexOf('Grbl') == 0) { 232 | this.result += 'M3 S{2}\nG1 X{0} Y{1} F{3} S{2}\nM5\n'.format(posx, gcodey, lastIntensity, speed); 233 | } else { 234 | this.result += 'G1 X{0} Y{1} S{2} F{3}\n'.format(posx, gcodey, lastIntensity, speed); 235 | } 236 | } else { 237 | if (this.config.firmware.indexOf('Grbl') == 0) { 238 | this.result += 'M3 S{2}\nG1 X{0} Y{1} S{2}\nM5\n'.format(posx, gcodey, lastIntensity); 239 | } else { 240 | this.result += 'G1 X{0} Y{1} S{2}\n'.format(posx, gcodey, lastIntensity); 241 | } 242 | } 243 | // This will hopefully get rid of black marks at the end of a line segment 244 | // It seems that some controllers dwell at a spot between gcode moves 245 | // If this does not work, switch to G1 to this.endPosx and then G0 to posx 246 | //this.result += 'G1 S0\n'; 247 | } else { 248 | if ((intensity > 0) || (this.config.optimizelineends == false)) { 249 | this.result += 'G0 X{0} Y{1} S0\n'.format(posx, gcodey); 250 | } 251 | 252 | } 253 | 254 | // Debug: Can be commented, but DON'T DELETE - I use it all the time when i find bug that I am not sure of 255 | // whether the root cause is the raster module or the gcode viewer module - by drawing the paper.js object I can 256 | // do a comparison to see which it is 257 | // Draw canvas (not used for GCODE generation) 258 | //path = new Path.Line({ 259 | // from: [(this.lastPosx * this.gridSize), (posy * this.gridSize)], 260 | // to: [(this.endPosx * this.gridSize), (posy * this.gridSize)], 261 | // strokeColor: 'black' 262 | // }); 263 | //path.strokeColor = 'black'; 264 | //path.opacity = (lastIntensity / 100); 265 | // End of debug drawing 266 | } else { 267 | this.skip++ 268 | } 269 | 270 | // End of write a line of gcode 271 | //this.endPosx = posx; 272 | 273 | // Store values to use in next loop 274 | if (intensity != lastIntensity) { 275 | lastIntensity = intensity; 276 | //this.lastPosx = posx 277 | } 278 | } 279 | 280 | this.dir = -this.dir; // Reverse direction for next row - makes us move in a more efficient zig zag down the image 281 | }; 282 | 283 | 284 | Rasterizer.prototype.rasterInterval = function() { 285 | if (this.currentPosy < this.raster.height) { 286 | 287 | this.rasterRow(this.currentPosy); 288 | 289 | this.currentPosy++; 290 | var progress = Math.round((this.currentPosy / this.raster.height) * 100.0); 291 | $('#rasterProgressShroud .progress-bar').width(progress + "%"); 292 | $('#rasterProgressShroud .progress-bar').text(progress + "%"); 293 | //console.log('[Rasterizer] ', progress, '% done'); 294 | } else { 295 | this.onFinish(); 296 | var rasterSendToLaserButton = document.getElementById("rasterWidgetSendRasterToLaser"); 297 | if (rasterSendToLaserButton.style.display == "none") { // Raster Mode 298 | $('#rasterparams').hide(); 299 | $('#rasterwidget').modal('hide'); 300 | } else { // Calibration Mode 301 | $('#rasterparams').show(); 302 | $('#rasterProgressShroud').hide(); 303 | $('.progress').removeClass('active'); 304 | $('#rasterProgressShroud .progress-bar').width(0); 305 | } 306 | window.clearInterval(this.rasterIntervalTimer); 307 | } 308 | }; 309 | 310 | Rasterizer.prototype.onRasterLoaded = function() { 311 | //console.log('[Rasterizer] onRasterLoaded'); 312 | var rasterSendToLaserButton = document.getElementById("rasterWidgetSendRasterToLaser"); 313 | if (rasterSendToLaserButton.style.display == "none") { // Raster Mode 314 | $('#rasterparams').hide(); 315 | $('#rasterProgressShroud').show(); 316 | $('.progress').removeClass('active'); 317 | $('#rasterProgressShroud .progress-bar').width(0); 318 | } else { // Calibration Mode 319 | $('#rasterparams').hide(); 320 | $('#rasterProgressShroud').show(); 321 | $('.progress').removeClass('active'); 322 | $('#rasterProgressShroud .progress-bar').width(0); 323 | } 324 | 325 | // Iterate through the Pixels asynchronously 326 | this.currentPosy = 0; 327 | this.rasterIntervalTimer = window.setInterval(this.rasterInterval.bind(this), 10); 328 | }; 329 | 330 | Rasterizer.prototype.onFinish = function() { 331 | if (firmware.indexOf('Lasaur') == 0) { 332 | this.result += 'M81\n'; // Air Assist off 333 | } 334 | 335 | // Populate the GCode textarea 336 | document.getElementById('gcodepreview').value = this.result; 337 | console.log('Optimized by number of line: ', this.skip); 338 | 339 | // Some Post-job Stats and Cleanup 340 | console.log('Number of GCode Moves: ', this.moveCount); 341 | var pixeltotal = this.raster.width * this.raster.height; 342 | console.log('Pixels: {0} done, of {1}'.format(this.megaPixel, pixeltotal)); 343 | 344 | console.timeEnd("Process Raster"); 345 | var currentTime = Date.now(); 346 | var elapsed = (currentTime - this.startTime); 347 | $('#console') 348 | .append('

    Raster completed in {0}ms

    '.format(elapsed)) 349 | .scrollTop($("#console")[0].scrollHeight - $("#console").height()); 350 | 351 | if (this.config.completed) { 352 | this.config.completed(); 353 | } 354 | }; 355 | 356 | 357 | this.RasterNow = function(config) { 358 | console.time("Process Raster"); 359 | 360 | var rasterizer = new Rasterizer(config); 361 | rasterizer.init(); 362 | }; 363 | -------------------------------------------------------------------------------- /i/raster/openbuilds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/raster/openbuilds.png -------------------------------------------------------------------------------- /i/raster/random grayscale pic from google images.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/raster/random grayscale pic from google images.jpg -------------------------------------------------------------------------------- /i/raster/tiny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/i/raster/tiny.png -------------------------------------------------------------------------------- /i/svg/js/SVGRenderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.SVGRenderer = function () { 6 | 7 | console.log( 'THREE.SVGRenderer', THREE.REVISION ); 8 | 9 | var _this = this, 10 | _renderData, _elements, _lights, 11 | _projector = new THREE.Projector(), 12 | _svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), 13 | _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, 14 | 15 | _v1, _v2, _v3, _v4, 16 | 17 | _clipBox = new THREE.Box2(), 18 | _elemBox = new THREE.Box2(), 19 | 20 | _color = new THREE.Color(), 21 | _diffuseColor = new THREE.Color(), 22 | _ambientLight = new THREE.Color(), 23 | _directionalLights = new THREE.Color(), 24 | _pointLights = new THREE.Color(), 25 | 26 | _w, // z-buffer to w-buffer 27 | _vector3 = new THREE.Vector3(), // Needed for PointLight 28 | 29 | _svgPathPool = [], _svgLinePool = [], _svgRectPool = [], 30 | _svgNode, _pathCount = 0, _lineCount = 0, _rectCount = 0, 31 | _quality = 1; 32 | 33 | this.domElement = _svg; 34 | 35 | this.autoClear = true; 36 | this.sortObjects = true; 37 | this.sortElements = true; 38 | 39 | this.info = { 40 | 41 | render: { 42 | 43 | vertices: 0, 44 | faces: 0 45 | 46 | } 47 | 48 | } 49 | 50 | this.setQuality = function( quality ) { 51 | 52 | switch(quality) { 53 | 54 | case "high": _quality = 1; break; 55 | case "low": _quality = 0; break; 56 | 57 | } 58 | 59 | }; 60 | 61 | // WebGLRenderer compatibility 62 | 63 | this.supportsVertexTextures = function () {}; 64 | this.setFaceCulling = function () {}; 65 | 66 | this.setClearColor = function ( color, alpha ) { 67 | 68 | // TODO 69 | 70 | }; 71 | 72 | this.setSize = function( width, height ) { 73 | 74 | _svgWidth = width; _svgHeight = height; 75 | _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; 76 | 77 | _svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); 78 | _svg.setAttribute( 'width', _svgWidth ); 79 | _svg.setAttribute( 'height', _svgHeight ); 80 | 81 | _clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf ); 82 | _clipBox.max.set( _svgWidthHalf, _svgHeightHalf ); 83 | 84 | }; 85 | 86 | this.clear = function () { 87 | 88 | _pathCount = 0; 89 | _lineCount = 0; 90 | _rectCount = 0; 91 | 92 | while ( _svg.childNodes.length > 0 ) { 93 | 94 | _svg.removeChild( _svg.childNodes[ 0 ] ); 95 | 96 | } 97 | 98 | }; 99 | 100 | this.render = function ( scene, camera ) { 101 | 102 | if ( camera instanceof THREE.Camera === false ) { 103 | 104 | console.error( 'THREE.SVGRenderer.render: camera is not an instance of THREE.Camera.' ); 105 | return; 106 | 107 | } 108 | 109 | if ( this.autoClear === true ) this.clear(); 110 | 111 | _this.info.render.vertices = 0; 112 | _this.info.render.faces = 0; 113 | 114 | _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); 115 | _elements = _renderData.elements; 116 | _lights = _renderData.lights; 117 | 118 | calculateLights( _lights ); 119 | 120 | for ( var e = 0, el = _elements.length; e < el; e ++ ) { 121 | 122 | var element = _elements[ e ]; 123 | var material = element.material; 124 | 125 | if ( material === undefined || material.visible === false ) continue; 126 | 127 | _elemBox.makeEmpty(); 128 | 129 | if ( element instanceof THREE.RenderableSprite ) { 130 | 131 | _v1 = element; 132 | _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; 133 | 134 | renderSprite( _v1, element, material ); 135 | 136 | } else if ( element instanceof THREE.RenderableLine ) { 137 | 138 | _v1 = element.v1; _v2 = element.v2; 139 | 140 | _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; 141 | _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; 142 | 143 | _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); 144 | 145 | if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { 146 | 147 | renderLine( _v1, _v2, element, material ); 148 | 149 | } 150 | 151 | } else if ( element instanceof THREE.RenderableFace ) { 152 | 153 | _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; 154 | 155 | if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; 156 | if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; 157 | if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; 158 | 159 | _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; 160 | _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; 161 | _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; 162 | 163 | _elemBox.setFromPoints( [ 164 | _v1.positionScreen, 165 | _v2.positionScreen, 166 | _v3.positionScreen 167 | ] ); 168 | 169 | if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { 170 | 171 | renderFace3( _v1, _v2, _v3, element, material ); 172 | 173 | } 174 | 175 | } 176 | 177 | } 178 | 179 | }; 180 | 181 | function calculateLights( lights ) { 182 | 183 | _ambientLight.setRGB( 0, 0, 0 ); 184 | _directionalLights.setRGB( 0, 0, 0 ); 185 | _pointLights.setRGB( 0, 0, 0 ); 186 | 187 | for ( var l = 0, ll = lights.length; l < ll; l++ ) { 188 | 189 | var light = lights[ l ]; 190 | var lightColor = light.color; 191 | 192 | if ( light instanceof THREE.AmbientLight ) { 193 | 194 | _ambientLight.r += lightColor.r; 195 | _ambientLight.g += lightColor.g; 196 | _ambientLight.b += lightColor.b; 197 | 198 | } else if ( light instanceof THREE.DirectionalLight ) { 199 | 200 | _directionalLights.r += lightColor.r; 201 | _directionalLights.g += lightColor.g; 202 | _directionalLights.b += lightColor.b; 203 | 204 | } else if ( light instanceof THREE.PointLight ) { 205 | 206 | _pointLights.r += lightColor.r; 207 | _pointLights.g += lightColor.g; 208 | _pointLights.b += lightColor.b; 209 | 210 | } 211 | 212 | } 213 | 214 | } 215 | 216 | function calculateLight( lights, position, normal, color ) { 217 | 218 | for ( var l = 0, ll = lights.length; l < ll; l ++ ) { 219 | 220 | var light = lights[ l ]; 221 | var lightColor = light.color; 222 | 223 | if ( light instanceof THREE.DirectionalLight ) { 224 | 225 | var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); 226 | 227 | var amount = normal.dot( lightPosition ); 228 | 229 | if ( amount <= 0 ) continue; 230 | 231 | amount *= light.intensity; 232 | 233 | color.r += lightColor.r * amount; 234 | color.g += lightColor.g * amount; 235 | color.b += lightColor.b * amount; 236 | 237 | } else if ( light instanceof THREE.PointLight ) { 238 | 239 | var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); 240 | 241 | var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); 242 | 243 | if ( amount <= 0 ) continue; 244 | 245 | amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); 246 | 247 | if ( amount == 0 ) continue; 248 | 249 | amount *= light.intensity; 250 | 251 | color.r += lightColor.r * amount; 252 | color.g += lightColor.g * amount; 253 | color.b += lightColor.b * amount; 254 | 255 | } 256 | 257 | } 258 | 259 | } 260 | 261 | function renderSprite( v1, element, material ) { 262 | 263 | var scaleX = element.scale.x * _svgWidthHalf; 264 | var scaleY = element.scale.y * _svgHeightHalf; 265 | 266 | _svgNode = getRectNode( _rectCount ++ ); 267 | 268 | _svgNode.setAttribute( 'x', v1.x - ( scaleX * 0.5 ) ); 269 | _svgNode.setAttribute( 'y', v1.y - ( scaleY * 0.5 ) ); 270 | _svgNode.setAttribute( 'width', scaleX ); 271 | _svgNode.setAttribute( 'height', scaleY ); 272 | 273 | if ( material instanceof THREE.SpriteMaterial ) { 274 | 275 | _svgNode.setAttribute( 'style', 'fill: ' + material.color.getStyle() ); 276 | 277 | } 278 | 279 | _svg.appendChild( _svgNode ); 280 | 281 | } 282 | 283 | function renderLine( v1, v2, element, material ) { 284 | 285 | _svgNode = getLineNode( _lineCount ++ ); 286 | 287 | _svgNode.setAttribute( 'x1', v1.positionScreen.x ); 288 | _svgNode.setAttribute( 'y1', v1.positionScreen.y ); 289 | _svgNode.setAttribute( 'x2', v2.positionScreen.x ); 290 | _svgNode.setAttribute( 'y2', v2.positionScreen.y ); 291 | 292 | if ( material instanceof THREE.LineBasicMaterial ) { 293 | 294 | _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin ); 295 | 296 | _svg.appendChild( _svgNode ); 297 | 298 | } 299 | 300 | } 301 | 302 | function renderFace3( v1, v2, v3, element, material ) { 303 | 304 | _this.info.render.vertices += 3; 305 | _this.info.render.faces ++; 306 | 307 | _svgNode = getPathNode( _pathCount ++ ); 308 | _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' ); 309 | 310 | if ( material instanceof THREE.MeshBasicMaterial ) { 311 | 312 | _color.copy( material.color ); 313 | 314 | if ( material.vertexColors === THREE.FaceColors ) { 315 | 316 | _color.multiply( element.color ); 317 | 318 | } 319 | 320 | } else if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { 321 | 322 | _diffuseColor.copy( material.color ); 323 | 324 | if ( material.vertexColors === THREE.FaceColors ) { 325 | 326 | _diffuseColor.multiply( element.color ); 327 | 328 | } 329 | 330 | _color.copy( _ambientLight ); 331 | 332 | calculateLight( _lights, element.centroidModel, element.normalModel, _color ); 333 | 334 | _color.multiply( _diffuseColor ).add( material.emissive ); 335 | 336 | } else if ( material instanceof THREE.MeshDepthMaterial ) { 337 | 338 | _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); 339 | _color.setRGB( _w, _w, _w ); 340 | 341 | } else if ( material instanceof THREE.MeshNormalMaterial ) { 342 | 343 | var normal = element.normalModelView; 344 | 345 | _color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); 346 | 347 | } 348 | 349 | if ( material.wireframe ) { 350 | 351 | _svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); 352 | 353 | } else { 354 | 355 | _svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity ); 356 | 357 | } 358 | 359 | _svg.appendChild( _svgNode ); 360 | 361 | } 362 | 363 | function getLineNode( id ) { 364 | 365 | if ( _svgLinePool[ id ] == null ) { 366 | 367 | _svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' ); 368 | 369 | if ( _quality == 0 ) { 370 | 371 | _svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed 372 | 373 | } 374 | 375 | return _svgLinePool[ id ]; 376 | 377 | } 378 | 379 | return _svgLinePool[ id ]; 380 | 381 | } 382 | 383 | function getPathNode( id ) { 384 | 385 | if ( _svgPathPool[ id ] == null ) { 386 | 387 | _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); 388 | 389 | if ( _quality == 0 ) { 390 | 391 | _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed 392 | 393 | } 394 | 395 | return _svgPathPool[ id ]; 396 | 397 | } 398 | 399 | return _svgPathPool[ id ]; 400 | 401 | } 402 | 403 | function getRectNode( id ) { 404 | 405 | if ( _svgRectPool[ id ] == null ) { 406 | 407 | _svgRectPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'rect' ); 408 | 409 | if ( _quality == 0 ) { 410 | 411 | _svgRectPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed 412 | 413 | } 414 | 415 | return _svgRectPool[ id ]; 416 | 417 | } 418 | 419 | return _svgRectPool[ id ]; 420 | 421 | } 422 | 423 | }; 424 | -------------------------------------------------------------------------------- /i/svg/js/array.find.js: -------------------------------------------------------------------------------- 1 | // Author Jordan Sitkin https://github.com/dustMason/Machine-Art 2 | 3 | // if (Array.prototype.find) return; 4 | 5 | var find = function(predicate) { 6 | var list = Object(this); 7 | var length = list.length >>> 0; // ES.ToUint32; 8 | if (length === 0) return undefined; 9 | if (typeof predicate !== 'function') { 10 | throw new TypeError('Array#find: predicate must be a function'); 11 | } 12 | var thisArg = arguments[1]; 13 | for (var i = 0, value; i < length && i in list; i++) { 14 | value = list[i]; 15 | if (predicate.call(thisArg, value, i, list)) return value; 16 | } 17 | return undefined; 18 | }; 19 | 20 | if (Object.defineProperty) { 21 | try { 22 | Object.defineProperty(Array.prototype, 'find', { 23 | value: find, configurable: true, enumerable: false, writable: true 24 | }); 25 | } catch(e) {} 26 | } 27 | 28 | if (!Array.prototype.find) { 29 | Array.prototype.find = find; 30 | } 31 | -------------------------------------------------------------------------------- /i/svg/js/main.js: -------------------------------------------------------------------------------- 1 | // Author Jordan Sitkin https://github.com/dustMason/Machine-Art 2 | 3 | // not used directly in laserweb, but this is where I ported the code from (peter van der walt) 4 | 5 | console.log('In svg main'); 6 | 7 | var artworks = [ 8 | './art/homer.svg', 9 | './art/phone.svg' 10 | ]; 11 | 12 | var $svgElm = $("#svg"), 13 | $menu = $("#menu-art"), 14 | generateButton = document.getElementById("btn-generate"), 15 | copyButton = document.getElementById("btn-copy"), 16 | gcodeText = document.getElementById("gcode"); 17 | 18 | artworks.forEach(function(artwork) { 19 | $menu.append($("")); 20 | }); 21 | 22 | $menu.change(function(e) { 23 | e.preventDefault(); 24 | var artwork = $(this).val(); 25 | $svgElm.html(""); 26 | $("#work-area").find("canvas").remove(); 27 | 28 | generateButton.onclick = function(e) { 29 | e.preventDefault(); 30 | var XMLS = new XMLSerializer(); 31 | var svgfile = XMLS.serializeToString($svgElm.get(0)); 32 | 33 | gcodeText.innerHTML = svg2gcode(svgfile, { 34 | feedRate: 1500, 35 | seekRate: 10000, 36 | bitWidth: 1, 37 | scale: 0.75 38 | }); 39 | }; 40 | 41 | if (artwork.indexOf("js") > 0) { 42 | $.getScript(artwork); 43 | } else if (artwork.indexOf("svg") > 0) { 44 | $.get(artwork, null, function(data) { 45 | var svgNode = $("svg", data); 46 | var docNode = document.adoptNode(svgNode[0]); 47 | $svgElm.html(docNode); 48 | }, 'xml'); 49 | } 50 | }).change(); 51 | -------------------------------------------------------------------------------- /i/svg/js/svg2gcode.js: -------------------------------------------------------------------------------- 1 | // Author Jordan Sitkin https://github.com/dustMason/Machine-Art 2 | 3 | globalpaths = {}; 4 | var hexvalue = []; 5 | var svgcolors = []; 6 | var svgcolorsoptions = []; 7 | var intensity = 100; 8 | var svglaserpwr = 0; 9 | 10 | // Helper function 11 | Array.prototype.unique = function() 12 | { 13 | var n = {},r=[]; 14 | for(var i = 0; i < this.length; i++) 15 | { 16 | if (!n[this[i]]) 17 | { 18 | n[this[i]] = true; 19 | r.push(this[i]); 20 | } 21 | } 22 | return r; 23 | } 24 | 25 | // Helper function 26 | RGBToHex = function(r,g,b){ 27 | var bin = r << 16 | g << 8 | b; 28 | return (function(h){ 29 | return new Array(7-h.length).join("0")+h 30 | })(bin.toString(16).toUpperCase()) 31 | } 32 | 33 | function pullcolors(svg) { 34 | var hexvalue = []; 35 | var svgcolors = []; 36 | var svgcolorsoptions = []; 37 | svg = svg.replace(/^[\n\r \t]/gm, ''); 38 | var paths = SVGReader.preview(svg, {}).allcolors, 39 | gcode, 40 | path, 41 | idx = paths.length, 42 | minX = Infinity, 43 | maxX = -Infinity, 44 | minY = Infinity, 45 | maxY = -Infinity; 46 | 47 | //var svgcolorsoptions = []; 48 | 49 | for (i = 0; i < paths.length; i++) { 50 | //onsole.log('PATH: '+i+', FILL: '+paths[i].node.fill+', STROKE: '+paths[i].node.stroke+', COLOR: '+paths[i].node.color+', OPACTITY: '+paths[i].node.opacity) 51 | //if (paths[i].node.fill) { svgcolors.push(paths[i].node.fill) }; 52 | if (paths[i].node.stroke) { svgcolors.push(paths[i].node.stroke) }; 53 | //if (paths[i].node.color) { svgcolors.push(paths[i].node.color) }; 54 | } 55 | //svgcolorsoptions = svgcolors.unique(); 56 | //for (c = 0; c < svgcolorsoptions.length; c++) { 57 | // var r = svgcolorsoptions[c][0]; 58 | // var g = svgcolorsoptions[c][1]; 59 | // var b = svgcolorsoptions[c][2]; 60 | // hexvalue.push('#'+RGBToHex(r, g, b)); 61 | //}; 62 | //console.log(svgcolors); 63 | return svgcolors; 64 | }; 65 | 66 | 67 | function svg2gcode(svg, settings) { 68 | // clean off any preceding whitespace 69 | svg = svg.replace(/^[\n\r \t]/gm, ''); 70 | settings = settings || {}; 71 | settings.scale = settings.scale || -1; 72 | settings.cutZ = settings.cutZ || 0; // cut z 73 | settings.safeZ = settings.safeZ || 90; // safe z 74 | settings.feedRate = settings.feedRate || 1400; 75 | settings.seekRate = settings.seekRate || 1100; 76 | settings.bitWidth = settings.bitWidth || 1; // in mm 77 | settings.verticalSlices = settings.verticalSlices || 1; 78 | settings.horizontalSlices = settings.horizontalSlices || 1; 79 | settings.offsetX = settings.offsetX || 0; 80 | settings.offsetY = settings.offsetY || 0; 81 | settings.laserpwr = settings.laserpwr || 100; 82 | settings.gcodePreamble = settings.gcodePreamble || [ 83 | 'G90', 84 | 'G21', 85 | 'G0 Z' + settings.safeZ 86 | 87 | ]; 88 | settings.gcodePostamble = settings.gcodePostamble || []; 89 | 90 | console.log('SVG Laser Power: '+settings.laserpwr+'%'); 91 | var svglaserpwr = parseInt(settings.laserpwr, 10) 92 | 93 | var scale = function(val) { 94 | return val * settings.scale; 95 | }; 96 | 97 | var paths = SVGReader.parse(svg, {}).allcolors, 98 | gcode, 99 | path, 100 | idx = paths.length, 101 | minX = Infinity, 102 | maxX = -Infinity, 103 | minY = Infinity, 104 | maxY = -Infinity; 105 | 106 | //console.log(paths); 107 | globalpaths = paths; 108 | 109 | //for (i = 0; i < paths.length; i++) { 110 | // console.log('PATH: '+i+', FILL: '+paths[i].node.fill+', STROKE: '+paths[i].node.stroke+', COLOR: '+paths[i].node.color+', OPACTITY: '+paths[i].node.opacity) 111 | //} 112 | 113 | while(idx--) { 114 | var subidx = paths[idx].length; 115 | var bounds = { x : Infinity , y : Infinity, x2 : -Infinity, y2: -Infinity, area : 0}; 116 | 117 | // find lower and upper bounds 118 | while(subidx--) { 119 | if (paths[idx][subidx].x < bounds.x) 120 | bounds.x = paths[idx][subidx].x; 121 | if (paths[idx][subidx].x < minX) 122 | minX = paths[idx][subidx].x; 123 | 124 | if (paths[idx][subidx].y < bounds.y) 125 | bounds.y = paths[idx][subidx].y; 126 | if (paths[idx][subidx].y < minY) 127 | minY = paths[idx][subidx].y; 128 | 129 | if (paths[idx][subidx].x > bounds.x2) 130 | bounds.x2 = paths[idx][subidx].x; 131 | if (paths[idx][subidx].x > maxX) 132 | maxX = paths[idx][subidx].x; 133 | 134 | if (paths[idx][subidx].y > bounds.y2) 135 | bounds.y2 = paths[idx][subidx].y; 136 | if (paths[idx][subidx].y > maxY) 137 | maxY = paths[idx][subidx].y; 138 | } 139 | 140 | // calculate area 141 | bounds.area = (1 + bounds.x2 - bounds.x) * (1 + bounds.y2-bounds.y); 142 | paths[idx].bounds = bounds; 143 | } 144 | 145 | if (settings.verticalSlices > 1 || settings.horizontalSlices > 1) { 146 | // break the job up into slices, work in small chunks 147 | var columnWidth = totalWidth / settings.verticalSlices; 148 | var rowHeight = totalHeight / settings.horizontalSlices; 149 | var sortedPaths = []; 150 | // create empty data structure 151 | for (i = 0; i < settings.horizontalSlices; i++) { 152 | sortedPaths[i] = []; 153 | for (j = 0; j < settings.verticalSlices; j++) { 154 | sortedPaths[i][j] = []; 155 | } 156 | } 157 | // populate it with paths 158 | paths.forEach(function(path) { 159 | var rowIndex = Math.floor((path[0].y + (totalHeight/2)) / rowHeight); 160 | var colIndex = Math.floor((path[0].x + (totalWidth/2)) / columnWidth); 161 | // console.log(rowIndex-2, colIndex-2); 162 | if (rowIndex < settings.verticalSlices && colIndex < settings.horizontalSlices) { 163 | sortedPaths[rowIndex][colIndex].push(path); 164 | } else { 165 | console.log("warning: skipped path"); 166 | } 167 | }); 168 | // concatenate all the paths together 169 | paths = sortedPaths.map(function(row, i) { 170 | if ((i % 2) == 1) row.reverse(); 171 | return [].concat.apply([], row); 172 | }); 173 | paths = [].concat.apply([], paths); 174 | } 175 | 176 | gcode = settings.gcodePreamble.slice(0); 177 | 178 | if (firmware.indexOf('Lasaur') == 0) { 179 | gcode.push('M80'); // Air Assist on 180 | }; 181 | 182 | // Firmware Specific Gcode Output 183 | if (firmware.indexOf('Grbl') == 0) { 184 | intensity = svglaserpwr.map(0, 100, 0, 255); 185 | //console.log('Mapping Intensity range for Grbl S0-S255'); 186 | intensity = intensity.toFixed(0); 187 | } else if (firmware.indexOf('Smooth') == 0) { 188 | intensity = svglaserpwr.map(0, 100, 0, 1); 189 | //console.log('Mapping Intensity range for Smoothieware S0-S1'); 190 | intensity = intensity.toFixed(2); 191 | } else if (firmware.indexOf('Lasaur') == 0) { 192 | intensity = svglaserpwr.map(0, 100, 0, 255); 193 | //console.log('Mapping Intensity range for Smoothieware S0-S1'); 194 | intensity = intensity.toFixed(0); 195 | } else { 196 | intensity = svglaserpwr.map(0, 100, 0, 100); 197 | //console.log('Mapping Intensity range for S0-S100'); 198 | intensity = intensity.toFixed(0); 199 | } 200 | 201 | 202 | for (var pathIdx = 0, pathLength = paths.length; pathIdx < pathLength; pathIdx++) { 203 | path = paths[pathIdx]; 204 | 205 | // seek to index 0 206 | gcode.push(['G0', 207 | 'X' + scale(path[0].x + settings.offsetX), 208 | 'Y' + scale(-path[0].y + settings.offsetY), 209 | 'F' + settings.seekRate, 210 | '; Seek to 0' 211 | ].join(' ')); 212 | 213 | 214 | // begin the cut by dropping the tool to the work 215 | gcode.push(['G0', 216 | 'Z' + (settings.cutZ), 217 | 'F' + settings.feedRate 218 | ].join(' ')); 219 | 220 | // keep track of the current path being cut, as we may need to reverse it 221 | // var localPath = []; 222 | for (var segmentIdx=0, segmentLength = path.length; segmentIdx vx ? tx : vx, 250 | y = ty > vy ? ty : vy; 251 | 252 | if (returnNew) { 253 | return new Vec2(x, y); 254 | } else { 255 | this.x = x; 256 | this.y = y; 257 | return this; 258 | } 259 | }, 260 | 261 | // Clamp values into a range. 262 | // If this vector's values are lower than the `low`'s 263 | // values, then raise them. If they are higher than 264 | // `high`'s then lower them. 265 | // 266 | // Passing returnNew as true will cause a new Vec2 to be 267 | // returned. Otherwise, this vector's values will be clamped 268 | clamp : function(low, high, returnNew) { 269 | var ret = this.min(high, true).max(low) 270 | if (returnNew) { 271 | return ret; 272 | } else { 273 | this.x = ret.x; 274 | this.y = ret.y; 275 | return this; 276 | } 277 | }, 278 | 279 | // Ensure this vector contains finite values 280 | isValid : function() { 281 | return isFinite(this.x) && isFinite(this.y); 282 | }, 283 | 284 | // Get the skew vector such that dot(skew_vec, other) == cross(vec, other) 285 | skew : function() { 286 | // Returns a new vector. 287 | return new Vec2(-this.y, this.x) 288 | } 289 | }; 290 | -------------------------------------------------------------------------------- /i/threejs/GridRect.js: -------------------------------------------------------------------------------- 1 | // Code courtesy of https://github.com/mrdoob/three.js/issues/6549 2 | 3 | THREE.GridHelperRect = function ( sizeX, stepX, sizeZ, stepZ ) { 4 | 5 | var x = Math.round( sizeX / stepX ); 6 | var y = Math.round( sizeZ / stepZ ); 7 | 8 | sizeX = x * stepX; 9 | sizeZ = y * stepZ; 10 | 11 | // console.log( "Grid sizeX: " + sizeX * 2 ); 12 | //console.log( "Grid sizeZ: " + sizeZ * 2 ); 13 | 14 | var geometry = new THREE.Geometry(); 15 | var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); 16 | 17 | this.color1 = new THREE.Color( 0x444444 ); 18 | this.color2 = new THREE.Color( 0x888888 ); 19 | 20 | for ( var i = - 1 * sizeX; i <= sizeX; i += stepX ) { 21 | 22 | geometry.vertices.push( 23 | new THREE.Vector3( i, 0, - 1 * sizeZ ), //x Y z 24 | new THREE.Vector3( i, 0, sizeZ ) //x Y z 25 | ); 26 | 27 | var color = i === 0 ? this.color1 : this.color2; 28 | 29 | geometry.colors.push( color, color, color, color ); 30 | 31 | } 32 | 33 | for ( var i = - 1 * sizeZ; i <= sizeZ; i += stepZ ) { 34 | 35 | geometry.vertices.push( 36 | new THREE.Vector3( - 1 * sizeX, 0, i ), //x Y z 37 | new THREE.Vector3( sizeX, 0, i ) //x Y z 38 | ); 39 | 40 | var color = i === 0 ? this.color1 : this.color2; 41 | 42 | geometry.colors.push( color, color, color, color ); 43 | 44 | } 45 | 46 | THREE.Line.call( this, geometry, material, THREE.LinePieces ); 47 | 48 | }; 49 | 50 | THREE.GridHelperRect.prototype = Object.create( THREE.Line.prototype ); 51 | THREE.GridHelperRect.prototype.constructor = THREE.GridHelper; 52 | 53 | THREE.GridHelperRect.prototype.setColors = function( colorCenterLine, colorGrid ) { 54 | 55 | this.color1.set( colorCenterLine ); 56 | this.color2.set( colorGrid ); 57 | 58 | //this.geometry.colorsNeedUpdate = true; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /i/threejs/TrackballControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Eberhard Graether / http://egraether.com/ 3 | * @author Mark Lundin / http://mark-lundin.com 4 | */ 5 | 6 | THREE.TrackballControls = function ( object, domElement ) { 7 | 8 | var _this = this; 9 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; 10 | 11 | this.object = object; 12 | this.domElement = ( domElement !== undefined ) ? domElement : document; 13 | 14 | // API 15 | 16 | this.enabled = true; 17 | 18 | this.screen = { left: 0, top: 0, width: 0, height: 0 }; 19 | 20 | this.rotateSpeed = 1.0; 21 | this.zoomSpeed = 1.2; 22 | this.panSpeed = 0.3; 23 | 24 | this.noRotate = false; 25 | this.noZoom = false; 26 | this.noPan = false; 27 | this.noRoll = false; 28 | 29 | this.staticMoving = false; 30 | this.dynamicDampingFactor = 0.2; 31 | 32 | this.minDistance = 0; 33 | this.maxDistance = Infinity; 34 | 35 | this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; 36 | 37 | // internals 38 | 39 | this.target = new THREE.Vector3(); 40 | 41 | var EPS = 0.000001; 42 | 43 | var lastPosition = new THREE.Vector3(); 44 | 45 | var _state = STATE.NONE, 46 | _prevState = STATE.NONE, 47 | 48 | _eye = new THREE.Vector3(), 49 | 50 | _rotateStart = new THREE.Vector3(), 51 | _rotateEnd = new THREE.Vector3(), 52 | 53 | _zoomStart = new THREE.Vector2(), 54 | _zoomEnd = new THREE.Vector2(), 55 | 56 | _touchZoomDistanceStart = 0, 57 | _touchZoomDistanceEnd = 0, 58 | 59 | _panStart = new THREE.Vector2(), 60 | _panEnd = new THREE.Vector2(); 61 | 62 | // for reset 63 | 64 | this.target0 = this.target.clone(); 65 | this.position0 = this.object.position.clone(); 66 | this.up0 = this.object.up.clone(); 67 | 68 | // events 69 | 70 | var changeEvent = { type: 'change' }; 71 | var startEvent = { type: 'start'}; 72 | var endEvent = { type: 'end'}; 73 | 74 | 75 | // methods 76 | 77 | this.handleResize = function () { 78 | 79 | if ( this.domElement === document ) { 80 | 81 | this.screen.left = 0; 82 | this.screen.top = 0; 83 | this.screen.width = window.innerWidth; 84 | this.screen.height = window.innerHeight; 85 | 86 | } else { 87 | 88 | var box = this.domElement.getBoundingClientRect(); 89 | // adjustments come from similar code in the jquery offset() function 90 | var d = this.domElement.ownerDocument.documentElement; 91 | this.screen.left = box.left + window.pageXOffset - d.clientLeft; 92 | this.screen.top = box.top + window.pageYOffset - d.clientTop; 93 | this.screen.width = box.width; 94 | this.screen.height = box.height; 95 | 96 | } 97 | 98 | }; 99 | 100 | this.handleEvent = function ( event ) { 101 | 102 | if ( typeof this[ event.type ] == 'function' ) { 103 | 104 | this[ event.type ]( event ); 105 | 106 | } 107 | 108 | }; 109 | 110 | var getMouseOnScreen = ( function () { 111 | 112 | var vector = new THREE.Vector2(); 113 | 114 | return function ( pageX, pageY ) { 115 | 116 | vector.set( 117 | ( pageX - _this.screen.left ) / _this.screen.width, 118 | ( pageY - _this.screen.top ) / _this.screen.height 119 | ); 120 | 121 | return vector; 122 | 123 | }; 124 | 125 | }() ); 126 | 127 | var getMouseProjectionOnBall = ( function () { 128 | 129 | var vector = new THREE.Vector3(); 130 | var objectUp = new THREE.Vector3(); 131 | var mouseOnBall = new THREE.Vector3(); 132 | 133 | return function ( pageX, pageY ) { 134 | 135 | mouseOnBall.set( 136 | ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5), 137 | ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5), 138 | 0.0 139 | ); 140 | 141 | var length = mouseOnBall.length(); 142 | 143 | if ( _this.noRoll ) { 144 | 145 | if ( length < Math.SQRT1_2 ) { 146 | 147 | mouseOnBall.z = Math.sqrt( 1.0 - length*length ); 148 | 149 | } else { 150 | 151 | mouseOnBall.z = .5 / length; 152 | 153 | } 154 | 155 | } else if ( length > 1.0 ) { 156 | 157 | mouseOnBall.normalize(); 158 | 159 | } else { 160 | 161 | mouseOnBall.z = Math.sqrt( 1.0 - length * length ); 162 | 163 | } 164 | 165 | _eye.copy( _this.object.position ).sub( _this.target ); 166 | 167 | vector.copy( _this.object.up ).setLength( mouseOnBall.y ) 168 | vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); 169 | vector.add( _eye.setLength( mouseOnBall.z ) ); 170 | 171 | return vector; 172 | 173 | }; 174 | 175 | }() ); 176 | 177 | this.rotateCamera = (function(){ 178 | 179 | var axis = new THREE.Vector3(), 180 | quaternion = new THREE.Quaternion(); 181 | 182 | 183 | return function () { 184 | 185 | var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); 186 | 187 | if ( angle ) { 188 | 189 | axis.crossVectors( _rotateStart, _rotateEnd ).normalize(); 190 | 191 | angle *= _this.rotateSpeed; 192 | 193 | quaternion.setFromAxisAngle( axis, -angle ); 194 | 195 | _eye.applyQuaternion( quaternion ); 196 | _this.object.up.applyQuaternion( quaternion ); 197 | 198 | _rotateEnd.applyQuaternion( quaternion ); 199 | 200 | if ( _this.staticMoving ) { 201 | 202 | _rotateStart.copy( _rotateEnd ); 203 | 204 | } else { 205 | 206 | quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); 207 | _rotateStart.applyQuaternion( quaternion ); 208 | 209 | } 210 | 211 | } 212 | } 213 | 214 | }()); 215 | 216 | this.zoomCamera = function () { 217 | 218 | if ( _state === STATE.TOUCH_ZOOM_PAN ) { 219 | 220 | var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; 221 | _touchZoomDistanceStart = _touchZoomDistanceEnd; 222 | _eye.multiplyScalar( factor ); 223 | 224 | } else { 225 | 226 | var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; 227 | 228 | if ( factor !== 1.0 && factor > 0.0 ) { 229 | 230 | _eye.multiplyScalar( factor ); 231 | 232 | if ( _this.staticMoving ) { 233 | 234 | _zoomStart.copy( _zoomEnd ); 235 | 236 | } else { 237 | 238 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; 239 | 240 | } 241 | 242 | } 243 | 244 | } 245 | 246 | }; 247 | 248 | this.panCamera = (function(){ 249 | 250 | var mouseChange = new THREE.Vector2(), 251 | objectUp = new THREE.Vector3(), 252 | pan = new THREE.Vector3(); 253 | 254 | return function () { 255 | 256 | mouseChange.copy( _panEnd ).sub( _panStart ); 257 | 258 | if ( mouseChange.lengthSq() ) { 259 | 260 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); 261 | 262 | pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); 263 | pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); 264 | 265 | _this.object.position.add( pan ); 266 | _this.target.add( pan ); 267 | 268 | if ( _this.staticMoving ) { 269 | 270 | _panStart.copy( _panEnd ); 271 | 272 | } else { 273 | 274 | _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); 275 | 276 | } 277 | 278 | } 279 | } 280 | 281 | }()); 282 | 283 | this.checkDistances = function () { 284 | 285 | if ( !_this.noZoom || !_this.noPan ) { 286 | 287 | if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) { 288 | 289 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) ); 290 | 291 | } 292 | 293 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { 294 | 295 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) ); 296 | 297 | } 298 | 299 | } 300 | 301 | }; 302 | 303 | this.update = function () { 304 | 305 | _eye.subVectors( _this.object.position, _this.target ); 306 | 307 | if ( !_this.noRotate ) { 308 | 309 | _this.rotateCamera(); 310 | 311 | } 312 | 313 | if ( !_this.noZoom ) { 314 | 315 | _this.zoomCamera(); 316 | 317 | } 318 | 319 | if ( !_this.noPan ) { 320 | 321 | _this.panCamera(); 322 | 323 | } 324 | 325 | _this.object.position.addVectors( _this.target, _eye ); 326 | 327 | _this.checkDistances(); 328 | 329 | _this.object.lookAt( _this.target ); 330 | 331 | if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { 332 | 333 | _this.dispatchEvent( changeEvent ); 334 | 335 | lastPosition.copy( _this.object.position ); 336 | 337 | } 338 | 339 | }; 340 | 341 | this.reset = function () { 342 | 343 | _state = STATE.NONE; 344 | _prevState = STATE.NONE; 345 | 346 | _this.target.copy( _this.target0 ); 347 | _this.object.position.copy( _this.position0 ); 348 | _this.object.up.copy( _this.up0 ); 349 | 350 | _eye.subVectors( _this.object.position, _this.target ); 351 | 352 | _this.object.lookAt( _this.target ); 353 | 354 | _this.dispatchEvent( changeEvent ); 355 | 356 | lastPosition.copy( _this.object.position ); 357 | 358 | }; 359 | 360 | // listeners 361 | 362 | function keydown( event ) { 363 | 364 | if ( _this.enabled === false ) return; 365 | 366 | window.removeEventListener( 'keydown', keydown ); 367 | 368 | _prevState = _state; 369 | 370 | if ( _state !== STATE.NONE ) { 371 | 372 | return; 373 | 374 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) { 375 | 376 | _state = STATE.ROTATE; 377 | 378 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) { 379 | 380 | _state = STATE.ZOOM; 381 | 382 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) { 383 | 384 | _state = STATE.PAN; 385 | 386 | } 387 | 388 | } 389 | 390 | function keyup( event ) { 391 | 392 | if ( _this.enabled === false ) return; 393 | 394 | _state = _prevState; 395 | 396 | window.addEventListener( 'keydown', keydown, false ); 397 | 398 | } 399 | 400 | function mousedown( event ) { 401 | 402 | if ( _this.enabled === false ) return; 403 | 404 | event.preventDefault(); 405 | event.stopPropagation(); 406 | 407 | if ( _state === STATE.NONE ) { 408 | 409 | _state = event.button; 410 | 411 | } 412 | 413 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 414 | 415 | _rotateStart.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); 416 | _rotateEnd.copy( _rotateStart ); 417 | 418 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 419 | 420 | _zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); 421 | _zoomEnd.copy(_zoomStart); 422 | 423 | } else if ( _state === STATE.PAN && !_this.noPan ) { 424 | 425 | _panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) ); 426 | _panEnd.copy(_panStart) 427 | 428 | } 429 | 430 | document.addEventListener( 'mousemove', mousemove, false ); 431 | document.addEventListener( 'mouseup', mouseup, false ); 432 | 433 | _this.dispatchEvent( startEvent ); 434 | 435 | } 436 | 437 | function mousemove( event ) { 438 | 439 | if ( _this.enabled === false ) return; 440 | 441 | event.preventDefault(); 442 | event.stopPropagation(); 443 | 444 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 445 | 446 | _rotateEnd.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); 447 | 448 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 449 | 450 | _zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); 451 | 452 | } else if ( _state === STATE.PAN && !_this.noPan ) { 453 | 454 | _panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) ); 455 | 456 | } 457 | 458 | } 459 | 460 | function mouseup( event ) { 461 | 462 | if ( _this.enabled === false ) return; 463 | 464 | event.preventDefault(); 465 | event.stopPropagation(); 466 | 467 | _state = STATE.NONE; 468 | 469 | document.removeEventListener( 'mousemove', mousemove ); 470 | document.removeEventListener( 'mouseup', mouseup ); 471 | _this.dispatchEvent( endEvent ); 472 | 473 | } 474 | 475 | function mousewheel( event ) { 476 | 477 | if ( _this.enabled === false ) return; 478 | 479 | event.preventDefault(); 480 | event.stopPropagation(); 481 | 482 | var delta = 0; 483 | 484 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 485 | 486 | delta = event.wheelDelta / 40; 487 | 488 | } else if ( event.detail ) { // Firefox 489 | 490 | delta = - event.detail / 3; 491 | 492 | } 493 | 494 | _zoomStart.y += delta * 0.01; 495 | _this.dispatchEvent( startEvent ); 496 | _this.dispatchEvent( endEvent ); 497 | 498 | } 499 | 500 | function touchstart( event ) { 501 | 502 | if ( _this.enabled === false ) return; 503 | 504 | switch ( event.touches.length ) { 505 | 506 | case 1: 507 | _state = STATE.TOUCH_ROTATE; 508 | _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 509 | _rotateEnd.copy( _rotateStart ); 510 | break; 511 | 512 | case 2: 513 | _state = STATE.TOUCH_ZOOM_PAN; 514 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 515 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 516 | _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); 517 | 518 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 519 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 520 | _panStart.copy( getMouseOnScreen( x, y ) ); 521 | _panEnd.copy( _panStart ); 522 | break; 523 | 524 | default: 525 | _state = STATE.NONE; 526 | 527 | } 528 | _this.dispatchEvent( startEvent ); 529 | 530 | 531 | } 532 | 533 | function touchmove( event ) { 534 | 535 | if ( _this.enabled === false ) return; 536 | 537 | event.preventDefault(); 538 | event.stopPropagation(); 539 | 540 | switch ( event.touches.length ) { 541 | 542 | case 1: 543 | _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 544 | break; 545 | 546 | case 2: 547 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 548 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 549 | _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); 550 | 551 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 552 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 553 | _panEnd.copy( getMouseOnScreen( x, y ) ); 554 | break; 555 | 556 | default: 557 | _state = STATE.NONE; 558 | 559 | } 560 | 561 | } 562 | 563 | function touchend( event ) { 564 | 565 | if ( _this.enabled === false ) return; 566 | 567 | switch ( event.touches.length ) { 568 | 569 | case 1: 570 | _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 571 | _rotateStart.copy( _rotateEnd ); 572 | break; 573 | 574 | case 2: 575 | _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; 576 | 577 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 578 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 579 | _panEnd.copy( getMouseOnScreen( x, y ) ); 580 | _panStart.copy( _panEnd ); 581 | break; 582 | 583 | } 584 | 585 | _state = STATE.NONE; 586 | _this.dispatchEvent( endEvent ); 587 | 588 | } 589 | 590 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 591 | 592 | this.domElement.addEventListener( 'mousedown', mousedown, false ); 593 | 594 | this.domElement.addEventListener( 'mousewheel', mousewheel, false ); 595 | this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox 596 | 597 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 598 | this.domElement.addEventListener( 'touchend', touchend, false ); 599 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 600 | 601 | window.addEventListener( 'keydown', keydown, false ); 602 | window.addEventListener( 'keyup', keyup, false ); 603 | 604 | this.handleResize(); 605 | 606 | // force an update at start 607 | this.update(); 608 | 609 | }; 610 | 611 | THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 612 | -------------------------------------------------------------------------------- /machines: -------------------------------------------------------------------------------- 1 | {"machines":[{"name":"Freeburn2","opts":[{"o":"laserxmax","v":"600"},{"o":"laserymax","v":"400"},{"o":"undefined","v":""},{"o":"startgcode","v":"G1G90"},{"o":"endgcode","v":"G28 X0"},{"o":"laseron","v":"M03"},{"o":"laseroff","v":"M05"},{"o":"0svalue","v":"0"},{"o":"100svaluee","v":"255"},{"o":"undefined","v":""}]}]} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laserweb", 3 | "description": "a fully featured web ui for laser cutter/engravers, reprap and other 3d printers", 4 | "contributors": [ 5 | { 6 | "name": "Peter van der Walt", 7 | "email": "peter.plaaswerf@gmail.com" 8 | } 9 | ], 10 | "repository" : 11 | { 12 | "type" : "git", 13 | "url" : "https://github.com/openhardwarecoza/LaserWeb.git" 14 | }, 15 | "licenses": [ 16 | { 17 | "type": "AGPL-3.0", 18 | "url": "http://www.gnu.org/licenses/agpl-3.0.txt" 19 | } 20 | ], 21 | "dependencies": { 22 | "chalk": "1.1.1", 23 | "dotenv": "1.2.0", 24 | "node-static": "0.7.x", 25 | "serialport": "2.0.x", 26 | "socket.io": "1.3.x" 27 | }, 28 | "license": "AGPL-3.0" 29 | } 30 | -------------------------------------------------------------------------------- /presets: -------------------------------------------------------------------------------- 1 | {"machines":[{"name":"Freeburn2","opts":[{"o":"laserxmax","v":"600"},{"o":"laserymax","v":"400"},{"o":"startgcode","v":"\nG91\nG21"},{"o":"endgcode","v":""},{"o":"laseron","v":"M03"},{"o":"laseroff","v":"M5"},{"o":"Laser0","v":"0"},{"o":"laser100","v":"255"},{"o":"button1","v":"M106"}]},{"name":"52566767","opts":[]}]} -------------------------------------------------------------------------------- /rrw_timelapse.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -d "timelapse" ]; then 4 | echo "Please remove the ./timelapse directory and re-run this script" 5 | echo "" 6 | exit 7 | fi 8 | 9 | # exit if user gave no args 10 | if [ $# -eq 0 ];then 11 | echo "RepRapWeb Timelapse Script" 12 | echo "USAGE:" 13 | echo "./rrw_timelapse.sh 192.168.0.50 1 4" 14 | echo "----" 15 | echo "The example above would create a timelapse from the RRW instance" 16 | echo "running at the IP Address 192.168.0.50. Every 1 second a snapshot" 17 | echo "would be taken and 4 snapshots would go into each second of video" 18 | echo "----" 19 | echo "The script will create a directory named timelapse containing all" 20 | echo "of the timelapse images and a timelapse video named timelapse.mp4" 21 | echo "" 22 | exit 23 | fi 24 | 25 | echo "Getting Images" 26 | echo "Once the print is done, hit Ctrl-C then the video will generate" 27 | echo "" 28 | 29 | # this is your raspberry pi ip address 30 | RPI_IP=$1 31 | 32 | # this is the duration between each snapshot, 15 is a good number 33 | TAKE_SNAPSHOT_EVERY_N_SECONDS=$2 34 | 35 | # how many snapshots to put in each second of video, 4 is a good number 36 | SNAPSHOTS_PER_SECOND_OF_VIDEO=$3 37 | 38 | COUNT=1 39 | 40 | # first create a directory for the timelapse 41 | mkdir timelapse 42 | 43 | # cd to that directory 44 | cd timelapse 45 | 46 | # hit ctrl-c when print is done, trap then uses next command to join the images together and make the "timelapse" 47 | trap "ffmpeg -framerate $SNAPSHOTS_PER_SECOND_OF_VIDEO -start_number 1 -i img%d.jpg -c:v libx264 output.mp4 && exit 0" 2 48 | 49 | while [ true ] 50 | do 51 | # get the images from the rpi mjpg streamer 52 | wget http://$RPI_IP:8080/?action=snapshot -q -O img$COUNT.jpg 53 | ((COUNT++)) 54 | sleep $TAKE_SNAPSHOT_EVERY_N_SECONDS 55 | done 56 | -------------------------------------------------------------------------------- /screenshots/50%.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/50%.png -------------------------------------------------------------------------------- /screenshots/Scale Widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/Scale Widget.png -------------------------------------------------------------------------------- /screenshots/Screenshot from 2016-01-22 15-24-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/Screenshot from 2016-01-22 15-24-02.png -------------------------------------------------------------------------------- /screenshots/controlmodal.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/controlmodal.PNG -------------------------------------------------------------------------------- /screenshots/ctrl+shift+i is debug logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/ctrl+shift+i is debug logs.png -------------------------------------------------------------------------------- /screenshots/dxf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/dxf.png -------------------------------------------------------------------------------- /screenshots/dxf1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/dxf1.png -------------------------------------------------------------------------------- /screenshots/hackaday.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/hackaday.jpg -------------------------------------------------------------------------------- /screenshots/job modal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/job modal.png -------------------------------------------------------------------------------- /screenshots/jog widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/jog widget.png -------------------------------------------------------------------------------- /screenshots/laserweb.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/laserweb.PNG -------------------------------------------------------------------------------- /screenshots/parametricgenerator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/parametricgenerator.png -------------------------------------------------------------------------------- /screenshots/port selector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/port selector.png -------------------------------------------------------------------------------- /screenshots/raster calibration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/raster calibration.png -------------------------------------------------------------------------------- /screenshots/raster gcode viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/raster gcode viewer.png -------------------------------------------------------------------------------- /screenshots/raster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/raster.png -------------------------------------------------------------------------------- /screenshots/serial console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/serial console.png -------------------------------------------------------------------------------- /screenshots/svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/svg.png -------------------------------------------------------------------------------- /screenshots/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/terminal.png -------------------------------------------------------------------------------- /screenshots/top toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LaserWeb/deprecated-LaserWeb1/6baa9df3621b51ce57b71a10488f68813e601f2a/screenshots/top toolbar.png -------------------------------------------------------------------------------- /slBaseOpts.js: -------------------------------------------------------------------------------- 1 | // base machine options 2 | var slBaseOpts = {'machines':[ 3 | {section:'Mechanical', options: [ 4 | {opt:'laserxmax',name:'Bed: X Length',value:'600'}, 5 | {opt:'laserymax',name:'Bed: Y Length',value:'400'} 6 | ]}, 7 | {section:'GCODE', options: [ 8 | {opt:'startgcode',name:'Start Gcode',value:'G1\nG90'}, 9 | {opt:'endgcode',name:'Post Gcode',value:'G28 X0'}, 10 | {opt:'laseron',name:'Laser ON command',value:'M03'}, 11 | {opt:'laseroff',name:'Laser OFF command',value:'M05'}, 12 | {opt:'0svalue',name:'0% power: S value',value:'0'}, 13 | {opt:'100svaluee',name:'100% power: S value',value:'255'}, 14 | ]}, 15 | 16 | ]} 17 | 18 | module.exports = slBaseOpts; 19 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git pull 3 | --------------------------------------------------------------------------------