diff options
Diffstat (limited to '')
-rw-r--r-- | src/display/control/canvas-item-curve.cpp | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/display/control/canvas-item-curve.cpp b/src/display/control/canvas-item-curve.cpp new file mode 100644 index 0000000..a99a154 --- /dev/null +++ b/src/display/control/canvas-item-curve.cpp @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * A class to represent a single Bezier control curve, either a line or a cubic Bezier. + */ + +/* + * Author: + * Tavmjong Bah + * + * Copyright (C) 2020 Tavmjong Bah + * + * Rewrite of SPCtrlLine and SPCtrlCurve + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <2geom/bezier-curve.h> + +#include "canvas-item-curve.h" + +#include "color.h" // SP_RGBA_x_F + +#include "ui/widget/canvas.h" + +namespace Inkscape { + +/** + * Create an null control curve. + */ +CanvasItemCurve::CanvasItemCurve(CanvasItemGroup *group) + : CanvasItem(group) +{ + _name = "CanvasItemCurve:Null"; + _pickable = false; // For now, nobody gets events from this class! +} + +/** + * Create a linear control curve. Points are in document coordinates. + */ +CanvasItemCurve::CanvasItemCurve(CanvasItemGroup *group, Geom::Point const &p0, Geom::Point const &p1) + : CanvasItem(group) + , _curve(std::make_unique<Geom::LineSegment>(p0, p1)) +{ + _name = "CanvasItemCurve:Line"; + _pickable = false; // For now, nobody gets events from this class! + + request_update(); +} + +/** + * Create a cubic Bezier control curve. Points are in document coordinates. + */ +CanvasItemCurve::CanvasItemCurve(CanvasItemGroup *group, + Geom::Point const &p0, Geom::Point const &p1, + Geom::Point const &p2, Geom::Point const &p3) + : CanvasItem(group) + , _curve(std::make_unique<Geom::CubicBezier>(p0, p1, p2, p3)) +{ + _name = "CanvasItemCurve:CubicBezier"; + _pickable = false; // For now, nobody gets events from this class! + + request_update(); +} + +/** + * Set a linear control curve. Points are in document coordinates. + */ +void CanvasItemCurve::set_coords(Geom::Point const &p0, Geom::Point const &p1) +{ + _name = "CanvasItemCurve:Line"; + _curve = std::make_unique<Geom::LineSegment>(p0, p1); + + request_update(); +} + +/** + * Set a cubic Bezier control curve. Points are in document coordinates. + */ +void CanvasItemCurve::set_coords(Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2, Geom::Point const &p3) +{ + _name = "CanvasItemCurve:CubicBezier"; + _curve = std::make_unique<Geom::CubicBezier>(p0, p1, p2, p3); + + request_update(); +} + +/** + * Set stroke width. + */ +void CanvasItemCurve::set_width(int w) +{ + width = w; + + request_update(); +} + +/** + * Set background stroke alpha. + */ +void CanvasItemCurve::set_bg_alpha(float alpha) +{ + bg_alpha = alpha; + + request_update(); +} + +/** + * Returns distance between point in canvas units and nearest point on curve. + */ +double CanvasItemCurve::closest_distance_to(Geom::Point const &p) +{ + double d = Geom::infinity(); + if (_curve) { + Geom::BezierCurve curve = *_curve; + curve *= _affine; // Document to canvas. + Geom::Point n = curve.pointAt(curve.nearestTime(p)); + d = Geom::distance(p, n); + } + return d; +} + +/** + * Returns true if point p (in canvas units) is within tolerance (canvas units) distance of curve. + */ +bool CanvasItemCurve::contains(Geom::Point const &p, double tolerance) +{ + return closest_distance_to(p) <= tolerance; +} + +/** + * Update and redraw control curve. + */ +void CanvasItemCurve::update(Geom::Affine const &affine) +{ + if (_affine == affine && !_need_update) { + // Nothing to do. + return; + } + + if (!_curve) { + return; // No curve! See node.h. + } + + // Queue redraw of old area (erase previous content). + request_redraw(); // This is actually never useful as curves are always deleted + // and recreated when a node is moved! But keep it in case we + // change that. CHECK + // Get new bounds + _affine = affine; + + // Trade off between updating a larger area (typically twice for Beziers?) vs computation time for bounds. + _bounds = _curve->boundsExact(); // Document units. + _bounds *= _affine; // Document to canvas. + _bounds.expandBy(2); // Room for stroke. + + // Queue redraw of new area + request_redraw(); + + _need_update = false; +} + +/** + * Render curve to screen via Cairo. + */ +void CanvasItemCurve::render(Inkscape::CanvasItemBuffer *buf) +{ + if (!buf) { + std::cerr << "CanvasItemCurve::Render: No buffer!" << std::endl; + return; + } + + if (!_curve) { + // Curve not defined (see node.h). + return; + } + + if (!_visible) { + // Hidden + return; + } + + if (_curve->isDegenerate()) { + // Nothing to render! + return; + } + + Geom::BezierCurve curve = *_curve; + curve *= _affine; // Document to canvas. + curve *= Geom::Translate(-buf->rect.min()); // Canvas to screen. + + buf->cr->save(); + + buf->cr->begin_new_path(); + + if (curve.size() == 2) { + // Line + buf->cr->move_to(curve[0].x(), curve[0].y()); + buf->cr->line_to(curve[1].x(), curve[1].y()); + } else { + // Curve + buf->cr->move_to(curve[0].x(), curve[0].y()); + buf->cr->curve_to(curve[1].x(), curve[1].y(), curve[2].x(), curve[2].y(), curve[3].x(), curve[3].y()); + } + + buf->cr->set_source_rgba(1.0, 1.0, 1.0, bg_alpha); + buf->cr->set_line_width(background_width); + buf->cr->stroke_preserve(); + + buf->cr->set_source_rgba(SP_RGBA32_R_F(_stroke), SP_RGBA32_G_F(_stroke), + SP_RGBA32_B_F(_stroke), SP_RGBA32_A_F(_stroke)); + buf->cr->set_line_width(width); + buf->cr->stroke(); + + // Uncomment to show bounds + // Geom::Rect bounds = _bounds; + // bounds.expandBy(-1); + // bounds -= buf->rect.min(); + // buf->cr->set_source_rgba(1.0, 0.0, 0.0, 1.0); + // buf->cr->rectangle(bounds.min().x(), bounds.min().y(), bounds.width(), bounds.height()); + // buf->cr->stroke(); + + buf->cr->restore(); +} + +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |