Project Method Returning Null and Failing to Get Correct UV

Project Method Returning Null and Failing to Get Correct UV

ChristianMolino
Enthusiast Enthusiast
214 Views
1 Reply
Message 1 of 2

Project Method Returning Null and Failing to Get Correct UV

ChristianMolino
Enthusiast
Enthusiast

I have a project that inserts a precast concrete connection family onto the surface of another family representing a concrete double tee and then applies a rotation to that family to orient it correctly. In order to apply that rotation, I am trying to get the normal of a hermite face at a point to use as the axis of rotation. This point is already calculated from being projected onto the surface for insertion of the family and now I am converting it to a UV point so that I can use hermiteFace.ComputeNormal(UVPoint).

This is working great in my test environment, however now that I am trying it in another Revit project, the Project method is failing and just returning null, preventing me from getting the normal needed at that point. The hermite face is relatively flat, with just some slight sloping/twisting on it for drainage.

 

Is there a way to resolve this error or a better way to get the normal that does not require the use of the Project method to get the UV point?

 

Image for reference of the families

ChristianMolino_0-1756152916887.png

 

        public static void ApplyRotation(Document doc, FamilyInstance instance, XYZ point, double startRotationRadians, Reference targetFaceRef)
        {
            double rotationTolerance = 0.001;
            if (instance == null || Math.Abs(startRotationRadians) < rotationTolerance) return;
            // Get the face from the reference
            Element hostElement = doc.GetElement(targetFaceRef.ElementId);

            GeometryObject geometryObject = hostElement?.GetGeometryObjectFromReference(targetFaceRef);

            if (geometryObject == null)
            {
                System.Diagnostics.Debug.WriteLine("ApplyRotation Error: Could not retrieve geometry object from reference.");
                return;
            }

            XYZ faceNormal;
            if (geometryObject is PlanarFace planarFace)
            {
                faceNormal = planarFace.FaceNormal;
            }
            else if (geometryObject is CylindricalFace cylindricalFace)
            {
                XYZ origin = cylindricalFace.Origin;
                XYZ axis = cylindricalFace.Axis;
                XYZ pointOnAxis = origin + (point - origin).DotProduct(axis) * axis;
                faceNormal = (point - pointOnAxis).Normalize();
                if (faceNormal.Z < 0) faceNormal = -faceNormal;
            }
            else if (geometryObject is HermiteFace hermiteFace)
            {
                // Project the point onto the HermiteFace to get UV coordinates
                //UV uv = hermiteFace.Project(point)?.UVPoint ?? new UV(0, 0); // Default to (0,0) if projection fails

                IntersectionResult projectResult = hermiteFace.Project(point);
                UV uv;
                double dist = 0;

                if (projectResult == null)
                {
                    MessageBox.Show($"Project failed on HermiteFace ID {geometryObject.Id}. Point: {point}", "Debug: Project Failed");
                    return;
                }
                else
                {
                    uv = projectResult.UVPoint;
                    dist = projectResult.Distance;
                    Debug.WriteLine($"Project success on HermiteFace ID {geometryObject.Id}. UV: {uv.U},{uv.V}, Distance: {dist}, Closest Point: {projectResult.XYZPoint}");
                    if (dist > 0.001) // Arbitrary small tolerance in feet; adjust as needed
                    {
                        MessageBox.Show($"High distance ({dist}) on HermiteFace ID {geometryObject.Id}. Point may not be precisely on face.", "Debug: High Distance");
                    }
                }

                faceNormal = hermiteFace.ComputeNormal(uv);
                if (faceNormal.IsZeroLength()) // Handle potential computation failure
                {
                    MessageBox.Show("ApplyRotation Warning: Failed to compute normal for HermiteFace. Using fallback - ensure accuracy after insertion.");
                    faceNormal = XYZ.BasisZ; // Fallback to Z-axis if normal computation fails
                }
                faceNormal = faceNormal.Normalize();

                MessageBox.Show($"Face ID:{geometryObject.Id}\nFace Normal: {faceNormal.ToString()}\nUV: {uv.ToString()}");
            }
            else
            {
                MessageBox.Show("ApplyRotation Warning: Unsupported face type, skipping rotation.");
                return;
            }
            Line rotationAxis = Line.CreateBound(point, point + faceNormal);
            ElementTransformUtils.RotateElement(doc, instance.Id, rotationAxis, startRotationRadians);
        }
0 Likes
215 Views
1 Reply
Reply (1)
Message 2 of 2

longt61
Advocate
Advocate

The Face.Project() method can only project a point onto a face if the projected point is:

on the face

forms a line with the original point which is perpendicular to the face at the location of project point.

Therefore, that method might fail if the projected point is outside of the face 's outer most edge loop. The easiest way to solve this is that you switch to the Face.GetSurface().Project() method, then get the XYZ from the return UV using Face.Evaluate(). You can refer to the attached image below where I tried to project origin to 4 elements with hermite faces. The top, left and right element contain projected point within their face boundaries (visualized by green lines), while the bottom element does not (visualized by the blue line).

longt61_0-1756977685219.png

Hope this answers your question. 

 

0 Likes