MegaShapeSVG.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. using UnityEngine;
  2. using System;
  3. using System.Collections.Generic;
  4. #if !UNITY_FLASH && !UNITY_METRO && !UNITY_WP8
  5. using System.Text.RegularExpressions;
  6. #endif
  7. // Part of MegaShape?
  8. public class MegaShapeSVG
  9. {
  10. public void LoadXML(string svgdata, MegaShape shape, bool clear, int start)
  11. {
  12. MegaXMLReader xml = new MegaXMLReader();
  13. MegaXMLNode node = xml.read(svgdata);
  14. if ( !clear )
  15. shape.splines.Clear();
  16. shape.selcurve = start;
  17. splineindex = start;
  18. ParseXML(node, shape);
  19. }
  20. int splineindex = 0;
  21. public void ParseXML(MegaXMLNode node, MegaShape shape)
  22. {
  23. foreach ( MegaXMLNode n in node.children )
  24. {
  25. switch ( n.tagName )
  26. {
  27. case "circle": ParseCircle(n, shape); break;
  28. case "path": ParsePath(n, shape); break;
  29. case "ellipse": ParseEllipse(n, shape); break;
  30. case "rect": ParseRect(n, shape); break;
  31. case "polygon": ParsePolygon(n, shape); break;
  32. default: break;
  33. }
  34. ParseXML(n, shape);
  35. }
  36. }
  37. MegaSpline GetSpline(MegaShape shape)
  38. {
  39. MegaSpline spline;
  40. if ( splineindex < shape.splines.Count )
  41. spline = shape.splines[splineindex];
  42. else
  43. {
  44. spline = new MegaSpline();
  45. shape.splines.Add(spline);
  46. }
  47. splineindex++;
  48. return spline;
  49. }
  50. Vector3 SwapAxis(Vector3 val, MegaAxis axis)
  51. {
  52. float v = 0.0f;
  53. switch ( axis )
  54. {
  55. case MegaAxis.X:
  56. v = val.x;
  57. val.x = val.y;
  58. val.y = v;
  59. break;
  60. case MegaAxis.Y:
  61. break;
  62. case MegaAxis.Z:
  63. v = val.y;
  64. val.y = val.z;
  65. val.z = v;
  66. break;
  67. }
  68. return val;
  69. }
  70. void AddKnot(MegaSpline spline, Vector3 p, Vector3 invec, Vector3 outvec, MegaAxis axis)
  71. {
  72. spline.AddKnot(SwapAxis(p, axis), SwapAxis(invec, axis), SwapAxis(outvec, axis));
  73. }
  74. void ParseCircle(MegaXMLNode node, MegaShape shape)
  75. {
  76. MegaSpline spline = GetSpline(shape);
  77. float cx = 0.0f;
  78. float cy = 0.0f;
  79. float r = 0.0f;
  80. for ( int i = 0; i < node.values.Count; i++ )
  81. {
  82. MegaXMLValue val = node.values[i];
  83. switch ( val.name )
  84. {
  85. case "cx": cx = float.Parse(val.value); break;
  86. case "cy": cy = float.Parse(val.value); break;
  87. case "r": r = float.Parse(val.value); break;
  88. }
  89. }
  90. float vector = CIRCLE_VECTOR_LENGTH * r;
  91. spline.knots.Clear();
  92. for ( int ix = 0; ix < 4; ++ix )
  93. {
  94. float angle = (Mathf.PI * 2.0f) * (float)ix / (float)4;
  95. float sinfac = Mathf.Sin(angle);
  96. float cosfac = Mathf.Cos(angle);
  97. Vector3 p = new Vector3((cosfac * r) + cx, 0.0f, (sinfac * r) + cy);
  98. Vector3 rotvec = new Vector3(sinfac * vector, 0.0f, -cosfac * vector);
  99. //spline.AddKnot(p, p + rotvec, p - rotvec);
  100. AddKnot(spline, p, p + rotvec, p - rotvec, shape.axis);
  101. }
  102. spline.closed = true;
  103. }
  104. void ParseEllipse(MegaXMLNode node, MegaShape shape)
  105. {
  106. MegaSpline spline = GetSpline(shape);
  107. float cx = 0.0f;
  108. float cy = 0.0f;
  109. float rx = 0.0f;
  110. float ry = 0.0f;
  111. for ( int i = 0; i < node.values.Count; i++ )
  112. {
  113. MegaXMLValue val = node.values[i];
  114. switch ( val.name )
  115. {
  116. case "cx": cx = float.Parse(val.value); break;
  117. case "cy": cy = float.Parse(val.value); break;
  118. case "rx": rx = float.Parse(val.value); break;
  119. case "ry": ry = float.Parse(val.value); break;
  120. }
  121. }
  122. ry = Mathf.Clamp(ry, 0.0f, float.MaxValue);
  123. rx = Mathf.Clamp(rx, 0.0f, float.MaxValue);
  124. float radius, xmult, ymult;
  125. if ( ry < rx )
  126. {
  127. radius = rx;
  128. xmult = 1.0f;
  129. ymult = ry / rx;
  130. }
  131. else
  132. {
  133. if ( rx < ry )
  134. {
  135. radius = ry;
  136. xmult = rx / ry;
  137. ymult = 1.0f;
  138. }
  139. else
  140. {
  141. radius = ry;
  142. xmult = ymult = 1.0f;
  143. }
  144. }
  145. float vector = CIRCLE_VECTOR_LENGTH * radius;
  146. Vector3 mult = new Vector3(xmult, ymult, 1.0f);
  147. for ( int ix = 0; ix < 4; ++ix )
  148. {
  149. float angle = 6.2831853f * (float)ix / 4.0f;
  150. float sinfac = Mathf.Sin(angle);
  151. float cosfac = Mathf.Cos(angle);
  152. Vector3 p = new Vector3(cosfac * radius + cx, 0.0f, sinfac * radius + cy);
  153. Vector3 rotvec = new Vector3(sinfac * vector, 0.0f, -cosfac * vector);
  154. //spline.AddKnot(Vector3.Scale(p, mult), Vector3.Scale((p + rotvec), mult), Vector3.Scale((p - rotvec), mult)); //, tm);
  155. AddKnot(spline, Vector3.Scale(p, mult), Vector3.Scale((p + rotvec), mult), Vector3.Scale((p - rotvec), mult), shape.axis); //, tm);
  156. }
  157. spline.closed = true;
  158. }
  159. void ParseRect(MegaXMLNode node, MegaShape shape)
  160. {
  161. MegaSpline spline = GetSpline(shape);
  162. Vector3[] ppoints = new Vector3[4];
  163. float w = 0.0f;
  164. float h = 0.0f;
  165. float x = 0.0f;
  166. float y = 0.0f;
  167. for ( int i = 0; i < node.values.Count; i++ )
  168. {
  169. MegaXMLValue val = node.values[i];
  170. switch ( val.name )
  171. {
  172. case "x": x = float.Parse(val.value); break;
  173. case "y": y = float.Parse(val.value); break;
  174. case "width": w = float.Parse(val.value); break;
  175. case "height": h = float.Parse(val.value); break;
  176. case "transform": Debug.Log("SVG Transform not implemented yet");
  177. break;
  178. }
  179. }
  180. ppoints[0] = new Vector3(x, 0.0f, y);
  181. ppoints[1] = new Vector3(x, 0.0f, y + h);
  182. ppoints[2] = new Vector3(x + w, 0.0f, y + h);
  183. ppoints[3] = new Vector3(x + w, 0.0f, y);
  184. spline.closed = true;
  185. spline.knots.Clear();
  186. //spline.AddKnot(ppoints[0], ppoints[0], ppoints[0]);
  187. //spline.AddKnot(ppoints[1], ppoints[1], ppoints[1]);
  188. //spline.AddKnot(ppoints[2], ppoints[2], ppoints[2]);
  189. //spline.AddKnot(ppoints[3], ppoints[3], ppoints[3]);
  190. AddKnot(spline, ppoints[0], ppoints[0], ppoints[0], shape.axis);
  191. AddKnot(spline, ppoints[1], ppoints[1], ppoints[1], shape.axis);
  192. AddKnot(spline, ppoints[2], ppoints[2], ppoints[2], shape.axis);
  193. AddKnot(spline, ppoints[3], ppoints[3], ppoints[3], shape.axis);
  194. }
  195. void ParsePolygon(MegaXMLNode node, MegaShape shape)
  196. {
  197. MegaSpline spline = GetSpline(shape);
  198. spline.knots.Clear();
  199. spline.closed = true;
  200. char[] charSeparators = new char[] { ' ' };
  201. for ( int i = 0; i < node.values.Count; i++ )
  202. {
  203. MegaXMLValue val = node.values[i];
  204. switch ( val.name )
  205. {
  206. case "points":
  207. string[] coordinates = val.value.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
  208. for ( int j = 0; j < coordinates.Length; j++ )
  209. {
  210. Vector3 p = ParseV2Split(coordinates[j], 0);
  211. MegaKnot k = new MegaKnot();
  212. k.p = SwapAxis(new Vector3(p.x, 0.0f, p.y), shape.axis);
  213. k.invec = k.p;
  214. k.outvec = k.p;
  215. spline.knots.Add(k);
  216. }
  217. break;
  218. }
  219. }
  220. if ( spline.closed )
  221. {
  222. Vector3 delta1 = spline.knots[0].outvec - spline.knots[0].p;
  223. spline.knots[0].invec = spline.knots[0].p - delta1;
  224. }
  225. }
  226. char[] commaspace = new char[] { ',', ' ' };
  227. void ParsePath(MegaXMLNode node, MegaShape shape)
  228. {
  229. Vector3 cp = Vector3.zero;
  230. Vector2 cP1;
  231. Vector2 cP2;
  232. char[] charSeparators = new char[] { ',', ' ' };
  233. MegaSpline spline = null;
  234. MegaKnot k;
  235. string[] coord;
  236. for ( int i = 0; i < node.values.Count; i++ )
  237. {
  238. MegaXMLValue val = node.values[i];
  239. //Debug.Log("val name " + val.name);
  240. switch ( val.name )
  241. {
  242. case "d":
  243. #if UNITY_FLASH || UNITY_METRO || UNITY_WP8
  244. string[] coordinates = null; //string.Split(val.value, @"(?=[MmLlCcSsZzHhVv])");
  245. #else
  246. string[] coordinates = Regex.Split(val.value, @"(?=[MmLlCcSsZzHhVv])");
  247. #endif
  248. for ( int j = 0; j < coordinates.Length; j++ )
  249. {
  250. if ( coordinates[j].Length > 0 )
  251. {
  252. string v = coordinates[j].Substring(1);
  253. if ( v != null && v.Length > 0 )
  254. {
  255. v = v.Replace("-", ",-");
  256. while ( v.Length > 0 && (v[0] == ',' || v[0] == ' ') )
  257. v = v.Substring(1);
  258. }
  259. switch ( coordinates[j][0] )
  260. {
  261. case 'Z':
  262. case 'z':
  263. if ( spline != null )
  264. {
  265. spline.closed = true;
  266. #if false
  267. Vector3 delta1 = spline.knots[0].outvec - spline.knots[0].p;
  268. spline.knots[0].invec = spline.knots[0].p - delta1;
  269. if ( spline.knots[0].p == spline.knots[spline.knots.Count - 1].p )
  270. spline.knots.Remove(spline.knots[spline.knots.Count - 1]);
  271. #else
  272. int kc = spline.knots.Count - 1;
  273. spline.knots[0].invec = spline.knots[kc].invec;
  274. spline.knots.Remove(spline.knots[kc]);
  275. #endif
  276. }
  277. break;
  278. case 'M':
  279. spline = GetSpline(shape);
  280. spline.knots.Clear();
  281. cp = ParseV2Split(v, 0);
  282. k = new MegaKnot();
  283. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  284. k.invec = k.p;
  285. k.outvec = k.p;
  286. spline.knots.Add(k);
  287. break;
  288. case 'm':
  289. spline = GetSpline(shape);
  290. spline.knots.Clear();
  291. //Debug.Log("v: " + v);
  292. coord = v.Split(" "[0]);
  293. //Debug.Log("m coords " + coord.Length);
  294. //Debug.Log("v2 " + coord[0]);
  295. for ( int k0 = 0; k0 < coord.Length - 1; k0 = k0 + 1 )
  296. {
  297. //Debug.Log("v2 " + coord[k0]);
  298. Vector3 cp1 = ParseV2Split(coord[k0], 0);
  299. //Debug.Log("cp1 " + cp1);
  300. cp.x += cp1.x; //ParseV2Split(coord[k0], 0); // ParseV2(coord, k0);
  301. cp.y += cp1.y;
  302. k = new MegaKnot();
  303. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  304. k.invec = k.p;
  305. k.outvec = k.p;
  306. spline.knots.Add(k);
  307. }
  308. #if false
  309. Vector3 cp1 = ParseV2Split(v, 0);
  310. cp.x += cp1.x;
  311. cp.y += cp1.y;
  312. k = new MegaKnot();
  313. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  314. k.invec = k.p;
  315. k.outvec = k.p;
  316. spline.knots.Add(k);
  317. #endif
  318. break;
  319. case 'l':
  320. coord = v.Split(","[0]);
  321. for ( int k0 = 0; k0 < coord.Length; k0 = k0 + 2 )
  322. cp += ParseV2(coord, k0);
  323. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  324. k = new MegaKnot();
  325. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  326. k.invec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  327. k.outvec = k.p - (k.invec - k.p);
  328. spline.knots.Add(k);
  329. break;
  330. case 'c':
  331. coord = v.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
  332. for ( int k2 = 0; k2 < coord.Length; k2 += 6 )
  333. {
  334. cP1 = cp + ParseV2(coord, k2);
  335. cP2 = cp + ParseV2(coord, k2 + 2);
  336. cp += ParseV2(coord, k2 + 4);
  337. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cP1.x, 0.0f, cP1.y), shape.axis);
  338. k = new MegaKnot();
  339. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  340. k.invec = SwapAxis(new Vector3(cP2.x, 0.0f, cP2.y), shape.axis);
  341. k.outvec = k.p - (k.invec - k.p);
  342. spline.knots.Add(k);
  343. }
  344. break;
  345. case 'L':
  346. coord = v.Split(","[0]);
  347. for ( int k3 = 0; k3 < coord.Length; k3 = k3 + 2 )
  348. cp = ParseV2(coord, k3);
  349. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  350. k = new MegaKnot();
  351. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  352. k.invec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  353. k.outvec = k.p - (k.invec - k.p);
  354. spline.knots.Add(k);
  355. break;
  356. case 'v':
  357. //Debug.Log("v: " + v);
  358. coord = v.Split(","[0]);
  359. for ( int k4 = 0; k4 < coord.Length; k4++ )
  360. cp.y += float.Parse(coord[k4]);
  361. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  362. k = new MegaKnot();
  363. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  364. k.invec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  365. k.outvec = k.p - (k.invec - k.p);
  366. spline.knots.Add(k);
  367. break;
  368. case 'V':
  369. coord = v.Split(","[0]);
  370. for ( int k9 = 0; k9 < coord.Length; k9++ )
  371. cp.y = float.Parse(coord[k9]);
  372. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  373. k = new MegaKnot();
  374. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  375. k.invec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  376. k.outvec = k.p - (k.invec - k.p);
  377. spline.knots.Add(k);
  378. break;
  379. case 'h':
  380. coord = v.Split(","[0]);
  381. for ( int k5 = 0; k5 < coord.Length; k5++ )
  382. cp.x += float.Parse(coord[k5]);
  383. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  384. k = new MegaKnot();
  385. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  386. k.invec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  387. k.outvec = k.p - (k.invec - k.p);
  388. spline.knots.Add(k);
  389. break;
  390. case 'H':
  391. coord = v.Split(","[0]);
  392. for ( int k6 = 0; k6 < coord.Length; k6++ )
  393. cp.x = float.Parse(coord[k6]);
  394. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  395. k = new MegaKnot();
  396. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  397. k.invec = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  398. k.outvec = k.p - (k.invec - k.p);
  399. spline.knots.Add(k);
  400. break;
  401. case 'S':
  402. coord = v.Split(","[0]);
  403. for ( int k7 = 0; k7 < coord.Length; k7 = k7 + 4 )
  404. {
  405. cp = ParseV2(coord, k7 + 2);
  406. cP1 = ParseV2(coord, k7);
  407. k = new MegaKnot();
  408. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  409. k.invec = SwapAxis(new Vector3(cP1.x, 0.0f, cP1.y), shape.axis);
  410. k.outvec = k.p - (k.invec - k.p);
  411. spline.knots.Add(k);
  412. }
  413. break;
  414. case 's':
  415. coord = v.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
  416. for ( int k7 = 0; k7 < coord.Length; k7 = k7 + 4 )
  417. {
  418. cP1 = cp + ParseV2(coord, k7);
  419. cp += ParseV2(coord, k7 + 2);
  420. k = new MegaKnot();
  421. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  422. k.invec = SwapAxis(new Vector3(cP1.x, 0.0f, cP1.y), shape.axis);
  423. k.outvec = k.p - (k.invec - k.p);
  424. spline.knots.Add(k);
  425. }
  426. break;
  427. case 'C':
  428. coord = v.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries);
  429. for ( int k2 = 0; k2 < coord.Length; k2 += 6 )
  430. {
  431. cP1 = ParseV2(coord, k2);
  432. cP2 = ParseV2(coord, k2 + 2);
  433. cp = ParseV2(coord, k2 + 4);
  434. spline.knots[spline.knots.Count - 1].outvec = SwapAxis(new Vector3(cP1.x, 0.0f, cP1.y), shape.axis);
  435. k = new MegaKnot();
  436. k.p = SwapAxis(new Vector3(cp.x, 0.0f, cp.y), shape.axis);
  437. k.invec = SwapAxis(new Vector3(cP2.x, 0.0f, cP2.y), shape.axis);
  438. k.outvec = k.p - (k.invec - k.p);
  439. spline.knots.Add(k);
  440. }
  441. break;
  442. default:
  443. break;
  444. }
  445. }
  446. }
  447. break;
  448. }
  449. }
  450. }
  451. public void importData(string svgdata, MegaShape shape, float scale, bool clear, int start)
  452. {
  453. LoadXML(svgdata, shape, clear, start);
  454. for ( int i = start; i < splineindex; i++ )
  455. {
  456. float area = shape.splines[i].Area();
  457. if ( area < 0.0f )
  458. shape.splines[i].reverse = false;
  459. else
  460. shape.splines[i].reverse = true;
  461. }
  462. //shape.Centre(0.01f, new Vector3(-1.0f, 1.0f, 1.0f));
  463. shape.Centre(scale, new Vector3(-1.0f, 1.0f, 1.0f), start);
  464. shape.CalcLength(); //10);
  465. }
  466. const float CIRCLE_VECTOR_LENGTH = 0.5517861843f;
  467. Vector2 ParseV2Split(string str, int i)
  468. {
  469. return ParseV2(str.Split(commaspace, StringSplitOptions.RemoveEmptyEntries), i);
  470. }
  471. Vector3 ParseV2(string[] str, int i)
  472. {
  473. Vector3 p = Vector2.zero;
  474. p.x = float.Parse(str[i]);
  475. p.y = float.Parse(str[i + 1]);
  476. return p;
  477. }
  478. static public string Export(MegaShape shape, int x, int y, float strokewidth, Color col)
  479. {
  480. string file = "";
  481. Color32 c = col;
  482. file += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
  483. file += "<!-- MegaShapes SVG Exporter v1.0 -->\n";
  484. file += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
  485. file += "<svg version=\"1.1\" id=\"" + shape.name + "\" x=\"0px\" y=\"0px\" width=\"640.0px\" height=\"480.0px\">\n";
  486. for ( int i = 0; i < shape.splines.Count; i++ )
  487. {
  488. MegaSpline spline = shape.splines[i];
  489. file += "<path d=\"";
  490. MegaKnot k1;
  491. MegaKnot k = spline.knots[0];
  492. k1 = k;
  493. file += "M" + k.p[x] + "," + -k.p[y];
  494. //Vector3 cp = k.p;
  495. for ( int j = 1; j < spline.knots.Count; j++ )
  496. {
  497. k = spline.knots[j];
  498. Vector3 po = k1.outvec; // - cp; // - k1.p;
  499. Vector3 pi = k.invec; // - cp; // - k.p;
  500. Vector3 kp = k.p; // - cp;
  501. kp[y] = -kp[y];
  502. po[y] = -po[y];
  503. pi[y] = -pi[y];
  504. file += "C" + po[x] + "," + po[y];
  505. file += " " + pi[x] + "," + pi[y];
  506. file += " " + kp[x] + "," + kp[y];
  507. k1 = k;
  508. }
  509. if ( spline.closed )
  510. {
  511. file += "z\"";
  512. }
  513. file += " fill=\"none\"";
  514. file += " stroke=\"#" + c.r.ToString("x") + c.g.ToString("x") + c.b.ToString("x") + "\"";
  515. file += " stroke-width=\"" + strokewidth + "\"";
  516. file += "/>\n";
  517. }
  518. file += "</svg>\n";
  519. return file;
  520. }
  521. }