如何用图像绘制等距平面?

时间:2016-04-08 17:44:55

标签: javascript html5 math canvas isometric

我想创建一个等轴测图。地图存在等长矩形,如图像中所示:isometric planes / (or 'rectangles')

我想将每个矩形表示为具有宽度和高度的2d矩形。 因此,当您在编辑器中创建地图时,可以绘制2d矩形,并且可以将此创建转换为等距平面。 等距平面的宽度为北向东南(从钻石形状的顶部到右侧。) 高度从北到西南。 (从上到下)

现在我有两个问题。

第一个问题

我在画布上绘制这个等距地图,我正在使用屏幕外画布单独绘制每个平面。我想计算屏幕外画布的2d宽度和高度。我用这个数学来做这个:

var canvasWidth  = Math.cos(30) * planeWidth + Math.cos(30) * planeHeight;
var canvasHeight = Math.sin(30) * planeWidth + Math.cos(30) * planeHeight;
// still remember that planeWidth and planeHeight are isometric, so oblique sides

但这并不能为画布提供合适的尺寸。 我想知道的是用像素计算倾斜边是否明智。

第二个问题 我想为飞机使用'纹理'图像。所以,我有像这样的图像: Tile images

我的想法是存储每个图像的斜边,就像平面的宽度和高度一样。 当我想绘制具有特定图像的平面时,我可以重复该图像。

主要目标是创建具有像素精确给定宽度和高度的等距平面。平面应该有一个图像作为纹理。图像已经是等距的,因此它们可以像等距瓷砖一样重复和剪裁。

我的问题: 1.我不知道使用斜边作为宽度和高度是明智的,不管怎么说,不管怎样才能找到继续使用2d尺寸的方法?

  1. 在屏幕外画布上创建一个2d矩形不是更聪明,并用非等距纹理图像填充它们,然后转换该画布。

  2. 如果我使用斜边来表示我的平面和图像的宽度/高度,这些长度是真的吗?它就像一个线性函数:它只是像素。每2个水平,1个垂直。水平方向的奇数,不会给出垂直方向的整数。 (我认为这会给我带来不必要的麻烦)。

1 个答案:

答案 0 :(得分:1)

要绘制等轴测平面,请使用变换矩阵设置x和y轴。

假设x轴沿着矢量(1,0.5),y轴沿着矢量(-1,0.5),原点仍然在左上角。

如此定义。

var xAxis = {
    x : 1,
    y : 0.5,
}
var yAxis = {
    x : -1,
    y : 0.5,
}
var origin = {
    x : 0,
    y : 0,
}

设置转换只需使用

ctx.setTransform(xAxis.x, xAxis.y, yAxis.x, yAxis.y, origin.x, origin.y);

现在所有渲染都在等距平面上。

您可以设置所需的等轴投影轴。我使用的那个会增加像素的面积。这将取决于x轴和y轴的长度和相对方向。

<强>更新

只是出于兴趣。

由于许多等距投影都倾向于改变每个像素的总面积,因此我已经包含了一个简单的计算,无论使用何种投影,都会对像素区域进行标准化。 (对于我给出的投影恰好是2的平方根上的投影)

使用xAxisyAxis计算该投影的像素区域

var area = (xAxis.x * ( xAxis.y + yAxis.y ) + ( xAxis.x + yAxis.x ) * yAxis.y) - (xAxis.y * ( xAxis.x + yAxis.x ) + ( xAxis.y + yAxis.y ) * yAxis.x)

该区域的平方根上的那个是所需的比例

var scaleBy = 1 / Math.sqrt(area);

您可以直接将其应用于轴......

xAxis.x *= scaleBy;
xAxis.y *= scaleBy;
yAxis.x *= scaleBy;
yAxis.y *= scaleBy;
ctx.setTransform(xAxis.x, xAxis.y, yAxis.x, yAxis.y, origin.x, origin.y);

或者在设置转换

后通过ctx.scale应用它
ctx.setTransform(xAxis.x, xAxis.y, yAxis.x, yAxis.y, origin.x, origin.y);
ctx.scale(scaleBy, scaleBy);

示例

演示显示了此方法的简单应用(没有像素规范化)。

    
    var canvas = document.createElement("canvas"); 
    canvas.width          = window.innerWidth;
    canvas.height         = window.innerHeight;
    canvas.style.position = "absolute";
    canvas.style.left     = "0px";
    canvas.style.top      = "0px";
    document.body.appendChild(canvas);
    
function demo(){
    canvas.width  = window.innerWidth;
    canvas.height = window.innerHeight;
    var ctx       = canvas.getContext("2d"); 

    ctx.font         = "80px arial black";
    ctx.lineWidth    = 8;
    ctx.lineJoin     = "round"
    ctx.strokeStyle  = "green";
    ctx.fillStyle    = "#aF6";
    ctx.textAlign    = "center";
    ctx.textBaseline = "middle";

    // the axis and origin
    var xAxis  = {x : 1, y: 0.5};
    var yAxis  = {x : -1, y: 0.5};
    var origin = {x : 0, y : 0};

    // cludge factor dividing by two to fit the display area
    ctx.setTransform(xAxis.x / 2, xAxis.y / 2, yAxis.x / 2, yAxis.y / 2, origin.x, origin.y);

    // draw big text
    ctx.strokeText("Isometric",ctx.canvas.width/2,0);
    ctx.fillText("Isometric",ctx.canvas.width/2,0);

    // half size text
    ctx.font      = "40px arial black";
    ctx.lineWidth = "4";
    ctx.strokeText("projection",ctx.canvas.width/2,60);
    ctx.fillText("projection",ctx.canvas.width/2,60);

     // tiny text
    ctx.font      = "16px arial black";
    ctx.lineWidth = "3";
    ctx.strokeText("using 2D context transformation",ctx.canvas.width/2,100);
    ctx.fillText("using 2D context transformation",ctx.canvas.width/2,100);

    // add an image to demonstrate that the project will be applied to 
    // anything that is rendered.
    var image = new Image();
    image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
    image.addEventListener("load",function(){
        ctx.drawImage(this,ctx.canvas.width-this.width,-this.height * 1.2);
    });
}
demo();
window.addEventListener("resize",demo);