public static VisibilityPolygon[] ComputeWaypointVisibilityPolygons(this PolyNode landNode) { if (landNode.visibilityGraphNodeData.AggregateContourWaypointVisibilityPolygons != null) { return(landNode.visibilityGraphNodeData.AggregateContourWaypointVisibilityPolygons); } var waypoints = FindAggregateContourCrossoverWaypoints(landNode); var barriers = FindContourAndChildHoleBarriers(landNode); var visibilityPolygons = waypoints.Map(waypoint => { var visibilityPolygon = VisibilityPolygon.Create(waypoint.ToDoubleVector2(), barriers); // for (var edgeDescriptionIndex = 0; edgeDescriptionIndex < landNode.visibilityGraphNodeData.SectorSnapshot.SourceSegmentEdgeDescriptions.Count; edgeDescriptionIndex++) { // var erodedCrossoverSegmentBox = landNode.visibilityGraphNodeData.SectorSnapshotGeometryContext.ErodedBoundaryCrossoverSegments[edgeDescriptionIndex]; // if (!erodedCrossoverSegmentBox.HasValue) { // continue; // } // if (waypoint == erodedCrossoverSegmentBox.Value.First || waypoint == erodedCrossoverSegmentBox.Value.Second) { // continue; // } // visibilityPolygon.ClearBeyond(landNode.visibilityGraphNodeData.SectorSnapshot.SourceSegmentEdgeDescriptions[edgeDescriptionIndex].SourceSegment); // } return(visibilityPolygon); }); return(landNode.visibilityGraphNodeData.AggregateContourWaypointVisibilityPolygons = visibilityPolygons); }
public static void DrawCrossSectorVisibilityPolygon( this IDebugCanvas canvas, TerrainOverlayNetworkNode terrainNode, IntVector2 visibilityPolygonOrigin, FillStyle fillStyle = null ) { fillStyle = fillStyle ?? kDefaultFillStyle; canvas.Transform = terrainNode.SectorNodeDescription.WorldTransform; // canvas.DrawPoint(visibilityPolygonOrigin, StrokeStyle.RedThick25Solid); var visibilityPolygon = VisibilityPolygon.Create(visibilityPolygonOrigin.ToDoubleVector2(), terrainNode.LandPolyNode.FindContourAndChildHoleBarriers()); var visibleCrossoverSegmentsByNeighbor = FindVisibleCrossoverSegmentsByNeighborAndClearLocalAt(canvas, terrainNode, visibilityPolygon, visibilityPolygonOrigin); canvas.DrawVisibilityPolygon(visibilityPolygon, fillStyle: fillStyle ?? kDefaultFillStyle, angleBoundaryStrokeStyle: StrokeStyle.None, visibleWallStrokeStyle: StrokeStyle.None); var visibilityPolygonOriginWorld = Vector3.Transform(new Vector3(visibilityPolygonOrigin.ToDotNetVector(), 0), terrainNode.SectorNodeDescription.WorldTransform); foreach (var(neighbor, inboundCrossoverSegments) in visibleCrossoverSegmentsByNeighbor) { var neighborPolygonOrigin = Vector3.Transform(visibilityPolygonOriginWorld, neighbor.SectorNodeDescription.WorldTransformInv); Z(canvas, new IntVector2((int)neighborPolygonOrigin.X, (int)neighborPolygonOrigin.Y), neighbor, inboundCrossoverSegments, new HashSet <TerrainOverlayNetworkNode> { terrainNode }, fillStyle); } }
public static VisibilityPolygon GetIntersectionArea(Polygon firstPolygon, Polygon secondPolygon) { // Prepare the second (main) polygon to be merged List <CyclicalList <IntPoint> > subj = new List <CyclicalList <IntPoint> >(); if (secondPolygon.GetVerticesCount() > 0) { subj.Add(new CyclicalList <IntPoint>()); for (int i = 0; i < secondPolygon.GetVerticesCount(); i++) { subj[subj.Count - 1].Add(new IntPoint(secondPolygon.GetPoint(i).x, secondPolygon.GetPoint(i).y)); } } // The current seen area so it will be merged with the previous area List <CyclicalList <IntPoint> > clip = new List <CyclicalList <IntPoint> >(); if (firstPolygon.GetVerticesCount() > 0) { clip.Add(new CyclicalList <IntPoint>()); for (int i = 0; i < firstPolygon.GetVerticesCount(); i++) { clip[clip.Count - 1].Add(new IntPoint(firstPolygon.GetPoint(i).x, firstPolygon.GetPoint(i).y)); } } // Merge the two polygons List <CyclicalList <IntPoint> > solution = new List <CyclicalList <IntPoint> >(); Clipper c = new Clipper(); c.AddPaths(clip, PolyType.ptClip, true); c.AddPaths(subj, PolyType.ptSubject, true); c.Execute(ClipType.ctIntersection, solution, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd); // Result merged polygon VisibilityPolygon mergedPolygon = new VisibilityPolygon(); // Fill the result if (solution.Count > 0) { foreach (CyclicalList <IntPoint> polygon in solution) { for (int i = 0; i < polygon.Count; i++) { mergedPolygon.AddPoint(new Vector2(polygon[i].X, polygon[i].Y)); } } } return(mergedPolygon); }
private static void Z(this IDebugCanvas canvas, IntVector2 visibilityPolygonOrigin, TerrainOverlayNetworkNode terrainNode, IReadOnlyCollection <IntLineSegment2> inboundCrossoverSegments, HashSet <TerrainOverlayNetworkNode> visited, FillStyle fillStyle) { canvas.Transform = terrainNode.SectorNodeDescription.WorldTransform; // canvas.DrawPoint(visibilityPolygonOrigin, StrokeStyle.RedThick25Solid); var visibilityPolygon = new VisibilityPolygon( visibilityPolygonOrigin.ToDoubleVector2(), new[] { new VisibilityPolygon.IntervalRange { Id = VisibilityPolygon.RANGE_ID_INFINITESIMALLY_NEAR, ThetaStart = 0, ThetaEnd = VisibilityPolygon.TwoPi }, }); foreach (var inboundCrossoverSegment in inboundCrossoverSegments) { visibilityPolygon.ClearBefore(inboundCrossoverSegment); } // Console.WriteLine("===="); foreach (var seg in terrainNode.LandPolyNode.FindContourAndChildHoleBarriers()) { if (GeometryOperations.Clockness(visibilityPolygon.Origin, seg.First.ToDoubleVector2(), seg.Second.ToDoubleVector2()) == Clockness.CounterClockwise) { continue; } visibilityPolygon.Insert(seg); // Console.WriteLine(seg); } // Console.WriteLine("===="); var visibleCrossoverSegmentsByNeighbor = FindVisibleCrossoverSegmentsByNeighborAndClearLocalAt(canvas, terrainNode, visibilityPolygon, visibilityPolygonOrigin, visited); canvas.DrawVisibilityPolygon(visibilityPolygon, fillStyle: fillStyle ?? kDefaultFillStyle, angleBoundaryStrokeStyle: StrokeStyle.None, visibleWallStrokeStyle: StrokeStyle.None); var visibilityPolygonOriginWorld = Vector3.Transform(new Vector3(visibilityPolygonOrigin.ToDotNetVector(), 0), terrainNode.SectorNodeDescription.WorldTransform); foreach (var(neighbor, nextInboundCrossoverSegments) in visibleCrossoverSegmentsByNeighbor) { var neighborPolygonOrigin = Vector3.Transform(visibilityPolygonOriginWorld, neighbor.SectorNodeDescription.WorldTransformInv); //visibilityPolygonOrigin Z(canvas, new IntVector2((int)neighborPolygonOrigin.X, (int)neighborPolygonOrigin.Y), neighbor, nextInboundCrossoverSegments, visited.Concat(new[] { terrainNode }).ToHashSet(), fillStyle); } }
private static void Benchmark() { var inputs = Util.Generate(5, RandomInput); while (true) { var sw = new Stopwatch(); sw.Start(); const int niters = 1000; for (var i = 0; i < niters; i++) { var(p, segments) = inputs[i % inputs.Length]; VisibilityPolygon.Create(p, segments); } Console.WriteLine(sw.Elapsed.TotalMilliseconds / niters); } }
private static void RenderVisualizationFrame(DoubleVector2 p, IntLineSegment2[] segments, int eventLimit = -1) { var canvas = host.CreateAndAddCanvas(frameCounter++); var vp = VisibilityPolygon.Create(p, segments, eventLimit); vp = new VisibilityPolygon(p); foreach (var seg in segments) { vp.Insert(seg); } // vp.Insert(new IntLineSegment2(p.LossyToIntVector2() + new IntVector2(50, -20), p.LossyToIntVector2() + new IntVector2(50, 20))); canvas.BatchDraw(() => { canvas.DrawVisibilityPolygon(vp, 0, new FillStyle(Color.FromArgb(120, Color.Cyan))); foreach (var s in segments) { canvas.DrawLine(s.First, s.Second, StrokeStyle.BlackHairLineSolid); } canvas.DrawPoint(p, StrokeStyle.RedThick5Solid); }); }
public static void DrawVisibilityPolygon(this IDebugCanvas debugCanvas, VisibilityPolygon avss, double z = 0.0, FillStyle fillStyle = null, StrokeStyle angleBoundaryStrokeStyle = null, StrokeStyle visibleWallStrokeStyle = null) { fillStyle = fillStyle ?? DefaultFillStyle; var oxy = avss.Origin; foreach (var range in avss.Get().Where(range => range.Id != VisibilityPolygon.RANGE_ID_INFINITELY_FAR && range.Id != VisibilityPolygon.RANGE_ID_INFINITESIMALLY_NEAR)) { var rstart = DoubleVector2.FromRadiusAngle(100, range.ThetaStart); var rend = DoubleVector2.FromRadiusAngle(100, range.ThetaEnd); var s = range.Segment; var s1 = s.First.ToDoubleVector2(); var s2 = s.Second.ToDoubleVector2(); DoubleVector2 visibleStart, visibleEnd; if (!GeometryOperations.TryFindLineLineIntersection(oxy, oxy + rstart, s1, s2, out visibleStart) || !GeometryOperations.TryFindLineLineIntersection(oxy, oxy + rend, s1, s2, out visibleEnd)) { continue; } debugCanvas.FillTriangle(oxy, visibleStart, visibleEnd, fillStyle); debugCanvas.DrawLine( new DoubleVector3(oxy.X, oxy.Y, z), new DoubleVector3(visibleStart.X, visibleStart.Y, z), angleBoundaryStrokeStyle ?? DefaultAngleBoundaryStrokeStyle); debugCanvas.DrawLine( new DoubleVector3(oxy.X, oxy.Y, z), new DoubleVector3(visibleEnd.X, visibleEnd.Y, z), angleBoundaryStrokeStyle ?? DefaultAngleBoundaryStrokeStyle); debugCanvas.DrawLine( new DoubleVector3(visibleStart.X, visibleStart.Y, z), new DoubleVector3(visibleEnd.X, visibleEnd.Y, z), visibleWallStrokeStyle ?? DefaultVisibleWallStrokeStyle); } }
private static MultiValueDictionary <TerrainOverlayNetworkNode, IntLineSegment2> FindVisibleCrossoverSegmentsByNeighborAndClearLocalAt( IDebugCanvas canvas, TerrainOverlayNetworkNode terrainNode, VisibilityPolygon visibilityPolygon, IntVector2 visibilityPolygonOrigin, HashSet <TerrainOverlayNetworkNode> visited = null) { var visibleCrossoverSegmentsByNeighbor = MultiValueDictionary <TerrainOverlayNetworkNode, IntLineSegment2> .Create(() => new HashSet <IntLineSegment2>()); foreach (var outboundEdgeGroup in terrainNode.OutboundEdgeGroups) { var otherTerrainNode = outboundEdgeGroup.Key; if (visited?.Contains(otherTerrainNode) ?? false) { continue; } foreach (var outboundEdge in outboundEdgeGroup.Value) { var ranges = visibilityPolygon.Get(); (IntLineSegment2, bool) FlipMaybeSorta(IntLineSegment2 x) => GeometryOperations.Clockness(visibilityPolygonOrigin, x.First, x.Second) == Clockness.CounterClockwise ? (new IntLineSegment2(x.Second, x.First), true) : (x, false); var(localCrossoverSegment, lcsFlipped) = FlipMaybeSorta(outboundEdge.EdgeJob.EdgeDescription.SourceSegment); var(remoteCrossoverSegment, rcsFlipped) = FlipMaybeSorta(outboundEdge.EdgeJob.EdgeDescription.DestinationSegment); // todo: clamp visibleStartT, visibleEndT to account for agent radius eroding crossover segmetmentnt var rangeIndexIntervals = visibilityPolygon.RangeStab(localCrossoverSegment); var locallyClearedSegments = new List <IntLineSegment2>(); foreach (var(startIndexInclusive, endIndexInclusive) in rangeIndexIntervals) { for (var i = startIndexInclusive; i <= endIndexInclusive; i++) { if (ranges[i].Id == VisibilityPolygon.RANGE_ID_INFINITELY_FAR || ranges[i].Id == VisibilityPolygon.RANGE_ID_INFINITESIMALLY_NEAR) { continue; } var seg = ranges[i].Segment; var rstart = DoubleVector2.FromRadiusAngle(100, ranges[i].ThetaStart) * 100; var rend = DoubleVector2.FromRadiusAngle(100, ranges[i].ThetaEnd) * 100; double visibleStartT, visibleEndT; if (!GeometryOperations.TryFindNonoverlappingLineLineIntersectionT(localCrossoverSegment.First.ToDoubleVector2(), localCrossoverSegment.Second.ToDoubleVector2(), visibilityPolygonOrigin.ToDoubleVector2(), visibilityPolygonOrigin.ToDoubleVector2() + rstart, out visibleStartT) || !GeometryOperations.TryFindNonoverlappingLineLineIntersectionT(localCrossoverSegment.First.ToDoubleVector2(), localCrossoverSegment.Second.ToDoubleVector2(), visibilityPolygonOrigin.ToDoubleVector2(), visibilityPolygonOrigin.ToDoubleVector2() + rend, out visibleEndT)) { // wtf? Console.WriteLine("???"); continue; } // Todo: I don't actually understand why visibleEndT > 1 is a thing? // t values are for parameterization of crossover line segment, so must be within [0, 1] if ((visibleStartT < 0 && visibleEndT < 0) || (visibleStartT > 1 && visibleEndT > 1)) { continue; } visibleStartT = Math.Min(1.0, Math.Max(0.0, visibleStartT)); visibleEndT = Math.Min(1.0, Math.Max(0.0, visibleEndT)); if (visibilityPolygon.SegmentComparer.Compare(localCrossoverSegment, seg) < 0) { var localVisibleStart = localCrossoverSegment.PointAt(visibleStartT).LossyToIntVector2(); var localVisibleEnd = localCrossoverSegment.PointAt(visibleEndT).LossyToIntVector2(); var remoteVisibleStart = remoteCrossoverSegment.PointAt(lcsFlipped == rcsFlipped ? visibleStartT : 1.0 - visibleStartT).LossyToIntVector2(); var remoteVisibleEnd = remoteCrossoverSegment.PointAt(lcsFlipped == rcsFlipped ? visibleEndT : 1.0 - visibleEndT).LossyToIntVector2(); if (localVisibleStart == localVisibleEnd) { continue; } if (remoteVisibleStart == remoteVisibleEnd) { continue; } var locallyClearedSegment = new IntLineSegment2(localVisibleStart, localVisibleEnd); locallyClearedSegments.Add(locallyClearedSegment); visibleCrossoverSegmentsByNeighbor.Add(otherTerrainNode, new IntLineSegment2(remoteVisibleStart, remoteVisibleEnd)); } } } foreach (var locallyClearedSegment in locallyClearedSegments) { visibilityPolygon.ClearBefore(locallyClearedSegment); } } } return(visibleCrossoverSegmentsByNeighbor); }