UNPKG

@joint/core

Version:

JavaScript diagramming library

512 lines (468 loc) 15.3 kB
import { Line } from './line.mjs'; import { Polygon } from './polygon.mjs'; import { Rect } from './rect.mjs'; import { types } from './types.mjs'; export function exists(shape1, shape2, shape1opt, shape2opt) { switch (shape1.type) { case types.Line: { switch (shape2.type) { case types.Line: { return lineWithLine(shape1, shape2); } } break; } case types.Ellipse: { switch (shape2.type) { case types.Line: { return ellipseWithLine(shape1, shape2); } case types.Ellipse: { return ellipseWithEllipse(shape1, shape2); } } break; } case types.Rect: { switch (shape2.type) { case types.Line: { return rectWithLine(shape1, shape2); } case types.Ellipse: { return rectWithEllipse(shape1, shape2); } case types.Rect: { return rectWithRect(shape1, shape2); } } break; } case types.Polyline: { switch (shape2.type) { case types.Line: { return polylineWithLine(shape1, shape2); } case types.Ellipse: { return polylineWithEllipse(shape1, shape2); } case types.Rect: { return polylineWithRect(shape1, shape2); } case types.Polyline: { return polylineWithPolyline(shape1, shape2); } } break; } case types.Polygon: { switch (shape2.type) { case types.Line: { return polygonWithLine(shape1, shape2); } case types.Ellipse: { return polygonWithEllipse(shape1, shape2); } case types.Rect: { return polygonWithRect(shape1, shape2); } case types.Polyline: { return polygonWithPolyline(shape1, shape2); } case types.Polygon: { return polygonWithPolygon(shape1, shape2); } } break; } case types.Path: { switch (shape2.type) { case types.Line: { return pathWithLine(shape1, shape2, shape1opt); } case types.Ellipse: { return pathWithEllipse(shape1, shape2, shape1opt); } case types.Rect: { return pathWithRect(shape1, shape2, shape1opt); } case types.Polyline: { return pathWithPolyline(shape1, shape2, shape1opt); } case types.Polygon: { return pathWithPolygon(shape1, shape2, shape1opt); } case types.Path: { return pathWithPath(shape1, shape2, shape1opt, shape2opt); } } break; } } // None of the cases above switch (shape2.type) { case types.Ellipse: case types.Rect: case types.Polyline: case types.Polygon: case types.Path: { return exists(shape2, shape1, shape2opt, shape1opt); } default: { throw Error(`The intersection for ${shape1} and ${shape2} could not be found.`); } } } /* Line */ export function lineWithLine(line1, line2) { const x1 = line1.start.x; const y1 = line1.start.y; const x2 = line1.end.x; const y2 = line1.end.y; const x3 = line2.start.x; const y3 = line2.start.y; const x4 = line2.end.x; const y4 = line2.end.y; const s1x = x2 - x1; const s1y = y2 - y1; const s2x = x4 - x3; const s2y = y4 - y3; const s3x = x1 - x3; const s3y = y1 - y3; const p = s1x * s2y - s2x * s1y; const s = (s1x * s3y - s1y * s3x) / p; const t = (s2x * s3y - s2y * s3x) / p; return s >= 0 && s <= 1 && t >= 0 && t <= 1; } /* Ellipse */ export function ellipseWithLine(ellipse, line) { const rex = ellipse.a; const rey = ellipse.b; const xe = ellipse.x; const ye = ellipse.y; const x1 = line.start.x - xe; const x2 = line.end.x - xe; const y1 = line.start.y - ye; const y2 = line.end.y - ye; const rex_2 = rex * rex; const rey_2 = rey * rey; const dx = x2 - x1; const dy = y2 - y1; const A = dx * dx / rex_2 + dy * dy / rey_2; const B = 2 * x1 * dx / rex_2 + 2 * y1 * dy / rey_2; const C = x1 * x1 / rex_2 + y1 * y1 / rey_2 - 1; const D = B * B - 4 * A * C; if (D === 0) { const t = -B / 2 / A; return t >= 0 && t <= 1; } else if (D > 0) { const sqrt = Math.sqrt(D); const t1 = (-B + sqrt) / 2 / A; const t2 = (-B - sqrt) / 2 / A; return (t1 >= 0 && t1 <= 1) || (t2 >= 0 && t2 <= 1); } return false; } export function ellipseWithEllipse(ellipse1, ellipse2) { return _ellipsesIntersection(ellipse1, 0, ellipse2, 0); } /* Rect */ export function rectWithLine(rect, line) { const { start, end } = line; const { x, y, width, height } = rect; if ( (start.x > x + width && end.x > x + width) || (start.x < x && end.x < x) || (start.y > y + height && end.y > y + height) || (start.y < y && end.y < y) ) { return false; } if (rect.containsPoint(line.start) || rect.containsPoint(line.end)) { return true; } return lineWithLine(rect.topLine(), line) || lineWithLine(rect.rightLine(), line) || lineWithLine(rect.bottomLine(), line) || lineWithLine(rect.leftLine(), line); } export function rectWithEllipse(rect, ellipse) { if (!rectWithRect(rect, Rect.fromEllipse(ellipse))) return false; return polygonWithEllipse(Polygon.fromRect(rect), ellipse); } export function rectWithRect(rect1, rect2) { return rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.y + rect1.height > rect2.y; } /* Polyline */ export function polylineWithLine(polyline, line) { return _polylineWithLine(polyline, line, { interior: false }); } export function polylineWithEllipse(polyline, ellipse) { return _polylineWithEllipse(polyline, ellipse, { interior: false }); } export function polylineWithRect(polyline, rect) { return _polylineWithRect(polyline, rect, { interior: false }); } export function polylineWithPolyline(polyline1, polyline2) { return _polylineWithPolyline(polyline1, polyline2, { interior: false }); } /* Polygon */ export function polygonWithLine(polygon, line) { return _polylineWithLine(polygon, line, { interior: true }); } export function polygonWithEllipse(polygon, ellipse) { return _polylineWithEllipse(polygon, ellipse, { interior: true }); } export function polygonWithRect(polygon, rect) { return _polylineWithRect(polygon, rect, { interior: true }); } export function polygonWithPolyline(polygon, polyline) { return _polylineWithPolyline(polygon, polyline, { interior: true }); } export function polygonWithPolygon(polygon1, polygon2) { return _polylineWithPolygon(polygon1, polygon2, { interior: true }); } /* Path */ export function pathWithLine(path, line, pathOpt) { return path.getSubpaths().some(subpath => { const [polyline] = subpath.toPolylines(pathOpt); const { type } = subpath.getSegment(-1); if (type === 'Z') { return polygonWithLine(polyline, line); } else { return polylineWithLine(polyline, line); } }); } export function pathWithEllipse(path, ellipse, pathOpt) { return path.getSubpaths().some(subpath => { const [polyline] = subpath.toPolylines(pathOpt); const { type } = subpath.getSegment(-1); if (type === 'Z') { return polygonWithEllipse(polyline, ellipse); } else { return polylineWithEllipse(polyline, ellipse); } }); } export function pathWithRect(path, rect, pathOpt) { return pathWithPolygon(path, Polygon.fromRect(rect), pathOpt); } export function pathWithPolyline(path, polyline, pathOpt) { return _pathWithPolyline(path, polyline, pathOpt, { interior: false }); } export function pathWithPolygon(path, polygon, pathOpt) { return _pathWithPolyline(path, polygon, pathOpt, { interior: true }); } export function pathWithPath(path1, path2, pathOpt1, pathOpt2) { return path1.getSubpaths().some(subpath => { const [polyline1] = subpath.toPolylines(pathOpt1); const { type } = subpath.getSegment(-1); if (type === 'Z') { return pathWithPolygon(path2, polyline1, pathOpt2); } else { return pathWithPolyline(path2, polyline1, pathOpt2); } }); } function _polylineWithLine(polyline, line, opt = {}) { const { interior = false } = opt; let thisPoints; if (interior) { if (polyline.containsPoint(line.start)) { // If any point of the polyline lies inside this polygon (interior = true) // there is an intersection (we've chosen the start point) return true; } const { start, end, points } = polyline; thisPoints = end.equals(start) ? points : [...points, start]; } else { thisPoints = polyline.points; } const { length } = thisPoints; const segment = new Line(); for (let i = 0; i < length - 1; i++) { segment.start = thisPoints[i]; segment.end = thisPoints[i + 1]; if (lineWithLine(line, segment)) { return true; } } return false; } function _polylineWithEllipse(polyline, ellipse, opt = {}) { const { start, end, points } = polyline; if (ellipse.containsPoint(start)) { return true; } let thisPoints; const { interior = false } = opt; if (interior) { if (polyline.containsPoint(ellipse.center())) { // If any point of the ellipse lies inside this polygon (interior = true) // there is an intersection (we've chosen the center point) return true; } thisPoints = end.equals(start) ? points : [...points, start]; } else { thisPoints = points; } const { length } = thisPoints; const segment = new Line(); for (let i = 0; i < length - 1; i++) { segment.start = thisPoints[i]; segment.end = thisPoints[i + 1]; if (ellipseWithLine(ellipse, segment)) { return true; } } return false; } function _polylineWithRect(polyline, rect, opt) { const polygon = Polygon.fromRect(rect); return _polylineWithPolygon(polyline, polygon, opt); } function _pathWithPolyline(path, polyline1, pathOpt, opt) { return path.getSubpaths().some(subpath => { const [polyline2] = subpath.toPolylines(pathOpt); const { type } = subpath.getSegment(-1); if (type === 'Z') { return _polylineWithPolygon(polyline1, polyline2, opt); } else { return _polylineWithPolyline(polyline1, polyline2, opt); } }); } function _polylineWithPolyline(polyline1, polyline2, opt = {}) { const { interior = false } = opt; let thisPolyline; if (interior) { const { start } = polyline2; if (polyline1.containsPoint(start)) { // If any point of the polyline lies inside this polygon (interior = true) // there is an intersection (we've chosen the start point) return true; } thisPolyline = polyline1.clone().close(); } else { thisPolyline = polyline1; } const otherPoints = polyline2.points; const { length } = otherPoints; const segment = new Line(); for (let i = 0; i < length - 1; i++) { segment.start = otherPoints[i]; segment.end = otherPoints[i + 1]; if (polylineWithLine(thisPolyline, segment)) { return true; } } return false; } function _polylineWithPolygon(polyline, polygon, opt) { return polygon.containsPoint(polyline.start) || _polylineWithPolyline(polyline, polygon.clone().close(), opt); } function _ellipsesIntersection(e1, w1, e2, w2) { const { cos, sin } = Math; const sinW1 = sin(w1); const cosW1 = cos(w1); const sinW2 = sin(w2); const cosW2 = cos(w2); const sinW1s = sinW1 * sinW1; const cosW1s = cosW1 * cosW1; const sinCos1 = sinW1 * cosW1; const sinW2s = sinW2 * sinW2; const cosW2s = cosW2 * cosW2; const sinCos2 = sinW2 * cosW2; const a1s = e1.a * e1.a; const b1s = e1.b * e1.b; const a2s = e2.a * e2.a; const b2s = e2.b * e2.b; const A1 = a1s * sinW1s + b1s * cosW1s; const A2 = a2s * sinW2s + b2s * cosW2s; const B1 = a1s * cosW1s + b1s * sinW1s; const B2 = a2s * cosW2s + b2s * sinW2s; let C1 = 2 * (b1s - a1s) * sinCos1; let C2 = 2 * (b2s - a2s) * sinCos2; let D1 = (-2 * A1 * e1.x - C1 * e1.y); let D2 = (-2 * A2 * e2.x - C2 * e2.y); let E1 = (-C1 * e1.x - 2 * B1 * e1.y); let E2 = (-C2 * e2.x - 2 * B2 * e2.y); const F1 = A1 * e1.x * e1.x + B1 * e1.y * e1.y + C1 * e1.x * e1.y - a1s * b1s; const F2 = A2 * e2.x * e2.x + B2 * e2.y * e2.y + C2 * e2.x * e2.y - a2s * b2s; C1 = C1 / 2; C2 = C2 / 2; D1 = D1 / 2; D2 = D2 / 2; E1 = E1 / 2; E2 = E2 / 2; const l3 = det3([ [A1, C1, D1], [C1, B1, E1], [D1, E1, F1] ]); const l0 = det3([ [A2, C2, D2], [C2, B2, E2], [D2, E2, F2] ]); const l2 = 0.33333333 * (det3([ [A2, C1, D1], [C2, B1, E1], [D2, E1, F1] ]) + det3([ [A1, C2, D1], [C1, B2, E1], [D1, E2, F1] ]) + det3([ [A1, C1, D2], [C1, B1, E2], [D1, E1, F2] ])); const l1 = 0.33333333 * (det3([ [A1, C2, D2], [C1, B2, E2], [D1, E2, F2] ]) + det3([ [A2, C1, D2], [C2, B1, E2], [D2, E1, F2] ]) + det3([ [A2, C2, D1], [C2, B2, E1], [D2, E2, F1] ])); const delta1 = det2([ [l3, l2], [l2, l1] ]); const delta2 = det2([ [l3, l1], [l2, l0] ]); const delta3 = det2([ [l2, l1], [l1, l0] ]); const dP = det2([ [2 * delta1, delta2], [delta2, 2 * delta3] ]); if (dP > 0 && (l1 > 0 || l2 > 0)) { return false; } return true; } function det2(m) { return m[0][0] * m[1][1] - m[0][1] * m[1][0]; } function det3(m) { return m[0][0] * m[1][1] * m[2][2] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][1] * m[1][2] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][2] * m[1][1] * m[2][0]; }