summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabiertxof <jtx@jtx>2017-05-06 18:07:14 +0000
committerJabiertxof <jtx@jtx>2017-05-06 18:07:14 +0000
commitcb39bc5cd6fbecf0f045d58f30dd4288c590a46a (patch)
tree9fbb940c15170a68e284dd8ca4a09f84eb8c311d /src
parentLPE widget refactor. Improvement to not update on same value (diff)
parentPre merge fixing (diff)
downloadinkscape-cb39bc5cd6fbecf0f045d58f30dd4288c590a46a.tar.gz
inkscape-cb39bc5cd6fbecf0f045d58f30dd4288c590a46a.zip
Merge pathVectorSatellites
(bzr r15666)
Diffstat (limited to 'src')
-rw-r--r--src/helper/CMakeLists.txt4
-rw-r--r--src/helper/geom-pathvectorsatellites.cpp244
-rw-r--r--src/helper/geom-pathvectorsatellites.h58
-rw-r--r--src/helper/geom-satellite.cpp245
-rw-r--r--src/helper/geom-satellite.h110
-rw-r--r--src/knotholder.h5
-rw-r--r--src/live_effects/CMakeLists.txt4
-rw-r--r--src/live_effects/effect.cpp2
-rw-r--r--src/live_effects/lpe-fillet-chamfer.cpp915
-rw-r--r--src/live_effects/lpe-fillet-chamfer.h64
-rw-r--r--src/live_effects/parameter/array.cpp44
-rw-r--r--src/live_effects/parameter/array.h39
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.cpp873
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.h123
-rw-r--r--src/live_effects/parameter/satellitesarray.cpp583
-rw-r--r--src/live_effects/parameter/satellitesarray.h114
-rw-r--r--src/ui/dialog/lpe-fillet-chamfer-properties.cpp101
-rw-r--r--src/ui/dialog/lpe-fillet-chamfer-properties.h48
-rw-r--r--src/ui/tool/path-manipulator.cpp8
19 files changed, 1973 insertions, 1611 deletions
diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt
index ff4760c24..92709e4e9 100644
--- a/src/helper/CMakeLists.txt
+++ b/src/helper/CMakeLists.txt
@@ -14,6 +14,8 @@ set(helper_SRC
geom.cpp
geom-nodetype.cpp
geom-pathstroke.cpp
+ geom-pathvectorsatellites.cpp
+ geom-satellite.cpp
gnome-utils.cpp
pixbuf-ops.cpp
png-write.cpp
@@ -32,6 +34,8 @@ set(helper_SRC
geom-curves.h
geom-nodetype.h
geom-pathstroke.h
+ geom-pathvectorsatellites.h
+ geom-satellite.h
geom.h
gnome-utils.h
mathfns.h
diff --git a/src/helper/geom-pathvectorsatellites.cpp b/src/helper/geom-pathvectorsatellites.cpp
new file mode 100644
index 000000000..e995c0a9b
--- /dev/null
+++ b/src/helper/geom-pathvectorsatellites.cpp
@@ -0,0 +1,244 @@
+/**
+ * \file
+ * \brief PathVectorSatellites a class to manage satellites -per node extra data- in a pathvector
+ */ /*
+ * Authors:
+ * Jabiertxof
+ * Nathan Hurst
+ * Johan Engelen
+ * Josh Andler
+ * suv
+ * Mc-
+ * Liam P. White
+ * Krzysztof Kosiński
+ * This code is in public domain
+ */
+
+#include <helper/geom-pathvectorsatellites.h>
+#include "util/units.h"
+
+Geom::PathVector PathVectorSatellites::getPathVector() const
+{
+ return _pathvector;
+}
+
+void PathVectorSatellites::setPathVector(Geom::PathVector pathv)
+{
+ _pathvector = pathv;
+}
+
+Satellites PathVectorSatellites::getSatellites()
+{
+ return _satellites;
+}
+
+void PathVectorSatellites::setSatellites(Satellites satellites)
+{
+ _satellites = satellites;
+}
+
+size_t PathVectorSatellites::getTotalSatellites()
+{
+ size_t counter = 0;
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ counter++;
+ }
+ }
+ return counter;
+}
+
+std::pair<size_t, size_t> PathVectorSatellites::getIndexData(size_t index)
+{
+ size_t counter = 0;
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ if (index == counter) {
+ return std::make_pair(i,j);
+ }
+ counter++;
+ }
+ }
+ return std::make_pair(0,0);
+}
+
+void PathVectorSatellites::setSelected(std::vector<size_t> selected)
+{
+ size_t counter = 0;
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ if (find (selected.begin(), selected.end(), counter) != selected.end()) {
+ _satellites[i][j].setSelected(true);
+ } else {
+ _satellites[i][j].setSelected(false);
+ }
+ counter++;
+ }
+ }
+}
+
+void PathVectorSatellites::updateSteps(size_t steps, bool apply_no_radius, bool apply_with_radius, bool only_selected)
+{
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ if ((!apply_no_radius && _satellites[i][j].amount == 0) ||
+ (!apply_with_radius && _satellites[i][j].amount != 0))
+ {
+ continue;
+ }
+ if (only_selected) {
+ if (_satellites[i][j].selected) {
+ _satellites[i][j].steps = steps;
+ }
+ } else {
+ _satellites[i][j].steps = steps;
+ }
+ }
+ }
+}
+
+void PathVectorSatellites::updateAmount(double radius, bool apply_no_radius, bool apply_with_radius, bool only_selected,
+ bool use_knot_distance, bool flexible)
+{
+ double power = 0;
+ if (!flexible) {
+ power = radius;
+ } else {
+ power = radius / 100;
+ }
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ boost::optional<size_t> previous_index = boost::none;
+ if (j == 0 && _pathvector[i].closed()) {
+ previous_index = _pathvector[i].size() - 1;
+ } else if (!_pathvector[i].closed() || j != 0) {
+ previous_index = j - 1;
+ }
+ if (!_pathvector[i].closed() && j == 0) {
+ _satellites[i][j].amount = 0;
+ continue;
+ }
+ if (_pathvector[i].size() == j) {
+ continue;
+ }
+ if ((!apply_no_radius && _satellites[i][j].amount == 0) ||
+ (!apply_with_radius && _satellites[i][j].amount != 0))
+ {
+ continue;
+ }
+
+ Geom::Point satellite_point = _pathvector[i].pointAt(j);
+ if (_satellites[i][j].selected || !only_selected) {
+ if (!use_knot_distance && !flexible) {
+ if (previous_index) {
+ _satellites[i][j].amount = _satellites[i][j].radToLen(power, _pathvector[i][*previous_index], _pathvector[i][j]);
+ if (power && !_satellites[i][j].amount) {
+ g_warning("Seems a too high radius value");
+ }
+ } else {
+ _satellites[i][j].amount = 0.0;
+ }
+ } else {
+ _satellites[i][j].amount = power;
+ }
+ }
+ }
+ }
+}
+
+void PathVectorSatellites::convertUnit(Glib::ustring in, Glib::ustring to, bool apply_no_radius, bool apply_with_radius)
+{
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ if (!_pathvector[i].closed() && j == 0) {
+ _satellites[i][j].amount = 0;
+ continue;
+ }
+ if (_pathvector[i].size() == j) {
+ continue;
+ }
+ if ((!apply_no_radius && _satellites[i][j].amount == 0) ||
+ (!apply_with_radius && _satellites[i][j].amount != 0))
+ {
+ continue;
+ }
+ _satellites[i][j].amount = Inkscape::Util::Quantity::convert(_satellites[i][j].amount, in.c_str(), to.c_str());
+ }
+ }
+}
+
+void PathVectorSatellites::updateSatelliteType(SatelliteType satellitetype, bool apply_no_radius, bool apply_with_radius,
+ bool only_selected)
+{
+ for (size_t i = 0; i < _satellites.size(); ++i) {
+ for (size_t j = 0; j < _satellites[i].size(); ++j) {
+ if ((!apply_no_radius && _satellites[i][j].amount == 0) ||
+ (!apply_with_radius && _satellites[i][j].amount != 0))
+ {
+ continue;
+ }
+ if (_pathvector[i].size() == j) {
+ if (!only_selected) {
+ _satellites[i][j].satellite_type = satellitetype;
+ }
+ continue;
+ }
+ if (only_selected) {
+ Geom::Point satellite_point = _pathvector[i].pointAt(j);
+ if (_satellites[i][j].selected) {
+ _satellites[i][j].satellite_type = satellitetype;
+ }
+ } else {
+ _satellites[i][j].satellite_type = satellitetype;
+ }
+ }
+ }
+}
+
+void PathVectorSatellites::recalculateForNewPathVector(Geom::PathVector const pathv, Satellite const S)
+{
+ Satellites satellites;
+ bool found = false;
+ //TODO evaluate fix on nodes at same position
+ size_t number_nodes = pathv.nodes().size();
+ size_t previous_number_nodes = _pathvector.nodes().size();
+ for (size_t i = 0; i < pathv.size(); i++) {
+ std::vector<Satellite> path_satellites;
+ for (size_t j = 0; j < pathv[i].size_closed(); j++) {
+ found = false;
+ for (size_t k = 0; k < _pathvector.size(); k++) {
+ for (size_t l = 0; l < _pathvector[k].size_closed(); l++) {
+ if (Geom::are_near(_pathvector[k][l].initialPoint(), pathv[i][j].initialPoint()))
+ {
+ path_satellites.push_back(_satellites[k][l]);
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+ }
+
+ if (!found && previous_number_nodes < number_nodes) {
+ path_satellites.push_back(S);
+ }
+ }
+ satellites.push_back(path_satellites);
+ }
+ setPathVector(pathv);
+ setSatellites(satellites);
+}
+
+/*
+ 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:fileencoding=utf-8:textwidth=99
+// :
diff --git a/src/helper/geom-pathvectorsatellites.h b/src/helper/geom-pathvectorsatellites.h
new file mode 100644
index 000000000..d86e6cb25
--- /dev/null
+++ b/src/helper/geom-pathvectorsatellites.h
@@ -0,0 +1,58 @@
+/**
+ * \file
+ * \brief PathVectorSatellites a class to manage satellites -per node extra data- in a pathvector
+ */ /*
+ * Authors:
+ * Jabiertxof
+ * Nathan Hurst
+ * Johan Engelen
+ * Josh Andler
+ * suv
+ * Mc-
+ * Liam P. White
+ * Krzysztof Kosiński
+ * This code is in public domain
+ */
+
+#ifndef SEEN_PATHVECTORSATELLITES_H
+#define SEEN_PATHVECTORSATELLITES_H
+
+#include <helper/geom-satellite.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+
+typedef std::vector<std::vector<Satellite> > Satellites;
+///@brief PathVectorSatellites a class to manage satellites in a pathvector
+class PathVectorSatellites {
+public:
+ Geom::PathVector getPathVector() const;
+ void setPathVector(Geom::PathVector pathv);
+ Satellites getSatellites();
+ void setSatellites(Satellites satellites);
+ size_t getTotalSatellites();
+ void setSelected(std::vector<size_t> selected);
+ void updateSteps(size_t steps, bool apply_no_radius, bool apply_with_radius, bool only_selected);
+ void updateAmount(double radius, bool apply_no_radius, bool apply_with_radius, bool only_selected,
+ bool use_knot_distance, bool flexible);
+ void convertUnit(Glib::ustring in, Glib::ustring to, bool apply_no_radius, bool apply_with_radius);
+ void updateSatelliteType(SatelliteType satellitetype, bool apply_no_radius, bool apply_with_radius, bool only_selected);
+ std::pair<size_t, size_t> getIndexData(size_t index);
+ void recalculateForNewPathVector(Geom::PathVector const pathv, Satellite const S);
+private:
+ Geom::PathVector _pathvector;
+ Satellites _satellites;
+};
+
+#endif //SEEN_PATHVECTORSATELLITES_H
+/*
+ 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:fileencoding=utf-8:textwidth=99
+// :
diff --git a/src/helper/geom-satellite.cpp b/src/helper/geom-satellite.cpp
new file mode 100644
index 000000000..8a92273d0
--- /dev/null
+++ b/src/helper/geom-satellite.cpp
@@ -0,0 +1,245 @@
+/**
+ * \file
+ * \brief Satellite a per node holder of data.
+ */ /*
+ * Authors:
+ * 2015 Jabier Arraiza Cenoz<jabier.arraiza@marker.es>
+ *
+ * This code is in public domain
+ */
+
+#include <helper/geom-satellite.h>
+#include <2geom/curve.h>
+#include <2geom/nearest-time.h>
+#include <2geom/path-intersection.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/ray.h>
+#include <boost/optional.hpp>
+//log cache
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <sys/time.h>
+#include <ctime>
+#endif
+
+///@brief Satellite a per node holder of data.
+Satellite::Satellite() {}
+
+
+Satellite::Satellite(SatelliteType satellite_type)
+ : satellite_type(satellite_type),
+ is_time(false),
+ selected(false),
+ has_mirror(false),
+ hidden(true),
+ amount(0.0),
+ angle(0.0),
+ steps(0)
+{}
+
+Satellite::~Satellite() {}
+
+///Calculate the time in curve_in with a size of A
+//TODO: find a better place to it
+double timeAtArcLength(double const A, Geom::Curve const &curve_in)
+{
+ if ( A == 0 || curve_in.isDegenerate()) {
+ return 0;
+ }
+
+ Geom::D2<Geom::SBasis> d2_in = curve_in.toSBasis();
+ double t = 0;
+ double length_part = curve_in.length();
+ if (A >= length_part || curve_in.isLineSegment()) {
+ if (length_part != 0) {
+ t = A / length_part;
+ }
+ } else if (!curve_in.isLineSegment()) {
+ std::vector<double> t_roots = roots(Geom::arcLengthSb(d2_in) - A);
+ if (!t_roots.empty()) {
+ t = t_roots[0];
+ }
+ }
+ return t;
+}
+
+///Calculate the size in curve_in with a point at A
+//TODO: find a better place to it
+double arcLengthAt(double const A, Geom::Curve const &curve_in)
+{
+ if ( A == 0 || curve_in.isDegenerate()) {
+ return 0;
+ }
+
+ double s = 0;
+ double length_part = curve_in.length();
+ if (A > length_part || curve_in.isLineSegment()) {
+ s = (A * length_part);
+ } else if (!curve_in.isLineSegment()) {
+ Geom::Curve *curve = curve_in.portion(0.0, A);
+ s = curve->length();
+ delete curve;
+ }
+ return s;
+}
+
+///Convert a arc radius of a fillet/chamfer to his satellite length -point position where fillet/chamfer knot be on original curve
+double Satellite::radToLen(
+ double const A, Geom::Curve const &curve_in,
+ Geom::Curve const &curve_out) const
+{
+ double len = 0;
+ Geom::D2<Geom::SBasis> d2_in = curve_in.toSBasis();
+ Geom::D2<Geom::SBasis> d2_out = curve_out.toSBasis();
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > offset_curve0 =
+ Geom::Piecewise<Geom::D2<Geom::SBasis> >(d2_in) +
+ rot90(unitVector(derivative(d2_in))) * (A);
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > offset_curve1 =
+ Geom::Piecewise<Geom::D2<Geom::SBasis> >(d2_out) +
+ rot90(unitVector(derivative(d2_out))) * (A);
+ Geom::Path p0 = path_from_piecewise(offset_curve0, 0.1)[0];
+ Geom::Path p1 = path_from_piecewise(offset_curve1, 0.1)[0];
+ Geom::Crossings cs = Geom::crossings(p0, p1);
+ if (cs.size() > 0) {
+ Geom::Point cp = p0(cs[0].ta);
+ double p0pt = nearest_time(cp, curve_out);
+ len = arcLengthAt(p0pt, curve_out);
+ } else {
+ if (A > 0) {
+ len = radToLen(A * -1, curve_in, curve_out);
+ }
+ }
+ return len;
+}
+
+///Convert a satelite length -point position where fillet/chamfer knot be on original curve- to a arc radius of fillet/chamfer
+double Satellite::lenToRad(
+ double const A, Geom::Curve const &curve_in,
+ Geom::Curve const &curve_out,
+ Satellite const previousSatellite) const
+{
+ double time_in = (previousSatellite).time(A, true, curve_in);
+ double time_out = timeAtArcLength(A, curve_out);
+ Geom::Point start_arc_point = curve_in.pointAt(time_in);
+ Geom::Point end_arc_point = curve_out.pointAt(time_out);
+ Geom::Curve *knot_curve1 = curve_in.portion(0, time_in);
+ Geom::Curve *knot_curve2 = curve_out.portion(time_out, 1);
+ Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(&*knot_curve1);
+ Geom::Ray ray1(start_arc_point, curve_in.pointAt(1));
+ if (cubic1) {
+ ray1.setPoints((*cubic1)[2], start_arc_point);
+ }
+ Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(&*knot_curve2);
+ Geom::Ray ray2(curve_out.pointAt(0), end_arc_point);
+ if (cubic2) {
+ ray2.setPoints(end_arc_point, (*cubic2)[1]);
+ }
+ bool ccw_toggle = cross(curve_in.pointAt(1) - start_arc_point,
+ end_arc_point - start_arc_point) < 0;
+ double distance_arc =
+ Geom::distance(start_arc_point, middle_point(start_arc_point, end_arc_point));
+ double angle = angle_between(ray1, ray2, ccw_toggle);
+ double divisor = std::sin(angle / 2.0);
+ if (divisor > 0) {
+ return distance_arc / divisor;
+ }
+ return 0;
+}
+
+///Get the time position of the satellite in curve_in
+double Satellite::time(Geom::Curve const &curve_in, bool inverse) const
+{
+ double t = amount;
+ if (!is_time) {
+ t = time(t, inverse, curve_in);
+ } else if (inverse) {
+ t = 1-t;
+ }
+ if (t > 1) {
+ t = 1;
+ }
+ return t;
+}
+
+///Get the time from a length A in other curve, a bolean inverse gived to reverse time
+double Satellite::time(double A, bool inverse,
+ Geom::Curve const &curve_in) const
+{
+ if (A == 0 && inverse) {
+ return 1;
+ }
+ if (A == 0 && !inverse) {
+ return 0;
+ }
+ if (!inverse) {
+ return timeAtArcLength(A, curve_in);
+ }
+ double length_part = curve_in.length();
+ A = length_part - A;
+ return timeAtArcLength(A, curve_in);
+}
+
+///Get the length of the satellite in curve_in
+double Satellite::arcDistance(Geom::Curve const &curve_in) const
+{
+ double s = amount;
+ if (is_time) {
+ s = arcLengthAt(s, curve_in);
+ }
+ return s;
+}
+
+///Get the point position of the satellite
+Geom::Point Satellite::getPosition(Geom::Curve const &curve_in, bool inverse) const
+{
+ double t = time(curve_in, inverse);
+ return curve_in.pointAt(t);
+}
+
+///Set the position of the satellite from a gived point P
+void Satellite::setPosition(Geom::Point const p, Geom::Curve const &curve_in, bool inverse)
+{
+ Geom::Curve * curve = const_cast<Geom::Curve *>(&curve_in);
+ if (inverse) {
+ curve = curve->reverse();
+ }
+ double A = Geom::nearest_time(p, *curve);
+ if (!is_time) {
+ A = arcLengthAt(A, *curve);
+ }
+ amount = A;
+}
+
+
+///Map a satellite type with gchar
+void Satellite::setSatelliteType(gchar const *A)
+{
+ std::map<std::string, SatelliteType> gchar_map_to_satellite_type =
+ boost::assign::map_list_of("F", FILLET)("IF", INVERSE_FILLET)("C", CHAMFER)("IC", INVERSE_CHAMFER)("KO", INVALID_SATELLITE);
+ std::map<std::string, SatelliteType>::iterator it = gchar_map_to_satellite_type.find(std::string(A));
+ if (it != gchar_map_to_satellite_type.end()) {
+ satellite_type = it->second;
+ }
+}
+
+///Map a gchar with satelliteType
+gchar const *Satellite::getSatelliteTypeGchar() const
+{
+ std::map<SatelliteType, gchar const *> satellite_type_to_gchar_map =
+ boost::assign::map_list_of(FILLET, "F")(INVERSE_FILLET, "IF")(CHAMFER, "C")(INVERSE_CHAMFER, "IC")(INVALID_SATELLITE, "KO");
+ return satellite_type_to_gchar_map.at(satellite_type);
+}
+
+/*
+ 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:fileencoding=utf-8:textwidth=99
+// :
diff --git a/src/helper/geom-satellite.h b/src/helper/geom-satellite.h
new file mode 100644
index 000000000..a4d63d66e
--- /dev/null
+++ b/src/helper/geom-satellite.h
@@ -0,0 +1,110 @@
+/**
+ * \file
+ * \brief Satellite a per node holder of data.
+ */ /*
+ * Authors:
+ * 2015 Jabier Arraiza Cenoz<jabier.arraiza@marker.es>
+ *
+ * This code is in public domain
+ */
+
+#ifndef SEEN_SATELLITE_H
+#define SEEN_SATELLITE_H
+
+#include <map>
+#include <boost/assign.hpp>
+#include <2geom/sbasis-geometric.h>
+#include "util/enums.h"
+
+
+enum SatelliteType {
+ FILLET = 0, //Fillet
+ INVERSE_FILLET, //Inverse Fillet
+ CHAMFER, //Chamfer
+ INVERSE_CHAMFER, //Inverse Chamfer
+ INVALID_SATELLITE // Invalid Satellite
+};
+/**
+ * @brief Satellite a per node holder of data.
+ */
+
+class Satellite {
+public:
+
+ Satellite();
+ Satellite(SatelliteType satellite_type);
+
+ virtual ~Satellite();
+ void setIsTime(bool set_is_time)
+ {
+ is_time = set_is_time;
+ }
+ void setSelected(bool set_selected)
+ {
+ selected = set_selected;
+ }
+ void setHasMirror(bool set_has_mirror)
+ {
+ has_mirror = set_has_mirror;
+ }
+ void setHidden(bool set_hidden)
+ {
+ hidden = set_hidden;
+ }
+ void setAmount(bool set_amount)
+ {
+ amount = set_amount;
+ }
+ void setAngle(bool set_angle)
+ {
+ angle = set_angle;
+ }
+ void setSteps(bool set_steps)
+ {
+ steps = set_steps;
+ }
+ double lenToRad(double const A, Geom::Curve const &curve_in,
+ Geom::Curve const &curve_out,
+ Satellite const previousSatellite) const;
+ double radToLen(double const A, Geom::Curve const &curve_in,
+ Geom::Curve const &curve_out) const;
+
+ double time(Geom::Curve const &curve_in, bool inverse = false) const;
+ double time(double A, bool inverse, Geom::Curve const &curve_in) const;
+ double arcDistance(Geom::Curve const &curve_in) const;
+
+ void setPosition(Geom::Point const p, Geom::Curve const &curve_in, bool inverse = false);
+ Geom::Point getPosition(Geom::Curve const &curve_in, bool inverse = false) const;
+
+ void setSatelliteType(gchar const *A);
+ gchar const *getSatelliteTypeGchar() const;
+ SatelliteType satellite_type;
+ //The value stored could be a time value of the satellite in the curve or a lenght of distance to the node from the satellite
+ //"is_time" tell is if is a time or lenght value
+ bool is_time;
+ bool selected;
+ bool has_mirror;
+ bool hidden;
+ //in "amount" we store the time or distance used in the satellite
+ double amount;
+ double angle;
+ size_t steps;
+};
+
+double timeAtArcLength(double const A, Geom::Curve const &curve_in);
+double arcLengthAt(double const A, Geom::Curve const &curve_in);
+
+#endif // SEEN_SATELLITE_H
+
+/*
+ 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:fileencoding=utf-8:textwidth=99
+// :
diff --git a/src/knotholder.h b/src/knotholder.h
index f1bacebe5..c8136da3f 100644
--- a/src/knotholder.h
+++ b/src/knotholder.h
@@ -30,7 +30,8 @@ class Node;
}
namespace LivePathEffect {
class PowerStrokePointArrayParamKnotHolderEntity;
-class FilletPointArrayParamKnotHolderEntity;
+class SatellitesArrayParam;
+class FilletChamferKnotHolderEntity;
}
}
@@ -63,7 +64,7 @@ public:
friend class Inkscape::UI::ShapeEditor; // FIXME why?
friend class Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity; // why?
- friend class Inkscape::LivePathEffect::FilletPointArrayParamKnotHolderEntity; // why?
+ friend class Inkscape::LivePathEffect::FilletChamferKnotHolderEntity; // why?
protected:
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 5ffccc7c0..f67c43db0 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -59,7 +59,6 @@ set(live_effects_SRC
parameter/array.cpp
parameter/bool.cpp
- parameter/filletchamferpointarray.cpp
parameter/item-reference.cpp
parameter/item.cpp
parameter/originalitem.cpp
@@ -70,6 +69,7 @@ set(live_effects_SRC
parameter/path.cpp
parameter/point.cpp
parameter/powerstrokepointarray.cpp
+ parameter/satellitesarray.cpp
parameter/random.cpp
parameter/text.cpp
parameter/fontbutton.cpp
@@ -144,7 +144,6 @@ set(live_effects_SRC
parameter/array.h
parameter/bool.h
parameter/enum.h
- parameter/filletchamferpointarray.h
parameter/item.h
parameter/item-reference.h
parameter/originalitem.h
@@ -155,6 +154,7 @@ set(live_effects_SRC
parameter/path.h
parameter/point.h
parameter/powerstrokepointarray.h
+ parameter/satellitesarray.h
parameter/random.h
parameter/text.h
parameter/fontbutton.h
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 0984a5681..467a65c7c 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -101,7 +101,6 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"},
{TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
{TEXT_LABEL, N_("Text label"), "text_label"},
- {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet_chamfer"},
#endif
/* 0.46 */
{BEND_PATH, N_("Bend"), "bend_path"},
@@ -142,6 +141,7 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{BOUNDING_BOX, N_("Bounding Box"), "bounding_box"},
/* 9.93 */
{MEASURE_LINE, N_("Measure Line"), "measure_line"},
+ {FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet_chamfer"},
};
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp
index 1e2df7dc8..e968a7822 100644
--- a/src/live_effects/lpe-fillet-chamfer.cpp
+++ b/src/live_effects/lpe-fillet-chamfer.cpp
@@ -4,86 +4,131 @@
*
* Copyright (C) 2014 Author(s)
*
- * Special thanks to Johan Engelen for the base of the effect -powerstroke-
- * Also to ScislaC for point me to the idea
- * Also su_v for his construvtive feedback and time
- * Also to Mc- (IRC nick) for his important contribution to find real time
- * values based on
- * and finaly to Liam P. White for his big help on coding, that save me a lot of hours
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include "live_effects/lpe-fillet-chamfer.h"
-
-#include <2geom/sbasis-to-bezier.h>
-#include <2geom/elliptical-arc.h>
+#include "live_effects/lpe-fillet-chamfer.h"
+#include "helper/geom.h"
#include "display/curve.h"
-#include "helper/geom-nodetype.h"
#include "helper/geom-curves.h"
-#include "helper/geom.h"
+#include "helper/geom-satellite.h"
+#include <2geom/elliptical-arc.h>
+#include "knotholder.h"
+#include <boost/optional.hpp>
-// for programmatically updating knots
-#include "ui/tools-switch.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
-using namespace Geom;
namespace Inkscape {
namespace LivePathEffect {
-static const Util::EnumData<FilletMethod> FilletMethodData[FM_END] = {
- { FM_AUTO, N_("Auto"), "auto" },
+static const Util::EnumData<Filletmethod> FilletmethodData[] = {
+ { FM_AUTO, N_("Auto"), "auto" },
{ FM_ARC, N_("Force arc"), "arc" },
{ FM_BEZIER, N_("Force bezier"), "bezier" }
};
-static const Util::EnumDataConverter<FilletMethod>
-FMConverter(FilletMethodData, FM_END);
-
-const double tolerance = 0.001;
-const double gapHelper = 0.00001;
-
-LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) :
- Effect(lpeobject),
- fillet_chamfer_values(_("Fillet point"), _("Fillet point"), "fillet_chamfer_values", &wr, this),
- hide_knots(_("Hide knots"), _("Hide knots"), "hide_knots", &wr, this, false),
- ignore_radius_0(_("Ignore 0 radius knots"), _("Ignore 0 radius knots"), "ignore_radius_0", &wr, this, false),
- only_selected(_("Change only selected nodes"), _("Change only selected nodes"), "only_selected", &wr, this, false),
- flexible(_("Flexible radius size (%)"), _("Flexible radius size (%)"), "flexible", &wr, this, false),
- use_knot_distance(_("Use knots distance instead radius"), _("Use knots distance instead radius"), "use_knot_distance", &wr, this, false),
- method(_("Method:"), _("Fillets methods"), "method", FMConverter, &wr, this, FM_AUTO),
- radius(_("Radius (unit or %):"), _("Radius, in unit or %"), "radius", &wr, this, 0.),
- chamfer_steps(_("Chamfer steps:"), _("Chamfer steps"), "chamfer_steps", &wr, this, 0),
-
- helper_size(_("Helper size with direction:"), _("Helper size with direction"), "helper_size", &wr, this, 0)
+static const Util::EnumDataConverter<Filletmethod> FMConverter(FilletmethodData, FM_END);
+
+LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject)
+ : Effect(lpeobject),
+ unit(_("Unit"), _("Unit"), "unit", &wr, this, "px"),
+ satellites_param("Satellites_param", "Satellites_param",
+ "satellites_param", &wr, this),
+ method(_("Method:"), _("Methods to calculate the fillet or chamfer"),
+ "method", FMConverter, &wr, this, FM_AUTO),
+ radius(_("Radius (unit or %):"), _("Radius, in unit or %"), "radius", &wr,
+ this, 0.0),
+ chamfer_steps(_("Chamfer steps:"), _("Chamfer steps"), "chamfer_steps",
+ &wr, this, 1),
+ flexible(_("Flexible radius size (%)"), _("Flexible radius size (%)"),
+ "flexible", &wr, this, false),
+ mirror_knots(_("Mirror Knots"), _("Mirror Knots"), "mirror_knots", &wr,
+ this, true),
+ only_selected(_("Change only selected nodes"),
+ _("Change only selected nodes"), "only_selected", &wr, this,
+ false),
+ use_knot_distance(_("Use knots distance instead radius"),
+ _("Use knots distance instead radius"),
+ "use_knot_distance", &wr, this, false),
+ hide_knots(_("Hide knots"), _("Hide knots"), "hide_knots", &wr, this,
+ false),
+ apply_no_radius(_("Apply changes if radius = 0"), _("Apply changes if radius = 0"), "apply_no_radius", &wr, this, true),
+ apply_with_radius(_("Apply changes if radius > 0"), _("Apply changes if radius > 0"), "apply_with_radius", &wr, this, true),
+ helper_size(_("Helper path size with direction to node:"),
+ _("Helper path size with direction to node"), "helper_size", &wr, this, 0),
+ _pathvector_satellites(NULL),
+ _degenerate_hide(false)
{
- registerParameter(&fillet_chamfer_values);
+ registerParameter(&satellites_param);
+ registerParameter(&unit);
registerParameter(&method);
registerParameter(&radius);
registerParameter(&chamfer_steps);
registerParameter(&helper_size);
registerParameter(&flexible);
registerParameter(&use_knot_distance);
- registerParameter(&ignore_radius_0);
+ registerParameter(&mirror_knots);
+ registerParameter(&apply_no_radius);
+ registerParameter(&apply_with_radius);
registerParameter(&only_selected);
registerParameter(&hide_knots);
- radius.param_set_range(0., infinity());
+ radius.param_set_range(0.0, Geom::infinity());
radius.param_set_increments(1, 1);
radius.param_set_digits(4);
radius.param_overwrite_widget(true);
chamfer_steps.param_set_range(1, 999);
chamfer_steps.param_set_increments(1, 1);
chamfer_steps.param_set_digits(0);
- chamfer_steps.param_overwrite_widget(true);
- helper_size.param_set_range(0, infinity());
+ helper_size.param_set_range(0, 999);
helper_size.param_set_increments(5, 5);
helper_size.param_set_digits(0);
- helper_size.param_overwrite_widget(true);
- fillet_chamfer_values.set_chamfer_steps(3);
+ _provides_knotholder_entities = true;
}
-LPEFilletChamfer::~LPEFilletChamfer() {}
+void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem)
+{
+ SPLPEItem *splpeitem = const_cast<SPLPEItem *>(lpeItem);
+ SPShape *shape = dynamic_cast<SPShape *>(splpeitem);
+ if (shape) {
+ Geom::PathVector const pathv = pathv_to_linear_and_cubic_beziers(shape->getCurve()->get_pathvector());
+ Satellites satellites;
+ for (Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
+ if (path_it->empty()) {
+ continue;
+ }
+ std::vector<Satellite> subpath_satellites;
+ for (Geom::Path::const_iterator curve_it = path_it->begin(); curve_it != path_it->end(); ++curve_it) {
+ //Maybe we want this satellites...
+ //if (curve_it->isDegenerate()) {
+ // continue
+ //}
+ Satellite satellite(FILLET);
+ satellite.setSteps(chamfer_steps);
+ subpath_satellites.push_back(satellite);
+ }
+ //we add the last satellite on open path because _pathvector_satellites is related to nodes, not curves
+ //so maybe in the future we can need this last satellite in other effects
+ //dont remove for this effect because _pathvector_satellites class has methods when the path is modiffied
+ //and we want one method for all uses
+ if (!path_it->closed()) {
+ Satellite satellite(FILLET);
+ satellite.setSteps(chamfer_steps);
+ subpath_satellites.push_back(satellite);
+ }
+ satellites.push_back(subpath_satellites);
+ }
+ _pathvector_satellites = new PathVectorSatellites();
+ _pathvector_satellites->setPathVector(pathv);
+ _pathvector_satellites->setSatellites(satellites);
+ satellites_param.setPathVectorSatellites(_pathvector_satellites);
+ } else {
+ g_warning("LPE Fillet/Chamfer can only be applied to shapes (not groups).");
+ SPLPEItem *item = const_cast<SPLPEItem *>(lpeItem);
+ item->removeCurrentPathEffect(false);
+ }
+}
Gtk::Widget *LPEFilletChamfer::newWidget()
{
@@ -94,54 +139,50 @@ Gtk::Widget *LPEFilletChamfer::newWidget()
vbox->set_border_width(5);
vbox->set_homogeneous(false);
vbox->set_spacing(2);
- Gtk::HBox *advertaising = Gtk::manage(new Gtk::HBox(true, 0));
- Gtk::Button *advert = Gtk::manage(new Gtk::Button(Glib::ustring(_("IMPORTANT! New version soon..."))));
- advertaising->pack_start(*advert, true, true, 2);
- vbox->pack_start(*advertaising, true, true, 2);
- Gtk::HBox *advertaising2 = Gtk::manage(new Gtk::HBox(true, 0));
- Gtk::Button *advert2 = Gtk::manage(new Gtk::Button(Glib::ustring(_("Not compatible. Convert to path after."))));
- advertaising2->pack_start(*advert2, true, true, 2);
- vbox->pack_start(*advertaising2, true, true, 2);
std::vector<Parameter *>::iterator it = param_vector.begin();
while (it != param_vector.end()) {
if ((*it)->widget_is_visible) {
Parameter *param = *it;
Gtk::Widget *widg = param->param_newWidget();
if (param->param_key == "radius") {
- Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
- widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEFilletChamfer::updateFillet));
- widg = widgRegistered;
+ Inkscape::UI::Widget::Scalar *widg_registered =
+ Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
+ widg_registered->signal_value_changed().connect(
+ sigc::mem_fun(*this, &LPEFilletChamfer::updateAmount));
+ widg = widg_registered;
if (widg) {
- Gtk::HBox *scalarParameter = dynamic_cast<Gtk::HBox *>(widg);
- std::vector<Gtk::Widget *> childList = scalarParameter->get_children();
- Gtk::Entry *entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
- entryWidg->set_width_chars(6);
+ Gtk::HBox *scalar_parameter = dynamic_cast<Gtk::HBox *>(widg);
+ std::vector<Gtk::Widget *> childList = scalar_parameter->get_children();
+ Gtk::Entry *entry_widget = dynamic_cast<Gtk::Entry *>(childList[1]);
+ entry_widget->set_width_chars(6);
}
+// } else if (param->param_key == "unit") {
+// Inkscape::UI::Widget::RegisteredUnitMenu* widg_registered =
+// Gtk::manage(dynamic_cast< Inkscape::UI::Widget::RegisteredUnitMenu *>(widg));
+// widg_registered->setUnit(unit.get_abbreviation());
+// widg_registered->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change unit parameter"));
+// widg_registered->getUnitMenu()->signal_changed().connect(sigc::mem_fun(*this, &LPEFilletChamfer::convertUnit));
+// widg = widg_registered;
} else if (param->param_key == "chamfer_steps") {
- Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
- widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEFilletChamfer::chamferSubdivisions));
- widg = widgRegistered;
+ Inkscape::UI::Widget::Scalar *widg_registered =
+ Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
+ widg_registered->signal_value_changed().connect(
+ sigc::mem_fun(*this, &LPEFilletChamfer::updateChamferSteps));
+ widg = widg_registered;
if (widg) {
- Gtk::HBox *scalarParameter = dynamic_cast<Gtk::HBox *>(widg);
- std::vector<Gtk::Widget *> childList = scalarParameter->get_children();
- Gtk::Entry *entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
- entryWidg->set_width_chars(3);
+ Gtk::HBox *scalar_parameter = dynamic_cast<Gtk::HBox *>(widg);
+ std::vector<Gtk::Widget *> childList = scalar_parameter->get_children();
+ Gtk::Entry *entry_widget = dynamic_cast<Gtk::Entry *>(childList[1]);
+ entry_widget->set_width_chars(3);
}
- } else if (param->param_key == "flexible") {
- Gtk::CheckButton *widgRegistered = Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg));
- widgRegistered->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::toggleFlexFixed));
} else if (param->param_key == "helper_size") {
- Inkscape::UI::Widget::Scalar *widgRegistered = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
- widgRegistered->signal_value_changed().connect(sigc::mem_fun(*this, &LPEFilletChamfer::refreshKnots));
- } else if (param->param_key == "hide_knots") {
- Gtk::CheckButton *widgRegistered = Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg));
- widgRegistered->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::toggleHide));
+ Inkscape::UI::Widget::Scalar *widg_registered =
+ Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
+ widg_registered->signal_value_changed().connect(
+ sigc::mem_fun(*this, &LPEFilletChamfer::refreshKnots));
} else if (param->param_key == "only_selected") {
Gtk::manage(widg);
- } else if (param->param_key == "ignore_radius_0") {
- Gtk::manage(widg);
}
-
Glib::ustring *tip = param->param_getTooltip();
if (widg) {
vbox->pack_start(*widg, true, true, 2);
@@ -153,504 +194,386 @@ Gtk::Widget *LPEFilletChamfer::newWidget()
}
}
}
-
++it;
}
- Gtk::HBox *filletContainer = Gtk::manage(new Gtk::HBox(true, 0));
- Gtk::Button *fillet = Gtk::manage(new Gtk::Button(Glib::ustring(_("Fillet"))));
- fillet->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::fillet));
- filletContainer->pack_start(*fillet, true, true, 2);
- Gtk::Button *inverseFillet = Gtk::manage(new Gtk::Button(Glib::ustring(_("Inverse fillet"))));
- inverseFillet->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::inverseFillet));
- filletContainer->pack_start(*inverseFillet, true, true, 2);
-
- Gtk::HBox *chamferContainer = Gtk::manage(new Gtk::HBox(true, 0));
+ Gtk::HBox *fillet_container = Gtk::manage(new Gtk::HBox(true, 0));
+ Gtk::Button *fillet = Gtk::manage(new Gtk::Button(Glib::ustring(_("Fillet"))));
+ fillet->signal_clicked()
+ .connect(sigc::bind<SatelliteType>(sigc::mem_fun(*this, &LPEFilletChamfer::updateSatelliteType),FILLET));
+
+ fillet_container->pack_start(*fillet, true, true, 2);
+ Gtk::Button *inverse_fillet = Gtk::manage(new Gtk::Button(Glib::ustring(_("Inverse fillet"))));
+ inverse_fillet->signal_clicked()
+ .connect(sigc::bind<SatelliteType>(sigc::mem_fun(*this, &LPEFilletChamfer::updateSatelliteType),INVERSE_FILLET));
+ fillet_container->pack_start(*inverse_fillet, true, true, 2);
+
+ Gtk::HBox *chamfer_container = Gtk::manage(new Gtk::HBox(true, 0));
Gtk::Button *chamfer = Gtk::manage(new Gtk::Button(Glib::ustring(_("Chamfer"))));
- chamfer->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::chamfer));
+ chamfer->signal_clicked()
+ .connect(sigc::bind<SatelliteType>(sigc::mem_fun(*this, &LPEFilletChamfer::updateSatelliteType),CHAMFER));
- chamferContainer->pack_start(*chamfer, true, true, 2);
- Gtk::Button *inverseChamfer = Gtk::manage(new Gtk::Button(Glib::ustring(_("Inverse chamfer"))));
- inverseChamfer->signal_clicked().connect(sigc::mem_fun(*this, &LPEFilletChamfer::inverseChamfer));
- chamferContainer->pack_start(*inverseChamfer, true, true, 2);
+ chamfer_container->pack_start(*chamfer, true, true, 2);
+ Gtk::Button *inverse_chamfer = Gtk::manage(new Gtk::Button(Glib::ustring(_("Inverse chamfer"))));
+ inverse_chamfer->signal_clicked()
+ .connect(sigc::bind<SatelliteType>(sigc::mem_fun(*this, &LPEFilletChamfer::updateSatelliteType),INVERSE_CHAMFER));
+ chamfer_container->pack_start(*inverse_chamfer, true, true, 2);
- vbox->pack_start(*filletContainer, true, true, 2);
- vbox->pack_start(*chamferContainer, true, true, 2);
+ vbox->pack_start(*fillet_container, true, true, 2);
+ vbox->pack_start(*chamfer_container, true, true, 2);
return vbox;
}
-void LPEFilletChamfer::toggleHide()
-{
- std::vector<Point> filletChamferData = fillet_chamfer_values.data();
- std::vector<Geom::Point> result;
- for (std::vector<Point>::const_iterator point_it = filletChamferData.begin();
- point_it != filletChamferData.end(); ++point_it) {
- if (hide_knots) {
- result.push_back(Point((*point_it)[X], std::abs((*point_it)[Y]) * -1));
- } else {
- result.push_back(Point((*point_it)[X], std::abs((*point_it)[Y])));
- }
- }
- fillet_chamfer_values.param_set_and_write_new_value(result);
- refreshKnots();
-}
-
-void LPEFilletChamfer::toggleFlexFixed()
+void LPEFilletChamfer::refreshKnots()
{
- std::vector<Point> filletChamferData = fillet_chamfer_values.data();
- std::vector<Geom::Point> result;
- unsigned int i = 0;
- for (std::vector<Point>::const_iterator point_it = filletChamferData.begin();
- point_it != filletChamferData.end(); ++point_it) {
- if (flexible) {
- result.push_back(Point(fillet_chamfer_values.to_time(i, (*point_it)[X]),
- (*point_it)[Y]));
- } else {
- result.push_back(Point(fillet_chamfer_values.to_len(i, (*point_it)[X]),
- (*point_it)[Y]));
- }
- i++;
- }
- if (flexible) {
- radius.param_set_range(0., 100);
- radius.param_set_value(0);
- } else {
- radius.param_set_range(0., infinity());
- radius.param_set_value(0);
+ if (satellites_param._knoth) {
+ satellites_param._knoth->update_knots();
}
- fillet_chamfer_values.param_set_and_write_new_value(result);
}
-void LPEFilletChamfer::updateFillet()
+void LPEFilletChamfer::updateAmount()
{
- double power = 0;
+ setSelected(_pathvector_satellites);
+ double power = radius;
if (!flexible) {
- power = radius * -1;
- } else {
- power = radius;
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ SPNamedView *nv = sp_document_namedview(document, NULL);
+ Glib::ustring display_unit = nv->display_units->abbr;
+ power = Inkscape::Util::Quantity::convert(power, unit.get_abbreviation(), display_unit.c_str());
}
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- doUpdateFillet(path_from_piecewise(pwd2, tolerance), power);
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
-}
-
-void LPEFilletChamfer::fillet()
-{
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- doChangeType(path_from_piecewise(pwd2, tolerance), 1);
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to fillet"));
+ _pathvector_satellites->updateAmount(power, apply_no_radius, apply_with_radius, only_selected,
+ use_knot_distance, flexible);
+ satellites_param.setPathVectorSatellites(_pathvector_satellites);
}
-void LPEFilletChamfer::inverseFillet()
-{
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- doChangeType(path_from_piecewise(pwd2, tolerance), 2);
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to inverse fillet"));
-}
-
-void LPEFilletChamfer::chamferSubdivisions()
-{
- fillet_chamfer_values.set_chamfer_steps(chamfer_steps);
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- doChangeType(path_from_piecewise(pwd2, tolerance), chamfer_steps + 5000);
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
-}
-
-void LPEFilletChamfer::chamfer()
-{
- fillet_chamfer_values.set_chamfer_steps(chamfer_steps);
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- doChangeType(path_from_piecewise(pwd2, tolerance), chamfer_steps + 3000);
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to chamfer"));
-}
+//void LPEFilletChamfer::convertUnit()
+//{
+// SPDocument * document = SP_ACTIVE_DOCUMENT;
+// SPNamedView *nv = sp_document_namedview(document, NULL);
+// Glib::ustring display_unit = nv->display_units->abbr;
+// _pathvector_satellites->convertUnit(unit.get_abbreviation(), display_unit, apply_no_radius, apply_with_radius);
+// satellites_param.setPathVectorSatellites(_pathvector_satellites);
+//}
-void LPEFilletChamfer::inverseChamfer()
+void LPEFilletChamfer::updateChamferSteps()
{
- fillet_chamfer_values.set_chamfer_steps(chamfer_steps);
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- doChangeType(path_from_piecewise(pwd2, tolerance), chamfer_steps + 4000);
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to inverse fillet"));
+ setSelected(_pathvector_satellites);
+ _pathvector_satellites->updateSteps(chamfer_steps, apply_no_radius, apply_with_radius, only_selected);
+ satellites_param.setPathVectorSatellites(_pathvector_satellites);
}
-void LPEFilletChamfer::refreshKnots()
+void LPEFilletChamfer::updateSatelliteType(SatelliteType satellitetype)
{
- Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
- fillet_chamfer_values.recalculate_knots(pwd2);
- SPDesktop *desktop = SP_ACTIVE_DESKTOP;
- if (tools_isactive(desktop, TOOLS_NODES)) {
- tools_switch(desktop, TOOLS_SELECT);
- tools_switch(desktop, TOOLS_NODES);
- }
- DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Knots and helper paths refreshed"));
+ setSelected(_pathvector_satellites);
+ _pathvector_satellites->updateSatelliteType(satellitetype, apply_no_radius, apply_with_radius, only_selected);
+ satellites_param.setPathVectorSatellites(_pathvector_satellites);
}
-void LPEFilletChamfer::doUpdateFillet(Geom::PathVector const &original_pathv, double power)
-{
- std::vector<Point> filletChamferData = fillet_chamfer_values.data();
- std::vector<Geom::Point> result;
- Geom::PathVector original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
- int counter = 0;
- for (PathVector::const_iterator path_it = original_pathv_processed.begin();
- path_it != original_pathv_processed.end(); ++path_it) {
- if (path_it->empty())
- continue;
-
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed() && path_it->back_closed().isDegenerate()) {
- const Curve &closingline = path_it->back_closed();
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- curve_endit = path_it->end_open();
- }
- }
- double powerend = 0;
- while (curve_it1 != curve_endit) {
- powerend = power;
- if (power < 0 && !use_knot_distance) {
- powerend = fillet_chamfer_values.rad_to_len(counter,powerend);
- }
- if (power > 0) {
- powerend = counter + (power / 100);
- }
- if (ignore_radius_0 && (filletChamferData[counter][X] == 0 ||
- filletChamferData[counter][X] == counter)) {
- powerend = filletChamferData[counter][X];
- }
- if (filletChamferData[counter][Y] == 0) {
- powerend = filletChamferData[counter][X];
- }
- if (only_selected && !isNodePointSelected(curve_it1->initialPoint())) {
- powerend = filletChamferData[counter][X];
+void LPEFilletChamfer::setSelected(PathVectorSatellites *_pathvector_satellites){
+ Geom::PathVector const pathv = _pathvector_satellites->getPathVector();
+ Satellites satellites = _pathvector_satellites->getSatellites();
+ for (size_t i = 0; i < satellites.size(); ++i) {
+ for (size_t j = 0; j < satellites[i].size(); ++j) {
+ Geom::Curve const &curve_in = pathv[i][j];
+ if (only_selected && isNodePointSelected(curve_in.initialPoint()) ){
+ satellites[i][j].setSelected(true);
+ } else {
+ satellites[i][j].setSelected(false);
}
- result.push_back(Point(powerend, filletChamferData[counter][Y]));
- ++curve_it1;
- ++curve_it2;
- counter++;
}
}
- fillet_chamfer_values.param_set_and_write_new_value(result);
+ _pathvector_satellites->setSatellites(satellites);
}
-void LPEFilletChamfer::doChangeType(Geom::PathVector const &original_pathv, int type)
+void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem)
{
- std::vector<Point> filletChamferData = fillet_chamfer_values.data();
- std::vector<Geom::Point> result;
- Geom::PathVector original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
- int counter = 0;
- for (PathVector::const_iterator path_it = original_pathv_processed.begin(); path_it != original_pathv_processed.end(); ++path_it) {
- int pathCounter = 0;
- if (path_it->empty())
- continue;
-
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed() && path_it->back_closed().isDegenerate()) {
- const Curve &closingline = path_it->back_closed();
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- curve_endit = path_it->end_open();
- }
+ if (sp_curve) {
+ //fillet chamfer specific calls
+ satellites_param.setUseDistance(use_knot_distance);
+ satellites_param.setCurrentZoom(current_zoom);
+ //mandatory call
+ satellites_param.setEffectType(effectType());
+ Geom::PathVector const pathv = pathv_to_linear_and_cubic_beziers(sp_curve->get_pathvector());
+ //if are diferent sizes call to recalculate
+ //TODO: Update the satellite data in paths modified,
+ Satellites satellites = satellites_param.data();
+ if (satellites.empty()) {
+ doOnApply(lpeItem);
+ satellites = satellites_param.data();
}
- while (curve_it1 != curve_endit) {
- bool toggle = true;
- if (filletChamferData[counter][Y] == 0 ||
- (ignore_radius_0 && (filletChamferData[counter][X] == 0 ||
- filletChamferData[counter][X] == counter)) ||
- (only_selected && !isNodePointSelected(curve_it1->initialPoint()))) {
- toggle = false;
+ if (_pathvector_satellites) {
+ size_t number_nodes = pathv.nodes().size();
+ size_t previous_number_nodes = _pathvector_satellites->getTotalSatellites();
+ if (number_nodes != previous_number_nodes) {
+ Satellite satellite(FILLET);
+ satellite.setIsTime(flexible);
+ satellite.setHasMirror(mirror_knots);
+ satellite.setHidden(hide_knots);
+ _pathvector_satellites->recalculateForNewPathVector(pathv, satellite);
+ satellites = _pathvector_satellites->getSatellites();
}
- if (toggle) {
- if(type >= 5000){
- if(filletChamferData[counter][Y] >= 3000 && filletChamferData[counter][Y] < 4000){
- type = type - 2000;
- } else if (filletChamferData[counter][Y] >= 4000 && filletChamferData[counter][Y] < 5000){
- type = type - 1000;
- }
- }
- result.push_back(Point(filletChamferData[counter][X], type));
- } else {
- result.push_back(filletChamferData[counter]);
- }
- ++curve_it1;
- if (curve_it2 != curve_endit) {
- ++curve_it2;
- }
- counter++;
- pathCounter++;
}
- }
- fillet_chamfer_values.param_set_and_write_new_value(result);
-}
-
-void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem)
-{
- if (SP_IS_SHAPE(lpeItem)) {
- std::vector<Point> point;
- PathVector const &original_pathv = pathv_to_linear_and_cubic_beziers(SP_SHAPE(lpeItem)->_curve->get_pathvector());
- Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(original_pathv);
- for (PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
- if (path_it->empty())
- continue;
-
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed()) {
- const Geom::Curve &closingline = path_it->back_closed();
- // the closing line segment is always of type
- // Geom::LineSegment.
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- // closingline.isDegenerate() did not work, because it only checks for
- // *exact* zero length, which goes wrong for relative coordinates and
- // rounding errors...
- // the closing line segment has zero-length. So stop before that one!
- curve_endit = path_it->end_open();
- }
- }
- int counter = 0;
- while (curve_it1 != curve_endit) {
- std::pair<std::size_t, std::size_t> positions = fillet_chamfer_values.get_positions(counter, original_pathv);
- Geom::NodeType nodetype;
- if (positions.second == 0) {
- if (path_it->closed()) {
- Piecewise<D2<SBasis> > u;
- u.push_cut(0);
- u.push(pwd2_in[fillet_chamfer_values.last_index(counter, original_pathv)], 1);
- Geom::Curve const * A = path_from_piecewise(u, 0.1)[0][0].duplicate();
- nodetype = get_nodetype(*A, *curve_it1);
+ if (_degenerate_hide) {
+ satellites_param.setGlobalKnotHide(true);
+ } else {
+ satellites_param.setGlobalKnotHide(false);
+ }
+ if (hide_knots) {
+ satellites_param.setHelperSize(0);
+ } else {
+ satellites_param.setHelperSize(helper_size);
+ }
+ for (size_t i = 0; i < satellites.size(); ++i) {
+ for (size_t j = 0; j < satellites[i].size(); ++j) {
+ Geom::Curve const &curve_in = pathv[i][j];
+ if (satellites[i][j].is_time != flexible) {
+ satellites[i][j].is_time = flexible;
+ double amount = satellites[i][j].amount;
+ if (pathv[i].size() == j) {
+ continue;
+ }
+ if (satellites[i][j].is_time) {
+ double time = timeAtArcLength(amount, curve_in);
+ satellites[i][j].amount = time;
} else {
- nodetype = NODE_NONE;
+ double size = arcLengthAt(amount, curve_in);
+ satellites[i][j].amount = size;
}
- } else {
- nodetype = get_nodetype((*path_it)[counter - 1], *curve_it1);
}
- if (nodetype == NODE_CUSP) {
- point.push_back(Point(0, 1));
- } else {
- point.push_back(Point(0, 0));
+ if (satellites[i][j].has_mirror != mirror_knots) {
+ satellites[i][j].has_mirror = mirror_knots;
}
- ++curve_it1;
- if (curve_it2 != curve_endit) {
- ++curve_it2;
+ satellites[i][j].hidden = hide_knots;
+ if (only_selected && isNodePointSelected(curve_in.initialPoint()) ){
+ satellites[i][j].setSelected(true);
}
- counter++;
}
}
- fillet_chamfer_values.param_set_and_write_new_value(point);
- } else {
- g_warning("LPE Fillet can only be applied to shapes (not groups).");
- SPLPEItem * item = const_cast<SPLPEItem*>(lpeItem);
- item->removeCurrentPathEffect(false);
- }
-}
-
-void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem)
-{
- if (SP_IS_SHAPE(lpeItem)) {
- if(hide_knots){
- fillet_chamfer_values.set_helper_size(0);
- } else {
- fillet_chamfer_values.set_helper_size(helper_size);
- }
- fillet_chamfer_values.set_use_distance(use_knot_distance);
- SPCurve *c = SP_IS_PATH(lpeItem) ? static_cast<SPPath const *>(lpeItem)
- ->get_original_curve()
- : SP_SHAPE(lpeItem)->getCurve();
- std::vector<Point> filletChamferData = fillet_chamfer_values.data();
- if (!filletChamferData.empty() && getKnotsNumber(c) != (int)
- filletChamferData.size()) {
- PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
- Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(original_pathv);
- fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pwd2_in);
+ if (!_pathvector_satellites) {
+ _pathvector_satellites = new PathVectorSatellites();
}
+ _pathvector_satellites->setPathVector(pathv);
+ _pathvector_satellites->setSatellites(satellites);
+ satellites_param.setPathVectorSatellites(_pathvector_satellites, false);
+ refreshKnots();
} else {
g_warning("LPE Fillet can only be applied to shapes (not groups).");
}
}
-int LPEFilletChamfer::getKnotsNumber(SPCurve const *c)
+void
+LPEFilletChamfer::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
{
- int nKnots = c->nodes_in_path();
- PathVector const pv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
- for (Geom::PathVector::const_iterator path_it = pv.begin();
- path_it != pv.end(); ++path_it) {
- if (!(*path_it).closed()) {
- nKnots--;
- }
- }
- return nKnots;
+ hp_vec.push_back(_hp);
}
void
-LPEFilletChamfer::adjustForNewPath(Geom::PathVector const &path_in)
+LPEFilletChamfer::addChamferSteps(Geom::Path &tmp_path, Geom::Path path_chamfer, Geom::Point end_arc_point, size_t steps)
{
- if (!path_in.empty()) {
- fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pathv_to_linear_and_cubic_beziers(path_in)[0].toPwSb());
+ setSelected(_pathvector_satellites);
+ double path_subdivision = 1.0 / steps;
+ for (size_t i = 1; i < steps; i++) {
+ Geom::Point chamfer_step = path_chamfer.pointAt(path_subdivision * i);
+ tmp_path.appendNew<Geom::LineSegment>(chamfer_step);
}
+ tmp_path.appendNew<Geom::LineSegment>(end_arc_point);
}
Geom::PathVector
LPEFilletChamfer::doEffect_path(Geom::PathVector const &path_in)
{
- Geom::PathVector pathvector_out;
- Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(pathv_to_linear_and_cubic_beziers(path_in));
- pwd2_in = remove_short_cuts(pwd2_in, .01);
- Piecewise<D2<SBasis> > der = derivative(pwd2_in);
- Piecewise<D2<SBasis> > n = rot90(unitVector(der));
- fillet_chamfer_values.set_pwd2(pwd2_in, n);
- std::vector<Point> filletChamferData = fillet_chamfer_values.data();
- unsigned int counter = 0;
+ const double GAP_HELPER = 0.00001;
+ Geom::PathVector path_out;
+ size_t path = 0;
const double K = (4.0 / 3.0) * (sqrt(2.0) - 1.0);
- Geom::PathVector path_in_processed = pathv_to_linear_and_cubic_beziers(path_in);
- for (PathVector::const_iterator path_it = path_in_processed.begin();
- path_it != path_in_processed.end(); ++path_it) {
- if (path_it->empty())
+ _degenerate_hide = false;
+ Geom::PathVector const pathv = pathv_to_linear_and_cubic_beziers(path_in);
+ Satellites satellites = _pathvector_satellites->getSatellites();
+ for (Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
+ if (path_it->empty()) {
+ continue;
+ }
+ Geom::Path tmp_path;
+ if (path_it->size() == 1) {
+ path++;
+ tmp_path.start(path_it[0].pointAt(0));
+ tmp_path.append(path_it[0]);
+ path_out.push_back(tmp_path);
continue;
- Geom::Path path_out;
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed()) {
- const Geom::Curve &closingline = path_it->back_closed();
- // the closing line segment is always of type
- // Geom::LineSegment.
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- // closingline.isDegenerate() did not work, because it only checks for
- // *exact* zero length, which goes wrong for relative coordinates and
- // rounding errors...
- // the closing line segment has zero-length. So stop before that one!
- curve_endit = path_it->end_open();
- }
}
- unsigned int counterCurves = 0;
- while (curve_it1 != curve_endit) {
- Curve *curve_it2Fixed = (*path_it->begin()).duplicate();
- if(!path_it->closed() || curve_it2 != curve_endit){
- curve_it2Fixed = (*curve_it2).duplicate();
+ double time0 = 0;
+ size_t curve = 0;
+ for (Geom::Path::const_iterator curve_it1 = path_it->begin(); curve_it1 != path_it->end(); ++curve_it1) {
+ size_t next_index = curve + 1;
+ if (curve == pathv[path].size() - 1 && pathv[path].closed()) {
+ next_index = 0;
+ }
+ //append last extreme of paths on open paths
+ if (curve == pathv[path].size() -1 && !pathv[path].closed()) { //the path is open and we are at end of path
+ if (time0 != 1) { //Previous satellite not at 100% amount
+ Geom::Curve *last_curve = curve_it1->portion(time0, 1);
+ last_curve->setInitial(tmp_path.finalPoint());
+ tmp_path.append(*last_curve);
+ }
+ continue;
}
- bool last = curve_it2 == curve_endit;
- std::vector<double> times = fillet_chamfer_values.get_times(counter, path_in, last);
- Curve *knotCurve1 = curve_it1->portion(times[0], times[1]);
- if (counterCurves > 0) {
- knotCurve1->setInitial(path_out.finalPoint());
+ Geom::Curve const &curve_it2 = pathv[path][next_index];
+ Satellite satellite = satellites[path][next_index];
+ if (Geom::are_near((*curve_it1).initialPoint(), (*curve_it1).finalPoint())) {
+ _degenerate_hide = true;
+ g_warning("Knots hidded if consecutive nodes has the same position.");
+ return path_in;
+ }
+ if (!curve) { //curve == 0
+ if (!path_it->closed()) {
+ time0 = 0;
+ } else {
+ time0 = satellites[path][0].time(*curve_it1);
+ }
+ }
+ double s = satellite.arcDistance(curve_it2);
+ double time1 = satellite.time(s, true, (*curve_it1));
+ double time2 = satellite.time(curve_it2);
+ if (time1 <= time0) {
+ time1 = time0;
+ }
+ if (time2 > 1) {
+ time2 = 1;
+ }
+ Geom::Curve *knot_curve_1 = curve_it1->portion(time0, time1);
+ Geom::Curve *knot_curve_2 = curve_it2.portion(time2, 1);
+ if (curve > 0) {
+ knot_curve_1->setInitial(tmp_path.finalPoint());
} else {
- path_out.start((*curve_it1).pointAt(times[0]));
+ tmp_path.start((*curve_it1).pointAt(time0));
+ }
+
+ Geom::Point start_arc_point = knot_curve_1->finalPoint();
+ Geom::Point end_arc_point = curve_it2.pointAt(time2);
+ //add a gap helper
+ if (time2 == 1) {
+ end_arc_point = curve_it2.pointAt(time2 - GAP_HELPER);
}
- Curve *knotCurve2 = curve_it2Fixed->portion(times[2], 1);
- Point startArcPoint = knotCurve1->finalPoint();
- Point endArcPoint = curve_it2Fixed->pointAt(times[2]);
- double k1 = distance(startArcPoint, curve_it1->finalPoint()) * K;
- double k2 = distance(endArcPoint, curve_it1->finalPoint()) * K;
- Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve1);
- Ray ray1(startArcPoint, curve_it1->finalPoint());
- if (cubic1) {
- ray1.setPoints((*cubic1)[2], startArcPoint);
+ if (time1 == time0) {
+ start_arc_point = curve_it1->pointAt(time1 + GAP_HELPER);
}
- Point handle1 = Point::polar(ray1.angle(),k1) + startArcPoint;
- Geom::CubicBezier const *cubic2 =
- dynamic_cast<Geom::CubicBezier const *>(&*knotCurve2);
- Ray ray2(curve_it1->finalPoint(), endArcPoint);
- if (cubic2) {
- ray2.setPoints(endArcPoint, (*cubic2)[1]);
+
+ double k1 = distance(start_arc_point, curve_it1->finalPoint()) * K;
+ double k2 = distance(curve_it2.initialPoint(), end_arc_point) * K;
+ Geom::CubicBezier const *cubic_1 = dynamic_cast<Geom::CubicBezier const *>(&*knot_curve_1);
+ Geom::CubicBezier const *cubic_2 = dynamic_cast<Geom::CubicBezier const *>(&*knot_curve_2);
+ Geom::Ray ray_1(start_arc_point, curve_it1->finalPoint());
+ Geom::Ray ray_2(curve_it2.initialPoint(), end_arc_point);
+ if (cubic_1) {
+ ray_1.setPoints((*cubic_1)[2], start_arc_point);
}
- Point handle2 = endArcPoint - Point::polar(ray2.angle(),k2);
- bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) > 0;
- double angle = angle_between(ray1, ray2, ccwToggle);
- double handleAngle = ray1.angle() - angle;
- if (ccwToggle) {
- handleAngle = ray1.angle() + angle;
+ if (cubic_2) {
+ ray_2.setPoints(end_arc_point, (*cubic_2)[1]);
}
- Point inverseHandle1 = Point::polar(handleAngle,k1) + startArcPoint;
- handleAngle = ray2.angle() + angle;
- if (ccwToggle) {
- handleAngle = ray2.angle() - angle;
+ bool ccw_toggle = cross(curve_it1->finalPoint() - start_arc_point, end_arc_point - start_arc_point) < 0;
+ double angle = angle_between(ray_1, ray_2, ccw_toggle);
+ double handle_angle_1 = ray_1.angle() - angle;
+ double handle_angle_2 = ray_2.angle() + angle;
+ if (ccw_toggle) {
+ handle_angle_1 = ray_1.angle() + angle;
+ handle_angle_2 = ray_2.angle() - angle;
}
- Point inverseHandle2 = endArcPoint - Point::polar(handleAngle,k2);
- //straigth lines arc based
- Line const x_line(Geom::Point(0,0),Geom::Point(1,0));
- Line const angled_line(startArcPoint,endArcPoint);
- double angleArc = Geom::angle_between( x_line,angled_line);
- double radius = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint))/sin(angle/2.0);
- Coord rx = radius;
- Coord ry = rx;
-
- if (times[1] != 1) {
- if (times[1] != gapHelper && times[1] != times[0] + gapHelper) {
- path_out.append(*knotCurve1);
- }
- int type = 0;
- if(path_it->closed() && last){
- type = std::abs(filletChamferData[counter - counterCurves][Y]);
- } else if (!path_it->closed() && last){
- //0
- } else {
- type = std::abs(filletChamferData[counter + 1][Y]);
- }
- if(are_near(middle_point(startArcPoint,endArcPoint),curve_it1->finalPoint(), 0.0001)){
- path_out.appendNew<Geom::LineSegment>(endArcPoint);
- } else if (type >= 3000 && type < 4000) {
- unsigned int chamferSubs = type-3000;
- Geom::Path path_chamfer;
- path_chamfer.start(path_out.finalPoint());
- if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
- path_chamfer.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
- } else {
- path_chamfer.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint);
- }
- double chamfer_stepsTime = 1.0/chamferSubs;
- for(unsigned int i = 1; i < chamferSubs; i++){
- Geom::Point chamferStep = path_chamfer.pointAt(chamfer_stepsTime * i);
- path_out.appendNew<Geom::LineSegment>(chamferStep);
+ Geom::Point handle_1 = Geom::Point::polar(ray_1.angle(), k1) + start_arc_point;
+ Geom::Point handle_2 = end_arc_point - Geom::Point::polar(ray_2.angle(), k2);
+ Geom::Point inverse_handle_1 = Geom::Point::polar(handle_angle_1, k1) + start_arc_point;
+ Geom::Point inverse_handle_2 = end_arc_point - Geom::Point::polar(handle_angle_2, k2);
+ if (time0 == 1) {
+ handle_1 = start_arc_point;
+ inverse_handle_1 = start_arc_point;
+ }
+ //remove gap helper
+ if (time2 == 1) {
+ end_arc_point = curve_it2.pointAt(time2);
+ }
+ if (time1 == time0) {
+ start_arc_point = curve_it1->pointAt(time0);
+ }
+ if (time1 != 1) {
+ if (time1 != time0 || (time1 == 1 && time0 == 1)) {
+ if (!knot_curve_1->isDegenerate()) {
+ tmp_path.append(*knot_curve_1);
}
- path_out.appendNew<Geom::LineSegment>(endArcPoint);
- } else if (type >= 4000 && type < 5000) {
- unsigned int chamferSubs = type-4000;
- Geom::Path path_chamfer;
- path_chamfer.start(path_out.finalPoint());
- if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
- ccwToggle = ccwToggle?0:1;
- path_chamfer.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
- }else{
- path_chamfer.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint);
+ }
+ SatelliteType type = satellite.satellite_type;
+ size_t steps = satellite.steps;
+ if (!steps) steps = 1;
+ Geom::Line const x_line(Geom::Point(0, 0), Geom::Point(1, 0));
+ Geom::Line const angled_line(start_arc_point, end_arc_point);
+ double arc_angle = Geom::angle_between(x_line, angled_line);
+ double radius = Geom::distance(start_arc_point, middle_point(start_arc_point, end_arc_point)) /
+ sin(angle / 2.0);
+ Geom::Coord rx = radius;
+ Geom::Coord ry = rx;
+ bool eliptical = (is_straight_curve(*curve_it1) &&
+ is_straight_curve(curve_it2) && method != FM_BEZIER) ||
+ method == FM_ARC;
+ switch (type) {
+ case CHAMFER:
+ {
+ Geom::Path path_chamfer;
+ path_chamfer.start(tmp_path.finalPoint());
+ if (eliptical) {
+ ccw_toggle = ccw_toggle ? 0 : 1;
+ path_chamfer.appendNew<Geom::EllipticalArc>(rx, ry, arc_angle, 0, ccw_toggle, end_arc_point);
+ } else {
+ path_chamfer.appendNew<Geom::CubicBezier>(handle_1, handle_2, end_arc_point);
+ }
+ addChamferSteps(tmp_path, path_chamfer, end_arc_point, steps);
}
- double chamfer_stepsTime = 1.0/chamferSubs;
- for(unsigned int i = 1; i < chamferSubs; i++){
- Geom::Point chamferStep = path_chamfer.pointAt(chamfer_stepsTime * i);
- path_out.appendNew<Geom::LineSegment>(chamferStep);
+ break;
+ case INVERSE_CHAMFER:
+ {
+ Geom::Path path_chamfer;
+ path_chamfer.start(tmp_path.finalPoint());
+ if (eliptical) {
+ path_chamfer.appendNew<Geom::EllipticalArc>(rx, ry, arc_angle, 0, ccw_toggle, end_arc_point);
+ } else {
+ path_chamfer.appendNew<Geom::CubicBezier>(inverse_handle_1, inverse_handle_2, end_arc_point);
+ }
+ addChamferSteps(tmp_path, path_chamfer, end_arc_point, steps);
}
- path_out.appendNew<Geom::LineSegment>(endArcPoint);
- } else if (type == 2) {
- if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
- ccwToggle = ccwToggle?0:1;
- path_out.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
- }else{
- path_out.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint);
+ break;
+ case INVERSE_FILLET:
+ {
+ if (eliptical) {
+ tmp_path.appendNew<Geom::EllipticalArc>(rx, ry, arc_angle, 0, ccw_toggle, end_arc_point);
+ } else {
+ tmp_path.appendNew<Geom::CubicBezier>(inverse_handle_1, inverse_handle_2, end_arc_point);
+ }
}
- } else if (type == 1){
- if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
- path_out.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
- } else {
- path_out.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint);
+ break;
+ default: //fillet
+ {
+ if (eliptical) {
+ ccw_toggle = ccw_toggle ? 0 : 1;
+ tmp_path.appendNew<Geom::EllipticalArc>(rx, ry, arc_angle, 0, ccw_toggle, end_arc_point);
+ } else {
+ tmp_path.appendNew<Geom::CubicBezier>(handle_1, handle_2, end_arc_point);
+ }
}
+ break;
}
} else {
- path_out.append(*knotCurve1);
- }
- if (path_it->closed() && last) {
- path_out.close();
- }
- ++curve_it1;
- if (curve_it2 != curve_endit) {
- ++curve_it2;
+ if (!knot_curve_1->isDegenerate()) {
+ tmp_path.append(*knot_curve_1);
+ }
}
- counter++;
- counterCurves++;
+ curve++;
+ time0 = time2;
+ }
+ if (path_it->closed()) {
+ tmp_path.close();
}
- pathvector_out.push_back(path_out);
+ path++;
+ path_out.push_back(tmp_path);
}
- return pathvector_out;
+ return path_out;
}
}; //namespace LivePathEffect
diff --git a/src/live_effects/lpe-fillet-chamfer.h b/src/live_effects/lpe-fillet-chamfer.h
index 290a37f92..7f5dd7d4c 100644
--- a/src/live_effects/lpe-fillet-chamfer.h
+++ b/src/live_effects/lpe-fillet-chamfer.h
@@ -7,25 +7,22 @@
*
* Copyright (C) 2014 Author(s)
*
- * Special thanks to Johan Engelen for the base of the effect -powerstroke-
- * Also to ScislaC for point me to the idea
- * Also su_v for his construvtive feedback and time
- * and finaly to Liam P. White for his big help on coding, that save me a lot of hours
+ * Jabiertxof:Thanks to all people help me
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "live_effects/parameter/enum.h"
-#include "live_effects/parameter/bool.h"
-#include "live_effects/parameter/unit.h"
-
-#include "live_effects/parameter/filletchamferpointarray.h"
+#include "live_effects/parameter/satellitesarray.h"
#include "live_effects/effect.h"
+#include "live_effects/parameter/unit.h"
+#include "helper/geom-pathvectorsatellites.h"
+#include "helper/geom-satellite.h"
namespace Inkscape {
namespace LivePathEffect {
-enum FilletMethod {
+enum Filletmethod {
FM_AUTO,
FM_ARC,
FM_BEZIER,
@@ -35,41 +32,38 @@ enum FilletMethod {
class LPEFilletChamfer : public Effect {
public:
LPEFilletChamfer(LivePathEffectObject *lpeobject);
- virtual ~LPEFilletChamfer();
-
+ virtual void doBeforeEffect(SPLPEItem const *lpeItem);
virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in);
-
virtual void doOnApply(SPLPEItem const *lpeItem);
- virtual void doBeforeEffect(SPLPEItem const *lpeItem);
- virtual void adjustForNewPath(Geom::PathVector const &path_in);
- virtual Gtk::Widget* newWidget();
-
- int getKnotsNumber(SPCurve const *c);
- void toggleHide();
- void toggleFlexFixed();
- void chamfer();
- void chamferSubdivisions();
- void inverseChamfer();
- void fillet();
- void inverseFillet();
- void updateFillet();
- void doUpdateFillet(Geom::PathVector const& original_pathv, double power);
- void doChangeType(Geom::PathVector const& original_pathv, int type);
+ virtual Gtk::Widget *newWidget();
+ Geom::Ray getRay(Geom::Point start, Geom::Point end, Geom::Curve *curve, bool reverse);
+ void addChamferSteps(Geom::Path &tmp_path, Geom::Path path_chamfer, Geom::Point end_arc_point, size_t steps);
+ void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec);
+ void updateSatelliteType(SatelliteType satellitetype);
+ void setSelected(PathVectorSatellites *_pathvector_satellites);
+ //void convertUnit();
+ void updateChamferSteps();
+ void updateAmount();
void refreshKnots();
- FilletChamferPointArrayParam fillet_chamfer_values;
+ SatellitesArrayParam satellites_param;
private:
-
- BoolParam hide_knots;
- BoolParam ignore_radius_0;
- BoolParam only_selected;
- BoolParam flexible;
- BoolParam use_knot_distance;
- EnumParam<FilletMethod> method;
+ UnitParam unit;
+ EnumParam<Filletmethod> method;
ScalarParam radius;
ScalarParam chamfer_steps;
+ BoolParam flexible;
+ BoolParam mirror_knots;
+ BoolParam only_selected;
+ BoolParam use_knot_distance;
+ BoolParam hide_knots;
+ BoolParam apply_no_radius;
+ BoolParam apply_with_radius;
ScalarParam helper_size;
+ bool _degenerate_hide;
+ PathVectorSatellites *_pathvector_satellites;
+ Geom::PathVector _hp;
LPEFilletChamfer(const LPEFilletChamfer &);
LPEFilletChamfer &operator=(const LPEFilletChamfer &);
diff --git a/src/live_effects/parameter/array.cpp b/src/live_effects/parameter/array.cpp
index 1b8f742da..7470f54cd 100644
--- a/src/live_effects/parameter/array.cpp
+++ b/src/live_effects/parameter/array.cpp
@@ -5,10 +5,7 @@
*/
#include "live_effects/parameter/array.h"
-
-#include "svg/svg.h"
-#include "svg/stringstream.h"
-
+#include "helper-fns.h"
#include <2geom/coord.h>
#include <2geom/point.h>
@@ -49,6 +46,45 @@ ArrayParam<Geom::Point>::readsvg(const gchar * str)
return Geom::Point(Geom::infinity(),Geom::infinity());
}
+
+template <>
+std::vector<Satellite>
+ArrayParam<std::vector<Satellite > >::readsvg(const gchar * str)
+{
+ std::vector<Satellite> subpath_satellites;
+ if (!str) {
+ return subpath_satellites;
+ }
+ gchar ** strarray = g_strsplit(str, "@", 0);
+ gchar ** iter = strarray;
+ while (*iter != NULL) {
+ gchar ** strsubarray = g_strsplit(*iter, ",", 8);
+ if (*strsubarray[7]) {//steps always > 0
+ Satellite *satellite = new Satellite();
+ satellite->setSatelliteType(g_strstrip(strsubarray[0]));
+ satellite->is_time = strncmp(strsubarray[1],"1",1) == 0;
+ satellite->selected = strncmp(strsubarray[2],"1",1) == 0;
+ satellite->has_mirror = strncmp(strsubarray[3],"1",1) == 0;
+ satellite->hidden = strncmp(strsubarray[4],"1",1) == 0;
+ double amount,angle;
+ float stepsTmp;
+ sp_svg_number_read_d(strsubarray[5], &amount);
+ sp_svg_number_read_d(strsubarray[6], &angle);
+ sp_svg_number_read_f(g_strstrip(strsubarray[7]), &stepsTmp);
+ unsigned int steps = (unsigned int)stepsTmp;
+ satellite->amount = amount;
+ satellite->angle = angle;
+ satellite->steps = steps;
+ subpath_satellites.push_back(*satellite);
+ }
+ g_strfreev (strsubarray);
+ iter++;
+ }
+ g_strfreev (strarray);
+ return subpath_satellites;
+}
+
+
} /* namespace LivePathEffect */
} /* namespace Inkscape */
diff --git a/src/live_effects/parameter/array.h b/src/live_effects/parameter/array.h
index fa08e1f91..10a3ea018 100644
--- a/src/live_effects/parameter/array.h
+++ b/src/live_effects/parameter/array.h
@@ -15,6 +15,7 @@
#include "live_effects/parameter/parameter.h"
+#include "helper/geom-satellite.h"
#include "svg/svg.h"
#include "svg/stringstream.h"
@@ -93,7 +94,43 @@ protected:
// separate items with pipe symbol
str << " | ";
}
- str << vector[i];
+ writesvgData(str,vector[i]);
+ }
+ }
+
+ void writesvgData(SVGOStringStream &str, float const &vector_data) const {
+ str << vector_data;
+ }
+
+ void writesvgData(SVGOStringStream &str, double const &vector_data) const {
+ str << vector_data;
+ }
+
+ void writesvgData(SVGOStringStream &str, Geom::Point const &vector_data) const {
+ str << vector_data;
+ }
+
+ void writesvgData(SVGOStringStream &str, std::vector<Satellite> const &vector_data) const {
+ for (size_t i = 0; i < vector_data.size(); ++i) {
+ if (i != 0) {
+ // separate items with @ symbol ¿Any other?
+ str << " @ ";
+ }
+ str << vector_data[i].getSatelliteTypeGchar();
+ str << ",";
+ str << vector_data[i].is_time;
+ str << ",";
+ str << vector_data[i].selected;
+ str << ",";
+ str << vector_data[i].has_mirror;
+ str << ",";
+ str << vector_data[i].hidden;
+ str << ",";
+ str << vector_data[i].amount;
+ str << ",";
+ str << vector_data[i].angle;
+ str << ",";
+ str << vector_data[i].steps;
}
}
diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp
deleted file mode 100644
index 7696288b0..000000000
--- a/src/live_effects/parameter/filletchamferpointarray.cpp
+++ /dev/null
@@ -1,873 +0,0 @@
-/*
- * Copyright (C) Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
- * Special thanks to Johan Engelen for the base of the effect -powerstroke-
- * Also to ScislaC for point me to the idea
- * Also su_v for his construvtive feedback and time
- * and finaly to Liam P. White for his big help on coding, that save me a lot of
- * hours
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include <2geom/piecewise.h>
-#include <2geom/sbasis-to-bezier.h>
-#include <2geom/sbasis-geometric.h>
-#include <2geom/line.h>
-#include <2geom/path-intersection.h>
-
-#include "ui/dialog/lpe-fillet-chamfer-properties.h"
-#include "live_effects/parameter/filletchamferpointarray.h"
-#include "live_effects/effect.h"
-#include "svg/svg.h"
-#include "svg/stringstream.h"
-#include "knotholder.h"
-#include "sp-lpe-item.h"
-#include "selection.h"
-
-// needed for on-canvas editting:
-#include "live_effects/lpeobject.h"
-#include "helper/geom-nodetype.h"
-#include "helper/geom-curves.h"
-#include "ui/tools/node-tool.h"
-
-// TODO due to internal breakage in glibmm headers,
-// this has to be included last.
-#include <glibmm/i18n.h>
-
-
-using namespace Geom;
-
-namespace Inkscape {
-
-namespace LivePathEffect {
-
-FilletChamferPointArrayParam::FilletChamferPointArrayParam(
- const Glib::ustring &label, const Glib::ustring &tip,
- const Glib::ustring &key, Inkscape::UI::Widget::Registry *wr,
- Effect *effect)
- : ArrayParam<Point>(label, tip, key, wr, effect, 0)
-{
- knot_shape = SP_KNOT_SHAPE_DIAMOND;
- knot_mode = SP_KNOT_MODE_XOR;
- knot_color = 0x00ff0000;
-}
-
-FilletChamferPointArrayParam::~FilletChamferPointArrayParam() {}
-
-Gtk::Widget *FilletChamferPointArrayParam::param_newWidget()
-{
- return NULL;
- /*
- Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg =
- Gtk::manage(
- new Inkscape::UI::Widget::RegisteredTransformedPoint(
- param_label,
- param_tooltip,
- param_key,
- *param_wr,
- param_effect->getRepr(),
- param_effect->getSPDoc()
- ) );
- // TODO: fix to get correct desktop (don't use SP_ACTIVE_DESKTOP)
- SPDesktop *desktop = SP_ACTIVE_DESKTOP;
- Affine transf = desktop->doc2dt();
- pointwdg->setTransform(transf);
- pointwdg->setValue( *this );
- pointwdg->clearProgrammatically();
- pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT,
- _("Change point parameter"));
-
- Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() );
- static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true);
- static_cast<Gtk::HBox*>(hbox)->show_all_children();
-
- return dynamic_cast<Gtk::Widget *> (hbox);
- */
-}
-
-void
-FilletChamferPointArrayParam::param_transform_multiply(Affine const &postmul,
- bool /*set*/)
-{
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-
- if (prefs->getBool("/options/transform/rectcorners", true) &&
- _vector[1][X] <= 0) {
- std::vector<Geom::Point> result;
- for (std::vector<Point>::const_iterator point_it = _vector.begin();
- point_it != _vector.end(); ++point_it) {
- Coord A =
- (*point_it)[X] * ((postmul.expansionX() + postmul.expansionY()) / 2);
- result.push_back(Point(A, (*point_it)[Y]));
- }
- param_set_and_write_new_value(result);
- }
-
- // param_set_and_write_new_value( (*this) * postmul );
-}
-
-/** call this method to recalculate the controlpoints such that they stay at the
- * same location relative to the new path. Useful after adding/deleting nodes to
- * the path.*/
-void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2(
- Piecewise<D2<SBasis> > const &pwd2_in)
-{
- if (!last_pwd2.empty()) {
- PathVector const pathv =
- path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001);
- PathVector last_pathv =
- path_from_piecewise(remove_short_cuts(last_pwd2, 0.1), 0.001);
- std::vector<Point> result;
- unsigned long counter = 0;
- unsigned long counterPaths = 0;
- unsigned long counterCurves = 0;
- long offset = 0;
- long offsetPaths = 0;
- Geom::NodeType nodetype;
- for (PathVector::const_iterator path_it = pathv.begin();
- path_it != pathv.end(); ++path_it) {
- if (path_it->empty()) {
- counterPaths++;
- counter++;
- continue;
- }
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed() && path_it->back_closed().isDegenerate()) {
- const Curve &closingline = path_it->back_closed();
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- curve_endit = path_it->end_open();
- }
- }
- counterCurves = 0;
- while (curve_it1 != curve_endit) {
- //if start a path get node type
- if (counterCurves == 0) {
- if (path_it->closed()) {
- if (path_it->back_closed().isDegenerate()) {
- nodetype = get_nodetype(path_it->back_open(), *curve_it1);
- } else {
- nodetype = get_nodetype(path_it->back_closed(), *curve_it1);
- }
- } else {
- nodetype = NODE_NONE;
- }
- } else {
- //check node type also whith straight lines because get_nodetype
- //return non cusp node in a node inserted inside a straight line
- //todo: if the path remove some nodes whith the result of a straight
- //line but with handles, the node inserted into dont fire the knot
- // because is not handle as cusp node by get_nodetype function
- bool next_is_line = is_straight_curve(*curve_it1);
- bool this_is_line = is_straight_curve((*path_it)[counterCurves - 1]);
- nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1);
- if (this_is_line || next_is_line) {
- nodetype = NODE_CUSP;
- }
- }
- if (last_pathv.size() > pathv.size() ||
- (last_pathv.size() > counterPaths &&
- last_pathv[counterPaths].size() > counter - offset &&
- !are_near(curve_it1->initialPoint(),
- last_pathv[counterPaths][counter - offset].initialPoint(),
- 0.1))) {
- if ( curve_it2 == curve_endit) {
- if (last_pathv[counterPaths].size() != pathv[counterPaths].size()) {
- offset = (last_pathv[counterPaths].size() - pathv[counterPaths].size()) * -1;
- } else {
- offset = 0;
- }
- offsetPaths += offset;
- offset = offsetPaths;
- } else if (counterCurves == 0 && last_pathv.size() <= pathv.size() &&
- counter - offset <= last_pathv[counterPaths].size() &&
- are_near(curve_it1->initialPoint(),
- last_pathv[counterPaths].finalPoint(), 0.1) &&
- !last_pathv[counterPaths].closed()) {
- long e = counter - offset + 1;
- std::vector<Point> tmp = _vector;
- for (unsigned long i =
- last_pathv[counterPaths].size() + counter - offset;
- i > counterCurves - offset + 1; i--) {
-
- if (tmp[i - 1][X] > 0) {
- double fractpart, intpart;
- fractpart = modf(tmp[i - 1][X], &intpart);
- _vector[e] = Point(e + fractpart, tmp[i - 1][Y]);
- } else {
- _vector[e] = Point(tmp[i - 1][X], tmp[i - 1][Y]);
- }
- e++;
- }
- //delete temp vector
- std::vector<Point>().swap(tmp);
- if (last_pathv.size() > counterPaths) {
- last_pathv[counterPaths] = last_pathv[counterPaths].reversed();
- }
- } else {
- if (last_pathv.size() > counterPaths) {
- if (last_pathv[counterPaths].size() <
- pathv[counterPaths].size()) {
- offset++;
- } else if (last_pathv[counterPaths].size() >
- pathv[counterPaths].size()) {
- offset--;
- continue;
- }
- } else {
- offset++;
- }
- }
- double xPos = 0;
- if (_vector[1][X] > 0) {
- xPos = nearest_time(curve_it1->initialPoint(), pwd2_in);
- }
- if (nodetype == NODE_CUSP) {
- result.push_back(Point(xPos, 1));
- } else {
- result.push_back(Point(xPos, 0));
- }
- } else {
- double xPos = _vector[counter - offset][X];
- if (_vector.size() <= (unsigned)(counter - offset)) {
- if (_vector[1][X] > 0) {
- xPos = nearest_time(curve_it1->initialPoint(), pwd2_in);
- } else {
- xPos = 0;
- }
- }
- if (nodetype == NODE_CUSP) {
- double vectorY = _vector[counter - offset][Y];
- if (_vector.size() <= (unsigned)(counter - offset) || vectorY == 0) {
- vectorY = 1;
- }
- result.push_back(Point(xPos, vectorY));
- } else {
- if (_vector[1][X] < 0) {
- xPos = 0;
- }
- result.push_back(Point(floor(xPos), 0));
- }
- }
- ++curve_it1;
- if (curve_it2 != curve_endit) {
- ++curve_it2;
- }
- counter++;
- counterCurves++;
- }
- counterPaths++;
- }
- _vector = result;
- write_to_SVG();
- }
-}
-
-void FilletChamferPointArrayParam::recalculate_knots(
- Piecewise<D2<SBasis> > const &pwd2_in)
-{
- bool change = false;
- if(_vector.size() == 0){
- return;
- }
- PathVector pathv = path_from_piecewise(pwd2_in, 0.001);
- if (!pathv.empty()) {
- std::vector<Point> result;
- int counter = 0;
- int counterCurves = 0;
- Geom::NodeType nodetype;
- for (PathVector::const_iterator path_it = pathv.begin();
- path_it != pathv.end(); ++path_it) {
- if (path_it->empty()) {
- counter++;
- continue;
- }
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed() && path_it->back_closed().isDegenerate()) {
- const Curve &closingline = path_it->back_closed();
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- curve_endit = path_it->end_open();
- }
- }
- counterCurves = 0;
- while (curve_it1 != curve_endit) {
- //if start a path get node type
- if (counterCurves == 0) {
- if (path_it->closed()) {
- if (path_it->back_closed().isDegenerate()) {
- nodetype = get_nodetype(path_it->back_open(), *curve_it1);
- } else {
- nodetype = get_nodetype(path_it->back_closed(), *curve_it1);
- }
- } else {
- nodetype = NODE_NONE;
- }
- } else {
- bool next_is_line = is_straight_curve(*curve_it1);
- bool this_is_line = is_straight_curve((*path_it)[counterCurves - 1]);
- nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1);
- if (this_is_line || next_is_line) {
- nodetype = NODE_CUSP;
- }
- }
- if (nodetype == NODE_CUSP) {
- double vectorY = _vector[counter][Y];
- if (vectorY == 0) {
- vectorY = 1;
- change = true;
- }
- result.push_back(Point(_vector[counter][X], vectorY));
- } else {
- double xPos = floor(_vector[counter][X]);
- if (_vector[1][X] < 0) {
- xPos = 0;
- }
- double vectorY = _vector[counter][Y];
- if (vectorY != 0) {
- change = true;
- }
- result.push_back(Point(xPos, 0));
- }
- ++curve_it1;
- counter++;
- if (curve_it2 != curve_endit) {
- ++curve_it2;
- }
- counterCurves++;
- }
- }
- if (change) {
- _vector = result;
- write_to_SVG();
- }
- }
-}
-
-void FilletChamferPointArrayParam::set_pwd2(
- Piecewise<D2<SBasis> > const &pwd2_in,
- Piecewise<D2<SBasis> > const &pwd2_normal_in)
-{
- last_pwd2 = pwd2_in;
- last_pwd2_normal = pwd2_normal_in;
-}
-
-void FilletChamferPointArrayParam::set_helper_size(int hs)
-{
- helper_size = hs;
-}
-
-void FilletChamferPointArrayParam::set_chamfer_steps(int value_chamfer_steps)
-{
- chamfer_steps = value_chamfer_steps;
-}
-
-void FilletChamferPointArrayParam::set_use_distance(bool use_knot_distance )
-{
- use_distance = use_knot_distance;
-}
-
-void FilletChamferPointArrayParam::updateCanvasIndicators()
-{
- std::vector<Point> ts = data();
- hp.clear();
- unsigned int i = 0;
- for (std::vector<Point>::const_iterator point_it = ts.begin();
- point_it != ts.end(); ++point_it) {
- double Xvalue = to_time(i, (*point_it)[X]) -i;
- if (Xvalue == 0) {
- i++;
- continue;
- }
- Geom::Point ptA = last_pwd2[i].valueAt(Xvalue);
- Geom::Point derivA = unit_vector(derivative(last_pwd2[i]).valueAt(Xvalue));
- Geom::Rotate rot(Geom::Rotate::from_degrees(-90));
- derivA = derivA * rot;
- Geom::Point C = ptA - derivA * helper_size;
- Geom::Point D = ptA + derivA * helper_size;
- Geom::Ray ray1(C, D);
- char const * svgd = "M 1,0.25 0.5,0 1,-0.25 M 1,0.5 0,0 1,-0.5";
- Geom::PathVector pathv = sp_svg_read_pathv(svgd);
- Geom::Affine aff = Geom::Affine();
- aff *= Geom::Scale(helper_size);
- aff *= Geom::Rotate(ray1.angle() - rad_from_deg(270));
- aff *= Geom::Translate(last_pwd2[i].valueAt(Xvalue));
- pathv *= aff;
- hp.push_back(pathv[0]);
- hp.push_back(pathv[1]);
- i++;
- }
-}
-
-void FilletChamferPointArrayParam::addCanvasIndicators(
- SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
-{
- hp_vec.push_back(hp);
-}
-
-double FilletChamferPointArrayParam::rad_to_len(int index, double rad)
-{
- double len = 0;
- Geom::PathVector subpaths = path_from_piecewise(last_pwd2, 0.1);
- std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
- D2<SBasis> A = last_pwd2[last_index(index, subpaths)];
- if(positions.second != 0){
- A = last_pwd2[index-1];
- }else{
- if(!subpaths[positions.first].closed()){
- return len;
- }
- }
- D2<SBasis> B = last_pwd2[index];
- Piecewise<D2<SBasis> > offset_curve0 = Piecewise<D2<SBasis> >(A)+rot90(unitVector(derivative(A)))*(rad);
- Piecewise<D2<SBasis> > offset_curve1 = Piecewise<D2<SBasis> >(B)+rot90(unitVector(derivative(B)))*(rad);
- Geom::Path p0 = path_from_piecewise(offset_curve0, 0.1)[0];
- Geom::Path p1 = path_from_piecewise(offset_curve1, 0.1)[0];
- Geom::Crossings cs = Geom::crossings(p0, p1);
- if(cs.size() > 0){
- Point cp =p0(cs[0].ta);
- double p0pt = nearest_time(cp, B);
- len = time_to_len(index,p0pt);
- } else {
- if(rad < 0){
- len = rad_to_len(index, rad * -1);
- }
- }
- return len;
-}
-
-double FilletChamferPointArrayParam::len_to_rad(int index, double len)
-{
- double rad = 0;
- double tmp_len = _vector[index][X];
- _vector[index] = Geom::Point(len,_vector[index][Y]);
- Geom::PathVector subpaths = path_from_piecewise(last_pwd2, 0.1);
- std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
- Piecewise<D2<SBasis> > u;
- u.push_cut(0);
- u.push(last_pwd2[last_index(index, subpaths)], 1);
- Geom::Curve * A = path_from_piecewise(u, 0.1)[0][0].duplicate();
- Geom::Curve * B = subpaths[positions.first][positions.second].duplicate();
- std::vector<double> times;
- if(positions.second != 0){
- A = subpaths[positions.first][positions.second-1].duplicate();
- times = get_times(index-1, subpaths, false);
- }else{
- if(!subpaths[positions.first].closed()){
- return rad;
- }
- times = get_times(last_index(index, subpaths), subpaths, true);
- }
- _vector[index] = Geom::Point(tmp_len,_vector[index][Y]);
- Geom::Point startArcPoint = A->toSBasis().valueAt(times[1]);
- Geom::Point endArcPoint = B->toSBasis().valueAt(times[2]);
- Curve *knotCurve1 = A->portion(times[0], times[1]);
- Curve *knotCurve2 = B->portion(times[2], 1);
- Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(knotCurve1);
- Ray ray1(startArcPoint, A->finalPoint());
- if (cubic1) {
- ray1.setPoints((*cubic1)[2], startArcPoint);
- }
- Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(knotCurve2);
- Ray ray2(B->initialPoint(), endArcPoint);
- if (cubic2) {
- ray2.setPoints(endArcPoint, (*cubic2)[1]);
- }
- bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) > 0;
- double distanceArc = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint));
- double angleBetween = angle_between(ray1, ray2, ccwToggle);
- rad = distanceArc/sin(angleBetween/2.0);
- return rad * -1;
-}
-
-std::vector<double> FilletChamferPointArrayParam::get_times(int index, Geom::PathVector subpaths, bool last)
-{
- const double tolerance = 0.001;
- const double gapHelper = 0.00001;
- std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
- Curve *curve_it1;
- curve_it1 = subpaths[positions.first][positions.second].duplicate();
- Coord it1_length = (*curve_it1).length(tolerance);
- double time_it1, time_it2, time_it1_B, intpart;
- if (static_cast<int>(_vector.size()) <= index){
- std::vector<double> out;
- out.push_back(0);
- out.push_back(1);
- out.push_back(0);
- return out;
- }
- time_it1 = modf(to_time(index, _vector[index][X]), &intpart);
- if (_vector[index][Y] == 0) {
- time_it1 = 0;
- }
- double resultLenght = 0;
- if (subpaths[positions.first].closed() && last) {
- time_it2 = modf(to_time(index - positions.second , _vector[index - positions.second ][X]), &intpart);
- resultLenght = it1_length + to_len(index - positions.second, _vector[index - positions.second ][X]);
- } else if (!subpaths[positions.first].closed() && last){
- time_it2 = 0;
- resultLenght = 0;
- } else {
- time_it2 = modf(to_time(index + 1, _vector[index + 1][X]), &intpart);
- resultLenght = it1_length + to_len( index + 1, _vector[index + 1][X]);
- }
- if (resultLenght > 0 && time_it2 != 0) {
- time_it1_B = modf(to_time(index, -resultLenght), &intpart);
- } else {
- if (time_it2 == 0) {
- time_it1_B = 1;
- } else {
- time_it1_B = gapHelper;
- }
- }
-
- if ((subpaths[positions.first].closed() && last && _vector[index - positions.second][Y] == 0) || (subpaths[positions.first].size() > positions.second + 1 && _vector[index + 1][Y] == 0)) {
- time_it1_B = 1;
- time_it2 = 0;
- }
- if (time_it1_B < time_it1) {
- time_it1_B = time_it1 + gapHelper;
- }
- std::vector<double> out;
- out.push_back(time_it1);
- out.push_back(time_it1_B);
- out.push_back(time_it2);
- return out;
-}
-
-std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, Geom::PathVector subpaths)
-{
- int counter = -1;
- std::size_t first = 0;
- std::size_t second = 0;
- for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) {
- if (path_it->empty())
- continue;
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed()) {
- const Geom::Curve &closingline = path_it->back_closed();
- // the closing line segment is always of type
- // Geom::LineSegment.
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- // closingline.isDegenerate() did not work, because it only checks for
- // *exact* zero length, which goes wrong for relative coordinates and
- // rounding errors...
- // the closing line segment has zero-length. So stop before that one!
- curve_endit = path_it->end_open();
- }
- }
- first++;
- second = 0;
- while (curve_it1 != curve_endit) {
- counter++;
- second++;
- if(counter == index){
- break;
- }
- ++curve_it1;
- }
- if(counter == index){
- break;
- }
- }
- first--;
- second--;
- std::pair<std::size_t, std::size_t> out(first, second);
- return out;
-}
-
-int FilletChamferPointArrayParam::last_index(int index, Geom::PathVector subpaths)
-{
- int counter = -1;
- bool inSubpath = false;
- for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) {
- if (path_it->empty())
- continue;
- Geom::Path::const_iterator curve_it1 = path_it->begin();
- Geom::Path::const_iterator curve_endit = path_it->end_default();
- if (path_it->closed()) {
- const Geom::Curve &closingline = path_it->back_closed();
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- curve_endit = path_it->end_open();
- }
- }
- while (curve_it1 != curve_endit) {
- counter++;
- if(counter == index){
- inSubpath = true;
- }
- ++curve_it1;
- }
- if(inSubpath){
- break;
- }
- }
- if(!inSubpath){
- counter = -1;
- }
- return counter;
-}
-
-
-double FilletChamferPointArrayParam::len_to_time(int index, double len)
-{
- double t = 0;
- if (last_pwd2.size() > (unsigned) index) {
- if (len != 0) {
- if (last_pwd2[index][0].degreesOfFreedom() != 2) {
- Piecewise<D2<SBasis> > u;
- u.push_cut(0);
- u.push(last_pwd2[index], 1);
- std::vector<double> t_roots = roots(arcLengthSb(u) - std::abs(len));
- if (t_roots.size() > 0) {
- t = t_roots[0];
- }
- } else {
- double lenghtPart = 0;
- if (last_pwd2.size() > (unsigned) index) {
- lenghtPart = length(last_pwd2[index], EPSILON);
- }
- if (std::abs(len) < lenghtPart && lenghtPart != 0) {
- t = std::abs(len) / lenghtPart;
- }
- }
- }
- t = double(index) + t;
- } else {
- t = double(last_pwd2.size() - 1);
- }
-
- return t;
-}
-
-double FilletChamferPointArrayParam::time_to_len(int index, double time)
-{
- double intpart;
- double len = 0;
- time = modf(time, &intpart);
- double lenghtPart = 0;
- if (last_pwd2.size() <= (unsigned) index || time == 0) {
- return len;
- }
- if (last_pwd2[index][0].degreesOfFreedom() != 2) {
- Piecewise<D2<SBasis> > u;
- u.push_cut(0);
- u.push(last_pwd2[index], 1);
- u = portion(u, 0, time);
- return length(u, 0.001) * -1;
- }
- lenghtPart = length(last_pwd2[index], EPSILON);
- return (time * lenghtPart) * -1;
-}
-
-double FilletChamferPointArrayParam::to_time(int index, double A)
-{
- if (A > 0) {
- return A;
- } else {
- return len_to_time(index, A);
- }
-}
-
-double FilletChamferPointArrayParam::to_len(int index, double A)
-{
- if (A > 0) {
- return time_to_len(index, A);
- } else {
- return A;
- }
-}
-
-void FilletChamferPointArrayParam::set_oncanvas_looks(SPKnotShapeType shape,
- SPKnotModeType mode,
- guint32 color)
-{
- knot_shape = shape;
- knot_mode = mode;
- knot_color = color;
-}
-
-FilletChamferPointArrayParamKnotHolderEntity::
-FilletChamferPointArrayParamKnotHolderEntity(
- FilletChamferPointArrayParam *p, unsigned int index)
- : _pparam(p), _index(index) {}
-
-void FilletChamferPointArrayParamKnotHolderEntity::knot_set(Point const &p,
- Point const &/*origin*/,
- guint state)
-{
- using namespace Geom;
-
- if (!valid_index(_index)) {
- return;
- }
- Piecewise<D2<SBasis> > const &pwd2 = _pparam->get_pwd2();
- double t = nearest_time(p, pwd2[_index]);
- Geom::Point const s = snap_knot_position(pwd2[_index].valueAt(t), state);
- t = nearest_time(s, pwd2[_index]);
- if (t == 1) {
- t = 0.9999;
- }
- t += _index;
-
- if (_pparam->_vector.at(_index)[X] <= 0) {
- _pparam->_vector.at(_index) =
- Point(_pparam->time_to_len(_index, t), _pparam->_vector.at(_index)[Y]);
- } else {
- _pparam->_vector.at(_index) = Point(t, _pparam->_vector.at(_index)[Y]);
- }
- sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
-}
-
-Point FilletChamferPointArrayParamKnotHolderEntity::knot_get() const
-{
- using namespace Geom;
-
- if (!valid_index(_index)) {
- return Point(infinity(), infinity());
- }
-
- Piecewise<D2<SBasis> > const &pwd2 = _pparam->get_pwd2();
-
- double time_it = _pparam->to_time(_index, _pparam->_vector.at(_index)[X]);
- Point canvas_point = pwd2.valueAt(time_it);
-
- _pparam->updateCanvasIndicators();
- return canvas_point;
-
-}
-
-void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state)
-{
- if (state & GDK_CONTROL_MASK) {
- if (state & GDK_MOD1_MASK) {
- _pparam->_vector.at(_index) = Point(_index, _pparam->_vector.at(_index)[Y]);
- _pparam->param_set_and_write_new_value(_pparam->_vector);
- sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
- }else{
- using namespace Geom;
- int type = (int)_pparam->_vector.at(_index)[Y];
- if (type >=3000 && type < 4000){
- type = 3;
- }
- if (type >=4000 && type < 5000){
- type = 4;
- }
- switch(type){
- case 1:
- type = 2;
- break;
- case 2:
- type = _pparam->chamfer_steps + 3000;
- break;
- case 3:
- type = _pparam->chamfer_steps + 4000;
- break;
- default:
- type = 1;
- break;
- }
- _pparam->_vector.at(_index) = Point(_pparam->_vector.at(_index)[X], (double)type);
- _pparam->param_set_and_write_new_value(_pparam->_vector);
- sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
- const gchar *tip;
- if (type >=3000 && type < 4000){
- tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- } else if (type >=4000 && type < 5000) {
- tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- } else if (type == 2) {
- tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- } else {
- tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- }
- this->knot->tip = g_strdup(tip);
- this->knot->show();
- }
- } else if (state & GDK_SHIFT_MASK) {
- double xModified = _pparam->_vector.at(_index).x();
- if(xModified < 0 && !_pparam->use_distance){
- xModified = _pparam->len_to_rad(_index, _pparam->_vector.at(_index).x());
- }
- Geom::PathVector subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1);
- std::pair<std::size_t, std::size_t> positions = _pparam->get_positions(_index, subpaths);
- D2<SBasis> A = _pparam->last_pwd2[_pparam->last_index(_index, subpaths)];
- if(positions.second != 0){
- A = _pparam->last_pwd2[_index-1];
- }
- D2<SBasis> B = _pparam->last_pwd2[_index];
- bool aprox = (A[0].degreesOfFreedom() != 2 || B[0].degreesOfFreedom() != 2) && !_pparam->use_distance?true:false;
- Geom::Point offset = Geom::Point(xModified, _pparam->_vector.at(_index).y());
- Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog(
- this->desktop, offset, this, _pparam->use_distance, aprox);
- }
-
-}
-
-void FilletChamferPointArrayParamKnotHolderEntity::knot_set_offset(
- Geom::Point offset)
-{
- double xModified = offset.x();
- if(xModified < 0 && !_pparam->use_distance){
- xModified = _pparam->rad_to_len(_index, offset.x());
- }
- _pparam->_vector.at(_index) = Geom::Point(xModified, offset.y());
- this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
-}
-
-void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) {
- recalculate_knots(get_pwd2());
- for (unsigned int i = 0; i < _vector.size(); ++i) {
- if (_vector[i][Y] <= 0) {
- continue;
- }
- const gchar *tip;
- if (_vector[i][Y] >=3000 && _vector[i][Y] < 4000){
- tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- } else if (_vector[i][Y] >=4000 && _vector[i][Y] < 5000) {
- tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- } else if (_vector[i][Y] == 2) {
- tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- } else {
- tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggle type, "
- "<b>Shift+Click</b> open dialog, "
- "<b>Ctrl+Alt+Click</b> reset");
- }
- FilletChamferPointArrayParamKnotHolderEntity *e =
- new FilletChamferPointArrayParamKnotHolderEntity(this, i);
- e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip),
- knot_shape, knot_mode, knot_color);
- knotholder->add(e);
- }
- updateCanvasIndicators();
-}
-
-} /* namespace LivePathEffect */
-
-} /* 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 :
diff --git a/src/live_effects/parameter/filletchamferpointarray.h b/src/live_effects/parameter/filletchamferpointarray.h
deleted file mode 100644
index 4e4268703..000000000
--- a/src/live_effects/parameter/filletchamferpointarray.h
+++ /dev/null
@@ -1,123 +0,0 @@
-#ifndef INKSCAPE_LIVEPATHEFFECT_FILLET_CHAMFER_POINT_ARRAY_H
-#define INKSCAPE_LIVEPATHEFFECT_FILLET_CHAMFER_POINT_ARRAY_H
-
-/*
- * Inkscape::LivePathEffectParameters
- * Copyright (C) Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
- * Special thanks to Johan Engelen for the base of the effect -powerstroke-
- * Also to ScislaC for point me to the idea
- * Also su_v for his construvtive feedback and time
- * and finaly to Liam P. White for his big help on coding, that save me a lot of
- * hours
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include <glib.h>
-#include <2geom/point.h>
-
-#include "live_effects/parameter/array.h"
-
-#include "knot-holder-entity.h"
-
-namespace Inkscape {
-
-namespace LivePathEffect {
-
-class FilletChamferPointArrayParamKnotHolderEntity;
-
-class FilletChamferPointArrayParam : public ArrayParam<Geom::Point> {
-public:
- FilletChamferPointArrayParam(const Glib::ustring &label,
- const Glib::ustring &tip,
- const Glib::ustring &key,
- Inkscape::UI::Widget::Registry *wr,
- Effect *effect);
- virtual ~FilletChamferPointArrayParam();
-
- virtual Gtk::Widget *param_newWidget();
-
- virtual void param_transform_multiply(Geom::Affine const &postmul,
- bool /*set*/);
-
- void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode,
- guint32 color);
- virtual double to_time(int index, double A);
- virtual double to_len(int index, double A);
- virtual double rad_to_len(int index, double rad);
- virtual double len_to_rad(int index, double len);
- virtual double len_to_time(int index, double len);
- virtual double time_to_len(int index, double time);
- virtual std::pair<std::size_t, std::size_t> get_positions(int index, Geom::PathVector subpaths);
- virtual int last_index(int index, Geom::PathVector subpaths);
- std::vector<double> get_times(int index, Geom::PathVector subpaths, bool last);
- virtual void set_helper_size(int hs);
- virtual void set_use_distance(bool use_knot_distance);
- virtual void set_chamfer_steps(int value_chamfer_steps);
- virtual void addCanvasIndicators(SPLPEItem const *lpeitem,
- std::vector<Geom::PathVector> &hp_vec);
- virtual void param_update_default(const gchar * default_value){};
- virtual bool providesKnotHolderEntities() const {
- return true;
- }
- virtual void updateCanvasIndicators();
- virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item);
-
- void set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in,
- Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_normal_in);
- Geom::Piecewise<Geom::D2<Geom::SBasis> > const &get_pwd2() const {
- return last_pwd2;
- }
- Geom::Piecewise<Geom::D2<Geom::SBasis> > const &get_pwd2_normal() const {
- return last_pwd2_normal;
- }
-
- void recalculate_controlpoints_for_new_pwd2(
- Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in);
- void recalculate_knots(
- Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in);
- friend class FilletChamferPointArrayParamKnotHolderEntity;
-
-private:
- FilletChamferPointArrayParam(const FilletChamferPointArrayParam &);
- FilletChamferPointArrayParam &operator=(const FilletChamferPointArrayParam &);
-
- SPKnotShapeType knot_shape;
- SPKnotModeType knot_mode;
- guint32 knot_color;
- int helper_size;
- int chamfer_steps;
- bool use_distance;
- Geom::PathVector hp;
-
- Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2;
- Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal;
-};
-
-class FilletChamferPointArrayParamKnotHolderEntity : public KnotHolderEntity {
-public:
- FilletChamferPointArrayParamKnotHolderEntity(FilletChamferPointArrayParam *p,
- unsigned int index);
- virtual ~FilletChamferPointArrayParamKnotHolderEntity() {}
-
- virtual void knot_set(Geom::Point const &p, Geom::Point const &origin,
- guint state);
- virtual Geom::Point knot_get() const;
- virtual void knot_click(guint state);
- virtual void knot_set_offset(Geom::Point offset);
-
- /*Checks whether the index falls within the size of the parameter's vector*/
- bool valid_index(unsigned int index) const {
- return (_pparam->_vector.size() > index);
- }
- ;
-
-private:
- FilletChamferPointArrayParam *_pparam;
- unsigned int _index;
-};
-
-} //namespace LivePathEffect
-
-} //namespace Inkscape
-
-#endif
diff --git a/src/live_effects/parameter/satellitesarray.cpp b/src/live_effects/parameter/satellitesarray.cpp
new file mode 100644
index 000000000..7626317a1
--- /dev/null
+++ b/src/live_effects/parameter/satellitesarray.cpp
@@ -0,0 +1,583 @@
+/*
+ * Author(s):
+ * Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
+ *
+ * Copyright (C) 2014 Author(s)
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "knotholder.h"
+#include "ui/dialog/lpe-fillet-chamfer-properties.h"
+#include "live_effects/parameter/satellitesarray.h"
+#include "live_effects/effect.h"
+#include "sp-lpe-item.h"
+#include "inkscape.h"
+#include <preferences.h>
+// TODO due to internal breakage in glibmm headers,
+// this has to be included last.
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+SatellitesArrayParam::SatellitesArrayParam(const Glib::ustring &label,
+ const Glib::ustring &tip,
+ const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr,
+ Effect *effect)
+ : ArrayParam<std::vector<Satellite> >(label, tip, key, wr, effect, 0), _knoth(NULL)
+{
+ _knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ _knot_mode = SP_KNOT_MODE_XOR;
+ _knot_color = 0xAAFF8800;
+ _helper_size = 0;
+ _use_distance = false;
+ _global_knot_hide = false;
+ _current_zoom = 0;
+ _effectType = FILLET_CHAMFER;
+ _last_pathvector_satellites = NULL;
+}
+
+
+void SatellitesArrayParam::set_oncanvas_looks(SPKnotShapeType shape,
+ SPKnotModeType mode,
+ guint32 color)
+{
+ _knot_shape = shape;
+ _knot_mode = mode;
+ _knot_color = color;
+}
+
+void SatellitesArrayParam::setPathVectorSatellites(PathVectorSatellites *pathVectorSatellites, bool write)
+{
+ _last_pathvector_satellites = pathVectorSatellites;
+ if (write) {
+ param_set_and_write_new_value(_last_pathvector_satellites->getSatellites());
+ } else {
+ param_setValue(_last_pathvector_satellites->getSatellites());
+ }
+}
+
+void SatellitesArrayParam::setUseDistance(bool use_knot_distance)
+{
+ _use_distance = use_knot_distance;
+}
+
+void SatellitesArrayParam::setCurrentZoom(double current_zoom)
+{
+ _current_zoom = current_zoom;
+}
+
+void SatellitesArrayParam::setGlobalKnotHide(bool global_knot_hide)
+{
+ _global_knot_hide = global_knot_hide;
+}
+void SatellitesArrayParam::setEffectType(EffectType et)
+{
+ _effectType = et;
+}
+
+void SatellitesArrayParam::setHelperSize(int hs)
+{
+ _helper_size = hs;
+ updateCanvasIndicators();
+}
+
+void SatellitesArrayParam::updateCanvasIndicators(bool mirror)
+{
+ if (!_last_pathvector_satellites) {
+ return;
+ }
+
+ if (!_hp.empty()) {
+ _hp.clear();
+ }
+ Geom::PathVector pathv = _last_pathvector_satellites->getPathVector();
+ if (pathv.empty()) {
+ return;
+ }
+ if (mirror == true) {
+ _hp.clear();
+ }
+ if (_effectType == FILLET_CHAMFER) {
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (_vector[i][j].hidden || //Ignore if hidden
+ (!_vector[i][j].has_mirror && mirror == true) || //Ignore if not have mirror and we are in mirror loop
+ _vector[i][j].amount == 0 || //no helper in 0 value
+ pathv[i].size() == j || //ignore last satellite in open paths with fillet chamfer effect
+ (!pathv[i].closed() && j == 0)) //ignore first satellites on open paths
+ {
+ continue;
+ }
+ Geom::Curve *curve_in = pathv[i][j].duplicate();
+ double pos = 0;
+ bool overflow = false;
+ double size_out = _vector[i][j].arcDistance(*curve_in);
+ double lenght_out = curve_in->length();
+ gint previous_index = j - 1; //Always are previous index because we skip first satellite on open paths
+ if (j == 0 && pathv[i].closed()) {
+ previous_index = pathv[i].size() - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ double lenght_in = pathv.curveAt(previous_index).length();
+ if (mirror) {
+ curve_in = const_cast<Geom::Curve *>(&pathv.curveAt(previous_index));
+ pos = _vector[i][j].time(size_out, true, *curve_in);
+ if (lenght_out < size_out) {
+ overflow = true;
+ }
+ } else {
+ pos = _vector[i][j].time(*curve_in);
+ if (lenght_in < size_out) {
+ overflow = true;
+ }
+ }
+ if (pos <= 0 || pos >= 1) {
+ continue;
+ }
+ Geom::Point point_a = curve_in->pointAt(pos);
+ Geom::Point deriv_a = unit_vector(derivative(curve_in->toSBasis()).pointAt(pos));
+ Geom::Rotate rot(Geom::Rotate::from_degrees(-90));
+ deriv_a = deriv_a * rot;
+ Geom::Point point_c = point_a - deriv_a * _helper_size;
+ Geom::Point point_d = point_a + deriv_a * _helper_size;
+ Geom::Ray ray_1(point_c, point_d);
+ char const *svgd = "M 1,0.25 0.5,0 1,-0.25 M 1,0.5 0,0 1,-0.5";
+ Geom::PathVector pathv = sp_svg_read_pathv(svgd);
+ Geom::Affine aff = Geom::Affine();
+ aff *= Geom::Scale(_helper_size);
+ if (mirror) {
+ aff *= Geom::Rotate(ray_1.angle() - Geom::rad_from_deg(90));
+ } else {
+ aff *= Geom::Rotate(ray_1.angle() - Geom::rad_from_deg(270));
+ }
+ aff *= Geom::Translate(curve_in->pointAt(pos));
+ pathv *= aff;
+ _hp.push_back(pathv[0]);
+ _hp.push_back(pathv[1]);
+ if (overflow) {
+ double diameter = _helper_size;
+ if (_helper_size == 0) {
+ diameter = 15;
+ char const *svgd;
+ svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 "
+ "0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z";
+ Geom::PathVector pathv = sp_svg_read_pathv(svgd);
+ aff = Geom::Affine();
+ aff *= Geom::Scale(diameter);
+ aff *= Geom::Translate(point_a - Geom::Point(diameter * 0.35, diameter * 0.35));
+ pathv *= aff;
+ _hp.push_back(pathv[0]);
+ } else {
+ char const *svgd;
+ svgd = "M 0 -1.32 A 1.32 1.32 0 0 0 -1.32 0 A 1.32 1.32 0 0 0 0 1.32 A "
+ "1.32 1.32 0 0 0 1.18 0.59 L 0 0 L 1.18 -0.59 A 1.32 1.32 0 0 0 "
+ "0 -1.32 z";
+ Geom::PathVector pathv = sp_svg_read_pathv(svgd);
+ aff = Geom::Affine();
+ aff *= Geom::Scale(_helper_size / 2.0);
+ if (mirror) {
+ aff *= Geom::Rotate(ray_1.angle() - Geom::rad_from_deg(90));
+ } else {
+ aff *= Geom::Rotate(ray_1.angle() - Geom::rad_from_deg(270));
+ }
+ aff *= Geom::Translate(curve_in->pointAt(pos));
+ pathv *= aff;
+ _hp.push_back(pathv[0]);
+ }
+ }
+ }
+ }
+ }
+ if (!_knot_reset_helper.empty()) {
+ _hp.insert(_hp.end(), _knot_reset_helper.begin(), _knot_reset_helper.end() );
+ }
+ if (mirror) {
+ updateCanvasIndicators(false);
+ }
+}
+void SatellitesArrayParam::updateCanvasIndicators()
+{
+ updateCanvasIndicators(true);
+}
+
+void SatellitesArrayParam::addCanvasIndicators(
+ SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ hp_vec.push_back(_hp);
+}
+
+void SatellitesArrayParam::param_transform_multiply(Geom::Affine const &postmul, bool /*set*/)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ if (prefs->getBool("/options/transform/rectcorners", true)) {
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (!_vector[i][j].is_time && _vector[i][j].amount > 0) {
+ _vector[i][j].amount = _vector[i][j].amount * ((postmul.expansionX() + postmul.expansionY()) / 2);
+ }
+ }
+ }
+ param_set_and_write_new_value(_vector);
+ }
+}
+
+void SatellitesArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
+ SPItem *item,
+ bool mirror)
+{
+ if (!_last_pathvector_satellites) {
+ return;
+ }
+ Geom::PathVector pathv = _last_pathvector_satellites->getPathVector();
+ size_t index = 0;
+ for (size_t i = 0; i < _vector.size(); ++i) {
+ for (size_t j = 0; j < _vector[i].size(); ++j) {
+ if (!_vector[i][j].has_mirror && mirror) {
+ continue;
+ }
+ SatelliteType type = _vector[i][j].satellite_type;
+ if (mirror && i == 0 && j == 0) {
+ index = index + _last_pathvector_satellites->getTotalSatellites();
+ }
+ using namespace Geom;
+ //If is for filletChamfer effect...
+ if (_effectType == FILLET_CHAMFER) {
+ const gchar *tip;
+ if (type == CHAMFER) {
+ tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else if (type == INVERSE_CHAMFER) {
+ tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else if (type == INVERSE_FILLET) {
+ tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ } else {
+ tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> reset");
+ }
+ FilletChamferKnotHolderEntity *e = new FilletChamferKnotHolderEntity(this, index);
+ e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip),_knot_shape, _knot_mode, _knot_color);
+ knotholder->add(e);
+ }
+ index++;
+ }
+ }
+ if (mirror) {
+ addKnotHolderEntities(knotholder, item, false);
+ }
+}
+
+void SatellitesArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
+ SPItem *item)
+{
+ _knoth = knotholder;
+ addKnotHolderEntities(knotholder, item, true);
+}
+
+FilletChamferKnotHolderEntity::FilletChamferKnotHolderEntity(
+ SatellitesArrayParam *p, size_t index)
+ : _pparam(p), _index(index) {}
+
+void FilletChamferKnotHolderEntity::knot_set(Geom::Point const &p,
+ Geom::Point const &/*origin*/,
+ guint state)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t path_index = index_data.first;
+ size_t curve_index = index_data.second;
+
+ Geom::Point s = snap_knot_position(p, state);
+ if (!valid_index(path_index, curve_index)) {
+ return;
+ }
+ Satellite satellite = _pparam->_vector[path_index][curve_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[path_index].closed() && curve_index == 0) ||//ignore first satellites on open paths
+ pathv[path_index].size() == curve_index) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ gint previous_index = curve_index - 1;
+ if (curve_index == 0 && pathv[path_index].closed()) {
+ previous_index = pathv[path_index].size() - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ Geom::Curve const &curve_in = pathv[path_index][previous_index];
+ double mirror_time = Geom::nearest_time(s, curve_in);
+ Geom::Point mirror = curve_in.pointAt(mirror_time);
+ double normal_time = Geom::nearest_time(s, pathv[path_index][curve_index]);
+ Geom::Point normal = pathv[path_index][curve_index].pointAt(normal_time);
+ double distance_mirror = Geom::distance(mirror,s);
+ double distance_normal = Geom::distance(normal,s);
+ if (Geom::are_near(s, pathv[path_index][curve_index].initialPoint(), 1.5 / _pparam->_current_zoom)) {
+ satellite.amount = 0;
+ } else if (distance_mirror < distance_normal) {
+ double time_start = 0;
+ Satellites satellites = _pparam->_last_pathvector_satellites->getSatellites();
+ time_start = satellites[path_index][previous_index].time(curve_in);
+ if (time_start > mirror_time) {
+ mirror_time = time_start;
+ }
+ double size = arcLengthAt(mirror_time, curve_in);
+ double amount = curve_in.length() - size;
+ if (satellite.is_time) {
+ amount = timeAtArcLength(amount, pathv[path_index][curve_index]);
+ }
+ satellite.amount = amount;
+ } else {
+ satellite.setPosition(s, pathv[path_index][curve_index]);
+ }
+ _pparam->_knot_reset_helper.clear();
+ if (satellite.amount == 0) {
+ char const *svgd;
+ svgd = "M -5.39,8.78 -9.13,5.29 -10.38,10.28 Z M -7.22,7.07 -3.43,3.37 m -1.95,-12.16 -3.74,3.5 -1.26,-5 z "
+ "m -1.83,1.71 3.78,3.7 M 5.24,8.78 8.98,5.29 10.24,10.28 Z "
+ "M 7.07,7.07 3.29,3.37 M 5.24,-8.78 l 3.74,3.5 1.26,-5 z M 7.07,-7.07 3.29,-3.37";
+ _pparam->_knot_reset_helper = sp_svg_read_pathv(svgd);
+ _pparam->_knot_reset_helper *= Geom::Affine(_pparam->_helper_size * 0.1,0,0,_pparam->_helper_size * 0.1,0,0) * Geom::Translate(pathv[path_index][curve_index].initialPoint());
+ }
+ _pparam->_vector[path_index][curve_index] = satellite;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+}
+
+Geom::Point FilletChamferKnotHolderEntity::knot_get() const
+{
+ if (!_pparam->_last_pathvector_satellites || _pparam->_global_knot_hide) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Point tmp_point;
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t path_index = index_data.first;
+ size_t curve_index = index_data.second;
+ if (!valid_index(path_index, curve_index)) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Satellite satellite = _pparam->_vector[path_index][curve_index];
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[path_index].closed() && curve_index == 0) ||//ignore first satellites on open paths
+ pathv[path_index].size() == curve_index) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ this->knot->show();
+ if (is_mirror) {
+ gint previous_index = curve_index - 1;
+ if (curve_index == 0 && pathv[path_index].closed()) {
+ previous_index = pathv[path_index].size() - 1;
+ }
+ if ( previous_index < 0 ) {
+ return Geom::Point(Geom::infinity(), Geom::infinity());
+ }
+ Geom::Curve const &curve_in = pathv[path_index][previous_index];
+ double s = satellite.arcDistance(pathv[path_index][curve_index]);
+ double t = satellite.time(s, true, curve_in);
+ if (t > 1) {
+ t = 1;
+ }
+ if (t < 0) {
+ t = 0;
+ }
+ double time_start = 0;
+ time_start = _pparam->_last_pathvector_satellites->getSatellites()[path_index][previous_index].time(curve_in);
+ if (time_start > t) {
+ t = time_start;
+ }
+ tmp_point = (curve_in).pointAt(t);
+ } else {
+ tmp_point = satellite.getPosition(pathv[path_index][curve_index]);
+ }
+ Geom::Point const canvas_point = tmp_point;
+ _pparam->updateCanvasIndicators();
+ return canvas_point;
+}
+
+void FilletChamferKnotHolderEntity::knot_click(guint state)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t path_index = index_data.first;
+ size_t curve_index = index_data.second;
+ if (!valid_index(path_index, curve_index)) {
+ return;
+ }
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if ((!pathv[path_index].closed() && curve_index == 0) ||//ignore first satellites on open paths
+ pathv[path_index].size() == curve_index) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ if (state & GDK_CONTROL_MASK) {
+ if (state & GDK_MOD1_MASK) {
+ _pparam->_vector[path_index][curve_index].amount = 0.0;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ } else {
+ using namespace Geom;
+ SatelliteType type = _pparam->_vector[path_index][curve_index].satellite_type;
+ switch (type) {
+ case FILLET:
+ type = INVERSE_FILLET;
+ break;
+ case INVERSE_FILLET:
+ type = CHAMFER;
+ break;
+ case CHAMFER:
+ type = INVERSE_CHAMFER;
+ break;
+ default:
+ type = FILLET;
+ break;
+ }
+ _pparam->_vector[path_index][curve_index].satellite_type = type;
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ const gchar *tip;
+ if (type == CHAMFER) {
+ tip = _("<b>Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else if (type == INVERSE_CHAMFER) {
+ tip = _("<b>Inverse Chamfer</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else if (type == INVERSE_FILLET) {
+ tip = _("<b>Inverse Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ } else {
+ tip = _("<b>Fillet</b>: <b>Ctrl+Click</b> toggles type, "
+ "<b>Shift+Click</b> open dialog, "
+ "<b>Ctrl+Alt+Click</b> resets");
+ }
+ this->knot->tip = g_strdup(tip);
+ this->knot->show();
+ }
+ } else if (state & GDK_SHIFT_MASK) {
+ double amount = _pparam->_vector[path_index][curve_index].amount;
+ gint previous_index = curve_index - 1;
+ if (curve_index == 0 && pathv[path_index].closed()) {
+ previous_index = pathv[path_index].size() - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ if (!_pparam->_use_distance && !_pparam->_vector[path_index][curve_index].is_time) {
+ amount = _pparam->_vector[path_index][curve_index].lenToRad(amount, pathv[path_index][previous_index], pathv[path_index][curve_index], _pparam->_vector[path_index][previous_index]);
+ }
+ bool aprox = false;
+ Geom::D2<Geom::SBasis> d2_out = pathv[path_index][curve_index].toSBasis();
+ Geom::D2<Geom::SBasis> d2_in = pathv[path_index][previous_index].toSBasis();
+ aprox = ((d2_in)[0].degreesOfFreedom() != 2 ||
+ d2_out[0].degreesOfFreedom() != 2) &&
+ !_pparam->_use_distance
+ ? true
+ : false;
+ Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog(
+ this->desktop, amount, this, _pparam->_use_distance,
+ aprox, _pparam->_vector[path_index][curve_index]);
+
+ }
+}
+
+void FilletChamferKnotHolderEntity::knot_set_offset(Satellite satellite)
+{
+ if (!_pparam->_last_pathvector_satellites) {
+ return;
+ }
+ size_t total_satellites = _pparam->_last_pathvector_satellites->getTotalSatellites();
+ bool is_mirror = false;
+ size_t index = _index;
+ if (_index >= total_satellites) {
+ index = _index - total_satellites;
+ is_mirror = true;
+ }
+ std::pair<size_t, size_t> index_data = _pparam->_last_pathvector_satellites->getIndexData(index);
+ size_t path_index = index_data.first;
+ size_t curve_index = index_data.second;
+ if (!valid_index(path_index, curve_index)) {
+ return;
+ }
+ Geom::PathVector pathv = _pparam->_last_pathvector_satellites->getPathVector();
+ if (satellite.hidden ||
+ (!pathv[path_index].closed() && curve_index == 0) ||//ignore first satellites on open paths
+ pathv[path_index].size() == curve_index) //ignore last satellite in open paths with fillet chamfer effect
+ {
+ return;
+ }
+ double amount = satellite.amount;
+ double max_amount = amount;
+ if (!_pparam->_use_distance && !satellite.is_time) {
+ gint previous_index = curve_index - 1;
+ if (curve_index == 0 && pathv[path_index].closed()) {
+ previous_index = pathv[path_index].size() - 1;
+ }
+ if ( previous_index < 0 ) {
+ return;
+ }
+ amount = _pparam->_vector[path_index][curve_index].radToLen(amount, pathv[path_index][previous_index], pathv[path_index][curve_index]);
+ if (max_amount > 0 && amount == 0) {
+ amount = _pparam->_vector[path_index][curve_index].amount;
+ }
+ }
+ satellite.amount = amount;
+ _pparam->_vector[path_index][curve_index] = satellite;
+ this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
+ SPLPEItem *splpeitem = dynamic_cast<SPLPEItem *>(item);
+ if (splpeitem) {
+ sp_lpe_item_update_patheffect(splpeitem, false, false);
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* 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 :
diff --git a/src/live_effects/parameter/satellitesarray.h b/src/live_effects/parameter/satellitesarray.h
new file mode 100644
index 000000000..5ae372ac2
--- /dev/null
+++ b/src/live_effects/parameter/satellitesarray.h
@@ -0,0 +1,114 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_SATELLITES_ARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_SATELLITES_ARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ * Copyright (C) Jabiertxo Arraiza Cenoz <jabier.arraiza@marker.es>
+ * Special thanks to Johan Engelen for the base of the effect -powerstroke-
+ * Also to ScislaC for point me to the idea
+ * Also su_v for his construvtive feedback and time
+ * To Nathan Hurst for his review and help on refactor
+ * and finaly to Liam P. White for his big help on coding, that save me a lot of
+ * hours
+ *
+ *
+ * This parameter act as bridge from pathVectorSatellites class to serialize it as a LPE
+ * parameter
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/array.h"
+#include "live_effects/effect-enum.h"
+#include "helper/geom-pathvectorsatellites.h"
+#include "knot-holder-entity.h"
+#include <glib.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class FilletChamferKnotHolderEntity;
+
+class SatellitesArrayParam : public ArrayParam<std::vector<Satellite> > {
+public:
+ SatellitesArrayParam(const Glib::ustring &label, const Glib::ustring &tip,
+ const Glib::ustring &key,
+ Inkscape::UI::Widget::Registry *wr, Effect *effect);
+
+ virtual Gtk::Widget *param_newWidget()
+ {
+ return NULL;
+ }
+ virtual void setHelperSize(int hs);
+ virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item);
+ virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item, bool mirror);
+ virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
+ virtual void updateCanvasIndicators();
+ virtual void updateCanvasIndicators(bool mirror);
+ virtual bool providesKnotHolderEntities() const
+ {
+ return true;
+ }
+ void param_transform_multiply(Geom::Affine const &postmul, bool /*set*/);
+ void setUseDistance(bool use_knot_distance);
+ void setCurrentZoom(double current_zoom);
+ void setGlobalKnotHide(bool global_knot_hide);
+ void setEffectType(EffectType et);
+ void setPathVectorSatellites(PathVectorSatellites *pathVectorSatellites, bool write = true);
+ void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+
+ friend class FilletChamferKnotHolderEntity;
+ friend class LPEFilletChamfer;
+
+protected:
+ KnotHolder *_knoth;
+
+private:
+ SatellitesArrayParam(const SatellitesArrayParam &);
+ SatellitesArrayParam &operator=(const SatellitesArrayParam &);
+
+ SPKnotShapeType _knot_shape;
+ SPKnotModeType _knot_mode;
+ guint32 _knot_color;
+ Geom::PathVector _hp;
+ Geom::PathVector _knot_reset_helper;
+ int _helper_size;
+ bool _use_distance;
+ bool _global_knot_hide;
+ double _current_zoom;
+ EffectType _effectType;
+ PathVectorSatellites *_last_pathvector_satellites;
+
+};
+
+class FilletChamferKnotHolderEntity : public KnotHolderEntity {
+public:
+ FilletChamferKnotHolderEntity(SatellitesArrayParam *p, size_t index);
+ virtual ~FilletChamferKnotHolderEntity()
+ {
+ _pparam->_knoth = NULL;
+ }
+
+ virtual void knot_set(Geom::Point const &p, Geom::Point const &origin,
+ guint state);
+ virtual Geom::Point knot_get() const;
+ virtual void knot_click(guint state);
+ void knot_set_offset(Satellite);
+ /** Checks whether the index falls within the size of the parameter's vector
+ */
+ bool valid_index(size_t index,size_t subindex) const
+ {
+ return (_pparam->_vector.size() > index && _pparam->_vector[index].size() > subindex);
+ };
+
+private:
+ SatellitesArrayParam *_pparam;
+ size_t _index;
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp
index 0baf15e6b..82c6035d4 100644
--- a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp
+++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp
@@ -43,7 +43,6 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog()
//todo: get tha max aloable infinity freeze the widget
_fillet_chamfer_position_numeric.set_range(0., SCALARPARAM_G_MAXDOUBLE);
_fillet_chamfer_position_numeric.set_hexpand();
-
_fillet_chamfer_position_label.set_label(_("Radius (pixels):"));
_fillet_chamfer_position_label.set_alignment(1.0, 0.5);
@@ -54,7 +53,6 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog()
//todo: get tha max aloable infinity freeze the widget
_fillet_chamfer_chamfer_subdivisions.set_range(0, SCALARPARAM_G_MAXDOUBLE);
_fillet_chamfer_chamfer_subdivisions.set_hexpand();
-
_fillet_chamfer_chamfer_subdivisions_label.set_label(_("Chamfer subdivisions:"));
_fillet_chamfer_chamfer_subdivisions_label.set_alignment(1.0, 0.5);
@@ -104,23 +102,26 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog()
FilletChamferPropertiesDialog::~FilletChamferPropertiesDialog()
{
- _set_desktop(NULL);
+ _setDesktop(NULL);
}
void FilletChamferPropertiesDialog::showDialog(
- SPDesktop *desktop, Geom::Point knotpoint,
+ SPDesktop *desktop,
+ double _amount,
const Inkscape::LivePathEffect::
- FilletChamferPointArrayParamKnotHolderEntity *pt,
- bool use_distance,
- bool aprox_radius)
+ FilletChamferKnotHolderEntity *pt,
+ bool _use_distance,
+ bool _aprox_radius,
+ Satellite _satellite)
{
FilletChamferPropertiesDialog *dialog = new FilletChamferPropertiesDialog();
- dialog->_set_desktop(desktop);
- dialog->_set_use_distance(use_distance);
- dialog->_set_aprox(aprox_radius);
- dialog->_set_knot_point(knotpoint);
- dialog->_set_pt(pt);
+ dialog->_setDesktop(desktop);
+ dialog->_setUseDistance(_use_distance);
+ dialog->_setAprox(_aprox_radius);
+ dialog->_setAmount(_amount);
+ dialog->_setSatellite(_satellite);
+ dialog->_setPt(pt);
dialog->set_title(_("Modify Fillet-Chamfer"));
dialog->_apply_button.set_label(_("_Modify"));
@@ -135,34 +136,38 @@ void FilletChamferPropertiesDialog::showDialog(
void FilletChamferPropertiesDialog::_apply()
{
- double d_width;
+
double d_pos = _fillet_chamfer_position_numeric.get_value();
- if (d_pos) {
+ if (d_pos >= 0) {
if (_fillet_chamfer_type_fillet.get_active() == true) {
- d_width = 1;
+ _satellite.satellite_type = FILLET;
} else if (_fillet_chamfer_type_inverse_fillet.get_active() == true) {
- d_width = 2;
+ _satellite.satellite_type = INVERSE_FILLET;
} else if (_fillet_chamfer_type_inverse_chamfer.get_active() == true) {
- d_width = _fillet_chamfer_chamfer_subdivisions.get_value() + 4000;
+ _satellite.satellite_type = INVERSE_CHAMFER;
} else {
- d_width = _fillet_chamfer_chamfer_subdivisions.get_value() + 3000;
+ _satellite.satellite_type = CHAMFER;
}
if (_flexible) {
if (d_pos > 99.99999 || d_pos < 0) {
d_pos = 0;
}
- d_pos = _index + (d_pos / 100);
- } else {
- d_pos = d_pos * -1;
+ d_pos = d_pos / 100;
+ }
+ _satellite.amount = d_pos;
+ size_t steps = (size_t)_fillet_chamfer_chamfer_subdivisions.get_value();
+ if (steps < 1) {
+ steps = 1;
}
- _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width));
+ _satellite.steps = steps;
+ _knotpoint->knot_set_offset(_satellite);
}
_close();
}
void FilletChamferPropertiesDialog::_close()
{
- _set_desktop(NULL);
+ _setDesktop(NULL);
destroy_();
Glib::signal_idle().connect(
sigc::bind_return(
@@ -184,62 +189,68 @@ void FilletChamferPropertiesDialog::_handleButtonEvent(GdkEventButton *event)
}
}
-void FilletChamferPropertiesDialog::_set_knot_point(Geom::Point knotpoint)
+void FilletChamferPropertiesDialog::_setSatellite(Satellite satellite)
{
double position;
std::string distance_or_radius = std::string(_("Radius"));
- if(aprox){
+ if (_aprox) {
distance_or_radius = std::string(_("Radius approximated"));
}
- if(use_distance){
+ if (_use_distance) {
distance_or_radius = std::string(_("Knot distance"));
}
- if (knotpoint.x() > 0) {
- double intpart;
- position = modf(knotpoint[Geom::X], &intpart) * 100;
+ if (satellite.is_time) {
+ position = _amount * 100;
_flexible = true;
- _index = intpart;
_fillet_chamfer_position_label.set_label(_("Position (%):"));
} else {
_flexible = false;
std::string posConcat = Glib::ustring::compose (_("%1:"), distance_or_radius);
_fillet_chamfer_position_label.set_label(_(posConcat.c_str()));
- position = knotpoint[Geom::X] * -1;
+ position = _amount;
}
_fillet_chamfer_position_numeric.set_value(position);
- if (knotpoint.y() == 1) {
+ _fillet_chamfer_chamfer_subdivisions.set_value(satellite.steps);
+ if (satellite.satellite_type == FILLET) {
_fillet_chamfer_type_fillet.set_active(true);
- } else if (knotpoint.y() == 2) {
+ } else if (satellite.satellite_type == INVERSE_FILLET) {
_fillet_chamfer_type_inverse_fillet.set_active(true);
- } else if (knotpoint.y() >= 3000 && knotpoint.y() < 4000) {
- _fillet_chamfer_chamfer_subdivisions.set_value(knotpoint.y() - 3000);
+ } else if (satellite.satellite_type == CHAMFER) {
_fillet_chamfer_type_chamfer.set_active(true);
- } else if (knotpoint.y() >= 4000 && knotpoint.y() < 5000) {
- _fillet_chamfer_chamfer_subdivisions.set_value(knotpoint.y() - 4000);
+ } else if (satellite.satellite_type == INVERSE_CHAMFER) {
_fillet_chamfer_type_inverse_chamfer.set_active(true);
}
+ _satellite = satellite;
}
-void FilletChamferPropertiesDialog::_set_pt(
+void FilletChamferPropertiesDialog::_setPt(
const Inkscape::LivePathEffect::
- FilletChamferPointArrayParamKnotHolderEntity *pt)
+ FilletChamferKnotHolderEntity *pt)
{
_knotpoint = const_cast<
- Inkscape::LivePathEffect::FilletChamferPointArrayParamKnotHolderEntity *>(
+ Inkscape::LivePathEffect::FilletChamferKnotHolderEntity *>(
pt);
}
-void FilletChamferPropertiesDialog::_set_use_distance(bool use_knot_distance)
+
+void FilletChamferPropertiesDialog::_setAmount(double amount)
+{
+ _amount = amount;
+}
+
+
+
+void FilletChamferPropertiesDialog::_setUseDistance(bool use_knot_distance)
{
- use_distance = use_knot_distance;
+ _use_distance = use_knot_distance;
}
-void FilletChamferPropertiesDialog::_set_aprox(bool aprox_radius)
+void FilletChamferPropertiesDialog::_setAprox(bool _aprox_radius)
{
- aprox = aprox_radius;
+ _aprox = _aprox_radius;
}
-void FilletChamferPropertiesDialog::_set_desktop(SPDesktop *desktop)
+void FilletChamferPropertiesDialog::_setDesktop(SPDesktop *desktop)
{
if (desktop) {
Inkscape::GC::anchor(desktop);
diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.h b/src/ui/dialog/lpe-fillet-chamfer-properties.h
index c74e3f011..4021d6152 100644
--- a/src/ui/dialog/lpe-fillet-chamfer-properties.h
+++ b/src/ui/dialog/lpe-fillet-chamfer-properties.h
@@ -10,7 +10,7 @@
#include <2geom/point.h>
#include <gtkmm.h>
-#include "live_effects/parameter/filletchamferpointarray.h"
+#include "live_effects/parameter/satellitesarray.h"
class SPDesktop;
@@ -23,20 +23,22 @@ public:
FilletChamferPropertiesDialog();
virtual ~FilletChamferPropertiesDialog();
- Glib::ustring getName() const {
+ Glib::ustring getName() const
+ {
return "LayerPropertiesDialog";
}
- static void showDialog(SPDesktop *desktop, Geom::Point knotpoint,
+ static void showDialog(SPDesktop *desktop, double _amount,
const Inkscape::LivePathEffect::
- FilletChamferPointArrayParamKnotHolderEntity *pt,
- bool use_distance,
- bool aprox_radius);
+ FilletChamferKnotHolderEntity *pt,
+ bool _use_distance,
+ bool _aprox_radius,
+ Satellite _satellite);
protected:
SPDesktop *_desktop;
- Inkscape::LivePathEffect::FilletChamferPointArrayParamKnotHolderEntity *
+ Inkscape::LivePathEffect::FilletChamferKnotHolderEntity *
_knotpoint;
Gtk::Label _fillet_chamfer_position_label;
@@ -51,36 +53,40 @@ protected:
Gtk::Grid _layout_table;
bool _position_visible;
- double _index;
Gtk::Button _close_button;
Gtk::Button _apply_button;
sigc::connection _destroy_connection;
- static FilletChamferPropertiesDialog &_instance() {
+ static FilletChamferPropertiesDialog &_instance()
+ {
static FilletChamferPropertiesDialog instance;
return instance;
}
- void _set_desktop(SPDesktop *desktop);
- void _set_pt(const Inkscape::LivePathEffect::
- FilletChamferPointArrayParamKnotHolderEntity *pt);
- void _set_use_distance(bool use_knot_distance);
- void _set_aprox(bool aprox_radius);
- void _apply();
- void _close();
- bool _flexible;
- bool use_distance;
- bool aprox;
- void _set_knot_point(Geom::Point knotpoint);
+ void _setDesktop(SPDesktop *desktop);
+ void _setPt(const Inkscape::LivePathEffect::
+ FilletChamferKnotHolderEntity *pt);
+ void _setUseDistance(bool use_knot_distance);
+ void _setAprox(bool aprox_radius);
+ void _setAmount(double amount);
+ void _setSatellite(Satellite satellite);
void _prepareLabelRenderer(Gtk::TreeModel::const_iterator const &row);
bool _handleKeyEvent(GdkEventKey *event);
void _handleButtonEvent(GdkEventButton *event);
+ void _apply();
+ void _close();
+ bool _flexible;
+ Satellite _satellite;
+ bool _use_distance;
+ double _amount;
+ bool _aprox;
+
friend class Inkscape::LivePathEffect::
- FilletChamferPointArrayParamKnotHolderEntity;
+ FilletChamferKnotHolderEntity;
private:
FilletChamferPropertiesDialog(
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index 5746a8f86..f2899dd01 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -12,7 +12,6 @@
#include "live_effects/lpe-powerstroke.h"
#include "live_effects/lpe-bspline.h"
-#include "live_effects/lpe-fillet-chamfer.h"
#include <2geom/bezier-utils.h>
#include <2geom/path-sink.h>
#include "ui/tool/path-manipulator.h"
@@ -1364,13 +1363,6 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE)
lpe_pwr->adjustForNewPath(pathv);
}
}
- this_effect = _path->getPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER);
- if(this_effect){
- LivePathEffect::LPEFilletChamfer *lpe_fll = dynamic_cast<LivePathEffect::LPEFilletChamfer*>(this_effect->getLPEObj()->get_lpe());
- if (lpe_fll) {
- lpe_fll->adjustForNewPath(pathv);
- }
- }
}
}