-
Notifications
You must be signed in to change notification settings - Fork 22
/
svg2poly.ulp
622 lines (535 loc) · 15.1 KB
/
svg2poly.ulp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
/*
* svg2poly.ulp
* Jan 2013, By Cruz Monrreal II ([email protected])
*
* Imports Plain .svg file and will draw it as a set of polygons
* For best results, follow GitHub instructions as closely as
* possible.
*
*
* NOTE: This is not a universal .svg converter. The spec is
* simply too large, and the .ulp language is not robust
* enough to create an entire .svg parser.
*
*/
#usage "<b>Import a Plain SVG file to a Polygon</b>\n"
"<p><b>NOTE:</b> Polygons will automatically be centered about the current 'mark'</p>"
"<p>Usage: run svg2poly [ -ratio <i>num</i> ] [ -layer <i>name</i> ] [ -outline <i>thickness</i> ]"
"<p>Options:<br>"
"<table>"
"<tr><td>-ratio <i>num</i></td><td>scale SVG with the given ratio, default 1</td></tr>"
"<tr><td>-layer <i>name</i></td><td>change layer that SVG is drawn on, default tDocu</td></tr>"
"<tr><td>-outline <i>thickness</i> </td><td>draw an outline of the given size, default 0</td></tr>"
"</table>"
"<p><p><b>Example:</b>"
"<p>run svg2poly -ratio 2 -layer tDocu"
"<p>This will import a Plain SVG file, scale it 2x, and draw it on the tDocu layer with no outline"
"<p><p>"
"<small><table>"
"<tr><td><i>Cruz Monrreal</i></td></tr>"
"<tr><td><i>[email protected]</i></td></tr>"
"<tr><td><i>http://anomalousmaker.com</i></td></tr>"
"</table></small>"
/* Inputs, Defaults */
string layer = "tNames";
real outline = 0.0005;
real ratioX = 0.01, ratioY = 0.01;
/* Globals */
string tmp[], master_xml;
int poly_start[], global_pts=0, num_polys=0;
real pts_x[], pts_y[];
real tmp_coords[];
real scaleFactor = 1.0; // SVG defined
real SvgTranslationX = 0.0; // SVG defined
real SvgTranslationY = 0.0; // SVG defined
string file; // filename of SVG
/* Output */
string cmd;
/*
* Stack imlemented to get around lack of recursion
*/
string stack = "";
/*** Stack Helper Functions ***/
void push(string s){ stack = (stack == "") ? s : stack + "|" + s; }
string pull()
{
string tmp;
if (stack == "")
// Nothing in stack
return "";
else if (strrstr(stack, "|") == -1){
// Stack only has one element
tmp = stack;
stack = "";
return tmp;
}
// Search for the last `|` in string
// Return string past last `|`
tmp = strsub(stack, strrstr(stack, "|") + 1);
stack = strsub(stack, 0, strrstr(stack, "|"));
return tmp;
}
/*
* Encodes all necessary information into a single string
* for stack-based recursion
*/
string pack(string xml, int global_pts)
{
string tmp;
sprintf(tmp, "%s^%d", xml, global_pts);
return tmp;
}
/*** Functions ***/
void print(string s) {printf("%s\n", s);}
/*
* Sets up the eagle commands for the script.
*/
void setup_cmd()
{
int i;
// Search for parameters
for(i=0; i<argc; i++){
if (argv[i] == "-ratio"){ // Scale SVG in the X and Y Axis
if (i+1 < argc){ // Make sure that there's another argument
if (strstr(argv[i+1], "-") == -1){ // And that it's not a command
ratioX = ratioY = strtod(argv[i+1]);
i++;
}
}
}else if (argv[i] == "-ratiox"){ //Scale SVG in X Axis
if (i+1 < argc){
if (strstr(argv[i+1], "-") == -1) {
ratioX = strtod(argv[i+1]);
i++;
}
}
}else if (argv[i] == "-ratioy"){ // Scale SVG in Y Axis
if (i+1 < argc){
if (strstr(argv[i+1], "-") == -1){
ratioY = strtod(argv[i+1]);
i++;
}
}
}else if (argv[i] == "-layer"){ // Change layer that SVG is drawn on
if (i+1 < argc){
if (strstr(argv[i+1], "-") == -1){
layer = argv[i+1];
i++;
}
}
}else if (argv[i] == "-outline"){ // Draw an outline of a given size
if (i+1 < argc){
if (strstr(argv[i+1], "-") == -1){
outline = strtol(argv[i+1]);
i++;
}
}
}
}
// Arguments passed to replace defaults
/*switch (argc){
case 5:
outline = strtol(argv[4]);
case 4:
layer = argv[3];
case 3:
ratioX = strtod(argv[2]);
ratioY = strtod(argv[1]);
break;
case 2:
ratioX = ratioY = strtod(argv[1]);
}*/
}
// launch a dialog box, for user input
void userDialog()
{
int Result = dlgDialog ("SVG to Polygon")
{
dlgHBoxLayout
{
dlgStretch(1);
dlgLabel("Enter Parameters");
dlgStretch(1);
}
dlgHBoxLayout
{
dlgLabel("File &name:");
dlgStringEdit(file);
dlgPushButton("Bro&wse")
{
file = dlgFileOpen("Select a file", "", "*.svg");
}
}
int layerNum = 25; // default ( tNames )
dlgLabel("Choose layer for outline");
string layerSelectionString; // build entries for combobox
string layersArray[]; // array of entries in combobox
int i = 0; // counter to keep track of combobox position (layer number isn't necessarily index number)
board(B)
{
B.layers(L) // this will loop through all used layers in board
{
string tempString;
sprintf(tempString, "%3d %s,", L.number, L.name);
layerSelectionString += tempString;
// check to see if layer was already defined by commandline
if (L.name == layer)
{
layerNum = i;
}
++i;
}
}
// convert string into array
strsplit(layersArray, layerSelectionString, ',');
dlgComboBox(layersArray, layerNum)
{
string tempString[];
strsplit(tempString, layersArray[layerNum],' ');
layer = tempString[2];
// dlgMessageBox("selected layer " + layer);
}
dlgLabel("Choose a scale factor");
dlgHBoxLayout
{
dlgLabel("X");
dlgRealEdit(ratioX, 0.0, 100.0);
dlgSpacing(10);
dlgLabel("Y");
dlgRealEdit(ratioY, 0.0, 100.0);
dlgSpacing(10);
}
dlgSpacing(10);
dlgLabel("Specify outline width");
dlgHBoxLayout
{
dlgRealEdit(outline, 0.0, 100.0);
}
dlgHBoxLayout
{
dlgStretch(1);
dlgPushButton("+OK") dlgAccept();
dlgPushButton("Cancel") dlgReject();
}
};
// Write it all to the eagle command
sprintf(cmd, "set wire_bend 2;\nchange layer %s;\nset width %f;\npoly", layer, outline);
}
string matrix_to_translate(string translate){
// Might need to implement sin & cos...
return translate;
}
/*
* Converts svg formatted coordinates to usable peices
* WARNING: Function assumes string is formatted correctly
*/
int svgcoords_to_coords(string s)
{
string coords[];
strsplit(coords, s, ',');
tmp_coords[0] = strtod(coords[0]);
tmp_coords[1] = strtod(coords[1]);
/*
// Lower the point resolution
sprintf(s, "%2.2f,%2.2f", tmp_coords[0], tmp_coords[1]);
strsplit(coords, s, ',');
tmp_coords[0] = strtod(coords[0]);
tmp_coords[1] = strtod(coords[1]);
*/
return 0;
}
/*
* Extract points from a given svg path.
*/
void extract_pts(string pts, string transform)
{
// TODO: deal with "rotate" transforms
// look for a matrix transformation
int matrixPos = strrstr(transform,"matrix");
if(matrixPos >= 0)
{
// assuming that X and Y scaling are equal, and no rotation.
string matrixString = strsub(transform,matrixPos+7);
string matrixFactors[];
strsplit(matrixFactors,strsub(matrixString,0, strlen(matrixString)-1),','); // trim last char ")"
scaleFactor = strtod(matrixFactors[0]); // Note: the Y factor is at matrixFactors[3]
SvgTranslationX = strtod(matrixFactors[4]);
SvgTranslationY = strtod(matrixFactors[5]);
}
else
{
int scalePos = strrstr(transform,"scale");
if(scalePos >= 0)
{
string transformString = strsub(transform,scalePos+6);
scaleFactor = strtod(strsub(transformString,0, strlen(transformString)-1)); // trim last char ")"
}
else
{
scaleFactor = 1.0;
}
// look for SVG translation
int transPos = strrstr(transform,"translate");
if(transPos >= 0)
{
string transformString = strsub(transform,transPos+10);
string transFactors[];
strsplit(transFactors,strsub(transformString,0, strlen(transformString)-1),','); // trim last char ")"
SvgTranslationX = strtod(transFactors[0]);
SvgTranslationY = strtod(transFactors[1]);
}
else
{
SvgTranslationX = 0.0;
SvgTranslationY = 0.0;
}
}
printf("SVG scale factor = %f\n",scaleFactor);
printf("SVG offset = %f,%f\n",SvgTranslationX,SvgTranslationY);
string svg_pts[];
int size = strsplit(svg_pts, pts, ' '),
new_pts = 0;
int i = 0;
int mode = 0;
printf("POLYGON %2d)\n", num_polys);
while ((i < size ) && (svg_pts[i] != "z") && (svg_pts[i] != "Z"))
{
if (svg_pts[i] == "L" )
{
// Coords are Absolute, command is L
printf("Line (abs)");
mode = 1;
}
else if (svg_pts[i] == "l")
{
// Coords are Offset, command is l
printf("Line (rel)");
mode = 0;
}
else if (svg_pts[i] == "H")
{
// Coords are Absolute, command is H
printf("Horz (abs)");
mode = 3;
}
else if (svg_pts[i] == "h")
{
// Coords are Offset, command is h
printf("Horz (rel)");
mode = 2;
tmp_coords[0] = 0;
tmp_coords[1] = 0;
}
else if (svg_pts[i] == "V" )
{
// Coords are Absolute, command is V
printf("Vert (abs)");
mode = 5;
}
else if (svg_pts[i] == "v")
{
// Coords are Offset, command is v
printf("Vert (rel)");
mode = 4;
tmp_coords[0] = 0;
tmp_coords[1] = 0;
}
else if (svg_pts[i] == "m")
{
// Coords are Relative
printf("Move (rel)");
mode = 6;
}
else if (svg_pts[i] == "M")
{
// Coords are Absolute
printf("Move (abs)");
mode = 7;
}
else if (svg_pts[i] == "c")
{
// Coords are Relative
printf("curve (rel)");
mode = 0;
i += 2; // flatten curve, just use endpoints
}
else if (svg_pts[i] == "C")
{
// Coords are Absolute
printf("curve (abs)");
mode = 1;
i += 2; // flatten curve, just use endpoints
}
// assume numerical coords if none of the above conditions are met
else
{
poly_start[global_pts + new_pts] = 0;
// Line-to command, parse coordinates from text
if ((mode == 0) || (mode == 1))
{
svgcoords_to_coords(svg_pts[i]);
}
// horizontal line, update X only
else if ((mode == 2) || (mode == 3))
{
mode -= 2;
tmp_coords[0] = strtod(svg_pts[i]);
}
// vertical line, update Y only
else if ((mode == 4) || (mode == 5))
{
mode -= 4;
tmp_coords[1] = strtod(svg_pts[i]);
}
// Move-to command, parse coordinates from text
else if ((mode == 6) || (mode == 7))
{
// If a relative moveto (m) appears as the first element of the path, then it is treated as a pair of absolute coordinates
mode = 1;
svgcoords_to_coords(svg_pts[i]);
poly_start[global_pts + new_pts] = 1;
}
if (tmp_coords[0] == 0 && tmp_coords[1] == 0)
{
print("ERROR\n");
}
else
{
if (mode == 0)
{
int prevIndex = global_pts + new_pts - 1;
if (prevIndex < 0) {prevIndex = 0;} // in case this is the very first instruction
pts_x[global_pts + new_pts] = pts_x[prevIndex] + tmp_coords[0] * scaleFactor + SvgTranslationX;
pts_y[global_pts + new_pts] = pts_y[prevIndex] + tmp_coords[1] * scaleFactor + SvgTranslationY;
}
else if (mode == 1)
{
pts_x[global_pts + new_pts] = tmp_coords[0] * scaleFactor + SvgTranslationX;
pts_y[global_pts + new_pts] = tmp_coords[1] * scaleFactor + SvgTranslationY;
}
printf("\traw = %2.2f,%2.2f\tcoord = %2.2f,%2.2f\n", tmp_coords[0], tmp_coords[1], pts_x[global_pts + new_pts], pts_y[global_pts + new_pts]);
new_pts++;
// remove duplicate points
if (global_pts + new_pts > 1)
{
if ((pts_x[global_pts + new_pts] == pts_x[global_pts + new_pts - 1]) && (pts_y[global_pts + new_pts] == pts_y[global_pts + new_pts - 1])) {new_pts--;}
}
}
}
i++;
}
// Close the polygon with the starting point
svgcoords_to_coords(svg_pts[1]);
pts_x[global_pts + new_pts] = tmp_coords[0] * scaleFactor + SvgTranslationX;
pts_y[global_pts + new_pts] = tmp_coords[1] * scaleFactor + SvgTranslationY;
printf("End polygon at %2.2f,%2.2f\n\n", pts_x[global_pts + new_pts], pts_y[global_pts + new_pts]);
// remove duplicate points
if ((pts_x[global_pts + new_pts] == pts_x[global_pts + new_pts - 1]) && (pts_y[global_pts + new_pts] == pts_y[global_pts + new_pts - 1])) {new_pts--;}
new_pts++;
global_pts += new_pts;
num_polys++;
}
/*
* Traverse through the tree of groups, adding points to paths,
* while applying group transforms in the process
*/
void extract_nested_data()
{
string xml[];
string elements[];
string tags[];
int size;
while(stack != ""){
// Pop xml to parse
strsplit(xml, pull(), '^');
// Find name of root
string tmp[], root;
xmltags(tmp, xml[0], "");
root = tmp[0];
if (root == "path"){
// Path found
print(" PATH " + xmlattribute(xml[0], "path", "id") + " FOUND ");
extract_pts(xmlattribute(xml[0], "path", "d"), xmlattribute(xml[0], "path", "transform"));
//}else if (root == ""){
// Add more cases here
}else{
int a = xmltags(tags, xml[0], root);
for(int i=0; i<a; i++){
int b = xmlelements(elements, xml[0], root+"/"+tags[i]);
// Reverse push children
for(int j=0; j<b; j++){
// Push child back to search for more paths
push(pack(elements[j], global_pts));
}
}
}
}
}
/*
* Writes all activity to a log file, while doing its thing
*/
output("svg2poly.log", "wt")
{
setup_cmd(); // get info from command line, if any
userDialog(); // launch user GUI
// Select file
//string file = dlgFileOpen("Select a file", "", "*.svg");
if (file != ""){
int i=0, j=0;
string svg[];
master_xml= "";
int len = fileread(svg, file);
// Read file into one massive xml line
for(; i<len; i++){
//print(svg[i]);
master_xml += svg[i];
}
// Extract Points and apply transformations
push(pack(master_xml, global_pts));
extract_nested_data();
// Correct Inkscape's Formatting issue
for(i=0; i<global_pts; i++)
pts_y[i] *= -1;
// Center object
real min_x, min_y,
max_x, max_y;
min_x = max_x = pts_x[0];
min_y = max_y = pts_y[0];
for(i=1; i<global_pts; i++){
// Find Min & Max
if (pts_x[i] < min_x)
min_x = pts_x[i];
if (pts_x[i] > max_x)
max_x = pts_x[i];
if (pts_y[i] < min_y)
min_y = pts_y[i];
if (pts_y[i] > max_y)
max_y = pts_y[i];
}
real x_center = (max_x - min_x)/2,
y_center = (max_y - min_y)/2;
// Apply offsets to all points
for(i=0; i < global_pts; i++){
pts_x[i] -= min_x + x_center;
pts_y[i] -= min_y + y_center;
}
printf("\nObject origin:\n%f %f\n", x_center, y_center);
// Generate eagle command
for(i=0; i<global_pts; i++){
if (poly_start[i] == 1){
//printf("%2d) %3.4f %3.4f\n", j++, pts_x[i] * ratioX, pts_y[i] * ratioY);
if (i != 0)
sprintf(cmd, "%s;\npoly (R %f %f)", cmd, pts_x[i] * ratioX, pts_y[i] * ratioY);
else
sprintf(cmd, "%s (R %f %f)", cmd, pts_x[i] * ratioX, pts_y[i] * ratioY);
}else{
//printf(" %3.4f %3.4f\n", pts_x[i] * ratioX, pts_y[i] * ratioY);
sprintf(cmd, "%s (R %f %f)", cmd, pts_x[i] * ratioX, pts_y[i] * ratioY);
}
}
cmd += ";\n";
print("\nEagle Command:\n" + cmd);
exit(cmd);
}
exit("");
}