<|start_filename|>src/readersstruct.h<|end_filename|>
#ifndef READERS_STRUCT_H
#define READERS_STRUCT_H
#include "libgeometry/libgeometry.h"

#define MAXIMUM_APPERTURE_COUNT 1000
#define MAXIMUM_TOOL_COUNT 1000
#define MAX_LINE_LENGTH 1024

enum Shape {
    SHAPE_ROUND,
    SHAPE_RECT
};

typedef struct {
   Shape sh;
   float x;
   float y;
} Apperture;

typedef struct tPath{
    GShape *firstElement;
    tPath *next;
} Path;

enum Units {
    UNITS_MM,
    UNITS_INC
};

enum Mirror {
    M_NO = 0x00,
    M_VERTICAL = 0x01,
    M_HORIZONTAL = 0x02,
    M_BOTH = 0x03
};

enum Coord {
    COORD_ABSOLUTE,
    COORD_RELATIVE
};

enum Interpolation {
    INTERPOLATION_LINEAR,
    INTERPOLATION_ROUND_CW,
    INTERPOLATION_ROUND_CCW,
    INTERPOLATION_FULLROUND
};

enum ZeroMode {
    ZM_LEADZERO,
    ZM_POSTZERO,
    ZM_DOTTED
};

typedef struct tDrill{
    GPoint *firstPoint;
    tDrill *next;
    float diameter;
} Drill;

namespace Readers {
    char *readArgi(char *ptr, int * result);

    char *readArgf(char *ptr, float * result);
}

#endif // READERS_STRUCT_H


<|start_filename|>src/pathcontur.cpp<|end_filename|>
#include "pathcontur.h"
#include "gerberreader.h"
#include "libgeometry/libgeometry.h"

PathContur::PathContur(double boardXSize, double boardYSize, double toolDiameter)
    :mFirst(0), mToolDiameter(toolDiameter)
{
    buildContur(boardXSize, boardYSize);
}

PathContur::~PathContur()
{
    while(mFirst) {
        GShape *next = mFirst->next;
        delete mFirst;
        mFirst = next;
    }
    while(mEdges) {
        GShape *next = mEdges->next;
        delete mEdges;
        mEdges = next;
    }
}

GShape *PathContur::first()
{
    return mFirst;
}

void PathContur::buildContur(double boardXSize, double boardYSize)
{
    // board contur
    const double  offset = 0.0L;
    mEdges = new GLine(GPoint(offset,offset), GPoint(boardXSize-offset,offset),toolDiameter());
    mEdges->next = new GLine(GPoint(boardXSize-offset,offset), GPoint(boardXSize-offset,boardYSize-offset),toolDiameter());
    mEdges->next->next = new GLine(GPoint(boardXSize-offset,boardYSize-offset), GPoint(offset,boardYSize-offset),toolDiameter());
    mEdges->next->next->next = new GLine(GPoint(offset,boardYSize-offset), GPoint(offset,offset),toolDiameter());
}

GShape *PathContur::edges()
{
    return mEdges;
}

double PathContur::toolDiameter()
{
    return mToolDiameter;
}

const char *PathContur::getWarnings()
{
    return mWarnings;
}


<|start_filename|>src/pathcontur.h<|end_filename|>
#ifndef PATHCONTUR_H
#define PATHCONTUR_H

class GShape;
typedef struct tPath Path;

typedef struct {
    float x;
    float y;

} ConturElement;

class PathContur
{
public:
    PathContur(double boardXSize, double boardYSize, double toolDiameter);
    ~PathContur();
    GShape *first();
    GShape *edges();
    double toolDiameter();
    const char *getWarnings();
protected:
    GShape *mFirst;
    GShape *mEdges;
    double mToolDiameter;
    const char* mWarnings;
private:
    void buildContur(double boardXSize, double boardYSize);
};

#endif // PATHCONTUR_H


<|start_filename|>src/gcodeexport.cpp<|end_filename|>
#include "gcodeexport.h"
#include "pathcontur.h"
#include "drillreader.h"
#include "libgeometry/libgeometry.h"
#include <stdio.h>
#include <cfloat>
#include <math.h>
#include <assert.h>

enum ShapeClosestpointPlace {
    SCP_WHOLECIRCLE,
    SCP_P1_FROM,
    SCP_P2_TO
};

#define SPEED_ON_IDLE 1800.0L // mm/min - very approximate machine idle speed
#define ACCELERATION 720000.0L // mm/min^2 very approximate machine axis acceleration
#define COMMAND_DELAY 0.0016L // 100 ms - delya to make a command to machine
#define E_SAFE 1.0f // 1 mm - move back filament while head moving

// time calculation is absurd!

double GCodeExport::timeForMovement(double speed, double distance) // calc time for movement
{
    const double accDistance = speed*speed/2.0L/ACCELERATION; // distance for acceleratrion for speed
    if(accDistance*2.0L<=distance) // if we can accelerate to max speed and brake on given distance
        return COMMAND_DELAY + 2.0L*speed/ACCELERATION + (distance-accDistance*2.0L)/speed;
    // otherwise
    return COMMAND_DELAY + 2.0L*sqrt(fabs(distance)/ACCELERATION); // t = sqrt(2S/a), give half distnace on acceleration and brake
}

void GCodeExport::probe(FILE *fl, double zsafe)
{
    char lineBuffer[256];
    fputs("G30 (probe Z)\n", fl);
    fputs("G92 (set founded Z as zero)\n", fl);
    sprintf(lineBuffer,"G00Z%f\n", zsafe);
    fputs(lineBuffer, fl);
    totalTime += timeForMovement(SPEED_ON_IDLE, zsafe);
    headZPosition = zsafe;
}

void GCodeExport::changeTool(FILE *fl, double drillDiameter, double oldZsafe, double zsafe, bool sameDiameter, bool isProbe, char *reason, bool moveToZeroFirst) {
    char lineBuffer[256];
    char tmpChars[128];
    if(headZPosition!=oldZsafe) {
        sprintf(lineBuffer,"G00Z%f\n", oldZsafe);
        fputs(lineBuffer, fl);
        totalTime += timeForMovement(SPEED_ON_IDLE, oldZsafe-headZPosition);
        headZPosition = oldZsafe;
    }

    if(moveToZeroFirst) {
        sprintf(lineBuffer,"G00X%fY%f\n", 0.0f, 0.0f);
        fputs(lineBuffer, fl);
        totalTime += timeForMovement(SPEED_ON_IDLE,  sqrt(headPosition.x*headPosition.x+headPosition.y*headPosition.y));
    }

    headPosition.x = 0.0L; headPosition.y = 0.0L;
    if(sameDiameter)
        sprintf(tmpChars, "%s", reason);
    else
        sprintf(tmpChars, "%s diameter %f mm", reason, drillDiameter);
    sprintf(lineBuffer, "M06T%d (%s)\n", toolNumber++, tmpChars);
    fputs(lineBuffer, fl);

    if(!moveToZeroFirst) {
        sprintf(lineBuffer,"G00X%fY%f\n", 0.0f, 0.0f);
        fputs(lineBuffer, fl);
        totalTime += timeForMovement(SPEED_ON_IDLE,  sqrt(headPosition.x*headPosition.x+headPosition.y*headPosition.y));
    }

    if(isProbe) {
        probe(fl, zsafe);
    } else {
        sprintf(lineBuffer,"G00Z%f\n", zsafe);
        fputs(lineBuffer, fl);
    }


    totalTime += 2.0L; // 2 min for manual tool changing with thought that head will visited parking place and return back
}

void GCodeExport::millingCircle(FILE *fl, Mode mode, const GPoint &center, const GPoint &to, double depth, int millingSpeed, bool isCCW, double circleDistance, double filamentMMPerHeadMM, double laserEnergy, double nozzleDiameter) {
    char lineBuffer[256];
    if(headZPosition!=depth && mode!=Lasering) {
        sprintf(lineBuffer,"G01Z%fF%d\n",depth, millingSpeed);
        fputs(lineBuffer, fl);
        totalTime += timeForMovement(millingSpeed, depth-headZPosition);
        headZPosition = depth;
    }
    char printBuff[64] = "\0";
    if(mode==Lasering) {
        double laserPower = nozzleDiameter*laserEnergy*millingSpeed/2.0L/6000.0L; // hell math. /6000 - recalc to unis
        sprintf(printBuff, "C%f", laserPower);
    } else if(mode==Extrudering) {
        double fmm = circleDistance*filamentMMPerHeadMM;
        sprintf(printBuff, "E%f", ePosition+fmm);
        filamentUsed += fmm;
        ePosition = ePosition + fmm;
    }
    sprintf(lineBuffer,"G0%dX%fY%fI%fJ%f%sF%d\n", isCCW?3:2, to.x, to.y, center.x-headPosition.x, center.y-headPosition.y, printBuff, millingSpeed);
    fputs(lineBuffer, fl);
    headPosition = to;
    totalTime += timeForMovement(millingSpeed, circleDistance);
}

void GCodeExport::millingLineTo(FILE *fl, Mode mode, const GPoint &to, double depth, int millingSpeed, double filamentMMPerHeadMM, double laserEnergy, double nozzleDiameter) {
    char lineBuffer[256];
    if(headZPosition!=depth && mode!=Lasering) {
        sprintf(lineBuffer,"G01Z%fF%d\n", depth, millingSpeed);
        fputs(lineBuffer, fl);
        totalTime += timeForMovement(millingSpeed, depth-headZPosition);
        headZPosition = depth;
    }
    double distance = sqrt((to.x-headPosition.x)*(to.x-headPosition.x)+(to.y-headPosition.y)*(to.y-headPosition.y));
    char printBuff[64] = "\0";
    if(mode==Lasering) {
        double laserPower = nozzleDiameter*laserEnergy*millingSpeed/6000.0L; // hell math. /6000 - recalc to unis
        sprintf(printBuff, "C%f", laserPower);
    } else if(mode==Extrudering){
        double fmm = distance*filamentMMPerHeadMM;
        sprintf(printBuff, "E%f", ePosition+fmm);
        filamentUsed += fmm;
        ePosition = ePosition + fmm;
    }
    sprintf(lineBuffer,"G01X%fY%f%sF%d\n", to.x, to.y, printBuff, millingSpeed);
    fputs(lineBuffer, fl);
    totalTime += timeForMovement(millingSpeed, distance);
    headPosition = to;
}

void GCodeExport::moveHeadTo(FILE *fl, Mode mode, const GPoint &to, double zsafe) {
    char lineBuffer[256];
    if(headZPosition!=zsafe && mode!=Lasering) {
        if(mode==Extrudering) {
            sprintf(lineBuffer,"G00E%f\n", ePosition-E_SAFE);
            fputs(lineBuffer, fl);
            totalTime += timeForMovement(SPEED_ON_IDLE, E_SAFE);
        }
        sprintf(lineBuffer,"G00Z%f\n", zsafe);
        fputs(lineBuffer, fl);
        if(mode==Extrudering) {
            sprintf(lineBuffer,"G00E%f\n", ePosition);
            fputs(lineBuffer, fl);
            totalTime += timeForMovement(SPEED_ON_IDLE, E_SAFE);
        }
        totalTime += timeForMovement(SPEED_ON_IDLE, zsafe-headZPosition);
        headZPosition = zsafe;
    }
    sprintf(lineBuffer,"G00X%fY%f\n", to.x, to.y);
    fputs(lineBuffer, fl);
    totalTime += timeForMovement(SPEED_ON_IDLE, sqrt((to.x-headPosition.x)*(to.x-headPosition.x)+(to.y-headPosition.y)*(to.y-headPosition.y)));
    headPosition = to;
}

void GCodeExport::drill(FILE *fl, const GPoint &point, double drillDepth, int drillingSpeed, int drillingLiftSpeed, double zsafe)
{
    char lineBuffer[256];
    moveHeadTo(fl, Milling, point, zsafe);
    sprintf(lineBuffer,"G01Z%fF%d\n", drillDepth, drillingSpeed);
    totalTime += timeForMovement(drillingSpeed, fabs(zsafe - drillDepth));
    fputs(lineBuffer, fl);
    sprintf(lineBuffer,"G01Z%fF%d\n", zsafe, drillingLiftSpeed);
    fputs(lineBuffer, fl);
    totalTime += timeForMovement(drillingLiftSpeed, fabs(zsafe - drillDepth));
}

void GCodeExport::timeToText(double time, char *buff)
{
    unsigned int minutes = round(time);
    unsigned int hours = minutes/60;
    minutes = minutes%60;
    buff[0] = 0;
    if(hours>0)
        sprintf(buff, (hours>1)?"%d hours":"%d hour", hours );
    sprintf(buff, (minutes>1)?"%s %d minutes":"%s %d minute", buff, minutes);
}

void GCodeExport::putFooterComments(FILE *fl, bool filamentReport)
{
    char lineBuffer[256];
    char timeBuffer[128];
    fputs("(Generated by PCBConverter by <NAME>)\n", fl);
    timeToText(totalTime, timeBuffer);
    sprintf(lineBuffer, "(Approximate time: %s)\n", timeBuffer);
    fputs(lineBuffer, fl);
    if(filamentReport) {
        sprintf(lineBuffer, "(Filament used : %f mm)\n", filamentUsed);
        fputs(lineBuffer, fl);
    }
}

void GCodeExport::doShapes(FILE *fl, Mode mode, GShape *shapes, double diameter, double zsafe, double depth, double speed, double filamentMMPerHeadMM, double laserEnergy)
{
    // mark all shapes as unused
    GShape *nshape = shapes;
    while(nshape) {
        nshape->used = false;
        nshape = nshape->next;
    }

    while(true) {
        double closetShapeDistance = FLT_MAX;
        GShape *closestShape = 0;
        ShapeClosestpointPlace closestPos = SCP_P1_FROM;
        GShape *nextShape = shapes;
        while(nextShape) { // looking for the closest shape
            if(!nextShape->used) {
                double tmpdst;
                switch(nextShape->type()) {
                case GSHAPE_CIRCLE:
                {
                    GCircle *circle = (GCircle*)nextShape;
                    if(circle->spanAngle>=2*M_PI) {
                        if( (tmpdst=fabs(circle->center.distance(headPosition)-circle->radius))<closetShapeDistance ) {
                            closestShape = nextShape;
                            closetShapeDistance = tmpdst;
                            closestPos =  SCP_WHOLECIRCLE;
                            if(tmpdst == 0.0L) goto foundClosest;
                        }
                    } else {
                        if( (tmpdst=circle->fromPoint().distance(headPosition))<closetShapeDistance) {
                            closestShape = nextShape;
                            closetShapeDistance = tmpdst;
                            closestPos =  SCP_P1_FROM;
                            if(tmpdst == 0.0L) goto foundClosest;
                        }
                        if( (tmpdst=circle->toPoint().distance(headPosition))<closetShapeDistance) {
                            closestShape = nextShape;
                            closetShapeDistance = tmpdst;
                            closestPos =  SCP_P2_TO;
                            if(tmpdst == 0.0L) goto foundClosest;
                        }
                    }
                }
                    break;
                case GSHAPE_LINE:
                {
                    GLine *line = (GLine*)nextShape;
                    if( (tmpdst=line->p1.distance(headPosition))<closetShapeDistance) {
                        closestShape = nextShape;
                        closetShapeDistance = tmpdst;
                        closestPos =  SCP_P1_FROM;
                        if(tmpdst == 0.0L) goto foundClosest;
                    }
                    if( (tmpdst=line->p2.distance(headPosition))<closetShapeDistance) {
                        closestShape = nextShape;
                        closetShapeDistance = tmpdst;
                        closestPos =  SCP_P2_TO;
                        if(tmpdst == 0.0L) goto foundClosest;
                    }
                }
                    break;
                case GSHAPE_RECT:
                    assert("Rect in contur while export!");
                    break;
                }
            }
            nextShape = nextShape->next;
        }
        foundClosest:
        if(closestShape) { // put shape in gcode file
            GPoint st;
            GPoint en;
            bool isCCW = false;
            switch(closestShape->type()){
            case GSHAPE_CIRCLE:
            {
                GCircle *circle = (GCircle*)closestShape;
                if(closestPos ==  SCP_P1_FROM) {
                    st = circle->fromPoint();
                    en = circle->toPoint();
                    isCCW = true;
                } else if(closestPos ==  SCP_P2_TO) {
                    st = circle->toPoint();
                    en = circle->fromPoint();
                    isCCW = false;
                } else if(closestPos ==  SCP_WHOLECIRCLE) {
                    isCCW = false;
                    GLine testLine(circle->center, headPosition);
                    GPoint p1, p2;
                    int intersectCount = GIntersects::lineWithCircle(testLine,*circle, &p1, &p2, false);
                    if(intersectCount==0){
                        st = circle->center+GPoint(circle->radius, 0.0L);
                        en = st;
                    } else {
                        if(intersectCount==1 || p1.distance(headPosition)<p2.distance(headPosition)){
                            st = p1;
                            en = p1;
                        } else {
                            st = p2;
                            en = p2;
                        }
                    }
                }
            }
                break;
            case GSHAPE_LINE:
            {
                GLine *line = (GLine*)closestShape;
                if(closestPos ==  SCP_P1_FROM) {
                    st = line->p1;
                    en = line->p2;
                } else if(closestPos ==  SCP_P2_TO) {
                    st = line->p2;
                    en = line->p1;
                } else {
                    assert("Rect in contur while export2!");
                }
            }
                break;
            case GSHAPE_RECT:
                assert("Rect in contur while export3!");
                break;
            }

            if(st.distance(headPosition)>diameter) {
                moveHeadTo(fl, mode, st, zsafe);
            } else if (st.distance(headPosition)!=0.0L) {
                millingLineTo(fl, mode, st, depth, speed, filamentMMPerHeadMM, laserEnergy, diameter);
            }
            if(closestShape->type()==GSHAPE_LINE) {
                millingLineTo(fl, mode, en, depth, speed, filamentMMPerHeadMM, laserEnergy, diameter);
            } else if(closestShape->type()==GSHAPE_CIRCLE) {
                GCircle* circle = (GCircle*)closestShape;
                millingCircle(fl, mode, ((GCircle*)closestShape)->center,en,depth,speed,isCCW, circle->spanAngle*circle->radius, filamentMMPerHeadMM, laserEnergy*(circle->outer?1.0L:1.5L), diameter);
            }

            closestShape->used = true;
        } else
            break; // from while(true)
    }

    if(headZPosition!=zsafe) {
        char lineBuffer[256];
        // raise head
        sprintf(lineBuffer, "G00Z%f\n", zsafe); // for the same style numeric
        fputs(lineBuffer, fl);
        totalTime += timeForMovement(SPEED_ON_IDLE, zsafe-headZPosition);
        headZPosition = zsafe;
    }
}

void GCodeExport::doDrill(FILE *fl, bool changeToDrill, DrillReader *drillReader, double drillZSafe, bool sameDiameter, bool isProbe, int drillingSpeed, \
             int drillingLiftSpeed, int drillingSpindleSpeed, double drillDepth, double zsafe)
{
    char lineBuffer[1024];
    // drilling
    if(drillReader) {
        if(drillReader->firstDrill()) {
            toolNumber = 1;
            if(changeToDrill)
                changeTool(fl, drillReader->firstDrill()->diameter, zsafe, drillZSafe, sameDiameter, isProbe, (char*)"Drill",true);
            else if(isProbe)
                probe(fl, drillZSafe);
            sprintf(lineBuffer, "M03S%d\nG04P%d\n", drillingSpindleSpeed, drillingSpindleSpeed/5); // start spindle and wait
            fputs(lineBuffer, fl);
            Drill *nextDrill = drillReader->firstDrill();
            if(sameDiameter || !changeToDrill) {
                sprintf(lineBuffer,"(Drill %f)\n", nextDrill->diameter);
                fputs(lineBuffer, fl);
            }
            while(nextDrill) {
                GPoint *next = nextDrill->firstPoint;
                while(next) {
                    next->used = false;
                    next = next->next;
                }
                while(true) {
                    GPoint *closestPoint = 0;
                    double closestDistance = FLT_MAX;
                    next = nextDrill->firstPoint;
                    double tmp;
                    while(next) {
                        if(!next->used){
                            if( (tmp=next->distance(headPosition))<closestDistance) {
                                closestDistance = tmp;
                                closestPoint = next;
                            }
                        }
                        next = next->next;
                    }
                    if(closestPoint) {
                        drill(fl, *closestPoint, drillDepth, drillingSpeed, drillingLiftSpeed, drillZSafe);
                        closestPoint->used = true;
                    } else {
                        break; // goto from while
                    }
                }

                nextDrill = nextDrill->next;
                if(nextDrill) {
                    if(!sameDiameter) {
                        fputs("M5\n", fl);
                        changeTool(fl, nextDrill->diameter, drillZSafe, drillZSafe, sameDiameter, isProbe, (char*)"Drill", true);
                        sprintf(lineBuffer, "M03S%d\nG04P%d\n", drillingSpindleSpeed, drillingSpindleSpeed/5); // start spindle and wait
                        fputs(lineBuffer, fl);
                    } else {
                        sprintf(lineBuffer,"(Drill %f)\n", nextDrill->diameter);
                        fputs(lineBuffer, fl);
                    }
                }
            }
            fputs("M5\n", fl);
        }
    }
}

FILE *GCodeExport::organizeSecondFile(FILE *fl, char *outputfile, bool filamentReport)
{
    char lineBuffer[1024];
    putFooterComments(fl, filamentReport);
    fclose(fl);
    totalTime = 0.0L;
    filamentUsed = 0.0L;
    int i = 0;
    int lPoint = -1;
    while(outputfile[i]) {
        if(outputfile[i]=='.')
            lPoint = i;
        lineBuffer[i] = outputfile[i];
        i++;
    }
    if(lPoint>0) {
        i = lPoint;
        lineBuffer[i++]='-';
        lineBuffer[i++]='2';
        while(outputfile[lPoint])
            lineBuffer[i++] = outputfile[lPoint++];
    } else {
        lineBuffer[i]='2';
    }
    lineBuffer[i] = 0;

    fl = fopen(lineBuffer, "w");
    if(fl==0)
        return fl;
    fputs("%\nG90G40G17G21\nG92\n", fl);
    return fl;
}

GCodeExport::GCodeExport(char *outputfile, PathContur *contur1, PathContur *contur2, Mode mode, double depth, double edgedepth, \
                                 double zsafe, int millingSpeed, int edgeMillingSpeed, int spindleRpm, double millingDiameter, \
                                 DrillReader *drillReader, int drillingSpeed, int drillingLiftSpeed, int drillingSpindleSpeed, \
                                 double drillDepth, double drillZSafe, bool sameDiameter , bool isProbe, double filamentDiameter, \
                                 double nozzleDiameter, int extruderTemperature, double printingSpeed, bool edges, double laserEnergy, \
                                 double toolXOffset, double toolYOffset)
{
    totalTime = 0.0L;
    filamentUsed = 0.0L;
    char lineBuffer[1024];
    if(contur1==0) {
        totalTime = -1.0L;
        return;
    }
    if(contur1->first()==0) {
        totalTime = -1.0L;
        return;
    }
    FILE *fl = fopen(outputfile, "w");
    if(fl==0) {
        totalTime = -1.0L;
        return;
    }
    double firstTime = 0.0L;
    double firstFilament = 0.0L;
    const double filamentMMPerHeadMM = nozzleDiameter*nozzleDiameter/(filamentDiameter*filamentDiameter);
    // writing gcode headers
    fputs("%\nG90G40G17G21\n", fl);

    switch (mode) {
    case Milling:
        if(isProbe) {
            fputs("G30 (probe Z)\n", fl);
        }
        fputs("G92 (set founded Z as zero)\n", fl);
        sprintf(lineBuffer, "G00Z%f\n", zsafe);
        fputs(lineBuffer, fl);
        headPosition = GPoint(0.0L,0.0L);
        headZPosition = zsafe;

        sprintf(lineBuffer, "M03S%d\nG04P%d\n", spindleRpm, spindleRpm/5); // start spindle and wait
        fputs(lineBuffer, fl);

        doShapes(fl, mode, contur1->first(), millingDiameter, zsafe, depth, millingSpeed, filamentMMPerHeadMM, laserEnergy);
        if(edges) {
            fputs("(Cutting edges)\n", fl);
            doShapes(fl, mode, contur1->edges(), millingDiameter, zsafe, edgedepth, edgeMillingSpeed, filamentMMPerHeadMM, laserEnergy);
            fputs("(Cutting edges done)\n", fl);
        }
        fputs("M5\n", fl);
        doDrill(fl, true, drillReader, drillZSafe, sameDiameter, isProbe, drillingSpeed, drillingLiftSpeed, drillingSpindleSpeed, drillDepth, zsafe);

        if(contur2) {
            if(contur2->first()) {
                fputs("G28 (Parking)\n", fl);
                headZPosition = zsafe;
                changeTool(fl, millingDiameter, zsafe, zsafe, true, isProbe, (char*)"Insert milling tool and turn board", false);
                sprintf(lineBuffer, "M03S%d\nG04P%d\n", spindleRpm, spindleRpm/5); // start spindle and wait
                fputs(lineBuffer, fl);
                doShapes(fl, mode, contur2->first(), millingDiameter, zsafe, depth, millingSpeed, filamentMMPerHeadMM, laserEnergy);
            }
        }
        break;
    case Printing:
        fputs("G92\n", fl);
        sprintf(lineBuffer, "G00Z%f\n", zsafe);
        fputs(lineBuffer, fl);
        headPosition = GPoint(0.0L,0.0L);
        headZPosition = zsafe;

        doShapes(fl, mode, contur1->first(), millingDiameter, zsafe, 0.0f, printingSpeed, filamentMMPerHeadMM, laserEnergy);
        sprintf(lineBuffer, "G00X%fY%f (Switch to tools coords)\nG92\n", toolXOffset, toolYOffset); // change coords to tool mode
        fputs(lineBuffer, fl);
        doDrill(fl, true, drillReader, drillZSafe, sameDiameter, isProbe, drillingSpeed, drillingLiftSpeed, drillingSpindleSpeed, drillDepth, zsafe);

        if(edges) {
            changeTool(fl, millingDiameter, drillZSafe, zsafe, true, isProbe, (char*)"Insert milling tool", true);
            sprintf(lineBuffer, "M03S%d\nG04P%d\n", spindleRpm, spindleRpm/5); // start spindle and wait
            fputs(lineBuffer, fl);
            fputs("(Cutting edges)\n", fl);
            doShapes(fl, Milling, contur1->edges(), millingDiameter, zsafe, edgedepth, edgeMillingSpeed, filamentMMPerHeadMM, laserEnergy);
            fputs("M5\n", fl);
            fputs("(Cutting edges done)\n", fl);
        }
        if(contur2) {
            if(contur2->first()) {
                moveHeadTo(fl,mode, GPoint(0.0L, 0.0L), zsafe);
                fputs("(Remove milling tool, turn board, prepare pen position manually)\n", fl);
                firstTime = totalTime;
                firstFilament = filamentUsed;
                fl = organizeSecondFile(fl, outputfile, false);
                if(fl==0) {
                    totalTime = -1.0L;
                    return;
                }
                headPosition = GPoint(0.0L,0.0L);
                headZPosition = 0.0L;
                doShapes(fl, mode, contur2->first(), millingDiameter, zsafe, 0.0f, printingSpeed, filamentMMPerHeadMM, laserEnergy);
            }
        }
        break;
    case Extrudering:
        sprintf(lineBuffer, "M109S%d ; wait temperaturen\n", extruderTemperature);
        fputs(lineBuffer, fl);
        fputs("M82\n", fl);
        fputs("G92 (set all axis to zero)\n", fl);
        headPosition = GPoint(0.0L,0.0L);
        headZPosition = 0.0L;
        ePosition = 0.0L;
        fputs("(Start clean extruder)\n", fl);
        doShapes(fl, mode, contur1->edges(), nozzleDiameter, zsafe, 0.0f, printingSpeed, filamentMMPerHeadMM, laserEnergy);
        fputs("(Extruder cleaning done)\n", fl);

        doShapes(fl, mode, contur1->first(), nozzleDiameter, zsafe, 0.0f, printingSpeed, filamentMMPerHeadMM, laserEnergy);
        sprintf(lineBuffer, "G00X%fY%f (Switch to tools coords)\nG92\n", toolXOffset, toolYOffset); // change coords to tool mode
        fputs(lineBuffer, fl);
        doDrill(fl, true, drillReader, drillZSafe, sameDiameter, isProbe, drillingSpeed, drillingLiftSpeed, drillingSpindleSpeed, drillDepth, zsafe);
        if(edges) {
            changeTool(fl, millingDiameter, drillZSafe, zsafe, true, isProbe, (char*)"Insert milling tool", true);
            sprintf(lineBuffer, "M03S%d\nG04P%d\n", spindleRpm, spindleRpm/5); // start spindle and wait
            fputs(lineBuffer, fl);
            fputs("(Cutting edges)\n", fl);
            doShapes(fl, Milling, contur1->edges(), millingDiameter, zsafe, edgedepth, edgeMillingSpeed, filamentMMPerHeadMM, laserEnergy);
            fputs("M5\n", fl);
            fputs("(Cutting edges done)\n", fl);
        }

        if(contur2) {
            if(contur2->first()) {
                moveHeadTo(fl,mode, GPoint(0.0L, 0.0L), zsafe);
                fputs("(Turn board, set head Z positon manualy and run second file)\n", fl);
                firstTime = totalTime;
                firstFilament = filamentUsed;
                fl = organizeSecondFile(fl, outputfile, true);
                if(fl==0) {
                    totalTime = -1.0L;
                    return;
                }
                headPosition = GPoint(0.0L,0.0L);
                headZPosition = 0.0L;
                doShapes(fl, mode, contur2->first(), millingDiameter, zsafe, 0.0f, millingSpeed, filamentMMPerHeadMM, laserEnergy);
            }
        }

        break;
    case Lasering:
        fputs("G92 (set founded Z as zero)\n", fl);
        headPosition = GPoint(0.0L,0.0L);
        headZPosition = 0.0f;
        doShapes(fl, mode, contur1->first(), millingDiameter, 0.0f, depth, printingSpeed, filamentMMPerHeadMM, laserEnergy);
        sprintf(lineBuffer, "G00X%fY%f (Switch to tools coords)\nG92\n", toolXOffset, toolYOffset); // change coords to tool mode
        fputs(lineBuffer, fl);
        doDrill(fl, false, drillReader, drillZSafe, sameDiameter, isProbe, drillingSpeed, drillingLiftSpeed, drillingSpindleSpeed, drillDepth, 0.0f);

        if(edges) {
            changeTool(fl, millingDiameter, drillZSafe, zsafe, true, isProbe, (char*)"Insert milling tool", true);
            sprintf(lineBuffer, "M03S%d\nG04P%d\n", spindleRpm, spindleRpm/5); // start spindle and wait
            fputs(lineBuffer, fl);
            fputs("(Cutting edges)\n", fl);
            doShapes(fl, Milling, contur1->edges(), millingDiameter, zsafe, edgedepth, edgeMillingSpeed, filamentMMPerHeadMM, laserEnergy);
            fputs("M5\n", fl);
            fputs("(Cutting edges done)\n", fl);
        }

        fputs("G28 (Parking)\nM0 (Turn board)\n", fl);
        sprintf(lineBuffer, "G00X%fY%f (Switch to laser coords)\nG92\n",-toolXOffset, -toolYOffset); // change coords to laser mode
        fputs(lineBuffer, fl);
        headPosition = GPoint(0.0L,0.0L);
        headZPosition = 0.0f;
        if(contur2)
            if(contur2->first())
                doShapes(fl, mode, contur2->first(), millingDiameter, 0.0f, depth, printingSpeed, filamentMMPerHeadMM, laserEnergy);
        break;
    }


    fputs("G28 (parking)\n", fl);
    fputs("M30\n", fl);
    putFooterComments(fl, mode==Extrudering);
    totalTime += firstTime;
    filamentUsed += firstFilament;
    fclose(fl);
}

double GCodeExport::filament()
{
    return filamentUsed;
}

double GCodeExport::time()
{
    return totalTime;
}

void GCodeExport::time(char *buff)
{
    timeToText(time(), buff);
}



<|start_filename|>src/libgeometry/gcircle.cpp<|end_filename|>
#include "gcircle.h"
#include "gline.h"
#include "grect.h"
#include "gintersects.h"

GCircle::GCircle(const GPoint &ncenter, double nradius, double ncircleWidth, double nstartAngle, double nspanAngle)
{
    center = ncenter;
    radius = nradius;
    circleWidth = ncircleWidth;
    if(nstartAngle>=2.0L*M_PI) {
        int c = nstartAngle/(2.0L*M_PI);
        nstartAngle -= 2.0L*M_PI*c;
    }
    if(nstartAngle<0.0L) {
        int c = -nstartAngle/(2.0L*M_PI) + 1;
        nstartAngle += 2.0L*M_PI*c;
    }
    startAngle = nstartAngle;
    spanAngle = nspanAngle;
    outer = false;
}

GShapeType GCircle::type() const
{
    return GSHAPE_CIRCLE;
}

void GCircle::move_relative(const GPoint &p)
{
    center = center - p;
}

void GCircle::move_vmirror(double p )
{
    center.y = p - center.y;
}

void GCircle::move_hmirror(double p )
{
    center.x = p - center.x;
}

bool GCircle::intersects(const GShape &other)
{
    switch(other.type()) {
    case GSHAPE_LINE:
        return GIntersects::lineWithCircle((const GLine &)other,*this,0,0, true)!=0;
    case GSHAPE_RECT:
        return GIntersects::circleWithRect(*this, (const GRect &)other);
    case GSHAPE_CIRCLE:
        return GIntersects::circleWithCircle(*this, (const GCircle &)other, 0, 0)!=0;
    }
    return false;
}

GShape *GCircle::contur(double offset, double conturLinesWidth) const
{
    return new GCircle(center,radius+offset+conturLinesWidth/2.0L, conturLinesWidth);
}

GPoint  GCircle::fromPoint()
{
    return GPoint(center.x+radius*cos(startAngle), center.y+radius*sin(startAngle));
}

GPoint  GCircle::toPoint()
{
    return GPoint(center.x+radius*cos(startAngle+spanAngle), center.y+radius*sin(startAngle+spanAngle));
}

double GCircle::chordAngle(double chordLenght) const
{
    if(radius==0.0)
        return nan("");
    return 2.0L*asin(chordLenght/2.0L/radius);
}

double GCircle::anglePoint(const GPoint &point)
{
    double res= (point-center).vectorAngle(GPoint(1.0L,0.0L));
    if(point.y<center.y)
        return 2*M_PI-res;
    return res;
}

double deltaBetweenAngles(double angle1, double angle2)
{
    double tmp = fabs(angle1-angle2);
    if(tmp>M_PI)
        return 2*M_PI-tmp;
    return tmp;
}

void GCircle::cut(const GShape &other)
{
    GPoint point1;
    double angle1=0.0L;
    double angle2 = 0.0L;
    double angles[8];
    GPoint point2;
    double ca = 0.0L;
    int intersectionCount = 0;
    GCircle testCircle1(center,radius-0.99L*circleWidth/2.0L, 0.0L, startAngle,spanAngle);
    GCircle testCircle2(center,radius+0.99L*circleWidth/2.0L, 0.0L, startAngle,spanAngle);

    switch(other.type()) {
    case GSHAPE_CIRCLE:
    {
        const GCircle &circle = (const GCircle &)other;
        GCircle ncircle(circle.center, circle.radius+circle.circleWidth/2.0L);
        ca = chordAngle((circle.circleWidth+circleWidth));
        intersectionCount = GIntersects::circleWithCircle(ncircle, testCircle1, &point1, &point2);
        if(intersectionCount>0)
            angles[0] = testCircle1.anglePoint(point1);
        if(intersectionCount==2)
            angles[1] = testCircle1.anglePoint(point2);
        int secondIntersectionCount = GIntersects::circleWithCircle(ncircle, testCircle2, &point1, &point2);
        if(secondIntersectionCount>0)
            angles[intersectionCount] = testCircle2.anglePoint(point1);
        if(secondIntersectionCount==2)
            angles[intersectionCount+1] = testCircle2.anglePoint(point2);
        intersectionCount += secondIntersectionCount;
    }
        break;
    case GSHAPE_LINE:
    {
        const GLine &oline = (const GLine &)other;

        //debug
        //if(oline.p1.distance(GPoint(59.20, 31.35))<0.2L && center.distance(GPoint(59.20,31.35))<0.1/* && radius>5.65 && radius<5.66*/)
//        if(oline.p1.distance(GPoint(38.61,4.71))<0.2 && oline.p2.distance(GPoint(41.39, 4.71))<0.2 && center.distance(GPoint(38.61,4.71))<0.1 && radius>1.00 && radius<1.1)
//            GPoint().vectorNormalize();

        GPoint dir = oline.p2-oline.p1;
        dir.vectorNormalize();
        dir *= oline.width/2.0L;
        GPoint widthVector(dir.y, -dir.x);
        GLine testLine1(oline.p1-dir+widthVector, oline.p2+dir+widthVector);
        GLine testLine2(oline.p1-dir-widthVector, oline.p2+dir-widthVector);

        double tintersectionCount = GIntersects::lineWithCircle(testLine1,testCircle1,&point1, &point2, true);
        if(tintersectionCount>0)
            angles[intersectionCount] = testCircle1.anglePoint(point1);
        if(tintersectionCount==2)
            angles[intersectionCount+1] = testCircle1.anglePoint(point2);
        intersectionCount += tintersectionCount;
        tintersectionCount = GIntersects::lineWithCircle(testLine1,testCircle2,&point1, &point2, true);
        if(tintersectionCount>0)
            angles[intersectionCount] = testCircle2.anglePoint(point1);
        if(tintersectionCount==2)
            angles[intersectionCount+1] = testCircle2.anglePoint(point2);
        intersectionCount += tintersectionCount;
        tintersectionCount = GIntersects::lineWithCircle(testLine2,testCircle1,&point1, &point2, true);
        if(tintersectionCount>0)
            angles[intersectionCount] = testCircle1.anglePoint(point1);
        if(tintersectionCount==2)
            angles[intersectionCount+1] = testCircle1.anglePoint(point2);
        intersectionCount += tintersectionCount;
        tintersectionCount = GIntersects::lineWithCircle(testLine2,testCircle2,&point1, &point2, true);
        if(tintersectionCount>0)
            angles[intersectionCount] = testCircle2.anglePoint(point1);
        if(tintersectionCount==2)
            angles[intersectionCount+1] = testCircle2.anglePoint(point2);
        intersectionCount += tintersectionCount;
        ca = chordAngle(circleWidth)/3.0L;
    }
        break;
    case GSHAPE_RECT:
    {
        GShape *rlines = other.contur(0.0L, 0.0L);
        while(rlines) {
            if(radius > 0 && rlines->type()==GSHAPE_LINE) {
                GLine *line = (GLine *)rlines;
                cut(*line);
            }
            GShape *tmp = rlines;
            rlines = rlines->next;
            delete tmp;
        }
    }
        if(spanAngle<M_PI) {
            if(other.contains(fromPoint()) || other.contains(toPoint()))
                radius = 0;
        }
        return;
    }
    ca *= 1.5L; // we don't calc enter angle in line, so make some spare space

    // find angles with minimum instrospection
    if(intersectionCount) {
        double maxdelta = -1.0L;
        for(int i=0; i<intersectionCount; i++) {
            for(int j=i; j<intersectionCount; j++) {
                double delta = fabs(angles[i]-angles[j]);
                if(delta>M_PI)
                    delta = 2*M_PI - delta;
                if(delta>maxdelta) {
                    maxdelta = delta;
                    angle1 = angles[i];
                    angle2 = angles[j];
                }
            }
        }
        if(angle1!=angle2)
            intersectionCount = 2;
        else
            intersectionCount = 1;
    }

    // take existing angles
    if(intersectionCount==2) {
        if(!angleContains(angle1)) {
            double tmp;
            if((tmp=deltaBetweenAngles(angle1,startAngle))<ca) {
                spanAngle -= fabs(tmp-ca);
                startAngle += fabs(tmp-ca);
            }
            tmp = startAngle+spanAngle;
            if(tmp>2.0L*M_PI)
                tmp = tmp-2.0L*M_PI;
            if((tmp=deltaBetweenAngles(angle1,tmp))<ca)
                spanAngle -= fabs(tmp-ca);

            intersectionCount = 1;
            angle1 = angle2;
        } else if(!angleContains(angle2)) {
            double tmp;
            if((tmp=deltaBetweenAngles(angle2,startAngle))<ca) {
                spanAngle -= fabs(tmp-ca);
                startAngle += fabs(tmp-ca);
            }
            tmp = startAngle+spanAngle;
            if(tmp>2.0L*M_PI)
                tmp = tmp-2.0L*M_PI;
            if((tmp=deltaBetweenAngles(angle2,tmp))<ca)
                spanAngle -= fabs(tmp-ca);

             intersectionCount = 1;
        }
    }
    if(intersectionCount==1 && !angleContains(angle1)) {
        double tmp;
        if((tmp=deltaBetweenAngles(angle1,startAngle))<ca) {
            spanAngle -= fabs(tmp-ca);
            startAngle += fabs(tmp-ca);
        }
        tmp = startAngle+spanAngle;
        if(tmp>2.0L*M_PI)
            tmp = tmp-2.0L*M_PI;
        if((tmp=deltaBetweenAngles(angle1,tmp))<ca)
            spanAngle -= fabs(tmp-ca);
        intersectionCount = 0;
    }
    if(spanAngle<0.0L) {
        spanAngle = 0.0L;
        radius = 0.0L;
        return;
    }

    if(intersectionCount==1) {
        if(spanAngle>=2*M_PI-ca/2) {
            startAngle = angle1+ca;
            spanAngle = (M_PI-ca)*2.0L;
        } else {
            double osp = spanAngle;
            if( (angle1-ca)<=startAngle )
                spanAngle = 2*M_PI-(startAngle-(angle1-ca));
            else
                spanAngle = angle1-ca-startAngle;
            if(spanAngle>osp) {
                spanAngle = 0.0L;
            }
            double nspan = osp-spanAngle-2*ca;
            if(nspan>0) {
                if(spanAngle == 0.0L) {
                    startAngle = angle1+ca;
                    spanAngle = nspan;
                } else {
                    GCircle *newc = new GCircle(center, radius, circleWidth, angle1+ca, nspan);
                    newc->next = next;
                    next = newc;
                }
            }
        }
    }
    if(intersectionCount==2) {
        if(angle2<angle1) {
            double tmp = angle1;
            angle1 = angle2;
            angle2 = tmp;
        }
        GCircle cutCircle(center,radius,0.0L,angle1,angle2 - angle1);
        if(cutCircle.spanAngle>=M_PI) {
            cutCircle.spanAngle = 2*M_PI - cutCircle.spanAngle;
            cutCircle.startAngle = angle2;
        }
        if(spanAngle>=2*M_PI-ca/2 || cutCircle.angleContains(startAngle)) { // if doesn't cut before or cut more then was cutted
            startAngle = cutCircle.startAngle+cutCircle.spanAngle+ca;
            if(startAngle>=2.0L*M_PI)
                startAngle -= 2.0L*M_PI;
            spanAngle = 2*M_PI - cutCircle.spanAngle-2*ca;
            if(spanAngle<0.0L)
                spanAngle = 0.0L;
        } else {
            double delta = (angle2 - angle1) + 2*ca;
            if(delta>M_PI) {
                delta = 2*M_PI - (angle2 - angle1) + 2*ca;
                double tmp = angle1;
                angle1 = angle2;
                angle2 = tmp;
            }
            double osp = spanAngle;
            if( (angle1-ca)<=startAngle )
                spanAngle = 2*M_PI-(startAngle-(angle1-ca));
            else
                spanAngle = angle1-ca-startAngle;
            double nspan= osp - spanAngle - delta;
            if(spanAngle>osp) {
                nspan = osp - delta+(2.0L*M_PI-spanAngle);
                spanAngle = 0.0L;
            }
            if(nspan>0) {
                if(spanAngle == 0.0L) {
                    startAngle = angle2+ca;
                    spanAngle = nspan;
                } else {
                    GCircle *newc = new GCircle(center, radius, circleWidth, angle2+ca, nspan);
                    newc->next = next;
                    next = newc;
                }
            }
        }
    }
}

bool GCircle::contains(const GPoint &point) const
{
    return center.distance(point)<=radius+circleWidth/2.0L;
}

bool GCircle::angleContains(double angle) const
{
//    double ca = chordAngle(circleWidth)/2.0L;
//    if(spanAngle>=2.0L*(M_PI-ca))
//        return true;

    double to = startAngle+spanAngle;
    double from = startAngle;
    if(from>to) {
        from = startAngle+spanAngle;
        to = startAngle;
    }
    if(to>2*M_PI) {
        if(angle>=0.0L && angle<=to-2.0L*M_PI)
            return true;
        return (angle>=from && angle<=2.0L*M_PI);
    } else
        return (from<=angle && angle<=to);
}


<|start_filename|>src/libgeometry/gpoint.h<|end_filename|>
#ifndef GPOINT_H
#define GPOINT_H

class GPoint {
public:
    GPoint *next;
    bool used;
    GPoint();
    GPoint(double nx, double ny);
    double x;
    double y;
    GPoint & operator=(const GPoint &other);
    bool operator==(const GPoint &other);
    bool operator!=(const GPoint &other);
    const GPoint operator*(double a);
    GPoint & operator*=(double a);
    GPoint & operator/=(double a);
    GPoint & operator+=(const GPoint &other);
    GPoint & operator-=(const GPoint &other);
    double vectorLength() const;
    void vectorNormalize();
    double vectorDot(const GPoint &other) const;
    double vectorAngle(const GPoint &otherVector) const;
    double distance(const GPoint &other) const;
};

const GPoint operator+(const GPoint &p1, const GPoint &p2);
const GPoint operator-(const GPoint &p1, const GPoint &p2);

#endif // GPOINT_H


<|start_filename|>src/libgeometry/gintersects.cpp<|end_filename|>
#include "gintersects.h"
#include "math.h"

bool GIntersects::lineWithLine(const GLine &l1, const GLine &l2, GPoint *ipoint)
{
    double s1_x, s1_y, s2_x, s2_y;
    s1_x = l1.p2.x - l1.p1.x;     s1_y = l1.p2.y - l1.p1.y;
    s2_x = l2.p2.x - l2.p1.x;     s2_y = l2.p2.y - l2.p1.y;

    double s, t;
    s = (-s1_y * (l1.p1.x - l2.p1.x) + s1_x * (l1.p1.y - l2.p1.y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (l1.p1.y - l2.p1.y) - s2_y * (l1.p1.x - l2.p1.x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
    {
        // Collision detected
        if (ipoint) {
            ipoint->x = l1.p1.x + (t * s1_x);
            ipoint->y = l1.p1.y + (t * s1_y);
        }
        return true;
    }

    return false;
}

bool GIntersects::lineWithRect(const GLine &l, const GRect &r)
{
    // Find min and max X for the segment
    double minX = l.p1.x;
    double maxX = l.p2.x;
    if(l.p1.x > l.p2.x)  {
        minX = l.p2.x;
        maxX = l.p1.x;
    }

    // Find the intersection of the segment's and rectangle's x-projections
    if(maxX > (r.bottomRight.x) )
        maxX = (r.bottomRight.x);

    if(minX < (r.topLeft.x))
        minX = (r.topLeft.x);

    if(minX > maxX) // If their projections do not intersect return false
        return false;

    // Find corresponding min and max Y for min and max X we found before

    double minY = l.p1.y;
    double maxY = l.p2.y;

    double dx = l.p2.x - l.p1.x;

    if(fabsf(dx) > 0.0000001f) {
        double a = (l.p2.y - l.p1.y) / dx;
        double b = l.p1.y - a * l.p1.x;
        minY = a * minX + b;
        maxY = a * maxX + b;
    }

    if(minY > maxY) {
        double tmp = maxY;
        maxY = minY;
        minY = tmp;
    }

    // Find the intersection of the segment's and rectangle's y-projections
    if(maxY > (r.topLeft.y))
        maxY = (r.topLeft.y);

    if(minY < (r.bottomRight.y))
        minY = (r.bottomRight.y);

    if(minY > maxY) // If Y-projections do not intersect return false
        return false;

    return true;
}

int GIntersects::lineWithCircle(const GLine &l, const GCircle &c, GPoint *res1, GPoint *res2, bool isSegment)
{
    // isSegment - if true then segment, if false then ray by 2 points
    GPoint vector1toC = l.p1 - c.center;
    GPoint vector2toC = l.p2 - c.center;
    GPoint vectorDelta = vector2toC - vector1toC;

    double A=vectorDelta.vectorDot(vectorDelta);
    double B=2.0L*vectorDelta.vectorDot(vector1toC);
    double C=vector1toC.vectorDot(vector1toC)-c.radius*c.radius;
    if(A==0.0L || A==-0.0L)
        return false;

    bool inter = true;

    if(isSegment) {
        if(-B<0) {
            inter = (C<0);
        } else if(-B<(2.0f*A)) {
            inter = (4.0f*A*C-B*B<0.0L);
        } else {
            inter = (A+B+C<0.0L);
        }
    }

    if(inter){
        double det = B * B - 4 * A * C;
        if(det < 0.0L) // shit hapened - introspection exist, but without solving
            return 0;
        else if(det == 0.0L) { // tangent, one point
            if(res1) {
                double t = -B / (2 * A);
                res1->x = l.p1.x + t*vectorDelta.x;
                res1->y = l.p1.y + t*vectorDelta.y;
            }
            return 1;
        } else { // two point introspection
            GPoint p1 = vectorDelta;
            GPoint p2 = vectorDelta;
            p1 *= ((-B + sqrt(det)) / (2 * A));
            p2 *= ((-B - sqrt(det)) / (2 * A));
            p1 += l.p1;
            p2 += l.p1;
            GPoint *p = res1;
            int intersectionCount = 0;
            double linelength = l.length();
            if(!isSegment || (p1.distance(l.p1)<=linelength && p1.distance(l.p2)<=linelength)) {
                if(p)
                    *p = p1;
                p = res2;
                intersectionCount++;
            }
            if(!isSegment || (p2.distance(l.p1)<=linelength && p2.distance(l.p2)<=linelength)) {
                if(p)
                    *p = p2;
                intersectionCount++;
            }

            return intersectionCount;
        }

    }
    return 0;
}

int GIntersects::circleWithCircle(const GCircle &c1, const GCircle &c2, GPoint *res1, GPoint *res2)
{
    double d = c1.center.distance(c2.center);
    if(d > c1.radius+c2.radius || d < fabs(c1.radius-c2.radius))
        return 0;
    if(d == 0 && c1.radius == c2.radius)
        return 3;
    double a = (c1.radius*c1.radius - c2.radius*c2.radius + d*d) / 2.0L / d;
    double h = sqrt(c1.radius*c1.radius - a*a);
    GPoint p2 = c1.center-c2.center;
    p2 *= a/d;
    p2 += c1.center;
    if(res1) {
        res1->x = p2.x + h*(c2.center.y-c1.center.y)/d;
        res1->y = p2.y - h*(c2.center.x-c1.center.x)/d;
    }
    if(d == c1.radius+c2.radius)
        return 1;
    if(res2) {
        res2->x = p2.x - h*(c2.center.y-c1.center.y)/d;
        res2->y = p2.y + h*(c2.center.x-c1.center.x)/d;
    }
    return 2;
//    double a = (c1.center.x-c2.center.x)*(c1.center.x-c2.center.x)+(c1.center.y-c2.center.y)*(c1.center.y-c2.center.y);
//    double b = (c1.radius+c2.radius)*(c1.radius+c2.radius);
//    int res = 0;
//    if(a==b)
//        res = 1;
//    if(a<b)
//        res = 2;
//    //if(res>0 && res1)
//     //   res1->x = ((c1.center.x * c2.radius) + (c2.center.x.x * c1.radius)) / (c1.radius + c2.radius);

//    return res;
}

bool GIntersects::circleWithRect(const GCircle &c, const GRect &r)
{
    double halfrectwidth = r.width()/2.0L;
    double halfrectheight = r.height()/2.0L;
    double circleDistanceX = fabsf(c.center.x - r.topLeft.x - halfrectwidth);
    double circleDistanceY = fabsf(c.center.y - r.bottomRight.y - halfrectheight);

    if (circleDistanceX > (halfrectwidth + c.radius)) { return false; }
    if (circleDistanceY > (halfrectheight + c.radius)) { return false; }

    if (circleDistanceX <= halfrectwidth) { return true; }
    if (circleDistanceY <= halfrectheight) { return true; }

    float cornerDistance_sq = (circleDistanceX - halfrectwidth)*(circleDistanceX - halfrectwidth) +
            (circleDistanceY - halfrectheight)*(circleDistanceY - halfrectheight);

    return (cornerDistance_sq <= c.radius*c.radius);
}

bool GIntersects::rectWithRect(const GRect &r1, const GRect &r2)
{
    //Y not intersects
    if (r1.bottomRight.y < r2.topLeft.y || r1.topLeft.y > r2.bottomRight.y)
        return false;
    //X not intersects
    if (r1.bottomRight.x < r2.topLeft.x || r1.topLeft.x > r2.bottomRight.x)
        return false;
    return true;
}


<|start_filename|>src/gerberreader.h<|end_filename|>
#ifndef GERBERREADER_H
#define GERBERREADER_H
#include "readersstruct.h"
#include "libgeometry/libgeometry.h"


class GerberReader
{
public:
    GerberReader(char *firstLayer, char *secondLayer, double offset, Mirror firstLayerMirror, Mirror secondLayerMirror);
    ~GerberReader();
    Path *firstLayerPath();
    Path *secondLayerPath();
    const GPoint & getMax();
    const GPoint & getMin();
    const char *getWarnings();

private:
    Units mUnits;
    Coord mCoord;
    Interpolation mInterpolation;
    ZeroMode mZeroMode;
    int mXFormat;
    int mYFormat;
    Apperture *mAppertures[MAXIMUM_APPERTURE_COUNT];
    Apperture *mCurrentAperture;
    float mX;
    float mY;
    float mReadX;
    float mReadY;
    int lastD;
    GPoint pMin;
    GPoint pMax;
    GShape *mFirst;
    GShape *mLast;
    Path *mFirstLayerPath;
    Path *mSecondLayerPath;
    const char * mWarnings;

    void doD();

    void createNext(bool inMove);
    void checkMinMax(float x, float y, float dx, float dy);
    void parseLine(char *line);
    char *readArgi(char *ptr, int * result);
    char *readArgf(char *ptr, float * result);
    char *readCoord(char *ptr, int format, float base, float *result);
    Path *postHandleLayer(GShape *first);
    void applyGeometry(GShape *first, Mirror mirror);
    GShape * readFile(char *filename);
};

#endif // GERBERREADER_H


<|start_filename|>src/GerberElement.h<|end_filename|>
#ifndef GERBERELEMENT_H
#define GERBERELEMENT_H

enum ElementType {
    TYPE_LINE_RECT,
    TYPE_LINE_ROUND,
    TYPE_RECT,
    TYPE_ROUND
};

typedef struct tGerberElement{
    ElementType type;
    float startX;
    float startY;
    float endX;
    float endY;
    float lx;
    float ly;
    tGerberElement * next;
} GerberElement;

bool intersects(GerberElement *a, GerberElement *b);

#endif // GERBERELEMENT_H


<|start_filename|>src/pcbconverter.cpp<|end_filename|>
#include "pcbconverter.h"
#include "ui_pcbconverter.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QProcess>
#include "gerberreader.h"
#include "pcbpathcontur.h"
#include "printingpathcontur.h"
#include "gcodeexport.h"
#include "drillreader.h"
#include "settings.h"

PCBConverter::PCBConverter(QString firstLayer, QString secondLayer, QString drillPath, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::PCBConverter)
{

    mGerberReader = 0;
    mPathContur1 = 0;
    mPathContur2 = 0;
    mDrillReader = 0;
    ui->setupUi(this);
    Settings::load(this);
    ui->generateButton->setEnabled(QFile::exists(ui->firstLayer->text()));
    connect(ui->firstLayer, SIGNAL(textChanged(QString)), this, SLOT(gp_textChanged(QString)));
    connect(ui->boardOffset,SIGNAL(valueChanged(double)), this, SLOT(dataForGenerateChanged()));
    connect(ui->millingDiameter,SIGNAL(valueChanged(double)), this, SLOT(dataForGenerateChanged()));
    connect(ui->conturTimes,SIGNAL(valueChanged(int)), this, SLOT(dataForGenerateChanged()));
    connect(ui->nozzleDiameter,SIGNAL(valueChanged(double)), this, SLOT(dataForGenerateChanged()));
    connect(ui->secondLayer,SIGNAL(textChanged(QString)), this, SLOT(dataForGenerateChanged()));
    connect(ui->drillPath,SIGNAL(textChanged(QString)), this, SLOT(dataForGenerateChanged()));
    connect(ui->firstMirror,SIGNAL(currentIndexChanged(int)), this, SLOT(dataForGenerateChanged()));
    connect(ui->secondMirror,SIGNAL(currentIndexChanged(int)), this, SLOT(dataForGenerateChanged()));

    if(!firstLayer.isEmpty())
        ui->firstLayer->setText(QFileInfo(firstLayer).canonicalFilePath());
    if(!secondLayer.isEmpty())
        ui->secondLayer->setText(QFileInfo(secondLayer).canonicalFilePath());
    if(!drillPath.isEmpty())
        ui->drillPath->setText(QFileInfo(drillPath).canonicalFilePath());

    for(int i=0; i<ui->formLayout->count(); i++)
        if(ui->formLayout->itemAt(i)->layout())
            ui->formLayout->itemAt(i)->layout()->setContentsMargins(0,0,0,6);

    on_methodComboBox_currentIndexChanged(ui->methodComboBox->currentIndex());
    on_cutEdges_stateChanged(ui->cutEdges->checkState());
}

PCBConverter::~PCBConverter()
{
    deleteGeneratedObjects();
    delete ui;
}

void PCBConverter::deleteGeneratedObjects()
{
    mGerberView.clear();
    if(mGerberReader)
        delete mGerberReader;
    mGerberReader = 0;
    if(mPathContur1)
        delete mPathContur1;
    mPathContur1 = 0;
    if(mPathContur2)
        delete mPathContur2;
    mPathContur2 = 0;
    if(mDrillReader)
        delete mDrillReader;
    mDrillReader = 0;
}

void PCBConverter::on_pushButton_clicked()
{
    disconnect(ui->firstLayer, SIGNAL(textChanged(QString)), this, SLOT(gp_textChanged(QString)));
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),QFileInfo(ui->firstLayer->text()).canonicalPath(),"*.gtl *.gbr *.gbl (*.gtl *.gbr *.gbl);;*.* (*.*)");
    if(!fileName.isEmpty()) {
        ui->firstLayer->setText(fileName);
        gp_textChanged(fileName);
    }
    connect(ui->firstLayer, SIGNAL(textChanged(QString)), this, SLOT(gp_textChanged(QString)));
}

void PCBConverter::on_pushButton_2_clicked()
{
    QString path = ui->drillPath->text();
    if(path.isEmpty())
        ui->firstLayer->text();
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),QFileInfo(path).canonicalPath(),"*.drl (*.drl);;*.* (*.*)");
    if(!fileName.isEmpty()) {
        ui->drillPath->setText(fileName);
    }
}

void PCBConverter::on_pushButton_4_clicked()
{
    QString path = ui->secondLayer->text();
    if(path.isEmpty())
        ui->firstLayer->text();
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),QFileInfo(path).canonicalPath(),"*.gtl *.gbr *.gbl (*.gtl *.gbr *.gbl);;*.* (*.*)");
    if(!fileName.isEmpty()) {
        ui->secondLayer->setText(fileName);
    }
}

void PCBConverter::gp_textChanged(const QString &arg1)
{
    dataForGenerateChanged();
    bool fexist = QFile::exists(arg1);
    ui->generateButton->setEnabled(fexist);
    if(fexist) {
        QString res;
        QFileInfo fi(arg1);
        QString fileDir = fi.canonicalPath();
        QString baseName = fi.baseName();
        if(baseName.right(5)=="-B_Cu" || baseName.right(5)=="-F_Cu")
            baseName = baseName.left(baseName.length()-5);
        res = fileDir+"/"+baseName+".drl";
        ui->outputFile->setText(fileDir+"/"+baseName+".ngc");
        QString secondLayer = fi.canonicalFilePath();
        secondLayer = secondLayer.replace("-B_Cu.gbl", "-F_Cu.gtl");
        if(secondLayer==fi.canonicalFilePath())
            secondLayer = secondLayer.replace("-F_Cu.gtl", "-B_Cu.gbl");
        if(secondLayer!=fi.canonicalFilePath()) {
            if(QFile::exists(secondLayer))
                ui->secondLayer->setText(secondLayer);
        }
        if(QFile::exists(res)) {
            ui->drillPath->setText(res);
        } else {
            QDir dir(fileDir);
            QStringList filters;
            filters << "*.drl";
            QStringList filesInDir = dir.entryList(filters);
            if(filesInDir.count()) {
                int findNumMax = -1;
                foreach(QString findName, filesInDir) {
                    int tmp = 0;
                    while(tmp<findName.length() && tmp<baseName.length()) {
                        if(findName[tmp]==baseName[tmp])
                            tmp++;
                        else
                            break;
                    }
                    if(tmp>findNumMax) {
                        res = fileDir+"/"+findName;
                    }
                }
                if(QFile::exists(res))
                    ui->drillPath->setText(res);
            }
        }
    }
}

void PCBConverter::on_exitButton_clicked()
{
    close();
}

void PCBConverter::closeEvent(QCloseEvent * /*event*/)
{
    mGerberView.close();
    Settings::save(this);
}

void PCBConverter::on_generateButton_clicked()
{
    ui->statusLabel->setText("Generating...");
    update();
    deleteGeneratedObjects();
    QString gerberFile1 = ui->firstLayer->text();
    QString gerberFile2 = ui->secondLayer->text();
#ifdef Q_OS_WIN32
    gerberFile1 = gerberFile1.replace("/", "\\");
    gerberFile2 = gerberFile2.replace("/", "\\");
#endif
    Mirror mirror1 = M_NO;
    if(ui->firstMirror->currentIndex() == 1) mirror1 = M_VERTICAL;
    if(ui->firstMirror->currentIndex() == 2) mirror1 = M_HORIZONTAL;
    if(ui->firstMirror->currentIndex() == 3) mirror1 = M_BOTH;
    Mirror mirror2 = M_NO;
    if(ui->secondMirror->currentIndex() == 1) mirror2 = M_VERTICAL;
    if(ui->secondMirror->currentIndex() == 2) mirror2 = M_HORIZONTAL;
    if(ui->secondMirror->currentIndex() == 3) mirror2 = M_BOTH;
    Mirror dmirror = (Mirror)((int)mirror1 ^ (int)mirror2);
    mGerberReader = new GerberReader(gerberFile1.toLocal8Bit().data(), gerberFile2.toLocal8Bit().data(), ui->boardOffset->value(), mirror1, mirror2);
    if(mGerberReader->firstLayerPath()) {
        if(mGerberReader->getWarnings())
            QMessageBox::warning(this, "Gerber warning", tr(mGerberReader->getWarnings()));
        if(ui->methodComboBox->currentIndex()==0)
            mPathContur1 = new PCBPathContur(mGerberReader->firstLayerPath(), mGerberReader->getMax().x,  mGerberReader->getMax().y, ui->millingDiameter->value(), ui->conturTimes->value());
        else
            mPathContur1 = new PrintingPathContur(mGerberReader->firstLayerPath(), mGerberReader->getMax().x,  mGerberReader->getMax().y, ui->nozzleDiameter->value());
        if(ui->methodComboBox->currentIndex()==0)
            mPathContur2 = new PCBPathContur(mGerberReader->secondLayerPath(), mGerberReader->getMax().x,  mGerberReader->getMax().y, ui->millingDiameter->value(), ui->conturTimes->value());
        else
            mPathContur2 = new PrintingPathContur(mGerberReader->secondLayerPath(), mGerberReader->getMax().x,  mGerberReader->getMax().y, ui->nozzleDiameter->value());
        if(mPathContur1->first()) {
            if(mPathContur1->getWarnings())
                QMessageBox::warning(this, "Contur generator warning", tr(mPathContur1->getWarnings()));
            if(mPathContur2->getWarnings())
                QMessageBox::warning(this, "Contur generator warning", tr(mPathContur1->getWarnings()));

            if(!ui->drillPath->text().isEmpty()) {
                if(QFile(ui->drillPath->text()).exists()) {
                    mDrillReader = new DrillReader(ui->drillPath->text().toLocal8Bit().data(), mGerberReader->getMin(), mGerberReader->getMax(), mirror1);
                }
            }

            ui->statusLabel->setText("");
            mGerberView.set(mGerberReader, mPathContur1, mPathContur2, mDrillReader, dmirror);
            if(!ui->drillPath->text().isEmpty()) {
                if(mDrillReader==0)
                    ui->statusLabel->setText("Error read drill file. ");
                else if(mDrillReader->firstDrill()==0) {
                    ui->statusLabel->setText("Empty on invalid drill file. ");
                }
            }
            if(!gerberFile2.isEmpty() && mGerberReader->secondLayerPath()==0)
                ui->statusLabel->setText(ui->statusLabel->text()+"Error read second layer.");

            ui->previewButton->setEnabled(true);
            on_previewButton_clicked();
            on_outputFile_textChanged(ui->outputFile->text());
            return;
        } else
            ui->statusLabel->setText("Error while generate contur");
    } else
        ui->statusLabel->setText("Error while reading gerber file");
    ui->previewButton->setEnabled(false);
    on_outputFile_textChanged(ui->outputFile->text());
}

void PCBConverter::on_previewButton_clicked()
{
    mGerberView.show();
    mGerberView.activateWindow();
    mGerberView.raise();
}

void PCBConverter::on_pushButton_3_clicked()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),ui->outputFile->text(),"*.ngc (*.ngc);;*.* (*.*)");
    if(!fileName.isEmpty()) {
        ui->outputFile->setText(fileName);
    }
}

void PCBConverter::on_outputFile_textChanged(const QString &arg1)
{
    if(mPathContur1)
        ui->writeToFileButton->setEnabled(!arg1.isEmpty() && mPathContur1->first()!=0);
    else
        ui->writeToFileButton->setEnabled(false);
    ui->startMilling->setEnabled(!exportedFileName.isEmpty() && exportedFileName==arg1);
}

void PCBConverter::on_writeToFileButton_clicked()
{
    QString outputFile = ui->outputFile->text();
    if(mPathContur1==0 || outputFile.isEmpty())
        return;
    ui->statusLabel->setText("Exporting...");
    update();
#ifdef Q_OS_WIN32
    outputFile = outputFile.replace("/", "\\");
#endif
    GCodeExport::Mode mode = GCodeExport::Milling;
    if(ui->methodComboBox->currentIndex()==1) mode = GCodeExport::Printing;
    if(ui->methodComboBox->currentIndex()==2) mode = GCodeExport::Extrudering;
    if(ui->methodComboBox->currentIndex()==3) mode = GCodeExport::Lasering;
    GCodeExport exporter(outputFile.toLocal8Bit().data(), mPathContur1, mPathContur2, \
                                 mode, ui->millingDepth->value(), ui->edgeMillingDepth->value(), \
                                 ui->zSafe->value(), ui->millingSpeed->value(), ui->edgeMillingSpeed->value(), \
                                 ui->spindleRpm->value(), ui->millingDiameter->value(), \
                                 mDrillReader, ui->drillingSpeed->value(), ui->drillingLiftSpeed->value(), \
                                 ui->drillingSpindleSpeed->value(), ui->drillDepth->value(), ui->drillZSafe->value(), \
                                 ui->driilThSame->checkState()==Qt::Checked, ui->useProbe->checkState()==Qt::Checked, \
                                 ui->filamentDiameter->value(), ui->nozzleDiameter->value(), \
                                 ui->extruderTemperature->value(), ui->printingSpeed->value(), ui->cutEdges->checkState()==Qt::Checked, \
                                 ui->energy->value(), ui->laserXoffset->value(), ui->laserYoffset->value());
    if(exporter.time()>=0.0L){
        exportedFileName =  ui->outputFile->text();
        ui->startMilling->setEnabled(true);
        char timeBuffer[255];
        exporter.time(timeBuffer);
        QString filament = "";
        if(ui->methodComboBox->currentIndex()==2) {
            long mm = exporter.filament()+0.5L;
            filament = "\nFilament used: "+QString::number(mm)+" mm.";
        }
        ui->statusLabel->setText("Export done.\nApproximate time "+QString(timeBuffer)+filament);
    } else {
        ui->statusLabel->setText("Error while export");
    }
}

void PCBConverter::on_startMilling_clicked()
{
    QString gcodecnc = "gcodecnc";
    QString outputFile = exportedFileName;
#ifdef Q_OS_WIN32
    outputFile = outputFile.replace("/", "\\");
    gcodecnc +=".exe";
#endif
    QStringList arg;
    arg << outputFile;
    if(QProcess::startDetached(gcodecnc, arg))
        ui->statusLabel->setText("");
    else
        ui->statusLabel->setText(gcodecnc+" not found");
}

void PCBConverter::dataForGenerateChanged()
{
    ui->previewButton->setEnabled(false);
    ui->writeToFileButton->setEnabled(false);
    ui->startMilling->setEnabled(false);
}

void PCBConverter::setLayoutVisible(QLayout *layout, bool visible)
{
    if(layout) {
        QWidget *w = ui->formLayout->labelForField(layout);
        if(w) {
            w->setVisible(visible);
        }
        for(int i=0; i<layout->count(); i++)
            if(layout->itemAt(i)->widget())
                layout->itemAt(i)->widget()->setVisible(visible);
            else if (layout->itemAt(i)->spacerItem()) {
                if(visible)
                    layout->itemAt(i)->spacerItem()->changeSize(1,1, QSizePolicy::Expanding, QSizePolicy::Fixed);
                else
                    layout->itemAt(i)->spacerItem()->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
            }
        layout->setContentsMargins(0,0,0,visible?6:0);
    }
}

void PCBConverter::on_methodComboBox_currentIndexChanged(int index)
{
    bool isPCBVisible = index == 0;
    setLayoutVisible(ui->horizontalLayout_6, isPCBVisible);
    setLayoutVisible(ui->horizontalLayout_7, isPCBVisible);
    setLayoutVisible(ui->horizontalLayout_8, isPCBVisible);

    setLayoutVisible(ui->horizontalLayout_25, !isPCBVisible && index==2);
    setLayoutVisible(ui->horizontalLayout_26, !isPCBVisible && index==2);
    setLayoutVisible(ui->horizontalLayout_27, !isPCBVisible);
    setLayoutVisible(ui->horizontalLayout_28, !isPCBVisible);
    setLayoutVisible(ui->horizontalLayout_30, index==3);
    setLayoutVisible(ui->horizontalLayout_31, !isPCBVisible);
    setLayoutVisible(ui->horizontalLayout_32, !isPCBVisible);
}

void PCBConverter::on_cutEdges_stateChanged(int state)
{
    mGerberView.setEdgesVisible(state==Qt::Checked);
}



<|start_filename|>src/libgeometry/libgeometry.h<|end_filename|>
#ifndef LIBGEOMETRY_H
#define LIBGEOMETRY_H

#include "gpoint.h"
#include "gshape.h"
#include "gline.h"
#include "grect.h"
#include "gcircle.h"
#include "gintersects.h"

#endif // LIBGEOMETRY_H


<|start_filename|>src/zoomablegerberview.h<|end_filename|>
#ifndef ZOOMABLEGERBERVIEW_H
#define ZOOMABLEGERBERVIEW_H

#include <QScrollArea>
#include "readersstruct.h"

class GerberView;
class GerberReader;
class PathContur;
class DrillReader;

class ZoomableGerberView : public QScrollArea
{
    Q_OBJECT
public:
    explicit ZoomableGerberView(QWidget *parent = 0);
    void set(GerberReader *reader, PathContur *contur1, PathContur *contur2, DrillReader *drillReader, Mirror drill2mirror);
    void clear();
    void setEdgesVisible(bool edgesVisible);

signals:
    
public slots:

protected:
    void wheelEvent ( QWheelEvent * event );
    void resizeEvent ( QResizeEvent * event );
    void mouseMoveEvent ( QMouseEvent * event );
    void mousePressEvent ( QMouseEvent * event );

private:
    void updateViewSize();
    double getActualZoom();
    void setActualZoom(double zoom);
    GerberView *mView;
    double mZoomCoeficient;
    QSize initSize;
    QPoint mMouseLastPos;
    
};

#endif // ZOOMABLEGERBERVIEW_H


<|start_filename|>src/libgeometry/gshape.h<|end_filename|>
#ifndef GSHAPE_H
#define GSHAPE_H
#include <math.h>
#include "gpoint.h"

enum GShapeType {
    GSHAPE_LINE,
    GSHAPE_CIRCLE,
    GSHAPE_RECT
};

class GShape {
public:
    GShape();
    virtual ~GShape();
    GShape *next;
    bool used;
    virtual GShapeType type() const = 0;
    virtual void move_relative(const GPoint &p) = 0;
    virtual void move_vmirror(double p ) = 0;
    virtual void move_hmirror(double p ) = 0;
    virtual bool intersects(const GShape &other) = 0;
    virtual GShape *contur(double offset, double conturLinesWidth) const = 0;
    virtual void cut(const GShape &other) = 0;
    virtual bool contains(const GPoint &point) const = 0;

};

#endif // GSHAPE_H


<|start_filename|>src/main.cpp<|end_filename|>
#include <QApplication>
#include "pcbconverter.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QString firstLayer;
    QString secondLayer;
    QString drillPath;
    if(argc>1)
        firstLayer = QString(argv[1]);
    if(argc>2)
        secondLayer = QString(argv[2]);
    if(argc>3)
        drillPath = QString(argv[3]);
    PCBConverter converter(firstLayer, secondLayer, drillPath);
    converter.show();
    return a.exec();
}


<|start_filename|>src/libgeometry/gshape.cpp<|end_filename|>
#include "gshape.h"

GShape::GShape()
    :next(0)
{

}

GShape::~GShape()
{

}


<|start_filename|>src/settings.h<|end_filename|>
#ifndef SETTINGS_H
#define SETTINGS_H

class QWidget;

class Settings
{
public:
    static void save(QWidget *mainWidget);
    static void load(QWidget *mainWidget);
private:
    Settings();
};

#endif // SETTINGS_H


<|start_filename|>src/pcbconverter.h<|end_filename|>
#ifndef PCBCONVERTER_H
#define PCBCONVERTER_H

#include <QWidget>
#include "zoomablegerberview.h"
class DrillReader;
class GerberReader;
class PathContur;

class GerberReader;
class PathContur;

namespace Ui {
class PCBConverter;
}

class PCBConverter : public QWidget
{
    Q_OBJECT

public:
    explicit PCBConverter(QString firstLayer, QString secondLayer, QString drillPath, QWidget *parent = 0);
    ~PCBConverter();

protected:
    void closeEvent(QCloseEvent *event);

private slots:

    void on_pushButton_clicked();

    void on_exitButton_clicked();

    void on_pushButton_2_clicked();

    void on_generateButton_clicked();

    void on_previewButton_clicked();

    void on_pushButton_3_clicked();

    void on_outputFile_textChanged(const QString &arg1);

    void on_writeToFileButton_clicked();
    
    void on_startMilling_clicked();

    void dataForGenerateChanged();

    void on_methodComboBox_currentIndexChanged(int index);

    void on_cutEdges_stateChanged(int state);

    void on_pushButton_4_clicked();

    void gp_textChanged(const QString &arg1);

private:
    void setLayoutVisible(QLayout *layout, bool visible);
    void setFormRowVisible(int row, bool visible);
    void deleteGeneratedObjects();
    Ui::PCBConverter *ui;
    GerberReader *mGerberReader;
    PathContur *mPathContur1;
    PathContur *mPathContur2;
    DrillReader *mDrillReader;
    ZoomableGerberView mGerberView;
    QString exportedFileName;
};

#endif // PCBCONVERTER_H


<|start_filename|>src/drillreader.h<|end_filename|>
#ifndef DRILLREADER_H
#define DRILLREADER_H
#include "readersstruct.h"

class DrillReader
{
public:
    DrillReader(char *filename, const GPoint &offset, const GPoint &max, Mirror mirror);
    ~DrillReader();
    Drill *firstDrill();

private:
    Units mUnits;
    Coord mCoord;
    double mXOffset;
    double mYOffset;
    double mXOffset92;
    double mYOffset92;
    Drill *mFirstDrill;
    Drill *mLastDrill;
    Drill *mTools[MAXIMUM_TOOL_COUNT];
    float mReadX;
    float mReadY;
    float mX;
    float mY;
    Drill *mCurrentDrill;
    GPoint *mCurrentDrillLastPoint;

    void parseLine(char *line);
    char *readCoord(char *ptr, float base, float *result);

};

#endif // DRILLREADER_H


<|start_filename|>src/zoomablegerberview.cpp<|end_filename|>
#include "zoomablegerberview.h"
#include "gerberview.h"
#include <QWheelEvent>
#include <QScrollBar>
#include <QApplication>
#include <QDesktopWidget>
#include <QtCore/qmath.h>
#include "QDebug"

#define ZOOM_POW 3.0L

ZoomableGerberView::ZoomableGerberView(QWidget *parent) :
    QScrollArea(parent), mZoomCoeficient(1.0L), initSize(QSize(640,480))
{
    mView = new GerberView(this);
    setWindowTitle(mView->windowTitle());
    setWidget(mView);
    setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}

void ZoomableGerberView::set(GerberReader *reader, PathContur *contur1, PathContur *contur2, DrillReader *drillReader, Mirror drill2mirror)
{
    mView->set(reader, contur1, contur2, drillReader, drill2mirror);
    QSize bs = mView->bestSize();
    const double aspect = (double)bs.width()/(double)bs.height();
    const int h = QApplication::desktop()->height()/2;
    resize(h*aspect, h);
    initSize = maximumViewportSize();
    mZoomCoeficient = 1.0L;
    mView->resize(initSize);
}

void ZoomableGerberView::clear()
{
    mView->clear();
}

void ZoomableGerberView::setEdgesVisible(bool edgesVisible)
{
    mView->setEdgesVisible(edgesVisible);
}

double ensure0to1(double v)
{
    if(v<0.0L)
        return 0.0L;
    if(v>1.0L)
        return 1.0L;
    return v;
}

void ZoomableGerberView::resizeEvent ( QResizeEvent * event )
{
    QPointF p((double)event->oldSize().width()/2.0L+(double)horizontalScrollBar()->value(), (double)event->oldSize().height()/2.0L+(double)verticalScrollBar()->value());
    double xp = ensure0to1(p.x()/(double)mView->width());
    double yp = ensure0to1(p.y()/(double)mView->height());
    updateViewSize();
    horizontalScrollBar()->setValue(qRound64((double)mView->width()*xp) - (viewport()->width()+1)/2);
    verticalScrollBar()->setValue(qRound64((double)mView->height()*yp) - (viewport()->height()+1)/2);
    QScrollArea::resizeEvent(event);
}

void ZoomableGerberView::updateViewSize()
{
    QSize ns = getActualZoom()*initSize;
    if(ns.width()<width() && ns.height()<height()) {
        if(initSize.width()>initSize.height())
            setActualZoom((double)viewport()->width()/(double)initSize.width());
        else
            setActualZoom((double)viewport()->height()/(double)initSize.height());
        ns = getActualZoom()*initSize;
    }
    mView->resize(ns);
}

void ZoomableGerberView::wheelEvent(QWheelEvent * event)
{
    mZoomCoeficient += event->delta()*0.001L;
    if(getActualZoom()>1000.0L)
        setActualZoom(1000.0L);
    if(getActualZoom()<0.001L)
        setActualZoom(0.001L);
    const QPoint cp = QCursor::pos();
    QPoint p = mView->mapFromGlobal(cp);
    QPoint s = mapFromGlobal(cp);
    double xp = ensure0to1((double)p.x()/(double)mView->width());
    double yp = ensure0to1((double)p.y()/(double)mView->height());
    updateViewSize();
    horizontalScrollBar()->setValue(mView->width()*xp-s.x());
    verticalScrollBar()->setValue(mView->height()*yp-s.y());
    event->accept();
}

void ZoomableGerberView::mouseMoveEvent ( QMouseEvent * event )
{
    if(event->buttons()!=Qt::NoButton) {
        horizontalScrollBar()->setValue(horizontalScrollBar()->value()+mMouseLastPos.x()-event->pos().x());
        verticalScrollBar()->setValue(verticalScrollBar()->value()+mMouseLastPos.y()-event->pos().y());
        mMouseLastPos = event->pos();
        event->accept();
    }
}

void ZoomableGerberView::mousePressEvent ( QMouseEvent * event )
{
    mMouseLastPos = event->pos();
}

double ZoomableGerberView::getActualZoom()
{
    return qPow(mZoomCoeficient, ZOOM_POW);
}

void ZoomableGerberView::setActualZoom(double zoom)
{
    mZoomCoeficient =  qPow(zoom, 1.0L/ZOOM_POW);
}


<|start_filename|>src/libgeometry/gcircle.h<|end_filename|>
#ifndef GCIRCLE_H
#define GCIRCLE_H
#include "gshape.h"
#include "gpoint.h"

class GCircle : public GShape
{
public:
    // start and span angle work onle with cut()
    GCircle(const GPoint &ncenter, double nradius, double ncircleWidth = 0.0L, double nstartAngle = 0.0L, double nspanAngle = 2.0L*M_PI);
    GShapeType type() const;
    GPoint fromPoint();
    GPoint toPoint();
    GPoint center;
    double radius;
    double startAngle;
    double spanAngle;
    double circleWidth;
    bool outer;
    void move_relative(const GPoint &p);
    void move_vmirror(double p );
    void move_hmirror(double p );
    bool intersects(const GShape &other);
    GShape *contur(double offset, double conturLinesWidth) const;
    void cut(const GShape &other);
    bool contains(const GPoint &point) const;
    double chordAngle(double chordLenght) const;
    bool angleContains(double angle) const;
    double anglePoint(const GPoint &point);
};

#endif // GCIRCLE_H


<|start_filename|>src/gerberview.h<|end_filename|>
#ifndef GERBERVIEW_H
#define GERBERVIEW_H

#include <QWidget>
#include <QLabel>
#include "readersstruct.h"

class DrillReader;
class GerberReader;
class PathContur;

class GerberView : public QWidget
{
    Q_OBJECT

public:
    GerberView(QWidget *parent = 0);
    ~GerberView();
    void set(GerberReader *reader, PathContur *contur1, PathContur *contur2, DrillReader *drillReader, Mirror drill2mirror);
    void clear();
    QSize bestSize();
    void setEdgesVisible(bool edgesVisible);
protected:
    void paintEvent (QPaintEvent *event);
    QSize sizeHint () const;
    void mouseMoveEvent ( QMouseEvent * event );
    void mousePressEvent ( QMouseEvent * event );
private:
    GShape *mSelectedShape;
    GPoint *mSelectedDrill;
    bool isResultVisible;
    double mSelectedDrillDiamter;
    double getScale();
    double getYoff(double scale);
    void printInfo(QMouseEvent * event);
    GerberReader *mReader;
    QSize mSizeHint;
    PathContur *mContur1;
    PathContur *mContur2;
    PathContur *mContur;
    DrillReader *mDrillReader;
    QLabel *mLabel;
    Path *mPath;
    bool mEdgesVisible;
    Mirror mDrill2Mirror;
};

#endif // GERBERVIEW_H


<|start_filename|>src/libgeometry/grect.cpp<|end_filename|>
#include "gcircle.h"
#include "gline.h"
#include "grect.h"
#include "gintersects.h"
#include <assert.h>

GRect::GRect(const GPoint &center, double width, double height)
{
    topLeft.x = center.x-width/2.0L;
    topLeft.y = center.y+height/2.0L;
    bottomRight.x = center.x+width/2.0L;
    bottomRight.y = center.y-height/2.0L;
}

GRect::GRect(const GPoint &ntopLeft, const GPoint &nbottomRight)
{
    topLeft = ntopLeft;
    bottomRight = nbottomRight;
}

GShapeType GRect::type() const
{
    return GSHAPE_RECT;
}

double GRect::width() const
{
    return bottomRight.x-topLeft.x;
}

double GRect::height() const
{
    return topLeft.y-bottomRight.y;
}
void GRect::move_relative(const GPoint &p)
{
    topLeft = topLeft - p;
    bottomRight = bottomRight - p;
}

void GRect::move_vmirror(double p )
{
    double t =  bottomRight.y;
    bottomRight.y = p - topLeft.y;
    topLeft.y = p - t;
}

void GRect::move_hmirror(double p )
{
    double t =  bottomRight.x;
    bottomRight.x = p - topLeft.x;
    topLeft.x = p - t;
}

bool GRect::intersects(const GShape &other)
{
    switch(other.type()) {
    case GSHAPE_LINE:
        return GIntersects::lineWithRect((const GLine &)other, *this);
    case GSHAPE_RECT:
        return GIntersects::rectWithRect(*this, (const GRect &)other);
    case GSHAPE_CIRCLE:
        return GIntersects::circleWithRect((const GCircle &)other, *this);
    }
    return false;
}

GShape *GRect::contur(double offset, double conturLinesWidth) const
{
    offset += conturLinesWidth/2.0L;
    GPoint p1 = topLeft + GPoint(-offset, offset);
    GPoint p2 = GPoint(bottomRight.x, topLeft.y)+ GPoint(offset, offset);
    GPoint p3 = bottomRight + GPoint(offset, -offset);
    GPoint p4 = GPoint(topLeft.x, bottomRight.y )+ GPoint(-offset, -offset);

    GLine *res = new GLine(p1, p2, conturLinesWidth);
    res->next = new GLine(p2, p3, conturLinesWidth);
    res->next->next = new GLine(p3, p4, conturLinesWidth);
    res->next->next->next = new GLine(p4, p1, conturLinesWidth);
    return res;
}

void GRect::cut(const GShape &/*other*/)
{
    assert("can't cut rect");
}

bool GRect::contains(const GPoint &point) const
{
    return point.x>=topLeft.x && point.x<=bottomRight.x && point.y>=bottomRight.y && point.y<=topLeft.y;
}


<|start_filename|>src/libgeometry/gpoint.cpp<|end_filename|>
#include "gpoint.h"
#include <math.h>

GPoint::GPoint()
    :next(0)
{

}

GPoint::GPoint(double nx, double ny)
    :next(0)
{
    x = nx;
    y = ny;
}

GPoint & GPoint::operator=(const GPoint &other)
{
    if (this == &other) {
        return *this;
    }
    this->x = other.x;
    this->y = other.y;
    return *this;
}

bool GPoint::operator==(const GPoint &other)
{
    return x==other.x && y==other.y;
}

bool GPoint::operator!=(const GPoint &other)
{
    return x!=other.x || y!=other.y;
}

const GPoint GPoint::operator*(double a)
{
    const GPoint p(this->x*a, this->y*a);
    return p;
}

GPoint & GPoint::operator*=(double a)
{
    this->x *= a;
    this->y *= a;
    return *this;
}

GPoint & GPoint::operator/=(double a)
{
    this->x /= a;
    this->y /= a;
    return *this;
}

GPoint & GPoint::operator+=(const GPoint &other)
{
    this->x += other.x;
    this->y += other.y;
    return *this;
}

GPoint & GPoint::operator-=(const GPoint &other)
{
    this->x -= other.x;
    this->y -= other.y;
    return *this;
}

double GPoint::vectorLength() const
{
    return sqrt(x*x+y*y);
}

void  GPoint::vectorNormalize()
{
    double l = vectorLength();
    x /= l;
    y /= l;
}

double GPoint::vectorDot(const GPoint &other) const
{
    return x*other.x+y*other.y;
}

double GPoint::vectorAngle(const GPoint &otherVector) const
{
    double d1 = vectorDot(*this);
    double d2 = otherVector.vectorDot(otherVector);
    if(d1==0.0L || d2==0.0L || d1==-0.0L || d2==-0.0L )
        return nan("");
    return acos(vectorDot(otherVector)/sqrt(d1*d2));
}

const GPoint operator+(const GPoint &p1, const GPoint &p2)
{
    return GPoint(p1.x+p2.x, p1.y+p2.y);
}

const GPoint operator-(const GPoint &p1, const GPoint &p2)
{
    return GPoint(p1.x-p2.x, p1.y-p2.y);
}

double GPoint::distance(const GPoint &other) const
{
    return sqrt((x-other.x)*(x-other.x)+(y-other.y)*(y-other.y));
}


<|start_filename|>src/libgeometry/gline.cpp<|end_filename|>
#include "gcircle.h"
#include "gline.h"
#include "grect.h"
#include "gintersects.h"

GLine::GLine(const GPoint &start, const GPoint &end, double nwidth, bool nrounded)
{
    p1 = start;
    p2 = end;
    width = nwidth;
    rounded = nrounded;
    weak = false;
    wayContur = false;
}

GLine::GLine(const GPoint &start, const GPoint &end, double xwidth, double ywidth, bool nrounded)
{
    GPoint vector = start - end;
    vector.vectorNormalize();
    width = sqrt(xwidth*vector.x*xwidth*vector.x+ywidth*vector.y*ywidth*vector.y);
    p1.x = start.x;
    p2.x = end.x;
    p1.y = start.y;
    p2.y = end.y;
    rounded = nrounded;
    weak = false;
    wayContur = false;
}

GShapeType GLine::type() const
{
    return GSHAPE_LINE;
}

void GLine::move_relative(const GPoint &p)
{
    p1 = p1 - p;
    p2 = p2 - p;
}

void GLine::move_vmirror(double p )
{
    p1.y = p - p1.y;
    p2.y = p - p2.y;
}

void GLine::move_hmirror(double p )
{
    p1.x = p - p1.x;
    p2.x = p - p2.x;
}

bool GLine::intersects(const GShape &other)
{
    switch(other.type()) {
    case GSHAPE_LINE:
        return GIntersects::lineWithLine(*this, (const GLine &)other, 0);
    case GSHAPE_RECT:
        return GIntersects::lineWithRect(*this, (const GRect &)other);
    case GSHAPE_CIRCLE:
        return GIntersects::lineWithCircle(*this, (const GCircle &)other, 0, 0, true)!=0;
    }
    return false;
}

GShape *GLine::contur( double offset, double conturLinesWidth) const
{
    GPoint a = p2 - p1;
    a.vectorNormalize();
    a *= (offset+conturLinesWidth+width)/2.0L;
    GPoint o = GPoint(a.y, -a.x);

    GPoint point1 = p1 + o - a;
    GPoint point2 = p2 + o + a;
    GPoint point3 = p1 - o - a;
    GPoint point4 = p2 - o + a;

    GLine *res = new GLine(point1, point2, conturLinesWidth);
    res->next = new GLine(point3, point4, conturLinesWidth);
    res->next->next = new GLine(point1, point3, conturLinesWidth);
    ((GLine*)res->next->next)->weak = true;
    res->next->next->next = new GLine(point4, point2, conturLinesWidth);
    ((GLine*)res->next->next->next)->weak = true;
    res->wayContur = true;
    ((GLine*)res->next)->wayContur = true;
    ((GLine*)res->next->next)->wayContur = true;
    ((GLine*)res->next->next->next)->wayContur = true;
    return res;
}

double GLine::length() const
{
    return p1.distance(p2);
}

void GLine::cut(const GShape &other)
{
    GPoint direction = p2-p1;
    direction.vectorNormalize();
    GPoint widthVector(direction.y*0.99L*(width/2.0L), -direction.x*0.99L*width/2.0L); // 0.99 to avoid two same lines,  i.e. contur will be the same
    if(other.type()==GSHAPE_LINE)
        direction *= (width+((const GLine &)other).width)/2.0L;
    else
        direction *= width/2.0L;
    // two lines, one above, one below
    GLine testLine1(p1-direction+widthVector, p2+direction+widthVector);
    GLine testLine2(p1-direction-widthVector, p2+direction-widthVector);

    GPoint point1;
    GPoint point2;
    int intersectionCount = 0;
    switch(other.type()) {
    case GSHAPE_CIRCLE:
    {
        const GCircle & ocircle = (const GCircle &)other;
//        if(ocircle.center.distance(GPoint(38.61, 4.71))<0.2 && p1.distance(GPoint(37.64, 5.67))<0.2)
//            GPoint().vectorNormalize();
        GCircle circle(ocircle.center,ocircle.radius+ocircle.circleWidth/2.0L);
        if(testLine1.distanceToPoint(circle.center)<testLine2.distanceToPoint(circle.center)) {
            intersectionCount = GIntersects::lineWithCircle(testLine1,(const GCircle &)other,&point1, &point2, true);
            point1 -= widthVector;
            point2 -= widthVector;
        } else {
            intersectionCount = GIntersects::lineWithCircle(testLine2,(const GCircle &)other,&point1, &point2, true);
            point1 += widthVector;
            point2 += widthVector;
        }
        if(intersectionCount) {
            direction.vectorNormalize();
            direction *= width*0.65L;
        }
    }
        break;
    case GSHAPE_RECT:
    {
        const GRect &orect = (const GRect &)other;
        GPoint rcenter = orect.topLeft+orect.bottomRight;
        rcenter /=2.0L;
        GShape *rlines = other.contur(0.0L, 0.0L);
        GPoint *p = &point1;
        while(rlines) {
            if(rlines->type()==GSHAPE_LINE && intersectionCount<2) {
                if(testLine1.distanceToPoint(rcenter)<testLine2.distanceToPoint(rcenter)) {
                    if(GIntersects::lineWithLine(testLine1, *(GLine*)rlines, p)) {
                        *p -= widthVector;
                        p = &point2;
                        intersectionCount++;
                    }
                } else  {
                   if(GIntersects::lineWithLine(testLine2, *(GLine*)rlines, p)) {
                        *p += widthVector;
                        p = &point2;
                        intersectionCount++;
                   }
                }
            }
            GShape *tmp = rlines;
            rlines = rlines->next;
            delete tmp;
        }
        if(intersectionCount) {
            direction.vectorNormalize();
            direction *= width;//*0.75L;
        }
    }
        break;
    case GSHAPE_LINE:
    {
        const GLine &oline = (const GLine &)other;
        if(oline.contains(p1) && oline.contains(p2)) {
            p2 = p1;
            return;
        }
        GPoint dir = oline.p2-oline.p1;
        dir.vectorNormalize();
        dir *= oline.width/2.0L*0.99L;
        GLine line(oline.p1-dir, oline.p2+dir);
        GPoint *p = &point1;
        if(GIntersects::lineWithLine(testLine1, line, p)) {
            intersectionCount++;
            *p -= widthVector;
            p = &point2;
        }
        if(GIntersects::lineWithLine(testLine2, line, p)) {
            intersectionCount++;
            *p += widthVector;
        }
        if(intersectionCount && p1.distance(GPoint(58.25, 6.96))<0.2 && p2.distance(GPoint(59.63, 8.34))<0.2)
            GPoint().vectorNormalize();

        if(intersectionCount==0) {
            GPoint a(dir.y, -dir.x);
            GLine tline1(oline.p1-a, oline.p1+a);
            GLine tline2(oline.p2-a, oline.p2+a);
            if(GIntersects::lineWithLine(testLine1, tline1, p)) {
                intersectionCount++;
                *p -= widthVector;
                p = &point2;
            }
            if(GIntersects::lineWithLine(testLine1, tline2, p)) {
                intersectionCount++;
                *p -= widthVector;
            }
            if(intersectionCount==0) {
                if(GIntersects::lineWithLine(testLine2, tline1, p)) {
                    intersectionCount++;
                    *p += widthVector;
                    p = &point2;
                }
                if(GIntersects::lineWithLine(testLine2, tline2, p)) {
                    intersectionCount++;
                    *p += widthVector;
                }
            }
        }
        if(intersectionCount) {
            direction.vectorNormalize();
            direction *= (width+oline.width)/2.0L*1.1L;
        }

        // debug
//        if(intersectionCount && /*p1.distance(GPoint(6.41, 0.83))<0.1 &&*/ ((const GLine &)other).p1.distance(GPoint(11.28, 8.87))<0.1)
//             GPoint().vectorNormalize();
    }
        break;
    }

    // if one cover another
//    if(weak && intersectionCount) {
//        p2 = p1;
//        return;
//    }
    if(intersectionCount==2) {
        if(point1==point2) {
            intersectionCount = 1;
        } else if(length()<p1.distance(point2)) {
            if(length()>p1.distance(point2-direction))
                p2 = point2-direction;
            intersectionCount = 1;
        } else if(length()<p2.distance(point2)) {
            if(length()>p2.distance(point2+direction))
                p1 = point2+direction;
            intersectionCount = 1;
        }
    }
    if(intersectionCount>0) {
        if(length()<p1.distance(point1)) {
            if(length()>p1.distance(point1-direction))
                p2 = point1-direction;
            intersectionCount--;
            if(intersectionCount)
                point1 = point2;
        } else if(length()<p2.distance(point1)) {
            if(length()>p2.distance(point1+direction))
                p1 = point1+direction;
            intersectionCount--;
            if(intersectionCount)
                point1 = point2;
        }
    }

    GPoint np1;
    GPoint np2;
    if(intersectionCount==1) {
        np1 = point1-direction;
        np2 = point1+direction;
    } else if (intersectionCount==2) {
        if(p1.distance(point1)<p1.distance(point2)) {
            np1 = point1-direction;
            np2 = point2+direction;
        } else {
            np1 = point2-direction;
            np2 = point1+direction;
        }
    }
    if(intersectionCount>0) {
        bool cnp1 = contains(np1);
        bool cnp2 = contains(np2);
        GPoint oldp2 = p2;
        bool cn = true;
        if(cnp1 && p1.distance(np1)>width/2.0L)
            p2 = np1;
        else
            cn = false;
        if(cnp2 && oldp2.distance(np2)>width/2.0L) {
            if(cn) {
                if(!other.contains(np2) && !other.contains(oldp2)) {
                    GPoint tdir = oldp2-np2;
                    tdir.vectorNormalize();
                    tdir *= width/2.0L*0.9L;
                    double ny =  -tdir.x;
                    tdir.x = tdir.y;
                    tdir.y = ny;
                    if (!other.contains(np2-tdir) && !other.contains(oldp2-tdir) && !other.contains(np2+tdir) && !other.contains(oldp2+tdir) ) {
                        GLine *newl = new GLine(np2, oldp2, width);
                        newl->next = next;
                        next = newl;
                        newl->wayContur = wayContur;
                        newl->weak = weak;
                    }
                }
            } else {
                p1 = np2;
            }
        } else if (!cn)
            p2 = p1;
    }
}

double GLine::distanceToPoint(const GPoint &lp1, const GPoint &lp2, const GPoint point) const
{
    GPoint direction = lp2-lp1;
    const double l = lp1.distance(lp2);
    if(l==0.0L)
        return lp1.distance(point);
    // Consider the line extending the segment, parameterized as point1 + t (point2 - point1).
    // We find projection of point p onto the line.
    // It falls where t = [(point-point1) . (w-point1)] / |w-point1|^2
    const double t = GPoint(point-lp1).vectorDot(direction) / l / l;
    if (t < 0.0) return lp1.distance(point);       // Beyond the 'point1' end of the segment
    else if (t > 1.0) return lp2.distance(point);  // Beyond the 'point2' end of the segment
    direction *= t;
    GPoint projection = lp1 + direction;  // Projection falls on the segment
    return projection.distance(point);
}

double GLine::distanceToPoint(const GPoint &point) const
{
    GPoint dir = p2-p1;
    dir.vectorNormalize();
    dir*=width/2.0L;
    GPoint point1 = p1 - dir;
    GPoint point2 = p2 + dir;

   return distanceToPoint(point1, point2, point);
}

double GLine::distanceToPointWOEnds(const GPoint &point) const
{
    return distanceToPoint(p1, p2, point);
}

bool GLine::contains(const GPoint &point) const
{
    return (distanceToPoint(p1,p2,point)<=width/2.0L);
}


<|start_filename|>src/libgeometry/grect.h<|end_filename|>
#ifndef GRECT_H
#define GRECT_H
#include "gshape.h"
#include "gpoint.h"

class GRect : public GShape
{
public:
    GRect(const GPoint &ntopLeft, const GPoint &nbottomRight);
    GRect(const GPoint &center, double width, double height);
    GShapeType type() const;
    double width() const;
    double height() const;
    GPoint topLeft;
    GPoint bottomRight;
    void move_relative(const GPoint &p);
    void move_vmirror(double p );
    void move_hmirror(double p );
    bool intersects(const GShape &other);
    GShape *contur(double offset, double conturLinesWidth) const;
    void cut(const GShape &other);
    bool contains(const GPoint &point) const;
};

#endif // GRECT_H


<|start_filename|>src/drillreader.cpp<|end_filename|>
#include "drillreader.h"
#include <stdio.h>


DrillReader::DrillReader(char *filename, const GPoint &offset, const GPoint &max, Mirror mirror)
{
    mUnits = UNITS_INC;
    mCoord = COORD_ABSOLUTE;
    mXOffset = offset.x;
    mYOffset = offset.y;
    mReadX = 0.0f;
    mReadY = 0.0f;
    mXOffset92 = 0.0f;
    mYOffset92 = 0.0f;
    mX = 0.0f;
    mY = 0.0f;
    mFirstDrill = 0;
    mLastDrill = 0;

    for(int i=0; i<MAXIMUM_TOOL_COUNT; i++)
        mTools[i] = 0;

    FILE *fl = fopen(filename, "r");
    if(fl) {
        char lineBuffer[MAX_LINE_LENGTH];
        while(fgets(lineBuffer, sizeof lineBuffer, fl)) {
            parseLine(lineBuffer);
        }
        fclose(fl);
    }
    if(mirror!=M_NO) {
        Drill *nextDrill = mFirstDrill;
        while(nextDrill) {
            GPoint *next = nextDrill->firstPoint;
            while(next) {
                if(mirror==M_HORIZONTAL || mirror==M_BOTH)
                    next->x = max.x - next->x;
                if(mirror==M_VERTICAL || mirror==M_BOTH)
                    next->y = max.y - next->y;
                next = next->next;
            }
            nextDrill = nextDrill->next;
        }
    }
}

DrillReader::~DrillReader() {
    while(mFirstDrill) {
        while(mFirstDrill->firstPoint) {
            GPoint *next = mFirstDrill->firstPoint->next;
            delete mFirstDrill->firstPoint;
            mFirstDrill->firstPoint = next;
        }
        Drill *nextDrill = mFirstDrill->next;
        delete mFirstDrill;
        mFirstDrill = nextDrill;
    }
}

Drill *DrillReader::firstDrill() {
    return mFirstDrill;
}

char *DrillReader::readCoord(char *ptr, float base, float *result)
{
    float v = 0;
    char *res = Readers::readArgf(ptr, &v);
    if(mUnits==UNITS_INC)
        v *=25.4f;
    v-=base;
    *result = v;
    return res;
}

void DrillReader::parseLine(char *line) {
    if(mCoord == COORD_RELATIVE) {
        mReadX = 0.0f;
        mReadY = 0.0f;
    }
    int tmp;
    int readT = -1;
    float readC = 0.0f;
    bool needD = false;
    bool need92 = false;
    if(line[0]=='M' && line[1]=='E' && line[2]=='T' && line[3]=='R' && line[4]=='I' && line[5]=='C')
        mUnits = UNITS_MM;
    if(line[0]=='I' && line[1]=='N' && line[2]=='C' && line[3]=='H')
        mUnits = UNITS_INC;
    while (*line && *line!='*' && *line!='\r' && *line!='\n') {
        switch(*line) {
        case '(': // comment
            while(*line!=')') {
                line++;
                if(*line==0 || *line=='\r' || *line=='\n')
                    goto gofromwhile;
            }
            break;
        case ';':  // comment
            return;
        case 'M':
        case 'm':
            line = Readers::readArgi(line+1, &tmp)-1;
            switch(tmp) {
            // avoid drill patterm
            case 71: // mm
                mUnits = UNITS_MM;
                break;
            case 72: // inch
                mUnits = UNITS_INC;
                break;
            }
            break;
        case 'g':
        case 'G':
            line = Readers::readArgi(line+1, &tmp)-1;
            switch(tmp) {
            case 5:
            case 81: // drill mode, fuck them
                break;
            case 90: // absolute mode
                mCoord = COORD_ABSOLUTE;
                break;
            case 91: // relative mode
                mCoord = COORD_RELATIVE;
                break;
            case 92: // set zero
            case 93: // set zero
                need92 = true;
                break;
            }
            break;
        case '%':
            return;
        case 't':
        case 'T':
            line = Readers::readArgi(line+1, &readT)-1;
            break;
        case 'c':
        case 'C':
            line = Readers::readArgf(line+1, &readC)-1;
            break;
        case 'x':
        case 'X':
            line = readCoord(line+1, mXOffset, &mReadX)-1;
            needD = true;
            break;
        case 'y':
        case 'Y':
            line = readCoord(line+1, mYOffset, &mReadY)-1;
            needD = true;
            break;
        }

        line++;
    }
gofromwhile:
    if(readT!=-1) {
        if(readC!=0.0f) {
            Drill *newdrill = new Drill;
            newdrill->next = 0;
            newdrill->firstPoint = 0;
            newdrill->diameter = readC;
            if(mUnits == UNITS_INC)
                newdrill->diameter *= 25.4f;
            if(mFirstDrill==0)
                mFirstDrill = newdrill;
            else
                mLastDrill->next = newdrill;
            mLastDrill = newdrill;
            mTools[readT] = newdrill;
        }
        mCurrentDrill =  mTools[readT];
        if(mCurrentDrill) {
            mCurrentDrillLastPoint = mCurrentDrill->firstPoint;
            if(mCurrentDrillLastPoint) {
                while(mCurrentDrillLastPoint->next)
                    mCurrentDrillLastPoint = mCurrentDrillLastPoint->next;
            }
        } else {
            mCurrentDrillLastPoint = 0;
        }
    }
    if(need92) {
        mXOffset92 = mReadX;
        mXOffset92 = mReadY;
    }
    if(needD && mCurrentDrill) {
        GPoint *newpoint = new GPoint(mReadX,mReadY);
        if(mCoord == COORD_RELATIVE) {
            newpoint->x  += mX;
            newpoint->y  += mY;
        } else if(mCoord == COORD_ABSOLUTE) {
            newpoint->x  += mXOffset92;
            newpoint->y  += mXOffset92;
        }
        mX = newpoint->x;
        mY = newpoint->y;
        newpoint->next = 0;
        if(mCurrentDrillLastPoint)
            mCurrentDrillLastPoint->next = newpoint;

        else
            mCurrentDrill->firstPoint = newpoint;
        mCurrentDrillLastPoint = newpoint;
    }
}


<|start_filename|>src/pcbpathcontur.cpp<|end_filename|>
#include "pcbpathcontur.h"
#include "gerberreader.h"
#include "libgeometry/libgeometry.h"

PCBPathContur::PCBPathContur(Path *sourcePath, double boardXSize, double boardYSize, double millingDiameter, int times)
    :PathContur(boardXSize, boardYSize, millingDiameter)
{
    mWarnings = 0;
    if(sourcePath==0)
        return;

    GShape *last = 0;

    GLine contur1(GPoint(0.0L,0.0L), GPoint(boardXSize,0.0L));
    GLine contur2(GPoint(boardXSize,0.0L), GPoint(boardXSize,boardYSize));
    GLine contur3(GPoint(boardXSize,boardYSize), GPoint(0.0L,boardYSize));
    GLine contur4(GPoint(0.0L,boardYSize), GPoint(0.0L,0.0L));

    // get contur around everything
    for(int ntimes=0; ntimes<times; ntimes++) {
        GShape *checkFrom = 0;
        double offset = ntimes*millingDiameter;
        Path *nextPath = sourcePath;
        while(nextPath) {
            GShape *next = nextPath->firstElement;
            while(next) {
                if(ntimes==0 || next->type()!=GSHAPE_LINE) {
                    if(last)
                        last->next = next->contur(offset, millingDiameter);
                    else
                        mFirst = last = next->contur(offset, millingDiameter);
                    if(checkFrom==0)
                        checkFrom = last->next;
                    while(last->next) {
                        last = last->next;
                    }
                }
                next = next->next;
            }
            nextPath = nextPath->next;
        }
        // check if contur introspect tracks
        GShape *checkFrom2 = checkFrom;
        GShape *checkFrom1 = checkFrom;
        while(checkFrom1) {
            Path *checkNextPath = sourcePath;
            while(checkNextPath) {
                GShape *checkNext = checkNextPath->firstElement;
                while(checkNext) {
                    checkFrom1->cut(*checkNext);
                    checkNext = checkNext->next;
                }
                checkNextPath = checkNextPath->next;
            }
            // cut board contur
            checkFrom1->cut(contur1);
            checkFrom1->cut(contur2);
            checkFrom1->cut(contur3);
            checkFrom1->cut(contur4);
            checkFrom1 = checkFrom1->next;
        }

        // filters
        while(checkFrom2) {
            GShape *checkNext = mFirst;
            while(checkNext) {
                if(checkFrom2!=checkNext) {
                    if(checkNext!=checkFrom2 && checkNext->type()==GSHAPE_LINE && checkFrom2->type()==GSHAPE_LINE){
                        GLine *line1 = (GLine*)checkFrom2;
                        GLine *line2 = (GLine*)checkNext;
                        // if one line inside another, remove it
                        if(line1->contains(line2->p1) && line1->contains(line2->p2) && line1->distanceToPoint(line2->p2)<line1->width/4.0L)
                            line2->p2 = line2->p1;
                        if(line2->contains(line1->p1) && line2->contains(line1->p2) && line2->distanceToPoint(line1->p2)<line2->width/4.0L)
                            line1->p2 = line1->p1;

                        // two track conturs are introspected
//                        GPoint point;
//                        if(ntimes==0 && line1->wayContur && line2->wayContur && line1->p1!=line1->p2 && line2->p1!=line2->p2) {

//                            if (GIntersects::lineWithLine(*line1, *line2, &point)) {
//                                if(line1->p1.distance(point)<line1->p2.distance(point))
//                                    line1->p1 = point;
//                                else
//                                    line1->p2 = point;
//                                if(line2->p1.distance(point)<line2->p2.distance(point))
//                                    line2->p1 = point;
//                                else
//                                    line2->p2 = point;
//                            }
//                        }
                    }
                }
                checkNext = checkNext->next;
            }
            checkFrom2 = checkFrom2->next;
        }

        // move next if new elements appear
        while(last->next) {
            last = last->next;
        }
    }

    // remove garbage: check for extra introspection
    Path *checkNextPath = sourcePath;
    while(checkNextPath) {
        GShape *checkNext = checkNextPath->firstElement;
        while(checkNext) {
            GShape *checkFrom1 = mFirst;
            while(checkFrom1) {
                if(checkFrom1->type()==GSHAPE_CIRCLE) {
                    GCircle *circle = (GCircle*)checkFrom1;
                    if(checkNext->type()==GSHAPE_LINE) {
                        GLine *line = (GLine*)checkNext;
                        if( /*circle->spanAngle<M_PI && */ (line->distanceToPointWOEnds(circle->fromPoint())<line->width/2.0L+circle->circleWidth/2.0L || \
                                 line->distanceToPointWOEnds(circle->toPoint())<line->width/2.0L+circle->circleWidth/2.0L))
                            circle->radius = 0;
                    } else if(checkNext->type()==GSHAPE_CIRCLE) {
                        GCircle *testCircle = (GCircle*)checkNext;
                        if(circle->spanAngle<M_PI/2.0L && \
                                (testCircle->center.distance(circle->fromPoint())<circle->circleWidth/2.0L+testCircle->radius+testCircle->circleWidth/2.0L || \
                                 testCircle->center.distance(circle->toPoint())<circle->circleWidth/2.0L+testCircle->radius+testCircle->circleWidth/2.0L))
                            circle->radius = 0;
                    } else if(checkNext->type()==GSHAPE_RECT) {
                        GShape *rlines = checkNext->contur(0.0L, 0.0L);
                        while(rlines) {
                            if(circle->radius > 0 && rlines->type()==GSHAPE_LINE) {
                                GLine *line = (GLine*)rlines;
                                if(circle->spanAngle<M_PI/2.0L && \
                                        (line->distanceToPoint(circle->fromPoint())<circle->circleWidth/2.0L+line->width/2.0L || \
                                         line->distanceToPoint(circle->toPoint())<circle->circleWidth/2.0L+line->width/2.0L))
                                    circle->radius = 0;
                            }
                            GShape *tmp = rlines;
                            rlines = rlines->next;
                            delete tmp;
                        }
                    }
                } else if(checkFrom1->type()==GSHAPE_LINE) {
                    GLine *line = (GLine*)checkFrom1;
                    if(checkNext->type()==GSHAPE_CIRCLE) {\
                        GCircle *testCircle = (GCircle*)checkNext;
                        if(line->p1.distance(testCircle->center)<testCircle->radius+line->width/2.0L || \
                           line->p2.distance(testCircle->center)<testCircle->radius+line->width/2.0L ) {
                            line->p2 = line->p1;
                        }
                    } else if (checkNext->type()==GSHAPE_RECT ) {
                        GShape *rlines = checkNext->contur(0.0L, 0.0L);
                        while(rlines) {
                            if(rlines->type()==GSHAPE_LINE) {
                                GLine *testLine = (GLine*)rlines;
                                if(testLine->distanceToPointWOEnds(line->p1)<line->width*0.25L || testLine->distanceToPointWOEnds(line->p2)<line->width*0.25L ) {
                                    line->p2 = line->p1;
                                }
                            }
                            GShape *tmp = rlines;
                            rlines = rlines->next;
                            delete tmp;
                        }
                        if (checkNext->contains(line->p1) || checkNext->contains(line->p2)) {
                            line->p2 = line->p1;
                        }
                    } else if(checkNext->type()==GSHAPE_LINE) {
                        GLine *testLine = (GLine*)checkNext;
                        if(testLine->distanceToPointWOEnds(line->p1)<(testLine->width+line->width)*0.49L && testLine->distanceToPointWOEnds(line->p2)<(testLine->width+line->width)*0.49L ) {
                            line->p2 = line->p1;
                        }
                    }
                }
                checkFrom1 = checkFrom1->next;
            }
            checkNext = checkNext->next;
        }
        checkNextPath = checkNextPath->next;
    }


    // remove garbage: short lines - we can't mill them anyway.
    // remove garbage: everything outside the board
    double halfMD = mToolDiameter/2.0L;
    double maxX = boardXSize-halfMD;
    double maxY = boardYSize-halfMD;
    GShape *nextContur = mFirst;
    GShape *prevContur = 0;
    GShape *toDel = 0;
    while(nextContur) {
        toDel = 0;
        if(nextContur->type()==GSHAPE_LINE) {
            GLine *l = (GLine *)nextContur;
            if(l->length()<=0.0L ||l->p1.x<halfMD || l->p1.x>maxX || l->p1.y<halfMD || l->p1.y>maxY ||  l->p2.x<halfMD || l->p2.x>maxX || l->p2.y<halfMD || l->p2.y>maxY) {
                if(prevContur) {
                    prevContur->next = nextContur->next;
                } else {
                    mFirst = nextContur->next;
                }
                toDel = nextContur;
            }
        } else if(nextContur->type()==GSHAPE_CIRCLE) {
            GCircle *c = (GCircle *)nextContur;
            //double minAngle = c->chordAngle(millingDiameter*0.25L);
            GPoint fp = c->fromPoint();
            GPoint tp = c->toPoint();
            if( c->radius<=halfMD || fabs(c->spanAngle)<=0.0L || fp.x<halfMD || fp.x>maxX || fp.y<halfMD || fp.y>maxY ||  tp.x<halfMD || tp.x>maxX || tp.y<halfMD || tp.y>maxY) {
                if(prevContur) {
                    prevContur->next = nextContur->next;
                } else {
                    mFirst = nextContur->next;
                }
                toDel = nextContur;
            }
        } else if(nextContur->type()==GSHAPE_RECT) {
            GRect *r = (GRect *)nextContur;
            if(r->topLeft.x<halfMD || r->bottomRight.x>maxX || r->bottomRight.y<halfMD || r->topLeft.y>maxY) {
                if(prevContur) {
                    prevContur->next = nextContur->next;
                } else {
                    mFirst = nextContur->next;
                }
                toDel = nextContur;
            }
        }

        if(toDel==0)
            prevContur = nextContur;
        nextContur = nextContur->next;
        if(toDel)
            delete toDel;
    }
}


<|start_filename|>src/settings.cpp<|end_filename|>
#include "settings.h"
#include <QWidget>
#include <QSettings>
#include <QXmlStreamReader>
#include <QApplication>
#include <QLineEdit>
#include <QComboBox>
#include <QSpinBox>
#include <QCheckBox>

bool readXmlFile( QIODevice& device, QSettings::SettingsMap& map )
{
    QXmlStreamReader xmlReader( &device );

    QString currentElementName;
    while( !xmlReader.atEnd() )
    {
        xmlReader.readNext();
        while( xmlReader.isStartElement() )
        {
            if( xmlReader.name() == "SettingsMap" )
            {
                                xmlReader.readNext();
                continue;
            }

            if( !currentElementName.isEmpty() )
            {
                currentElementName += "/";
            }
            currentElementName += xmlReader.name().toString();
            xmlReader.readNext();
        }

        if( xmlReader.isEndElement() )
        {
            continue;
        }

        if( xmlReader.isCharacters() && !xmlReader.isWhitespace() )
        {
            QString key = currentElementName;
            QString value = xmlReader.text().toString();

            map[ key ] = value;

            currentElementName.clear();
        }
    }

     if( xmlReader.hasError() )
     {
        return false;
     }

    return true;
}

bool writeXmlFile( QIODevice& device, const QSettings::SettingsMap& map )
{
    QXmlStreamWriter xmlWriter( &device );
    xmlWriter.setAutoFormatting( true );

    xmlWriter.writeStartDocument();
        xmlWriter.writeStartElement( "SettingsMap" );

    QSettings::SettingsMap::const_iterator mi = map.begin();
    for( ; mi != map.end(); ++mi )
    {
        QStringList groups = mi.key().split("/");
        foreach( QString groupName, groups )
        {
            xmlWriter.writeStartElement( groupName );
        }

        xmlWriter.writeCharacters( mi.value().toString() );

        foreach( QString groupName, groups )
        {
            xmlWriter.writeEndElement();
        }
    }

        xmlWriter.writeEndElement();
    xmlWriter.writeEndDocument();

    return true;
}

QSettings *getSettings()
{
    const QSettings::Format xmlFormat = QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
    QString path = QApplication::applicationFilePath();
    if(path.right(4)==".exe")
        path = path.left(path.length()-4);
    path += ".xml";
    return new QSettings(path, xmlFormat);
}

Settings::Settings()
{

}

void saveWidget(QWidget *widget, QSettings *settings)
{
    QObjectList list = widget->children();
    foreach(QObject *obj, list) {
        QWidget *w = qobject_cast<QWidget *>(obj);
        if(w)
            saveWidget(w, settings);
    }


    QLineEdit *edit = qobject_cast<QLineEdit *>(widget);
    if(edit) {
        if(qobject_cast<QAbstractSpinBox*>(edit->parent()))
            return;
        if(!edit->text().isEmpty())
            settings->setValue(widget->objectName(), edit->text());
        else
            settings->remove(widget->objectName());
    } else {
        QComboBox *combo = qobject_cast<QComboBox *>(widget);
        if(combo) {
            settings->setValue(widget->objectName(), combo->currentIndex());
        } else {
            QSpinBox *spin = qobject_cast<QSpinBox *>(widget);
            if(spin) {
                settings->setValue(widget->objectName(), spin->value());
            } else {
                QDoubleSpinBox *dspin = qobject_cast<QDoubleSpinBox *>(widget);
                if(dspin) {
                    settings->setValue(widget->objectName(), dspin->value());
                } else {
                    QCheckBox *box = qobject_cast<QCheckBox*>(widget);
                    if(box) {
                        settings->setValue(widget->objectName(), box->checkState());
                    }
                }
            }
        }
    }

}

void Settings::save(QWidget *mainWidget)
{
    QSettings *settings = getSettings();

    saveWidget(mainWidget, settings);

    settings->deleteLater();
}

QWidget *findWidget(QWidget *wdg, const QString &name)
{
    if(wdg->objectName()==name)
        return wdg;
    QObjectList list = wdg->children();
    foreach(QObject *obj, list) {
        QWidget *w = qobject_cast<QWidget *>(obj);
        if(w) {
            w = findWidget(w, name);
            if(w)
                return w;
        }

    }
    return NULL;
}

void restoreValue(QWidget *widget, QVariant value)
{
    bool ok;
    qreal f = value.toReal(&ok);
    if(ok) {
        QComboBox *combo = qobject_cast<QComboBox *>(widget);
        if(combo) {
            combo->setCurrentIndex(qRound(f));
            return;
        } else {
            QSpinBox *spin = qobject_cast<QSpinBox *>(widget);
            if(spin) {
                spin->setValue(qRound(f));
                return;
            } else {
                QDoubleSpinBox *dspin = qobject_cast<QDoubleSpinBox *>(widget);
                if(dspin) {
                    dspin->setValue(f);
                    return;
                } else {
                    QCheckBox *box = qobject_cast<QCheckBox*>(widget);
                    if(box) {
                        box->setCheckState((Qt::CheckState)qRound(f));
                        return;
                    }
                }
            }
        }
    }
    QString str = value.toString();
    if(!str.isEmpty())
    {
        QLineEdit *edit = qobject_cast<QLineEdit *>(widget);
        if(edit) {
            edit->setText(str);
            return;
        }
    }
}


void Settings::load(QWidget *mainWidget)
{
    QSettings *settings = getSettings();
    QStringList list = settings->allKeys();
    foreach(QString key, list){
        QWidget *wdg = findWidget(mainWidget, key);
        if(wdg)
            restoreValue(wdg, settings->value(key,QString("")));
    }

    settings->deleteLater();
}



<|start_filename|>src/gerberreader.cpp<|end_filename|>
/*
 * Class for reading gerber files
 * automatically cut of everything around tracks, i.e. board always in zero coordinats and all x and y are possitive
 * automatically find and merge all vectors and tracks
 * all pins in linked lists
 * all child objects delete with this object
 */
#include "gerberreader.h"
#include <stdio.h>
#include <assert.h>
#include <cfloat>


GerberReader::GerberReader(char *firstLayer, char *secondLayer, double offset, Mirror firstLayerMirror, Mirror secondLayerMirror)
{
    mWarnings = 0;
    mFirstLayerPath = 0;
    mSecondLayerPath = 0;
    pMin.x = FLT_MAX;
    pMin.y = FLT_MAX;
    pMax.x = -FLT_MAX;
    pMax.y = -FLT_MAX;
    GShape *mFirstLayerShape = readFile(firstLayer);
    GShape *mSecondLayerShape = readFile(secondLayer);

    pMin.x -= offset;
    pMin.y -= offset;
    pMax = pMax - pMin;
    pMax.x += offset;
    pMax.y += offset;

    applyGeometry(mFirstLayerShape, firstLayerMirror);
    applyGeometry(mSecondLayerShape, secondLayerMirror);

    mFirstLayerPath = postHandleLayer(mFirstLayerShape);
    mSecondLayerPath = postHandleLayer(mSecondLayerShape);
}

GShape *GerberReader::readFile(char *filename)
{
    if(filename==0)
        return 0;
    if(filename[0]==0)
        return 0;
    mUnits = UNITS_INC;
    mCoord = COORD_ABSOLUTE;
    mInterpolation = INTERPOLATION_LINEAR;
    mZeroMode = ZM_DOTTED;
    mCurrentAperture = 0;
    mX = 0.0f;
    mY = 0.0f;
    mReadX = 0.0f;
    mReadY = 0.0f;
    mFirst = 0;
    mLast = 0;

    for(int i=0; i<MAXIMUM_APPERTURE_COUNT; i++)
        mAppertures[i] = 0;

    FILE *fl = fopen(filename, "r");
    if(fl) {
        char lineBuffer[MAX_LINE_LENGTH];
        while(fgets(lineBuffer, sizeof lineBuffer, fl)) {
            parseLine(lineBuffer);
        }
        fclose(fl);
    }
    for(int i=0; i<MAXIMUM_APPERTURE_COUNT; i++) {
        if(mAppertures[i]!=0) {
            delete mAppertures[i];
            mAppertures[i] = 0;
        }
    }
    return mFirst;
}

void GerberReader::applyGeometry(GShape *first, Mirror mirror)
{
        GShape *next = first;
        while(next) {
            next->move_relative(pMin);
            if(mirror==M_HORIZONTAL)
                next->move_hmirror(pMax.x);
            else if(mirror==M_VERTICAL)
                next->move_vmirror(pMax.y);
            else if(mirror==M_BOTH) {
                next->move_hmirror(pMax.x);
                next->move_vmirror(pMax.y);
            }

            next = next->next;
        }
}

Path *GerberReader::postHandleLayer(GShape *first)
{
    Path * result = 0;
    // collect interspection in tracks
    Path *prevPath = 0;
    while(first) {
        Path *nextPath = new Path;
        if(result==0)
            result = nextPath;
        if(prevPath)
            prevPath->next = nextPath;
        nextPath->next = 0;
        nextPath->firstElement = first;
        first = first->next;
        nextPath->firstElement->next = 0;
        prevPath = nextPath;

        GShape *groupElement = nextPath->firstElement;
        GShape *addGroupElement = groupElement;
        while(groupElement) {
            GShape *checkElement = first;
            GShape *prevCheckElement = 0;
            while(checkElement) {
                GShape *nextCheckElement = checkElement->next;
                if(groupElement->intersects(*checkElement)) {
                    addGroupElement->next = checkElement;
                    addGroupElement = addGroupElement->next;
                    if(prevCheckElement)
                        prevCheckElement->next = nextCheckElement;
                    else
                        first = nextCheckElement;
                    addGroupElement->next = 0;
                } else {
                    prevCheckElement = checkElement;
                }
                checkElement = nextCheckElement;
            }

            groupElement = groupElement->next;
        }
    }

    // remove all lines inside another lines, rounds and squares
    Path *nextPath = result;
    while(nextPath) {
            GShape *next = nextPath->firstElement;
            GShape *prev = 0;
            afterdel:
            while(next) {
                if(next->type()==GSHAPE_LINE) {
                    GLine *line = (GLine*)next;
                    Path *nextPath2 = result;
                    while(nextPath2) {
                        GShape *next2 = nextPath2->firstElement;
                        while(next2) {
                            if(next!=next2 && next2->contains(line->p1) && next2->contains(line->p2)) {
                                GShape *tmp = next->next;
                                if(prev==0)
                                    nextPath->firstElement = next->next;
                                else
                                    prev->next = next->next;
                                delete next;
                                next = tmp;
                                goto afterdel;
                            }
                            next2 = next2->next;
                        }
                        nextPath2 = nextPath2->next;
                    }
                }
                prev = next;
                next = next->next;
            }
        nextPath = nextPath->next;
    }
    return result;
}

void deletePath(Path *mFirstPath)
{
    while(mFirstPath) {
        while(mFirstPath->firstElement) {
            GShape *next = mFirstPath->firstElement->next;
            delete mFirstPath->firstElement;
            mFirstPath->firstElement = next;
        }
        Path *nextPath = mFirstPath->next;
        delete mFirstPath;
        mFirstPath = nextPath;
    }
}

GerberReader::~GerberReader()
{
   deletePath(mFirstLayerPath);
   deletePath(mSecondLayerPath);
}

const GPoint & GerberReader::getMax()
{
    return pMax;
}

const GPoint & GerberReader::getMin()
{
    return pMin;
}


Path *GerberReader::firstLayerPath()
{
    return mFirstLayerPath;
}

Path *GerberReader::secondLayerPath()
{
    return mSecondLayerPath;
}

char *GerberReader::readCoord(char *ptr, int format, float base, float *result)
{
    int v = 0;
    double val;
    char *res = Readers::readArgi(ptr, &v);
    val = v;
    if(mZeroMode==ZM_POSTZERO) {
        for(int i=res-ptr; i<format/10+format%10; i++)
            val *=10;
    }
    for(int i=0; i<format%10; i++)
        val /=10;
    if(mUnits==UNITS_INC)
        val *=25.4L;
    if(mCoord==COORD_RELATIVE)
        val+=base;
    *result = val;
    return res;
}

void GerberReader::parseLine(char *line)
{
    if(mCoord == COORD_RELATIVE) {
        mReadX = 0.0f;
        mReadY = 0.0f;
    }
    int tmp;
    bool needD = false;
    while (*line && *line!='*' && *line!='\r' && *line!='\n') {
        switch(*line) {
        case '(': // comment
            while(*line!=')') {
                line++;
                if(*line==0 || *line=='\r' || *line=='\n')
                    goto gofromwhile;
            }
            break;
        case ';':  // comment
            goto gofromwhile;
        case 'M':
        case 'm': // ignore
            line = Readers::readArgi(line+1, &tmp)-1;
            break;
        case 'g':
        case 'G':
            line = Readers::readArgi(line+1, &tmp)-1;
            switch(tmp) {
            case 1: // linear inrterpolation
                mInterpolation = INTERPOLATION_LINEAR;
                break;
            case 2: // round clock wise interpolastion
                mInterpolation = INTERPOLATION_ROUND_CW;
                break;
            case 3: // round couter clock wise interpolation
                mInterpolation = INTERPOLATION_ROUND_CCW;
                break;
            case 4: // comment
                goto gofromwhile;
            case 36:
            case 37: // polygons
                mWarnings = "Unimplemented commands G36/G37(Polygons).";
                break;
            case 54: // choose aperture Dxx
                break;
            case 70: // inch
                mUnits = UNITS_INC;
                break;
            case 71: // mm
                mUnits = UNITS_MM;
                break;
            case 75: // full round
                mInterpolation = INTERPOLATION_FULLROUND;
                break;
            case 90: // absolute coordinats
                mCoord = COORD_ABSOLUTE;
                break;
            case 91: // relative coordinats
                mCoord = COORD_RELATIVE;
                break;

            }
            break;
        case '%':
            if(line[1]=='M' && line[2]=='O' && line[3]=='I' && line[4]=='N') {// units
                mUnits = UNITS_INC;
                line++;
            } else if(line[1]=='M' && line[2]=='O' && line[3]=='M' && line[4]=='M') {// unts
                mUnits = UNITS_MM;
                line++;
            } else if(line[1]=='F' && line[2]=='S') { // gerber's properties
                line+=2;
                while(*line!='*') {
                    line++;
                    if(*line==0 || *line=='\r' || *line=='\n')
                        goto gofromwhile;
                    if(*line == 'L')
                        mZeroMode = ZM_LEADZERO;
                    else if (*line == 'T')
                        mZeroMode = ZM_POSTZERO;
                    else if (*line == 'D')
                        mZeroMode = ZM_DOTTED;
                    else if (*line == 'A')
                        mCoord = COORD_ABSOLUTE;
                    else if (*line == 'I')
                        mCoord = COORD_RELATIVE;
                    else if (*line == 'X')
                        line = Readers::readArgi(line+1, &mXFormat)-1;
                    else if (*line == 'Y')
                        line = Readers::readArgi(line+1, &mYFormat)-1;
                }
            } else if(line[1]=='A' && line[2]=='D' && line[3]=='D') { // appertures
                line+=3;
                int dnum=-1;
                line = Readers::readArgi(line+1, &dnum);
                if(dnum!=-1) {
                    Apperture *a = new Apperture;
                    a->sh = SHAPE_RECT; // always make square, even if something tricky inside
                    if(mAppertures[dnum]!=0)
                        delete mAppertures[dnum];
                    mAppertures[dnum] = a;
                    if(*line++=='C')
                        a->sh = SHAPE_ROUND;
                    line++; // ignore ';'
                    float f = -1.0f;
                    line = Readers::readArgf(line, &f);
                    if(mUnits == UNITS_INC)
                        f *= 25.4f;
                    a->x = f;
                    if(*line++=='X') {
                        line = Readers::readArgf(line, &f);
                        if(mUnits == UNITS_INC)
                            f *= 25.4f;
                        a->y = f;
                    } else {
                        a->y = a->x;
                    }
                }
            }

            while(*line!='%') {
                line++;
                if(*line==0 || *line=='\r' || *line=='\n')
                    goto gofromwhile;
            }
            break;
        case 'd':
        case 'D':
            line = Readers::readArgi(line+1, &lastD)-1;
            doD();
            needD = false;
            break;
        case 'x':
        case 'X':
            line = readCoord(line+1, mXFormat, mX, &mReadX)-1;
            needD = true;
            break;
        case 'y':
        case 'Y':
            line = readCoord(line+1, mYFormat, mY, &mReadY)-1;
            needD = true;
            break;
        }

        line++;
    }
    if(needD)
        doD();
gofromwhile:
    return;
}

void GerberReader::doD() {
    if(lastD==1) {// move and burn
       createNext(true);
    } else if(lastD==2) {// move with closed light
        mX = mReadX;
        mY = mReadY;
    } else if(lastD==3) { // burn at point
        mX = mReadX;
        mY = mReadY;
        createNext(false);
    } else if(lastD>3) { // choose apperture
        mCurrentAperture = mAppertures[lastD];
    }
}

void GerberReader::createNext(bool inMove)
{
    if(mCurrentAperture) {
        GShape *shape;
        if(inMove) {
            shape = new GLine(GPoint(mX,mY),GPoint(mReadX, mReadY), mCurrentAperture->x, (mCurrentAperture->sh==SHAPE_ROUND)?mCurrentAperture->x:mCurrentAperture->y, mCurrentAperture->sh==SHAPE_ROUND);
            checkMinMax(mX, mY, mCurrentAperture->x, mCurrentAperture->y);
            checkMinMax(mReadX, mReadY, mCurrentAperture->x, mCurrentAperture->y);
        } else{
            if(mCurrentAperture->sh==SHAPE_ROUND)
                shape = new GCircle(GPoint(mX,mY),mCurrentAperture->x/2.0L);
            else
                shape = new GRect(GPoint(mX,mY), mCurrentAperture->x, mCurrentAperture->y);
             checkMinMax(mX, mY, mCurrentAperture->x, mCurrentAperture->y);
        }
        shape->next = 0;
         if(mFirst==0) {
             mFirst = shape;
             mLast = mFirst;
         } else {
             mLast->next = shape;
             mLast = shape;
         }
    }
    if(inMove) {
        mX = mReadX;
        mY = mReadY;
    }
}

void GerberReader::checkMinMax(float x, float y, float dx, float dy)
{
    if(x+dx>pMax.x)
        pMax.x = x+dx;
    if(x-dx>pMax.x)
        pMax.x = x-dx;
    if(x+dx<pMin.x)
        pMin.x = x+dx;
    if(x-dx<pMin.x)
        pMin.x = x-dx;

    if(y+dy>pMax.y)
        pMax.y = y+dy;
    if(y-dy>pMax.y)
        pMax.y = y-dy;
    if(y+dy<pMin.y)
        pMin.y = y+dy;
    if(y-dy<pMin.y)
        pMin.y = y-dy;
}

const char * GerberReader::getWarnings()
{
    return mWarnings;
}


<|start_filename|>src/gerberview.cpp<|end_filename|>
#include "gerberview.h"
#include "gerberreader.h"
#include "pathcontur.h"
#include "drillreader.h"
#include <math.h>
#include <QtGui>
#include <assert.h>
#include "libgeometry/libgeometry.h"

GerberView::GerberView(QWidget *parent)
    : QWidget(parent)
{
   mReader = 0;
   mContur = 0;
   mDrillReader = 0;
   mSelectedDrill = 0;
   mSelectedShape = 0;
   mDrill2Mirror = M_NO;
   isResultVisible = true;
   mEdgesVisible = true;
   mLabel = new QLabel(parent==0?this:parent);
   mLabel->move(0,0);
   setMouseTracking(true);
   setWindowTitle("Contur preview");
}

GerberView::~GerberView()
{

}

void GerberView::set(GerberReader *reader, PathContur *contur1, PathContur *contur2, DrillReader *drillReader, Mirror drill2mirror)
{
    mReader = reader;
    mContur1 = contur1;
    mContur2 = contur2;
    mDrillReader = drillReader;
    mDrill2Mirror = drill2mirror;
    if(mReader==0) {
        mPath = 0;
        mContur = 0;
    } else {
        if(mReader->firstLayerPath()) {
            mPath = mReader->firstLayerPath();
            mContur = mContur1;
        } else if(mReader->secondLayerPath()) {
            mPath = mReader->secondLayerPath();
            mContur = mContur2;
        } else {
            mPath = 0;
            mContur = 0;
        }
    }
    update();
}

void GerberView::clear()
{
    set(0,0,0,0, M_NO);
    mSelectedShape = 0;
    printInfo(0);
}

void GerberView::setEdgesVisible(bool edgesVisible)
{
    mEdgesVisible = edgesVisible;
    update();
}

double GerberView::getYoff(double scale)
{
    if(mReader==0)
        return 0.0L;
    double res =  height()/scale-mReader->getMax().y;
    if(res<0.0L)
            res = 0.0L;
    return res;
}

double GerberView::getScale()
{
    if(mReader==0)
        return 1.0L;
    double xs = width()/mReader->getMax().x;
    double ys = height()/mReader->getMax().y;
    if(ys<xs)
        return ys;
    else
        return xs;
}

void GerberView::printInfo(QMouseEvent *event)
{
    if(mReader==0 || mContur==0 || event==0) {
        mLabel->setText("");
        return;
    }
    double scale = getScale();
    double x = event->x()/scale;
    double y = (height() - event->y())/scale - getYoff(scale);
    QString text;
    text.sprintf("x=%f y=%f",x , y);
    if(mSelectedShape) {
        if(mSelectedShape->type()==GSHAPE_CIRCLE) {
            text+= ", angle=" + QString::number( ((GCircle*)mSelectedShape)->anglePoint(GPoint(x,y)) );
        }
    }
    if(mSelectedDrill) {
        text += QString().sprintf("\nDrill: x = %f, y = %f, diameter = %f", mSelectedDrill->x, mSelectedDrill->y, mSelectedDrillDiamter);
    } else if(mSelectedShape) {
        switch(mSelectedShape->type()){
        case GSHAPE_CIRCLE:
        {
            GCircle *circle = (GCircle*)mSelectedShape;
            text += QString().sprintf("\nCircle: x = %f, y = %f, radius = %f; angle = %f span = %f", circle->center.x, circle->center.y, circle->radius,circle->startAngle,  circle->spanAngle);
        }
            break;
        case GSHAPE_LINE:
        {
            GLine *line = (GLine*)mSelectedShape;
            text += QString().sprintf("\nLine: x1 = %f, y1 = %f, x2 = %f, y2 = %f, width = %f", line->p1.x,line->p1.y,line->p2.x,line->p2.y,line->width);
        }
            break;
        case GSHAPE_RECT:
        {
            GRect *rect = (GRect*)mSelectedShape;
            text += QString().sprintf("\nRect: left = %f, top = %f, right = %f, bottom = %f", rect->topLeft.x, rect->topLeft.y, rect->bottomRight.x, rect->bottomRight.y);
        }
            break;
        }
    }
    mLabel->setText(text);
    mLabel->resize(mLabel->sizeHint());
}

void GerberView::mouseMoveEvent ( QMouseEvent * event )
{
    QWidget::mouseMoveEvent(event);
    printInfo(event);
}

void GerberView::mousePressEvent ( QMouseEvent * event )
{
    QWidget::mousePressEvent(event);
    if(mReader==0 || mContur==0) {
        mSelectedShape = 0;
        return;
    }
    if(event->button()==Qt::MiddleButton && event->type()==QEvent::MouseButtonPress) {
        isResultVisible = !isResultVisible;
        update();
        return;
    }
    if(event->button()==Qt::RightButton && event->type()==QEvent::MouseButtonPress) {
        if(mPath==mReader->firstLayerPath() && mReader->secondLayerPath()) {
            mPath = mReader->secondLayerPath();
            mContur = mContur2;
            mSelectedShape = 0;
        } else if(mReader->firstLayerPath()){
            mPath = mReader->firstLayerPath();
            mContur = mContur1;
            mSelectedShape = 0;
        }
        update();
        return;
    }
    double scale = getScale();
    GPoint point(event->x()/scale, (height() - event->y())/scale  - getYoff(scale) );

    if(mDrillReader) {
        Drill *nextDrill = mDrillReader->firstDrill();
        while(nextDrill) {
            GPoint *next = nextDrill->firstPoint;
            while(next) {
                if(next->distance(point)<=nextDrill->diameter/2.0L) {
                    mSelectedShape = 0;
                    mSelectedDrillDiamter = nextDrill->diameter;
                    mSelectedDrill = next;
                    printInfo(event);
                    update();
                    return;
                }
                next = next->next;
            }
            nextDrill = nextDrill->next;
        }
    }
    mSelectedDrill = 0;

    bool found = mSelectedShape == 0;
    GShape *firstFound = 0;
    Path *nextPath = mPath;
    while(nextPath) {
        GShape *next = nextPath->firstElement;
        while(next) {
            if(next->contains(point)) {
                if(mSelectedShape != next) {
                    if(found) {
                        mSelectedShape = next;
                        printInfo(event);
                        update();
                        return;
                    }
                } else
                    found = true;
                if(firstFound==0)
                    firstFound = next;
            }
            next = next->next;
        }
        nextPath = nextPath->next;
    }
    if(isResultVisible)
    {
        GShape *nextConturShape = mContur->first();
        while(nextConturShape) {
            if(nextConturShape->contains(point)) {
                if(mSelectedShape != nextConturShape) {
                    if(found) {
                        mSelectedShape = nextConturShape;
                        printInfo(event);
                        update();
                        return;
                    }
                } else
                    found = true;
                if(firstFound==0)
                    firstFound = nextConturShape;
            }
            nextConturShape = nextConturShape->next;
        }
    }
    if(firstFound && mSelectedShape != firstFound)
        mSelectedShape = firstFound;
    else
        mSelectedShape = 0;
    printInfo(event);
    update();
}

QSize GerberView::sizeHint () const
{
    if(mReader) {
        if(mReader->firstLayerPath()) {
            return QSize(mReader->getMax().x*20.0f, mReader->getMax().y*20.0f);
        }
    }
    return QWidget::sizeHint();
}

QSize GerberView::bestSize()
{
    return sizeHint();
}

void GerberView::paintEvent (QPaintEvent * event)
{
    QWidget::paintEvent(event);
    if(mReader==0 || mContur==0)
            return;
    double scale = getScale();
    int maxY = mReader->getMax().y*scale;
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.fillRect(0,0,mReader->getMax().x*scale,maxY, Qt::white);
    painter.setPen(Qt::red);
    painter.drawRect(0,0,mReader->getMax().x*scale,maxY);

    Path *nextPath = mPath;
    if(nextPath) {

        QColor colors[] = {Qt::darkRed, Qt::darkBlue, Qt::darkYellow, Qt::darkCyan, Qt::darkMagenta, Qt::darkGray};
        unsigned int colorNum = 0;

        while(nextPath) {
            GShape *next = nextPath->firstElement;
            if(next) {
                QColor color;
                if(next->next==0)
                    color = Qt::black;
                else {
                    color = colors[colorNum];
                    colorNum++;
                    if( colorNum >= (sizeof colors)/(sizeof (QColor)) )
                        colorNum = 0;
                }
                while(next) {
                    QColor dcolor = (next==mSelectedShape)?Qt::red:color;
                    switch(next->type()) {
                    case GSHAPE_LINE:
                    {
                        GLine *line = (GLine*)next;
                        if(line->rounded)
                            painter.setPen(QPen(dcolor,scale*line->width, Qt::SolidLine,Qt::RoundCap));
                        else
                            painter.setPen(QPen(dcolor,scale*line->width));
                        painter.setBrush(QBrush(Qt::NoBrush));
                        painter.drawLine(line->p1.x*scale, maxY-line->p1.y*scale, line->p2.x*scale, maxY-line->p2.y*scale);
                    }
                    break;
                    case GSHAPE_RECT:
                    {
                        GRect *rect = (GRect*)next;
                        painter.fillRect(rect->topLeft.x*scale, maxY-(rect->topLeft.y*scale) , rect->width()*scale, rect->height()*scale, dcolor);
                    }
                    break;
                    case GSHAPE_CIRCLE:
                    {
                        GCircle *circle = (GCircle*)next;
                        painter.setBrush(QBrush(dcolor));
                        painter.setPen( Qt::NoPen );
                        painter.drawEllipse(QPoint(circle->center.x*scale, maxY-circle->center.y*scale), (int)round(circle->radius*scale), (int)round(circle->radius*scale));
                    }
                    break;
                    }
                    next = next->next;
                }
            }
            nextPath = nextPath->next;
        }
    }

    if(isResultVisible)
    {

        bool contur = mEdgesVisible;
        GShape *nextConturShape = contur?(mContur->edges()):(mContur->first());
        int penWidth = round(scale*mContur->toolDiameter());
        painter.setBrush(QBrush(Qt::NoBrush));
        while(nextConturShape) {
            if(nextConturShape==mSelectedShape) {
                painter.setPen(QPen(Qt::red,penWidth, Qt::SolidLine,Qt::RoundCap));
            } else {
                if(contur) {
                    painter.setPen(QPen(Qt::darkGreen,penWidth, Qt::SolidLine,Qt::RoundCap));
                } else {
                    painter.setPen(QPen(Qt::green,penWidth, Qt::SolidLine,Qt::RoundCap));
                }
            }
            switch(nextConturShape->type()) {
                case GSHAPE_LINE:
                {
                    GLine *line = (GLine*)nextConturShape;
                    painter.drawLine(line->p1.x*scale, maxY-line->p1.y*scale, line->p2.x*scale, maxY-line->p2.y*scale);
                }
                break;
                case GSHAPE_RECT:
                {
                    assert("Rect in contur while painting!");
                    // rects shouldn't be here, because it's contur. But just in case... for future.
                    //GRect *rect = (GRect*)nextConturShape;
                    //painter.drawRect(rect->topLeft.x*scale+penWidth, maxY-(rect->topLeft.y*scale+penWidth) , rect->width()*scale-2*penWidth, rect->height()*scale-2*penWidth);
                }
                break;
                case GSHAPE_CIRCLE:
                {
                    GCircle *circle = (GCircle*)nextConturShape;
                    painter.drawArc((circle->center.x-circle->radius)*scale, maxY-(circle->center.y+circle->radius)*scale, \
                                    circle->radius*scale*2.0L, circle->radius*scale*2.0L, \
                                    circle->startAngle*180.0L/M_PI*16.0L, circle->spanAngle*180.0L/M_PI*16.0L);
                    //painter.drawEllipse(QPoint(circle->center.x*scale, maxY-circle->center.y*scale), (int)round(circle->radius*scale), (int)round(circle->radius*scale));
                }
                break;
            }

            nextConturShape = nextConturShape->next;
            if(contur && nextConturShape==0) {
                contur = false;
                nextConturShape = mContur->first();
            }
        }
    }

    if(mDrillReader) {
        Drill *nextDrill = mDrillReader->firstDrill();
        while(nextDrill) {
            GPoint *next = nextDrill->firstPoint;
            int radius = (int)round(nextDrill->diameter/2.0f*scale);
            if(radius==0)
                radius = 1;
            while(next) {
                if(next==mSelectedDrill) {
                    painter.setBrush(QBrush(Qt::red));
                    painter.setPen( Qt::red );
                } else {
                    painter.setBrush(QBrush(Qt::white));
                    painter.setPen( Qt::white );
                }
                double x = next->x, y = next->y;
                if(mPath==mReader->secondLayerPath()) {
                    if(mDrill2Mirror == M_HORIZONTAL || mDrill2Mirror == M_BOTH)
                        x = mReader->getMax().x-x;
                    if(mDrill2Mirror & M_VERTICAL || mDrill2Mirror == M_BOTH)
                        y = mReader->getMax().y-y;
                }
                painter.drawEllipse(QPoint(x*scale, maxY-y*scale), radius, radius);
                painter.setPen( Qt::black );
                painter.drawLine(x*scale-radius,maxY-y*scale, x*scale+radius,maxY-y*scale);
                painter.drawLine(x*scale,maxY-y*scale+radius, x*scale, maxY-y*scale-radius);

                next = next->next;
            }
            nextDrill = nextDrill->next;
        }
    }

}


<|start_filename|>src/pcbpathcontur.h<|end_filename|>
#ifndef PCBPATHCONTUR_H
#define PCBPATHCONTUR_H
#include "pathcontur.h"

class PCBPathContur : public PathContur
{
public:
    PCBPathContur(Path *sourcePath, double boardXSize, double boardYSize, double millingDiameter, int times);

};

#endif // PCBPATHCONTUR_H


<|start_filename|>src/printingpathcontur.h<|end_filename|>
#ifndef PRINTINGPATHCONTUR_H
#define PRINTINGPATHCONTUR_H
#include "pathcontur.h"

class PrintingPathContur : public PathContur
{
public:
    PrintingPathContur(Path *sourcePath, double boardXSize, double boardYSize, double nozzleDiameter);
private:
    void addShape(GShape *newShape);
    GShape *mLast;
};

#endif // PRINTINGPATHCONTUR_H


<|start_filename|>src/readersstruct.cpp<|end_filename|>
#include "readersstruct.h"

char *Readers::readArgi(char *ptr, int * result){
    int res = 0;
    int sign = 1;
    char found = 0;
    while(1) {
        if(*ptr == '+'){
            sign = 1;
        } else if (*ptr == '-') {
            sign = -1;
        } else if(*ptr>=0x30 && *ptr<=0x39) {
            unsigned char v = *ptr-0x30;
            res*=10;
            res+=v;
            found = 1;
        } else {
            if(found)
                *result = res*sign;
            return ptr;
        }
        ptr++;
    }
}

char *Readers::readArgf(char *ptr, float * result){
    double res = 0.0L;
    double sign = 1.0L;
    double fract = 1.0L;
    char found = 0;
    while(1) {
        if(*ptr == '+'){
            sign = 1.0L;
        } else if (*ptr == '-') {
            sign = -1.0L;
        } else if(*ptr == '.' || *ptr ==',') {
            fract = 0.1L;
        } else if(*ptr>=0x30 && *ptr<=0x39) {
            unsigned char v = *ptr-0x30;
            if(fract==1.0L) {
                res*=10.0L;
                res+=v;
            } else {
                res+=(double)v*fract;
                fract = fract/10.0L;
            }
            found = 1;
        } else {
            if(found)
                *result = (double)res*sign;
            return ptr;
        }
        ptr++;
    }
}


<|start_filename|>src/printingpathcontur.cpp<|end_filename|>
#include "printingpathcontur.h"
#include "gerberreader.h"
#include "math.h"
#include "libgeometry/libgeometry.h"

#define ERROR_DIAMETER "Diameter too small."

double edgeDelta(double nozzleDiameter, double width, const GPoint &v)
{
    return width/2.0L*cos(asin(v.vectorLength()/width*2.0L))-nozzleDiameter/2.0L;
}

void PrintingPathContur::addShape(GShape *newShape)
{
    if(mLast)
        mLast->next = newShape;
    else
        mFirst = newShape;
    mLast = newShape;
}

PrintingPathContur::PrintingPathContur(Path *sourcePath, double boardXSize, double boardYSize, double nozzleDiameter)
    :PathContur(boardXSize, boardYSize, nozzleDiameter), mLast(0)
{
    mWarnings = 0;
    if(sourcePath==0)
        return;

    Path *nextPath = sourcePath;
    while(nextPath) {
        GShape *next = nextPath->firstElement;
        while(next) {
            switch(next->type()) {
                case GSHAPE_LINE:
                {
                    GLine *line = (GLine*)next;
                    if(fabs(line->width-nozzleDiameter)<0.0001L) {
                        addShape(new GLine(line->p1, line->p2, nozzleDiameter, true));
                    } else if(line->width>nozzleDiameter) {
                        GPoint a = line->p2 - line->p1;
                        a.vectorNormalize();
                        GPoint o = GPoint(a.y, -a.x);
                        o *= nozzleDiameter;

                        int c = line->width/2.0L/nozzleDiameter+1.0L;
                        const double edge = (line->width-nozzleDiameter)/2.0L;
                        for(int i=0; i<=c; i++) {
                            GPoint s = a*edgeDelta(nozzleDiameter, line->width, o*i);
                            if((o*i).vectorLength()<=edge) {
                                addShape(new GLine(line->p1+o*i-s, line->p2+o*i+s, nozzleDiameter, true));
                                if(i!=0)
                                    addShape(new GLine(line->p1-o*i-s, line->p2-o*i+s, nozzleDiameter, true));
                            } else {
                                o.vectorNormalize();
                                GPoint s = a*edgeDelta(nozzleDiameter, line->width, o*edge);
                                addShape(new GLine(line->p1+o*edge-s, line->p2+o*edge+s, nozzleDiameter, true));
                                addShape(new GLine(line->p1-o*edge-s, line->p2-o*edge+s, nozzleDiameter, true));
                                break;
                            }
                        }
                    } else {
                        mWarnings = ERROR_DIAMETER;
                    }
                    break;
                }
                case GSHAPE_CIRCLE:
                {
                    GCircle *circle = (GCircle*)next;
                    if(circle->radius >= nozzleDiameter) {
                        int c = circle->radius/nozzleDiameter;
                        for(int i=0; i<=c; i++) {
                            double r = circle->radius - nozzleDiameter/2.0L - i*nozzleDiameter;
                            if(r >= nozzleDiameter/2.0L) {
                                GCircle *result = new GCircle(circle->center, r, nozzleDiameter);
                                result->outer = i==0;
                                addShape(result);
                            } else {
                                GCircle *result = new GCircle(circle->center, nozzleDiameter/2.0L, nozzleDiameter);
                                result->outer = i==0;
                                addShape(result);
                                break;
                            }
                        }
                    } else {
                        mWarnings = ERROR_DIAMETER;
                    }
                    break;
                }
                case GSHAPE_RECT:
                {
                    GRect *rect = (GRect*)next;
                    if(rect->width()>=nozzleDiameter && rect->height()>=nozzleDiameter) {
                        int c = rect->height()/nozzleDiameter;
                        GPoint pl(rect->topLeft.x+nozzleDiameter/2.0L, rect->bottomRight.y);
                        GPoint pr(rect->bottomRight.x-nozzleDiameter/2.0L,rect->bottomRight.y);
                        double y =pl.y;
                        for(int i=0; i<=c; i++) {
                           pr.y = pl.y = y + nozzleDiameter*i+nozzleDiameter/2.0L;
                           if(pr.y<rect->topLeft.y-nozzleDiameter/2.0L) {
                                addShape(new GLine(pl, pr, nozzleDiameter, true));
                           } else {
                               pr.y = pl.y = rect->topLeft.y-nozzleDiameter/2.0L;
                               addShape(new GLine(pl, pr, nozzleDiameter, true));
                               break;
                           }
                        }
                        addShape(new GLine(GPoint(rect->topLeft.x+nozzleDiameter/2.0L, rect->topLeft.y-nozzleDiameter/2.0L), \
                                            GPoint(rect->topLeft.x+nozzleDiameter/2.0L, rect->bottomRight.y+nozzleDiameter/2.0L), nozzleDiameter, true));
                        addShape(new GLine(GPoint(rect->bottomRight.x-nozzleDiameter/2.0L, rect->topLeft.y-nozzleDiameter/2.0L), \
                                            GPoint(rect->bottomRight.x-nozzleDiameter/2.0L, rect->bottomRight.y+nozzleDiameter/2.0L), nozzleDiameter, true));
                    } else {
                        mWarnings = ERROR_DIAMETER;
                    }
                    break;
                }
            }

            next = next->next;
        }
        nextPath = nextPath->next;
    }
}


<|start_filename|>src/libgeometry/gline.h<|end_filename|>
#ifndef GLINE_H
#define GLINE_H
#include "gshape.h"
#include "gpoint.h"

class GLine : public GShape
{
public:
    GLine(const GPoint &start, const GPoint &end, double nwidth = 0.0L, bool nrounded = true);
    GLine(const GPoint &start, const GPoint &end, double xwidth, double ywidth, bool nrounded = true);
    GShapeType type() const;
    GPoint p1;
    GPoint p2;
    double width;
    bool rounded;
    bool weak;
    bool wayContur;
    void move_relative(const GPoint &p);
    void move_vmirror(double p );
    void move_hmirror(double p );
    bool intersects(const GShape &other);
    GShape *contur(double offset, double conturLinesWidth) const;
    void cut(const GShape &other);
    bool contains(const GPoint &point) const;
    double length() const;
    double distanceToPoint(const GPoint &point) const;
    double distanceToPointWOEnds(const GPoint &point) const;
private:
    double distanceToPoint(const GPoint &lp1, const GPoint &lp2, const GPoint point) const;
};

#endif // GLINE_H


<|start_filename|>src/libgeometry/gintersects.h<|end_filename|>
#ifndef GINTERSECTS_H
#define GINTERSECTS_H
#include "gcircle.h"
#include "gline.h"
#include "grect.h"

namespace GIntersects
{

bool lineWithLine(const GLine &l1, const GLine &l2, GPoint *ipoint);

bool lineWithRect(const GLine &l, const GRect &r);

int lineWithCircle(const GLine &l, const GCircle &c, GPoint *res1, GPoint *res2, bool isSegment);

int circleWithCircle(const GCircle &c1, const GCircle &c2, GPoint *res1, GPoint *res2);

bool circleWithRect(const GCircle &c, const GRect &r);

bool rectWithRect(const GRect &r1, const GRect &r2);

}

#endif // GINTERSECTS_H


<|start_filename|>src/gcodeexport.h<|end_filename|>
#ifndef GCODEEXPORT_H
#define GCODEEXPORT_H
#include <stdio.h>
#include "libgeometry/gpoint.h"

class PathContur;
class DrillReader;
class GShape;

class GCodeExport {

public:
    enum Mode {
        Milling,
        Printing,
        Extrudering,
        Lasering
    };

    GCodeExport(char *outputfile, PathContur *contur1, PathContur *contur2, Mode mode, double depth, double edgedepth, \
                  double zsafe, int millingSpeed, int edgeMillingSpeed, int spindleRpm, double millingDiameter, \
                  DrillReader *drillReader, int drillingSpeed, int drillingLiftSpeed, int drillingSpindleSpeed, \
                  double drillDepth, double drillZSafe, bool sameDiameter , bool isProbe, double filamentDiameter, \
                  double nozzleDiameter, int extruderTemperature, double printingSpeed, bool edges, double laserEnergy, \
                  double laserXOffset, double toolYOffset);
    double filament();
    double time();
    void time(char *buff);
private:
    void probe(FILE *fl, double zsafe);
    void changeTool(FILE *fl, double drillDiameter, double oldZsafe, double zsafe, bool sameDiameter, bool isProbe, char *reason, bool moveToZeroFirst);
    void millingCircle(FILE *fl, Mode mode, const GPoint &center, const GPoint &to, double depth, int millingSpeed, bool isCCW, double circleDistance, double filamentMMPerHeadMM, double laserEnergy, double nozzleDiameter);
    void millingLineTo(FILE *fl, Mode mode, const GPoint &to, double depth, int millingSpeed, double filamentMMPerHeadMM, double laserEnergy, double nozzleDiameter);
    void moveHeadTo(FILE *fl, Mode mode, const GPoint &to, double zsafe);
    void drill(FILE *fl, const GPoint &point, double drillDepth, int drillingSpeed, int drillingLiftSpeed, double zsafe);
    void putFooterComments(FILE *fl, bool filamentReport);
    void doShapes(FILE *fl, Mode mode, GShape *shapes, double diameter, double zsafe, double depth, double speed, double filamentMMPerHeadMM, double laserEnergy);
    void doDrill(FILE *fl, bool changeToDrill, DrillReader *drillReader, double drillZSafe, bool sameDiameter, bool isProbe, int drillingSpeed, \
                 int drillingLiftSpeed, int drillingSpindleSpeed, double drillDepth, double zsafe);
    FILE *organizeSecondFile(FILE *fl, char *outputfile, bool filamentReport);

    static void timeToText(double time, char *buff);
    static double timeForMovement(double speed, double distance);
    GPoint headPosition;
    double headZPosition;
    int toolNumber;
    double totalTime;
    double ePosition;
    double filamentUsed;
};

#endif // GCODEEXPORT_H
