classes/edge.js

  1. /**
  2. * Created by Alex Bol on 3/17/2017.
  3. */
  4. import Flatten from '../flatten';
  5. import {ray_shoot} from "../algorithms/ray_shooting";
  6. /**
  7. * Class representing an edge of polygon. Edge shape may be Segment or Arc.
  8. * Each edge contains references to the next and previous edges in the face of the polygon.
  9. *
  10. * @type {Edge}
  11. */
  12. export class Edge {
  13. /**
  14. * Construct new instance of edge
  15. * @param {Shape} shape Shape of type Segment or Arc
  16. */
  17. constructor(shape) {
  18. /**
  19. * Shape of the edge: Segment or Arc
  20. * @type {Segment|Arc}
  21. */
  22. this.shape = shape;
  23. /**
  24. * Pointer to the next edge in the face
  25. * @type {Edge}
  26. */
  27. this.next = undefined;
  28. /**
  29. * Pointer to the previous edge in the face
  30. * @type {Edge}
  31. */
  32. this.prev = undefined;
  33. /**
  34. * Pointer to the face containing this edge
  35. * @type {Face}
  36. */
  37. this.face = undefined;
  38. /**
  39. * "Arc distance" from the face start
  40. * @type {number}
  41. */
  42. this.arc_length = 0;
  43. /**
  44. * Start inclusion flag (inside/outside/boundary)
  45. * @type {*}
  46. */
  47. this.bvStart = undefined;
  48. /**
  49. * End inclusion flag (inside/outside/boundary)
  50. * @type {*}
  51. */
  52. this.bvEnd = undefined;
  53. /**
  54. * Edge inclusion flag (Flatten.INSIDE, Flatten.OUTSIDE, Flatten.BOUNDARY)
  55. * @type {*}
  56. */
  57. this.bv = undefined;
  58. /**
  59. * Overlap flag for boundary edge (Flatten.OVERLAP_SAME/Flatten.OVERLAP_OPPOSITE)
  60. * @type {*}
  61. */
  62. this.overlap = undefined;
  63. }
  64. /**
  65. * Get edge start point
  66. */
  67. get start() {
  68. return this.shape.start;
  69. }
  70. /**
  71. * Get edge end point
  72. */
  73. get end() {
  74. return this.shape.end;
  75. }
  76. /**
  77. * Get edge length
  78. */
  79. get length() {
  80. return this.shape.length;
  81. }
  82. /**
  83. * Get bounding box of the edge
  84. * @returns {Box}
  85. */
  86. get box() {
  87. return this.shape.box;
  88. }
  89. get isSegment() {
  90. return this.shape instanceof Flatten.Segment;
  91. }
  92. get isArc() {
  93. return this.shape instanceof Flatten.Arc;
  94. }
  95. get isLine() {
  96. return this.shape instanceof Flatten.Line;
  97. }
  98. get isRay() {
  99. return this.shape instanceof Flatten.Ray
  100. }
  101. /**
  102. * Get middle point of the edge
  103. * @returns {Point}
  104. */
  105. middle() {
  106. return this.shape.middle();
  107. }
  108. /**
  109. * Get point at given length
  110. * @param {number} length - The length along the edge
  111. * @returns {Point}
  112. */
  113. pointAtLength(length) {
  114. return this.shape.pointAtLength(length);
  115. }
  116. /**
  117. * Returns true if point belongs to the edge, false otherwise
  118. * @param {Point} pt - test point
  119. */
  120. contains(pt) {
  121. return this.shape.contains(pt);
  122. }
  123. /**
  124. * Set inclusion flag of the edge with respect to another polygon
  125. * Inclusion flag is one of Flatten.INSIDE, Flatten.OUTSIDE, Flatten.BOUNDARY
  126. * @param polygon
  127. */
  128. setInclusion(polygon) {
  129. if (this.bv !== undefined) return this.bv;
  130. if (this.shape instanceof Flatten.Line || this.shape instanceof Flatten.Ray) {
  131. this.bv = Flatten.OUTSIDE;
  132. return this.bv;
  133. }
  134. if (this.bvStart === undefined) {
  135. this.bvStart = ray_shoot(polygon, this.start);
  136. }
  137. if (this.bvEnd === undefined) {
  138. this.bvEnd = ray_shoot(polygon, this.end);
  139. }
  140. /* At least one end outside - the whole edge outside */
  141. if (this.bvStart === Flatten.OUTSIDE || this.bvEnd == Flatten.OUTSIDE) {
  142. this.bv = Flatten.OUTSIDE;
  143. }
  144. /* At least one end inside - the whole edge inside */
  145. else if (this.bvStart === Flatten.INSIDE || this.bvEnd == Flatten.INSIDE) {
  146. this.bv = Flatten.INSIDE;
  147. }
  148. /* Both are boundary - check the middle point */
  149. else {
  150. let bvMiddle = ray_shoot(polygon, this.middle());
  151. // let boundary = this.middle().distanceTo(polygon)[0] < 10*Flatten.DP_TOL;
  152. // let bvMiddle = boundary ? Flatten.BOUNDARY : ray_shoot(polygon, this.middle());
  153. this.bv = bvMiddle;
  154. }
  155. return this.bv;
  156. }
  157. /**
  158. * Set overlapping between two coincident boundary edges
  159. * Overlapping flag is one of Flatten.OVERLAP_SAME or Flatten.OVERLAP_OPPOSITE
  160. * @param edge
  161. */
  162. setOverlap(edge) {
  163. let flag = undefined;
  164. let shape1 = this.shape;
  165. let shape2 = edge.shape;
  166. if (shape1 instanceof Flatten.Segment && shape2 instanceof Flatten.Segment) {
  167. if (shape1.start.equalTo(shape2.start) && shape1.end.equalTo(shape2.end)) {
  168. flag = Flatten.OVERLAP_SAME;
  169. } else if (shape1.start.equalTo(shape2.end) && shape1.end.equalTo(shape2.start)) {
  170. flag = Flatten.OVERLAP_OPPOSITE;
  171. }
  172. } else if (shape1 instanceof Flatten.Arc && shape2 instanceof Flatten.Arc) {
  173. if (shape1.start.equalTo(shape2.start) && shape1.end.equalTo(shape2.end) && /*shape1.counterClockwise === shape2.counterClockwise &&*/
  174. shape1.middle().equalTo(shape2.middle())) {
  175. flag = Flatten.OVERLAP_SAME;
  176. } else if (shape1.start.equalTo(shape2.end) && shape1.end.equalTo(shape2.start) && /*shape1.counterClockwise !== shape2.counterClockwise &&*/
  177. shape1.middle().equalTo(shape2.middle())) {
  178. flag = Flatten.OVERLAP_OPPOSITE;
  179. }
  180. } else if (shape1 instanceof Flatten.Segment && shape2 instanceof Flatten.Arc ||
  181. shape1 instanceof Flatten.Arc && shape2 instanceof Flatten.Segment) {
  182. if (shape1.start.equalTo(shape2.start) && shape1.end.equalTo(shape2.end) && shape1.middle().equalTo(shape2.middle())) {
  183. flag = Flatten.OVERLAP_SAME;
  184. } else if (shape1.start.equalTo(shape2.end) && shape1.end.equalTo(shape2.start) && shape1.middle().equalTo(shape2.middle())) {
  185. flag = Flatten.OVERLAP_OPPOSITE;
  186. }
  187. }
  188. /* Do not update overlap flag if already set on previous chain */
  189. if (this.overlap === undefined) this.overlap = flag;
  190. if (edge.overlap === undefined) edge.overlap = flag;
  191. }
  192. svg() {
  193. if (this.shape instanceof Flatten.Segment) {
  194. return ` L${this.shape.end.x},${this.shape.end.y}`;
  195. } else if (this.shape instanceof Flatten.Arc) {
  196. let arc = this.shape;
  197. let largeArcFlag;
  198. let sweepFlag = arc.counterClockwise ? "1" : "0";
  199. // Draw full circe arc as special case: split it into two half-circles
  200. if (Flatten.Utils.EQ(arc.sweep, 2 * Math.PI)) {
  201. let sign = arc.counterClockwise ? 1 : -1;
  202. let halfArc1 = new Flatten.Arc(arc.pc, arc.r, arc.startAngle, arc.startAngle + sign * Math.PI, arc.counterClockwise);
  203. let halfArc2 = new Flatten.Arc(arc.pc, arc.r, arc.startAngle + sign * Math.PI, arc.endAngle, arc.counterClockwise);
  204. largeArcFlag = "0";
  205. return ` A${halfArc1.r},${halfArc1.r} 0 ${largeArcFlag},${sweepFlag} ${halfArc1.end.x},${halfArc1.end.y}
  206. A${halfArc2.r},${halfArc2.r} 0 ${largeArcFlag},${sweepFlag} ${halfArc2.end.x},${halfArc2.end.y}`
  207. } else {
  208. largeArcFlag = arc.sweep <= Math.PI ? "0" : "1";
  209. return ` A${arc.r},${arc.r} 0 ${largeArcFlag},${sweepFlag} ${arc.end.x},${arc.end.y}`;
  210. }
  211. }
  212. }
  213. toJSON() {
  214. return this.shape.toJSON();
  215. }
  216. };
  217. Flatten.Edge = Edge;