Package arcmap :: Module editortools
[hide private]
[frames] | no frames]

Source Code for Module arcmap.editortools

  1  ################################################################################ 
  2  # Authors: Brian Schott (Sir Alaran) 
  3  # Copyright: Brian Schott (Sir Alaran) 
  4  # Date: Sep 29 2009 
  5  # License: 
  6  # 
  7  # This program is free software: you can redistribute it and/or modify 
  8  # it under the terms of the GNU General Public License as published by 
  9  # the Free Software Foundation, either version 3 of the License, or 
 10  # (at your option) any later version. 
 11  # 
 12  # This program is distributed in the hope that it will be useful, 
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 15  # GNU General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU General Public License 
 18  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 19  ################################################################################ 
 20   
 21   
 22   
 23  """ 
 24  Code for the GUI tools that allow the user to edit the map. This is used by the 
 25  mapgrid module. 
 26  """ 
 27   
 28   
 29  import gtk 
 30  import cairo 
 31   
 32  import graphics 
 33  import mapcontroller 
 34  import shapes 
 35  import dialogs 
 36  import undo 
 37   
 38  # Constants for use in the UI code. 
 39  TILE_DRAW_ID = 0 
 40  TILE_DELETE_ID = 1 
 41  PHYSICS_SELECT_ID = 2 
 42  CIRCLE_DRAW_ID = 3 
 43  POLYGON_DRAW_ID = 4 
 44  LIGHT_DRAW_ID = 5 
 45  # Add new codes here for new tools 
 46   
 47   
48 -def coordsToTileEdges(ix, iy, ts):
49 """ 50 Return the ix and iy snapped to the nearest division between tiles 51 This is used for physics drawing 52 @type ix: int 53 @param ix: x-coordinate of mouse 54 @type iy: int 55 @param iy: y-coordinate of mouse 56 @type ts: int 57 @param ts: the size (in pixels) of a tile 58 """ 59 x = round(ix / ts) * ts 60 y = round(iy / ts) * ts 61 return x, y
62 63
64 -def coordsToTiles(x, y, ts):
65 """ 66 Translates mouse coordinates to tile coordinates 67 @type x: int 68 @param x: x-coordinate of the mouse 69 @type y: int 70 @param y: y-cooridnate of the mouse 71 @type ts: int 72 @param ts: size of a tile in pixels 73 """ 74 return x // ts, y // ts
75 76
77 -def rectangleSelect(x1, y1, x2, y2, ts):
78 """ 79 Returns the coordinates of a rectangle whose edges are snapped to the 80 divisions between tiles. The returned value is in pixel units in the form 81 (x, y, w, h) 82 @type x1: int 83 @param x1: left x-coordinate in tiles 84 @type y1: int 85 @param y1: top y-coordinate in tiles 86 @type x2: int 87 @param x2: right x-coordinate in tiles 88 @type y2: int 89 @param y2: bottom y-coordinate in tiles 90 @type ts: int 91 @param ts: size of tile in pixels 92 """ 93 rx = min(x1, x2) * ts 94 ry = min(y1, y2) * ts 95 rw = (abs(x2 - x1) + 1) * ts 96 rh = (abs(y2 - y1) + 1) * ts 97 return int(rx), int(ry), int(rw), int(rh)
98 99
100 -class EditorTool(mapcontroller.MapListener):
101 """ 102 Base class for tools used in the map editor. Derive from this class to add 103 more functionality to the program 104 """
105 - def __init__(self, controller):
106 self.__x = 0 107 self.__y = 0 108 self.__instructions = None 109 self.__id = -1 110 self.__hasPointer = False 111 mapcontroller.MapListener.__init__(self, controller)
112
113 - def getPointer(self):
114 """ 115 @rtype: bool 116 @return: True if the mouse is over the TileGrid 117 """ 118 return self.__hasPointer
119
120 - def setInstructions(self, text):
121 """ 122 @type text: string 123 @param text: the instructions for using this tool 124 """ 125 self.__instructions = text
126
127 - def getInstructions(self):
128 """ 129 @rtype: string 130 @return: the instructions for using this tool. 131 """ 132 return self.__instructions
133
134 - def getID(self):
135 """ 136 @rtype: int 137 @return: the ID of this tool 138 """ 139 return self.__id
140
141 - def draw(self, context):
142 """ 143 Draws the tool to the given cairo context 144 @type context: cairo.Context 145 @param context: the drawing context 146 """ 147 pass
148
149 - def x(self):
150 """ 151 @rtype: int 152 @return: the x-coordinate of the mouse 153 """ 154 return self.__x
155
156 - def y(self):
157 """ 158 @rtype: int 159 @return: the y-coordinate of the mouse 160 """ 161 return self.__y
162
163 - def mouseMotion(self, x, y):
164 """ 165 @rtype: bool 166 @return: True if this event causes a redraw to be necessary. 167 """ 168 # This line about setting the __hasPointer to True should not be 169 # necessary. I don't know why gtk gives a leave notification when the 170 # mouse button is pressed or released. It makes no sense. 171 self.__hasPointer = True 172 self.__x = x 173 self.__y = y 174 return False
175
176 - def mouseButtonPress(self, button, time):
177 """ 178 @type button: int 179 @param button: button that was pressed 180 @type time: int 181 @param time: the time that the button press happened. Necessary for 182 popping up menus 183 @rtype: bool 184 @return: True if this event causes a redraw to be necessary. 185 """ 186 self.__hasPointer = True 187 return False
188
189 - def mouseButtonRelease(self, button, time):
190 """ 191 @type button: int 192 @param button: button that was pressed 193 @param time: the time that the button press happened. Necessary for 194 popping up menus 195 @rtype: bool 196 @return: True if this event causes a redraw to be necessary. 197 """ 198 self.__hasPointer = True 199 return False
200
201 - def mouseEnter(self):
202 """ 203 Called when the mouse enters the area over the TileGrid 204 @rtype: bool 205 @return: True if the TileGrid should be redrawn 206 """ 207 self.__hasPointer = True 208 return True
209
210 - def mouseLeave(self):
211 """ 212 Called when the mouse leaves the area over the TileGrid 213 @rtype: bool 214 @return: True if the TileGrid should be redrawn 215 """ 216 self.__hasPointer = False 217 return True
218
219 - def keyPress(self, key):
220 """ 221 TODO: figure out and document the type of key 222 @rtype: bool 223 @return: True if this event causes a redraw to be necessary. 224 """ 225 return False
226
227 - def draw(self, context):
228 pass
229
230 - def undo(self):
231 pass
232 233
234 -class TileTool(EditorTool):
235 - def __init__(self, controller):
236 EditorTool.__init__(self, controller) 237 self.setInstructions("Error: This is an abstract class") 238 self.selectX1 = -1 239 self.selectY1 = -1 240 self.selectX2 = -1 241 self.selectY2 = -1 242 self.lastX = -1 243 self.lastY = -1 244 self.buttonDown = False
245
246 - def mouseButtonPress(self, button, time):
247 if button != 1: 248 return False 249 self.selectX1, self.selectY1 = coordsToTiles(self.x(), self.y(), 250 self.getController().mapTileSize()) 251 self.selectY2 = self.selectY1 252 self.selectX2 = self.selectX1 253 self.lastX = self.selectX1 254 self.lastY = self.selectY1 255 self.buttonDown = True 256 return True
257
258 - def mouseButtonRelease(self, button, time):
259 if button != 1: 260 return False 261 x1 = int(min(self.selectX1, self.selectX2)) 262 y1 = int(min(self.selectY1, self.selectY2)) 263 x2 = int(max(self.selectX1, self.selectX2)) 264 y2 = int(max(self.selectY1, self.selectY2)) 265 self.selectX1 = x1 266 self.selectY1 = y1 267 self.selectX2 = x2 268 self.selectY2 = y2 269 self.buttonDown = False 270 return True
271
272 - def mouseMotion(self, x, y):
273 """ See TileGrid.mouseMotion """ 274 275 def updateLast(a, b): 276 if a == self.lastX and b == self.lastY: 277 return False 278 else: 279 self.lastX = a 280 self.lastY = b 281 return True
282 283 EditorTool.mouseMotion(self, x, y) 284 if self.buttonDown: 285 self.selectX2, self.selectY2 = coordsToTiles(self.x(), self.y(), 286 self.getController().mapTileSize()) 287 return updateLast(self.selectX2, self.selectY2) 288 else: 289 tmpx, tmpy = coordsToTiles(self.x(), self.y(), 290 self.getController().mapTileSize()) 291 return updateLast(tmpx, tmpy)
292
293 -class TileDeleteTool(TileTool):
294 - def __init__(self, controller):
295 TileTool.__init__(self, controller) 296 self.setInstructions("Click and drag to select tiles to delete")
297
298 - def mouseMotion(self, x, y):
299 TileTool.mouseMotion(self, x, y) 300 return self.buttonDown
301
302 - def mouseButtonRelease(self, button, time):
303 TileTool.mouseButtonRelease(self, button, time) 304 controller = self.getController() 305 z = controller.selectedLayer() 306 307 action = undo.TileRemoveAction(controller) 308 309 for x in range(self.selectX1, self.selectX2 + 1): 310 for y in range(self.selectY1, self.selectY2 + 1): 311 r = self.getController().removeTile(x, y, z) 312 action.appendTileRemove((x, y, z), r) 313 314 controller.addUndoAction(action)
315 316
317 - def draw(self, context):
318 if self.buttonDown == False: 319 return 320 321 ts = self.getController().mapTileSize() 322 x, y, w, h = rectangleSelect(self.selectX1, self.selectY1, 323 self.selectX2, self.selectY2, ts) 324 pattern = graphics.getDeletePattern(ts) 325 context.rectangle(x, y, w, h) 326 context.set_source(pattern) 327 pattern.set_extend(cairo.EXTEND_REPEAT) 328 matrix = cairo.Matrix() 329 matrix.translate(-x, -y) 330 pattern.set_matrix(matrix) 331 context.set_source(pattern) 332 context.rectangle(x, y, w, h) 333 context.fill()
334 335
336 -class TileSelectTool(TileTool):
337 - def __init__(self, controller, index):
338 TileTool.__init__(self, controller) 339 self.setInstructions("Left-click to select tiles. Click and drag to" 340 + " select multiple tiles.") 341 self.__index = index
342
343 - def mouseMotion(self, x, y):
344 TileTool.mouseMotion(self, x, y) 345 return self.buttonDown
346
347 - def mouseButtonRelease(self, button, time):
348 TileTool.mouseButtonRelease(self, button, time) 349 if button != 1: 350 return False 351 else: 352 self.getController().setSelection(self.__index, self.selectX1, 353 self.selectY1, self.selectX2, self.selectY2) 354 return False
355
356 - def draw(self, context):
357 """ 358 Draws a rectangle around the selected tiles 359 """ 360 x, y, w, h = rectangleSelect(self.selectX1, self.selectY1, 361 self.selectX2, self.selectY2, self.getController().mapTileSize()) 362 363 # semi-transparent square 364 fc = graphics.getHighlightColor() 365 hc = graphics.getBackgroundColor() 366 context.set_source_rgba(fc.r, fc.g, fc.b, 0.5) 367 context.rectangle(x, y, w, h) 368 context.fill() 369 context.rectangle(x + 0.5, y + 0.5, w, h) 370 context.set_source_rgba(fc.r, fc.g, fc.b, 1.0) 371 context.set_line_width(3.0) 372 context.stroke() 373 context.rectangle(x + 0.5, y + 0.5, w, h) 374 context.set_source_rgba(hc.r, hc.g, hc.b, 1.0) 375 context.set_line_width(1.0) 376 context.stroke()
377 378
379 -class TileDrawTool(TileTool):
380 - def __init__(self, controller):
381 TileTool.__init__(self, controller) 382 self.setInstructions("Left-click to draw tiles on the map." 383 + " Right-click to delete them.") 384 385 # Index of the source image 386 self.__selectionIndex = None 387 # surface for drawing 388 self.__brush = None
389
390 - def mouseMotion(self, x, y):
391 if self.__selectionIndex == None: 392 return False 393 """ See TileGrid.mouseMotion """ 394 return TileTool.mouseMotion(self, x, y)
395
396 - def mouseButtonPress(self, button, time):
397 """ See TileGrid.buttonPress """ 398 if self.__selectionIndex == None or button != 1: 399 return False 400 TileTool.mouseButtonPress(self, button, time) 401 return True
402
403 - def mouseButtonRelease(self, button, time):
404 """ See TileGrid.buttonRelease """ 405 if self.__selectionIndex == None or button != 1: 406 return False 407 TileTool.mouseButtonRelease(self, button, time) 408 self.__setTiles(self.selectX1, self.selectX2, self.selectY1, 409 self.selectY2) 410 return True
411
412 - def __setTiles(self, startX, endX, startY, endY):
413 """ 414 Sets tiles on the map in the range defined by startX, endX, startY 415 and endY 416 """ 417 if self.__selectionIndex == None: 418 return 419 420 421 422 # Ensure that start < end 423 sX = int(min(startX, endX)) 424 eX = int(max(startX, endX)) 425 sY = int(min(startY, endY)) 426 eY = int(max(startY, endY)) 427 428 controller = self.getController() 429 z = controller.selectedLayer() 430 action = undo.TileAddAction(controller) 431 432 if startX == endX and startY == endY: 433 # Do not repeat the pattern. Overflow of the range is allowed if 434 # the starts and ends are the same 435 tempY = sY 436 tempX = sX 437 for i in range(self.selectionX1, self.selectionX2 + 1): 438 for j in range(self.selectionY1, self.selectionY2 + 1): 439 r = controller.addTile(tempX, tempY, z, i, j, 440 self.__selectionIndex) 441 action.appendTileAdd((tempX, tempY, z), 442 (i, j, self.__selectionIndex), r) 443 tempY += 1 444 tempY = sY 445 tempX += 1 446 else: 447 # Repeat the pattern but do not allow it to overflow the specified 448 # area 449 sdx = abs(self.selectionX2 - self.selectionX1) 450 sdy = abs(self.selectionY2 - self.selectionY1) 451 ddx = eX - sX 452 ddy = eY - sY 453 for i in range(ddx + 1): 454 for j in range(ddy + 1): 455 ix = (i % (sdx + 1)) + self.selectionX1 456 iy = (j % (sdy + 1)) + self.selectionY1 457 x = i + sX 458 y = j + sY 459 r = controller.addTile(x, y, z, ix, iy, 460 self.__selectionIndex) 461 action.appendTileAdd((x, y, z), (ix, iy, 462 self.__selectionIndex), r) 463 464 controller.addUndoAction(action)
465
466 - def draw(self, context):
467 if self.__brush == None or self.getPointer() == False: 468 return 469 ts = self.getController().mapTileSize() 470 if self.buttonDown: 471 x, y, w, h = rectangleSelect(self.selectX1, self.selectY1, 472 self.selectX2, self.selectY2, ts) 473 pattern = cairo.SurfacePattern(self.__brush) 474 pattern.set_extend(cairo.EXTEND_REPEAT) 475 matrix = cairo.Matrix() 476 matrix.translate(-x, -y) 477 pattern.set_matrix(matrix) 478 context.set_source(pattern) 479 context.rectangle(x, y, w, h) 480 context.fill() 481 else: 482 x = self.lastX * ts 483 y = self.lastY * ts 484 context.set_source_surface(self.__brush, x, y) 485 w = (abs(self.selectionX2 - self.selectionX1) + 1) * ts 486 h = (abs(self.selectionY2 - self.selectionY1) + 1) * ts 487 context.rectangle(x, y, w, h) 488 context.fill()
489
490 - def listenSetSelection(self, index, brush, x1, y1, x2, y2):
491 self.__selectionIndex = index 492 self.__brush = brush 493 self.selectionX1 = int(x1) 494 self.selectionY1 = int(y1) 495 self.selectionX2 = int(x2) 496 self.selectionY2 = int(y2)
497
498 - def listenFileClosed(self):
499 self.__selectionIndex = None 500 self.__brush = None 501 self.selectionX1 = self.selectionY1 = 0 502 self.selectionX2 = self.selectionY2 = 0
503 504
505 -class ShapeTool(EditorTool):
506 """ 507 Base class for tools that draw physics shapes 508 """
509 - def __init__(self, controller):
510 EditorTool.__init__(self, controller)
511
512 - def draw(self, context):
513 self.getController().drawShapes(context)
514 515
516 -class PhysicsSelectTool(ShapeTool):
517 - def __init__(self, controller):
518 ShapeTool.__init__(self, controller) 519 self.__selectedShape = None 520 self.__dragLastX = -1 521 self.__dragLastY = -1 522 # Coordinate for the start of a drag operation. This is either the start 523 # point for dragging an entire shape, or for dragging a single handle. 524 self.__dragStartX = -1 525 self.__dragStartY = -1 526 self.__dragging = False 527 self.__handleIndex = None 528 self.setInstructions("Click and drag to move shapes")
529
530 - def mouseButtonPress(self, button, time):
531 s = self.selectShape(self.x(), self.y()) 532 533 def startDrag(shape): 534 self.__selectedShape = shape 535 self.__dragging = True 536 self.__dragLastX, self.__dragLastY = coordsToTileEdges( 537 self.x(), self.y(), 538 self.getController().mapTileSize()) 539 self.__dragStartX = self.__dragLastX 540 self.__dragStartY = self.__dragLastY
541 542 def selectHandle(): 543 for i, p in enumerate(self.__selectedShape.getHandles()): 544 if shapes.pointInHandle(shapes.Point(self.x(), self.y()), p): 545 self.__handleIndex = i 546 self.__dragStartX, self.__dragStartY = coordsToTileEdges( 547 self.x(), self.y(), self.getController().mapTileSize()) 548 break
549 550 if button == 1: 551 if self.__selectedShape != None: 552 selectHandle() 553 if self.__handleIndex == None: 554 startDrag(s) 555 else: 556 startDrag(s) 557 elif button == 3: 558 self.__selectedShape = s 559 if s != None: 560 self.popupShapeMenu(self.__selectedShape, button, time) 561 return True 562
563 - def mouseButtonRelease(self, button, time):
564 if button == 1: 565 if (self.__dragging == True and 566 (self.__dragLastX != self.__dragStartX 567 or self.__dragLastY != self.__dragStartY)): 568 action = undo.ShapeMoveAction( 569 self.getController(), 570 self.__selectedShape, 571 self.__dragLastX - self.__dragStartX, 572 self.__dragLastY - self.__dragStartY, 573 ) 574 self.getController().addUndoAction(action) 575 if (self.__handleIndex != None and ( 576 self.__dragLastX != self.__dragStartX 577 or self.__dragLastY != self.__dragStartY)): 578 action = undo.ShapeAdjustAction( 579 self.getController(), 580 self.__selectedShape, 581 self.__handleIndex, 582 self.__dragStartX, 583 self.__dragStartY, 584 self.__dragLastX, 585 self.__dragLastY, 586 ) 587 self.getController().addUndoAction(action) 588 self.__dragging = False 589 self.__handleIndex = None
590 591
592 - def mouseMotion(self, x, y):
593 ShapeTool.mouseMotion(self, x, y) 594 if self.__selectedShape != None: 595 tmpX, tmpY = coordsToTileEdges(self.x(), self.y(), 596 self.getController().mapTileSize()) 597 if self.__handleIndex != None: 598 self.__selectedShape.adjust(tmpX, tmpY, 599 self.__handleIndex) 600 self.__dragLastX = tmpX 601 self.__dragLastY = tmpY 602 return True 603 elif self.__dragging: 604 dx = self.__dragLastX - tmpX 605 dy = self.__dragLastY - tmpY 606 if dx != 0 or dy != 0: 607 ts = self.getController().mapTileSize() 608 self.__selectedShape.shift(-dx, -dy) 609 self.__dragLastX = tmpX 610 self.__dragLastY = tmpY 611 self.getController().notifyModification(True) 612 return True 613 return False
614
615 - def draw(self, context):
616 ShapeTool.draw(self, context) 617 if self.__selectedShape != None: 618 if self.__selectedShape.friction == None: 619 # The friction being None (a completely bogus value) means that 620 # the shape was deleted. Because the shape was deleted in a 621 # callback function, this code to set __selectedShape to None 622 # can't be in the button event handler 623 self.__selectedShape = None 624 return 625 for p in self.__selectedShape.getHandles(): 626 graphics.drawPointHandle(context, p) 627 graphics.drawMoveHandle(context, self.__selectedShape.getCenter())
628
629 - def popupShapeMenu(self, shape, button, time):
630 """ 631 @type shape: shapes.Shape 632 @param shape: the shape that this is a context menu for 633 @type button: int 634 @param button: the button pressed 635 @type time: int? 636 @param time: the time that this event was triggered 637 """ 638 menu = gtk.Menu() 639 deleteItem = gtk.ImageMenuItem(gtk.STOCK_DELETE) 640 deleteItem.connect("activate", self.deleteCB, shape) 641 propertiesItem = gtk.ImageMenuItem(gtk.STOCK_PROPERTIES) 642 propertiesItem.connect("activate", self.propertiesCB, shape) 643 menu.attach(deleteItem, 0, 1, 0, 1) 644 menu.attach(propertiesItem, 0, 1, 1, 2) 645 menu.show_all() 646 menu.popup(None, None, None, button, time)
647
648 - def deleteCB(self, menuitem, shape):
649 """ 650 callback for the delete item in popupShapeMenu 651 @type menuitem: gtk.MenuItem 652 @param menuitem: ignored 653 @type shape: shapes.Shape 654 @param shape: the shape to delete 655 """ 656 self.__selectedShape = None 657 action = undo.ShapeDeleteAction(self.getController(), shape) 658 self.getController.addUndoAction(action) 659 self.getController().removeShape(shape)
660
661 - def propertiesCB(self, menuitem, shape):
662 """ 663 callback for the properties item in popupShapeMenu 664 @type menuitem: gtk.MenuItem 665 @param menuitem: ignored 666 @type shape: shapes.Shape 667 @param shape: the shape whose properties should be changed 668 """ 669 d = dialogs.StaticShapePropertiesDialog( 670 self.getController().getToplevel(), shape) 671 result = d.run() 672 if result == gtk.RESPONSE_ACCEPT: 673 shape.friction = d.getFriction() 674 shape.damage = d.getDamage() 675 shape.restitution = d.getRestitution() 676 self.getController().notifyModification(True) 677 d.destroy()
678
679 - def selectShape(self, x, y):
680 """ 681 @type x: int 682 @param x: x-coordinate to test 683 @type y: int 684 @param y: y-coordinate to test 685 @rtype: shapes.Shape 686 @return: the shape selected, or None 687 """ 688 shapeList = self.getController().getShapes() 689 for shape in shapeList: 690 if shape.intersects(shapes.Point(x, y)): 691 return shape 692 return None
693
694 - def listenRemoveShape(self, shape):
695 if self.__selectedShape == shape: 696 self.__selectedShape = None
697 698 699
700 -class CircleDrawTool(ShapeTool):
701 - def __init__(self, controller):
702 ShapeTool.__init__(self, controller) 703 self.__drawing = False 704 self.__circle = shapes.Circle(0, 705 int(self.getController().mapTileSize() / 2)) 706 # A good default value, since the radius can't be negative 707 self.__oldRadius = -1 708 self.setInstructions("Click to set the center point. Click" 709 + " again to set the radius")
710
711 - def mouseButtonPress(self, button, time):
712 if self.__drawing: 713 controller = self.getController() 714 action = undo.ShapeAddAction(controller, self.__circle) 715 controller.addUndoAction(action) 716 controller.addShape(self.__circle) 717 self.__circle = shapes.Circle(0.0, 718 int(controller.mapTileSize() / 2)) 719 self.__drawing = False 720 else: 721 if button == 1: 722 self.__circle.setCenter(shapes.Point(self.x(), self.y())) 723 self.__drawing = True
724
725 - def mouseButtonRelease(self, button, time):
726 return False
727
728 - def mouseMotion(self, x, y):
729 ShapeTool.mouseMotion(self, x, y) 730 if self.__drawing == True: 731 self.__circle.adjust(self.x(), self.y()) 732 if self.__circle.getRadius() == self.__oldRadius: 733 return False 734 else: 735 self.__oldRadius = self.__circle.getRadius() 736 return True
737
738 - def draw(self, context):
739 if self.__drawing: 740 graphics.drawShape(self.__circle, context, True) 741 ShapeTool.draw(self, context)
742
743 -class PolygonDrawTool(ShapeTool):
744 - def __init__(self, controller):
745 ShapeTool.__init__(self, controller) 746 # True if a shape is in the middle of being drawn 747 self.__drawing = False 748 self.__polygon = shapes.Polygon() 749 # These keep the number of redraws down 750 self.__lastX = 0 751 self.__lastY = 0 752 self.setInstructions("Left click to add points, right click to finish.")
753
754 - def mouseButtonPress(self, button, time):
755 controller = self.getController() 756 if button == 1: 757 p = coordsToTileEdges(self.x(), self.y(), 758 controller.mapTileSize()) 759 if self.__drawing == True: 760 last = self.__polygon.getLastPoint() 761 if last != None and p[0] == last.x and p[1] == last.y \ 762 and self.__polygon.convex(): 763 self.__polygon.delExtraPoint() 764 self.__polygon.forceCClockwise() 765 action = undo.ShapeAddAction(controller, self.__polygon) 766 controller.addUndoAction(action) 767 controller.addShape(self.__polygon) 768 self.__polygon = shapes.Polygon() 769 self.__drawing = False 770 else: 771 self.__polygon.addPoint(shapes.Point(p[0], p[1])) 772 else: 773 self.__polygon.addPoint(shapes.Point(p[0], p[1])) 774 self.__drawing = True 775 elif button == 3: 776 if self.__drawing: 777 if self.__polygon.convex(): 778 self.__drawing = False 779 self.__polygon.forceCClockwise() 780 action = undo.ShapeAddAction(controller, self.__polygon) 781 controller.addUndoAction(action) 782 controller.addShape(self.__polygon) 783 self.__polygon = shapes.Polygon() 784 return True
785
786 - def mouseButtonRelease(self, button, time):
787 return False
788
789 - def mouseMotion(self, x, y):
790 ShapeTool.mouseMotion(self, x, y) 791 p = coordsToTileEdges(self.x(), self.y(), 792 self.getController().mapTileSize()) 793 self.__polygon.adjust(p[0], p[1]) 794 # Only force a redraw if the snapped mouse coordinates have actually 795 # changed. 796 if p[0] == self.__lastX and p[1] == self.__lastY: 797 self.__lastX = p[0] 798 self.__lastY = p[1] 799 return False 800 else: 801 self.__lastX = p[0] 802 self.__lastY = p[1] 803 return True
804
805 - def draw(self, context):
806 if self.__drawing == True: 807 graphics.drawShape(self.__polygon, context, True) 808 ShapeTool.draw(self, context)
809 810
811 -class LightDrawTool(EditorTool):
812 - def __init__(self, controller):
813 EditorTool.__init__(self, controller)
814
815 - def mouseButtonPress(self, button, time):
816 pass
817
818 - def mouseButtonRelease(self, button, time):
819 pass
820
821 - def mouseMotion(self, x, y):
822 pass
823