public void AddPolygonLine(int xi1, int yi1, int xi2, int yi2, double xv1, double yv1, double xv2, double yv2) { if (ActivePolygon == null) { ActivePolygon = new PolygonModel(); } double xp1 = xi1 * Config.XIncrement + Config.XStart; double yp1 = yi1 * Config.YIncrement + Config.YStart; double xp2 = xi2 * Config.XIncrement + Config.XStart; double yp2 = yi2 * Config.YIncrement + Config.YStart; var line = new Line(); line.Stroke = new SolidColorBrush(Colors.Red); line.StrokeThickness = 3; line.Fill = new SolidColorBrush(Colors.Red); line.X1 = xp1; line.Y1 = yp1; line.X2 = xp2; line.Y2 = yp2; ActivePolygon.Lines.Add(new LineModel { StartPoint = new Vector { X = xv1, Y = yv1, Alternates = new CanvasPoint { DotIndexLeft = xi1, DotIndexTop = yi1 } }, EndPoint = new Vector { X = xv2, Y = yv2, Alternates = new CanvasPoint { DotIndexLeft = xi2, DotIndexTop = yi2 } } }); var cc = new CanvasComponent(); cc.AddUiElement(line); line.Tag = ActivePolygon.Lines[ActivePolygon.Lines.Count - 1]; polygonLinesInProgress.Add(cc); GetInputLayer().AddComponent(cc); // if we're at least completing a triangle... if (ActivePolygon.Lines.Count > 2) { // if endpoint of current line matches start point of first line, we're connecting // back to start of polygon if (GeomMath.AlmostEqual(new Vector { X = xv2, Y = yv2 }, ActivePolygon.Lines[0].StartPoint)) { CompletePolygon(); } } }
private bool turn_left(Vector p1, Vector p2, Vector p3) { var v1 = GeomMath.Subtract(p2, p1); var v2 = GeomMath.Subtract(p3, p2); return((v1.X * v2.Y - v2.X * v1.Y) > 0); }
public int GrahamSort(Vector v1, Vector v2) { if (v1 == v2 || (v1.X == v2.X && v1.Y == v2.Y)) { return(0); } LowestPoint = FindLowestPoint(); double thetaA = Math.Atan2(v1.Y - LowestPoint.Y, v1.X - LowestPoint.X); double thetaB = Math.Atan2(v2.Y - LowestPoint.Y, v2.X - LowestPoint.X); if (thetaA < thetaB) { return(-1); } else if (thetaA > thetaB) { return(1); } double distanceA = GeomMath.Distance(LowestPoint, v1); double distanceB = GeomMath.Distance(LowestPoint, v2); if (distanceA < distanceB) { return(-1); } else { return(1); } }
private void LineTo(Vector p) { //Console.WriteLine("LineTo: " + p); if (IntersectingPolygon == null) { IntersectingPolygon = new PolygonModel(); } if (!GeomMath.AlmostEqual(currentStartPoint, p)) { IntersectingPolygon.Lines.Add(new LineModel { StartPoint = new Vector { X = currentStartPoint.X, Y = currentStartPoint.Y }, EndPoint = new Vector { X = p.X, Y = p.Y } }); currentStartPoint = new Vector { X = p.X, Y = p.Y }; //currentStartPoint = p; // point becomes start of next line } }
public bool AlmostEqual(DelaunayTriangle t2) { bool side1 = GeomMath.AlmostEqual(V1, t2.V1) || GeomMath.AlmostEqual(V1, t2.V2) || GeomMath.AlmostEqual(V1, t2.V3); bool side2 = GeomMath.AlmostEqual(V2, t2.V1) || GeomMath.AlmostEqual(V2, t2.V2) || GeomMath.AlmostEqual(V2, t2.V3); bool side3 = GeomMath.AlmostEqual(V3, t2.V1) || GeomMath.AlmostEqual(V3, t2.V2) || GeomMath.AlmostEqual(V3, t2.V3); return(side1 && side2 && side3); }
private int FindAngles(int[] e, double[] a, Vector v, Vector n) { Vector[] u = new Vector[4]; //for (int i = 0; i < 4; i++) // u[i] = (m_chull[(e[i] + 1) % size] - m_chull[e[i]]).normalize(); int size = hull.Count; for (int i = 0; i < 4; i++) { u[i] = GeomMath.Subtract(hull[(e[i] + 1) % size], hull[e[i]]).normalize(); } int w = 0; //a[0] = fabs(v * u[0]); //a[1] = fabs(n * u[1]); if (a[1] > a[w]) { w = 1; } //larger dot product means smaller angle //a[2] = fabs(v * u[2]); if (a[2] > a[w]) { w = 2; } //a[3] = fabs(n * u[3]); if (a[3] > a[w]) { w = 3; } a[0] = Math.Abs(GeomMath.DotProduct(v, u[0])); a[1] = Math.Abs(GeomMath.DotProduct(n, u[1])); if (a[1] > a[w]) { w = 1; } a[2] = Math.Abs(GeomMath.DotProduct(v, u[2])); if (a[2] > a[w]) { w = 2; } a[3] = Math.Abs(GeomMath.DotProduct(n, u[3])); if (a[3] > a[w]) { w = 3; } return(w); }
private bool AllPointsCollinear() { if (InputPoints.Count <= 2) { return(true); } Vector p1 = InputPoints[0]; Vector p2 = InputPoints[1]; for (int i = 2; i < InputPoints.Count; i++) { Vector p3 = InputPoints[i]; if (GeomMath.GetTurnDirection(p1, p2, p3) != GeomMath.DIRECTION_NONE) { return(false); } } return(true); }
private BoundingBox CreateBoundingBox(int[] e, Vector v, Vector n) { //box.corners[0] = m_chull[e[0]] + v * ((m_chull[e[1]] - m_chull[e[0]]) * v); //box.corners[3] = m_chull[e[0]] + v * ((m_chull[e[3]] - m_chull[e[0]]) * v); //box.corners[1] = m_chull[e[2]] + v * ((m_chull[e[1]] - m_chull[e[2]]) * v); //box.corners[2] = m_chull[e[2]] + v * ((m_chull[e[3]] - m_chull[e[2]]) * v); //box.width = fabs((m_chull[e[3]] - m_chull[e[1]]) * v); //box.height = fabs((m_chull[e[2]] - m_chull[e[0]]) * n); var box = new BoundingBox(); box.Corners[0] = GeomMath.Add(hull[e[0]], GeomMath.Multiply(v, GeomMath.DotProduct(GeomMath.Subtract(hull[e[1]], hull[e[0]]), v))); box.Corners[3] = GeomMath.Add(hull[e[0]], GeomMath.Multiply(v, GeomMath.DotProduct(GeomMath.Subtract(hull[e[3]], hull[e[0]]), v))); box.Corners[1] = GeomMath.Add(hull[e[2]], GeomMath.Multiply(v, GeomMath.DotProduct(GeomMath.Subtract(hull[e[1]], hull[e[2]]), v))); box.Corners[2] = GeomMath.Add(hull[e[2]], GeomMath.Multiply(v, GeomMath.DotProduct(GeomMath.Subtract(hull[e[3]], hull[e[2]]), v))); box.Width = Math.Abs(GeomMath.DotProduct(GeomMath.Subtract(hull[e[3]], hull[e[1]]), v)); box.Height = Math.Abs(GeomMath.DotProduct(GeomMath.Subtract(hull[e[2]], hull[e[0]]), n)); return(box); }
public int VectorCompare(Vector v1, Vector v2) { if (v1 == v2 || (v1.X == v2.X && v1.Y == v2.Y)) { return(0); } double thetaA = Math.Atan2(v1.Y - LowestPoint.Y, v1.X - LowestPoint.X); double thetaB = Math.Atan2(v2.Y - LowestPoint.Y, v2.X - LowestPoint.X); // Angle of difference vector v1 and LowestPoint, and the x-axis // If v1 has the smaller angle, return -1 if (thetaA < thetaB) { return(-1); } // If v2 has the smaller angle, return 1 else if (thetaA > thetaB) { return(1); } // If angles are the same, then choose the point that is closer to LowestPoint double distanceA = GeomMath.Distance(LowestPoint, v1); double distanceB = GeomMath.Distance(LowestPoint, v2); if (distanceA < distanceB) { return(-1); } else { return(1); } }
public bool IsPolygonConvex() { if (InputPolygon == null || InputPolygon.Lines.Count < 3) { return(false); } if (InputPolygon.Lines.Count == 3) { return(true); } int lastSign = Math.Sign(0); bool hasLastSign = false; for (int i = 0; i < InputPolygon.Lines.Count; i++) { Vector v1 = GetPoint(i, 0); Vector v2 = GetPoint(i, 1); Vector v3 = GetPoint(i, 2); double cp = GeomMath.CrossProduct(v1, v2, v3); int sign = Math.Sign(cp); if (hasLastSign && sign != lastSign) { return(false); } hasLastSign = true; lastSign = sign; } return(true); }
public bool HasVertex(Vector v) { return(GeomMath.AlmostEqual(V1, v) || GeomMath.AlmostEqual(V2, v) || GeomMath.AlmostEqual(V3, v)); }
public void Compute() { SortPolygons(); List <Vector> P = SortedPoly1; List <Vector> Q = SortedPoly2; int n = SortedPoly1.Count; int m = SortedPoly2.Count; int a, b; int a1, b1; Vector A = new Vector(), B = new Vector(); int cross; int bHA, aHB; Vector origin = new Vector { X = 0, Y = 0 }; Vector p = new Vector(), q = new Vector(); int inFlag; int aa, ba; bool firstPoint; Vector p0 = new Vector(); int code; a = 0; b = 0; aa = 0; ba = 0; inFlag = INFLAG_UNKNOWN; firstPoint = true; activeStatusLayer = History.CreateAndAddNewLayer("Setup"); activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The 'in' flag starts as unknown, initial vectors start at index 0" }); do { a1 = (a + n - 1) % n; b1 = (b + m - 1) % m; activeStatusLayer = History.CreateAndAddNewLayer("Main loop, a=" + a + ", b=" + b + ", a1=" + a1 + ", b1=" + b1); // SubVec(P[a], P[a1], A); A.X = P[a].X - P[a1].X; A.Y = P[a].Y - P[a1].Y; // SubVec(Q[b], Q[b1], B); B.X = Q[b].X - Q[b1].X; B.Y = Q[b].Y - Q[b1].Y; activeStatusLayer.AddCommand(new ClearTextStatusCommand { AssociatedAlgorithm = this }); AddNonIndexedLineCommand(activeStatusLayer, new Vector { X = P[a1].X, Y = P[a1].Y }, new Vector { X = P[a].X, Y = P[a].Y }); AddNonIndexedLineCommand(activeStatusLayer, new Vector { X = Q[b1].X, Y = Q[b1].Y }, new Vector { X = Q[b].X, Y = Q[b].Y }); cross = AreaSign(origin, A, B); aHB = AreaSign(Q[b1], Q[b], P[a]); bHA = AreaSign(P[a1], P[a], Q[b]); activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "cross=" + cross }); activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "aHB=" + aHB }); activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "bHA=" + bHA }); code = SegSegInt(P[a1], P[a], Q[b1], Q[b], p, q); activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Segment intersection status: " + TranslateCode(code) }); if (code == STATUS_1 || code == STATUS_V) { if (inFlag == STATUS_UNKNOWN && firstPoint) { aa = 0; ba = 0; firstPoint = false; p0.X = p.X; p0.Y = p.Y; // moveto p0 MoveTo(p0); } LineTo(p); inFlag = InOut(p, inFlag, aHB, bHA); activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Newly computed 'in' flag: " + TranslateInOut(inFlag) }); } if (code == STATUS_E && GeomMath.DotProduct(A, B) < 0) { activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Shared segement and ..." }); // print shared segment // return } if (cross == 0 && aHB < 0 && bHA < 0) { // disjoint // return activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Polygons are disjoint" }); } else if (cross == 0 && aHB == 0 && bHA == 0) { // advance but do not output point // page 262 if (inFlag == INFLAG_PIN) { // b = Advance(b, &ba, m, inflag == Qin, Q[b]); // if inFlag == Qin // always false here // lineto Q[b] ba++; b = (b + 1) % m; activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Advancing b vector but not including in output as not part of interection" }); } else { // a = Advance(a, &aa, n, inflag == Pin, P[a]); // if inFlag == Pin // always false here // lineto P[a] aa++; a = (a + 1) % n; activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Advancing a vector but not including in output as not part of interection" }); } } else if (cross >= 0) { if (bHA > 0) { // if inflag == PIN // lineto P[a] if (inFlag == INFLAG_PIN) { LineTo(P[a]); } aa++; a = (a + 1) % n; activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Advancing a vector and including in output as part of interection" }); } else { // if inflag == QIN // lineto Q[b] if (inFlag == INFLAG_QIN) { LineTo(Q[b]); } ba++; b = (b + 1) % m; activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Advancing b vector and including in output as part of interection" }); } } else { // cross < 0 if (aHB > 0) { // if inflag == QIN // lineto Q[b] if (inFlag == INFLAG_QIN) { LineTo(Q[b]); } ba++; b = (b + 1) % m; activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Advancing b vector and including in output as part of interection" }); } else { // if inflag == PIN // lineto P[a] if (inFlag == INFLAG_PIN) { LineTo(P[a]); } aa++; a = (a + 1) % n; activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Advancing a vector and including in output as part of interection" }); } } } while (((aa < n) || (ba < m)) && (aa < 2 * n) && (ba < 2 * m)); if (aa >= 2 * n) { activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Termination condition met: aa >= 2*n" }); } else if (aa >= n) { activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Termination condition met: aa >= n" }); } if (ba >= 2 * m) { activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Termination condition met: ba >= 2*m" }); } else if (ba >= m) { activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Termination condition met: ba >= m" }); } if (!firstPoint) { // close intesection, line to p0 LineTo(p0); } if (inFlag == INFLAG_UNKNOWN) { // boundaries do not cross activeStatusLayer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Boundaries of polygons do not cross, no intersection" }); } }
public override void Run() { // Algorithm Setup: save input points var layer = History.CreateAndAddNewLayer("Setup (sorting input)"); layer.AddCommand(new UpdatePointSetCommand { Label = "Input", Points = AlgorithmUtil.CopyVectorList(InputPoints) }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_SORT_START, SECTION_SORT)); SortedInput = new List <Vector>(); // algorithm step 1: sort, saved sorted input points foreach (var v in InputPoints) { SortedInput.Add(new Vector { X = v.X, Y = v.Y, Alternates = v.Alternates }); } SortedInput.Sort(GrahamSort); layer.AddCommand(new UpdatePointSetCommand { Label = "Sorted Input", Points = AlgorithmUtil.CopyVectorList(SortedInput) }); // check degenerate case #1: 0,1,2 points layer = History.CreateAndAddNewLayer("Degenerate Case Checks"); if (SortedInput.Count <= 2) { layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Algorithm cannot execute, it requires at least 3 points" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_DEGENERATE)); return; } // check degenerate case #2: all points on same line if (AllPointsCollinear()) { layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Algorithm cannot execute if all points are collinear" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_DEGENERATE)); return; } // points cannot be collinear, points must be at least 3 Stack <Vector> grahamStack = new Stack <Vector>(); grahamStack.Push(SortedInput[0]); grahamStack.Push(SortedInput[1]); // starting points: first two sorted points, pushed onto stack // STATIC LAYER: "Starting Points", has only input points // layer commentary: if 0, 1 or 2 points, cannot perform graham scan // if all points on same line, cannot perform graham scan layer = History.CreateAndAddNewLayer("Initiailzation"); layer.AddCommand(new AlgorithmStartingCommand { AssociatedAlgorithm = this }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_PRE)); var hip = new HighlightPointsCommand { AssociatedAlgorithm = this, HighlightLevel = 1 }; hip.Points.Add(new Vector { X = SortedInput[0].X, Y = SortedInput[0].Y }); hip.Points.Add(new Vector { X = SortedInput[1].X, Y = SortedInput[1].Y }); layer.AddCommand(hip); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Initial stack" }); AddStackLinesToLayer(layer, grahamStack); for (int i = 2; i < SortedInput.Count; i++) { layer = History.CreateAndAddNewLayer("Processing Point, index=" + i); layer.AddCommand(new AlgorithmStepStartingCommand { AssociatedAlgorithm = this, Comments = "Step Starting" }); layer.AddCommand(new ClearPointHighlightsCommand { AssociatedAlgorithm = this, Comments = "" }); layer.AddCommand(new ClearTextStatusCommand { AssociatedAlgorithm = this, Comments = "" }); // NORMAL LAYER: "Algorithm Layer (i-2)" // Commentary: "We examine the next point in sorted order with the top two // elements from the stack status structure, popping the first one" // stack visualization: highlight top of stack, label it "tail" // standalone visualization: show SortedInput[i] as "head" // show popped element as "middle" // highlight the "head" point in yellow, and the "middle" / "tail" points in green // layer commentary: // loop iteration "i" Vector head = SortedInput[i]; Vector middle = grahamStack.Pop(); Vector tail = grahamStack.Peek(); layer.AddCommand(new HighlightInputPointCommand { AssociatedAlgorithm = this, Comments = "Next input point", X = head.X, Y = head.Y, HightlightLevel = 1 }); hip = new HighlightPointsCommand { AssociatedAlgorithm = this, HighlightLevel = 1 }; hip.Points.Add(new Vector { X = head.X, Y = head.Y }); hip.Points.Add(new Vector { X = middle.X, Y = middle.Y }); hip.Points.Add(new Vector { X = tail.X, Y = tail.Y }); layer.AddCommand(hip); layer.AddCommand(new HighlightInputPointCommand { AssociatedAlgorithm = this, Comments = "Freshly popped from top of stack", X = middle.X, Y = middle.Y, HightlightLevel = 2 }); layer.AddCommand(new HighlightInputPointCommand { AssociatedAlgorithm = this, Comments = "Top of stack, which is left on stack and peeked", X = tail.X, Y = tail.Y, HightlightLevel = 2 }); // we examine next point in sorted list, with top element of stack (which we pop) // and 2nd element of stack (which we leave as new top of stack) // Commentary: "The turn direction of these three points is calculated using // the cross product of these three points" int turn = GeomMath.GetTurnDirection(tail, middle, head); // determine "turn" of these 3 points, in sequence tail / middle / head string turnString = "Counter-clockwise"; if (turn == GeomMath.DIRECTION_CLOCKWISE) { turnString = "Clockwise"; } else if (turn == GeomMath.DIRECTION_NONE) { turnString = "None"; } layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Computed turn: " + turnString }); // Standalone visualization: "The turn direction for these three points is: " switch (turn) { case GeomMath.DIRECTION_COUNTERCLOCKWISE: layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The point popped is pushed back onto stack since it is part of the hull" }); layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The input point is also pushed since it is potentially part of the hull" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_COUNTERCLOCKWISE)); grahamStack.Push(middle); grahamStack.Push(head); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Updated processing stack" }); break; case GeomMath.DIRECTION_CLOCKWISE: layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "The point on the top of the stack is discarded, but the input point is preserved for re-consideration" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_CLOCKWISE)); i--; break; case GeomMath.DIRECTION_NONE: layer.AddCommand(new AddTextStatusCommand { AssociatedAlgorithm = this, Comments = "Input point is co-linear with other points, so it is part of the hull" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_NONE)); grahamStack.Push(head); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Updated processing stack" }); break; } AddStackLinesToLayer(layer, grahamStack); } layer = History.CreateAndAddNewLayer("Final Results"); layer.AddCommand(new AlgorithmCompleteCommand { AssociatedAlgorithm = this }); layer.AddCommand(new ClearPointHighlightsCommand { AssociatedAlgorithm = this, Comments = "" }); layer.AddCommand(new ClearTextStatusCommand { AssociatedAlgorithm = this, Comments = "" }); layer.AddCommand(new HighlightLiveCodeSectionsCommand(AlgorithmId, SECTION_POST)); grahamStack.Push(SortedInput[0]); layer.AddCommand(new VectorProcessingStackUpdatedCommand { AssociatedAlgorithm = this, ProcessingStack = AlgorithmUtil.CopyVectorStack(grahamStack), Comments = "Final stack, first input point is pushed to complete the hull" }); AddStackLinesToLayer(layer, grahamStack); Hull = new PolygonModel(); var a = grahamStack.ToArray <Vector>(); for (int i = 0; i < a.Length - 1; i++) { var ap = a[i].Alternates; var ap2 = a[i + 1].Alternates; if (ap != null && ap2 != null) { // Main operation Hull.Lines.Add(new LineModel { StartPoint = new Vector { X = a[i].X, Y = a[i].Y, Alternates = new CanvasPoint { DotIndexLeft = ap.DotIndexLeft, DotIndexTop = ap.DotIndexTop } }, EndPoint = new Vector { X = a[i + 1].X, Y = a[i + 1].Y, Alternates = new CanvasPoint { DotIndexLeft = ap2.DotIndexLeft, DotIndexTop = ap2.DotIndexTop } } }); } else { // Side operation that doesn't involve points from grid Hull.Lines.Add(new LineModel { StartPoint = new Vector { X = a[i].X, Y = a[i].Y }, EndPoint = new Vector { X = a[i + 1].X, Y = a[i + 1].Y } }); } } }
public BoundingBox FindSmallestBoundingBox() { // mathtool::Vector2d v, n; // int e[4]; //vertex indices of extreme points // float a[4]; //angles // int w; //index (0~3), so that e[w] has the smallest value a[w] // int hullsize = m_chull.size(); Vector v, n = new Vector(); int[] e = new int[4]; double[] a = new double[4]; int w; //int hullsize = hull.Count; // //init extreme points // e[0] = 0; // v = (m_chull[1] - m_chull[0]).normalize(); // n.set(-v[1], v[0]); // const mathtool::Vector2d v0 = v; e[0] = 0; v = GeomMath.Subtract(hull[1], hull[0]).normalize(); n.X = -v.Y; n.Y = v.X; //Vector v0 = v; // float max_v = -FLT_MAX, min_v = FLT_MAX, max_n = -FLT_MAX; // for (int i = 2; i < hullsize; i++) // { // auto & pt = m_chull[i]; // double dv = (pt - m_chull[0]) * v; // double dn = (pt - m_chull[0]) * n; // if (dv > max_v) { max_v = dv; e[1] = i; } // if (dv < min_v) { min_v = dv; e[3] = i; } // if (dn > max_n) { max_n = dn; e[2] = i; } // } double max_v = Double.MinValue; double min_v = Double.MaxValue; double max_n = Double.MinValue; for (int i = 2; i < hull.Count; i++) { Vector pt = hull[i]; double dv = GeomMath.DotProduct(GeomMath.Subtract(pt, hull[0]), v); double dn = GeomMath.DotProduct(GeomMath.Subtract(pt, hull[0]), n); if (dv > max_v) { max_v = dv; e[1] = i; } if (dv < min_v) { min_v = dv; e[3] = i; } if (dn > max_n) { max_n = dn; e[2] = i; } } // w = findAngles(e, a, v, n); w = FindAngles(e, a, v, n); // //update extreme points // char svg_filename[256]; // for (int i = 0; i < m_chull.size(); i++) // { //#if DEBUG // cout << "box=" << box << endl; // sprintf(svg_filename, "%s%03d.svg", "DEBUG_", i); // saveSVG(svg_filename, m_chull_ply, box); // //saveSVG(svg_filename,m_ply.front(),box); //#endif // } double smallestArea = Double.MaxValue; BoundingBox smallestBox = null; for (int i = 0; i < hull.Count; i++) { // //create a box from v,n,e[4] // obb box = createOBB(e, v, n); var box = CreateBoundingBox(e, v, n); double area = box.Width * box.Height; if (smallestBox == null) { smallestBox = box; smallestArea = area; } else if (area < smallestArea) { smallestBox = box; smallestArea = area; } Console.WriteLine("Bounding Box #" + i + ": " + area); // //check if this box solve the problem // if (problem.solved(box)) break; // //update // int ne = (e[w] + 1) % hullsize; // mathtool::Vector2d nev = (m_chull[ne] - m_chull[e[w]]).normalize(); // if (w == 0 || w == 2) // { // v = nev; // n.set(-v[1], v[0]); // } // else // { // n = nev; // v.set(-n[1], n[0]); // } // e[w] = ne; // w = findAngles(e, a, v, n); int ne = (e[w] + 1) % hull.Count; Vector nev = GeomMath.Subtract(hull[ne], hull[e[w]]).normalize(); if (w == 0 || w == 2) { v = nev; n.X = -v.Y; n.Y = v.X; } else { n = nev; v.X = -n.Y; v.Y = n.X; } e[w] = ne; w = FindAngles(e, a, v, n); } return(smallestBox); }
public bool AlmostEquals(DelaunayEdge de) { return((GeomMath.AlmostEqual(VStart, de.VStart) && GeomMath.AlmostEqual(VEnd, de.VEnd)) || (GeomMath.AlmostEqual(VStart, de.VEnd) && GeomMath.AlmostEqual(VEnd, de.VStart))); }