How can I rotate x labels?
- Dominant language
- C++
- Stars
- 6.2k
- Forks
- 693
- PR merge metrics
- No merged PRs in 30d
Description
I want to rotate x labels in the plot, so I get something like the following (but with the label actually at the correct tick location inside the plot).
I can't find an example so could anyone give me any pointers.
Presumably this would be done using ImPlot::SetupAxisFormat with a custom formatter which calls ImPlot::PlotText with the ImPlotTextFlags_Vertical flag (which internally calls the private function ::AddTextVertical)?
I was also wondering about arbitrary angles so used the alternative as below.
.
NB there is a bug in this code:
Imgui clips the text *before* it is rotated. This means text at the edges can have parts missing
(as you can see in this image with the text at the right)

// Rotating text from here:
https://github.com/ocornut/imgui/issues/6923
```
#include "imgui_internal.h"
#include "imgui_internal.h"
//internal bounding boxes
// BB = outermost 'true' bounding box (including bounding box for handles)
// DR = bounding box for drawing
// IN = inner (ie inside bufferzone for boundaries handles
// CE = centron (prob central 10%)
// TL...B = bb for handles for grabbing the various edges/corners
enum bbEnum { BB_BB, BB_DR, BB_IN, BB_CE, BB_TL, BB_TR, BB_BL, BB_BR, BB_L, BB_R, BB_T, BB_B };
int rotation_start_index;
void ImRotateStart()
{
rotation_start_index = ImGui::GetWindowDrawList()->VtxBuffer.Size;
}
ImVec2 ImRotationCenter()
{
ImVec2 l(FLT_MAX, FLT_MAX), u(-FLT_MAX, -FLT_MAX); // bounds
const auto& buf = ImGui::GetWindowDrawList()->VtxBuffer;
for (int i = rotation_start_index; i < buf.Size; i++)
l = ImMin(l, buf[i].pos), u = ImMax(u, buf[i].pos);
return ImVec2((l.x + u.x) / 2, (l.y + u.y) / 2); // or use _ClipRectStack?
}
void ImRotateEnd(float rad, ImVec2 center = ImRotationCenter())
{
// Adjust the angle to match the standard horizontal orientation
rad -= IM_PI / 2.0f; // Subtract 90 degrees (PI/2 radians)
float s = sin(rad), c = cos(rad);
center = ImRotate(center, s, c) - center;
auto& buf = ImGui::GetWindowDrawList()->VtxBuffer;
for (int i = rotation_start_index; i < buf.Size; i++)
buf[i].pos = ImRotate(buf[i].pos, s, c) - center;
}
void drawRotatedText(std::string textToRotate, float angleToRotate = 0, bbEnum rotationCentre = BB_CE, bool isRads = true)
{
// Calculate rotation angle in radians
float rad = angleToRotate;
if (!isRads)
rad = rad * IM_PI / 180.0f;
ImVec2 textStartPosition = ImGui::GetWindowPos() + ImGui::GetCursorPos(); // ImRotateEnd uses GetWindowDrawList which is based on position of the screen
// Calculate text size to determine bounding box
ImVec2 textSize = ImGui::CalcTextSize(textToRotate.c_str());
ImVec2 rotationCenter;
// Calculate rotation center based on bbEnum value
switch (rotationCentre)
{
case BB_CE:
rotationCenter = ImVec2(textStartPosition.x + textSize.x * 0.5f, textStartPosition.y + textSize.y * 0.5f);
break;
case BB_TL:
rotationCenter = textStartPosition;
break;
case BB_TR:
rotationCenter = ImVec2(textStartPosition.x + textSize.x, textStartPosition.y);
break;
case BB_BL:
rotationCenter = ImVec2(textStartPosition.x, textStartPosition.y + textSize.y);
break;
case BB_BR:
rotationCenter = ImVec2(textStartPosition.x + textSize.x, textStartPosition.y + textSize.y);
break;
case BB_L:
rotationCenter = ImVec2(textStartPosition.x, textStartPosition.y + textSize.y / 2);
break;
case BB_R:
rotationCenter = ImVec2(textStartPosition.x + textSize.x, textStartPosition.y + textSize.y / 2);
break;
case BB_T:
rotationCenter = ImVec2(textStartPosition.x + textSize.x / 2, textStartPosition.y);
break;
case BB_B:
rotationCenter = ImVec2(textStartPosition.x + textSize.x / 2, textStartPosition.y + textSize.y);
break;
default:
std::cout << "ERROR: invalid rotationCentre in drawRotatedText\n";
return;
}
// Start rotation
ImRotateStart();
//std::cout << ImGui::GetCursorPosX() << " " << ImGui::GetCursorPosY() << std::endl;
// Render the text
ImGui::Text(textToRotate.c_str());
// Apply the rotation
ImRotateEnd(rad, rotationCenter);
}
// Function to display the ImPlot child with rotated X-axis tick labels
void rotatedXLabelExample() {
ImGui::Begin("Rotated Label Example");
// Dummy data for plotting
static double maxTime = 10.0;
static std::vector modtimeCosts = { 2.0, 4.0, 6.0, 8.0, 5.0 };
static std::vector modNames = { "Module 1", "Module 2", "Module 3", "Module 4", "Module 5" };
ImGui::BeginChild("ModuleProcessingTimeCostChild", ImVec2(0, 0), false);
ImVec2 spaceAvail = ImGui::GetContentRegionAvail();
if (ImPlot::BeginPlot("Module Processing Time Cost", "Module", "Time Cost", ImVec2(-1, spaceAvail.y - 250)))
{
// Customize X-axis tick labels
double x_ticks[5];
const char* x_labels[5];
for (size_t i = 0; i < modNames.size(); ++i)
{
x_ticks[i] = static_cast(i);
x_labels[i] = modNames[i].c_str();
}
ImPlot::SetupAxisTicks(ImAxis_X1, x_ticks, 5, x_labels, true);
ImPlot::SetupAxes("Module", "Time (secs)", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxisLimits(ImAxis_Y1, 0, maxTime, ImGuiCond_Always);
ImPlot::PlotBars("Processing Time Cost", modtimeCosts.data(), modtimeCosts.size());
ImPlot::EndPlot();
}
// Rotate the X-axis tick labels
float xLoc = 70;
float rightMargin = 30;
float xStep = (spaceAvail.x - xLoc - rightMargin) / (float)modNames.size();
float yLoc = ImGui::GetCursorPosY();
for (int i = 0; i < modNames.size(); ++i) {
ImVec2 textSize = ImGui::CalcTextSize(modNames[i].c_str());
ImGui::SetCursorPos({ xLoc, yLoc + textSize.x });
// This has to be called from within a plot
//ImPlot::PlotText(modNames[i].c_str(), xLoc, yLoc + textSize.x, { 0, 0 }, ImPlotTextFlags_Vertical);
drawRotatedText(modNames[i], IM_PI * 1.5, BB_L);
xLoc += xStep;
}
ImGui::EndChild();
ImGui::End();
}
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.