Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
clonetiler.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/* Authors:
7 * bulia byak <buliabyak@users.sf.net>
8 * Johan Engelen <goejendaagh@zonnet.nl>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 * Romain de Bossoreille
12 *
13 * Copyright (C) 2004-2011 Authors
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include "clonetiler.h"
18
19#include <memory>
20#include <2geom/transforms.h>
21#include <glibmm/i18n.h>
22#include <gtkmm/adjustment.h>
23#include <gtkmm/checkbutton.h>
24#include <gtkmm/combobox.h>
25#include <gtkmm/comboboxtext.h>
26#include <gtkmm/frame.h>
27#include <gtkmm/grid.h>
28#include <gtkmm/liststore.h>
29#include <gtkmm/notebook.h>
30#include <gtkmm/sizegroup.h>
31#include <gtkmm/togglebutton.h>
32
33#include "desktop.h"
34#include "document-undo.h"
35#include "document.h"
36#include "filter-chemistry.h"
37#include "inkscape.h"
38#include "message-stack.h"
39#include "selection.h"
40#include "display/cairo-utils.h"
42#include "display/drawing.h"
44#include "object/sp-item.h"
45#include "object/sp-namedview.h"
46#include "object/sp-root.h"
47#include "object/sp-use.h"
48#include "svg/svg.h"
49#include "ui/icon-loader.h"
50#include "ui/icon-names.h"
51#include "ui/pack.h"
52#include "ui/util.h"
54#include "ui/widget/unit-menu.h"
56
58
59namespace Inkscape::UI {
60
61namespace Widget {
62
67class CheckButtonInternal final : public Gtk::CheckButton {
68
69 bool _uncheckable = false;
70
71public:
72 CheckButtonInternal() = default;
73
74 CheckButtonInternal(const Glib::ustring &label)
75 : Gtk::CheckButton(label)
76 {}
77
78 void set_uncheckable(const bool val = true) { _uncheckable = val; }
79 bool get_uncheckable() const { return _uncheckable; }
80};
81
82} // namespace Widget
83
84namespace Dialog {
85
86static constexpr int SB_MARGIN = 1;
87static constexpr int VB_MARGIN = 4;
88
89static Glib::ustring const prefs_path = "/dialogs/clonetiler/";
90
91static std::unique_ptr<Inkscape::Drawing> trace_drawing;
92static unsigned trace_visionkey;
93static gdouble trace_zoom;
94static SPDocument *trace_doc = nullptr;
95
97 : DialogBase("/dialogs/clonetiler/", "CloneTiler")
98 , table_row_labels(nullptr)
99{
100 set_spacing(0);
101
102 {
103 auto prefs = Inkscape::Preferences::get();
104
105 auto const mainbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 4);
106 mainbox->set_margin(6);
107 UI::pack_start(*this, *mainbox, true, true);
108
109 nb = Gtk::make_managed<Gtk::Notebook>();
110 UI::pack_start(*mainbox, *nb, false, false);
111
112 // Symmetry
113 {
114 auto vb = new_tab(nb, _("_Symmetry"));
115
116 /* TRANSLATORS: For the following 17 symmetry groups, see
117 * http://www.bib.ulb.ac.be/coursmath/doc/17.htm (visual examples);
118 * http://www.clarku.edu/~djoyce/wallpaper/seventeen.html (English vocabulary); or
119 * http://membres.lycos.fr/villemingerard/Geometri/Sym1D.htm (French vocabulary).
120 */
121 struct SymGroups {
122 gint group;
123 Glib::ustring label;
124 } const sym_groups[] = {
125 // TRANSLATORS: "translation" means "shift" / "displacement" here.
126 {TILE_P1, _("<b>P1</b>: simple translation")},
127 {TILE_P2, _("<b>P2</b>: 180&#176; rotation")},
128 {TILE_PM, _("<b>PM</b>: reflection")},
129 // TRANSLATORS: "glide reflection" is a reflection and a translation combined.
130 // For more info, see http://mathforum.org/sum95/suzanne/symsusan.html
131 {TILE_PG, _("<b>PG</b>: glide reflection")},
132 {TILE_CM, _("<b>CM</b>: reflection + glide reflection")},
133 {TILE_PMM, _("<b>PMM</b>: reflection + reflection")},
134 {TILE_PMG, _("<b>PMG</b>: reflection + 180&#176; rotation")},
135 {TILE_PGG, _("<b>PGG</b>: glide reflection + 180&#176; rotation")},
136 {TILE_CMM, _("<b>CMM</b>: reflection + reflection + 180&#176; rotation")},
137 {TILE_P4, _("<b>P4</b>: 90&#176; rotation")},
138 {TILE_P4M, _("<b>P4M</b>: 90&#176; rotation + 45&#176; reflection")},
139 {TILE_P4G, _("<b>P4G</b>: 90&#176; rotation + 90&#176; reflection")},
140 {TILE_P3, _("<b>P3</b>: 120&#176; rotation")},
141 {TILE_P31M, _("<b>P31M</b>: reflection + 120&#176; rotation, dense")},
142 {TILE_P3M1, _("<b>P3M1</b>: reflection + 120&#176; rotation, sparse")},
143 {TILE_P6, _("<b>P6</b>: 60&#176; rotation")},
144 {TILE_P6M, _("<b>P6M</b>: reflection + 60&#176; rotation")},
145 };
146
147 gint current = prefs->getInt(prefs_path + "symmetrygroup", 0);
148
149 // Add a new combo box widget with the list of symmetry groups to the vbox
150 auto const combo = Gtk::make_managed<Gtk::ComboBoxText>();
151 combo->set_tooltip_text(_("Select one of the 17 symmetry groups for the tiling"));
152
153 // Hack to add markup support
154 auto cell_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(combo->gobj()));
155 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo->gobj()),
156 GTK_CELL_RENDERER(cell_list->data),
157 "markup", 0, nullptr);
158
159 for (const auto & sg : sym_groups) {
160 // Add the description of the symgroup to a new row
161 combo->append(sg.label);
162 }
163
164 UI::pack_start(*vb, *combo, false, false, SB_MARGIN);
165
166 combo->set_active(current);
167 combo->signal_changed().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::symgroup_changed), combo));
168 }
169
170 table_row_labels = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL);
171
172 // Shift
173 {
174 auto vb = new_tab(nb, _("S_hift"));
175
176 auto table = table_x_y_rand (3);
177 UI::pack_start(*vb, *table, false, false);
178
179 // X
180 {
181 auto const l = Gtk::make_managed<Gtk::Label>("");
182 // TRANSLATORS: "shift" means: the tiles will be shifted (offset) horizontally by this amount
183 // xgettext:no-c-format
184 l->set_markup(_("<b>Shift X:</b>"));
185 l->set_xalign(0.0);
186 table_row_labels->add_widget(*l);
187 table_attach (table, l, 1, 2, 1);
188 }
189
190 {
191 auto l = spinbox (
192 // xgettext:no-c-format
193 _("Horizontal shift per row (in % of tile width)"), "shiftx_per_j",
194 -10000, 10000, "%");
195 table_attach (table, l, 0, 2, 2);
196 }
197
198 {
199 auto l = spinbox (
200 // xgettext:no-c-format
201 _("Horizontal shift per column (in % of tile width)"), "shiftx_per_i",
202 -10000, 10000, "%");
203 table_attach (table, l, 0, 2, 3);
204 }
205
206 {
207 auto l = spinbox (_("Randomize the horizontal shift by this percentage"), "shiftx_rand",
208 0, 1000, "%");
209 table_attach (table, l, 0, 2, 4);
210 }
211
212 // Y
213 {
214 auto const l = Gtk::make_managed<Gtk::Label>("");
215 // TRANSLATORS: "shift" means: the tiles will be shifted (offset) vertically by this amount
216 // xgettext:no-c-format
217 l->set_markup(_("<b>Shift Y:</b>"));
218 l->set_xalign(0.0);
219 table_row_labels->add_widget(*l);
220 table_attach (table, l, 1, 3, 1);
221 }
222
223 {
224 auto l = spinbox (
225 // xgettext:no-c-format
226 _("Vertical shift per row (in % of tile height)"), "shifty_per_j",
227 -10000, 10000, "%");
228 table_attach (table, l, 0, 3, 2);
229 }
230
231 {
232 auto l = spinbox (
233 // xgettext:no-c-format
234 _("Vertical shift per column (in % of tile height)"), "shifty_per_i",
235 -10000, 10000, "%");
236 table_attach (table, l, 0, 3, 3);
237 }
238
239 {
240 auto l = spinbox (
241 _("Randomize the vertical shift by this percentage"), "shifty_rand",
242 0, 1000, "%");
243 table_attach (table, l, 0, 3, 4);
244 }
245
246 // Exponent
247 {
248 auto const l = Gtk::make_managed<Gtk::Label>("");
249 l->set_markup(_("<b>Exponent:</b>"));
250 l->set_xalign(0.0);
251 table_row_labels->add_widget(*l);
252 table_attach (table, l, 1, 4, 1);
253 }
254
255 {
256 auto l = spinbox (
257 _("Whether rows are spaced evenly (1), converge (<1) or diverge (>1)"), "shifty_exp",
258 0, 10, "", true);
259 table_attach (table, l, 0, 4, 2);
260 }
261
262 {
263 auto l = spinbox (
264 _("Whether columns are spaced evenly (1), converge (<1) or diverge (>1)"), "shiftx_exp",
265 0, 10, "", true);
266 table_attach (table, l, 0, 4, 3);
267 }
268
269 { // alternates
270 auto const l = Gtk::make_managed<Gtk::Label>("");
271 // TRANSLATORS: "Alternate" is a verb here
272 l->set_markup(_("<small>Alternate:</small>"));
273 l->set_xalign(0.0);
274 table_row_labels->add_widget(*l);
275 table_attach (table, l, 1, 5, 1);
276 }
277
278 {
279 auto l = checkbox (_("Alternate the sign of shifts for each row"), "shifty_alternate");
280 table_attach (table, l, 0, 5, 2);
281 }
282
283 {
284 auto l = checkbox (_("Alternate the sign of shifts for each column"), "shiftx_alternate");
285 table_attach (table, l, 0, 5, 3);
286 }
287
288 { // Cumulate
289 auto const l = Gtk::make_managed<Gtk::Label>("");
290 // TRANSLATORS: "Cumulate" is a verb here
291 l->set_markup(_("<small>Cumulate:</small>"));
292 l->set_xalign(0.0);
293 table_row_labels->add_widget(*l);
294 table_attach (table, l, 1, 6, 1);
295 }
296
297 {
298 auto l = checkbox (_("Cumulate the shifts for each row"), "shifty_cumulate");
299 table_attach (table, l, 0, 6, 2);
300 }
301
302 {
303 auto l = checkbox (_("Cumulate the shifts for each column"), "shiftx_cumulate");
304 table_attach (table, l, 0, 6, 3);
305 }
306
307 { // Exclude tile width and height in shift
308 auto const l = Gtk::make_managed<Gtk::Label>("");
309 // TRANSLATORS: "Cumulate" is a verb here
310 l->set_markup(_("<small>Exclude tile:</small>"));
311 l->set_xalign(0.0);
312 table_row_labels->add_widget(*l);
313 table_attach (table, l, 1, 7, 1);
314 }
315
316 {
317 auto l = checkbox (_("Exclude tile height in shift"), "shifty_excludeh");
318 table_attach (table, l, 0, 7, 2);
319 }
320
321 {
322 auto l = checkbox (_("Exclude tile width in shift"), "shiftx_excludew");
323 table_attach (table, l, 0, 7, 3);
324 }
325
326 }
327
328
329 // Scale
330 {
331 auto vb = new_tab(nb, _("Sc_ale"));
332
333 auto table = table_x_y_rand(2);
334 UI::pack_start(*vb, *table, false, false);
335
336 // X
337 {
338 auto const l = Gtk::make_managed<Gtk::Label>("");
339 l->set_markup(_("<b>Scale X:</b>"));
340 l->set_xalign(0.0);
341 table_row_labels->add_widget(*l);
342 table_attach (table, l, 1, 2, 1);
343 }
344
345 {
346 auto l = spinbox (
347 // xgettext:no-c-format
348 _("Horizontal scale per row (in % of tile width)"), "scalex_per_j",
349 -100, 1000, "%");
350 table_attach (table, l, 0, 2, 2);
351 }
352
353 {
354 auto l = spinbox (
355 // xgettext:no-c-format
356 _("Horizontal scale per column (in % of tile width)"), "scalex_per_i",
357 -100, 1000, "%");
358 table_attach (table, l, 0, 2, 3);
359 }
360
361 {
362 auto l = spinbox (_("Randomize the horizontal scale by this percentage"), "scalex_rand",
363 0, 1000, "%");
364 table_attach (table, l, 0, 2, 4);
365 }
366
367 // Y
368 {
369 auto const l = Gtk::make_managed<Gtk::Label>("");
370 l->set_markup(_("<b>Scale Y:</b>"));
371 l->set_xalign(0.0);
372 table_row_labels->add_widget(*l);
373 table_attach (table, l, 1, 3, 1);
374 }
375
376 {
377 auto l = spinbox (
378 // xgettext:no-c-format
379 _("Vertical scale per row (in % of tile height)"), "scaley_per_j",
380 -100, 1000, "%");
381 table_attach (table, l, 0, 3, 2);
382 }
383
384 {
385 auto l = spinbox (
386 // xgettext:no-c-format
387 _("Vertical scale per column (in % of tile height)"), "scaley_per_i",
388 -100, 1000, "%");
389 table_attach (table, l, 0, 3, 3);
390 }
391
392 {
393 auto l = spinbox (_("Randomize the vertical scale by this percentage"), "scaley_rand",
394 0, 1000, "%");
395 table_attach (table, l, 0, 3, 4);
396 }
397
398 // Exponent
399 {
400 auto const l = Gtk::make_managed<Gtk::Label>("");
401 l->set_markup(_("<b>Exponent:</b>"));
402 l->set_xalign(0.0);
403 table_row_labels->add_widget(*l);
404 table_attach (table, l, 1, 4, 1);
405 }
406
407 {
408 auto l = spinbox (_("Whether row scaling is uniform (1), converge (<1) or diverge (>1)"), "scaley_exp",
409 0, 10, "", true);
410 table_attach (table, l, 0, 4, 2);
411 }
412
413 {
414 auto l = spinbox (_("Whether column scaling is uniform (1), converge (<1) or diverge (>1)"), "scalex_exp",
415 0, 10, "", true);
416 table_attach (table, l, 0, 4, 3);
417 }
418
419 // Logarithmic (as in logarithmic spiral)
420 {
421 auto const l = Gtk::make_managed<Gtk::Label>("");
422 l->set_markup(_("<b>Base:</b>"));
423 l->set_xalign(0.0);
424 table_row_labels->add_widget(*l);
425 table_attach (table, l, 1, 5, 1);
426 }
427
428 {
429 auto l = spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scaley_log",
430 0, 10, "", false);
431 table_attach (table, l, 0, 5, 2);
432 }
433
434 {
435 auto l = spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scalex_log",
436 0, 10, "", false);
437 table_attach (table, l, 0, 5, 3);
438 }
439
440 { // alternates
441 auto const l = Gtk::make_managed<Gtk::Label>("");
442 // TRANSLATORS: "Alternate" is a verb here
443 l->set_markup(_("<small>Alternate:</small>"));
444 l->set_xalign(0.0);
445 table_row_labels->add_widget(*l);
446 table_attach (table, l, 1, 6, 1);
447 }
448
449 {
450 auto l = checkbox (_("Alternate the sign of scales for each row"), "scaley_alternate");
451 table_attach (table, l, 0, 6, 2);
452 }
453
454 {
455 auto l = checkbox (_("Alternate the sign of scales for each column"), "scalex_alternate");
456 table_attach (table, l, 0, 6, 3);
457 }
458
459 { // Cumulate
460 auto const l = Gtk::make_managed<Gtk::Label>("");
461 // TRANSLATORS: "Cumulate" is a verb here
462 l->set_markup(_("<small>Cumulate:</small>"));
463 l->set_xalign(0.0);
464 table_row_labels->add_widget(*l);
465 table_attach (table, l, 1, 7, 1);
466 }
467
468 {
469 auto l = checkbox (_("Cumulate the scales for each row"), "scaley_cumulate");
470 table_attach (table, l, 0, 7, 2);
471 }
472
473 {
474 auto l = checkbox (_("Cumulate the scales for each column"), "scalex_cumulate");
475 table_attach (table, l, 0, 7, 3);
476 }
477
478 }
479
480
481 // Rotation
482 {
483 auto vb = new_tab(nb, _("_Rotation"));
484
485 auto table = table_x_y_rand (1);
486 UI::pack_start(*vb, *table, false, false);
487
488 // Angle
489 {
490 auto const l = Gtk::make_managed<Gtk::Label>("");
491 l->set_markup(_("<b>Angle:</b>"));
492 l->set_xalign(0.0);
493 table_row_labels->add_widget(*l);
494 table_attach (table, l, 1, 2, 1);
495 }
496
497 {
498 auto l = spinbox (
499 // xgettext:no-c-format
500 _("Rotate tiles by this angle for each row"), "rotate_per_j",
501 -180, 180, "&#176;");
502 table_attach (table, l, 0, 2, 2);
503 }
504
505 {
506 auto l = spinbox (
507 // xgettext:no-c-format
508 _("Rotate tiles by this angle for each column"), "rotate_per_i",
509 -180, 180, "&#176;");
510 table_attach (table, l, 0, 2, 3);
511 }
512
513 {
514 auto l = spinbox (_("Randomize the rotation angle by this percentage"), "rotate_rand",
515 0, 100, "%");
516 table_attach (table, l, 0, 2, 4);
517 }
518
519 { // alternates
520 auto const l = Gtk::make_managed<Gtk::Label>("");
521 // TRANSLATORS: "Alternate" is a verb here
522 l->set_markup(_("<small>Alternate:</small>"));
523 l->set_xalign(0.0);
524 table_row_labels->add_widget(*l);
525 table_attach (table, l, 1, 3, 1);
526 }
527
528 {
529 auto l = checkbox (_("Alternate the rotation direction for each row"), "rotate_alternatej");
530 table_attach (table, l, 0, 3, 2);
531 }
532
533 {
534 auto l = checkbox (_("Alternate the rotation direction for each column"), "rotate_alternatei");
535 table_attach (table, l, 0, 3, 3);
536 }
537
538 { // Cumulate
539 auto const l = Gtk::make_managed<Gtk::Label>("");
540 // TRANSLATORS: "Cumulate" is a verb here
541 l->set_markup(_("<small>Cumulate:</small>"));
542 l->set_xalign(0.0);
543 table_row_labels->add_widget(*l);
544 table_attach (table, l, 1, 4, 1);
545 }
546
547 {
548 auto l = checkbox (_("Cumulate the rotation for each row"), "rotate_cumulatej");
549 table_attach (table, l, 0, 4, 2);
550 }
551
552 {
553 auto l = checkbox (_("Cumulate the rotation for each column"), "rotate_cumulatei");
554 table_attach (table, l, 0, 4, 3);
555 }
556
557 }
558
559
560 // Blur and opacity
561 {
562 auto vb = new_tab(nb, _("_Blur & opacity"));
563
564 auto table = table_x_y_rand(1);
565 UI::pack_start(*vb, *table, false, false);
566
567
568 // Blur
569 {
570 auto const l = Gtk::make_managed<Gtk::Label>("");
571 l->set_markup(_("<b>Blur:</b>"));
572 l->set_xalign(0.0);
573 table_row_labels->add_widget(*l);
574 table_attach (table, l, 1, 2, 1);
575 }
576
577 {
578 auto l = spinbox (_("Blur tiles by this percentage for each row"), "blur_per_j",
579 0, 100, "%");
580 table_attach (table, l, 0, 2, 2);
581 }
582
583 {
584 auto l = spinbox (_("Blur tiles by this percentage for each column"), "blur_per_i",
585 0, 100, "%");
586 table_attach (table, l, 0, 2, 3);
587 }
588
589 {
590 auto l = spinbox (_("Randomize the tile blur by this percentage"), "blur_rand",
591 0, 100, "%");
592 table_attach (table, l, 0, 2, 4);
593 }
594
595 { // alternates
596 auto const l = Gtk::make_managed<Gtk::Label>("");
597 // TRANSLATORS: "Alternate" is a verb here
598 l->set_markup(_("<small>Alternate:</small>"));
599 l->set_xalign(0.0);
600 table_row_labels->add_widget(*l);
601 table_attach (table, l, 1, 3, 1);
602 }
603
604 {
605 auto l = checkbox (_("Alternate the sign of blur change for each row"), "blur_alternatej");
606 table_attach (table, l, 0, 3, 2);
607 }
608
609 {
610 auto l = checkbox (_("Alternate the sign of blur change for each column"), "blur_alternatei");
611 table_attach (table, l, 0, 3, 3);
612 }
613
614
615
616 // Dissolve
617 {
618 auto const l = Gtk::make_managed<Gtk::Label>("");
619 l->set_markup(_("<b>Opacity:</b>"));
620 l->set_xalign(0.0);
621 table_row_labels->add_widget(*l);
622 table_attach (table, l, 1, 4, 1);
623 }
624
625 {
626 auto l = spinbox (_("Decrease tile opacity by this percentage for each row"), "opacity_per_j",
627 0, 100, "%");
628 table_attach (table, l, 0, 4, 2);
629 }
630
631 {
632 auto l = spinbox (_("Decrease tile opacity by this percentage for each column"), "opacity_per_i",
633 0, 100, "%");
634 table_attach (table, l, 0, 4, 3);
635 }
636
637 {
638 auto l = spinbox (_("Randomize the tile opacity by this percentage"), "opacity_rand",
639 0, 100, "%");
640 table_attach (table, l, 0, 4, 4);
641 }
642
643 { // alternates
644 auto const l = Gtk::make_managed<Gtk::Label>("");
645 // TRANSLATORS: "Alternate" is a verb here
646 l->set_markup(_("<small>Alternate:</small>"));
647 l->set_xalign(0.0);
648 table_row_labels->add_widget(*l);
649 table_attach (table, l, 1, 5, 1);
650 }
651
652 {
653 auto l = checkbox (_("Alternate the sign of opacity change for each row"), "opacity_alternatej");
654 table_attach (table, l, 0, 5, 2);
655 }
656
657 {
658 auto l = checkbox (_("Alternate the sign of opacity change for each column"), "opacity_alternatei");
659 table_attach (table, l, 0, 5, 3);
660 }
661 }
662
663
664 // Color
665 {
666 auto vb = new_tab(nb, _("Co_lor"));
667
668 {
669 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
670
671 auto const l = Gtk::make_managed<Gtk::Label>(_("Initial color: "));
672 UI::pack_start(*hb, *l, false, false);
673
674 auto color = prefs->getColor(prefs_path + "initial_color", "#000000ff");
675 color_picker = Gtk::make_managed<UI::Widget::ColorPicker>(_("Initial color of tiled clones"),
676 _("Initial color for clones (works only if the original has unset fill or stroke or on spray tool in copy mode)"),
677 color, false);
679
680 UI::pack_start(*hb, *color_picker, false, false);
681
682 UI::pack_start(*vb, *hb, false, false);
683 }
684
685
686 auto table = table_x_y_rand(3);
687 UI::pack_start(*vb, *table, false, false);
688
689 // Hue
690 {
691 auto const l = Gtk::make_managed<Gtk::Label>("");
692 l->set_markup(_("<b>H:</b>"));
693 l->set_xalign(0.0);
694 table_row_labels->add_widget(*l);
695 table_attach (table, l, 1, 2, 1);
696 }
697
698 {
699 auto l = spinbox (_("Change the tile hue by this percentage for each row"), "hue_per_j",
700 -100, 100, "%");
701 table_attach (table, l, 0, 2, 2);
702 }
703
704 {
705 auto l = spinbox (_("Change the tile hue by this percentage for each column"), "hue_per_i",
706 -100, 100, "%");
707 table_attach (table, l, 0, 2, 3);
708 }
709
710 {
711 auto l = spinbox (_("Randomize the tile hue by this percentage"), "hue_rand",
712 0, 100, "%");
713 table_attach (table, l, 0, 2, 4);
714 }
715
716
717 // Saturation
718 {
719 auto const l = Gtk::make_managed<Gtk::Label>("");
720 l->set_markup(_("<b>S:</b>"));
721 l->set_xalign(0.0);
722 table_row_labels->add_widget(*l);
723 table_attach (table, l, 1, 3, 1);
724 }
725
726 {
727 auto l = spinbox (_("Change the color saturation by this percentage for each row"), "saturation_per_j",
728 -100, 100, "%");
729 table_attach (table, l, 0, 3, 2);
730 }
731
732 {
733 auto l = spinbox (_("Change the color saturation by this percentage for each column"), "saturation_per_i",
734 -100, 100, "%");
735 table_attach (table, l, 0, 3, 3);
736 }
737
738 {
739 auto l = spinbox (_("Randomize the color saturation by this percentage"), "saturation_rand",
740 0, 100, "%");
741 table_attach (table, l, 0, 3, 4);
742 }
743
744 // Lightness
745 {
746 auto const l = Gtk::make_managed<Gtk::Label>("");
747 l->set_markup(_("<b>L:</b>"));
748 l->set_xalign(0.0);
749 table_row_labels->add_widget(*l);
750 table_attach (table, l, 1, 4, 1);
751 }
752
753 {
754 auto l = spinbox (_("Change the color lightness by this percentage for each row"), "lightness_per_j",
755 -100, 100, "%");
756 table_attach (table, l, 0, 4, 2);
757 }
758
759 {
760 auto l = spinbox (_("Change the color lightness by this percentage for each column"), "lightness_per_i",
761 -100, 100, "%");
762 table_attach (table, l, 0, 4, 3);
763 }
764
765 {
766 auto l = spinbox (_("Randomize the color lightness by this percentage"), "lightness_rand",
767 0, 100, "%");
768 table_attach (table, l, 0, 4, 4);
769 }
770
771
772 { // alternates
773 auto const l = Gtk::make_managed<Gtk::Label>("");
774 l->set_markup(_("<small>Alternate:</small>"));
775 l->set_xalign(0.0);
776 table_row_labels->add_widget(*l);
777 table_attach (table, l, 1, 5, 1);
778 }
779
780 {
781 auto l = checkbox (_("Alternate the sign of color changes for each row"), "color_alternatej");
782 table_attach (table, l, 0, 5, 2);
783 }
784
785 {
786 auto l = checkbox (_("Alternate the sign of color changes for each column"), "color_alternatei");
787 table_attach (table, l, 0, 5, 3);
788 }
789
790 }
791
792 // Trace
793 {
794 auto vb = new_tab(nb, _("_Trace"));
795 {
796 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, VB_MARGIN);
797 hb->set_margin(4);
798 UI::pack_start(*vb, *hb, false, false);
799
800 _b = Gtk::make_managed<UI::Widget::CheckButtonInternal>(_("Trace the drawing under the clones/sprayed items"));
801 _b->set_uncheckable();
802 bool old = prefs->getBool(prefs_path + "dotrace");
803 _b->set_active(old);
804 _b->set_tooltip_text(_("For each clone/sprayed item, pick a value from the drawing in its location and apply it"));
805 UI::pack_start(*hb, *_b, false, false);
806 _b->signal_toggled().connect(sigc::mem_fun(*this, &CloneTiler::do_pick_toggled));
807 }
808
809 {
810 auto const vvb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
811 UI::pack_start(*vb, *vvb, false, false);
812 _dotrace = vvb;
813
814 {
815 auto const frame = Gtk::make_managed<Gtk::Frame>(_("1. Pick from the drawing:"));
816 frame->add_css_class("flat");
817 UI::pack_start(*vvb, *frame, false, false);
818
819 auto const table = Gtk::make_managed<Gtk::Grid>();
820 table->set_row_spacing(4);
821 table->set_column_spacing(6);
822 table->set_margin(4);
823 frame->set_child(*table);
824
825 Gtk::CheckButton *rb_group;
826 {
827 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("Color"));
828 rb_group = radio;
829 radio->set_tooltip_text(_("Pick the visible color and opacity"));
830 table_attach(table, radio, 0.0, 1, 1);
831 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_COLOR));
832 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_COLOR);
833 }
834 {
835 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("Opacity"));
836 radio->set_group(*rb_group);
837 radio->set_tooltip_text(_("Pick the total accumulated opacity"));
838 table_attach (table, radio, 0.0, 2, 1);
839 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_OPACITY));
840 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_OPACITY);
841 }
842 {
843 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("R"));
844 radio->set_group(*rb_group);
845 radio->set_tooltip_text(_("Pick the Red component of the color"));
846 table_attach (table, radio, 0.0, 1, 2);
847 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_R));
848 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_R);
849 }
850 {
851 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("G"));
852 radio->set_group(*rb_group);
853 radio->set_tooltip_text(_("Pick the Green component of the color"));
854 table_attach (table, radio, 0.0, 2, 2);
855 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_G));
856 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_G);
857 }
858 {
859 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("B"));
860 radio->set_group(*rb_group);
861 radio->set_tooltip_text(_("Pick the Blue component of the color"));
862 table_attach (table, radio, 0.0, 3, 2);
863 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_B));
864 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_B);
865 }
866 {
867 auto const radio = Gtk::make_managed<Gtk::CheckButton>(C_("Clonetiler color hue", "H"));
868 radio->set_group(*rb_group);
869 radio->set_tooltip_text(_("Pick the hue of the color"));
870 table_attach (table, radio, 0.0, 1, 3);
871 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_H));
872 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_H);
873 }
874 {
875 auto const radio = Gtk::make_managed<Gtk::CheckButton>(C_("Clonetiler color saturation", "S"));
876 radio->set_group(*rb_group);
877 radio->set_tooltip_text(_("Pick the saturation of the color"));
878 table_attach (table, radio, 0.0, 2, 3);
879 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_S));
880 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_S);
881 }
882 {
883 auto const radio = Gtk::make_managed<Gtk::CheckButton>(C_("Clonetiler color lightness", "L"));
884 radio->set_group(*rb_group);
885 radio->set_tooltip_text(_("Pick the lightness of the color"));
886 table_attach (table, radio, 0.0, 3, 3);
887 radio->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_switched), PICK_L));
888 radio->set_active(prefs->getInt(prefs_path + "pick", 0) == PICK_L);
889 }
890
891 }
892
893 {
894 auto const frame = Gtk::make_managed<Gtk::Frame>(_("2. Tweak the picked value:"));
895 frame->add_css_class("flat");
896 UI::pack_start(*vvb, *frame, false, false, VB_MARGIN);
897
898 auto const table = Gtk::make_managed<Gtk::Grid>();
899 table->set_row_spacing(4);
900 table->set_column_spacing(6);
901 table->set_halign(Gtk::Align::START);
902 table->set_margin(4);
903 frame->set_child(*table);
904
905 {
906 auto const l = Gtk::make_managed<Gtk::Label>("");
907 l->set_markup(_("Gamma-correct:"));
908 table_attach (table, l, 1.0, 1, 1);
909 }
910 {
911 auto l = spinbox (_("Shift the mid-range of the picked value upwards (>0) or downwards (<0)"), "gamma_picked",
912 -10, 10, "");
913 table_attach (table, l, 0.0, 1, 2);
914 }
915
916 {
917 auto const l = Gtk::make_managed<Gtk::Label>("");
918 l->set_markup(_("Randomize:"));
919 table_attach (table, l, 1.0, 1, 3);
920 }
921 {
922 auto l = spinbox (_("Randomize the picked value by this percentage"), "rand_picked",
923 0, 100, "%");
924 table_attach (table, l, 0.0, 1, 4);
925 }
926
927 {
928 auto const l = Gtk::make_managed<Gtk::Label>("");
929 l->set_markup(_("Invert:"));
930 table_attach (table, l, 1.0, 2, 1);
931 }
932 {
933 auto l = checkbox (_("Invert the picked value"), "invert_picked");
934 table_attach (table, l, 0.0, 2, 2);
935 }
936 }
937
938 {
939 auto const frame = Gtk::make_managed<Gtk::Frame>(_("3. Apply the value to the clones':"));
940 frame->add_css_class("flat");
941 UI::pack_start(*vvb, *frame, false, false);
942
943 auto const table = Gtk::make_managed<Gtk::Grid>();
944 table->set_row_spacing(4);
945 table->set_column_spacing(6);
946 table->set_margin(4);
947 frame->set_child(*table);
948
949 {
950 auto const b = Gtk::make_managed<Gtk::CheckButton>(_("Presence"));
951 bool old = prefs->getBool(prefs_path + "pick_to_presence", true);
952 b->set_active(old);
953 b->set_tooltip_text(_("Each clone is created with the probability determined by the picked value in that point"));
954 table_attach (table, b, 0.0, 1, 1);
955 b->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_to), b, "pick_to_presence"));
956 }
957
958 {
959 auto const b = Gtk::make_managed<Gtk::CheckButton>(_("Size"));
960 bool old = prefs->getBool(prefs_path + "pick_to_size");
961 b->set_active(old);
962 b->set_tooltip_text(_("Each clone's size is determined by the picked value in that point"));
963 table_attach (table, b, 0.0, 2, 1);
964 b->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_to), b, "pick_to_size"));
965 }
966
967 {
968 auto const b = Gtk::make_managed<Gtk::CheckButton>(_("Color"));
969 bool old = prefs->getBool(prefs_path + "pick_to_color", false);
970 b->set_active(old);
971 b->set_tooltip_text(_("Each clone is painted by the picked color (the original must have unset fill or stroke)"));
972 table_attach (table, b, 0.0, 1, 2);
973 b->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_to), b, "pick_to_color"));
974 }
975
976 {
977 auto const b = Gtk::make_managed<Gtk::CheckButton>(_("Opacity"));
978 bool old = prefs->getBool(prefs_path + "pick_to_opacity", false);
979 b->set_active(old);
980 b->set_tooltip_text(_("Each clone's opacity is determined by the picked value in that point"));
981 table_attach (table, b, 0.0, 2, 2);
982 b->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::pick_to), b, "pick_to_opacity"));
983 }
984 }
985 vvb->set_sensitive(prefs->getBool(prefs_path + "dotrace"));
986 }
987 }
988
989 {
990 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, VB_MARGIN);
991 UI::pack_start(*mainbox, *hb, false, false);
992
993 auto const l = Gtk::make_managed<Gtk::Label>("");
994 l->set_markup(_("Apply to tiled clones:"));
995 UI::pack_start(*hb, *l, false, false);
996 }
997
998 // Rows/columns, width/height
999 {
1000 auto const table = Gtk::make_managed<Gtk::Grid>();
1001 table->set_row_spacing(4);
1002 table->set_column_spacing(6);
1003 table->set_margin(VB_MARGIN);
1004 UI::pack_start(*mainbox, *table, false, false);
1005
1006 {
1007 {
1008 auto a = Gtk::Adjustment::create(0.0, 1, 500, 1, 10, 0);
1009 int value = prefs->getInt(prefs_path + "jmax", 2);
1010 a->set_value (value);
1011
1012 auto const sb = Gtk::make_managed<UI::Widget::SpinButton>(a, 1.0, 0);
1013 sb->set_tooltip_text (_("How many rows in the tiling"));
1014 sb->set_width_chars (7);
1015 sb->set_name("row");
1016 table_attach(table, sb, 0.0f, 1, 2);
1017 _rowscols.push_back(sb);
1018
1019 a->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::xy_changed), a, "jmax"));
1020 }
1021
1022 {
1023 auto const l = Gtk::make_managed<Gtk::Label>("");
1024 l->set_markup("&#215;");
1025 table_attach(table, l, 0.0f, 1, 3);
1026 _rowscols.push_back(l);
1027 }
1028
1029 {
1030 auto a = Gtk::Adjustment::create(0.0, 1, 500, 1, 10, 0);
1031 int value = prefs->getInt(prefs_path + "imax", 2);
1032 a->set_value (value);
1033
1034 auto const sb = Gtk::make_managed<UI::Widget::SpinButton>(a, 1.0, 0);
1035 sb->set_tooltip_text (_("How many columns in the tiling"));
1036 sb->set_width_chars (7);
1037 table_attach(table, sb, 0.0f, 1, 4);
1038 _rowscols.push_back(sb);
1039
1040 a->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::xy_changed), a, "imax"));
1041 }
1042 }
1043
1044 {
1045 // unitmenu
1046 unit_menu = Gtk::make_managed<UI::Widget::UnitMenu>();
1048 unit_menu->setUnit(SP_ACTIVE_DESKTOP->getNamedView()->display_units->abbr);
1049 unitChangedConn = unit_menu->signal_changed().connect(sigc::mem_fun(*this, &CloneTiler::unit_changed));
1050
1051 {
1052 // Width spinbutton
1053 fill_width = Gtk::Adjustment::create(0.0, -1e6, 1e6, 1.0, 10.0, 0);
1054
1055 double value = prefs->getDouble(prefs_path + "fillwidth", 50.0);
1056 Inkscape::Util::Unit const *unit = unit_menu->getUnit();
1057 gdouble const units = Inkscape::Util::Quantity::convert(value, "px", unit);
1058 fill_width->set_value (units);
1059
1060 auto const e = Gtk::make_managed<UI::Widget::SpinButton>(fill_width, 1.0, 2);
1061 e->set_tooltip_text (_("Width of the rectangle to be filled"));
1062 e->set_width_chars (7);
1063 e->set_digits (4);
1064 table_attach(table, e, 0.0f, 2, 2);
1065 _widthheight.push_back(e);
1066 fill_width->signal_value_changed().connect(sigc::mem_fun(*this, &CloneTiler::fill_width_changed));
1067 }
1068 {
1069 auto const l = Gtk::make_managed<Gtk::Label>("");
1070 l->set_markup("&#215;");
1071 table_attach(table, l, 0.0f, 2, 3);
1072 _widthheight.push_back(l);
1073 }
1074
1075 {
1076 // Height spinbutton
1077 fill_height = Gtk::Adjustment::create(0.0, -1e6, 1e6, 1.0, 10.0, 0);
1078
1079 double value = prefs->getDouble(prefs_path + "fillheight", 50.0);
1080 Inkscape::Util::Unit const *unit = unit_menu->getUnit();
1081 gdouble const units = Inkscape::Util::Quantity::convert(value, "px", unit);
1082 fill_height->set_value (units);
1083
1084 auto const e = Gtk::make_managed<UI::Widget::SpinButton>(fill_height, 1.0, 2);
1085 e->set_tooltip_text (_("Height of the rectangle to be filled"));
1086 e->set_width_chars (7);
1087 e->set_digits (4);
1088 table_attach(table, e, 0.0f, 2, 4);
1089 _widthheight.push_back(e);
1090 fill_height->signal_value_changed().connect(sigc::mem_fun(*this, &CloneTiler::fill_height_changed));
1091 }
1092
1093 table_attach(table, unit_menu, 0.0f, 2, 5);
1094 _widthheight.push_back(unit_menu);
1095 }
1096
1097 // Switch
1098 Gtk::CheckButton *rb_group;
1099 {
1100 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("Rows, columns: "));
1101 rb_group = radio;
1102 radio->set_tooltip_text(_("Create the specified number of rows and columns"));
1103 table_attach(table, radio, 0.0, 1, 1);
1104
1105 if (!prefs->getBool(prefs_path + "fillrect")) {
1106 radio->set_active(true);
1108 }
1109 radio->signal_toggled().connect(sigc::mem_fun(*this, &CloneTiler::switch_to_create));
1110 }
1111 {
1112 auto const radio = Gtk::make_managed<Gtk::CheckButton>(_("Width, height: "));
1113 radio->set_group(*rb_group);
1114 radio->set_tooltip_text(_("Fill the specified width and height with the tiling"));
1115 table_attach(table, radio, 0.0, 2, 1);
1116
1117 if (prefs->getBool(prefs_path + "fillrect")) {
1118 radio->set_active(true);
1120 }
1121 radio->signal_toggled().connect(sigc::mem_fun(*this, &CloneTiler::switch_to_fill));
1122 }
1123 }
1124
1125
1126 // Use saved pos
1127 {
1128 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, VB_MARGIN);
1129 UI::pack_start(*mainbox, *hb, false, false);
1130
1131 _cb_keep_bbox = Gtk::make_managed<UI::Widget::CheckButtonInternal>(_("Use saved size and position of the tile"));
1132 auto keepbbox = prefs->getBool(prefs_path + "keepbbox", true);
1133 _cb_keep_bbox->set_active(keepbbox);
1134 _cb_keep_bbox->set_tooltip_text(_("Pretend that the size and position of the tile are the same "
1135 "as the last time you tiled it (if any), instead of using the "
1136 "current size"));
1137 UI::pack_start(*hb, *_cb_keep_bbox, false, false);
1138 _cb_keep_bbox->signal_toggled().connect(sigc::mem_fun(*this, &CloneTiler::keep_bbox_toggled));
1139 }
1140
1141 // Statusbar
1142 {
1143 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, VB_MARGIN);
1144 UI::pack_end(*mainbox, *hb, false, false);
1145
1146 auto const l = Gtk::make_managed<Gtk::Label>("");
1147 _status = l;
1148 UI::pack_start(*hb, *l, false, false);
1149 }
1150
1151 // Buttons
1152 {
1153 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, VB_MARGIN);
1154 UI::pack_start(*mainbox, *hb, false, false);
1155
1156 {
1157 auto const b = Gtk::make_managed<Gtk::Button>();
1158 auto const l = Gtk::make_managed<Gtk::Label>("");
1159 l->set_markup_with_mnemonic(_(" <b>_Create</b> "));
1160 b->set_child(*l);
1161 b->set_tooltip_text(_("Create and tile the clones of the selection"));
1162 b->signal_clicked().connect(sigc::mem_fun(*this, &CloneTiler::apply));
1163 UI::pack_end(*hb, *b, false, false);
1164 }
1165
1166 { // buttons which are enabled only when there are tiled clones
1167 auto const sb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
1168 UI::pack_end(*hb, *sb, false, false);
1169 sb->set_hexpand();
1170
1171 _buttons_on_tiles = sb;
1172
1173 {
1174 // TRANSLATORS: if a group of objects are "clumped" together, then they
1175 // are unevenly spread in the given amount of space - as shown in the
1176 // diagrams on the left in the following screenshot:
1177 // http://www.inkscape.org/screenshots/gallery/inkscape-0.42-CVS-tiles-unclump.png
1178 // So unclumping is the process of spreading a number of objects out more evenly.
1179 auto const b = Gtk::make_managed<Gtk::Button>(_(" _Unclump "), true);
1180 b->set_tooltip_text(_("Spread out clones to reduce clumping; can be applied repeatedly"));
1181 b->signal_clicked().connect(sigc::mem_fun(*this, &CloneTiler::unclump));
1182 UI::pack_end(*sb, *b, false, false);
1183 }
1184
1185 {
1186 auto const b = Gtk::make_managed<Gtk::Button>(_(" Re_move "), true);
1187 b->set_tooltip_text(_("Remove existing tiled clones of the selected object (siblings only)"));
1188 b->signal_clicked().connect(sigc::mem_fun(*this, &CloneTiler::on_remove_button_clicked));
1189 UI::pack_end(*sb, *b, false, false);
1190 }
1191
1192 // connect to global selection changed signal (so we can change desktops) and
1193 // external_change (so we're not fooled by undo)
1194 selectChangedConn = INKSCAPE.signal_selection_changed.connect(sigc::mem_fun(*this, &CloneTiler::change_selection));
1195 externChangedConn = INKSCAPE.signal_external_change.connect(sigc::mem_fun(*this, &CloneTiler::external_change));
1196
1197 // update now
1198 change_selection(SP_ACTIVE_DESKTOP->getSelection());
1199 }
1200
1201 {
1202 auto const b = Gtk::make_managed<Gtk::Button>(_(" R_eset "), true);
1203 // TRANSLATORS: "change" is a noun here
1204 b->set_tooltip_text(_("Reset all shifts, scales, rotates, opacity and color changes in the dialog to zero"));
1205 b->signal_clicked().connect(sigc::mem_fun(*this, &CloneTiler::reset));
1206 UI::pack_start(*hb, *b, false, false);
1207 }
1208 }
1209 }
1210
1211 set_visible(true);
1212}
1213
1215{
1216 selectChangedConn.disconnect();
1217 externChangedConn.disconnect();
1218 color_changed_connection.disconnect();
1219}
1220
1222{
1223 static bool is_updating = false;
1224 if (is_updating || !SP_ACTIVE_DESKTOP)
1225 return;
1226
1227 is_updating = true;
1229 prefs->setColor(prefs_path + "initial_color", color);
1230 is_updating = false;
1231}
1232
1234{
1235 if (selection->isEmpty()) {
1236 _buttons_on_tiles->set_sensitive(false);
1237 _status->set_markup(_("<small>Nothing selected.</small>"));
1238 return;
1239 }
1240
1241 if (boost::distance(selection->items()) > 1) {
1242 _buttons_on_tiles->set_sensitive(false);
1243 _status->set_markup(_("<small>More than one object selected.</small>"));
1244 return;
1245 }
1246
1248 if (n > 0) {
1249 _buttons_on_tiles->set_sensitive(true);
1250 gchar *sta = g_strdup_printf (_("<small>Object has <b>%d</b> tiled clones.</small>"), n);
1251 _status->set_markup(sta);
1252 g_free (sta);
1253 } else {
1254 _buttons_on_tiles->set_sensitive(false);
1255 _status->set_markup(_("<small>Object has no tiled clones.</small>"));
1256 }
1257}
1258
1260{
1261 change_selection(SP_ACTIVE_DESKTOP->getSelection());
1262}
1263
1265 // symmetry group
1266 int type,
1267
1268 // row, column
1269 int i, int j,
1270
1271 // center, width, height of the tile
1272 double cx, double cy,
1273 double w, double h,
1274
1275 // values from the dialog:
1276 // Shift
1277 double shiftx_per_i, double shifty_per_i,
1278 double shiftx_per_j, double shifty_per_j,
1279 double shiftx_rand, double shifty_rand,
1280 double shiftx_exp, double shifty_exp,
1281 int shiftx_alternate, int shifty_alternate,
1282 int shiftx_cumulate, int shifty_cumulate,
1283 int shiftx_excludew, int shifty_excludeh,
1284
1285 // Scale
1286 double scalex_per_i, double scaley_per_i,
1287 double scalex_per_j, double scaley_per_j,
1288 double scalex_rand, double scaley_rand,
1289 double scalex_exp, double scaley_exp,
1290 double scalex_log, double scaley_log,
1291 int scalex_alternate, int scaley_alternate,
1292 int scalex_cumulate, int scaley_cumulate,
1293
1294 // Rotation
1295 double rotate_per_i, double rotate_per_j,
1296 double rotate_rand,
1297 int rotate_alternatei, int rotate_alternatej,
1298 int rotate_cumulatei, int rotate_cumulatej
1299 )
1300{
1301
1302 // Shift (in units of tile width or height) -------------
1303 double delta_shifti = 0.0;
1304 double delta_shiftj = 0.0;
1305
1306 if( shiftx_alternate ) {
1307 delta_shifti = (double)(i%2);
1308 } else {
1309 if( shiftx_cumulate ) { // Should the delta shifts be cumulative (i.e. 1, 1+2, 1+2+3, ...)
1310 delta_shifti = (double)(i*i);
1311 } else {
1312 delta_shifti = (double)i;
1313 }
1314 }
1315
1316 if( shifty_alternate ) {
1317 delta_shiftj = (double)(j%2);
1318 } else {
1319 if( shifty_cumulate ) {
1320 delta_shiftj = (double)(j*j);
1321 } else {
1322 delta_shiftj = (double)j;
1323 }
1324 }
1325
1326 // Random shift, only calculate if non-zero.
1327 double delta_shiftx_rand = 0.0;
1328 double delta_shifty_rand = 0.0;
1329 if( shiftx_rand != 0.0 ) delta_shiftx_rand = shiftx_rand * g_random_double_range (-1, 1);
1330 if( shifty_rand != 0.0 ) delta_shifty_rand = shifty_rand * g_random_double_range (-1, 1);
1331
1332
1333 // Delta shift (units of tile width/height)
1334 double di = shiftx_per_i * delta_shifti + shiftx_per_j * delta_shiftj + delta_shiftx_rand;
1335 double dj = shifty_per_i * delta_shifti + shifty_per_j * delta_shiftj + delta_shifty_rand;
1336
1337 // Shift in actual x and y, used below
1338 double dx = w * di;
1339 double dy = h * dj;
1340
1341 double shifti = di;
1342 double shiftj = dj;
1343
1344 // Include tile width and height in shift if required
1345 if( !shiftx_excludew ) shifti += i;
1346 if( !shifty_excludeh ) shiftj += j;
1347
1348 // Add exponential shift if necessary
1349 double shifti_sign = (shifti > 0.0) ? 1.0 : -1.0;
1350 shifti = shifti_sign * pow(fabs(shifti), shiftx_exp);
1351 double shiftj_sign = (shiftj > 0.0) ? 1.0 : -1.0;
1352 shiftj = shiftj_sign * pow(fabs(shiftj), shifty_exp);
1353
1354 // Final shift
1355 Geom::Affine rect_translate (Geom::Translate (w * shifti, h * shiftj));
1356
1357 // Rotation (in degrees) ------------
1358 double delta_rotationi = 0.0;
1359 double delta_rotationj = 0.0;
1360
1361 if( rotate_alternatei ) {
1362 delta_rotationi = (double)(i%2);
1363 } else {
1364 if( rotate_cumulatei ) {
1365 delta_rotationi = (double)(i*i + i)/2.0;
1366 } else {
1367 delta_rotationi = (double)i;
1368 }
1369 }
1370
1371 if( rotate_alternatej ) {
1372 delta_rotationj = (double)(j%2);
1373 } else {
1374 if( rotate_cumulatej ) {
1375 delta_rotationj = (double)(j*j + j)/2.0;
1376 } else {
1377 delta_rotationj = (double)j;
1378 }
1379 }
1380
1381 double delta_rotate_rand = 0.0;
1382 if( rotate_rand != 0.0 ) delta_rotate_rand = rotate_rand * 180.0 * g_random_double_range (-1, 1);
1383
1384 double dr = rotate_per_i * delta_rotationi + rotate_per_j * delta_rotationj + delta_rotate_rand;
1385
1386 // Scale (times the original) -----------
1387 double delta_scalei = 0.0;
1388 double delta_scalej = 0.0;
1389
1390 if( scalex_alternate ) {
1391 delta_scalei = (double)(i%2);
1392 } else {
1393 if( scalex_cumulate ) { // Should the delta scales be cumulative (i.e. 1, 1+2, 1+2+3, ...)
1394 delta_scalei = (double)(i*i + i)/2.0;
1395 } else {
1396 delta_scalei = (double)i;
1397 }
1398 }
1399
1400 if( scaley_alternate ) {
1401 delta_scalej = (double)(j%2);
1402 } else {
1403 if( scaley_cumulate ) {
1404 delta_scalej = (double)(j*j + j)/2.0;
1405 } else {
1406 delta_scalej = (double)j;
1407 }
1408 }
1409
1410 // Random scale, only calculate if non-zero.
1411 double delta_scalex_rand = 0.0;
1412 double delta_scaley_rand = 0.0;
1413 if( scalex_rand != 0.0 ) delta_scalex_rand = scalex_rand * g_random_double_range (-1, 1);
1414 if( scaley_rand != 0.0 ) delta_scaley_rand = scaley_rand * g_random_double_range (-1, 1);
1415 // But if random factors are same, scale x and y proportionally
1416 if( scalex_rand == scaley_rand ) delta_scalex_rand = delta_scaley_rand;
1417
1418 // Total delta scale
1419 double scalex = 1.0 + scalex_per_i * delta_scalei + scalex_per_j * delta_scalej + delta_scalex_rand;
1420 double scaley = 1.0 + scaley_per_i * delta_scalei + scaley_per_j * delta_scalej + delta_scaley_rand;
1421
1422 if( scalex < 0.0 ) scalex = 0.0;
1423 if( scaley < 0.0 ) scaley = 0.0;
1424
1425 // Add exponential scale if necessary
1426 if ( scalex_exp != 1.0 ) scalex = pow( scalex, scalex_exp );
1427 if ( scaley_exp != 1.0 ) scaley = pow( scaley, scaley_exp );
1428
1429 // Add logarithmic factor if necessary
1430 if ( scalex_log > 0.0 ) scalex = pow( scalex_log, scalex - 1.0 );
1431 if ( scaley_log > 0.0 ) scaley = pow( scaley_log, scaley - 1.0 );
1432 // Alternative using rotation angle
1433 //if ( scalex_log != 1.0 ) scalex *= pow( scalex_log, M_PI*dr/180 );
1434 //if ( scaley_log != 1.0 ) scaley *= pow( scaley_log, M_PI*dr/180 );
1435
1436
1437 // Calculate transformation matrices, translating back to "center of tile" (rotation center) before transforming
1438 Geom::Affine drot_c = Geom::Translate(-cx, -cy) * Geom::Rotate (M_PI*dr/180) * Geom::Translate(cx, cy);
1439
1440 Geom::Affine dscale_c = Geom::Translate(-cx, -cy) * Geom::Scale (scalex, scaley) * Geom::Translate(cx, cy);
1441
1442 Geom::Affine d_s_r = dscale_c * drot_c;
1443
1444 Geom::Affine rotate_180_c = Geom::Translate(-cx, -cy) * Geom::Rotate (M_PI) * Geom::Translate(cx, cy);
1445
1446 Geom::Affine rotate_90_c = Geom::Translate(-cx, -cy) * Geom::Rotate (-M_PI/2) * Geom::Translate(cx, cy);
1447 Geom::Affine rotate_m90_c = Geom::Translate(-cx, -cy) * Geom::Rotate ( M_PI/2) * Geom::Translate(cx, cy);
1448
1449 Geom::Affine rotate_120_c = Geom::Translate(-cx, -cy) * Geom::Rotate (-2*M_PI/3) * Geom::Translate(cx, cy);
1450 Geom::Affine rotate_m120_c = Geom::Translate(-cx, -cy) * Geom::Rotate ( 2*M_PI/3) * Geom::Translate(cx, cy);
1451
1452 Geom::Affine rotate_60_c = Geom::Translate(-cx, -cy) * Geom::Rotate (-M_PI/3) * Geom::Translate(cx, cy);
1453 Geom::Affine rotate_m60_c = Geom::Translate(-cx, -cy) * Geom::Rotate ( M_PI/3) * Geom::Translate(cx, cy);
1454
1455 Geom::Affine flip_x = Geom::Translate(-cx, -cy) * Geom::Scale (-1, 1) * Geom::Translate(cx, cy);
1456 Geom::Affine flip_y = Geom::Translate(-cx, -cy) * Geom::Scale (1, -1) * Geom::Translate(cx, cy);
1457
1458
1459 // Create tile with required symmetry
1460 const double cos60 = cos(M_PI/3);
1461 const double sin60 = sin(M_PI/3);
1462 const double cos30 = cos(M_PI/6);
1463 const double sin30 = sin(M_PI/6);
1464
1465 switch (type) {
1466
1467 case TILE_P1:
1468 return d_s_r * rect_translate;
1469 break;
1470
1471 case TILE_P2:
1472 if (i % 2 == 0) {
1473 return d_s_r * rect_translate;
1474 } else {
1475 return d_s_r * rotate_180_c * rect_translate;
1476 }
1477 break;
1478
1479 case TILE_PM:
1480 if (i % 2 == 0) {
1481 return d_s_r * rect_translate;
1482 } else {
1483 return d_s_r * flip_x * rect_translate;
1484 }
1485 break;
1486
1487 case TILE_PG:
1488 if (j % 2 == 0) {
1489 return d_s_r * rect_translate;
1490 } else {
1491 return d_s_r * flip_x * rect_translate;
1492 }
1493 break;
1494
1495 case TILE_CM:
1496 if ((i + j) % 2 == 0) {
1497 return d_s_r * rect_translate;
1498 } else {
1499 return d_s_r * flip_x * rect_translate;
1500 }
1501 break;
1502
1503 case TILE_PMM:
1504 if (j % 2 == 0) {
1505 if (i % 2 == 0) {
1506 return d_s_r * rect_translate;
1507 } else {
1508 return d_s_r * flip_x * rect_translate;
1509 }
1510 } else {
1511 if (i % 2 == 0) {
1512 return d_s_r * flip_y * rect_translate;
1513 } else {
1514 return d_s_r * flip_x * flip_y * rect_translate;
1515 }
1516 }
1517 break;
1518
1519 case TILE_PMG:
1520 if (j % 2 == 0) {
1521 if (i % 2 == 0) {
1522 return d_s_r * rect_translate;
1523 } else {
1524 return d_s_r * rotate_180_c * rect_translate;
1525 }
1526 } else {
1527 if (i % 2 == 0) {
1528 return d_s_r * flip_y * rect_translate;
1529 } else {
1530 return d_s_r * rotate_180_c * flip_y * rect_translate;
1531 }
1532 }
1533 break;
1534
1535 case TILE_PGG:
1536 if (j % 2 == 0) {
1537 if (i % 2 == 0) {
1538 return d_s_r * rect_translate;
1539 } else {
1540 return d_s_r * flip_y * rect_translate;
1541 }
1542 } else {
1543 if (i % 2 == 0) {
1544 return d_s_r * rotate_180_c * rect_translate;
1545 } else {
1546 return d_s_r * rotate_180_c * flip_y * rect_translate;
1547 }
1548 }
1549 break;
1550
1551 case TILE_CMM:
1552 if (j % 4 == 0) {
1553 if (i % 2 == 0) {
1554 return d_s_r * rect_translate;
1555 } else {
1556 return d_s_r * flip_x * rect_translate;
1557 }
1558 } else if (j % 4 == 1) {
1559 if (i % 2 == 0) {
1560 return d_s_r * flip_y * rect_translate;
1561 } else {
1562 return d_s_r * flip_x * flip_y * rect_translate;
1563 }
1564 } else if (j % 4 == 2) {
1565 if (i % 2 == 1) {
1566 return d_s_r * rect_translate;
1567 } else {
1568 return d_s_r * flip_x * rect_translate;
1569 }
1570 } else {
1571 if (i % 2 == 1) {
1572 return d_s_r * flip_y * rect_translate;
1573 } else {
1574 return d_s_r * flip_x * flip_y * rect_translate;
1575 }
1576 }
1577 break;
1578
1579 case TILE_P4:
1580 {
1581 Geom::Affine ori (Geom::Translate ((w + h) * pow((i/2), shiftx_exp) + dx, (h + w) * pow((j/2), shifty_exp) + dy));
1582 Geom::Affine dia1 (Geom::Translate (w/2 + h/2, -h/2 + w/2));
1583 Geom::Affine dia2 (Geom::Translate (-w/2 + h/2, h/2 + w/2));
1584 if (j % 2 == 0) {
1585 if (i % 2 == 0) {
1586 return d_s_r * ori;
1587 } else {
1588 return d_s_r * rotate_m90_c * dia1 * ori;
1589 }
1590 } else {
1591 if (i % 2 == 0) {
1592 return d_s_r * rotate_90_c * dia2 * ori;
1593 } else {
1594 return d_s_r * rotate_180_c * dia1 * dia2 * ori;
1595 }
1596 }
1597 }
1598 break;
1599
1600 case TILE_P4M:
1601 {
1602 double max = MAX(w, h);
1603 Geom::Affine ori (Geom::Translate ((max + max) * pow((i/4), shiftx_exp) + dx, (max + max) * pow((j/2), shifty_exp) + dy));
1604 Geom::Affine dia1 (Geom::Translate ( w/2 - h/2, h/2 - w/2));
1605 Geom::Affine dia2 (Geom::Translate (-h/2 + w/2, w/2 - h/2));
1606 if (j % 2 == 0) {
1607 if (i % 4 == 0) {
1608 return d_s_r * ori;
1609 } else if (i % 4 == 1) {
1610 return d_s_r * flip_y * rotate_m90_c * dia1 * ori;
1611 } else if (i % 4 == 2) {
1612 return d_s_r * rotate_m90_c * dia1 * Geom::Translate (h, 0) * ori;
1613 } else if (i % 4 == 3) {
1614 return d_s_r * flip_x * Geom::Translate (w, 0) * ori;
1615 }
1616 } else {
1617 if (i % 4 == 0) {
1618 return d_s_r * flip_y * Geom::Translate(0, h) * ori;
1619 } else if (i % 4 == 1) {
1620 return d_s_r * rotate_90_c * dia2 * Geom::Translate(0, h) * ori;
1621 } else if (i % 4 == 2) {
1622 return d_s_r * flip_y * rotate_90_c * dia2 * Geom::Translate(h, 0) * Geom::Translate(0, h) * ori;
1623 } else if (i % 4 == 3) {
1624 return d_s_r * flip_y * flip_x * Geom::Translate(w, 0) * Geom::Translate(0, h) * ori;
1625 }
1626 }
1627 }
1628 break;
1629
1630 case TILE_P4G:
1631 {
1632 double max = MAX(w, h);
1633 Geom::Affine ori (Geom::Translate ((max + max) * pow((i/4), shiftx_exp) + dx, (max + max) * pow(j, shifty_exp) + dy));
1634 Geom::Affine dia1 (Geom::Translate ( w/2 + h/2, h/2 - w/2));
1635 Geom::Affine dia2 (Geom::Translate (-h/2 + w/2, w/2 + h/2));
1636 if (((i/4) + j) % 2 == 0) {
1637 if (i % 4 == 0) {
1638 return d_s_r * ori;
1639 } else if (i % 4 == 1) {
1640 return d_s_r * rotate_m90_c * dia1 * ori;
1641 } else if (i % 4 == 2) {
1642 return d_s_r * rotate_90_c * dia2 * ori;
1643 } else if (i % 4 == 3) {
1644 return d_s_r * rotate_180_c * dia1 * dia2 * ori;
1645 }
1646 } else {
1647 if (i % 4 == 0) {
1648 return d_s_r * flip_y * Geom::Translate (0, h) * ori;
1649 } else if (i % 4 == 1) {
1650 return d_s_r * flip_y * rotate_m90_c * dia1 * Geom::Translate (-h, 0) * ori;
1651 } else if (i % 4 == 2) {
1652 return d_s_r * flip_y * rotate_90_c * dia2 * Geom::Translate (h, 0) * ori;
1653 } else if (i % 4 == 3) {
1654 return d_s_r * flip_x * Geom::Translate (w, 0) * ori;
1655 }
1656 }
1657 }
1658 break;
1659
1660 case TILE_P3:
1661 {
1662 double width;
1663 double height;
1664 Geom::Affine dia1;
1665 Geom::Affine dia2;
1666 if (w > h) {
1667 width = w + w * cos60;
1668 height = 2 * w * sin60;
1669 dia1 = Geom::Affine (Geom::Translate (w/2 + w/2 * cos60, -(w/2 * sin60)));
1670 dia2 = dia1 * Geom::Affine (Geom::Translate (0, 2 * (w/2 * sin60)));
1671 } else {
1672 width = h * cos (M_PI/6);
1673 height = h;
1674 dia1 = Geom::Affine (Geom::Translate (h/2 * cos30, -(h/2 * sin30)));
1675 dia2 = dia1 * Geom::Affine (Geom::Translate (0, h/2));
1676 }
1677 Geom::Affine ori (Geom::Translate (width * pow((2*(i/3) + j%2), shiftx_exp) + dx, (height/2) * pow(j, shifty_exp) + dy));
1678 if (i % 3 == 0) {
1679 return d_s_r * ori;
1680 } else if (i % 3 == 1) {
1681 return d_s_r * rotate_m120_c * dia1 * ori;
1682 } else if (i % 3 == 2) {
1683 return d_s_r * rotate_120_c * dia2 * ori;
1684 }
1685 }
1686 break;
1687
1688 case TILE_P31M:
1689 {
1690 Geom::Affine ori;
1691 Geom::Affine dia1;
1692 Geom::Affine dia2;
1693 Geom::Affine dia3;
1694 Geom::Affine dia4;
1695 if (w > h) {
1696 ori = Geom::Affine(Geom::Translate (w * pow((i/6) + 0.5*(j%2), shiftx_exp) + dx, (w * cos30) * pow(j, shifty_exp) + dy));
1697 dia1 = Geom::Affine (Geom::Translate (0, h/2) * Geom::Translate (w/2, 0) * Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (-h/2 * cos30, -h/2 * sin30) );
1698 dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
1699 dia3 = dia2 * Geom::Affine (Geom::Translate (0, 2 * (w/2 * sin60 - h/2 * sin30)));
1700 dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
1701 } else {
1702 ori = Geom::Affine (Geom::Translate (2*h * cos30 * pow((i/6 + 0.5*(j%2)), shiftx_exp) + dx, (2*h - h * sin30) * pow(j, shifty_exp) + dy));
1703 dia1 = Geom::Affine (Geom::Translate (0, -h/2) * Geom::Translate (h/2 * cos30, h/2 * sin30));
1704 dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
1705 dia3 = dia2 * Geom::Affine (Geom::Translate (0, h/2));
1706 dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
1707 }
1708 if (i % 6 == 0) {
1709 return d_s_r * ori;
1710 } else if (i % 6 == 1) {
1711 return d_s_r * flip_y * rotate_m120_c * dia1 * ori;
1712 } else if (i % 6 == 2) {
1713 return d_s_r * rotate_m120_c * dia2 * ori;
1714 } else if (i % 6 == 3) {
1715 return d_s_r * flip_y * rotate_120_c * dia3 * ori;
1716 } else if (i % 6 == 4) {
1717 return d_s_r * rotate_120_c * dia4 * ori;
1718 } else if (i % 6 == 5) {
1719 return d_s_r * flip_y * Geom::Translate(0, h) * ori;
1720 }
1721 }
1722 break;
1723
1724 case TILE_P3M1:
1725 {
1726 double width;
1727 double height;
1728 Geom::Affine dia1;
1729 Geom::Affine dia2;
1730 Geom::Affine dia3;
1731 Geom::Affine dia4;
1732 if (w > h) {
1733 width = w + w * cos60;
1734 height = 2 * w * sin60;
1735 dia1 = Geom::Affine (Geom::Translate (0, h/2) * Geom::Translate (w/2, 0) * Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (-h/2 * cos30, -h/2 * sin30) );
1736 dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
1737 dia3 = dia2 * Geom::Affine (Geom::Translate (0, 2 * (w/2 * sin60 - h/2 * sin30)));
1738 dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
1739 } else {
1740 width = 2 * h * cos (M_PI/6);
1741 height = 2 * h;
1742 dia1 = Geom::Affine (Geom::Translate (0, -h/2) * Geom::Translate (h/2 * cos30, h/2 * sin30));
1743 dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
1744 dia3 = dia2 * Geom::Affine (Geom::Translate (0, h/2));
1745 dia4 = dia3 * Geom::Affine (Geom::Translate (-h * cos30, h * sin30));
1746 }
1747 Geom::Affine ori (Geom::Translate (width * pow((2*(i/6) + j%2), shiftx_exp) + dx, (height/2) * pow(j, shifty_exp) + dy));
1748 if (i % 6 == 0) {
1749 return d_s_r * ori;
1750 } else if (i % 6 == 1) {
1751 return d_s_r * flip_y * rotate_m120_c * dia1 * ori;
1752 } else if (i % 6 == 2) {
1753 return d_s_r * rotate_m120_c * dia2 * ori;
1754 } else if (i % 6 == 3) {
1755 return d_s_r * flip_y * rotate_120_c * dia3 * ori;
1756 } else if (i % 6 == 4) {
1757 return d_s_r * rotate_120_c * dia4 * ori;
1758 } else if (i % 6 == 5) {
1759 return d_s_r * flip_y * Geom::Translate(0, h) * ori;
1760 }
1761 }
1762 break;
1763
1764 case TILE_P6:
1765 {
1766 Geom::Affine ori;
1767 Geom::Affine dia1;
1768 Geom::Affine dia2;
1769 Geom::Affine dia3;
1770 Geom::Affine dia4;
1771 Geom::Affine dia5;
1772 if (w > h) {
1773 ori = Geom::Affine(Geom::Translate (w * pow((2*(i/6) + (j%2)), shiftx_exp) + dx, (2*w * sin60) * pow(j, shifty_exp) + dy));
1774 dia1 = Geom::Affine (Geom::Translate (w/2 * cos60, -w/2 * sin60));
1775 dia2 = dia1 * Geom::Affine (Geom::Translate (w/2, 0));
1776 dia3 = dia2 * Geom::Affine (Geom::Translate (w/2 * cos60, w/2 * sin60));
1777 dia4 = dia3 * Geom::Affine (Geom::Translate (-w/2 * cos60, w/2 * sin60));
1778 dia5 = dia4 * Geom::Affine (Geom::Translate (-w/2, 0));
1779 } else {
1780 ori = Geom::Affine(Geom::Translate (2*h * cos30 * pow((i/6 + 0.5*(j%2)), shiftx_exp) + dx, (h + h * sin30) * pow(j, shifty_exp) + dy));
1781 dia1 = Geom::Affine (Geom::Translate (-w/2, -h/2) * Geom::Translate (h/2 * cos30, -h/2 * sin30) * Geom::Translate (w/2 * cos60, w/2 * sin60));
1782 dia2 = dia1 * Geom::Affine (Geom::Translate (-w/2 * cos60, -w/2 * sin60) * Geom::Translate (h/2 * cos30, -h/2 * sin30) * Geom::Translate (h/2 * cos30, h/2 * sin30) * Geom::Translate (-w/2 * cos60, w/2 * sin60));
1783 dia3 = dia2 * Geom::Affine (Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (h/2 * cos30, h/2 * sin30) * Geom::Translate (-w/2, h/2));
1784 dia4 = dia3 * dia1.inverse();
1785 dia5 = dia3 * dia2.inverse();
1786 }
1787 if (i % 6 == 0) {
1788 return d_s_r * ori;
1789 } else if (i % 6 == 1) {
1790 return d_s_r * rotate_m60_c * dia1 * ori;
1791 } else if (i % 6 == 2) {
1792 return d_s_r * rotate_m120_c * dia2 * ori;
1793 } else if (i % 6 == 3) {
1794 return d_s_r * rotate_180_c * dia3 * ori;
1795 } else if (i % 6 == 4) {
1796 return d_s_r * rotate_120_c * dia4 * ori;
1797 } else if (i % 6 == 5) {
1798 return d_s_r * rotate_60_c * dia5 * ori;
1799 }
1800 }
1801 break;
1802
1803 case TILE_P6M:
1804 {
1805
1806 Geom::Affine ori;
1807 Geom::Affine dia1, dia2, dia3, dia4, dia5, dia6, dia7, dia8, dia9, dia10;
1808 if (w > h) {
1809 ori = Geom::Affine(Geom::Translate (w * pow((2*(i/12) + (j%2)), shiftx_exp) + dx, (2*w * sin60) * pow(j, shifty_exp) + dy));
1810 dia1 = Geom::Affine (Geom::Translate (w/2, h/2) * Geom::Translate (-w/2 * cos60, -w/2 * sin60) * Geom::Translate (-h/2 * cos30, h/2 * sin30));
1811 dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, -h * sin30));
1812 dia3 = dia2 * Geom::Affine (Geom::Translate (-h/2 * cos30, h/2 * sin30) * Geom::Translate (w * cos60, 0) * Geom::Translate (-h/2 * cos30, -h/2 * sin30));
1813 dia4 = dia3 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
1814 dia5 = dia4 * Geom::Affine (Geom::Translate (-h/2 * cos30, -h/2 * sin30) * Geom::Translate (-w/2 * cos60, w/2 * sin60) * Geom::Translate (w/2, -h/2));
1815 dia6 = dia5 * Geom::Affine (Geom::Translate (0, h));
1816 dia7 = dia6 * dia1.inverse();
1817 dia8 = dia6 * dia2.inverse();
1818 dia9 = dia6 * dia3.inverse();
1819 dia10 = dia6 * dia4.inverse();
1820 } else {
1821 ori = Geom::Affine(Geom::Translate (4*h * cos30 * pow((i/12 + 0.5*(j%2)), shiftx_exp) + dx, (2*h + 2*h * sin30) * pow(j, shifty_exp) + dy));
1822 dia1 = Geom::Affine (Geom::Translate (-w/2, -h/2) * Geom::Translate (h/2 * cos30, -h/2 * sin30) * Geom::Translate (w/2 * cos60, w/2 * sin60));
1823 dia2 = dia1 * Geom::Affine (Geom::Translate (h * cos30, -h * sin30));
1824 dia3 = dia2 * Geom::Affine (Geom::Translate (-w/2 * cos60, -w/2 * sin60) * Geom::Translate (h * cos30, 0) * Geom::Translate (-w/2 * cos60, w/2 * sin60));
1825 dia4 = dia3 * Geom::Affine (Geom::Translate (h * cos30, h * sin30));
1826 dia5 = dia4 * Geom::Affine (Geom::Translate (w/2 * cos60, -w/2 * sin60) * Geom::Translate (h/2 * cos30, h/2 * sin30) * Geom::Translate (-w/2, h/2));
1827 dia6 = dia5 * Geom::Affine (Geom::Translate (0, h));
1828 dia7 = dia6 * dia1.inverse();
1829 dia8 = dia6 * dia2.inverse();
1830 dia9 = dia6 * dia3.inverse();
1831 dia10 = dia6 * dia4.inverse();
1832 }
1833 if (i % 12 == 0) {
1834 return d_s_r * ori;
1835 } else if (i % 12 == 1) {
1836 return d_s_r * flip_y * rotate_m60_c * dia1 * ori;
1837 } else if (i % 12 == 2) {
1838 return d_s_r * rotate_m60_c * dia2 * ori;
1839 } else if (i % 12 == 3) {
1840 return d_s_r * flip_y * rotate_m120_c * dia3 * ori;
1841 } else if (i % 12 == 4) {
1842 return d_s_r * rotate_m120_c * dia4 * ori;
1843 } else if (i % 12 == 5) {
1844 return d_s_r * flip_x * dia5 * ori;
1845 } else if (i % 12 == 6) {
1846 return d_s_r * flip_x * flip_y * dia6 * ori;
1847 } else if (i % 12 == 7) {
1848 return d_s_r * flip_y * rotate_120_c * dia7 * ori;
1849 } else if (i % 12 == 8) {
1850 return d_s_r * rotate_120_c * dia8 * ori;
1851 } else if (i % 12 == 9) {
1852 return d_s_r * flip_y * rotate_60_c * dia9 * ori;
1853 } else if (i % 12 == 10) {
1854 return d_s_r * rotate_60_c * dia10 * ori;
1855 } else if (i % 12 == 11) {
1856 return d_s_r * flip_y * Geom::Translate (0, h) * ori;
1857 }
1858 }
1859 break;
1860
1861 default:
1862 break;
1863 }
1864
1865 return Geom::identity();
1866}
1867
1869{
1870 bool result = false;
1871 char *id_href = nullptr;
1872
1873 if (obj) {
1874 Inkscape::XML::Node *obj_repr = obj->getRepr();
1875 id_href = g_strdup_printf("#%s", obj_repr->attribute("id"));
1876 }
1877
1878 auto href = Inkscape::getHrefAttribute(*tile->getRepr()).second;
1879
1880 if (is<SPUse>(tile) &&
1881 href && (!id_href || !strcmp(id_href, href)) &&
1882 tile->getRepr()->attribute("inkscape:tiled-clone-of") &&
1883 (!id_href || !strcmp(id_href, tile->getRepr()->attribute("inkscape:tiled-clone-of"))))
1884 {
1885 result = true;
1886 } else {
1887 result = false;
1888 }
1889 if (id_href) {
1890 g_free(id_href);
1891 id_href = nullptr;
1892 }
1893 return result;
1894}
1895
1897{
1898 if (!trace_drawing)
1899 return;
1900
1901 for (auto& o: from->children) {
1902 auto item = cast<SPItem>(&o);
1903 if (item && is_a_clone_of(&o, nullptr)) {
1904 item->invoke_hide(trace_visionkey); // FIXME: hide each tiled clone's original too!
1905 }
1907 }
1908}
1909
1911{
1912 trace_drawing = std::make_unique<Inkscape::Drawing>();
1913
1914 /* Create ArenaItem and set transform */
1916 trace_doc = doc;
1917 trace_drawing->setRoot(trace_doc->getRoot()->invoke_show(*trace_drawing, trace_visionkey, SP_ITEM_SHOW_DISPLAY));
1918
1919 // hide the (current) original and any tiled clones, we only want to pick the background
1920 original->invoke_hide(trace_visionkey);
1922
1923 trace_doc->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1925
1926 trace_zoom = zoom;
1927}
1928
1930{
1931 if (!trace_drawing) {
1932 return 0;
1933 }
1934
1935 trace_drawing->root()->setTransform(Geom::Scale(trace_zoom));
1936 trace_drawing->update();
1937
1938 /* Item integer bbox in points */
1939 Geom::IntRect ibox = (box * Geom::Scale(trace_zoom)).roundOutwards();
1940
1941 /* Find visible area */
1942 cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ibox.width(), ibox.height());
1943 Inkscape::DrawingContext dc(s, ibox.min());
1944 /* Render */
1945 trace_drawing->render(dc, ibox);
1946 auto color = ink_cairo_surface_average_color(s);
1947 cairo_surface_destroy(s);
1948 return color.toRGBA();
1949}
1950
1952{
1953 if (trace_doc) {
1955 trace_doc = nullptr;
1956 trace_drawing.reset();
1957 }
1958}
1959
1961{
1962 auto selection = getSelection();
1963 if (!selection)
1964 return;
1965
1966 // check if something is selected
1967 if (selection->isEmpty() || boost::distance(selection->items()) > 1) {
1968 getDesktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one object</b> whose tiled clones to unclump."));
1969 return;
1970 }
1971
1972 auto obj = selection->singleItem();
1973 auto parent = obj->parent;
1974
1975 std::vector<SPItem*> to_unclump; // not including the original
1976
1977 for (auto& child: parent->children) {
1978 if (is_a_clone_of (&child, obj)) {
1979 to_unclump.push_back((SPItem*)&child);
1980 }
1981 }
1982
1984 reverse(to_unclump.begin(),to_unclump.end());
1985 ::unclump (to_unclump);
1986
1987 DocumentUndo::done(getDocument(), _("Unclump tiled clones"), INKSCAPE_ICON("dialog-tile-clones"));
1988}
1989
1991{
1992 SPObject *parent = obj->parent;
1993
1994 guint n = 0;
1995
1996 for (auto& child: parent->children) {
1997 if (is_a_clone_of (&child, obj)) {
1998 n ++;
1999 }
2000 }
2001
2002 return n;
2003}
2004
2005void CloneTiler::remove(bool do_undo/* = true*/)
2006{
2007 auto selection = getSelection();
2008 if (!selection)
2009 return;
2010
2011 // check if something is selected
2012 if (selection->isEmpty() || boost::distance(selection->items()) > 1) {
2013 getDesktop()->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one object</b> whose tiled clones to remove."));
2014 return;
2015 }
2016
2017 SPObject *obj = selection->singleItem();
2018 SPObject *parent = obj->parent;
2019
2020// remove old tiling
2021 std::vector<SPObject *> to_delete;
2022 for (auto& child: parent->children) {
2023 if (is_a_clone_of (&child, obj)) {
2024 to_delete.push_back(&child);
2025 }
2026 }
2027 for (auto obj:to_delete) {
2028 g_assert(obj != nullptr);
2029 obj->deleteObject();
2030 }
2031
2033
2034 if (do_undo) {
2035 DocumentUndo::done(getDocument(), _("Delete tiled clones"), INKSCAPE_ICON("dialog-tile-clones"));
2036 }
2037}
2038
2040{
2041 using Geom::X;
2042 using Geom::Y;
2043 Geom::Point const p1 = r.corner(1) * m;
2044 Geom::Point const p2 = r.corner(2) * m;
2045 Geom::Point const p3 = r.corner(3) * m;
2046 Geom::Point const p4 = r.corner(4) * m;
2047 return Geom::Rect(
2049 std::min(std::min(p1[X], p2[X]), std::min(p3[X], p4[X])),
2050 std::min(std::min(p1[Y], p2[Y]), std::min(p3[Y], p4[Y]))),
2052 std::max(std::max(p1[X], p2[X]), std::max(p3[X], p4[X])),
2053 std::max(std::max(p1[Y], p2[Y]), std::max(p3[Y], p4[Y]))));
2054}
2055
2060double CloneTiler::randomize01(double val, double rand)
2061{
2062 double base = MIN (val - rand, 1 - 2*rand);
2063 if (base < 0) {
2064 base = 0;
2065 }
2066 val = base + g_random_double_range (0, MIN (2 * rand, 1 - base));
2067 return CLAMP(val, 0, 1); // this should be unnecessary with the above provisions, but just in case...
2068}
2069
2070
2072{
2073 auto desktop = getDesktop();
2074 auto selection = getSelection();
2075 if (!selection)
2076 return;
2077
2078 // check if something is selected
2079 if (selection->isEmpty()) {
2080 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select an <b>object</b> to clone."));
2081 return;
2082 }
2083
2084 // Check if more than one object is selected.
2085 if (boost::distance(selection->items()) > 1) {
2086 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, <b>group</b> them and <b>clone the group</b>."));
2087 return;
2088 }
2089
2090 // set "busy" cursor
2092
2093 // set statusbar text
2094 _status->set_markup(_("<small>Creating tiled clones...</small>"));
2095 _status->queue_draw();
2096
2097 SPObject *obj = selection->singleItem();
2098 if (!obj) {
2099 // Should never happen (empty selection checked above).
2100 std::cerr << "CloneTiler::clonetile_apply(): No object in single item selection!!!" << std::endl;
2101 return;
2102 }
2103 Inkscape::XML::Node *obj_repr = obj->getRepr();
2104 const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id"));
2105 SPObject *parent = obj->parent;
2106
2107 remove(false);
2108
2110 double scale_units = scale[Geom::X]; // Use just x direction....
2111
2113 double shiftx_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_i", 0, -10000, 10000);
2114 double shifty_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_per_i", 0, -10000, 10000);
2115 double shiftx_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_j", 0, -10000, 10000);
2116 double shifty_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_per_j", 0, -10000, 10000);
2117 double shiftx_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_rand", 0, 0, 1000);
2118 double shifty_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_rand", 0, 0, 1000);
2119 double shiftx_exp = prefs->getDoubleLimited(prefs_path + "shiftx_exp", 1, 0, 10);
2120 double shifty_exp = prefs->getDoubleLimited(prefs_path + "shifty_exp", 1, 0, 10);
2121 bool shiftx_alternate = prefs->getBool(prefs_path + "shiftx_alternate");
2122 bool shifty_alternate = prefs->getBool(prefs_path + "shifty_alternate");
2123 bool shiftx_cumulate = prefs->getBool(prefs_path + "shiftx_cumulate");
2124 bool shifty_cumulate = prefs->getBool(prefs_path + "shifty_cumulate");
2125 bool shiftx_excludew = prefs->getBool(prefs_path + "shiftx_excludew");
2126 bool shifty_excludeh = prefs->getBool(prefs_path + "shifty_excludeh");
2127
2128 double scalex_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "scalex_per_i", 0, -100, 1000);
2129 double scaley_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "scaley_per_i", 0, -100, 1000);
2130 double scalex_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "scalex_per_j", 0, -100, 1000);
2131 double scaley_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "scaley_per_j", 0, -100, 1000);
2132 double scalex_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "scalex_rand", 0, 0, 1000);
2133 double scaley_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "scaley_rand", 0, 0, 1000);
2134 double scalex_exp = prefs->getDoubleLimited(prefs_path + "scalex_exp", 1, 0, 10);
2135 double scaley_exp = prefs->getDoubleLimited(prefs_path + "scaley_exp", 1, 0, 10);
2136 double scalex_log = prefs->getDoubleLimited(prefs_path + "scalex_log", 0, 0, 10);
2137 double scaley_log = prefs->getDoubleLimited(prefs_path + "scaley_log", 0, 0, 10);
2138 bool scalex_alternate = prefs->getBool(prefs_path + "scalex_alternate");
2139 bool scaley_alternate = prefs->getBool(prefs_path + "scaley_alternate");
2140 bool scalex_cumulate = prefs->getBool(prefs_path + "scalex_cumulate");
2141 bool scaley_cumulate = prefs->getBool(prefs_path + "scaley_cumulate");
2142
2143 double rotate_per_i = prefs->getDoubleLimited(prefs_path + "rotate_per_i", 0, -180, 180);
2144 double rotate_per_j = prefs->getDoubleLimited(prefs_path + "rotate_per_j", 0, -180, 180);
2145 double rotate_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "rotate_rand", 0, 0, 100);
2146 bool rotate_alternatei = prefs->getBool(prefs_path + "rotate_alternatei");
2147 bool rotate_alternatej = prefs->getBool(prefs_path + "rotate_alternatej");
2148 bool rotate_cumulatei = prefs->getBool(prefs_path + "rotate_cumulatei");
2149 bool rotate_cumulatej = prefs->getBool(prefs_path + "rotate_cumulatej");
2150
2151 double blur_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "blur_per_i", 0, 0, 100);
2152 double blur_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "blur_per_j", 0, 0, 100);
2153 bool blur_alternatei = prefs->getBool(prefs_path + "blur_alternatei");
2154 bool blur_alternatej = prefs->getBool(prefs_path + "blur_alternatej");
2155 double blur_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "blur_rand", 0, 0, 100);
2156
2157 double opacity_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "opacity_per_i", 0, 0, 100);
2158 double opacity_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "opacity_per_j", 0, 0, 100);
2159 bool opacity_alternatei = prefs->getBool(prefs_path + "opacity_alternatei");
2160 bool opacity_alternatej = prefs->getBool(prefs_path + "opacity_alternatej");
2161 double opacity_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "opacity_rand", 0, 0, 100);
2162
2163 auto initial_color = prefs->getColor(prefs_path + "initial_color");
2164 double hue_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "hue_per_j", 0, -100, 100);
2165 double hue_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "hue_per_i", 0, -100, 100);
2166 double hue_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "hue_rand", 0, 0, 100);
2167 double saturation_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "saturation_per_j", 0, -100, 100);
2168 double saturation_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "saturation_per_i", 0, -100, 100);
2169 double saturation_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "saturation_rand", 0, 0, 100);
2170 double lightness_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "lightness_per_j", 0, -100, 100);
2171 double lightness_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "lightness_per_i", 0, -100, 100);
2172 double lightness_rand = 0.01 * prefs->getDoubleLimited(prefs_path + "lightness_rand", 0, 0, 100);
2173 bool color_alternatej = prefs->getBool(prefs_path + "color_alternatej");
2174 bool color_alternatei = prefs->getBool(prefs_path + "color_alternatei");
2175
2176 int type = prefs->getInt(prefs_path + "symmetrygroup", 0);
2177 bool keepbbox = prefs->getBool(prefs_path + "keepbbox", true);
2178 int imax = prefs->getInt(prefs_path + "imax", 2);
2179 int jmax = prefs->getInt(prefs_path + "jmax", 2);
2180
2181 bool fillrect = prefs->getBool(prefs_path + "fillrect");
2182 double fillwidth = scale_units*prefs->getDoubleLimited(prefs_path + "fillwidth", 50, 0, 1e6);
2183 double fillheight = scale_units*prefs->getDoubleLimited(prefs_path + "fillheight", 50, 0, 1e6);
2184
2185 bool dotrace = prefs->getBool(prefs_path + "dotrace");
2186 int pick = prefs->getInt(prefs_path + "pick");
2187 bool pick_to_presence = prefs->getBool(prefs_path + "pick_to_presence");
2188 bool pick_to_size = prefs->getBool(prefs_path + "pick_to_size");
2189 bool pick_to_color = prefs->getBool(prefs_path + "pick_to_color");
2190 bool pick_to_opacity = prefs->getBool(prefs_path + "pick_to_opacity");
2191 double rand_picked = 0.01 * prefs->getDoubleLimited(prefs_path + "rand_picked", 0, 0, 100);
2192 bool invert_picked = prefs->getBool(prefs_path + "invert_picked");
2193 double gamma_picked = prefs->getDoubleLimited(prefs_path + "gamma_picked", 0, -10, 10);
2194
2195 auto item = cast<SPItem>(obj);
2196 if (dotrace) {
2197 trace_setup(getDocument(), 1.0, item);
2198 }
2199
2200 Geom::Point center;
2201 double w = 0;
2202 double h = 0;
2203 double x0 = 0;
2204 double y0 = 0;
2205
2206 if (keepbbox &&
2207 obj_repr->attribute("inkscape:tile-w") &&
2208 obj_repr->attribute("inkscape:tile-h") &&
2209 obj_repr->attribute("inkscape:tile-x0") &&
2210 obj_repr->attribute("inkscape:tile-y0") &&
2211 obj_repr->attribute("inkscape:tile-cx") &&
2212 obj_repr->attribute("inkscape:tile-cy")) {
2213
2214 double cx = obj_repr->getAttributeDouble("inkscape:tile-cx", 0);
2215 double cy = obj_repr->getAttributeDouble("inkscape:tile-cy", 0);
2216 center = Geom::Point (cx, cy);
2217
2218 w = obj_repr->getAttributeDouble("inkscape:tile-w", w);
2219 h = obj_repr->getAttributeDouble("inkscape:tile-h", h);
2220 x0 = obj_repr->getAttributeDouble("inkscape:tile-x0", x0);
2221 y0 = obj_repr->getAttributeDouble("inkscape:tile-y0", y0);
2222 } else {
2223 bool prefs_bbox = prefs->getBool("/tools/bounding_box", false);
2224 SPItem::BBoxType bbox_type = ( !prefs_bbox ?
2226 Geom::OptRect r = item->documentBounds(bbox_type);
2227 if (r) {
2228 w = scale_units*r->dimensions()[Geom::X];
2229 h = scale_units*r->dimensions()[Geom::Y];
2230 x0 = scale_units*r->min()[Geom::X];
2231 y0 = scale_units*r->min()[Geom::Y];
2232 center = scale_units*desktop->dt2doc(item->getCenter());
2233
2234 obj_repr->setAttributeSvgDouble("inkscape:tile-cx", center[Geom::X]);
2235 obj_repr->setAttributeSvgDouble("inkscape:tile-cy", center[Geom::Y]);
2236 obj_repr->setAttributeSvgDouble("inkscape:tile-w", w);
2237 obj_repr->setAttributeSvgDouble("inkscape:tile-h", h);
2238 obj_repr->setAttributeSvgDouble("inkscape:tile-x0", x0);
2239 obj_repr->setAttributeSvgDouble("inkscape:tile-y0", y0);
2240 } else {
2241 center = Geom::Point(0, 0);
2242 w = h = 0;
2243 x0 = y0 = 0;
2244 }
2245 }
2246
2247 Geom::Point cur(0, 0);
2248 Geom::Rect bbox_original (Geom::Point (x0, y0), Geom::Point (x0 + w, y0 + h));
2249 double perimeter_original = (w + h)/4;
2250
2251 // The integers i and j are reserved for tile column and row.
2252 // The doubles x and y are used for coordinates
2253 for (int i = 0;
2254 fillrect?
2255 (fabs(cur[Geom::X]) < fillwidth && i < 200) // prevent "freezing" with too large fillrect, arbitrarily limit rows
2256 : (i < imax);
2257 i ++) {
2258 for (int j = 0;
2259 fillrect?
2260 (fabs(cur[Geom::Y]) < fillheight && j < 200) // prevent "freezing" with too large fillrect, arbitrarily limit cols
2261 : (j < jmax);
2262 j ++) {
2263
2264 // Note: We create a clone at 0,0 too, right over the original, in case our clones are colored
2265
2266 // Get transform from symmetry, shift, scale, rotation
2267 Geom::Affine orig_t = get_transform (type, i, j, center[Geom::X], center[Geom::Y], w, h,
2268 shiftx_per_i, shifty_per_i,
2269 shiftx_per_j, shifty_per_j,
2270 shiftx_rand, shifty_rand,
2271 shiftx_exp, shifty_exp,
2272 shiftx_alternate, shifty_alternate,
2273 shiftx_cumulate, shifty_cumulate,
2274 shiftx_excludew, shifty_excludeh,
2275 scalex_per_i, scaley_per_i,
2276 scalex_per_j, scaley_per_j,
2277 scalex_rand, scaley_rand,
2278 scalex_exp, scaley_exp,
2279 scalex_log, scaley_log,
2280 scalex_alternate, scaley_alternate,
2281 scalex_cumulate, scaley_cumulate,
2282 rotate_per_i, rotate_per_j,
2283 rotate_rand,
2284 rotate_alternatei, rotate_alternatej,
2285 rotate_cumulatei, rotate_cumulatej );
2286 Geom::Affine parent_transform = (((SPItem*)item->parent)->i2doc_affine())*(item->document->getRoot()->c2p.inverse());
2287 Geom::Affine t = parent_transform*orig_t*parent_transform.inverse();
2288 cur = center * t - center;
2289 if (fillrect) {
2290 if ((cur[Geom::X] > fillwidth) || (cur[Geom::Y] > fillheight)) { // off limits
2291 continue;
2292 }
2293 }
2294
2295 std::string color_string;
2296
2297 // Color tab
2298 double eff_i = (color_alternatei? (i%2) : (i));
2299 double eff_j = (color_alternatej? (j%2) : (j));
2300
2301 auto hsl = *initial_color.converted(Colors::Space::Type::HSL);
2302 hsl.set(0, hsl[0] + hue_per_i * eff_i + hue_per_j * eff_j + hue_rand * g_random_double_range (-1, 1));
2303 hsl.set(1, hsl[1] + saturation_per_i * eff_i + saturation_per_j * eff_j + saturation_rand * g_random_double_range (-1, 1));
2304 hsl.set(2, hsl[2] + lightness_per_i * eff_i + lightness_per_j * eff_j + lightness_rand * g_random_double_range (-1, 1));
2305 hsl.normalize();
2306
2307 // We could convert to RGB here, but we shouldn't actually need to.
2308 color_string = hsl.toString();
2309
2310 // Blur
2311 double blur = 0.0;
2312 {
2313 int eff_i = (blur_alternatei? (i%2) : (i));
2314 int eff_j = (blur_alternatej? (j%2) : (j));
2315 blur = (blur_per_i * eff_i + blur_per_j * eff_j + blur_rand * g_random_double_range (-1, 1));
2316 blur = CLAMP (blur, 0, 1);
2317 }
2318
2319 // Opacity
2320 double opacity = 1.0;
2321 {
2322 int eff_i = (opacity_alternatei? (i%2) : (i));
2323 int eff_j = (opacity_alternatej? (j%2) : (j));
2324 opacity = 1 - (opacity_per_i * eff_i + opacity_per_j * eff_j + opacity_rand * g_random_double_range (-1, 1));
2325 opacity = CLAMP (opacity, 0, 1);
2326 }
2327
2328 // Trace tab
2329 if (dotrace) {
2330 Geom::Rect bbox_t = transform_rect (bbox_original, t*Geom::Scale(1.0/scale_units));
2331
2332 auto rgba = Colors::Color(trace_pick(bbox_t));
2333 auto hsl = *rgba.converted(Colors::Space::Type::HSL);
2334
2335 gdouble val = 0;
2336 switch (pick) {
2337 case PICK_COLOR:
2338 val = 1 - hsl[2]; // inverse lightness; to match other picks where black = max
2339 break;
2340 case PICK_OPACITY:
2341 val = rgba.getOpacity();
2342 break;
2343 case PICK_R:
2344 val = rgba[0];
2345 break;
2346 case PICK_G:
2347 val = rgba[1];
2348 break;
2349 case PICK_B:
2350 val = rgba[2];
2351 break;
2352 case PICK_H:
2353 val = hsl[0];
2354 break;
2355 case PICK_S:
2356 val = hsl[1];
2357 break;
2358 case PICK_L:
2359 val = 1 - hsl[2];
2360 break;
2361 default:
2362 break;
2363 }
2364
2365 if (rand_picked > 0) {
2366 val = randomize01 (val, rand_picked);
2367 for (auto i = 0; i < 3; i++)
2368 rgba.set(i, randomize01(rgba[i], rand_picked));
2369 }
2370
2371 if (gamma_picked != 0) {
2372 double power;
2373 if (gamma_picked > 0)
2374 power = 1/(1 + fabs(gamma_picked));
2375 else
2376 power = 1 + fabs(gamma_picked);
2377
2378 val = pow (val, power);
2379 for (auto i = 0; i < 3; i++)
2380 rgba.set(i, pow(rgba[i], power));
2381 }
2382
2383 if (invert_picked) {
2384 val = 1 - val;
2385 rgba.invert();
2386 }
2387
2388 val = CLAMP (val, 0, 1);
2389 rgba.normalize();
2390
2391 if (pick_to_presence) {
2392 if (g_random_double_range (0, 1) > val) {
2393 continue; // skip!
2394 }
2395 }
2396 if (pick_to_size) {
2397 t = parent_transform * Geom::Translate(-center[Geom::X], -center[Geom::Y])
2398 * Geom::Scale (val, val) * Geom::Translate(center[Geom::X], center[Geom::Y])
2399 * parent_transform.inverse() * t;
2400 }
2401 if (pick_to_opacity) {
2402 opacity *= val;
2403 }
2404 if (pick_to_color) {
2405 color_string = rgba.toString();
2406 }
2407 }
2408
2409 if (opacity < 1e-6) { // invisibly transparent, skip
2410 continue;
2411 }
2412
2413 if (fabs(t[0]) + fabs (t[1]) + fabs(t[2]) + fabs(t[3]) < 1e-6) { // too small, skip
2414 continue;
2415 }
2416
2417 // Create the clone
2418 Inkscape::XML::Node *clone = obj_repr->document()->createElement("svg:use");
2419 clone->setAttribute("x", "0");
2420 clone->setAttribute("y", "0");
2421 clone->setAttribute("inkscape:tiled-clone-of", id_href);
2422 clone->setAttribute("xlink:href", id_href);
2423
2424 Geom::Point new_center;
2425 bool center_set = false;
2426 if (obj_repr->attribute("inkscape:transform-center-x") || obj_repr->attribute("inkscape:transform-center-y")) {
2427 new_center = scale_units*desktop->dt2doc(item->getCenter()) * orig_t;
2428 center_set = true;
2429 }
2430
2431 clone->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(t));
2432
2433 if (opacity < 1.0) {
2434 clone->setAttributeCssDouble("opacity", opacity);
2435 }
2436
2437 if (!color_string.empty()) {
2438 clone->setAttribute("fill", color_string);
2439 clone->setAttribute("stroke", color_string);
2440 }
2441
2442 // add the new clone to the top of the original's parent
2443 parent->getRepr()->appendChild(clone);
2444
2445 if (blur > 0.0) {
2446 SPObject *clone_object = desktop->getDocument()->getObjectByRepr(clone);
2447 auto item = cast<SPItem>(clone_object);
2448 double radius = blur * perimeter_original * t.descrim();
2449 // this is necessary for all newly added clones to have correct bboxes,
2450 // otherwise filters won't work:
2452 SPFilter *constructed = new_filter_gaussian_blur(desktop->getDocument(), radius, t.descrim());
2453 constructed->update_filter_region(item);
2454 sp_style_set_property_url (clone_object, "filter", constructed, false);
2455 }
2456
2457 if (center_set) {
2458 SPObject *clone_object = desktop->getDocument()->getObjectByRepr(clone);
2459 auto item = cast<SPItem>(clone_object);
2460 if (clone_object && item) {
2461 clone_object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
2462 item->setCenter(desktop->doc2dt(new_center));
2463 clone_object->updateRepr();
2464 }
2465 }
2466
2467 Inkscape::GC::release(clone);
2468 }
2469 cur[Geom::Y] = 0;
2470 }
2471
2472 if (dotrace) {
2473 trace_finish ();
2474 }
2475
2477
2479 DocumentUndo::done(getDocument(), _("Create tiled clones"), INKSCAPE_ICON("dialog-tile-clones"));
2480}
2481
2482Gtk::Box * CloneTiler::new_tab(Gtk::Notebook *nb, const gchar *label)
2483{
2484 auto const l = Gtk::make_managed<Gtk::Label>(label, true);
2485 auto const vb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, VB_MARGIN);
2486 vb->set_margin(VB_MARGIN);
2487 nb->append_page(*vb, *l);
2488 return vb;
2489}
2490
2491void CloneTiler::checkbox_toggled(Gtk::CheckButton *tb,
2492 const Glib::ustring &attr)
2493{
2494 auto prefs = Inkscape::Preferences::get();
2495 prefs->setBool(prefs_path + attr, tb->get_active());
2496}
2497
2498Gtk::Widget * CloneTiler::checkbox(const char *tip,
2499 const Glib::ustring &attr)
2500{
2501 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, VB_MARGIN);
2502 auto const b = Gtk::make_managed<UI::Widget::CheckButtonInternal>();
2503 b->set_tooltip_text(tip);
2504
2505 auto const prefs = Inkscape::Preferences::get();
2506 auto const value = prefs->getBool(prefs_path + attr);
2507 b->set_active(value);
2508
2509 UI::pack_start(*hb, *b, false, true);
2510 b->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::checkbox_toggled), b, attr));
2511
2512 b->set_uncheckable();
2513
2514 return hb;
2515}
2516
2517void CloneTiler::value_changed(Glib::RefPtr<Gtk::Adjustment> const &adj ,
2518 Glib::ustring const &pref)
2519{
2520 auto prefs = Inkscape::Preferences::get();
2521 prefs->setDouble(prefs_path + pref, adj->get_value());
2522}
2523
2524Gtk::Widget * CloneTiler::spinbox(const char *tip,
2525 const Glib::ustring &attr,
2526 double lower,
2527 double upper,
2528 const gchar *suffix,
2529 bool exponent/* = false*/)
2530{
2531 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
2532
2533 {
2534 // Parameters for adjustment
2535 auto const initial_value = (exponent ? 1.0 : 0.0);
2536 auto const step_increment = (exponent ? 0.01 : 0.1);
2537 auto const page_increment = (exponent ? 0.05 : 0.4);
2538
2539 auto a = Gtk::Adjustment::create(initial_value,
2540 lower,
2541 upper,
2542 step_increment,
2543 page_increment);
2544
2545 auto const climb_rate = (exponent ? 0.01 : 0.1);
2546 auto const digits = (exponent ? 2 : 1);
2547
2548 auto sb = Gtk::make_managed<UI::Widget::SpinButton>(a, climb_rate, digits);
2549
2550 sb->set_tooltip_text (tip);
2551 sb->set_width_chars (5);
2552 sb->set_digits(3);
2553 UI::pack_start(*hb, *sb, false, false, SB_MARGIN);
2554
2555 auto prefs = Inkscape::Preferences::get();
2556 auto value = prefs->getDoubleLimited(prefs_path + attr, exponent? 1.0 : 0.0, lower, upper);
2557 a->set_value (value);
2558 a->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, &CloneTiler::value_changed), a, attr));
2559
2560 if (exponent) {
2561 sb->set_oneable();
2562 } else {
2563 sb->set_zeroable();
2564 }
2565 }
2566
2567 {
2568 auto const l = Gtk::make_managed<Gtk::Label>("");
2569 l->set_markup(suffix);
2570 UI::pack_start(*hb, *l);
2571 }
2572
2573 return hb;
2574}
2575
2576void CloneTiler::symgroup_changed(Gtk::ComboBox *cb)
2577{
2578 auto prefs = Inkscape::Preferences::get();
2579 auto group_new = cb->get_active_row_number();
2580 prefs->setInt(prefs_path + "symmetrygroup", group_new);
2581}
2582
2583void CloneTiler::xy_changed(Glib::RefPtr<Gtk::Adjustment> const &adj ,
2584 Glib::ustring const &pref)
2585{
2586 auto prefs = Inkscape::Preferences::get();
2587 prefs->setInt(prefs_path + pref, (int) floor(adj->get_value() + 0.5));
2588}
2589
2591{
2592 auto prefs = Inkscape::Preferences::get();
2593 prefs->setBool(prefs_path + "keepbbox", _cb_keep_bbox->get_active());
2594}
2595
2596void CloneTiler::pick_to(Gtk::CheckButton *tb, Glib::ustring const &pref)
2597{
2598 auto prefs = Inkscape::Preferences::get();
2599 prefs->setBool(prefs_path + pref, tb->get_active());
2600}
2601
2602
2604{
2605 if (w) {
2606 auto sb = dynamic_cast<Inkscape::UI::Widget::SpinButton *>(w);
2607 auto tb = dynamic_cast<Inkscape::UI::Widget::CheckButtonInternal *>(w);
2608
2609 {
2610 if (sb && sb->get_zeroable()) { // spinbutton
2611 auto a = sb->get_adjustment();
2612 a->set_value(0);
2613 }
2614 }
2615 {
2616 if (sb && sb->get_oneable()) { // spinbutton
2617 auto a = sb->get_adjustment();
2618 a->set_value(1);
2619 }
2620 }
2621 {
2622 if (tb && tb->get_uncheckable()) { // checkbox
2623 tb->set_active(false);
2624 }
2625 }
2626 }
2627
2628 for (auto const i : UI::get_children(*w)) {
2629 reset_recursive(i);
2630 }
2631}
2632
2634{
2635 reset_recursive(this);
2636}
2637
2638void CloneTiler::table_attach(Gtk::Grid *table, Gtk::Widget *widget, float align, int row, int col)
2639{
2640 widget->set_halign(Gtk::Align::FILL);
2641 widget->set_valign(Gtk::Align::CENTER);
2642 table->attach(*widget, col, row, 1, 1);
2643}
2644
2645Gtk::Grid * CloneTiler::table_x_y_rand(int values)
2646{
2647 auto const table = Gtk::make_managed<Gtk::Grid>();
2648 table->set_row_spacing(6);
2649 table->set_column_spacing(8);
2650 table->set_halign(Gtk::Align::START);
2651 table->set_margin(VB_MARGIN);
2652
2653 {
2654 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
2655
2656 auto i = sp_get_icon_image("object-rows", Gtk::IconSize::NORMAL); // Previously GTK_ICON_SIZE_MENU
2657 UI::pack_start(*hb, *i, false, false, 2);
2658
2659 auto const l = Gtk::make_managed<Gtk::Label>("");
2660 l->set_markup(_("<small>Per row:</small>"));
2661 UI::pack_start(*hb, *l, false, false, 2);
2662
2663 table_attach(table, hb, 0, 1, 2);
2664 }
2665
2666 {
2667 auto const hb = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 0);
2668
2669 auto i = sp_get_icon_image("object-columns", Gtk::IconSize::NORMAL); // Previously GTK_ICON_SIZE_MENU
2670 UI::pack_start(*hb, *i, false, false, 2);
2671
2672 auto const l = Gtk::make_managed<Gtk::Label>("");
2673 l->set_markup(_("<small>Per column:</small>"));
2674 UI::pack_start(*hb, *l, false, false, 2);
2675
2676 table_attach(table, hb, 0, 1, 3);
2677 }
2678
2679 {
2680 auto const l = Gtk::make_managed<Gtk::Label>("");
2681 l->set_markup(_("<small>Randomize:</small>"));
2682 table_attach(table, l, 0, 1, 4);
2683 }
2684
2685 return table;
2686}
2687
2689{
2690 auto prefs = Inkscape::Preferences::get();
2691 prefs->setInt(prefs_path + "pick", v);
2692}
2693
2695{
2696 for (auto w : _rowscols) {
2697 w->set_sensitive(true);
2698 }
2699 for (auto w : _widthheight) {
2700 w->set_sensitive(false);
2701 }
2702
2703 auto prefs = Inkscape::Preferences::get();
2704 prefs->setBool(prefs_path + "fillrect", false);
2705}
2706
2707
2709{
2710 for (auto w : _rowscols) {
2711 w->set_sensitive(false);
2712 }
2713 for (auto w : _widthheight) {
2714 w->set_sensitive(true);
2715 }
2716
2717 auto prefs = Inkscape::Preferences::get();
2718 prefs->setBool(prefs_path + "fillrect", true);
2719}
2720
2722{
2723 auto const raw_dist = fill_width->get_value();
2724 auto const unit = unit_menu->getUnit();
2725 auto const pixels = Inkscape::Util::Quantity::convert(raw_dist, unit, "px");
2726
2727 auto prefs = Inkscape::Preferences::get();
2728 prefs->setDouble(prefs_path + "fillwidth", pixels);
2729}
2730
2732{
2733 auto const raw_dist = fill_height->get_value();
2734 auto const unit = unit_menu->getUnit();
2735 auto const pixels = Inkscape::Util::Quantity::convert(raw_dist, unit, "px");
2736
2737 auto prefs = Inkscape::Preferences::get();
2738 prefs->setDouble(prefs_path + "fillheight", pixels);
2739}
2740
2742{
2744 gdouble width_pixels = prefs->getDouble(prefs_path + "fillwidth");
2745 gdouble height_pixels = prefs->getDouble(prefs_path + "fillheight");
2746
2747 Inkscape::Util::Unit const *unit = unit_menu->getUnit();
2748
2749 gdouble width_value = Inkscape::Util::Quantity::convert(width_pixels, "px", unit);
2750 gdouble height_value = Inkscape::Util::Quantity::convert(height_pixels, "px", unit);
2751 fill_width->set_value(width_value);
2752 fill_height->set_value(height_value);
2753}
2754
2756{
2757 auto prefs = Inkscape::Preferences::get();
2758 auto active = _b->get_active();
2759 prefs->setBool(prefs_path + "dotrace", active);
2760
2761 if (_dotrace) {
2762 _dotrace->set_sensitive(active);
2763 }
2764}
2765
2767{
2768 nb->set_current_page(6);
2769 _b->set_active(false);
2770}
2771
2772
2773} // namespace Dialog
2774
2775} // namespace Inkscape::UI
2776
2777/*
2778 Local Variables:
2779 mode:c++
2780 c-file-style:"stroustrup"
2781 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2782 indent-tabs-mode:nil
2783 fill-column:99
2784 End:
2785*/
2786// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
Colors::Color ink_cairo_surface_average_color(cairo_surface_t *surface, cairo_surface_t *mask)
Get the average color from the given surface.
Cairo integration helpers.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Axis aligned, non-empty, generic rectangle.
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
void normalize()
Normalize the vector representing the point.
Definition point.cpp:96
Axis aligned, non-empty rectangle.
Definition rect.h:92
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Scale inverse() const
Definition transforms.h:172
Translation by a vector.
Definition transforms.h:115
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Minimal wrapper over Cairo.
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
bool isEmpty()
Returns true if no items are selected.
SPItem * singleItem()
Returns a single selected item.
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
static Preferences * get()
Access the singleton Preferences object.
Colors::Color getColor(Glib::ustring const &pref_path, std::string const &def="black")
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
void setColor(Glib::ustring const &pref_path, Colors::Color const &color)
Set an RGBA color value.
double getDoubleLimited(Glib::ustring const &pref_path, double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="")
Retrieve a limited floating point value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
sigc::connection selectChangedConn
Definition clonetiler.h:175
double randomize01(double val, double rand)
Randomizes val by rand, with 0 < val < 1 and all values (including 0, 1) having the same probability ...
Glib::RefPtr< Gtk::SizeGroup > table_row_labels
Definition clonetiler.h:169
Geom::Rect transform_rect(Geom::Rect const &r, Geom::Affine const &m)
bool is_a_clone_of(SPObject *tile, SPObject *obj)
guint number_of_clones(SPObject *obj)
Gtk::Widget * spinbox(const char *tip, const Glib::ustring &attr, double lower, double upper, const gchar *suffix, bool exponent=false)
void pick_to(Gtk::CheckButton *tb, Glib::ustring const &pref)
void value_changed(Glib::RefPtr< Gtk::Adjustment > const &adj, Glib::ustring const &pref)
sigc::connection externChangedConn
Definition clonetiler.h:176
Gtk::Widget * checkbox(const char *tip, const Glib::ustring &attr)
Gtk::Box * new_tab(Gtk::Notebook *nb, const gchar *label)
void on_picker_color_changed(Colors::Color const &rgba)
void change_selection(Inkscape::Selection *selection)
void checkbox_toggled(Gtk::CheckButton *tb, Glib::ustring const &attr)
void reset_recursive(Gtk::Widget *w)
Gtk::Grid * table_x_y_rand(int values)
void remove(bool do_undo=true)
void trace_hide_tiled_clones_recursively(SPObject *from)
UI::Widget::CheckButtonInternal * _b
Definition clonetiler.h:165
void symgroup_changed(Gtk::ComboBox *cb)
Inkscape::UI::Widget::UnitMenu * unit_menu
Definition clonetiler.h:170
Inkscape::UI::Widget::ColorPicker * color_picker
Definition clonetiler.h:168
sigc::connection color_changed_connection
Definition clonetiler.h:177
void xy_changed(Glib::RefPtr< Gtk::Adjustment > const &adj, Glib::ustring const &pref)
Geom::Affine get_transform(int type, int i, int j, double cx, double cy, double w, double h, double shiftx_per_i, double shifty_per_i, double shiftx_per_j, double shifty_per_j, double shiftx_rand, double shifty_rand, double shiftx_exp, double shifty_exp, int shiftx_alternate, int shifty_alternate, int shiftx_cumulate, int shifty_cumulate, int shiftx_excludew, int shifty_excludeh, double scalex_per_i, double scaley_per_i, double scalex_per_j, double scaley_per_j, double scalex_rand, double scaley_rand, double scalex_exp, double scaley_exp, double scalex_log, double scaley_log, int scalex_alternate, int scaley_alternate, int scalex_cumulate, int scaley_cumulate, double rotate_per_i, double rotate_per_j, double rotate_rand, int rotate_alternatei, int rotate_alternatej, int rotate_cumulatei, int rotate_cumulatej)
std::vector< Gtk::Widget * > _widthheight
Definition clonetiler.h:184
guint32 trace_pick(Geom::Rect box)
void trace_setup(SPDocument *doc, gdouble zoom, SPItem *original)
Glib::RefPtr< Gtk::Adjustment > fill_height
Definition clonetiler.h:173
Glib::RefPtr< Gtk::Adjustment > fill_width
Definition clonetiler.h:172
UI::Widget::CheckButtonInternal * _cb_keep_bbox
Definition clonetiler.h:166
void table_attach(Gtk::Grid *table, Gtk::Widget *widget, float align, int row, int col)
std::vector< Gtk::Widget * > _rowscols
Definition clonetiler.h:183
DialogBase is the base class for the dialog system.
Definition dialog-base.h:42
Selection * getSelection() const
Definition dialog-base.h:84
SPDocument * getDocument() const
Definition dialog-base.h:83
SPDesktop * getDesktop() const
Definition dialog-base.h:79
sigc::connection connectChanged(sigc::slot< void(Colors::Color const &)> slot)
SpinButton widget, that allows entry of simple math expressions (also units, when linked with UnitMen...
Definition spinbutton.h:52
Unit const * getUnit() const
Returns the Unit object corresponding to the current selection in the dropdown widget.
Definition unit-menu.cpp:64
Glib::SignalProxyProperty signal_changed()
bool setUnit(Glib::ustring const &unit)
Sets the dropdown widget to the given unit abbreviation.
Definition unit-menu.cpp:75
bool setUnitType(UnitType unit_type, bool svg_length=false)
Adds the unit type to the widget.
Definition unit-menu.cpp:33
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
Interface for refcounted XML nodes.
Definition node.h:80
double getAttributeDouble(Util::const_char_ptr key, double default_value=0.0) const
Definition node.cpp:76
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual Document * document()=0
Get the node's associated document.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
void clearWaitingCursor()
Definition desktop.cpp:1166
Geom::Affine const & doc2dt() const
Definition desktop.cpp:1337
void setWaitingCursor()
Definition desktop.cpp:1155
Typed SVG document implementation.
Definition document.h:103
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
Definition document.cpp:773
void update_filter_region(SPItem *item)
Update the filter region based on the object's bounding box.
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::OptRect documentBounds(BBoxType type) const
Definition sp-item.cpp:1026
Inkscape::DrawingItem * invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
Definition sp-item.cpp:1269
Geom::Point getCenter(bool ensure_uptodate=true) const
Definition sp-item.cpp:377
void invoke_hide(unsigned int key)
Definition sp-item.cpp:1320
@ VISUAL_BBOX
Definition sp-item.h:118
@ GEOMETRIC_BBOX
Definition sp-item.h:116
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
Definition sp-item.cpp:1246
void setCenter(Geom::Point const &object_centre)
Sets the transform_center_x and transform_center_y properties to retain the rotation center.
Definition sp-item.cpp:331
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPDocument * document
Definition sp-object.h:188
SPObject * parent
Definition sp-object.h:189
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.
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
Geom::Affine c2p
Definition viewbox.h:43
Clone tiling dialog.
const double w
Definition conic-4.cpp:19
Css & result
Editable view implementation.
static char const *const current
Definition dir-util.cpp:71
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
Cairo drawing context with Inkscape extensions.
unsigned int guint32
struct _cairo_surface cairo_surface_t
SVG drawing for display.
SPFilter * new_filter_gaussian_blur(SPDocument *document, gdouble radius, double expansion)
Creates a filter with blur primitive of specified radius for an item with the given matrix expansion,...
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:130
Gtk::Image * sp_get_icon_image(Glib::ustring const &icon_name, int size)
Icon Loader.
Macro for icon names used in Inkscape.
SPItem * item
std::string original
Glib::ustring label
Raw stack of active status messages.
Affine identity()
Create an identity matrix.
Definition affine.h:210
Definition desktop.h:50
static R & release(R &r)
Decrements the reference count of a anchored object.
static constexpr int VB_MARGIN
static unsigned trace_visionkey
void align(Gtk::Widget *top, int const spinbutton_width_chars)
static Glib::ustring const prefs_path
static std::unique_ptr< Inkscape::Drawing > trace_drawing
static constexpr int SB_MARGIN
static gdouble trace_zoom
static SPDocument * trace_doc
static constexpr int height
User interface code.
Definition desktop.h:113
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.
Definition pack.cpp:153
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().
Definition util.cpp:155
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.
Definition pack.cpp:141
@ UNIT_TYPE_LINEAR
Definition units.h:34
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
@ ERROR_MESSAGE
Definition message.h:29
@ WARNING_MESSAGE
Definition message.h:28
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Ocnode * child[8]
Definition quantize.cpp:33
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
SPRoot: SVG <svg> implementation.
virtual Node * createElement(char const *name)=0
void sp_style_set_property_url(SPObject *item, gchar const *property, SPObject *linked, bool recursive)
Definition style.cpp:1380
std::string sp_svg_transform_write(Geom::Affine const &transform)
double width
Affine transformation classes.
Unclumping objects.