5

I managed to draw four different curves with the examples of the Raphael library. Now, I would like to create a single curve with multiple handles in it. How do I add more handles in this example.

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Bezier curve</title>
            <style>
        #holder {
            height: 100%;
            left: 100%;
            margin: -100% 0 0 -100%;
            position: absolute;
            top: 100%;
            width: 100%;
        }
        </style>
        <script src='jquery.js'></script>
        <script src="raphael.js"></script>
        <script>
           $('document').ready(function () {
                var r = Raphael("holder", window.innerWidth, window.innerHeight)

                function curve(x, y, ax, ay, bx, by, zx, zy, color) {
                    var path = [["M", x, y], ["C", ax, ay, bx, by, zx, zy]],
                        path2 = [["M", x, y], ["L", ax, ay], ["M", bx, by], ["L", zx, zy]],
                        curve = r.path(path).attr({stroke: color || Raphael.getColor(), "stroke-width": 4, "stroke-linecap": "round"}),
                        controls = r.set(
                            r.path(path2).attr({stroke: "#ccc", "stroke-dasharray": ". ","stroke-width":2}),
                            r.circle(x, y, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(ax, ay, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(bx, by, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(zx, zy, 5).attr({fill: "#9F2200", stroke: "none"})
                        );
                    controls[1].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[0][1] = X;
                        path[0][2] = Y;
                        path2[0][2] = X;
                        path2[0][2] = Y;
                        controls[2].update(x, y);
                    };
                    controls[2].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][3] = X;
                        path[1][2] = Y;
                        path2[1][4] = X;
                        path2[1][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[3].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][3] = X;
                        path[1][4] = Y;
                        path2[2][5] = X;
                        path2[2][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[4].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][5] = X;
                        path[1][6] = Y;
                        path2[3][6] = X;
                        path2[3][2] = Y;
                        controls[3].update(x, y);
                    };
                    controls.drag(move, up);
                }
                function move(dx, dy) {
                    this.update(dx - (this.dx || 0), dy - (this.dy || 0));
                    console.log(this.dx,this.dy);
                    this.dx = dx;
                    this.dy = dy;
                }
                function up() {
                    this.dx = this.dy = 0;
                }
                curve(70, 100, 110, 100, 130, 200, 170, 200, "hsb(0, 0, 0)");
                 curve(800, 200, 800, 100, 600, 100, 600, 200, "hsb(0, 0, 0)");  // xp1,yp1,  , , , , xp2,yp2 where (xp1,xp2) & (xp2,yp2) are two end points 

                  curve(500, 200,500, 300, 300, 300, 300, 200, "hsb(0, 0, 0)");  // xp1,yp1,  , , , , xp2,yp2 where (xp1,xp2) & (xp2,yp2) are two end points 

                    curve(920, 100,880, 100, 1020, 200, 980, 200, "hsb(0, 0, 0)"); 

            });
        </script>
    </head>
    <body>
        <div id="holder"></div>
    </body>
</html>

</body>

The link for demo is http://jsfiddle.net/insane36/fddGJ/1/

I edited the code and again tried to put multiple handle to show the main handle in the middle but has some problem and I dont know if I understood the concept behind it. I wanted to create a figure with handle as figure below and be able to manipulate the handles;

enter image description here

The code for three handles is ;

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Bezier curve</title>
            <style>
        #holder {
            height: 100%;
            left: 100%;
            margin: -100% 0 0 -100%;
            position: absolute;
            top: 100%;
            width: 100%;
        }
        </style>
        <script src="raphael.js"></script>
        <script>
           window.onload=function () {
                var r = Raphael("holder", window.innerWidth, window.innerHeight)

                function curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2,cx3,cy3,cx4,cy4, color) {  //zx --x1
                    var path = [["M", x1, y1], ["C", cx1, cy1, cx2, cy2, x2, y2,"S",cx3,cy3,cx4,cy4]],
                        path2 = [["M", x1, y1], ["L", cx1, cy1], ["M", cx2, cy2], ["L", x2, y2],["M", cx3,cy3],['L',cx4,cy4]],
                        curve = r.path(path).attr({stroke: color || Raphael.getColor(), "stroke-width": 4, "stroke-linecap": "round"}),
                        controls = r.set(
                            r.path(path2).attr({stroke: "#ccc", "stroke-dasharray": ". ","stroke-width":2}),
                            r.circle(x1, y1, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(cx1, cy1, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(cx2, cy2, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(x2, y2, 5).attr({fill: "#9F2200", stroke: "none"}),
                             r.circle(cx3, cy3, 5).attr({fill: "#9F2200", stroke: "none"}),
                              r.circle(cx4, cy4, 5).attr({fill: "#9F2200", stroke: "none"})

                        );
                    controls[1].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[0][9] = X;
                        path[0][2] = Y;
                        path2[0][10] = X;
                        path2[0][2] = Y;
                        controls[2].update(x, y);
                    };
                    controls[2].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][11] = X;
                        path[1][2] = Y;
                        path2[1][12] = X;
                        path2[1][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[3].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][3] = X;
                        path[1][4] = Y;
                        path2[2][13] = X;
                        path2[2][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[4].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][5] = X;
                        path[1][6] = Y;
                        path2[3][14] = X;
                        path2[3][2] = Y;
                        controls[3].update(x, y);
                    };
                    controls[5].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][8] = X;
                        path[1][9] = Y;
                        path2[4][15] = X;
                        path2[4][2] = Y;
                        controls[4].update(x, y);
                    };
                      controls[6].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][10] = X;
                        path[1][11] = Y;
                        path2[5][16] = X;
                        path2[5][2] = Y;
                        controls[5].update(x, y);
                    };

                    controls.drag(move, up);
                }
                function move(dx, dy) {
                    this.update(dx - (this.dx || 0), dy - (this.dy || 0));
                    console.log(this.dx,this.dy);
                    this.dx = dx;
                    this.dy = dy;
                }
                function up() {
                    this.dx = this.dy = 0;
                }
                curve(10, 80, 40, 10, 65,10,150,150,95, 80,  180,180, "hsb(0, 0, 0)");

            };
        </script>
    </head>
    <body>
        <div id="holder"></div>
    </body>
</html>

</body>
</html>

I think I have missed and not arranged properly the control points and the values

Sandeep
  • 20,908
  • 7
  • 66
  • 106
  • 1
    I did something similar to what you're trying to do here: http://type.method.ac, but I can't tell you what's wrong with your code just by looking at it. I didn't use the "S" node in my paths, but might have been my own ignorance. – methodofaction Dec 08 '11 at 07:16
  • did you use bezier curve for this operation ?? I don't see the curves. – Sandeep Dec 08 '11 at 21:28
  • 1
    The letters are in a separate file http://shape.method.ac/js/letters.js – methodofaction Dec 09 '11 at 05:02

1 Answers1

1

Take a look at this fiddle - I think I have it doing what you are looking for. (Edit: fixed this fiddle so you don't have to specify the reflected control point in the curve() constructor)

I think the key is that the second control point of the middle point on the curve is just a reflection of the first control point (per the SVG documentation), so you have to sort of 'fake' that control. (Your code did have some issues where your update() functions were trying to update array values that don't exist, like path[1][6] = Y; ... path[1] only has three elements)

If you want the two control points to behave independently (so that the curve would not necessarily be smooth through that point), I think you have to remove the "S" from your path, and change some of the code (here is one like that)

If you want the two control points to be allowed to move different distances from the point, but keep the curve smooth through the point, I think you have to do that by hand. You could start with the second example, but you would have to programatically reflect the angle of the moving control point to the opposite control point, while allowing the distance from the opposite control point to the point on the curve to remain fixed.

Mike C
  • 3,077
  • 22
  • 29
  • Thank you, it looks great but I have a question. Which are the variables for control points in middle. – Sandeep Dec 10 '11 at 20:07
  • The first control point is cx2, cy2 and the second one is cx2b, cy2b. In the first case, cx2b, cy2b is calculated in the constructor (ie., by reflecting the first control point). In the second case, cx2b and cy2b are passed in to the constructor. – Mike C Dec 10 '11 at 20:16
  • What if I would want to create such path dynamically, wherever the user clicks I would like to generate a small curve and when the other point is clicked it would be associated with the curve already drawn and make handle and new curve. – Sandeep Dec 10 '11 at 20:18
  • How do you know that x2, y2 lies on the line or how did you calculate it ? – Sandeep Dec 10 '11 at 20:24
  • I think the way most drawing programs handle this is that after you click the second point on the curve, the mouse position then switches to the control point. So I guess you could get the mouse position, and pass that into the constructor, then continue to call control[x].update() until the mouse is clicked. Another possible alternative is to have a default offset from the point on the curve to the control point, and then let the user change it after it is created. – Mike C Dec 10 '11 at 20:31
  • Regarding x2,y2 - if you look at the findReflected() function, you will see that the calculated position of cx2b, cy2b is basically calculated to be on the line through (cx2,cy2),(x2,y2) – Mike C Dec 10 '11 at 20:35