2016-05-08 8 views
0

アイソメ平面を作成するための描画関数を作成しました。 この関数は、次のパラメータを受け入れます。変換後に必要な結果を得るために斜辺を計算する

  • widthIso - 平面の等角(斜め)側の幅。
  • heightIso - 平面の等高さの高さ。
  • imgObj - 私はそれをより明確にするためにJSFiddleを作りました(この問題では重要ではない)パターン画像を含むオブジェクト

https://jsfiddle.net/Le45oo71/10/

draw機能は、キャンバスを作成しますので、私はすることができますそのキャンバスを使用してメインキャンバスに描画します。 (それはあらかじめレンダリングしています)。

機能は以下のん:

  • はオフスクリーンキャンバスを作成します。
  • キャンバスサイズを計算して設定します。
  • コンテキストを等角投影に変換します。コンテキストに変換するためには、等角面の斜辺が正確に所望の長さを有することを期待する少しナイーブだ起因
  • 、もちろんimgObj

からの画像で塗りつぶされた四角形を描画。 私はたくさん試しましたが、数値を計算する方法を理解することはできません。そのため、トランスフュージョンの後に、結果のプレーンに必要なサイドの長さがあります。

私は、長方形が描画されるとき、平らな(変形されていない)矩形の辺が少し長くなる必要があると思います。変換後、面の辺が必要な長さになるようにします。

答えて

1

トランスフォームマトリックス

簡単な答えが一番下です。あなたはどのようにして残りの部分を読むのか知りたければ。あなたはこの問題を解決するためにctx.setTransform(a, b, c, d, e, f);を使用することができます

トランスフォームセットと狂気のABCの

ctx.transformctx.rotatectx.scale、またはctx.translateではなく、既存のトランスフォームを掛け合わせるのではなく、既存のトランスフォームを置き換えるので、以前のトランスフォームを心配する必要はありません。

マジック引数は、x軸とy軸の方向とスケール、および原点のスクリーン座標として最もよく理解できます。

すべての値はピクセルの座標に関連しています。軸は、1つのピクセルの方向とサイズ、ピクセルの上部のaとb、左側のc、dを表します。 e、fは変換された座標0,0が現れる、原点と呼ばれるスクリーン上の座標です。

デフォルトでは、ピクセルはa = 1に1ピクセルあり、ゼロピクセルはb = 0になります。このピクセルは、c = 0のゼロピクセルと、d = 1の1ピクセルです。原点は左上にありますe = 0f = 0;したがってさんは、より意味のある何かの変数を呼ぶことにしましょう。デフォルトはctx.setTransform(1,0,0,1,0,0);

を変換設定する

。彼らはベクトルであり、その軸xに対してxおよびy成分a = xAXX XIS Y成分)とb = xAYX XIS Y成分)を有しています。 y軸も同じです。c = yAXd = yAYです。全部少し明確に(のような少ない泥を)作るctx.setTransform(xAx, xAy, yAx, yAy, ox, oy)したがって、原点oxため、oy

(私はxAxxAyyAxyAy大会からわずかな休憩を好みます)。

2軸は独立しており、任意の方向を指すことができ、任意の長さにすることができるため、等長投影などを作成できます。スケールは軸の長さによって決まります。 xスケールはx軸の長さで、yスケールはy軸の長さです。

軸のベクトルを作成するには、角度のxとy成分を返すときにcosとsinを使用します。私たちは45度(ラジアンにMath.PI/4)であることをx軸をしたい場合、我々は135度(にMath.PI *(3でy軸を望むのであれば、私たちは

var direction = Math.PI/4; // 45 deg down and right 
var xAx = Math.cos(direction); 
var xAy = Math.sin(direction); 

を経由してベクトルを得ることができます/ 4)ラジアン)

var direction = Math.PI * (3/4); // 135 deg down and left 
var yAx = Math.cos(direction); 
var yAy = Math.sin(direction); 

COSおよび罪なかれ単位ベクトル(ベクトルの規模が1であることを意味する1つの単位の長さであるベクトル)を作成する(効果なしスケールなし)

は変更しますベクトルのスケールは、xとyの両方の成分を乗算します。したがって、上記に加えて両方の軸をスケールしましょう。

var scale = 2; // zoom by 2 
direction = Math.PI/4; // 45 deg down and right 
xAx = Math.cos(direction) * scale; 
xAy = Math.sin(direction) * scale; 
direction = Math.PI * (3/4); // 135 deg down and left 
yAx = Math.cos(direction) * scale; 
yAy = Math.sin(direction) * scale; 

今、私たちは、うまくいけば、あなたはこれがどのようにシンプル見ることができます変換

ctx.setTransform(xAx, xAy, yAx, yAy, ox, oy); // set the transform 
ctx.strokeRect(0,0,20,10); // draws a rectangle 40 pixels (scale 2) at 45 deg 
          // and 20 pixels at 135 deg centered on the canvas 

を作成するためにそれを使用するので、キャンバスの中央

var ox = ctx.canvas.width/2; 
var oy = ctx.canvas.height/2; 

に起源を設定することができます。

回答

あなたは私達がスケールを見つける必要がある側面は、必要なサイズのものであるようにスケーリングされる必要があるイメージを持っている場合。画像の幅が100ピクセルで、300ピクセルで表示する必要があるとします。スケールを取得するには、画面サイズを画像サイズで割ります。300/100 = 3スケールは3です。

スケールは軸方向に依存しません。

x軸とy軸の角度、画像の幅と高さのサイズ、表示の幅と高さ、原点の位置をとる関数として、これらをすべてまとめることができます。その後、我々は、規模を計算軸を作成し、変換

function createTransform(angX, angY, imgWidth, imgHeight, displayWidth, displayHeight, originX, originY){ 
    var scaleX = displayWidth/imgWidth; // get x scale 
    var scaleY = displayHeight/imgHeight; // get y scale 
    // create the x axis 
    var xAx = Math.cos(angX) * scaleX; 
    var xAy = Math.sin(angX) * scaleX; 
    // create the y axis 
    var yAx = Math.cos(angY) * scaleY; 
    var yAy = Math.sin(angY) * scaleY; 
    // set the transform 
    ctx.setTransform(xAx, xAy, yAx, yAy, originX, originY); 
} 

そして今、あなたが一致していない上記画像

// assume image is a loaded image 
// axis at 45 and 135 deg make the display width and height 200,300 at the canvas center 
createTransform(Math.PI/4, Math.PI * (3/4), image.width, image.height, 200, 300, ctx.canvas.width/2, ctx.canvas.height/2) 
ctx.drawImage(image,0,0); // drawn to fit requirements. 

コピーを描き、簡単な答えに

を貼り付けることができますを設定しますパターンと角度を使用しているときに必要なものは少し異なりますが、私があなたのためにソリューションをやっただけでは、問題の即時切り取りと貼り付けのソリューションを除いて、それほど多くは得られません。

function draw(widthIso, heightIso, imgObj) { 
    // rather than the messing around with the and rotations hand code the axis 
    // x axis 
    var xAx = 1; 
    var xAy = 0.5; 

    // y axis 
    var yAx = -1; 
    var yAy = 0.5; 

    // need the length of the axis so we can calculate a scale adjustment 
    var scaleX = Math.sqrt(xAx * xAx + xAy * xAy); // pythagoras to get the length of x axis 
    var scaleY = Math.sqrt(yAx * yAx + yAy * yAy); // pythagoras to get the length of y axis 

    // now we know how big a pixel is in isometric projection 
    // assuming you don't want to change the image pixel size from what you have in the fiddle 
    // I will workout the draw size 

    var drawWidth = widthIso/scaleX; 
    var drawHeight = heightIso/scaleY; 

    // get the width as sum of the product of axis x & y, x components 
    var canvasWidth = xAx * widthIso + yAx * heightIso; 

    // get the width as sum of the product of axis x & y, y components 
    var canvasHeight = xAy * widthIso + yAy * heightIso; 

    // create the canvas rounding up for size so we don't lose any pixels 
    var ca = document.createElement('canvas'); 
    ca.width = Math.ceil(canvasWidth); 
    ca.height = Math.ceil(canvasHeight); 
    var ctx = ca.getContext('2d'); 

    // now get the origin 
    var originY = 0; // always at the top 
    var originX = yAx * heightIso; // in by x component of height 

    // now just set the transform 
    ctx.setTransform(xAx, xAy, yAx, yAy, originX, originY); 

    // create pattern 
    var pattern = ctx.createPattern(imgObj.img, 'repeat'); 
    ctx.fillStyle = pattern; 
    // draw with the corrected width and height 
    ctx.fillRect(0, 0, drawWidth, drawHeight); 
    // return the canvas image 
    return {canvas: ca, ctx: ctx, toLeft: originX}; 
} 

これが行われます。キャンバス画像を作成するときにピクセルを丸めることを忘れないでください。エッジのピクセルが失われる可能性があります。

これは私がテストしたコードです。あなたの赤と青の線を中央に移動し、正確に200と300ピクセルの長さにしました。

// to visualise with red and blue lines. 
var x = Math.cos(Math.atan2(0.5,-1))*150 + 230; 
var y = Math.sin(Math.atan2(0.5,-1))*150 + 50; 
ctx.strokeStyle = 'red'; 
ctx.beginPath(); 
ctx.moveTo(x, y); // no guessing 
ctx.lineTo(x + Math.cos(Math.atan2(0.5,1))*200, y + Math.sin(Math.atan2(0.5,1))*200); // 
ctx.stroke(); 

var x = Math.cos(Math.atan2(0.5,1))*100 + 230; 
var y = Math.sin(Math.atan2(0.5,1))*100 + 50; 
ctx.strokeStyle = 'blue'; 
ctx.beginPath(); 
ctx.moveTo(x, y); // 
ctx.lineTo(x + Math.cos(Math.atan2(0.5,-1))*300, y + Math.sin(Math.atan2(0.5,-1))*300); // 
ctx.stroke(); 
関連する問題