Optimized Drawing of Great Circles in C#

I am trying to draw lots of different great circles every couple of frames (they don’t need to be animated, just need to be 3D and appear on the screen), so I am hoping I am using optimized code to do this. I am pretty new to Unity3D, but I was able to hack together a class that uses Vectrosity lines - 50 line segments to be precise - to connect a couple objects with a great circle. I don’t think this is the best approach because the lines don’t look that smooth, but it is the best I have been able to come up with. I have posted my code below and I am hoping someone will be able to help me find a faster and better looking approach to this problem. Thanks.

using UnityEngine;
using System.Collections;

public class GreatCircleArc {
	// Create an arc with numberSegments great circle vertices between a start and end point
	
	public string shapeName { get; set; }
	public Color lineColor { get; set; }
	public Vector3[] linePoints;
	public VectorLine line;
	
	int numberSegments = 50;
	float radius = -.01f;
	float fixed_y = .001f;
	int lineWidth = 2;
	Vector2 point_1;
	Vector2 point_2;
	
	public void AddSphereLine ( Vector2 argPoint_1, Vector2 argPoint_2 ) {
		// Get the coordinates of a great circle between two points given a center point and a specified radius.
		// http://www.mathworks.com/matlabcentral/newsreader/view_thread/277881
		this.point_1 = argPoint_1;
		this.point_2 = argPoint_2;
		Vector3[] linePoints = new Vector3[numberSegments];
		line = new VectorLine( shapeName, linePoints, lineColor, null, lineWidth, LineType.Continuous );
		Vector2 xz_0 = GetGreatCircle ();
		float y = fixed_y - radius;
		
		// (x, y, z) Vector from center to 1st point
		Vector3 v1 = new Vector3 ( point_1.x - xz_0.x, y, point_1.y - xz_0.y );
			
		// (x, y, z) Vector from center to 2nd point
		Vector3 v2 = new Vector3 ( point_2.x - xz_0.x, y, point_2.y - xz_0.y );
	
		// (x, y, z) Vector that lies in plane of v1 and v2 and is orthogonal to v1 and is length radius
		Vector3 v3 = Cross ( Cross ( v1, v2 ), v1 );
		v3 = Norm (v1) * v3 / Norm (v3);
		
		// Let theta range through the inner angle between v1 and v2
		float[] theta = linspace ( 0f, Mathf.Atan2 (Norm ( Cross ( v1, v2 ) ), Dot( v1, v2 )), numberSegments );
		
		// New matrix, v, traces great circle path, relative to center
		for ( int i = 0; i < theta.Length; ++i ) {
			Vector3 x = v1 * Mathf.Cos( theta[i] ) + v3 * Mathf.Sin( theta[i] );
			linePoints[i] = new Vector3( x.x + xz_0.x, x.y + y, x.z + xz_0.y );
		}
	}
	
	private Vector2 GetGreatCircle() {
		// Convert initial coordinates from degrees to radians
		float lat_1_radians = Radians (point_1.x);
		float lon_1_radians = Radians (point_1.y);
		float lat_2_radians = Radians (point_2.x);
		float lon_2_radians = Radians (point_2.y);
		
		// Distance between the two points in radians
		float lat_diff = lat_1_radians - lat_2_radians;
		float lon_diff = lon_1_radians - lon_2_radians;
		float dist_radians = 2f * Mathf.Asin (Mathf.Sqrt (
			(Mathf.Sin (lat_diff / 2f) * Mathf.Sin (lat_diff / 2f)) + 
			Mathf.Cos (lat_1_radians) * Mathf.Cos (lat_2_radians)
			* (Mathf.Sin (lon_diff / 2f) * Mathf.Sin (lon_diff / 2f))));
		
		// Calculate the 2D (x, z) mid-point on the great circle line for creation of the sphere
		float f = 1f / (numberSegments - 1) * numberSegments / 2f;
		float A = Mathf.Sin ((1 - f) * dist_radians) / Mathf.Sin (dist_radians);
		float B = Mathf.Sin (f * dist_radians) / Mathf.Sin (dist_radians);
		float x = A * Mathf.Cos (lat_1_radians) * Mathf.Cos (lon_1_radians) + B * Mathf.Cos (lat_2_radians) * Mathf.Cos (lon_2_radians);
		float y = A * Mathf.Cos (lat_1_radians) * Mathf.Sin (lon_1_radians) + B * Mathf.Cos (lat_2_radians) * Mathf.Sin (lon_2_radians);
		float z = A * Mathf.Sin (lat_1_radians) + B * Mathf.Sin (lat_2_radians);
		
		// Return x and z center points
		return new Vector2( Degrees (Mathf.Atan2 (z, Mathf.Sqrt ((x * x) + (y * y)))), Degrees (Mathf.Atan2 (y, x)) );
	}
	
	private static float Radians(float angle) {
		// Convert angle into radians
	   return Mathf.PI * angle / 180f;
	}
	
	private static float Degrees(float angle) {
		// Convert angle into degrees
	   return angle * (180f / Mathf.PI);
	}
	
	private static Vector3 Cross( Vector3 a, Vector3 b ) {
		// Cross product of two vectors
		return new Vector3 ( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x );
	}
	
	private static float Norm( Vector3 a ) {
		// Frobenius norm of a vector
		return Mathf.Sqrt(
			Mathf.Abs(a.x) * Mathf.Abs(a.x) + 
			Mathf.Abs(a.y) * Mathf.Abs(a.y) + 
			Mathf.Abs(a.z) * Mathf.Abs(a.z)
			);	
	}
	
	private static float Dot(Vector3 a, Vector3 b) {
		// Dot product of two vectors
		return a.x * b.x + a.y * b.y + a.z * b.z;	
	}
	
	private static float[] linspace(float start, float end, int n) {
		// Equally spaced linear values between two points
		float[] xs = new float[n];
		xs[0] = start;
		xs[n - 1] = end;
		float delta = (end - start) / (n - 1);
		for ( int i = 1; i < n - 1; ++i ) {
			xs[i] = xs[i - 1] + delta;	
		}
		return xs;
	}
}
1 Like

Just increase the number of segments, I would say; that will make it look smoother. By the way, you don’t need the Degrees, Radians, Cross, or Dot functions, since Unity has all those…you can use Mathf.Deg2Rad/Rad2Deg, and Vector3.Cross/Dot.

–Eric

Thanks Eric. I had a feeling I was missing out on some built-in optimized functions.

Is there a way to add a texture that may make it look smoother?

Really the only way to make curved lines look smooth is to subdivide them more. In 3D, everything is straight lines, so you need a lot of them to make a convincing curve. That shouldn’t be an issue though.

–Eric