關於優化多段線中重複點
本帖最后由 箭头_Row 于 2024-8-8 23:29 编辑功能介紹:優化多段線中重複點
優化方向:感覺首尾那段單獨弄出來的就為了做索引號好笨的方法,想考慮用其它方法替代。
namespace ArrowTools;
public class P_PolylineTest
{
public void P_Polyline()
{
var p1 = new Point2d(5, 0);
var p2 = new Point2d(10, 0);
var p3 = new Point2d(15, 0);
var bu1 = PointEx.GetArcBulge(p1, p2, p3);
Env.Print(bu1);
p3 = new Point2d(0, 0);
var bu2 = p1.GetArcBulge(p2, p3);
// 備註:此處如果形參點順序不同剛輸出結果不同。
Env.Print(bu2);
Env.Print(MathEx.ConvertRadToDeg(bu2));
}
public void P_Polyline_02()
{
using DBTrans tr = new();
var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
if (ents == null)
return;
foreach (var pl in ents)
{
Env.Printl($"顶点个数:{pl.NumberOfVertices}");
var deleteNum = SimplifyPolylineDemo(pl);
Env.Printl($"优化点个数:{deleteNum}");
}
}
public void P_Polyline_03()
{
using DBTrans tr = new();
var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
if (ents == null)
return;
foreach (var pl in ents)
{
Env.Printl($"顶点个数:{pl.NumberOfVertices}");
#if Debug
for (int i = pl.NumberOfVertices - 1; i >= 0; i--)
{
var type = pl.GetSegmentType(i);
Env.Printl(type.GetType().Name + ":" + type.ToString());
}
#endif
#if true
var deleteNum_Coincident = SimplifyPolyline_Coincident(pl);
Env.Printl($"优化点个数_Coincident: {deleteNum_Coincident} ");
#endif
#if true
var deleteNum_Line = SimplifyPolyline_Line(pl);
Env.Printl($"优化点个数_Line: {deleteNum_Line}");
#endif
#if true
var deleteNum_Arc = SimplifyPolyline_Arc(pl);
Env.Printl($"优化点个数_Arc: {deleteNum_Arc}");
#endif
}
}
public void P_Polyline_05()
{
using DBTrans tr = new();
var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
if (ents == null)
return;
foreach (var pl in ents)
{
Env.Printl($"顶点个数:{pl.NumberOfVertices}");
for (var i = pl.NumberOfVertices - 1; i >= 0; i--)
{
var type = pl.GetSegmentType(i);
if (type == SegmentType.Arc)
Env.Printl(pl.GetBulgeAt(i));
}
}
}
public void P_Polyline_07()
{
using DBTrans tr = new();
var ents = Env.Editor.SSGet()?.Value?.GetEntities<Polyline>();
if (ents == null)
return;
foreach (var pl in ents)
{
Env.Printl($"顶点个数:{pl.NumberOfVertices}");
var deleteNum = SimplifyPolyline(pl);
Env.Printl($"优化端点个数: {deleteNum}");
}
}
/// <summary>
/// 優化多段線中重複點:清理多余端點
/// </summary>
/// <param name="pl">多段線</param>
/// <param name="tol">容差值</param>
/// <returns>清理的端點數量</returns>
private static int SimplifyPolyline(Polyline pl, double tol = 1e-6)
{
if (pl.NumberOfVertices < 3)
return 0;
int num = pl.NumberOfVertices;
SimplifyPolyline_Coincident(pl);
SimplifyPolyline_Line(pl, tol);
SimplifyPolyline_Arc(pl, tol);
return num - pl.NumberOfVertices;
}
/// <summary>
/// 優化多段線中重複點:清理相同點的端點
/// </summary>
/// <param name="pl">多段線</param>
/// <returns>清理的端點數量</returns>
private static int SimplifyPolyline_Coincident(Polyline pl, double tol = 1e-6)
{
if (pl.NumberOfVertices < 3)
return 0;
int num = pl.NumberOfVertices;
using (pl.ForWrite())
{
for (var i = pl.NumberOfVertices - 1; i >= 0; i--)
{
if (pl.NumberOfVertices < 3)
break;
// 優化多段線:頂點去重
var type = pl.GetSegmentType(i);
if (type == SegmentType.Coincident)
pl.RemoveVertexAt(i);
}
}
return num - pl.NumberOfVertices;
}
/// <summary>
/// 優化多段線中重複點:清理共線的多余端點
/// </summary>
/// <param name="pl">多段線</param>
/// <param name="tol">容差值</param>
/// <returns>清理的端點數量</returns>
private static int SimplifyPolyline_Line(Polyline pl, double tol = 1e-6)
{
if (pl.NumberOfVertices < 3)
return 0;
int num = pl.NumberOfVertices;
using (pl.ForWrite())
{
var p1 = Point2d.Origin;
var p2 = Point2d.Origin;
var p3 = Point2d.Origin;
double bu = default;
for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
{
if (pl.NumberOfVertices < 3)
break;
// 優化多段線:頂點去重,非直線跳過
var type01 = pl.GetSegmentType(i);
if (i != pl.NumberOfVertices - 1)
if (type01 != SegmentType.Line)
continue;
var type02 = pl.GetSegmentType(i - 1);
if (type02 != SegmentType.Line)
continue;
// 獲取相鄰三点
p1 = pl.GetPoint2dAt(i);
p2 = pl.GetPoint2dAt(i - 1);
p3 = pl.GetPoint2dAt(i - 2);
// 優化第1點至 第2點、第3點 向量.Normal相等時
bu = p1.GetArcBulge(p2, p3, tol);
if (bu == 0)
pl.RemoveVertexAt(index: i - 1); // 移除第2点
}
// 判斷首尾如果閉合的部份
if (pl.Closed && pl.NumberOfVertices > 2)
{
// 從結束-1點往前比對
p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 2);
p2 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
p3 = pl.GetPoint2dAt(0);
bu = p1.GetArcBulge(p2, p3, tol);
if (bu == 0)
pl.RemoveVertexAt(index: pl.NumberOfVertices - 1); // 移除第2点
// 從起始+1點往后比對
p1 = pl.GetPoint2dAt(1);
p2 = pl.GetPoint2dAt(0);
p3 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
bu = p1.GetArcBulge(p2, p3, tol);
if (bu == 0)
pl.RemoveVertexAt(index: 0); // 移除第2点
}
}
return num - pl.NumberOfVertices;
}
/// <summary>
/// 優化多段線中重複點:清理共圓弧的多余端點
/// </summary>
/// <param name="pl">多段線</param>
/// <param name="tol">容差值</param>
/// <returns>清理的端點數量</returns>
// AutoCAD 中凸度(bulge)的概念:
// https://www.cnblogs.com/BensonLaur/p/16189673.html
private static int SimplifyPolyline_Arc(Polyline pl, double tol = 1e-6)
{
if (pl.NumberOfVertices < 3)
return 0;
int num = pl.NumberOfVertices;
using (pl.ForWrite())
{
double bulge = default;
double bulge02 = default;
double angle = default;
double angle02 = default;
double radius = default;
double radius02 = default;
var p1 = Point2d.Origin;
var p2 = Point2d.Origin;
var p3 = Point2d.Origin;
var center = Point2d.Origin;
var center02 = Point2d.Origin;
double b = default;
double x = default;
double y = default;
for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
{
if (pl.NumberOfVertices < 3)
break;
// 優化多段線:頂點去重,非Arc跳過
var type01 = pl.GetSegmentType(i);
if (i != pl.NumberOfVertices - 1)
if (type01 != SegmentType.Arc)
continue;
var type02 = pl.GetSegmentType(i - 1);
if (type02 != SegmentType.Arc)
continue;
// 獲取相鄰三点
p1 = pl.GetPoint2dAt(i);
p2 = pl.GetPoint2dAt(i - 1);
p3 = pl.GetPoint2dAt(i - 2);
bulge = pl.GetBulgeAt(i - 1);
angle = 4 * Math.Atan(bulge);// 計算出角度值
radius = (p1.GetDistanceTo(p2) / 2) / Math.Sin(angle / 2);// 計算出半徑值
b = (1 / bulge - bulge) / 2;
x = 0.5 * ((p1.X + p2.X) - b * (p1.Y - p2.Y));
y = 0.5 * ((p1.Y + p2.Y) + b * (p1.X - p2.X));
center = new Point2d(x, y);
bulge02 = pl.GetBulgeAt(i - 2);
angle02 = 4 * Math.Atan(bulge02);// 計算出角度值
radius02 = (p2.GetDistanceTo(p3) / 2) / Math.Sin(angle02 / 2);// 計算出半徑值
b = (1 / bulge02 - bulge02) / 2;
x = 0.5 * ((p2.X + p3.X) - b * (p2.Y - p3.Y));
y = 0.5 * ((p2.Y + p3.Y) + b * (p2.X - p3.X));
center02 = new Point2d(x, y);
#if Debug
Env.Printl($"before 半徑01 {radius} ,{bulge} ,{center}");
Env.Printl($"before 半徑02 {radius02} ,{bulge02} ,{center02}");
#endif
// 比對半徑及圓心座標值
if (Math.Abs(radius - radius02) < tol && center.GetDistanceTo(center02) < tol)
{
pl.RemoveVertexAt(index: i - 1); // 移除第2点
angle += angle02;// 計算出角度值
bulge = Math.Tan(angle / 4);
pl.SetBulgeAt(i - 2, bulge);
#if Debug
Env.Printl($"after 半徑{radius} ,{bulge}");
#endif
}
}
// 判斷首尾如果閉合的部份
if (pl.Closed && pl.NumberOfVertices > 2)
{
// 從結束-1點往前比對
p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 2);
p2 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
p3 = pl.GetPoint2dAt(0);
bulge = pl.GetBulgeAt(pl.NumberOfVertices - 1);
angle = 4 * Math.Atan(bulge);// 計算出角度值
radius = (p1.GetDistanceTo(p2) / 2) / Math.Sin(angle / 2);// 計算出半徑值
b = (1 / bulge - bulge) / 2;
x = 0.5 * ((p1.X + p2.X) - b * (p1.Y - p2.Y));
y = 0.5 * ((p1.Y + p2.Y) + b * (p1.X - p2.X));
center = new Point2d(x, y);
bulge02 = pl.GetBulgeAt(0);
angle02 = 4 * Math.Atan(bulge02);// 計算出角度值
radius02 = (p2.GetDistanceTo(p3) / 2) / Math.Sin(angle02 / 2);// 計算出半徑值
b = (1 / bulge02 - bulge02) / 2;
x = 0.5 * ((p2.X + p3.X) - b * (p2.Y - p3.Y));
y = 0.5 * ((p2.Y + p3.Y) + b * (p2.X - p3.X));
center02 = new Point2d(x, y);
// 比對半徑及圓心座標值
if (Math.Abs(radius - radius02) < tol && center.GetDistanceTo(center02) < tol)
{
pl.RemoveVertexAt(pl.NumberOfVertices - 1); // 移除第2点
angle += angle02;// 計算出角度值
bulge = Math.Tan(angle / 4);
pl.SetBulgeAt(0, bulge);
#if Debug
Env.Printl($"after 半徑{radius} ,{bulge}");
#endif
}
// 從起始+1點往后比對
p1 = pl.GetPoint2dAt(1);
p2 = pl.GetPoint2dAt(0);
p3 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
bulge = pl.GetBulgeAt(0);
angle = 4 * Math.Atan(bulge);// 計算出角度值
radius = (p1.GetDistanceTo(p2) / 2) / Math.Sin(angle / 2);// 計算出半徑值
b = (1 / bulge - bulge) / 2;
x = 0.5 * ((p1.X + p2.X) - b * (p1.Y - p2.Y));
y = 0.5 * ((p1.Y + p2.Y) + b * (p1.X - p2.X));
center = new Point2d(x, y);
bulge02 = pl.GetBulgeAt(pl.NumberOfVertices - 1);
angle02 = 4 * Math.Atan(bulge02);// 計算出角度值
radius02 = (p2.GetDistanceTo(p3) / 2) / Math.Sin(angle02 / 2);// 計算出半徑值
b = (1 / bulge02 - bulge02) / 2;
x = 0.5 * ((p2.X + p3.X) - b * (p2.Y - p3.Y));
y = 0.5 * ((p2.Y + p3.Y) + b * (p2.X - p3.X));
center02 = new Point2d(x, y);
// 比對半徑及圓心座標值
if (Math.Abs(radius - radius02) < tol && center.GetDistanceTo(center02) < tol)
{
pl.RemoveVertexAt(0); // 移除第2点
angle += angle02;// 計算出角度值
bulge = Math.Tan(angle / 4);
pl.SetBulgeAt(pl.NumberOfVertices - 1, bulge);
#if Debug
Env.Printl($"after 半徑{radius} ,{bulge}");
#endif
}
}
}
return num - pl.NumberOfVertices;
}
private static int SimplifyPolylineDemo(Polyline pl, double tol = 1e-6)
{
int num = pl.NumberOfVertices;
using (pl.ForWrite())
{
if (pl.NumberOfVertices < 3)
return 0;
// 優化多段線相鄰兩點共點時
for (var i = 0; i < pl.NumberOfVertices; i++)
{
var type = pl.GetSegmentType(i);
if (type == SegmentType.Coincident)
pl.RemoveVertexAt(i);
}
for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
{
var type = pl.GetSegmentType(i);
Env.Printl(type.GetType().Name + " " + type.ToString());
// 優化多段線相鄰兩點共點時
if (type == SegmentType.Coincident)
{
pl.RemoveVertexAt(i); // 移除第1点
continue;
}
// 跳过非线段部分
if (type != SegmentType.Line)
continue;
var type2 = pl.GetSegmentType(index: i - 1);
Env.Printl(type2.GetType().Name + " " + type2.ToString());
if (type2 != SegmentType.Line)
continue;
// 獲取相鄰三点
var p1 = pl.GetPoint2dAt(i);
var p2 = pl.GetPoint2dAt(i - 1);
var p3 = pl.GetPoint2dAt(i - 2);
// 優化第1點至 第2點、第3點 向量.Normal相等時
var bu = p1.GetArcBulge(p2, p3, tol);
if (bu != 0)
continue; //不共线跳过
pl.SetPointAt(index: i - 1, p3); // 第2点移到第3点位置
pl.RemoveVertexAt(index: i - 2); // 移除第3点
}
}
return num - pl.NumberOfVertices;
}
}
備註: 感謝 小叶 Moy 的Demo!!!!
本帖最后由 箭头_Row 于 2024-8-10 02:39 编辑
2024-8-10:BUG修復,直接判斷: i-1 點類型
/// <summary>
/// 優化多段線中重複點:清理共線的多余端點
/// 2024-8-10:BUG修復,直接判斷: i-1 點類型
/// </summary>
/// <param name="pl">多段線</param>
/// <param name="tol">容差值</param>
/// <returns>清理的端點數量</returns>
public static int SimplifyPolyline_Line(Polyline pl, double tol = 1e-6)
{
if (pl.NumberOfVertices < 3)
return 0;
int num = pl.NumberOfVertices;
using (pl.ForWrite())
{
var p1 = Point2d.Origin;
var p2 = Point2d.Origin;
var p3 = Point2d.Origin;
SegmentType type01;
SegmentType type02;
double bulge = default;
for (var i = pl.NumberOfVertices - 1; i >= 2; i--)
{
if (pl.NumberOfVertices < 3)
break;
// 2024-8-10:BUG修復,直接判斷: i-1 點類型
// 避免: i 點== Arc、i-1 點== Line、i -2 點== Line 時 直接跳過的情況。
//var type01 = pl.GetSegmentType(i);
//if (i != pl.NumberOfVertices - 1)
// if (type01 != SegmentType.Line)
// continue;
//var type02 = pl.GetSegmentType(i - 1);
//if (type02 != SegmentType.Line)
// continue;
// 優化多段線:頂點去重,非直線跳過
type01 = pl.GetSegmentType(i - 1);
if (type01 != SegmentType.Line)
continue;
type02 = pl.GetSegmentType(i - 2);
if (type02 != SegmentType.Line)
continue;
// 獲取相鄰三点
p1 = pl.GetPoint2dAt(i);
p2 = pl.GetPoint2dAt(i - 1);
p3 = pl.GetPoint2dAt(i - 2);
// 優化第1點至 第2點、第3點 向量.Normal相等時
bulge = p1.GetArcBulge(p2, p3, tol);
if (bulge == 0)
pl.RemoveVertexAt(index: i - 1); // 移除第2点
}
// 判斷首尾如果閉合的部份
if (pl.Closed && pl.NumberOfVertices > 2)
{
// 從結束-1點往前比對
type01 = pl.GetSegmentType(pl.NumberOfVertices - 2);
type02 = pl.GetSegmentType(pl.NumberOfVertices - 1);
if (type01 == SegmentType.Line && type02 == SegmentType.Line)
{
p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 2);
p2 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
p3 = pl.GetPoint2dAt(0);
bulge = p1.GetArcBulge(p2, p3, tol);
if (bulge == 0)
pl.RemoveVertexAt(index: pl.NumberOfVertices - 1); // 移除第2点
}
// 從結束點往前比對
type01 = pl.GetSegmentType(pl.NumberOfVertices - 1);
type02 = pl.GetSegmentType(0);
if (type01 == SegmentType.Line && type02 == SegmentType.Line)
{
p1 = pl.GetPoint2dAt(pl.NumberOfVertices - 1);
p2 = pl.GetPoint2dAt(0);
p3 = pl.GetPoint2dAt(1);
bulge = p1.GetArcBulge(p2, p3, tol);
if (bulge == 0)
pl.RemoveVertexAt(index: 0); // 移除第2点
}
}
}
return num - pl.NumberOfVertices;
}
備註:SimplifyPolyline_Arc 函數的修改邏輯同上。 本帖最后由 wang2006zhi 于 2024-12-10 18:01 编辑
/// <summary>
/// 简化多段线,仅考虑直线段
/// </summary>
/// <param name="pl"></param>
public static void SimpPolyline(this Polyline pl)
{
var i = 0;
var st = pl.Closed;
while (true)
{
var flag = st ? pl.NumberOfVertices : pl.NumberOfVertices - 1;
if (i == flag)
break;
var j = (i + 1) % pl.NumberOfVertices;
var k = (i + 2) % pl.NumberOfVertices;
var pt0 = pl.GetPoint3dAt(i);
var pt1 = pl.GetPoint3dAt(j);
var pt2 = pl.GetPoint3dAt(k);
//若点在线上,移除中间点
if (pt1.IsOnLine(pt0, pt2) == EnumOnline.OnLine)
pl.RemoveVertexAt(j);
else
i++;
}
}
页:
[1]