29#include <glibmm/i18n.h>
34 {
FM_AUTO, N_(
"Auto"),
"auto" },
35 {
FM_ARC, N_(
"Force arc"),
"arc" },
36 {
FM_BEZIER, N_(
"Force bezier"),
"bezier" }
42 unit(_(
"Unit:"), _(
"Unit"),
"unit", &wr, this,
"px"),
43 nodesatellites_param(
"NodeSatellite_param",
"NodeSatellite_param",
44 "nodesatellites_param", &wr, this),
45 method(_(
"Method:"), _(
"Method to calculate the fillet or chamfer"),
47 mode(_(
"Mode:"), _(
"Mode, e.g. fillet or chamfer"),
48 "mode", &wr, this,
"F", true),
49 radius(_(
"Radius:"), _(
"Radius, in unit or %"),
"radius", &wr,
51 chamfer_steps(_(
"Chamfer steps:"), _(
"Chamfer steps"),
"chamfer_steps",
53 flexible(_(
"Radius in %"), _(
"Flexible radius size (%)"),
54 "flexible", &wr, this, false),
55 only_selected(_(
"Change only selected nodes"),
56 _(
"Change only selected nodes"),
"only_selected", &wr, this,
58 use_knot_distance(_(
"Use knots distance instead radius"),
59 _(
"Use knots distance instead radius"),
60 "use_knot_distance", &wr, this, true),
61 hide_knots(_(
"Hide knots"), _(
"Hide knots"),
"hide_knots", &wr, this,
63 apply_no_radius(_(
"Apply changes if radius = 0"), _(
"Apply changes if radius = 0"),
"apply_no_radius", &wr, this, true),
64 apply_with_radius(_(
"Apply changes if radius > 0"), _(
"Apply changes if radius > 0"),
"apply_with_radius", &wr, this, true),
65 _pathvector_nodesatellites(nullptr)
69 if (satellites_param){
100 auto shape = cast<SPShape>(splpeitem);
102 g_warning(
"LPE Fillet/Chamfer can only be applied to shapes (not groups).");
104 item->removeCurrentPathEffect(
false);
106 auto rect = cast<SPRect>(splpeitem);
111 double a = rect->getVisibleRx();
112 a = std::max(a, rect->getVisibleRy());
114 rect->setRx(
true, 0);
115 rect->setRy(
true, 0);
132 power *= ((trans.expansionX() + trans.expansionY()) / 2.0);
137 std::map<std::string, NodeSatelliteType> gchar_map_to_nodesatellite_type = boost::assign::map_list_of(
141 auto it = gchar_map_to_nodesatellite_type.find(mode_str.raw());
143 if (it != gchar_map_to_nodesatellite_type.end()) {
144 nodesatellite_type = it->second;
163 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
167 if (!param->widget_is_visible)
continue;
169 auto const widg = param->param_newWidget();
172 if (param->param_key ==
"radius") {
176 scalar.getSpinButton().set_width_chars(6);
177 }
else if (param->param_key ==
"chamfer_steps") {
181 scalar.getSpinButton().set_width_chars(3);
186 if (
auto const tip = param->param_getTooltip()) {
187 widg->set_tooltip_markup(*tip);
189 widg->set_tooltip_text({});
190 widg->set_has_tooltip(
false);
196 auto const fillet_container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
197 Gtk::Button *fillet = Gtk::make_managed<Gtk::Button>(Glib::ustring(_(
"Fillet")));
198 fillet->signal_clicked().connect(
202 auto const inverse_fillet = Gtk::make_managed<Gtk::Button>(Glib::ustring(_(
"Inverse fillet")));
203 inverse_fillet->signal_clicked().connect(sigc::bind(
205 UI::pack_start(*fillet_container, *inverse_fillet,
true,
true, 2);
207 auto const chamfer_container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
208 auto const chamfer = Gtk::make_managed<Gtk::Button>(Glib::ustring(_(
"Chamfer")));
209 chamfer->signal_clicked().connect(
213 auto const inverse_chamfer = Gtk::make_managed<Gtk::Button>(Glib::ustring(_(
"Inverse chamfer")));
214 inverse_chamfer->signal_clicked().connect(sigc::bind(
216 UI::pack_start(*chamfer_container, *inverse_chamfer,
true,
true, 2);
233 if (lpeitems.size() == 1) {
236 power *= ((trans.expansionX() + trans.expansionY()) / 2.0);
259 std::map<NodeSatelliteType, gchar const *> nodesatellite_type_to_gchar_map = boost::assign::map_list_of(
261 mode.
param_setValue((Glib::ustring)nodesatellite_type_to_gchar_map.at(nodesatellitetype));
274 if (lpeitems.size() == 1) {
281 for (
size_t i = 0; i < nodesatellites.size(); ++i) {
282 for (
size_t j = 0; j < nodesatellites[i].size(); ++j) {
285 nodesatellites[i][j].setSelected(
true);
287 nodesatellites[i][j].setSelected(
false);
306 if (nodesatellites.empty()) {
310 for (
size_t i = 0; i < nodesatellites.size(); ++i) {
311 for (
size_t j = 0; j < nodesatellites[i].size(); ++j) {
319 if (nodesatellites[i][j].is_time !=
flexible) {
320 nodesatellites[i][j].is_time =
flexible;
321 double amount = nodesatellites[i][j].amount;
322 if (nodesatellites[i][j].is_time) {
324 nodesatellites[i][j].amount = time;
327 nodesatellites[i][j].amount =
size;
332 nodesatellites[i][j].setSelected(
true);
336 if (pathv.
size() > i && !pathv[i].closed()) {
337 nodesatellites[i].front().amount = 0;
338 nodesatellites[i].back().amount = 0;
351 std::map<std::string, NodeSatelliteType> gchar_map_to_nodesatellite_type = boost::assign::map_list_of(
354 auto it = gchar_map_to_nodesatellite_type.find(mode_str.raw());
355 if (it != gchar_map_to_nodesatellite_type.end()) {
356 nodesatellite_type = it->second;
374 g_warning(
"LPE Fillet can only be applied to shapes (not groups).");
386 hp_vec.push_back(
_hp);
393 double path_subdivision = 1.0 / steps;
394 for (
size_t i = 1; i < steps; i++) {
407 const double GAP_HELPER = 0.00001;
409 std::size_t path = -1;
410 const double K = (4.0 / 3.0) * (
sqrt(2.0) - 1.0);
413 for (
const auto &path_it : pathv) {
417 std::size_t
curve = -1;
420 if (path_it.closed()) {
421 auto const &closingline = path_it.back_closed();
424 if (
are_near(closingline.initialPoint(), closingline.finalPoint())) {
429 curve_endit = path_it.end_open();
433 while (curve_it1 != curve_endit) {
435 size_t next_index =
curve + 1;
436 if (
curve == tcurves - 1 && pathv[path].closed()) {
440 if (
curve == tcurves - 1 && !pathv[path].closed()) {
443 Geom::Curve *last_curve = curve_it1->portion(time0, 1);
445 tmp_path.
append(*last_curve);
451 NodeSatellite nodesatellite = nodesatellites.at(path).at(next_index);
454 if (!path_it.closed()) {
457 time0 = nodesatellites[path][0].
time(*curve_it1);
461 double time1 = nodesatellite.
time(s,
true, (*curve_it1));
462 double time2 = nodesatellite.
time(curve_it2);
463 if (time1 <= time0) {
469 Geom::Curve *knot_curve_1 = curve_it1->portion(time0, time1);
474 tmp_path.
start((*curve_it1).pointAt(time0));
481 end_arc_point = curve_it2.
pointAt(time2 - GAP_HELPER);
483 if (time1 == time0) {
484 start_arc_point = curve_it1->pointAt(time1 + GAP_HELPER);
488 double k1 =
distance(start_arc_point, curveit1) *
K;
492 Geom::Ray ray_1(start_arc_point, curveit1);
493 Geom::Ray ray_2(curveit2, end_arc_point);
495 ray_1.
setPoints((*cubic_1)[2], start_arc_point);
498 ray_2.
setPoints(end_arc_point, (*cubic_2)[1]);
500 bool ccw_toggle =
cross(curveit1 - start_arc_point, end_arc_point - start_arc_point) < 0;
502 double handle_angle_1 = ray_1.
angle() - angle;
503 double handle_angle_2 = ray_2.
angle() + angle;
505 handle_angle_1 = ray_1.
angle() + angle;
506 handle_angle_2 = ray_2.
angle() - angle;
508 Geom::Point handle_1 = Geom::Point::polar(ray_1.
angle(), k1) + start_arc_point;
509 Geom::Point handle_2 = end_arc_point - Geom::Point::polar(ray_2.
angle(), k2);
510 Geom::Point inverse_handle_1 = Geom::Point::polar(handle_angle_1, k1) + start_arc_point;
511 Geom::Point inverse_handle_2 = end_arc_point - Geom::Point::polar(handle_angle_2, k2);
513 handle_1 = start_arc_point;
514 inverse_handle_1 = start_arc_point;
518 end_arc_point = curve_it2.
pointAt(time2);
520 if (time1 == time0) {
521 start_arc_point = curve_it1->pointAt(time0);
523 if (time1 != 1 && !
Geom::are_near(angle, Geom::rad_from_deg(360)) &&
524 !curve_it1->isDegenerate() && !curve_it2.
isDegenerate())
526 if (time1 != time0 || (time1 == 1 && time0 == 1)) {
528 tmp_path.
append(*knot_curve_1);
532 size_t steps = nodesatellite.
steps;
533 if (!steps) steps = 1;
535 Geom::Line const angled_line(start_arc_point, end_arc_point);
550 ccw_toggle = !ccw_toggle;
582 ccw_toggle = !ccw_toggle;
592 tmp_path.
append(*knot_curve_1);
598 if (path_it.closed()) {
Affine inverse() const
Compute the inverse matrix.
Abstract continuous curve on a plane defined on [0,1].
virtual Point initialPoint() const =0
Retrieve the start of the curve.
virtual Point finalPoint() const =0
Retrieve the end of the curve.
virtual bool isDegenerate() const =0
Check whether the curve has exactly zero length.
virtual void setInitial(Point const &v)=0
Change the starting point of the curve.
virtual Curve * portion(Coord a, Coord b) const =0
Create a curve that corresponds to a part of this curve.
virtual Point pointAt(Coord t) const
Evaluate the curve at a specified time value.
Infinite line on a plane.
size_type size() const
Get the number of paths in the vector.
void push_back(Path const &path)
Append a path at the end.
Path & at(size_type index)
void clear()
Remove all paths from the vector.
bool empty() const
Check whether the vector contains any paths.
Sequence of contiguous curves, aka spline.
Point finalPoint() const
Get the last point in the path.
void close(bool closed=true)
Set whether the path is closed.
Point pointAt(Coord t) const
Get the point at the specified time value.
void append(Curve *curve)
Add a new curve to the end of the path.
Curve const & at(size_type i) const
Access a curve by index.
void appendNew(Args &&... args)
Append a new curve to the path.
void start(Point const &p)
Two-dimensional point that doubles as a vector.
Straight ray from a specific point to infinity.
void setPoints(Point const &a, Point const &b)
std::vector< StorageType > const & data() const
void param_setValue(bool newvalue)
std::vector< Parameter * > param_vector
void registerParameter(Parameter *param)
bool _provides_path_adjustment
bool isNodePointSelected(Geom::Point const &nodePoint) const
std::vector< SPLPEItem * > getCurrrentLPEItems() const
Geom::PathVector pathvector_before_effect
EffectType effectType() const
Geom::PathVector pathvector_after_effect
bool _provides_knotholder_entities
LivePathEffectObject * getLPEObj()
void param_setValue(Glib::ustring newvalue, bool write=false)
Glib::ustring param_getSVGValue() const override
BoolParam apply_no_radius
void doBeforeEffect(SPLPEItem const *lpeItem) override
Is performed each time before the effect is updated.
LPEFilletChamfer(LivePathEffectObject *lpeobject)
void addChamferSteps(Geom::Path &tmp_path, Geom::Path path_chamfer, Geom::Point end_arc_point, size_t steps)
Glib::ustring previous_unit
void adjustForNewPath() override
BoolParam apply_with_radius
void updateChamferSteps()
EnumParam< Filletmethod > method
BoolParam use_knot_distance
ScalarParam chamfer_steps
void updateNodeSatelliteType(NodeSatelliteType nodesatellitetype)
PathVectorNodeSatellites * _pathvector_nodesatellites
void doOnApply(SPLPEItem const *lpeItem) override
Is performed a single time when the effect is freshly applied to a path.
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
Gtk::Widget * newWidget() override
This creates a managed widget.
NodeSatelliteArrayParam nodesatellites_param
void addCanvasIndicators(SPLPEItem const *, std::vector< Geom::PathVector > &hp_vec) override
Add possible canvas indicators (i.e., helperpaths other than the original path) to hp_vec This functi...
void setSelected(PathVectorNodeSatellites *_pathvector_nodesatellites)
void setEffectType(EffectType et)
void setCurrentZoom(double current_zoom)
void setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites, bool write=true)
void setUseDistance(bool use_knot_distance)
void param_set_digits(unsigned digits)
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
void param_make_integer(bool yes=true)
void param_set_value(double val)
const gchar * get_abbreviation() const
void param_set_value(const gchar *unit)
Simplified management of enumerations of svg items with UI labels.
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
NodeSatellite a per node holder of data.
NodeSatelliteType nodesatellite_type
void setHasMirror(bool set_has_mirror)
void setSteps(size_t set_steps)
void setHidden(bool set_hidden)
void setAmount(double set_amount)
void setIsTime(bool set_is_time)
double arcDistance(Geom::Curve const &curve_in) const
Get the length of the nodesatellite in curve_in.
double time(Geom::Curve const &curve_in, bool inverse=false) const
Get the time position of the nodesatellite in curve_in.
PathVectorNodeSatellites a class to manage nodesatellites in a pathvector.
void setPathVector(Geom::PathVector pathv)
void updateNodeSatelliteType(NodeSatelliteType nodesatellitetype, bool apply_no_radius, bool apply_with_radius, bool only_selected)
Geom::PathVector getPathVector() const
NodeSatellites getNodeSatellites()
void recalculateForNewPathVector(Geom::PathVector const pathv, NodeSatellite const S)
void updateSteps(size_t steps, bool apply_no_radius, bool apply_with_radius, bool only_selected)
void setNodeSatellites(NodeSatellites nodesatellites)
void updateAmount(double radius, bool apply_no_radius, bool apply_with_radius, bool only_selected, bool use_knot_distance, bool flexible)
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
char const * getAttribute(char const *name) const
Specific curve type functions for Inkscape, not provided by lib2geom.
bool is_straight_curve(Geom::BezierCurve const &c)
double timeAtArcLength(double const A, Geom::Curve const &curve_in)
Calculate the time in curve_in with a size of A.
double arcLengthAt(double const A, Geom::Curve const &curve_in)
Calculate the size in curve_in with a point at A.
NodeSatellite – a per node holder of data.
std::vector< std::vector< NodeSatellite > > NodeSatellites
double Coord
Floating point type used to store coordinates.
size_t count_path_curves(Geom::Path const &path)
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Specific geometry functions for Inkscape, not provided my lib2geom.
Angle distance(Angle const &a, Angle const &b)
SBasisN< n > sqrt(SBasisN< n > const &a, int k)
double angle_between(Line const &l1, Line const &l2)
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
SBasisN< n > sin(LinearN< n > bo, int k)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Point middle_point(LineSegment const &_segment)
static const Util::EnumData< Filletmethod > FilletmethodData[]
static const Util::EnumDataConverter< Filletmethod > FMConverter(FilletmethodData, FM_END)
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
Simplified management of enumerations of svg items with UI labels.