private void ExportSolid(Solid solid) { SolidOrShellTessellationControls solidOrShellTessellationControls = new SolidOrShellTessellationControls { LevelOfDetail = userSetting.LevelOfDetail / 30.0, Accuracy = 0.1, MinAngleInTriangle = 0.0001, MinExternalAngleBetweenTriangles = 1.0 }; try { TriangulatedSolidOrShell triangulatedSolidOrShell = SolidUtils.TessellateSolidOrShell(solid, solidOrShellTessellationControls); int shellComponentCount = triangulatedSolidOrShell.ShellComponentCount; for (int i = 0; i < shellComponentCount; i++) { TriangulatedShellComponent shellComponent = triangulatedSolidOrShell.GetShellComponent(i); ModelGeometry exportedGeometry = new ModelGeometry { Transform = transformationStack.Peek(), Points = new List <XYZ>(shellComponent.VertexCount) }; for (int num = 0; num != shellComponent.VertexCount; num++) { exportedGeometry.Points.Add(shellComponent.GetVertex(num)); } exportedGeometry.Indices = new List <int>(shellComponent.TriangleCount * 3); for (int j = 0; j < shellComponent.TriangleCount; j++) { TriangleInShellComponent triangle = shellComponent.GetTriangle(j); exportedGeometry.Indices.Add(triangle.VertexIndex0); exportedGeometry.Indices.Add(triangle.VertexIndex1); exportedGeometry.Indices.Add(triangle.VertexIndex2); } exportedGeometry.CalculateNormals(false); exportedGeometry.CalculateUVs(true, false); ElementId materialElementId = solid.Faces.get_Item(0).MaterialElementId; Tuple <Document, ElementId> tuple = new Tuple <Document, ElementId>(documentStack.Peek(), materialElementId); ChangeCurrentMaterial(tuple); documentAndMaterialIdToGeometries[tuple].Add(exportedGeometry); } } catch (Exception) { } }
/// <summary> /// Create a new list of geometry objects from the /// given input. As input, we supply the result of /// Room.GetClosedShell. The output is the exact /// same solid lacking whatever flaws are present /// in the input solid. /// </summary> static IList <GeometryObject> CopyGeometry( GeometryElement geo, ElementId materialId, List <IntPoint3d> coords, List <TriangleIndices> indices) { TessellatedShapeBuilderResult result = null; TessellatedShapeBuilder builder = new TessellatedShapeBuilder(); // Need to include the key in the value, otherwise // no way to access it later, cf. // https://stackoverflow.com/questions/1619090/getting-a-keyvaluepair-directly-from-a-dictionary Dictionary <XYZ, KeyValuePair <XYZ, int> > pts = new Dictionary <XYZ, KeyValuePair <XYZ, int> >( new XyzEqualityComparer()); int nSolids = 0; //int nFaces = 0; int nTriangles = 0; //int nVertices = 0; List <XYZ> vertices = new List <XYZ>(3); foreach (GeometryObject obj in geo) { Solid solid = obj as Solid; if (null != solid) { if (0 < solid.Volume) { ++nSolids; builder.OpenConnectedFaceSet(false); #region Create a new solid based on tessellation of the invalid room closed shell solid #if CREATE_NEW_SOLID_USING_TESSELATION Debug.Assert( SolidUtils.IsValidForTessellation(solid), "expected a valid solid for room closed shell"); SolidOrShellTessellationControls controls = new SolidOrShellTessellationControls() { // // Summary: // A positive real number specifying how accurately a triangulation should approximate // a solid or shell. // // Exceptions: // T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException: // When setting this property: The given value for accuracy must be greater than // 0 and no more than 30000 feet. // This statement is not true. I set Accuracy = 0.003 and an exception was thrown. // Setting it to 0.006 was acceptable. 0.03 is a bit over 9 mm. // // Remarks: // The maximum distance from a point on the triangulation to the nearest point on // the solid or shell should be no greater than the specified accuracy. This constraint // may be approximately enforced. Accuracy = 0.03, // // Summary: // An number between 0 and 1 (inclusive) specifying the level of detail for the // triangulation of a solid or shell. // // Exceptions: // T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException: // When setting this property: The given value for levelOfDetail must lie between // 0 and 1 (inclusive). // // Remarks: // Smaller values yield coarser triangulations (fewer triangles), while larger values // yield finer triangulations (more triangles). LevelOfDetail = 0.1, // // Summary: // A non-negative real number specifying the minimum allowed angle for any triangle // in the triangulation, in radians. // // Exceptions: // T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException: // When setting this property: The given value for minAngleInTriangle must be at // least 0 and less than 60 degrees, expressed in radians. The value 0 means to // ignore the minimum angle constraint. // // Remarks: // A small value can be useful when triangulating long, thin objects, in order to // keep the number of triangles small, but it can result in long, thin triangles, // which are not acceptable for all applications. If the value is too large, this // constraint may not be satisfiable, causing the triangulation to fail. This constraint // may be approximately enforced. A value of 0 means to ignore the minimum angle // constraint. MinAngleInTriangle = 3 * Math.PI / 180.0, // // Summary: // A positive real number specifying the minimum allowed value for the external // angle between two adjacent triangles, in radians. // // Exceptions: // T:Autodesk.Revit.Exceptions.ArgumentOutOfRangeException: // When setting this property: The given value for minExternalAngleBetweenTriangles // must be greater than 0 and no more than 30000 feet. // // Remarks: // A small value yields more smoothly curved triangulated surfaces, usually at the // expense of an increase in the number of triangles. Note that this setting has // no effect for planar surfaces. This constraint may be approximately enforced. MinExternalAngleBetweenTriangles = 0.2 * Math.PI }; TriangulatedSolidOrShell shell = SolidUtils.TessellateSolidOrShell(solid, controls); int n = shell.ShellComponentCount; Debug.Assert(1 == n, "expected just one shell component in room closed shell"); TriangulatedShellComponent component = shell.GetShellComponent(0); int coordsBase = coords.Count; int indicesBase = indices.Count; n = component.VertexCount; for (int i = 0; i < n; ++i) { XYZ v = component.GetVertex(i); coords.Add(new IntPoint3d(v)); } n = component.TriangleCount; for (int i = 0; i < n; ++i) { TriangleInShellComponent t = component.GetTriangle(i); vertices.Clear(); vertices.Add(component.GetVertex(t.VertexIndex0)); vertices.Add(component.GetVertex(t.VertexIndex1)); vertices.Add(component.GetVertex(t.VertexIndex2)); indices.Add(new TriangleIndices( coordsBase + t.VertexIndex0, coordsBase + t.VertexIndex1, coordsBase + t.VertexIndex2)); TessellatedFace tf = new TessellatedFace( vertices, materialId); if (builder.DoesFaceHaveEnoughLoopsAndVertices(tf)) { builder.AddFace(tf); ++nTriangles; } } #else // Iterate over the individual solid faces foreach (Face f in solid.Faces) { vertices.Clear(); #region Use face triangulation #if USE_FACE_TRIANGULATION Mesh mesh = f.Triangulate(); int n = mesh.NumTriangles; for (int i = 0; i < n; ++i) { MeshTriangle triangle = mesh.get_Triangle(i); XYZ p1 = triangle.get_Vertex(0); XYZ p2 = triangle.get_Vertex(1); XYZ p3 = triangle.get_Vertex(2); vertices.Clear(); vertices.Add(p1); vertices.Add(p2); vertices.Add(p3); TessellatedFace tf = new TessellatedFace( vertices, materialId); if (builder.DoesFaceHaveEnoughLoopsAndVertices(tf)) { builder.AddFace(tf); ++nTriangles; } } #endif // USE_FACE_TRIANGULATION #endregion // Use face triangulation #region Use original solid and its EdgeLoops #if USE_EDGELOOPS // This returns arbitrarily ordered and // oriented edges, so no solid can be // generated. foreach (EdgeArray loop in f.EdgeLoops) { foreach (Edge e in loop) { XYZ p = e.AsCurve().GetEndPoint(0); XYZ q = p; if (pts.ContainsKey(p)) { KeyValuePair <XYZ, int> kv = pts[p]; q = kv.Key; int n = kv.Value; pts[p] = new KeyValuePair <XYZ, int>( q, ++n); Debug.Print("Ignoring vertex at {0} " + "with distance {1} to existing " + "vertex {2}", p, p.DistanceTo(q), q); } else { pts[p] = new KeyValuePair <XYZ, int>( p, 1); } vertices.Add(q); ++nVertices; } } #endif // USE_EDGELOOPS #endregion // Use original solid and its EdgeLoops #region Use original solid and GetEdgesAsCurveLoops #if USE_AS_CURVE_LOOPS // The solids generated by this have some weird // normals, so they do not render correctly in // the Forge viewer. Revert to triangles again. IList <CurveLoop> loops = f.GetEdgesAsCurveLoops(); foreach (CurveLoop loop in loops) { foreach (Curve c in loop) { XYZ p = c.GetEndPoint(0); XYZ q = p; if (pts.ContainsKey(p)) { KeyValuePair <XYZ, int> kv = pts[p]; q = kv.Key; int n = kv.Value; pts[p] = new KeyValuePair <XYZ, int>( q, ++n); Debug.Print("Ignoring vertex at {0} " + "with distance {1} to existing " + "vertex {2}", p, p.DistanceTo(q), q); } else { pts[p] = new KeyValuePair <XYZ, int>( p, 1); } vertices.Add(q); ++nVertices; } } #endif // USE_AS_CURVE_LOOPS #endregion // Use original solid and GetEdgesAsCurveLoops builder.AddFace(new TessellatedFace( vertices, materialId)); ++nFaces; } #endif // CREATE_NEW_SOLID_USING_TESSELATION #endregion // Create a new solid based on tessellation of the invalid room closed shell solid builder.CloseConnectedFaceSet(); builder.Target = TessellatedShapeBuilderTarget.AnyGeometry; // Solid failed builder.Fallback = TessellatedShapeBuilderFallback.Mesh; // use Abort if target is Solid builder.Build(); result = builder.GetBuildResult(); // Debug printout log of current solid's glTF facet data n = coords.Count - coordsBase; Debug.Print("{0} glTF vertex coordinates " + "in millimetres:", n); Debug.Print(string.Join(" ", coords .TakeWhile <IntPoint3d>((p, i) => coordsBase <= i) .Select <IntPoint3d, string>(p => p.ToString()))); n = indices.Count - indicesBase; Debug.Print("{0} glTF triangles:", n); Debug.Print(string.Join(" ", indices .TakeWhile <TriangleIndices>((ti, i) => indicesBase <= i) .Select <TriangleIndices, string>(ti => ti.ToString()))); } } } return(result.GetGeometricalObjects()); }
private void _solidToMeshGeometry3D(Solid solid) { try { IList <Solid> solids = SolidUtils.SplitVolumes(solid); foreach (Solid _solid in solids) { if (SolidUtils.IsValidForTessellation(_solid)) { var material = this.getMaterial(_solid); TriangulatedSolidOrShell shell = SolidUtils.TessellateSolidOrShell(_solid, control); for (int i = 0; i < shell.ShellComponentCount; i++) { try { TriangulatedShellComponent component = shell.GetShellComponent(i); MeshGeometry3D WPFMesh = new MeshGeometry3D(); Point3D[] points = new Point3D[component.VertexCount]; WPFMesh.TriangleIndices = new System.Windows.Media.Int32Collection(); for (int j = 0; j < component.TriangleCount; j++) { TriangleInShellComponent triangle = component.GetTriangle(j); WPFMesh.TriangleIndices.Add(triangle.VertexIndex0); XYZ p = inverse.OfPoint(component.GetVertex(triangle.VertexIndex0)); points[triangle.VertexIndex0] = new Point3D(p.X, p.Y, p.Z); WPFMesh.TriangleIndices.Add(triangle.VertexIndex1); p = inverse.OfPoint(component.GetVertex(triangle.VertexIndex1)); points[triangle.VertexIndex1] = new Point3D(p.X, p.Y, p.Z); WPFMesh.TriangleIndices.Add(triangle.VertexIndex2); p = inverse.OfPoint(component.GetVertex(triangle.VertexIndex2)); points[triangle.VertexIndex2] = new Point3D(p.X, p.Y, p.Z); } WPFMesh.Positions = new Point3DCollection(points); if (this._territory.IntersectsWith(WPFMesh.Bounds)) { GeometryModel3D geometryModel3D = new GeometryModel3D(WPFMesh, material); this.WPFGeometries.Add(geometryModel3D); solidNum++; } else { outOfTerritory++; } } catch (Exception e) { failedAttempt++; reporter.AppendLine(string.Format("{0}: Failed to parse Solid:\n {1}", failedAttempt.ToString(), e.Report())); } } } else { MessageBox.Show("Solid not valid for tessellation!"); } } } catch (Exception e) { failedAttempt++; reporter.AppendLine(string.Format("{0}: Failed to parse S0lid:\n {1}", failedAttempt.ToString(), e.Report())); } }
/* * void f() * { * var cx, cy, cz, volume, v, i, x1, y1, z1, x2, y2, z2, x3, y3, z3; * volume = 0; * cx = 0; cy = 0; cz = 0; * // Assuming vertices are in vertX[i], vertY[i], and vertZ[i] * // and faces are faces[i, j] where the first index indicates the * // face and the second index indicates the vertex of that face * // The value in the faces array is an index into the vertex array * i = 0; * repeat (numFaces) { * x1 = vertX[faces[i, 0]]; y1 = vertY[faces[i, 0]]; z1 = vertZ[faces[i, 0]]; * x2 = vertX[faces[i, 1]]; y2 = vertY[faces[i, 1]]; z2 = vertZ[faces[i, 1]]; * x3 = vertX[faces[i, 2]]; y3 = vertY[faces[i, 2]]; z3 = vertZ[faces[i, 2]]; * v = x1*(y2*z3 - y3*z2) + y1*(z2*x3 - z3*x2) + z1*(x2*y3 - x3*y2); * volume += v; * cx += (x1 + x2 + x3)*v; * cy += (y1 + y2 + y3)*v; * cz += (z1 + z2 + z3)*v; * i += 1; * } * // Set centroid coordinates to their final value * cx /= 4 * volume; * cy /= 4 * volume; * cz /= 4 * volume; * // And, just in case you want to know the total volume of the model: * volume /= 6; * } */ CentroidVolume GetCentroid(Solid solid) { CentroidVolume cv = new CentroidVolume(); double v; XYZ v0, v1, v2; SolidOrShellTessellationControls controls = new SolidOrShellTessellationControls(); controls.LevelOfDetail = 0; TriangulatedSolidOrShell triangulation = null; try { triangulation = SolidUtils.TessellateSolidOrShell( solid, controls); } catch (Autodesk.Revit.Exceptions .InvalidOperationException) { return(null); } int n = triangulation.ShellComponentCount; for (int i = 0; i < n; ++i) { TriangulatedShellComponent component = triangulation.GetShellComponent(i); int m = component.TriangleCount; for (int j = 0; j < m; ++j) { TriangleInShellComponent t = component.GetTriangle(j); v0 = component.GetVertex(t.VertexIndex0); v1 = component.GetVertex(t.VertexIndex1); v2 = component.GetVertex(t.VertexIndex2); v = v0.X * (v1.Y * v2.Z - v2.Y * v1.Z) + v0.Y * (v1.Z * v2.X - v2.Z * v1.X) + v0.Z * (v1.X * v2.Y - v2.X * v1.Y); cv.Centroid += v * (v0 + v1 + v2); cv.Volume += v; } } // Set centroid coordinates to their final value cv.Centroid /= 4 * cv.Volume; XYZ diffCentroid = cv.Centroid - solid.ComputeCentroid(); Debug.Assert(0.6 > diffCentroid.GetLength(), "expected centroid approximation to be " + "similar to solid ComputeCentroid result"); // And, just in case you want to know // the total volume of the model: cv.Volume /= 6; double diffVolume = cv.Volume - solid.Volume; Debug.Assert(0.3 > Math.Abs( diffVolume / cv.Volume), "expected volume approximation to be " + "similar to solid Volume property value"); return(cv); }