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