@itwin/core-geometry 4.4.0-dev.2 → 4.4.0-dev.20

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 (66) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/lib/cjs/geometry3d/YawPitchRollAngles.d.ts +7 -5
  3. package/lib/cjs/geometry3d/YawPitchRollAngles.d.ts.map +1 -1
  4. package/lib/cjs/geometry3d/YawPitchRollAngles.js +8 -6
  5. package/lib/cjs/geometry3d/YawPitchRollAngles.js.map +1 -1
  6. package/lib/cjs/serialization/GeometrySamples.d.ts +11 -0
  7. package/lib/cjs/serialization/GeometrySamples.d.ts.map +1 -1
  8. package/lib/cjs/serialization/GeometrySamples.js +16 -0
  9. package/lib/cjs/serialization/GeometrySamples.js.map +1 -1
  10. package/lib/cjs/topology/Graph.d.ts +18 -10
  11. package/lib/cjs/topology/Graph.d.ts.map +1 -1
  12. package/lib/cjs/topology/Graph.js +20 -10
  13. package/lib/cjs/topology/Graph.js.map +1 -1
  14. package/lib/cjs/topology/HalfEdgeGraphSearch.d.ts +81 -76
  15. package/lib/cjs/topology/HalfEdgeGraphSearch.d.ts.map +1 -1
  16. package/lib/cjs/topology/HalfEdgeGraphSearch.js +126 -111
  17. package/lib/cjs/topology/HalfEdgeGraphSearch.js.map +1 -1
  18. package/lib/cjs/topology/HalfEdgeGraphValidation.d.ts +23 -18
  19. package/lib/cjs/topology/HalfEdgeGraphValidation.d.ts.map +1 -1
  20. package/lib/cjs/topology/HalfEdgeGraphValidation.js +23 -18
  21. package/lib/cjs/topology/HalfEdgeGraphValidation.js.map +1 -1
  22. package/lib/cjs/topology/HalfEdgeNodeXYZUV.d.ts +17 -10
  23. package/lib/cjs/topology/HalfEdgeNodeXYZUV.d.ts.map +1 -1
  24. package/lib/cjs/topology/HalfEdgeNodeXYZUV.js +38 -17
  25. package/lib/cjs/topology/HalfEdgeNodeXYZUV.js.map +1 -1
  26. package/lib/cjs/topology/SignedDataSummary.d.ts +13 -13
  27. package/lib/cjs/topology/SignedDataSummary.d.ts.map +1 -1
  28. package/lib/cjs/topology/SignedDataSummary.js +3 -3
  29. package/lib/cjs/topology/SignedDataSummary.js.map +1 -1
  30. package/lib/cjs/topology/XYParitySearchContext.d.ts +27 -21
  31. package/lib/cjs/topology/XYParitySearchContext.d.ts.map +1 -1
  32. package/lib/cjs/topology/XYParitySearchContext.js +73 -71
  33. package/lib/cjs/topology/XYParitySearchContext.js.map +1 -1
  34. package/lib/esm/geometry3d/YawPitchRollAngles.d.ts +7 -5
  35. package/lib/esm/geometry3d/YawPitchRollAngles.d.ts.map +1 -1
  36. package/lib/esm/geometry3d/YawPitchRollAngles.js +8 -6
  37. package/lib/esm/geometry3d/YawPitchRollAngles.js.map +1 -1
  38. package/lib/esm/serialization/GeometrySamples.d.ts +11 -0
  39. package/lib/esm/serialization/GeometrySamples.d.ts.map +1 -1
  40. package/lib/esm/serialization/GeometrySamples.js +16 -0
  41. package/lib/esm/serialization/GeometrySamples.js.map +1 -1
  42. package/lib/esm/topology/Graph.d.ts +18 -10
  43. package/lib/esm/topology/Graph.d.ts.map +1 -1
  44. package/lib/esm/topology/Graph.js +20 -10
  45. package/lib/esm/topology/Graph.js.map +1 -1
  46. package/lib/esm/topology/HalfEdgeGraphSearch.d.ts +81 -76
  47. package/lib/esm/topology/HalfEdgeGraphSearch.d.ts.map +1 -1
  48. package/lib/esm/topology/HalfEdgeGraphSearch.js +126 -111
  49. package/lib/esm/topology/HalfEdgeGraphSearch.js.map +1 -1
  50. package/lib/esm/topology/HalfEdgeGraphValidation.d.ts +23 -18
  51. package/lib/esm/topology/HalfEdgeGraphValidation.d.ts.map +1 -1
  52. package/lib/esm/topology/HalfEdgeGraphValidation.js +23 -18
  53. package/lib/esm/topology/HalfEdgeGraphValidation.js.map +1 -1
  54. package/lib/esm/topology/HalfEdgeNodeXYZUV.d.ts +17 -10
  55. package/lib/esm/topology/HalfEdgeNodeXYZUV.d.ts.map +1 -1
  56. package/lib/esm/topology/HalfEdgeNodeXYZUV.js +38 -17
  57. package/lib/esm/topology/HalfEdgeNodeXYZUV.js.map +1 -1
  58. package/lib/esm/topology/SignedDataSummary.d.ts +13 -13
  59. package/lib/esm/topology/SignedDataSummary.d.ts.map +1 -1
  60. package/lib/esm/topology/SignedDataSummary.js +3 -3
  61. package/lib/esm/topology/SignedDataSummary.js.map +1 -1
  62. package/lib/esm/topology/XYParitySearchContext.d.ts +27 -21
  63. package/lib/esm/topology/XYParitySearchContext.d.ts.map +1 -1
  64. package/lib/esm/topology/XYParitySearchContext.js +73 -71
  65. package/lib/esm/topology/XYParitySearchContext.js.map +1 -1
  66. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"SignedDataSummary.js","sourceRoot":"","sources":["../../../src/topology/SignedDataSummary.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;;;AAE/F;;GAEG;AACH;;;;GAIG;AACH,MAAa,iBAAiB;IAuB5B,+CAA+C;IAC/C,YAAmB,YAAqB;QACtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACzD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,GAAG,GAAG,CAAC;QAC5D,IAAI,YAAY,EAAE;YAChB,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;SACzB;IACH,CAAC;IACD,8CAA8C;IACvC,YAAY,CAAC,IAAO,EAAE,IAAY;QACvC,IAAI,IAAI,GAAG,CAAC,EAAE;YACZ,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;YACzB,IAAI,IAAI,CAAC,iBAAiB;gBACxB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,IAAI,GAAG,IAAI,CAAC,oBAAoB,EAAE;gBACpC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;aACjC;SACF;aAAM,IAAI,IAAI,GAAG,CAAC,EAAE;YACnB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;YACzB,IAAI,IAAI,CAAC,iBAAiB;gBACxB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,IAAI,GAAG,IAAI,CAAC,oBAAoB,EAAE;gBACpC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;aACjC;SACF;aAAM;YACL,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,aAAa;gBACpB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACjC;IACH,CAAC;CACF;AA5DD,8CA4DC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n/** @packageDocumentation\r\n * @module Topology\r\n */\r\n/**\r\n * Class to accumulate statistics about a stream of signed numbers with tag items.\r\n * * All sums, counts, extrema, and item values are initialized to zero in the constructor.\r\n * * Each call to `announceItem (item, value)` updates the various sums, counts, and extrema.\r\n */\r\nexport class SignedDataSummary<T> {\r\n /** sum of all positive area items */\r\n public positiveSum: number;\r\n /** number of positive area items */\r\n public numPositive: number;\r\n /** sum of negative area items */\r\n public negativeSum: number;\r\n /** number of negative area items */\r\n public numNegative: number;\r\n /** number of zero area items */\r\n public numZero: number;\r\n /** the tag item item with the largest positive data */\r\n public largestPositiveItem?: T;\r\n /** the tag item item with the most negative data */\r\n public largestNegativeItem?: T;\r\n public largestPositiveValue: number;\r\n public largestNegativeValue: number;\r\n /** array of all negative area items */\r\n public negativeItemArray?: T[];\r\n /** array of zero area items */\r\n public zeroItemArray?: T[];\r\n /** array of positive area items */\r\n public positiveItemArray?: T[];\r\n /** setup with zero sums and optional arrays */\r\n public constructor(createArrays: boolean) {\r\n this.positiveSum = this.negativeSum = 0.0;\r\n this.numPositive = this.numNegative = this.numZero = 0.0;\r\n this.largestPositiveValue = this.largestNegativeValue = 0.0;\r\n if (createArrays) {\r\n this.negativeItemArray = [];\r\n this.positiveItemArray = [];\r\n this.zeroItemArray = [];\r\n }\r\n }\r\n /** update with an item and its data value. */\r\n public announceItem(item: T, data: number) {\r\n if (data < 0) {\r\n this.numNegative++;\r\n this.negativeSum += data;\r\n if (this.negativeItemArray)\r\n this.negativeItemArray.push(item);\r\n if (data < this.largestNegativeValue) {\r\n this.largestNegativeValue = data;\r\n this.largestNegativeItem = item;\r\n }\r\n } else if (data > 0) {\r\n this.numPositive++;\r\n this.positiveSum += data;\r\n if (this.positiveItemArray)\r\n this.positiveItemArray.push(item);\r\n if (data > this.largestPositiveValue) {\r\n this.largestPositiveValue = data;\r\n this.largestPositiveItem = item;\r\n }\r\n } else {\r\n this.numZero++;\r\n if (this.zeroItemArray)\r\n this.zeroItemArray.push(item);\r\n }\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"SignedDataSummary.js","sourceRoot":"","sources":["../../../src/topology/SignedDataSummary.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;;;AAE/F;;GAEG;AACH;;;;GAIG;AACH,MAAa,iBAAiB;IAuB5B,gDAAgD;IAChD,YAAmB,YAAqB;QACtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACzD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,GAAG,GAAG,CAAC;QAC5D,IAAI,YAAY,EAAE;YAChB,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;SACzB;IACH,CAAC;IACD,8CAA8C;IACvC,YAAY,CAAC,IAAO,EAAE,IAAY;QACvC,IAAI,IAAI,GAAG,CAAC,EAAE;YACZ,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;YACzB,IAAI,IAAI,CAAC,iBAAiB;gBACxB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,IAAI,GAAG,IAAI,CAAC,oBAAoB,EAAE;gBACpC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;aACjC;SACF;aAAM,IAAI,IAAI,GAAG,CAAC,EAAE;YACnB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;YACzB,IAAI,IAAI,CAAC,iBAAiB;gBACxB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,IAAI,GAAG,IAAI,CAAC,oBAAoB,EAAE;gBACpC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;aACjC;SACF;aAAM;YACL,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,IAAI,CAAC,aAAa;gBACpB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACjC;IACH,CAAC;CACF;AA5DD,8CA4DC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n/** @packageDocumentation\r\n * @module Topology\r\n */\r\n/**\r\n * Class to accumulate statistics about a stream of signed numbers with tag items.\r\n * * All sums, counts, extrema, and item values are initialized to zero in the constructor.\r\n * * Each call to `announceItem(item, value)` updates the various sums, counts, and extrema.\r\n */\r\nexport class SignedDataSummary<T> {\r\n /** Sum of all positive area items. */\r\n public positiveSum: number;\r\n /** Number of positive area items. */\r\n public numPositive: number;\r\n /** Sum of negative area items. */\r\n public negativeSum: number;\r\n /** Number of negative area items. */\r\n public numNegative: number;\r\n /** Number of zero area items. */\r\n public numZero: number;\r\n /** The tag item item with the largest positive data. */\r\n public largestPositiveItem?: T;\r\n /** The tag item item with the most negative data. */\r\n public largestNegativeItem?: T;\r\n public largestPositiveValue: number;\r\n public largestNegativeValue: number;\r\n /** Array of all negative area items. */\r\n public negativeItemArray?: T[];\r\n /** Array of zero area items. */\r\n public zeroItemArray?: T[];\r\n /** Array of positive area items. */\r\n public positiveItemArray?: T[];\r\n /** Setup with zero sums and optional arrays. */\r\n public constructor(createArrays: boolean) {\r\n this.positiveSum = this.negativeSum = 0.0;\r\n this.numPositive = this.numNegative = this.numZero = 0.0;\r\n this.largestPositiveValue = this.largestNegativeValue = 0.0;\r\n if (createArrays) {\r\n this.negativeItemArray = [];\r\n this.positiveItemArray = [];\r\n this.zeroItemArray = [];\r\n }\r\n }\r\n /** Update with an item and its data value. */\r\n public announceItem(item: T, data: number) {\r\n if (data < 0) {\r\n this.numNegative++;\r\n this.negativeSum += data;\r\n if (this.negativeItemArray)\r\n this.negativeItemArray.push(item);\r\n if (data < this.largestNegativeValue) {\r\n this.largestNegativeValue = data;\r\n this.largestNegativeItem = item;\r\n }\r\n } else if (data > 0) {\r\n this.numPositive++;\r\n this.positiveSum += data;\r\n if (this.positiveItemArray)\r\n this.positiveItemArray.push(item);\r\n if (data > this.largestPositiveValue) {\r\n this.largestPositiveValue = data;\r\n this.largestPositiveItem = item;\r\n }\r\n } else {\r\n this.numZero++;\r\n if (this.zeroItemArray)\r\n this.zeroItemArray.push(item);\r\n }\r\n }\r\n}\r\n"]}
@@ -2,52 +2,58 @@
2
2
  * @module Topology
3
3
  */
4
4
  /**
5
- * * XYParitySearchContext is an internal class for callers that can feed points (without extracting to array structures)
5
+ * `XYParitySearchContext` is an internal class for callers that can feed points (without extracting to array structures)
6
6
  * * Most will be via static methods which handle a specific data source.
7
- * * PolygonOps.classifyPointInPolygon (x,y,points: XAndY[])
8
- * * HalfEdgeGraphSearch.pointInOrOnFaceXY (halfEdgeOnFace, x, y)
7
+ * * PolygonOps.classifyPointInPolygon(x,y,points: XAndY[])
8
+ * * HalfEdgeGraphSearch.pointInOrOnFaceXY(halfEdgeOnFace, x, y)
9
9
  * Use pattern:
10
- * * Caller must be able walk around polygon producing x,y coordinates (possibly transformed from actual polygon)
10
+ * * Caller must be able to walk around polygon producing x,y coordinates (possibly transformed from actual polygon).
11
11
  * * Caller announce edges to tryStartEdge until finding one acceptable to the search.
12
12
  * * Caller then passes additional points up to and including both x0,y0 and x1, y1 of the accepted start edge.
13
13
  * Call sequence is:
14
- * `context = new XYParitySearchContext`
15
- * `repeat { acquire edge (x0,y0) (x1,y1)} until context.tryStartEdge (x0,y0,x1,y1);`
16
- * `for each (x,y) beginning AFTER x1,y1 and ending with (x1,y1) context.advance (x,y)`
17
- * `return context.classifyCounts ();`
14
+ * * `context = new XYParitySearchContext`
15
+ * * `repeat { acquire edge (x0,y0) (x1,y1) } until context.tryStartEdge(x0,y0,x1,y1);`
16
+ * * `for each (x,y) beginning AFTER x1,y1 and ending with (x1,y1) context.advance (x,y)`
17
+ * * `return context.classifyCounts();`
18
18
  */
19
19
  export declare class XYParitySearchContext {
20
20
  xTest: number;
21
21
  yTest: number;
22
+ /** local x-coordinate of the start of the previous (or earlier) edge */
22
23
  u0: number;
24
+ /** local y-coordinate of the start of the previous (or earlier) edge */
23
25
  v0: number;
26
+ /** local x-coordinate of the end of the previous edge (and start of current edge) */
24
27
  u1: number;
28
+ /** local y-coordinate of the end of the previous edge (and start of current edge) */
25
29
  v1: number;
26
30
  numLeftCrossing: number;
27
31
  numRightCrossing: number;
28
32
  numHit: number;
29
33
  /**
30
34
  * Create a new searcher for specified test point.
31
- * @param xTest x coordinate of test point
32
- * @param yTest y coordinate of test point
35
+ * @param xTest x coordinate of test point.
36
+ * @param yTest y coordinate of test point.
33
37
  */
34
38
  constructor(xTest: number, yTest: number);
35
- /**
36
- * test if x,y is a safe first coordinate to start the search.
37
- * * safe start must have non-zero y so that final point test (return to x0,y0) does not need look back for exact crossing logic.
38
- * @param x
39
- * @param y
40
- */
39
+ /** Test if parity processing can begin with this edge. */
41
40
  tryStartEdge(x0: number, y0: number, x1: number, y1: number): boolean;
42
- /** Return true if parity accumulation proceeded normally.
43
- * Return false if interrupted for exact hit.
41
+ /** Update local coordinates: the current edge becomes the previous edge. */
42
+ private updateUV01;
43
+ /**
44
+ * Process the current edge ending at (x2,y2).
45
+ * * Accumulate left/right parity of the test point wrt to the polygon. These counts track the number of polygon crossings
46
+ * of the left and right horizontal rays emanating from the test point. After all edges are processed, if either count is
47
+ * odd/even, the test point is inside/outside the polygon (see [[classifyCounts]]).
48
+ * * Check whether the test point lies on the edge.
49
+ * @returns whether caller should continue processing with the next edge. In particular, `false` if we have an exact hit.
44
50
  */
45
- advance(x: number, y: number): boolean;
51
+ advance(x2: number, y2: number): boolean;
46
52
  /**
47
53
  * Return classification as ON, IN, or OUT according to hit and crossing counts.
48
- * * Any nonzero hit count is ON
54
+ * * Any nonzero hit count is ON.
49
55
  * * Otherwise IN if left crossing count is odd.
50
- * @return 0 if ON, 1 if IN, -1 if OUT
56
+ * @return 0 if ON, 1 if IN, -1 if OUT.
51
57
  */
52
58
  classifyCounts(): number | undefined;
53
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"XYParitySearchContext.d.ts","sourceRoot":"","sources":["../../../src/topology/XYParitySearchContext.ts"],"names":[],"mappings":"AAKA;;GAEG;AAEH;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qBAAqB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACtB;;;;OAIG;gBACgB,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAO/C;;;;;OAKG;IACI,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAU5E;;OAEG;IACI,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAqE7C;;;;;OAKG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS;CAM5C"}
1
+ {"version":3,"file":"XYParitySearchContext.d.ts","sourceRoot":"","sources":["../../../src/topology/XYParitySearchContext.ts"],"names":[],"mappings":"AAKA;;GAEG;AAEH;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qBAAqB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACjE,EAAE,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACjE,EAAE,EAAE,MAAM,CAAC;IAClB,qFAAqF;IAC9E,EAAE,EAAE,MAAM,CAAC;IAClB,qFAAqF;IAC9E,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACtB;;;;OAIG;gBACgB,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAO/C,0DAA0D;IACnD,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAU5E,4EAA4E;IAC5E,OAAO,CAAC,UAAU;IAOlB;;;;;;;OAOG;IACI,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAgE/C;;;;;OAKG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS;CAM5C"}
@@ -9,127 +9,129 @@ exports.XYParitySearchContext = void 0;
9
9
  * @module Topology
10
10
  */
11
11
  /**
12
- * * XYParitySearchContext is an internal class for callers that can feed points (without extracting to array structures)
12
+ * `XYParitySearchContext` is an internal class for callers that can feed points (without extracting to array structures)
13
13
  * * Most will be via static methods which handle a specific data source.
14
- * * PolygonOps.classifyPointInPolygon (x,y,points: XAndY[])
15
- * * HalfEdgeGraphSearch.pointInOrOnFaceXY (halfEdgeOnFace, x, y)
14
+ * * PolygonOps.classifyPointInPolygon(x,y,points: XAndY[])
15
+ * * HalfEdgeGraphSearch.pointInOrOnFaceXY(halfEdgeOnFace, x, y)
16
16
  * Use pattern:
17
- * * Caller must be able walk around polygon producing x,y coordinates (possibly transformed from actual polygon)
17
+ * * Caller must be able to walk around polygon producing x,y coordinates (possibly transformed from actual polygon).
18
18
  * * Caller announce edges to tryStartEdge until finding one acceptable to the search.
19
19
  * * Caller then passes additional points up to and including both x0,y0 and x1, y1 of the accepted start edge.
20
20
  * Call sequence is:
21
- * `context = new XYParitySearchContext`
22
- * `repeat { acquire edge (x0,y0) (x1,y1)} until context.tryStartEdge (x0,y0,x1,y1);`
23
- * `for each (x,y) beginning AFTER x1,y1 and ending with (x1,y1) context.advance (x,y)`
24
- * `return context.classifyCounts ();`
21
+ * * `context = new XYParitySearchContext`
22
+ * * `repeat { acquire edge (x0,y0) (x1,y1) } until context.tryStartEdge(x0,y0,x1,y1);`
23
+ * * `for each (x,y) beginning AFTER x1,y1 and ending with (x1,y1) context.advance (x,y)`
24
+ * * `return context.classifyCounts();`
25
25
  */
26
26
  class XYParitySearchContext {
27
27
  /**
28
28
  * Create a new searcher for specified test point.
29
- * @param xTest x coordinate of test point
30
- * @param yTest y coordinate of test point
29
+ * @param xTest x coordinate of test point.
30
+ * @param yTest y coordinate of test point.
31
31
  */
32
32
  constructor(xTest, yTest) {
33
33
  this.xTest = xTest;
34
34
  this.yTest = yTest;
35
- this.u0 = this.v0 = this.u1 = this.v1 = 0; // Not valid for search -- caller must satisfy tryStartEdge !!!
35
+ this.u0 = this.v0 = this.u1 = this.v1 = 0; // not valid for search; caller must satisfy tryStartEdge
36
36
  this.numLeftCrossing = this.numRightCrossing = 0;
37
37
  this.numHit = 0;
38
38
  }
39
- /**
40
- * test if x,y is a safe first coordinate to start the search.
41
- * * safe start must have non-zero y so that final point test (return to x0,y0) does not need look back for exact crossing logic.
42
- * @param x
43
- * @param y
44
- */
39
+ /** Test if parity processing can begin with this edge. */
45
40
  tryStartEdge(x0, y0, x1, y1) {
46
41
  if (y0 !== this.yTest) {
47
42
  this.u0 = x0 - this.xTest;
48
43
  this.v0 = y0 - this.yTest;
49
44
  this.u1 = x1 - this.xTest;
50
45
  this.v1 = y1 - this.yTest;
51
- return true;
46
+ return true; // we won't need wraparound logic to process the final edge ending at (x0,y0)
52
47
  }
53
48
  return false;
54
49
  }
55
- /** Return true if parity accumulation proceeded normally.
56
- * Return false if interrupted for exact hit.
50
+ /** Update local coordinates: the current edge becomes the previous edge. */
51
+ updateUV01(u2, v2) {
52
+ this.u0 = this.u1;
53
+ this.v0 = this.v1;
54
+ this.u1 = u2;
55
+ this.v1 = v2;
56
+ return true;
57
+ }
58
+ /**
59
+ * Process the current edge ending at (x2,y2).
60
+ * * Accumulate left/right parity of the test point wrt to the polygon. These counts track the number of polygon crossings
61
+ * of the left and right horizontal rays emanating from the test point. After all edges are processed, if either count is
62
+ * odd/even, the test point is inside/outside the polygon (see [[classifyCounts]]).
63
+ * * Check whether the test point lies on the edge.
64
+ * @returns whether caller should continue processing with the next edge. In particular, `false` if we have an exact hit.
57
65
  */
58
- advance(x, y) {
59
- const u = x - this.xTest;
60
- const v = y - this.yTest;
61
- const p = v * this.v1;
66
+ advance(x2, y2) {
67
+ // In this method we use local u,v coordinates obtained by translating the test point to the origin.
68
+ // This simplifies our computations:
69
+ // * left (right) parity is incremented if the current edge crosses the u-axis at u<0 (u>0)
70
+ // * we have an exact hit if the current edge crosses the u-axis at u=0
71
+ const u2 = x2 - this.xTest;
72
+ const v2 = y2 - this.yTest;
73
+ const p = v2 * this.v1;
62
74
  if (p > 0) {
63
- // The common case -- skittering along above or below the x axis . . .
64
- this.u0 = this.u1;
65
- this.v0 = this.v1;
66
- this.u1 = u;
67
- this.v1 = v;
68
- return true;
75
+ // Current edge does not cross u-axis.
76
+ return this.updateUV01(u2, v2);
69
77
  }
70
78
  if (p < 0) {
71
- // crossing within (u1,v1) to (u,v)
72
- // both v values are nonzero and of opposite sign, so this division is safe . . .
73
- const fraction = -this.v1 / (v - this.v1);
74
- const uCross = this.u1 + fraction * (u - this.u1);
79
+ // Current edge crosses the u-axis at edge parameter 0 < lambda < 1 by the Intermediate Value Theorem.
80
+ // Solve for lambda in 0 = v1 + lambda (v2 - v1), then use it to compute the u-value of the crossing.
81
+ const lambda = -this.v1 / (v2 - this.v1);
82
+ const uCross = this.u1 + lambda * (u2 - this.u1);
75
83
  if (uCross === 0.0) {
76
- this.numHit++;
84
+ this.numHit++; // Current edge crosses at the origin.
77
85
  return false;
78
86
  }
79
87
  if (uCross > 0)
80
88
  this.numRightCrossing++;
81
89
  else
82
90
  this.numLeftCrossing++;
83
- this.u0 = this.u1;
84
- this.v0 = this.v1;
85
- this.u1 = u;
86
- this.v1 = v;
87
- return true;
91
+ return this.updateUV01(u2, v2);
88
92
  }
89
- // hard stuff -- one or more exact hits . . .
90
- if (v === 0.0) {
93
+ // At this point, at least one endpoint of the current edge lies on the u-axis.
94
+ if (v2 === 0.0) {
91
95
  if (this.v1 === 0.0) {
92
- // uh oh -- moving along x axis. Does it pass through xTest:
93
- if (u * this.u1 <= 0.0) {
94
- this.numHit++;
96
+ if (u2 * this.u1 <= 0.0) {
97
+ this.numHit++; // Current edge lies on u-axis and contains the origin.
95
98
  return false;
96
99
  }
97
- // quietly moving along the scan line, both xy and x1y1 to same side of test point ...
98
- // u0 and u1 remain unchanged !!!
99
- this.u1 = u;
100
- this.v1 = v;
100
+ // Current edge lies on the u-axis to one side of the origin.
101
+ // This edge doesn't contribute to parity computations, so advance past it.
102
+ this.u1 = u2;
103
+ this.v1 = v2;
101
104
  return true;
102
105
  }
103
- // just moved onto the scan line ...
104
- this.u0 = this.u1;
105
- this.v0 = this.v1;
106
- this.u1 = u;
107
- this.v1 = v;
108
- return true;
106
+ if (u2 === 0.0) {
107
+ this.numHit++; // Current edge ends at the origin.
108
+ return false;
109
+ }
110
+ // Current edge ends on the u-axis away from the origin.
111
+ return this.updateUV01(u2, v2);
109
112
  }
110
- // fall out with v1 = 0
111
- // both v0 and v are nonzero.
112
- // any along-0 v values that have passed through are on the same side of xTest, so u1 determines crossing
113
- const q = this.v0 * v;
114
- if (this.u1 > 0) {
115
- if (q < 0)
116
- this.numRightCrossing++;
113
+ // At this point, the current edge starts at the u-axis.
114
+ if (this.u1 === 0.0) {
115
+ this.numHit++; // Current edge starts at the origin.
116
+ return false;
117
117
  }
118
- else {
119
- if (q < 0)
118
+ // At this point, the current edge starts on the u-axis away from the origin.
119
+ const q = this.v0 * v2;
120
+ if (q < 0) {
121
+ // The current edge and the previous edge lie on opposite sides of the u-axis, so we have a parity change.
122
+ if (this.u1 > 0)
123
+ this.numRightCrossing++;
124
+ else
120
125
  this.numLeftCrossing++;
121
126
  }
122
- this.u0 = this.u1;
123
- this.v0 = this.v1;
124
- this.u1 = u;
125
- this.v1 = v;
126
- return true;
127
+ // The current edge and the previous edge lie on the same sides of the u-axis, so no parity change.
128
+ return this.updateUV01(u2, v2);
127
129
  }
128
130
  /**
129
131
  * Return classification as ON, IN, or OUT according to hit and crossing counts.
130
- * * Any nonzero hit count is ON
132
+ * * Any nonzero hit count is ON.
131
133
  * * Otherwise IN if left crossing count is odd.
132
- * @return 0 if ON, 1 if IN, -1 if OUT
134
+ * @return 0 if ON, 1 if IN, -1 if OUT.
133
135
  */
134
136
  classifyCounts() {
135
137
  if (this.numHit > 0)
@@ -1 +1 @@
1
- {"version":3,"file":"XYParitySearchContext.js","sourceRoot":"","sources":["../../../src/topology/XYParitySearchContext.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n/** @packageDocumentation\r\n * @module Topology\r\n */\r\n\r\n/**\r\n * * XYParitySearchContext is an internal class for callers that can feed points (without extracting to array structures)\r\n * * Most will be via static methods which handle a specific data source.\r\n * * PolygonOps.classifyPointInPolygon (x,y,points: XAndY[])\r\n * * HalfEdgeGraphSearch.pointInOrOnFaceXY (halfEdgeOnFace, x, y)\r\n * Use pattern:\r\n * * Caller must be able walk around polygon producing x,y coordinates (possibly transformed from actual polygon)\r\n * * Caller announce edges to tryStartEdge until finding one acceptable to the search.\r\n * * Caller then passes additional points up to and including both x0,y0 and x1, y1 of the accepted start edge.\r\n * Call sequence is:\r\n * `context = new XYParitySearchContext`\r\n * `repeat { acquire edge (x0,y0) (x1,y1)} until context.tryStartEdge (x0,y0,x1,y1);`\r\n * `for each (x,y) beginning AFTER x1,y1 and ending with (x1,y1) context.advance (x,y)`\r\n * `return context.classifyCounts ();`\r\n */\r\nexport class XYParitySearchContext {\r\n public xTest: number;\r\n public yTest: number;\r\n public u0: number; // local coordinates of recent point with nonzero v. Usually \"second last point\" but points can be skipped if y1 is zero\r\n public v0: number;\r\n public u1: number; // local coordinates of most recent point\r\n public v1: number;\r\n public numLeftCrossing: number;\r\n public numRightCrossing: number;\r\n public numHit: number;\r\n /**\r\n * Create a new searcher for specified test point.\r\n * @param xTest x coordinate of test point\r\n * @param yTest y coordinate of test point\r\n */\r\n public constructor(xTest: number, yTest: number) {\r\n this.xTest = xTest;\r\n this.yTest = yTest;\r\n this.u0 = this.v0 = this.u1 = this.v1 = 0; // Not valid for search -- caller must satisfy tryStartEdge !!!\r\n this.numLeftCrossing = this.numRightCrossing = 0;\r\n this.numHit = 0;\r\n }\r\n /**\r\n * test if x,y is a safe first coordinate to start the search.\r\n * * safe start must have non-zero y so that final point test (return to x0,y0) does not need look back for exact crossing logic.\r\n * @param x\r\n * @param y\r\n */\r\n public tryStartEdge(x0: number, y0: number, x1: number, y1: number): boolean {\r\n if (y0 !== this.yTest) {\r\n this.u0 = x0 - this.xTest;\r\n this.v0 = y0 - this.yTest;\r\n this.u1 = x1 - this.xTest;\r\n this.v1 = y1 - this.yTest;\r\n return true;\r\n }\r\n return false;\r\n }\r\n /** Return true if parity accumulation proceeded normally.\r\n * Return false if interrupted for exact hit.\r\n */\r\n public advance(x: number, y: number): boolean {\r\n const u = x - this.xTest;\r\n const v = y - this.yTest;\r\n const p = v * this.v1;\r\n if (p > 0) {\r\n // The common case -- skittering along above or below the x axis . . .\r\n this.u0 = this.u1;\r\n this.v0 = this.v1;\r\n this.u1 = u;\r\n this.v1 = v;\r\n return true;\r\n }\r\n if (p < 0) {\r\n // crossing within (u1,v1) to (u,v)\r\n // both v values are nonzero and of opposite sign, so this division is safe . . .\r\n const fraction = -this.v1 / (v - this.v1);\r\n const uCross = this.u1 + fraction * (u - this.u1);\r\n if (uCross === 0.0) {\r\n this.numHit++;\r\n return false;\r\n }\r\n if (uCross > 0)\r\n this.numRightCrossing++;\r\n else\r\n this.numLeftCrossing++;\r\n this.u0 = this.u1;\r\n this.v0 = this.v1;\r\n this.u1 = u;\r\n this.v1 = v;\r\n return true;\r\n }\r\n // hard stuff -- one or more exact hits . . .\r\n if (v === 0.0) {\r\n if (this.v1 === 0.0) {\r\n // uh oh -- moving along x axis. Does it pass through xTest:\r\n if (u * this.u1 <= 0.0) {\r\n this.numHit++;\r\n return false;\r\n }\r\n // quietly moving along the scan line, both xy and x1y1 to same side of test point ...\r\n // u0 and u1 remain unchanged !!!\r\n this.u1 = u;\r\n this.v1 = v;\r\n return true;\r\n }\r\n // just moved onto the scan line ...\r\n this.u0 = this.u1;\r\n this.v0 = this.v1;\r\n this.u1 = u;\r\n this.v1 = v;\r\n return true;\r\n }\r\n // fall out with v1 = 0\r\n // both v0 and v are nonzero.\r\n // any along-0 v values that have passed through are on the same side of xTest, so u1 determines crossing\r\n const q = this.v0 * v;\r\n if (this.u1 > 0) {\r\n if (q < 0)\r\n this.numRightCrossing++;\r\n } else {\r\n if (q < 0)\r\n this.numLeftCrossing++;\r\n }\r\n this.u0 = this.u1;\r\n this.v0 = this.v1;\r\n this.u1 = u;\r\n this.v1 = v;\r\n return true;\r\n }\r\n /**\r\n * Return classification as ON, IN, or OUT according to hit and crossing counts.\r\n * * Any nonzero hit count is ON\r\n * * Otherwise IN if left crossing count is odd.\r\n * @return 0 if ON, 1 if IN, -1 if OUT\r\n */\r\n public classifyCounts(): number | undefined {\r\n if (this.numHit > 0)\r\n return 0;\r\n const parity = this.numLeftCrossing & 0x01;\r\n return (parity === 1) ? 1 : -1;\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"XYParitySearchContext.js","sourceRoot":"","sources":["../../../src/topology/XYParitySearchContext.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n/** @packageDocumentation\r\n * @module Topology\r\n */\r\n\r\n/**\r\n * `XYParitySearchContext` is an internal class for callers that can feed points (without extracting to array structures)\r\n * * Most will be via static methods which handle a specific data source.\r\n * * PolygonOps.classifyPointInPolygon(x,y,points: XAndY[])\r\n * * HalfEdgeGraphSearch.pointInOrOnFaceXY(halfEdgeOnFace, x, y)\r\n * Use pattern:\r\n * * Caller must be able to walk around polygon producing x,y coordinates (possibly transformed from actual polygon).\r\n * * Caller announce edges to tryStartEdge until finding one acceptable to the search.\r\n * * Caller then passes additional points up to and including both x0,y0 and x1, y1 of the accepted start edge.\r\n * Call sequence is:\r\n * * `context = new XYParitySearchContext`\r\n * * `repeat { acquire edge (x0,y0) (x1,y1) } until context.tryStartEdge(x0,y0,x1,y1);`\r\n * * `for each (x,y) beginning AFTER x1,y1 and ending with (x1,y1) context.advance (x,y)`\r\n * * `return context.classifyCounts();`\r\n */\r\nexport class XYParitySearchContext {\r\n public xTest: number;\r\n public yTest: number;\r\n /** local x-coordinate of the start of the previous (or earlier) edge */\r\n public u0: number;\r\n /** local y-coordinate of the start of the previous (or earlier) edge */\r\n public v0: number;\r\n /** local x-coordinate of the end of the previous edge (and start of current edge) */\r\n public u1: number;\r\n /** local y-coordinate of the end of the previous edge (and start of current edge) */\r\n public v1: number;\r\n public numLeftCrossing: number;\r\n public numRightCrossing: number;\r\n public numHit: number;\r\n /**\r\n * Create a new searcher for specified test point.\r\n * @param xTest x coordinate of test point.\r\n * @param yTest y coordinate of test point.\r\n */\r\n public constructor(xTest: number, yTest: number) {\r\n this.xTest = xTest;\r\n this.yTest = yTest;\r\n this.u0 = this.v0 = this.u1 = this.v1 = 0; // not valid for search; caller must satisfy tryStartEdge\r\n this.numLeftCrossing = this.numRightCrossing = 0;\r\n this.numHit = 0;\r\n }\r\n /** Test if parity processing can begin with this edge. */\r\n public tryStartEdge(x0: number, y0: number, x1: number, y1: number): boolean {\r\n if (y0 !== this.yTest) {\r\n this.u0 = x0 - this.xTest;\r\n this.v0 = y0 - this.yTest;\r\n this.u1 = x1 - this.xTest;\r\n this.v1 = y1 - this.yTest;\r\n return true; // we won't need wraparound logic to process the final edge ending at (x0,y0)\r\n }\r\n return false;\r\n }\r\n /** Update local coordinates: the current edge becomes the previous edge. */\r\n private updateUV01(u2: number, v2: number) {\r\n this.u0 = this.u1;\r\n this.v0 = this.v1;\r\n this.u1 = u2;\r\n this.v1 = v2;\r\n return true;\r\n }\r\n /**\r\n * Process the current edge ending at (x2,y2).\r\n * * Accumulate left/right parity of the test point wrt to the polygon. These counts track the number of polygon crossings\r\n * of the left and right horizontal rays emanating from the test point. After all edges are processed, if either count is\r\n * odd/even, the test point is inside/outside the polygon (see [[classifyCounts]]).\r\n * * Check whether the test point lies on the edge.\r\n * @returns whether caller should continue processing with the next edge. In particular, `false` if we have an exact hit.\r\n */\r\n public advance(x2: number, y2: number): boolean {\r\n // In this method we use local u,v coordinates obtained by translating the test point to the origin.\r\n // This simplifies our computations:\r\n // * left (right) parity is incremented if the current edge crosses the u-axis at u<0 (u>0)\r\n // * we have an exact hit if the current edge crosses the u-axis at u=0\r\n const u2 = x2 - this.xTest;\r\n const v2 = y2 - this.yTest;\r\n const p = v2 * this.v1;\r\n if (p > 0) {\r\n // Current edge does not cross u-axis.\r\n return this.updateUV01(u2, v2);\r\n }\r\n if (p < 0) {\r\n // Current edge crosses the u-axis at edge parameter 0 < lambda < 1 by the Intermediate Value Theorem.\r\n // Solve for lambda in 0 = v1 + lambda (v2 - v1), then use it to compute the u-value of the crossing.\r\n const lambda = -this.v1 / (v2 - this.v1);\r\n const uCross = this.u1 + lambda * (u2 - this.u1);\r\n if (uCross === 0.0) {\r\n this.numHit++; // Current edge crosses at the origin.\r\n return false;\r\n }\r\n if (uCross > 0)\r\n this.numRightCrossing++;\r\n else\r\n this.numLeftCrossing++;\r\n return this.updateUV01(u2, v2);\r\n }\r\n // At this point, at least one endpoint of the current edge lies on the u-axis.\r\n if (v2 === 0.0) {\r\n if (this.v1 === 0.0) {\r\n if (u2 * this.u1 <= 0.0) {\r\n this.numHit++; // Current edge lies on u-axis and contains the origin.\r\n return false;\r\n }\r\n // Current edge lies on the u-axis to one side of the origin.\r\n // This edge doesn't contribute to parity computations, so advance past it.\r\n this.u1 = u2;\r\n this.v1 = v2;\r\n return true;\r\n }\r\n if (u2 === 0.0) {\r\n this.numHit++; // Current edge ends at the origin.\r\n return false;\r\n }\r\n // Current edge ends on the u-axis away from the origin.\r\n return this.updateUV01(u2, v2);\r\n }\r\n // At this point, the current edge starts at the u-axis.\r\n if (this.u1 === 0.0) {\r\n this.numHit++; // Current edge starts at the origin.\r\n return false;\r\n }\r\n // At this point, the current edge starts on the u-axis away from the origin.\r\n const q = this.v0 * v2;\r\n if (q < 0) {\r\n // The current edge and the previous edge lie on opposite sides of the u-axis, so we have a parity change.\r\n if (this.u1 > 0)\r\n this.numRightCrossing++;\r\n else\r\n this.numLeftCrossing++;\r\n }\r\n // The current edge and the previous edge lie on the same sides of the u-axis, so no parity change.\r\n return this.updateUV01(u2, v2);\r\n }\r\n /**\r\n * Return classification as ON, IN, or OUT according to hit and crossing counts.\r\n * * Any nonzero hit count is ON.\r\n * * Otherwise IN if left crossing count is odd.\r\n * @return 0 if ON, 1 if IN, -1 if OUT.\r\n */\r\n public classifyCounts(): number | undefined {\r\n if (this.numHit > 0)\r\n return 0;\r\n const parity = this.numLeftCrossing & 0x01;\r\n return (parity === 1) ? 1 : -1;\r\n }\r\n}\r\n"]}
@@ -123,11 +123,13 @@ export declare class YawPitchRollAngles {
123
123
  angles: YawPitchRollAngles | undefined;
124
124
  };
125
125
  /**
126
- * Attempts to create a YawPitchRollAngles object from a Matrix3d
127
- * * This conversion fails if the matrix is not rigid (unit rows and columns, and transpose is inverse)
128
- * * In the failure case the method's return value is `undefined`.
129
- * * In the failure case, if the optional result was supplied, that result will nonetheless be filled with
130
- * a set of angles.
126
+ * Attempts to create a YawPitchRollAngles object from a Matrix3d.
127
+ * @param matrix rigid matrix to process. Caller can test for rigidity with [[Matrix3d.isRigid]] and
128
+ * ensure rigid input with [[Matrix3d.createRigidFromMatrix3d]] or [[Matrix3d.makeRigid]].
129
+ * @param result optional pre-allocated object to populate and return.
130
+ * @returns computed angles, or undefined if `matrix` is not rigid.
131
+ * * Even when undefined is returned, `result` (if supplied) is populated with angles that may be used with caution:
132
+ * their usefulness decreases the further `matrix` is from being rigid.
131
133
  */
132
134
  static createFromMatrix3d(matrix: Matrix3d, result?: YawPitchRollAngles): YawPitchRollAngles | undefined;
133
135
  }
@@ -1 +1 @@
1
- {"version":3,"file":"YawPitchRollAngles.d.ts","sourceRoot":"","sources":["../../../src/geometry3d/YawPitchRollAngles.ts"],"names":[],"mappings":"AAKA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAY,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB;IAChB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,iBAAiB;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAkB;IAC7B,+DAA+D;IACxD,GAAG,EAAE,KAAK,CAAC;IAClB,6DAA6D;IACtD,KAAK,EAAE,KAAK,CAAC;IACpB,+DAA+D;IACxD,IAAI,EAAE,KAAK,CAAC;IACnB;;;;;OAKG;gBACS,GAAG,GAAE,KAAoB,EAAE,KAAK,GAAE,KAAoB,EAAE,IAAI,GAAE,KAAoB;IAK9F,qCAAqC;IAC9B,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC;IAM/B;;;;;OAKG;WACW,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB;IAO9G;;;;;OAKG;WACW,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB;IAO9G,2EAA2E;WAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,kBAAkB;IAQpE,iEAAiE;IAC1D,WAAW,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAMlD;;;OAGG;IACI,MAAM,IAAI,iBAAiB;IAUlC;;;OAGG;IACI,OAAO,CAAC,KAAK,EAAE,kBAAkB;IAKxC;;;;OAIG;IACI,aAAa,CAAC,KAAK,EAAE,kBAAkB;IAK9C,6CAA6C;IACtC,KAAK;IAOZ;;;;;OAKG;IACI,UAAU,CAAC,MAAM,CAAC,EAAE,QAAQ;IAqCnC;;;;OAIG;IACI,UAAU,CAAC,gBAAgB,GAAE,OAAc,GAAG,OAAO;IAU5D,0CAA0C;IACnC,aAAa,IAAI,MAAM;IAG9B,sDAAsD;IAC/C,iBAAiB,IAAI,MAAM;IAGlC,kFAAkF;IAC3E,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM;IAOxD,2CAA2C;IACpC,aAAa,IAAI,MAAM;IAG9B,mDAAmD;IAC5C,iBAAiB,IAAI,MAAM;IAGlC,kFAAkF;IAC3E,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM;IAOxD,6EAA6E;WAC/D,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG;QACpD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,kBAAkB,GAAG,SAAS,CAAC;KACxC;IAMD;;;;;;OAMG;WACW,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,SAAS;CA0EhH"}
1
+ {"version":3,"file":"YawPitchRollAngles.d.ts","sourceRoot":"","sources":["../../../src/geometry3d/YawPitchRollAngles.ts"],"names":[],"mappings":"AAKA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAY,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB;IAChB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,iBAAiB;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAkB;IAC7B,+DAA+D;IACxD,GAAG,EAAE,KAAK,CAAC;IAClB,6DAA6D;IACtD,KAAK,EAAE,KAAK,CAAC;IACpB,+DAA+D;IACxD,IAAI,EAAE,KAAK,CAAC;IACnB;;;;;OAKG;gBACS,GAAG,GAAE,KAAoB,EAAE,KAAK,GAAE,KAAoB,EAAE,IAAI,GAAE,KAAoB;IAK9F,qCAAqC;IAC9B,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC;IAM/B;;;;;OAKG;WACW,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB;IAO9G;;;;;OAKG;WACW,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB;IAO9G,2EAA2E;WAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,kBAAkB;IAQpE,iEAAiE;IAC1D,WAAW,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,IAAI;IAMlD;;;OAGG;IACI,MAAM,IAAI,iBAAiB;IAUlC;;;OAGG;IACI,OAAO,CAAC,KAAK,EAAE,kBAAkB;IAKxC;;;;OAIG;IACI,aAAa,CAAC,KAAK,EAAE,kBAAkB;IAK9C,6CAA6C;IACtC,KAAK;IAOZ;;;;;OAKG;IACI,UAAU,CAAC,MAAM,CAAC,EAAE,QAAQ;IAqCnC;;;;OAIG;IACI,UAAU,CAAC,gBAAgB,GAAE,OAAc,GAAG,OAAO;IAU5D,0CAA0C;IACnC,aAAa,IAAI,MAAM;IAG9B,sDAAsD;IAC/C,iBAAiB,IAAI,MAAM;IAGlC,kFAAkF;IAC3E,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM;IAOxD,2CAA2C;IACpC,aAAa,IAAI,MAAM;IAG9B,mDAAmD;IAC5C,iBAAiB,IAAI,MAAM;IAGlC,kFAAkF;IAC3E,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM;IAOxD,6EAA6E;WAC/D,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG;QACpD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,kBAAkB,GAAG,SAAS,CAAC;KACxC;IAMD;;;;;;;;OAQG;WACW,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,SAAS;CA0EhH"}
@@ -202,11 +202,13 @@ export class YawPitchRollAngles {
202
202
  };
203
203
  }
204
204
  /**
205
- * Attempts to create a YawPitchRollAngles object from a Matrix3d
206
- * * This conversion fails if the matrix is not rigid (unit rows and columns, and transpose is inverse)
207
- * * In the failure case the method's return value is `undefined`.
208
- * * In the failure case, if the optional result was supplied, that result will nonetheless be filled with
209
- * a set of angles.
205
+ * Attempts to create a YawPitchRollAngles object from a Matrix3d.
206
+ * @param matrix rigid matrix to process. Caller can test for rigidity with [[Matrix3d.isRigid]] and
207
+ * ensure rigid input with [[Matrix3d.createRigidFromMatrix3d]] or [[Matrix3d.makeRigid]].
208
+ * @param result optional pre-allocated object to populate and return.
209
+ * @returns computed angles, or undefined if `matrix` is not rigid.
210
+ * * Even when undefined is returned, `result` (if supplied) is populated with angles that may be used with caution:
211
+ * their usefulness decreases the further `matrix` is from being rigid.
210
212
  */
211
213
  static createFromMatrix3d(matrix, result) {
212
214
  /**
@@ -284,7 +286,7 @@ export class YawPitchRollAngles {
284
286
  }
285
287
  // sanity check
286
288
  const matrix1 = angles.toMatrix3d();
287
- return matrix.maxDiff(matrix1) < Geometry.smallAngleRadians ? angles : undefined;
289
+ return matrix.isAlmostEqual(matrix1) ? angles : undefined;
288
290
  }
289
291
  }
290
292
  //# sourceMappingURL=YawPitchRollAngles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"YawPitchRollAngles.js","sourceRoot":"","sources":["../../../src/geometry3d/YawPitchRollAngles.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n/** @packageDocumentation\r\n * @module CartesianGeometry\r\n */\r\n\r\nimport { AngleProps, Geometry } from \"../Geometry\";\r\nimport { Angle } from \"./Angle\";\r\nimport { Matrix3d } from \"./Matrix3d\";\r\nimport { Point3d } from \"./Point3dVector3d\";\r\nimport { Transform } from \"./Transform\";\r\n\r\n// cspell:word Tait\r\n\r\n/**\r\n * Angle properties of a `YawPitchRoll` orientation\r\n * @public\r\n */\r\nexport interface YawPitchRollProps {\r\n /** yaw field */\r\n yaw?: AngleProps;\r\n /** pitch field */\r\n pitch?: AngleProps;\r\n /** roll field */\r\n roll?: AngleProps;\r\n}\r\n\r\n/**\r\n * Three angles that determine the orientation of an object in space, sometimes referred to as [Tait–Bryan angles]\r\n * (https://en.wikipedia.org/wiki/Euler_angles).\r\n * * The matrix construction can be replicated by this logic:\r\n * * xyz coordinates have\r\n * * x forward\r\n * * y to left\r\n * * z up\r\n * * Note that this is a right handed coordinate system.\r\n * * yaw is a rotation of x towards y, i.e. around positive z (counterclockwise):\r\n * * `yawMatrix = Matrix3d.createRotationAroundAxisIndex(2, Angle.createDegrees(yawDegrees));`\r\n * * pitch is a rotation that raises x towards z, i.e. rotation around **negative y** (**clockwise**):\r\n * * `pitchMatrix = Matrix3d.createRotationAroundAxisIndex(1, Angle.createDegrees(-pitchDegrees));`\r\n * * roll is rotation of y towards z, i.e. rotation around positive x (counterclockwise):\r\n * * `rollMatrix = Matrix3d.createRotationAroundAxisIndex(0, Angle.createDegrees(rollDegrees));`\r\n * * The YPR matrix is the product\r\n * * `result = yawMatrix.multiplyMatrixMatrix(pitchMatrix.multiplyMatrixMatrix(rollMatrix));`\r\n * * Note that this is for \"column based\" matrix with vectors multiplying on the right of the matrix.\r\n * Hence a vector is first rotated by roll, then the pitch, finally yaw. So multiplication order in\r\n * the sense of AxisOrder is `RPY` (i.e., XYZ), in contrast to the familiar name `YPR`.\r\n * @public\r\n */\r\nexport class YawPitchRollAngles {\r\n /** The yaw angle: counterclockwise rotation angle around z */\r\n public yaw: Angle;\r\n /** The pitch angle: **clockwise** rotation angle around y */\r\n public pitch: Angle;\r\n /** The roll angle: counterclockwise rotation angle around x */\r\n public roll: Angle;\r\n /**\r\n * Constructor\r\n * @param yaw counterclockwise rotation angle around z\r\n * @param pitch **clockwise** rotation angle around y\r\n * @param roll counterclockwise rotation angle around x\r\n */\r\n constructor(yaw: Angle = Angle.zero(), pitch: Angle = Angle.zero(), roll: Angle = Angle.zero()) {\r\n this.yaw = yaw;\r\n this.pitch = pitch;\r\n this.roll = roll;\r\n }\r\n /** Freeze this YawPitchRollAngles */\r\n public freeze(): Readonly<this> {\r\n this.yaw.freeze();\r\n this.pitch.freeze();\r\n this.roll.freeze();\r\n return Object.freeze(this);\r\n }\r\n /**\r\n * Constructor for YawPitchRollAngles with angles in degrees.\r\n * @param yawDegrees counterclockwise rotation angle (in degrees) around z\r\n * @param pitchDegrees **clockwise** rotation angle (in degrees) around y\r\n * @param rollDegrees counterclockwise rotation angle (in degrees) around x\r\n */\r\n public static createDegrees(yawDegrees: number, pitchDegrees: number, rollDegrees: number): YawPitchRollAngles {\r\n return new YawPitchRollAngles(\r\n Angle.createDegrees(yawDegrees),\r\n Angle.createDegrees(pitchDegrees),\r\n Angle.createDegrees(rollDegrees),\r\n );\r\n }\r\n /**\r\n * Constructor for YawPitchRollAngles with angles in radians.\r\n * @param yawRadians counterclockwise rotation angle (in radians) around z\r\n * @param pitchRadians **clockwise** rotation angle (in radians) around y\r\n * @param rollRadians counterclockwise rotation angle (in radians) around x\r\n */\r\n public static createRadians(yawRadians: number, pitchRadians: number, rollRadians: number): YawPitchRollAngles {\r\n return new YawPitchRollAngles(\r\n Angle.createRadians(yawRadians),\r\n Angle.createRadians(pitchRadians),\r\n Angle.createRadians(rollRadians),\r\n );\r\n }\r\n /** Construct a `YawPitchRoll` object from an object with 3 named angles */\r\n public static fromJSON(json?: YawPitchRollProps): YawPitchRollAngles {\r\n json = json ? json : {};\r\n return new YawPitchRollAngles(\r\n Angle.fromJSON(json.yaw),\r\n Angle.fromJSON(json.pitch),\r\n Angle.fromJSON(json.roll),\r\n );\r\n }\r\n /** Populate yaw, pitch and roll fields using `Angle.fromJSON` */\r\n public setFromJSON(json?: YawPitchRollProps): void {\r\n json = json ? json : {};\r\n this.yaw = Angle.fromJSON(json.yaw);\r\n this.pitch = Angle.fromJSON(json.pitch);\r\n this.roll = Angle.fromJSON(json.roll);\r\n }\r\n /**\r\n * Convert to a JSON object of form { pitch: 20 , roll: 30 , yaw: 10 }. Angles are in degrees.\r\n * Any values that are exactly zero (with tolerance `Geometry.smallAngleRadians`) are omitted.\r\n */\r\n public toJSON(): YawPitchRollProps {\r\n const val: YawPitchRollProps = {};\r\n if (!this.pitch.isAlmostZero)\r\n val.pitch = this.pitch.toJSON();\r\n if (!this.roll.isAlmostZero)\r\n val.roll = this.roll.toJSON();\r\n if (!this.yaw.isAlmostZero)\r\n val.yaw = this.yaw.toJSON();\r\n return val;\r\n }\r\n /**\r\n * Install all rotations from `other` into `this`.\r\n * @param other YawPitchRollAngles source\r\n */\r\n public setFrom(other: YawPitchRollAngles) {\r\n this.yaw.setFrom(other.yaw);\r\n this.pitch.setFrom(other.pitch);\r\n this.roll.setFrom(other.roll);\r\n }\r\n /**\r\n * Compare angles between `this` and `other`.\r\n * * Comparisons are via `isAlmostEqualAllowPeriodShift`.\r\n * @param other YawPitchRollAngles source\r\n */\r\n public isAlmostEqual(other: YawPitchRollAngles) {\r\n return this.yaw.isAlmostEqualAllowPeriodShift(other.yaw)\r\n && this.pitch.isAlmostEqualAllowPeriodShift(other.pitch)\r\n && this.roll.isAlmostEqualAllowPeriodShift(other.roll);\r\n }\r\n /** Make a copy of this YawPitchRollAngles */\r\n public clone() {\r\n return new YawPitchRollAngles(\r\n this.yaw.clone(),\r\n this.pitch.clone(),\r\n this.roll.clone(),\r\n );\r\n }\r\n /**\r\n * Expand the angles into a (rigid rotation) matrix.\r\n * * The returned matrix is \"rigid\" (i.e., it has unit length rows and columns, and its transpose is its inverse).\r\n * * The rigid matrix is always a right handed coordinate system.\r\n * @param result optional pre-allocated `Matrix3d`\r\n */\r\n public toMatrix3d(result?: Matrix3d) {\r\n const cz = Math.cos(this.yaw.radians);\r\n const sz = Math.sin(this.yaw.radians);\r\n const cy = Math.cos(this.pitch.radians);\r\n const sy = Math.sin(this.pitch.radians);\r\n const cx = Math.cos(this.roll.radians);\r\n const sx = Math.sin(this.roll.radians);\r\n /**\r\n * The axis order is XYZ (i.e., RPY) so the rotation matrix is calculated via rZ*rY*rX where\r\n * rX, rY, and rZ are base rotation matrixes:\r\n *\r\n * const rX = Matrix3d.createRowValues(\r\n * 1, 0, 0,\r\n * 0, Math.cos(x), -Math.sin(x),\r\n * 0, Math.sin(x), Math.cos(x),\r\n * );\r\n * const rY = Matrix3d.createRowValues(\r\n * Math.cos(y), 0, Math.sin(y),\r\n * 0, 1, 0,\r\n * -Math.sin(y), 0, Math.cos(y),\r\n * );\r\n * const rZ = Matrix3d.createRowValues(\r\n * Math.cos(z), -Math.sin(z), 0,\r\n * Math.sin(z), Math.cos(z), 0,\r\n * 0, 0, 1,\r\n * );\r\n *\r\n * Then we replace sin(y) with -sin(y) because y rotation (i.e., pitch) is clockwise (alternatively, you\r\n * can use transpose of rY in the matrix multiplication to get the same result)\r\n */\r\n return Matrix3d.createRowValues(\r\n cz * cy, -(sz * cx + cz * sy * sx), (sz * sx - cz * sy * cx),\r\n sz * cy, (cz * cx - sz * sy * sx), -(cz * sx + sz * sy * cx),\r\n sy, cy * sx, cy * cx,\r\n result,\r\n );\r\n }\r\n /**\r\n * Returns true if this rotation does nothing.\r\n * * If allowPeriodShift is false, any nonzero angle is considered a non-identity\r\n * * If allowPeriodShift is true, all angles are individually allowed to be any multiple of 360 degrees.\r\n */\r\n public isIdentity(allowPeriodShift: boolean = true): boolean {\r\n if (allowPeriodShift)\r\n return Angle.isAlmostEqualRadiansAllowPeriodShift(0.0, this.yaw.radians)\r\n && Angle.isAlmostEqualRadiansAllowPeriodShift(0.0, this.pitch.radians)\r\n && Angle.isAlmostEqualRadiansAllowPeriodShift(0.0, this.roll.radians);\r\n else\r\n return Angle.isAlmostEqualRadiansNoPeriodShift(0.0, this.yaw.radians)\r\n && Angle.isAlmostEqualRadiansNoPeriodShift(0.0, this.pitch.radians)\r\n && Angle.isAlmostEqualRadiansNoPeriodShift(0.0, this.roll.radians);\r\n }\r\n /** Return the largest angle in radians */\r\n public maxAbsRadians(): number {\r\n return Geometry.maxAbsXYZ(this.yaw.radians, this.pitch.radians, this.roll.radians);\r\n }\r\n /** Return the sum of the angles in squared radians */\r\n public sumSquaredRadians(): number {\r\n return Geometry.hypotenuseSquaredXYZ(this.yaw.radians, this.pitch.radians, this.roll.radians);\r\n }\r\n /** Return the largest difference of angles (in radians) between this and other */\r\n public maxDiffRadians(other: YawPitchRollAngles): number {\r\n return Math.max(\r\n this.yaw.radians - other.yaw.radians,\r\n this.pitch.radians - other.pitch.radians,\r\n this.roll.radians - other.roll.radians,\r\n );\r\n }\r\n /** Return the largest angle in degrees. */\r\n public maxAbsDegrees(): number {\r\n return Geometry.maxAbsXYZ(this.yaw.degrees, this.pitch.degrees, this.roll.degrees);\r\n }\r\n /** Return the sum of squared angles in degrees. */\r\n public sumSquaredDegrees(): number {\r\n return Geometry.hypotenuseSquaredXYZ(this.yaw.degrees, this.pitch.degrees, this.roll.degrees);\r\n }\r\n /** Return the largest difference of angles (in degrees) between this and other */\r\n public maxDiffDegrees(other: YawPitchRollAngles): number {\r\n return Math.max(\r\n this.yaw.degrees - other.yaw.degrees,\r\n this.pitch.degrees - other.pitch.degrees,\r\n this.roll.degrees - other.roll.degrees,\r\n );\r\n }\r\n /** Return an object from a Transform as an origin and YawPitchRollAngles. */\r\n public static tryFromTransform(transform: Transform): {\r\n origin: Point3d;\r\n angles: YawPitchRollAngles | undefined;\r\n } {\r\n return {\r\n origin: Point3d.createFrom(transform.origin),\r\n angles: YawPitchRollAngles.createFromMatrix3d(transform.matrix),\r\n };\r\n }\r\n /**\r\n * Attempts to create a YawPitchRollAngles object from a Matrix3d\r\n * * This conversion fails if the matrix is not rigid (unit rows and columns, and transpose is inverse)\r\n * * In the failure case the method's return value is `undefined`.\r\n * * In the failure case, if the optional result was supplied, that result will nonetheless be filled with\r\n * a set of angles.\r\n */\r\n public static createFromMatrix3d(matrix: Matrix3d, result?: YawPitchRollAngles): YawPitchRollAngles | undefined {\r\n /**\r\n * The rotation matrix form is\r\n *\r\n * Matrix3d.createRowValues(\r\n * cz * cy, -(sz * cx + cz * sy * sx), (sz * sx - cz * sy * cx),\r\n * sz * cy, (cz * cx - sz * sy * sx), -(cz * sx + sz * sy * cx),\r\n * sy, cy * sx, cy * cx\r\n * );\r\n *\r\n * where cx = cos(x), sx = sin(x), cy = cos(y), sy = sin(y), cz = cos(z), and sz = sin(z)\r\n */\r\n const sy = matrix.at(2, 0); // sin(y)\r\n const cy = Math.sqrt(matrix.at(2, 1) * matrix.at(2, 1) + matrix.at(2, 2) * matrix.at(2, 2)); // |cos(y)|\r\n const pitchA = Angle.createAtan2(sy, cy); // with positive cosine\r\n const pitchB = Angle.createAtan2(sy, -cy); // with negative cosine\r\n const angles = result ? result : new YawPitchRollAngles();\r\n /**\r\n * If cos(y) = 0 then y = +-90 degrees so we have a gimbal lock.\r\n * This means x and z can be anything as long as their sum x + z is constant.\r\n * so we can pick z = 0 and calculate x (or pick x = 0 and calculate z).\r\n * Math details can be found\r\n * https://en.wikipedia.org/wiki/Gimbal_lock#Loss_of_a_degree_of_freedom_with_Euler_angles\r\n *\r\n * The rotation matrix for y = +-90 degrees and x = 0 becomes\r\n *\r\n * Matrix3d.createRowValues(\r\n * 0, -sz, -+cz,\r\n * 0, cz, -+sz,\r\n * +-1, 0, 0\r\n * );\r\n *\r\n * so z = atan(sz/cz) = atan(-matrix.at(0, 1), matrix.at(1, 1))\r\n */\r\n if (cy < Geometry.smallAngleRadians) {\r\n angles.yaw = Angle.createAtan2(-matrix.at(0, 1), matrix.at(1, 1));\r\n angles.pitch = pitchA; // this is an arbitrary choice. can pick pitchB instead.\r\n angles.roll = Angle.createRadians(0.0);\r\n } else {\r\n /**\r\n * positive cosine\r\n * z = atan(sz/cz) = atan(matrix.at(1, 0), matrix.at(0, 0))\r\n * x = atan(sx/cx) = atan(matrix.at(2, 1), matrix.at(2, 2))\r\n */\r\n const yawA = Angle.createAtan2(matrix.at(1, 0), matrix.at(0, 0));\r\n const rollA = Angle.createAtan2(matrix.at(2, 1), matrix.at(2, 2));\r\n // similar with negative cosine\r\n const yawB = Angle.createAtan2(-matrix.at(1, 0), -matrix.at(0, 0));\r\n const rollB = Angle.createAtan2(-matrix.at(2, 1), -matrix.at(2, 2));\r\n // create YPR\r\n const yprA = new YawPitchRollAngles(yawA, pitchA, rollA);\r\n const yprB = new YawPitchRollAngles(yawB, pitchB, rollB);\r\n // decide to pick yprA or yprB with smallest magnitude angles\r\n const absFactor = 0.95;\r\n const maxRadiansA = yprA.maxAbsRadians();\r\n const maxRadiansB = yprB.maxAbsRadians();\r\n if (maxRadiansA < absFactor * maxRadiansB) {\r\n angles.setFrom(yprA);\r\n } else if (maxRadiansB < absFactor * maxRadiansA) {\r\n angles.setFrom(yprB);\r\n } else {\r\n const sumA = yprA.sumSquaredRadians();\r\n const sumB = yprB.sumSquaredRadians();\r\n if (sumA <= sumB) {\r\n angles.setFrom(yprA);\r\n } else {\r\n angles.setFrom(yprB);\r\n }\r\n }\r\n }\r\n // sanity check\r\n const matrix1 = angles.toMatrix3d();\r\n return matrix.maxDiff(matrix1) < Geometry.smallAngleRadians ? angles : undefined;\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"YawPitchRollAngles.js","sourceRoot":"","sources":["../../../src/geometry3d/YawPitchRollAngles.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\n/** @packageDocumentation\r\n * @module CartesianGeometry\r\n */\r\n\r\nimport { AngleProps, Geometry } from \"../Geometry\";\r\nimport { Angle } from \"./Angle\";\r\nimport { Matrix3d } from \"./Matrix3d\";\r\nimport { Point3d } from \"./Point3dVector3d\";\r\nimport { Transform } from \"./Transform\";\r\n\r\n// cspell:word Tait\r\n\r\n/**\r\n * Angle properties of a `YawPitchRoll` orientation\r\n * @public\r\n */\r\nexport interface YawPitchRollProps {\r\n /** yaw field */\r\n yaw?: AngleProps;\r\n /** pitch field */\r\n pitch?: AngleProps;\r\n /** roll field */\r\n roll?: AngleProps;\r\n}\r\n\r\n/**\r\n * Three angles that determine the orientation of an object in space, sometimes referred to as [Tait–Bryan angles]\r\n * (https://en.wikipedia.org/wiki/Euler_angles).\r\n * * The matrix construction can be replicated by this logic:\r\n * * xyz coordinates have\r\n * * x forward\r\n * * y to left\r\n * * z up\r\n * * Note that this is a right handed coordinate system.\r\n * * yaw is a rotation of x towards y, i.e. around positive z (counterclockwise):\r\n * * `yawMatrix = Matrix3d.createRotationAroundAxisIndex(2, Angle.createDegrees(yawDegrees));`\r\n * * pitch is a rotation that raises x towards z, i.e. rotation around **negative y** (**clockwise**):\r\n * * `pitchMatrix = Matrix3d.createRotationAroundAxisIndex(1, Angle.createDegrees(-pitchDegrees));`\r\n * * roll is rotation of y towards z, i.e. rotation around positive x (counterclockwise):\r\n * * `rollMatrix = Matrix3d.createRotationAroundAxisIndex(0, Angle.createDegrees(rollDegrees));`\r\n * * The YPR matrix is the product\r\n * * `result = yawMatrix.multiplyMatrixMatrix(pitchMatrix.multiplyMatrixMatrix(rollMatrix));`\r\n * * Note that this is for \"column based\" matrix with vectors multiplying on the right of the matrix.\r\n * Hence a vector is first rotated by roll, then the pitch, finally yaw. So multiplication order in\r\n * the sense of AxisOrder is `RPY` (i.e., XYZ), in contrast to the familiar name `YPR`.\r\n * @public\r\n */\r\nexport class YawPitchRollAngles {\r\n /** The yaw angle: counterclockwise rotation angle around z */\r\n public yaw: Angle;\r\n /** The pitch angle: **clockwise** rotation angle around y */\r\n public pitch: Angle;\r\n /** The roll angle: counterclockwise rotation angle around x */\r\n public roll: Angle;\r\n /**\r\n * Constructor\r\n * @param yaw counterclockwise rotation angle around z\r\n * @param pitch **clockwise** rotation angle around y\r\n * @param roll counterclockwise rotation angle around x\r\n */\r\n constructor(yaw: Angle = Angle.zero(), pitch: Angle = Angle.zero(), roll: Angle = Angle.zero()) {\r\n this.yaw = yaw;\r\n this.pitch = pitch;\r\n this.roll = roll;\r\n }\r\n /** Freeze this YawPitchRollAngles */\r\n public freeze(): Readonly<this> {\r\n this.yaw.freeze();\r\n this.pitch.freeze();\r\n this.roll.freeze();\r\n return Object.freeze(this);\r\n }\r\n /**\r\n * Constructor for YawPitchRollAngles with angles in degrees.\r\n * @param yawDegrees counterclockwise rotation angle (in degrees) around z\r\n * @param pitchDegrees **clockwise** rotation angle (in degrees) around y\r\n * @param rollDegrees counterclockwise rotation angle (in degrees) around x\r\n */\r\n public static createDegrees(yawDegrees: number, pitchDegrees: number, rollDegrees: number): YawPitchRollAngles {\r\n return new YawPitchRollAngles(\r\n Angle.createDegrees(yawDegrees),\r\n Angle.createDegrees(pitchDegrees),\r\n Angle.createDegrees(rollDegrees),\r\n );\r\n }\r\n /**\r\n * Constructor for YawPitchRollAngles with angles in radians.\r\n * @param yawRadians counterclockwise rotation angle (in radians) around z\r\n * @param pitchRadians **clockwise** rotation angle (in radians) around y\r\n * @param rollRadians counterclockwise rotation angle (in radians) around x\r\n */\r\n public static createRadians(yawRadians: number, pitchRadians: number, rollRadians: number): YawPitchRollAngles {\r\n return new YawPitchRollAngles(\r\n Angle.createRadians(yawRadians),\r\n Angle.createRadians(pitchRadians),\r\n Angle.createRadians(rollRadians),\r\n );\r\n }\r\n /** Construct a `YawPitchRoll` object from an object with 3 named angles */\r\n public static fromJSON(json?: YawPitchRollProps): YawPitchRollAngles {\r\n json = json ? json : {};\r\n return new YawPitchRollAngles(\r\n Angle.fromJSON(json.yaw),\r\n Angle.fromJSON(json.pitch),\r\n Angle.fromJSON(json.roll),\r\n );\r\n }\r\n /** Populate yaw, pitch and roll fields using `Angle.fromJSON` */\r\n public setFromJSON(json?: YawPitchRollProps): void {\r\n json = json ? json : {};\r\n this.yaw = Angle.fromJSON(json.yaw);\r\n this.pitch = Angle.fromJSON(json.pitch);\r\n this.roll = Angle.fromJSON(json.roll);\r\n }\r\n /**\r\n * Convert to a JSON object of form { pitch: 20 , roll: 30 , yaw: 10 }. Angles are in degrees.\r\n * Any values that are exactly zero (with tolerance `Geometry.smallAngleRadians`) are omitted.\r\n */\r\n public toJSON(): YawPitchRollProps {\r\n const val: YawPitchRollProps = {};\r\n if (!this.pitch.isAlmostZero)\r\n val.pitch = this.pitch.toJSON();\r\n if (!this.roll.isAlmostZero)\r\n val.roll = this.roll.toJSON();\r\n if (!this.yaw.isAlmostZero)\r\n val.yaw = this.yaw.toJSON();\r\n return val;\r\n }\r\n /**\r\n * Install all rotations from `other` into `this`.\r\n * @param other YawPitchRollAngles source\r\n */\r\n public setFrom(other: YawPitchRollAngles) {\r\n this.yaw.setFrom(other.yaw);\r\n this.pitch.setFrom(other.pitch);\r\n this.roll.setFrom(other.roll);\r\n }\r\n /**\r\n * Compare angles between `this` and `other`.\r\n * * Comparisons are via `isAlmostEqualAllowPeriodShift`.\r\n * @param other YawPitchRollAngles source\r\n */\r\n public isAlmostEqual(other: YawPitchRollAngles) {\r\n return this.yaw.isAlmostEqualAllowPeriodShift(other.yaw)\r\n && this.pitch.isAlmostEqualAllowPeriodShift(other.pitch)\r\n && this.roll.isAlmostEqualAllowPeriodShift(other.roll);\r\n }\r\n /** Make a copy of this YawPitchRollAngles */\r\n public clone() {\r\n return new YawPitchRollAngles(\r\n this.yaw.clone(),\r\n this.pitch.clone(),\r\n this.roll.clone(),\r\n );\r\n }\r\n /**\r\n * Expand the angles into a (rigid rotation) matrix.\r\n * * The returned matrix is \"rigid\" (i.e., it has unit length rows and columns, and its transpose is its inverse).\r\n * * The rigid matrix is always a right handed coordinate system.\r\n * @param result optional pre-allocated `Matrix3d`\r\n */\r\n public toMatrix3d(result?: Matrix3d) {\r\n const cz = Math.cos(this.yaw.radians);\r\n const sz = Math.sin(this.yaw.radians);\r\n const cy = Math.cos(this.pitch.radians);\r\n const sy = Math.sin(this.pitch.radians);\r\n const cx = Math.cos(this.roll.radians);\r\n const sx = Math.sin(this.roll.radians);\r\n /**\r\n * The axis order is XYZ (i.e., RPY) so the rotation matrix is calculated via rZ*rY*rX where\r\n * rX, rY, and rZ are base rotation matrixes:\r\n *\r\n * const rX = Matrix3d.createRowValues(\r\n * 1, 0, 0,\r\n * 0, Math.cos(x), -Math.sin(x),\r\n * 0, Math.sin(x), Math.cos(x),\r\n * );\r\n * const rY = Matrix3d.createRowValues(\r\n * Math.cos(y), 0, Math.sin(y),\r\n * 0, 1, 0,\r\n * -Math.sin(y), 0, Math.cos(y),\r\n * );\r\n * const rZ = Matrix3d.createRowValues(\r\n * Math.cos(z), -Math.sin(z), 0,\r\n * Math.sin(z), Math.cos(z), 0,\r\n * 0, 0, 1,\r\n * );\r\n *\r\n * Then we replace sin(y) with -sin(y) because y rotation (i.e., pitch) is clockwise (alternatively, you\r\n * can use transpose of rY in the matrix multiplication to get the same result)\r\n */\r\n return Matrix3d.createRowValues(\r\n cz * cy, -(sz * cx + cz * sy * sx), (sz * sx - cz * sy * cx),\r\n sz * cy, (cz * cx - sz * sy * sx), -(cz * sx + sz * sy * cx),\r\n sy, cy * sx, cy * cx,\r\n result,\r\n );\r\n }\r\n /**\r\n * Returns true if this rotation does nothing.\r\n * * If allowPeriodShift is false, any nonzero angle is considered a non-identity\r\n * * If allowPeriodShift is true, all angles are individually allowed to be any multiple of 360 degrees.\r\n */\r\n public isIdentity(allowPeriodShift: boolean = true): boolean {\r\n if (allowPeriodShift)\r\n return Angle.isAlmostEqualRadiansAllowPeriodShift(0.0, this.yaw.radians)\r\n && Angle.isAlmostEqualRadiansAllowPeriodShift(0.0, this.pitch.radians)\r\n && Angle.isAlmostEqualRadiansAllowPeriodShift(0.0, this.roll.radians);\r\n else\r\n return Angle.isAlmostEqualRadiansNoPeriodShift(0.0, this.yaw.radians)\r\n && Angle.isAlmostEqualRadiansNoPeriodShift(0.0, this.pitch.radians)\r\n && Angle.isAlmostEqualRadiansNoPeriodShift(0.0, this.roll.radians);\r\n }\r\n /** Return the largest angle in radians */\r\n public maxAbsRadians(): number {\r\n return Geometry.maxAbsXYZ(this.yaw.radians, this.pitch.radians, this.roll.radians);\r\n }\r\n /** Return the sum of the angles in squared radians */\r\n public sumSquaredRadians(): number {\r\n return Geometry.hypotenuseSquaredXYZ(this.yaw.radians, this.pitch.radians, this.roll.radians);\r\n }\r\n /** Return the largest difference of angles (in radians) between this and other */\r\n public maxDiffRadians(other: YawPitchRollAngles): number {\r\n return Math.max(\r\n this.yaw.radians - other.yaw.radians,\r\n this.pitch.radians - other.pitch.radians,\r\n this.roll.radians - other.roll.radians,\r\n );\r\n }\r\n /** Return the largest angle in degrees. */\r\n public maxAbsDegrees(): number {\r\n return Geometry.maxAbsXYZ(this.yaw.degrees, this.pitch.degrees, this.roll.degrees);\r\n }\r\n /** Return the sum of squared angles in degrees. */\r\n public sumSquaredDegrees(): number {\r\n return Geometry.hypotenuseSquaredXYZ(this.yaw.degrees, this.pitch.degrees, this.roll.degrees);\r\n }\r\n /** Return the largest difference of angles (in degrees) between this and other */\r\n public maxDiffDegrees(other: YawPitchRollAngles): number {\r\n return Math.max(\r\n this.yaw.degrees - other.yaw.degrees,\r\n this.pitch.degrees - other.pitch.degrees,\r\n this.roll.degrees - other.roll.degrees,\r\n );\r\n }\r\n /** Return an object from a Transform as an origin and YawPitchRollAngles. */\r\n public static tryFromTransform(transform: Transform): {\r\n origin: Point3d;\r\n angles: YawPitchRollAngles | undefined;\r\n } {\r\n return {\r\n origin: Point3d.createFrom(transform.origin),\r\n angles: YawPitchRollAngles.createFromMatrix3d(transform.matrix),\r\n };\r\n }\r\n /**\r\n * Attempts to create a YawPitchRollAngles object from a Matrix3d.\r\n * @param matrix rigid matrix to process. Caller can test for rigidity with [[Matrix3d.isRigid]] and\r\n * ensure rigid input with [[Matrix3d.createRigidFromMatrix3d]] or [[Matrix3d.makeRigid]].\r\n * @param result optional pre-allocated object to populate and return.\r\n * @returns computed angles, or undefined if `matrix` is not rigid.\r\n * * Even when undefined is returned, `result` (if supplied) is populated with angles that may be used with caution:\r\n * their usefulness decreases the further `matrix` is from being rigid.\r\n */\r\n public static createFromMatrix3d(matrix: Matrix3d, result?: YawPitchRollAngles): YawPitchRollAngles | undefined {\r\n /**\r\n * The rotation matrix form is\r\n *\r\n * Matrix3d.createRowValues(\r\n * cz * cy, -(sz * cx + cz * sy * sx), (sz * sx - cz * sy * cx),\r\n * sz * cy, (cz * cx - sz * sy * sx), -(cz * sx + sz * sy * cx),\r\n * sy, cy * sx, cy * cx\r\n * );\r\n *\r\n * where cx = cos(x), sx = sin(x), cy = cos(y), sy = sin(y), cz = cos(z), and sz = sin(z)\r\n */\r\n const sy = matrix.at(2, 0); // sin(y)\r\n const cy = Math.sqrt(matrix.at(2, 1) * matrix.at(2, 1) + matrix.at(2, 2) * matrix.at(2, 2)); // |cos(y)|\r\n const pitchA = Angle.createAtan2(sy, cy); // with positive cosine\r\n const pitchB = Angle.createAtan2(sy, -cy); // with negative cosine\r\n const angles = result ? result : new YawPitchRollAngles();\r\n /**\r\n * If cos(y) = 0 then y = +-90 degrees so we have a gimbal lock.\r\n * This means x and z can be anything as long as their sum x + z is constant.\r\n * so we can pick z = 0 and calculate x (or pick x = 0 and calculate z).\r\n * Math details can be found\r\n * https://en.wikipedia.org/wiki/Gimbal_lock#Loss_of_a_degree_of_freedom_with_Euler_angles\r\n *\r\n * The rotation matrix for y = +-90 degrees and x = 0 becomes\r\n *\r\n * Matrix3d.createRowValues(\r\n * 0, -sz, -+cz,\r\n * 0, cz, -+sz,\r\n * +-1, 0, 0\r\n * );\r\n *\r\n * so z = atan(sz/cz) = atan(-matrix.at(0, 1), matrix.at(1, 1))\r\n */\r\n if (cy < Geometry.smallAngleRadians) {\r\n angles.yaw = Angle.createAtan2(-matrix.at(0, 1), matrix.at(1, 1));\r\n angles.pitch = pitchA; // this is an arbitrary choice. can pick pitchB instead.\r\n angles.roll = Angle.createRadians(0.0);\r\n } else {\r\n /**\r\n * positive cosine\r\n * z = atan(sz/cz) = atan(matrix.at(1, 0), matrix.at(0, 0))\r\n * x = atan(sx/cx) = atan(matrix.at(2, 1), matrix.at(2, 2))\r\n */\r\n const yawA = Angle.createAtan2(matrix.at(1, 0), matrix.at(0, 0));\r\n const rollA = Angle.createAtan2(matrix.at(2, 1), matrix.at(2, 2));\r\n // similar with negative cosine\r\n const yawB = Angle.createAtan2(-matrix.at(1, 0), -matrix.at(0, 0));\r\n const rollB = Angle.createAtan2(-matrix.at(2, 1), -matrix.at(2, 2));\r\n // create YPR\r\n const yprA = new YawPitchRollAngles(yawA, pitchA, rollA);\r\n const yprB = new YawPitchRollAngles(yawB, pitchB, rollB);\r\n // decide to pick yprA or yprB with smallest magnitude angles\r\n const absFactor = 0.95;\r\n const maxRadiansA = yprA.maxAbsRadians();\r\n const maxRadiansB = yprB.maxAbsRadians();\r\n if (maxRadiansA < absFactor * maxRadiansB) {\r\n angles.setFrom(yprA);\r\n } else if (maxRadiansB < absFactor * maxRadiansA) {\r\n angles.setFrom(yprB);\r\n } else {\r\n const sumA = yprA.sumSquaredRadians();\r\n const sumB = yprB.sumSquaredRadians();\r\n if (sumA <= sumB) {\r\n angles.setFrom(yprA);\r\n } else {\r\n angles.setFrom(yprB);\r\n }\r\n }\r\n }\r\n // sanity check\r\n const matrix1 = angles.toMatrix3d();\r\n return matrix.isAlmostEqual(matrix1) ? angles : undefined;\r\n }\r\n}\r\n"]}
@@ -662,6 +662,17 @@ export declare class Sample {
662
662
  * @param numStroke number of steps to take.
663
663
  */
664
664
  static createZigZag(start: Point3d | Point3d[], steps: Vector3d[], numStroke: number): Point3d[];
665
+ /** Create a point on the polar parametric curve r = cos(a * theta), aka "rose".
666
+ * @param theta angle
667
+ * @param a period multiplier. If odd, this is the petal count; if even, twice the number of petals.
668
+ * @param z z-coordinate for output
669
+ */
670
+ static createRosePoint3d(theta: number, a: number, z?: number): Point3d;
671
+ /** Create a point on the polar parametric curve r = cos(a * theta), aka "rose".
672
+ * @param theta angle
673
+ * @param a period multiplier. If odd, this is the petal count; if even, twice the number of petals.
674
+ */
675
+ static createRosePoint2d(theta: number, a: number): Point2d;
665
676
  /**
666
677
  * Create a mesh surface from samples of a smooth function over [0,1]x[0,1].
667
678
  * @param size grid size; the number of intervals on each side of the unit square domain.