Created
September 20, 2026 04:20
-
-
Save maz-1/6d0c9679e5358ca9c19d347064d8546d to your computer and use it in GitHub Desktop.
Revit-Command-ExportStl.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| using System.Data; | |
| using System.Windows.Forms; | |
| using Autodesk.Revit; | |
| using Autodesk.Revit.Attributes; | |
| using Autodesk.Revit.DB; | |
| using Autodesk.Revit.UI; | |
| using Autodesk.Revit.ApplicationServices; | |
| using System.IO; | |
| using Autodesk.Revit.DB.Structure; | |
| using System.Diagnostics; | |
| using Autodesk.Revit.Creation; | |
| using g3; | |
| namespace Amberg.CompareMeshCloud | |
| { | |
| [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] | |
| [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)] | |
| [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)] | |
| public class CompareMeshCloudCMD : IExternalCommand | |
| { | |
| #region IExternalCommand Members | |
| public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, | |
| ref string message, Autodesk.Revit.DB.ElementSet elements) | |
| { | |
| { | |
| //FileInfo fi = new FileInfo(Utils.ExecutingAssemblyPath); | |
| //DateTime creationTime = fi.CreationTime; | |
| DateTime creationTime = new DateTime(2022, 09, 25, 0, 0, 0); | |
| TimeSpan duration = DateTime.Now - creationTime; | |
| if (duration.Days > 90) | |
| { | |
| MessageBox.Show("Unknown error occurred"); | |
| return Result.Cancelled; | |
| } | |
| } | |
| var uiapp = commandData.Application; | |
| var app = uiapp.Application; | |
| var uidoc = uiapp.ActiveUIDocument; | |
| var doc = uidoc.Document; | |
| var sel = uidoc.Selection; | |
| //MessageBox.Show(Utils.GetProjectBasePointMetric(doc).ToString()); | |
| var optionsDlg = new OptionsForm(); | |
| optionsDlg.ShowDialog(); | |
| if (optionsDlg.DialogResult != DialogResult.OK) | |
| return Result.Cancelled; | |
| //var stlPath = Path.Combine(Directory.GetParent(optionsDlg.pointCloudPath).FullName, Path.GetFileNameWithoutExtension(doc.PathName) + ".stl"); | |
| var stlPath = Path.Combine(Directory.GetParent(optionsDlg.pointCloudPath).FullName, Guid.NewGuid().ToString() + ".stl"); | |
| //var stlPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".stl"); | |
| var pointCloudOutPath = Path.Combine(Directory.GetParent(optionsDlg.pointCloudPath).FullName, Path.GetFileNameWithoutExtension(optionsDlg.pointCloudPath) + "_compare.e57"); | |
| var compareExePath = Path.Combine(Utils.ExecutingAssemblyDir, "Compare.exe"); | |
| if (!File.Exists(compareExePath)) | |
| { | |
| compareExePath = @"C:\Users\ling\OneDrive\Work\PointCloud\BIM_CompareMeshCloud\CompareMeshCloud\build\Compare.exe"; | |
| } | |
| ICollection<ElementId> ids = sel.GetElementIds(); | |
| if (ids.Count == 0) | |
| { | |
| message = "Please select elements to export."; | |
| return Result.Failed; | |
| } | |
| var listPtCoords = new List<Vector3d>(); | |
| var listTriIdxs = new List<Index3i>(); | |
| var listPtNormals = new List<Vector3d>(); | |
| var listPtNormalsXYZ = new List<XYZ>(); | |
| Options opt = app.Create.NewGeometryOptions(); | |
| opt.View = doc.ActiveView; | |
| //var listSolids = new List<Tuple<Solid, Transform>>(); | |
| var listSolids = new List<Solid>(); | |
| HashSet<ElementId> idSet = new HashSet<ElementId>() ; | |
| foreach (ElementId id in ids) | |
| { | |
| if (idSet.Contains(id)) | |
| continue; | |
| idSet.Add(id); | |
| Element e = doc.GetElement(id); | |
| Group group = e as Group; | |
| if (group is null) | |
| { | |
| GeometryElement geo = e.get_Geometry(opt); | |
| Utils.IterateSolids(geo, ref listSolids); | |
| } | |
| else | |
| { | |
| Utils.IterateSolids(opt, doc, group, ref listSolids, ref idSet); | |
| } | |
| } | |
| if (listSolids.Count == 0) | |
| { | |
| return Result.Failed; | |
| } | |
| /* | |
| Solid mergedSolid = listSolids[0]; | |
| if (listSolids.Count >= 2) | |
| { | |
| for (int i = 1; i < listSolids.Count; i++) | |
| { | |
| mergedSolid = BooleanOperationsUtils.ExecuteBooleanOperation(mergedSolid, listSolids[i], BooleanOperationsType.Union); | |
| } | |
| } | |
| */ | |
| foreach (var solid in listSolids) | |
| { | |
| try | |
| { | |
| var controls = new SolidOrShellTessellationControls(); | |
| controls.Accuracy = 0.05; | |
| controls.LevelOfDetail = 0.95; | |
| controls.MinAngleInTriangle = 0.13; | |
| controls.MinExternalAngleBetweenTriangles = 0.35; | |
| if (solid.Faces.IsEmpty) | |
| { | |
| continue; | |
| } | |
| var triangulation = SolidUtils.TessellateSolidOrShell(solid, controls); | |
| for (int i = 0; i < triangulation.ShellComponentCount; ++i) | |
| { | |
| int vertexIdxOffset = listPtCoords.Count; | |
| TriangulatedShellComponent component | |
| = triangulation.GetShellComponent(i); | |
| for (int j = 0; j < component.VertexCount; ++j) | |
| { | |
| var coord = component.GetVertex(j); | |
| listPtCoords.Add(new Vector3d(coord.X * Utils._footToM, coord.Y * Utils._footToM, coord.Z * Utils._footToM)); | |
| listPtNormalsXYZ.Add(new XYZ(0, 0, 0)); | |
| } | |
| for (int j = 0; j < component.TriangleCount; ++j) | |
| { | |
| TriangleInShellComponent t = component.GetTriangle(j); | |
| int idx0 = t.VertexIndex0 + vertexIdxOffset; | |
| int idx1 = t.VertexIndex1 + vertexIdxOffset; | |
| int idx2 = t.VertexIndex2 + vertexIdxOffset; | |
| XYZ v = component.GetVertex(1) - component.GetVertex(0); | |
| XYZ w = component.GetVertex(2) - component.GetVertex(1); | |
| listTriIdxs.Add(new Index3i(idx0, idx1, idx2)); | |
| XYZ triangleNormal = v.CrossProduct(w); //.Normalize(); | |
| listPtNormalsXYZ[idx0] += triangleNormal; | |
| listPtNormalsXYZ[idx1] += triangleNormal; | |
| listPtNormalsXYZ[idx2] += triangleNormal; | |
| } | |
| } | |
| } catch (Autodesk.Revit.Exceptions.InvalidOperationException ex) | |
| { | |
| ex.ToString(); | |
| continue; | |
| } | |
| } | |
| foreach (var v in listPtNormalsXYZ) | |
| { | |
| var vector = v.Normalize(); | |
| listPtNormals.Add(new Vector3d(vector.X, vector.Y, vector.Z)); | |
| } | |
| DMesh3 mesh = DMesh3Builder.Build(listPtCoords, listTriIdxs, listPtNormals); | |
| var writeOptions = WriteOptions.Defaults; | |
| writeOptions.bWriteBinary = true; | |
| StandardMeshWriter.WriteFile(stlPath, new List<WriteMesh>() { new WriteMesh(mesh) }, writeOptions); | |
| ProcessStartInfo cmdsi = new ProcessStartInfo(compareExePath); | |
| cmdsi.UseShellExecute = true; | |
| cmdsi.CreateNoWindow = false; | |
| var CultureName = Thread.CurrentThread.CurrentCulture.Name; | |
| var ci = new System.Globalization.CultureInfo(CultureName); | |
| if (ci.NumberFormat.NumberDecimalSeparator != ".") | |
| { | |
| // Forcing use of decimal separator for numerical values | |
| ci.NumberFormat.NumberDecimalSeparator = "."; | |
| Thread.CurrentThread.CurrentCulture = ci; | |
| } | |
| cmdsi.Arguments = String.Format("--mesh \"{0}\" --input \"{1}\" --output \"{2}\" --delta {3} --show", stlPath, optionsDlg.pointCloudPath, pointCloudOutPath, optionsDlg.delta); | |
| if (optionsDlg.applyOffset) | |
| { | |
| var offset = Utils.GetProjectBasePointMetric(doc); | |
| cmdsi.Arguments += String.Format(" --offset \"{0},{1},{2}\"", -offset.X, -offset.Y, -offset.Z); | |
| } | |
| //Clipboard.SetText(cmdsi.Arguments); | |
| Process cmd = Process.Start(cmdsi); | |
| cmd.WaitForExit(); | |
| if (File.Exists(pointCloudOutPath)) | |
| { | |
| MessageBox.Show(String.Format("Comparison done, please open\n{0}\nin Autodesk Recap", pointCloudOutPath)); | |
| System.Diagnostics.Process.Start("explorer.exe", "/select, \"" + pointCloudOutPath + "\""); | |
| } | |
| File.Delete(stlPath); | |
| return Result.Succeeded; | |
| } | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment