private static float MaxErrorLocation(ArcOfSphere arc, float begin, float end) { Vector2 L1 = SpaceConverter.SphereToUV(arc.Evaluate(begin)); Vector2 L2 = SpaceConverter.SphereToUV(arc.Evaluate(end)); // 2) use binary / bisection search to find the point of maximal error while (end - begin > delta) { float midpoint = (begin + end) / 2; float error_left = Point_Line_Distance(L1, L2, SpaceConverter.SphereToUV(arc.Evaluate(midpoint - delta))); float error_right = Point_Line_Distance(L1, L2, SpaceConverter.SphereToUV(arc.Evaluate(midpoint + delta))); 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 }
public static ConcaveCorner Spawn(ArcOfSphere previous_edge, ArcOfSphere next_edge) { GameObject prefab = (GameObject)Resources.Load("ConcaveCornerPrefab"); #if UNITY_EDITOR GameObject obj = PrefabUtility.InstantiatePrefab(prefab) as GameObject; #else GameObject obj = Instantiate(prefab) as GameObject; #endif #if UNITY_EDITOR Undo.RegisterCreatedObjectUndo(obj, "Created concave corner"); #endif obj.name = "Concave corner"; ConcaveCorner result = obj.GetComponent <ConcaveCorner>(); result.Initialize(previous_edge, next_edge); #if UNITY_EDITOR result.Save(); result.LinkBlock(previous_edge); #endif return(result); }
public void GroundedUpdate(CharacterMotor self) { if (self.input.sqrMagnitude > 1) { self.input.Normalize(); } Vector3 input3D = new Vector3(self.input.x, self.input.y, 0f); //FIXME: JANK if (input3D.sqrMagnitude > 1) { input3D.Normalize(); } Quaternion rotation = Quaternion.LookRotation(self.current_position, self.arc.EvaluateNormal(self.angle, self.radius)); float left_product = Vector3.Dot(rotation * input3D, self.left); float right_product = Vector3.Dot(rotation * input3D, self.right); float product = -Mathf.Abs(left_product); if (right_product > left_product) { product = +Mathf.Abs(right_product); } if (right_product < 0 && left_product < 0) { product = 0; } self.angle += product / self.height / 64; //FIXME: slight math error here-ish self.current_position = ArcOfSphere.Evaluate(self.ground.data, self.radius); self.transform.rotation = Quaternion.LookRotation(self.current_position, self.arc.EvaluateNormal(self.angle, self.radius)); }
public bool Traverse(ArcOfSphere path, Vector3 desiredPos) //I don't like these parameters, they can be fixed //I don't like that this is public, it should be private and exposed via a generic move option if possible { optional <float> interpolation_factor = path.CartesianToRadial(desiredPos); if (interpolation_factor.exists) { ground = new GroundInfo(); ground.data.angle = interpolation_factor.data; ground.data.arc = path; ground.data.block = path.GetComponentInParent <Block>(); ground.data.height = path.LengthRadius(radius); ground.data.begin = path.Begin(radius); ground.data.end = path.End(radius); current_position = ArcOfSphere.Evaluate(ground.data, radius); } else { Debug.Log("Critical Failure: Traverse's interpolation factor doesn't exist!"); } return(interpolation_factor.exists); }
//step 0: Character Controller adds the observed SphericalIsoscelesTriangle to a vector in OnTriggerEnter... public void OnTriggerEnter(Collider col) { ArcOfSphere arc = col.gameObject.GetComponent <ArcOfSphere>(); if (arc && !colliders.Contains(arc)) { colliders.Add(arc); } }
//step 0.5: Character Controller removes the observed SphericalIsoscelesTriangle from a vector in OnTriggerExit... public void OnTriggerExit(Collider col) //FIXME: not deleting { ArcOfSphere arc = col.gameObject.GetComponent <ArcOfSphere>(); if (arc) { colliders.Remove(arc); } }
public QuadraticBezier(ArcOfSphere a, Vector2 b, Vector2 c, Vector2 e, float bt, float et) { begin_UV = b; control_point = c; end_UV = e; arc = a; begin = bt; end = et; }
public static Edge LinkRight(ArcOfSphere left, Vector3 position) { Vector3 right = left.Evaluate(left.End()); Edge obj = Edge.StartEdge(left.transform.parent.transform, right, position); obj.Relink(left, left.next); return(obj); }
protected static Vector3 MaxGradient(ArcOfSphere arc, Vector3 desired) { Vector3 max_gradient = Vector3.zero; float max_product = Mathf.NegativeInfinity; /** if we don't calculate per quadrant, calculations for an arc with angle 2*PI become ambiguous because left == right */ float quadrants = Mathf.Ceil(arc.End() / (Mathf.PI / 2f)); //maximum of 4, always integral, float for casting "left" and "right" for (float quadrant = 0; quadrant < quadrants; ++quadrant) { float left = arc.End() * (quadrant / quadrants); //get beginning of quadrant i.e. 0.00,0.25,0.50,0.75 float right = arc.End() * ((quadrant + 1) / quadrants); //get end of quadrant i.e. 0.25,0.50,0.75,1.00 float left_product = Vector3.Dot(arc.Evaluate(left), desired); //find the correlation factor between left and the desired direction float right_product = Vector3.Dot(arc.Evaluate(right), desired); /** this is basically a binary search * * 1) take the left and right vectors and compute their dot products with the desired direction. * 2) take the lesser dot product and ignore that half of the remaining arc */ for (int iteration = 0; iteration < 8 * sizeof(float); ++iteration) //because we are dealing with floats, more precision could help (or hurt?) { float midpoint = (left + right) / 2; if (left_product < right_product) //is the right vector closer to the desired direction? { left = midpoint; //throw out the left half if the right vector is closer left_product = Vector3.Dot(arc.Evaluate(left), desired); } else { right = midpoint; //throw out the right half if the left vector is closer right_product = Vector3.Dot(arc.Evaluate(right), desired); } } /** figure out if this quadrant contains a larger gradient */ if (max_product < right_product) { max_gradient = arc.Evaluate(right); max_product = right_product; } if (max_product < left_product) { max_gradient = arc.Evaluate(left); max_product = left_product; } } return(max_gradient); }
private static void UpdateSigns(ArcOfSphere arc, ref int[,] data, float location, float delta) { //assert data's size is [2, 3] for (int dimension = 0; dimension < 3; ++dimension) { data[0, dimension] = System.Math.Sign(arc.Evaluate(location)[dimension]); } for (int dimension = 0; dimension < 3; ++dimension) { data[1, dimension] = System.Math.Sign(arc.Evaluate(location + delta)[dimension] - arc.Evaluate(location)[dimension]) * System.Math.Sign(delta); } }
public static void AddLine(ArcOfSphere edge, float begin, float end) { Vector2 begin_UV = SpaceConverter.SphereToUV(edge.Evaluate(begin)); Vector2 end_UV = SpaceConverter.SphereToUV(edge.Evaluate(end)); if (Vector2.Distance(begin_UV, end_UV) > threshold) { Vector2 delta_begin_UV = SpaceConverter.SphereToUV(edge.Evaluate(begin + 64 * delta)); Vector2 delta_end_UV = SpaceConverter.SphereToUV(edge.Evaluate(end - 64 * delta)); Vector2 control_point = Intersection(begin_UV, delta_begin_UV, delta_end_UV, end_UV); DebugUtility.Log("AddLine:", begin_UV, end_UV); lines.Add(new QuadraticBezier(edge, begin_UV, control_point, end_UV, begin, end)); } }
public ArcOfSphere Relink(ArcOfSphere left, ArcOfSphere right) { #if UNITY_EDITOR this.Save(); left.Save(); right.Save(); #endif this.next = right; this.prev = left; left.next = this; right.prev = this; return(this); }
public void Activate() { SphereCollider region = GetComponent <SphereCollider>(); Collider[] arc_objects = Physics.OverlapSphere(region.transform.position + region.center, region.transform.localScale.x * region.radius); region.enabled = true; foreach (Collider arc_object in arc_objects) { ArcOfSphere arc = arc_object.gameObject.GetComponent <ArcOfSphere>(); if (arc) { colliders.Add(arc); } } }
public void Move(Vector2 input) { if (between_levels) //FEATURE: enable movement (grounded and aerial) in between_levels { connection.data.Move(Input.GetAxis("Vertical"), this); } else if (grounded) //FIXME: this entire block if it is JANK //spelling -_-' { if (input.sqrMagnitude > 1) { input.Normalize(); } Transform camera_transform = GameObject.Find("MainCamera").transform; //FIXME: JANK Vector3 input3D = new Vector3(input.x, input.y, 0f); //FIXME: JANK if (input3D.sqrMagnitude > 1) { input3D.Normalize(); } float left_product = Vector3.Dot(camera_transform.rotation * input3D, left); float right_product = Vector3.Dot(camera_transform.rotation * input3D, right); float product = -Mathf.Abs(left_product); if (right_product > left_product) { product = +Mathf.Abs(right_product); } if (right_product < 0 && left_product < 0) { product = 0; } angle += product / height / 64; //FIXME: slight math error here-ish current_position = ArcOfSphere.Evaluate(ground.data, radius); transform.rotation = Quaternion.LookRotation(current_position, arc.EvaluateNormal(angle, radius)); } else { SphereUtility.Accelerate(ref phi, ref theta, ref vertical_velocity, ref horizontal_velocity, 0.03f, -input.x / 10, Time.fixedDeltaTime); current_position = SphereUtility.SphereToCartesian(new Vector2(phi, theta)); transform.rotation = Quaternion.LookRotation(current_position, North); } }
private static void Subdivide(ArcOfSphere arc, float begin, float end) { float midpoint = MaxErrorLocation(arc, begin, end); Vector2 L1 = SpaceConverter.SphereToUV(arc.Evaluate(begin)); Vector2 L2 = SpaceConverter.SphereToUV(arc.Evaluate(end)); Vector2 P = SpaceConverter.SphereToUV(arc.Evaluate(midpoint)); if (Point_Line_Distance(L1, L2, P) > 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, midpoint); Subdivide(arc, midpoint, end); } else { AddLine(arc, begin, end); } }
/** Create a AABB that perfectly contains a circular arc * * TODO: detailed description and math link * * TODO: Ex. * * @param collider the box collider that will be altered to contain the ArcOfSphere */ protected static void RecalculateAABB(ArcOfSphere arc) { float x_min = MaxGradient(arc, Vector3.left).x; float x_max = MaxGradient(arc, Vector3.right).x; float y_min = MaxGradient(arc, Vector3.down).y; float y_max = MaxGradient(arc, Vector3.up).y; float z_min = MaxGradient(arc, Vector3.back).z; float z_max = MaxGradient(arc, Vector3.forward).z; arc.transform.position = new Vector3((x_max + x_min) / 2, (y_max + y_min) / 2, (z_max + z_min) / 2); BoxCollider collider = arc.GetComponent <BoxCollider>(); collider.size = new Vector3(x_max - x_min, y_max - y_min, z_max - z_min); }
public override void Initialize(ArcOfSphere left, ArcOfSphere right) { #if UNITY_EDITOR this.Save(); #endif Vector3 path_center = right.Evaluate(right.Begin()); //Debug.DrawRay(path_center, Vector3.up, Color.yellow); path_normal = right.Evaluate(right.Begin()); arc_left = left.EvaluateNormal(left.End()); arc_right = right.EvaluateNormal(right.Begin()); Initialize(path_center); this.Relink(left, right); }
public static void Append(GameObject shape) { lines = new List <QuadraticBezier>(); ArcOfSphere first_edge = shape.GetComponentInChildren <Edge>(); ArcOfSphere arc = first_edge; do { float begin = arc.Begin(); float end = arc.End(); float range_begin = arc.Begin(); float range_end = arc.End(); float range_mid = arc.End() / 2; int[,] xyz_signs_begin = new int[2, 3]; //positional, derivative; x, y, z int[,] xyz_signs_end = new int[2, 3]; int[,] xyz_signs_range_begin = new int[2, 3]; //immediately before the first detected change in sign int[,] xyz_signs_range_end = new int[2, 3]; //the first detected change in any sign int[,] xyz_signs_range_mid = new int[2, 3]; //middle of begin and range_end // get signs for beginning and end UpdateSigns(arc, ref xyz_signs_begin, arc.Begin(), delta); UpdateSigns(arc, ref xyz_signs_end, arc.End(), -delta); // process new lines until the signs match while (!SameSigns(ref xyz_signs_begin, ref xyz_signs_end)) { xyz_signs_range_begin = xyz_signs_begin; xyz_signs_range_end = xyz_signs_end; // binary search and discard ranges with matching slope signs and position signs at ends; and then update the slope signs. while (range_end - range_begin > delta) { range_mid = (range_begin + range_end) / 2; //guaranteed not to overflow since numbers are in range [0, 2pi] UpdateSigns(arc, ref xyz_signs_range_mid, range_mid, delta); if (SameSigns(ref xyz_signs_range_begin, ref xyz_signs_range_mid)) { range_begin = range_mid; //xyz_signs_begin = xyz_signs_range_mid; //not necessary, the signs are the same } else { range_end = range_mid; xyz_signs_range_end = xyz_signs_range_mid; } } // when you find a sign that switches, log the exact position of the switch with as much precision as possible Subdivide(arc, begin, range_begin); // when you find that position, you must then switch the x, y, z signs at the new beginning of the arc and the slope signs xyz at the beginning of the arc begin = range_end; xyz_signs_begin = xyz_signs_range_end; } // draw the last line Subdivide(arc, begin, end); arc = arc.next; // move to next arc while (arc.GetType().IsSubclassOf(typeof(Corner))) // skip corners //XXX: Corner is abstract... for now { arc = arc.next; } } while (arc != first_edge); ClampToEdges(); OverlapSharedEdges(); BuildShape(); }
public virtual void Initialize(ArcOfSphere left, ArcOfSphere right) { }
public void LinkBlock(ArcOfSphere other) { LinkBlock(other.gameObject.transform.parent); }