Skip to content

Commit 6a330c4

Browse files
authored
fix(advanced outline): fix rendering of width point in non-homogeneous position (synfig#3555)
fix(advanced outline): fix rendering of width point in non-homogeneous position
2 parents 8eca8af + be05568 commit 6a330c4

8 files changed

Lines changed: 750 additions & 129 deletions

File tree

synfig-core/src/modules/mod_geometry/advanced_outline.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ namespace {
9494
Real calc_position(Real p, const rendering::Bend &bend, bool homogeneous) {
9595
return homogeneous
9696
? p*bend.length1()
97-
: bend.length_by_l( p*bend.l1() );
97+
: bend.length_by_index( p*bend.l1() );
9898
}
9999

100100
class AdvancedPoint {

synfig-core/src/synfig/rendering/primitive/bend.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ Bend::add(const Vector &p, const Vector &t0, const Vector &t1, Mode mode, bool c
173173
&& t0.is_equal_to(Vector()) )
174174
{
175175
// skip zero length segment
176+
last.last_index += 1;
176177
last.t1 = t1;
177178
last.tn1 = t1.norm();
178179
last.mode = mode;
@@ -185,18 +186,21 @@ Bend::add(const Vector &p, const Vector &t0, const Vector &t1, Mode mode, bool c
185186

186187
point.p = last.p;
187188
point.length = last.length;
188-
Real l0 = last.l, l = step;
189+
const Real l0 = last.last_index;
190+
Real l = step;
189191
for(int i = 1; i < segments; ++i, l += step) {
190192
Vector pp = h.p(l);
191-
point.l = l0 + l;
192-
point.length = calc_length ? point.length + (pp - point.p).mag() : point.l;
193+
point.index = l0 + l;
194+
point.last_index = l0 + l;
195+
point.length = calc_length ? point.length + (pp - point.p).mag() : point.index;
193196
point.p = pp;
194197
point.t0 = point.t1 = h.t(l);
195198
point.tn0 = point.tn1 = h.d(l);
196199
points.push_back(point);
197200
}
198-
point.l = l0 + 1;
199-
point.length = calc_length ? point.length + (p - point.p).mag() : point.l;
201+
point.index = l0 + 1;
202+
point.last_index = l0 + 1;
203+
point.length = calc_length ? point.length + (p - point.p).mag() : point.index;
200204
point.tn0 = h.d(1);
201205
} else {
202206
point.tn0 = t0.norm();
@@ -235,15 +239,15 @@ Bend::tails()
235239

236240
{
237241
PointList::iterator i1 = points.begin(), i0 = i1++;
238-
Real dl = i1->l - i0->l;
242+
Real dl = i1->index - i0->last_index;
239243
Hermite h(i0->p, i1->p, i0->t1*dl, i1->t0*dl);
240244
i0->t0 = i0->tn0 = h.d(0);
241245
i0->mode = NONE;
242246
}
243247

244248
{
245249
PointList::iterator i0 = points.end(), i1 = (--i0)--;
246-
Real dl = i1->l - i0->l;
250+
Real dl = i1->index - i0->last_index;
247251
Hermite h(i0->p, i1->p, i0->t1*dl, i1->t0*dl);
248252
i1->t1 = i1->tn1 = h.d(1);
249253
i1->mode = NONE;
@@ -252,16 +256,21 @@ Bend::tails()
252256

253257

254258
Bend::PointList::const_iterator
255-
Bend::find_by_l(Real l) const
259+
Bend::find_by_index(Real index) const
256260
{
257261
if (points.empty())
258262
return points.end();
259263
PointList::const_iterator a = points.begin(), b = points.end() - 1, c;
260-
if (!approximate_less(a->l, l)) return a;
261-
if (!approximate_less(l, b->l)) return b;
264+
265+
if (approximate_greater_or_equal(a->last_index, index))
266+
return a;
267+
if (approximate_greater_or_equal(index, b->index))
268+
return b;
269+
262270
while( (c = a + (b - a)/2) != a ) {
263-
if (approximate_equal(l, c->l)) return c;
264-
(l < c->l ? b : a) = c;
271+
if (approximate_greater_or_equal(index, c->index) && approximate_less_or_equal(index, c->last_index))
272+
return c;
273+
(index < c->index ? b : a) = c;
265274
}
266275
return c;
267276
}
@@ -272,30 +281,35 @@ Bend::find(Real length) const
272281
if (points.empty())
273282
return points.end();
274283
PointList::const_iterator a = points.begin(), b = points.end() - 1, c;
275-
if (!approximate_less(a->length, length)) return a;
276-
if (!approximate_less(length, b->length)) return b;
284+
285+
if (approximate_greater_or_equal(a->length, length))
286+
return a;
287+
if (approximate_greater_or_equal(length, b->length))
288+
return b;
289+
277290
while( (c = a + (b - a)/2) != a ) {
278291
if (approximate_equal(length, c->length)) return c;
279292
(length < c->length ? b : a) = c;
280293
}
281294
return c;
282295
}
283296

284-
Real Bend::length_by_l(Real l) const {
285-
PointList::const_iterator i = find_by_l(l);
297+
Real Bend::length_by_index(Real index) const {
298+
PointList::const_iterator i = find_by_index(index);
286299
if (i == points.end())
287300
return 0;
288301

289-
if (approximate_equal(l, i->l)) return i->length;
290-
if (l < i->l)
291-
return i->length + (l - i->l);
302+
if (approximate_greater_or_equal(index, i->index) && approximate_less_or_equal(index, i->last_index))
303+
return i->length;
304+
if (index < i->index)
305+
return i->length + (index - i->index); // TODO: review: shouldn't interpolate?
292306

293307
PointList::const_iterator j = i + 1;
294308
if (j == points.end())
295-
return i->length + (l - i->l);
309+
return i->length + (index - i->last_index); // TODO: review: shouldn't interpolate?
296310

297-
Real dl = j->l - i->l;
298-
return approximate_zero(dl) ? i->length : i->length + (j->length - i->length)*(l - i->l)/dl;
311+
Real dl = j->index - i->last_index;
312+
return approximate_zero(dl) ? i->length : i->length + (j->length - i->length)*(index - i->last_index)/dl;
299313
}
300314

301315
Bend::Point
@@ -312,7 +326,8 @@ Bend::interpolate(Real length) const
312326
Point s;
313327
s.p = i->p + i->tn0*dlen;
314328
s.t0 = s.t1 = s.tn0 = s.tn1 = i->tn0;
315-
s.l = i->l + dlen;
329+
s.index = i->last_index + dlen;
330+
s.last_index = s.index;
316331
s.length = length;
317332
s.e0 = s.e1 = i->e0;
318333
return s;
@@ -325,7 +340,8 @@ Bend::interpolate(Real length) const
325340
Point s;
326341
s.p = i->p + i->tn1*dlen;
327342
s.t0 = s.t1 = s.tn0 = s.tn1 = i->t1;
328-
s.l = i->l + dlen;
343+
s.index = i->last_index + dlen;
344+
s.last_index = s.index;
329345
s.length = length;
330346
s.e0 = s.e1 = i->e1;
331347
return s;
@@ -334,14 +350,15 @@ Bend::interpolate(Real length) const
334350
// interpolation
335351
Real l = j->length - i->length;
336352
l = approximate_zero(l) ? 0 : (length - i->length)/l;
337-
Real dl = j->l - i->l;
353+
Real dl = j->index - i->last_index;
338354
Hermite h(i->p, j->p, i->t1*dl, j->t0*dl);
339355

340356
Point s;
341357
s.p = h.p(l);
342358
s.t0 = s.t1 = h.t(l);
343359
s.tn0 = s.tn1 = h.d(l);
344-
s.l = i->l + l*(j->l - i->l);
360+
s.index = i->last_index + l*(j->index - i->last_index);
361+
s.last_index = s.index;
345362
s.length = length;
346363
s.e0 = s.e1 = (i->e1 && j->e0);
347364

@@ -456,7 +473,7 @@ Bend::bend(Contour &dst, const Contour &src, const Matrix &matrix, int segments)
456473
const Intersection &next = *bi;
457474
if (approximate_equal(prev.l, next.l)) continue;
458475

459-
bool flip = next.point.l < prev.point.l;
476+
bool flip = next.point.index < prev.point.last_index;
460477
bool e0 = flip ? prev.point.e0 : prev.point.e1;
461478
bool e1 = flip ? next.point.e1 : next.point.e0;
462479
if (e0 && e1) {
@@ -469,7 +486,7 @@ Bend::bend(Contour &dst, const Contour &src, const Matrix &matrix, int segments)
469486
Vector dst_p0 = prev.point.p + tn0.perp()*src_p0y;
470487
Vector dst_p1 = next.point.p + tn1.perp()*src_p1y;
471488

472-
bool vertical = approximate_equal(next.point.l, prev.point.l);
489+
bool vertical = approximate_greater_or_equal(next.point.index, prev.point.index) && approximate_less_or_equal(next.point.index, prev.point.last_index);
473490

474491
half_corner(dst, dst_move_flag, prev.point, src_p0y, flip || vertical, true);
475492
touch(dst, dst_move_flag, dst_p0);

synfig-core/src/synfig/rendering/primitive/bend.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ class Bend: public etl::shared_object
7272
Vector tn0, tn1;
7373
Mode mode;
7474
bool e0, e1;
75-
Real l;
75+
Real index; /**< The point index in the B-line */
76+
Real last_index; /**< It is different (greater) of index only if zero-length segments follow this point */
7677
Real length;
77-
Point(): mode(NONE), e0(), e1(), l(), length() { }
78+
Point(): mode(NONE), e0(), e1(), index(), last_index(), length() { }
7879
};
7980

8081
typedef std::vector<Point> PointList;
@@ -86,18 +87,18 @@ class Bend: public etl::shared_object
8687
void tails();
8788

8889
Real l0() const
89-
{ return points.empty() ? Real() : points.front().l; }
90+
{ return points.empty() ? Real() : points.front().index; }
9091
Real l1() const
91-
{ return points.empty() ? Real() : points.back().l; }
92+
{ return points.empty() ? Real() : points.back().last_index; }
9293

9394
Real length0() const
9495
{ return points.empty() ? Real() : points.front().length; }
9596
Real length1() const
9697
{ return points.empty() ? Real() : points.back().length; }
9798

98-
PointList::const_iterator find_by_l(Real l) const;
99+
PointList::const_iterator find_by_index(Real index) const;
99100
PointList::const_iterator find(Real length) const;
100-
Real length_by_l(Real length) const;
101+
Real length_by_index(Real index) const;
101102
Point interpolate(Real length) const;
102103

103104
void bend(Contour &dst, const Contour &src, const Matrix &matrix, int segments) const;

synfig-core/src/synfig/valuenodes/valuenode_bline.cpp

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,8 @@ synfig::find_closest_point(const ValueBase &bline, const Point &pos, Real radius
228228
Real
229229
synfig::std_to_hom(const ValueBase &bline, Real pos, bool index_loop, bool bline_loop)
230230
{
231-
Real loops = index_loop ? floor(pos) : 0.0;
231+
const Real loops = index_loop ? floor(pos) : 0.0;
232232
pos -= loops;
233-
assert(approximate_greater_or_equal(pos, 0.0));
234233

235234
// trivial cases
236235
if (approximate_less_or_equal(pos, Real(0)))
@@ -239,31 +238,31 @@ synfig::std_to_hom(const ValueBase &bline, Real pos, bool index_loop, bool bline
239238
return loops + 1;
240239

241240
const std::vector<BLinePoint> list(bline.get_list_of(BLinePoint()));
242-
size_t size = list.size();
241+
const size_t size = list.size();
243242
if (size == 0)
244243
return loops;
245-
size_t count = bline_loop? size : size - 1;
244+
const size_t count = bline_loop? size : size - 1;
246245
if (count < 1)
247246
return loops + pos;
248247

249248
// Calculate the lengths and the total length
250249
std::vector<Real> lengths;
251-
Real bline_total_length = bline_length(list, bline_loop, &lengths);
250+
const Real bline_total_length = bline_length(list, bline_loop, &lengths);
252251
// If the total length of the bline is zero return pos
253-
if(approximate_equal(bline_total_length, 0.0))
252+
if (approximate_equal(bline_total_length, 0.0))
254253
return pos;
255-
size_t from_vertex = size_t(pos*count);
254+
const size_t from_vertex = size_t(pos*count);
256255
// Calculate the partial length until the bezier that holds the current
257256
Real partial_length = 0;
258257
std::vector<Real>::const_iterator length_iter(lengths.begin());
259-
for(size_t i=0; i < from_vertex; ++i, ++length_iter)
258+
for (size_t i = 0; i < from_vertex; ++i, ++length_iter)
260259
partial_length += *length_iter;
261260
// Calculate the remaining length of the position over current bezier
262261
// Setup the curve of the current bezier.
263-
size_t next_vertex = (from_vertex + 1) % size;
264-
const BLinePoint &blinepoint0 = list[from_vertex];
265-
const BLinePoint &blinepoint1 = list[next_vertex];
266-
hermite<Vector> curve(blinepoint0.get_vertex(), blinepoint1.get_vertex(),
262+
const size_t next_vertex = (from_vertex + 1) % size;
263+
const BLinePoint& blinepoint0 = list[from_vertex];
264+
const BLinePoint& blinepoint1 = list[next_vertex];
265+
const hermite<Vector> curve(blinepoint0.get_vertex(), blinepoint1.get_vertex(),
267266
blinepoint0.get_tangent2(), blinepoint1.get_tangent1());
268267
// add the distance on the bezier we are on.
269268
partial_length += curve.find_distance(0.0, pos*count - from_vertex);
@@ -274,7 +273,7 @@ synfig::std_to_hom(const ValueBase &bline, Real pos, bool index_loop, bool bline
274273
Real
275274
synfig::hom_to_std(const ValueBase &bline, Real pos, bool index_loop, bool bline_loop)
276275
{
277-
Real loops = index_loop ? floor(pos) : 0.0;
276+
const Real loops = index_loop ? floor(pos) : 0.0;
278277
pos -= loops;
279278
assert(approximate_greater_or_equal(pos, 0.0));
280279

@@ -285,59 +284,58 @@ synfig::hom_to_std(const ValueBase &bline, Real pos, bool index_loop, bool bline
285284
return loops + 1;
286285

287286
const std::vector<BLinePoint> list(bline.get_list_of(BLinePoint()));
288-
size_t size = list.size();
287+
const size_t size = list.size();
289288
if (size == 0)
290289
return loops;
291-
size_t count = bline_loop? size : size - 1;
290+
const size_t count = bline_loop? size : size - 1;
292291
if (count < 1)
293292
return loops + pos;
294293

295294
// Calculate the lengths and the total length
296295
std::vector<Real> lengths;
297-
Real bline_total_length=bline_length(bline, bline_loop,&lengths);
296+
const Real bline_total_length = bline_length(bline, bline_loop, &lengths);
298297
// Calculate the my partial length (the length where pos is)
299-
Real target_length = pos * bline_total_length;
298+
const Real target_length = pos * bline_total_length;
300299
std::vector<Real>::const_iterator length_iter(lengths.begin());
301300
// Find the previous bezier where we pos is placed and the sum
302301
// of lengths to it (cumulative_length)
303302
// also remember the bezier's length where we stop
304303
Real cumulative_length = 0;
305304
size_t from_vertex = 0;
306305
Real segment_length = 0;
307-
while(target_length > cumulative_length && length_iter != lengths.end())
308-
{
306+
while (approximate_greater(target_length, cumulative_length) && length_iter != lengths.end()) {
309307
segment_length = *length_iter;
310308
cumulative_length += segment_length;
311309

312310
++length_iter;
313311
++from_vertex;
314312
}
315-
// correct the iters and partial length in case we passed over
316-
if(cumulative_length > target_length)
317-
{
313+
if (approximate_equal(target_length, cumulative_length)) {
314+
return loops + Real(from_vertex) / count;
315+
} else {
316+
// correct the iters and partial length in case we passed over
318317
--length_iter;
319318
cumulative_length -= *length_iter;
320319
--from_vertex;
321320
}
322321
// set up the curve
323-
const BLinePoint &blinepoint0 = list[from_vertex];
324-
const BLinePoint &blinepoint1 = list[(from_vertex+1) % size];
325-
hermite<Vector> curve(blinepoint0.get_vertex(), blinepoint1.get_vertex(),
322+
const BLinePoint& blinepoint0 = list[from_vertex];
323+
const BLinePoint& blinepoint1 = list[(from_vertex+1) % size];
324+
const hermite<Vector> curve(blinepoint0.get_vertex(), blinepoint1.get_vertex(),
326325
blinepoint0.get_tangent2(), blinepoint1.get_tangent1());
327326
// Find the solution to which is the standard position which matches the current
328327
// homogeneous position
329328
// Secant method: http://en.wikipedia.org/wiki/Secant_method
330329
Real sn(0.0); // the standard position on current bezier
331330
Real sn1(0.0), sn2(1.0);
332-
Real t0((target_length-cumulative_length)/segment_length); // the homogeneous position on the current bezier
331+
const Real t0((target_length-cumulative_length)/segment_length); // the homogeneous position on the current bezier
333332
int iterations=0;
334333
const int max_iterations=100;
335334
const Real max_error(0.00001);
336335
Real error;
337336
Real fsn1(t0-curve.find_distance(0.0,sn1)/segment_length);
338337
Real fsn2(t0-curve.find_distance(0.0,sn2)/segment_length);
339-
do
340-
{
338+
do {
341339
sn=sn1-fsn1*((sn1-sn2)/(fsn1-fsn2));
342340
Real fsn=t0-curve.find_distance(0.0, sn)/segment_length;
343341
sn2=sn1;
@@ -360,22 +358,22 @@ synfig::bline_length(const ValueBase &bline, bool bline_loop, std::vector<Real>
360358
const std::vector<BLinePoint> list(bline.get_list_of(BLinePoint()));
361359
if (list.empty())
362360
return 0;
363-
size_t max_vertex_index(list.size());
364-
if(!bline_loop) max_vertex_index--;
365-
if(max_vertex_index < 1) return Real();
361+
const size_t max_vertex_index = bline_loop ? list.size() : list.size() - 1;
362+
if (max_vertex_index < 1)
363+
return Real();
366364

367365
if (lengths)
368366
lengths->reserve(max_vertex_index);
369367

370368
// Calculate the lengths and the total length
371369
Real total_length = 0;
372-
for(size_t i0 = 0; i0 < max_vertex_index; ++i0) {
373-
size_t i1 = (i0 + 1)%list.size();
374-
const BLinePoint &blinepoint0 = list[i0];
375-
const BLinePoint &blinepoint1 = list[i1];
376-
hermite<Vector> curve(blinepoint0.get_vertex(), blinepoint1.get_vertex(),
370+
for (size_t i0 = 0; i0 < max_vertex_index; ++i0) {
371+
const size_t i1 = (i0 + 1) % list.size();
372+
const BLinePoint& blinepoint0 = list[i0];
373+
const BLinePoint& blinepoint1 = list[i1];
374+
const hermite<Vector> curve(blinepoint0.get_vertex(), blinepoint1.get_vertex(),
377375
blinepoint0.get_tangent2(), blinepoint1.get_tangent1());
378-
Real l=curve.length();
376+
const Real l = curve.length();
379377
if(lengths) lengths->push_back(l);
380378
total_length+=l;
381379
}

0 commit comments

Comments
 (0)