/// <summary> /// Inspector - move a position by its velocity. /// </summary> /// <param name="position">The position.</param> /// <param name="velocity">A velocity vector perpendicular to the position. The magnitude is the radians to rotate (can be zero or greater).</param> /// <returns>The mext position after adjusting by velocity.</returns> public static Vector3 move(Vector3 position, Vector3 velocity) { float speed_in_radians = velocity.magnitude; Vector3 direction = velocity.normalized; return(PlanetariaMath.spherical_linear_interpolation(position, direction, speed_in_radians)); }
/// <summary> /// Inspector - Get the normal at a particular angle. /// </summary> /// <param name="angle">The angle in radians along the arc.</param> /// <param name="extrusion">The radius to extrude.</param> /// <returns>A normal on the arc.</returns> public Vector3 normal(float angle, float extrusion = 0f) // TODO: delegate to position() [bug-prone when adding PI/2] { if (curvature == ArcType.ConcaveCorner) // Concave corners are "inside-out" { extrusion *= -1; } float actual_elevation = arc_latitude + extrusion; if (actual_elevation >= -Mathf.PI / 2) { Vector3 equator_position = PlanetariaMath.spherical_linear_interpolation(forward_axis, right_axis, angle); Vector3 result = PlanetariaMath.spherical_linear_interpolation(equator_position, center_axis, actual_elevation + Mathf.PI / 2); return(curvature == ArcType.ConcaveCorner ? -result : result); } else // if (actual_elevation < -Mathf.PI/2) // Primarily used for concave corners { actual_elevation += Mathf.PI / 2; actual_elevation /= Mathf.Cos(half_angle); actual_elevation -= Mathf.PI / 2; if (curvature == ArcType.ConcaveCorner || curvature == ArcType.ConvexCorner) // Concave corners are "inside-out" { angle *= -1; } Vector3 normal_position = PlanetariaMath.spherical_linear_interpolation(forward_axis, center_axis, actual_elevation - Mathf.PI / 2); Vector3 result = PlanetariaMath.spherical_linear_interpolation(normal_position, right_axis, angle); return(curvature == ArcType.ConvexCorner ? -result : result); } }
// Preview rendering of this function is non-trivial, since ephemeral edges only work for the most recent edge. public static PlanetariaShape create_equilateral(PlanetariaShape shape, Vector3 center, Vector3 vertex, int faces, PlanetariaShape.AppendMode permanence = PlanetariaShape.AppendMode.OverwriteWithPermanent) { if (faces > 0 && center != vertex) { if (faces >= 2) { Vector3 forward = center.normalized; Vector3 right = Vector3.ProjectOnPlane(vertex, forward).normalized; Vector3 up = Vector3.Cross(forward, right).normalized; float phi = Vector3.Angle(vertex, center) * Mathf.Deg2Rad; List <Vector3> vertices = new List <Vector3>(); for (float face_index = 0; face_index < faces; ++face_index) { Vector3 equatorial_position = PlanetariaMath.spherical_linear_interpolation(right, up, -(face_index / faces) * (Mathf.PI * 2)); Vector3 final_position = PlanetariaMath.spherical_linear_interpolation(forward, equatorial_position, phi); vertices.Add(final_position); } List <SerializedArc> polygon = new List <SerializedArc>(); for (int face_index = 0; face_index < faces; ++face_index) { Vector3 start_point = vertices[face_index]; Vector3 end_point = vertices[(face_index + 1) % faces]; polygon.Add(ArcFactory.line(start_point, end_point)); } shape.append(polygon, permanence); return(shape); } else // create a circle with given radius { // first_vertex is circle start Vector3 right = Vector3.Cross(center, vertex).normalized; Vector3 mirror = Vector3.Cross(center, right).normalized; Vector3 hidden_vertex = Vector3.Reflect(vertex, mirror).normalized; // opposite end of circle start Vector3 first_up = Vector3.Cross(vertex, right).normalized; Vector3 first_tangent = -Vector3.Cross(first_up, vertex).normalized; Vector3 second_up = -Vector3.Cross(hidden_vertex, right).normalized; Vector3 second_tangent = -Vector3.Cross(second_up, hidden_vertex).normalized; SerializedArc upper_circle = ArcFactory.curve(vertex, first_tangent, hidden_vertex); SerializedArc lower_circle = ArcFactory.curve(hidden_vertex, second_tangent, vertex); // TODO: this entire function can be replaced now (with the circle generator) shape.append(new List <SerializedArc>() { upper_circle, lower_circle }, permanence); return(shape); } } shape.append(new List <SerializedArc>(), permanence); return(shape); }
/// <summary> /// Inspector - Finds the point at "angle" extruded "extrusion" along "local_angle" direction. /// </summary> /// <param name="arc">The arc (used to determine positions and relative angles).</param> /// <param name="angle">The angle along the arc path. Range: [-arc.angle()/2, +arc.angle()/2]</param> /// <param name="local_angle">The secant angle relative to the arc at position("angle"). Range: [0, 2PI]</param> /// <param name="extrusion">The distance along "local_angle" to extrude.</param> /// <returns>The relative position after extruding the point at "angle" by "extrusion" along "local_angle".</returns> public static Vector3 relative_point(Arc arc, float angle, float local_angle, float extrusion) { Vector3 from = arc.position(angle); Vector3 local_direction = Bearing.bearing(arc.position(angle), arc.normal(angle), local_angle); Vector3 to = PlanetariaMath.spherical_linear_interpolation(from, local_direction, extrusion); return(to); }
private void aerial_move(float delta) { Vector3 next_position = PlanetariaMath.spherical_linear_interpolation(get_position(), velocity.normalized, delta); // Note: when velocity = Vector3.zero, it luckily still returns "position" intact. Vector3 next_velocity = PlanetariaMath.spherical_linear_interpolation(get_position(), velocity.normalized, delta + Mathf.PI / 2); transform.position = next_position; velocity = next_velocity.normalized * velocity.magnitude; // FIXME: I thought this was numerically stable, but it seems to create more energy. //velocity = Vector3.ProjectOnPlane(velocity, get_position()); // TODO: CONSIDER: ensure velocity and position are orthogonal - they seem to desynchronize //Debug.DrawRay(get_position(), velocity, Color.green); // draw new velocity (not old one) }
public static void draw_grid() { UnityEditor.Handles.color = Color.white; for (float row = 1; row <= EditorGlobal.self.rows; ++row) // equator lines { float angle = Mathf.PI * row / (EditorGlobal.self.rows + 1); float radius = Mathf.Sin(angle); Vector3 center = Vector3.down * Mathf.Cos(angle); UnityEditor.Handles.DrawWireDisc(center, Vector3.up, radius); } for (float column = 0; column < EditorGlobal.self.columns; ++column) // time zone lines { float angle = Mathf.PI * column / EditorGlobal.self.columns; Vector3 normal = PlanetariaMath.spherical_linear_interpolation(Vector3.forward, Vector3.right, angle); UnityEditor.Handles.DrawWireDisc(Vector3.zero, normal, 1); } }
/// <summary> /// Inspector - Get the position at a particular angle. /// </summary> /// <param name="angle">The angle in radians along the arc.</param> /// <param name="extrusion">The radius to extrude.</param> /// <returns>A position on the arc.</returns> public Vector3 position(float angle, float extrusion = 0f) { if (curvature == ArcType.ConcaveCorner) // Concave corners are "inside-out" { extrusion *= -1; } float actual_elevation = arc_latitude + extrusion; if (actual_elevation >= -Mathf.PI / 2) { Vector3 equator_position = PlanetariaMath.spherical_linear_interpolation(forward_axis, right_axis, angle); return(PlanetariaMath.spherical_linear_interpolation(equator_position, center_axis, actual_elevation)); } else // if (actual_elevation < -Mathf.PI/2) // Primarily used for concave corners { actual_elevation += Mathf.PI / 2; actual_elevation /= Mathf.Cos(half_angle); actual_elevation -= Mathf.PI / 2; return(PlanetariaMath.spherical_linear_interpolation(forward_axis, center_axis, actual_elevation)); } }