ScriptUi custom shape button with OnDraw

144 views Asked by At

I wrote a script with a dialog with custom round buttons (something like the official adobe ones).
Being ignorant of mathematics, I drew the button with straight lines, like this:

    ButtonPanel ();
    function ButtonPanel () {
        var Panel = new Window ("dialog");
        Panel.text = "Panel";
        var Button = Panel.add ("button");
        Button.text = "Exit";
        Button.preferredSize.width = 170;
        Button.onClick = function () {
                Panel.close ();
            }
        Draw (Button);
        Panel.show ();
        function Draw (Obj) {
            if (Obj.type == "button") {
                Obj.graphics.foregroundColor = Obj.graphics.newPen (Obj.graphics.PenType.SOLID_COLOR, [1, 1, 1], 1);
                Obj.graphics.font = ScriptUI.newFont (Obj.graphics.font.name, "Bold", Obj.graphics.font.size);
                Obj.onDraw = function (Event) {
                    with (Obj) {
                        graphics.drawOSControl ();
                        graphics.newPath ();
                        graphics.moveTo ((size[0] / 340) * 25, (size[1] / 50) * 0);
                        graphics.lineTo ((size[0] / 340) * 17.73, (size[1] / 50) * 1.06);
                        graphics.lineTo ((size[0] / 340) * 11.98, (size[1] / 50) * 3.65);
                        graphics.lineTo ((size[0] / 340) * 7.32, (size[1] / 50) * 7.32);
                        graphics.lineTo ((size[0] / 340) * 3.65, (size[1] / 50) * 11.98);
                        graphics.lineTo ((size[0] / 340) * 1.06, (size[1] / 50) * 17.73);
                        graphics.lineTo ((size[0] / 340) * 0, (size[1] / 50) * 25);
                        graphics.lineTo ((size[0] / 340) * 1.06, (size[1] / 50) * 32.26);
                        graphics.lineTo ((size[0] / 340) * 3.65, (size[1] / 50) * 38.02);
                        graphics.lineTo ((size[0] / 340) * 7.32, (size[1] / 50) * 42.68);
                        graphics.lineTo ((size[0] / 340) * 11.98, (size[1] / 50) * 46.35);
                        graphics.lineTo ((size[0] / 340) * 17.73, (size[1] / 50) * 48.93);
                        graphics.lineTo ((size[0] / 340) * 25, (size[1] / 50) * 50);
                        graphics.lineTo ((size[0] / 340) * 315, (size[1] / 50) * 50);
                        graphics.lineTo ((size[0] / 340) * 322.25, (size[1] / 50) * 48.93);
                        graphics.lineTo ((size[0] / 340) * 328, (size[1] / 50) * 46.35);
                        graphics.lineTo ((size[0] / 340) * 332.66, (size[1] / 50) * 42.68);
                        graphics.lineTo ((size[0] / 340) * 336.34, (size[1] / 50) * 38.02);
                        graphics.lineTo ((size[0] / 340) * 338.92, (size[1] / 50) * 32.26);
                        graphics.lineTo ((size[0] / 340) * 340, (size[1] / 50) * 25);
                        graphics.lineTo ((size[0] / 340) * 338.92, (size[1] / 50) * 17.73);
                        graphics.lineTo ((size[0] / 340) * 336.34, (size[1] / 50) * 11.98);
                        graphics.lineTo ((size[0] / 340) * 332.66, (size[1] / 50) * 7.32);
                        graphics.lineTo ((size[0] / 340) * 328, (size[1] / 50) * 3.65);
                        graphics.lineTo ((size[0] / 340) * 322.25, (size[1] / 50) * 1.06);
                        graphics.lineTo ((size[0] / 340) * 315, (size[1] / 50) * 0);
                        graphics.lineTo ((size[0] / 340) * 25, (size[1] / 50) * 0);
                        graphics.closePath ();
                        graphics.fillPath (graphics.newBrush (graphics.BrushType.SOLID_COLOR, [1, 0, 0]));
                        graphics.closePath ();
                        if (text) {
                            graphics.drawString (text, (graphics.newPen (graphics.PenType.SOLID_COLOR, [1, 1, 1], 1)), (size[0] - graphics.measureString (text, graphics.font, size[0])[0]) / 2, (size[1] - graphics.measureString (text, graphics.font, size[1])[1]) / 2, graphics.font);
                            }
                        if (Event.mouseOver) {
                            graphics.fillPath (graphics.newBrush (graphics.BrushType.SOLID_COLOR, [1, 1, 1]));
                            if (text) {
                                graphics.drawString (text, (graphics.newPen (graphics.PenType.SOLID_COLOR, [1, 0, 0], 1)), (size[0] - graphics.measureString (text, graphics.font, size[0])[0]) / 2, (size[1] - graphics.measureString (text, graphics.font, size[1])[1]) / 2, graphics.font);
                                }
                            }
                        }
                    }
                }
            }
        }

However I have seen on this site that it is possible to draw the various shapes with a geometric formulas.
Does anyone know how the half circles on the sides of the button could be drawn with "geometric calculations"?


UPDATE

After messing with the stib's code and the linked site's one, I came up with this final script:

    ButtonPanel ();
    function ButtonPanel () {
        var Panel = new Window ("dialog");
        Panel.text = "Panel";
        var Button = Panel.add ("button");
        Button.text = "Exit";
        Button.preferredSize.width = 170;
        Button.onClick = function () {
                Panel.close ();
            }
        Draw (Button);
        Panel.show ();
        function Draw (Obj) {
            if (Obj.type == "button") {
                Obj.graphics.foregroundColor = Obj.graphics.newPen (Obj.graphics.PenType.SOLID_COLOR, [1, 1, 1], 1);
                Obj.graphics.font = ScriptUI.newFont (Obj.graphics.font.name, "Bold", Obj.graphics.font.size);
                Obj.onDraw = function (Event) {
                    with (Obj) {
                        graphics.drawOSControl ();
                        graphics.newPath ();
                        graphics.moveTo (12.5, 0);
                        for (var i = 0; i < Math.PI; i += Math.PI / 100) {
                            graphics.lineTo ((-12.5 * Math.sin (i)) + 12.5, (-12.5 * Math.cos (i)) + 12.5);
                            }
                            graphics.lineTo (157.5, 25);
                        for (var i = 0; i < Math.PI; i += Math.PI / 100) {
                            graphics.lineTo ((12.5 * Math.sin (i)) + 157.5, (12.5 * Math.cos (i)) + 12.5);
                            }
                        graphics.lineTo (12.5, 0);
                        graphics.closePath ();
                        graphics.fillPath (graphics.newBrush (graphics.BrushType.SOLID_COLOR, [1, 0, 0]));
                        if (text) {
                            graphics.drawString (text, (graphics.newPen (graphics.PenType.SOLID_COLOR, [1, 1, 1], 1)), (size[0] - graphics.measureString (text, graphics.font, size[0])[0]) / 2, (size[1] - graphics.measureString (text, graphics.font, size[1])[1]) / 2, graphics.font);
                            }
                        if (Event.mouseOver) {
                            graphics.fillPath (graphics.newBrush (graphics.BrushType.SOLID_COLOR, [1, 1, 1]));
                            if (text) {
                                graphics.drawString (text, (graphics.newPen (graphics.PenType.SOLID_COLOR, [1, 0, 0], 1)), (size[0] - graphics.measureString (text, graphics.font, size[0])[0]) / 2, (size[1] - graphics.measureString (text, graphics.font, size[1])[1]) / 2, graphics.font);
                                }
                            }
                        }
                    }
                }
            }
        }
2

There are 2 answers

3
stib On BEST ANSWER

Oof. You know that loops are a thing right?

The site you link to is just using straight lines but is generating each segment using the formula for the various curves. The formula for a point on the circumference of a circle given the angle θ from the x axis to the point, and the radius r is [ cos(θ), sin(θ) ] * r. You can decide how many segments you want in the half circle and make a loop to call that formula that many times.

    function halfCircle(
        radius, //radius of the half circle
        segStart, //coordinates of the start of the segment
        angleStart, // angle of the start of the segment from the positive x axis
        numSegments //how many segments to draw
    ){

        var circle = {
            x: function(i, r){ return Math.cos(i) * r},
            y: function(i, r){ return Math.cos(i) * r}
        }

        var g = this.graphics;
        g.newPath();
        g.moveTo(segStart);

        var increment = Math.pi / numSegments; //Pi in radians is 180°
        var offset = angleStart * Math.pi / 180;
        for (var i = 0; i < numSegments; i++){
            g.lineTo(
                circle.x(i * increment + offset, radius) + segStart, 
                circle.y(i * increment + offset, radius) + segStart
            );
        }
    }

    halfCircle(340, [123,456], 123, 45);
1
Ghoul Fool On

If you look at the values of size[0] which is 170 and size[1], which is 25 you can rewrite the semi circle code with the exact values:

graphics.lineTo ((size[0] / 340) * 17.73, (size[1] / 50) * 1.06);
graphics.lineTo ((size[0] / 340) * 11.98, (size[1] / 50) * 3.65);
graphics.lineTo ((size[0] / 340) * 7.32, (size[1] / 50) * 7.32);
graphics.lineTo ((size[0] / 340) * 3.65, (size[1] / 50) * 11.98);
graphics.lineTo ((size[0] / 340) * 1.06, (size[1] / 50) * 17.73);
graphics.lineTo ((size[0] / 340) * 0, (size[1] / 50) * 25);
graphics.lineTo ((size[0] / 340) * 1.06, (size[1] / 50) * 32.26);
graphics.lineTo ((size[0] / 340) * 3.65, (size[1] / 50) * 38.02);
graphics.lineTo ((size[0] / 340) * 7.32, (size[1] / 50) * 42.68);
graphics.lineTo ((size[0] / 340) * 11.98, (size[1] / 50) * 46.35);
graphics.lineTo ((size[0] / 340) * 17.73, (size[1] / 50) * 48.93);

or simply

graphics.lineTo (8.865, 0.53);
graphics.lineTo (5.99, 1.825);
graphics.lineTo (3.66, 3.66);
graphics.lineTo (1.825, 5.99);
graphics.lineTo (0.53, 8.865);
graphics.lineTo (0, 12.5);
graphics.lineTo (0.53, 8.865);
graphics.lineTo (1.825, 5.99);
graphics.lineTo (3.66, 3.66);
graphics.lineTo (5.99, 1.825);
graphics.lineTo (8.865, 0.53);

So it's now simply a matter of substituting the x an y values with [ cos(θ), sin(θ) ] * r as stib has shown you in his much better (and quicker) answer.