// 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 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()); }
/// <summary> /// Constructor - Create a concave corner arc (for underground path) /// </summary> /// <param name="serialized_left">The arc that attaches to the beginning of the corner.</param> /// <param name="serialized_right">The arc that attaches to the end of the corner.</param> /// <returns>An arc that represents the path of a burrowed object.</returns> private static SerializedArc concave_corner(Arc left, Arc right) // CONSIDER: combine with convex_corner? { SerializedArc convex = convex_corner(left, right); Vector3 forward_axis = convex.compact_basis_vectors * Vector3.back; // forward axis negated because it's concave Vector3 center_axis = convex.compact_basis_vectors * Vector3.up; Quaternion concave_rotation = Quaternion.LookRotation(forward_axis, center_axis); return(new SerializedArc(concave_rotation, convex.half_angle, convex.arc_latitude, ArcType.ConcaveCorner)); }
private Arc(SerializedArc serialized_arc) { half_angle = serialized_arc.half_angle; arc_latitude = serialized_arc.arc_latitude; curvature = serialized_arc.curvature; forward_axis = Vector3.forward; right_axis = (curvature == ArcType.ConcaveCorner ? Vector3.left : Vector3.right); center_axis = Vector3.up; if (serialized_arc.compact_basis_vectors != Quaternion.identity) { forward_axis = serialized_arc.compact_basis_vectors * forward_axis; right_axis = serialized_arc.compact_basis_vectors * right_axis; center_axis = serialized_arc.compact_basis_vectors * center_axis; } }
/// <summary> /// Constructor - Create a straight corner arc (should have zero length). /// </summary> /// <param name="serialized_left">The arc that attaches to the beginning of the corner.</param> /// <param name="serialized_right">The arc that attaches to the end of the corner.</param> /// <returns>A straight corner arc.</returns> private static SerializedArc straight_corner(Arc left, Arc right) { SerializedArc convex = convex_corner(left, right); return(new SerializedArc(convex.compact_basis_vectors, Precision.just_above_zero, convex.arc_latitude, ArcType.StraightCorner)); }
/// <summary> /// Inspector/Constructor - Appends a new arc to a current shape. /// </summary> /// <param name="arc">The arc you are appending to the end of the shape.</param> /// <param name="permanence">Are the changes permanent?</param> /// <returns>A shape with a new arc appended.</returns> public void append(SerializedArc arc, AppendMode permanence = AppendMode.OverwriteWithPermanent) { append(new List <SerializedArc> { arc }, permanence); }