mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-26 21:19:56 +03:00
1005 lines
No EOL
21 KiB
D
1005 lines
No EOL
21 KiB
D
// Copyright 2024 Alexandros F. G. Kapretsos
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
/// The math module covers
|
|
/// essential mathematical operations, vectors, and shapes.
|
|
|
|
module popka.core.math;
|
|
|
|
import popka.core.stdc;
|
|
import popka.core.strutils;
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
enum pi = 3.141592f;
|
|
enum epsilon = 0.0001f;
|
|
|
|
enum Hook : ubyte {
|
|
topLeft, top, topRight,
|
|
left, center, right,
|
|
bottomLeft, bottom, bottomRight,
|
|
}
|
|
|
|
struct Vec2 {
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
|
|
enum zero = Vec2(0.0f, 0.0f);
|
|
enum one = Vec2(1.0f, 1.0f);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(float x, float y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float x) {
|
|
this(x, x);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float[2] xy) {
|
|
this(xy[0], xy[1]);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(IVec2 xy) {
|
|
this(xy.x, xy.y);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec2 opUnary(const(char)[] op)() {
|
|
return Vec2(
|
|
mixin(op, "x"),
|
|
mixin(op, "y"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec2 opBinary(const(char)[] op)(Vec2 rhs) {
|
|
return Vec2(
|
|
mixin("x", op, "rhs.x"),
|
|
mixin("y", op, "rhs.y"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec2 opBinary(const(char)[] op)(float rhs) {
|
|
return Vec2(
|
|
mixin("x", op, "rhs"),
|
|
mixin("y", op, "rhs"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec2 opBinaryRight(const(char)[] op)(float lhs) {
|
|
return Vec2(
|
|
mixin("lhs", op, "x"),
|
|
mixin("lhs", op, "y"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
void opOpAssign(const(char)[] op)(Vec2 rhs) {
|
|
mixin("x", op, "=rhs.x;");
|
|
mixin("y", op, "=rhs.y;");
|
|
}
|
|
|
|
pragma(inline, true)
|
|
void opOpAssign(const(char)[] op)(float rhs) {
|
|
mixin("x", op, "=rhs;");
|
|
mixin("y", op, "=rhs;");
|
|
}
|
|
|
|
Vec2 abs() {
|
|
return Vec2(x.abs, y.abs);
|
|
}
|
|
|
|
Vec2 floor() {
|
|
return Vec2(x.floor, y.floor);
|
|
}
|
|
|
|
Vec2 ceil() {
|
|
return Vec2(x.ceil, y.ceil);
|
|
}
|
|
|
|
Vec2 round() {
|
|
return Vec2(x.round, y.round);
|
|
}
|
|
|
|
float length() {
|
|
return sqrt(x * x + y * y);
|
|
}
|
|
|
|
Vec2 normalize() {
|
|
float l = length;
|
|
if (l == 0.0f) {
|
|
return Vec2();
|
|
} else {
|
|
return this / Vec2(l);
|
|
}
|
|
}
|
|
|
|
Vec2 directionTo(Vec2 to) {
|
|
return (to - this).normalize();
|
|
}
|
|
|
|
float distanceTo(Vec2 to) {
|
|
return (to - this).length;
|
|
}
|
|
|
|
Vec2 moveTo(Vec2 to, Vec2 delta) {
|
|
Vec2 result = void;
|
|
Vec2 offset = this.directionTo(to) * delta;
|
|
if (.abs(to.x - x) > .abs(offset.x)) {
|
|
result.x = x + offset.x;
|
|
} else {
|
|
result.x = to.x;
|
|
}
|
|
if (.abs(to.y - y) > .abs(offset.y)) {
|
|
result.y = y + offset.y;
|
|
} else {
|
|
result.y = to.y;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Vec2 moveTo(Vec2 to, Vec2 delta, float slowdown) {
|
|
return Vec2(
|
|
.moveTo(x, to.x, delta.x, slowdown),
|
|
.moveTo(y, to.y, delta.y, slowdown),
|
|
);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {})".format(x, y);
|
|
}
|
|
}
|
|
|
|
struct Vec3 {
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
float z = 0.0f;
|
|
|
|
enum zero = Vec3(0.0f, 0.0f, 0.0f);
|
|
enum one = Vec3(1.0f, 1.0f, 1.0f);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(float x, float y, float z) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float x) {
|
|
this(x, x, x);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float[3] xyz) {
|
|
this(xyz[0], xyz[1], xyz[2]);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 xy, float z) {
|
|
this(xy.x, xy.y, z);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(IVec3 xyz) {
|
|
this(xyz.x, xyz.y, xyz.z);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec3 opUnary(const(char)[] op)() {
|
|
return Vec3(
|
|
mixin(op, "x"),
|
|
mixin(op, "y"),
|
|
mixin(op, "z"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec3 opBinary(const(char)[] op)(Vec3 rhs) {
|
|
return Vec3(
|
|
mixin("x", op, "rhs.x"),
|
|
mixin("y", op, "rhs.y"),
|
|
mixin("z", op, "rhs.z"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec3 opBinary(const(char)[] op)(float rhs) {
|
|
return Vec3(
|
|
mixin("x", op, "rhs"),
|
|
mixin("y", op, "rhs"),
|
|
mixin("z", op, "rhs"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec3 opBinaryRight(const(char)[] op)(float lhs) {
|
|
return Vec3(
|
|
mixin("lhs", op, "x"),
|
|
mixin("lhs", op, "y"),
|
|
mixin("lhs", op, "z"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
void opOpAssign(const(char)[] op)(Vec3 rhs) {
|
|
mixin("x", op, "=rhs.x;");
|
|
mixin("y", op, "=rhs.y;");
|
|
mixin("z", op, "=rhs.z;");
|
|
}
|
|
|
|
pragma(inline, true)
|
|
void opOpAssign(const(char)[] op)(float rhs) {
|
|
mixin("x", op, "=rhs;");
|
|
mixin("y", op, "=rhs;");
|
|
mixin("z", op, "=rhs;");
|
|
}
|
|
|
|
Vec3 abs() {
|
|
return Vec3(x.abs, y.abs, z.abs);
|
|
}
|
|
|
|
Vec3 floor() {
|
|
return Vec3(x.floor, y.floor, z.floor);
|
|
}
|
|
|
|
Vec3 ceil() {
|
|
return Vec3(x.ceil, y.ceil, z.ceil);
|
|
}
|
|
|
|
Vec3 round() {
|
|
return Vec3(x.round, y.round, z.round);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {} {})".format(x, y, z);
|
|
}
|
|
}
|
|
|
|
struct Vec4 {
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
float z = 0.0f;
|
|
float w = 0.0f;
|
|
|
|
enum zero = Vec4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
enum one = Vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(float x, float y, float z, float w) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
this.w = w;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float x) {
|
|
this(x, x, x, x);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float[4] xyzw) {
|
|
this(xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(IVec4 xyzw) {
|
|
this(xyzw.x, xyzw.y, xyzw.z, xyzw.w);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec4 opUnary(const(char)[] op)() {
|
|
return Vec4(
|
|
mixin(op, "x"),
|
|
mixin(op, "y"),
|
|
mixin(op, "z"),
|
|
mixin(op, "w"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec4 opBinary(const(char)[] op)(Vec4 rhs) {
|
|
return Vec4(
|
|
mixin("x", op, "rhs.x"),
|
|
mixin("y", op, "rhs.y"),
|
|
mixin("z", op, "rhs.z"),
|
|
mixin("w", op, "rhs.w"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec4 opBinary(const(char)[] op)(float rhs) {
|
|
return Vec4(
|
|
mixin("x", op, "rhs"),
|
|
mixin("y", op, "rhs"),
|
|
mixin("z", op, "rhs"),
|
|
mixin("w", op, "rhs"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
Vec4 opBinaryRight(const(char)[] op)(float lhs) {
|
|
return Vec4(
|
|
mixin("lhs", op, "x"),
|
|
mixin("lhs", op, "y"),
|
|
mixin("lhs", op, "z"),
|
|
mixin("lhs", op, "w"),
|
|
);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
void opOpAssign(const(char)[] op)(Vec4 rhs) {
|
|
mixin("x", op, "=rhs.x;");
|
|
mixin("y", op, "=rhs.y;");
|
|
mixin("z", op, "=rhs.z;");
|
|
mixin("w", op, "=rhs.w;");
|
|
}
|
|
|
|
pragma(inline, true)
|
|
void opOpAssign(const(char)[] op)(float rhs) {
|
|
mixin("x", op, "=rhs;");
|
|
mixin("y", op, "=rhs;");
|
|
mixin("z", op, "=rhs;");
|
|
mixin("w", op, "=rhs;");
|
|
}
|
|
|
|
Vec4 abs() {
|
|
return Vec4(x.abs, y.abs, z.abs, w.abs);
|
|
}
|
|
|
|
Vec4 floor() {
|
|
return Vec4(x.floor, y.floor, z.floor, w.floor);
|
|
}
|
|
|
|
Vec4 ceil() {
|
|
return Vec4(x.ceil, y.ceil, z.ceil, w.ceil);
|
|
}
|
|
|
|
Vec4 round() {
|
|
return Vec4(x.round, y.round, z.round, w.round);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {} {} {})".format(x, y, z, w);
|
|
}
|
|
}
|
|
|
|
struct Rect {
|
|
Vec2 position;
|
|
Vec2 size;
|
|
|
|
enum zero = Rect(0.0f, 0.0f, 0.0f, 0.0f);
|
|
enum one = Rect(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 position, Vec2 size) {
|
|
this.position = position;
|
|
this.size = size;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 size) {
|
|
this(Vec2(), size);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float x, float y, float w, float h) {
|
|
this(Vec2(x, y), Vec2(w, h));
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float w, float h) {
|
|
this(Vec2(), Vec2(w, h));
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 position, float w, float h) {
|
|
this(position, Vec2(w, h));
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float x, float y, Vec2 size) {
|
|
this(Vec2(x, y), size);
|
|
}
|
|
|
|
void fix() {
|
|
if (size.x < 0.0f) {
|
|
position.x = position.x + size.x;
|
|
size.x = -size.x;
|
|
}
|
|
if (size.y < 0.0f) {
|
|
position.y = position.y + size.y;
|
|
size.y = -size.y;
|
|
}
|
|
}
|
|
|
|
Rect floor() {
|
|
Rect result = void;
|
|
result.position = position.floor;
|
|
result.size = size.floor;
|
|
return result;
|
|
}
|
|
|
|
Rect ceil() {
|
|
Rect result = void;
|
|
result.position = position.ceil;
|
|
result.size = size.ceil;
|
|
return result;
|
|
}
|
|
|
|
Rect round() {
|
|
Rect result = void;
|
|
result.position = position.round;
|
|
result.size = size.round;
|
|
return result;
|
|
}
|
|
|
|
Vec2 origin(Hook hook) {
|
|
final switch (hook) {
|
|
case Hook.topLeft: return size * Vec2(0.0f, 0.0f);
|
|
case Hook.top: return size * Vec2(0.5f, 0.0f);
|
|
case Hook.topRight: return size * Vec2(1.0f, 0.0f);
|
|
case Hook.left: return size * Vec2(0.0f, 0.5f);
|
|
case Hook.center: return size * Vec2(0.5f, 0.5f);
|
|
case Hook.right: return size * Vec2(1.0f, 0.5f);
|
|
case Hook.bottomLeft: return size * Vec2(0.0f, 1.0f);
|
|
case Hook.bottom: return size * Vec2(0.5f, 1.0f);
|
|
case Hook.bottomRight: return size * Vec2(1.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
Rect area(Hook hook) {
|
|
Rect result = void;
|
|
result.position = position - origin(hook);
|
|
result.size = size;
|
|
return result;
|
|
}
|
|
|
|
Vec2 point(Hook hook) {
|
|
Vec2 result = void;
|
|
result = position + origin(hook);
|
|
return result;
|
|
}
|
|
|
|
Vec2 topLeftPoint() {
|
|
return point(Hook.topLeft);
|
|
}
|
|
|
|
Vec2 topPoint() {
|
|
return point(Hook.top);
|
|
}
|
|
|
|
Vec2 topRightPoint() {
|
|
return point(Hook.topRight);
|
|
}
|
|
|
|
Vec2 leftPoint() {
|
|
return point(Hook.left);
|
|
}
|
|
|
|
Vec2 centerPoint() {
|
|
return point(Hook.center);
|
|
}
|
|
|
|
Vec2 rightPoint() {
|
|
return point(Hook.right);
|
|
}
|
|
|
|
Vec2 bottomLeftPoint() {
|
|
return point(Hook.bottomLeft);
|
|
}
|
|
|
|
Vec2 bottomPoint() {
|
|
return point(Hook.bottom);
|
|
}
|
|
|
|
Vec2 bottomRightPoint() {
|
|
return point(Hook.bottomRight);
|
|
}
|
|
|
|
Rect topLeftArea() {
|
|
return area(Hook.topLeft);
|
|
}
|
|
|
|
Rect topArea() {
|
|
return area(Hook.top);
|
|
}
|
|
|
|
Rect topRightArea() {
|
|
return area(Hook.topRight);
|
|
}
|
|
|
|
Rect leftArea() {
|
|
return area(Hook.left);
|
|
}
|
|
|
|
Rect centerArea() {
|
|
return area(Hook.center);
|
|
}
|
|
|
|
Rect rightArea() {
|
|
return area(Hook.right);
|
|
}
|
|
|
|
Rect bottomLeftArea() {
|
|
return area(Hook.bottomLeft);
|
|
}
|
|
|
|
Rect bottomArea() {
|
|
return area(Hook.bottom);
|
|
}
|
|
|
|
Rect bottomRightArea() {
|
|
return area(Hook.bottomRight);
|
|
}
|
|
|
|
bool hasPoint(Vec2 point) {
|
|
return (
|
|
point.x > position.x &&
|
|
point.x < position.x + size.x &&
|
|
point.y > position.y &&
|
|
point.y < position.y + size.y
|
|
);
|
|
}
|
|
|
|
bool hasIntersection(Rect area) {
|
|
return (
|
|
position.x + size.x > area.position.x &&
|
|
position.x < area.position.x + area.size.x &&
|
|
position.y + size.y > area.position.y &&
|
|
position.y < area.position.y + area.size.y
|
|
);
|
|
}
|
|
|
|
Rect intersection(Rect area) {
|
|
Rect result = void;
|
|
if (!this.hasIntersection(area)) {
|
|
result = Rect();
|
|
} else {
|
|
float maxY = max(position.x, area.position.x);
|
|
float maxX = max(position.y, area.position.y);
|
|
result.position.x = maxX;
|
|
result.position.y = maxY;
|
|
result.size.x = min(position.x + size.x, area.position.x + area.size.x) - maxX;
|
|
result.size.y = min(position.y + size.y, area.position.y + area.size.y) - maxY;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Rect merger(Rect area) {
|
|
Rect result = void;
|
|
float minX = min(position.x, area.position.x);
|
|
float minY = min(position.y, area.position.y);
|
|
result.position.x = minX;
|
|
result.position.y = minY;
|
|
result.size.x = max(position.x + size.x, area.position.x + area.size.x) - minX;
|
|
result.size.y = max(position.y + size.y, area.position.y + area.size.y) - minY;
|
|
return result;
|
|
}
|
|
|
|
Rect addLeft(float amount) {
|
|
position.x -= amount;
|
|
size.x += amount;
|
|
return Rect(position.x, position.y, amount, size.y);
|
|
}
|
|
|
|
Rect addRight(float amount) {
|
|
float w = size.x;
|
|
size.x += amount;
|
|
return Rect(w, position.y, amount, size.y);
|
|
}
|
|
|
|
Rect addTop(float amount) {
|
|
position.y -= amount;
|
|
size.y += amount;
|
|
return Rect(position.x, position.y, size.x, amount);
|
|
}
|
|
|
|
Rect addBottom(float amount) {
|
|
float h = size.y;
|
|
size.y += amount;
|
|
return Rect(position.x, h, size.x, amount);
|
|
}
|
|
|
|
Rect subLeft(float amount) {
|
|
float x = position.x;
|
|
position.x = min(position.x + amount, position.x + size.x);
|
|
size.x = max(size.x - amount, 0.0f);
|
|
return Rect(x, position.y, amount, size.y);
|
|
}
|
|
|
|
Rect subRight(float amount) {
|
|
size.x = max(size.x - amount, 0.0f);
|
|
return Rect(position.x + size.x, position.y, amount, size.y);
|
|
}
|
|
|
|
Rect subTop(float amount) {
|
|
float y = position.y;
|
|
position.y = min(position.y + amount, position.y + size.y);
|
|
size.y = max(size.y - amount, 0.0f);
|
|
return Rect(position.x, y, size.x, amount);
|
|
}
|
|
|
|
Rect subBottom(float amount) {
|
|
size.y = max(size.y - amount, 0.0f);
|
|
return Rect(position.x, position.y + size.y, size.x, amount);
|
|
}
|
|
|
|
Rect addLeftRight(float amount) {
|
|
this.addLeft(amount);
|
|
this.addRight(amount);
|
|
return this;
|
|
}
|
|
|
|
Rect addTopBottom(float amount) {
|
|
this.addTop(amount);
|
|
this.addBottom(amount);
|
|
return this;
|
|
}
|
|
|
|
Rect addAll(float amount) {
|
|
this.addLeftRight(amount);
|
|
this.addTopBottom(amount);
|
|
return this;
|
|
}
|
|
|
|
Rect subLeftRight(float amount) {
|
|
this.subLeft(amount);
|
|
this.subRight(amount);
|
|
return this;
|
|
}
|
|
|
|
Rect subTopBottom(float amount) {
|
|
this.subTop(amount);
|
|
this.subBottom(amount);
|
|
return this;
|
|
}
|
|
|
|
Rect subAll(float amount) {
|
|
this.subLeftRight(amount);
|
|
this.subTopBottom(amount);
|
|
return this;
|
|
}
|
|
|
|
Rect left(float amount) {
|
|
Rect temp = this;
|
|
return temp.subLeft(amount);
|
|
}
|
|
|
|
Rect right(float amount) {
|
|
Rect temp = this;
|
|
return temp.subRight(amount);
|
|
}
|
|
|
|
Rect top(float amount) {
|
|
Rect temp = this;
|
|
return temp.subTop(amount);
|
|
}
|
|
|
|
Rect bottom(float amount) {
|
|
Rect temp = this;
|
|
return temp.subBottom(amount);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {} {} {})".format(position.x, position.y, size.x, size.y);
|
|
}
|
|
}
|
|
|
|
struct Circ {
|
|
Vec2 position;
|
|
float radius = 0.0f;
|
|
|
|
enum zero = Circ(0.0f, 0.0f, 0.0f);
|
|
enum one = Circ(1.0f, 1.0f, 1.0f);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 position, float radius) {
|
|
this.position = position;
|
|
this.radius = radius;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float x, float y, float radius) {
|
|
this(Vec2(x, y), radius);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {} {})".format(position.x, position.y, radius);
|
|
}
|
|
}
|
|
|
|
struct Line {
|
|
Vec2 a;
|
|
Vec2 b;
|
|
|
|
enum zero = Line(0.0f, 0.0f, 0.0f, 0.0f);
|
|
enum one = Line(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 a, Vec2 b) {
|
|
this.a = a;
|
|
this.b = b;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float ax, float ay, float bx, float by) {
|
|
this(Vec2(ax, ay), Vec2(bx, by));
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 a, float bx, float by) {
|
|
this(a, Vec2(bx, by));
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(float ax, float ay, Vec2 b) {
|
|
this(Vec2(ax, ay), b);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {} {} {})".format(a.x, a.y, b.x, b.y);
|
|
}
|
|
}
|
|
|
|
struct IVec2 {
|
|
int x;
|
|
int y;
|
|
|
|
enum zero = IVec2(0, 0);
|
|
enum one = IVec2(1, 1);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(int x) {
|
|
this(x, x);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(int[2] xy) {
|
|
this(xy[0], xy[1]);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec2 xy) {
|
|
this(cast(int) xy.x, cast(int) xy.y);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {})".format(x, y);
|
|
}
|
|
}
|
|
|
|
struct IVec3 {
|
|
int x;
|
|
int y;
|
|
int z;
|
|
|
|
enum zero = IVec3(0, 0, 0);
|
|
enum one = IVec3(1, 1, 1);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(int x, int y, int z) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(int x) {
|
|
this(x, x, x);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(int[3] xyz) {
|
|
this(xyz[0], xyz[1], xyz[2]);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(IVec2 xy, int z) {
|
|
this(xy.x, xy.y, z);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec3 xyz) {
|
|
this(cast(int) xyz.x, cast(int) xyz.y, cast(int) xyz.z);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {})".format(x, y);
|
|
}
|
|
}
|
|
|
|
struct IVec4 {
|
|
int x;
|
|
int y;
|
|
int z;
|
|
int w;
|
|
|
|
enum zero = IVec4(0, 0, 0, 0);
|
|
enum one = IVec4(1, 1, 1, 1);
|
|
|
|
@safe @nogc nothrow:
|
|
|
|
pragma(inline, true)
|
|
this(int x, int y, int z, int w) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
this.w = w;
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(int x) {
|
|
this(x, x, x, x);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(int[4] xyzw) {
|
|
this(xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
|
|
}
|
|
|
|
pragma(inline, true)
|
|
this(Vec4 xyzw) {
|
|
this(cast(int) xyzw.x, cast(int) xyzw.y, cast(int) xyzw.z, cast(int) xyzw.w);
|
|
}
|
|
|
|
const(char)[] toStr() {
|
|
return "({} {})".format(x, y);
|
|
}
|
|
}
|
|
|
|
T min(T)(T a, T b) {
|
|
return a < b ? a : b;
|
|
}
|
|
|
|
T max(T)(T a, T b) {
|
|
return a < b ? b : a;
|
|
}
|
|
|
|
T sign(T)(T x) {
|
|
return x < 0 ? -1 : 1;
|
|
}
|
|
|
|
T abs(T)(T x) {
|
|
return x < 0 ? -x : x;
|
|
}
|
|
|
|
T clamp(T)(T x, T a, T b) {
|
|
return x <= a ? a : x >= b ? b : x;
|
|
}
|
|
|
|
T wrap(T)(T x, T a, T b) {
|
|
auto result = x;
|
|
while (result < a) {
|
|
result += b - a;
|
|
}
|
|
while (result >= b) {
|
|
result -= b - a;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
float floor(float x) {
|
|
float xx = cast(float) cast(int) x;
|
|
return (x <= 0.0f && xx != x) ? xx - 1.0f : xx;
|
|
}
|
|
|
|
float ceil(float x) {
|
|
float xx = cast(float) cast(int) x;
|
|
return (x <= 0.0f || xx == x) ? xx : xx + 1.0f;
|
|
}
|
|
|
|
float round(float x) {
|
|
return x <= 0.0f ? cast(float) cast(int) (x - 0.5f) : cast(float) cast(int) (x + 0.5f);
|
|
}
|
|
|
|
@trusted
|
|
float sqrt(float x) {
|
|
return sqrtf(x);
|
|
}
|
|
|
|
@trusted
|
|
float sin(float x) {
|
|
return sinf(x);
|
|
}
|
|
|
|
@trusted
|
|
float cos(float x) {
|
|
return cosf(x);
|
|
}
|
|
|
|
float lerp(float from, float to, float weight) {
|
|
return from + (to - from) * weight;
|
|
}
|
|
|
|
float smoothstep(float from, float to, float weight) {
|
|
float v = weight * weight * (3.0f - 2.0f * weight);
|
|
return (to * v) + (from * (1.0f - v));
|
|
}
|
|
|
|
float smootherstep(float from, float to, float weight) {
|
|
float v = weight * weight * weight * (weight * (weight * 6.0f - 15.0f) + 10.0f);
|
|
return (to * v) + (from * (1.0f - v));
|
|
}
|
|
|
|
float moveTo(float from, float to, float delta) {
|
|
if (abs(to - from) > abs(delta)) return from + sign(to - from) * delta;
|
|
else return to;
|
|
}
|
|
|
|
float moveTo(float from, float to, float delta, float slowdown) {
|
|
float target = ((from * (slowdown - 1.0f)) + to) / slowdown;
|
|
float offset = target - from;
|
|
if (abs(offset) > abs(delta)) return from + offset * delta;
|
|
else return to;
|
|
}
|
|
|
|
bool equals(float a, float b) {
|
|
return abs(a - b) < epsilon;
|
|
}
|
|
|
|
bool equals(Vec2 a, Vec2 b) {
|
|
return equals(a.x, b.x) && equals(a.y, b.y);
|
|
}
|
|
|
|
bool equals(Vec3 a, Vec3 b) {
|
|
return equals(a.x, b.x) && equals(a.y, b.y) && equals(a.z, b.z);
|
|
}
|
|
|
|
bool equals(Vec4 a, Vec4 b) {
|
|
return equals(a.x, b.x) && equals(a.y, b.y) && equals(a.z, b.z) && equals(a.w, b.w);
|
|
}
|
|
|
|
Vec2 toVec(IVec2 vec) {
|
|
return Vec2(vec);
|
|
}
|
|
|
|
Vec3 toVec(IVec3 vec) {
|
|
return Vec3(vec);
|
|
}
|
|
|
|
Vec4 toVec(IVec4 vec) {
|
|
return Vec4(vec);
|
|
}
|
|
|
|
IVec2 toIVec(Vec2 vec) {
|
|
return IVec2(vec);
|
|
}
|
|
|
|
IVec3 toIVec(Vec3 vec) {
|
|
return IVec3(vec);
|
|
}
|
|
|
|
IVec4 toIVec(Vec4 vec) {
|
|
return IVec4(vec);
|
|
} |