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