Separate coord space transform class for more correct scaling
This commit is contained in:
parent
46370e5762
commit
d261293d6b
7 changed files with 307 additions and 7 deletions
|
|
@ -33,6 +33,7 @@ set(sources "src/main.cpp"
|
|||
"src/DrawCamera.cpp"
|
||||
"src/DrawCollision.cpp"
|
||||
"src/CoordSpaceHelper.cpp"
|
||||
"src/CoordSpaceHelperTransform.cpp"
|
||||
"src/ResourceManager.cpp"
|
||||
"src/Toolbar.cpp"
|
||||
"src/World.cpp"
|
||||
|
|
|
|||
|
|
@ -318,9 +318,191 @@ namespace FixedPoint {
|
|||
private:
|
||||
T val;
|
||||
};
|
||||
|
||||
template <typename T, int bitFractionCount> class Multiplier {
|
||||
public:
|
||||
typedef Number<T, bitFractionCount> UnderlyingNumber;
|
||||
|
||||
Multiplier():
|
||||
val(1),
|
||||
isReciprocal(false)
|
||||
{}
|
||||
|
||||
Multiplier(const UnderlyingNumber& initVal):
|
||||
val(initVal),
|
||||
isReciprocal(false)
|
||||
{
|
||||
if(val != UnderlyingNumber(0) && abs(val) < UnderlyingNumber(1)) {
|
||||
val = UnderlyingNumber(1) / val;
|
||||
isReciprocal = true;
|
||||
}
|
||||
}
|
||||
|
||||
Multiplier(const UnderlyingNumber& initVal, bool isReciprocal):
|
||||
val(initVal),
|
||||
isReciprocal(isReciprocal)
|
||||
{
|
||||
if(abs(val) == UnderlyingNumber(1))
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit Multiplier(double initVal) {
|
||||
if(std::abs(initVal) < 1.0) {
|
||||
val = UnderlyingNumber(1.0 / initVal);
|
||||
isReciprocal = true;
|
||||
}
|
||||
else {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
}
|
||||
|
||||
explicit Multiplier(float initVal) {
|
||||
if(std::abs(initVal) < 1.0) {
|
||||
val = UnderlyingNumber(1.0 / initVal);
|
||||
isReciprocal = true;
|
||||
}
|
||||
else {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
}
|
||||
|
||||
explicit Multiplier(int64_t initVal) {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit Multiplier(int32_t initVal) {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit Multiplier(int16_t initVal) {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit Multiplier(uint64_t initVal) {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit Multiplier(uint32_t initVal) {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit Multiplier(uint16_t initVal) {
|
||||
val = UnderlyingNumber(initVal);
|
||||
isReciprocal = false;
|
||||
}
|
||||
|
||||
explicit operator float() const {
|
||||
if(isReciprocal)
|
||||
return 1.0f / static_cast<float>(val);
|
||||
else
|
||||
return static_cast<float>(val);
|
||||
}
|
||||
|
||||
explicit operator double() const {
|
||||
if(isReciprocal)
|
||||
return 1.0 / static_cast<double>(val);
|
||||
else
|
||||
return static_cast<double>(val);
|
||||
}
|
||||
|
||||
explicit operator int64_t() const {
|
||||
if(isReciprocal)
|
||||
return 0;
|
||||
else
|
||||
return static_cast<int64_t>(val);
|
||||
}
|
||||
|
||||
explicit operator uint64_t() const {
|
||||
if(isReciprocal)
|
||||
return 0;
|
||||
else
|
||||
return static_cast<uint64_t>(val);
|
||||
}
|
||||
|
||||
explicit operator UnderlyingNumber() const {
|
||||
if(isReciprocal)
|
||||
return UnderlyingNumber(1) / val;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
Multiplier multiply_double(double a) const {
|
||||
return (*this) * Multiplier(a);
|
||||
}
|
||||
|
||||
Multiplier divide_double(double a) const {
|
||||
return (*this) / Multiplier(a);
|
||||
}
|
||||
|
||||
Multiplier operator*(const Multiplier& a) const {
|
||||
return (*this) / Multiplier(a.val, !a.isReciprocal);
|
||||
}
|
||||
|
||||
Multiplier operator/(const Multiplier& a) const {
|
||||
if(val == UnderlyingNumber(0))
|
||||
return Multiplier(0);
|
||||
|
||||
if(!isReciprocal && !a.isReciprocal) {
|
||||
if(abs(val) < abs(a.val))
|
||||
return Multiplier(a.val / val, true);
|
||||
else
|
||||
return Multiplier(val / a.val, false);
|
||||
}
|
||||
else if(!isReciprocal && a.isReciprocal) {
|
||||
return Multiplier(val * a.val, false);
|
||||
}
|
||||
else if(isReciprocal && !a.isReciprocal) {
|
||||
if(abs(val) < abs(a.val))
|
||||
return Multiplier(a.val / val, false);
|
||||
else
|
||||
return Multiplier(val / a.val, true);
|
||||
}
|
||||
else {
|
||||
if(abs(val) < abs(a.val))
|
||||
return Multiplier(a.val / val, false);
|
||||
else
|
||||
return Multiplier(val / a.val, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Multiplier& other) const {
|
||||
return (val == other.val) && (isReciprocal == other.isReciprocal || val == UnderlyingNumber(1));
|
||||
}
|
||||
|
||||
bool operator!=(const Multiplier& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
private:
|
||||
Number<T, bitFractionCount> val;
|
||||
bool isReciprocal;
|
||||
};
|
||||
|
||||
template <typename Mult> Mult::UnderlyingNumber operator*(const Mult& a, const typename Mult::UnderlyingNumber& b) {
|
||||
return static_cast<Mult::UnderlyingNumber>(a * Multiplier(b));
|
||||
}
|
||||
|
||||
template <typename Mult> Mult::UnderlyingNumber operator*(const typename Mult::UnderlyingNumber& b, const Mult& a) {
|
||||
return static_cast<Mult::UnderlyingNumber>(a * Multiplier(b));
|
||||
}
|
||||
|
||||
template <typename Mult> Mult::UnderlyingNumber operator/(const Mult& a, const typename Mult::UnderlyingNumber& b) {
|
||||
return static_cast<Mult::UnderlyingNumber>(a / Multiplier(b));
|
||||
}
|
||||
|
||||
template <typename Mult> Mult::UnderlyingNumber operator/(const typename Mult::UnderlyingNumber& a, const Mult& b) {
|
||||
return static_cast<Mult::UnderlyingNumber>(Multiplier(a) / b);
|
||||
}
|
||||
}
|
||||
|
||||
typedef FixedPoint::Number<boost::multiprecision::cpp_int, 32> WorldScalar;
|
||||
typedef FixedPoint::Multiplier<boost::multiprecision::cpp_int, 32> WorldMultiplier;
|
||||
|
||||
namespace Eigen {
|
||||
template<> struct NumTraits<WorldScalar> : GenericNumTraits<WorldScalar>
|
||||
|
|
@ -344,3 +526,19 @@ namespace Eigen {
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
namespace FixedPoint {
|
||||
template <typename Mult> Eigen::Vector<typename Mult::UnderlyingNumber, 2> multiplier_vec_mult(const Eigen::Vector<typename Mult::UnderlyingNumber, 2>& a, const Mult& m) {
|
||||
auto toRet = a;
|
||||
toRet.x() = toRet.x() * m;
|
||||
toRet.y() = toRet.y() * m;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
template <typename Mult> Eigen::Vector<typename Mult::UnderlyingNumber, 2> multiplier_vec_div(const Eigen::Vector<typename Mult::UnderlyingNumber, 2>& a, const Mult& m) {
|
||||
auto toRet = a;
|
||||
toRet.x() = toRet.x() / m;
|
||||
toRet.y() = toRet.y() / m;
|
||||
return toRet;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
class World;
|
||||
struct DrawData;
|
||||
|
||||
WorldVec rotate_world_coord(const WorldVec& a, double rotationAngle);
|
||||
|
||||
class CoordSpaceHelper {
|
||||
public:
|
||||
template <typename Archive> void save(Archive& a) const {
|
||||
|
|
@ -82,8 +84,8 @@ class CoordSpaceHelper {
|
|||
void translate(const WorldVec& translation);
|
||||
void scale(const WorldScalar& scaleAmount);
|
||||
void rotate_about(const WorldVec& rotatePos, double angle);
|
||||
void scale_about_double(const WorldVec& scalePos, double scaleAmount);
|
||||
void scale_about(const WorldVec& scalePos, const WorldScalar& scaleAmount, bool flipScale);
|
||||
void scale_about_double(const WorldVec& scalePos, double scaleAmount);
|
||||
void set_rotation(double newRotation);
|
||||
|
||||
template <size_t S> std::array<Vector2f, S> to_space(const std::array<WorldVec, S>& coord) const {
|
||||
|
|
|
|||
63
src/CoordSpaceHelperTransform.cpp
Normal file
63
src/CoordSpaceHelperTransform.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "CoordSpaceHelperTransform.hpp"
|
||||
|
||||
bool CoordSpaceHelperTransform::operator==(const CoordSpaceHelperTransform& otherCoords) const {
|
||||
return rotation == otherCoords.rotation && pos == otherCoords.pos && inverseScale == otherCoords.inverseScale;
|
||||
}
|
||||
|
||||
bool CoordSpaceHelperTransform::operator!=(const CoordSpaceHelperTransform& otherCoords) const {
|
||||
return rotation != otherCoords.rotation || pos != otherCoords.pos || inverseScale != otherCoords.inverseScale;
|
||||
}
|
||||
|
||||
WorldVec CoordSpaceHelperTransform::to_space_world(const WorldVec& coord) const {
|
||||
return rotate_world_coord(FixedPoint::multiplier_vec_div((coord - pos), inverseScale), -rotation);
|
||||
}
|
||||
|
||||
WorldVec CoordSpaceHelperTransform::from_space_world(const WorldVec& coord) const {
|
||||
return FixedPoint::multiplier_vec_mult(rotate_world_coord(coord, rotation), inverseScale) + pos;
|
||||
}
|
||||
|
||||
CoordSpaceHelper CoordSpaceHelperTransform::other_coord_space_to_this_space(const CoordSpaceHelper& other) const {
|
||||
CoordSpaceHelper toRet;
|
||||
toRet.pos = to_space_world(other.pos);
|
||||
toRet.rotation = other.rotation - rotation;
|
||||
toRet.inverseScale = other.inverseScale / inverseScale;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
CoordSpaceHelper CoordSpaceHelperTransform::other_coord_space_from_this_space(const CoordSpaceHelper& other) const {
|
||||
CoordSpaceHelper toRet;
|
||||
toRet.pos = from_space_world(other.pos);
|
||||
toRet.rotation = other.rotation + rotation;
|
||||
toRet.inverseScale = other.inverseScale * inverseScale;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void CoordSpaceHelperTransform::translate(const WorldVec& translation) {
|
||||
pos += translation;
|
||||
}
|
||||
|
||||
void CoordSpaceHelperTransform::rotate_about(const WorldVec& rotatePos, double angle) {
|
||||
WorldVec a = pos - rotatePos;
|
||||
FixedPoint::Number<boost::multiprecision::cpp_int, 15> s = FixedPoint::Number<boost::multiprecision::cpp_int, 15>(std::sin(angle));
|
||||
FixedPoint::Number<boost::multiprecision::cpp_int, 15> c = FixedPoint::Number<boost::multiprecision::cpp_int, 15>(std::cos(angle));
|
||||
set_rotation(rotation + angle);
|
||||
pos = {
|
||||
rotatePos.x() + a.x().multiply_different_precision(c) - a.y().multiply_different_precision(s),
|
||||
rotatePos.y() + a.x().multiply_different_precision(s) + a.y().multiply_different_precision(c)
|
||||
};
|
||||
}
|
||||
|
||||
void CoordSpaceHelperTransform::set_rotation(double newRotation) {
|
||||
rotation = newRotation;
|
||||
// using circular fmod caused inaccuracies, so these loops might be a better solution
|
||||
while(rotation >= 2.0 * std::numbers::pi)
|
||||
rotation -= 2.0 * std::numbers::pi;
|
||||
while (rotation < 0.0)
|
||||
rotation += 2.0 * std::numbers::pi;
|
||||
}
|
||||
|
||||
void CoordSpaceHelperTransform::scale_about(const WorldVec& scalePos, const WorldMultiplier& scaleAmount) {
|
||||
inverseScale = inverseScale / scaleAmount;
|
||||
WorldVec mVec = pos - scalePos;
|
||||
pos = scalePos + FixedPoint::multiplier_vec_div(mVec, scaleAmount);
|
||||
}
|
||||
35
src/CoordSpaceHelperTransform.hpp
Normal file
35
src/CoordSpaceHelperTransform.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include "SharedTypes.hpp"
|
||||
#include <Helpers/Serializers.hpp>
|
||||
#include <array>
|
||||
#include <Helpers/SCollision.hpp>
|
||||
#include "CoordSpaceHelper.hpp"
|
||||
|
||||
#ifndef IS_SERVER
|
||||
#include "include/core/SkMatrix.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#endif
|
||||
|
||||
class World;
|
||||
struct DrawData;
|
||||
|
||||
class CoordSpaceHelperTransform {
|
||||
public:
|
||||
WorldVec from_space_world(const WorldVec& coord) const;
|
||||
WorldVec to_space_world(const WorldVec& coord) const;
|
||||
|
||||
CoordSpaceHelper other_coord_space_to_this_space(const CoordSpaceHelper& other) const;
|
||||
CoordSpaceHelper other_coord_space_from_this_space(const CoordSpaceHelper& other) const;
|
||||
|
||||
bool operator==(const CoordSpaceHelperTransform& otherCoords) const;
|
||||
bool operator!=(const CoordSpaceHelperTransform& otherCoords) const;
|
||||
|
||||
void set_rotation(double newRotation);
|
||||
void translate(const WorldVec& translation);
|
||||
void rotate_about(const WorldVec& rotatePos, double angle);
|
||||
void scale_about(const WorldVec& scalePos, const WorldMultiplier& scaleAmount);
|
||||
|
||||
WorldVec pos{0, 0};
|
||||
WorldMultiplier inverseScale{1};
|
||||
double rotation = 0.0;
|
||||
};
|
||||
|
|
@ -283,7 +283,7 @@ void DrawingProgramSelection::update() {
|
|||
scaleAmount = centerToScaleStartNorm / centerToScaleCurrentNorm;
|
||||
|
||||
selectionTransformCoords = startingSelectionTransformCoords;
|
||||
selectionTransformCoords.scale_about(scaleData.centerPos, scaleAmount, flipScale);
|
||||
selectionTransformCoords.scale_about(scaleData.centerPos, WorldMultiplier(scaleAmount, flipScale));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -313,9 +313,9 @@ void DrawingProgramSelection::update() {
|
|||
deselect_all();
|
||||
else if(drawP.world.main.input.key(InputManager::KEY_DRAW_DELETE).pressed)
|
||||
delete_all();
|
||||
else if(drawP.world.main.input.key(InputManager::KEY_COPY).pressed && startingSelectionTransformCoords == CoordSpaceHelper())
|
||||
else if(drawP.world.main.input.key(InputManager::KEY_COPY).pressed && startingSelectionTransformCoords == CoordSpaceHelperTransform())
|
||||
selection_to_clipboard();
|
||||
else if(drawP.world.main.input.key(InputManager::KEY_CUT).pressed && startingSelectionTransformCoords == CoordSpaceHelper()) {
|
||||
else if(drawP.world.main.input.key(InputManager::KEY_CUT).pressed && startingSelectionTransformCoords == CoordSpaceHelperTransform()) {
|
||||
selection_to_clipboard();
|
||||
delete_all();
|
||||
}
|
||||
|
|
@ -448,7 +448,7 @@ void DrawingProgramSelection::reset_selection_data() {
|
|||
}
|
||||
|
||||
void DrawingProgramSelection::reset_transform_data() {
|
||||
selectionTransformCoords = CoordSpaceHelper();
|
||||
selectionTransformCoords = CoordSpaceHelperTransform();
|
||||
transformOpHappening = TransformOperation::NONE;
|
||||
translateData = TranslationData();
|
||||
scaleData = ScaleData();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "DrawingProgramCache.hpp"
|
||||
#include "../CoordSpaceHelperTransform.hpp"
|
||||
|
||||
class DrawingProgram;
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ class DrawingProgramSelection {
|
|||
|
||||
std::array<WorldVec, 4> selectionRectPoints;
|
||||
|
||||
CoordSpaceHelper selectionTransformCoords;
|
||||
CoordSpaceHelperTransform selectionTransformCoords;
|
||||
std::unordered_set<CollabListType::ObjectInfoPtr> selectedSet;
|
||||
DrawingProgramCache cache;
|
||||
DrawingProgram& drawP;
|
||||
|
|
@ -53,7 +54,7 @@ class DrawingProgramSelection {
|
|||
ROTATE
|
||||
} transformOpHappening = TransformOperation::NONE;
|
||||
|
||||
CoordSpaceHelper startingSelectionTransformCoords;
|
||||
CoordSpaceHelperTransform startingSelectionTransformCoords;
|
||||
|
||||
struct TranslationData {
|
||||
WorldVec startPos;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue