C# 路径查询 在CAD上实现效果

226 阅读4分钟

起因是之前做某个功能用到了一种思想,先将图上的数据整理出来,之间的路径关系转换成字符串去查找路径。(当时没有封装,而且那功能想要的结果也不止简单的从A到D查询路径,是要梳理出图上所有路径,根据规则排除穿插路径
先放一下本篇算法的最后效果(其实大家应该都想象的出来效果甚至算法,网上现有的应该挺多的路径查询最终结果.gif

本人由于有上面提到的那个功能思路,在写这个路径查询的时候没有在网上查资料,也没考虑到什么高效,纯靠楞想一点点敲出来。

一开始想算法时候没有明确的代码思路,这个时候可以先手动模拟算法在excel上走走看流程,找找有哪些逻辑规律。 查询路径算法示意.png 整个算法大体流程是:
1、读取图纸数据并按照特定规则转换数据
2、查询路径
3、将路径转换成结果
算法不变的是2、3点,第1点要根据图纸数据实际情况具体分析
第1步提到的特定规则,是以线为主体,记录两端关联的对象,如果线连着线,为了统一规则需要虚拟出一个对象。下图截取了一段,虚拟的点是"50D,50E,511"。
路径示意1.png
第2步的查询路径:根据第1步的结果拿到了数据结构 线-关联1|关联2,查询出起始所在数据项集合A,遍历集合A里另一个关联,递归直到查询不到关联或者遇到终点,查询不到的话回退到上个分支点(通过数据状态回退)。(查询到的数据是路径里经过的节点集合)
第3步将路径转换成结果:得到路径节点集合后 到第1步的总数据里 拿到两个节点中的线。(按照第1步的规则,读取出的数据两个节点只会有一条线)

根据前面的工作,开始准备创建类(我一开始没考虑那么细节,都是后期迭代出来的)
查询路径类图.png

public class RuleMain
{
    /// 起始
    public Entity startEntity;
    /// 结束
    public Entity endEntity;
    /// 输出结果
    public LinePathMode linePathMode;
    public RuleMain(Entity startEntity, Entity endEntity)
    {
        this.startEntity = startEntity;
        this.endEntity = endEntity;
        linePathMode = new LinePathMode();
    }
    //准备数据会根据情况变化 摘出去
    #region 准备数据
    #endregion

    #region 处理数据(查询路径
    /// <summary>
    /// 查询路径
    /// </summary>
    /// <param name="currentHandle">当前起始端</param>
    /// <param name="endHandle">最终目标</param>
    /// <param name="linePathMode">要处理的数据对象</param>
    /// <param name="currentPath">查询过程中的记录</param>
    /// <param name="resultPaths">最终路径结果</param>
    /// <param name="branchLog">分支节点记录</param>
    /// <param name="branchAllLog">查询过程中节点记录</param>
    /// <param name="backState">是否回退</param>
    private void DealLinePathMode(string currentHandle, string endHandle, LinePathMode linePathMode,
        List<string> currentPath, ref List<List<string>> resultPaths, List<Tuple<string, int, int>> branchLog, List<string> branchAllLog, ref bool backState)
    {
        linePathMode.SetElementState(currentHandle, ElementState.Activate);
        if (branchAllLog.Contains(currentHandle)) branchAllLog.Remove(currentHandle);
        //查找另一端可能的元素
        IEnumerable<string> otherHandles = linePathMode.GetOtherElementHandles(currentHandle);
        foreach (string otherHandle in otherHandles)
        {
            if (!backState)
            {
                linePathMode.SetElementState(otherHandle, ElementState.Activate);
                if (otherHandles.Count() > 1 && !branchAllLog.Contains(currentHandle))
                {//有分支,记录下截止当前已经查询过的记录
                    branchAllLog.Add(currentHandle);
                    branchLog.Add(new Tuple<string, int, int>(currentHandle, currentPath.Count, otherHandles.Count()));
                }
                currentPath.Add(otherHandle);
                if (otherHandle == endHandle)
                {//找到结束元素
                    linePathMode.SetElementState(endHandle, ElementState.Activate);
                    string[] temp = new string[currentPath.Count];
                    currentPath.CopyTo(temp);
                    resultPaths.Add(temp.ToList());
                    //回到上一个
                    if (branchLog.Count > 0) backState = true;
                }
                else
                {
                    DealLinePathMode(otherHandle, endHandle, linePathMode, currentPath, ref resultPaths, branchLog, branchAllLog, ref backState);
                }
            }
            if (backState)
            {//回退
                linePathMode.SetElementState(currentPath.LastOrDefault(), ElementState.None);
                currentPath.RemoveAt(currentPath.Count - 1);
                if (currentHandle == branchLog.LastOrDefault().Item1)
                {
                    backState = false;
                    Tuple<string, int, int> temp = branchLog.LastOrDefault();
                    if (temp.Item3 - 2 > 0)
                    {
                        branchLog.RemoveAt(branchLog.Count - 1);
                        branchLog.Add(new Tuple<string, int, int>(temp.Item1, temp.Item2, temp.Item3 - 1));
                    }
                    else
                    {
                        branchLog.RemoveAt(branchLog.Count - 1);
                    }
                }
            }
        }
        if (otherHandles.Count() == 0)
        {//找不到路径, 回到上一个分支点的状态
            if (branchLog.Count > 0) backState = true;
        }
    }
    #endregion

    #region 梳理数据,返回最终结果
    private static List<Tuple<List<Entity>, List<Entity>>> GetFindPathResults(Entity startEntity, LinePathMode linePathMode, params List<string>[] handlePaths)
    {
        List<Tuple<List<Entity>, List<Entity>>> findPathResults = new List<Tuple<List<Entity>, List<Entity>>>();
        foreach (var item in handlePaths)
        {
            findPathResults.Add(GetFindPathResult(startEntity, linePathMode, item));
        }
        return findPathResults;
    }
    /// <summary>
    /// 将查询到的路径转变成结果数据
    /// </summary>
    /// <param name="startEntity"></param>
    /// <param name="linePathMode"></param>
    /// <param name="handlePath"></param>
    /// <returns></returns>
    private static Tuple<List<Entity>, List<Entity>> GetFindPathResult(Entity startEntity, LinePathMode linePathMode, List<string> handlePath)
    {
        List<Entity> elements = new List<Entity>();
        List<Entity> lines = new List<Entity>();
        if (handlePath == null) return new Tuple<List<Entity>, List<Entity>>(elements, lines);
        Entity currentEntity = startEntity;
        for (int i = 0; i < handlePath.Count - 1; i++)
        {
            //得到两个元素之间关联的线
            ItemData itemData = linePathMode.GetItemDataByTwoHandle(handlePath[i], handlePath[i + 1]);
            elements.Add(linePathMode.GetElement(handlePath[i]).EntityElement);
            elements.Add(linePathMode.GetElement(handlePath[i + 1]).EntityElement);
            lines.Add(itemData.LineEntity);
        }
        return new Tuple<List<Entity>, List<Entity>>(elements, lines);
    }
    #endregion

    /// <summary>
    /// 流程
    /// </summary>
    /// <returns></returns>
    public List<Tuple<List<Entity>, List<Entity>>> GetResult(BaseRuleData baseRuleData)
    {
        linePathMode = baseRuleData.GetResult();
        string currentHandle = startEntity.Handle.ToString();
        string endHandle = endEntity.Handle.ToString();
        List<string> currentPath = new List<string>();
        List<List<string>> resultPaths = new List<List<string>>();
        List<Tuple<string, int, int>> branchLog = new List<Tuple<string, int, int>>();
        //记录本次线路经过的元素
        currentPath.Add(currentHandle);
        bool backState = false;
        List<string> branchAllLog = new List<string>();
        DealLinePathMode(currentHandle, endHandle, linePathMode, currentPath, ref resultPaths, branchLog, branchAllLog, ref backState);
        List<string> handlePathMin = resultPaths.OrderBy(o => o.Count).FirstOrDefault();//路径节点最少的
        List<string> handlePathMax = resultPaths.OrderBy(o => o.Count).LastOrDefault();//路径节点最长的
        //将查询到的路径数据整理成想要的结果
        return GetFindPathResults(startEntity, linePathMode, handlePathMin, handlePathMax);
    }
}

//这是调用的方法
RuleDataPath ruleDataPath = new RuleDataPath(draw, startEntity);//准备数据
RuleMain ruleMain = new RuleMain(startEntity, endEntity);
return ruleMain.GetResult(ruleDataPath);

扩展
从类图的字段可以看出我没有记录线的长度,结果只考虑了节点数量,如果考虑路径长短只需要在声明ItemData的时候记录下当前线段的长度即可,不需要做别的额外处理,因为这个算法把所有可能的路径都查询到了。只要记录了相关数据,想要得出最长、最短、路过节点最多、最少,最后无非是对每条路径的筛选。同理如果是两个节点之间有多条线相连也只要在ItemData里记录下就好。