diff --git a/share/ui/toolbar-star.ui b/share/ui/toolbar-star.ui
index ddaf932d719c46f648a0bbb32018558c23b6e8b0..fc4aca34156228fee40d1fdfda82be77598c7529 100644
--- a/share/ui/toolbar-star.ui
+++ b/share/ui/toolbar-star.ui
@@ -29,6 +29,11 @@
True
True
+
+
+
+
+
+
+
diff --git a/src/object/sp-star.cpp b/src/object/sp-star.cpp
index de4e195d97824ff50916b8f20405569757841577..dff17d8d5779dccf7fea9249e2df213b8da73179 100644
--- a/src/object/sp-star.cpp
+++ b/src/object/sp-star.cpp
@@ -522,6 +522,74 @@ void SPStar::update_patheffect(bool write) {
SPShape::update_patheffect(write);
}
+/**
+ * Calculate the average side length of the polygon.
+ *
+ * For spoked polygons (stars) this is the radius delta; for non-spoked
+ * polygons this is the regular side length directly.
+ *
+ * @returns the average length of the polygon sides.
+ */
+double SPStar::getSideLength() const
+{
+ if (!flatsided) {
+ // Pointy star
+ double totalLength = 0.0;
+ auto tr = i2doc_affine();
+
+ for (gint i = 0; i < sides; i++) {
+ Geom::Point outer1 = sp_star_get_xy(this, SP_STAR_POINT_KNOT1, i, false) * tr;
+ Geom::Point inner1 = sp_star_get_xy(this, SP_STAR_POINT_KNOT2, i, false) * tr;
+
+ totalLength += Geom::distance(outer1, inner1);
+
+ Geom::Point outer2 = sp_star_get_xy(this, SP_STAR_POINT_KNOT1, (i + 1) % sides, false) * tr;
+ totalLength += Geom::distance(inner1, outer2);
+ }
+
+ // Return the average side length (since we have 2 * sides distances, divide by 2 * sides)
+ return totalLength / (2 * sides);
+ }
+
+ double diameter = 0.0;
+ auto tr = i2doc_affine();
+ for (gint i = 0; i < sides; i++) {
+ diameter += Geom::distance(sp_star_get_xy(this, SP_STAR_POINT_KNOT1, i, false) * tr,
+ sp_star_get_xy(this, SP_STAR_POINT_KNOT1, (i + 1) % sides, false) * tr);
+ }
+ return diameter / sides;
+}
+
+/**
+ * Set the average side length of the polygon.
+ *
+ * For spoked polygons (stars) this is the radius delta; for non-spoked
+ * polygons this is the regular side length directly.
+ *
+ * @param length the new average length of the polygon sides.
+ */
+void SPStar::setSideLength(double length) {
+ double currentLength = getSideLength();
+ if (currentLength <= 0 || length <= 0) {
+ return; // Prevent division by zero or invalid scaling
+ }
+
+ double scale = length / currentLength;
+
+ if (!flatsided) {
+ // Pointy star
+ r[0] *= scale;
+ r[1] *= scale;
+ } else {
+ // Flat star
+ r[0] *= scale;
+ }
+
+ this -> set_shape();
+
+ requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
/**
* sp_star_get_xy: Get X-Y value as item coordinate system
* @star: star item
diff --git a/src/object/sp-star.h b/src/object/sp-star.h
index 47abebf70b9487ee47650ccc77cda3b2a39b036b..1066847f57d3ba94fdea769f8e3533a5d60d5e35 100644
--- a/src/object/sp-star.h
+++ b/src/object/sp-star.h
@@ -38,11 +38,6 @@ public:
double rounded;
double randomized;
-// CPPIFY: This derivation is a bit weird.
-// parent_class = reinterpret_cast(g_type_class_ref(SP_TYPE_SHAPE));
-// So shouldn't star be derived from shape instead of polygon?
-// What does polygon have that shape doesn't?
-
void build(SPDocument *document, Inkscape::XML::Node *repr) override;
Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override;
void set(SPAttr key, char const* value) override;
@@ -55,6 +50,8 @@ public:
void update_patheffect(bool write) override;
void set_shape() override;
Geom::Affine set_transform(Geom::Affine const& xform) override;
+ double getSideLength() const;
+ void setSideLength(double length);
};
void sp_star_position_set (SPStar *star, int sides, Geom::Point center, double r1, double r2, double arg1, double arg2, bool isflat, double rounded, double randomized);
diff --git a/src/ui/dialog/object-attributes.cpp b/src/ui/dialog/object-attributes.cpp
index 9bf7aaec465d0566a0140f91f69e55069a5de1a2..f73c6d515168495177bd16ed349e609f6fed09bc 100644
--- a/src/ui/dialog/object-attributes.cpp
+++ b/src/ui/dialog/object-attributes.cpp
@@ -726,13 +726,13 @@ public:
_path->updateRepr();
});
});
+
_clear_rnd.signal_clicked().connect([this](){ _rand.get_adjustment()->set_value(0); });
_clear_round.signal_clicked().connect([this](){ _rounded.get_adjustment()->set_value(0); });
_clear_ratio.signal_clicked().connect([this](){ _ratio.get_adjustment()->set_value(0.5); });
_poly.signal_toggled().connect([this](){ set_flat(true); });
_star.signal_toggled().connect([this](){ set_flat(false); });
-
_align.signal_clicked().connect([this](){
change_value(_path, {}, [this](double) { align_star_shape(_path); });
});
@@ -755,6 +755,7 @@ public:
}
_rounded.set_value(_path->rounded);
_rand.set_value(_path->randomized);
+
_clear_rnd .set_visible(_path->randomized != 0);
_clear_round.set_visible(_path->rounded != 0);
_clear_ratio.set_visible(std::abs(_ratio.get_value() - 0.5) > 0.0005);
diff --git a/src/ui/toolbar/star-toolbar.cpp b/src/ui/toolbar/star-toolbar.cpp
index 27ef1109eacc02f4332b7520899b4382a5b0731c..30595c6999a331a65a235056e82dfa48965e619e 100644
--- a/src/ui/toolbar/star-toolbar.cpp
+++ b/src/ui/toolbar/star-toolbar.cpp
@@ -41,6 +41,8 @@
#include "ui/icon-names.h"
#include "ui/tools/star-tool.h"
#include "ui/util.h"
+#include "ui/widget/combo-tool-item.h"
+#include "ui/widget/unit-tracker.h"
#include "ui/widget/spinbutton.h"
using Inkscape::DocumentUndo;
@@ -59,6 +61,8 @@ StarToolbar::StarToolbar(Glib::RefPtr const &builder)
, _spoke_item{get_derived_widget(builder, "_spoke_item")}
, _roundedness_item{get_derived_widget(builder, "_roundedness_item")}
, _randomization_item{get_derived_widget(builder, "_randomization_item")}
+ , _tracker{std::make_unique(Util::UNIT_TYPE_LINEAR)}
+ , _length_item{get_derived_widget(builder, "_length_item")}
{
bool is_flat_sided = Preferences::get()->getBool("/tools/shapes/star/isflatsided", false);
@@ -111,6 +115,7 @@ StarToolbar::StarToolbar(Glib::RefPtr const &builder)
setup_derived_spin_button(_spoke_item, "proportion", 0.5, &StarToolbar::proportion_value_changed);
setup_derived_spin_button(_roundedness_item, "rounded", 0.0, &StarToolbar::rounded_value_changed);
setup_derived_spin_button(_randomization_item, "randomized", 0.0, &StarToolbar::randomized_value_changed);
+ setup_derived_spin_button(_length_item, "length", 0.0, &StarToolbar::length_value_changed);
// Flatsided checkbox
_flat_item_buttons.push_back(&get_widget(builder, "flat_polygon_button"));
@@ -128,6 +133,10 @@ StarToolbar::StarToolbar(Glib::RefPtr const &builder)
_spoke_box.set_visible(!is_flat_sided);
+ auto unit_menu = _tracker->create_tool_item(_("Units"), (""));
+ get_widget(builder, "unit_menu_box").append(*unit_menu);
+ _tracker->addAdjustment(_length_item.get_adjustment()->gobj());
+
_initMenuBtns();
}
@@ -166,6 +175,7 @@ void StarToolbar::setDesktop(SPDesktop *desktop)
{
if (_desktop) {
_selection_changed_conn.disconnect();
+ _selection_modified_conn.disconnect();
if (_repr) {
_detachRepr();
@@ -177,10 +187,16 @@ void StarToolbar::setDesktop(SPDesktop *desktop)
if (_desktop) {
auto sel = _desktop->getSelection();
_selection_changed_conn = sel->connectChanged(sigc::mem_fun(*this, &StarToolbar::_selectionChanged));
+ _selection_modified_conn = sel->connectChanged(sigc::mem_fun(*this, &StarToolbar::_selectionModified));
_selectionChanged(sel); // Synthesize an emission to trigger the update
}
}
+void StarToolbar::setActiveUnit(Util::Unit const *unit)
+{
+ _tracker->setActiveUnit(unit);
+}
+
void StarToolbar::side_mode_changed(int mode)
{
bool const flat = mode == 0;
@@ -356,6 +372,25 @@ void StarToolbar::randomized_value_changed()
}
}
+void StarToolbar::length_value_changed()
+{
+ if (!_blocker.pending() || _tracker->isUpdating()) {
+ // in turn, prevent listener from responding
+ auto guard = _blocker.block();
+
+ auto adj = _length_item.get_adjustment();
+ Preferences::get()->setDouble("/tools/shapes/star/length", adj->get_value());
+
+ auto value = Util::Quantity::convert(adj->get_value(), _tracker->getActiveUnit(), "px");
+
+ for (auto item : _desktop->getSelection()->items()) {
+ if (auto star = cast(item)) {
+ star -> setSideLength(value);
+ }
+ }
+ }
+}
+
void StarToolbar::_setDefaults()
{
_batchundo = true;
@@ -393,20 +428,45 @@ void StarToolbar::_selectionChanged(Selection *selection)
int n_selected = 0;
XML::Node *repr = nullptr;
+ double lengths = 0;
for (auto item : selection->items()) {
- if (is(item)) {
+ if (auto star = cast(item)) {
n_selected++;
repr = item->getRepr();
+ lengths += star->getSideLength();
}
}
_mode_item.set_markup(n_selected == 0 ? _("New:") : _("Change:"));
+ _length_item.set_sensitive(n_selected > 0);
if (n_selected == 1) {
_attachRepr(repr);
_repr->synthesizeEvents(*this); // Fixme: BAD
}
+ _selectionModified(selection);
+}
+
+void StarToolbar::_selectionModified(Selection *selection)
+{
+ if (!_blocker.pending()|| _tracker->isUpdating()) {
+ auto guard = _blocker.block();
+ auto length_adj = _length_item.get_adjustment();
+
+ int n_selected = 0;
+ double lengths = 0;
+ for (auto item : selection->items()) {
+ if (auto star = cast(item)) {
+ n_selected++;
+ lengths += star->getSideLength();
+ }
+ }
+ if (n_selected > 0) {
+ auto value = Util::Quantity::convert(lengths / n_selected, "px", _tracker->getActiveUnit());
+ length_adj->set_value(value);
+ }
+ }
}
void StarToolbar::notifyAttributeChanged(XML::Node &, GQuark name_, Util::ptr_shared, Util::ptr_shared)
@@ -426,6 +486,7 @@ void StarToolbar::notifyAttributeChanged(XML::Node &, GQuark name_, Util::ptr_sh
bool isFlatSided = Preferences::get()->getBool("/tools/shapes/star/isflatsided", false);
auto mag_adj = _magnitude_item.get_adjustment();
auto spoke_adj = _spoke_item.get_adjustment();
+ auto length_adj = _length_item.get_adjustment();
if (!strcmp(name, "inkscape:randomized")) {
double randomized = _repr->getAttributeDouble("inkscape:randomized", 0.0);
@@ -457,6 +518,20 @@ void StarToolbar::notifyAttributeChanged(XML::Node &, GQuark name_, Util::ptr_sh
int sides = _repr->getAttributeInt("sodipodi:sides", 0);
mag_adj->set_value(sides);
}
+
+ double lengths = 0;
+ int n_selected = 0;
+ for (auto item : _desktop->getSelection()->items()) {
+ if (auto star = cast(item)) {
+ n_selected++;
+ lengths += star->getSideLength();
+ }
+ }
+
+ if (n_selected > 0) {
+ auto value = Util::Quantity::convert(lengths / n_selected, "px", _tracker->getActiveUnit());
+ length_adj->set_value(value);
+ }
}
} // namespace Inkscape::UI::Toolbar
diff --git a/src/ui/toolbar/star-toolbar.h b/src/ui/toolbar/star-toolbar.h
index d3dbd2726d67d95aefd84e321f73cbc97d7b99e5..9bad4f4659ab8c9fef3f82375c14b69f920ee0cd 100644
--- a/src/ui/toolbar/star-toolbar.h
+++ b/src/ui/toolbar/star-toolbar.h
@@ -45,6 +45,7 @@ namespace Tools { class ToolBase; }
namespace Widget {
class Label;
class SpinButton;
+class UnitTracker;
} // namespace Widget
} // namespace UI
namespace XML { class Node; }
@@ -61,6 +62,7 @@ public:
~StarToolbar() override;
void setDesktop(SPDesktop *desktop) override;
+ void setActiveUnit(Util::Unit const *unit) override;
private:
StarToolbar(Glib::RefPtr const &builder);
@@ -74,6 +76,9 @@ private:
UI::Widget::SpinButton &_spoke_item;
UI::Widget::SpinButton &_roundedness_item;
UI::Widget::SpinButton &_randomization_item;
+ UI::Widget::SpinButton &_length_item;
+
+ std::unique_ptr _tracker;
XML::Node *_repr = nullptr;
void _attachRepr(XML::Node *repr);
@@ -82,6 +87,7 @@ private:
bool _batchundo = false;
OperationBlocker _blocker;
sigc::connection _selection_changed_conn;
+ sigc::connection _selection_modified_conn;
void setup_derived_spin_button(UI::Widget::SpinButton &btn, Glib::ustring const &name, double default_value,
ValueChangedMemFun value_changed_mem_fun);
@@ -90,8 +96,10 @@ private:
void proportion_value_changed();
void rounded_value_changed();
void randomized_value_changed();
+ void length_value_changed();
void _setDefaults();
void _selectionChanged(Selection *selection);
+ void _selectionModified(Selection *selection);
void notifyAttributeChanged(XML::Node &node, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value) override;
};