#include <errno.h>
#include "game/camera.h"
-#include "system/lt.h"
#include "system/stacktrace.h"
#include "system/nth_alloc.h"
#include "system/log.h"
#include "color.h"
#include "rect_layer.h"
#include "dynarray.h"
-#include "system/line_stream.h"
-#include "color_picker.h"
#include "system/str.h"
-#include "ui/edit_field.h"
#include "undo_history.h"
#include "game/level/action.h"
-#include "action_picker.h"
#include "game.h"
+#include "math/extrema.h"
#define RECT_LAYER_SELECTION_THICCNESS 15.0f
#define RECT_LAYER_ID_LABEL_SIZE vec(3.0f, 3.0f)
#define CREATE_AREA_THRESHOLD 10.0
-#define RECT_LAYER_GRID_ROWS 3
-#define RECT_LAYER_GRID_COLUMNS 4
-static int clipboard = 0;
-static Rect clipboard_rect;
-static Color clipboard_color;
+static int rect_clipboard = 0;
+static Rect rect_clipboard_rect;
+static Color rect_clipboard_color;
static Cursor_Style resize_styles[1 << RECT_SIDE_N] = {
0, // [0]
};
typedef enum {
- RECT_LAYER_IDLE = 0,
- RECT_LAYER_CREATE,
- RECT_LAYER_RESIZE,
- RECT_LAYER_MOVE,
- RECT_LAYER_ID_RENAME,
- RECT_LAYER_RECOLOR
-} RectLayerState;
-
-struct RectLayer {
- Lt *lt;
- RectLayerState state;
- int resize_mask;
- Dynarray *ids;
- Dynarray *rects;
- Dynarray *colors;
- Dynarray *actions;
- ColorPicker color_picker;
- ActionPicker action_picker;
- Vec2f create_begin;
- Vec2f create_end;
- int selection;
- Vec2f move_anchor; // The mouse offset from the left-top
- // corner of the rect during moving it
- Edit_field *id_edit_field;
- Color inter_color;
- Rect inter_rect;
- int id_name_counter;
- const char *id_name_prefix;
- Grid *grid;
- Cursor *cursor;
-
- // this is the initial size of the selected rectangle during a resize.
- // we update this whenever the rectangle is resized, so we know the ratio
- // to fix it to.
- Vec2f initial_rectangle_size;
-};
-
-typedef enum {
- UNDO_ADD,
- UNDO_DELETE,
- UNDO_UPDATE,
- UNDO_SWAP
-} UndoType;
+ RECT_UNDO_ADD,
+ RECT_UNDO_DELETE,
+ RECT_UNDO_UPDATE,
+ RECT_UNDO_SWAP
+} RectUndoType;
// Delete, Update
typedef struct {
- UndoType type;
+ RectUndoType type;
RectLayer *layer;
size_t index;
Rect rect;
// Add
typedef struct {
- UndoType type;
+ RectUndoType type;
RectLayer *layer;
size_t index;
} UndoAddContext;
// Swap
typedef struct {
- UndoType type;
+ RectUndoType type;
RectLayer *layer;
size_t index1;
size_t index2;
} UndoSwapContext;
typedef union {
- UndoType type;
+ RectUndoType type;
UndoAddContext add;
UndoElementContext element;
UndoSwapContext swap;
-} UndoContext;
+} RectUndoContext;
static
-UndoContext create_undo_add_context(RectLayer *layer, size_t index)
+RectUndoContext create_rect_undo_add_context(RectLayer *layer, size_t index)
{
trace_assert(layer);
- trace_assert(index < dynarray_count(layer->rects));
+ trace_assert(index < layer->rects.count);
- UndoContext undo_context;
- undo_context.add.type = UNDO_ADD;
+ RectUndoContext undo_context;
+ undo_context.add.type = RECT_UNDO_ADD;
undo_context.add.layer = layer;
undo_context.add.index = index;
return undo_context;
}
static
-UndoContext create_undo_element_context(RectLayer *layer)
+RectUndoContext create_rect_undo_element_context(RectLayer *layer, size_t index)
{
trace_assert(layer);
- size_t index = (size_t) layer->selection;
- trace_assert(index < dynarray_count(layer->rects));
+ trace_assert(index < layer->rects.count);
- UndoContext undo_context;
+ RectUndoContext undo_context;
undo_context.element.layer = layer;
undo_context.element.index = index;
- dynarray_copy_to(layer->rects, &undo_context.element.rect, index);
- dynarray_copy_to(layer->colors, &undo_context.element.color, index);
- dynarray_copy_to(layer->ids, undo_context.element.id, index);
- dynarray_copy_to(layer->actions, &undo_context.element.action, index);
+ dynarray_copy_to(&layer->rects, &undo_context.element.rect, index);
+ dynarray_copy_to(&layer->colors, &undo_context.element.color, index);
+ dynarray_copy_to(&layer->ids, undo_context.element.id, index);
+ dynarray_copy_to(&layer->actions, &undo_context.element.action, index);
return undo_context;
}
static
-UndoContext create_undo_update_context(RectLayer *rect_layer)
+RectUndoContext create_rect_undo_update_context(RectLayer *rect_layer, size_t index)
{
- UndoContext undo_context = create_undo_element_context(rect_layer);
- undo_context.type = UNDO_UPDATE;
+ RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, index);
+ undo_context.type = RECT_UNDO_UPDATE;
return undo_context;
}
static
-UndoContext create_undo_delete_context(RectLayer *rect_layer)
+RectUndoContext create_rect_undo_delete_context(RectLayer *rect_layer, size_t i)
{
- UndoContext undo_context = create_undo_element_context(rect_layer);
- undo_context.type = UNDO_DELETE;
+ RectUndoContext undo_context = create_rect_undo_element_context(rect_layer, i);
+ undo_context.type = RECT_UNDO_DELETE;
return undo_context;
}
static
-UndoContext create_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
+RectUndoContext create_rect_undo_swap_context(RectLayer *rect_layer, size_t index1, size_t index2)
{
- UndoContext undo_context;
- undo_context.swap.type = UNDO_SWAP;
+ RectUndoContext undo_context;
+ undo_context.swap.type = RECT_UNDO_SWAP;
undo_context.swap.layer = rect_layer;
undo_context.swap.index1 = index1;
undo_context.swap.index2 = index2;
void rect_layer_undo(void *context, size_t context_size)
{
trace_assert(context);
- trace_assert(sizeof(UndoContext) == context_size);
+ trace_assert(sizeof(RectUndoContext) == context_size);
- UndoContext *undo_context = context;
+ RectUndoContext *undo_context = context;
switch (undo_context->type) {
- case UNDO_ADD: {
+ case RECT_UNDO_ADD: {
RectLayer *layer = undo_context->add.layer;
- dynarray_delete_at(layer->rects, undo_context->add.index);
- dynarray_delete_at(layer->colors, undo_context->add.index);
- dynarray_delete_at(layer->ids, undo_context->add.index);
- dynarray_delete_at(layer->actions, undo_context->add.index);
+ dynarray_delete_at(&layer->rects, undo_context->add.index);
+ dynarray_delete_at(&layer->colors, undo_context->add.index);
+ dynarray_delete_at(&layer->ids, undo_context->add.index);
+ dynarray_delete_at(&layer->actions, undo_context->add.index);
layer->selection = -1;
} break;
- case UNDO_DELETE: {
+ case RECT_UNDO_DELETE: {
RectLayer *layer = undo_context->element.layer;
- dynarray_insert_before(layer->rects, undo_context->element.index, &undo_context->element.rect);
- dynarray_insert_before(layer->colors, undo_context->element.index, &undo_context->element.color);
- dynarray_insert_before(layer->ids, undo_context->element.index, &undo_context->element.id);
- dynarray_insert_before(layer->actions, undo_context->element.index, &undo_context->element.action);
+ dynarray_insert_before(&layer->rects, undo_context->element.index, &undo_context->element.rect);
+ dynarray_insert_before(&layer->colors, undo_context->element.index, &undo_context->element.color);
+ dynarray_insert_before(&layer->ids, undo_context->element.index, &undo_context->element.id);
+ dynarray_insert_before(&layer->actions, undo_context->element.index, &undo_context->element.action);
layer->selection = -1;
} break;
- case UNDO_UPDATE: {
+ case RECT_UNDO_UPDATE: {
RectLayer *layer = undo_context->element.layer;
- dynarray_replace_at(layer->rects, undo_context->element.index, &undo_context->element.rect);
- dynarray_replace_at(layer->colors, undo_context->element.index, &undo_context->element.color);
- dynarray_replace_at(layer->ids, undo_context->element.index, &undo_context->element.id);
- dynarray_replace_at(layer->actions, undo_context->element.index, &undo_context->element.action);
+ dynarray_replace_at(&layer->rects, undo_context->element.index, &undo_context->element.rect);
+ dynarray_replace_at(&layer->colors, undo_context->element.index, &undo_context->element.color);
+ dynarray_replace_at(&layer->ids, undo_context->element.index, &undo_context->element.id);
+ dynarray_replace_at(&layer->actions, undo_context->element.index, &undo_context->element.action);
} break;
- case UNDO_SWAP: {
+ case RECT_UNDO_SWAP: {
RectLayer *layer = undo_context->element.layer;
- dynarray_swap(layer->rects, undo_context->swap.index1, undo_context->swap.index2);
- dynarray_swap(layer->colors, undo_context->swap.index1, undo_context->swap.index2);
- dynarray_swap(layer->ids, undo_context->swap.index1, undo_context->swap.index2);
- dynarray_swap(layer->actions, undo_context->swap.index1, undo_context->swap.index2);
+ dynarray_swap(&layer->rects, undo_context->swap.index1, undo_context->swap.index2);
+ dynarray_swap(&layer->colors, undo_context->swap.index1, undo_context->swap.index2);
+ dynarray_swap(&layer->ids, undo_context->swap.index1, undo_context->swap.index2);
+ dynarray_swap(&layer->actions, undo_context->swap.index1, undo_context->swap.index2);
} break;
}
}
-#define UNDO_PUSH(HISTORY, CONTEXT) \
+#define RECT_UNDO_PUSH(HISTORY, CONTEXT) \
do { \
- UndoContext context = (CONTEXT); \
+ RectUndoContext context = (CONTEXT); \
undo_history_push( \
HISTORY, \
rect_layer_undo, \
{
trace_assert(layer);
- if (dynarray_push(layer->rects, &rect) < 0) {
- return -1;
- }
-
- if (dynarray_push(layer->colors, &color) < 0) {
- return -1;
- }
+ dynarray_push(&layer->rects, &rect);
+ dynarray_push(&layer->colors, &color);
char id[ENTITY_MAX_ID_SIZE];
snprintf(id, ENTITY_MAX_ID_SIZE, "%s_%d",
layer->id_name_prefix,
layer->id_name_counter++);
- if (dynarray_push(layer->ids, id)) {
- return -1;
- }
+ dynarray_push(&layer->ids, id);
- dynarray_push_empty(layer->actions);
+ dynarray_push_empty(&layer->actions);
- UNDO_PUSH(
+ RECT_UNDO_PUSH(
undo_history,
- create_undo_add_context(
+ create_rect_undo_add_context(
layer,
- dynarray_count(layer->rects) - 1));
+ layer->rects.count - 1));
return 0;
}
{
trace_assert(layer);
- int n = (int) dynarray_count(layer->rects);
- Rect *rects = dynarray_data(layer->rects);
+ int n = (int) layer->rects.count;
+ Rect *rects = (Rect*)layer->rects.data;
for (int i = n - 1; i >= 0; --i) {
if (rect_contains_point(rects[i], position)) {
UndoHistory *undo_history)
{
trace_assert(layer);
- trace_assert(a < dynarray_count(layer->rects));
- trace_assert(b < dynarray_count(layer->rects));
+ trace_assert(a < layer->rects.count);
+ trace_assert(b < layer->rects.count);
- dynarray_swap(layer->rects, a, b);
- dynarray_swap(layer->colors, a, b);
- dynarray_swap(layer->ids, a, b);
- dynarray_swap(layer->actions, a, b);
+ dynarray_swap(&layer->rects, a, b);
+ dynarray_swap(&layer->colors, a, b);
+ dynarray_swap(&layer->ids, a, b);
+ dynarray_swap(&layer->actions, a, b);
- UNDO_PUSH(undo_history, create_undo_swap_context(layer, a, b));
+ RECT_UNDO_PUSH(undo_history, create_rect_undo_swap_context(layer, a, b));
}
-static int rect_layer_delete_rect_at(RectLayer *layer,
+static int rect_layer_delete_rect_at_index(RectLayer *layer,
size_t i,
UndoHistory *undo_history)
{
trace_assert(layer);
- UNDO_PUSH(undo_history, create_undo_delete_context(layer));
+ RECT_UNDO_PUSH(undo_history, create_rect_undo_delete_context(layer, i));
- dynarray_delete_at(layer->rects, i);
- dynarray_delete_at(layer->colors, i);
- dynarray_delete_at(layer->ids, i);
- dynarray_delete_at(layer->actions, i);
+ dynarray_delete_at(&layer->rects, i);
+ dynarray_delete_at(&layer->colors, i);
+ dynarray_delete_at(&layer->ids, i);
+ dynarray_delete_at(&layer->actions, i);
return 0;
}
return mask;
}
+#define TOOL_BUTTON_WIDTH 50.0f
+#define TOOL_BUTTON_HEIGHT 50.0f
+#define TOOL_BAR_PADDING 20.0f
+#define TOOL_BAR_BACKGROUND rgba(0.8f, 0.8f, 0.8f, 1.0f)
+#define TOOL_BAR_FOREGROUND rgba(0.2f, 0.2f, 0.2f, 1.0f)
+
+static
+Rect subtract_tool_button_rect(const Camera *camera)
+{
+ const Rect view_port = camera_view_port_screen(camera);
+ return rect(
+ TOOL_BAR_PADDING,
+ view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
+ TOOL_BUTTON_WIDTH,
+ TOOL_BUTTON_HEIGHT);
+}
+
+static
+Rect snapping_tool_button_rect(const Camera *camera)
+{
+ const Rect view_port = camera_view_port_screen(camera);
+ return rect(
+ TOOL_BAR_PADDING + TOOL_BUTTON_WIDTH + TOOL_BAR_PADDING,
+ view_port.h - TOOL_BUTTON_HEIGHT - TOOL_BAR_PADDING,
+ TOOL_BUTTON_WIDTH,
+ TOOL_BUTTON_HEIGHT);
+}
+
static int rect_layer_event_idle(RectLayer *layer,
const SDL_Event *event,
const Camera *camera,
if (color_changed) {
if (layer->selection >= 0) {
- dynarray_copy_to(layer->colors, &layer->inter_color, (size_t)layer->selection);
+ dynarray_copy_to(&layer->colors, &layer->inter_color, (size_t)layer->selection);
layer->state = RECT_LAYER_RECOLOR;
}
return 0;
}
- Rect *rects = dynarray_data(layer->rects);
+ Rect *rects = (Rect*)layer->rects.data;
switch (event->type) {
case SDL_MOUSEBUTTONDOWN: {
switch (event->button.button) {
case SDL_BUTTON_LEFT: {
+ Vec2f screen_position =
+ vec((float) event->button.x,
+ (float) event->button.y);
+ if (rect_contains_point(subtract_tool_button_rect(camera), screen_position)) {
+ layer->subtract_enabled = !layer->subtract_enabled;
+ return 0;
+ }
+
+ if (rect_contains_point(snapping_tool_button_rect(camera), screen_position)) {
+ layer->snapping_enabled = !layer->snapping_enabled;
+ return 0;
+ }
+
Vec2f position = camera_map_screen(
camera,
event->button.x,
rect_layer_rect_at(layer, position);
- Color *colors = dynarray_data(layer->colors);
+ Color *colors = (Color*)layer->colors.data;
- if (layer->selection >= 0 &&
- layer->selection == rect_at_position &&
- (layer->resize_mask = calc_resize_mask(
- vec((float) event->button.x, (float)event->button.y),
- camera_rect(camera, rects[layer->selection])))) {
+ if (layer->subtract_enabled) {
+ layer->state = RECT_LAYER_SUBTRACT;
+ layer->create_begin = position;
+ layer->create_end = position;
+ } else if (layer->selection >= 0 &&
+ layer->selection == rect_at_position &&
+ (layer->resize_mask = calc_resize_mask(
+ vec((float) event->button.x, (float)event->button.y),
+ camera_rect(camera, rects[layer->selection])))) {
layer->state = RECT_LAYER_RESIZE;
- dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) layer->selection);
+ dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) layer->selection);
} else if (rect_at_position >= 0) {
layer->selection = rect_at_position;
layer->state = RECT_LAYER_MOVE;
rects[layer->selection].y));
layer->color_picker =
create_color_picker_from_rgba(colors[rect_at_position]);
- dynarray_copy_to(layer->rects, &layer->inter_rect, (size_t) rect_at_position);
+ dynarray_copy_to(&layer->rects, &layer->inter_rect, (size_t) rect_at_position);
} else {
layer->selection = rect_at_position;
layer->create_end = position;
}
}
-
- if (layer->selection >= 0) {
- layer->initial_rectangle_size = vec(rects[layer->selection].w,
- rects[layer->selection].h);
- }
} break;
}
} break;
case SDLK_UP: {
if ((event->key.keysym.mod & KMOD_SHIFT)
&& (layer->selection >= 0)
- && ((size_t)(layer->selection + 1) < dynarray_count(layer->rects))) {
+ && ((size_t)(layer->selection + 1) < layer->rects.count)) {
rect_layer_swap_elements(
layer,
(size_t) layer->selection,
case SDLK_DOWN: {
if ((event->key.keysym.mod & KMOD_SHIFT)
&& (layer->selection > 0)
- && ((size_t) layer->selection < dynarray_count(layer->rects))) {
+ && ((size_t) layer->selection < layer->rects.count)) {
rect_layer_swap_elements(
layer,
(size_t) layer->selection,
case SDLK_DELETE: {
if (layer->selection >= 0) {
- rect_layer_delete_rect_at(layer, (size_t) layer->selection, undo_history);
+ rect_layer_delete_rect_at_index(
+ layer,
+ (size_t) layer->selection,
+ undo_history);
layer->selection = -1;
}
} break;
+ case SDLK_q: {
+ // TODO(#1171): there is no UI indication that we are in the snapping mode
+ layer->snapping_enabled = !layer->snapping_enabled;
+ } break;
+
case SDLK_F2: {
if (layer->selection >= 0) {
- const char *ids = dynarray_data(layer->ids);
- Color *colors = dynarray_data(layer->colors);
+ const char *ids = (char*)layer->ids.data;
+ Color *colors = (Color*)layer->colors.data;
edit_field_restyle(
- layer->id_edit_field,
+ &layer->id_edit_field,
RECT_LAYER_ID_LABEL_SIZE,
color_invert(colors[layer->selection]));
layer->state = RECT_LAYER_ID_RENAME;
edit_field_replace(
- layer->id_edit_field,
+ &layer->id_edit_field,
ids + layer->selection * ENTITY_MAX_ID_SIZE);
SDL_StartTextInput();
}
case SDLK_c: {
if ((event->key.keysym.mod & KMOD_LCTRL) && layer->selection >= 0) {
- clipboard = 1;
- dynarray_copy_to(layer->rects, &clipboard_rect, (size_t)layer->selection);
- dynarray_copy_to(layer->colors, &clipboard_color, (size_t)layer->selection);
+ rect_clipboard = 1;
+ dynarray_copy_to(&layer->rects, &rect_clipboard_rect, (size_t)layer->selection);
+ dynarray_copy_to(&layer->colors, &rect_clipboard_color, (size_t)layer->selection);
}
} break;
case SDLK_v: {
- if ((event->key.keysym.mod & KMOD_LCTRL) && clipboard) {
+ if ((event->key.keysym.mod & KMOD_LCTRL) && rect_clipboard) {
int x, y;
SDL_GetMouseState(&x, &y);
Vec2f position = camera_map_screen(camera, x, y);
rect_layer_add_rect(
layer,
rect(position.x, position.y,
- clipboard_rect.w, clipboard_rect.h),
- clipboard_color,
+ rect_clipboard_rect.w, rect_clipboard_rect.h),
+ rect_clipboard_color,
undo_history);
}
} break;
return 0;
}
+#define GEOMETRY_CAPACITY 256
+
+typedef struct {
+ size_t first;
+ size_t count;
+ Rect rects[GEOMETRY_CAPACITY];
+ Color colors[GEOMETRY_CAPACITY];
+} Geometry;
+
+static
+void push_geometry(Geometry *geometry, Rect rect, Color color)
+{
+ assert(geometry);
+ // TODO(#1252): push_geometry may fail if there is too many rects produced
+ assert(geometry->count < GEOMETRY_CAPACITY);
+
+ if ((rect.w * rect.h) > 1e-6f) {
+ size_t i = (geometry->first + geometry->count) % GEOMETRY_CAPACITY;
+ geometry->rects[i] = rect;
+ geometry->colors[i] = color;
+ geometry->count++;
+ }
+}
+
+static
+void subtract_rect_from_rect(Rect a, Color color_a, Rect c, Geometry *result)
+{
+ assert(result);
+
+ Rect b = rects_overlap_area(a, c);
+
+ if (b.w * b.h < 1e-6) {
+ push_geometry(result, a, color_a);
+ return;
+ }
+
+ push_geometry(result, (Rect) {a.x, a.y, a.w, b.y - a.y}, color_a);
+ push_geometry(result, (Rect) {a.x, b.y, b.x - a.x, b.h}, color_a);
+ push_geometry(result, (Rect) {
+ b.x + b.w,
+ b.y,
+ a.w - (b.x - a.x) - b.w,
+ b.h
+ }, color_a);
+ push_geometry(result, (Rect) {
+ a.x,
+ b.y + b.h,
+ a.w,
+ a.h - (b.y - a.y) - b.h
+ }, color_a);
+}
+
+static
+void subtract_rect_from_geometry(Geometry *result, Rect b)
+{
+ assert(result);
+
+ size_t count = result->count;
+ size_t first = result->first;
+
+ for (size_t i = 0; i < count; ++i) {
+ result->first = (result->first + 1) % GEOMETRY_CAPACITY;
+ result->count -= 1;
+ subtract_rect_from_rect(
+ result->rects[(i + first) % GEOMETRY_CAPACITY],
+ result->colors[(i + first) % GEOMETRY_CAPACITY],
+ b,
+ result);
+ }
+}
+
+static int rect_layer_event_subtract(RectLayer *layer,
+ const SDL_Event *event,
+ const Camera *camera,
+ UndoHistory *undo_history)
+{
+ trace_assert(layer);
+ trace_assert(event);
+ trace_assert(camera);
+ trace_assert(undo_history);
+
+ Rect *rects = layer->rects.data;
+ Color *colors = layer->colors.data;
+
+ switch (event->type) {
+ case SDL_MOUSEBUTTONUP: {
+ switch (event->button.button) {
+ case SDL_BUTTON_LEFT: {
+ const Rect real_rect =
+ rect_from_points(
+ layer->create_begin,
+ layer->create_end);
+ const float area = real_rect.w * real_rect.h;
+
+ Geometry geometry = {0};
+
+ if (area >= CREATE_AREA_THRESHOLD) {
+ for (size_t i = 0; i < layer->rects.count;) {
+ Rect overlap_area = rects_overlap_area(
+ real_rect,
+ rects[i]);
+ if (overlap_area.w * overlap_area.h > 1e-6) {
+ push_geometry(&geometry, rects[i], colors[i]);
+ rect_layer_delete_rect_at_index(layer, i, undo_history);
+ } else {
+ i++;
+ }
+ }
+
+ subtract_rect_from_geometry(&geometry, real_rect);
+
+ for (size_t i = 0; i < geometry.count; ++i) {
+ size_t j = (i + geometry.first) % GEOMETRY_CAPACITY;
+ rect_layer_add_rect(
+ layer,
+ geometry.rects[j],
+ geometry.colors[j],
+ undo_history);
+ }
+ } else {
+ log_info("The area is too small %f. Such small box won't be cut.\n", area);
+ }
+ layer->state = RECT_LAYER_IDLE;
+ } break;
+ }
+ } break;
+
+ case SDL_MOUSEMOTION: {
+ layer->create_end = camera_map_screen(
+ camera,
+ event->motion.x,
+ event->motion.y);
+ } break;
+ }
+
+ return 0;
+}
+
static int rect_layer_event_create(RectLayer *layer,
const SDL_Event *event,
const Camera *camera,
}
static
-void fix_rect_ratio(RectLayer *layer,
- float *x, float *y, // the things we can change to fix the ratio
- Vec2f ref_pt) // the (fixed) reference point of the rect
+void snap_rect_resize_if_enabled(RectLayer *layer, Rect *a, float snapping_threshold)
{
- trace_assert(x);
- trace_assert(y);
+ trace_assert(layer);
+ trace_assert(layer->selection >= 0);
+ trace_assert(a);
- // if we're not holding down shift, don't bother.
- if (!(SDL_GetKeyboardState(NULL)[SDL_SCANCODE_LSHIFT] || SDL_GetKeyboardState(NULL)[SDL_SCANCODE_RSHIFT]))
- return;
+ if (!layer->snapping_enabled) return;
- float ratio = layer->initial_rectangle_size.x / layer->initial_rectangle_size.y;
+ Rect *rects = (Rect*)layer->rects.data;
+ size_t rects_size = layer->rects.count;
- // if we are holding down control also, then make squares.
- if (SDL_GetKeyboardState(NULL)[SDL_SCANCODE_LCTRL] || SDL_GetKeyboardState(NULL)[SDL_SCANCODE_RCTRL])
- ratio = 1.0f;
+ for (size_t i = 0; i < rects_size; ++i) {
+ if (i == (size_t) layer->selection) continue;
- // make some constants for us to use.
- float inv_ratio = 1.0f / ratio;
- float w = *x - ref_pt.x;
- float h = *y - ref_pt.y;
+ const Rect b = rects[i];
- float ab_w = fabsf(w);
- float ab_h = fabsf(h);
+ if (segment_overlap(vec(a->x, a->x + a->w), vec(b.x, b.x + b.w))) {
+ snap_var2seg(&a->y, b.y, 0, b.h, snapping_threshold);
+ }
- // note: copysign takes (magnitude, sign). this thing basically lengthens the shorter side
- // to fit the ratio. copysign is used to handle when the length is negative due to the
- // different corner positions.
- if (ab_w <= ratio * ab_h) {
- *x = ref_pt.x + (ratio * copysignf(h, w));
- } else if (ab_h <= inv_ratio * ab_w) {
- *y = ref_pt.y + inv_ratio * copysignf(w, h);
+ if (segment_overlap(vec(a->y, a->y + a->h), vec(b.y, b.y + b.h))) {
+ snap_var2seg(&a->x, b.x, 0, b.w, snapping_threshold);
+ }
}
}
trace_assert(camera);
trace_assert(layer->selection >= 0);
- Rect *rects = dynarray_data(layer->rects);
+ Rect *rects = (Rect*)layer->rects.data;
float scaled_snap_threshold = SNAPPING_THRESHOLD / camera->scale;
switch (layer->resize_mask) {
case 1: { // TOP
- float y = position.y;
- float x = rects[layer->selection].x;
- float w = rects[layer->selection].w;
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
- snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold);
- }
- }
+ Rect a = rect(rects[layer->selection].x,
+ position.y,
+ rects[layer->selection].w,
+ rects[layer->selection].h);
+
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
- vec(x, y),
+ vec(a.x, a.y),
rect_position2(rects[layer->selection]));
} break;
case 2: { // LEFT
- float y = rects[layer->selection].y;
- float x = position.x;
- float h = rects[layer->selection].h;
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
- snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold);
- }
- }
+ Rect a = rect(position.x,
+ rects[layer->selection].y,
+ rects[layer->selection].w,
+ rects[layer->selection].h);
+
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
- vec(x, y),
+ vec(a.x, a.y),
rect_position2(rects[layer->selection]));
} break;
case 3: { // TOP,LEFT
- float x = position.x;
- float y = position.y;
- float w = rects[layer->selection].w;
- float h = rects[layer->selection].h;
-
- // use the bottom-right as reference.
- Vec2f ref_pos = rect_position2(rects[layer->selection]);
- fix_rect_ratio(layer, &x, &y, ref_pos);
-
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
- if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
- // if we did a snap, we need to update the rect to make sure it
- // still fits the ratio. same pattern repeats below.
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
+ Rect a = rect(
+ position.x,
+ position.y,
+ rects[layer->selection].w,
+ rects[layer->selection].h);
- if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
- if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
- }
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
- vec(x, y),
+ vec(a.x, a.y),
rect_position2(rects[layer->selection]));
} break;
case 4: { // BOTTOM
- float y = position.y;
- float x = rects[layer->selection].x;
- float w = rects[layer->selection].w;
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
- snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold);
- }
- }
+ Rect a = rect(rects[layer->selection].x,
+ position.y,
+ rects[layer->selection].w,
+ rects[layer->selection].h);
+
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
rect_position(rects[layer->selection]),
vec(rects[layer->selection].x + rects[layer->selection].w,
- y));
+ a.y));
} break;
case 6: { // BOTTOM,LEFT
- float x = position.x;
- float y = position.y;
- float w = rects[layer->selection].w;
- float h = rects[layer->selection].h;
-
- // use the top-right as reference.
- Vec2f ref_pos = vec(rects[layer->selection].x + rects[layer->selection].w, rects[layer->selection].y);
- fix_rect_ratio(layer, &x, &y, ref_pos);
-
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
- if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
+ Rect a = rect(
+ position.x,
+ position.y,
+ rects[layer->selection].w,
+ -rects[layer->selection].h);
- if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
- if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
- }
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
- vec(x, rects[layer->selection].y),
+ vec(a.x, rects[layer->selection].y),
vec(rects[layer->selection].x + rects[layer->selection].w,
- y));
+ a.y));
} break;
case 8: { // RIGHT
- float y = rects[layer->selection].y;
- float x = position.x;
- float h = rects[layer->selection].h;
-
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
+ Rect a = rect(position.x,
+ rects[layer->selection].y,
+ rects[layer->selection].w,
+ rects[layer->selection].h);
- const Rect b = rects[i];
- if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
- snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold);
- }
- }
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
rect_position(rects[layer->selection]),
- vec(x,
- rects[layer->selection].y + rects[layer->selection].h));
+ vec(a.x, rects[layer->selection].y + rects[layer->selection].h));
} break;
case 9: { // TOP,RIGHT
- float x = position.x;
- float y = position.y;
- float w = rects[layer->selection].w;
- float h = rects[layer->selection].h;
-
- // use bottom-left as reference.
- Vec2f ref_pos = vec(rects[layer->selection].x, rects[layer->selection].y + rects[layer->selection].h);
- fix_rect_ratio(layer, &x, &y, ref_pos);
-
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
- if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
+ Rect a = rect(
+ position.x,
+ position.y,
+ -rects[layer->selection].w,
+ rects[layer->selection].h);
- if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
- if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
- }
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
- vec(rects[layer->selection].x, y),
- vec(x,
+ vec(rects[layer->selection].x, a.y),
+ vec(a.x,
rects[layer->selection].y + rects[layer->selection].h));
} break;
case 12: { // BOTTOM,RIGHT
- float x = position.x;
- float y = position.y;
- float w = rects[layer->selection].w;
- float h = rects[layer->selection].h;
-
- // use top-left as reference.
- Vec2f ref_pos = rect_position(rects[layer->selection]);
- fix_rect_ratio(layer, &x, &y, ref_pos);
-
- for (size_t i = 0; i < dynarray_count(layer->rects); ++i) {
- if (i == (size_t) layer->selection) continue;
-
- const Rect b = rects[i];
- if (segment_overlap(vec(y, y + h), vec(b.y, b.y + b.h))) {
- if (snap_var2seg(&x, b.x, 0, b.w, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
+ Rect a = rect(
+ position.x,
+ position.y,
+ -rects[layer->selection].w,
+ -rects[layer->selection].h);
- if (segment_overlap(vec(x, x + w), vec(b.x, b.x + b.w))) {
- if (snap_var2seg(&y, b.y, 0, b.h, scaled_snap_threshold)) {
- fix_rect_ratio(layer, &x, &y, ref_pos);
- }
- }
- }
+ snap_rect_resize_if_enabled(layer, &a, scaled_snap_threshold);
layer->inter_rect = rect_from_points(
rect_position(rects[layer->selection]),
- vec(x, y));
+ vec(a.x, a.y));
} break;
}
- // note that we need to update the "initial size" even during the drag. this is because
- // we can enter/exit the ratio mode in the middle of dragging!
- layer->initial_rectangle_size = vec(layer->inter_rect.w, layer->inter_rect.h);
-
} break;
case SDL_MOUSEBUTTONUP: {
layer->state = RECT_LAYER_IDLE;
- UNDO_PUSH(undo_history, create_undo_update_context(layer));
- dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
+ RECT_UNDO_PUSH(
+ undo_history,
+ create_rect_undo_update_context(
+ layer,
+ (size_t) layer->selection));
+ dynarray_replace_at(&layer->rects, (size_t) layer->selection, &layer->inter_rect);
} break;
}
}
static
-void snap_rects(size_t ignore_index, Rect *a,
- Rect *rects, size_t rects_size,
- float snapping_threshold)
+void snap_rect_move_if_enabled(RectLayer *layer, Rect *a,
+ float snapping_threshold)
{
- trace_assert(rects);
trace_assert(a);
+ trace_assert(layer);
+ trace_assert(layer->selection >= 0);
+
+ if (!layer->snapping_enabled) return;
+
+ Rect *rects = (Rect*)layer->rects.data;
+ size_t rects_size = layer->rects.count;
for (size_t i = 0; i < rects_size; ++i) {
- if (i == ignore_index) continue;
+ if (i == (size_t) layer->selection) continue;
const Rect b = rects[i];
trace_assert(camera);
trace_assert(layer->selection >= 0);
- Rect *rects = dynarray_data(layer->rects);
+ Rect *rects = (Rect*)layer->rects.data;
switch (event->type) {
case SDL_MOUSEMOTION: {
}
}
- // TODO(#1141): Rect Snapping in Level Editor should be optional
- snap_rects((size_t) layer->selection, &layer->inter_rect,
- rects, dynarray_count(layer->rects),
- SNAPPING_THRESHOLD / camera->scale);
+ snap_rect_move_if_enabled(layer, &layer->inter_rect,
+ SNAPPING_THRESHOLD / camera->scale);
} break;
case SDL_MOUSEBUTTONUP: {
rect_position(rects[layer->selection])));
if (distance > 1e-6) {
- UNDO_PUSH(undo_history, create_undo_update_context(layer));
- dynarray_replace_at(layer->rects, (size_t) layer->selection, &layer->inter_rect);
+ RECT_UNDO_PUSH(
+ undo_history,
+ create_rect_undo_update_context(
+ layer, (size_t) layer->selection));
+ dynarray_replace_at(
+ &layer->rects,
+ (size_t) layer->selection,
+ &layer->inter_rect);
}
} break;
}
case SDL_KEYDOWN: {
switch (event->key.keysym.sym) {
case SDLK_RETURN: {
- UNDO_PUSH(undo_history, create_undo_update_context(layer));
+ RECT_UNDO_PUSH(
+ undo_history,
+ create_rect_undo_update_context(
+ layer,
+ (size_t) layer->selection));
- char *id = dynarray_pointer_at(layer->ids, (size_t)layer->selection);
+ char *id = dynarray_pointer_at(&layer->ids, (size_t)layer->selection);
memset(id, 0, ENTITY_MAX_ID_SIZE);
- memcpy(id, edit_field_as_text(layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
+ memcpy(id, edit_field_as_text(&layer->id_edit_field), ENTITY_MAX_ID_SIZE - 1);
layer->state = RECT_LAYER_IDLE;
SDL_StopTextInput();
} break;
} break;
}
- return edit_field_event(layer->id_edit_field, event);
+ return edit_field_event(&layer->id_edit_field, event);
}
LayerPtr rect_layer_as_layer(RectLayer *rect_layer)
return layer;
}
-RectLayer *create_rect_layer(const char *id_name_prefix, Cursor *cursor)
+RectLayer *create_rect_layer(Memory *memory,
+ const char *id_name_prefix,
+ Cursor *cursor)
{
+ trace_assert(memory);
+ trace_assert(id_name_prefix);
trace_assert(cursor);
- Lt *lt = create_lt();
+ RectLayer *rect_layer = memory_alloc(memory, sizeof(RectLayer));
- RectLayer *layer = PUSH_LT(lt, nth_calloc(1, sizeof(RectLayer)), free);
- if (layer == NULL) {
- RETURN_LT(lt, NULL);
- }
- layer->lt = lt;
-
- layer->ids = PUSH_LT(
- lt,
- create_dynarray(sizeof(char) * ENTITY_MAX_ID_SIZE),
- destroy_dynarray);
- if (layer->ids == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- layer->rects = PUSH_LT(
- lt,
- create_dynarray(sizeof(Rect)),
- destroy_dynarray);
- if (layer->rects == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- layer->colors = PUSH_LT(
- lt,
- create_dynarray(sizeof(Color)),
- destroy_dynarray);
- if (layer->colors == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- layer->actions = PUSH_LT(
- lt,
- create_dynarray(sizeof(Action)),
- destroy_dynarray);
- if (layer->actions == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- layer->id_edit_field = PUSH_LT(
- lt,
- create_edit_field(
- RECT_LAYER_ID_LABEL_SIZE,
- COLOR_BLACK),
- destroy_edit_field);
- if (layer->id_edit_field == NULL) {
- RETURN_LT(lt, NULL);
- }
-
- layer->grid =
- PUSH_LT(
- lt,
- nth_calloc(
- 1,
- sizeof(Grid) + sizeof(Widget*) * RECT_LAYER_GRID_ROWS * RECT_LAYER_GRID_COLUMNS),
- free);
- if (layer->grid == NULL) {
- RETURN_LT(lt, NULL);
- }
- layer->grid->rows = RECT_LAYER_GRID_ROWS;
- layer->grid->columns = RECT_LAYER_GRID_COLUMNS;
- grid_put_widget(layer->grid, &layer->action_picker.widget, 0, RECT_LAYER_GRID_COLUMNS - 1);
+ rect_layer->ids = create_dynarray(memory, sizeof(char) * ENTITY_MAX_ID_SIZE);
+ rect_layer->rects = create_dynarray(memory, sizeof(Rect));
+ rect_layer->colors = create_dynarray(memory, sizeof(Color));
+ rect_layer->actions = create_dynarray(memory, sizeof(Action));
+ rect_layer->id_edit_field.font_size = RECT_LAYER_ID_LABEL_SIZE;
+ rect_layer->id_edit_field.font_color = COLOR_BLACK;
+ rect_layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
+ rect_layer->selection = -1;
+ rect_layer->id_name_prefix = id_name_prefix;
+ rect_layer->cursor = cursor;
- layer->color_picker = create_color_picker_from_rgba(rgba(1.0f, 0.0f, 0.0f, 1.0f));
- layer->selection = -1;
- layer->id_name_prefix = id_name_prefix;
- layer->cursor = cursor;
-
- return layer;
+ return rect_layer;
}
-RectLayer *create_rect_layer_from_line_stream(LineStream *line_stream,
- const char *id_name_prefix,
- Cursor *cursor)
+void rect_layer_load(RectLayer *layer, Memory *memory, String *input)
{
- trace_assert(line_stream);
-
- RectLayer *layer = create_rect_layer(id_name_prefix, cursor);
- if (layer == NULL) {
- return NULL;
- }
-
- const char *line = line_stream_next(line_stream);
- if (line == NULL) {
- RETURN_LT(layer->lt, NULL);
- }
-
- size_t count = 0;
- if (sscanf(line, "%zu", &count) < 0) {
- RETURN_LT(layer->lt, NULL);
- }
-
- for (size_t i = 0; i < count; ++i) {
- line = line_stream_next(line_stream);
- if (line == NULL) {
- RETURN_LT(layer->lt, NULL);
- }
+ trace_assert(layer);
+ trace_assert(memory);
+ trace_assert(input);
- char hex[7];
+ int n = atoi(string_to_cstr(memory, trim(chop_by_delim(input, '\n'))));
+ char id[ENTITY_MAX_ID_SIZE];
+ for (int i = 0; i < n; ++i) {
Rect rect;
- char id[ENTITY_MAX_ID_SIZE];
-
- int n = 0;
- if (sscanf(line,
- "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s%f%f%f%f%6s%n",
- id,
- &rect.x, &rect.y,
- &rect.w, &rect.h,
- hex, &n) <= 0) {
- log_fail("%s\n", strerror(errno));
- RETURN_LT(layer->lt, NULL);
- }
- line += n;
-
- Color color = hexstr(hex);
- dynarray_push(layer->rects, &rect);
- dynarray_push(layer->ids, id);
- dynarray_push(layer->colors, &color);
+ String line = trim(chop_by_delim(input, '\n'));
+ String string_id = trim(chop_word(&line));
+ rect.x = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+ rect.y = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+ rect.w = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+ rect.h = strtof(string_to_cstr(memory, trim(chop_word(&line))), NULL);
+ Color color = hexs(trim(chop_word(&line)));
+
+ memset(id, 0, ENTITY_MAX_ID_SIZE);
+ memcpy(
+ id,
+ string_id.data,
+ min_size_t(ENTITY_MAX_ID_SIZE - 1, string_id.count));
+
+ dynarray_push(&layer->rects, &rect);
+ dynarray_push(&layer->colors, &color);
+ dynarray_push(&layer->ids, id);
Action action = {
.type = ACTION_NONE,
.entity_id = {0}
};
- if (sscanf(line, "%d%n", (int*)&action.type, &n) > 0) {
- line += n;
+ String action_string = trim(chop_word(&line));
+ if (action_string.count > 0) {
+ action.type = (ActionType)atol(string_to_cstr(memory, action_string));
switch (action.type) {
case ACTION_NONE: break;
-
case ACTION_TOGGLE_GOAL:
case ACTION_HIDE_LABEL: {
- if (sscanf(line, "%"STRINGIFY(ENTITY_MAX_ID_SIZE)"s", action.entity_id) <= 0) {
- log_fail("%s\n", strerror(errno));
- RETURN_LT(layer->lt, NULL);
- }
+ String label_id = trim(chop_word(&line));
+ trace_assert(label_id.count > 0);
+ memset(action.entity_id, 0, ENTITY_MAX_ID_SIZE);
+ memcpy(action.entity_id,
+ label_id.data,
+ min_size_t(
+ ENTITY_MAX_ID_SIZE - 1,
+ label_id.count));
} break;
case ACTION_N: break;
}
}
- dynarray_push(layer->actions, &action);
+ dynarray_push(&layer->actions, &action);
}
-
- return layer;
}
-void destroy_rect_layer(RectLayer *layer)
+static
+void render_tool_bar_button(const Camera *camera,
+ Rect button_rect, const char *text,
+ Color background_color,
+ Color foreground_color)
{
- trace_assert(layer);
- RETURN_LT0(layer->lt);
+ const Vec2f text_size = vec(5.0f, 5.0f);
+ const Rect text_rect = sprite_font_boundary_box(
+ vec(0.0f, 0.0f), text_size, text);
+ camera_fill_rect_screen(
+ camera,
+ button_rect,
+ background_color);
+ camera_render_text_screen(
+ camera,
+ text,
+ text_size,
+ foreground_color,
+ vec(
+ button_rect.x + (button_rect.w - text_rect.w) * 0.5f,
+ button_rect.y + (button_rect.h - text_rect.h) * 0.5f));
}
+
int rect_layer_render(const RectLayer *layer, const Camera *camera, int active)
{
trace_assert(layer);
trace_assert(camera);
- const size_t n = dynarray_count(layer->rects);
- Rect *rects = dynarray_data(layer->rects);
- Color *colors = dynarray_data(layer->colors);
- const char *ids = dynarray_data(layer->ids);
+ const size_t n = layer->rects.count;
+ Rect *rects = (Rect *)layer->rects.data;
+ Color *colors = (Color *)layer->colors.data;
+ const char *ids = (const char *)layer->ids.data;
// The Rectangles
for (size_t i = 0; i < n; ++i) {
if (layer->state == RECT_LAYER_ID_RENAME) {
// ID renaming Edit Field
if (edit_field_render_world(
- layer->id_edit_field,
+ &layer->id_edit_field,
camera,
rect_id_pos) < 0) {
return -1;
}
}
+ if (layer->state == RECT_LAYER_SUBTRACT) {
+ if (camera_draw_rect(camera, rect_from_points(layer->create_begin, layer->create_end), color) < 0) {
+ return -1;
+ }
+ }
+
if (active && color_picker_render(&layer->color_picker, camera) < 0) {
return -1;
}
+ // Tool bar
+ if (active) {
+ render_tool_bar_button(
+ camera, subtract_tool_button_rect(camera), "/",
+ layer->subtract_enabled ? TOOL_BAR_BACKGROUND : TOOL_BAR_FOREGROUND,
+ layer->subtract_enabled ? TOOL_BAR_FOREGROUND : TOOL_BAR_BACKGROUND);
+
+ render_tool_bar_button(
+ camera, snapping_tool_button_rect(camera), "S",
+ layer->snapping_enabled ? TOOL_BAR_BACKGROUND : TOOL_BAR_FOREGROUND,
+ layer->snapping_enabled ? TOOL_BAR_FOREGROUND : TOOL_BAR_BACKGROUND);
+ }
+
return 0;
}
layer->inter_color = color_picker_rgba(&layer->color_picker);
if (!color_picker_drag(&layer->color_picker)) {
- UNDO_PUSH(undo_history, create_undo_update_context(layer));
- dynarray_replace_at(layer->colors, (size_t) layer->selection, &layer->inter_color);
+ RECT_UNDO_PUSH(
+ undo_history,
+ create_rect_undo_update_context(
+ layer,
+ (size_t)layer->selection));
+ dynarray_replace_at(&layer->colors, (size_t) layer->selection, &layer->inter_color);
layer->state = RECT_LAYER_IDLE;
}
}
trace_assert(event);
trace_assert(undo_history);
- switch (event->type) {
- case SDL_WINDOWEVENT: {
- switch (event->window.event) {
- case SDL_WINDOWEVENT_RESIZED: {
- grid_relayout(layer->grid, rect(0.0f, 0.0f,
- (float) event->window.data1,
- (float) event->window.data2));
- } break;
- }
- } break;
- }
-
switch (layer->state) {
case RECT_LAYER_IDLE:
return rect_layer_event_idle(layer, event, camera, undo_history);
case RECT_LAYER_RECOLOR:
return rect_layer_event_recolor(layer, event, camera, undo_history);
+
+ case RECT_LAYER_SUBTRACT:
+ return rect_layer_event_subtract(layer, event, camera, undo_history);
}
size_t rect_layer_count(const RectLayer *layer)
{
- return dynarray_count(layer->rects);
+ return layer->rects.count;
}
const Rect *rect_layer_rects(const RectLayer *layer)
{
- return dynarray_data(layer->rects);
+ return (const Rect *)layer->rects.data;
}
const Color *rect_layer_colors(const RectLayer *layer)
{
- return dynarray_data(layer->colors);
+ return (const Color *)layer->colors.data;
}
const char *rect_layer_ids(const RectLayer *layer)
{
- return dynarray_data(layer->ids);
+ return (const char *)layer->ids.data;
}
int rect_layer_dump_stream(const RectLayer *layer, FILE *filedump)
trace_assert(layer);
trace_assert(filedump);
- size_t n = dynarray_count(layer->ids);
- char *ids = dynarray_data(layer->ids);
- Rect *rects = dynarray_data(layer->rects);
- Color *colors = dynarray_data(layer->colors);
- Action *actions = dynarray_data(layer->actions);
+ size_t n = layer->ids.count;
+ char *ids = (char *)layer->ids.data;
+ Rect *rects = (Rect *)layer->rects.data;
+ Color *colors = (Color *)layer->colors.data;
+ Action *actions = (Action *)layer->actions.data;
fprintf(filedump, "%zd\n", n);
for (size_t i = 0; i < n; ++i) {
const Action *rect_layer_actions(const RectLayer *layer)
{
- return dynarray_data(layer->actions);
+ return (const Action *)layer->actions.data;
}