2009-12-25 79 views
5

PostScript上的绘图圆圈很简单,我很惊讶PDF显然没有包含那些相同的原语。有很多商业图书馆会这样做,但不应该比这更简单吗?如何用PDF基元绘制填充和未填充的圆圈?

也有一些使用贝塞尔曲线的技巧,但你没有得到一个完美的圆,你必须绘制连接段。只要看起来接近完美,我不需要一个完美的圆圈。

我正在做这个,作为Perl模块的一个补充,但是这个语言并不是我需要帮助的部分。

回答

5

情况总是如此。 PDF在原语中没有圆圈,只有贝齐尔。 PDF成像模型是从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)将在内容流中变成“×Y M”。我从唐Lancaster's fabulous explanation(PDF,自然)拿走了我的数学和我的神奇数字。这假定圆或椭圆将在原点绘制。要将其移动到其他位置,请首先进行翻译转换或修改代码以减去所需原点的添加。该代码给出了大约1/1250(大约.08%)和平均值1/2500(大约.04%)的最坏情况误差。

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"; 
    } 
1

这两个其他答案在这里是完美的,但我只是想增加一个小的劈了坚实充满(这样的特殊情况下,不中风)

简单地画出一条长度为线条直径的零长度线,但将“线条上限”设置为rounded

这显然不是一个很好的解决椭圆,弧线,线段和更多相关形状的方法......但这是一个很棒的技巧。