Changeset 1670
- Timestamp:
- 07/18/07 11:13:12 (1 year ago)
- Files:
-
- gaphas/trunk/demo.py (modified) (3 diffs)
- gaphas/trunk/demo_profile.py (modified) (2 diffs)
- gaphas/trunk/gaphas/canvas.py (modified) (16 diffs)
- gaphas/trunk/gaphas/constraint.py (modified) (5 diffs)
- gaphas/trunk/gaphas/decorators.py (modified) (1 diff)
- gaphas/trunk/gaphas/examples.py (modified) (8 diffs)
- gaphas/trunk/gaphas/item.py (modified) (15 diffs)
- gaphas/trunk/gaphas/painter.py (modified) (4 diffs)
- gaphas/trunk/gaphas/solver.py (modified) (2 diffs)
- gaphas/trunk/gaphas/tests/test_element.py (modified) (1 diff)
- gaphas/trunk/gaphas/tests/test_solver.py (modified) (2 diffs)
- gaphas/trunk/gaphas/tool.py (modified) (9 diffs)
- gaphas/trunk/gaphas/view.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphas/trunk/demo.py
r1562 r1670 25 25 import cairo 26 26 from gaphas import Canvas, GtkView, View 27 from gaphas.examples import Box, Text, DefaultExampleTool27 from gaphas.examples import Box, Text, FatLine, Circle, DefaultExampleTool 28 28 from gaphas.item import Line, NW, SE 29 29 from gaphas.tool import PlacementTool, HandleTool … … 300 300 print 'box', b 301 301 b.matrix=(1.0, 0.0, 0.0, 1, 20,20) 302 b.width =b.height = 40302 b.width = b.height = 40 303 303 c.add(b) 304 304 … … 307 307 bb.matrix=(1.0, 0.0, 0.0, 1, 10,10) 308 308 c.add(bb, parent=b) 309 #v.selected_items = bb 309 310 fl = FatLine() 311 fl.height = 50 312 fl.matrix.translate(100, 100) 313 c.add(fl) 314 315 316 circle = Circle() 317 h1, h2 = circle.handles() 318 circle.radius = 20 319 circle.matrix.translate(50, 100) 320 c.add(circle) 310 321 311 322 # AJM: extra boxes: gaphas/trunk/demo_profile.py
r1628 r1670 174 174 c.add(bb, parent=b) 175 175 176 for i in range( count):176 for i in range(40): 177 177 bb = MyBox() 178 178 bb.width = bb.height = 15 … … 210 210 211 211 if __name__ == '__main__': 212 import hotshot, hotshot.stats 213 prof = hotshot.Profile('demo-gaphas.prof') 214 prof.runcall(main) 215 prof.close() 216 stats = hotshot.stats.load('demo-gaphas.prof') 217 stats.strip_dirs() 218 stats.sort_stats('time', 'calls') 219 stats.print_stats(20) 212 try: 213 import cProfile 214 import pstats 215 cProfile.run('main()', 'demo-gaphas.prof') 216 p = pstats.Stats('demo-gaphas.prof') 217 p.strip_dirs().sort_stats('time').print_stats(20) 218 except ImportError, ex: 219 import hotshot, hotshot.stats 220 import gc 221 prof = hotshot.Profile('demo-gaphas.prof') 222 prof.runcall(main) 223 prof.close() 224 stats = hotshot.stats.load('demo-gaphas.prof') 225 stats.strip_dirs() 226 stats.sort_stats('time', 'calls') 227 stats.print_stats(20) 220 228 221 229 # vim: sw=4:et: gaphas/trunk/gaphas/canvas.py
r1609 r1670 6 6 __version__ = "$Revision$" 7 7 # $HeadURL$ 8 9 8 10 9 import cairo … … 14 13 from gaphas.decorators import nonrecursive, async, PRIORITY_HIGH_IDLE 15 14 from state import observed, reversible_method, reversible_pair 15 from gaphas.constraint import Projector 16 16 17 17 … … 38 38 class Canvas(object): 39 39 """ 40 Container class for Items. 40 Container class for items. 41 42 Attributes: 43 - projector: canvas constraint projector between item and canvas 44 coordinates 45 - _canvas_constraints: constraints set between canvas items 41 46 """ 42 47 … … 48 53 49 54 self._registered_views = set() 55 self._canvas_constraints = {} 56 57 self.projector = CanvasProjector(self) 50 58 51 59 solver = property(lambda s: s._solver) … … 68 76 item.canvas = self 69 77 self._tree.add(item, parent) 78 self._canvas_constraints[item] = {} 79 80 for v in self._registered_views: 81 v.update_matrix(item) 82 70 83 self.request_update(item) 71 84 self._update_views((item,)) 85 72 86 73 87 @observed … … 92 106 self._tree.remove(item) 93 107 self.remove_connections_to_item(item) 108 del self._canvas_constraints[item] 94 109 self._update_views((item,)) 95 110 self._dirty_items.discard(item) … … 98 113 reversible_pair(add, remove, 99 114 bind1={'parent': lambda self, item: self.get_parent(item) }) 115 116 117 def add_canvas_constraint(self, item, handle, c): 118 """ 119 Add constraint between items. 120 121 Parameters: 122 - item: item holding constraint 123 - handle: handle holding constraint 124 - c: constraint between items 125 """ 126 if item not in self._canvas_constraints: 127 raise ValueError, 'Item not added to canvas' 128 129 i_cons = self._canvas_constraints[item] 130 if handle not in i_cons: 131 i_cons[handle] = set() 132 i_cons[handle].add(c) 133 self._solver.add_constraint(c) 134 135 136 def remove_canvas_constraint(self, item, handle, c=None): 137 """ 138 Remove constraint set between item. 139 140 If constraint is not set then all constraints are removed for given 141 item and handle. 142 143 Parameters: 144 - item: item holding constraint 145 - handle: handle holding constraint 146 - c: constraint between items 147 """ 148 if item not in self._canvas_constraints: 149 raise ValueError, 'Item not added to canvas' 150 151 i_cons = self._canvas_constraints[item] 152 153 if c is None: # remove all handle's constraints 154 h_cons = i_cons[handle] 155 for c in h_cons: 156 self._solver.remove_constraint(c) 157 h_cons.clear() 158 else: 159 # remove specific constraint 160 self._solver.remove_constraint(c) 161 i_cons[handle].remove(c) 162 163 164 def canvas_constraints(self, item): 165 """ 166 Get all constraints set between items for specific item. 167 """ 168 if item not in self._canvas_constraints: 169 raise ValueError, 'Item not added to canvas' 170 171 i_cons = self._canvas_constraints[item] 172 173 for cons in i_cons.values(): 174 for c in cons: 175 yield c 176 100 177 101 178 def remove_connections_to_item(self, item): … … 262 339 return connected_items 263 340 264 def get_matrix_i2 w(self, item, calculate=False):341 def get_matrix_i2c(self, item, calculate=False): 265 342 """ 266 343 Get the Item to World matrix for @item. … … 272 349 yet. Note that out-of-date matrices are not recalculated. 273 350 """ 274 try: 275 return item._canvas_matrix_i2w 276 except AttributeError, e: 277 if calculate: 278 self.update_matrix(item, recursive=False) 279 return item._canvas_matrix_i2w 280 else: 281 self.request_matrix_update(item) 282 raise e 283 284 def get_matrix_w2i(self, item, calculate=False): 351 if item._matrix_i2c is None or calculate: 352 self.update_matrix(item, recursive=False) 353 return item._matrix_i2c 354 355 356 def get_matrix_c2i(self, item, calculate=False): 285 357 """ 286 358 Get the World to Item matrix for @item. 287 359 See get_matrix_i2w(). 288 360 """ 289 try: 290 return item._canvas_matrix_w2i 291 except AttributeError, e: 292 if calculate: 293 self.update_matrix(item, recursive=False) 294 return item._canvas_matrix_w2i 295 else: 296 self.request_matrix_update(item) 297 raise e 361 if item._matrix_c2i is None or calculate: 362 self.update_matrix(item, recursive=False) 363 return item._matrix_c2i 364 298 365 299 366 @observed … … 314 381 0 315 382 """ 316 assert item in self.get_all_items()317 383 self._dirty_items.add(item) 318 384 self._dirty_matrix_items.add(item) … … 332 398 Schedule only the matrix to be updated. 333 399 """ 334 assert item in self.get_all_items()335 400 self._dirty_matrix_items.add(item) 336 401 self.update() … … 434 499 >>> c.add(ii, i) 435 500 >>> c.update_matrices() 436 >>> i._canvas_matrix_i2w501 >>> c.get_matrix_i2c(i) 437 502 cairo.Matrix(1, 0, 0, 1, 5, 0) 438 >>> ii._canvas_matrix_i2w503 >>> c.get_matrix_i2c(ii) 439 504 cairo.Matrix(1, 0, 0, 1, 5, 8) 440 505 >>> len(c._dirty_items) … … 463 528 return 464 529 else: 465 item._ canvas_matrix_i2w= Matrix(*item.matrix)466 item._ canvas_matrix_i2w *= parent._canvas_matrix_i2w530 item._matrix_i2c = Matrix(*item.matrix) 531 item._matrix_i2c *= self.get_matrix_i2c(parent) 467 532 else: 468 item._ canvas_matrix_i2w= Matrix(*item.matrix)533 item._matrix_i2c = Matrix(*item.matrix) 469 534 470 535 # It's nice to have the W2I matrix present too: 471 item._canvas_matrix_w2i = Matrix(*item._canvas_matrix_i2w) 472 item._canvas_matrix_w2i.invert() 536 item._matrix_c2i = Matrix(*item._matrix_i2c) 537 item._matrix_c2i.invert() 538 for v in self._registered_views: 539 v.update_matrix(item) 473 540 474 541 # Make sure handles are marked (for constraint solving) 475 542 request_resolve = self._solver.request_resolve 476 for h in item.handles(): 477 request_resolve(h.x) 478 request_resolve(h.y) 479 543 for c in self.canvas_constraints(item): 544 request_resolve(c) 545 480 546 if recursive: 481 547 for child in self._tree.get_children(item): 482 548 self.update_matrix(child) 549 483 550 484 551 def _update_handles(self, item): … … 523 590 h.y._value -= y 524 591 592 525 593 def register_view(self, view): 526 594 """ … … 529 597 """ 530 598 self._registered_views.add(view) 599 for item in self.get_all_items(): 600 view.update_matrix(item) 601 531 602 532 603 def unregister_view(self, view): … … 567 638 568 639 640 641 class CanvasProjector(Projector): 642 """ 643 Canvas constraint projector between item and canvas coordinates. 644 645 Attributes: 646 - _canvas: canvas reference 647 """ 648 def __init__(self, canvas): 649 super(CanvasProjector, self).__init__() 650 self._canvas = canvas 651 652 653 def _cproj(self, c, x=None, y=None, xy=None, **kw): 654 if xy is not None: 655 for point, item in xy.items(): 656 x, y = point 657 i2c = self._canvas.get_matrix_i2c(item).transform_point 658 x._value, y._value = i2c(x._value, y._value) 659 elif x is not None: 660 for v, item in x.items(): 661 i2c = self._canvas.get_matrix_i2c(item).transform_point 662 v._value, _ = i2c(v._value, 0) 663 elif y is not None: 664 for v, item in y.items(): 665 i2c = self._canvas.get_matrix_i2c(item).transform_point 666 _, v._value = i2c(0, v._value) 667 else: 668 raise AttributeError('Projection data not specified') 669 670 671 def _iproj(self, c, x=None, y=None, xy=None, **kw): 672 if xy is not None: 673 for point, item in xy.items(): 674 x, y = point 675 c2i = self._canvas.get_matrix_c2i(item).transform_point 676 x._value, y._value = c2i(x._value, y._value) 677 item.request_update() 678 elif x is not None: 679 for v, item in x.items(): 680 c2i = self._canvas.get_matrix_c2i(item).transform_point 681 v._value, _ = c2i(v._value, 0) 682 item.request_update() 683 elif y is not None: 684 for v, item in y.items(): 685 c2i = self._canvas.get_matrix_c2i(item).transform_point 686 _, v._value = c2i(0, v._value) 687 item.request_update() 688 else: 689 raise AttributeError('Projection data not specified') 690 691 569 692 # Additional tests in @observed methods 570 693 __test__ = { gaphas/trunk/gaphas/constraint.py
r1588 r1670 120 120 assert var in (self.a, self.b) 121 121 122 if var is self.a: 123 self.a.value = self.b.value 124 else: 125 self.b.value = self.a.value 122 if self.a.value != self.b.value: 123 if var is self.a: 124 self.a.value = self.b.value 125 else: 126 self.b.value = self.a.value 126 127 127 128 … … 285 286 args[nm] = v.value 286 287 if v is var: arg = nm 287 var.value = self._solve_for(arg, args) 288 v = self._solve_for(arg, args) 289 if var != v: 290 var.value = v 291 288 292 289 293 def _solve_for(self, arg, args): … … 337 341 338 342 339 class LineConstraint(Constraint):340 """341 Ensure a point is kept on a line, taking into account item342 specific coordinates.343 344 #>>> from solver import Variable345 #>>> a, b = Variable(3.0), Variable(2.0)346 #>>> lt = LessThanConstraint(smaller=a, bigger=b)347 #>>> lt.solve_for('smaller')348 #>>> a, b349 #(Variable(3, 20), Variable(3, 20))350 #>>> b.value = 0.8351 #>>> lt.solve_for('bigger')352 #>>> a, b353 #(Variable(0.8, 20), Variable(0.8, 20))354 """355 356 def __init__(self, canvas, connect_to_item, handle_1, handle_2,357 connected_item, connected_handle):358 super(LineConstraint, self).__init__(handle_1.x,359 handle_1.y,360 handle_2.x,361 handle_2.y,362 connected_handle.x,363 connected_handle.y)364 365 self._canvas = canvas366 self._connect_to_item = connect_to_item367 self._handle_1 = handle_1368 self._handle_2 = handle_2369 self._connected_item = connected_item370 self._connected_handle = connected_handle371 self.update_ratio()372 373 374 def update_ratio(self):375 """376 >>> from item import Handle, Item377 >>> from canvas import Canvas378 >>> c = Canvas()379 >>> i1, i2 = Item(), Item()380 >>> c.add(i1)381 >>> c.add(i2)382 >>> c.update_now()383 >>> h1, h2, h3 = Handle(0, 0), Handle(30, 20), Handle(15, 4)384 >>> eq = LineConstraint(c, i1, h1, h2, i2, h3)385 >>> eq.ratio_x, eq.ratio_y386 (0.5, 0.20000000000000001)387 >>> h2.pos = 40, 30388 >>> eq.solve_for(h3.x)389 >>> eq.ratio_x, eq.ratio_y390 (0.5, 0.20000000000000001)391 >>> h3.pos392 (Variable(20, 20), Variable(6, 20))393 """394 start = self._handle_1395 end = self._handle_2396 point = self._connected_handle397 398 get_i2w = self._canvas.get_matrix_i2w399 400 sx, sy = get_i2w(self._connect_to_item, calculate=True).transform_point(start.x, start.y)401 ex, ey = get_i2w(self._connect_to_item).transform_point(end.x, end.y)402 px, py = get_i2w(self._connected_item, calculate=True).transform_point(point.x, point.y)403 404 try:405 self.ratio_x = float(px - sx) / float(ex - sx)406 except ZeroDivisionError:407 self.ratio_x = 0.0408 try:409 self.ratio_y = float(py - sy) / float(ey - sy)410 except ZeroDivisionError:411 self.ratio_y = 0.0412 413 def solve_for(self, var=None):414 self._solve()415 416 def _solve(self):417 """418 Solve the equation for the connected_handle.419 >>> from item import Handle, Item420 >>> from canvas import Canvas421 >>> c = Canvas()422 >>> i1, i2 = Item(), Item()423 >>> c.add(i1)424 >>> c.add(i2)425 >>> c.update_now()426 >>> h1, h2, h3 = Handle(0, 0), Handle(30, 20), Handle(15, 4)427 >>> eq = LineConstraint(c, i1, h1, h2, i2, h3)428 >>> eq.solve_for(h3.x)429 >>> h3.pos430 (Variable(15, 20), Variable(4, 20))431 >>> h2.pos = 40, 30432 >>> eq.solve_for(h3.x)433 >>> h3.pos434 (Variable(20, 20), Variable(6, 20))435 >>> i2.matrix.translate(5,5)436 >>> i2.request_update()437 >>> c.update_now()438 >>> eq.solve_for(h3.x)439 >>> h3.pos440 (Variable(15, 20), Variable(1, 20))441 """442 start = self._handle_1443 end = self._handle_2444 point = self._connected_handle445 446 get_i2w = self._canvas.get_matrix_i2w447 get_w2i = self._canvas.get_matrix_w2i448 449 sx, sy = get_i2w(self._connect_to_item).transform_point(start.x, start.y)450 ex, ey = get_i2w(self._connect_to_item).transform_point(end.x, end.y)451 452 px = sx + (ex - sx) * self.ratio_x453 py = sy + (ey - sy) * self.ratio_y454 455 point.x.value, point.y.value = \456 get_w2i(self._connected_item).transform_point(px, py)457 # Need to queue a redraw of the manipulated item.458 self._canvas.request_update(self._connected_item)459 460 461 343 462 344 class BalanceConstraint(Constraint): … … 487 369 """ 488 370 489 def __init__(self, band=None, v=None ):371 def __init__(self, band=None, v=None, balance=None): 490 372 super(BalanceConstraint, self).__init__(band[0], band[1], v) 491 373 self.band = band 374 self.balance = balance 375 self.v = v 376 377 if self.balance is None: 378 self.update_balance() 379 380 381 def update_balance(self): 492 382 b1, b2 = self.band 493 383 w = b2 - b1 494 384 if w != 0: 495 self.balance = ( v - b1) / w385 self.balance = (self.v - b1) / w 496 386 else: 497 387 self.balance = 0 498 self.v = v499 388 500 389 … … 502 391 b1, b2 = self.band 503 392 w = b2 - b1 504 var.value = b1 + w * self.balance 393 value = b1 + w * self.balance 394 if var.value != value: 395 var.value = value 396 397 398 class LineConstraint(Constraint): 399 """ 400 Ensure a point is kept on a line. 401 402 Attributes: 403 - _line: line defined by tuple ((x1, y1), (x2, y2)) 404 - _point: point defined by tuple (x, y) 405 """ 406 407 def __init__(self, line, point): 408 super(LineConstraint, self).__init__(*(line[0] + line[1] + point)) 409 410 self._line = line 411 self._point = point 412 413 414 def update_ratio(self): 415 """ 416 >>> from gaphas.solver import Variable 417 >>> line = (Variable(0), Variable(0)), (Variable(30), Variable(20)) 418 >>> point = (Variable(15), Variable(4)) 419 >>> lc = LineConstraint(line=line, point=point) 420 >>> lc.update_ratio() 421 >>> lc.ratio_x, lc.ratio_y 422 (0.5, 0.20000000000000001) 423 >>> line[1][0].value = 40 424 >>> line[1][1].value = 30 425 >>> lc.solve_for(point[0]) 426 >>> lc.ratio_x, lc.ratio_y 427 (0.5, 0.20000000000000001) 428 >>> point 429 (Variable(20, 20), Variable(6, 20)) 430 """ 431 sx, sy = self._line[0] 432 ex, ey = self._line[1] 433 px, py = self._point 434 435 try: 436 self.ratio_x = float(px - sx) / float(ex - sx) 437 except ZeroDivisionError: 438 self.ratio_x = 0.0 439 try: 440 self.ratio_y = float(py - sy) / float(ey - sy) 441 except ZeroDivisionError: 442 self.ratio_y = 0.0 443 444 445 def solve_for(self, var=None): 446 self._solve() 447 448 def _solve(self): 449 """ 450 Solve the equation for the connected_handle. 451 >>> from gaphas.solver import Variable 452 >>> line = (Variable(0), Variable(0)), (Variable(30), Variable(20)) 453 >>> point = (Variable(15), Variable(4)) 454 >>> lc = LineConstraint(line=line, point=point) 455 >>> lc.update_ratio() 456 >>> lc.solve_for(point[0]) 457 >>> point 458 (Variable(15, 20), Variable(4, 20)) 459 >>> line[1][0].value = 40 460 >>> line[1][1].value = 30 461 >>> lc.solve_for(point[0]) 462 >>> point 463 (Variable(20, 20), Variable(6, 20)) 464 """ 465 sx, sy = self._line[0] 466 ex, ey = self._line[1] 467 px, py = self._point 468 469 x = sx + (ex - sx) * self.ratio_x 470 y = sy + (ey - sy) * self.ratio_y 471 472 if px.value != x: 473 px.value = x 474 if py.value != y: 475 py.value = y 476 477 478 479 class Projector(object): 480 """ 481 Base class for variable space projectors. 482 483 Variable space projectors allow to convert variables' values to common 484 space before constraint solving. 485 486 Consider two geometrical objects defined by their position (x0, y0) 487 and object's affine information (see gaphas.tests.test_projector module) 488 >>> from solver import Variable 489 >>> from gaphas.tests.test_projector import Rectangle, Vector, AffineProjector 490 >>> r = Rectangle(5, 5, 20, 20) 491 >>> v = Vector(5, 50, 5, -25) 492 493 To keep vector's terminal point on a rectangle's bottom side create 494 affine projector and appropriate constraints 495 >>> proj = AffineProjector() 496 >>> zero = Variable(0) # used for balance constraint band 497 >>> bc = BalanceConstraint(band=(zero, r.width), v=v.x, balance=0.25) 498 >>> eq = EqualsConstraint(a=r.height, b=v.y) 499 >>> proj(bc, data={v.x: v.x0, r.width: r.x0, zero: r.x0}) 500 >>> proj(eq, data={v.y: v.y0, r.height: r.y0}) 501 502 Let's change rectangle dimensions 503 >>> r.width.value = 24 504 >>> r.height.value = 25 505 506 and use constraints to keep vector's terminal point on rectangle bottom 507 side 508 >>> bc.solve_for(v.x) 509 >>> eq.solve_for(v.y) 510 >>> v.x, v.y 511 (Variable(6, 20), Variable(-20, 20)) 512 513 It is also possible to use projection in case of other constraint's 514 methods than 'solve_for' 515 >>> r = Rectangle(5, 5, 20, 20) 516 >>> v = Vector(10, 50, 5, -25) 517 >>> zero = Variable(0) 518 >>> bc = BalanceConstraint(band=(zero, r.width), v=v.x) 519 >>> proj(bc, data={v.x: v.x0, r.width: r.x0, zero: r.x0}, f=bc.update_balance) 520 >>> bc.update_balance() 521 >>> bc.balance 522 0.5 523 """ 524 def _cproj(self): 525 """ 526 Perform projection to common space. 527 """ 528 raise NotImplemented 529 530 531 def _iproj(self): 532 """ 533 Perform projection to internal space. 534 """ 535 raise NotImplemented 536 537 538 def __call__(self, c, *args, **kw): 539 """ 540 Decorator for Constraint.solve_for method to perform projection to 541 common space before variable solving and later project to internal 542 variable space. 543 """ 544 if 'f' in kw: 545 f = kw['f'] 546 fn = f.__name__ 547 else: 548 f = c.solve_for 549 fn = 'solve_for' 550 551 def wrapper(*fargs): 552 self._cproj(c, *args, **kw) 553 f(*fargs) 554 self._iproj(c, *args, **kw) 555 556 setattr(c, fn, wrapper) 505 557 506 558 gaphas/trunk/gaphas/decorators.py
r1304 r1670 138 138 return wrapper 139 139 140 class recursive(object): 141 """ 142 This decorator limits the recursion for a specific function 143 144 >>> class A(object): 145 ... def __init__(self): self.r = 0 146 ... @recursive(10) 147 ... def a(self, x=0): 148 ... self.r += 1 149 ... self.a() 150 >>> a = A() 151 >>> a.a() 152 >>> a.r 153 10 154 """ 155 156 def __init__(self, limit=10000): 157 self.limit = limit 158 159 def __call__(self, func): 160 def wrapper(*args, **kwargs): 161 try: 162 func._recursion_level += 1 163 except AttributeError: 164 # _recursion_level not present 165 func._recursion_level = 0 166 if func._recursion_level < self.limit: 167 try: 168 return func(*args, **kwargs) 169 finally: 170 func._recursion_level -= 1 171 return wrapper 172 140 173 141 174 if __name__ == '__main__': gaphas/trunk/gaphas/examples.py
r1518 r1670 11 11 from solver import solvable 12 12 import tool 13 from constraint import LineConstraint 13 from constraint import LineConstraint, LessThanConstraint, EqualsConstraint 14 14 from geometry import point_on_rectangle, distance_rectangle_point 15 from util import text_extents, text_align, text_multiline 15 from util import text_extents, text_align, text_multiline, path_ellipse 16 from cairo import Matrix 16 17 17 18 class Box(Element): … … 44 45 """ 45 46 h = self._handles 46 hnw = h[NW] 47 hse = h[SE] 48 x0, y0 = float(hnw.x), float(hnw.y) 49 x1, y1 = float(hse.x), float(hse.y) 50 r = (x0, y0, x1 - x0, y1 - y0) 47 h_se = h[SE] 48 r = (0, 0, h_se.x, h_se.y) 51 49 por = point_on_rectangle(r, (x, y), border=True) 52 #print 'Point', r, (x, y), por53 return distance_rectangle_point(r, (x, y)), por50 p = distance_rectangle_point(r, (x, y)) 51 return p, por 54 52 55 53 … … 82 80 83 81 82 class FatLine(Item): 83 def __init__(self): 84 super(FatLine, self).__init__() 85 self._handles.extend((Handle(), Handle())) 86 87 h1, h2 = self._handles 88 cons = self._constraints 89 cons.append(EqualsConstraint(a=h1.x, b=h2.x)) 90 cons.append(LessThanConstraint(smaller=h1.y, bigger=h2.y, delta=20)) 91 92 93 def _set_height(self, height): 94 h1, h2 = self._handles 95 h2.y = height 96 97 98 def _get_height(self): 99 h1, h2 = self._handles 100 return h2.y 101 102 103 height = property(_get_height, _set_height) 104 105 106 def draw(self, context): 107 cr = context.cairo 108 cr.set_line_width(10) 109 h1, h2 = self.handles() 110 cr.move_to(0, 0) 111 cr.line_to(0, self.height) 112 cr.stroke() 113 114 115 116 class Circle(Item): 117 def __init__(self): 118 super(Circle, self).__init__() 119 self._handles.extend((Handle(), Handle())) 120 121 122 def _set_radius(self, r): 123 h1, h2 = self._handles 124 h2.x = r 125 h2.y = r 126 127 128 def _get_radius(self): 129 h1, h2 = self._handles 130 return ((h2.x - h1.x) ** 2 + (h2.y - h1.y) ** 2) ** 0.5 131 132 radius = property(_get_radius, _set_radius) 133 134 135 def setup_canvas(self): 136 super(Circle, self).setup_canvas() 137 h1, h2 = self._handles 138 h1.movable = False 139 140 141 def draw(self, context): 142 cr = context.cairo 143 path_ellipse(cr, 0, 0, 2 * self.radius, 2 * self.radius) 144 cr.stroke() 145 146 84 147 class ConnectingHandleTool(tool.HandleTool): 85 148 """ … … 96 159 if not handle.connectable: 97 160 return 98 matrix_w2i = view.canvas.get_matrix_w2i99 matrix_i2w = view.canvas.get_matrix_i2w100 161 101 162 # Make glue distance depend on the zoom ratio (should be about 10 pixels) 102 glue_distance, dummy = view.transform_distance_c2w(10, 0) 163 inverse = Matrix(*view.matrix) 164 inverse.invert() 165 #glue_distance, dummy = inverse.transform_distance(10, 0) 166 glue_distance = 10 103 167 glue_point = None 104 168 glue_item = None 105 169 for i in view.canvas.get_all_items(): 106 170 if not i is item: 107 ix, iy = matrix_w2i(i).transform_point(wx, wy) 171 v2i = view.get_matrix_v2i(i).transform_point 172 ix, iy = v2i(wx, wy) 108 173 try: 109 174 distance, point = i.glue(item, handle, ix, iy) 110 # print distance, point111 175 # Transform distance to world coordinates 112 distance, dumy = matrix_i2w(i).transform_distance(distance, 0)176 #distance, dumy = matrix_i2w(i).transform_distance(distance, 0) 113 177 if distance <= glue_distance: 114 178 glue_distance = distance 115 glue_point = matrix_i2w(i).transform_point(*point) 179 i2v = view.get_matrix_i2v(i).transform_point 180 glue_point = i2v(*point) 116 181 glue_item = i 117 182 except AttributeError: 118 183 pass 119 184 if glue_point: 120 handle.x, handle.y = matrix_w2i(item).transform_point(*glue_point) 185 v2i = view.get_matrix_v2i(item).transform_point 186 handle.x, handle.y = v2i(*glue_point) 121 187 return glue_item 122 188 … … 131 197 """ 132 198 def side(handle, glued): 133 hx, hy = view.canvas.get_matrix_i2w(item).transform_point(handle.x, handle.y) 134 ax, ay = view.canvas.get_matrix_i2w(glued).transform_point(glued.handles()[0].x, glued.handles()[0].y) 135 bx, by = view.canvas.get_matrix_i2w(glued).transform_point(glued.handles()[2].x, glued.handles()[2].y) 199 handles = glued.handles() 200 hx, hy = view.get_matrix_i2v(item).transform_point(handle.x, handle.y) 201 ax, ay = view.get_matrix_i2v(glued).transform_point(handles[NW].x, handles[NW].y) 202 bx, by = view.get_matrix_i2v(glued).transform_point(handles[SE].x, handles[SE].y) 203 136 204 if abs(hx - ax) < 0.01: 137 side = 3205 return handles[NW], handles[SW] 138 206 elif abs(hy - ay) < 0.01: 139 side = 0207 return handles[NW], handles[NE] 140 208 elif abs(hx - bx) < 0.01: 141 side = 1209 return handles[NE], handles[SE] 142 210 else: 143 side = 2144 return side211 return handles[SW], handles[SE] 212 assert False 145 213 146 214 147 215 def handle_disconnect(): 148 216 try: 149 view.canvas. solver.remove_constraint(handle._connect_constraint)217 view.canvas.remove_canvas_constraint(item, handle) 150 218 except KeyError: 151 219 pass # constraint was alreasy removed 152 handle._connect_constraint = None153 220 handle.connected_to = None 154 221 # Remove disconnect handler: … … 158 225 glue_item = self.glue(view, item, handle, wx, wy) 159 226 if glue_item and glue_item is handle.connected_to: 160 s = side(handle, glue_item)161 227 try: 162 view.canvas. solver.remove_constraint(handle._connect_constraint)228 view.canvas.remove_canvas_constraint(item, handle) 163 229 except KeyError: 164 pass # constraint was alreasy removed 165 handle._connect_constraint = LineConstraint(view.canvas, glue_item, glue_item.handles()[s], glue_item.handles()[(s+1)%4], item, handle) 166 view.canvas.solver.add_constraint(handle._connect_constraint) 230 pass # constraint was already removed 231 232 h1, h2 = side(handle, glue_item) 233 lc = LineConstraint(line=(h1.pos, h2.pos), point=handle.pos) 234 pdata = { 235 h1.pos: glue_item, 236 h2.pos: glue_item, 237 handle.pos: item, 238 } 239 240 view.canvas.projector(lc, xy=pdata) 241 view.canvas.projector(lc, xy=pdata, f=lc.update_ratio) 242 lc.update_ratio() 243 view.canvas.add_canvas_constraint(item, handle, lc) 244 167 245 handle.disconnect = handle_disconnect 168 246 return … … 174 252 if glue_item: 175 253 if isinstance(glue_item, Box): 176 s = side(handle, glue_item) 254 h1, h2 = side(handle, glue_item) 255 177 256 # Make a constraint that keeps into account item coordinates. 178 handle._connect_constraint = LineConstraint(view.canvas, glue_item, glue_item.handles()[s], glue_item.handles()[(s+1)%4], item, handle) 179 view.canvas.solver.add_constraint(handle._connect_constraint) 257 lc = LineConstraint(line=(h1.pos, h2.pos), point=handle.pos) 258 pdata = { 259 h1.pos: glue_item, 260 h2.pos: glue_item, 261 handle.pos: item, 262 } 263 264 view.canvas.projector(lc, xy=pdata) 265 view.canvas.projector(lc, xy=pdata, f=lc.update_ratio) 266 lc.update_ratio() 267 view.canvas.add_canvas_constraint(item, handle, lc) 268 180 269 handle.connected_to = glue_item 181 270 handle.disconnect = handle_disconnect … … 184 273 if handle.connected_to: 185 274 #print 'Handle.disconnect', view, item, handle 186 view.canvas. solver.remove_constraint(handle._connect_constraint)275 view.canvas.remove_canvas_constraint(item, handle) 187 276 188 277 gaphas/trunk/gaphas/item.py
