mtsedit/src/hist.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);
}