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 }