如何将三次曲线的 2 个控制点转换为二次曲线的单个控制点?

发布于 2024-08-16 11:47:14 字数 308 浏览 8 评论 0原文

在网上搜索后,我看到各种论坛上的不同人都提到用二次曲线来近似三次曲线。但我找不到公式。

我想要的是这样的:

输入:startX,startY,control1X,control1Y,control2X,control2Y,endX,endY 输出:startX,startY,controlX,controlY,endX,endY

实际上,由于起点和终点相同,所以我真正需要的是...

输入:startX,startY,control1X,control1Y,control2X,control2Y,endX,结束Y 输出:控制X、控制Y

Having searched the web, I see various people in various forums alluding to approximating a cubic curve with a quadratic one. But I can't find the formula.

What I want is this:

input: startX, startY, control1X, control1Y, control2X, control2Y, endX, endY
output: startX, startY, controlX, controlY, endX, endY

Actually, since the starting and ending points will be the same, all I really need is...

input: startX, startY, control1X, control1Y, control2X, control2Y, endX, endY
output: controlX, controlY

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

最初的梦 2024-08-23 11:47:14

如前所述,控制点从 4 个增加到 3 个通常是一个近似值。只有一种情况是准确的 - 当三次贝塞尔曲线实际上是一条提升度数的二次贝塞尔曲线时。

您可以使用度数高程方程得出近似值。这很简单,而且结果通常都很好。

我们将三次方的控制点称为 Q0..Q3,将二次方的控制点称为 P0..P2。然后对于度数提升,方程为:

Q0 = P0
Q1 = 1/3 P0 + 2/3 P1
Q2 = 2/3 P1 + 1/3 P2
Q3 = P2

在您的情况下,您有 Q0..Q3,并且您正在求解 P0..P2。有两种方法可以根据上面的方程计算 P1:

P1 = 3/2 Q1 - 1/2 Q0
P1 = 3/2 Q2 - 1/2 Q3

如果这是一个三次方,则两个方程将为 P1 提供相同的答案。由于可能性不大,所以最好的办法就是对它们进行平均。因此,

P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3

换句话说:

controlX = -0.25*startX + .75*control1X + .75*control2X -0.25*endX

Y 的计算方式类似 - 尺寸是独立的,因此这适用于 3d(或 nd)。

这将是一个近似值。如果您需要更好的近似值,获得它的一种方法是使用 deCastlejau 算法细分初始立方体,然后对每个段进行度约化。如果您需要更好的连续性,还有其他不那么快速和肮脏的近似方法。

As mentioned, going from 4 control points to 3 is normally going to be an approximation. There's only one case where it will be exact - when the cubic bezier curve is actually a degree-elevated quadratic bezier curve.

You can use the degree elevation equations to come up with an approximation. It's simple, and the results are usually pretty good.

Let's call the control points of the cubic Q0..Q3 and the control points of the quadratic P0..P2. Then for degree elevation, the equations are:

Q0 = P0
Q1 = 1/3 P0 + 2/3 P1
Q2 = 2/3 P1 + 1/3 P2
Q3 = P2

In your case you have Q0..Q3 and you're solving for P0..P2. There are two ways to compute P1 from the equations above:

P1 = 3/2 Q1 - 1/2 Q0
P1 = 3/2 Q2 - 1/2 Q3

If this is a degree-elevated cubic, then both equations will give the same answer for P1. Since it's likely not, your best bet is to average them. So,

P1 = -1/4 Q0 + 3/4 Q1 + 3/4 Q2 - 1/4 Q3

To translate to your terms:

controlX = -0.25*startX + .75*control1X + .75*control2X -0.25*endX

Y is computed similarly - the dimensions are independent, so this works for 3d (or n-d).

This will be an approximation. If you need a better approximation, one way to get it is by subdividing the initial cubic using the deCastlejau algorithm, and then degree-reduce each segment. If you need better continuity, there are other approximation methods that are less quick and dirty.

涫野音 2024-08-23 11:47:14

三次方可以有环和尖点,而二次方不能有。这意味着几乎从来没有简单的解决方案。如果三次方已经是二次方,则存在简单解。通常,您必须将三次方除以二次方部分。你必须决定细分的关键点是什么。

http://fontforge.org/bezier.html#ps2ttf 说:
“我在网上读到的其他来源建议检查三次样条曲线的拐点(二次样条曲线不能有)并在那里强制中断。在我看来,这实际上使结果变得更糟,它使用了更多的点,并且近似值看起来并不好就像忽略拐点一样接近,所以我忽略它们。”

确实如此,拐点(三次方的二阶导数)还不够。但是,如果您还考虑到三次函数的一阶导数的局部极值(最小值、最大值),并且对所有这些进行力破坏,则子曲线都是二次曲线,并且可以用二次曲线表示。

我测试了以下函数,它们按预期工作(找到立方体的所有临界点并将立方体划分为下高的立方体)。当绘制这些子曲线时,曲线与原始三次曲线完全相同,但由于某种原因,当将子曲线绘制为二次曲线时,结果几乎正确,但不完全正确。

所以这个答案并不是对问题的严格帮助,但这些函数提供了三次到二次转换的起点。

要找到局部极值和拐点,以下 get_t_values_of_ritic_points() 应提供它们。当

function compare_num(a,b) {
  if (a < b) return -1;
  if (a > b) return 1;
  return 0;
}

function find_inflection_points(p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y)
{
  var ax = -p1x + 3*p2x - 3*p3x + p4x;
  var bx = 3*p1x - 6*p2x + 3*p3x;
  var cx = -3*p1x + 3*p2x;

  var ay = -p1y + 3*p2y - 3*p3y + p4y;
  var by = 3*p1y - 6*p2y + 3*p3y;
  var cy = -3*p1y + 3*p2y;
  var a = 3*(ay*bx-ax*by);
  var b = 3*(ay*cx-ax*cy);
  var c = by*cx-bx*cy;
  var r2 = b*b - 4*a*c;
  var firstIfp = 0;
  var secondIfp = 0;
  if (r2>=0 && a!==0)
  {
    var r = Math.sqrt(r2);
    firstIfp = (-b + r) / (2*a);
    secondIfp = (-b - r) / (2*a);
    if ((firstIfp>0 && firstIfp<1) && (secondIfp>0 && secondIfp<1))
    {
      if (firstIfp>secondIfp)
      {
        var tmp = firstIfp;
        firstIfp = secondIfp;
        secondIfp = tmp;
      }
      if (secondIfp-firstIfp >0.00001)
        return [firstIfp, secondIfp];
      else return [firstIfp];
    }
    else if (firstIfp>0 && firstIfp<1)
      return [firstIfp];
    else if (secondIfp>0 && secondIfp<1)
    {
      firstIfp = secondIfp;
      return [firstIfp];
    }
    return [];
  }
  else return [];
}

function get_t_values_of_critical_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
    var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
    b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
    c = p1x - c1x,
    t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
    t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
    tvalues=[];
    Math.abs(t1) > "1e12" && (t1 = 0.5);
    Math.abs(t2) > "1e12" && (t2 = 0.5);
    if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1)
    if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);

    a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
    b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
    c = p1y - c1y;
    t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
    t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
    Math.abs(t1) > "1e12" && (t1 = 0.5);
    Math.abs(t2) > "1e12" && (t2 = 0.5);
    if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1);
    if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);

    var inflectionpoints = find_inflection_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
    if (inflectionpoints[0]) tvalues.push(inflectionpoints[0]);
    if (inflectionpoints[1]) tvalues.push(inflectionpoints[1]);

    tvalues.sort(compare_num);
    return tvalues;
};

您拥有这些临界 t 值(范围为 0-1)时,您可以将三次曲线划分为多个部分:

function CPoint()
{
  var arg = arguments;
  if (arg.length==1)
  {
    this.X = arg[0].X;
    this.Y = arg[0].Y;
  }
  else if (arg.length==2)
  {
    this.X = arg[0];
    this.Y = arg[1];
  }
}

function subdivide_cubic_to_cubics()
{
    var arg = arguments;
    if (arg.length!=9) return [];
    var m_p1 = {X:arg[0], Y:arg[1]};
  var m_p2 = {X:arg[2], Y:arg[3]};
  var m_p3 = {X:arg[4], Y:arg[5]};
  var m_p4 = {X:arg[6], Y:arg[7]};
  var t = arg[8];
  var p1p = new CPoint(m_p1.X + (m_p2.X - m_p1.X) * t,
                       m_p1.Y + (m_p2.Y - m_p1.Y) * t);
  var p2p = new CPoint(m_p2.X + (m_p3.X - m_p2.X) * t,
                       m_p2.Y + (m_p3.Y - m_p2.Y) * t);
  var p3p = new CPoint(m_p3.X + (m_p4.X - m_p3.X) * t,
                       m_p3.Y + (m_p4.Y - m_p3.Y) * t);
  var p1d = new CPoint(p1p.X + (p2p.X - p1p.X) * t,
                       p1p.Y + (p2p.Y - p1p.Y) * t);
  var p2d = new CPoint(p2p.X + (p3p.X - p2p.X) * t,
                       p2p.Y + (p3p.Y - p2p.Y) * t);
  var p1t = new CPoint(p1d.X + (p2d.X - p1d.X) * t,
                       p1d.Y + (p2d.Y - p1d.Y) * t);
  return [[m_p1.X, m_p1.Y, p1p.X, p1p.Y, p1d.X, p1d.Y, p1t.X, p1t.Y],
          [p1t.X, p1t.Y, p2d.X, p2d.Y, p3p.X, p3p.Y, m_p4.X, m_p4.Y]];
}

上面代码中的 subdivide_cubic_to_cubics() 将原始三次曲线划分为两部分值t。因为 get_t_values_of_ritic_points() 将 t 值作为按 t 值排序的数组返回,因此您可以轻松遍历所有 t 值并获取相应的子曲线。当您拥有这些划分的曲线时,您必须将第二条子曲线除以下一个 t 值。

当所有分割完成后,您就拥有了所有子曲线的控制点。现在只剩下三次控制点到二次控制点的转换了。由于所有子曲线现在都是下高的三次曲线,因此相应的二次控制点很容易计算。二次方的第一个和最后一个控制点与三次方(子曲线)的第一个和最后一个控制点相同,中间的控制点位于直线 P1-P2 和 P4-P3 交叉的点处。

The cubic can have loops and cusps, which quadratic cannot have. This means that there are not simple solutions nearly never. If cubic is already a quadratic, then the simple solution exists. Normally you have to divide cubic to parts that are quadratics. And you have to decide what are the critical points for subdividing.

http://fontforge.org/bezier.html#ps2ttf says:
"Other sources I have read on the net suggest checking the cubic spline for points of inflection (which quadratic splines cannot have) and forcing breaks there. To my eye this actually makes the result worse, it uses more points and the approximation does not look as close as it does when ignoring the points of inflection. So I ignore them."

This is true, the inflection points (second derivatives of cubic) are not enough. But if you take into account also local extremes (min, max) which are the first derivatives of cubic function, and force breaks on those all, then the sub curves are all quadratic and can be presented by quadratics.

I tested the below functions, they work as expected (find all critical points of cubic and divides the cubic to down-elevated cubics). When those sub curves are drawn, the curve is exactly the same as original cubic, but for some reason, when sub curves are drawn as quadratics, the result is nearly right, but not exactly.

So this answer is not for strict help for the problem, but those functions provide a starting point for cubic to quadratic conversion.

To find both local extremes and inflection points, the following get_t_values_of_critical_points() should provide them. The

function compare_num(a,b) {
  if (a < b) return -1;
  if (a > b) return 1;
  return 0;
}

function find_inflection_points(p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y)
{
  var ax = -p1x + 3*p2x - 3*p3x + p4x;
  var bx = 3*p1x - 6*p2x + 3*p3x;
  var cx = -3*p1x + 3*p2x;

  var ay = -p1y + 3*p2y - 3*p3y + p4y;
  var by = 3*p1y - 6*p2y + 3*p3y;
  var cy = -3*p1y + 3*p2y;
  var a = 3*(ay*bx-ax*by);
  var b = 3*(ay*cx-ax*cy);
  var c = by*cx-bx*cy;
  var r2 = b*b - 4*a*c;
  var firstIfp = 0;
  var secondIfp = 0;
  if (r2>=0 && a!==0)
  {
    var r = Math.sqrt(r2);
    firstIfp = (-b + r) / (2*a);
    secondIfp = (-b - r) / (2*a);
    if ((firstIfp>0 && firstIfp<1) && (secondIfp>0 && secondIfp<1))
    {
      if (firstIfp>secondIfp)
      {
        var tmp = firstIfp;
        firstIfp = secondIfp;
        secondIfp = tmp;
      }
      if (secondIfp-firstIfp >0.00001)
        return [firstIfp, secondIfp];
      else return [firstIfp];
    }
    else if (firstIfp>0 && firstIfp<1)
      return [firstIfp];
    else if (secondIfp>0 && secondIfp<1)
    {
      firstIfp = secondIfp;
      return [firstIfp];
    }
    return [];
  }
  else return [];
}

function get_t_values_of_critical_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
    var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
    b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
    c = p1x - c1x,
    t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
    t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
    tvalues=[];
    Math.abs(t1) > "1e12" && (t1 = 0.5);
    Math.abs(t2) > "1e12" && (t2 = 0.5);
    if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1)
    if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);

    a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
    b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
    c = p1y - c1y;
    t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
    t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
    Math.abs(t1) > "1e12" && (t1 = 0.5);
    Math.abs(t2) > "1e12" && (t2 = 0.5);
    if (t1 >= 0 && t1 <= 1 && tvalues.indexOf(t1)==-1) tvalues.push(t1);
    if (t2 >= 0 && t2 <= 1 && tvalues.indexOf(t2)==-1) tvalues.push(t2);

    var inflectionpoints = find_inflection_points(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
    if (inflectionpoints[0]) tvalues.push(inflectionpoints[0]);
    if (inflectionpoints[1]) tvalues.push(inflectionpoints[1]);

    tvalues.sort(compare_num);
    return tvalues;
};

And when you have those critical t values (which are from range 0-1), you can divide the cubic to parts:

function CPoint()
{
  var arg = arguments;
  if (arg.length==1)
  {
    this.X = arg[0].X;
    this.Y = arg[0].Y;
  }
  else if (arg.length==2)
  {
    this.X = arg[0];
    this.Y = arg[1];
  }
}

function subdivide_cubic_to_cubics()
{
    var arg = arguments;
    if (arg.length!=9) return [];
    var m_p1 = {X:arg[0], Y:arg[1]};
  var m_p2 = {X:arg[2], Y:arg[3]};
  var m_p3 = {X:arg[4], Y:arg[5]};
  var m_p4 = {X:arg[6], Y:arg[7]};
  var t = arg[8];
  var p1p = new CPoint(m_p1.X + (m_p2.X - m_p1.X) * t,
                       m_p1.Y + (m_p2.Y - m_p1.Y) * t);
  var p2p = new CPoint(m_p2.X + (m_p3.X - m_p2.X) * t,
                       m_p2.Y + (m_p3.Y - m_p2.Y) * t);
  var p3p = new CPoint(m_p3.X + (m_p4.X - m_p3.X) * t,
                       m_p3.Y + (m_p4.Y - m_p3.Y) * t);
  var p1d = new CPoint(p1p.X + (p2p.X - p1p.X) * t,
                       p1p.Y + (p2p.Y - p1p.Y) * t);
  var p2d = new CPoint(p2p.X + (p3p.X - p2p.X) * t,
                       p2p.Y + (p3p.Y - p2p.Y) * t);
  var p1t = new CPoint(p1d.X + (p2d.X - p1d.X) * t,
                       p1d.Y + (p2d.Y - p1d.Y) * t);
  return [[m_p1.X, m_p1.Y, p1p.X, p1p.Y, p1d.X, p1d.Y, p1t.X, p1t.Y],
          [p1t.X, p1t.Y, p2d.X, p2d.Y, p3p.X, p3p.Y, m_p4.X, m_p4.Y]];
}

subdivide_cubic_to_cubics() in above code divides an original cubic curve to two parts by the value t. Because get_t_values_of_critical_points() returns t values as an array sorted by t value, you can easily traverse all t values and get the corresponding sub curve. When you have those divided curves, you have to divide the 2nd sub curve by the next t value.

When all splitting is proceeded, you have the control points of all sub curves. Now there are left only the cubic control point conversion to quadratic. Because all sub curves are now down-elevated cubics, the corresponding quadratic control points are easy to calculate. The first and last of quadratic control points are the same as cubic's (sub curve) first and last control point and the middle one is found in the point, where lines P1-P2 and P4-P3 crosses.

╰ゝ天使的微笑 2024-08-23 11:47:14

约定/术语

  • 三次定义为:P1/2 - 锚点,C1/C2 控制点
  • |x| 欧氏范数
  • 是立方体的 x 中点近似值的

:与立方体共享相同锚点且控制点位于 C = (3·C2 - P2 + 3·C1 - P1)/4 的四边形算法

  1. 选择绝对精度 (prec)
  2. 计算 Tdiv 作为(三次)方程 sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/ 的根2 · Tdiv ^ 3 = prec
  3. 如果 Tdiv < 0.5 在 Tdiv 处除三次。第一段 [0..Tdiv] 可以通过二次方来近似,缺陷小于 prec,通过中点近似。对第二个结果段(对应于 1-Tdiv)重复步骤 2
  4. 0.5<=Tdiv<1 - 只需将立方体一分为二。两半可以通过中点近似来近似
  5. Tdiv>=1 - 整个立方体可以通过中点近似来近似

演示了步骤 2 中的“神奇公式”(带有交互式示例) 在此页面

Conventions/terminology

  • Cubic defined by: P1/2 - anchor points, C1/C2 control points
  • |x| is the euclidean norm of x
  • mid-point approx of cubic: a quad that shares the same anchors with the cubic and has the control point at C = (3·C2 - P2 + 3·C1 - P1)/4

Algorithm

  1. pick an absolute precision (prec)
  2. Compute the Tdiv as the root of (cubic) equation sqrt(3)/18 · |P2 - 3·C2 + 3·C1 - P1|/2 · Tdiv ^ 3 = prec
  3. if Tdiv < 0.5 divide the cubic at Tdiv. First segment [0..Tdiv] can be approximated with by a quadratic, with a defect less than prec, by the mid-point approximation. Repeat from step 2 with the second resulted segment (corresponding to 1-Tdiv)
  4. 0.5<=Tdiv<1 - simply divide the cubic in two. The two halves can be approximated by the mid-point approximation
  5. Tdiv>=1 - the entire cubic can be approximated by the mid-point approximation

The "magic formula" at step 2 is demonstrated (with interactive examples) on this page.

转身泪倾城 2024-08-23 11:47:14

tfinniga 答案的另一个推导:
首先参见维基百科贝塞尔曲线
对于二次和三次贝塞尔曲线的公式(也很好的动画):

Q(t) = (1-t)^2 P0 + 2 (1-t) t Q + t^2 P3
P(t) + (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3

要求这些在中间匹配,t = 1/2:(

(P0 + 2 Q + P3) / 4 = (P0 + 3 P1 + 3 P2 + P3) / 8  
=> Q = P1 + P2 - (P0 + P1 + P2 + P3) / 4  

这样写的 Q 有几何解​​释:

Pmid = middle of P0 P1 P2 P3  
P12mid = midway between P1 and P2  
draw a line from Pmid to P12mid, and that far again: you're at Q.  

希望这是有道理的 - 画几个例子.)

Another derivation of tfinniga's answer:
First see Wikipedia Bezier curve
for the formulas for quadratic and cubic Bezier curves (also nice animations):

Q(t) = (1-t)^2 P0 + 2 (1-t) t Q + t^2 P3
P(t) + (1-t)^3 P0 + 3 (1-t)^2 t P1 + 3 (1-t) t^2 P2 + t^3 P3

Require these to match at the middle, t = 1/2:

(P0 + 2 Q + P3) / 4 = (P0 + 3 P1 + 3 P2 + P3) / 8  
=> Q = P1 + P2 - (P0 + P1 + P2 + P3) / 4  

(Q written like this has a geometric interpretation:

Pmid = middle of P0 P1 P2 P3  
P12mid = midway between P1 and P2  
draw a line from Pmid to P12mid, and that far again: you're at Q.  

Hope this makes sense -- draw a couple of examples.)

太阳男子 2024-08-23 11:47:14

一般来说,您必须使用多个二次曲线 - 许多情况下三次曲线甚至不能用单个二次曲线模糊地近似。

有一篇很好的文章讨论了这个问题,并提供了多种解决方法,位于 http ://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm(包括交互式演示)。

In general, you'll have to use multiple quadratic curves - many cases of cubic curves can't be even vaguely approximated with a single quadratic curve.

There is a good article discussing the problem, and a number of ways to solve it, at http://www.timotheegroleau.com/Flash/articles/cubic_bezier_in_flash.htm (including interactive demonstrations).

女皇必胜 2024-08-23 11:47:14

我应该注意到,阿德里安的解决方案对于单个三次方非常有用,但是当三次方是平滑三次样条线的段时,使用他的中点近似方法会导致段节点处的斜率连续性丢失。因此 http://fontforge.org/bezier.html#ps2ttf 中描述的方法要好得多如果您正在使用字体字形或出于任何其他原因您想要保留曲线的平滑度(最有可能是这种情况)。

尽管这是一个老问题,但像我这样的很多人都会在搜索结果中看到它,所以我将其发布在这里。

I should note that Adrian's solution is great for single cubics, but when the cubics are segments of a smooth cubic spline, then using his midpoint approximation method causes slope continuity at the nodes of the segments to be lost. So the method described at http://fontforge.org/bezier.html#ps2ttf is much better if you are working with font glyphs or for any other reason you want to retain the smoothness of the curve (which is most probably the case).

Even though this is an old question, many people like me will see it in search results, so I'm posting this here.

瞎闹 2024-08-23 11:47:14

我可能会绘制一系列曲线,而不是尝试使用不同的算法绘制一条曲线。有点像画两个半圆组成一个完整的圆。

I would probably draw a series of curves instead of trying to draw one curve using a different alg. Sort of like drawing two half circles to make up a whole circle.

潜移默化 2024-08-23 11:47:14

尝试寻找开源 Postcript 字体到 Truetype 字体转换器。我确信他们有。 Postscript 使用三次贝塞尔曲线,而 Truetype 使用二次贝塞尔曲线。祝你好运。

Try looking for opensource Postcript font to Truetype font converters. I'm sure they have it. Postscript uses cubic bezier curves, whereas Truetype uses quadratic bezier curves. Good luck.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文