mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[apps/shared][python/port] CurveView::drawArrow uses pixel computation
instead of float computation to avoid precision errors, by default the arrow size is decided in pixels.
This commit is contained in:
@@ -133,6 +133,22 @@ float CurveView::floatToPixel(Axis axis, float f) const {
|
||||
}
|
||||
}
|
||||
|
||||
float CurveView::floatLengthToPixelLength(Axis axis, float f) const {
|
||||
float dist = floatToPixel(axis, f) - floatToPixel(axis, 0.0f);
|
||||
return axis == Axis::Vertical ? - dist : dist;
|
||||
}
|
||||
|
||||
float CurveView::floatLengthToPixelLength(float dx, float dy) const {
|
||||
float dxPixel = floatLengthToPixelLength(Axis::Horizontal, dx);
|
||||
float dyPixel = floatLengthToPixelLength(Axis::Vertical, dy);
|
||||
return std::sqrt(dxPixel*dxPixel+dyPixel*dyPixel);
|
||||
}
|
||||
|
||||
float CurveView::pixelLengthToFloatLength(Axis axis, float f) const {
|
||||
f = axis == Axis::Vertical ? -f : f;
|
||||
return pixelToFloat(axis, floatToPixel(axis, 0.0f) + f);
|
||||
}
|
||||
|
||||
void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor boldColor, KDColor lightColor) const {
|
||||
Axis otherAxis = (axis == Axis::Horizontal) ? Axis::Vertical : Axis::Horizontal;
|
||||
/* We translate the pixel coordinates into floats, adding/subtracting 1 to
|
||||
@@ -449,7 +465,27 @@ void CurveView::drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor
|
||||
}
|
||||
|
||||
|
||||
void CurveView::drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, float arrowWidth, float tanAngle) const {
|
||||
void CurveView::drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, float arrowWith, float tanAngle) const {
|
||||
/* TODO: all computations are done in pixels because doing them in float
|
||||
* values led to approximation error (?). But this leads to useless back and
|
||||
* forth between float and pixels. Find a proper way to handle approximation
|
||||
* errors (if this was the problem). */
|
||||
|
||||
assert(tanAngle >= 0.0f);
|
||||
if (std::fabs(dx) < FLT_EPSILON && std::fabs(dy) < FLT_EPSILON) {
|
||||
// We can't draw an arrow without any orientation
|
||||
return;
|
||||
}
|
||||
|
||||
// Turn arrowWith in pixel length
|
||||
float pixelArrowWith = 8.0f; // default value in pixels
|
||||
if (arrowWith > 0.0f) {
|
||||
float dxdy = std::sqrt(dx*dx+dy*dy);
|
||||
float dxArrow = arrowWith*dx/dxdy;
|
||||
float dyArrow = arrowWith*dy/dxdy;
|
||||
pixelArrowWith = floatLengthToPixelLength(dxArrow, dyArrow);
|
||||
}
|
||||
|
||||
/* Let's call the following variables L and l:
|
||||
*
|
||||
* / |
|
||||
@@ -466,22 +502,22 @@ void CurveView::drawArrow(KDContext * ctx, KDRect rect, float x, float y, float
|
||||
*
|
||||
* ----- L -----
|
||||
*
|
||||
**/
|
||||
assert(tanAngle >= 0.0f);
|
||||
if (std::fabs(dx) < FLT_EPSILON && std::fabs(dy) < FLT_EPSILON) {
|
||||
// We can't draw an arrow without any orientation
|
||||
return;
|
||||
}
|
||||
float l = arrowWidth/2.0f;
|
||||
float L = l/tanAngle;
|
||||
float dx2dy2 = std::sqrt(dx*dx+dy*dy);
|
||||
*/
|
||||
|
||||
float arrow1dx = L*dx/dx2dy2 + l*dy/dx2dy2;
|
||||
float arrow1dy = L*dy/dx2dy2 - l*dx/dx2dy2;
|
||||
float l = pixelArrowWith/2.0;
|
||||
float L = l/tanAngle;
|
||||
|
||||
// We compute the arrow segments in pixels
|
||||
float dxPixel = floatLengthToPixelLength(Axis::Horizontal, dx);
|
||||
float dyPixel = floatLengthToPixelLength(Axis::Vertical, dy);
|
||||
float dx2dy2Pixel = floatLengthToPixelLength(dx, dy);
|
||||
|
||||
float arrow1dx = pixelLengthToFloatLength(Axis::Horizontal, L*dxPixel/dx2dy2Pixel + l*dyPixel/dx2dy2Pixel);
|
||||
float arrow1dy = pixelLengthToFloatLength(Axis::Vertical, L*dyPixel/dx2dy2Pixel - l*dxPixel/dx2dy2Pixel);
|
||||
drawSegment(ctx, rect, x, y, x - arrow1dx, y - arrow1dy, color, false);
|
||||
|
||||
float arrow2dx = L*dx/dx2dy2 - l*dy/dx2dy2;
|
||||
float arrow2dy = L*dy/dx2dy2 + l*dx/dx2dy2;
|
||||
float arrow2dx = pixelLengthToFloatLength(Axis::Horizontal, L*dxPixel/dx2dy2Pixel - l*dyPixel/dx2dy2Pixel);
|
||||
float arrow2dy = pixelLengthToFloatLength(Axis::Vertical, L*dyPixel/dx2dy2Pixel + l*dxPixel/dx2dy2Pixel);
|
||||
drawSegment(ctx, rect, x, y, x - arrow2dx, y - arrow2dy, color, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ protected:
|
||||
constexpr static int k_externRectMargin = 2;
|
||||
float pixelToFloat(Axis axis, KDCoordinate p) const;
|
||||
float floatToPixel(Axis axis, float f) const;
|
||||
float floatLengthToPixelLength(Axis axis, float f) const;
|
||||
float pixelLengthToFloatLength(Axis axis, float f) const;
|
||||
float floatLengthToPixelLength(float dx, float dy) const;
|
||||
void drawLine(KDContext * ctx, KDRect rect, Axis axis,
|
||||
float coordinate, KDColor color, KDCoordinate thickness = 1, KDCoordinate dashSize = -1) const {
|
||||
return drawHorizontalOrVerticalSegment(ctx, rect, axis, coordinate, -INFINITY, INFINITY, color,
|
||||
@@ -77,9 +80,9 @@ protected:
|
||||
void drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor color, Size size = Size::Small) const;
|
||||
/* 'drawArrow' draws the edge of an arrow pointing to (x,y) with the
|
||||
* orientation (dx,dy).
|
||||
* The parameters defining the shape of the arrow are the length of the base
|
||||
* of the arrow triangle - 'arrowWith' - and the tangent of the angle between
|
||||
* the segment and each wing of the arrow called 'tanAngle'.
|
||||
* The parameters defining the shape of the arrow are the length of
|
||||
* the base of the arrow triangle - 'pixelArrowWith' - and the tangent of the
|
||||
* angle between the segment and each wing of the arrow called 'tanAngle'.
|
||||
*
|
||||
* / |
|
||||
* / |
|
||||
@@ -95,11 +98,11 @@ protected:
|
||||
*
|
||||
* <--- L --->
|
||||
*
|
||||
* l = arrowWith
|
||||
* l = pixelArrowWith
|
||||
* tanAngle = tan(angle) = l/2L
|
||||
*/
|
||||
|
||||
void drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, float arrowWith = 4, float tanAngle = 1.0f/3.0f) const;
|
||||
void drawArrow(KDContext * ctx, KDRect rect, float x, float y, float dx, float dy, KDColor color, float arrowWith, float tanAngle = 1.0f/3.0f) const;
|
||||
void drawGrid(KDContext * ctx, KDRect rect) const;
|
||||
void drawAxes(KDContext * ctx, KDRect rect) const;
|
||||
void drawAxis(KDContext * ctx, KDRect rect, Axis axis) const;
|
||||
|
||||
@@ -106,7 +106,9 @@ mp_obj_t modpyplot_arrow(size_t n_args, const mp_obj_t *args, mp_map_t* kw_args)
|
||||
mp_map_elem_t * elem;
|
||||
// Setting arrow width
|
||||
elem = mp_map_lookup(kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_head_width), MP_MAP_LOOKUP);
|
||||
mp_obj_t arrowWidth = (elem == nullptr) ? mp_obj_new_float(0.003) : elem->value;
|
||||
/* Default head_width is 0.0f because we want a default width in pixel
|
||||
* coordinates which is handled by CurveView::drawArrow. */
|
||||
mp_obj_t arrowWidth = (elem == nullptr) ? mp_obj_new_float(0.0f) : elem->value;
|
||||
|
||||
// Setting arrow color
|
||||
KDColor color;
|
||||
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
KDColor m_color;
|
||||
};
|
||||
|
||||
void addSegment(mp_obj_t xStart, mp_obj_t yStart, mp_obj_t xEnd, mp_obj_t yEnd, KDColor c, mp_obj_t arrowWidth = mp_obj_new_float(0.0));
|
||||
void addSegment(mp_obj_t xStart, mp_obj_t yStart, mp_obj_t xEnd, mp_obj_t yEnd, KDColor c, mp_obj_t arrowWidth = mp_obj_new_float(NAN));
|
||||
Iterable<ListIterator<Segment>> segments() { return Iterable<ListIterator<Segment>>(m_segments); }
|
||||
|
||||
// Rect
|
||||
|
||||
@@ -46,7 +46,7 @@ void PlotView::traceSegment(KDContext * ctx, KDRect r, PlotStore::Segment segmen
|
||||
segment.xEnd(), segment.yEnd(),
|
||||
segment.color()
|
||||
);
|
||||
if (segment.arrowWidth() > 0.0f) {
|
||||
if (!std::isnan(segment.arrowWidth())) {
|
||||
float dx = segment.xEnd() - segment.xStart();
|
||||
float dy = segment.yEnd() - segment.yStart();
|
||||
drawArrow(ctx, r, segment.xEnd(), segment.yEnd(), dx, dy, segment.color(), segment.arrowWidth());
|
||||
|
||||
Reference in New Issue
Block a user