private static Point3D?CastRay_Cylinder(Point3D point, Vector3D direction, double radius, RayHitTestParameters mouseDownClickRay, RayHitTestParameters mouseDownCenterRay, RayHitTestParameters currentClickRay) { // Get points on the cylinder Point3D?mouseDownClickPoint = null; Point3D?mouseDownCenterPoint = null; Point3D?currentClickPoint = null; Point3D[] nearestCylinderPoints, nearestLinePoints; if (Math3D.GetClosestPoints_Cylinder_Line(out nearestCylinderPoints, out nearestLinePoints, point, direction, radius, currentClickRay.Origin, currentClickRay.Direction, Math3D.RayCastReturn.ClosestToRay)) { currentClickPoint = nearestCylinderPoints[0]; if (Math3D.GetClosestPoints_Cylinder_Line(out nearestCylinderPoints, out nearestLinePoints, point, direction, radius, mouseDownClickRay.Origin, mouseDownClickRay.Direction, Math3D.RayCastReturn.ClosestToRay)) { mouseDownClickPoint = nearestCylinderPoints[0]; if (Math3D.GetClosestPoints_Cylinder_Line(out nearestCylinderPoints, out nearestLinePoints, point, direction, radius, mouseDownCenterRay.Origin, mouseDownCenterRay.Direction, Math3D.RayCastReturn.ClosestToRay)) { mouseDownCenterPoint = nearestCylinderPoints[0]; } } } if (mouseDownCenterPoint == null || mouseDownClickPoint == null || currentClickPoint == null) { return(currentClickPoint); // it doesn't matter if this one is null or not, the offset can't be determined, so just return the raw click value } // Circle only cared about an offset angle, but cylinder needs two things: // Offset line (the part of the offset that is parallel to the cylinder's axis) // Offset angle (the part of the offset that is perpendicular to the cylinder's axis) Vector3D offsetLinear = (mouseDownCenterPoint.Value - mouseDownClickPoint.Value).GetProjectedVector(direction); Quaternion offsetRadial = GetRotation_Circle(point, direction, mouseDownClickPoint.Value, mouseDownCenterPoint.Value); //TODO: Get the radial offset working as well (sphere is also messed up, the same fix should work for both) //TODO: See if this is the most effiecient way or not Transform3DGroup transform = new Transform3DGroup(); //transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(offsetRadial))); transform.Children.Add(new TranslateTransform3D(offsetLinear)); //TODO: Bring currentClickPoint into local coords, do the transform, then put it back into global coords // Find the point along the cylinder's axis that is nearest to the current click. This will become the center of the model coords Point3D modelCenter = Math3D.GetClosestPoint_Line_Point(point, direction, currentClickPoint.Value); // Shift the click point into model coords Vector3D modelClick = currentClickPoint.Value - modelCenter; // Adjust by the offset transform (needed to put into model coords, because there is a rotation) modelClick = transform.Transform(modelClick); // Now put back into world coords return(modelCenter + modelClick); }
/// <summary> /// This overload is a straight ray cast, nothing extra /// </summary> public Point3D?CastRay(RayHitTestParameters ray) { Point3D?retVal = null; switch (_shape) { case ShapeType.Line: #region Line Point3D?point1, point2; if (Math3D.GetClosestPoints_Line_Line(out point1, out point2, _point, _direction, ray.Origin, ray.Direction)) { retVal = point1.Value; } #endregion break; case ShapeType.Lines: #region Lines retVal = CastRay_LinesCircles(_lines, null, ray); #endregion break; case ShapeType.Plane: #region Plane retVal = Math3D.GetIntersection_Plane_Line(_plane, ray.Origin, ray.Direction); #endregion break; case ShapeType.Circle: #region Circle Point3D[] nearestCirclePoints, nearestLinePoints; if (Math3D.GetClosestPoints_Circle_Line(out nearestCirclePoints, out nearestLinePoints, _plane, _point, _radius, ray.Origin, ray.Direction, Math3D.RayCastReturn.ClosestToRay)) { retVal = nearestCirclePoints[0]; } #endregion break; case ShapeType.Circles: #region Circles retVal = CastRay_LinesCircles(null, _circles, ray); #endregion break; case ShapeType.LinesCircles: #region LinesCircles retVal = CastRay_LinesCircles(_lines, _circles, ray); #endregion break; case ShapeType.Cylinder: #region Cylinder Point3D[] nearestCylinderPoints, nearestLinePoints2; if (Math3D.GetClosestPoints_Cylinder_Line(out nearestCylinderPoints, out nearestLinePoints2, _point, _direction, _radius, ray.Origin, ray.Direction, Math3D.RayCastReturn.ClosestToRay)) { retVal = nearestCylinderPoints[0]; } #endregion break; case ShapeType.Sphere: #region Sphere Point3D[] nearestSpherePoints, nearestLinePoints3; Math3D.GetClosestPoints_Sphere_Line(out nearestSpherePoints, out nearestLinePoints3, _point, _radius, ray.Origin, ray.Direction, Math3D.RayCastReturn.ClosestToRay); { retVal = nearestSpherePoints[0]; } #endregion break; case ShapeType.Mesh: throw new ApplicationException("finish this"); case ShapeType.None: retVal = null; break; default: throw new ApplicationException("Unknown ShapeType: " + _shape.ToString()); } return(retVal); }