Mathematical Translations

Mathematical Translations

Anonymous
Not applicable
4,357 Views
10 Replies
Message 1 of 11

Mathematical Translations

Anonymous
Not applicable

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

 

 

0 Likes
4,358 Views
10 Replies
Replies (10)
Message 2 of 11

jeremytammik
Autodesk
Autodesk

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
Advisor
Advisor

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

Anonymous
Not applicable

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
0 Likes
Message 5 of 11

Anonymous
Not applicable

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

0 Likes
Message 6 of 11

jeremytammik
Autodesk
Autodesk

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

0 Likes
Message 7 of 11

aignatovich
Advisor
Advisor

Dear Jeremy!

 

Thank you very much! Man Happy

0 Likes
Message 8 of 11

Anonymous
Not applicable

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

0 Likes
Message 9 of 11

jeremytammik
Autodesk
Autodesk

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

0 Likes
Message 10 of 11

so-chong
Advocate
Advocate

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

Anonymous
Not applicable

Thanks, very much appreciated!

0 Likes