66
66
normal_offset(_("Normal offset"), "", "normal_offset", &wr, this, 0),
67
67
tang_offset(_("Tangential offset"), "", "tang_offset", &wr, this, 0),
68
68
prop_units(_("Offsets in unit of pattern size"), _("Spacing, tangential and normal offset are expressed as a ratio of width/height"), "prop_units", &wr, this, false),
69
vertical_pattern(_("Pattern is vertical"), _("Rotate pattern 90 deg before applying"), "vertical_pattern", &wr, this, false)
69
vertical_pattern(_("Pattern is vertical"), _("Rotate pattern 90 deg before applying"), "vertical_pattern", &wr, this, false),
70
fuse_tolerance(_("Fuse nearby ends"), "Fuse ends closer than this number. 0 means don't fuse.", "fuse_tolerance", &wr, this, 0)
71
72
registerParameter( dynamic_cast<Parameter *>(&pattern) );
72
73
registerParameter( dynamic_cast<Parameter *>(©type) );
95
97
/* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */
96
98
Piecewise<D2<SBasis> > output;
99
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > pre_output;
98
101
PAPCopyType type = copytype.get_value();
100
103
D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pattern.get_pwd2());
101
104
Piecewise<SBasis> x0 = vertical_pattern.get_value() ? Piecewise<SBasis>(patternd2[1]) : Piecewise<SBasis>(patternd2[0]);
102
105
Piecewise<SBasis> y0 = vertical_pattern.get_value() ? Piecewise<SBasis>(patternd2[0]) : Piecewise<SBasis>(patternd2[1]);
103
Interval pattBndsX = bounds_exact(x0);
104
x0 -= pattBndsX.min();
105
Interval pattBndsY = bounds_exact(y0);
106
y0 -= pattBndsY.middle();
108
double xspace = spacing;
109
double noffset = normal_offset;
110
double toffset = tang_offset;
111
if (prop_units.get_value()){
112
xspace *= pattBndsX.extent();
113
noffset *= pattBndsY.extent();
114
toffset *= pattBndsX.extent();
117
//Prevent more than 90% overlap...
118
if (xspace < -pattBndsX.extent()*.9) {
119
xspace = -pattBndsX.extent()*.9;
121
//TODO: dynamical update of parameter ranges?
122
//if (prop_units.get_value()){
123
// spacing.param_set_range(-.9, NR_HUGE);
125
// spacing.param_set_range(-pattBndsX.extent()*.9, NR_HUGE);
130
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in;
131
paths_in = split_at_discontinuities(pwd2_in);
133
for (unsigned idx = 0; idx < paths_in.size(); idx++){
134
Geom::Piecewise<Geom::D2<Geom::SBasis> > path_i = paths_in[idx];
135
Piecewise<SBasis> x = x0;
136
Piecewise<SBasis> y = y0;
137
Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1);
138
uskeleton = remove_short_cuts(uskeleton,.01);
139
Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));
140
n = force_continuity(remove_short_cuts(n,.1));
146
nbCopies = static_cast<int>(floor((uskeleton.domain().extent() - toffset + xspace)/(pattBndsX.extent()+xspace)));
147
pattBndsX = Interval(pattBndsX.min(),pattBndsX.max()+xspace);
151
nbCopies = (toffset + pattBndsX.extent() < uskeleton.domain().extent()) ? 1 : 0;
154
case PAPCT_SINGLE_STRETCHED:
156
scaling = (uskeleton.domain().extent() - toffset)/pattBndsX.extent();
159
case PAPCT_REPEATED_STRETCHED:
160
// if uskeleton is closed:
161
if(path_i.segs.front().at0() == path_i.segs.back().at1()){
162
nbCopies = static_cast<int>(std::floor((uskeleton.domain().extent() - toffset)/(pattBndsX.extent()+xspace)));
163
pattBndsX = Interval(pattBndsX.min(),pattBndsX.max()+xspace);
164
scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX.extent());
165
// if not closed: no space at the end
106
OptInterval pattBndsX = bounds_exact(x0);
107
OptInterval pattBndsY = bounds_exact(y0);
108
if (pattBndsX && pattBndsY) {
109
x0 -= pattBndsX->min();
110
y0 -= pattBndsY->middle();
112
double xspace = spacing;
113
double noffset = normal_offset;
114
double toffset = tang_offset;
115
if (prop_units.get_value() && pattBndsY){
116
xspace *= pattBndsX->extent();
117
noffset *= pattBndsY->extent();
118
toffset *= pattBndsX->extent();
121
//Prevent more than 90% overlap...
122
if (xspace < -pattBndsX->extent()*.9) {
123
xspace = -pattBndsX->extent()*.9;
125
//TODO: dynamical update of parameter ranges?
126
//if (prop_units.get_value()){
127
// spacing.param_set_range(-.9, NR_HUGE);
129
// spacing.param_set_range(-pattBndsX.extent()*.9, NR_HUGE);
134
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in;
135
paths_in = split_at_discontinuities(pwd2_in);
137
for (unsigned idx = 0; idx < paths_in.size(); idx++){
138
Geom::Piecewise<Geom::D2<Geom::SBasis> > path_i = paths_in[idx];
139
Piecewise<SBasis> x = x0;
140
Piecewise<SBasis> y = y0;
141
Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1);
142
uskeleton = remove_short_cuts(uskeleton,.01);
143
Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));
144
n = force_continuity(remove_short_cuts(n,.1));
150
nbCopies = static_cast<int>(floor((uskeleton.domain().extent() - toffset + xspace)/(pattBndsX->extent()+xspace)));
151
pattBndsX = Interval(pattBndsX->min(),pattBndsX->max()+xspace);
155
nbCopies = (toffset + pattBndsX->extent() < uskeleton.domain().extent()) ? 1 : 0;
158
case PAPCT_SINGLE_STRETCHED:
160
scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent();
163
case PAPCT_REPEATED_STRETCHED:
164
// if uskeleton is closed:
165
if(path_i.segs.front().at0() == path_i.segs.back().at1()){
166
nbCopies = static_cast<int>(std::floor((uskeleton.domain().extent() - toffset)/(pattBndsX->extent()+xspace)));
167
pattBndsX = Interval(pattBndsX->min(),pattBndsX->max()+xspace);
168
scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX->extent());
169
// if not closed: no space at the end
171
nbCopies = static_cast<int>(std::floor((uskeleton.domain().extent() - toffset + xspace)/(pattBndsX->extent()+xspace)));
172
pattBndsX = Interval(pattBndsX->min(),pattBndsX->max()+xspace);
173
scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX->extent() - xspace);
181
double pattWidth = pattBndsX->extent() * scaling;
183
if (scaling != 1.0) {
186
if ( scale_y_rel.get_value() ) {
187
y*=(scaling*prop_scale);
189
if (prop_scale != 1.0) y *= prop_scale;
194
for (int i=0; i<nbCopies; i++){
195
if (fuse_tolerance > 0){
196
Geom::Piecewise<Geom::D2<Geom::SBasis> > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs);
197
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > splited_output_piece = split_at_discontinuities(output_piece);
198
pre_output.insert(pre_output.end(), splited_output_piece.begin(), splited_output_piece.end() );
167
nbCopies = static_cast<int>(std::floor((uskeleton.domain().extent() - toffset + xspace)/(pattBndsX.extent()+xspace)));
168
pattBndsX = Interval(pattBndsX.min(),pattBndsX.max()+xspace);
169
scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX.extent() - xspace);
200
output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
177
double pattWidth = pattBndsX.extent() * scaling;
179
if (scaling != 1.0) {
182
if ( scale_y_rel.get_value() ) {
183
y*=(scaling*prop_scale);
185
if (prop_scale != 1.0) y *= prop_scale;
190
for (int i=0; i<nbCopies; i++){
191
output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
205
if (fuse_tolerance > 0){
206
pre_output = fuse_nearby_ends(pre_output, fuse_tolerance);
207
for (unsigned i=0; i<pre_output.size(); i++){
208
output.concat(pre_output[i]);
198
217
LPEFreehandShape::LPEFreehandShape(LivePathEffectObject *lpeobject) : LPEPatternAlongPath(lpeobject)