~ubuntu-branches/ubuntu/precise/fritzing/precise

« back to all changes in this revision

Viewing changes to src/svg/svg2gerber.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Georges Khaznadar
  • Date: 2011-08-26 10:11:05 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20110826101105-w5hmn7zcf93ig5v6
Tags: 0.6.3b-1
* upgrapded to the newer upstream version
* parameters of the function GraphicsUtils::distanceFromLine in 
  src/svg/groundplanegenerator.cpp:767 are now declared as doubles,
  which Closes: #636441
* the new version fixes src/utils/folderutils.cpp, which
  Closes: #636061

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
********************************************************************
20
20
 
21
 
$Revision: 5143 $:
 
21
$Revision: 5396 $:
22
22
$Author: cohen@irascible.com $:
23
 
$Date: 2011-07-01 02:37:01 +0200 (Fri, 01 Jul 2011) $
 
23
$Date: 2011-08-15 01:36:25 +0200 (Mon, 15 Aug 2011) $
24
24
 
25
25
********************************************************************/
26
26
 
30
30
#include <QTextStream>
31
31
#include <qmath.h>
32
32
 
33
 
static const qreal MaskClearance = 0.003;  // 3 mils clearance
 
33
static const double MaskClearance = 0.003;  // 3 mils clearance
34
34
 
35
35
bool fillNotStroke(QDomElement & element, SVG2gerber::ForWhy forWhy) {
36
36
        if (forWhy == SVG2gerber::ForOutline) return false;
203
203
        if(tag=="polygon"){
204
204
            path = element;
205
205
        }
 
206
        else if(tag=="polyline"){
 
207
            path = element;
 
208
        }
206
209
        else if(tag=="rect"){
207
210
            path = element;
208
211
        }
282
285
    QDomNodeList polyList = m_SVGDom.elementsByTagName("polygon");
283
286
    //DebugDialog::debug("polygons to gerber: " + QString::number(polyList.length()));
284
287
 
 
288
    QDomNodeList polyLineList = m_SVGDom.elementsByTagName("polyline");
 
289
    DebugDialog::debug("polylines to gerber: " + QString::number(polyLineList.length()));
 
290
 
285
291
        QDomNodeList lineList = m_SVGDom.elementsByTagName("line");
286
292
    //DebugDialog::debug("lines to gerber: " + QString::number(lineList.length()));
287
293
 
289
295
    //DebugDialog::debug("paths to gerber: " + QString::number(pathList.length()));
290
296
 
291
297
        int totalCount = circleList.count() + rectList.count() + polyList.count() + lineList.count() + pathList.count();
 
298
        int outlineCount = 0;
292
299
 
293
300
    // if this is the board outline, use it as the contour
294
301
    if (forWhy == ForOutline) {
310
317
                        if (circle.attribute("id").compare("boardoutline", Qt::CaseInsensitive) != 0) continue;
311
318
                }
312
319
 
313
 
        qreal centerx = circle.attribute("cx").toDouble();
314
 
        qreal centery = circle.attribute("cy").toDouble();
315
 
        qreal r = circle.attribute("r").toDouble();
 
320
                outlineCount++;
 
321
 
 
322
        double centerx = circle.attribute("cx").toDouble();
 
323
        double centery = circle.attribute("cy").toDouble();
 
324
        double r = circle.attribute("r").toDouble();
316
325
                if (r == 0) continue;
317
326
 
318
 
        qreal stroke_width = circle.attribute("stroke-width").toDouble();
319
 
                qreal hole = ((2*r) - stroke_width)/1000;
 
327
        double stroke_width = circle.attribute("stroke-width").toDouble();
 
328
                double hole = ((2*r) - stroke_width)/1000;
320
329
 
321
330
                if (forWhy == ForDrill) {
322
331
                        QString drill_cx = QString::number(flipxNoRound(centerx) / 1000, 'f');                          // drill file seems to be in inches
344
353
 
345
354
        QString fill = circle.attribute("fill");
346
355
 
347
 
        qreal diam = ((2*r) + stroke_width)/1000;
 
356
        double diam = ((2*r) + stroke_width)/1000;
348
357
                if (forWhy == ForMask) {
349
358
                        diam += 2 * MaskClearance;
350
359
                }
351
360
 
352
 
        if(fill=="none" && forWhy != ForMask){
 
361
        if(false && fill=="none" && forWhy != ForMask){
353
362
                        aperture = QString("C,%1X%2").arg(diam, 0, 'f').arg(hole);
354
363
        }
355
364
        else {
398
407
 
399
408
                        QString aperture;
400
409
 
401
 
                        qreal width = rect.attribute("width").toDouble();
402
 
                        qreal height = rect.attribute("height").toDouble();
 
410
                        double width = rect.attribute("width").toDouble();
 
411
                        double height = rect.attribute("height").toDouble();
 
412
 
 
413
                        double rx = rect.attribute("rx", "0").toDouble();
 
414
                        double ry = rect.attribute("ry", "0").toDouble();
 
415
                        if (rx != 0 || ry != 0) {
 
416
                                // not sure how to do rounded rects in gerber
 
417
                                invalidPathsCount++;
 
418
                                continue;
 
419
                        }
403
420
 
404
421
                        if (width == 0) continue;
405
422
                        if (height == 0) continue;
406
423
 
407
 
                        qreal x = rect.attribute("x").toDouble();
408
 
                        qreal y = rect.attribute("y").toDouble();
409
 
                        qreal centerx = x + (width/2);
410
 
                        qreal centery = y + (height/2);
 
424
                        double x = rect.attribute("x").toDouble();
 
425
                        double y = rect.attribute("y").toDouble();
 
426
                        double centerx = x + (width/2);
 
427
                        double centery = y + (height/2);
411
428
                        QString cx = QString::number(flipx(centerx));
412
429
                        QString cy = QString::number(flipy(centery));
413
430
                        QString fill = rect.attribute("fill");
414
 
                        qreal stroke_width = rect.attribute("stroke-width").toDouble();
 
431
                        double stroke_width = rect.attribute("stroke-width").toDouble();
415
432
 
416
 
                        qreal totalx = (width + stroke_width)/1000;
417
 
                        qreal totaly = (height + stroke_width)/1000;
418
 
                        qreal holex = (width - stroke_width)/1000;
419
 
                        qreal holey = (height - stroke_width)/1000;
 
433
                        double totalx = (width + stroke_width)/1000;
 
434
                        double totaly = (height + stroke_width)/1000;
 
435
                        double holex = (width - stroke_width)/1000;
 
436
                        double holey = (height - stroke_width)/1000;
420
437
 
421
438
                        if (forWhy == ForMask) {
422
439
                                totalx += 2 * MaskClearance;
424
441
                        }
425
442
 
426
443
 
427
 
                        if(fill=="none" && forWhy != ForMask) {
 
444
                        if(false && fill=="none" && forWhy != ForMask) {
428
445
                                aperture = QString("R,%1X%2X%3X%4").arg(totalx, 0, 'f').arg(totaly, 0, 'f').arg(holex, 0, 'f').arg(holey, 0, 'f');
429
446
                        }
430
447
                        else {
451
468
                        else {
452
469
                                // draw 4 lines
453
470
 
 
471
                                outlineCount++;
454
472
                                standardAperture(rect, apertureMap, current_dcode, dcode_index, 0);
455
473
                                m_gerber_paths += "X" + QString::number(flipx(x)) + "Y" + QString::number(flipy(y)) + "D02*\n";
456
474
                                m_gerber_paths += "X" + QString::number(flipx(x+width)) + "Y" + QString::number(flipy(y)) + "D01*\n";
464
482
                // lines - NOTE: this assumes a circular aperture
465
483
                for(uint k = 0; k < lineList.length(); k++){
466
484
                        QDomElement line = lineList.item(k).toElement();
467
 
                        if (forWhy == ForOutline && totalCount > 1) {
468
 
                                if (line.attribute("id").compare("boardoutline", Qt::CaseInsensitive) != 0) continue;
469
 
                        }
470
485
 
471
486
                        // Note: should be no forWhy == ForMask cases 
472
487
 
473
 
                        qreal x1 = line.attribute("x1").toDouble();
474
 
                        qreal y1 = line.attribute("y1").toDouble();
475
 
                        qreal x2 = line.attribute("x2").toDouble();
476
 
                        qreal y2 = line.attribute("y2").toDouble();
 
488
                        double x1 = line.attribute("x1").toDouble();
 
489
                        double y1 = line.attribute("y1").toDouble();
 
490
                        double x2 = line.attribute("x2").toDouble();
 
491
                        double y2 = line.attribute("y2").toDouble();
477
492
 
478
493
                        standardAperture(line, apertureMap, current_dcode, dcode_index, 0);
479
494
 
485
500
                                }
486
501
                        }
487
502
 
 
503
                        outlineCount++;
 
504
 
488
505
                        //go to start - light off
489
506
                        m_gerber_paths += "X" + QString::number(flipx(x1)) + "Y" + QString::number(flipy(y1)) + "D02*\n";
490
507
                        //go to end point - light on
495
512
                }
496
513
 
497
514
                // polys - NOTE: assumes comma- or space- separated formatting
498
 
                for(uint p = 0; p < polyList.length(); p++){
499
 
                        QDomElement polygon = polyList.item(p).toElement();
500
 
                        if (forWhy == ForOutline && totalCount > 1) {
501
 
                                if (polygon.attribute("id").compare("boardoutline", Qt::CaseInsensitive) != 0) continue;
502
 
                        }
503
 
 
504
 
                        QString temp;
505
 
                        QTextStream tempStream(&temp);
506
 
                        polygon.save(tempStream, 1);
507
 
 
508
 
                        QString points = polygon.attribute("points");
509
 
                        QStringList pointList = points.split(QRegExp("\\s+|,"), QString::SkipEmptyParts);
510
 
 
511
 
                        QString aperture;
512
 
 
513
 
                        QString pointString;
514
 
 
515
 
                        qreal startx = pointList.at(0).toDouble();
516
 
                        qreal starty = pointList.at(1).toDouble();
517
 
                        // move to start - light off
518
 
                        pointString += "X" + QString::number(flipx(startx)) + "Y" + QString::number(flipy(starty)) + "D02*\n";
519
 
 
520
 
                        // iterate through all other points - light on
521
 
                        for(int pt = 2; pt < pointList.length(); pt +=2){
522
 
                                qreal ptx = pointList.at(pt).toDouble();
523
 
                                qreal pty = pointList.at(pt+1).toDouble();
524
 
                                pointString += "X" + QString::number(flipx(ptx)) + "Y" + QString::number(flipy(pty)) + "D01*\n";
525
 
                        }
526
 
 
527
 
                        // move back to start point
528
 
                        pointString += "X" + QString::number(flipx(startx)) + "Y" + QString::number(flipy(starty)) + "D01*\n";
529
 
 
530
 
                        standardAperture(polygon, apertureMap, current_dcode, dcode_index, 0);          
531
 
 
532
 
                        bool polyFill = fillNotStroke(polygon, forWhy);
533
 
                        // set poly fill if this is actually a filled in shape
534
 
                        if (polyFill) {                                                 
535
 
                                // start poly fill
536
 
                                m_gerber_paths += "G36*\n";
537
 
                        }
538
 
 
539
 
                        m_gerber_paths += pointString;
540
 
 
541
 
                        // stop poly fill if this is actually a filled in shape
542
 
                        if(polyFill){
543
 
                                // stop poly fill
544
 
                                m_gerber_paths += "G37*\n";
545
 
                        }
546
 
 
547
 
                        if (forWhy == ForMask) {
548
 
                                // draw the outline, G36 only does the fill
549
 
                                standardAperture(polygon, apertureMap, current_dcode, dcode_index,  polygon.attribute("stroke-width").toDouble() + (MaskClearance * 2 * 1000));
550
 
                                m_gerber_paths += pointString;
551
 
                        }
552
 
 
553
 
                        // light off
554
 
                        m_gerber_paths += "D02*\n";
 
515
                for(uint p = 0; p < polyList.length(); p++) {
 
516
                        QDomElement polygon = polyList.item(p).toElement();
 
517
                        doPoly(polygon, forWhy, totalCount, true, apertureMap, current_dcode, dcode_index, outlineCount);
 
518
                }
 
519
                for(uint p = 0; p < polyLineList.length(); p++) {
 
520
                        QDomElement polygon = polyList.item(p).toElement();
 
521
                        doPoly(polygon, forWhy, totalCount, false, apertureMap, current_dcode, dcode_index, outlineCount);
555
522
                }
556
523
        }
557
524
 
562
529
                        if (path.attribute("id").compare("boardoutline", Qt::CaseInsensitive) != 0) continue;
563
530
                }
564
531
 
 
532
                outlineCount++;
 
533
 
565
534
                if (forWhy == ForDrill) {
566
535
                        handleOblongPath(path, dcode_index);
567
536
                        continue;
578
547
        pathUserData.string = "";
579
548
 
580
549
        SvgFlattener flattener;
581
 
        flattener.parsePath(data, slot, pathUserData, this, true);
582
 
 
 
550
                bool invalid = false;
 
551
                try {
 
552
                        flattener.parsePath(data, slot, pathUserData, this, true);
 
553
                }
 
554
                catch (const QString & msg) {
 
555
                        DebugDialog::debug("flattener.parsePath failed " + msg);
 
556
                        invalid = true;
 
557
                }
 
558
                catch (char const *str) {
 
559
                        DebugDialog::debug("flattener.parsePath failed " + QString(str));
 
560
                        invalid = true;
 
561
                }
 
562
                catch (...) {
 
563
                        DebugDialog::debug("flattener.parsePath failed");
 
564
                        invalid = true;
 
565
                }
583
566
                
584
567
 
585
568
        // only add paths if they contained gerber-izable path commands (NO CURVES!)
586
569
        // TODO: display some informative error for the user
587
 
        if(pathUserData.string.contains("INVALID")) {
 
570
        if (invalid || pathUserData.string.contains("INVALID")) {
588
571
                        invalidPathsCount++;
589
572
            continue;
590
573
                }
622
605
    if (forWhy == ForOutline) {
623
606
        // add circular aperture with 0 width
624
607
        m_gerber_header += "%ADD10C,0.008*%\n";
 
608
                if (outlineCount != 1) {
 
609
                        // if we can't find one and only one svg element as the board outline, then go to plan b
 
610
                        invalidPathsCount++;
 
611
                }
625
612
    }
626
613
 
627
614
        return invalidPathsCount;
628
615
}
629
616
 
630
 
QString SVG2gerber::standardAperture(QDomElement & element, QHash<QString, QString> & apertureMap, QString & current_dcode, int & dcode_index, qreal stroke_width) {
 
617
void SVG2gerber::doPoly(QDomElement & polygon, ForWhy forWhy, int totalCount, bool closedCurve,
 
618
                                        QHash<QString, QString> & apertureMap, QString & current_dcode, int & dcode_index, int & outlineCount) 
 
619
{
 
620
        if (forWhy == ForOutline && totalCount > 1) {
 
621
                if (polygon.attribute("id").compare("boardoutline", Qt::CaseInsensitive) != 0) return;
 
622
        }
 
623
 
 
624
        outlineCount++;
 
625
 
 
626
        //QString temp;
 
627
        //QTextStream tempStream(&temp);
 
628
        //polygon.save(tempStream, 1);
 
629
 
 
630
        QString points = polygon.attribute("points");
 
631
        QStringList pointList = points.split(QRegExp("\\s+|,"), QString::SkipEmptyParts);
 
632
 
 
633
        QString aperture;
 
634
 
 
635
        QString pointString;
 
636
 
 
637
        double startx = pointList.at(0).toDouble();
 
638
        double starty = pointList.at(1).toDouble();
 
639
        // move to start - light off
 
640
        pointString += "X" + QString::number(flipx(startx)) + "Y" + QString::number(flipy(starty)) + "D02*\n";
 
641
 
 
642
        // iterate through all other points - light on
 
643
        for(int pt = 2; pt < pointList.length(); pt +=2){
 
644
                double ptx = pointList.at(pt).toDouble();
 
645
                double pty = pointList.at(pt+1).toDouble();
 
646
                pointString += "X" + QString::number(flipx(ptx)) + "Y" + QString::number(flipy(pty)) + "D01*\n";
 
647
        }
 
648
 
 
649
        if (closedCurve) {
 
650
                // move back to start point
 
651
                pointString += "X" + QString::number(flipx(startx)) + "Y" + QString::number(flipy(starty)) + "D01*\n";
 
652
        }
 
653
 
 
654
        standardAperture(polygon, apertureMap, current_dcode, dcode_index, 0);          
 
655
 
 
656
        bool polyFill = fillNotStroke(polygon, forWhy);
 
657
        // set poly fill if this is actually a filled in shape
 
658
        if (polyFill) {                                                 
 
659
                // start poly fill
 
660
                m_gerber_paths += "G36*\n";
 
661
        }
 
662
 
 
663
        m_gerber_paths += pointString;
 
664
 
 
665
        // stop poly fill if this is actually a filled in shape
 
666
        if(polyFill){
 
667
                // stop poly fill
 
668
                m_gerber_paths += "G37*\n";
 
669
        }
 
670
 
 
671
        if (forWhy == ForMask) {
 
672
                // draw the outline, G36 only does the fill
 
673
                standardAperture(polygon, apertureMap, current_dcode, dcode_index,  polygon.attribute("stroke-width").toDouble() + (MaskClearance * 2 * 1000));
 
674
                m_gerber_paths += pointString;
 
675
        }
 
676
 
 
677
        // light off
 
678
        m_gerber_paths += "D02*\n";
 
679
 
 
680
}
 
681
 
 
682
QString SVG2gerber::standardAperture(QDomElement & element, QHash<QString, QString> & apertureMap, QString & current_dcode, int & dcode_index, double stroke_width) {
631
683
        if (stroke_width == 0) {
632
684
                stroke_width = element.attribute("stroke-width").toDouble();
633
685
        }
665
717
        QDomElement nextLine = nextPath.nextSiblingElement("line");
666
718
        if (nextLine.isNull()) return;
667
719
                                
668
 
        qreal diameter = parent.attribute("stroke-width").toDouble();
669
 
        qreal cx1 = nextLine.attribute("x1").toDouble();
670
 
        qreal cy1 = nextLine.attribute("y1").toDouble();
671
 
        qreal cx2 = nextLine.attribute("x2").toDouble();
672
 
        qreal cy2 = nextLine.attribute("y2").toDouble();
 
720
        double diameter = parent.attribute("stroke-width").toDouble();
 
721
        double cx1 = nextLine.attribute("x1").toDouble();
 
722
        double cy1 = nextLine.attribute("y1").toDouble();
 
723
        double cx2 = nextLine.attribute("x2").toDouble();
 
724
        double cy2 = nextLine.attribute("y2").toDouble();
673
725
 
674
726
        QString drill_aperture = QString("C%1").arg(diameter / 1000, 0, 'f') + "\n";
675
727
        if (!m_gerber_header.contains(drill_aperture)) {
712
764
                                        case 'C':
713
765
                                        case 'q':
714
766
                                        case 'Q':
 
767
                                        case 's':
 
768
                                        case 'S':
715
769
                                        case 't':
716
770
                                        case 'T':
717
771
                                                // TODO: implement elliptical arc, etc.
718
772
                                                pathUserData->string.append("INVALID");
 
773
                                                argIndex = args.count();
719
774
                                                break;
720
775
                                        case 'm':
721
776
                                        case 'M':
772
827
                                                pathUserData->pathStarting = true;
773
828
                                                break;
774
829
                                        default:
 
830
                                                argIndex = args.count();
775
831
                                                pathUserData->string.append("INVALID");
776
832
                                                break;
777
833
                }
779
835
}
780
836
 
781
837
 
782
 
int SVG2gerber::flipx(qreal x)
 
838
int SVG2gerber::flipx(double x)
783
839
{
784
840
        return qRound(x);
785
841
}
786
842
 
787
 
int SVG2gerber::flipy(qreal y)
 
843
int SVG2gerber::flipy(double y)
788
844
{
789
845
        return qRound(m_boardSize.height() - y);
790
846
}
791
847
 
792
 
qreal SVG2gerber::flipxNoRound(qreal x)
 
848
double SVG2gerber::flipxNoRound(double x)
793
849
{
794
850
        return x;
795
851
}
796
852
 
797
 
qreal SVG2gerber::flipyNoRound(qreal y)
 
853
double SVG2gerber::flipyNoRound(double y)
798
854
{
799
855
        return m_boardSize.height() - y;
800
856
}