/* For circles only. */ private RoundaboutNode CheckAngularDistance(RoundaboutNode p1, RoundaboutNode p2) { RoundaboutNode prevNode = p1; double angDif = NormalizeAngle(prevNode.angle - p2.angle); if (p1 == p2) { angDif = 2 * Math.PI; } /* If the distance between two nodes is too great, we put in an intermediate node inbetween */ while (angDif > m_maxAngDistance) { recursionGuard(); double angle = NormalizeAngle(prevNode.angle - angDif / Math.Ceiling(angDif / m_maxAngDistance)); Vector3 vector = ellipse.VectorAtAbsoluteAngle(angle); //ushort newNodeId = NetAccess.CreateNode(centerNodeNetInfo, vector); WrappedNode newNodeW = new WrappedNode(); newNodeW.Position = new Vector3(vector.x, m_followTerrain ? NetUtil.TerrainHeight(vector) : vector.y, vector.z);; newNodeW.NetInfo = centerNodeNetInfo; actionGroupRoads.Actions.Add(newNodeW); RoundaboutNode newNode = new RoundaboutNode(newNodeW); newNode.angle = angle; ConnectNodes(newNode, prevNode); prevNode = newNode; angDif = NormalizeAngle(prevNode.angle - p2.angle); } return(prevNode); }
/* Ellipse only. Adds nodes where the ellipse intersects its axes to keep it in shape. User can turn this off. Kepp in mind that since we can only * approximate the ellipse (maybe I am wrong), every node on its circumference changes its actual shape. */ private void InsertIntermediateNodes() { List <RoundaboutNode> newNodes = new List <RoundaboutNode>(); /* Originally I planned to pair every node on the ellipse to make it symmetric... Didn't work, I gave up on making it work. */ /*foreach(VectorNodeStruct intersection in intersections.ToArray()) * { * double angle = getAbsoluteAngle(intersection.vector); * VectorNodeStruct newNode = new VectorNodeStruct(ellipse.VectorAtAbsoluteAngle(getConjugateAngle(angle))); * intersections.Add( newNode ); * EllipseTool.Instance.debugDrawPositions.Add(newNode.vector); * }*/ var vec = ellipse.VectorAtAngle(0); newNodes.Add(new RoundaboutNode(new Vector3(vec.x, m_followTerrain ? NetUtil.TerrainHeight(vec) : vec.y, vec.z))); vec = ellipse.VectorAtAngle(Math.PI / 2); newNodes.Add(new RoundaboutNode(new Vector3(vec.x, m_followTerrain ? NetUtil.TerrainHeight(vec) : vec.y, vec.z))); vec = ellipse.VectorAtAngle(Math.PI); newNodes.Add(new RoundaboutNode(new Vector3(vec.x, m_followTerrain ? NetUtil.TerrainHeight(vec) : vec.y, vec.z))); vec = ellipse.VectorAtAngle(3 * Math.PI / 2); newNodes.Add(new RoundaboutNode(new Vector3(vec.x, m_followTerrain ? NetUtil.TerrainHeight(vec) : vec.y, vec.z))); /*EllipseTool.Instance.debugDrawPositions.Add(new VectorNodeStruct(ellipse.VectorAtAngle(0)).vector); * EllipseTool.Instance.debugDrawPositions.Add(new VectorNodeStruct(ellipse.VectorAtAngle(Math.PI / 2)).vector); * EllipseTool.Instance.debugDrawPositions.Add(new VectorNodeStruct(ellipse.VectorAtAngle(Math.PI)).vector); * EllipseTool.Instance.debugDrawPositions.Add(new VectorNodeStruct(ellipse.VectorAtAngle(3 * Math.PI / 2)).vector);*/ /* Disgusting nested FOR cycle. We don't want to cluster the nodes too close to each other. */ foreach (RoundaboutNode vectorNode in newNodes.ToArray()) { foreach (RoundaboutNode intersection in intersections) { if (VectorDistance(vectorNode.wrappedNode.Position, intersection.wrappedNode.Position) < DISTANCE_MIN) { newNodes.Remove(vectorNode); //Debug.Log("Node too close, removing from list"); } } } intersections.AddRange(newNodes); }
private void SnappingAlgorithmNew() { //Debug //EllipseTool.Instance.debugDraw = segmentBeziers; List <Bezier2> segmentBeziers = makeBeziers(traveller.OuterSegments); List <Bezier2> ellipseBeziers = traveller.Ellipse.Beziers; List <ushort> processedSegments = new List <ushort>(); /* We find all intersections between roads and ellipse beziers */ for (int i = 0; i < segmentBeziers.Count; i++) { for (int j = 0; j < ellipseBeziers.Count; j++) { if (ellipseBeziers[j].Intersect(segmentBeziers[i], out float t1, out float t2, ITERATIONS)) { if (processedSegments.Contains(traveller.OuterSegments[i])) { continue; } else { processedSegments.Add(traveller.OuterSegments[i]); } //Debug.Log("Segment " + i.ToString() + " intersects ellipse bezier " + j.ToString()); Vector3 intersection = new Vector3(ellipseBeziers[j].Position(t1).x, CenterNode.m_position.y, ellipseBeziers[j].Position(t1).y); segmentBeziers[i].Divide(out Bezier2 segementBezier1, out Bezier2 segementBezier2, t2); Bezier2 outerBezier; Vector2 outerNodePos = new Vector2(NetUtil.Node(traveller.OuterNodes[i]).m_position.x, NetUtil.Node(traveller.OuterNodes[i]).m_position.z); bool invert = false; // outerBezier - the bezier outside the ellipse (not the one inside) if (segementBezier1.Position(0f) == outerNodePos || segementBezier1.Position(1f) == outerNodePos) { //Debug.Log("first is outer"); outerBezier = segementBezier1.Invert(); invert = true; } else if (segementBezier2.Position(0f) == outerNodePos || segementBezier2.Position(1f) == outerNodePos) { //Debug.Log("second is probably outer"); outerBezier = segementBezier2; invert = false; } else { throw new Exception("Error - Failed to determine segment geometry."); } //debug: //EllipseTool.Instance.debugDraw.Add(outerBezier); /* We create a node at the intersection. */ WrappedNode newNode = new WrappedNode(); newNode.Position = new Vector3(intersection.x, m_followTerrain ? NetUtil.TerrainHeight(intersection) : intersection.y, intersection.z); newNode.NetInfo = CenterNode.Info; RoundaboutNode raNode = new RoundaboutNode(newNode); raNode.Create(ActionGroupRoads); Intersections.Add(raNode); WrappedNode outerNode = networkDictionary.RegisterNode(traveller.OuterNodes[i]); WrappedSegment outerSegment = networkDictionary.RegisterSegment(traveller.OuterSegments[i]); BezierToSegment(outerBezier, outerSegment, newNode, outerNode, invert); } } } }
/* Old algorithm. Originally intended only for circles. From older documentation: */ /* "For now, the intersection isn't created at the exact point where the segment crosses the circle, but rather on the intersection of * the circle and straight line, which goes from origin and ends at outer node of that segment. That could be unfortunately very * inaccurate, as the first note outside the circle could be quite far away". */ private void SnappingAlgorithmOld() { float centerX = CenterNode.m_position.x; float centerY = CenterNode.m_position.y; float centerZ = CenterNode.m_position.z; for (int i = 0; i < traveller.OuterNodes.Count; i++) { NetNode curNode = NetUtil.Node(traveller.OuterNodes[i]); Vector3 circleIntersection = new Vector3(); float directionX = (curNode.m_position.x - centerX) / VectorDistance(CenterNode.m_position, curNode.m_position); float directionZ = (curNode.m_position.z - centerZ) / VectorDistance(CenterNode.m_position, curNode.m_position); float radius = (float)ellipse.RadiusAtAbsoluteAngle(Math.Abs(Ellipse.VectorsAngle(curNode.m_position - ellipse.Center))); if (radius > 10000) { throw new Exception("Algortithm error"); } circleIntersection.x = (directionX * radius + centerX); circleIntersection.y = centerY; circleIntersection.z = (directionZ * radius + centerZ); WrappedNode newNode; WrappedSegment newSegment; newNode = new WrappedNode(); newNode.NetInfo = CenterNode.Info; newNode.Position = circleIntersection; newNode.Position = new Vector3(circleIntersection.x, m_followTerrain ? NetUtil.TerrainHeight(circleIntersection) : circleIntersection.y, circleIntersection.z); RoundaboutNode raNode = new RoundaboutNode(newNode); raNode.Create(ActionGroupRoads); Intersections.Add(raNode); //EllipseTool.Instance.debugDrawPositions.Add(Intersections.Last().vector); NetSegment curSegment = NetUtil.Segment(traveller.OuterSegments[i]); /* For now ignoring anything regarding Y coordinate */ //float directionY2 = (GetNode(newNodeId).m_position.y - curNode.m_position.z) / NodeDistance(GetNode(newNodeId), curNode); float directionY2 = 0f; Vector3 startDirection = new Vector3(); startDirection.x = (directionX /** NodeDistance( GetNode( newNodeId ), curNode ) / 2*/); startDirection.y = directionY2; startDirection.z = (directionZ /** NodeDistance(GetNode(newNodeId), curNode) / 2*/); Vector3 endDirection = new Vector3(); endDirection.x = -startDirection.x; endDirection.y = -startDirection.y; endDirection.z = -startDirection.z; bool invert; //Debug.Log(string.Format("same node: {0}, invert: {1}", curSegment.m_startNode == traveller.OuterNodes[i], curSegment.m_flags.IsFlagSet(NetSegment.Flags.Invert))); if (curSegment.m_startNode == traveller.OuterNodes[i] ^ curSegment.m_flags.IsFlagSet(NetSegment.Flags.Invert)) { invert = true; } else { invert = false; } newSegment = new WrappedSegment(); newSegment.StartNode = newNode; newSegment.EndNode = networkDictionary.RegisterNode(traveller.OuterNodes[i]); newSegment.StartDirection = startDirection; newSegment.EndDirection = endDirection; newSegment.NetInfo = curSegment.Info; newSegment.Invert = invert; ActionGroupRoads.Actions.Add(newSegment); ActionGroupTMPE.Actions.Add(new EnteringBlockedJunctionAllowedAction(newSegment, true, true)); ActionGroupTMPE.Actions.Add(new YieldSignAction(newSegment, true)); //Debug.Log(string.Format("Segment and node created... ")); } }
public FinalConnector(NetInfo centerNodeNetInfo, EdgeIntersections2 edgeIntersections, Ellipse ellipse, bool insertControllingVertices, bool followTerrain, bool reverseDirection) { intersections = edgeIntersections?.Intersections ?? new List <RoundaboutNode>(); actionGroupTMPE = edgeIntersections?.ActionGroupTMPE ?? new ActionGroup("Set up TMPE"); actionGroupRoads = edgeIntersections?.ActionGroupRoads ?? new ActionGroup("Build roundabout"); wrappersDictionary = edgeIntersections?.networkDictionary ?? new WrappersDictionary(); this.ellipse = ellipse; pleasenoinfiniterecursion = 0; this.centerNodeNetInfo = centerNodeNetInfo; leftHandTraffic = Singleton <SimulationManager> .instance.m_metaData.m_invertTraffic == SimulationMetaData.MetaBool.True; m_followTerrain = followTerrain; m_reverseDirection = reverseDirection; // We ensure that the segments are not too long. For circles only (with ellipses it would be more difficult) m_maxAngDistance = Math.Min(Math.PI * 25 / ellipse.RadiusMain, Math.PI / 2 + 0.1d); bool isCircle = ellipse.IsCircle(); if (!isCircle && insertControllingVertices) { /* See doc in the method below */ InsertIntermediateNodes(); } /* If the list of edge nodes is empty, we add one default intersection. */ if (isCircle && intersections.Count == 0) { Vector3 defaultIntersection = new Vector3(ellipse.RadiusMain, 0, 0) + ellipse.Center; defaultIntersection = new Vector3(defaultIntersection.x, m_followTerrain ? NetUtil.TerrainHeight(defaultIntersection) : defaultIntersection.y, defaultIntersection.z); //ushort newNodeId = NetAccess.CreateNode(centerNodeNetInfo, defaultIntersection); WrappedNode newNodeW = new WrappedNode(); newNodeW.Position = defaultIntersection; newNodeW.NetInfo = centerNodeNetInfo; RoundaboutNode raNode = new RoundaboutNode(newNodeW); raNode.Create(actionGroupRoads); intersections.Add(raNode); } int count = intersections.Count; foreach (RoundaboutNode item in intersections) { item.angle = Ellipse.VectorsAngle(item.wrappedNode.Position - ellipse.Center); } /* We sort the nodes according to their angles */ intersections.Sort(); /* Goes over all the nodes and conntets each of them to the angulary closest neighbour. (In a given direction) */ for (int i = 0; i < count; i++) { RoundaboutNode prevNode = intersections[i]; if (isCircle) { prevNode = CheckAngularDistance(intersections[i], intersections[(i + 1) % count]); } ConnectNodes(intersections[(i + 1) % count], prevNode); } // Charge player actionGroupRoads.ItemClass = centerNodeNetInfo.m_class; }