mirror of https://gitee.com/cf-fz/WebCAD.git
!2494 功能:余料归方实现
parent
80f8be1d6b
commit
7f61ffd525
@ -0,0 +1,171 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`lir 1`] = `
|
||||||
|
Array [
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 22139.12887289156,
|
||||||
|
"y": -2841.682010503894,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 21469.741102231354,
|
||||||
|
"y": -3263.948131063738,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 21899.684788619557,
|
||||||
|
"y": -2641.585451102241,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 21688.071880475363,
|
||||||
|
"y": -2841.682010503894,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lir 2`] = `
|
||||||
|
Array [
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 23460.421387167884,
|
||||||
|
"y": -2637.4589312808325,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 23048.13690406113,
|
||||||
|
"y": -3242.62504391121,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lir 3`] = `
|
||||||
|
Array [
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 24485.042744543087,
|
||||||
|
"y": -2542.4597375987987,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 23720.604568827835,
|
||||||
|
"y": -3516.687337476508,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 24131.56193396799,
|
||||||
|
"y": -2013.675435600278,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 23720.604568827835,
|
||||||
|
"y": -2542.4597375987987,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lir 4`] = `
|
||||||
|
Array [
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 25469.309065156027,
|
||||||
|
"y": -2798.706322535294,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 24639.22077503745,
|
||||||
|
"y": -3486.2359715248085,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 24932,
|
||||||
|
"y": -2175,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 24639.22077503745,
|
||||||
|
"y": -2798.706322535294,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 25072.310317708012,
|
||||||
|
"y": -2434.1892907875726,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 24932,
|
||||||
|
"y": -2798.706322535294,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lir 5`] = `
|
||||||
|
Array [
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 26840,
|
||||||
|
"y": -2267.7761534803576,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 26140,
|
||||||
|
"y": -3306,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 26540,
|
||||||
|
"y": -3306,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 26240,
|
||||||
|
"y": -3486,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 26140,
|
||||||
|
"y": -2246,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 26040,
|
||||||
|
"y": -2806,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 26940,
|
||||||
|
"y": -2866,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 26840,
|
||||||
|
"y": -3266,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`lir 6`] = `
|
||||||
|
Array [
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 20867.54619958679,
|
||||||
|
"y": -2462.241147060554,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 20334.371107671588,
|
||||||
|
"y": -3470.37106223355,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Box2 {
|
||||||
|
"max": Vector2 {
|
||||||
|
"x": 20971.36213127695,
|
||||||
|
"y": -2627.7092790342367,
|
||||||
|
},
|
||||||
|
"min": Vector2 {
|
||||||
|
"x": 20867.54619958679,
|
||||||
|
"y": -3291.6654797019723,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
@ -0,0 +1,24 @@
|
|||||||
|
import { LargestInteriorRectangle } from "../../src/Add-on/testEntity/LargestInteriorRectangle";
|
||||||
|
import { Polyline } from "../../src/DatabaseServices/Entity/Polyline";
|
||||||
|
import { AsVector2, equalv3 } from "../../src/Geometry/GeUtils";
|
||||||
|
import { LoadEntityFromFileData } from "../Utils/LoadEntity.util";
|
||||||
|
|
||||||
|
test('lir', () =>
|
||||||
|
{
|
||||||
|
let d =
|
||||||
|
{ "file": [6, "Polyline", 10, 2, 30486, 0, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 21688.071880475363, -2641.585451102241, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10111.128053715596, -1330.0440361938563, 0, 1], 0, 0, 1, 2, 15, [0, -91.20902138697647], 0, [-99.27451926151298, -91.20902138697647], 0, [-99.27451926151298, -200.09655940165294], 0, [-218.3307782440097, -200.09655940165294], 0, [-218.3307782440097, -622.3626799614967], 0, [0, -622.3626799614967], 0, [0, -745.6835810795424], 0, [211.61290814419408, -745.6835810795424], 0, [211.61290814419408, -622.3626799614967], 0, [451.0569924161973, -622.3626799614967], 0, [451.0569924161973, -200.09655940165294], 0, [211.61290814419408, -200.09655940165294], 0, [211.61290814419408, 0], 0, [0, 0], 0, [0, -91.20902138697647], 0, false, "Polyline", 10, 2, 30487, 0, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11643.478317846675, -1064.2917826726493, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11643.478317846675, -1064.2917826726493, 0, 1], 0, 0, 1, 2, 8, [11404.658586214457, -2178.3332612385607], 0, [11404.658586214457, -1330.8596015191276], 0, [11544.497767619087, -1330.8596015191276], 0, [11544.497767619087, -1458.6436810785299], 0, [11673.487357363012, -1458.6436810785299], 0, [11673.487357363012, -1573.1671486081832], 0, [11816.943069321209, -1573.1671486081832], 0, [11816.943069321209, -2178.3332612385607], 0, true, "Polyline", 10, 2, 30488, 0, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 23720.604568827835, -2013.675435600278, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7839.635766842221, -1733.9653511997813, 0, 1], 0, 0, 1, 2, 7, [764.4381757152514, -528.7843019985207], 0, [410.95736514015334, -528.7843019985207], 0, [410.95736514015334, 0], 0, [0, 0], 0, [0, -1503.01190187623], 0, [764.4381757152514, -1503.01190187623], 0, [764.4381757152514, -528.7843019985206], 0, false, "Polyline", 10, 2, 30489, 0, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 25072.310317708012, -2434.1892907875726, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11510.346173220109, -921.6709897391447, 0, 1], 0, 0, 1, 2, 9, [396.9987474480131, -364.5170317477214], 0, [0, -364.5170317477214], 0, [0, 0], 0, [-191.2812146794986, 369.9306510311035], 0, [-220.15385085754497, 398.803287209141], 0, [-433.08954267056106, 398.803287209141], 0, [-433.08954267056106, -1052.0466807372359], 0, [396.9987474480131, -1052.0466807372359], 0, [396.9987474480131, -364.5170317477214], 0, false, "Polyline", 10, 2, 30490, 0, 1, 7, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 14328.52642470492, -1135.02410656956, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 14328.52642470492, -1135.02410656956, 0, 1], 0, 0, 1, 2, 9, [11652.077020504916, -1169.4279001173695], 0, [11960.765451660282, -860.7394689621026], 0, [12049.398763576115, -1132.7520469107974], 0, [12510.903249758816, -1074.6819460003912], 0, [12572.02967176977, -1554.5243587863783], 0, [12706.507800193867, -2095.493193583319], 0, [12229.721708508428, -2315.5483128227534], 0, [12067.736690179401, -2630.349386179165], 0, [11755.991937923536, -2074.0989458794857], 0, true, "Polyline", 10, 2, 30485, 0, 1, 1, 0, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4219.473337791907, -173.97252646749985, 0, 1], 0, 0, 1, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 4219.473337791907, -173.97252646749985, 0, 1], 0, 0, 1, 2, 16, [16114.89776987968, -3305.5912097645887], 0, [16114.89776987968, -2288.268620593054], 0, [16648.072861794884, -2288.268620593054], 0, [16648.072861794884, -2453.736752566737], 0, [16707.76395829206, -2453.736752566737], 0, [16721.001408849956, -2416.1080736660547], 0, [16748.57943084557, -2416.1080736660547], 0, [16748.57943084557, -2453.6141835800895], 0, [16782.776178120075, -2419.417436305495], 0, [16782.776178120075, -2480.089084695879], 0, [16861.097760587672, -2480.089084695879], 0, [16837.932222111358, -2536.3482495669314], 0, [16884.26329906399, -2604.7417441160537], 0, [16751.888793485043, -2631.2166452318434], 0, [16751.888793485043, -3117.6929532344725], 0, [16677.979694536796, -3296.3985357660504], 0, true], "basePt": { "x": 20334.371107671588, "y": -3765.373492748725, "z": 0 }, "ucs": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] };
|
||||||
|
let pls = LoadEntityFromFileData(d.file) as Polyline[];
|
||||||
|
|
||||||
|
for (let pl of pls)
|
||||||
|
{
|
||||||
|
let pts = pl.GetStretchPoints();
|
||||||
|
if (equalv3(pts[0], pts[pts.length - 1]))
|
||||||
|
pts.pop();
|
||||||
|
|
||||||
|
let lir = new LargestInteriorRectangle;
|
||||||
|
|
||||||
|
let rects = lir.ParseLIR(pts.map(AsVector2));
|
||||||
|
|
||||||
|
expect(rects).toMatchSnapshot();
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,499 @@
|
|||||||
|
import Flatbush from 'flatbush';
|
||||||
|
import { Box2, Vector2, Vector3 } from "three";
|
||||||
|
import { arrayRemoveIf } from '../../Common/ArrayExt';
|
||||||
|
import { FixIndex } from "../../Common/Utils";
|
||||||
|
import { InsertSortedIndex, RangeUnion } from "../../DatabaseServices/Room/ParseService/RangeUtils";
|
||||||
|
import { BoxCheckIntersect } from '../../Geometry/CheckIntersect';
|
||||||
|
import { equaln } from "../../Geometry/GeUtils";
|
||||||
|
import { arrayPushArray, arraySortByNumber } from "../../Nest/Common/ArrayExt";
|
||||||
|
import { Max } from '../../Nest/Common/Util';
|
||||||
|
import { FuzzyFactory } from '../../csg/core/FuzzyFactory';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取面积最大的矩形
|
||||||
|
* @param rects
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function GetMaxAreaFn(rects: [number, number, number][]): number
|
||||||
|
{
|
||||||
|
return Max(rects, (t1, t2) => t2[0] > t1[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetMaxWidthFn(rects: [number, number, number][]): number
|
||||||
|
{
|
||||||
|
return Max(rects, (t1, t2) => t2[1] > t1[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetMaxHeightFn(rects: [number, number, number][]): number
|
||||||
|
{
|
||||||
|
return Max(rects, (t1, t2) => t2[2] > t1[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//最大内接矩形
|
||||||
|
//1.分析盒子
|
||||||
|
// 1.默认我们使用顶点坐标进行一维展开
|
||||||
|
// 2.当存在斜线时, 我们精确展开(当跨度大于(100/50)时,我们精确产开另一条轴)
|
||||||
|
// (1).获取需要精确展开的区间 随后合并区间
|
||||||
|
// (2).对区间进行展开数值,使用二分搜索,如果在已有的顶点附近,则不进行增项
|
||||||
|
|
||||||
|
//2.标记轮廓内的矩形
|
||||||
|
// (1).我们收集多边形所有的斜线,与矩形盒子求交,如果相交,则矩形盒子无效化
|
||||||
|
// (2).矩形终点在轮廓内(使用单条射线检测,因为我们始终在端点增加锚点的关系 我们似乎不会在顶点位置得到(交点? 稳妥起见 实现标准的射线检测算法)
|
||||||
|
|
||||||
|
//3.获取最大内接矩形
|
||||||
|
// 迭代开始
|
||||||
|
// 动态规划 存储每个方块得到的最大矩形
|
||||||
|
// 获取最大的(根据规则)
|
||||||
|
// 对池子里的结果进行生存选择(如果被占用了方块,则该结果已经不可用)
|
||||||
|
// 取剩下可用的最大的(因为我们已经是在合理的情况下拿最大的了,所以不需要在重新迭代)
|
||||||
|
// 重复迭代
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* //1. 设置过滤条件(舍弃掉一些无用的矩形)
|
||||||
|
* let lir = new LargestInteriorRectangle;
|
||||||
|
* lir.MinWidth = 200;//设置最小可以接受的矩形宽度
|
||||||
|
* lir.MinHeight = 200;//设置最小可以接受的矩形宽度
|
||||||
|
* lir.MinArea = 200 * 200;//设置最小可以接受的矩形面积
|
||||||
|
*
|
||||||
|
* //除了变量 也可以设置过滤函数
|
||||||
|
* //例如:
|
||||||
|
* lir.FilterRectFn = !(w,h)=> (w>500 || h>500) //这样将对长或者宽不足500的矩形板过滤(舍弃)
|
||||||
|
*
|
||||||
|
* //2. 可以设置优先模式,默认提供了面积优先,你也可以使用宽度优先或者高度优先,或许默认规则可能不好用,可以自定义传入自定义函数.
|
||||||
|
* lir.GetMaxRectIndexFn = LargestInteriorRectangle.GetMaxAreaFn; //或者 LargestInteriorRectangle.GetMaxWidthFn / LargestInteriorRectangle.GetMaxHeightFn
|
||||||
|
*
|
||||||
|
* //3. 你已经设置好解析器,你现在可以进行解析了
|
||||||
|
* let rects = lir.ParseLir([ new Vector3(0,0,0) , new Vector3(100,0,0) , new Vector3(50,100,0)]) //首尾不需要重复,首尾重复可能造成错误,程序不再校验
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class LargestInteriorRectangle
|
||||||
|
{
|
||||||
|
MinWidth = 100;
|
||||||
|
MinHeight = 100;
|
||||||
|
MinArea = 200 * 200;
|
||||||
|
|
||||||
|
/** 可以自定义过滤函数,以便过滤掉某些不需要的矩形,当返回true时,我们会过滤它 */
|
||||||
|
FilterRectFn: (width: number, height: number) => boolean;
|
||||||
|
|
||||||
|
GetMaxRectIndexFn: (rects: [number, number, number][]) => number = GetMaxAreaFn;
|
||||||
|
|
||||||
|
|
||||||
|
static GetMaxAreaFn = GetMaxAreaFn;
|
||||||
|
static GetMaxWidthFn = GetMaxWidthFn;
|
||||||
|
static GetMaxHeightFn = GetMaxHeightFn;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析最大内接矩形
|
||||||
|
* @param polygonPts 多边形点表,首尾不要相等
|
||||||
|
*/
|
||||||
|
ParseLIR(polygonPts: Vector2[]): Box2[]
|
||||||
|
{
|
||||||
|
//端点列表
|
||||||
|
let xs: number[] = [];
|
||||||
|
let ys: number[] = [];
|
||||||
|
|
||||||
|
let xset = new Set<number>();
|
||||||
|
let yset = new Set<number>();
|
||||||
|
|
||||||
|
//需要展开的范围
|
||||||
|
let xranges: [number, number][] = [];
|
||||||
|
let yranges: [number, number][] = [];
|
||||||
|
|
||||||
|
let polygonBox = new Box2().setFromPoints(polygonPts);
|
||||||
|
|
||||||
|
let vec = new Vector2;
|
||||||
|
|
||||||
|
let klines: [Vector2, Vector2][] = []; //所有的斜线
|
||||||
|
// let linesP: [Vector2, Vector2][] = []; //所有的线点
|
||||||
|
// let linesFb = new Flatbush(pts.length);//所有的线的索引
|
||||||
|
|
||||||
|
let fuzzX = new FuzzyFactory(1, 0.1);
|
||||||
|
let fuzzY = new FuzzyFactory(1, 0.1);
|
||||||
|
|
||||||
|
for (let i = 0; i < polygonPts.length; i++)
|
||||||
|
{
|
||||||
|
let p = polygonPts[i];
|
||||||
|
let p2 = polygonPts[FixIndex(i + 1, polygonPts)];
|
||||||
|
|
||||||
|
// linesP.push([p, p2]);
|
||||||
|
// let box = new Box2().setFromPoints([p, p2]);
|
||||||
|
// linesFb.add(box.min.x, box.min.y, box.max.x, box.max.y);
|
||||||
|
|
||||||
|
vec.subVectors(p2, p);
|
||||||
|
|
||||||
|
//收集端点
|
||||||
|
let x = fuzzX.lookupOrCreate([p.x], p.x);
|
||||||
|
if (!xset.has(x))
|
||||||
|
{
|
||||||
|
xs.push(x);
|
||||||
|
xset.add(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
let y = fuzzY.lookupOrCreate([p.y], p.y);
|
||||||
|
if (!yset.has(y))
|
||||||
|
{
|
||||||
|
ys.push(y);
|
||||||
|
yset.add(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//展开斜线 每20分段
|
||||||
|
if (Math.abs(vec.x) > 20 && Math.abs(vec.y) > 20)
|
||||||
|
{
|
||||||
|
if (Math.abs(vec.x) > 20)
|
||||||
|
yranges.push(vec.y > 0 ? [p.y, p2.y] : [p2.y, p.y]);
|
||||||
|
|
||||||
|
if (Math.abs(vec.y) > 20)
|
||||||
|
xranges.push(vec.x > 0 ? [p.x, p2.x] : [p2.x, p.x]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//收集所有的斜线
|
||||||
|
if (!equaln(vec.x, 0, 0.1) && !equaln(vec.y, 0, 0.1))
|
||||||
|
klines.push([p, p2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// linesFb.finish();
|
||||||
|
|
||||||
|
//合并展开区间
|
||||||
|
xranges.sort((a, b) => a[0] - b[0]);
|
||||||
|
yranges.sort((a, b) => a[0] - b[0]);
|
||||||
|
xranges = RangeUnion(xranges);
|
||||||
|
yranges = RangeUnion(yranges);
|
||||||
|
|
||||||
|
arraySortByNumber(xs);
|
||||||
|
arraySortByNumber(ys);
|
||||||
|
|
||||||
|
//展开
|
||||||
|
RangesAdd(xranges, xs);
|
||||||
|
RangesAdd(yranges, ys);
|
||||||
|
|
||||||
|
//最大化
|
||||||
|
xs[0] = polygonBox.min.x;
|
||||||
|
xs[xs.length - 1] = polygonBox.max.x;
|
||||||
|
|
||||||
|
ys[0] = polygonBox.min.y;
|
||||||
|
ys[ys.length - 1] = polygonBox.max.y;
|
||||||
|
|
||||||
|
//所有网格的索引
|
||||||
|
let gridFb = new Flatbush((xs.length - 1) * (ys.length - 1));
|
||||||
|
|
||||||
|
// let pls: Polyline[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < xs.length - 1; i++)
|
||||||
|
{
|
||||||
|
for (let j = 0; j < ys.length - 1; j++)
|
||||||
|
{
|
||||||
|
gridFb.add(xs[i], ys[j], xs[i + 1], ys[j + 1]);
|
||||||
|
|
||||||
|
// let pl = new Polyline().RectangleFrom2Pt(new Vector3(xs[i], ys[j]), new Vector3(xs[i + 1], ys[j + 1]));
|
||||||
|
// if (false) TestDraw(pl);
|
||||||
|
// pls.push(pl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gridFb.finish();
|
||||||
|
|
||||||
|
const matrix: number[][] = new Array(xs.length - 1).fill(1).map(() => new Array(ys.length - 1).fill(1));
|
||||||
|
|
||||||
|
//矩形与斜线相交
|
||||||
|
let checks: BoxCheckIntersect[] = [];
|
||||||
|
for (let line of klines)
|
||||||
|
{
|
||||||
|
let box = new Box2().setFromPoints(line);
|
||||||
|
let ids = gridFb.search(box.min.x, box.min.y, box.max.x, box.max.y);
|
||||||
|
|
||||||
|
for (let id of ids)
|
||||||
|
{
|
||||||
|
let check = checks[id];
|
||||||
|
|
||||||
|
let i = Math.floor((id) / (ys.length - 1));
|
||||||
|
let j = id - (i * (ys.length - 1));
|
||||||
|
|
||||||
|
if (!check)
|
||||||
|
{
|
||||||
|
check = new BoxCheckIntersect(new Box2(new Vector2(xs[i] + 0.01, ys[j] + 0.01), new Vector2(xs[i + 1] - 0.01, ys[j + 1] - 0.01)));
|
||||||
|
checks[id] = check;
|
||||||
|
|
||||||
|
// let pl = new Polyline().RectangleFrom2Pt(new Vector3(xs[i], ys[j]), new Vector3(xs[i + 1], ys[j + 1]));
|
||||||
|
// TestDraw(pl, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check.IsIntersectLine(line[0], line[1]))
|
||||||
|
{
|
||||||
|
// pls[id].ColorIndex = 1;
|
||||||
|
// pls[id].Erase();
|
||||||
|
matrix[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//y轴扫描线(矩形在多边形外)
|
||||||
|
// if (xs.length < ys.length)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < xs.length - 1; i++)
|
||||||
|
{
|
||||||
|
let x = (xs[i + 1] + xs[i]) * 0.5;
|
||||||
|
|
||||||
|
let iPtYs = IsPointInPolygon(polygonPts, new Vector3(x, polygonBox.min.y - 0.1, 0));
|
||||||
|
arraySortByNumber(iPtYs);
|
||||||
|
|
||||||
|
for (let j = 0; j < ys.length - 1; j++)
|
||||||
|
{
|
||||||
|
let y = (ys[j + 1] + ys[j]) * 0.5;
|
||||||
|
while (iPtYs.length && iPtYs[0] < y)
|
||||||
|
iPtYs.shift();
|
||||||
|
|
||||||
|
if (iPtYs.length % 2 !== 1)
|
||||||
|
{
|
||||||
|
// pls[i * (ys.length - 1) + j].ColorIndex = 3;
|
||||||
|
// pls[i * (ys.length - 1) + j].Erase();
|
||||||
|
matrix[i][j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else//x轴扫描线
|
||||||
|
|
||||||
|
//ref https://leetcode.cn/problems/maximal-rectangle/solutions/535672/zui-da-ju-xing-by-leetcode-solution-bjlu/
|
||||||
|
const maximalRectangle = () =>
|
||||||
|
{
|
||||||
|
const m = matrix.length;//m个竖条 xlist
|
||||||
|
if (m === 0) return 0;
|
||||||
|
const n = matrix[0].length;//n个横条 ylist
|
||||||
|
if (n === 0) return;
|
||||||
|
|
||||||
|
const left: number[][] = new Array(m).fill(0).map(() => new Array(n).fill(0));
|
||||||
|
|
||||||
|
for (let i = 0; i < m; i++)
|
||||||
|
{
|
||||||
|
for (let j = 0; j < n; j++)
|
||||||
|
{
|
||||||
|
if (matrix[i][j])
|
||||||
|
{
|
||||||
|
left[i][j] = (j === 0 ? 0 : left[i][j - 1]) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//area maxXIndex maxYIndex xcount ycount
|
||||||
|
let rects: [number, number, number, number, number][] = [];
|
||||||
|
|
||||||
|
for (let j = 0; j < n; j++) // 对于每一列,使用基于柱状图的方法
|
||||||
|
{
|
||||||
|
const up = new Array(m).fill(0);
|
||||||
|
const down = new Array(m).fill(0);
|
||||||
|
|
||||||
|
let stack: number[] = [];
|
||||||
|
for (let i = 0; i < m; i++)
|
||||||
|
{
|
||||||
|
while (stack.length && left[stack[stack.length - 1]][j] >= left[i][j])
|
||||||
|
{
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
up[i] = stack.length === 0 ? -1 : stack[stack.length - 1];
|
||||||
|
stack.push(i);
|
||||||
|
}
|
||||||
|
stack = [];
|
||||||
|
for (let i = m - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
while (stack.length && left[stack[stack.length - 1]][j] >= left[i][j])
|
||||||
|
{
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
down[i] = stack.length === 0 ? m : stack[stack.length - 1];
|
||||||
|
stack.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < m; i++)
|
||||||
|
{
|
||||||
|
const xCount = down[i] - up[i] - 1;
|
||||||
|
const yCount = left[i][j];
|
||||||
|
|
||||||
|
if (!yCount || !xCount) continue;
|
||||||
|
|
||||||
|
let width = xs[down[i]] - xs[down[i] - xCount];
|
||||||
|
let height = ys[j + 1] - ys[j + 1 - yCount];
|
||||||
|
|
||||||
|
if (width < this.MinWidth || height < this.MinHeight) continue;
|
||||||
|
|
||||||
|
//自定义过滤函数
|
||||||
|
if (this.FilterRectFn && this.FilterRectFn(width, height)) continue;
|
||||||
|
|
||||||
|
let area = width * height;
|
||||||
|
|
||||||
|
//面积小于最小允许面积
|
||||||
|
if (area < this.MinArea) continue;
|
||||||
|
|
||||||
|
rects.push([area, down[i], j, xCount, yCount]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (rects.length)
|
||||||
|
{
|
||||||
|
let maxIndex = Max(rects, (t1, t2) => t2[0] > t1[0]);
|
||||||
|
|
||||||
|
let [area, maxXIndex, maxYIndex, xCount, yCount] = rects[maxIndex];
|
||||||
|
|
||||||
|
let xMax = xs[maxXIndex];
|
||||||
|
let yMax = ys[maxYIndex + 1];
|
||||||
|
|
||||||
|
let xMin = xs[maxXIndex - xCount];
|
||||||
|
let yMin = ys[maxYIndex + 1 - yCount];
|
||||||
|
|
||||||
|
maxRects.push(new Box2(new Vector2(xMin, yMin), new Vector2(xMax, yMax)));
|
||||||
|
|
||||||
|
rects.splice(maxIndex, 1);
|
||||||
|
|
||||||
|
//对方块进行标记
|
||||||
|
for (let i = 0; i < xCount; i++)
|
||||||
|
{
|
||||||
|
for (let j = 0; j < yCount; j++)
|
||||||
|
{
|
||||||
|
matrix[maxXIndex - 1 - i][maxYIndex - j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果有被标记的方块,则删除它
|
||||||
|
arrayRemoveIf(rects, rect =>
|
||||||
|
{
|
||||||
|
let [area, maxX, maxY, xCount, yCount] = rect;
|
||||||
|
for (let i = 0; i < xCount; i++)
|
||||||
|
{
|
||||||
|
for (let j = 0; j < yCount; j++)
|
||||||
|
{
|
||||||
|
if (!matrix[maxX - 1 - i][maxY - j])
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let maxRects: Box2[] = [];
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
let count = maxRects.length;
|
||||||
|
maximalRectangle();
|
||||||
|
if (count === maxRects.length) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxRects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function RangesAdd(ranges: [number, number][], vList: number[])
|
||||||
|
{
|
||||||
|
let adds: number[] = [];
|
||||||
|
for (let range of ranges)
|
||||||
|
{
|
||||||
|
let dist = range[1] - range[0];
|
||||||
|
let count = Math.floor(dist / 20);
|
||||||
|
let divDist = Math.floor(dist / count);
|
||||||
|
|
||||||
|
for (let i = 1; i < count - 1; i++)
|
||||||
|
{
|
||||||
|
let d = Math.floor(range[0] + divDist * i);
|
||||||
|
|
||||||
|
let index = InsertSortedIndex(vList, d, (a, b) => a - b);
|
||||||
|
|
||||||
|
if (Math.abs(vList[index] - d) < 5)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (index !== vList.length - 1 && Math.abs(vList[index + 1] - d) < 5)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
adds.push(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adds.length)
|
||||||
|
{
|
||||||
|
arrayPushArray(vList, adds);
|
||||||
|
arraySortByNumber(vList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断点在多段线内外 这是为了LIR实现的优化算法,返回交点的Y轴列表
|
||||||
|
*/
|
||||||
|
function IsPointInPolygon(polyPts: Vector2[], pt: Vector3): number[]
|
||||||
|
{
|
||||||
|
// let crossings = 0;
|
||||||
|
// let insLine = new Line(pt, p2);
|
||||||
|
|
||||||
|
let iPtYs: number[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < polyPts.length; i++)
|
||||||
|
{
|
||||||
|
let sp = polyPts[i];
|
||||||
|
let ep = polyPts[FixIndex(i + 1, polyPts)];
|
||||||
|
|
||||||
|
// if (equalv2(sp, pt, 1e-5) || equalv2(ep, pt, 1e-5))//在起点或者终点
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
//点位于线上面
|
||||||
|
if (pt.y > Math.max(sp.y, ep.y))
|
||||||
|
continue;
|
||||||
|
//线垂直Y轴
|
||||||
|
let derX = ep.x - sp.x;
|
||||||
|
if (equaln(derX, 0, 5e-6))
|
||||||
|
{
|
||||||
|
// if (equaln(pt.x, ep.x, 1e-5)
|
||||||
|
// && (pt.y > Math.min(sp.y, ep.y) - 1e-5 && pt.y < Math.max(sp.y, ep.y) + 1e-5))
|
||||||
|
// return false;//点在线上
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//起点
|
||||||
|
if (equaln(sp.x, pt.x, 5e-6))
|
||||||
|
{
|
||||||
|
// if (sp.y > pt.y && derX < 0)
|
||||||
|
if (derX < 0)
|
||||||
|
{
|
||||||
|
// crossings++;
|
||||||
|
iPtYs.push(sp.y);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//终点
|
||||||
|
if (equaln(ep.x, pt.x, 5e-6))
|
||||||
|
{
|
||||||
|
// if (ep.y > pt.y && derX > 0)
|
||||||
|
if (derX > 0)
|
||||||
|
{
|
||||||
|
// crossings++;
|
||||||
|
iPtYs.push(ep.y);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//快速求交,只验证有没有交点
|
||||||
|
let [x1, x2] = sp.x > ep.x ? [ep.x, sp.x] : [sp.x, ep.x];
|
||||||
|
if (pt.x > x1 && pt.x < x2)
|
||||||
|
{
|
||||||
|
let derY = ep.y - sp.y;
|
||||||
|
let k = derY / derX;
|
||||||
|
|
||||||
|
let iptY = (pt.x - sp.x) * k + sp.y;
|
||||||
|
// if (equaln(iptY, pt.y, 1e-5))//点在线上 返回false
|
||||||
|
// return false;
|
||||||
|
if (iptY > pt.y)
|
||||||
|
{
|
||||||
|
// crossings++;
|
||||||
|
iPtYs.push(iptY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iPtYs;
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
import { app } from "../../ApplicationServices/Application";
|
||||||
|
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
||||||
|
import { Command } from "../../Editor/CommandMachine";
|
||||||
|
import { PromptStatus } from "../../Editor/PromptResult";
|
||||||
|
import { AsVector2, AsVector3, equalv3 } from "../../Geometry/GeUtils";
|
||||||
|
import { TestDraw } from "../test/TestUtil";
|
||||||
|
import { LargestInteriorRectangle } from "./LargestInteriorRectangle";
|
||||||
|
|
||||||
|
export class Command_TestLIR implements Command
|
||||||
|
{
|
||||||
|
async exec()
|
||||||
|
{
|
||||||
|
let ssRes = await app.Editor.GetSelection({
|
||||||
|
Msg: "选择多边形进行余料归方分析:(我们会忽略圆弧),请在俯视图下选择多边形,我们忽略三维多边形,请不要绘制大于2440大小的多边形",
|
||||||
|
Filter: { filterTypes: [Polyline] }
|
||||||
|
});
|
||||||
|
if (ssRes.Status !== PromptStatus.OK) return;
|
||||||
|
|
||||||
|
let ents = ssRes.SelectSet.SelectEntityList as Polyline[];
|
||||||
|
|
||||||
|
for (let pl of ents)
|
||||||
|
{
|
||||||
|
let pts = pl.GetStretchPoints();
|
||||||
|
if (equalv3(pts[0], pts[pts.length - 1]))
|
||||||
|
pts.pop();
|
||||||
|
|
||||||
|
let lir = new LargestInteriorRectangle;
|
||||||
|
|
||||||
|
let rects = lir.ParseLIR(pts.map(AsVector2));
|
||||||
|
|
||||||
|
for (let rect of rects)
|
||||||
|
{
|
||||||
|
let pl = new Polyline().RectangleFrom2Pt(AsVector3(rect.min), AsVector3(rect.max));
|
||||||
|
TestDraw(pl, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue