1 /* 2 * Copyright (C) 2008, 2009 Mihai Şucan 3 * 4 * This file is part of PaintWeb. 5 * 6 * PaintWeb is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * PaintWeb is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with PaintWeb. If not, see <http://www.gnu.org/licenses/>. 18 * 19 * $URL: http://code.google.com/p/paintweb $ 20 * $Date: 2009-08-24 13:18:05 +0300 $ 21 */ 22 23 /** 24 * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a> 25 * @fileOverview Holds the Bézier curve tool implementation. 26 */ 27 28 /** 29 * @class The Bézier curve tool. 30 * 31 * @param {PaintWeb} app Reference to the main paint application object. 32 */ 33 pwlib.tools.bcurve = function (app) { 34 var _self = this, 35 clearInterval = app.win.clearInterval, 36 config = app.config, 37 context = app.buffer.context, 38 gui = app.gui, 39 image = app.image, 40 mouse = app.mouse, 41 setInterval = app.win.setInterval, 42 snapXY = app.toolSnapXY; 43 44 /** 45 * Holds the points in the Bézier curve being drawn. 46 * 47 * @private 48 * @type Array 49 */ 50 var points = []; 51 52 /** 53 * The interval ID used for invoking the drawing operation every few 54 * milliseconds. 55 * 56 * @private 57 * @see PaintWeb.config.toolDrawDelay 58 */ 59 var timer = null; 60 61 /** 62 * Tells if the <kbd>Shift</kbd> key is down or not. This is used by the 63 * drawing function. 64 * 65 * @private 66 * @type Boolean 67 * @default false 68 */ 69 var shiftKey = false; 70 71 /** 72 * Tells if the drawing canvas needs to be updated or not. 73 * 74 * @private 75 * @type Boolean 76 * @default false 77 */ 78 var needsRedraw = false; 79 80 /** 81 * The tool deactivation method, used for clearing the buffer. 82 */ 83 this.deactivate = function () { 84 if (timer) { 85 clearInterval(timer); 86 timer = null; 87 } 88 89 if (points.length > 0) { 90 context.clearRect(0, 0, image.width, image.height); 91 } 92 93 needsRedraw = false; 94 points = []; 95 96 return true; 97 }; 98 99 /** 100 * The <code>mousedown</code> event handler. 101 * 102 * @param {Event} ev The DOM Event object. 103 */ 104 this.mousedown = function (ev) { 105 if (points.length === 0) { 106 gui.statusShow('bcurveSnapping'); 107 points.push([mouse.x, mouse.y]); 108 } 109 110 if (!timer) { 111 timer = setInterval(_self.draw, config.toolDrawDelay); 112 } 113 114 shiftKey = ev.shiftKey; 115 needsRedraw = false; 116 117 return true; 118 }; 119 120 /** 121 * Store the <kbd>Shift</kbd> key state which is used by the drawing function. 122 * 123 * @param {Event} ev The DOM Event object. 124 */ 125 this.mousemove = function (ev) { 126 shiftKey = ev.shiftKey; 127 needsRedraw = true; 128 }; 129 130 /** 131 * Draw the Bézier curve, using the available points. 132 * 133 * @see PaintWeb.config.toolDrawDelay 134 */ 135 this.draw = function () { 136 if (!needsRedraw) { 137 return; 138 } 139 140 var n = points.length; 141 142 // Add the temporary point while the mouse button is down. 143 if (mouse.buttonDown) { 144 if (shiftKey && n === 1) { 145 snapXY(points[0][0], points[0][1]); 146 } 147 points.push([mouse.x, mouse.y]); 148 n++; 149 } 150 151 var p0 = points[0], 152 p1 = points[1], 153 p2 = points[2], 154 p3 = points[3] || points[2]; 155 156 if (mouse.buttonDown) { 157 points.pop(); 158 } 159 160 context.clearRect(0, 0, image.width, image.height); 161 162 if (!n) { 163 needsRedraw = false; 164 return; 165 } 166 167 // Draw the main line 168 if (n === 2) { 169 context.beginPath(); 170 context.moveTo(p0[0], p0[1]+2); 171 context.lineTo(p1[0], p1[1]+2); 172 173 if (config.shapeType === 'fill') { 174 var lineWidth = context.lineWidth, 175 strokeStyle = context.strokeStyle; 176 177 context.lineWidth = 1; 178 context.strokeStyle = context.fillStyle; 179 } 180 181 context.stroke(); 182 context.closePath(); 183 184 if (config.shapeType === 'fill') { 185 context.lineWidth = lineWidth; 186 context.strokeStyle = strokeStyle; 187 } 188 189 needsRedraw = false; 190 return; 191 } 192 193 // Draw the Bézier curve 194 195 context.beginPath(); 196 context.moveTo(p0[0], p0[1]); 197 context.bezierCurveTo( 198 p2[0], p2[1], 199 p3[0], p3[1], 200 p1[0], p1[1]); 201 202 if (config.shapeType !== 'stroke') { 203 context.fill(); 204 } 205 206 if (config.shapeType !== 'fill') { 207 context.stroke(); 208 } 209 210 context.closePath(); 211 212 needsRedraw = false; 213 }; 214 215 /** 216 * The <code>mouseup</code> event handler. This method stores the current 217 * mouse coordinates as a point to be used for drawing the Bézier curve. 218 * 219 * @param {Event} ev The DOM Event object. 220 */ 221 this.mouseup = function (ev) { 222 var n = points.length; 223 224 // Allow click+mousemove+click, not only mousedown+mousemove+mouseup. 225 // Do this only for the start point. 226 if (n === 1 && mouse.x === points[0][0] && mouse.y === points[0][1]) { 227 mouse.buttonDown = true; 228 return true; 229 } 230 231 if (timer) { 232 clearInterval(timer); 233 timer = null; 234 } 235 236 if (n === 1 && ev.shiftKey) { 237 snapXY(points[0][0], points[0][1]); 238 } 239 240 // We need 4 points to draw the Bézier curve: start, end, and two control 241 // points. 242 if (n < 4) { 243 points.push([mouse.x, mouse.y]); 244 needsRedraw = true; 245 n++; 246 } 247 248 // Make sure the canvas is up-to-date. 249 shiftKey = ev.shiftKey; 250 _self.draw(); 251 252 if (n === 2 || n === 3) { 253 gui.statusShow('bcurveControlPoint' + (n-1)); 254 } else if (n === 4) { 255 gui.statusShow('bcurveActive'); 256 app.layerUpdate(); 257 points = []; 258 } 259 260 return true; 261 }; 262 263 /** 264 * The <code>keydown</code> event handler. This method allows the user to 265 * press the <kbd>Escape</kbd> key to cancel the current drawing operation. 266 * 267 * @param {Event} ev The DOM Event object. 268 * 269 * @returns {Boolean} True if the keyboard shortcut was recognized, or false 270 * if not. 271 */ 272 this.keydown = function (ev) { 273 if (!points.length || ev.kid_ !== 'Escape') { 274 return false; 275 } 276 277 if (timer) { 278 clearInterval(timer); 279 timer = null; 280 } 281 282 context.clearRect(0, 0, image.width, image.height); 283 284 points = []; 285 needsRedraw = false; 286 mouse.buttonDown = false; 287 288 gui.statusShow('bcurveActive'); 289 290 return true; 291 }; 292 }; 293 294 // vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix: 295 296