/// <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()); }
// 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); }
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); }
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)); }
/// <summary> /// Inspector - Draw an extruded radial arc at a particular angle of the specified arc (i.e. the extrusion is its own arc). /// </summary> /// <param name="arc">The arc that will be rendered.</param> /// <param name="angle">The angle along the original Arc at which the extruded radius is drawn.</param> /// <param name="local_angle">The angle at which the ray is "refracting" from the surface. 0=>right; PI/2=>up.</param> /// <param name="extrusion">The distance (radius) to extrude the radial arc.</param> /// <param name="color">The color of the drawn radial arc.</param> /// <param name="orientation">The Transform's rotation (for moving platforms). For static objects, use Quaternion.identity.</param> public static void draw_ray(Arc arc, float angle, float local_angle, float extrusion, Color color, Quaternion orientation) { Vector3 from = arc.position(angle); Vector3 to = ArcUtility.relative_point(arc, angle, local_angle, extrusion); Arc composite = ArcFactory.line(from, to); draw_arc(composite, 0.0f, color, orientation); }
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; }
/// <summary> /// Constructor - Creates a circle (shape) of the given radius. /// </summary> /// <param name="radius">The radius of the circle.</param> public static PlanetariaShape Create(float radius) { PlanetariaShape asset = Create(); asset.serialized_arc_list = new List <SerializedArc> { ArcFactory.circle(radius) }; asset.has_corners = false; asset.initialize(); return(asset); }
/// <summary> /// Inspector - Draw a fraction of the given arc segment. /// </summary> /// <param name="arc">The arc that will be rendered.</param> /// <param name="begin_angle">The angle (along the arc) at which the partial arc begins.</param> /// <param name="end_angle">The angle (along the arc) at which the partial arc ends.</param> /// <param name="extrusion">The distance (radius) to extrude the radial arc.</param> /// <param name="color">The color of the drawn radial arc.</param> /// <param name="orientation">The Transform's rotation (for moving platforms). For static objects, use Quaternion.identity.</param> public static void draw_partial_arc(Arc arc, float begin_angle, float end_angle, float extrusion, Color color, Quaternion orientation) { if (arc.curvature >= ArcType.ConvexCorner && extrusion == 0) { return; } Vector3 from = arc.position(begin_angle, extrusion); Vector3 to = arc.position(end_angle, extrusion); Arc composite = ArcFactory.line(from, to); draw_arc(composite, 0.0f, color, orientation); }
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); } }
public bool is_convex_hull() { if (arc_list.Length > 1) { close(new optional <Vector3>(), AppendMode.AppendWithEphemeral); Arc last_arc = serialized_arc_list.Last(); foreach (Arc arc in serialized_arc_list) { if (ArcFactory.corner_type(last_arc, arc) == ArcType.ConcaveCorner) { return(false); } last_arc = arc; } } return(true); }
/// <summary> /// Inspector - Generates a shape (with corners if has_corners is set) from a list of edges. /// </summary> /// <param name="edges">The arc edges that define the shape.</param> /// <returns>Returns a list of edges interspliced with corners.</returns> private List <Arc> add_corners_between_edges(List <Arc> edges) { if (!has_corners) { return(edges); } List <Arc> result = new List <Arc>(); for (int edge = 0; edge < edges.Count; ++edge) { Arc left_edge = edges[(edge + 0) % edges.Count]; Arc right_edge = edges[(edge + 1) % edges.Count]; result.Add(left_edge); bool ignore = !closed() && edge == edges.Count - 1; //ignore the last corner for unclosed shapes if (!ignore) { result.Add(ArcFactory.corner(left_edge, right_edge)); } } return(result); }
/// <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 }
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)); }
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)); }
/// <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; } }