1/* DINImport.m
2 * DIN import object (Drill data import)
3 *
4 * Copyright (C) 1996-2012 by vhf interservice GmbH
5 * Author:   Ilonka Fleischmann
6 *
7 * created:  1996-05-03
8 * modified: 2012-06-18 (leading zero support added (LZ), clean-up)
9 *           2006-11-04
10 *
11 * This file is part of the vhf Import 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 <math.h>
38
39#include "DINImport.h"
40#include "../VHFShared/vhfCFGFunctions.h"
41#include "../VHFShared/types.h"
42#include "../VHFShared/VHFStringAdditions.h"
43#include "../VHFShared/vhf2DFunctions.h"
44
45/* r in points/inch */
46#define	InternalToDeviceRes(a, r)	((float)(a) * (float)(r) / 72.0)
47#define	DeviceResToInternal(a, r)	((float)(a) * 72.0 / (float)(r))
48
49/* the following characters may apear as digits in a coordinate */
50#define DIGITS @".+-0123456789"
51
52
53@interface DINImport(PrivateMethods)
54- importDIN:(NSData*)DINData;
55- (BOOL)checkFileFormat:(NSString*)dinStr;
56- (BOOL)loadApertures:(NSString*)toolStr;
57- (void)loadSM1000Apertures:(NSScanner*)scanner;
58- (void)loadSM3000Apertures:(NSScanner*)scanner;
59- (void)loadExcellonApertures:(NSScanner*)scanner;
60- (BOOL)interpret:(NSString*)dataP;
61- (BOOL)getGraphicFromData:(NSScanner*)scanner :cList;
62- (BOOL)getToolFromData:(NSScanner*)scanner :(NSString*)macroData :(NSString**)code :(float*)w :(float*)h :(NSString**)formCode :(NSString**)macroStr;
63- (BOOL)getTool:(NSScanner*)scanner;
64- (BOOL)getPlotAbs:(NSScanner*)scanner;
65- (BOOL)getPlotRel:(NSScanner*)scanner;
66- (void)setArc:cList;
67- (void)setLine:cList;
68- (void)setMark:cList;
69- (void)updateBounds:(NSPoint)p;
70@end
71
72@implementation DINImport
73
74- init
75{
76    self = [super init];
77
78    [self setDefaultParameter];
79    parameterLoaded = 0;
80    fileFormat = 0;
81
82    res = 1000.0;
83    tz = YES;       // trailing zeros
84
85    state.inch = 1; // inch
86    state.pa   = 1;
87
88    state.tool = 0;
89    state.point.x = state.point.y = LARGENEG_COORD;
90    state.g = 0;
91    state.x = state.y = 0.0;
92    state.a = state.c = 0.0;
93    state.path = 0;
94    state.offset = 0;
95    return self;
96}
97
98- (void)setDefaultParameter
99{
100    switch (fileFormat)
101    {
102        case DIN_SM3000:
103            ops.start = @"%%3000";
104            res = 25400.0;
105            ops.prgend = @"$";
106            ops.coordC = @"D";  // diameter of tool
107            break;
108        case DIN_SM1000:
109            ops.start = @"%%1000";
110            res = 25400.0;
111            ops.prgend = @"$";
112            ops.coordC = @"";   // diameter of tool
113            break;
114        default:
115            ops.start = @"%";
116            res = 10000.0;
117            ops.prgend = @"M30";
118            ops.coordC = @"C";  // diameter of tool
119    }
120    resMM = 25400.0;
121    tz    = YES;                // trailing zeros
122    ops.offset      = @"M50";
123    ops.mm          = @"M71";
124    ops.reset       = @"";
125    ops.selectTool  = @"T";
126    ops.coordX      = @"X";
127    ops.coordY      = @"Y";
128    ops.coordR      = @"R";     // radius of circle
129    ops.plotAbs     = @"G90";
130    ops.plotRel     = @"G91";
131    ops.termi       = @"\n";
132    //ops.polyBegin = @"G41";   // left
133    //ops.polyBegin2 = @"G42";  // right
134    //ops.polyEnd = @"G40";
135    ops.comment     = @"/";
136    ops.line        = @"G1";    // G01
137    ops.circleCW    = @"G2";    // G02
138    ops.circleCCW   = @"G3";    // G03
139    ops.drill       = @"G05";
140}
141
142- (BOOL)checkFileFormat:(NSString*)dinStr
143{   NSScanner   *scanner = [NSScanner scannerWithString:dinStr];
144    int         location;
145
146    location = [scanner scanLocation];
147    [scanner scanUpToString:@"%%1000" intoString:NULL];
148    if ( [scanner scanString:@"%%1000" intoString:NULL] )
149    {   fileFormat = DIN_SM1000;
150        return YES;
151    }
152    [scanner setScanLocation:location];
153    [scanner scanUpToString:@"%%3000" intoString:NULL];
154    if ( [scanner scanString:@"%%3000" intoString:NULL] )
155    {   fileFormat = DIN_SM3000;
156        return YES;
157    }
158    fileFormat = DIN_EXCELLON;
159    return YES;
160/*
161    [scanner setScanLocation:location];
162    [scanner scanUpToString:@"G05" intoString:NULL]; // 2
163    if ( [scanner scanString:@"G05" intoString:NULL] )
164    {   fileFormat = DIN_EXCELLON;
165        return YES;
166    }
167    [scanner setScanLocation:location];
168    [scanner scanUpToString:@"G81" intoString:NULL]; // 1
169    if ( [scanner scanString:@"G81" intoString:NULL] )
170    {   fileFormat = DIN_EXCELLON;
171        return YES;
172    }
173    [scanner setScanLocation:location];
174    [scanner scanUpToString:@"M72" intoString:NULL];
175    if ( [scanner scanString:@"M72" intoString:NULL] )
176    {   fileFormat = DIN_EXCELLON;
177        return YES;
178    }
179    [scanner setScanLocation:location];
180    [scanner scanUpToString:@"M71" intoString:NULL];
181    if ( [scanner scanString:@"M71" intoString:NULL] )
182    {   fileFormat = DIN_EXCELLON;
183        return YES;
184    }
185    [scanner setScanLocation:location];
186    [scanner scanUpToString:@"INCH" intoString:NULL];
187    if ( [scanner scanString:@"INCH" intoString:NULL] )
188    {   fileFormat = DIN_EXCELLON;
189        return YES;
190    }
191
192    [scanner setScanLocation:location];
193    [scanner scanUpToString:@"METRIC" intoString:NULL];
194    if ( [scanner scanString:@"METRIC" intoString:NULL] )
195    {   fileFormat = DIN_EXCELLON;
196        return YES;
197    }
198    return NO;
199*/
200}
201
202/* created:   03.05.96
203 * modified:  07.03.97
204 * parameter: fileName
205 * purpose:   load parameter file
206 */
207- (BOOL)loadParameter:(NSString*)fileName
208{   NSMutableString	*parmData;
209    NSString		*val;
210
211    [self setDefaultParameter];
212
213    if ( !(parmData = [NSMutableString stringWithContentsOfFile:fileName]) )
214        return NO;
215
216    parameterLoaded = 1;
217
218    if ( 1 )// fileFormat == DIN_EXCELLON
219    {   vhfGetTypesFromData(parmData, @"f", @"#RMM", &resMM);
220        ops.comment = vhfGetStringFromData(parmData, @"#REM");
221        ops.mm = vhfGetStringFromData(parmData, @"#UMM");
222    }
223    vhfGetTypesFromData(parmData, @"f", @"#RES", &res);
224
225    ops.init = vhfGetStringFromData(parmData, @"#INI");
226    ops.reset = vhfGetStringFromData(parmData, @"#RST");
227    ops.selectTool = vhfGetStringFromData(parmData, @"#ITL");
228    if ( (val = vhfGetStringFromData(parmData, @"#IST")) )
229        ops.start = val;
230    ops.coordX = vhfGetStringFromData(parmData, @"#IXP");
231    ops.coordY = vhfGetStringFromData(parmData, @"#IYP");
232    ops.coordC = vhfGetStringFromData(parmData, @"#IDM");
233    //ops.coordI = vhfGetStringFromData(parmData, @"#IIP");
234    //ops.coordJ = vhfGetStringFromData(parmData, @"#IJP");
235    //ops.circle = vhfGetStringFromData(parmData, @"#ICI");
236    //ops.arc = vhfGetStringFromData(parmData, @"#IAR");
237    ops.termi = vhfGetStringFromData(parmData, @"#ITM");
238
239    return YES;
240}
241
242/*
243 * modified:  03.05.93 05.05.96 19.09.96 07.03.97
244 * purpose:   load tools from aperture table
245 * parameter: filename
246 *
247 * "code" = "D10"
248 * "formCode" = "C", "O" or "R"
249 * "typeCode" = "L", "P", or "A"
250 * "width" = width of tool
251 * "height" = height od tool
252 */
253- (BOOL)loadApertures:(NSString*)toolStr
254{   NSScanner		*scanner = [NSScanner scannerWithString:toolStr];
255
256    if ( !scanner )
257        return NO;
258
259    // NSScanner else jump over \n (default)
260    [scanner setCharactersToBeSkipped:[NSCharacterSet whitespaceCharacterSet]];
261
262    [tools release];
263    tools = [[NSMutableArray array] retain];
264
265    switch (fileFormat)
266    {
267        case DIN_SM1000:	[self loadSM1000Apertures:scanner]; break;
268        case DIN_SM3000:	[self loadSM3000Apertures:scanner]; break;
269        case DIN_EXCELLON:	[self loadExcellonApertures:scanner]; break;
270        default: NSLog(@"DINImport: loadApertures no valid fileFormat.");
271    }
272    return YES;
273}
274
275- (void)loadSM1000Apertures:(NSScanner*)scanner
276{   NSString    *parameter = @"$";
277    int         i, value, cnt = 1;
278
279    // $80$50$20$99$15$500 6 times $ at end
280
281    while (![scanner isAtEnd])
282    {
283        for (i=1; i<7; i++) // six times
284        {
285            [scanner scanUpToString:parameter intoString:NULL];
286            if ( [scanner scanString:parameter intoString:NULL] && i == 1 ) // first of six values is diameter
287            {   NSMutableDictionary	*dict = [NSMutableDictionary dictionary];
288                float	dia;
289
290                if ( ![scanner scanInt:&value] )
291                    return; // last $
292                dia = DeviceResToInternal(value, res); // in same mannor like coordinates
293                if ( !dia )
294                    return; // last - format says 15 tools must be defined - we assume 0 is last
295                [dict setObject:[NSString stringWithFormat:@"T%d", cnt] forKey:@"code"];
296                [dict setObject:[NSNumber numberWithFloat:dia] forKey:@"diameter"];
297                [tools addObject:dict];
298                cnt++;
299            }
300        }
301    }
302}
303
304- (void)loadSM3000Apertures:(NSScanner*)scanner
305{   NSString    *parameter = @"$", *parmDia = @"D", *codeStr;
306    int         i, value, location = [scanner scanLocation];
307    float       dia;
308
309    // $
310    // T1D80S50F20R99N15A500T2D50...
311
312    [scanner scanUpToString:parameter intoString:NULL];
313    if ( ![scanner scanString:parameter intoString:NULL] )
314        return; // no tools
315
316    while (![scanner isAtEnd])
317    {
318        [scanner scanUpToString:ops.selectTool intoString:NULL];
319        if ( [scanner scanString:ops.selectTool intoString:NULL] )
320        {   NSMutableDictionary	*dict = [NSMutableDictionary dictionary];
321
322            if ( ![scanner scanInt:&value] )
323                return;
324            codeStr = [NSString stringWithFormat:@"T%d", value];
325            if ( [scanner scanString:parmDia intoString:NULL] )
326            {   if ( ![scanner scanInt:&value] )
327                    return;
328            }
329            dia = DeviceResToInternal(value, res); // in same mannor like coordinates
330            if ( !dia )
331                return; // we assume 0 is last
332            [dict setObject:codeStr forKey:@"code"];
333            [dict setObject:[NSNumber numberWithFloat:dia] forKey:@"diameter"];
334            [tools addObject:dict];
335        }
336        else
337            break; // no more tools
338    }
339    // if X|Y values are realy floatValues -> 01.123 -> -> res = 1.0 !!!!!!!!!!!!!
340    [scanner setScanLocation:location];
341    for (i=0; i<3 && ![scanner isAtEnd]; i++)
342    {   [scanner scanUpToString:ops.coordX intoString:NULL];
343        if ( [scanner scanString:ops.coordX intoString:NULL] )
344        {   NSString	*str;
345            NSCharacterSet	*stopSet = [NSCharacterSet characterSetWithCharactersInString:@"\nY"];
346
347            if ( [scanner scanUpToCharactersFromSet:stopSet intoString:&str] )
348            {   NSRange	range = [str rangeOfString:@"."];
349                state.inch = 0;
350                if ( range.length && state.inch )
351                    res = 1.0;
352                else if ( range.length && !state.inch )
353                    res = 25.4;
354                return;
355            }
356        }
357    }
358}
359
360- (void)loadExcellonApertures:(NSScanner*)scanner
361{   NSString        *parmDia = @"C", *codeStr;
362    int             value, location = [scanner scanLocation], inchLoc=0, mmLoc=0;
363    float           dia;
364    NSCharacterSet  *stopSet = [NSCharacterSet characterSetWithCharactersInString:@"TC"];
365
366    // get state.inch, state.pa and correct perhaps res
367
368    [scanner scanUpToString:@"LZ" intoString:NULL];     // leading zeros
369    if ( [scanner scanString:@"LZ" intoString:NULL] )
370        tz = NO;    // leading zeros
371    // TODO: there is also "TZ" = trailing zeros, and "FMAT,2"
372
373    [scanner setScanLocation:location];
374    [scanner scanUpToString:@"G91" intoString:NULL];    // relative
375    if ( [scanner scanString:@"G91" intoString:NULL] )
376        state.pa = 0;
377
378    [scanner setScanLocation:location];
379    [scanner scanUpToString:@"INCH" intoString:NULL];   // inch
380    if ( [scanner scanString:@"INCH" intoString:NULL] )
381        inchLoc = [scanner scanLocation];
382    [scanner setScanLocation:location];
383    [scanner scanUpToString:@"M72" intoString:NULL];    // inch
384    if ( [scanner scanString:@"M72" intoString:NULL] )
385    {   if ( !inchLoc || inchLoc > (int)[scanner scanLocation] )
386            inchLoc = [scanner scanLocation];
387    }
388    [scanner setScanLocation:location];
389    [scanner scanUpToString:@"M71" intoString:NULL];    // mm
390    if ( [scanner scanString:@"M71" intoString:NULL] )
391        mmLoc = [scanner scanLocation];
392    [scanner setScanLocation:location];
393    [scanner scanUpToString:@"METRIC" intoString:NULL]; // mm
394    if ( [scanner scanString:@"METRIC" intoString:NULL] )
395    {   if ( !mmLoc || mmLoc > (int)[scanner scanLocation] )
396            mmLoc = [scanner scanLocation];
397    }
398    [scanner setScanLocation:location];
399    [scanner scanUpToString:ops.mm intoString:NULL];    // mm (from device file)
400    if ( [scanner scanString:ops.mm intoString:NULL] )
401    {   if ( !mmLoc || mmLoc > (int)[scanner scanLocation] )
402            mmLoc = [scanner scanLocation];
403    }
404    state.inch = ( (mmLoc && inchLoc && mmLoc < inchLoc) || (mmLoc && !inchLoc) ) ? 0 : 1; // mm is first
405
406    // T1 C.04 F200 S65
407    // T2 C.05 F200 S65
408    // % // prg start
409
410    // T1 C.04
411    // X123Y234...
412
413    // T1 no C follows is possible -> than C values must be befor % (start of prg)
414    // else no tools are declared
415    [scanner setScanLocation:location];
416    while (![scanner isAtEnd])
417    {
418        [scanner scanUpToCharactersFromSet:stopSet intoString:NULL];
419
420        if ( [scanner scanString:ops.selectTool intoString:NULL] ) // T
421        {   NSMutableDictionary	*dict = [NSMutableDictionary dictionary];
422
423            if (![scanner scanInt:&value])
424                continue;
425            codeStr = [NSString stringWithFormat:@"T%d", value];
426
427            [scanner scanUpToCharactersFromSet:stopSet intoString:NULL];
428            if (![scanner scanString:parmDia intoString:NULL]) // C
429                continue; // METRIC or something ? break; // no C follows -> end of tool init
430            if (![scanner scanFloat:&dia])
431                continue;
432            if (!state.inch)
433                dia /= 25.4; // mm/ 25.4 -> inch
434            dia *= INCH; // diameter is spezified with dot
435            if ( !dia )
436            {   state.inch = ( mmLoc > inchLoc ) ? 0 : 1;
437                break; // we assume 0 is last
438            }
439            [dict setObject:codeStr forKey:@"code"];
440            [dict setObject:[NSNumber numberWithFloat:dia] forKey:@"diameter"];
441            [tools addObject:dict];
442        }
443        else if (![scanner isAtEnd])
444            [scanner setScanLocation:[scanner scanLocation]+1]; // break; // no more tools
445        else break;
446    }
447    state.inch = ( mmLoc > inchLoc ) ? 0 : 1;
448
449    if ( !state.inch )
450        res = resMM;
451
452    // if X|Y values are realy floatValues -> 01.123 -> -> res = 1.0 !!!!!!!!!!!!!
453    [scanner setScanLocation:location];
454    [scanner scanUpToString:ops.coordX intoString:NULL];
455    if ( [scanner scanString:ops.coordX intoString:NULL] )
456    {   NSString	*str;
457        NSCharacterSet	*stopSet = [NSCharacterSet characterSetWithCharactersInString:@"\nY"];
458
459        if ( [scanner scanUpToCharactersFromSet:stopSet intoString:&str] )
460        {   NSRange	range = [str rangeOfString:@"."];
461
462            if ( range.length && state.inch )
463                res = 1.0;
464            else if ( range.length && !state.inch )
465                res = 25.4;
466        }
467    }
468}
469
470/* created:   2001-01-27
471 * modified:  2002-10-26
472 * parameter: DINData	the DIN data stream
473 * purpose:   start interpretation of the contents of DINData
474 */
475- importDIN:(NSData*)DINData
476{   NSString	*dinStr = [[[NSString alloc] initWithData:DINData
477                                                 encoding:NSASCIIStringEncoding] autorelease];
478
479    state.color = [NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0];
480    state.width = 0.0;
481
482    [self checkFileFormat:dinStr];
483
484    if ( !parameterLoaded )
485        [self setDefaultParameter];
486
487    [self loadApertures:dinStr];
488
489    if ( ![tools count] )
490        NSLog(@"No tools loaded !");
491
492    /* interpret data
493     */
494    if ( ![self interpret:dinStr] )
495        return 0;
496
497    return [list autorelease];
498}
499
500/* private methods
501 */
502- (BOOL)interpret:(NSString*)dataP
503{   int         startLocation;
504    NSRect      bounds;
505    NSScanner   *scanner = [NSScanner scannerWithString:dataP];
506
507    digitsSet = [NSCharacterSet characterSetWithCharactersInString:DIGITS];
508    invDigitsSet = [digitsSet invertedSet];
509    // NSScanner else jump over \n (default)
510    [scanner setCharactersToBeSkipped:[NSCharacterSet whitespaceCharacterSet]];
511
512    startLocation = [scanner scanLocation];
513    /* init bounds */
514    ll.x = ll.y = LARGE_COORD;
515    ur.x = ur.y = LARGENEG_COORD;
516
517    list = [self allocateList];
518
519    // set scanner to start of programm (%%1000 or %%3000)
520    // if ( fileFormat != DIN_EXCELLON )
521    {
522        [scanner scanUpToString:ops.start intoString:NULL];
523        if ( ![scanner scanString:ops.start intoString:NULL] )
524        {
525            if ( fileFormat == DIN_EXCELLON )
526                [scanner setScanLocation:startLocation];
527            else
528            {   NSLog(@"DINImport: No start sign (%@) found", ops.start);
529                return NO;
530            }
531        }
532    }
533
534    while ( ![scanner isAtEnd] )
535        if ( ![self getGraphicFromData:scanner :list] )
536            break;
537
538    bounds.origin      = ll;
539    bounds.size.width  = ur.x - ll.x;
540    bounds.size.height = ur.y - ll.y;
541
542    [self setBounds:bounds];
543    return YES;
544}
545
546/* we need cp on a number !
547 */
548- (BOOL)getGraphicFromData:(NSScanner*)scanner :cList
549{   int         location;
550    float       value;
551    NSString    *str = nil;
552
553    location = [scanner scanLocation];
554
555    if ( [scanner scanString:ops.termi intoString:NULL] ) // else we jump over \n
556    {
557        if ( state.draw )
558        {
559            switch (state.g)
560            {
561                case DIN_LINE:
562                    [self setLine:cList];
563                    break;
564                case DIN_ARC:
565                    [self setArc:cList];
566                    break;
567                /*case DIN_PATH:
568                    [self setPath:scanner :cList];
569                    break;*/
570                default:
571                    [self setMark:cList];
572            }
573            if (!state.offset)
574            {   state.point.x = state.x;
575                state.point.y = state.y;
576            }
577            else state.offset = 0;
578            state.draw = 0;
579        }
580    }
581    else if ( [scanner scanString:ops.comment intoString:NULL] )
582    {   [scanner scanUpToString:ops.termi intoString:NULL];
583        [scanner scanString:ops.termi intoString:NULL];
584    }
585    else if ( [scanner scanString:ops.offset intoString:NULL] ) // relativ M50
586        state.offset = 1;
587    //else if ( [scanner scanString:ops.drill intoString:NULL] )
588    //    state.g = 0;
589    else if ( [scanner scanString:ops.line intoString:NULL] )
590        state.g = DIN_LINE;
591    else if ( [scanner scanString:ops.circleCW intoString:NULL] )
592    {   state.g = DIN_ARC;
593        state.ccw = 0;
594    }
595    else if ( [scanner scanString:ops.circleCCW intoString:NULL] )
596    {   state.g = DIN_ARC;
597        state.ccw = 1;
598    }
599    /*else if ( [scanner scanString:ops.polyBegin intoString:NULL] || [scanner scanString:ops.polyBegin2 intoString:NULL] )
600    {   state.g = DIN_PATH;
601        state.path = 1;
602    }
603    else if ( [scanner scanString:ops.polyEnd intoString:NULL] )
604    {   state.g = 0;
605        state.path = 0;
606    }*/
607    else if ( [scanner scanString:ops.coordX intoString:NULL] )
608    {
609        if ( [scanner scanCharactersFromSet:digitsSet intoString:&str] )
610        //if ( [scanner scanFloat:&value] )
611        {   //state.point.x = state.x;
612            value = [str floatValue];
613            if ( ! tz ) // leading zeros
614                value *= pow(10, 7 - [str length]);
615            state.x = DeviceResToInternal(value, res);
616            state.draw = 1;
617        }
618        else
619        {   state.x = 0;
620//            NSLog(@"Coordinate X expected at location: %d", [scanner scanLocation]);
621        }
622    }
623    else if ( [scanner scanString:ops.coordY intoString:NULL] )
624    {
625        if ( [scanner scanCharactersFromSet:digitsSet intoString:&str] )
626        //if ( [scanner scanFloat:&value] )
627        {   //state.point.y = state.y;
628            value = [str floatValue];
629            if ( ! tz ) // leading zeros
630                value *= pow(10, 7 - [str length]);
631            state.y = DeviceResToInternal(value, res);
632            state.draw = 1;
633        }
634        else
635        {   state.y = 0;
636//            NSLog(@"Coordinate Y expected at location: %d", [scanner scanLocation]);
637        }
638    }
639    else if ( [scanner scanString:ops.coordR intoString:NULL] )
640    {
641        if ( [scanner scanCharactersFromSet:digitsSet intoString:&str] )
642        //if ( [scanner scanFloat:&value] )
643        {   value = [str floatValue];
644            if ( ! tz ) // leading zeros
645                value *= pow(10, 7 - [str length]);
646            state.a = DeviceResToInternal(value, res);
647            state.draw = 1;
648        }
649        else
650            NSLog(@"Coordinate R expected at location: %d", [scanner scanLocation]);
651    }
652    else if ( [scanner scanString:ops.selectTool intoString:NULL] )
653//        [self getTool:scanner];
654    {   if (![self getTool:scanner])
655        {   // move
656            state.point.x = state.x;
657            state.point.y = state.y;
658//            state.draw = 0;
659        }
660
661    }
662    else if ( [scanner scanString:ops.plotAbs intoString:NULL] )
663        [self getPlotAbs:scanner];
664    else if ( [scanner scanString:ops.plotRel intoString:NULL] )
665        [self getPlotRel:scanner];
666    else if ( [scanner scanString:ops.prgend intoString:NULL] )
667        return NO;
668    else
669    {   // we cant step until termi cause we run over next statement if blank or \n follows !
670        if ( [scanner isAtEnd] )
671            return NO;
672        [scanner setScanLocation:location+1];
673        return YES;
674    }
675    return YES;
676}
677
678/*
679 * created:      02.05.93
680 * modified:     02.05.93 19.07.93 05.05.96 07.03.97
681 * purpose:      return tool number of specified tool (cp = "D10* ...")
682 * parameter:    scanner
683 * return value: index of tool
684 */
685- (int)toolFromString:(NSScanner*)scanner
686{   int		i, d, cnt = [tools count];
687
688    if (![scanner scanInt:&d])
689        return -2; // move - we need current tool
690    for (i=0; i<cnt; i++)
691    {	NSString	*code = [[tools objectAtIndex:i] objectForKey:@"code"];
692        int		d1 = [[code substringFromIndex:[code rangeOfCharacterFromSet:digitsSet].location] intValue];
693
694        if ( d==d1 )
695            return i;
696    }
697    return -1;
698}
699
700/*
701 * created:   xx.12.92
702 * modified:  02.05.93 19.07.93 05.05.96 08.03.97
703 * purpose:   get tool
704 * parameter: scanner
705 *            state
706 *            parms
707 */
708- (BOOL)getTool:(NSScanner*)scanner
709{   int	val;
710
711    if ( (val = [self toolFromString:scanner]) < -1 )
712         return NO;
713    else if ( val < 0 )
714    {
715        NSLog(@"Gerber import, Can't find tool at location %d. Default used.", [scanner scanLocation]);
716        state.tool = state.tool + 1; //  = 0;
717        return NO;
718    }
719    state.tool = val;
720    return YES;
721}
722
723/*
724 * modified:  16.12.92
725 * purpose:   get status plot absolut
726 * parameter: cp
727 * return:    cp
728 */
729- (BOOL)getPlotAbs:(NSScanner*)scanner
730{
731    state.pa = 1;
732    return YES;
733}
734
735/*
736 * modified:  16.12.92
737 * purpose:   get status plot relativ
738 * parameter: cp
739 */
740- (BOOL)getPlotRel:(NSScanner*)scanner
741{
742    state.pa = 0;
743    return YES;
744}
745
746/*
747 * created:   07.05.93
748 * modified:  17.06.93 05.05.96
749 * purpose:   set arc ccw
750 * parameter: g
751 */
752- (void)setArc:cList
753{   NSPoint		center, start, end;
754    float		angle; // ba, ea
755    NSDictionary	*tool = [tools objectAtIndex:state.tool];
756
757    if ( !state.path )
758        state.width = (tool) ? [[tool objectForKey:@"diameter"] floatValue] : 0.0;
759
760/*    if (state.i || state.j)
761    {
762        // center
763        center.x = state.point.x + state.i;
764        center.y = state.point.y + state.j;
765
766        start = state.point;
767
768        end.x = state.x;
769        end.y = state.y;
770
771        // end angle
772        ba = calcAngleOfPointRelativeCenter(start, center);
773        ea = calcAngleOfPointRelativeCenter(end, center);
774        if ( ea <= ba+TOLERANCE ) ea += 360.0;
775
776        if ( !state.ccw )
777            angle = - (360.0 - (ea-ba));
778        else
779            angle = ea - ba;
780
781        if ( state.ipolFull && Diff(angle, 360.0) <= TOLERANCE )
782        {
783            if ( state.path )
784                [self addCircle:center :sqrt(SqrDistPoints(start, center)) filled:YES toList:cList];
785            else
786                [self addCircle:center :sqrt(SqrDistPoints(start, center)) filled:NO toList:cList];
787        }
788        else
789        {
790            if ( !state.ipolFull) // 90 degree interpolation - both angles must be inside one quarter !
791            {
792                if ( ea <= ba+TOLERANCE )
793                    return;
794                if ( (ba >= 0 && ba < 90) && (ea < 0 || ea > 90) )
795                    return;
796                if ( (ba >= 90 && ba < 180) && (ea < 90 || ea > 180) )
797                    return;
798                if ( (ba >= 180 && ba < 270) && (ea < 180 || ea > 270) )
799                    return;
800                if ( (ba >= 270 && ba < 360) && (ea < 270 || ea > 360) )
801                    return;
802            }
803            [self addArc:center :start :angle toList:cList];
804        }
805        state.point = end;	// end point of arc
806        state.i = state.j = 0; // important if i or j not set -> value is 0
807    }
808    else
809*/
810    {   float	b, radius = state.a;
811        NSPoint	grad;
812
813        start = state.point;
814        end.x = state.x;
815        end.y = state.y;
816        grad.x = end.x - start.x;
817        grad.y = end.y - start.y;
818        if (SqrDistPoints(start, end) > (radius*radius)*2.0)
819        {
820            angle = 180.0;
821            if ( !(b = sqrt(grad.x*grad.x+grad.y*grad.y)) )
822            {   LineMiddlePoint(start, end, center); }
823            else
824            {   center.x = start.x + grad.y*radius*1.0/b;
825                center.y = start.y + grad.x*radius*1.0/b;
826            }
827            [self addArc:center :start :angle toList:cList];
828        }
829        else if (state.a > 0) // angle smaller or equal 180 degree
830        {   NSPoint	mp;
831            float	hight;
832
833            if ( !(b = sqrt(grad.x*grad.x+grad.y*grad.y)) )
834                return;
835            LineMiddlePoint(start, end, mp);
836            hight = sqrt(radius*radius - SqrDistPoints(mp, start)); // pythagoras
837
838            // (!state.ccw) -> orthogonal to the right !
839            center.x = (!state.ccw) ? (mp.x + grad.y*hight*1.0/b) : (mp.x + grad.y*hight*-1.0/b);
840            center.y = (!state.ccw) ? (mp.y - grad.x*hight*1.0/b) : (mp.y - grad.x*hight*-1.0/b);
841
842            angle = vhfAngleBetweenPoints(start, center, end);	/* ccw */
843            if (!state.ccw)
844                angle = -(360.0 - angle);
845            [self addArc:center :start :angle toList:cList];
846        }
847        else // angle greater 180 degree
848        {   NSPoint	mp;
849            float	hight;
850
851            radius = -radius;
852            if ( !(b = sqrt(grad.x*grad.x+grad.y*grad.y)) )
853                return;
854            LineMiddlePoint(start, end, mp);
855            hight = sqrt(radius*radius - SqrDistPoints(mp, start)); // pythagoras
856
857            // (!state.ccw) -> orthogonal to the left !
858            center.x = (!state.ccw) ? (mp.x + grad.y*hight*-1.0/b) : (mp.x + grad.y*hight*1.0/b);
859            center.y = (!state.ccw) ? (mp.y - grad.x*hight*-1.0/b) : (mp.y - grad.x*hight*1.0/b);
860
861            angle = vhfAngleBetweenPoints(start, center, end);	/* ccw */
862            if (!state.ccw)
863                angle = -(360.0 - angle);
864            [self addArc:center :start :angle toList:cList];
865        }
866    }
867    [self updateBounds:center];
868    [self updateBounds:start];
869    [self updateBounds:end];
870}
871
872/*
873 * modified:  17.06.93 12.06.95
874 * purpose:   get line
875 * parameter: g
876 *		state
877 *		parms
878 */
879- (void)setLine:cList
880{   float		x, y;
881    NSPoint		p0, p1;
882    NSDictionary	*tool; // = [tools objectAtIndex:state.tool];
883
884    tool = ([tools count]) ? [tools objectAtIndex:state.tool] : nil;
885
886    x = state.x;
887    y = state.y;
888
889    if ( !state.path )
890        state.width = (tool) ? [[tool objectForKey:@"diameter"] floatValue] : 0.0;
891    p0 = state.point;
892
893    if (state.pa)
894    {	p1.x = x;
895        p1.y = y;
896    }
897    else
898    {	p1.x = state.point.x + x;
899        p1.y = state.point.y + y;
900    }
901
902    [self addLine:p0 :p1 toList:cList];
903
904    state.point = p1;
905
906    [self updateBounds:p0];
907    [self updateBounds:p1];
908}
909
910- (void)setMark:cList
911{   NSPoint         center, pll, pur;
912    NSDictionary    *tool; // = [tools objectAtIndex:state.tool];
913
914    tool = ([tools count]) ? [tools objectAtIndex:state.tool] : nil;
915
916    state.g = 0; // else we go on with an arc
917
918    // state.width = [[tool objectForKey:@"diameter"] floatValue];
919    state.width = (tool) ? [[tool objectForKey:@"diameter"] floatValue] : state.tool;
920
921    /* center */
922    if (state.offset)
923    {   center.x = state.point.x + state.x; // !state.x && !state.y -> return
924        center.y = state.point.y + state.y;
925    }
926    else
927
928    {   center.x = state.x;
929        center.y = state.y;
930    }
931    [self addMark:center withDiameter:state.width toList:cList];
932
933    pll.x = center.x - state.a; pll.y = center.y - state.a;
934    pur.x = center.x + state.a; pur.y = center.y + state.a;
935    [self updateBounds:pll];
936    [self updateBounds:pur];
937}
938
939- (void)updateBounds:(NSPoint)p
940{
941    ll.x = Min(ll.x, p.x);
942    ll.y = Min(ll.y, p.y);
943    ur.x = Max(ur.x, p.x);
944    ur.y = Max(ur.y, p.y);
945}
946
947- (void)dealloc
948{
949    [super dealloc];
950}
951
952/* methods to be subclassed
953 */
954- (id)allocateList
955{
956    return nil;
957}
958
959- (void)addMark:(NSPoint)pt withDiameter:(float)dia toList:aList;
960{
961    NSLog(@"addMark: %f %f %f", pt.x, pt.y, dia);
962}
963
964- (void)addLine:(NSPoint)beg :(NSPoint)end toList:aList
965{
966    NSLog(@"line: %f %f %f %f", beg.x, beg.y, end.x, end.y);
967}
968
969- (void)addCircle:(NSPoint)center :(float)radius filled:(BOOL)fill toList:aList
970{
971    NSLog(@"circle: %f %f %f %d", center.x, center.y, radius, fill);
972}
973
974- (void)addArc:(NSPoint)center :(NSPoint)start :(float)angle toList:aList
975{
976    NSLog(@"arc: %f %f %f %f %f", center.x, center.y, start.x, start.y, angle);
977}
978
979- (void)addFillList:aList toList:bList
980{
981    NSLog(@"filled path.");
982}
983
984- (void)setBounds:(NSRect)bounds
985{
986    NSLog(@"bounds: %f %f %f %f", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
987}
988@end
989