kate-deft/src/radial/radialmodel.cpp

120 lines
3.3 KiB
C++

/*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "radialmodel.h"
#include <QtMath>
namespace katecustom
{
RadialModel::RadialModel(RadialNode root)
: m_root(std::move(root))
{
}
const RadialNode &RadialModel::nodeAtPath() const
{
const RadialNode *node = &m_root;
for (int idx : m_path) {
node = &node->children.at(idx);
}
return *node;
}
const QList<RadialNode> &RadialModel::currentSlices() const
{
return nodeAtPath().children;
}
void RadialModel::sliceAngles(int index, int sliceCount, double &startAngle, double &endAngle) const
{
// Even wedges starting at the top (0 = up), going clockwise. Each slice is
// centred on its nominal direction, so wedge i spans [i-0.5, i+0.5) steps.
const double step = (2.0 * M_PI) / static_cast<double>(sliceCount);
startAngle = (static_cast<double>(index) - 0.5) * step;
endAngle = (static_cast<double>(index) + 0.5) * step;
}
int RadialModel::sliceAt(const QPointF &point, const RadialLayout &layout) const
{
const int count = currentSlices().size();
if (count == 0 || layout.sliceCount == 0) {
return InvalidSlice;
}
const double dx = point.x() - layout.center.x();
const double dy = point.y() - layout.center.y();
const double dist = std::sqrt(dx * dx + dy * dy);
// Inner dead zone: ambiguous direction, treat as "no slice".
if (dist < layout.innerRadius) {
return InvalidSlice;
}
// Angle measured from "up" (screen: -y), increasing clockwise (toward +x).
double angle = std::atan2(dx, -dy); // 0 = up, +pi/2 = right
if (angle < 0.0) {
angle += 2.0 * M_PI;
}
const double step = (2.0 * M_PI) / static_cast<double>(count);
// Shift by half a step so slice 0 is centred on "up".
int index = static_cast<int>(std::floor((angle + step / 2.0) / step));
index %= count;
return index;
}
QPointF RadialModel::sliceCenter(int index, const RadialLayout &layout) const
{
const int count = currentSlices().size();
if (count == 0) {
return layout.center;
}
const double step = (2.0 * M_PI) / static_cast<double>(count);
const double angle = static_cast<double>(index) * step; // 0 = up, CW
const double r = (layout.innerRadius + layout.outerRadius) / 2.0;
// Convert "up, clockwise" angle back to screen coordinates.
const double x = layout.center.x() + r * std::sin(angle);
const double y = layout.center.y() - r * std::cos(angle);
return QPointF(x, y);
}
bool RadialModel::descend(int index)
{
const QList<RadialNode> &slices = currentSlices();
if (index < 0 || index >= slices.size()) {
return false;
}
if (!slices.at(index).isBranch()) {
return false;
}
m_path.push_back(index);
return true;
}
bool RadialModel::ascend()
{
if (m_path.isEmpty()) {
return false;
}
m_path.removeLast();
return true;
}
RadialModel::Hit RadialModel::resolve(int index, QString &outActionId) const
{
const QList<RadialNode> &slices = currentSlices();
if (index < 0 || index >= slices.size()) {
return atRoot() ? Hit::Cancel : Hit::Ascend;
}
const RadialNode &node = slices.at(index);
if (node.isBranch()) {
return Hit::Descend;
}
outActionId = node.actionId;
return Hit::Activate;
}
} // namespace katecustom