greasedLineRibbonMesh.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. import { Quaternion, TmpVectors, Vector3 } from "../../Maths/math.vector.js";
  2. import { Mesh } from "../mesh.js";
  3. import { Buffer } from "../../Buffers/buffer.js";
  4. import { DeepCopier } from "../../Misc/deepCopier.js";
  5. import { GreasedLineTools } from "../../Misc/greasedLineTools.js";
  6. import { GreasedLineBaseMesh, GreasedLineRibbonAutoDirectionMode, GreasedLineRibbonFacesMode, GreasedLineRibbonPointsMode } from "./greasedLineBaseMesh.js";
  7. Mesh._GreasedLineRibbonMeshParser = (parsedMesh, scene) => {
  8. return GreasedLineRibbonMesh.Parse(parsedMesh, scene);
  9. };
  10. /**
  11. * GreasedLineRibbonMesh
  12. * Use the GreasedLineBuilder.CreateGreasedLine function to create an instance of this class.
  13. */
  14. export class GreasedLineRibbonMesh extends GreasedLineBaseMesh {
  15. /**
  16. * GreasedLineRibbonMesh
  17. * @param name name of the mesh
  18. * @param scene the scene
  19. * @param _options mesh options
  20. * @param _pathOptions used internaly when parsing a serialized GreasedLineRibbonMesh
  21. */
  22. constructor(name, scene, _options, _pathOptions) {
  23. super(name, scene, _options);
  24. this.name = name;
  25. if (!_options.ribbonOptions) {
  26. // eslint-disable-next-line no-throw-literal
  27. throw "'GreasedLineMeshOptions.ribbonOptions' is not set.";
  28. }
  29. this._paths = [];
  30. this._counters = [];
  31. this._slopes = [];
  32. this._widths = _options.widths ?? [];
  33. this._ribbonWidths = [];
  34. this._pathsOptions = _pathOptions ?? [];
  35. if (_options.points) {
  36. this.addPoints(GreasedLineTools.ConvertPoints(_options.points), _options, !!_pathOptions);
  37. }
  38. }
  39. /**
  40. * Adds new points to the line. It doesn't rerenders the line if in lazy mode.
  41. * @param points points table
  42. * @param options mesh options
  43. * @param hasPathOptions defaults to false
  44. */
  45. addPoints(points, options, hasPathOptions = false) {
  46. if (!options.ribbonOptions) {
  47. // eslint-disable-next-line no-throw-literal
  48. throw "addPoints() on GreasedLineRibbonMesh instance requires 'GreasedLineMeshOptions.ribbonOptions'.";
  49. }
  50. if (!hasPathOptions) {
  51. this._pathsOptions.push({ options, pathCount: points.length });
  52. }
  53. super.addPoints(points, options);
  54. }
  55. /**
  56. * "GreasedLineRibbonMesh"
  57. * @returns "GreasedLineRibbonMesh"
  58. */
  59. getClassName() {
  60. return "GreasedLineRibbonMesh";
  61. }
  62. /**
  63. * Return true if the line was created from two edge paths or one points path.
  64. * In this case the line is always flat.
  65. */
  66. get isFlatLine() {
  67. return this._paths.length < 3;
  68. }
  69. /**
  70. * Returns the slopes of the line at each point relative to the center of the line
  71. */
  72. get slopes() {
  73. return this._slopes;
  74. }
  75. /**
  76. * Set the slopes of the line at each point relative to the center of the line
  77. */
  78. set slopes(slopes) {
  79. this._slopes = slopes;
  80. }
  81. _updateColorPointers() {
  82. if (this._options.colorPointers) {
  83. return;
  84. }
  85. let colorPointer = 0;
  86. this._colorPointers = [];
  87. for (let i = 0; i < this._pathsOptions.length; i++) {
  88. const { options: pathOptions, pathCount } = this._pathsOptions[i];
  89. const points = this._points[i];
  90. if (pathOptions.ribbonOptions.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_POINTS) {
  91. for (let k = 0; k < pathCount; k++) {
  92. for (let j = 0; j < points.length; j += 3) {
  93. this._colorPointers.push(colorPointer);
  94. this._colorPointers.push(colorPointer++);
  95. }
  96. }
  97. }
  98. else {
  99. for (let j = 0; j < points.length; j += 3) {
  100. for (let k = 0; k < pathCount; k++) {
  101. this._colorPointers.push(colorPointer);
  102. }
  103. colorPointer++;
  104. }
  105. }
  106. }
  107. }
  108. _updateWidths() {
  109. super._updateWidthsWithValue(1);
  110. }
  111. _setPoints(points, _options) {
  112. if (!this._options.ribbonOptions) {
  113. // eslint-disable-next-line no-throw-literal
  114. throw "No 'GreasedLineMeshOptions.ribbonOptions' provided.";
  115. }
  116. this._points = points;
  117. this._options.points = points;
  118. this._initGreasedLine();
  119. let indiceOffset = 0;
  120. let directionPlanes;
  121. for (let i = 0, c = 0; i < this._pathsOptions.length; i++) {
  122. const { options: pathOptions, pathCount } = this._pathsOptions[i];
  123. const subPoints = points.slice(c, c + pathCount);
  124. c += pathCount;
  125. if (pathOptions.ribbonOptions?.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_PATHS) {
  126. indiceOffset = this._preprocess(GreasedLineTools.ToVector3Array(subPoints), indiceOffset, pathOptions);
  127. }
  128. else {
  129. if (pathOptions.ribbonOptions?.directionsAutoMode === GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_NONE) {
  130. if (!pathOptions.ribbonOptions.directions) {
  131. // eslint-disable-next-line no-throw-literal
  132. throw "In GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_NONE 'GreasedLineMeshOptions.ribbonOptions.directions' must be defined.";
  133. }
  134. directionPlanes = GreasedLineRibbonMesh._GetDirectionPlanesFromDirectionsOption(subPoints.length, pathOptions.ribbonOptions.directions);
  135. }
  136. subPoints.forEach((p, idx) => {
  137. const pathArray = GreasedLineRibbonMesh._ConvertToRibbonPath(p, pathOptions.ribbonOptions, this._scene.useRightHandedSystem, directionPlanes ? directionPlanes[idx] : directionPlanes);
  138. indiceOffset = this._preprocess(pathArray, indiceOffset, pathOptions);
  139. });
  140. }
  141. }
  142. if (!this._lazy) {
  143. this._createVertexBuffers();
  144. this.refreshBoundingInfo();
  145. }
  146. }
  147. static _GetDirectionPlanesFromDirectionsOption(count, directions) {
  148. if (Array.isArray(directions)) {
  149. return directions;
  150. }
  151. return new Array(count).fill(directions);
  152. }
  153. static _CreateRibbonVertexData(pathArray, options) {
  154. const numOfPaths = pathArray.length;
  155. if (numOfPaths < 2) {
  156. // eslint-disable-next-line no-throw-literal
  157. throw "Minimum of two paths are required to create a GreasedLineRibbonMesh.";
  158. }
  159. const positions = [];
  160. const indices = [];
  161. const path = pathArray[0];
  162. for (let i = 0; i < path.length; i++) {
  163. for (let pi = 0; pi < pathArray.length; pi++) {
  164. const v = pathArray[pi][i];
  165. positions.push(v.x, v.y, v.z);
  166. }
  167. }
  168. const v = [1, 0, numOfPaths];
  169. const doubleSided = options.ribbonOptions?.facesMode === GreasedLineRibbonFacesMode.FACES_MODE_DOUBLE_SIDED ?? false;
  170. const closePath = options.ribbonOptions?.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_PATHS && options.ribbonOptions.closePath;
  171. if (numOfPaths > 2) {
  172. for (let i = 0; i < path.length - 1; i++) {
  173. v[0] = 1 + numOfPaths * i;
  174. v[1] = numOfPaths * i;
  175. v[2] = (i + 1) * numOfPaths;
  176. for (let pi = 0; pi < (numOfPaths - 1) * 2; pi++) {
  177. if (pi % 2 !== 0) {
  178. v[2] += 1;
  179. }
  180. if (pi % 2 === 0 && pi > 0) {
  181. v[0] += 1;
  182. v[1] += 1;
  183. }
  184. indices.push(v[1] + (pi % 2 !== 0 ? numOfPaths : 0), v[0], v[2]);
  185. if (doubleSided) {
  186. indices.push(v[0], v[1] + (pi % 2 !== 0 ? numOfPaths : 0), v[2]);
  187. }
  188. }
  189. }
  190. }
  191. else {
  192. for (let i = 0; i < positions.length / 3 - 3; i += 2) {
  193. indices.push(i, i + 1, i + 2);
  194. indices.push(i + 2, i + 1, i + 3);
  195. if (doubleSided) {
  196. indices.push(i + 1, i, i + 2);
  197. indices.push(i + 1, i + 2, i + 3);
  198. }
  199. }
  200. }
  201. if (closePath) {
  202. let lastIndice = numOfPaths * (path.length - 1);
  203. for (let pi = 0; pi < numOfPaths - 1; pi++) {
  204. indices.push(lastIndice, pi + 1, pi);
  205. indices.push(lastIndice + 1, pi + 1, lastIndice);
  206. if (doubleSided) {
  207. indices.push(pi, pi + 1, lastIndice);
  208. indices.push(lastIndice, pi + 1, lastIndice + 1);
  209. }
  210. lastIndice++;
  211. }
  212. }
  213. return {
  214. positions,
  215. indices,
  216. };
  217. }
  218. _preprocess(pathArray, indiceOffset, options) {
  219. this._paths = pathArray;
  220. const ribbonVertexData = GreasedLineRibbonMesh._CreateRibbonVertexData(pathArray, options);
  221. const positions = ribbonVertexData.positions;
  222. if (!this._options.widths) {
  223. // eslint-disable-next-line no-throw-literal
  224. throw "No 'GreasedLineMeshOptions.widths' table is specified.";
  225. }
  226. const vertexPositions = Array.isArray(this._vertexPositions) ? this._vertexPositions : Array.from(this._vertexPositions);
  227. this._vertexPositions = vertexPositions;
  228. const uvs = Array.isArray(this._uvs) ? this._uvs : Array.from(this._uvs);
  229. this._uvs = uvs;
  230. const indices = Array.isArray(this._indices) ? this._indices : Array.from(this._indices);
  231. this._indices = indices;
  232. for (const p of positions) {
  233. vertexPositions.push(p);
  234. }
  235. let pathArrayCopy = pathArray;
  236. if (options.ribbonOptions?.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_PATHS && options.ribbonOptions.closePath) {
  237. pathArrayCopy = [];
  238. for (let i = 0; i < pathArray.length; i++) {
  239. const pathCopy = pathArray[i].slice();
  240. pathCopy.push(pathArray[i][0].clone());
  241. pathArrayCopy.push(pathCopy);
  242. }
  243. }
  244. this._calculateSegmentLengths(pathArrayCopy);
  245. const pathArrayLength = pathArrayCopy.length;
  246. const previousCounters = new Array(pathArrayLength).fill(0);
  247. for (let i = 0; i < pathArrayCopy[0].length; i++) {
  248. let v = 0;
  249. for (let pi = 0; pi < pathArrayLength; pi++) {
  250. const counter = previousCounters[pi] + this._vSegmentLengths[pi][i] / this._vTotalLengths[pi];
  251. this._counters.push(counter);
  252. uvs.push(counter, v);
  253. previousCounters[pi] = counter;
  254. v += this._uSegmentLengths[i][pi] / this._uTotalLengths[i];
  255. }
  256. }
  257. for (let i = 0, c = 0; i < pathArrayCopy[0].length; i++) {
  258. const widthLower = this._uSegmentLengths[i][0] / 2;
  259. const widthUpper = this._uSegmentLengths[i][pathArrayLength - 1] / 2;
  260. this._ribbonWidths.push(((this._widths[c++] ?? 1) - 1) * widthLower);
  261. for (let pi = 0; pi < pathArrayLength - 2; pi++) {
  262. this._ribbonWidths.push(0);
  263. }
  264. this._ribbonWidths.push(((this._widths[c++] ?? 1) - 1) * widthUpper);
  265. }
  266. const slopes = options.ribbonOptions?.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_PATHS
  267. ? new Array(pathArrayCopy[0].length * pathArrayCopy.length * 6).fill(0)
  268. : GreasedLineRibbonMesh._CalculateSlopes(pathArrayCopy);
  269. for (const s of slopes) {
  270. this._slopes.push(s);
  271. }
  272. if (ribbonVertexData.indices) {
  273. for (let i = 0; i < ribbonVertexData.indices.length; i++) {
  274. indices.push(ribbonVertexData.indices[i] + indiceOffset);
  275. }
  276. }
  277. indiceOffset += positions.length / 3;
  278. return indiceOffset;
  279. }
  280. static _ConvertToRibbonPath(points, ribbonInfo, rightHandedSystem, directionPlane) {
  281. if (ribbonInfo.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_POINTS && !ribbonInfo.width) {
  282. // eslint-disable-next-line no-throw-literal
  283. throw "'GreasedLineMeshOptions.ribbonOptiosn.width' must be specified in GreasedLineRibbonPointsMode.POINTS_MODE_POINTS.";
  284. }
  285. const path1 = [];
  286. const path2 = [];
  287. if (ribbonInfo.pointsMode === GreasedLineRibbonPointsMode.POINTS_MODE_POINTS) {
  288. const width = ribbonInfo.width / 2;
  289. const pointVectors = GreasedLineTools.ToVector3Array(points);
  290. let direction = null;
  291. let fatDirection = null;
  292. if (ribbonInfo.directionsAutoMode === GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_FROM_FIRST_SEGMENT) {
  293. // set the direction plane from the first line segment for the whole line
  294. directionPlane = GreasedLineRibbonMesh._GetDirectionFromPoints(pointVectors[0], pointVectors[1], null);
  295. }
  296. if (ribbonInfo.directionsAutoMode === GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_FACE_TO && !(ribbonInfo.directions instanceof Vector3)) {
  297. // eslint-disable-next-line no-throw-literal
  298. throw "In GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_FACE_TO 'GreasedLineMeshOptions.ribbonOptions.directions' must be a Vector3.";
  299. }
  300. TmpVectors.Vector3[1] = ribbonInfo.directions instanceof Vector3 ? ribbonInfo.directions : GreasedLineRibbonMesh.DIRECTION_XZ;
  301. for (let i = 0; i < pointVectors.length - (directionPlane ? 0 : 1); i++) {
  302. const p1 = pointVectors[i];
  303. const p2 = pointVectors[i + 1];
  304. if (directionPlane) {
  305. direction = directionPlane;
  306. }
  307. else if (ribbonInfo.directionsAutoMode === GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_FACE_TO) {
  308. p2.subtractToRef(p1, TmpVectors.Vector3[0]);
  309. direction = Vector3.CrossToRef(TmpVectors.Vector3[0], TmpVectors.Vector3[1], TmpVectors.Vector3[2]).normalize();
  310. }
  311. else if (ribbonInfo.directionsAutoMode === GreasedLineRibbonAutoDirectionMode.AUTO_DIRECTIONS_FROM_ALL_SEGMENTS) {
  312. direction = GreasedLineRibbonMesh._GetDirectionFromPoints(p1, p2, direction);
  313. }
  314. else {
  315. // GreasedLineRibbonAutoDirectionMode.DIRECTION_ENHANCED
  316. const directionTemp = p2.subtract(p1);
  317. directionTemp.applyRotationQuaternionInPlace(directionTemp.x > directionTemp.y && directionTemp.x > directionTemp.z
  318. ? rightHandedSystem
  319. ? GreasedLineRibbonMesh._RightHandedForwardReadOnlyQuaternion
  320. : GreasedLineRibbonMesh._LeftHandedForwardReadOnlyQuaternion
  321. : GreasedLineRibbonMesh._LeftReadOnlyQuaternion);
  322. direction = directionTemp.normalize();
  323. }
  324. fatDirection = direction.multiplyByFloats(width, width, width);
  325. path1.push(p1.add(fatDirection));
  326. path2.push(p1.subtract(fatDirection));
  327. }
  328. if (!directionPlane) {
  329. path1.push(pointVectors[pointVectors.length - 1].add(fatDirection));
  330. path2.push(pointVectors[pointVectors.length - 1].subtract(fatDirection));
  331. }
  332. }
  333. return [path1, path2];
  334. }
  335. static _GetDirectionFromPoints(p1, p2, previousDirection) {
  336. // handle straight lines
  337. if (p1.x === p2.x && (!previousDirection || previousDirection?.x === 1)) {
  338. return GreasedLineRibbonMesh.DIRECTION_YZ;
  339. }
  340. if (p1.y === p2.y) {
  341. return GreasedLineRibbonMesh.DIRECTION_XZ;
  342. }
  343. if (p1.z === p2.z) {
  344. return GreasedLineRibbonMesh.DIRECTION_XY;
  345. }
  346. return GreasedLineRibbonMesh.DIRECTION_XZ;
  347. }
  348. /**
  349. * Clones the GreasedLineRibbonMesh.
  350. * @param name new line name
  351. * @param newParent new parent node
  352. * @returns cloned line
  353. */
  354. clone(name = `${this.name}-cloned`, newParent) {
  355. const lineOptions = this._createLineOptions();
  356. const deepCopiedLineOptions = {};
  357. const pathOptionsCloned = [];
  358. DeepCopier.DeepCopy(this._pathsOptions, pathOptionsCloned, undefined, undefined, true);
  359. DeepCopier.DeepCopy(lineOptions, deepCopiedLineOptions, ["instance"], undefined, true);
  360. const cloned = new GreasedLineRibbonMesh(name, this._scene, deepCopiedLineOptions, pathOptionsCloned);
  361. if (newParent) {
  362. cloned.parent = newParent;
  363. }
  364. cloned.material = this.material;
  365. return cloned;
  366. }
  367. /**
  368. * Serializes this GreasedLineRibbonMesh
  369. * @param serializationObject object to write serialization to
  370. */
  371. serialize(serializationObject) {
  372. super.serialize(serializationObject);
  373. serializationObject.type = this.getClassName();
  374. serializationObject.lineOptions = this._createLineOptions();
  375. serializationObject.pathsOptions = this._pathsOptions;
  376. }
  377. /**
  378. * Parses a serialized GreasedLineRibbonMesh
  379. * @param parsedMesh the serialized GreasedLineRibbonMesh
  380. * @param scene the scene to create the GreasedLineRibbonMesh in
  381. * @returns the created GreasedLineRibbonMesh
  382. */
  383. static Parse(parsedMesh, scene) {
  384. const lineOptions = parsedMesh.lineOptions;
  385. const name = parsedMesh.name;
  386. const pathOptions = parsedMesh.pathOptions;
  387. const result = new GreasedLineRibbonMesh(name, scene, lineOptions, pathOptions);
  388. return result;
  389. }
  390. _initGreasedLine() {
  391. super._initGreasedLine();
  392. this._paths = [];
  393. this._counters = [];
  394. this._slopes = [];
  395. this._ribbonWidths = [];
  396. }
  397. _calculateSegmentLengths(pathArray) {
  398. const pathArrayLength = pathArray.length;
  399. this._vSegmentLengths = new Array(pathArrayLength);
  400. this._vTotalLengths = new Array(pathArrayLength);
  401. let length = 0;
  402. for (let pi = 0; pi < pathArrayLength; pi++) {
  403. const points = pathArray[pi];
  404. this._vSegmentLengths[pi] = [0]; // first point has 0 distance
  405. length = 0;
  406. for (let i = 0; i < points.length - 1; i++) {
  407. const l = Math.abs(points[i].subtract(points[i + 1]).lengthSquared()); // it's ok to have lengthSquared() here
  408. length += l;
  409. this._vSegmentLengths[pi].push(l);
  410. }
  411. this._vTotalLengths[pi] = length;
  412. }
  413. const positionsLength = pathArray[0].length;
  414. this._uSegmentLengths = new Array(positionsLength).fill([]);
  415. this._uTotalLengths = new Array(positionsLength).fill([]);
  416. const uLength = new Vector3();
  417. for (let i = 0; i < positionsLength; i++) {
  418. length = 0;
  419. for (let pi = 1; pi < pathArrayLength; pi++) {
  420. pathArray[pi][i].subtractToRef(pathArray[pi - 1][i], uLength);
  421. const l = uLength.length(); // must be length()
  422. length += l;
  423. this._uSegmentLengths[i].push(l);
  424. }
  425. this._uTotalLengths[i] = length;
  426. }
  427. }
  428. static _CalculateSlopes(paths) {
  429. const points1 = paths[0];
  430. const points2 = paths.length === 2 ? paths[1] : paths[paths.length - 1];
  431. const slopes = [];
  432. const slope = new Vector3();
  433. for (let i = 0; i < points1.length; i++) {
  434. for (let pi = 0; pi < paths.length; pi++) {
  435. if (pi === 0 || pi === paths.length - 1) {
  436. points1[i].subtract(points2[i]).normalizeToRef(slope);
  437. slopes.push(slope.x, slope.y, slope.z);
  438. slopes.push(-slope.x, -slope.y, -slope.z);
  439. }
  440. else {
  441. slopes.push(0, 0, 0, 0, 0, 0);
  442. }
  443. }
  444. }
  445. return slopes;
  446. }
  447. _createVertexBuffers() {
  448. this._uvs = this._options.uvs ?? this._uvs;
  449. const vertexData = super._createVertexBuffers(this._options.ribbonOptions?.smoothShading);
  450. const countersBuffer = new Buffer(this._engine, this._counters, this._updatable, 1);
  451. this.setVerticesBuffer(countersBuffer.createVertexBuffer("grl_counters", 0, 1));
  452. const colorPointersBuffer = new Buffer(this._engine, this._colorPointers, this._updatable, 1);
  453. this.setVerticesBuffer(colorPointersBuffer.createVertexBuffer("grl_colorPointers", 0, 1));
  454. const slopesBuffer = new Buffer(this._engine, this._slopes, this._updatable, 3);
  455. this.setVerticesBuffer(slopesBuffer.createVertexBuffer("grl_slopes", 0, 3));
  456. const widthsBuffer = new Buffer(this._engine, this._ribbonWidths, this._updatable, 1);
  457. this.setVerticesBuffer(widthsBuffer.createVertexBuffer("grl_widths", 0, 1));
  458. this._widthsBuffer = widthsBuffer;
  459. return vertexData;
  460. }
  461. }
  462. /**
  463. * Default line width
  464. */
  465. GreasedLineRibbonMesh.DEFAULT_WIDTH = 0.1;
  466. GreasedLineRibbonMesh._RightHandedForwardReadOnlyQuaternion = Quaternion.RotationAxis(Vector3.RightHandedForwardReadOnly, Math.PI / 2);
  467. GreasedLineRibbonMesh._LeftHandedForwardReadOnlyQuaternion = Quaternion.RotationAxis(Vector3.LeftHandedForwardReadOnly, Math.PI / 2);
  468. GreasedLineRibbonMesh._LeftReadOnlyQuaternion = Quaternion.RotationAxis(Vector3.LeftReadOnly, Math.PI / 2);
  469. /**
  470. * Direction which the line segment will be thickened if drawn on the XY plane
  471. */
  472. GreasedLineRibbonMesh.DIRECTION_XY = Vector3.LeftHandedForwardReadOnly; // doesn't matter in which handed system the scene operates
  473. /**
  474. * Direction which the line segment will be thickened if drawn on the XZ plane
  475. */
  476. GreasedLineRibbonMesh.DIRECTION_XZ = Vector3.UpReadOnly;
  477. /**
  478. * Direction which the line segment will be thickened if drawn on the YZ plane
  479. */
  480. GreasedLineRibbonMesh.DIRECTION_YZ = Vector3.LeftReadOnly;
  481. //# sourceMappingURL=greasedLineRibbonMesh.js.map