x_ite 8.6.4 → 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 (50) 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 +800 -654
  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/package.json +1 -1
  47. package/src/standard/Math/Numbers/Matrix4.js +7 -0
  48. package/src/x_ite/Browser/VERSION.js +1 -1
  49. package/src/x_ite/Parser/SVGParser.js +403 -264
  50. 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",
@@ -153,6 +153,42 @@ function SVGParser (scene)
153
153
  stopOpacity: 1,
154
154
  vectorEffect: "none",
155
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;
156
192
  }
157
193
 
158
194
  SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
@@ -261,16 +297,16 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
261
297
  // Get attributes of svg element.
262
298
 
263
299
  const
264
- defaultWidth = this .lengthAttribute (xmlElement .getAttribute ("width"), 300),
265
- defaultHeight = this .lengthAttribute (xmlElement .getAttribute ("height"), 150),
266
- 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),
267
303
  viewBox = this .viewBoxAttribute (xmlElement .getAttribute ("viewBox"), defaultViewBox),
268
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), viewBox [2]),
269
- 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");
270
306
 
271
307
  if (true) // default
272
308
  {
273
- // preserveAspectRatio="xMidYMid meet"
309
+ // preserveAspectRatio = "xMidYMid meet"
274
310
 
275
311
  const
276
312
  r = width / height,
@@ -389,10 +425,10 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
389
425
  // Create Transform node.
390
426
 
391
427
  const
392
- x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0),
393
- y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0),
394
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0),
395
- 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");
396
432
 
397
433
  const transformNode = this .createTransform (xmlElement, new Vector2 (x, y));
398
434
 
@@ -400,8 +436,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
400
436
 
401
437
  this .element (usedElement);
402
438
 
403
- this .styles .pop ();
404
- this .groupNodes .pop ();
439
+ this .groupNodes .pop ();
440
+ this .modelMatrix .pop ();
441
+ this .styles .pop ();
405
442
 
406
443
  // Add node.
407
444
 
@@ -424,8 +461,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
424
461
 
425
462
  this .elements (xmlElement);
426
463
 
427
- this .styles .pop ();
428
- this .groupNodes .pop ();
464
+ this .groupNodes .pop ();
465
+ this .modelMatrix .pop ();
466
+ this .styles .pop ();
429
467
 
430
468
  // Add node.
431
469
 
@@ -454,8 +492,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
454
492
 
455
493
  this .elements (xmlElement);
456
494
 
457
- this .styles .pop ();
458
- this .groupNodes .pop ();
495
+ this .groupNodes .pop ();
496
+ this .modelMatrix .pop ();
497
+ this .styles .pop ();
459
498
 
460
499
  // Add node.
461
500
 
@@ -496,75 +535,119 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
496
535
 
497
536
  this .elements (xmlElement);
498
537
 
499
- this .groupNodes .pop ();
500
- this .styles .pop ();
538
+ this .groupNodes .pop ();
539
+ this .modelMatrix .pop ();
540
+ this .styles .pop ();
501
541
 
502
542
  this .groupNodes .at (-1) .children .push (transformNode);
503
543
  },
504
544
  rectElement: function (xmlElement)
505
545
  {
506
- // Determine style.
507
-
508
- if (!this .styleAttributes (xmlElement))
509
- return;
510
-
511
546
  // Create Transform node.
512
547
 
513
548
  const
514
- x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0),
515
- y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0),
516
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0),
517
- 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");
518
553
 
519
- const
520
- scene = this .getExecutionContext (),
521
- size = new Vector2 (width, height),
522
- center = new Vector2 (x + width / 2, y + height / 2),
523
- bbox = new Box2 (size, center),
524
- 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"));
525
557
 
526
- this .groupNodes .push (transformNode);
558
+ if (rx === 0 && ry === 0)
559
+ {
560
+ // Determine style.
527
561
 
528
- // Create nodes.
562
+ if (!this .styleAttributes (xmlElement))
563
+ return;
564
+
565
+ // Create Transform node.
529
566
 
530
- if (this .style .fillType !== "none")
531
- {
532
567
  const
533
- shapeNode = scene .createNode ("Shape"),
534
- 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);
535
573
 
536
- shapeNode .appearance = this .createFillAppearance (bbox);
537
- shapeNode .geometry = rectangleNode;
538
- rectangleNode .solid = this .solid;
539
- rectangleNode .size = size;
574
+ this .groupNodes .push (transformNode);
540
575
 
541
- transformNode .children .push (shapeNode);
542
- }
576
+ // Create nodes.
543
577
 
544
- if (this .style .strokeType !== "none")
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];
608
+
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
545
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
+
546
628
  const
547
- shapeNode = scene .createNode ("Shape"),
548
- polylineNode = scene .createNode ("Polyline2D"),
549
- width1_2 = width / 2,
550
- 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 });
551
632
 
552
- shapeNode .appearance = this .createStrokeAppearance ();
553
- shapeNode .geometry = polylineNode;
633
+ for (let c = 0; c < 4; ++ c)
634
+ {
635
+ const s = c * Math .PI / 2;
554
636
 
555
- polylineNode .lineSegments = [ width1_2, height1_2,
556
- -width1_2, height1_2,
557
- -width1_2, -height1_2,
558
- width1_2, -height1_2,
559
- 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));
560
640
 
561
- transformNode .children .push (shapeNode);
562
- }
641
+ points .push (new Vector3 (xOffsets [c] + p .real * rx, yOffsets [c] + p .imag * ry, 0));
642
+ }
643
+ }
563
644
 
564
- this .groupNodes .pop ();
565
- this .styles .pop ();
645
+ points .pop ();
566
646
 
567
- this .groupNodes .at (-1) .children .push (transformNode);
647
+ // Create nodes.
648
+
649
+ this .pathLikeElement (xmlElement, [points]);
650
+ }
568
651
  },
569
652
  circleElement: function (xmlElement)
570
653
  {
@@ -576,8 +659,8 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
576
659
  // Create Transform node.
577
660
 
578
661
  const
579
- cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0),
580
- 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"),
581
664
  r = this .lengthAttribute (xmlElement .getAttribute ("r"), 0);
582
665
 
583
666
  const
@@ -616,8 +699,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
616
699
  transformNode .children .push (shapeNode);
617
700
  }
618
701
 
619
- this .groupNodes .pop ();
620
- this .styles .pop ();
702
+ this .groupNodes .pop ();
703
+ this .modelMatrix .pop ();
704
+ this .styles .pop ();
621
705
 
622
706
  this .groupNodes .at (-1) .children .push (transformNode);
623
707
  },
@@ -631,10 +715,10 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
631
715
  // Create Transform node.
632
716
 
633
717
  const
634
- cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), 0),
635
- cy = this .lengthAttribute (xmlElement .getAttribute ("cy"), 0),
636
- rx = this .lengthAttribute (xmlElement .getAttribute ("rx"), 0),
637
- 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");
638
722
 
639
723
  const
640
724
  scene = this .getExecutionContext (),
@@ -673,8 +757,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
673
757
  transformNode .children .push (shapeNode);
674
758
  }
675
759
 
676
- this .groupNodes .pop ();
677
- this .styles .pop ();
760
+ this .groupNodes .pop ();
761
+ this .modelMatrix .pop ();
762
+ this .styles .pop ();
678
763
 
679
764
  this .groupNodes .at (-1) .children .push (transformNode);
680
765
  },
@@ -684,13 +769,18 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
684
769
  },
685
770
  imageElement: function (xmlElement)
686
771
  {
772
+ // Determine style.
773
+
774
+ if (!this .styleAttributes (xmlElement))
775
+ return;
776
+
687
777
  // Create Transform node.
688
778
 
689
779
  const
690
- x = this .lengthAttribute (xmlElement .getAttribute ("x"), 0),
691
- y = this .lengthAttribute (xmlElement .getAttribute ("y"), 0),
692
- width = this .lengthAttribute (xmlElement .getAttribute ("width"), 0),
693
- 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"),
694
784
  href = xmlElement .getAttribute ("xlink:href");
695
785
 
696
786
  const
@@ -717,74 +807,37 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
717
807
 
718
808
  transformNode .children .push (shapeNode);
719
809
 
720
- this .groupNodes .pop ();
810
+ this .groupNodes .pop ();
811
+ this .modelMatrix .pop ();
812
+ this .styles .pop ();
813
+
721
814
  this .groupNodes .at (-1) .children .push (transformNode);
722
815
  },
723
- polylineElement: function (xmlElement, closed = false)
816
+ polylineElement: function (xmlElement)
724
817
  {
818
+ // Get points.
819
+
725
820
  const points = Object .assign ([ ], { index: 0 });
726
821
 
727
822
  if (!this .pointsAttribute (xmlElement .getAttribute ("points"), points))
728
823
  return;
729
824
 
730
- // Determine style.
731
-
732
- if (!this .styleAttributes (xmlElement))
733
- return;
734
-
735
- // Create Transform node.
736
-
737
- const
738
- scene = this .getExecutionContext (),
739
- transformNode = this .createTransform (xmlElement),
740
- bbox = new Box2 (Vector2 .min (... points), Vector2 .max (... points), true);
741
-
742
- this .groupNodes .push (transformNode);
743
-
744
825
  // Create nodes.
745
826
 
746
- const coordinateNode = scene .createNode ("Coordinate");
747
-
748
- coordinateNode .point .push (... points);
749
-
750
- if (this .style .fillType !== "none")
751
- {
752
- const
753
- shapeNode = scene .createNode ("Shape"),
754
- geometryNode = scene .createNode ("IndexedTriangleSet");
755
-
756
- shapeNode .appearance = this .createFillAppearance (bbox);
757
- shapeNode .geometry = geometryNode;
758
- geometryNode .solid = this .solid;
759
- geometryNode .index = this .triangulatePolygon ([points], coordinateNode);
760
- geometryNode .texCoord = this .createTextureCoordinate (coordinateNode, bbox);
761
- geometryNode .coord = coordinateNode;
762
-
763
- transformNode .children .push (shapeNode);
764
- }
765
-
766
- if (this .style .strokeType !== "none")
767
- {
768
- const
769
- shapeNode = scene .createNode ("Shape"),
770
- geometryNode = scene .createNode ("IndexedLineSet");
827
+ this .pathLikeElement (xmlElement, [points]);
828
+ },
829
+ polygonElement: function (xmlElement)
830
+ {
831
+ // Get points.
771
832
 
772
- shapeNode .appearance = this .createStrokeAppearance ();
773
- shapeNode .geometry = geometryNode;
774
- geometryNode .coordIndex = [... points .keys (), ... (closed ? [points [0]] : [ ]), -1];
775
- geometryNode .coord = coordinateNode;
833
+ const points = Object .assign ([ ], { index: 0, closed: true });
776
834
 
777
- transformNode .children .push (shapeNode);
778
- }
835
+ if (!this .pointsAttribute (xmlElement .getAttribute ("points"), points))
836
+ return;
779
837
 
780
- this .groupNodes .pop ();
781
- this .styles .pop ();
838
+ // Create nodes.
782
839
 
783
- this .groupNodes .at (-1) .children .push (transformNode);
784
- },
785
- polygonElement: function (xmlElement)
786
- {
787
- this .polylineElement (xmlElement, true);
840
+ this .pathLikeElement (xmlElement, [points]);
788
841
  },
789
842
  pathElement: function (xmlElement)
790
843
  {
@@ -795,6 +848,12 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
795
848
  if (!this .dAttribute (xmlElement .getAttribute ("d"), contours))
796
849
  return;
797
850
 
851
+ // Create nodes.
852
+
853
+ this .pathLikeElement (xmlElement, contours);
854
+ },
855
+ pathLikeElement: function (xmlElement, contours)
856
+ {
798
857
  // Determine style.
799
858
 
800
859
  if (!this .styleAttributes (xmlElement))
@@ -841,41 +900,43 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
841
900
  shapeNode = scene .createNode ("Shape"),
842
901
  geometryNode = scene .createNode ("IndexedLineSet");
843
902
 
844
- shapeNode .appearance = this .createStrokeAppearance ();
845
- shapeNode .geometry = geometryNode;
846
- 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;
847
910
 
848
911
  for (const points of contours)
849
912
  {
850
913
  for (const i of points .keys ())
851
- geometryNode .coordIndex .push (points .index + i);
914
+ indices .push (points .index + i);
852
915
 
853
916
  if (points .closed)
854
- geometryNode .coordIndex .push (points .index);
917
+ indices .push (points .index);
855
918
 
856
- geometryNode .coordIndex .push (-1);
919
+ indices .push (-1);
857
920
  }
858
921
 
859
922
  transformNode .children .push (shapeNode);
860
923
  }
861
924
 
862
- this .groupNodes .pop ();
863
- this .styles .pop ();
925
+ this .groupNodes .pop ();
926
+ this .modelMatrix .pop ();
927
+ this .styles .pop ();
864
928
 
865
929
  this .groupNodes .at (-1) .children .push (transformNode);
866
930
  },
867
- linearGradientElementURL: function (xmlElement, bbox)
931
+ linearGradientElementUrl: function (xmlElement, bbox)
868
932
  {
869
933
  const
870
- g = this .linearGradientElement (xmlElement, { stops: [ ] }),
934
+ g = this .linearGradientElement (xmlElement, bbox, { stops: [ ] }),
871
935
  gradient = this .context .createLinearGradient (g .x1, g .y1, g .x2, g .y2);
872
936
 
873
- for (const [o, c, a] of g .stops)
874
- gradient .addColorStop (o, `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`);
875
-
876
937
  return this .drawGradient (gradient, g, bbox);
877
938
  },
878
- linearGradientElement: function (xmlElement, gradient)
939
+ linearGradientElement: function (xmlElement, bbox, gradient)
879
940
  {
880
941
  if (xmlElement .nodeName !== "linearGradient")
881
942
  return;
@@ -885,17 +946,29 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
885
946
  const refElement = this .hrefAttribute (xmlElement .getAttribute ("xlink:href"));
886
947
 
887
948
  if (refElement)
888
- this .gradientElement (refElement, gradient);
949
+ this .gradientElement (refElement, bbox, gradient);
889
950
 
890
951
  // Attributes
891
952
 
892
- gradient .x1 = this .lengthAttribute (xmlElement .getAttribute ("x1"), gradient .x1 || 0);
893
- gradient .y1 = this .lengthAttribute (xmlElement .getAttribute ("y1"), gradient .y1 || 0);
894
- gradient .x2 = this .lengthAttribute (xmlElement .getAttribute ("x2"), gradient .x2 || 1);
895
- 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");
896
957
  gradient .units = xmlElement .getAttribute ("gradientUnits") || "objectBoundingBox";
897
958
  gradient .transform = this .transformAttribute (xmlElement .getAttribute ("gradientTransform"));
898
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
+
899
972
  // Stops
900
973
 
901
974
  for (const childNode of xmlElement .childNodes)
@@ -903,38 +976,47 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
903
976
 
904
977
  return gradient;
905
978
  },
906
- radialGradientElementURL: function (xmlElement, bbox)
979
+ radialGradientElementUrl: function (xmlElement, bbox)
907
980
  {
908
981
  const
909
- g = this .radialGradientElement (xmlElement, { stops: [ ] }),
982
+ g = this .radialGradientElement (xmlElement, bbox, { stops: [ ] }),
910
983
  gradient = this .context .createRadialGradient (g .fx, g .fy, g. fr, g .cx, g .cy, g .r);
911
984
 
912
- for (const [o, c, a] of g .stops)
913
- gradient .addColorStop (o, `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`);
914
-
915
985
  return this .drawGradient (gradient, g, bbox);
916
986
  },
917
- radialGradientElement: function (xmlElement, gradient)
987
+ radialGradientElement: function (xmlElement, bbox, gradient)
918
988
  {
919
989
  // Attribute xlink:href
920
990
 
921
991
  const refElement = this .hrefAttribute (xmlElement .getAttribute ("xlink:href"));
922
992
 
923
993
  if (refElement)
924
- this .gradientElement (refElement, gradient);
994
+ this .gradientElement (refElement, bbox, gradient);
925
995
 
926
996
  // Attributes
927
997
 
928
- gradient .cx = this .lengthAttribute (xmlElement .getAttribute ("cx"), gradient .cx || 0.5),
929
- 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"),
930
1000
  gradient .r = this .lengthAttribute (xmlElement .getAttribute ("r"), gradient .r || 0.5),
931
- gradient .fx = this .lengthAttribute (xmlElement .getAttribute ("fx"), gradient .fx || gradient .cx),
932
- 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"),
933
1003
  gradient .fr = this .lengthAttribute (xmlElement .getAttribute ("fr"), gradient .fr || 0),
934
1004
  gradient .units = xmlElement .getAttribute ("gradientUnits") || "objectBoundingBox";
935
1005
  gradient .spreadMethod = xmlElement .getAttribute ("spreadMethod");
936
1006
  gradient .transform = this .transformAttribute (xmlElement .getAttribute ("gradientTransform"));
937
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
+
938
1020
  // Stops
939
1021
 
940
1022
  for (const childNode of xmlElement .childNodes)
@@ -942,7 +1024,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
942
1024
 
943
1025
  return gradient;
944
1026
  },
945
- gradientElement: function (xmlElement, gradient)
1027
+ gradientElement: function (xmlElement, bbox, gradient)
946
1028
  {
947
1029
  if (!xmlElement)
948
1030
  return;
@@ -950,9 +1032,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
950
1032
  switch (xmlElement .nodeName)
951
1033
  {
952
1034
  case "linearGradient":
953
- return this .linearGradientElement (xmlElement, gradient);
1035
+ return this .linearGradientElement (xmlElement, bbox, gradient);
954
1036
  case "radialGradient":
955
- return this .radialGradientElement (xmlElement, gradient);
1037
+ return this .radialGradientElement (xmlElement, bbox, gradient);
956
1038
  }
957
1039
  },
958
1040
  gradientChild: function (xmlElement, gradient)
@@ -967,6 +1049,9 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
967
1049
 
968
1050
  const offset = this .percentAttribute (xmlElement .getAttribute ("offset"), 0);
969
1051
 
1052
+ if (offset < 0 || offset > 1)
1053
+ return;
1054
+
970
1055
  const { stopColor, stopOpacity } = this .style;
971
1056
 
972
1057
  gradient .stops .push ([offset, stopColor, stopOpacity]);
@@ -975,9 +1060,54 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
975
1060
  },
976
1061
  drawGradient: function (gradient, g, bbox)
977
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
+
978
1108
  const m = new Matrix3 ();
979
1109
 
980
- m .scale (new Vector2 (GRADIENT_SIZE / 2, GRADIENT_SIZE / 2));
1110
+ m .scale (new Vector2 (this .GRADIENT_SIZE / 2, this .GRADIENT_SIZE / 2));
981
1111
  m .translate (Vector2 .One);
982
1112
  m .scale (new Vector2 (1, -1));
983
1113
 
@@ -987,6 +1117,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
987
1117
  m .multLeft (new Matrix3 (2, 0, 0, 0, 2, 0, -1, -1, 1));
988
1118
 
989
1119
  m .multLeft (g .transform);
1120
+ m .multLeft (spreadMatrix);
990
1121
 
991
1122
  // Paint.
992
1123
 
@@ -994,8 +1125,8 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
994
1125
 
995
1126
  cx .fillStyle = gradient;
996
1127
  cx .save ();
997
- cx .clearRect (0, 0, GRADIENT_SIZE, GRADIENT_SIZE);
998
- 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);
999
1130
  cx .transform (m [0], m [1], m [3], m [4], m [6], m [7]);
1000
1131
  cx .fill ();
1001
1132
  cx .restore ();
@@ -1003,6 +1134,10 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1003
1134
  // Use PNG because image can have alpha channel.
1004
1135
  return this .canvas .toDataURL ("image/png");
1005
1136
  },
1137
+ patternUrl: function (xmlElement)
1138
+ {
1139
+ //console .debug ("pattern");
1140
+ },
1006
1141
  idAttribute: function (attribute, node)
1007
1142
  {
1008
1143
  if (attribute === null)
@@ -1057,7 +1192,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1057
1192
 
1058
1193
  return this .document .getElementById (hash);
1059
1194
  },
1060
- lengthAttribute: function (attribute, defaultValue)
1195
+ lengthAttribute: function (attribute, defaultValue, percent)
1061
1196
  {
1062
1197
  // Returns length in pixel.
1063
1198
 
@@ -1100,6 +1235,23 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1100
1235
  case "pc":
1101
1236
  value *= PICA / PIXEL;
1102
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
+ }
1103
1255
  }
1104
1256
  }
1105
1257
 
@@ -1365,7 +1517,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1365
1517
  y += ay;
1366
1518
  }
1367
1519
 
1368
- 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);
1369
1521
 
1370
1522
  ax = x;
1371
1523
  ay = y;
@@ -1426,7 +1578,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1426
1578
  }
1427
1579
  }
1428
1580
 
1429
- 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);
1430
1582
 
1431
1583
  ax = x;
1432
1584
  ay = y;
@@ -1492,7 +1644,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1492
1644
  y += ay;
1493
1645
  }
1494
1646
 
1495
- 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);
1496
1648
 
1497
1649
  ax = x;
1498
1650
  ay = y;
@@ -1569,7 +1721,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1569
1721
  }
1570
1722
  }
1571
1723
 
1572
- 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);
1573
1725
 
1574
1726
  ax = x;
1575
1727
  ay = y;
@@ -1641,7 +1793,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1641
1793
  y += ay;
1642
1794
  }
1643
1795
 
1644
- 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);
1645
1797
 
1646
1798
  ax = x;
1647
1799
  ay = y;
@@ -1923,45 +2075,24 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
1923
2075
 
1924
2076
  return matrix;
1925
2077
  },
1926
- styleAttributes: (function ()
1927
- {
1928
- const Styles = [
1929
- "display",
1930
- "fill",
1931
- "fill-opacity",
1932
- "fill-rule",
1933
- "stroke",
1934
- "stroke-opacity",
1935
- "stroke-width",
1936
- "opacity",
1937
- "stop-color",
1938
- "stop-opacity",
1939
- "vector-effect",
1940
- ];
1941
-
1942
- return function (xmlElement)
1943
- {
1944
- const style = Object .assign ({ }, this .styles [0]);
1945
-
1946
- if (this .style .display === "none")
1947
- return false;
2078
+ styleAttributes: function (xmlElement)
2079
+ {
2080
+ const style = Object .assign ({ }, this .styles [0]);
1948
2081
 
1949
- this .styles .push (style);
2082
+ if (this .style .display === "none")
2083
+ return false;
1950
2084
 
1951
- for (const style of Styles)
1952
- {
1953
- const attribute = xmlElement .getAttribute (style);
2085
+ this .styles .push (style);
1954
2086
 
1955
- this .parseStyle (style, attribute ?? "default");
1956
- }
2087
+ for (const attribute of xmlElement .attributes)
2088
+ this .parseStyle (attribute .name, attribute .value)
1957
2089
 
1958
- // Style attribute has higher precedence.
2090
+ // Style attribute has higher precedence.
1959
2091
 
1960
- this .styleAttribute (xmlElement .getAttribute ("style"));
2092
+ this .styleAttribute (xmlElement .getAttribute ("style"));
1961
2093
 
1962
- return true;
1963
- };
1964
- })(),
2094
+ return true;
2095
+ },
1965
2096
  styleAttribute: function (attribute)
1966
2097
  {
1967
2098
  if (attribute === null)
@@ -2053,13 +2184,13 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2053
2184
 
2054
2185
  if (value === "none")
2055
2186
  {
2056
- this .style .fillType ="none";
2187
+ this .style .fillType = "none";
2057
2188
  return;
2058
2189
  }
2059
2190
 
2060
2191
  if (!value .match (/^(?:inherit|unset|default)$/))
2061
2192
  {
2062
- if (this .colorValue ())
2193
+ if (this .colorValue (this .styles .at (-1) .fillColor))
2063
2194
  {
2064
2195
  this .style .fillType = "COLOR";
2065
2196
  this .style .fillColor = this .value .copy ();
@@ -2112,13 +2243,13 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2112
2243
 
2113
2244
  if (value === "none")
2114
2245
  {
2115
- this .style .strokeType ="none";
2246
+ this .style .strokeType = "none";
2116
2247
  return;
2117
2248
  }
2118
2249
 
2119
2250
  if (!value .match (/^(?:inherit|unset|default)$/))
2120
2251
  {
2121
- if (this .colorValue ())
2252
+ if (this .colorValue (this .styles .at (-1) .strokeColor))
2122
2253
  {
2123
2254
  this .style .strokeType = "COLOR";
2124
2255
  this .style .strokeColor = this .value .copy ();
@@ -2154,7 +2285,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2154
2285
  {
2155
2286
  if (this .double ())
2156
2287
  {
2157
- this .style .strokeWidth = this .lengthAttribute (this .value);
2288
+ this .style .strokeWidth = this .lengthAttribute (this .value, 1);
2158
2289
  return;
2159
2290
  }
2160
2291
 
@@ -2184,7 +2315,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2184
2315
  },
2185
2316
  stopColorStyle: function (value)
2186
2317
  {
2187
- if (this .colorValue ())
2318
+ if (this .colorValue (Color4 .Black))
2188
2319
  {
2189
2320
  this .style .stopColor = this .value .copy ();
2190
2321
  return;
@@ -2270,12 +2401,14 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2270
2401
  {
2271
2402
  const color = new Color4 (0, 0, 0, 0);
2272
2403
 
2273
- return function ()
2404
+ return function (c)
2274
2405
  {
2275
2406
  if (!Grammar .color .parse (this))
2276
2407
  return false;
2277
2408
 
2278
- 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));
2279
2412
 
2280
2413
  return true;
2281
2414
  };
@@ -2284,13 +2417,20 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2284
2417
  {
2285
2418
  return Grammar .url .parse (this);
2286
2419
  },
2420
+ css: function (c, a = c .a)
2421
+ {
2422
+ return `rgba(${c .r * 255},${c .g * 255},${c .b * 255},${a})`;
2423
+ },
2287
2424
  createTransform: function (xmlElement, t = Vector2 .Zero, s = Vector2 .One)
2288
2425
  {
2289
2426
  // Determine matrix.
2290
2427
 
2291
2428
  const
2292
- scene = this .getExecutionContext (),
2293
- 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));
2294
2434
 
2295
2435
  m .translate (t);
2296
2436
  m .scale (s);
@@ -2299,7 +2439,7 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2299
2439
 
2300
2440
  const
2301
2441
  transformNode = scene .createNode ("Transform"),
2302
- 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),
2303
2443
  translation = new Vector3 (0, 0, 0),
2304
2444
  rotation = new Rotation4 (),
2305
2445
  scale = new Vector3 (1, 1, 1),
@@ -2378,10 +2518,13 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2378
2518
  switch (xmlElement .nodeName)
2379
2519
  {
2380
2520
  case "linearGradient":
2381
- return this .linearGradientElementURL (xmlElement, bbox);
2521
+ return this .linearGradientElementUrl (xmlElement, bbox);
2382
2522
 
2383
2523
  case "radialGradient":
2384
- return this .radialGradientElementURL (xmlElement, bbox);
2524
+ return this .radialGradientElementUrl (xmlElement, bbox);
2525
+
2526
+ case "pattern":
2527
+ return this .patternUrl (xmlElement);
2385
2528
  }
2386
2529
  },
2387
2530
  createStrokeAppearance: function ()
@@ -2400,23 +2543,39 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2400
2543
  : this .getStokeWidth ();
2401
2544
 
2402
2545
  if (strokeWidth > 1)
2403
- {
2404
- const lineProperties = scene .createNode ("LineProperties");
2405
-
2406
- appearanceNode .lineProperties = lineProperties;
2407
- lineProperties .linewidthScaleFactor = strokeWidth;
2408
- }
2546
+ appearanceNode .lineProperties = this .getLineProperties (strokeWidth);
2409
2547
 
2410
2548
  return appearanceNode;
2411
2549
  },
2412
2550
  getStokeWidth: function ()
2413
2551
  {
2414
2552
  const
2415
- modelMatrix = this .getModelMatrix (),
2553
+ modelMatrix = this .modelMatrix .get (),
2416
2554
  strokeWidth = modelMatrix .multDirMatrix (new Vector3 (this .style .strokeWidth, this .style .strokeWidth, 0));
2417
2555
 
2418
2556
  return (strokeWidth .x + strokeWidth .y) / 2;
2419
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
+ },
2420
2579
  createTextureProperties: function ()
2421
2580
  {
2422
2581
  const
@@ -2445,26 +2604,6 @@ SVGParser .prototype = Object .assign (Object .create (X3DParser .prototype),
2445
2604
 
2446
2605
  return texCoordNode;
2447
2606
  },
2448
- getModelMatrix: function ()
2449
- {
2450
- const modelMatrix = new Matrix4 ();
2451
-
2452
- for (let i = 1; i < this .groupNodes .length; ++ i)
2453
- {
2454
- const
2455
- node = this .groupNodes [i],
2456
- matrix = new Matrix4 ();
2457
-
2458
- matrix .set (node .translation .getValue (),
2459
- node .rotation .getValue (),
2460
- node .scale .getValue (),
2461
- node .scaleOrientation .getValue ());
2462
-
2463
- modelMatrix .multLeft (matrix);
2464
- }
2465
-
2466
- return modelMatrix;
2467
- },
2468
2607
  createTesselator: function ()
2469
2608
  {
2470
2609
  // Function called for each vertex of tessellator output.