cad.net 并行前投影分块

投影分块

根据桶排序可以知道,分桶的边界之后可以并行.
所以这就给我们制作区间容器一个思路.
首先SortedList是有序KV结构,每次加入都会排序.
(微软提供的有序类型都是不允许重复key,所以自带二分法也没有找最左最右,需要自己写.)

我们把它的key作为区间min值,value作为区间max值+容器.
区间表示CAD图元包围盒影子,左边缘和右边缘投影到X轴.
那么同一个区间是不是有可能碰撞,而不同区间肯定就是不碰撞的.

重点是加入分裂和重组:
每次加入都判断重叠
0x01,如果重叠就把区间max更新,把容器合并.
0x02,如果原有key在新插入Min Max之间,
需要更新key,但是key不能改的,
就记录原有容器后,移除key,再加入新插入.

通过这种动态分块的方式,最后得到一份投影在x轴的区间表,
区间就是分块就可以并行执行各自任务.
要怎么才能把y轴也分块呢?就需要改用哈希网格了?

可以放到这里在线运行看看效果,手机也能跑耶.
https://www.json.cn/run/csharp
https://www.onlinegdb.com/online_csharp_compiler

代码

using System;
using System.Collections.Generic;
using System.Linq;

// 区间容器
public class RangeMap<T> where T : IEquatable<T> {
    // 内部数据容器
    private class Chunk {
        public int Max { get; set; }
        // 这里元素唯一的,即使不是,也会在碰撞时候消除.
        public List<T> Data { get; set; } = new List<T>();
        // 碰撞链
        public List<List<T>> Link = new List<List<T>>();

        // 分块之后内部只需要串行任务
        // 只有碰撞的才是链条
        public void CollisionsLink(Func<T, T, bool> isCollision) {
            // 排序后消除碰撞才更快.
            Data.Sort();
            var indices = new HashSet<int>();
            var currentSet = new HashSet<int>();
            for (int i = Data.Count - 1; i >= 0; i--) {
                if (indices.Contains(i)) continue;
                for (int j = i - 1; j >= 0; j--) {
                    if (indices.Contains(j)) continue;
                    if (!isCollision(Data[i], Data[j])) continue;
                    currentSet.Add(i);
                    currentSet.Add(j);
                    indices.Add(i);
                    indices.Add(j);
                }
                if (currentSet.Count == 0) continue; 
                Link.Add(currentSet.Select(item => Data[item]).ToList());
                currentSet.Clear();
            }
            // Data.Clear();
        }

        // 分块之后内部只需要串行任务
        // 全部都是链条,碰撞的会归位一条链
        public void CollisionsAll(Func<T, T, bool> isCollision) {
            // 排序后消除碰撞才更快.
            Data.Sort();
            var indices = new HashSet<int>();
            var currentSet = new HashSet<int>();
            for (int i = Data.Count - 1; i >= 0; i--) {
                if (indices.Contains(i)) continue;
                currentSet.Add(i);
                indices.Add(i);
                for (int j = i - 1; j >= 0; j--) {
                    if (indices.Contains(j)) continue;
                    if (!isCollision(Data[i], Data[j])) continue;
                    currentSet.Add(j);
                    indices.Add(j);
                }
                Link.Add(currentSet.Select(item => Data[item]).ToList());
                currentSet.Clear();
            }
            // Data.Clear();
        }
    }

    // 区间有序容器,就可以并行每个容器任务.
    private SortedList<int, Chunk> rangeList = new SortedList<int, Chunk>();

    // 因为是有序容器,使用二分法找到目标
    public void Add(int newStart, int newEnd, List<T> newValues) {
        int left = 0, right = rangeList.Count - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            // 原有记录
            var existingRange = rangeList.Values[mid];
            int existingStart = rangeList.Keys[mid];
            int existingEnd = existingRange.Max;
            // 插入前判断投影碰撞区域
            // 0x01,有碰撞加入原有容器中,并更新max
            if (existingStart <= newEnd && newStart <= existingEnd) {
                existingRange.Max = Math.Max(existingRange.Max, newEnd);
                foreach (var val in newValues) {
                    existingRange.Data.Add(val);
                }
                return;

            } else if (newStart < existingStart) {
                // 0x02,插入min比key小的情况,移除原有记录,重新添加
                var tempRange = rangeList[existingStart];
                rangeList.Remove(existingStart);
                // 比原有范围大,就能递归到0x03加入.
                Add(newStart, Math.Max(newEnd, tempRange.Max), newValues);
                // 原有范围插入,就能递归到0x01合并容器.
                Add(existingStart, tempRange.Max, tempRange.Data);
                return;

            } else if (newStart > existingEnd) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        // 0x03,没有重叠,直接添加
        rangeList.Add(newStart, new Chunk { Max = newEnd, Data = newValues });
    }

    public void Add(HashSet<T> set) {
        if (set.Count == 0) return;
        int minValue = int.MaxValue;
        int maxValue = int.MinValue;
        // CAD这里改为求包围盒
        foreach (var element in set) {
            // int currentValue = Convert.ToInt32(element); 居然是银行家算法
            int currentValue = (int)(double)(object)element;
            minValue = Math.Min(minValue, currentValue);
            maxValue = Math.Max(maxValue, currentValue);
        }
        Add(minValue, maxValue, set.ToList());
        // Console.WriteLine($"小{minValue}, 大{maxValue}");
    }

    // 并行
    public void CollisionsLink(Func<T, T, bool> isCollision) {
        rangeList.Values.AsParallel()
        .WithDegreeOfParallelism(System.Environment.ProcessorCount)
        .ForAll(v => v.CollisionsLink(isCollision));
    }
    public void CollisionsAll(Func<T, T, bool> isCollision) {
        rangeList.Values.AsParallel()
        .WithDegreeOfParallelism(System.Environment.ProcessorCount)
        .ForAll(v => v.CollisionsAll(isCollision));
    }

    public void PrintLink() {
        foreach (var item in rangeList) {
            Console.WriteLine($"起始值: {item.Key}, 结束值: {item.Value.Max}");
            foreach(var item2 in item.Value.Link)
                Console.WriteLine($"链条: {string.Join(", ", item2)}");
        }
    }

    // 用于输出当前管理的所有范围及对应值信息的方法
    public void Print() {
        foreach (var item in rangeList) {
            Console.WriteLine($"起始值: {item.Key}, 结束值: {item.Value.Max}, 值列表: {string.Join(", ", item.Value.Data)}");
        }
    }
}

class Program {
    static void Main() {
        // CAD这个泛型是ObjectId来的,我用double模拟包围盒而已.
        RangeMap<double> map = new RangeMap<double>();
        map.Add(new HashSet<double> { 1.1, 5.9 });
        map.Add(new HashSet<double> { 3.1, 8.9 });
        map.Add(new HashSet<double> { 10.1, 15.9 });
        map.Add(new HashSet<double> { 2.1, 6.9 });
        map.Add(new HashSet<double> { 16.1, 20.9 });
        map.Add(new HashSet<double> { 4.1, 12.9 });
        map.Add(new HashSet<double> { 7.1, 9.9 });
        map.Add(new HashSet<double> { 21.1, 22.9 });
        map.Print();
        Console.WriteLine("==========");
        map.CollisionsLink((a,b) => Math.Abs(a-b) < 1);
        map.PrintLink();
        Console.WriteLine("==========");
        map.CollisionsAll((a,b) => Math.Abs(a-b) < 1);
        map.PrintLink();
    }
}
posted @ 2024-12-25 04:13  惊惊  阅读(21)  评论(0编辑  收藏  举报