x_ite 8.6.3 → 8.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/assets/components/Annotation.js +13 -13
  2. package/dist/assets/components/Annotation.min.js +1 -1
  3. package/dist/assets/components/CADGeometry.js +13 -13
  4. package/dist/assets/components/CADGeometry.min.js +1 -1
  5. package/dist/assets/components/CubeMapTexturing.js +25 -25
  6. package/dist/assets/components/CubeMapTexturing.min.js +1 -1
  7. package/dist/assets/components/DIS.js +13 -13
  8. package/dist/assets/components/DIS.min.js +1 -1
  9. package/dist/assets/components/EventUtilities.js +9 -9
  10. package/dist/assets/components/EventUtilities.min.js +1 -1
  11. package/dist/assets/components/Geometry2D.js +19 -19
  12. package/dist/assets/components/Geometry2D.min.js +1 -1
  13. package/dist/assets/components/Geospatial.js +33 -33
  14. package/dist/assets/components/Geospatial.min.js +1 -1
  15. package/dist/assets/components/HAnim.js +18 -18
  16. package/dist/assets/components/HAnim.min.js +1 -1
  17. package/dist/assets/components/KeyDeviceSensor.js +8 -8
  18. package/dist/assets/components/KeyDeviceSensor.min.js +1 -1
  19. package/dist/assets/components/Layout.js +27 -27
  20. package/dist/assets/components/Layout.min.js +1 -1
  21. package/dist/assets/components/NURBS.js +24 -24
  22. package/dist/assets/components/NURBS.min.js +1 -1
  23. package/dist/assets/components/ParticleSystems.js +23 -23
  24. package/dist/assets/components/ParticleSystems.min.js +1 -1
  25. package/dist/assets/components/Picking.js +19 -19
  26. package/dist/assets/components/Picking.min.js +1 -1
  27. package/dist/assets/components/RigidBodyPhysics.js +18 -18
  28. package/dist/assets/components/RigidBodyPhysics.min.js +1 -1
  29. package/dist/assets/components/Scripting.js +28 -28
  30. package/dist/assets/components/Scripting.min.js +1 -1
  31. package/dist/assets/components/Text.js +24 -24
  32. package/dist/assets/components/Text.min.js +1 -1
  33. package/dist/assets/components/TextureProjector.js +14 -14
  34. package/dist/assets/components/TextureProjector.min.js +1 -1
  35. package/dist/assets/components/Texturing3D.js +30 -30
  36. package/dist/assets/components/Texturing3D.min.js +1 -1
  37. package/dist/assets/components/VolumeRendering.js +19 -19
  38. package/dist/assets/components/VolumeRendering.min.js +1 -1
  39. package/dist/assets/components/X_ITE.js +9 -9
  40. package/dist/assets/components/X_ITE.min.js +1 -1
  41. package/dist/x_ite.css +1 -1
  42. package/dist/x_ite.js +1047 -823
  43. package/dist/x_ite.min.js +1 -1
  44. package/dist/x_ite.zip +0 -0
  45. package/docs/_config.yml +2 -2
  46. package/docs/_posts/getting-started.md +1 -1
  47. package/docs/_posts/laboratory/x3d-file-converter.md +1 -1
  48. package/docs/_tabs/laboratory.md +6 -0
  49. package/package.json +1 -1
  50. package/src/standard/Math/Numbers/Matrix4.js +7 -0
  51. package/src/x_ite/Browser/VERSION.js +1 -1
  52. package/src/x_ite/Parser/OBJParser.js +29 -22
  53. package/src/x_ite/Parser/SVGParser.js +483 -273
  54. package/src/x_ite/Parser/X3DParser.js +2 -2
@@ -57,8 +57,10 @@ import Vector4 from "../../standard/Math/Numbers/Vector4.js";
57
57
  import Rotation4 from "../../standard/Math/Numbers/Rotation4.js";
58
58
  import Matrix3 from "../../standard/Math/Numbers/Matrix3.js";
59
59
  import Matrix4 from "../../standard/Math/Numbers/Matrix4.js";
60
+ import Complex from "../../standard/Math/Numbers/Complex.js";
60
61
  import Box2 from "../../standard/Math/Geometry/Box2.js"
61
62
  import Bezier from "../../standard/Math/Algorithms/Bezier.js";
63
+ import MatrixStack from "../../standard/Math/Utility/MatrixStack.js";
62
64
 
63
65
  /*
64
66
  * Grammar
@@ -73,7 +75,7 @@ const Grammar = Expressions ({
73
75
  closeParenthesis: /\)/gy,
74
76
 
75
77
  // Units
76
- length: /(em|ex|px|in|cm|mm|pt|pc)/gy,
78
+ length: /(em|ex|px|in|cm|mm|pt|pc|%)/gy,
77
79
  percent: /%/gy,
78
80
 
79
81
  // Values
@@ -96,16 +98,14 @@ const Grammar = Expressions ({
96
98
  */
97
99
 
98
100
  const
99
- MM = 0.001, // One mm in meters.
100
- CM = 0.01, // One cm in meters.
101
- INCH = 0.0254, // One inch in meters.
102
- POINT = INCH / 72, // One point in meters.
103
- PICA = INCH / 6, // One pica in meters.
104
- PIXEL = INCH / 90, // One pixel in meters.
105
- EM = 16, // One em in pixels,
106
- BEZIER_STEPS = 10, // Subdivisions of a span.
107
- CIRCLE_STEPS = 64, // Subdivisions of a circle, used for arc.
108
- GRADIENT_SIZE = 256; // In pixels.
101
+ MM = 0.001, // One mm in meters.
102
+ CM = 0.01, // One cm in meters.
103
+ INCH = 0.0254, // One inch in meters.
104
+ POINT = INCH / 72, // One point in meters.
105
+ PICA = INCH / 6, // One pica in meters.
106
+ PIXEL = INCH / 90, // One pixel in meters.
107
+ EM = 16, // One em in pixels.
108
+ SPREAD = 16; // Spread factor, Integer.
109
109
 
110
110
  /*
111
111
  * Parser
@@ -124,17 +124,17 @@ function SVGParser (scene)
124
124
 
125
125
  // Options
126
126
 
127
- this .solid = false; // Are 2D primitives solid.
127
+ this .solid = false; // Are 2D primitives solid?
128
128
 
129
129
  // Globals
130
130
 
131
- this .nodes = new Map ();
132
- this .tessy = this .createTesselator ();
133
- this .canvas = document .createElement ("canvas");
134
- this .context = this .canvas .getContext ("2d");
135
-
136
- this .canvas .width = GRADIENT_SIZE;
137
- this .canvas .height = GRADIENT_SIZE;
131
+ this .viewBox = new Vector4 (0, 0, 100, 100);
132
+ this .modelMatrix = new MatrixStack (Matrix4);
133
+ this .nodes = new Map ();
134
+ this .lineProperties = new Map ();
135
+ this .tessy = this .createTesselator ();
136
+ this .canvas = document .createElement ("canvas");
137
+ this .context = this .canvas .getContext ("2d");
138
138
 
139
139
  this .styles = [{
140
140
  display: "inline",
@@ -143,7 +143,7 @@ function SVGParser (scene)
143
143
  fillURL: "",
144
144
  fillOpacity: 1,
145
145
  fillRule: "nonzero",
146
- strokeType: "NONE",
146
+ strokeType: "none",
147
147
  strokeColor: Color4 .Black,
148
148
  strokeURL: "",
149
149
  strokeOpacity: 1,
@@ -151,7 +151,44 @@ function SVGParser (scene)
151
151
  opacity: 1,
152
152
  stopColor: Color4 .Black,
153
153
  stopOpacity: 1,
154
+ vectorEffect: "none",
154
155
  }];
156
+
157
+ // Constants
158
+
159
+ const browser = scene .getBrowser ()
160
+
161
+ switch (browser .getBrowserOption ("PrimitiveQuality"))
162
+ {
163
+ case "LOW":
164
+ this .BEZIER_STEPS = 6; // Subdivisions of a span.
165
+ this .CIRCLE_STEPS = 20; // Subdivisions of a circle, used for arc and rounded rect.
166
+ break;
167
+ case "HIGH":
168
+ this .BEZIER_STEPS = 10; // Subdivisions of a span.
169
+ this .CIRCLE_STEPS = 64; // Subdivisions of a circle, used for arc and rounded rect.
170
+ break;
171
+ default:
172
+ this .BEZIER_STEPS = 8; // Subdivisions of a span.
173
+ this .CIRCLE_STEPS = 32; // Subdivisions of a circle, used for arc and rounded rect.
174
+ break;
175
+ }
176
+
177
+ switch (browser .getBrowserOption ("TextureQuality"))
178
+ {
179
+ case "LOW":
180
+ this .GRADIENT_SIZE = 128; // In pixels.
181
+ break;
182
+ case "HIGH":
183
+ this .GRADIENT_SIZE = 512; // In pixels.
184
+ break;
185
+ default:
186
+ this .GRADIENT_SIZE = 256; // In pixels.
187
+ break;
188
+ }
189
+
190
+ this .canvas .width = this .GRADIENT_SIZE;
191
+ this .canvas .height = this .GRADIENT_SIZE;
155
192
  }
156
193
 
157
194
  SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
@@ -260,17 +297,31 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
260
297
  // Get attributes of svg element.
261
298
 
262
299
  const
263
- defaultWidth = this .lengthAttribute (xmlElement .getAttribute ("width", 100)),
264
- defaultHeight = this .lengthAttribute (xmlElement .getAttribute ("height", 100)),
265
- defaultViewBox = new Vector4 (0, 0, defaultWidth, defaultHeight),
300
+ defaultWidth = this .lengthAttribute (xmlElement .getAttribute ("width"), 300, "width"),
301
+ defaultHeight = this .lengthAttribute (xmlElement .getAttribute ("height"), 150, "height"),
302
+ defaultViewBox = this .viewBox .set (0, 0, defaultWidth, defaultHeight),
266
303
  viewBox = this .viewBoxAttribute (xmlElement .getAttribute ("viewBox"), defaultViewBox),
267
- width = this .lengthAttribute (xmlElement .getAttribute ("width", viewBox [2])),
268
- height = this .lengthAttribute (xmlElement .getAttribute ("height", viewBox [3]));
304
+ width = this .lengthAttribute (xmlElement .getAttribute ("width"), viewBox [2], "width"),
305
+ height = this .lengthAttribute (xmlElement .getAttribute ("height"), viewBox [3], "height");
306
+
307
+ if (true) // default
308
+ {
309
+ // preserveAspectRatio = "xMidYMid meet"
310
+
311
+ const
312
+ r = width / height,
313
+ rv = viewBox [2] / viewBox [3];
314
+
315
+ if (rv > r)
316
+ viewBox [3] += viewBox [2] / r - viewBox [3];
317
+ else
318
+ viewBox [2] += viewBox [3] * r - viewBox [2];
319
+ }
269
320
 
270
321
  // Create viewpoint.
271
322
 
272
323
  const
273
- viewpoint = scene .createNode ("OrthoViewpoint",),
324
+ viewpoint = scene .createNode ("OrthoViewpoint"),
274
325
  x = (viewBox .x + width / 2) * PIXEL,
275
326
  y = -(viewBox .y + height / 2) * PIXEL;
276
327
 
@@ -374,10 +425,10 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
374
425
  // Create Transform node.
375
426
 
376
427
  const
377
- x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0),
378
- y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0),
379
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0),
380
- height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0);
428
+ x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0, "width"),
429
+ y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0, "height"),
430
+ width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0, "width"),
431
+ height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0, "height");
381
432
 
382
433
  const transformNode = this .createTransform (xmlElement, new Vector2 (x, y));
383
434
 
@@ -385,8 +436,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
385
436
 
386
437
  this .element (usedElement);
387
438
 
388
- this .styles .pop ();
389
- this .groupNodes .pop ();
439
+ this .groupNodes .pop ();
440
+ this .modelMatrix .pop ();
441
+ this .styles .pop ();
390
442
 
391
443
  // Add node.
392
444
 
@@ -409,8 +461,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
409
461
 
410
462
  this .elements (xmlElement);
411
463
 
412
- this .styles .pop ();
413
- this .groupNodes .pop ();
464
+ this .groupNodes .pop ();
465
+ this .modelMatrix .pop ();
466
+ this .styles .pop ();
414
467
 
415
468
  // Add node.
416
469
 
@@ -439,8 +492,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
439
492
 
440
493
  this .elements (xmlElement);
441
494
 
442
- this .styles .pop ();
443
- this .groupNodes .pop ();
495
+ this .groupNodes .pop ();
496
+ this .modelMatrix .pop ();
497
+ this .styles .pop ();
444
498
 
445
499
  // Add node.
446
500
 
@@ -481,75 +535,119 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
481
535
 
482
536
  this .elements (xmlElement);
483
537
 
484
- this .groupNodes .pop ();
485
- this .styles .pop ();
538
+ this .groupNodes .pop ();
539
+ this .modelMatrix .pop ();
540
+ this .styles .pop ();
486
541
 
487
542
  this .groupNodes .at (-1) .children .push (transformNode);
488
543
  },
489
544
  rectElement: function (xmlElement)
490
545
  {
491
- // Determine style.
492
-
493
- if (!this .styleAttributes (xmlElement))
494
- return;
495
-
496
546
  // Create Transform node.
497
547
 
498
548
  const
499
- x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0),
500
- y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0),
501
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0),
502
- height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0);
549
+ x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0, "width"),
550
+ y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0, "height"),
551
+ width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0, "width"),
552
+ height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0, "height");
503
553
 
504
- const
505
- scene = this .getExecutionContext (),
506
- size = new Vector2 (width, height),
507
- center = new Vector2 (x + width / 2, y + height / 2),
508
- bbox = new Box2 (size, center),
509
- transformNode = this .createTransform (xmlElement, center);
554
+ let
555
+ rx = Math .max (0, this .lengthAttribute (xmlElement .getAttribute ("rx"), 0, "width")),
556
+ ry = Math .max (0, this .lengthAttribute (xmlElement .getAttribute ("ry"), 0, "height"));
510
557
 
511
- this .groupNodes .push (transformNode);
558
+ if (rx === 0 && ry === 0)
559
+ {
560
+ // Determine style.
512
561
 
513
- // Create nodes.
562
+ if (!this .styleAttributes (xmlElement))
563
+ return;
564
+
565
+ // Create Transform node.
514
566
 
515
- if (this .style .fillType !== "NONE")
516
- {
517
567
  const
518
- shapeNode = scene .createNode ("Shape"),
519
- rectangleNode = scene .createNode ("Rectangle2D");
568
+ scene = this .getExecutionContext (),
569
+ size = new Vector2 (width, height),
570
+ center = new Vector2 (x + width / 2, y + height / 2),
571
+ bbox = new Box2 (size, center),
572
+ transformNode = this .createTransform (xmlElement, center);
520
573
 
521
- shapeNode .appearance = this .createFillAppearance (bbox);
522
- shapeNode .geometry = rectangleNode;
523
- rectangleNode .solid = this .solid;
524
- rectangleNode .size = size;
574
+ this .groupNodes .push (transformNode);
525
575
 
526
- transformNode .children .push (shapeNode);
527
- }
576
+ // Create nodes.
577
+
578
+ if (this .style .fillType !== "none")
579
+ {
580
+ const
581
+ shapeNode = scene .createNode ("Shape"),
582
+ rectangleNode = scene .createNode ("Rectangle2D");
583
+
584
+ shapeNode .appearance = this .createFillAppearance (bbox);
585
+ shapeNode .geometry = rectangleNode;
586
+ rectangleNode .solid = this .solid;
587
+ rectangleNode .size = size;
588
+
589
+ transformNode .children .push (shapeNode);
590
+ }
591
+
592
+ if (this .style .strokeType !== "none")
593
+ {
594
+ const
595
+ shapeNode = scene .createNode ("Shape"),
596
+ polylineNode = scene .createNode ("Polyline2D"),
597
+ width1_2 = width / 2,
598
+ height1_2 = height / 2;
599
+
600
+ shapeNode .appearance = this .createStrokeAppearance ();
601
+ shapeNode .geometry = polylineNode;
602
+
603
+ polylineNode .lineSegments = [ width1_2, height1_2,
604
+ -width1_2, height1_2,
605
+ -width1_2, -height1_2,
606
+ width1_2, -height1_2,
607
+ width1_2, height1_2];
528
608
 
529
- if (this .style .strokeType !== "NONE")
609
+ transformNode .children .push (shapeNode);
610
+ }
611
+
612
+ this .groupNodes .pop ();
613
+ this .modelMatrix .pop ();
614
+ this .styles .pop ();
615
+
616
+ this .groupNodes .at (-1) .children .push (transformNode);
617
+ }
618
+ else
530
619
  {
620
+ // Create points.
621
+
622
+ if (rx && !ry) ry = rx;
623
+ if (ry && !rx) rx = ry;
624
+
625
+ rx = Math .min (rx, width / 2);
626
+ ry = Math .min (ry, height / 2);
627
+
531
628
  const
532
- shapeNode = scene .createNode ("Shape"),
533
- polylineNode = scene .createNode ("Polyline2D"),
534
- width1_2 = width / 2,
535
- height1_2 = height / 2;
629
+ xOffsets = [x + width - rx, x + rx , x + rx, x + width - rx],
630
+ yOffsets = [y + height - ry, y + height - ry, y + ry, y + ry],
631
+ points = Object .assign ([ ], { index: 0, closed: true });
536
632
 
537
- shapeNode .appearance = this .createStrokeAppearance ();
538
- shapeNode .geometry = polylineNode;
633
+ for (let c = 0; c < 4; ++ c)
634
+ {
635
+ const s = c * Math .PI / 2;
539
636
 
540
- polylineNode .lineSegments = [ width1_2, height1_2,
541
- -width1_2, height1_2,
542
- -width1_2, -height1_2,
543
- width1_2, -height1_2,
544
- width1_2, height1_2];
637
+ for (let i = 0, N = this .CIRCLE_STEPS / 4; i < N; ++ i)
638
+ {
639
+ const p = Complex .Polar (1, s + Math .PI / 2 * i / (N - 1));
545
640
 
546
- transformNode .children .push (shapeNode);
547
- }
641
+ points .push (new Vector3 (xOffsets [c] + p .real * rx, yOffsets [c] + p .imag * ry, 0));
642
+ }
643
+ }
548
644
 
549
- this .groupNodes .pop ();
550
- this .styles .pop ();
645
+ points .pop ();
551
646
 
552
- this .groupNodes .at (-1) .children .push (transformNode);
647
+ // Create nodes.
648
+
649
+ this .pathLikeElement (xmlElement, [points]);
650
+ }
553
651
  },
554
652
  circleElement: function (xmlElement)
555
653
  {
@@ -561,8 +659,8 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
561
659
  // Create Transform node.
562
660
 
563
661
  const
564
- cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0),
565
- cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0),
662
+ cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0, "width"),
663
+ cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0, "height"),
566
664
  r = this .lengthAttribute (xmlElement .getAttribute ("r"), 0);
567
665
 
568
666
  const
@@ -574,7 +672,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
574
672
 
575
673
  // Create nodes.
576
674
 
577
- if (this .style .fillType !== "NONE")
675
+ if (this .style .fillType !== "none")
578
676
  {
579
677
  const
580
678
  shapeNode = scene .createNode ("Shape"),
@@ -588,7 +686,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
588
686
  transformNode .children .push (shapeNode);
589
687
  }
590
688
 
591
- if (this .style .strokeType !== "NONE")
689
+ if (this .style .strokeType !== "none")
592
690
  {
593
691
  const
594
692
  shapeNode = scene .createNode ("Shape"),
@@ -601,8 +699,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
601
699
  transformNode .children .push (shapeNode);
602
700
  }
603
701
 
604
- this .groupNodes .pop ();
605
- this .styles .pop ();
702
+ this .groupNodes .pop ();
703
+ this .modelMatrix .pop ();
704
+ this .styles .pop ();
606
705
 
607
706
  this .groupNodes .at (-1) .children .push (transformNode);
608
707
  },
@@ -616,10 +715,10 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
616
715
  // Create Transform node.
617
716
 
618
717
  const
619
- cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0),
620
- cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0),
621
- rx = this .lengthAttribute (xmlElement .getAttribute ("rx"), 0),
622
- ry = this .lengthAttribute (xmlElement .getAttribute ("ry"), 0);
718
+ cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0, "width"),
719
+ cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0, "height"),
720
+ rx = this .lengthAttribute (xmlElement .getAttribute ("rx"), 0, "width"),
721
+ ry = this .lengthAttribute (xmlElement .getAttribute ("ry"), 0, "height");
623
722
 
624
723
  const
625
724
  scene = this .getExecutionContext (),
@@ -631,7 +730,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
631
730
 
632
731
  // Create nodes.
633
732
 
634
- if (this .style .fillType !== "NONE")
733
+ if (this .style .fillType !== "none")
635
734
  {
636
735
  const
637
736
  shapeNode = scene .createNode ("Shape"),
@@ -645,7 +744,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
645
744
  transformNode .children .push (shapeNode);
646
745
  }
647
746
 
648
- if (this .style .strokeType !== "NONE")
747
+ if (this .style .strokeType !== "none")
649
748
  {
650
749
  const
651
750
  shapeNode = scene .createNode ("Shape"),
@@ -658,8 +757,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
658
757
  transformNode .children .push (shapeNode);
659
758
  }
660
759
 
661
- this .groupNodes .pop ();
662
- this .styles .pop ();
760
+ this .groupNodes .pop ();
761
+ this .modelMatrix .pop ();
762
+ this .styles .pop ();
663
763
 
664
764
  this .groupNodes .at (-1) .children .push (transformNode);
665
765
  },
@@ -669,13 +769,18 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
669
769
  },
670
770
  imageElement: function (xmlElement)
671
771
  {
772
+ // Determine style.
773
+
774
+ if (!this .styleAttributes (xmlElement))
775
+ return;
776
+
672
777
  // Create Transform node.
673
778
 
674
779
  const
675
- x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0),
676
- y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0),
677
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0),
678
- height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0),
780
+ x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0, "width"),
781
+ y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0, "height"),
782
+ width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0, "width"),
783
+ height = this .lengthAttribute (xmlElement .getAttribute ("height"), 0, "height"),
679
784
  href = xmlElement .getAttribute ("xlink:href");
680
785
 
681
786
  const
@@ -702,74 +807,37 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
702
807
 
703
808
  transformNode .children .push (shapeNode);
704
809
 
705
- this .groupNodes .pop ();
810
+ this .groupNodes .pop ();
811
+ this .modelMatrix .pop ();
812
+ this .styles .pop ();
813
+
706
814
  this .groupNodes .at (-1) .children .push (transformNode);
707
815
  },
708
- polylineElement: function (xmlElement, closed = false)
816
+ polylineElement: function (xmlElement)
709
817
  {
818
+ // Get points.
819
+
710
820
  const points = Object .assign ([ ], { index: 0 });
711
821
 
712
822
  if (!this .pointsAttribute (xmlElement .getAttribute ("points"), points))
713
823
  return;
714
824
 
715
- // Determine style.
716
-
717
- if (!this .styleAttributes (xmlElement))
718
- return;
719
-
720
- // Create Transform node.
721
-
722
- const
723
- scene = this .getExecutionContext (),
724
- transformNode = this .createTransform (xmlElement),
725
- bbox = new Box2 (Vector2 .min (... points), Vector2 .max (... points), true);
726
-
727
- this .groupNodes .push (transformNode);
728
-
729
825
  // Create nodes.
730
826
 
731
- const coordinateNode = scene .createNode ("Coordinate");
732
-
733
- coordinateNode .point .push (... points);
734
-
735
- if (this .style .fillType !== "NONE")
736
- {
737
- const
738
- shapeNode = scene .createNode ("Shape"),
739
- geometryNode = scene .createNode ("IndexedTriangleSet");
740
-
741
- shapeNode .appearance = this .createFillAppearance (bbox);
742
- shapeNode .geometry = geometryNode;
743
- geometryNode .solid = this .solid;
744
- geometryNode .index = this .triangulatePolygon ([points], coordinateNode);
745
- geometryNode .texCoord = this .createTextureCoordinate (coordinateNode, bbox);
746
- geometryNode .coord = coordinateNode;
747
-
748
- transformNode .children .push (shapeNode);
749
- }
827
+ this .pathLikeElement (xmlElement, [points]);
828
+ },
829
+ polygonElement: function (xmlElement)
830
+ {
831
+ // Get points.
750
832
 
751
- if (this .style .strokeType !== "NONE")
752
- {
753
- const
754
- shapeNode = scene .createNode ("Shape"),
755
- geometryNode = scene .createNode ("IndexedLineSet");
833
+ const points = Object .assign ([ ], { index: 0, closed: true });
756
834
 
757
- shapeNode .appearance = this .createStrokeAppearance ();
758
- shapeNode .geometry = geometryNode;
759
- geometryNode .coordIndex = [... points .keys (), ... (closed ? [points [0]] : [ ]), -1];
760
- geometryNode .coord = coordinateNode;
761
-
762
- transformNode .children .push (shapeNode);
763
- }
835
+ if (!this .pointsAttribute (xmlElement .getAttribute ("points"), points))
836
+ return;
764
837
 
765
- this .groupNodes .pop ();
766
- this .styles .pop ();
838
+ // Create nodes.
767
839
 
768
- this .groupNodes .at (-1) .children .push (transformNode);
769
- },
770
- polygonElement: function (xmlElement)
771
- {
772
- this .polylineElement (xmlElement, true);
840
+ this .pathLikeElement (xmlElement, [points]);
773
841
  },
774
842
  pathElement: function (xmlElement)
775
843
  {
@@ -780,6 +848,12 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
780
848
  if (!this .dAttribute (xmlElement .getAttribute ("d"), contours))
781
849
  return;
782
850
 
851
+ // Create nodes.
852
+
853
+ this .pathLikeElement (xmlElement, contours);
854
+ },
855
+ pathLikeElement: function (xmlElement, contours)
856
+ {
783
857
  // Determine style.
784
858
 
785
859
  if (!this .styleAttributes (xmlElement))
@@ -804,7 +878,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
804
878
  for (const points of contours)
805
879
  coordinateNode .point .push (... points);
806
880
 
807
- if (this .style .fillType !== "NONE")
881
+ if (this .style .fillType !== "none")
808
882
  {
809
883
  const
810
884
  shapeNode = scene .createNode ("Shape"),
@@ -820,47 +894,49 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
820
894
  transformNode .children .push (shapeNode);
821
895
  }
822
896
 
823
- if (this .style .strokeType !== "NONE")
897
+ if (this .style .strokeType !== "none")
824
898
  {
825
899
  const
826
900
  shapeNode = scene .createNode ("Shape"),
827
901
  geometryNode = scene .createNode ("IndexedLineSet");
828
902
 
829
- shapeNode .appearance = this .createStrokeAppearance ();
830
- shapeNode .geometry = geometryNode;
831
- geometryNode .coord = coordinateNode;
903
+ shapeNode .appearance = this .createStrokeAppearance ();
904
+ shapeNode .geometry = geometryNode;
905
+ geometryNode .coord = coordinateNode;
906
+
907
+ // Create contour indices.
908
+
909
+ const indices = geometryNode .coordIndex;
832
910
 
833
911
  for (const points of contours)
834
912
  {
835
913
  for (const i of points .keys ())
836
- geometryNode .coordIndex .push (points .index + i);
914
+ indices .push (points .index + i);
837
915
 
838
916
  if (points .closed)
839
- geometryNode .coordIndex .push (points .index);
917
+ indices .push (points .index);
840
918
 
841
- geometryNode .coordIndex .push (-1);
919
+ indices .push (-1);
842
920
  }
843
921
 
844
922
  transformNode .children .push (shapeNode);
845
923
  }
846
924
 
847
- this .groupNodes .pop ();
848
- this .styles .pop ();
925
+ this .groupNodes .pop ();
926
+ this .modelMatrix .pop ();
927
+ this .styles .pop ();
849
928
 
850
929
  this .groupNodes .at (-1) .children .push (transformNode);
851
930
  },
852
- linearGradientElementURL: function (xmlElement, bbox)
931
+ linearGradientElementUrl: function (xmlElement, bbox)
853
932
  {
854
933
  const
855
- g = this .linearGradientElement (xmlElement, { stops: [ ] }),
934
+ g = this .linearGradientElement (xmlElement, bbox, { stops: [ ] }),
856
935
  gradient = this .context .createLinearGradient (g .x1, g .y1, g .x2, g .y2);
857
936
 
858
- for (const [o, c, a] of g .stops)
859
- gradient .addColorStop (o, `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`);
860
-
861
- return this .drawGradient (gradient, g .transform, bbox, g .units);
937
+ return this .drawGradient (gradient, g, bbox);
862
938
  },
863
- linearGradientElement: function (xmlElement, gradient)
939
+ linearGradientElement: function (xmlElement, bbox, gradient)
864
940
  {
865
941
  if (xmlElement .nodeName !== "linearGradient")
866
942
  return;
@@ -870,17 +946,29 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
870
946
  const refElement = this .hrefAttribute (xmlElement .getAttribute ("xlink:href"));
871
947
 
872
948
  if (refElement)
873
- this .gradientElement (refElement, gradient);
949
+ this .gradientElement (refElement, bbox, gradient);
874
950
 
875
951
  // Attributes
876
952
 
877
- gradient .x1 = this .lengthAttribute (xmlElement .getAttribute ("x1"), gradient .x1 || 0);
878
- gradient .y1 = this .lengthAttribute (xmlElement .getAttribute ("y1"), gradient .y1 || 0);
879
- gradient .x2 = this .lengthAttribute (xmlElement .getAttribute ("x2"), gradient .x2 || 1);
880
- gradient .y2 = this .lengthAttribute (xmlElement .getAttribute ("y2"), gradient .y2 || 0);
953
+ gradient .x1 = this .lengthAttribute (xmlElement .getAttribute ("x1"), gradient .x1 || 0, "width");
954
+ gradient .y1 = this .lengthAttribute (xmlElement .getAttribute ("y1"), gradient .y1 || 0, "height");
955
+ gradient .x2 = this .lengthAttribute (xmlElement .getAttribute ("x2"), gradient .x2 || 1, "width");
956
+ gradient .y2 = this .lengthAttribute (xmlElement .getAttribute ("y2"), gradient .y2 || 0, "height");
881
957
  gradient .units = xmlElement .getAttribute ("gradientUnits") || "objectBoundingBox";
882
958
  gradient .transform = this .transformAttribute (xmlElement .getAttribute ("gradientTransform"));
883
959
 
960
+ // Spread matrix
961
+
962
+ const
963
+ s = new Matrix3 (),
964
+ c = new Vector2 (gradient .x1, gradient .y1);
965
+
966
+ s .translate (c);
967
+ s .scale (new Vector2 (SPREAD, SPREAD));
968
+ s .translate (c .negate ());
969
+
970
+ gradient .spreadMatrix = s;
971
+
884
972
  // Stops
885
973
 
886
974
  for (const childNode of xmlElement .childNodes)
@@ -888,38 +976,47 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
888
976
 
889
977
  return gradient;
890
978
  },
891
- radialGradientElementURL: function (xmlElement, bbox)
979
+ radialGradientElementUrl: function (xmlElement, bbox)
892
980
  {
893
981
  const
894
- g = this .radialGradientElement (xmlElement, { stops: [ ] }),
982
+ g = this .radialGradientElement (xmlElement, bbox, { stops: [ ] }),
895
983
  gradient = this .context .createRadialGradient (g .fx, g .fy, g. fr, g .cx, g .cy, g .r);
896
984
 
897
- for (const [o, c, a] of g .stops)
898
- gradient .addColorStop (o, `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`);
899
-
900
- return this .drawGradient (gradient, g .transform, bbox, g .units);
985
+ return this .drawGradient (gradient, g, bbox);
901
986
  },
902
- radialGradientElement: function (xmlElement, gradient)
987
+ radialGradientElement: function (xmlElement, bbox, gradient)
903
988
  {
904
989
  // Attribute xlink:href
905
990
 
906
991
  const refElement = this .hrefAttribute (xmlElement .getAttribute ("xlink:href"));
907
992
 
908
993
  if (refElement)
909
- this .gradientElement (refElement, gradient);
994
+ this .gradientElement (refElement, bbox, gradient);
910
995
 
911
996
  // Attributes
912
997
 
913
- gradient .cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), gradient .cx || 0.5),
914
- gradient .cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), gradient .cy || 0.5),
998
+ gradient .cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), gradient .cx || 0.5, "width"),
999
+ gradient .cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), gradient .cy || 0.5, "height"),
915
1000
  gradient .r = this .lengthAttribute (xmlElement .getAttribute ("r"), gradient .r || 0.5),
916
- gradient .fx = this .lengthAttribute (xmlElement .getAttribute ("fx"), gradient .fx || gradient .cx),
917
- gradient .fy = this .lengthAttribute (xmlElement .getAttribute ("fy"), gradient .fy || gradient .cy),
1001
+ gradient .fx = this .lengthAttribute (xmlElement .getAttribute ("fx"), gradient .fx || gradient .cx, "width"),
1002
+ gradient .fy = this .lengthAttribute (xmlElement .getAttribute ("fy"), gradient .fy || gradient .cy, "height"),
918
1003
  gradient .fr = this .lengthAttribute (xmlElement .getAttribute ("fr"), gradient .fr || 0),
919
1004
  gradient .units = xmlElement .getAttribute ("gradientUnits") || "objectBoundingBox";
920
1005
  gradient .spreadMethod = xmlElement .getAttribute ("spreadMethod");
921
1006
  gradient .transform = this .transformAttribute (xmlElement .getAttribute ("gradientTransform"));
922
1007
 
1008
+ // Spread matrix
1009
+
1010
+ const
1011
+ s = new Matrix3 (),
1012
+ c = new Vector2 (gradient .fx, gradient .fy);
1013
+
1014
+ s .translate (c);
1015
+ s .scale (new Vector2 (SPREAD, SPREAD));
1016
+ s .translate (c .negate ());
1017
+
1018
+ gradient .spreadMatrix = s;
1019
+
923
1020
  // Stops
924
1021
 
925
1022
  for (const childNode of xmlElement .childNodes)
@@ -927,7 +1024,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
927
1024
 
928
1025
  return gradient;
929
1026
  },
930
- gradientElement: function (xmlElement, gradient)
1027
+ gradientElement: function (xmlElement, bbox, gradient)
931
1028
  {
932
1029
  if (!xmlElement)
933
1030
  return;
@@ -935,9 +1032,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
935
1032
  switch (xmlElement .nodeName)
936
1033
  {
937
1034
  case "linearGradient":
938
- return this .linearGradientElement (xmlElement, gradient);
1035
+ return this .linearGradientElement (xmlElement, bbox, gradient);
939
1036
  case "radialGradient":
940
- return this .radialGradientElement (xmlElement, gradient);
1037
+ return this .radialGradientElement (xmlElement, bbox, gradient);
941
1038
  }
942
1039
  },
943
1040
  gradientChild: function (xmlElement, gradient)
@@ -952,26 +1049,75 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
952
1049
 
953
1050
  const offset = this .percentAttribute (xmlElement .getAttribute ("offset"), 0);
954
1051
 
1052
+ if (offset < 0 || offset > 1)
1053
+ return;
1054
+
955
1055
  const { stopColor, stopOpacity } = this .style;
956
1056
 
957
1057
  gradient .stops .push ([offset, stopColor, stopOpacity]);
958
1058
 
959
1059
  this .styles .pop ();
960
1060
  },
961
- drawGradient: function (gradient, transform, bbox, units)
1061
+ drawGradient: function (gradient, g, bbox)
962
1062
  {
1063
+ // Add color stops.
1064
+
1065
+ const spreadMatrix = new Matrix3 ();
1066
+
1067
+ switch (g .spreadMethod)
1068
+ {
1069
+ default: // pad
1070
+ {
1071
+ for (const [o, c, a] of g .stops)
1072
+ gradient .addColorStop (o, this .css (c, a));
1073
+
1074
+ break;
1075
+ }
1076
+ case "repeat":
1077
+ {
1078
+ spreadMatrix .assign (g .spreadMatrix);
1079
+
1080
+ for (let i = 0; i < SPREAD; ++ i)
1081
+ {
1082
+ const s = i / SPREAD;
1083
+
1084
+ for (const [o, c, a] of g .stops)
1085
+ gradient .addColorStop (s + o / SPREAD, this .css (c, a));
1086
+ }
1087
+
1088
+ break;
1089
+ }
1090
+ case "reflect":
1091
+ {
1092
+ spreadMatrix .assign (g .spreadMatrix);
1093
+
1094
+ for (let i = 0; i < SPREAD; ++ i)
1095
+ {
1096
+ const s = i / SPREAD;
1097
+
1098
+ for (const [o, c, a] of g .stops)
1099
+ gradient .addColorStop (s + (i % 2 ? 1 - o : o) / SPREAD, this .css (c, a));
1100
+ }
1101
+
1102
+ break;
1103
+ }
1104
+ }
1105
+
1106
+ // Create Matrix.
1107
+
963
1108
  const m = new Matrix3 ();
964
1109
 
965
- m .scale (new Vector2 (GRADIENT_SIZE / 2, GRADIENT_SIZE / 2));
1110
+ m .scale (new Vector2 (this .GRADIENT_SIZE / 2, this .GRADIENT_SIZE / 2));
966
1111
  m .translate (Vector2 .One);
967
1112
  m .scale (new Vector2 (1, -1));
968
1113
 
969
- if (units === "userSpaceOnUse")
1114
+ if (g .units === "userSpaceOnUse")
970
1115
  m .multLeft (Matrix3 .inverse (bbox .matrix));
971
1116
  else
972
1117
  m .multLeft (new Matrix3 (2, 0, 0, 0, 2, 0, -1, -1, 1));
973
1118
 
974
- m .multLeft (transform);
1119
+ m .multLeft (g .transform);
1120
+ m .multLeft (spreadMatrix);
975
1121
 
976
1122
  // Paint.
977
1123
 
@@ -979,8 +1125,8 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
979
1125
 
980
1126
  cx .fillStyle = gradient;
981
1127
  cx .save ();
982
- cx .clearRect (0, 0, GRADIENT_SIZE, GRADIENT_SIZE);
983
- cx .rect (0, 0, GRADIENT_SIZE, GRADIENT_SIZE);
1128
+ cx .clearRect (0, 0, this .GRADIENT_SIZE, this .GRADIENT_SIZE);
1129
+ cx .rect (0, 0, this .GRADIENT_SIZE, this .GRADIENT_SIZE);
984
1130
  cx .transform (m [0], m [1], m [3], m [4], m [6], m [7]);
985
1131
  cx .fill ();
986
1132
  cx .restore ();
@@ -988,6 +1134,10 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
988
1134
  // Use PNG because image can have alpha channel.
989
1135
  return this .canvas .toDataURL ("image/png");
990
1136
  },
1137
+ patternUrl: function (xmlElement)
1138
+ {
1139
+ //console .debug ("pattern");
1140
+ },
991
1141
  idAttribute: function (attribute, node)
992
1142
  {
993
1143
  if (attribute === null)
@@ -1042,7 +1192,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1042
1192
 
1043
1193
  return this .document .getElementById (hash);
1044
1194
  },
1045
- lengthAttribute: function (attribute, defaultValue)
1195
+ lengthAttribute: function (attribute, defaultValue, percent)
1046
1196
  {
1047
1197
  // Returns length in pixel.
1048
1198
 
@@ -1085,6 +1235,23 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1085
1235
  case "pc":
1086
1236
  value *= PICA / PIXEL;
1087
1237
  break;
1238
+ case "%":
1239
+ {
1240
+ switch (percent)
1241
+ {
1242
+ case "width":
1243
+ value *= this .viewBox [2] / 100;
1244
+ break;
1245
+ case "height":
1246
+ value *= this .viewBox [3] / 100;
1247
+ break;
1248
+ default:
1249
+ value *= Math .hypot (this .viewBox [2], this .viewBox [3]) / 100;
1250
+ break;
1251
+ }
1252
+
1253
+ break;
1254
+ }
1088
1255
  }
1089
1256
  }
1090
1257
 
@@ -1350,7 +1517,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1350
1517
  y += ay;
1351
1518
  }
1352
1519
 
1353
- Bezier .quadric (ax, ay, 0, x1, y1, 0, x, y, 0, BEZIER_STEPS, points);
1520
+ Bezier .quadric (ax, ay, 0, x1, y1, 0, x, y, 0, this .BEZIER_STEPS, points);
1354
1521
 
1355
1522
  ax = x;
1356
1523
  ay = y;
@@ -1411,7 +1578,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1411
1578
  }
1412
1579
  }
1413
1580
 
1414
- Bezier .quadric (ax, ay, 0, x1, y1, 0, x, y, 0, BEZIER_STEPS, points);
1581
+ Bezier .quadric (ax, ay, 0, x1, y1, 0, x, y, 0, this .BEZIER_STEPS, points);
1415
1582
 
1416
1583
  ax = x;
1417
1584
  ay = y;
@@ -1477,7 +1644,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1477
1644
  y += ay;
1478
1645
  }
1479
1646
 
1480
- Bezier .cubic (ax, ay, 0, x1, y1, 0, x2, y2, 0, x, y, 0, BEZIER_STEPS, points);
1647
+ Bezier .cubic (ax, ay, 0, x1, y1, 0, x2, y2, 0, x, y, 0, this .BEZIER_STEPS, points);
1481
1648
 
1482
1649
  ax = x;
1483
1650
  ay = y;
@@ -1554,7 +1721,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1554
1721
  }
1555
1722
  }
1556
1723
 
1557
- Bezier .cubic (ax, ay, 0, x1, y1, 0, x2, y2, 0, x, y, 0, BEZIER_STEPS, points);
1724
+ Bezier .cubic (ax, ay, 0, x1, y1, 0, x2, y2, 0, x, y, 0, this .BEZIER_STEPS, points);
1558
1725
 
1559
1726
  ax = x;
1560
1727
  ay = y;
@@ -1626,7 +1793,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1626
1793
  y += ay;
1627
1794
  }
1628
1795
 
1629
- Bezier .arc (ax, ay, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y, CIRCLE_STEPS, points);
1796
+ Bezier .arc (ax, ay, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y, this .CIRCLE_STEPS, points);
1630
1797
 
1631
1798
  ax = x;
1632
1799
  ay = y;
@@ -1764,16 +1931,20 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1764
1931
  {
1765
1932
  if (this .double ())
1766
1933
  {
1767
- const ty = this .value;
1934
+ var ty = this .value;
1935
+ }
1936
+ }
1937
+ else
1938
+ {
1939
+ var ty = 0;
1940
+ }
1768
1941
 
1769
- this .whitespaces ();
1942
+ this .whitespaces ();
1770
1943
 
1771
- if (Grammar .closeParenthesis .parse (this))
1772
- {
1773
- matrix .translate (new Vector2 (tx, ty));
1774
- continue;
1775
- }
1776
- }
1944
+ if (Grammar .closeParenthesis .parse (this))
1945
+ {
1946
+ matrix .translate (new Vector2 (tx, ty));
1947
+ continue;
1777
1948
  }
1778
1949
  }
1779
1950
  }
@@ -1840,16 +2011,20 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1840
2011
  {
1841
2012
  if (this .double ())
1842
2013
  {
1843
- const sy = this .value;
2014
+ var sy = this .value;
2015
+ }
2016
+ }
2017
+ else
2018
+ {
2019
+ var sy = sx;
2020
+ }
1844
2021
 
1845
- this .whitespaces ();
2022
+ this .whitespaces ();
1846
2023
 
1847
- if (Grammar .closeParenthesis .parse (this))
1848
- {
1849
- matrix .scale (new Vector2 (sx, sy));
1850
- continue;
1851
- }
1852
- }
2024
+ if (Grammar .closeParenthesis .parse (this))
2025
+ {
2026
+ matrix .scale (new Vector2 (sx, sy));
2027
+ continue;
1853
2028
  }
1854
2029
  }
1855
2030
  }
@@ -1900,44 +2075,24 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1900
2075
 
1901
2076
  return matrix;
1902
2077
  },
1903
- styleAttributes: (function ()
1904
- {
1905
- const Styles = [
1906
- "display",
1907
- "fill",
1908
- "fill-opacity",
1909
- "fill-rule",
1910
- "stroke",
1911
- "stroke-opacity",
1912
- "stroke-width",
1913
- "opacity",
1914
- "stop-color",
1915
- "stop-opacity",
1916
- ];
1917
-
1918
- return function (xmlElement)
1919
- {
1920
- const style = Object .assign ({ }, this .styles [0]);
1921
-
1922
- if (this .style .display === "none")
1923
- return false;
2078
+ styleAttributes: function (xmlElement)
2079
+ {
2080
+ const style = Object .assign ({ }, this .styles [0]);
1924
2081
 
1925
- this .styles .push (style);
2082
+ if (this .style .display === "none")
2083
+ return false;
1926
2084
 
1927
- for (const style of Styles)
1928
- {
1929
- const attribute = xmlElement .getAttribute (style);
2085
+ this .styles .push (style);
1930
2086
 
1931
- this .parseStyle (style, attribute ?? "default");
1932
- }
2087
+ for (const attribute of xmlElement .attributes)
2088
+ this .parseStyle (attribute .name, attribute .value)
1933
2089
 
1934
- // Style attribute has higher precedence.
2090
+ // Style attribute has higher precedence.
1935
2091
 
1936
- this .styleAttribute (xmlElement .getAttribute ("style"));
2092
+ this .styleAttribute (xmlElement .getAttribute ("style"));
1937
2093
 
1938
- return true;
1939
- };
1940
- })(),
2094
+ return true;
2095
+ },
1941
2096
  styleAttribute: function (attribute)
1942
2097
  {
1943
2098
  if (attribute === null)
@@ -1991,6 +2146,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1991
2146
  case "stop-opacity":
1992
2147
  this .stopOpacityStyle (value);
1993
2148
  break;
2149
+ case "vector-effect":
2150
+ this .vectorEffectStyle (value);
2151
+ break;
1994
2152
  }
1995
2153
  },
1996
2154
  displayStyle: function (value)
@@ -2020,19 +2178,19 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2020
2178
 
2021
2179
  if (value === "transparent")
2022
2180
  {
2023
- this .style .fillType = "NONE";
2181
+ this .style .fillType = "none";
2024
2182
  return;
2025
2183
  }
2026
2184
 
2027
2185
  if (value === "none")
2028
2186
  {
2029
- this .style .fillType ="NONE";
2187
+ this .style .fillType = "none";
2030
2188
  return;
2031
2189
  }
2032
2190
 
2033
2191
  if (!value .match (/^(?:inherit|unset|default)$/))
2034
2192
  {
2035
- if (this .colorValue ())
2193
+ if (this .colorValue (this .styles .at (-1) .fillColor))
2036
2194
  {
2037
2195
  this .style .fillType = "COLOR";
2038
2196
  this .style .fillColor = this .value .copy ();
@@ -2079,19 +2237,19 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2079
2237
 
2080
2238
  if (value === "transparent")
2081
2239
  {
2082
- this .style .strokeType = "NONE";
2240
+ this .style .strokeType = "none";
2083
2241
  return;
2084
2242
  }
2085
2243
 
2086
2244
  if (value === "none")
2087
2245
  {
2088
- this .style .strokeType ="NONE";
2246
+ this .style .strokeType = "none";
2089
2247
  return;
2090
2248
  }
2091
2249
 
2092
2250
  if (!value .match (/^(?:inherit|unset|default)$/))
2093
2251
  {
2094
- if (this .colorValue ())
2252
+ if (this .colorValue (this .styles .at (-1) .strokeColor))
2095
2253
  {
2096
2254
  this .style .strokeType = "COLOR";
2097
2255
  this .style .strokeColor = this .value .copy ();
@@ -2127,7 +2285,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2127
2285
  {
2128
2286
  if (this .double ())
2129
2287
  {
2130
- this .style .strokeWidth = this .value / (1000 * PIXEL);
2288
+ this .style .strokeWidth = this .lengthAttribute (this .value, 1);
2131
2289
  return;
2132
2290
  }
2133
2291
 
@@ -2157,7 +2315,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2157
2315
  },
2158
2316
  stopColorStyle: function (value)
2159
2317
  {
2160
- if (this .colorValue ())
2318
+ if (this .colorValue (Color4 .Black))
2161
2319
  {
2162
2320
  this .style .stopColor = this .value .copy ();
2163
2321
  return;
@@ -2177,6 +2335,18 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2177
2335
  return;
2178
2336
  }
2179
2337
  },
2338
+ vectorEffectStyle: function (value)
2339
+ {
2340
+ if (value !== "inherit")
2341
+ {
2342
+ this .style .vectorEffect = value;
2343
+ return;
2344
+ }
2345
+
2346
+ // inherit
2347
+
2348
+ this .style .vectorEffect = this .styles .at (-1) .vectorEffect;
2349
+ },
2180
2350
  parseValue: function (value)
2181
2351
  {
2182
2352
  this .input = value;
@@ -2231,12 +2401,14 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2231
2401
  {
2232
2402
  const color = new Color4 (0, 0, 0, 0);
2233
2403
 
2234
- return function ()
2404
+ return function (c)
2235
2405
  {
2236
2406
  if (!Grammar .color .parse (this))
2237
2407
  return false;
2238
2408
 
2239
- this .value = color .set (... this .convertColor (this .result [1]));
2409
+ const defaultColor = this .css (c);
2410
+
2411
+ this .value = color .set (... this .convertColor (this .result [1], defaultColor));
2240
2412
 
2241
2413
  return true;
2242
2414
  };
@@ -2245,13 +2417,20 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2245
2417
  {
2246
2418
  return Grammar .url .parse (this);
2247
2419
  },
2420
+ css: function (c, a = c .a)
2421
+ {
2422
+ return `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`;
2423
+ },
2248
2424
  createTransform: function (xmlElement, t = Vector2 .Zero, s = Vector2 .One)
2249
2425
  {
2250
2426
  // Determine matrix.
2251
2427
 
2252
2428
  const
2253
- scene = this .getExecutionContext (),
2254
- m = this .transformAttribute (xmlElement .getAttribute ("transform"));
2429
+ scene = this .getExecutionContext (),
2430
+ m = this .transformAttribute (xmlElement .getAttribute ("transform"));
2431
+
2432
+ this .modelMatrix .push ();
2433
+ this .modelMatrix .multLeft (Matrix4 .Matrix3 (m));
2255
2434
 
2256
2435
  m .translate (t);
2257
2436
  m .scale (s);
@@ -2260,7 +2439,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2260
2439
 
2261
2440
  const
2262
2441
  transformNode = scene .createNode ("Transform"),
2263
- matrix = new Matrix4 (m [0], m [1], 0, 0, m [3], m [4], 0, 0, 0, 0, 1, 0, m [6], m [7], 0, 1),
2442
+ matrix = Matrix4 .Matrix3 (m),
2264
2443
  translation = new Vector3 (0, 0, 0),
2265
2444
  rotation = new Rotation4 (),
2266
2445
  scale = new Vector3 (1, 1, 1),
@@ -2291,7 +2470,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2291
2470
 
2292
2471
  switch (this .style .fillType)
2293
2472
  {
2294
- case "NONE":
2473
+ case "none":
2295
2474
  {
2296
2475
  return null;
2297
2476
  }
@@ -2339,10 +2518,13 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2339
2518
  switch (xmlElement .nodeName)
2340
2519
  {
2341
2520
  case "linearGradient":
2342
- return this .linearGradientElementURL (xmlElement, bbox);
2521
+ return this .linearGradientElementUrl (xmlElement, bbox);
2343
2522
 
2344
2523
  case "radialGradient":
2345
- return this .radialGradientElementURL (xmlElement, bbox);
2524
+ return this .radialGradientElementUrl (xmlElement, bbox);
2525
+
2526
+ case "pattern":
2527
+ return this .patternUrl (xmlElement);
2346
2528
  }
2347
2529
  },
2348
2530
  createStrokeAppearance: function ()
@@ -2356,16 +2538,44 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2356
2538
  materialNode .emissiveColor = new Color3 (... this .style .strokeColor);
2357
2539
  materialNode .transparency = 1 - this .style .strokeOpacity * this .style .opacity;
2358
2540
 
2359
- if (this .style .strokeWidth !== 1)
2360
- {
2361
- const lineProperties = scene .createNode ("LineProperties");
2541
+ const strokeWidth = this .vectorEffect === "non-scaling-stroke"
2542
+ ? this .style .strokeWidth
2543
+ : this .getStokeWidth ();
2362
2544
 
2363
- appearanceNode .lineProperties = lineProperties;
2364
- lineProperties .linewidthScaleFactor = this .style .strokeWidth;
2365
- }
2545
+ if (strokeWidth > 1)
2546
+ appearanceNode .lineProperties = this .getLineProperties (strokeWidth);
2366
2547
 
2367
2548
  return appearanceNode;
2368
2549
  },
2550
+ getStokeWidth: function ()
2551
+ {
2552
+ const
2553
+ modelMatrix = this .modelMatrix .get (),
2554
+ strokeWidth = modelMatrix .multDirMatrix (new Vector3 (this .style .strokeWidth, this .style .strokeWidth, 0));
2555
+
2556
+ return (strokeWidth .x + strokeWidth .y) / 2;
2557
+ },
2558
+ getLineProperties: function (strokeWidth)
2559
+ {
2560
+ const lineProperties = this .lineProperties .get (strokeWidth);
2561
+
2562
+ if (lineProperties)
2563
+ {
2564
+ return lineProperties;
2565
+ }
2566
+ else
2567
+ {
2568
+ const
2569
+ scene = this .getExecutionContext (),
2570
+ lineProperties = scene .createNode ("LineProperties");
2571
+
2572
+ lineProperties .linewidthScaleFactor = strokeWidth;
2573
+
2574
+ this .lineProperties .set (strokeWidth, lineProperties);
2575
+
2576
+ return lineProperties;
2577
+ }
2578
+ },
2369
2579
  createTextureProperties: function ()
2370
2580
  {
2371
2581
  const