!54 实现点在多段线内部的算法

Merge pull request !54 from ChenX/PointInPolyline
pull/704344/MERGE
ChenX 7 years ago
parent 3e62adc4ce
commit 4d424fba59

@ -15,6 +15,29 @@
"name": "Attach to Chrome",
"port": 7778,
"webRoot": "${workspaceRoot}"
},
//Ref: https://github.com/Microsoft/vscode-recipes/blob/master/debugging-jest-tests/.vscode/launch.json
{
"type": "node",
"request": "launch",
"name": "Jest All",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": [
"--runInBand"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Jest Current File",
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": [
"${relativeFile}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}

@ -0,0 +1,144 @@
import { Vector2, Vector3 } from 'three';
import { IsPointInPolyLine } from '../../src/DatabaseServices/PointInPolyline';
import { Polyline } from '../../src/DatabaseServices/Polyline';
test('点在多段线内', () =>
{
let pl = new Polyline([
{
pt: new Vector2(0, 0),
bul: 0
},
{
pt: new Vector2(10, 0),
bul: 0
},
{
pt: new Vector2(10, 10),
bul: 0
},
{
pt: new Vector2(8, 10),
bul: 0
},
{
pt: new Vector2(8, 5),
bul: 0
},
{
pt: new Vector2(4, 5),
bul: 0
},
{
pt: new Vector2(4, 10),
bul: 0
},
{
pt: new Vector2(0, 10),
bul: 0
},
]);
pl.CloseMark = true;
let bIn = IsPointInPolyLine(pl, new Vector3(8, 3));//?
expect(bIn).toBeTruthy();
});
describe("", () =>
{
test('退化', () =>
{
let pl = new Polyline(
[
{
pt: new Vector2(0, 0),
bul: 0
},
{
pt: new Vector2(10, 0),
bul: 0
},
{
pt: new Vector2(5, 5),
bul: 0
},
])
pl.CloseMark = true;
expect(IsPointInPolyLine(pl, new Vector3(5, 2))).toBeTruthy();
});
test('退化2', () =>
{
//右边中间 <
let pl = new Polyline(
[
{
pt: new Vector2(0, 0),
bul: 0
},
{
pt: new Vector2(10, 0),
bul: 0
},
{
pt: new Vector2(5, 5),
bul: 0
},
{
pt: new Vector2(10, 10),
bul: 0
},
{
pt: new Vector2(0, 10),
bul: 0
},
])
pl.CloseMark = true;
expect(IsPointInPolyLine(pl, new Vector3(5, 2))).toBeTruthy();
});
test('左边中间', () =>
{
//左边中间
let pl = new Polyline([
{
pt: new Vector2(0, 0),
bul: 0
},
{
pt: new Vector2(10, 0),
bul: 0
},
{
pt: new Vector2(10, 10),
bul: 0
},
{
pt: new Vector2(0, 10),
bul: 0
},
{
pt: new Vector2(0, 8),
bul: 0
},
{
pt: new Vector2(5, 5),
bul: 0
}]
)
pl.CloseMark = true;
expect(IsPointInPolyLine(pl, new Vector3(5, 2))).toBeTruthy();
});
})

@ -37,7 +37,6 @@
"html-loader": "^0.5.5",
"html-webpack-plugin": "^2.30.1",
"jest": "^22.1.4",
"jest-environment-node-debug": "^2.0.0",
"less": "^2.7.3",
"less-loader": "^4.0.5",
"mobx-react-devtools": "^4.2.15",

@ -2,6 +2,8 @@ import { app } from '../ApplicationServices/Application';
import { Curve } from '../DatabaseServices/Curve';
import { Command } from '../Editor/CommandMachine';
import { PromptStatus } from '../Editor/PromptResult';
import { Polyline } from '../DatabaseServices/Polyline';
import { IsPointInPolyLine } from '../DatabaseServices/PointInPolyline';
export class Command_Offset implements Command
{
@ -72,6 +74,11 @@ export class Command_TestOffset implements Command
let d = cu.GetFistDeriv(cu.GetParamAtPoint(ptClose));//切线。
let c = toPtVec.cross(d);
if (cu instanceof Polyline)
{
c.z = (IsPointInPolyLine(cu, p)) ? -1 : 1;
}
lastpls = cu.GetOffsetCurves(p.distanceTo(cu.GetClosestPointTo(p, !cu.IsClose)) * Math.sign(c.z));
lastpls.forEach((offCur) =>
{

@ -1,9 +1,14 @@
import { Command } from "../Editor/CommandMachine";
import { app } from "../ApplicationServices/Application";
import { Vector3 } from 'three';
import { app } from '../ApplicationServices/Application';
import { Arc } from '../DatabaseServices/Arc';
import { Circle } from '../DatabaseServices/Circle';
import { Curve } from '../DatabaseServices/Curve';
import { Line } from '../DatabaseServices/Line';
import { Curve, ExtendType } from "../DatabaseServices/Curve";
import { PromptStatus } from "../Editor/PromptResult";
import { Polyline } from "../DatabaseServices/Polyline";
import { IsPointInBowArc, IsPointInPolyLine } from '../DatabaseServices/PointInPolyline';
import { Polyline } from '../DatabaseServices/Polyline';
import { Command } from '../Editor/CommandMachine';
import { PromptStatus } from '../Editor/PromptResult';
export class Command_ClosePt implements Command
@ -18,10 +23,16 @@ export class Command_ClosePt implements Command
{
let cu = cuRes.Entity as Curve;
let line = new Line();
let closeCir = new Circle(new Vector3(), 0.5);
let derLine = new Line();
app.m_Database.ModelSpace.Append(closeCir);
app.m_Database.ModelSpace.Append(line);
app.m_Database.ModelSpace.Append(derLine);
app.m_Editor.AddNoSnapEntity(closeCir);
app.m_Editor.AddNoSnapEntity(line);
app.m_Editor.AddNoSnapEntity(derLine);
let extend = false;
while (true)
{
@ -36,6 +47,17 @@ export class Command_ClosePt implements Command
derLine.StartPoint = line.EndPoint;
derLine.EndPoint = line.EndPoint.add(cu.GetFistDeriv(line.EndPoint));
if (cu instanceof Arc)
{
line.ColorIndex = IsPointInBowArc(cu, p) ? 1 : 2;
}
if (cu instanceof Polyline)
{
closeCir.Center = p;
closeCir.ColorIndex = IsPointInPolyLine(cu, p) ? 1 : 2;
}
}
})
if (p.Status === PromptStatus.Keyword)

@ -0,0 +1,39 @@
import { Command } from "../Editor/CommandMachine";
import { app } from '../ApplicationServices/Application';
import { PromptStatus } from "../Editor/PromptResult";
import { Box3, Vector3 } from "three";
import { Circle } from '../DatabaseServices/Circle';
import { IsPointInPolyLine } from "../DatabaseServices/PointInPolyline";
import { Polyline } from '../DatabaseServices/Polyline';
export class Command_PtInCu implements Command
{
async exec()
{
let cuRes = await app.m_Editor.GetEntity();
if (cuRes.Status != PromptStatus.OK)
return;
if (!(cuRes.Entity instanceof Polyline))
{ return }
let p1Res = await app.m_Editor.GetPoint();
if (p1Res.Status != PromptStatus.OK) return;
let p2Res = await app.m_Editor.GetPoint({ BasePoint: p1Res.Value });
if (p2Res.Status != PromptStatus.OK) return;
let box = new Box3().setFromPoints([p1Res.Value, p2Res.Value]);
let size = box.getSize();
for (let i = 0; i < size.x; i++)
{
for (let j = 0; j < size.y; j++)
{
let c = new Circle(box.min.clone().add(new Vector3(i, j)), 0.5);
c.ColorIndex = IsPointInPolyLine(cuRes.Entity as Polyline, c.Center) ? 1 : 2;
app.m_Database.ModelSpace.Append(c);
}
}
}
}

@ -271,12 +271,6 @@ export class Arc extends Curve
}
return arcs;
}
PtOnCurve(pt: Vector3): boolean
{
let param = this.GetParamAtPoint(pt);
return !isNaN(param) && param >= 0 && param <= 1;
}
GetOffsetCurves(offsetDist: number)
{
if (this.m_Clockwise) offsetDist *= -1;

@ -1,6 +1,7 @@
import { Mesh, Object3D, Vector3 } from 'three';
import { ColorMaterial } from '../Common/ColorPalette';
import { equal } from '../Geometry/GeUtils';
import { RenderType } from '../GraphicsSystem/Enum';
import { IntersectOption } from '../GraphicsSystem/IntersectWith';
import { Factory } from './CADFactory';
@ -88,10 +89,13 @@ export abstract class Curve extends Entity
//翻转曲线.首尾调换.
Reverse() { };
//点在曲线内部
PtOnCurve(pt: Vector3): boolean { return false; }
//点在曲线上
PtOnCurve(pt: Vector3): boolean
{
return equal(this.StartPoint, pt) || equal(this.EndPoint, pt) || this.ParamOnCurve(this.GetParamAtPoint(pt));
}
//参数在曲线内部
//参数在曲线
ParamOnCurve(param: number): boolean { return !isNaN(param) && param >= 0 && param <= this.EndParam; }
GetOffsetCurves(offsetDist: number): Array<Curve> { return; }

@ -151,11 +151,6 @@ export class Line extends Curve
{
return this.GetDistAtParam(this.GetParamAtPoint(pt));
}
PtOnCurve(pt: Vector3): boolean
{
let param = this.GetParamAtPoint(pt);
return !isNaN(param) && param >= -1e-6 && param <= 1.0000001;
}
GetSplitCurves(params: number[] | number)
{
let pts = new Array<Vector3>();

@ -0,0 +1,154 @@
import { Vector2, Vector3 } from 'three';
import { angle, equal, equaln } from '../Geometry/GeUtils';
import { IntersectOption } from '../GraphicsSystem/IntersectWith';
import { Arc } from './Arc';
import { Line } from './Line';
import { Polyline } from './Polyline';
/**
* ,.
* :http://www.cnblogs.com/miloyip/archive/2013/04/19/3029852.html
*
* @param {Arc} arc
* @param {Vector3} pt
* @returns {boolean} .
*/
function IsPointInCircularSector(arc: Arc, pt: Vector3): boolean
{
let center = arc.Center;
let disSq = center.distanceTo(pt);
if (disSq > arc.Radius * arc.Radius) return false;
let an = angle(pt.clone().sub(center));
let param = arc.GetParamAtAngle(an);
return arc.ParamOnCurve(param);
}
/**
*
*
* @param {Arc} arc
* @param {Vector3} pt
* @returns {boolean}
*/
export function IsPointInBowArc(arc: Arc, pt: Vector3): boolean
{
let pv = pt.clone().sub(arc.StartPoint);
let av = arc.EndPoint.sub(arc.StartPoint);
pv.cross(av);
//未优化的代码
// if (pv.z > 0 && arc.IsClockWise)
// return false;
// else if (pv.z < 0 && !arc.IsClockWise)
// return false;
// else
// return arc.Center.distanceToSquared(pt) < arc.Radius * arc.Radius;
//简化的代码
if ((pv.z > 0) !== arc.IsClockWise)
{
return arc.Center.distanceToSquared(pt) < arc.Radius * arc.Radius;
}
return false;
}
/**
* 线
*
* @export
* @param {Polyline} pl 线
* @param {Vector3} pt
* @returns {boolean} 线
*/
export function IsPointInPolyLine(pl: Polyline, pt: Vector3): boolean
{
let crossings = 0;
let insLine = new Line(pt, pt.clone().add(new Vector3(0, 10, 0)));
for (let i = 0; i < pl.EndParam; i++)
{
let cu = pl.GetCurveAtIndex(i);
let inpts = cu.IntersectWith(insLine, IntersectOption.ExtendArg);
for (let pti of inpts)
{
if (pti.y < pt.y)
continue;
if (equal(pti, cu.StartPoint))
{
let der = cu.GetFistDeriv(0);
if (der.x < 1e-3) //左边+ 右边0
crossings++;
}
else if (equal(pti, cu.EndPoint))
{
let der = cu.GetFistDeriv(1);
if (der.x > 1e-3) //左边+ 右边0
crossings++;
}
else
{
let der = cu.GetFistDeriv(pti);
if (!equaln(der.x, 0)) //相切.
crossings++;
}
}
}
return (crossings % 2) === 1;
}
/**
*
*
* @param {Vector3} pt
* @param {Vector2[]} pts
* @returns
*/
function IsPointInPolygon(pt: Vector3, pts: Vector2[])
{
let crossings = 0; //int
let [px, py] = [pt.x, pt.y];
let ptCout = pts.length;
for (let i = 0; i < ptCout; i++)
{
let pti = pts[i];
let ptn = pts[(i + 1) % ptCout];
let [x1, x2] = [pti.x, ptn.x];
/* This is done to ensure that we get the same result when
the line goes from left to right and right to left */
if (x1 > x2)[x1, x2] = [x2, x1];
/* First check if the ray is possible to cross the line */
if (px > x1 && px <= x2 && (py < pti.y || py <= ptn.y))
{
const eps = 0.000001;
/* Calculate the equation of the line */
let dx = ptn.x - pti.x;
let dy = ptn.y - pti.y;
let k;
if (Math.abs(dx) < eps)
k = 1e300;
else
k = dy / dx;
let m = pti.y - k * pts[i].x;
/* Find if the ray crosses the line */
let y2 = k * px + m;
if (py <= y2)
crossings++;
}
}
return crossings % 2 === 1;
}

@ -52,6 +52,7 @@ import { DrawText } from '../Add-on/DrawText';
// import { DrawFloor } from '../Add-on/DrawFloor';
// import { RevTarget, SaveTarget } from '../Add-on/RenderTarget';
import { Command_Array } from '../Add-on/Array';
import { Command_PtInCu } from '../Add-on/ptincu';
export function registerCommand()
{
commandMachine.RegisterCommand("l", new DrawLine())
@ -157,6 +158,9 @@ export function registerCommand()
//阵列
commandMachine.RegisterCommand("array", new Command_Array());
commandMachine.RegisterCommand("incu", new Command_PtInCu());
// commandMachine.RegisterCommand("st", new SaveTarget())
// commandMachine.RegisterCommand("rt", new RevTarget())

@ -14,7 +14,7 @@ export function equaln(v1: number, v2: number, fuzz = 1e-3)
}
export function equal(v1: THREE.Vector3, v2: THREE.Vector3)
{
return v1.distanceToSquared(v2) < 1e-12;
return v1.distanceToSquared(v2) < 1e-8;
}
export function fixAngle(an: number, fixAngle: number, fuzz: number = 0.1)

Loading…
Cancel
Save