【问题标题】:How to convert TCanvas->Arc value to SVG Arc如何将 TCanvas->Arc 值转换为 SVG Arc
【发布时间】:2019-06-07 15:49:18
【问题描述】:

我为我的公司编写了一个从 Metafile 到 SVG (TCanvas->arc) 的转换器。 我已经完成了转换矩形或其他一些元素,但我不明白如何转换弧线。 我用 JavaScript 编写代码。 :)

我有一个文件,我在缓冲区中读取它并获取值,但这对你来说并不有趣。

所以我们目前拥有我能得到的所有值: 点1,点2,开始,结束

这4点已经给出,我现在应该画一个弧

    dc->Arc (Point1.x + offset->x,
             Point1.y + offset->y, 
             Point2.x + offset->x,
             Point2.y + offset->y,
              Start.x + offset->x,
              Start.y + offset->y, 
              Ende.x + offset->x, 
              Ende.y + offset->y);

他们当前正在使用此命令绘制圆弧。这里的偏移量可以不用注意。

如何从给定点获取所有信息以在 SVG 中绘制 Arc。

例如实际值:

         Point1: -50, -6
         Point2: -10, 34
         Start:  -10, 34
         End:    -10, -6 

         Point1:  1, 18
         Point2: 41, 58
         Start:  1, 18
         End:    1, 58 

如何获得:大圆弧标志、扫描标志和旋转以及我必须使用哪些值或计算它是否正确绘制。

我尝试绘制它并查看了很多文档并尝试以书面形式创建它。

【问题讨论】:

  • 您缺少一些标签。什么是 TCanvas?你说的是 Delphi TCanvas 吗?
  • SVG 需要弧的起点和终点。所以首先你需要找到椭圆和起点之间以及椭圆和终点线之间的两个交点。旋转将始终为 0。TCanvas 弧始终逆时针运行,因此您的扫描标志将始终为 0。对于大弧标志,您需要找到起点和终点线之间的角度。如果>180,大圆弧标志为1。
  • 这里的Delphi部分在哪里?是 TCanvas 吗?

标签: javascript delphi svg converters tcanvas


【解决方案1】:

我制作了一些似乎有效的东西。它基于the documentation here

我没有彻底测试过。

我假设在TCanvas 中,(0,0) 位于顶部。如果不是,则需要反转扫描和大弧标志的逻辑。

var svg = document.querySelector("svg");
var debug = svg.getElementById("debug");


function arc(x1, y1, x2, y2, x3, y3, x4, y4)
{
  let xRadius = Math.abs(x2 - x1) / 2;
  let yRadius = Math.abs(y2 - y1) / 2;
  let xCentre = Math.min(x1, x2) + xRadius;
  let yCentre = Math.min(y1, y2) + yRadius;

  // get intercepts relative to ellipse centre
  let startpt = interceptEllipseAndLine(xRadius, yRadius, x3 - xCentre, y3 - yCentre);
  let endpt = interceptEllipseAndLine(xRadius, yRadius, x4 - xCentre, y4 - yCentre);
  let largeArcFlag = isLargeArc(startpt, endpt) ? 1 : 0;

  return ['M', xCentre + startpt.x, yCentre + startpt.y,
          'A', xRadius, yRadius, 0, largeArcFlag, 0, xCentre + endpt.x, yCentre + endpt.y].join(' ');
}

// Finds the intercept of an ellipse and a line from centre to x0,y0
function interceptEllipseAndLine(xRadius, yRadius, x0,y0)
{
  let den = Math.sqrt(xRadius * xRadius * y0 * y0 + yRadius * yRadius * x0 * x0);
  let mult = xRadius * yRadius / den;
  return {x: mult * x0, y: mult * y0};
}

// Returns true if the angle between the two intercept lines is >= 180deg
function isLargeArc(start, end)
{
  let angle = Math.atan2(start.x * end.y - start.y * end.x, start.x * end.x + start.y * end.y);
  return angle > 0; 
}


let path1 = svg.getElementById("path1");
path1.setAttribute("d", arc(1, 18, 41, 58, 1, 18, 1, 58) );

let path2 = svg.getElementById("path2");
path2.setAttribute("d", arc(-50, -6, -10, 34, -10, 34, -10, -6) );
svg {
  width: 400px;
}

path {
  fill: none;
  stroke: red;
  stroke-width: 1px;
}
<svg viewBox="-100 -100 200 200">
  <path id="path1"/>
  <path id="path2"/>
</svg>

还有一个版本添加了一些额外的形状用于调试目的...

var svg = document.querySelector("svg");
var debug = svg.getElementById("debug");


function arc(x1, y1, x2, y2, x3, y3, x4, y4)
{
  let xRadius = Math.abs(x2 - x1) / 2;
  let yRadius = Math.abs(y2 - y1) / 2;
  let xCentre = Math.min(x1, x2) + xRadius;
  let yCentre = Math.min(y1, y2) + yRadius;

  {
    let rect = document.createElementNS(svg.namespaceURI, "rect");
    rect.setAttribute("x", x1);
    rect.setAttribute("y", y1);
    rect.setAttribute("width", x2-x1);
    rect.setAttribute("height", y2-y1);
    debug.append(rect);

    let ellipse = document.createElementNS(svg.namespaceURI, "ellipse");
    ellipse.setAttribute("cx", xCentre);
    ellipse.setAttribute("cy", yCentre);
    ellipse.setAttribute("rx", xRadius);
    ellipse.setAttribute("ry", yRadius);
    debug.append(ellipse);

    let start = document.createElementNS(svg.namespaceURI, "line");
    start.setAttribute("x1", xCentre);
    start.setAttribute("y1", yCentre);
    start.setAttribute("x2", x3);
    start.setAttribute("y2", y3);
    debug.append(start);

    let end = document.createElementNS(svg.namespaceURI, "line");
    end.setAttribute("x1", xCentre);
    end.setAttribute("y1", yCentre);
    end.setAttribute("x2", x4);
    end.setAttribute("y2", y4);
    debug.append(end);
  }

  // get intercepts relative to ellipse centre
  let startpt = interceptEllipseAndLine(xRadius, yRadius, x3 - xCentre, y3 - yCentre);
  let endpt = interceptEllipseAndLine(xRadius, yRadius, x4 - xCentre, y4 - yCentre);
  let largeArcFlag = isLargeArc(startpt, endpt) ? 1 : 0;

  {
    let circ = document.createElementNS(svg.namespaceURI, "circle");
    circ.setAttribute("cx", xCentre + startpt.x);
    circ.setAttribute("cy", yCentre + startpt.y);
    circ.setAttribute("r", 1);
    debug.append(circ);
  }

  return ['M', xCentre + startpt.x, yCentre + startpt.y,
          'A', xRadius, yRadius, 0, largeArcFlag, 0, xCentre + endpt.x, yCentre + endpt.y].join(' ');
}

// Finds the intercept of an ellipse and a line from centre to x0,y0
function interceptEllipseAndLine(xRadius, yRadius, x0,y0)
{
  let den = Math.sqrt(xRadius * xRadius * y0 * y0 + yRadius * yRadius * x0 * x0);
  let mult = xRadius * yRadius / den;
  return {x: mult * x0, y: mult * y0};
}

// Returns true if the angle between the two intercept lines is >= 180deg
function isLargeArc(start, end)
{
  let angle = Math.atan2(start.x * end.y - start.y * end.x, start.x * end.x + start.y * end.y);
  return angle > 0; 
}


let path1 = svg.getElementById("path1");
path1.setAttribute("d", arc(1, 18, 41, 58, 1, 18, 1, 58) );

let path2 = svg.getElementById("path2");
path2.setAttribute("d", arc(-50, -6, -10, 34, -10, 34, -10, -6) );
svg {
  width: 400px;
}

ellipse, rect, line {
  fill: none;
  stroke: lightgrey;
  stroke-width: 0.5px;
}

path {
  fill: none;
  stroke: red;
  stroke-width: 1px;
}
<svg viewBox="-100 -100 200 200">
  <g id="debug"></g>
  
  <path id="path1"/>
  <path id="path2"/>
</svg>

更新:派

对于 Pie 函数,它应该与 arc() 几乎相同,但它会返回稍微不同的路径。

function pie(x1, y1, x2, y2, x3, y3, x4, y4)
{
  // ... rest of function is the same as arc() ...
  return ['M', xCentre, yCentre,
          'L', xCentre + startpt.x, yCentre + startpt.y,
          'A', xRadius, yRadius, 0, largeArcFlag, 0, xCentre + endpt.x, yCentre + endpt.y,
          'Z'].join(' ');
}

【讨论】:

  • isLargeArc 真的不需要atan2 - 足以让return start.x * end.y - start.y * end.x &gt; 0; 提供相同的结果
  • 感谢您的回答。我将直接对其进行测试并分享我的结果。 :D
  • 你的帖子对我帮助很大。
  • 我怎样才能将此代码用于 TCanvas->Pie ?
  • 我已经更新了我的答案,对 Pie 函数进行了额外的猜测。
猜你喜欢
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
相关资源
最近更新 更多