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