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-06-11 20:28:07 +0300 $ 21 */ 22 23 /** 24 * @author <a lang="ro" href="http://www.robodesign.ro/mihai">Mihai Şucan</a> 25 * @fileOverview Holds the polygon tool implementation. 26 */ 27 28 /** 29 * @class The polygon tool. 30 * 31 * @param {PaintWeb} app Reference to the main paint application object. 32 */ 33 pwlib.tools.polygon = 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 MathAbs = Math.abs, 41 mouse = app.mouse, 42 setInterval = app.win.setInterval, 43 snapXY = app.toolSnapXY; 44 45 /** 46 * Holds the points in the polygon being drawn. 47 * 48 * @private 49 * @type Array 50 */ 51 var points = []; 52 53 /** 54 * The interval ID used for invoking the drawing operation every few 55 * milliseconds. 56 * 57 * @private 58 * @see PaintWeb.config.toolDrawDelay 59 */ 60 var timer = null; 61 62 /** 63 * Tells if the <kbd>Shift</kbd> key is down or not. This is used by the 64 * drawing function. 65 * 66 * @private 67 * @type Boolean 68 * @default false 69 */ 70 var shiftKey = false; 71 72 /** 73 * Tells if the drawing canvas needs to be updated or not. 74 * 75 * @private 76 * @type Boolean 77 * @default false 78 */ 79 var needsRedraw = false; 80 81 /** 82 * The tool deactivation method, used for clearing the buffer. 83 */ 84 this.deactivate = function () { 85 if (timer) { 86 clearInterval(timer); 87 timer = null; 88 } 89 90 if (points.length) { 91 context.clearRect(0, 0, image.width, image.height); 92 } 93 94 needsRedraw = false; 95 points = []; 96 97 return true; 98 }; 99 100 /** 101 * The <code>mousedown</code> event handler. 102 * 103 * @param {Event} ev The DOM Event object. 104 * @returns {Boolean} True if the event handler executed, or false if not. 105 */ 106 this.mousedown = function (ev) { 107 if (points.length == 0) { 108 points.push([mouse.x, mouse.y]); 109 } 110 111 if (!timer) { 112 timer = setInterval(_self.draw, config.toolDrawDelay); 113 } 114 115 shiftKey = ev.shiftKey; 116 needsRedraw = false; 117 118 gui.statusShow('polygonMousedown'); 119 120 return true; 121 }; 122 123 /** 124 * Store the <kbd>Shift</kbd> key state which is used by the drawing function. 125 * 126 * @param {Event} ev The DOM Event object. 127 */ 128 this.mousemove = function (ev) { 129 shiftKey = ev.shiftKey; 130 needsRedraw = true; 131 }; 132 133 /** 134 * Draw the polygon. 135 * 136 * @see PaintWeb.config.toolDrawDelay 137 */ 138 this.draw = function (ev) { 139 if (!needsRedraw) { 140 return; 141 } 142 143 var n = points.length; 144 145 if (!n || (n == 1 && !mouse.buttonDown)) { 146 needsRedraw = false; 147 return; 148 } 149 150 // Snapping on the X/Y axis for the current point (if available). 151 if (mouse.buttonDown && shiftKey) { 152 snapXY(points[n-1][0], points[n-1][1]); 153 } 154 155 context.clearRect(0, 0, image.width, image.height); 156 context.beginPath(); 157 context.moveTo(points[0][0], points[0][1]); 158 159 // Draw the path of the polygon 160 for (var i = 0; i < n; i++) { 161 context.lineTo(points[i][0], points[i][1]); 162 } 163 164 if (mouse.buttonDown) { 165 context.lineTo(mouse.x, mouse.y); 166 } 167 168 if (config.shapeType != 'stroke') { 169 context.fill(); 170 } 171 172 // In the case where we only have a straight line, draw a stroke even if no 173 // stroke should be drawn, such that the user has better visual feedback. 174 if (config.shapeType != 'fill' || n == 1) { 175 context.stroke(); 176 } 177 178 context.closePath(); 179 180 needsRedraw = false; 181 }; 182 183 /** 184 * The <code>mouseup</code> event handler. 185 * 186 * @param {Event} ev The DOM Event object. 187 * @returns {Boolean} True if the event handler executed, or false if not. 188 */ 189 this.mouseup = function (ev) { 190 var n = points.length; 191 192 // Allow click+mousemove+click, not only mousedown+mousemove+mouseup. 193 // Do this only for the first point in the polygon. 194 if (n == 1 && mouse.x == points[n-1][0] && mouse.y == points[n-1][1]) { 195 mouse.buttonDown = true; 196 return true; 197 } 198 199 if (timer) { 200 clearInterval(timer); 201 timer = null; 202 } 203 204 shiftKey = ev.shiftKey; 205 needsRedraw = true; 206 207 if (ev.shiftKey) { 208 snapXY(points[n-1][0], points[n-1][1]); 209 } 210 211 var diffx1 = MathAbs(mouse.x - points[0][0]), 212 diffy1 = MathAbs(mouse.y - points[0][1]), 213 diffx2 = MathAbs(mouse.x - points[n-1][0]), 214 diffy2 = MathAbs(mouse.y - points[n-1][1]); 215 216 // End the polygon if the new point is close enough to the first/last point. 217 if ((diffx1 < 5 && diffy1 < 5) || (diffx2 < 5 && diffy2 < 5)) { 218 // Add the start point to complete the polygon shape. 219 points.push(points[0]); 220 221 _self.draw(); 222 points = []; 223 224 gui.statusShow('polygonActive'); 225 app.layerUpdate(); 226 227 return true; 228 } 229 230 if (n > 3) { 231 gui.statusShow('polygonEnd'); 232 } else { 233 gui.statusShow('polygonAddPoint'); 234 } 235 236 points.push([mouse.x, mouse.y]); 237 _self.draw(); 238 239 return true; 240 }; 241 242 /** 243 * The <code>keydown</code> event handler. This method allows the user to 244 * cancel drawing the current polygon, using the <kbd>Escape</kbd> key. The 245 * <kbd>Enter</kbd> key can be used to accept the current polygon shape, and 246 * end the drawing operation. 247 * 248 * @param {Event} ev The DOM Event object. 249 * 250 * @returns {Boolean} True if the keyboard shortcut was recognized, or false 251 * if not. 252 */ 253 this.keydown = function (ev) { 254 var n = points.length; 255 if (!n || (ev.kid_ != 'Escape' && ev.kid_ != 'Enter')) { 256 return false; 257 } 258 259 if (timer) { 260 clearInterval(timer); 261 timer = null; 262 } 263 mouse.buttonDown = false; 264 265 if (ev.kid_ == 'Escape') { 266 context.clearRect(0, 0, image.width, image.height); 267 needsRedraw = false; 268 269 } else if (ev.kid_ == 'Enter') { 270 // Add the point of the last mousemove event, and the start point, to 271 // complete the polygon. 272 points.push([mouse.x, mouse.y]); 273 points.push(points[0]); 274 needsRedraw = true; 275 _self.draw(); 276 app.layerUpdate(); 277 } 278 279 points = []; 280 gui.statusShow('polygonActive'); 281 282 return true; 283 }; 284 }; 285 286 // vim:set spell spl=en fo=wan1croqlt tw=80 ts=2 sw=2 sts=2 sta et ai cin fenc=utf-8 ff=unix: 287 288