30
41
#include <2geom/sbasis-to-bezier.h>
31
42
#include <2geom/matrix.h>
43
#include <2geom/pathvector.h>
34
45
// include effects:
35
#include "live_effects/lpe-skeletalstrokes.h"
36
#include "live_effects/lpe-pathalongpath.h"
37
#include "live_effects/lpe-slant.h"
46
#include "live_effects/lpe-patternalongpath.h"
47
#include "live_effects/lpe-bendpath.h"
48
#include "live_effects/lpe-sketch.h"
49
#include "live_effects/lpe-vonkoch.h"
50
#include "live_effects/lpe-knot.h"
51
#include "live_effects/lpe-rough-hatches.h"
52
#include "live_effects/lpe-dynastroke.h"
38
53
#include "live_effects/lpe-test-doEffect-stack.h"
39
54
#include "live_effects/lpe-gears.h"
40
55
#include "live_effects/lpe-curvestitch.h"
56
#include "live_effects/lpe-circle_with_radius.h"
57
#include "live_effects/lpe-perspective_path.h"
58
#include "live_effects/lpe-spiro.h"
59
#include "live_effects/lpe-lattice.h"
60
#include "live_effects/lpe-envelope.h"
61
#include "live_effects/lpe-constructgrid.h"
62
#include "live_effects/lpe-perp_bisector.h"
63
#include "live_effects/lpe-tangent_to_curve.h"
64
#include "live_effects/lpe-mirror_symmetry.h"
65
#include "live_effects/lpe-circle_3pts.h"
66
#include "live_effects/lpe-angle_bisector.h"
67
#include "live_effects/lpe-parallel.h"
68
#include "live_effects/lpe-copy_rotate.h"
69
#include "live_effects/lpe-offset.h"
70
#include "live_effects/lpe-ruler.h"
71
#include "live_effects/lpe-boolops.h"
72
#include "live_effects/lpe-interpolate.h"
73
#include "live_effects/lpe-text_label.h"
74
#include "live_effects/lpe-path_length.h"
75
#include "live_effects/lpe-line_segment.h"
76
#include "live_effects/lpe-recursiveskeleton.h"
44
79
namespace Inkscape {
46
81
namespace LivePathEffect {
48
const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
49
// {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
50
{PATH_ALONG_PATH, N_("Bend Path"), "bend_path"},
51
{SKELETAL_STROKES, N_("Pattern Along Path"), "skeletal"},
83
const Util::EnumData<EffectType> LPETypeData[] = {
84
// {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
52
85
#ifdef LPE_ENABLE_TEST_EFFECTS
53
{SLANT, N_("Slant"), "slant"},
54
{DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
86
{DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
87
{ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"},
88
// TRANSLATORS: boolean operations
89
{BOOLOPS, N_("Boolops"), "boolops"},
90
{CIRCLE_WITH_RADIUS, N_("Circle (by center and radius)"), "circle_with_radius"},
91
{CIRCLE_3PTS, N_("Circle by 3 points"), "circle_3pts"},
92
{DYNASTROKE, N_("Dynamic stroke"), "dynastroke"},
93
{LATTICE, N_("Lattice Deformation"), "lattice"},
94
{LINE_SEGMENT, N_("Line Segment"), "line_segment"},
95
{MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"},
96
{OFFSET, N_("Offset"), "offset"},
97
{PARALLEL, N_("Parallel"), "parallel"},
98
{PATH_LENGTH, N_("Path length"), "path_length"},
99
{PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"},
100
{PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"},
101
{COPY_ROTATE, N_("Rotate copies"), "copy_rotate"},
102
{RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"},
103
{TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
104
{TEXT_LABEL, N_("Text label"), "text_label"},
56
{GEARS, N_("Gears"), "gears"},
57
{CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
107
{BEND_PATH, N_("Bend"), "bend_path"},
108
{GEARS, N_("Gears"), "gears"},
109
{PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
110
{CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
112
{VONKOCH, N_("VonKoch"), "vonkoch"},
113
{KNOT, N_("Knot"), "knot"},
114
{CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
115
{SPIRO, N_("Spiro spline"), "spiro"},
116
{ENVELOPE, N_("Envelope Deformation"), "envelope"},
117
{INTERPOLATE, N_("Interpolate Sub-Paths"), "interpolate"},
118
{ROUGH_HATCHES, N_("Hatches (rough)"), "rough_hatches"},
119
{SKETCH, N_("Sketch"), "sketch"},
120
{RULER, N_("Ruler"), "ruler"},
59
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
122
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
125
Effect::acceptsNumClicks(EffectType type) {
127
case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
128
case ANGLE_BISECTOR: return 3;
129
case CIRCLE_3PTS: return 3;
130
case CIRCLE_WITH_RADIUS: return 2;
131
case LINE_SEGMENT: return 2;
132
case PERP_BISECTOR: return 2;
62
138
Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
64
140
Effect* neweffect = NULL;
66
case SKELETAL_STROKES:
67
neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
70
neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
72
#ifdef LPE_ENABLE_TEST_EFFECTS
74
neweffect = (Effect*) new LPESlant(lpeobj);
76
case DOEFFECTSTACK_TEST:
77
neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
142
case PATTERN_ALONG_PATH:
143
neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
146
neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
149
neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
152
neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
155
neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
158
neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
81
neweffect = (Effect*) new LPEGears(lpeobj);
161
neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
83
163
case CURVE_STITCH:
84
neweffect = (Effect*) new LPECurveStitch(lpeobj);
164
neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
167
neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
170
neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
172
case CIRCLE_WITH_RADIUS:
173
neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
175
case PERSPECTIVE_PATH:
176
neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
179
neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
182
neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
185
neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
187
case TANGENT_TO_CURVE:
188
neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
190
case MIRROR_SYMMETRY:
191
neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
194
neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
197
neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
200
neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
203
neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
206
neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
209
neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
212
neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
215
neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
218
neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
221
neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
224
neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
226
case DOEFFECTSTACK_TEST:
227
neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
230
neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
232
case RECURSIVE_SKELETON:
233
neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
87
236
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
110
288
Effect::getName()
112
if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
290
if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
113
291
return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
115
293
return Glib::ustring( _("No effect") );
297
Effect::effectType() {
298
return lpeobj->effecttype;
302
* Is performed a single time when the effect is freshly applied to a path
305
Effect::doOnApply (SPLPEItem */*lpeitem*/)
310
* Is performed each time before the effect is updated.
313
Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
315
//Do nothing for simple effects
319
* Effects can have a parameter path set before they are applied by accepting a nonzero number of
320
* mouse clicks. This method activates the pen context, which waits for the specified number of
321
* clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
324
Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
326
// switch to pen context
327
SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
328
if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
329
tools_switch(desktop, TOOLS_FREEHAND_PEN);
332
SPEventContext *ec = desktop->event_context;
333
SPPenContext *pc = SP_PEN_CONTEXT(ec);
334
pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
335
pc->waiting_LPE = this;
336
pc->waiting_item = lpeitem;
337
pc->polylines_only = true;
339
ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
340
g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
341
getName().c_str(), acceptsNumClicks()));
345
Effect::writeParamsToSVG() {
346
std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
347
for (p = param_vector.begin(); p != param_vector.end(); ++p) {
348
(*p)->write_to_SVG();
353
* If the effect expects a path parameter (specified by a number of mouse clicks) before it is
354
* applied, this is the method that processes the resulting path. Override it to customize it for
355
* your LPE. But don't forget to call the parent method so that is_ready is set to true!
358
Effect::acceptParamPath (SPPath */*param_path*/) {
119
363
* Here be the doEffect function chain:
122
366
Effect::doEffect (SPCurve * curve)
124
NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
126
if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) { // FIXME, add function to SPCurve to change bpath? or a copy function?
128
g_free(curve->_bpath); //delete old bpath
130
curve->_bpath = new_bpath;
135
Effect::doEffect_nartbpath (NArtBpath * path_in)
138
std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
140
std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
142
NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
146
catch (std::exception & e) {
147
g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
148
SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
149
_("An exception occurred during execution of the Path Effect.") );
154
while ( path_in[ret].code != NR_END ) {
157
unsigned len = ++ret;
159
path_out = g_new(NArtBpath, len);
160
memcpy(path_out, path_in, len * sizeof(NArtBpath));
368
std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
370
std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
372
curve->set_pathvector(result_pathv);
165
375
std::vector<Geom::Path>
166
Effect::doEffect_path (std::vector<Geom::Path> & path_in)
376
Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
168
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
378
std::vector<Geom::Path> path_out;
170
for (unsigned int i=0; i < path_in.size(); i++) {
171
pwd2_in.concat( path_in[i].toPwSb() );
380
if ( !concatenate_before_pwd2 ) {
382
for (unsigned int i=0; i < path_in.size(); i++) {
383
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
384
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
385
std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
386
// add the output path vector to the already accumulated vector:
387
for (unsigned int j=0; j < path.size(); j++) {
388
path_out.push_back(path[j]);
392
// concatenate the path into possibly discontinuous pwd2
393
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
394
for (unsigned int i=0; i < path_in.size(); i++) {
395
pwd2_in.concat( path_in[i].toPwSb() );
397
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
398
path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
174
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
176
std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
181
404
Geom::Piecewise<Geom::D2<Geom::SBasis> >
182
Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
405
Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
184
407
g_warning("Effect has no doEffect implementation");
231
454
param_vector.push_back(param);
235
* This *creates* a new widget, management of deletion should be done by the caller
457
// TODO: should we provide a way to alter the handle's appearance?
459
Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
461
kh_entity_vector.push_back(std::make_pair(entity, descr));
465
* Add all registered LPE knotholder handles to the knotholder
468
Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
469
using namespace Inkscape::LivePathEffect;
471
// add handles provided by the effect itself
472
addKnotHolderEntities(knotholder, desktop, item);
474
// add handles provided by the effect's parameters (if any)
475
for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
476
(*p)->addKnotHolderEntities(knotholder, desktop, item);
481
Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
482
// TODO: The entities in kh_entity_vector are already instantiated during the call
483
// to registerKnotHolderHandle(), but they are recreated here. Also, we must not
484
// delete them when the knotholder is destroyed, whence the clumsy function
485
// isDeletable(). If we could create entities of different classes dynamically,
486
// this would be much nicer. How to do this?
487
std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
488
for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
489
KnotHolderEntity *entity = i->first;
490
const char *descr = i->second;
492
entity->create(desktop, item, knotholder, descr);
493
knotholder->add(entity);
498
* Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
499
* This is the function called by external code like SPLPEItem.
501
std::vector<Geom::PathVector>
502
Effect::getHelperPaths(SPLPEItem *lpeitem)
504
std::vector<Geom::PathVector> hp_vec;
506
if (!SP_IS_SHAPE(lpeitem)) {
507
g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
511
// TODO: we can probably optimize this by using a lot more references
512
// rather than copying PathVectors all over the place
513
if (show_orig_path) {
514
// add original path to helperpaths
515
SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
516
hp_vec.push_back(curve->get_pathvector());
519
// add other helperpaths provided by the effect itself
520
addCanvasIndicators(lpeitem, hp_vec);
522
// add helperpaths provided by the effect's parameters
523
for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
524
(*p)->addCanvasIndicators(lpeitem, hp_vec);
531
* Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
532
* This function should be overwritten by derived effects if they want to provide their own helperpaths.
535
Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
541
* This *creates* a new widget, management of deletion should be done by the caller
238
544
Effect::newWidget(Gtk::Tooltips * tooltips)