404 lines
16 KiB
C
404 lines
16 KiB
C
/*
|
|
* mtsedit/hist.c
|
|
*
|
|
* Copyright (C) 2019 bzt (bztsrc@gitlab)
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use, copy,
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
* of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* @brief History (undo / redo) functions
|
|
*
|
|
*/
|
|
|
|
#include "main.h"
|
|
|
|
#define HISTORYMAX 256
|
|
|
|
int histtop = 0, histmax = 0, histcapacity = 0;
|
|
histlist_t history[HISTORYMAX];
|
|
histlist_t histcurr;
|
|
|
|
/**
|
|
* Prepare for a history transaction
|
|
*/
|
|
void hist_prepare(int type, int volume)
|
|
{
|
|
int i, x, y, z;
|
|
|
|
if(histcurr.type != HIST_EMPTY) hist_commit();
|
|
if(type > HIST_NODE)
|
|
mts_getbounds(0, NULL, NULL);
|
|
switch(type) {
|
|
case HIST_NODE:
|
|
case HIST_ROTCW:
|
|
case HIST_ROTCCW:
|
|
case HIST_FLIP:
|
|
volume = 0; break;
|
|
case HIST_BRUSH:
|
|
if(volume < 1) volume = (may - miy + 1) * (maz - miz + 1) * (max - mix + 1);
|
|
histcurr.data.multiple.entry = (hist_t*)malloc(volume * sizeof(hist_t));
|
|
if(!histcurr.data.multiple.entry) error(lang[ERR_MEM]);
|
|
break;
|
|
case HIST_ADDY:
|
|
case HIST_DELY:
|
|
histcurr.y = volume;
|
|
histcurr.x = mix;
|
|
histcurr.z = miz;
|
|
histcurr.data.bulk.pitch = max - mix + 1;
|
|
histcurr.data.bulk.numentry = volume = (maz - miz + 1) * (max - mix + 1);
|
|
histcurr.data.bulk.entry = (node_t*)malloc(volume * sizeof(hist_t));
|
|
if(!histcurr.data.bulk.entry) error(lang[ERR_MEM]);
|
|
if(type == HIST_DELY) {
|
|
for(i = 0, z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++, i++)
|
|
memcpy(&histcurr.data.bulk.entry[i], &nodes[currlayer][z][x], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_ADDZ:
|
|
case HIST_DELZ:
|
|
histcurr.z = volume;
|
|
histcurr.x = mix;
|
|
histcurr.y = miy;
|
|
histcurr.data.bulk.pitch = max - mix + 1;
|
|
histcurr.data.bulk.numentry = volume = (may - miy + 1) * (max - mix + 1);
|
|
histcurr.data.bulk.entry = (node_t*)malloc(volume * sizeof(hist_t));
|
|
if(!histcurr.data.bulk.entry) error(lang[ERR_MEM]);
|
|
if(type == HIST_DELZ) {
|
|
for(i = 0, y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++, i++)
|
|
memcpy(&histcurr.data.bulk.entry[i], &nodes[y][cz][x], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_ADDX:
|
|
case HIST_DELX:
|
|
histcurr.x = volume;
|
|
histcurr.y = miy;
|
|
histcurr.z = miz;
|
|
histcurr.data.bulk.pitch = maz - miz + 1;
|
|
histcurr.data.bulk.numentry = volume = (may - miy + 1) * (maz - miz + 1);
|
|
histcurr.data.bulk.entry = (node_t*)malloc(volume * sizeof(hist_t));
|
|
if(!histcurr.data.bulk.entry) error(lang[ERR_MEM]);
|
|
if(type == HIST_DELX) {
|
|
for(i = 0, y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++, i++)
|
|
memcpy(&histcurr.data.bulk.entry[i], &nodes[y][z][cx], sizeof(node_t));
|
|
}
|
|
break;
|
|
}
|
|
histcurr.type = type;
|
|
histcapacity = volume;
|
|
}
|
|
|
|
/**
|
|
* Commit a history transaction
|
|
*/
|
|
void hist_commit()
|
|
{
|
|
int i, j, x, y, z, op0, op2, np0, np2;
|
|
|
|
if(histcurr.type > HIST_BRUSH && !histcurr.data.bulk.numentry) {
|
|
if(histcurr.data.bulk.entry) free(histcurr.data.bulk.entry);
|
|
memset(&histcurr, 0, sizeof(histlist_t));
|
|
histcapacity = 0;
|
|
return;
|
|
}
|
|
if(histcurr.type == HIST_BRUSH) {
|
|
if(!histcurr.data.multiple.numentry) {
|
|
if(histcurr.data.multiple.entry) free(histcurr.data.multiple.entry);
|
|
memset(&histcurr, 0, sizeof(histlist_t));
|
|
histcapacity = 0;
|
|
return;
|
|
}
|
|
/* if there's only one node in the list, convert it to single node to save memory */
|
|
if(histcurr.data.multiple.numentry == 1) {
|
|
histcurr.x = histcurr.data.multiple.entry[0].x;
|
|
histcurr.y = histcurr.data.multiple.entry[0].y;
|
|
histcurr.z = histcurr.data.multiple.entry[0].z;
|
|
op0 = histcurr.data.multiple.entry[0].oldparam0;
|
|
op2 = histcurr.data.multiple.entry[0].oldparam2;
|
|
np0 = histcurr.data.multiple.entry[0].newparam0;
|
|
np2 = histcurr.data.multiple.entry[0].newparam2;
|
|
free(histcurr.data.multiple.entry);
|
|
histcurr.data.node.oldparam0 = op0;
|
|
histcurr.data.node.oldparam2 = op2;
|
|
histcurr.data.node.newparam0 = np0;
|
|
histcurr.data.node.newparam2 = np2;
|
|
} else
|
|
if(histcurr.data.multiple.numentry != histcapacity) {
|
|
histcurr.data.multiple.entry = (hist_t*)realloc(histcurr.data.multiple.entry,
|
|
histcurr.data.multiple.numentry * sizeof(hist_t));
|
|
if(!histcurr.data.multiple.entry) error(lang[ERR_MEM]);
|
|
}
|
|
}
|
|
if(histcurr.type == HIST_EMPTY || (histcurr.type == HIST_NODE &&
|
|
histcurr.data.node.oldparam0 == histcurr.data.node.newparam0 &&
|
|
histcurr.data.node.oldparam2 == histcurr.data.node.newparam2)) {
|
|
memset(&histcurr, 0, sizeof(histlist_t));
|
|
return;
|
|
}
|
|
if(histcurr.type == HIST_NODE && histcurr.type == HIST_NODE && histcurr.x == history[histtop].x &&
|
|
histcurr.y == history[histtop].y && histcurr.z == history[histtop].z &&
|
|
histcurr.data.node.oldparam0 == history[histtop].data.node.oldparam0 &&
|
|
histcurr.data.node.newparam0 == history[histtop].data.node.oldparam0 &&
|
|
histcurr.data.node.newparam0 == history[histtop].data.node.newparam0) {
|
|
history[histtop].data.node.newparam2 = histcurr.data.node.newparam2;
|
|
memset(&histcurr, 0, sizeof(histlist_t));
|
|
return;
|
|
}
|
|
if(histcurr.type == HIST_ADDY) {
|
|
for(i = j = 0, z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++, i++)
|
|
if(nodes[currlayer][z][x].param0) {
|
|
memcpy(&histcurr.data.bulk.entry[i], &nodes[currlayer][z][x], sizeof(node_t));
|
|
j++;
|
|
}
|
|
if(!j) {
|
|
free(histcurr.data.bulk.entry);
|
|
histcurr.data.bulk.entry = NULL;
|
|
histcurr.data.bulk.numentry = 0;
|
|
}
|
|
}
|
|
if(histcurr.type == HIST_ADDZ) {
|
|
for(i = j = 0, y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++, i++)
|
|
if(nodes[y][cz][x].param0) {
|
|
memcpy(&histcurr.data.bulk.entry[i], &nodes[y][cz][x], sizeof(node_t));
|
|
j++;
|
|
}
|
|
if(!j) {
|
|
free(histcurr.data.bulk.entry);
|
|
histcurr.data.bulk.entry = NULL;
|
|
histcurr.data.bulk.numentry = 0;
|
|
}
|
|
}
|
|
if(histcurr.type == HIST_ADDX) {
|
|
for(i = j = 0, y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++, i++)
|
|
if(nodes[y][z][cx].param0) {
|
|
memcpy(&histcurr.data.bulk.entry[i], &nodes[y][z][cx], sizeof(node_t));
|
|
j++;
|
|
}
|
|
if(!j) {
|
|
free(histcurr.data.bulk.entry);
|
|
histcurr.data.bulk.entry = NULL;
|
|
histcurr.data.bulk.numentry = 0;
|
|
}
|
|
}
|
|
if(histtop != 0) {
|
|
hist_free(0, histtop);
|
|
memcpy(&history[0], &history[histtop], (histmax - histtop) * sizeof(histlist_t));
|
|
histmax -= histtop;
|
|
if(histmax < 0) histmax = 0;
|
|
}
|
|
hist_free(HISTORYMAX-1, HISTORYMAX);
|
|
if(histmax)
|
|
memmove(&history[1], &history[0], histmax * sizeof(histlist_t));
|
|
if(histmax + 1 < HISTORYMAX) histmax++;
|
|
memcpy(&history[0], &histcurr, sizeof(histlist_t));
|
|
memset(&histcurr, 0, sizeof(histlist_t));
|
|
histcapacity = 0;
|
|
histtop = 0;
|
|
}
|
|
|
|
/**
|
|
* Add to history
|
|
*/
|
|
void hist_add(int y, int z, int x, unsigned short op0, unsigned char op2, unsigned short np0, unsigned char np2)
|
|
{
|
|
int i;
|
|
|
|
if(histcurr.type == HIST_NODE) {
|
|
if(op0 == np0 && op2 == np2) histcurr.type = HIST_EMPTY;
|
|
else {
|
|
histcurr.x = x;
|
|
histcurr.y = y;
|
|
histcurr.z = z;
|
|
histcurr.data.node.oldparam0 = op0;
|
|
histcurr.data.node.oldparam2 = op2;
|
|
histcurr.data.node.newparam0 = np0;
|
|
histcurr.data.node.newparam2 = np2;
|
|
}
|
|
} else if(histcurr.type != HIST_EMPTY && (op0 != np0 || op2 != np2)) {
|
|
i = histcurr.data.multiple.numentry++;
|
|
if(histcurr.data.multiple.numentry >= histcapacity) {
|
|
histcapacity += 1024;
|
|
histcurr.data.multiple.entry = (hist_t*)realloc(histcurr.data.multiple.entry, histcapacity * sizeof(hist_t));
|
|
if(!histcurr.data.multiple.entry) error(lang[ERR_MEM]);
|
|
}
|
|
histcurr.data.multiple.entry[i].x = x;
|
|
histcurr.data.multiple.entry[i].y = y;
|
|
histcurr.data.multiple.entry[i].z = z;
|
|
histcurr.data.multiple.entry[i].oldparam0 = op0;
|
|
histcurr.data.multiple.entry[i].oldparam2 = op2;
|
|
histcurr.data.multiple.entry[i].newparam0 = np0;
|
|
histcurr.data.multiple.entry[i].newparam2 = np2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Undo from history
|
|
*/
|
|
int hist_undo()
|
|
{
|
|
int i, j, x, y, z;
|
|
|
|
if(histtop < histmax) {
|
|
switch(history[histtop].type) {
|
|
case HIST_NODE:
|
|
nodes[history[histtop].y][history[histtop].z][history[histtop].x].param0 = history[histtop].data.node.oldparam0;
|
|
nodes[history[histtop].y][history[histtop].z][history[histtop].x].param2 = history[histtop].data.node.oldparam2;
|
|
break;
|
|
case HIST_BRUSH:
|
|
for(i = 0; i < history[histtop].data.multiple.numentry; i++) {
|
|
nodes[history[histtop].data.multiple.entry[i].y]
|
|
[history[histtop].data.multiple.entry[i].z]
|
|
[history[histtop].data.multiple.entry[i].x].param0 = history[histtop].data.multiple.entry[i].oldparam0;
|
|
nodes[history[histtop].data.multiple.entry[i].y]
|
|
[history[histtop].data.multiple.entry[i].z]
|
|
[history[histtop].data.multiple.entry[i].x].param2 = history[histtop].data.multiple.entry[i].oldparam2;
|
|
}
|
|
break;
|
|
case HIST_DELY:
|
|
x = history[histtop].x;
|
|
y = history[histtop].y;
|
|
z = history[histtop].z;
|
|
mts_addy(y, 0);
|
|
for(i = j = 0; i < history[histtop].data.bulk.numentry; i++, j++, x++) {
|
|
if(j >= history[histtop].data.bulk.pitch) { j = 0; z++; x = history[histtop].x; }
|
|
memcpy(&nodes[y][z][x], &history[histtop].data.bulk.entry[i], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_ADDY: mts_dely(history[histtop].y); break;
|
|
case HIST_DELZ:
|
|
x = history[histtop].x;
|
|
y = history[histtop].y;
|
|
z = history[histtop].z;
|
|
mts_addz(z, 0);
|
|
for(i = j = 0; i < history[histtop].data.bulk.numentry; i++, j++, x++) {
|
|
if(j >= history[histtop].data.bulk.pitch) { j = 0; y++; x = history[histtop].x; }
|
|
memcpy(&nodes[y][z][x], &history[histtop].data.bulk.entry[i], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_ADDZ: mts_delz(history[histtop].z); break;
|
|
case HIST_DELX:
|
|
x = history[histtop].x;
|
|
y = history[histtop].y;
|
|
z = history[histtop].z;
|
|
mts_addx(x, 0);
|
|
for(i = j = 0; i < history[histtop].data.bulk.numentry; i++, j++, z++) {
|
|
if(j >= history[histtop].data.bulk.pitch) { j = 0; y++; z = history[histtop].z; }
|
|
memcpy(&nodes[y][z][x], &history[histtop].data.bulk.entry[i], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_ADDX: mts_delx(history[histtop].x); break;
|
|
case HIST_ROTCW: mts_rotate(1); break;
|
|
case HIST_ROTCCW: mts_rotate(0); break;
|
|
case HIST_FLIP: mts_flip(); break;
|
|
}
|
|
histtop++;
|
|
status = nodes[currlayer][cz][cx].param0 ? blocks[nodes[currlayer][cz][cx].param0].name : NULL;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Redo from history
|
|
*/
|
|
int hist_redo()
|
|
{
|
|
int i, j, x, y, z;
|
|
|
|
if(histtop) {
|
|
histtop--;
|
|
switch(history[histtop].type) {
|
|
case HIST_NODE:
|
|
nodes[history[histtop].y][history[histtop].z][history[histtop].x].param0 = history[histtop].data.node.newparam0;
|
|
nodes[history[histtop].y][history[histtop].z][history[histtop].x].param2 = history[histtop].data.node.newparam2;
|
|
break;
|
|
case HIST_BRUSH:
|
|
for(i = 0; i < history[histtop].data.multiple.numentry; i++) {
|
|
nodes[history[histtop].data.multiple.entry[i].y]
|
|
[history[histtop].data.multiple.entry[i].z]
|
|
[history[histtop].data.multiple.entry[i].x].param0 = history[histtop].data.multiple.entry[i].newparam0;
|
|
nodes[history[histtop].data.multiple.entry[i].y]
|
|
[history[histtop].data.multiple.entry[i].z]
|
|
[history[histtop].data.multiple.entry[i].x].param2 = history[histtop].data.multiple.entry[i].newparam2;
|
|
}
|
|
break;
|
|
case HIST_ADDY:
|
|
x = history[histtop].x;
|
|
y = history[histtop].y;
|
|
z = history[histtop].z;
|
|
mts_addy(y, 0);
|
|
for(i = j = 0; i < history[histtop].data.bulk.numentry; i++, j++, x++) {
|
|
if(j >= history[histtop].data.bulk.pitch) { j = 0; z++; x = history[histtop].x; }
|
|
memcpy(&nodes[y][z][x], &history[histtop].data.bulk.entry[i], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_DELY: mts_dely(history[histtop].y); break;
|
|
case HIST_ADDZ:
|
|
x = history[histtop].x;
|
|
y = history[histtop].y;
|
|
z = history[histtop].z;
|
|
mts_addz(z, 0);
|
|
for(i = j = 0; i < history[histtop].data.bulk.numentry; i++, j++, x++) {
|
|
if(j >= history[histtop].data.bulk.pitch) { j = 0; y++; x = history[histtop].x; }
|
|
memcpy(&nodes[y][z][x], &history[histtop].data.bulk.entry[i], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_DELZ: mts_delz(history[histtop].z); break;
|
|
case HIST_ADDX:
|
|
x = history[histtop].x;
|
|
y = history[histtop].y;
|
|
z = history[histtop].z;
|
|
mts_addx(x, 0);
|
|
for(i = j = 0; i < history[histtop].data.bulk.numentry; i++, j++, z++) {
|
|
if(j >= history[histtop].data.bulk.pitch) { j = 0; y++; z = history[histtop].z; }
|
|
memcpy(&nodes[y][z][x], &history[histtop].data.bulk.entry[i], sizeof(node_t));
|
|
}
|
|
break;
|
|
case HIST_DELX: mts_delx(history[histtop].x); break;
|
|
case HIST_ROTCW: mts_rotate(0); break;
|
|
case HIST_ROTCCW: mts_rotate(1); break;
|
|
case HIST_FLIP: mts_flip(); break;
|
|
}
|
|
status = nodes[currlayer][cz][cx].param0 ? blocks[nodes[currlayer][cz][cx].param0].name : NULL;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Free history
|
|
*/
|
|
void hist_free(int s, int e)
|
|
{
|
|
int i;
|
|
|
|
if(s == -1) { s = 0; e = histmax; }
|
|
for(i = s; i < e; i++)
|
|
if(history[i].type == HIST_BRUSH && history[i].data.multiple.entry)
|
|
free(history[i].data.multiple.entry);
|
|
else if(history[i].type > HIST_BRUSH && history[i].data.bulk.entry)
|
|
free(history[i].data.bulk.entry);
|
|
}
|
|
|