Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System clipboard copy/paste #395

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 36 additions & 26 deletions src/goxel.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include "file_format.h"
#include "script.h"
#include "shader_cache.h"
#include "system.h"
#include "utils/box.h"
#include "volume.h"
#include "xxhash.h"

#include "../ext_src/stb/stb_ds.h"
Expand Down Expand Up @@ -1669,43 +1672,50 @@ ACTION_REGISTER(ACTION_sub_selection,

static void copy_action(void)
{
painter_t painter;
image_t *img = goxel.image;
int aabb[2][3];
char *encoded_voxels;

volume_delete(goxel.clipboard.volume);
mat4_copy(img->selection_box, goxel.clipboard.box);
goxel.clipboard.volume = volume_copy(goxel.image->active_layer->volume);
if (!box_is_null(img->selection_box)) {
painter = (painter_t) {
.shape = &shape_cube,
.mode = MODE_INTERSECT,
.color = {255, 255, 255, 255},
};
volume_op(goxel.clipboard.volume, &painter, img->selection_box);
bbox_to_aabb(img->selection_box, aabb);
} else if (!box_is_null(goxel.image->active_layer->box)) {
bbox_to_aabb(goxel.image->active_layer->box, aabb);
}
else {
volume_get_bbox(goxel.image->active_layer->volume, aabb, true);
}

encoded_voxels = volume_copy_to_string(goxel.image->active_layer->volume,
aabb);
sys_callbacks.set_clipboard_text(sys_callbacks.user, encoded_voxels);
free(encoded_voxels);
}

static void paste_action(void)
{
image_t *img = goxel.image;
volume_t *volume = img->active_layer->volume;
volume_t *tmp;
float p1[3], p2[3], mat[4][4];
int aabb[2][3];
const char *clipboard_text;

mat4_set_identity(mat);
if (!goxel.clipboard.volume) return;

tmp = volume_copy(goxel.clipboard.volume);
if ( !box_is_null(img->selection_box) &&
!box_is_null(goxel.clipboard.box)) {
vec3_copy(img->selection_box[3], p1);
vec3_copy(goxel.clipboard.box[3], p2);
mat4_itranslate(mat, +p1[0], +p1[1], +p1[2]);
mat4_itranslate(mat, -p2[0], -p2[1], -p2[2]);
volume_move(tmp, mat);
}
volume_merge(volume, tmp, MODE_OVER, NULL);
volume_delete(tmp);
clipboard_text = sys_callbacks.get_clipboard_text(sys_callbacks.user);
if (clipboard_text == NULL) {
return;
}

if (!box_is_null(img->selection_box)) {
bbox_to_aabb(img->selection_box, aabb);
} else {
aabb[0][0] = 0;
aabb[0][1] = 0;
aabb[0][2] = 0;

if (volume_parse_string_header(clipboard_text, aabb[1]) == NULL) {
return;
}
}

volume_merge_from_string(volume, aabb, clipboard_text);
}

ACTION_REGISTER(ACTION_copy,
Expand Down
5 changes: 0 additions & 5 deletions src/goxel.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,11 +493,6 @@ typedef struct goxel
layer_t *render_layers;
uint32_t render_layers_hash;

struct {
volume_t *volume;
float box[4][4];
} clipboard;

int snap_mask; // Global snap mask (can edit in the GUI).
float snap_offset; // Only for brush tool, remove that?

Expand Down
19 changes: 18 additions & 1 deletion src/gui/edit_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@

#include "goxel.h"

static bool have_volume_to_paste()
{
const char *clipboard_text;
int size[3];

clipboard_text = sys_callbacks.get_clipboard_text(sys_callbacks.user);
if (clipboard_text == NULL) {
return false;
}

if (volume_parse_string_header(clipboard_text, size) == NULL) {
return false;
}

return true;
}

void gui_edit_panel(void)
{
image_t *img = goxel.image;
Expand All @@ -40,7 +57,7 @@ void gui_edit_panel(void)
gui_enabled_begin(!volume_is_empty(goxel.image->selection_mask));
gui_action_button(ACTION_copy, _("Copy"), 1.0);
gui_enabled_end();
gui_enabled_begin(!volume_is_empty(goxel.clipboard.volume));
gui_enabled_begin(have_volume_to_paste());
gui_action_button(ACTION_paste, _("Paste"), 1.0);
gui_enabled_end();
gui_group_end();
Expand Down
141 changes: 137 additions & 4 deletions src/volume.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@

#include "volume.h"
#include "uthash.h"
#include "utils/box.h"
#include <assert.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>

#define min(a, b) ({ \
__typeof__ (a) _a = (a); \
Expand Down Expand Up @@ -82,7 +84,7 @@ static volume_global_stats_t g_global_stats = {};
#define DATA_AT(d, x, y, z) (d->voxels[x + y * N + z * N * N])
#define TILE_AT(c, x, y, z) (DATA_AT(c->data, x, y, z))

static void mat4_mul_vec4(float mat[4][4], const float v[4], float out[4])
static void mat4_mul_vec4_f(float mat[4][4], const float v[4], float out[4])
{
float ret[4] = {0};
int i, j;
Expand All @@ -94,7 +96,7 @@ static void mat4_mul_vec4(float mat[4][4], const float v[4], float out[4])
memcpy(out, ret, sizeof(ret));
}

static void box_get_bbox(float box[4][4], int bbox[2][3])
static void box_get_bbox_f(float box[4][4], int bbox[2][3])
{
const float vertices[8][4] = {
{-1, -1, +1, 1},
Expand All @@ -110,7 +112,7 @@ static void box_get_bbox(float box[4][4], int bbox[2][3])
{INT_MIN, INT_MIN, INT_MIN}};
float p[4];
for (i = 0; i < 8; i++) {
mat4_mul_vec4(box, vertices[i], p);
mat4_mul_vec4_f(box, vertices[i], p);
ret[0][0] = min(ret[0][0], (int)floor(p[0]));
ret[0][1] = min(ret[0][1], (int)floor(p[1]));
ret[0][2] = min(ret[0][2], (int)floor(p[2]));
Expand Down Expand Up @@ -403,7 +405,7 @@ volume_iterator_t volume_get_box_iterator(const volume_t *volume,
.flags = VOLUME_ITER_BOX | VOLUME_ITER_VOXELS | flags,
};
memcpy(iter.box, box, sizeof(iter.box));
box_get_bbox(iter.box, iter.bbox);
box_get_bbox_f(iter.box, iter.bbox);

if (flags & VOLUME_ITER_SKIP_EMPTY) {
volume_get_bbox(volume, volume_bbox, false);
Expand Down Expand Up @@ -828,3 +830,134 @@ void volume_get_global_stats(volume_global_stats_t *stats)
{
*stats = g_global_stats;
}

char* volume_copy_to_string(const volume_t *volume, const int aabb[2][3])
{
int pos[3];
int volume_pos[3];
int size[3];
char str_header[256];
size_t total_size;
char *encoded_str;
char *writer;
int i;
uint8_t voxel[4];

size[0] = aabb[1][0] - aabb[0][0];
size[1] = aabb[1][1] - aabb[0][1];
size[2] = aabb[1][2] - aabb[0][2];

sprintf(str_header, "goxel::voxels::%dx%dx%d::", size[0], size[1], size[2]);
total_size = strlen(str_header) + size[0] * size[1] * size[2] * 8 + 1;
encoded_str = malloc(total_size);
writer = encoded_str + strlen(str_header);

strcpy(encoded_str, str_header);

for (pos[2] = 0; pos[2] < size[2]; pos[2]++) {
for (pos[1] = 0; pos[1] < size[1]; pos[1]++) {
for (pos[0] = 0; pos[0] < size[0]; pos[0]++) {
memcpy(volume_pos, pos, sizeof(pos));

for (i = 0; i < 3; i++) {
volume_pos[i] += aabb[0][i];
}

volume_get_at(volume, NULL, volume_pos, voxel);
for (i = 0; i < 4; i++) {
sprintf(writer, "%02hhx", voxel[i]);
writer += 2;
}
}
}
}

return encoded_str;
}

const char* volume_parse_string_header(const char *encoded_str, int size[3])
{
int num_parsed;
const char* vdata;

num_parsed = sscanf(encoded_str, "goxel::voxels::%dx%dx%d::",
&size[0], &size[1], &size[2]);

if (num_parsed != 3) {
return NULL;
}

vdata = strstr(encoded_str, "::");
if (!vdata) {
return NULL;
}

vdata = strstr(vdata + 2, "::");
if (!vdata) {
return NULL;
}

vdata += 2;

vdata = strstr(vdata + 2, "::");
if (!vdata) {
return NULL;
}

vdata += 2;

return vdata;
}

void volume_merge_from_string(volume_t *volume, const int aabb[2][3],
const char *encoded_str)
{
const char *reader;
int pos[3];
int volume_pos[3];
int size[3];
int i;
uint8_t voxel[4];

reader = volume_parse_string_header(encoded_str, size);
if (reader == NULL) {
return;
}

if (strlen(reader) != 8 * size[0] * size[1] * size[2]) {
return;
}

for (pos[2] = 0; pos[2] < size[2]; pos[2]++) {
for (pos[1] = 0; pos[1] < size[1]; pos[1]++) {
for (pos[0] = 0; pos[0] < size[0]; pos[0]++) {
if (sscanf(reader, "%2hhx%2hhx%2hhx%2hhx", &voxel[0], &voxel[1],
&voxel[2], &voxel[3]) != 4) {
continue;
}

reader += 8;

memcpy(volume_pos, pos, sizeof(pos));

for (i = 0; i < 3; i++) {
volume_pos[i] += aabb[0][i];

if (volume_pos[i] < aabb[0][i]) {
break;
}

if (volume_pos[i] >= aabb[1][i]) {
break;
}
}

if (i != 3) {
continue;
}

volume_set_at(volume, NULL, volume_pos, voxel);
}
}
}
}
44 changes: 44 additions & 0 deletions src/volume.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,48 @@ typedef struct {

void volume_get_global_stats(volume_global_stats_t *stats);

/*
* Function: volume_copy_to_string
*
* Returns a string a box within this volume. The returned string is on the
* heap and owned by the caller, you must call free() on it later.
*
* Inputs:
* volume - The volume.
* aabb - The bounding box within the volume to write to the string.
*
* Return:
* The heap-allocated encoded string.
*/
char* volume_copy_to_string(const volume_t *volume, const int aabb[2][3]);

/*
* Function: volume_parse_string_header
*
* Parses a voxel encoding string header, as returned by
* volume_copy_to_string().
*
* Inputs:
* encoded_str - The string with encoded voxels.
* size - If parsed successfully, the size will be written to this array.
*
* Return:
* The beginning of the hex-encoded voxels if the header is valid,
* NULL otherwise.
*/
const char* volume_parse_string_header(const char *encoded_str, int size[3]);

/*
* Function: volume_merge_from_string
*
* Merges the string-encoded voxels into this volume within the specified aabb.
*
* Inputs:
* volume - The volume.
* aabb - The bounding box within the volume to merge the string into.
* encoded_str - The string with encoded voxels, from volume_copy_to_string().
*/
void volume_merge_from_string(volume_t *volume, const int aabb[2][3],
const char *encoded_str);

#endif // VOLUME_H