0
\$\begingroup\$

I need help with a complex task I'm trying to creating roads using RoadArchitect: https://github.com/FritzsHero/RoadArchitect/tree/RewrittenAPI I'm trying to detect all roads from a texture. I have created a model class to store every detected road using a little bit of recursion:

    [Serializable]
    public class RoadNode : IPathNode
    {
        //[JsonConverter(typeof(RoadNodeConverter))]
        public ConcurrentBag<int> Connections { get; set; } // TODO: IPathNode<T> + RoadNode : IPathNode<RoadNode> + Connections (ConcurrentBag<RoadNode>), but can't be serialized due to StackOverflow and OutOfMemory exceptions

        public Point Position { get; set; }
        public bool Invalid { get; set; }
        public int Thickness { get; set; }
        public ConcurrentBag<int> ParentNodes { get; set; }

        public RoadNode()
        {
            //Connections = new List<RoadNode>();
        }

        public RoadNode(Point position, int thickness)
        //: this()
        {
            Position = position;
            Thickness = thickness;
        }

        public RoadNode(Point position, bool invalid, int thickness)
        //: this()
        {
            Position = position;
            Invalid = invalid;
            Thickness = thickness;
        }

        public RoadNode(int x, int y, int thickness)
            : this(new Point(x, y), thickness)
        {
        }

        public void SetThickness(int thickness)
        {
            // TODO: Call this when needed and thickness == -1
            Thickness = thickness;
        }

        public int GetKey()
        {
            return F.P(Position.x, Position.y, mapWidth, mapHeight);
        }
    }
    public interface IPathNode
    {
        ConcurrentBag<int> Connections { get; }
        Point Position { get; }
        bool Invalid { get; }
    }

At every loaded chunk (I'm using SebLeague tutorial to create the chunks), I get all the road points for this chunk. Then, I try to iterare them. But I have problems interpreting the results.

I have part of the task done:

        public static IEnumerable<Road> CreateRoad(this IEnumerable<Point> points, StringBuilder builder)
        {
            if (points.IsNullOrEmpty())
            {
                builder?.AppendLine($"\tskipped...");
                yield break; // This chunk doesn't contain any road. Exiting.
            }

            //var dict = points.Select(p => new {Index = p.GetKey(), Point = p}).ToDictionary(x => x.Index, x => x.Point);
            //var builder = new StringBuilder();
            var roads = GetRoads(points, builder);
            foreach (var list in roads)
            {
                if (list.IsNullOrEmpty()) continue;
                //var first = road.First();
                //var backIndex = ((Point)CityGenerator.SConv.GetRealPositionOnMap(first)).GetKey();

                var road = CreateIndependantRoad(list);
                builder?.AppendLine($"\t... finished road ({road.name}) with {list.Count} nodes.");
                yield return road;
            }
            //Debug.Log(builder?.ToString());
        }

        private static IEnumerable<List<Vector3>> GetRoads(IEnumerable<Point> points, StringBuilder builder)
        {
            var model = RoadGenerator.RoadModel;

            var queue = new Queue<Point>(points);
            int i = 0;

            builder?.AppendLine($"\tcount: {queue.Count}");

            var dictionary = new Dictionary<int, List<Vector3>>();

            while (queue.Count > 0)
            {
                var list = new List<Vector3>();

                var pt = queue.Dequeue();
                var itemIndex = pt.GetKey();
                dictionary.Add(itemIndex, list);

                var node = model.SimplifiedRoadNodes[itemIndex];

                builder?.AppendLine($"\troad iteration: {i}");

                //var conn = node.Connections;
                var nodes = GetRoadNodes(node, ptVal =>
                {
                    if (ptVal.HasValue) queue = new Queue<Point>(queue.Remove(ptVal.Value));
                    return queue;
                },
                    parentNodeIndex => { return dictionary[parentNodeIndex]; },
                    builder);

                foreach (var point in nodes)
                    list.Add(CityGenerator.SConv.GetRealPositionOnMap((Vector2)point).GetHeightForPoint());

                yield return list;
                ++i;
            }
        }

        private static IEnumerable<Point> GetRoadNodes(RoadNode node, Func<Point?, Queue<Point>> queueFunc, Func<int, List<Vector3>> parentFunc, StringBuilder builder, int level = -1)
        {
            if (queueFunc == null) throw new ArgumentNullException(nameof(queueFunc));
            if (parentFunc == null) throw new ArgumentNullException(nameof(parentFunc));

            var conn = node.Connections;
            if (conn.IsNullOrEmpty())
            {
                yield return node.Position;
                yield break;
            }
            if (queueFunc(null).Count == 0) yield break;

            ++level;
            builder?.AppendLine($"{new string('\t', 2)}level: {level} -> {queueFunc(null).Count} items");

            //if (conn.Count == 1)
            //{
            //    var firstNode = conn.First().GetNode();
            //    ////var firstPoint = conn.First().GetPoint();

            //    var list = parentFunc(firstNode.ParentNodes.First()); // TODO: parent nodes should be one...
            //    list.Add(CityGenerator.SConv.GetRealPositionOnMap((Vector2)conn.First().GetPoint()).GetHeightForPoint());
            //}
            //else
            {
                foreach (var item in conn)
                {
                    var pt = item.GetPoint();
                    if (!queueFunc(null).Contains(pt)) yield break;
                    yield return pt;
                    if (queueFunc(pt).Count == 0) yield break;

                    var subnode = pt.GetKey().GetNode();
                    var pts = GetRoadNodes(subnode, queueFunc, parentFunc, builder, level);
                    foreach (var point in pts)
                        yield return point;
                }
            }
        }

You can see here the StringBuilder results: https://pastebin.com/1tW4V4BC

But as you can see I have problems with roads. Some roads only have one node, other roads only have 2 nodes...

I'm thinking that the current implementation I did isn't right at all, because instead of grouping all nodes into one major road I'm splitting them into road chunks without sense.

By this reason I created this topic, in order to clarify myself because I'm not sure about what I'm doing and which way should I take.

\$\endgroup\$
  • \$\begingroup\$ If your current code isn't doing the right thing, then showing us just that code won't help us figure out what the right thing should be - all we can see is code that does the wrong thing. So be sure to include in your question a clear step-by-step description of what this code should be doing. What's the input, what's each step of processing, what's the desired output. Then we can help you find where it deviates from that plan. \$\endgroup\$ – DMGregory Oct 14 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.