diff --git a/src/intersection.class.ts b/src/intersection.class.ts index e8fed9d635d..2b6a14bd4eb 100644 --- a/src/intersection.class.ts +++ b/src/intersection.class.ts @@ -1,70 +1,80 @@ //@ts-nocheck -import { Point } from './point.class'; +import { Point } from "./point.class"; +import { fabric } from '../HEADER'; -(function(global) { - /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */ - var fabric = global.fabric || (global.fabric = { }); - /** - * Intersection class - * @class fabric.Intersection - * @memberOf fabric - * @constructor - */ - function Intersection(status) { +/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */ + +type IntersectionType = 'Intersection' | 'Coincident' | 'Parallel'; + +/** + * **Assuming `T`, `A`, `B` are points on the same line**, + * check if `T` is contained in `[A, B]` by comparing the direction of the vectors from `T` to `A` and `B` + * @param T + * @param A + * @param B + * @returns true if `T` is contained + */ +const isContainedInInterval = (T: Point, A: Point, B: Point) => { + const TA = new Point(T).subtract(A); + const TB = new Point(T).subtract(B); + return Math.sign(TA.x) !== Math.sign(TB.x) || Math.sign(TA.y) !== Math.sign(TB.y); +} + +export class Intersection { + + points: Point[] + + status?: IntersectionType + + constructor(status?: IntersectionType) { this.status = status; this.points = []; } - fabric.Intersection = Intersection; - - fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ { - - constructor: Intersection, - - /** - * Appends a point to intersection - * @param {Point} point - * @return {fabric.Intersection} thisArg - * @chainable - */ - appendPoint: function (point) { - this.points.push(point); - return this; - }, - - /** - * Appends points to intersection - * @param {Array} points - * @return {fabric.Intersection} thisArg - * @chainable - */ - appendPoints: function (points) { - this.points = this.points.concat(points); - return this; - } - }; + /** + * + * @param {Point} point + * @returns + */ + contains(point) { + return this.points.some(p => p.eq(point)); + } /** - * Checks if one line intersects another - * TODO: rename in intersectSegmentSegment + * Appends points of intersection + * @param {...Point[]} points + * @return {Intersection} thisArg + * @chainable + */ + private append(...points) { + this.points = this.points.concat(points.filter(point => { + return !this.contains(point); + })); + return this; + } + + /** + * Checks if a line intersects another * @static * @param {Point} a1 * @param {Point} a2 * @param {Point} b1 * @param {Point} b2 - * @return {fabric.Intersection} + * @param {boolean} [aIinfinite=true] check segment intersection by passing `false` + * @param {boolean} [bIinfinite=true] check segment intersection by passing `false` + * @return {Intersection} */ - fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) { - var result, - uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), - ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x), - uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); + static intersectLineLine(a1, a2, b1, b2, aIinfinite = true, bIinfinite = true) { + let result; + const uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), + ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x), + uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); if (uB !== 0) { - var ua = uaT / uB, - ub = ubT / uB; - if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) { + const ua = uaT / uB, + ub = ubT / uB; + if ((aIinfinite || (0 <= ua && ua <= 1)) && (bIinfinite || (0 <= ub && ub <= 1))) { result = new Intersection('Intersection'); - result.appendPoint(new Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y))); + result.append(new Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y))); } else { result = new Intersection(); @@ -72,95 +82,154 @@ import { Point } from './point.class'; } else { if (uaT === 0 || ubT === 0) { - result = new Intersection('Coincident'); + const segmentsCoincide = aIinfinite || bIinfinite + || isContainedInInterval(a1, b1, b2) || isContainedInInterval(a2, b1, b2) + || isContainedInInterval(b1, a1, a2) || isContainedInInterval(b2, a1, a2); + result = new Intersection(segmentsCoincide ? 'Coincident' : undefined); } else { result = new Intersection('Parallel'); } } return result; - }; + } + + /** + * Checks if a segment intersects a line + * @see {@link intersectLineLine} for line intersection + * @static + * @param {Point} s1 boundary point of segment + * @param {Point} s2 other boundary point of segment + * @param {Point} l1 point on line + * @param {Point} l2 other point on line + * @return {Intersection} + */ + static intersectSegmentLine(s1, s2, l1, l2) { + return Intersection.intersectLineLine(s1, s2, l1, l2, false, true); + } + + /** + * Checks if a segment intersects another + * @see {@link intersectLineLine} for line intersection + * @static + * @param {Point} a1 boundary point of segment + * @param {Point} a2 other boundary point of segment + * @param {Point} b1 boundary point of segment + * @param {Point} b2 other boundary point of segment + * @return {Intersection} + */ + static intersectSegmentSegment(a1, a2, b1, b2) { + return Intersection.intersectLineLine(a1, a2, b1, b2, false, false); + } /** * Checks if line intersects polygon - * TODO: rename in intersectSegmentPolygon - * fix detection of coincident + * + * @todo account for stroke + * * @static - * @param {Point} a1 - * @param {Point} a2 - * @param {Array} points - * @return {fabric.Intersection} + * @see {@link intersectSegmentPolygon} for segment intersection + * @param {Point} a1 point on line + * @param {Point} a2 other point on line + * @param {Point[]} points polygon points + * @param {boolean} [infinite=true] check segment intersection by passing `false` + * @return {Intersection} */ - fabric.Intersection.intersectLinePolygon = function(a1, a2, points) { - var result = new Intersection(), - length = points.length, - b1, b2, inter, i; + static intersectLinePolygon(a1, a2, points, infinite = true) { + const result = new Intersection(); + const length = points.length; - for (i = 0; i < length; i++) { + for (let i = 0, b1, b2, inter; i < length; i++) { b1 = points[i]; b2 = points[(i + 1) % length]; - inter = Intersection.intersectLineLine(a1, a2, b1, b2); - - result.appendPoints(inter.points); + inter = Intersection.intersectLineLine(a1, a2, b1, b2, infinite, false); + if (inter.status === 'Coincident') { + return inter; + } + result.append(...inter.points); } + if (result.points.length > 0) { result.status = 'Intersection'; } + return result; - }; + } /** - * Checks if polygon intersects another polygon + * Checks if segment intersects polygon * @static - * @param {Array} points1 - * @param {Array} points2 - * @return {fabric.Intersection} + * @see {@link intersectLinePolygon} for line intersection + * @param {Point} a1 boundary point of segment + * @param {Point} a2 other boundary point of segment + * @param {Point[]} points polygon points + * @return {Intersection} */ - fabric.Intersection.intersectPolygonPolygon = function (points1, points2) { - var result = new Intersection(), - length = points1.length, i; + static intersectSegmentPolygon(a1, a2, points) { + return Intersection.intersectLinePolygon(a1, a2, points, false); + } - for (i = 0; i < length; i++) { - var a1 = points1[i], - a2 = points1[(i + 1) % length], - inter = Intersection.intersectLinePolygon(a1, a2, points2); + /** + * Checks if polygon intersects another polygon + * + * @todo account for stroke + * + * @static + * @param {Point[]} points1 + * @param {Point[]} points2 + * @return {Intersection} + */ + static intersectPolygonPolygon(points1, points2) { + const result = new Intersection(), + length = points1.length; + const coincidents = []; + + for (let i = 0; i < length; i++) { + const a1 = points1[i], + a2 = points1[(i + 1) % length], + inter = Intersection.intersectSegmentPolygon(a1, a2, points2); + if (inter.status === 'Coincident') { + coincidents.push(inter); + result.append(a1, a2); + } + else { + result.append(...inter.points); + } + } - result.appendPoints(inter.points); + if (coincidents.length > 0 && coincidents.length === points1.length) { + return new Intersection('Coincident'); } - if (result.points.length > 0) { + else if (result.points.length > 0) { result.status = 'Intersection'; } + return result; - }; + } /** * Checks if polygon intersects rectangle * @static - * @param {Array} points - * @param {Point} r1 - * @param {Point} r2 - * @return {fabric.Intersection} + * @see {@link intersectPolygonPolygon} for polygon intersection + * @param {Point[]} points polygon points + * @param {Point} r1 top left point of rect + * @param {Point} r2 bottom right point of rect + * @return {Intersection} */ - fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) { - var min = r1.min(r2), - max = r1.max(r2), - topRight = new Point(max.x, min.y), - bottomLeft = new Point(min.x, max.y), - inter1 = Intersection.intersectLinePolygon(min, topRight, points), - inter2 = Intersection.intersectLinePolygon(topRight, max, points), - inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points), - inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points), - result = new Intersection(); - - result.appendPoints(inter1.points); - result.appendPoints(inter2.points); - result.appendPoints(inter3.points); - result.appendPoints(inter4.points); + static intersectPolygonRectangle(points, r1, r2) { + const min = r1.min(r2), + max = r1.max(r2), + topRight = new Point(max.x, min.y), + bottomLeft = new Point(min.x, max.y); + + return Intersection.intersectPolygonPolygon(points, [ + min, + topRight, + max, + bottomLeft + ]); + } - if (result.points.length > 0) { - result.status = 'Intersection'; - } - return result; - }; +} -})(typeof exports !== 'undefined' ? exports : window); +fabric.Intersection = Intersection; \ No newline at end of file diff --git a/src/mixins/object_geometry.mixin.ts b/src/mixins/object_geometry.mixin.ts index 7edc093657a..648c869943a 100644 --- a/src/mixins/object_geometry.mixin.ts +++ b/src/mixins/object_geometry.mixin.ts @@ -1,4 +1,6 @@ //@ts-nocheck + +import { Intersection } from '../intersection.class'; import { Point } from '../point.class'; (function(global) { @@ -235,12 +237,13 @@ import { Point } from '../point.class'; * @return {Boolean} true if object intersects with another object */ intersectsWithObject: function(other, absolute, calculate) { - var intersection = fabric.Intersection.intersectPolygonPolygon( + var intersection = Intersection.intersectPolygonPolygon( this.getCoords(absolute, calculate), other.getCoords(absolute, calculate) ); return intersection.status === 'Intersection' + || intersection.status === 'Coincident' || other.isContainedWithinObject(this, absolute, calculate) || this.isContainedWithinObject(other, absolute, calculate); }, diff --git a/test/unit/intersection.js b/test/unit/intersection.js index 8b93da9244a..a9948c9e109 100644 --- a/test/unit/intersection.js +++ b/test/unit/intersection.js @@ -20,118 +20,177 @@ assert.equal(intersection.status, status, 'constructor pass status value'); }); - QUnit.test('appendPoint', function(assert) { + QUnit.test('append', function(assert) { var point = new fabric.Point(1, 1); var intersection = new fabric.Intersection(); - assert.ok(typeof intersection.appendPoint === 'function', 'has appendPoint method'); - var returned = intersection.appendPoint(point); + assert.ok(typeof intersection.append === 'function', 'has appendPoint method'); + var returned = intersection.append(point, point); assert.ok(returned instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(returned, intersection, 'is chainable'); assert.equal(intersection.points.indexOf(point), 0, 'now intersection contain points'); + assert.equal(intersection.points.length, 2, 'now intersection contains 2 points'); }); - QUnit.test('appendPoints', function(assert) { - var point = new fabric.Point(1, 1); - var intersection = new fabric.Intersection(); - assert.ok(typeof intersection.appendPoints === 'function', 'has appendPoint method'); - var returned = intersection.appendPoints([point, point]); - assert.ok(returned instanceof fabric.Intersection, 'returns a fabric.Intersection'); - assert.equal(returned, intersection, 'is chainable'); - assert.equal(intersection.points.indexOf(point), 0, 'now intersection contain points'); - assert.equal(intersection.points.length, 2, 'now intersection contains 2 points'); + QUnit.test('intersectLineLine intersection', function (assert) { + var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(-10, -10), + p3 = new fabric.Point(0, 10), p4 = new fabric.Point(10, 0), + intersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); + assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection.status, 'Intersection', 'it return a intersection result'); + assert.deepEqual(intersection.points[0], new fabric.Point(5, 5), 'intersect in 5,5'); }); - QUnit.test('intersectLineLine simple intersection', function(assert) { + QUnit.test('intersectSegmentLine intersection', function (assert) { + var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(-10, -10), + p3 = new fabric.Point(0, 10), p4 = new fabric.Point(10, 0), + intersection1 = fabric.Intersection.intersectSegmentLine(p1, p2, p3, p4), + intersection2 = fabric.Intersection.intersectSegmentLine(p4, p3, p2, p1); + assert.ok(intersection1 instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection1.status, undefined, 'no result'); + assert.equal(intersection1.points.length, 0, 'no result'); + assert.ok(intersection2 instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection2.status, 'Intersection', 'Intersection result'); + assert.equal(intersection2.points.length, 1, 'has result'); + assert.deepEqual(intersection2.points[0], new fabric.Point(5, 5), 'intersect in 5,5'); + }); + + QUnit.test('intersectSegmentSegment simple intersection', function(assert) { var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(10,10), p3 = new fabric.Point(0, 10), p4 = new fabric.Point(10, 0), - intersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); - assert.ok(typeof fabric.Intersection.intersectLineLine === 'function', 'has intersectLineLine function'); + intersection = fabric.Intersection.intersectSegmentSegment(p1, p2, p3, p4); + assert.ok(typeof fabric.Intersection.intersectSegmentSegment === 'function', 'has intersectSegmentSegment function'); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, 'Intersection', 'it return a intersection result'); assert.deepEqual(intersection.points[0], new fabric.Point(5, 5), 'intersect in 5,5'); }); - QUnit.test('intersectLineLine parallel', function(assert) { + QUnit.test('intersectSegmentSegment parallel', function(assert) { var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(0,10), p3 = new fabric.Point(10, 0), p4 = new fabric.Point(10, 10), - intersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); + intersection = fabric.Intersection.intersectSegmentSegment(p1, p2, p3, p4); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, 'Parallel', 'it return a Parallel result'); assert.deepEqual(intersection.points, [], 'no point of intersections'); }); - QUnit.test('intersectLineLine coincident', function(assert) { + QUnit.test('intersectSegmentSegment coincident', function(assert) { var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(0, 10), p3 = new fabric.Point(0, 0), p4 = new fabric.Point(0, 10), - intersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); + intersection = fabric.Intersection.intersectSegmentSegment(p1, p2, p3, p4); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, 'Coincident', 'it return a Coincident result'); assert.deepEqual(intersection.points, [], 'no point of intersections'); }); - QUnit.test('intersectLineLine coincident but different', function(assert) { + QUnit.test('intersectSegmentSegment coincident but different', function(assert) { var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(0, 10), p3 = new fabric.Point(0, 1), p4 = new fabric.Point(0, 9), - intersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); + intersection = fabric.Intersection.intersectSegmentSegment(p1, p2, p3, p4); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, 'Coincident', 'it return a Coincident result'); assert.deepEqual(intersection.points, [], 'no point of intersections'); }); - QUnit.test('intersectLineLine no intersect', function(assert) { + QUnit.test('intersectSegmentSegment no coincident, intersectLineLine coincident', function (assert) { + var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(0, 10), + p3 = new fabric.Point(0, 20), p4 = new fabric.Point(0, 15), + segmentIntersection = fabric.Intersection.intersectSegmentSegment(p1, p2, p3, p4), + segLineIntersection = fabric.Intersection.intersectSegmentLine(p1, p2, p3, p4), + infiniteIntersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); + assert.ok(segmentIntersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(segmentIntersection.status, undefined, 'it return no result'); + assert.deepEqual(segmentIntersection.points, [], 'no point of intersections'); + assert.ok(segLineIntersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(segLineIntersection.status, 'Coincident', 'it return a Coincident result'); + assert.deepEqual(segLineIntersection.points, [], 'no point of intersections'); + assert.ok(infiniteIntersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(infiniteIntersection.status, 'Coincident', 'it return a Coincident result'); + assert.deepEqual(infiniteIntersection.points, [], 'no point of intersections'); + }); + + QUnit.test('intersectSegmentSegment no intersect', function(assert) { var p1 = new fabric.Point(0, 0), p2 = new fabric.Point(0,10), p3 = new fabric.Point(10, 0), p4 = new fabric.Point(1, 10), - intersection = fabric.Intersection.intersectLineLine(p1, p2, p3, p4); + intersection = fabric.Intersection.intersectSegmentSegment(p1, p2, p3, p4); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, undefined, 'it return a undefined status result'); assert.deepEqual(intersection.points, [], 'no point of intersections'); }); - QUnit.test('intersectLinePolygon', function(assert) { + QUnit.test('intersectSegmentPolygon', function(assert) { var p1 = new fabric.Point(0, 5), p2 = new fabric.Point(10, 5), p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), p5 = new fabric.Point(8, 10), points = [p3, p4, p5], - intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + intersection = fabric.Intersection.intersectSegmentPolygon(p1, p2, points); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); - assert.ok(typeof fabric.Intersection.intersectLinePolygon === 'function', 'has intersectLinePolygon function'); + assert.ok(typeof fabric.Intersection.intersectSegmentPolygon === 'function', 'has intersectSegmentPolygon function'); assert.equal(intersection.status, 'Intersection', 'it return a Intersection result'); assert.equal(intersection.points.length, 2, '2 points of intersections'); assert.deepEqual(intersection.points[0], new fabric.Point(3.5, 5), 'intersect in 3.5 ,5'); assert.deepEqual(intersection.points[1], new fabric.Point(6.5, 5), 'intersect in 6.5 ,5'); }); - QUnit.test('intersectLinePolygon in one point', function(assert) { + QUnit.test('intersectSegmentPolygon in one point', function(assert) { var p1 = new fabric.Point(0, 5), p2 = new fabric.Point(5, 5), p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), p5 = new fabric.Point(8, 10), points = [p3, p4, p5], - intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + intersection = fabric.Intersection.intersectSegmentPolygon(p1, p2, points); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, 'Intersection', 'it return a Intersection result'); assert.equal(intersection.points.length, 1, '1 points of intersections'); assert.deepEqual(intersection.points[0], new fabric.Point(3.5, 5), 'intersect in 3.5 ,5'); }); - QUnit.test('intersectLinePolygon in one point', function(assert) { + QUnit.test('intersectSegmentPolygonno intersection', function(assert) { var p1 = new fabric.Point(0, 5), p2 = new fabric.Point(3, 5), p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), p5 = new fabric.Point(8, 10), points = [p3, p4, p5], - intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + intersection = fabric.Intersection.intersectSegmentPolygon(p1, p2, points); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); assert.equal(intersection.status, undefined, 'it return a undefined result'); - assert.equal(intersection.points.length, 0, '0 points of intersections'); + assert.equal(intersection.points.length, 0, '0 points of intersections, closet intersection is (3.5, 5)'); }); - QUnit.test('intersectLinePolygon on a polygon segment', function(assert) { - //TODO: fix this. it should return coincident. + QUnit.test('intersectSegmentPolygon on a polygon segment', function(assert) { var p1 = new fabric.Point(1, 10), p2 = new fabric.Point(9, 10), p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), p5 = new fabric.Point(8, 10), points = [p3, p4, p5], - intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + intersection = fabric.Intersection.intersectSegmentPolygon(p1, p2, points); assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); - assert.equal(intersection.status, 'Intersection', 'it return a Intersection result'); - assert.equal(intersection.points.length, 2, '2 points of intersections'); - assert.deepEqual(intersection.points[0], new fabric.Point(2, 10), 'intersect in 2, 10'); - assert.deepEqual(intersection.points[1], new fabric.Point(8, 10), 'intersect in 8, 10'); + assert.equal(intersection.status, 'Coincident', 'it return a Intersection result'); + assert.equal(intersection.points.length, 0, 'infinte points of intersections'); + }); + + QUnit.test('intersectLinePolygon one point', function (assert) { + var p1 = new fabric.Point(1, 0), p2 = new fabric.Point(0, 0), + p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), + p5 = new fabric.Point(8, 10), points = [p3, p4, p5], + intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection.status, 'Intersection', 'it return an Intersection result'); + assert.equal(intersection.points.length, 1, '1 point of intersection'); + assert.deepEqual(intersection.points, [new fabric.Point(5, 0)], 'intersection points should match'); + }); + + QUnit.test('intersectLinePolygon', function (assert) { + var p1 = new fabric.Point(0, 5), p2 = new fabric.Point(3, 5), + p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), + p5 = new fabric.Point(8, 10), points = [p3, p4, p5], + intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection.status, 'Intersection', 'it return an Intersection result'); + assert.equal(intersection.points.length, 2, '2 points of intersection'); + assert.deepEqual(intersection.points, [new fabric.Point(3.5, 5), new fabric.Point(6.5, 5)], 'intersection points should match'); + }); + + QUnit.test('intersectLinePolygon on a polygon segment', function (assert) { + var p1 = new fabric.Point(1, 10), p2 = new fabric.Point(9, 10), + p3 = new fabric.Point(5, 0), p4 = new fabric.Point(2, 10), + p5 = new fabric.Point(8, 10), points = [p3, p4, p5], + intersection = fabric.Intersection.intersectLinePolygon(p1, p2, points); + assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection.status, 'Coincident', 'it return a Intersection result'); + assert.equal(intersection.points.length, 0, 'infinte points of intersections'); }); QUnit.test('intersectPolygonPolygon not intersecting', function(assert) { @@ -189,4 +248,41 @@ assert.equal(intersection.status, undefined, 'it return a Intersection result'); assert.equal(intersection.points.length, 0, '0 points of intersections'); }); + + QUnit.test('intersectPolygonPolygon coincident', function (assert) { + const points = [ + new fabric.Point(0, 0), + new fabric.Point(10, 0), + new fabric.Point(15, 5), + new fabric.Point(10, 10), + new fabric.Point(-5, 5), + ]; + assert.ok(typeof fabric.Intersection.intersectPolygonRectangle === 'function', 'has intersectPolygonPolygon function'); + + let intersection = fabric.Intersection.intersectPolygonPolygon(points, points.concat()); + assert.ok(intersection instanceof fabric.Intersection, 'returns a fabric.Intersection'); + assert.equal(intersection.status, 'Coincident', 'Coincident result'); + assert.equal(intersection.points.length, 0, 'Coincident'); + assert.deepEqual(intersection.points, [], 'result should be empty'); + + intersection = fabric.Intersection.intersectPolygonPolygon(points, points.concat(points[0].clone())); + assert.equal(intersection.status, 'Coincident', 'Coincident result'); + assert.equal(intersection.points.length, 0, 'Coincident'); + assert.deepEqual(intersection.points, [], 'result should be empty'); + + intersection = fabric.Intersection.intersectPolygonPolygon(points, points.concat(points[points.length - 1].clone())); + assert.equal(intersection.status, 'Coincident', 'Coincident result'); + assert.equal(intersection.points.length, 0, 'Coincident'); + assert.deepEqual(intersection.points, [], 'result should be empty'); + + intersection = fabric.Intersection.intersectPolygonPolygon(points, points.concat(points[1].clone())); + assert.equal(intersection.status, 'Intersection', 'Intersection result'); + assert.equal(intersection.points.length, points.length, 'all points intersect'); + assert.deepEqual(intersection.points, points, 'result should equal points'); + + intersection = fabric.Intersection.intersectPolygonPolygon(points, points.slice(0, -1)); + assert.equal(intersection.status, 'Intersection', 'Intersection result'); + assert.equal(intersection.points.length, points.length - 1, 'all points intersect accept the last'); + assert.deepEqual(intersection.points, points.slice(0, -1), 'result should equal points accept the last'); + }); })();