infinipaint/src/GUIStuff/Elements/RotateWheel.cpp

138 lines
5.5 KiB
C++
Raw Normal View History

2025-09-28 16:32:57 +03:00
#include "RotateWheel.hpp"
#include "Helpers/ConvertVec.hpp"
#include "../GUIManager.hpp"
2025-09-28 16:32:57 +03:00
namespace GUIStuff {
constexpr float WHEEL_WIDTH = 13.0f;
2025-09-28 16:32:57 +03:00
constexpr double ROTATE_BAR_SNAP_DISTANCE = 0.3;
constexpr double ROTATE_BAR_SNAP_DISTRIBUTION = std::numbers::pi * 0.25;
RotateWheel::RotateWheel(GUIManager& gui):
Element(gui) {}
2025-09-28 16:32:57 +03:00
void RotateWheel::layout(const Clay_ElementId& id, double* rotateAngle, const std::function<void()>& onChange) {
this->rotateAngle = rotateAngle;
this->onChange = onChange;
CLAY(id, {
.layout = {
2025-09-28 16:32:57 +03:00
.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}
},
2026-03-30 19:41:18 +03:00
.aspectRatio = {1.0f},
2025-09-28 16:32:57 +03:00
.custom = { .customData = this }
}) {
}
}
float RotateWheel::wheel_start() {
return wheel_end() - WHEEL_WIDTH;
}
float RotateWheel::wheel_end() {
return boundingBox.value().width() * 0.5f;
}
2026-04-02 12:46:16 +03:00
void RotateWheel::input_mouse_button_callback(const InputManager::MouseButtonCallbackArgs& button, bool mouseHovering) {
isHovering = mouseHovering;
isHeld = isHovering && button.button == InputManager::MouseButton::LEFT && button.down;
2026-04-02 12:46:16 +03:00
update_rotate_wheel_mouse_hover(button.pos);
2026-03-31 20:52:46 +03:00
isRotateBarHeld = isRotateBarHovered && isHeld;
2026-04-02 12:46:16 +03:00
update_rotate_wheel_mouse(button.pos);
Element::input_mouse_button_callback(button, mouseHovering);
}
2026-04-02 12:46:16 +03:00
void RotateWheel::input_mouse_motion_callback(const InputManager::MouseMotionCallbackArgs& motion, bool mouseHovering) {
2026-03-31 20:52:46 +03:00
isHovering = mouseHovering;
2026-04-02 12:46:16 +03:00
update_rotate_wheel_mouse_hover(motion.pos);
update_rotate_wheel_mouse(motion.pos);
Element::input_mouse_motion_callback(motion, mouseHovering);
}
2026-04-02 12:46:16 +03:00
void RotateWheel::update_rotate_wheel_mouse_hover(const Vector2f& p) {
float distFromCenter = vec_distance(p, boundingBox.value().center());
isRotateBarHovered = isHovering && distFromCenter > wheel_start() && distFromCenter < wheel_end();
}
void RotateWheel::update_rotate_wheel_mouse(const Vector2f& p) {
if(boundingBox.has_value()) {
Vector2f vecFromCenter = (p - boundingBox.value().center()).normalized();
if(isRotateBarHeld) {
gui.set_post_callback_func([&, vecFromCenter] {
auto& rotationAngle = *rotateAngle;
rotationAngle = std::atan2(vecFromCenter.y(), -vecFromCenter.x()) + std::numbers::pi;
if(isRotateBarHovered) { // If we're hovering over the rotation bar, we should try snapping to specific angles
for(double snapPos = 0.0; snapPos < std::numbers::pi * 2.0 + 0.001; snapPos += ROTATE_BAR_SNAP_DISTRIBUTION) {
if(std::fabs(rotationAngle - snapPos) < ROTATE_BAR_SNAP_DISTANCE) {
rotationAngle = snapPos;
if(rotationAngle >= std::numbers::pi * 2.0 - 0.001) // Set 2pi back to 0
rotationAngle = 0;
break;
}
}
}
2026-03-31 20:52:46 +03:00
onChange();
});
}
}
2025-09-28 16:32:57 +03:00
}
void RotateWheel::clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command, bool skiaAA) {
auto& bb = boundingBox.value();
2025-09-28 16:32:57 +03:00
canvas->save();
canvas->translate(bb.center().x(), bb.center().y());
draw_rotate_wheel(canvas, io, skiaAA);
2025-09-28 16:32:57 +03:00
canvas->restore();
}
void RotateWheel::draw_rotate_wheel(SkCanvas* canvas, UpdateInputData& io, bool skiaAA) {
2025-09-28 16:32:57 +03:00
float wheelStart = wheel_start();
float wheelEnd = wheel_end();
float rotateBarMiddleRadius = (wheelEnd + wheelStart) / 2.0f;
SkPaint rotateBarFill(io.theme->backColor2);
rotateBarFill.setStroke(true);
rotateBarFill.setStrokeWidth(wheelEnd - wheelStart);
canvas->drawCircle(0.0f, 0.0f, rotateBarMiddleRadius, rotateBarFill);
if(isRotateBarHovered) {
2025-09-28 16:32:57 +03:00
for(double snapPos = 0.0; snapPos < std::numbers::pi * 2.0; snapPos += ROTATE_BAR_SNAP_DISTRIBUTION) {
Vector2f lineDir{std::cos(snapPos), -std::sin(snapPos)};
SkPaint p(snapPos == 0.0 ? io.theme->fillColor1 : io.theme->frontColor2);
p.setAntiAlias(skiaAA);
2025-11-01 20:47:34 +03:00
p.setStrokeWidth(1.0f);
2025-09-28 16:32:57 +03:00
canvas->drawLine(lineDir.x() * wheelStart, lineDir.y() * wheelStart, lineDir.x() * wheelEnd, lineDir.y() * wheelEnd, p);
}
}
SkPaint rotateBarHolderFill;
rotateBarHolderFill.setAntiAlias(skiaAA);
2026-03-31 20:52:46 +03:00
if(isRotateBarHeld)
2025-09-28 16:32:57 +03:00
rotateBarHolderFill.setColor4f(io.theme->fillColor1);
2026-03-31 20:52:46 +03:00
else if(isRotateBarHovered)
2025-11-01 20:47:34 +03:00
rotateBarHolderFill.setColor4f(io.theme->fillColor2);
2025-09-28 16:32:57 +03:00
else
rotateBarHolderFill.setColor4f(io.theme->frontColor2);
float rotateBarHolderRadius = (wheelEnd - wheelStart) / 2.0f;
Vector2f rotateBarHolderPos{std::cos(*rotateAngle) * rotateBarMiddleRadius, -std::sin(*rotateAngle) * rotateBarMiddleRadius};
2025-09-28 16:32:57 +03:00
canvas->drawCircle(rotateBarHolderPos.x(), rotateBarHolderPos.y(), rotateBarHolderRadius, rotateBarHolderFill);
SkPaint rotateBarHolderOutline(io.theme->frontColor1);
rotateBarHolderOutline.setAntiAlias(skiaAA);
2025-09-28 16:32:57 +03:00
rotateBarHolderOutline.setStroke(true);
rotateBarHolderOutline.setStrokeWidth(2.0f);
canvas->drawCircle(rotateBarHolderPos.x(), rotateBarHolderPos.y(), rotateBarHolderRadius, rotateBarHolderOutline);
SkPaint rotateBarOutline(io.theme->frontColor2);
rotateBarOutline.setAntiAlias(skiaAA);
2025-09-28 16:32:57 +03:00
rotateBarOutline.setStroke(true);
2025-11-01 20:47:34 +03:00
rotateBarOutline.setStrokeWidth(1.0f);
2025-09-28 16:32:57 +03:00
canvas->drawCircle(0.0f, 0.0f, wheelStart, rotateBarOutline);
canvas->drawCircle(0.0f, 0.0f, wheelEnd, rotateBarOutline);
}
}