/// <summary> /// Boolean intersect geometric operation, return a new solid as the result /// </summary> /// <param name="solid1">Operation solid 1</param> /// <param name="solid2">Operation solid 2</param> /// <returns>The operation result</returns> public static Solid BooleanOperation_Intersect(Solid solid1, Solid solid2) { return(BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Intersect)); }
/// <summary> /// Boolean union geometric operation, modify the original solid as the result /// </summary> /// <param name="solid1">Operation solid 1 and operation result</param> /// <param name="solid2">Operation solid 2</param> public static void BooleanOperation_Union(ref Solid solid1, Solid solid2) { BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(solid1, solid2, BooleanOperationsType.Union); }
/// <summary> /// Boolean difference geometric operation, modify the original solid as the result /// </summary> /// <param name="solid1">Operation solid 1 and operation result</param> /// <param name="solid2">Operation solid 2</param> public static void BooleanOperation_Difference(ref Solid solid1, Solid solid2) { BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(solid1, solid2, BooleanOperationsType.Difference); }
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Application app = uiapp.Application; Document doc = uidoc.Document; Selection sel = uidoc.Selection; // Retrieve all floors from the model var floors = new FilteredElementCollector(doc) .OfClass(typeof(Floor)) .ToElements() .Cast <Floor>() .ToList(); if (2 != floors.Count) { message = "Please create two intersected floors"; return(Result.Failed); } // Retrieve the floor solids Options opt = new Options(); var geometry1 = floors[0].get_Geometry(opt); var geometry2 = floors[1].get_Geometry(opt); var solid1 = geometry1.FirstOrDefault() as Solid; var solid2 = geometry2.FirstOrDefault() as Solid; // Calculate the intersection solid var intersectedSolid = BooleanOperationsUtils .ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Intersect); // Search for the metric mass family template file string template_path = DirSearch( app.FamilyTemplatePath, "Metric Mass.rft"); // Create a new temporary family var family_doc = app.NewFamilyDocument( template_path); // Create a free form element // from the intersection solid using (var t = new Transaction(family_doc)) { t.Start("Add Free Form Element"); var freeFormElement = FreeFormElement.Create( family_doc, intersectedSolid); t.Commit(); } string dir = Path.GetTempPath(); string filepath = Path.Combine(dir, "floor_intersection_family.rfa"); SaveAsOptions sao = new SaveAsOptions() { OverwriteExistingFile = true }; family_doc.SaveAs(filepath, sao); // Create 3D View var viewFamilyType = new FilteredElementCollector(family_doc) .OfClass(typeof(ViewFamilyType)) .OfType <ViewFamilyType>() .FirstOrDefault(x => x.ViewFamily == ViewFamily.ThreeDimensional); View3D threeDView; using (var t = new Transaction(family_doc)) { t.Start("Create 3D View"); threeDView = View3D.CreateIsometric( family_doc, viewFamilyType.Id); t.Commit(); } // Export to SAT var viewSet = new List <ElementId>() { threeDView.Id }; SATExportOptions exportOptions = new SATExportOptions(); var res = family_doc.Export(dir, "SolidFile.sat", viewSet, exportOptions); return(Result.Succeeded); }
/// <summary> /// Boolean intersect geometric operation, modify the original solid as the result /// </summary> /// <param name="solid1">Operation solid 1 and operation result</param> /// <param name="solid2">Operation solid 2</param> public static void BooleanOperation_Intersect(ref Solid solid1, Solid solid2) { BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(solid1, solid2, BooleanOperationsType.Intersect); }
/// <summary> /// Gets the result of between a solid and a solid cross. /// </summary> /// <param name="solid1"></param> /// <param name="solid2"></param> /// <returns></returns> public static bool CrossSolid(this Solid solid1, Solid solid2) { return(BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Intersect).Volume > 0); }
/// <summary> /// Execute a Boolean operation, and catch the exception. /// </summary> /// <param name="id">The id of the object demanding the Boolean operation.</param> /// <param name="secondId">The id of the object providing the second solid.</param> /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param> /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param> /// <param name="opType">The Boolean operation type.</param> /// <param name="suggestedShiftDirection">If the Boolean operation fails, a unit vector used to retry with a small shift. Can be null.</param> /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns> public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType, XYZ suggestedShiftDirection) { const double footToMillimeter = 1.0 / 304.8; // Perform default operations if one of the arguments is null. if (firstSolid == null || secondSolid == null) { if (firstSolid == null && secondSolid == null) { return(null); } switch (opType) { case BooleanOperationsType.Union: { if (firstSolid == null) { return(secondSolid); } return(firstSolid); } case BooleanOperationsType.Difference: { if (firstSolid == null) { return(null); } return(firstSolid); } default: // for .Intersect return(null); } } Solid resultSolid = null; bool failedAllAttempts = true; // We will attempt to do the Boolean operation 3 times: // 1st pass: With the passed-in arguments. // 2nd pass: With a 1mm shift in a direction in suggestedShiftDirection, or +Z if suggestedShiftDirection is null // 3rd pass: With a 1mm shift in a direction in -suggestedShiftDirection, or -Z if suggestedShiftDirection is null for (int ii = 0; ii < 3; ii++) { try { resultSolid = null; Solid secondOperand = secondSolid; if (ii > 0) { // 1 mm shift. XYZ shiftDirection = (suggestedShiftDirection == null) ? new XYZ(0, 0, 1) : suggestedShiftDirection; Transform secondSolidShift = Transform.CreateTranslation(shiftDirection * ((ii == 1) ? footToMillimeter : -footToMillimeter)); secondOperand = SolidUtils.CreateTransformed(secondOperand, secondSolidShift); } resultSolid = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondOperand, opType); failedAllAttempts = false; } catch (Exception ex) { if (ii < 2) { continue; } Importer.TheLog.LogError(id, ex.Message, false); resultSolid = firstSolid; } if (SolidValidator.IsValidGeometry(resultSolid)) { // If we got here not on out first attempt, generate a warning, unless we got here because we gave up on our 3rd attempt. if (ii > 0 && !failedAllAttempts) { Importer.TheLog.LogWarning(id, "The second argument in the Boolean " + opType.ToString() + " operation was shifted by 1mm to allow the operation to succeed. This may result in a very small difference in appearance.", false); } return(resultSolid); } } Importer.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false); return(firstSolid); }
/// <summary> /// Creates a negative block family from the geometry of the target element and boundaries. /// </summary> /// <remarks>This is the main implementation of the sample command.</remarks> /// <param name="targetElement">The target solid element.</param> /// <param name="boundaries">The selected curve element boundaries.</param> /// <param name="familyLoadOptions">The family load options when loading the new family.</param> /// <param name="familyTemplate">The family template.</param> public static FailureCondition CreateNegativeBlock(Element targetElement, IList <Reference> boundaries, IFamilyLoadOptions familyLoadOptions, String familyTemplate) { Document doc = targetElement.Document; Autodesk.Revit.ApplicationServices.Application app = doc.Application; // Get curve loop for boundary IList <Curve> curves = GetContiguousCurvesFromSelectedCurveElements(doc, boundaries); CurveLoop loop = null; try { loop = CurveLoop.Create(curves); } catch (Autodesk.Revit.Exceptions.ArgumentException) { // Curves are not contiguous return(FailureCondition.CurvesNotContigous); } List <CurveLoop> loops = new List <CurveLoop>(); loops.Add(loop); // Get elevation of loop double elevation = curves[0].GetEndPoint(0).Z; // Get height for extrusion BoundingBoxXYZ bbox = targetElement.get_BoundingBox(null); double height = bbox.Max.Z - elevation; if (height <= 1e-5) { return(FailureCondition.CurveLoopAboveTarget); } height += 1; // Create family Document familyDoc = app.NewFamilyDocument(familyTemplate); // Create block from boundaries Solid block = GeometryCreationUtilities.CreateExtrusionGeometry(loops, XYZ.BasisZ, height); // Subtract target element IList <Solid> fromElement = GetTargetSolids(targetElement); int solidCount = fromElement.Count; // Merge all found solids into single one Solid toSubtract = null; if (solidCount == 1) { toSubtract = fromElement[0]; } else if (solidCount > 1) { toSubtract = BooleanOperationsUtils.ExecuteBooleanOperation(fromElement[0], fromElement[1], BooleanOperationsType.Union); } if (solidCount > 2) { for (int i = 2; i < solidCount; i++) { toSubtract = BooleanOperationsUtils.ExecuteBooleanOperation(toSubtract, fromElement[i], BooleanOperationsType.Union); } } // Subtract merged solid from overall block try { BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(block, toSubtract, BooleanOperationsType.Difference); } catch (Autodesk.Revit.Exceptions.InvalidOperationException) { return(FailureCondition.NoIntersection); } // Create freeform element using (Transaction t = new Transaction(familyDoc, "Add element")) { t.Start(); RevitFreeFormElement element = Autodesk.Revit.DB.FreeFormElement.Create(familyDoc, block); t.Commit(); } // Load family into document Family family = familyDoc.LoadFamily(doc, familyLoadOptions); familyDoc.Close(false); // Get symbol as first symbol of loaded family FilteredElementCollector collector = new FilteredElementCollector(doc); collector.WherePasses(new FamilySymbolFilter(family.Id)); FamilySymbol fs = collector.FirstElement() as FamilySymbol; // Place instance at location of original curves using (Transaction t2 = new Transaction(doc, "Place instance")) { t2.Start(); if (!fs.IsActive) { fs.Activate(); } doc.Create.NewFamilyInstance(XYZ.Zero, fs, Autodesk.Revit.DB.Structure.StructuralType.NonStructural); t2.Commit(); } return(FailureCondition.Success); }
/// <summary> /// This method takes the solidsList and clips all of its solids between the given range. /// </summary> /// <param name="elem"> /// The Element from which we obtain our BoundingBoxXYZ. /// </param> /// <param name="geomElem"> /// The top-level GeometryElement from which to gather X and Y coordinates for the intersecting solid. /// </param> /// <param name="range"> /// The IFCRange whose Z values we use to create an intersecting solid to clip the solids in this class's internal solidsList. /// If range boundaries are equal, method returns, performing no clippings. /// </param> public void ClipSolidsList(GeometryElement geomElem, IFCRange range) { if (geomElem == null) { throw new ArgumentNullException("geomElemToUse"); } if (MathUtil.IsAlmostEqual(range.Start, range.End) || solidsList.Count == 0) { return; } double bottomZ; double boundDifference; if (range.Start < range.End) { bottomZ = range.Start; boundDifference = range.End - range.Start; } else { bottomZ = range.End; boundDifference = range.Start - range.End; } Autodesk.Revit.DB.Document doc = ExporterCacheManager.Document; Autodesk.Revit.ApplicationServices.Application application = doc.Application; Autodesk.Revit.Creation.Application app = application.Create; // create a new solid using the X and Y of the bounding box on the top level GeometryElement and the Z of the IFCRange BoundingBoxXYZ elemBoundingBox = geomElem.GetBoundingBox(); XYZ pointA = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointB = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointC = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Max.Y, bottomZ); XYZ pointD = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Max.Y, bottomZ); List <Curve> perimeter = new List <Curve>(); perimeter.Add(app.NewLineBound(pointA, pointB)); perimeter.Add(app.NewLineBound(pointB, pointC)); perimeter.Add(app.NewLineBound(pointC, pointD)); perimeter.Add(app.NewLineBound(pointD, pointA)); List <CurveLoop> boxPerimeterList = new List <CurveLoop>(); boxPerimeterList.Add(CurveLoop.Create(perimeter)); Solid intersectionSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boxPerimeterList, XYZ.BasisZ, boundDifference); // cycle through the elements in solidsList and intersect them against intersectionSolid to create a new list List <Solid> clippedSolidsList = new List <Solid>(); Solid currSolid; foreach (Solid solid in solidsList) { currSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, intersectionSolid, BooleanOperationsType.Intersect); if (currSolid != null && currSolid.Volume != 0) { clippedSolidsList.Add(currSolid); } } solidsList = clippedSolidsList; }
/// <summary> /// Execute a Boolean operation, and catch the exception. /// </summary> /// <param name="id">The id of the object demanding the Boolean operation.</param> /// <param name="secondId">The id of the object providing the second solid.</param> /// <param name="firstSolid">The first solid parameter to ExecuteBooleanOperation.</param> /// <param name="secondSolid">The second solid parameter to ExecuteBooleanOperation.</param> /// <param name="opType">The Boolean operation type.</param> /// <param name="suggestedShiftDirection">If the Boolean operation fails, a unit vector used to retry with a small shift. Can be null.</param> /// <returns>The result of the Boolean operation, or the first solid if the operation fails.</returns> public static Solid ExecuteSafeBooleanOperation(int id, int secondId, Solid firstSolid, Solid secondSolid, BooleanOperationsType opType, XYZ suggestedShiftDirection) { const double footToMillimeter = 1.0 / 304.8; // Perform default operations if one of the arguments is null. if (firstSolid == null || secondSolid == null) { if (firstSolid == null && secondSolid == null) { return(null); } switch (opType) { case BooleanOperationsType.Union: { if (firstSolid == null) { return(secondSolid); } return(firstSolid); } case BooleanOperationsType.Difference: { if (firstSolid == null) { return(null); } return(firstSolid); } default: // for .Intersect return(null); } } Solid resultSolid = null; bool failedAllAttempts = true; // We will attempt to do the Boolean operation here. // In the first pass, we will try to do the Boolean operation as-is. // For subsequent passes, we will shift the second operand by a small distance in // a given direction, using the following formula: // We start with a 1mm shift, and try each of (up to 4) shift directions given by // the shiftDirections list below, in alternating positive and negative directions. // In none of these succeed, we will increment the distance by 1mm and try again // until we reach numPasses. // Boolean operations are expensive, and as such we want to limit the number of // attempts we make here to balance fidelity and performance. Initial experimentation // suggests that a maximum 3mm shift is a good first start for this balance. IList <XYZ> shiftDirections = new List <XYZ>() { suggestedShiftDirection, XYZ.BasisZ, XYZ.BasisX, XYZ.BasisY }; const int numberOfNudges = 4; const int numPasses = numberOfNudges * 8 + 1; // 1 base, 8 possible nudges up to 0.75mm. double currentShiftFactor = 0.0; for (int ii = 0; ii < numPasses; ii++) { try { resultSolid = null; Solid secondOperand = secondSolid; if (ii > 0) { int shiftDirectionIndex = (ii - 1) % 4; XYZ shiftDirectionToUse = shiftDirections[shiftDirectionIndex]; if (shiftDirectionToUse == null) { continue; } // ((ii + 3) >> 3) * 0.25mm shift. Basically, a 0.25mm shift for every 8 attempts. currentShiftFactor = ((ii + 1) >> 3) * 0.25; int posOrNegDirection = (ii % 2 == 1) ? 1 : -1; double scale = currentShiftFactor * posOrNegDirection * footToMillimeter; Transform secondSolidShift = Transform.CreateTranslation(scale * shiftDirectionToUse); secondOperand = SolidUtils.CreateTransformed(secondOperand, secondSolidShift); } resultSolid = BooleanOperationsUtils.ExecuteBooleanOperation(firstSolid, secondOperand, opType); failedAllAttempts = false; } catch (Exception ex) { string msg = ex.Message; // This is the only error that we are trying to catch and fix. // For any other error, we will re-throw. if (!msg.Contains("Failed to perform the Boolean operation for the two solids")) { throw ex; } if (ii < numPasses - 1) { continue; } Importer.TheLog.LogError(id, msg, false); resultSolid = firstSolid; } if (SolidValidator.IsValidGeometry(resultSolid)) { // If we got here not on out first attempt, generate a warning, unless we got here because we gave up on our 3rd attempt. if (ii > 0 && !failedAllAttempts) { Importer.TheLog.LogWarning(id, "The second argument in the Boolean " + opType.ToString() + " operation was shifted by " + currentShiftFactor + "mm to allow the operation to succeed. This may result in a very small difference in appearance.", false); } return(resultSolid); } } Importer.TheLog.LogError(id, opType.ToString() + " operation failed with void from #" + secondId.ToString(), false); return(firstSolid); }
/// <summary> /// Retrieve all plan view boundary loops from /// all solids of the given element geometry /// united together. /// </summary> internal static JtLoops GetPlanViewBoundaryLoopsGeo( Autodesk.Revit.Creation.Application creapp, GeometryElement geo, ref int nFailures) { Solid union = null; Plane plane = Plane.CreateByOriginAndBasis( XYZ.Zero, XYZ.BasisX, XYZ.BasisY); foreach (GeometryObject obj in geo) { Solid solid = obj as Solid; if (null != solid && 0 < solid.Faces.Size) { // Some solids, e.g. in the standard // content 'Furniture Chair - Office' // cause an extrusion analyser failure, // so skip adding those. try { ExtrusionAnalyzer extrusionAnalyzer = ExtrusionAnalyzer.Create( solid, plane, XYZ.BasisZ); } catch (Autodesk.Revit.Exceptions .InvalidOperationException) { solid = null; ++nFailures; } if (null != solid) { if (null == union) { union = solid; } else { try { union = BooleanOperationsUtils .ExecuteBooleanOperation(union, solid, BooleanOperationsType.Union); } catch (Autodesk.Revit.Exceptions .InvalidOperationException) { ++nFailures; } } } } } JtLoops loops = new JtLoops(1); AddLoops(creapp, loops, union, ref nFailures); return(loops); }
/// <summary> /// Gets the rectangular openings. /// </summary> /// <returns></returns> /// <remarks>This method uses walls, doors, windows and generic models bounding boxes to determine the rectangles. /// These objects can be in the host file or in linked Revit files.</remarks> public static IList <Autodesk.DesignScript.Geometry.Rectangle> GetRectangularOpenings() { Utils.Log(string.Format("OpeningUtils.GetRectangularOpenings started...", "")); Autodesk.Revit.DB.Document doc = DocumentManager.Instance.CurrentDBDocument; //look for Walls, Doors, Windows, Generic Models in the current document and in the linked documents ElementCategoryFilter wallFilter = new ElementCategoryFilter(BuiltInCategory.OST_Walls); ElementCategoryFilter doorFilter = new ElementCategoryFilter(BuiltInCategory.OST_Doors); ElementCategoryFilter windowFilter = new ElementCategoryFilter(BuiltInCategory.OST_Windows); ElementCategoryFilter genericFilter = new ElementCategoryFilter(BuiltInCategory.OST_GenericModel); IList <ElementFilter> filterList = new List <ElementFilter>() { wallFilter, doorFilter, windowFilter, genericFilter }; LogicalOrFilter orFilter = new LogicalOrFilter(filterList); IList <Autodesk.DesignScript.Geometry.Solid> solids = new List <Autodesk.DesignScript.Geometry.Solid>(); IList <Autodesk.DesignScript.Geometry.Rectangle> output = new List <Autodesk.DesignScript.Geometry.Rectangle>(); foreach (Autodesk.Revit.DB.Element e in new FilteredElementCollector(doc) .WherePasses(orFilter) .WhereElementIsNotElementType() .Where(x => x.Parameters.Cast <Autodesk.Revit.DB.Parameter>() .First(p => p.Id.IntegerValue == (int)BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).HasValue)) { string comments = e.Parameters.Cast <Autodesk.Revit.DB.Parameter>().First(p => p.Id.IntegerValue == (int)BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).AsString(); if (comments.ToLower() != "opening") { continue; } Transform tr = Transform.Identity; if (e is Instance) { Instance instance = e as Instance; tr = instance.GetTotalTransform(); } IList <Autodesk.Revit.DB.Solid> temp = new List <Autodesk.Revit.DB.Solid>(); foreach (GeometryObject go in e.get_Geometry(new Options())) { if (go is GeometryInstance) { GeometryInstance geoInstance = go as GeometryInstance; foreach (var gi in geoInstance.SymbolGeometry) { if (gi is Autodesk.Revit.DB.Solid) { Autodesk.Revit.DB.Solid s = gi as Autodesk.Revit.DB.Solid; s = SolidUtils.CreateTransformed(s, tr); temp.Add(s); } } } else { if (go is Autodesk.Revit.DB.Solid) { Autodesk.Revit.DB.Solid s = go as Autodesk.Revit.DB.Solid; s = SolidUtils.CreateTransformed(s, tr); temp.Add(s); } } } if (temp.Count > 0) { Autodesk.Revit.DB.Solid s0 = temp[0]; for (int i = 1; i < temp.Count; ++i) { s0 = BooleanOperationsUtils.ExecuteBooleanOperation(s0, temp[i], BooleanOperationsType.Union); } solids.Add(s0.ToProtoType()); } } foreach (RevitLinkInstance rli in new FilteredElementCollector(doc).OfClass(typeof(RevitLinkInstance)).WhereElementIsNotElementType()) { Autodesk.Revit.DB.Document link = rli.GetLinkDocument(); foreach (Autodesk.Revit.DB.Element e in new FilteredElementCollector(link) .WherePasses(orFilter) .WhereElementIsNotElementType() .Where(x => x.Parameters.Cast <Autodesk.Revit.DB.Parameter>() .First(p => p.Id.IntegerValue == (int)BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).HasValue)) { Transform tr = rli.GetTotalTransform(); string comments = e.Parameters.Cast <Autodesk.Revit.DB.Parameter>().First(p => p.Id.IntegerValue == (int)BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).AsString(); if (comments.ToLower() != "opening") { continue; } if (e is Instance) { Instance instance = e as Instance; tr = tr.Multiply(instance.GetTotalTransform()); } IList <Autodesk.Revit.DB.Solid> temp = new List <Autodesk.Revit.DB.Solid>(); foreach (var go in e.get_Geometry(new Options())) { if (go is GeometryInstance) { GeometryInstance geoInstance = go as GeometryInstance; foreach (var gi in geoInstance.SymbolGeometry) { if (gi is Autodesk.Revit.DB.Solid) { Autodesk.Revit.DB.Solid s = gi as Autodesk.Revit.DB.Solid; s = SolidUtils.CreateTransformed(s, tr); temp.Add(s); } } } else { if (go is Autodesk.Revit.DB.Solid) { Autodesk.Revit.DB.Solid s = go as Autodesk.Revit.DB.Solid; s = SolidUtils.CreateTransformed(s, tr); temp.Add(s); } } } if (temp.Count > 0) { Autodesk.Revit.DB.Solid s0 = temp[0]; for (int i = 1; i < temp.Count; ++i) { s0 = BooleanOperationsUtils.ExecuteBooleanOperation(s0, temp[i], BooleanOperationsType.Union); } solids.Add(s0.ToProtoType()); } } } foreach (var s in solids) { IList <Autodesk.DesignScript.Geometry.Point> points = new List <Autodesk.DesignScript.Geometry.Point>(); foreach (var v in s.Vertices) { points.Add(v.PointGeometry); } points = Autodesk.DesignScript.Geometry.Point.PruneDuplicates(points); Autodesk.DesignScript.Geometry.Plane plane = Autodesk.DesignScript.Geometry.Plane.ByBestFitThroughPoints(points); plane = Autodesk.DesignScript.Geometry.Plane.ByOriginNormal(points.Last(), plane.Normal); IList <Autodesk.DesignScript.Geometry.Point> temp = new List <Autodesk.DesignScript.Geometry.Point>(); foreach (var p in points) { foreach (var q in p.Project(plane, plane.Normal)) { temp.Add(q as Autodesk.DesignScript.Geometry.Point); } foreach (var q in p.Project(plane, plane.Normal.Reverse())) { temp.Add(q as Autodesk.DesignScript.Geometry.Point); } } temp = Autodesk.DesignScript.Geometry.Point.PruneDuplicates(temp); CoordinateSystem cs = CoordinateSystem.ByPlane(plane); IList <Autodesk.DesignScript.Geometry.Point> relative = new List <Autodesk.DesignScript.Geometry.Point>(); foreach (var p in temp) { relative.Add(p.Transform(cs.Inverse()) as Autodesk.DesignScript.Geometry.Point); } var min = Autodesk.DesignScript.Geometry.Point.ByCoordinates(relative.Min(p => p.X), relative.Min(p => p.Y), relative.Min(p => p.Z)); //.Transform(cs) as Autodesk.DesignScript.Geometry.Point; var max = Autodesk.DesignScript.Geometry.Point.ByCoordinates(relative.Max(p => p.X), relative.Max(p => p.Y), relative.Max(p => p.Z)); //.Transform(cs) as Autodesk.DesignScript.Geometry.Point; double width = max.X - min.X; double height = max.Y - min.Y; min = min.Transform(cs) as Autodesk.DesignScript.Geometry.Point; max = max.Transform(cs) as Autodesk.DesignScript.Geometry.Point; plane = Autodesk.DesignScript.Geometry.Plane.ByOriginNormal(Autodesk.DesignScript.Geometry.Line.ByStartPointEndPoint(min, max).PointAtParameter(0.5), plane.Normal); Autodesk.DesignScript.Geometry.Rectangle rectangle = Autodesk.DesignScript.Geometry.Rectangle.ByWidthLength(plane, width, height); output.Add(rectangle); plane.Dispose(); cs.Dispose(); min.Dispose(); max.Dispose(); } Utils.Log(string.Format("OpeningUtils.GetRectangularOpenings completed.", "")); return(output); }
private void OnIdling(object sender, IdlingEventArgs e) { //var App = sender as Application; var docsInApp = App.Documents.Cast <Document>(); var doc = docsInApp.FirstOrDefault(); var uidoc = new UIDocument(doc); var uiapp = uidoc.Application; try { var ActiveDoc = docsInApp.FirstOrDefault(); if (ids_add.Count != 1) { return; } var modelline = ids_add.First().GetElement(ActiveDoc) as ModelLine; var line = modelline.GeometryCurve as Line; //MessageBox.Show(line.Length.ToString()); var linedir = line.Direction; var startpo = line.StartPoint(); var endpo = line.EndPoint(); var updir = XYZ.BasisZ; var leftNorm = updir.CrossProduct(linedir).Normalize(); var rightNorm = updir.CrossProduct(-linedir).Normalize(); var leftspacePlane = default(Plane); // Plane.CreateByNormalAndOrigin(leftNorm, startpo); var rightspacePlane = default(Plane); //Plane.CreateByNormalAndOrigin(rightNorm, startpo); #if Revit2016 leftspacePlane = new Plane(leftNorm, startpo); rightspacePlane = new Plane(rightNorm, startpo); #endif #if Revit2019 leftspacePlane = Plane.CreateByNormalAndOrigin(leftNorm, startpo); rightspacePlane = Plane.CreateByNormalAndOrigin(rightNorm, startpo); #endif //var slapshapeEditor = floor.SlabShapeEditor; //剪切楼板 //var verticals = slapshapeEditor.SlabShapeVertices.Cast<XYZ>(); //var upfaceRef = HostObjectUtils.GetTopFaces(floor); //var upface = floor.GetGeometryObjectFromReference(upfaceRef.FirstOrDefault()); var geoele = floor.get_Geometry(new Options() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine }); var solid = geoele.Getsolids().FirstOrDefault(); var newsolid1 = BooleanOperationsUtils.CutWithHalfSpace(solid, leftspacePlane); var newsolid2 = BooleanOperationsUtils.CutWithHalfSpace(solid, rightspacePlane); var upface1 = newsolid1.GetFacesOfGeometryObject().Where(m => m.ComputeNormal(new UV()).IsSameDirection(XYZ.BasisZ)) .FirstOrDefault(); var upface2 = newsolid2.GetFacesOfGeometryObject().Where(m => m.ComputeNormal(new UV()).IsSameDirection(XYZ.BasisZ)) .FirstOrDefault(); var curveloop1 = upface1.GetEdgesAsCurveLoops().FirstOrDefault(); var curvearray1 = curveloop1.ToCurveArray(); var curveloop2 = upface2.GetEdgesAsCurveLoops().FirstOrDefault(); var curvearray2 = curveloop2.ToCurveArray(); ActiveDoc.Invoke(m => { var newfloor1 = ActiveDoc.Create.NewFloor(curvearray1, floor.FloorType, floor.LevelId.GetElement(ActiveDoc) as Level, false); var newfloor2 = ActiveDoc.Create.NewFloor(curvearray2, floor.FloorType, floor.LevelId.GetElement(ActiveDoc) as Level, false); doc.Delete(floor.Id); }, "newfloor"); //var newfloor2 = // ActiveDoc.Create.NewFloor(, floor.FloorType, floor.LevelId.GetElement(ActiveDoc) as Level, false); //slapshapeEditor.DrawSplitLine() if (this != null) { uiapp.Idling -= OnIdling; } } catch (Exception e1) { MessageBox.Show(e1.ToString()); uiapp.Idling -= OnIdling; } }
/***************************************************/ /**** Private Methods ****/ /***************************************************/ private static List <ISurface> GetOpeningGeometry(Transaction t, Document doc, List <HostObject> hosts, List <ElementId> inserts, FamilyInstance familyInstance, RevitSettings settings = null) { List <List <Solid> > solidsWithOpening = new List <List <Solid> >(); foreach (HostObject h in hosts) { solidsWithOpening.Add(h.Solids(new Options()).Select(x => SolidUtils.Clone(x)).ToList()); } // Rollback and restart of the transaction is needed because otherwise the object, to which familyInstance is pointing can become invalidated. FailureHandlingOptions failureHandlingOptions = t.GetFailureHandlingOptions().SetClearAfterRollback(true); t.RollBack(failureHandlingOptions); t.Start(); List <ISurface> surfaces = new List <ISurface> (); List <CurveLoop> loops = new List <CurveLoop>(); try { doc.Delete(inserts); doc.Regenerate(); for (int i = 0; i < hosts.Count; i++) { HostObject h = hosts[i]; List <Autodesk.Revit.DB.Plane> planes = h.IPanelPlanes(); if (planes.Count == 0) { continue; } List <Solid> fullSolids = h.Solids(new Options()).SelectMany(x => SolidUtils.SplitVolumes(x)).ToList(); if (h is Wall) { fullSolids = fullSolids.Select(x => BooleanOperationsUtils.CutWithHalfSpace(x, planes[0])).ToList(); planes[0] = Autodesk.Revit.DB.Plane.CreateByNormalAndOrigin(-planes[0].Normal, planes[0].Origin); } foreach (Solid s in fullSolids) { foreach (Solid s2 in solidsWithOpening[i]) { BooleanOperationsUtils.ExecuteBooleanOperationModifyingOriginalSolid(s, s2, BooleanOperationsType.Difference); } foreach (Autodesk.Revit.DB.Face f in s.Faces) { PlanarFace pf = f as PlanarFace; if (pf == null) { continue; } if (planes.Any(x => Math.Abs(1 - pf.FaceNormal.DotProduct(x.Normal)) <= settings.DistanceTolerance && Math.Abs((pf.Origin - x.Origin).DotProduct(x.Normal)) <= settings.AngleTolerance)) { loops.AddRange(pf.GetEdgesAsCurveLoops()); } } } } } catch { loops = null; } t.RollBack(failureHandlingOptions); if (loops != null) { surfaces.AddRange(loops.Select(x => new PlanarSurface(x.FromRevit(), null))); } else if (surfaces.Count != 0) { BH.Engine.Reflection.Compute.RecordWarning(String.Format("Geometrical processing of a Revit element failed due to an internal Revit error. Converted opening might be missing one or more of its surfaces. Revit ElementId: {0}", familyInstance.Id)); } return(surfaces); }
/// <summary> /// Boolean union geometric operation, return a new solid as the result /// </summary> /// <param name="solid1">Operation solid 1</param> /// <param name="solid2">Operation solid 2</param> /// <returns>The operation result</returns> public static Solid BooleanOperation_Union(Solid solid1, Solid solid2) { return(BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Union)); }
/// <summary> /// This method takes the solidsList and clips all of its solids between the given range. /// </summary> /// <param name="elem">The Element from which we obtain our BoundingBoxXYZ.</param> /// <param name="geomElem">The top-level GeometryElement from which to gather X and Y /// coordinates for the intersecting solid.</param> /// <param name="range">The IFCRange whose Z values we use to create an intersecting /// solid to clip the solids in this class's internal solidsList. /// If range boundaries are equal, method returns, performing no clippings.</param> public void ClipSolidsList(GeometryElement geomElem, IFCRange range) { if (geomElem == null) { throw new ArgumentNullException("geomElemToUse"); } if (MathUtil.IsAlmostEqual(range.Start, range.End) || SolidsCount() == 0) { return; } double bottomZ; double boundDifference; if (range.Start < range.End) { bottomZ = range.Start; boundDifference = range.End - range.Start; } else { bottomZ = range.End; boundDifference = range.Start - range.End; } // create a new solid using the X and Y of the bounding box on the top level GeometryElement and the Z of the IFCRange BoundingBoxXYZ elemBoundingBox = geomElem.GetBoundingBox(); XYZ pointA = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointB = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Min.Y, bottomZ); XYZ pointC = new XYZ(elemBoundingBox.Max.X, elemBoundingBox.Max.Y, bottomZ); XYZ pointD = new XYZ(elemBoundingBox.Min.X, elemBoundingBox.Max.Y, bottomZ); List <Curve> perimeter = new List <Curve>(); try { perimeter.Add(Line.CreateBound(pointA, pointB)); perimeter.Add(Line.CreateBound(pointB, pointC)); perimeter.Add(Line.CreateBound(pointC, pointD)); perimeter.Add(Line.CreateBound(pointD, pointA)); } catch { // One of the boundary lines was invalid. Do nothing. return; } List <CurveLoop> boxPerimeterList = new List <CurveLoop>(); boxPerimeterList.Add(CurveLoop.Create(perimeter)); Solid intersectionSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boxPerimeterList, XYZ.BasisZ, boundDifference); // cycle through the elements in solidsList and intersect them against intersectionSolid to create a new list List <SolidInfo> clippedSolidsList = new List <SolidInfo>(); Solid currSolid; foreach (SolidInfo solidAndElement in m_SolidInfoList) { Solid solid = solidAndElement.Solid; try { // ExecuteBooleanOperation can throw if it fails. In this case, just ignore the clipping. currSolid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, intersectionSolid, BooleanOperationsType.Intersect); if (currSolid != null && currSolid.Volume != 0) { clippedSolidsList.Add(new SolidInfo(currSolid, solidAndElement.OwnerElement)); } } catch { // unable to perform intersection, add original solid instead clippedSolidsList.Add(solidAndElement); } } m_SolidInfoList = clippedSolidsList; }
/// <summary> /// Boolean difference geometric operation, return a new solid as the result /// </summary> /// <param name="solid1">Operation solid 1</param> /// <param name="solid2">Operation solid 2</param> /// <returns>The operation result</returns> public static Solid BooleanOperation_Difference(Solid solid1, Solid solid2) { return(BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Difference)); }
public double GetWallAsOpeningArea( Element elemOpening, Solid solidRoom) { Document doc = elemOpening.Document; Wall wallAsOpening = elemOpening as Wall; // most likely an embedded curtain wall Options options = doc.Application.Create.NewGeometryOptions(); options.ComputeReferences = true; options.IncludeNonVisibleObjects = true; List <Element> walls = new List <Element>(); walls.Add(wallAsOpening); // To my recollection this won't // pick up an edited wall profile List <List <XYZ> > polygons = GetWallProfilePolygons( walls, options); IList <CurveLoop> solidProfile = XYZAsCurveloop(polygons.First()); Solid solidOpening = GeometryCreationUtilities .CreateExtrusionGeometry(solidProfile, wallAsOpening.Orientation, 1); Solid intersectSolid = BooleanOperationsUtils .ExecuteBooleanOperation(solidOpening, solidRoom, BooleanOperationsType.Intersect); if (intersectSolid.Faces.Size.Equals(0)) { // Then we are extruding in the wrong direction solidOpening = GeometryCreationUtilities .CreateExtrusionGeometry(solidProfile, wallAsOpening.Orientation.Negate(), 1); intersectSolid = BooleanOperationsUtils .ExecuteBooleanOperation(solidOpening, solidRoom, BooleanOperationsType.Intersect); } if (DebugHandler.EnableSolidUtilityVolumes) { using (Transaction t = new Transaction(doc)) { t.Start("Test1"); ShapeCreator.CreateDirectShape(doc, intersectSolid, "namesolid"); t.Commit(); } } double openingArea = GetLargestFaceArea( intersectSolid); LogCreator.LogEntry(";_______OPENINGAREA;" + elemOpening.Id.ToString() + ";" + elemOpening.Category.Name + ";" + elemOpening.Name + ";" + (openingArea * 0.09290304).ToString()); return(openingArea); }