Hi all
I find the XYZ class very confusing because it can be a point or a vector.
Does any body knows any good guide on how to use them? Or some documentation.
Also, I have a specific question, how does one extract start and end points of a XYZ vector?
Thanks
Solved! Go to Solution.
Solved by BobbyC.Jones. Go to Solution.
The XYZ class defines methods that are specific to the concept of points and vectors. The biggest problem I had was in reviewing our code; it was difficult to know which concept it was dealing with at any given time.
Another benefit of providing your own point and vector wrappers is that you can limit their interface to only methods that make sense. All of this code compiles, but none of it makes any sense.
public static void PointAndVectorExample(Line revitLine) { var lineOrigin = revitLine.Origin; var whatIstheLengthOfaPoint = lineOrigin.GetLength(); var howIsaPointaUnitLength = lineOrigin.IsUnitLength(); var lineDirection = revitLine.Direction; var whatDoesThisRepresent = lineDirection.CrossProduct(lineOrigin); var thisDoesntMakeSense = lineDirection.DistanceTo(lineOrigin); }
Here are some PARTIAL Point3D and Vector3D classes to give you the idea. And some helper extensions to make using them easier to use. You can take these and wrap the appropriate XYZ methods for each as well as define additional methods of your own. And even implement additional interfaces, such as IEquatable.
public class Point3D { public Point3D(XYZ revitXyz) { XYZ = revitXyz; } public Point3D() : this(XYZ.Zero) {} public Point3D(double x, double y, double z) : this(new XYZ(x, y, z)) {} public XYZ XYZ { get; private set; } public double X => XYZ.X; public double Y => XYZ.Y; public double Z => XYZ.Z; public double DistanceTo(Point3D source) { return XYZ.DistanceTo(source.XYZ); } public Point3D Add(Vector3D source) { return new Point3D(XYZ.Add(source.XYZ)); } public static Point3D operator +(Point3D point, Vector3D vector) { return point.Add(vector); } public override string ToString() { return XYZ.ToString(); } public static Point3D Zero => new Point3D(XYZ.Zero); }
public class Vector3D { public Vector3D(XYZ revitXyz) { XYZ = revitXyz; } public Vector3D() : this(XYZ.Zero) {} public Vector3D(double x, double y, double z) : this(new XYZ(x, y, z)) {} public XYZ XYZ { get; private set; } public double X => XYZ.X; public double Y => XYZ.Y; public double Z => XYZ.Z; public Vector3D CrossProduct(Vector3D source) { return new Vector3D(XYZ.CrossProduct(source.XYZ)); } public double GetLength() { return XYZ.GetLength(); } public override string ToString() { return XYZ.ToString(); } public static Vector3D BasisX => new Vector3D(XYZ.BasisX); public static Vector3D BasisY => new Vector3D(XYZ.BasisY); public static Vector3D BasisZ => new Vector3D(XYZ.BasisZ); }
public static class XYZExtensions { public static Point3D ToPoint3D(this XYZ revitXyz) { return new Point3D(revitXyz); } public static Vector3D ToVector3D(this XYZ revitXyz) { return new Vector3D(revitXyz); } }
public static class LineExtensions { public static Vector3D Direction(this Line revitLine) { return new Vector3D(revitLine.Direction); } public static Point3D Origin(this Line revitLine) { return new Point3D(revitLine.Origin); } }
Ha! Don't we all have much to learn; I certainly do! You're very welcome and I hope this helped get you started. And don't hesitate to ask if you have any more questions.
Edited and summarised for posterity:
http://thebuildingcoder.typepad.com/blog/2017/08/birthday-post-on-the-xyz-class.html
Cheers,
Jeremy
Hi Jeremy,
Very cool! In looking over that code I noticed a call to an extension method that I did not supply. For completeness sake:
public static class DoubleExtensions {
private const double Tolerance = 1.0e-10;
public static bool IsAlmostEqualTo(this double double1, double double2) {
var isAlmostEqual = Math.Abs(double1 - double2) <= Tolerance;
return isAlmostEqual; }
}
Thank you!
Added to the post.
Cheers,
Jeremy
Thank you very much for help on this topic. I've finally come around to implement the ConnectorXYZComparer in my codez and, while developing a new utility which at one point seeks to find distinct pipe connectors based on location in space by comparing Connector.Origin which is a XYZ, I have discovered that some connectors can have a much larger tolerance than the 1.0e-9 which is used in the BuildingCoder samples and utils.
In my test set, which is around 800 connectors, I got 32 false negatives. The largest delta is at 0.0004... ft. See the attached text file for my test results. So the HashString method from the ConnectorXYZComparer, which writes the coordinates to 9th decimal place returns slightly different hashstrings for connectors at the same location, but with large deltas. Also the Equals method operating with 1.0e-9 tolerance is returning false negatives.
So anybody using the Comparer provided by Jeremy is going to have some false negatives and wrong results when working with XYZs from MEP connectors.
To fix this I had to up the tolerance on the Equals method to 0.00328 ft -> roughly 1 mm, which is okay, because I will never have two distinct groups of connectors at distances lower than 1 mm, and I could probably have set it even higher safely.
Second part is that the hash string has to have only two decimals or else I was getting with four decimals:
(13.6971,19.1862,11.0728)
(13.6971,19.1866,11.0728)
And with three decimals:
(13.697,19.186,11.073)
(13.697,19.187,11.073)
I *HOPE* that two decimals is enough to cover the wildest imaginable tolerance for Revit piping Connectors...
Dear Mgo,
Thank you for your update, research and results.
I agree that 1 mm is a sensible threshold to use.
In fact, I often an integer-based coordinate system by rounding every single coordinate value to the closest millimetre when processing Revit models:
Best regards,
Jeremy