I am building an API that will create walls and doors inside a custom family that acts somewhat as an empty area if you will. Since I cannot nest walls within the custom family, I have to create them via the API with (x,y,z) coordinates. When the user places the custom cube (area) family, the walls will get built. The issue is when the user selects a rotation that is not 0 deg, I do not know the translation of coordinates to assign to the wall start and end points. I have completed all the trig to determine the angles and it will account for any angle the user selects. How do I do the Mathematical translations to determine a formula which will help me determine the new start and end points of the walls? I have attached documents to show how I determined the angles using the Bounding Box Mins and Max along with the insertion point.
The knowns I have are:
User selected Length from UI
User selected Width from UI
User selected Insertion Point
Bounding Box Max Point
Bounding Box Min Point
Points pulled from this code:
BoundingBoxXYZ modBox = currMod.get_BoundingBox(doc.ActiveView);
XYZ modBoxMin = modBox.Min;
XYZ modBoxMax = modBox.Max;
startPoint = GetFamLocation(currMod);
//currMod is a custom family in the shape of a cube which I wish to build walls in but cannot due to not being able to nest system families.
Code used for trig:
string quadrant = "";
//------------QUADRANT I--------------//
if ((minXAbs <= 0) && (minYAbs >= 0) && (maxXAbs >= 0) && (maxYAbs >= 0))
{
quadrant = "Q1";
double ang01RAD = Math.Acos(Math.Abs(minXAbs) / massWidthDbl);
double ang01DEG = ang01RAD * (180 / Math.PI);
if (ang01RAD == 1)
ang01DEG = 0;
modAngle = Math.Floor(90 - ang01DEG);
}
//------------QUADRANT II--------------//
else if ((minXAbs <= 0) && (minYAbs <= 0) && (maxXAbs <= 0) && (maxYAbs >= 0))
{
quadrant = "Q2";
double ang01RAD = Math.Acos(Math.Abs(maxYAbs) / massLengthDblUR);
double ang01DEG = ang01RAD * (180 / Math.PI);
modAngle = Math.Floor(90 + ang01DEG);
}
//------------QUADRANT III--------------//
else if ((minXAbs <= 0) && (minYAbs <= 0) && (maxXAbs >= 0) && (maxYAbs <= 0))
{
quadrant = "Q3";
double ang01RAD = Math.Atan(minXAbs / minYAbs);
double ang01DEG = ang01RAD * (180 / Math.PI);
double ang01Cos = Math.Cos(ang01RAD);
double hyp = Math.Abs(minYAbs) / ang01Cos;
double ang02DEG = 90 - ang01DEG;
double ang03DEG = 90 - ang02DEG;
double ang03RAD = ang03DEG * (Math.PI / 180);
double ang03Sin = Math.Sin(ang03RAD);
double ang04Helper = (hyp * ang03Sin) / massLengthDbl;
double ang04suppRAD = Math.Asin(Math.Abs(ang04Helper));
double ang04suppDEG = ang04suppRAD * (180 / Math.PI);
double ang04DEG = 180 - ang04suppDEG;
double ang05DEG = 180 - ang04DEG - ang01DEG;
double ang06DEG = 90 - ang05DEG - ang01DEG;
modAngle = Math.Floor(180 + ang06DEG);
}
//------------QUADRANT IV--------------//
else if ((minXAbs >= 0) && (minYAbs <= 0) && (maxXAbs >= 0) && (maxYAbs >= 0))
{
if (quadrant != "Q1")
{
quadrant = "Q4";
double ang01RAD = Math.Acos(Math.Abs(minYAbs) / massLengthDbl);
double ang01DEG = ang01RAD * (180 / Math.PI);
modAngle = Math.Floor(270 + Math.Round(ang01DEG));
}
Dear Elizabeth,
Thank you for your interesting query.
I looked at your attached 5-page PDF listing about 100 formulas, in my subjective impression.
Sorry, too much for me. Much too much.
As far as I can tell from your verbal description, your problem is small and simple.
Very small and simple.
I have a hunch that the solution is smaller and simpler still.
Are the cubes real cubes? I.e., all three axis lengths are equal?
Are the walls you wish to create vertical?
Do you wish to create walls along the four vertical faces of the cube, or where?
Do you wish to add a floor and a ceiling?
I presume that an easy solution can be found that is valid for all quadrants, without distinction.
I don't know what the purpose of your quadrants is.
I hope this helps.
Second attempt at answering:
You have two points, p and q, representing the start and end point of the wall.
The cube is inserted into the model, defining a transform T consisting of a rotation by an angle `a` and a translation by a vector `v`.
To transform p and q by T, you could first rotate them both around the origin (or whatever axis point you prefer) by `a` and then translate then by `v` to wherever they are supposed to end up.
Does that get us any closer to understanding your question?
Best regards,
Jeremy
Hi!
Look at this code and attached revit file, may be it will be useful for you:
using System.Collections.Generic; using System.Linq; using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Structure; using Autodesk.Revit.Exceptions; using Autodesk.Revit.UI; namespace AutoWallsByCubes { [Transaction(TransactionMode.Manual)] public class CreateWallsAutomaticallyCommand : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { var uiapp = commandData.Application; var uidoc = uiapp.ActiveUIDocument; var doc = uidoc.Document; var cubes = FindCubes(doc); using (var transaction = new Transaction(doc, "create walls")) { transaction.Start(); foreach (var cube in cubes) { var countours = FindCountors(cube) .SelectMany(x => x); var height = cube.LookupParameter("height").AsDouble(); foreach (var countour in countours) { var wall = CreateWall(cube, countour, height); CreateDoor(wall); } } transaction.Commit(); } return Result.Succeeded; } private static Wall CreateWall(FamilyInstance cube, Curve curve, double height) { var doc = cube.Document; var wallTypeId = doc.GetDefaultElementTypeId(ElementTypeGroup.WallType); return Wall.Create(doc, curve.CreateReversed(), wallTypeId, cube.LevelId, height, 0, false, false); } private static void CreateDoor(Wall wall) { var locationCurve = (LocationCurve) wall.Location; var position = locationCurve.Curve.Evaluate(0.5, true); var document = wall.Document; var level = (Level)document.GetElement(wall.LevelId); var symbolId = document.GetDefaultFamilyTypeId(new ElementId(BuiltInCategory.OST_Doors)); var symbol = (FamilySymbol)document.GetElement(symbolId); if (!symbol.IsActive) symbol.Activate(); document.Create.NewFamilyInstance(position, symbol, wall, level, StructuralType.NonStructural); } private static IEnumerable<FamilyInstance> FindCubes(Document doc) { var collector = new FilteredElementCollector(doc); return collector .OfCategory(BuiltInCategory.OST_GenericModel) .OfClass(typeof (FamilyInstance)) .OfType<FamilyInstance>() .Where(x => x.Symbol.FamilyName == "cube"); } private static IEnumerable<CurveLoop> FindCountors(FamilyInstance familyInstance) { return GetSolids(familyInstance) .SelectMany(x => GetCountours(x, familyInstance)); } private static IEnumerable<Solid> GetSolids(Element element) { var geometry = element .get_Geometry(new Options { ComputeReferences = true, IncludeNonVisibleObjects = true }); if (geometry == null) return Enumerable.Empty<Solid>(); return GetSolids(geometry) .Where(x => x.Volume > 0); } private static IEnumerable<Solid> GetSolids(IEnumerable<GeometryObject> geometryElement) { foreach (var geometry in geometryElement) { var solid = geometry as Solid; if (solid != null) yield return solid; var instance = geometry as GeometryInstance; if (instance != null) foreach (var instanceSolid in GetSolids(instance.GetInstanceGeometry())) yield return instanceSolid; var element = geometry as GeometryElement; if (element != null) foreach (var elementSolid in GetSolids(element)) yield return elementSolid; } } private static IEnumerable<CurveLoop> GetCountours(Solid solid, Element element) { try { var plane = Plane.CreateByNormalAndOrigin(XYZ.BasisZ, element.get_BoundingBox(null).Min); var analyzer = ExtrusionAnalyzer.Create(solid, plane, XYZ.BasisZ); var face = analyzer.GetExtrusionBase(); return face.GetEdgesAsCurveLoops(); } catch (InvalidOperationException) { return Enumerable.Empty<CurveLoop>(); } } } }
Before running command:
After:
If not, explore Transform class (and its concept). You can get transform of your family instance using familyInstance.GetTotalTransform() method
Jeremy,
Aignatovich,
Thank you for your time and input. From the looks of your samples, this looks like it will work, but I need time to digest the code. I will look it over!
Some of these concepts I have not used before such as Finding Contours.
Elizabeth
Dear Alexander,
I love your code!
I love your simple and efficient approach!
I love your succinct generic helper functions!
I promoted this to a post on The Building Coder to raise visibility:
http://thebuildingcoder.typepad.com/blog/2017/11/avoid-exorbitant-coordinates.html
I hope many read your code and learn from it.
Thank you very much for sharing this!
Cheers,
Jeremy
I'm trying to follow along with this example you had by creating a macro and I get this error when I try to build, Any idea why?
I do not generally use macros, so I cannot say for sure.
I would try to simply delete the offending line 🙂
i do regulary use macro 🙂
copy and paste should be enough.
many thanks to Alexander for sharing his code.
Cheers,
so-chong
/* * Created by SharpDevelop. * * * To change this template use Tools | Options | Coding | Edit Standard Headers. */ using System; using Autodesk.Revit.UI; using Autodesk.Revit.DB; using Autodesk.Revit.UI.Selection; using Autodesk.Revit.Attributes; using Autodesk.Revit.Exceptions; using Autodesk.Revit.DB.Structure; using System.Collections.Generic; using System.Linq; namespace AutoWallsByCubes { [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] [Autodesk.Revit.DB.Macros.AddInId("AB4E399B-3B90-43ED-A8B5-E10C49F90340")] public partial class ThisApplication { private void Module_Startup(object sender, EventArgs e) { } private void Module_Shutdown(object sender, EventArgs e) { } #region Revit Macros generated code private void InternalStartup() { this.Startup += new System.EventHandler(Module_Startup); this.Shutdown += new System.EventHandler(Module_Shutdown); } #endregion public void AutomaticWallCreation() { var uidoc = this.ActiveUIDocument; var doc = uidoc.Document; var cubes = FindCubes(doc); using (var transaction = new Transaction(doc, "create walls")) { transaction.Start(); foreach (var cube in cubes) { var countours = FindCountors(cube) .SelectMany(x => x); var height = cube.LookupParameter("height").AsDouble(); foreach (var countour in countours) { var wall = CreateWall(cube, countour, height); CreateDoor(wall); } } transaction.Commit(); } } private static Wall CreateWall(FamilyInstance cube, Curve curve, double height) { var doc = cube.Document; var wallTypeId = doc.GetDefaultElementTypeId(ElementTypeGroup.WallType); return Wall.Create(doc, curve.CreateReversed(), wallTypeId, cube.LevelId, height, 0, false, false); } private static void CreateDoor(Wall wall) { var locationCurve = (LocationCurve) wall.Location; var position = locationCurve.Curve.Evaluate(0.5, true); var document = wall.Document; var level = (Level)document.GetElement(wall.LevelId); var symbolId = document.GetDefaultFamilyTypeId(new ElementId(BuiltInCategory.OST_Doors)); var symbol = (FamilySymbol)document.GetElement(symbolId); if (!symbol.IsActive) symbol.Activate(); document.Create.NewFamilyInstance(position, symbol, wall, level, StructuralType.NonStructural); } private static IEnumerable<FamilyInstance> FindCubes(Document doc) { var collector = new FilteredElementCollector(doc); return collector .OfCategory(BuiltInCategory.OST_GenericModel) .OfClass(typeof (FamilyInstance)) .OfType<FamilyInstance>() .Where(x => x.Symbol.FamilyName == "cube"); } private static IEnumerable<CurveLoop> FindCountors(FamilyInstance familyInstance) { return GetSolids(familyInstance) .SelectMany(x => GetCountours(x, familyInstance)); } private static IEnumerable<Solid> GetSolids(Element element) { var geometry = element .get_Geometry(new Options { ComputeReferences = true, IncludeNonVisibleObjects = true }); if (geometry == null) return Enumerable.Empty<Solid>(); return GetSolids(geometry) .Where(x => x.Volume > 0); } private static IEnumerable<Solid> GetSolids(IEnumerable<GeometryObject> geometryElement) { foreach (var geometry in geometryElement) { var solid = geometry as Solid; if (solid != null) yield return solid; var instance = geometry as GeometryInstance; if (instance != null) foreach (var instanceSolid in GetSolids(instance.GetInstanceGeometry())) yield return instanceSolid; var element = geometry as GeometryElement; if (element != null) foreach (var elementSolid in GetSolids(element)) yield return elementSolid; } } private static IEnumerable<CurveLoop> GetCountours(Solid solid, Element element) { try { var plane = Plane.CreateByNormalAndOrigin(XYZ.BasisZ, element.get_BoundingBox(null).Min); var analyzer = ExtrusionAnalyzer.Create(solid, plane, XYZ.BasisZ); var face = analyzer.GetExtrusionBase(); return face.GetEdgesAsCurveLoops(); } catch (Autodesk.Revit.Exceptions.InvalidOperationException) { return Enumerable.Empty<CurveLoop>(); } } } }