代码之家  ›  专栏  ›  技术社区  ›  brian d foy

如何用PDF原语绘制填充和未填充的圆?

  •  5
  • brian d foy  · 技术社区  · 16 年前

    在PostScript上画圆很容易,而且我很惊讶PDF显然没有包含这些原语。有很多商业图书馆可以做到这一点,但它不应该比这简单吗?

    使用B_)zier曲线也有一些技巧,但是你不能得到一个完美的圆,你必须把它们画成连接段。我不需要一个完美的圆圈,只要它看起来接近完美。

    我这样做是为了增加 PDF-EasyPDF Perl模块,但是语言不是我需要帮助的部分。

    3 回复  |  直到 8 年前
        1
  •  5
  •   brian d foy    16 年前

    情况总是这样。PDF在原语中没有圆,只有贝塞尔曲线。PDF成像模型是在PostScript成像模型的基础上构建的,PostScript成像模型本身只提供使用弧/arcto原语的圆,而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”。我从唐那里学了数学和魔法数字 Lancaster's fabulous explanation (自然是pdf)。这假设圆或椭圆将在原点绘制。若要将其移动到其他位置,请先执行转换,或修改代码以减去所需原点的加法。此代码给出了最坏情况下的错误约为1/1250(约0.08%),平均为1/2500(约0.04%)。

        2
  •  4
  •   Community Mohan Dere    9 年前

    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";
        }
    
        3
  •  1
  •   Marius    8 年前

    这里的其他两个答案都是完美的,但是我想为一个特定的固体案例添加一个小的黑客 填满 圆圈 (因此没有中风) .

    只需画一条长度为零的线,线的厚度为圆的直径,但将“线帽”设置为 rounded .

    显然,对于椭圆、弧、段和许多相关形状来说,这不是一个很好的解决方案…但这是个很酷的把戏。