autocad-dxf
Version:
A module which can be used to parse AutoCAD dxf files and to make programmatic and geometric operations on the AutoCAD drawing entities.
1,270 lines (1,190 loc) • 56.6 kB
JavaScript
const Intersection = require("./Intersection");
const Distance = require("./Distance");
const CheckPointInside = require("./PolylineInside");
const BSpline = require("./BSpline");
const ErrorMessages = require("./ErrorMessages.json");
const checkInside = (data, item, plane, getAxes, tolerance) => {
const etype = item.subclass;
let [ax1, ax2] = getAxes(plane);
if (plane && ax1 === undefined && ax2 === undefined) {
throw new Error(ErrorMessages.INCORRECT_PARAMS);
return;
}
if (etype == "AcDbLine" || (etype == "AcDbDimension" && (item.specific_type == 'AcDbRotatedDimension' || item.specific_type == 'AcDbAlignedDimension'))) {
let x, y;
if (Array.isArray(data)) {
x = data[0];
y = data[1];
} else if (typeof data == "object" && data.subclass == "AcDbPoint") {
x = data[ax1];
y = data[ax2];
}
if (isNaN(x) || isNaN(y)) {
return false;
} else {
let x0, x1, y0, y1;
if (etype == "AcDbLine") {
x0 = item[`start_${ax1}`];
y0 = item[`start_${ax2}`];
x1 = item[`end_${ax1}`];
y1 = item[`end_${ax2}`];
} else {
x0 = item[`${ax1}`];
y0 = item[`${ax2}`];
x1 = item[`${ax1}_end`];
y1 = item[`${ax2}_end`];
}
const m = (y0 - y1)/(x0 - x1);
if (Math.abs(m) == Infinity) {
return x == x0 && (y - y0)*(y - y1) < tolerance;
} else {
return Math.abs(y - (m*x + (y0 - m*x0))) < tolerance && (x - x0)*(x - x1) < tolerance && (y - y0)*(y - y1) < tolerance;
}
}
} else if (etype == "AcDbCircle") {
if (item.etype == "ARC") return false;
const xc = item[ax1];
const yc = item[ax2];
const radius = item.radius;
if (Array.isArray(data)) {
const x = data[0];
const y = data[1];
return ((x - xc)*(x - xc) + (y - yc)*(y - yc) <= (radius + tolerance)*(radius + tolerance));
} else if (typeof data == "object") {
data = JSON.parse(JSON.stringify(data));
const etype2 = data.subclass;
if (etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) {
let x0, x1, y0, y1;
if (etype2 == "AcDbLine") {
x0 = data[`start_${ax1}`];
y0 = data[`start_${ax2}`];
x1 = data[`end_${ax1}`];
y1 = data[`end_${ax2}`];
} else {
x0 = data[`${ax1}`];
y0 = data[`${ax2}`];
x1 = data[`${ax1}_end`];
y1 = data[`${ax2}_end`];
}
const startPointInside = checkInside([x0, y0], item, plane, getAxes, tolerance);
const endPointInside = checkInside([x1, y1], item, plane, getAxes, tolerance);
return startPointInside && endPointInside;
} else if (etype2 == "AcDbPoint" || etype2 == "AcDbText" || etype2 == "AcDbMText" || etype2 == "AcDbDimension") {
data = [data[ax1], data[ax2]];
return checkInside(data, item, plane, getAxes, tolerance);
} else if (etype2 == "AcDbPolyline") {
const vertices = data.vertices;
for (let i = 0; i < vertices.length; i++) {
const vertexInside = checkInside([vertices[i][ax1], vertices[i][ax2]], item, plane, getAxes, tolerance);
if (!vertexInside) return false;
}
return true;
} else if (etype2 == "AcDbCircle" && data.etype == "CIRCLE") {
const radius2 = data.radius;
if (radius2 > (radius + tolerance)) {
return false;
} else {
let temp = JSON.parse(JSON.stringify(item));
temp.radius = temp.radius - data.radius;
data = [data[ax1], data[ax2]];
return checkInside(data, temp, plane, getAxes, tolerance);
}
} else if (etype2 == "AcDbCircle" && data.etype == "ARC") {
const radius2 = data.radius;
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let sa = data.start_angle*Math.PI/180, ea = data.end_angle*Math.PI/180;
const x1 = data[ax1] + data.radius*Math.cos(sa);
const y1 = data[ax2] + data.radius*Math.sin(sa);
const x2 = data[ax1] + data.radius*Math.cos(ea);
const y2 = data[ax2] + data.radius*Math.sin(ea);
if ((!intersection || intersection.length == 0) && checkInside([x1, y1], item, plane, getAxes, tolerance)) { // there should not be intersection and any of the end points should be inside
return true;
} else if (intersection.length == 2 && checkInside([x1, y1], item, plane, getAxes, tolerance) && checkInside([x2, y2], item, plane, getAxes, tolerance)) { // there is intersection but both of the end points should be on the circle and the mid point along the arc must be outside
if (sa > ea) {
sa = sa - 2*Math.PI;
}
const x_mid = data[ax1] + data.radius*Math.cos((sa + ea)/2);
const y_mid = data[ax2] + data.radius*Math.sin((sa + ea)/2);
return checkInside([x_mid, y_mid], item, plane, getAxes, tolerance);
} else if (intersection.length == 1 && checkInside([x1, y1], item, plane, getAxes, tolerance) && checkInside([x2, y2], item, plane, getAxes, tolerance)) { // there is tangency and the arc ends must be inside
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
} else if (etype == "AcDbPolyline") {
const vertices = JSON.parse(JSON.stringify(item.vertices));
if (Array.isArray(data)) {
const x = data[0], y = data[1];
return CheckPointInside(vertices, x, y, ax1, ax2);
} else if (typeof data == "object") {
const etype2 = data.subclass;
if (etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) {
let x0, x1, y0, y1;
if (etype2 == "AcDbLine") {
x0 = data[`start_${ax1}`];
y0 = data[`start_${ax2}`];
x1 = data[`end_${ax1}`];
y1 = data[`end_${ax2}`];
} else {
x0 = data[`${ax1}`];
y0 = data[`${ax2}`];
x1 = data[`${ax1}_end`];
y1 = data[`${ax2}_end`];
}
const ipt = Intersection(data, item, plane, getAxes, tolerance);
if (ipt && ipt.length > 0) {
for (let j = 0; j < ipt.length; j++) {
if ((Math.abs(ipt[j][ax1] - x0) > tolerance || Math.abs(ipt[j][ax2] - y0) > tolerance) &&
(Math.abs(ipt[j][ax1] - x1) > tolerance || Math.abs(ipt[j][ax2] - y1) > tolerance)) {
return false;
}
}
}
const startPointInside = CheckPointInside(vertices, x0, y0, ax1, ax2);
const endPointInside = CheckPointInside(vertices, x1, y1, ax1, ax2);
return startPointInside && endPointInside;
} else if (etype2 == "AcDbPoint" || etype2 == "AcDbText" || etype2 == "AcDbMText" || etype2 == "AcDbDimension") {
return CheckPointInside(vertices, data[ax1], data[ax2], ax1, ax2);
} else if (etype2 == "AcDbPolyline") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
const vert = data.vertices;
if (!intersection || intersection.length == 0) {
for (let i = 0; i < vert.length; i++) {
let x = vert[i][ax1], y = vert[i][ax2];
const vertexInside = CheckPointInside(vertices, x, y, ax1, ax2);
if (!vertexInside) return false;
}
return true;
} else {
for (let i = 0; i < intersection.length; i++) {
let isVertex = false;
const x = intersection[i][ax1];
const y = intersection[i][ax2];
for (let j = 0; j < vert.length; j++) {
const pointInside = CheckPointInside(vertices, vert[j][ax1], vert[j][ax2], ax1, ax2);
if (!pointInside) return false;
if (Math.abs(x - vert[j][ax1]) < tolerance && Math.abs(y - vert[j][ax2]) < tolerance) {
isVertex = true;
}
}
if (!isVertex) return false;
}
return true;
}
} else if (etype2 == "AcDbCircle" && data.etype == "CIRCLE") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let x = data[ax1], y = data[ax2], r = data.radius;
if (!intersection || intersection.length == 0) {
return CheckPointInside(vertices, x, y, ax1, ax2);
} else { // center inside, all intersections are tangents
const centerInside = CheckPointInside(vertices, x, y, ax1, ax2);
if (!centerInside) {
return false;
}
for (let i = 0; i < intersection.length; i++) {
if (!intersection[i].tangency_point) {
return false
}
}
return true;
}
} else if (etype2 == "AcDbCircle" && data.etype == "ARC") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
const x1 = data[ax1] + data.radius*Math.cos(data.start_angle);
const y1 = data[ax2] + data.radius*Math.sin(data.start_angle);
const x2 = data[ax1] + data.radius*Math.cos(data.end_angle);
const y2 = data[ax2] + data.radius*Math.sin(data.end_angle);
const p1Inside = CheckPointInside(vertices, x1, y1, ax1, ax2);
const p2Inside = CheckPointInside(vertices, x2, y2, ax1, ax2);
if ((!intersection || intersection.length == 0) && p1Inside) { // there should not be intersection and any of the end points should be inside
return true;
} else if (intersection.length == 2 && p1Inside && p2Inside &&
!CheckPointInside(vertices, data[ax1], data[ax2], ax1, ax2)) { // there is intersection but both of the end points should be on the circle and the center must be outside
return true;
} else if (intersection.length == 1 && p1Inside && p2Inside) { // there is tangency and the arc ends must be inside
return true;
} else {
return false;
}
} else if (etype2 == "AcDbEllipse") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let x = data[ax1], y = data[ax2];
if (!intersection || intersection.length == 0) {
if (!isNaN(data[`start_${ax1}`]) && !isNaN(data[`start_${ax2}`])) {
return CheckPointInside(vertices, data[`start_${ax1}`], data[`start_${ax2}`], ax1, ax2);
} else {
return CheckPointInside(vertices, x, y, ax1, ax2);
}
} else { // center inside, all intersections are tangents or ellipse end points
if (!isNaN(data[`start_${ax1}`]) && !isNaN(data[`start_${ax2}`])) {
const x0 = data[`start_${ax1}`];
const y0 = data[`start_${ax2}`];
const x1 = data[`end_${ax1}`];
const y1 = data[`end_${ax2}`];
const p1Inside = CheckPointInside(vertices, x0, y0, ax1, ax2);
if (!p1Inside) {
return false;
}
const p2Inside = CheckPointInside(vertices, x1, y1, ax1, ax2);
if (!p2Inside) {
return false;
}
let dx = data[`major_end_d${ax1}`];
let dy = data[`major_end_d${ax2}`];
const ratio = data.minorToMajor;
const a = Math.sqrt(dx*dx + dy*dy);
const b = ratio*a;
const theta = Math.atan2(dy, dx);
let sa = data.start_parameter;
let ea = data.end_parameter;
if (sa > ea) {
sa = sa - 2*Math.PI;
}
const mid = (sa + ea)/2;
const mid_x = data[ax1] + a*Math.cos(mid)*Math.cos(theta) - b*Math.sin(mid)*Math.sin(theta);
const mid_y = data[ax2] + a*Math.cos(mid)*Math.sin(theta) + b*Math.sin(mid)*Math.cos(theta);
const midInside = CheckPointInside(vertices, mid_x, mid_y, ax1, ax2);
if (!midInside) {
return false;
}
for (let i = 0; i < intersection.length; i++) {
const endPt = (Math.abs(intersection[i][ax1] - x0) < tolerance && Math.abs(intersection[i][ax2] - y0) < tolerance) ||
(Math.abs(intersection[i][ax1] - x1) < tolerance && Math.abs(intersection[i][ax2] - y1) < tolerance);
if (!intersection[i].tangency_point && !endPt) {
return false
}
}
} else {
const centerInside = CheckPointInside(vertices, x, y, ax1, ax2);
if (!centerInside) {
return false;
}
for (let i = 0; i < intersection.length; i++) {
if (!intersection[i].tangency_point) {
return false
}
}
}
return true;
}
} else {
return false;
}
}
} else if (etype == "AcDbEllipse") {
const sa = item.start_parameter;
const ea = item.end_parameter;
if (Math.abs(Math.abs(ea - sa) - 2*Math.PI) > tolerance) {
return false;
}
const xc = item[ax1];
const yc = item[ax2];
const dx = item[`major_end_d${ax1}`];
const dy = item[`major_end_d${ax2}`];
const ratio = item.minorToMajor;
const a = Math.sqrt(dx*dx + dy*dy);
const b = ratio*a;
const theta = Math.atan2(dy, dx);
const A = Math.pow(a*Math.sin(theta), 2) + Math.pow(b*Math.cos(theta), 2);
const B = 2*(b*b - a*a)*Math.sin(theta)*Math.cos(theta);
const C = Math.pow(a*Math.cos(theta), 2) + Math.pow(b*Math.sin(theta), 2);
const D = -2*A*xc - B*yc;
const E = -B*xc - 2*C*yc;
const F = A*xc*xc + B*xc*yc + C*yc*yc - a*a*b*b;
if (Array.isArray(data)) {
const x = data[0];
const y = data[1];
return (A*x*x + B*x*y + C*y*y + D*x + E*y + F) <= tolerance;
} else if (typeof data == "object") {
data = JSON.parse(JSON.stringify(data));
const etype2 = data.subclass;
if (etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) {
let x0, x1, y0, y1;
if (etype2 == "AcDbLine") {
x0 = data[`start_${ax1}`];
y0 = data[`start_${ax2}`];
x1 = data[`end_${ax1}`];
y1 = data[`end_${ax2}`];
} else {
x0 = data[`${ax1}`];
y0 = data[`${ax2}`];
x1 = data[`${ax1}_end`];
y1 = data[`${ax2}_end`];
}
const startPointInside = checkInside([x0, y0], item, plane, getAxes, tolerance);
const endPointInside = checkInside([x1, y1], item, plane, getAxes, tolerance);
return startPointInside && endPointInside;
} else if (etype2 == "AcDbPoint" || etype2 == "AcDbText" || etype2 == "AcDbMText" || etype2 == "AcDbDimension") {
data = [data[ax1], data[ax2]];
return checkInside(data, item, plane, getAxes, tolerance);
} else if (etype2 == "AcDbPolyline") {
const vertices = data.vertices;
for (let i = 0; i < vertices.length; i++) {
data = [vertices[i][ax1], vertices[i][ax2]];
const vertexInside = checkInside(data, item, plane, getAxes, tolerance);
if (!vertexInside) return false;
}
return true;
} else if (etype2 == "AcDbCircle") {
return false; // to be implemented
} else if (etype2 == "AcDbArc") {
return false; // to be implemented
} else {
return false;
}
} else {
return false;
}
}
return false;
};
const checkOutside = (data, item, plane, getAxes, tolerance) => {
const etype = item.subclass;
let [ax1, ax2] = getAxes(plane);
if (plane && ax1 === undefined && ax2 === undefined) {
throw new Error(ErrorMessages.INCORRECT_PARAMS);
return;
}
if (etype == "AcDbPoint" || etype == "AcDbMLine" || etype == "AcDbText" || etype == "AcDbMText" || etype == "AcDbSpline") {
return false;
} else if (etype == "AcDbLine" || (etype == "AcDbDimension" && (item.specific_type == 'AcDbRotatedDimension' || item.specific_type == 'AcDbAlignedDimension'))) {
let x, y;
if (Array.isArray(data)) {
x = data[0];
y = data[1];
} else if (typeof data == "object") {
x = data[ax1];
y = data[ax2];
}
if (isNaN(x) || isNaN(y)) {
return false;
} else {
let x0, x1, y0, y1;
if (etype == "AcDbLine") {
x0 = item[`start_${ax1}`];
y0 = item[`start_${ax2}`];
x1 = item[`end_${ax1}`];
y1 = item[`end_${ax2}`];
} else {
x0 = item[`${ax1}`];
y0 = item[`${ax2}`];
x1 = item[`${ax1}_end`];
y1 = item[`${ax2}_end`];
}
const m = (y0 - y1)/(x0 - x1);
const intersection = Intersection(data, item, plane, getAxes, tolerance);
if (intersection.length > 1) {
return false;
}
if (intersection.length == 1 && (Math.abs(x - x0) > tolerance || Math.abs(y - y0) > tolerance) && (Math.abs(x - x1) > tolerance || Math.abs(y - y1) > tolerance)) {
return false;
}
if (Math.abs(m) == Infinity) {
return Math.abs(x - x0) > tolerance || (y - y0)*(y - y1) > tolerance;
} else {
return Math.abs(y - (m*x + (y0 - m*x0))) > tolerance || (x - x0)*(x - x1) > tolerance || (y - y0)*(y - y1) > tolerance;
}
}
} else if (etype == "AcDbCircle") {
const xc = item[ax1];
const yc = item[ax2];
const radius = item.radius;
if (Array.isArray(data)) {
const x = data[0];
const y = data[1];
return ((x - xc)*(x - xc) + (y - yc)*(y - yc) > (radius + tolerance)*(radius + tolerance));
} else if (typeof data == "object") {
data = JSON.parse(JSON.stringify(data));
const etype2 = data.subclass;
if (etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
if (intersection.length > 1) {
return false;
} else {
let x0, x1, y0, y1;
if (etype2 == "AcDbLine") {
x0 = data[`start_${ax1}`];
y0 = data[`start_${ax2}`];
x1 = data[`end_${ax1}`];
y1 = data[`end_${ax2}`];
} else {
x0 = data[`${ax1}`];
y0 = data[`${ax2}`];
x1 = data[`${ax1}_end`];
y1 = data[`${ax2}_end`];
}
const startPointOutside = checkOutside([x0, y0], item, plane, getAxes, tolerance);
const endPointOutside = checkOutside([x1, y1], item, plane, getAxes, tolerance);
if (intersection.length == 1) {
const xi = intersection[0][ax1];
const yi = intersection[0][ax2];
return (((startPointOutside || endPointOutside) && ((Math.abs(x0 - xi) < tolerance && Math.abs(y0 - yi) < tolerance) ||
(Math.abs(x1 - xi) < tolerance && Math.abs(y1 - yi) < tolerance))) || // both points outside or one point outside and one point on the circle
(startPointOutside && endPointOutside && intersection[0].tangency_point)); // both points outside but the line is tangent
} else {
return startPointOutside && endPointOutside;
}
}
} else if (etype2 == "AcDbPoint" || etype2 == "AcDbText" || etype2 == "AcDbMText" || etype2 == "AcDbDimension") {
data = [data[ax1], data[ax2]];
return checkOutside(data, item, plane, getAxes, tolerance);
} else if (etype2 == "AcDbPolyline") {
const vertices = JSON.parse(JSON.stringify(data.vertices));
if (data.type == "Closed") {
vertices.push(vertices[0]);
}
for (let i = 1; i < vertices.length; i++) {
const x0 = vertices[i - 1][ax1];
const y0 = vertices[i - 1][ax2];
const x1 = vertices[i][ax1];
const y1 = vertices[i][ax2];
const json = {subclass: "AcDbLine"};
json[`start_${ax1}`] = x0;
json[`start_${ax2}`] = y0;
json[`end_${ax1}`] = x1;
json[`end_${ax2}`] = y1;
const vertexOutside = checkOutside(json, item, plane, getAxes, tolerance);
if (!vertexOutside) return false;
}
return true;
} else if (etype2 == "AcDbCircle" && data.etype == "CIRCLE") {
const radius2 = data.radius;
if (radius2 > (radius + tolerance)) {
return true;
} else {
let temp = JSON.parse(JSON.stringify(item));
temp.radius = temp.radius - data.radius;
data = [data[ax1], data[ax2]];
return checkOutside(data, temp, plane, getAxes, tolerance);
}
} else if (etype2 == "AcDbCircle" && data.etype == "ARC") {
const radius2 = data.radius;
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let sa = data.start_angle*Math.PI/180, ea = data.end_angle*Math.PI/180;
const x1 = data[ax1] + data.radius*Math.cos(sa);
const y1 = data[ax2] + data.radius*Math.sin(sa);
const x2 = data[ax1] + data.radius*Math.cos(ea);
const y2 = data[ax2] + data.radius*Math.sin(ea);
if ((!intersection || intersection.length == 0) && checkOutside([x1, y1], item, plane, getAxes, tolerance)) { // there should not be intersection and any of the end points should be inside
return true;
} else if (intersection.length == 2 && checkInside([x1, y1], item, plane, getAxes, tolerance) && checkInside([x2, y2], item, plane, getAxes, tolerance)) { // there is intersection but both of the end points should be on the circle and the mid point along the arc must be outside
if (sa > ea) {
sa = sa - 2*Math.PI;
}
const x_mid = data[ax1] + data.radius*Math.cos((sa + ea)/2);
const y_mid = data[ax2] + data.radius*Math.sin((sa + ea)/2);
return !checkInside([x_mid, y_mid], item, plane, getAxes, tolerance);
} else if (intersection.length == 1 &&
(((intersection[0][ax1] - x1) < tolerance && (intersection[0][ax2] - y1) < tolerance && checkOutside([x2, y2], item, plane, getAxes, tolerance)) ||
((intersection[0][ax1] - x2) < tolerance && (intersection[0][ax2] - y2) < tolerance && checkOutside([x1, y1], item, plane, getAxes, tolerance)))) { // there is tangency and the arc ends must be inside
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
} else if (etype == "AcDbPolyline") {
const vertices = JSON.parse(JSON.stringify(item.vertices));
if (Array.isArray(data)) {
const x = data[0], y = data[1];
return !CheckPointInside(vertices, x, y, ax1, ax2);
} else if (typeof data == "object") {
const etype2 = data.subclass;
if (etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) {
let x0, x1, y0, y1;
if (etype2 == "AcDbLine") {
x0 = data[`start_${ax1}`];
y0 = data[`start_${ax2}`];
x1 = data[`end_${ax1}`];
y1 = data[`end_${ax2}`];
} else {
x0 = data[`${ax1}`];
y0 = data[`${ax2}`];
x1 = data[`${ax1}_end`];
y1 = data[`${ax2}_end`];
}
const ipt = Intersection(data, item, plane, getAxes, tolerance);
const startPointInside = CheckPointInside(vertices, x0, y0, ax1, ax2);
if (!ipt || ipt.length == 0) {
return !startPointInside;
} else {
ipt.sort((a, b) => x1 > x0 && a[ax1] > b[ax1] ? 1 : (x1 == x0 && y1 > y0 ? (a[ax2] > b[ax2] ? 1: -1): -1));
const d1 = (ipt[0][ax1] - x0)*(ipt[0][ax1] - x0) + (ipt[0][ax2] - y0)*(ipt[0][ax2] - y0);
const d2 = (ipt[0][ax1] - x1)*(ipt[0][ax1] - x1) + (ipt[0][ax2] - y1)*(ipt[0][ax2] - y1);
if (d1 > tolerance) {
const x_mid = (ipt[0][ax1] + x0)/2;
const y_mid = (ipt[0][ax2] + y0)/2;
const midPointInside = CheckPointInside(vertices, x_mid, y_mid, ax1, ax2);
if (midPointInside) return false;
}
if (d2 > tolerance) {
const x_mid = (ipt[ipt.length - 1][ax1] + x1)/2;
const y_mid = (ipt[ipt.length - 1][ax2] + y1)/2;
const midPointInside = CheckPointInside(vertices, x_mid, y_mid, ax1, ax2);
if (midPointInside) return false;
}
for (let j = 1; j < ipt.length; j++) { // any mid point between end points and intersection points or between two consecutive intersection points should be outside
if ((Math.abs(ipt[j][ax1] - x0) < tolerance && Math.abs(ipt[j][ax2] - y0) < tolerance) ||
(Math.abs(ipt[j][ax1] - x1) < tolerance && Math.abs(ipt[j][ax2] - y1) < tolerance)) {
continue;
}
const x_mid = (ipt[j][ax1] + ipt[j - 1][ax1])/2;
const y_mid = (ipt[j][ax2] + ipt[j - 1][ax2])/2;
const midPointInside = CheckPointInside(vertices, x_mid, y_mid, ax1, ax2);
if (midPointInside) return false;
}
}
return true;
} else if (etype2 == "AcDbPoint" || etype2 == "AcDbText" || etype2 == "AcDbMText" || etype2 == "AcDbDimension") {
return !CheckPointInside(vertices, data[ax1], data[ax2], ax1, ax2);
} else if (etype2 == "AcDbPolyline") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
const vert = data.vertices;
if (!intersection || intersection.length == 0) {
for (let i = 0; i < vert.length; i++) {
let x = vert[i][ax1], y = vert[i][ax2];
const vertexInside = CheckPointInside(vertices, x, y, ax1, ax2);
if (vertexInside) return false;
}
return true;
} else {
// all the intersection points should be one of the vertices of either polyline and at least one vertex of the outer polyline should be outside;
let onePointOutside = false;
for (let i = 0; i < intersection.length; i++) {
let isVertex = false;
const x = intersection[i][ax1];
const y = intersection[i][ax2];
for (let j = 0; j < vert.length; j++) {
if (!onePointOutside) {
const pointInside = CheckPointInside(vertices, vert[j][ax1], vert[j][ax2], ax1, ax2);
if (!pointInside) {
onePointOutside = true;
}
}
if (Math.abs(x - vert[j][ax1]) < tolerance && Math.abs(y - vert[j][ax2]) < tolerance) {
isVertex = true;
}
}
for (let j = 0; j < vertices.length; j++) {
if (Math.abs(x - vertices[j][ax1]) < tolerance && Math.abs(y - vertices[j][ax2]) < tolerance) {
isVertex = true;
break;
}
}
if (!isVertex) return false;
}
return onePointOutside;
}
} else if (etype2 == "AcDbCircle" && data.etype == "CIRCLE") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let x = data[ax1], y = data[ax2], r = data.radius;
if (!intersection || intersection.length == 0) {
return !CheckPointInside(vertices, x, y, ax1, ax2);
} else { // center inside, all intersections are tangents
const centerInside = CheckPointInside(vertices, x, y, ax1, ax2);
if (centerInside) {
return false;
}
for (let i = 0; i < intersection.length; i++) {
if (!intersection[i].tangency_point) {
return false
}
}
return true;
}
} else if (etype2 == "AcDbCircle" && data.etype == "ARC") {
const radius2 = data.radius;
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let sa = data.start_angle*Math.PI/180, ea = data.end_angle*Math.PI/180;
const x1 = data[ax1] + data.radius*Math.cos(sa);
const y1 = data[ax2] + data.radius*Math.sin(sa);
const x2 = data[ax1] + data.radius*Math.cos(ea);
const y2 = data[ax2] + data.radius*Math.sin(ea);
if ((!intersection || intersection.length == 0) && checkOutside([x1, y1], item, plane, getAxes, tolerance)) { // there should not be intersection and any of the end points should be inside
return true;
} else if (intersection.length == 2 && checkInside([x1, y1], item, plane, getAxes, tolerance) && checkInside([x2, y2], item, plane, getAxes, tolerance)) { // there is intersection but both of the end points should be on the circle and the mid point along the arc must be outside
if (sa > ea) {
sa = sa - 2*Math.PI;
}
const x_mid = data[ax1] + data.radius*Math.cos((sa + ea)/2);
const y_mid = data[ax2] + data.radius*Math.sin((sa + ea)/2);
return !checkInside([x_mid, y_mid], item, plane, getAxes, tolerance);
} else if (intersection.length == 1 &&
(((intersection[0][ax1] - x1) < tolerance && (intersection[0][ax2] - y1) < tolerance && checkOutside([x2, y2], item, plane, getAxes, tolerance)) ||
((intersection[0][ax1] - x2) < tolerance && (intersection[0][ax2] - y2) < tolerance && checkOutside([x1, y1], item, plane, getAxes, tolerance)))) { // there is tangency and the arc ends must be inside
return true;
} else {
return false;
}
} else if (etype2 == "AcDbEllipse") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let x = data[ax1], y = data[ax2];
if (!intersection || intersection.length == 0) {
if (!isNaN(data[`start_${ax1}`]) && !isNaN(data[`start_${ax2}`])) {
return !CheckPointInside(vertices, data[`start_${ax1}`], data[`start_${ax2}`], ax1, ax2);
} else {
return !CheckPointInside(vertices, x, y, ax1, ax2);
}
} else { // center inside, all intersections are tangents
if (!isNaN(data[`start_${ax1}`]) && !isNaN(data[`start_${ax2}`])) {
const x0 = data[`start_${ax1}`];
const y0 = data[`start_${ax2}`];
const x1 = data[`end_${ax1}`];
const y1 = data[`end_${ax2}`];
let p1EndPoint = false, p2EndPoint = false;
for (let j = 0; j < intersection.length; j++) {
const x = intersection[j][ax1];
const y = intersection[j][ax2];
if (!p1EndPoint && Math.abs(x0 - x) < tolerance && Math.abs(y0 - y) < tolerance && !intersection[j].tangency_point) {
p1EndPoint = true;
}
if (!p2EndPoint && Math.abs(x1 - x) < tolerance && Math.abs(y1 - y) < tolerance && !intersection[j].tangency_point) {
p2EndPoint = true;
}
}
const p1Inside = CheckPointInside(vertices, x0, y0, ax1, ax2);
if (p1Inside && !p1EndPoint) {
return false;
}
const p2Inside = CheckPointInside(vertices, x1, y1, ax1, ax2);
if (p2Inside && !p2EndPoint) {
return false;
}
let dx = data[`major_end_d${ax1}`];
let dy = data[`major_end_d${ax2}`];
const ratio = data.minorToMajor;
const a = Math.sqrt(dx*dx + dy*dy);
const b = ratio*a;
const theta = Math.atan2(dy, dx);
let sa = data.start_parameter;
let ea = data.end_parameter;
if (sa > ea) {
sa = sa - 2*Math.PI;
}
const mid = (sa + ea)/2;
const mid_x = data[ax1] + a*Math.cos(mid)*Math.cos(theta) - b*Math.sin(mid)*Math.sin(theta);
const mid_y = data[ax2] + a*Math.cos(mid)*Math.sin(theta) + b*Math.sin(mid)*Math.cos(theta);
const midInside = CheckPointInside(vertices, mid_x, mid_y, ax1, ax2);
if (midInside) {
return false;
}
} else {
const centerInside = CheckPointInside(vertices, x, y, ax1, ax2);
if (centerInside) {
return false;
}
for (let i = 0; i < intersection.length; i++) {
if (!intersection[i].tangency_point) {
return false
}
}
}
return true;
}
} else {
return false;
}
}
} else if (etype == "AcDbEllipse") {
const sa = item.start_parameter;
const ea = item.end_parameter;
if (Math.abs(Math.abs(ea - sa) - 2*Math.PI) > tolerance) {
return false;
}
const xc = item[ax1];
const yc = item[ax2];
const dx = item[`major_end_d${ax1}`];
const dy = item[`major_end_d${ax2}`];
const ratio = item.minorToMajor;
const a = Math.sqrt(dx*dx + dy*dy);
const b = ratio*a;
const theta = Math.atan2(dy, dx);
const A = Math.pow(a*Math.sin(theta), 2) + Math.pow(b*Math.cos(theta), 2);
const B = 2*(b*b - a*a)*Math.sin(theta)*Math.cos(theta);
const C = Math.pow(a*Math.cos(theta), 2) + Math.pow(b*Math.sin(theta), 2);
const D = -2*A*xc - B*yc;
const E = -B*xc - 2*C*yc;
const F = A*xc*xc + B*xc*yc + C*yc*yc - a*a*b*b;
if (Array.isArray(data)) {
const x = data[0];
const y = data[1];
return (A*x*x + B*x*y + C*y*y + D*x + E*y + F) > tolerance;
} else if (typeof data == "object") {
data = JSON.parse(JSON.stringify(data));
const etype2 = data.subclass;
if (etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
let x0, x1, y0, y1;
if (etype2 == "AcDbLine") {
x0 = data[`start_${ax1}`];
y0 = data[`start_${ax2}`];
x1 = data[`end_${ax1}`];
y1 = data[`end_${ax2}`];
} else {
x0 = data[`${ax1}`];
y0 = data[`${ax2}`];
x1 = data[`${ax1}_end`];
y1 = data[`${ax2}_end`];
}
const startPointOutside = checkOutside([x0, y0], item, plane, getAxes, tolerance);
const endPointOutside = checkOutside([x1, y1], item, plane, getAxes, tolerance);
if (!intersection || intersection.length == 0) {
return startPointOutside && endPointOutside;
} else if (intersection.length > 1) {
return false;
} else {
const i = intersection[0];
const tangent = i.tangency_point;
const endPt = ((Math.abs(i[ax1] - x0) < tolerance && Math.abs(i[ax2] - y0) < tolerance)) ||
((Math.abs(i[ax1] - x1) < tolerance && Math.abs(i[ax2] - y1) < tolerance));
return (startPointOutside || endPointOutside) && (tangent || endPt);
}
} else if (etype2 == "AcDbPoint" || etype2 == "AcDbText" || etype2 == "AcDbMText" || etype2 == "AcDbDimension") {
data = [data[ax1], data[ax2]];
return !checkInside(data, item, plane, getAxes, tolerance);
} else if (etype2 == "AcDbPolyline") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
if (!intersection || intersection.length == 0) {
const vertices = data.vertices;
for (let i = 0; i < vertices.length; i++) {
data = [vertices[i][ax1], vertices[i][ax2]];
const vertexOutside = checkOutside(data, item, plane, getAxes, tolerance);
if (!vertexOutside) return false;
}
} else {
const vertices = JSON.parse(JSON.stringify(data.vertices));
if (data.type == "Closed") {
vertices.push(vertices[0]);
}
for (let i = 0; i < intersection.length; i++) {
const x = intersection[i][ax1];
const y = intersection[i][ax2];
let endPoint = false;
for (let j = 0; j < vertices.length; j++) {
if (j > 0) {
const mid_x = (vertices[j - 1][ax1] + vertices[j][ax1])/2;
const mid_y = (vertices[j - 1][ax2] + vertices[j][ax2])/2;
const midOutside = checkOutside([mid_x, mid_y], item, plane, getAxes, tolerance);
if (!midOutside) return false;
}
if (Math.abs(vertices[j][ax1] - x) < tolerance && Math.abs(vertices[j][ax2] - y) < tolerance) {
endPoint = true;
break;
}
}
if (!endPoint) return false;
}
}
return true;
} else if (etype2 == "AcDbCircle") {
return false; // to be implemented
} else if (etype2 == "AcDbArc") {
return false; // to be implemented
} else {
return false;
}
} else {
return false;
}
}
return false;
};
const checkOnside = (data, item, plane, getAxes, tolerance) => {
const etype = item.subclass;
let [ax1, ax2] = getAxes(plane);
if (plane && ax1 === undefined && ax2 === undefined) {
throw new Error(ErrorMessages.INCORRECT_PARAMS);
return;
}
const x = Array.isArray(data) ? data[0] : data[ax1];
const y = Array.isArray(data) ? data[1] : data[ax2];
if (etype == "AcDbLine" || (etype == "AcDbDimension" && (item.specific_type == 'AcDbRotatedDimension' || item.specific_type == 'AcDbAlignedDimension'))) {
let x0, x1, y0, y1;
if (etype == "AcDbLine") {
x0 = item[`start_${ax1}`];
y0 = item[`start_${ax2}`];
x1 = item[`end_${ax1}`];
y1 = item[`end_${ax2}`];
} else {
x0 = item[`${ax1}`];
y0 = item[`${ax2}`];
x1 = item[`${ax1}_end`];
y1 = item[`${ax2}_end`];
}
const m = (y1 - y0)/(x1 - x0);
if (Math.abs(m) == Infinity) {
return (y0 - y)*(y1 - y) < tolerance && (Math.abs(x - x0) <= tolerance);
} else {
const isInside = (x0 - x)*(x1 - x) < tolerance && (y0 - y)*(y1 - y) < tolerance;
return isInside && (Math.abs(y - (m*(x - x1) + y1)) <= tolerance);
}
} else if (etype == "AcDbPoint" || etype == "AcDbText" || etype == "AcDbMText" || etype == "AcDbDimension") {
return Math.abs(x - item[ax1]) < tolerance && Math.abs(y - item[ax2]) < tolerance;
} else if (etype == "AcDbCircle" && item.etype == "CIRCLE") {
const xc = item[ax1];
const yc = item[ax2];
const radius = item.radius;
const d = Math.sqrt((x - xc)*(x - xc) + (y - yc)*(y - yc));
return Math.abs(d - radius) <= tolerance;
} else if (etype == "AcDbCircle") {
const xc = item[ax1];
const yc = item[ax2];
const radius = item.radius;
const d = Math.sqrt((x - xc)*(x - xc) + (y - yc)*(y - yc));
let theta = Math.atan2(y - yc, x - xc);
if (theta < 0) {
theta = 2*Math.PI + theta;
}
let sa = item.start_angle*Math.PI/180;
let ea = item.end_angle*Math.PI/180;
if (sa > ea) {
sa = sa - 2*Math.PI;
}
const isInside = (sa - theta)*(ea - theta) < tolerance;
return isInside && Math.abs(d - radius) <= tolerance;
} else if (etype == "AcDbSpline") { // excluding points on each end
const eqn = BSpline(item).equations;
const X = ax1.toUpperCase();
const Y = ax2.toUpperCase();
for (let i = 0; i < eqn.length; i++) {
const interval = eqn[i].interval;
const lower = parseFloat(interval.substring(0, interval.indexOf("≤")));
const upper = parseFloat(interval.substring(interval.lastIndexOf("≤") + 1));
const nurbs = eqn[i].nurbs;
const x_num = nurbs[X].coefficients_numerator;
const x_denom = nurbs[X].coefficients_denominator;
const x_fn = (t) => (((x_num[0]*t*t*t + x_num[1]*t*t + x_num[2]*t + x_num[3])/(x_denom[0]*t*t*t + x_denom[1]*t*t + x_denom[2]*t + x_denom[3])) - x);
const y_num = nurbs[Y].coefficients_numerator;
const y_denom = nurbs[Y].coefficients_denominator;
const y_fn = (t) => (((y_num[0]*t*t*t + y_num[1]*t*t + y_num[2]*t + y_num[3])/(y_denom[0]*t*t*t + y_denom[1]*t*t + y_denom[2]*t + y_denom[3])) - y);
const x_sol = bisection(x_fn, lower, upper, 0.0000001);
const y_sol = bisection(y_fn, lower, upper, 0.0000001);
if (x_sol === undefined || y_sol === undefined) continue;
if ((x_sol - lower)*(x_sol - upper) < tolerance && (y_sol - lower)*(y_sol - upper) < tolerance && Math.abs(x_sol - y_sol) < tolerance) {
return true;
}
}
return false;
} else if (etype == "AcDbPolyline") {
const vertices = JSON.parse(JSON.stringify(item.vertices));
if (vertices.type == "Closed") {
vertices.push(vertices[0]);
}
for (let i = 1; i < vertices.length; i++) {
const x0 = vertices[i - 1][ax1];
const y0 = vertices[i - 1][ax2];
const x1 = vertices[i][ax1];
const y1 = vertices[i][ax2];
const m = (y1 - y0)/(x1 - x0);
if (Math.abs(m) == Infinity) {
if ((y0 - y)*(y1 - y) < tolerance && (Math.abs(x - x0) <= tolerance)) {
return true;
}
} else {
const isInside = (x0 - x)*(x1 - x) < tolerance && (y0 - y)*(y1 - y) < tolerance;
if (isInside && (Math.abs(y - (m*(x - x1) + y1)) <= tolerance)) {
return true;
}
}
}
return false;
} else if (etype == "AcDbEllipse") {
const xc = item[ax1];
const yc = item[ax2];
const dx = item[`major_end_d${ax1}`];
const dy = item[`major_end_d${ax2}`];
const ratio = item.minorToMajor;
const a = Math.sqrt(dx*dx + dy*dy);
const b = ratio*a;
const theta = Math.atan2(dy, dx);
if (!isNaN(item[`start_${ax1}`]) && !isNaN(item[`start_${ax2}`])) {
let sa = 0;
let ea = item.end_angle - item.start_angle;
const a1 = a*Math.cos(theta);
const b1 = -b*Math.sin(theta);
const c1 = x - xc;
const a2 = a*Math.sin(theta);
const b2 = b*Math.cos(theta);
const c2 = y - yc;
const sin_a = (a2*c1 - a1*c2)/(a2*b1 - a1*b2);
const cos_a = (b2*c1 - b1*c2)/(b2*a1 - b1*a2);
const alpha = Math.atan2(sin_a, cos_a);
const zs = (a - b)*Math.sin(alpha);
const dx1s = zs*Math.sin(theta);
const dy1s = zs*Math.cos(theta);
const xp1s = a*Math.cos(alpha + theta);
const yp1s = a*Math.sin(alpha + theta);
let true_sa = (Math.atan2((yp1s - dy1s), (xp1s + dx1s)))*180/Math.PI- item.start_angle;
if (true_sa < -tolerance) true_sa = true_sa + 360;
if (ea < -tolerance) ea = ea + 360;
if ((sa - true_sa)*(ea - true_sa) > tolerance) return false;
}
const A = Math.pow(a*Math.sin(theta), 2) + Math.pow(b*Math.cos(theta), 2);
const B = 2*(b*b - a*a)*Math.sin(theta)*Math.cos(theta);
const C = Math.pow(a*Math.cos(theta), 2) + Math.pow(b*Math.sin(theta), 2);
const D = -2*A*xc - B*yc;
const E = -B*xc - 2*C*yc;
const F = A*xc*xc + B*xc*yc + C*yc*yc - a*a*b*b;
const eqn = (A*x*x + B*x*y + C*y*y + D*x + E*y + F);
return Math.abs(eqn) <= tolerance;
}
return false;
};
const bisection = (fn, xl, xu, tolerance) => {
let ea = Infinity, xr_prev;
const fxl = fn(xl);
const fxu = fn(xu);
if (fxl*fxu > 0) return;
//let xr = (xu*fxl - xl*fxu)/(fxl - fxu); // false-position formula
let xr = 0.5*(xl + xu);
let count = 0;
do {
count++;
xr_prev = xr;
if (fn(xl)*fn(xr) < 0) {
xu = xr;
} else if (fn(xl)*fn(xr) > 0) {
xl = xr;
} else {
return xr;
}
//xr = (xu*fn(xl) - xl*fn(xu))/(fn(xl) - fn(xu)); // false-position formula
xr = 0.5*(xl + xu);
//if (Math.abs(fn(xr)) < tolerance) { to avoid instabilities associated with false-position formula
//return xr;
//}
ea = xr_prev === 0 ? (xr - xr_prev)/xr : (xr - xr_prev)/xr_prev;
} while (Math.abs(ea) > tolerance && count < 50);
if (count >= 50) return;
return xr;
};
const checkParallel = (data, item, plane, getAxes, tolerance, getCorners) => {
const etype1 = data.subclass;
const etype2 = item.subclass;
let [ax1, ax2] = getAxes(plane);
if (plane && ax1 === undefined && ax2 === undefined) {
throw new Error(ErrorMessages.INCORRECT_PARAMS);
return;
}
if ((etype1 == "AcDbLine" || (etype1 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) &&
(etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (item.specific_type == 'AcDbRotatedDimension' || item.specific_type == 'AcDbAlignedDimension')))) {
let x11, y11, x12, y12, x21, y21, x22, y22;
if (etype1 == "AcDbLine") {
x11 = data[`start_${ax1}`];
y11 = data[`start_${ax2}`];
x12 = data[`end_${ax1}`];
y12 = data[`end_${ax2}`];
} else {
x11 = data[`${ax1}`];
y11 = data[`${ax2}`];
x12 = data[`${ax1}_end`];
y12 = data[`${ax2}_end`];
}
if (etype2 == "AcDbLine") {
x21 = item[`start_${ax1}`];
y21 = item[`start_${ax2}`];
x22 = item[`end_${ax1}`];
y22 = item[`end_${ax2}`];
} else {
x21 = item[`${ax1}`];
y21 = item[`${ax2}`];
x22 = item[`${ax1}_end`];
y22 = item[`${ax2}_end`];
}
const len1 = Math.sqrt((x11 - x12)*(x11 - x12) + (y11 - y12)*(y11 - y12));
const len2 = Math.sqrt((x21 - x22)*(x21 - x22) + (y21 - y22)*(y21 - y22));
const dotproduct = ((x11 - x12)*(x21 - x22) + (y11 - y12)*(y21 - y22))/(len1*len2);
return Math.abs(dotproduct - 1) < tolerance || Math.abs(dotproduct + 1) < tolerance;
} else if (etype1 == "AcDbPolyline" && etype2 == "AcDbPolyline") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
if (intersection && intersection.length > 0) return false;
const vertices1 = JSON.parse(JSON.stringify(data.vertices));
const vertices2 = JSON.parse(JSON.stringify(item.vertices));
if (data.type == "Closed") {
vertices1.push(vertices1[0]);
}
if (item.type == "Closed") {
vertices2.push(vertices2[0]);
}
const start_x = vertices1[0][ax1];
const start_y = vertices1[0][ax2];
let corners1 = getCorners(data, plane), corners2 = getCorners(item, plane);
if (corners1.length > corners2.length) {
let temp = corners1;
corners1 = corners2;
corners2 = temp;
}
// pick corresponding sides
const x0 = corners1[0][ax1];
const y0 = corners1[0][ax2];
const x1 = corners1[1][ax1];
const y1 = corners1[1][ax2];
const line1 = {subclass: "AcDbLine", start_x: x0, start_y: y0, end_x: x1, end_y: y1};
const len1 = Math.sqrt((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0));
let hasParallel = false;
for (let i = 1; i < corners2.length; i++) {
const x20 = corners2[i - 1][ax1];
const y20 = corners2[i - 1][ax2];
const x21 = corners2[i][ax1];
const y21 = corners2[i][ax2];
const line2 = {subclass: "AcDbLine", start_x: (x20 + x21)/2, start_y: (y20 + y21)/2, end_x: (x0 + x1)/2, end_y: (y0 + y1)/2};
const len2 = Math.sqrt((x21 - x20)*(x21 - x20) + (y21 - y20)*(y21 - y20));
const dotproduct = ((x1 - x0)*(x21 - x20) + (y1 - y0)*(y21 - y20))/(len1*len2);
let intersected = false;
if (Math.abs(dotproduct - 1) < tolerance || Math.abs(dotproduct + 1) < tolerance) {
for (let j = 1; j < corners2.length; j++) {
if (j == i) continue;
const x30 = corners2[j - 1][ax1];
const y30 = corners2[j - 1][ax2];
const x31 = corners2[j][ax1];
const y31 = corners2[j][ax2];
const line3 = {subclass: "AcDbLine", start_x: x30, start_y: y30, end_x: x31, end_y: y31};
const intersection = Intersection(line2, line3, plane, getAxes, tolerance);
if (intersection && intersection.length > 0) {
intersected = true;
break;
}
}
}
if (!intersected && Math.abs(dotproduct - 1) < tolerance) {
corners2 = [...corners2.slice(i - 1), ...corners2.slice(0, i - 1)];
hasParallel = true;
break;
} else if (!intersected && Math.abs(dotproduct + 1) < tolerance) {
corners2 = [...corners2.slice(0, i + 1).reverse(), ...corners2.slice(i + 1).reverse()];
hasParallel = true;
break;
}
}
if (!hasParallel) {
return false;
}
if (data.type == "Closed" || ((data.vertices[0][ax1] - data.vertices[data.vertices.length - 1][ax1]) < tolerance
&& (data.vertices[0][ax2] - data.vertices[data.vertices.length - 1][ax2]) < tolerance)) {
corners1.push(corners1[0]);
}
if (item.type == "Closed" || ((item.vertices[0][ax1] - item.vertices[item.vertices.length - 1][ax1]) < tolerance
&& (item.vertices[0][ax2] - item.vertices[item.vertices.length - 1][ax2]) < tolerance)) {
corners2.push(corners2[0]);
}
// dot products for each corresponding unit vector should be either consistently 1 or -1
// perpendicular distances between corresponding sides are consistent
let isParallel = false, dprev;
for (let i = 1; i < corners1.length; i++) {
const x0 = corners1[i - 1][ax1];
const y0 = corners1[i - 1][ax2];
const x1 = corners1[i][ax1];
const y1 = corners1[i][ax2];
const len1 = Math.sqrt((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0));
const x20 = corners2[i - 1][ax1];
const y20 = corners2[i - 1][ax2];
const x21 = corners2[i][ax1];
const y21 = corners2[i][ax2];
const len2 = Math.sqrt((x21 - x20)*(x21 - x20) + (y21 - y20)*(y21 - y20));
const dotproduct = ((x1 - x0)*(x21 - x20) + (y1 - y0)*(y21 - y20))/(len1*len2);
if (Math.abs(dotproduct - 1) > tolerance) return false;
const line = {subclass: "AcDbLine", start_x: x20, start_y: y20, end_x: x21, end_y: y21};
const d1 = Distance([x0, y0], line, plane, getAxes, tolerance);
const d2 = Distance([x1, y1], line, plane, getAxes, tolerance);
if (Math.abs(d1 - d2) > tolerance) return false;
if (dprev === undefined) {
dprev = d1;
} else {
if (Math.abs(dprev - d1) > tolerance) return false;
}
}
return true;
} else {
return false;
}
};
const checkOrthogonal = (data, item, plane, getAxes, tolerance) => {
const etype1 = data.subclass;
const etype2 = item.subclass;
let [ax1, ax2] = getAxes(plane);
if (plane && ax1 === undefined && ax2 === undefined) {
throw new Error(ErrorMessages.INCORRECT_PARAMS);
return;
}
if ((etype1 == "AcDbLine" || (etype1 == "AcDbDimension" && (data.specific_type == 'AcDbRotatedDimension' || data.specific_type == 'AcDbAlignedDimension'))) &&
(etype2 == "AcDbLine" || (etype2 == "AcDbDimension" && (item.specific_type == 'AcDbRotatedDimension' || item.specific_type == 'AcDbAlignedDimension')))) {
let x11, y11, x12, y12, x21, y21, x22, y22;
if (etype1 == "AcDbLine") {
x11 = data[`start_${ax1}`];
y11 = data[`start_${ax2}`];
x12 = data[`end_${ax1}`];
y12 = data[`end_${ax2}`];
} else {
x11 = data[`${ax1}`];
y11 = data[`${ax2}`];
x12 = data[`${ax1}_end`];
y12 = data[`${ax2}_end`];
}
if (etype2 == "AcDbLine") {
x21 = item[`start_${ax1}`];
y21 = item[`start_${ax2}`];
x22 = item[`end_${ax1}`];
y22 = item[`end_${ax2}`];
} else {
x21 = item[`${ax1}`];
y21 = item[`${ax2}`];
x22 = item[`${ax1}_end`];
y22 = item[`${ax2}_end`];
}
const m1 = (y12 - y11)/(x12 - x11);
const m2 = (y22 - y21)/(x22 - x21);
if (Math.abs(m1) != Infinity && Math.abs(m2) == Infinity) {
return Math.abs(m1) < tolerance && (y11 - y21)*(y11 - y22) < tolerance && (x21 - x11)*(x21 - x12) < tolerance;
} else if (Math.abs(m2) != Infinity && Math.abs(m1) == Infinity) {
return Math.abs(m2) < tolerance && (y21 - y11)*(y21 - y12) < tolerance && (x11 - x21)*(x11 - x22) < tolerance;
} else if (Math.abs(m2) != Infinity && Math.abs(m1) != Infinity) {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
if (!intersection || intersection.length != 1) {
return false;
}
return Math.abs(m2*m1 + 1) < tolerance;
} else {
return false;
}
} else if (etype1 == "AcDbCircle" && etype2 == "AcDbCircle") {
const intersection = Intersection(data, item, plane, getAxes, tolerance);
if (!intersection || intersection.length == 0) return false;
const xc1 = data[ax1];
const yc1 = data[ax2];
const xc2 = item[ax1];
const yc2 = item[ax2];
for (let i = 0; i < intersection.length; i++) {
const x = intersection[i][ax1];
const y = intersection[i][ax2];
const m1 = (y - yc1)/(x - xc1);
const m2 = (y - yc2)/(x - xc2);
if (Math.abs(m1) != Infinity && Math.abs(m2) == Infinity) {
if (Math.abs(m1) < tolerance) return true;
} else if (Math.abs(m2) != Infinity && Math.abs(m1) == Infinity) {
if (Math.abs(m2) < tolerance) return true;
} else if (Math.abs(m2) != Infinity && Math.abs(m1) != Infinity) {
if (Math.abs(m2*m1 + 1) < tolerance) return true;
}
}
return false;
} else {
return false;
}
// ellipse vs ellipse - product of tangent slopes = -1
};
const checkAligned = (data, item, plane, getAxes, tolerance) => {
const etype1 = data.subclass;
const etype2 = item.subclass;
let [ax1, ax2] = getAxes(plane);
if (plane && ax1 === undefined && ax2 === undefined) {
throw