diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index 50a2266a7..37ec62e23 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -87,8 +87,53 @@ void CurveView::setOkView(View * okView) { layoutSubviews(); } +/* We need to locate physical points on the screen more precisely than pixels, + * hence by floating-point coordinates. We agree that the coordinates of the + * center of a pixel corresponding to KDPoint(x,y) are precisely (x,y). In + * particular, the coordinates of a pixel's corners are not integers but half + * integers. Finally, a physical point with floating-point coordinates (x,y) + * is located in the pixel with coordinates (std::round(x), std::round(y)). + * + * Translating CurveViewRange coordinates to pixel coordinates on the screen: + * Along the horizontal axis + * Pixel / physical coordinate CurveViewRange coordinate + * 0 xMin() + * m_frame.width() - 1 xMax() + * Along the vertical axis + * Pixel / physical coordinate CurveViewRange coordinate + * 0 yMax() + * m_frame.height() - 1 yMin() + */ + const float CurveView::pixelWidth() const { - return (m_curveViewRange->xMax() - m_curveViewRange->xMin()) / m_frame.width(); + return (m_curveViewRange->xMax() - m_curveViewRange->xMin()) / (m_frame.width() - 1); +} + +const float CurveView::pixelHeight() const { + return (m_curveViewRange->yMax() - m_curveViewRange->yMin()) / (m_frame.height() - 1); +} + +float CurveView::pixelToFloat(Axis axis, KDCoordinate p) const { + return (axis == Axis::Horizontal) ? + m_curveViewRange->xMin() + p * pixelWidth() : + m_curveViewRange->yMax() - p * pixelHeight(); +} + +float CurveView::floatToPixel(Axis axis, float f) const { + float result = (axis == Axis::Horizontal) ? + (f - m_curveViewRange->xMin()) / pixelWidth() : + (m_curveViewRange->yMax() - f) / pixelHeight(); + /* Make sure that the returned value is between the maximum and minimum + * possible values of KDCoordinate. */ + if (result == NAN) { + return NAN; + } else if (result < KDCOORDINATE_MIN) { + return KDCOORDINATE_MIN; + } else if (result > KDCOORDINATE_MAX) { + return KDCOORDINATE_MAX; + } else { + return result; + } } void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor boldColor, KDColor lightColor) const { @@ -118,11 +163,6 @@ float CurveView::gridUnit(Axis axis) const { return (axis == Axis::Horizontal ? m_curveViewRange->xGridUnit() : m_curveViewRange->yGridUnit()); } -KDCoordinate CurveView::pixelLength(Axis axis) const { - assert(axis == Axis::Horizontal || axis == Axis::Vertical); - return (axis == Axis::Horizontal ? m_frame.width() : m_frame.height()); -} - int CurveView::numberOfLabels(Axis axis) const { float labelStep = 2.0f * gridUnit(axis); float minLabel = std::ceil(min(axis)/labelStep); @@ -130,28 +170,6 @@ int CurveView::numberOfLabels(Axis axis) const { return maxLabel - minLabel + 1; } -float CurveView::pixelToFloat(Axis axis, KDCoordinate p) const { - float pixelLen = pixelLength(axis); - float minA = min(axis); - KDCoordinate pixels = axis == Axis::Horizontal ? p : pixelLen - p; - return minA + pixels*(max(axis)-minA)/pixelLen; -} - -float CurveView::floatToPixel(Axis axis, float f) const { - float fraction = (f-min(axis))/(max(axis)-min(axis)); - fraction = axis == Axis::Horizontal ? fraction : 1.0f - fraction; - /* Fraction is a float that translates the relative position of f on the axis. - * When fraction is between 0 and 1, f is visible. Otherwise, f is out of the - * visible window. We need to clip fraction to avoid big float issue (often - * due to float to int transformation). However, we cannot clip fraction - * between 0 and 1 because drawing a sized stamp on the extern boarder of the - * window should still be visible. We thus arbitrarily clip fraction between - * -10 and 10. */ - fraction = fraction < -10.0f ? -10.0f : fraction; - fraction = fraction > 10.0f ? 10.0f : fraction; - return pixelLength(axis)*fraction; -} - void CurveView::computeLabels(Axis axis) { float step = gridUnit(axis); int axisLabelsCount = numberOfLabels(axis); diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 632b54cf5..347a55b7b 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -37,6 +37,7 @@ public: void setOkView(View * okView); void setForceOkDisplay(bool force) { m_forceOkDisplay = force; } const float pixelWidth() const; + const float pixelHeight() const; protected: CurveViewRange * curveViewRange() const { return m_curveViewRange; } void setCurveViewRange(CurveViewRange * curveViewRange); @@ -78,7 +79,6 @@ private: float min(Axis axis) const; float max(Axis axis) const; float gridUnit(Axis axis) const; - KDCoordinate pixelLength(Axis axis) const; virtual char * label(Axis axis, int index) const = 0; int numberOfLabels(Axis axis) const; /* Recursively join two dots (dichotomy). The method stops when the diff --git a/apps/shared/interactive_curve_view_controller.cpp b/apps/shared/interactive_curve_view_controller.cpp index 74ea2fd6f..547dbd638 100644 --- a/apps/shared/interactive_curve_view_controller.cpp +++ b/apps/shared/interactive_curve_view_controller.cpp @@ -276,7 +276,7 @@ int InteractiveCurveViewController::closestCurveIndexVertically(bool goingUp, in } float InteractiveCurveViewController::cursorBottomMarginRatio() { - return (curveView()->cursorView()->minimalSizeForOptimalDisplay().height()/2+estimatedBannerHeight())/k_viewHeight; + return (curveView()->cursorView()->minimalSizeForOptimalDisplay().height()/2+estimatedBannerHeight())/(k_viewHeight-1); } float InteractiveCurveViewController::estimatedBannerHeight() const { diff --git a/apps/shared/simple_interactive_curve_view_controller.h b/apps/shared/simple_interactive_curve_view_controller.h index 16eb6d627..ab6a40e53 100644 --- a/apps/shared/simple_interactive_curve_view_controller.h +++ b/apps/shared/simple_interactive_curve_view_controller.h @@ -20,10 +20,10 @@ public: bool textFieldDidAbortEditing(TextField * textField) override; bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; protected: - constexpr static float k_cursorRightMarginRatio = 0.04f; // (cursorWidth/2)/graphViewWidth - constexpr static float k_cursorLeftMarginRatio = 0.04f; // (cursorWidth/2)/graphViewWidth - virtual float cursorTopMarginRatio() { return 0.07f; } // (cursorHeight/2)/graphViewHeight - virtual float cursorBottomMarginRatio() = 0; // (cursorHeight/2+bannerHeight)/graphViewHeight + constexpr static float k_cursorRightMarginRatio = 0.04f; // (cursorWidth/2)/(graphViewWidth-1) + constexpr static float k_cursorLeftMarginRatio = 0.04f; // (cursorWidth/2)/(graphViewWidth-1) + virtual float cursorTopMarginRatio() { return 0.07f; } // (cursorHeight/2)/(graphViewHeight-1) + virtual float cursorBottomMarginRatio() = 0; // (cursorHeight/2+bannerHeight)/(graphViewHeight-1) constexpr static float k_numberOfCursorStepsInGradUnit = 5.0f; virtual bool handleZoom(Ion::Events::Event event); virtual bool handleLeftRightEvent(Ion::Events::Event event);