简体   繁体   English

如何用画布在顶点之间绘制平行边(箭头)?

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

I'm working on a flow-network visualization with Javascript. 我正在使用Javascript进行流网络可视化。 Vertices are represented as circles and edges are represented as arrows. 顶点表示为圆,边表示为箭头。

Here is my Edge class: 这是我的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();
  };
}

Given the code 给定代码

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();

The draw() function will draw an edge between two vertices like this: draw()函数将在两个顶点之间绘制一条边,如下所示:

边缘

If we add the code 如果我们添加代码

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

We will get: 我们将得到:

不好

but I want to draw edges both directions as following: (sorry for my bad paint skills) 但是我想按以下两个方向绘制边缘:(对不起我的绘画技巧很抱歉)

两边

Of course the vertices and the edge directions are not fixed. 当然,顶点和边缘方向不是固定的。 A working example (with drawing vertex fucntion) on JSFiddle: https://jsfiddle.net/Romansko/0fu01oec/18/ JSFiddle上的一个工作示例(带有绘制顶点功能): https ://jsfiddle.net/Romansko/0fu01oec/18/

Aligning axis to a line. 将轴对准直线。

It can make everything a little easier if you rotate the rendering to align with the line. 如果旋转渲染以使其与直线对齐,则可使一切变得简单。 Once you do that it is then easy to draw above or below the line as that is just in the y direction and along the line is the x direction. 一旦这样做,就可以轻松地在线条的上方或下方进行绘制,因为它只是在y方向上,沿着线是x方向。

Thus if you have a line 因此,如果您有一行

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

Convert it to a vector and normalise that vector 将其转换为向量并将其标准化

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

The normalised vector represents the new x axis we want to render along, and the y axis is at 90 deg to that. 归一化向量表示我们要渲染的新x轴,而y轴与该轴成90度。 We can use setTransform to set both axis and the origin (0,0) point at the start of the line. 我们可以使用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)
)

Now rendering the line and arrow heads is easy as they are axis aligned 现在绘制线和箭头很容易,因为它们是轴对齐的

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();

To render two lines offset from the center 渲染偏离中心的两条线

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();

And you can reset the transform with 您可以使用

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