I looked at the math in this 'till my head hurt.  Googling tells me  
you are implementing de Casteljau's algorithm; perhaps a note to that  
effect would be good.  AFAICT it is correct.  What is the bug you see?

(i.e., I approve this, but if there is someone out there with a  
better grasp on the math, feel free to chime in!)

On 2006-03-13 16:19 EST, Max Carlson wrote:

> Change 40515 by [EMAIL PROTECTED] on 2006/03/13 13:15:50 *pending*
>
>         Summary: Add cubic bezier implementation.  It works, but  
> the updated testcase (attached) shows a bug.  Should we accept this  
> patch and file a separate bug?
>
>         New Features:
>
>         Bugs Fixed: LPP-1639
>
>         Technical Reviewer:  Tucker
>         QA Reviewer:  (pending)
>         Doc Reviewer:  (pending)
>
>         Documentation:
>
>         Release Notes:
>
>         Details:
>
>         Tests:
>
> Affected files ...
>
> ... //depot/lps-dev/WEB-INF/lps/lfc/views/LzDrawView.as#19 edit
> ... //depot/lps-dev/test/drawing/drawing.lzx#5 edit
>
>
> Differences ...
>
> ==== //depot/lps-dev/WEB-INF/lps/lfc/views/LzDrawView.as#19 - c: 
> \Laszlo\lps-dev\
> WEB-INF\lps\lfc\views\LzDrawView.as ====
> 521a522,639
> > //------------------------------------------------------------------ 
> ----------
> -
> > // bezierCurveTo() adds the given coordinate (x, y) to the list  
> of points of
> > // the subpath, and connects the two points with a bezier curve  
> with control
> > // points (cp1x, cp1y) and (cp2x, cp2y). It then sets the current  
> position to
>
> > // the given coordinate (x, y).  Approximates a cubic bezier with  
> quadratic
> > // segments, to within a midpoint error of
> > // LzDrawView.prototype.bezierCurveTo.error.
> > //
> > // @param Number cp1x: X value of control point 1
> > // @param Number cp1y: Y value of control point 1
> > // @param Number cp2x: X value of control point 2
> > // @param Number cp2y: Y value of control point 2
> > // @param Number x: X value of endpoint
> > // @param Number y: Y value of endpoint
> > //------------------------------------------------------------------ 
> ----------
> -
> > LzDrawView.prototype.bezierCurveTo = function(cp1x, cp1y, cp2x,  
> cp2y, x, y) {
> >     var error = arguments.callee.error;
> >
> >     // These would be useful generally, but put them inside the
> >     // function so they don't pollute the general namespace.
> >     function distance(p0, p1) {
> >         var dx = p1.x - p0.x;
> >         var dy = p1.y - p0.y;
> >         return Math.sqrt(dx*dx+dy*dy);
> >     }
> >     // returns null if they're collinear
> >     function intersection(p0, p1, p2, p3) {
> >         var u = (p3.x-p2.x)*(p0.y-p2.y) - (p3.y-p2.y)*(p0.x-p2.x);
> >         var d = (p3.y-p2.y)*(p1.x-p0.x) - (p3.x-p2.x)*(p1.y-p0.y);
> >         _root.Debug.write('intersection', u, d);
> >         if (!d) return null;
> >         u /= d;
> >         return {x: p0.x + (p1.x-p0.x) * u,
> >                 y: p0.y + (p1.y-p0.y) * u};
> >     }
> >     function midpoint(p0, p1) {
> >         return {x: (p0.x+p1.x)/2, y: (p0.y+p1.y)/2};
> >     }
> >
> >     // Start from the cursor position, or (0, 0)
> >     var x0 = 0, y0 = 0;
> >     if (this.__path.length) {
> >         var instr = this.__path[this.__path.length - 1];
> >         x0 = instr[instr.length - 2];
> >         y0 = instr[instr.length - 1];
> >     }
> >     // The algorithm used is to recursively subdivide the cubic  
> until
> >     // it's close enough to a quadratic, and then draw that.
> >     // The code below has the effect of
> >     //   function draw_cubic(cubic) {
> >     //     if (|midpoint(cubic)-midpoint(quadratic)| < error)
> >     //       draw_quadratic(qudratic);
> >     //     else
> >     //       map(draw_cubic, subdivide(cubic));
> >     //   }
> >     // where the recursion has been replaced by an explicit
> >     // work item queue.
> >
> >     // To avoid recursion and undue temporary structure, the  
> following
> >     // loop has a funny control flow.  Each iteration either pops
> >     // the next work item from queue, or creates two new work items
> >     // and pushes one to the queue while setting +points+ to the  
> other one.
> >     // The loop effectively exits from the *middle*, when the next
> >     // work item is null.  (This continues to the loop test,
> >     // which then exits.)
> >
> >     // each item is a list of control points, with a sentinel of  
> null
> >     var work_items = [null];
> >     // the current work item
> >     var points = [{x: x0, y: y0}, {x: cp1x, y: cp1y}, {x: cp2x,  
> y: cp2y}, {x:
> x, y: y}];
> >     while (points) {
> >         // Compute the triangle, since the fringe is the subdivision
> >         // if we need that and the peak is the midpoint which we  
> need
> >         // in any case
> >         var m = [points, [], [], []];
> >         for (var i = 1; i < 4; i++) {
> >             for (var j = 0; j < 4 - i; j++) {
> >                 var c0 = m[i-1][j];
> >                 var c1 = m[i-1][j+1];
> >                 m[i][j] = {x: (c0.x + c1.x)/2,
> >                            y: (c0.y + c1.y)/2};
> >             }
> >         }
> >         // Posit a quadratic.  For C1 continuity, control point  
> has to
> >         // be at the intersection of the tangents.
> >         var q1 = intersection.apply(null, points);
> >         var q0 = points[0];
> >         var q2 = points[3];
> >         if (!q1) {
> >             _root.Debug.write('a line');
> >             // It's really a line.
> >             this.lineTo(q2.x, q2.y);
> >             points = work_items.pop();
> >             continue;
> >         }
> >         var qa = midpoint(q0, q1);
> >         var qb = midpoint(q1, q2);
> >         var qm = midpoint(qa, qb);
> >         // Is the midpoint of the quadratic close to the midpoint of
> >         // the cubic?  If so, use it as the approximation.
> >         if (distance(qm, m[3][0]) < error) {
> >             this.quadraticCurveTo(q1.x, q1.y, q2.x, q2.y);
> >             points = work_items.pop();
> >             continue;
> >         }
> >         // Otherwise subdivide the cubic.  The first division is the
> >         // next work item, and the second goes on the work queue.
> >         var left = new Array(4), right = new Array(4);
> >         for (i = 0; i < 4; i++) {
> >             left[i]  = m[i][0];
> >             right[i] = m[3-i][i];
> >         }
> >         points = left;
> >         work_items.push(right);
> >     }
> > };
> > LzDrawView.prototype.bezierCurveTo.error = 10;
> >
> ==== //depot/lps-dev/test/drawing/drawing.lzx#5 - c:\Laszlo\lps-dev 
> \test\drawing
> \drawing.lzx ====
> 87a88,101
> > <!-- test new bezierCurveTo method  - from http:// 
> developer.mozilla.org/sample
> s/canvas-tutorial/2_6_canvas_beziercurveto.html -->
> >     <drawview>
> >         <method event="oninit">
> >             this.beginPath();
> >             this.moveTo(75,40);
> >             this.bezierCurveTo(75,37,70,25,50,25);
> >             this.bezierCurveTo(20,25,20,62.5,20,62.5);
> >             this.bezierCurveTo(20,80,40,102,75,120);
> >             this.bezierCurveTo(110,102,130,80,130,62.5);
> >             this.bezierCurveTo(130,62.5,130,25,100,25);
> >             this.bezierCurveTo(85,25,75,37,75,40);
> >             this.fill();
> >         </method>
> >     </drawview>
> <changeset-40515.zip>

_______________________________________________
Laszlo-dev mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-dev

Reply via email to