public void LaplaceRelaxation(TopoModel model) { double n = 2; RHVector3 newPos = new RHVector3(pos); newPos.AddInternal(pos); foreach (TopoTriangle t in connectedFacesList) { int idx = t.VertexIndexFor(this); n += 2; newPos.AddInternal(t.vertices[(idx + 1) % 3].pos); newPos.AddInternal(t.vertices[(idx + 2) % 3].pos); } newPos.Scale(1.0 / n); // validate newPos does not create intersecting triangles or bad shapes foreach (TopoTriangle t in connectedFacesList) { int idx = t.VertexIndexFor(this); RHVector3 d1 = t.vertices[(idx+1)%3].pos.Subtract(newPos); RHVector3 d2 = t.vertices[(idx+2)%3].pos.Subtract(t.vertices[(idx+1)%3].pos); RHVector3 normal = d1.CrossProduct(d2); if (normal.ScalarProduct(t.normal) < 0) return; double angle = t.AngleEdgePoint((idx + 1) % 3, newPos); if(angle < 0.088 || angle > 2.96) return; // Angle gets to small } model.vertices.ChangeCoordinates(this, newPos); }
public void LaplaceRelaxation(TopoModel model) { double n = 2; RHVector3 newPos = new RHVector3(pos); newPos.AddInternal(pos); foreach (TopoTriangle t in connectedFacesList) { int idx = t.VertexIndexFor(this); n += 2; newPos.AddInternal(t.vertices[(idx + 1) % 3].pos); newPos.AddInternal(t.vertices[(idx + 2) % 3].pos); } newPos.Scale(1.0 / n); // validate newPos does not create intersecting triangles or bad shapes foreach (TopoTriangle t in connectedFacesList) { int idx = t.VertexIndexFor(this); RHVector3 d1 = t.vertices[(idx + 1) % 3].pos.Subtract(newPos); RHVector3 d2 = t.vertices[(idx + 2) % 3].pos.Subtract(t.vertices[(idx + 1) % 3].pos); RHVector3 normal = d1.CrossProduct(d2); if (normal.ScalarProduct(t.normal) < 0) { return; } double angle = t.AngleEdgePoint((idx + 1) % 3, newPos); if (angle < 0.088 || angle > 2.96) { return; // Angle gets to small } } model.vertices.ChangeCoordinates(this, newPos); }
public void FlipDirection() { normal.Scale(-1); TopoVertex v = vertices[0]; vertices[0] = vertices[1]; vertices[1] = v; TopoEdge e = edges[1]; edges[1] = edges[2]; edges[2] = e; }
public bool SmoothAspectRatio(TopoModel model, double maxRatio) { double maxLen, minLen; int maxIdx, minIdx; LongestShortestEdgeLength(out maxIdx, out maxLen, out minIdx, out minLen); if (minLen == 0) { return(false); } if (maxLen > 1 && maxLen / minLen > maxRatio) { RHVector3 center = edges[maxIdx].v1.pos.Add(edges[maxIdx].v2.pos); center.Scale(0.5); TopoVertex newVertex = new TopoVertex(0, center); model.addVertex(newVertex); edges[maxIdx].InsertVertex(model, newVertex); return(true); } return(false); }
public void importSTL(string filename,double scale=1) { StartAction("L_LOADING..."); clear(); try { FileStream f = File.OpenRead(filename); byte[] header = new byte[80]; ReadArray(f, header); /* if (header[0] == 's' && header[1] == 'o' && header[2] == 'l' && header[3] == 'i' && header[4] == 'd') { f.Close(); LoadText(file); } else {*/ BinaryReader r = new BinaryReader(f); int nTri = r.ReadInt32(); if (f.Length != 84 + nTri * 50) { f.Close(); importSTLAscii(filename,scale); } else { for (int i = 0; i < nTri; i++) { Progress((double)i / nTri); if (i % 2000 == 0) { Application.DoEvents(); if(IsActionStopped()) return; } RHVector3 normal = new RHVector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); RHVector3 p1 = new RHVector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); RHVector3 p2 = new RHVector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); RHVector3 p3 = new RHVector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); p1.Scale(scale); p2.Scale(scale); p3.Scale(scale); normal.NormalizeSafe(); addTriangle(p1, p2, p3, normal); r.ReadUInt16(); } r.Close(); f.Close(); } } catch (Exception e) { MessageBox.Show(e.ToString(), "Error reading STL file", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
public bool import3Ds(string filename,double scale=1) { _3DSLoader loader = new _3DSLoader(); _3DSLoader._3DScene scene = loader.Load(filename); if (scene.GetObjectCount() == 0) return false; foreach (_3DSLoader._3DObject obj in scene.GetObjects()) { for (int index = 0; index < obj.GetFaceCount(); index++) { _3DSLoader._3DFace face = obj.GetFace(index); _3DSLoader._3DVertex vertex; vertex = obj.GetVertex(face.Vertex1); RHVector3 vert1 = new RHVector3(vertex.X, vertex.Y, vertex.Z); vertex = obj.GetVertex(face.Vertex2); RHVector3 vert2 = new RHVector3(vertex.X, vertex.Y, vertex.Z); vertex = obj.GetVertex(face.Vertex3); RHVector3 vert3 = new RHVector3(vertex.X, vertex.Y, vertex.Z); RHVector3 normal = new RHVector3(0, 0, 0); vert1.Scale(scale); vert2.Scale(scale); vert3.Scale(scale); addTriangle(vert1, vert2, vert3, normal).RecomputeNormal(); } } return true; }
public bool Intersects(TopoTriangle tri) { // First detect shared edges for more reliable and faster tests TopoVertex[] shared = new TopoVertex[3]; TopoVertex[] uniqueA = new TopoVertex[3]; TopoVertex[] uniqueB = new TopoVertex[3]; int nShared = 0, nUniqueA = 0, nUniqueB = 0; for (int i = 0; i < 3; i++) { bool isDouble = false; for (int j = 0; j < 3; j++) { if (vertices[i] == tri.vertices[j]) { shared[nShared++] = vertices[i]; isDouble = true; break; } } if (!isDouble) { uniqueA[nUniqueA++] = vertices[i]; } } if (nShared > 0) { for (int i = 0; i < 3; i++) { bool isDouble = false; for (int j = 0; j < nShared; j++) { if (tri.vertices[i] == shared[j]) { isDouble = true; break; } } if (!isDouble) { uniqueB[nUniqueB++] = tri.vertices[i]; } } if (nShared == 1) { return(IntersectsSharedVertex(shared[0], uniqueA, uniqueB, tri)); } if (nShared == 2) { return(IntersectsSharedEdge(shared, uniqueA[0], uniqueB[0], tri)); } return(true); } // Nice to read but unoptimized intersection computation RHMatrix3 A = new RHMatrix3(); RHVector3 p1 = vertices[1].pos.Subtract(vertices[0].pos); RHVector3 p2 = vertices[2].pos.Subtract(vertices[0].pos); A.SetXColumn(p1); A.SetYColumn(p2); RHVector3 P = new RHVector3(vertices[0].pos); RHVector3 q1 = tri.vertices[1].pos.Subtract(tri.vertices[0].pos); RHVector3 q2 = tri.vertices[2].pos.Subtract(tri.vertices[0].pos); RHVector3 r1 = tri.vertices[0].pos.Subtract(P); // r2 == r1! RHVector3 r3 = tri.vertices[2].pos.Subtract(P); A.SetZColumn(q1); double detAq1 = A.Determinant; A.SetZColumn(q2); double detAq2 = A.Determinant; //A.SetZColumn(q3); double detAq3 = detAq1 - detAq2; // A.Determinant; A.SetZColumn(r1); double detAr1 = A.Determinant; //A.SetZColumn(r3); double detAr3 = detAr1 + detAq2; // A.Determinant; int intersect = 0; if (detAq1 == 0 && detAq2 == 0 && detAq3 == 0) // same plane case { if (detAr1 != 0) { return(false); // other parallel plance } // Select plane for computation x-y or x-z based on normal int idx1, idx2; DominantAxis(out idx1, out idx2); if (InPlaneIntersectLine(idx1, idx2, vertices[0].pos, vertices[1].pos, tri.vertices[0].pos, tri.vertices[1].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[0].pos, vertices[1].pos, tri.vertices[1].pos, tri.vertices[2].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[0].pos, vertices[1].pos, tri.vertices[2].pos, tri.vertices[0].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[1].pos, vertices[2].pos, tri.vertices[0].pos, tri.vertices[1].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[1].pos, vertices[2].pos, tri.vertices[1].pos, tri.vertices[2].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[1].pos, vertices[2].pos, tri.vertices[2].pos, tri.vertices[0].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[2].pos, vertices[0].pos, tri.vertices[0].pos, tri.vertices[1].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[2].pos, vertices[0].pos, tri.vertices[1].pos, tri.vertices[2].pos)) { return(true); } if (InPlaneIntersectLine(idx1, idx2, vertices[2].pos, vertices[0].pos, tri.vertices[2].pos, tri.vertices[0].pos)) { return(true); } // Test if point inside. 1 test per triangle is enough if (InPlanePointInside(idx1, idx2, tri.vertices[0].pos)) { return(true); } if (InPlanePointInside(idx1, idx2, tri.vertices[1].pos)) { return(true); } if (InPlanePointInside(idx1, idx2, tri.vertices[2].pos)) { return(true); } if (tri.InPlanePointInside(idx1, idx2, vertices[0].pos)) { return(true); } if (tri.InPlanePointInside(idx1, idx2, vertices[1].pos)) { return(true); } if (tri.InPlanePointInside(idx1, idx2, vertices[2].pos)) { return(true); } return(false); } double beta1 = -1, beta2 = -1, beta3 = -1; if (detAq1 != 0) { beta1 = -detAr1 / detAq1; if (beta1 >= epsilonZeroMinus && beta1 <= epsilonOnePlus) { intersect = 1; } } if (detAq2 != 0) { beta2 = -detAr1 / detAq2; if (beta2 >= epsilonZeroMinus && beta2 <= epsilonOnePlus) { intersect |= 2; } } if (detAq3 != 0) { beta3 = -detAr3 / detAq3; if (beta3 >= epsilonZeroMinus && beta3 <= epsilonOnePlus) { intersect |= 4; } } if (intersect == 7) { // Special case intersection in one point caused 3 valid betas if (Math.Abs(beta1) < epsilonZero) { intersect = 6; } else if (Math.Abs(beta3) < epsilonZero) { intersect = 3; } else { intersect = 5; } } //if (intersect == 0) return false; // Lies on wrong side RHVector3 T = null, t = null; if ((intersect & 1) == 1) { T = new RHVector3(q1); T.Scale(beta1); T.AddInternal(tri.vertices[0].pos); } if ((intersect & 2) == 2) { if (T == null) { T = new RHVector3(q2); T.Scale(beta2); T.AddInternal(tri.vertices[0].pos); } else { q2.Scale(beta2); q2.AddInternal(tri.vertices[0].pos); t = q2.Subtract(T); } } if ((intersect & 4) == 4 && T != null && (t == null || t.Length < epsilonZero)) { RHVector3 q3 = tri.vertices[1].pos.Subtract(tri.vertices[2].pos); q3.Scale(beta3); q3.AddInternal(tri.vertices[2].pos); t = q3.Subtract(T); } if (t == null) { return(false); } if (t.Length < epsilonZero) { // Only one point touches the plane int idx1, idx2; DominantAxis(out idx1, out idx2); return(InPlanePointInside(idx1, idx2, T)); } // Compute intersection points with this triangle double d1 = p1.x * t.y - p1.y * t.x; double d2 = p1.x * t.z - p1.z * t.x; double delta1 = -1, delta2 = -1, delta3 = -1, gamma1 = -1, gamma2 = -1, gamma3 = -1; if (Math.Abs(d1) > epsilonZero || Math.Abs(d2) > epsilonZero) { if (Math.Abs(d1) > Math.Abs(d2)) { delta1 = -(t.x * T.y - t.y * T.x + P.x * t.y - P.y * t.x) / d1; gamma1 = -(p1.x * T.y - p1.y * T.x - p1.x * P.y + p1.y * P.x) / d1; } else { delta1 = -(t.x * T.z - t.z * T.x + P.x * t.z - P.z * t.x) / d2; gamma1 = -(p1.x * T.z - p1.z * T.x - p1.x * P.z + p1.z * P.x) / d2; } } d1 = p2.x * t.y - p2.y * t.x; d2 = p2.x * t.z - p2.z * t.x; if (Math.Abs(d1) > epsilonZero || Math.Abs(d2) > epsilonZero) { if (Math.Abs(d1) > Math.Abs(d2)) { delta2 = -(t.x * T.y - t.y * T.x + P.x * t.y - P.y * t.x) / d1; gamma2 = -(p2.x * T.y - p2.y * T.x - p2.x * P.y + p2.y * P.x) / d1; } else { delta2 = -(t.x * T.z - t.z * T.x + P.x * t.z - P.z * t.x) / d2; gamma2 = -(p2.x * T.z - p2.z * T.x - p2.x * P.z + p2.z * P.x) / d2; } } P.AddInternal(p1); p2.SubtractInternal(p1); // p2 is now p3! d1 = p2.x * t.y - p2.y * t.x; d2 = p2.x * t.z - p2.z * t.x; if (Math.Abs(d1) > epsilonZero || Math.Abs(d2) > epsilonZero) { if (Math.Abs(d1) > Math.Abs(d2)) { delta3 = -(t.x * T.y - t.y * T.x + P.x * t.y - P.y * t.x) / d1; gamma3 = -(p2.x * T.y - p2.y * T.x - p2.x * P.y + p2.y * P.x) / d1; } else { delta3 = -(t.x * T.z - t.z * T.x + P.x * t.z - P.z * t.x) / d2; gamma3 = -(p2.x * T.z - p2.z * T.x - p2.x * P.z + p2.z * P.x) / d2; } } // Check for line intersection inside the line. Hits at the vertices to not count! if (delta1 >= epsilonZero && delta1 <= epsilonOneMinus && gamma1 >= epsilonZero && gamma1 <= epsilonOneMinus) { return(true); } if (delta2 >= epsilonZero && delta2 <= epsilonOneMinus && gamma2 >= epsilonZero && gamma2 <= epsilonOneMinus) { return(true); } if (delta3 >= epsilonZero && delta3 <= epsilonOneMinus && gamma3 >= epsilonZero && gamma3 <= epsilonOneMinus) { return(true); } // Test if intersection is inside triangle intersect = 0; if (delta1 >= epsilonZeroMinus && delta1 <= epsilonOnePlus) { intersect |= 1; } if (delta2 >= epsilonZeroMinus && delta2 <= epsilonOnePlus) { intersect |= 2; } if (delta3 >= epsilonZeroMinus && delta3 <= epsilonOnePlus) { intersect |= 4; } /* if (gamma1 == 0) gamma1 = -1; * if (gamma2 == 0) gamma2 = -1; * if (gamma3 == 0) gamma3 = -1;*/ if (gamma1 == 0) { intersect &= ~1; } if (gamma2 == 0) { intersect &= ~2; } if (gamma3 == 0) { intersect &= ~4; } /* if ((intersect & 3) == 3) return gamma1 * gamma2 < 0; * if ((intersect & 5) == 5) return gamma1 * gamma3 < 0; * if ((intersect & 6) == 6) return gamma3 * gamma2 < 0;*/ if ((intersect & 3) == 3) { if (gamma1 * gamma2 < 0) { return(true); } else { return(false); } } if ((intersect & 5) == 5) { if (gamma1 * gamma3 < 0) { return(true); } else { return(false); } } if ((intersect & 6) == 6) { if (gamma3 * gamma2 < 0) { return(true); } else { return(false); } } // if (intersect!=0) happens only with numeric problems // return true; return(false); // No intersection found :-) }
public void FixColinear(TopoModel model) { RHVector3 center = vertices[0].pos.Add(vertices[1].pos).Add(vertices[2].pos); center.Scale(1 / 3.0); int best = -1; double bestdist = 1e30; for (int i = 0; i < 3; i++) { if (vertices[i].connectedFaces == 1) { continue; } double dist = center.Subtract(vertices[i].pos).Length; if (dist < bestdist) { bestdist = dist; best = i; } } if (best == -1) { throw new Exception("CheckIfColinearAndFix called on isolated triangle"); } edges[(best + 1) % 3].InsertVertex(model, vertices[best]); /* * // Find an other face sharing vertex * TopoTriangle otherFace = null; * TopoVertex moveVertex = vertices[best]; * foreach (TopoTriangle triangle in moveVertex.connectedFacesList) * { * if (triangle != this) * { * otherFace = triangle; * break; * } * } * // Now find the not shared vertex * TopoVertex oppositeVertex = null; * for (int i = 0; i < 3; i++) * { * bool notSame = true; * for (int j = 0; j < 3; j++) * { * if (otherFace.vertices[i] == vertices[j]) * { * notSame = false; * break; * } * } * if (notSame) * { * oppositeVertex = otherFace.vertices[i]; * } * } * RHVector3 line = moveVertex.pos.Subtract(oppositeVertex.pos); * double lineLength = line.Length; * double moveFactor = 0.01; * if (0.99 * lineLength > 0.01) moveFactor = 0.01 / lineLength; * line.Scale(moveFactor); * moveVertex.pos = moveVertex.pos.Add(line); * RecomputeNormal();*/ }