Skip to content

Commit

Permalink
Move selection and selection mask into image
Browse files Browse the repository at this point in the history
So that they get taken into account in the undo stack.
  • Loading branch information
guillaumechereau committed Jul 6, 2024
1 parent 429030b commit ef2499e
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/filters/wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static int gui(filter_t *filter)
bool should_wrap;
layer_t *layer;

memcpy(box, goxel.selection, sizeof(box));
memcpy(box, goxel.image->selection_box, sizeof(box));

if (box_is_null(box))
memcpy(box, goxel.image->active_layer->box, sizeof(box));
Expand Down
87 changes: 51 additions & 36 deletions src/goxel.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,11 @@ int goxel_unproject(const float viewport[4],
}
if ((1 << i) == SNAP_SELECTION_IN)
r = goxel_unproject_on_box(viewport, pos,
goxel.selection, true,
goxel.image->selection_box, true,
p, n, NULL);
if ((1 << i) == SNAP_SELECTION_OUT)
r = goxel_unproject_on_box(viewport, pos,
goxel.selection, false,
goxel.image->selection_box, false,
p, n, NULL);
if ((1 << i) == SNAP_SHAPE_BOX) {
r = goxel_unproject_on_box(viewport, pos, snap_shape, false,
Expand Down Expand Up @@ -1059,10 +1059,13 @@ void goxel_render_view(const float viewport[4], bool render_mode)
render_img(rend, layer->image, layer->mat, EFFECT_NO_SHADING);
}

render_box(rend, goxel.selection, NULL, EFFECT_STRIP | EFFECT_WIREFRAME);
render_box(rend, goxel.image->selection_box, NULL,
EFFECT_STRIP | EFFECT_WIREFRAME);

if (goxel.tool->flags & TOOL_SHOW_MASK)
render_volume(rend, goxel.mask, NULL, EFFECT_GRID_ONLY);
if (goxel.tool->flags & TOOL_SHOW_MASK) {
render_volume(rend, goxel.image->selection_mask, NULL,
EFFECT_GRID_ONLY);
}

// Debug: show the current layer volume tiles.
if ((0)) {
Expand Down Expand Up @@ -1424,19 +1427,20 @@ void goxel_apply_color_filter(
int p[3];
uint8_t color[4];
painter_t painter;
image_t *img = goxel.image;

/* Compute the volume where we want to apply the filter. Mask, rect
* selection, or the whole layer. */
volume = volume_copy(layer->volume);
if (!volume_is_empty(goxel.mask)) {
volume_merge(volume, goxel.mask, MODE_INTERSECT, NULL);
} else if (!box_is_null(goxel.selection)) {
if (!volume_is_empty(img->selection_mask)) {
volume_merge(volume, img->selection_mask, MODE_INTERSECT, NULL);
} else if (!box_is_null(img->selection_box)) {
painter = (painter_t) {
.shape = &shape_cube,
.mode = MODE_INTERSECT,
.color = {255, 255, 255, 255},
};
volume_op(volume, &painter, goxel.selection);
volume_op(volume, &painter, img->selection_box);
}
iter = volume_get_iterator(volume,
VOLUME_ITER_VOXELS | VOLUME_ITER_SKIP_EMPTY);
Expand All @@ -1455,17 +1459,17 @@ static void a_cut_as_new_layer(void)
{
layer_t *new_layer;
painter_t painter;

image_t *img = goxel.image;
layer_t *layer = img->active_layer;
const float (*box)[4][4] = &goxel.selection;
const float (*box)[4][4] = &img->selection_box;

new_layer = image_duplicate_layer(img, layer);

// Use the mask in priority.
if (!volume_is_empty(goxel.mask)) {
volume_merge(new_layer->volume, goxel.mask, MODE_INTERSECT, NULL);
volume_merge(layer->volume, goxel.mask , MODE_SUB, NULL);
if (!volume_is_empty(img->selection_mask)) {
volume_merge(new_layer->volume, img->selection_mask,
MODE_INTERSECT, NULL);
volume_merge(layer->volume, img->selection_mask , MODE_SUB, NULL);
return;
}

Expand All @@ -1487,11 +1491,12 @@ ACTION_REGISTER(cut_as_new_layer,

static void a_reset_selection(void)
{
if (!volume_is_empty(goxel.mask)) {
volume_delete(goxel.mask);
goxel.mask = NULL;
image_t *img = goxel.image;
if (!volume_is_empty(img->selection_mask)) {
volume_delete(img->selection_mask);
img->selection_mask = NULL;
}
mat4_copy(mat4_zero, goxel.selection);
mat4_copy(mat4_zero, img->selection_box);
}

ACTION_REGISTER(reset_selection,
Expand All @@ -1500,9 +1505,10 @@ ACTION_REGISTER(reset_selection,

static void a_fill_selection_box(void)
{
layer_t *layer = goxel.image->active_layer;
if (box_is_null(goxel.selection)) return;
volume_op(layer->volume, &goxel.painter, goxel.selection);
image_t *img = goxel.image;
layer_t *layer = img->active_layer;
if (box_is_null(img->selection_box)) return;
volume_op(layer->volume, &goxel.painter, img->selection_box);
}

ACTION_REGISTER(fill_selection_box,
Expand All @@ -1513,9 +1519,11 @@ ACTION_REGISTER(fill_selection_box,

static void a_paint_selection(void)
{
layer_t *layer = goxel.image->active_layer;
if (volume_is_empty(goxel.mask)) return;
volume_merge(layer->volume, goxel.mask, MODE_OVER, goxel.painter.color);
image_t *img = goxel.image;
layer_t *layer = img->active_layer;
if (volume_is_empty(img->selection_mask)) return;
volume_merge(layer->volume, img->selection_mask, MODE_OVER,
goxel.painter.color);
}

ACTION_REGISTER(paint_selection,
Expand All @@ -1528,17 +1536,18 @@ static void a_add_selection(void)
{
volume_t *tmp;
painter_t painter;
image_t *img = goxel.image;

if (box_is_null(goxel.selection)) return;
if (box_is_null(img->selection_box)) return;
painter = (painter_t) {
.shape = &shape_cube,
.mode = MODE_INTERSECT_FILL,
.color = {255, 255, 255, 255},
};
tmp = volume_copy(goxel.image->active_layer->volume);
volume_op(tmp, &painter, goxel.selection);
if (goxel.mask == NULL) goxel.mask = volume_new();
volume_merge(goxel.mask, tmp, MODE_OVER, painter.color);
volume_op(tmp, &painter, img->selection_box);
if (img->selection_mask == NULL) img->selection_mask = volume_new();
volume_merge(img->selection_mask, tmp, MODE_OVER, painter.color);
volume_delete(tmp);
}

Expand All @@ -1550,13 +1559,16 @@ ACTION_REGISTER(add_selection,
static void a_sub_selection(void)
{
painter_t painter;
if (goxel.mask == NULL || box_is_null(goxel.selection)) return;
image_t *img = goxel.image;

if (img->selection_mask == NULL || box_is_null(img->selection_box))
return;
painter = (painter_t) {
.shape = &shape_cube,
.mode = MODE_SUB,
.color = {255, 255, 255, 255},
};
volume_op(goxel.mask, &painter, goxel.selection);
volume_op(img->selection_mask, &painter, img->selection_box);
}

ACTION_REGISTER(sub_selection,
Expand All @@ -1567,32 +1579,35 @@ ACTION_REGISTER(sub_selection,
static void copy_action(void)
{
painter_t painter;
image_t *img = goxel.image;

volume_delete(goxel.clipboard.volume);
mat4_copy(goxel.selection, goxel.clipboard.box);
mat4_copy(img->selection_box, goxel.clipboard.box);
goxel.clipboard.volume = volume_copy(goxel.image->active_layer->volume);
if (!box_is_null(goxel.selection)) {
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, goxel.selection);
volume_op(goxel.clipboard.volume, &painter, img->selection_box);
}
}

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

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

tmp = volume_copy(goxel.clipboard.volume);
if ( !box_is_null(goxel.selection) &&
if ( !box_is_null(img->selection_box) &&
!box_is_null(goxel.clipboard.box)) {
vec3_copy(goxel.selection[3], p1);
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]);
Expand Down
3 changes: 0 additions & 3 deletions src/goxel.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,6 @@ typedef struct goxel
float tool_radius;
bool pathtrace; // Render pathtraced mode.

float selection[4][4]; // The selection box.
volume_t *mask; // Global selection mask volume.

struct {
float rotation[4];
float pos[2];
Expand Down
2 changes: 1 addition & 1 deletion src/gui/snap_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ void gui_snap_panel(void)
snap_button(_(VOLUME), SNAP_VOLUME);
snap_button(_(PLANE), SNAP_PLANE);
gui_row_end();
if (!box_is_null(goxel.selection)) {
if (!box_is_null(goxel.image->selection_box)) {
snap_button(_(SELECTION_IN), SNAP_SELECTION_IN);
snap_button(_(SELECTION_OUT), SNAP_SELECTION_OUT);
}
Expand Down
35 changes: 26 additions & 9 deletions src/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ static void image_restore(image_t *img, const image_t *snap)
DL_DELETE(img->materials, material);
material_delete(material);
}
mat4_copy(snap->box, img->box);

// Set copy from the other.
img->layers = NULL;
Expand Down Expand Up @@ -222,6 +221,15 @@ static void image_restore(image_t *img, const image_t *snap)
layer->material = material;
}
}

// Copy other attributes.
mat4_copy(snap->box, img->box);
mat4_copy(snap->selection_box, img->selection_box);

volume_delete(img->selection_mask);
img->selection_mask = NULL;
if (snap->selection_mask)
img->selection_mask = volume_copy(snap->selection_mask);
}

/*
Expand All @@ -235,7 +243,7 @@ static image_t *image_snap(const image_t *other)
material_t *material, *other_material;

img = calloc(1, sizeof(*img));
*img = *other;
*img = *other; // XXX: better to copy manually I think!

img->layers = NULL;
img->active_layer = NULL;
Expand Down Expand Up @@ -269,6 +277,10 @@ static image_t *image_snap(const image_t *other)
}
}

img->selection_mask = NULL;
if (other->selection_mask)
img->selection_mask = volume_copy(other->selection_mask);

img->history = img->history_next = img->history_prev = NULL;
img->history_pos = NULL;
return img;
Expand Down Expand Up @@ -340,8 +352,8 @@ layer_t *image_add_shape_layer(image_t *img)
layer->shape = &shape_sphere;
vec4_copy(goxel.painter.color, layer->color);
// If the selection is on use it, otherwise center it in the image.
if (!box_is_null(goxel.selection)) {
mat4_copy(goxel.selection, layer->mat);
if (!box_is_null(img->selection_box)) {
mat4_copy(img->selection_box, layer->mat);
} else {
vec3_copy(img->box[3], layer->mat[3]);
mat4_iscale(layer->mat, 4, 4, 4);
Expand Down Expand Up @@ -683,15 +695,17 @@ void image_redo(image_t *img)
static void image_clear_layer(void)
{
painter_t painter;
layer_t *layer = goxel.image->active_layer;
if (box_is_null(goxel.selection) && volume_is_empty(goxel.mask)) {
image_t *img = goxel.image;
layer_t *layer = img->active_layer;
if ( box_is_null(img->selection_box) &&
volume_is_empty(img->selection_mask)) {
volume_clear(layer->volume);
return;
}

// Use the mask in priority if it exists.
if (!volume_is_empty(goxel.mask)) {
volume_merge(layer->volume, goxel.mask, MODE_SUB, NULL);
if (!volume_is_empty(img->selection_mask)) {
volume_merge(layer->volume, img->selection_mask, MODE_SUB, NULL);
return;
}

Expand All @@ -700,7 +714,7 @@ static void image_clear_layer(void)
.mode = MODE_SUB,
.color = {255, 255, 255, 255},
};
volume_op(layer->volume, &painter, goxel.selection);
volume_op(layer->volume, &painter, img->selection_box);
}

bool image_layer_can_edit(const image_t *img, const layer_t *layer)
Expand Down Expand Up @@ -731,6 +745,9 @@ uint32_t image_get_key(const image_t *img)
k = material_get_hash(material);
key = XXH32(&k, sizeof(k), key);
}
key = XXH32(img->selection_box, sizeof(img->selection_box), key);
k = volume_get_key(img->selection_mask);
key = XXH32(&k, sizeof(k), key);
return key;
}

Expand Down
3 changes: 3 additions & 0 deletions src/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ struct image {

float box[4][4];

float selection_box[4][4];
volume_t *selection_mask;

// For saving.
// XXX: I think those should be persistend data of export code instead.
char *path; // Path to save the gox file.
Expand Down
2 changes: 1 addition & 1 deletion src/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ static klass_t image_klass = {
{"addLayer", .fn=js_image_addLayer},
{"activeLayer", .klass=&layer_klass, MEMBER(image_t, active_layer)},
{"getLayersVolume", .fn=js_image_getLayersVolume},
{"selectionBox", .klass=&box_klass, MEMBER(image_t, selection_box)},
{}
}
};
Expand Down Expand Up @@ -625,7 +626,6 @@ static klass_t goxel_klass = {
.def.class_name = "Goxel",
.attributes = {
{"image", .klass=&image_klass, MEMBER(goxel_t, image)},
{"selection", .klass=&box_klass, MEMBER(goxel_t, selection)},
{"registerFormat", .fn=js_goxel_registerFormat},
{"registerScript", .fn=js_goxel_registerScript},
{}
Expand Down
Loading

0 comments on commit ef2499e

Please sign in to comment.