├── LICENSE ├── README.md ├── example-basics ├── output │ ├── Screen Shot 2021-05-24 at 11.21.57 AM.jpg │ ├── basics_output.JPG │ └── my_first_drawing.nc └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── example-matrix_transformations ├── output │ ├── Screen Shot 2021-05-24 at 11.28.43 AM.jpg │ ├── plot.nc │ └── transformations_output.JPG └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── example-multi-color ├── output │ ├── Screen Shot 2021-05-24 at 11.31.46 AM.jpg │ ├── multicolor0.nc │ ├── multicolor1.nc │ └── multicolor_output.JPG └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── example-preview_modes ├── output │ ├── Screen Shot 2021-05-24 at 10.54.58 AM.jpg │ ├── lines.nc │ └── preview_modes_output.JPG └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── example-text_hershey ├── output │ ├── Screen Shot 2021-05-24 at 11.34.48 AM.jpg │ ├── hershey.nc │ └── hershey_output.JPG └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── example-text_true_type_font ├── output │ ├── Screen Shot 2021-05-24 at 12.58.58 PM.jpg │ ├── font_output.JPG │ └── text.nc ├── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h └── verdana.ttf ├── example-trim_advanced ├── output │ ├── Screen Shot 2021-05-24 at 1.01.35.jpg │ ├── trim_advanced.nc │ └── trim_advanced_output.JPG └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── example-trim_basic ├── output │ ├── Screen Shot 2021-05-24 at 1.04.41 PM.jpg │ ├── trim.nc │ └── trim_output.JPG └── src │ ├── main.cpp │ ├── ofApp.cpp │ └── ofApp.h ├── ofxaddons_thumbnail.png ├── src ├── Clipping.cpp ├── Clipping.hpp ├── GCodeLineGroup.h ├── GLine.cpp ├── GLine.hpp ├── ofxGCode.cpp ├── ofxGCode.hpp ├── ofxHersheyFont.cpp ├── ofxHersheyFont.h └── simplexCharacterSet.h └── topper.jpg /README.md: -------------------------------------------------------------------------------- 1 | # ofxGCode 2 | #### version 1.0 3 | 4 | ![ofxGCode drawings](https://github.com/andymasteroffish/ofxGCode/blob/master/topper.jpg) 5 | 6 | This is a G-Code library for openFrameworks 0.11.0 (also tested on oF 0.12.0), mostly made with my AxidDaw pen-plotter. 7 | 8 | This creates 2 dimensional G-code instructions, primarily for plotters. 9 | 10 | You could probably use it for laser cutters or CNC machines but you might need to edit it a little bit. Please let me know if you do! 11 | 12 | This repo is constantly evolving. I add new features as I need them. This project is by me and for me, but I think it can be helpful to other folks too! 13 | 14 | There are parts of this that are wildly inefficient. The trimming functions in particular will really slow things down when done in large numbers. Luckily you don't need to hit 90FPS when making a still image. 15 | 16 | # Structure 17 | 18 | The goal of this library is to create an environment that feels very similar to drawing with oF and other creative coding tools. It has functions to do things like draw a line or a circle or a polygon. 19 | 20 | It also includes a lot of tools to do helpful things like trim lines that are inside or outside a certain shape, and to optimize the order of lines to make a shorter overall path. 21 | 22 | The most important class is ofxGCode. This represents a single drawing and will produce a single G-code file (typically .nc). 23 | 24 | The second most important class is GLine. Everything in this library is a line and this class has a lot of helpful tools for dealing with them. 25 | 26 | See ofxGCode.hpp and ofxGLine.h for explanations. These two classes make up the bulk of the functionality and I have tried to comment them effectively. 27 | 28 | # Examples 29 | 30 | In Lieu of proper documentation, I have provided a lot of example projects that cover just about everything I do with this library. 31 | 32 | You can use a site like https://ncviewer.com/ to test the output. 33 | 34 | 35 | # Using G-code with the AxiDraw 36 | 37 | If you use an AxiDraw you may be scratching your head because the AxiDraw does not natively support G-code. 38 | 39 | To get around this, I have a python script that reads G-code and controls the AxiDraw. 40 | 41 | https://github.com/andymasteroffish/axidraw_gcode_reader 42 | 43 | 44 | 45 | # Stray Thoughts 46 | 47 | I'm sure typos abound. Sorry about that. 48 | 49 | If you like my plotter work, you can buy some of it here: https://shop.andymakes.com/ 50 | 51 | Everything on that page was made with this library! 52 | 53 | If you use this library, please let me know. I'd love to see what you make! 54 | 55 | You can email me at andy[at]andymakes[dot]com or hit me up on twitter @andy_makes 56 | 57 | 58 | # Acknowledgements 59 | 60 | This library incorporates code from these repos: 61 | 62 | ### ofxHersheyFont by Tobias Zimmer 63 | https://github.com/tobiaszimmer/ofxHersheyFont 64 | 65 | ### VST by Trammell Hudson 66 | https://github.com/osresearch/vst 67 | 68 | Thank you so much for your contributions! -------------------------------------------------------------------------------- /example-basics/output/Screen Shot 2021-05-24 at 11.21.57 AM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/example-basics/output/Screen Shot 2021-05-24 at 11.21.57 AM.jpg -------------------------------------------------------------------------------- /example-basics/output/basics_output.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/example-basics/output/basics_output.JPG -------------------------------------------------------------------------------- /example-basics/output/my_first_drawing.nc: -------------------------------------------------------------------------------- 1 | M3 S0 2 | G0 X0 Y0 3 | M3 S0 4 | G0 X1.2 Y1.7 5 | M3 S60 6 | G1 X1.2 Y0.2 7 | G1 X0.2 Y0.2 8 | G1 X0.2 Y1.7 9 | G1 X1.2 Y1.7 10 | M3 S0 11 | G0 X2.5 Y1.6 12 | M3 S60 13 | G1 X4.5 Y1.6 14 | M3 S0 15 | G0 X4.5 Y1.4 16 | M3 S60 17 | G1 X2.5 Y1.4 18 | M3 S0 19 | G0 X2.5 Y1.2 20 | M3 S60 21 | G1 X4.5 Y1.2 22 | M3 S0 23 | G0 X4.5 Y1 24 | M3 S60 25 | G1 X2.5 Y1 26 | M3 S0 27 | G0 X2.5 Y1.8 28 | M3 S60 29 | G1 X4.5 Y1.8 30 | M3 S0 31 | G0 X4.5 Y2 32 | M3 S60 33 | G1 X2.5 Y2 34 | M3 S0 35 | G0 X2.5 Y2.2 36 | M3 S60 37 | G1 X4.5 Y2.2 38 | M3 S0 39 | G0 X4.5 Y2.4 40 | M3 S60 41 | G1 X2.5 Y2.4 42 | M3 S0 43 | G0 X2.5 Y2.6 44 | M3 S60 45 | G1 X4.5 Y2.6 46 | M3 S0 47 | G0 X4.5 Y2.8 48 | M3 S60 49 | G1 X2.5 Y2.8 50 | M3 S0 51 | G0 X2.74869 Y3.46858 52 | M3 S60 53 | G1 X2.62533 Y3.49211 54 | G1 X2.5 Y3.5 55 | G1 X2.37467 Y3.49211 56 | G1 X2.25131 Y3.46858 57 | G1 X2.13188 Y3.42978 58 | G1 X2.01825 Y3.37631 59 | G1 X1.91221 Y3.30902 60 | G1 X1.81545 Y3.22897 61 | G1 X1.72949 Y3.13742 62 | G1 X1.65567 Y3.03583 63 | G1 X1.59517 Y2.92578 64 | G1 X1.54894 Y2.80902 65 | G1 X1.51771 Y2.68738 66 | G1 X1.50197 Y2.56279 67 | G1 X1.50197 Y2.43721 68 | G1 X1.51771 Y2.31262 69 | G1 X1.54894 Y2.19098 70 | G1 X1.59517 Y2.07422 71 | G1 X1.65567 Y1.96417 72 | G1 X1.72949 Y1.86258 73 | G1 X1.81545 Y1.77103 74 | G1 X1.91221 Y1.69098 75 | G1 X2.01825 Y1.62369 76 | G1 X2.13188 Y1.57022 77 | G1 X2.25131 Y1.53142 78 | G1 X2.37467 Y1.50789 79 | G1 X2.5 Y1.5 80 | G1 X2.62533 Y1.50789 81 | G1 X2.74869 Y1.53142 82 | G1 X2.86812 Y1.57022 83 | G1 X2.98175 Y1.62369 84 | G1 X3.08779 Y1.69098 85 | G1 X3.18455 Y1.77103 86 | G1 X3.27051 Y1.86258 87 | G1 X3.34433 Y1.96417 88 | G1 X3.40483 Y2.07422 89 | G1 X3.45106 Y2.19098 90 | G1 X3.48229 Y2.31262 91 | G1 X3.49803 Y2.43721 92 | G1 X3.49803 Y2.56279 93 | G1 X3.48229 Y2.68738 94 | G1 X3.45106 Y2.80902 95 | G1 X3.40483 Y2.92578 96 | G1 X3.34433 Y3.03583 97 | G1 X3.27051 Y3.13742 98 | G1 X3.18455 Y3.22897 99 | G1 X3.08779 Y3.30902 100 | G1 X2.98175 Y3.37631 101 | G1 X2.86812 Y3.42978 102 | G1 X2.74869 Y3.46858 103 | M3 S0 104 | G0 X4.00197 Y5 105 | M3 S60 106 | G1 X4.00197 Y4.93721 107 | G1 X4.01771 Y4.81262 108 | G1 X4.04894 Y4.69098 109 | G1 X4.09517 Y4.57422 110 | G1 X4.15567 Y4.46417 111 | G1 X4.22949 Y4.36258 112 | G1 X4.31545 Y4.27103 113 | G1 X4.41221 Y4.19098 114 | G1 X4.51825 Y4.12369 115 | G1 X4.63188 Y4.07022 116 | G1 X4.75131 Y4.03142 117 | G1 X4.87467 Y4.00789 118 | G1 X5 Y4 119 | G1 X5 Y4 120 | M3 S0 121 | G0 X0.5 Y4 122 | M3 S60 123 | G1 X1.3 Y3.5 124 | G1 X0.5 Y3 125 | G1 X0.5 Y4 126 | M3 S0 127 | G0 X0 Y0 128 | -------------------------------------------------------------------------------- /example-basics/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | //======================================================================== 5 | int main( ){ 6 | ofSetupOpenGL(500,500,OF_WINDOW); // <-------- setup the GL context 7 | 8 | // this kicks off the running of my app 9 | // can be OF_WINDOW or OF_FULLSCREEN 10 | // pass in width and height too: 11 | ofRunApp(new ofApp()); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /example-basics/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | //-------------------------------------------------------------- 4 | void ofApp::setup(){ 5 | 6 | ofBackground(250); 7 | 8 | //call setup before drawing anything 9 | gcode.setup(); //no arguments means we're using the default 100px per inch 10 | 11 | //the size of the page will be set to the size of our sketch 12 | 13 | //unlike normal creative coding where we redraw every frame, we draw everything once 14 | //making a line will add it to the drawing 15 | //for this reason, you typically want to do your drawing in setup or in a function that gets called once 16 | 17 | //basic shapes 18 | gcode.circle(ofGetWidth()/2, ofGetHeight()/2, 100); 19 | 20 | gcode.rect(20,20, 100, 150); 21 | 22 | //lines 23 | for (int i=0; i<10; i++){ 24 | 25 | //x1,y1 , x2,y2 26 | gcode.line(250, 100+i*20, 450, 100+i*20); 27 | } 28 | 29 | //polygons 30 | gcode.begin_shape(); 31 | gcode.vertex(50, 300); 32 | gcode.vertex(130, 350); 33 | gcode.vertex(50, 400); 34 | gcode.end_shape(true); //true here means the shape will be closed 35 | 36 | //if any part of a line is outside of the bounds it will be clipped 37 | //only a quarter of this circle is in bounds, so the rest will not be present in the final file 38 | gcode.circle(500,500, 100); 39 | 40 | 41 | //when you are done drawing you can optimize and save 42 | 43 | //sort() will do its best to reduce the travel time to draw your drawing 44 | //this will maintain all of the lines, but will change the order that they are drawn in 45 | //it will almost always speed up the plot, but if you have a specific order you want to draw your lines in you may want to skip this step 46 | //for drawings with thousands and thousands of lines, this may take a few seconds so you might not want to do this or save while you're still working out the design 47 | gcode.sort(); 48 | 49 | //save will take your drawing and convert it to G-Code, saving it in the bin/data folder 50 | //You can use a site like https://ncviewer.com/ to test the output 51 | gcode.save("my_first_drawing.nc"); 52 | } 53 | 54 | //-------------------------------------------------------------- 55 | void ofApp::update(){ 56 | 57 | } 58 | 59 | //-------------------------------------------------------------- 60 | void ofApp::draw(){ 61 | 62 | //this will demo the drawing 63 | //you can change the color by modifying gcode.demo_col 64 | gcode.draw(); 65 | } 66 | 67 | //-------------------------------------------------------------- 68 | void ofApp::keyPressed(int key){ 69 | 70 | } 71 | 72 | //-------------------------------------------------------------- 73 | void ofApp::keyReleased(int key){ 74 | 75 | } 76 | 77 | //-------------------------------------------------------------- 78 | void ofApp::mouseMoved(int x, int y ){ 79 | 80 | } 81 | 82 | //-------------------------------------------------------------- 83 | void ofApp::mouseDragged(int x, int y, int button){ 84 | 85 | } 86 | 87 | //-------------------------------------------------------------- 88 | void ofApp::mousePressed(int x, int y, int button){ 89 | 90 | } 91 | 92 | //-------------------------------------------------------------- 93 | void ofApp::mouseReleased(int x, int y, int button){ 94 | 95 | } 96 | 97 | //-------------------------------------------------------------- 98 | void ofApp::mouseEntered(int x, int y){ 99 | 100 | } 101 | 102 | //-------------------------------------------------------------- 103 | void ofApp::mouseExited(int x, int y){ 104 | 105 | } 106 | 107 | //-------------------------------------------------------------- 108 | void ofApp::windowResized(int w, int h){ 109 | 110 | } 111 | 112 | //-------------------------------------------------------------- 113 | void ofApp::gotMessage(ofMessage msg){ 114 | 115 | } 116 | 117 | //-------------------------------------------------------------- 118 | void ofApp::dragEvent(ofDragInfo dragInfo){ 119 | 120 | } 121 | -------------------------------------------------------------------------------- /example-basics/src/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "ofxGCode.hpp" 5 | 6 | class ofApp : public ofBaseApp{ 7 | 8 | public: 9 | void setup(); 10 | void update(); 11 | void draw(); 12 | 13 | void keyPressed(int key); 14 | void keyReleased(int key); 15 | void mouseMoved(int x, int y ); 16 | void mouseDragged(int x, int y, int button); 17 | void mousePressed(int x, int y, int button); 18 | void mouseReleased(int x, int y, int button); 19 | void mouseEntered(int x, int y); 20 | void mouseExited(int x, int y); 21 | void windowResized(int w, int h); 22 | void dragEvent(ofDragInfo dragInfo); 23 | void gotMessage(ofMessage msg); 24 | 25 | //you need at least one ofxGCode object 26 | ofxGCode gcode; 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /example-matrix_transformations/output/Screen Shot 2021-05-24 at 11.28.43 AM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/example-matrix_transformations/output/Screen Shot 2021-05-24 at 11.28.43 AM.jpg -------------------------------------------------------------------------------- /example-matrix_transformations/output/plot.nc: -------------------------------------------------------------------------------- 1 | M3 S0 2 | G0 X0 Y0 3 | M3 S0 4 | G0 X3.5 Y3.5 5 | M3 S60 6 | G1 X3.5 Y2.5 7 | G1 X2.5 Y2.5 8 | G1 X2.5 Y3.5 9 | G1 X3.5 Y3.5 10 | M3 S0 11 | G0 X3.67138 Y3.82113 12 | M3 S60 13 | G1 X3.82113 Y2.32862 14 | G1 X2.32862 Y2.17887 15 | G1 X2.17887 Y3.67138 16 | G1 X3.67138 Y3.82113 17 | M3 S0 18 | G0 X3.7814 Y4.17874 19 | M3 S60 20 | G1 X4.17874 Y2.2186 21 | G1 X2.2186 Y1.82126 22 | G1 X1.82126 Y3.7814 23 | G1 X3.7814 Y4.17874 24 | M3 S0 25 | G0 X3.82477 Y4.56357 26 | M3 S60 27 | G1 X4.56357 Y2.17523 28 | G1 X2.17523 Y1.43643 29 | G1 X1.43643 Y3.82477 30 | G1 X3.82477 Y4.56357 31 | M3 S0 32 | G0 X3.79746 Y4.96572 33 | M3 S60 34 | G1 X4.96572 Y2.20254 35 | G1 X2.20254 Y1.03428 36 | G1 X1.03428 Y3.79746 37 | G1 X3.79746 Y4.96572 38 | M3 S0 39 | G0 X3.69677 Y5.37476 40 | M3 S60 41 | G1 X5.37476 Y2.30323 42 | G1 X2.30323 Y0.625236 43 | G1 X0.625236 Y3.69677 44 | G1 X3.69677 Y5.37476 45 | M3 S0 46 | G0 X3.52139 Y5.77996 47 | M3 S60 48 | G1 X5.77996 Y2.47861 49 | G1 X2.47861 Y0.220044 50 | G1 X0.220044 Y3.52139 51 | G1 X3.52139 Y5.77996 52 | M3 S0 53 | G0 X0 Y0 54 | -------------------------------------------------------------------------------- /example-matrix_transformations/output/transformations_output.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/example-matrix_transformations/output/transformations_output.JPG -------------------------------------------------------------------------------- /example-matrix_transformations/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | //======================================================================== 5 | int main( ){ 6 | ofSetupOpenGL(600,600,OF_WINDOW); // <-------- setup the GL context 7 | 8 | // this kicks off the running of my app 9 | // can be OF_WINDOW or OF_FULLSCREEN 10 | // pass in width and height too: 11 | ofRunApp(new ofApp()); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /example-matrix_transformations/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | //-------------------------------------------------------------- 4 | void ofApp::setup(){ 5 | ofBackground(250); 6 | 7 | //setup the drawing 8 | gcode.setup(); 9 | 10 | //push & pop can be used outside of draw 11 | //as long as you only use 2D transformations, the lines you add will be affected by your matrix 12 | //it should behave more or less identically to how you would normally use them 13 | 14 | for (int i=0; i<7; i++){ 15 | ofPushMatrix(); 16 | 17 | //adjusting the scale, rotation & position 18 | ofTranslate(ofGetWidth()/2, ofGetHeight()/2); 19 | ofRotateRad(i * 0.1); 20 | ofScale(1 + i * 0.5); 21 | 22 | //drawing a square centered on the origin 23 | gcode.rect(-50, -50, 100, 100); 24 | 25 | ofPopMatrix(); 26 | } 27 | 28 | //optimize the plot and save 29 | gcode.sort(); 30 | gcode.save("plot.nc"); 31 | 32 | } 33 | 34 | //-------------------------------------------------------------- 35 | void ofApp::update(){ 36 | 37 | } 38 | 39 | //-------------------------------------------------------------- 40 | void ofApp::draw(){ 41 | //demo the drawing 42 | gcode.draw(); 43 | } 44 | 45 | //-------------------------------------------------------------- 46 | void ofApp::keyPressed(int key){ 47 | 48 | } 49 | 50 | //-------------------------------------------------------------- 51 | void ofApp::keyReleased(int key){ 52 | 53 | } 54 | 55 | //-------------------------------------------------------------- 56 | void ofApp::mouseMoved(int x, int y ){ 57 | 58 | } 59 | 60 | //-------------------------------------------------------------- 61 | void ofApp::mouseDragged(int x, int y, int button){ 62 | 63 | } 64 | 65 | //-------------------------------------------------------------- 66 | void ofApp::mousePressed(int x, int y, int button){ 67 | 68 | } 69 | 70 | //-------------------------------------------------------------- 71 | void ofApp::mouseReleased(int x, int y, int button){ 72 | 73 | } 74 | 75 | //-------------------------------------------------------------- 76 | void ofApp::mouseEntered(int x, int y){ 77 | 78 | } 79 | 80 | //-------------------------------------------------------------- 81 | void ofApp::mouseExited(int x, int y){ 82 | 83 | } 84 | 85 | //-------------------------------------------------------------- 86 | void ofApp::windowResized(int w, int h){ 87 | 88 | } 89 | 90 | //-------------------------------------------------------------- 91 | void ofApp::gotMessage(ofMessage msg){ 92 | 93 | } 94 | 95 | //-------------------------------------------------------------- 96 | void ofApp::dragEvent(ofDragInfo dragInfo){ 97 | 98 | } 99 | -------------------------------------------------------------------------------- /example-matrix_transformations/src/ofApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | #include "ofxGCode.hpp" 5 | 6 | class ofApp : public ofBaseApp{ 7 | 8 | public: 9 | void setup(); 10 | void update(); 11 | void draw(); 12 | 13 | void keyPressed(int key); 14 | void keyReleased(int key); 15 | void mouseMoved(int x, int y ); 16 | void mouseDragged(int x, int y, int button); 17 | void mousePressed(int x, int y, int button); 18 | void mouseReleased(int x, int y, int button); 19 | void mouseEntered(int x, int y); 20 | void mouseExited(int x, int y); 21 | void windowResized(int w, int h); 22 | void dragEvent(ofDragInfo dragInfo); 23 | void gotMessage(ofMessage msg); 24 | 25 | ofxGCode gcode; 26 | }; 27 | -------------------------------------------------------------------------------- /example-multi-color/output/Screen Shot 2021-05-24 at 11.31.46 AM.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/example-multi-color/output/Screen Shot 2021-05-24 at 11.31.46 AM.jpg -------------------------------------------------------------------------------- /example-multi-color/output/multicolor0.nc: -------------------------------------------------------------------------------- 1 | M3 S0 2 | G0 X0 Y0 3 | M3 S0 4 | G0 X1.67408 Y2.17801 5 | M3 S60 6 | G1 X1.58773 Y2.19448 7 | G1 X1.5 Y2.2 8 | G1 X1.41227 Y2.19448 9 | G1 X1.32592 Y2.17801 10 | G1 X1.24231 Y2.15084 11 | G1 X1.16277 Y2.11341 12 | G1 X1.08855 Y2.06631 13 | G1 X1.02082 Y2.01028 14 | G1 X0.960641 Y1.9462 15 | G1 X0.90897 Y1.87508 16 | G1 X0.866621 Y1.79805 17 | G1 X0.83426 Y1.71631 18 | G1 X0.812399 Y1.63117 19 | G1 X0.801381 Y1.54395 20 | G1 X0.801381 Y1.45605 21 | G1 X0.812399 Y1.36883 22 | G1 X0.83426 Y1.28369 23 | G1 X0.866621 Y1.20195 24 | G1 X0.90897 Y1.12492 25 | G1 X0.960641 Y1.0538 26 | G1 X1.02082 Y0.989722 27 | G1 X1.08855 Y0.933688 28 | G1 X1.16277 Y0.886585 29 | G1 X1.24231 Y0.849157 30 | G1 X1.32592 Y0.821992 31 | G1 X1.41227 Y0.80552 32 | G1 X1.5 Y0.8 33 | G1 X1.58773 Y0.80552 34 | G1 X1.67408 Y0.821992 35 | G1 X1.75769 Y0.849156 36 | G1 X1.83723 Y0.886585 37 | G1 X1.91145 Y0.933688 38 | G1 X1.97918 Y0.989722 39 | G1 X2.03936 Y1.0538 40 | G1 X2.09103 Y1.12492 41 | G1 X2.13338 Y1.20195 42 | G1 X2.16574 Y1.28369 43 | G1 X2.1876 Y1.36883 44 | G1 X2.19862 Y1.45605 45 | G1 X2.19862 Y1.54395 46 | G1 X2.1876 Y1.63117 47 | G1 X2.16574 Y1.71631 48 | G1 X2.13338 Y1.79805 49 | G1 X2.09103 Y1.87508 50 | G1 X2.03936 Y1.9462 51 | G1 X1.97918 Y2.01028 52 | G1 X1.91145 Y2.06631 53 | G1 X1.83723 Y2.11341 54 | G1 X1.75769 Y2.15084 55 | G1 X1.67408 Y2.17801 56 | M3 S0 57 | G0 X2.87408 Y3.37801 58 | M3 S60 59 | G1 X2.78773 Y3.39448 60 | G1 X2.7 Y3.4 61 | G1 X2.61227 Y3.39448 62 | G1 X2.52592 Y3.37801 63 | G1 X2.44231 Y3.35084 64 | G1 X2.36277 Y3.31341 65 | G1 X2.28855 Y3.26631 66 | G1 X2.22082 Y3.21028 67 | G1 X2.16064 Y3.1462 68 | G1 X2.10897 Y3.07508 69 | G1 X2.06662 Y2.99805 70 | G1 X2.03426 Y2.91631 71 | G1 X2.0124 Y2.83117 72 | G1 X2.00138 Y2.74395 73 | G1 X2.00138 Y2.65605 74 | G1 X2.0124 Y2.56883 75 | G1 X2.03426 Y2.48369 76 | G1 X2.06662 Y2.40195 77 | G1 X2.10897 Y2.32492 78 | G1 X2.16064 Y2.2538 79 | G1 X2.22082 Y2.18972 80 | G1 X2.28855 Y2.13369 81 | G1 X2.36277 Y2.08659 82 | G1 X2.44231 Y2.04916 83 | G1 X2.52592 Y2.02199 84 | G1 X2.61227 Y2.00552 85 | G1 X2.7 Y2 86 | G1 X2.78773 Y2.00552 87 | G1 X2.87408 Y2.02199 88 | G1 X2.95769 Y2.04916 89 | G1 X3.03723 Y2.08659 90 | G1 X3.11145 Y2.13369 91 | G1 X3.17918 Y2.18972 92 | G1 X3.23936 Y2.2538 93 | G1 X3.29103 Y2.32492 94 | G1 X3.33338 Y2.40195 95 | G1 X3.36574 Y2.48369 96 | G1 X3.3876 Y2.56883 97 | G1 X3.39862 Y2.65605 98 | G1 X3.39862 Y2.74395 99 | G1 X3.3876 Y2.83117 100 | G1 X3.36574 Y2.91631 101 | G1 X3.33338 Y2.99805 102 | G1 X3.29103 Y3.07508 103 | G1 X3.23936 Y3.1462 104 | G1 X3.17918 Y3.21028 105 | G1 X3.11145 Y3.26631 106 | G1 X3.03723 Y3.31341 107 | G1 X2.95769 Y3.35084 108 | G1 X2.87408 Y3.37801 109 | M3 S0 110 | G0 X4.07408 Y4.57801 111 | M3 S60 112 | G1 X3.98773 Y4.59448 113 | G1 X3.9 Y4.6 114 | G1 X3.81227 Y4.59448 115 | G1 X3.72592 Y4.57801 116 | G1 X3.64231 Y4.55084 117 | G1 X3.56277 Y4.51341 118 | G1 X3.48855 Y4.46631 119 | G1 X3.42082 Y4.41028 120 | G1 X3.36064 Y4.3462 121 | G1 X3.30897 Y4.27508 122 | G1 X3.26662 Y4.19805 123 | G1 X3.23426 Y4.11631 124 | G1 X3.2124 Y4.03117 125 | G1 X3.20138 Y3.94395 126 | G1 X3.20138 Y3.85605 127 | G1 X3.2124 Y3.76883 128 | G1 X3.23426 Y3.68369 129 | G1 X3.26662 Y3.60195 130 | G1 X3.30897 Y3.52492 131 | G1 X3.36064 Y3.4538 132 | G1 X3.42082 Y3.38972 133 | G1 X3.48855 Y3.33369 134 | G1 X3.56277 Y3.28659 135 | G1 X3.64231 Y3.24916 136 | G1 X3.72592 Y3.22199 137 | G1 X3.81227 Y3.20552 138 | G1 X3.9 Y3.2 139 | G1 X3.98773 Y3.20552 140 | G1 X4.07408 Y3.22199 141 | G1 X4.15769 Y3.24916 142 | G1 X4.23723 Y3.28659 143 | G1 X4.31145 Y3.33369 144 | G1 X4.37918 Y3.38972 145 | G1 X4.43936 Y3.4538 146 | G1 X4.49103 Y3.52492 147 | G1 X4.53338 Y3.60195 148 | G1 X4.56574 Y3.68369 149 | G1 X4.5876 Y3.76883 150 | G1 X4.59862 Y3.85605 151 | G1 X4.59862 Y3.94395 152 | G1 X4.5876 Y4.03117 153 | G1 X4.56574 Y4.11631 154 | G1 X4.53338 Y4.19805 155 | G1 X4.49103 Y4.27508 156 | G1 X4.43936 Y4.3462 157 | G1 X4.37918 Y4.41028 158 | G1 X4.31145 Y4.46631 159 | G1 X4.23723 Y4.51341 160 | G1 X4.15769 Y4.55084 161 | G1 X4.07408 Y4.57801 162 | M3 S0 163 | G0 X5.5 Y5.5 164 | M3 S60 165 | G1 X5.5 Y0.5 166 | G1 X0.5 Y0.5 167 | G1 X0.5 Y5.5 168 | G1 X5.5 Y5.5 169 | M3 S0 170 | G0 X0 Y0 171 | -------------------------------------------------------------------------------- /example-multi-color/output/multicolor1.nc: -------------------------------------------------------------------------------- 1 | M3 S0 2 | G0 X0 Y0 3 | M3 S0 4 | G0 X2.27408 Y2.77801 5 | M3 S60 6 | G1 X2.18773 Y2.79448 7 | G1 X2.1 Y2.8 8 | G1 X2.01227 Y2.79448 9 | G1 X1.92592 Y2.77801 10 | G1 X1.84231 Y2.75084 11 | G1 X1.76277 Y2.71341 12 | G1 X1.68855 Y2.66631 13 | G1 X1.62082 Y2.61028 14 | G1 X1.56064 Y2.5462 15 | G1 X1.50897 Y2.47508 16 | G1 X1.46662 Y2.39805 17 | G1 X1.43426 Y2.31631 18 | G1 X1.4124 Y2.23117 19 | G1 X1.40138 Y2.14395 20 | G1 X1.40138 Y2.05605 21 | G1 X1.4124 Y1.96883 22 | G1 X1.43426 Y1.88369 23 | G1 X1.46662 Y1.80195 24 | G1 X1.50897 Y1.72492 25 | G1 X1.56064 Y1.6538 26 | G1 X1.62082 Y1.58972 27 | G1 X1.68855 Y1.53369 28 | G1 X1.76277 Y1.48659 29 | G1 X1.84231 Y1.44916 30 | G1 X1.92592 Y1.42199 31 | G1 X2.01227 Y1.40552 32 | G1 X2.1 Y1.4 33 | G1 X2.18773 Y1.40552 34 | G1 X2.27408 Y1.42199 35 | G1 X2.35769 Y1.44916 36 | G1 X2.43723 Y1.48659 37 | G1 X2.51145 Y1.53369 38 | G1 X2.57918 Y1.58972 39 | G1 X2.63936 Y1.6538 40 | G1 X2.69103 Y1.72492 41 | G1 X2.73338 Y1.80195 42 | G1 X2.76574 Y1.88369 43 | G1 X2.7876 Y1.96883 44 | G1 X2.79862 Y2.05605 45 | G1 X2.79862 Y2.14395 46 | G1 X2.7876 Y2.23117 47 | G1 X2.76574 Y2.31631 48 | G1 X2.73338 Y2.39805 49 | G1 X2.69103 Y2.47508 50 | G1 X2.63936 Y2.5462 51 | G1 X2.57918 Y2.61028 52 | G1 X2.51145 Y2.66631 53 | G1 X2.43723 Y2.71341 54 | G1 X2.35769 Y2.75084 55 | G1 X2.27408 Y2.77801 56 | M3 S0 57 | G0 X3.47408 Y3.97801 58 | M3 S60 59 | G1 X3.38773 Y3.99448 60 | G1 X3.3 Y4 61 | G1 X3.21227 Y3.99448 62 | G1 X3.12592 Y3.97801 63 | G1 X3.04231 Y3.95084 64 | G1 X2.96277 Y3.91341 65 | G1 X2.88855 Y3.86631 66 | G1 X2.82082 Y3.81028 67 | G1 X2.76064 Y3.7462 68 | G1 X2.70897 Y3.67508 69 | G1 X2.66662 Y3.59805 70 | G1 X2.63426 Y3.51631 71 | G1 X2.6124 Y3.43117 72 | G1 X2.60138 Y3.34395 73 | G1 X2.60138 Y3.25605 74 | G1 X2.6124 Y3.16883 75 | G1 X2.63426 Y3.08369 76 | G1 X2.66662 Y3.00195 77 | G1 X2.70897 Y2.92492 78 | G1 X2.76064 Y2.8538 79 | G1 X2.82082 Y2.78972 80 | G1 X2.88855 Y2.73369 81 | G1 X2.96277 Y2.68659 82 | G1 X3.04231 Y2.64916 83 | G1 X3.12592 Y2.62199 84 | G1 X3.21227 Y2.60552 85 | G1 X3.3 Y2.6 86 | G1 X3.38773 Y2.60552 87 | G1 X3.47408 Y2.62199 88 | G1 X3.55769 Y2.64916 89 | G1 X3.63723 Y2.68659 90 | G1 X3.71145 Y2.73369 91 | G1 X3.77918 Y2.78972 92 | G1 X3.83936 Y2.8538 93 | G1 X3.89103 Y2.92492 94 | G1 X3.93338 Y3.00195 95 | G1 X3.96574 Y3.08369 96 | G1 X3.9876 Y3.16883 97 | G1 X3.99862 Y3.25605 98 | G1 X3.99862 Y3.34395 99 | G1 X3.9876 Y3.43117 100 | G1 X3.96574 Y3.51631 101 | G1 X3.93338 Y3.59805 102 | G1 X3.89103 Y3.67508 103 | G1 X3.83936 Y3.7462 104 | G1 X3.77918 Y3.81028 105 | G1 X3.71145 Y3.86631 106 | G1 X3.63723 Y3.91341 107 | G1 X3.55769 Y3.95084 108 | G1 X3.47408 Y3.97801 109 | M3 S0 110 | G0 X4.67408 Y5.17801 111 | M3 S60 112 | G1 X4.58773 Y5.19448 113 | G1 X4.5 Y5.2 114 | G1 X4.41227 Y5.19448 115 | G1 X4.32592 Y5.17801 116 | G1 X4.24231 Y5.15084 117 | G1 X4.16277 Y5.11341 118 | G1 X4.08855 Y5.06631 119 | G1 X4.02082 Y5.01028 120 | G1 X3.96064 Y4.9462 121 | G1 X3.90897 Y4.87508 122 | G1 X3.86662 Y4.79805 123 | G1 X3.83426 Y4.71631 124 | G1 X3.8124 Y4.63117 125 | G1 X3.80138 Y4.54395 126 | G1 X3.80138 Y4.45605 127 | G1 X3.8124 Y4.36883 128 | G1 X3.83426 Y4.28369 129 | G1 X3.86662 Y4.20195 130 | G1 X3.90897 Y4.12492 131 | G1 X3.96064 Y4.0538 132 | G1 X4.02082 Y3.98972 133 | G1 X4.08855 Y3.93369 134 | G1 X4.16277 Y3.88659 135 | G1 X4.24231 Y3.84916 136 | G1 X4.32592 Y3.82199 137 | G1 X4.41227 Y3.80552 138 | G1 X4.5 Y3.8 139 | G1 X4.58773 Y3.80552 140 | G1 X4.67408 Y3.82199 141 | G1 X4.75769 Y3.84916 142 | G1 X4.83723 Y3.88659 143 | G1 X4.91145 Y3.93369 144 | G1 X4.97918 Y3.98972 145 | G1 X5.03936 Y4.0538 146 | G1 X5.09103 Y4.12492 147 | G1 X5.13338 Y4.20195 148 | G1 X5.16574 Y4.28369 149 | G1 X5.1876 Y4.36883 150 | G1 X5.19862 Y4.45605 151 | G1 X5.19862 Y4.54395 152 | G1 X5.1876 Y4.63117 153 | G1 X5.16574 Y4.71631 154 | G1 X5.13338 Y4.79805 155 | G1 X5.09103 Y4.87508 156 | G1 X5.03936 Y4.9462 157 | G1 X4.97918 Y5.01028 158 | G1 X4.91145 Y5.06631 159 | G1 X4.83723 Y5.11341 160 | G1 X4.75769 Y5.15084 161 | G1 X4.67408 Y5.17801 162 | M3 S0 163 | G0 X5.4 Y5.4 164 | M3 S60 165 | G1 X5.4 Y0.6 166 | G1 X0.6 Y0.6 167 | G1 X0.6 Y5.4 168 | G1 X5.4 Y5.4 169 | M3 S0 170 | G0 X0 Y0 171 | -------------------------------------------------------------------------------- /example-multi-color/output/multicolor_output.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/example-multi-color/output/multicolor_output.JPG -------------------------------------------------------------------------------- /example-multi-color/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | #include "ofApp.h" 3 | 4 | //======================================================================== 5 | int main( ){ 6 | ofSetupOpenGL(600,600,OF_WINDOW); // <-------- setup the GL context 7 | 8 | // this kicks off the running of my app 9 | // can be OF_WINDOW or OF_FULLSCREEN 10 | // pass in width and height too: 11 | ofRunApp(new ofApp()); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /example-multi-color/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofApp.h" 2 | 3 | //-------------------------------------------------------------- 4 | void ofApp::setup(){ 5 | 6 | ofBackground(250); 7 | 8 | //all ofxGCode objects need to be setup 9 | for(int i=0; i circle_pnts = ofxGCode::get_circle_pnts(circle_pos, circle_size, 70); 35 | //the last argument in the above code is the circle resolution: how many points will make up the circle 36 | 37 | //draw the circle 38 | gcode.polygon(circle_pnts); 39 | 40 | //create a vector of GLines for the vertical lines 41 | //At its core a GLine just stores two points to describe a line. It has a lot of useful functions built in though 42 | //All lines inside of ofxGCode are stored as GLines 43 | //check GLine.h for more detailed info 44 | vector lines; 45 | 46 | //the lines get closer with each circle 47 | float spacing = 18 - i * 5; 48 | 49 | //go through and make vertical lines across the whole screen 50 | //(the trim process would be more efficient if we only drew lines that would be over the circle, but that's OK. One of the nice things about doing plotter art is that you can often afford to be a little inefficient) 51 | for (float x=0; x polygon; 32 | 33 | //changing how we want to trim 34 | int trim_mode; 35 | 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /ofxaddons_thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andymasteroffish/ofxGCode/3baf9e591fc2914ef96fd2090b216bc24ae11af5/ofxaddons_thumbnail.png -------------------------------------------------------------------------------- /src/Clipping.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Clipping.cpp 3 | // 4 | // Created by Andrew Wallace on 4/8/19. Based on code by Trammel Hudson. 5 | // 6 | 7 | // This code is entirely based on the Processing VST library by Trammell Hudson 8 | // https://github.com/osresearch/vst 9 | 10 | #include "Clipping.hpp" 11 | 12 | void Clipping::setup(ofVec2f p0, ofVec2f p1) { 13 | min.set(MIN(p0.x, p1.x), MIN(p0.y, p1.y)); 14 | max.set(MAX(p0.x, p1.x), MAX(p0.y, p1.y)); 15 | } 16 | 17 | int Clipping::compute_code(ofVec2f p) { 18 | int code = INSIDE; 19 | 20 | if (p.x < min.x) 21 | code |= LEFT; 22 | if (p.x > max.x) 23 | code |= RIGHT; 24 | if (p.y < min.y) 25 | code |= BOTTOM; 26 | if (p.y > max.y) 27 | code |= TOP; 28 | 29 | return code; 30 | } 31 | 32 | float Clipping::intercept(float y, float x0, float y0, float x1, float y1) { 33 | return x0 + (x1 - x0) * (y - y0) / (y1 - y0); 34 | } 35 | 36 | // Clip a line segment from p0 to p1 by the 37 | // rectangular clipping region min/max. 38 | // p0 and p1 will be modified to be in the region 39 | // returns true if the line segment is visible at all 40 | bool Clipping::clip(ofVec2f &p0, ofVec2f &p1) { 41 | int code0 = compute_code(p0); 42 | int code1 = compute_code(p1); 43 | 44 | while (true) { 45 | // both are inside the clipping region. 46 | // accept them as is. 47 | if ((code0 | code1) == 0) 48 | return true; 49 | 50 | // both are outside the clipping region 51 | // and do not cross the visible area. 52 | // reject the point. 53 | if ((code0 & code1) != 0) 54 | return false; 55 | 56 | // At least one endpoint is outside 57 | // the region. 58 | int code = code0 != 0 ? code0 : code1; 59 | float x = 0, y = 0; 60 | 61 | if ((code & TOP) != 0) { 62 | // point is above the clip rectangle 63 | y = max.y; 64 | x = intercept(y, p0.x, p0.y, p1.x, p1.y); 65 | } else if ((code & BOTTOM) != 0) { 66 | // point is below the clip rectangle 67 | y = min.y; 68 | x = intercept(y, p0.x, p0.y, p1.x, p1.y); 69 | } else if ((code & RIGHT) != 0) { 70 | // point is to the right of clip rectangle 71 | x = max.x; 72 | y = intercept(x, p0.y, p0.x, p1.y, p1.x); 73 | } else if ((code & LEFT) != 0) { 74 | // point is to the left of clip rectangle 75 | x = min.x; 76 | y = intercept(x, p0.y, p0.x, p1.y, p1.x); 77 | } 78 | 79 | // Now we move outside point to intersection point to clip 80 | // and get ready for next pass. 81 | if (code == code0) { 82 | p0.x = x; 83 | p0.y = y; 84 | code0 = compute_code(p0); 85 | } else { 86 | p1.x = x; 87 | p1.y = y; 88 | code1 = compute_code(p1); 89 | } 90 | } 91 | } 92 | 93 | bool Clipping::check_point(ofVec2f pnt){ 94 | return compute_code(pnt) == INSIDE; 95 | } 96 | -------------------------------------------------------------------------------- /src/Clipping.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Clipping.hpp 3 | // 4 | // Created by Andrew Wallace on 4/8/19. Based on code by Trammel Hudson. 5 | // 6 | 7 | // This code is entirely based on the Processing VST library by Trammell Hudson 8 | // https://github.com/osresearch/vst 9 | 10 | #ifndef Clipping_hpp 11 | #define Clipping_hpp 12 | 13 | #include "ofMain.h" 14 | 15 | class Clipping{ 16 | public: 17 | 18 | ofVec3f min; 19 | ofVec3f max; 20 | 21 | const static int INSIDE = 0; 22 | const static int LEFT = 1; 23 | const static int RIGHT = 2; 24 | const static int BOTTOM = 4; 25 | const static int TOP = 8; 26 | 27 | void setup(ofVec2f p0, ofVec2f p1); 28 | int compute_code(ofVec2f p); 29 | float intercept(float y, float x0, float y0, float x1, float y1); 30 | bool clip(ofVec2f &p0, ofVec2f &p1); 31 | bool check_point(ofVec2f pnt); 32 | }; 33 | 34 | #endif /* Clipping_hpp */ 35 | -------------------------------------------------------------------------------- /src/GCodeLineGroup.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCodeLineGroup.h 3 | // ofxgcode_testing 4 | // 5 | // Created by Andrew Wallace on 11/3/20. 6 | // 7 | // Line Groups are used during the sorting process 8 | // They represent continous sets of lines 9 | 10 | 11 | #ifndef GCodeLineGroup_h 12 | #define GCodeLineGroup_h 13 | 14 | #include "GLine.hpp" 15 | #include "ofMain.h" 16 | 17 | class GCodeLineGroup{ 18 | public: 19 | ofVec2f start_pos, end_pos; 20 | vector lines; 21 | bool do_not_reverse; 22 | 23 | void clear(){ 24 | lines.clear(); 25 | do_not_reverse = false; 26 | } 27 | 28 | void add_to_front(GLine line){ 29 | lines.insert( lines.begin(), line); 30 | start_pos = line.a; 31 | if (lines.size() == 1) end_pos = line.b; 32 | 33 | if (line.do_not_reverse) do_not_reverse = true; 34 | } 35 | 36 | void add_to_back(GLine line){ 37 | lines.push_back(line); 38 | end_pos = line.b; 39 | if (lines.size() == 1) start_pos = line.a; 40 | 41 | if (line.do_not_reverse) do_not_reverse = true; 42 | } 43 | 44 | 45 | }; 46 | 47 | 48 | #endif /* GCodeLineGroup_h */ 49 | -------------------------------------------------------------------------------- /src/GLine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // GLine.cpp 3 | // 4 | // Created by Andrew Wallace on 2/16/20. 5 | // 6 | 7 | #include "GLine.hpp" 8 | 9 | //--- Constructors 10 | 11 | GLine::GLine(){ 12 | a.x = 0; 13 | a.y = 0; 14 | b.x = 0; 15 | b.y = 0; 16 | skip_me = false; 17 | do_not_reverse = false; 18 | is_locked = false; 19 | } 20 | 21 | GLine::GLine(ofVec2f _a, ofVec2f _b){ 22 | set(_a, _b); 23 | skip_me = false; 24 | do_not_reverse = false; 25 | is_locked = false; 26 | } 27 | 28 | GLine::GLine(float x1, float y1, float x2, float y2){ 29 | set(x1,y1, x2,y2); 30 | skip_me = false; 31 | do_not_reverse = false; 32 | is_locked = false; 33 | } 34 | 35 | //--- Set functions 36 | 37 | void GLine::set(ofVec2f _a, ofVec2f _b){ 38 | set(_a.x, _a.y, _b.x, _b.y); 39 | } 40 | 41 | void GLine::set(float x1, float y1, float x2, float y2){ 42 | a.x = x1; 43 | a.y = y1; 44 | b.x = x2; 45 | b.y = y2; 46 | } 47 | 48 | void GLine::set(GLine other){ 49 | set(other.a, other.b); 50 | } 51 | 52 | void GLine::set_locked(bool val){ 53 | is_locked = val; 54 | } 55 | 56 | //--- Drawing 57 | 58 | void GLine::draw(){ 59 | ofDrawLine(a,b); 60 | 61 | //if this line has no distance, draw a little circle to mark it 62 | if (a == b){ 63 | ofDrawCircle(a.x, a.y, 0.5); 64 | } 65 | } 66 | 67 | 68 | //--- Utilities 69 | 70 | //returns a new line with the same length and angle of the current line, but offset along X and Y. This will not change the line it is called on 71 | GLine GLine::get_offset(ofVec2f offset){ 72 | GLine line; 73 | line.set(a,b); 74 | line.a += offset; 75 | line.b += offset; 76 | return line; 77 | } 78 | 79 | vector GLine::get_segments(int num_segments){ 80 | vector lines; 81 | ofVec2f prev_pos = ofVec2f(a); 82 | for (int i=1; i<=num_segments; i++){ 83 | float prc = (float)i/(float)num_segments; 84 | ofVec2f pos = (1.0-prc)*a + prc*b; 85 | GLine line; 86 | line.set(prev_pos, pos ); 87 | lines.push_back(line); 88 | prev_pos.set(pos); 89 | } 90 | return lines; 91 | } 92 | 93 | float GLine::get_length(){ 94 | return a.distance(b); 95 | } 96 | 97 | /// returns pnt a or b 98 | /// 0 = a, 1 (or anything else) = b 99 | ofVec2f GLine::get_pnt_by_index(int ind){ 100 | if (ind == 0) return a; 101 | 102 | return b; 103 | } 104 | 105 | //returns a box around the line 106 | vector GLine::get_bounds(float padding){ 107 | vector bounds; 108 | 109 | 110 | float angle = atan2(b.y-a.y, b.x-a.x); 111 | float tan_angle = angle + PI/2; 112 | 113 | ofVec2f push_a = ofVec2f(a); 114 | push_a.x -= cos(angle) * padding; 115 | push_a.y -= sin(angle) * padding; 116 | 117 | ofVec2f push_b = ofVec2f(b); 118 | push_b.x += cos(angle) * padding; 119 | push_b.y += sin(angle) * padding; 120 | 121 | bounds.push_back(push_a); 122 | bounds.push_back(push_a); 123 | bounds.push_back(push_b); 124 | bounds.push_back(push_b); 125 | 126 | bounds[0].x += cos(tan_angle) * padding; 127 | bounds[0].y += sin(tan_angle) * padding; 128 | bounds[1].x -= cos(tan_angle) * padding; 129 | bounds[1].y -= sin(tan_angle) * padding; 130 | 131 | bounds[2].x -= cos(tan_angle) * padding; 132 | bounds[2].y -= sin(tan_angle) * padding; 133 | bounds[3].x += cos(tan_angle) * padding; 134 | bounds[3].y += sin(tan_angle) * padding; 135 | 136 | return bounds; 137 | } 138 | 139 | //--- Intersection 140 | 141 | //returns true if line intersects 142 | bool GLine::intersects(GLine other){ 143 | ofPoint out; 144 | ofPoint my_a = a; 145 | ofPoint my_b = b; 146 | ofPoint other_a = other.a; 147 | ofPoint other_b = other.b; 148 | if (ofLineSegmentIntersection(my_a, my_b, other_a, other_b, out)){ 149 | return true; 150 | } 151 | return false; 152 | } 153 | 154 | //returns true if line intersects, and writes the intersect point to the passed ofVec2f 155 | bool GLine::intersects(GLine other, ofVec2f &intersect_pnt){ 156 | ofPoint out; //ofLineSegmentIntersection takes ofPoint, but I don't like 'em 157 | ofPoint my_a = a; 158 | ofPoint my_b = b; 159 | ofPoint other_a = other.a; 160 | ofPoint other_b = other.b; 161 | if (ofLineSegmentIntersection(my_a, my_b, other_a, other_b, out)){ 162 | intersect_pnt.x = out.x; 163 | intersect_pnt.y = out.y; 164 | return true; 165 | } 166 | return false; 167 | } 168 | 169 | 170 | //--- Clipping 171 | 172 | //this always assumes A is the side that will remain 173 | bool GLine::clip_to_other_line(float other_a_x, float other_a_y, float other_b_x, float other_b_y){ 174 | ofPoint out; 175 | ofPoint my_a = a; 176 | ofPoint my_b = b; 177 | ofPoint other_a = ofPoint(other_a_x, other_a_y); 178 | ofPoint other_b = ofPoint(other_b_x, other_b_y); 179 | if (ofLineSegmentIntersection(my_a, my_b, other_a, other_b, out)){ 180 | b = out; 181 | return true; 182 | } 183 | return false; 184 | } 185 | bool GLine::clip_to_other_line(GLine other){ 186 | return clip_to_other_line(other.a.x, other.a.y, other.b.x, other.b.y); 187 | } 188 | bool GLine::clip_to_other_line(ofVec2f other_a, ofVec2f other_b){ 189 | return clip_to_other_line(other_a.x, other_a.y, other_b.x, other_b.y); 190 | } 191 | 192 | void GLine::swap_a_and_b(){ 193 | ofVec2f temp = ofVec2f(a); 194 | a.set(b); 195 | b.set(temp); 196 | } 197 | 198 | //--- Trimming lines 199 | 200 | //removes all parts of this line inside the polygon 201 | //if a list is provided, any new lines that need to be created will be added there 202 | void GLine::trim_inside(vector pnts, vector* list){ 203 | return trim_flexible(pnts, true, list); 204 | } 205 | void GLine::trim_inside(ofRectangle rect, vector* list){ 206 | vector pnts; 207 | pnts.push_back(ofVec2f(rect.x, rect.y)); 208 | pnts.push_back(ofVec2f(rect.x+rect.width, rect.y)); 209 | pnts.push_back(ofVec2f(rect.x+rect.width, rect.y+rect.height)); 210 | pnts.push_back(ofVec2f(rect.x, rect.y+rect.height)); 211 | trim_inside(pnts, list); 212 | } 213 | 214 | //removes all parts of this line outside the polygon 215 | //if a list is provided, any new lines that need to be created will be added there 216 | void GLine::trim_outside(vector pnts, vector* list){ 217 | trim_flexible(pnts, false, list); 218 | } 219 | void GLine::trim_outside(ofRectangle rect, vector* list){ 220 | vector pnts; 221 | pnts.push_back(ofVec2f(rect.x, rect.y)); 222 | pnts.push_back(ofVec2f(rect.x+rect.width, rect.y)); 223 | pnts.push_back(ofVec2f(rect.x+rect.width, rect.y+rect.height)); 224 | pnts.push_back(ofVec2f(rect.x, rect.y+rect.height)); 225 | trim_outside(pnts, list); 226 | } 227 | 228 | //this will trim inside or outside depending on what is passed in 229 | void GLine::trim_flexible(vector pnts, bool trim_inside, vector* list){ 230 | if (is_locked){ 231 | return; 232 | } 233 | 234 | //if A or B is exactly equal to one of the points, pull it a tiny bit towards the other 235 | for (int i=0; i intersection_pnts, intersection_pnts_unordered; 262 | 263 | bool testo_add_a = false; //these are for trakcing down a bug and should be removed. 264 | bool testo_add_b = false; 265 | 266 | //if a is outside the zone that will be trimmed, treat it as the first intersection 267 | if ( (!a_in && trim_inside) || (a_in && !trim_inside) ){ 268 | intersection_pnts.push_back(a); 269 | testo_add_a = true; 270 | } 271 | 272 | //find all intersections with the shape 273 | for (int i=0; i 0){ 286 | int best_id = 0; 287 | float close_dist_sq = ofDistSquared(intersection_pnts_unordered[0].x, intersection_pnts_unordered[0].y, a.x, a.y); 288 | for (int i=1; ipush_back(new_line); 332 | } 333 | } 334 | 335 | } 336 | 337 | 338 | bool GLine::checkInPolygon(vector p, float x, float y) 339 | { 340 | int i, j, c = 0; 341 | for (i = 0, j = p.size()-1; i < p.size(); j = i++) { 342 | if ((((p[i].y <= y) && (y < p[j].y)) || 343 | ((p[j].y <= y) && (y < p[i].y))) && 344 | (x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)) 345 | c = !c; 346 | } 347 | return c; 348 | } 349 | 350 | //https://stackoverflow.com/questions/7050186/find-if-point-lays-on-line-segment 351 | //answer from user2571999 352 | bool GLine::check_point_on_line(ofVec2f t, ofVec2f p1, ofVec2f p2){ 353 | float x1 = p1.x; 354 | float y1 = p1.y; 355 | float x2 = p2.x; 356 | float y2 = p2.y; 357 | 358 | float AB = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); 359 | float AP = sqrt((t.x-x1)*(t.x-x1)+(t.y-y1)*(t.y-y1)); 360 | float PB = sqrt((x2-t.x)*(x2-t.x)+(y2-t.y)*(y2-t.y)); 361 | if(AB == AP + PB){ 362 | return true; 363 | } 364 | 365 | return false; 366 | } 367 | 368 | 369 | -------------------------------------------------------------------------------- /src/GLine.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // GLine.hpp 3 | // 4 | // Created by Andrew Wallace on 2/16/20. 5 | // 6 | 7 | #ifndef GLine_hpp 8 | #define GLine_hpp 9 | 10 | #include "ofMain.h" 11 | 12 | //helper class for dealing with lines 13 | //A GLine is a line between two 2D points (a & b) 14 | 15 | class GLine{ 16 | public: 17 | 18 | //---------------- 19 | // Variables 20 | //---------------- 21 | 22 | ///the two points that make up the line 23 | ofVec2f a, b; 24 | 25 | ///if true, this line will not be drawn 26 | bool skip_me; 27 | 28 | ///if true, this line will not be flipped in the sorting process. 29 | ///this is typically set automatically in ofxGCode for things like set_outwards_only_bounds() 30 | bool do_not_reverse; 31 | 32 | ///if true, this line will not be trimmed or moved 33 | bool is_locked; 34 | 35 | 36 | //---------------- 37 | // Functions 38 | //---------------- 39 | 40 | //--- Constructors 41 | 42 | /// initializes an empty GLine 43 | GLine(); 44 | 45 | /// initializes a GLine with starting A and B points as ofVec2f 46 | GLine(ofVec2f _a, ofVec2f _b); 47 | 48 | /// initializes a GLine with starting A and B points as floats 49 | GLine(float x1, float y1, float x2, float y2); 50 | 51 | 52 | //--- Set functions 53 | //these functions override the A and B values of the line 54 | //the line will keep other values (do_not_reverse etc) 55 | 56 | /// sets the A and B points to new values 57 | void set(ofVec2f _a, ofVec2f _b); 58 | 59 | /// sets the A and B points to new values 60 | void set(float x1, float y1, float x2, float y2); 61 | 62 | /// sets the A and B points to match another line 63 | /// other attributes of the other line are not copied 64 | void set(GLine other); 65 | 66 | /// sets is_locked. Locked lines will not be trimmed or moved 67 | void set_locked(bool val); 68 | 69 | 70 | //--- Drawing 71 | 72 | /// draws the line to the screen. If the line has no distance, a small circle is drawn. 73 | /// This will use the current drawing settings (ofSetColor, ofSetLineWidth etc) 74 | /// This is for testing. Calling this will not add the line to the plot 75 | void draw(); 76 | 77 | 78 | //--- Utilities 79 | //Some helpful functions that don't really fit into other categories 80 | 81 | /// returns a new line with the same length and angle of the current line, but offset along X and Y. 82 | /// This will not change the line it is called on 83 | GLine get_offset(ofVec2f offset); 84 | 85 | /// Breaks the line into evenly spaced segments and returns them as a vector of GLines. 86 | /// No change is made to the line. 87 | vector get_segments(int num_segments); 88 | 89 | /// returns the length of this line 90 | float get_length(); 91 | 92 | /// returns pnt a or b 93 | ofVec2f get_pnt_by_index(int ind); 94 | 95 | /// Returns a vector of 4 points forming a box around the line that is padding distance from the line 96 | vector get_bounds(float padding); 97 | 98 | 99 | //--- Intersection 100 | 101 | /// returns true if this line intersects the other 102 | bool intersects(GLine other); 103 | 104 | /// returns true if this line intersects the other. 105 | /// If they do intersect and intersect_pnt is provided, it will be set to the the x/y position of the intersection 106 | bool intersects(GLine other, ofVec2f &intersect_pnt); 107 | 108 | //--- Clipping 109 | //These functions cut a line to end where it touches another line 110 | 111 | /// if this line intersects other, the B position of this line will be moved to the intersection point 112 | bool clip_to_other_line(GLine other); 113 | 114 | /// if this line intersects the line described by other_a & other_b, the B position of this line will be moved to the intersection point 115 | bool clip_to_other_line(ofVec2f other_a, ofVec2f other_b); 116 | 117 | /// if this line intersects the line described by the four points, the B position of this line will be moved to the intersection point 118 | bool clip_to_other_line(float other_a_x, float other_a_y, float other_b_x, float other_b_y); 119 | 120 | /// Swaps the values of A and B on this line. The clipping functions always assume that the A point will stay the same and the B point will be moved, so this is useful to do if you want the A side to change 121 | void swap_a_and_b(); 122 | 123 | 124 | //--- Trimming lines 125 | // These are mostly helper functions for ofxGCode::trim_lines_inside & ofxGCode::trim_lines_outside 126 | // If the line passes through the provided shape, it will be trimmed. 127 | // However, this sometimes requires that new lines are created (for instance, we trim a circle in the middle of the line, we now have a line on either side) 128 | // For this reason a list value is passed in where any new lines will be put. 129 | // The trimming functions in ofxGCode will deal with this automatically 130 | 131 | /// trims all points of the line inside the rectangle. if a list is provided, any new lines that need to be created will be added there 132 | /// generally speaking, you should use ofxGCode::trim_lines_inside instead of calling this on an individual line 133 | void trim_inside(ofRectangle rect, vector* list = NULL); 134 | /// trims all points of the line inside the polygon described by pnts. if a list is provided, any new lines that need to be created will be added there 135 | /// generally speaking, you should use ofxGCode::trim_lines_inside instead of calling this on an individual line 136 | void trim_inside(vector pnts, vector* list = NULL); 137 | 138 | /// trims all points of the line inside the rectangle. if a list is provided, any new lines that need to be created will be added there 139 | /// generally speaking, you should use ofxGCode::trim_lines_outside instead of calling this on an individual line 140 | void trim_outside(ofRectangle rect, vector* list = NULL); 141 | /// trims all points of the line outside of the polygon described by pnts. if a list is provided, any new lines that need to be created will be added there 142 | /// generally speaking, you should use ofxGCode::trim_lines_outside instead of calling this on an individual line 143 | void trim_outside(vector pnts, vector* list = NULL); 144 | 145 | 146 | /// returns true if the point defined by x and y is inside the polygon defined by p 147 | bool checkInPolygon(vector p, float x, float y); 148 | 149 | /// returns true if point t is on the line defined by p1 & p2 150 | static bool check_point_on_line(ofVec2f t, ofVec2f p1, ofVec2f p2); 151 | 152 | 153 | 154 | private: 155 | 156 | //I don't want this to show up in auto-complete. just call trim_inside_polygon or trim_outside_polygon. 157 | void trim_flexible(vector pnts, bool trim_inside, vector* list = NULL); 158 | 159 | 160 | }; 161 | 162 | #endif /* GLine_hpp */ 163 | -------------------------------------------------------------------------------- /src/ofxGCode.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // ofxGCode.hpp 3 | // Created by Andrew Wallace on 11/7/19. 4 | // 5 | 6 | //A fair chunk of code in this project was ported from the Processing VST library by Trammell Hudson 7 | //https://github.com/osresearch/vst 8 | 9 | 10 | #ifndef ofxGCode_hpp 11 | #define ofxGCode_hpp 12 | 13 | #include "ofMain.h" 14 | #include "Clipping.hpp" 15 | #include "GLine.hpp" 16 | #include "GCodeLineGroup.h" 17 | 18 | //ofxGCode is the core class of this library 19 | //an ofxGCode object represents a single g-code file (typically one pass on the plotter) 20 | //use multiple ofxGCode objects to create layered drawings (for multiple colors etc) 21 | 22 | //The static functions are tools that can be used anywhere. They take all of the info they need and return some result (most often a vector of GLines) 23 | 24 | class ofxGCode{ 25 | public: 26 | 27 | //---------------- 28 | // Variables 29 | //---------------- 30 | 31 | float pixels_per_inch; //all the drawing tools use pixels (just like oF). This value is used to convert that to inches on the page. 32 | 33 | int circle_resolution; //equivalent to the value set by ofSetCircleResolution() 34 | 35 | int pen_down_value; //height value used when dropping the pen 36 | //NOTE: the axidraw python script I use currently ignores this value 37 | 38 | vector shape_pnts; //used for begin_shape / end_shape 39 | 40 | vector lines; //the collection of lines that make up this drawing 41 | 42 | Clipping clip; //clipping mask to make sure we don't have lines out of bounds 43 | 44 | //showing info 45 | bool show_transit_lines; //if true, the movement of the plotter head while the pen is up will be drawn 46 | bool show_path_with_color; //if true, the line color will go from green to blue over the length of the plot. Useful to check optimization 47 | bool show_do_not_reverse; //if true, lines with the do not reverse flag will be displayed along with arrows showing what direction they are going 48 | 49 | ofColor demo_col; //the color used for drawing this g-code. Set this to whatever you want 50 | float demo_fade_prc; //how faded the color will be. Typically overlapping pen strokes will appear darker so it is helpful have this be true in our display as well 51 | 52 | 53 | 54 | 55 | //---------------- 56 | // Functions 57 | //---------------- 58 | 59 | //--- Setup 60 | 61 | ///setup must be called before you can use a ofxGCode object. pixels_per_inch argument is optional and will default to 100 pixels per inch 62 | void setup(float _pixels_per_inch = 100); 63 | 64 | ///changes the canvas size. By default this is set to ofGetWidth() & ofGetHeight() in setup 65 | void set_size(int w, int h); 66 | 67 | ///clears all lines from this drawing 68 | void clear(); 69 | 70 | 71 | //--- Demo Drawing 72 | 73 | ///draws a preview of the current lines to the screen using the demo_col and the various "show" variables. 74 | ///if max_lines_to_show is a positive number the drawing will stop after that many lines. This can be useful to walk through your drawing 75 | ///the preview is always drawn at 0,0 76 | void draw(int max_lines_to_show = -1); 77 | 78 | 79 | //--- Saving 80 | 81 | ///saves the file to the bin/data folder 82 | void save(string name); 83 | 84 | 85 | //--- Rectangles 86 | 87 | ///draws a rectangle with the given values 88 | void rect(ofRectangle box); 89 | ///draws a rectangle with the given values 90 | void rect(float x, float y, float w, float h); 91 | 92 | ///draws a rectangle with rounded corners 93 | void rounded_rect(ofRectangle rect, float corner_size, int corner_resolution=10); 94 | ///draws a rectangle with rounded corners 95 | void rounded_rect(float x, float y, float w, float h, float corner_size, int corner_resolution=10); 96 | 97 | ///static function to get the points that make up a rounded rectangle 98 | ///these points are not evenly spaced 99 | static vector get_rounded_pnts(ofRectangle rect, float corner_size, int corner_resolution); 100 | ///static function to get the points that make up a rounded rectangle 101 | ///these points are not evenly spaced 102 | static vector get_rounded_pnts(float x, float y, float w, float h, float corner_size, int corner_resolution); 103 | 104 | 105 | //--- Circles 106 | 107 | ///draws a circle with the given values 108 | void circle(ofVec2f center, float size); 109 | ///draws a circle with the given values 110 | void circle(float x, float y, float size); 111 | ///static function to get the points that make up a circle. steps will be the resolution of the circle 112 | static vector get_circle_pnts(ofVec2f center, float size, int steps, float angle_offset=0); 113 | static vector get_oval_pnts(ofVec2f center, float width, float height, int steps, float angle_offset=0); 114 | 115 | static vector get_arc_pnts(ofVec2f center, float size, int steps, float start_angle, float end_angle, float height_scale = 1); 116 | 117 | 118 | //--- Polygons 119 | 120 | ///analogous to ofBeginShape() 121 | void begin_shape(); 122 | ///analogous to ofVertex() 123 | void vertex(ofVec2f p); 124 | ///analogous to ofVertex() 125 | void vertex(float x, float y); 126 | ///analogous to ofEndShape() 127 | void end_shape(bool close); 128 | 129 | ///draws a polygon defined by a vector of points. if close_shape is true, the last pnt will be connected to the first 130 | void polygon(vector pnts, bool close_shape = true); 131 | 132 | 133 | //--- Lines 134 | ///draws a line that matches the GLine being passed in. (only the position will be used. Flag values will not be copied over) 135 | void line(GLine _line); 136 | ///draws a line between the given points 137 | void line(ofVec2f a, ofVec2f b); 138 | ///draws a line between the given points 139 | void line(float x1, float y1, float x2, float y2); 140 | 141 | ///adds multiple lines to the drawing 142 | void add_lines(vector new_lines); 143 | 144 | ///creates a thick line by adding multiple close lines next to the defined line. spacing determines how far apart the extra lines will be and layers determines how many lines will be added 145 | void thick_line(float x1, float y1, float x2, float y2, float spacing, int layers); 146 | void thick_line(ofVec2f base_a, ofVec2f base_b, float spacing, int layers); 147 | 148 | //--- Line Tools 149 | 150 | ///takes a polygon defined as a vector of points and resamples it, returning another vector of points that should be the same shape but evenly spaced 151 | ///note: this translation is lossy 152 | static vector resample_lines(vector src_pnts, float sample_dist, bool close_shape, int steps_per_point=100); 153 | 154 | ///takes a vector of points and returns a vector of GLines that connects those points. if close is true, a final GLine will be created connecting the first and last point 155 | static vector pnts_to_lines(vector pnts, bool close); 156 | 157 | 158 | //--- Bezier Curves 159 | 160 | ///draws a bezier curve using the given values. steps defines the number of points that will be used in the line 161 | void bezier(ofVec2f p1, ofVec2f c1, ofVec2f c2, ofVec2f p2, int steps = 50); 162 | ///static function that returns a vector of the points that make up a bezier curve with the given values. steps defines the number of points that will be used in the line 163 | static vector get_bezier_pnts(ofVec2f p1, ofVec2f c1, ofVec2f c2, ofVec2f p2, int steps); 164 | 165 | 166 | //--- Dot 167 | ///draws a line with 0 length. This may no longer work. 168 | void dot(float x, float y); 169 | 170 | //--- Font-based text 171 | ///draws text at the x and y position using the given font (which must be passed in as a pointer) 172 | void text(string text, ofTrueTypeFont * font, float x, float y); 173 | 174 | //returns a vector of vectors with the outlines that make up each letter 175 | static vector> get_text_outlines(string text, ofTrueTypeFont * font); 176 | 177 | 178 | //--- Getting the screen point from inside a matrix 179 | //This was a long process of trial and error and only works in 2D 180 | //Almost all of the drawing functions call these at some point. 181 | //This is how we get the position when inside a matrix 182 | //there is probably a much better way to do this 183 | 184 | ///gets the position on the screen of a point, accounting for any 2D matrix transformations 185 | static ofVec2f getModelPoint(ofVec3f pnt); 186 | ///gets the position on the screen of a point, accounting for any 2D matrix transformations 187 | static ofVec2f getModelPoint(float x, float y); 188 | 189 | //takes a vector of points and gets the model position for each one 190 | static vector convert_pnts_to_model_point(vector src_pnts); 191 | 192 | //takes a vector of lines and gets the model position for each one 193 | static vector convert_lines_to_model_point(vector src_lines); 194 | 195 | //--- Optimizing the lines 196 | //When using a plotter, optimizing the path saves a lot of time 197 | //The sorting code comes from Trammell Hudson 198 | //https://github.com/osresearch/vst 199 | 200 | ///Takes all of the unlocked lines in the drawing and reorders them to try and produce the shortest possible travel distance 201 | ///This often involves flipping a line so it is drawn from B to A 202 | void sort(); 203 | 204 | ///unlocks all current lines in the drawing, allowing them to be trimmed or moved 205 | void unlock_lines(); 206 | ///locks all current lines in the drawing, preventing them from being trimmed or moved 207 | void lock_lines(); 208 | 209 | ///gets the total pen-down distance of the drawing 210 | float measureTransitDistance(); 211 | 212 | //--- Trimming 213 | //These functions are used to mask the lines against a shape, removing parts of lines or removing the line entirely if it is inside or outside the shape (depending on the function called 214 | //They work but are not optimized and will slow things down if you do it a lot 215 | 216 | //The non-static version of these functions will perform the operation on all lines currently in the drawing 217 | 218 | //The static version will take a vector of lines to trim and will return a new vector of the trimmed lines. The original set of lines will not be changed 219 | //This is useful to trim sets of lines before adding them to the drawing (for instance, when there are already lines in the drawing that you do not want trimmed) 220 | 221 | ///takes a vector of source lines and a polygon defined by bounds. returns a vector of lines with any line or line segment inside bounds removed 222 | static vector trim_lines_inside(vector lines, vector bounds); 223 | ///trims all lines of the drawing inside the given polygon 224 | void trim_inside(vector bounds); 225 | ///takes a vector of source lines and rectangle. returns a vector of lines with any line or line segment inside bounds removed 226 | static vector trim_lines_inside(vector lines, ofRectangle bounds); 227 | ///trims all lines of the drawing inside the given rectangle 228 | void trim_inside(ofRectangle bounds); 229 | 230 | ///takes a vector of source lines and a polygon defined by bounds. returns a vector of lines with any line or line segment outside bounds removed 231 | static vector trim_lines_outside(vector lines, vector bounds); 232 | ///trims all lines of the drawing outside the given polygon 233 | void trim_outside(vector bounds); 234 | ///takes a vector of source lines and rectangle. returns a vector of lines with any line or line segment outside bounds removed 235 | static vector trim_lines_outside(vector lines, ofRectangle bounds); 236 | ///trims all lines of the drawing outside the given rectangle 237 | void trim_outside(ofRectangle bounds); 238 | 239 | ///static function that takes a vector of lines_to_trim and another vector of static_lines. returns a copy of lines_to_trim that has been trimmed any time one of the lines intersected any of the lines in static_lines 240 | static vector trim_intersecting_lines(vector lines_to_trim, vector static_lines); 241 | 242 | ///used to demo an area by trimming to a box and translating it to 0,0 243 | ///I almost always want to do this based on two points, so those are the arguments 244 | void demo_trim(float x1, float y1, float x2, float y2, bool do_translate=true); 245 | 246 | 247 | //--- Other tools 248 | 249 | ///Any lines outside of this rectangle will be forced to draw from the center out. 250 | ///This is useful when drawing right up to the edge of the page to make sure that the pen always moves from inside the page out towards the edge (instead of the other way around, which could cause the pen to catch on the edge of the paper) 251 | ///This will set do_not_reverse on the lines that are in the edge area 252 | void set_outwards_only_bounds(ofRectangle safe_area); 253 | 254 | ///moves all non-locked lines in the drawing by the specified x and y values 255 | void translate(float x, float y); 256 | 257 | ///Rotates all points of the drawing counter clockwise 258 | void rotate_ccw(); 259 | 260 | ///static function that takes a point, a rectangle of the source area, and 4 points to map that source area to. returns a new point that has been warped into the new_bounds 261 | ///x_curve and y_curve are optional values that will apply an exponential curve when calculating the new point 262 | static ofVec2f perspective_warp(ofVec2f orig_pnt, ofRectangle src_bounds, ofVec2f new_bounds[4], float x_curve = 1, float y_curve = 1); 263 | 264 | 265 | //--- Saving / loading from files 266 | //this saves/load is using a pretty wonky format 267 | //I only made it as an easy way to copy a bunch of line segments from one project to another 268 | //it was designed to be simple and won't work outside of this library 269 | //This is not what you should use to save your g-code 270 | 271 | ///loads a list of outlines 272 | static vector> load_outlines(string file_path); 273 | ///saves a list of line points 274 | static vector load_lines(string file_path); 275 | ///saves a list of line points 276 | void save_lines(string file_path); 277 | 278 | //--- other 279 | 280 | //code is a modified version of code by Randolph Franklin 281 | //from http://paulbourke.net/geometry/insidepoly/ 282 | ///returns true if the point described by x & y is inside the polygon defined by p 283 | static bool checkInPolygon(vector p, float x, float y); 284 | 285 | ///returns true if the point described by pnt is inside the polygon defined by p 286 | static bool checkInPolygon(vector p, ofVec2f pnt); 287 | 288 | 289 | 290 | 291 | 292 | 293 | }; 294 | 295 | #endif /* ofxGCode.hpp */ 296 | -------------------------------------------------------------------------------- /src/ofxHersheyFont.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ofxHersheyFont.cpp 3 | * 4 | * Created by Tobias Zimmer, August 2016. 5 | * www.tobiaszimmer.net 6 | * 7 | * Font originally developed by Dr. Allen V. Hershey in 1967. 8 | * Font vectors made available by Paul Bourke. 9 | * paulbourke.net/dataformats/hershey/ 10 | * 11 | * A simple single line font for machines like CNC, Lasercutter, ... 12 | * Available characters: ASCII codes 32 to 126. 13 | * 14 | */ 15 | 16 | // This is a heavily modified verison of ofxHersheyFont by Tobias Zimmer 17 | // you can find the original here: https://github.com/tobiaszimmer/ofxHersheyFont 18 | 19 | #include "ofxHersheyFont.h" 20 | #include "simplexCharacterSet.h" 21 | 22 | 23 | //-------------------------------------------------------------- 24 | ofxHersheyFont::ofxHersheyFont(){ 25 | line_height = 35; 26 | } 27 | 28 | //-------------------------------------------------------------- 29 | //moves on to the next line if string would hit the width 30 | //returns height 31 | //passing a null gcode value will just give you the height 32 | float ofxHersheyFont::draw(string stringValue, float x, float y, float scale, ofxGCode * gcode, float width){ 33 | //break it up into words 34 | vector words; 35 | string cur_word = ""; 36 | for (int i=0; i 0){ 39 | words.push_back(cur_word); 40 | cur_word = ""; 41 | } 42 | //add new lines as their own word 43 | if (stringValue.at(i) == '\n'){ 44 | words.push_back("\n"); 45 | } 46 | } 47 | else{ 48 | cur_word += stringValue.at(i); 49 | } 50 | } 51 | if (cur_word.length() > 0){ 52 | words.push_back(cur_word); 53 | } 54 | 55 | //add those words until we run out of space 56 | float cur_y = y; 57 | string cur_line = ""; 58 | for (int i=0; i 1){ 61 | if (gcode != NULL) draw(cur_line, x, cur_y, scale, gcode); 62 | } 63 | cur_y += line_height * scale * 1.5; 64 | cur_line = ""; 65 | }else{ 66 | //cout<<"check word "< 0) cur_line+=" "; 72 | cur_line += words[i]; 73 | //cout<<"cur line: "< 0){ 85 | if (gcode != NULL) draw(cur_line, x, cur_y, scale, gcode); 86 | //cur_y += line_height * scale; 87 | } 88 | 89 | return cur_y; 90 | 91 | } 92 | 93 | //-------------------------------------------------------------- 94 | void ofxHersheyFont::draw(string stringValue, float xPos, float yPos, float scale, ofxGCode * gcode) { 95 | draw(stringValue, xPos, yPos, scale, false, 0, gcode); 96 | } 97 | 98 | //-------------------------------------------------------------- 99 | void ofxHersheyFont::draw(string stringValue, float xPos, float yPos, float scale, bool centered, ofxGCode * gcode) { 100 | draw(stringValue, xPos, yPos, scale, centered, 0, gcode); 101 | } 102 | 103 | //-------------------------------------------------------------- 104 | void ofxHersheyFont::draw(string stringValue, float xPos, float yPos, float scale, bool centered, float angle, ofxGCode * gcode) { 105 | 106 | float characterXPos = 0; 107 | float center = 0; 108 | if (centered) center = getWidth(stringValue, scale) / 2; 109 | 110 | ofPushMatrix(); 111 | ofTranslate(xPos, yPos); 112 | ofRotateRad(angle); 113 | ofTranslate(-center, 0); 114 | 115 | int line_num = 0; 116 | 117 | //iterate through each character of the input string 118 | for (int i = 0; i < stringValue.size(); i++) 119 | { 120 | ofPushMatrix(); 121 | ofTranslate(characterXPos, line_num*line_height*scale); 122 | ofScale(scale, scale); //ofScale(scale, -scale); 123 | 124 | //get ascii value of specific character from the input string 125 | int asciiValue = stringValue.at(i); 126 | 127 | 128 | if (asciiValue == '\n'){ 129 | line_num++; 130 | characterXPos = 0; 131 | } 132 | else{ 133 | 134 | //if character is not available, use questionmark 135 | if (asciiValue < 32 || asciiValue > 126) asciiValue = 63; 136 | 137 | //draw the character 138 | drawChar(asciiValue, gcode); 139 | 140 | //update xPos / starting position for the next character 141 | float charWidth = simplex[asciiValue - 32][1] * scale; 142 | characterXPos += charWidth; 143 | } 144 | 145 | ofPopMatrix(); 146 | } 147 | 148 | ofPopMatrix(); 149 | } 150 | 151 | //-------------------------------------------------------------- 152 | void ofxHersheyFont::drawChar(int asciiValue, ofxGCode * gcode) { 153 | gcode->begin_shape(); 154 | 155 | //iterate through points of the character 156 | 157 | for (int i = 2; i <= simplex[asciiValue - 32][0] * 2; i += 2) 158 | { 159 | float x = simplex[asciiValue - 32][i]; 160 | float y = simplex[asciiValue - 32][i + 1]; 161 | 162 | if (x != -1){ 163 | gcode->vertex(x, -y); 164 | 165 | } 166 | 167 | //skip -1,-1 value -> equals pen up operation / end of a line 168 | //and move to next point 169 | if (x == -1) { 170 | gcode->end_shape(false); 171 | 172 | gcode->begin_shape(); 173 | } 174 | } 175 | gcode->end_shape(false); 176 | } 177 | 178 | //-------------------------------------------------------------- 179 | float ofxHersheyFont::getWidth(string stringValue, float scale){ 180 | float stringWidth = 0; 181 | float longest_string_width; 182 | 183 | for (int i = 0; i < stringValue.size(); i++) 184 | { 185 | int asciiValue = stringValue.at(i); 186 | if (asciiValue < 32 || asciiValue > 126) asciiValue = 63; 187 | 188 | if (stringValue.at(i) == '\n'){ 189 | if (longest_string_width < stringWidth){ 190 | longest_string_width = stringWidth; 191 | } 192 | stringWidth = 0; 193 | } 194 | else{ 195 | stringWidth += (float)simplex[asciiValue - 32][1] * scale; 196 | } 197 | } 198 | 199 | if (longest_string_width < stringWidth){ 200 | longest_string_width = stringWidth; 201 | } 202 | 203 | return longest_string_width; 204 | } 205 | 206 | //-------------------------------------------------------------- 207 | //count the number of newlines and multiply by line height 208 | float ofxHersheyFont::getHeight(string stringValue, float scale){ 209 | int lines = 1; 210 | for (int i=0; i