1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
// SPDX-License-Identifier: GPL-2.0-or-later
/** \file
* LPE "Ellipse through 5 points" implementation
*/
/*
* Authors:
* Theodore Janeczko
*
* Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
*
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
#include <2geom/ellipse.h>
#include <2geom/path-sink.h>
#include <glibmm/i18n.h>
#include "desktop.h"
#include "inkscape.h"
#include "message-stack.h"
#include "live_effects/lpe-ellipse_5pts.h"
namespace Inkscape::LivePathEffect {
LPEEllipse5Pts::LPEEllipse5Pts(LivePathEffectObject *lpeobject)
: Effect(lpeobject)
, _unit_circle{ // Run an IIFE to build the unit circle.
[]() {
Geom::PathBuilder builder;
builder.moveTo({1, 0});
builder.arcTo(1, 1, 0, true, true, {-1, 0});
builder.arcTo(1, 1, 0, true, true, { 1, 0});
builder.closePath();
return builder.peek();
}()}
{}
/** Flash a warning message on the status bar. */
void LPEEllipse5Pts::_flashWarning(char const *message)
{
auto &app = Inkscape::Application::instance();
if (auto desktop = app.active_desktop()) {
_clearWarning();
_error = desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, message);
}
}
/** Clear our warning from the status bar. */
void LPEEllipse5Pts::_clearWarning()
{
if (_error == INVALID) {
return;
}
auto &app = Inkscape::Application::instance();
if (auto desktop = app.active_desktop()) {
desktop->messageStack()->cancel(_error);
_error = INVALID;
}
}
/** Fit an ellipse to the first 5 nodes in the given PathVector. */
Geom::PathVector LPEEllipse5Pts::doEffect_path(Geom::PathVector const &path_in)
{
auto const &source = path_in[0];
if (source.size() < 4 /* For 5 nodes, we need at least 4 segments. */) {
_flashWarning(_("Five points required for constructing an ellipse"));
return path_in;
}
std::vector<Geom::Point> source_points;
source_points.reserve(5);
for (int i = 0; i < 5; i++) {
source_points.push_back(source.pointAt((Geom::Coord)i));
}
Geom::Ellipse ellipse;
bool fitting_fail = false;
try {
ellipse.fit(source_points);
} catch (Geom::RangeError &e) {
fitting_fail = true;
}
if (fitting_fail || ellipse.ray(Geom::X) == 0 || ellipse.ray(Geom::Y) == 0) {
_flashWarning(_("No unique ellipse passing through these points"));
return path_in;
}
_clearWarning();
// Transform the unit circle contour to the fitted ellipse.
return _unit_circle * ellipse.unitCircleTransform();
}
} //namespace Inkscape::LivePathEffect
/*
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 :
|