28#include <gdk/gdkkeysyms.h>
29#include <glibmm/i18n.h>
57#define DDC_RED_RGBA 0xff0000ff
58#define DYNA_MIN_WIDTH 1.0e-6
63#define ENABLE_SPRAY_MODE_SINGLE_PATH
86 return mu + sigma * sqrt( -2.0 *
log(g_random_double_range(0, 1)) ) * cos( 2.0*M_PI*g_random_double_range(0, 1) );
95 return translate.inverse() * affine * translate;
108 auto clone = cast<SPUse>(
item);
109 if (
auto grp = cast<SPGroup>(
item)) {
110 for (
auto ig : grp->item_list()) {
113 }
else if (
auto shape = cast<SPShape>(
item)) {
115 for (
auto path : shape->curve()->get_pathvector()) {
119 }
else if (clone && !
root) {
120 get_paths(clone->trueOriginal(), res,
false);
124 get_paths(clone->trueOriginal(), res,
false);
125 res *= clone->trueOriginal()->transform.inverse();
126 res *= clone->get_root_transform();
138 , pressure(TC_DEFAULT_PRESSURE)
151 prefs->
setBool(
"/dialogs/clonetiler/dotrace",
false);
152 if (prefs->
getBool(
"/tools/spray/selcue")) {
155 if (prefs->
getBool(
"/tools/spray/gradientdrag")) {
185 object_set.
add(selected_objects.begin(), selected_objects.end());
194 gchar *sel_message =
nullptr;
198 sel_message = g_strdup_printf(ngettext(
"<b>%i</b> object selected",
"<b>%i</b> objects selected",
num),
num);
200 sel_message = g_strdup_printf(
"%s", _(
"<b>Nothing</b> selected"));
203 switch (this->
mode) {
205 this->
message_context->setF(
Inkscape::NORMAL_MESSAGE, _(
"%s. Drag, click or click and scroll to spray <b>copies</b> of the initial selection. Right-click + move to update single click item."), sel_message);
208 this->
message_context->setF(
Inkscape::NORMAL_MESSAGE, _(
"%s. Drag, click or click and scroll to spray <b>clones</b> of the initial selection. Right-click + move to update single click item."), sel_message);
211 this->
message_context->setF(
Inkscape::NORMAL_MESSAGE, _(
"%s. Drag, click or click and scroll to spray into a <b>single path</b>. Right-click + move to update single click item."), sel_message);
223 pick = prefs->
getInt(
"/dialogs/clonetiler/pick");
236 if (path ==
"mode") {
239 }
else if (path ==
"width") {
240 this->
width = 0.01 * CLAMP(val.
getInt(10), 1, 100);
241 }
else if (path ==
"usepressurewidth") {
243 }
else if (path ==
"usepressurepopulation") {
245 }
else if (path ==
"usepressurescale") {
247 }
else if (path ==
"population") {
249 }
else if (path ==
"rotation_variation") {
251 }
else if (path ==
"scale_variation") {
253 }
else if (path ==
"standard_deviation") {
255 }
else if (path ==
"mean") {
256 this->
mean = 0.01 * CLAMP(val.
getInt(10), 1, 100);
258 }
else if (path ==
"distribution") {
260 }
else if (path ==
"tilt") {
262 }
else if (path ==
"ratio") {
264 }
else if (path ==
"offset") {
266 }
else if (path ==
"pick_center") {
268 }
else if (path ==
"pick_inverse_value") {
270 }
else if (path ==
"pick_fill") {
272 }
else if (path ==
"pick_stroke") {
274 }
else if (path ==
"pick_no_overlap") {
276 }
else if (path ==
"over_no_transparent") {
278 }
else if (path ==
"over_transparent") {
280 }
else if (path ==
"no_overlap") {
282 }
else if (path ==
"picker") {
299 return pressure * tc->
width;
325 double pressure = tc->
pressure / TC_DEFAULT_PRESSURE;
351 angle = g_random_double_range(0, M_PI*2.0);
354 double radius_temp =-1;
355 while(!((radius_temp >= 0) && (radius_temp <=1 )))
363 radius = pow(radius_temp, 0.5);
387 double base =
MIN (val - rand, 1 - 2*rand);
391 val = base + g_random_double_range (0,
MIN (2 * rand, 1 - base));
392 return CLAMP(val, 0, 1);
404 if (avg.getOpacity() < 1e-6) {
414 for (
auto item_hidden : items_down) {
415 item_hidden->setHidden(
false);
416 item_hidden->updateRepr();
432 bool pick_inverse_value,
435 bool pick_no_overlap,
436 bool over_no_transparent,
437 bool over_transparent,
446 bool pick_to_presence,
448 bool pick_to_opacity,
450 double gamma_picked ,
453 if (
set->isEmpty()) {
457 double width = bbox->width();
458 double height = bbox->height();
460 if(offset_width < 0 ){
464 if(offset_height < 0 ){
467 if(picker && pick_to_size && !trace_scale &&
do_trace){
483 double bbox_left_main = bbox_procesed->left();
484 double bbox_right_main = bbox_procesed->right();
485 double bbox_top_main = bbox_procesed->top();
486 double bbox_bottom_main = bbox_procesed->bottom();
487 double width_transformed = bbox_procesed->width();
488 double height_transformed = bbox_procesed->height();
497 if(pick_no_overlap) {
518 offset_width = ((99.0 -
offset) * width_transformed)/100.0 - width_transformed;
519 offset_height = ((99.0 -
offset) * height_transformed)/100.0 - height_transformed;
525 std::vector<SPItem*> items_down_erased;
526 for (std::vector<SPItem*>::const_iterator i=items_down.begin(); i!=items_down.end(); ++i) {
529 double bbox_left = bbox_down->left();
530 double bbox_top = bbox_down->top();
531 gchar
const * item_down_sharp = g_strdup_printf(
"#%s", item_down->
getId());
532 items_down_erased.push_back(item_down);
533 for (
auto item_selected :
set->items()) {
534 gchar
const * spray_origin;
535 if(!item_selected->getAttribute(
"inkscape:spray-origin")){
536 spray_origin = g_strdup_printf(
"#%s", item_selected->getId());
538 spray_origin = item_selected->getAttribute(
"inkscape:spray-origin");
540 if(strcmp(item_down_sharp, spray_origin) == 0 ||
542 strcmp(item_down->
getAttribute(
"inkscape:spray-origin"),spray_origin) == 0 ))
545 if(strcmp(item_down_sharp, spray_origin) != 0 && !
set->includes(item_down) ){
547 items_down_erased.pop_back();
550 }
else if(no_overlap) {
551 if(!(offset_width < 0 && offset_height < 0 && std::abs(bbox_left - bbox_left_main) > std::abs(offset_width) &&
552 std::abs(bbox_top - bbox_top_main) > std::abs(offset_height))){
553 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
558 }
else if(picker || over_transparent || over_no_transparent) {
566 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
571 if(picker || over_transparent || over_no_transparent){
581 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
590 double opacity = 1.0;
591 gchar color_string[32]; *color_string = 0;
593 bool invisible = color.getOpacity() < 1e-6;
595 if(!over_transparent && invisible){
596 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
601 if(!over_no_transparent && !invisible){
602 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
617 val = color.getOpacity();
641 if (rand_picked > 0) {
643 for (
auto i = 0; i < 3; i++) {
648 if (gamma_picked != 0) {
650 if (gamma_picked > 0)
651 power = 1/(1 + fabs(gamma_picked));
653 power = 1 + fabs(gamma_picked);
655 val = pow (val, power);
656 for (
auto i = 0; i < 3; i++) {
657 color.set(i, pow(color[i], (
double)power));
666 val = CLAMP (val, 0, 1);
671 if(pick_inverse_value) {
677 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
698 , over_no_transparent
716 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
724 if (pick_to_opacity) {
725 if(pick_inverse_value) {
726 opacity *= 1.0 - val;
730 std::stringstream opacity_str;
731 opacity_str.imbue(std::locale::classic());
732 opacity_str << opacity;
735 if (pick_to_presence) {
736 if (g_random_double_range (0, 1) > val) {
745 if (opacity < 1e-6) {
746 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
757 if (pick_inverse_value) {
762 if(!no_overlap && (picker || over_transparent || over_no_transparent)){
772 SPItem *&single_path_output,
779 double scale_variation,
782 double standard_deviation,
785 double rotation_variation,
790 bool pick_inverse_value,
793 bool pick_no_overlap,
794 bool over_no_transparent,
795 bool over_transparent,
797 bool usepressurescale,
805 bool pick_to_presence,
807 bool pick_to_opacity,
809 double gamma_picked ,
817 if (
auto box = cast<SPBox3D>(
item)) {
819 item = box->convert_to_group();
824 double _fid = single_click ? 0 : g_random_double_range(0, 1);
825 double angle = single_click ? single_angle : g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
826 double _scale = single_click ? single_scale : g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
827 if(!single_click && usepressurescale){
830 double dr;
double dp;
836 if(_fid <= population || no_overlap)
839 gchar
const * spray_origin;
841 spray_origin = g_strdup_printf(
"#%s",
item->
getId());
846 Geom::Point move = (
Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio), -sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-bbox->midpoint());
848 move = p-bbox->midpoint();
854 pick_no_overlap || no_overlap || picker ||
855 !over_transparent || !over_no_transparent)
857 for (
auto i : {0,1}) {
874 , over_no_transparent
890 if (no_overlap && i == 0) {
915 if(!clone->attribute(
"inkscape:spray-origin")){
916 clone->setAttribute(
"inkscape:spray-origin", spray_origin);
918 gchar *href_str = g_strdup_printf(
"#%s", old_repr->
attribute(
"id"));
919 clone->setAttribute(
"xlink:href", href_str);
923 item_copied = cast<SPItem>(clone_object);
926 if(!copy->attribute(
"inkscape:spray-origin")){
927 copy->setAttribute(
"inkscape:spray-origin", spray_origin);
929 parent->appendChild(copy);
931 item_copied = cast<SPItem>(new_obj);
938 center = bbox->midpoint();
955#ifdef ENABLE_SPRAY_MODE_SINGLE_PATH
964 if (_fid <= population) {
967 gchar
const * spray_origin;
968 if(!copy->attribute(
"inkscape:spray-origin")){
969 spray_origin = g_strdup_printf(
"#%s", old_repr->
attribute(
"id"));
971 spray_origin = copy->attribute(
"inkscape:spray-origin");
973 parent->appendChild(copy);
975 auto item_copied = cast<SPItem>(new_obj);
978 Geom::Point move = (
Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio), -sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-bbox->midpoint());
988 object_set_tmp.add(item_copied);
989 object_set_tmp.removeLPESRecursive(
true);
990 if (is<SPUse>(object_set_tmp.objects().front())) {
991 object_set_tmp.unlinkRecursive(
true);
993 if (single_path_output) {
994 object_set_tmp.add(single_path_output);
996 object_set_tmp.pathUnion(
true);
997 single_path_output = object_set_tmp.items().front();
998 for (
auto item : object_set_tmp.items()) {
1000 repr->
setAttribute(
"inkscape:spray-origin", spray_origin);
1016 if (
set->isEmpty()) {
1023 if (radius == 0 || (population == 0 && !force)) {
1027 if (radius == 0 || path_mean == 0) {
1031 if (radius == 0 || path_standard_deviation == 0) {
1039 g_assert(
item !=
nullptr);
1044 g_assert(
item !=
nullptr);
1057 , move_standard_deviation
1091 g_assert(
item !=
nullptr);
1129 std::cerr <<
"Could not access Spray toolbar" << std::endl;
1151 if (event.num_press == 1 && event.button == 1) {
1153 xyp =
event.pos.floor();
1173 if (event.num_press == 1 && event.button == 3) {
1190 items.push_back(randintem);
1260 if (event.
modifiers == GDK_BUTTON1_MASK) {
1332#ifdef ENABLE_SPRAY_MODE_SINGLE_PATH
1353 case GDK_KEY_KP_Down:
1364 case GDK_KEY_KP_Right:
1377 case GDK_KEY_KP_Left:
1389 case GDK_KEY_KP_Home:
1396 case GDK_KEY_KP_End:
1409 case GDK_KEY_Shift_L:
1410 case GDK_KEY_Shift_R:
1413 case GDK_KEY_Control_L:
1414 case GDK_KEY_Control_R:
1416 case GDK_KEY_Delete:
1417 case GDK_KEY_KP_Delete:
1418 case GDK_KEY_BackSpace:
1429 case GDK_KEY_Shift_L:
1430 case GDK_KEY_Shift_R:
1433 case GDK_KEY_Control_L:
1434 case GDK_KEY_Control_R:
3x3 matrix representing an affine transformation.
Affine inverse() const
Compute the inverse matrix.
Affine withoutTranslation() const
Set of all points at a fixed distance from the center.
Axis aligned, non-empty, generic rectangle.
static CRect from_xywh(C x, C y, C w, C h)
Create rectangle from origin and dimensions.
Axis-aligned rectangle that can be empty.
OptRect boundsFast() const
void clear()
Remove all paths from the vector.
iterator insert(iterator pos, Path const &p)
Sequence of contiguous curves, aka spline.
void close(bool closed=true)
Set whether the path is closed.
OptRect boundsFast() const
Get the approximate bounding box.
void appendNew(Args &&... args)
Append a new curve to the path.
void start(Point const &p)
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
IntRect roundOutwards() const
Return the smallest integer rectangle which contains this one.
bool hasZeroArea(Coord eps=EPSILON) const
Check whether the rectangle has zero area up to specified tolerance.
Rotation around the origin.
Translate inverse() const
Get the inverse translation.
Inkscape::Drawing * get_drawing()
bool set(unsigned int index, double value)
Set a specific channel in the color.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Colors::Color averageColor(Geom::IntRect const &area) const
bool add(SPObject *object, bool nosignal=false)
Add an SPObject to the set of selected objects.
bool isEmpty()
Returns true if no items are selected.
int size()
Returns size of the selection.
SPObjectRange objects()
Returns the list of selected objects.
std::vector< SPItem * > items_vector()
Data type representing a typeless value of a preference.
double getDouble(double def=0.0, Glib::ustring const &unit="") const
Interpret the preference as a floating point value.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
int getInt(int def=0) const
Interpret the preference as an integer.
double getDoubleLimited(double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="") const
Interpret the preference as a limited floating point value.
Preference storage class.
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean 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.
Interface for refcounted XML nodes.
virtual Node * parent()=0
Get the parent of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
To do: update description of desktop.
double current_zoom() const
Inkscape::CanvasItemGroup * getCanvasControls() const
SPDocument * getDocument() const
void setToolboxFocusTo(char const *label)
Geom::Affine const & dt2doc() const
Geom::Affine const & d2w() const
Transformation from desktop to window coordinates.
Geom::Point point() const
Returns the mouse point in desktop coordinates; if mouse is outside the canvas, returns the center of...
Gtk::Widget * get_toolbar_by_name(Glib::ustring const &name)
Inkscape::CanvasItemDrawing * getCanvasDrawing() const
Inkscape::Selection * getSelection() const
void setToolboxAdjustmentValue(char const *id, double val)
Geom::Affine const & doc2dt() const
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Typed SVG document implementation.
const Geom::Affine & dt2doc() const
Desktop to document coordinate transformation.
std::vector< SPItem * > getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden=false, bool take_insensitive=false, bool take_groups=true, bool enter_groups=false, bool enter_layers=true) const
Get items whose bounding box overlaps with given area.
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
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
Base class for visual SVG elements.
void set_i2d_affine(Geom::Affine const &transform)
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Geom::OptRect documentVisualBounds() const
Get item's visual bbox in document coordinate system.
Geom::Point getCenter(bool ensure_uptodate=true) const
void setHidden(bool hidden)
bool updateCenterIfSet(Geom::Point const ¢er)
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv=nullptr, bool compensate=true)
Set a new transform on an object.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
char const * getId() const
Returns the objects current ID string.
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
constexpr double SP_RGBA32_A_F(uint32_t v)
std::shared_ptr< Css const > css
void sp_desktop_apply_css_recursive(SPObject *o, SPCSSAttr *css, bool skip_lines)
Apply style on object and children, recursively.
Editable view implementation.
static char const *const parent
TODO: insert short description here.
auto floor(Geom::Rect const &rect)
Macro for icon names used in Inkscape.
Interface for locally managing a current status message.
Coord LInfty(Point const &p)
std::string rgba_to_hex(uint32_t value, bool alpha)
Output the RGBA value as a #RRGGBB hex color, if alpha is true then the output will be #RRGGBBAA inst...
static R & release(R &r)
Decrements the reference count of a anchored object.
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
bool mod_ctrl_only(unsigned modifiers)
bool mod_shift_only(unsigned modifiers)
bool mod_shift(unsigned modifiers)
bool mod_alt_only(unsigned modifiers)
bool have_viable_layer(SPDesktop *desktop, MessageContext *message)
Check to see if the current layer is both unhidden and unlocked.
Piecewise< SBasis > log(Interval in)
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
void sp_repr_css_set_property_string(SPCSSAttr *css, char const *name, std::string const &value)
Set a style property to a standard string.
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Geom::Affine i2anc_affine(SPObject const *object, SPObject const *ancestor)
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
The pointer has entered a widget or item.
The pointer has exited a widget or item.
Movement of the mouse pointer.
Interface for XML documents.
virtual Node * createElement(char const *name)=0