예제 #1
0
        /// <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));
        }
예제 #2
0
 /// <summary>
 /// Mutator - Make sure the face index is in range [0,6); UV coordinate normalization handled in UVCoordinate
 /// </summary>
 private void normalize()
 {
     if (6 <= face_index_variable || face_index_variable < 0)
     {
         face_index_variable = (int)PlanetariaMath.modolo_using_euclidean_division(face_index_variable, 6); // HACK: CONSIDER: would anyone ever enter something like 2^31 in here?
     }
 }
예제 #3
0
        // Methods (non-Public)

        /// <summary>
        /// Mutator - Wrap UV coordinates so that neither value is outside of [0,1].
        /// </summary>
        private void normalize()
        {
            if (data_variable.x < 0 || data_variable.x > 1)
            {
                data_variable.x = PlanetariaMath.modolo_using_euclidean_division(data_variable.x, 1); // TODO: does this work?
            }
            else if (data_variable.x == 0)
            {
                data_variable.x = Precision.just_above_zero;
            }
            else if (data_variable.x == 1)
            {
                data_variable.x = Precision.just_below_one;
            }
            if (data_variable.y < 0 || data_variable.y > 1)
            {
                data_variable.y = PlanetariaMath.modolo_using_euclidean_division(data_variable.y, 1);
            }
            else if (data_variable.y == 0)
            {
                data_variable.y = Precision.just_above_zero;
            }
            else if (data_variable.y == 1)
            {
                data_variable.y = Precision.just_below_one;
            }
        }
예제 #4
0
        private static float max_error_location(Arc arc, float begin_point_angle, float end_point_angle)
        {
            OctahedronUVCoordinates begin_point = new NormalizedCartesianCoordinates(arc.position(begin_point_angle));
            OctahedronUVCoordinates end_point   = new NormalizedCartesianCoordinates(arc.position(end_point_angle));

            float begin = begin_point_angle;
            float end   = end_point_angle;

            // 2) use binary / bisection search to find the point of maximal error
            while (end - begin > Precision.delta)
            {
                float midpoint = (begin + end) / 2;

                OctahedronUVCoordinates left_midpoint  = new NormalizedCartesianCoordinates(arc.position(midpoint - Precision.delta));
                OctahedronUVCoordinates right_midpoint = new NormalizedCartesianCoordinates(arc.position(midpoint + Precision.delta));

                float error_left  = PlanetariaMath.point_line_distance(begin_point.data, end_point.data, left_midpoint.data);
                float error_right = PlanetariaMath.point_line_distance(begin_point.data, end_point.data, right_midpoint.data);

                if (error_left < error_right) //error begin should be replaced since it has less error
                {
                    begin = midpoint;
                }
                else //error end should be replaced since it has less error
                {
                    end = midpoint;
                }
            }

            return((begin + end) / 2); // return location of max error
        }
예제 #5
0
        /// <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);
            }
        }
예제 #6
0
        // 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);
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        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)
        }
        /// <summary>
        /// Mutator - Normalizes Cartesian vector using Manhattan distance
        /// </summary>
        private void normalize()
        {
            float length         = PlanetariaMath.manhattan_distance(Vector3.zero, data_variable);
            float absolute_error = Mathf.Abs(length - 1);

            if (absolute_error > Precision.tolerance)
            {
                data_variable /= length;
            }
        }
예제 #10
0
        protected override void initialize()
        {
            Camera camera = this.GetComponentInChildren <Camera>() as Camera;

            shutter_edges = new GameObject[edges];

            screen_height = PlanetariaMath.cone_radius(PlanetariaCamera.near_clip_plane, camera.fieldOfView * Mathf.Deg2Rad) * 2;
            screen_width  = screen_height * camera.aspect;

            for (int edge_index = 0; edge_index < edges; ++edge_index)
            {
                shutter_edges[edge_index] = (GameObject)Instantiate(Resources.Load("BlinkShutter"),
                                                                    new Vector3(0, screen_height * Mathf.Cos(edge_index * Mathf.PI), PlanetariaCamera.near_clip_plane + Precision.tolerance),
                                                                    Quaternion.Euler(0, 0, edge_index * 180f), camera.transform);
                shutter_edges[edge_index].transform.localScale = new Vector3(screen_width, screen_height, 1);
            }
        }
        /*public static implicit operator OctahedronUVCoordinates(NormalizedSphericalCoordinates spherical)
         * {
         *  return (NormalizedCartesianCoordinates) spherical;
         * }*/

        /// <summary>
        /// Mutator - Wrap elevation and azimuth so they are within [0, PI] and [0, 2*PI) respectively.
        /// </summary>
        private void normalize() // FIXME: verify - it's been wrong until now at least
        {
            if (data_variable.x < -Mathf.PI / 2 || data_variable.x > +Mathf.PI / 2)
            {
                data_variable.x = PlanetariaMath.modolo_using_euclidean_division(data_variable.x + Mathf.PI / 2, 2 * Mathf.PI); // modulo is undefined behavior with negative numbers
                if (data_variable.x > Mathf.PI)                                                                                 // result must be positive (due to euclidean division)
                {
                    data_variable.x  = 2 * Mathf.PI - data_variable.x;
                    data_variable.y += Mathf.PI; // going past the pole (to the opposite hemisphere) changes the azimuth
                }
                data_variable.x -= Mathf.PI / 2;
            }

            if (data_variable.y < 0 || data_variable.y >= 2 * Mathf.PI)
            {
                data_variable.y = PlanetariaMath.modolo_using_euclidean_division(data_variable.y, 2 * Mathf.PI);
            }
        }
예제 #12
0
        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);
            }
        }
예제 #13
0
        /// <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));
            }
        }
예제 #14
0
        private static void subdivide(Arc arc, float begin_point_angle, float end_point_angle)
        {
            float middle_point_angle = max_error_location(arc, begin_point_angle, end_point_angle);

            Vector3 begin  = arc.position(begin_point_angle);
            Vector3 middle = arc.position(middle_point_angle);
            Vector3 end    = arc.position(end_point_angle);

            OctahedronUVCoordinates begin_point  = new NormalizedCartesianCoordinates(begin);
            OctahedronUVCoordinates middle_point = new NormalizedCartesianCoordinates(middle);
            OctahedronUVCoordinates end_point    = new NormalizedCartesianCoordinates(end);

            if (PlanetariaMath.point_line_distance(begin_point.data, end_point.data, middle_point.data) > Precision.threshold) // if the max error is greater than a threshold, recursively add the left and right halves into the list of lines
            {
                subdivide(arc, begin_point_angle, middle_point_angle);
                subdivide(arc, middle_point_angle, end_point_angle);
            }
            else
            {
                QuadraticBezierCurve curve = new QuadraticBezierCurve(begin_point.data, middle_point.data, end_point.data);
                VectorGraphicsWriter.set_edge(curve);
            }
        }
        protected override void initialize()
        {
            Camera camera = this.GetComponentInChildren <Camera>() as Camera;

            shutter_edges = new GameObject[edges];

            for (int edge_index = 0; edge_index < edges; ++edge_index)
            {
                shutter_edges[edge_index] = (GameObject)Instantiate(Resources.Load("PrimaryEdge"),
                                                                    new Vector3(0, 0, 2 * PlanetariaCamera.near_clip_plane),
                                                                    Quaternion.Euler(0, 0, edge_index * 360f / edges), camera.transform);
#if UNITY_EDITOR
                shutter_edges[edge_index].transform.localScale = Vector3.one * 4 * PlanetariaMath.cone_radius(2 * PlanetariaCamera.near_clip_plane, camera.fieldOfView * Mathf.Deg2Rad) * Mathf.Sqrt(1 + (camera.aspect * camera.aspect));
#else
                float x = PlanetariaMath.cone_radius(2 * PlanetariaCamera.near_clip_plane, camera.fieldOfView * Mathf.Deg2Rad);
                float y = x * camera.aspect;
                float z = 2 * PlanetariaCamera.near_clip_plane;

                StereoscopicProjectionCoordinates stereoscopic_projection = new NormalizedCartesianCoordinates(new Vector3(x, y, z));

                shutter_edges[edge_index].transform.localScale = Vector3.one * stereoscopic_projection.data.magnitude; // FIXME: VR FOV
#endif
            }
        }
예제 #16
0
 public bool grounded(Vector3 velocity)
 {
     return(geometry_visitor.contains(PlanetariaMath.move(geometry_visitor.position(), velocity * Time.deltaTime)));
 }
예제 #17
0
        /// <summary>
        /// Inspector (Cache Mutator) - Creates a spherical rectangle UV coordinate set from a point on a unit sphere and a rectangle representing the x/y angles.
        /// </summary>
        /// <param name="cartesian">The point on the surface of a unit sphere to be converted.</param>
        /// <param name="canvas">A Rect (measuring radians) representing the start and stop angles relative to Quaternion.identity. X/Y Range: (-2PI, +2PI).</param>
        /// <returns>UV Coordinates for a spherical rectangle. Valid X/Y Range: [0, 1], although one axis may be in the (-INF, +INF) range.</returns>
        public static SphericalRectangleUVCoordinates cartesian_to_spherical_rectangle(Vector3 cartesian, Rect canvas)
        {
            // cache the canvas and derivative arcs (so they are not recomputed every time the function is called)
            cache_spherical_rectangle(canvas);

            Vector3 equator_projection        = PlanetariaMath.project_onto_equator(cartesian, cached_north_hemisphere);
            Vector3 prime_meridian_projection = PlanetariaMath.project_onto_equator(cartesian, cached_east_hemisphere);

            float u;                                                 // FIXME: keep it DRY (Do not Repeat Yourself)

            if (Vector3.Dot(cartesian, cached_east_hemisphere) >= 0) // right (East)
            {
                float angle;
                if (Vector3.Dot(cartesian, cached_right_positive_partition) >= 0)
                {
                    angle = Vector3.Angle(cached_right_biangle_focus, equator_projection) * Mathf.Deg2Rad;
                }
                else
                {
                    angle = Vector3.Angle(-cached_right_biangle_focus, equator_projection) * Mathf.Deg2Rad + Mathf.PI;
                }
                angle -= cached_right_start_angle;
                u      = angle / (cached_right_end_angle - cached_right_start_angle);
                u      = 0.5f + u / 2;
            }
            else // if (Vector3.Dot(cartesian, cached_east_hemisphere) < 0) // left (West)
            {
                float angle;
                if (Vector3.Dot(cartesian, cached_left_positive_partition) >= 0)
                {
                    angle = Vector3.Angle(cached_left_biangle_focus, equator_projection) * Mathf.Deg2Rad;
                }
                else
                {
                    angle = Vector3.Angle(-cached_left_biangle_focus, equator_projection) * Mathf.Deg2Rad + Mathf.PI;
                }
                angle -= cached_left_start_angle;
                u      = angle / (cached_left_end_angle - cached_left_start_angle);
                u      = 0.5f - u / 2;
            }

            float v;

            if (Vector3.Dot(cartesian, cached_north_hemisphere) >= 0) // upper (North)
            {
                float angle;
                if (Vector3.Dot(cartesian, cached_upper_positive_partition) >= 0)
                {
                    angle = Vector3.Angle(cached_upper_biangle_focus, prime_meridian_projection) * Mathf.Deg2Rad;
                }
                else
                {
                    angle = Vector3.Angle(-cached_upper_biangle_focus, prime_meridian_projection) * Mathf.Deg2Rad + Mathf.PI;
                }
                angle -= cached_upper_start_angle;
                v      = angle / (cached_upper_end_angle - cached_upper_start_angle);
                v      = 0.5f + v / 2;
            }
            else // if (Vector3.Dot(cartesian, cached_north_hemisphere) < 0) // lower (South)
            {
                float angle;
                if (Vector3.Dot(cartesian, cached_lower_positive_partition) >= 0)
                {
                    angle = Vector3.Angle(cached_lower_biangle_focus, prime_meridian_projection) * Mathf.Deg2Rad;
                }
                else
                {
                    angle = Vector3.Angle(-cached_lower_biangle_focus, prime_meridian_projection) * Mathf.Deg2Rad + Mathf.PI;
                }
                angle -= cached_lower_start_angle;
                v      = angle / (cached_lower_end_angle - cached_lower_start_angle);
                v      = 0.5f - v / 2;
            }

            return(new SphericalRectangleUVCoordinates(new Vector2(u, v), canvas));
        }