/// <summary> /// Apply the transform to the ORIGINAL mesh that was created. Multiple /// calls are NOT cumulative. /// </summary> /// <param name="transform"></param> public override void ApplyTransform(Matrix4x4 transform) { { var list = new TriangleWithNormals[_OriginalTriangleVerticies.Count]; list.Length .ParallelChunked((lower, upper) => { for (int i1 = lower; i1 < upper; i1++) { list[i1] = _OriginalTriangleVerticies[i1].ApplyTransform(transform); } }); TrianglesWithNormals = list; } { var list = new Edge3[_OriginalEdgeVertices.Count]; list.Length .ParallelChunked((lower, upper) => { for (int i = lower; i < upper; i++) { list[i] = _OriginalEdgeVertices[i].ApplyTransform(transform); } }); Edges = list; } UpdateBoundingSphere(); }
public static void InitRaw2Sym() { Edge3 e = new Edge3(); sbyte[] occ = new sbyte[11880 / 8]; int count = 0; for (int i = 0; i < 11880; i++) { if ((occ[i >> 3] & (1 << (i & 7))) == 0) //> { e.Set(i * factX[8]); for (int j = 0; j < 8; j++) { int idx = e.Get(4); if (idx == i) { symstate[count] |= (char)(1 << j); } occ[idx >> 3] |= (sbyte)(1 << (idx & 7)); raw2sym[idx] = count << 3 | syminv[j]; e.Rot(0); if (j % 2 == 1) { e.Rot(1); e.Rot(2); } } sym2raw[count++] = i; } } //assert count == 1538; }
private static IEnumerable <Edge3> GetPolygonEdges(IEnumerable <Vector3> vectors) { var edges = new List <Edge3>(); var vectorsEnumerator = vectors.GetEnumerator(); vectorsEnumerator.MoveNext(); var firstPoint = vectorsEnumerator.Current; var point1 = firstPoint; var point2 = firstPoint; while (vectorsEnumerator.MoveNext()) { point1 = point2; point2 = vectorsEnumerator.Current; var edge = new Edge3(point1, point2); edges.Add(edge); } var lastEdge = new Edge3(point2, firstPoint); edges.Add(lastEdge); return(edges); }
public void TrimLineShouldWork (float[] p0 , float[] p1) { CreatePartDoc(modelDoc => { var v0 = new Vector3(p0[0], p0[1], p0[2]); var v1 = new Vector3(p1[0], p1[1], p1[2]); var edge = new Edge3(v0, v1); var limitedLine = Modeler.CreateTrimmedLine(edge); var tol = 1e-9f; var startPoint = limitedLine.StartPoint(0)[0]; var endPoint = limitedLine.EndPoint(0)[0]; v0.X.Should().BeApproximately(startPoint.X, tol); v0.Y.Should().BeApproximately(startPoint.Y, tol); v0.Z.Should().BeApproximately(startPoint.Z, tol); v1.X.Should().BeApproximately(endPoint.X, tol); v1.Y.Should().BeApproximately(endPoint.Y, tol); v1.Z.Should().BeApproximately(endPoint.Z, tol); //###################################################### var wbv = limitedLine.CreateWireBody(); var d = wbv.DisplayUndoable(modelDoc, Color.Blue); return(d); }); }
public Model(Mesh source) { vertices = new List <Vertex>(); edges = new List <Edge3>(); this.triangles = new List <Triangle3>(); Vector3[] points = source.vertices; for (int i = 0, n = points.Length; i < n; i++) { Vertex v = new Vertex(points[i], i); vertices.Add(v); } int[] triangles = source.triangles; for (int i = 0, n = triangles.Length; i < n; i += 3) { int i0 = triangles[i], i1 = triangles[i + 1], i2 = triangles[i + 2]; Vertex v0 = vertices[i0], v1 = vertices[i1], v2 = vertices[i2]; Edge3 e0 = GetEdge(edges, v0, v1); Edge3 e1 = GetEdge(edges, v1, v2); Edge3 e2 = GetEdge(edges, v2, v0); Triangle3 f = new Triangle3(v0, v1, v2, e0, e1, e2); this.triangles.Add(f); v0.AddTriangle(f); v1.AddTriangle(f); v2.AddTriangle(f); e0.AddTriangle(f); e1.AddTriangle(f); e2.AddTriangle(f); } }
public static Gen <Triangle> Triangle(this Gen <double> floats) { return(floats .Vector3() .Triangle() .Where (v => { var e0 = new Edge3(v.A, v.B); var e1 = new Edge3(v.A, v.C); var e2 = new Edge3(v.B, v.C); var minDist = 1e-2; var maxDist = 1; if (List(e0, e1, e2).Any(e => e.Length < minDist)) { return false; } if (List(e0, e1, e2).XPaired().Any(t => t.Map((a, b) => a.AngleBetween(b) < 5d / 180 * Math.PI))) { return false; } return true; })); }
public void Set(Edge3 e) { for (int i = 0; i < 12; i++) { edge[i] = e.edge[i]; edgeo[i] = e.edgeo[i]; } isStd = e.isStd; }
public void NonIntersectingLinesShouldReturnNone_v2(Vector3 position, Vector3 forward, Vector3 up) { var line = new Edge3(new Vector3(-1, -2, -3) * 0.001, new Vector3(-1, -2, -3)); var plane = new PointDirection3(Vector3.Zero, Vector3.UnitX); var transform = Matrix4x4.CreateWorld(position, forward, up); var tline = line.ApplyTransform(transform); var tplane = plane.ApplyTransform(transform); tline.IntersectPlane(tplane).IsSome.Should().BeFalse(); }
private static Edge3[] TransformEdges(MeshData data, Matrix4x4 transform) { var list = new Edge3[data.Edges.Count]; list.Length .ParallelChunked((lower, upper) => { for (int i = lower; i < upper; i++) { list[i] = data.Edges[i].ApplyTransform(transform); } }); return(list); }
public void IntersectingLineWithPlaneShouldReturnIntersectionPoint(Vector3 position, Vector3 forward, Vector3 up) { var line = new Edge3(new Vector3(1, 2, 3), new Vector3(-1, -2, -3)); var plane = new PointDirection3(Vector3.Zero, Vector3.UnitX); var transform = Matrix4x4.CreateWorld(position, forward, up); var tline = line.ApplyTransform(transform); var tplane = plane.ApplyTransform(transform); var intersectPlane = tline.IntersectPlane(tplane).__Value__(); var intersectPlaneExpectedValue = Vector3.Transform(Vector3.Zero, transform); intersectPlane.Should().BeApproximately(intersectPlaneExpectedValue, 1e-6); }
public void ShouldGetTheShortestLine() { var edge0 = new Edge3(vector(0, -1, -1), vector(0, 1, -1)); var edge1 = new Edge3(vector(-1, 0, 1), vector(1, 0, 1)); var connect = edge0.ShortestEdgeJoining(edge1); connect.A.X.Should().BeApproximately(0, 1e-6f); connect.A.Y.Should().BeApproximately(0, 1e-6f); connect.A.Z.Should().BeApproximately(-1, 1e-6f); connect.B.X.Should().BeApproximately(0, 1e-6f); connect.B.Y.Should().BeApproximately(0, 1e-6f); connect.B.Z.Should().BeApproximately(1, 1e-6f); }
/// <summary> /// Create a line from p0 to p1. /// </summary> /// <param name="modeler"></param> /// <param name="edge"></param> /// <returns></returns> public static ICurve CreateTrimmedLine(this IModeler modeler, Edge3 edge) { Debug.Assert(edge.Delta.Length() > double.Epsilon); var startPoint = new[] { (double)edge.A.X, (double)edge.A.Y, (double)edge.A.Z }; var dir = edge.Delta.Unit(); var direction = new[] { (double)dir.X, (double)dir.Y, (double)dir.Z }; var line = (ICurve)modeler.CreateLine(startPoint, direction); Debug.Assert(line != null, "line != null"); var pp0 = line.GetClosestPointOn(edge.A.X, edge.A.Y, edge.A.Z).CastArray <double>(); var pp1 = line.GetClosestPointOn(edge.B.X, edge.B.Y, edge.B.Z).CastArray <double>(); //line = line.CreateTrimmedCurve2(pp0[0], pp0[1], pp0[2], pp1[0], pp1[1], pp1[2]); var trimmedLine = line.CreateTrimmedCurve(pp0[3], pp1[3]); Debug.Assert(trimmedLine != null, "line != null"); return(trimmedLine); }
Edge3 GetEdge(List <Edge3> edges, Vertex v0, Vertex v1) { Edge3 match = v0.edges.Find(e => { return(e.Has(v1)); }); if (match != null) { return(match); } Edge3 ne = new Edge3(v0, v1); v0.AddEdge(ne); v1.AddEdge(ne); edges.Add(ne); return(ne); }
public static int Getprun(int edge) { Edge3 e = new Edge3(); int depth = 0; int depm3 = GetPruning(eprun, edge); if (depm3 == 0x3) { return(MAX_DEPTH); } while (edge != 0) { if (depm3 == 0) { depm3 = 2; } else { depm3--; } int symcord1 = edge / N_RAW; int cord1 = sym2raw[symcord1]; int cord2 = edge % N_RAW; e.Set(cord1 * N_RAW + cord2); for (int m = 0; m < 17; m++) { int cord1x = Getmvrot(e.edge, m << 3, 4); int symcord1x = raw2sym[cord1x]; int symx = symcord1x & 0x7; symcord1x >>= 3; int cord2x = Getmvrot(e.edge, m << 3 | symx, 10) % N_RAW; int idx = symcord1x * N_RAW + cord2x; if (GetPruning(eprun, idx) == depm3) { depth++; edge = idx; break; } } } return(depth); }
Edge3 GetEdge(Vertex v0, Vertex v1) { Edge3 match = v0.edges.Find(e => { return(e.a == v1 || e.b == v1); }); if (match != null) { return(match); } Edge3 ne = new Edge3(v0, v1); edges.Add(ne); v0.AddEdge(ne); v1.AddEdge(ne); return(ne); }
public void DrawPolygonFilled(ITransformation transformation, IEnumerable <Vector3> vectors, ushort?colorOverride = null, byte?pixelOverride = null) { var transformedVectors = vectors .Select(transformation.Transform) .ToList(); var yMax = (int)System.Math.Ceiling(transformedVectors.Max(vector => vector.Y)); var yMin = (int)System.Math.Floor(transformedVectors.Min(vector => vector.Y)); var edges = GetPolygonEdges(transformedVectors); for (var scanlineY = yMin; scanlineY <= yMax; scanlineY++) { var yEdgeIntersections = edges .Where(edge => edge.IsYValueOnEdge(scanlineY)) .Select(edge => edge.GetPointFromY(scanlineY)) .ToList(); var xSortedIntersections = yEdgeIntersections.OrderBy(vector => vector.X).ToList(); var index1 = 0; var index2 = 1; while (index2 < xSortedIntersections.Count) { var point1 = xSortedIntersections[index1]; var point2 = xSortedIntersections[index2]; var line = new Edge3(point1, point2); var startX = point1.X; var endX = point2.X; for (var x = startX; x <= endX; x++) { // Z ALGO IS BUGGY var point = new Vector3(x, scanlineY, line.GetPointFromX(x).Z); //var point = new Vector2(x, scanlineY); DrawPoint(Transformation.None, point, colorOverride, pixelOverride); } index1 += 2; index2 += 2; } } }
public static void InitMvrot() { Edge3 e = new Edge3(); for (int m = 0; m < 20; m++) { for (int r = 0; r < 8; r++) { e.Set(0); e.Move(m); e.Rotate(r); for (int i = 0; i < 12; i++) { mvrot[m << 3 | r, i] = e.edge[i]; } e.Std(); for (int i = 0; i < 12; i++) { mvroto[m << 3 | r, i] = e.temp[i]; } } } }
public void LinspaceForVector3ShouldWork() { CreatePartDoc(modelDoc => { var v0 = new Vector3(0, 0, 0); var v1 = new Vector3(1, 0, 0); var intV = Sequences.LinSpace(v0, v1, 5).ToList(); var interpolated = intV .Buffer(2, 1) .Where(p => p.Count == 2) .Select(ps => { var edge = new Edge3(ps[0], ps[1]); var limitedLine = Modeler.CreateTrimmedLine(edge); var wB = limitedLine.CreateWireBody(); return(wB.DisplayUndoable(modelDoc, Color.Blue)); }) .ToCompositeDisposable(); return(new CompositeDisposable(interpolated)); }); }
public Vertex GetEdgePoint(Edge3 e) { if (e.ept != null) { return(e.ept); } if (e.faces.Count != 2) { // boundary case for edge Vector3 m = (e.a.p + e.b.p) * 0.5f; e.ept = new Vertex(m, e.a.index); } else { const float alpha = 3f / 8f; const float beta = 1f / 8f; var left = e.faces[0].GetOtherVertex(e); var right = e.faces[1].GetOtherVertex(e); e.ept = new Vertex((e.a.p + e.b.p) * alpha + (left.p + right.p) * beta, e.a.index); } return(e.ept); }
public void AddTriangle(Vertex v0, Vertex v1, Vertex v2) { if (!vertices.Contains(v0)) { vertices.Add(v0); } if (!vertices.Contains(v1)) { vertices.Add(v1); } if (!vertices.Contains(v2)) { vertices.Add(v2); } Edge3 e0 = GetEdge(v0, v1); Edge3 e1 = GetEdge(v1, v2); Edge3 e2 = GetEdge(v2, v0); Triangle3 f = new Triangle3(v0, v1, v2, e0, e1, e2); this.triangles.Add(f); v0.AddTriangle(f); v1.AddTriangle(f); v2.AddTriangle(f); e0.AddTriangle(f); e1.AddTriangle(f); e2.AddTriangle(f); }
private static IEnumerable<Edge3> GetPolygonEdges(IEnumerable<Vector3> vectors) { var edges = new List<Edge3>(); var vectorsEnumerator = vectors.GetEnumerator(); vectorsEnumerator.MoveNext(); var firstPoint = vectorsEnumerator.Current; var point1 = firstPoint; var point2 = firstPoint; while (vectorsEnumerator.MoveNext()) { point1 = point2; point2 = vectorsEnumerator.Current; var edge = new Edge3(point1, point2); edges.Add(edge); } var lastEdge = new Edge3(point2, firstPoint); edges.Add(lastEdge); return edges; }
public void DrawPolygonFilled(ITransformation transformation, IEnumerable<Vector3> vectors, ushort? colorOverride = null, byte? pixelOverride = null) { var transformedVectors = vectors .Select(transformation.Transform) .ToList(); var yMax = (int) System.Math.Ceiling(transformedVectors.Max(vector => vector.Y)); var yMin = (int) System.Math.Floor(transformedVectors.Min(vector => vector.Y)); var edges = GetPolygonEdges(transformedVectors); for (var scanlineY = yMin; scanlineY <= yMax; scanlineY++) { var yEdgeIntersections = edges .Where(edge => edge.IsYValueOnEdge(scanlineY)) .Select(edge => edge.GetPointFromY(scanlineY)) .ToList(); var xSortedIntersections = yEdgeIntersections.OrderBy(vector => vector.X).ToList(); var index1 = 0; var index2 = 1; while (index2 < xSortedIntersections.Count) { var point1 = xSortedIntersections[index1]; var point2 = xSortedIntersections[index2]; var line = new Edge3(point1, point2); var startX = point1.X; var endX = point2.X; for (var x = startX; x <= endX; x++) { // Z ALGO IS BUGGY var point = new Vector3(x, scanlineY, line.GetPointFromX(x).Z); //var point = new Vector2(x, scanlineY); DrawPoint(Transformation.None, point, colorOverride, pixelOverride); } index1 += 2; index2 += 2; } } }
public EdgeDistance(Edge3 edge, double distance) { Edge = edge; Distance = distance; }
public EdgeDistance(Edge3 edge, double distance) { this.Edge = edge; this.Distance = distance; }
public override double GetInitializationStatus() => Edge3.InitStatus();
public static void CreatePrun() { Edge3 e = new Edge3(); Edge3 f = new Edge3(); Edge3 g = new Edge3(); for (int i = 0; i < eprun.Length; i++) { eprun[i] = -1; } int depth = 0; done = 1; SetPruning(eprun, 0, 0); while (done != N_EPRUN) { bool inv = depth > 9; int depm3 = depth % 3; int dep1m3 = (depth + 1) % 3; int find = inv ? 0x3 : depm3; int chk = inv ? depm3 : 0x3; if (depth >= MAX_DEPTH - 1) { break; } for (int i_ = 0; i_ < N_EPRUN; i_ += 16) { int val = eprun[i_ >> 4]; if (!inv && val == -1) { continue; } for (int i = i_, end = i_ + 16; i < end; i++, val >>= 2) { if ((val & 0x3) != find) { continue; } int symcord1 = i / N_RAW; int cord1 = sym2raw[symcord1]; int cord2 = i % N_RAW; e.Set(cord1 * N_RAW + cord2); for (int m = 0; m < 17; m++) { int cord1x = Getmvrot(e.edge, m << 3, 4); int symcord1x = raw2sym[cord1x]; int symx = symcord1x & 0x7; symcord1x >>= 3; int cord2x = Getmvrot(e.edge, m << 3 | symx, 10) % N_RAW; int idx = symcord1x * N_RAW + cord2x; if (GetPruning(eprun, idx) != chk) { continue; } SetPruning(eprun, inv ? i : idx, dep1m3); done++; // if ((done & 0x3ffff) == 0) { // System.out.print(string.format("%d\r", done)); // } if (inv) { break; } char symState = symstate[symcord1x]; if (symState == 1) { continue; } f.Set(e); f.Move(m); f.Rotate(symx); for (int j = 1; (symState >>= 1) != 0; j++) { if ((symState & 1) != 1) { continue; } g.Set(f); g.Rotate(j); int idxx = symcord1x * N_RAW + g.Get(10) % N_RAW; if (GetPruning(eprun, idxx) == chk) { SetPruning(eprun, idxx, dep1m3); done++; // if ((done & 0x3ffff) == 0) { // System.out.print(string.format("%d\r", done)); // } } } } } } depth++; //System.out.println(depth + "\t" + done); } }
private IEnumerator QEMLoop(HalfEdgeData3 halfEdgeMeshData, Heap <QEM_Edge> sorted_QEM_edges, Dictionary <MyVector3, Matrix4x4> qMatrices, Dictionary <HalfEdge3, QEM_Edge> halfEdge_QEM_Lookup, int maxEdgesToContract, float maxError, bool normalizeTriangles = false) { //PAUSE FOR VISUALIZATION //Display what we have so far controller.DisplayMeshMain(halfEdgeMeshData.faces); controller.displayStuffUI.text = "Triangles: " + halfEdgeMeshData.faces.Count.ToString(); yield return(new WaitForSeconds(5f)); // // Start contracting edges // //For each edge we want to remove for (int i = 0; i < maxEdgesToContract; i++) { //Check that we can simplify the mesh //The smallest mesh we can have is a tetrahedron with 4 faces, itherwise we get a flat triangle if (halfEdgeMeshData.faces.Count <= 4) { Debug.Log($"Cant contract more than {i} edges"); break; } // // Remove the pair (v1,v2) of the least cost and contract the pair // //timer.Start(); QEM_Edge smallestErrorEdge = sorted_QEM_edges.RemoveFirst(); //This means an edge in this face has already been contracted //We are never removing edges from the heap after contracting and edges, //so we do it this way for now, which is maybe better? if (smallestErrorEdge.halfEdge.face == null) { //This edge wasn't contracted so don't add it to iteration i -= 1; continue; } if (smallestErrorEdge.qem > maxError) { Debug.Log($"Cant contract more than {i} edges because reached max error"); break; } //timer.Stop(); //timer.Start(); //Get the half-edge we want to contract HalfEdge3 edgeToContract = smallestErrorEdge.halfEdge; //Need to save the endpoints so we can remove the old Q matrices from the pos-matrix lookup table Edge3 contractedEdgeEndpoints = new Edge3(edgeToContract.prevEdge.v.position, edgeToContract.v.position); //Contract edge HashSet <HalfEdge3> edgesPointingToNewVertex = halfEdgeMeshData.ContractTriangleHalfEdge(edgeToContract, smallestErrorEdge.mergePosition); //timer.Stop(); // // Remove all QEM_edges that belonged to the faces we contracted // //This is not needed if we check if an edge in the triangle has already been contracted /* * //timer.Start(); * * //This edge doesnt exist anymore, so remove it from the lookup * halfEdge_QEM_Lookup.Remove(edgeToContract); * * //Remove the two edges that were a part of the triangle of the edge we contracted * RemoveHalfEdgeFromQEMEdges(edgeToContract.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * RemoveHalfEdgeFromQEMEdges(edgeToContract.nextEdge.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * * //Remove the three edges belonging to the triangle on the opposite side of the edge we contracted * //If there was an opposite side... * if (edgeToContract.oppositeEdge != null) * { * HalfEdge3 oppositeEdge = edgeToContract.oppositeEdge; * * RemoveHalfEdgeFromQEMEdges(oppositeEdge, QEM_edges, halfEdge_QEM_Lookup); * RemoveHalfEdgeFromQEMEdges(oppositeEdge.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * RemoveHalfEdgeFromQEMEdges(oppositeEdge.nextEdge.nextEdge, QEM_edges, halfEdge_QEM_Lookup); * } * //timer.Stop(); */ //Remove the edges start and end vertices from the pos-matrix lookup table qMatrices.Remove(contractedEdgeEndpoints.p1); qMatrices.Remove(contractedEdgeEndpoints.p2); //timer.Stop(); // // Update all QEM_edges that is now connected with the new contracted vertex because their errors have changed // //The contracted position has a new Q matrix Matrix4x4 QNew = MeshSimplification_QEM.CalculateQMatrix(edgesPointingToNewVertex, normalizeTriangles); //Add the Q matrix to the pos-matrix lookup table qMatrices.Add(smallestErrorEdge.mergePosition, QNew); //Update the error of the QEM_edges of the edges that pointed to and from one of the two old Q matrices //Those edges are the same edges that points to the new vertex and goes from the new vertex //timer.Start(); foreach (HalfEdge3 edgeToV in edgesPointingToNewVertex) { //The edge going from the new vertex is the next edge of the edge going to the vertex HalfEdge3 edgeFromV = edgeToV.nextEdge; //To QEM_Edge QEM_edgeToV = halfEdge_QEM_Lookup[edgeToV]; Edge3 edgeToV_endPoints = QEM_edgeToV.GetEdgeEndPoints(); Matrix4x4 Q1_edgeToV = qMatrices[edgeToV_endPoints.p1]; Matrix4x4 Q2_edgeToV = QNew; QEM_edgeToV.UpdateEdge(edgeToV, Q1_edgeToV, Q2_edgeToV); sorted_QEM_edges.UpdateItem(QEM_edgeToV); //From QEM_Edge QEM_edgeFromV = halfEdge_QEM_Lookup[edgeFromV]; Edge3 edgeFromV_endPoints = QEM_edgeFromV.GetEdgeEndPoints(); Matrix4x4 Q1_edgeFromV = QNew; Matrix4x4 Q2_edgeFromV = qMatrices[edgeFromV_endPoints.p2]; QEM_edgeFromV.UpdateEdge(edgeFromV, Q1_edgeFromV, Q2_edgeFromV); sorted_QEM_edges.UpdateItem(QEM_edgeFromV); } //timer.Stop(); //PAUSE FOR VISUALIZATION //Display what we have so far controller.DisplayMeshMain(halfEdgeMeshData.faces); controller.displayStuffUI.text = "Triangles: " + halfEdgeMeshData.faces.Count.ToString(); yield return(new WaitForSeconds(0.02f)); } }
public static Matrix4x4 CreateFromEdgeAngleOrigin(Edge3 p, float angle) { return(CreateFromAxisAngleOrigin(new PointDirection3(p.A, p.Delta), angle)); }
public override double GetInitializationStatus() { return(Edge3.InitStatus()); }