File vector.h¶
File List > endstone > util > vector.h
Go to the documentation of this file
// Copyright (c) 2024, The Endstone Project. (https://endstone.dev) All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <algorithm>
#include <cmath>
#include <fmt/format.h>
namespace endstone {
class Vector {
public:
constexpr Vector() = default;
template <std::convertible_to<float> T>
constexpr Vector(T x, T y, T z) : x_(static_cast<float>(x)), y_(static_cast<float>(y)), z_(static_cast<float>(z))
{
}
Vector(const Vector &other) = default;
Vector(Vector &&other) noexcept = default;
Vector &operator=(const Vector &other) = default;
Vector &operator=(Vector &&other) noexcept = default;
[[nodiscard]] constexpr float getX() const
{
return x_;
}
template <std::convertible_to<float> T>
constexpr Vector &setX(T x)
{
x_ = static_cast<float>(x);
return *this;
}
[[nodiscard]] constexpr float getY() const
{
return y_;
}
template <std::convertible_to<float> T>
constexpr Vector &setY(T y)
{
y_ = static_cast<float>(y);
return *this;
}
[[nodiscard]] constexpr float getZ() const
{
return z_;
}
template <std::convertible_to<float> T>
constexpr Vector &setZ(T z)
{
z_ = static_cast<float>(z);
return *this;
}
[[nodiscard]] int getBlockX() const
{
return static_cast<int>(std::floor(x_));
}
[[nodiscard]] int getBlockY() const
{
return static_cast<int>(std::floor(y_));
}
[[nodiscard]] int getBlockZ() const
{
return static_cast<int>(std::floor(z_));
}
constexpr Vector operator+(const Vector &other) const
{
return {x_ + other.x_, y_ + other.y_, z_ + other.z_};
}
constexpr Vector operator-(const Vector &other) const
{
return {x_ - other.x_, y_ - other.y_, z_ - other.z_};
}
constexpr Vector operator*(const Vector &other) const
{
return {x_ * other.x_, y_ * other.y_, z_ * other.z_};
}
constexpr Vector operator/(const Vector &other) const
{
return {x_ / other.x_, y_ / other.y_, z_ / other.z_};
}
Vector &operator+=(const Vector &other)
{
x_ += other.x_;
y_ += other.y_;
z_ += other.z_;
return *this;
}
Vector &operator-=(const Vector &other)
{
x_ -= other.x_;
y_ -= other.y_;
z_ -= other.z_;
return *this;
}
Vector &operator*=(const Vector &other)
{
x_ *= other.x_;
y_ *= other.y_;
z_ *= other.z_;
return *this;
}
Vector &operator/=(const Vector &other)
{
x_ /= other.x_;
y_ /= other.y_;
z_ /= other.z_;
return *this;
}
template <std::convertible_to<float> T>
constexpr Vector operator+(T scalar) const
{
const auto s = static_cast<float>(scalar);
return {x_ + s, y_ + s, z_ + s};
}
template <std::convertible_to<float> T>
constexpr Vector operator-(T scalar) const
{
const auto s = static_cast<float>(scalar);
return {x_ - s, y_ - s, z_ - s};
}
template <std::convertible_to<float> T>
constexpr Vector operator*(T scalar) const
{
const auto s = static_cast<float>(scalar);
return {x_ * s, y_ * s, z_ * s};
}
template <std::convertible_to<float> T>
constexpr Vector operator/(T scalar) const
{
const auto s = static_cast<float>(scalar);
return {x_ / s, y_ / s, z_ / s};
}
template <typename T>
requires std::convertible_to<T, float>
friend Vector operator+(T scalar, const Vector &v)
{
const auto s = static_cast<float>(scalar);
return {s + v.x_, s + v.y_, s + v.z_};
}
template <typename T>
requires std::convertible_to<T, float>
friend Vector operator-(T scalar, const Vector &v)
{
const auto s = static_cast<float>(scalar);
return {s - v.x_, s - v.y_, s - v.z_};
}
template <typename T>
requires std::convertible_to<T, float>
friend Vector operator*(T scalar, const Vector &v)
{
float s = static_cast<float>(scalar);
return {s * v.x_, s * v.y_, s * v.z_};
}
template <typename T>
requires std::convertible_to<T, float>
friend Vector operator/(T scalar, const Vector &v)
{
float s = static_cast<float>(scalar);
return {s / v.x_, s / v.y_, s / v.z_};
}
bool operator==(const Vector &other) const noexcept
{
constexpr static float eps = 1e-6f;
return (std::fabs(x_ - other.x_) <= eps) && (std::fabs(y_ - other.y_) <= eps) &&
(std::fabs(z_ - other.z_) <= eps);
}
bool operator!=(const Vector &other) const noexcept
{
return !(*this == other);
}
[[nodiscard]] float length() const
{
return std::sqrt(lengthSquared());
}
[[nodiscard]] constexpr float lengthSquared() const
{
return (x_ * x_) + (y_ * y_) + (z_ * z_);
}
[[nodiscard]] float distance(const Vector &other) const
{
return std::sqrt(distanceSquared(other));
}
[[nodiscard]] constexpr float distanceSquared(const Vector &other) const
{
return ((x_ - other.x_) * (x_ - other.x_)) + ((y_ - other.y_) * (y_ - other.y_)) +
((z_ - other.z_) * (z_ - other.z_));
}
[[nodiscard]] float angle(const Vector &other) const
{
const auto dot_product = std::clamp(dot(other) / (length() * other.length()), -1.0f, 1.0f);
return std::acos(dot_product);
}
constexpr Vector &midpoint(const Vector &other)
{
x_ = (x_ + other.x_) / 2;
y_ = (y_ + other.y_) / 2;
z_ = (z_ + other.z_) / 2;
return *this;
}
[[nodiscard]] constexpr Vector getMidpoint(const Vector &other) const
{
auto x = (x_ + other.x_) / 2;
auto y = (y_ + other.y_) / 2;
auto z = (z_ + other.z_) / 2;
return {x, y, z};
}
[[nodiscard]] constexpr float dot(const Vector &other) const
{
return (x_ * other.x_) + (y_ * other.y_) + (z_ * other.z_);
}
constexpr Vector &crossProduct(const Vector &other)
{
const auto new_x = (y_ * other.z_) - (other.y_ * z_);
const auto new_y = (z_ * other.x_) - (other.z_ * x_);
const auto new_z = (x_ * other.y_) - (other.x_ * y_);
x_ = new_x;
y_ = new_y;
z_ = new_z;
return *this;
}
[[nodiscard]] constexpr Vector getCrossProduct(const Vector &other) const
{
float x = (y_ * other.z_) - (other.y_ * z_);
float y = (z_ * other.x_) - (other.z_ * x_);
float z = (x_ * other.y_) - (other.x_ * y_);
return {x, y, z};
}
Vector &normalize()
{
const auto len = length();
x_ /= len;
y_ /= len;
z_ /= len;
return *this;
}
constexpr Vector &zero()
{
x_ = 0;
y_ = 0;
z_ = 0;
return *this;
}
[[nodiscard]] constexpr bool isZero() const
{
return x_ == 0 && y_ == 0 && z_ == 0;
}
constexpr Vector &normalizeZeros()
{
if (x_ == -0.0F) {
x_ = 0.0F;
}
if (y_ == -0.0F) {
y_ = 0.0F;
}
if (z_ == -0.0F) {
z_ = 0.0F;
}
return *this;
}
[[nodiscard]] constexpr bool isInAABB(const Vector &min, const Vector &max) const
{
return x_ >= min.x_ && x_ <= max.x_ && y_ >= min.y_ && y_ <= max.y_ && z_ >= min.z_ && z_ <= max.z_;
}
[[nodiscard]] constexpr bool isInSphere(const Vector &origin, float radius) const
{
return ((origin.x_ - x_) * (origin.x_ - x_) + (origin.y_ - y_) * (origin.y_ - y_) +
(origin.z_ - z_) * (origin.z_ - z_)) <= radius * radius;
}
[[nodiscard]] bool isNormalized() const
{
constexpr static float eps = 1e-6f;
return std::abs(lengthSquared() - 1) < eps;
}
Vector &rotateAroundX(float angle)
{
const auto angle_cos = std::cos(angle);
const auto angle_sin = std::sin(angle);
const auto y = (angle_cos * getY()) - (angle_sin * getZ());
const auto z = (angle_sin * getY()) + (angle_cos * getZ());
return setY(y).setZ(z);
}
Vector &rotateAroundY(float angle)
{
const auto angle_cos = std::cos(angle);
const auto angle_sin = std::sin(angle);
const auto x = (angle_cos * getX()) + (angle_sin * getZ());
const auto z = (-angle_sin * getX()) + (angle_cos * getZ());
return setX(x).setZ(z);
}
Vector &rotateAroundZ(float angle)
{
const auto angle_cos = std::cos(angle);
const auto angle_sin = std::sin(angle);
const auto x = (angle_cos * getX()) - (angle_sin * getY());
const auto y = (angle_sin * getX()) + (angle_cos * getY());
return setX(x).setY(y);
}
Vector &rotateAroundAxis(const Vector &axis, float angle)
{
return rotateAroundNonUnitAxis(axis.isNormalized() ? axis : Vector(axis).normalize(), angle);
}
Vector &rotateAroundNonUnitAxis(const Vector &axis, float angle)
{
const auto x = getX(), y = getY(), z = getZ();
const auto x2 = axis.getX(), y2 = axis.getY(), z2 = axis.getZ();
const auto cos_theta = std::cos(angle);
const auto sin_theta = std::sin(angle);
const auto dot_product = dot(axis);
const auto x_prime =
(x2 * dot_product * (1.0F - cos_theta)) + (x * cos_theta) + ((-z2 * y + y2 * z) * sin_theta);
const auto y_prime =
(y2 * dot_product * (1.0F - cos_theta)) + (y * cos_theta) + ((z2 * x - x2 * z) * sin_theta);
const auto z_prime =
(z2 * dot_product * (1.0F - cos_theta)) + (z * cos_theta) + ((-y2 * x + x2 * y) * sin_theta);
return setX(x_prime).setY(y_prime).setZ(z_prime);
}
protected:
float x_ = 0.0;
float y_ = 0.0;
float z_ = 0.0;
};
} // namespace endstone
template <>
struct fmt::formatter<endstone::Vector> : formatter<string_view> {
template <typename FormatContext>
auto format(const endstone::Vector &self, FormatContext &ctx) const -> format_context::iterator
{
auto out = ctx.out();
return fmt::format_to(out, "{},{},{},", self.getX(), self.getY(), self.getZ());
}
};