epezent / epezent/implot

How can I rotate x labels?

Open
#523 0 comments 0 reactions 0 assignees View on GitHub
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)

![image](https://github.com/epezent/implot/assets/51210/e4bbaabf-063e-4bec-894a-b0b27381a773)

// 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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.