changeset 873:881efcd8a18f abs_n_rel_center

Merge from default branch
author Thinker K.F. Li <thinker@codemud.net>
date Fri, 24 Sep 2010 16:06:42 +0800
parents 3ce9daa9558b (diff) bcc63b20d5c6 (current diff)
children ec8d7e9c9642
files nodejs/mbapp.js nodejs/svg.js nodejs/testdesktop.js src/redraw_man.c
diffstat 7 files changed, 591 insertions(+), 201 deletions(-) [+]
line wrap: on
line diff
--- a/include/mb_types.h	Fri Sep 24 16:00:04 2010 +0800
+++ b/include/mb_types.h	Fri Sep 24 16:06:42 2010 +0800
@@ -175,6 +175,12 @@
                                  *   cached. */
     area_t *pcache_cur_area;	/*!< Current area for parent cached. */
     area_t *pcache_last_area;	/*!< Last area for parent cached. */
+    co_aix cache_2_pdev[6];	/*!< Transfrom matrix from space of
+				 * cached one to its parent. */
+    co_aix cache_2_pdev_rev[6];	/*!< Reverse of cache_2_pdev. */
+    co_aix aggr_2_pdev[6];	/*!< Aggregation of cache_2_pdev from root  */
+    co_aix aggr_2_pdev_rev[6];	/*!< Aggregation of cache_2_pdev_rev
+				 * from root  */
 } coord_canvas_info_t;
 
 /*! \brief A coordination system.
@@ -367,6 +373,10 @@
 #define _coord_get_dirty_areas(coord) (&(coord)->canvas_info->dirty_areas)
 #define _coord_get_aggr_dirty_areas(coord)	\
     ((coord)->canvas_info->aggr_dirty_areas)
+#define coord_get_2pdev(coord) ((coord)->canvas_info->cache_2_pdev)
+#define coord_get_2pdev_rev(coord) ((coord)->canvas_info->cache_2_pdev_rev)
+#define coord_get_aggr2pdev(coord) ((coord)->canvas_info->aggr_2_pdev)
+#define coord_get_aggr2pdev_rev(coord) ((coord)->canvas_info->aggr_2_pdev_rev)
 
 /* @} */
 
--- a/nodejs/animate.js	Fri Sep 24 16:00:04 2010 +0800
+++ b/nodejs/animate.js	Fri Sep 24 16:06:42 2010 +0800
@@ -11,6 +11,7 @@
 
 function linear_draw() {
     var percent;
+    var x, y;
     
     percent = (Date.now() - this._start_tm) / this.duration;
     if(percent >= 1) {
@@ -20,8 +21,9 @@
 	    delete this.obj.timer;
 	}
     }
-    this.obj.x = (this.targetx-this.startposx)*percent+this.startposx;
-    this.obj.y = (this.targety-this.startposy)*percent+this.startposy;
+    x = (this.targetx-this.startposx)*percent+this.startposx;
+    y = (this.targety-this.startposy)*percent+this.startposy;
+    this.obj.center.move(x, y);
     this.app.refresh();
 }
 
@@ -41,10 +43,10 @@
     this.app = app;
     this.obj = obj;
     this.end = 0;
-    this.targetx = shiftx + obj.x;
-    this.targety = shifty + obj.y;
-    this.startposx = obj.x;
-    this.startposy = obj.y;
+    this.targetx = shiftx + obj.center.x;
+    this.targety = shifty + obj.center.y;
+    this.startposx = obj.center.x;
+    this.startposy = obj.center.y;
     this.duration = duration*1000;
 }
 
@@ -64,6 +66,9 @@
     var obj = this._obj;
     var self = this;
     
+    if(obj.timer)
+	obj.timer.stop();
+
     this._start_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
     this._start_tm = Date.now();
     obj.timer = setInterval(function() { self.draw(); }, frame_interval);
@@ -123,50 +128,58 @@
     if (this.end == 1) return;
     var percent = (Date.now() - this.starttime)/this.duration;
     if (percent > 1) percent = 1;
-    var sx = (this.targetx-this.startsx)*percent+this.startsx;
-    var sy = (this.targety-this.startsy)*percent+this.startsy;
-    var t=[sx,0,0,0,sy,0];
-    this.obj[0] = sx;
-    this.obj[4] = sy;
-    this.obj[2] = this.origin_offset_x - (sx-this.startsx)*this.obj.center.x;
-    this.obj[5] = this.origin_offset_y - (sy-this.startsy)*this.obj.center.y;
+    var sx = 1 + (this.totalsx - 1) * percent;
+    var sy = 1 + (this.totalsy - 1) * percent;
+    var sh1 = [1, 0, -this.center_x, 0, 1, -this.center_y];
+    var sh2 = [1, 0, this.center_x, 0, 1, this.center_y];
+    var scale=[sx, 0, 0, 0, sy, 0];
+    var obj = this.obj;
+    var mtx;
+
+    mtx = multiply(scale, sh1);
+    mtx = multiply(sh2, mtx);
+    mtx = multiply(this.orig_mtx, mtx);
+    obj[0] = mtx[0];
+    obj[1] = mtx[1];
+    obj[2] = mtx[2];
+    obj[3] = mtx[3];
+    obj[4] = mtx[4];
+    obj[5] = mtx[5];
 
     this.app.refresh();
     var self = this;
     if (percent < 1) {
-	this.obj.timer=setTimeout(function() { self.draw();}, frame_interval);
+	obj.timer=setTimeout(function() { self.draw();}, frame_interval);
 	return;
     }
     this.app.refresh();
-    this.obj.animated_scale = null;
+    obj.animated_scale = null;
 }
 
-function scale(app,obj,targetx,targety, duration) {
+function scale(app, obj, fact_x, fact_y, duration) {
+    var bbox;
+    
     try {
         if (obj.animated_scale) {
-	    //obj[0] = obj.animated_scale.targetx;
-	    //obj[4] = obj.animated_scale.targety;
-	    //obj[2] = obj.animated_scale.final_offset_x;
-	    //obj[5] = obj.aninated_scale.final_offset_y;
 	    obj.animated_scale.end = 1;
 	}
     } catch(e) {
 	    
     }
+
+    bbox = obj.bbox;
+    bbox.update();
     obj.animated_scale = this;
     this.app = app;
     this.obj = obj;
     this.end = 0;
     this.starttime = Date.now();
-    this.startsx = obj[0];
-    this.startsy = obj[4];
-    this.targetx = targetx;
-    this.targety = targety;
+    this.totalsx = fact_x * bbox.orig.width / bbox.width;
+    this.totalsy = fact_y * bbox.orig.height / bbox.height;
     this.duration = duration*1000;
-    this.origin_offset_x = obj[2];
-    this.origin_offset_y = obj[5];
-    this.final_offset_x = this.origin_offset_x-(targetx-this.startsx)*obj.center.x;
-    this.final_offset_y = this.origin_offset_y-(targety-this.startsy)*obj.center.y;
+    this.center_x = obj.center.rel.x;
+    this.center_y = obj.center.rel.y;
+    this.orig_mtx = [obj[0], obj[1], obj[2], obj[3], obj[4], obj[5]];
 }
 
 
--- a/nodejs/desktop.svg	Fri Sep 24 16:00:04 2010 +0800
+++ b/nodejs/desktop.svg	Fri Sep 24 16:06:42 2010 +0800
@@ -154,15 +154,15 @@
      inkscape:pageshadow="2"
      inkscape:zoom="1.56875"
      inkscape:cx="207.9"
-     inkscape:cy="240"
+     inkscape:cy="290.99601"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1006"
-     inkscape:window-x="0"
-     inkscape:window-y="25"
-     inkscape:window-maximized="1" />
+     inkscape:window-width="1440"
+     inkscape:window-height="900"
+     inkscape:window-x="-1"
+     inkscape:window-y="-1"
+     inkscape:window-maximized="0" />
   <metadata
      id="metadata7">
     <rdf:RDF>
@@ -217,13 +217,13 @@
        transform="translate(-27.274116,-798.02051)"
        mbname="video"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="42.294399"
        inkscape:bbox-x="35.992421"
        inkscape:bbox-y="383.87928"
        inkscape:bbox-width="85.598946"
        inkscape:bbox-height="84.588799">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/video.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/video.png"
          xlink:href="video.png"
          width="85.598946"
          height="84.588799"
@@ -242,13 +242,13 @@
        transform="translate(126.77414,-798.02053)"
        mbname="audio"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="38.464287"
        inkscape:bbox-x="189.7836"
        inkscape:bbox-y="386.37278"
        inkscape:bbox-width="86.5"
        inkscape:bbox-height="76.928574">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/music.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/music.png"
          xlink:href="music.png"
          width="86.5"
          height="76.928574"
@@ -267,13 +267,13 @@
        transform="translate(290.67139,-799.03067)"
        mbname="picture"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="38.07143"
        inkscape:bbox-x="354.2955"
        inkscape:bbox-y="392.95458"
        inkscape:bbox-width="84"
        inkscape:bbox-height="76.14286">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/photo.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/photo.png"
          xlink:href="photo.png"
          width="84"
          height="76.14286"
@@ -292,13 +292,13 @@
        transform="translate(462.9024,-794.99007)"
        mbname="setting"
        inkscape:transform-center-x="0"
-       inkscape:transform-center-y="0"
+       inkscape:transform-center-y="37.57143"
        inkscape:bbox-x="526.42387"
        inkscape:bbox-y="389.0568"
        inkscape:bbox-width="84.571426"
        inkscape:bbox-height="75.14286">
       <image
-         sodipodi:absref="/home/wycc/devel/md3/MadButterfly/nodejs/tool.png"
+         sodipodi:absref="/usr/home/thinker/progm/MadButterfly/nodejs/tool.png"
          xlink:href="tool.png"
          width="84.571426"
          height="75.14286"
@@ -336,8 +336,8 @@
        id="text2931"
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
-       inkscape:bbox-x="140.30434"
-       inkscape:bbox-y="273.84843"
+       inkscape:bbox-x="126.28044"
+       inkscape:bbox-y="271.84843"
        inkscape:bbox-width="488.15625"
        inkscape:bbox-height="32.382812"
        mbname="line1"><tspan
@@ -348,8 +348,8 @@
          y="185.925"
          id="tspan2935"
          style="font-size:16px"
-         inkscape:bbox-x="140.56215"
-         inkscape:bbox-y="290.74687"
+         inkscape:bbox-x="126.53825"
+         inkscape:bbox-y="288.74687"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -358,8 +358,8 @@
          x="124.96794"
          y="205.925"
          style="font-size:16px"
-         inkscape:bbox-x="140.30434"
-         inkscape:bbox-y="273.84843"
+         inkscape:bbox-x="126.28044"
+         inkscape:bbox-y="271.84843"
          inkscape:bbox-width="75.335938"
          inkscape:bbox-height="12.101562"
          id="tspan2941">7:00-7:30</tspan></text>
@@ -372,7 +372,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="126.28044"
-       inkscape:bbox-y="212.01578"
+       inkscape:bbox-y="210.01578"
        inkscape:bbox-width="488.15625"
        inkscape:bbox-height="32.382812"
        mbname="line2"><tspan
@@ -384,7 +384,7 @@
          id="tspan2935-1"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="228.91422"
+         inkscape:bbox-y="226.91422"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -394,7 +394,7 @@
          y="267.75766"
          style="font-size:16px"
          inkscape:bbox-x="126.28044"
-         inkscape:bbox-y="212.01578"
+         inkscape:bbox-y="210.01578"
          inkscape:bbox-width="75.335938"
          inkscape:bbox-height="12.101562"
          id="tspan2941-9">7:30-8:30</tspan></text>
@@ -407,7 +407,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="126.05388"
-       inkscape:bbox-y="150.18311"
+       inkscape:bbox-y="152.18311"
        inkscape:bbox-width="488.38281"
        inkscape:bbox-height="32.382812"
        mbname="line3"><tspan
@@ -419,7 +419,7 @@
          id="tspan2935-2"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="167.08154"
+         inkscape:bbox-y="169.08154"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -429,8 +429,8 @@
          y="329.59033"
          style="font-size:16px"
          inkscape:bbox-x="126.05388"
-         inkscape:bbox-y="150.18311"
-         inkscape:bbox-width="85.75"
+         inkscape:bbox-y="152.18311"
+         inkscape:bbox-width="75.5625"
          inkscape:bbox-height="12.101562"
          id="tspan2941-0">8:30-9:30</tspan></text>
     <text
@@ -442,7 +442,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="125.97575"
-       inkscape:bbox-y="88.350433"
+       inkscape:bbox-y="90.350433"
        inkscape:bbox-width="488.46094"
        inkscape:bbox-height="32.382812"
        mbname="line4"><tspan
@@ -454,7 +454,7 @@
          id="tspan2935-11"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="105.24887"
+         inkscape:bbox-y="107.24887"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -464,7 +464,7 @@
          y="391.423"
          style="font-size:16px"
          inkscape:bbox-x="125.97575"
-         inkscape:bbox-y="88.350433"
+         inkscape:bbox-y="90.350433"
          inkscape:bbox-width="85.828125"
          inkscape:bbox-height="12.101562"
          id="tspan2941-90">9:30-10:30</tspan></text>
@@ -477,7 +477,7 @@
        inkscape:transform-center-x="0"
        inkscape:transform-center-y="0"
        inkscape:bbox-x="126.53825"
-       inkscape:bbox-y="26.517761"
+       inkscape:bbox-y="28.517761"
        inkscape:bbox-width="487.89844"
        inkscape:bbox-height="32.382812"
        mbname="line5"><tspan
@@ -489,7 +489,7 @@
          id="tspan2935-7"
          style="font-size:16px"
          inkscape:bbox-x="126.53825"
-         inkscape:bbox-y="43.416199"
+         inkscape:bbox-y="45.416199"
          inkscape:bbox-width="487.89844"
          inkscape:bbox-height="15.484375">Lim JeongHee (J.Lim) &amp; JoKwon (2AM) - The Road to Break Up</tspan><tspan
          sodipodi:role="line"
@@ -499,7 +499,7 @@
          y="453.25568"
          style="font-size:16px"
          inkscape:bbox-x="126.72575"
-         inkscape:bbox-y="26.517761"
+         inkscape:bbox-y="28.517761"
          inkscape:bbox-width="95.265625"
          inkscape:bbox-height="12.101562"
          id="tspan2941-7">10:30-11:30</tspan></text>
--- a/nodejs/mbapp.js	Fri Sep 24 16:00:04 2010 +0800
+++ b/nodejs/mbapp.js	Fri Sep 24 16:06:42 2010 +0800
@@ -5,6 +5,74 @@
 var sys = require("sys");
 var ldr = mbfly.img_ldr_new(".");
 
+function _reverse(m1) {
+    var rev = new Array(1, 0, 0, 0, 1, 0);
+    var m = new Array(m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
+
+    rev[3] = -m[3] / m[0];
+    m[3] = 0;
+    m[4] += rev[3] * m[1];
+    m[5] += rev[3] * m[2];
+    
+    rev[1] = -m[1] / m[4];
+    rev[0] += rev[1] * rev[3];
+    m[1] = 0;
+    m[2] += rev[1] * m[5];
+    
+    rev[2] = -m[2];
+    rev[5] = -m[5];
+    
+    rev[0] = rev[0] / m[0];
+    rev[1] = rev[1] / m[0];
+    rev[2] = rev[2] / m[0];
+    
+    rev[3] = rev[3] / m[4];
+    rev[4] = rev[4] / m[4];
+    rev[5] = rev[5] / m[4];
+
+    return rev;
+}
+
+function _decorate_mb_rt(mb_rt) {
+    var coord;
+    
+    mb_rt._mbapp_saved_coord_new = mb_rt.coord_new;
+    mb_rt.coord_new = function(parent) {
+	var coord;
+	
+	coord = this._mbapp_saved_coord_new(parent);
+	coord.type = "coord";
+	coord._mbapp_saved_mtx = [coord[0], coord[1], coord[2],
+				  coord[3], coord[4], coord[5]];
+	coord._mbapp_saved_rev_mtx = _reverse(coord._mbapp_saved_mtx);
+	coord.parent = parent;
+	coord._mbapp_saved_add_shape = coord.add_shape;
+	coord.add_shape = function(shape) {
+	    var coord;
+	    
+	    this._mbapp_saved_add_shape(shape);
+	    shape.parent = this;
+	}
+	
+	return coord;
+    };
+
+    /*
+     * Decorate root coord
+     */
+    coord = mb_rt.root;
+    coord.type = "coord";
+    coord._mbapp_saved_mtx = [coord[0], coord[1], coord[2],
+			      coord[3], coord[4], coord[5]];
+	coord._mbapp_saved_rev_mtx = _reverse(coord._mbapp_saved_mtx);
+    coord._mbapp_saved_add_shape = coord.add_shape;
+    coord.add_shape = function(shape) {
+	var coord;
+	
+	this._mbapp_saved_add_shape(shape);
+	shape.parent = this;
+    }
+}
 
 app=function(display, w, h) {
     var self = this;
@@ -18,6 +86,7 @@
 	h = 480;
     
     mb_rt = this.mb_rt = new mbfly.mb_rt(display, w, h);
+    _decorate_mb_rt(mb_rt);
     var background = mb_rt.rect_new(0, 0, 720, 480, 0, 0);
     var paint = mb_rt.paint_color_new(1, 1, 1, 1);
     paint.fill(background);
--- a/nodejs/svg.js	Fri Sep 24 16:00:04 2010 +0800
+++ b/nodejs/svg.js	Fri Sep 24 16:06:42 2010 +0800
@@ -246,49 +246,330 @@
 	coord.center.y = miny;
 };
 
+function _mul(m1, m2) {
+    var res = new Array();
+
+    res.push(m1[0] * m2[0] + m1[1] * m2[3]);
+    res.push(m1[0] * m2[1] + m1[1] * m2[4]);
+    res.push(m1[0] * m2[2] + m1[1] * m2[5] + m1[2]);
+    res.push(m1[3] * m2[0] + m1[4] * m2[3]);
+    res.push(m1[3] * m2[1] + m1[4] * m2[4]);
+    res.push(m1[3] * m2[2] + m1[4] * m2[5] + m1[5]);
+
+    return res;
+}
+
+function _pnt_transform(x, y, matrix) {
+    var rx, ry;
+
+    rx = x * matrix[0] + y * matrix[1] + matrix[2];
+    ry = x * matrix[3] + y * matrix[4] + matrix[5];
+    return new Array(rx, ry);
+}
+
+function _shift_transform(x, y, matrix) {
+    var rx, ry;
+
+    rx = x * matrix[0] + y * matrix[1];
+    ry = x * matrix[3] + y * matrix[4];
+    return new Array(rx, ry);
+}
+
+function _transform_bbox(bbox, matrix) {
+    var min_x, min_y, max_x, max_y;
+    var x, y;
+    var pnt;
+    var pnts = new Array();
+    var i;
+
+    pnt = _pnt_transform(bbox.x, bbox.y, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x + bbox.width, bbox.y, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x, bbox.y + bbox.height, matrix);
+    pnts.push(pnt);
+    pnt = _pnt_transform(bbox.x + bbox.width, bbox.y + bbox.height, matrix);
+    pnts.push(pnt);
+
+    min_x = max_x = pnts[0][0];
+    min_y = max_y = pnts[0][1];
+    for(i = 1; i < pnts.length; i++) {
+	pnt = pnts[i];
+	if(pnt[0] < min_x)
+	    min_x = pnt[0];
+	if(pnt[1] < min_y)
+	    min_y = pnt[1];
+	if(pnt[0] > max_x)
+	    max_x = pnt[0];
+	if(pnt[1] > max_y)
+	    max_y = pnt[1];
+    }
+
+    bbox.x = min_x;
+    bbox.y = min_y;
+    bbox.width = max_x - min_x;
+    bbox.height = max_y - min_y;
+}
+
+function _reverse(m1) {
+    var rev = new Array(1, 0, 0, 0, 1, 0);
+    var m = new Array(m1[0], m1[1], m1[2], m1[3], m1[4], m1[5]);
+
+    rev[3] = -m[3] / m[0];
+    m[3] = 0;
+    m[4] += rev[3] * m[1];
+    m[5] += rev[3] * m[2];
+    
+    rev[1] = -m[1] / m[4];
+    rev[0] += rev[1] * rev[3];
+    m[1] = 0;
+    m[2] += rev[1] * m[5];
+    
+    rev[2] = -m[2];
+    rev[5] = -m[5];
+    
+    rev[0] = rev[0] / m[0];
+    rev[1] = rev[1] / m[0];
+    rev[2] = rev[2] / m[0];
+    
+    rev[3] = rev[3] / m[4];
+    rev[4] = rev[4] / m[4];
+    rev[5] = rev[5] / m[4];
+
+    return rev;
+}
+
+var _bbox_proto = {
+    _get_ac_saved_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+	
+	mtx = c._mbapp_saved_rev_mtx;
+	while(c.parent && typeof c.parent != "undefined") {
+	    c = c.parent;
+	    mtx = _mul(mtx, c._mbapp_saved_rev_mtx);
+	}
+
+	return mtx;
+    },
+    
+    _get_ac_mtx: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = [c[0], c[1], c[2], c[3], c[4], c[5]];
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(c, mtx);
+	}
+
+	return mtx;
+    },
+
+    _saved_to_current: function() {
+	var r;
+	
+	r = _mul(this._get_ac_mtx(), this._get_ac_saved_rev());
+	
+	return r;
+    },
+
+    /*! \brief Update x, y, width, and height of the bbox.
+     */
+    update: function() {
+	var mtx;
+
+	this.x = this.orig.x;
+	this.y = this.orig.y;
+	this.width = this.orig.width;
+	this.height = this.orig.height;
+	
+	mtx = this._saved_to_current();
+	_transform_bbox(this, mtx);
+    },    
+};
+
+var _center_proto = {
+    _get_ac_saved_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+	
+	mtx = c._mbapp_saved_rev_mtx;
+	while(c.parent && typeof c.parent != "undefined") {
+	    c = c.parent;
+	    mtx = _mul(mtx, c._mbapp_saved_rev_mtx);
+	}
+
+	return mtx;
+    },
+    
+    _get_ac_mtx: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = [c[0], c[1], c[2], c[3], c[4], c[5]];
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(c, mtx);
+	}
+
+	return mtx;
+    },
+
+    _get_ac_rev: function() {
+	var c = this.owner;
+	var mtx;
+	
+	if(c.type != "coord")
+	    c = c.parent;	// is a shape!
+
+	mtx = _reverse([c[0], c[1], c[2], c[3], c[4], c[5]]);
+	while(c.parent) {
+	    c = c.parent;
+	    mtx = _mul(mtx, _reverse([c[0], c[1], c[2], c[3], c[4], c[5]]));
+	}
+
+	return mtx;
+    },
+
+    _saved_to_current: function() {
+	var r;
+	
+	r = _mul(this._get_ac_mtx(), this._get_ac_saved_rev());
+	
+	return r;
+    },
+
+    /*! \brief Update x, y of center point of an object.
+     */
+    update: function() {
+	var mtx;
+	var xy;
+
+	mtx = this._saved_to_current();
+	xy = _pnt_transform(this.orig.x, this.orig.y, mtx);
+
+	this._x = xy[0];
+	this._y = xy[1];
+    },
+
+    /*! \brief Move owner object to make center at (x, y).
+     */
+    move: function(x, y) {
+	var mtx;
+	var xdiff = x - this._x;
+	var ydiff = y - this._y;
+	var shiftxy;
+	var c;
+
+	mtx = this._get_ac_rev();
+	shiftxy = _shift_transform(xdiff, ydiff, mtx);
+
+	c = this.owner;
+	if(c.type != "coord")
+	    c = c.parent;
+
+	c[2] += shiftxy[0];
+	c[5] += shiftxy[1];
+
+	this._x = x;
+	this._y = y;
+    },
+
+    /*! \brief Move owner object to make center at position specified by pnt.
+     */
+    move_pnt: function(pnt) {
+	this.move(pnt.x, pnt.y);
+    },
+
+    /*! \brief Prevent user to modify value.
+     */
+    get x() { return this._x; },
+    
+    /*! \brief Prevent user to modify value.
+     */
+    get y() { return this._y; },
+
+    get rel() {
+	var rev;
+	var xy;
+	
+	rev = this._get_ac_rev();
+	xy = _pnt_transform(this.orig.x, this.orig.y, rev);
+
+	return {x: xy[0], y: xy[1]};
+    },
+};
 
 loadSVG.prototype._set_bbox = function(node, tgt) {
     var a;
     var vstr;
     var bbox, center;
+    var orig;
     
     a = node.attr("bbox-x");
     sys.puts("a="+a);
     if(!a)
 	return 0;
     
+    /* bbox.orig is initial values of bbox.  The bbox is recomputed
+     * according bbox.orig and current matrix.  See bbox.update().
+     */
     tgt.bbox = bbox = new Object();
+    bbox.orig = orig = new Object();
+    bbox.owner = tgt;
+    bbox.__proto__ = _bbox_proto;
     vstr = a.value();
-    bbox.x = parseFloat(vstr);
+    orig.x = parseFloat(vstr);
 
     a = node.attr("bbox-y");
     vstr = a.value();
-    bbox.y = this.height - parseFloat(vstr);
+    orig.y = this.height - parseFloat(vstr);
 
     a = node.attr("bbox-width");
     vstr = a.value();
-    bbox.width = parseFloat(vstr);
+    orig.width = parseFloat(vstr);
 
     a = node.attr("bbox-height");
     vstr = a.value();
-    bbox.height = parseFloat(vstr);
-    bbox.y -= bbox.height;
+    orig.height = parseFloat(vstr);
+    orig.y -= orig.height;
+    
+    bbox.update();
 
+    /* center.orig is initial values of center.  The center is
+     * recomputed according center.orig and current matrix.  See
+     * center.update().
+     */
     tgt.center = center = new Object();
+    center.orig = orig = new Object();
     
-    center.x = bbox.width / 2 + bbox.x;
-    center.y = bbox.height / 2 + bbox.y;
-    //center.x = bbox.x;
-    //center.y = bbox.y;
+    orig.x = bbox.orig.width / 2 + bbox.orig.x;
+    orig.y = bbox.orig.height / 2 + bbox.orig.y;
     a = node.attr("transform-center-x");
-    if(!a)
-	return 1;
+    if(a) {
+	vstr = a.value();
+	orig.x += parseFloat(vstr);
+	a = node.attr("transform-center-y");
+	vstr = a.value();
+	orig.y -= parseFloat(vstr);
+    }
+    center.__proto__ = _center_proto;
+    center.owner = tgt;
+    center.update();
     
-    vstr = a.value();
-    center.x += parseFloat(vstr);
-    a = node.attr("transform-center-y");
-    vstr = a.value();
-    center.y -= parseFloat(vstr);
     return 1;
 }
 
@@ -356,9 +637,9 @@
     var path = this.mb_rt.path_new(d);
     
     guessPathBoundingBox(coord,d);
-    this._set_bbox(n, path);
+    coord.add_shape(path);
     this._set_paint(n, path);
-    coord.add_shape(path);
+    this._set_bbox(n, path);
 
     make_mbnames(this.mb_rt, n, path);
 };
@@ -393,15 +674,7 @@
 	}
     }
     sys.puts(y);
-    if (this._set_bbox(n, tcoord)) {
-        tcoord.center.x -= tcoord[2];
-        tcoord.center.y -= tcoord[5];
-        tcoord._x = tcoord.center.x;
-        tcoord._y = tcoord.center.y;
-    } else {
-        tcoord._x = coord.center.x;
-        tcoord._y = coord.center.y;
-    }
+    this._set_bbox(n, tcoord);
 	
     make_mbnames(this.mb_rt, n, tcoord);
 };
@@ -478,25 +751,9 @@
         parseTransform(tcoord,trans.value());
 
     var rect = this.mb_rt.rect_new(x,y,w,h,10, 10);
+    tcoord.add_shape(rect);
     this._set_paint(n, rect);
-    if (this._set_bbox(n, tcoord)) {
-	tcoord.center.x -= tcoord[2];
-	tcoord.center.y -= tcoord[5];
-    } else {
-        if (trans) {
-            rx = tcoord[0]*x+tcoord[1]*y+tcoord[2];
-            ry = tcoord[3]*x+tcoord[4]*y+tcoord[5];
-            if (tcoord.center.x > rx)
-                tcoord.center.x = rx;
-            if (tcoord.center.y > ry)
-                tcoord.center.y = ry;
-	}
-    }
-    sys.puts("center.x="+tcoord.center.x);
-    sys.puts("center.y="+tcoord.center.y);
-    tcoord._x = tcoord.center.x;
-    tcoord._y = tcoord.center.y;
-    tcoord.add_shape(rect);
+    this._set_bbox(n, tcoord);
 
     make_mbnames(this.mb_rt, n, tcoord);
 };
@@ -583,13 +840,7 @@
     if (root.center.y > coord.center.y)
 	root.center.y = coord.center.y;
 
-    if (this._set_bbox(n, coord)) {
-        coord.center.x -= accu[2];
-        coord.center.y -= accu[5];
-    }
-    coord._x = coord.center.x;
-    coord._y = coord.center.y;
-    sys.puts("coord.center.x="+coord.center.x+",coord.center.y="+coord.center.y);
+    this._set_bbox(n, coord);
     
     make_mbnames(this.mb_rt, n, coord);
 };
@@ -641,11 +892,8 @@
     paint.fill(img);
     tcoord.add_shape(img);
     
-    if (this._set_bbox(n, img)) {
-        img.center.x -= accu[2]+coord[2];
-        img.center.y -= accu[5]+coord[5];
-    }
-
+    this._set_bbox(n, img);
+    
     make_mbnames(this.mb_rt, n, img);
 };
 
--- a/nodejs/testdesktop.js	Fri Sep 24 16:00:04 2010 +0800
+++ b/nodejs/testdesktop.js	Fri Sep 24 16:06:42 2010 +0800
@@ -1,3 +1,4 @@
+// -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 4; -*-
 // vim: sw=4:ts=8:sts=4
 var svg = require("./svg");
 var mbapp = require("./mbapp");
@@ -16,97 +17,109 @@
 setting = app.get("setting");
 
 lightbar = app.get("lightbar");
-lines=[app.get("line1"),app.get("line2"),app.get("line3"), app.get("line4"),app.get("line5")];
-for(i=0;i<lines.length;i++) {
-    sys.puts("["+i+"]="+lines[i].y);
+lines = [];
+for(i = 0; i < 5; i++) {
+    line = app.get("line" + (i + 1));
+    lines.push(line);
 }
 line=0;
 
-items=[video,audio,picture,setting];
+items=[video, audio, picture, setting];
+
 item = 0;
 an = new animate.scale(app,items[item],1,1.5,0.1);
 an.start();
 app.refresh();
 app.addKeyListener(mbapp.KEY_LEFT, function() {
-	var old = items[item];
-	item = item - 1;
-	if (item == -1) {
-		item = 0;
-		return;
-	}
-        var target = items[item];
-	var an = new animate.scale(app,old,1,1,0.1);
-        an.start();
-	an = new animate.scale(app,target,1,1.5,0.3);
-        an.start();
+    var old = items[item];
+    item = item - 1;
+    if (item == -1) {
+	item = 0;
+	return;
+    }
+    
+    var target = items[item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(app, old, 1, 1, 0.1);
+    an.start();
+    an = new animate.scale(app, target, 1, 1.5, 0.3);
+    an.start();
 });
 
 app.addKeyListener(mbapp.KEY_RIGHT, function() {
-	var old = items[item];
-	item = item + 1;
-	if (item == items.length) {
-		item = item - 1;
-		return;
-	}
-        var target = items[item];
-	var an = new animate.scale(app,old,1,1,0.1);
-        an.start();
-	an = new animate.scale(app,target,1,1.5,0.3);
-        an.start();
+    var old = items[item];
+    item = item + 1;
+    if (item == items.length) {
+	item = item - 1;
+	return;
+    }
+    
+    var target = items[item];
+
+    old.bbox.update();
+    target.bbox.update();
+    
+    var an = new animate.scale(app, old, 1, 1, 0.1);
+    an.start();
+    an = new animate.scale(app, target, 1, 1.5, 0.3);
+    an.start();
 });
 
 app.addKeyListener(mbapp.KEY_UP, function() {
-	var old = lines[line];
-	line = line - 1;
-	if (line == -1) {
-		line = 0;
-		return;
-	}
-        var target = lines[line];
-	var sy = target.y-lightbar.y;
-	sys.puts(sy);
-	var an = new animate.linear(app,lightbar,0,sy,0.3);
-        an.start();
+    var old = lines[line];
+    line = line - 1;
+    if (line == -1) {
+	line = 0;
+	return;
+    }
+    var target = lines[line];
+    var sy = target.center.y - lightbar.center.y;
+    sys.puts(sy);
+    var an = new animate.linear(app,lightbar,0,sy,0.3);
+    an.start();
 });
 app.addKeyListener(mbapp.KEY_DOWN, function() {
-	var old = lines[line];
-	line = line + 1;
-	if (line == lines.length) {
-		line = line - 1; 
-		return;
-	}
-        var target = lines[line];
-	var sy = target.y-lightbar.y;
-	sys.puts("line="+line);
-	sys.puts("sy="+sy);
-	sys.puts("target.y="+target.y);
-	sys.puts("lightbar.y="+lightbar.y);
-	var an = new animate.linear(app,lightbar,0,sy,0.3);
-        an.start();
+    var old = lines[line];
+    line = line + 1;
+    if (line == lines.length) {
+	line = line - 1; 
+	return;
+    }
+    var target = lines[line];
+    var sy = target.center.y - lightbar.center.y;
+    sys.puts("line="+line);
+    sys.puts("sy="+sy);
+    sys.puts("target.y="+target.center.y);
+    sys.puts("lightbar.y="+lightbar.center.y);
+    var an = new animate.linear(app,lightbar,0,sy,0.3);
+    an.start();
 });
 
 app.addKeyListener(mbapp.KEY_ENTER, function() {
-	var target = items[item];
-	var sx = 500-target.x;
-	var sy = 220-target.y;
-	sys.puts("target "+sx+','+sy);
-	var an = new animate.linear(app,target,sx,sy,1);
+    var target = items[item];
+    var sx = 500 - target.center.x;
+    var sy = 220 - target.center.y;
+    sys.puts("target "+sx+','+sy);
+    var an = new animate.linear(app,target,sx,sy,1);
+    an.start();
+    for(i=0;i<items.length;i++) {
+	if (i == item) continue;
+	var x = Math.random();
+	var y = Math.random();
+	if (x > 0.5) x = 900;
+	else x = -500;
+	if (y > 0.5) y = 900;
+	else y = -500;
+	sx = x - items[i].center.x;
+	sy = y - items[i].center.y;
+	an = new animate.linear(app,items[i], sx,sy,2);
 	an.start();
-	for(i=0;i<items.length;i++) {
-	    if (i == item) continue;
-	    var x = Math.random();
-	    var y = Math.random();
-	    if (x > 0.5) x = 900;
-	    else x = -500;
-	    if (y > 0.5) y = 900;
-	    else y = -500;
-	    sx = x-items[i].x;
-	    sy = y-items[i].y;
-	    an = new animate.linear(app,items[i], sx,sy,2);
-	    an.start();
-	    alpha = new animate.alpha(app,items[i],0, 1);
-	    alpha.start();
-	}
+	alpha = new animate.alpha(app,items[i],0, 1);
+	alpha.start();
+    }
 });
 
 app.loop();
--- a/src/redraw_man.c	Fri Sep 24 16:00:04 2010 +0800
+++ b/src/redraw_man.c	Fri Sep 24 16:06:42 2010 +0800
@@ -612,6 +612,7 @@
 coord_canvas_info_new(redraw_man_t *rdman, coord_t *coord,
 		      mbe_t *canvas) {
     coord_canvas_info_t *info;
+    static co_aix id[6] = {1, 0, 0, 0, 1, 0};
 
     info = (coord_canvas_info_t *)elmpool_elm_alloc(rdman->coord_canvas_pool);
     if(info == NULL)
@@ -624,6 +625,10 @@
     bzero(info->pcache_areas, sizeof(area_t) * 2);
     info->pcache_cur_area = &info->pcache_areas[0];
     info->pcache_last_area = &info->pcache_areas[1];
+    memcpy(info->cache_2_pdev, id, sizeof(co_aix) * 6);
+    memcpy(info->cache_2_pdev_rev, id, sizeof(co_aix) * 6);
+    memcpy(info->aggr_2_pdev, id, sizeof(co_aix) * 6);
+    memcpy(info->aggr_2_pdev_rev, id, sizeof(co_aix) * 6);
 
     return info;
 }
@@ -1310,8 +1315,8 @@
 
 /* \brief Compute matrix from cached canvas to parent device space.
  */
-static void compute_cached_2_pdev_matrix(coord_t *coord,
-					   co_aix canvas2pdev_matrix[6]) {
+static void compute_cached_2_pdev_matrix(coord_t *coord) {
+    co_aix *canvas2pdev_matrix = coord_get_2pdev(coord);
     coord_t *parent;
     co_aix *aggr;
     co_aix *matrix, *paggr;
@@ -1337,6 +1342,8 @@
     canvas2p[5] = shift_y;
 
     matrix_mul(paggr, canvas2p, canvas2pdev_matrix);
+
+    compute_reverse(canvas2pdev_matrix, coord_get_2pdev_rev(coord));
 }
 
 /*! \brief Compute area in parent cached coord for a cached coord.
@@ -1351,7 +1358,7 @@
  * ancestral cached coord.
  */
 static void compute_pcache_area(coord_t *coord) {
-    co_aix cached2pdev[6];
+    co_aix *cached2pdev = coord_get_2pdev(coord);
     int c_w, c_h;
     canvas_t *canvas;
     coord_canvas_info_t *canvas_info;
@@ -1360,7 +1367,6 @@
     canvas_info = coord->canvas_info;
     SWAP(canvas_info->pcache_cur_area, canvas_info->pcache_last_area,
 	 area_t *);
-    compute_cached_2_pdev_matrix(coord, cached2pdev);
 
     canvas = _coord_get_canvas(coord);
     canvas_get_size(canvas, &c_w, &c_h);
@@ -1766,6 +1772,7 @@
 	coord = all_zeroing->ds[i];
 	if(coord_is_zeroing(coord))
 	    zeroing_coord(rdman, coord);
+	compute_cached_2_pdev_matrix(coord);
 	/* This is required by ancester cached ones to perform
 	 * zeroing.
 	 */
@@ -1775,6 +1782,34 @@
     return OK;
 }
 
+/*! \brief Update aggregated cache_2_pdev matrix for cached coords.
+ *
+ * This is perfromed from root to leaves.  Aggregated cache_2_pdev is
+ * named as aggr_2_pdev field of canvas_info_t.  It is the matrix to
+ * transform a point from space of a cached coord to the space of root
+ * coord.
+ */
+static int
+update_aggr_pdev(redraw_man_t *rdman) {
+    int i;
+    coords_t *all_zeroing;
+    coord_t *coord, *parent_cached;
+
+    all_zeroing = &rdman->zeroing_coords;
+    for(i = 0; i < all_zeroing->num; i++) {
+	coord = all_zeroing->ds[i];
+	parent_cached = coord_get_cached(coord_get_parent(coord));
+	matrix_mul(coord_get_2pdev(parent_cached),
+		   coord_get_2pdev(coord),
+		   coord_get_aggr2pdev(coord));
+	matrix_mul(coord_get_2pdev_rev(coord),
+		   coord_get_2pdev_rev(parent_cached),
+		   coord_get_aggr2pdev_rev(coord));
+    }
+
+    return OK;
+}
+
 /*! \brief Add aggregated dirty areas to ancestor.
  *
  * Dirty areas are aggregated into two areas.  It assumes that even or odd
@@ -1786,7 +1821,7 @@
     int i;
     int n_areas;
     co_aix poses0[2][2], poses1[2][2];
-    co_aix canvas2pdev_matrix[6];
+    co_aix *canvas2pdev_matrix;
     area_t **areas, *area;
     area_t *area0, *area1;
     coord_t *parent, *pcached_coord;
@@ -1863,7 +1898,7 @@
     parent = coord_get_parent(coord);
     pcached_coord = coord_get_cached(parent);
 
-    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
+    canvas2pdev_matrix = coord_get_2pdev(coord);
 
     /* Add dirty areas to parent cached coord. */
     matrix_trans_pos(canvas2pdev_matrix, poses0[0], poses0[0] + 1);
@@ -2032,6 +2067,10 @@
     if(r != OK)
 	return ERR;
 
+    r = update_aggr_pdev(rdman);
+    if(r != OK)
+	return ERR;
+
     /*
      * Clear all flags setted by zeroing.
      */
@@ -2209,22 +2248,20 @@
 static void update_cached_canvas_2_parent(redraw_man_t *rdman,
 					  coord_t *coord) {
     mbe_t *pcanvas, *canvas;
-    co_aix reverse[6];
-    co_aix canvas2pdev_matrix[6];
+    co_aix *c2pdev_reverse;
 
     if(coord_is_root(coord))
 	return;
 
-    compute_cached_2_pdev_matrix(coord, canvas2pdev_matrix);
-    compute_reverse(canvas2pdev_matrix, reverse);
+    c2pdev_reverse = coord_get_2pdev_rev(coord);
 
     canvas = _coord_get_canvas(coord);
     pcanvas = _coord_get_canvas(coord->parent);
 #ifndef UNITTEST
-    _update_cached_canvas_2_parent(rdman, reverse, canvas, pcanvas,
+    _update_cached_canvas_2_parent(rdman, c2pdev_reverse, canvas, pcanvas,
 				   coord->opacity);
 #else
-    memcpy(((mock_mbe_t *)canvas)->parent_2_cache, reverse,
+    memcpy(((mock_mbe_t *)canvas)->parent_2_cache, c2pdev_reverse,
 	   sizeof(co_aix) * 6);
 #endif
 }