├── README.md ├── Svg2Gcode.pro ├── abstracttransform.cpp ├── abstracttransform.h ├── arc.cpp ├── arc.h ├── basicpolygon.cpp ├── basicpolygon.h ├── circle.cpp ├── circle.h ├── gcodeviewer.cpp ├── gcodeviewer.h ├── gcodeviewer.ui ├── images ├── icon.png └── zoom-fit-best.png ├── line.cpp ├── line.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── polygon.cpp ├── polygon.h ├── qbezier.cpp ├── qbezier.h ├── res.qrc ├── svgitem.cpp ├── svgitem.h ├── transform.cpp └── transform.h /README.md: -------------------------------------------------------------------------------- 1 | SvgToGcode 2 | ========== 3 | 4 | Takes SVG and converts the elements in gcode instructions. 5 | 6 | Last year in course of a month I wrote a simple application which takes SVG xml file and converts it to gcode. 7 | However, due to lack of time I wasn't able to finish it. 8 | 9 | As of now the application has the following features: 10 | 11 | - Nearly all primitves of SVG can be drawn 12 | - Transformations like scale, rotate, translate, matrix have been implemented 13 | - GUI for rearrenging the object on the platform 14 | - Recursive best effort longest path searching algorithm 15 | 16 | 17 | 18 | There are still many features I would like to implement and I belive are quite mandatory for succesful project: 19 | 20 | - Filling 21 | - Support line width, could be viewed as filling algorithm 22 | - Automatic line invisibility test (detect where lines shouldn't be visible 23 | - Scaling of the image 24 | - Improved longest path algorithm, for example use djekstra algorithm to find the longest path 25 | - Construct a new algorithm to detect the shortest path between multiple elements 26 | - Support of multiple pictures in one workspace 27 | 28 | 29 | Maybe the code for svg parsing will be useful for someone. 30 | 31 | Original article: http://morf.lv/modules.php?name=projects&lasit=16 32 | -------------------------------------------------------------------------------- /Svg2Gcode.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2013-09-15T14:10:24 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = Svg2Gcode 12 | TEMPLATE = app 13 | 14 | 15 | SOURCES += main.cpp\ 16 | mainwindow.cpp \ 17 | svgitem.cpp \ 18 | gcodeviewer.cpp \ 19 | line.cpp \ 20 | circle.cpp \ 21 | qbezier.cpp \ 22 | polygon.cpp \ 23 | transform.cpp \ 24 | arc.cpp \ 25 | basicpolygon.cpp \ 26 | abstracttransform.cpp 27 | 28 | HEADERS += mainwindow.h \ 29 | svgitem.h \ 30 | gcodeviewer.h \ 31 | line.h \ 32 | circle.h \ 33 | qbezier.h \ 34 | polygon.h \ 35 | transform.h \ 36 | arc.h \ 37 | basicpolygon.h \ 38 | abstracttransform.h 39 | 40 | FORMS += mainwindow.ui \ 41 | gcodeviewer.ui 42 | 43 | RESOURCES += \ 44 | res.qrc 45 | -------------------------------------------------------------------------------- /abstracttransform.cpp: -------------------------------------------------------------------------------- 1 | #include "abstracttransform.h" 2 | 3 | -------------------------------------------------------------------------------- /abstracttransform.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTTRANSFORM_H 2 | #define ABSTRACTTRANSFORM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class AbstractTransform 10 | { 11 | public: 12 | virtual int type() = 0; 13 | enum TransformTypes { TRANS_ROTATE, TRANS_MATRIX, TRANS_TRANSLATE }; 14 | }; 15 | 16 | class RotationTransform : public AbstractTransform 17 | { 18 | public: 19 | int type() { return 0; } 20 | 21 | RotationTransform(double a, QPointF c) { angle = a; center = c; } 22 | 23 | double angle; 24 | QPointF center; 25 | }; 26 | 27 | class Matrix3x3 : public AbstractTransform 28 | { 29 | public: 30 | int type() { return 1; } 31 | 32 | Matrix3x3() {} 33 | 34 | double m11; 35 | double m12; 36 | double m13; 37 | double m21; 38 | double m22; 39 | double m23; 40 | bool isSet; 41 | }; 42 | 43 | class Translate : public AbstractTransform 44 | { 45 | public: 46 | int type() { return 2; } 47 | 48 | Translate(QPointF t) { translate = t; } 49 | 50 | QPointF translate; 51 | }; 52 | 53 | #endif // ABSTRACTTRANSFORM_H 54 | -------------------------------------------------------------------------------- /arc.cpp: -------------------------------------------------------------------------------- 1 | #include "arc.h" 2 | 3 | 4 | /*! 5 | * \brief Arc::Arc 6 | * \param radii - radius of x and y axis of ellipse, semi-minor and semi-major axis 7 | * \param rotation - rotated relative to the current coordinate system. 8 | * \param largeArcFlag - large arc flag. If true the arc spanning less than or equal 9 | * to 180 degrees is chose, otherwise the spanning greate than 180 10 | * degrees is chosen 11 | * \param sweepFlag - If true line joining center to arch sweeps though decreasing angles 12 | * otherwise it sweeps thorugh decreasing angles 13 | * \param endPos - absolute position of final points of the arc (read from SVG) 14 | * \param initPos - absoule initial position of the arc (cPos) 15 | */ 16 | 17 | 18 | Arc::Arc(QPointF radii, double rotation, int largeArcFlag, int sweepFlag, QPointF endPos, QPointF initPos) 19 | { 20 | //See these pages for implemenatation: 21 | //https://java.net/projects/svgsalamander/sources/svn/content/trunk/svg-core/src/main/java/com/kitfox/svg/pathcmd/Arc.java 22 | //http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes 23 | 24 | 25 | 26 | //Compute half distance between current and final point 27 | QPointF deltaPos = (initPos-endPos)/2.0; 28 | double angle = qDegreesToRadians( fmod( rotation, 360.0) ); 29 | double cosAngle = qCos(angle); 30 | double sinAngle = qSin(angle); 31 | 32 | //Step 1 : Compute (x1, y1) 33 | QPointF primePos = QPointF( 34 | cosAngle * deltaPos.x() + sinAngle * deltaPos.y(), 35 | -sinAngle * deltaPos.x() + cosAngle * deltaPos.y() 36 | ); 37 | 38 | //Corrention of out-of-range-radii 39 | radii = QPointF( qAbs(radii.x()), qAbs(radii.y()) ); 40 | double deltaRadii = (primePos.x() * primePos.x())/(radii.x()*radii.x()) 41 | + (primePos.y()*primePos.y())/(radii.y() * radii.y()); 42 | if(deltaRadii > 1) 43 | { 44 | radii = QPointF( 45 | qSqrt(deltaRadii) * radii.x(), 46 | qSqrt(deltaRadii) * radii.y() 47 | ); 48 | } 49 | 50 | //Step 2 : Compute (cx1, cy1) 51 | QPointF radiiSq = QPointF( radii.x() * radii.x() , radii.y() * radii.y()); //Squared radii 52 | int sign = largeArcFlag == sweepFlag ? -1 : 1; 53 | double sq = (radiiSq.x()*radiiSq.y() - radiiSq.x()*(primePos.y()*primePos.y()) - radiiSq.y()*(primePos.x()*primePos.x())) 54 | / ( radiiSq.x()*(primePos.y()*primePos.y()) + radiiSq.y()*(primePos.x()*primePos.x()) ); 55 | sq = sq < 0 ? 0 : sq; 56 | double coef = sign * qSqrt(sq); 57 | QPointF cp1 = QPointF( 58 | coef*((radii.x()*primePos.y())/radii.y()), 59 | coef* (-( (radii.y()*primePos.x())/(radii.x()) )) 60 | ); //center position prime 61 | 62 | //Step 3 : compute (cx, cy) from cx' and cy' 63 | QPointF sumPos = (initPos+endPos)/2.0; 64 | QPointF cp = QPointF( 65 | cosAngle*cp1.x() - sinAngle*cp1.y() + sumPos.x(), 66 | sinAngle*cp1.x() + cosAngle*cp1.y() + sumPos.y() 67 | ); 68 | //Step 4 : compute theta1 and deltaTheta 69 | QPointF up = QPointF( 70 | (primePos.x()-cp1.x()) / radii.x(), 71 | (primePos.y()-cp1.y()) / radii.y() 72 | ); 73 | 74 | QPointF vp = QPointF( 75 | (-primePos.x()-cp1.x()) / radii.x(), 76 | (-primePos.y()-cp1.y()) / radii.y() 77 | ); 78 | 79 | double p, n; 80 | 81 | //Compute theta 82 | n = qSqrt( up.x()*up.x() + up.y()*up.y() ); 83 | p = up.x(); //(1*up.x())+(0*u.y()); 84 | sign = (up.y() < 0) ? -1 : 1; 85 | double theta = qRadiansToDegrees( sign* qAcos( p/n ) ); 86 | 87 | //Compute deltaTheta 88 | n = qSqrt( (up.x()*up.x()+up.y()*up.y()) * ( vp.x()*vp.x()+vp.y()*vp.y() ) ); 89 | p = up.x()*vp.x()+up.y()*vp.y(); 90 | 91 | sign = (up.x() * vp.y() - up.y()*vp.x() < 0) ? -1 : 1; 92 | double deltaTheta = qRadiansToDegrees( sign*qAcos( p/n ) ); 93 | 94 | if(!sweepFlag && deltaTheta > 0) 95 | deltaTheta -= 360.0; 96 | else if(sweepFlag && deltaTheta < 0) 97 | deltaTheta += 360.0; 98 | 99 | deltaTheta = fmod(deltaTheta, 360.0); 100 | theta = fmod(theta, 360.0); 101 | 102 | constructPolygon( cp , radii , qDegreesToRadians(-theta), qDegreesToRadians(-deltaTheta) ); 103 | 104 | //Finally rotate: 105 | RotationTransform* rot = new RotationTransform(rotation, cp); 106 | setRotation( rot ); 107 | 108 | } 109 | 110 | void Arc::constructPolygon(QPointF center, QPointF radii, double start, double extent) 111 | { 112 | qDebug() << "Arc: " << center << radii << start << extent << qRadiansToDegrees(start) << qRadiansToDegrees(extent); 113 | double inc = (extent) / (ARC_POINTS-1); 114 | double theta = start; 115 | for(int i=0; i<=ARC_POINTS-1; i++) 116 | { 117 | 118 | myPolygon << QPointF( 119 | center.x() + radii.x()*qCos(theta), 120 | center.y() - radii.y()*qSin(theta) 121 | ); 122 | 123 | theta += inc; 124 | } 125 | 126 | //myPolygon << myPolygon[0]; 127 | } 128 | 129 | -------------------------------------------------------------------------------- /arc.h: -------------------------------------------------------------------------------- 1 | #ifndef ARC_H 2 | #define ARC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "transform.h" 8 | #include 9 | 10 | #include "basicpolygon.h" 11 | 12 | #define ARC_POINTS 64 13 | 14 | class Arc : public BasicPolygon 15 | { 16 | public: 17 | explicit Arc(QPointF radii, double rotation, 18 | int largeArcFlag, int sweepFlag, 19 | QPointF endPos, QPointF initPos); 20 | 21 | private slots: 22 | void constructPolygon(QPointF center, QPointF radii, double start, double extent); 23 | 24 | 25 | }; 26 | 27 | #endif // ARC_H 28 | -------------------------------------------------------------------------------- /basicpolygon.cpp: -------------------------------------------------------------------------------- 1 | #include "basicpolygon.h" 2 | 3 | void BasicPolygon::applyTransformations(QList trans) 4 | { 5 | if(trans.size() == 0) 6 | return; 7 | 8 | 9 | for(int i=trans.size()-1; i>=0; i--) 10 | { 11 | AbstractTransform* t = trans.at(i); 12 | if(t->type() == AbstractTransform::TRANS_ROTATE) 13 | { 14 | RotationTransform *rot = dynamic_cast(t); 15 | setRotation(rot); 16 | } 17 | else if(t->type() == AbstractTransform::TRANS_MATRIX) 18 | { 19 | Matrix3x3* m = dynamic_cast(t); 20 | setMatrix(m); 21 | } 22 | else if(t->type() == AbstractTransform::TRANS_TRANSLATE) 23 | { 24 | Translate* translate = dynamic_cast(t); 25 | setTranslate(translate); 26 | } 27 | } 28 | } 29 | 30 | void BasicPolygon::applyTransformations(QList > trans) 31 | { 32 | for(int i=trans.size()-1; i>=0; i--) 33 | { 34 | applyTransformations( trans.at(i) ); 35 | } 36 | } 37 | 38 | void BasicPolygon::setTranslate(Translate* offset) 39 | { 40 | myPolygon.translate( offset->translate ); 41 | } 42 | 43 | void BasicPolygon::setRotation(RotationTransform* transform) 44 | { 45 | QTransform t; 46 | t.translate( transform->center.x(), transform->center.y() ); 47 | t.rotate( transform->angle ); 48 | t.translate( -transform->center.x(), -transform->center.y() ); 49 | myPolygon = t.map(myPolygon); 50 | } 51 | 52 | void BasicPolygon::setMatrix(Matrix3x3* matrix) 53 | { 54 | //qDebug() << "BEFORE Matrix transformed polygon: " << myPolygon; 55 | QPolygonF tempPoly; 56 | foreach(QPointF p, myPolygon) 57 | { 58 | //See this website: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform 59 | tempPoly << QPointF( matrix->m11*p.x() + matrix->m12*p.y()+matrix->m13, 60 | matrix->m21*p.x()+matrix->m22*p.y()+matrix->m23 ); 61 | } 62 | 63 | //qDebug() << "Matrix transformed polygon: " << tempPoly; 64 | myPolygon = tempPoly; 65 | } 66 | -------------------------------------------------------------------------------- /basicpolygon.h: -------------------------------------------------------------------------------- 1 | #ifndef BASICPOLYGON_H 2 | #define BASICPOLYGON_H 3 | 4 | #include 5 | #include "transform.h" 6 | #include 7 | 8 | class BasicPolygon 9 | { 10 | public: 11 | QPolygonF getPolygon() { return myPolygon; } 12 | void setTranslate(Translate* offset); 13 | void setRotation(RotationTransform* transform); 14 | void setMatrix(Matrix3x3* matrix); 15 | void applyTransformations( QList trans ); 16 | void applyTransformations( QList< QList > trans ); 17 | 18 | protected: 19 | QPolygonF myPolygon; 20 | }; 21 | 22 | #endif // BASICPOLYGON_H 23 | -------------------------------------------------------------------------------- /circle.cpp: -------------------------------------------------------------------------------- 1 | #include "circle.h" 2 | 3 | Circle::Circle(QPointF center, int steps, double rx, double ry, double thickness) 4 | { 5 | this->thickness = thickness; 6 | 7 | double inc = (2.0 * M_PI) / (steps-1); 8 | double theta = 0.0; 9 | for(int i=0; i 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "basicpolygon.h" 10 | 11 | class Circle : public BasicPolygon 12 | { 13 | 14 | public: 15 | Circle (QPointF center, int steps, double rx, double ry, double thickness = 0.25); 16 | 17 | 18 | double getWidth() { return thickness; } 19 | 20 | private: 21 | double thickness; 22 | 23 | 24 | }; 25 | 26 | #endif // CIRCLE_H 27 | -------------------------------------------------------------------------------- /gcodeviewer.cpp: -------------------------------------------------------------------------------- 1 | #include "gcodeviewer.h" 2 | #include "ui_gcodeviewer.h" 3 | 4 | GCodeViewer::GCodeViewer(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::GCodeViewer) 7 | { 8 | ui->setupUi(this); 9 | } 10 | 11 | void GCodeViewer::setGCode(QStringList list) 12 | { 13 | gcode = list; 14 | 15 | ui->gcodeEdit->clear(); 16 | 17 | foreach(QString cmd, gcode) 18 | { 19 | ui->gcodeEdit->append(cmd); 20 | } 21 | } 22 | 23 | GCodeViewer::~GCodeViewer() 24 | { 25 | delete ui; 26 | } 27 | -------------------------------------------------------------------------------- /gcodeviewer.h: -------------------------------------------------------------------------------- 1 | #ifndef GCODEVIEWER_H 2 | #define GCODEVIEWER_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class GCodeViewer; 8 | } 9 | 10 | class GCodeViewer : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit GCodeViewer(QWidget *parent = 0); 16 | ~GCodeViewer(); 17 | 18 | void setGCode(QStringList list); 19 | 20 | private: 21 | Ui::GCodeViewer *ui; 22 | 23 | QStringList gcode; 24 | }; 25 | 26 | #endif // GCODEVIEWER_H 27 | -------------------------------------------------------------------------------- /gcodeviewer.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | GCodeViewer 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | GCode Preview 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcoder123/SvgToGcode/c9cd771446238a99673428e95e7c870d4876fb65/images/icon.png -------------------------------------------------------------------------------- /images/zoom-fit-best.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcoder123/SvgToGcode/c9cd771446238a99673428e95e7c870d4876fb65/images/zoom-fit-best.png -------------------------------------------------------------------------------- /line.cpp: -------------------------------------------------------------------------------- 1 | #include "line.h" 2 | 3 | Line::Line() 4 | { 5 | } 6 | 7 | Line::Line(const QLine &line, double width) 8 | :QLineF(line) 9 | { 10 | this->width = width; 11 | } 12 | 13 | Line::Line(const QPointF &p1, const QPointF &p2, double width) 14 | :QLineF(p1,p2) 15 | { 16 | this->width = width; 17 | } 18 | 19 | Line::Line(qreal x1, qreal y1, qreal x2, qreal y2, double width) 20 | :QLineF(x1,y1,x2,y2) 21 | { 22 | this->width = width; 23 | } 24 | -------------------------------------------------------------------------------- /line.h: -------------------------------------------------------------------------------- 1 | #ifndef LINE_H 2 | #define LINE_H 3 | 4 | #include 5 | #include 6 | 7 | class Line : public QLineF 8 | { 9 | 10 | public: 11 | Line ( const QPointF & p1, const QPointF & p2, double width = 0.25 ); 12 | Line ( qreal x1, qreal y1, qreal x2, qreal y2, double width = 0.25 ); 13 | Line ( const QLine & line, double width = 0.25 ); 14 | Line(); 15 | 16 | 17 | public: 18 | double getWidth() { return width; } 19 | 20 | private: 21 | double width; 22 | 23 | 24 | 25 | }; 26 | 27 | #endif // LINE_H 28 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | 5 | 6 | MainWindow::MainWindow(QWidget *parent) : 7 | QMainWindow(parent), 8 | ui(new Ui::MainWindow) 9 | { 10 | ui->setupUi(this); 11 | 12 | connect(ui->actionOpen_SVG, SIGNAL(triggered()), this, SLOT(openSvg())); 13 | connect(ui->actionGenerate_GCode, SIGNAL(triggered()), this, SLOT(generateGCode())); 14 | connect(ui->actionFit_To_View, SIGNAL(triggered()), this, SLOT(fitToView())); 15 | 16 | QDoubleSpinBox *penWidth = new QDoubleSpinBox(this); 17 | penWidth->setMinimum(0.1); 18 | penWidth->setValue(0.5); 19 | penWidth->setSingleStep( 0.1 ); 20 | penWidth->setToolTip( tr("Pen width") ); 21 | penWidth->setSuffix( " mm" ); 22 | 23 | ui->mainToolBar->addWidget( penWidth ); 24 | 25 | //ui->graphicsView->setViewportUpdateMode( QGraphicsView::FullViewportUpdate ); 26 | scene = new QGraphicsScene(-400, -400, 1200,1200, this); 27 | ui->graphicsView->setScene( scene ); 28 | 29 | bedBoundary = new QGraphicsRectItem( 0,0,200,200 ); 30 | bedBoundary->setPen( QPen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) ); 31 | scene->addItem(bedBoundary); 32 | 33 | svgItem = new SvgItem(); 34 | svgItem->setRect(0,0,200,200); 35 | scene->addItem( svgItem ); 36 | 37 | 38 | 39 | 40 | elevation = 2.2; 41 | qDebug() << tr("Application locked and loaded...") << QString("-5e-5").toDouble()+1; 42 | 43 | } 44 | 45 | void MainWindow::openSvg() 46 | { 47 | /*QVector points; 48 | points << QPointF(10,10) << QPointF(20,40) << QPointF(30,10) << QPointF(50,20) << QPointF(40,80) << QPointF(70,20) << QPointF(10,40); 49 | qBezierList.append( new QBezier(points , 32 ) ); 50 | svgItem->setQBezierList( qBezierList );*/ 51 | 52 | /*QPointF cp = QPointF(20,20); 53 | Circle * c = new Circle(cp, 48, 20.0, 20.0); 54 | circleList.append(c); 55 | svgItem->setCircleList(circleList); 56 | Arc *arc = new Arc(QPointF(150,150), 0, 1, 0, QPointF(300,50), QPointF(150,200)); 57 | arcList.push_back( arc ); 58 | svgItem->setArcList( arcList ); 59 | 60 | //lineList.append( new Line(48,10, 48,5) ); 61 | 62 | //svgItem->setLineList(lineList); 63 | //svgItem->setRect(0,0,80, 90); 64 | qDebug() << "HW " << svgItem->rect(); 65 | 66 | ui->graphicsView->fitInView(svgItem, Qt::KeepAspectRatio);*/ 67 | 68 | QString fileName = QFileDialog::getOpenFileName(this, 69 | tr("Open SVG"), "", tr("SVG File (*.svg)")); 70 | 71 | if(fileName.isEmpty()) 72 | return; 73 | 74 | qDebug() << "Opening SVG: " << fileName; 75 | 76 | QFile file(fileName); 77 | if (!file.open(QFile::ReadOnly | QFile::Text)) { 78 | QMessageBox::warning(this, tr("Svg2GCode"), 79 | tr("Cannot read file %1:\n%2.") 80 | .arg(fileName) 81 | .arg(file.errorString())); 82 | return; 83 | } 84 | 85 | read(&file); 86 | } 87 | 88 | void MainWindow::read(QIODevice *device) 89 | { 90 | xml.setDevice( device ); 91 | if (xml.readNextStartElement()) 92 | { 93 | if(xml.name() == "svg") 94 | { 95 | readSVG(); 96 | } 97 | else 98 | { 99 | QMessageBox::warning(this, tr("Svg2GCode"), 100 | tr("Not a valid SVG file")); 101 | } 102 | } 103 | } 104 | 105 | void MainWindow::readSVG() 106 | { 107 | lineList.clear(); 108 | circleList.clear(); 109 | qBezierList.clear(); 110 | polyList.clear(); 111 | arcList.clear(); 112 | 113 | 114 | QStringList commandsUSED; 115 | 116 | float width = 0; 117 | float height = 0; 118 | 119 | QPointF offset = QPointF(0,0); 120 | 121 | QList< QList > groupTransformList; 122 | 123 | while (!xml.atEnd() && !xml.hasError()) 124 | { 125 | 126 | QString name = xml.name().toString(); 127 | if (xml.isStartElement()) 128 | { 129 | qDebug() << "Cname " << name; 130 | if (name == "line") 131 | { 132 | //qDebug() << "element name: '" << name; 133 | QXmlStreamAttributes attrib = xml.attributes(); 134 | QString style = attrib.value("style").toString(); 135 | 136 | double lineWidth = style.mid( style.indexOf("stroke-width:")+13, style.indexOf(";")-13 ).toDouble(); 137 | 138 | /*Line *line = new Line(qAbs(attrib.value("x1").toDouble()), qAbs(attrib.value("y1").toDouble()), 139 | qAbs(attrib.value("x2").toDouble()),qAbs(attrib.value("y2").toDouble()), 140 | lineWidth);*/ 141 | 142 | QVector points; 143 | 144 | points << QPointF( attrib.value("x1").toDouble(), attrib.value("y1").toDouble() ); 145 | points << QPointF( attrib.value("x2").toDouble(), attrib.value("y2").toDouble() ); 146 | 147 | Polygon * poly = new Polygon(points,false); 148 | 149 | 150 | 151 | //qDebug() << "line: " << line->p1() << line->p2(); 152 | //lineList.push_back(line); 153 | 154 | QList elementTransformList; 155 | Transform trans; 156 | if(attrib.hasAttribute("transform")) 157 | { 158 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 159 | } 160 | QList< QList< AbstractTransform * > > tempTransformList; 161 | tempTransformList.append( groupTransformList ); 162 | tempTransformList.push_back( elementTransformList ); 163 | poly->applyTransformations( tempTransformList ); 164 | 165 | polyList.push_back( poly ); 166 | 167 | 168 | 169 | } 170 | else if(name == "g") 171 | { 172 | qDebug() << "God dammit, a group"; 173 | QXmlStreamAttributes attrib = xml.attributes(); 174 | QList elementTransformList; 175 | Transform trans; 176 | if(attrib.hasAttribute("transform")) 177 | { 178 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 179 | } 180 | 181 | groupTransformList.push_back( elementTransformList ); 182 | 183 | } 184 | else if(name == "polygon") 185 | { 186 | QXmlStreamAttributes attrib = xml.attributes(); 187 | QString pointsStr = attrib.value("points").toString(); 188 | pointsStr.replace(",", " "); 189 | QVector pointLst = parseSVGNumbers( pointsStr ); 190 | if(pointLst.size() % 2 == 0) //Is the number of cooridnates even? 191 | { 192 | QList elementTransformList; 193 | Transform trans; 194 | if(attrib.hasAttribute("transform")) 195 | { 196 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 197 | } 198 | 199 | QVector points; 200 | for(int i = 0; i > tempTransformList; 207 | tempTransformList.append( groupTransformList ); 208 | tempTransformList.push_back( elementTransformList ); 209 | poly->applyTransformations( tempTransformList ); 210 | polyList.push_back( poly ); 211 | 212 | qDebug() << "Polygon:" << pointsStr << points; 213 | } 214 | else 215 | { 216 | qDebug() << "Error, odd number of coordinates for polygon: " << pointLst; 217 | } 218 | } 219 | else if(name == "polyline") 220 | { 221 | QXmlStreamAttributes attrib = xml.attributes(); 222 | QString pointsStr = attrib.value("points").toString(); 223 | pointsStr.replace(",", " "); 224 | QVector pointLst = parseSVGNumbers( pointsStr ); 225 | if(pointLst.size() % 2 == 0) //Is the number of cooridnates even? 226 | { 227 | QList elementTransformList; 228 | Transform trans; 229 | if(attrib.hasAttribute("transform")) 230 | { 231 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 232 | } 233 | 234 | 235 | QVector points; 236 | for(int i = 0; i > tempTransformList; 243 | tempTransformList.append( groupTransformList ); 244 | tempTransformList.push_back( elementTransformList ); 245 | poly->applyTransformations( tempTransformList ); 246 | polyList.push_back( poly ); 247 | 248 | //qDebug() << "Polygon:" << pointsStr << points; 249 | } 250 | else 251 | { 252 | qDebug() << "Error, odd number of coordinates for polygon: " << pointLst; 253 | } 254 | } 255 | else if(name == "ellipse") 256 | { 257 | //qDebug() << "element name: '" << name; 258 | QXmlStreamAttributes attrib = xml.attributes(); 259 | float rx = attrib.value("rx").toFloat(); 260 | float ry = attrib.value("rx").toFloat(); 261 | 262 | QList elementTransformList; 263 | Transform trans; 264 | if(attrib.hasAttribute("transform")) 265 | { 266 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 267 | } 268 | 269 | /*Circle *circle = new Circle(attrib.value("cx").toFloat()-diameter/2, attrib.value("cy").toFloat()-diameter/2, 270 | diameter,diameter);*/ 271 | Circle *ellipse = new Circle( 272 | QPointF(qAbs(attrib.value("cx").toDouble()), qAbs(attrib.value("cy").toDouble())), 273 | 24, 274 | rx, 275 | ry 276 | ); 277 | 278 | QRectF* cRect = new QRectF(attrib.value("cx").toFloat()-rx/2, attrib.value("cy").toFloat()-ry/2, 279 | rx,ry); 280 | //qDebug() << "line: " << line; 281 | //qDebug() << offset; 282 | 283 | QList< QList< AbstractTransform * > > tempTransformList; 284 | tempTransformList.append( groupTransformList ); 285 | tempTransformList.push_back( elementTransformList ); 286 | ellipse->applyTransformations( tempTransformList ); 287 | 288 | polyList.push_back(ellipse); 289 | } 290 | else if(name == "path") 291 | { 292 | //See this: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands 293 | QXmlStreamAttributes attrib = xml.attributes(); 294 | QString d = attrib.value("d").toString(); 295 | qDebug( ) << "path: " << d << d.length(); 296 | QStringList cmds; 297 | int i = 0; 298 | while(i < d.length()) 299 | { 300 | 301 | if(d[i] >= QChar('A') && d[i] != QChar('e')) //Captial A in ASCII 302 | { 303 | QString temp; 304 | do 305 | { 306 | if(d[i] != ',') 307 | temp.push_back( d[i] ); 308 | else 309 | temp.push_back( ' ' ); 310 | i++; 311 | } while(i < d.length() && (d[i] < QChar('A') || d[i] == QChar('e')) ); 312 | i--; 313 | cmds << temp; 314 | } 315 | i++; 316 | } 317 | 318 | qDebug() << "CMDS" << cmds; 319 | 320 | qDebug() << "Constructing list of transform"; 321 | QList elementTransformList; 322 | Transform trans; 323 | if(attrib.hasAttribute("transform")) 324 | { 325 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 326 | } 327 | QList< QList< AbstractTransform * > > tempTransformList; 328 | tempTransformList.append( groupTransformList ); 329 | tempTransformList.push_back( elementTransformList ); 330 | 331 | qDebug() << "TRANSFORMS: " << tempTransformList.size(); 332 | foreach(QList at, tempTransformList) 333 | { 334 | foreach(AbstractTransform* t, at) 335 | qDebug() << "T: " << t->type(); 336 | } 337 | 338 | qDebug() << "DONE."; 339 | 340 | QPointF cPos = QPointF(0,0); //CURRENT POSITION 341 | QPointF zPos = QPointF(0,0); //Position of CLOSE PATH, used for Z command 342 | QPointF sPos = QPointF(0,0); //Position for S/s command, the second point of previous curve. 343 | foreach(QString cmd, cmds) 344 | { 345 | qDebug() << cmd[0]; 346 | commandsUSED.push_back( cmd.mid(0,1) ); 347 | if(cmd[0] == 'M') //Absolute MOVE 348 | { 349 | // If a moveto is followed by multiple pairs of coordinates, 350 | //the subsequent pairs are treated as implicit lineto commands. 351 | qDebug() << "M" << parseSVGNumbers(cmd); 352 | QVector num = parseSVGNumbers(cmd); 353 | for(int i=0; i points; 363 | points << cPos << QPointF(num[i], num[i+1]); 364 | Polygon *poly = new Polygon(points, false); 365 | cPos = QPointF(num[i], num[i+1]); 366 | //Apperantly, only the first point counts as new Z value. 367 | //Couldn't find that in documentation, though. Strange. 368 | //zPos = cPos; 369 | 370 | poly->applyTransformations( tempTransformList ); 371 | 372 | polyList.push_back( poly ); 373 | } 374 | } 375 | } 376 | if(cmd[0] == 'm') //Relative move 377 | { 378 | // If a moveto is followed by multiple pairs of coordinates, 379 | //the subsequent pairs are treated as implicit lineto commands. 380 | qDebug() << "m" << parseSVGNumbers(cmd); 381 | QVector num = parseSVGNumbers(cmd); 382 | for(int i=0; i points; 392 | points << cPos <applyTransformations( tempTransformList ); 398 | polyList.push_back( poly ); 399 | } 400 | } 401 | } 402 | if(cmd[0] == 'H') //Absolute horizontal line 403 | { 404 | qDebug() << "H" << parseSVGNumbers(cmd); 405 | QVector num = parseSVGNumbers(cmd); 406 | for(int i=0; i points; 409 | points << cPos << QPointF(num[i], cPos.y()); 410 | Polygon *poly = new Polygon(points, false); 411 | cPos = QPointF(num[i], cPos.y()); 412 | 413 | poly->applyTransformations( tempTransformList ); 414 | polyList.push_back( poly ); 415 | } 416 | } 417 | if(cmd[0] == 'h') //relative horizontal line 418 | { 419 | qDebug() << "h" << parseSVGNumbers(cmd); 420 | QVector num = parseSVGNumbers(cmd); 421 | for(int i=0; i points; 424 | points << cPos <applyTransformations( tempTransformList ); 429 | polyList.push_back( poly ); 430 | } 431 | } 432 | if(cmd[0] == 'V') //Absolute vertical line 433 | { 434 | qDebug() << "V" << parseSVGNumbers(cmd); 435 | QVector num = parseSVGNumbers(cmd); 436 | for(int i=0; i points; 439 | points << cPos << QPointF(cPos.x(), num[i]); 440 | Polygon *poly = new Polygon(points, false); 441 | cPos = QPointF(cPos.x() , num[i]); 442 | 443 | poly->applyTransformations( tempTransformList ); 444 | polyList.push_back( poly ); 445 | } 446 | } 447 | if(cmd[0] == 'v') //relative vertical line 448 | { 449 | qDebug() << "v" << parseSVGNumbers(cmd); 450 | QVector num = parseSVGNumbers(cmd); 451 | for(int i=0; i points; 454 | points << cPos <applyTransformations( tempTransformList ); 459 | polyList.push_back( poly ); 460 | } 461 | } 462 | if(cmd[0] == 'A') 463 | { 464 | qDebug() << "A" << parseSVGNumbers(cmd); 465 | QVector num = parseSVGNumbers(cmd); 466 | for(int i=0; iapplyTransformations( tempTransformList ); 474 | 475 | //arcList.push_back( arc ); 476 | polyList.push_back( arc ); 477 | } 478 | } 479 | if(cmd[0] == 'a') 480 | { 481 | qDebug() << "a" << parseSVGNumbers(cmd); 482 | QVector num = parseSVGNumbers(cmd); 483 | for(int i=0; iapplyTransformations( tempTransformList ); 491 | 492 | //arcList.push_back( arc ); 493 | polyList.push_back( arc ); 494 | } 495 | } 496 | if(cmd[0] == 'L') //Line to Absolute 497 | { 498 | qDebug() << "L" << parseSVGNumbers(cmd); 499 | QVector num = parseSVGNumbers(cmd); 500 | for(int i=0; i points; 503 | points << cPos << QPointF(num[i], num[i+1]); 504 | Polygon *poly = new Polygon(points, false); 505 | cPos = QPointF(num[i], num[i+1]); 506 | 507 | 508 | poly->applyTransformations( tempTransformList ); 509 | 510 | polyList.push_back( poly ); 511 | } 512 | } 513 | if(cmd[0] == 'l') // line to relative 514 | { 515 | qDebug() << "l" << parseSVGNumbers(cmd); 516 | QVector num = parseSVGNumbers(cmd); 517 | for(int i=0; i points; 520 | points << cPos <applyTransformations( tempTransformList ); 526 | 527 | polyList.push_back( poly ); 528 | } 529 | } 530 | if(cmd[0] == 'C') //Absolute cubic Bezier curve 531 | { 532 | //I think bezier curve might be auto filled, basically automaticaly Z cmd is initiated 533 | qDebug() << "C" << parseSVGNumbers(cmd); 534 | QVector num = parseSVGNumbers(cmd); 535 | for(int i=0; i points; 540 | points << cPos << QPointF(num[i], num[i+1]) << QPointF(num[i+2], num[i+3]) << QPointF(num[i+4], num[i+5]); 541 | QBezier *bezier = new QBezier(points, 32); 542 | cPos = QPointF(num[i+4], num[i+5]); 543 | sPos = QPointF( num[i+2], num[i+3] ); 544 | 545 | 546 | bezier->applyTransformations( tempTransformList ); 547 | 548 | //qBezierList.push_back( bezier ); 549 | polyList.push_back( bezier ); 550 | 551 | 552 | } 553 | } 554 | if(cmd[0] == 'c') //Relative cubic Bezier curve 555 | { 556 | //I think bezier curve might be auto filled, basically automaticaly Z cmd is initiated 557 | qDebug() << "c" << parseSVGNumbers(cmd); 558 | QVector num = parseSVGNumbers(cmd); 559 | for(int i=0; i points; 565 | points << cPos 566 | << cPos + QPointF(num[i], num[i+1]) 567 | << cPos + QPointF(num[i+2], num[i+3]) 568 | << cPos + QPointF(num[i+4], num[i+5]); 569 | 570 | QBezier *bezier = new QBezier(points, 32); 571 | 572 | 573 | bezier->applyTransformations( tempTransformList ); 574 | 575 | //qBezierList.push_back( bezier ); 576 | polyList.push_back( bezier ); 577 | 578 | cPos = cPos + QPointF(num[i+4], num[i+5]); 579 | sPos = cPos + QPointF( num[i+2], num[i+3] ); 580 | 581 | } 582 | } 583 | if(cmd[0] == 'S') //Absolute shorthand cubic Bezier curve 584 | { 585 | //I think bezier curve might be auto filled, basically automaticaly Z cmd is initiated 586 | qDebug() << "S" << parseSVGNumbers(cmd); 587 | QVector num = parseSVGNumbers(cmd); 588 | for(int i=0; i points; 594 | points << cPos 595 | << 2*cPos - sPos 596 | << QPointF(num[i], num[i+1]) 597 | << QPointF(num[i+2], num[i+3]); 598 | 599 | QBezier *bezier = new QBezier(points, 32); 600 | 601 | 602 | 603 | bezier->applyTransformations( tempTransformList ); 604 | 605 | //qBezierList.push_back( bezier ); 606 | polyList.push_back( bezier ); 607 | 608 | sPos = QPointF(num[i], num[i+1]); 609 | cPos = QPointF( num[i+2], num[i+3] ); 610 | 611 | } 612 | } 613 | if(cmd[0] == 's') //relative shorthand cubic Bezier curve 614 | { 615 | //I think bezier curve might be auto filled, basically automaticaly Z cmd is initiated 616 | qDebug() << "s" << parseSVGNumbers(cmd); 617 | QVector num = parseSVGNumbers(cmd); 618 | for(int i=0; i points; 624 | points << cPos 625 | << 2*cPos - sPos 626 | << cPos + QPointF(num[i], num[i+1]) 627 | << cPos + QPointF(num[i+2], num[i+3]); 628 | 629 | QBezier *bezier = new QBezier(points, 32); 630 | 631 | 632 | bezier->applyTransformations( tempTransformList ); 633 | 634 | //qBezierList.push_back( bezier ); 635 | polyList.push_back( bezier ); 636 | 637 | sPos = cPos + QPointF(num[i], num[i+1]); 638 | cPos = cPos + QPointF( num[i+2], num[i+3] ); 639 | 640 | } 641 | } 642 | if(cmd[0] == 'z' || cmd[0] == 'Z') 643 | { 644 | qDebug() << "z"; 645 | QVector points; 646 | points << cPos <applyTransformations( tempTransformList ); 654 | 655 | polyList.push_back( poly ); 656 | //this->lineList.push_back( line ); 657 | } 658 | } 659 | 660 | } 661 | else if (name == "circle") 662 | { 663 | //qDebug() << "element name: '" << name; 664 | QXmlStreamAttributes attrib = xml.attributes(); 665 | float radius = attrib.value("r").toFloat(); 666 | /*Circle *circle = new Circle(attrib.value("cx").toFloat()-diameter/2, attrib.value("cy").toFloat()-diameter/2, 667 | diameter,diameter);*/ 668 | Circle *circle = new Circle( 669 | QPointF(qAbs(attrib.value("cx").toDouble()), qAbs(attrib.value("cy").toDouble())), 670 | 24, 671 | radius, 672 | radius 673 | ); 674 | 675 | QRectF* cRect = new QRectF(attrib.value("cx").toFloat()-radius/2, attrib.value("cy").toFloat()-radius/2, 676 | radius,radius); 677 | //qDebug() << "line: " << line; 678 | //qDebug() << offset; 679 | //circleList.push_back(circle); 680 | 681 | QList elementTransformList; 682 | Transform trans; 683 | if(attrib.hasAttribute("transform")) 684 | { 685 | elementTransformList = trans.getTransforms( attrib.value("transform").toString() ); 686 | } 687 | 688 | QList< QList< AbstractTransform * > > tempTransformList; 689 | tempTransformList.append( groupTransformList ); 690 | tempTransformList.push_back( elementTransformList ); 691 | circle->applyTransformations( tempTransformList ); 692 | 693 | polyList.push_back( circle ); 694 | 695 | } 696 | } 697 | else if( xml.isEndElement() ) 698 | { 699 | if(name == "g") 700 | { 701 | qDebug() << "God dammit, an END of group - "; 702 | groupTransformList.removeLast(); 703 | } 704 | } 705 | 706 | //qDebug() << "ACTUAL XML ELEMENT: " << xml.name(); 707 | xml.readNext(); 708 | } 709 | 710 | if (xml.hasError()) 711 | { 712 | qDebug() << "XML error: " << xml.errorString() << endl; 713 | } 714 | else if (xml.atEnd()) 715 | { 716 | qDebug() << "Reached end, done" << endl; 717 | 718 | 719 | height *= -1; 720 | 721 | /*foreach(Line* line, lineList) 722 | { 723 | line->setPoints( QPointF(line->p1().x(), height+line->p1().y()), 724 | QPointF(line->p2().x(), height+line->p2().y())); 725 | }*/ 726 | 727 | /*foreach(Circle* circle, circleList) 728 | { 729 | circle->setRect( circle->x(), height+circle->y(), circle->width(), circle->height() ); 730 | }*/ 731 | 732 | 733 | 734 | svgItem->setLineList(lineList); 735 | svgItem->setCircleList(circleList); 736 | svgItem->setQBezierList( qBezierList ); 737 | svgItem->setPolygonList( polyList ); 738 | svgItem->setArcList( arcList ); 739 | //svgItem->setRect(0,0,width, height); 740 | 741 | QPointF min = QPointF(999999 ,999999 ); 742 | QPointF max = QPointF( 0,0 ); 743 | foreach(BasicPolygon* poly, polyList) 744 | { 745 | foreach(QPointF pts, poly->getPolygon()) 746 | { 747 | if(pts.x() < min.x()) 748 | min.setX(pts.x()); 749 | if(pts.y() < min.y()) 750 | min.setY( pts.y() ); 751 | 752 | if(pts.x() > max.x()) 753 | max.setX(pts.x()); 754 | if(pts.y() > max.y()) 755 | max.setY(pts.y()); 756 | } 757 | } 758 | 759 | QRectF imageBoundary(min, max); 760 | 761 | svgItem->setRect( imageBoundary ); 762 | 763 | 764 | qDebug() << "Min: " << min << "Max: " << max; 765 | //qDebug() << "HW " << width << height << svgItem->rect(); 766 | 767 | qDebug() << "commands used: " << commandsUSED.toSet(); 768 | 769 | fitToView(); 770 | //ui->graphicsView->fitInView(QRectF(0,0,1200.0,1200.0), Qt::KeepAspectRatio); 771 | } 772 | } 773 | 774 | void MainWindow::fitToView() 775 | { 776 | if(svgItem->rect().x()+svgItem->rect().width() < 200 && svgItem->rect().y()+svgItem->rect().height() < 200) 777 | { 778 | ui->graphicsView->fitInView(-25,-25, 250,250, Qt::KeepAspectRatio); 779 | //ui->graphicsView->centerOn( bedBoundary ); 780 | } 781 | else 782 | { 783 | ui->graphicsView->fitInView(-25,-25, svgItem->rect().width() +50 ,svgItem->rect().height() +50, Qt::KeepAspectRatio); 784 | } 785 | } 786 | 787 | QVector MainWindow::parseSVGNumbers(QString cmd) 788 | { 789 | cmd.replace(QRegExp("[a-df-zA-DF-Z]"), ""); 790 | cmd.replace("-", " -"); 791 | cmd.replace( "e -", "e-" ); 792 | //qDebug( ) << "COMMAND " << cmd; 793 | cmd = cmd.trimmed(); 794 | cmd = cmd.simplified(); 795 | 796 | QStringList numStr = cmd.split(" "); 797 | QVector num; 798 | foreach(QString str, numStr) 799 | { 800 | //num.push_back( qAbs(str.toDouble()) ); // <-- honestly that was stupido. 801 | num.push_back( str.toDouble() ); 802 | } 803 | 804 | return num; 805 | } 806 | 807 | 808 | void MainWindow::generateGCode() 809 | { 810 | QStringList gcode; 811 | 812 | gcode << "G21 ; set units to millimeters"; 813 | //gcode << "G28 ;home all axes"; 814 | gcode << "G92 X0 Y0 Z10 ;set home position to 10mm above platform"; 815 | gcode << "G0 Z5"; 816 | gcode << "G90 ; use absolute coordinates"; 817 | gcode << "G1 F1800.000"; 818 | gcode << QString("G1 Z%1 F7800.000 ;lift up %1 mm").arg(elevation,0,'f',3); 819 | 820 | //Do calculation here 821 | float dx = svgItem->rect().width()/2.0; 822 | float dy = svgItem->rect().height()/2.0; 823 | 824 | qDebug() << "Lines: " << lineList.size(); 825 | qDebug() << "Bezier curver: " << qBezierList.size(); 826 | 827 | foreach(QLineF *line, lineList) 828 | { 829 | gcode << QString("G1 X%1 Y%2 F1800.000").arg(line->x1(),0, 'f', 3).arg(line->y1(),0, 'f', 3); 830 | gcode << "G1 Z0.350 F7800.000"; 831 | gcode << QString("G1 X%1 Y%2 F1800.000").arg(line->x2(),0, 'f', 3).arg(line->y2(),0, 'f', 3); 832 | gcode << QString("G1 Z%1 F1800.000 ;lift up %1 mm").arg(elevation,0,'f',3); 833 | } 834 | 835 | // foreach(QBezier * bezier , qBezierList) 836 | // { 837 | // QPolygonF polygon = bezier->getPolygon(); 838 | // gcode << QString("G1 X%1 Y%2 F1800.000").arg(polygon[0].x(),0, 'f', 3).arg(polygon[0].y(),0, 'f', 3); 839 | // gcode << "G1 Z0.350 F7800.000"; 840 | // for(int i=1; i > optimizedPoly; 849 | 850 | QList tempPoly = polyList; 851 | int polyCount = 0; 852 | while(tempPoly.size()) 853 | { 854 | QList tmpPolyLst; 855 | 856 | tmpPolyLst = searchPolygons( tempPoly.first(), tempPoly ); 857 | 858 | optimizedPoly.append(tmpPolyLst ); 859 | 860 | foreach(BasicPolygon* poly, tmpPolyLst) 861 | { 862 | tempPoly.removeOne( poly ); 863 | polyCount++; 864 | } 865 | 866 | 867 | } 868 | qDebug() << "Poly count: " << polyCount << polyList.size() << optimizedPoly.size(); 869 | 870 | 871 | /*foreach(BasicPolygon * poly , polyList) 872 | { 873 | QPolygonF polygon = poly->getPolygon(); 874 | gcode << QString("G1 X%1 Y%2 F1800.000").arg(polygon[0].x(),0, 'f', 3).arg(polygon[0].y(),0, 'f', 3); 875 | gcode << "G1 Z0.350 F7800.000"; 876 | for(int i=1; i list, optimizedPoly) 885 | { 886 | gcode << QString("G1 X%1 Y%2 F1800.000").arg(list.first()->getPolygon().at(0).x(),0, 'f', 3).arg(list.first()->getPolygon().at(0).y(),0, 'f', 3); 887 | gcode << "G1 Z0.350 F7800.000"; 888 | foreach(BasicPolygon * poly , list) 889 | { 890 | QPolygonF polygon = poly->getPolygon(); 891 | 892 | for(int i=1; igetPolygon(); 906 | gcode << QString("G1 X%1 Y%2 F1800.000").arg(polygon[0].x(),0, 'f', 3).arg(polygon[0].y(),0, 'f', 3); 907 | gcode << "G1 Z0.350 F7800.000"; 908 | for(int i=1; isetGCode( gcode ); 921 | gviewer->show(); 922 | } 923 | 924 | QList MainWindow::searchPolygons(BasicPolygon *previous, QList fullLst) 925 | { 926 | QList lst; 927 | lst.append(previous); 928 | fullLst.removeOne( previous ); 929 | foreach(BasicPolygon* poly, fullLst) 930 | { 931 | if(previous->getPolygon().last() == poly->getPolygon().first()) 932 | { 933 | lst.append( searchPolygons(poly, fullLst) ); 934 | break; 935 | } 936 | } 937 | return lst; 938 | } 939 | 940 | void MainWindow::updateBoundary(float &w, float &h, float x1, float x2, float y1, float y2) 941 | { 942 | if(w < x1) 943 | w = x1; 944 | if(w < x2) 945 | w =x2; 946 | if(h > y1) 947 | h = y1; 948 | if(h > y2) 949 | h = y2; 950 | } 951 | 952 | MainWindow::~MainWindow() 953 | { 954 | delete ui; 955 | } 956 | -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "svgitem.h" 11 | #include "gcodeviewer.h" 12 | #include 13 | #include 14 | #include "line.h" 15 | #include "circle.h" 16 | #include "qbezier.h" 17 | #include "polygon.h" 18 | #include "transform.h" 19 | #include 20 | #include "arc.h" 21 | #include "basicpolygon.h" 22 | 23 | 24 | namespace Ui { 25 | class MainWindow; 26 | } 27 | 28 | class MainWindow : public QMainWindow 29 | { 30 | Q_OBJECT 31 | 32 | public: 33 | explicit MainWindow(QWidget *parent = 0); 34 | ~MainWindow(); 35 | //enum Cmds { none, Move, move, Circle, circle }; 36 | 37 | private slots: 38 | void openSvg(); 39 | void read(QIODevice *device); 40 | void readSVG(); 41 | void updateBoundary(float &w, float&h, float x1, float x2, float y1, float y2); 42 | void generateGCode(); 43 | QVector parseSVGNumbers(QString cmd); 44 | QList searchPolygons(BasicPolygon *previous, QList fullLst); 45 | void fitToView(); 46 | 47 | private: 48 | Ui::MainWindow *ui; 49 | QXmlStreamReader xml; 50 | QGraphicsScene * scene; 51 | SvgItem * svgItem; 52 | QList lineList; 53 | QList circleList; 54 | QList qBezierList; 55 | QList polyList; 56 | QList arcList; 57 | double elevation; 58 | QGraphicsRectItem *bedBoundary; 59 | }; 60 | 61 | 62 | #endif // MAINWINDOW_H 63 | -------------------------------------------------------------------------------- /mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | Svg2GCode 15 | 16 | 17 | 18 | :/images/icon.png:/images/icon.png 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | TopToolBarArea 30 | 31 | 32 | false 33 | 34 | 35 | 36 | 37 | 38 | 39 | 0 40 | 0 41 | 400 42 | 21 43 | 44 | 45 | 46 | 47 | File 48 | 49 | 50 | 51 | 52 | 53 | Tools 54 | 55 | 56 | 57 | 58 | 59 | 60 | View 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Open SVG 71 | 72 | 73 | 74 | 75 | Generate GCode 76 | 77 | 78 | 79 | 80 | Simulate GCode 81 | 82 | 83 | 84 | 85 | 86 | :/images/zoom-fit-best.png:/images/zoom-fit-best.png 87 | 88 | 89 | Fit To View 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /polygon.cpp: -------------------------------------------------------------------------------- 1 | #include "polygon.h" 2 | 3 | Polygon::Polygon(QVector points, bool close) 4 | { 5 | foreach(QPointF p, points) 6 | myPolygon.push_back( p ); 7 | 8 | if(close) 9 | myPolygon.push_back( points.at(0) ); 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /polygon.h: -------------------------------------------------------------------------------- 1 | #ifndef POLYGON_H 2 | #define POLYGON_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #include "basicpolygon.h" 10 | 11 | class Polygon : public BasicPolygon 12 | { 13 | public: 14 | explicit Polygon(QVector points, bool close = true); 15 | //QPolygonF getPolygon() { return myPolygon; } 16 | // void setTranslate(QPointF offset); 17 | // void setRotation(rotationTransform transform); 18 | // void setMatrix(matrix3x3_struct matrix); 19 | 20 | private: 21 | //QPolygonF myPolygon; 22 | 23 | }; 24 | 25 | #endif // POLYGON_H 26 | -------------------------------------------------------------------------------- /qbezier.cpp: -------------------------------------------------------------------------------- 1 | #include "qbezier.h" 2 | 3 | //http://www.codeproject.com/Articles/25237/Bezier-Curves-Made-Simple 4 | //Check the general case. Notice I missed binominial coefficient when coding 5 | 6 | QBezier::QBezier(QVector points, int steps) 7 | { 8 | int n = points.size()-1; 9 | for(double t=0; t<=1; t+=1.0/steps) // t = [0,1] !!! not [0, 1) brainfail. I was thinking why the hell there are spaces. 10 | { 11 | QPointF newP; 12 | 13 | for(int i=0; i<=n; i++) 14 | { 15 | newP.setX( newP.x() + points[i].x()*qPow(1.0-t,n-i)*qPow(t,i)*double(binomialCoefficient(n, i)) ); 16 | newP.setY( newP.y() + points[i].y()*qPow(1.0-t,n-i)*qPow(t,i)*double(binomialCoefficient(n, i)) ); 17 | } 18 | /*QPointF p0 = points.at(0); 19 | QPointF p1 = points.at(1); 20 | QPointF p2 = points.at(2); 21 | newP.setX( qPow((1.0-t),2.0)*p0.x()+2*t*(1.0-t)*p1.x()+qPow(t,2.0)*p2.x() ); 22 | newP.setY( qPow((1.0-t),2.0)*p0.y()+2*t*(1.0-t)*p1.y()+qPow(t,2.0)*p2.y() );*/ 23 | 24 | //this->setPoint(point++, newP); 25 | myPolygon.push_back( newP ); 26 | 27 | } 28 | 29 | //qDebug() << "Bezier: " << myPolygon << n; 30 | } 31 | 32 | int QBezier::binomialCoefficient(int n, int i) 33 | { 34 | return (factorial(n) / ( factorial(i)*factorial(n-i) ) ); 35 | } 36 | 37 | int QBezier::factorial(int n) 38 | { 39 | if(n == 0) 40 | return 1; 41 | return n*factorial(n-1); 42 | } 43 | -------------------------------------------------------------------------------- /qbezier.h: -------------------------------------------------------------------------------- 1 | #ifndef QBEZIER_H 2 | #define QBEZIER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "basicpolygon.h" 9 | 10 | class QBezier : public BasicPolygon 11 | { 12 | public: 13 | explicit QBezier(QVector points, int steps); 14 | 15 | 16 | private: 17 | 18 | int binomialCoefficient(int n, int i); 19 | int factorial(int n); 20 | 21 | }; 22 | 23 | #endif // QBEZIER_H 24 | -------------------------------------------------------------------------------- /res.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/icon.png 4 | images/zoom-fit-best.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /svgitem.cpp: -------------------------------------------------------------------------------- 1 | #include "svgitem.h" 2 | 3 | SvgItem::SvgItem(QGraphicsItem *parent) : 4 | QGraphicsRectItem(parent) 5 | { 6 | setFlag(QGraphicsItem::ItemIsMovable, true); 7 | setFlag(QGraphicsItem::ItemIsSelectable, true); 8 | } 9 | 10 | void SvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 11 | { 12 | painter->setRenderHint( QPainter::Antialiasing ); 13 | QPen pen(Qt::black, 0.15, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); 14 | pen.setWidthF(0.5); 15 | painter->setPen(pen); 16 | 17 | 18 | // foreach(QBezier *qbezier, qBezierList) 19 | // { 20 | // //qDebug() << qbezier->getPolygon(); 21 | // painter->drawPolyline(qbezier->getPolygon()); 22 | // } 23 | 24 | foreach(BasicPolygon *poly, polyList) 25 | { 26 | //qDebug() << qbezier->getPolygon(); 27 | painter->drawPolyline(poly->getPolygon()); 28 | } 29 | 30 | // foreach(Arc *arc, arcList) 31 | // { 32 | // //qDebug() << qbezier->getPolygon(); 33 | // painter->drawPolyline(arc->getPolygon()); 34 | // } 35 | 36 | // foreach(BasicPolygon* poly, polygons) 37 | // { 38 | // painter->drawPolyline( poly->getPolygon() ); 39 | // } 40 | 41 | foreach(Line* line, lineList) 42 | { 43 | pen.setWidthF( line->getWidth() ); 44 | painter->setPen(pen); 45 | painter->drawLine(line->p1(), line->p2()); 46 | } 47 | 48 | 49 | 50 | pen.setWidthF(0.25); 51 | painter->setPen(pen); 52 | foreach(Circle* circle, circleList) 53 | { 54 | /*QBrush brush = painter->brush(); 55 | brush.setStyle(Qt::SolidPattern); 56 | painter->setBrush(brush); 57 | 58 | painter->drawEllipse(circle->center(), circle->height(), circle->width());*/ 59 | painter->drawPolyline(circle->getPolygon()); 60 | } 61 | 62 | 63 | //QGraphicsRectItem::paint(painter, option, widget); 64 | } 65 | -------------------------------------------------------------------------------- /svgitem.h: -------------------------------------------------------------------------------- 1 | #ifndef SVGITEM_H 2 | #define SVGITEM_H 3 | 4 | #include 5 | #include 6 | #include "line.h" 7 | #include 8 | #include "circle.h" 9 | #include "qbezier.h" 10 | #include "polygon.h" 11 | #include "arc.h" 12 | #include "basicpolygon.h" 13 | 14 | class SvgItem : public QGraphicsRectItem 15 | { 16 | public: 17 | enum { Type = UserType + 15 }; 18 | explicit SvgItem(QGraphicsItem *parent = 0); 19 | 20 | int type() const 21 | { return Type;} 22 | 23 | 24 | protected: 25 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); 26 | 27 | public slots: 28 | void setLineList(QList list) { lineList = list; } 29 | void setCircleList(QList list) { circleList = list; } 30 | void setQBezierList(QList list) { qBezierList = list; } 31 | void setPolygonList(QList list) { polyList = list; } 32 | void setArcList(QList list) { arcList = list; } 33 | 34 | private: 35 | QList lineList; 36 | QList circleList; 37 | QList qBezierList; 38 | QList polyList; 39 | QList arcList; 40 | 41 | 42 | }; 43 | 44 | #endif // SVGITEM_H 45 | -------------------------------------------------------------------------------- /transform.cpp: -------------------------------------------------------------------------------- 1 | #include "transform.h" 2 | 3 | Transform::Transform() 4 | { 5 | // translate = QPointF(0,0); 6 | // rotation.angle = 0; 7 | // matrix.isSet = false; 8 | 9 | } 10 | 11 | QList Transform::getTransforms(QString transform) 12 | { 13 | // translate = QPointF(0,0); 14 | // rotation.angle = 0; 15 | // matrix.isSet = false; 16 | 17 | QStringList cmds = transform.split(")"); 18 | 19 | foreach(QString cmd, cmds) 20 | { 21 | cmd = cmd.simplified(); 22 | 23 | if(cmd.contains("translate")) 24 | { 25 | QString translateStr = cmd.mid( cmd.indexOf("(")+1, cmd.indexOf(")")-1 ); 26 | translateStr.replace(",", " "); 27 | QStringList numStr = translateStr.split(" "); 28 | 29 | Translate *trans = new Translate( QPointF( numStr[0].toDouble(), numStr[1].toDouble()) ); 30 | trList.push_back( trans ); 31 | } 32 | else if(cmd.contains("rotate")) 33 | { 34 | QString rotateStr = cmd.mid( cmd.indexOf("(")+1, cmd.indexOf(")")-1 ); 35 | rotateStr = rotateStr.simplified(); 36 | QStringList numStr = rotateStr.split( "," ); 37 | 38 | RotationTransform *rotation = new RotationTransform(0, QPointF()); 39 | 40 | rotation->angle = numStr.at(0).toDouble(); 41 | if(numStr.size() > 1) 42 | { 43 | rotation->center = QPointF(numStr[1].toDouble() , numStr[2].toDouble()); 44 | } 45 | 46 | trList.push_back( rotation ); 47 | } 48 | else if(cmd.contains("matrix")) 49 | { 50 | QString matrixStr = cmd.mid( cmd.indexOf("(")+1, cmd.indexOf(")")-1 ); 51 | matrixStr = matrixStr.simplified(); 52 | QStringList numStr = matrixStr.split( "," ); 53 | 54 | Matrix3x3 *matrix = new Matrix3x3(); 55 | 56 | matrix->m11 = numStr[0].toDouble(); 57 | matrix->m21 = numStr[1].toDouble(); 58 | matrix->m12 = numStr[2].toDouble(); 59 | matrix->m22 = numStr[3].toDouble(); 60 | matrix->m13 = numStr[4].toDouble(); 61 | matrix->m23 = numStr[5].toDouble(); 62 | matrix->isSet = true; 63 | 64 | trList.push_back( matrix ); 65 | 66 | /*qDebug() << "matrix: " << matrix.m11 << matrix.m21 << matrix.m12 << matrix.m22 67 | << matrix.m13 << matrix.m23;*/ 68 | } 69 | } 70 | 71 | return trList; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /transform.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSFORM_H 2 | #define TRANSFORM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "abstracttransform.h" 9 | 10 | 11 | 12 | 13 | class Transform 14 | { 15 | public: 16 | Transform(); 17 | 18 | QList getTransforms(QString transform); 19 | 20 | /*QPointF getTranslate() { return translate; } 21 | rotationTransform getRotate() {return rotation; } 22 | matrix3x3_struct getMatrix() { return matrix; }*/ 23 | 24 | 25 | private: 26 | /*QPointF translate; 27 | rotationTransform rotation; 28 | matrix3x3_struct matrix;*/ 29 | QList trList; 30 | 31 | }; 32 | 33 | #endif // TRANSFORM_H 34 | 35 | --------------------------------------------------------------------------------