1. Path Construction
Paths are the fundamental building blocks of all vector graphics in PostScript. Understanding how to construct and manipulate paths is essential for creating any visual output.
1.1. Overview
A path is a mathematical description of shapes made up of straight lines and curves. Paths exist in user space and can be:
-
Stroked - Drawn as an outline
-
Filled - Filled with color
-
Clipped - Used to restrict drawing area
-
Converted - Used as character outlines
Paths are built step-by-step by adding elements, then rendered with painting operators.
1.2. Path Concepts
1.2.1. The Current Path
PostScript maintains a single current path that you build incrementally:
newpath % Clear current path
100 100 moveto % Start new subpath
200 100 lineto % Add line segment
200 200 lineto % Add another segment
% Path exists but is not visible yet
stroke % Now render the path
1.2.2. The Current Point
The current point is the end of the last path segment:
newpath
100 100 moveto % Current point: (100, 100)
200 100 lineto % Current point: (200, 100)
200 200 lineto % Current point: (200, 200)
currentpoint % Returns: 200 200
1.3. Basic Path Construction
1.3.1. Starting a New Path: newpath
Clears the current path and resets the current point:
newpath % Start with clean slate
% currentpoint would cause error - no current point yet
1.3.2. Moving: moveto
Begins a new subpath at specified coordinates:
newpath
100 100 moveto % Absolute coordinates
% Current point is now (100, 100)
1.3.3. Relative Moving: rmoveto
Moves relative to current point:
newpath
100 100 moveto % Start at (100, 100)
50 0 rmoveto % Move to (150, 100)
0 50 rmoveto % Move to (150, 150)
1.3.4. Drawing Lines: lineto
Adds a straight line from current point to specified coordinates:
newpath
100 100 moveto
200 100 lineto % Horizontal line
200 200 lineto % Vertical line
100 200 lineto % Back horizontally
stroke
1.4. Curved Path Segments
1.4.1. Bézier Curves: curveto
Creates a cubic Bézier curve using two control points:
% curveto: x1 y1 x2 y2 x3 y3 curveto
% (x1,y1) = first control point
% (x2,y2) = second control point
% (x3,y3) = end point
newpath
100 100 moveto
150 50 200 50 250 100 curveto % S-curve
stroke
Smooth curve example:
newpath
50 100 moveto
100 50 150 150 200 100 curveto % Wave
200 100 moveto
250 50 300 150 350 100 curveto % Another wave
stroke
1.4.2. Relative Bézier Curves: rcurveto
Bézier curve with relative coordinates:
newpath
100 100 moveto
50 -50 100 -50 150 0 rcurveto % All relative to (100, 100)
stroke
1.4.3. Arc Segments: arc
Creates a circular arc (counter-clockwise):
% arc: x y radius startAngle endAngle arc
newpath
200 200 50 0 360 arc % Complete circle
stroke
newpath
200 200 50 0 90 arc % Quarter circle
stroke
Important: arc adds a line from current point to arc start if current point exists:
newpath
100 100 moveto
200 200 50 0 180 arc % Line from (100,100) to arc start
stroke
1.4.4. Clockwise Arc: arcn
Creates arc in clockwise direction:
% arcn: x y radius startAngle endAngle arcn
newpath
200 200 50 0 90 arcn % Clockwise from 0° to 90°
stroke
1.5. Common Path Shapes
1.5.1. Rectangle
Building a rectangle manually:
% Rectangle: x y width height
/rectangle {
4 dict begin
/h exch def
/w exch def
/y exch def
/x exch def
newpath
x y moveto
w 0 rlineto
0 h rlineto
w neg 0 rlineto
closepath
end
} def
100 100 200 150 rectangle
stroke
1.5.3. Rounded Rectangle
% Rounded rectangle: x y width height radius
/roundrect {
5 dict begin
/r exch def
/h exch def
/w exch def
/y exch def
/x exch def
newpath
x r add y moveto
x w add y x w add y h add r arcto 4 {pop} repeat
x w add y h add x y h add r arcto 4 {pop} repeat
x y h add x y r arcto 4 {pop} repeat
x y x w add y r arcto 4 {pop} repeat
closepath
end
} def
100 100 200 150 20 roundrect
stroke
1.5.4. Regular Polygon
% Regular polygon: x y radius sides startAngle
/polygon {
5 dict begin
/angle exch def
/n exch def
/r exch def
/y exch def
/x exch def
newpath
/angleStep 360 n div def
x y moveto
x r angle cos mul add y r angle sin mul add moveto
1 1 n {
pop
/angle angle angleStep add def
x r angle cos mul add y r angle sin mul add lineto
} for
closepath
end
} def
% Pentagon
200 200 50 5 90 polygon
stroke
% Hexagon
300 300 50 6 0 polygon
stroke
1.5.5. Star
% 5-pointed star: x y outerRadius innerRadius
/star5 {
4 dict begin
/ir exch def
/or exch def
/y exch def
/x exch def
newpath
/angle 90 def
x or angle cos mul add y or angle sin mul add moveto
0 1 4 {
pop
/angle angle 72 sub def
x ir angle 36 sub cos mul add y ir angle 36 sub sin mul add lineto
x or angle cos mul add y or angle sin mul add lineto
} for
closepath
end
} def
200 200 80 30 star5
stroke
1.6. Path Manipulation
1.6.1. Reversing Path Direction: reversepath
Reverses the direction of the current path:
newpath
100 100 moveto
200 100 lineto
200 200 lineto
reversepath % Now goes: (200,200)->(200,100)->(100,100)
1.6.2. Converting Stroke to Path: strokepath
Converts stroked outline to a fillable path:
newpath
100 100 moveto
200 200 lineto
10 setlinewidth
strokepath % Path is now the outline of the stroke
fill % Fill the stroke outline
1.6.3. Flattening Curves: flattenpath
Converts curves to straight line segments:
newpath
100 100 moveto
200 100 250 200 150 200 curveto
flattenpath % Curve becomes multiple line segments
1.6.4. Getting Path Information: pathforall
Iterate through path segments:
% pathforall: moveto lineto curveto closepath pathforall
% Executes procedures for each path element
{ % moveto procedure
(Move to: ) print exch =string cvs print ( ) print =string cvs print ()
}
{ % lineto procedure
(Line to: ) print exch =string cvs print ( ) print =string cvs print ()
}
{ % curveto procedure
(Curve) print
}
{ % closepath procedure
(Close) print
}
pathforall
1.7. Advanced Path Techniques
1.7.1. Compound Paths with Holes
Create shapes with holes using subpaths:
% Outer rectangle
newpath
100 100 moveto
300 100 lineto
300 300 lineto
100 300 lineto
closepath
% Inner rectangle (hole) - reverse direction
200 200 moveto
200 150 lineto
150 150 lineto
150 200 lineto
closepath
% Fill with even-odd rule
eofill
1.7.2. Path Clipping
Use paths to define clipping regions:
gsave
% Create clipping path
newpath
200 200 100 0 360 arc
clip
newpath % Start new path for drawing
% Only visible inside circle
100 100 200 200 rectfill
grestore
1.8. Path Construction Patterns
1.8.1. Pattern 1: Path Builder
Construct complex paths programmatically:
/pathBuilder {
10 dict begin
/segments exch def
newpath
segments {
aload pop
/type exch def
type (M) eq { moveto } if
type (L) eq { lineto } if
type (C) eq { curveto } if
type (Z) eq { closepath } if
} forall
end
} def
% Usage
[
[(M) 100 100]
[(L) 200 100]
[(L) 200 200]
[(L) 100 200]
[(Z)]
] pathBuilder
stroke
1.8.2. Pattern 2: Parametric Curves
Generate curves from mathematical functions:
% Draw sine wave
/sineWave {
newpath
0 1 360 {
/x exch def
x 200 div dup
100 mul sin 50 mul
200 add exch 100 add exch
x 0 eq { moveto } { lineto } ifelse
} for
} def
sineWave
stroke
1.8.3. Pattern 3: Morphing Shapes
Interpolate between two paths:
% Morph from circle to square
/morphShape { % t (0-1)
1 dict begin
/t exch def
newpath
0 15 345 {
/angle exch def
% Circle point
/cx 100 angle cos mul def
/cy 100 angle sin mul def
% Square point (approximate)
/sx 100 angle cos mul abs angle cos mul def
/sy 100 angle sin mul abs angle sin mul def
% Interpolate
cx 1 t sub mul sx t mul add 200 add
cy 1 t sub mul sy t mul add 200 add
angle 0 eq { moveto } { lineto } ifelse
} for
closepath
end
} def
% Draw morphing sequence
0 0.2 1 {
gsave
0 morphShape
stroke
grestore
} for
1.8.4. Pattern 4: Constrained Paths
Build paths with constraints:
% Path that stays within bounds
/constrainedLine { % x1 y1 x2 y2 minX minY maxX maxY
8 dict begin
/maxY exch def /maxX exch def
/minY exch def /minX exch def
/y2 exch def /x2 exch def
/y1 exch def /x1 exch def
% Clamp endpoints
/x1 x1 minX maxX clamp def
/y1 y1 minY maxY clamp def
/x2 x2 minX maxX clamp def
/y2 y2 minY maxY clamp def
newpath
x1 y1 moveto
x2 y2 lineto
end
} def
/clamp { % value min max -> clampedValue
2 copy gt { exch } if pop
2 copy lt { exch } if pop
} def
1.9. Practical Examples
1.9.1. Example 1: Grid Pattern
% Draw a grid
/drawGrid { % spacing width height
3 dict begin
/h exch def
/w exch def
/s exch def
0.5 setgray
0.5 setlinewidth
% Vertical lines
0 s w {
dup 0 moveto h lineto stroke
} for
% Horizontal lines
0 s h {
dup 0 exch moveto w exch lineto stroke
} for
end
} def
20 400 400 drawGrid
1.9.2. Example 2: Flow Chart Connector
% Connector with rounded corners
/connector { % x1 y1 x2 y2 radius
5 dict begin
/r exch def
/y2 exch def /x2 exch def
/y1 exch def /x1 exch def
newpath
x1 y1 moveto
% Horizontal then vertical with rounded corner
x2 r sub y1 lineto
x2 y1 x2 y2 r sub r arct 4 {pop} repeat
x2 y2 lineto
end
} def
100 100 300 200 15 connector
stroke
1.9.3. Example 3: Pie Chart Slice
% Pie slice: centerX centerY radius startAngle endAngle
/pieSlice {
5 dict begin
/endAngle exch def
/startAngle exch def
/r exch def
/cy exch def
/cx exch def
newpath
cx cy moveto
cx cy r startAngle endAngle arc
closepath
end
} def
% Draw pie chart
200 200 100 0 60 pieSlice fill
200 200 100 60 180 pieSlice fill
200 200 100 180 360 pieSlice fill
1.9.4. Example 4: Arrow Path
% Arrow from (x1,y1) to (x2,y2) with given head size
/arrow { % x1 y1 x2 y2 headSize
5 dict begin
/hs exch def
/y2 exch def /x2 exch def
/y1 exch def /x1 exch def
% Calculate angle
/dx x2 x1 sub def
/dy y2 y1 sub def
/angle dy dx atan def
% Draw shaft
newpath
x1 y1 moveto
x2 y2 lineto
stroke
% Draw head
newpath
x2 y2 moveto
x2 hs angle 180 add 30 add cos mul add
y2 hs angle 180 add 30 add sin mul add lineto
x2 y2 lineto
x2 hs angle 180 add 30 sub cos mul add
y2 hs angle 180 add 30 sub sin mul add lineto
closepath
fill
end
} def
100 100 300 200 15 arrow
1.10. Best Practices
1.10.1. Always Start with newpath
% Good
newpath
100 100 moveto
200 200 lineto
stroke
% Risky - might add to existing path
100 100 moveto
200 200 lineto
stroke
1.10.2. Close Paths When Appropriate
% Good: explicitly closed
newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
closepath
fill
% OK for strokes but not ideal for fills
newpath
100 100 moveto
200 100 lineto
200 200 lineto
100 200 lineto
fill % PostScript closes automatically for fill
1.10.3. Use Relative Coordinates for Reusability
% Good: reusable shape
/square { % size
dup 0 rlineto
dup 0 exch rlineto
neg dup rlineto
0 exch rlineto
closepath
} def
% Use anywhere
newpath 100 100 moveto 50 square
% Bad: hardcoded positions
/square {
100 100 moveto
150 100 lineto
% ... not reusable
} def
1.10.4. Separate Path Construction from Rendering
% Good: path construction separate from rendering
/buildCircle {
newpath
0 360 arc
} def
/renderFilled {
gsave
0.8 0.2 0.2 setrgbcolor
fill
grestore
} def
/renderStroked {
gsave
0 0 0 setrgbcolor
2 setlinewidth
stroke
grestore
} def
% Use flexibly
100 100 50 buildCircle renderFilled
200 200 50 buildCircle renderStroked
1.11. Common Pitfalls
1.11.1. Forgetting Current Point
% Wrong: no current point
newpath
lineto % ERROR: nocurrentpoint
% Correct
newpath
100 100 moveto
200 200 lineto
1.11.2. Not Clearing Path with newpath
% Wrong: paths accumulate
100 100 moveto
200 100 lineto
stroke
300 300 moveto % Still part of previous path!
400 400 lineto
stroke % Draws both paths
% Correct
newpath
100 100 moveto
200 100 lineto
stroke
newpath % Clear previous path
300 300 moveto
400 400 lineto
stroke
1.12. See Also
-
Painting - Rendering paths
-
Coordinate Systems - User space coordinates
-
Path Construction Commands - Complete command reference
-
moveto - Move to point
-
lineto - Draw line
-
arc - Draw arc
-
curveto - Draw curve