|
@@ -0,0 +1,879 @@
|
|
|
+import * as THREE from "../../libs/three.js/build/three.module.js";
|
|
|
+import math from "./math";
|
|
|
+
|
|
|
+var points = [];
|
|
|
+var lines = [];
|
|
|
+var rings = [];
|
|
|
+
|
|
|
+var precision = 0.1 //容错精度 //正常是0.01 但是在编辑时容易出现交错的线看不出来,导致需要getSliceLines 然后多出新增点
|
|
|
+
|
|
|
+
|
|
|
+var getPoint = function(o, type){
|
|
|
+ var point;
|
|
|
+ if(typeof o == "string" || typeof o == "number")point = points.find(p=> p.ids.includes(o));
|
|
|
+ else{
|
|
|
+ point = points.find(p=> math.closeTo(p.x , o.x, precision) && math.closeTo(p.y , o.y, precision) )
|
|
|
+ if(!point) point = new Point(o.x, o.y,{record:true, id:o.id}, type)
|
|
|
+ else{
|
|
|
+ //console.log('addPoint', point, o)
|
|
|
+ point.addPoint(o.id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(!point){
|
|
|
+ console.log("no point!")
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return point
|
|
|
+}
|
|
|
+
|
|
|
+var getLine = function(id){
|
|
|
+ return lines.find(line=> line.ids.includes(id));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+var getAngleInfo = function(points){
|
|
|
+ var info = {}
|
|
|
+ info.angle = points[1].clone().sub(points[0]).angle();
|
|
|
+ if(math.closeTo(info.angle, Math.PI*2)){ //如360-0.01
|
|
|
+ info.angle -= Math.PI*2; //有可能得到负数-0.001
|
|
|
+ }else if(info.angle > Math.PI || math.closeTo(info.angle, Math.PI)){//如180+-0.01
|
|
|
+ info.angle -= Math.PI;
|
|
|
+ info.reverse = true
|
|
|
+ }
|
|
|
+ return info //结果大约是 0 - 3.14
|
|
|
+}
|
|
|
+
|
|
|
+class Point extends THREE.Vector2{
|
|
|
+ constructor(x, y, o={}){
|
|
|
+ super(x, y);
|
|
|
+
|
|
|
+ if(o.record){
|
|
|
+ this.id = o.id;
|
|
|
+ if(this.id == void 0) this.id = "add_"+points.length
|
|
|
+ this.ids = [this.id] ;//存储拥有该坐标的点原始数据的id
|
|
|
+ points.push(this)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.type = o.type || ""
|
|
|
+ this.lines = [];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ addPoint(id){
|
|
|
+ this.ids.push(id)
|
|
|
+ }
|
|
|
+
|
|
|
+ searchLineByFactor(dir, type, comeLine){
|
|
|
+
|
|
|
+ var lines = this.lines.filter(line=>line.searchTime<2)
|
|
|
+
|
|
|
+ if(lines.length==0)return;
|
|
|
+ else if(lines.length==1)return lines[0];
|
|
|
+ else lines = lines.filter(line=>line!=comeLine)
|
|
|
+
|
|
|
+ if(lines.length==1)return lines[0];
|
|
|
+
|
|
|
+ var result;
|
|
|
+ lines.forEach(line=>{
|
|
|
+ var vec = line.getVector();
|
|
|
+ if(line.points[1] == this) vec.negate();
|
|
|
+ var factor = math.getVec2Angle(dir, vec);
|
|
|
+
|
|
|
+ if(new THREE.Vector3(dir.x, dir.y, 0).cross(new THREE.Vector3(vec.x, vec.y, 0)).z<0) factor*= -1 /////
|
|
|
+
|
|
|
+ if(!result){
|
|
|
+ result = {line, factor}
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ if(type == "min" && factor<result.factor || type == "max" && factor>result.factor) result = {line, factor}
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return result.line;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var lineLen=0;
|
|
|
+class Line{
|
|
|
+ constructor(o){
|
|
|
+ if(o.points[0] == o.points[1])return;
|
|
|
+ this.points = o.points;
|
|
|
+ this.type = o.type || 'line'
|
|
|
+
|
|
|
+ if(this.type == 'line'){
|
|
|
+ var oldLine = lines.find(line=>line.points.includes(o.points[0]) && line.points.includes(o.points[1]))
|
|
|
+ if(oldLine){
|
|
|
+ o.id != void 0 && oldLine.ids.push(o.id)
|
|
|
+ return oldLine;
|
|
|
+ }
|
|
|
+ this.id = o.id == void 0 ? ("line"+lineLen ++) : o.id
|
|
|
+ this.ids = [this.id]
|
|
|
+
|
|
|
+ o.dontWriteToPoint || this.points.forEach((point)=>{point.lines.push(this)})
|
|
|
+ o.isChild || lines.push(this);
|
|
|
+ this.searchTime = 0 // 最多两次
|
|
|
+ }
|
|
|
+
|
|
|
+ this.children = [];//分割
|
|
|
+ this.parents = [];//分割
|
|
|
+ this.match = [];
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ getAngleInfo(){
|
|
|
+ var angleInfo = getAngleInfo(this.points)
|
|
|
+ this.angle = angleInfo.angle
|
|
|
+ this.reverse = angleInfo.reverse
|
|
|
+ }
|
|
|
+
|
|
|
+ getIntersectWithLine(line, precision){
|
|
|
+ var joint = line.points.find(point=>this.points.includes(point))
|
|
|
+ if(joint)return {point:joint, type:"joint"};
|
|
|
+
|
|
|
+ var intersect = math.isLineIntersect( line.points , this.points , false, precision );
|
|
|
+ if(intersect) return {point: intersect, type:"intersect"};
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ writeToPoint(){
|
|
|
+ this.points.forEach((point)=>{point.lines.includes(this) || point.lines.push(this)})
|
|
|
+ }
|
|
|
+
|
|
|
+ checkIfParent(line){
|
|
|
+ if(this == line){
|
|
|
+ return true;//原因就是slice的点和端点很近 误差导致
|
|
|
+ }
|
|
|
+ else return this.parents.find(e=>e.checkIfParent(line))
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ splitByPoint(point){
|
|
|
+ var line1 = new Line({points:[point, this.points[0]], dontWriteToPoint:true, hasntsure:true})
|
|
|
+ var line2 = new Line({points:[point, this.points[1]], dontWriteToPoint:true, hasntsure:true})
|
|
|
+
|
|
|
+ if(!line1.points || !line2.points){//有至少一个是点相同的,没写到group.lines里
|
|
|
+
|
|
|
+ console.warn('splitByPoint 线有点相同')
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(this.checkIfParent(line1)||this.checkIfParent(line2) || line1.checkIfParent(this) || line2.checkIfParent(this)){
|
|
|
+ console.warn("splitByPoint 发现parent和children一样")//,请检查getSliceWalls,尤其 if(math.closeTo(line1.angle,line2.angle)){ 处
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ var deal = (line)=>{
|
|
|
+ this.children.push(line);
|
|
|
+ line.parents.push(this)
|
|
|
+
|
|
|
+ if(!lines.includes(line))lines.push(line)
|
|
|
+ line.writeToPoint()
|
|
|
+
|
|
|
+ }
|
|
|
+ deal(line1)
|
|
|
+ deal(line2)
|
|
|
+
|
|
|
+ var index = this.points[0].lines.indexOf(this);
|
|
|
+ index > -1 && this.points[0].lines.splice(index,1)
|
|
|
+ var index = this.points[1].lines.indexOf(this);
|
|
|
+ index > -1 && this.points[1].lines.splice(index,1)
|
|
|
+
|
|
|
+
|
|
|
+ return [line1,line2]
|
|
|
+ }
|
|
|
+ splitByPoints(points){
|
|
|
+ points = points.map(point=>{return {dis:point.distanceTo(this.points[0]), point:point}})
|
|
|
+ points.sort((point1, point2)=>{return point1.dis - point2.dis})
|
|
|
+ var children = [];
|
|
|
+
|
|
|
+
|
|
|
+ points.forEach((point, index)=>{
|
|
|
+ var line1 = new Line({points:[point.point, index==0?this.points[0]:points[index-1].point ],group:this.group , dontWriteToPoint:true, hasntsure:true})
|
|
|
+ children.push(line1)
|
|
|
+ })
|
|
|
+ var line2 = new Line({points:[points[points.length-1].point, this.points[1] ],group:this.group , dontWriteToPoint:true, hasntsure:true})
|
|
|
+ children.push(line2);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var a = children.find(line=> !line.points || this.checkIfParent(line) || line.checkIfParent(this))
|
|
|
+ if(a){
|
|
|
+ console.error("splitByPoints return")
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ children.forEach(line=>{
|
|
|
+ this.children.push(line);
|
|
|
+ line.parents.push(this)
|
|
|
+
|
|
|
+ if(!lines.includes(line))lines.push(line)
|
|
|
+ line.writeToPoint()
|
|
|
+ line.writeToPoint()
|
|
|
+ })
|
|
|
+
|
|
|
+ var index = this.points[0].lines.indexOf(this);
|
|
|
+ index > -1 && this.points[0].lines.splice(index,1)
|
|
|
+ var index = this.points[1].lines.indexOf(this);
|
|
|
+ index > -1 && this.points[1].lines.splice(index,1)
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ getAllSlices(){//如果有被分割的片段 就返回片段,否则返回自身
|
|
|
+ var children = [];
|
|
|
+ var traverse = function(elem){
|
|
|
+ if(elem.children.length == 0) children.push(elem)
|
|
|
+ else elem.children.forEach(traverse)
|
|
|
+ }
|
|
|
+ traverse(this)
|
|
|
+ return children
|
|
|
+ }
|
|
|
+ getVector(){
|
|
|
+ return this.points[1].clone().sub(this.points[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ getLength(){
|
|
|
+ return this.points[0].distanceTo(this.points[1])
|
|
|
+ }
|
|
|
+
|
|
|
+ getCenter(){
|
|
|
+ return this.points[1].clone().add(this.points[0]).multiplyScalar(.5);
|
|
|
+ }
|
|
|
+}
|
|
|
+var getMixedSet = function(arr1, arr2){//交集
|
|
|
+ return arr1.filter(item=>arr2.includes(item));
|
|
|
+}
|
|
|
+var getUnionSet = function(arr1, arr2){//并集
|
|
|
+ return arr1.concat(arr2.filter(item=>!arr1.includes(item)))
|
|
|
+}
|
|
|
+var getDifferenceSet = function(arr1, arr2){//差集
|
|
|
+ var arr11 = arr1.filter(item=>!arr2.includes(item));
|
|
|
+ var arr22 = arr2.filter(item=>!arr1.includes(item));
|
|
|
+ return arr11.concat(arr22)
|
|
|
+}
|
|
|
+var getDifferenceSetMuti = function(arr){//收集绝对没有重复的元素,也就是判断出现次数=1的
|
|
|
+ var set = [];
|
|
|
+ arr.forEach(arr1=>{
|
|
|
+ arr1.forEach(item=>{
|
|
|
+ var index = set.indexOf(item)
|
|
|
+ if(index>-1){
|
|
|
+ set.splice(index, 1)
|
|
|
+ }else{
|
|
|
+ set.push(item)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return set;
|
|
|
+}
|
|
|
+
|
|
|
+function DoorAtWhichLine(points, lines){
|
|
|
+ var mid = points[0].clone().add(points[1]).multiplyScalar(0.5)
|
|
|
+ lines = lines.filter(line=>math.ifPointAtLineBound(mid, line.points, precision))
|
|
|
+ if(lines.length == 0)return
|
|
|
+ var result = {line:null, dis:Infinity}
|
|
|
+ lines.forEach(line=>{
|
|
|
+ var foot = math.getFootPoint(mid, line.points[0], line.points[1] )
|
|
|
+ var dis = foot.distanceTo(mid)
|
|
|
+ if(dis<result.dis){
|
|
|
+ result.line = line; result.dis = dis
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return result
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+var ringLen = 0;
|
|
|
+class Ring{
|
|
|
+ constructor(o){
|
|
|
+ this.id = ringLen ++
|
|
|
+ this.type = o.type || 'normal';
|
|
|
+ this.points = o.points;
|
|
|
+ this.lines = o.lines;
|
|
|
+ rings.push(this);
|
|
|
+ this.child = []//包含的环
|
|
|
+ this.parent = []//被包含的环
|
|
|
+ this.smallNeibours = []//相邻最小环(存在和它有一个以上的相同边的最小环)
|
|
|
+
|
|
|
+ var area = math.getArea(this.points);
|
|
|
+ this.area = Math.abs(area)
|
|
|
+ this.isClockwise = area<0//是否逆时针。一般都是逆时针得到的,如果是顺时针,可能是贪吃蛇的情况,可能不是最小环,需要去掉。
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+var findLine = function(p1,p2){
|
|
|
+ return lines.find(line=>line.points.includes(p1) && line.points.includes(p2) )
|
|
|
+}
|
|
|
+var ifSamePart = function(checkPart , part){//checkPart中所包含的part片段是否和基准part的顺序一样(逆序也可以, 中间有其他数也可以,起始不同也行。比如 01234和204一样的)
|
|
|
+ var axis, startIndex, newCheckPart=[];
|
|
|
+
|
|
|
+
|
|
|
+ for(var j=0,len1 = checkPart.length; j<len1; j++){//将checkPart中比part多的数除去,使两个数组中包含的数完全相同。
|
|
|
+ if(part.indexOf(checkPart[j])>-1)newCheckPart.push(checkPart[j]);
|
|
|
+ }
|
|
|
+
|
|
|
+ for(var i=0,len = part.length; i<len; i++){
|
|
|
+ var index = newCheckPart.indexOf(part[i]);
|
|
|
+ if(index == -1)return false;
|
|
|
+ if(i == 0) startIndex = index;//标记第一个查找点对应的index
|
|
|
+ else if(i == 1){//标记查找顺序是正还是逆
|
|
|
+ axis = index - startIndex;
|
|
|
+ if(axis == len - 1) axis = -1;//刚好是首和尾
|
|
|
+ else if(axis == 1- len) axis = 1;
|
|
|
+
|
|
|
+ if(axis != -1 && axis != 1){
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }else{//判断是否是按顺序的
|
|
|
+ if(index != ((startIndex+axis * i + len) % len) ) return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {sameAxis:axis>0}; //如果一样的话返回正逆是否相同
|
|
|
+}
|
|
|
+
|
|
|
+//或者判断是否有相同边(但是相同点是可以组成不同环)
|
|
|
+var ifSameRing = function(ring1, ring2){//判断两个环是否相等。 除了可以逆向外顺序要对
|
|
|
+ if(ring1 instanceof Ring)ring1 = ring1.points;
|
|
|
+ if(ring2 instanceof Ring)ring2 = ring2.points;
|
|
|
+ if(ring1.length != ring2.length)return false;
|
|
|
+ if(ring1.lines && ring2.lines){
|
|
|
+ if(getDifferenceSet(ring1.lines , ring2.lines).length == 0)return true;//差集个数为0
|
|
|
+ }else{
|
|
|
+ if(ifSamePart(ring1, ring2))return true
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+var atWhichChildLine = function(point, line, precision){
|
|
|
+ if(line.children.length == 0){//这里可能要放低精度 保证能找到
|
|
|
+ if(math.ifPointAtLineBound(point, line.points, precision)) return line;
|
|
|
+
|
|
|
+ }else{
|
|
|
+ for(var i=0;i<line.children.length;i++){
|
|
|
+ var at = atWhichChildLine(point, line.children[i], precision)
|
|
|
+ if(at)return at
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function getSliceLines(){
|
|
|
+ var len = lines.length;
|
|
|
+
|
|
|
+
|
|
|
+ var deal = function(line1,line2){
|
|
|
+ if(line1 == line2)return;
|
|
|
+
|
|
|
+ if(line1.angle == void 0) line1.getAngleInfo()
|
|
|
+ if(line2.angle == void 0) line2.getAngleInfo()
|
|
|
+
|
|
|
+ var intersect = line1.getIntersectWithLine(line2, precision);
|
|
|
+ if(intersect){
|
|
|
+ var point //得到交点
|
|
|
+ if(intersect.type == "intersect"){
|
|
|
+ point = getPoint(intersect.point, "whenGetSliceLines");
|
|
|
+
|
|
|
+ var line1_ = atWhichChildLine(point, line1)
|
|
|
+ var line2_ = atWhichChildLine(point, line2)
|
|
|
+ //重合的情况还没考虑(平行)
|
|
|
+ if(!line1_) line1_ = atWhichChildLine(point, line1, precision)//降低精度
|
|
|
+ if(!line1_) line1_ = atWhichChildLine(point, line1, precision*2)//降低精度
|
|
|
+ if(!line2_) line2_ = atWhichChildLine(point, line2, precision)
|
|
|
+ if(!line2_) line2_ = atWhichChildLine(point, line2, precision*2)//降低精度
|
|
|
+ //拆分线条:
|
|
|
+
|
|
|
+ //如果还报错,找不到ChildLine,就直接返回吧 或者搞个循环 逐渐降低精度
|
|
|
+ if(!line1_ || !line2_){
|
|
|
+ console.warn("atWhichChildLine仍旧找不到 :" + line1.id + ',' + line2.id + ", pointId: "+point.id)
|
|
|
+ line1_ || console.warn("找不到line1")
|
|
|
+ line2_ || console.warn("找不到line2")
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(line1_.points.find(p=>p == point) && line2_.points.find(p=>p == point)){//这个点是line1_、 line2_端点,不做处理
|
|
|
+ //console.log("joint型 "+point.id)
|
|
|
+ }else if(line1_.points.find(p=>p == point)){//T型交叉
|
|
|
+ line2_.splitByPoint(point)//加入到母线中,之后还先用母线判断交点
|
|
|
+ //console.log("T型交叉1 "+point.id)
|
|
|
+ }else if(line2_.points.find(p=>p == point)){//T型交叉
|
|
|
+ line1_.splitByPoint(point)
|
|
|
+ //console.log("T型交叉2 "+point.id)
|
|
|
+ }else{//十字交叉
|
|
|
+ line1_.splitByPoint(point)
|
|
|
+ line2_.splitByPoint(point)
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ point = intersect.point//交点是端点
|
|
|
+ if(math.closeTo(line1.angle,line2.angle)){ //重合一部分
|
|
|
+ var children1 = line1.getAllSlices()
|
|
|
+ var children2 = line2.getAllSlices();
|
|
|
+ if(children1.length>1 || children2.length>1){ //使用最小分割片段来比较
|
|
|
+ children1.forEach(child1=>{
|
|
|
+ children2.forEach(child2=>{
|
|
|
+ deal(child1, child2)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var anotherPoint1 = line1.points.find(point_=>point_!=point)
|
|
|
+ var anotherPoint2 = line2.points.find(point_=>point_!=point)
|
|
|
+ if(math.ifPointAtLineBound(anotherPoint1, line2.points)){
|
|
|
+ line2.splitByPoint(anotherPoint1)
|
|
|
+ }else if(math.ifPointAtLineBound(anotherPoint2, line1.points)){
|
|
|
+ line1.splitByPoint(anotherPoint2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }else if(math.closeTo(line1.angle,line2.angle)){
|
|
|
+ var vec1 = line1.getVector()
|
|
|
+ var vec = line1.points[0].clone().sub(line2.points[0]);
|
|
|
+ var cos = math.getVec2Cos(vec1, vec);
|
|
|
+ if(math.closeTo(cos, -1, 1e-4) || math.closeTo(cos, 1, 1e-4)){ //共线
|
|
|
+
|
|
|
+ var children1 = line1.getAllSlices()
|
|
|
+ var children2 = line2.getAllSlices();
|
|
|
+ if(children1.length>1 || children2.length>1){ //使用最小分割片段来比较
|
|
|
+ children1.forEach(child1=>{
|
|
|
+ children2.forEach(child2=>{
|
|
|
+ deal(child1, child2)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断是否重叠
|
|
|
+ var A = line1.points[0];
|
|
|
+ var C = line1.reverse == line2.reverse ? line2.points[0] : line2.points[1];
|
|
|
+
|
|
|
+ var B = line1.points[1];
|
|
|
+ var D = line1.reverse == line2.reverse ? line2.points[1] : line2.points[0];
|
|
|
+
|
|
|
+ var BC = C.clone().sub(B)
|
|
|
+ var AD = D.clone().sub(A)
|
|
|
+ if(BC.length()<AD.length()){
|
|
|
+ var BA = A.clone().sub(B);
|
|
|
+ if(math.getVec2Angle(BC, BA) >= 1.57 )return;//没有重叠部分
|
|
|
+ }else{
|
|
|
+ var AB = B.clone().sub(A)
|
|
|
+ if(math.getVec2Angle(AD, AB) >= 1.57 )return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var f = function(line1,line2){
|
|
|
+ var one = math.ifPointAtLineBound(line1.points[0], line2.points);
|
|
|
+ var two = math.ifPointAtLineBound(line1.points[1], line2.points);
|
|
|
+ if(one && two){//line1在line2上
|
|
|
+ line2.splitByPoints( line1.points )
|
|
|
+ return true
|
|
|
+ }else if(one || two){//错开
|
|
|
+ var point1 = one ? line1.points[0] : line1.points[1];
|
|
|
+ var anotherPoint1 = one ? line1.points[1] : line1.points[0];
|
|
|
+ var dis1 = line2.points[0].distanceTo(anotherPoint1);
|
|
|
+ var dis2 = line2.points[1].distanceTo(anotherPoint1);
|
|
|
+ var point2 = dis1 < dis2 ? line2.points[0] : line2.points[1]
|
|
|
+ line1.splitByPoint(point2)
|
|
|
+ line2.splitByPoint(point1)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ f(line1, line2) || f(line2, line1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ for(let i=0;i<len;i++){
|
|
|
+ let line1 = lines[i];
|
|
|
+ for(let j=i+1;j<len;j++){
|
|
|
+ let line2 = lines[j];
|
|
|
+ deal(line1,line2)
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //console.log("原有线条个数:"+len)
|
|
|
+
|
|
|
+ //lines = lines.filter((line)=>{return line.children.length == 0})
|
|
|
+
|
|
|
+ //console.log("现有线条个数:"+lines.length)
|
|
|
+
|
|
|
+}
|
|
|
+var bound = new THREE.Box2()
|
|
|
+var build = function(o){
|
|
|
+ //融合了相近点
|
|
|
+ //根据bound 处理precision
|
|
|
+ o.points.forEach(p=>{
|
|
|
+ bound.expandByPoint(new THREE.Vector2(p.x,p.y))
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+ if(o.precision != void 0){
|
|
|
+ precision = o.precision
|
|
|
+ }else{
|
|
|
+ var boundSize = bound.getSize(new THREE.Vector2)
|
|
|
+ precision = THREE.Math.clamp(Math.max(boundSize.x, boundSize.y) / 70, 0.2, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ o.points.forEach(point=>getPoint(point))//{x:..,y:..}
|
|
|
+
|
|
|
+
|
|
|
+ o.lines.forEach(line=>{ //{p1:id1. p2:id2}
|
|
|
+ new Line({points:[getPoint(line.p1), getPoint(line.p2)], id:line.id })
|
|
|
+ })
|
|
|
+ //注意:不能出现一条线的两个点坐标一致,否则寻路时方向出错。 所以手动融合下相近点。
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+var searchRings = function(o={}){
|
|
|
+ points = [];
|
|
|
+ lines = [];
|
|
|
+ rings = [];
|
|
|
+ lineLen = ringLen = 0
|
|
|
+ o.points = o.points || []
|
|
|
+ o.lines = o.lines || []
|
|
|
+
|
|
|
+
|
|
|
+ build(o)
|
|
|
+
|
|
|
+ if(!o.dontSliceLines){
|
|
|
+ getSliceLines()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //查找最小回路:
|
|
|
+ //参考: 引入方向因子的最小回路、最大回路搜索算法.pdf
|
|
|
+ //方法: 逆时针寻找(标记)最外层大环 -->从走过的点开始逆时针寻找最小环(直到所有可走的路被走过两次)-->逆时针寻找最外层大环(直到所有可走的路被走过两次)-->..
|
|
|
+ //其中找大环时选择方向因子最小的路, 而小环则相反(但只有开始第一条路是一样的, 都是选择最左边的点的因子最小的路)。
|
|
|
+ //标记方法: 每条线需要被搜索两次才算完毕。搜索完毕的线退出搜索。(依据:搜索完全部最小回路后 , 在无向图中删除搜索过 2 次的边及孤立节点得到退化图 , 恰好构成最大回路。)
|
|
|
+ var searchTime = 0;
|
|
|
+ var addRingJudgeCount = 0
|
|
|
+ var addRingJudge = function(ring, lines, connectedLines, type){// 处理拣出的片段
|
|
|
+ addRingJudgeCount++;
|
|
|
+
|
|
|
+ //console.log("addRingJudge points("+ type+"):"+ ring.map(point=>point.id) )
|
|
|
+ if(o.onlyGetOutRing && type == "small")return
|
|
|
+
|
|
|
+ if(type == "small" || o.onlyGetOutRing){//挑出回路:
|
|
|
+ var newRings = []
|
|
|
+ while(ring.length){
|
|
|
+ var road = [];
|
|
|
+ var turnBack = false;
|
|
|
+ for(let i=0;i<ring.length;i++){
|
|
|
+ if(road.includes(ring[i])){//如果走到方才的点,可能形成回路。 无论是不是回路都要摘去这段。
|
|
|
+ var index = road.indexOf(ring[i])
|
|
|
+ var pointArr = ring.slice(index, i);
|
|
|
+ var linesArr = lines.slice(index, i);
|
|
|
+ ring.splice(index,i-index);
|
|
|
+ lines.splice(index,i-index);
|
|
|
+ if(pointArr.length>2){// 如果只有两个数,代表原路返回, 如 1->2(->1)
|
|
|
+ if( !rings.find(ring_=>ifSameRing(pointArr, ring_))) newRings.push( new Ring({points: pointArr, lines:linesArr}) )
|
|
|
+ }
|
|
|
+ turnBack = true
|
|
|
+ break;
|
|
|
+ }else{
|
|
|
+ road.push(ring[i])
|
|
|
+ turnBack = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(!turnBack){//没有重复的点,那么就直接处理整条。
|
|
|
+ if(ring.length>2){// 如果只有两个数,代表原路返回, 如 1->2(->1)
|
|
|
+ if( !rings.find(ring_=>ifSameRing(ring, ring_))) newRings.push( new Ring({points: ring, lines}) )
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(type != 'small'){
|
|
|
+ newRings.forEach(e=>e.isOutRing = true)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //console.log(newRings)
|
|
|
+ }else{
|
|
|
+ return ring
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var search = function(point2d, comeRoad, type, connectedLines){
|
|
|
+ searchTime++
|
|
|
+ var goLine;
|
|
|
+ var direction;
|
|
|
+ if(type.includes("big")){
|
|
|
+ if(!comeRoad){
|
|
|
+ if(type.includes("Left")){//逆时针
|
|
|
+ direction = new THREE.Vector2(1,0);
|
|
|
+ }else{
|
|
|
+ direction = new THREE.Vector2(-1,0);
|
|
|
+ }
|
|
|
+ goLine = point2d.searchLineByFactor(direction,"min")
|
|
|
+ }else{
|
|
|
+ var lastPoint = comeRoad.points[comeRoad.points.length-1]
|
|
|
+ direction = point2d.clone().sub(lastPoint);
|
|
|
+ goLine = point2d.searchLineByFactor(direction,"min", findLine(point2d, lastPoint))
|
|
|
+ }
|
|
|
+
|
|
|
+ }else{
|
|
|
+ if(!comeRoad){
|
|
|
+ //似乎找最小环时,第一条线也是找最小的因子,这样才能保证逆时针(除非只有顺时针一条路)
|
|
|
+ direction = new THREE.Vector2(1,0);
|
|
|
+ goLine = point2d.searchLineByFactor(direction,"min")
|
|
|
+
|
|
|
+ }else{
|
|
|
+ var lastPoint = comeRoad.points[comeRoad.points.length-1]
|
|
|
+ direction = point2d.clone().sub(lastPoint);
|
|
|
+ goLine = point2d.searchLineByFactor(direction,"max", findLine(point2d, lastPoint))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(!goLine)return
|
|
|
+
|
|
|
+
|
|
|
+ goLine.searchTime++;
|
|
|
+ connectedLines.includes(goLine) || connectedLines.push(goLine)
|
|
|
+
|
|
|
+
|
|
|
+ var nextPoint = goLine.points.find( point => point2d!=point )
|
|
|
+
|
|
|
+ //if( comeRoad && comeRoad.points[comeRoad.points.length - 1] == nextPoint ) return;//不能查找来时的方向(反方向)
|
|
|
+ //走不通就原路返回
|
|
|
+
|
|
|
+ var roadPoints = comeRoad ? comeRoad.points.concat([point2d]) : [point2d];//每个分叉都能构成一条新的road
|
|
|
+ var roadLines = comeRoad ? comeRoad.lines.concat([goLine]) : [goLine];
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //走到第一个点就算停止,这时候可能得到一个环、或者一段走了两遍的线、或者一条线上带了些环。
|
|
|
+ if(nextPoint == roadPoints[0]) return addRingJudge(roadPoints, roadLines, connectedLines, type) //形成环
|
|
|
+ else{
|
|
|
+ /* var len = roadPoints.indexOf(nextPoint);
|
|
|
+ if( len > -1){ //走到走过的路的某一点 构成这段路的回路
|
|
|
+ var points = roadPoints.slice(len, roadPoints.length);
|
|
|
+ var lines = roadLines.slice(len, roadPoints.length);
|
|
|
+ addRingJudge(points, lines)
|
|
|
+ }else{ */
|
|
|
+ return search(nextPoint, {lines:roadLines, points:roadPoints}, type, connectedLines);//继续寻路
|
|
|
+ //}
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ while(1){//搜寻一次大环
|
|
|
+ var connectedLines = [];//被搜寻过的且searchTime<2的线。一旦全部搜完就说明该连通区域搜寻完毕,继续查下一个连通区域。
|
|
|
+ var startPoint = null
|
|
|
+ points.forEach(point=>{//找出x最小的点
|
|
|
+ if(!point.lines.find(line=>line.searchTime<2))return;
|
|
|
+ if(!startPoint)startPoint = point
|
|
|
+ else if(point.x < startPoint.x)startPoint = point;
|
|
|
+ })
|
|
|
+ if(!startPoint)break; //说明全部找完
|
|
|
+
|
|
|
+
|
|
|
+ var ring = search(startPoint, null, "bigLeft", connectedLines)//逆时针
|
|
|
+ //search(startPoint, null, "bigRight", connectedLines);//顺时针(为了防止最外层不是回路之前写了顺时针,但如果是回路就会走重复。后来发现只要逆时针即可,因为走完后剩下的可以再次找大环)
|
|
|
+
|
|
|
+ connectedLines = connectedLines.filter(line=>line.searchTime<2)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ while(connectedLines.length>0){//目标是顺着connectedLines把所有连通的小环都找到
|
|
|
+
|
|
|
+ let points_ = [];//connectedLines中所有的点
|
|
|
+ connectedLines.forEach(line=>line.points.forEach(point=>{if(!points_.includes(point))points_.push(point) }))
|
|
|
+ var startPoint = null
|
|
|
+ points_.forEach(point=>{//找出x最小的点
|
|
|
+ if(!point.lines.find(line=>line.searchTime<2))return;
|
|
|
+ if(!startPoint)startPoint = point
|
|
|
+ else if(point.x < startPoint.x)startPoint = point;
|
|
|
+ })
|
|
|
+ if(!startPoint)break;
|
|
|
+
|
|
|
+
|
|
|
+ search(startPoint, null, "small", connectedLines)
|
|
|
+
|
|
|
+ connectedLines = connectedLines.filter(line=>line.searchTime<2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /* if(o.onlyGetOutRing){
|
|
|
+ rings = rings.filter(e=>e.isOutRing)
|
|
|
+ } */
|
|
|
+
|
|
|
+ //console.log("searchTime "+searchTime + ", addRingJudgeCount " +addRingJudgeCount)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //找出所有的相邻关系,包括公共边
|
|
|
+ var len = rings.length;
|
|
|
+ for(let i=0; i<len; i++){
|
|
|
+ let ring1 = rings[i]
|
|
|
+ for(let j=i+1; j<len; j++){
|
|
|
+ let ring2 = rings[j]
|
|
|
+ var bothHasLines = getMixedSet(ring1.lines, ring2.lines)
|
|
|
+ if(bothHasLines.length){//ring1oíring2?àáú
|
|
|
+ ring1.smallNeibours.push(ring2)
|
|
|
+ ring2.smallNeibours.push(ring1)
|
|
|
+ }else{
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ rings.forEach(ring1=>{
|
|
|
+ for(let i=0; i<len; i++){
|
|
|
+ var ring2 = rings[i];
|
|
|
+ if(ring1 == ring2 || ring1.smallNeibours.includes(ring2))continue;
|
|
|
+
|
|
|
+ let inside
|
|
|
+ for(let u=0;u<ring1.points.length;u++){
|
|
|
+ inside = math.isPointInArea(ring2.points, null, ring1.points[u]);
|
|
|
+ if(!inside)break
|
|
|
+ else if(inside && !inside.atLine){
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if(inside){ //只要其中一个点在ring2内,就说明ring1是内环
|
|
|
+ if(inside.atLine){//(还是会存在点全在线上的情况,这时候判断中心点)
|
|
|
+ var center = math.getCenterOfGravityPoint(ring1.points)
|
|
|
+ let inside1 = math.isPointInArea(ring2.points, null, center);
|
|
|
+ if(!inside1){
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ring2.child.push(ring1);
|
|
|
+ ring1.parent.push(ring2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ //去除非最小的ring 是否应该检测parent child?
|
|
|
+ /*
|
|
|
+ like this:
|
|
|
+ |———————————————————————|
|
|
|
+ |———|———————|———————| |
|
|
|
+ | | | | |
|
|
|
+ | |———————|———————| |
|
|
|
+ |———————————————————————|
|
|
|
+ */
|
|
|
+ var wiseRings = rings.filter(r=>!r.isClockwise)//一般都是逆时针得到的,如果是顺时针,可能是贪吃蛇的情况,可能不是最小环,需要去掉。
|
|
|
+ if(wiseRings.length > 0){
|
|
|
+ //console.log('%c存在非最小的ring! 进行处理:',"color:#00f");
|
|
|
+ wiseRings.forEach(ring=>{ //(此案例验证出smallNeibours就是它的最小构成,可以再看看别的案例)
|
|
|
+ if(ring.smallNeibours.length>0){//另:如果内部只有一个,说明它是最小环,不需要处理
|
|
|
+ var is = false
|
|
|
+ var difference = getDifferenceSet(ring.lines , getDifferenceSetMuti(ring.smallNeibours.concat(ring.child).map(ring=>ring.lines)))//获取所有smallNeibours和child的边中没有重复过的边(就是outline) 和该ring的线比较
|
|
|
+
|
|
|
+
|
|
|
+ is = difference.every(line=> ring.child.find(r=>r.lines.includes(line)) ) //多出的线只能是child中的线
|
|
|
+
|
|
|
+ if(is){
|
|
|
+ console.log('%c删除非最小环 ring'+ring.id,"color:#00f");
|
|
|
+ console.log(ring)
|
|
|
+ rings.splice(rings.indexOf(ring), 1)
|
|
|
+ ring.child.forEach(c=>{var index = c.parent.indexOf(ring);index>-1 && c.parent.splice(index,1)})
|
|
|
+ ring.parent.forEach(c=>{var index = c.child.indexOf(ring);index>-1 && c.child.splice(index,1)})
|
|
|
+ ring.smallNeibours.forEach(c=>{var index = c.smallNeibours.indexOf(ring);index>-1 && c.smallNeibours.splice(index,1)})
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /* rings = rings.filter(ring=>{
|
|
|
+ rings = rings.filter(ring=>{
|
|
|
+ var enoughSize = ring.area > 0.5
|
|
|
+ if(!enoughSize){console.log('因面积过小去除ring '+ring.id + " , area: "+ring.area)}
|
|
|
+ return enoughSize
|
|
|
+ })
|
|
|
+ rings.forEach(ring=>{
|
|
|
+ if(ring.closetChilds){
|
|
|
+ ring.closetChilds = ring.closetChilds.filter(e=>rings.includes(e))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ return rings
|
|
|
+
|
|
|
+ }) */ //在dealRings前不能随意删除rings,因为判断是否是最小环时需要全部的环
|
|
|
+
|
|
|
+ rings.forEach(ring=>{ //这里和cad中的不太一样, cad中双数个parent算外环,单数内环; 这里不分内外, 只看有无parent child
|
|
|
+ if(ring.parent.length){
|
|
|
+ ring.closetParent = ring.parent.find(ring_ => ring_.parent.length == ring.parent.length - 1)//最近一层的大环就是比它的parent个数少一的
|
|
|
+ ring.closetParent.closetChilds || (ring.closetParent.closetChilds = [])//内环可能多个
|
|
|
+ ring.closetParent.closetChilds.push(ring)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //console.log(rings)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var _ring = rings.map(ring=>{
|
|
|
+ var data = {
|
|
|
+ id: ring.id,
|
|
|
+ points: ring.points.map(point=>{return {id: point.ids[0], x:point.x, y:point.y}}),
|
|
|
+ /* doors : o.doors.filter(door=>{
|
|
|
+ if(ring.closetChilds){
|
|
|
+ var childOutLines = getDifferenceSetMuti(ring.closetChilds.map(ring=>ring.lines)) //最近子环的外边
|
|
|
+ return ring.lines.concat(childOutLines).includes(door.atLine)
|
|
|
+ }else{
|
|
|
+ return ring.lines.includes(door.atLine)
|
|
|
+ }
|
|
|
+ }), */
|
|
|
+ area:ring.area,
|
|
|
+ closetParent : ring.closetParent && ring.closetParent.id,
|
|
|
+ closetChilds : ring.closetChilds && ring.closetChilds.map(e=>e.id)
|
|
|
+ }
|
|
|
+
|
|
|
+ return data
|
|
|
+ })
|
|
|
+
|
|
|
+ //console.log(JSON.stringify(_ring))
|
|
|
+
|
|
|
+ return _ring
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+export default searchRings
|