Frontend to the famous GRBL.

qpainter2gcode.cpp 16KB

    /* Part of x2grbl * * Copyright Johann Wilhelm <johann.wilhelm@9mal6.de> 2015 * * see Readme.md for detailed license and usage information! */ #include "qpainter2gcode.h" #include <QDebug> #include <QSettings> #include <QVector2D> #include <QPointF> #include <QList> #include <limits> const double GCodePainterEngine::ps2mm=1/72.0; QPainter2GCode::QPainter2GCode() : QPaintDevice() { Engine=new GCodePainterEngine(QPaintEngine::AllFeatures); } int QPainter2GCode::metric(PaintDeviceMetric metric) const { switch (metric) { case PdmWidth: case PdmHeight: case PdmWidthMM: case PdmHeightMM: return 1000; case PdmNumColors: case PdmDepth: return 1; case PdmDpiX: case PdmDpiY: case PdmPhysicalDpiX: case PdmPhysicalDpiY: return 30; default: return QPaintDevice::metric(metric); } } QPaintEngine *QPainter2GCode::paintEngine() const { return Engine; } QPainter2GCode::~QPainter2GCode() { delete Engine; } void QPainter2GCode::InitParser(GCodeParser &Parser) { Parser.Lines.clear(); const int count=Engine->GCodeListing.count(); for (int i=0;i<count;i++) { Parser.Lines.insert(i+1,Engine->GCodeListing[i].serialize()); } } GCodePainterEngine::GCodePainterEngine(PaintEngineFeatures features/*=0*/) : QPaintEngine(features) { minLength=0.1/ps2mm; } GCodePainterEngine::~GCodePainterEngine() { } bool GCodePainterEngine::begin(QPaintDevice *pdev) { highZ=1; lowZ=-.1; GCodeListing.clear(); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QString("2.5d Grbl-Commander")); settings.beginGroup("QPainter2GCode"); if (settings.contains(QString("HighZ"))) highZ=settings.value("HighZ").toDouble(); if (settings.contains(QString("LowZ"))) lowZ=settings.value("LowZ").toDouble(); settings.endGroup(); settings.beginGroup("MillingBits"); QStringList Keys=settings.allKeys(); for (int i=0;i<Keys.count();i++) { if (Keys[i].contains(QString("BitDiameter"))) { QString BitNumber=Keys[i]; while (BitNumber.count() && !BitNumber[0].isDigit()) { BitNumber.remove(0,1); } int BitDiameter=settings.value(Keys[i]).toInt(); Mills[BitNumber.toInt()]=BitDiameter; } } settings.endGroup(); settings.beginGroup("DrillingBits"); Keys=settings.allKeys(); for (int i=0;i<Keys.count();i++) { if (Keys[i].contains(QString("BitDiameter"))) { QString BitNumber=Keys[i]; while (BitNumber.count() && !BitNumber[0].isDigit()) { BitNumber.remove(0,1); } int BitDiameter=settings.value(Keys[i]).toInt(); Drills[BitNumber.toInt()+1000]=BitDiameter; } } settings.endGroup(); return true; } bool GCodePainterEngine::end() { return true; } void GCodePainterEngine::updateState(const QPaintEngineState &state) { } void GCodePainterEngine::drawRects(const QRect *rects, int rectCount) { for (int i=0;i<rectCount;i++) { QPainterPath path; path.addRect(rects[i]); drawPath(path); } } void GCodePainterEngine::drawRects(const QRectF *rects, int rectCount) { QColor c=painter()->brush().color(); Qt::BGMode mode=painter()->backgroundMode(); for (int i=0;i<rectCount;i++) { QPainterPath path; path.addRect(rects[i]); drawPath(path); } } void GCodePainterEngine::drawLines(const QLine *lines, int lineCount) { for (int i=0;i<lineCount;i++) { QPainterPath path; path.moveTo(lines[i].p1()); path.lineTo(lines[i].p2()); drawPath(path); } } void GCodePainterEngine::drawLines(const QLineF *lines, int lineCount) { for (int i=0;i<lineCount;i++) { QPainterPath path; path.moveTo(lines[i].p1()); path.lineTo(lines[i].p2()); drawPath(path); } } void GCodePainterEngine::drawEllipse(const QRectF &r) { QPainterPath path; path.addEllipse(r); drawPath(path); } void GCodePainterEngine::drawEllipse(const QRect &r) { QPainterPath path; path.addEllipse(r); drawPath(path); } void GCodePainterEngine::drawPathInternal(const QPainterPath &path) { bool bIsLow=false; GCode Code; Code.Cmd=0; Code.Code=CodeType_GCode; Code.Parameters["Z"]=QString("%1").arg(highZ); GCodeListing.append(Code); Code.Parameters.clear(); QPointF currentPoint=QPointF(0,0); for (int elementIndex=0;elementIndex<path.elementCount();elementIndex++) { QPainterPath::Element element=path.elementAt(elementIndex); if (element.type==QPainterPath::MoveToElement) { currentPoint=element; Code.Parameters.clear(); if (bIsLow) { Code.Cmd=0; Code.Parameters["Z"]=QString("%1").arg(highZ); GCodeListing.append(Code); bIsLow=false; Code.Parameters.clear(); } Code.Parameters["X"]=QString("%1").arg(element.x*ps2mm); Code.Parameters["Y"]=QString("%1").arg(element.y*ps2mm); GCodeListing.append(Code); } else if (element.type==QPainterPath::LineToElement) { currentPoint=element; Code.Parameters.clear(); if (!bIsLow) { Code.Cmd=0; Code.Parameters["Z"]=QString("%1").arg(lowZ); GCodeListing.append(Code); bIsLow=true; Code.Parameters.clear(); } Code.Cmd=1; Code.Parameters["X"]=QString("%1").arg(element.x*ps2mm); Code.Parameters["Y"]=QString("%1").arg(element.y*ps2mm); GCodeListing.append(Code); } else if (element.type==QPainterPath::CurveToElement) { Code.Parameters.clear(); if (!bIsLow) { Code.Cmd=0; Code.Parameters["Z"]=QString("%1").arg(lowZ); GCodeListing.append(Code); bIsLow=true; Code.Parameters.clear(); } QVector2D BezierFrom=QVector2D(currentPoint); QList<QVector2D> ControlPoints; ControlPoints.append(QVector2D(element)); elementIndex++; while (elementIndex<path.elementCount()) { if (path.elementAt(elementIndex).type!=QPainterPath::CurveToDataElement) break; ControlPoints.append((QVector2D)(QPointF)path.elementAt(elementIndex)); elementIndex++; } elementIndex--; QVector2D BezierTo=QVector2D(ControlPoints.last()); currentPoint=BezierTo.toPointF(); ControlPoints.removeLast(); if (ControlPoints.count()==2) { Code.Cmd=1; const int NumParts=10; for(int i=0;i<NumParts;i++) { const double t=(double)i/(double)NumParts; double c1=pow(1-t,3); double c2=3*t*pow(1-t,2); double c3=3*pow(t,2)*(1-t); double c4=pow(t,3); QVector2D Point=BezierFrom*c1+ControlPoints[0]*c2+ControlPoints[1]*c3+BezierTo*c4; Code.Parameters["X"]=QString("%1").arg(Point.x()*ps2mm); Code.Parameters["Y"]=QString("%1").arg(Point.y()*ps2mm); GCodeListing.append(Code); } Code.Parameters["X"]=QString("%1").arg(BezierTo.x()*ps2mm); Code.Parameters["Y"]=QString("%1").arg(BezierTo.y()*ps2mm); GCodeListing.append(Code); Code.Parameters.clear(); } else if (ControlPoints.count()==1) { Code.Cmd=1; const int NumParts=2; for(int i=0;i<NumParts;i++) { const double t=(double)i/(double)NumParts; QVector2D Point=BezierFrom*pow(1-t,2)+ControlPoints[0]*2*t*(1-t)+BezierTo*pow(t,2); Code.Parameters["X"]=QString("%1").arg(Point.x()*ps2mm); Code.Parameters["Y"]=QString("%1").arg(Point.y()*ps2mm); GCodeListing.append(Code); } Code.Parameters["X"]=QString("%1").arg(BezierTo.x()*ps2mm); Code.Parameters["Y"]=QString("%1").arg(BezierTo.y()*ps2mm); GCodeListing.append(Code); Code.Parameters.clear(); } else { Code.Cmd=1; Code.Parameters["X"]=QString("%1").arg(BezierTo.x()*ps2mm); Code.Parameters["Y"]=QString("%1").arg(BezierTo.y()*ps2mm); GCodeListing.append(Code); Code.Parameters.clear(); } } } Code.Cmd=0; Code.Parameters["Z"]=QString("%1").arg(highZ); GCodeListing.append(Code); } void GCodePainterEngine::SimplyfyInternal(QPainterPath &path) { QPointF currentPoint=QPointF(0,0); QPainterPath Part; int RemoveCount=0; for (int elementIndex=0;elementIndex<path.elementCount();elementIndex++) { QPainterPath::Element element=path.elementAt(elementIndex); if (element.type==QPainterPath::MoveToElement) { currentPoint=element; Part.moveTo(element.x, element.y); } else if (element.type==QPainterPath::LineToElement) { QVector2D len=QVector2D(currentPoint-element); if (len.length()<minLength) { // if (elementIndex+1<path.elementCount()) { // if (path.elementAt(elementIndex).type==QPainterPath::LineToElement) { //qDebug()<<"Line-Segment removed\n"; RemoveCount++; continue; // } // } } currentPoint=element; Part.lineTo(element.x, element.y); } else if (element.type==QPainterPath::CurveToElement) { QList<QPointF> ControlPoints; ControlPoints.append(QPointF(element)); elementIndex++; while (elementIndex<path.elementCount()) { if (path.elementAt(elementIndex).type!=QPainterPath::CurveToDataElement) break; ControlPoints.append((QPointF)(QPointF)path.elementAt(elementIndex)); elementIndex++; } elementIndex--; if (ControlPoints.count()==3) { Part.cubicTo(ControlPoints[0], ControlPoints[1], ControlPoints[2]); } else { Part.quadTo(ControlPoints[0], ControlPoints[1]); } currentPoint=ControlPoints.last(); } } // if (RemoveCount) // qDebug()<<RemoveCount<<" line-Segments removed\n"; path=Part; } void GCodePainterEngine::drawPath(const QPainterPath &path) { QTransform Transform=painter()->transform(); if (painter()->brush().style()!=Qt::NoBrush) { double pen=painter()->pen().widthF()*ps2mm; double millWidth=SetMillingTool(pen*10, false)/10.0; QPainterPath Path=path; QPainterPathStroker Stroker; Stroker.setWidth(pen/ps2mm); //Create outline of filled polygon including the line-width. Path=Path.united(Stroker.createStroke(Path)); SimplyfyInternal(Path); //Mill Outline Stroker.setWidth(millWidth/ps2mm); QPainterPath MilledPath=Stroker.createStroke(Path); Path=Path.subtracted(MilledPath); SimplyfyInternal(Path); drawPathInternal(Transform.map(Path)); //Clear Stroker.setWidth(0.75*millWidth/ps2mm); while (!Path.isEmpty()) { MilledPath=Stroker.createStroke(Path); Path=Path.subtracted(MilledPath); SimplyfyInternal(Path); drawPathInternal(Transform.map(Path)); } } else { double pen=painter()->pen().widthF(); int width=(int)( (pen*ps2mm) *10.0); if (width==0) width=32; int millWidth=SetMillingTool(width); if (millWidth<0) { width=32; int millWidth=SetMillingTool(width); } drawPathInternal(Transform.map(path)); } } void GCodePainterEngine::drawPoints(const QPointF *points, int pointCount) { qDebug()<<"GCodePainterEngine::drawPoints(const QPointF *points, int pointCount) is unsupported!\n"; } void GCodePainterEngine::drawPoints(const QPoint *points, int pointCount) { qDebug()<<"GCodePainterEngine::drawPoints(const QPoint *points, int pointCount) is unsupported!\n"; } void GCodePainterEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) { qDebug()<<"GCodePainterEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) is unsupported!\n"; } void GCodePainterEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) { qDebug()<<"GCodePainterEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) is unsupported!\n"; } void GCodePainterEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) { qDebug()<<"GCodePainterEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) is unsupported!\n"; } void GCodePainterEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) { qDebug()<<"GCodePainterEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) is unsupported!\n"; } void GCodePainterEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) { qDebug()<<"GCodePainterEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) is unsupported!\n"; } void GCodePainterEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags /*= Qt::AutoColor*/) { qDebug()<<"GCodePainterEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags /*= Qt::AutoColor*/) is unsupported!\n"; } QPaintEngine::Type GCodePainterEngine::type() const { return QPaintEngine::User; } int GCodePainterEngine::SetDrillingTool(int width) { QList<int>DrillIds=Drills.keys(); int bestDrill=-1; double bestMatch=std::numeric_limits<bool>::max(); bool perfectMatch=false; int DrillWidth; for (int i=0;i<DrillIds.count();i++) { if (width==Drills[DrillIds[i]]) { perfectMatch=true; bestDrill=DrillIds[i]; DrillWidth=width; bestMatch=-1; } } if (!perfectMatch) { qDebug()<<"GCodePainterEngine only supports perfect line-width <-> tool-diameter matching!\n"; return -1; } GCode Code; Code.Cmd=6; Code.Code=CodeType_MCode; Code.Parameters["T"]=QString("%1").arg(1000+bestDrill); GCodeListing.append(Code); Code.Parameters.clear(); return DrillWidth; } int GCodePainterEngine::SetMillingTool(int width, bool ForcePerfect/*=true*/) { QList<int>MillIds=Mills.keys(); int bestMill=-1; double bestMatch=std::numeric_limits<double>::max(); bool perfectMatch=false; int MillWidth; for (int i=0;i<MillIds.count();i++) { if (width==Mills[MillIds[i]]) { perfectMatch=true; bestMill=MillIds[i]; MillWidth=width; bestMatch=0; break; } if (abs(width-Mills[MillIds[i]])<bestMatch) { bestMill=MillIds[i]; MillWidth=Mills[MillIds[i]]; bestMatch=abs(width-Mills[MillIds[i]]); } } if (!perfectMatch && ForcePerfect) { qDebug()<<"GCodePainterEngine only supports perfect line-width <-> tool-diameter matching!\n"; return -1; } GCode Code; Code.Cmd=6; Code.Code=CodeType_MCode; Code.Parameters["T"]=QString("%1").arg(bestMill); GCodeListing.append(Code); Code.Parameters.clear(); return MillWidth; }