23#include <glibmm/i18n.h>
24#include <glibmm/ustring.h>
26#include <gtkmm/combobox.h>
27#include <gtkmm/frame.h>
28#include <gtkmm/image.h>
29#include <gtkmm/label.h>
30#include <gtkmm/object.h>
31#include <gtkmm/spinbutton.h>
61 ~KnotHolderEntityCopyGapX()
final;
64 void knot_click(guint state)
final;
73 ~KnotHolderEntityCopyGapY()
final;
76 void knot_click(guint state)
final;
87 unit(_(
"Unit:"), _(
"Unit"),
"unit", &wr, this,
"px"),
88 lpesatellites(_(
"lpesatellites"), _(
"Items satellites"),
"lpesatellites", &wr, this, false),
89 num_cols(_(
"Columns"), _(
"Number of columns"),
"num_cols", &wr, this, 3),
90 num_rows(_(
"Rows"), _(
"Number of rows"),
"num_rows", &wr, this, 3),
91 gapx(_(
"Gap X"), _(
"Horizontal gap between tiles (uses selected unit)"),
"gapx", &wr, this, 0.0),
92 gapy(_(
"Gap Y"), _(
"Vertical gap between tiles (uses selected unit)"),
"gapy", &wr, this, 0.0),
93 scale(_(
"Scale %"), _(
"Scale tiles by this percentage"),
"scale", &wr, this, 0.0),
94 rotate(_(
"Rotate °"), _(
"Rotate tiles by this amount of degrees"),
"rotate", &wr, this, 0.0),
95 offset(_(
"Offset %"), _(
"Offset tiles by this percentage of width/height"),
"offset", &wr, this, 0.0),
96 offset_type(_(
"Offset type"), _(
"Choose whether to offset rows or columns"),
"offset_type", &wr, this, false),
97 interpolate_scalex(_(
"Interpolate scale X"), _(
"Interpolate tile size in each row"),
"interpolate_scalex", &wr, this, false),
98 interpolate_scaley(_(
"Interpolate scale Y"), _(
"Interpolate tile size in each column"),
"interpolate_scaley", &wr, this, true),
99 shrink_interp(_(
"Minimize gaps"), _(
"Minimize gaps between scaled objects (does not work with rotation/diagonal mode)"),
"shrink_interp", &wr, this, false),
100 interpolate_rotatex(_(
"Interpolate rotation X"), _(
"Interpolate tile rotation in row"),
"interpolate_rotatex", &wr, this, false),
101 interpolate_rotatey(_(
"Interpolate rotation Y"), _(
"Interpolate tile rotation in column"),
"interpolate_rotatey", &wr, this, true),
102 split_items(_(
"Split elements"), _(
"Split elements, so they can be selected, styled, and moved (if grouped) independently"),
"split_items", &wr, this, false),
103 mirrorrowsx(_(
"Mirror rows in X"), _(
"Mirror rows horizontally"),
"mirrorrowsx", &wr, this, false),
104 mirrorrowsy(_(
"Mirror rows in Y"), _(
"Mirror rows vertically"),
"mirrorrowsy", &wr, this, false),
105 mirrorcolsx(_(
"Mirror cols in X"), _(
"Mirror columns horizontally"),
"mirrorcolsx", &wr, this, false),
106 mirrorcolsy(_(
"Mirror cols in Y"), _(
"Mirror columns vertically"),
"mirrorcolsy", &wr, this, false),
107 mirrortrans(_(
"Mirror transforms"), _(
"Mirror transformations"),
"mirrortrans", &wr, this, false),
108 link_styles(_(
"Link styles"), _(
"Link styles in split mode, can also be used to reset style of copies"),
"link_styles", &wr, this, false),
109 random_gap_x(_(
"Random gaps X"), _(
"Randomize horizontal gaps"),
"random_gap_x", &wr, this, false),
110 random_gap_y(_(
"Random gaps Y"), _(
"Randomize vertical gaps"),
"random_gap_y", &wr, this, false),
111 random_rotate(_(
"Random rotation"), _(
"Randomize tile rotation"),
"random_rotate", &wr, this, false),
112 random_scale(_(
"Random scale"), _(
"Randomize scale"),
"random_scale", &wr, this, false),
113 seed(_(
"Seed"), _(
"Randomization seed"),
"seed", &wr, this, 1.),
114 transformorigin(
"transformorigin:",
"transformorigin",
"transformorigin", &wr, this,
"", true)
210 if (lpereference && lpereference->isAttached() && lpereference.get()->getObject() !=
nullptr) {
226 if (lpereference && lpereference->isAttached()) {
227 auto copies = cast<SPItem>(lpereference->getObject());
230 copies->setHidden(
true);
231 }
else if (copies->isHidden()) {
232 copies->setHidden(
false);
245 bool forcewrite =
false;
256 double gapscalex = 0;
257 double maxheight = 0;
259 double minheight = std::numeric_limits<double>::max();
275 for (
int i = 0; i <
num_rows; ++i) {
280 for (
int j = 0; j <
num_cols; ++j) {
292 mx = (j+i)%2 != 0 ? -1 : 1;
295 mx = i%2 != 0 ? -1 : 1;
297 mx = j%2 != 0 ? -1 : 1;
301 my = (j+i)%2 != 0 ? -1 : 1;
304 my = i%2 != 0 ? -1 : 1;
306 my = j%2 != 0 ? -1 : 1;
314 double fracyin = fracy;
320 rotatein = rotatein * (i + j);
322 rotatein = rotatein * j;
324 rotatein = rotatein * i;
334 double scalegap =
scaleok - scalein;
336 scalein = (scalegap * (i + j)) + 1;
338 scalein = (scalegap * j) + 1;
340 scalein = (scalegap * i) + 1;
380 double fixed_heightrows = heightrows;
381 double fixed_widthcols = widthcols;
384 shrink_interpove =
false;
387 maxheight = std::max(maxheight,(*bbox).height() * scalein);
388 maxwidth = std::max(maxwidth,(*bbox).width() * scalein);
389 minheight = std::min(minheight,(*bbox).height() * scalein);
392 fixed_widthcols = widthcols;
393 fixed_heightrows = heightrows;
394 double cx = (*bbox).width() * scalein;
395 double cy = (*bbox).height() * scalein;
402 px = (*prev_bbox).width();
403 py = (*prev_bbox).height();
409 x = cx - ((cx-px)/2.0);
420 ygap[j] = ((cy-y[j])/2.0);
424 y[j] += cy + ygap[j];
437 xset += widthcols * j;
439 yset = heightrows * i;
453 offset_y = fixed_heightrows/(100.0/(double)
offset);
456 offset_x = fixed_widthcols/(100.0/(double)
offset);
462 auto translate = p * gap.
inverse();
467 forcewrite = forcewrite || write;
474 if (forcewrite || !connected) {
494 for (
auto iter :
orig->style->properties()) {
496 auto key = iter->id();
498 if (
auto const attr =
orig->getAttribute(iter->name().c_str())) {
512 if ( is<SPGroup>(
orig) && is<SPGroup>(dest) && cast<SPGroup>(
orig)->getItemCount() == cast<SPGroup>(dest)->getItemCount() ) {
516 std::vector< SPObject * > childs =
orig->childList(
true);
518 for (
auto &
child : childs) {
524 }
else if( is<SPGroup>(
orig) && is<SPGroup>(dest) && cast<SPGroup>(
orig)->getItemCount() != cast<SPGroup>(dest)->getItemCount()) {
529 if ( is<SPText>(
orig) && is<SPText>(dest) && cast<SPText>(
orig)->children.size() == cast<SPText>(dest)->children.size()) {
534 for (
auto &
child : cast<SPText>(
orig)->children) {
541 auto shape = cast<SPShape>(
orig);
542 auto path = cast<SPPath>(dest);
547 if (shape && !path) {
554 dest->
updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
555 path = cast<SPPath>(dest);
557 path->setAttribute(
"d", str);
559 path->removeAttribute(
"d");
575 auto group = cast<SPGroup>(elemref);
583 std::vector<SPItem*>
const item_list = group->item_list();
585 for (
auto sub_item : item_list) {
589 previous = resultnode;
620 bool creation =
false;
637 return cast<SPItem>(elemref);
640Gtk::ToggleButton*
create_radio_button(Gtk::ToggleButton *&group,
const Glib::ustring& tooltip,
const Glib::ustring& icon_name) {
641 auto const button = Gtk::make_managed<Gtk::ToggleButton>();
643 button->set_group(*group);
647 button->set_tooltip_text(tooltip);
648 button->set_image_from_icon_name(icon_name, Gtk::IconSize::NORMAL);
649 button->set_halign(Gtk::Align::CENTER);
650 button->set_valign(Gtk::Align::CENTER);
651 button->add_css_class(
"lpe-square-button");
655void align_widgets(
const std::vector<Gtk::Widget*>& widgets,
int spinbutton_chars = 7) {
657 auto const for_child_n = [&widgets](
int const child_index,
658 std::function<void (Gtk::Widget *)>
const &action)
660 for (
auto child : widgets) {
661 auto container =
dynamic_cast<Gtk::Box *
>(
child);
662 if (!container)
continue;
665 if (child_index < children.size()) {
666 action(children[child_index]);
671 auto const get_natural_width = [](Gtk::Widget
const &widget)
673 g_assert(widget.get_visible());
674 int natural{}, ignore{};
675 widget.measure(Gtk::Orientation::HORIZONTAL, -1, ignore, natural, ignore, ignore);
681 for_child_n(0, [&](Gtk::Widget*
child){
682 if (
auto label =
dynamic_cast<Gtk::Label*
>(
child)) {
683 label->set_xalign(0);
684 max_width = std::max(max_width, get_natural_width(*
label));
688 for_child_n(0, [=](Gtk::Widget*
child) {
689 if (
auto label =
dynamic_cast<Gtk::Label*
>(
child)) {
690 label->set_size_request(max_width);
695 int button_width = 0;
696 for_child_n(1, [&](Gtk::Widget*
child) {
697 if (
auto spin =
dynamic_cast<Gtk::SpinButton*
>(
child)) {
699 spin->set_width_chars(spinbutton_chars);
700 button_width = std::max(button_width, get_natural_width(*spin));
704 int combo_size = button_width > 0 ? button_width : 50;
705 for_child_n(1, [=](Gtk::Widget*
child) {
706 if (
auto combo =
dynamic_cast<Gtk::ComboBox*
>(
child)) {
707 combo->set_size_request(combo_size);
716 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
719 Gtk::Widget *combo =
nullptr;
720 Gtk::Widget *randbutton =
nullptr;
721 Gtk::Box *containerstart =
nullptr;
722 Gtk::Box *containerend =
nullptr;
723 Gtk::Box *movestart =
nullptr;
724 Gtk::Box *moveend =
nullptr;
725 Gtk::Box *rowcols =
nullptr;
727 bool usemirroricons = prefs->
getBool(
"/live_effects/copy/mirroricons",
true);
728 std::vector<Gtk::Widget*> scalars;
731 if (!param->widget_is_visible)
continue;
733 auto const widg = param->param_newWidget();
736 if (param->param_key ==
"unit") {
741 auto const destroy_child = widgcombo->get_first_child();
742 widgcombo->remove(*destroy_child);
746 if (!usemirroricons)
continue;
748 Gtk::ToggleButton *group =
nullptr;
749 auto const frame = Gtk::make_managed<Gtk::Frame>(_(
"Mirroring mode"));
750 frame->set_halign(Gtk::Align::START);
751 auto const cbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
752 auto const vbox1 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
753 auto const hbox1 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
754 auto const hbox2 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
755 auto const vbox2 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
756 auto const hbox3 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
757 auto const hbox4 = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
758 vbox2->set_margin_start(5);
759 vbox1->set_margin_bottom(3);
762 cbox->set_margin_start(6);
763 cbox->set_margin_end(6);
764 cbox->set_margin_bottom(3);
765 cbox->set_halign(Gtk::Align::START);
766 hbox1->set_margin_bottom(3);
767 hbox3->set_margin_bottom(3);
768 frame->set_child(*cbox);
780 }
else if (param->param_key ==
"seed"){
783 auto const destroy_child = widgrand->get_first_child();
784 widgrand->remove(*destroy_child);
786 auto const first = widgrand->get_first_child();
787 first->set_visible(
false);
789 auto const button =
dynamic_cast<Gtk::Button *
>(first->get_next_sibling());
792 auto const box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
793 box->append(*Gtk::manage(
sp_get_icon_image(INKSCAPE_ICON(
"randomize"), Gtk::IconSize::NORMAL)));
794 box->append(*Gtk::make_managed<Gtk::Label>(_(
"Randomize")));
795 button->set_child(*box);
797 button->set_tooltip_markup(_(
"Randomization seed for random mode for scaling, rotation and gaps"));
798 button->set_has_frame(
true);
799 button->set_valign(Gtk::Align::START);
801 widgrand->set_vexpand(
false);
802 widgrand->set_hexpand(
false);
803 widgrand->set_valign(Gtk::Align::START);
804 widgrand->set_halign(Gtk::Align::START);
805 randbutton = Gtk::manage(widgrand);
808 }
else if (param->param_key ==
"offset_type" ||
809 param->param_key ==
"mirrorrowsx" && usemirroricons ||
810 param->param_key ==
"mirrorrowsy" && usemirroricons ||
811 param->param_key ==
"mirrorcolsx" && usemirroricons ||
812 param->param_key ==
"mirrorcolsy" && usemirroricons ||
813 param->param_key ==
"interpolate_rotatex" ||
814 param->param_key ==
"interpolate_rotatey" ||
815 param->param_key ==
"interpolate_scalex" ||
816 param->param_key ==
"interpolate_scaley" ||
817 param->param_key ==
"random_scale" ||
818 param->param_key ==
"random_rotate" ||
819 param->param_key ==
"random_gap_x" ||
820 param->param_key ==
"random_gap_y")
823 }
else if (param->param_key ==
"offset") {
825 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
826 Gtk::ToggleButton *group =
nullptr;
829 rows->set_tooltip_markup(_(
"Offset alternate rows"));
830 cols->set_tooltip_markup(_(
"Offset alternate cols"));
841 }
else if (param->param_key ==
"scale") {
842 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
843 Gtk::ToggleButton *group =
nullptr;
844 auto cols =
create_radio_button(group, _(
"Interpolate X"), INKSCAPE_ICON(
"interpolate-scale-x"));
845 auto rows =
create_radio_button(group, _(
"Interpolate Y"), INKSCAPE_ICON(
"interpolate-scale-y"));
846 auto both =
create_radio_button(group, _(
"Interpolate both"), INKSCAPE_ICON(
"interpolate-scale-both"));
847 auto none =
create_radio_button(group, _(
"No interpolation"), INKSCAPE_ICON(
"interpolate-scale-none"));
848 auto rand =
create_radio_button(group, _(
"Interpolate random"), INKSCAPE_ICON(
"scale-random"));
860 cols->set_tooltip_markup(_(
"Blend scale from <b>left to right</b> (left column uses original scale, right column uses new scale)"));
861 rows->set_tooltip_markup(_(
"Blend scale from <b>top to bottom</b> (top row uses original scale, bottom row uses new scale)"));
862 both->set_tooltip_markup(_(
"Blend scale <b>diagonally</b> (top left tile uses original scale, bottom right tile uses new scale)"));
863 none->set_tooltip_markup(_(
"Uniform scale"));
864 rand->set_tooltip_markup(_(
"Random scale (hit <b>Randomize</b> button to shuffle)"));
877 }
else if (param->param_key ==
"rotate") {
879 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
880 Gtk::ToggleButton *group =
nullptr;
881 auto cols =
create_radio_button(group, _(
"Interpolate X"), INKSCAPE_ICON(
"interpolate-rotate-x"));
882 auto rows =
create_radio_button(group, _(
"Interpolate Y"), INKSCAPE_ICON(
"interpolate-rotate-y"));
883 auto both =
create_radio_button(group, _(
"Interpolate both"), INKSCAPE_ICON(
"interpolate-rotate-both"));
884 auto none =
create_radio_button(group, _(
"No interpolation"), INKSCAPE_ICON(
"interpolate-rotate-none"));
885 auto rand =
create_radio_button(group, _(
"Interpolate random"), INKSCAPE_ICON(
"rotate-random"));
897 cols->set_tooltip_markup(_(
"Blend rotation from <b>left to right</b> (left column uses original rotation, right column uses new rotation)"));
898 rows->set_tooltip_markup(_(
"Blend rotation from <b>top to bottom</b> (top row uses original rotation, bottom row uses new rotation)"));
899 both->set_tooltip_markup(_(
"Blend rotation <b>diagonally</b> (top left tile uses original rotation, bottom right tile uses new rotation)"));
900 none->set_tooltip_markup(_(
"Uniform rotation"));
901 rand->set_tooltip_markup(_(
"Random rotation (hit <b>Randomize</b> button to shuffle)"));
913 }
else if (param->param_key ==
"gapx") {
914 auto const wrapper = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
915 movestart = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
916 moveend = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
917 moveend->set_homogeneous();
918 moveend->set_valign(Gtk::Align::FILL);
919 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
920 Gtk::ToggleButton *group =
nullptr;
921 auto normal =
create_radio_button(group, _(
"Normal"), INKSCAPE_ICON(
"interpolate-scale-none"));
926 normal->set_active();
928 normal->set_tooltip_markup(_(
"All horizontal gaps have the same width"));
929 randx->set_tooltip_markup(_(
"Random horizontal gaps (hit <b>Randomize</b> button to shuffle)"));
934 combo->set_margin_end(0);
941 combo->set_halign(Gtk::Align::END);
942 widg->set_halign(Gtk::Align::START);
944 }
else if (param->param_key ==
"gapy") {
946 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
947 Gtk::ToggleButton *group =
nullptr;
948 auto normal =
create_radio_button(group, _(
"Normal"), INKSCAPE_ICON(
"interpolate-scale-none"));
953 normal->set_active();
955 normal->set_tooltip_markup(_(
"All vertical gaps have the same height"));
956 randy->set_tooltip_markup(_(
"Random vertical gaps (hit <b>Randomize</b> button to shuffle)"));
961 widg->set_halign(Gtk::Align::START);
963 }
else if (param->param_key ==
"mirrortrans"){
964 auto const container = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
965 auto const containerwraper = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
966 containerend = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
967 containerstart = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL,0);
971 UI::pack_end(*containerend, *randbutton,
true,
true, 2);
972 containerend->set_margin_start(8);
975 containerwraper->set_hexpand(
false);
976 containerend->set_hexpand(
false);
977 containerstart->set_hexpand(
false);
980 param->param_key ==
"split_items" ||
981 param->param_key ==
"link_styles" ||
982 param->param_key ==
"shrink_interp")
985 widg->set_vexpand(
false);
986 widg->set_hexpand(
false);
987 widg->set_valign(Gtk::Align::START);
988 widg->set_halign(Gtk::Align::START);
989 }
else if (param->param_key ==
"num_rows") {
990 rowcols = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
993 }
else if (param->param_key ==
"num_cols") {
999 if (
auto const tip = param->param_getTooltip()) {
1000 widg->set_tooltip_markup(*tip);
1002 widg->set_tooltip_markup({});
1003 widg->set_has_tooltip(
false);
1007 scalars.push_back(widg);
1019 for (
int i = 0; i < 4; i++) {
1020 int const position = (pos * 4) + i;
1022 auto const iconname = Glib::ustring::compose(
"mirroring-%1",
result);
1026 button->set_active();
1030 static constexpr int zero =
static_cast<gunichar>(
'0');
1031 Glib::ustring tooltip =
result[0] == zero ?
"" :
"rx+";
1032 tooltip +=
result[1] == zero ?
"" :
"ry+";
1033 tooltip +=
result[2] == zero ?
"" :
"cx+";
1034 tooltip +=
result[3] == zero ?
"" :
"cy+";
1035 if (tooltip.size()) {
1036 tooltip.erase(tooltip.size()-1);
1038 button->set_tooltip_markup(tooltip);
1039 button->set_margin_start(1);
1047 Glib::ustring
result =
"0000";
1050 }
else if (
index == 2) {
1052 }
else if (
index == 3) {
1054 }
else if (
index == 4) {
1056 }
else if (
index == 5) {
1058 }
else if (
index == 6) {
1060 }
else if (
index == 7) {
1062 }
else if (
index == 8) {
1064 }
else if (
index == 9) {
1066 }
else if (
index == 10) {
1068 }
else if (
index == 11) {
1070 }
else if (
index == 12) {
1072 }
else if (
index == 13) {
1074 }
else if (
index == 14) {
1076 }
else if (
index == 15) {
1100 static constexpr int zero =
static_cast<gunichar>(
'0');
1183 auto transformorigin_str = lpeitem->
getAttribute(
"transform");
1184 if (transformorigin_str) {
1188 if (!transformorigin_str.empty()) {
1196 using namespace Geom;
1238 auto const prev_display_unit = std::move(
display_unit);
1277 scale_fix = 1 + ((scale_fix - 1) * (
num_cols -1));
1279 scale_fix = 1 + ((scale_fix - 1) * (
num_rows -1));
1282 scale_fix = std::max(scale_fix, 1.0);
1319 double gapscalex = 0;
1320 double maxheight = 0;
1321 double maxwidth = 0;
1322 double minheight = std::numeric_limits<double>::max();
1329 double posx = ((*gap_bbox).left() - (*bbox).left()) / (*gap_bbox).width();
1335 for (
int i = 0; i <
num_rows; ++i) {
1340 for (
int j = 0; j <
num_cols; ++j) {
1349 bool reverse_pv =
false;
1354 mx = (j+i)%2 != 0 ? -1 : 1;
1357 mx = i%2 != 0 ? -1 : 1;
1359 mx = j%2 != 0 ? -1 : 1;
1363 my = (j+i)%2 != 0 ? -1 : 1;
1366 my = i%2 != 0 ? -1 : 1;
1368 my = j%2 != 0 ? -1 : 1;
1372 reverse_pv = mx * my == -1;
1377 double fracyin = fracy;
1379 fracyin = 1-fracyin;
1384 double rotatein =
rotate;
1386 rotatein = rotatein * (i + j);
1388 rotatein = rotatein * j;
1390 rotatein = rotatein * i;
1400 double scalegap =
scaleok - scalein;
1402 scalein = (scalegap * (i + j)) + 1;
1404 scalein = (scalegap * j) + 1;
1406 scalein = (scalegap * i) + 1;
1463 double fixed_heightrows = heightrows;
1464 double fixed_widthcols = widthcols;
1474 maxheight = std::max(maxheight,(*bbox).height());
1475 maxwidth = std::max(maxwidth,(*bbox).width());
1476 minheight = std::min(minheight,(*bbox).height());
1479 fixed_widthcols = widthcols;
1480 fixed_heightrows = heightrows;
1481 double cx = (*bbox).width();
1482 double cy = (*bbox).height();
1489 px = (*prev_bbox).width();
1490 py = (*prev_bbox).height();
1494 x = ((cx - ((cx - px) / 2.0))) * factorx;
1505 y[j] = maxheight * factory;
1510 gap[j] = ((cy * factory) - y[j])/2.0;
1511 }
else if (i == 0) {
1514 yset = y[j] + (gap[j] * i);
1516 y[j] += cy * factory;
1518 y[j] += maxheight * factory;
1527 xset += widthcols * j;
1529 yset = heightrows * i;
1531 double offset_x = 0;
1532 double offset_y = 0;
1535 offset_y = fixed_heightrows/(100.0/(double)
offset);
1538 offset_x = fixed_widthcols/(100.0/(double)
offset);
1558 using namespace Geom;
1566 hp_vec.push_back(pathv);
1579 auto transformorigin_str = lpeitem->
getAttribute(
"transform");
1581 if (transformorigin_str) {
1584 ontoggle = ontoggle;
1614 _(
"<b>Horizontal gaps between tiles</b>: drag to adjust, <b>Shift+click</b> to reset"));
1619 _(
"<b>Vertical gaps between tiles</b>: drag to adjust, <b>Shift+click</b> to reset"));
1625KnotHolderEntityCopyGapX::~KnotHolderEntityCopyGapX()
1633KnotHolderEntityCopyGapY::~KnotHolderEntityCopyGapY()
1635 LPETiling* lpe =
dynamic_cast<LPETiling *
>(_effect);
1641void KnotHolderEntityCopyGapX::knot_click(guint state)
1643 if (!(state & GDK_SHIFT_MASK)) {
1647 LPETiling* lpe =
dynamic_cast<LPETiling *
>(_effect);
1649 lpe->gapx.param_set_value(0);
1654void KnotHolderEntityCopyGapY::knot_click(guint state)
1656 if (!(state & GDK_SHIFT_MASK)) {
1660 LPETiling* lpe =
dynamic_cast<LPETiling *
>(_effect);
1662 lpe->gapy.param_set_value(0);
1669 LPETiling* lpe =
dynamic_cast<LPETiling *
>(_effect);
1671 Geom::Point const s = snap_knot_position(p, state);
1672 if (lpe->originalbbox) {
1673 Geom::Point point = (*lpe->originalbbox).corner(1);
1674 point *= lpe->transformoriginal.inverse();
1677 Glib::ustring doc_unit = SP_ACTIVE_DOCUMENT->getWidth().unit->abbr.c_str();
1682 lpe->gapx.param_set_value(value);
1683 lpe->gapx.write_to_SVG();
1689 LPETiling* lpe =
dynamic_cast<LPETiling *
>(_effect);
1691 Geom::Point const s = snap_knot_position(p, state);
1692 if (lpe->originalbbox) {
1693 Geom::Point point = (*lpe->originalbbox).corner(3);
1694 point *= lpe->transformoriginal.inverse();
1697 Glib::ustring doc_unit = SP_ACTIVE_DOCUMENT->getWidth().unit->abbr.c_str();
1702 lpe->gapy.param_set_value(value);
1703 lpe->gapy.write_to_SVG();
1707Geom::Point KnotHolderEntityCopyGapX::knot_get()
const
1709 LPETiling
const * lpe =
dynamic_cast<LPETiling const*
> (_effect);
1711 if (lpe->originalbbox) {
1712 auto bbox = (*lpe->originalbbox);
1715 Glib::ustring prev_unit = SP_ACTIVE_DOCUMENT->getDisplayUnit()->abbr.c_str();
1720 double scale = lpe->scaleok;
1721 ret = (bbox).corner(1) +
Geom::Point((value * lpe->end_scale(scale,
false))/2.0,0);
1722 ret *= lpe->transformoriginal.inverse();
1727Geom::Point KnotHolderEntityCopyGapY::knot_get()
const
1729 LPETiling
const * lpe =
dynamic_cast<LPETiling const*
> (_effect);
1731 if (lpe->originalbbox) {
1732 auto bbox = (*lpe->originalbbox);
1735 Glib::ustring prev_unit = SP_ACTIVE_DOCUMENT->getDisplayUnit()->abbr.c_str();
1740 double scale = lpe->scaleok;
1741 ret = (bbox).corner(3) +
Geom::Point(0,(value * lpe->end_scale(scale,
false))/2.0);
1742 ret *= lpe->transformoriginal.inverse();
3x3 matrix representing an affine transformation.
Affine inverse() const
Compute the inverse matrix.
Affine withoutTranslation() const
Axis-aligned rectangle that can be empty.
void push_back(Path const &path)
Append a path at the end.
OptRect boundsFast() const
iterator insert(iterator pos, Path const &p)
void reverse(bool reverse_paths=true)
Reverse the direction of paths in the vector.
Sequence of contiguous curves, aka spline.
Two-dimensional point that doubles as a vector.
static Rotate from_degrees(Coord deg)
Construct a rotation from its angle in degrees.
Translate inverse() const
Get the inverse translation.
std::vector< StorageType > const & data() const
void param_setValue(bool newvalue)
std::vector< Parameter * > param_vector
void registerParameter(Parameter *param)
virtual void resetDefaults(SPItem const *item)
Sets all parameters to their default values and writes them to SVG.
bool apply_to_clippath_and_mask
virtual void processObjects(LPEAction lpe_action)
bool _provides_knotholder_entities
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
Geom::Interval boundingbox_Y
Geom::Interval boundingbox_X
void param_setValue(Glib::ustring newvalue, bool write=false)
Glib::ustring param_getSVGValue() const override
BoolParam interpolate_rotatey
SPItem * toItem(size_t i, bool reset, bool &write)
double end_scale(double scale_fix, bool tomax) const
Geom::OptRect originalbbox
Geom::Affine transformoriginal
std::vector< double > random_s
std::vector< double > random_r
std::vector< double > random_y
void doOnVisibilityToggled(SPLPEItem const *) final
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) final
void doAfterEffect(SPLPEItem const *lpeitem, SPCurve *curve) final
Is performed at the end of the LPE only one time per "lpeitem" in paths/shapes is called in middle of...
BoolParam interpolate_scaley
bool getActiveMirror(int index)
void generate_buttons(Gtk::Box *container, Gtk::ToggleButton *&group, int pos)
void doOnApply(SPLPEItem const *lpeitem) final
Is performed a single time when the effect is freshly applied to a path.
void cloneD(SPObject *orig, SPObject *dest)
std::vector< double > random_x
void cloneStyle(SPObject *orig, SPObject *dest)
void setGapYMode(bool random)
Glib::ustring getMirrorMap(int index)
void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) final
BoolParam interpolate_scalex
void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector< Geom::PathVector > &hp_vec) final
Add possible canvas indicators (i.e., helperpaths other than the original path) to hp_vec This functi...
Geom::PathVector doEffect_path_post(Geom::PathVector const &path_in, FillRuleBool fillrule)
Glib::ustring display_unit
void setScaleInterpolate(bool x, bool y)
HiddenParam transformorigin
Inkscape::XML::Node * createPathBase(SPObject *elemref)
Gtk::Widget * newWidget() final
This creates a managed widget.
void setMirroring(int index)
bool doOnOpen(SPLPEItem const *) final
Is performed on load document or revert If the item is fixed legacy return true.
void setRotateInterpolate(bool x, bool y)
void doOnRemove(SPLPEItem const *) final
LPETiling(LivePathEffectObject *lpeobject)
void setGapXMode(bool random)
friend class CoS::KnotHolderEntityCopyGapX
friend class CoS::KnotHolderEntityCopyGapY
void resetDefaults(SPItem const *item) final
Sets all parameters to their default values and writes them to SVG.
void doBeforeEffect(SPLPEItem const *lpeitem) final
Is performed each time before the effect is updated.
Geom::Affine originatrans
BoolParam interpolate_rotatex
SatelliteArrayParam lpesatellites
void param_set_randomsign(bool randomsign)
void param_set_range(gdouble min, gdouble max)
double param_get_random_number()
void link(SPObject *to, size_t pos=Glib::ustring::npos)
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
Preference storage class.
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Interface for refcounted XML nodes.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
KnotHolderEntity definition.
void create(SPDesktop *desktop, SPItem *item, KnotHolder *parent, Inkscape::CanvasItemCtrlType type=Inkscape::CANVAS_ITEM_CTRL_TYPE_DEFAULT, Glib::ustring const &name="unknown", char const *tip="", uint32_t color=0xffffff00)
void add(KnotHolderEntity *e)
Inkscape::LivePathEffect::Effect * _effect
Wrapper around a Geom::PathVector object.
Typed SVG document implementation.
Inkscape::Util::Quantity getWidth() const
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
Inkscape::Util::Unit const * getDisplayUnit()
guaranteed not to return nullptr
Base class for visual SVG elements.
Geom::OptRect geometricBounds(Geom::Affine const &transform=Geom::identity()) const
Get item's geometric bounding box in this item's coordinate system.
void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv=nullptr, bool compensate=true)
Set a new transform on an object.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
SPObject * appendChildRepr(Inkscape::XML::Node *repr)
Append repr as child of this object.
SPObject * nthChild(unsigned index)
void addChild(Inkscape::XML::Node *child, Inkscape::XML::Node *prev=nullptr)
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
T< SPAttr::FILL_RULE, SPIEnum< SPWindRule > > fill_rule
fill-rule: 0 nonzero, 1 evenodd
constexpr Coord infinity()
Get a value representing infinity.
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Specific geometry functions for Inkscape, not provided my lib2geom.
Gtk::Image * sp_get_icon_image(Glib::ustring const &icon_name, int size)
Macro for icon names used in Inkscape.
LPE <tiling> implementation.
Various utility functions.
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Affine identity()
Create an identity matrix.
Piecewise< SBasis > min(SBasis const &f, SBasis const &g)
Return the more negative of the two functions pointwise.
static R & release(R &r)
Decrements the reference count of a anchored object.
Gtk::ToggleButton * create_radio_button(Gtk::ToggleButton *&group, const Glib::ustring &tooltip, const Glib::ustring &icon_name)
void align_widgets(const std::vector< Gtk::Widget * > &widgets, int spinbutton_chars=7)
void pack_end(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the end of box.
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
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.
Glib::ustring format_classic(T const &... args)
@ CANVAS_ITEM_CTRL_TYPE_LPE
static cairo_user_data_key_t key
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Singleton class to access the preferences file in a convenient way.
Conversion between SBasis and Bezier basis polynomials.
Geom::Affine sp_item_transform_repr(SPItem *item)
Find out the inverse of previous transform of an item (from its repr)
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
Interface for XML documents.
virtual Node * createElement(char const *name)=0
SPStyle - a style object for SPItem objects.
bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
std::string sp_svg_transform_write(Geom::Affine const &transform)
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)