Aetheria/Assets/Plugins/MSAGL/Routing/Rectilinear/ScanSegmentVectorItem.cs
Reinier Vens dbc522a224 Weapos Rejiggered
Modified weapon scripts to eliminate specialized projectile behavior - allows charged weapons to have projectiles and modify weapon stats when charged
Added machine gun weapon & VFX
Moved third party code to Plugins folder to improve compile times
2021-01-31 03:52:32 +00:00

180 lines
No EOL
8.1 KiB
C#

//
// ScanSegmentVectorItem.cs
// MSAGL base class to create the visibility graph consisting of nlogn ScanSegment intersections for Rectilinear Edge Routing.
//
// Copyright Microsoft Corporation.
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Msagl.Core.Geometry;
namespace Microsoft.Msagl.Routing.Rectilinear {
internal partial class SparseVisibilityGraphGenerator {
/// <summary>
/// This forms one slot in the scan segment vector.
/// </summary>
internal class ScanSegmentVectorItem {
/// <summary>
/// The head of the linked list.
/// </summary>
internal ScanSegment FirstSegment;
/// <summary>
/// The current segment of the linked list, used when appending or intersecting.
/// </summary>
internal ScanSegment CurrentSegment;
/// <summary>
/// Perpendicular coordinates that are not in a ScanSegment, due to either not having the ScanSegments created
/// yet or because it will be faster to do a single pass after accumulating them (e.g. for GroupBoundaryCrossings).
/// </summary>
private List<double> pendingPerpCoords;
internal void AddPendingPerpendicularCoord(double coord) {
if (pendingPerpCoords == null) {
pendingPerpCoords = new List<double>();
}
pendingPerpCoords.Add(coord);
}
/// <summary>
/// Restores state between intersection passes.
/// </summary>
internal void ResetForIntersections() {
Debug.Assert(null != this.FirstSegment, "Empty ScanSegmentVectorItem");
this.CurrentSegment = this.FirstSegment;
}
/// <summary>
/// Indicates whether ScanSegments in this item are horizontally or vertically oriented.
/// </summary>
internal bool IsHorizontal {
get { return !this.FirstSegment.IsVertical; }
}
/// <summary>
/// Returns the constant coordinate of the ScanSegments in this item, i.e. the coordinate
/// that intersects the perpendicular axis.
/// </summary>
internal double Coord { get; private set; }
/// <summary>
/// Ctor, taking the parallel (constant) coordinate.
/// </summary>
/// <param name="coord">the parallel (constant) coordinate</param>
internal ScanSegmentVectorItem(double coord) {
this.Coord = coord;
}
/// <summary>
/// Move along the linked list until we hit the ScanSegment that contains the point.
/// </summary>
internal bool TraverseToSegmentContainingPoint(Point point) {
// This is not a simple Next() because scan segments are extended "through" obstacles
// (intermixing overlapped and non-overlapped) and thus a ScanSegment's Start and End
// may not be in the vertexPoints collection and the ScanSegment must be skipped.
if (this.CurrentSegment.ContainsPoint(point)) {
return true;
}
var pointCoord = this.IsHorizontal ? point.Y : point.X;
if (!PointComparer.Equal(this.Coord, pointCoord)) {
Debug.Assert(PointComparer.Compare(this.Coord, pointCoord) == -1, "point is before current Coord");
while (this.MoveNext()) {
// Skip to the end of the linked list if this point is not on the same coordinate.
}
return false;
}
for (;;) {
// In the event of mismatched rounding on horizontal versus vertical intersections
// with a sloped obstacle side, we may have a point that is just before or just
// after the current segment. If the point is in some space that doesn't have a
// scansegment, and if we are "close enough" to one end or the other of a scansegment,
// then grow the scansegment enough to include the new point.
if ((null == this.CurrentSegment.NextSegment) ||
PointComparer.GetDirections(this.CurrentSegment.End, point)
== PointComparer.GetDirections(point, this.CurrentSegment.NextSegment.Start)) {
if (ApproximateComparer.CloseIntersections(this.CurrentSegment.End, point)) {
this.CurrentSegment.Update(this.CurrentSegment.Start, point);
return true;
}
}
if (!this.MoveNext()) {
return false;
}
if (this.CurrentSegment.ContainsPoint(point)) {
return true;
}
// This is likely the reverse of the above; the point rounding mismatched to just before
// rather than just after the current segment.
if (PointComparer.IsPureLower(point, this.CurrentSegment.Start)) {
Debug.Assert(ApproximateComparer.CloseIntersections(this.CurrentSegment.Start, point), "Skipped over the point in the ScanSegment linked list");
this.CurrentSegment.Update(point, this.CurrentSegment.End);
return true;
}
}
}
internal bool MoveNext() {
this.CurrentSegment = this.CurrentSegment.NextSegment;
return this.HasCurrent;
}
internal bool HasCurrent {
get { return (null != this.CurrentSegment); }
}
/// <summary>
/// Returns true if the point is the end of the current segment and there is an adjoining NextSegment.
/// </summary>
internal bool PointIsCurrentEndAndNextStart(Point point) {
return (point == this.CurrentSegment.End) && (null != this.CurrentSegment.NextSegment) && (point == this.CurrentSegment.NextSegment.Start);
}
/// <summary>
/// Set Current to the ScanSegment containing the perpendicular coordinate, then add that coordinate to its
/// sparse-vector coordinate list.
/// </summary>
/// <param name="perpCoord"></param>
internal void AddPerpendicularCoord(double perpCoord) {
var point = this.IsHorizontal ? new Point(perpCoord, this.Coord) : new Point(this.Coord, perpCoord);
this.TraverseToSegmentContainingPoint(point);
this.CurrentSegment.AddSparseVertexCoord(perpCoord);
}
/// <summary>
/// </summary>
/// <returns></returns>
public override string ToString() {
if (null == this.FirstSegment) {
return "-0- " + this.Coord;
}
return (this.IsHorizontal ? "(H) Y == " : "(V) X == ") + this.Coord;
}
internal void AppendScanSegment(ScanSegment segment) {
if (null == this.FirstSegment) {
this.FirstSegment = segment;
} else {
// Note: segment.Start may != Current.End due to skipping internal ScanSegment creation for non-overlapped obstacles.
this.CurrentSegment.NextSegment = segment;
}
this.CurrentSegment = segment;
}
internal void AddPendingPerpendicularCoordsToScanSegments() {
if (this.pendingPerpCoords != null) {
this.ResetForIntersections();
foreach (var point in this.pendingPerpCoords) {
this.AddPerpendicularCoord(point);
}
}
}
}
}
}