在PostScript上绘制圆圈很容易,我很惊讶PDF显然没有传承那些相同的原语。有很多商业图书馆可以做到这一点,但它不应该比这简单吗?
还有一些使用贝塞尔曲线的技巧,但你没有得到一个完美的圆,你必须在连接段中绘制它们。只要它看起来接近完美,我就不需要一个完美的圆圈。
我这样做是PDF-EasyPDF Perl模块的补充,但语言不是我需要帮助的部分。
答案 0 :(得分:5)
情况总是如此。 PDF在原语中没有圆圈,只有贝塞尔曲线。 PDF成像模型是根据PostScript成像模型构建的,PostScript成像模型本身仅使用arc / arcto基元提供圆形,它们本身是根据贝塞尔曲线实现的。
奇怪的是,我曾打电话在我正在研究的一些测试代码中执行这个确切的任务,生成PDF文件。我是这样做的:
private void DrawEllipse(PdfGraphics g, double xrad, double yrad)
{
const double magic = 0.551784;
double xmagic = xrad * magic;
double ymagic = yrad * magic;
g.MoveTo(-xrad, 0);
g.CurveTo(-xrad, ymagic, -xmagic, yrad, 0, yrad);
g.CurveTo(xmagic, yrad, xrad, ymagic, xrad, 0);
g.CurveTo(xrad, -ymagic, xmagic, -yrad, 0, -yrad);
g.CurveTo(-xmagic, -yrad, -xrad, -ymagic, -xrad, 0);
}
private void DrawCircle(PdfGraphics g, double radius)
{
DrawEllipse(g, radius, radius);
}
假设PdfGraphics
是一个用于发出PDF命令的类,因此g.MoveTo(x, y)
将在内容流中变成“x y m”。我从Don Lancaster's fabulous explanation获取了我的数学和我的幻数(PDF,自然而然)。这假设将在原点绘制圆或椭圆。要将其移动到其他位置,请先进行平移变换,或修改代码以在所需原点中减去添加。此代码的最差情况误差约为1/1250(约0.08%),平均值为1/2500(约0.04%)。
答案 1 :(得分:4)
plinth's answer与我最终找到的一样。有许多棘手的数学可以减少到魔术常数,并将任务细分为四条独立的Bézier曲线。 我需要在原始PDF命令中执行此操作,但过程是相同的。
移动到第一条曲线的开头。这是圆的中心减去你想要的任何方向的半径。
计算出曲线的结尾(此代码中的$x3
和$y3
值)。下标来自大多数人使用的Bézier曲线控制点标签。
找出控制点。这就是$magic
值显示的位置。
完成一个细分后,请执行下一个细分。除了在笛卡尔坐标系中很好地使用加法和减法之外,四个部分没有什么特别之处。
如果您想要填充,最后使用f
绘制刚刚创建的路径的内部。
我可以做一些重构,但是当我使用它时,更容易看到我通过使用单独的代码块来获得正确的标记。这是我添加到PDF::EasyPDF的子程序:
sub make_magic_circle
{
my( $pdf, # PDF::EasyPDF object
$center,
$r # radius
) = @_;
my( $xc, $yc ) = $center->xy;
my $magic = $r * 0.552;
my( $x0p, $y0p ) = ( $xc - $r, $yc );
$pdf->{stream} .= "$x0p $y0p m\n";
{
( $x0p, $y0p ) = ( $xc - $r, $yc );
my( $x1, $y1 ) = ( $x0p, $y0p + $magic );
my( $x2, $y2 ) = ( $x0p + $r - $magic, $y0p + $r );
my( $x3, $y3 ) = ( $x0p + $r, $y0p + $r );
$pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
}
{
( $x0p, $y0p ) = ( $xc, $yc + $r );
my( $x1, $y1 ) = ( $x0p + $magic, $y0p );
my( $x2, $y2 ) = ( $x0p + $r, $y0p - $r + $magic );
my( $x3, $y3 ) = ( $x0p + $r, $y0p - $r );
$pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
}
{
( $x0p, $y0p ) = ( $xc + $r, $yc );
my( $x1, $y1 ) = ( $x0p, $y0p - $magic );
my( $x2, $y2 ) = ( $x0p - $r + $magic, $y0p - $r );
my( $x3, $y3 ) = ( $x0p - $r, $y0p - $r );
$pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
}
{
( $x0p, $y0p ) = ( $xc, $yc - $r );
my( $x1, $y1 ) = ( $x0p - $magic, $y0p );
my( $x2, $y2 ) = ( $x0p - $r, $y0p + $r - $magic );
my( $x3, $y3 ) = ( $x0p - $r, $y0p + $r );
$pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
}
$pdf->{stream} .= "f\n";
}
答案 2 :(得分:1)
这里的其他答案都是完美的,但我只是想为一个坚实的填充圈(因此没有笔画)的特定情况添加一个小的黑客。
只需绘制一条零长度线,其线宽为圆的直径,但设置"线帽"成为rounded
。
对于椭圆,圆弧,线段以及更多相关的形状来说,这显然不是一个很好的解决方案......但这是一个很酷的技巧。