Пример #1
0
        // 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());
        }
Пример #2
0
 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);
     }
 }
Пример #3
0
        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);
            }
        }
Пример #4
0
        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));
        }
Пример #5
0
        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
                }
            }
Пример #6
0
        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);
        }