Skip to content

Instantly share code, notes, and snippets.

@01010111
Last active July 10, 2019 21:45
Show Gist options
  • Save 01010111/8ed40ff86454bcbfc253333451726837 to your computer and use it in GitHub Desktop.
Save 01010111/8ed40ff86454bcbfc253333451726837 to your computer and use it in GitHub Desktop.
Vector class
using Math;
abstract Vec2(Array<Float>)
{
public static var UP (default, never):Vec2 = [0, -1];
public static var DOWN (default, never):Vec2 = [0, 1];
public static var LEFT (default, never):Vec2 = [-1, 0];
public static var RIGHT (default, never):Vec2 = [1, 0];
// Utility
static var epsilon:Float = 1e-8;
static function zero(n:Float):Float return n.abs() <= epsilon ? 0 : n;
public static function run_tests() Vec2Tests.run();
// Array creation/access
@:from static function from_array_float(input:Array<Float>) return new Vec2(input[0], input[1]);
@:from static function from_array_int(input:Array<Int>) return new Vec2(input[0], input[1]);
@:arrayAccess function arr_set(n:Int, v:Float) n < 0 || n > 1 ? return : this[n] = v;
@:arrayAccess function arr_get(n:Int):Float return this[n.min(1).max(0).floor()];
// Pooling
static var pool:Array<Vec2> = [];
public static function get(x:Float = 0, y:Float = 0):Vec2 return pool.length > 0 ? pool.shift().set(x, y) : new Vec2(x, y);
public inline function put()
{
pool.push(this);
this = null;
}
function new(x:Float = 0, y:Float = 0) this = [x, y];
public inline function set(x:Float = 0, y:Float = 0):Vec2
{
this[0] = zero(x);
this[1] = zero(y);
return this;
}
public var x (get, set):Float;
function get_x() return this[0];
function set_x(v) return this[0] = v;
public var y (get, set):Float;
function get_y() return this[1];
function set_y(v) return this[1] = v;
public var xx (get, never):Vec2;
function get_xx() return Vec2.get(x, x);
public var yy (get, never):Vec2;
function get_yy() return Vec2.get(y, y);
public var length (get, set):Float;
inline function get_length() return (x*x + y*y).sqrt();
inline function set_length(v:Float)
{
normalize();
scale(v);
return v;
}
public var angle (get, set):Float;
function get_angle() return ((Math.atan2(y, x) * (180 / Math.PI)) % 360 + 360) % 360;
function set_angle(v:Float)
{
v *= (Math.PI / 180);
set(length * v.cos(), length * v.sin());
return v;
}
// These functions modify the vector in place!
public inline function copy_from(v:Vec2):Vec2 return set(v.x, v.y);
public inline function normalize():Vec2 return set(x / length, y / length);
public inline function scale(n:Float):Vec2 return set(x * n, y * n);
public inline function copy():Vec2 return Vec2.get(x, y);
public inline function equals(v:Vec2):Bool return x == v.x && y == v.y;
public inline function dot(v:Vec2):Float return zero(x * v.x + y * v.y);
public inline function cross(v:Vec2):Float return zero(x * v.y - y * v.x);
public inline function facing(v:Vec2):Float return zero(x / length * v.x / v.length + y / length * v.y / v.length);
public inline function distance(v:Vec2):Float return (v - this).length;
public inline function toString():String return 'x: $x | y: $y | length: $length | angle: $angle';
// Operator Overloads
@:op(A + B) static function add(v1:Vec2, v2:Vec2):Vec2 return Vec2.get(v1.x + v2.x, v1.y + v2.y);
@:op(A + B) static function add_f(v:Vec2, n:Float):Vec2 return Vec2.get(v.x + n, v.y + n);
@:op(A - B) static function subtract(v1:Vec2, v2:Vec2):Vec2 return Vec2.get(v1.x - v2.x, v1.y - v2.y);
@:op(A - B) static function subtract_f(v:Vec2, n:Float):Vec2 return Vec2.get(v.x - n, v.y - n);
@:op(A * B) static function multiply(v1:Vec2, v2:Vec2):Vec2 return Vec2.get(v1.x * v2.x, v1.y * v2.y);
@:op(A * B) static function multiply_f(v:Vec2, n:Float):Vec2 return Vec2.get(v.x * n, v.y * n);
@:op(A / B) static function divide(v1:Vec2, v2:Vec2):Vec2 return Vec2.get(v1.x / v2.x, v1.y / v2.y);
@:op(A / B) static function divide_f(v:Vec2, n:Float):Vec2 return Vec2.get(v.x / n, v.y / n);
@:op(A % B) static function mod(v1:Vec2, v2:Vec2):Vec2 return Vec2.get(v1.x % v2.x, v1.y % v2.y);
@:op(A % B) static function mod_f(v:Vec2, n:Float):Vec2 return Vec2.get(v.x % n, v.y % n);
}
private class Vec2Tests
{
public static function run()
{
var pass = true;
pass = pass && vector_variables();
pass = pass && vector_methods();
pass = pass && operators();
if (pass) trace('All tests passsed!');
}
static function vector_variables():Bool
{
var pass = true;
var v1 = Vec2.get(1, 0);
var v2 = Vec2.get().copy_from(v1);
var v3 = Vec2.get().copy_from(v1);
v2.angle += 90;
v3.length *= 2;
pass = pass && eval('Angle', v1.angle == 0 && v2.angle == 90);
pass = pass && eval('Length', v1.length == v2.length && v3.length == v1.length * 2);
return pass;
}
static function vector_methods():Bool
{
var pass = true;
var v1 = Vec2.get(1, 2);
var v2 = Vec2.get().copy_from(v1);
v2.angle += 90;
pass = pass && eval('Copy', v1.copy().equals(v1) && v2.copy() != v2);
pass = pass && eval('Copy From', Vec2.get().copy_from(v1).equals(v1));
pass = pass && eval('Equals', v1.equals([1, 2]));
pass = pass && eval('Normalize', Vec2.get(20, 50).normalize().length == 1);
pass = pass && eval('Scale', v1.scale(2).equals([2, 4]));
pass = pass && eval('Dot Product', Vec2.UP.dot(Vec2.RIGHT) == 0);
pass = pass && eval('Cross Product', Vec2.RIGHT.cross(Vec2.DOWN) == 1);
pass = pass && eval('Facing', v1.facing(v2) == 0);
pass = pass && eval('Distance', Vec2.get(0, 3).distance([4, 0]) == 5);
return pass;
}
static function operators():Bool
{
var pass = true;
pass = pass && eval('Vector Addition', (Vec2.DOWN + Vec2.RIGHT).equals([1, 1]));
pass = pass && eval('Vector Subtraction', (Vec2.DOWN - Vec2.LEFT).equals([1, 1]));
pass = pass && eval('Vector Multiplication', (Vec2.get(1, 1) * [2, 2]).equals([2, 2]));
pass = pass && eval('Vector Division', (Vec2.get(4, 4) / [2, 2]).equals([2, 2]));
pass = pass && eval('Vector Modulo', (Vec2.get(3, 4) % [2, 2]).equals([1, 0]));
pass = pass && eval('Float Addition', (Vec2.DOWN + 1).equals([1, 2]));
pass = pass && eval('Float Subtraction', (Vec2.DOWN - 1).equals([-1, 0]));
pass = pass && eval('Float Multiplication', (Vec2.get(1, 1) * 2).equals([2, 2]));
pass = pass && eval('Float Division', (Vec2.get(4, 4) / 2).equals([2, 2]));
pass = pass && eval('Float Modulo', (Vec2.get(3, 4) % 2).equals([1, 0]));
return pass;
}
static function eval(name:String, exp:Bool)
{
if (!exp) trace('$name test failed!');
return exp;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment