MeshLib Documentation
Loading...
Searching...
No Matches
MRUIRectAllocator.h
Go to the documentation of this file.
1#pragma once
2
3#include "exports.h"
4#include "MRMesh/MRBox.h"
5#include "MRMesh/MRHash.h"
6#include "MRMesh/MRphmap.h"
7#include "MRMesh/MRVector2.h"
9
10#include <imgui.h>
11
12namespace MR::UI
13{
14
15// A generic rect allocator.
16// Given a set of rects and a target rect, finds the closest free rect next to the target (using dijkstra's algorithm).
17// The class can be reused multiple times to avoid repeated heap allocation, it doesn't store any state other than memory pools.
18// For optimal results you should bring your own AABB tree (see `FindPotentiallyOverlappingRects`) below,
19// but in simple cases you can get away with a flat list.
21{
22public:
23 MRVIEWER_API RectAllocator();
24
26 {
27 Box2f rect;
28 bool ok = true; // False if no free space for this rect.
29 [[nodiscard]] explicit operator bool() const { return ok; }
30 };
31
32 // Given an input rect, this function must find all POTENTIALLY overlapping rects ("overlapping" means according to `rectRectOverlap()`).
33 // `name` is only useful for debugging.
34 using FindPotentiallyOverlappingRects = std::function<void( Box2f target, std::function<void( const char* name, Box2f box )> overlaps )>;
35
36 // Finds a free rectangle of the specified size, as close as possible to the specified position.
37 // On failure, returns `.ok == false` and returns the input rect unchanged.
38 [[nodiscard]] MRVIEWER_API FindFreeRectResult findFreeRect(
39 Box2f preferredRect,
40 // The outer bounds that we try to fit the rect into. The input rect doesn't need to be in bounds,
41 // but we will not move it more out of bounds than it already is.
42 Box2f preferredBounds,
43 // Given any rect, this must return all existing rects potentially overlapping with it.
45 // This sets the preference for X and Y axes. Larger number = less likely to shift over that axis.
46 ImVec2 axisWeights = ImVec2( 1, 1 )
47 );
48
49 // Checks if two rects overlap.
50 // We can't use `.intersects()` here because we want `.max` to be exclusive, while `.intersects()` treats both bounds as inclusive.
51 [[nodiscard]] static bool rectRectOverlap( Box2f a, Box2f b )
52 {
53 return a.max.x > b.min.x && a.min.x < b.max.x && a.max.y > b.min.y && a.min.y < b.max.y;
54 }
55
56private:
57 // Maps coords to cost.
58 phmap::flat_hash_map<Vector2f, float> visitedCoords;
59
60 struct CoordsToVisit
61 {
62 Vector2f pos;
63 float cost = 0;
64 std::array<float, 4> overlapWithBounds;
65 };
66 std::vector<CoordsToVisit> coordsToVisitHeap;
67};
68
69// A rect allocator specifically for ImGui windows.
71{
72public:
73 // Call this before drawing a window.
74 // `expectedWindowName` must match the window name.
75 // The remaining parameters are forwarded to `ImGui::SetNextWindowPos()`, expect for one time where we find a free rect and use it instead.
76 // `cond` must not be `ImGuiCond_Always` (aka 0), in that case we just forward the arguments and don't try to find a rect.
77 // We automatically avoid all windows with `[rect_allocator_ignore]` anywhere after `##` in the name.
78 MRVIEWER_API void setFreeNextWindowPos( const char* expectedWindowName, ImVec2 defaultPos, ImGuiCond cond = ImGuiCond_Appearing, ImVec2 pivot = ImVec2() );
79
80private:
81 int lastFrameCount_ = -1;
82
83 enum class AllocationState
84 {
85 None, // This window wasn't yet drawn during this frame. If it's not drawn until the end of frame, it will be deleted from this map.
86 Requested, // This window has just appeared and a free position will be selected for it in the next frame
87 Set // This window has set position and was already drawn in this frame
88 };
89
90 struct WindowEntry
91 {
92 AllocationState state_{ AllocationState::None };
93 };
94 phmap::flat_hash_map<std::string, WindowEntry> windows_;
95};
97
98// A rect allocator for labels.
100{
101public:
102 // Call this every frame to maintain a rectangle. Using the same ID more than once per frame triggers an assertion.
103 // Returns the free position closest to `pos`.
104 // Separate label lists are maintained per viewport.
105 // If `forceExactPosition == true`, the input position is returned unchanged, but the rect is still added to the internal list to push away other rects.
106 MRVIEWER_API ImVec2 createRect( ViewportId viewportId, std::string id, ImVec2 pos, ImVec2 size, bool forceExactPosition = false );
107
108private:
109 int lastFrameCount_ = -1;
110
111 struct Entry
112 {
113 Box2f box;
114 bool visitedThisFrame = true;
115 };
116 // The vector uses viewport indices.
117 std::vector<phmap::flat_hash_map<std::string, Entry>> entries_;
118};
120
121}
Definition MRUIRectAllocator.h:100
MRVIEWER_API ImVec2 createRect(ViewportId viewportId, std::string id, ImVec2 pos, ImVec2 size, bool forceExactPosition=false)
Definition MRUIRectAllocator.h:21
std::function< void(Box2f target, std::function< void(const char *name, Box2f box)> overlaps)> FindPotentiallyOverlappingRects
Definition MRUIRectAllocator.h:34
MRVIEWER_API RectAllocator()
static bool rectRectOverlap(Box2f a, Box2f b)
Definition MRUIRectAllocator.h:51
MRVIEWER_API FindFreeRectResult findFreeRect(Box2f preferredRect, Box2f preferredBounds, FindPotentiallyOverlappingRects findOverlaps, ImVec2 axisWeights=ImVec2(1, 1))
Definition MRUIRectAllocator.h:71
MRVIEWER_API void setFreeNextWindowPos(const char *expectedWindowName, ImVec2 defaultPos, ImGuiCond cond=ImGuiCond_Appearing, ImVec2 pivot=ImVec2())
Definition MRViewportId.h:16
@ None
special value not to limit path in one slice
Definition MRVoxelPath.h:33
Definition MRUINonOverlappingLabels.h:10
MRVIEWER_API WindowRectAllocator & getDefaultWindowRectAllocator()
MRVIEWER_API LabelRectAllocator & getDefaultLabelRectAllocator()
ImVec2 size(const ViewportRectangle &rect)
Definition MRViewport.h:32
Definition MRUIRectAllocator.h:26
bool ok
Definition MRUIRectAllocator.h:28
Box2f rect
Definition MRUIRectAllocator.h:27