public void TestDissolve() { // Outer space triangle. var dummy = new Triangle() { id = -1 }; var triangles = CreateExampleMesh(); Otri s = default; Otri t = default; Otri tmp = default; // The bottom left triangle with edge 1 -> 3. s.tri = triangles[0]; s.orient = 1; // The center triangle with edge 3 -> 1. t.tri = triangles[1]; t.orient = 2; // Make sure we're on the correct edges. Assert.AreEqual(1, s.Org().ID); Assert.AreEqual(3, s.Dest().ID); Assert.AreEqual(3, t.Org().ID); Assert.AreEqual(1, t.Dest().ID); // Check that neighbors are properly set. s.Sym(ref tmp); Assert.AreEqual(1, tmp.tri.ID); t.Sym(ref tmp); Assert.AreEqual(0, tmp.tri.ID); // Now dissolve the bond from one side. s.Dissolve(dummy); // Check neighbors. s.Sym(ref tmp); Assert.AreEqual(-1, tmp.tri.ID); t.Sym(ref tmp); Assert.AreEqual(0, tmp.tri.ID); // And dissolve the bond from the other side. t.Dissolve(dummy); // Check neighbors. s.Sym(ref tmp); Assert.AreEqual(-1, tmp.tri.ID); t.Sym(ref tmp); Assert.AreEqual(-1, tmp.tri.ID); }
/// <summary> /// Removes ghost triangles. /// </summary> /// <param name="startghost"></param> /// <returns>Number of vertices on the hull.</returns> int RemoveGhosts(ref Otri startghost) { Otri searchedge = default(Otri); Otri dissolveedge = default(Otri); Otri deadtriangle = default(Otri); Vertex markorg; int hullsize; bool noPoly = !mesh.behavior.Poly; var dummytri = mesh.dummytri; // Find an edge on the convex hull to start point location from. startghost.Lprev(ref searchedge); searchedge.Sym(); dummytri.neighbors[0] = searchedge; // Remove the bounding box and count the convex hull edges. startghost.Copy(ref dissolveedge); hullsize = 0; do { hullsize++; dissolveedge.Lnext(ref deadtriangle); dissolveedge.Lprev(); dissolveedge.Sym(); // If no PSLG is involved, set the boundary markers of all the vertices // on the convex hull. If a PSLG is used, this step is done later. if (noPoly) { // Watch out for the case where all the input vertices are collinear. if (dissolveedge.tri.id != Mesh.DUMMY) { markorg = dissolveedge.Org(); if (markorg.label == 0) { markorg.label = 1; } } } // Remove a bounding triangle from a convex hull triangle. dissolveedge.Dissolve(dummytri); // Find the next bounding triangle. deadtriangle.Sym(ref dissolveedge); // Delete the bounding triangle. mesh.TriangleDealloc(deadtriangle.tri); } while (!dissolveedge.Equals(startghost)); return(hullsize); }
public int CheckSeg4Encroach(ref Osub testsubseg) { double num; Vertex vertex; Otri otri = new Otri(); Osub osub = new Osub(); int num1 = 0; int num2 = 0; Vertex vertex1 = testsubseg.Org(); Vertex vertex2 = testsubseg.Dest(); testsubseg.TriPivot(ref otri); if (otri.triangle != Mesh.dummytri) { num2++; vertex = otri.Apex(); num = (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y); if (num < 0 && (this.behavior.ConformingDelaunay || num * num >= (2 * this.behavior.goodAngle - 1) * (2 * this.behavior.goodAngle - 1) * ((vertex1.x - vertex.x) * (vertex1.x - vertex.x) + (vertex1.y - vertex.y) * (vertex1.y - vertex.y)) * ((vertex2.x - vertex.x) * (vertex2.x - vertex.x) + (vertex2.y - vertex.y) * (vertex2.y - vertex.y)))) { num1 = 1; } } testsubseg.Sym(ref osub); osub.TriPivot(ref otri); if (otri.triangle != Mesh.dummytri) { num2++; vertex = otri.Apex(); num = (vertex1.x - vertex.x) * (vertex2.x - vertex.x) + (vertex1.y - vertex.y) * (vertex2.y - vertex.y); if (num < 0 && (this.behavior.ConformingDelaunay || num * num >= (2 * this.behavior.goodAngle - 1) * (2 * this.behavior.goodAngle - 1) * ((vertex1.x - vertex.x) * (vertex1.x - vertex.x) + (vertex1.y - vertex.y) * (vertex1.y - vertex.y)) * ((vertex2.x - vertex.x) * (vertex2.x - vertex.x) + (vertex2.y - vertex.y) * (vertex2.y - vertex.y)))) { num1 = num1 + 2; } } if (num1 > 0 && (this.behavior.NoBisect == 0 || this.behavior.NoBisect == 1 && num2 == 2)) { BadSubseg badSubseg = new BadSubseg(); if (num1 != 1) { badSubseg.encsubseg = osub; badSubseg.subsegorg = vertex2; badSubseg.subsegdest = vertex1; } else { badSubseg.encsubseg = testsubseg; badSubseg.subsegorg = vertex1; badSubseg.subsegdest = vertex2; } this.badsubsegs.Enqueue(badSubseg); } return(num1); }
public static void WriteEdges(Mesh mesh, string filename) { Otri otri = new Otri(); Otri otri1 = new Otri(); Osub osub = new Osub(); Behavior behavior = mesh.behavior; using (StreamWriter streamWriter = new StreamWriter(new FileStream(filename, FileMode.Create))) { streamWriter.WriteLine("{0} {1}", mesh.edges, (behavior.UseBoundaryMarkers ? "1" : "0")); long num = (long)0; foreach (Triangle value in mesh.triangles.Values) { otri.triangle = value; otri.orient = 0; while (otri.orient < 3) { otri.Sym(ref otri1); if (otri.triangle.id < otri1.triangle.id || otri1.triangle == Mesh.dummytri) { Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); if (!behavior.UseBoundaryMarkers) { streamWriter.WriteLine("{0} {1} {2}", num, vertex.id, vertex1.id); } else if (!behavior.useSegments) { StreamWriter streamWriter1 = streamWriter; object[] objArray = new object[] { num, vertex.id, vertex1.id, null }; objArray[3] = (otri1.triangle == Mesh.dummytri ? "1" : "0"); streamWriter1.WriteLine("{0} {1} {2} {3}", objArray); } else { otri.SegPivot(ref osub); if (osub.seg != Mesh.dummysub) { streamWriter.WriteLine("{0} {1} {2} {3}", new object[] { num, vertex.id, vertex1.id, osub.seg.boundary }); } else { streamWriter.WriteLine("{0} {1} {2} {3}", new object[] { num, vertex.id, vertex1.id, 0 }); } } num = num + (long)1; } otri.orient = otri.orient + 1; } } } }
private void BuildCache(Vertex vertex, bool vertices) { cache.Clear(); Otri init = vertex.tri; Otri next = default(Otri); Otri prev = default(Otri); init.Copy(ref next); // Move counter-clockwise around the vertex. while (next.tri.id != TriangleNetMesh.DUMMY) { cache.Add(next); next.Copy(ref prev); next.Onext(); if (next.Equals(init)) { break; } } if (next.tri.id == TriangleNetMesh.DUMMY) { // We reached the boundary. To get all adjacent triangles, start // again at init triangle and now move clockwise. init.Copy(ref next); if (vertices) { // Don't forget to add the vertex lying on the boundary. prev.Lnext(); cache.Add(prev); } next.Oprev(); while (next.tri.id != TriangleNetMesh.DUMMY) { cache.Insert(0, next); next.Oprev(); if (next.Equals(init)) { break; } } } }
public void Enqueue(ref Otri enqtri, double minedge, Vertex enqapex, Vertex enqorg, Vertex enqdest) { BadTriangle badTriangle = new BadTriangle() { poortri = enqtri, key = minedge, triangapex = enqapex, triangorg = enqorg, triangdest = enqdest }; this.Enqueue(badTriangle); }
public void Update(Otri otri) { if (otri.Triangle == null || otri.Triangle.ID < 0) { renderer.SelectTriangle(null, null, null); } else { renderer.SelectTriangle(otri.Triangle, otri.Org(), otri.Dest()); } this.Render(); }
/// <summary> Add a bad triangle to the end of a queue. </summary> /// <param name="enqtri"></param> /// <param name="minedge"></param> /// <param name="apex"></param> /// <param name="org"></param> /// <param name="dest"></param> public void Enqueue(ref Otri enqtri, double minedge, Vertex apex, Vertex org, Vertex dest) { // Allocate space for the bad triangle. BadTriangle newbad = TrianglePool.AllocBadTri(); newbad.poortri = enqtri; newbad.key = minedge; newbad.apex = apex; newbad.org = org; newbad.dest = dest; Enqueue(newbad); }
private SweepLine.SplayNode FrontLocate(SweepLine.SplayNode splayroot, Otri bottommost, Vertex searchvertex, ref Otri searchtri, ref bool farright) { bool i; bottommost.Copy(ref searchtri); splayroot = this.Splay(splayroot, searchvertex, ref searchtri); for (i = false; !i && this.RightOfHyperbola(ref searchtri, searchvertex); i = searchtri.Equal(bottommost)) { searchtri.OnextSelf(); } farright = i; return(splayroot); }
private void TallyFaces() { Otri otri = new Otri() { orient = 0 }; foreach (Triangle value in this.mesh.triangles.Values) { otri.triangle = value; this.TestTriangle(ref otri); } }
/// <summary> /// Add a bad triangle to the end of a queue. /// </summary> /// <param name="enqtri"></param> /// <param name="minedge"></param> /// <param name="enqapex"></param> /// <param name="enqorg"></param> /// <param name="enqdest"></param> public void Enqueue(ref Otri enqtri, double minedge, Vertex enqapex, Vertex enqorg, Vertex enqdest) { // Allocate space for the bad triangle. BadTriangle newbad = new BadTriangle(); newbad.poortri = enqtri; newbad.key = minedge; newbad.triangapex = enqapex; newbad.triangorg = enqorg; newbad.triangdest = enqdest; Enqueue(newbad); }
public int Triangulate(Mesh m) { Otri otri = new Otri(); Otri otri1 = new Otri(); this.mesh = m; this.sortarray = new Vertex[m.invertices]; int num = 0; foreach (Vertex value in m.vertices.Values) { int num1 = num; num = num1 + 1; this.sortarray[num1] = value; } this.VertexSort(0, m.invertices - 1); num = 0; for (int i = 1; i < m.invertices; i++) { if (this.sortarray[num].x != this.sortarray[i].x || this.sortarray[num].y != this.sortarray[i].y) { num++; this.sortarray[num] = this.sortarray[i]; } else { if (Behavior.Verbose) { SimpleLog.Instance.Warning(string.Format("A duplicate vertex appeared and was ignored (ID {0}).", this.sortarray[i].hash), "DivConquer.DivconqDelaunay()"); } this.sortarray[i].type = VertexType.UndeadVertex; Mesh mesh = m; mesh.undeads = mesh.undeads + 1; } } num++; if (this.useDwyer) { int num2 = num >> 1; if (num - num2 >= 2) { if (num2 >= 2) { this.AlternateAxes(0, num2 - 1, 1); } this.AlternateAxes(num2, num - 1, 1); } } this.DivconqRecurse(0, num - 1, 0, ref otri, ref otri1); return(this.RemoveGhosts(ref otri)); }
/// <summary> /// Form a Delaunay triangulation by the divide-and-conquer method. /// </summary> /// <returns></returns> /// <remarks> /// Sorts the vertices, calls a recursive procedure to triangulate them, and /// removes the bounding box, setting boundary markers as appropriate. /// </remarks> public Mesh Triangulate(List <Vertex> points) { this.mesh = TrianglePool.AllocMesh(); this.mesh.TransferNodes(points); Otri hullleft = default(Otri), hullright = default(Otri); int divider; int i, j, n = points.Count; sortarray = points; VertexSort(0, n - 1); // Discard duplicate vertices, which can really mess up the algorithm. i = 0; for (j = 1; j < n; j++) { if ((sortarray[i].X == sortarray[j].X) && (sortarray[i].Y == sortarray[j].Y)) { sortarray[j].type = VertexType.UndeadVertex; mesh.undeads++; } else { i++; sortarray[i] = sortarray[j]; } } i++; if (UseDwyer) { // Re-sort the array of vertices to accommodate alternating cuts. divider = i >> 1; if (i - divider >= 2) { if (divider >= 2) { AlternateAxes(0, divider - 1, 1); } AlternateAxes(divider, i - 1, 1); } } // Form the Delaunay triangulation. DivconqRecurse(0, i - 1, 0, ref hullleft, ref hullright); //DebugWriter.Session.Write(mesh); //DebugWriter.Session.Finish(); this.mesh.hullsize = RemoveGhosts(ref hullleft); return(this.mesh); }
private void ComputeCircumCenters() { Otri otri = new Otri(); double num = 0; double num1 = 0; foreach (Triangle value in this.mesh.triangles.Values) { otri.triangle = value; Point point = Primitives.FindCircumcenter(otri.Org(), otri.Dest(), otri.Apex(), ref num, ref num1); point.id = value.id; this.points[value.id] = point; } }
/// <summary> /// Test every triangle in the mesh for quality measures. /// </summary> private void TallyFaces() { Otri triangleloop = default(Otri); triangleloop.orient = 0; foreach (var tri in mesh.triangles.Values) { triangleloop.triangle = tri; // If the triangle is bad, enqueue it. TestTriangle(ref triangleloop); } }
public void TestApex() { Otri t = default; t.tri = Helper.CreateTriangle(0, vertices[1], vertices[4], vertices[3]); t.orient = 0; Assert.AreEqual(3, t.Apex().ID); t.orient = 1; Assert.AreEqual(1, t.Apex().ID); t.orient = 2; Assert.AreEqual(4, t.Apex().ID); }
private void InfectHull() { Otri otri = new Otri(); Otri otri1 = new Otri(); Otri otri2 = new Otri(); Osub osub = new Osub(); otri.triangle = Mesh.dummytri; otri.orient = 0; otri.SymSelf(); otri.Copy(ref otri2); do { if (!otri.IsInfected()) { otri.SegPivot(ref osub); if (osub.seg == Mesh.dummysub) { if (!otri.IsInfected()) { otri.Infect(); this.viri.Add(otri.triangle); } } else if (osub.seg.boundary == 0) { osub.seg.boundary = 1; Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); if (vertex.mark == 0) { vertex.mark = 1; } if (vertex1.mark == 0) { vertex1.mark = 1; } } } otri.LnextSelf(); otri.Oprev(ref otri1); while (otri1.triangle != Mesh.dummytri) { otri1.Copy(ref otri); otri.Oprev(ref otri1); } }while (!otri.Equal(otri2)); }
private SweepLine.SplayNode CircleTopInsert(SweepLine.SplayNode splayroot, Otri newkey, Vertex pa, Vertex pb, Vertex pc, double topy) { Point point = new Point(); Otri otri = new Otri(); double num = Primitives.CounterClockwise(pa, pb, pc); double num1 = pa.x - pc.x; double num2 = pa.y - pc.y; double num3 = pb.x - pc.x; double num4 = pb.y - pc.y; double num5 = num1 * num1 + num2 * num2; double num6 = num3 * num3 + num4 * num4; point.x = pc.x - (num2 * num6 - num4 * num5) / (2 * num); point.y = topy; return(this.SplayInsert(this.Splay(splayroot, point, ref otri), newkey, point)); }
private void dotri(SCENE scene, Otri tri, vec2[] pts, vec2 phantom, int i, float w, vec4 col) { float rot = angle(phantom, pts[2]) + PI; vec2 pos = pts[2]; vec2 size = v2(distance(phantom, pts[2]), distance(phantom, pts[i])); float d = angle(pts[i], phantom) - angle(pts[2], phantom); if (d > PI || (d < 0 && d > -PI)) { pos = pts[i]; rot -= PI2; size = v2(size.y, size.x); } tri.update(scene.time, col, rot, v4(pos.x, pos.y, 1f, w), size); tri.draw(scene.g); }
/// <summary> /// Apply given action to each triangle of selected region. /// </summary> /// <param name="action"></param> /// <param name="protector"></param> void ProcessRegion(Action <Triangle> action, Func <SubSegment, bool> protector) { Otri testtri = default(Otri); Otri neighbor = default(Otri); Osub neighborsubseg = default(Osub); // Loop through all the infected triangles, spreading the attribute // and/or area constraint to their neighbors, then to their neighbors' // neighbors. for (int i = 0; i < region.Count; i++) { // WARNING: Don't use foreach, viri list gets modified. testtri.tri = region[i]; // Apply function. action(testtri.tri); // Check each of the triangle's three neighbors. for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { // Find the neighbor. testtri.Sym(ref neighbor); // Check for a subsegment between the triangle and its neighbor. testtri.Pivot(ref neighborsubseg); // Make sure the neighbor exists, is not already infected, and // isn't protected by a subsegment. if ((neighbor.tri.id != TriangleNetMesh.DUMMY) && !neighbor.IsInfected() && protector(neighborsubseg.seg)) { // Infect the neighbor. neighbor.Infect(); // Ensure that the neighbor's neighbors will be infected. region.Add(neighbor.tri); } } } // Uninfect all triangles. foreach (var virus in region) { virus.infected = false; } // Empty the virus pool. region.Clear(); }
/// <summary> /// Reconstruct a triangulation from its raw data representation. /// </summary> public static Mesh ToMesh(Polygon polygon, ITriangle[] triangles) { Otri tri = default(Otri); Osub subseg = default(Osub); int i = 0; int elements = triangles == null ? 0 : triangles.Length; int segments = polygon.Segments.Count; // TODO: Configuration should be a function argument. var mesh = new Mesh(new Configuration()); mesh.TransferNodes(polygon.Points); mesh.regions.AddRange(polygon.Regions); mesh.behavior.useRegions = polygon.Regions.Count > 0; if (polygon.Segments.Count > 0) { mesh.behavior.Poly = true; mesh.holes.AddRange(polygon.Holes); } // Create the triangles. for (i = 0; i < elements; i++) { mesh.MakeTriangle(ref tri); } if (mesh.behavior.Poly) { mesh.insegments = segments; // Create the subsegments. for (i = 0; i < segments; i++) { mesh.MakeSegment(ref subseg); } } var vertexarray = SetNeighbors(mesh, triangles); SetSegments(mesh, polygon, vertexarray); return(mesh); }
private void ComputeCircumCenters() { Otri tri = default(Otri); float xi = 0, eta = 0; Point pt; // Compue triangle circumcenters foreach (var item in _TriangleNetMesh.triangles) { tri.tri = item; pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta); pt.id = item.id; points[item.id] = pt; } }
SplayNode FrontLocate(SplayNode splayroot, Otri bottommost, Vertex searchvertex, ref Otri searchtri, ref bool farright) { bool farrightflag; bottommost.Copy(ref searchtri); splayroot = Splay(splayroot, searchvertex, ref searchtri); farrightflag = false; while (!farrightflag && RightOfHyperbola(ref searchtri, searchvertex)) { searchtri.Onext(); farrightflag = searchtri.Equals(bottommost); } farright = farrightflag; return(splayroot); }
void Check4DeadEvent(ref Otri checktri, SweepEvent[] eventheap, ref int heapsize) { SweepEvent deadevent; SweepEventVertex eventvertex; int eventnum = -1; eventvertex = checktri.Org() as SweepEventVertex; if (eventvertex != null) { deadevent = eventvertex.evt; eventnum = deadevent.heapposition; HeapDelete(eventheap, heapsize, eventnum); heapsize--; checktri.SetOrg(null); } }
private void ComputeCircumCenters() { Otri tri = default(Otri); double xi = 0, eta = 0; Point pt; // Compue triangle circumcenters foreach (var item in mesh.triangles.Values) { tri.triangle = item; pt = Primitives.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta); pt.id = item.id; points[item.id] = pt; } }
public bool CheckDelaunay() { Otri otri = new Otri(); Otri otri1 = new Otri(); Osub osub = new Osub(); bool noExact = Behavior.NoExact; Behavior.NoExact = false; int num = 0; foreach (Triangle value in this.mesh.triangles.Values) { otri.triangle = value; otri.orient = 0; while (otri.orient < 3) { Vertex vertex = otri.Org(); Vertex vertex1 = otri.Dest(); Vertex vertex2 = otri.Apex(); otri.Sym(ref otri1); Vertex vertex3 = otri1.Apex(); bool flag = (otri1.triangle == Mesh.dummytri || Otri.IsDead(otri1.triangle) || otri.triangle.id >= otri1.triangle.id || !(vertex != this.mesh.infvertex1) || !(vertex != this.mesh.infvertex2) || !(vertex != this.mesh.infvertex3) || !(vertex1 != this.mesh.infvertex1) || !(vertex1 != this.mesh.infvertex2) || !(vertex1 != this.mesh.infvertex3) || !(vertex2 != this.mesh.infvertex1) || !(vertex2 != this.mesh.infvertex2) || !(vertex2 != this.mesh.infvertex3) || !(vertex3 != this.mesh.infvertex1) || !(vertex3 != this.mesh.infvertex2) ? false : vertex3 != this.mesh.infvertex3); if (this.mesh.checksegments & flag) { otri.SegPivot(ref osub); if (osub.seg != Mesh.dummysub) { flag = false; } } if (flag && Primitives.NonRegular(vertex, vertex1, vertex2, vertex3) > 0) { this.logger.Warning(string.Format("Non-regular pair of triangles found (IDs {0}/{1}).", otri.triangle.id, otri1.triangle.id), "Quality.CheckDelaunay()"); num++; } otri.orient = otri.orient + 1; } } if (num == 0) { this.logger.Info("Mesh is Delaunay."); } Behavior.NoExact = noExact; return(num == 0); }
private void ComputeCircumCenters() { Otri otri = new Otri(); double num = 0; double num1 = 0; foreach (Triangle value in this.mesh.triangles.Values) { otri.triangle = value; Point point = Primitives.FindCircumcenter(otri.Org(), otri.Dest(), otri.Apex(), ref num, ref num1); point.id = value.id; this.points[value.id] = point; this.bounds.Update(point.x, point.y); } double num2 = Math.Max(this.bounds.Width, this.bounds.Height); this.bounds.Scale(num2, num2); }
/// <summary> /// Enumerate all edges of the given mesh. /// </summary> /// <param name="mesh"></param> /// <param name="skipSegments"></param> /// <returns></returns> /// <remarks> /// In contrast to <see cref="EnumerateEdges(IMesh)"/> this method will return /// objects that include the vertex information (and not only the indices). /// </remarks> public static IEnumerable <ISegment> EnumerateEdges(IMesh mesh, bool skipSegments = true) { Otri tri = default; Otri neighbor = default; Osub sub = default; Vertex p1, p2; bool segments = !skipSegments; foreach (var t in mesh.Triangles) { tri.tri = t; tri.orient = 0; for (int i = 0; i < 3; i++) { tri.Sym(ref neighbor); int nid = neighbor.tri.id; if ((tri.tri.id < nid) || (nid == Mesh.DUMMY)) { p1 = tri.Org(); p2 = tri.Dest(); tri.Pivot(ref sub); if (sub.seg.hash == Mesh.DUMMY) { yield return(new Segment(p1, p2)); } else if (segments) { // Segments might be processed separately, so only // include them if requested. yield return(sub.seg); } } tri.orient++; } } }
/// <summary> /// Write the triangles to an .ele file. /// </summary> /// <param name="mesh"></param> /// <param name="filename"></param> public void WriteElements(Mesh mesh, string filename) { Otri tri = default(Otri); Vertex p1, p2, p3; bool regions = mesh.behavior.useRegions; int j = 0; tri.orient = 0; using ( #if NETFX_CORE var writer = new WinRTLegacy.IO.StreamWriter(filename) #else var writer = new StreamWriter(filename) #endif ) { // Number of triangles, vertices per triangle, attributes per triangle. writer.WriteLine("{0} 3 {1}", mesh.triangles.Count, regions ? 1 : 0); foreach (var item in mesh.triangles) { tri.tri = item; p1 = tri.Org(); p2 = tri.Dest(); p3 = tri.Apex(); // Triangle number, indices for three vertices. writer.Write("{0} {1} {2} {3}", j, p1.id, p2.id, p3.id); if (regions) { writer.Write(" {0}", tri.tri.label); } writer.WriteLine(); // Number elements item.id = j++; } } }
public Ztestcube3(int start, int stop) { this.start = start; this.stop = stop; framedelta = 100; points = new vec3[] { v3(-10f, -10f, 90f), v3(-10f, -10f, 110f), v3(-10f, 10f, 90f), v3(-10f, 10f, 110f), v3(10f, -10f, 90f), v3(10f, -10f, 110f), v3(10f, 10f, 90f), v3(10f, 10f, 110f), }; _points = new vec3[points.Length]; cube = new Cube( Color.Cyan, Color.Lime, Color.Red, Color.Blue, Color.Yellow, Color.Orange, _points, PA, PD, PC, PB, PF, PE, PH, PG ); tris = new Otri[24]; for (int i = 0; i < tris.Length; i++) { tris[i] = new Otri(); } }