You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
WebCAD/src/Geometry/RegionParse.ts

361 lines
11 KiB

import { Vector3 } from 'three';
import { arrayRemoveDuplicateBySort, arrayRemoveIf } from '../Common/ArrayExt';
import { Arc } from '../DatabaseServices/Entity/Arc';
import { Curve } from '../DatabaseServices/Entity/Curve';
import { Polyline } from '../DatabaseServices/Entity/Polyline';
import { Count } from './Count';
import { CurveMap } from './CurveMap';
import { angle, equalv3 } from './GeUtils';
//路线
export interface Route
{
curve: Curve; //路线的曲线
to: Stand; //终点的点
}
//站台
export interface Stand
{
//位置
position: Vector3;
//路径
routes: Array<Route>;
}
//区域的路线表 表示了一个区域
type RegionRouteS = Array<Set<Route>>;
//区域搜索算法
export class RegionParse
{
//曲线使用计数器
private _CountCu = new Count();
//区域列表 通常是外轮廓
RegionsOutline: RegionRouteS = [];
//区域列表 通常是内轮廓
RegionsInternal: RegionRouteS = [];
//碎线 曲线进入到这里会被炸开.
ExpLineMap: Map<Curve, Curve[]> = new Map();
/**
* Creates an instance of RegionParse.
* @param {Curve[]} cuList .
* @memberof RegionParse
*/
constructor(cuList: Curve[], public fractionDigits = 3)
{
//需要搜索的站
let needFinds = this.GenerateNodeMap(cuList);
//搜索大轮廓.
while (needFinds.size > 0)
{
//找到最小的站.
let minStand = this.FindMinStand(needFinds);
needFinds.delete(minStand);
7 years ago
//逆时针+逆时针
for (let i = minStand.routes.length; i--;)
{
let wayS = new Set<Stand>();
let routeS = new Set<Route>();
let isFind = this.FindRegion(minStand, minStand, undefined, wayS, routeS, 1);
if (isFind)
{
this.RegionsOutline.push(routeS);
//计数增加
for (let route of routeS)
this._CountCu.AddCount(route.curve, 1);
this.FindMinRegion(wayS);
}
}
}
}
/**
* 线使
* @param cu
* @returns true if cueve used
*/
GetCueveUsed(cu: Curve): boolean
{
if (this.ExpLineMap.has(cu))
{
let use = this.ExpLineMap.get(cu).some(c => this._CountCu.GetCount(c) > 0);
if (!use)
this.ExpLineMap.delete(cu);
return use;
}
else
return this._CountCu.GetCount(cu) > 0;
}
/**
7 years ago
* 使,,.
* .
*
* @param {Set<Stand>} standS
* @memberof RegionAlg
*/
private FindMinRegion(standS: Set<Stand>)
{
//需要搜索的.
let needFinds = new Set<Stand>();
standS.forEach(w => needFinds.add(w));
//已经走过的
let passingStands = new Set<Stand>();
let regs: RegionRouteS = [];
while (needFinds.size > 0)
{
//找到最小站
let minStand = this.FindMinStand(needFinds);
//添加为已经计算
passingStands.add(minStand);
needFinds.delete(minStand);
7 years ago
//顺时针搜索
for (let j = minStand.routes.length; j--;)
{
let route = minStand.routes[j];
if (!needFinds.has(route.to))
continue;
let wayS = new Set<Stand>();
let routeS = new Set<Route>();
routeS.add(route);
wayS.add(route.to);
7 years ago
let isFindMin = this.FindRegion(minStand, route.to, route.curve, wayS, routeS, 2);
if (isFindMin)
{
regs.push(routeS);
wayS.forEach(w =>
{
//站点拓展,如果该地点没有被走过,那么加入到需要搜寻的站点表
if (!passingStands.has(w))
needFinds.add(w);
});
for (let route of routeS)
this._CountCu.AddCount(route.curve, 1);
}
}
}
if (regs.length > 1)
this.RegionsInternal.push(...regs);
}
/**
* .
*
* @private
* @param {Set<Stand>} standS
* @returns
* @memberof RegionAlg
*/
private FindMinStand(standS: Set<Stand>)
{
let minStand: Stand;
for (let stand of standS)
{
if (!minStand)
{
minStand = stand;
continue;
}
if (minStand.position.y > stand.position.y)
{
minStand = stand;
}
else if (minStand.position.y === stand.position.y && minStand.position.x > stand.position.x)
{
minStand = stand;
}
}
return minStand;
}
/**
* 线. 线. 使,使x.
*
* @private
* @param {Curve[]} cuList
* @returns {Set<Stand>}
* @memberof RegionParse
*/
private GenerateNodeMap(cuList: Curve[]): Set<Stand>
{
let curveMap = new CurveMap(this.fractionDigits);
//将多段线炸开
let plcus: Curve[] = [];
arrayRemoveIf(cuList, c =>
{
if (c instanceof Polyline)
{
let cus = c.Explode();
//如果为圆弧,提前打断
let arcs: Arc[] = [];
arrayRemoveIf(cus, c =>
{
if (c instanceof Arc)
{
let arcBrs = this.BreakArc(c);
if (arcBrs.length > 1)
{
arcs.push(...arcBrs);
return true;
}
}
return false;
});
//加入到计算
cus.push(...arcs);
this.ExpLineMap.set(c, cus);
plcus.push(...cus);
return true;
}
return false;
});
cuList.push(...plcus);
for (let cu of cuList)
{
7 years ago
//由于圆弧可能导致最低点计算错误的问题.
if (cu instanceof Arc)
{
let arcs = this.BreakArc(cu);
if (arcs.length > 1)
7 years ago
{
arcs.forEach(a => curveMap.addCurveToMap(a));
this.ExpLineMap.set(cu, arcs);
7 years ago
continue;
}
}
curveMap.addCurveToMap(cu);
}
//排序,根据角度逆时针排序.
curveMap.m_NodeMap.forEach(s =>
{
s.routes.sort((r1, r2) =>
{
let a1: number, a2: number;
if (equalv3(r1.curve.StartPoint, s.position))
a1 = angle(r1.curve.GetFistDeriv(0));
else
a1 = angle(r1.curve.GetFistDeriv(r1.curve.EndParam).negate());
if (equalv3(r2.curve.StartPoint, s.position))
a2 = angle(r2.curve.GetFistDeriv(0));
else
a2 = angle(r2.curve.GetFistDeriv(r1.curve.EndParam).negate());
return a1 - a2;
});
//移除重复的线
arrayRemoveDuplicateBySort(s.routes, (r1, r2) =>
{
let isEqual = r1.to === r2.to && r1.curve.constructor.name === r2.curve.constructor.name;
if (isEqual && r1.curve instanceof Arc)
return equalv3(r1.curve.GetPointAtParam(0.5), r2.curve.GetPointAtParam(0.5));
return isEqual;
});
});
return new Set(curveMap.Stands);
7 years ago
}
private BreakArc(arc: Arc)
{
let underPt = arc.Center.add(new Vector3(0, -arc.Radius));
let param = arc.GetParamAtPoint(underPt);
if (param > 0.01 && param < 0.99)
return arc.GetSplitCurves(param);
else
return [arc];
}
7 years ago
//寻找闭合轮廓,下一站总是使用逆时针规划.
private FindRegion(firstS: Stand, //起点
nowStand: Stand, //当前站
7 years ago
lastCurve: Curve, //上一条线索引
wayStands: Set<Stand>, //走过的站
routeS: Set<Route>, //走过的路
cuMaximumCount: number, //允许最大的行走次数
)
{
let routeCount = nowStand.routes.length;
//查找上一条线的索引.
let lastIndex = -1;
7 years ago
if (lastCurve)
{
7 years ago
for (let i = routeCount; i--;)//顺时针搜索.
{
7 years ago
if (nowStand.routes[i].curve === lastCurve)
{
lastIndex = i;
break;
}
}
}
for (let i = 0; i < routeCount - 1; i++)
{
7 years ago
let index = lastIndex + i + 1;//转弯,逆时针
if (index >= routeCount) index -= routeCount;
//下一站
let route = nowStand.routes[index];
let usedCount = this._CountCu.GetCount(route.curve);
if (usedCount >= cuMaximumCount)
continue;
//如果发现这条路已经走回去,中途回路
if (wayStands.has(route.to))
{
return false;
// for (let stand of wayStands)
// {
// if (stand === route.to)
// break;
// wayStands.delete(stand);//删除不是回路的部分
// }
// return true;
}
//加入到已经走过的道路.
wayStands.add(route.to);
routeS.add(route);
//已经回原地,此处不验证该轮廓的正确性.
if (route.to === firstS)
return true;
//在下个路口试着往前走
let isFind = this.FindRegion(firstS, route.to, route.curve, wayStands, routeS, cuMaximumCount);
if (isFind)
return true;
else
{
wayStands.delete(route.to);//不走这条路
routeS.delete(route);
}
}
return false;
}
}