/// <summary> /// Scans the vertices on the polygon until either an advance or retard step can be made. /// Used when iterating over a section of the polygon not visible from z. /// </summary> /// <param name="v"></param> /// <param name="s"></param> /// <param name="iprev"></param> /// <param name="windowEnd"></param> /// <param name="ccw"></param> /// <returns> </returns> public static NextCall Scan(ref VsRep v, ref Stack <VertDispl> s, ref int i, ref VertDispl windowEnd, ref bool ccw) { while (i < v.n) { i++; if (ccw && // CounterClockWise MathUtil.GreaterEps(v.Get(i + 1).alpha, s.Peek().alpha) && MathUtil.GEQEps(s.Peek().alpha, v.Get(i).alpha)) { VertDispl intersec = IntersectWithWindow(v.Get(i), v.Get(i + 1), s.Peek(), windowEnd); if (intersec != null && !(windowEnd != null && MathUtil.EqualsEps(intersec.p.Cartesian, windowEnd.p.Cartesian))) { s.Push(intersec); return(NextCall.ADVANCE); } } else if (!ccw && // ClockWise MathUtil.LEQEps(v.Get(i + 1).alpha, s.Peek().alpha) && s.Peek().alpha < v.Get(i).alpha) { if (IntersectWithWindow(v.Get(i), v.Get(i + 1), s.Peek(), windowEnd) != null) { return(NextCall.RETARD); } } } return(NextCall.STOP); }
/// <summary> /// Pushes a new vertex on the stack and calls the appropriated function /// (advance, retard, scan) depending on the next vertex on the polygon. /// </summary> /// <param name="v"></param> /// <param name="s"></param> /// <param name="i"></param> /// <returns></returns> public static NextCall Advance(ref VsRep v, ref Stack <VertDispl> s, ref int i, ref VertDispl w, ref bool ccw) { var n = v.n - 1; Debug.Assert(i + 1 <= n); if (MathUtil.LEQEps(v.Get(i + 1).alpha, MathUtil.PI2)) { i++; s.Push(v.Get(i)); // TODO check order of returned list if (i == n) { return(NextCall.STOP); } if (MathUtil.LessEps(v.Get(i + 1).alpha, v.Get(i).alpha) && MathUtil.Orient2D(v.Get(i - 1).p.Cartesian, v.Get(i).p.Cartesian, v.Get(i + 1).p.Cartesian) < 0) { // -1 is RightTurn w = null; ccw = true; return(NextCall.SCAN); } else if (MathUtil.LessEps(v.Get(i + 1).alpha, v.Get(i).alpha) && MathUtil.Orient2D(v.Get(i - 1).p.Cartesian, v.Get(i).p.Cartesian, v.Get(i + 1).p.Cartesian) > 0) { // 1 is LeftTurn return(NextCall.RETARD); } else { return(NextCall.ADVANCE); } } else { var v0 = v.Get(0); if (MathUtil.LEQEps(s.Peek().alpha, MathUtil.PI2)) { var isect = (new LineSegment(v.Get(i).p.Cartesian, v.Get(i + 1).p.Cartesian)).Intersect(v0.p.Ray); Debug.Assert(isect != null); var st = DisplacementInBetween(new PolarPoint2D(isect.Value), v.Get(i), v.Get(i + 1)); s.Push(st); } w = v0; ccw = false; return(NextCall.SCAN); } }
/// <summary> /// Transforms the vertices on the stack to the final visibility polygon. /// Reverses the pre-process steps, namely the translation and rotation. /// Also removes one of endpoints if duplicate /// </summary> /// <param name="pre_s"></param> /// <param name="vs"></param> /// <param name="z"></param> /// <param name="initAngle"></param> /// <returns> Final visibility polygon. </returns> private static Polygon2D Postprocess(List <VertDispl> pre_s, VsRep vs, Vector2 z, double initAngle) { // reverse order of stack to establish CCW order of final visibility polygon pre_s.Reverse(); if (vs.zIsVertex) { pre_s.Add(new VertDispl(new PolarPoint2D(Vector2.zero), 0)); } // convert VertDispl to PolarPoint2D var rotatedPol = pre_s.Select(v => v.p).ToList(); // rotates points back to original position before the rotation in preprocess() foreach (var curr in rotatedPol) { curr.RotateClockWise(-initAngle); } // convert PolarPoint2D to Vector2 // shifts points back to their position before the shift in preprocess() var shiftedPol = rotatedPol.Select(v => v.Cartesian + z).ToList(); Debug.Assert(shiftedPol.Count > 0); // check if first and last vertex are the same if (shiftedPol.Count > 1 && MathUtil.EqualsEps(shiftedPol.FirstOrDefault(), shiftedPol.LastOrDefault(), MathUtil.EPS * 10)) { shiftedPol.RemoveAt(shiftedPol.Count - 1); } // check if first point is colinear with last and second if (shiftedPol.Count > 2 && Line.Colinear(shiftedPol.LastOrDefault(), shiftedPol.FirstOrDefault(), shiftedPol.ElementAt(1))) { shiftedPol.RemoveAt(0); } // make polygon clockwise // to conform to clockwise standard in project shiftedPol.Reverse(); return(new Polygon2D(shiftedPol)); }
/// <summary> /// Pops all vertices from the stack that have become invisible after the addition /// of a new vertex. /// Calls appropriate method (advance, scan, retard) after being done based on next vertex. /// </summary> /// <param name="v"></param> /// <param name="sOld"></param> /// <param name="iprev"></param> /// <returns></returns> public static NextCall Retard(ref VsRep v, ref Stack <VertDispl> s, ref int i, ref VertDispl w, ref bool ccw) { // LocateSj will pop vertices from the stack // until appropriated s_j is found // see paper var sjNext = LocateSj(v.Get(i), v.Get(i + 1), s); if (s.Count == 0) { return(NextCall.STOP); } var sj = s.Peek(); if (sj.alpha < v.Get(i + 1).alpha) { i++; var vi = v.Get(i); var p = (new LineSegment(sj.p.Cartesian, sjNext.p.Cartesian)).Intersect(vi.p.Ray); // fallback method, return point closest to intersection with segment line if (p == null) { var line = new Line(vi.p.Ray.origin, vi.p.Ray.origin + vi.p.Ray.direction); p = (new LineSegment(sj.p.Cartesian, sjNext.p.Cartesian)).Line.Intersect(line); p = Vector2.Distance(p.Value, sj.p.Cartesian) < Vector2.Distance(p.Value, sjNext.p.Cartesian) ? sj.p.Cartesian : sjNext.p.Cartesian; } var st1 = DisplacementInBetween(new PolarPoint2D(p.Value), sj, sjNext); if (st1 != null) { s.Push(st1); } s.Push(vi); // paper does i == v.n if (i == v.n - 1) { // TODO order of returned list correct? (check stack to list conversion) return(NextCall.STOP); } else if (MathUtil.GEQEps(v.Get(i + 1).alpha, vi.alpha) && MathUtil.Orient2D(v.Get(i - 1).p.Cartesian, vi.p.Cartesian, v.Get(i + 1).p.Cartesian) <= 0) { // -1 is RighTurn return(NextCall.ADVANCE); } else if (MathUtil.GreaterEps(v.Get(i + 1).alpha, vi.alpha) && MathUtil.Orient2D(v.Get(i - 1).p.Cartesian, vi.p.Cartesian, v.Get(i + 1).p.Cartesian) > 0) { // 1 is LeftTurn s.Pop(); w = vi; ccw = false; return(NextCall.SCAN); } else { s.Pop(); return(NextCall.RETARD); } } else { if (MathUtil.EqualsEps(v.Get(i + 1).alpha, sj.alpha) && MathUtil.GreaterEps(v.Get(i + 2).alpha, v.Get(i + 1).alpha) && MathUtil.Orient2D(v.Get(i).p.Cartesian, v.Get(i + 1).p.Cartesian, v.Get(i + 2).p.Cartesian) <= 0) { // -1 is RightTurn s.Push(v.Get(i + 1)); return(NextCall.ADVANCE); } else { w = IntersectWithWindow(v.Get(i), v.Get(i + 1), sj, sjNext); ccw = true; if (w == null) { var seg = new LineSegment(v.Get(i).p, v.Get(i + 1).p); var res = seg.ClosestPoint(sj.p.Cartesian); w = DisplacementInBetween(new PolarPoint2D(res), v.Get(i), v.Get(i + 1)); } return(NextCall.SCAN); } } }