簡體   English   中英

如何用畫布在頂點之間繪制平行邊(箭頭)?

[英]How to draw parallel edges (arrows) between vertices with canvas?

我正在使用Javascript進行流網絡可視化。 頂點表示為圓,邊表示為箭頭。

這是我的Edge課程:

function Edge(u, v) {
  this.u = u; // start vertex
  this.v = v; // end vertex

  this.draw = function() {
    var x1 = u.x;
    var y1 = u.y;
    var x2 = v.x;
    var y2 = v.y;

    context.beginPath();
    context.moveTo(x1, y1);
    context.lineTo(x2, y2);
    context.stroke();

    var dx = x1 - x2;
    var dy = y1 - y2;
    var length = Math.sqrt(dx * dx + dy * dy);

    x1 = x1 - Math.round(dx / ((length / (radius))));
    y1 = y1 - Math.round(dy / ((length / (radius))));
    x2 = x2 + Math.round(dx / ((length / (radius))));
    y2 = y2 + Math.round(dy / ((length / (radius))));

    // calculate the angle of the edge
    var deg = (Math.atan(dy / dx)) * 180.0 / Math.PI;
    if (dx < 0) {
      deg += 180.0;
    }
    if (deg < 0) {
      deg += 360.0;
    }
    // calculate the angle for the two triangle points
    var deg1 = ((deg + 25 + 90) % 360) * Math.PI * 2 / 360.0;
    var deg2 = ((deg + 335 + 90) % 360) * Math.PI * 2 / 360.0;
    // calculate the triangle points
    var arrowx = [];
    var arrowy = [];
    arrowx[0] = x2;
    arrowy[0] = y2;
    arrowx[1] = Math.round(x2 + 12 * Math.sin(deg1));
    arrowy[1] = Math.round(y2 - 12 * Math.cos(deg1));
    arrowx[2] = Math.round(x2 + 12 * Math.sin(deg2));
    arrowy[2] = Math.round(y2 - 12 * Math.cos(deg2));

    context.beginPath();
    context.moveTo(arrowx[0], arrowy[0]);
    context.lineTo(arrowx[1], arrowy[1]);
    context.lineTo(arrowx[2], arrowy[2]);
    context.closePath();
    context.stroke();
    context.fillStyle = "black";
    context.fill();
  };
}

給定代碼

var canvas = document.getElementById('canvas'); // canvas element
var context = canvas.getContext("2d");
context.lineWidth = 1;
context.strokeStyle = "black";

var radius = 20; // vertex radius

var u = {
  x: 50,
  y: 80
};

var v = {
  x: 150,
  y: 200
};

var e = new Edge(u, v);

e.draw();

draw()函數將在兩個頂點之間繪制一條邊,如下所示:

邊緣

如果我們添加代碼

var k = new Edge(v, u);
k.draw();

我們將得到:

不好

但是我想按以下兩個方向繪制邊緣:(對不起我的繪畫技巧很抱歉)

兩邊

當然,頂點和邊緣方向不是固定的。 JSFiddle上的一個工作示例(帶有繪制頂點功能): https ://jsfiddle.net/Romansko/0fu01oec/18/

將軸對准直線。

如果旋轉渲染以使其與直線對齊,則可使一切變得簡單。 一旦這樣做,就可以輕松地在線條的上方或下方進行繪制,因為它只是在y方向上,沿着線是x方向。

因此,如果您有一行

const line = { 
   p1 : { x : ? , y : ? },
   p2 : { x : ? , y : ? },
};

將其轉換為向量並將其標准化

// as vector from p1 to p2
var nx = line.p2.x - line.p1.x;    
var ny = line.p2.y - line.p1.y;

// then get length
const len = Math.sqrt(nx * nx + ny * ny);

// use the length to normalise the vector
nx /= len;
ny /= len;

歸一化向量表示我們要渲染的新x軸,而y軸與該軸成90度。 我們可以使用setTransform在行的起點設置軸和原點(0,0)。

ctx.setTransform(
    nx, ny,   // the x axis
    -ny, nx,  // the y axis at 90 deg to the x axis
    line.p1.x, line.p1.y  // the origin (0,0)
)

現在繪制線和箭頭很容易,因為它們是軸對齊的

ctx.beginPath();
ctx.lineTo(0,0); // start of line
ctx.lineTo(len,0); // end of line
ctx.stroke();

// add the arrow head
ctx.beginPath();
ctx.lineTo(len,0); // tip of arrow
ctx.lineTo(len - 10, 10);
ctx.lineTo(len - 10, -10);
ctx.fill();

渲染偏離中心的兩條線

var offset = 10;
ctx.beginPath();
ctx.lineTo(0,offset); // start of line
ctx.lineTo(len,offset); // end of line
ctx.moveTo(0,-offset); // start of second line
ctx.lineTo(len,-offset); // end of second line
ctx.stroke();

// add the arrow head
ctx.beginPath();
ctx.lineTo(len,offset); // tip of arrow
ctx.lineTo(len - 10, offset+10);
ctx.lineTo(len - 10, offset-10);
ctx.fill();

offset = -10;

// add second  arrow head
ctx.beginPath();
ctx.lineTo(0,offset); // tip of arrow
ctx.lineTo(10, offset+10);
ctx.lineTo(10, offset-10);
ctx.fill();

您可以使用

ctx.setTransform(1,0,0,1,0,0);  // restore default transform

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM