1 module util.rounded_rectangle;
2 
3 /+
4  +           Copyright Andrej Mitrovic 2011.
5  +  Distributed under the Boost Software License, Version 1.0.
6  +     (See accompanying file LICENSE_1_0.txt or copy at
7  +           http://www.boost.org/LICENSE_1_0.txt)
8  +/
9 
10 import cairo.cairo;
11 
12 import std.math;
13 import std.algorithm;
14 import std.stdio;
15 
16 enum RoundMethod
17 {
18     A,
19     B,
20     C,
21     D,
22 }
23 
24 /+ @BUG@ 6574 - Erroneous recursive call: http://d.puremagic.com/issues/show_bug.cgi?id=6574
25 void roundedRectangle(RoundMethod roundMethod = RoundMethod.A)(Context ctx, Rectangle!double rect, double radius1 = 5, double radius2 = 5)
26 {
27     with (rect)
28     {
29         roundedRectangle!(roundMethod)(ctx, point.x, point.y, width, height, radius1, radius2);
30     }
31 }
32 
33 void roundedRectangle(RoundMethod roundMethod = RoundMethod.A)(Context ctx, double x, double y, double width, double height, double radius1 = 5, double radius2 = 5)
34 {
35     roundedRectangle!(roundMethod)(ctx, x, y, width, height, radius1, radius2);
36 }
37 +/
38 
39 // convenience
40 void roundedRectangle(RoundMethod roundMethod)(Context ctx, Rectangle!double rect, double radius1 = 5, double radius2 = 5)
41 {
42     with (rect)
43     {
44         roundedRectangle!(roundMethod)(ctx, point.x, point.y, width, height, radius1, radius2);
45     }
46 }
47 
48 void roundedRectangle(RoundMethod roundMethod : RoundMethod.A)(Context ctx, double x, double y, double width, double height, double radiusX = 5, double radiusY = 5)
49 {
50     enum ArcToBezier = 0.55228475;
51 
52     if (radiusX > width - radiusX)
53         radiusX = width / 2;
54 
55     if (radiusY > height - radiusY)
56         radiusY = height / 2;
57 
58     // approximate the arc using a bezier curve
59     auto c1 = ArcToBezier * radiusX;
60     auto c2 = ArcToBezier * radiusY;
61 
62     ctx.newPath();
63     ctx.moveTo(x + radiusX, y);
64     ctx.relLineTo(width - 2 * radiusX, 0.0);
65     ctx.relCurveTo(c1, 0.0, radiusX, c2, radiusX, radiusY);
66     ctx.relLineTo(0, height - 2 * radiusY);
67     ctx.relCurveTo(0.0, c2, c1 - radiusX, radiusY, -radiusX, radiusY);
68     ctx.relLineTo(-width + 2 * radiusX, 0);
69     ctx.relCurveTo(-c1, 0, -radiusX, -c2, -radiusX, -radiusY);
70     ctx.relLineTo(0, -height + 2 * radiusY);
71     ctx.relCurveTo(0.0, -c2, radiusX - c1, -radiusY, radiusX, -radiusY);
72     ctx.closePath();
73 }
74 
75 void roundedRectangle(RoundMethod roundMethod:RoundMethod.B)(Context ctx, double x, double y, double width, double height, double radius = 5, double unused = 0)
76 {
77     // a custom shape, that could be wrapped in a function
78     // radius = 5; and an approximate curvature radius
79     immutable x0 = x + radius / 2.0;  // parameters like cairo_rectangle
80     immutable y0 = y + radius / 2.0;
81     immutable rectWidth  = width - radius;
82     immutable rectHeight = height - radius;
83 
84     ctx.save();
85     ctx.newPath();
86     // ctx.set_line_width (0.04)
87     // self.snippet_normalize (cr, width, height)
88 
89     immutable x1 = x0 + rectWidth;
90     immutable y1 = y0 + rectHeight;
91 
92     // if (!rectWidth || !rectHeight)
93     //     return
94 
95     if (rectWidth / 2 < radius)
96     {
97         if (rectHeight / 2 < radius)
98         {
99             ctx.moveTo(x0, (y0 + y1) / 2);
100             ctx.curveTo(x0, y0, x0, y0, (x0 + x1) / 2, y0);
101             ctx.curveTo(x1, y0, x1, y0, x1, (y0 + y1) / 2);
102             ctx.curveTo(x1, y1, x1, y1, (x1 + x0) / 2, y1);
103             ctx.curveTo(x0, y1, x0, y1, x0, (y0 + y1) / 2);
104         }
105         else
106         {
107             ctx.moveTo(x0, y0 + radius);
108             ctx.curveTo(x0, y0, x0, y0, (x0 + x1) / 2, y0);
109             ctx.curveTo(x1, y0, x1, y0, x1, y0 + radius);
110             ctx.lineTo(x1, y1 - radius);
111             ctx.curveTo(x1, y1, x1, y1, (x1 + x0) / 2, y1);
112             ctx.curveTo(x0, y1, x0, y1, x0, y1 - radius);
113         }
114     }
115 
116     else
117     {
118         if (rectHeight / 2 < radius)
119         {
120             ctx.moveTo(x0, (y0 + y1) / 2);
121             ctx.curveTo(x0, y0, x0, y0, x0 + radius, y0);
122             ctx.lineTo(x1 - radius, y0);
123             ctx.curveTo(x1, y0, x1, y0, x1, (y0 + y1) / 2);
124             ctx.curveTo(x1, y1, x1, y1, x1 - radius, y1);
125             ctx.lineTo(x0 + radius, y1);
126             ctx.curveTo(x0, y1, x0, y1, x0, (y0 + y1) / 2);
127         }
128         else
129         {
130             ctx.moveTo(x0, y0 + radius);
131             ctx.curveTo(x0, y0, x0, y0, x0 + radius, y0);
132             ctx.lineTo(x1 - radius, y0);
133             ctx.curveTo(x1, y0, x1, y0, x1, y0 + radius);
134             ctx.lineTo(x1, y1 - radius);
135             ctx.curveTo(x1, y1, x1, y1, x1 - radius, y1);
136             ctx.lineTo(x0 + radius, y1);
137             ctx.curveTo(x0, y1, x0, y1, x0, y1 - radius);
138         }
139     }
140 
141     ctx.closePath();
142     ctx.restore();
143 }
144 
145 void roundedRectangle(RoundMethod roundMethod:RoundMethod.C)(Context ctx, double x, double y, double width, double height, double radius = 10, double unused = 0)
146 {
147     immutable x1 = x + width;
148     immutable y1 = y + height;
149 
150     ctx.save();
151     ctx.moveTo(x + radius, y);                    // Move to A
152     ctx.lineTo(x1 - radius, y);                   // Straight line to B
153     ctx.curveTo(x1, y, x1, y, x1, y + radius);    // Curve to C, Control points are both at Q
154     ctx.lineTo(x1, y1 - radius);                  // Move to D
155     ctx.curveTo(x1, y1, x1, y1, x1 - radius, y1); // Curve to E
156     ctx.lineTo(x + radius, y1);                   // Line to F
157     ctx.curveTo(x, y1, x, y1, x, y1 - radius);    // Curve to G
158     ctx.lineTo(x, y + radius);                    // Line to H
159     ctx.curveTo(x, y, x, y, x + radius, y);       // Curve to A
160     ctx.restore();
161 }
162 
163 void roundedRectangle(RoundMethod roundMethod:RoundMethod.D)(Context ctx, double x, double y, double width, double height, double radius = 10, double unused = 0)
164 {
165     immutable x1 = x + width;
166     immutable y1 = y + height;
167 
168     ctx.save();
169     ctx.newPath();
170     ctx.arc(x + radius, y + radius, radius, 2.0 * (PI / 2.0), 3.0 * (PI / 2.0));
171     ctx.arc(x1 - radius, y + radius, radius, 3.0 * (PI / 2.0), 4.0 * (PI / 2.0));
172     ctx.arc(x1 - radius, y1 - radius, radius, 0 * (PI / 2.0), 1.0 * (PI / 2.0));
173     ctx.arc(x + radius, y1 - radius, radius, 1.0 * (PI / 2.0), 2.0 * (PI / 2.0));
174     ctx.closePath();
175     ctx.restore();
176 }
177 
178 RGB brightness(RGB rgb, double amount)
179 {
180     with (rgb)
181     {
182         if (red > 0)
183             red = max(0, min(1.0, red + amount));
184 
185         if (green > 0)
186             green = max(0, min(1.0, green + amount));
187 
188         if (blue > 0)
189             blue  = max(0, min(1.0, blue  + amount));
190     }
191 
192     return rgb;
193 }