three-stdlib 2.29.7 → 2.29.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }