Changeset 1670

Show
Ignore:
Timestamp:
07/18/07 11:13:12 (1 year ago)
Author:
wrobe..@pld-linux.org
Message:

- merged hw branch on trunk

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphas/trunk/demo.py

    r1562 r1670  
    2525import cairo 
    2626from gaphas import Canvas, GtkView, View 
    27 from gaphas.examples import Box, Text, DefaultExampleTool 
     27from gaphas.examples import Box, Text, FatLine, Circle, DefaultExampleTool 
    2828from gaphas.item import Line, NW, SE 
    2929from gaphas.tool import PlacementTool, HandleTool 
     
    300300    print 'box', b 
    301301    b.matrix=(1.0, 0.0, 0.0, 1, 20,20) 
    302     b.width=b.height = 40 
     302    b.width = b.height = 40 
    303303    c.add(b) 
    304304 
     
    307307    bb.matrix=(1.0, 0.0, 0.0, 1, 10,10) 
    308308    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) 
    310321 
    311322    # AJM: extra boxes: 
  • gaphas/trunk/demo_profile.py

    r1628 r1670  
    174174        c.add(bb, parent=b) 
    175175 
    176     for i in range(count): 
     176    for i in range(40): 
    177177        bb = MyBox() 
    178178        bb.width = bb.height = 15 
     
    210210 
    211211if __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) 
    220228 
    221229# vim: sw=4:et: 
  • gaphas/trunk/gaphas/canvas.py

    r1609 r1670  
    66__version__ = "$Revision$" 
    77# $HeadURL$ 
    8  
    98 
    109import cairo 
     
    1413from gaphas.decorators import nonrecursive, async, PRIORITY_HIGH_IDLE 
    1514from state import observed, reversible_method, reversible_pair 
     15from gaphas.constraint import Projector 
    1616 
    1717 
     
    3838class Canvas(object): 
    3939    """ 
    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 
    4146    """ 
    4247 
     
    4853 
    4954        self._registered_views = set() 
     55        self._canvas_constraints = {} 
     56 
     57        self.projector = CanvasProjector(self) 
    5058 
    5159    solver = property(lambda s: s._solver) 
     
    6876        item.canvas = self 
    6977        self._tree.add(item, parent) 
     78        self._canvas_constraints[item] = {} 
     79 
     80        for v in self._registered_views: 
     81            v.update_matrix(item) 
     82 
    7083        self.request_update(item) 
    7184        self._update_views((item,)) 
     85 
    7286 
    7387    @observed 
     
    92106        self._tree.remove(item) 
    93107        self.remove_connections_to_item(item) 
     108        del self._canvas_constraints[item] 
    94109        self._update_views((item,)) 
    95110        self._dirty_items.discard(item) 
     
    98113    reversible_pair(add, remove, 
    99114                    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 
    100177 
    101178    def remove_connections_to_item(self, item): 
     
    262339        return connected_items 
    263340 
    264     def get_matrix_i2w(self, item, calculate=False): 
     341    def get_matrix_i2c(self, item, calculate=False): 
    265342        """ 
    266343        Get the Item to World matrix for @item. 
     
    272349              yet. Note that out-of-date matrices are not recalculated. 
    273350        """ 
    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): 
    285357        """ 
    286358        Get the World to Item matrix for @item. 
    287359        See get_matrix_i2w(). 
    288360        """ 
    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 
    298365 
    299366    @observed 
     
    314381            0 
    315382        """ 
    316         assert item in self.get_all_items() 
    317383        self._dirty_items.add(item) 
    318384        self._dirty_matrix_items.add(item) 
     
    332398        Schedule only the matrix to be updated. 
    333399        """ 
    334         assert item in self.get_all_items() 
    335400        self._dirty_matrix_items.add(item) 
    336401        self.update() 
     
    434499            >>> c.add(ii, i) 
    435500            >>> c.update_matrices() 
    436             >>> i._canvas_matrix_i2w 
     501            >>> c.get_matrix_i2c(i) 
    437502            cairo.Matrix(1, 0, 0, 1, 5, 0) 
    438             >>> ii._canvas_matrix_i2w 
     503            >>> c.get_matrix_i2c(ii) 
    439504            cairo.Matrix(1, 0, 0, 1, 5, 8) 
    440505            >>> len(c._dirty_items) 
     
    463528                return 
    464529            else: 
    465                 item._canvas_matrix_i2w = Matrix(*item.matrix) 
    466                 item._canvas_matrix_i2w *= parent._canvas_matrix_i2w 
     530                item._matrix_i2c = Matrix(*item.matrix) 
     531                item._matrix_i2c *= self.get_matrix_i2c(parent) 
    467532        else: 
    468             item._canvas_matrix_i2w = Matrix(*item.matrix) 
     533            item._matrix_i2c = Matrix(*item.matrix) 
    469534 
    470535        # 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) 
    473540 
    474541        # Make sure handles are marked (for constraint solving) 
    475542        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             
    480546        if recursive: 
    481547            for child in self._tree.get_children(item): 
    482548                self.update_matrix(child) 
     549 
    483550 
    484551    def _update_handles(self, item): 
     
    523590                h.y._value -= y 
    524591 
     592 
    525593    def register_view(self, view): 
    526594        """ 
     
    529597        """ 
    530598        self._registered_views.add(view) 
     599        for item in self.get_all_items(): 
     600            view.update_matrix(item) 
     601 
    531602 
    532603    def unregister_view(self, view): 
     
    567638 
    568639 
     640 
     641class 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 
    569692# Additional tests in @observed methods 
    570693__test__ = { 
  • gaphas/trunk/gaphas/constraint.py

    r1588 r1670  
    120120        assert var in (self.a, self.b) 
    121121 
    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 
    126127 
    127128 
     
    285286            args[nm] = v.value 
    286287            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 
    288292 
    289293    def _solve_for(self, arg, args): 
     
    337341 
    338342 
    339 class LineConstraint(Constraint): 
    340     """ 
    341     Ensure a point is kept on a line, taking into account item 
    342     specific coordinates. 
    343  
    344     #>>> from solver import Variable 
    345     #>>> a, b = Variable(3.0), Variable(2.0) 
    346     #>>> lt = LessThanConstraint(smaller=a, bigger=b) 
    347     #>>> lt.solve_for('smaller') 
    348     #>>> a, b 
    349     #(Variable(3, 20), Variable(3, 20)) 
    350     #>>> b.value = 0.8 
    351     #>>> lt.solve_for('bigger') 
    352     #>>> a, b 
    353     #(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 = canvas 
    366         self._connect_to_item = connect_to_item 
    367         self._handle_1 = handle_1 
    368         self._handle_2 = handle_2 
    369         self._connected_item = connected_item 
    370         self._connected_handle = connected_handle 
    371         self.update_ratio() 
    372  
    373  
    374     def update_ratio(self): 
    375         """ 
    376         >>> from item import Handle, Item 
    377         >>> from canvas import Canvas 
    378         >>> 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_y 
    386         (0.5, 0.20000000000000001) 
    387         >>> h2.pos = 40, 30 
    388         >>> eq.solve_for(h3.x) 
    389         >>> eq.ratio_x, eq.ratio_y 
    390         (0.5, 0.20000000000000001) 
    391         >>> h3.pos 
    392         (Variable(20, 20), Variable(6, 20)) 
    393         """ 
    394         start = self._handle_1 
    395         end = self._handle_2 
    396         point = self._connected_handle 
    397  
    398         get_i2w = self._canvas.get_matrix_i2w 
    399  
    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.0 
    408         try: 
    409             self.ratio_y = float(py - sy) / float(ey - sy) 
    410         except ZeroDivisionError: 
    411             self.ratio_y = 0.0 
    412          
    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, Item 
    420         >>> from canvas import Canvas 
    421         >>> 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.pos 
    430         (Variable(15, 20), Variable(4, 20)) 
    431         >>> h2.pos = 40, 30 
    432         >>> eq.solve_for(h3.x) 
    433         >>> h3.pos 
    434         (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.pos 
    440         (Variable(15, 20), Variable(1, 20)) 
    441         """ 
    442         start = self._handle_1 
    443         end = self._handle_2 
    444         point = self._connected_handle 
    445  
    446         get_i2w = self._canvas.get_matrix_i2w 
    447         get_w2i = self._canvas.get_matrix_w2i 
    448  
    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_x 
    453         py = sy + (ey - sy) * self.ratio_y 
    454  
    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  
    461343 
    462344class BalanceConstraint(Constraint): 
     
    487369    """ 
    488370 
    489     def __init__(self, band=None, v=None): 
     371    def __init__(self, band=None, v=None, balance=None): 
    490372        super(BalanceConstraint, self).__init__(band[0], band[1], v) 
    491373        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): 
    492382        b1, b2 = self.band 
    493383        w = b2 - b1 
    494384        if w != 0: 
    495             self.balance = (v - b1) / w 
     385            self.balance = (self.v - b1) / w 
    496386        else: 
    497387            self.balance = 0 
    498         self.v = v 
    499388 
    500389 
     
    502391        b1, b2 = self.band 
    503392        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 
     398class 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 
     479class 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) 
    505557 
    506558 
  • gaphas/trunk/gaphas/decorators.py

    r1304 r1670  
    138138    return wrapper 
    139139 
     140class 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 
    140173 
    141174if __name__ == '__main__': 
  • gaphas/trunk/gaphas/examples.py

    r1518 r1670  
    1111from solver import solvable 
    1212import tool 
    13 from constraint import LineConstraint 
     13from constraint import LineConstraint, LessThanConstraint, EqualsConstraint 
    1414from geometry import point_on_rectangle, distance_rectangle_point 
    15 from util import text_extents, text_align, text_multiline 
     15from util import text_extents, text_align, text_multiline, path_ellipse 
     16from cairo import Matrix 
    1617 
    1718class Box(Element): 
     
    4445        """ 
    4546        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) 
    5149        por = point_on_rectangle(r, (x, y), border=True) 
    52         #print 'Point', r, (x, y), por 
    53         return distance_rectangle_point(r, (x, y)), por 
     50        p = distance_rectangle_point(r, (x, y)) 
     51        return p, por 
    5452 
    5553 
     
    8280 
    8381 
     82class 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 
     116class 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 
    84147class ConnectingHandleTool(tool.HandleTool): 
    85148    """ 
     
    96159        if not handle.connectable: 
    97160            return 
    98         matrix_w2i = view.canvas.get_matrix_w2i 
    99         matrix_i2w = view.canvas.get_matrix_i2w 
    100161 
    101162        # 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 
    103167        glue_point = None 
    104168        glue_item = None 
    105169        for i in view.canvas.get_all_items(): 
    106170            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) 
    108173                try: 
    109174                    distance, point = i.glue(item, handle, ix, iy) 
    110                     # print distance, point 
    111175                    # 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) 
    113177                    if distance <= glue_distance: 
    114178                        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) 
    116181                        glue_item = i 
    117182                except AttributeError: 
    118183                    pass 
    119184        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) 
    121187        return glue_item 
    122188 
     
    131197        """ 
    132198        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 
    136204            if abs(hx - ax) < 0.01: 
    137                 side = 3 
     205                return handles[NW], handles[SW] 
    138206            elif abs(hy - ay) < 0.01: 
    139                 side = 0 
     207                return handles[NW], handles[NE] 
    140208            elif abs(hx - bx) < 0.01: 
    141                 side = 1 
     209                return handles[NE], handles[SE] 
    142210            else: 
    143                 side = 2 
    144             return sid
     211                return handles[SW], handles[SE] 
     212            assert Fals
    145213 
    146214 
    147215        def handle_disconnect(): 
    148216            try: 
    149                 view.canvas.solver.remove_constraint(handle._connect_constraint
     217                view.canvas.remove_canvas_constraint(item, handle
    150218            except KeyError: 
    151219                pass # constraint was alreasy removed 
    152             handle._connect_constraint = None 
    153220            handle.connected_to = None 
    154221            # Remove disconnect handler: 
     
    158225        glue_item = self.glue(view, item, handle, wx, wy) 
    159226        if glue_item and glue_item is handle.connected_to: 
    160             s = side(handle, glue_item) 
    161227            try: 
    162                 view.canvas.solver.remove_constraint(handle._connect_constraint
     228                view.canvas.remove_canvas_constraint(item, handle
    163229            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 
    167245            handle.disconnect = handle_disconnect 
    168246            return 
     
    174252        if glue_item: 
    175253            if isinstance(glue_item, Box): 
    176                 s = side(handle, glue_item) 
     254                h1, h2 = side(handle, glue_item) 
     255 
    177256                # 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 
    180269                handle.connected_to = glue_item 
    181270                handle.disconnect = handle_disconnect 
     
    184273        if handle.connected_to: 
    185274            #print 'Handle.disconnect', view, item, handle 
    186             view.canvas.solver.remove_constraint(handle._connect_constraint
     275            view.canvas.remove_canvas_constraint(item, handle
    187276 
    188277 
  • gaphas/trunk/gaphas/item.py