1/* GerberExportSub.m
2 * Sub class of Gerber export
3 *
4 * Copyright (C) 2002-2012 by vhf interservice GmbH
5 * Author:   Ilonka Fleischmann
6 *
7 * created:  2002-04-25
8 * modified: 2008-12-02 (fix: export paths stacked on each other)
9 *           2008-06-21 (fixes)
10 *
11 * This file is part of the vhf Export Library.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the vhf Public License as
15 * published by the vhf interservice GmbH. Among other things,
16 * the License requires that the copyright notices and this notice
17 * be preserved on all copies.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 * See the vhf Public License for more details.
23 *
24 * You should have received a copy of the vhf Public License along
25 * with this library; see the file LICENSE. If not, write to vhf.
26 *
27 * If you want to link this library to your proprietary software,
28 * or for other uses which are not covered by the definitions
29 * laid down in the vhf Public License, vhf also offers a proprietary
30 * license scheme. See the vhf internet pages or ask for details.
31 *
32 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany
33 * eMail: info@vhf.de
34 * http://www.vhf.de
35 */
36
37#include <AppKit/AppKit.h>
38#include <VHFShared/vhfCommonFunctions.h>
39#include <VHFShared/VHFStringAdditions.h> // stringByReplacing -> eps export
40#include "App.h"
41#include "GerberExportSub.h"
42#include "Graphics.h"
43#include "messages.h"
44#include "locations.h"
45#include "PreferencesMacros.h"
46
47//#define RES 1000
48
49@interface GerberExportSub(PrivateMethods)
50- (void)exportPolyLine:(VPolyLine*)g;
51- (void)exportRectangle:(VRectangle*)g;
52- (void)exportArc:(VArc*)g;
53- (void)exportCurve:(VCurve*)g;
54- (void)exportText:(VText*)g;
55- (void)exportPath:(VPath*)path;
56- (void)exportGroup:(VGroup*)group;
57@end
58
59@implementation GerberExportSub
60
61- (void)setDocumentView:view
62{
63    docView = view;
64}
65
66- (void)exportPolyLine:(VPolyLine*)g
67{   int		i, cnt = [(VPolyLine*)g numPoints];
68
69    // start polygon mode G36*
70    if ([g filled] && cnt > 3)
71        [self writePolygonMode:YES];
72
73    for (i=0; i<cnt-1; i++)
74        [self writeLine:[g pointWithNum:i] :[g pointWithNum:i+1]];
75
76    // end polygon mode G37*
77    if ([g filled] && cnt > 3)
78        [self writePolygonMode:NO];
79}
80
81- (void)exportRectangle:(VRectangle*)g
82{
83    if ([g radius] || ![g filled])
84        [self exportPath:[g pathRepresentation]];
85    else
86    {   NSPoint	origin, size, center;
87        [g getVertices:&origin :&size];
88        center.x = origin.x + size.x/2.0;
89        center.y = origin.y + size.y/2.0;
90        [self writeRectangle:center]; // flash to center
91    }
92}
93
94- (void)exportArc:(VArc*)g
95{
96    if (Abs([g angle]) != 360.0 || ![g filled])
97    {   float	angle;
98        NSPoint	center, start, end;
99        BOOL	ccw = 0; // cw -> G02
100
101        [g getCenter:&center start:&start angle:&angle];
102        end = [g pointWithNum:MAXINT]; // end
103
104        if (angle > 0.0) // ccw -> G03
105            ccw = 1;
106
107        [self writeArc:center :start :end :ccw];
108    }
109    else
110        [self writeCircle:[g center]]; // flash
111}
112
113- (void)exportCurve:(VCurve*)g
114{   NSAutoreleasePool	*pool = [NSAutoreleasePool new];
115    VPath		*path;
116
117    if ( (path = [g flattenedObjectWithFlatness:0.1]) )
118    {   int	i, cnt = [[path list] count];
119
120        for (i=0; i<cnt; i++)
121        {   id	line = [[path list] objectAtIndex:i];
122
123            [self writeLine:[line pointWithNum:0] :[line pointWithNum:1]];
124        }
125    }
126    [pool release];
127}
128
129- (void)exportText:(VText*)g
130{   NSAutoreleasePool	*pool = [NSAutoreleasePool new];
131    id			text = [g pathRepresentation];
132
133    if (text && [text isKindOfClass:[VPath class]])
134        [self exportPath:text];
135    else if (text /*&& [text isKindOfClass:[VGroup class]]*/)
136        [self exportGroup:text];
137    [pool release];
138}
139
140- (void)exportPath:(VPath*)path
141{   int			i, j, k, b, cnt=[[path list] count], subPathsCnt = 0, subPathsInCnt = 0;
142    NSMutableArray	*subpaths = [NSMutableArray array], *subpathsIns = [NSMutableArray array];
143    BOOL		lastDrawnIsClear = NO;
144
145    /* sort subPaths of path in arrays */
146    for ( i=0; i<cnt; i=j+1 )
147    {	VPath		*pth = [VPath path];
148
149        j = [path getLastObjectOfSubPath:i];
150        subPathsCnt++;
151
152        [pth setFilled:YES];
153        for ( k=i; k < j+1; k++ )
154            [[pth list] addObject:[[path list] objectAtIndex:k]];
155        [subpaths addObject:pth];
156    }
157    /* polygons inside must be clear(white) outside must be dark(black)*/
158    if (subPathsCnt > 1 && [path filled])
159    {
160        /* count how many times each subpath is inside all others */
161        for ( i=0; i<subPathsCnt; i++ )
162        {   id		gr = [[[subpaths objectAtIndex:i] list] objectAtIndex:0]; // first gr in subpath
163            NSPoint	pt = [gr pointAt:0.4];
164            int		inside = 0;
165
166            for ( j=0; j< subPathsCnt; j++ )
167            {	VPath	*sp = [subpaths objectAtIndex:j];
168
169                if ( i == j )
170                    continue;
171                if ( [sp isPointInside:pt] )
172                    inside++;
173            }
174            [subpathsIns addObject:[NSNumber numberWithInt:inside]];
175            subPathsInCnt++;
176        }
177    }
178
179    b = 0;
180    while ( b >= 0 )
181    {   BOOL	drawed = NO, somethingToDraw = NO;
182
183        for ( j=0; j<subPathsCnt; j++ )
184        {   int	inside;
185
186            inside = (subPathsInCnt > 1) ? [[subpathsIns objectAtIndex:j] intValue] : 0;
187            /* !b - we draw only the black one which have no realy no inside ; b - we draw only the white one */
188            if ( (inside == b) )
189            {   somethingToDraw  = YES;
190                break;
191            }
192        }
193
194        if (!Even(b) && [path filled] && b && subPathsCnt > b && somethingToDraw)
195        {
196            [self writeLayerPolarityMode:YES]; // clear
197            lastDrawnIsClear = YES;
198        }
199        else if (Even(b) && [path filled] && b && subPathsCnt > b && somethingToDraw)
200        {   [self writeLayerPolarityMode:NO]; // dark
201            lastDrawnIsClear = NO;
202        }
203
204        for ( j=0; j<subPathsCnt; j++ )
205        {   VPath	*sp = [subpaths objectAtIndex:j];
206            int		cnt = [[sp list] count], inside;
207
208            inside = (subPathsInCnt > 1) ? [[subpathsIns objectAtIndex:j] intValue] : 0;
209            /* !b - we draw only the black one which have no realy no inside ; b - we draw only the white one */
210            if ( !(inside == b) )
211                continue;
212
213            drawed = YES;
214            // start polygon mode G36*
215            if ([path filled])
216                [self writePolygonMode:YES];
217
218            for (i=0; i<cnt; i++)
219            {   id	g = [[sp list] objectAtIndex:i];
220
221                if ( [g isKindOfClass:[VLine class]] )
222                    [self writeLine:[g pointWithNum:0] :[g pointWithNum:1]];
223                else if ( [g isKindOfClass:[VPolyLine class]] )
224                    [self exportPolyLine:g];
225                else if (  [g isKindOfClass:[VRectangle class]] )
226                    [self exportRectangle:g];
227                else if (  [g isKindOfClass:[VArc class]] )
228                    [self exportArc:g];
229                else if (  [g isKindOfClass:[VPath class]] )
230                    [self exportPath:g];
231                else if (  [g isKindOfClass:[VCurve class]] )
232                    [self exportCurve:g];
233                else NSLog(@"Gerber exportPath g type not implemented\n");
234            }
235            // end polygon mode G37*
236            if ([path filled])
237                [self writePolygonMode:NO];
238        }
239        if ( drawed == NO )
240            break;
241        b++;
242    }
243    if ([path filled] && lastDrawnIsClear == YES)
244        [self writeLayerPolarityMode:NO]; // set LayerPolarity back to dark
245}
246
247- (void)exportGroup:(VGroup*)group
248{   int		j, k, cnt=[[group list] count];
249    int		insideIs[cnt], insideCnt = 0, polygonIs[cnt], polyCnt = 0, pathsIns[cnt];
250    NSRect	polyBounds[cnt];
251
252    /* get all bounds of polygons and */
253    /* get all indexes of polygons */
254    for (j=0; j<cnt; j++)
255    {   VGraphic    *g = [[group list] objectAtIndex:j];
256
257        if ([g isKindOfClass:[VPath class]])
258        {
259            polygonIs[polyCnt] = j;
260            polyBounds[polyCnt++] = [g bounds];
261        }
262    }
263
264    if (polyCnt > 1)
265    {
266        /* count how many times each path is inside all others */
267        for (j=0; j < polyCnt; j++)
268        {   NSArray     *list = [[[group list] objectAtIndex:polygonIs[j]] list];
269            VGraphic    *gr;
270            NSPoint     pt;
271            int         inside = 0;
272
273            if (![list count])
274                continue;   // empty group
275            gr = [list objectAtIndex:0];
276            pt = [gr pointWithNum:0];
277            for (k=0; k < polyCnt; k++)
278            {   NSRect	kbounds = polyBounds[k];
279
280                if ( j == k )
281                    continue;
282                if ( NSPointInRect(pt, kbounds) )
283                    inside++;
284            }
285            pathsIns[j] = inside;
286        }
287    }
288
289    /* get all indexes of graphics inside/intersect bounds of polygons */
290    for (j=0; j<cnt; j++)
291    {   id	g = [[group list] objectAtIndex:j];
292
293        if (![g isKindOfClass:[VPath class]])
294        {   NSRect	bounds = [g bounds];
295
296            for (k=0; k<polyCnt; k++)
297            {
298                if ( vhfIntersectsRect(polyBounds[k], bounds) )
299                {   insideIs[insideCnt++] = j;
300                    break;
301                }
302            }
303        }
304    }
305
306    /* draw graphics, but polygons, outside bounds of polygons */
307    /* draw polygons from outside to inside */
308    /* draw graphics inside bounds of polygons */
309    for (k=0; k < 3; k++)
310    {
311        /* draw polygons from outside to inside */
312        if ( k == 1 )
313        {   int	b = 0;
314
315            while ( b >= 0 )
316            {   BOOL	drawed = NO;
317
318                for (j=0; j<polyCnt; j++)
319                {   int	inside;
320                    VPath	*sp = [[group list] objectAtIndex:polygonIs[j]];
321
322                    inside = (polyCnt > 1) ? pathsIns[j] : 0;
323                    /* b == inside; we draw from outside to inside */
324                    if ( !(inside == b) )
325                        continue;
326
327                    drawed = YES;
328                    [self exportPath:sp];
329                }
330                if (drawed == NO)
331                    break;
332                b++;
333            }
334            continue;
335        }
336        for (j=0; j<cnt; j++)
337        {   VGraphic    *g = [[group list] objectAtIndex:j];
338            float       w = [g width];
339
340            if ( !(!k && ![g isKindOfClass:[VPath class]] && !valueInArray(j, insideIs, insideCnt)) &&
341                 /*!(k == 1 && [g isKindOfClass:[VPath class]]) &&*/
342                 !(k == 2 && ![g isKindOfClass:[VPath class]] && valueInArray(j, insideIs, insideCnt)) )
343                continue;
344
345            if ([g isKindOfClass:[VRectangle class]] && [g filled] && ![(VRectangle*)g radius])
346            {   NSSize	rsize = [(VRectangle*)g size];
347
348                [self writeRectangleTool:w+rsize.width :w+rsize.height];
349            }
350            else if (w ||
351                     ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0 && [(VArc*)g filled]))
352            {
353                if ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0 && [(VArc*)g filled])
354                    w += [(VArc*)g radius]*2.0;
355                [self writeCircleTool:w];
356            }
357
358            if ( [g isKindOfClass:[VLine class]] )
359                [self writeLine:[g pointWithNum:0] :[g pointWithNum:1]];
360            else if ( [g isKindOfClass:[VPolyLine class]] )
361                [self exportPolyLine:(VPolyLine*)g];
362            else if (  [g isKindOfClass:[VRectangle class]] )
363                [self exportRectangle:(VRectangle*)g];
364            else if (  [g isKindOfClass:[VArc class]] )
365                [self exportArc:(VArc*)g];
366            else if (  [g isKindOfClass:[VCurve class]] )
367                [self exportCurve:(VCurve*)g];
368            else if (  [g isKindOfClass:[VPath class]] )
369                NSLog(@"GerberExportSub.m: exportToFile, exportPath\n"); /*[self exportPath:(VPath*)g]*/
370            else if (  [g isKindOfClass:[VGroup class]] )
371                [self exportGroup:(VGroup*)g];
372            else if (  [g isKindOfClass:[VText class]] )
373                 [self exportText:(VText*)g];
374        }
375    }
376/*
377 for (i=0; i<cnt; i++)
378 {   id	g = [[group list] objectAtIndex:i];
379     float	w = [g width];
380
381     if ([g isKindOfClass:[VRectangle class]] && [g filled] && ![(VRectangle*)g radius])
382     {   NSSize	rsize = [(VRectangle*)g size];
383
384         [self writeRectangleTool:w+rsize.width :w+rsize.height];
385     }
386     else if (w ||
387              ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0 && [(VArc*)g filled]))
388     {
389         if ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0 && [(VArc*)g filled])
390             w += [(VArc*)g radius]*2.0;
391         [self writeCircleTool:w];
392     }
393
394     if ( [g isKindOfClass:[VLine class]] )
395         [self writeLine:[g pointWithNum:0] :[g pointWithNum:1]];
396     else if ( [g isKindOfClass:[VPolyLine class]] )
397         [self exportPolyLine:g];
398     else if (  [g isKindOfClass:[VRectangle class]] )
399         [self exportRectangle:g];
400     else if (  [g isKindOfClass:[VArc class]] )
401         [self exportArc:g];
402     else if (  [g isKindOfClass:[VCurve class]] )
403         [self exportCurve:g];
404     else if (  [g isKindOfClass:[VPath class]] )
405         [self exportPath:g];
406     else if (  [g isKindOfClass:[VGroup class]] )
407         [self exportGroup:g];
408     else NSLog(@"gerber exportGroup g type not implemented\n");
409 }
410 */
411}
412
413
414- (BOOL)exportToFile:(NSString*)filename
415{   BOOL		savedOk = NO;
416    NSString		*backupFilename;
417    NSFileManager	*fileManager = [NSFileManager defaultManager];
418
419    backupFilename = [[[filename stringByDeletingPathExtension] stringByAppendingString:@"~"]
420                      stringByAppendingPathExtension:GERBER_EXT];
421    /* file not writable */
422    if ( [fileManager fileExistsAtPath:filename] && ![fileManager isWritableFileAtPath:filename] )
423    {   NSRunAlertPanel(SAVE_TITLE, CANT_CREATE_BACKUP, nil, nil, nil);
424        return NO;
425    }
426    /* rename to backup */
427    if ( ([fileManager fileExistsAtPath:backupFilename] && ![fileManager removeFileAtPath:backupFilename handler:nil]) || ([fileManager fileExistsAtPath:filename] && ![fileManager movePath:filename toPath:backupFilename handler:nil]) )
428    {   NSRunAlertPanel(SAVE_TITLE, CANT_CREATE_BACKUP, nil, nil, nil);
429        return NO;
430    }
431
432    //else if ( isDirectory && [fileManager createDirectoryAtPath:filename attributes:nil] )
433    //fileDirectory = [filename stringByDeletingLastPathComponent];
434    /* save */
435//    if ([fileManager isWritableFileAtPath:filename])
436    {   NSArray         *layerList = [docView layerList];
437        int             i, lcnt = [layerList count];
438        NSMutableArray	*gList = [NSMutableArray array];
439
440
441// 72 25400 div 72 25400 div scale\n
442
443        /* first clip rect */
444        for (i=0 ; i < lcnt  ; i++)
445        {   LayerObject	*lObj = [layerList objectAtIndex:i];
446
447            if ([lObj type] == LAYER_CLIPPING || ![lObj state]) // not visible - continue
448                continue;
449
450            /* add all visible objects to gList */
451            {   int	j, cnt = [[lObj list] count];
452
453                for (j=0; j<cnt; j++)
454                    [gList addObject:[[lObj list] objectAtIndex:j]];
455            }
456        }
457        /* export gList */
458        {   int	j, k, cnt = [gList count], maxInside = 0;
459            int	insideIs[cnt], insideCnt = 0, polygonIs[cnt], polyCnt = 0, pathsIns[cnt];
460            NSRect	polyBounds[cnt];
461
462            /* get all bounds of polygons and */
463            /* get all indexes of polygons */
464            for (j=0; j<cnt; j++)
465            {   id	g = [gList objectAtIndex:j];
466
467                if ([g isKindOfClass:[VPath class]])
468                {
469                    polygonIs[polyCnt] = j;
470                    polyBounds[polyCnt++] = [g bounds];
471                }
472            }
473
474            if (polyCnt > 1)
475            {
476                /* count how many times each path is inside all others */
477                for (j=0; j < polyCnt; j++)
478                {   id	gr = [[[gList objectAtIndex:polygonIs[j]] list] objectAtIndex:0];
479                    NSPoint	pt = [gr pointWithNum:0];
480                    int	inside = 0;
481
482                    for (k=0; k < polyCnt; k++)
483                    {   NSRect	kbounds = polyBounds[k];
484
485                        if ( j == k )
486                            continue;
487                        if ( NSPointInRect(pt, kbounds) )
488                            inside++;
489                    }
490                    if ( inside > maxInside )
491                        maxInside = inside;
492                    pathsIns[j] = inside;
493                }
494            }
495
496            /* get all indexes of graphics inside/intersect bounds of polygons */
497            for (j=0; j<cnt; j++)
498            {   id	g = [gList objectAtIndex:j];
499
500                if (![g isKindOfClass:[VPath class]])
501                {   NSRect	bounds = [g bounds];
502
503                    for (k=0; k<polyCnt; k++)
504                    {
505                        if ( vhfIntersectsRect(polyBounds[k], bounds) )
506                        {   insideIs[insideCnt++] = j;
507                            break;
508                        }
509                    }
510                }
511            }
512
513            /* draw graphics, but polygons, outside bounds of polygons */
514            /* draw polygons from outside to inside */
515            /* draw graphics inside bounds of polygons */
516            for (k=0; k < 3; k++)
517            {
518                /* draw polygons from outside to inside */
519                if ( k == 1 )
520                {   int	b = 0;
521
522                    while ( b >= 0 )
523                    {   BOOL	drawed = NO;
524
525                        for (j=0; j<polyCnt; j++)
526                        {   int	inside;
527                            VPath	*sp = [gList objectAtIndex:polygonIs[j]];
528
529                            inside = (polyCnt > 1) ? pathsIns[j] : 0;
530                            /* b == inside; we draw from outside to inside */
531                            if ( !(inside == b) )
532                                continue;
533
534                            drawed = YES;
535                            [self exportPath:sp];
536                        }
537                        if ( b >= maxInside ) // if (drawed == NO)
538                            break;
539                        b++;
540                    }
541                    continue;
542                }
543                for (j=0; j<cnt; j++)
544                {   VGraphic    *g = [gList objectAtIndex:j];
545                    float       w = [g width];
546
547                    if ( !(!k && ![g isKindOfClass:[VPath class]] && !valueInArray(j, insideIs, insideCnt)) &&
548                         /*!(k == 1 && [g isKindOfClass:[VPath class]]) &&*/
549                         !(k == 2 && ![g isKindOfClass:[VPath class]] && valueInArray(j, insideIs, insideCnt)) )
550                        continue;
551
552                    if ([g isKindOfClass:[VRectangle class]] && [g filled] && ![(VRectangle*)g radius])
553                    {   NSSize	rsize = [(VRectangle*)g size];
554
555                        [self writeRectangleTool:w+rsize.width :w+rsize.height];
556                    }
557                    else if (w ||
558                             ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0 && [(VArc*)g filled]))
559                    {
560                        if ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0 && [(VArc*)g filled])
561                            w += [(VArc*)g radius]*2.0;
562                        [self writeCircleTool:w];
563                    }
564
565                    if ( [g isKindOfClass:[VLine class]] )
566                        [self writeLine:[g pointWithNum:0] :[g pointWithNum:1]];
567                    else if ( [g isKindOfClass:[VPolyLine class]] )
568                        [self exportPolyLine:(VPolyLine*)g];
569                    else if (  [g isKindOfClass:[VRectangle class]] )
570                        [self exportRectangle:(VRectangle*)g];
571                    else if (  [g isKindOfClass:[VArc class]] )
572                        [self exportArc:(VArc*)g];
573                    else if (  [g isKindOfClass:[VCurve class]] )
574                        [self exportCurve:(VCurve*)g];
575                    else if (  [g isKindOfClass:[VPath class]] )
576                        NSLog(@"GerberExportSub.m: exportToFile, exportPath\n");/*[self exportPath:(VPath*)g];*/
577                              else if (  [g isKindOfClass:[VGroup class]] )
578                              [self exportGroup:(VGroup*)g];
579                              else if (  [g isKindOfClass:[VText class]] )
580                              [self exportText:(VText*)g];
581                }
582            }
583        }
584        [gList removeAllObjects];
585    }
586    savedOk = [self saveToFile:filename];
587
588    /* restore backup */
589    if (!savedOk)
590    {
591        [fileManager removeFileAtPath:filename handler:nil];	// remove what we just started to write
592        [fileManager movePath:backupFilename toPath:filename handler:nil];	// restore backup
593        NSRunAlertPanel(SAVE_TITLE, CANT_SAVE, nil, nil, nil);
594    }
595    else
596    {
597        if (Prefs_RemoveBackups)
598            [fileManager removeFileAtPath:backupFilename handler:nil];
599    }
600    return YES;
601}
602
603- (void)dealloc
604{
605    [super dealloc];
606}
607
608@end
609