计算斜边以获得变换后所需的结果



我已经编写了一个用于创建等轴测平面的绘图函数。该函数接受以下参数。

  • widthIso—平面的等轴测(倾斜)侧宽度
  • heightIso—平面的等轴测高度
  • imgObj-包含图案图像的对象(在本问题中不重要)

我制作了一个JSFiddle以使其更加清晰:https://jsfiddle.net/Le45oo71/10/

我的draw函数创建了一个画布,因此我可以使用该画布在主画布上绘制它。(这是预呈现)。

该功能执行以下操作:

  • 创建一个屏幕外画布
  • 计算并设置画布大小
  • 将上下文转换为等轴测投影
  • imgObj中的图像绘制一个矩形

当然,由于上下文的转换,期望等轴测平面的斜边具有所需的长度有点天真。我试了很多,但我不知道如何计算一个数字,所以在变换之后,得到的平面具有所需的边长。

我希望在绘制矩形时,平面(未变换)矩形的边一定要长一点。这样在变换之后,平面边就有了所需的长度。

变换矩阵

简单的答案在底部。如果你想知道如何阅读其余部分。

集变换和疯狂的abc的

您可以使用ctx.setTransform(a, b, c, d, e, f);来解决此问题。使用它而不是ctx.transformctx.rotatectx.scalectx.translate意味着您不必担心任何以前的变换,因为它会替换现有变换,而不是与现有变换相乘。

魔术参数可以最好地理解为x和y轴的方向和比例以及原点的屏幕坐标。

所有值都与像素坐标有关。轴描述单个像素的方向和大小,a,b表示像素的顶部,c,d表示左侧。e、 f是屏幕上显示变换坐标0,0的坐标,称为原点。

默认情况下,a = 1上的像素为一个像素,b = 0下的像素为零。该像素在c = 0上为零像素,在d = 1下为一像素。原点位于左上角的e = 0f = 0

从而设置默认变换ctx.setTransform(1,0,0,1,0,0);

让我们把这些变量称为更有意义的变量。它们是向量,对于x轴,它有一个x和y分量a = xAXxay分量)和b = xAYxay轴)。对于y轴CCD_ 20和CCD_。(我更喜欢xAxxAyyAxyAy与惯例略有不同),对于来源oxoy

因此ctx.setTransform(xAx, xAy, yAx, yAy, ox, oy)使整个事情稍微清晰(不那么像泥)。

两个轴是独立的,可以指向任何方向,也可以是任何长度,这使我们能够创建等轴测投影等。比例由轴的长度决定。x刻度是x轴的长度,y刻度是y轴的长度。

为了创建轴的矢量,我们使用cos和sin,因为它们返回角度的x和y分量。因此,如果我们希望x轴为45度(Math.PI/4,弧度),我们可以通过获得矢量

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

如果我们想要135度的y轴(Math.PI*(3/4)弧度)

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

Cos和sin总是创建一个单位向量(一个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;

现在我们可以将原点设置为画布的中心

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

所以用它来创建转换

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

希望你能看到这是多么简单。

答案

如果你有一张需要缩放的图像,以便侧面达到所需的尺寸,我们需要找到缩放比例。假设图像宽度为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();

相关内容

  • 没有找到相关文章

最新更新