Математическая реализация класса Vector2


Это моя первая попытка построить Vector2 класс. Я перерыла в интернете все, что может сделать этот класс более эффективным, но знаю, что я до точки, где я готова поделиться. Таким образом я могу получить консультацию о том, как я могу улучшить его в дальнейшем. Я хотел сделать это прежде, чем я сделать мой метода Vector3 и Vector4 класс.

Vector2.ч

//VECTOR2 H
#ifndef VECTOR2_H
#define VECTOR2_H

//INCLUDES
#include <math.h>

//DEFINE TYPES
typedef float float32;

//VECTOR2 CLASS
class Vector2
{
public:
//MEMBERS
float32 x;
float32 y;

//CONSTRUCTORS
Vector2(void);
Vector2(float32 xValue, float32 yValue);
Vector2(const Vector2 & v);
Vector2(const Vector2 * v);

//DECONSTRUCTOR
~Vector2(void);

//METHODS
void Set(float32 xValue, float32 yValue);

float32 Length() const;
float32 LengthSquared() const;
float32 Distance(const Vector2 & v) const;
float32 DistanceSquared(const Vector2 & v) const;
float32 Dot(const Vector2 & v) const;
float32 Cross(const Vector2 & v) const;

Vector2 & Normal();
Vector2 & Normalize();

//ASSINGMENT AND EQUALITY OPERATIONS
inline Vector2 & Vector2::operator = (const Vector2 & v) { x = v.x; y = v.y; return *this; }
inline Vector2 & Vector2::operator = (const float32 & f) { x = f; y = f; return *this; }
inline Vector2 & Vector2::operator - (void) { x = -x; y = -y; return *this; }
inline bool Vector2::operator == (const Vector2 & v) const { return (x == v.x) && (y == v.y); }
inline bool Vector2::operator != (const Vector2 & v) const { return (x != v.x) || (y != v.y); }

//VECTOR2 TO VECTOR2 OPERATIONS
inline const Vector2 Vector2::operator + (const Vector2 & v) const { return Vector2(x + v.x, y + v.y); }
inline const Vector2 Vector2::operator - (const Vector2 & v) const { return Vector2(x - v.x, y - v.y); }
inline const Vector2 Vector2::operator * (const Vector2 & v) const { return Vector2(x * v.x, y * v.y); }
inline const Vector2 Vector2::operator / (const Vector2 & v) const { return Vector2(x / v.x, y / v.y); }

//VECTOR2 TO THIS OPERATIONS
inline Vector2 & Vector2::operator += (const Vector2 & v) { x += v.x; y += v.y; return *this; }
inline Vector2 & Vector2::operator -= (const Vector2 & v) { x -= v.x; y -= v.y; return *this; }
inline Vector2 & Vector2::operator *= (const Vector2 & v) { x *= v.x; y *= v.y; return *this; }
inline Vector2 & Vector2::operator /= (const Vector2 & v) { x /= v.x; y /= v.y; return *this; }

//SCALER TO VECTOR2 OPERATIONS
inline const Vector2 Vector2::operator + (float32 v) const { return Vector2(x + v, y + v); }
inline const Vector2 Vector2::operator - (float32 v) const { return Vector2(x - v, y - v); }
inline const Vector2 Vector2::operator * (float32 v) const { return Vector2(x * v, y * v); }
inline const Vector2 Vector2::operator / (float32 v) const { return Vector2(x / v, y / v); }

//SCALER TO THIS OPERATIONS
inline Vector2 & Vector2::operator += (float32 v) { x += v; y += v; return *this; }
inline Vector2 & Vector2::operator -= (float32 v) { x -= v; y -= v; return *this; }
inline Vector2 & Vector2::operator *= (float32 v) { x *= v; y *= v; return *this; }
inline Vector2 & Vector2::operator /= (float32 v) { x /= v; y /= v; return *this; }
};

#endif
//ENDFILE

Vector2.cpp

//VECTOR2 CPP
#include "Vector2.h"

//CONSTRUCTORS
Vector2::Vector2(void) : x(0), y(0) { }
Vector2::Vector2(float32 xValue, float32 yValue) : x(xValue), y(yValue) { }
Vector2::Vector2(const Vector2 & v) : x(v.x), y(v.y) { }
Vector2::Vector2(const Vector2 * v) : x(v->x), y(v->y) { }

//DECONSTRUCTOR
Vector2::~Vector2(void) { }

//METHODS
void Vector2::Set(float32 xValue, float32 yValue) { x = xValue; y = yValue; }

float32 Vector2::Length() const { return sqrt(x * x + y * y); }
float32 Vector2::LengthSquared() const { return x * x + y * y; }
float32 Vector2::Distance(const Vector2 & v) const { return sqrt(((x - v.x) * (x -     v.x)) + ((y - v.y) * (y - v.y))); }
float32 Vector2::DistanceSquared(const Vector2 & v) const { return ((x - v.x) * (x -     v.x)) + ((y - v.y) * (y - v.y)); }
float32 Vector2::Dot(const Vector2 & v) const { return x * v.x + y * v.y; }
float32 Vector2::Cross(const Vector2 & v) const { return x * v.y + y * v.x; }

Vector2 & Vector2::Normal() { Set(-y, x); return *this; }
Vector2 & Vector2::Normalize()
{
if(Length() != 0)
{
    float32 length = LengthSquared();
    x /= length; y /= length;
    return *this;
}

x = y = 0;
return *this;
}

//ENDFILE

Первая Правка:

Поменял крест на OrthoVector:

Vector2 & Vector2::Ortho() { Set(-y, x); return *this; }

Изменен нормальный код, чтобы на самом деле получить нормального вектора:

Vector2 & Vector2::Normal() { float32 len = Length(); Set(x / len, y / len); return *this; }

Второй Редактировать:

Снова изменили обычный код для проверки деления на ноль:

Vector2 & Vector2::Normal()
{
if(Length() != 0)
{
    float32 len = Length();
    x /= len; y /= len;
    return *this;
}

x = y = 0;
return *this;
}

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



4872
6
задан 7 ноября 2011 в 09:11 Источник Поделиться
Комментарии
4 ответа

Вектор-это довольно распространенное имя (как вектор 2/3 и т. д.). Поэтому вам может понадобиться, чтобы сделать ваш включают охранников немного более уникальным. Я всегда кладу свои вещи в мое собственное пространство имен (что происходит, чтобы соответствовать домена, которым я владею, чтобы сделать вещи уникальными). Тогда я включаю пространство имен как части включают в себя охранника. В качестве альтернативы вы можете создать GUID, который также будет убедиться, что он уникален.

//VECTOR2 H
#ifndef VECTOR2_H
#define VECTOR2_H

Шахта будет выглядеть так:

#ifndef BOBS_DOMAIN_VECTOR_2_H
#define BOBS_DOMAIN_VECTOR_2_H

namespace BobsDomain {

Это допустимо, чтобы создать вектор 2 с нулевыми значениями? Если это значит, что Х/Y по умолчанию 0.0 в этом случае следующие 2

Vector2(void);
Vector2(float32 xValue, float32 yValue);

Может быть переопределен как (хотя тогда можно создать Vector2 с одно значение (которое не может быть желательным).

Vector2(float32 xValue = 0.0, float32 yValue = 0.0);

Не по умолчанию, конструктор копирования делать что-то особенное?

Vector2(const Vector2 & v);

Если нет, то я позволю генерируемый компилятором версии, которую вы используете.

Не уверен, что вы хотите быть в состоянии построить из Указателя. Очень мало классов позволяют это (кроме СамАрт указатели). Это значит, что вы принимаете владение указателем или вы просто сделать копию вектора.

Vector2(const Vector2 * v);

Я бы бросил этот конструктор.
Тогда следующий код

Vector2* data = /* Get Vector2 */;
Vector2 copy(data);

Должен быть изменен, как это (не значительные изменения).

Vector2* data =           /* Get Vector2 */;
Vector2 copy(*data); // Notice the extra star.
// But this is not a big cost and simplifies the interface
// considerably as we do not need to wory about ownership
// semantics.

Не ставьте пустоту, когда у вас есть пустой список параметров.
Также если деструктор ничего не делает, затем с помощью компилятора версии.

~Vector2(void);

Почему вы должны установить способ, когда члены являются открытыми.

void Set(float32 xValue, float32 yValue);

Все операторы, которые имеют версию задание легче определить, если определить их с точки зрения оператора присваивания. Это держит смысл последовательны.

Я имею в виду: оператор+ легко определиться в терминах оператора+=

Vector2  const operator+ (Vector2 const& rhs) const {Vector2 result(*this); return result += rhs;}
Vector2& operator+=(Vector2 const& rhs) {x += v.x; y += v.y; return *this;}

Поскольку мы определяем всех остальных операторов, вы можете определить оператор сравнения.

bool operator<(Vector const& rhs)      {return (x < rhs.x) || ((x == rhs.x) && (y < rhs.y));}

Теперь вы можете использовать Vector2 в качестве ключа в std::карта.

6
ответ дан 7 ноября 2011 в 10:11 Источник Поделиться

Векторное произведение не имеет смысла в контексте того, что вы создали его для 2D векторов. Если вы собираетесь на ортогональных вектора, это просто "(Х, Y) -> (у, -Х)".

Ваша "нормальная" функция возвращает ортогональных вектора. "Нормальный" - это vectorland означает "вектор единичной длины в том же направлении".

Для расчета реальной нормальной, это

  ai + bj         double mag = sqrt((a*a) + (b*b));
----------- or Vector2 normal(a / mag, b / mag);
| ai + bj |

1
ответ дан 7 ноября 2011 в 10:11 Источник Поделиться

Одно фундаментальное свойство векторов не представлены: времена скаляр вектор вектор.

Я не смогу реализовать свой вектор * вектор и вектор / векторные операции на всех ... они не обыкновенно полезные.

На самом деле есть смысл векторного произведения в двух измерениях, но возвращаемое значение-скаляр, а не вектор.

float32 Cross(const Vector2& a, const Vector2& b) {
return a.x * b.y - a.y * b.x;
}

Это вызывает еще один момент: я предпочитаю свободные функции на функции-члены, когда смысл примерно симметрично в две переменные, как точка, крест, или даже оператора+. Помимо эстетики, есть то, что оператор+(модификатор const х& а, const и Y и B) можно использовать неявные преобразования на оба операнда, Но Х::оператор+(const и Г& Б) константные можно использовать только неявные преобразования на б.

1
ответ дан 8 ноября 2011 в 12:11 Источник Поделиться

Альтернативные методы возврата экземпляров.

На таких уроках я обычно определяет setAdd и способ setSub, которые заполняют нынешний вектор с результатом сложения (вычитания респ).

void setAdd(const Vector2& v1, const Vector2& v2)
{
x = v1.x + v2.x;
y = v1.y + v2.y;
}

используя такой способ, на месте оператора+ запасные Vector2 копия. В некоторых случаях это видимого влияния на производительность кода (см. физика Хавок математике Либ).

Обрабатывать большее количество случаев использования в нормализации

Для нормализации вектора часто делать дальнейшие вычисления, используя свои предыдущие нормой, и часто требуется, чтобы установить его до нужной нормы. Это легко изменить нормализовать способ для тех случаев.

float32 Vector2::Normalize(float32 desiredNorm = 1.f)
{
float32 len = Length();
if(len < EPSILON)
{
x = y = 0.f;
return 0.f;
}
else
{
float32 mult = desiredNorm/len;
x *= mult; y *= mult;
return len;
}
}

НБ. У меня тоже внести следующие изменения:


  • Только один расчет длины(), квадратный корень вычисление стоят дорого.

  • Использовать меньше, чем Эпсилон, а не иначе-в 0, который может привести к различные проблемы из-за плавающей точкой, значение Эпсилон должна быть небольшой (0.00001 например).

Предоставить метода truncate

Я нашел это очень полезно для определения метода truncate, обеспечивающие верхнюю границу нормы вектора.

void Truncate(float32 upperBound)
{
float32 sqrLen = SqrLength();
if(sqrLen > upperBound * upperBound)
{
float32 mult = upperBound/sqrt(sqrLen);
x *= mult; y *= mult;
}
}

НБ.


  • Вы должны, вероятно, утверждать, что верхним положительный.

  • Снова я пытаюсь минимизировать количество вычисляется квадратный корень.

1
ответ дан 5 декабря 2011 в 02:12 Источник Поделиться