three-stdlib 2.29.7 → 2.29.9

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.
@@ -1,4 +1,5 @@
1
1
  import { Loader, FileLoader, Matrix3, Vector2, Vector3, ShapeUtils, Box2, Shape, Path, BufferGeometry, Float32BufferAttribute, ShapePath } from "three";
2
+ const COLOR_SPACE_SVG = "srgb";
2
3
  class SVGLoader extends Loader {
3
4
  constructor(manager) {
4
5
  super(manager);
@@ -35,10 +36,11 @@ class SVGLoader extends Loader {
35
36
  if (node.nodeType !== 1)
36
37
  return;
37
38
  const transform = getNodeTransform(node);
38
- let traverseChildNodes = true;
39
+ let isDefsNode = false;
39
40
  let path = null;
40
41
  switch (node.nodeName) {
41
42
  case "svg":
43
+ style = parseStyle(node, style);
42
44
  break;
43
45
  case "style":
44
46
  parseCSSStylesheet(node);
@@ -76,14 +78,12 @@ class SVGLoader extends Loader {
76
78
  path = parseLineNode(node);
77
79
  break;
78
80
  case "defs":
79
- traverseChildNodes = false;
80
- break;
81
- case "mask":
82
- traverseChildNodes = false;
81
+ isDefsNode = true;
83
82
  break;
84
83
  case "use":
85
84
  style = parseStyle(node, style);
86
- const usedNodeId = node.href.baseVal.substring(1);
85
+ const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || "";
86
+ const usedNodeId = href.substring(1);
87
87
  const usedNode = node.viewportElement.getElementById(usedNodeId);
88
88
  if (usedNode) {
89
89
  parseNode(usedNode, style);
@@ -94,17 +94,19 @@ class SVGLoader extends Loader {
94
94
  }
95
95
  if (path) {
96
96
  if (style.fill !== void 0 && style.fill !== "none") {
97
- path.color.setStyle(style.fill);
97
+ path.color.setStyle(style.fill, COLOR_SPACE_SVG);
98
98
  }
99
99
  transformPath(path, currentTransform);
100
100
  paths.push(path);
101
101
  path.userData = { node, style };
102
102
  }
103
- if (traverseChildNodes) {
104
- const nodes = node.childNodes;
105
- for (let i = 0; i < nodes.length; i++) {
106
- parseNode(nodes[i], style);
103
+ const childNodes = node.childNodes;
104
+ for (let i = 0; i < childNodes.length; i++) {
105
+ const node2 = childNodes[i];
106
+ if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs") {
107
+ continue;
107
108
  }
109
+ parseNode(node2, style);
108
110
  }
109
111
  if (transform) {
110
112
  transformStack.pop();
@@ -123,11 +125,13 @@ class SVGLoader extends Loader {
123
125
  let isFirstPoint = true;
124
126
  let doSetFirstPoint = false;
125
127
  const d = node.getAttribute("d");
128
+ if (d === "" || d === "none")
129
+ return null;
126
130
  const commands = d.match(/[a-df-z][^a-df-z]*/gi);
127
131
  for (let i = 0, l = commands.length; i < l; i++) {
128
132
  const command = commands[i];
129
133
  const type = command.charAt(0);
130
- const data2 = command.substr(1).trim();
134
+ const data2 = command.slice(1).trim();
131
135
  if (isFirstPoint === true) {
132
136
  doSetFirstPoint = true;
133
137
  isFirstPoint = false;
@@ -440,7 +444,8 @@ class SVGLoader extends Loader {
440
444
  continue;
441
445
  const selectorList = stylesheet.selectorText.split(/,/gm).filter(Boolean).map((i2) => i2.trim());
442
446
  for (let j = 0; j < selectorList.length; j++) {
443
- stylesheets[selectorList[j]] = Object.assign(stylesheets[selectorList[j]] || {}, stylesheet.style);
447
+ const definitions = Object.fromEntries(Object.entries(stylesheet.style).filter(([, v]) => v !== ""));
448
+ stylesheets[selectorList[j]] = Object.assign(stylesheets[selectorList[j]] || {}, definitions);
444
449
  }
445
450
  }
446
451
  }
@@ -528,7 +533,7 @@ class SVGLoader extends Loader {
528
533
  }
529
534
  index++;
530
535
  }
531
- const regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
536
+ const regex = /([+-]?\d*\.?\d+(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
532
537
  const path = new ShapePath();
533
538
  let index = 0;
534
539
  node.getAttribute("points").replace(regex, iterator);
@@ -546,7 +551,7 @@ class SVGLoader extends Loader {
546
551
  }
547
552
  index++;
548
553
  }
549
- const regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
554
+ const regex = /([+-]?\d*\.?\d+(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g;
550
555
  const path = new ShapePath();
551
556
  let index = 0;
552
557
  node.getAttribute("points").replace(regex, iterator);
@@ -598,13 +603,12 @@ class SVGLoader extends Loader {
598
603
  stylesheetStyles = Object.assign(stylesheetStyles, stylesheets["#" + node.getAttribute("id")]);
599
604
  }
600
605
  function addStyle(svgName, jsName, adjustFunction) {
601
- if (adjustFunction === void 0) {
606
+ if (adjustFunction === void 0)
602
607
  adjustFunction = function copy(v) {
603
608
  if (v.startsWith("url"))
604
609
  console.warn("SVGLoader: url access in attributes is not implemented.");
605
610
  return v;
606
611
  };
607
- }
608
612
  if (node.hasAttribute(svgName))
609
613
  style[jsName] = adjustFunction(node.getAttribute(svgName));
610
614
  if (stylesheetStyles[svgName])
@@ -869,14 +873,14 @@ class SVGLoader extends Loader {
869
873
  const openParPos = transformText.indexOf("(");
870
874
  const closeParPos = transformText.length;
871
875
  if (openParPos > 0 && openParPos < closeParPos) {
872
- const transformType = transformText.substr(0, openParPos);
873
- const array = parseFloats(transformText.substr(openParPos + 1, closeParPos - openParPos - 1));
876
+ const transformType = transformText.slice(0, openParPos);
877
+ const array = parseFloats(transformText.slice(openParPos + 1));
874
878
  currentTransform2.identity();
875
879
  switch (transformType) {
876
880
  case "translate":
877
881
  if (array.length >= 1) {
878
882
  const tx = array[0];
879
- let ty = tx;
883
+ let ty = 0;
880
884
  if (array.length >= 2) {
881
885
  ty = array[1];
882
886
  }
@@ -888,15 +892,15 @@ class SVGLoader extends Loader {
888
892
  let angle = 0;
889
893
  let cx = 0;
890
894
  let cy = 0;
891
- angle = -array[0] * Math.PI / 180;
895
+ angle = array[0] * Math.PI / 180;
892
896
  if (array.length >= 3) {
893
897
  cx = array[1];
894
898
  cy = array[2];
895
899
  }
896
- tempTransform1.identity().translate(-cx, -cy);
897
- tempTransform2.identity().rotate(angle);
900
+ tempTransform1.makeTranslation(-cx, -cy);
901
+ tempTransform2.makeRotation(angle);
898
902
  tempTransform3.multiplyMatrices(tempTransform2, tempTransform1);
899
- tempTransform1.identity().translate(cx, cy);
903
+ tempTransform1.makeTranslation(cx, cy);
900
904
  currentTransform2.multiplyMatrices(tempTransform1, tempTransform3);
901
905
  }
902
906
  break;
@@ -937,7 +941,55 @@ class SVGLoader extends Loader {
937
941
  tempV3.set(v2.x, v2.y, 1).applyMatrix3(m);
938
942
  v2.set(tempV3.x, tempV3.y);
939
943
  }
940
- const isRotated = isTransformRotated(m);
944
+ function transfEllipseGeneric(curve) {
945
+ const a = curve.xRadius;
946
+ const b = curve.yRadius;
947
+ const cosTheta = Math.cos(curve.aRotation);
948
+ const sinTheta = Math.sin(curve.aRotation);
949
+ const v1 = new Vector3(a * cosTheta, a * sinTheta, 0);
950
+ const v2 = new Vector3(-b * sinTheta, b * cosTheta, 0);
951
+ const f1 = v1.applyMatrix3(m);
952
+ const f2 = v2.applyMatrix3(m);
953
+ const mF = tempTransform0.set(f1.x, f2.x, 0, f1.y, f2.y, 0, 0, 0, 1);
954
+ const mFInv = tempTransform1.copy(mF).invert();
955
+ const mFInvT = tempTransform2.copy(mFInv).transpose();
956
+ const mQ = mFInvT.multiply(mFInv);
957
+ const mQe = mQ.elements;
958
+ const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]);
959
+ const rt1sqrt = Math.sqrt(ed.rt1);
960
+ const rt2sqrt = Math.sqrt(ed.rt2);
961
+ curve.xRadius = 1 / rt1sqrt;
962
+ curve.yRadius = 1 / rt2sqrt;
963
+ curve.aRotation = Math.atan2(ed.sn, ed.cs);
964
+ const isFullEllipse = (curve.aEndAngle - curve.aStartAngle) % (2 * Math.PI) < Number.EPSILON;
965
+ if (!isFullEllipse) {
966
+ const mDsqrt = tempTransform1.set(rt1sqrt, 0, 0, 0, rt2sqrt, 0, 0, 0, 1);
967
+ const mRT = tempTransform2.set(ed.cs, ed.sn, 0, -ed.sn, ed.cs, 0, 0, 0, 1);
968
+ const mDRF = mDsqrt.multiply(mRT).multiply(mF);
969
+ const transformAngle = (phi) => {
970
+ const { x: cosR, y: sinR } = new Vector3(Math.cos(phi), Math.sin(phi), 0).applyMatrix3(mDRF);
971
+ return Math.atan2(sinR, cosR);
972
+ };
973
+ curve.aStartAngle = transformAngle(curve.aStartAngle);
974
+ curve.aEndAngle = transformAngle(curve.aEndAngle);
975
+ if (isTransformFlipped(m)) {
976
+ curve.aClockwise = !curve.aClockwise;
977
+ }
978
+ }
979
+ }
980
+ function transfEllipseNoSkew(curve) {
981
+ const sx = getTransformScaleX(m);
982
+ const sy = getTransformScaleY(m);
983
+ curve.xRadius *= sx;
984
+ curve.yRadius *= sy;
985
+ const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]);
986
+ curve.aRotation += theta;
987
+ if (isTransformFlipped(m)) {
988
+ curve.aStartAngle *= -1;
989
+ curve.aEndAngle *= -1;
990
+ curve.aClockwise = !curve.aClockwise;
991
+ }
992
+ }
941
993
  const subPaths = path.subPaths;
942
994
  for (let i = 0, n = subPaths.length; i < n; i++) {
943
995
  const subPath = subPaths[i];
@@ -957,21 +1009,31 @@ class SVGLoader extends Loader {
957
1009
  transfVec2(curve.v1);
958
1010
  transfVec2(curve.v2);
959
1011
  } else if (curve.isEllipseCurve) {
960
- if (isRotated) {
961
- console.warn("SVGLoader: Elliptic arc or ellipse rotation or skewing is not implemented.");
962
- }
963
1012
  tempV2.set(curve.aX, curve.aY);
964
1013
  transfVec2(tempV2);
965
1014
  curve.aX = tempV2.x;
966
1015
  curve.aY = tempV2.y;
967
- curve.xRadius *= getTransformScaleX(m);
968
- curve.yRadius *= getTransformScaleY(m);
1016
+ if (isTransformSkewed(m)) {
1017
+ transfEllipseGeneric(curve);
1018
+ } else {
1019
+ transfEllipseNoSkew(curve);
1020
+ }
969
1021
  }
970
1022
  }
971
1023
  }
972
1024
  }
973
- function isTransformRotated(m) {
974
- return m.elements[1] !== 0 || m.elements[3] !== 0;
1025
+ function isTransformFlipped(m) {
1026
+ const te = m.elements;
1027
+ return te[0] * te[4] - te[1] * te[3] < 0;
1028
+ }
1029
+ function isTransformSkewed(m) {
1030
+ const te = m.elements;
1031
+ const basisDot = te[0] * te[3] + te[1] * te[4];
1032
+ if (basisDot === 0)
1033
+ return false;
1034
+ const sx = getTransformScaleX(m);
1035
+ const sy = getTransformScaleY(m);
1036
+ return Math.abs(basisDot / (sx * sy)) > Number.EPSILON;
975
1037
  }
976
1038
  function getTransformScaleX(m) {
977
1039
  const te = m.elements;
@@ -981,6 +1043,45 @@ class SVGLoader extends Loader {
981
1043
  const te = m.elements;
982
1044
  return Math.sqrt(te[3] * te[3] + te[4] * te[4]);
983
1045
  }
1046
+ function eigenDecomposition(A, B, C) {
1047
+ let rt1, rt2, cs, sn, t;
1048
+ const sm = A + C;
1049
+ const df = A - C;
1050
+ const rt = Math.sqrt(df * df + 4 * B * B);
1051
+ if (sm > 0) {
1052
+ rt1 = 0.5 * (sm + rt);
1053
+ t = 1 / rt1;
1054
+ rt2 = A * t * C - B * t * B;
1055
+ } else if (sm < 0) {
1056
+ rt2 = 0.5 * (sm - rt);
1057
+ } else {
1058
+ rt1 = 0.5 * rt;
1059
+ rt2 = -0.5 * rt;
1060
+ }
1061
+ if (df > 0) {
1062
+ cs = df + rt;
1063
+ } else {
1064
+ cs = df - rt;
1065
+ }
1066
+ if (Math.abs(cs) > 2 * Math.abs(B)) {
1067
+ t = -2 * B / cs;
1068
+ sn = 1 / Math.sqrt(1 + t * t);
1069
+ cs = t * sn;
1070
+ } else if (Math.abs(B) === 0) {
1071
+ cs = 1;
1072
+ sn = 0;
1073
+ } else {
1074
+ t = -0.5 * cs / B;
1075
+ cs = 1 / Math.sqrt(1 + t * t);
1076
+ sn = t * cs;
1077
+ }
1078
+ if (df > 0) {
1079
+ t = cs;
1080
+ cs = -sn;
1081
+ sn = t;
1082
+ }
1083
+ return { rt1, rt2, cs, sn };
1084
+ }
984
1085
  const paths = [];
985
1086
  const stylesheets = {};
986
1087
  const transformStack = [];
@@ -1180,13 +1281,13 @@ class SVGLoader extends Loader {
1180
1281
  let isHoleFor = null;
1181
1282
  let lastCWValue = null;
1182
1283
  for (let i2 = 0; i2 < stack.length; i2++) {
1183
- const identifier2 = stack[i2];
1284
+ const identifier = stack[i2];
1184
1285
  if (isHole) {
1185
- lastCWValue = allPaths[identifier2].isCW;
1286
+ lastCWValue = allPaths[identifier].isCW;
1186
1287
  isHole = false;
1187
- isHoleFor = identifier2;
1188
- } else if (lastCWValue !== allPaths[identifier2].isCW) {
1189
- lastCWValue = allPaths[identifier2].isCW;
1288
+ isHoleFor = identifier;
1289
+ } else if (lastCWValue !== allPaths[identifier].isCW) {
1290
+ lastCWValue = allPaths[identifier].isCW;
1190
1291
  isHole = true;
1191
1292
  }
1192
1293
  }
@@ -1195,7 +1296,6 @@ class SVGLoader extends Loader {
1195
1296
  console.warn('fill-rule: "' + _fillRule + '" is currently not implemented.');
1196
1297
  }
1197
1298
  }
1198
- let identifier = 0;
1199
1299
  let scanlineMinX = BIGNUMBER;
1200
1300
  let scanlineMaxX = -BIGNUMBER;
1201
1301
  let simplePaths = shapePath.subPaths.map((p) => {
@@ -1229,13 +1329,22 @@ class SVGLoader extends Loader {
1229
1329
  curves: p.curves,
1230
1330
  points,
1231
1331
  isCW: ShapeUtils.isClockWise(points),
1232
- identifier: identifier++,
1332
+ identifier: -1,
1233
1333
  boundingBox: new Box2(new Vector2(minX, minY), new Vector2(maxX, maxY))
1234
1334
  };
1235
1335
  });
1236
1336
  simplePaths = simplePaths.filter((sp) => sp.points.length > 1);
1337
+ for (let identifier = 0; identifier < simplePaths.length; identifier++) {
1338
+ simplePaths[identifier].identifier = identifier;
1339
+ }
1237
1340
  const isAHole = simplePaths.map(
1238
- (p) => isHoleTo(p, simplePaths, scanlineMinX, scanlineMaxX, shapePath.userData.style.fillRule)
1341
+ (p) => isHoleTo(
1342
+ p,
1343
+ simplePaths,
1344
+ scanlineMinX,
1345
+ scanlineMaxX,
1346
+ shapePath.userData ? shapePath.userData.style.fillRule : void 0
1347
+ )
1239
1348
  );
1240
1349
  const shapesToReturn = [];
1241
1350
  simplePaths.forEach((p) => {
@@ -1331,9 +1440,8 @@ class SVGLoader extends Loader {
1331
1440
  if (iPoint === numPoints - 1) {
1332
1441
  if (isClosed) {
1333
1442
  nextPoint = points[1];
1334
- } else {
1443
+ } else
1335
1444
  nextPoint = void 0;
1336
- }
1337
1445
  } else {
1338
1446
  nextPoint = points[iPoint + 1];
1339
1447
  }
@@ -1359,7 +1467,7 @@ class SVGLoader extends Loader {
1359
1467
  tempV2_3.subVectors(nextPoint, currentPoint);
1360
1468
  tempV2_3.normalize();
1361
1469
  const dot = Math.abs(normal1.dot(tempV2_3));
1362
- if (dot !== 0) {
1470
+ if (dot > Number.EPSILON) {
1363
1471
  const miterSide = strokeWidth2 / dot;
1364
1472
  tempV2_3.multiplyScalar(-miterSide);
1365
1473
  tempV2_4.subVectors(currentPoint, previousPoint);
@@ -1569,8 +1677,8 @@ class SVGLoader extends Loader {
1569
1677
  addVertex(lastPointL, u0, 0);
1570
1678
  addVertex(currentPointL, u1, 0);
1571
1679
  addVertex(lastPointR, u0, 1);
1572
- addVertex(currentPointL, u1, 1);
1573
- addVertex(currentPointR, u1, 0);
1680
+ addVertex(currentPointL, u1, 0);
1681
+ addVertex(currentPointR, u1, 1);
1574
1682
  }
1575
1683
  function makeSegmentWithBevelJoin(joinIsOnLeftSide2, innerSideModified2, u) {
1576
1684
  if (innerSideModified2) {
@@ -1592,8 +1700,8 @@ class SVGLoader extends Loader {
1592
1700
  addVertex(innerPoint, u1, 0);
1593
1701
  addVertex(currentPointR, u1, 1);
1594
1702
  addVertex(currentPointR, u, 1);
1595
- addVertex(nextPointR, u, 0);
1596
- addVertex(innerPoint, u, 0.5);
1703
+ addVertex(innerPoint, u, 0);
1704
+ addVertex(nextPointR, u, 1);
1597
1705
  }
1598
1706
  } else {
1599
1707
  if (joinIsOnLeftSide2) {
@@ -1659,7 +1767,7 @@ class SVGLoader extends Loader {
1659
1767
  tempV2_4.toArray(vertices, 3 * 3);
1660
1768
  } else {
1661
1769
  tempV2_3.toArray(vertices, 1 * 3);
1662
- tempV2_3.toArray(vertices, 3 * 3);
1770
+ uvs[3 * 2 + 1] === 1 ? tempV2_4.toArray(vertices, 3 * 3) : tempV2_3.toArray(vertices, 3 * 3);
1663
1771
  tempV2_4.toArray(vertices, 0 * 3);
1664
1772
  }
1665
1773
  } else {
@@ -1673,8 +1781,8 @@ class SVGLoader extends Loader {
1673
1781
  tempV2_4.toArray(vertices, vl - 2 * 3);
1674
1782
  tempV2_4.toArray(vertices, vl - 4 * 3);
1675
1783
  } else {
1676
- tempV2_3.toArray(vertices, vl - 2 * 3);
1677
- tempV2_4.toArray(vertices, vl - 1 * 3);
1784
+ tempV2_4.toArray(vertices, vl - 2 * 3);
1785
+ tempV2_3.toArray(vertices, vl - 1 * 3);
1678
1786
  tempV2_4.toArray(vertices, vl - 4 * 3);
1679
1787
  }
1680
1788
  }