Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to draw circle in PDF?

848 views
Skip to first unread message

Anders Kierulf

unread,
Oct 30, 2002, 12:05:47 PM10/30/02
to
I was surprised to learn that PDF, unlike PostScript, doesn't include an
operator for drawing an arc or circle. The PDF 1.4 specification doesn't
even include 'circle' or 'arc' in the index. So a circle has to be expressed
as a cubic Bezier curve? What's the most efficient way to do that and make
it look as close to a real circle as possible?

Efficiency in expressing circles is important to me as my SmartGo program
will be creating PDF files that contain diagrams of a Go board, and thus a
single page can easily contain close to a thousand circles.

Thanks,
Anders Kierulf
www.smartgo.com

Nikolai Grigoriev

unread,
Oct 30, 2002, 3:40:08 PM10/30/02
to
Hi,

> What's the most efficient way to do that and make
> it look as close to a real circle as possible?

A 90-deg arc of radius 1 can be closely approximated
by a Bezier with the following control points:

(0, 1) (0.5523, 1) (1, 0.5523) (1, 0)

The exact magic number is (4/3)*(sqrt(2)-1).

To draw a full circle, you will need four Beziers. My rough estimations
showed that the deviation is at most 0.3% by radius (probably less).
There is no visible difference.

Best regards,
Nikolai Grigoriev
RenderX

Michael Malak

unread,
Oct 31, 2002, 11:19:41 AM10/31/02
to
"Nikolai Grigoriev" <gr...@iitp.ru> wrote in message news:<appg5n$ovf$1...@news.comcor-tv.ru>...

> Hi,
>
> > What's the most efficient way to do that and make
> > it look as close to a real circle as possible?
>
> A 90-deg arc of radius 1 can be closely approximated
> by a Bezier with the following control points:
>
> (0, 1) (0.5523, 1) (1, 0.5523) (1, 0)
>
> The exact magic number is (4/3)*(sqrt(2)-1).
>
> Best regards,
> Nikolai Grigoriev
> RenderX

In case any of you were wondering, he got that "magic number" by
requiring that the curve described by (0, 1), (a, 1), (1, a) and (1,
0) go through the midpoint of the arc, (sqrt(2)/2, sqrt(2)/2) and
solving for a.

Although PostScript has the arc operators, the Red Book states that
the curves they produce are actually represented by Bezier cubics. So
PDF isn't actually missing anything here.

Mike

Nikolai Grigoriev

unread,
Oct 31, 2002, 3:32:14 PM10/31/02
to
Hi,

Michael Malak <mma...@alum.mit.edu> wrote:

> In case any of you were wondering, he got that "magic number" by
> requiring that the curve described by (0, 1), (a, 1), (1, a) and (1,
> 0) go through the midpoint of the arc, (sqrt(2)/2, sqrt(2)/2) and
> solving for a.

You are right. Just to be precise, he (= I) was actually solving
the equation for a more general case, using the approach you describe.
For an arc of radius 1 and angle phi, the distance between
the first and the second control point is:

a = (4/3) * tg (phi/4)

Regards,
Nikolai

Jose Fernando Tepedino

unread,
Oct 31, 2002, 4:39:17 PM10/31/02
to
"Nikolai Grigoriev" <gr...@iitp.ru> wrote in message news:<appg5n$ovf$1...@news.comcor-tv.ru>...


Would it be something like this, with 4 bezier curves
(for a 20-pixel radius, red circle, centered at position (100,700))?

q
0.05 w
20 0 0 20 100 700 cm
1 0 0 RG
0 1 m
.5523 1 1 .5523 1 0 c
1 -.5523 .5523 -1 0 -1 c
-.5523 -1 -1 -.5523 -1 0 c
-1 .5523 -.5523 1 0 1 c
S
Q


Jose.
--------------------------
Jose Fernando Tepedino
e-mail: jo...@wiser.com.br
Mobile S.A.
Recife - Brasil

Ken Starks

unread,
Oct 31, 2002, 4:48:17 PM10/31/02
to
In article <aps44j$2gpf$1...@news.comcor-tv.ru>, Nikolai Grigoriev
<gr...@iitp.ru> writes

An even more general case !
If a cubic Bezier segment ABCD :-
+ goes from A to B, with control points B abd C
+ has an axis of symmetry
+ has a midpoint M, obviously on the axis of symmetry
+ P is the midpoint of AD
+ Q is the midpoint of BC

then :-
+ ABCD is an isosceles trapezium
+ AB and DC are the tangents at A and B
+ M is on PQ and three-quarters of the way up
i.e. MQ = 3 x PM


Nearly all software that uses Bezier curves to approximate
a quarter circle uses this curve, but it is NOT actually the
'BEST FIT' because its `distance from the centre' is NEVER greater
than the true radius.

If you try using it for a semi-circle, it really looks bad; I
would always use at least four segments to a circle. ( You can
do a bad semicircle of diameter 6 in your
head: ABCD is a 4 x 6 rectangle )

Another point is that you don't need to know the angle-measure
and you don't need to use trigonometry. You can use
co-ordinates instead--for example, calculating the origin-centred
arc through (a,b) and (a,-b)
--
Ken Starks

Ken Starks

unread,
Oct 31, 2002, 5:00:40 PM10/31/02
to
In article <pvydnekx862...@giganews.com>, Anders Kierulf
<and...@smartgo.com> writes

>
>Efficiency in expressing circles is important to me as my SmartGo program
>will be creating PDF files that contain diagrams of a Go board, and thus a
>single page can easily contain close to a thousand circles.

Three points:

1. You should certainly use X-Objects (The PDF answer to 'Define once,
use many times )

2. You may wish to draw all possible stones onto your diagram, and
then just switch on and off a `hidden' attribute, or change the
fill and stroke colours. You can do that with JavaScript.

3. There's a Go package for LaTeX, I believe. You would find it on
CTAN. I am not familiar with it, but the LaTeX package for chess
can take a list of moves and draw the board at any stage, so the
Go package might do something similar.

--
Ken Starks

Anders Kierulf

unread,
Nov 4, 2002, 3:28:16 PM11/4/02
to
Bezier curves and X-Objects work well, exactly what I needed. Thanks to
everybody who helped with this, much appreciated.

Anders Kierulf
www.smartgo.com

paul_k

unread,
Feb 14, 2005, 9:45:48 PM2/14/05
to
This sounds very complex and I'm not sure why it has to be, here is a
simple equation that draws perfect circles, arcs (plot the points on
graph paper to figure it out). I'll post the arc equation later.

void PDF::Circle(unsigned int x, unsigned int y, unsigned int radius)
{
unsigned int offset;

if (p_page != NULL)
{
if (p_page->width > p_page->height) offset = (unsigned
int)(((float)radius * p_page->width) / p_page->height);
else offset = (unsigned int)(((float)radius * p_page->height) /
p_page->width);

Curve(x - radius, y, x - radius, y + offset, x + radius, y +
offset, x + radius, y);
Curve(x - radius, y, x - radius, y - offset, x + radius, y -
offset, x + radius, y);
}
}

Hope this helps...
-Paul K.

Ralf Koenig

unread,
Feb 15, 2005, 4:44:50 AM2/15/05
to
There is also the "line with zero length and round line caps hack", that already
existed in Postscript.

Such as:

10 w % set diameter
1 J % round line caps
100 100 m % center coordinates
100 100 l % line of zero length, only the caps remain and make the circle

But I guess, the "4 curved arcs" approach is more robust than these "zero
length" lines, as they might be disregarded by some PDF software.

Ralf

paul_k

unread,
Feb 23, 2005, 10:31:53 PM2/23/05
to
And the arc code is:

ArcPoints PDF::Arc(unsigned int x_, unsigned int y_, unsigned int
radius_, float start_, float end_, bool continuation_)
{
float start = start_;
float end = end_;

struct Coordinate
{
int x;
int y;
};

Coordinate start_point;
Coordinate start_offset;
Coordinate end_point;
Coordinate end_offset;

ArcPoints result;
result.start_x = 0;
result.start_y = 0;
result.end_x = 0;
result.end_y = 0;

float offset = 0;

if (p_page != NULL && end_ > start_)
{
//do 1/4 slice increments
if (end - start > (PI / 2)) end = start + (PI / 2);

//figure out start and end points
start_point.x = (int)(radius_ * cos(start));
start_point.y = (int)(radius_ * sin(start));

end_point.x = (int)(radius_ * cos(end));
end_point.y = (int)(radius_ * sin(end));

//figure out point offset
offset = radius_ * ((end - start) / PI);

//find middle curve points at 90 degree angles
start_offset.x = start_point.x + (int)(offset * cos(start + (PI /
2)));
start_offset.y = start_point.y + (int)(offset * sin(start + (PI /
2)));

end_offset.x = end_point.x + (int)(offset * cos(end - (PI / 2)));
end_offset.y = end_point.y + (int)(offset * sin(end - (PI / 2)));

//mark curve
Curve(x_ + start_point.x, y_ + start_point.y, x_ + start_offset.x,
y_ + start_offset.y, x_ + end_offset.x, y_ + end_offset.y, x_ +
end_point.x, y_ + end_point.y, continuation_);

//remember end point from last arc for continuations
result.end_x = x_ + end_point.x;
result.end_y = y_ + end_point.y;

//if entire arc is not drawn draw next segment
if (end_ - start_ > (PI / 2))
{
result = Arc(x_, y_, radius_, start_ + (PI / 2), end_, true);
}

//remember start point of original arc, recursion above
result.start_x = x_ + start_point.x;
result.start_y = y_ + start_point.y;
}
return result;
}

0 new messages