asymptote: Pens
6.3 Pens
========
In 'Asymptote', pens provide a context for the four basic drawing
commands (Drawing commands). They are used to specify the
following drawing attributes: color, line type, line width, line cap,
line join, fill rule, text alignment, font, font size, pattern,
overwrite mode, and calligraphic transforms on the pen nib. The default
pen used by the drawing routines is called 'currentpen'. This provides
the same functionality as the 'MetaPost' command 'pickup'. The implicit
initializer for pens is 'defaultpen'.
Pens may be added together with the nonassociative binary operator
'+'. This will add the colors of the two pens. All other non-default
attributes of the rightmost pen will override those of the leftmost pen.
Thus, one can obtain a yellow dashed pen by saying 'dashed+red+green' or
'red+green+dashed' or 'red+dashed+green'. The binary operator '*' can
be used to scale the color of a pen by a real number, until it saturates
with one or more color components equal to 1.
* Colors are specified using one of the following colorspaces:
'pen gray(real g);'
This produces a grayscale color, where the intensity 'g' lies
in the interval [0,1], with 0.0 denoting black and 1.0
denoting white.
'pen rgb(real r, real g, real b);'
This produces an RGB color, where each of the red, green, and
blue intensities 'r', 'g', 'b', lies in the interval [0,1].
'pen cmyk(real c, real m, real y, real k);'
This produces a CMYK color, where each of the cyan, magenta,
yellow, and black intensities 'c', 'm', 'y', 'k', lies in the
interval [0,1].
'pen invisible;'
This special pen writes in invisible ink, but adjusts the
bounding box as if something had been drawn (like the
'\phantom' command in TeX). The function 'bool
invisible(pen)' can be used to test whether a pen is
invisible.
The default color is 'black'; this may be changed with the routine
'defaultpen(pen)'. The function 'colorspace(pen p)' returns the
colorspace of pen 'p' as a string ('"gray"', '"rgb"', '"cmyk"', or
'""').
The function 'real[] colors(pen)' returns the color components of a
pen. The functions 'pen gray(pen)', 'pen rgb(pen)', and 'pen
cmyk(pen)' return new pens obtained by converting their arguments
to the respective color spaces. The function
'colorless(pen=currentpen)' returns a copy of its argument with the
color attributes stripped (to avoid color mixing).
A 6-character RGB hexidecimal string can be converted to a pen with
the routine
pen rgb(string s);
A pen can be converted to a hexidecimal string with
* string hex(pen p);
Various shades and mixtures of the grayscale primary colors 'black'
and 'white', RGB primary colors 'red', 'green', and 'blue', and RGB
secondary colors 'cyan', 'magenta', and 'yellow' are defined as
named colors, along with the CMYK primary colors 'Cyan', 'Magenta',
'Yellow', and 'Black', in the module 'plain':
[colors]
The standard 140 RGB 'X11' colors can be imported with the command
import x11colors;
and the standard 68 CMYK TeX colors can be imported with the
command
import texcolors;
Note that there is some overlap between these two standards and the
definitions of some colors (e.g. 'Green') actually disagree.
'Asymptote' also comes with a 'asycolors.sty' 'LaTeX' package that
defines to 'LaTeX' CMYK versions of 'Asymptote''s predefined
colors, so that they can be used directly within 'LaTeX' strings.
Normally, such colors are passed to 'LaTeX' via a pen argument;
however, to change the color of only a portion of a string, say for
a slide presentation, (slide) it may be desirable to
specify the color directly to 'LaTeX'. This file can be passed to
'LaTeX' with the 'Asymptote' command
usepackage("asycolors");
The structure 'hsv' defined in 'plain_pens.asy' may be used to
convert between HSV and RGB spaces, where the hue 'h' is an angle
in [0,360) and the saturation 's' and value 'v' lie in '[0,1]':
pen p=hsv(180,0.5,0.75);
write(p); // ([default], red=0.375, green=0.75, blue=0.75)
hsv q=p;
write(q.h,q.s,q.v); // 180 0.5 0.75
* Line types are specified with the function 'pen linetype(real[] a,
real offset=0, bool scale=true, bool adjust=true)', where 'a' is an
array of real array numbers. The optional parameter 'offset'
specifies where in the pattern to begin. The first number
specifies how far (if 'scale' is 'true', in units of the pen line
width; otherwise in 'PostScript' units) to draw with the pen on,
the second number specifies how far to draw with the pen off, and
so on. If 'adjust' is 'true', these spacings are automatically
adjusted by 'Asymptote' to fit the arclength of the path. Here are
the predefined line types:
pen solid=linetype(new real[]);
pen dotted=linetype(new real[] {0,4});
pen dashed=linetype(new real[] {8,8});
pen longdashed=linetype(new real[] {24,8});
pen dashdotted=linetype(new real[] {8,8,0,8});
pen longdashdotted=linetype(new real[] {24,8,0,8});
pen Dotted(pen p=currentpen) {return linetype(new real[] {0,3})+2*linewidth(p);}
pen Dotted=Dotted();
[linetype]
The default line type is 'solid'; this may be changed with
'defaultpen(pen)'. The line type of a pen can be determined with
the functions 'real[] linetype(pen p=currentpen)', 'real offset(pen
p)', 'bool scale(pen p)', and 'bool adjust(pen p)'.
* The pen line width is specified in 'PostScript' units with 'pen
linewidth(real)'. The default line width is 0.5 bp; this value may
be changed with 'defaultpen(pen)'. The line width of a pen is
returned by 'real linewidth(pen p=currentpen)'. For convenience,
in the module 'plain_pens' we define
void defaultpen(real w) {defaultpen(linewidth(w));}
pen operator +(pen p, real w) {return p+linewidth(w);}
pen operator +(real w, pen p) {return linewidth(w)+p;}
so that one may set the line width like this:
defaultpen(2);
pen p=red+0.5;
* A pen with a specific 'PostScript' line cap is returned on calling
'linecap' with an integer argument:
pen squarecap=linecap(0);
pen roundcap=linecap(1);
pen extendcap=linecap(2);
The default line cap, 'roundcap', may be changed with
'defaultpen(pen)'. The line cap of a pen is returned by 'int
linecap(pen p=currentpen)'.
* A pen with a specific 'PostScript' join style is returned on
calling 'linejoin' with an integer argument:
pen miterjoin=linejoin(0);
pen roundjoin=linejoin(1);
pen beveljoin=linejoin(2);
The default join style, 'roundjoin', may be changed with
'defaultpen(pen)'.The join style of a pen is returned by 'int
linejoin(pen p=currentpen)'.
* A pen with a specific 'PostScript' miter limit is returned by
calling 'miterlimit(real)'. The default miterlimit, '10.0', may be
changed with 'defaultpen(pen)'. The miter limit of a pen is
returned by 'real miterlimit(pen p=currentpen)'.
* A pen with a specific 'PostScript' fill rule is returned on calling
'fillrule' with an integer argument:
pen zerowinding=fillrule(0);
pen evenodd=fillrule(1);
The fill rule, which identifies the algorithm used to determine the
insideness of a path or array of paths, only affects the 'clip',
'fill', and 'inside' functions. For the 'zerowinding' fill rule, a
point 'z' is outside the region bounded by a path if the number of
upward intersections of the path with the horizontal line
'z--z+infinity' minus the number of downward intersections is zero.
For the 'evenodd' fill rule, 'z' is considered to be outside the
region if the total number of such intersections is even. The
default fill rule, 'zerowinding', may be changed with
'defaultpen(pen)'. The fill rule of a pen is returned by 'int
fillrule(pen p=currentpen)'.
* A pen with a specific text alignment setting is returned on calling
'basealign' with an integer argument:
pen nobasealign=basealign(0);
pen basealign=basealign(1);
The default setting, 'nobasealign',which may be changed with
'defaultpen(pen)', causes the label alignment routines to use the
full label bounding box for alignment. In contrast, 'basealign'
requests that the TeX baseline be respected. The base align
setting of a pen is returned by 'int basealigin(pen p=currentpen)'.
* The font size is specified in TeX points (1 pt = 1/72.27 inches)
with the function 'pen fontsize(real size, real
lineskip=1.2*size)'. The default font size, 12pt, may be changed
with 'defaultpen(pen)'. Nonstandard font sizes may require
inserting
import fontsize;
at the beginning of the file (this requires the 'type1cm' package
available from
<http://mirror.ctan.org/macros/latex/contrib/type1cm/>
and included in recent 'LaTeX' distributions). The font size and
line skip of a pen can be examined with the routines 'real
fontsize(pen p=currentpen)' and 'real lineskip(pen p=currentpen)',
respectively.
* A pen using a specific 'LaTeX' 'NFSS' font is returned by calling
the function 'pen font(string encoding, string family, string
series, string shape)'. The default setting,
'font("OT1","cmr","m","n")', corresponds to 12pt Computer Modern
Roman; this may be changed with 'defaultpen(pen)'. The font
setting of a pen is returned by 'string font(pen p=currentpen)'.
Support for standardized international characters is provided by
the 'unicode' package (unicode).
Alternatively, one may select a fixed-size TeX font (on which
'fontsize' has no effect) like '"cmr12"' (12pt Computer Modern
Roman) or '"pcrr"' (Courier) using the function 'pen font(string
name)'. An optional size argument can also be given to scale the
font to the requested size: 'pen font(string name, real size)'.
A nonstandard font command can be generated with 'pen
fontcommand(string)'.
A convenient interface to the following standard 'PostScript' fonts
is also provided:
pen AvantGarde(string series="m", string shape="n");
pen Bookman(string series="m", string shape="n");
pen Courier(string series="m", string shape="n");
pen Helvetica(string series="m", string shape="n");
pen NewCenturySchoolBook(string series="m", string shape="n");
pen Palatino(string series="m", string shape="n");
pen TimesRoman(string series="m", string shape="n");
pen ZapfChancery(string series="m", string shape="n");
pen Symbol(string series="m", string shape="n");
pen ZapfDingbats(string series="m", string shape="n");
* The transparency of a pen can be changed with the command:
pen opacity(real opacity=1, string blend="Compatible");
The opacity can be varied from '0' (fully transparent) to the
default value of '1' (opaque), and 'blend' specifies one of the
following foreground-background blending operations:
"Compatible","Normal","Multiply","Screen","Overlay","SoftLight",
"HardLight","ColorDodge","ColorBurn","Darken","Lighten","Difference",
"Exclusion","Hue","Saturation","Color","Luminosity",
as described in
<http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf>.
Since 'PostScript' does not support transparency, this feature is
only effective with the '-f pdf' output format option; other
formats can be produced from the resulting PDF file with the
'ImageMagick' 'convert' program. Labels are always drawn with an
'opacity' of 1. A simple example of transparent filling is
provided in the example file 'transparency.asy'.
* 'PostScript' commands within a 'picture' may be used to create a
tiling pattern, identified by the string 'name', for 'fill' and
'draw' operations by adding it to the global 'PostScript' frame
'currentpatterns', with optional left-bottom margin 'lb' and
right-top margin 'rt'.
import patterns;
void add(string name, picture pic, pair lb=0, pair rt=0);
To 'fill' or 'draw' using pattern 'name', use the pen
'pattern("name")'. For example, rectangular tilings can be
constructed using the routines 'picture tile(real Hx=5mm, real
Hy=0, pen p=currentpen, filltype filltype=NoFill)', 'picture
checker(real Hx=5mm, real Hy=0, pen p=currentpen)', and 'picture
brick(real Hx=5mm, real Hy=0, pen p=currentpen)' defined in
'patterns.asy':
size(0,90);
import patterns;
add("tile",tile());
add("filledtilewithmargin",tile(6mm,4mm,red,Fill),(1mm,1mm),(1mm,1mm));
add("checker",checker());
add("brick",brick());
real s=2.5;
filldraw(unitcircle,pattern("tile"));
filldraw(shift(s,0)*unitcircle,pattern("filledtilewithmargin"));
filldraw(shift(2s,0)*unitcircle,pattern("checker"));
filldraw(shift(3s,0)*unitcircle,pattern("brick"));
[tile]
Hatch patterns can be generated with the routines 'picture
hatch(real H=5mm, pair dir=NE, pen p=currentpen)', 'picture
crosshatch(real H=5mm, pen p=currentpen)':
size(0,100);
import patterns;
add("hatch",hatch());
add("hatchback",hatch(NW));
add("crosshatch",crosshatch(3mm));
real s=1.25;
filldraw(unitsquare,pattern("hatch"));
filldraw(shift(s,0)*unitsquare,pattern("hatchback"));
filldraw(shift(2s,0)*unitsquare,pattern("crosshatch"));
[hatch]
You may need to turn off aliasing in your 'PostScript' viewer for
patterns to appear correctly. Custom patterns can easily be
constructed, following the examples in 'patterns.asy'. The tiled
pattern can even incorporate shading (gradient shading), as
illustrated in this example (not included in the manual because not
all printers support 'PostScript' 3):
size(0,100);
import patterns;
real d=4mm;
picture tiling;
path square=scale(d)*unitsquare;
axialshade(tiling,square,white,(0,0),black,(d,d));
fill(tiling,shift(d,d)*square,blue);
add("shadedtiling",tiling);
filldraw(unitcircle,pattern("shadedtiling"));
* One can specify a custom pen nib as an arbitrary polygonal path
with 'pen makepen(path)'; this path represents the mark to be drawn
for paths containing a single point. This pen nib path can be
recovered from a pen with 'path nib(pen)'. Unlike in 'MetaPost',
the path need not be convex:
size(200);
pen convex=makepen(scale(10)*polygon(8))+grey;
draw((1,0.4),convex);
draw((0,0)---(1,1)..(2,0)--cycle,convex);
pen nonconvex=scale(10)*
makepen((0,0)--(0.25,-1)--(0.5,0.25)--(1,0)--(0.5,1.25)--cycle)+red;
draw((0.5,-1.5),nonconvex);
draw((0,-1.5)..(1,-0.5)..(2,-1.5),nonconvex);
[makepen]
The value 'nullpath' represents a circular pen nib (the default);
an elliptical pen can be achieved simply by multiplying the pen by
a transform: 'yscale(2)*currentpen'.
* One can prevent labels from overwriting one another by using the
pen attribute 'overwrite', which takes a single argument:
'Allow'
Allow labels to overwrite one another. This is the default
behaviour (unless overridden with 'defaultpen(pen)'.
'Suppress'
Suppress, with a warning, each label that would overwrite
another label.
'SuppressQuiet'
Suppress, without warning, each label that would overwrite
another label.
'Move'
Move a label that would overwrite another out of the way and
issue a warning. As this adjustment is during the final
output phase (in 'PostScript' coordinates) it could result in
a larger figure than requested.
'MoveQuiet'
Move a label that would overwrite another out of the way,
without warning. As this adjustment is during the final
output phase (in 'PostScript' coordinates) it could result in
a larger figure than requested.
The routine 'defaultpen()' returns the current default pen
attributes. Calling the routine 'resetdefaultpen()' resets all pen
default attributes to their initial values.