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); }
private double?GetClickDistanceNearSwarm_ALLBOTS(RayHitTestParameters clickRay) { Point3D?closestPoint = null; double minDist = double.MaxValue; foreach (SwarmBot1a bot in _map.GetItems <SwarmBot1a>(false)) { Point3D botPos = bot.PositionWorld; Point3D closest = Math3D.GetClosestPoint_Line_Point(clickRay.Origin, clickRay.Direction, botPos); if (Vector3D.DotProduct(closest - clickRay.Origin, clickRay.Direction) < 0) { // It's behind the camera continue; } double lenSqr = (botPos - closest).LengthSquared; if (lenSqr < minDist) { minDist = lenSqr; closestPoint = closest; } } if (closestPoint == null) { return(null); } return((closestPoint.Value - clickRay.Origin).Length); }
private static Quaternion GetRotation_Circle(Point3D center, Vector3D normal, Point3D from, Point3D to) { // Get the two angles, it doesn't matter where they are along that normal Vector3D fromLine = from - Math3D.GetClosestPoint_Line_Point(center, normal, from); Vector3D toLine = to - Math3D.GetClosestPoint_Line_Point(center, normal, to); return(Math3D.GetRotation(fromLine, toLine)); // I was about to just take the angle, since I already have the normal, but the angle could be greater than 180, so the axis would flip, and the math3d method already accounts for that, so I'm just using it }
private static Point3D?CastRay_LinesCircles(IEnumerable <RayHitTestParameters> lines, IEnumerable <CircleDefinition> circles, Point3D testPoint) { Point3D?retVal = null; double distance = double.MaxValue; if (lines != null) { // Cast onto each line, and return the point that's closest to the test point foreach (RayHitTestParameters line in lines) { Point3D point = Math3D.GetClosestPoint_Line_Point(line.Origin, line.Direction, testPoint); double localDistance = (point - testPoint).LengthSquared; if (retVal == null || localDistance < distance) { retVal = point; distance = localDistance; } } } if (circles != null) { // Cast onto each circle, and return the point that's closest to the test point foreach (CircleDefinition circle in circles) { Point3D?point = Math3D.GetClosestPoint_Circle_Point(circle.Plane, circle.Center, circle.Radius, testPoint); if (point != null) { double localDistance = (point.Value - testPoint).LengthSquared; if (retVal == null || localDistance < distance) { retVal = point.Value; distance = localDistance; } } } } return(retVal); }
private static BezierSegment3D[][] GetModel_Second_Curves(AxeSecondProps arg) { BezierSegment3D[][] retVal = new BezierSegment3D[5][]; AxeSecondProps[] argLevels = new AxeSecondProps[5]; argLevels[2] = arg; // Edge #region Middle argLevels[1] = UtilityCore.Clone_Shallow(arg); // Right argLevels[1].EndTR = new Point3D(argLevels[1].EndTR.X * .8, argLevels[1].EndTR.Y * .9, .15); Vector3D offsetTR = new Point3D(argLevels[1].EndTR.X, argLevels[1].EndTR.Y, 0) - arg.EndTR; Quaternion angleTR = Math3D.GetRotation((arg.EndTL - arg.EndTR), offsetTR); Vector3D rotated = (arg.EndBL_1 - arg.EndBR).ToUnit() * offsetTR.Length; rotated = rotated.GetRotatedVector(angleTR.Axis, angleTR.Angle * -1.3); argLevels[1].EndBR = new Point3D(argLevels[1].EndBR.X + rotated.X, argLevels[1].EndBR.Y + rotated.Y, .15); // can't just use percents of coordinates. Instead use the same offset angle,distance that top left had // Left argLevels[1].EndTL = new Point3D(argLevels[1].EndTL.X, argLevels[1].EndTL.Y * .95, .3); if (argLevels[1].EndBL_2 == null) { argLevels[1].EndBL_1 = new Point3D(argLevels[1].EndBL_1.X, argLevels[1].EndBL_1.Y * .9, .3); } else { Vector3D offsetBL1 = arg.EndBR - arg.EndBL_1; double lengthBL1 = (new Point3D(argLevels[1].EndBR.X, argLevels[1].EndBR.Y, 0) - arg.EndBR).Length; offsetBL1 = offsetBL1.ToUnit() * (offsetBL1.Length - lengthBL1); argLevels[1].EndBL_1 = argLevels[1].EndBR - offsetBL1; argLevels[1].EndBL_1 = new Point3D(argLevels[1].EndBL_1.X, argLevels[1].EndBL_1.Y, .18); argLevels[1].EndBL_2 = new Point3D(argLevels[1].EndBL_2.Value.X, argLevels[1].EndBL_2.Value.Y * .9, .3); } argLevels[3] = argLevels[1].CloneNegateZ(); #endregion #region Far argLevels[0] = UtilityCore.Clone_Shallow(arg); argLevels[0].EndTL = new Point3D(argLevels[0].EndTL.X, argLevels[0].EndTL.Y * .7, .4); argLevels[0].EndTR = new Point3D(argLevels[0].EndTR.X * .5, argLevels[0].EndTR.Y * .6, .25); if (argLevels[0].EndBL_2 == null) { argLevels[0].EndBR = new Point3D(argLevels[0].EndBR.X * .5, argLevels[0].EndBR.Y * .6, .25); argLevels[0].EndBL_1 = new Point3D(argLevels[0].EndBL_1.X, argLevels[0].EndBL_1.Y * .6, .4); } else { // Bottom Right Vector3D offset = (argLevels[1].EndBR - argLevels[1].EndBL_1) * .5; Point3D startPoint = argLevels[1].EndBL_1 + offset; // midway along bottom edge offset = argLevels[1].EndTR - startPoint; argLevels[0].EndBR = startPoint + (offset * .15); // from midway point toward upper right point argLevels[0].EndBR = new Point3D(argLevels[0].EndBR.X, argLevels[0].EndBR.Y, .25); // fix z // Left of bottom right (where the circle cutout ends) offset = argLevels[1].EndBR - argLevels[1].EndBL_1; argLevels[0].EndBL_1 = Math3D.GetClosestPoint_Line_Point(argLevels[0].EndBR, offset, argLevels[0].EndBL_1); offset *= .05; Point3D minBL1 = argLevels[0].EndBR - offset; Vector3D testOffset = argLevels[0].EndBL_1 - argLevels[0].EndBR; if (Vector3D.DotProduct(testOffset, offset) < 0 || testOffset.LengthSquared < offset.LengthSquared) { argLevels[0].EndBL_1 = minBL1; } // Bottom Left argLevels[0].EndBL_2 = new Point3D(argLevels[0].EndBL_2.Value.X, argLevels[0].EndBL_2.Value.Y * .6, .4); // Reduce the curve a bit argLevels[0].B2AngleL = argLevels[0].B2AngleL * .9; argLevels[0].B2PercentL = argLevels[0].B2PercentL * .95; argLevels[0].B2AngleR = argLevels[0].B2AngleR * .85; argLevels[0].B2PercentR = argLevels[0].B2PercentR * .95; } argLevels[4] = argLevels[0].CloneNegateZ(); #endregion for (int cntr = 0; cntr < 5; cntr++) { BezierSegment3D[] segments = GetModel_Second_Segments(argLevels[cntr]); retVal[cntr] = segments; } return(retVal); }
/// <summary> /// This returns the normal of the drag shape relative to the point passed in /// NOTE: I'll wait till this method is needed before finishing it /// </summary> /// <remarks> /// This is important for cases where they are dragging an object across a cylinder or sphere. You would want that object's orientation /// to change so it appears to be stuck to the side of the shape /// /// Drag shape of line(s) just returns null /// </remarks> public Vector3D?GetNormal(Point3D point) { // Can't do this, because if the point goes under the plane, the normal gets reversed //// This should handle most cases //Point3D? pointOnShape = CastRay(point); //if (pointOnShape != null && !Math3D.IsNearValue(point, pointOnShape.Value)) //{ // return point - pointOnShape.Value; //} Vector3D?retVal = null; // The request point is sitting on the shape. Some of the shapes can get more generic switch (_shape) { case ShapeType.Plane: #region Plane retVal = _plane.NormalUnit; // No need to do all this. Execution only gets here when the request point is sitting on the plane //Point3D pointOnPlane = Math3D.GetClosestPoint_Point_Plane(_plane, point); //if (!Math3D.IsNearValue(point, pointOnPlane) && Vector3D.DotProduct(point - pointOnPlane, retVal.Value) < 0) //{ // // The test point is away from the normal, reverse it // retVal = retVal.Value * -1; //} #endregion break; case ShapeType.Cylinder: #region Cylinder // Execution gets here when the request point is sitting on the surface of the cylinder. Treat it like a line and try again Point3D nearestAxisPoint = Math3D.GetClosestPoint_Line_Point(_point, _direction, point); if (!Math3D.IsNearValue(nearestAxisPoint, point)) { retVal = point - nearestAxisPoint; } #endregion break; case ShapeType.Sphere: #region Sphere if (!Math3D.IsNearValue(_point, point)) { retVal = point - _point; } #endregion break; } return(retVal); }
/// <summary> /// This overload returns a point on the shape that is closest to the point passed in /// </summary> public Point3D?CastRay(Point3D point) { Point3D?retVal = null; switch (_shape) { case ShapeType.Line: #region Line retVal = Math3D.GetClosestPoint_Line_Point(_point, _direction, point); #endregion break; case ShapeType.Lines: #region Lines retVal = CastRay_LinesCircles(_lines, null, point); #endregion break; case ShapeType.Plane: #region Plane retVal = Math3D.GetClosestPoint_Plane_Point(_plane, point); #endregion break; case ShapeType.Circle: #region Circle retVal = Math3D.GetClosestPoint_Circle_Point(_plane, _point, _radius, point); #endregion break; case ShapeType.Circles: #region Circles retVal = CastRay_LinesCircles(null, _circles, point); #endregion break; case ShapeType.LinesCircles: #region LinesCircles retVal = CastRay_LinesCircles(_lines, _circles, point); #endregion break; case ShapeType.Cylinder: #region Cylinder retVal = Math3D.GetClosestPoint_Cylinder_Point(_point, _direction, _radius, point); #endregion break; case ShapeType.Sphere: #region Sphere retVal = Math3D.GetClosestPoint_Sphere_Point(_point, _radius, point); #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); }