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; };