[Sie-announce] SIEコード [1692] SVGPathElementにおけるnormalizedPathSegListとpathSegListプロパティの実装

Back to archive index

svnno****@sourc***** svnno****@sourc*****
2010年 3月 2日 (火) 23:24:54 JST


Revision: 1692
          http://sourceforge.jp/projects/sie/svn/view?view=rev&revision=1692
Author:   dhrname
Date:     2010-03-02 23:24:54 +0900 (Tue, 02 Mar 2010)

Log Message:
-----------
SVGPathElementにおけるnormalizedPathSegListとpathSegListプロパティの実装

Modified Paths:
--------------
    branches/ufltima/dom/svg.js

Modified: branches/ufltima/dom/svg.js
===================================================================
--- branches/ufltima/dom/svg.js	2010-03-02 11:43:19 UTC (rev 1691)
+++ branches/ufltima/dom/svg.js	2010-03-02 14:24:54 UTC (rev 1692)
@@ -1306,8 +1306,231 @@
   this.addEventListener("DOMAttrModified", function(evt){
     var tar = evt.target;
     if (evt.attrName === "d") {
-      var tnlist = tar.normalizedPathSegList, tlist = tar.pathSegList;
+      var tnl = tar.normalizedPathSegList, tlist = tar.pathSegList, D = [], _parseFloat = parseFloat;
+      /*d属性の値を正規表現を用いて、二次元配列Dに変換している。もし、d属性の値が"M 20 30 L20 40"ならば、
+       *JSONにおける表現は以下のとおり
+       *D = s[["M", 20, 30], ["L", 20 40]]
+       */
+      var dd = evt.newValue
+      .replace(/\-/g, " -")
+      .replace(/,/g, " ")
+      .replace(/([a-yA-Y])/g, ",$1 ")
+      .replace(/([zZ])/g, ",$1 1")
+      .replace(/,/, "")
+      .split(",");
+      for (var i=0, dli=dd.length;i<dli;++i) {
+        D[i] = dd[i].match(/\S+/g);
+        for (var j=1, dili=D[i].length;j<dili;++j) {
+          D[i][j] = _parseFloat(D[i][j]); //_parseFloatは高速化のためのAlias
+        }
+      }
+      dd = null;
+      for (var i=0, Dli=D.length; i < Dli; ++i) {
+        var di = D[i], s;
+        for (var j=1, dii=di[0], dili=di.length; j < dili; ++j) {
+          if (dii === "M") {
+            s = tar.createSVGPathSegMovetoAbs(di[j], di[j+1]);
+            ++j;
+          } else if (dii === "m") {
+            s = tar.createSVGPathSegMovetoRel(di[j], di[j+1]);
+            ++j;
+          } else if (dii === "L") {
+            s = tar.createSVGPathSegLinetoAbs(di[j], di[j+1]);
+            ++j;
+          } else if (dii === "l") {
+            s = tar.createSVGPathSegLinetoRel(di[j], di[j+1]);
+            ++j;
+          } else if (dii === "C") {
+            s = tar.createSVGPathSegCurvetoCubicAbs(di[j+4], di[j+5], di[j], di[j+1], di[j+2], di[j+3]);
+            j += 5;
+          } else if (dii === "c") {
+            s = tar.createSVGPathSegCurvetoCubicRel(di[j+4], di[j+5], di[j], di[j+1], di[j+2], di[j+3]);
+            j += 5;
+          } else if (dii === "Q") {
+            s = tar.createSVGPathSegCurvetoQuadraticAbs(di[j+2], di[j+3], di[j], di[j+1]);
+            j += 3;
+          } else if (dii === "q") {
+            s = tar.createSVGPathSegCurvetoQuadraticRel(di[j+2], di[j+3], di[j], di[j+1]);
+            j += 3;
+          } else if (dii === "A") {
+            s = tar.createSVGPathSegArcAbs(di[j+5], di[j+6], di[j], di[j+1], di[j+2], di[j+3], di[j+4]);
+            j += 6;
+          } else if (dii === "a") {
+            s = tar.createSVGPathSegArcRel(di[j+5], di[j+6], di[j], di[j+1], di[j+2], di[j+3], di[j+4]);
+            j += 6;
+          } else if (dii === "Z") {
+            s = tar.createSVGPathSegClosePath();
+            break;
+          } else if (dii === "z") {
+            s = tar.createSVGPathSegClosePath();
+            break;
+          } else if (dii === "S") {
+            s = tar.createSVGPathSegCurvetoCubicSmoothAbs(di[j+2], di[j+3], di[j], di[j+1]);
+            j += 3;
+          } else if (dii === "s") {
+            s = tar.createSVGPathSegCurvetoCubicSmoothRel(di[j+2], di[j+3], di[j], di[j+1]);
+            j += 3;
+          } else if (dii === "T") {
+            s = tar.createSVGPathSegCurvetoQuadraticSmoothAbs(di[j], di[j+1]);
+            ++j;
+          } else if (dii === "t") {
+            s = tar.createSVGPathSegCurvetoQuadraticSmoothRel(di[j], di[j+1]);
+            ++j;
+          } else if (dii === "H") {
+            s = tar.createSVGPathSegLinetoHorizontalAbs(di[j]);
+          } else if (dii === "h") {
+            s = tar.createSVGPathSegLinetoHorizontalRel(di[j]);
+          } else if (dii === "V") {
+            s = tar.createSVGPathSegLinetoVerticalAbs(di[j]);
+          } else if (dii === "v") {
+            s = tar.createSVGPathSegLinetoVerticalRel(di[j]);
+          } else {
+            s = new SVGPathSeg();
+          }
+          tlist.appendItem(s);
+        }
+      }
+      /*以下の処理は、pathSegListからnormalizedPathSegListへの
+       *変換をする処理。相対座標を絶対座標に変換して、M、L、Cコマンドに正規化していく
+       */
+      var cx = cy = 0; //現在セグメントの終了点の絶対座標を示す (相対座標を絶対座標に変換するときに使用)
+      var xn = yn = 0; //subpath の始点の絶対座標を示す (SコマンドやTコマンドなどで使用)
       for (var i=0, tli=tlist.numberOfItems;i<tli;++i) {
+        var ti = tlist.getItem(i), ts = ti.pathSegType, dii = ti.pathSegTypeAsLetter;
+        if (ts === SVGPathSeg.PATHSEG_UNKNOWN) {
+        } else if (ts === SVGPathSeg.PATHSEG_CLOSEPATH) {
+          tnl.appendItem(ti);
+        } else {
+          if (ts % 2 === 1) {     //相対座標ならば
+            var rx = cx, ry = cy; //rx, ryは前のセグメントの終了点
+            cx += ti.x;
+            cy += ti.y;
+          } else {
+            cx = ti.x;
+            cy = ti.y;
+          }
+          if (dii === "M") {
+            if (i !== 0) {
+              /*Mコマンドが続いた場合は、2番目以降はLコマンドと解釈する
+               * W3C SVG1.1 
+               */
+              var tg = tlis.getItem(i-1);
+              if (tg.pathSegTypeAsLetter === "M") {
+                tnl.appendItem(tar.createSVGPathSegLinetoABS(cx, cy));
+                continue;
+              }
+            }
+            tnl.appendItem(ti);
+          } else if (dii === "m") {
+            if (i !== 0) {
+              var tg = tlist.getItem(i-1);
+              if (tg.pathSegTypeAsLetter === "m") {
+                tnl.appendItem(tar.createSVGPathSegLinetoABS(cx, cy));
+                continue;
+              }
+            }
+            tnl.appendItem(tar.createSVGPathSegMovetoABS(cx, cy));
+          } else if (dii === "L") {
+            tnl.appendItem(ti);
+          } else if (dii === "l") {
+            tnl.appendItem(tar.createSVGPathSegLinetoABS(cx, cy));
+          } else if (dii === "C") {
+            tnl.appendItem(ti);
+          } else if (dii === "c") {
+            tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(cx, cy, ti.x1+rx, ti.y1+ry, ti.x2+rx, ti.y2+ry));
+          } else if (dii === "Q") {
+            tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(cx, cy, (rx + 2*ti.x1) / 3, (ry + 2*ti.y1) / 3, (2*ti.x1 + cx) / 3, (2*ti.y1 + cy) / 3));
+          } else if (dii === "q") {
+            var x1 = ti.x1 + rx, y1 = ti.y1 + ry;
+            tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(cx, cy, (rx + 2*x1) / 3, (ry + 2*y1) / 3, (2*x1 + cx) / 3, (2*y1 + cy) / 3));
+            x1 = y1 = null;
+          } else if (dii === "A" || dii === "a") {
+            //以下は、Arctoを複数のCuvetoに変換する処理
+            var fS = ti.sweepFlag, psai = ti.angle;
+            if (ti.r1 === 0 || ti.r2 === 0) {
+              continue;
+            }
+            var r1 = Math.abs(ti.r1);
+            var r2 = Math.abs(ti.r2);
+            var ctx = (rx - cx) / 2,  cty = (ry - cy) / 2;
+            var cpsi = Math.cos(psai * Math.PI / 180), spsi = Math.sin(psai * Math.PI / 180);
+            var rxd = cpsi*ctx + spsi*cty,  ryd = -1*spsi*ctx + cpsi*cty;
+            var rxdd = rxd * rxd, rydd = ryd * ryd;
+            var r1x = r1 * r1, r2y = r2 * r2;
+            var lamda = rxdd/r1x + rydd/r2y;
+            var sds;
+            if (lamda > 1) {
+              r1 = Math.sqrt(lamda) * r1;
+              r2 = Math.sqrt(lamda) * r2;
+              sds = 0;
+            }  else{
+              var seif = 1;
+              if (ti.largeArcFlag === fS) {
+                seif = -1;
+              }
+              sds = seif * Math.sqrt((r1x*r2y - r1x*rydd - r2y*rxdd) / (r1x*rydd + r2y*rxdd));
+            }
+            var txd = sds*r1*ryd / r2,  tyd = -1 * sds*r2*rxd / r1;
+            var tx = cpsi*txd - spsi*tyd + (rx+cx)/2, ty = spsi*txd + cpsi*tyd + (ry+cy)/2;
+            var s1 = this.CVAngle(1,0,(rxd-txd)/r1, (ryd-tyd)/r2);
+            var dr = this.CVAngle((rxd-txd)/r1, (ryd-tyd)/r2, (-rxd-txd)/r1, (-ryd-tyd)/r2);
+            if (!fS  &&  dr > 0) {
+              dr -=   2*Math.PI;
+            } else if (fS  &&  dr < 0) {
+              dr += 2*Math.PI;
+            }
+            var sse = dr * 2 / Math.PI;
+            var seg = Math.ceil(sse<0 ? -1*sse  :  sse);
+            var segr = dr / seg;
+            var t = 8/3 * Math.sin(segr/4) * Math.sin(segr/4) / Math.sin(segr/2);
+            var cpsir1 = cpsi * r1, cpsir2 = cpsi * r2;
+            var spsir1 = spsi * r1, spsir2 = spsi * r2;
+            var mc = Math.cos(s1);
+            var ms = Math.sin(s1);
+            var x2 = rx - t * (cpsir1*ms + spsir2*mc), y2 = ry - t * (spsir1*ms - cpsir2*mc);
+            for (var i = 0; i < seg; ++i) {
+              s1 += segr;
+              mc = Math.cos(s1);
+              ms = Math.sin(s1);
+              var x3 = cpsir1*mc - spsir2*ms + tx, y3 = spsir1*mc + cpsir2*ms + ty;
+              var dx = -t * (cpsir1*ms + spsir2*mc), dy = -t * (spsir1*ms - cpsir2*mc);
+              tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(x3, y3, x2, y2, x3-dx, y3-dy));
+              x2 = x3 + dx;
+              y2 = y3 + dy;
+            }
+          } else if (dii === "S") {
+            if (i !== 0) {
+              var tg = tlist.getItem(i-1);
+              if (tg.pathSegTypeAsLetter === "C" || tg.pathSegTypeAsLetter === "c") {
+                var x1 = 2*tg.x - tg.x2;
+                var y1 = 2*tg.y - tg.y2;
+                tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(cx, cy, x1, y1, ti.x2, ti.y2));
+              }
+            }
+          } else if (dii === "s") {
+            if (i !== 0) {
+              var tg = tlist.getItem(i-1);
+              if (tg.pathSegTypeAsLetter === "C" || tg.pathSegTypeAsLetter === "c") {
+                var x1 = 2*tg.x - tg.x2;
+                var y1 = 2*tg.y - tg.y2;
+                tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(cx, cy, x1, y1, ti.x2+rx, ti.y2+ry));
+              }
+            }
+          } else if (dii === "T" || dii === "t") {
+            if (i !== 0) {
+              var tg = tlist.getItem(i-1);
+              if (tg.pathSegTypeAsLetter === "Q" || tg.pathSegTypeAsLetter === "q") {
+                var x1 = 2*tg.x - tg.x1;
+                var y1 = 2*tg.y - tg.y1;
+                tnl.appendItem(tar.createSVGPathSegCurvetoCubicABS(cx, cy, (rx + 2*x1) / 3, (ry + 2*y1) / 3, (2*x1 + cx) / 3, (2*y1 + cy) / 3));
+              }
+            }
+          } else if (dii === "H" || dii === "h") {
+            tnl.appendItem(tar.createSVGPathSegLinetoABS(cx, ry));
+          } else if (dii === "V" || dii === "v") {
+            tnl.appendItem(tar.createSVGPathSegLinetoABS(rx, cy));
+          }
+        }
       }
       evt.stopPropagation();
     }
@@ -1326,21 +1549,26 @@
       var tar = evt.target, matrix = tar.getScreenCTM(), tlist = tar.normalizedPathSegList;
       var dat = "", ma = matrix.a, mb = matrix.b, mc = matrix.c, md = matrix.d, me = matrix.e, mf = matrix.f,;
       for (var i=0, tli=tlist.numberOfItems;i<tli;++i) {
-        var ti = tlist.getItem(i);
-        if (ti.pathSegTypeAsLetter === "z") {
-          dat += "x e";
+        var ti = tlist.getItem(i), tps = ti.pathSegTypeAsLetter;
+        if (tps === "z" || tps === "Z") {
+          dat += " x ";
         } else {
+          /*CTM(maなど)の行列と座標(x, y)の積を算出する。数学における表現は以下のとおり
+           *[ma mc me]   [x]
+           *[mb md mf] * [y]
+           *[0  0  1 ]   [1]
+           */
           var x = ma * ti.x + mc * ti.y + me;
           var y = mb * ti.x * md * ti.y + mf;
           x += " "; //文字列型に変換しておき、空白で区切りを付ける
           y += " ";
           dat += x;
           dat += y;
-          if (ti.pathSegTypeAsLetter === "M") {
+          if (tps === "M") {
             dat = "m" + dat;
-          } else if (ti.pathSegTypeAsLetter === "L") {
+          } else if (tps === "L") {
             dat = "l" + dat;
-          } else if (ti.pathSegTypeAsLetter === "C") {
+          } else if (tps === "C") {
             dat = "c" + dat;
             x = ma * ti.x1 + mc * ti.y1 + me;
             y = mb * ti.x1 * md * ti.y1 + mf;
@@ -1357,6 +1585,7 @@
           }
         }
       }
+      dat += "e";
       tar._tar.path = dat;
       matrix = dat = x = y = null;
     });
@@ -1462,20 +1691,24 @@
   /*SVGPathSegLinetoHorizontalAbs*/    SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function(/*float*/ x ) {
     var s = new SVGPathSegLinetoHorizontalAbs();
     s.x = x;
+    s.y = 0; //DOMでは指定されていないが、変換処理が楽なので用いる
     return s;
   };
   /*SVGPathSegLinetoHorizontalRel*/    SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function(/*float*/ x ) {
     var s = new SVGPathSegLinetoHorizontalRel();
     s.x = x;
+    s.y = 0;
     return s;
   };
   /*SVGPathSegLinetoVerticalAbs*/    SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function(/*float*/ y ) {
     var s = new SVGPathSegLinetoVerticalAbs();
+    s.x = 0; //DOMでは指定されていないが、変換処理が楽なので用いる
     s.y = y;
     return s;
   };
   /*SVGPathSegLinetoVerticalRel*/    SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function(/*float*/ y ) {
     var s = new SVGPathSegLinetoVerticalRel();
+    s.x = 0;
     s.y = y;
     return s;
   };




Sie-announce メーリングリストの案内
Back to archive index