Pièce jointe « mod_svg2obj.py »
Téléchargement 1 """
2 SVG 2 OBJ translater, 0.4.3
3 (c) jm soler juillet/novembre 2004-juin/aout 2005,
4 # released under Blender Artistic Licence
5 for the Blender 2.38/37/36/35/34/33 Python Scripts Bundle.
6 #---------------------------------------------------------------------------
7 # Page officielle :
8 # http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_import_svg.htm
9 # http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_import_svg_en.htm
10 # Communiquer les problemes et erreurs sur:
11 # http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender
12 #---------------------------------------------------------------------------
13
14 -- Concept : translate SVG file in GEO .obj file and try to load it.
15 -- Curiousity : the original matrix must be :
16
17 0.0 0.0 1.0 0.0
18 0.0 1.0 0.0 0.0
19 0.0 0.0 1.0 0.0
20 0.0 0.0 0.0 1.0
21
22 and not:
23 1.0 0.0 0.0 0.0
24 0.0 1.0 0.0 0.0
25 0.0 0.0 1.0 0.0
26 0.0 0.0 0.0 1.0
27
28 -- Options :
29 SHARP_IMPORT = 0
30 choise between "As is", "Devide by height" and "Devide by width"
31 SHARP_IMPORT = 1
32 no choise
33
34 -- Possible bug : sometime, the new curves object's RotY value
35 jumps to -90.0 degrees without any reason.
36
37 Yet done:
38 M : absolute move to
39 Z : close path
40 L : absolute line to
41 C : absolute curve to
42 S : absolute curve to with only one handle
43 H : absolute horizontal line to
44 V : absolute vertical line to
45
46 l : relative line to 2004/08/03
47 c : relative curve to 2004/08/03
48 s : relative curve to with only one handle
49 h : relative horizontal line to
50 v : relative vertical line to
51
52 A : courbe_vers_a,
53 V : ligne_tracee_v,
54 H : ligne_tracee_h,
55 Z : boucle_z,
56 Q : courbe_vers_q,
57 T : courbe_vers_t,
58 a : courbe_vers_a,
59 v : ligne_tracee_v,
60 h : ligne_tracee_h,
61 z : boucle_z,
62 q : courbe_vers_q,
63
64 transfrom for <g> tag
65 transform for <path> tag
66
67 Changelog:
68 0.1.1 : - control file without extension
69 0.2.0 : - improved reading of several data of the same type
70 following the same command (for gimp import)
71 0.2.1 : - better choice for viewboxing ( takes the viewbox if found,
72 instead of x,y,width and height
73 0.2.2 : - read compact path data from Illustrator 10
74 0.2.3 : - read a few new relative displacements
75 0.2.4 : - better hash for command followed by a lone data
76 (h,v) or uncommun number (a)
77 0.2.5 : - correction for gimp import
78 0.2.6 : - correction for illustrator 10 SVG
79 0.2.7 : - correction for inskape 0.40 cvs SVG
80 0.2.8 : - correction for inskape plain SVG
81 0.3 : - reading of the transform properties added :
82 translate
83 0.3.1 : - compatibility restored with gimp
84 0.3.2 : - transform properties added (june, 15-16):
85 scale,
86 rotate,
87 matrix,
88 skew
89 - added a test on __name__ to load the script
90 outside from the blender menu
91 0.3.3 : - matrix transform content control
92 0.3.4 : - paths data reading rewritten (19/06/05)
93 0.3.5 : - test on empty curve (22/06/05)
94 - removed overlayed points
95 0.3.6 : - rewriting of the bezier point contruction to correct
96 a problem in the connection between L type point and
97 C or S type point
98 0.3.7 : - code correction for bezier knot in Curveto command when
99 the command close a path
100 0.3.8 : - code was aded to manage quadratic bezier,
101 Q,q command and T,t commands, as a normal blender's bezier point
102 - The last modications does not work with gimp 2.0 svg export .
103 corrected too .
104 0.3.9 : - Path's A,a command for ellipse's arc .
105 0.4.0 : - To speed up the function filtre_DATA was removed and text
106 variables are changed into numeric variables
107 0.4.1 : - svg, groups and shapes hierarchy added
108 - now transform properties are computed using a stack with all
109 parented groups
110 - removed or replaced useless functions :
111 - skewY, skewX transforms
112 - radians in rotate transform
113 0.4.2 : - Added functon to translate others shapes in path
114 rect, line, polyline, polygon
115
116 0.4.3 : - various corrections
117 text font (id property exported by Adobe Illustrator are between coma)
118 function to code s tag has been rewritten
119 ==================================================================================
120 =================================================================================="""
121
122 SHARP_IMPORT=0
123 SCALE=1
124 scale_=1
125 DEBUG = 0#print
126 DEVELOPPEMENT=0
127
128 import sys
129 from math import cos,sin,tan, atan2, pi, ceil
130 PI=pi
131 import Blender
132 from Blender import Mathutils
133 BLversion=Blender.Get('version')
134
135 try:
136 import nt
137 os=nt
138 os.sep='\\'
139
140 except:
141 import posix
142 os=posix
143 os.sep='/'
144
145 def isdir(path):
146 try:
147 st = os.stat(path)
148 return 1
149 except:
150 return 0
151
152 def split(pathname):
153 if pathname.find(os.sep)!=-1:
154 k0=pathname.split(os.sep)
155 else:
156 if os.sep=='/':
157 k0=pathname.split('\\')
158 else:
159 k0=pathname.split('/')
160
161 directory=pathname.replace(k0[len(k0)-1],'')
162 Name=k0[len(k0)-1]
163 return directory, Name
164
165 def join(l0,l1):
166 return l0+os.sep+l1
167
168 os.isdir=isdir
169 os.split=split
170 os.join=join
171
172 def filtreFICHIER(nom):
173 """
174 Function filtreFICHIER
175
176 in : string nom , filename
177 out : string t , if correct filecontaint
178
179 Lit le contenu du fichier et en fait une pre-analyse
180 pour savoir s'il merite d'etre traite .
181 """
182 f=open(nom,'rU')
183 t=f.read()
184 f.close()
185
186 # -----------------
187 # pre-format ...
188 # -----------------
189 t=t.replace('\r','')
190 t=t.replace('\n','')
191 t=t.replace('svg:','')
192
193 if t.upper().find('<SVG')==-1 :
194 name = "ERROR: invalid or empty file ... " # if no %xN int is set, indices start from 1
195 result = Blender.Draw.PupMenu(name)
196 return "false"
197 else:
198 return t
199
200 """
201 elif t.upper().find('<PATH')==-1 and\
202 t.upper().find('<RECT')==-1 and\
203 t.upper().find('<LINE')==-1 and\
204 t.upper().find('<POLYLINE')==-1 :
205 name = "ERROR: there's no Path in this file ... " # if no %xN int is set, indices start from 1
206 result = Blender.Draw.PupMenu(name)
207 return "false"
208 """
209
210
211 #===============================
212 # Data
213 #===============================
214
215
216 #===============================
217 # Blender Curve Data
218 #===============================
219 objBEZIER=0
220 objSURFACE=5
221 typBEZIER3D=1 #3D
222 typBEZIER2D=9 #2D
223
224 class Bez:
225 def __init__(self):
226 self.co=[]
227 self.ha=[0,0]
228 self.tag=''
229
230 class ITEM:
231 def __init__(self):
232 self.type = typBEZIER3D,
233 self.pntsUV = [0,0]
234 self.resolUV = [32,0]
235 self.orderUV = [0,0]
236 self.flagUV = [0,0]
237 self.Origine = [0.0,0.0]
238 self.beziers_knot = []
239 self.fill=0
240 self.closed=0
241 self.color=[0.0,0.0,0.0]
242
243 class COURBE:
244 def __init__(self):
245 self.magic_number='3DG3'
246 self.type = objBEZIER
247 self.number_of_items = 0
248 self.ext1_ext2 = [0,0]
249 self.matrix = """0.0 0.0 1.0 0.0
250 0.0 1.0 0.0 0.0
251 0.0 0.0 1.0 0.0
252 0.0 0.0 0.0 1.0 """
253 self.ITEM = {}
254
255
256 courbes=COURBE()
257 PATTERN={}
258 BOUNDINGBOX={'rec':[],'coef':1.0}
259 npat=0
260 #=====================================================================
261 #======== name of the curve in the curves dictionnary ===============
262 #=====================================================================
263 n0=0
264
265 #=====================================================================
266 #====================== current Point ================================
267 #=====================================================================
268 CP=[0.0,0.0] #currentPoint
269
270 #=====================================================================
271 #===== to compare last position to the original move to displacement =
272 #===== needed for cyclic definition inAI, EPS forma ================
273 #=====================================================================
274 def test_egalitedespositions(f1,f2):
275 if f1[0]==f2[0] and f1[1]==f2[1]:
276 return Blender.TRUE
277 else:
278 return Blender.FALSE
279
280
281 def Open_GEOfile(dir,nom):
282 global SCALE,BOUNDINGBOX, scale_
283 if BLversion>=233:
284 Blender.Load(dir+nom+'OOO.obj', 1)
285 BO=Blender.Object.Get()
286
287 BO[-1].RotY=3.1416
288 BO[-1].RotZ=3.1416
289 BO[-1].RotX=3.1416/2.0
290
291 if scale_==1:
292 BO[-1].LocY+=BOUNDINGBOX['rec'][3]
293 else:
294 BO[-1].LocY+=BOUNDINGBOX['rec'][3]/SCALE
295
296 BO[-1].makeDisplayList()
297 Blender.Window.RedrawAll()
298 else:
299 print "Not yet implemented"
300
301 def create_GEOtext(courbes):
302 global SCALE, B, BOUNDINGBOX,scale_
303
304 r=BOUNDINGBOX['rec']
305 if scale_==1:
306 SCALE=1.0
307 elif scale_==2:
308 SCALE=r[2]-r[0]
309 elif scale_==3:
310 SCALE=r[3]-r[1]
311
312 t=[]
313 t.append(courbes.magic_number+'\n')
314 t.append(str(courbes.type)+'\n')
315 t.append(str(courbes.number_of_items)+'\n')
316 t.append(str(courbes.ext1_ext2[0])+' '+str(courbes.ext1_ext2[1])+'\n')
317 t.append(courbes.matrix+'\n')
318
319 for k in courbes.ITEM.keys():
320 t.append("%s\n"%courbes.ITEM[k].type)
321 t.append("%s %s \n"%(courbes.ITEM[k].pntsUV[0],courbes.ITEM[k].pntsUV[1]))
322 t.append("%s %s \n"%(courbes.ITEM[k].resolUV[0],courbes.ITEM[k].resolUV[1]))
323 t.append("%s %s \n"%(courbes.ITEM[k].orderUV[0],courbes.ITEM[k].orderUV[1]))
324 t.append("%s %s \n"%(courbes.ITEM[k].flagUV[0],courbes.ITEM[k].flagUV[1]))
325
326 flag =0#courbes.ITEM[k].flagUV[0]
327
328 for k2 in range(flag,len(courbes.ITEM[k].beziers_knot)):
329 #k1 =courbes.ITEM[k].beziers_knot[k2]
330 k1=ajustement(courbes.ITEM[k].beziers_knot[k2], SCALE)
331
332 t.append("%4f 0.0 %4f \n"%(k1[4],k1[5]))
333 t.append("%4f 0.0 %4f \n"%(k1[0],k1[1]))
334 t.append("%4f 0.0 %4f \n"%(k1[2],k1[3]))
335 t.append(str(courbes.ITEM[k].beziers_knot[k2].ha[0])+' '+str(courbes.ITEM[k].beziers_knot[k2].ha[1])+'\n')
336
337 return t
338
339 def save_GEOfile(dir,nom,t):
340 f=open(dir+nom+'OOO.obj','w')
341 f.writelines(t)
342 f.close()
343
344 #=====================================================================
345 #===== SVG format : DEBUT =========================
346 #=====================================================================
347 #--------------------
348 # 0.4.2
349 #--------------------
350 OTHERSSHAPES=['rect','line', 'polyline', 'polygon','circle','ellipse']
351
352 #--------------------
353 # 0.4.2
354 #--------------------
355 def rect(prp):
356 D=[]
357 if 'x' not in prp.keys(): x=0.0
358 else : x=float(prp['x'])
359 if 'y' not in prp.keys(): y=0.0
360 else : y=float(prp['y'])
361
362 height=float(prp['height'])
363 width=float(prp['width'])
364
365 """
366 normal rect
367
368 x,y
369 h1
370 *----------*
371 | |
372 | |
373 | |
374 *----------* v1
375 h2
376 """
377 if 'rx' not in prp.keys() or 'rx' not in prp.keys():
378 exec """D=['M','%s','%s','h','%s','v','%s','h','%s','z']"""%(x,y,width,height,-width)
379 else :
380 rx=float(prp['rx'])
381 if 'ry' not in prp.keys() :
382 ry=float(prp['rx'])
383 else : ry=float(prp['ry'])
384 if 'rx' in prp.keys() and prp['rx']<0.0: rx*=-1
385 if 'ry' in prp.keys() and prp['ry']<0.0: ry*=-1
386
387 """
388 rounded corner
389
390 x,y M h1
391 ---*----------*
392 / \
393 / \
394 v2 * * c1
395 | |
396 | |
397 | |
398 c3 * * v2
399 \ /
400 \ /
401 *----------*
402 h2 c2
403 """
404 exec """D=['M','%s','%s',
405 'h','%s',
406 'c','%s','%s','%s','%s','%s','%s',
407 'v','%s',
408 'c','%s','%s','%s','%s','%s','%s',
409 'h','%s',
410 'c','%s','%s','%s','%s','%s','%s',
411 'v','%s',
412 'c','%s','%s','%s','%s','%s','%s',
413 'z']"""%(x+rx,y,
414 width-2*rx,
415 rx,0.0,rx,ry,rx,ry,
416 height-ry,
417 0.0,ry,-rx,ry,-rx,ry,
418 -width+2*rx,
419 -rx,0.0,-rx,-ry,-rx,-ry,
420 -height+ry,
421 0.0,0.0,0.0,-ry,rx,-ry
422 )
423
424 return D
425
426 #--------------------
427 # 0.4.2
428 #--------------------
429 def circle(prp):
430 if 'cx' not in prp.keys(): cx=0.0
431 else : cx =float(prp['cx'])
432 if 'cy' not in prp.keys(): cy=0.0
433 else : cy =float(prp['cy'])
434 r = float(prp['r'])
435 exec """D=['M','%s','%s',
436 'C','%s','%s','%s','%s','%s','%s',
437 'C','%s','%s','%s','%s','%s','%s',
438 'C','%s','%s','%s','%s','%s','%s',
439 'C','%s','%s','%s','%s','%s','%s',
440 'Z']"""%(
441 cx,cy+r,
442 cx-r,cy+r*0.552, cx-0.552*r,cy+r, cx,cy+r,
443 cx+r*0.552,cy+r, cx+r,cy+r*0.552, cx+r,cy,
444 cx+r,cy-r*0.552, cx+r*0.552,cy-r, cx,cy-r,
445 cx-r*0.552,cy-r, cx-r,cy-r*0.552, cx-r,cy
446 )
447 return D
448
449 #--------------------
450 # 0.4.2
451 #--------------------
452 def ellipse(prp):
453 if 'cx' not in prp.keys(): cx=0.0
454 else : cx =float(prp['cx'])
455 if 'cy' not in prp.keys(): cy=0.0
456 else : cy =float(prp['cy'])
457 ry = float(prp['rx'])
458 rx = float(prp['ry'])
459
460 exec """D=['M','%s','%s',
461 'C','%s','%s','%s','%s','%s','%s',
462 'C','%s','%s','%s','%s','%s','%s',
463 'C','%s','%s','%s','%s','%s','%s',
464 'C','%s','%s','%s','%s','%s','%s',
465 'Z']"""%(
466 cx,cy+rx,
467 cx-ry,cy+rx*0.552, cx-0.552*ry,cy+rx, cx,cy+rx,
468 cx+ry*0.552,cy+rx, cx+ry,cy+rx*0.552, cx+ry,cy,
469 cx+ry,cy-rx*0.552, cx+ry*0.552,cy-rx, cx,cy-rx,
470 cx-ry*0.552,cy-rx, cx-ry,cy-rx*0.552, cx-ry,cy
471 )
472 return D
473 #--------------------
474 # 0.4.2
475 #--------------------
476 def line(prp):
477 exec """D=['M','%s','%s',
478 'L','%s','%s']"""%(prp['x1'],prp['y1'], prp['x2'],prp['y2'])
479 return D
480 #--------------------
481 # 0.4.2
482 #--------------------
483 def polyline(prp):
484 if 'points' in prp.keys():
485 #print prp['points']
486 points=prp['points'].split(' ')
487 #print points
488 np=0
489 for p in points:
490 if p!='':
491 p=p.split(',')
492 if np==0:
493 exec "D=['M','%s','%s']"%(p[0],p[1])
494 np+=1
495 else:
496 exec """D.append('L');D.append('%s');D.append('%s')"""%(p[0],p[1])
497 #print D
498 return D
499 else:
500 return []
501
502 #--------------------
503 # 0.4.2
504 #--------------------
505 def polygon(prp):
506 D=polyline(prp)
507 if D!=[]:
508 D.append('Z')
509 return D
510
511 #--------------------
512 # 0.3.9
513 #--------------------
514 def Ellipse2bezier(P, xc, yc, th0, th1, rx, ry, phi):
515 sin_th = sin (phi * (PI / 180.0))
516 cos_th = cos (phi * (PI / 180.0))
517 a00 = cos_th * rx
518 a01 = -sin_th * ry
519 a10 = sin_th * rx
520 a11 = cos_th * ry
521 th_half = 0.5 * (th1 - th0)
522 t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half)
523 x1 = xc + cos (th0) - t * sin (th0)
524 y1 = yc + sin (th0) + t * cos (th0)
525 x3 = xc + cos (th1)
526 y3 = yc + sin (th1)
527 x2 = x3 + t * sin (th1)
528 y2 = y3 - t * cos (th1)
529 P.append([[a00 * x1 + a01 * y1, a10 * x1 + a11 * y1],
530 [a00 * x2 + a01 * y2, a10 * x2 + a11 * y2],
531 [a00 * x3 + a01 * y3, a10 * x3 + a11 * y3]]);
532
533 #--------------------
534 # 0.3.9
535 #--------------------
536 def calc_arc (cpx,cpy, rx, ry, phi, fa , fs , x, y) :
537
538 sin_th = sin (phi * (PI / 180.0))
539 cos_th = cos (phi * (PI / 180.0))
540
541 if rx < 0.0 : rx = -rx
542 if ry < 0.0 : ry = -ry
543
544 px = cos_th * (cpx - x) * 0.5 + sin_th * (cpy - y) * 0.5
545 py = cos_th * (cpy - y) * 0.5 - sin_th * (cpx - x) * 0.5
546 pl = (px * px) / (rx * rx) + (py * py) / (ry * ry)
547
548 if pl > 1.0 :
549 pl = pl**0.5;
550 rx *= pl
551 ry *= pl
552
553 a00 = cos_th / rx
554 a01 = sin_th / rx
555 a10 = -sin_th / ry
556 a11 = cos_th / ry
557
558 x0 = a00 * cpx + a01 * cpy
559 y0 = a10 * cpx + a11 * cpy
560 x1 = a00 * x + a01 * y
561 y1 = a10 * x + a11 * y
562 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)
563 #print d,x1,x0,y1,y0
564 if abs(d)>0.0 : sfactor_sq = 1.0 / d - 0.25
565 else :sfactor_sq=-0.25
566
567 if sfactor_sq < 0.0 : sfactor_sq = 0.0
568 sfactor = sfactor_sq**0.5
569
570 if fs == fa : sfactor = -sfactor
571
572 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0)
573 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0)
574
575 theta0 = atan2 (y0 - yc, x0 - xc);
576 theta1 = atan2 (y1 - yc, x1 - xc);
577 theta_arc = theta1 - theta0;
578
579 if (theta_arc < 0.0 and fs==1) :
580 theta_arc += 2.0 * PI
581 elif (theta_arc > 0.0 and fs==0) :
582 theta_arc -= 2.0 * PI
583
584 n_segs = int(ceil(abs (theta_arc*2.0/(PI * 0.5 + 0.001))))
585 P=[]
586 for i in range(n_segs):
587 Ellipse2bezier(P, xc, yc,
588 theta0 + i * theta_arc / n_segs,
589 theta0 + (i + 1) * theta_arc / n_segs,
590 rx, ry, phi)
591
592 return P
593
594 #--------------------
595 # 0.3.9
596 #--------------------
597 def courbe_vers_a(c,D,n0,CP): #A,a
598 global SCALE
599
600 l=[float(D[c[1]+1]),float(D[c[1]+2]),float(D[c[1]+3]),
601 int(D[c[1]+4]),int(D[c[1]+5]),float(D[c[1]+6]),float(D[c[1]+7])]
602 if c[0]=='a':
603 l[5]=l[5] + CP[0]
604 l[6]=l[6] + CP[1]
605
606 B=Bez()
607 B.co=[ CP[0], CP[1], CP[0], CP[1], CP[0], CP[1] ]
608 B.ha=[0,0]
609 B.tag=c[0]
610
611 POINTS= calc_arc (CP[0],CP[1],
612 l[0], l[1], l[2],
613 l[3], l[4],
614 l[5], l[6] )
615
616 if DEBUG == 1 : print POINTS
617
618 for p in POINTS :
619 B=Bez()
620 B.co=[ p[2][0],p[2][1], p[0][0],p[0][1], p[1][0],p[1][1]]
621 B.ha=[0,0]
622 B.tag=c[0]
623 BP=courbes.ITEM[n0].beziers_knot[-1]
624 BP.co[2]=B.co[2]
625 BP.co[3]=B.co[3]
626 courbes.ITEM[n0].beziers_knot.append(B)
627
628 BP=courbes.ITEM[n0].beziers_knot[-1]
629 BP.co[2]=BP.co[0]
630 BP.co[3]=BP.co[1]
631
632 CP=[l[5], l[6]]
633 return courbes,n0,CP
634
635 def mouvement_vers(c, D, n0,CP, proprietes):
636 global DEBUG,TAGcourbe
637
638 #l=filtre_DATA(c,D,2)
639 l=[float(D[c[1]+1]),float(D[c[1]+2])]
640
641 if c[0]=='m':
642 l=[l[0]+CP[0],
643 l[1] + CP[1]]
644
645 if n0 in courbes.ITEM.keys():
646 n0+=1
647
648 CP=[l[0],l[1]]
649 courbes.ITEM[n0]=ITEM()
650 courbes.ITEM[n0].Origine=[l[0],l[1]]
651
652 proprietes['n'].append(n0)
653 #print 'prop et item',proprietes['n'], courbes.ITEM.keys()
654
655 B=Bez()
656 B.co=[CP[0],CP[1],CP[0],CP[1],CP[0],CP[1]]
657 B.ha=[0,0]
658 B.tag=c[0]
659 courbes.ITEM[n0].beziers_knot.append(B)
660
661 if DEBUG==1: print courbes.ITEM[n0], CP
662
663 return courbes,n0,CP
664
665 def boucle_z(c,D,n0,CP): #Z,z
666 #print c, 'close'
667 #print courbes.ITEM[n0].beziers_knot
668 courbes.ITEM[n0].flagUV[0]=1
669 #print 'len(courbes.ITEM[n0].beziers_knot)',len(courbes.ITEM[n0].beziers_knot)
670 if len(courbes.ITEM[n0].beziers_knot)>1:
671 BP=courbes.ITEM[n0].beziers_knot[-1]
672 BP0=courbes.ITEM[n0].beziers_knot[0]
673 if BP.tag in ['c','C','s','S']:
674 BP.co[2]=BP0.co[2] #4-5 point prec
675 BP.co[3]=BP0.co[3]
676 del courbes.ITEM[n0].beziers_knot[0]
677 else:
678 del courbes.ITEM[n0]
679
680 n0-=1
681 return courbes,n0,CP
682
683 def courbe_vers_q(c,D,n0,CP): #Q,q
684 l=[float(D[c[1]+1]),float(D[c[1]+2]),float(D[c[1]+3]),float(D[c[1]+4])]
685 if c[0]=='q':
686 l=[l[0]+CP[0], l[1]+CP[1], l[2]+CP[0], l[3]+CP[1]]
687 B=Bez()
688 B.co=[l[2], l[3], l[2], l[3], l[0], l[1]] #plus toucher au 2-3
689 B.ha=[0,0]
690 B.tag=c[0]
691 BP=courbes.ITEM[n0].beziers_knot[-1]
692 BP.co[2]=BP.co[0]
693 BP.co[3]=BP.co[1]
694 courbes.ITEM[n0].beziers_knot.append(B)
695 if DEBUG==1: print B.co,BP.co
696
697 CP=[l[2],l[3]]
698 if DEBUG==1:
699 pass
700 if len(D)>c[1]+5 and D[c[1]+5] not in TAGcourbe :
701 c[1]+=4
702 courbe_vers_q(c, D, n0,CP)
703 return courbes,n0,CP
704
705 def courbe_vers_t(c,D,n0,CP): #T,t
706 l=[float(D[c[1]+1]),float(D[c[1]+2])]
707 if c[0]=='t':
708 l=[l[0]+CP[0], l[1]+CP[1]]
709
710 B=Bez()
711 B.co=[l[0], l[1], l[0], l[1], l[0], l[1]] #plus toucher au 2-3
712 B.ha=[0,0]
713 B.tag=c[0]
714
715 BP=courbes.ITEM[n0].beziers_knot[-1]
716
717 l0=contruit_SYMETRIC([BP.co[0],BP.co[1],BP.co[4],BP.co[5]])
718
719 if BP.tag in ['q','Q','t','T','m','M']:
720 BP.co[2]=l0[2]
721 BP.co[3]=l0[3]
722
723 courbes.ITEM[n0].beziers_knot.append(B)
724 if DEBUG==1: print B.co,BP.co
725
726 CP=[l[0],l[1]]
727 if len(D)>c[1]+3 and D[c[1]+3] not in TAGcourbe :
728 c[1]+=4
729 courbe_vers_t(c, D, n0,CP)
730 return courbes,n0,CP
731
732 #--------------------
733 # 0.4.3 : rewritten
734 #--------------------
735 def contruit_SYMETRIC(l):
736 X=l[2]-(l[0]-l[2])
737 Y=l[3]-(l[1]-l[3])
738 return X,Y
739
740 def courbe_vers_s(c,D,n0,CP): #S,s
741 l=[float(D[c[1]+1]),
742 float(D[c[1]+2]),
743 float(D[c[1]+3]),
744 float(D[c[1]+4])]
745 if c[0]=='s':
746 l=[l[0]+CP[0], l[1]+CP[1],
747 l[2]+CP[0], l[3]+CP[1]]
748 B=Bez()
749 B.co=[l[2],l[3],l[2],l[3],l[0],l[1]] #plus toucher au 2-3
750 B.ha=[0,0]
751 B.tag=c[0]
752 BP=courbes.ITEM[n0].beziers_knot[-1]
753 #--------------------
754 # 0.4.3
755 #--------------------
756 BP.co[2],BP.co[3]=contruit_SYMETRIC([BP.co[4],BP.co[5],BP.co[0],BP.co[1]])
757 courbes.ITEM[n0].beziers_knot.append(B)
758 if DEBUG==1: print B.co,BP.co
759 #--------------------
760 # 0.4.3
761 #--------------------
762 CP=[l[2],l[3]]
763 if len(D)>c[1]+5 and D[c[1]+5] not in TAGcourbe :
764 c[1]+=4
765 courbe_vers_c(c, D, n0,CP)
766 return courbes,n0,CP
767
768 def courbe_vers_c(c, D, n0,CP): #c,C
769 l=[float(D[c[1]+1]),float(D[c[1]+2]),float(D[c[1]+3]),
770 float(D[c[1]+4]),float(D[c[1]+5]),float(D[c[1]+6])]
771 if c[0]=='c':
772 l=[l[0]+CP[0],
773 l[1]+CP[1],
774 l[2]+CP[0],
775 l[3]+CP[1],
776 l[4]+CP[0],
777 l[5]+CP[1]]
778 B=Bez()
779 B.co=[l[4],
780 l[5],
781 l[4],
782 l[5],
783 l[2],
784 l[3]] #plus toucher au 2-3
785 B.ha=[0,0]
786 B.tag=c[0]
787 BP=courbes.ITEM[n0].beziers_knot[-1]
788 BP.co[2]=l[0]
789 BP.co[3]=l[1]
790 courbes.ITEM[n0].beziers_knot.append(B)
791 if DEBUG==1: print B.co,BP.co
792 CP=[l[4],l[5]]
793 if len(D)>c[1]+7 and D[c[1]+7] not in TAGcourbe :
794 c[1]+=6
795 courbe_vers_c(c, D, n0,CP)
796 return courbes,n0,CP
797
798
799 def ligne_tracee_l(c, D, n0,CP): #L,l
800 l=[float(D[c[1]+1]),float(D[c[1]+2])]
801 if c[0]=='l':
802 l=[l[0]+CP[0],
803 l[1]+CP[1]]
804 B=Bez()
805 B.co=[l[0],l[1],l[0],l[1],l[0],l[1]]
806 B.ha=[0,0]
807 B.tag=c[0]
808 BP=courbes.ITEM[n0].beziers_knot[-1]
809 if BP.tag in ['c','C','s','S','m','M']:
810 BP.co[2]=B.co[4]
811 BP.co[3]=B.co[5]
812 courbes.ITEM[n0].beziers_knot.append(B)
813 CP=[B.co[0],B.co[1]]
814 if len(D)>c[1]+3 and D[c[1]+3] not in TAGcourbe :
815 c[1]+=2
816 ligne_tracee_l(c, D, n0,CP) #L
817 return courbes,n0,CP
818
819
820 def ligne_tracee_h(c,D,n0,CP): #H,h
821 if c[0]=='h':
822 l=[float(D[c[1]+1])+float(CP[0]),CP[1]]
823 else:
824 l=[float(D[c[1]+1]),CP[1]]
825 B=Bez()
826 B.co=[l[0],l[1],l[0],l[1],l[0],l[1]]
827 B.ha=[0,0]
828 B.tag=c[0]
829 BP=courbes.ITEM[n0].beziers_knot[-1]
830 if BP.tag in ['c','C','s','S','m','M']:
831 BP.co[2]=B.co[4]
832 BP.co[3]=B.co[5]
833 courbes.ITEM[n0].beziers_knot.append(B)
834 CP=[l[0],l[1]]
835 return courbes,n0,CP
836
837 def ligne_tracee_v(c,D,n0,CP): #V, v
838 if c[0]=='v':
839 l=[CP[0], float(D[c[1]+1])+CP[1]]
840 else:
841 l=[CP[0], float(D[c[1]+1])]
842 B=Bez()
843 B.co=[l[0],l[1],l[0],l[1],l[0],l[1]]
844 B.ha=[0,0]
845 B.tag=c[0]
846 BP=courbes.ITEM[n0].beziers_knot[-1]
847 if BP.tag in ['c','C','s','S','m','M']:
848 BP.co[2]=B.co[4]
849 BP.co[3]=B.co[5]
850 courbes.ITEM[n0].beziers_knot.append(B)
851 CP=[l[0],l[1]]
852 return courbes,n0,CP
853
854 Actions= { "C" : courbe_vers_c,
855 "A" : courbe_vers_a,
856 "S" : courbe_vers_s,
857 "M" : mouvement_vers,
858 "V" : ligne_tracee_v,
859 "L" : ligne_tracee_l,
860 "H" : ligne_tracee_h,
861 "Z" : boucle_z,
862 "Q" : courbe_vers_q,
863 "T" : courbe_vers_t,
864
865 "c" : courbe_vers_c,
866 "a" : courbe_vers_a,
867 "s" : courbe_vers_s,
868 "m" : mouvement_vers,
869 "v" : ligne_tracee_v,
870 "l" : ligne_tracee_l,
871 "h" : ligne_tracee_h,
872 "z" : boucle_z,
873 "q" : courbe_vers_q,
874 "T" : courbe_vers_t
875 }
876
877 TAGcourbe=Actions.keys()
878 TAGtransform=['M','L','C','S','H','V','T','Q']
879 tagTRANSFORM=0
880
881 def wash_DATA(ndata):
882 if ndata!='':
883 while ndata[0]==' ':
884 ndata=ndata[1:]
885 while ndata[-1]==' ':
886 ndata=ndata[:-1]
887 if ndata[0]==',':ndata=ndata[1:]
888 if ndata[-1]==',':ndata=ndata[:-1]
889 #--------------------
890 # 0.4.0 : 'e'
891 #--------------------
892 if ndata.find('-')!=-1 and ndata[ndata.find('-')-1] not in [' ', ',', 'e']:
893 ndata=ndata.replace('-',',-')
894 ndata=ndata.replace(',,',',')
895 ndata=ndata.replace(' ',',')
896 ndata=ndata.split(',')
897 for n in ndata :
898 if n=='' : ndata.remove(n)
899 return ndata
900
901 #--------------------
902 # 0.3.4 : - reading data rewrittten
903 #--------------------
904 def list_DATA(DATA):
905 """
906 cette fonction doit retourner une liste proposant
907 une suite correcte de commande avec le nombre de valeurs
908 attendu pour chacune d'entres-elles .
909 Par exemple :
910 d="'M0,14.0 z" devient ['M','0.0','14.0','z']
911 """
912 tagplace=[]
913 for d in Actions.keys():
914 b1=0
915 b2=len(DATA)
916 while DATA.find(d,b1,b2)!=-1 :
917 tagplace.append(DATA.find(d,b1,b2))
918 b1=DATA.find(d,b1,b2)+1
919 tagplace.sort()
920 tpn=range(len(tagplace)-1)
921 #--------------------
922 # 0.3.5 :: short data,only one tag
923 #--------------------
924 if len(tagplace)-1>0:
925 DATA2=[]
926 for t in tpn:
927 DATA2.append(DATA[tagplace[t]:tagplace[t]+1])
928 ndata=DATA[tagplace[t]+1:tagplace[t+1]]
929 if DATA2[-1] not in ['z','Z'] :
930 ndata=wash_DATA(ndata)
931 for n in ndata : DATA2.append(n)
932 DATA2.append(DATA[tagplace[t+1]:tagplace[t+1]+1])
933 if DATA2[-1] not in ['z','Z'] and len(DATA)-1>=tagplace[t+1]+1:
934 ndata=DATA[tagplace[t+1]+1:-1]
935 ndata=wash_DATA(ndata)
936 for n in ndata : DATA2.append(n)
937 else:
938 #--------------------
939 # 0.3.5 : short data,only one tag
940 #--------------------
941 DATA2=[]
942 DATA2.append(DATA[tagplace[0]:tagplace[0]+1])
943 ndata=DATA[tagplace[0]+1:]
944 ndata=wash_DATA(ndata)
945 for n in ndata : DATA2.append(n)
946 return DATA2
947
948 # 0.3
949 def translate(tx=None,ty=None):
950 return [1, 0, tx], [0, 1, ty],[0,0,1]
951 # 0.3.2
952 def scale(sx=None,sy=None):
953 if sy==None: sy=sx
954 return [sx, 0, 0], [0, sy, 0],[0,0,1]
955 # 0.4.1 : transslate a in radians
956 def rotate(a):
957 return [cos(a*3.1416/90.0), -sin(a*3.1416/90.0), 0], [sin(a*3.1416/90.0), cos(a*3.1416/90.0),0],[0,0,1]
958 # 0.3.2
959 def skewX(a):
960 return [1, tan(a), 0], [0, 1, 0],[0,0,1]
961 # 0.4.1
962 def skewY(a):
963 return [1, 0, 0], [tan(a), 1 , 0],[0,0,1]
964 # 0.3.2
965 def matrix(a,b,c,d,e,f):
966 return [a,c,e],[b,d,f],[0,0,1]
967
968 # 0.4.2 : rewritten
969 def control_CONTAINT(txt):
970 """
971 les descriptions de transformation peuvent être seules ou plusieurs et
972 les séparateurs peuvent avoir été oubliés
973 """
974 t0=0
975 tlist=[]
976 while txt.count(')',t0)>0:
977 t1=txt.find(')',t0)
978 nt0=txt[t0:t1+1]
979 t2=nt0[nt0.find('(')+1:-1]
980 val=nt0[:nt0.find('(')]
981 while t2.find(' ')!=-1:
982 t2=t2.replace(' ',' ')
983 t2=t2.replace(' ',',')
984 if t2.find('e'):
985 t3=t2.split(',')
986 t2=''
987 for t in t3 :
988 t=str(float(t))
989 t2=str(t3).replace(']','').replace('[','').replace('\'','')
990 if val=='rotate' :
991 t3=t2.split(',')
992 if len(t3)==3:
993 tlist.append('translate('+t3[1]+','+t3[2]+')')
994 tlist.append('rotate('+t3[0]+')')
995 tlist.append('translate(-'+t3[1]+',-'+t3[2]+')')
996 else:
997 tlist.append(val+'('+t2+')')
998 t0=t1+1
999 return tlist
1000
1001 # 0.4.1 : apply transform stack
1002 def courbe_TRANSFORM(Courbe,proprietes):
1003 # 1/ deplier le STACK
1004 # créer une matrice pour chaque transformation
1005 ST=[]
1006 #print proprietes['stack']
1007 for st in proprietes['stack'] :
1008 if st and type(st)==list:
1009 for t in st:
1010 exec "a,b,c=%s;T=Mathutils.Matrix(a,b,c)"%control_CONTAINT(t)[0]
1011 ST.append(T)
1012 elif st :
1013 exec "a,b,c=%s;T=Mathutils.Matrix(a,b,c)"%control_CONTAINT(st)[0]
1014 ST.append(T)
1015 if 'transform' in proprietes.keys():
1016 for trans in control_CONTAINT(proprietes['transform']):
1017 exec """a,b,c=%s;T=Mathutils.Matrix(a,b,c)"""%trans
1018 ST.append(T)
1019 #print ST
1020 ST.reverse()
1021 for n in proprietes['n']:
1022 if n in Courbe.keys():
1023 for bez0 in Courbe[n].beziers_knot:
1024 bez=bez0.co
1025 for b in [0,2,4]:
1026 for t in ST:
1027 v=Mathutils.Vector([bez[b],bez[b+1],1.0])
1028 v=Mathutils.MatMultVec(t, v)
1029 bez[b]=v[0]
1030 bez[b+1]=v[1]
1031
1032 def filtre(d):
1033 for nn in d:
1034 if '0123456789.'.find(nn)==-1:
1035 d=d.replace(nn,"")
1036 return d
1037
1038 def get_BOUNDBOX(BOUNDINGBOX,SVG):
1039 if 'viewbox' not in SVG.keys():
1040 h=float(filtre(SVG['height']))
1041 if DEBUG==1 : print 'h : ',h
1042 w=float(filtre(SVG['width']))
1043 if DEBUG==1 : print 'w :',w
1044 BOUNDINGBOX['rec']=[0.0,0.0,w,h]
1045 r=BOUNDINGBOX['rec']
1046 BOUNDINGBOX['coef']=w/h
1047 else:
1048 viewbox=SVG['viewbox'].split()
1049 BOUNDINGBOX['rec']=[float(viewbox[0]),float(viewbox[1]),float(viewbox[2]),float(viewbox[3])]
1050 r=BOUNDINGBOX['rec']
1051 BOUNDINGBOX['coef']=(r[2]-r[0])/(r[3]-r[1])
1052 return BOUNDINGBOX
1053
1054 # 0.4.1 : attributs ex : 'id=', 'transform=', 'd=' ...
1055 def collecte_ATTRIBUTS(data):
1056 data=data.replace(' ',' ')
1057 ELEM={'TYPE':data[1:data.find(' ')]}
1058 t1=len(data)
1059 t2=0
1060 ct=data.count('="')
1061 while ct>0:
1062 t0=data.find('="',t2)
1063 t2=data.find(' ',t2)+1
1064 id=data[t2:t0]
1065 t2=data.find('"',t0+2)
1066 if id!='d':
1067 exec "ELEM[id]=\"\"\"%s\"\"\""%(data[t0+2:t2].replace('\\','/'))
1068 else:
1069 exec "ELEM[id]=[%s,%s]"%(t0+2,t2)
1070 ct=data.count('="',t2)
1071 return ELEM
1072
1073 # --------------------------------------------
1074 # 0.4.1 : to avoid to use sax and ths xml
1075 # tools of the complete python
1076 # --------------------------------------------
1077 def contruit_HIERARCHIE(t):
1078 global CP, courbes, SCALE, DEBUG, BOUNDINGBOX, scale_, tagTRANSFORM
1079 TRANSFORM=0
1080 t=t.replace('\t',' ')
1081 while t.find(' ')!=-1:
1082 t=t.replace(' ',' ')
1083 n0=0
1084 t0=t1=0
1085 baliste=[]
1086 balisetype=['?','?','/','/','!','!']
1087 BALISES=['D', #DECL_TEXTE',
1088 'D', #DECL_TEXTE',
1089 'F', #FERMANTE',
1090 'E', #ELEM_VIDE',
1091 'd', #DOC',
1092 'R', #REMARQUES',
1093 'C', #CONTENU',
1094 'O' #OUVRANTE'
1095 ]
1096 STACK=[]
1097 while t1<len(t) and t0>-1:
1098 t0=t.find('<',t0)
1099 t1=t.find('>',t0)
1100 ouvrante=0
1101 if t0>-1 and t1>-1:
1102 if t[t0+1] in balisetype:
1103 b=balisetype.index(t[t0+1])
1104 if t[t0+2]=='-':
1105 b=balisetype.index(t[t0+1])+1
1106 balise=BALISES[b]
1107 if b==2:
1108 parent=STACK.pop(-1)
1109 if parent!=None and TRANSFORM>0:
1110 TRANSFORM-=1
1111 elif t[t1-1] in balisetype:
1112 balise=BALISES[balisetype.index(t[t1-1])+1]
1113 else:
1114 t2=t.find(' ',t0)
1115 if t2>t1: t2=t1
1116 ouvrante=1
1117 NOM=t[t0+1:t2]
1118 if t.find('</'+NOM)>-1:
1119 balise=BALISES[-1]
1120 else:
1121 balise=BALISES[-2]
1122 if balise=='E' or balise=='O':
1123 proprietes=collecte_ATTRIBUTS(t[t0:t1+ouvrante])
1124 if balise=='O' and 'transform' in proprietes.keys():
1125 STACK.append(proprietes['transform'])
1126 TRANSFORM+=1
1127 elif balise=='O' :
1128 STACK.append(None)
1129 proprietes['stack']=STACK[:]
1130 D=[]
1131 if proprietes['TYPE'] in ['path'] and (proprietes['d'][1]-proprietes['d'][0]>1):
1132 D=list_DATA(t[proprietes['d'][0]+t0:proprietes['d'][1]+t0])
1133 elif proprietes['TYPE'] in OTHERSSHAPES:
1134 exec "D=%s(proprietes)"% proprietes['TYPE']
1135 if len(D)>0:
1136 cursor=0
1137 proprietes['n']=[]
1138 for cell in D:
1139 if DEBUG==2 : print 'cell : ',cell ,' --'
1140 if len(cell)>=1 and cell[0] in TAGcourbe:
1141 prop=''
1142 if cell[0] in ['m','M']:
1143 prop=',proprietes'
1144 exec """courbes,n0,CP=Actions[cell]([cell,cursor], D, n0,CP%s)"""%prop
1145 cursor+=1
1146 if TRANSFORM>0 or 'transform' in proprietes.keys() :
1147 courbe_TRANSFORM(courbes.ITEM,proprietes)
1148 elif proprietes['TYPE'] in ['svg'] :
1149 BOUNDINGBOX = get_BOUNDBOX(BOUNDINGBOX,proprietes)
1150 t1+=1
1151 t0=t1
1152
1153 def scan_FILE(nom):
1154 global CP, courbes, SCALE, DEBUG, BOUNDINGBOX, scale_, tagTRANSFORM
1155 dir,name=split(nom)
1156 name=name.split('.')
1157 result=0
1158 t=filtreFICHIER(nom)
1159 if t!='false':
1160 if not SHARP_IMPORT:
1161 warning = "Select Size : %t| As is %x1 | Scale on Height %x2| Scale on Width %x3"
1162 scale_ = Blender.Draw.PupMenu(warning)
1163 # 0.4.1 : to avoid to use sax and the xml
1164 # tools of the complete python
1165 contruit_HIERARCHIE(t)
1166 r=BOUNDINGBOX['rec']
1167 if scale_==1:
1168 SCALE=1.0
1169 elif scale==2:
1170 SCALE=r[2]-r[0]
1171 elif scale_==3:
1172 SCALE=r[3]-r[1]
1173 courbes.number_of_items=len(courbes.ITEM.keys())
1174 for k in courbes.ITEM.keys():
1175 courbes.ITEM[k].pntsUV[0] =len(courbes.ITEM[k].beziers_knot)
1176 if courbes.number_of_items>0:
1177 if len(PATTERN.keys() )>0:
1178 if DEBUG == 3 : print len(PATTERN.keys() )
1179 t=create_GEOtext(courbes)
1180 save_GEOfile(dir,name[0],t)
1181 Open_GEOfile(dir,name[0])
1182 else:
1183 pass
1184
1185 def ajustement(v,s):
1186
1187 a,b,c,d,e,f=float(v.co[0]),float(v.co[1]),float(v.co[2]),float(v.co[3]),float(v.co[4]),float(v.co[5])
1188 return [a/s,-b/s,c/s,-d/s,e/s,-f/s]
1189
1190 #=====================================================================
1191 #====================== SVG format mouvements ========================
1192 #=====================================================================
1193
1194 #=====================================================================
1195 # une sorte de contournement qui permet d'utiliser la fonction
1196 # et de documenter les variables Window.FileSelector
1197 #=====================================================================
1198 def fonctionSELECT(nom):
1199 scan_FILE(nom)
1200
1201 if __name__=='__main__':
1202 Blender.Window.FileSelector (fonctionSELECT, 'SELECT a .SVG FILE')
Fichiers joints
Pour vous référer aux pièces jointes d'une page, utilisez attachment:filename, comme indiqué ci-dessous dans la liste de fichiers. N'utilisez pas l'URL du lien [get], car elle peut changer et donc être facilement cassée.Vous n'êtes pas autorisé à joindre un fichier à cette page.