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> /// For each face, the affine manifold that represents the plane in which the face is located;<br/> /// </summary> /// <param name="iFace"> /// face index /// </param> public AffineManifold GetFacePlane(int iFace) { if (m_FacePlanes == null) { m_FacePlanes = new AffineManifold[this.NoOfFaces]; var fCen = this.FaceCenters; var fNor = this.FaceNormals; for (int iF = 0; iF < this.NoOfFaces; iF++) { m_FacePlanes[iF] = new AffineManifold(fNor.GetRow(iF), fCen.GetRow(iF)); Debug.Assert(m_FacePlanes[iF].PointDistance(fCen.GetRow(iF)).Abs() < 1.0e-6); } } return(m_FacePlanes[iFace].CloneAs()); }
/// <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); }
/// <summary> /// Test on a 2D Voronoi mesh /// </summary> /// <param name="Res"> /// number of randomly chosen Delaunay vertices /// </param> /// <param name="deg"> /// polynomial degree /// </param> /// <param name="solver_name"> /// Name of solver to use. /// </param> public static SipControl TestVoronoi(int Res, SolverCodes solver_name = SolverCodes.classic_pardiso, int deg = 3) { if (System.Environment.MachineName.ToLowerInvariant().EndsWith("rennmaschin") //|| System.Environment.MachineName.ToLowerInvariant().Contains("jenkins") ) { // This is Florians Laptop; // he is to poor to afford MATLAB, so he uses OCTAVE BatchmodeConnector.Flav = BatchmodeConnector.Flavor.Octave; BatchmodeConnector.MatlabExecuteable = "C:\\cygwin64\\bin\\bash.exe"; } var R = new SipControl(); R.ProjectName = "SipPoisson-Voronoi"; R.SessionName = "testrun"; R.savetodb = false; R.FieldOptions.Add("T", new FieldOpts() { Degree = deg, SaveToDB = FieldOpts.SaveToDBOpt.TRUE }); R.FieldOptions.Add("Tex", new FieldOpts() { Degree = deg * 2 }); R.InitialValues_Evaluators.Add("RHS", X => 1.0); R.InitialValues_Evaluators.Add("Tex", X => 0.0); R.ExactSolution_provided = false; R.NoOfMultigridLevels = int.MaxValue; R.solver_name = solver_name; //R.TargetBlockSize = 100; bool IsIn(params double[] X) { Debug.Assert(X.Length == 2); double xi = X[0]; double yi = X[1]; //for(int l = 0; l < bndys.Length; l++) { // Debug.Assert(bndys[l].Normal.Length == 2); // if (bndys[l].PointDistance(xi, yi) > 0.0) // return false; //} if (xi > 1.0) { return(false); } if (yi > 1.0) { return(false); } if (xi < 0 && yi < 0) { return(false); } if (xi < -1) { return(false); } if (yi < -1) { return(false); } return(true); } int Mirror(ref double[] _x, ref double[] _y, AffineManifold[] bndys) { if (_x.Length != _y.Length) { throw new ArgumentException(); } var x = _x.ToList(); var y = _y.ToList(); int N = _x.Length; // filter all points that are outside of the domain for (int n = 0; n < N; n++) { if (!IsIn(x[n], y[n])) { x.RemoveAt(n); y.RemoveAt(n); N--; n--; } } Debug.Assert(x.Count == N); Debug.Assert(y.Count == N); for (int n = 0; n < N; n++) { Debug.Assert(IsIn(x[n], y[n])); } // mirror each point for (int n = 0; n < N; n++) { double xn = x[n]; double yn = y[n]; for (int l = 0; l < bndys.Length; l++) { var bndy_l = bndys[l]; double dist = bndy_l.PointDistance(xn, yn); if (dist < 0) { double xMirr = xn - bndy_l.Normal[0] * dist * 2; double yMirr = yn - bndy_l.Normal[1] * dist * 2; Debug.Assert(bndy_l.PointDistance(xMirr, yMirr) > 0); if (!IsIn(xMirr, yMirr)) { x.Add(xMirr); y.Add(yMirr); } } } } // return _x = x.ToArray(); _y = y.ToArray(); return(N); } GridCommons GridFunc() { GridCommons grd = null; var Matlab = new BatchmodeConnector(); // boundaries for L-domain AffineManifold[] Boundaries = new AffineManifold[6]; Boundaries[0] = new AffineManifold(new[] { 0.0, 1.0 }, new[] { 0.0, 1.0 }); Boundaries[1] = new AffineManifold(new[] { 1.0, 0.0 }, new[] { 1.0, 0.0 }); Boundaries[2] = new AffineManifold(new[] { -1.0, 0.0 }, new[] { -1.0, 0.0 }); Boundaries[3] = new AffineManifold(new[] { -1.0, 0.0 }, new[] { 0.0, 0.0 }); Boundaries[4] = new AffineManifold(new[] { 0.0, -1.0 }, new[] { 0.0, -1.0 }); Boundaries[5] = new AffineManifold(new[] { 0.0, -1.0 }, new[] { 0.0, 0.0 }); // generate Delaunay vertices Random rnd = new Random(0); double[] xNodes = Res.ForLoop(idx => rnd.NextDouble() * 2 - 1); double[] yNodes = Res.ForLoop(idx => rnd.NextDouble() * 2 - 1); int ResFix = Mirror(ref xNodes, ref yNodes, Boundaries); var Nodes = MultidimensionalArray.Create(xNodes.Length, 2); Nodes.SetColumn(0, xNodes); Nodes.SetColumn(1, yNodes); Matlab.PutMatrix(Nodes, "Nodes"); // compute Voronoi diagramm Matlab.Cmd("[V, C] = voronoin(Nodes);"); // output (export from matlab) int[][] OutputVertexIndex = new int[Nodes.NoOfRows][]; Matlab.GetStaggeredIntArray(OutputVertexIndex, "C"); Matlab.GetMatrix(null, "V"); // run matlab Matlab.Execute(false); // import here MultidimensionalArray 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]--; } } // tessellation List <Cell> cells = new List <Cell>(); List <int[]> aggregation = new List <int[]>(); for (int jV = 0; jV < ResFix; jV++) // loop over Voronoi Cells { Debug.Assert(IsIn(Nodes.GetRow(jV))); int[] iVtxS = OutputVertexIndex[jV]; int NV = iVtxS.Length; List <int> Agg2Pt = new List <int>(); for (int iTri = 0; iTri < NV - 2; iTri++) // loop over triangles of voronoi cell { int iV0 = iVtxS[0]; int iV1 = iVtxS[iTri + 1]; int iV2 = iVtxS[iTri + 2]; double[] V0 = VertexCoordinates.GetRow(iV0); double[] V1 = VertexCoordinates.GetRow(iV1); double[] V2 = VertexCoordinates.GetRow(iV2); double[] D1 = V1.Minus(V0); double[] D2 = V2.Minus(V0); Debug.Assert(D1.CrossProduct2D(D2).Abs() > 1.0e-8); if (D1.CrossProduct2D(D2) < 0) { double[] T = V2; int t = iV2; V2 = V1; iV2 = iV1; V1 = T; iV1 = t; } double[] Center = V0.Plus(V1).Plus(V2).Mul(1.0 / 3.0); //Debug.Assert(IsIn(Center[0], Center[1])); Cell Cj = new Cell(); Cj.GlobalID = cells.Count; Cj.Type = CellType.Triangle_3; Cj.TransformationParams = MultidimensionalArray.Create(3, 2); Cj.NodeIndices = new int[] { iV0, iV1, iV2 }; Cj.TransformationParams.SetRow(0, V0); Cj.TransformationParams.SetRow(1, V1); Cj.TransformationParams.SetRow(2, V2); Agg2Pt.Add(cells.Count); cells.Add(Cj); } aggregation.Add(Agg2Pt.ToArray()); } // return grid grd = new Grid2D(Triangle.Instance); grd.Cells = cells.ToArray(); grd.EdgeTagNames.Add(1, BoundaryType.Dirichlet.ToString()); grd.DefineEdgeTags(X => (byte)1); //grd.Plot2DGrid(); // create aggregation grid //var agrd = new AggregationGrid(grd, aggregation.ToArray()); return(grd); }; R.GridFunc = GridFunc; R.AddBoundaryValue(BoundaryType.Dirichlet.ToString(), "T", delegate(double[] X) { //double x = X[0], y = X[1]; return(0.0); //if(Math.Abs(X[0] - (0.0)) < 1.0e-8) // return 0.0; // //throw new ArgumentOutOfRangeException(); }); return(R); }
/// <summary> /// Usually used after <see cref="MergeLogically(GridCommons, GridCommons)"/>; this method finds element boundaries /// which intersect geometrically, but not logically and inserts a <see cref="CellFaceTag"/> which connects those cells. /// </summary> /// <param name="g"></param> /// <param name="upsampling"></param> /// <returns></returns> public static GridCommons Seal(GridCommons g, int upsampling = 4) { GridCommons R = g.CloneAs(); g = null; GridData gdat = new GridData(R); int D = gdat.SpatialDimension; int J = gdat.Cells.NoOfLocalUpdatedCells; if (R.CellPartitioning.MpiSize > 1) { throw new NotSupportedException("Not supported in MPI-parallel mode."); } //NodeSet[] TestNodes = gdat.Edges.EdgeRefElements.Select(KrefEdge => KrefEdge.GetSubdivisionTree(upsampling).GlobalVertice).ToArray(); NodeSet[] TestNodes = gdat.Edges.EdgeRefElements.Select(KrefEdge => KrefEdge.GetBruteForceQuadRule(upsampling, 1).Nodes).ToArray(); // Its better to use vertices in the interior of the element; if we use vertices at the corners, we might get // intersection of edges that just share one point. // Define all edges that will be tested (set to boundary edges) // ============================================================ int[] UnknownEdges = gdat.BoundaryEdges.ItemEnum.ToArray(); int L = UnknownEdges.Sum(iEdg => TestNodes[gdat.Edges.GetRefElementIndex(iEdg)].NoOfNodes); // Transform nodes on edges (that should be tested) to global coordinates // ====================================================================== MultidimensionalArray TestNodesGlobal = MultidimensionalArray.Create(L, D); MultidimensionalArray NormalsGlobal = MultidimensionalArray.Create(L, D); int[] NodeToEdge = new int[L]; // pointer l -> Edge index, where l is the first index into 'TestNodesGlobal' & 'NormalsGlobal' int[,] E2C = gdat.Edges.CellIndices; int cnt = 0; foreach (int iEdg in UnknownEdges) { int iKref = gdat.Edges.GetRefElementIndex(iEdg); NodeSet Ns = TestNodes[iKref]; int K = Ns.NoOfNodes; int[] I0 = new int[] { cnt, 0 }; int[] IE = new int[] { cnt + K - 1, D - 1 }; MultidimensionalArray TN = gdat.GlobalNodes.GetValue_EdgeSV(Ns, iEdg, 1); TestNodesGlobal.SetSubArray(TN.ExtractSubArrayShallow(0, -1, -1), I0, IE); MultidimensionalArray N1 = gdat.Edges.NormalsCache.GetNormals_Edge(Ns, iEdg, 1); NormalsGlobal.SetSubArray(N1.ExtractSubArrayShallow(0, -1, -1), I0, IE); for (int i = cnt; i < cnt + K; i++) { NodeToEdge[i] = iEdg; } cnt += K; } // binary tree to speed up point localization int[] pl_Permutation = new int[L]; PointLocalization pl = new PointLocalization(TestNodesGlobal, 0.01, pl_Permutation); Debug.Assert(!object.ReferenceEquals(pl.Points, TestNodesGlobal)); // compare search edges to all other nodes // ======================================= // mapping: cell --> Neighbour cell index, face index // 1st index: Cell index; // 2nd index: enumeration List <Tuple <int, int> >[] FoundPairings = new List <Tuple <int, int> > [gdat.Cells.NoOfCells]; int[][] C2E = gdat.Cells.Cells2Edges; byte[,] E2F = gdat.Edges.FaceIndices; int cnt2 = 0; for (int iEdgC = 0; iEdgC < UnknownEdges.Length; iEdgC++) // loop over edges that may get sealed { int iEdg = UnknownEdges[iEdgC]; int iKref = gdat.Edges.GetRefElementIndex(iEdg); NodeSet Ns = TestNodes[iKref]; int K = Ns.NoOfNodes; int jCell1 = E2C[iEdg, 0]; Debug.Assert(E2C[iEdg, 1] < 0); int iFace1 = E2F[iEdg, 0]; Debug.Assert(E2F[iEdg, 1] == byte.MaxValue); int[] I0 = new int[] { cnt2, 0 }; int[] IE = new int[] { cnt2 + K - 1, D - 1 }; MultidimensionalArray TN = TestNodesGlobal.ExtractSubArrayShallow(I0, IE); //MultidimensionalArray N1 = NormalsGlobal.ExtractSubArrayShallow(I0, IE); // find bounding box for edge BoundingBox bbEdge = new BoundingBox(TN); if (bbEdge.h_min / bbEdge.h_max < 1.0e-5) { // very thin bounding box, thicken slightly double delta = bbEdge.h_max * 1.0e-5; for (int d = 0; d < D; d++) { bbEdge.Min[d] -= delta; bbEdge.Max[d] += delta; } } bbEdge.ExtendByFactor(0.01); // determine binary code for bounding box int bbEdgeSigBits; GeomBinTreeBranchCode bbEdgeCode = GeomBinTreeBranchCode.Combine( GeomBinTreeBranchCode.CreateFormPoint(pl.PointsBB, bbEdge.Min), GeomBinTreeBranchCode.CreateFormPoint(pl.PointsBB, bbEdge.Max), out bbEdgeSigBits); // determine all points in bounding box int iP0, Len; pl.GetPointsInBranch(bbEdgeCode, bbEdgeSigBits, out iP0, out Len); // determine all edged which potentially overlap with edge 'iEdg' HashSet <int> PotOvrlap = new HashSet <int>(); // a set of edge indices for (int n = 0; n < Len; n++) { int l = iP0 + n; int iPt = pl_Permutation[l]; Debug.Assert(GenericBlas.L2DistPow2(pl.Points.GetRow(l), TestNodesGlobal.GetRow(iPt)) <= 0); int iOvlpEdge = NodeToEdge[iPt]; if (iOvlpEdge != iEdg) { PotOvrlap.Add(iOvlpEdge); } } //int[] PotOvrlap = UnknownEdges.CloneAs(); // determine actually overlapping boundary edges: foreach (int iOvrlapEdge in PotOvrlap) { int jCell2 = E2C[iOvrlapEdge, 0]; Debug.Assert(E2C[iOvrlapEdge, 1] < 0); if (jCell2 == jCell1) { continue; } int iFace2 = E2F[iOvrlapEdge, 0]; Debug.Assert(E2F[iOvrlapEdge, 1] == byte.MaxValue); int AllreadyFound = FoundPairings[jCell1] == null ? 0 : FoundPairings[jCell1].Where(tp => tp.Item1 == jCell2 && tp.Item2 == iFace1).Count(); if (AllreadyFound > 1) { throw new ApplicationException("Error in algorithmus."); } if (AllreadyFound > 0) { continue; } var Kref_j2 = gdat.Cells.GetRefElement(jCell2); double h = Kref_j2.GetMaxDiameter(); MultidimensionalArray LocVtx_j2 = MultidimensionalArray.Create(K, D); bool[] NewtonConvervence = new bool[K]; gdat.TransformGlobal2Local(TN, LocVtx_j2, jCell2, NewtonConvervence); for (int k = 0; k < K; k++) // loop over all transformed points { if (!NewtonConvervence[k]) { continue; } double[] pt = LocVtx_j2.GetRow(k); double[] ptClose = new double[D]; double dist = Kref_j2.ClosestPoint(pt, ptClose); if (dist > h * 1.0e-8) { continue; } AffineManifold Face = Kref_j2.GetFacePlane(iFace2); double FaceDist = Face.PointDistance(pt); if (FaceDist.Abs() > 1.0e-8 * h) { continue; } NodeSet Ns2 = new NodeSet(Kref_j2, pt); MultidimensionalArray Normals2 = MultidimensionalArray.Create(1, D); gdat.Edges.GetNormalsForCell(Ns2, jCell2, iFace2, Normals2); double[] N1d = NormalsGlobal.GetRow(cnt2 + k); double[] N2d = Normals2.GetRow(0); //Check if face normals points exactly in the opposite direction, 2 ways: // 1) calculate angle between both normals -> bad choice because of Math.Acos // 2) inner product of two opposite vectors is -1 //if (Math.Abs(Math.Abs(Math.Acos(GenericBlas.InnerProd(N1d, N2d))) - Math.PI) > 1.0e-8) if (Math.Abs(GenericBlas.InnerProd(N1d, N2d) + 1.0) > 1.0e-8) { continue; } // if we reach this point, jCell1 should match with jCell2/iFace2 if (FoundPairings[jCell1] == null) { FoundPairings[jCell1] = new List <Tuple <int, int> >(); } if (FoundPairings[jCell2] == null) { FoundPairings[jCell2] = new List <Tuple <int, int> >(); } FoundPairings[jCell1].Add(new Tuple <int, int>(jCell2, iFace1)); FoundPairings[jCell2].Add(new Tuple <int, int>(jCell1, iFace2)); break; // no need to test jCell1 vs. jCell2 anymore } } cnt2 += K; } // add the newly found pairings to the grid for (int j = 0; j < J; j++) { var fp = FoundPairings[j]; if (fp != null) { foreach (var t in fp) { ArrayTools.AddToArray(new CellFaceTag() { EdgeTag = 0, FaceIndex = t.Item2, ConformalNeighborship = false, NeighCell_GlobalID = gdat.CurrentGlobalIdPermutation.Values[t.Item1] }, ref R.Cells[j].CellFaceTags); } } } return(R); }
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 ) } } }