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:¢er 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