Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Mathematical Translations

10 REPLIES 10
Reply
Message 1 of 11
efortune
1419 Views, 10 Replies

Mathematical Translations

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));
}

 

 

10 REPLIES 10
Message 2 of 11
jeremytammik
in reply to: efortune

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



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 3 of 11
aignatovich
in reply to: efortune

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:

before.PNG

 

After:

after.PNG

 

If not, explore Transform class (and its concept). You can get transform of your family instance using familyInstance.GetTotalTransform() method

Message 4 of 11
efortune
in reply to: jeremytammik

Jeremy,

 
Thank you for taking the time to help!  The cube is not necessarily a cube...it is rectangular in shape.  Items I wish to create that cannot be nested include walls, floors, and ceilings.  The walls which I want to create are both along the exterior faces of the rectangle and inside the area in which it consumes and they are all vertical.  Imagine this rectangle being a living quarters which would consist of bathroom, kitchen, laundry, and the walls that separate those areas.  
 
The use of quadrants was for me to determine which trig function I needed to determine the angle of rotation because I could only find the insertion point along with the BoundingBox Max and Min points.  I am unsure on how to do this in any other fashion, but would love to find a way which would eliminate the quadrants.  I tried to find faces so I could get their geometry information, but I kept getting an error as they always came back null. 
 
On my commute home yesterday, I thought I could be completely overthinking this....which I tend to do often.  I was thinking I could build the walls in at 0 degrees and then rotate them all by the insertion point, which is the stationary point of the rectangle.  I believe this is a lot like what you are describing in the second portion.  However, I am still a little unclear on the translation vector you are describing.
 
Elizabeth
Message 5 of 11
efortune
in reply to: aignatovich

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

Message 6 of 11
jeremytammik
in reply to: aignatovich

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

 

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 11
aignatovich
in reply to: jeremytammik

Dear Jeremy!

 

Thank you very much! Man Happy

Message 8 of 11

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?

 

 

error_automatic_wall_creation.png

Message 9 of 11

I do not generally use macros, so I cannot say for sure.

 

I would try to simply delete the offending line  🙂



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 10 of 11
so-chong
in reply to: daniel.swearson

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>();
            }
        }
    }
}
		
	

 

Message 11 of 11
daniel.swearson
in reply to: so-chong

Thanks, very much appreciated!

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community