import { SLine, SPoint } from "@persagy-web/draw/lib"; // @ts-ignore import { intersect, polygon, segments, combine, selectUnion, selectDifference, selectDifferenceRev } from "polybooljs"; export class generate { /** * 计算角平分线上的距离这两条直线l的点 * * @param p1 第一个点 * @param p2 第二个点 也是2条线的交点 * @param p3 第三个点 * @param l 距离2条线的距离 * @return number Array(4) 两个点 */ static getBisector(p1: SPoint, p2: SPoint, p3: SPoint, l: number): [number, number, number, number] { const dy1 = p1.y - p2.y; const dx1 = p1.x - p2.x; const dy2 = p3.y - p2.y; const dx2 = p3.x - p2.x; // 线1的斜率 const k1 = dy1 / dx1; // 线2的斜率 const k2 = dy2 / dx2; // 线1与x轴的夹角 const temp1 = Math.atan(k1) let a1 = dx1 >= 0 ? temp1 : temp1 + Math.PI; // 线2与x轴的夹角 const temp2 = Math.atan(k2) let a2 = dx2 >= 0 ? temp2 : temp2 + Math.PI; // 角平分线斜率 const k = Math.tan((a1 + a2) / 2); // 角平分线b const b = p2.y - k * p2.x; // 距离两条线l的点 到交点p2的距离 const Lb2 = l / Math.sin(Math.abs(a1 - a2) / 2); // 特殊情况-角分线垂直或平行X轴 if (k1 / k2 == -1) { if (dx2 * dx1 > 0 && dy2 * dy1 < 0) { // 上下开角 return [Number((p2.x - Lb2).toFixed(6)), Number(p2.y.toFixed(6)), Number((p2.x + Lb2).toFixed(6)), Number(p2.y.toFixed(6))] } else if (dx2 * dx1 < 0 && dy2 * dy1 > 0) { // 左右开角 return [Number(p2.x.toFixed(6)), Number((p2.y - Lb2).toFixed(6)), Number(p2.x.toFixed(6)), Number((p2.y + Lb2).toFixed(6))] } } // 将距离公式与直线方程连立,并且将直线方程代入,得到一元二次方程 Ax^2 + Bx + C = 0;然后根据得根公式得到2个解 const A = k * k + 1; const B = 2 * k * b - 2 * p2.x - 2 * k * p2.y; const C = b * b - Lb2 * Lb2 + p2.x * p2.x + p2.y * p2.y - 2 * b * p2.y; // 求解 const X1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A); const X2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A); const Y1 = k * X1 + b; const Y2 = k * X2 + b; return [Number(X1.toFixed(6)), Number(Y1.toFixed(6)), Number(X2.toFixed(6)), Number(Y2.toFixed(6))] } /** * 计算一条线的过p1垂线上距离线l的2个点 * * @param p1 点1 * @param p2 点2 * @param l 距离这条线的距离 * @return number Array(4) 两个点 */ static getVertical(p1: SPoint, p2: SPoint, l: number): [number, number, number, number] { const dy1 = p1.y - p2.y; const dx1 = p1.x - p2.x; if (dy1 == 0) { return [Number(p1.x.toFixed(6)), Number((p1.y - l).toFixed(6)), Number(p1.x.toFixed(6)), Number((p1.y + l).toFixed(6))] } if (dx1 == 0) { return [Number((p1.x - l).toFixed(6)), Number(p1.y.toFixed(6)), Number((p1.x + l).toFixed(6)), Number(p1.y.toFixed(6))] } // 线1的斜率 const k1 = dy1 / dx1; // 垂线的斜率 const k = -1 / k1; // 垂线的b const b = p1.y - k * p1.x; // 将距离公式与直线方程连立,并且将直线方程代入,得到一元二次方程 Ax^2 + Bx + C = 0;然后根据得根公式得到2个解 const A = k * k + 1; const B = 2 * k * b - 2 * p1.x - 2 * k * p1.y; const C = b * b - l * l + p1.x * p1.x + p1.y * p1.y - 2 * b * p1.y; // 求解 const X1 = (-B + Math.sqrt(B * B - 4 * A * C)) / (2 * A); const X2 = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A); const Y1 = k * X1 + b; const Y2 = k * X2 + b; return [Number(X1.toFixed(6)), Number(Y1.toFixed(6)), Number(X2.toFixed(6)), Number(Y2.toFixed(6))] } /** * 计算线段交点 * * @param line1 线段1 * @param line2 线段2 * @return SPoint 交点 null 平行但不重合 'repeat' 重合 */ static lineIntersection( p1: SPoint, p2: SPoint, p3: SPoint, p4: SPoint ): SPoint | null | string { let k1 = (p2.y - p1.y) / (p2.x - p1.x); let b1 = p2.y - k1 * p2.x; let k2 = (p4.y - p3.y) / (p4.x - p3.x); let b2 = p3.y - k2 * p3.x; if (k1 == k2) { if (b1 == b2) { return "repeat"; } return null; } let intersectionX = (b2 - b1) / (k1 - k2); let intersectionY = k1 * intersectionX + b1; // 取线段上的最大最小值可以上下换 let minX = Math.min(p1.x, p2.x); let maxX = Math.max(p3.x, p4.x); if (intersectionX >= minX && intersectionX <= maxX) { return new SPoint(intersectionX, intersectionY); } return null; } /** * 一条线生成的4个点,根据这4个点和这条线,将轮廓线排序 * * @description 计算3条线的斜率,差值小的一组 * * @param [number, number, number, number] * @param [number, number, number, number] * @param SLine * @return 两组线的数组 */ static generateOrderLine(arr1: [number, number, number, number], arr2: [number, number, number, number], line: SLine) { if (arr1.length && arr2.length) { // 排除特殊情况 if (line.dy == 0) { // 线本身是水平的 if (generate.isEqual(arr1[1], arr2[1])) { return [ [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))], [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))], ] } else { return [ [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))], [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))], ] } } if (line.dx == 0) { // 线本身是垂直的 if (generate.isEqual(arr1[0], arr2[0])) { // 与另外一组的第一个点的连线是水平的 return [ [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))], [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))], ] } else { // 与另外一组的第二个点的连线是竖直的 return [ [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))], [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))], ] } } // 标准线的斜率 const k = line.dy / line.dx; // 第一条线 const l1 = new SLine(arr1[0], arr1[1], arr2[0], arr2[1]); // 第一条线的斜率 const k1 = l1.dy / l1.dx; // 第一条线斜率差值 const dk1 = Math.abs(k1 - k); // 第二条线 const l2 = new SLine(arr1[0], arr1[1], arr2[2], arr2[3]); // 第二条线的斜率 const k2 = l2.dy / l2.dx; // 第二条线斜率差值 const dk2 = Math.abs(k2 - k); // 判断差值小的放一组 if (dk1 < dk2) { return [ [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))], [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))], ] } else { return [ [Number(arr1[0].toFixed(6)), Number(arr1[1].toFixed(6)), Number(arr2[2].toFixed(6)), Number(arr2[3].toFixed(6))], [Number(arr1[2].toFixed(6)), Number(arr1[3].toFixed(6)), Number(arr2[0].toFixed(6)), Number(arr2[1].toFixed(6))], ] } } return [] } /** * 删除轮廓线中共线的点 * * @description 连续2条线的斜率相等则这3点共线,删除中间的一个点 * @param outline 轮廓线数据 * @return 简化后的轮廓线数据 */ static simplyOutline(outline: SPoint[]): SPoint[] { for (let i = 0; i < outline.length - 1; i++) { if (outline[i].x - outline[i + 1].x == 0 && outline[i].y - outline[i + 1].y == 0) { outline.splice(i, 1); i--; } } if (outline.length > 2) { for (let i = 1; i < outline.length - 1; i++) { if (outline[i].y - outline[i - 1].y == 0 && outline[i + 1].y - outline[i].y == 0) { outline.splice(i, 1); i--; } else if (outline[i].x - outline[i - 1].x == 0 && outline[i + 1].x - outline[i].x == 0) { outline.splice(i, 1); i--; } else if ( outline[i].y - outline[i - 1].y == outline[i + 1].y - outline[i].y && outline[i].x - outline[i - 1].x == outline[i + 1].x - outline[i].x ) { outline.splice(i, 1); i--; } } } return outline } /** * 解决0.1+0.2!=0.3的问题 * * @param a 第一个数 * @param b 第二个数 * @return 是否相等 */ static isEqual(a: number, b: number) { return Math.abs(a - b) < Math.pow(2, -52); } static llll() { const a = { regions: [ [[0, 0], [0, 200], [200, 200], [200, 0]], // [[150, 150], [250, 150], [250, 250], [150, 250]], ], inverted: false } const b = { regions: [ [[150, 150], [250, 150], [250, 250], [150, 250]], ], inverted: false } const start = +new Date() const sa = segments(a) const sb = segments(b) const comb = combine(sa, sb) // const start = +new Date() // const poly = segments(a) // console.log(segments(a)) // const end = +new Date(); // console.log(end - start); // console.log(polygon(poly)); console.log(comb); const selecomb = selectDifference(comb) console.log(selecomb) console.log(polygon(selecomb)) const end = +new Date(); console.log(end - start); } }