static Vector ComputeIntersection(Vector S, Vector E, AffineManifold clipEdge) { var SE = AffineManifold.FromPoints(S, E); var I = AffineManifold.Intersect2D(SE, clipEdge); //var D = S - E; //D.Normalize(); //var N1 = new Vector(D.y, -D.x); //double inner = N1 * clipEdge.Normal; Debug.Assert(!double.IsNaN(I.x)); Debug.Assert(!double.IsInfinity(I.x)); Debug.Assert(!double.IsNaN(I.y)); Debug.Assert(!double.IsInfinity(I.y)); return(I); }
/// <summary> /// Intersection of line <paramref name="S1"/>--<paramref name="S2"/> and <paramref name="E1"/>--<paramref name="E2"/> /// </summary> /// <param name="S1"></param> /// <param name="S2"></param> /// <param name="E1"></param> /// <param name="E2"></param> /// <param name="alpha1"> /// coordinate of <paramref name="I"/> on the line <paramref name="S1"/>--<paramref name="S2"/> /// </param> /// <param name="alpha2"> /// coordinate of <paramref name="I"/> on the line <paramref name="E1"/>--<paramref name="E2"/> /// </param> /// <param name="I"></param> /// <returns></returns> public static bool ComputeIntersection(Vector S1, Vector S2, Vector E1, Vector E2, out double alpha1, out double alpha2, out Vector I) { if (S1.Dim != 2) { throw new ArgumentException("spatial dimension mismatch."); } if (S2.Dim != 2) { throw new ArgumentException("spatial dimension mismatch."); } if (E1.Dim != 2) { throw new ArgumentException("spatial dimension mismatch."); } if (E2.Dim != 2) { throw new ArgumentException("spatial dimension mismatch."); } Vector S12 = S2 - S1; Vector E12 = E2 - E1; var P_S12 = AffineManifold.FromPoints(S1, S2); var P_E12 = AffineManifold.FromPoints(E1, E2); double parallel = S12[0] * E12[1] - S12[1] * E12[0]; double relParallel = parallel * parallel / (S12.AbsSquare() * E12.AbsSquare()); if (Math.Abs(relParallel) <= 1e-20) { alpha1 = P_S12.PointDistance(E1); alpha1 /= E12.Abs(); alpha2 = double.PositiveInfinity; I = new Vector(double.PositiveInfinity, double.PositiveInfinity); return(false); } //S12.Normalize(); //E12.Normalize(); I = AffineManifold.Intersect2D(P_S12, P_E12); Vector IS1 = I - S2; Vector IE1 = I - E2; Vector IS2 = I - S1; Vector IE2 = I - E1; Vector IS; bool flip_1; if (IS1.AbsSquare() > IS2.AbsSquare()) { IS = IS1; flip_1 = true; } else { IS = IS2; flip_1 = false; } Vector IE; bool flip_2; if (IE1.AbsSquare() > IE2.AbsSquare()) { IE = IE1; flip_2 = true; } else { IE = IE2; flip_2 = false; } Debug.Assert((S12.AngleTo(IS).Abs() <= 1.0e-5) || ((S12.AngleTo(IS).Abs() - Math.PI).Abs() <= 1.0e-5)); Debug.Assert((E12.AngleTo(IE).Abs() <= 1.0e-5) || ((E12.AngleTo(IE).Abs() - Math.PI).Abs() <= 1.0e-5)); alpha1 = (S12 * IS) / S12.AbsSquare(); alpha2 = (E12 * IE) / E12.AbsSquare(); if (flip_1) { alpha1 = 1 + alpha1; } if (flip_2) { alpha2 = 1 + alpha2; } return(true); }
public void CreateWithMatlab() { // ================================ // generate voronoi graph in matlab // ================================ int J = this.DelaunayVertices.NoOfRows; int D = this.DelaunayVertices.NoOfCols; if (D != 2) { throw new NotSupportedException("todo"); } int[][] OutputVertexIndex = new int[J * 5][]; MultidimensionalArray VertexCoordinates; { var Matlab = new BatchmodeConnector(); Matlab.PutMatrix(this.DelaunayVertices, "X"); // create mirror points Matlab.Cmd("[J, D] = size(X);"); Matlab.Cmd("Xneg = [-X(:, 1), X(:, 2)];"); Matlab.Cmd("Yneg = [X(:, 1), -X(:, 2)];"); Matlab.Cmd("X2 = [ones(J, 1) * 2, zeros(J, 1)];"); Matlab.Cmd("Y2 = [zeros(J, 1), ones(J, 1) * 2];"); Matlab.Cmd("Xm = X;"); Matlab.Cmd("Xm = [Xm; Xneg]; % mirror at x = 0"); Matlab.Cmd("Xm = [Xm; X2 + Xneg]; % mirror at x = 1"); Matlab.Cmd("Xm = [Xm; Yneg]; % mirror at x = 0"); Matlab.Cmd("Xm = [Xm; Y2 + Yneg]; % mirror at x = 1"); // compute Voronoi diagramm Matlab.Cmd("[V, C] = voronoin(Xm);"); // output (export from matlab) Matlab.GetStaggeredIntArray(OutputVertexIndex, "C"); Matlab.GetMatrix(null, "V"); // run matlab Matlab.Execute(false); // import here VertexCoordinates = (MultidimensionalArray)(Matlab.OutputObjects["V"]); // correct indices (1-based index to 0-based index) foreach (int[] cell in OutputVertexIndex) { int K = cell.Length; for (int k = 0; k < K; k++) { cell[k]--; } } } // =============== // record internal // =============== { // define Cell data { this.m_CellData = new CellData(); this.m_CellData.m_Owner = this; this.m_VertexData = new VertexData(); this.m_LogEdges = new LogEdgeData(); this.m_VertexData.Coordinates = VertexCoordinates; this.m_CellData.CellVertices = OutputVertexIndex.GetSubVector(0, J); this.m_CellData.InfoFlags = new CellInfo[J]; ArrayTools.SetAll(this.m_CellData.InfoFlags, CellInfo.CellIsAffineLinear | CellInfo.IsAggregate); } // decomposition of Voronoi cells to triangles/tetrahedrons { m_CellData.AggregateCellToParts = new int[J][]; if (D == 2) { var Tri = RefElements.Triangle.Instance; m_CellData.RefElements = new RefElement[] { Tri }; int cnt = 0; for (int j = 0; j < J; j++) { int[] VtxIndices = m_CellData.CellVertices[j]; int[] PartIdx = new int[VtxIndices.Length - 2]; for (int i = 0; i < PartIdx.Length; i++) { PartIdx[i] = cnt; cnt++; } m_CellData.AggregateCellToParts[j] = PartIdx; } int NoOfParts = cnt; m_CellData.PartTransformation = MultidimensionalArray.Create(NoOfParts, D, D); m_CellData.PartCenter = MultidimensionalArray.Create(NoOfParts, D); MultidimensionalArray TriangleVtx = MultidimensionalArray.Create(3, D); for (int j = 0; j < J; j++) { int[] VtxIndices = m_CellData.CellVertices[j]; int[] PartIdx = m_CellData.AggregateCellToParts[j]; for (int i = 0; i < PartIdx.Length; i++) { int iV0 = VtxIndices[0]; int iV1 = VtxIndices[i + 1]; int iV2 = VtxIndices[i + 2]; TriangleVtx[0, 0] = m_VertexData.Coordinates[iV0, 0]; TriangleVtx[0, 1] = m_VertexData.Coordinates[iV0, 1]; TriangleVtx[1, 0] = m_VertexData.Coordinates[iV1, 0]; TriangleVtx[1, 1] = m_VertexData.Coordinates[iV1, 1]; TriangleVtx[2, 0] = m_VertexData.Coordinates[iV2, 0]; TriangleVtx[2, 1] = m_VertexData.Coordinates[iV2, 1]; var TR = AffineTrafo.FromPoints(Tri.Vertices, TriangleVtx); m_CellData.PartTransformation.ExtractSubArrayShallow(PartIdx[i], -1, -1).Set(TR.Matrix); m_CellData.PartCenter.ExtractSubArrayShallow(PartIdx[i], -1).SetVector(TR.Affine); } } } else if (D == 3) { throw new NotImplementedException("todo"); } else { throw new NotSupportedException("Unknown spatial dimension."); } } // bounding boxes, transformations { BoundingBox BB = new BoundingBox(D); m_CellData.BoundingBoxTransformation = MultidimensionalArray.Create(J, D, D); m_CellData.BoundingBoxCenter = MultidimensionalArray.Create(J, D); for (int j = 0; j < J; j++) { m_CellData.GetCellBoundingBox(j, BB); for (int d = 0; d < D; d++) { double lo = BB.Min[d]; double hi = BB.Max[d]; m_CellData.BoundingBoxCenter[j, d] = 0.5 * (lo + hi); m_CellData.BoundingBoxTransformation[j, d, d] = 0.5 * (hi - lo); } } } // mapping: vertex to cell { List <int>[] VertexToCell = new List <int> [VertexCoordinates.Length]; for (int j = 0; j < J; j++) { foreach (int iVtx in OutputVertexIndex[j]) { if (VertexToCell[iVtx] == null) { VertexToCell[iVtx] = new List <int>(); } if (!VertexToCell[iVtx].Contains(j)) { VertexToCell[iVtx].Add(j); } } } m_VertexData.VerticeToCell = new int[VertexToCell.Length][]; for (int i = 0; i < VertexToCell.Length; i++) { if (VertexToCell[i] == null) { m_VertexData.VerticeToCell[i] = new int[0]; } else { m_VertexData.VerticeToCell[i] = VertexToCell[i].ToArray(); } } VertexToCell = null; } // cell neighbors, edges { m_CellData.CellNeighbours = new int[J][]; var tmpCells2Edges = new List <int> [J]; Dictionary <int, int> ShareCount = new Dictionary <int, int>(); // key: cell index; value: number of vertices shared with this cell var EdgesTemp = new List <EdgeTemp>(); List <int> Neighs = new List <int>(); List <int> EdgeVtx = new List <int>(); List <int> IdedEdgsAtOneCell = new List <int>(); for (int jCell = 0; jCell < J; jCell++) // loop over cells { ShareCount.Clear(); Neighs.Clear(); IdedEdgsAtOneCell.Clear(); // determine how many vertices 'jCell' shares with other cells foreach (int iVtx in m_CellData.CellVertices[jCell]) { foreach (int jOtherCell in m_VertexData.VerticeToCell[iVtx]) { if (jOtherCell != jCell) { if (!ShareCount.ContainsKey(jOtherCell)) { ShareCount.Add(jOtherCell, 1); } else { ShareCount[jOtherCell]++; } } } } // find faces int[][] FaceIdx = ConvexHullFaces(m_VertexData.Coordinates, m_CellData.CellVertices[jCell]); // determine cell neighbors and edges int NoOfFacesFound = 0; foreach (var kv in ShareCount) { int jCellNeigh = kv.Key; int NoOfSharedVtx = kv.Value; if (NoOfSharedVtx >= D) { // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // cells 'jCell' and 'jCellNeigh' share more than 'D' vertices - this is an edge to another cell, // resp. a face of 'jCell'. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Debug.Assert(jCellNeigh != jCell); Debug.Assert(!Neighs.Contains(jCellNeigh)); Neighs.Add(jCellNeigh); NoOfFacesFound++; EdgeVtx.Clear(); foreach (int iVtx in m_CellData.CellVertices[jCell]) { if (Array.IndexOf(m_VertexData.VerticeToCell[iVtx], jCellNeigh) >= 0) { EdgeVtx.Add(iVtx); } } if (jCell < jCellNeigh) { // the pairing 'jCell'/'jCellNeigh' will be discovered twice; // we only want to record once var Etmp = new EdgeTemp() { jCell1 = jCell, jCell2 = jCellNeigh, Vertices = EdgeVtx.ToArray() }; EdgesTemp.Add(Etmp); IdedEdgsAtOneCell.Add(EdgesTemp.Count - 1); if (tmpCells2Edges[jCell] == null) { tmpCells2Edges[jCell] = new List <int>(); } if (tmpCells2Edges[jCellNeigh] == null) { tmpCells2Edges[jCellNeigh] = new List <int>(); } tmpCells2Edges[jCell].Add(EdgesTemp.Count); // the funky convention for edges-to-cell: the index is tmpCells2Edges[jCellNeigh].Add(-EdgesTemp.Count); // shifted by 1, out-cell is negative } else { Debug.Assert(jCellNeigh < jCell); int MatchCount = 0; foreach (int i in tmpCells2Edges[jCellNeigh]) { int iEdge = Math.Abs(i) - 1; if (EdgesTemp[iEdge].jCell1 == jCellNeigh && EdgesTemp[iEdge].jCell2 == jCell) { MatchCount++; IdedEdgsAtOneCell.Add(iEdge); } } Debug.Assert(MatchCount == 1); } #if DEBUG if (D == 2) { Debug.Assert(EdgeVtx.Count == 2); } else if (D == 3) { // verify that all vertices of the edge are geometrically in one plane Debug.Assert(EdgeVtx.Count >= 3); var FacePlane = AffineManifold.FromPoints( m_VertexData.Coordinates.GetRowPt(EdgeVtx[0]), m_VertexData.Coordinates.GetRowPt(EdgeVtx[1]), m_VertexData.Coordinates.GetRowPt(EdgeVtx[2]) ); BoundingBox BB = new BoundingBox(D); m_CellData.GetCellBoundingBox(jCell, BB); double h = BB.Diameter; foreach (int iVtx in EdgeVtx) { double dist = Math.Abs(FacePlane.PointDistance(m_VertexData.Coordinates.GetRow(iVtx))); Debug.Assert(dist < h * 1e-8); } } else { throw new NotSupportedException("Unknown spatial dimension."); } #endif } } m_CellData.CellNeighbours[jCell] = Neighs.ToArray(); Debug.Assert(NoOfFacesFound <= FaceIdx.Length); Debug.Assert(NoOfFacesFound == IdedEdgsAtOneCell.Count); // boundary edges if (NoOfFacesFound == FaceIdx.Length) { // nothing to do - all faces/edges identified #if DEBUG for (int i = 0; i < NoOfFacesFound; i++) { int Matches = 0; for (int l = 0; l < NoOfFacesFound; l++) { if (FaceIdx[i].SetEquals(EdgesTemp[IdedEdgsAtOneCell[l]].Vertices)) { Matches++; } } Debug.Assert(Matches == 1); } #endif } else { // missing boundary for (int i = 0; i < FaceIdx.Length; i++) { int Matches = 0; for (int l = 0; l < NoOfFacesFound; l++) { if (FaceIdx[i].SetEquals(EdgesTemp[IdedEdgsAtOneCell[l]].Vertices)) { Matches++; } } Debug.Assert(Matches <= 1); if (Matches == 0) { // boundary edge found var Etmp = new EdgeTemp() { jCell1 = jCell, jCell2 = int.MinValue, Vertices = EdgeVtx.ToArray() }; EdgesTemp.Add(Etmp); tmpCells2Edges[jCell].Add(EdgesTemp.Count); // index shifted by 1 } } } } // convert temporary data structures to the final ones m_CellData.Cells2Edges = new int[J][]; var C2E = m_CellData.Cells2Edges; for (int j = 0; j < J; j++) { C2E[j] = tmpCells2Edges[j] != null ? tmpCells2Edges[j].ToArray() : new int[0]; } m_GeomEdges = new GeomEdgeData(); int NoOfEdges = EdgesTemp.Count; m_GeomEdges.Info = new EdgeInfo[NoOfEdges]; m_LogEdges.CellIndices = new int[NoOfEdges, 2]; m_GeomEdges.VertexIndices = new int[NoOfEdges][]; var Evtx = m_GeomEdges.VertexIndices; var E2C = m_LogEdges.CellIndices; var Einf = m_GeomEdges.Info; for (int iEdge = 0; iEdge < NoOfEdges; iEdge++) { var Etmp = EdgesTemp[iEdge]; E2C[iEdge, 0] = Etmp.jCell1; E2C[iEdge, 1] = Etmp.jCell2; Einf[iEdge] = EdgeInfo.EdgeIsAffineLinear | EdgeInfo.IsAggregate; if (Etmp.jCell2 < 0) { Einf[iEdge] |= EdgeInfo.Boundary; } Evtx[iEdge] = Etmp.Vertices; } } // edge metrics { if (D == 2) { m_GeomEdges.EdgeRefElements = new RefElement[] { Line.Instance }; } else if (D == 3) { m_GeomEdges.EdgeRefElements = new RefElement[] { Triangle.Instance }; } else { throw new NotSupportedException("Unknown spatial dimension."); } int[][] Evtx = m_GeomEdges.VertexIndices; int NoOfParts = 0; int NoOfEdges = m_GeomEdges.Count; for (int iEdge = 0; iEdge < NoOfEdges; iEdge++) { NoOfParts += Evtx[iEdge].Length - D + 1; } Debug.Assert(D != 2 || NoOfParts == NoOfEdges); m_GeomEdges.Edge2CellTrafoIndex = new int[NoOfParts, 2]; var tmpEdg2CellTrafo = new Dictionary <int, Tuple <int, AffineTrafo> >(); // unsolved problem: // (D-1) -- dimensional tesselation of edge is given by D -- dimensional tesselation of adjacent cells // * case D == 2: trivial // * case D == 3: the problem is that two adjacent cells will induce _two different_ tesselations, which // wont match in the general case. MultidimensionalArray PreImage = m_GeomEdges.EdgeRefElements[0].Vertices; MultidimensionalArray Image = MultidimensionalArray.Create(PreImage.NoOfRows, D); //for(int iEdge ) } } }