-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Chore(TS): migrate Intersection #8121
Changes from all commits
d4bcf4e
5a19804
20d3e77
59a6107
8f492ae
7c17d24
7cec891
3b5f60d
7aa20fa
91fb5eb
d3ac3f3
578717e
5574cb2
4ff2483
46182f2
6c1120c
3a3e902
627c73d
26fb94a
89f13c4
e94d62e
1df3ec2
ecdb285
dd5ce7d
4fb0413
b1a8bef
e1fa4a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,166 +1,235 @@ | ||
//@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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. refactored |
||
this.points = this.points.concat(points.filter(point => { | ||
return !this.contains(point); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. append only unique points There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this hot code? why does it matters? If this is used during render i don't really want to do it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hot code? What's that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How damaging is this loop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only 2 polygons that are almost identical will have <= n-1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ahh code used by the dev? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with hot code i mean code we use in tight loops during rendering. Goint to check how many intersections we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok is fine, the Intersection is just a fancy container for points, the math to find the point didn't change. I'm good with this. |
||
})); | ||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added option for infinite line check |
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. who ever wrote this knows their algerba There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some matrix det/cross clever calculation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of those can be probably grouped in smaller const, i m not sure the extra const are worth the subtraction saved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought of it too. |
||
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(); | ||
} | ||
} | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If segments intersection => check that coincident happens in range |
||
} | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
* | ||
* @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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed detection coincident (someone left a note about it) |
||
} | ||
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'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same fix of TODO comment |
||
} | ||
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid future misuse of the method because it is meant to be used in a very specific context as mentioned in the comment
Any suggestions?