Fix some deprecation warnings

Namely:

 - [[deprecated("Use Mat2")]]
 - [[deprecated("Use Vec2")]]
 - [[deprecated("Use Mat2::operator*(Vec2)")]]
 - [[deprecated("Use Vec2<float>::operator*(float)")]]
 - [[deprecated("Use Vec2::operator+")]]
 - [[deprecated("Use Vec2::operator-")]]
 - [[deprecated("Use Vec2::Zero")]]
 - [[deprecated("Use Mat2::Identity")]]
This commit is contained in:
Tamás Bálint Misius 2023-05-12 16:54:35 +02:00
parent c8bc8a7285
commit 9bd8bd2274
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
12 changed files with 232 additions and 387 deletions

View File

@ -5,72 +5,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <cmath> #include <cmath>
matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2)
{
matrix2d result = {
m1.a*m2.a+m1.b*m2.c, m1.a*m2.b+m1.b*m2.d,
m1.c*m2.a+m1.d*m2.c, m1.c*m2.b+m1.d*m2.d
};
return result;
}
vector2d m2d_multiply_v2d(matrix2d m, vector2d v)
{
vector2d result = {
m.a*v.x+m.b*v.y,
m.c*v.x+m.d*v.y
};
return result;
}
matrix2d m2d_multiply_float(matrix2d m, float s)
{
matrix2d result = {
m.a*s, m.b*s,
m.c*s, m.d*s,
};
return result;
}
vector2d v2d_multiply_float(vector2d v, float s)
{
vector2d result = {
v.x*s,
v.y*s
};
return result;
}
vector2d v2d_add(vector2d v1, vector2d v2)
{
vector2d result = {
v1.x+v2.x,
v1.y+v2.y
};
return result;
}
vector2d v2d_sub(vector2d v1, vector2d v2)
{
vector2d result = {
v1.x-v2.x,
v1.y-v2.y
};
return result;
}
matrix2d m2d_new(float me0, float me1, float me2, float me3)
{
matrix2d result = {me0,me1,me2,me3};
return result;
}
vector2d v2d_new(float x, float y)
{
vector2d result = {x, y};
return result;
}
void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB
{ {
float hh, ss, vv, c, x; float hh, ss, vv, c, x;
@ -152,9 +86,6 @@ void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize)
} }
} }
vector2d v2d_zero = {0,0};
matrix2d m2d_identity = {1,0,0,1};
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size) bool byteStringEqualsString(const ByteString &str, const char *data, size_t size)
{ {
return str.size() == size && !memcmp(str.data(), data, size); return str.size() == size && !memcmp(str.data(), data, size);

View File

@ -57,45 +57,6 @@ void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b);
void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v); void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v);
void membwand(void * dest, void * src, size_t destsize, size_t srcsize); void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
// a b
// c d
struct matrix2d {
[[deprecated("Use Mat2")]]
float a,b,c,d;
};
[[deprecated("Use Mat2")]]
typedef struct matrix2d matrix2d;
// column vector
struct vector2d {
[[deprecated("Use Vec2")]]
float x,y;
};
[[deprecated("Use Vec2")]]
typedef struct vector2d vector2d;
//matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
[[deprecated("Use Mat2::operator*(Vec2)")]]
vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
//matrix2d m2d_multiply_float(matrix2d m, float s);
[[deprecated("Use Vec2<float>::operator*(float)")]]
vector2d v2d_multiply_float(vector2d v, float s);
[[deprecated("Use Vec2::operator+")]]
vector2d v2d_add(vector2d v1, vector2d v2);
[[deprecated("Use Vec2::operator-")]]
vector2d v2d_sub(vector2d v1, vector2d v2);
[[deprecated("Use Mat2")]]
matrix2d m2d_new(float me0, float me1, float me2, float me3);
[[deprecated("Use Vec2")]]
vector2d v2d_new(float x, float y);
[[deprecated("Use Vec2::Zero")]]
extern vector2d v2d_zero;
[[deprecated("Use Mat2::Identity")]]
extern matrix2d m2d_identity;
class ByteString; class ByteString;
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size); bool byteStringEqualsString(const ByteString &str, const char *data, size_t size);

View File

@ -111,232 +111,141 @@ std::pair<bool, std::vector<char>> GameSave::Serialise() const
return { false, {} }; return { false, {} };
} }
vector2d GameSave::Translate(vector2d translate) extern const std::array<Vec2<int>, 8> Element_PIPE_offsets;
void Element_PIPE_transformPatchOffsets(Particle &part, const std::array<int, 8> &offsetMap);
void GameSave::Transform(Mat2<int> transform, Vec2<int> nudge)
{ {
float nx, ny; // undo translation by rotation
vector2d pos; auto br = transform * (blockSize * CELL - Vec2{ 1, 1 });
vector2d translateReal = translate; auto bbr = transform * (blockSize - Vec2{ 1, 1 });
float minx = 0, miny = 0, maxx = 0, maxy = 0; auto translate = Vec2{ std::max(0, -br.X), std::max(0, -br.Y) };
// determine minimum and maximum position of all particles / signs auto btranslate = Vec2{ std::max(0, -bbr.X), std::max(0, -bbr.Y) };
for (size_t i = 0; i < signs.size(); i++) auto newBlockS = transform * blockSize;
newBlockS.X = std::abs(newBlockS.X);
newBlockS.Y = std::abs(newBlockS.Y);
translate += nudge;
// Grow as needed.
assert((Vec2{ CELL, CELL }.OriginRect().Contains(nudge)));
if (nudge.X) newBlockS.X += 1;
if (nudge.Y) newBlockS.Y += 1;
// TODO: allow transforms to yield bigger saves. For this we'd need SaveRenderer (the singleton, not Renderer)
// to fully render them (possible with stitching) and Simulation::Load to be able to take only the part that fits.
newBlockS = newBlockS.Clamp(RectBetween({ 0, 0 }, CELLS));
auto newPartS = newBlockS * CELL;
// Prepare to patch pipes.
std::array<int, 8> pipeOffsetMap;
{ {
pos = v2d_new(float(signs[i].x), float(signs[i].y)); std::transform(Element_PIPE_offsets.begin(), Element_PIPE_offsets.end(), pipeOffsetMap.begin(), [transform](auto offset) {
pos = v2d_add(pos,translate); auto it = std::find(Element_PIPE_offsets.begin(), Element_PIPE_offsets.end(), transform * offset);
nx = floor(pos.x+0.5f); assert(it != Element_PIPE_offsets.end());
ny = floor(pos.y+0.5f); return int(it - Element_PIPE_offsets.begin());
if (nx < minx) });
minx = nx;
if (ny < miny)
miny = ny;
if (nx > maxx)
maxx = nx;
if (ny > maxy)
maxy = ny;
} }
for (int i = 0; i < particlesCount; i++)
// Translate signs.
for (auto i = 0U; i < signs.size(); i++)
{ {
if (!particles[i].type) continue; auto newPos = transform * Vec2{ signs[i].x, signs[i].y } + translate;
pos = v2d_new(particles[i].x, particles[i].y); if (!newPartS.OriginRect().Contains(newPos))
pos = v2d_add(pos,translate);
nx = floor(pos.x+0.5f);
ny = floor(pos.y+0.5f);
if (nx < minx)
minx = nx;
if (ny < miny)
miny = ny;
if (nx > maxx)
maxx = nx;
if (ny > maxy)
maxy = ny;
}
// determine whether corrections are needed. If moving in this direction would delete stuff, expand the save
vector2d backCorrection = v2d_new(
(minx < 0) ? (-floor(minx / CELL)) : 0,
(miny < 0) ? (-floor(miny / CELL)) : 0
);
int blockBoundsX = int(maxx / CELL) + 1, blockBoundsY = int(maxy / CELL) + 1;
vector2d frontCorrection = v2d_new(
float((blockBoundsX > blockSize.X) ? (blockBoundsX - blockSize.X) : 0),
float((blockBoundsY > blockSize.Y) ? (blockBoundsY - blockSize.Y) : 0)
);
// get new width based on corrections
auto newWidth = int((blockSize.X + backCorrection.x + frontCorrection.x) * CELL);
auto newHeight = int((blockSize.Y + backCorrection.y + frontCorrection.y) * CELL);
if (newWidth > XRES)
frontCorrection.x = backCorrection.x = 0;
if (newHeight > YRES)
frontCorrection.y = backCorrection.y = 0;
// call Transform to do the transformation we wanted when calling this function
translate = v2d_add(translate, v2d_multiply_float(backCorrection, CELL));
Transform(m2d_identity, translate, translateReal, {
int((blockSize.X + backCorrection.x + frontCorrection.x) * CELL),
int((blockSize.Y + backCorrection.y + frontCorrection.y) * CELL)
});
// return how much we corrected. This is used to offset the position of the current stamp
// otherwise it would attempt to recenter it with the current size
return v2d_add(v2d_multiply_float(backCorrection, -CELL), v2d_multiply_float(frontCorrection, CELL));
}
void GameSave::Transform(matrix2d transform, vector2d translate)
{
int width = blockSize.X*CELL, height = blockSize.Y*CELL;
vector2d tmp, ctl, cbr;
vector2d cornerso[4];
vector2d translateReal = translate;
// undo any translation caused by rotation
cornerso[0] = v2d_new(0,0);
cornerso[1] = v2d_new(float(width-1),0);
cornerso[2] = v2d_new(0,float(height-1));
cornerso[3] = v2d_new(float(width-1),float(height-1));
for (int i = 0; i < 4; i++)
{
tmp = m2d_multiply_v2d(transform,cornerso[i]);
if (i==0) ctl = cbr = tmp; // top left, bottom right corner
if (tmp.x<ctl.x) ctl.x = tmp.x;
if (tmp.y<ctl.y) ctl.y = tmp.y;
if (tmp.x>cbr.x) cbr.x = tmp.x;
if (tmp.y>cbr.y) cbr.y = tmp.y;
}
// casting as int doesn't quite do what we want with negative numbers, so use floor()
tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f));
translate = v2d_sub(translate,tmp);
auto newWidth = int(floor(cbr.x+0.5f))-int(floor(ctl.x+0.5f))+1;
auto newHeight = int(floor(cbr.y+0.5f))-int(floor(ctl.y+0.5f))+1;
Transform(transform, translate, translateReal, { newWidth, newHeight });
}
// transform is a matrix describing how we want to rotate this save
// translate can vary depending on whether the save is bring rotated, or if a normal translate caused it to expand
// translateReal is the original amount we tried to translate, used to calculate wall shifting
void GameSave::Transform(matrix2d transform, vector2d translate, vector2d translateReal, Vec2<int> newPartSize)
{
if (newPartSize.X>XRES) newPartSize.X = XRES;
if (newPartSize.Y>YRES) newPartSize.Y = YRES;
auto newBlockSize = newPartSize / CELL;
PlaneAdapter<std::vector<unsigned char>> blockMapNew(newBlockSize, 0);
PlaneAdapter<std::vector<float>> fanVelXNew(newBlockSize, 0.0f);
PlaneAdapter<std::vector<float>> fanVelYNew(newBlockSize, 0.0f);
PlaneAdapter<std::vector<float>> pressureNew(newBlockSize, 0.0f);
PlaneAdapter<std::vector<float>> velocityXNew(newBlockSize, 0.0f);
PlaneAdapter<std::vector<float>> velocityYNew(newBlockSize, 0.0f);
PlaneAdapter<std::vector<float>> ambientHeatNew(newBlockSize, 0.0f);
PlaneAdapter<std::vector<unsigned char>> blockAirNew(newBlockSize, 0);
PlaneAdapter<std::vector<unsigned char>> blockAirhNew(newBlockSize, 0);
// Match these up with the matrices provided in GameView::OnKeyPress.
bool patchPipeR = transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0;
bool patchPipeH = transform.a == -1 && transform.b == 0 && transform.c == 0 && transform.d == 1;
bool patchPipeV = transform.a == 1 && transform.b == 0 && transform.c == 0 && transform.d == -1;
// rotate and translate signs, parts, walls
for (size_t i = 0; i < signs.size(); i++)
{
auto pos = v2d_new(float(signs[i].x), float(signs[i].y));
pos = v2d_add(m2d_multiply_v2d(transform,pos),translate);
auto newPos = Vec2{ int(floor(pos.x+0.5f)), int(floor(pos.y+0.5f)) };
if (!newPartSize.OriginRect().Contains(newPos))
{ {
signs[i].text[0] = 0; signs[i].text.clear();
continue; continue;
} }
signs[i].x = newPos.X; signs[i].x = newPos.X;
signs[i].y = newPos.Y; signs[i].y = newPos.Y;
} }
// Translate particles.
for (int i = 0; i < particlesCount; i++) for (int i = 0; i < particlesCount; i++)
{ {
if (!particles[i].type) continue; if (!particles[i].type)
auto pos = v2d_new(particles[i].x, particles[i].y); {
pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); continue;
auto newPos = Vec2{ int(floor(pos.x+0.5f)), int(floor(pos.y+0.5f)) }; }
if (!newPartSize.OriginRect().Contains(newPos)) {
// * We want particles to retain their distance from the centre of the particle grid cell
// they are in, but more importantly we also don't want them to change grid cells,
// so we just get rid of the edge cases.
constexpr auto threshold = 0.001f;
auto boundaryDiffX = particles[i].x - (floor(particles[i].x) + 0.5f);
if (fabs(boundaryDiffX) < threshold)
{
particles[i].x += copysign(threshold, boundaryDiffX);
}
auto boundaryDiffY = particles[i].y - (floor(particles[i].y) + 0.5f);
if (fabs(boundaryDiffY) < threshold)
{
particles[i].y += copysign(threshold, boundaryDiffY);
}
}
if (particles[i].x - floor(particles[i].x) == 0.5f) particles[i].x += 0.001f;
if (particles[i].y - floor(particles[i].y) == 0.5f) particles[i].y += 0.001f;
auto newPos = transform * Vec2{ particles[i].x, particles[i].y } + translate;
if (!newPartS.OriginRect().Contains(Vec2{ int(floor(newPos.X + 0.5f)), int(floor(newPos.Y + 0.5f)) }))
{ {
particles[i].type = PT_NONE; particles[i].type = PT_NONE;
continue; continue;
} }
particles[i].x = float(newPos.X); particles[i].x = newPos.X;
particles[i].y = float(newPos.Y); particles[i].y = newPos.Y;
auto vel = v2d_new(particles[i].vx, particles[i].vy); auto newVel = transform * Vec2{ particles[i].vx, particles[i].vy };
vel = m2d_multiply_v2d(transform, vel); particles[i].vx = newVel.X;
particles[i].vx = vel.x; particles[i].vy = newVel.Y;
particles[i].vy = vel.y;
if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP) if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP)
{ {
if (patchPipeR) Element_PIPE_transformPatchOffsets(particles[i], pipeOffsetMap);
{
void Element_PIPE_patchR(Particle &part);
Element_PIPE_patchR(particles[i]);
}
if (patchPipeH)
{
void Element_PIPE_patchH(Particle &part);
Element_PIPE_patchH(particles[i]);
}
if (patchPipeV)
{
void Element_PIPE_patchV(Particle &part);
Element_PIPE_patchV(particles[i]);
}
} }
} }
// translate walls and other grid items when the stamp is shifted more than 4 pixels in any direction // Translate blocky stuff.
int translateX = 0, translateY = 0; PlaneAdapter<std::vector<unsigned char>> newBlockMap(newBlockS, 0);
if (translateReal.x > 0 && ((int)translated.x%CELL == 3 PlaneAdapter<std::vector<float>> newFanVelX(newBlockS, 0.0f);
|| (translated.x < 0 && (int)translated.x%CELL == 0))) PlaneAdapter<std::vector<float>> newFanVelY(newBlockS, 0.0f);
translateX = CELL; PlaneAdapter<std::vector<float>> newPressure(newBlockS, 0.0f);
else if (translateReal.x < 0 && ((int)translated.x%CELL == -3 PlaneAdapter<std::vector<float>> newVelocityX(newBlockS, 0.0f);
|| (translated.x > 0 && (int)translated.x%CELL == 0))) PlaneAdapter<std::vector<float>> newVelocityY(newBlockS, 0.0f);
translateX = -CELL; PlaneAdapter<std::vector<float>> newAmbientHeat(newBlockS, 0.0f);
if (translateReal.y > 0 && ((int)translated.y%CELL == 3 PlaneAdapter<std::vector<unsigned char>> newBlockAir(newBlockS, 0);
|| (translated.y < 0 && (int)translated.y%CELL == 0))) PlaneAdapter<std::vector<unsigned char>> newBlockAirh(newBlockS, 0);
translateY = CELL;
else if (translateReal.y < 0 && ((int)translated.y%CELL == -3
|| (translated.y > 0 && (int)translated.y%CELL == 0)))
translateY = -CELL;
for (auto bpos : blockSize.OriginRect()) for (auto bpos : blockSize.OriginRect())
{ {
auto pos = v2d_new(bpos.X*CELL+CELL*0.4f+translateX, bpos.Y*CELL+CELL*0.4f+translateY); auto newBpos = transform * bpos + btranslate;
pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); if (!newBlockS.OriginRect().Contains(newBpos))
auto newBpos = Vec2{ int(pos.x/CELL), int(pos.y/CELL) }; {
if (!newBlockSize.OriginRect().Contains(newBpos))
continue; continue;
}
if (blockMap[bpos]) if (blockMap[bpos])
{ {
blockMapNew[newBpos] = blockMap[bpos]; newBlockMap[newBpos] = blockMap[bpos];
if (blockMap[bpos]==WL_FAN) if (blockMap[bpos] == WL_FAN)
{ {
auto vel = v2d_new(fanVelX[bpos], fanVelY[bpos]); auto newVel = transform * Vec2{ fanVelX[bpos], fanVelY[bpos] };
vel = m2d_multiply_v2d(transform, vel); newFanVelX[newBpos] = newVel.X;
fanVelXNew[newBpos] = vel.x; newFanVelY[newBpos] = newVel.Y;
fanVelYNew[newBpos] = vel.y;
} }
} }
pressureNew[newBpos] = pressure[bpos]; newPressure[newBpos] = pressure[bpos];
velocityXNew[newBpos] = velocityX[bpos]; newVelocityX[newBpos] = velocityX[bpos];
velocityYNew[newBpos] = velocityY[bpos]; newVelocityY[newBpos] = velocityY[bpos];
ambientHeatNew[newBpos] = ambientHeat[bpos]; newAmbientHeat[newBpos] = ambientHeat[bpos];
blockAirNew[newBpos] = blockAir[bpos]; newBlockAir[newBpos] = blockAir[bpos];
blockAirhNew[newBpos] = blockAirh[bpos]; newBlockAirh[newBpos] = blockAirh[bpos];
} }
translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal); blockMap = std::move(newBlockMap);
fanVelX = std::move(newFanVelX);
fanVelY = std::move(newFanVelY);
pressure = std::move(newPressure);
velocityX = std::move(newVelocityX);
velocityY = std::move(newVelocityY);
ambientHeat = std::move(newAmbientHeat);
blockAir = std::move(newBlockAir);
blockAirh = std::move(newBlockAirh);
blockSize = newBlockSize; blockSize = newBlockS;
blockMap = blockMapNew;
fanVelX = fanVelXNew;
fanVelY = fanVelYNew;
pressure = pressureNew;
velocityX = velocityXNew;
velocityY = velocityYNew;
ambientHeat = ambientHeatNew;
blockAir = blockAirNew;
blockAirh = blockAirhNew;
} }
static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen) static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen)

View File

@ -57,7 +57,6 @@ public:
class GameSave class GameSave
{ {
// number of pixels translated. When translating CELL pixels, shift all CELL grids // number of pixels translated. When translating CELL pixels, shift all CELL grids
vector2d translated = { 0, 0 };
void readOPS(const std::vector<char> &data); void readOPS(const std::vector<char> &data);
void readPSv(const std::vector<char> &data); void readPSv(const std::vector<char> &data);
std::pair<bool, std::vector<char>> serialiseOPS() const; std::pair<bool, std::vector<char>> serialiseOPS() const;
@ -120,9 +119,7 @@ public:
void setSize(Vec2<int> newBlockSize); void setSize(Vec2<int> newBlockSize);
// return value is [ fakeFromNewerVersion, gameData ] // return value is [ fakeFromNewerVersion, gameData ]
std::pair<bool, std::vector<char>> Serialise() const; std::pair<bool, std::vector<char>> Serialise() const;
vector2d Translate(vector2d translate); void Transform(Mat2<int> transform, Vec2<int> nudge);
void Transform(matrix2d transform, vector2d translate);
void Transform(matrix2d transform, vector2d translate, vector2d translateReal, Vec2<int> newPartSize);
void Expand(const std::vector<char> &data); void Expand(const std::vector<char> &data);

View File

@ -156,6 +156,15 @@ struct Mat2
return Vec2<decltype(std::declval<T>() * std::declval<S>())>(A * vec.X + B * vec.Y, C * vec.X + D * vec.Y); return Vec2<decltype(std::declval<T>() * std::declval<S>())>(A * vec.X + B * vec.Y, C * vec.X + D * vec.Y);
} }
template<typename S>
constexpr Mat2<decltype(std::declval<T>() * std::declval<S>())> operator*(Mat2<S> mat) const
{
return Mat2<decltype(std::declval<T>() * std::declval<S>())>(
A * mat.A + B * mat.C, A * mat.B + B * mat.D,
C * mat.A + D * mat.C, C * mat.B + D * mat.D
);
}
static Mat2<T> const Identity, MirrorX, MirrorY, CCW; static Mat2<T> const Identity, MirrorX, MirrorY, CCW;
}; };

View File

@ -238,7 +238,7 @@ std::pair<int, sign::Type> GameController::GetSignSplit(int signID)
void GameController::PlaceSave(ui::Point position) void GameController::PlaceSave(ui::Point position)
{ {
auto *placeSave = gameModel->GetPlaceSave(); auto *placeSave = gameModel->GetTransformedPlaceSave();
if (placeSave) if (placeSave)
{ {
HistorySnapshot(); HistorySnapshot();
@ -248,6 +248,7 @@ void GameController::PlaceSave(ui::Point position)
Client::Ref().MergeStampAuthorInfo(placeSave->authors); Client::Ref().MergeStampAuthorInfo(placeSave->authors);
} }
} }
gameModel->SetPlaceSave(nullptr);
} }
void GameController::Install() void GameController::Install()
@ -420,23 +421,9 @@ void GameController::LoadStamp(std::unique_ptr<GameSave> stamp)
gameModel->SetPlaceSave(std::move(stamp)); gameModel->SetPlaceSave(std::move(stamp));
} }
void GameController::TranslateSave(ui::Point point) void GameController::TransformPlaceSave(Mat2<int> transform, Vec2<int> nudge)
{ {
vector2d translate = v2d_new(float(point.X), float(point.Y)); gameModel->TransformPlaceSave(transform, nudge);
auto save = gameModel->TakePlaceSave();
vector2d translated = save->Translate(translate);
ui::Point currentPlaceSaveOffset = gameView->GetPlaceSaveOffset();
// resets placeSaveOffset to 0, which is why we back it up first
gameModel->SetPlaceSave(std::move(save));
gameView->SetPlaceSaveOffset(ui::Point(int(translated.x), int(translated.y)) + currentPlaceSaveOffset);
}
void GameController::TransformSave(matrix2d transform)
{
vector2d translate = v2d_zero;
auto save = gameModel->TakePlaceSave();
save->Transform(transform, translate);
gameModel->SetPlaceSave(std::move(save));
} }
void GameController::ToolClick(int toolSelection, ui::Point point) void GameController::ToolClick(int toolSelection, ui::Point point)

View File

@ -152,8 +152,7 @@ public:
void ShowConsole(); void ShowConsole();
void HideConsole(); void HideConsole();
void FrameStep(); void FrameStep();
void TranslateSave(ui::Point point); void TransformPlaceSave(Mat2<int> transform, Vec2<int> nudge);
void TransformSave(matrix2d transform);
bool MouseInZoom(ui::Point position); bool MouseInZoom(ui::Point position);
ui::Point PointTranslate(ui::Point point); ui::Point PointTranslate(ui::Point point);
ui::Point PointTranslateNoClamp(ui::Point point); ui::Point PointTranslateNoClamp(ui::Point point);

View File

@ -1336,10 +1336,21 @@ void GameModel::ClearSimulation()
void GameModel::SetPlaceSave(std::unique_ptr<GameSave> save) void GameModel::SetPlaceSave(std::unique_ptr<GameSave> save)
{ {
transformedPlaceSave.reset();
placeSave = std::move(save); placeSave = std::move(save);
notifyPlaceSaveChanged(); notifyPlaceSaveChanged();
} }
void GameModel::TransformPlaceSave(Mat2<int> transform, Vec2<int> nudge)
{
if (placeSave)
{
transformedPlaceSave = std::make_unique<GameSave>(*placeSave);
transformedPlaceSave->Transform(transform, nudge);
}
notifyTransformedPlaceSaveChanged();
}
void GameModel::SetClipboard(std::unique_ptr<GameSave> save) void GameModel::SetClipboard(std::unique_ptr<GameSave> save)
{ {
clipboard = std::move(save); clipboard = std::move(save);
@ -1350,15 +1361,9 @@ const GameSave *GameModel::GetClipboard() const
return clipboard.get(); return clipboard.get();
} }
const GameSave *GameModel::GetPlaceSave() const const GameSave *GameModel::GetTransformedPlaceSave() const
{ {
return placeSave.get(); return transformedPlaceSave.get();
}
std::unique_ptr<GameSave> GameModel::TakePlaceSave()
{
// we don't notify listeners because we'll get a new save soon anyway
return std::move(placeSave);
} }
void GameModel::Log(String message, bool printToFile) void GameModel::Log(String message, bool printToFile)
@ -1559,6 +1564,14 @@ void GameModel::notifyPlaceSaveChanged()
} }
} }
void GameModel::notifyTransformedPlaceSaveChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
observers[i]->NotifyTransformedPlaceSaveChanged(this);
}
}
void GameModel::notifyLogChanged(String entry) void GameModel::notifyLogChanged(String entry)
{ {
for (size_t i = 0; i < observers.size(); i++) for (size_t i = 0; i < observers.size(); i++)

View File

@ -46,6 +46,7 @@ private:
//unsigned char * clipboardData; //unsigned char * clipboardData;
std::unique_ptr<GameSave> clipboard; std::unique_ptr<GameSave> clipboard;
std::unique_ptr<GameSave> placeSave; std::unique_ptr<GameSave> placeSave;
std::unique_ptr<GameSave> transformedPlaceSave;
std::deque<String> consoleLog; std::deque<String> consoleLog;
std::vector<GameView*> observers; std::vector<GameView*> observers;
std::vector<Tool*> toolList; std::vector<Tool*> toolList;
@ -104,6 +105,7 @@ private:
void notifyZoomChanged(); void notifyZoomChanged();
void notifyClipboardChanged(); void notifyClipboardChanged();
void notifyPlaceSaveChanged(); void notifyPlaceSaveChanged();
void notifyTransformedPlaceSaveChanged();
void notifyColourSelectorColourChanged(); void notifyColourSelectorColourChanged();
void notifyColourSelectorVisibilityChanged(); void notifyColourSelectorVisibilityChanged();
void notifyColourPresetsChanged(); void notifyColourPresetsChanged();
@ -227,11 +229,12 @@ public:
ui::Point GetZoomWindowPosition(); ui::Point GetZoomWindowPosition();
void SetClipboard(std::unique_ptr<GameSave> save); void SetClipboard(std::unique_ptr<GameSave> save);
void SetPlaceSave(std::unique_ptr<GameSave> save); void SetPlaceSave(std::unique_ptr<GameSave> save);
void TransformPlaceSave(Mat2<int> transform, Vec2<int> nudge);
void Log(String message, bool printToFile); void Log(String message, bool printToFile);
std::deque<String> GetLog(); std::deque<String> GetLog();
const GameSave *GetClipboard() const; const GameSave *GetClipboard() const;
const GameSave *GetPlaceSave() const; const GameSave *GetPlaceSave() const;
std::unique_ptr<GameSave> TakePlaceSave(); const GameSave *GetTransformedPlaceSave() const;
bool GetMouseClickRequired(); bool GetMouseClickRequired();
void SetMouseClickRequired(bool mouseClickRequired); void SetMouseClickRequired(bool mouseClickRequired);
bool GetIncludePressure(); bool GetIncludePressure();

View File

@ -36,6 +36,7 @@
#include "Config.h" #include "Config.h"
#include <cstring> #include <cstring>
#include <cstdlib>
#include <iostream> #include <iostream>
#include <SDL.h> #include <SDL.h>
@ -1198,7 +1199,7 @@ void GameView::OnMouseUp(int x, int y, unsigned button)
{ {
if (placeSaveThumb && y <= WINDOWH-BARSIZE) if (placeSaveThumb && y <= WINDOWH-BARSIZE)
{ {
auto thumb = selectPoint2 - (placeSaveThumb->Size() - placeSaveOffset) / 2; auto thumb = selectPoint2 + placeSaveOffset;
thumb = thumb.Clamp(RectBetween(Vec2<int>::Zero, RES - placeSaveThumb->Size())); thumb = thumb.Clamp(RectBetween(Vec2<int>::Zero, RES - placeSaveThumb->Size()));
c->PlaceSave(thumb); c->PlaceSave(thumb);
} }
@ -1334,16 +1335,16 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
switch (key) switch (key)
{ {
case SDLK_RIGHT: case SDLK_RIGHT:
c->TranslateSave(ui::Point(1, 0)); TranslateSave({ 1, 0 });
return; return;
case SDLK_LEFT: case SDLK_LEFT:
c->TranslateSave(ui::Point(-1, 0)); TranslateSave({ -1, 0 });
return; return;
case SDLK_UP: case SDLK_UP:
c->TranslateSave(ui::Point(0, -1)); TranslateSave({ 0, -1 });
return; return;
case SDLK_DOWN: case SDLK_DOWN:
c->TranslateSave(ui::Point(0, 1)); TranslateSave({ 0, 1 });
return; return;
} }
if (scan == SDL_SCANCODE_R && !repeat) if (scan == SDL_SCANCODE_R && !repeat)
@ -1351,17 +1352,17 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
if (ctrl && shift) if (ctrl && shift)
{ {
//Vertical flip //Vertical flip
c->TransformSave(m2d_new(1,0,0,-1)); TransformSave(Mat2<int>::MirrorY);
} }
else if (!ctrl && shift) else if (!ctrl && shift)
{ {
//Horizontal flip //Horizontal flip
c->TransformSave(m2d_new(-1,0,0,1)); TransformSave(Mat2<int>::MirrorX);
} }
else else
{ {
//Rotate 90deg //Rotate 90deg
c->TransformSave(m2d_new(0,1,-1,0)); TransformSave(Mat2<int>::CCW);
} }
return; return;
} }
@ -1908,16 +1909,62 @@ void GameView::NotifyLogChanged(GameModel * sender, String entry)
void GameView::NotifyPlaceSaveChanged(GameModel * sender) void GameView::NotifyPlaceSaveChanged(GameModel * sender)
{ {
placeSaveOffset = ui::Point(0, 0); placeSaveTransform = Mat2<int>::Identity;
if(sender->GetPlaceSave()) placeSaveTranslate = Vec2<int>::Zero;
ApplyTransformPlaceSave();
}
void GameView::TranslateSave(Vec2<int> addToTranslate)
{
placeSaveTranslate += addToTranslate;
ApplyTransformPlaceSave();
}
void GameView::TransformSave(Mat2<int> mulToTransform)
{
placeSaveTranslate = Vec2<int>::Zero; // reset offset
placeSaveTransform = mulToTransform * placeSaveTransform;
ApplyTransformPlaceSave();
}
template<class Signed>
static std::pair<Signed, Signed> floorDiv(Signed a, Signed b)
{
auto quo = a / b;
auto rem = a % b;
if (a < Signed(0) && rem)
{ {
placeSaveThumb = SaveRenderer::Ref().Render(sender->GetPlaceSave(), true, true, sender->GetRenderer()); quo -= Signed(1);
rem += b;
}
return { quo, rem };
}
void GameView::ApplyTransformPlaceSave()
{
auto remX = floorDiv(placeSaveTranslate.X, CELL).second;
auto remY = floorDiv(placeSaveTranslate.Y, CELL).second;
c->TransformPlaceSave(placeSaveTransform, { remX, remY });
}
void GameView::NotifyTransformedPlaceSaveChanged(GameModel *sender)
{
if (sender->GetTransformedPlaceSave())
{
placeSaveThumb = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer());
auto [ quoX, remX ] = floorDiv(placeSaveTranslate.X, CELL);
auto [ quoY, remY ] = floorDiv(placeSaveTranslate.Y, CELL);
placeSaveOffset = Vec2{ quoX, quoY } * CELL;
auto usefulSize = placeSaveThumb->Size();
if (remX) usefulSize.X -= CELL;
if (remY) usefulSize.Y -= CELL;
placeSaveOffset -= usefulSize / 2;
selectMode = PlaceSave; selectMode = PlaceSave;
selectPoint2 = mousePosition; selectPoint2 = mousePosition;
} }
else else
{ {
placeSaveThumb = nullptr; placeSaveThumb.reset();
selectMode = SelectNone; selectMode = SelectNone;
} }
} }
@ -2127,7 +2174,7 @@ void GameView::OnDraw()
{ {
if(placeSaveThumb && selectPoint2.X!=-1) if(placeSaveThumb && selectPoint2.X!=-1)
{ {
auto thumb = selectPoint2 - (placeSaveThumb->Size() - placeSaveOffset) / 2 + Vec2(1, 1) * CELL / 2; auto thumb = selectPoint2 + placeSaveOffset + Vec2(1, 1) * CELL / 2;
thumb = c->NormaliseBlockCoord(thumb).Clamp(RectBetween(Vec2<int>::Zero, RES - placeSaveThumb->Size())); thumb = c->NormaliseBlockCoord(thumb).Clamp(RectBetween(Vec2<int>::Zero, RES - placeSaveThumb->Size()));
auto rect = RectSized(thumb, placeSaveThumb->Size()); auto rect = RectSized(thumb, placeSaveThumb->Size());
ren->BlendImage(placeSaveThumb->Data(), 0x80, rect); ren->BlendImage(placeSaveThumb->Data(), 0x80, rect);

View File

@ -119,6 +119,11 @@ private:
std::unique_ptr<VideoBuffer> placeSaveThumb; std::unique_ptr<VideoBuffer> placeSaveThumb;
ui::Point placeSaveOffset; ui::Point placeSaveOffset;
Mat2<int> placeSaveTransform = Mat2<int>::Identity;
Vec2<int> placeSaveTranslate = Vec2<int>::Zero;
void TranslateSave(Vec2<int> addToTranslate);
void TransformSave(Mat2<int> mulToTransform);
void ApplyTransformPlaceSave();
SimulationSample sample; SimulationSample sample;
@ -185,6 +190,7 @@ public:
void NotifyColourPresetsChanged(GameModel * sender); void NotifyColourPresetsChanged(GameModel * sender);
void NotifyColourActivePresetChanged(GameModel * sender); void NotifyColourActivePresetChanged(GameModel * sender);
void NotifyPlaceSaveChanged(GameModel * sender); void NotifyPlaceSaveChanged(GameModel * sender);
void NotifyTransformedPlaceSaveChanged(GameModel *sender);
void NotifyNotificationsChanged(GameModel * sender); void NotifyNotificationsChanged(GameModel * sender);
void NotifyLogChanged(GameModel * sender, String entry); void NotifyLogChanged(GameModel * sender, String entry);
void NotifyToolTipChanged(GameModel * sender); void NotifyToolTipChanged(GameModel * sender);

View File

@ -1,5 +1,7 @@
#include "simulation/ElementCommon.h" #include "simulation/ElementCommon.h"
extern const std::array<Vec2<int>, 8> Element_PIPE_offsets;
void Element_PIPE_transformPatchOffsets(Particle &part, const std::array<int, 8> &offsetMap);
int Element_PIPE_update(UPDATE_FUNC_ARGS); int Element_PIPE_update(UPDATE_FUNC_ARGS);
int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS); int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS);
void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR); void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR);
@ -78,40 +80,21 @@ constexpr int PPIP_TMPFLAG_TRIGGER_OFF = 0x08000000;
constexpr int PPIP_TMPFLAG_TRIGGER_ON = 0x10000000; constexpr int PPIP_TMPFLAG_TRIGGER_ON = 0x10000000;
constexpr int PPIP_TMPFLAG_TRIGGERS = 0x1C000000; constexpr int PPIP_TMPFLAG_TRIGGERS = 0x1C000000;
signed char pos_1_rx[] = { -1,-1,-1, 0, 0, 1, 1, 1 }; const std::array<Vec2<int>, 8> Element_PIPE_offsets = {{
signed char pos_1_ry[] = { -1, 0, 1,-1, 1,-1, 0, 1 }; { -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, -1 },
{ 0, 1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 },
}};
static void transformPatch(Particle &part, const int (&patch)[8]) void Element_PIPE_transformPatchOffsets(Particle &part, const std::array<int, 8> &offsetMap)
{ {
if (part.tmp & 0x00000200) part.tmp = (part.tmp & 0xFFFFE3FF) | (patch[(part.tmp & 0x00001C00) >> 10] << 10); if (part.tmp & 0x00000200) part.tmp = (part.tmp & 0xFFFFE3FF) | (offsetMap[(part.tmp & 0x00001C00) >> 10] << 10);
if (part.tmp & 0x00002000) part.tmp = (part.tmp & 0xFFFE3FFF) | (patch[(part.tmp & 0x0001C000) >> 14] << 14); if (part.tmp & 0x00002000) part.tmp = (part.tmp & 0xFFFE3FFF) | (offsetMap[(part.tmp & 0x0001C000) >> 14] << 14);
}
void Element_PIPE_patchR(Particle &part)
{
// 035 -> 210
// 1 6 -> 4 3
// 247 -> 765
const int patchR[] = { 2, 4, 7, 1, 6, 0, 3, 5 };
transformPatch(part, patchR);
}
void Element_PIPE_patchH(Particle &part)
{
// 035 -> 530
// 1 6 -> 6 1
// 247 -> 742
const int patchH[] = { 5, 6, 7, 3, 4, 0, 1, 2 };
transformPatch(part, patchH);
}
void Element_PIPE_patchV(Particle &part)
{
// 035 -> 247
// 1 6 -> 1 6
// 247 -> 035
const int patchV[] = { 2, 1, 0, 4, 3, 7, 6, 5 };
transformPatch(part, patchV);
} }
static unsigned int prevColor(unsigned int flags) static unsigned int prevColor(unsigned int flags)
@ -260,8 +243,8 @@ int Element_PIPE_update(UPDATE_FUNC_ARGS)
rndstore = sim->rng.gen(); rndstore = sim->rng.gen();
rnd = rndstore&7; rnd = rndstore&7;
//rndstore = rndstore>>3; //rndstore = rndstore>>3;
rx = pos_1_rx[rnd]; rx = Element_PIPE_offsets[rnd].X;
ry = pos_1_ry[rnd]; ry = Element_PIPE_offsets[rnd].Y;
if (BOUNDS_CHECK) if (BOUNDS_CHECK)
{ {
r = pmap[y+ry][x+rx]; r = pmap[y+ry][x+rx];
@ -501,8 +484,8 @@ static void pushParticle(Simulation * sim, int i, int count, int original)
{ {
rnd = rndstore&7; rnd = rndstore&7;
rndstore = rndstore>>3; rndstore = rndstore>>3;
rx = pos_1_rx[rnd]; rx = Element_PIPE_offsets[rnd].X;
ry = pos_1_ry[rnd]; ry = Element_PIPE_offsets[rnd].Y;
if (BOUNDS_CHECK) if (BOUNDS_CHECK)
{ {
r = sim->pmap[y+ry][x+rx]; r = sim->pmap[y+ry][x+rx];
@ -537,7 +520,7 @@ static void pushParticle(Simulation * sim, int i, int count, int original)
else //predefined 1 pixel thick pipe movement else //predefined 1 pixel thick pipe movement
{ {
int coords = 7 - ((sim->parts[i].tmp>>10)&7); int coords = 7 - ((sim->parts[i].tmp>>10)&7);
r = sim->pmap[y+ pos_1_ry[coords]][x+ pos_1_rx[coords]]; r = sim->pmap[y+ Element_PIPE_offsets[coords].Y][x+ Element_PIPE_offsets[coords].X];
if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype)) if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype))
{ {
transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)), false); transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)), false);
@ -563,8 +546,8 @@ static void pushParticle(Simulation * sim, int i, int count, int original)
} }
else if (!r) //Move particles out of pipe automatically, much faster at ends else if (!r) //Move particles out of pipe automatically, much faster at ends
{ {
rx = pos_1_rx[coords]; rx = Element_PIPE_offsets[coords].X;
ry = pos_1_ry[coords]; ry = Element_PIPE_offsets[coords].Y;
np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].ctype)); np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].ctype));
if (np!=-1) if (np!=-1)
{ {