mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 08:47:28 +01:00
Additional checks have been added to ensure that the first move of the cursor from the center of the graph would not cause the window to pan up or down. Change-Id: I44d7e86223941076cbf03db7a221e9c0427a64e4
224 lines
8.8 KiB
C++
224 lines
8.8 KiB
C++
#include "interactive_curve_view_range.h"
|
|
#include <ion.h>
|
|
#include <cmath>
|
|
#include <stddef.h>
|
|
#include <assert.h>
|
|
#include <poincare/preferences.h>
|
|
#include <algorithm>
|
|
|
|
using namespace Poincare;
|
|
|
|
namespace Shared {
|
|
|
|
uint32_t InteractiveCurveViewRange::rangeChecksum() {
|
|
float data[5] = {xMin(), xMax(), yMin(), yMax(), m_yAuto ? 1.0f : 0.0f};
|
|
size_t dataLengthInBytes = 5*sizeof(float);
|
|
assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4
|
|
return Ion::crc32Word((uint32_t *)data, dataLengthInBytes/sizeof(uint32_t));
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setYAuto(bool yAuto) {
|
|
m_yAuto = yAuto;
|
|
notifyRangeChange();
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setXMin(float xMin) {
|
|
MemoizedCurveViewRange::protectedSetXMin(xMin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
notifyRangeChange();
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setXMax(float xMax) {
|
|
MemoizedCurveViewRange::protectedSetXMax(xMax, k_lowerMaxFloat, k_upperMaxFloat);
|
|
notifyRangeChange();
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setYMin(float yMin) {
|
|
MemoizedCurveViewRange::protectedSetYMin(yMin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setYMax(float yMax) {
|
|
MemoizedCurveViewRange::protectedSetYMax(yMax, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
|
|
void InteractiveCurveViewRange::zoom(float ratio, float x, float y) {
|
|
float xMi = xMin();
|
|
float xMa = xMax();
|
|
float yMi = yMin();
|
|
float yMa = yMax();
|
|
if (ratio*std::fabs(xMa-xMi) < Range1D::k_minFloat || ratio*std::fabs(yMa-yMi) < Range1D::k_minFloat) {
|
|
return;
|
|
}
|
|
float centerX = std::isnan(x) || std::isinf(x) ? xCenter() : x;
|
|
float centerY = std::isnan(y) || std::isinf(y) ? yCenter() : y;
|
|
float newXMin = centerX*(1.0f-ratio)+ratio*xMi;
|
|
float newXMax = centerX*(1.0f-ratio)+ratio*xMa;
|
|
m_yAuto = false;
|
|
if (!std::isnan(newXMin) && !std::isnan(newXMax)) {
|
|
m_xRange.setMax(newXMax, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
float newYMin = centerY*(1.0f-ratio)+ratio*yMi;
|
|
float newYMax = centerY*(1.0f-ratio)+ratio*yMa;
|
|
if (!std::isnan(newYMin) && !std::isnan(newYMax)) {
|
|
m_yRange.setMax(newYMax, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
}
|
|
|
|
void InteractiveCurveViewRange::panWithVector(float x, float y) {
|
|
m_yAuto = false;
|
|
if (clipped(xMin() + x, false) != xMin() + x || clipped(xMax() + x, true) != xMax() + x || clipped(yMin() + y, false) != yMin() + y || clipped(yMax() + y, true) != yMax() + y || std::isnan(clipped(xMin() + x, false)) || std::isnan(clipped(xMax() + x, true)) || std::isnan(clipped(yMin() + y, false)) || std::isnan(clipped(yMax() + y, true))) {
|
|
return;
|
|
}
|
|
m_xRange.setMax(xMax()+x, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMin(xMin() + x, k_lowerMaxFloat, k_upperMaxFloat);
|
|
m_yRange.setMax(yMax()+y, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMin(yMin() + y, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
|
|
void InteractiveCurveViewRange::roundAbscissa() {
|
|
// Set x range
|
|
float newXMin = std::round(xCenter()) - (float)Ion::Display::Width/2.0f;
|
|
float newXMax = std::round(xCenter()) + (float)Ion::Display::Width/2.0f-1.0f;
|
|
if (std::isnan(newXMin) || std::isnan(newXMax)) {
|
|
return;
|
|
}
|
|
m_xRange.setMax(newXMax, k_lowerMaxFloat, k_upperMaxFloat);
|
|
setXMin(newXMin);
|
|
}
|
|
|
|
void InteractiveCurveViewRange::normalize() {
|
|
/* We center the ranges on the current range center, and put each axis so that
|
|
* 1cm = 2 current units. */
|
|
|
|
m_yAuto = false;
|
|
|
|
float xRange = xMax() - xMin();
|
|
float yRange = yMax() - yMin();
|
|
float xyRatio = xRange/yRange;
|
|
|
|
const float unit = std::max(xGridUnit(), yGridUnit());
|
|
const float newXHalfRange = NormalizedXHalfRange(unit);
|
|
const float newYHalfRange = NormalizedYHalfRange(unit);
|
|
float normalizedXYRatio = newXHalfRange/newYHalfRange;
|
|
if (xyRatio < normalizedXYRatio) {
|
|
float newXRange = normalizedXYRatio * yRange;
|
|
assert(newXRange > xRange);
|
|
float delta = (newXRange - xRange) / 2.0f;
|
|
m_xRange.setMin(xMin() - delta, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMax(xMax()+delta, k_lowerMaxFloat, k_upperMaxFloat);
|
|
} else if (xyRatio > normalizedXYRatio) {
|
|
float newYRange = newYHalfRange/newXHalfRange * xRange;
|
|
assert(newYRange > yRange);
|
|
float delta = (newYRange - yRange) / 2.0f;
|
|
m_yRange.setMin(yMin() - delta, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMax(yMax()+delta, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setTrigonometric() {
|
|
m_yAuto = false;
|
|
// Set x range
|
|
float x = (Preferences::sharedPreferences()->angleUnit() == Preferences::AngleUnit::Degree) ? 600.0f : 10.5f;
|
|
m_xRange.setMax(x, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMin(-x, k_lowerMaxFloat, k_upperMaxFloat);
|
|
// Set y range
|
|
float y = 1.6f;
|
|
m_yRange.setMax(y, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMin(-y, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
|
|
void InteractiveCurveViewRange::setDefault() {
|
|
if (m_delegate == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Compute the interesting range
|
|
m_yAuto = false;
|
|
m_delegate->interestingRanges(this);
|
|
|
|
// Add margins
|
|
float xRange = xMax() - xMin();
|
|
float yRange = yMax() - yMin();
|
|
m_xRange.setMin(m_delegate->addMargin(xMin(), xRange, false, true), k_lowerMaxFloat, k_upperMaxFloat);
|
|
// Use MemoizedCurveViewRange::protectedSetXMax to update xGridUnit
|
|
MemoizedCurveViewRange::protectedSetXMax(m_delegate->addMargin(xMax(), xRange, false, false), k_lowerMaxFloat, k_upperMaxFloat);
|
|
m_yRange.setMin(m_delegate->addMargin(yMin(), yRange, true, true), k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMax(m_delegate->addMargin(yMax(), yRange, true, false), k_lowerMaxFloat, k_upperMaxFloat);
|
|
|
|
if (!m_delegate->defaultRangeIsNormalized()) {
|
|
return;
|
|
}
|
|
|
|
// Normalize the axes, so that a polar circle is displayed as a circle
|
|
normalize();
|
|
}
|
|
|
|
void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) {
|
|
if (std::isnan(position)) {
|
|
return;
|
|
}
|
|
if (axis == Axis::X) {
|
|
float range = xMax() - xMin();
|
|
if (std::fabs(position/range) > k_maxRatioPositionRange) {
|
|
range = Range1D::defaultRangeLengthFor(position);
|
|
}
|
|
m_xRange.setMax(position + range/2.0f, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMin(position - range/2.0f, k_lowerMaxFloat, k_upperMaxFloat);
|
|
} else {
|
|
m_yAuto = false;
|
|
float range = yMax() - yMin();
|
|
if (std::fabs(position/range) > k_maxRatioPositionRange) {
|
|
range = Range1D::defaultRangeLengthFor(position);
|
|
}
|
|
m_yRange.setMax(position + range/2.0f, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMin(position - range/2.0f, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
}
|
|
|
|
void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRatio, float leftMarginRatio, float pixelWidth) {
|
|
if (!std::isinf(x) && !std::isnan(x)) {
|
|
const float xRange = xMax() - xMin();
|
|
const float leftMargin = leftMarginRatio * xRange;
|
|
if (x < xMin() + leftMargin) {
|
|
m_yAuto = false;
|
|
/* The panning increment is a whole number of pixels so that the caching
|
|
* for cartesian functions is not invalidated. */
|
|
const float newXMin = std::floor((x - leftMargin - xMin()) / pixelWidth) * pixelWidth + xMin();
|
|
m_xRange.setMax(newXMin + xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMin(newXMin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
const float rightMargin = rightMarginRatio * xRange;
|
|
if (x > xMax() - rightMargin) {
|
|
m_yAuto = false;
|
|
const float newXMax = std::ceil((x + rightMargin - xMax()) / pixelWidth) * pixelWidth + xMax();
|
|
m_xRange.setMax(newXMax, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetXMin(xMax() - xRange, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
}
|
|
if (!std::isinf(y) && !std::isnan(y)) {
|
|
const float yRange = yMax() - yMin();
|
|
const float bottomMargin = bottomMarginRatio * yRange;
|
|
if (y < yMin() + bottomMargin) {
|
|
m_yAuto = false;
|
|
const float newYMin = y - bottomMargin;
|
|
m_yRange.setMax(newYMin + yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMin(newYMin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
const float topMargin = topMarginRatio * yRange;
|
|
if (y > yMax() - topMargin) {
|
|
m_yAuto = false;
|
|
m_yRange.setMax(y + topMargin, k_lowerMaxFloat, k_upperMaxFloat);
|
|
MemoizedCurveViewRange::protectedSetYMin(yMax() - yRange, k_lowerMaxFloat, k_upperMaxFloat);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InteractiveCurveViewRange::notifyRangeChange() {
|
|
if (m_delegate) {
|
|
m_delegate->didChangeRange(this);
|
|
}
|
|
}
|
|
|
|
}
|