5#include "absl/strings/str_format.h"
9#include "imgui/imgui.h"
44 "Rectangle mode used for single object selection");
53 int room_min_x,
int room_min_y,
int room_max_x,
int room_max_y,
54 const std::vector<zelda3::RoomObject>& objects,
SelectionMode mode) {
56 int min_x = std::min(room_min_x, room_max_x);
57 int max_x = std::max(room_min_x, room_max_x);
58 int min_y = std::min(room_min_y, room_max_y);
59 int max_y = std::max(room_min_y, room_max_y);
67 for (
size_t i = 0; i < objects.size(); ++i) {
88 for (
size_t i = 0; i < object_count; ++i) {
95 const std::vector<zelda3::RoomObject>& objects) {
97 for (
size_t i = 0; i < objects.size(); ++i) {
121 std::vector<size_t> result;
124 result.push_back(idx);
151 "UpdateRectangleSelection called when not active");
160 const std::vector<zelda3::RoomObject>& objects,
SelectionMode mode,
164 "EndRectangleSelection called when not active");
169 auto [start_room_x, start_room_y] =
171 auto [end_room_x, end_room_y] =
176 if (!is_object_visible) {
183 const int min_x = std::min(start_room_x, end_room_x);
184 const int max_x = std::max(start_room_x, end_room_x);
185 const int min_y = std::min(start_room_y, end_room_y);
186 const int max_y = std::max(start_room_y, end_room_y);
187 for (
size_t index = 0; index < objects.size(); ++index) {
188 if (!is_object_visible(objects[index]) ||
214 return {min_x, min_y, max_x, max_y};
223 const int width = std::abs(max_x - min_x);
224 const int height = std::abs(max_y - min_y);
225 return (width >= min_pixels) || (height >= min_pixels);
233 gui::Canvas* canvas,
const std::vector<zelda3::RoomObject>& objects,
240 ImDrawList* draw_list = ImGui::GetWindowDrawList();
243 const float scale = transform.
scale();
246 if (index >= objects.size()) {
250 const auto&
object = objects[index];
258 int pixel_height = 0;
259 if (bounds_calculator) {
260 auto [dx, dy, w, h] = bounds_calculator(
object);
267 auto [tile_x, tile_y, tile_width, tile_height] =
GetObjectBounds(
object);
268 offset_x = (tile_x -
object.x_) * 8;
269 offset_y = (tile_y -
object.y_) * 8;
270 pixel_width = tile_width * 8;
271 pixel_height = tile_height * 8;
278 ImVec2(
static_cast<float>(obj_x),
static_cast<float>(obj_y)));
280 static_cast<float>(pixel_width),
static_cast<float>(pixel_height)));
281 ImVec2 obj_end(obj_start.x + obj_size.x, obj_start.y + obj_size.y);
284 constexpr float margin = 2.0f;
285 obj_start.x -= margin;
286 obj_start.y -= margin;
295 0.6f + 0.4f * std::sin(
static_cast<float>(ImGui::GetTime()) * 6.0f);
296 ImVec4 pulsing_color = theme.selection_primary;
297 pulsing_color.w = 0.4f + 0.4f * pulse;
299 ImU32 border_color = ImGui::GetColorU32(theme.selection_primary);
300 ImU32 pulse_color_u32 = ImGui::GetColorU32(pulsing_color);
302 draw_list->AddRect(obj_start, obj_end, border_color, 0.0f, 0, 2.0f);
303 draw_list->AddRect(obj_start, obj_end, pulse_color_u32, 0.0f, 0, 4.0f);
306 float handle_size = 6.0f * std::max(1.0f, scale * 0.5f);
307 ImU32 handle_color = ImGui::GetColorU32(theme.selection_handle);
308 ImU32 handle_outline = ImGui::GetColorU32(ImVec4(0, 0, 0, 0.8f));
310 auto draw_handle = [&](ImVec2 pos) {
311 draw_list->AddRectFilled(
312 ImVec2(pos.x - handle_size / 2, pos.y - handle_size / 2),
313 ImVec2(pos.x + handle_size / 2, pos.y + handle_size / 2),
316 ImVec2(pos.x - handle_size / 2, pos.y - handle_size / 2),
317 ImVec2(pos.x + handle_size / 2, pos.y + handle_size / 2),
318 handle_outline, 2.0f, 0, 1.0f);
321 draw_handle(obj_start);
322 draw_handle(ImVec2(obj_end.x, obj_start.y));
323 draw_handle(ImVec2(obj_start.x, obj_end.y));
324 draw_handle(obj_end);
331 int layer =
object.GetLayerValue();
335 return theme.dungeon_outline_layer0;
337 return theme.dungeon_outline_layer1;
339 return theme.dungeon_outline_layer2;
341 return theme.status_inactive;
351 ImDrawList* draw_list = ImGui::GetWindowDrawList();
359 ImVec2(
static_cast<float>(min_x),
static_cast<float>(min_y)));
361 ImVec2(
static_cast<float>(max_x),
static_cast<float>(max_y)));
365 ImU32 border_color = ImGui::ColorConvertFloat4ToU32(
366 ImVec4(theme.selection_secondary.x, theme.selection_secondary.y,
367 theme.selection_secondary.z, 0.85f));
369 ImU32 fill_color = ImGui::ColorConvertFloat4ToU32(
370 ImVec4(theme.selection_secondary.x, theme.selection_secondary.y,
371 theme.selection_secondary.z, 0.15f));
373 draw_list->AddRectFilled(box_start, box_end, fill_color);
374 draw_list->AddRect(box_start, box_end, border_color, 0.0f, 0, 2.0f);
384 return {room_x * 8, room_y * 8};
390 return {canvas_x / 8, canvas_y / 8};
409 int min_x,
int min_y,
int max_x,
421 int obj_min_x = obj_x;
422 int obj_max_x = obj_x + obj_width - 1;
423 int obj_min_y = obj_y;
424 int obj_max_y = obj_y + obj_height - 1;
427 bool x_overlap = (obj_min_x <= max_x) && (obj_max_x >= min_x);
428 bool y_overlap = (obj_min_y <= max_y) && (obj_max_y >= min_y);
430 return x_overlap && y_overlap;
442 return object.GetLayerValue() ==
kLayer2;
bool IsObjectInRectangle(const zelda3::RoomObject &object, int min_x, int min_y, int max_x, int max_y) const
std::tuple< int, int, int, int > GetRectangleSelectionBounds() const
Get rectangle selection bounds in canvas coordinates.
void UpdateRectangleSelection(int canvas_x, int canvas_y)
Update rectangle selection endpoint.
bool rectangle_selection_active_
static std::pair< int, int > RoomToCanvasCoordinates(int room_x, int room_y)
Convert room tile coordinates to canvas pixel coordinates.
void DrawSelectionHighlights(gui::Canvas *canvas, const std::vector< zelda3::RoomObject > &objects, std::function< std::tuple< int, int, int, int >(const zelda3::RoomObject &)> bounds_calculator)
Draw selection highlights for all selected objects.
std::function< void()> selection_changed_callback_
static constexpr int kMaskLayer
bool IsObjectSelected(size_t index) const
Check if an object is selected.
std::set< size_t > selected_indices_
void EndRectangleSelection(const std::vector< zelda3::RoomObject > &objects, SelectionMode mode=SelectionMode::Single, std::function< bool(const zelda3::RoomObject &)> is_object_visible={})
Complete rectangle selection operation.
std::vector< size_t > GetSelectedIndices() const
Get all selected object indices.
static std::tuple< int, int, int, int > GetObjectBounds(const zelda3::RoomObject &object)
Calculate the bounding box of an object.
bool IsRectangleLargeEnough(int min_pixels) const
Check if rectangle selection exceeds a minimum pixel size.
void SelectAll(size_t object_count)
Select all objects in the current room.
static std::pair< int, int > CanvasToRoomCoordinates(int canvas_x, int canvas_y)
Convert canvas pixel coordinates to room tile coordinates.
void SelectObject(size_t index, SelectionMode mode=SelectionMode::Single)
Select a single object by index.
void ClearSelection()
Clear all selections.
void NotifySelectionChanged()
void BeginRectangleSelection(int canvas_x, int canvas_y)
Begin a rectangle selection operation.
bool PassesLayerFilter(const zelda3::RoomObject &object) const
static constexpr int kLayer2
ImVec4 GetLayerTypeColor(const zelda3::RoomObject &object) const
Get selection highlight color based on object layer and type.
static constexpr int kLayerAll
void CancelRectangleSelection()
Cancel rectangle selection without modifying selection.
std::optional< size_t > GetPrimarySelection() const
Get the primary selected object (first in selection)
void DrawRectangleSelectionBox(gui::Canvas *canvas)
Draw the active rectangle selection box.
void SelectObjectsInRect(int room_min_x, int room_min_y, int room_max_x, int room_max_y, const std::vector< zelda3::RoomObject > &objects, SelectionMode mode=SelectionMode::Single)
Select multiple objects within a rectangle.
Modern, robust canvas for drawing and manipulating graphics.
auto global_scale() const
static DimensionService & Get()
std::tuple< int, int, int, int > GetHitTestBounds(const RoomObject &obj) const
#define LOG_ERROR(category, format,...)
const AgentUITheme & GetTheme()
Editors are the view controllers for the application.