// Note: Holes in polytree are in reverse clockness than lands. private static IntVector2[] FindContourWaypoints(this PolyNode node) { if (node.visibilityGraphNodeData.ContourWaypoints != null) { return(node.visibilityGraphNodeData.ContourWaypoints); } var results = new List <IntVector2>(); var contour = node.Contour; var contourIsOpen = contour.First() != contour.Last(); var pointCount = contourIsOpen ? node.Contour.Count : node.Contour.Count - 1; for (var i = 0; i < pointCount; i++) { var a = contour[i]; var b = contour[(i + 1) % pointCount]; var c = contour[(i + 2) % pointCount]; var clockness = GeometryOperations.Clockness(a.X, a.Y, b.X, b.Y, c.X, c.Y); if (clockness == Clockness.CounterClockwise) { results.Add(b); } } return(node.visibilityGraphNodeData.ContourWaypoints = results.ToArray()); }
private static void RenderRandomVisualizationFrames() { for (var i = 0; i < 100; i++) { var(p, segments) = RandomInput(); p = new DoubleVector2(bounds.Width / 2, bounds.Height / 2); segments = segments.Where(s => GeometryOperations.Clockness(p.X, p.Y, s.X1, s.Y1, s.X2, s.Y2) == Clockness.Clockwise).ToArray(); RenderVisualizationFrame(p, segments); } }
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); } }
public bool ContainsOrIntersects(ref IntLineSegment2 segment) { var tl = GeometryOperations.Clockness(segment.X1, segment.Y1, segment.X2, segment.Y2, Left, Top); var tr = GeometryOperations.Clockness(segment.X1, segment.Y1, segment.X2, segment.Y2, Right, Top); var bl = GeometryOperations.Clockness(segment.X1, segment.Y1, segment.X2, segment.Y2, Left, Bottom); var br = GeometryOperations.Clockness(segment.X1, segment.Y1, segment.X2, segment.Y2, Right, Bottom); // If all on same side (and assuming assume not all collinear), then not intersecting! if (tl == tr && tr == bl && bl == br && br != Clockness.Neither) { return(false); } // Some point (or cross-sectional segment!) of rect intersects with line formed by segment return((segment.X1 >= Left || segment.X2 >= Left) && (segment.X1 <= Right || segment.X2 <= Right) && (segment.Y1 >= Top || segment.Y2 >= Top) && (segment.Y1 <= Bottom || segment.Y2 <= Bottom)); }
private void TriangulateHelper(PolyNode node, List <TriangulationIsland> islands) { DebugPrint("TriangulateRoot out"); var cps = new Polygon(ConvertToTriangulationPoints(node.Contour)); foreach (var hole in node.Childs) { cps.AddHole(new Polygon(ConvertToTriangulationPoints(hole.Contour))); } P2T.Triangulate(cps); var triangles = new Triangle3[cps.Triangles.Count]; var p2tTriangleToIndex = new Dictionary <Poly2Tri.Triangulation.Delaunay.DelaunayTriangle, int>(); for (int i = 0; i < cps.Triangles.Count; i++) { triangles[i].Index = i; p2tTriangleToIndex[cps.Triangles[i]] = i; } for (var i = 0; i < cps.Triangles.Count; i++) { var p2tTriangle = cps.Triangles[i]; ref Triangle3 t = ref triangles[i]; t.Points = new Array3 <DoubleVector2>( p2tTriangle.Points[0].ToOpenMobaPointD(), p2tTriangle.Points[1].ToOpenMobaPointD(), p2tTriangle.Points[2].ToOpenMobaPointD() ); // Console.WriteLine(string.Join(", ", p2tTriangle.Points.Select(p => p.Z))); t.Centroid = (t.Points[0] + t.Points[1] + t.Points[2]) / 3.0; t.IntPaddedBounds2D = CreatePaddedIntAxisAlignedBoundingBoxXY2D(ref triangles[i].Points); for (int j = 0; j < 3; j++) { if (p2tTriangle.Neighbors[j] != null && p2tTriangle.Neighbors[j].IsInterior) { triangles[i].NeighborOppositePointIndices[j] = p2tTriangleToIndex[p2tTriangle.Neighbors[j]]; } else { triangles[i].NeighborOppositePointIndices[j] = Triangle3.NO_NEIGHBOR_INDEX; } #if DEBUG var p0 = triangles[i].Points[0]; var p1 = triangles[i].Points[1]; var p2 = triangles[i].Points[2]; var centroid = triangles[i].Centroid; var cp0 = p0 - centroid; var cp1 = p1 - centroid; var cp2 = p2 - centroid; var cl01 = GeometryOperations.Clockness(cp0, cp1); var cl12 = GeometryOperations.Clockness(cp1, cp2); var cl20 = GeometryOperations.Clockness(cp2, cp0); var cond = cl01 == cl12 && cl12 == cl20 && cl20 == Clockness.CounterClockwise; if (!cond) { throw new ArgumentException("P2T Triangle Clockness wasn't expected"); } #endif } }
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); }