Skip to content

Commit

Permalink
fix: Smarter tick placement (#21)
Browse files Browse the repository at this point in the history
* Move more tick handling into the axes

* Smarter tic spacing

* Ensure plots are updated when axes are updated

* Much better linear tick points

* Better log axes

* Fix c++ formatting

* Fix QML formatting
  • Loading branch information
rprospero authored Jun 5, 2024
1 parent 1a5e73f commit df618aa
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 42 deletions.
3 changes: 1 addition & 2 deletions src/AxisModel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ Node {
model: axis.tickLabels

delegate: Node {
position: axis.direction ? Qt.vector3d(-root.scl.x + 2 * tickX * root.scl.x / (rep.count - 1), -root.scl.y - 210, 0) : Qt.vector3d(-root.scl.x - 30, -root.scl.y - 180 + 2 * tickY * root.scl.y / (rep.count - 1), 0)
position: axis.direction ? Qt.vector3d(-root.scl.x + tickX * root.scl.x, -root.scl.y - 210, 0) : Qt.vector3d(-root.scl.x - 30, -root.scl.y - 180 + tickY * root.scl.y, 0)

/* position: axis.direction ? Qt.vector3d(-root.scl.x + 2 * index * root.scl.x / (rep.count - 1), -root.scl.y - 210, 0) : Qt.vector3d(-root.scl.x - 30.0, -root.scl.y - 180 + 2 * index * root.scl.y / (rep.count - 1), 0) */
Item {
anchors.centerIn: parent
height: 400
Expand Down
15 changes: 15 additions & 0 deletions src/LineModel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,19 @@ Model {
baseColor: root.color
}
]

Connections {
function onDataChanged() {
plotLine.dataChanged();
}

target: xAxis
}
Connections {
function onDataChanged() {
plotLine.dataChanged();
}

target: yAxis
}
}
15 changes: 15 additions & 0 deletions src/ScatterModel.qml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,19 @@ Model {
baseColor: root.color
}
]

Connections {
function onDataChanged() {
plotLine.dataChanged();
}

target: xAxis
}
Connections {
function onDataChanged() {
plotLine.dataChanged();
}

target: yAxis
}
}
42 changes: 35 additions & 7 deletions src/axis.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#include "axis.h"
#include <algorithm>

#include <cmath>
#include <iostream>

Axis::Axis() : direction_(false), minimum_(-1), maximum_(1), thickness_(0.001), tickLabels_(*this)
Axis::Axis() : minimum_(-1), maximum_(1), direction_(false), thickness_(0.001), tickLabels_(*this)
{
updateData();
connect(this, &Axis::dataChanged, this, &Axis::updateData);
Expand All @@ -15,14 +16,42 @@ bool Axis::direction() const { return direction_; }
double Axis::minimum() const { return minimum_; }
double Axis::maximum() const { return maximum_; }

int Axis::tickCount() const { return tickLabels_.tickCount(); }
int Axis::tickCount() const { return tics_.size(); }

void Axis::setTickCount(const int count) { tickLabels_.setTickCount(count); }
void Axis::updateTicks_()
{

auto full_diff = maximum_ - minimum_;
auto diff = pow(10.0, floor(log(full_diff) / log(10.0)));
while (full_diff / diff < 4)
{
diff /= 2;
}
while (full_diff / diff > 9)
{
diff *= 2;
}
tics_.clear();

auto current = floor(minimum_ / diff) * diff;

while (current < minimum_)
current += diff;

while (current <= maximum_)
{
tics_.emplace_back(current);
current += diff;
}
}

void Axis::updateData()
{
clear();

updateTicks_();
tickLabels_.reset();

int stride = 3 * sizeof(float);

QByteArray vertexData(6 * stride, Qt::Initialization::Uninitialized);
Expand Down Expand Up @@ -92,7 +121,6 @@ std::vector<float> Axis::convert(QList<double> points) const
return result;
}

double Axis::tick(int index, int count) const
{
return minimum_ + (double)index / ((double)count - 1.0) * (maximum_ - minimum_);
}
double Axis::tick(int index) const { return tics_[index]; }

double Axis::tickCoord(int index) const { return 2.0 * (tics_[index] - minimum_) / (maximum_ - minimum_); }
13 changes: 9 additions & 4 deletions src/axis.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Axis : public QQuick3DGeometry
Q_PROPERTY(double maximum MEMBER maximum_ NOTIFY dataChanged)
Q_PROPERTY(bool direction MEMBER direction_ NOTIFY dataChanged)
Q_PROPERTY(AxisTickLabels *tickLabels READ tickLabels NOTIFY dataChanged)
Q_PROPERTY(int tickCount READ tickCount WRITE setTickCount)
Q_PROPERTY(int tickCount READ tickCount NOTIFY dataChanged)

public:
Axis();
Expand All @@ -23,15 +23,20 @@ class Axis : public QQuick3DGeometry
double minimum() const;
double maximum() const;
int tickCount() const;
void setTickCount(const int count);
virtual double tick(int index, int count) const;
double tick(int index) const;
virtual double tickCoord(int index) const;

Q_SIGNALS:
void dataChanged();

protected:
std::vector<double> tics_;
double minimum_, maximum_;

private:
virtual void updateTicks_();
void updateData();
bool direction_;
double minimum_, maximum_, thickness_;
double thickness_;
AxisTickLabels tickLabels_;
};
25 changes: 11 additions & 14 deletions src/axisTickLabels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,20 @@
#include "axis.h"
#include <iostream>

AxisTickLabels::AxisTickLabels(Axis &parent) : parent_(parent), N_(5) {}
AxisTickLabels::AxisTickLabels(Axis &parent) : parent_(parent) {}

int AxisTickLabels::tickCount() const { return N_; }

void AxisTickLabels::setTickCount(const int count)
{
beginResetModel();
N_ = count;
endResetModel();
}

int AxisTickLabels::rowCount([[maybe_unused]] const QModelIndex &parent) const { return N_; }
int AxisTickLabels::rowCount([[maybe_unused]] const QModelIndex &parent) const { return parent_.tickCount(); }

QVariant AxisTickLabels::data(const QModelIndex &index, int role) const
{
switch (role)
{
case Qt::UserRole:
return QString("%1").arg(parent_.tick(index.row(), N_));
return QString("%1").arg(parent_.tick(index.row()));
case (Qt::UserRole + 1):
return parent_.direction() ? index.row() : 0;
return parent_.direction() ? parent_.tickCoord(index.row()) : 0;
case (Qt::UserRole + 2):
return parent_.direction() ? 0 : index.row();
return parent_.direction() ? 0 : parent_.tickCoord(index.row());
default:
return index.row();
}
Expand All @@ -38,3 +29,9 @@ QHash<int, QByteArray> AxisTickLabels::roleNames() const
roles[Qt::UserRole + 2] = "tickY";
return roles;
}

void AxisTickLabels::reset()
{
beginResetModel();
endResetModel();
}
2 changes: 1 addition & 1 deletion src/axisTickLabels.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class AxisTickLabels : public QAbstractListModel
QHash<int, QByteArray> roleNames() const override;
int tickCount() const;
void setTickCount(const int count);
void reset();

private:
Axis &parent_;
int N_;
};
50 changes: 46 additions & 4 deletions src/logAxis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <cmath>
#include <iostream>
#include <vector>

LogAxis::LogAxis() : Axis() {}

Expand All @@ -19,10 +20,51 @@ std::vector<float> LogAxis::convert(QList<double> points) const
return result;
}

double LogAxis::tick(int index, int count) const
void LogAxis::updateTicks_()
{
auto min = log(minimum());
auto max = log(maximum());
std::vector<double> subTicks;
tics_.clear();

int orders = ceil(log(maximum_ / minimum_) / log(10.0));

switch (orders)
{
case 0:
case 1:
subTicks = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
break;
case 2:
subTicks = {1.0, 2.0, 3.0, 5.0, 8.0};
break;
case 3:
case 4:
subTicks = {1.0, 2.0, 5.0};
break;
case 5:
subTicks = {1.0, 3.0};
break;
default:
subTicks = {1.0};
}

double current = pow(10.0, floor(log(minimum_) / log(10.0)));

while (current <= maximum_)
{
for (auto sub : subTicks)
{
if (sub * current >= minimum_ && sub * current <= maximum_)
tics_.emplace_back(sub * current);
}
current *= 10.0;
}
}

double LogAxis::tickCoord(int index) const
{
double actual = log10(tics_[index]);
double min = log10(minimum_);
double max = log10(maximum_);

return exp(min + (double)index / ((double)count - 1.0) * (max - min));
return 2.0 * (actual - min) / (max - min);
}
5 changes: 4 additions & 1 deletion src/logAxis.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ class LogAxis : public Axis
public:
LogAxis();
std::vector<float> convert(QList<double> values) const override;
double tick(int index, int count) const override;
double tickCoord(int index) const override;

private:
void updateTicks_() override;

Q_SIGNALS:
void dataChanged();
Expand Down
26 changes: 17 additions & 9 deletions src/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,9 @@ ApplicationWindow {
id: xAxis

direction: true
maximum: 1.0
minimum: -1.0
maximum: yMax.value
minimum: yMin.value
thickness: 0.01
tickCount: ticCount.value
}
}
AxisModel {
Expand All @@ -67,7 +66,6 @@ ApplicationWindow {
maximum: 1.0
minimum: 0.01
thickness: 0.01
tickCount: ticCount.value
}
}
}
Expand Down Expand Up @@ -147,14 +145,24 @@ ApplicationWindow {
onMoved: renderButton.onClicked()
}
Label {
text: "Tic Count"
text: "Y Min"
}
SpinBox {
id: ticCount
id: yMin

from: 2
to: 10
value: 5
from: -30
to: yMax.value
value: -1
}
Label {
text: "Y Max"
}
SpinBox {
id: yMax

from: 0
to: 30
value: 1
}
ColorDialog {
id: colorDialog
Expand Down

0 comments on commit df618aa

Please sign in to comment.