红色孔和白色孔各为一个图层,如果白色和红色孔出现在同一位置上,则优先删除白色孔。
由于有上万个孔,优先排除piont in curves电池(需要运算14min)
我自己的方法如下
想讨论看看有没有计算量更小的方法来实现,部分孔数据已经内置在gh文件里了,有兴趣的可以试一下,请注意保持孔始终在各自图层内。
0308.gh (571.2 KB)
想讨论看看有没有计算量更小的方法来实现,部分孔数据已经内置在gh文件里了,有兴趣的可以试一下,请注意保持孔始终在各自图层内。
0308.gh (571.2 KB)
获得圆心以后用点的closest point功能
能用低维度数据解决的,不要刻意使用高维度,能用数字判断的就数字,不行就点
还不行就曲线,再不行曲面,还是不行brep,实体等等
你这明明是一个点的问题,一上来如此迅猛地用上了brep。。。那就会非常慢了
感谢大鹿老师,我重新用closest point做了一下,可能是我对几个工具不够熟悉,目前可以筛选出重叠位置的孔,但是没有办法用Cull Index反回去删掉,原因应该是closest point之后的数据结构与一开始不一样了
什么叫同一位置?是指圆心和大小都重合,还是仅仅指圆心重合?
如果是圆心相同就要删减,那就直接取交集,然后再从需要删除的集合里删去交集。如果仅仅需要删除同圆心和同半径的,则将圆心和半径合并,形成新的集合,再按前面的方法求得结果,再分拆就可以了。
感谢回复,是的,求交集可以不用考虑数据的顺序直接求出来,是个好办法 ![]()
这个不需要考虑两圆心的距离精度问题,只要是包含关系,内部的圆都会删掉。
因为工作原因经常处理这种重孔的问题,检查圆心是否重叠经常需要遍历所有点,所以会导致运算时间很长,用C#解决了这个问题,在这里留个档,如果有碰到类似问题的小伙伴可以做个参考。程序都已经调试过了,已经优化到计算15万个点花费2秒。
using System;
using System.Collections.Generic;
using Rhino.Geometry;
using Rhino;
// 并查集(Union-Find)数据结构实现
public class UnionFind
{
private int[] parent;
private int[] rank;
public UnionFind(int n)
{
parent = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++)
parent[i] = i;
}
public int Find(int x)
{
if (parent[x] != x)
parent[x] = Find(parent[x]);
return parent[x];
}
public void Union(int x, int y)
{
int rootX = Find(x);
int rootY = Find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY])
{
parent[rootX] = rootY;
}
else if (rank[rootX] > rank[rootY])
{
parent[rootY] = rootX;
}
else
{
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
// 存储原始圆的信息
public class CircleInfo
{
public Point3d Center { get; set; }
public double Radius { get; set; }
public CircleInfo(Point3d center, double radius)
{
Center = center;
Radius = radius;
}
}
// 主程序
{
try
{
// 检查输入数据
if (curves == null)
{
A = new List<Circle>();
B = 0;
C = 0;
return;
}
// 开始计时
var watch = System.Diagnostics.Stopwatch.StartNew();
// 第一步:从曲线中提取圆心和半径信息
var circleInfos = new List<CircleInfo>();
foreach (var curve in curves)
{
if (curve == null) continue;
// 只处理封闭曲线
if (curve.IsClosed)
{
// 尝试获取圆的中心点和半径
Arc arc;
if (curve.TryGetArc(out arc))
{
circleInfos.Add(new CircleInfo(arc.Center, arc.Radius));
}
else
{
// 对于其他封闭曲线,使用包围盒中心作为近似圆心,使用平均尺寸作为半径
var bbox = curve.GetBoundingBox(true);
var center = bbox.Center;
var radius = (bbox.Diagonal.X + bbox.Diagonal.Y) / 4.0; // 近似半径
circleInfos.Add(new CircleInfo(center, radius));
}
}
}
// 如果没有圆或容差为0,直接返回
if (circleInfos.Count == 0 || tolerance <= 0)
{
A = new List<Circle>();
B = 0;
C = 0;
return;
}
// 提取圆心点
var points = circleInfos.ConvertAll(ci => ci.Center);
// 第二步:使用空间哈希加速查找接近的点
var spatialDict = new Dictionary<Tuple<int, int>, List<int>>();
double cellSize = tolerance * 2; // 网格大小设置为容差的2倍
// 将点分配到空间网格中
for (int i = 0; i < points.Count; i++)
{
var point = points[i];
// 计算点所在的网格单元
var key = Tuple.Create((int)(point.X / cellSize), (int)(point.Y / cellSize));
// 如果网格单元不存在,创建新列表
if (!spatialDict.ContainsKey(key))
spatialDict[key] = new List<int>();
// 将点索引添加到网格单元
spatialDict[key].Add(i);
}
// 第三步:使用并查集合并接近的点
var uf = new UnionFind(points.Count);
// 遍历所有网格单元
foreach (var kvp in spatialDict)
{
int cellX = kvp.Key.Item1;
int cellY = kvp.Key.Item2;
// 检查当前单元格和相邻的8个单元格
for (int dx = -1; dx <= 1; dx++)
{
for (int dy = -1; dy <= 1; dy++)
{
// 计算相邻单元格的键
var neighborKey = Tuple.Create(cellX + dx, cellY + dy);
// 如果相邻单元格存在
if (spatialDict.TryGetValue(neighborKey, out var neighborIndices))
{
// 检查当前单元格和相邻单元格中的点是否接近
foreach (var i in kvp.Value)
{
foreach (var j in neighborIndices)
{
// 避免与自身比较,且检查距离是否小于容差
if (i != j && points[i].DistanceTo(points[j]) < tolerance)
{
uf.Union(i, j); // 合并接近的点
}
}
}
}
}
}
}
// 第四步:分组结果并计算平均位置和平均半径
var groups = new Dictionary<int, List<int>>();
for (int i = 0; i < points.Count; i++)
{
int root = uf.Find(i);
if (!groups.ContainsKey(root))
groups[root] = new List<int>();
groups[root].Add(i);
}
// 计算每组的新圆心(平均位置)和平均半径
var mergedCircles = new List<Circle>();
foreach (var group in groups.Values)
{
double avgX = 0, avgY = 0, avgRadius = 0;
foreach (var index in group)
{
avgX += circleInfos[index].Center.X;
avgY += circleInfos[index].Center.Y;
avgRadius += circleInfos[index].Radius;
}
avgX /= group.Count;
avgY /= group.Count;
avgRadius /= group.Count;
// 创建新的圆
var center = new Point3d(avgX, avgY, 0);
mergedCircles.Add(new Circle(center, avgRadius));
}
// 停止计时
watch.Stop();
// 第五步:设置输出
A = mergedCircles; // 合并后的圆
B = circleInfos.Count - mergedCircles.Count; // 去除的重复圆数量
C = watch.Elapsed.TotalSeconds; // 处理时间(秒)
}
catch (Exception)
{
// 静默处理异常
// 确保输出不为 null
A = new List<Circle>();
B = 0;
C = 0;
}
}