From 3387b0a7b6844ef51f2fab988d8d0ac608912264 Mon Sep 17 00:00:00 2001 From: alexanderbol Date: Sat, 2 Mar 2024 21:46:49 +0200 Subject: [PATCH 1/6] Cut polygon with multiline (not finished, 1 test failed) --- index.d.ts | 6 ++- src/algorithms/intersection.js | 43 +++++++++++++----- src/classes/edge.js | 12 ++++- src/classes/line.js | 2 +- src/classes/multiline.js | 8 ++++ src/classes/polygon.js | 53 ++++++++++++---------- src/classes/ray.js | 13 ++++++ src/data_structures/smart_intersections.js | 46 +++++++++++-------- test/classes/line.js | 2 +- test/classes/polygon.js | 18 ++++++++ 10 files changed, 143 insertions(+), 60 deletions(-) diff --git a/index.d.ts b/index.d.ts index e17d6862..c08a1295 100644 --- a/index.d.ts +++ b/index.d.ts @@ -461,10 +461,12 @@ declare namespace Flatten { readonly end: Point; readonly length: number; readonly box: Box; + readonly isSegment: boolean; + readonly isArc: boolean; + readonly isLine: boolean; + readonly isRay: boolean // public methods - isSegment() : boolean; - isArc() : boolean; contains(pt: Point): boolean; middle(): Point; pointAtLength(length: number): Point|null; diff --git a/src/algorithms/intersection.js b/src/algorithms/intersection.js index c5883399..43590d8a 100644 --- a/src/algorithms/intersection.js +++ b/src/algorithms/intersection.js @@ -424,19 +424,23 @@ export function intersectArc2Box(arc, box) { } export function intersectEdge2Segment(edge, segment) { - return edge.isSegment() ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); + return edge.isSegment ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); } export function intersectEdge2Arc(edge, arc) { - return edge.isSegment() ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); + return edge.isSegment ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); } export function intersectEdge2Line(edge, line) { - return edge.isSegment() ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); + return edge.isSegment ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); +} + +export function intersectEdge2Ray(edge, ray) { + return edge.isSegment ? intersectRay2Segment(ray, edge.shape) : intersectRay2Arc(ray, edge.shape); } export function intersectEdge2Circle(edge, circle) { - return edge.isSegment() ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); + return edge.isSegment ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); } export function intersectSegment2Polygon(segment, polygon) { @@ -498,11 +502,19 @@ export function intersectCircle2Polygon(circle, polygon) { } export function intersectEdge2Edge(edge1, edge2) { - const shape1 = edge1.shape; - const shape2 = edge2.shape; - return edge1.isSegment() ? - (edge2.isSegment() ? intersectSegment2Segment(shape1, shape2) : intersectSegment2Arc(shape1, shape2)) : - (edge2.isSegment() ? intersectSegment2Arc(shape2, shape1) : intersectArc2Arc(shape1, shape2)); + if (edge1.isSegment) { + return intersectEdge2Segment(edge2, edge1.shape) + } + else if (edge1.isArc) { + return intersectEdge2Arc(edge2, edge1.shape) + } + else if (edge1.isLine) { + return intersectEdge2Line(edge2, edge1.shape) + } + else if (edge1.isRay) { + return intersectEdge2Ray(edge2, edge1.shape) + } + return [] } export function intersectEdge2Polygon(edge, polygon) { @@ -515,8 +527,17 @@ export function intersectEdge2Polygon(edge, polygon) { let resp_edges = polygon.edges.search(edge.shape.box); for (let resp_edge of resp_edges) { - for (let pt of intersectEdge2Edge(edge, resp_edge)) { - ip.push(pt); + if (resp_edge.isSegment) { + ip = [...ip, ...intersectSegment2Polygon(resp_edge, polygon)] + } + else if (resp_edge.isArc) { + ip = [...ip, ...intersectArc2Polygon(resp_edge, polygon)] + } + else if (resp_edge.isLine) { + ip = [...ip, ...intersectLine2Polygon(resp_edge, polygon)] + } + else if (resp_edge.isRay) { + ip = [...ip, ...intersectRay2Polygon(resp_edge, polygon)] } } diff --git a/src/classes/edge.js b/src/classes/edge.js index 2f1f54a9..82fb1f9d 100644 --- a/src/classes/edge.js +++ b/src/classes/edge.js @@ -93,14 +93,22 @@ export class Edge { return this.shape.box; } - isSegment() { + get isSegment() { return this.shape instanceof Flatten.Segment; } - isArc() { + get isArc() { return this.shape instanceof Flatten.Arc; } + get isLine() { + return this.shape instanceof Flatten.Line; + } + + get isRay() { + return this.shape instanceof Flatten.Ray + } + /** * Get middle point of the edge * @returns {Point} diff --git a/src/classes/line.js b/src/classes/line.js index 17a1d067..4ddcebdf 100644 --- a/src/classes/line.js +++ b/src/classes/line.js @@ -283,7 +283,7 @@ export class Line extends Shape { */ split(pt) { if (pt instanceof Flatten.Point) { - return [new Flatten.Ray(pt, this.norm.invert()), new Flatten.Ray(pt, this.norm)] + return [new Flatten.Ray(pt, this.norm), new Flatten.Ray(pt, this.norm)] } else { let multiline = new Flatten.Multiline([this]); diff --git a/src/classes/multiline.js b/src/classes/multiline.js index 50fdbc50..b6f136b5 100644 --- a/src/classes/multiline.js +++ b/src/classes/multiline.js @@ -103,6 +103,14 @@ export class Multiline extends LinkedList { return newEdge; } + getChain(edgeFrom, edgeTo) { + let edges = [] + for (let edge = edgeFrom; edge !== edgeTo.next; edge = edge.next) { + edges.push(edge) + } + return edges + } + /** * Split edges of multiline with intersection points and return mutated multiline * @param {Point[]} ip - array of points to be added as new vertices diff --git a/src/classes/polygon.js b/src/classes/polygon.js index 31c80721..38b70de8 100644 --- a/src/classes/polygon.js +++ b/src/classes/polygon.js @@ -11,11 +11,11 @@ import * as Intersection from "../algorithms/intersection"; import * as Relations from "../algorithms/relation"; import { addToIntPoints, calculateInclusionFlags, filterDuplicatedIntersections, - getSortedArray, getSortedArrayOnLine, initializeInclusionFlags, insertBetweenIntPoints, + getSortedArray, initializeInclusionFlags, insertBetweenIntPoints, splitByIntersections } from "../data_structures/smart_intersections"; import {Multiline} from "./multiline"; -import {intersectEdge2Line} from "../algorithms/intersection"; +import {intersectEdge2Edge} from "../algorithms/intersection"; import {INSIDE, BOUNDARY} from "../utils/constants"; import {convertToString} from "../utils/attributes"; import {Matrix} from "./matrix"; @@ -391,23 +391,26 @@ export class Polygon { int_points2_sorted: [] }; - // intersect line with each edge of the polygon + // intersect each edge of multiline with each edge of the polygon // and create smart intersections - for (let edge of newPoly.edges) { - let ip = intersectEdge2Line(edge, line); - // for each intersection point - for (let pt of ip) { - addToIntPoints(multiline.first, pt, intersections.int_points1); - addToIntPoints(edge, pt, intersections.int_points2); + for (let edge1 of multiline.edges) { + for (let edge2 of newPoly.edges) { + let ip = intersectEdge2Edge(edge1, edge2); + // for each intersection point + for (let pt of ip) { + addToIntPoints(edge1, pt, intersections.int_points1); + addToIntPoints(edge2, pt, intersections.int_points2); + } } } + // No intersections - return a copy of the original polygon if (intersections.int_points1.length === 0) return newPoly; // sort smart intersections - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); // split by intersection points @@ -418,7 +421,7 @@ export class Polygon { filterDuplicatedIntersections(intersections); // sort intersection points again after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); // initialize inclusion flags for edges of multiline incident to intersections @@ -442,23 +445,27 @@ export class Polygon { return newPoly; // sort intersection points 3d time after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); // Add 2 new inner edges between intersection points - let int_point1_prev = intersections.int_points1[0]; - let new_edge; - for (let int_point1_curr of intersections.int_points1_sorted) { + let int_point1_prev + let int_point1_curr; + for (let i = 1; i < intersections.int_points1_sorted.length; i++) { + int_point1_curr = intersections.int_points1_sorted[i] + int_point1_prev = intersections.int_points1_sorted[i-1]; if (int_point1_curr.edge_before.bv === INSIDE) { - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_prev.pt, int_point1_curr.pt)); // (int_point1_curr.edge_before.shape); - insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], new_edge); - newPoly.edges.add(new_edge); - - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_curr.pt, int_point1_prev.pt)); // (int_point1_curr.edge_before.shape.reverse()); - insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], new_edge); - newPoly.edges.add(new_edge); + let edgeFrom = int_point1_prev.edge_after + let edgeTo = int_point1_curr.edge_before + let newEdges = multiline.getChain(edgeFrom, edgeTo) + insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)) + + newEdges = newEdges.reverse().map(edge => new Flatten.Edge(edge.shape.reverse())) + insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); } - int_point1_prev = int_point1_curr; + } // Recreate faces diff --git a/src/classes/ray.js b/src/classes/ray.js index 302c2f28..ea0264f1 100644 --- a/src/classes/ray.js +++ b/src/classes/ray.js @@ -4,6 +4,7 @@ import Flatten from '../flatten'; import * as Intersection from "../algorithms/intersection"; import {Shape} from "./shape"; import {Errors} from "../utils/errors"; +import {vector} from './vector' /** * Class representing a ray (a half-infinite line). @@ -111,6 +112,18 @@ export class Ray extends Shape { return Flatten.Utils.EQ_0(this.norm.dot(vec)) && Flatten.Utils.GE(vec.cross(this.norm),0); } + /** + * Return coordinate of the point that lies on the ray in the transformed + * coordinate system where center is the projection of the point(0,0) to + * the line containing this ray and axe y is collinear to the normal vector.
+ * This method assumes that point lies on the ray + * @param {Point} pt - point on a ray + * @returns {number} + */ + coord(pt) { + return vector(pt.x, pt.y).cross(this.norm); + } + /** * Split ray with point and return array of segment and new ray * @param {Point} pt diff --git a/src/data_structures/smart_intersections.js b/src/data_structures/smart_intersections.js index 6d446938..cfa7be60 100644 --- a/src/data_structures/smart_intersections.js +++ b/src/data_structures/smart_intersections.js @@ -32,7 +32,13 @@ export function addToIntPoints(edge, pt, int_points) is_vertex |= Constants.END_VERTEX; } // Fix intersection point which is end point of the last edge - let arc_length = (is_vertex & Constants.END_VERTEX) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + let arc_length + if (len === Infinity) { + arc_length = shapes[0].coord(pt) + } + else { + arc_length = (is_vertex & Constants.END_VERTEX) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + } int_points.push({ id: id, @@ -95,17 +101,17 @@ function compareFn(ip1, ip2) return 0; } -export function getSortedArrayOnLine(line, int_points) { - return int_points.slice().sort( (int_point1, int_point2) => { - if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { - return -1; - } - if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { - return 1; - } - return 0; - }) -} +// export function getSortedArrayOnLine(line, int_points) { +// return int_points.slice().sort( (int_point1, int_point2) => { +// if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { +// return -1; +// } +// if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { +// return 1; +// } +// return 0; +// }) +// } export function filterDuplicatedIntersections(intersections) { @@ -352,13 +358,13 @@ export function splitByIntersections(polygon, int_points) } } -export function insertBetweenIntPoints(int_point1, int_point2, new_edge) { - let edge_before = int_point1.edge_before; - let edge_after = int_point2.edge_after; - - edge_before.next = new_edge; - new_edge.prev = edge_before; +export function insertBetweenIntPoints(int_point1, int_point2, new_edges) { + const edge_before = int_point1.edge_before; + const edge_after = int_point2.edge_after; + const len = new_edges.length + edge_before.next = new_edges[0]; + new_edges[0].prev = edge_before; - new_edge.next = edge_after; - edge_after.prev = new_edge; + new_edges[len-1].next = edge_after; + edge_after.prev = new_edges[len-1]; } diff --git a/test/classes/line.js b/test/classes/line.js index 97a8866b..0a9949e2 100644 --- a/test/classes/line.js +++ b/test/classes/line.js @@ -87,7 +87,7 @@ describe('#Flatten.Line', function() { let l = line(pt,norm); let split_pt = point(300,200); let res = l.split(split_pt); - expect(res[0]).to.deep.equal(ray(split_pt, norm.invert())); + expect(res[0]).to.deep.equal(ray(split_pt, norm)); expect(res[1]).to.deep.equal(ray(split_pt, norm)); }); it('May return 1-dim coordinate of point on line', function() { diff --git a/test/classes/polygon.js b/test/classes/polygon.js index 64904b8a..b1b4abb9 100644 --- a/test/classes/polygon.js +++ b/test/classes/polygon.js @@ -908,4 +908,22 @@ describe('#Flatten.Polygon', function() { expect(res_poly.faces.size).to.equal(3); expect(res_poly.edges.size).to.equal(12); }); + + it('Can cut polygon without holes by line. Line is touching concave vertex', () => { + let l = line(point(100, 250), vector(0.7, 1)); + let points = [ + point(100, 20), + point(250, 75), + point(350, 75), + point(300, 350), + point(170, 200), + point(120, 350), + point(70, 120) + ]; + let poly = new Polygon(points); + const res_poly = poly.cutWithLine(l); + + expect(res_poly.faces.size).to.equal(3); + expect(res_poly.edges.size).to.equal(14); + }); }); From b15e05d70c4ccd1db26e6007c945f16de674c3e3 Mon Sep 17 00:00:00 2001 From: alexanderbol Date: Thu, 14 Mar 2024 16:14:27 +0200 Subject: [PATCH 2/6] Cut polygon with multiline - completed. Interface changed: function returns single polygon, not an array of polygons fixed some tests --- src/algorithms/relation.js | 6 +- src/classes/polygon.js | 166 +++++++++++++++++-------------------- test/classes/polygon.js | 41 ++++----- 3 files changed, 95 insertions(+), 118 deletions(-) diff --git a/src/algorithms/relation.js b/src/algorithms/relation.js index c3a818f7..3a09f87f 100644 --- a/src/algorithms/relation.js +++ b/src/algorithms/relation.js @@ -203,7 +203,7 @@ function relateLine2Circle(line,circle) { denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon([circle.toArc()]).cut(multiline); + denim.E2I = new Flatten.Polygon([circle.toArc()]).cutWithLine(line); } return denim; @@ -245,7 +245,7 @@ function relateLine2Box(line, box) { denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon(box.toSegments()).cut(multiline); + denim.E2I = new Flatten.Polygon(box.toSegments()).cutWithLine(line); } } return denim; @@ -265,7 +265,7 @@ function relateLine2Polygon(line, polygon) { denim.I2B = [...multiline].slice(1).map( (edge) => edge.bv === Flatten.BOUNDARY ? edge.shape : edge.shape.start ); denim.I2E = [...multiline].filter(edge => edge.bv === Flatten.OUTSIDE).map(edge => edge.shape); - denim.E2I = polygon.cut(multiline); + denim.E2I = polygon.cutWithLine(line); return denim; } diff --git a/src/classes/polygon.js b/src/classes/polygon.js index 38b70de8..7d5fc76c 100644 --- a/src/classes/polygon.js +++ b/src/classes/polygon.js @@ -291,97 +291,12 @@ export class Polygon { } /** - * Cut polygon with multiline and return array of new polygons - * Multiline should be constructed from a line with intersection point, see notebook: - * https://next.observablehq.com/@alexbol99/cut-polygon-with-line + * Cut polygon with multiline and return a new polygon * @param {Multiline} multiline - * @returns {Polygon[]} + * @returns {Polygon} */ cut(multiline) { - let cutPolygons = [this.clone()]; - for (let edge of multiline) { - if (edge.setInclusion(this) !== INSIDE) - continue; - - let cut_edge_start = edge.shape.start; - let cut_edge_end = edge.shape.end; - - let newCutPolygons = []; - for (let polygon of cutPolygons) { - if (polygon.findEdgeByPoint(cut_edge_start) === undefined) { - newCutPolygons.push(polygon); - } else { - let [cutPoly1, cutPoly2] = polygon.cutFace(cut_edge_start, cut_edge_end); - newCutPolygons.push(cutPoly1, cutPoly2); - } - } - cutPolygons = newCutPolygons; - } - return cutPolygons; - } - - /** - * Cut face of polygon with a segment between two points and create two new polygons - * Supposed that a segments between points does not intersect any other edge - * @param {Point} pt1 - * @param {Point} pt2 - * @returns {Polygon[]} - */ - cutFace(pt1, pt2) { - let edge1 = this.findEdgeByPoint(pt1); - let edge2 = this.findEdgeByPoint(pt2); - if (edge1.face !== edge2.face) - return []; - - // Cut face into two and create new polygon with two faces - let edgeBefore1 = this.addVertex(pt1, edge1); - edge2 = this.findEdgeByPoint(pt2); - let edgeBefore2 = this.addVertex(pt2, edge2); - - let face = edgeBefore1.face; - let newEdge1 = new Flatten.Edge( - new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) - ); - let newEdge2 = new Flatten.Edge( - new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) - ); - - // Swap links - edgeBefore1.next.prev = newEdge2; - newEdge2.next = edgeBefore1.next; - - edgeBefore1.next = newEdge1; - newEdge1.prev = edgeBefore1; - - edgeBefore2.next.prev = newEdge1; - newEdge1.next = edgeBefore2.next; - - edgeBefore2.next = newEdge2; - newEdge2.prev = edgeBefore2; - - // Insert new edge to the edges container and 2d index - this.edges.add(newEdge1); - this.edges.add(newEdge2); - - // Add two new faces - let face1 = this.addFace(newEdge1, edgeBefore1); - let face2 = this.addFace(newEdge2, edgeBefore2); - - // Remove old face - this.faces.delete(face); - - return [face1.toPolygon(), face2.toPolygon()]; - } - - /** - * Return a result of cutting polygon with line - * @param {Line} line - cutting line - * @returns {Polygon} newPoly - resulted polygon - */ - cutWithLine(line) { - let newPoly = this.clone(); - - let multiline = new Multiline([line]); + let newPoly = this.clone() // smart intersections let intersections = { @@ -462,6 +377,10 @@ export class Polygon { newEdges.forEach(edge => newPoly.edges.add(edge)) newEdges = newEdges.reverse().map(edge => new Flatten.Edge(edge.shape.reverse())) + for (let k=0; k < newEdges.length-1; k++) { + newEdges[k].next = newEdges[k+1] + newEdges[k+1].prev = newEdges[k] + } insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], newEdges); newEdges.forEach(edge => newPoly.edges.add(edge)); } @@ -470,7 +389,72 @@ export class Polygon { // Recreate faces newPoly.recreateFaces(); - return newPoly; + + return newPoly + } + + /** + * Cut face of polygon with a segment between two points and create two new polygons + * Supposed that a segments between points does not intersect any other edge + * @param {Point} pt1 + * @param {Point} pt2 + * @returns {Polygon[]} + */ + cutFace(pt1, pt2) { + let edge1 = this.findEdgeByPoint(pt1); + let edge2 = this.findEdgeByPoint(pt2); + if (edge1.face !== edge2.face) + return []; + + // Cut face into two and create new polygon with two faces + let edgeBefore1 = this.addVertex(pt1, edge1); + edge2 = this.findEdgeByPoint(pt2); + let edgeBefore2 = this.addVertex(pt2, edge2); + + let face = edgeBefore1.face; + let newEdge1 = new Flatten.Edge( + new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) + ); + let newEdge2 = new Flatten.Edge( + new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) + ); + + // Swap links + edgeBefore1.next.prev = newEdge2; + newEdge2.next = edgeBefore1.next; + + edgeBefore1.next = newEdge1; + newEdge1.prev = edgeBefore1; + + edgeBefore2.next.prev = newEdge1; + newEdge1.next = edgeBefore2.next; + + edgeBefore2.next = newEdge2; + newEdge2.prev = edgeBefore2; + + // Insert new edge to the edges container and 2d index + this.edges.add(newEdge1); + this.edges.add(newEdge2); + + // Add two new faces + let face1 = this.addFace(newEdge1, edgeBefore1); + let face2 = this.addFace(newEdge2, edgeBefore2); + + // Remove old face + this.faces.delete(face); + + return [face1.toPolygon(), face2.toPolygon()]; + } + + /** + * A special case of cut() function + * The return is a polygon cut with line + * @param {Line} line - cutting line + * @returns {Polygon} newPoly - resulted polygon + */ + cutWithLine(line) { + let multiline = new Multiline([line]); + return this.cut(multiline); } /** @@ -490,8 +474,8 @@ export class Polygon { } /** - * Split polygon into array of polygons, where each polygon is an island with all - * hole that it contains + * Split polygon into array of polygons, where each polygon is an outer face with all + * containing inner faces * @returns {Flatten.Polygon[]} */ splitToIslands() { diff --git a/test/classes/polygon.js b/test/classes/polygon.js index b1b4abb9..942457f4 100644 --- a/test/classes/polygon.js +++ b/test/classes/polygon.js @@ -815,51 +815,44 @@ describe('#Flatten.Polygon', function() { let ip = intersectLine2Polygon(l, p); let mline = multiline([l]); mline.split(ip); - let cut_polygons = p.cut(mline); + let cut_polygon = p.cut(mline); - expect(cut_polygons.length).to.equal(1); - expect(cut_polygons[0].faces.size).to.equal(p.faces.size); - expect(cut_polygons[0].edges.size).to.equal(p.edges.size); + expect(cut_polygon.faces.size).to.equal(p.faces.size); + expect(cut_polygon.edges.size).to.equal(p.edges.size); }); it('Can cut polygon with multiline line. Case of touching in one point', function() { let l = line( point(100,350), vector(0,1) ); let points = [point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let p = new Polygon(points); - let ip = intersectLine2Polygon(l, p); let mline = multiline([l]); - mline.split(ip); - let cut_polygons = p.cut(mline); + let cut_polygon = p.cut(mline); - expect(cut_polygons.length).to.equal(1); - expect(cut_polygons[0].faces.size).to.equal(p.faces.size); - expect(cut_polygons[0].edges.size).to.equal(p.edges.size); + expect(cut_polygon.faces.size).to.equal(p.faces.size); + expect(cut_polygon.edges.size).to.equal(p.edges.size); }); it('Can cut polygon with line into 2 polygons', function() { let l = line( point(100,175), vector(0,1) ); let points = [point(100, 20), point(250, 75), point(350, 75), point(300, 200), point(170, 200), point(120, 350), point(70, 120) ]; let p = new Polygon(points); - let ip = intersectLine2Polygon(l, p); let mline = multiline([l]); - mline.split(ip); - let cut_polygons = p.cut(mline); + let cut_polygon = p.cut(mline); - expect(cut_polygons.length).to.equal(2); - expect(cut_polygons[0].edges.size).to.equal(5); - expect(cut_polygons[1].edges.size).to.equal(6); + expect(cut_polygon.faces.size).to.equal(2); + expect([...cut_polygon.faces][0].edges.length).to.equal(6); + expect([...cut_polygon.faces][1].edges.length).to.equal(5); }); it('Can cut polygon with line into 3 polygons', function() { let l = line( point(100,250), vector(0,1) ); let points = [point(100, 20), point(250, 75), point(350, 75), point(300, 350), point(170, 200), point(120, 350), point(70, 120) ]; let p = new Polygon(points); - let ip = intersectLine2Polygon(l, p); let mline = multiline([l]); - mline.split(ip); - let cut_polygons = p.cut(mline); + let cut_polygon = p.cut(mline); - expect(cut_polygons.length).to.equal(3); - expect(cut_polygons[0].edges.size).to.equal(3); - expect(cut_polygons[1].edges.size).to.equal(3); - expect(cut_polygons[2].edges.size).to.equal(9); + expect(cut_polygon.faces.size).to.equal(3); + const faces = [...cut_polygon.faces] + expect(faces[0].edges.length).to.equal(9); + expect(faces[1].edges.length).to.equal(3); + expect(faces[2].edges.length).to.equal(3); }); }); @@ -906,7 +899,7 @@ describe('#Flatten.Polygon', function() { const res_poly = poly.cutWithLine(l); expect(res_poly.faces.size).to.equal(3); - expect(res_poly.edges.size).to.equal(12); + expect(res_poly.edges.size).to.equal(14); }); it('Can cut polygon without holes by line. Line is touching concave vertex', () => { From e7359b87a97c5e9528ddf1000f0b3aad4b3b46af Mon Sep 17 00:00:00 2001 From: alexanderbol Date: Thu, 14 Mar 2024 16:17:26 +0200 Subject: [PATCH 3/6] Fixed some warnings --- src/classes/multiline.js | 7 +++---- src/data_structures/smart_intersections.js | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/classes/multiline.js b/src/classes/multiline.js index b6f136b5..c523c13a 100644 --- a/src/classes/multiline.js +++ b/src/classes/multiline.js @@ -2,7 +2,6 @@ import Flatten from '../flatten'; import LinkedList from '../data_structures/linked_list'; -import {END_VERTEX, NOT_VERTEX, START_VERTEX} from "../utils/constants"; import {convertToString} from "../utils/attributes"; /** @@ -17,10 +16,10 @@ export class Multiline extends LinkedList { return; } - if (args.length == 1) { + if (args.length === 1) { if (args[0] instanceof Array) { let shapes = args[0]; - if (shapes.length == 0) + if (shapes.length === 0) return; // TODO: more strict validation: @@ -54,7 +53,7 @@ export class Multiline extends LinkedList { * @returns {Box} */ get box() { - return this.edges.reduce( (acc,edge) => acc = acc.merge(edge.box), new Flatten.Box() ); + return this.edges.reduce( (acc,edge) => acc.merge(edge.box), new Flatten.Box() ); } /** diff --git a/src/data_structures/smart_intersections.js b/src/data_structures/smart_intersections.js index cfa7be60..8a88d93e 100644 --- a/src/data_structures/smart_intersections.js +++ b/src/data_structures/smart_intersections.js @@ -159,10 +159,10 @@ export function filterDuplicatedIntersections(intersections) for (let i = 1; i < intersections.int_points2_sorted.length; i++) { let int_point_cur2 = intersections.int_points2_sorted[i]; - if (int_point_cur2.id == -1) continue; + if (int_point_cur2.id === -1) continue; /* already deleted */ - if (int_point_ref2.id == -1 || /* can't be reference if already deleted */ + if (int_point_ref2.id === -1 || /* can't be reference if already deleted */ !(Utils.EQ(int_point_cur2.arc_length, int_point_ref2.arc_length))) { int_point_ref2 = int_point_cur2; int_point_ref1 = intersections.int_points1[int_point_ref2.id]; @@ -303,12 +303,12 @@ export function intPointsPoolCount(int_points, cur_int_point_num, cur_face) let int_points_pool_num = 1; - if (int_points.length == 1) return 1; + if (int_points.length === 1) return 1; int_point_current = int_points[cur_int_point_num]; for (let i = cur_int_point_num + 1; i < int_points.length; i++) { - if (int_point_current.face != cur_face) { /* next face started */ + if (int_point_current.face !== cur_face) { /* next face started */ break; } From 25b5da5bc2d1ed1eb1fc92b4099a4dea16f12c61 Mon Sep 17 00:00:00 2001 From: alexanderbol Date: Thu, 14 Mar 2024 16:18:27 +0200 Subject: [PATCH 4/6] Commit packages --- dist/main.cjs | 347 ++++++++++++++++++++++++++--------------------- dist/main.mjs | 347 ++++++++++++++++++++++++++--------------------- dist/main.umd.js | 347 ++++++++++++++++++++++++++--------------------- 3 files changed, 588 insertions(+), 453 deletions(-) diff --git a/dist/main.cjs b/dist/main.cjs index 85dce761..b1c5939c 100644 --- a/dist/main.cjs +++ b/dist/main.cjs @@ -431,7 +431,13 @@ function addToIntPoints(edge, pt, int_points) is_vertex |= END_VERTEX$1; } // Fix intersection point which is end point of the last edge - let arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + let arc_length; + if (len === Infinity) { + arc_length = shapes[0].coord(pt); + } + else { + arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + } int_points.push({ id: id, @@ -494,17 +500,17 @@ function compareFn(ip1, ip2) return 0; } -function getSortedArrayOnLine(line, int_points) { - return int_points.slice().sort( (int_point1, int_point2) => { - if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { - return -1; - } - if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { - return 1; - } - return 0; - }) -} +// export function getSortedArrayOnLine(line, int_points) { +// return int_points.slice().sort( (int_point1, int_point2) => { +// if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { +// return -1; +// } +// if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { +// return 1; +// } +// return 0; +// }) +// } function filterDuplicatedIntersections(intersections) { @@ -552,10 +558,10 @@ function filterDuplicatedIntersections(intersections) for (let i = 1; i < intersections.int_points2_sorted.length; i++) { let int_point_cur2 = intersections.int_points2_sorted[i]; - if (int_point_cur2.id == -1) continue; + if (int_point_cur2.id === -1) continue; /* already deleted */ - if (int_point_ref2.id == -1 || /* can't be reference if already deleted */ + if (int_point_ref2.id === -1 || /* can't be reference if already deleted */ !(EQ(int_point_cur2.arc_length, int_point_ref2.arc_length))) { int_point_ref2 = int_point_cur2; int_point_ref1 = intersections.int_points1[int_point_ref2.id]; @@ -696,12 +702,12 @@ function intPointsPoolCount(int_points, cur_int_point_num, cur_face) let int_points_pool_num = 1; - if (int_points.length == 1) return 1; + if (int_points.length === 1) return 1; int_point_current = int_points[cur_int_point_num]; for (let i = cur_int_point_num + 1; i < int_points.length; i++) { - if (int_point_current.face != cur_face) { /* next face started */ + if (int_point_current.face !== cur_face) { /* next face started */ break; } @@ -751,15 +757,15 @@ function splitByIntersections(polygon, int_points) } } -function insertBetweenIntPoints(int_point1, int_point2, new_edge) { - let edge_before = int_point1.edge_before; - let edge_after = int_point2.edge_after; +function insertBetweenIntPoints(int_point1, int_point2, new_edges) { + const edge_before = int_point1.edge_before; + const edge_after = int_point2.edge_after; + const len = new_edges.length; + edge_before.next = new_edges[0]; + new_edges[0].prev = edge_before; - edge_before.next = new_edge; - new_edge.prev = edge_before; - - new_edge.next = edge_after; - edge_after.prev = new_edge; + new_edges[len-1].next = edge_after; + edge_after.prev = new_edges[len-1]; } var smart_intersections = /*#__PURE__*/Object.freeze({ @@ -768,7 +774,6 @@ var smart_intersections = /*#__PURE__*/Object.freeze({ calculateInclusionFlags: calculateInclusionFlags, filterDuplicatedIntersections: filterDuplicatedIntersections, getSortedArray: getSortedArray, - getSortedArrayOnLine: getSortedArrayOnLine, initializeInclusionFlags: initializeInclusionFlags, insertBetweenIntPoints: insertBetweenIntPoints, intPointsPoolCount: intPointsPoolCount, @@ -2099,19 +2104,23 @@ function intersectArc2Box(arc, box) { } function intersectEdge2Segment(edge, segment) { - return edge.isSegment() ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); + return edge.isSegment ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); } function intersectEdge2Arc(edge, arc) { - return edge.isSegment() ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); + return edge.isSegment ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); } function intersectEdge2Line(edge, line) { - return edge.isSegment() ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); + return edge.isSegment ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); +} + +function intersectEdge2Ray(edge, ray) { + return edge.isSegment ? intersectRay2Segment(ray, edge.shape) : intersectRay2Arc(ray, edge.shape); } function intersectEdge2Circle(edge, circle) { - return edge.isSegment() ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); + return edge.isSegment ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); } function intersectSegment2Polygon(segment, polygon) { @@ -2173,11 +2182,19 @@ function intersectCircle2Polygon(circle, polygon) { } function intersectEdge2Edge(edge1, edge2) { - const shape1 = edge1.shape; - const shape2 = edge2.shape; - return edge1.isSegment() ? - (edge2.isSegment() ? intersectSegment2Segment(shape1, shape2) : intersectSegment2Arc(shape1, shape2)) : - (edge2.isSegment() ? intersectSegment2Arc(shape2, shape1) : intersectArc2Arc(shape1, shape2)); + if (edge1.isSegment) { + return intersectEdge2Segment(edge2, edge1.shape) + } + else if (edge1.isArc) { + return intersectEdge2Arc(edge2, edge1.shape) + } + else if (edge1.isLine) { + return intersectEdge2Line(edge2, edge1.shape) + } + else if (edge1.isRay) { + return intersectEdge2Ray(edge2, edge1.shape) + } + return [] } function intersectEdge2Polygon(edge, polygon) { @@ -2190,8 +2207,17 @@ function intersectEdge2Polygon(edge, polygon) { let resp_edges = polygon.edges.search(edge.shape.box); for (let resp_edge of resp_edges) { - for (let pt of intersectEdge2Edge(edge, resp_edge)) { - ip.push(pt); + if (resp_edge.isSegment) { + ip = [...ip, ...intersectSegment2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isArc) { + ip = [...ip, ...intersectArc2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isLine) { + ip = [...ip, ...intersectLine2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isRay) { + ip = [...ip, ...intersectRay2Polygon(resp_edge, polygon)]; } } @@ -2324,10 +2350,10 @@ class Multiline extends LinkedList { return; } - if (args.length == 1) { + if (args.length === 1) { if (args[0] instanceof Array) { let shapes = args[0]; - if (shapes.length == 0) + if (shapes.length === 0) return; // TODO: more strict validation: @@ -2361,7 +2387,7 @@ class Multiline extends LinkedList { * @returns {Box} */ get box() { - return this.edges.reduce( (acc,edge) => acc = acc.merge(edge.box), new Flatten.Box() ); + return this.edges.reduce( (acc,edge) => acc.merge(edge.box), new Flatten.Box() ); } /** @@ -2410,6 +2436,14 @@ class Multiline extends LinkedList { return newEdge; } + getChain(edgeFrom, edgeTo) { + let edges = []; + for (let edge = edgeFrom; edge !== edgeTo.next; edge = edge.next) { + edges.push(edge); + } + return edges + } + /** * Split edges of multiline with intersection points and return mutated multiline * @param {Point[]} ip - array of points to be added as new vertices @@ -2835,7 +2869,7 @@ function relateLine2Circle(line,circle) { denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon([circle.toArc()]).cut(multiline); + denim.E2I = new Flatten.Polygon([circle.toArc()]).cutWithLine(line); } return denim; @@ -2877,7 +2911,7 @@ function relateLine2Box(line, box) { denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon(box.toSegments()).cut(multiline); + denim.E2I = new Flatten.Polygon(box.toSegments()).cutWithLine(line); } } return denim; @@ -2897,7 +2931,7 @@ function relateLine2Polygon(line, polygon) { denim.I2B = [...multiline].slice(1).map( (edge) => edge.bv === Flatten.BOUNDARY ? edge.shape : edge.shape.start ); denim.I2E = [...multiline].filter(edge => edge.bv === Flatten.OUTSIDE).map(edge => edge.shape); - denim.E2I = polygon.cut(multiline); + denim.E2I = polygon.cutWithLine(line); return denim; } @@ -5275,7 +5309,7 @@ let Line$1 = class Line extends Shape { */ split(pt) { if (pt instanceof Flatten.Point) { - return [new Flatten.Ray(pt, this.norm.invert()), new Flatten.Ray(pt, this.norm)] + return [new Flatten.Ray(pt, this.norm), new Flatten.Ray(pt, this.norm)] } else { let multiline = new Flatten.Multiline([this]); @@ -6500,14 +6534,22 @@ class Edge { return this.shape.box; } - isSegment() { + get isSegment() { return this.shape instanceof Flatten.Segment; } - isArc() { + get isArc() { return this.shape instanceof Flatten.Arc; } + get isLine() { + return this.shape instanceof Flatten.Line; + } + + get isRay() { + return this.shape instanceof Flatten.Ray + } + /** * Get middle point of the edge * @returns {Point} @@ -7302,6 +7344,18 @@ class Ray extends Shape { return Flatten.Utils.EQ_0(this.norm.dot(vec)) && Flatten.Utils.GE(vec.cross(this.norm),0); } + /** + * Return coordinate of the point that lies on the ray in the transformed + * coordinate system where center is the projection of the point(0,0) to + * the line containing this ray and axe y is collinear to the normal vector.
+ * This method assumes that point lies on the ray + * @param {Point} pt - point on a ray + * @returns {number} + */ + coord(pt) { + return vector$1(pt.x, pt.y).cross(this.norm); + } + /** * Split ray with point and return array of segment and new ray * @param {Point} pt @@ -7686,33 +7740,106 @@ class Polygon { } /** - * Cut polygon with multiline and return array of new polygons - * Multiline should be constructed from a line with intersection point, see notebook: - * https://next.observablehq.com/@alexbol99/cut-polygon-with-line + * Cut polygon with multiline and return a new polygon * @param {Multiline} multiline - * @returns {Polygon[]} + * @returns {Polygon} */ cut(multiline) { - let cutPolygons = [this.clone()]; - for (let edge of multiline) { - if (edge.setInclusion(this) !== INSIDE$2) - continue; + let newPoly = this.clone(); - let cut_edge_start = edge.shape.start; - let cut_edge_end = edge.shape.end; + // smart intersections + let intersections = { + int_points1: [], + int_points2: [], + int_points1_sorted: [], + int_points2_sorted: [] + }; - let newCutPolygons = []; - for (let polygon of cutPolygons) { - if (polygon.findEdgeByPoint(cut_edge_start) === undefined) { - newCutPolygons.push(polygon); - } else { - let [cutPoly1, cutPoly2] = polygon.cutFace(cut_edge_start, cut_edge_end); - newCutPolygons.push(cutPoly1, cutPoly2); + // intersect each edge of multiline with each edge of the polygon + // and create smart intersections + for (let edge1 of multiline.edges) { + for (let edge2 of newPoly.edges) { + let ip = intersectEdge2Edge(edge1, edge2); + // for each intersection point + for (let pt of ip) { + addToIntPoints(edge1, pt, intersections.int_points1); + addToIntPoints(edge2, pt, intersections.int_points2); + } + } + } + + + // No intersections - return a copy of the original polygon + if (intersections.int_points1.length === 0) + return newPoly; + + // sort smart intersections + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // split by intersection points + splitByIntersections(multiline, intersections.int_points1_sorted); + splitByIntersections(newPoly, intersections.int_points2_sorted); + + // filter duplicated intersection points + filterDuplicatedIntersections(intersections); + + // sort intersection points again after filtering + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // initialize inclusion flags for edges of multiline incident to intersections + initializeInclusionFlags(intersections.int_points1); + + // calculate inclusion flag for edges of multiline incident to intersections + calculateInclusionFlags(intersections.int_points1, newPoly); + + // filter intersections between two edges that got same inclusion flag + for (let int_point1 of intersections.int_points1_sorted) { + if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + intersections.int_points2[int_point1.id] = -1; // to be filtered out + int_point1.id = -1; // to be filtered out + } + } + intersections.int_points1 = intersections.int_points1.filter( int_point => int_point.id >= 0); + intersections.int_points2 = intersections.int_points2.filter( int_point => int_point.id >= 0); + + // No intersections left after filtering - return a copy of the original polygon + if (intersections.int_points1.length === 0) + return newPoly; + + // sort intersection points 3d time after filtering + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // Add 2 new inner edges between intersection points + let int_point1_prev; + let int_point1_curr; + for (let i = 1; i < intersections.int_points1_sorted.length; i++) { + int_point1_curr = intersections.int_points1_sorted[i]; + int_point1_prev = intersections.int_points1_sorted[i-1]; + if (int_point1_curr.edge_before.bv === INSIDE$2) { + let edgeFrom = int_point1_prev.edge_after; + let edgeTo = int_point1_curr.edge_before; + let newEdges = multiline.getChain(edgeFrom, edgeTo); + insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); + + newEdges = newEdges.reverse().map(edge => new Flatten.Edge(edge.shape.reverse())); + for (let k=0; k < newEdges.length-1; k++) { + newEdges[k].next = newEdges[k+1]; + newEdges[k+1].prev = newEdges[k]; } + insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); } - cutPolygons = newCutPolygons; + } - return cutPolygons; + + // Recreate faces + newPoly.recreateFaces(); + + return newPoly } /** @@ -7769,96 +7896,14 @@ class Polygon { } /** - * Return a result of cutting polygon with line + * A special case of cut() function + * The return is a polygon cut with line * @param {Line} line - cutting line * @returns {Polygon} newPoly - resulted polygon */ cutWithLine(line) { - let newPoly = this.clone(); - let multiline = new Multiline([line]); - - // smart intersections - let intersections = { - int_points1: [], - int_points2: [], - int_points1_sorted: [], - int_points2_sorted: [] - }; - - // intersect line with each edge of the polygon - // and create smart intersections - for (let edge of newPoly.edges) { - let ip = intersectEdge2Line(edge, line); - // for each intersection point - for (let pt of ip) { - addToIntPoints(multiline.first, pt, intersections.int_points1); - addToIntPoints(edge, pt, intersections.int_points2); - } - } - - // No intersections - return a copy of the original polygon - if (intersections.int_points1.length === 0) - return newPoly; - - // sort smart intersections - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // split by intersection points - splitByIntersections(multiline, intersections.int_points1_sorted); - splitByIntersections(newPoly, intersections.int_points2_sorted); - - // filter duplicated intersection points - filterDuplicatedIntersections(intersections); - - // sort intersection points again after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // initialize inclusion flags for edges of multiline incident to intersections - initializeInclusionFlags(intersections.int_points1); - - // calculate inclusion flag for edges of multiline incident to intersections - calculateInclusionFlags(intersections.int_points1, newPoly); - - // filter intersections between two edges that got same inclusion flag - for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { - intersections.int_points2[int_point1.id] = -1; // to be filtered out - int_point1.id = -1; // to be filtered out - } - } - intersections.int_points1 = intersections.int_points1.filter( int_point => int_point.id >= 0); - intersections.int_points2 = intersections.int_points2.filter( int_point => int_point.id >= 0); - - // No intersections left after filtering - return a copy of the original polygon - if (intersections.int_points1.length === 0) - return newPoly; - - // sort intersection points 3d time after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // Add 2 new inner edges between intersection points - let int_point1_prev = intersections.int_points1[0]; - let new_edge; - for (let int_point1_curr of intersections.int_points1_sorted) { - if (int_point1_curr.edge_before.bv === INSIDE$2) { - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_prev.pt, int_point1_curr.pt)); // (int_point1_curr.edge_before.shape); - insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], new_edge); - newPoly.edges.add(new_edge); - - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_curr.pt, int_point1_prev.pt)); // (int_point1_curr.edge_before.shape.reverse()); - insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], new_edge); - newPoly.edges.add(new_edge); - } - int_point1_prev = int_point1_curr; - } - - // Recreate faces - newPoly.recreateFaces(); - return newPoly; + return this.cut(multiline); } /** @@ -7878,8 +7923,8 @@ class Polygon { } /** - * Split polygon into array of polygons, where each polygon is an island with all - * hole that it contains + * Split polygon into array of polygons, where each polygon is an outer face with all + * containing inner faces * @returns {Flatten.Polygon[]} */ splitToIslands() { diff --git a/dist/main.mjs b/dist/main.mjs index 445c38cf..142b05ed 100644 --- a/dist/main.mjs +++ b/dist/main.mjs @@ -427,7 +427,13 @@ function addToIntPoints(edge, pt, int_points) is_vertex |= END_VERTEX$1; } // Fix intersection point which is end point of the last edge - let arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + let arc_length; + if (len === Infinity) { + arc_length = shapes[0].coord(pt); + } + else { + arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + } int_points.push({ id: id, @@ -490,17 +496,17 @@ function compareFn(ip1, ip2) return 0; } -function getSortedArrayOnLine(line, int_points) { - return int_points.slice().sort( (int_point1, int_point2) => { - if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { - return -1; - } - if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { - return 1; - } - return 0; - }) -} +// export function getSortedArrayOnLine(line, int_points) { +// return int_points.slice().sort( (int_point1, int_point2) => { +// if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { +// return -1; +// } +// if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { +// return 1; +// } +// return 0; +// }) +// } function filterDuplicatedIntersections(intersections) { @@ -548,10 +554,10 @@ function filterDuplicatedIntersections(intersections) for (let i = 1; i < intersections.int_points2_sorted.length; i++) { let int_point_cur2 = intersections.int_points2_sorted[i]; - if (int_point_cur2.id == -1) continue; + if (int_point_cur2.id === -1) continue; /* already deleted */ - if (int_point_ref2.id == -1 || /* can't be reference if already deleted */ + if (int_point_ref2.id === -1 || /* can't be reference if already deleted */ !(EQ(int_point_cur2.arc_length, int_point_ref2.arc_length))) { int_point_ref2 = int_point_cur2; int_point_ref1 = intersections.int_points1[int_point_ref2.id]; @@ -692,12 +698,12 @@ function intPointsPoolCount(int_points, cur_int_point_num, cur_face) let int_points_pool_num = 1; - if (int_points.length == 1) return 1; + if (int_points.length === 1) return 1; int_point_current = int_points[cur_int_point_num]; for (let i = cur_int_point_num + 1; i < int_points.length; i++) { - if (int_point_current.face != cur_face) { /* next face started */ + if (int_point_current.face !== cur_face) { /* next face started */ break; } @@ -747,15 +753,15 @@ function splitByIntersections(polygon, int_points) } } -function insertBetweenIntPoints(int_point1, int_point2, new_edge) { - let edge_before = int_point1.edge_before; - let edge_after = int_point2.edge_after; +function insertBetweenIntPoints(int_point1, int_point2, new_edges) { + const edge_before = int_point1.edge_before; + const edge_after = int_point2.edge_after; + const len = new_edges.length; + edge_before.next = new_edges[0]; + new_edges[0].prev = edge_before; - edge_before.next = new_edge; - new_edge.prev = edge_before; - - new_edge.next = edge_after; - edge_after.prev = new_edge; + new_edges[len-1].next = edge_after; + edge_after.prev = new_edges[len-1]; } var smart_intersections = /*#__PURE__*/Object.freeze({ @@ -764,7 +770,6 @@ var smart_intersections = /*#__PURE__*/Object.freeze({ calculateInclusionFlags: calculateInclusionFlags, filterDuplicatedIntersections: filterDuplicatedIntersections, getSortedArray: getSortedArray, - getSortedArrayOnLine: getSortedArrayOnLine, initializeInclusionFlags: initializeInclusionFlags, insertBetweenIntPoints: insertBetweenIntPoints, intPointsPoolCount: intPointsPoolCount, @@ -2095,19 +2100,23 @@ function intersectArc2Box(arc, box) { } function intersectEdge2Segment(edge, segment) { - return edge.isSegment() ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); + return edge.isSegment ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); } function intersectEdge2Arc(edge, arc) { - return edge.isSegment() ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); + return edge.isSegment ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); } function intersectEdge2Line(edge, line) { - return edge.isSegment() ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); + return edge.isSegment ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); +} + +function intersectEdge2Ray(edge, ray) { + return edge.isSegment ? intersectRay2Segment(ray, edge.shape) : intersectRay2Arc(ray, edge.shape); } function intersectEdge2Circle(edge, circle) { - return edge.isSegment() ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); + return edge.isSegment ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); } function intersectSegment2Polygon(segment, polygon) { @@ -2169,11 +2178,19 @@ function intersectCircle2Polygon(circle, polygon) { } function intersectEdge2Edge(edge1, edge2) { - const shape1 = edge1.shape; - const shape2 = edge2.shape; - return edge1.isSegment() ? - (edge2.isSegment() ? intersectSegment2Segment(shape1, shape2) : intersectSegment2Arc(shape1, shape2)) : - (edge2.isSegment() ? intersectSegment2Arc(shape2, shape1) : intersectArc2Arc(shape1, shape2)); + if (edge1.isSegment) { + return intersectEdge2Segment(edge2, edge1.shape) + } + else if (edge1.isArc) { + return intersectEdge2Arc(edge2, edge1.shape) + } + else if (edge1.isLine) { + return intersectEdge2Line(edge2, edge1.shape) + } + else if (edge1.isRay) { + return intersectEdge2Ray(edge2, edge1.shape) + } + return [] } function intersectEdge2Polygon(edge, polygon) { @@ -2186,8 +2203,17 @@ function intersectEdge2Polygon(edge, polygon) { let resp_edges = polygon.edges.search(edge.shape.box); for (let resp_edge of resp_edges) { - for (let pt of intersectEdge2Edge(edge, resp_edge)) { - ip.push(pt); + if (resp_edge.isSegment) { + ip = [...ip, ...intersectSegment2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isArc) { + ip = [...ip, ...intersectArc2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isLine) { + ip = [...ip, ...intersectLine2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isRay) { + ip = [...ip, ...intersectRay2Polygon(resp_edge, polygon)]; } } @@ -2320,10 +2346,10 @@ class Multiline extends LinkedList { return; } - if (args.length == 1) { + if (args.length === 1) { if (args[0] instanceof Array) { let shapes = args[0]; - if (shapes.length == 0) + if (shapes.length === 0) return; // TODO: more strict validation: @@ -2357,7 +2383,7 @@ class Multiline extends LinkedList { * @returns {Box} */ get box() { - return this.edges.reduce( (acc,edge) => acc = acc.merge(edge.box), new Flatten.Box() ); + return this.edges.reduce( (acc,edge) => acc.merge(edge.box), new Flatten.Box() ); } /** @@ -2406,6 +2432,14 @@ class Multiline extends LinkedList { return newEdge; } + getChain(edgeFrom, edgeTo) { + let edges = []; + for (let edge = edgeFrom; edge !== edgeTo.next; edge = edge.next) { + edges.push(edge); + } + return edges + } + /** * Split edges of multiline with intersection points and return mutated multiline * @param {Point[]} ip - array of points to be added as new vertices @@ -2831,7 +2865,7 @@ function relateLine2Circle(line,circle) { denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon([circle.toArc()]).cut(multiline); + denim.E2I = new Flatten.Polygon([circle.toArc()]).cutWithLine(line); } return denim; @@ -2873,7 +2907,7 @@ function relateLine2Box(line, box) { denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon(box.toSegments()).cut(multiline); + denim.E2I = new Flatten.Polygon(box.toSegments()).cutWithLine(line); } } return denim; @@ -2893,7 +2927,7 @@ function relateLine2Polygon(line, polygon) { denim.I2B = [...multiline].slice(1).map( (edge) => edge.bv === Flatten.BOUNDARY ? edge.shape : edge.shape.start ); denim.I2E = [...multiline].filter(edge => edge.bv === Flatten.OUTSIDE).map(edge => edge.shape); - denim.E2I = polygon.cut(multiline); + denim.E2I = polygon.cutWithLine(line); return denim; } @@ -5271,7 +5305,7 @@ let Line$1 = class Line extends Shape { */ split(pt) { if (pt instanceof Flatten.Point) { - return [new Flatten.Ray(pt, this.norm.invert()), new Flatten.Ray(pt, this.norm)] + return [new Flatten.Ray(pt, this.norm), new Flatten.Ray(pt, this.norm)] } else { let multiline = new Flatten.Multiline([this]); @@ -6496,14 +6530,22 @@ class Edge { return this.shape.box; } - isSegment() { + get isSegment() { return this.shape instanceof Flatten.Segment; } - isArc() { + get isArc() { return this.shape instanceof Flatten.Arc; } + get isLine() { + return this.shape instanceof Flatten.Line; + } + + get isRay() { + return this.shape instanceof Flatten.Ray + } + /** * Get middle point of the edge * @returns {Point} @@ -7298,6 +7340,18 @@ class Ray extends Shape { return Flatten.Utils.EQ_0(this.norm.dot(vec)) && Flatten.Utils.GE(vec.cross(this.norm),0); } + /** + * Return coordinate of the point that lies on the ray in the transformed + * coordinate system where center is the projection of the point(0,0) to + * the line containing this ray and axe y is collinear to the normal vector.
+ * This method assumes that point lies on the ray + * @param {Point} pt - point on a ray + * @returns {number} + */ + coord(pt) { + return vector$1(pt.x, pt.y).cross(this.norm); + } + /** * Split ray with point and return array of segment and new ray * @param {Point} pt @@ -7682,33 +7736,106 @@ class Polygon { } /** - * Cut polygon with multiline and return array of new polygons - * Multiline should be constructed from a line with intersection point, see notebook: - * https://next.observablehq.com/@alexbol99/cut-polygon-with-line + * Cut polygon with multiline and return a new polygon * @param {Multiline} multiline - * @returns {Polygon[]} + * @returns {Polygon} */ cut(multiline) { - let cutPolygons = [this.clone()]; - for (let edge of multiline) { - if (edge.setInclusion(this) !== INSIDE$2) - continue; + let newPoly = this.clone(); - let cut_edge_start = edge.shape.start; - let cut_edge_end = edge.shape.end; + // smart intersections + let intersections = { + int_points1: [], + int_points2: [], + int_points1_sorted: [], + int_points2_sorted: [] + }; - let newCutPolygons = []; - for (let polygon of cutPolygons) { - if (polygon.findEdgeByPoint(cut_edge_start) === undefined) { - newCutPolygons.push(polygon); - } else { - let [cutPoly1, cutPoly2] = polygon.cutFace(cut_edge_start, cut_edge_end); - newCutPolygons.push(cutPoly1, cutPoly2); + // intersect each edge of multiline with each edge of the polygon + // and create smart intersections + for (let edge1 of multiline.edges) { + for (let edge2 of newPoly.edges) { + let ip = intersectEdge2Edge(edge1, edge2); + // for each intersection point + for (let pt of ip) { + addToIntPoints(edge1, pt, intersections.int_points1); + addToIntPoints(edge2, pt, intersections.int_points2); + } + } + } + + + // No intersections - return a copy of the original polygon + if (intersections.int_points1.length === 0) + return newPoly; + + // sort smart intersections + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // split by intersection points + splitByIntersections(multiline, intersections.int_points1_sorted); + splitByIntersections(newPoly, intersections.int_points2_sorted); + + // filter duplicated intersection points + filterDuplicatedIntersections(intersections); + + // sort intersection points again after filtering + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // initialize inclusion flags for edges of multiline incident to intersections + initializeInclusionFlags(intersections.int_points1); + + // calculate inclusion flag for edges of multiline incident to intersections + calculateInclusionFlags(intersections.int_points1, newPoly); + + // filter intersections between two edges that got same inclusion flag + for (let int_point1 of intersections.int_points1_sorted) { + if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + intersections.int_points2[int_point1.id] = -1; // to be filtered out + int_point1.id = -1; // to be filtered out + } + } + intersections.int_points1 = intersections.int_points1.filter( int_point => int_point.id >= 0); + intersections.int_points2 = intersections.int_points2.filter( int_point => int_point.id >= 0); + + // No intersections left after filtering - return a copy of the original polygon + if (intersections.int_points1.length === 0) + return newPoly; + + // sort intersection points 3d time after filtering + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // Add 2 new inner edges between intersection points + let int_point1_prev; + let int_point1_curr; + for (let i = 1; i < intersections.int_points1_sorted.length; i++) { + int_point1_curr = intersections.int_points1_sorted[i]; + int_point1_prev = intersections.int_points1_sorted[i-1]; + if (int_point1_curr.edge_before.bv === INSIDE$2) { + let edgeFrom = int_point1_prev.edge_after; + let edgeTo = int_point1_curr.edge_before; + let newEdges = multiline.getChain(edgeFrom, edgeTo); + insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); + + newEdges = newEdges.reverse().map(edge => new Flatten.Edge(edge.shape.reverse())); + for (let k=0; k < newEdges.length-1; k++) { + newEdges[k].next = newEdges[k+1]; + newEdges[k+1].prev = newEdges[k]; } + insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); } - cutPolygons = newCutPolygons; + } - return cutPolygons; + + // Recreate faces + newPoly.recreateFaces(); + + return newPoly } /** @@ -7765,96 +7892,14 @@ class Polygon { } /** - * Return a result of cutting polygon with line + * A special case of cut() function + * The return is a polygon cut with line * @param {Line} line - cutting line * @returns {Polygon} newPoly - resulted polygon */ cutWithLine(line) { - let newPoly = this.clone(); - let multiline = new Multiline([line]); - - // smart intersections - let intersections = { - int_points1: [], - int_points2: [], - int_points1_sorted: [], - int_points2_sorted: [] - }; - - // intersect line with each edge of the polygon - // and create smart intersections - for (let edge of newPoly.edges) { - let ip = intersectEdge2Line(edge, line); - // for each intersection point - for (let pt of ip) { - addToIntPoints(multiline.first, pt, intersections.int_points1); - addToIntPoints(edge, pt, intersections.int_points2); - } - } - - // No intersections - return a copy of the original polygon - if (intersections.int_points1.length === 0) - return newPoly; - - // sort smart intersections - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // split by intersection points - splitByIntersections(multiline, intersections.int_points1_sorted); - splitByIntersections(newPoly, intersections.int_points2_sorted); - - // filter duplicated intersection points - filterDuplicatedIntersections(intersections); - - // sort intersection points again after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // initialize inclusion flags for edges of multiline incident to intersections - initializeInclusionFlags(intersections.int_points1); - - // calculate inclusion flag for edges of multiline incident to intersections - calculateInclusionFlags(intersections.int_points1, newPoly); - - // filter intersections between two edges that got same inclusion flag - for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { - intersections.int_points2[int_point1.id] = -1; // to be filtered out - int_point1.id = -1; // to be filtered out - } - } - intersections.int_points1 = intersections.int_points1.filter( int_point => int_point.id >= 0); - intersections.int_points2 = intersections.int_points2.filter( int_point => int_point.id >= 0); - - // No intersections left after filtering - return a copy of the original polygon - if (intersections.int_points1.length === 0) - return newPoly; - - // sort intersection points 3d time after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // Add 2 new inner edges between intersection points - let int_point1_prev = intersections.int_points1[0]; - let new_edge; - for (let int_point1_curr of intersections.int_points1_sorted) { - if (int_point1_curr.edge_before.bv === INSIDE$2) { - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_prev.pt, int_point1_curr.pt)); // (int_point1_curr.edge_before.shape); - insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], new_edge); - newPoly.edges.add(new_edge); - - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_curr.pt, int_point1_prev.pt)); // (int_point1_curr.edge_before.shape.reverse()); - insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], new_edge); - newPoly.edges.add(new_edge); - } - int_point1_prev = int_point1_curr; - } - - // Recreate faces - newPoly.recreateFaces(); - return newPoly; + return this.cut(multiline); } /** @@ -7874,8 +7919,8 @@ class Polygon { } /** - * Split polygon into array of polygons, where each polygon is an island with all - * hole that it contains + * Split polygon into array of polygons, where each polygon is an outer face with all + * containing inner faces * @returns {Flatten.Polygon[]} */ splitToIslands() { diff --git a/dist/main.umd.js b/dist/main.umd.js index 412b61dd..bbad2800 100644 --- a/dist/main.umd.js +++ b/dist/main.umd.js @@ -433,7 +433,13 @@ is_vertex |= END_VERTEX$1; } // Fix intersection point which is end point of the last edge - let arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + let arc_length; + if (len === Infinity) { + arc_length = shapes[0].coord(pt); + } + else { + arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + } int_points.push({ id: id, @@ -496,17 +502,17 @@ return 0; } - function getSortedArrayOnLine(line, int_points) { - return int_points.slice().sort( (int_point1, int_point2) => { - if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { - return -1; - } - if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { - return 1; - } - return 0; - }) - } + // export function getSortedArrayOnLine(line, int_points) { + // return int_points.slice().sort( (int_point1, int_point2) => { + // if (line.coord(int_point1.pt) < line.coord(int_point2.pt)) { + // return -1; + // } + // if (line.coord(int_point1.pt) > line.coord(int_point2.pt)) { + // return 1; + // } + // return 0; + // }) + // } function filterDuplicatedIntersections(intersections) { @@ -554,10 +560,10 @@ for (let i = 1; i < intersections.int_points2_sorted.length; i++) { let int_point_cur2 = intersections.int_points2_sorted[i]; - if (int_point_cur2.id == -1) continue; + if (int_point_cur2.id === -1) continue; /* already deleted */ - if (int_point_ref2.id == -1 || /* can't be reference if already deleted */ + if (int_point_ref2.id === -1 || /* can't be reference if already deleted */ !(EQ(int_point_cur2.arc_length, int_point_ref2.arc_length))) { int_point_ref2 = int_point_cur2; int_point_ref1 = intersections.int_points1[int_point_ref2.id]; @@ -698,12 +704,12 @@ let int_points_pool_num = 1; - if (int_points.length == 1) return 1; + if (int_points.length === 1) return 1; int_point_current = int_points[cur_int_point_num]; for (let i = cur_int_point_num + 1; i < int_points.length; i++) { - if (int_point_current.face != cur_face) { /* next face started */ + if (int_point_current.face !== cur_face) { /* next face started */ break; } @@ -753,15 +759,15 @@ } } - function insertBetweenIntPoints(int_point1, int_point2, new_edge) { - let edge_before = int_point1.edge_before; - let edge_after = int_point2.edge_after; + function insertBetweenIntPoints(int_point1, int_point2, new_edges) { + const edge_before = int_point1.edge_before; + const edge_after = int_point2.edge_after; + const len = new_edges.length; + edge_before.next = new_edges[0]; + new_edges[0].prev = edge_before; - edge_before.next = new_edge; - new_edge.prev = edge_before; - - new_edge.next = edge_after; - edge_after.prev = new_edge; + new_edges[len-1].next = edge_after; + edge_after.prev = new_edges[len-1]; } var smart_intersections = /*#__PURE__*/Object.freeze({ @@ -770,7 +776,6 @@ calculateInclusionFlags: calculateInclusionFlags, filterDuplicatedIntersections: filterDuplicatedIntersections, getSortedArray: getSortedArray, - getSortedArrayOnLine: getSortedArrayOnLine, initializeInclusionFlags: initializeInclusionFlags, insertBetweenIntPoints: insertBetweenIntPoints, intPointsPoolCount: intPointsPoolCount, @@ -2101,19 +2106,23 @@ } function intersectEdge2Segment(edge, segment) { - return edge.isSegment() ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); + return edge.isSegment ? intersectSegment2Segment(edge.shape, segment) : intersectSegment2Arc(segment, edge.shape); } function intersectEdge2Arc(edge, arc) { - return edge.isSegment() ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); + return edge.isSegment ? intersectSegment2Arc(edge.shape, arc) : intersectArc2Arc(edge.shape, arc); } function intersectEdge2Line(edge, line) { - return edge.isSegment() ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); + return edge.isSegment ? intersectSegment2Line(edge.shape, line) : intersectLine2Arc(line, edge.shape); + } + + function intersectEdge2Ray(edge, ray) { + return edge.isSegment ? intersectRay2Segment(ray, edge.shape) : intersectRay2Arc(ray, edge.shape); } function intersectEdge2Circle(edge, circle) { - return edge.isSegment() ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); + return edge.isSegment ? intersectSegment2Circle(edge.shape, circle) : intersectArc2Circle(edge.shape, circle); } function intersectSegment2Polygon(segment, polygon) { @@ -2175,11 +2184,19 @@ } function intersectEdge2Edge(edge1, edge2) { - const shape1 = edge1.shape; - const shape2 = edge2.shape; - return edge1.isSegment() ? - (edge2.isSegment() ? intersectSegment2Segment(shape1, shape2) : intersectSegment2Arc(shape1, shape2)) : - (edge2.isSegment() ? intersectSegment2Arc(shape2, shape1) : intersectArc2Arc(shape1, shape2)); + if (edge1.isSegment) { + return intersectEdge2Segment(edge2, edge1.shape) + } + else if (edge1.isArc) { + return intersectEdge2Arc(edge2, edge1.shape) + } + else if (edge1.isLine) { + return intersectEdge2Line(edge2, edge1.shape) + } + else if (edge1.isRay) { + return intersectEdge2Ray(edge2, edge1.shape) + } + return [] } function intersectEdge2Polygon(edge, polygon) { @@ -2192,8 +2209,17 @@ let resp_edges = polygon.edges.search(edge.shape.box); for (let resp_edge of resp_edges) { - for (let pt of intersectEdge2Edge(edge, resp_edge)) { - ip.push(pt); + if (resp_edge.isSegment) { + ip = [...ip, ...intersectSegment2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isArc) { + ip = [...ip, ...intersectArc2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isLine) { + ip = [...ip, ...intersectLine2Polygon(resp_edge, polygon)]; + } + else if (resp_edge.isRay) { + ip = [...ip, ...intersectRay2Polygon(resp_edge, polygon)]; } } @@ -2326,10 +2352,10 @@ return; } - if (args.length == 1) { + if (args.length === 1) { if (args[0] instanceof Array) { let shapes = args[0]; - if (shapes.length == 0) + if (shapes.length === 0) return; // TODO: more strict validation: @@ -2363,7 +2389,7 @@ * @returns {Box} */ get box() { - return this.edges.reduce( (acc,edge) => acc = acc.merge(edge.box), new Flatten.Box() ); + return this.edges.reduce( (acc,edge) => acc.merge(edge.box), new Flatten.Box() ); } /** @@ -2412,6 +2438,14 @@ return newEdge; } + getChain(edgeFrom, edgeTo) { + let edges = []; + for (let edge = edgeFrom; edge !== edgeTo.next; edge = edge.next) { + edges.push(edge); + } + return edges + } + /** * Split edges of multiline with intersection points and return mutated multiline * @param {Point[]} ip - array of points to be added as new vertices @@ -2837,7 +2871,7 @@ denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon([circle.toArc()]).cut(multiline); + denim.E2I = new Flatten.Polygon([circle.toArc()]).cutWithLine(line); } return denim; @@ -2879,7 +2913,7 @@ denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon(box.toSegments()).cut(multiline); + denim.E2I = new Flatten.Polygon(box.toSegments()).cutWithLine(line); } } return denim; @@ -2899,7 +2933,7 @@ denim.I2B = [...multiline].slice(1).map( (edge) => edge.bv === Flatten.BOUNDARY ? edge.shape : edge.shape.start ); denim.I2E = [...multiline].filter(edge => edge.bv === Flatten.OUTSIDE).map(edge => edge.shape); - denim.E2I = polygon.cut(multiline); + denim.E2I = polygon.cutWithLine(line); return denim; } @@ -5277,7 +5311,7 @@ */ split(pt) { if (pt instanceof Flatten.Point) { - return [new Flatten.Ray(pt, this.norm.invert()), new Flatten.Ray(pt, this.norm)] + return [new Flatten.Ray(pt, this.norm), new Flatten.Ray(pt, this.norm)] } else { let multiline = new Flatten.Multiline([this]); @@ -6502,14 +6536,22 @@ return this.shape.box; } - isSegment() { + get isSegment() { return this.shape instanceof Flatten.Segment; } - isArc() { + get isArc() { return this.shape instanceof Flatten.Arc; } + get isLine() { + return this.shape instanceof Flatten.Line; + } + + get isRay() { + return this.shape instanceof Flatten.Ray + } + /** * Get middle point of the edge * @returns {Point} @@ -7304,6 +7346,18 @@ return Flatten.Utils.EQ_0(this.norm.dot(vec)) && Flatten.Utils.GE(vec.cross(this.norm),0); } + /** + * Return coordinate of the point that lies on the ray in the transformed + * coordinate system where center is the projection of the point(0,0) to + * the line containing this ray and axe y is collinear to the normal vector.
+ * This method assumes that point lies on the ray + * @param {Point} pt - point on a ray + * @returns {number} + */ + coord(pt) { + return vector$1(pt.x, pt.y).cross(this.norm); + } + /** * Split ray with point and return array of segment and new ray * @param {Point} pt @@ -7688,33 +7742,106 @@ } /** - * Cut polygon with multiline and return array of new polygons - * Multiline should be constructed from a line with intersection point, see notebook: - * https://next.observablehq.com/@alexbol99/cut-polygon-with-line + * Cut polygon with multiline and return a new polygon * @param {Multiline} multiline - * @returns {Polygon[]} + * @returns {Polygon} */ cut(multiline) { - let cutPolygons = [this.clone()]; - for (let edge of multiline) { - if (edge.setInclusion(this) !== INSIDE$2) - continue; + let newPoly = this.clone(); - let cut_edge_start = edge.shape.start; - let cut_edge_end = edge.shape.end; + // smart intersections + let intersections = { + int_points1: [], + int_points2: [], + int_points1_sorted: [], + int_points2_sorted: [] + }; - let newCutPolygons = []; - for (let polygon of cutPolygons) { - if (polygon.findEdgeByPoint(cut_edge_start) === undefined) { - newCutPolygons.push(polygon); - } else { - let [cutPoly1, cutPoly2] = polygon.cutFace(cut_edge_start, cut_edge_end); - newCutPolygons.push(cutPoly1, cutPoly2); + // intersect each edge of multiline with each edge of the polygon + // and create smart intersections + for (let edge1 of multiline.edges) { + for (let edge2 of newPoly.edges) { + let ip = intersectEdge2Edge(edge1, edge2); + // for each intersection point + for (let pt of ip) { + addToIntPoints(edge1, pt, intersections.int_points1); + addToIntPoints(edge2, pt, intersections.int_points2); + } + } + } + + + // No intersections - return a copy of the original polygon + if (intersections.int_points1.length === 0) + return newPoly; + + // sort smart intersections + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // split by intersection points + splitByIntersections(multiline, intersections.int_points1_sorted); + splitByIntersections(newPoly, intersections.int_points2_sorted); + + // filter duplicated intersection points + filterDuplicatedIntersections(intersections); + + // sort intersection points again after filtering + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // initialize inclusion flags for edges of multiline incident to intersections + initializeInclusionFlags(intersections.int_points1); + + // calculate inclusion flag for edges of multiline incident to intersections + calculateInclusionFlags(intersections.int_points1, newPoly); + + // filter intersections between two edges that got same inclusion flag + for (let int_point1 of intersections.int_points1_sorted) { + if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + intersections.int_points2[int_point1.id] = -1; // to be filtered out + int_point1.id = -1; // to be filtered out + } + } + intersections.int_points1 = intersections.int_points1.filter( int_point => int_point.id >= 0); + intersections.int_points2 = intersections.int_points2.filter( int_point => int_point.id >= 0); + + // No intersections left after filtering - return a copy of the original polygon + if (intersections.int_points1.length === 0) + return newPoly; + + // sort intersection points 3d time after filtering + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); + intersections.int_points2_sorted = getSortedArray(intersections.int_points2); + + // Add 2 new inner edges between intersection points + let int_point1_prev; + let int_point1_curr; + for (let i = 1; i < intersections.int_points1_sorted.length; i++) { + int_point1_curr = intersections.int_points1_sorted[i]; + int_point1_prev = intersections.int_points1_sorted[i-1]; + if (int_point1_curr.edge_before.bv === INSIDE$2) { + let edgeFrom = int_point1_prev.edge_after; + let edgeTo = int_point1_curr.edge_before; + let newEdges = multiline.getChain(edgeFrom, edgeTo); + insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); + + newEdges = newEdges.reverse().map(edge => new Flatten.Edge(edge.shape.reverse())); + for (let k=0; k < newEdges.length-1; k++) { + newEdges[k].next = newEdges[k+1]; + newEdges[k+1].prev = newEdges[k]; } + insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); } - cutPolygons = newCutPolygons; + } - return cutPolygons; + + // Recreate faces + newPoly.recreateFaces(); + + return newPoly } /** @@ -7771,96 +7898,14 @@ } /** - * Return a result of cutting polygon with line + * A special case of cut() function + * The return is a polygon cut with line * @param {Line} line - cutting line * @returns {Polygon} newPoly - resulted polygon */ cutWithLine(line) { - let newPoly = this.clone(); - let multiline = new Multiline([line]); - - // smart intersections - let intersections = { - int_points1: [], - int_points2: [], - int_points1_sorted: [], - int_points2_sorted: [] - }; - - // intersect line with each edge of the polygon - // and create smart intersections - for (let edge of newPoly.edges) { - let ip = intersectEdge2Line(edge, line); - // for each intersection point - for (let pt of ip) { - addToIntPoints(multiline.first, pt, intersections.int_points1); - addToIntPoints(edge, pt, intersections.int_points2); - } - } - - // No intersections - return a copy of the original polygon - if (intersections.int_points1.length === 0) - return newPoly; - - // sort smart intersections - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // split by intersection points - splitByIntersections(multiline, intersections.int_points1_sorted); - splitByIntersections(newPoly, intersections.int_points2_sorted); - - // filter duplicated intersection points - filterDuplicatedIntersections(intersections); - - // sort intersection points again after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // initialize inclusion flags for edges of multiline incident to intersections - initializeInclusionFlags(intersections.int_points1); - - // calculate inclusion flag for edges of multiline incident to intersections - calculateInclusionFlags(intersections.int_points1, newPoly); - - // filter intersections between two edges that got same inclusion flag - for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { - intersections.int_points2[int_point1.id] = -1; // to be filtered out - int_point1.id = -1; // to be filtered out - } - } - intersections.int_points1 = intersections.int_points1.filter( int_point => int_point.id >= 0); - intersections.int_points2 = intersections.int_points2.filter( int_point => int_point.id >= 0); - - // No intersections left after filtering - return a copy of the original polygon - if (intersections.int_points1.length === 0) - return newPoly; - - // sort intersection points 3d time after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); - intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - - // Add 2 new inner edges between intersection points - let int_point1_prev = intersections.int_points1[0]; - let new_edge; - for (let int_point1_curr of intersections.int_points1_sorted) { - if (int_point1_curr.edge_before.bv === INSIDE$2) { - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_prev.pt, int_point1_curr.pt)); // (int_point1_curr.edge_before.shape); - insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], new_edge); - newPoly.edges.add(new_edge); - - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_curr.pt, int_point1_prev.pt)); // (int_point1_curr.edge_before.shape.reverse()); - insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], new_edge); - newPoly.edges.add(new_edge); - } - int_point1_prev = int_point1_curr; - } - - // Recreate faces - newPoly.recreateFaces(); - return newPoly; + return this.cut(multiline); } /** @@ -7880,8 +7925,8 @@ } /** - * Split polygon into array of polygons, where each polygon is an island with all - * hole that it contains + * Split polygon into array of polygons, where each polygon is an outer face with all + * containing inner faces * @returns {Flatten.Polygon[]} */ splitToIslands() { From 4c85cec0ec19d1098dcddbf5ea8d40e096d54829 Mon Sep 17 00:00:00 2001 From: alexanderbol Date: Fri, 29 Mar 2024 17:30:10 +0300 Subject: [PATCH 5/6] Add case of finite multiline that does not start or end with ray --- dist/main.cjs | 127 +++++++++------------ dist/main.mjs | 127 +++++++++------------ dist/main.umd.js | 127 +++++++++------------ index.d.ts | 1 - src/classes/multiline.js | 20 ++++ src/classes/polygon.js | 61 +--------- src/data_structures/smart_intersections.js | 48 +++++--- test/classes/polygon.js | 21 ++++ 8 files changed, 238 insertions(+), 294 deletions(-) diff --git a/dist/main.cjs b/dist/main.cjs index b1c5939c..22de6033 100644 --- a/dist/main.cjs +++ b/dist/main.cjs @@ -436,7 +436,9 @@ function addToIntPoints(edge, pt, int_points) arc_length = shapes[0].coord(pt); } else { - arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + arc_length = (is_vertex & END_VERTEX$1) && edge.next && edge.next.arc_length === 0 ? + 0 : + edge.arc_length + len; } int_points.push({ @@ -594,28 +596,32 @@ function filterDuplicatedIntersections(intersections) function initializeInclusionFlags(int_points) { for (let int_point of int_points) { - int_point.edge_before.bvStart = undefined; - int_point.edge_before.bvEnd = undefined; - int_point.edge_before.bv = undefined; - int_point.edge_before.overlap = undefined; + if (int_point.edge_before) { + int_point.edge_before.bvStart = undefined; + int_point.edge_before.bvEnd = undefined; + int_point.edge_before.bv = undefined; + int_point.edge_before.overlap = undefined; + } - int_point.edge_after.bvStart = undefined; - int_point.edge_after.bvEnd = undefined; - int_point.edge_after.bv = undefined; - int_point.edge_after.overlap = undefined; + if (int_point.edge_after) { + int_point.edge_after.bvStart = undefined; + int_point.edge_after.bvEnd = undefined; + int_point.edge_after.bv = undefined; + int_point.edge_after.overlap = undefined; + } } for (let int_point of int_points) { - int_point.edge_before.bvEnd = BOUNDARY$1; - int_point.edge_after.bvStart = BOUNDARY$1; + if (int_point.edge_before) int_point.edge_before.bvEnd = BOUNDARY$1; + if (int_point.edge_after) int_point.edge_after.bvStart = BOUNDARY$1; } } function calculateInclusionFlags(int_points, polygon) { for (let int_point of int_points) { - int_point.edge_before.setInclusion(polygon); - int_point.edge_after.setInclusion(polygon); + if (int_point.edge_before) int_point.edge_before.setInclusion(polygon); + if (int_point.edge_after) int_point.edge_after.setInclusion(polygon); } } @@ -740,8 +746,14 @@ function splitByIntersections(polygon, int_points) } if (int_point.is_vertex & START_VERTEX$1) { // nothing to split - int_point.edge_before = edge.prev; - int_point.is_vertex = END_VERTEX$1; + if (edge.prev) { + int_point.edge_before = edge.prev; // polygon + int_point.is_vertex = END_VERTEX$1; + } + else { // multiline start vertex + int_point.edge_after = int_point.edge_before; + int_point.edge_before = edge.prev; + } continue; } if (int_point.is_vertex & END_VERTEX$1) { // nothing to split @@ -753,7 +765,9 @@ function splitByIntersections(polygon, int_points) } for (let int_point of int_points) { - int_point.edge_after = int_point.edge_before.next; + if (int_point.edge_before) { + int_point.edge_after = int_point.edge_before.next; + } } } @@ -2370,6 +2384,8 @@ class Multiline extends LinkedList { let edge = new Flatten.Edge(shape); this.append(edge); } + + this.setArcLength(); } } } @@ -2408,6 +2424,24 @@ class Multiline extends LinkedList { return new Multiline(this.toShapes()); } + /** + * Set arc_length property for each of the edges in the face. + * Arc_length of the edge it the arc length from the first edge of the face + */ + setArcLength() { + for (let edge of this) { + this.setOneEdgeArcLength(edge); + } + } + + setOneEdgeArcLength(edge) { + if (edge === this.first) { + edge.arc_length = 0.0; + } else { + edge.arc_length = edge.prev.arc_length + edge.prev.length; + } + } + /** * Split edge and add new vertex, return new edge inserted * @param {Point} pt - point on edge that will be added as new vertex @@ -7768,7 +7802,6 @@ class Polygon { } } - // No intersections - return a copy of the original polygon if (intersections.int_points1.length === 0) return newPoly; @@ -7796,7 +7829,8 @@ class Polygon { // filter intersections between two edges that got same inclusion flag for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + if (int_point1.edge_before && int_point1.edge_after && + int_point1.edge_before.bv === int_point1.edge_after.bv) { intersections.int_points2[int_point1.id] = -1; // to be filtered out int_point1.id = -1; // to be filtered out } @@ -7812,13 +7846,13 @@ class Polygon { intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - // Add 2 new inner edges between intersection points + // Add new inner edges between intersection points let int_point1_prev; let int_point1_curr; for (let i = 1; i < intersections.int_points1_sorted.length; i++) { int_point1_curr = intersections.int_points1_sorted[i]; int_point1_prev = intersections.int_points1_sorted[i-1]; - if (int_point1_curr.edge_before.bv === INSIDE$2) { + if (int_point1_curr.edge_before && int_point1_curr.edge_before.bv === INSIDE$2) { let edgeFrom = int_point1_prev.edge_after; let edgeTo = int_point1_curr.edge_before; let newEdges = multiline.getChain(edgeFrom, edgeTo); @@ -7842,59 +7876,6 @@ class Polygon { return newPoly } - /** - * Cut face of polygon with a segment between two points and create two new polygons - * Supposed that a segments between points does not intersect any other edge - * @param {Point} pt1 - * @param {Point} pt2 - * @returns {Polygon[]} - */ - cutFace(pt1, pt2) { - let edge1 = this.findEdgeByPoint(pt1); - let edge2 = this.findEdgeByPoint(pt2); - if (edge1.face !== edge2.face) - return []; - - // Cut face into two and create new polygon with two faces - let edgeBefore1 = this.addVertex(pt1, edge1); - edge2 = this.findEdgeByPoint(pt2); - let edgeBefore2 = this.addVertex(pt2, edge2); - - let face = edgeBefore1.face; - let newEdge1 = new Flatten.Edge( - new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) - ); - let newEdge2 = new Flatten.Edge( - new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) - ); - - // Swap links - edgeBefore1.next.prev = newEdge2; - newEdge2.next = edgeBefore1.next; - - edgeBefore1.next = newEdge1; - newEdge1.prev = edgeBefore1; - - edgeBefore2.next.prev = newEdge1; - newEdge1.next = edgeBefore2.next; - - edgeBefore2.next = newEdge2; - newEdge2.prev = edgeBefore2; - - // Insert new edge to the edges container and 2d index - this.edges.add(newEdge1); - this.edges.add(newEdge2); - - // Add two new faces - let face1 = this.addFace(newEdge1, edgeBefore1); - let face2 = this.addFace(newEdge2, edgeBefore2); - - // Remove old face - this.faces.delete(face); - - return [face1.toPolygon(), face2.toPolygon()]; - } - /** * A special case of cut() function * The return is a polygon cut with line diff --git a/dist/main.mjs b/dist/main.mjs index 142b05ed..f665b360 100644 --- a/dist/main.mjs +++ b/dist/main.mjs @@ -432,7 +432,9 @@ function addToIntPoints(edge, pt, int_points) arc_length = shapes[0].coord(pt); } else { - arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + arc_length = (is_vertex & END_VERTEX$1) && edge.next && edge.next.arc_length === 0 ? + 0 : + edge.arc_length + len; } int_points.push({ @@ -590,28 +592,32 @@ function filterDuplicatedIntersections(intersections) function initializeInclusionFlags(int_points) { for (let int_point of int_points) { - int_point.edge_before.bvStart = undefined; - int_point.edge_before.bvEnd = undefined; - int_point.edge_before.bv = undefined; - int_point.edge_before.overlap = undefined; + if (int_point.edge_before) { + int_point.edge_before.bvStart = undefined; + int_point.edge_before.bvEnd = undefined; + int_point.edge_before.bv = undefined; + int_point.edge_before.overlap = undefined; + } - int_point.edge_after.bvStart = undefined; - int_point.edge_after.bvEnd = undefined; - int_point.edge_after.bv = undefined; - int_point.edge_after.overlap = undefined; + if (int_point.edge_after) { + int_point.edge_after.bvStart = undefined; + int_point.edge_after.bvEnd = undefined; + int_point.edge_after.bv = undefined; + int_point.edge_after.overlap = undefined; + } } for (let int_point of int_points) { - int_point.edge_before.bvEnd = BOUNDARY$1; - int_point.edge_after.bvStart = BOUNDARY$1; + if (int_point.edge_before) int_point.edge_before.bvEnd = BOUNDARY$1; + if (int_point.edge_after) int_point.edge_after.bvStart = BOUNDARY$1; } } function calculateInclusionFlags(int_points, polygon) { for (let int_point of int_points) { - int_point.edge_before.setInclusion(polygon); - int_point.edge_after.setInclusion(polygon); + if (int_point.edge_before) int_point.edge_before.setInclusion(polygon); + if (int_point.edge_after) int_point.edge_after.setInclusion(polygon); } } @@ -736,8 +742,14 @@ function splitByIntersections(polygon, int_points) } if (int_point.is_vertex & START_VERTEX$1) { // nothing to split - int_point.edge_before = edge.prev; - int_point.is_vertex = END_VERTEX$1; + if (edge.prev) { + int_point.edge_before = edge.prev; // polygon + int_point.is_vertex = END_VERTEX$1; + } + else { // multiline start vertex + int_point.edge_after = int_point.edge_before; + int_point.edge_before = edge.prev; + } continue; } if (int_point.is_vertex & END_VERTEX$1) { // nothing to split @@ -749,7 +761,9 @@ function splitByIntersections(polygon, int_points) } for (let int_point of int_points) { - int_point.edge_after = int_point.edge_before.next; + if (int_point.edge_before) { + int_point.edge_after = int_point.edge_before.next; + } } } @@ -2366,6 +2380,8 @@ class Multiline extends LinkedList { let edge = new Flatten.Edge(shape); this.append(edge); } + + this.setArcLength(); } } } @@ -2404,6 +2420,24 @@ class Multiline extends LinkedList { return new Multiline(this.toShapes()); } + /** + * Set arc_length property for each of the edges in the face. + * Arc_length of the edge it the arc length from the first edge of the face + */ + setArcLength() { + for (let edge of this) { + this.setOneEdgeArcLength(edge); + } + } + + setOneEdgeArcLength(edge) { + if (edge === this.first) { + edge.arc_length = 0.0; + } else { + edge.arc_length = edge.prev.arc_length + edge.prev.length; + } + } + /** * Split edge and add new vertex, return new edge inserted * @param {Point} pt - point on edge that will be added as new vertex @@ -7764,7 +7798,6 @@ class Polygon { } } - // No intersections - return a copy of the original polygon if (intersections.int_points1.length === 0) return newPoly; @@ -7792,7 +7825,8 @@ class Polygon { // filter intersections between two edges that got same inclusion flag for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + if (int_point1.edge_before && int_point1.edge_after && + int_point1.edge_before.bv === int_point1.edge_after.bv) { intersections.int_points2[int_point1.id] = -1; // to be filtered out int_point1.id = -1; // to be filtered out } @@ -7808,13 +7842,13 @@ class Polygon { intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - // Add 2 new inner edges between intersection points + // Add new inner edges between intersection points let int_point1_prev; let int_point1_curr; for (let i = 1; i < intersections.int_points1_sorted.length; i++) { int_point1_curr = intersections.int_points1_sorted[i]; int_point1_prev = intersections.int_points1_sorted[i-1]; - if (int_point1_curr.edge_before.bv === INSIDE$2) { + if (int_point1_curr.edge_before && int_point1_curr.edge_before.bv === INSIDE$2) { let edgeFrom = int_point1_prev.edge_after; let edgeTo = int_point1_curr.edge_before; let newEdges = multiline.getChain(edgeFrom, edgeTo); @@ -7838,59 +7872,6 @@ class Polygon { return newPoly } - /** - * Cut face of polygon with a segment between two points and create two new polygons - * Supposed that a segments between points does not intersect any other edge - * @param {Point} pt1 - * @param {Point} pt2 - * @returns {Polygon[]} - */ - cutFace(pt1, pt2) { - let edge1 = this.findEdgeByPoint(pt1); - let edge2 = this.findEdgeByPoint(pt2); - if (edge1.face !== edge2.face) - return []; - - // Cut face into two and create new polygon with two faces - let edgeBefore1 = this.addVertex(pt1, edge1); - edge2 = this.findEdgeByPoint(pt2); - let edgeBefore2 = this.addVertex(pt2, edge2); - - let face = edgeBefore1.face; - let newEdge1 = new Flatten.Edge( - new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) - ); - let newEdge2 = new Flatten.Edge( - new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) - ); - - // Swap links - edgeBefore1.next.prev = newEdge2; - newEdge2.next = edgeBefore1.next; - - edgeBefore1.next = newEdge1; - newEdge1.prev = edgeBefore1; - - edgeBefore2.next.prev = newEdge1; - newEdge1.next = edgeBefore2.next; - - edgeBefore2.next = newEdge2; - newEdge2.prev = edgeBefore2; - - // Insert new edge to the edges container and 2d index - this.edges.add(newEdge1); - this.edges.add(newEdge2); - - // Add two new faces - let face1 = this.addFace(newEdge1, edgeBefore1); - let face2 = this.addFace(newEdge2, edgeBefore2); - - // Remove old face - this.faces.delete(face); - - return [face1.toPolygon(), face2.toPolygon()]; - } - /** * A special case of cut() function * The return is a polygon cut with line diff --git a/dist/main.umd.js b/dist/main.umd.js index bbad2800..105ec108 100644 --- a/dist/main.umd.js +++ b/dist/main.umd.js @@ -438,7 +438,9 @@ arc_length = shapes[0].coord(pt); } else { - arc_length = (is_vertex & END_VERTEX$1) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + arc_length = (is_vertex & END_VERTEX$1) && edge.next && edge.next.arc_length === 0 ? + 0 : + edge.arc_length + len; } int_points.push({ @@ -596,28 +598,32 @@ function initializeInclusionFlags(int_points) { for (let int_point of int_points) { - int_point.edge_before.bvStart = undefined; - int_point.edge_before.bvEnd = undefined; - int_point.edge_before.bv = undefined; - int_point.edge_before.overlap = undefined; + if (int_point.edge_before) { + int_point.edge_before.bvStart = undefined; + int_point.edge_before.bvEnd = undefined; + int_point.edge_before.bv = undefined; + int_point.edge_before.overlap = undefined; + } - int_point.edge_after.bvStart = undefined; - int_point.edge_after.bvEnd = undefined; - int_point.edge_after.bv = undefined; - int_point.edge_after.overlap = undefined; + if (int_point.edge_after) { + int_point.edge_after.bvStart = undefined; + int_point.edge_after.bvEnd = undefined; + int_point.edge_after.bv = undefined; + int_point.edge_after.overlap = undefined; + } } for (let int_point of int_points) { - int_point.edge_before.bvEnd = BOUNDARY$1; - int_point.edge_after.bvStart = BOUNDARY$1; + if (int_point.edge_before) int_point.edge_before.bvEnd = BOUNDARY$1; + if (int_point.edge_after) int_point.edge_after.bvStart = BOUNDARY$1; } } function calculateInclusionFlags(int_points, polygon) { for (let int_point of int_points) { - int_point.edge_before.setInclusion(polygon); - int_point.edge_after.setInclusion(polygon); + if (int_point.edge_before) int_point.edge_before.setInclusion(polygon); + if (int_point.edge_after) int_point.edge_after.setInclusion(polygon); } } @@ -742,8 +748,14 @@ } if (int_point.is_vertex & START_VERTEX$1) { // nothing to split - int_point.edge_before = edge.prev; - int_point.is_vertex = END_VERTEX$1; + if (edge.prev) { + int_point.edge_before = edge.prev; // polygon + int_point.is_vertex = END_VERTEX$1; + } + else { // multiline start vertex + int_point.edge_after = int_point.edge_before; + int_point.edge_before = edge.prev; + } continue; } if (int_point.is_vertex & END_VERTEX$1) { // nothing to split @@ -755,7 +767,9 @@ } for (let int_point of int_points) { - int_point.edge_after = int_point.edge_before.next; + if (int_point.edge_before) { + int_point.edge_after = int_point.edge_before.next; + } } } @@ -2372,6 +2386,8 @@ let edge = new Flatten.Edge(shape); this.append(edge); } + + this.setArcLength(); } } } @@ -2410,6 +2426,24 @@ return new Multiline(this.toShapes()); } + /** + * Set arc_length property for each of the edges in the face. + * Arc_length of the edge it the arc length from the first edge of the face + */ + setArcLength() { + for (let edge of this) { + this.setOneEdgeArcLength(edge); + } + } + + setOneEdgeArcLength(edge) { + if (edge === this.first) { + edge.arc_length = 0.0; + } else { + edge.arc_length = edge.prev.arc_length + edge.prev.length; + } + } + /** * Split edge and add new vertex, return new edge inserted * @param {Point} pt - point on edge that will be added as new vertex @@ -7770,7 +7804,6 @@ } } - // No intersections - return a copy of the original polygon if (intersections.int_points1.length === 0) return newPoly; @@ -7798,7 +7831,8 @@ // filter intersections between two edges that got same inclusion flag for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + if (int_point1.edge_before && int_point1.edge_after && + int_point1.edge_before.bv === int_point1.edge_after.bv) { intersections.int_points2[int_point1.id] = -1; // to be filtered out int_point1.id = -1; // to be filtered out } @@ -7814,13 +7848,13 @@ intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - // Add 2 new inner edges between intersection points + // Add new inner edges between intersection points let int_point1_prev; let int_point1_curr; for (let i = 1; i < intersections.int_points1_sorted.length; i++) { int_point1_curr = intersections.int_points1_sorted[i]; int_point1_prev = intersections.int_points1_sorted[i-1]; - if (int_point1_curr.edge_before.bv === INSIDE$2) { + if (int_point1_curr.edge_before && int_point1_curr.edge_before.bv === INSIDE$2) { let edgeFrom = int_point1_prev.edge_after; let edgeTo = int_point1_curr.edge_before; let newEdges = multiline.getChain(edgeFrom, edgeTo); @@ -7844,59 +7878,6 @@ return newPoly } - /** - * Cut face of polygon with a segment between two points and create two new polygons - * Supposed that a segments between points does not intersect any other edge - * @param {Point} pt1 - * @param {Point} pt2 - * @returns {Polygon[]} - */ - cutFace(pt1, pt2) { - let edge1 = this.findEdgeByPoint(pt1); - let edge2 = this.findEdgeByPoint(pt2); - if (edge1.face !== edge2.face) - return []; - - // Cut face into two and create new polygon with two faces - let edgeBefore1 = this.addVertex(pt1, edge1); - edge2 = this.findEdgeByPoint(pt2); - let edgeBefore2 = this.addVertex(pt2, edge2); - - let face = edgeBefore1.face; - let newEdge1 = new Flatten.Edge( - new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) - ); - let newEdge2 = new Flatten.Edge( - new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) - ); - - // Swap links - edgeBefore1.next.prev = newEdge2; - newEdge2.next = edgeBefore1.next; - - edgeBefore1.next = newEdge1; - newEdge1.prev = edgeBefore1; - - edgeBefore2.next.prev = newEdge1; - newEdge1.next = edgeBefore2.next; - - edgeBefore2.next = newEdge2; - newEdge2.prev = edgeBefore2; - - // Insert new edge to the edges container and 2d index - this.edges.add(newEdge1); - this.edges.add(newEdge2); - - // Add two new faces - let face1 = this.addFace(newEdge1, edgeBefore1); - let face2 = this.addFace(newEdge2, edgeBefore2); - - // Remove old face - this.faces.delete(face); - - return [face1.toPolygon(), face2.toPolygon()]; - } - /** * A special case of cut() function * The return is a polygon cut with line diff --git a/index.d.ts b/index.d.ts index c08a1295..2edc199c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -538,7 +538,6 @@ declare namespace Flatten { addVertex(pt: Point, edge: PolygonEdge): PolygonEdge; removeEndVertex(edge: Edge): void; cut(multiline: Multiline): Polygon[]; - cutFace(pt1: Point, pt2: Point): [Polygon, Polygon]; cutWithLine(line: Line): Polygon; findEdgeByPoint(pt: Point): PolygonEdge; splitToIslands() : Polygon[]; diff --git a/src/classes/multiline.js b/src/classes/multiline.js index c523c13a..12d76d11 100644 --- a/src/classes/multiline.js +++ b/src/classes/multiline.js @@ -36,6 +36,8 @@ export class Multiline extends LinkedList { let edge = new Flatten.Edge(shape); this.append(edge); } + + this.setArcLength() } } } @@ -74,6 +76,24 @@ export class Multiline extends LinkedList { return new Multiline(this.toShapes()); } + /** + * Set arc_length property for each of the edges in the face. + * Arc_length of the edge it the arc length from the first edge of the face + */ + setArcLength() { + for (let edge of this) { + this.setOneEdgeArcLength(edge); + } + } + + setOneEdgeArcLength(edge) { + if (edge === this.first) { + edge.arc_length = 0.0; + } else { + edge.arc_length = edge.prev.arc_length + edge.prev.length; + } + } + /** * Split edge and add new vertex, return new edge inserted * @param {Point} pt - point on edge that will be added as new vertex diff --git a/src/classes/polygon.js b/src/classes/polygon.js index 7d5fc76c..1187f3af 100644 --- a/src/classes/polygon.js +++ b/src/classes/polygon.js @@ -319,7 +319,6 @@ export class Polygon { } } - // No intersections - return a copy of the original polygon if (intersections.int_points1.length === 0) return newPoly; @@ -347,7 +346,8 @@ export class Polygon { // filter intersections between two edges that got same inclusion flag for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + if (int_point1.edge_before && int_point1.edge_after && + int_point1.edge_before.bv === int_point1.edge_after.bv) { intersections.int_points2[int_point1.id] = -1; // to be filtered out int_point1.id = -1; // to be filtered out } @@ -363,13 +363,13 @@ export class Polygon { intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - // Add 2 new inner edges between intersection points + // Add new inner edges between intersection points let int_point1_prev let int_point1_curr; for (let i = 1; i < intersections.int_points1_sorted.length; i++) { int_point1_curr = intersections.int_points1_sorted[i] int_point1_prev = intersections.int_points1_sorted[i-1]; - if (int_point1_curr.edge_before.bv === INSIDE) { + if (int_point1_curr.edge_before && int_point1_curr.edge_before.bv === INSIDE) { let edgeFrom = int_point1_prev.edge_after let edgeTo = int_point1_curr.edge_before let newEdges = multiline.getChain(edgeFrom, edgeTo) @@ -393,59 +393,6 @@ export class Polygon { return newPoly } - /** - * Cut face of polygon with a segment between two points and create two new polygons - * Supposed that a segments between points does not intersect any other edge - * @param {Point} pt1 - * @param {Point} pt2 - * @returns {Polygon[]} - */ - cutFace(pt1, pt2) { - let edge1 = this.findEdgeByPoint(pt1); - let edge2 = this.findEdgeByPoint(pt2); - if (edge1.face !== edge2.face) - return []; - - // Cut face into two and create new polygon with two faces - let edgeBefore1 = this.addVertex(pt1, edge1); - edge2 = this.findEdgeByPoint(pt2); - let edgeBefore2 = this.addVertex(pt2, edge2); - - let face = edgeBefore1.face; - let newEdge1 = new Flatten.Edge( - new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) - ); - let newEdge2 = new Flatten.Edge( - new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) - ); - - // Swap links - edgeBefore1.next.prev = newEdge2; - newEdge2.next = edgeBefore1.next; - - edgeBefore1.next = newEdge1; - newEdge1.prev = edgeBefore1; - - edgeBefore2.next.prev = newEdge1; - newEdge1.next = edgeBefore2.next; - - edgeBefore2.next = newEdge2; - newEdge2.prev = edgeBefore2; - - // Insert new edge to the edges container and 2d index - this.edges.add(newEdge1); - this.edges.add(newEdge2); - - // Add two new faces - let face1 = this.addFace(newEdge1, edgeBefore1); - let face2 = this.addFace(newEdge2, edgeBefore2); - - // Remove old face - this.faces.delete(face); - - return [face1.toPolygon(), face2.toPolygon()]; - } - /** * A special case of cut() function * The return is a polygon cut with line diff --git a/src/data_structures/smart_intersections.js b/src/data_structures/smart_intersections.js index 8a88d93e..ace7ccc4 100644 --- a/src/data_structures/smart_intersections.js +++ b/src/data_structures/smart_intersections.js @@ -37,7 +37,9 @@ export function addToIntPoints(edge, pt, int_points) arc_length = shapes[0].coord(pt) } else { - arc_length = (is_vertex & Constants.END_VERTEX) && edge.next.arc_length === 0 ? 0 : edge.arc_length + len; + arc_length = (is_vertex & Constants.END_VERTEX) && edge.next && edge.next.arc_length === 0 ? + 0 : + edge.arc_length + len; } int_points.push({ @@ -195,28 +197,32 @@ export function filterDuplicatedIntersections(intersections) export function initializeInclusionFlags(int_points) { for (let int_point of int_points) { - int_point.edge_before.bvStart = undefined; - int_point.edge_before.bvEnd = undefined; - int_point.edge_before.bv = undefined; - int_point.edge_before.overlap = undefined; - - int_point.edge_after.bvStart = undefined; - int_point.edge_after.bvEnd = undefined; - int_point.edge_after.bv = undefined; - int_point.edge_after.overlap = undefined; + if (int_point.edge_before) { + int_point.edge_before.bvStart = undefined; + int_point.edge_before.bvEnd = undefined; + int_point.edge_before.bv = undefined; + int_point.edge_before.overlap = undefined; + } + + if (int_point.edge_after) { + int_point.edge_after.bvStart = undefined; + int_point.edge_after.bvEnd = undefined; + int_point.edge_after.bv = undefined; + int_point.edge_after.overlap = undefined; + } } for (let int_point of int_points) { - int_point.edge_before.bvEnd = Constants.BOUNDARY; - int_point.edge_after.bvStart = Constants.BOUNDARY; + if (int_point.edge_before) int_point.edge_before.bvEnd = Constants.BOUNDARY; + if (int_point.edge_after) int_point.edge_after.bvStart = Constants.BOUNDARY; } } export function calculateInclusionFlags(int_points, polygon) { for (let int_point of int_points) { - int_point.edge_before.setInclusion(polygon); - int_point.edge_after.setInclusion(polygon); + if (int_point.edge_before) int_point.edge_before.setInclusion(polygon); + if (int_point.edge_after) int_point.edge_after.setInclusion(polygon); } } @@ -341,8 +347,14 @@ export function splitByIntersections(polygon, int_points) } if (int_point.is_vertex & Constants.START_VERTEX) { // nothing to split - int_point.edge_before = edge.prev; - int_point.is_vertex = Constants.END_VERTEX; + if (edge.prev) { + int_point.edge_before = edge.prev; // polygon + int_point.is_vertex = Constants.END_VERTEX; + } + else { // multiline start vertex + int_point.edge_after = int_point.edge_before + int_point.edge_before = edge.prev + } continue; } if (int_point.is_vertex & Constants.END_VERTEX) { // nothing to split @@ -354,7 +366,9 @@ export function splitByIntersections(polygon, int_points) } for (let int_point of int_points) { - int_point.edge_after = int_point.edge_before.next; + if (int_point.edge_before) { + int_point.edge_after = int_point.edge_before.next; + } } } diff --git a/test/classes/polygon.js b/test/classes/polygon.js index 942457f4..e8850dfc 100644 --- a/test/classes/polygon.js +++ b/test/classes/polygon.js @@ -919,4 +919,25 @@ describe('#Flatten.Polygon', function() { expect(res_poly.faces.size).to.equal(3); expect(res_poly.edges.size).to.equal(14); }); + it('Cannot cut polygon with MultiLine #159 - fixed', () => { + const { point, Polygon, Multiline } = Flatten; + + const poly = new Polygon([ + point(20, 20), + point(60, 20), + point(60, 60), + point(20, 60) + ]); + const segments = [ + segment(20, 20, 40, 40), + segment(40, 40, 50, 40), + segment(50, 40, 60, 60) + ]; + + const multiLine = new Multiline(segments); + const newPoly = poly.cut(multiLine); + + expect(newPoly.faces.size).to.equal(2); + expect(newPoly.edges.size).to.equal(10) + }) }); From 4f967d4b24ccc2840e13986c86b6bacd4bbadb37 Mon Sep 17 00:00:00 2001 From: alexanderbol Date: Fri, 29 Mar 2024 18:07:42 +0300 Subject: [PATCH 6/6] Exported type not found issue #165 - export constants OVERLAP_SAME, OVERLAP_OPPOSITE --- dist/main.cjs | 2 + dist/main.mjs | 2 +- dist/main.umd.js | 2 + docs/Arc.html | 4 +- docs/Box.html | 4 +- docs/Circle.html | 4 +- docs/CircularLinkedList.html | 4 +- docs/DE9IM.html | 4 +- docs/Edge.html | 14 +- docs/Errors.html | 4 +- docs/Face.html | 4 +- docs/Inversion.html | 4 +- docs/Line.html | 4 +- docs/LinkedList.html | 4 +- docs/Matrix.html | 4 +- docs/Multiline.html | 116 +++++++-- docs/PlanarSet.html | 4 +- docs/Point.html | 4 +- docs/Polygon.html | 232 ++---------------- docs/Ray.html | 189 ++++++++++++-- docs/Segment.html | 4 +- docs/Shape.html | 4 +- docs/Vector.html | 4 +- docs/algorithms_boolean_op.js.html | 4 +- docs/algorithms_distance.js.html | 4 +- docs/algorithms_ray_shooting.js.html | 4 +- docs/algorithms_relation.js.html | 10 +- docs/classes_arc.js.html | 4 +- docs/classes_box.js.html | 4 +- docs/classes_circle.js.html | 4 +- docs/classes_edge.js.html | 16 +- docs/classes_face.js.html | 4 +- docs/classes_inversion.js.html | 4 +- docs/classes_line.js.html | 6 +- docs/classes_matrix.js.html | 4 +- docs/classes_multiline.js.html | 39 ++- docs/classes_point.js.html | 4 +- docs/classes_polygon.js.html | 176 +++++-------- docs/classes_ray.js.html | 17 +- docs/classes_segment.js.html | 4 +- docs/classes_shape.js.html | 4 +- docs/classes_vector.js.html | 4 +- ...ta_structures_circular_linked_list.js.html | 4 +- docs/data_structures_de9im.js.html | 4 +- docs/data_structures_linked_list.js.html | 4 +- docs/data_structures_planar_set.js.html | 4 +- docs/global.html | 8 +- docs/index.html | 4 +- docs/module-BooleanOperations.html | 4 +- docs/module-RayShoot.html | 4 +- docs/module-Relation.html | 4 +- docs/utils_constants.js.html | 4 +- docs/utils_errors.js.html | 4 +- docs/utils_utils.js.html | 4 +- index.js | 2 +- package.json | 2 +- 56 files changed, 521 insertions(+), 472 deletions(-) diff --git a/dist/main.cjs b/dist/main.cjs index 22de6033..ca2970b3 100644 --- a/dist/main.cjs +++ b/dist/main.cjs @@ -8837,6 +8837,8 @@ exports.Matrix = Matrix; exports.Multiline = Multiline; exports.ORIENTATION = ORIENTATION; exports.OUTSIDE = OUTSIDE$1; +exports.OVERLAP_OPPOSITE = OVERLAP_OPPOSITE$1; +exports.OVERLAP_SAME = OVERLAP_SAME$1; exports.PlanarSet = PlanarSet; exports.Point = Point$1; exports.Polygon = Polygon; diff --git a/dist/main.mjs b/dist/main.mjs index f665b360..4c121d8e 100644 --- a/dist/main.mjs +++ b/dist/main.mjs @@ -8815,4 +8815,4 @@ Flatten.Distance = Distance; Flatten.BooleanOperations = BooleanOperations; Flatten.Relations = Relations; -export { Arc, BOUNDARY$1 as BOUNDARY, BooleanOperations, Box, CCW, CW, Circle$1 as Circle, Distance, Edge, Errors, Face, INSIDE$2 as INSIDE, Inversion, Line$1 as Line, Matrix, Multiline, ORIENTATION, OUTSIDE$1 as OUTSIDE, PlanarSet, Point$1 as Point, Polygon, Ray, Relations, Segment, smart_intersections as SmartIntersections, Utils$1 as Utils, Vector$1 as Vector, arc, box, circle, Flatten as default, inversion, line, matrix, multiline, point, polygon, ray, ray_shoot, segment, vector$1 as vector }; +export { Arc, BOUNDARY$1 as BOUNDARY, BooleanOperations, Box, CCW, CW, Circle$1 as Circle, Distance, Edge, Errors, Face, INSIDE$2 as INSIDE, Inversion, Line$1 as Line, Matrix, Multiline, ORIENTATION, OUTSIDE$1 as OUTSIDE, OVERLAP_OPPOSITE$1 as OVERLAP_OPPOSITE, OVERLAP_SAME$1 as OVERLAP_SAME, PlanarSet, Point$1 as Point, Polygon, Ray, Relations, Segment, smart_intersections as SmartIntersections, Utils$1 as Utils, Vector$1 as Vector, arc, box, circle, Flatten as default, inversion, line, matrix, multiline, point, polygon, ray, ray_shoot, segment, vector$1 as vector }; diff --git a/dist/main.umd.js b/dist/main.umd.js index 105ec108..cec3f16c 100644 --- a/dist/main.umd.js +++ b/dist/main.umd.js @@ -8839,6 +8839,8 @@ exports.Multiline = Multiline; exports.ORIENTATION = ORIENTATION; exports.OUTSIDE = OUTSIDE$1; + exports.OVERLAP_OPPOSITE = OVERLAP_OPPOSITE$1; + exports.OVERLAP_SAME = OVERLAP_SAME$1; exports.PlanarSet = PlanarSet; exports.Point = Point$1; exports.Polygon = Polygon; diff --git a/docs/Arc.html b/docs/Arc.html index 7b6c3514..9b8a9528 100644 --- a/docs/Arc.html +++ b/docs/Arc.html @@ -24,7 +24,7 @@
@@ -3096,7 +3096,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Box.html b/docs/Box.html index c9427128..987460f6 100644 --- a/docs/Box.html +++ b/docs/Box.html @@ -24,7 +24,7 @@
@@ -3027,7 +3027,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Circle.html b/docs/Circle.html index 6a43af9c..6b6835e2 100644 --- a/docs/Circle.html +++ b/docs/Circle.html @@ -24,7 +24,7 @@
@@ -1788,7 +1788,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/CircularLinkedList.html b/docs/CircularLinkedList.html index d3ffeaa9..075c9820 100644 --- a/docs/CircularLinkedList.html +++ b/docs/CircularLinkedList.html @@ -24,7 +24,7 @@
@@ -664,7 +664,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/DE9IM.html b/docs/DE9IM.html index dc20a311..fa425180 100644 --- a/docs/DE9IM.html +++ b/docs/DE9IM.html @@ -24,7 +24,7 @@
@@ -1507,7 +1507,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Edge.html b/docs/Edge.html index 7918238a..f0b5291d 100644 --- a/docs/Edge.html +++ b/docs/Edge.html @@ -24,7 +24,7 @@
@@ -1193,7 +1193,7 @@

containsSource:
@@ -1328,7 +1328,7 @@

middleSource:
@@ -1432,7 +1432,7 @@

pointAtL
Source:
@@ -1589,7 +1589,7 @@

setInclus
Source:
@@ -1720,7 +1720,7 @@

setOverlap<
Source:
@@ -1819,7 +1819,7 @@

Parameters:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Errors.html b/docs/Errors.html index c6bde709..e68d0b82 100644 --- a/docs/Errors.html +++ b/docs/Errors.html @@ -24,7 +24,7 @@
@@ -426,7 +426,7 @@

(static)
- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Face.html b/docs/Face.html index bb2a1342..4b59c721 100644 --- a/docs/Face.html +++ b/docs/Face.html @@ -24,7 +24,7 @@
@@ -2343,7 +2343,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Inversion.html b/docs/Inversion.html index 216d2b83..178b7a66 100644 --- a/docs/Inversion.html +++ b/docs/Inversion.html @@ -24,7 +24,7 @@
@@ -232,7 +232,7 @@

Classes


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Line.html b/docs/Line.html index 24726c29..619ba6d5 100644 --- a/docs/Line.html +++ b/docs/Line.html @@ -24,7 +24,7 @@
@@ -2629,7 +2629,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/LinkedList.html b/docs/LinkedList.html index 3d7e479f..225d0560 100644 --- a/docs/LinkedList.html +++ b/docs/LinkedList.html @@ -24,7 +24,7 @@
@@ -1094,7 +1094,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Matrix.html b/docs/Matrix.html index 7ee25849..e031435a 100644 --- a/docs/Matrix.html +++ b/docs/Matrix.html @@ -24,7 +24,7 @@
@@ -1613,7 +1613,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Multiline.html b/docs/Multiline.html index aec92f0f..cb2fe080 100644 --- a/docs/Multiline.html +++ b/docs/Multiline.html @@ -24,7 +24,7 @@
@@ -100,7 +100,7 @@

new Multilin
Source:
@@ -202,7 +202,7 @@

boxSource:
@@ -266,7 +266,7 @@

edgesSource:
@@ -330,7 +330,7 @@

verticesSource:
@@ -404,7 +404,7 @@

addVertexSource:
@@ -586,7 +586,7 @@

cloneSource:
@@ -690,7 +690,7 @@

findEd
Source:
@@ -848,7 +848,7 @@

rotateSource:
@@ -993,6 +993,90 @@
Returns:

+ + + +
+ + +
+ + + +

setArcLength()

+ + + + + +
+ Set arc_length property for each of the edges in the face. +Arc_length of the edge it the arc length from the first edge of the face +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + @@ -1046,7 +1130,7 @@

splitSource:
@@ -1202,7 +1286,7 @@

svgSource:
@@ -1355,7 +1439,7 @@

toJSONSource:
@@ -1459,7 +1543,7 @@

toShapesSource:
@@ -1564,7 +1648,7 @@

transformSource:
@@ -1724,7 +1808,7 @@

translateSource:
@@ -1849,7 +1933,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/PlanarSet.html b/docs/PlanarSet.html index 50db8074..b55e198e 100644 --- a/docs/PlanarSet.html +++ b/docs/PlanarSet.html @@ -24,7 +24,7 @@
@@ -1053,7 +1053,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Point.html b/docs/Point.html index e5ee07d6..10df256f 100644 --- a/docs/Point.html +++ b/docs/Point.html @@ -24,7 +24,7 @@
@@ -1861,7 +1861,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Polygon.html b/docs/Polygon.html index ba934081..ffbd1285 100644 --- a/docs/Polygon.html +++ b/docs/Polygon.html @@ -24,7 +24,7 @@
@@ -1129,7 +1129,7 @@

containsSource:
@@ -1242,16 +1242,14 @@
Returns:
-

cut(multiline) → {Array.<Polygon>}

+

cut(multiline) → {Polygon}

- Cut polygon with multiline and return array of new polygons -Multiline should be constructed from a line with intersection point, see notebook: -https://next.observablehq.com/@alexbol99/cut-polygon-with-line + Cut polygon with multiline and return a new polygon
@@ -1287,7 +1285,7 @@

cutSource:
@@ -1381,190 +1379,7 @@
Returns:
-Array.<Polygon> - - -
- - - - -

- - - -
- - -
- - - -

cutFace(pt1, pt2) → {Array.<Polygon>}

- - - - - -
- Cut face of polygon with a segment between two points and create two new polygons -Supposed that a segments between points does not intersect any other edge -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
pt1 - - -Point - - - - - - -
pt2 - - -Point - - - - - - -
- - - - - - - - - - - - - - -
-
Returns:
- - - -
-
- Type: -
-
- -Array.<Polygon> +Polygon
@@ -1590,7 +1405,8 @@

cutWithLin
- Return a result of cutting polygon with line + A special case of cut() function +The return is a polygon cut with line
@@ -1626,7 +1442,7 @@

cutWithLin
Source:
@@ -1942,7 +1758,7 @@

distanceTo<
Source:
@@ -2102,7 +1918,7 @@

findEd
Source:
@@ -2258,7 +2074,7 @@

intersectSource:
@@ -3026,7 +2842,7 @@

reverseSource:
@@ -3132,7 +2948,7 @@

rotateSource:
@@ -3330,7 +3146,7 @@

scaleSource:
@@ -3476,8 +3292,8 @@

splitTo
- Split polygon into array of polygons, where each polygon is an island with all -hole that it contains + Split polygon into array of polygons, where each polygon is an outer face with all +containing inner faces
@@ -3513,7 +3329,7 @@

splitTo
Source:
@@ -3617,7 +3433,7 @@

svgSource:
@@ -3768,7 +3584,7 @@

toArraySource:
@@ -3873,7 +3689,7 @@

toJSONSource:
@@ -3977,7 +3793,7 @@

transformSource:
@@ -4137,7 +3953,7 @@

translateSource:
@@ -4262,7 +4078,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Ray.html b/docs/Ray.html index ce332fd5..48675805 100644 --- a/docs/Ray.html +++ b/docs/Ray.html @@ -24,7 +24,7 @@
@@ -108,7 +108,7 @@

new RaySource:
@@ -288,7 +288,7 @@

boxSource:
@@ -352,7 +352,7 @@

endSource:
@@ -416,7 +416,7 @@

lengthSource:
@@ -480,7 +480,7 @@

slopeSource:
@@ -544,7 +544,7 @@

startSource:
@@ -618,7 +618,7 @@

cloneSource:
@@ -722,7 +722,7 @@

containsSource:
@@ -828,6 +828,165 @@
Returns:
+

+ + +
+ + + +

coord(pt) → {number}

+ + + + + +
+ Return coordinate of the point that lies on the ray in the transformed +coordinate system where center is the projection of the point(0,0) to +the line containing this ray and axe y is collinear to the normal vector.
+This method assumes that point lies on the ray +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
pt + + +Point + + + + + point on a ray + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + + +
+ + +
@@ -878,7 +1037,7 @@

intersectSource:
@@ -1038,7 +1197,7 @@

rotateSource:
@@ -1199,7 +1358,7 @@

splitSource:
@@ -1347,7 +1506,7 @@

svgSource:
@@ -1508,7 +1667,7 @@

transformSource:
@@ -1633,7 +1792,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Segment.html b/docs/Segment.html index 221eb700..2964cc61 100644 --- a/docs/Segment.html +++ b/docs/Segment.html @@ -24,7 +24,7 @@
@@ -2854,7 +2854,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Shape.html b/docs/Shape.html index 83a9934a..77269d17 100644 --- a/docs/Shape.html +++ b/docs/Shape.html @@ -24,7 +24,7 @@
@@ -838,7 +838,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/Vector.html b/docs/Vector.html index 65000b7a..51d652d3 100644 --- a/docs/Vector.html +++ b/docs/Vector.html @@ -24,7 +24,7 @@
@@ -2628,7 +2628,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/algorithms_boolean_op.js.html b/docs/algorithms_boolean_op.js.html index 5f2cf51f..a29035fa 100644 --- a/docs/algorithms_boolean_op.js.html +++ b/docs/algorithms_boolean_op.js.html @@ -24,7 +24,7 @@
@@ -741,7 +741,7 @@

algorithms/boolean_op.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/algorithms_distance.js.html b/docs/algorithms_distance.js.html index d61b05b0..6f81dbe2 100644 --- a/docs/algorithms_distance.js.html +++ b/docs/algorithms_distance.js.html @@ -24,7 +24,7 @@
@@ -651,7 +651,7 @@

algorithms/distance.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/algorithms_ray_shooting.js.html b/docs/algorithms_ray_shooting.js.html index 61e578dc..6bcc0a9b 100644 --- a/docs/algorithms_ray_shooting.js.html +++ b/docs/algorithms_ray_shooting.js.html @@ -24,7 +24,7 @@
@@ -194,7 +194,7 @@

algorithms/ray_shooting.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/algorithms_relation.js.html b/docs/algorithms_relation.js.html index 41856b75..b93034d9 100644 --- a/docs/algorithms_relation.js.html +++ b/docs/algorithms_relation.js.html @@ -24,7 +24,7 @@
@@ -244,7 +244,7 @@

algorithms/relation.js

denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon([circle.toArc()]).cut(multiline); + denim.E2I = new Flatten.Polygon([circle.toArc()]).cutWithLine(line); } return denim; @@ -286,7 +286,7 @@

algorithms/relation.js

denim.I2B = ip_sorted; denim.I2E = [splitShapes[0], splitShapes[2]]; - denim.E2I = new Flatten.Polygon(box.toSegments()).cut(multiline); + denim.E2I = new Flatten.Polygon(box.toSegments()).cutWithLine(line); } } return denim; @@ -306,7 +306,7 @@

algorithms/relation.js

denim.I2B = [...multiline].slice(1).map( (edge) => edge.bv === Flatten.BOUNDARY ? edge.shape : edge.shape.start ); denim.I2E = [...multiline].filter(edge => edge.bv === Flatten.OUTSIDE).map(edge => edge.shape); - denim.E2I = polygon.cut(multiline); + denim.E2I = polygon.cutWithLine(line); return denim; } @@ -387,7 +387,7 @@

algorithms/relation.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_arc.js.html b/docs/classes_arc.js.html index 03bb7c96..5848b6fd 100644 --- a/docs/classes_arc.js.html +++ b/docs/classes_arc.js.html @@ -24,7 +24,7 @@
@@ -549,7 +549,7 @@

classes/arc.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_box.js.html b/docs/classes_box.js.html index 477a6d75..002303ea 100644 --- a/docs/classes_box.js.html +++ b/docs/classes_box.js.html @@ -24,7 +24,7 @@
@@ -363,7 +363,7 @@

classes/box.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_circle.js.html b/docs/classes_circle.js.html index 55d4975b..456044ac 100644 --- a/docs/classes_circle.js.html +++ b/docs/classes_circle.js.html @@ -24,7 +24,7 @@
@@ -301,7 +301,7 @@

classes/circle.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_edge.js.html b/docs/classes_edge.js.html index 447d6341..3f037b1c 100644 --- a/docs/classes_edge.js.html +++ b/docs/classes_edge.js.html @@ -24,7 +24,7 @@
@@ -134,14 +134,22 @@

classes/edge.js

return this.shape.box; } - isSegment() { + get isSegment() { return this.shape instanceof Flatten.Segment; } - isArc() { + get isArc() { return this.shape instanceof Flatten.Arc; } + get isLine() { + return this.shape instanceof Flatten.Line; + } + + get isRay() { + return this.shape instanceof Flatten.Ray + } + /** * Get middle point of the edge * @returns {Point} @@ -286,7 +294,7 @@

classes/edge.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_face.js.html b/docs/classes_face.js.html index b42bed7c..e3b46803 100644 --- a/docs/classes_face.js.html +++ b/docs/classes_face.js.html @@ -24,7 +24,7 @@
@@ -548,7 +548,7 @@

classes/face.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_inversion.js.html b/docs/classes_inversion.js.html index f46dd6ab..0a5920e4 100644 --- a/docs/classes_inversion.js.html +++ b/docs/classes_inversion.js.html @@ -24,7 +24,7 @@
@@ -141,7 +141,7 @@

classes/inversion.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_line.js.html b/docs/classes_line.js.html index 97331f96..82b4e7ea 100644 --- a/docs/classes_line.js.html +++ b/docs/classes_line.js.html @@ -24,7 +24,7 @@
@@ -324,7 +324,7 @@

classes/line.js

*/ split(pt) { if (pt instanceof Flatten.Point) { - return [new Flatten.Ray(pt, this.norm.invert()), new Flatten.Ray(pt, this.norm)] + return [new Flatten.Ray(pt, this.norm), new Flatten.Ray(pt, this.norm)] } else { let multiline = new Flatten.Multiline([this]); @@ -425,7 +425,7 @@

classes/line.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_matrix.js.html b/docs/classes_matrix.js.html index bb53e6b2..dbd27093 100644 --- a/docs/classes_matrix.js.html +++ b/docs/classes_matrix.js.html @@ -24,7 +24,7 @@
@@ -202,7 +202,7 @@

classes/matrix.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_multiline.js.html b/docs/classes_multiline.js.html index 19486550..66d46f5c 100644 --- a/docs/classes_multiline.js.html +++ b/docs/classes_multiline.js.html @@ -24,7 +24,7 @@
@@ -43,7 +43,6 @@

classes/multiline.js

import Flatten from '../flatten'; import LinkedList from '../data_structures/linked_list'; -import {END_VERTEX, NOT_VERTEX, START_VERTEX} from "../utils/constants"; import {convertToString} from "../utils/attributes"; /** @@ -58,10 +57,10 @@

classes/multiline.js

return; } - if (args.length == 1) { + if (args.length === 1) { if (args[0] instanceof Array) { let shapes = args[0]; - if (shapes.length == 0) + if (shapes.length === 0) return; // TODO: more strict validation: @@ -78,6 +77,8 @@

classes/multiline.js

let edge = new Flatten.Edge(shape); this.append(edge); } + + this.setArcLength() } } } @@ -95,7 +96,7 @@

classes/multiline.js

* @returns {Box} */ get box() { - return this.edges.reduce( (acc,edge) => acc = acc.merge(edge.box), new Flatten.Box() ); + return this.edges.reduce( (acc,edge) => acc.merge(edge.box), new Flatten.Box() ); } /** @@ -116,6 +117,24 @@

classes/multiline.js

return new Multiline(this.toShapes()); } + /** + * Set arc_length property for each of the edges in the face. + * Arc_length of the edge it the arc length from the first edge of the face + */ + setArcLength() { + for (let edge of this) { + this.setOneEdgeArcLength(edge); + } + } + + setOneEdgeArcLength(edge) { + if (edge === this.first) { + edge.arc_length = 0.0; + } else { + edge.arc_length = edge.prev.arc_length + edge.prev.length; + } + } + /** * Split edge and add new vertex, return new edge inserted * @param {Point} pt - point on edge that will be added as new vertex @@ -144,6 +163,14 @@

classes/multiline.js

return newEdge; } + getChain(edgeFrom, edgeTo) { + let edges = [] + for (let edge = edgeFrom; edge !== edgeTo.next; edge = edge.next) { + edges.push(edge) + } + return edges + } + /** * Split edges of multiline with intersection points and return mutated multiline * @param {Point[]} ip - array of points to be added as new vertices @@ -258,7 +285,7 @@

classes/multiline.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_point.js.html b/docs/classes_point.js.html index 48602f21..6923f551 100644 --- a/docs/classes_point.js.html +++ b/docs/classes_point.js.html @@ -24,7 +24,7 @@
@@ -308,7 +308,7 @@

classes/point.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_polygon.js.html b/docs/classes_polygon.js.html index 423a9c65..017e2824 100644 --- a/docs/classes_polygon.js.html +++ b/docs/classes_polygon.js.html @@ -24,7 +24,7 @@
@@ -52,11 +52,11 @@

classes/polygon.js

import * as Relations from "../algorithms/relation"; import { addToIntPoints, calculateInclusionFlags, filterDuplicatedIntersections, - getSortedArray, getSortedArrayOnLine, initializeInclusionFlags, insertBetweenIntPoints, + getSortedArray, initializeInclusionFlags, insertBetweenIntPoints, splitByIntersections } from "../data_structures/smart_intersections"; import {Multiline} from "./multiline"; -import {intersectEdge2Line} from "../algorithms/intersection"; +import {intersectEdge2Edge} from "../algorithms/intersection"; import {INSIDE, BOUNDARY} from "../utils/constants"; import {convertToString} from "../utils/attributes"; import {Matrix} from "./matrix"; @@ -332,97 +332,12 @@

classes/polygon.js

} /** - * Cut polygon with multiline and return array of new polygons - * Multiline should be constructed from a line with intersection point, see notebook: - * https://next.observablehq.com/@alexbol99/cut-polygon-with-line + * Cut polygon with multiline and return a new polygon * @param {Multiline} multiline - * @returns {Polygon[]} + * @returns {Polygon} */ cut(multiline) { - let cutPolygons = [this.clone()]; - for (let edge of multiline) { - if (edge.setInclusion(this) !== INSIDE) - continue; - - let cut_edge_start = edge.shape.start; - let cut_edge_end = edge.shape.end; - - let newCutPolygons = []; - for (let polygon of cutPolygons) { - if (polygon.findEdgeByPoint(cut_edge_start) === undefined) { - newCutPolygons.push(polygon); - } else { - let [cutPoly1, cutPoly2] = polygon.cutFace(cut_edge_start, cut_edge_end); - newCutPolygons.push(cutPoly1, cutPoly2); - } - } - cutPolygons = newCutPolygons; - } - return cutPolygons; - } - - /** - * Cut face of polygon with a segment between two points and create two new polygons - * Supposed that a segments between points does not intersect any other edge - * @param {Point} pt1 - * @param {Point} pt2 - * @returns {Polygon[]} - */ - cutFace(pt1, pt2) { - let edge1 = this.findEdgeByPoint(pt1); - let edge2 = this.findEdgeByPoint(pt2); - if (edge1.face !== edge2.face) - return []; - - // Cut face into two and create new polygon with two faces - let edgeBefore1 = this.addVertex(pt1, edge1); - edge2 = this.findEdgeByPoint(pt2); - let edgeBefore2 = this.addVertex(pt2, edge2); - - let face = edgeBefore1.face; - let newEdge1 = new Flatten.Edge( - new Flatten.Segment(edgeBefore1.end, edgeBefore2.end) - ); - let newEdge2 = new Flatten.Edge( - new Flatten.Segment(edgeBefore2.end, edgeBefore1.end) - ); - - // Swap links - edgeBefore1.next.prev = newEdge2; - newEdge2.next = edgeBefore1.next; - - edgeBefore1.next = newEdge1; - newEdge1.prev = edgeBefore1; - - edgeBefore2.next.prev = newEdge1; - newEdge1.next = edgeBefore2.next; - - edgeBefore2.next = newEdge2; - newEdge2.prev = edgeBefore2; - - // Insert new edge to the edges container and 2d index - this.edges.add(newEdge1); - this.edges.add(newEdge2); - - // Add two new faces - let face1 = this.addFace(newEdge1, edgeBefore1); - let face2 = this.addFace(newEdge2, edgeBefore2); - - // Remove old face - this.faces.delete(face); - - return [face1.toPolygon(), face2.toPolygon()]; - } - - /** - * Return a result of cutting polygon with line - * @param {Line} line - cutting line - * @returns {Polygon} newPoly - resulted polygon - */ - cutWithLine(line) { - let newPoly = this.clone(); - - let multiline = new Multiline([line]); + let newPoly = this.clone() // smart intersections let intersections = { @@ -432,14 +347,16 @@

classes/polygon.js

int_points2_sorted: [] }; - // intersect line with each edge of the polygon + // intersect each edge of multiline with each edge of the polygon // and create smart intersections - for (let edge of newPoly.edges) { - let ip = intersectEdge2Line(edge, line); - // for each intersection point - for (let pt of ip) { - addToIntPoints(multiline.first, pt, intersections.int_points1); - addToIntPoints(edge, pt, intersections.int_points2); + for (let edge1 of multiline.edges) { + for (let edge2 of newPoly.edges) { + let ip = intersectEdge2Edge(edge1, edge2); + // for each intersection point + for (let pt of ip) { + addToIntPoints(edge1, pt, intersections.int_points1); + addToIntPoints(edge2, pt, intersections.int_points2); + } } } @@ -448,7 +365,7 @@

classes/polygon.js

return newPoly; // sort smart intersections - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); // split by intersection points @@ -459,7 +376,7 @@

classes/polygon.js

filterDuplicatedIntersections(intersections); // sort intersection points again after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); // initialize inclusion flags for edges of multiline incident to intersections @@ -470,7 +387,8 @@

classes/polygon.js

// filter intersections between two edges that got same inclusion flag for (let int_point1 of intersections.int_points1_sorted) { - if (int_point1.edge_before.bv === int_point1.edge_after.bv) { + if (int_point1.edge_before && int_point1.edge_after && + int_point1.edge_before.bv === int_point1.edge_after.bv) { intersections.int_points2[int_point1.id] = -1; // to be filtered out int_point1.id = -1; // to be filtered out } @@ -483,28 +401,48 @@

classes/polygon.js

return newPoly; // sort intersection points 3d time after filtering - intersections.int_points1_sorted = getSortedArrayOnLine(line, intersections.int_points1); + intersections.int_points1_sorted = getSortedArray(intersections.int_points1); intersections.int_points2_sorted = getSortedArray(intersections.int_points2); - // Add 2 new inner edges between intersection points - let int_point1_prev = intersections.int_points1[0]; - let new_edge; - for (let int_point1_curr of intersections.int_points1_sorted) { - if (int_point1_curr.edge_before.bv === INSIDE) { - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_prev.pt, int_point1_curr.pt)); // (int_point1_curr.edge_before.shape); - insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], new_edge); - newPoly.edges.add(new_edge); - - new_edge = new Flatten.Edge(new Flatten.Segment(int_point1_curr.pt, int_point1_prev.pt)); // (int_point1_curr.edge_before.shape.reverse()); - insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], new_edge); - newPoly.edges.add(new_edge); + // Add new inner edges between intersection points + let int_point1_prev + let int_point1_curr; + for (let i = 1; i < intersections.int_points1_sorted.length; i++) { + int_point1_curr = intersections.int_points1_sorted[i] + int_point1_prev = intersections.int_points1_sorted[i-1]; + if (int_point1_curr.edge_before && int_point1_curr.edge_before.bv === INSIDE) { + let edgeFrom = int_point1_prev.edge_after + let edgeTo = int_point1_curr.edge_before + let newEdges = multiline.getChain(edgeFrom, edgeTo) + insertBetweenIntPoints(intersections.int_points2[int_point1_prev.id], intersections.int_points2[int_point1_curr.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)) + + newEdges = newEdges.reverse().map(edge => new Flatten.Edge(edge.shape.reverse())) + for (let k=0; k < newEdges.length-1; k++) { + newEdges[k].next = newEdges[k+1] + newEdges[k+1].prev = newEdges[k] + } + insertBetweenIntPoints(intersections.int_points2[int_point1_curr.id], intersections.int_points2[int_point1_prev.id], newEdges); + newEdges.forEach(edge => newPoly.edges.add(edge)); } - int_point1_prev = int_point1_curr; + } // Recreate faces newPoly.recreateFaces(); - return newPoly; + + return newPoly + } + + /** + * A special case of cut() function + * The return is a polygon cut with line + * @param {Line} line - cutting line + * @returns {Polygon} newPoly - resulted polygon + */ + cutWithLine(line) { + let multiline = new Multiline([line]); + return this.cut(multiline); } /** @@ -524,8 +462,8 @@

classes/polygon.js

} /** - * Split polygon into array of polygons, where each polygon is an island with all - * hole that it contains + * Split polygon into array of polygons, where each polygon is an outer face with all + * containing inner faces * @returns {Flatten.Polygon[]} */ splitToIslands() { @@ -762,7 +700,7 @@

classes/polygon.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_ray.js.html b/docs/classes_ray.js.html index 21d8f253..6ef5d792 100644 --- a/docs/classes_ray.js.html +++ b/docs/classes_ray.js.html @@ -24,7 +24,7 @@
@@ -45,6 +45,7 @@

classes/ray.js

import * as Intersection from "../algorithms/intersection"; import {Shape} from "./shape"; import {Errors} from "../utils/errors"; +import {vector} from './vector' /** * Class representing a ray (a half-infinite line). @@ -152,6 +153,18 @@

classes/ray.js

return Flatten.Utils.EQ_0(this.norm.dot(vec)) && Flatten.Utils.GE(vec.cross(this.norm),0); } + /** + * Return coordinate of the point that lies on the ray in the transformed + * coordinate system where center is the projection of the point(0,0) to + * the line containing this ray and axe y is collinear to the normal vector. <br/> + * This method assumes that point lies on the ray + * @param {Point} pt - point on a ray + * @returns {number} + */ + coord(pt) { + return vector(pt.x, pt.y).cross(this.norm); + } + /** * Split ray with point and return array of segment and new ray * @param {Point} pt @@ -271,7 +284,7 @@

classes/ray.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_segment.js.html b/docs/classes_segment.js.html index 9e251cb4..149f7b16 100644 --- a/docs/classes_segment.js.html +++ b/docs/classes_segment.js.html @@ -24,7 +24,7 @@
@@ -420,7 +420,7 @@

classes/segment.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_shape.js.html b/docs/classes_shape.js.html index c633850e..b94b6b7b 100644 --- a/docs/classes_shape.js.html +++ b/docs/classes_shape.js.html @@ -24,7 +24,7 @@
@@ -122,7 +122,7 @@

classes/shape.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/classes_vector.js.html b/docs/classes_vector.js.html index 6f7e7b0d..3e7f8918 100644 --- a/docs/classes_vector.js.html +++ b/docs/classes_vector.js.html @@ -24,7 +24,7 @@
@@ -311,7 +311,7 @@

classes/vector.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/data_structures_circular_linked_list.js.html b/docs/data_structures_circular_linked_list.js.html index 2628ea31..a44299ba 100644 --- a/docs/data_structures_circular_linked_list.js.html +++ b/docs/data_structures_circular_linked_list.js.html @@ -24,7 +24,7 @@
@@ -117,7 +117,7 @@

data_structures/circular_linked_list.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/data_structures_de9im.js.html b/docs/data_structures_de9im.js.html index 8b717c8f..65ee6620 100644 --- a/docs/data_structures_de9im.js.html +++ b/docs/data_structures_de9im.js.html @@ -24,7 +24,7 @@
@@ -262,7 +262,7 @@

data_structures/de9im.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/data_structures_linked_list.js.html b/docs/data_structures_linked_list.js.html index c0d1d212..01eec6c7 100644 --- a/docs/data_structures_linked_list.js.html +++ b/docs/data_structures_linked_list.js.html @@ -24,7 +24,7 @@
@@ -215,7 +215,7 @@

data_structures/linked_list.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/data_structures_planar_set.js.html b/docs/data_structures_planar_set.js.html index 307fba15..cee942d3 100644 --- a/docs/data_structures_planar_set.js.html +++ b/docs/data_structures_planar_set.js.html @@ -24,7 +24,7 @@
@@ -156,7 +156,7 @@

data_structures/planar_set.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/global.html b/docs/global.html index 1d7c411b..9864a642 100644 --- a/docs/global.html +++ b/docs/global.html @@ -24,7 +24,7 @@
@@ -761,7 +761,7 @@

(constant) m
Source:
@@ -964,7 +964,7 @@

(constant) pol
Source:
@@ -2466,7 +2466,7 @@

Parameters:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/index.html b/docs/index.html index 2188a7e7..a0029da1 100644 --- a/docs/index.html +++ b/docs/index.html @@ -24,7 +24,7 @@
@@ -341,7 +341,7 @@

Support


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/module-BooleanOperations.html b/docs/module-BooleanOperations.html index 2bf81684..41d8ae70 100644 --- a/docs/module-BooleanOperations.html +++ b/docs/module-BooleanOperations.html @@ -24,7 +24,7 @@
@@ -1185,7 +1185,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/module-RayShoot.html b/docs/module-RayShoot.html index 9ed56370..ee64a3db 100644 --- a/docs/module-RayShoot.html +++ b/docs/module-RayShoot.html @@ -24,7 +24,7 @@
@@ -275,7 +275,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/module-Relation.html b/docs/module-Relation.html index 5a910e7f..443dc8de 100644 --- a/docs/module-Relation.html +++ b/docs/module-Relation.html @@ -24,7 +24,7 @@
@@ -1641,7 +1641,7 @@
Returns:

- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/utils_constants.js.html b/docs/utils_constants.js.html index 5503d170..b0cfeeac 100644 --- a/docs/utils_constants.js.html +++ b/docs/utils_constants.js.html @@ -24,7 +24,7 @@
@@ -85,7 +85,7 @@

utils/constants.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/utils_errors.js.html b/docs/utils_errors.js.html index 075b96da..4d54a762 100644 --- a/docs/utils_errors.js.html +++ b/docs/utils_errors.js.html @@ -24,7 +24,7 @@
@@ -108,7 +108,7 @@

utils/errors.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/docs/utils_utils.js.html b/docs/utils_utils.js.html index 10a5909a..351414b9 100644 --- a/docs/utils_utils.js.html +++ b/docs/utils_utils.js.html @@ -24,7 +24,7 @@
@@ -135,7 +135,7 @@

utils/utils.js


- Generated by JSDoc 3.6.11 on Sat Dec 23 2023 10:36:58 GMT+0200 (Israel Standard Time) using the Minami theme. + Generated by JSDoc 3.6.11 on Fri Mar 29 2024 18:06:33 GMT+0300 (Israel Daylight Time) using the Minami theme.
diff --git a/index.js b/index.js index 4d7082cf..dba0e5a2 100644 --- a/index.js +++ b/index.js @@ -34,6 +34,6 @@ export {SmartIntersections}; Flatten.BooleanOperations = BooleanOperations; Flatten.Relations = Relations; -export {CCW, CW, ORIENTATION, INSIDE, OUTSIDE, BOUNDARY} from './src/utils/constants'; +export {CCW, CW, ORIENTATION, INSIDE, OUTSIDE, BOUNDARY, OVERLAP_SAME, OVERLAP_OPPOSITE} from './src/utils/constants'; export default Flatten; diff --git a/package.json b/package.json index 01eb0534..47067dbc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@flatten-js/core", - "version": "1.4.8", + "version": "1.5.0", "description": "Javascript library for 2d geometry", "main": "dist/main.cjs", "umd:main": "dist/main.umd.js",