Changeset 1611

Show
Ignore:
Timestamp:
07/10/07 16:07:16 (1 year ago)
Author:
wrobe..@pld-linux.org
Message:

- handles are represented in item's coordinates again
- calculate and cache item to view, item to canvas and opposite matrices

Files:

Legend:

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

    r1597 r1611  
    321321 
    322322#   # AJM: extra boxes: 
    323 #   bb=Box() 
    324 #   print 'box', bb 
    325 #   bb.matrix.rotate(math.pi/4.) 
    326 #   c.add(bb, parent=b) 
     323    bb=Box() 
     324    print 'box', bb 
     325    bb.matrix.rotate(math.pi/4.) 
     326    c.add(bb, parent=b) 
    327327#   for i in xrange(1): 
    328328#       bb=Box() 
     
    336336 
    337337    l=MyLine() 
    338     h = l.handles() 
    339     x, y = h[0].pos 
    340     h[0].x = 30 
    341     h[0].y = 30 
    342     h[1].x += h[0].x + 30 
    343     h[1].y += h[0].y + 30 
    344338    l.split_segment(0, 3) 
    345339    l.matrix.translate(30, 30) 
  • gaphas/branches/hw/gaphas/canvas.py

    r1597 r1611  
    77# $HeadURL$ 
    88 
     9from weakref import WeakKeyDictionary 
    910 
    1011import cairo 
     
    1213from gaphas import tree 
    1314from gaphas import solver 
    14 from gaphas.decorators import async, PRIORITY_HIGH_IDLE 
     15from gaphas.decorators import nonrecursive, async, PRIORITY_HIGH_IDLE 
    1516from state import observed, reversible_method, reversible_pair 
    1617 
     
    3637 
    3738 
     39class ViewBucket(object): 
     40    __slots__ = ('matrix_v2i', 'matrix_i2v', 'handles') 
     41    def __init__(self): 
     42        self.matrix_v2i = None 
     43        self.matrix_i2v = None 
     44        self.handles = [] 
     45 
     46 
     47class CanvasBucket(object): 
     48    __slots__ = ('matrix_c2i', 'matrix_i2c', 'view', 'handles') 
     49    def __init__(self): 
     50        self.matrix_c2i = None 
     51        self.matrix_i2c = None 
     52        self.view = {} 
     53        self.handles = [] 
     54 
     55 
     56 
    3857class Canvas(object): 
    3958    """ 
    4059    Container class for Items. 
     60 
     61    Attributes: 
     62     - _cache: additional cache of item data 
    4163    """ 
    4264 
     
    4668        self._dirty_items = set() 
    4769        self._dirty_matrix_items = set() 
    48         self._in_update = False 
    4970 
    5071        self._registered_views = set() 
     72        self._cache = WeakKeyDictionary() 
    5173 
    5274    solver = property(lambda s: s._solver) 
     
    6991        item.canvas = self 
    7092        self._tree.add(item, parent) 
     93 
     94        self._cache[item] = CanvasBucket() 
     95        for v in self._registered_views: 
     96            self._cache[item].view[v] = ViewBucket() 
     97            v.update_matrix(item) 
     98 
    7199        self.request_update(item) 
    72100        self._update_views((item,)) 
     101 
    73102 
    74103    @observed 
     
    259288        return connected_items 
    260289 
    261     def get_matrix_i2w(self, item, calculate=False): 
     290    def get_matrix_i2c(self, item, calculate=False): 
    262291        """ 
    263292        Get the Item to World matrix for @item. 
     
    269298              yet. Note that out-of-date matrices are not recalculated. 
    270299        """ 
    271         try: 
    272             return item._canvas_matrix_i2w 
    273         except AttributeError, e: 
    274             if calculate: 
    275                 self.update_matrix(item, recursive=False) 
    276                 return item._canvas_matrix_i2w 
    277             else: 
    278                 self.request_matrix_update(item) 
    279                 raise e 
    280  
    281     def get_matrix_w2i(self, item, calculate=False): 
     300        data = self._cache[item] 
     301        if data.matrix_i2c is None or calculate: 
     302            self.update_matrix(item, recursive=False) 
     303        return data.matrix_i2c 
     304 
     305 
     306    def get_matrix_c2i(self, item, calculate=False): 
    282307        """ 
    283308        Get the World to Item matrix for @item. 
    284309        See get_matrix_i2w(). 
    285310        """ 
    286         try: 
    287             return item._canvas_matrix_w2i 
    288         except AttributeError, e: 
    289             if calculate: 
    290                 self.update_matrix(item, recursive=False) 
    291                 return item._canvas_matrix_w2i 
    292             else: 
    293                 self.request_matrix_update(item) 
    294                 raise e 
     311        data = self._cache[item] 
     312        if data.matrix_c2i is None or calculate: 
     313            self.update_matrix(item, recursive=False) 
     314        return data.matrix_c2i 
     315 
    295316 
    296317    @observed 
     
    357378        update job is scheduled as idle job. 
    358379        """ 
    359         if not self._in_update: 
    360             self.update_now() 
    361  
     380        self.update_now() 
     381 
     382    @nonrecursive 
    362383    def update_now(self): 
    363384        """ 
    364385        Peform an update of the items that requested an update. 
    365386        """ 
    366         self._in_update = True 
    367  
    368387        # Order the dirty items, so they are updated bottom to top 
    369388        dirty_items = [ item for item in reversed(self._tree.nodes) \ 
     
    395414            dirty_matrix_items.update(self._dirty_matrix_items) 
    396415            self.update_matrices() 
     416 
     417            # Also need to set up the dirty_items list here, since items 
     418            # may be marked as dirty during maxtrix update or solving. 
     419            dirty_items = [ item for item in reversed(self._tree.nodes) \ 
     420                                 if item in self._dirty_items ] 
    397421 
    398422            for item in dirty_items: 
     
    411435            self._update_views(self._dirty_items, dirty_matrix_items) 
    412436            self._dirty_items.clear() 
    413             self._in_update = False 
    414437 
    415438    def update_matrices(self): 
     
    450473        self._dirty_matrix_items.discard(item) 
    451474 
     475        data = self._cache[item] 
    452476        if parent: 
    453477            if parent in self._dirty_matrix_items: 
     
    456480                return 
    457481            else: 
    458                 item._canvas_matrix_i2w = Matrix(*item.matrix) 
    459                 item._canvas_matrix_i2w *= parent._canvas_matrix_i2w 
     482                data.matrix_i2c = Matrix(*item.matrix) 
     483                data.matrix_i2c *= self.get_matrix_i2c(parent) 
    460484        else: 
    461             item._canvas_matrix_i2w = Matrix(*item.matrix) 
     485            data.matrix_i2c = Matrix(*item.matrix) 
    462486 
    463487        # It's nice to have the W2I matrix present too: 
    464         item._canvas_matrix_w2i = Matrix(*item._canvas_matrix_i2w) 
    465         item._canvas_matrix_w2i.invert() 
     488        data.matrix_c2i = Matrix(*data.matrix_i2c) 
     489        data.matrix_c2i.invert() 
     490        for v in self._registered_views: 
     491            v.update_matrix(item) 
    466492 
    467493        # Make sure handles are marked (for constraint solving) 
     
    471497            request_resolve(h.y) 
    472498             
    473  
    474499        if recursive: 
    475500            for child in self._tree.get_children(item): 
    476501                self.update_matrix(child) 
     502 
    477503 
    478504    def _update_handles(self, item): 
     
    515541            item.matrix.translate(0, y) 
    516542            for h in handles: 
    517                 h.y -= y 
     543                h.y._value -= y 
     544 
    518545 
    519546    def register_view(self, view): 
     
    523550        """ 
    524551        self._registered_views.add(view) 
     552        for item in self.get_all_items(): 
     553            data = ViewBucket() 
     554            self._cache[item].view[view] = data 
     555            view.update_matrix(item) 
     556 
    525557 
    526558    def unregister_view(self, view): 
  • gaphas/branches/hw/gaphas/examples.py

    r1557 r1611  
    2727        #print 'Box.draw', self 
    2828        c = context.cairo 
    29         nw = self._handles[NW] 
    3029        c.rectangle(0, 0, self.width, self.height) 
    3130        if context.hovered: 
     
    9089    def _set_height(self, height): 
    9190        h1, h2 = self._handles 
    92         h2.y = h1.y + height 
     91        h2.y = height 
    9392 
    9493 
    9594    def _get_height(self): 
    9695        h1, h2 = self._handles 
    97         return h2.y - h1.y 
     96        return h2.y 
    9897 
    9998 
     
    115114        cr = context.cairo 
    116115        cr.set_line_width(10) 
     116        h1, h2 = self.handles() 
    117117        cr.move_to(0, 0) 
    118118        cr.line_to(0, self.height) 
     
    129129    def _set_radius(self, r): 
    130130        h1, h2 = self._handles 
    131         h2.x = h1.x +
    132         h2.y = h1.y +
     131        h2.x =
     132        h2.y =
    133133 
    134134 
     
    137137        return ((h2.x - h1.x) ** 2 + (h2.y - h1.y) ** 2) ** 0.5 
    138138 
    139  
    140139    radius = property(_get_radius, _set_radius) 
    141  
    142  
    143     def _get_pos(self): 
    144         h1, h2 = self._handles 
    145         x, y = h2.x - h1.x, h2.y - h1.y 
    146         if x > 0: 
    147             x = 0 
    148         if y > 0: 
    149             y = 0 
    150         return abs(x), abs(y) 
    151  
    152  
    153     pos = property(_get_pos) 
    154140 
    155141 
     
    162148    def draw(self, context): 
    163149        cr = context.cairo 
    164         x, y = self.pos 
    165         path_ellipse(cr, x, y, 2 * self.radius, 2 * self.radius) 
     150        path_ellipse(cr, 0, 0, 2 * self.radius, 2 * self.radius) 
    166151        cr.stroke() 
    167152 
  • gaphas/branches/hw/gaphas/item.py

    r1597 r1611  
    358358        self._constraints = [ 
    359359            add(eq(a=h_nw.y, b=h_ne.y)), 
    360             add(eq(a=h_sw.y, b=h_se.y)), 
    361360            add(eq(a=h_nw.x, b=h_sw.x)), 
    362             add(eq(a=h_ne.x, b=h_se.x)), 
     361            add(eq(a=h_se.y, b=h_sw.y)), 
     362            add(eq(a=h_se.x, b=h_ne.x)), 
    363363            # set h_nw < h_se and h_sw < h_ne constraints 
    364364            # with minimal size functionality 
    365             add(lt(smaller=h_nw.x, bigger=h_se.x, delta=10)), 
    366             add(lt(smaller=h_nw.y, bigger=h_se.y, delta=10)), 
    367             add(lt(smaller=h_sw.x, bigger=h_ne.x, delta=10)), 
    368             add(lt(smaller=h_ne.y, bigger=h_sw.y, delta=10)), 
     365            add(lt(smaller=h_nw.y, bigger=h_se.y, delta=30)), 
     366#            add(lt(smaller=h_ne.y, bigger=h_sw.y, delta=30)), 
     367            add(lt(smaller=h_nw.x, bigger=h_se.x, delta=30)), 
     368#            add(lt(smaller=h_sw.x, bigger=h_ne.x, delta=30)), 
    369369        ] 
    370370 
     
    658658        See Item.draw(context). 
    659659        """ 
    660         m = self._canvas.get_matrix_w2i(self) 
    661  
    662660        def draw_line_end(handle, angle, draw): 
    663661            cr = context.cairo 
    664662            cr.save() 
    665663            try: 
    666                 cr.translate(*m.transform_point(handle.x, handle.y)
     664                cr.translate(handle.x, handle.y
    667665                cr.rotate(angle) 
    668666                draw(context) 
     
    674672        draw_line_end(self._handles[0], self._head_angle, self.draw_head) 
    675673        h = self._handles[0] 
    676         cr.move_to(*m.transform_point(h.x, h.y)
     674        cr.move_to(h.x, h.y
    677675        for h in self._handles[1:]: 
    678             cr.line_to(*m.transform_point(h.x, h.y)
     676            cr.line_to(h.x, h.y
    679677        h0, h1 = self._handles[-2:] 
    680678        draw_line_end(self._handles[-1], self._tail_angle, self.draw_tail) 
  • gaphas/branches/hw/gaphas/painter.py

    r1597 r1611  
    9595        try: 
    9696            cairo.set_matrix(view.matrix) 
    97             cairo.transform(view.canvas.get_matrix_i2w(item)) 
     97            cairo.transform(view.canvas.get_matrix_i2c(item)) 
    9898 
    9999            item.draw(DrawContext(painter=self, 
     
    248248    draw_all = True 
    249249 
    250     def _draw_handles(self, item, view, cairo): 
    251         """ 
    252         Update the bounding box with handle's position. 
    253         """ 
    254         cairo.save() 
    255         try: 
    256             m = Matrix(*view.canvas.get_matrix_i2w(item)) 
    257             m *= view._matrix 
    258  
    259             for h in item.handles(): 
    260                 cairo.identity_matrix() 
    261                 cairo.translate(*m.transform_point(h.x, h.y)) 
    262                 cairo.translate(h.x, h.y) 
    263                 cairo.rectangle(-5, -5, 9, 9) 
    264                 cairo.fill() 
    265         finally: 
    266             cairo.restore() 
    267  
    268  
    269250    def _draw_item(self, item, view, cairo, area=None): 
    270251        cairo = CairoBoundingBoxContext(cairo) 
    271252        super(BoundingBoxPainter, self)._draw_item(item, view, cairo) 
    272253        bounds = cairo.get_bounds() 
    273    
     254 
    274255        # Update bounding box with handles. 
    275         #transform_i2c = (view.canvas.get_matrix_i2w(item) * view._matrix).transform_point 
    276         transform_i2c = view.canvas.get_matrix_i2w(item).transform_point 
     256        i2v = view.get_matrix_i2v(item).transform_point 
     257        i2v = (view.canvas.get_matrix_i2c(item) * view._matrix).transform_point 
    277258        for h in item.handles(): 
    278             cx, cy = transform_i2c(h.x, h.y) 
     259            cx, cy = i2v(h.x, h.y) 
    279260            bounds += (cx - 5, cy - 5, 9, 9) 
    280261 
     
    309290        """ 
    310291        cairo.save() 
    311         m = Matrix(*view.canvas.get_matrix_i2w(item)) 
    312         m *= view._matrix 
     292        i2v = view.get_matrix_i2v(item) 
    313293        if not opacity: 
    314294            opacity = (item is view.focused_item) and .7 or .4 
     
    328308            cairo.identity_matrix() 
    329309            cairo.set_antialias(ANTIALIAS_NONE) 
    330             cairo.translate(h.x, h.y
     310            cairo.translate(*i2v.transform_point(h.x, h.y)
    331311            cairo.rectangle(-4, -4, 8, 8) 
    332312            cairo.set_source_rgba(r, g, b, opacity) 
  • gaphas/branches/hw/gaphas/tool.py

    r1597 r1611  
    330330 
    331331            # Calculate the distance the item has to be moved 
    332             dx, dy = view.transform_distance_c2w(event.x - self.last_x, 
    333                                                  event.y - self.last_y) 
    334  
    335             get_matrix_w2i = canvas.get_matrix_w2i 
     332            dx, dy = event.x - self.last_x, event.y - self.last_y 
    336333 
    337334            # Now do the actual moving. 
    338335            for i in self._movable_items: 
    339336                # Move the item and schedule it for an update 
    340                 i.matrix.translate(*get_matrix_w2i(i).transform_distance(dx, dy)) 
    341                 for h in i.handles(): 
    342                     h.x += dx 
    343                     h.y += dy 
     337                v2i = view.get_matrix_v2i(i) 
     338                i.matrix.translate(*v2i.transform_distance(dx, dy)) 
    344339                canvas.request_matrix_update(i) 
    345340 
     
    377372        Find item's handle at (event.x, event.y) 
    378373        """ 
    379         transform_point = view.canvas.get_matrix_i2w(item).transform_point 
    380         e_x, e_y = view.transform_point_c2w(event.x, event.y) 
    381         dx, dy = view.transform_distance_c2w(6, 6) 
     374        i2v = view.get_matrix_i2v(item).transform_point 
     375        x, y = event.x, event.y 
    382376        for h in item.handles(): 
    383377            if not h.movable: 
    384378                continue 
    385             #wx, wy = transform_point(h.x, h.y) 
    386             wx, wy = h.x, h.y 
    387             if -dx < (wx - e_x) < dx and -dy < (wy - e_y) < dy: 
     379            wx, wy = i2v(h.x, h.y) 
     380            if -6 < (wx - x) < 6 and -6 < (wy - y) < 6: 
    388381                return h 
    389382        return None 
     
    432425    def move(self, view, item, handle, x, y): 
    433426        """ 
    434         Move the handle to position (x,y). If handle changed the position 
    435         of an item, then move the item, too. 
    436         """ 
    437         handle.x += x 
    438         handle.y += y 
    439  
    440         # calculate current position 
    441         matrix_w2i = view.canvas.get_matrix_w2i(item) 
    442         x1 = min(h.x for h in item.handles()) 
    443         y1 = min(h.y for h in item.handles()) 
    444         dx, dy = matrix_w2i.transform_point(x1, y1) 
    445         item.matrix.translate(dx, dy) 
    446  
     427        Move the handle to position (x,y). 
     428        """ 
     429        handle.x = x 
     430        handle.y = y 
    447431 
    448432 
     
    472456        item, handle = self.find_handle(view, event) 
    473457        if handle: 
    474             self.last_x, self.last_y = event.x, event.y 
    475458            # Deselect all items unless CTRL or SHIFT is pressed 
    476459            # or the item is already selected. 
     
    525508            view.queue_draw_item(item) 
    526509 
    527             dx, dy = view.transform_distance_c2w(event.x - self.last_x, 
    528                                                  event.y - self.last_y) 
    529             wx, wy = view.transform_point_c2w(event.x, event.y) 
     510            v2i = view.get_matrix_v2i(item) 
     511            x, y = v2i.transform_point(event.x, event.y) 
    530512 
    531513            # Do the actual move: 
    532             self.move(view, item, handle, dx, dy) 
     514            self.move(view, item, handle, x, y) 
    533515             
    534516            item.request_update() 
     
    536518            try: 
    537519                if self._grabbed_handle.connectable: 
    538                     self.glue(view, item, handle, wx, wy) 
     520                    self.glue(view, item, handle, x, y) 
    539521            finally: 
    540522                pass 
    541             self.last_x, self.last_y = event.x, event.y 
    542523            return True 
    543524        else: 
     
    620601        x, y = view.transform_point_c2w(x, y) 
    621602        item.matrix.translate(x, y) 
    622         h = item.handles()[0] 
    623         h.x = x 
    624         h.y = y 
    625603        return item 
    626604 
  • gaphas/branches/hw/gaphas/view.py

    r1597 r1611  
    199199 
    200200            if point in self.get_item_bounding_box(item): 
    201                 inverse = Matrix(*self._matrix) 
    202                 inverse.invert() 
    203                 wx, wy = inverse.transform_point(x, y) 
    204                 ix, iy = self._canvas.get_matrix_w2i(item).transform_point(wx, wy) 
     201                v2i = self.get_matrix_v2i(item) 
     202                ix, iy = v2i.transform_point(x, y) 
    205203                if item.point(ix, iy) < 0.5: 
    206204                    return item 
     
    224222        # Make sure everything's updated 
    225223        self.request_update(self._canvas.get_all_items()) 
     224        for i in self._canvas.get_all_items(): 
     225            self.update_matrix(i) 
    226226 
    227227    def set_item_bounding_box(self, item, bounds): 
     
    236236        # bounding boxes are calculated. Now, the child objects should not 
    237237        # be hindered by their own matrix settings. 
    238         ix0, iy0 = self.transform_point_c2i(item, bounds.x, bounds.y) 
    239         ix1, iy1 = self.transform_point_c2i(item, bounds.x1, bounds.y1) 
     238        c2i = self._canvas.get_matrix_c2i(item).transform_point 
     239        ix0, iy0 = c2i(bounds.x, bounds.y) 
     240        ix1, iy1 = c2i(bounds.x1, bounds.y1) 
    240241        self._item_bounds[item] = bounds, Rectangle(ix0, iy0, x1=ix1, y1=iy1) 
    241242 
     
    260261        inverse = Matrix(*self._matrix) 
    261262        inverse.invert() 
    262         ww, wh = self.transform_point_c2w(self._bounds.x1, self._bounds.y1) 
     263        ww, wh = inverse.transform_point(self._bounds.x1, self._bounds.y1) 
    263264        return self._matrix.transform_distance(ww, wh) 
    264265 
     
    289290                                    area=None)) 
    290291 
    291     def transform_distance_c2w(self, x, y): 
    292         """ 
    293         Transform a point from canvas to world coordinates. 
    294         """ 
    295         inverse = Matrix(*self._matrix) 
    296         inverse.invert() 
    297         return inverse.transform_distance(x, y) 
    298  
    299     def transform_distance_w2c(self, x, y): 
    300         """ 
    301         Transform a distance from world to canvas coordinates. 
    302         """ 
    303         return self._matrix.transform_distance(x, y) 
    304  
    305     def transform_point_c2w(self, x, y): 
    306         """ 
    307         Transform a distance from canvas to world coordinates. 
    308         """ 
    309         inverse = Matrix(*self._matrix) 
    310         inverse.invert() 
    311         return inverse.transform_point(x, y) 
    312  
    313     def transform_point_w2c(self, x, y): 
    314         """ 
    315         Transform a point from world to canvas coordinates. 
    316         """ 
    317         return self._matrix.transform_point(x, y) 
    318  
    319     def transform_point_c2i(self, item, x, y): 
    320         """ 
    321         Transforma point from canvas to item coordinates. 
    322         """ 
    323         assert self.canvas 
    324         wx, wy = self.transform_point_c2w(x, y) 
    325         return self._canvas.get_matrix_w2i(item).transform_point(wx, wy) 
    326  
    327     def transform_point_i2c(self, item, x, y): 
    328         """ 
    329         Transform a point from item coordinates to canvas coordinates. 
    330         """ 
    331         assert self.canvas 
    332         wx, wy = self._canvas.get_matrix_i2w(item).transform_point(x, y) 
    333         return self.transform_point_w2c(wx, wy) 
     292    def get_matrix_i2v(self, item): 
     293        return self._canvas._cache[item].view[self].matrix_i2v 
     294 
     295    def get_matrix_v2i(self, item): 
     296        return self._canvas._cache[item].view[self].matrix_v2i 
     297 
     298 
     299    def update_matrix(self, item): 
     300        """ 
     301        Update item matrices related to view. 
     302        """ 
     303        v = self._canvas._cache[item].view[self] 
     304        v.matrix_i2v = self._canvas.get_matrix_i2c(item) * self._matrix 
     305        v.matrix_v2i = Matrix(*v.matrix_i2v) 
     306        v.matrix_v2i.invert() 
     307 
    334308 
    335309 
     
    562536                else: 
    563537                    self.queue_draw_item(i) 
    564                     x0, y0 = self.transform_point_i2c(i, bounds.x, bounds.y) 
    565                     x1, y1 = self.transform_point_i2c(i, bounds.x1, bounds.y1) 
     538 
     539                    i2c = self._canvas.get_matrix_i2c(i).transform_point 
     540                    x0, y0 = i2c(bounds.x, bounds.y) 
     541                    x1, y1 = i2c(bounds.x1, bounds.y1) 
    566542                    cbounds = Rectangle(x0, y0, x1=x1, y1=y1) 
    567543                    self._item_bounds[i] = cbounds, bounds