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