├── .gitignore ├── README.md ├── draw ├── __init__.py ├── test.py └── DrawBot.py ├── photo ├── __init__.py ├── test.py └── PhotoConverter.py ├── cup.png ├── out.jpg ├── banana.jpg ├── dogman.jpg ├── js ├── webcam.swf └── webcam.js ├── outputface_0001.gcode ├── tsquare.jpeg ├── brown-logo1.png ├── templates ├── hello.html ├── static │ ├── contour.jpg │ ├── frame.jpg │ ├── placeholder.png │ ├── index.html │ ├── normalize.css │ └── skeleton.css ├── test.html ├── gcode.html ├── camera.html ├── index.html ├── admin.html └── css │ ├── normalize.css │ └── skeleton.css ├── Village_Square.gif ├── static ├── contour.jpg ├── frame.jpg ├── placeholder.png ├── index.html ├── normalize.css └── skeleton.css ├── outputface_0005.gcode ├── output_0001.gcode ├── outputface_0003.gcode ├── outputface_0004.gcode ├── test_server.py ├── flasktest.py ├── draw_module_test.py ├── googletest.py ├── banana.html ├── gcode.py ├── outputface_0006.gcode ├── outputface_0007.gcode ├── outputface_0008.gcode ├── outputface_0009.gcode ├── outputface_0002.gcode ├── server.csr ├── server.key.org ├── randon_walk.py ├── edge_filter.py ├── spiral.py ├── server.crt ├── photo_server.py ├── contour.py ├── hatch_test.py ├── hatch.py ├── webspeech └── index.html ├── control.py ├── gcode2.py ├── Core_XY_control2 └── Core_XY_control2.ino ├── server.key ├── Core_XY_control3 └── Core_XY_control3.ino ├── Core_XY_control └── Core_XY_control.ino ├── gcode3.py ├── drawing.eps ├── speech.py ├── Core_XY_control4 └── Core_XY_control4.ino ├── draw_server.py ├── Core_XY_control5 └── Core_XY_control5.ino ├── Core_XY_control6 └── Core_XY_control6.ino └── gnu_0001.gcode /.gitignore: -------------------------------------------------------------------------------- 1 | images 2 | *.pyc 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FartMachine10 2 | -------------------------------------------------------------------------------- /draw/__init__.py: -------------------------------------------------------------------------------- 1 | from DrawBot import DrawBot 2 | -------------------------------------------------------------------------------- /photo/__init__.py: -------------------------------------------------------------------------------- 1 | from PhotoConverter import PhotoConverter 2 | 3 | -------------------------------------------------------------------------------- /cup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/cup.png -------------------------------------------------------------------------------- /out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/out.jpg -------------------------------------------------------------------------------- /banana.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/banana.jpg -------------------------------------------------------------------------------- /dogman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/dogman.jpg -------------------------------------------------------------------------------- /js/webcam.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/js/webcam.swf -------------------------------------------------------------------------------- /outputface_0001.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X0 Y0 7 | M18 8 | -------------------------------------------------------------------------------- /tsquare.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/tsquare.jpeg -------------------------------------------------------------------------------- /brown-logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/brown-logo1.png -------------------------------------------------------------------------------- /templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Coooooool 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Village_Square.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/Village_Square.gif -------------------------------------------------------------------------------- /static/contour.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/static/contour.jpg -------------------------------------------------------------------------------- /static/frame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/static/frame.jpg -------------------------------------------------------------------------------- /static/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/static/placeholder.png -------------------------------------------------------------------------------- /templates/static/contour.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/templates/static/contour.jpg -------------------------------------------------------------------------------- /templates/static/frame.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/templates/static/frame.jpg -------------------------------------------------------------------------------- /templates/static/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/philzook58/FartMachine10/master/templates/static/placeholder.png -------------------------------------------------------------------------------- /templates/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Test 4 | 5 | 6 | Here we gooooo {{a}} 7 | 8 | 9 | -------------------------------------------------------------------------------- /templates/gcode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Gcode 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /draw/test.py: -------------------------------------------------------------------------------- 1 | from DrawBot import DrawBot 2 | 3 | bot = DrawBot() 4 | print bot.draw(["G1 X300 400"]) 5 | print "mybutt" 6 | print bot.draw(["G1 X300 400"]) 7 | print "your butt" 8 | print bot.draw(["G1 X300 400"]) 9 | print "his butt" -------------------------------------------------------------------------------- /outputface_0005.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X0.5702 Y0.5702 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X9.693 Y8.8377 12 | G4 P0 13 | M05 S0 14 | G1 F3000 15 | G1 X0 Y0 16 | M18 17 | -------------------------------------------------------------------------------- /output_0001.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X48.381 Y92.7302 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X121.7587 Y146.7556 12 | G1 X18.546 Y169.3333 13 | G1 X-1.6127 Y13.7079 14 | G1 X48.381 Y92.7302 15 | G4 P0 16 | M05 S0 17 | G1 F3000 18 | G1 X0 Y0 19 | M18 20 | -------------------------------------------------------------------------------- /outputface_0003.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X4.8465 Y13.114 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X38.2017 Y13.114 12 | G1 X38.2017 Y40.1973 13 | G1 X5.1316 Y40.1973 14 | G1 X4.8465 Y13.114 15 | G4 P0 16 | M05 S0 17 | G1 F3000 18 | G1 X0 Y0 19 | M18 20 | -------------------------------------------------------------------------------- /outputface_0004.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X0.5702 Y0.2851 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X33.9254 Y0.2851 12 | G1 X33.9254 Y27.3684 13 | G1 X0.8553 Y27.3684 14 | G1 X0.5702 Y0.2851 15 | G4 P0 16 | M05 S0 17 | G1 F3000 18 | G1 X0 Y0 19 | M18 20 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 |

3 | 10 |

11 | -------------------------------------------------------------------------------- /templates/static/index.html: -------------------------------------------------------------------------------- 1 | 2 |

3 | 10 |

11 | -------------------------------------------------------------------------------- /photo/test.py: -------------------------------------------------------------------------------- 1 | from PhotoConverter import PhotoConverter 2 | import time 3 | 4 | mydude = PhotoConverter() 5 | mydude.takePhoto() 6 | mydude.drawContours() 7 | mydude.sortContours() 8 | mydude.drawContours() 9 | mydude.scaleContours(1600,1200) 10 | mydude.drawContours() 11 | #print mydude.convertContourstoGcode() 12 | #mydude.simplifyContours() 13 | #mydude.drawContours() 14 | 15 | -------------------------------------------------------------------------------- /test_server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import render_template 3 | import threading 4 | import time 5 | 6 | app = Flask(__name__) 7 | 8 | a = 1 9 | 10 | @app.route("/") 11 | def hello(): 12 | return render_template('test.html', a=a) 13 | 14 | 15 | 16 | t = threading.Thread(target=app.run) 17 | t.start() 18 | 19 | for i in range(1000): 20 | a+=1 21 | time.sleep(1) 22 | 23 | -------------------------------------------------------------------------------- /flasktest.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, url_for 2 | from flask import render_template 3 | app = Flask(__name__) 4 | 5 | @app.route("/") 6 | def whathello(): 7 | return "Hello World!" 8 | 9 | if __name__ == "__main__": 10 | app.run() 11 | 12 | @app.route('/hello/') 13 | @app.route('/hello/') 14 | def hello(name=None): 15 | return render_template('hello.html', name=name) 16 | 17 | url_for('static', filename='style.css') 18 | -------------------------------------------------------------------------------- /draw_module_test.py: -------------------------------------------------------------------------------- 1 | from draw import DrawBot 2 | import sys 3 | from flask import Flask 4 | from flask import render_template 5 | import threading 6 | import time 7 | 8 | bot = DrawBot("/dev/ttyUSB0") 9 | 10 | 11 | 12 | filename = sys.argv[1] 13 | gcode = ''' 14 | G90 15 | 16 | G21 17 | 18 | G1 F3000 19 | 20 | G1 X0.5702 Y0.5702 21 | 22 | G1 X0 Y0 23 | 24 | G1 X0.5 Y0.5 25 | ''' 26 | 27 | 28 | bot.draw(gcode.split('\n')) 29 | #bot.drawFromFile(filename) 30 | -------------------------------------------------------------------------------- /googletest.py: -------------------------------------------------------------------------------- 1 | #need to install firefox 2 | # selenium, unidecode 3 | 4 | from google import google, images 5 | 6 | options = images.ImageOptions() 7 | #options.image_type = images.ImageType.LINE_DRAWING 8 | #options.format = 9 | results = google.search_images("banana svg", options) 10 | print results 11 | 12 | for result in results: 13 | if result.link[-4:] == '.svg': 14 | images.download([result], path = "images/") 15 | print "downloaded image" 16 | -------------------------------------------------------------------------------- /banana.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gcode.py: -------------------------------------------------------------------------------- 1 | 2 | from parse import compile 3 | mycode = "G01 X1.0000 Y3.0000" 4 | p = compile("G{code} X{x} Y{y}") 5 | command = p.parse(mycode) 6 | print command 7 | 8 | 9 | #Does not do what I want 10 | ''' 11 | mycode = "G04" 12 | command = p.parse(mycode) 13 | print command 14 | ''' 15 | 16 | 17 | 18 | f= open("output_001.gcode") 19 | for line in f: 20 | print line 21 | if line[0:3] == "G01": 22 | command = p.parse(line) 23 | draw_line 24 | elif line[0:3] == "G03": 25 | raisepen() 26 | elif line[0:3] == "G05": 27 | lowerpen() 28 | -------------------------------------------------------------------------------- /outputface_0006.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X0.5702 Y0.5702 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X9.693 Y8.8377 12 | G4 P0 13 | M05 S0 14 | G1 F3000 15 | G1 X0.8553 Y27.3684 16 | G4 P0 17 | M03 S256 18 | G4 P0 19 | G1 F100.000000 20 | G1 X9.4079 Y35.6359 21 | G4 P0 22 | M05 S0 23 | G1 F3000 24 | G1 X33.3552 Y26.7982 25 | G4 P0 26 | M03 S256 27 | G4 P0 28 | G1 F100.000000 29 | G1 X42.478 Y35.3509 30 | G4 P0 31 | M05 S0 32 | G1 F3000 33 | G1 X34.4956 Y0. 34 | G4 P0 35 | M03 S256 36 | G4 P0 37 | G1 F100.000000 38 | G1 X43.0482 Y8.5526 39 | G4 P0 40 | M05 S0 41 | G1 F3000 42 | G1 X0.5702 Y0.2851 43 | G4 P0 44 | M03 S256 45 | G4 P0 46 | G1 F100.000000 47 | G1 X33.9254 Y0.2851 48 | G1 X33.9254 Y27.3684 49 | G1 X0.8553 Y27.3684 50 | G1 X0.5702 Y0.2851 51 | G4 P0 52 | M05 S0 53 | G1 F3000 54 | G1 X0 Y0 55 | M18 56 | -------------------------------------------------------------------------------- /outputface_0007.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X0.5702 Y0.5702 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X9.693 Y8.8377 12 | G4 P0 13 | M05 S0 14 | G1 F3000 15 | G1 X0.8553 Y27.3684 16 | G4 P0 17 | M03 S256 18 | G4 P0 19 | G1 F100.000000 20 | G1 X9.4079 Y35.6359 21 | G4 P0 22 | M05 S0 23 | G1 F3000 24 | G1 X33.3552 Y26.7982 25 | G4 P0 26 | M03 S256 27 | G4 P0 28 | G1 F100.000000 29 | G1 X42.478 Y35.3509 30 | G4 P0 31 | M05 S0 32 | G1 F3000 33 | G1 X34.4956 Y0. 34 | G4 P0 35 | M03 S256 36 | G4 P0 37 | G1 F100.000000 38 | G1 X43.0482 Y8.5526 39 | G4 P0 40 | M05 S0 41 | G1 F3000 42 | G1 X0.5702 Y0.2851 43 | G4 P0 44 | M03 S256 45 | G4 P0 46 | G1 F100.000000 47 | G1 X33.9254 Y0.2851 48 | G1 X33.9254 Y27.3684 49 | G1 X0.8553 Y27.3684 50 | G1 X0.5702 Y0.2851 51 | G4 P0 52 | M05 S0 53 | G1 F3000 54 | G1 X0 Y0 55 | M18 56 | -------------------------------------------------------------------------------- /outputface_0008.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X0.5702 Y0.5702 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X9.693 Y8.8377 12 | G4 P0 13 | M05 S0 14 | G1 F3000 15 | G1 X0.8553 Y27.3684 16 | G4 P0 17 | M03 S256 18 | G4 P0 19 | G1 F100.000000 20 | G1 X9.4079 Y35.6359 21 | G4 P0 22 | M05 S0 23 | G1 F3000 24 | G1 X33.3552 Y26.7982 25 | G4 P0 26 | M03 S256 27 | G4 P0 28 | G1 F100.000000 29 | G1 X42.478 Y35.3509 30 | G4 P0 31 | M05 S0 32 | G1 F3000 33 | G1 X34.4956 Y0. 34 | G4 P0 35 | M03 S256 36 | G4 P0 37 | G1 F100.000000 38 | G1 X43.0482 Y8.5526 39 | G4 P0 40 | M05 S0 41 | G1 F3000 42 | G1 X8.4724 Y35.2706 43 | G4 P0 44 | M03 S256 45 | G4 P0 46 | G1 F100.000000 47 | G1 X41.8276 Y35.2706 48 | G1 X41.8276 Y8.1873 49 | G1 X8.7575 Y8.1873 50 | G1 X8.4724 Y35.2706 51 | G4 P0 52 | M05 S0 53 | G1 F3000 54 | G1 X0 Y0 55 | M18 56 | -------------------------------------------------------------------------------- /templates/camera.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 | 28 | 29 | Take Snapshot 30 | -------------------------------------------------------------------------------- /outputface_0009.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X31.3596 Y31.6447 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G1 X31.6447 Y61.2938 12 | G4 P0 13 | M05 S0 14 | G1 F3000 15 | G1 X23.0921 Y53.5965 16 | G4 P0 17 | M03 S256 18 | G4 P0 19 | G1 F100.000000 20 | G1 X31.6447 Y61.5789 21 | G1 X66.4254 Y61.2938 22 | G4 P0 23 | M05 S0 24 | G1 F3000 25 | G1 X58.4429 Y53.3114 26 | G4 P0 27 | M03 S256 28 | G4 P0 29 | G1 F100.000000 30 | G1 X66.9956 Y60.7236 31 | G1 X66.7105 Y31.3596 32 | G1 X58.4429 Y23.6623 33 | G1 X58.1578 Y24.8026 34 | G4 P0 35 | M05 S0 36 | G1 F3000 37 | G1 X23.0921 Y24.2324 38 | G4 P0 39 | M03 S256 40 | G4 P0 41 | G1 F100.000000 42 | G1 X32.2149 Y31.3596 43 | G1 X66.4254 Y31.3596 44 | G4 P0 45 | M05 S0 46 | G1 F3000 47 | G1 X23.0921 Y23.9474 48 | G4 P0 49 | M03 S256 50 | G4 P0 51 | G1 F100.000000 52 | G1 X58.4429 Y23.9474 53 | G1 X58.4429 Y53.3114 54 | G1 X22.807 Y53.3114 55 | G1 X23.0921 Y23.9474 56 | G4 P0 57 | M05 S0 58 | G1 F3000 59 | G1 X0 Y0 60 | M18 61 | -------------------------------------------------------------------------------- /outputface_0002.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X27.9383 Y38.9866 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G3 X15.0865 Y33.6632 I0. J-18.1752 12 | G3 X9.7631 Y20.8114 I12.8518 J-12.8518 13 | G3 X15.0865 Y7.9596 I18.1752 J-0. 14 | G3 X27.9383 Y2.6362 I12.8518 J12.8518 15 | G3 X40.7905 Y7.9595 I0. J18.1762 16 | G3 X46.1141 Y20.8114 I-12.8512 J12.8519 17 | G3 X40.7905 Y33.6633 I-18.1748 J0. 18 | G3 X27.9383 Y38.9866 I-12.8522 J-12.8528 19 | G1 X27.9383 Y38.9866 20 | G4 P0 21 | M05 S0 22 | G1 F3000 23 | G1 X27.9383 Y37.9867 24 | G4 P0 25 | M03 S256 26 | G4 P0 27 | G1 F100.000000 28 | G2 X40.0835 Y32.9562 I-0. J-17.1763 29 | G2 X45.1142 Y20.8114 I-12.1442 J-12.1448 30 | G2 X40.0835 Y8.6665 I-17.1749 J-0. 31 | G2 X27.9383 Y3.6361 I-12.1451 J12.1458 32 | G2 X15.7936 Y8.6666 I-0. J17.1753 33 | G2 X10.763 Y20.8114 I12.1448 J12.1448 34 | G2 X15.7936 Y32.9562 I17.1753 J0. 35 | G2 X27.9383 Y37.9867 I12.1448 J-12.1448 36 | G1 X27.9383 Y37.9867 37 | G4 P0 38 | M05 S0 39 | G1 F3000 40 | G1 X0 Y0 41 | M18 42 | -------------------------------------------------------------------------------- /server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIClzCCAX8CAQAwUjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlJJMRMwEQYDVQQH 3 | DApQcm92aWRlbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQw 4 | ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSLNTDP6NzVFCzxdnB9Axg 5 | GLr6Uxl0muhSs0CMHU4bKKfrk+8JJpu+LJAEh7fguf+8WiLvX59GQxrxbT4khV/u 6 | dG4ZkQtyvP9DdcEmM/86jLqNPDj+yXohGp72jLQB0vvLrqUcdZy0MeiUbAVW6ywx 7 | uQca3RqjpsrSyNf0HN+ShVjONGfSW2R+IYGUXR6pAI9NiDliaOTPayPfETbWmPOB 8 | EY2zlG6xy71O2eNSoCKj6Dz0HEnucRU1Mo0wKrpOSmHZ/qQL+/LJQlo9jd7y0wpI 9 | Awmioh7ZkAoPrs7LCxCFDnSygZZLqQsbluVy333VWI+GIzwPv7f/Jv2Ad6n51qLb 10 | AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAqDnOBCpqXfhcstzvshW9nKofe88H 11 | lHjwlelcjavcIuiKfoFNhFzmxj4xJi4Hw+6jIoLTYMaFXHRcqgedI8EQclahFW0T 12 | n+r46il94b9Z6ncAk19vDjoB4tCGycAhUm2vnEpB0JCgOnoJ9Sa9wT0/KClN52bP 13 | xyPAF6CZ6nKb36T09swMQFIEPJZSNWs+CNYiatg63+LMyNuFUWH8pZ5mRM05Glhg 14 | sk0mqaVPLL4k52Ls1tep4XGXJQaRIkGVDEy+08a5DLb2W8gs3mMHvYZn+bB7Vgfu 15 | EEEeBIymXtULtLPBlcb1QFakq/Il17RLytv46uoexORQnMNPneQl7tnK2Q== 16 | -----END CERTIFICATE REQUEST----- 17 | -------------------------------------------------------------------------------- /server.key.org: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,CD8DEB8891BEC75F 4 | 5 | xXDF8x8fEbPmId90CYveXlbqTf4Q4mkCJl+2SnduVvT/nK2ETts6ZJVxU6vvaMoh 6 | BwlbHlwf1+LLP0lfb6LmLo+7OVLACO2X8lsE7wun7PbxsavflnIsXhuHTXQpQwH8 7 | tFPWUteP2Q0fJTa5JWEmNu5i1vUizru7cdSg8W92VbQm5RUnUgaqEq/59jWrbOW+ 8 | 6kzhLIwrV/ShWQs2LKACH2TvH3+Py7uTAXdSvLQHMcsssEWe7XGxEUqADv1Vc55I 9 | pw7qeV7wZmjVlmUtaPe1vQfdRmbxtk059L/WEHBkWrcwIlrAzt8DuAvgK4lKJGTS 10 | Mi+QRt3olR50ScKuC4OtwI1KjVgbcVA7JTrvVZ323/6UMIjY79Rak3gCbTtUg1or 11 | pxtMsDaEDHMj7gGh6vyzCGxa5YX2YpVHFOyXDndR34FF5UQhBnBdBHuuGtIQk8AH 12 | 3a8o+ZWS3vT4ezCJX5QAwr/81D/O+VnHj6v9wP4nTujLoO18Rh4LNiGtbN+zR51y 13 | u92IccyzKXT1CGUYKdtfGyJ4e86ihL8LZGWK93u/p5M6hZ+03ttnLIoltd1pKoK3 14 | c01ZXsU3nEXvQV34G8mlUNmbUd72MWMCWZbRL2PqwRjQJ7Fvtyqq3N62syuXzh2g 15 | mlfCwk8Zlv+ViA2PuSYBcJMZW9hnUYtwhUSoGY1lvzYJu2OQAQAepp8+MLpKZvNj 16 | t5We6bOk9U4BhgpRXLoF5lkHJxoTZ5oBhQtjEyc2Q5cgM/koOxc+w7yzvCCqnkcT 17 | 4wqdJKGldIxHXiJEtVDJHC5rTp/wgt7kamgr3baauS62rfArsuaoYQ== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /randon_walk.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | 5 | grayimg = cv2.imread('./dogman.jpg',0) 6 | maxval = np.max(grayimg) 7 | img = np.zeros((512,512,3), np.uint8) 8 | 9 | 10 | def inbounds(pos): 11 | if pos[0] < 0: 12 | return False 13 | if pos[1] < 0: 14 | return False 15 | if pos[0] > grayimg.shape[0]: 16 | return False 17 | if pos[1] > grayimg.shape[1]: 18 | return False 19 | return True 20 | 21 | 22 | def move_to(pos): 23 | #cv2.line(img,(int(x[1]),int(x[0])),(int(pos[1]),int(pos[0])),(255,0,0),1) 24 | cv2.circle(img,(int(pos[1]),int(pos[0])),1,(255,0,0),1) 25 | 26 | def accept(pos): 27 | return inbounds(pos) and np.random.random()*maxval > grayimg[ int(pos[0]), int(pos[1])] 28 | x = np.zeros(2) 29 | 30 | 31 | for i in range(100000): 32 | angle = np.random.random()*2*np.pi 33 | r = 50 34 | pos = x + np.array([np.cos(angle), np.sin(angle)]) * r 35 | 36 | if accept(pos): 37 | move_to(pos) 38 | x = pos 39 | 40 | cv2.imshow('lineman',img) 41 | cv2.imshow('dogman',grayimg) 42 | cv2.waitKey(0) 43 | -------------------------------------------------------------------------------- /edge_filter.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import random 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import time 7 | 8 | cam = cv2.VideoCapture(0) 9 | s, im = cam.read() # captures image 10 | s, im = cam.read() 11 | time.sleep(.2) 12 | s, im = cam.read() 13 | gray_im = cv2.cvtColor( im, cv2.COLOR_BGR2GRAY ); 14 | 15 | edges = cv2.Canny(gray_im,100,200) 16 | 17 | height, width = edges.shape 18 | contours, hierarchy = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) 19 | 20 | 21 | target = open('out.gcode', 'w') 22 | 23 | 24 | cv2.drawContours(im, contours, -1, (0,255,0), 3) 25 | cv2.imshow("Test Picture", im) 26 | cv2.waitKey() 27 | 28 | pen_down = False 29 | 30 | 31 | for contour in contours: 32 | for point in contour: 33 | target.write("G1 X" + str(point[0][0] * 1280 / width) + " Y" + str(point[0][1] * 720 / height) + "\n") 34 | if not pen_down: 35 | target.write("M03\n") 36 | pen_down = True 37 | target.write("M05\n") 38 | pen_down = False 39 | 40 | ''' 41 | for contour in contours: 42 | for point in contour: 43 | print point[0] 44 | print 45 | ''' 46 | -------------------------------------------------------------------------------- /spiral.py: -------------------------------------------------------------------------------- 1 | 2 | from parse import compile 3 | p = compile("G{code} X{x} Y{y}") 4 | import sys 5 | import serial 6 | port = '/dev/cu.wch'#'/dev/ttyUSB0'#'tty.wch'#'cu.wch ch341 USB=>RS232 1410' 7 | ser = serial.Serial(port,38400) # open serial port 8 | print(ser.name) # check which port was really used 9 | import numpy as np 10 | import time 11 | 12 | 13 | def raisepen(): 14 | ser.write('30c') 15 | #time.sleep(0.1) 16 | def lowerpen(): 17 | ser.write('20c') 18 | #time.sleep(0.1) 19 | 20 | 21 | def convertToMotor(x): 22 | return np.array([x[0]+x[1],x[0]-x[1]])/np.sqrt(2) 23 | 24 | 25 | def move_to(x): 26 | x = convertToMotor(x) 27 | ser.write(str(int(x[0])) + b'a') 28 | ser.write(str(int(x[1])) + b'b') 29 | ser.write(b'x') 30 | print ser.readline() 31 | #time.sleep(.1) 32 | print ser.readline() 33 | 34 | radius = 400 35 | N = 14 36 | maxangle = 2 * np.pi * N 37 | deltaangle = 2 * np.pi / 6 38 | angles = np.arange(0,maxangle,deltaangle) 39 | r = angles * radius / maxangle 40 | x = r * np.cos(angles) 41 | y = r * np.sin(angles) 42 | 43 | lowerpen() 44 | for i in range(len(x)): 45 | move_to(np.array([x[i],y[i]])) 46 | 47 | ser.close() 48 | -------------------------------------------------------------------------------- /server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDIDCCAggCCQD4tbLQPwroeDANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCUkkxEzARBgNVBAcMClByb3ZpZGVuY2UxITAfBgNVBAoMGElu 4 | dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xNjA1MTEwMDE1MjRaFw0xNzA1MTEw 5 | MDE1MjRaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJSSTETMBEGA1UEBwwKUHJv 6 | dmlkZW5jZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjAN 7 | BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0izUwz+jc1RQs8XZwfQMYBi6+lMZ 8 | dJroUrNAjB1OGyin65PvCSabviyQBIe34Ln/vFoi71+fRkMa8W0+JIVf7nRuGZEL 9 | crz/Q3XBJjP/Ooy6jTw4/sl6IRqe9oy0AdL7y66lHHWctDHolGwFVussMbkHGt0a 10 | o6bK0sjX9BzfkoVYzjRn0ltkfiGBlF0eqQCPTYg5Ymjkz2sj3xE21pjzgRGNs5Ru 11 | scu9TtnjUqAio+g89BxJ7nEVNTKNMCq6Tkph2f6kC/vyyUJaPY3e8tMKSAMJoqIe 12 | 2ZAKD67OywsQhQ50soGWS6kLG5blct991ViPhiM8D7+3/yb9gHep+dai2wIDAQAB 13 | MA0GCSqGSIb3DQEBCwUAA4IBAQCWTqqzfyR2PoIPEFuVwYx+2ws2FAwN+mkJur8o 14 | QLq0MsEglvHD6BJIoXt5lw0IN0hM+QRMA82cr2+skhFL3n8+TSMVxwHDoWmEV3iw 15 | NV1Rt/3caMuibT+tgVq1tk1919MQBKUZQcqK3E8M8A8TYYzHHCLxtCSMW5ELVJ9E 16 | BAA3WqJC/ZevsZpCdGaexlEy86E8kdR0+FhxAIFcLE1fGfCm4kHs9LaaE3WyeU4k 17 | pm+TpuehOxyYFH3CstXtQ5Y2/aVZ9IvVzRfYPtJJaBuo1MBu/kUutuQSbzCVwTzl 18 | GWmFOxySkDUqQlSyfmGst8Eu5LA5rMDr9LL/vFjxMWnGYAbm 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /photo_server.py: -------------------------------------------------------------------------------- 1 | from draw import DrawBot 2 | import sys 3 | from flask import Flask 4 | from flask import request 5 | from flask import render_template 6 | from flask import send_from_directory 7 | import threading 8 | import time 9 | import base64 10 | 11 | app = Flask(__name__) 12 | app.debug = True 13 | 14 | #bot = DrawBot("/dev/ttyUSB0") 15 | 16 | from OpenSSL import SSL 17 | context = SSL.Context(SSL.SSLv23_METHOD) 18 | context.use_privatekey_file('server.key') 19 | context.use_certificate_file('server.crt') 20 | 21 | @app.route("/") 22 | def hello(): 23 | return render_template("camera.html") 24 | 25 | @app.route('/js/') 26 | def send_js(path): 27 | print path 28 | return send_from_directory('js', path) 29 | 30 | @app.route("/draw") 31 | def draw(): 32 | bot.draw(request.args.get('gcode','').split('\r\n')) 33 | return '

done

' 34 | 35 | @app.route("/image", methods=['POST']) 36 | def image(): 37 | #newjpgtxt = request.get_json().get('data','').split(",") 38 | newjpgtxt = request.form['data'].split(",")[1] 39 | g = open("out.jpg", "w") 40 | g.write(base64.decodestring(newjpgtxt)) 41 | g.close() 42 | return '

done

' 43 | 44 | app.run(host='0.0.0.0', ssl_context=context) 45 | -------------------------------------------------------------------------------- /contour.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | def show_contour(filename): 5 | im = cv2.imread(filename) 6 | imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) 7 | 8 | 9 | #Tighten up threshold to avoid watermarks 10 | ret,thresh = cv2.threshold(imgray,200,255,0) 11 | #thresh = cv2.Canny(imgray,100,200) 12 | contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 13 | print hierarchy 14 | #**[Next, Previous, First_Child, Parent]** 15 | 16 | empty = np.zeros(im.shape,np.uint8) 17 | empty2 = np.zeros(im.shape,np.uint8) 18 | #To remove border. This is bad if image touches boundaries. Buffer image? 19 | ymax = 1000. 20 | xmax = 1000. 21 | scale = min(ymax/im.shape[0], xmax/im.shape[1]) 22 | 23 | 24 | for i in range(len(contours)): 25 | if hierarchy[0,i,3] != -1: 26 | cv2.drawContours(empty,contours,i,(0,255,0),1) 27 | #cv2.imshow('res',empty) 28 | cv2.drawContours(empty2,contours,-1,(0,255,0),1) 29 | #cv2.imshow('butt',empty2) 30 | #if cv2.waitKey(0) & 0xff == 27: 31 | # cv2.destroyAllWindows() 32 | writename = '/'.join(filename.split('/')[:-1]) + '/' + 'contour.jpg' 33 | cv2.imwrite(writename,empty) 34 | #show_contour('images/bird_bird/bird-skeleton-big.jpg') 35 | -------------------------------------------------------------------------------- /hatch_test.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import numpy as np 5 | import cv2 6 | 7 | 8 | pendown = False 9 | img = np.zeros((512,512,3), np.uint8) 10 | x = np.array([0,0]) 11 | def raisepen(): 12 | global pendown 13 | pendown = False 14 | def lowerpen(): 15 | global pendown 16 | pendown = True 17 | def move_to(xnew): 18 | global img 19 | global x 20 | xnew = np.rint(xnew).astype(int) 21 | if pendown: 22 | cv2.line(img,(x[1],x[0]),(xnew[1],xnew[0]),(255,0,0),5) 23 | x = xnew 24 | 25 | grayimg = cv2.imread('./dogman.jpg',0) 26 | 27 | #cv2.imshow('dogman',grayimg) 28 | #cv2.waitKey(0) 29 | grayimg = cv2.pyrDown(grayimg) 30 | grayimg = cv2.pyrDown(grayimg) 31 | grayimg = cv2.pyrDown(grayimg) 32 | grayimg = cv2.pyrDown(grayimg) 33 | print grayimg.shape 34 | 35 | scale = 20. 36 | maxval = float(np.max(grayimg)) 37 | #cv2.line(img,(0,0),(511,511),(255,0,0),5) 38 | print maxval 39 | print grayimg 40 | for i in range(grayimg.shape[0]): 41 | move_to( np.array([i , 0] ) * scale) 42 | for j in range(grayimg.shape[1]): 43 | raisepen() 44 | move_to( np.array([i , j + grayimg[i,j]/maxval] ) * scale) 45 | lowerpen() 46 | move_to( np.array([i, j+1]) * scale ) 47 | raisepen() 48 | 49 | cv2.imshow('lineman',img) 50 | cv2.imshow('dogman',grayimg) 51 | cv2.waitKey(0) 52 | -------------------------------------------------------------------------------- /hatch.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import numpy as np 5 | import cv2 6 | 7 | 8 | import sys 9 | import serial 10 | port = '/dev/cu.wch ch341 USB=>RS232 1410'#'tty.wch'#'cu.wch ch341 USB=>RS232 1410' 11 | ser = serial.Serial(port,38400) # open serial port 12 | print(ser.name) # check which port was really used 13 | import numpy as np 14 | import time 15 | 16 | 17 | def raisepen(): 18 | ser.write('55c') 19 | time.sleep(0.1) 20 | def lowerpen(): 21 | ser.write('40c') 22 | time.sleep(0.1) 23 | 24 | def convertToMotor(x): 25 | return np.array([x[0]+x[1],x[0]-x[1]])*5/np.sqrt(2) 26 | 27 | def move_to(x): 28 | x = convertToMotor(x) 29 | ser.write(str(int(x[0])) + b'a') 30 | ser.write(str(int(x[1])) + b'b') 31 | ser.write(b'x') 32 | print ser.readline() 33 | time.sleep(.1) 34 | 35 | print ser.readline() 36 | grayimg = cv2.imread('./dogman.jpg',0) 37 | 38 | #cv2.imshow('dogman',grayimg) 39 | #cv2.waitKey(0) 40 | grayimg = cv2.pyrDown(grayimg) 41 | grayimg = cv2.pyrDown(grayimg) 42 | grayimg = cv2.pyrDown(grayimg) 43 | grayimg = cv2.pyrDown(grayimg) 44 | print grayimg.shape 45 | 46 | scale = 6. 47 | maxval = np.max(grayimg) 48 | 49 | for i in range(grayimg.shape[0]): 50 | for j in range(grayimg.shape[1]): 51 | lowerpen() 52 | move_to( np.array([i , j + grayimg[i,j]/maxval] ) * scale) 53 | raisepen() 54 | move_to( np.array([i, j+1]) * scale ) 55 | -------------------------------------------------------------------------------- /webspeech/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /control.py: -------------------------------------------------------------------------------- 1 | import serial 2 | port = '/dev/cu.wch ch341 USB=>RS232 1410'#'tty.wch'#'cu.wch ch341 USB=>RS232 1410' 3 | ser = serial.Serial(port,38400) # open serial port 4 | print(ser.name) # check which port was really used 5 | import numpy as np 6 | import time 7 | 8 | 9 | x = np.zeros(2) 10 | 11 | N=1000 12 | 13 | globalsteps = np.array([0,0]) 14 | 15 | def raisepen(): 16 | pass 17 | def lowerpen(): 18 | pass 19 | 20 | 21 | def move(steps): 22 | #print steps 23 | global globalsteps 24 | ser.write(str(int(steps[0])) + b'a') 25 | ser.write(str(int(steps[1])) + b'b') 26 | globalsteps = globalsteps + steps 27 | #print globalsteps 28 | 29 | 30 | 31 | def convertToMotor(x): 32 | return np.array([x[0]+x[1],x[0]-x[1]]) 33 | 34 | def draw_line(x,xend): 35 | x = convertToMotor(x) 36 | xend = convertToMotor(xend) 37 | deltax = (xend - x)/N 38 | xint = np.rint(x) 39 | for i in range(N): 40 | x = x + deltax 41 | #print x 42 | if np.any(np.rint(x) != xint): 43 | steps = np.rint(x) - xint 44 | move(steps) 45 | xint = np.rint(x) 46 | time.sleep(0.03) 47 | 48 | 49 | pos = np.array([[-10.,-10.], [10.,-10.],[-10.,-10.], [-10.,10.]])*3 50 | i = 0 51 | while(1): 52 | 53 | draw_line(x,pos[i,:]) 54 | x = pos[i,:] 55 | print x 56 | time.sleep(.5) 57 | print("YO") 58 | #print(ser.read(100)) 59 | i+= 1 60 | i = i%4 61 | ser.close() # close port 62 | -------------------------------------------------------------------------------- /gcode2.py: -------------------------------------------------------------------------------- 1 | 2 | from parse import compile 3 | p = compile("G{code} X{x} Y{y}") 4 | import sys 5 | import serial 6 | port = '/dev/cu.wch ch341 USB=>RS232 1410'#'tty.wch'#'cu.wch ch341 USB=>RS232 1410' 7 | ser = serial.Serial(port,38400) # open serial port 8 | print(ser.name) # check which port was really used 9 | import numpy as np 10 | import time 11 | 12 | 13 | def raisepen(): 14 | ser.write('70c') 15 | time.sleep(0.1) 16 | def lowerpen(): 17 | ser.write('95c') 18 | time.sleep(0.1) 19 | 20 | 21 | 22 | 23 | def move(steps): 24 | ser.write(str(int(steps[0])) + b'a') 25 | ser.write(str(int(steps[1])) + b'b') 26 | 27 | def convertToMotor(x): 28 | return np.array([x[0]+x[1],x[0]-x[1]]) 29 | 30 | def draw_line(x,xend): 31 | x = convertToMotor(x) 32 | xend = convertToMotor(xend) 33 | deltax = (xend - x)/N 34 | xint = np.rint(x) 35 | for i in range(N): 36 | x = x + deltax 37 | #print x 38 | if np.any(np.rint(x) != xint): 39 | steps = np.rint(x) - xint 40 | move(steps) 41 | xint = np.rint(x) 42 | time.sleep(0.03) 43 | 44 | x = np.zeros(2) 45 | N=1000 46 | raisepen() 47 | filename = sys.argv[1] 48 | f= open(filename) 49 | for line in f: 50 | print line 51 | if line[0:2] == "G1": 52 | command = p.parse(line) 53 | if command != None: 54 | command = command.named 55 | xnew = np.array([float(command['x']),float(command['y'])]) 56 | draw_line(x, xnew) 57 | x = xnew 58 | elif line[0:3] == "M05": 59 | raisepen() 60 | elif line[0:3] == "M03": 61 | lowerpen() 62 | 63 | ser.close() # close port 64 | -------------------------------------------------------------------------------- /Core_XY_control2/Core_XY_control2.ino: -------------------------------------------------------------------------------- 1 | 2 | int v = 0; 3 | int sign = 1; 4 | 5 | #include 6 | #include 7 | // write a,b for steppers 8 | Stepper motor1 = Stepper(200, 4,5, 6,7); 9 | Stepper motor2 = Stepper(200, 10,11,12,13); 10 | Servo myservo; 11 | 12 | void setup() { 13 | 14 | Serial.begin(38400); 15 | Serial.println("Stepper test!"); 16 | motor1.setSpeed(60); // rpm 17 | motor2.setSpeed(60); 18 | myservo.attach(3); 19 | } 20 | 21 | void loop() { 22 | // put your main code here, to run repeatedly: 23 | if ( Serial.available()) { 24 | char ch = Serial.read(); 25 | 26 | switch(ch) { 27 | case '0'...'9': 28 | //Pretty goddamn clever right here. Not mine. 29 | v = v * 10 + ch - '0'; 30 | break; 31 | case '-': 32 | //Pretty goddamn clever right here. This time ours. 33 | sign *= -1; 34 | //Serial.println("NEGATE!"); 35 | break; 36 | case 'a': 37 | move('a', v, sign); 38 | resetV(); 39 | break; 40 | case 'b': 41 | move('b', v, sign); 42 | resetV(); 43 | break; 44 | case 'c': 45 | move('c', v, sign); 46 | resetV(); 47 | break; 48 | 49 | default: 50 | Serial.write("...the fuck is that?"); 51 | resetV(); 52 | break; 53 | } 54 | } 55 | } 56 | 57 | void resetV() { 58 | sign = 1; 59 | v = 0; 60 | } 61 | 62 | void move(char ch, int steps, int sign) { 63 | 64 | switch(ch) { 65 | 66 | case 'a': 67 | motor1.step(steps*sign); 68 | break; 69 | case 'b': 70 | motor2.step(steps* sign); 71 | break; 72 | case 'c': 73 | myservo.write(steps); 74 | break; 75 | 76 | 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA0izUwz+jc1RQs8XZwfQMYBi6+lMZdJroUrNAjB1OGyin65Pv 3 | CSabviyQBIe34Ln/vFoi71+fRkMa8W0+JIVf7nRuGZELcrz/Q3XBJjP/Ooy6jTw4 4 | /sl6IRqe9oy0AdL7y66lHHWctDHolGwFVussMbkHGt0ao6bK0sjX9BzfkoVYzjRn 5 | 0ltkfiGBlF0eqQCPTYg5Ymjkz2sj3xE21pjzgRGNs5Ruscu9TtnjUqAio+g89BxJ 6 | 7nEVNTKNMCq6Tkph2f6kC/vyyUJaPY3e8tMKSAMJoqIe2ZAKD67OywsQhQ50soGW 7 | S6kLG5blct991ViPhiM8D7+3/yb9gHep+dai2wIDAQABAoIBAA4kiiShUaRfFhci 8 | xyZv/60vvNU/ZkUcb2mxOjSzfCADulj12W3qNSy03vQPFeZ/XaAVCKjG7HbT6So5 9 | yaa3et8W4pnB0t1jrmGhuN+TPCd6dMswND2g3SEBI5U/npVI1zy7gczKdUMC4L2s 10 | DyfsJ0spQjsiibpdbOSGM1Hv53N6elojiI4xSJ8C8hSuwNqVWWgNVmwDYeiGjsxw 11 | B7jkjt2WzgtOfnRJi/dXXxstXJ5UycwWnGq5H+/xNc20htm5fGqSSfuxlsQDWqrz 12 | KrxWPl5mejDbaK/QOnR/zOvuLprWBO+9FdidBFxIqjKpHMW4pspEUBzMDeY6i0g3 13 | FiADJAECgYEA9twiceXauRbQtjoYfYGMas2hDltkTmMD0YUArmpDm5T51SolVvyJ 14 | vHdNVnHM8/rTOEPpIIBphYxzSZV+A9R69YLgZZtuG1XVpxVEZcZAzPk334OTShnG 15 | 5wcn8DlRDpxp+UxHLcEb86Kktky+lzBmHHfCVUlpjS5x/wGSDPNoMQECgYEA2fT6 16 | s0xpDdbL6a0dailMq1OCe2f4+2+C4GZwbAqPo7oSoj09w5tAXPlBo9WJPRMdmiyE 17 | 3eMzaJZz7hoEVft5YpWnrIDVN19VF5DnCfq1JQFsC2S9tCuW2Z2zQM4mh5i6yVD7 18 | CsAF5SAvsGg6tUDnCr7JQM3B3g6P/JgRtiett9sCgYAp3vTItOAAfOoxvnDg+NRE 19 | WVR0BvRgh1Lieti+vK9y+DXX/ioPMw55tRKdEvhcebLR3FLyfFVpnYUAeibBp4yj 20 | j+tX0KFAabKvgF/1k0byfWMIulyFORIPpZsUl+QzwjwHWPTJwfNnw7KEaUf6hO8M 21 | 7RM9PBJ3K+WSB0YqhC6sAQKBgQDE0Zl2QgD380jOanqOfF70s4O3xssq2qbX1wmc 22 | NulBE21yPlVBBzhW/OsOdGzqzxFDQQJ2kI3C22Iy2CGkN2OE59mzR9/Ckf74Oey4 23 | sUHU+fTEUSORyG7/co4FbmOSvROnac5ZBdAM+US3S1gAh2xb4q9AKs3vpTzW/8e2 24 | sxwHUwKBgECcdzllYaYovU2CfnpeoYuXSsUwVWo5vCyoewn0c4slEqor/6QiwsQF 25 | IJCPGpbSJphT/3vCFapQ/SZjNR/UXVUPX6FtSM5zpEB7t65PNeZBXS0wGFwSzJkr 26 | yUtGxWJxDJ0KX9BwbAIJODXAz/EdLjW/Rd3sk/GAsZGyfUOwcAqe 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /Core_XY_control3/Core_XY_control3.ino: -------------------------------------------------------------------------------- 1 | 2 | int v = 0; 3 | int sign = 1; 4 | 5 | #include 6 | #include 7 | // write a,b for steppers 8 | Stepper motor1 = Stepper(200, 4,5, 6,7); 9 | Stepper motor2 = Stepper(200, 10,11,12,13); 10 | Servo myservo; 11 | int rpm = 60; 12 | 13 | void setup() { 14 | 15 | Serial.begin(38400); 16 | Serial.println("Stepper test!"); 17 | motor1.setSpeed(rpm); // rpm 18 | motor2.setSpeed(rpm); 19 | myservo.attach(3); 20 | } 21 | 22 | void loop() { 23 | // put your main code here, to run repeatedly: 24 | if ( Serial.available()) { 25 | char ch = Serial.read(); 26 | 27 | switch(ch) { 28 | case '0'...'9': 29 | //Pretty goddamn clever right here. Not mine. 30 | v = v * 10 + ch - '0'; 31 | break; 32 | case '-': 33 | //Pretty goddamn clever right here. This time ours. 34 | sign *= -1; 35 | //Serial.println("NEGATE!"); 36 | break; 37 | case 'a': 38 | move('a', v, sign); 39 | resetV(); 40 | break; 41 | case 'b': 42 | move('b', v, sign); 43 | resetV(); 44 | break; 45 | case 'c': 46 | move('c', v, sign); 47 | resetV(); 48 | break; 49 | 50 | default: 51 | Serial.write("...the fuck is that?"); 52 | resetV(); 53 | break; 54 | } 55 | } 56 | } 57 | 58 | void resetV() { 59 | sign = 1; 60 | v = 0; 61 | } 62 | 63 | void move(char ch, int steps, int sign) { 64 | 65 | switch(ch) { 66 | 67 | case 'a': 68 | motor1.step(steps*sign); 69 | break; 70 | case 'b': 71 | motor2.step(steps* sign); 72 | break; 73 | case 'c': 74 | myservo.write(steps); 75 | break; 76 | 77 | 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Core_XY_control/Core_XY_control.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | int v = 0; 4 | int sign = 1; 5 | 6 | // Connect a stepper motor with 48 steps per revolution (7.5 degree) 7 | // to motor port #2 (M3 and M4) 8 | AF_Stepper motor1(200, 1); 9 | AF_Stepper motor2(200, 2); 10 | 11 | void setup() { 12 | // put your setup code here, to run once: 13 | 14 | Serial.begin(38400); // set up Serial library at 9600 bps 15 | Serial.println("Stepper test!"); 16 | 17 | motor1.setSpeed(200); // 10 rpm 18 | motor2.setSpeed(200); 19 | } 20 | 21 | void loop() { 22 | // put your main code here, to run repeatedly: 23 | if ( Serial.available()) { 24 | char ch = Serial.read(); 25 | 26 | switch(ch) { 27 | case '0'...'9': 28 | //Pretty goddamn clever right here. Not mine. 29 | v = v * 10 + ch - '0'; 30 | break; 31 | case '-': 32 | //Pretty goddamn clever right here. This time ours. 33 | sign *= -1; 34 | //Serial.println("NEGATE!"); 35 | break; 36 | case 'a': 37 | move('a', v, sign); 38 | resetV(); 39 | break; 40 | case 'b': 41 | move('b', v, sign); 42 | resetV(); 43 | break; 44 | default: 45 | Serial.write("...the fuck is that?"); 46 | resetV(); 47 | break; 48 | } 49 | } 50 | } 51 | 52 | void resetV() { 53 | sign = 1; 54 | v = 0; 55 | } 56 | 57 | void move(char ch, int steps, int sign) { 58 | int dir; 59 | if(sign <= 0){ 60 | //Serial.println("BACKWARD!"); 61 | dir = BACKWARD; 62 | } 63 | else{ 64 | dir = FORWARD; 65 | } 66 | switch(ch) { 67 | 68 | case 'a': 69 | motor1.step(steps, dir, MICROSTEP); 70 | break; 71 | case 'b': 72 | motor2.step(steps, dir, MICROSTEP); 73 | break; 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /gcode3.py: -------------------------------------------------------------------------------- 1 | #Now we're going to use control4 firmware 2 | 3 | 4 | from parse import compile 5 | p = compile("G{code} X{x} Y{y}") 6 | import sys 7 | import serial 8 | port = '/dev/ttyUSB0'#'/dev/cu.wch ch341 USB=>RS232 1410'#'tty.wch'#'cu.wch ch341 USB=>RS232 1410' 9 | ser = serial.Serial(port,38400) # open serial port 10 | print(ser.name) # check which port was really used 11 | import numpy as np 12 | import time 13 | 14 | 15 | def raisepen(): 16 | ser.write('50c') 17 | #time.sleep(0.1) 18 | def lowerpen(): 19 | ser.write('40c') 20 | #time.sleep(0.1) 21 | 22 | 23 | 24 | 25 | def move(steps): 26 | ser.write(str(int(steps[0])) + b'a') 27 | ser.write(str(int(steps[1])) + b'b') 28 | ser.write(b'x') 29 | 30 | def convertToMotor(x): 31 | return np.array([x[0]+x[1],x[0]-x[1]])/np.sqrt(2) 32 | ''' 33 | def draw_line(x,xend): 34 | x = convertToMotor(x) 35 | xend = convertToMotor(xend) 36 | deltax = (xend - x)/N 37 | xint = np.rint(x) 38 | for i in range(N): 39 | x = x + deltax 40 | #print x 41 | if np.any(np.rint(x) != xint): 42 | steps = np.rint(x) - xint 43 | move(steps) 44 | xint = np.rint(x) 45 | time.sleep(0.03) 46 | ''' 47 | 48 | def move_to(x): 49 | x = convertToMotor(x) 50 | ser.write(str(int(x[0])) + b'a') 51 | ser.write(str(int(x[1])) + b'b') 52 | ser.write(b'x') 53 | print ser.readline() 54 | #time.sleep(.1) 55 | 56 | 57 | 58 | x = np.zeros(2) 59 | N=1000 60 | raisepen() 61 | filename = sys.argv[1] 62 | f= open(filename) 63 | 64 | #We need initial readline for some inexplicable reason. Seriously. 65 | print ser.readline() 66 | 67 | for line in f: 68 | #print line 69 | if line[0:2] == "G1": 70 | command = p.parse(line) 71 | if command != None: 72 | command = command.named 73 | x = np.array([float(command['x']),float(command['y'])]) 74 | move_to(x) 75 | 76 | elif line[0:3] == "M05": 77 | raisepen() 78 | elif line[0:3] == "M03": 79 | lowerpen() 80 | 81 | ser.close() # close port 82 | -------------------------------------------------------------------------------- /drawing.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%Creator: cairo 1.14.0 (http://cairographics.org) 3 | %%CreationDate: Wed Apr 13 22:58:59 2016 4 | %%Pages: 1 5 | %%DocumentData: Clean7Bit 6 | %%LanguageLevel: 2 7 | %%BoundingBox: 0 -1 352 442 8 | %%EndComments 9 | %%BeginProlog 10 | save 11 | 50 dict begin 12 | /q { gsave } bind def 13 | /Q { grestore } bind def 14 | /cm { 6 array astore concat } bind def 15 | /w { setlinewidth } bind def 16 | /J { setlinecap } bind def 17 | /j { setlinejoin } bind def 18 | /M { setmiterlimit } bind def 19 | /d { setdash } bind def 20 | /m { moveto } bind def 21 | /l { lineto } bind def 22 | /c { curveto } bind def 23 | /h { closepath } bind def 24 | /re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto 25 | 0 exch rlineto 0 rlineto closepath } bind def 26 | /S { stroke } bind def 27 | /f { fill } bind def 28 | /f* { eofill } bind def 29 | /n { newpath } bind def 30 | /W { clip } bind def 31 | /W* { eoclip } bind def 32 | /BT { } bind def 33 | /ET { } bind def 34 | /pdfmark where { pop globaldict /?pdfmark /exec load put } 35 | { globaldict begin /?pdfmark /pop load def /pdfmark 36 | /cleartomark load def end } ifelse 37 | /BDC { mark 3 1 roll /BDC pdfmark } bind def 38 | /EMC { mark /EMC pdfmark } bind def 39 | /cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def 40 | /Tj { show currentpoint cairo_store_point } bind def 41 | /TJ { 42 | { 43 | dup 44 | type /stringtype eq 45 | { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse 46 | } forall 47 | currentpoint cairo_store_point 48 | } bind def 49 | /cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore 50 | cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def 51 | /Tf { pop /cairo_font exch def /cairo_font_matrix where 52 | { pop cairo_selectfont } if } bind def 53 | /Td { matrix translate cairo_font_matrix matrix concatmatrix dup 54 | /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point 55 | /cairo_font where { pop cairo_selectfont } if } bind def 56 | /Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def 57 | cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def 58 | /g { setgray } bind def 59 | /rg { setrgbcolor } bind def 60 | /d1 { setcachedevice } bind def 61 | %%EndProlog 62 | %%BeginSetup 63 | %%EndSetup 64 | %%Page: 1 1 65 | %%BeginPageSetup 66 | %%PageBoundingBox: 0 -1 352 442 67 | %%EndPageSetup 68 | q 0 -1 352 443 rectclip q 69 | 0 g 70 | 0.8 w 71 | 0 J 72 | 0 j 73 | [] 0.0 d 74 | 4 M q 1 0 0 -1 0 441.840698 cm 75 | 142.109 217.625 m 350.109 64.484 l 57.539 0.484 l 0.398 441.625 l h 76 | 142.109 217.625 m S Q 77 | Q Q 78 | showpage 79 | %%Trailer 80 | end restore 81 | %%EOF 82 | -------------------------------------------------------------------------------- /speech.py: -------------------------------------------------------------------------------- 1 | import speech_recognition as sr 2 | 3 | import contour 4 | 5 | from google import google, images 6 | 7 | options = images.ImageOptions() 8 | options.image_type = images.ImageType.LINE_DRAWING 9 | 10 | from os import listdir 11 | 12 | #options.format = 13 | 14 | import threading 15 | 16 | # this is called from the background thread 17 | def callback(recognizer, audio): 18 | # received audio data, now we'll recognize it using Google Speech Recognition 19 | try: 20 | # for testing purposes, we're just using the default API key 21 | # to use another API key, use `r.recognize_google(audio, key="GOOGLE_SPEECH_RECOGNITION_API_KEY")` 22 | # instead of `r.recognize_google(audio)` 23 | #text = recognizer.recognize_sphinx(audio) 24 | text = recognizer.recognize_google(audio) 25 | print("Google Speech Recognition thinks you said " +text) 26 | if len(text.split())>=2: 27 | request = text.split()[-2:] 28 | else: 29 | request = [text] 30 | results = google.search_images(' '.join(request), options) 31 | print results 32 | ''' 33 | for result in results: 34 | if result.link[-4:] == '.svg': 35 | images.download([result], path = "images/"+ '_'.join(request)) 36 | print "downloaded image" 37 | break 38 | ''' 39 | filepath = "images/"+ '_'.join(request) 40 | images.download([results[0]], path = filepath) 41 | filename = listdir(filepath)[0] 42 | #stop_listening() 43 | contour.show_contour(filepath+'/'+filename) 44 | #t = threading.Thread(target=contour.show_contour, args=(filepath+'/'+filename,)) 45 | #t.start() 46 | #r.listen_in_background(m, callback) 47 | except sr.UnknownValueError: 48 | print("Google Speech Recognition could not understand audio") 49 | except sr.RequestError as e: 50 | print("Could not request results from Google Speech Recognition service; {0}".format(e)) 51 | 52 | r = sr.Recognizer() 53 | m = sr.Microphone() 54 | with m as source: 55 | r.adjust_for_ambient_noise(source) # we only need to calibrate once, before we start listening 56 | 57 | r.pause_threshold = 0.2 58 | r.non_speaking_duration = r.pause_threshold - 0.1 59 | # start listening in the background (note that we don't have to do this inside a `with` statement) 60 | stop_listening = r.listen_in_background(m, callback) 61 | # `stop_listening` is now a function that, when called, stops background listening 62 | 63 | # do some other computation for 5 seconds, then stop listening and keep doing other computations 64 | import time 65 | for _ in range(50): time.sleep(0.1) # we're still listening even though the main thread is doing other things 66 | #print "didn't find nothing" 67 | #stop_listening() # calling this function requests that the background listener stop listening 68 | while True: time.sleep(0.1) 69 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Your page title here :) 9 | 10 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 38 |
39 |
40 |
41 |

DrawBot

42 |
43 |
44 |
45 | 46 |
47 | 48 | 49 |
50 |
51 | 52 |
53 |
54 |
55 | 56 | 58 | 59 | 60 | 106 | 107 | -------------------------------------------------------------------------------- /Core_XY_control4/Core_XY_control4.ino: -------------------------------------------------------------------------------- 1 | 2 | int v = 0; 3 | int sign = 1; 4 | 5 | #include 6 | #include 7 | #include 8 | #define SERVOPIN 3 9 | #define TOUCHDOWN_PIN 9 10 | 11 | // write a,b for steppers 12 | AccelStepper stepper1(AccelStepper::FULL4WIRE, 4, 5, 6, 7); 13 | AccelStepper stepper2(AccelStepper::FULL4WIRE, 10, 11, 12, 13); 14 | 15 | long positions[2]= {0,0}; 16 | 17 | // Up to 10 steppers can be handled as a group by MultiStepper 18 | MultiStepper steppers; 19 | Servo myservo; 20 | 21 | 22 | void setup() { 23 | 24 | Serial.begin(38400); 25 | Serial.print("LET'S PRINT BABAAAAH!\n"); //This is important for some reason? 26 | stepper1.setMaxSpeed(100); 27 | stepper2.setMaxSpeed(100); 28 | myservo.attach(SERVOPIN); 29 | // Then give them to MultiStepper to manage 30 | steppers.addStepper(stepper1); 31 | steppers.addStepper(stepper2); 32 | pinMode(TOUCHDOWN_PIN, INPUT_PULLUP); 33 | } 34 | 35 | void loop() { 36 | // put your main code here, to run repeatedly: 37 | if ( Serial.available()) { 38 | char ch = Serial.read(); 39 | 40 | switch(ch) { 41 | case '0'...'9': 42 | //Pretty goddamn clever right here. Not mine. 43 | v = v * 10 + ch - '0'; 44 | break; 45 | case '-': 46 | //Pretty goddamn clever right here. This time ours. 47 | sign *= -1; 48 | //Serial.println("NEGATE!"); 49 | break; 50 | case 'a': 51 | move('a', v, sign); 52 | resetV(); 53 | break; 54 | case 'b': 55 | move('b', v, sign); 56 | resetV(); 57 | break; 58 | case 'c': // Move 59 | move('c', v, sign); 60 | resetV(); 61 | break; 62 | case 's': // Set Speed 63 | stepper1.setMaxSpeed(v); 64 | stepper2.setMaxSpeed(v); 65 | resetV(); 66 | break; 67 | case 'r': 68 | stepper1.setCurrentPosition(0); 69 | stepper2.setCurrentPosition(0); 70 | resetV(); 71 | break; 72 | case 'h': 73 | touchdown(); 74 | resetV(); 75 | break; 76 | 77 | case 'x': // Execute action 78 | steppers.moveTo(positions); 79 | steppers.runSpeedToPosition(); 80 | Serial.print("Ready\n"); 81 | resetV(); 82 | break; 83 | 84 | default: 85 | Serial.write("...the fuck is that?"); 86 | resetV(); 87 | break; 88 | } 89 | } 90 | } 91 | 92 | void resetV() { 93 | sign = 1; 94 | v = 0; 95 | } 96 | 97 | 98 | void touchdown(){ 99 | int angle = 90; 100 | myservo.write(angle); 101 | delay(10); 102 | while(digitalRead(TOUCHDOWN_PIN) == 1 && angle > 0){ 103 | angle--; 104 | myservo.write(angle); 105 | delay(50); 106 | //Serial.println(digitalRead(TOUCHDOWN_PIN)); 107 | } 108 | Serial.println(angle); 109 | myservo.write(90); 110 | } 111 | 112 | void move(char ch, int steps, int sign) { 113 | 114 | switch(ch) { 115 | 116 | case 'a': 117 | positions[0]=long(steps*sign); 118 | break; 119 | case 'b': 120 | positions[1]=long(steps*sign); 121 | break; 122 | case 'c': 123 | myservo.write(steps); 124 | break; 125 | 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /draw_server.py: -------------------------------------------------------------------------------- 1 | from draw import DrawBot 2 | import sys 3 | from flask import Flask 4 | from flask import request 5 | from flask import render_template 6 | import threading 7 | import time 8 | import json 9 | from photo import PhotoConverter 10 | app = Flask(__name__) 11 | #app.debug = True 12 | bot = DrawBot() 13 | 14 | #!/usr/bin/env python 15 | import signal 16 | 17 | def signal_handler(signal, frame): 18 | print('You pressed Ctrl+C!') 19 | bot.ser.close() 20 | sys.exit(0) 21 | signal.signal(signal.SIGINT, signal_handler) 22 | #bot = False 23 | gcode = "" 24 | 25 | 26 | 27 | @app.route("/gcode") 28 | def hello(): 29 | return '
' 30 | 31 | 32 | @app.route('/') 33 | def root(): 34 | return render_template('index.html') 35 | 36 | @app.route('/contour') 37 | def contour(): 38 | return app.send_static_file('contour.jpg') 39 | 40 | @app.route('/frame') 41 | def frame(): 42 | return app.send_static_file('frame.jpg') 43 | 44 | @app.route('/admin') 45 | def admin(): 46 | return render_template('admin.html') 47 | 48 | ''' 49 | @app.route('/js/') 50 | def send_js(path): 51 | return send_from_directory('js', path) 52 | ''' 53 | 54 | @app.route("/home") 55 | def home(): 56 | bot.homepen() 57 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 58 | 59 | 60 | 61 | @app.route("/draw") 62 | def draw(): 63 | bot.draw(request.args.get('gcode','').split('\r\n')) 64 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 65 | 66 | @app.route("/photo") #This is pretend 67 | def photo(): 68 | photo = PhotoConverter() 69 | photo.takePhoto() 70 | gcode = photo.convertContourstoGcode().split('\r\n') 71 | photo.closeCamera() 72 | photo.saveContour('static/contour.jpg') 73 | photo.saveFrame('static/frame.jpg') 74 | photo.sortContours() 75 | photo.scaleContours() 76 | status = bot.draw(gcode) 77 | if status == "busy": 78 | return json.dumps({'success':False}), 200, {'ContentType':'application/json'} 79 | else: 80 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 81 | 82 | @app.route("/takephoto") 83 | def takePhoto(): 84 | photo = PhotoConverter() 85 | photo.takePhoto() 86 | global gcode 87 | photo.closeCamera() 88 | photo.saveContour('static/contour.jpg') 89 | photo.saveFrame('static/frame.jpg') 90 | photo.sortContours() 91 | photo.scaleContours() 92 | gcode = photo.convertContourstoGcode().split('\r\n') 93 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 94 | 95 | @app.route("/drawphoto") 96 | def drawPhoto(): 97 | print gcode 98 | bot.draw(gcode) 99 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 100 | 101 | ''' 102 | 103 | @app.route('/offset/') 104 | def set_offset(offset): 105 | bot.offset = offset 106 | print offset 107 | print type(offset) 108 | return 'New offset' 109 | ''' 110 | 111 | @app.route('/move', methods=['GET', 'POST']) 112 | def move(): 113 | if request.method == 'POST': 114 | bot.move_to( [int(request.form['x']), int(request.form['y'])] ) 115 | return '
x
y
' 116 | 117 | 118 | @app.route('/reset') 119 | def reset(): 120 | bot.reset() 121 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 122 | 123 | @app.route('/stop') 124 | def stop(): 125 | bot.setStop(True) 126 | return json.dumps({'success':True}), 200, {'ContentType':'application/json'} 127 | 128 | 129 | 130 | 131 | app.run(host="0.0.0.0", port=5000) 132 | -------------------------------------------------------------------------------- /Core_XY_control5/Core_XY_control5.ino: -------------------------------------------------------------------------------- 1 | 2 | int v = 0; 3 | int sign = 1; 4 | 5 | #include 6 | #include 7 | #include 8 | #define SERVOPIN 3 9 | #define TOUCHDOWN_PIN 9 10 | #define X_LIMIT_PIN 2 11 | #define Y_LIMIT_PIN 8 12 | 13 | // write a,b for steppers 14 | AccelStepper stepper1(AccelStepper::FULL4WIRE, 4, 5, 6, 7); 15 | AccelStepper stepper2(AccelStepper::FULL4WIRE, 10, 11, 12, 13); 16 | 17 | long positions[2]= {0,0}; 18 | 19 | // Up to 10 steppers can be handled as a group by MultiStepper 20 | MultiStepper steppers; 21 | Servo myservo; 22 | 23 | 24 | void setup() { 25 | 26 | Serial.begin(38400); 27 | Serial.print("LET'S PRINT BABAAAAH!\n"); //This is important for some reason? 28 | stepper1.setMaxSpeed(100); 29 | stepper2.setMaxSpeed(100); 30 | myservo.attach(SERVOPIN); 31 | // Then give them to MultiStepper to manage 32 | steppers.addStepper(stepper1); 33 | steppers.addStepper(stepper2); 34 | pinMode(TOUCHDOWN_PIN, INPUT_PULLUP); 35 | pinMode(X_LIMIT_PIN, INPUT_PULLUP); 36 | pinMode(Y_LIMIT_PIN, INPUT_PULLUP); 37 | } 38 | 39 | void loop() { 40 | // put your main code here, to run repeatedly: 41 | if ( Serial.available()) { 42 | char ch = Serial.read(); 43 | 44 | switch(ch) { 45 | case '0'...'9': 46 | //Pretty goddamn clever right here. Not mine. 47 | v = v * 10 + ch - '0'; 48 | break; 49 | case '-': 50 | //Pretty goddamn clever right here. This time ours. 51 | sign *= -1; 52 | //Serial.println("NEGATE!"); 53 | break; 54 | case 'a': 55 | move('a', v, sign); 56 | resetV(); 57 | break; 58 | case 'b': 59 | move('b', v, sign); 60 | resetV(); 61 | break; 62 | case 'c': // Move 63 | move('c', v, sign); 64 | resetV(); 65 | break; 66 | case 's': // Set Speed 67 | stepper1.setMaxSpeed(v); 68 | stepper2.setMaxSpeed(v); 69 | resetV(); 70 | break; 71 | case 'r': 72 | resetCoordinates(); 73 | resetV(); 74 | break; 75 | case 'h': 76 | touchdown(); 77 | resetV(); 78 | break; 79 | case 'z': 80 | homeCoords(); 81 | resetV(); 82 | break; 83 | case 'x': // Execute action 84 | steppers.moveTo(positions); 85 | steppers.runSpeedToPosition(); 86 | Serial.print("Ready\n"); 87 | resetV(); 88 | break; 89 | 90 | default: 91 | Serial.write("...the fuck is that?"); 92 | resetV(); 93 | break; 94 | } 95 | } 96 | } 97 | 98 | void resetV() { 99 | sign = 1; 100 | v = 0; 101 | } 102 | 103 | void homeCoords() { 104 | Serial.println("homing"); 105 | bool x_home = false; 106 | bool y_home = false; 107 | y_home = (digitalRead(Y_LIMIT_PIN) == 0); 108 | while (!y_home){ 109 | positions[0] -= 1; 110 | positions[1] += 1; 111 | steppers.moveTo(positions); 112 | steppers.runSpeedToPosition(); 113 | y_home = (digitalRead(Y_LIMIT_PIN) == 0); 114 | } 115 | 116 | 117 | x_home = (digitalRead(X_LIMIT_PIN) == 0); 118 | while (!x_home){ 119 | positions[0] -= 1; 120 | positions[1] -= 1; 121 | steppers.moveTo(positions); 122 | steppers.runSpeedToPosition(); 123 | x_home = (digitalRead(X_LIMIT_PIN) == 0); 124 | } 125 | 126 | resetCoordinates(); 127 | } 128 | 129 | void resetCoordinates() { 130 | stepper1.setCurrentPosition(0); 131 | stepper2.setCurrentPosition(0); 132 | } 133 | 134 | void touchdown(){ 135 | int angle = 90; 136 | myservo.write(angle); 137 | delay(10); 138 | while(digitalRead(TOUCHDOWN_PIN) == 1 && angle > 0){ 139 | angle--; 140 | myservo.write(angle); 141 | delay(50); 142 | //Serial.println(digitalRead(TOUCHDOWN_PIN)); 143 | } 144 | Serial.println(angle); 145 | myservo.write(90); 146 | } 147 | 148 | void move(char ch, int steps, int sign) { 149 | 150 | switch(ch) { 151 | 152 | case 'a': 153 | positions[0]=long(steps*sign); 154 | break; 155 | case 'b': 156 | positions[1]=long(steps*sign); 157 | break; 158 | case 'c': 159 | myservo.write(steps); 160 | break; 161 | 162 | 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Core_XY_control6/Core_XY_control6.ino: -------------------------------------------------------------------------------- 1 | 2 | int v = 0; 3 | int sign = 1; 4 | 5 | #include 6 | #include 7 | #include 8 | #define SERVOPIN 9 9 | #define TOUCHDOWN_PIN 8 10 | #define X_LIMIT_PIN 2 11 | #define Y_LIMIT_PIN 3 12 | 13 | // write a,b for steppers 14 | AccelStepper stepper1(AccelStepper::FULL4WIRE, 4, 5, 6, 7); 15 | AccelStepper stepper2(AccelStepper::FULL4WIRE, A5, A4, A3, A2); 16 | 17 | long positions[2]= {0,0}; 18 | 19 | // Up to 10 steppers can be handled as a group by MultiStepper 20 | MultiStepper steppers; 21 | Servo myservo; 22 | 23 | 24 | void setup() { 25 | 26 | Serial.begin(38400); 27 | Serial.print("LET'S PRINT BABAAAAH!\n"); //This is important for some reason? 28 | stepper1.setMaxSpeed(100); 29 | stepper2.setMaxSpeed(100); 30 | myservo.attach(SERVOPIN); 31 | myservo.write(150); 32 | // Then give them to MultiStepper to manage 33 | steppers.addStepper(stepper1); 34 | steppers.addStepper(stepper2); 35 | pinMode(TOUCHDOWN_PIN, INPUT_PULLUP); 36 | pinMode(X_LIMIT_PIN, INPUT_PULLUP); 37 | pinMode(Y_LIMIT_PIN, INPUT_PULLUP); 38 | } 39 | 40 | void loop() { 41 | // put your main code here, to run repeatedly: 42 | if ( Serial.available()) { 43 | char ch = Serial.read(); 44 | 45 | switch(ch) { 46 | case '0'...'9': 47 | //Pretty goddamn clever right here. Not mine. 48 | v = v * 10 + ch - '0'; 49 | break; 50 | case '-': 51 | //Pretty goddamn clever right here. This time ours. 52 | sign *= -1; 53 | //Serial.println("NEGATE!"); 54 | break; 55 | case 'a': 56 | move('a', v, sign); 57 | resetV(); 58 | break; 59 | case 'b': 60 | move('b', v, sign); 61 | resetV(); 62 | break; 63 | case 'c': // Move 64 | move('c', v, sign); 65 | resetV(); 66 | break; 67 | case 's': // Set Speed 68 | stepper1.setMaxSpeed(v); 69 | stepper2.setMaxSpeed(v); 70 | resetV(); 71 | break; 72 | case 'r': 73 | resetCoordinates(); 74 | resetV(); 75 | break; 76 | case 'h': 77 | touchdown(); 78 | resetV(); 79 | break; 80 | case 'z': 81 | homeCoords(); 82 | resetV(); 83 | break; 84 | case 'x': // Execute action 85 | steppers.moveTo(positions); 86 | steppers.runSpeedToPosition(); 87 | Serial.print("Ready\n"); 88 | resetV(); 89 | break; 90 | 91 | default: 92 | Serial.write("...the fuck is that?"); 93 | resetV(); 94 | break; 95 | } 96 | } 97 | } 98 | 99 | void resetV() { 100 | sign = 1; 101 | v = 0; 102 | } 103 | 104 | void homeCoords() { 105 | Serial.println("homing"); 106 | bool x_home = false; 107 | bool y_home = false; 108 | y_home = (digitalRead(Y_LIMIT_PIN) == 0); 109 | while (!y_home){ 110 | positions[0] -= 1; 111 | positions[1] += 1; 112 | steppers.moveTo(positions); 113 | steppers.runSpeedToPosition(); 114 | y_home = (digitalRead(Y_LIMIT_PIN) == 0); 115 | } 116 | 117 | 118 | x_home = (digitalRead(X_LIMIT_PIN) == 0); 119 | while (!x_home){ 120 | positions[0] -= 1; 121 | positions[1] -= 1; 122 | steppers.moveTo(positions); 123 | steppers.runSpeedToPosition(); 124 | x_home = (digitalRead(X_LIMIT_PIN) == 0); 125 | } 126 | 127 | resetCoordinates(); 128 | Serial.println("homed"); 129 | } 130 | 131 | void resetCoordinates() { 132 | stepper1.setCurrentPosition(0); 133 | stepper2.setCurrentPosition(0); 134 | } 135 | 136 | void touchdown(){ 137 | int angle = 150; 138 | myservo.write(angle); 139 | delay(10); 140 | while(digitalRead(TOUCHDOWN_PIN) == 1 && angle > 0){ 141 | angle--; 142 | myservo.write(angle); 143 | delay(50); 144 | //Serial.println(digitalRead(TOUCHDOWN_PIN)); 145 | } 146 | Serial.println(angle); 147 | myservo.write(90); 148 | } 149 | 150 | void move(char ch, int steps, int sign) { 151 | 152 | switch(ch) { 153 | 154 | case 'a': 155 | positions[0]=long(steps*sign); 156 | break; 157 | case 'b': 158 | positions[1]=long(steps*sign); 159 | break; 160 | case 'c': 161 | myservo.write(steps); 162 | break; 163 | 164 | 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /templates/admin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Your page title here :) 9 | 10 | 11 | 12 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 38 |
39 |
40 |
41 |

DrawBot Control Page

42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 |
50 |
51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 |
59 |
60 |
61 | 62 | 64 | 65 | 66 | 156 | 157 | -------------------------------------------------------------------------------- /draw/DrawBot.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | from parse import compile 4 | import sys 5 | import serial 6 | import serial.tools.list_ports 7 | import threading 8 | 9 | #Full Range of x is 1600, y is 1200 10 | 11 | class DrawBot: 12 | def __init__(self, port=None, baud=38400): 13 | if port: 14 | self.port = port 15 | else: 16 | self.port = serial.tools.list_ports.comports()[-1][0] 17 | #self.port = "/dev/ttyUSB0" 18 | self.ser = serial.Serial(self.port, baud,timeout=30.) # open serial port 19 | self.ser.flushInput() 20 | self.p = compile("G{code} X{x} Y{y}") 21 | print self.ser.readline() 22 | self.busy = False 23 | #self.homepen() 24 | self.ser.write(str(150) + 'c') 25 | self.stop = False 26 | self.homexy() 27 | self.ser.flushInput() 28 | 29 | self.w = 1600 30 | self.h= 1200 31 | 32 | #Checks if busy, spins off a thread if not that will set busy to false when done 33 | def busyCheck(func): 34 | def func_wrapper(self,*args, **kwargs): 35 | if not self.busy: 36 | self.busy = True 37 | def func_wrapper2(*args, **kwargs): 38 | func(self,*args, **kwargs) 39 | self.busy = False 40 | threading.Thread(target=func_wrapper2, args=args, kwargs=kwargs).start() 41 | return "printing" 42 | else: 43 | print "busy" 44 | return "busy" 45 | return func_wrapper 46 | 47 | def drawFromFile(self, file_name): 48 | file = open(file_name) 49 | self.draw(file) 50 | file.close() 51 | 52 | @busyCheck 53 | def draw(self,gcode): 54 | self.ser.flushInput() 55 | x = np.zeros(2) 56 | #self.raisepen() 57 | self.ser.write(str(150) + 'c') 58 | self.goToPad() 59 | 60 | self.ser.flushInput() 61 | 62 | 63 | #self.ser.timeout=0.5 64 | #print self.ser.readline() 65 | #self.ser.timeout =0. 66 | self.ser.flushInput() 67 | 68 | for line in gcode: 69 | print line 70 | if line[0:2] == "G1": 71 | command = self.p.parse(line) 72 | print command 73 | if command != None: 74 | command = command.named 75 | x = np.array([float(command['x']),float(command['y'])]) 76 | self.move_to(x) 77 | 78 | elif line[0:3] == "M05": 79 | self.raisepen() 80 | elif line[0:3] == "M03": 81 | self.lowerpen() 82 | if self.stop: 83 | print "Bird Stopped" 84 | self.stop = False 85 | self.ser.flushInput() 86 | self.homexy() 87 | return "Bird. You Stopped" 88 | self.homexy() 89 | return "bird" 90 | def __del__(self): 91 | self.ser.close() 92 | def raisepen(self): 93 | self.ser.write(str(self.penAngle) + 'c') 94 | print str(self.penAngle) 95 | def lowerpen(self): 96 | self.ser.write(str(self.penAngle - 10) + 'c') 97 | print str(self.penAngle - 10) 98 | def homepen(self): 99 | self.ser.flushInput() 100 | self.ser.write('h'); 101 | self.penAngle = int(self.ser.readline()) 102 | def setStop(self, val): 103 | self.stop = val 104 | def reset(self): 105 | self.ser.flushInput() 106 | self.ser.write('r'); 107 | def homexy(self): 108 | self.ser.flushInput() 109 | self.ser.write('z'); 110 | print self.ser.readline() 111 | print self.ser.readline() 112 | def goToPad(self): 113 | self.move_to([0,1100]) 114 | self.move_to([815,1100]) 115 | self.move_to([815,665]) 116 | self.ser.flushInput() 117 | self.homepen() 118 | def move(self,steps): #DO NOT USE 119 | self.ser.write(str(int(steps[0])) + b'a') 120 | self.ser.write(str(int(steps[1])) + b'b') 121 | self.ser.write(b'x') 122 | def convertToMotor(self,x): 123 | return np.array([x[0]+x[1],x[0]-x[1]])/np.sqrt(2) 124 | def move_to(self,x): 125 | self.ser.flushInput() 126 | x = self.convertToMotor(x) 127 | self.ser.write(str(int(x[0])) + b'a') 128 | self.ser.write(str(int(x[1])) + b'b') 129 | self.ser.write(b'x') 130 | print self.ser.readline() 131 | #while self.ser.readline() != "Ready\n": 132 | #self.ser.write(str(int(x[0])) + b'a') 133 | #self.ser.write(str(int(x[1])) + b'b') 134 | #self.ser.write(b'x') 135 | # time.sleep(0.05) 136 | -------------------------------------------------------------------------------- /photo/PhotoConverter.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | 5 | class PhotoConverter: 6 | 7 | def __init__(self): 8 | self.fraction = 16 # 1/fraction largest area contours are drawn 9 | self.openCamera() 10 | self.robotFraction = 0.25 #Full Range of x is 1600, y is 1200 11 | 12 | 13 | def openCamera(self): 14 | self.cap = cv2.VideoCapture(0) 15 | _, frame = self.cap.read() 16 | def closeCamera(self): 17 | self.cap.release() 18 | 19 | def setDrawFraction(self, newfrac): 20 | self.robotFraction = newfrac 21 | 22 | def squareFrame(self,frame): 23 | print frame.shape 24 | if frame.shape[0] < frame.shape[1]: 25 | offset = int((frame.shape[1]-frame.shape[0])/2) 26 | 27 | return frame[:,offset:frame.shape[1]-offset,:] 28 | else: 29 | offset = int((frame.shape[0]-frame.shape[1])/2) 30 | return frame[offset:frame.shape[0]-offset,:,:] 31 | 32 | 33 | def takePhoto(self): 34 | #Take photo, convert to gray, downsample, threshold, find contours 35 | for i in range(10): 36 | _, frame = self.cap.read() 37 | if frame != None: 38 | break 39 | 40 | 41 | self.frame = self.squareFrame(frame) 42 | #self.frame = frame 43 | gray = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY) 44 | #gray = self.findFace(gray) 45 | gray = cv2.pyrDown(gray) 46 | 47 | self.h = gray.shape[0] 48 | self.w = gray.shape[1] 49 | 50 | self.thres = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,15,2) 51 | self.contours,self.hierarchy = cv2.findContours(self.thres,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) 52 | #Order contours by area 53 | area = np.asarray(map(cv2.contourArea, self.contours)) 54 | self.indexorder = np.argsort(area)[::-1] 55 | self.contours = np.array(self.contours) 56 | self.contours = self.contours[self.indexorder[: int(len(self.contours)/self.fraction)]] 57 | 58 | 59 | def drawContours(self): 60 | newempty = np.ones((self.h,self.w,3), dtype=np.uint8) 61 | for i in range(len(self.contours)): 62 | cv2.drawContours(newempty, self.contours, i, (0,0,255), 1) 63 | #Simplified triangles 64 | cv2.imshow('Contours',newempty) 65 | k = cv2.waitKey(0) 66 | cv2.destroyAllWindows() 67 | 68 | def saveContour(self, filename): 69 | newempty = np.ones((self.h,self.w,3), dtype=np.uint8) 70 | for i in range(len(self.contours)): 71 | cv2.drawContours(newempty, self.contours, i, (0,0,255), 1) 72 | cv2.imwrite(filename, newempty) 73 | 74 | ''' 75 | def scaleContours(self, w, h): 76 | xmax = 0 77 | ymax = 0 78 | w = float(w) 79 | h = float(h) 80 | 81 | scale = self.robotFraction * min(w / self.w, h / self.h) 82 | offsetx = (w - scale*self.w)/2 83 | offsety = (h - scale*self.h)/2 84 | 85 | for cnt in self.contours: 86 | for point in cnt: 87 | point = point[0] 88 | point[0] = int(point[0] * scale + offsetx) 89 | point[1] = int(point[1] * scale + offsety) 90 | self.w = w #int(self.w * scale) 91 | self.h = h #int(self.h * scale) 92 | print self.contours 93 | ''' 94 | #x = 630 95 | #y = 480 96 | #w = 370 97 | #h = 370 98 | def scaleContours(self, x=630, y=480, w=370, h=370): 99 | 100 | 101 | xmax = 0 102 | ymax = 0 103 | w = float(w) 104 | h = float(h) 105 | 106 | scale = min(w / self.w, h / self.h) 107 | offsetx = x#(w - scale*self.w)/2 108 | offsety = y#(h - scale*self.h)/2 109 | 110 | for cnt in self.contours: 111 | for point in cnt: 112 | point = point[0] 113 | point[0] = int(point[0] * scale + offsetx) 114 | point[1] = int((self.h-point[1]) * scale + offsety) 115 | self.w = w #int(self.w * scale) 116 | self.h = h #int(self.h * scale) 117 | #print self.contours 118 | 119 | def sortContours(self): 120 | temp = [] 121 | unused = np.copy(self.contours) 122 | temp.append(self.contours[0]) 123 | 124 | def rotateList(l,n): #cyclically roatets the list l so that the n index position is now at index 0 125 | return np.concatenate((l[n:],l[:n])) 126 | 127 | for i in range(1,len(self.contours)): 128 | closestdist = 10000000000 129 | closestcontourindex = 0 130 | lastpoint = temp[i-1][0] #where the pen is after drawing i-1 contour 131 | 132 | dists = map(lambda cnt: (cnt[:,0,0]-lastpoint[0][0])**2 + (cnt[:,0,1]-lastpoint[0][1])**2, unused) 133 | minofcnt = np.asarray(map(lambda cnt: np.amin(cnt), dists)) 134 | closestcontourindex = np.argmin(minofcnt) 135 | firstpointindex = np.argmin(dists[closestcontourindex]) 136 | 137 | closestcnt = unused[closestcontourindex] 138 | unused = np.delete(unused, closestcontourindex , axis=0) 139 | temp.append(rotateList(closestcnt, firstpointindex)) 140 | 141 | 142 | self.contours = np.asarray(temp) 143 | 144 | 145 | 146 | 147 | 148 | def saveFrame(self,filename): 149 | cv2.imwrite(filename, self.frame) 150 | 151 | 152 | def convertContourstoGcode(self): 153 | raisepen = "M05\r\n" 154 | lowerpen = "M03\r\n" 155 | def move(x,y): 156 | return "G1 X" + str(x) + " Y" + str(y) + "\r\n" 157 | 158 | gcode = "" 159 | #for cnt in self.contours[self.indexorder]: #format of contour is [ [[x y]], [[x,y]] ] Why they put double array in there god knows 160 | #for i in range(int(len(self.indexorder)/self.fraction)): 161 | for i in range(len(self.contours)): 162 | #cnt = self.contours[self.indexorder[i]] 163 | cnt = self.contours[i] 164 | gcode += raisepen # Also dule note that i needed to convert contours to numpy array to index like this 165 | gcode += move(cnt[0][0][0],cnt[0][0][1]) 166 | gcode += lowerpen 167 | for point in cnt: 168 | point = point[0] 169 | gcode += move(point[0],point[1]) 170 | gcode += raisepen 171 | gcode += move(0,0) 172 | return gcode 173 | 174 | def simplifyContours(self,eps=0.001): 175 | for i in range(len(self.contours)): 176 | cnt = self.contours[i] 177 | epsilon = eps*cv2.arcLength(cnt,True) 178 | self.contours[i] = cv2.approxPolyDP(cnt,epsilon,True) 179 | 180 | def findFace(self,gray): 181 | face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') 182 | faces = face_cascade.detectMultiScale(gray, 1.3, 5) 183 | if faces: #Perhaps a better approach would be to blur using faces as crispness points? Might want mutiple faces 184 | faces = sorted(faces, key = lambda (x,y,w,h): w * h, reverse=True) #Also sort for largest faces rather than arbitrary face 185 | (x,y,w,h) = faces[0] 186 | left = max(x-w/2,0) 187 | right = min(x+3*w/2,gray.shape[1]) 188 | down = min(y+3*h/2,gray.shape[0]) 189 | up = max(y-h/2,0) 190 | return gray[up:down, left:right] #Yes. The coordinate system of computers is goofy. 191 | else: 192 | return gray 193 | -------------------------------------------------------------------------------- /static/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /templates/static/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /templates/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } 428 | -------------------------------------------------------------------------------- /static/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | -------------------------------------------------------------------------------- /templates/css/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | -------------------------------------------------------------------------------- /templates/static/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | -------------------------------------------------------------------------------- /js/webcam.js: -------------------------------------------------------------------------------- 1 | // WebcamJS v1.0.6 2 | // Webcam library for capturing JPEG/PNG images in JavaScript 3 | // Attempts getUserMedia, falls back to Flash 4 | // Author: Joseph Huckaby: http://github.com/jhuckaby 5 | // Based on JPEGCam: http://code.google.com/p/jpegcam/ 6 | // Copyright (c) 2012 - 2015 Joseph Huckaby 7 | // Licensed under the MIT License 8 | 9 | (function(window) { 10 | var _userMedia; 11 | 12 | var Webcam = { 13 | version: '1.0.6', 14 | 15 | // globals 16 | protocol: location.protocol.match(/https/i) ? 'https' : 'http', 17 | loaded: false, // true when webcam movie finishes loading 18 | live: false, // true when webcam is initialized and ready to snap 19 | userMedia: true, // true when getUserMedia is supported natively 20 | 21 | params: { 22 | width: 0, 23 | height: 0, 24 | dest_width: 0, // size of captured image 25 | dest_height: 0, // these default to width/height 26 | image_format: 'jpeg', // image format (may be jpeg or png) 27 | jpeg_quality: 90, // jpeg image quality from 0 (worst) to 100 (best) 28 | force_flash: false, // force flash mode, 29 | flip_horiz: false, // flip image horiz (mirror mode) 30 | fps: 30, // camera frames per second 31 | upload_name: 'webcam', // name of file in upload post data 32 | constraints: null, // custom user media constraints, 33 | swfURL: '', // URI to webcam.swf movie (defaults to the js location) 34 | flashNotDetectedText: 'ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).' 35 | }, 36 | 37 | hooks: {}, // callback hook functions 38 | 39 | init: function() { 40 | // initialize, check for getUserMedia support 41 | var self = this; 42 | 43 | // Setup getUserMedia, with polyfill for older browsers 44 | // Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia 45 | this.mediaDevices = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) ? 46 | navigator.mediaDevices : ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? { 47 | getUserMedia: function(c) { 48 | return new Promise(function(y, n) { 49 | (navigator.mozGetUserMedia || 50 | navigator.webkitGetUserMedia).call(navigator, c, y, n); 51 | }); 52 | } 53 | } : null); 54 | 55 | window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; 56 | this.userMedia = this.userMedia && !!this.mediaDevices && !!window.URL; 57 | 58 | // Older versions of firefox (< 21) apparently claim support but user media does not actually work 59 | if (navigator.userAgent.match(/Firefox\D+(\d+)/)) { 60 | if (parseInt(RegExp.$1, 10) < 21) this.userMedia = null; 61 | } 62 | 63 | // Make sure media stream is closed when navigating away from page 64 | if (this.userMedia) { 65 | window.addEventListener( 'beforeunload', function(event) { 66 | self.reset(); 67 | } ); 68 | } 69 | }, 70 | 71 | attach: function(elem) { 72 | // create webcam preview and attach to DOM element 73 | // pass in actual DOM reference, ID, or CSS selector 74 | if (typeof(elem) == 'string') { 75 | elem = document.getElementById(elem) || document.querySelector(elem); 76 | } 77 | if (!elem) { 78 | return this.dispatch('error', "Could not locate DOM element to attach to."); 79 | } 80 | this.container = elem; 81 | elem.innerHTML = ''; // start with empty element 82 | 83 | // insert "peg" so we can insert our preview canvas adjacent to it later on 84 | var peg = document.createElement('div'); 85 | elem.appendChild( peg ); 86 | this.peg = peg; 87 | 88 | // set width/height if not already set 89 | if (!this.params.width) this.params.width = elem.offsetWidth; 90 | if (!this.params.height) this.params.height = elem.offsetHeight; 91 | 92 | // set defaults for dest_width / dest_height if not set 93 | if (!this.params.dest_width) this.params.dest_width = this.params.width; 94 | if (!this.params.dest_height) this.params.dest_height = this.params.height; 95 | 96 | this.userMedia = _userMedia === undefined ? this.userMedia : _userMedia; 97 | // if force_flash is set, disable userMedia 98 | if (this.params.force_flash) { 99 | _userMedia = this.userMedia; 100 | this.userMedia = null 101 | } 102 | 103 | // check for default fps 104 | if (typeof this.params.fps !== "number") this.params.fps = 30; 105 | 106 | // adjust scale if dest_width or dest_height is different 107 | var scaleX = this.params.width / this.params.dest_width; 108 | var scaleY = this.params.height / this.params.dest_height; 109 | 110 | if (this.userMedia) { 111 | // setup webcam video container 112 | var video = document.createElement('video'); 113 | video.setAttribute('autoplay', 'autoplay'); 114 | video.style.width = '' + this.params.dest_width + 'px'; 115 | video.style.height = '' + this.params.dest_height + 'px'; 116 | 117 | if ((scaleX != 1.0) || (scaleY != 1.0)) { 118 | elem.style.overflow = 'hidden'; 119 | video.style.webkitTransformOrigin = '0px 0px'; 120 | video.style.mozTransformOrigin = '0px 0px'; 121 | video.style.msTransformOrigin = '0px 0px'; 122 | video.style.oTransformOrigin = '0px 0px'; 123 | video.style.transformOrigin = '0px 0px'; 124 | video.style.webkitTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 125 | video.style.mozTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 126 | video.style.msTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 127 | video.style.oTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 128 | video.style.transform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 129 | } 130 | 131 | // add video element to dom 132 | elem.appendChild( video ); 133 | this.video = video; 134 | 135 | // ask user for access to their camera 136 | var self = this; 137 | this.mediaDevices.getUserMedia({ 138 | "audio": false, 139 | "video": this.params.constraints || { 140 | mandatory: { 141 | minWidth: this.params.dest_width, 142 | minHeight: this.params.dest_height 143 | } 144 | } 145 | }) 146 | .then( function(stream) { 147 | // got access, attach stream to video 148 | video.src = window.URL.createObjectURL( stream ) || stream; 149 | self.stream = stream; 150 | self.loaded = true; 151 | self.live = true; 152 | self.dispatch('load'); 153 | self.dispatch('live'); 154 | self.flip(); 155 | }) 156 | .catch( function(err) { 157 | return self.dispatch('error', "Could not access webcam: " + err.name + ": " + err.message, err); 158 | }); 159 | } 160 | else { 161 | // flash fallback 162 | window.Webcam = Webcam; // needed for flash-to-js interface 163 | var div = document.createElement('div'); 164 | div.innerHTML = this.getSWFHTML(); 165 | elem.appendChild( div ); 166 | } 167 | 168 | // setup final crop for live preview 169 | if (this.params.crop_width && this.params.crop_height) { 170 | var scaled_crop_width = Math.floor( this.params.crop_width * scaleX ); 171 | var scaled_crop_height = Math.floor( this.params.crop_height * scaleY ); 172 | 173 | elem.style.width = '' + scaled_crop_width + 'px'; 174 | elem.style.height = '' + scaled_crop_height + 'px'; 175 | elem.style.overflow = 'hidden'; 176 | 177 | elem.scrollLeft = Math.floor( (this.params.width / 2) - (scaled_crop_width / 2) ); 178 | elem.scrollTop = Math.floor( (this.params.height / 2) - (scaled_crop_height / 2) ); 179 | } 180 | else { 181 | // no crop, set size to desired 182 | elem.style.width = '' + this.params.width + 'px'; 183 | elem.style.height = '' + this.params.height + 'px'; 184 | } 185 | }, 186 | 187 | reset: function() { 188 | // shutdown camera, reset to potentially attach again 189 | if (this.preview_active) this.unfreeze(); 190 | 191 | // attempt to fix issue #64 192 | this.unflip(); 193 | 194 | if (this.userMedia) { 195 | if (this.stream) { 196 | if (this.stream.getVideoTracks) { 197 | // get video track to call stop on it 198 | var tracks = this.stream.getVideoTracks(); 199 | if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop(); 200 | } 201 | else if (this.stream.stop) { 202 | // deprecated, may be removed in future 203 | this.stream.stop(); 204 | } 205 | } 206 | delete this.stream; 207 | delete this.video; 208 | } 209 | 210 | if (this.userMedia !== true) { 211 | // call for turn off camera in flash 212 | this.getMovie()._releaseCamera(); 213 | } 214 | 215 | if (this.container) { 216 | this.container.innerHTML = ''; 217 | delete this.container; 218 | } 219 | 220 | this.loaded = false; 221 | this.live = false; 222 | }, 223 | 224 | set: function() { 225 | // set one or more params 226 | // variable argument list: 1 param = hash, 2 params = key, value 227 | if (arguments.length == 1) { 228 | for (var key in arguments[0]) { 229 | this.params[key] = arguments[0][key]; 230 | } 231 | } 232 | else { 233 | this.params[ arguments[0] ] = arguments[1]; 234 | } 235 | }, 236 | 237 | on: function(name, callback) { 238 | // set callback hook 239 | name = name.replace(/^on/i, '').toLowerCase(); 240 | if (!this.hooks[name]) this.hooks[name] = []; 241 | this.hooks[name].push( callback ); 242 | }, 243 | 244 | off: function(name, callback) { 245 | // remove callback hook 246 | name = name.replace(/^on/i, '').toLowerCase(); 247 | if (this.hooks[name]) { 248 | if (callback) { 249 | // remove one selected callback from list 250 | var idx = this.hooks[name].indexOf(callback); 251 | if (idx > -1) this.hooks[name].splice(idx, 1); 252 | } 253 | else { 254 | // no callback specified, so clear all 255 | this.hooks[name] = []; 256 | } 257 | } 258 | }, 259 | 260 | dispatch: function() { 261 | // fire hook callback, passing optional value to it 262 | var name = arguments[0].replace(/^on/i, '').toLowerCase(); 263 | var args = Array.prototype.slice.call(arguments, 1); 264 | 265 | if (this.hooks[name] && this.hooks[name].length) { 266 | for (var idx = 0, len = this.hooks[name].length; idx < len; idx++) { 267 | var hook = this.hooks[name][idx]; 268 | 269 | if (typeof(hook) == 'function') { 270 | // callback is function reference, call directly 271 | hook.apply(this, args); 272 | } 273 | else if ((typeof(hook) == 'object') && (hook.length == 2)) { 274 | // callback is PHP-style object instance method 275 | hook[0][hook[1]].apply(hook[0], args); 276 | } 277 | else if (window[hook]) { 278 | // callback is global function name 279 | window[ hook ].apply(window, args); 280 | } 281 | } // loop 282 | return true; 283 | } 284 | else if (name == 'error') { 285 | // default error handler if no custom one specified 286 | alert("Webcam.js Error: " + args[0]); 287 | } 288 | 289 | return false; // no hook defined 290 | }, 291 | 292 | setSWFLocation: function(value) { 293 | // for backward compatibility. 294 | this.set('swfURL', value); 295 | }, 296 | 297 | 298 | detectFlash: function() { 299 | // return true if browser supports flash, false otherwise 300 | // Code snippet borrowed from: https://github.com/swfobject/swfobject 301 | var SHOCKWAVE_FLASH = "Shockwave Flash", 302 | SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", 303 | FLASH_MIME_TYPE = "application/x-shockwave-flash", 304 | win = window, 305 | nav = navigator, 306 | hasFlash = false; 307 | 308 | if (typeof nav.plugins !== "undefined" && typeof nav.plugins[SHOCKWAVE_FLASH] === "object") { 309 | var desc = nav.plugins[SHOCKWAVE_FLASH].description; 310 | if (desc && (typeof nav.mimeTypes !== "undefined" && nav.mimeTypes[FLASH_MIME_TYPE] && nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { 311 | hasFlash = true; 312 | } 313 | } 314 | else if (typeof win.ActiveXObject !== "undefined") { 315 | try { 316 | var ax = new ActiveXObject(SHOCKWAVE_FLASH_AX); 317 | if (ax) { 318 | var ver = ax.GetVariable("$version"); 319 | if (ver) hasFlash = true; 320 | } 321 | } 322 | catch (e) {;} 323 | } 324 | 325 | return hasFlash; 326 | }, 327 | 328 | getSWFHTML: function() { 329 | // Return HTML for embedding flash based webcam capture movie 330 | var html = '', 331 | swfURL = this.params.swfURL; 332 | 333 | // make sure we aren't running locally (flash doesn't work) 334 | if (location.protocol.match(/file/)) { 335 | this.dispatch('error', "Flash does not work from local disk. Please run from a web server."); 336 | return '

ERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.

'; 337 | } 338 | 339 | // make sure we have flash 340 | if (!this.detectFlash()) { 341 | this.dispatch('error', "Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again."); 342 | return '

' + this.params.flashNotDetectedText + '

'; 343 | } 344 | 345 | // set default swfURL if not explicitly set 346 | if (!swfURL) { 347 | // find our script tag, and use that base URL 348 | var base_url = ''; 349 | var scpts = document.getElementsByTagName('script'); 350 | for (var idx = 0, len = scpts.length; idx < len; idx++) { 351 | var src = scpts[idx].getAttribute('src'); 352 | if (src && src.match(/\/webcam(\.min)?\.js/)) { 353 | base_url = src.replace(/\/webcam(\.min)?\.js.*$/, ''); 354 | idx = len; 355 | } 356 | } 357 | if (base_url) swfURL = base_url + '/webcam.swf'; 358 | else swfURL = 'webcam.swf'; 359 | } 360 | 361 | // if this is the user's first visit, set flashvar so flash privacy settings panel is shown first 362 | if (window.localStorage && !localStorage.getItem('visited')) { 363 | this.params.new_user = 1; 364 | localStorage.setItem('visited', 1); 365 | } 366 | 367 | // construct flashvars string 368 | var flashvars = ''; 369 | for (var key in this.params) { 370 | if (flashvars) flashvars += '&'; 371 | flashvars += key + '=' + escape(this.params[key]); 372 | } 373 | 374 | // construct object/embed tag 375 | html += ''; 376 | 377 | return html; 378 | }, 379 | 380 | getMovie: function() { 381 | // get reference to movie object/embed in DOM 382 | if (!this.loaded) return this.dispatch('error', "Flash Movie is not loaded yet"); 383 | var movie = document.getElementById('webcam_movie_obj'); 384 | if (!movie || !movie._snap) movie = document.getElementById('webcam_movie_embed'); 385 | if (!movie) this.dispatch('error', "Cannot locate Flash movie in DOM"); 386 | return movie; 387 | }, 388 | 389 | freeze: function() { 390 | // show preview, freeze camera 391 | var self = this; 392 | var params = this.params; 393 | 394 | // kill preview if already active 395 | if (this.preview_active) this.unfreeze(); 396 | 397 | // determine scale factor 398 | var scaleX = this.params.width / this.params.dest_width; 399 | var scaleY = this.params.height / this.params.dest_height; 400 | 401 | // must unflip container as preview canvas will be pre-flipped 402 | this.unflip(); 403 | 404 | // calc final size of image 405 | var final_width = params.crop_width || params.dest_width; 406 | var final_height = params.crop_height || params.dest_height; 407 | 408 | // create canvas for holding preview 409 | var preview_canvas = document.createElement('canvas'); 410 | preview_canvas.width = final_width; 411 | preview_canvas.height = final_height; 412 | var preview_context = preview_canvas.getContext('2d'); 413 | 414 | // save for later use 415 | this.preview_canvas = preview_canvas; 416 | this.preview_context = preview_context; 417 | 418 | // scale for preview size 419 | if ((scaleX != 1.0) || (scaleY != 1.0)) { 420 | preview_canvas.style.webkitTransformOrigin = '0px 0px'; 421 | preview_canvas.style.mozTransformOrigin = '0px 0px'; 422 | preview_canvas.style.msTransformOrigin = '0px 0px'; 423 | preview_canvas.style.oTransformOrigin = '0px 0px'; 424 | preview_canvas.style.transformOrigin = '0px 0px'; 425 | preview_canvas.style.webkitTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 426 | preview_canvas.style.mozTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 427 | preview_canvas.style.msTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 428 | preview_canvas.style.oTransform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 429 | preview_canvas.style.transform = 'scaleX('+scaleX+') scaleY('+scaleY+')'; 430 | } 431 | 432 | // take snapshot, but fire our own callback 433 | this.snap( function() { 434 | // add preview image to dom, adjust for crop 435 | preview_canvas.style.position = 'relative'; 436 | preview_canvas.style.left = '' + self.container.scrollLeft + 'px'; 437 | preview_canvas.style.top = '' + self.container.scrollTop + 'px'; 438 | 439 | self.container.insertBefore( preview_canvas, self.peg ); 440 | self.container.style.overflow = 'hidden'; 441 | 442 | // set flag for user capture (use preview) 443 | self.preview_active = true; 444 | 445 | }, preview_canvas ); 446 | }, 447 | 448 | unfreeze: function() { 449 | // cancel preview and resume live video feed 450 | if (this.preview_active) { 451 | // remove preview canvas 452 | this.container.removeChild( this.preview_canvas ); 453 | delete this.preview_context; 454 | delete this.preview_canvas; 455 | 456 | // unflag 457 | this.preview_active = false; 458 | 459 | // re-flip if we unflipped before 460 | this.flip(); 461 | } 462 | }, 463 | 464 | flip: function() { 465 | // flip container horiz (mirror mode) if desired 466 | if (this.params.flip_horiz) { 467 | var sty = this.container.style; 468 | sty.webkitTransform = 'scaleX(-1)'; 469 | sty.mozTransform = 'scaleX(-1)'; 470 | sty.msTransform = 'scaleX(-1)'; 471 | sty.oTransform = 'scaleX(-1)'; 472 | sty.transform = 'scaleX(-1)'; 473 | sty.filter = 'FlipH'; 474 | sty.msFilter = 'FlipH'; 475 | } 476 | }, 477 | 478 | unflip: function() { 479 | // unflip container horiz (mirror mode) if desired 480 | if (this.params.flip_horiz) { 481 | var sty = this.container.style; 482 | sty.webkitTransform = 'scaleX(1)'; 483 | sty.mozTransform = 'scaleX(1)'; 484 | sty.msTransform = 'scaleX(1)'; 485 | sty.oTransform = 'scaleX(1)'; 486 | sty.transform = 'scaleX(1)'; 487 | sty.filter = ''; 488 | sty.msFilter = ''; 489 | } 490 | }, 491 | 492 | savePreview: function(user_callback, user_canvas) { 493 | // save preview freeze and fire user callback 494 | var params = this.params; 495 | var canvas = this.preview_canvas; 496 | var context = this.preview_context; 497 | 498 | // render to user canvas if desired 499 | if (user_canvas) { 500 | var user_context = user_canvas.getContext('2d'); 501 | user_context.drawImage( canvas, 0, 0 ); 502 | } 503 | 504 | // fire user callback if desired 505 | user_callback( 506 | user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ), 507 | canvas, 508 | context 509 | ); 510 | 511 | // remove preview 512 | this.unfreeze(); 513 | }, 514 | 515 | snap: function(user_callback, user_canvas) { 516 | // take snapshot and return image data uri 517 | var self = this; 518 | var params = this.params; 519 | 520 | if (!this.loaded) return this.dispatch('error', "Webcam is not loaded yet"); 521 | // if (!this.live) return this.dispatch('error', "Webcam is not live yet"); 522 | if (!user_callback) return this.dispatch('error', "Please provide a callback function or canvas to snap()"); 523 | 524 | // if we have an active preview freeze, use that 525 | if (this.preview_active) { 526 | this.savePreview( user_callback, user_canvas ); 527 | return null; 528 | } 529 | 530 | // create offscreen canvas element to hold pixels 531 | var canvas = document.createElement('canvas'); 532 | canvas.width = this.params.dest_width; 533 | canvas.height = this.params.dest_height; 534 | var context = canvas.getContext('2d'); 535 | 536 | // flip canvas horizontally if desired 537 | if (this.params.flip_horiz) { 538 | context.translate( params.dest_width, 0 ); 539 | context.scale( -1, 1 ); 540 | } 541 | 542 | // create inline function, called after image load (flash) or immediately (native) 543 | var func = function() { 544 | // render image if needed (flash) 545 | if (this.src && this.width && this.height) { 546 | context.drawImage(this, 0, 0, params.dest_width, params.dest_height); 547 | } 548 | 549 | // crop if desired 550 | if (params.crop_width && params.crop_height) { 551 | var crop_canvas = document.createElement('canvas'); 552 | crop_canvas.width = params.crop_width; 553 | crop_canvas.height = params.crop_height; 554 | var crop_context = crop_canvas.getContext('2d'); 555 | 556 | crop_context.drawImage( canvas, 557 | Math.floor( (params.dest_width / 2) - (params.crop_width / 2) ), 558 | Math.floor( (params.dest_height / 2) - (params.crop_height / 2) ), 559 | params.crop_width, 560 | params.crop_height, 561 | 0, 562 | 0, 563 | params.crop_width, 564 | params.crop_height 565 | ); 566 | 567 | // swap canvases 568 | context = crop_context; 569 | canvas = crop_canvas; 570 | } 571 | 572 | // render to user canvas if desired 573 | if (user_canvas) { 574 | var user_context = user_canvas.getContext('2d'); 575 | user_context.drawImage( canvas, 0, 0 ); 576 | } 577 | 578 | // fire user callback if desired 579 | user_callback( 580 | user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100 ), 581 | canvas, 582 | context 583 | ); 584 | }; 585 | 586 | // grab image frame from userMedia or flash movie 587 | if (this.userMedia) { 588 | // native implementation 589 | context.drawImage(this.video, 0, 0, this.params.dest_width, this.params.dest_height); 590 | 591 | // fire callback right away 592 | func(); 593 | } 594 | else { 595 | // flash fallback 596 | var raw_data = this.getMovie()._snap(); 597 | 598 | // render to image, fire callback when complete 599 | var img = new Image(); 600 | img.onload = func; 601 | img.src = 'data:image/'+this.params.image_format+';base64,' + raw_data; 602 | } 603 | 604 | return null; 605 | }, 606 | 607 | configure: function(panel) { 608 | // open flash configuration panel -- specify tab name: 609 | // "camera", "privacy", "default", "localStorage", "microphone", "settingsManager" 610 | if (!panel) panel = "camera"; 611 | this.getMovie()._configure(panel); 612 | }, 613 | 614 | flashNotify: function(type, msg) { 615 | // receive notification from flash about event 616 | switch (type) { 617 | case 'flashLoadComplete': 618 | // movie loaded successfully 619 | this.loaded = true; 620 | this.dispatch('load'); 621 | break; 622 | 623 | case 'cameraLive': 624 | // camera is live and ready to snap 625 | this.live = true; 626 | this.dispatch('live'); 627 | this.flip(); 628 | break; 629 | 630 | case 'error': 631 | // Flash error 632 | this.dispatch('error', msg); 633 | break; 634 | 635 | default: 636 | // catch-all event, just in case 637 | // console.log("webcam flash_notify: " + type + ": " + msg); 638 | break; 639 | } 640 | }, 641 | 642 | b64ToUint6: function(nChr) { 643 | // convert base64 encoded character to 6-bit integer 644 | // from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding 645 | return nChr > 64 && nChr < 91 ? nChr - 65 646 | : nChr > 96 && nChr < 123 ? nChr - 71 647 | : nChr > 47 && nChr < 58 ? nChr + 4 648 | : nChr === 43 ? 62 : nChr === 47 ? 63 : 0; 649 | }, 650 | 651 | base64DecToArr: function(sBase64, nBlocksSize) { 652 | // convert base64 encoded string to Uintarray 653 | // from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding 654 | var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, 655 | nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, 656 | taBytes = new Uint8Array(nOutLen); 657 | 658 | for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { 659 | nMod4 = nInIdx & 3; 660 | nUint24 |= this.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; 661 | if (nMod4 === 3 || nInLen - nInIdx === 1) { 662 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { 663 | taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; 664 | } 665 | nUint24 = 0; 666 | } 667 | } 668 | return taBytes; 669 | }, 670 | 671 | upload: function(image_data_uri, target_url, callback) { 672 | // submit image data to server using binary AJAX 673 | var form_elem_name = this.params.upload_name || 'webcam'; 674 | 675 | // detect image format from within image_data_uri 676 | var image_fmt = ''; 677 | if (image_data_uri.match(/^data\:image\/(\w+)/)) 678 | image_fmt = RegExp.$1; 679 | else 680 | throw "Cannot locate image format in Data URI"; 681 | 682 | // extract raw base64 data from Data URI 683 | var raw_image_data = image_data_uri.replace(/^data\:image\/\w+\;base64\,/, ''); 684 | 685 | // contruct use AJAX object 686 | var http = new XMLHttpRequest(); 687 | http.open("POST", target_url, true); 688 | 689 | // setup progress events 690 | if (http.upload && http.upload.addEventListener) { 691 | http.upload.addEventListener( 'progress', function(e) { 692 | if (e.lengthComputable) { 693 | var progress = e.loaded / e.total; 694 | Webcam.dispatch('uploadProgress', progress, e); 695 | } 696 | }, false ); 697 | } 698 | 699 | // completion handler 700 | var self = this; 701 | http.onload = function() { 702 | if (callback) callback.apply( self, [http.status, http.responseText, http.statusText] ); 703 | Webcam.dispatch('uploadComplete', http.status, http.responseText, http.statusText); 704 | }; 705 | 706 | // create a blob and decode our base64 to binary 707 | var blob = new Blob( [ this.base64DecToArr(raw_image_data) ], {type: 'image/'+image_fmt} ); 708 | 709 | // stuff into a form, so servers can easily receive it as a standard file upload 710 | var form = new FormData(); 711 | form.append( form_elem_name, blob, form_elem_name+"."+image_fmt.replace(/e/, '') ); 712 | 713 | // send data to server 714 | http.send(form); 715 | } 716 | 717 | }; 718 | 719 | Webcam.init(); 720 | 721 | if (typeof define === 'function' && define.amd) { 722 | define( function() { return Webcam; } ); 723 | } 724 | else if (typeof module === 'object' && module.exports) { 725 | module.exports = Webcam; 726 | } 727 | else { 728 | window.Webcam = Webcam; 729 | } 730 | 731 | }(window)); 732 | -------------------------------------------------------------------------------- /gnu_0001.gcode: -------------------------------------------------------------------------------- 1 | M05 S0 2 | 3 | G90 4 | G21 5 | G1 F3000 6 | G1 X31.9278 Y73.97 7 | G4 P0 8 | M03 S256 9 | G4 P0 10 | G1 F100.000000 11 | G3 X30.5159 Y73.5895 I0.2554 J-3.7572 12 | G3 X28.6712 Y71.8353 I1.8968 J-3.8417 13 | G2 X26.8042 Y70.4886 I-2.3743 J1.3241 14 | G2 X25.4271 Y70.5791 I-0.5137 J2.7047 15 | G3 X23.4279 Y70.9451 I-2.3164 J-7.01 16 | G3 X20.7422 Y70.1493 I-0.1851 J-4.305 17 | G2 X18.6105 Y70.0241 I-1.1656 J1.6362 18 | G2 X17.6236 Y72.0052 I0.933 J1.7011 19 | G2 X18.5423 Y74.8973 I9.2669 J-1.3516 20 | G2 X20.7639 Y77.237 I5.0422 J-2.5631 21 | G3 X23.2182 Y81.1184 I-2.4563 J4.2698 22 | G2 X24.5943 Y84.2037 I5.041 J-0.3988 23 | G3 X26.1872 Y87.4303 I-4.3912 J4.1744 24 | G3 X26.4343 Y91.1173 I-20.2701 J3.2105 25 | G2 X16.2813 Y94.5179 I4.5916 J30.5652 26 | G2 X8.0944 Y101.3656 I12.8607 J23.6939 27 | G2 X2.5664 Y111.0016 I28.2507 J22.6105 28 | G2 X0.2252 Y121.6512 I33.9837 J13.0532 29 | G2 X0.6388 Y132.5369 I52.3265 J3.4625 30 | G2 X3.7236 Y142.1349 I29.6776 J-4.2437 31 | G2 X9.768 Y150.1438 I26.8378 J-13.9692 32 | G2 X17.5709 Y155.7247 I24.1679 J-25.5449 33 | G2 X27.7287 Y159.1913 I16.1641 J-30.7487 34 | G2 X36.3725 Y159.8706 I7.873 J-44.8495 35 | G2 X44.5641 Y159.1728 I-1.0197 J-60.4048 36 | G2 X49.5404 Y157.4361 I-2.2115 J-14.3349 37 | G2 X54.9282 Y154.0517 I-37.2304 J-65.2485 38 | G3 X57.8694 Y153.1223 I2.9878 J4.3371 39 | G3 X60.5924 Y153.5495 I0.0745 J8.4187 40 | G3 X62.0832 Y154.3387 I-1.5973 J4.8197 41 | G2 X58.1786 Y148.5238 I-5.9343 J-0.2337 42 | G2 X51.4099 Y147.2394 I-7.3319 J20.1619 43 | G2 X44.6219 Y148.0943 I-0.5938 J22.6631 44 | G3 X38.3286 Y148.4445 I-3.9256 J-13.8211 45 | G3 X30.8345 Y146.4179 I6.4998 J-38.9043 46 | G3 X22.0068 Y138.8913 I6.1133 J-16.1102 47 | G3 X18.8916 Y130.7677 I24.3899 J-14.0121 48 | G3 X18.4026 Y124.4302 I23.2811 J-4.9842 49 | G3 X22.1555 Y116.3599 I12.0259 J0.6846 50 | G3 X26.1671 Y114.7254 I4.1115 J4.351 51 | G3 X29.0518 Y115.3954 I0.102 J6.1063 52 | G3 X31.8073 Y116.9934 I-13.85 J27.0575 53 | G3 X38.4073 Y123.5783 I-12.4342 J19.0628 54 | G2 X50.2697 Y136.7365 I48.4878 J-31.7863 55 | G2 X56.7406 Y140.2075 I12.1925 J-14.963 56 | G2 X62.78 Y141.1612 I6.3004 J-20.2987 57 | G2 X67.6773 Y140.7888 I0.3424 J-27.8871 58 | G2 X71.7852 Y139.6231 I-3.1072 J-18.7696 59 | G2 X76.4443 Y136.6952 I-6.0905 J-14.8629 60 | G3 X80.3114 Y133.8965 I14.9973 J16.6512 61 | G3 X82.2061 Y135.414 I-4.3097 J7.3224 62 | G2 X83.9964 Y136.9998 I9.3279 J-8.7273 63 | G2 X90.2367 Y140.1647 I12.8151 J-17.5339 64 | G2 X96.8954 Y140.8683 I5.0447 J-15.8817 65 | G2 X102.1464 Y139.4748 I-1.6276 J-16.7239 66 | G2 X106.6998 Y136.5644 I-7.5534 J-16.8343 67 | G2 X112.9065 Y129.8507 I-27.5925 J-31.7348 68 | G2 X117.5698 Y123.4896 I-681.0399 J-504.1634 69 | G3 X121.3047 Y118.7496 I61.8831 J44.9199 70 | G3 X126.3088 Y115.4654 I8.145 J6.956 71 | G3 X132.1647 Y117.5993 I1.5155 J4.9431 72 | G3 X134.0519 Y122.5602 I-10.6418 J6.8876 73 | G2 X134.436 Y124.9654 I208.9384 J-32.1336 74 | G3 X134.6544 Y127.597 I-16.0396 J2.6563 75 | G3 X134.5118 Y130.538 I-29.4662 J0.0447 76 | G3 X133.9302 Y133.6603 I-18.4649 J-1.8238 77 | G3 X132.8608 Y136.5732 I-18.2502 J-5.0475 78 | G3 X131.4783 Y139.1337 I-21.5849 J-10.001 79 | G3 X128.4284 Y142.9789 I-18.2729 J-11.3614 80 | G3 X124.4476 Y145.8706 I-11.0828 J-11.071 81 | G3 X121.3291 Y147.2681 I-15.754 J-30.9772 82 | G3 X116.9605 Y148.14 I-4.6482 J-11.9097 83 | G3 X113.0352 Y147.6751 I-0.3078 J-14.2041 84 | G2 X109.4023 Y147.0786 I-4.956 J18.8228 85 | G3 X103.6108 Y146.6276 I20.6068 J-302.0886 86 | G2 X98.1614 Y148.5884 I-0.6021 J6.8796 87 | G2 X96.7273 Y150.7927 I4.3077 J4.3713 88 | G2 X96.1246 Y153.1875 I10.125 J3.8213 89 | G3 X97.3336 Y152.4755 I2.8549 J3.465 90 | G3 X98.653 Y152.1667 I1.7136 J4.3487 91 | G3 X101.5553 Y152.9421 I0.3734 J4.4216 92 | G2 X104.0621 Y154.5428 I22.2326 J-32.0556 93 | G3 X106.2173 Y155.9118 I-18.0846 J30.8494 94 | G2 X108.9513 Y157.3785 I8.0526 J-11.7291 95 | G2 X116.1489 Y158.8921 I7.8551 J-19.4836 96 | G2 X123.4801 Y158.633 I1.7242 J-55.0637 97 | G2 X129.6094 Y157.3 I-2.8468 J-27.8463 98 | G2 X135.36 Y154.7271 I-10.7318 J-31.6994 99 | G2 X140.1533 Y151.169 I-11.9084 J-21.0505 100 | G2 X144.1656 Y146.7484 I-28.8524 J-30.2184 101 | G2 X147.596 Y141.2787 I-24.1639 J-18.9653 102 | G2 X149.757 Y135.1595 I-25.317 J-12.3818 103 | G2 X151.0984 Y124.7514 I-53.3018 J-12.1601 104 | G2 X149.8496 Y114.4249 I-34.2411 J-1.0979 105 | G2 X146.3769 Y106.4019 I-31.1112 J8.7031 106 | G2 X140.6155 Y99.2337 I-29.7105 J17.9801 107 | G2 X134.7256 Y95.3025 I-14.2267 J14.9372 108 | G2 X128.1255 Y93.2418 I-12.3449 J27.94 109 | G2 X128.3394 Y91.8076 I-16.3387 J-3.1692 110 | G3 X128.5858 Y90.6146 I6.1102 J0.6404 111 | G3 X129.3538 Y89.1706 I4.2325 J1.3248 112 | G3 X130.9644 Y88.6571 I1.1905 J0.9514 113 | G2 X131.585 Y88.7174 I0.4736 J-1.6528 114 | G2 X132.5189 Y88.3202 I-0.1394 J-1.6243 115 | G2 X132.9582 Y86.9984 I-0.906 J-1.035 116 | G2 X130.843 Y84.176 I-4.1092 J0.8757 117 | G2 X129.345 Y83.6701 I-2.2324 J4.1396 118 | G2 X127.0976 Y83.471 I-2.5766 J16.2998 119 | G2 X124.2411 Y83.4563 I-1.924 J96.4011 120 | G2 X121.1165 Y83.7431 I0.1856 J19.1892 121 | G2 X118.8265 Y84.7113 I0.9531 J5.4464 122 | G3 X116.3001 Y85.689 I-3.1194 J-4.3074 123 | G2 X112.1097 Y87.3157 I0.976 J8.725 124 | G3 X108.4749 Y89.8066 I-45.7189 J-62.8148 125 | G3 X107.2288 Y90.4593 I-4.2337 J-6.5678 126 | G2 X106.5236 Y90.8029 I2.4554 J5.9343 127 | G3 X107.0497 Y89.3069 I33.6131 J10.9799 128 | G2 X107.4756 Y87.887 I-10.0313 J-3.7824 129 | G2 X107.6291 Y86.4673 I-6.0674 J-1.3744 130 | G3 X107.7093 Y85.3391 I7.2419 J-0.0527 131 | G3 X107.9396 Y84.2729 I8.3923 J1.255 132 | G2 X108.1492 Y83.4044 I-10.6911 J-3.0405 133 | G2 X108.7313 Y82.7265 I-2.2673 J-2.5356 134 | G3 X109.6137 Y82.0949 I1.2508 J0.8154 135 | G3 X111.0778 Y82.0667 I0.7923 J3.1177 136 | G2 X112.559 Y82.3241 I4.0927 J-19.1659 137 | G3 X113.7118 Y82.6583 I-0.5403 J4.0185 138 | G2 X114.62 Y82.7691 I0.6274 J-1.3653 139 | G2 X115.5679 Y82.381 I-0.4775 J-2.5178 140 | G2 X116.4562 Y81.73 I-7.1787 J-10.7256 141 | G3 X117.1121 Y81.209 I67.0731 J83.7664 142 | G2 X117.9302 Y80.1196 I-1.6446 J-2.0871 143 | G2 X118.5411 Y78.0169 I-9.947 J-4.0297 144 | G2 X118.8486 Y75.2459 I-19.6063 J-3.5784 145 | G2 X118.7487 Y73.0081 I-13.1657 J-0.5338 146 | G2 X118.2929 Y71.1735 I-8.2498 J1.0759 147 | G2 X117.5715 Y69.8145 I-5.5334 J2.0664 148 | G2 X116.2397 Y68.2564 I-9.4336 J6.7152 149 | G2 X114.1693 Y66.3616 I-28.9732 J29.5792 150 | G2 X111.0841 Y64.1134 I-16.013 J18.7339 151 | G2 X111.5344 Y63.3713 I-7.4185 J-5.0093 152 | G3 X112.1732 Y62.5019 I3.4785 J1.8864 153 | G3 X113.3281 Y61.5899 I4.0067 J3.8865 154 | G2 X114.4425 Y60.6505 I-2.2786 J-3.8341 155 | G2 X115.0296 Y59.7675 I-3.3965 J-2.8946 156 | G2 X115.2401 Y58.9047 I-1.7515 J-0.8844 157 | G2 X115.1772 Y57.8538 I-7.3587 J-0.0874 158 | G2 X114.6548 Y56.5671 I-2.7353 J0.3612 159 | G2 X112.291 Y54.6709 I-4.8263 J3.5952 160 | G2 X109.6055 Y53.4793 I-153.488 J342.2709 161 | G2 X109.9449 Y50.6302 I-35.2244 J-5.641 162 | G2 X109.7305 Y47.766 I-9.3912 J-0.7373 163 | G2 X109.3366 Y46.079 I-675.5423 J156.87 164 | G2 X108.0327 Y44.4679 I-2.3206 J0.545 165 | G2 X106.0084 Y44.0983 I-1.7071 J3.6212 166 | G3 X103.6467 Y44.0059 I-0.7927 J-9.9734 167 | G3 X101.9531 Y43.6843 I4.5129 J-28.3808 168 | G2 X100.6283 Y43.4062 I-13.5423 J61.2237 169 | G2 X99.5242 Y41.9705 I-14.7878 J10.2301 170 | G3 X98.795 Y40.7556 I3.2743 J-2.7914 171 | G3 X98.6528 Y39.8175 I1.9759 J-0.7793 172 | G3 X98.839 Y38.8706 I4.0426 J0.3032 173 | G3 X99.5136 Y37.5141 I4.8873 J1.5842 174 | G2 X100.2467 Y35.9369 I-4.1127 J-2.8709 175 | G2 X100.4647 Y34.479 I-6.344 J-1.6937 176 | G2 X100.3128 Y33.0204 I-5.2617 J-0.1891 177 | G2 X99.8153 Y31.799 I-4.4989 J1.1204 178 | G2 X98.8624 Y30.5383 I-5.8071 J3.3985 179 | G2 X97.2094 Y29.3496 I-4.0408 J3.8759 180 | G3 X95.6683 Y28.3488 I2.9026 J-6.1565 181 | G2 X94.7083 Y27.5527 I-21.5807 J25.0454 182 | G3 X94.3012 Y26.4514 I0.7085 J-0.8879 183 | G3 X95.6713 Y23.6951 I5.6262 J1.078 184 | G3 X98.5119 Y21.7773 I5.2783 J4.7552 185 | G3 X100.0844 Y21.5636 I1.2743 J3.486 186 | G3 X102.0132 Y22.0355 I-0.5022 J6.2312 187 | G2 X101.2364 Y20.2628 I-2.8012 J0.1709 188 | G3 X100.0331 Y18.9322 I21.6742 J-20.8092 189 | G3 X103.0091 Y20.1925 I-3.6478 J12.7577 190 | G3 X105.3993 Y22.1004 I-5.0747 J8.8085 191 | G2 X104.4225 Y20.213 I-6.3838 J2.1073 192 | G2 X101.3463 Y16.9583 I-14.9299 J11.03 193 | G2 X99.15 Y15.4461 I-9.5084 J11.4586 194 | G3 X98.1024 Y14.8041 I8.2755 J-14.6797 195 | G3 X101.1123 Y14.5483 I2.6267 J13.0718 196 | G3 X103.4392 Y14.7894 I-0.4517 J15.7074 197 | G2 X102.1124 Y14.1592 I-5.8661 J10.6393 198 | G2 X99.4083 Y13.4867 I-3.7532 J9.318 199 | G2 X97.012 Y13.4682 I-1.2932 J12.3328 200 | G2 X94.8272 Y13.829 I1.3217 J14.7994 201 | G2 X92.5627 Y14.9198 I1.4202 J5.8446 202 | G3 X89.7032 Y16.7458 I-11.4282 J-14.7442 203 | G3 X90.0136 Y15.6884 I18.573 J4.8779 204 | G3 X90.8702 Y14.3641 I2.9182 J0.9484 205 | G3 X92.2753 Y13.4782 I3.2199 J3.5495 206 | G2 X94.3141 Y12.565 I-12.9227 J-31.5836 207 | G3 X91.7079 Y12.8096 I-7.6056 J-67.0137 208 | G2 X89.1244 Y13.1884 I1.3571 J18.257 209 | G2 X87.4285 Y13.6648 I3.3486 J15.1771 210 | G2 X86.1328 Y14.2052 I3.528 J10.2815 211 | G2 X84.8741 Y15.0072 I3.0953 J6.2469 212 | G3 X83.7136 Y15.8684 I-12.4857 J-15.6135 213 | G2 X84.1855 Y14.7353 I-6.2139 J-3.2531 214 | G3 X84.8721 Y13.6538 I2.3881 J0.7572 215 | G3 X86.062 Y12.9599 I2.1558 J2.3298 216 | G2 X87.4629 Y12.4007 I-3.2549 J-10.1885 217 | G3 X86.1457 Y12.3536 I-0.2507 J-11.4417 218 | G2 X84.7592 Y12.46 I-0.3833 J4.095 219 | G2 X83.4595 Y12.9122 I1.8952 J7.5399 220 | G2 X81.9745 Y13.7808 I3.3496 J7.4309 221 | G2 X80.5222 Y14.9544 I12.3892 J16.8167 222 | G2 X79.1698 Y16.2807 I12.2436 J13.8371 223 | G2 X77.7694 Y17.8742 I34.6692 J31.8804 224 | G2 X77.0069 Y19.1828 I3.411 J2.8641 225 | G2 X77.0285 Y20.3437 I1.4818 J0.5531 226 | G3 X77.2863 Y21.302 I-3.4905 J1.4529 227 | G2 X76.3832 Y20.8039 I-3.6701 J5.5864 228 | G3 X75.5692 Y20.1725 I0.9239 J-2.0316 229 | G3 X75.0794 Y19.3898 I3.0285 J-2.44 230 | G3 X74.9339 Y18.1758 I1.7529 J-0.8257 231 | G3 X75.5456 Y16.7207 I3.9729 J0.8139 232 | G2 X76.6287 Y15.0014 I-26.6293 J-17.976 233 | G2 X74.4107 Y16.9794 I21.992 J26.8933 234 | G2 X73.2988 Y18.9293 I2.8154 J2.8974 235 | G2 X73.6024 Y21.4497 I3.5348 J0.8525 236 | G2 X74.766 Y23.4898 I24.9441 J-12.8752 237 | G2 X75.4767 Y24.439 I6.1033 J-3.8286 238 | G3 X76.1498 Y25.3516 I-4.7458 J4.2048 239 | G3 X74.8507 Y26.5357 I-17.3902 J-17.7736 240 | G2 X73.6377 Y27.8095 I5.378 J6.336 241 | G2 X72.7648 Y29.3792 I4.8486 J3.7236 242 | G3 X71.8865 Y30.9018 I-5.0622 J-1.9057 243 | G3 X70.636 Y32.0642 I-4.891 J-4.0079 244 | G2 X69.3101 Y33.0907 I10.6527 J15.1291 245 | G3 X70.7673 Y30.3066 I30.646 J14.2662 246 | G2 X71.5735 Y28.2283 I-6.0935 J-3.5595 247 | G2 X71.3815 Y26.5245 I-2.6373 J-0.5656 248 | G2 X70.8268 Y25.5294 I-7.0872 J3.2991 249 | G2 X69.9842 Y24.8128 I-1.6476 J1.0837 250 | G3 X68.9813 Y24.2854 I3.6289 J-8.1162 251 | G2 X67.7702 Y24.0179 I-0.9947 J1.6286 252 | G3 X66.8336 Y23.5795 I-0.1119 J-0.9805 253 | G3 X66.4507 Y22.4923 I2.003 J-1.3164 254 | G3 X66.7069 Y21.4211 I1.6893 J-0.1623 255 | G1 X68.2986 Y18.9087 256 | G2 X67.2477 Y19.6075 I2.3888 J4.7321 257 | G2 X66.2463 Y20.6766 I4.2555 J4.9899 258 | G3 X65.5014 Y21.5749 I-9.0854 J-6.7762 259 | G2 X64.9963 Y22.6773 I1.4434 J1.3281 260 | G2 X64.9728 Y23.8201 I5.9974 J0.6951 261 | G2 X65.3889 Y24.9548 I2.2016 J-0.1637 262 | G2 X66.6812 Y25.8098 I1.7919 J-1.3042 263 | G3 X68.0443 Y26.5014 I-0.7188 J3.1061 264 | G3 X68.7838 Y27.3155 I-2.9546 J3.4267 265 | G3 X69.0301 Y28.1654 I-1.1037 J0.7805 266 | G3 X68.5821 Y29.118 I-1.387 J-0.0707 267 | G3 X67.6656 Y29.8793 I-8.182 J-8.9172 268 | G3 X64.6146 Y31.9655 I-25.3502 J-33.7985 269 | G2 X61.799 Y34.2721 I7.391 J11.8939 270 | G2 X60.6705 Y35.7665 I6.376 J5.9886 271 | G2 X60.2886 Y36.6614 I3.0728 J1.8402 272 | G2 X60.2899 Y37.5279 I1.5763 J0.4308 273 | G2 X60.7455 Y38.6476 I5.012 J-1.3866 274 | G2 X61.9699 Y40.0084 I3.5188 J-1.9349 275 | G3 X62.9093 Y41.4256 I-1.3186 J1.8941 276 | G3 X62.5227 Y42.3311 I-0.8392 J0.177 277 | G3 X61.6241 Y42.8076 I-3.929 J-6.3239 278 | G3 X60.3006 Y42.9022 I-0.7922 J-1.7777 279 | G2 X58.9531 Y43.1892 I-0.4023 J1.418 280 | G2 X58.6983 Y44.2627 I0.5914 J0.7074 281 | G3 X58.4138 Y45.4904 I-0.9737 J0.4212 282 | G3 X56.9022 Y45.9492 I-1.2799 J-1.4971 283 | G2 X55.1536 Y46.4364 I-0.2811 J2.3729 284 | G2 X54.1846 Y47.5912 I2.2096 J2.838 285 | G2 X53.6389 Y48.9129 I6.6312 J3.5114 286 | G2 X53.3705 Y50.5061 I5.9933 J1.8286 287 | G2 X53.5096 Y51.6567 I3.6836 J0.1385 288 | G2 X54.1038 Y52.4113 I1.2181 J-0.3478 289 | G3 X54.68 Y52.8499 I-1.3195 J2.3314 290 | G3 X55.0038 Y53.3454 I-1.0251 J1.0236 291 | G3 X54.875 Y54.0626 I-0.6483 J0.2538 292 | G3 X53.7675 Y54.3799 I-0.8051 J-0.7187 293 | G2 X51.8809 Y54.7312 I-0.5959 J2.0417 294 | G2 X51.2784 Y56.3522 I0.9835 J1.288 295 | G3 X50.8785 Y57.8105 I-1.6193 J0.3399 296 | G3 X49.525 Y58.3287 I-1.1955 J-1.0961 297 | G2 X48.4567 Y58.4656 I-0.2351 J2.4019 298 | G3 X47.93 Y58.5827 I-0.6978 J-1.896 299 | G2 X47.2276 Y59.1536 I0.0681 J0.8012 300 | G2 X46.8535 Y62.1726 I8.7689 J2.6195 301 | G3 X46.3358 Y65.5629 I-8.8334 J0.3859 302 | G3 X45.339 Y66.3083 I-1.0648 J-0.3847 303 | G2 X44.3758 Y67.0488 I0.065 J1.0813 304 | G2 X44.1291 Y69.3589 I5.1986 J1.7234 305 | G3 X44.1441 Y72.7658 I-16.4823 J1.776 306 | G1 X43.2029 Y78.4828 307 | G2 X38.6794 Y76.1684 I-11.9729 J17.8237 308 | G2 X34.2245 Y74.9615 I-8.935 J24.154 309 | G3 X34.1122 Y64.0507 I229.1533 J-7.8153 310 | G3 X36.0488 Y51.6725 I44.3546 J0.5989 311 | G3 X42.4423 Y39.8684 I32.3869 J9.9085 312 | G3 X52.9048 Y28.3471 I144.4171 J120.6333 313 | G2 X40.0814 Y40.6721 I43.6013 J58.1978 314 | G2 X34.159 Y50.9164 I29.0963 J23.655 315 | G2 X31.8215 Y62.859 I33.1129 J12.6814 316 | G2 X31.9278 Y73.97 I182.6939 J3.8075 317 | G1 X31.9278 Y73.97 318 | G4 P0 319 | M05 S0 320 | G1 F3000 321 | G1 X65.3161 Y74.101 322 | G4 P0 323 | M03 S256 324 | G4 P0 325 | G1 F100.000000 326 | G2 X66.8516 Y74.4802 I1.1723 J-1.448 327 | G3 X69.5695 Y74.1142 I4.2887 J21.5725 328 | G1 X66.3318 Y73.2851 329 | G3 X66.2367 Y72.7208 I2.763 J-0.7561 330 | G2 X66.0304 Y72.1841 I-0.9707 J0.0651 331 | G2 X65.5442 Y71.826 I-0.8928 J0.703 332 | G3 X64.958 Y71.5406 I1.3409 J-3.4998 333 | G3 X64.8748 Y72.9578 I-7.4662 J0.273 334 | G2 X65.3161 Y74.101 I1.2141 J0.1881 335 | G1 X65.3161 Y74.101 336 | G4 P0 337 | M05 S0 338 | G1 F3000 339 | G1 X77.8273 Y79.155 340 | G4 P0 341 | M03 S256 342 | G4 P0 343 | G1 F100.000000 344 | G2 X78.5538 Y81.9339 I6.9922 J-0.3437 345 | G2 X80.5349 Y83.9926 I4.2731 J-2.1295 346 | G2 X82.1653 Y84.7821 I8.7689 J-16.0308 347 | G2 X84.3356 Y85.3574 I3.0942 J-7.2907 348 | G2 X86.8631 Y85.4522 I1.8606 J-15.8612 349 | G2 X90.7313 Y84.5263 I-0.4326 J-10.3499 350 | G2 X92.7074 Y83.2954 I-3.7612 J-8.2401 351 | G3 X94.488 Y82.0509 I9.0699 J11.0807 352 | G2 X96.2254 Y81.0174 I-228.6815 J-386.4396 353 | G2 X97.9204 Y79.9221 I-14.8436 J-24.8279 354 | G2 X100.4633 Y77.7553 I-9.8533 J-14.1391 355 | G3 X102.2138 Y76.2978 I7.5657 J7.3069 356 | G3 X103.2632 Y75.9353 I1.2262 J1.8497 357 | G2 X104.5588 Y75.6888 I-0.4862 J-6.0819 358 | G2 X103.5048 Y75.4354 I-2.9249 J9.8511 359 | G2 X102.364 Y75.5076 I-0.4269 J2.3057 360 | G2 X100.8804 Y76.2065 I1.9147 J5.9884 361 | G2 X99.5962 Y77.1552 I6.6225 J10.3078 362 | G3 X98.0569 Y78.3753 I-20.3518 J-24.0945 363 | G3 X96.4763 Y79.4194 I-10.2159 J-13.7469 364 | G3 X95.0979 Y80.1268 I-7.3056 J-12.5395 365 | G2 X93.5586 Y80.9663 I4.5611 J10.1936 366 | G2 X91.92 Y82.1685 I10.0227 J15.3785 367 | G3 X90.3924 Y83.164 I-5.2353 J-6.3644 368 | G3 X88.3962 Y83.8113 I-3.1 J-6.1585 369 | G3 X86.2979 Y83.9494 I-1.7736 J-10.9357 370 | G3 X83.5891 Y83.6936 I0.6114 J-20.9426 371 | G3 X81.1287 Y82.5112 I0.7594 J-4.7312 372 | G3 X79.9012 Y80.7848 I3.3275 J-3.6655 373 | G3 X79.7049 Y78.9518 I2.9097 J-1.2387 374 | G3 X80.3431 Y79.8397 I-4.5067 J3.9125 375 | G2 X80.9442 Y80.5571 I2.4419 J-1.4355 376 | G2 X82.5161 Y81.2602 I1.9799 J-2.3178 377 | G2 X84.2145 Y81.2643 I0.8649 J-6.4048 378 | G2 X85.7514 Y80.5129 I-0.3456 J-2.6543 379 | G2 X86.6868 Y79.0303 I-2.7278 J-2.7573 380 | G2 X86.1391 Y79.4632 I5.8422 J7.9544 381 | G3 X85.4013 Y79.8837 I-1.485 J-1.748 382 | G3 X84.0071 Y80.0374 I-1.0369 J-3.0052 383 | G3 X82.6524 Y79.6147 I0.4227 J-3.7366 384 | G3 X81.8401 Y78.4045 I0.7359 J-1.3717 385 | G2 X81.6681 Y77.1159 I-22.3743 J2.3322 386 | G2 X81.4959 Y76.4662 I-3.3776 J0.5474 387 | G3 X81.2961 Y75.8708 I8.7278 J-3.2597 388 | G2 X80.6933 Y76.0964 I0.8289 J3.1322 389 | G2 X80.1579 Y76.4781 I0.9026 J1.8324 390 | G2 X80.1507 Y75.8236 I-8.2805 J-0.2359 391 | G3 X80.1988 Y75.0814 I3.2268 J-0.1633 392 | G3 X80.4202 Y74.2668 I4.8257 J0.8741 393 | G2 X80.7235 Y73.3632 I-16.1464 J-5.9219 394 | G2 X79.5776 Y74.5392 I45.339 J45.3227 395 | G2 X78.6254 Y75.8967 I4.0789 J3.8738 396 | G2 X78.0113 Y77.4951 I7.8915 J3.9491 397 | G2 X77.8273 Y79.155 I5.0765 J1.403 398 | G1 X77.8273 Y79.155 399 | G4 P0 400 | M05 S0 401 | G1 F3000 402 | G1 X88.0076 Y77.4391 403 | G4 P0 404 | M03 S256 405 | G4 P0 406 | G1 F100.000000 407 | G2 X89.347 Y77.2599 I-0.3436 J-7.663 408 | G2 X90.3763 Y76.8264 I-0.6942 J-3.0871 409 | G3 X91.2239 Y76.2953 I16.949 J26.1082 410 | G2 X92.0696 Y75.6267 I-2.2307 J-3.6906 411 | G2 X93.7299 Y73.4631 I-8.0109 J-7.8665 412 | G2 X94.7578 Y70.9028 I-7.8971 J-4.6568 413 | G2 X95.0288 Y69.0434 I-10.6499 J-2.5015 414 | G3 X95.1707 Y66.8865 I154.4579 J9.0801 415 | G2 X94.6226 Y68.8113 I41.2473 J12.7859 416 | G3 X93.9602 Y70.5921 I-9.0433 J-2.35 417 | G3 X92.2371 Y73.1252 I-9.8878 J-4.8734 418 | G3 X89.7385 Y74.9129 I-5.3818 J-4.882 419 | G3 X88.8521 Y75.1083 I-0.9463 J-2.1852 420 | G3 X88.383 Y75.0817 I-0.0724 J-2.8755 421 | G3 X88.6234 Y74.7291 I2.9875 J1.7791 422 | G2 X88.7964 Y74.2474 I-0.6524 J-0.5062 423 | G2 X88.7373 Y73.6515 I-2.3338 J-0.0694 424 | G2 X88.0974 Y72.8493 I-1.1591 J0.2683 425 | G2 X86.5229 Y72.6868 I-1.0106 J2.0828 426 | G2 X85.1803 Y73.3943 I0.7808 J3.1095 427 | G2 X84.4062 Y74.7016 I1.636 J1.8516 428 | G2 X84.5366 Y76.2794 I2.5751 J0.5816 429 | G2 X86.0886 Y77.4096 I1.6887 J-0.6882 430 | G2 X88.0076 Y77.4391 I1.2061 J-16.0484 431 | G1 X88.0076 Y77.4391 432 | G4 P0 433 | M05 S0 434 | G1 F3000 435 | G1 X79.1876 Y94.2426 436 | G4 P0 437 | M03 S256 438 | G4 P0 439 | G1 F100.000000 440 | G3 X79.8222 Y95.7464 I-3.5178 J2.3702 441 | G2 X80.2939 Y97.4941 I15.2985 J-3.192 442 | G3 X80.2358 Y95.359 I13.711 J-1.4415 443 | G2 X79.7625 Y93.6581 I-2.7881 J-0.1404 444 | G2 X78.411 Y92.8468 I-1.5443 J1.041 445 | G3 X76.9696 Y92.5606 I0.8252 J-7.9287 446 | G2 X78.1103 Y93.283 I4.7199 J-6.1907 447 | G3 X79.1876 Y94.2426 I-1.3407 J2.5896 448 | G1 X79.1876 Y94.2426 449 | G4 P0 450 | M05 S0 451 | G1 F3000 452 | G1 X71.0706 Y104.1438 453 | G4 P0 454 | M03 S256 455 | G4 P0 456 | G1 F100.000000 457 | G2 X72.4559 Y104.0987 I0.5055 J-5.7673 458 | G3 X73.7517 Y104.0366 I0.9436 J6.1375 459 | G3 X74.8415 Y104.2048 I-0.3249 J5.7188 460 | G2 X75.7826 Y104.4114 I3.5548 J-13.9476 461 | G2 X75.6767 Y106.6052 I19.1962 J2.0262 462 | G3 X74.6777 Y108.5818 I-2.4135 J0.021 463 | G3 X72.4708 Y109.0294 I-1.5967 J-2.208 464 | G2 X70.2759 Y108.5405 I-37.7453 J164.2973 465 | G3 X67.5187 Y107.619 I2.7849 J-12.917 466 | G2 X64.7909 Y106.464 I-19.2572 J41.6813 467 | G2 X67.1456 Y108.0351 I37.9472 J-54.3237 468 | G2 X69.7636 Y109.2342 I5.6423 J-8.8607 469 | G2 X72.2834 Y109.8331 I6.3482 J-21.1114 470 | G2 X74.8733 Y109.7174 I1.0354 J-5.8713 471 | G2 X75.4352 Y109.3899 I-0.3243 J-1.2023 472 | G2 X75.8706 Y108.873 I-3.0507 J-3.0115 473 | G2 X76.8825 Y106.5035 I-4.8433 J-3.469 474 | G2 X77.3153 Y104.0103 I-90.6614 J-17.0249 475 | G3 X77.7598 Y103.3947 I5.8374 J3.7461 476 | G2 X78.1586 Y102.7773 I-2.6873 J-2.1735 477 | G2 X78.4416 Y101.9571 I-2.9047 J-1.4611 478 | G2 X78.5963 Y100.9581 I-11.086 J-2.2288 479 | G2 X78.3174 Y101.5391 I1.7071 J1.1771 480 | G3 X77.6461 Y102.2665 I-1.0812 J-0.3245 481 | G3 X76.242 Y102.4147 I-0.9737 J-2.4998 482 | G2 X74.4118 Y102.1705 I-5.1972 J31.9773 483 | G2 X75.2524 Y101.2405 I-1.7525 J-2.4287 484 | G2 X75.6691 Y99.7609 I-2.695 J-1.5576 485 | G2 X74.8786 Y97.8865 I-2.4708 J-0.0618 486 | G2 X73.167 Y97.1376 I-1.9311 J2.0831 487 | G2 X71.0839 Y97.5377 I-0.3108 J4.0049 488 | G2 X69.7103 Y98.932 I1.3098 J2.6642 489 | G2 X69.543 Y100.0439 I1.7617 J0.8337 490 | G3 X69.596 Y101.0738 I-5.5674 J0.803 491 | G3 X67.5768 Y100.6463 I0.516 J-7.4201 492 | G3 X65.887 Y99.6757 I1.9226 J-5.303 493 | G3 X67.1006 Y98.1657 I10.2597 J7.0031 494 | G3 X68.7078 Y96.9395 I4.5076 J4.242 495 | G3 X69.9433 Y96.3565 I6.7989 J12.808 496 | G3 X71.3929 Y95.9379 I2.5756 J6.1995 497 | G3 X73.171 Y95.7078 I3.7812 J22.2294 498 | G3 X75.0361 Y95.7669 I0.6887 J7.7223 499 | G3 X76.3206 Y96.1712 I-0.6718 J4.3773 500 | G3 X77.4296 Y96.933 I-2.0655 J4.1947 501 | G3 X78.1925 Y98.2339 I-1.766 J1.9097 502 | G2 X78.5695 Y99.4674 I10.3864 J-2.5006 503 | G2 X78.4329 Y98.1536 I-41.8174 J3.6866 504 | G2 X78.1248 Y97.1441 I-2.9788 J0.3574 505 | G2 X76.313 Y95.4318 I-3.3241 J1.7024 506 | G2 X73.9304 Y94.9239 I-2.4011 J5.4218 507 | G2 X71.1403 Y95.1808 I-0.048 J14.7572 508 | G2 X68.4383 Y95.987 I2.5459 J13.4631 509 | G2 X66.0984 Y97.2883 I4.4811 J10.8124 510 | G2 X64.503 Y99.3505 I2.6551 J3.7023 511 | G2 X64.3258 Y100.7969 I2.7863 J1.0755 512 | G3 X64.3944 Y102.3525 I-9.6157 J1.2033 513 | G2 X66.4781 Y103.199 I3.1194 J-4.6909 514 | G2 X68.7352 Y103.5957 I19.3326 J-103.3729 515 | G3 X69.9329 Y103.9001 I-1.1756 J7.1359 516 | G2 X71.0706 Y104.1438 I1.5523 J-4.4683 517 | G1 X71.0706 Y104.1438 518 | G4 P0 519 | M05 S0 520 | G1 F3000 521 | G1 X75.8969 Y115.9319 522 | G4 P0 523 | M03 S256 524 | G4 P0 525 | G1 F100.000000 526 | G3 X77.4407 Y115.119 I44.7705 J83.1526 527 | G3 X78.7225 Y114.7915 I1.3557 J2.6333 528 | G3 X79.617 Y115.1507 I0.0304 J1.2178 529 | G2 X80.187 Y115.6192 I2.608 J-2.5912 530 | G3 X81.3979 Y111.9435 I89.522 J27.4541 531 | G3 X83.008 Y108.4375 I19.8819 J7.0084 532 | G3 X83.675 Y107.7324 I1.8033 J1.0378 533 | G2 X84.2614 Y107.0388 I-0.8639 J-1.325 534 | G2 X84.7332 Y105.2928 I-5.6877 J-2.4735 535 | G3 X85.2111 Y103.1038 I11.4404 J1.3511 536 | G3 X85.8616 Y101.4915 I11.7073 J3.7862 537 | G2 X86.5141 Y100.0682 I-28.887 J-14.1043 538 | G2 X84.6144 Y102.7778 I430.8 J304.0575 539 | G2 X83.3715 Y104.9239 I12.2152 J8.5071 540 | G2 X82.9801 Y106.291 I3.9759 J1.8779 541 | G3 X82.6533 Y107.5756 I-4.9752 J-0.5819 542 | G3 X81.7153 Y109.6455 I-24.1779 J-9.7086 543 | G2 X80.3995 Y112.3288 I101.2908 J51.3364 544 | G2 X79.4905 Y112.2186 I-5.3286 J40.1433 545 | G2 X78.7866 Y112.2856 I-0.1902 J1.7324 546 | G2 X78.4285 Y112.6433 I0.1598 J0.518 547 | G3 X78.0898 Y113.1278 I-0.9253 J-0.2864 548 | G3 X77.4715 Y113.3684 I-0.7298 J-0.9606 549 | G2 X76.5799 Y113.7487 I0.1466 J1.5791 550 | G2 X76.0604 Y114.6431 I1.0407 J1.2025 551 | G2 X75.8969 Y115.9319 I9.2786 J1.8319 552 | G1 X75.8969 Y115.9319 553 | G4 P0 554 | M05 S0 555 | G1 F3000 556 | G1 X78.8433 Y127.807 557 | G4 P0 558 | M03 S256 559 | G4 P0 560 | G1 F100.000000 561 | G3 X78.856 Y125.2571 I13.13 J-1.2094 562 | G3 X79.25 Y122.7383 I23.8009 J2.4323 563 | G3 X79.9681 Y120.7468 I7.5583 J1.6002 564 | G3 X81.3077 Y119.1815 I4.1001 J2.1529 565 | G3 X84.7646 Y117.5657 I5.6858 J7.6591 566 | G2 X87.3077 Y116.5706 I-2.3334 J-9.7102 567 | G2 X88.7819 Y115.5819 I-5.3894 J-9.6289 568 | G2 X89.7131 Y114.2999 I-1.8389 J-2.3148 569 | G2 X89.6339 Y112.8959 I-1.6404 J-0.6116 570 | G2 X88.7987 Y112.0271 I-1.8418 J0.9347 571 | G3 X90.4317 Y112.6022 I0.2726 J1.832 572 | G3 X90.7991 Y114.6114 I-1.401 J1.2943 573 | G3 X89.8796 Y115.4462 I-1.3255 J-0.5363 574 | G2 X88.786 Y116.3031 I0.5363 J1.8108 575 | G2 X88.4622 Y117.384 I2.0687 J1.2086 576 | G2 X88.5721 Y118.42 I3.2858 J0.1754 577 | G2 X90.1593 Y120.2692 I2.7368 J-0.7434 578 | G3 X92.3259 Y121.8555 I-2.555 J5.7623 579 | G3 X93.5632 Y124.1098 I-4.3711 J3.866 580 | G3 X93.4455 Y126.6928 I-3.8869 J1.1169 581 | G3 X90.086 Y130.5985 I-7.2086 J-2.8028 582 | G3 X85.6305 Y132.8407 I-22.7173 J-39.5934 583 | G3 X84.0991 Y133.3428 I-3.4103 J-7.8152 584 | G2 X82.9313 Y133.6493 I3.8588 J17.0811 585 | G2 X85.4784 Y132.0349 I-21.904 J-37.3755 586 | G2 X87.6458 Y129.298 I-3.3203 J-4.8562 587 | G2 X87.9158 Y126.8379 I-4.5052 J-1.7392 588 | G3 X87.7759 Y124.7697 I12.4642 J-1.8816 589 | G3 X88.4003 Y123.7525 I1.1692 J0.0174 590 | G2 X89.3346 Y123.0358 I-1.6234 J-3.0834 591 | G2 X89.5784 Y122.4149 I-0.6709 J-0.6217 592 | G2 X89.3843 Y121.6407 I-1.6345 J-0.0015 593 | G3 X89.1659 Y122.3779 I-1.7376 J-0.1137 594 | G3 X88.6833 Y122.9144 I-1.429 J-0.8004 595 | G3 X87.7259 Y123.4304 I-2.5757 J-3.6331 596 | G2 X86.8432 Y124.3314 I0.5574 J1.429 597 | G2 X86.6903 Y126.4906 I3.6763 J1.3453 598 | G3 X86.1325 Y129.114 I-3.2246 J0.6854 599 | G3 X84.4889 Y130.9876 I-10.9993 J-7.9915 600 | G3 X82.211 Y131.9258 I-2.3548 J-2.483 601 | G3 X81.122 Y131.8446 I-0.1279 J-5.6316 602 | G3 X79.9341 Y131.5671 I1.7931 J-10.3548 603 | G1 X78.7843 Y132.9508 604 | G1 X78.7578 Y132.9689 605 | G3 X75.573 Y135.7799 I-86.2093 J-94.462 606 | G3 X71.538 Y138.3294 I-10.869 J-12.7343 607 | G3 X67.4044 Y139.5929 I-7.1465 J-15.9857 608 | G3 X62.8785 Y139.9347 I-4.0086 J-22.9498 609 | G3 X57.8158 Y139.2713 I0.5233 J-23.6443 610 | G3 X51.0266 Y135.7795 I3.8467 J-15.8253 611 | G3 X39.5957 Y122.7491 I40.8411 J-47.357 612 | G2 X32.718 Y115.814 I-20.53 J13.4819 613 | G2 X29.5722 Y114.1091 I-10.7316 J16.0475 614 | G2 X25.9896 Y113.3753 I-3.5849 J8.3894 615 | G2 X20.9617 Y115.4155 I-0.0018 J7.2108 616 | G2 X17.0688 Y124.2019 I8.9732 J9.2313 617 | G2 X17.6427 Y130.9076 I28.0966 J0.9729 618 | G2 X20.9581 Y139.6008 I28.7645 J-5.9915 619 | G2 X30.0473 Y147.5868 I16.3346 J-9.4257 620 | G2 X38.3345 Y149.8234 I12.6498 J-30.3989 621 | G2 X45.9103 Y149.3301 I2.5578 J-19.1353 622 | G3 X51.4897 Y148.5859 I5.6279 J20.9076 623 | G3 X57.5501 Y149.4258 I0.0506 J21.9203 624 | G3 X60.9671 Y152.6002 I-1.4092 J4.943 625 | G2 X60.1604 Y152.2603 I-1.6246 J2.7284 626 | G2 X57.3456 Y151.9664 I-2.3393 J8.776 627 | G2 X54.0069 Y153.1267 I0.3324 J6.3399 628 | G3 X48.5292 Y156.4032 I-23.8286 J-33.6197 629 | G3 X43.2775 Y157.9593 I-7.0672 J-14.2116 630 | G3 X36.1933 Y158.4329 I-8.4857 J-73.7003 631 | G3 X28.7245 Y158.0157 I-0.9469 J-50.1109 632 | G3 X18.8651 Y154.8275 I3.7197 J-28.3418 633 | G3 X11.1339 Y149.342 I18.4902 J-34.2512 634 | G3 X4.9536 Y141.4903 I20.5168 J-22.5074 635 | G3 X1.8344 Y132.2706 I24.3925 J-13.3899 636 | G3 X1.4295 Y121.8239 I46.3665 J-7.0284 637 | G3 X3.8935 Y111.1958 I35.2723 J2.5779 638 | G3 X9.1014 Y102.3511 I31.9688 J12.8682 639 | G3 X16.4383 Y96.1612 I19.905 J16.1502 640 | G3 X26.8582 Y92.7299 I13.927 J24.7556 641 | G1 X28.6303 Y100.5463 642 | G1 X25.4669 Y96.0065 643 | G1 X25.5451 Y101.0557 644 | G1 X22.6836 Y97.5051 645 | G1 X21.4286 Y102.3204 646 | G1 X19.1104 Y98.4599 647 | G1 X17.9589 Y103.8362 648 | G1 X14.5996 Y101.1192 649 | G1 X15.663 Y105.5555 650 | G1 X11.218 Y105.0636 651 | G1 X13.2599 Y108.1471 652 | G1 X9.2069 Y108.0977 653 | G1 X11.5652 Y112.2145 654 | G1 X7.9691 Y112.7855 655 | G1 X10.8365 Y116.5297 656 | G1 X6.4338 Y117.835 657 | G1 X10.079 Y121.3255 658 | G1 X6.4798 Y124.2586 659 | G1 X9.295 Y126.4481 660 | G1 X6.6624 Y129.9511 661 | G1 X9.4705 Y133.22 662 | G1 X8.4224 Y137.5369 663 | G1 X10.8069 Y139.6155 664 | G1 X11.5166 Y144.549 665 | G1 X14.1927 Y145.8929 666 | G1 X16.495 Y149.7441 667 | G1 X20.7507 Y150.196 668 | G1 X22.8504 Y153.66 669 | G1 X27.1247 Y153.4424 670 | G1 X31.1638 Y155.6378 671 | G1 X35.3528 Y154.6621 672 | G1 X40.1111 Y156.0992 673 | G1 X43.6352 Y155.2734 674 | G1 X48.2343 Y154.5975 675 | G1 X52.6745 Y151.0472 676 | G1 X45.8636 Y153.4246 677 | G1 X42.4375 Y151.7818 678 | G1 X39.6054 Y153.5104 679 | G1 X37.6555 Y152.1859 680 | G1 X34.2053 Y151.8405 681 | G1 X31.1113 Y152.0403 682 | G1 X28.8558 Y149.2869 683 | G1 X24.2581 Y149.2395 684 | G1 X22.6181 Y145.5303 685 | G1 X18.5299 Y144.096 686 | G1 X18.1475 Y139.8325 687 | G1 X15.2157 Y137.5358 688 | G1 X16.1925 Y133.5237 689 | G1 X13.6607 Y129.3567 690 | G1 X15.6029 Y125.7118 691 | G1 X13.6914 Y122.3759 692 | G1 X16.2345 Y120.7165 693 | G1 X15.6577 Y115.3548 694 | G1 X19.1945 Y114.4635 695 | G1 X19.9912 Y110.4862 696 | G1 X23.8452 Y110.8867 697 | G1 X26.2396 Y108.1615 698 | G1 X29.199 Y111.0512 699 | G3 X35.4301 Y114.8298 I-19.7152 J39.5379 700 | G3 X41.0202 Y119.7936 I-22.3371 J30.7851 701 | G3 X47.3758 Y127.0413 I-134.5873 J124.4325 702 | G2 X54.925 Y133.301 I20.5097 J-17.0524 703 | G2 X61.4605 Y135.7436 I11.362 J-20.436 704 | G2 X68.3635 Y135.8707 I3.7827 J-17.9306 705 | G2 X73.822 Y133.1562 I-1.812 J-10.4892 706 | G3 X78.3528 Y129.0973 I97.1312 J103.8664 707 | G2 X78.8185 Y129.825 I5.853 J-3.2331 708 | G2 X79.4261 Y130.4613 I2.7692 J-2.0359 709 | G2 X79.0844 Y129.1019 I-23.0678 J5.0755 710 | G3 X78.8433 Y127.807 I6.9094 J-1.9564 711 | G1 X78.8433 Y127.807 712 | G4 P0 713 | M05 S0 714 | G1 F3000 715 | G1 X60.6676 Y112.1874 716 | G4 P0 717 | M03 S256 718 | G4 P0 719 | G1 F100.000000 720 | G2 X60.7837 Y112.887 I3.7687 J-0.2661 721 | G3 X60.8612 Y113.2319 I-4.5585 J1.2052 722 | G3 X59.1469 Y112.7753 I-0.4599 J-1.7196 723 | G3 X57.6278 Y110.6513 I5.5032 J-5.5412 724 | G3 X57.4479 Y109.6843 I1.6637 J-0.8097 725 | G2 X57.2412 Y108.9983 I-0.9676 J-0.0825 726 | G2 X56.5665 Y108.5653 I-0.9686 J0.767 727 | G3 X55.6212 Y108.1195 I0.6241 J-2.5482 728 | G3 X54.6897 Y107.2038 I2.883 J-3.8645 729 | G2 X53.8037 Y106.2884 I-4.5496 J3.5171 730 | G2 X51.7804 Y105.0364 I-5.5642 J6.731 731 | G3 X49.6186 Y104.0388 I32.5127 J-73.2988 732 | G2 X51.4698 Y105.2717 I13.7218 J-18.5978 733 | G3 X52.9872 Y106.8038 I-2.2312 J3.7273 734 | G3 X53.2881 Y107.8142 I-1.9079 J1.1181 735 | G2 X53.7444 Y108.8343 I1.5354 J-0.0748 736 | G2 X54.3562 Y109.4246 I26.6265 J-26.9847 737 | G2 X55.0364 Y109.7739 I0.9431 J-0.9996 738 | G3 X55.4833 Y110.3344 I-0.1057 J0.5427 739 | G2 X55.6178 Y111.2109 I2.4152 J0.078 740 | G2 X56.0338 Y111.9693 I2.4868 J-0.8707 741 | G3 X56.6592 Y112.7874 I-29.163 J22.9425 742 | G2 X57.3593 Y113.72 I92.1832 J-68.4662 743 | G2 X58.4883 Y114.5587 I2.0066 J-1.5222 744 | G2 X60.1068 Y114.6764 I1.0113 J-2.7181 745 | G3 X61.3159 Y114.7156 I0.5256 J2.4548 746 | G3 X61.7431 Y115.1886 I-0.1633 J0.5768 747 | G2 X62.0593 Y115.6489 I0.6628 J-0.1165 748 | G2 X62.9324 Y115.8723 I0.811 J-1.3514 749 | G3 X64.0678 Y116.1075 I0.0947 J2.4016 750 | G3 X65.3946 Y117.168 I-1.6413 J3.4135 751 | G3 X66.3787 Y118.4883 I-21.6474 J17.1618 752 | G2 X66.3284 Y115.7172 I-21.204 J-1.0017 753 | G2 X65.7135 Y113.02 I-9.8282 J0.8221 754 | G2 X64.2613 Y110.4502 I-9.2668 J3.5416 755 | G2 X61.6783 Y108.2236 I-6.9085 J5.4028 756 | G2 X60.3993 Y107.6881 I-2.8794 J5.0818 757 | G3 X59.3812 Y107.3609 I4.3831 J-15.3838 758 | G2 X59.9769 Y108.0178 I3.4496 J-2.5291 759 | G3 X60.5609 Y108.9695 I-1.303 J1.4547 760 | G3 X60.6947 Y110.7243 I-4.6579 J1.2376 761 | G2 X60.6676 Y112.1874 I8.1856 J0.8835 762 | G1 X60.6676 Y112.1874 763 | G4 P0 764 | M05 S0 765 | G1 F3000 766 | G1 X95.8376 Y103.2531 767 | G4 P0 768 | M03 S256 769 | G4 P0 770 | G1 F100.000000 771 | G2 X98.0873 Y104.1025 I11.3127 J-26.5602 772 | G2 X100.4375 Y104.5567 I2.934 J-8.8734 773 | G2 X101.6393 Y104.5429 I0.5081 J-8.0831 774 | G3 X102.8525 Y104.4633 I2.5752 J29.9585 775 | G2 X103.771 Y105.6822 I12.8431 J-8.7225 776 | G3 X104.451 Y106.9787 I-2.673 J2.2284 777 | G3 X104.7145 Y108.9743 I-6.9808 J1.9369 778 | G3 X104.2233 Y111.4285 I-6.6449 J-0.0538 779 | G3 X102.6304 Y113.6682 I-5.5615 J-2.2691 780 | G2 X101.1095 Y115.1096 I18.8251 J21.3871 781 | G2 X100.2693 Y116.755 I2.2054 J2.1633 782 | G3 X99.866 Y119.114 I-1316.1435 J-223.7875 783 | G3 X99.6189 Y120.0748 I-5.9882 J-1.0279 784 | G2 X99.3363 Y121.0709 I8.5863 J2.9743 785 | G3 X96.8181 Y121.3472 I-3.6505 J-21.6616 786 | G3 X93.9394 Y121.3495 I-1.4621 J-28.3295 787 | G3 X95.2117 Y122.6961 I-13.0427 J13.5978 788 | G3 X96.0428 Y124.4065 I-2.8575 J2.4455 789 | G3 X96.0963 Y126.54 I-6.1434 J1.2217 790 | G3 X95.3285 Y128.608 I-5.2478 J-0.7721 791 | G3 X93.9318 Y130.2961 I-7.3528 J-4.6614 792 | G3 X92.224 Y131.8448 I-46.7582 J-49.8469 793 | G3 X90.2493 Y133.2475 I-7.852 J-8.9624 794 | G2 X88.4355 Y134.4404 I8.1275 J14.3338 795 | G3 X90.5498 Y135.2861 I-10.4688 J29.2373 796 | G2 X92.579 Y135.9234 I3.826 J-8.6328 797 | G2 X97.4734 Y135.7125 I2.006 J-10.3416 798 | G2 X102.5378 Y133.5068 I-5.7096 J-20.0264 799 | G2 X105.1586 Y131.7327 I-16.0332 J-26.5088 800 | G2 X108.2142 Y129.0135 I-15.2077 J-20.165 801 | G2 X111.5325 Y124.8929 I-22.6742 J-21.6562 802 | G3 X114.8407 Y120.5322 I40.8958 J27.5898 803 | G3 X117.8221 Y117.1487 I96.556 J82.0755 804 | G3 X120.199 Y114.9165 I15.7135 J14.3507 805 | G3 X122.8315 Y113.344 I6.9411 J8.6303 806 | G2 X125.1842 Y112.2125 I-10.4321 J-24.7031 807 | G1 X127.7739 Y109.4626 808 | G1 X129.8696 Y112.0559 809 | G1 X133.2419 Y111.6746 810 | G1 X133.9387 Y115.4598 811 | G1 X137.0338 Y116.3079 812 | G1 X136.5289 Y121.4104 813 | G1 X138.7543 Y122.9898 814 | G1 X137.0818 Y126.1645 815 | G1 X138.7811 Y129.6336 816 | G1 X136.5656 Y133.5991 817 | G1 X137.4199 Y137.4172 818 | G1 X134.8551 Y139.6031 819 | G1 X134.5204 Y143.6606 820 | G1 X130.9424 Y145.0254 821 | G1 X129.507 Y148.5554 822 | G1 X125.4842 Y148.6006 823 | G1 X123.5109 Y151.221 824 | G1 X120.8027 Y151.0314 825 | G1 X117.7837 Y151.3601 826 | G1 X116.0766 Y152.6206 827 | G1 X113.599 Y150.9758 828 | G1 X110.6009 Y152.539 829 | G1 X104.6412 Y150.2764 830 | G1 X108.5263 Y153.6552 831 | G1 X112.5513 Y154.2981 832 | G1 X115.6349 Y155.0841 833 | G1 X119.7994 Y153.7164 834 | G1 X123.4649 Y154.6449 835 | G1 X126.9992 Y152.5556 836 | G1 X130.7392 Y152.7625 837 | G1 X132.5764 Y149.4659 838 | G1 X136.3003 Y149.036 839 | G1 X138.3151 Y145.3708 840 | G1 X140.6573 Y144.0921 841 | G1 X141.279 Y139.3967 842 | G1 X143.3649 Y137.4184 843 | G1 X142.4477 Y133.3101 844 | G1 X144.905 Y130.1988 845 | G1 X142.601 Y126.8652 846 | G1 X145.0648 Y124.7816 847 | G1 X141.9152 Y121.9901 848 | G1 X145.1051 Y118.6681 849 | G1 X141.2528 Y117.4257 850 | G1 X143.762 Y113.8627 851 | G1 X140.6152 Y113.3197 852 | G1 X142.6789 Y109.4019 853 | G1 X139.1324 Y109.449 854 | G1 X140.9189 Y106.5145 855 | G1 X137.029 Y106.9827 856 | G1 X137.9595 Y102.7609 857 | G1 X135.0199 Y105.3469 858 | G1 X134.0129 Y100.2302 859 | G1 X131.9843 Y103.9042 860 | G1 X130.8856 Y99.3218 861 | G1 X128.3815 Y102.7008 862 | G1 X128.4498 Y97.8957 863 | G1 X125.682 Y102.2162 864 | G2 X126.3281 Y98.55 I-30.5522 J-7.274 865 | G3 X127.7589 Y94.7695 I8.2222 J0.9508 866 | G3 X134.0979 Y96.5474 I-3.1875 J23.5545 867 | G3 X139.8298 Y100.3792 I-7.8517 J17.9481 868 | G3 X145.3718 Y107.3618 I-23.1723 J24.0822 869 | G3 X148.5979 Y115.1417 I-25.2095 J15.0125 870 | G3 X149.828 Y124.8077 I-38.6502 J9.8298 871 | G3 X148.6569 Y134.6388 I-40.4554 J0.1662 872 | G3 X146.329 Y140.6586 I-24.5834 J-6.0464 873 | G3 X143.0246 Y146.0033 I-48.5429 J-26.3184 874 | G3 X138.9129 Y150.2771 I-200.5246 J-188.8032 875 | G3 X134.2266 Y153.9594 I-16.6527 J-16.3699 876 | G3 X125.8296 Y157.0333 I-12.3178 J-20.6426 877 | G3 X116.9822 Y157.6991 I-8.166 J-49.3941 878 | G3 X112.5217 Y157.1648 I0.2914 J-21.3183 879 | G3 X109.7706 Y156.3535 I5.1576 J-22.5588 880 | G3 X105.1349 Y153.8335 I6.067 J-16.6843 881 | G2 X100.4065 Y151.2038 I-11.7258 J15.5182 882 | G2 X98.8306 Y150.91 I-1.5794 J4.0991 883 | G2 X97.5524 Y151.2295 I-0.0022 J2.7084 884 | G3 X99.0239 Y149.4078 I4.8877 J2.443 885 | G3 X101.4473 Y148.147 I4.3922 J5.4831 886 | G3 X104.3993 Y147.8294 I2.3648 J8.1024 887 | G3 X109.1593 Y148.2151 I-14.8071 J212.3469 888 | G3 X112.4649 Y148.7431 I-2.3239 J25.161 889 | G2 X116.9546 Y149.2184 I4.2291 J-18.5055 890 | G2 X122.4154 Y148.1353 I-0.2112 J-15.374 891 | G2 X124.9545 Y146.9132 I-7.3492 J-18.5163 892 | G2 X129.113 Y143.8715 I-11.4336 J-19.9956 893 | G2 X132.4737 Y139.8091 I-13.1549 J-14.3039 894 | G2 X133.8861 Y137.012 I-15.2343 J-9.4481 895 | G2 X134.8859 Y133.9945 I-24.5359 J-9.8031 896 | G2 X135.496 Y130.7709 I-21.8347 J-5.8021 897 | G2 X135.7006 Y127.6684 I-31.8528 J-3.6593 898 | G2 X135.5836 Y124.7559 I-25.3468 J-0.4403 899 | G2 X135.0938 Y122.0878 I-16.1154 J1.5791 900 | G2 X132.3064 Y116.6701 I-14.1005 J3.829 901 | G2 X126.2473 Y114.2553 I-5.207 J4.2559 902 | G2 X120.3955 Y117.7238 I1.0871 J8.5047 903 | G3 X116.6963 Y122.7947 I-1049.1076 J-761.4179 904 | G3 X111.7105 Y129.5513 I-1067.9793 J-782.8747 905 | G3 X106.0842 Y135.7465 I-34.189 J-25.3969 906 | G3 X101.8208 Y138.5324 I-10.9798 J-12.1477 907 | G3 X96.8945 Y139.8269 I-6.3337 J-14.0829 908 | G3 X90.011 Y138.832 I-1.3622 J-14.8853 909 | G3 X84.1031 Y135.3398 I8.1838 J-20.5887 910 | G3 X85.5037 Y134.9386 I7.4557 J23.382 911 | G2 X86.9716 Y134.4254 I-2.2072 J-8.6693 912 | G2 X90.0865 Y132.7292 I-9.6848 J-21.4929 913 | G2 X92.868 Y130.5559 I-10.7455 J-16.6196 914 | G2 X94.7684 Y128.1618 I-7.125 J-7.6071 915 | G2 X95.5652 Y125.3393 I-5.1602 J-2.9805 916 | G2 X94.8108 Y123.1456 I-3.3113 J-0.0878 917 | G2 X93.056 Y121.2558 I-16.7762 J13.8181 918 | G2 X91.7571 Y120.1774 I-8.2668 J8.6356 919 | G3 X90.7161 Y119.3316 I6.5132 J-9.0801 920 | G3 X89.8093 Y118.2444 I3.6227 J-3.9434 921 | G3 X89.929 Y116.6169 I1.162 J-0.7327 922 | G3 X90.6184 Y116.0351 I2.1463 J1.844 923 | G2 X91.5199 Y115.4318 I-5.7102 J-9.5079 924 | G2 X92.2282 Y113.6791 I-1.0809 J-1.4563 925 | G2 X90.8375 Y111.7062 I-2.7384 J0.4537 926 | G2 X88.482 Y111.1792 I-2.0726 J3.7359 927 | G2 X86.3566 Y112.0771 I0.2334 J3.517 928 | G3 X88.1698 Y112.6497 I-0.8979 J6.0004 929 | G3 X88.8421 Y113.7766 I-0.5436 J1.0884 930 | G3 X88.3528 Y114.7167 I-1.2219 J-0.0386 931 | G3 X86.9741 Y115.534 I-4.4791 J-5.9845 932 | G3 X84.0278 Y116.5452 I-6.6622 J-14.6137 933 | G2 X80.9123 Y117.9687 I2.0439 J8.5944 934 | G2 X79.0092 Y120.2394 I3.5076 J4.8725 935 | G2 X78.0491 Y122.9407 I12.1259 J5.8312 936 | G3 X76.5801 Y122.7776 I12.1539 J-116.168 937 | G3 X74.569 Y122.3313 I1.1862 J-10.1009 938 | G3 X72.2986 Y121.2723 I3.3769 J-10.2032 939 | G2 X70.2654 Y120.4546 I-3.3867 J5.4838 940 | G2 X66.5854 Y120.1286 I-3.1516 J14.6468 941 | G2 X64.5614 Y120.4227 I0.3316 J9.3958 942 | G3 X59.6508 Y120.41 I-2.4311 J-9.3925 943 | G3 X53.2471 Y118.5408 I33.3385 J-126.1187 944 | G2 X51.84 Y118.3804 I-1.1076 J3.4627 945 | G3 X47.9905 Y117.8869 I-0.7671 J-9.278 946 | G3 X43.1784 Y115.6254 I8.6526 J-24.6617 947 | G3 X38.8451 Y112.5281 I17.3106 J-28.7983 948 | G3 X34.2356 Y106.7251 I12.0437 J-14.299 949 | G3 X28.1635 Y90.666 I69.4872 J-35.4509 950 | G3 X28.0498 Y86.6177 I33.4212 J-2.9646 951 | G2 X26.8289 Y84.0208 I-3.1554 J-0.102 952 | G3 X24.9234 Y79.9862 I3.0183 J-3.8928 953 | G2 X24.29 Y77.5952 I-4.356 J-0.1255 954 | G2 X23.2884 Y76.7408 I-1.7996 J1.0955 955 | G3 X21.4605 Y75.4788 I1.9643 J-4.7999 956 | G3 X20.1695 Y73.9063 I11.7506 J-10.9624 957 | G3 X19.3635 Y71.3533 I3.4721 J-2.4999 958 | G2 X21.4601 Y72.5369 I9.7824 J-14.8788 959 | G2 X23.9158 Y73.1408 I2.6791 J-5.599 960 | G3 X25.1244 Y73.2016 I-1.5573 J43.0421 961 | G3 X28.8547 Y74.8762 I-0.375 J5.8272 962 | G2 X32.6268 Y76.6907 I4.5216 J-4.572 963 | G3 X36.9169 Y77.6344 I-2.5715 J21.9126 964 | G3 X40.5806 Y79.1476 I-8.173 J24.9793 965 | G3 X48.5555 Y85.262 I-12.1759 J24.1389 966 | G3 X53.5092 Y93.1453 I-20.6628 J18.4824 967 | G3 X56.1811 Y103.4843 I-35.503 J14.6897 968 | G2 X56.295 Y101.7003 I-23.243 J-2.3796 969 | G2 X55.9774 Y96.7628 I-27.5926 J-0.7037 970 | G3 X55.289 Y91.3319 I96.914 J-15.0435 971 | G3 X56.7933 Y83.6017 I13.6868 J-1.348 972 | G1 X58.4595 Y80.4213 973 | G3 X56.5116 Y82.8863 I-34.8281 J-25.5203 974 | G2 X54.9464 Y85.5572 I6.767 J5.7596 975 | G1 X53.6008 Y89.1874 976 | G2 X49.9387 Y84.2959 I-34.1579 J21.7555 977 | G2 X45.7163 Y80.2289 I-25.7003 J22.4568 978 | G3 X45.6335 Y77.0498 I29.9029 J-2.3701 979 | G3 X46.1955 Y73.9266 I10.5384 J0.2843 980 | G2 X46.2328 Y70.5313 I-5.1347 J-1.7541 981 | G3 X46.3101 Y68.2756 I3.2115 J-1.019 982 | G3 X47.4171 Y67.3233 I1.458 J0.5753 983 | G2 X48.4353 Y66.365 I-0.31 J-1.3494 984 | G2 X48.5367 Y63.7219 I-5.1699 J-1.5219 985 | G3 X48.8411 Y61.0685 I4.1054 J-0.8732 986 | G3 X50.4482 Y60.306 I1.2657 J0.5928 987 | G2 X51.6817 Y60.1329 I0.4039 J-1.6034 988 | G2 X52.5075 Y59.3539 I-1.2471 J-2.1491 989 | G3 X53.0363 Y58.5771 I28.5908 J18.8965 990 | G2 X53.2016 Y57.4684 I-1.0268 J-0.7197 991 | G3 X53.6756 Y56.2846 I0.9451 J-0.3084 992 | G3 X55.0778 Y56.039 I1.0706 J1.9873 993 | G2 X56.524 Y55.8104 I0.3591 J-2.4171 994 | G2 X57.2786 Y54.8427 I-0.6309 J-1.27 995 | G2 X57.275 Y53.5377 I-2.9534 J-0.6444 996 | G2 X56.632 Y52.47 I-1.9248 J0.4317 997 | G3 X55.8114 Y51.3572 I1.9865 J-2.3238 998 | G3 X55.5987 Y50.1528 I2.441 J-1.0521 999 | G3 X55.9325 Y49.0156 I2.6155 J0.1502 1000 | G3 X56.9222 Y48.0436 I2.1792 J1.229 1001 | G3 X58.4477 Y47.3249 I7.2744 J13.4601 1002 | G3 X60.155 Y47.0098 I1.6203 J3.9961 1003 | G2 X61.967 Y46.5688 I0.0736 J-3.6408 1004 | G2 X63.5127 Y45.3386 I-2.7149 J-4.9974 1005 | G2 X64.5312 Y44.0195 I-9.3972 J-8.309 1006 | G2 X65.0764 Y42.8479 I-3.4601 J-2.3228 1007 | G2 X65.2156 Y41.7278 I-3.4856 J-1.0019 1008 | G2 X64.9743 Y40.3875 I-4.7031 J0.1549 1009 | G2 X64.3659 Y39.0032 I-7.9092 J2.6505 1010 | G3 X63.8931 Y37.8955 I5.1998 J-2.8737 1011 | G3 X64.4771 Y35.5198 I2.261 J-0.7039 1012 | G3 X66.0251 Y34.3183 I5.4834 J5.4671 1013 | G2 X66.9454 Y33.5572 I-1.9301 J-3.2706 1014 | G3 X66.5248 Y36.1648 I-12.6347 J-0.6998 1015 | G2 X66.6442 Y38.2012 I3.1014 J0.8398 1016 | G2 X68.1982 Y39.6574 I2.3394 J-0.9392 1017 | G3 X69.697 Y40.4121 I-1.7539 J5.349 1018 | G3 X70.8006 Y41.4793 I-2.9235 J4.1274 1019 | G2 X71.742 Y42.5934 I11.2187 J-8.5249 1020 | G3 X71.7781 Y41.0877 I21.8235 J-0.2304 1021 | G2 X71.7259 Y39.9476 I-5.4777 J-0.3206 1022 | G3 X71.7684 Y38.3341 I4.545 J-0.6878 1023 | G3 X73.2209 Y37.0141 I1.6307 J0.3352 1024 | G3 X74.607 Y37.2615 I0.2688 J2.5018 1025 | G2 X76.1071 Y37.9085 I6.8552 J-13.8312 1026 | G2 X75.1787 Y36.5711 I-7.2967 J4.0747 1027 | G3 X74.5168 Y35.0603 I2.1866 J-1.8583 1028 | G3 X74.5662 Y33.3862 I5.5287 J-0.6748 1029 | G3 X75.331 Y31.5506 I4.5975 J0.8385 1030 | G3 X76.8759 Y30.0098 I5.0766 J3.5451 1031 | G3 X78.4708 Y28.9257 I55.3431 J79.6987 1032 | G3 X79.7916 Y28.2564 I3.4728 J5.2159 1033 | G2 X80.7632 Y27.8521 I-4.1304 J-11.2959 1034 | G2 X80.6986 Y26.1299 I-12.1185 J-0.4074 1035 | G3 X80.7082 Y24.5024 I7.0776 J-0.772 1036 | G3 X81.0661 Y23.0107 I6.6353 J0.8031 1037 | G3 X81.7747 Y21.6508 I5.5589 J2.0322 1038 | G3 X82.8918 Y20.573 I3.3203 J2.3235 1039 | G2 X84.2778 Y19.6397 I-25.4759 J-39.3277 1040 | G3 X83.3083 Y21.3899 I-9.1835 J-3.9434 1041 | G2 X82.7659 Y23.1593 I2.5035 J1.7353 1042 | G2 X83.1172 Y24.6894 I3.6875 J-0.0414 1043 | G2 X84.3523 Y26.3605 I4.8209 J-2.2708 1044 | G2 X86.9331 Y27.9166 I5.2155 J-5.7319 1045 | G3 X89.699 Y29.4681 I-3.2955 J9.1161 1046 | G3 X91.1929 Y30.994 I-5.5625 J6.9402 1047 | G3 X91.9319 Y32.2175 I-5.1136 J3.9236 1048 | G3 X92.319 Y34.1698 I-3.8564 J1.7792 1049 | G2 X92.364 Y35.8441 I12.3767 J0.5051 1050 | G2 X92.7696 Y34.3268 I-11.8338 J-3.9763 1051 | G2 X93.0055 Y32.7774 I-16.2907 J-3.273 1052 | G2 X92.606 Y30.3525 I-4.6184 J-0.4844 1053 | G3 X91.8159 Y28.4987 I32.4115 J-14.9076 1054 | G3 X93.9597 Y30.1154 I-2.9953 J6.2016 1055 | G3 X95.0273 Y31.7992 I-4.5726 J4.0794 1056 | G2 X95.2573 Y31.3878 I-0.2395 J-0.4038 1057 | G3 X95.3316 Y29.9143 I11.0996 J-0.1793 1058 | G2 X96.256 Y30.7212 I6.6247 J-6.6568 1059 | G3 X97.1716 Y31.4947 I-7.3895 J9.6756 1060 | G3 X98.0418 Y32.3101 I-130.0156 J139.6365 1061 | G3 X98.7002 Y33.2939 I-1.8191 J1.9294 1062 | G3 X98.8663 Y34.45 I-2.4867 J0.9473 1063 | G3 X98.655 Y35.767 I-8.2704 J-0.6512 1064 | G3 X97.9688 Y37.1176 I-3.2201 J-0.7864 1065 | G2 X96.9055 Y38.6657 I7.2767 J6.1371 1066 | G2 X96.7497 Y40.1251 I1.6657 J0.9158 1067 | G3 X96.9639 Y41.7704 I-4.9752 J1.4845 1068 | G2 X95.4657 Y42.1576 I1.5853 J9.228 1069 | G3 X93.7472 Y42.3236 I-1.1995 J-3.4391 1070 | G3 X91.8105 Y41.4678 I0.5576 J-3.8809 1071 | G2 X89.8401 Y40.746 I-2.0108 J2.4387 1072 | G2 X88.2051 Y41.409 I-0.029 J2.2757 1073 | G3 X86.8381 Y42.2355 I-2.3758 J-2.3857 1074 | G3 X85.2684 Y42.5198 I-1.9146 J-6.0967 1075 | G2 X83.7198 Y42.669 I0.9978 J18.4617 1076 | G2 X86.7791 Y44.1206 I3.713 J-3.8759 1077 | G3 X93.6394 Y45.1004 I-21.3428 J173.9419 1078 | G2 X98.2449 Y45.6886 I10.7863 J-66.1321 1079 | G2 X102.881 Y45.9355 I5.4684 J-59.0308 1080 | G2 X105.3079 Y45.7067 I0.1602 J-11.285 1081 | G3 X107.0266 Y45.7735 I0.7235 J3.5289 1082 | G3 X107.7761 Y46.5344 I-0.2994 J1.0445 1083 | G2 X108.0739 Y47.4267 I7.8329 J-2.1181 1084 | G3 X108.493 Y50.361 I-5.8253 J2.3291 1085 | G3 X108.0443 Y53.2394 I-24.987 J-2.4214 1086 | G3 X103.7917 Y53.7021 I-29.9044 J-255.0369 1087 | G3 X99.5152 Y53.7522 I-2.4206 J-24.1099 1088 | G3 X95.1515 Y52.9071 I1.4885 J-19.3744 1089 | G2 X90.656 Y52.1421 I-5.0145 J15.877 1090 | G2 X86.3843 Y52.4284 I-0.6796 J21.8733 1091 | G2 X82.4055 Y53.9085 I1.8328 J11.0144 1092 | G2 X78.4224 Y57.8777 I6.2514 J10.2564 1093 | G3 X77.0952 Y59.9721 I-75.6581 J-46.4778 1094 | G2 X75.1505 Y63.6195 I19.4878 J12.7326 1095 | G3 X73.5827 Y66.821 I-30.1588 J-12.7842 1096 | G3 X72.4973 Y68.6461 I-34.3789 J-19.2098 1097 | G3 X71.7567 Y69.523 I-3.5307 J-2.2307 1098 | G3 X70.4749 Y70.1784 I-1.8786 J-2.0932 1099 | G2 X69.1255 Y70.5969 I1.7252 J7.9461 1100 | G3 X70.6612 Y70.601 I0.7481 J7.3212 1101 | G2 X72.1586 Y70.287 I0.2651 J-2.4629 1102 | G2 X74.3097 Y67.9101 I-2.5801 J-4.4969 1103 | G3 X75.9412 Y64.7865 I26.7917 J12.0053 1104 | G2 X77.1263 Y68.1535 I35.2541 J-10.5168 1105 | G2 X79.2706 Y70.7538 I5.237 J-2.1342 1106 | G3 X77.3874 Y67.295 I6.4475 J-5.7525 1107 | G3 X76.5796 Y63.7764 I40.4756 J-11.1442 1108 | G2 X79.6457 Y58.7012 I-673.053 J-410.0781 1109 | G3 X84.0046 Y54.9903 I7.671 J4.595 1110 | G3 X86.1571 Y54.3991 I3.7126 J9.303 1111 | G3 X88.4072 Y54.0827 I10.6084 J67.2808 1112 | G3 X94.1732 Y54.3974 I2.0068 J16.2072 1113 | G3 X99.8985 Y55.7814 I-138.1684 J584.1297 1114 | G2 X104.0114 Y56.2164 I3.6373 J-14.7273 1115 | G2 X108.944 Y55.9942 I-5.6638 J-180.5389 1116 | G3 X111.413 Y56.2065 I0.5039 J8.5994 1117 | G3 X113.3901 Y57.7911 I-0.6708 J2.8628 1118 | G3 X113.5872 Y58.769 I-1.8066 J0.873 1119 | G3 X113.2346 Y59.8521 I-2.1924 J-0.1148 1120 | G3 X112.2256 Y60.6715 I-1.7789 J-1.1598 1121 | G2 X111.0034 Y61.4546 I1.3322 J3.4246 1122 | G2 X110.2067 Y62.4405 I3.6064 J3.7291 1123 | G3 X109.5251 Y63.4397 I-12.3992 J-7.7252 1124 | G2 X105.6821 Y62.8778 I-8.2489 J42.994 1125 | G2 X102.8548 Y62.6967 I-3.8736 J38.2935 1126 | G2 X104.8941 Y63.4236 I8.5301 J-20.707 1127 | G3 X107.0088 Y64.156 I-8.2369 J27.1998 1128 | G3 X109.2979 Y65.2311 I-6.9561 J17.7857 1129 | G3 X111.2492 Y66.4124 I-14.1504 J25.5764 1130 | G3 X112.7743 Y67.456 I-30.7774 J46.6128 1131 | G3 X113.9963 Y68.5192 I-4.7229 J6.6626 1132 | G2 X115.3447 Y69.8874 I30.0949 J-28.309 1133 | G3 X116.5363 Y71.4269 I-4.7652 J4.9195 1134 | G3 X117.1206 Y73.1312 I-4.2308 J2.4028 1135 | G3 X117.2876 Y75.0944 I-16.4802 J2.3904 1136 | G3 X117.253 Y77.4406 I-29.0171 J0.745 1137 | G3 X116.7423 Y79.4333 I-5.2823 J-0.2918 1138 | G3 X115.563 Y80.6385 I-2.3584 J-1.1282 1139 | G3 X114.4095 Y81.1105 I-3.5419 J-7.01 1140 | G3 X113.3199 Y80.9912 I-0.4105 J-1.2863 1141 | G2 X112.2096 Y80.6285 I-1.4008 J2.4072 1142 | G3 X111.0811 Y80.4236 I0.796 J-7.5951 1143 | G2 X109.3699 Y80.5822 I-0.6365 J2.4441 1144 | G2 X107.6598 Y81.6801 I3.3061 J7.0303 1145 | G2 X105.487 Y84.042 I8.298 J9.8139 1146 | G2 X103.874 Y87.0712 I10.8762 J7.7357 1147 | G3 X102.1297 Y90.7594 I-23.4838 J-8.8495 1148 | G3 X98.5765 Y96.2151 I-52.0944 J-30.0439 1149 | G3 X94.7121 Y101.4618 I-778.0107 J-568.9816 1150 | G2 X94.3786 Y102.828 I208.6587 J51.6653 1151 | G2 X94.0765 Y104.6577 I12.431 J2.9918 1152 | G2 X93.9744 Y107.7074 I26.3815 J2.4097 1153 | G2 X94.4843 Y110.7475 I10.8863 J-0.2631 1154 | G2 X95.432 Y112.9046 I10.6495 J-3.3918 1155 | G2 X97.2442 Y114.4695 I3.1252 J-1.7875 1156 | G2 X98.3005 Y114.7256 I1.4661 J-3.7404 1157 | G2 X99.2412 Y114.6061 I0.2134 J-2.0817 1158 | G2 X99.9563 Y114.0699 I-0.5434 J-1.4697 1159 | G2 X100.1392 Y113.0217 I-0.9521 J-0.7062 1160 | G2 X99.5099 Y112.2249 I-1.3237 J0.3987 1161 | G3 X98.9519 Y111.7799 I1.2231 J-2.1058 1162 | G3 X98.2376 Y110.7866 I3.5254 J-3.2889 1163 | G3 X97.8004 Y109.7106 I4.6355 J-2.5099 1164 | G3 X99.0105 Y109.739 I-1.337 J82.8926 1165 | G2 X100.0212 Y109.624 I0.1089 J-3.5424 1166 | G2 X100.971 Y109.1313 I-0.6214 J-2.3597 1167 | G2 X101.8506 Y107.8663 I-1.9035 J-2.2619 1168 | G3 X100.6686 Y108.7044 I-4.4304 J-4.9959 1169 | G3 X99.3933 Y109.0528 I-1.3451 J-2.4158 1170 | G3 X98.5883 Y108.9695 I-0.0787 J-3.1708 1171 | G2 X97.6929 Y108.7971 I-2.5569 J10.8692 1172 | G3 X97.4021 Y107.6566 I11.5858 J-3.5612 1173 | G2 X97.0579 Y106.7312 I-3.1321 J0.6384 1174 | G2 X96.4971 Y106.2518 I-0.9326 J0.5231 1175 | G3 X95.9587 Y105.7036 I0.3143 J-0.8473 1176 | G3 X95.762 Y104.6299 I3.2957 J-1.1584 1177 | G3 X95.8376 Y103.2531 I8.725 J-0.2116 1178 | G1 X95.8376 Y103.2531 1179 | G4 P0 1180 | M05 S0 1181 | G1 F3000 1182 | G1 X98.7783 Y102.1666 1183 | G4 P0 1184 | M03 S256 1185 | G4 P0 1186 | G1 F100.000000 1187 | G3 X97.5673 Y102.0379 I0.9698 J-14.8921 1188 | G3 X96.4918 Y101.6078 I0.3654 J-2.4734 1189 | G2 X97.2219 Y100.4714 I-4308.5646 J-2769.1449 1190 | G2 X97.9514 Y99.3353 I-4306.4129 J-2766.075 1191 | G2 X98.1737 Y100.9373 I4.8766 J0.1399 1192 | G2 X98.7783 Y102.1666 I4.2941 J-1.3489 1193 | G1 X98.7783 Y102.1666 1194 | G4 P0 1195 | M05 S0 1196 | G1 F3000 1197 | G1 X103.6594 Y104.1864 1198 | G4 P0 1199 | M03 S256 1200 | G4 P0 1201 | G1 F100.000000 1202 | G2 X104.0422 Y104.0166 I-1.8316 J-4.6459 1203 | G2 X104.5673 Y103.6911 I-1.4363 J-2.9033 1204 | G2 X105.1983 Y103.2046 I-37.546 J-49.3452 1205 | G2 X105.9448 Y102.2411 I-1.5268 J-1.9539 1206 | G2 X106.2699 Y100.6785 I-3.572 J-1.5582 1207 | G2 X106.1999 Y99.7321 I-6.5367 J0.0073 1208 | G3 X105.728 Y99.8102 I-0.5561 J-1.895 1209 | G2 X105.1729 Y100.0287 I0.039 J0.9133 1210 | G2 X104.7714 Y100.5736 I0.9786 J1.1416 1211 | G3 X104.2732 Y101.1627 I-1.2222 J-0.5284 1212 | G3 X103.6874 Y101.3607 I-0.6843 J-1.0588 1213 | G2 X103.0941 Y101.4215 I0.974 J12.4336 1214 | G2 X103.3407 Y100.4235 I-5.0721 J-1.7827 1215 | G2 X103.3266 Y99.5377 I-2.7112 J-0.4 1216 | G2 X102.5353 Y98.1038 I-2.5088 J0.4493 1217 | G2 X101.1219 Y97.4518 I-1.7346 J1.9019 1218 | G2 X100.138 Y97.4839 I-0.3908 J3.1146 1219 | G3 X99.2782 Y97.6448 I-16.2356 J-84.3797 1220 | G3 X99.6754 Y96.9541 I3.6071 J1.6149 1221 | G2 X100.0729 Y96.2633 I-3.2184 J-2.3118 1222 | G2 X101.5783 Y96.4289 I3.1825 J-22.0065 1223 | G3 X102.4255 Y96.6511 I-0.1835 J2.4257 1224 | G3 X103.2502 Y97.165 I-1.723 J3.6839 1225 | G2 X104.2882 Y97.7388 I2.1705 J-2.7006 1226 | G2 X105.3163 Y97.9634 I1.4638 J-4.2337 1227 | G3 X106.4158 Y98.1028 I-1.8785 J19.224 1228 | G3 X105.6002 Y97.3088 I6.1191 J-7.1014 1229 | G2 X104.7854 Y96.5151 I-6.9685 J6.3381 1230 | G3 X105.4602 Y94.5538 R89514.514375 1231 | G2 X106.1347 Y92.593 I-13090.8083 J-4498.7551 1232 | G3 X107.4864 Y91.8584 I6.6689 J10.6595 1233 | G2 X109.1847 Y90.9454 I-7.2023 J-15.4336 1234 | G2 X111.0441 Y89.6308 I-9.0833 J-14.82 1235 | G3 X113.3938 Y88.1717 I6.9691 J8.6013 1236 | G3 X115.4554 Y87.3232 I11.6353 J25.34 1237 | G3 X117.5272 Y86.7533 I4.5013 J12.3131 1238 | G2 X119.823 Y85.7844 I-1.0928 J-5.794 1239 | G3 X122.0735 Y84.9047 I2.9256 J4.1658 1240 | G3 X125.3838 Y84.6609 I3.7018 J27.6692 1241 | G3 X127.9296 Y84.767 I0.3212 J22.8969 1242 | G3 X130.081 Y85.3813 I-0.6041 J6.1899 1243 | G3 X131.3318 Y87.1752 I-1.0052 J2.0339 1244 | G2 X129.685 Y87.2479 I-0.6998 J2.8372 1245 | G2 X127.4369 Y89.3672 I1.2119 J3.5375 1246 | G2 X127.1146 Y91.1613 I4.0264 J1.6493 1247 | G3 X126.6122 Y93.335 I-4.3299 J0.1442 1248 | G3 X125.5939 Y94.6427 I-4.221 J-2.2366 1249 | G2 X124.1016 Y96.3141 I7.0258 J7.775 1250 | G2 X122.3293 Y98.7878 I29508.2284 J21143.5088 1251 | G2 X120.4403 Y101.9456 I18.1365 J12.9931 1252 | G2 X119.3816 Y105.0301 I11.6736 J5.7307 1253 | G3 X118.4724 Y107.7788 I-12.4018 J-2.5775 1254 | G3 X116.9895 Y110.237 I-11.9978 J-5.5613 1255 | G2 X115.5311 Y112.2283 I50.5087 J38.5216 1256 | G3 X112.4764 Y115.8637 I-22.4836 J-15.7909 1257 | G3 X108.4794 Y118.5882 I-9.1916 J-9.1903 1258 | G3 X105.9354 Y119.3548 I-4.4923 J-10.304 1259 | G2 X103.2902 Y119.9282 I5.9734 J33.9428 1260 | G3 X102.1828 Y119.3995 I10.3019 J-23.0017 1261 | G3 X101.4499 Y118.4415 I0.6304 J-1.2418 1262 | G3 X101.4939 Y117.0322 I2.9472 J-0.6133 1263 | G3 X102.2051 Y115.7843 I2.7948 J0.7663 1264 | G3 X103.8132 Y114.208 I30.6824 J29.6935 1265 | G2 X105.2692 Y111.9921 I-3.7906 J-4.0769 1266 | G2 X105.8184 Y109.0847 I-9.0578 J-3.2165 1267 | G2 X105.4278 Y106.4467 I-7.4783 J-0.2406 1268 | G2 X104.6275 Y105.1616 I-3.1315 J1.0583 1269 | G3 X103.6594 Y104.1864 I77.5543 J-77.96 1270 | G1 X103.6594 Y104.1864 1271 | G4 P0 1272 | M05 S0 1273 | G1 F3000 1274 | G1 X103.9851 Y95.8245 1275 | G4 P0 1276 | M03 S256 1277 | G4 P0 1278 | G1 F100.000000 1279 | G3 X103.5589 Y95.4547 I63.9405 J-74.1174 1280 | G2 X102.9318 Y95.0543 I-1.6441 J1.8837 1281 | G2 X102.121 Y94.816 I-1.2921 J2.8987 1282 | G3 X101.0776 Y94.6061 I1.7426 J-11.3584 1283 | G3 X102.4891 Y92.421 I435.6963 J279.8972 1284 | G2 X103.8888 Y90.1524 I-48.1102 J-31.2492 1285 | G2 X105.1605 Y87.7919 I-32.3327 J-18.9413 1286 | G3 X106.4156 Y85.3475 I77.0425 J38.0138 1287 | G2 X106.3919 Y86.7673 I25.9756 J1.1422 1288 | G3 X106.2959 Y88.1014 I-8.1114 J0.0866 1289 | G3 X105.9881 Y89.2484 I-5.455 J-0.8493 1290 | G2 X105.3885 Y90.9965 I21.767 J8.4419 1291 | G2 X104.8051 Y93.0154 I111.2852 J33.2562 1292 | G3 X103.9851 Y95.8245 I-120.1862 J-33.5599 1293 | G1 X103.9851 Y95.8245 1294 | G4 P0 1295 | M05 S0 1296 | G1 F3000 1297 | G1 X0 Y0 1298 | M18 1299 | --------------------------------------------------------------------------------