1/**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc.  All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27#include "bezier.h"
28
29#include "matrix.h"
30#include "polygon.h"
31
32#include "pipe/p_compiler.h"
33#include "util/u_debug.h"
34
35#include <stdlib.h>
36#include <stdio.h>
37#include <assert.h>
38#include <math.h>
39
40static const float flatness = 0.5;
41
42
43static INLINE void split_left(struct bezier *bez, VGfloat t, struct bezier* left)
44{
45    left->x1 = bez->x1;
46    left->y1 = bez->y1;
47
48    left->x2 = bez->x1 + t * (bez->x2 - bez->x1);
49    left->y2 = bez->y1 + t * (bez->y2 - bez->y1);
50
51    left->x3 = bez->x2 + t * (bez->x3 - bez->x2);
52    left->y3 = bez->y2 + t * (bez->y3 - bez->y2);
53
54    bez->x3 = bez->x3 + t * (bez->x4 - bez->x3);
55    bez->y3 = bez->y3 + t * (bez->y4 - bez->y3);
56
57    bez->x2 = left->x3 + t * (bez->x3 - left->x3);
58    bez->y2 = left->y3 + t * (bez->y3 - left->y3);
59
60    left->x3 = left->x2 + t * (left->x3 - left->x2);
61    left->y3 = left->y2 + t * (left->y3 - left->y2);
62
63    left->x4 = bez->x1 = left->x3 + t * (bez->x2 - left->x3);
64    left->y4 = bez->y1 = left->y3 + t * (bez->y2 - left->y3);
65}
66
67static INLINE void split(struct bezier *bez,
68                         struct bezier *first_half,
69                         struct bezier *second_half)
70{
71   double c         = (bez->x2 + bez->x3) * 0.5;
72   first_half->x2  = (bez->x1 + bez->x2) * 0.5;
73   second_half->x3 = (bez->x3 + bez->x4) * 0.5;
74   first_half->x1  = bez->x1;
75   second_half->x4 = bez->x4;
76   first_half->x3  = (first_half->x2 + c) * 0.5;
77   second_half->x2 = (second_half->x3 + c) * 0.5;
78   first_half->x4  = second_half->x1 =
79                     (first_half->x3 + second_half->x2) * 0.5;
80
81   c = (bez->y2 + bez->y3) / 2;
82   first_half->y2  = (bez->y1 + bez->y2) * 0.5;
83   second_half->y3 = (bez->y3 + bez->y4) * 0.5;
84   first_half->y1  = bez->y1;
85   second_half->y4 = bez->y4;
86   first_half->y3  = (first_half->y2 + c) * 0.5;
87   second_half->y2 = (second_half->y3 + c) * 0.5;
88   first_half->y4  = second_half->y1 =
89                     (first_half->y3 + second_half->y2) * 0.5;
90}
91
92struct polygon * bezier_to_polygon(struct bezier *bez)
93{
94   struct polygon *poly = polygon_create(64);
95   polygon_vertex_append(poly, bez->x1, bez->y1);
96   bezier_add_to_polygon(bez, poly);
97   return poly;
98}
99
100void bezier_add_to_polygon(const struct bezier *bez,
101                           struct polygon *poly)
102{
103   struct bezier beziers[32];
104   struct bezier *b;
105
106   beziers[0] = *bez;
107   b = beziers;
108
109   while (b >= beziers) {
110      double y4y1 = b->y4 - b->y1;
111      double x4x1 = b->x4 - b->x1;
112      double l = ABS(x4x1) + ABS(y4y1);
113      double d;
114      if (l > 1.f) {
115         d = ABS((x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2))
116             + ABS((x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3));
117      } else {
118         d = ABS(b->x1 - b->x2) + ABS(b->y1 - b->y2) +
119             ABS(b->x1 - b->x3) + ABS(b->y1 - b->y3);
120         l = 1.;
121      }
122      if (d < flatness*l || b == beziers + 31) {
123         /* good enough, we pop it off and add the endpoint */
124         polygon_vertex_append(poly, b->x4, b->y4);
125         --b;
126      } else {
127         /* split, second half of the bezier goes lower into the stack */
128         split(b, b+1, b);
129         ++b;
130      }
131   }
132}
133
134static void add_if_close(struct bezier *bez, VGfloat *length, VGfloat error)
135{
136   struct bezier left, right;     /* bez poly splits */
137   VGfloat len = 0.0;        /* arc length */
138   VGfloat chord;            /* chord length */
139
140   len = len + line_length(bez->x1, bez->y1, bez->x2, bez->y2);
141   len = len + line_length(bez->x2, bez->y2, bez->x3, bez->y3);
142   len = len + line_length(bez->x3, bez->y3, bez->x4, bez->y4);
143
144   chord = line_length(bez->x1, bez->y1, bez->x4, bez->y4);
145
146   if ((len-chord) > error) {
147      split(bez, &left, &right);                 /* split in two */
148      add_if_close(&left, length, error);       /* try left side */
149      add_if_close(&right, length, error);      /* try right side */
150      return;
151   }
152
153   *length = *length + len;
154
155   return;
156}
157
158float bezier_length(struct bezier *bez, float error)
159{
160   VGfloat length = 0.f;
161
162   add_if_close(bez, &length, error);
163   return length;
164}
165
166void bezier_init(struct bezier *bez,
167                 float x1, float y1,
168                 float x2, float y2,
169                 float x3, float y3,
170                 float x4, float y4)
171{
172   bez->x1 = x1;
173   bez->y1 = y1;
174   bez->x2 = x2;
175   bez->y2 = y2;
176   bez->x3 = x3;
177   bez->y3 = y3;
178   bez->x4 = x4;
179   bez->y4 = y4;
180#if 0
181   debug_printf("bezier in [%f, %f, %f, %f, %f, %f]\n",
182                x1, y1, x2, y2, x3, y3, x4, y4);
183#endif
184}
185
186
187static INLINE void bezier_init2v(struct bezier *bez,
188                                 float *pt1,
189                                 float *pt2,
190                                 float *pt3,
191                                 float *pt4)
192{
193   bez->x1 = pt1[0];
194   bez->y1 = pt1[1];
195
196   bez->x2 = pt2[0];
197   bez->y2 = pt2[1];
198
199   bez->x3 = pt3[0];
200   bez->y3 = pt3[1];
201
202   bez->x4 = pt4[0];
203   bez->y4 = pt4[1];
204}
205
206
207void bezier_transform(struct bezier *bez,
208                      struct matrix *matrix)
209{
210   assert(matrix_is_affine(matrix));
211   matrix_map_point(matrix, bez->x1, bez->y1, &bez->x1, &bez->y1);
212   matrix_map_point(matrix, bez->x2, bez->y2, &bez->x2, &bez->y2);
213   matrix_map_point(matrix, bez->x3, bez->y3, &bez->x3, &bez->y3);
214   matrix_map_point(matrix, bez->x4, bez->y4, &bez->x4, &bez->y4);
215}
216
217static INLINE void bezier_point_at(const struct bezier *bez, float t, float *pt)
218{
219   float a, b, c, d;
220   float m_t;
221   m_t = 1. - t;
222   b = m_t * m_t;
223   c = t * t;
224   d = c * t;
225   a = b * m_t;
226   b *= 3. * t;
227   c *= 3. * m_t;
228   pt[0] = a*bez->x1 + b*bez->x2 + c*bez->x3 + d*bez->x4;
229   pt[1] = a*bez->y1 + b*bez->y2 + c*bez->y3 + d*bez->y4;
230}
231
232static INLINE void bezier_normal_at(const struct bezier *bez, float t, float *norm)
233{
234   float m_t = 1. - t;
235   float a = m_t * m_t;
236   float b = t * m_t;
237   float c = t * t;
238
239   norm[0] =  (bez->y2-bez->y1) * a + (bez->y3-bez->y2) * b + (bez->y4-bez->y3) * c;
240   norm[1] = -(bez->x2-bez->x1) * a - (bez->x3-bez->x2) * b - (bez->x4-bez->x3) * c;
241}
242
243enum shift_result {
244   Ok,
245   Discard,
246   Split,
247   Circle
248};
249
250static enum shift_result good_offset(const struct bezier *b1,
251                                     const struct bezier *b2,
252                                     float offset, float threshold)
253{
254   const float o2 = offset*offset;
255   const float max_dist_line = threshold*offset*offset;
256   const float max_dist_normal = threshold*offset;
257   const float spacing = 0.25;
258   float i;
259   for (i = spacing; i < 0.99; i += spacing) {
260      float p1[2],p2[2], d, l;
261      float normal[2];
262      bezier_point_at(b1, i, p1);
263      bezier_point_at(b2, i, p2);
264      d = (p1[0] - p2[0])*(p1[0] - p2[0]) + (p1[1] - p2[1])*(p1[1] - p2[1]);
265      if (ABS(d - o2) > max_dist_line)
266         return Split;
267
268      bezier_normal_at(b1, i, normal);
269      l = ABS(normal[0]) + ABS(normal[1]);
270      if (l != 0.) {
271         d = ABS(normal[0]*(p1[1] - p2[1]) - normal[1]*(p1[0] - p2[0]) ) / l;
272         if (d > max_dist_normal)
273            return Split;
274      }
275   }
276   return Ok;
277}
278
279static INLINE void shift_line_by_normal(float *l, float offset)
280{
281   float norm[4];
282   float tx, ty;
283
284   line_normal(l, norm);
285   line_normalize(norm);
286
287   tx = (norm[2] - norm[0]) * offset;
288   ty = (norm[3] - norm[1]) * offset;
289   l[0] += tx; l[1] += ty;
290   l[2] += tx; l[3] += ty;
291}
292
293static INLINE VGboolean is_bezier_line(float (*points)[2], int count)
294{
295   float dx13 = points[2][0] - points[0][0];
296   float dy13 = points[2][1] - points[0][1];
297
298   float dx12 = points[1][0] - points[0][0];
299   float dy12 = points[1][1] - points[0][1];
300
301   debug_assert(count > 2);
302
303   if (count == 3) {
304      return floatsEqual(dx12 * dy13, dx13 * dy12);
305   } else if (count == 4) {
306      float dx14 = points[3][0] - points[0][0];
307      float dy14 = points[3][1] - points[0][1];
308
309      return (floatsEqual(dx12 * dy13, dx13 * dy12) &&
310              floatsEqual(dx12 * dy14, dx14 * dy12));
311   }
312
313   return VG_FALSE;
314}
315
316static INLINE void compute_pt_normal(float *pt1, float *pt2, float *res)
317{
318   float line[4];
319   float normal[4];
320   line[0] = 0.f; line[1] = 0.f;
321   line[2] = pt2[0] - pt1[0];
322   line[3] = pt2[1] - pt1[1];
323   line_normal(line, normal);
324   line_normalize(normal);
325
326   res[0] = normal[2];
327   res[1] = normal[3];
328}
329
330static enum shift_result shift(const struct bezier *orig,
331                               struct bezier *shifted,
332                               float offset, float threshold)
333{
334   int i;
335   int map[4];
336   VGboolean p1_p2_equal = (orig->x1 == orig->x2 && orig->y1 == orig->y2);
337   VGboolean p2_p3_equal = (orig->x2 == orig->x3 && orig->y2 == orig->y3);
338   VGboolean p3_p4_equal = (orig->x3 == orig->x4 && orig->y3 == orig->y4);
339
340   float points[4][2];
341   int np = 0;
342   float bounds[4];
343   float points_shifted[4][2];
344   float prev_normal[2];
345
346   points[np][0] = orig->x1;
347   points[np][1] = orig->y1;
348   map[0] = 0;
349   ++np;
350   if (!p1_p2_equal) {
351      points[np][0] = orig->x2;
352      points[np][1] = orig->y2;
353      ++np;
354   }
355   map[1] = np - 1;
356   if (!p2_p3_equal) {
357      points[np][0] = orig->x3;
358      points[np][1] = orig->y3;
359      ++np;
360   }
361   map[2] = np - 1;
362   if (!p3_p4_equal) {
363      points[np][0] = orig->x4;
364      points[np][1] = orig->y4;
365      ++np;
366   }
367   map[3] = np - 1;
368   if (np == 1)
369      return Discard;
370
371   /* We need to specialcase lines of 3 or 4 points due to numerical
372      instability in intersection code below */
373   if (np > 2 && is_bezier_line(points, np)) {
374      float l[4] = { points[0][0], points[0][1],
375                     points[np-1][0], points[np-1][1] };
376      float ctrl1[2], ctrl2[2];
377      if (floatsEqual(points[0][0], points[np-1][0]) &&
378          floatsEqual(points[0][1], points[np-1][1]))
379         return Discard;
380
381      shift_line_by_normal(l, offset);
382      line_point_at(l, 0.33, ctrl1);
383      line_point_at(l, 0.66, ctrl2);
384      bezier_init(shifted, l[0], l[1],
385                  ctrl1[0], ctrl1[1], ctrl2[0], ctrl2[1],
386                  l[2], l[3]);
387      return Ok;
388   }
389
390   bezier_bounds(orig, bounds);
391   if (np == 4 && bounds[2] < .1*offset && bounds[3] < .1*offset) {
392      float l = (orig->x1 - orig->x2)*(orig->x1 - orig->x2) +
393                (orig->y1 - orig->y2)*(orig->y1 - orig->y1) *
394                (orig->x3 - orig->x4)*(orig->x3 - orig->x4) +
395                (orig->y3 - orig->y4)*(orig->y3 - orig->y4);
396      float dot = (orig->x1 - orig->x2)*(orig->x3 - orig->x4) +
397                  (orig->y1 - orig->y2)*(orig->y3 - orig->y4);
398      if (dot < 0 && dot*dot < 0.8*l)
399         /* the points are close and reverse dirction. Approximate the whole
400            thing by a semi circle */
401         return Circle;
402   }
403
404   compute_pt_normal(points[0], points[1], prev_normal);
405
406   points_shifted[0][0] = points[0][0] + offset * prev_normal[0];
407   points_shifted[0][1] = points[0][1] + offset * prev_normal[1];
408
409   for (i = 1; i < np - 1; ++i) {
410      float normal_sum[2], r;
411      float next_normal[2];
412      compute_pt_normal(points[i], points[i + 1], next_normal);
413
414      normal_sum[0] = prev_normal[0] + next_normal[0];
415      normal_sum[1] = prev_normal[1] + next_normal[1];
416
417      r = 1.0 + prev_normal[0] * next_normal[0]
418          + prev_normal[1] * next_normal[1];
419
420      if (floatsEqual(r + 1, 1)) {
421         points_shifted[i][0] = points[i][0] + offset * prev_normal[0];
422         points_shifted[i][1] = points[i][1] + offset * prev_normal[1];
423      } else {
424         float k = offset / r;
425         points_shifted[i][0] = points[i][0] + k * normal_sum[0];
426         points_shifted[i][1] = points[i][1] + k * normal_sum[1];
427      }
428
429      prev_normal[0] = next_normal[0];
430      prev_normal[1] = next_normal[1];
431   }
432
433   points_shifted[np - 1][0] = points[np - 1][0] + offset * prev_normal[0];
434   points_shifted[np - 1][1] = points[np - 1][1] + offset * prev_normal[1];
435
436   bezier_init2v(shifted,
437                 points_shifted[map[0]], points_shifted[map[1]],
438                 points_shifted[map[2]], points_shifted[map[3]]);
439
440   return good_offset(orig, shifted, offset, threshold);
441}
442
443static VGboolean make_circle(const struct bezier *b, float offset, struct bezier *o)
444{
445   float normals[3][2];
446   float dist;
447   float angles[2];
448   float sign = 1.f;
449   int i;
450   float circle[3][2];
451
452   normals[0][0] = b->y2 - b->y1;
453   normals[0][1] = b->x1 - b->x2;
454   dist = sqrt(normals[0][0]*normals[0][0] + normals[0][1]*normals[0][1]);
455   if (floatsEqual(dist + 1, 1.f))
456      return VG_FALSE;
457   normals[0][0] /= dist;
458   normals[0][1] /= dist;
459
460   normals[2][0] = b->y4 - b->y3;
461   normals[2][1] = b->x3 - b->x4;
462   dist = sqrt(normals[2][0]*normals[2][0] + normals[2][1]*normals[2][1]);
463   if (floatsEqual(dist + 1, 1.f))
464      return VG_FALSE;
465   normals[2][0] /= dist;
466   normals[2][1] /= dist;
467
468   normals[1][0] = b->x1 - b->x2 - b->x3 + b->x4;
469   normals[1][1] = b->y1 - b->y2 - b->y3 + b->y4;
470   dist = -1*sqrt(normals[1][0]*normals[1][0] + normals[1][1]*normals[1][1]);
471   normals[1][0] /= dist;
472   normals[1][1] /= dist;
473
474   for (i = 0; i < 2; ++i) {
475      float cos_a = normals[i][0]*normals[i+1][0] + normals[i][1]*normals[i+1][1];
476      if (cos_a > 1.)
477         cos_a = 1.;
478      if (cos_a < -1.)
479         cos_a = -1;
480      angles[i] = acos(cos_a)/M_PI;
481   }
482
483   if (angles[0] + angles[1] > 1.) {
484      /* more than 180 degrees */
485      normals[1][0] = -normals[1][0];
486      normals[1][1] = -normals[1][1];
487      angles[0] = 1. - angles[0];
488      angles[1] = 1. - angles[1];
489      sign = -1.;
490   }
491
492   circle[0][0] = b->x1 + normals[0][0]*offset;
493   circle[0][1] = b->y1 + normals[0][1]*offset;
494
495   circle[1][0] = 0.5*(b->x1 + b->x4) + normals[1][0]*offset;
496   circle[1][1] = 0.5*(b->y1 + b->y4) + normals[1][1]*offset;
497
498   circle[2][0] = b->x4 + normals[2][0]*offset;
499   circle[2][1] = b->y4 + normals[2][1]*offset;
500
501   for (i = 0; i < 2; ++i) {
502      float kappa = 2.*KAPPA * sign * offset * angles[i];
503
504      o->x1 = circle[i][0];
505      o->y1 = circle[i][1];
506      o->x2 = circle[i][0] - normals[i][1]*kappa;
507      o->y2 = circle[i][1] + normals[i][0]*kappa;
508      o->x3 = circle[i+1][0] + normals[i+1][1]*kappa;
509      o->y3 = circle[i+1][1] - normals[i+1][0]*kappa;
510      o->x4 = circle[i+1][0];
511      o->y4 = circle[i+1][1];
512
513      ++o;
514   }
515   return VG_TRUE;
516}
517
518int bezier_translate_by_normal(struct bezier *bez,
519                               struct bezier *curves,
520                               int max_curves,
521                               float normal_len,
522                               float threshold)
523{
524   struct bezier beziers[10];
525   struct bezier *b, *o;
526
527   /* fixme: this should really be floatsEqual */
528   if (bez->x1 == bez->x2 && bez->x1 == bez->x3 && bez->x1 == bez->x4 &&
529       bez->y1 == bez->y2 && bez->y1 == bez->y3 && bez->y1 == bez->y4)
530      return 0;
531
532   --max_curves;
533redo:
534   beziers[0] = *bez;
535   b = beziers;
536   o = curves;
537
538   while (b >= beziers) {
539      int stack_segments = b - beziers + 1;
540      enum shift_result res;
541      if ((stack_segments == 10) || (o - curves == max_curves - stack_segments)) {
542         threshold *= 1.5;
543         if (threshold > 2.)
544            goto give_up;
545         goto redo;
546      }
547      res = shift(b, o, normal_len, threshold);
548      if (res == Discard) {
549         --b;
550      } else if (res == Ok) {
551         ++o;
552         --b;
553         continue;
554      } else if (res == Circle && max_curves - (o - curves) >= 2) {
555         /* add semi circle */
556         if (make_circle(b, normal_len, o))
557            o += 2;
558         --b;
559      } else {
560         split(b, b+1, b);
561         ++b;
562      }
563   }
564
565give_up:
566   while (b >= beziers) {
567      enum shift_result res = shift(b, o, normal_len, threshold);
568
569      /* if res isn't Ok or Split then *o is undefined */
570      if (res == Ok || res == Split)
571         ++o;
572
573      --b;
574   }
575
576   debug_assert(o - curves <= max_curves);
577   return o - curves;
578}
579
580void bezier_bounds(const struct bezier *bez,
581                   float *bounds/*x/y/width/height*/)
582{
583   float xmin = bez->x1;
584   float xmax = bez->x1;
585   float ymin = bez->y1;
586   float ymax = bez->y1;
587
588   if (bez->x2 < xmin)
589      xmin = bez->x2;
590   else if (bez->x2 > xmax)
591      xmax = bez->x2;
592   if (bez->x3 < xmin)
593      xmin = bez->x3;
594   else if (bez->x3 > xmax)
595      xmax = bez->x3;
596   if (bez->x4 < xmin)
597      xmin = bez->x4;
598   else if (bez->x4 > xmax)
599      xmax = bez->x4;
600
601   if (bez->y2 < ymin)
602      ymin = bez->y2;
603   else if (bez->y2 > ymax)
604      ymax = bez->y2;
605   if (bez->y3 < ymin)
606      ymin = bez->y3;
607   else if (bez->y3 > ymax)
608      ymax = bez->y3;
609   if (bez->y4 < ymin)
610      ymin = bez->y4;
611   else if (bez->y4 > ymax)
612      ymax = bez->y4;
613
614   bounds[0] = xmin; /* x */
615   bounds[1] = ymin; /* y */
616   bounds[2] = xmax - xmin; /* width */
617   bounds[3] = ymax - ymin; /* height */
618}
619
620void bezier_start_tangent(const struct bezier *bez,
621                          float *tangent)
622{
623   tangent[0] = bez->x1;
624   tangent[1] = bez->y1;
625   tangent[2] = bez->x2;
626   tangent[3] = bez->y2;
627
628   if (null_line(tangent)) {
629      tangent[0] = bez->x1;
630      tangent[1] = bez->y1;
631      tangent[2] = bez->x3;
632      tangent[3] = bez->y3;
633   }
634   if (null_line(tangent)) {
635      tangent[0] = bez->x1;
636      tangent[1] = bez->y1;
637      tangent[2] = bez->x4;
638      tangent[3] = bez->y4;
639   }
640}
641
642
643static INLINE VGfloat bezier_t_at_length(struct bezier *bez,
644                                         VGfloat at_length,
645                                         VGfloat error)
646{
647   VGfloat len = bezier_length(bez, error);
648   VGfloat t   = 1.0;
649   VGfloat last_bigger = 1.;
650
651   if (at_length > len || floatsEqual(at_length, len))
652      return t;
653
654   if (floatIsZero(at_length))
655      return 0.f;
656
657   t *= 0.5;
658   while (1) {
659      struct bezier right = *bez;
660      struct bezier left;
661      VGfloat tmp_len;
662      split_left(&right, t, &left);
663      tmp_len = bezier_length(&left, error);
664      if (ABS(tmp_len - at_length) < error)
665         break;
666
667      if (tmp_len < at_length) {
668         t += (last_bigger - t)*.5;
669      } else {
670         last_bigger = t;
671         t -= t*.5;
672      }
673   }
674   return t;
675}
676
677void bezier_point_at_length(struct bezier *bez,
678                            float length,
679                            float *point,
680                            float *normal)
681{
682   /* ~0.000001 seems to be required to pass G2080x tests */
683   VGfloat t = bezier_t_at_length(bez, length, 0.000001);
684   bezier_point_at(bez, t, point);
685   bezier_normal_at(bez, t, normal);
686   vector_unit(normal);
687}
688
689void bezier_point_at_t(struct bezier *bez, float t,
690                       float *point, float *normal)
691{
692   bezier_point_at(bez, t, point);
693   bezier_normal_at(bez, t, normal);
694   vector_unit(normal);
695}
696
697void bezier_exact_bounds(const struct bezier *bez,
698                         float *bounds/*x/y/width/height*/)
699{
700   struct polygon *poly = polygon_create(64);
701   polygon_vertex_append(poly, bez->x1, bez->y1);
702   bezier_add_to_polygon(bez, poly);
703   polygon_bounding_rect(poly, bounds);
704   polygon_destroy(poly);
705}
706
707