3

I have created a circle in which I can choose two points along the circumference of of circle.

I want to fill the portion between those two points.

Demo

If you see the demo, I want to fill the angle between two points.

JS:

(function (Raphael) {
    Raphael.colorwheel = function (x, y, size, initcolor, element) {
        return new ColorWheel(x, y, size, initcolor, element);
    };
    var pi = Math.PI,
        doc = document,
        win = window,
        ColorWheel = function (x, y, size, initcolor, element) {
            size = size || 200;
            var w3 = 3 * size / 200,
                w1 = size / 200,
                fi = 1.6180339887,
                segments = 3,//pi * size / 50,
                size20 = size / 20,
                size2 = size / 2,
                padding = 2 * size / 200,
                t = this;

            var H = 1, S = 1, B = 1, s = size - (size20 * 4);
            var r = element ? Raphael(element, size, size) : Raphael(x, y, size, size),
                xy = s / 6 + size20 * 2 + padding,
                wh = s * 2 / 3 - padding * 2;
            w1 < 1 && (w1 = 1);
            w3 < 1 && (w3 = 1);



            // ring drawing
            var a = pi / 2 - pi * 2 / segments * 1.3,
                R = size2 - padding,
                R2 = size2 - padding - size20 * 2,
                path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", R2 * Math.cos(a) + R + padding, R - R2 * Math.sin(a) + padding, "A", R2, R2, 0, 0, 0, size2, padding + size20 * 2, "z"].join();

            for (var i = 0; i < segments; i++) {
                r.path(path).attr({
                    stroke: "none",
                    fill: "#8fd117",
                    transform: "r" + [(360 / segments) * i, size2, size2]
                });
            }

            r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0", "M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                "stroke-width": w3,
                stroke: "#fff"
            });

            t.startCursor = r.set();
            var h = size20 * 2 + 2;
            t.startCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#00A0C6",
                opacity: .5,
                "stroke-width": w3
            }));
            t.startCursor.push(t.startCursor[0].clone().attr({
                stroke: "#00A0C6",
                opacity: 1,
                "stroke-width": w1
            }));


            t.endCursor = r.set();
            var h = size20 * 2 + 2;
            t.endCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#F96E5B",
                opacity: .5,
                "stroke-width": w3
            }));

            t.endCursor.push(t.endCursor[0].clone().attr({
                stroke: "#F96E5B",
                opacity: 1,
                "stroke-width": w1
            }));



            t.ring = r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                fill: "#000",
                opacity: 0,
                stroke: "none"
            }); 

            t.H = t.S = t.B = 1;
            t.raphael = r;
            t.size2 = size2;
            t.wh = wh;
            t.x = x;
            t.xy = xy;
            t.y = y;


            t.endCursor.attr({transform: "r" + [50, t.size2, t.size2]});

            // events
            t.ring.drag(function (dx, dy, x, y) {
                t.docOnMove(dx, dy, x, y);
            }, function (x, y) { 
                // Rotate on click 
                t.setH(x - t.x - t.size2, y - t.y - t.size2);
            }, function () { 
            }); 
        },
        proto = ColorWheel.prototype;

    proto.setH = function (x, y) { 

        var d = Raphael.angle(x, y, 0, 0); 

        this.H = (d + 90) / 360;
        var a  = 0;

        if(d > 270) {
            d = d - 270;
        }
        else {
            d = d + 90;
        } 

        var m = Math.abs(d - this.startCursor[0]._.deg);
        var n = Math.abs(d - this.endCursor[0]._.deg);

        if(m > 180) {
            m = 360 - m ;
        }
        if(n > 180) {
            n = 360 - n;
        }    

        if( m <= n) {
            this.startCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }
        else {
            this.endCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }

        m = this.startCursor[0]._.deg ;
        n = this.endCursor[0]._.deg;

        if(m > 360) {
            m = m - 360;
        }
        if( n > 360 ) {
            n = n - 360;
        } 

        var diff = m > n ? m - n : n - m;

        this.onchange(m,n,diff);
    };

    proto.docOnMove = function (dx, dy, x, y) {
        this.setH(x - this.x - this.size2, y - this.y - this.size2);

    };

})(window.Raphael);



window.onload = function () {
    var cp2 = Raphael.colorwheel(60, 20, 200, "#eee");
    var X = document.getElementById('x');
    var Y = document.getElementById('y');
    var angle = document.getElementById('angle');

    cp2.onchange = function (x, y, ang) {
        X.innerHTML = Math.round(x * 100) / 100;
        Y.innerHTML = Math.round(y * 100) / 100;
        angle.innerHTML = Math.round(ang * 100) / 100;
    }
};

HTML:

<div id="wrapper">X : <span id="x">0</span>
    <br>Y: <span id="y">50</span> 
    <br>Angle: <span id="angle">50</span> 
</div>

CSS:

  body {
      background: #e6e6e6;
  }
  #wrapper {
      position: absolute;
      top: 240px;
      left: 100px;
  }

UPDATE:

With Chris's help,

I have got some success.

See Demo

Bugs :
1. If you start green first, red breaks,
2. If you start red first and makes angle of greater than 180 degree and when green reduces that below 180 degree, it breaks again.

UPDATE 2
DEMO
BUGS:
1. If you start red first and makes angle of greater than 180 degree and when green reduces that below 180 degree, it breaks again.
2. Sometimes arcs in opposite direction.

Jashwant
  • 28,410
  • 16
  • 70
  • 105

2 Answers2

4

Cool project. You just need to add an elliptical arc to the color wheel and redraw the path on the onchange event.

I got you half the way here: It works if you move the orange cursor, completely breaks if you move the blue cursor.

To start:

        t.x0 = t.startCursor[0].attr("x") + t.startCursor[0].attr("width") / 2;
        t.y0 = t.startCursor[0].attr("y") + t.startCursor[0].attr("height") / 2;
        t.R1 = (R2 + R) / 2;
        t.x1 = t.x0 + t.R1 * Math.sin(50 * Math.PI / 180);
        t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(50 * Math.PI / 180);

        t.arc = r.path("M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " 50 0,1 " + t.x1 + "," + t.y1)
        .attr({
            stroke: "#009900",
            "stroke-width": 10
        });

On update:

    if (n > 180) {
        flag = 1;
    }

    var diff = m > n ? m - n : n - m;

    t.x0 = t.x0 + t.R1 * Math.sin(m * Math.PI / 180);
    t.y0 = t.y0 + t.R1 - t.R1 * Math.cos(m * Math.PI / 180);
    t.x1 = t.x0 + t.R1 * Math.sin(diff * Math.PI / 180);
    t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(diff * Math.PI / 180);

    t.arc = t.arc.attr("path", "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " " +  diff + " " +  flag + ",1 " + t.x1 + "," + t.y1);

jsfiddle

Should be able to take it from here.

UPDATE, May 8:

You can fix your first problem by changing the flag on the diff, not on the second angle:

    if (diff > 180) { 
        flag = 1;
    }

The event that's triggering the second problem is the second angle (the red handle) passing the 0-degree mark. The easiest way to catch this is just to add 360 to the angle IF it's less than the first angle:

    var m = this.startCursor[0]._.deg ;
    var n = this.endCursor[0]._.deg;
    var t = this;        
    var flag = 0;
    var sweep = 1; 

    var path = "";
    if (n < m) {
       m += 360;
    } 

    var diff = Math.abs(m - n); 

    if (diff > 180) { 
        flag = 1;
    }

Here's the fiddle

Note: You were catching situations where (n > 360) and (m > 360), but this doesn't appear necessary -- the angles arrive at this point in the code already set below 360, at least in Chrome.

Chris Wilson
  • 6,599
  • 8
  • 35
  • 71
  • wow, great attempt. I'll try to take it from here. But this was my fist time with raphael and it will be quite difficult for me to complete this. But +1 – Jashwant May 05 '13 at 18:44
  • You just need to calculate the position of the first cursor and make sure you set the flags of the elliptical arc correctly. Trial and error should do the trick. – Chris Wilson May 05 '13 at 21:07
  • I have simplified a lot of code. Can you point out what is it not working ? http://jsfiddle.net/jashwant/CDBk6/4/ – Jashwant May 08 '13 at 12:55
  • When I make this, X : 314.08 Y: 331.89 and move 'red' it breaks – Jashwant May 08 '13 at 17:18
  • You'll have to play with the flags for every possible combination of signs for m and n--I can don't every last bit for you -- but the structure I've set up should work just fine. Would appreciate an accept. – Chris Wilson May 08 '13 at 17:36
  • Believe me I have played with it for so many hours. But I do not have previous experience with Raphael. Thats the problem. Thanks for your efforts. This question has bounty of +50, its worth try though. – Jashwant May 08 '13 at 17:39
4

Here's working solution:

Demo

(function (Raphael) {
    Raphael.colorwheel = function (x, y, size, initcolor, element) {
        return new ColorWheel(x, y, size, initcolor, element);
    };
    var pi = Math.PI,
        doc = document,
        win = window,
        ColorWheel = function (x, y, size, initcolor, element) {
            size = size || 200;
            var w3 = 3 * size / 200,
                w1 = size / 200,
                fi = 1.6180339887,
                segments = 3,//pi * size / 50,
                size20 = size / 20,
                size2 = size / 2,
                padding = 2 * size / 200,
                t = this;

            var H = 1, S = 1, B = 1, s = size - (size20 * 4);
            var r = element ? Raphael(element, size, size) : Raphael(x, y, size, size),
                xy = s / 6 + size20 * 2 + padding,
                wh = s * 2 / 3 - padding * 2;
            w1 < 1 && (w1 = 1);
            w3 < 1 && (w3 = 1);

            // ring drawing
            var a = pi / 2 - pi * 2 / segments * 1.3,
                R = size2 - padding,
                R2 = size2 - padding - size20 * 2,
                path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", R2 * Math.cos(a) + R + padding, R - R2 * Math.sin(a) + padding, "A", R2, R2, 0, 0, 0, size2, padding + size20 * 2, "z"].join();
            for (var i = 0; i < segments; i++) {
                r.path(path).attr({
                    stroke: "none",
                    fill: "#8fd117",
                    transform: "r" + [(360 / segments) * i, size2, size2]
                });
            }

            r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0", "M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                "stroke-width": w3,
                stroke: "#fff"
            });


            t.startCursor = r.set();
            var h = size20 * 2 + 2;

            t.startCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#00A0C6",
                opacity: 1,
                "stroke-width": w3
            }));
            t.startCursor.push(t.startCursor[0].clone().attr({
                stroke: "#00A0C6",
                fill : "#8fd117", 
                opacity: 1,
                "stroke-width": w1
            }));

            t.endCursor = r.set();
            var h = size20 * 2 + 2;

            t.endCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#F96E5B",
                opacity: 1,
                "stroke-width": w3,

            }));

            t.endCursor.push(t.endCursor[0].clone().attr({
                stroke: "#F96E5B",
                fill : "#8fd117",
                opacity: 1,
                "stroke-width": w1
            }));

            t.ring = r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                fill: "#000",
                opacity: 0,
                stroke: "none"
            }); 

            t.H = t.S = t.B = 1;
            t.raphael = r;
            t.size2 = size2;
            t.wh = wh;
            t.x = x;
            t.xy = xy;
            t.y = y;

            t.endCursor.attr({transform: "r" + [50, t.size2, t.size2]});

            t.x0 = t.startCursor[0].attr("x") + t.startCursor[0].attr("width") / 2;
            t.y0 = t.startCursor[0].attr("y") + t.startCursor[0].attr("height") / 2;
            t.initX0 = t.x0;
            t.initY0 = t.y0;
            t.R1 = (R2 + R) / 2;
            t.x1 = t.x0 + t.R1 * Math.sin(50 * Math.PI / 180);
            t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(50 * Math.PI / 180);
            t.initX1 = t.x1;
            t.initY1 = t.y1;
            var path = "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " 50 0,1 " + t.x1 + "," + t.y1;

            t.arc = r.path(path)
            .attr({
                stroke: "#009900",
                "stroke-width": 10
            });


            t.startCursor.drag(function (dx, dy, x, y) {
                   t.docOnMove(dx, dy, x, y,'startCursor');
                }, function (x, y) { 
                    t.setH(x - t.x - t.size2, y - t.y - t.size2,'startCursor');
                }, function () { 

            }); 

            t.endCursor.drag(function (dx, dy, x, y) {
                   t.docOnMove(dx, dy, x, y,'endCursor');
                }, function (x, y) { 
                    t.setH(x - t.x - t.size2, y - t.y - t.size2,'endCursor');
                }, function () { 

            }); 

            t.startCursor.toFront();
            t.endCursor.toFront();
        },
        proto = ColorWheel.prototype;

    proto.setH = function (x, y,cursor) {  
        var d = Raphael.angle(x, y, 0, 0);
        if(d > 270) {
            d = d - 270;
        }
        else {
            d = d + 90;
        } 

        if((cursor === 'startCursor' && d > this.endCursor[0]._.deg) || (cursor === 'endCursor' && d <= this.startCursor[0]._.deg)) {
            return;
        }

        if(cursor === 'startCursor') {
            this.startCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }
        else {
            this.endCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }   

        var m = this.startCursor[0]._.deg ;
        var n = this.endCursor[0]._.deg;
        var t = this;        
        var flag = 0;

        if(m > 360) {
            m = m - 360;
            flag = 1;  
        }

        if( n > 360 ) {
            n = n - 360;
        } 


        var diff = Math.abs(m - n); 

        if (diff > 180) { 
            flag = 1;
        }

        var path = "";
        var sweep = 1; 

        if(cursor === 'endCursor') { 
            t.x1 = t.initX0 + t.R1 * Math.sin(n * Math.PI / 180);
            t.y1 = t.initY0 + t.R1 - t.R1 * Math.cos(n * Math.PI / 180);
        }
        else { 
            t.x0 = t.initX0 + t.R1 * Math.sin(m * Math.PI / 180);
            t.y0 = t.initY0 + t.R1 - t.R1 * Math.cos(m * Math.PI / 180);
        }

        console.log(m,t.x0,t.y0,t.x1,t.y1);

        path = "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " " +  diff + " " +  flag + "," + sweep + " " + t.x1 + "," + t.y1;

        t.arc = t.arc.attr("path", path );

        this.onchange(m,n,diff);
    };

    proto.docOnMove = function (dx, dy, x, y,cursor) {
        this.setH(x - this.x - this.size2, y - this.y - this.size2,cursor);

    };

})(window.Raphael);

window.onload = function () {
    var cp2 = Raphael.colorwheel(60, 20, 200, "#eee");
    var X = document.getElementById('x');
    var Y = document.getElementById('y');
    var angle = document.getElementById('angle');

    cp2.onchange = function (x, y, ang) {
        X.innerHTML = Math.round(x * 100) / 100;
        Y.innerHTML = Math.round(y * 100) / 100;
        angle.innerHTML = Math.round(ang * 100) / 100;


    }
};
Jashwant
  • 28,410
  • 16
  • 70
  • 105