Community

Revit API Forum

Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.

Turn on suggestions

Auto-suggest helps you quickly narrow down your search results by suggesting possible matches as you type.

This page has been translated for your convenience with an automatic translation service. This is not an official translation and may contain errors and inaccurate translations. Autodesk does not warrant, either expressly or implied, the accuracy, reliability or completeness of the information translated by the machine translation service and will not be liable for damages or losses caused by the trust placed in the translation service.
Translate

Topic Options

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page

Message 1 of 19

10-29-2019
08:06 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-29-2019
08:06 AM

Dear all,

I am relative new to revit api and was looking into transformations, trying to use them to project geometry (a curve) onto a plane. This should come out of the box, so I thought.

When apllying the following transformation

1,0,0,0

0,1,0,0

0,0,0,0

0,0,0,1 // for completness

to a curve I get a "transform is not conformal" error.

Is there any way to do this in revit or do I need to reinvent the wheel and write the transformations for each curve type myself?

Best

Richard

Solved! Go to Solution.

Solved by Revitalizer. Go to Solution.

Solved by jeremytammik. Go to Solution.

18 REPLIES 18

Message 2 of 19

10-29-2019
10:11 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-29-2019
10:11 AM

Dear Richard,

I guess I said most of what I have to say on this in my answers to your comments on The Building Coder:

- https://thebuildingcoder.typepad.com/blog/2008/12/polygon-transformation.html#comment-4669997409
- https://thebuildingcoder.typepad.com/blog/2008/12/polygon-transformation.html#comment-4670106610

Using such a projection is obviously a good idea, in principle.

However, Revit transformations are built for BIM purposes.

You cannot apply a non-conformal transformation to a door or window and still expect it to comply with the building codes and standards.

So, how to project your 3D curve to 2D depends on what you want to achieve and do with it.

If you can live with an approximation, I would suggest you tessellate the curve.

That produces a series of line segments, and those are easy to project any way you like.

If that is not an option and you need precise curve results, I am indeed afraid you might have to write the transformations for each curve type yourself.

Alternatively, you might be able to hook up with some external geometric library that can support you in that transformation.

I have researched that topic briefly multiple times, and all the 3D libraries that I looked at were rather large and complex for my immediate needs.

However, I think any good reliable powerful library that you can connect will do the job.

So, I have no specific preference in that area. There are a huge number of them out there, some are immensely powerful, some are good and relatively unknown. Several CAD vendors have open-sourced significant libraries.

Please do let us know how you end up solving this.

Thank you!

Best regards,

Jeremy

Message 3 of 19

10-30-2019
06:49 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-30-2019
06:49 AM

Well I went the long way and wrote projection methodes for all curve types (except CylindricalHelix ), as for lines it is straight forward, but I am not so sure with hermitSplines / NurbsSplines, maybe someone could comment on that, maybe there are problems I have missed...

I have tested it for some occations and two things that happened where:

a) projected curves where longer than edges of faces from which they where generated...

b) projected hermitSplines didn't rebuild correctly, which might be becaus you can only define start / end tangents and not the complete list when creating a curve.

c) Ellipse.createcurve() is a little too clever since it will create an arc if the two radius are equal... -> resulted in a endless loop...

private static Curve Project(Autodesk.Revit.DB.NurbSpline nurbsSpline, Autodesk.Revit.DB.Plane plane) { XYZ[] controlPoints = GeometryHelper.ProjectPoint(nurbsSpline.CtrlPoints, plane); double[] knots = new double[nurbsSpline.Knots.Size]; for(int i = 0; i < knots.Length;i++) { knots[i] = nurbsSpline.Knots.get_Item(i); } double[] weights = new double[nurbsSpline.Weights.Size]; for (int i = 0; i < weights.Length; i++) { weights[i] = nurbsSpline.Weights.get_Item(i); } return NurbSpline.CreateCurve(nurbsSpline.Degree, knots, controlPoints, weights); }

private static Curve Project(Autodesk.Revit.DB.HermiteSpline hermiteSpline, Autodesk.Revit.DB.Plane plane) { XYZ[] tangents= GeometryHelper.ProjectVector(hermiteSpline.Tangents,plane); XYZ[] controlPoints = GeometryHelper.ProjectPoint(hermiteSpline.ControlPoints, plane); HermiteSplineTangents hermiteSplineTangents = new HermiteSplineTangents() { StartTangent = tangents[0].Normalize(),EndTangent = tangents[tangents.Length - 1].Normalize() }; return HermiteSpline.Create(controlPoints, hermiteSpline.IsPeriodic, hermiteSplineTangents); }

Message 4 of 19

10-30-2019
10:57 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-30-2019
10:57 AM

Well done! Sounds like pretty good progress. I cannot say anything off-hand about the issues you observed, though, so I passed on your questions to the development team for you. I hope to hear back from them soon.

Message 5 of 19

10-31-2019
03:06 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
03:06 AM

Feedback so far from the development team:

If Revit's public API does not include a function to project a curve to a plane (or more generally, to a surface), and it looks like it doesn't, you can request that such a function be added. This wouldn't be done immediately. Note that there are various complications - the curve may project to a self-intersecting curve, or one with singularities such as cusps, or to a degenerate curve (a point), and such curves may cause downstream problems. For projecting a curve onto a surface, part of the curve may miss the surface when projected, and one might want to distinguish between projecting orthogonal to the surface and projecting along a given direction.

You could file a wish list item for this in the Revit Idea Station and ensure it gets many votes. However, it would take time to plan and implement, of course.

I'll let you know if they add anything else, e.g., take a look at and comment on your sample code.

Message 6 of 19

10-31-2019
03:15 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
03:15 AM

Hi,

there is a workaround for the projection of curves to a plane.

From your curves, create ModelCurves.

Depending on context (project or family), you can create DetailCurves or SymbolicCurves in a given view.

Document.ConvertModelToDetailCurves or

Document.ConvertModelToSymbolicCurves

In the RevitAPI.chm, it says in the text:

"The lines are projected on the view plane. If the lines are not parallel to the view plane, lines are foreshortened and arcs are converted to ellipses. Splines are modified."

Get the DetailCurve.GeometryCurve from the resulting elements and your are done.

Since you cannot create (2D) views for arbitrary planes but for vertical or horizontal ones only, this workaround is limited.

Revitalizer

Message 7 of 19

10-31-2019
03:19 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
03:19 AM

Thanks for the effort, dont't get me wrong when saying this, I doubt that something is going to happen... but I had a little conversation incuding a wish for Inventor a while ago. It wasn't accepting surface generated from another program (Rhino3d) for unrolling them even so I got the feedback that the surface was perfectly developable.

So that didn't change...

Message 8 of 19

10-31-2019
03:25 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
03:25 AM

Wow, Rudi, absolutely brilliant!

If you really want to work around the restriction to horizontal and vertical planes, you can add another twist (plus reverse twist) to the process as follows:

- Apply a suitable transformation to the curve before converting it to a model line
- Project the model line onto a horizontal or vertical plane
- Apply the reverse transformation to the horizontal or vertical planar result to get it positioned where you want it

Haha!

Message 9 of 19

10-31-2019
03:36 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
03:36 AM

Hi Jeremy,

simple task, surprising workaround - that's Revit API at its best.

That would be a nice entry for your blog.

Rudi

Message 10 of 19

10-31-2019
04:15 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
04:15 AM

Definitly a thing to try out. I had some issues with preciosion and further boolean operations with my projection algorithm, I am wondering if this is a more precise way to do so?

Have to test.

Message 11 of 19

10-31-2019
04:44 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
04:44 AM

Hi,

as for precision, I think that there may be inaccuracies, due to the fact that we create elements (which react to other elements, views etc.).

Revit's AI may adjust elements according to its will.

Also, further limitations are apparent.

There is a ShortCurveTolerance constant, "the enforced minimum length for any curve created by Revit", as the documentation says.

So the conversion methods will fail if a projected result would contain such a curve.

Imagine a line in the plane's normal direction - it would result in a point on the plane.

But after all, I didn't try this myself.

You could project some control points to the plane (start point, end point) and check the projected curves' related points.

So you could get a tolerance value.

Revitalizer

Message 12 of 19

10-31-2019
04:59 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
04:59 AM

Will give it a try, but might stick to my implmenetation, at least I know were the inprecision is...

Message 13 of 19

10-31-2019
02:01 PM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

10-31-2019
02:01 PM

It would be absolutely great if you could share your current implementation with all its imprecision aspects documented plus the results of testing Rudi's suggestion. That would be awesome material for a nicely rounded-off discussion by The Building Coder. Thank you!

Message 14 of 19

11-01-2019
08:26 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

11-01-2019
08:26 AM

Here is a comment from the development team on your projection implementation:

In the second function, which projects a Hermite spline curve onto a plane, the user should not normalise the tangent vectors. The lengths of the start and end tangent vectors (i.e., first derivatives) affect the shape of the spline curve.The first function, which projects Nurbs spline curves onto a plane, looks correct. The user didn't seem to indicate any problems with that function, but if I misunderstood, let me know. The fact that the Ellipse constructor will create an Arc if the ellipse happens to be circular is intentional, because Revit expects curves and surfaces to be represented in the simplest possible form, with an arc being considered simpler than an ellipse. I don't know what the user's first comment means ("projected curves where longer than edges of faces from which they where generated").

I hope this helps.

Message 15 of 19

11-04-2019
11:34 PM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

11-04-2019
11:34 PM

Some more feedback from the development team on the ideas discussed above:

Regarding Document.ConvertModelToDetailCurves and Document.ConvertModelToSymbolicCurves: the user should be aware that if they produce curves that Revit considers invalid, such as self-intersecting curves or zero-length curves, they may cause problems if they're used in creating Revit geometry (or in other contexts for that matter). However, a quick look at the internal function those API functions call indicates that it does check if the projection curve (i.e., the result) would have singularities or self-intersections and does not return a result in such cases. So, I would guess that the same is true for the two API functions.

Note, too, that there are cases when users might want to allow the projection curve to be self-intersecting. For example, if a full circle lying in a vertical plane is projected to a horizontal plane, some users might want that operation to succeed and to return a line segment representing the "shadow" of the circle (as if the sun were directly overhead). I don't believe the Revit API provides such a function.

Message 16 of 19

11-07-2019
01:05 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

11-07-2019
01:05 AM

Dear Richard,

How did you end up solving this, please, so we can wrap it up?

Thank you!

Cheers,

Jeremy

Message 17 of 19

04-28-2023
01:28 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

04-28-2023
01:28 AM

*Dear Jeremy, I have written several methods for projecting points on a plane and visualizing the calculated geometry, for a better understanding of what is happeningA method of projecting a point onto a plane defined by two points p1 and p2*

```
public static XYZ PointAtPlane(XYZ point, XYZ start, XYZ end)
{
// Find the vector "v" between the start and end points:
XYZ v = end - start;
// Find the unit vector "n" of the normal to the plane passing through the given start and end points:
XYZ n = v.Normalize();
// Find the vector "u" from the center of the plane start to a given point point:
XYZ u = point - start;
// Find the distance "d" from the center of the plane to the projection of the point point on the plane:
double d = u.DotProduct(n);
// Find the projection p of a given point point on a plane:
XYZ result = point - d * n;
return result;
}
```

This method creates a square in the plane of the facade "In front"

```
//This method creates a square in the plane of the facade "In front"
public static List<XYZ> CreateRect(double hight, double wight)
{
return new List<XYZ>
{
new XYZ(-wight / 2, 0, hight / 2), // leftUp
new XYZ(wight / 2, 0, hight / 2), // rightUp
new XYZ(wight / 2, 0, -hight / 2), // rightDown
new XYZ(-wight / 2, 0, -hight / 2) // leftDown
};
}
```

*Next, using the Create Model Line method (described below), we draw lines in the model that visualize all the vectors of the points used.*

```
public static List<XYZ> CreateVisual(XYZ p1, XYZ p2, List<XYZ> listPoint)
{
Element red = Main.Doc.GetElement(new ElementId(490985));
Element orange = Main.Doc.GetElement(new ElementId(421355));
Element green = Main.Doc.GetElement(new ElementId(78));
CreateModelLine(Main.Doc, p1, p2, red);
List<XYZ> lPtAtPl = new List<XYZ>();
foreach (XYZ point in listPoint)
{
CreateModelLine(Main.Doc, XYZ.Zero, point, orange);
XYZ p = PointAtPlane(point, p1, p2);
CreateModelLine(Main.Doc, XYZ.Zero, p, green);
lPtAtPl.Add(p);
}
}
```

The "red" line style is used to display the line that defines the normal of the plane on which the point vectors of the previously created rectangle should be projected. The orange line style displays the point vectors of the original rectangle. The blue lines represent just the projection vectors of the points of the original rectangle on the plane specified by points p1 and p2

Demonstration of the projection of the rectangle createRect(10, 10) and the plane defined by the points new XYZ(0,0,0) and new XYZ(1, 2, 2)

The ModelCurve creation method is described below

```
public static ModelCurve CreateModelLine(Document Doc, XYZ start, XYZ end, Element lineStyle)
{
ModelCurve modelLine = null;
try
{
Line geomLine = Line.CreateBound(start, end);
XYZ lineDirection = end - start;
XYZ normal = lineDirection.CrossProduct(XYZ.BasisZ).Normalize();
if (normal.IsZeroLength())
{
normal = lineDirection.CrossProduct(XYZ.BasisY).Normalize();
if (normal.IsZeroLength())
{
normal = lineDirection.CrossProduct(XYZ.BasisX).Normalize();
}
}
Plane plane = Plane.CreateByNormalAndOrigin(normal, start);
Plane geomPlane = Plane.CreateByNormalAndOrigin(plane.Normal, start);
SketchPlane sketch = SketchPlane.Create(Doc, geomPlane);
modelLine = Doc.Create.NewModelCurve(geomLine, sketch);
modelLine.LineStyle = lineStyle;
}
catch { }
return modelLine;
}
```

Message 18 of 19

04-28-2023
01:53 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

04-28-2023
01:53 AM

Thank you for sharing. I am travelling right now. I'll take a closer look asap. Meanwhile, can you compare your methods with similar functions in The Building Coder samples Creator and Util modules?

```
/// <summary>
/// Project given 3D XYZ point onto plane.
/// </summary>
public static XYZ ProjectOnto(
this Plane plane,
XYZ p)
```

Thank you!

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open

Message 19 of 19

04-28-2023
02:00 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report

04-28-2023
02:00 AM

Thanks for the link, I haven't seen it before. Regarding my solution, I think I have found a more optimal solution that does the work of my PointAtPlane method. It was found in CmdDetailCurves.cs in the BuildingCoder folder by your link

```
private XYZ ProjectPointOntoPlane(
XYZ point,
XYZ planeNormal)
{
var a = planeNormal.X;
var b = planeNormal.Y;
var c = planeNormal.Z;
var dx = (b * b + c * c) * point.X - a * b * point.Y - a * c * point.Z;
var dy = -(b * a) * point.X + (a * a + c * c) * point.Y - b * c * point.Z;
var dz = -(c * a) * point.X - c * b * point.Y + (a * a + b * b) * point.Z;
return new XYZ(dx, dy, dz);
}
```

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page