Добавлен , опубликован
Моя имплементация векторов (2D и 3D) с полным набором инструментария для работы с ними.

Исходники в тексте:
Vec2
struct Vec2 {
    public float x, y;

    public Vec2 setVal(float x, float y) {
        this.x = x;
        this.y = y;
        return this;
    }

    public Vec2 clear() {
        return this.setVal(0.0, 0.0);
    }

    // Constructors
    public static Vec2 create() {
        return Vec2.allocate().setVal(0.0, 0.0);
    }

    public static Vec2 createAt(float x, float y) {
        return Vec2.allocate().setVal(x, y);
    }

    public static Vec2 createRandom() {
        return Vec2.allocate().setVal(GetRandomReal(-1.0, 1.0), GetRandomReal(-1.0, 1.0));
    }

    // Transitional behavior
    public Vec2 clone() {
        return Vec2.allocate().setVal(this.x, this.y);
    }

    public Vec2 copy(Vec2 another) {
        return this.setVal(another.x, another.y);
    }

    // Math
    public Vec2 add(Vec2 another) {
        return this.setVal(this.x + another.x, this.y + another.y);
    }

    public Vec2 sub(Vec2 another) {
        return this.setVal(this.x - another.x, this.y - another.y);
    }

    public Vec2 scale(float number) {
        return this.setVal(this.x*number, this.y*number);
    }

    public Vec2 div(float number) {
        return this.scale(1.0/number);
    }

    public float dot(Vec2 another) {
        return this.x*another.x + this.y*another.y;
    }

    public float lengthSqr() {
        return this.x*this.x + this.y*this.y;
    }

    public float length() {
        return SquareRoot(this.x*this.x + this.y*this.y);
    }

    public float distToSqr(Vec2 another) {
        return (this.x - another.x)*(this.x - another.x) + (this.y - another.y)*(this.y - another.y);
    }

    public float distTo(Vec2 another) {
        return SquareRoot(this.distToSqr(another));
    }

    public float angle(Vec2 another) {
        return Acos(this.dot(another)/this.length()*another.length());  // radians
    }

    public Vec2 normalize() {
        debug if (this.length == 0.0) { BJDebugMsg("attempt to normalize vector with zero length"); return this; }
        return this.scale(1.0/this.length());
    }

    // Misc.
    public Vec2 reflect(Vec2 normal) {
        Vec2 temp = normal.clone().scale(2.0*this.dot(normal));
        this.sub(temp);
        temp.destroy();
        return this;
    }
}
Vec3
struct Vec3 {
    public float x, y, z;

    public Vec3 setVal(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
        return this;
    }

    public Vec3 clear() {
        return this.setVal(0.0, 0.0, 0.0);
    }

    // Constructors
    public static Vec3 create() {
        return Vec3.allocate().setVal(0.0, 0.0, 0.0);
    }

    public static Vec3 createAt(float x, float y, float z) {
        return Vec3.allocate().setVal(x, y, z);
    }

    public static Vec3 createRandom() {
        return Vec3.allocate().setVal(GetRandomReal(-1.0, 1.0), GetRandomReal(-1.0, 1.0), GetRandomReal(-1.0, 1.0));
    }

    // Transitional behavior
    public Vec3 clone() {
        return Vec3.allocate().setVal(this.x, this.y, this.z);
    }

    public Vec3 copy(Vec3 another) {
        return this.setVal(another.x, another.y, another.z);
    }

    // Math
    public Vec3 add(Vec3 another) {
        return this.setVal(this.x + another.x, this.y + another.y, this.z + another.z);
    }

    public Vec3 sub(Vec3 another) {
        return this.setVal(this.x - another.x, this.y - another.y, this.z - another.z);
    }

    public Vec3 scale(float number) {
        return this.setVal(this.x*number, this.y*number, this.z*number);
    }

    public Vec3 div(float number) {
        return this.scale(1.0/number);
    }

    public float dot(Vec3 another) {
        return this.x*another.x + this.y*another.y + this.z*another.z;
    }

    public float lengthSqr() {
        return this.x*this.x + this.y*this.y + this.z*this.z;
    }

    public float length() {
        return SquareRoot(this.x*this.x + this.y*this.y + this.z*this.z);
    }

    public float distToSqr(Vec3 another) {
        return (this.x - another.x)*(this.x - another.x) + (this.y - another.y)*(this.y - another.y) + (this.z - another.z)*(this.z - another.z);
    }

    public float distTo(Vec3 another) {
        return SquareRoot(this.distToSqr(another));
    }

    public float angle(Vec3 another) {
        return Acos(this.dot(another)/this.length()*another.length());
    }

    public Vec3 normalize() {
        debug if (this.length == 0.0) { BJDebugMsg("attempt to normalize vector with zero length"); return this; }
        return this.scale(1.0/this.length());
    }

    public static Vec3 cross(Vec3 edge1, Vec3 edge2) {
        return Vec3.createAt(edge1.y*edge2.z - edge1.z*edge2.y, edge1.z*edge2.x - edge1.x*edge2.z, edge1.x*edge2.y - edge1.y*edge2.x);
    }

    // Misc.
    public Vec3 reflect(Vec3 normal) {
        Vec3 temp = normal.clone().scale(2.0*this.dot(normal));
        this.sub(temp);
        temp.destroy();
        return this;
    }

    public static Vec3 triangleNormal(Vec3 a, Vec3 b, Vec3 c) {
        Vec3 edge1 = b.clone().sub(a);
        Vec3 edge2 = c.clone().sub(a);
        Vec3 result = Vec3.cross(edge1, edge2).normalize();
        edge1.destroy();
        edge2.destroy();
        return result;
    }
}
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
30
ScorpioT1000, помню, что специально изучал этот вопрос и даже раскладывал модель на низком уровне, чтобы выяснить как оно точно работает. Выходило, что вызов функции для экономии двух делений выгоднее, хотя, конечно, говорить о драматическом росте производительности тут нельзя никаким образом. Это относится только к Vec3, в Vec2 замена деления на умножение с вызовом давали в рассчёте очень-очень небольшой, но оверхед, однако оставил для consistency.

Использование подхода с setVal также в некоторой степени менее оптимально, но здесь во главу угла я ставил собственное удобство и компактность кода вкупе с его читаемостью.

Вопрос: стоит ли закинуть в Warcraft, или пусть здесь будет?
29
normalize не хендлит вектор нулевой длины, имена plus/minus вместо общепринятых и более консистентных add/sub, radius (какой радиус у вектора? у него длина). Умножение вектора на число чаще называют scale.
38
Стоит закинуть в варкрафт, в блогах никто никогда не ищет
30
Doc, здравые комментарии. Радиус поэтому, но тоже здравое замечание.

Каким образом принято хандлить нормализацию нулевого вектора?

Учёл все замечания, кроме нулевого вектора пока что.
29
Как минимум в девелопер режиме принтить ошибку.
30
Полагал, что помимо этого есть какой-нибудь общий подход типа "вернуть сам вектор". Добавил вывод ошибки при включенном дебаге.
29
Где Vec4 с параметрами ct, x, y, z?
Мне интересно применение 4D в варике.. (Псевдо 4-мерного пространства)
30
Инструменты делают для того, чтобы закрывать конкретные потребности, а не для того, чтобы делать инструменты.
38
Дело в том, что нулевой вектор - это вектор без направления по определению. Нормаль - это вектор по сумме с единицей по определению. Нельзя нарушить любое из этих правил. Оставь деление на ноль, лишняя проверка только перегрузит вц3.
Либо можно в дебаг моде поставить иф, но если это точно выпилится вместе с проверкой
29
Возвращать другой вектор - рецепт для еще большего числа ошибок. Нужна просто возможность этот баг поймать, то же самое, что вжасс пишет про double free.
30
Либо можно в дебаг моде поставить иф, но если это точно выпилится вместе с проверкой
Там так и реализовано.
Я знаю, что такое нулевой вектор и что такое нормаль, само собой. Лично у меня никогда не возникало потребности в этой проверке на корректность, но для общего случая дебаг там действительно полезен.

Возвращать другой вектор - рецепт для еще большего числа ошибок. Нужна просто возможность этот баг поймать, то же самое, что вжасс пишет про double free.
Ну да, это понятно, просто высказал предположение, не более.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.