// 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);
        }
Beispiel #2
0
        /// <summary>
        /// Inspector - Finds the collision points between an arc extrapolated to be distance long (the PlanetariaRaycastHit structs have no particular order)
        /// </summary>
        /// <param name="arc">A fragment that defines the arc in space (might not be fully used or return collisions after the end of the arc).</param>
        /// <param name="distance">The distance to raycast (may be greater than or less than the length of the arc - or negative).</param>
        /// <param name="layer_mask">The collision mask that defines which objects will be ignored.</param>
        /// <returns>All of the collision points of the Raycast (listed exactly once).</returns>
        private static PlanetariaRaycastHit[] unordered_raycast_all(Arc arc, float distance, int layer_mask, bool collide_with_fields)
        {
            //float angle = arc.angle(); // this would determine the intersections for the un-modified arc (ignoring distance)

            // desired_angle = desired_length * (partial_angle/partial_length) i.e. length * length_to_angle ratio
            float desired_angle = distance * (arc.angle() / arc.length()); // TODO: verify negative distances go backwards

            desired_angle = Mathf.Clamp(desired_angle, -2 * Mathf.PI, 2 * Mathf.PI);

            // primative arc points
            Vector3 arc_left   = arc.position(-arc.angle() / 2);
            Vector3 arc_center = arc.position(-arc.angle() / 2 + desired_angle / 2);
            Vector3 arc_right  = arc.position(-arc.angle() / 2 + desired_angle);

            SerializedArc ray_arc = ArcFactory.curve(arc_left, arc_center, arc_right);

            PlanetariaShape ray_shape = PlanetariaShape.Create(new List <SerializedArc> {
                ray_arc
            }, false);

            // composites
            Vector3 arc_boundary_midpoint = (arc_left + arc_right) / 2;                      // if the arc is like a wooden bow, this is the midpoint of the string
            Vector3 arc_forward           = (arc_center - arc_boundary_midpoint).normalized; // the direction a hypothetical arrow would travel
            Vector3 arc_up = arc.floor().normal;                                             // orthogonal/perpendicular to the imaginary "bow"

            // UnityEngine.Physics.OverlapBox() requirements
            // FIXME: OPTIMIZE: half_extents currently provides unnecessary false positives because the "width" of plane (the depth into the distance and zero height are fine)
            Vector3    half_extents = new Vector3(1, 0, 1);                    // The largest collision "box" for a unit sphere is a radius of 1 in the x-z plane; height along y is 0.
            Vector3    center       = arc_boundary_midpoint + arc_forward * 1; // The center of the "box" must be offset 1 (the radius) along the forward axis from the two arc boundaries.
            Quaternion rotation     = Quaternion.LookRotation(arc_forward, arc_up);

            // SphereColliders (only) that represent potential collisions (not guaranteed).
            Collider[] colliders = Physics.OverlapBox(center, half_extents, rotation, layer_mask, QueryTriggerInteraction.Collide); // TODO: verify this casts properly
            List <PlanetariaRaycastHit> raycast_hits = new List <PlanetariaRaycastHit>();

            Debug.Log(colliders.Length);
            foreach (SphereCollider sphere_collider in colliders)
            {
                PlanetariaCollider planetaria_collider = PlanetariaCache.collider_fetch(sphere_collider);
                if (planetaria_collider.is_field && !collide_with_fields)
                {
                    Debug.LogError("Why?");
                    continue;
                }
                Quaternion geometry_rotation = planetaria_collider.gameObject.internal_game_object.transform.rotation;
                Debug.Log("Found a collider with " + planetaria_collider.shape.Length + " arcs.");
                foreach (Arc geometry_arc in ray_shape.block_collision(planetaria_collider.shape, geometry_rotation))
                {
                    Vector3[] intersections = PlanetariaIntersection.raycast_intersection(arc, geometry_arc, distance, geometry_rotation); // TODO: verify distance is indeed the angle in this scenario
                    Debug.Log("Found an arc with " + intersections.Length + " intersections.");
                    foreach (Vector3 intersection in intersections)
                    {
                        PlanetariaRaycastHit single_collision = PlanetariaRaycastHit.hit(arc, planetaria_collider, geometry_arc, intersection, distance);
                        raycast_hits.Add(single_collision);
                    }
                }
            }
            return(raycast_hits.ToArray());
        }
Beispiel #3
0
        private static Vector3 intersection(float longitude, float latitude)                                      // FIXME: Rect canvas
        {
            Arc     x_boundary = ArcFactory.curve(Vector3.down, equator_longitude(longitude), Vector3.up);        // full-circle
            Arc     y_boundary = ArcFactory.curve(Vector3.left, prime_meridian_latitude(latitude), Vector3.left); // semi-circle
            Vector3 corner     = PlanetariaIntersection.arc_arc_intersection(x_boundary, y_boundary, 0).data;

            return(corner);
        }
Beispiel #4
0
        private static Arc boundary(Vector2 start_longitude_latitude, Vector2 end_longitude_latitude)
        {
            Vector3 first_corner = intersection(start_longitude_latitude.x, start_longitude_latitude.y);
            Vector3 end_corner   = intersection(end_longitude_latitude.x, end_longitude_latitude.y);
            Vector2 middle_longitude_latitude = (start_longitude_latitude + end_longitude_latitude) / 2;
            Vector3 axis = intersection(middle_longitude_latitude.x, middle_longitude_latitude.y);

            return(ArcFactory.curve(first_corner, axis, end_corner));
        }
Beispiel #5
0
 public void next()
 {
     if (state == CreationState.SetPoint)
     {
         shape.append(ArcFactory.curve(previous_point, slope, point));
         previous_point = point;
     }
     state = (state == CreationState.SetSlope ? CreationState.SetPoint : CreationState.SetSlope); // state = !state;
 }
Beispiel #6
0
 public void receive_vector(Vector3 vector)
 {
     if (state == CreationState.SetSlope)
     {
         slope = vector;
         shape.append(ArcFactory.line(point, slope), PlanetariaShape.AppendMode.OverwriteWithEphemeral);
     }
     else // CreationState.SetPoint
     {
         point = vector;
         shape.append(ArcFactory.curve(previous_point, slope, point), PlanetariaShape.AppendMode.OverwriteWithEphemeral);
     }
 }
Beispiel #7
0
        /// <summary>
        /// Inspector/Constructor - Creates a copy of the shape then sets closed=true
        /// </summary>
        /// <returns>A closed shape mirroring all properties of original but with closed=true.</returns>
        public void close(optional <Vector3> slope = new optional <Vector3>(), AppendMode permanence = AppendMode.OverwriteWithPermanent)
        {
            if (permanence == AppendMode.OverwriteWithEphemeral ||
                permanence == AppendMode.OverwriteWithPermanent)
            {
                serialized_arc_list.RemoveRange(serialized_arc_list.Count - ephemeral_arcs, ephemeral_arcs);
                ephemeral_arcs = 0;
            }

            // Add last edge! (if not already closed and Dot of last/first point is != 1)
            if (!closed())
            {
                Arc first_arc = serialized_arc_list[0];
                Arc last_arc  = serialized_arc_list[serialized_arc_list.Count - 1];
                if (!slope.exists)
                {
                    slope = first_arc.begin();
                }
                append(ArcFactory.curve(last_arc.end(), slope.data, first_arc.begin()), permanence);
            }
            initialize();

            // TODO: if field, make this a convex_hull() // TODO: add convex property
        }
Beispiel #8
0
        private static Vector3 prime_meridian_latitude(float latitude)
        {
            Arc prime_meridian = ArcFactory.curve(Vector3.back, Vector3.down, Vector3.back); // same as equator_longitude comment

            return(prime_meridian.position(latitude));
        }
Beispiel #9
0
        private static Vector3 equator_longitude(float longitude)
        {
            Arc equator = ArcFactory.curve(Vector3.back, Vector3.left, Vector3.back); // The center of the arc is between begin and endpoint hence Vector3.back (not Vector3.forward) and Vector3.left (not Vector3.right)

            return(equator.position(longitude));
        }
Beispiel #10
0
        /// <summary>
        /// Inspector (Cache Mutator) - Updates the cache so that spherical rectangle calculations avoid recomputing old values.
        /// </summary>
        /// <param name="canvas">A Rect (measuring radians) representing the start and stop angles relative to Quaternion.identity. X/Y Range: (-2PI, +2PI).</param>
        public static void cache_spherical_rectangle(Rect canvas)
        {
            if (cached_canvas != canvas)
            {
                Vector3 lower_left   = intersection(canvas.xMin, canvas.yMin);
                Vector3 lower_center = intersection(canvas.center.x, canvas.yMin);
                Vector3 lower_right  = intersection(canvas.xMax, canvas.yMin);

                Vector3 middle_left   = intersection(canvas.xMin, canvas.center.y);
                Vector3 middle_center = intersection(canvas.center.x, canvas.center.y);
                Vector3 middle_right  = intersection(canvas.xMax, canvas.center.y);

                Vector3 upper_left   = intersection(canvas.xMin, canvas.yMax);
                Vector3 upper_center = intersection(canvas.center.x, canvas.yMax);
                Vector3 upper_right  = intersection(canvas.xMax, canvas.yMax);

                Arc biangle_segment1 = ArcFactory.curve(upper_center, upper_right, -upper_center);
                Arc biangle_segment2 = ArcFactory.curve(lower_center, lower_right, -lower_center);
                cached_left_biangle_focus      = PlanetariaIntersection.arc_arc_intersection(biangle_segment1, biangle_segment2, 0).data;
                cached_left_positive_partition = Bearing.attractor(cached_left_biangle_focus, middle_left); // used for a dot product to determine if the angle applied for UV is +/-
                if (Vector3.Dot(cached_left_positive_partition, middle_center) >= 0)
                {
                    cached_left_start_angle = Vector3.Angle(cached_left_biangle_focus, middle_center) * Mathf.Deg2Rad;
                    cached_left_end_angle   = Vector3.Angle(cached_left_biangle_focus, middle_left) * Mathf.Deg2Rad;
                }
                else
                {
                    cached_left_start_angle = Vector3.Angle(-cached_left_biangle_focus, middle_center) * Mathf.Deg2Rad + Mathf.PI;
                    cached_left_end_angle   = Vector3.Angle(-cached_left_biangle_focus, middle_left) * Mathf.Deg2Rad + Mathf.PI;
                }

                biangle_segment1                = ArcFactory.curve(upper_center, upper_left, -upper_center);
                biangle_segment2                = ArcFactory.curve(lower_center, lower_left, -lower_center);
                cached_right_biangle_focus      = PlanetariaIntersection.arc_arc_intersection(biangle_segment1, biangle_segment2, 0).data;
                cached_right_positive_partition = Bearing.attractor(cached_right_biangle_focus, middle_right); // used for a dot product to determine if the angle applied for UV is +/-
                if (Vector3.Dot(cached_right_positive_partition, middle_center) >= 0)
                {
                    cached_right_start_angle = Vector3.Angle(cached_right_biangle_focus, middle_center) * Mathf.Deg2Rad;
                    cached_right_end_angle   = Vector3.Angle(cached_right_biangle_focus, middle_right) * Mathf.Deg2Rad;
                }
                else
                {
                    cached_right_start_angle = Vector3.Angle(-cached_right_biangle_focus, middle_center) * Mathf.Deg2Rad + Mathf.PI;
                    cached_right_end_angle   = Vector3.Angle(-cached_right_biangle_focus, middle_right) * Mathf.Deg2Rad + Mathf.PI;
                }

                biangle_segment1                = ArcFactory.curve(middle_left, upper_left, -middle_left);
                biangle_segment2                = ArcFactory.curve(middle_right, upper_right, -middle_right);
                cached_lower_biangle_focus      = PlanetariaIntersection.arc_arc_intersection(biangle_segment1, biangle_segment2, 0).data;
                cached_lower_positive_partition = Bearing.attractor(cached_lower_biangle_focus, lower_center); // used for a dot product to determine if the angle applied for UV is +/-
                if (Vector3.Dot(cached_lower_positive_partition, middle_center) >= 0)
                {
                    cached_lower_start_angle = Vector3.Angle(cached_lower_biangle_focus, middle_center) * Mathf.Deg2Rad;
                    cached_lower_end_angle   = Vector3.Angle(cached_lower_biangle_focus, lower_center) * Mathf.Deg2Rad;
                }
                else
                {
                    cached_lower_start_angle = Vector3.Angle(-cached_lower_biangle_focus, middle_center) * Mathf.Deg2Rad + Mathf.PI;
                    cached_lower_end_angle   = Vector3.Angle(-cached_lower_biangle_focus, lower_center) * Mathf.Deg2Rad + Mathf.PI;
                }

                biangle_segment1                = ArcFactory.curve(middle_left, lower_left, -middle_left);
                biangle_segment2                = ArcFactory.curve(middle_right, lower_right, -middle_right);
                cached_upper_biangle_focus      = PlanetariaIntersection.arc_arc_intersection(biangle_segment1, biangle_segment2, 0).data;
                cached_upper_positive_partition = Bearing.attractor(cached_upper_biangle_focus, upper_center); // used for a dot product to determine if the angle applied for UV is +/-
                if (Vector3.Dot(cached_upper_positive_partition, middle_center) >= 0)
                {
                    cached_upper_start_angle = Vector3.Angle(cached_upper_biangle_focus, middle_center) * Mathf.Deg2Rad;
                    cached_upper_end_angle   = Vector3.Angle(cached_upper_biangle_focus, upper_center) * Mathf.Deg2Rad;
                }
                else
                {
                    cached_upper_start_angle = Vector3.Angle(-cached_upper_biangle_focus, middle_center) * Mathf.Deg2Rad + Mathf.PI;
                    cached_upper_end_angle   = Vector3.Angle(-cached_upper_biangle_focus, upper_center) * Mathf.Deg2Rad + Mathf.PI;
                }

                cached_north_hemisphere = Bearing.attractor(middle_center, upper_center);
                cached_east_hemisphere  = Bearing.attractor(middle_center, middle_right);

                cached_canvas = canvas;
            }
        }