如何从 canvas 示例中的椭圆三角形中识别值?

How to identify values from oval triangle on canvas example?

我试图从椭圆三角形的示例中识别以下参数,但是当我修改该行时:

drawCircle(canvas.width / 3,canvas.height / 2,2.5,'red');
//want to replace with specific values but its not working
drawCircle(32 / 3,33 / 2,2.5,'red');

我想识别正确的参数,以便演示可以将红色点更改为三角形内的其他 space

CH4=  20
C2H4= 70
C2H2= 20

演示: https://codepen.io/Barak/pen/WwdPxQ

我从 Whosebug 社区阅读了 post,但看不到值

在您提到的 post 中,markE 很好地复制了 Duval 三角形的外观。唯一的问题是包含 drawCircle() 函数的 codepen 只是一个虚拟对象,只不过是在任意位置放置一个点,由它的 x 和 y 参数给出。

要使此函数显示三角形上实际数据的正确位置 - 例如CH4=20 | C2H4=70 | C2H2=20 - 涉及的内容更多。

让我们更深入地了解一下,如果我们要在纸上以图形方式解决这个问题。 (以下内容基于this论文)

杜瓦尔三角形基本上是这样的等边三角形,边b=%CH4, 边 a=%C2H4c=%C2H2.

如果我们考虑以下示例数据%CH4=25 | %C2H4=35 | %C2H2=40

我们必须从 A 点到 C 点,b 边长度的 25% 并画一条平行于 c 边的线:

然后从C点到B点,a边长度的35%画一条平行于b边的直线:

最后从B点到A点,c边长的40%画一条平行于a边的直线:

因此,在这三条线相交的地方——如论文所述——我们有指示状态的目标位置。这可以使用普通三角函数以编程方式完成。好吧,我不太清楚为什么我们需要这三行。由于 CH4 的百分比总是平行于三角形的底边,而 C2H4 总是平行于边 b,我们还有一个交点,C2H2 的线是自动给出的。

基本上我们只需要一个函数来计算 CH4 和 C2H4 线之间的交点。

我采用了 markE 的现有代码并通过函数 plotResult() 对其进行了增强,该函数采用三个参数来表示 CH4、C2H2 和 C2H4 ppm 值:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// https://www.researchgate.net/publication/4345236_A_Software_Implementation_of_the_Duval_Triangle_Method

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

var v0 = {
  x: 114,
  y: 366
};
var v1 = {
  x: 306,
  y: 30
};
var v2 = {
  x: 498,
  y: 366
};
var triangle = [v0, v1, v2];

// Define all your segments here
var segments = [{
    points: [{
      x: 114,
      y: 366
    }, {
      x: 281,
      y: 76
    }, {
      x: 324,
      y: 150
    }, {
      x: 201,
      y: 366
    }],
    fill: 'rgb(172,236,222)',
    label: {
      text: 'D1',
      cx: 200,
      cy: 290,
      withLine: false,
      endX: null,
      endY: null
    },
  },
  {
    points: [{
      x: 385,
      y: 366
    }, {
      x: 201,
      y: 366
    }, {
      x: 324,
      y: 150
    }, {
      x: 356,
      y: 204
    }, {
      x: 321,
      y: 256
    }],
    fill: 'deepskyblue',
    label: {
      text: 'D2',
      cx: 290,
      cy: 290,
      withLine: false,
      endX: null,
      endY: null
    },
  },
  {
    points: [{
      x: 297,
      y: 46
    }, {
      x: 392,
      y: 214
    }, {
      x: 372,
      y: 248
    }, {
      x: 441,
      y: 366
    }, {
      x: 385,
      y: 366
    }, {
      x: 321,
      y: 256
    }, {
      x: 356,
      y: 204
    }, {
      x: 281,
      y: 76
    }],
    fill: 'lightCyan',
    label: {
      text: 'DT',
      cx: 370,
      cy: 290,
      withLine: false,
      endX: 366,
      endY: 120
    },
  },
  {
    points: [{
      x: 306,
      y: 30
    }, {
      x: 312,
      y: 40
    }, {
      x: 300,
      y: 40
    }],
    fill: 'black',
    label: {
      text: 'PD',
      cx: 356,
      cy: 40,
      withLine: true,
      endX: 321,
      endY: 40
    },
  },
  {
    points: [{
      x: 312,
      y: 40
    }, {
      x: 348,
      y: 103
    }, {
      x: 337,
      y: 115
    }, {
      x: 297,
      y: 46
    }, {
      x: 300,
      y: 40
    }],
    fill: 'navajoWhite',
    label: {
      text: 'T1',
      cx: 375,
      cy: 70,
      withLine: true,
      endX: 340,
      endY: 75
    },
  },
  {
    points: [{
      x: 348,
      y: 103
    }, {
      x: 402,
      y: 199
    }, {
      x: 392,
      y: 214
    }, {
      x: 337,
      y: 115
    }],
    fill: 'tan',
    label: {
      text: 'T2',
      cx: 400,
      cy: 125,
      withLine: true,
      endX: 366,
      endY: 120
    },
  },
  {
    points: [{
      x: 402,
      y: 199
    }, {
      x: 498,
      y: 366
    }, {
      x: 441,
      y: 366
    }, {
      x: 372,
      y: 248
    }],
    fill: 'peru',
    label: {
      text: 'T3',
      cx: 425,
      cy: 290,
      withLine: false,
      endX: null,
      endY: null
    },
  },
];

// label styles
var labelfontsize = 12;
var labelfontface = 'verdana';
var labelpadding = 3;

// pre-create a canvas-image of the arrowhead
var arrowheadLength = 10;
var arrowheadWidth = 8;
var arrowhead = document.createElement('canvas');
premakeArrowhead();

var legendTexts = ['PD = Partial Discharge', 'T1 = Thermal fault < 300 celcius', '...'];


// start drawing
/////////////////////


// draw colored segments inside triangle
for (var i = 0; i < segments.length; i++) {
  drawSegment(segments[i]);
}
// draw ticklines
ticklines(v0, v1, 9, 0, 20);
ticklines(v1, v2, 9, Math.PI * 3 / 4, 20);
ticklines(v2, v0, 9, Math.PI * 5 / 4, 20);
// molecules
moleculeLabel(v0, v1, 100, Math.PI, '% CH4');
moleculeLabel(v1, v2, 100, 0, '% C2H4');
moleculeLabel(v2, v0, 75, Math.PI / 2, '% C2H2');
// draw outer triangle
drawTriangle(triangle);
// draw legend
drawLegend(legendTexts, 10, 10, 12.86);

plotResult(25, 40, 35);
// end drawing
/////////////////////

function drawSegment(s) {
  // draw and fill the segment path
  ctx.beginPath();
  ctx.moveTo(s.points[0].x, s.points[0].y);
  for (var i = 1; i < s.points.length; i++) {
    ctx.lineTo(s.points[i].x, s.points[i].y);
  }
  ctx.closePath();
  ctx.fillStyle = s.fill;
  ctx.fill();
  ctx.lineWidth = 2;
  ctx.strokeStyle = 'black';
  ctx.stroke();
  // draw segment's box label
  if (s.label.withLine) {
    lineBoxedLabel(s, labelfontsize, labelfontface, labelpadding);
  } else {
    boxedLabel(s, labelfontsize, labelfontface, labelpadding);
  }
}


function moleculeLabel(start, end, offsetLength, angle, text) {
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle'
  ctx.font = '14px verdana';
  var dx = end.x - start.x;
  var dy = end.y - start.y;
  var x0 = parseInt(start.x + dx * 0.50);
  var y0 = parseInt(start.y + dy * 0.50);
  var x1 = parseInt(x0 + offsetLength * Math.cos(angle));
  var y1 = parseInt(y0 + offsetLength * Math.sin(angle));
  ctx.fillStyle = 'black';
  ctx.fillText(text, x1, y1);
  // arrow
  var x0 = parseInt(start.x + dx * 0.35);
  var y0 = parseInt(start.y + dy * 0.35);
  var x1 = parseInt(x0 + 50 * Math.cos(angle));
  var y1 = parseInt(y0 + 50 * Math.sin(angle));
  var x2 = parseInt(start.x + dx * 0.65);
  var y2 = parseInt(start.y + dy * 0.65);
  var x3 = parseInt(x2 + 50 * Math.cos(angle));
  var y3 = parseInt(y2 + 50 * Math.sin(angle));
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x3, y3);
  ctx.strokeStyle = 'black';
  ctx.lineWidth = 1;
  ctx.stroke();
  var angle = Math.atan2(dy, dx);
  ctx.translate(x3, y3);
  ctx.rotate(angle);
  ctx.drawImage(arrowhead, -arrowheadLength, -arrowheadWidth / 2);
  ctx.setTransform(1, 0, 0, 1, 0, 0);
}


function boxedLabel(s, fontsize, fontface, padding) {
  var centerX = s.label.cx;
  var centerY = s.label.cy;
  var text = s.label.text;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle'
  ctx.font = fontsize + 'px ' + fontface
  var textwidth = ctx.measureText(text).width;
  var textheight = fontsize * 1.286;
  var leftX = centerX - textwidth / 2 - padding;
  var topY = centerY - textheight / 2 - padding;
  ctx.fillStyle = 'white';
  ctx.fillRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
  ctx.lineWidth = 1;
  ctx.strokeRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
  ctx.fillStyle = 'black';
  ctx.fillText(text, centerX, centerY);
}


function lineBoxedLabel(s, fontsize, fontface, padding) {
  var centerX = s.label.cx;
  var centerY = s.label.cy;
  var text = s.label.text;
  var lineToX = s.label.endX;
  var lineToY = s.label.endY;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle'
  ctx.font = fontsize + 'px ' + fontface
  var textwidth = ctx.measureText(text).width;
  var textheight = fontsize * 1.286;
  var leftX = centerX - textwidth / 2 - padding;
  var topY = centerY - textheight / 2 - padding;
  // the line
  ctx.beginPath();
  ctx.moveTo(leftX, topY + textheight / 2);
  ctx.lineTo(lineToX, topY + textheight / 2);
  ctx.strokeStyle = 'black';
  ctx.lineWidth = 1;
  ctx.stroke();
  // the boxed text
  ctx.fillStyle = 'white';
  ctx.fillRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
  ctx.strokeRect(leftX, topY, textwidth + padding * 2, textheight + padding * 2);
  ctx.fillStyle = 'black';
  ctx.fillText(text, centerX, centerY);
}


function ticklines(start, end, count, angle, length) {
  var dx = end.x - start.x;
  var dy = end.y - start.y;
  ctx.lineWidth = 1;
  for (var i = 1; i < count; i++) {
    var x0 = parseInt(start.x + dx * i / count);
    var y0 = parseInt(start.y + dy * i / count);
    var x1 = parseInt(x0 + length * Math.cos(angle));
    var y1 = parseInt(y0 + length * Math.sin(angle));
    ctx.beginPath();
    ctx.moveTo(x0, y0);
    ctx.lineTo(x1, y1);
    ctx.stroke();
    if (i == 2 || i == 4 || i == 6 || i == 8) {
      var labelOffset = length * 3 / 4;
      var x1 = parseInt(x0 - labelOffset * Math.cos(angle));
      var y1 = parseInt(y0 - labelOffset * Math.sin(angle));
      ctx.fillStyle = 'black';
      ctx.fillText(parseInt(i * 10), x1, y1);
    }
  }
}


function premakeArrowhead() {
  var actx = arrowhead.getContext('2d');
  arrowhead.width = arrowheadLength;
  arrowhead.height = arrowheadWidth;
  actx.beginPath();
  actx.moveTo(0, 0);
  actx.lineTo(arrowheadLength, arrowheadWidth / 2);
  actx.lineTo(0, arrowheadWidth);
  actx.closePath();
  actx.fillStyle = 'black';
  actx.fill();
}


function drawTriangle(t) {
  ctx.beginPath();
  ctx.moveTo(t[0].x, t[0].y);
  ctx.lineTo(t[1].x, t[1].y);
  ctx.lineTo(t[2].x, t[2].y);
  ctx.closePath();
  ctx.strokeStyle = 'black';
  ctx.lineWidth = 2;
  ctx.stroke();
}


function drawLegend(texts, x, y, lineheight) {
  ctx.textAlign = 'left';
  ctx.textBaseline = 'top';
  ctx.fillStyle = 'black';
  ctx.font = '12px arial';
  for (var i = 0; i < texts.length; i++) {
    ctx.fillText(texts[i], x, y + i * lineheight);
  }
}

function plotResult(val1, val2, val3) {
  let deltaX, length;
  let sum = val1 + val2 + val3;
  const cos60 = Math.cos(Math.PI / 3);
  const sin60 = Math.sin(Math.PI / 3);
  let ch4 = val1 / sum;
  let c2h2 = val2 / sum;
  let c2h4 = val3 / sum;

  length = Math.sqrt(Math.pow((v1.x - v0.x), 2) + Math.pow((v1.y - v0.y), 2));
  let ch4PointA = new Point(v0.x + (length * ch4) * cos60, v0.y - (length * ch4) * sin60);
  length = Math.sqrt(Math.pow((v2.x - v1.x), 2) + Math.pow((v2.y - v1.y), 2));
  let ch4PointB = new Point(v2.x - (length * ch4) * cos60, v2.y - (length * ch4) * sin60);

  length = Math.sqrt(Math.pow((v1.x - v2.x), 2) + Math.pow((v1.y - v2.y), 2));
  let c2h4PointA = new Point(v1.x + (length * c2h4) * cos60, v1.y + (length * c2h4) * sin60);

  deltaX = (v2.x - v0.x) * c2h4;
  let c2h4PointB = new Point(v0.x + deltaX, v0.y);

  let point = getIntersection(ch4PointA, ch4PointB, c2h4PointA, c2h4PointB);

  ctx.beginPath();
  ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI, false);
  ctx.fillStyle = "red";
  ctx.fill();
}

function getIntersection(pointA, pointB, pointC, pointD) {
  let denominator, a, b, numeratorA, numeratorB;
  denominator = ((pointD.y - pointC.y) * (pointB.x - pointA.x)) - ((pointD.x - pointC.x) * (pointB.y - pointA.y));
  a = pointA.y - pointC.y;
  b = pointA.x - pointC.x;
  numeratorA = ((pointD.x - pointC.x) * a) - ((pointD.y - pointC.y) * b);
  numeratorB = ((pointB.x - pointA.x) * a) - ((pointB.y - pointA.y) * b);
  a = numeratorA / denominator;
  b = numeratorB / denominator;

  return new Point(pointA.x + (a * (pointB.x - pointA.x)), pointA.y + (a * (pointB.y - pointA.y)));
}
body {
  background-color: ivory;
  padding: 10px;
}

#canvas {
  border: 1px solid red;
  margin: 0 auto;
}
<canvas id="canvas" width=650 height=500></canvas>