The metapict library provides functions and data structures useful
for generating picts. The library includes support for points, vectors, Bezier curves,
and, general curves.
The algorithm used to calculate a nice curve from points and tangents is the
same as the one used in MetaPost.
With this library I to hope narrow the gap between Scribble and LaTeX + MetaPost/Tikz.
If you find any features in MetaPost or Tikz that you miss, don’t hesitate to mail me.
2Guide
2.1Coordinates
2.1.1Points
In order to make a computer draw a shape, a way to specify the key points of
the shape is needed.
Note: This is different from racket/pict
which reverses the direction of the y-axis.
MetaPict uses standard (x,y)-coordinates for this purpose.
The location of a point is always relative to the reference point (0,0).
The x-coordinate of a point is the number of units to the right of the reference point.
The y-coordinate of a point is the number of units upward from the reference point.
Consider these points:
The coordinates of these points are:
p1=(0,100)
p2=(100,100)
p3=(200,100)
p4=(0,0)
p5=(100,0)
p6=(200,0)
Notice that the point p4=(0,0) is the reference point.
The point p3=(200,100) is located 200 units to the right of p4
and 100 units upwards.
In order to write a MetaPict program to draw a shape, a good strategy is
to draw the shape on paper. Determine the coordinates for the key points, and
then write the MetaPict program that draws lines or curves between the points.
Let us write such a program, that connects point p1 and p6.
If you zoom, you will see that the lines have a thickness and
that the ends are rounded. Imagine that you have a pen with
a circular nib. The drawings produced by MetaPict will try
to mimick the result you get by drawing with such a pen.
In the chapter on pens you will learn to the control the
thickness of the pen and the shape of the ends of lines.
2.1.2Displacements
In the example above the point p2=(100,100) was described as being 100 to
the right and 100 upwards relative to the reference point (0,0).
An alternative way of describing the location of p2 would be to say
that is located 100 to the right of p1 (and 0 upwards).
Such a displacement can be described with a vector. Since Racket uses the name
"vector", we will represent displacement vectors with a vec structure.
To displace a point p with a vector v, use pt+.
The displacements left, right, up, and, down.
are predefined. As are the vector operations vec+,vec-, and, vec*.
The displacement that moves a point a to point b is given by (pt-ba).
It is common to need points that lie between two point A and B.
The mediation operation is called med. The call (med0.25AB)
will compute the point M on the line from A to B whose
distance from A is 25% of the length of AB.
Note: (medxAB) is equivalent to (pt+A(vec*x(pt-BA))).
Let’s use the knowledge from this section to write a small program to generate
the character A. The shape depends on the parameters w (width),
h (height) and the placement of the bar α.
The form (pt-BA) returns the vector AB. That is, if A=(a1,a2) and
B=(b1,b2), then (b1-a1,b2-a2) is returned.
The form (pt-Av) returns the displacement of the point A with
the opposite of vector v. If A=(a1,a2) and v=(v1,v2) then
the vector (a1-v1,a2-v2) is returned.
The form (pt-A) returns the reflection of the point A with respect to origo.
If A=(a1,a2), then the vector (-a1,-a2) is returned.
Returns the point with polar coordinations (r,θ).
That is, the point is on the circle with center (0,0) and radius r.
The angle from the x-axis to the line through origo and the point is θ.
The angle θ is given in radians (0 rad = 0 degrees, π rad = 180 degrees).
Converts the vector (x,y) into a point with the same coordinates.
If a point A has the same coordinates as a vector v, then
the vector is said to a position vector for the point and OA=v.
Returns the dot product of the vectors v and w.
The dot product is the number v1 w1 + v2 w2.
The dot product of two vectors are the same as the product of the lengths
of the two vectors times the cosine of the angle between the vectors.
Thus the dot product of two orthogonal vectors are zero, and the
dot product of two vectors sharing directions are the product of their lengths.
The function make-color* is a fault tolerant version of make-color
that also accepts color names.
Given a color name as a string, make-color* returns a color% object.
Given real numbers to use as the color components, make-color* works
like make-color, but accepts both non-integer numbers, and
numbers outside the range 0–255. For a real number x the value
used is (min255(max0(exact-floorx))).
The optional argument α is the transparency. The default value is 1.
Given a transparency outside the interval 0–1 whichever value of 0 and 1 is
closest to α is used.
As a match pattern (colorrgba) matches both color% objects
and color names (represented as strings). The variables r,
g, and, b will be bound to the red, green, and, blue components
of the color. The variable a will be bound to the transparency.
Returns a color% object, whose color components are
the components of c1 and c2 added componentwise.
The transparency is (min1.0(+α1α2)) where
α1 and α2 the transparencies of the two colors.
Interpolates linearly between the colors in the list cs.
For "t=0" corresponds to the first color in the list,
and "t=1" corresponds to the last color.
All images in MetaPict are represented as picts. A pict
is a structure that holds information on how to draw a picture.
A pict can be rendered to produce an image in various
formats such as png, pdf, and, svg.
The standard library pict defines several functions to
construct and manipulate picts. MetaPict provides and
offers some extra operations. Since they are not MetaPict specific,
they are also useful outside of the world of MetaPict.
A few of the pict operations are provided under new names.
The basic concept in MetaPict is the curve. Therefore it makes sense
for, say, circle to return a curve. In the pict library
the name circle returns a pict, so to avoid a name conflict
it is exported as circle-pict.
An attempt have been made to make the pict the last argument
of all operations. This explains the existance of a few functions whose
functionality overlap with the pict library.
The operations in this section operate on picts, so use
draw to convert curves into picts.
Adjusts the current pen style, and then draws the pict p.
The available styles are:
'transparent'solid'hilite'dot'long-dash'short-dash'dot-dash.
Note: The pen% documentation mentions a few xor- styles, these
are no longer supported by Racket.
Adjusts the current pen cap, and then draws the pict p.
The available caps are: 'round, 'projecting, and, 'butt.
The cap determines how the end of curves are drawn.
The default pen is 'round.
Adjusts the current pen join, and then draws the pict p.
The available joins are: 'round, 'bevel, and, 'miter.
The join determines how the transition from one curve section to the next is
drawn. The default join is 'round.
Note: If you want to draw a rectangle with a crisp 90 degree
outer angle, then use the 'miter join.
Adjust the brush to use the style s, then draw the pict p.
The example below shows the available styles. The brush style
hilite is black with a 30% alpha.
A Bezier curve from point A to point B with
control points A+ and B- is represented as
an instance of a bez structure: (bezAA+B-B).
Graphically such a curve begins at point A and ends in point B.
The curve leaves point A directed towards
the control point A+. The direction in which the curve enters the
end point B is from B-.
The points A and B are referred to as start and end point
of the Bezier curve. The points A+ and B- are refererred
to as control points. The point A+ is the post control of A
and the point B- is the pre control of B.
Most users will not have reason to work with bez structures directly.
The curve constructor is intended to cover all use cases.
Each point on the Bezier curve corresponds to a real number t between 0 and 1.
The correspondence is called a parameterization of the curve. The number t
is called a parameter. Thus for each value of the parameter t between 0 and 1,
you get a point on the curve. The parameter value t=0 corresponds to the start
point A and the parameter value t=1 corresponds to the end point.
Let’s see an example of a Bezier curve and its construction.
Returns #t if the defining points of the two Bezier curves
are within pairwise distance ε of each other.
The default value of ε=0.0001 was chosen
to mimick the precision of MetaPost.
Given a Bezier curve b from p0 to p3 with control
points p1 and p2, split the Bezier curve at time t
in two parts b1 (from p0 to b(t)) and b2
(from b(t) to p3),
such that (point-of-bezb11) = (point-of-bezb20)
and the graphs of b1 and b2 gives the graph of b.
If the graphs of the Bezier curves intersect numbers t1 and t2
such that b1(t1)=b2(t2) are returned. If there are more than one
intersection, the parameter values for the first intersection is returned.
If no such numbers exist the result is (values#f#f).
If the graphs of the Bezier curves intersect, returns a list of
the intersection point and two numbers t1 and t2
such that b1(t1)=b2(t2).
If there are more than one intersection, the parameter values for the first
intersection is returned. If no such numbers exist the result is (values#f#f).
Convert the "consecutive" Bezier curves bs into a dc-path%.
If the optional transformation t is present, it is applied
to the bs before the conversion.
Returns a bez structure representing a Bezier curve from p0 to p3
that leaves p0 in the direction of w0
and arrives in p3 from the the direction of w0
with tensions t0 and t3 respectively.
Returns a bez structure representing a Bezier curve from p0 to p3
that leaves p0 with an angle of θ
and arrives in p3 with an angle of φ
with tensions t0 and t3 respectively.
A curve represents the path of a curve. Use draw
and fill to create a picture in the form of a pict.
Given a single curve draw will use the current pen to
stroke the path and fill will use the current brush to fill it.
The size of the pict created by draw and fill functions is determined
by the parameters curve-pict-width and curve-pict-height.
The position of points, curves etc. are given in logical coordinates.
A pict will only draw the section of the coordinate plane that
is given by the parameter curve-pict-window. This parameter
holds the logical window (an x- and y-range) that
will be drawn.
Creates a pict representing an image of the drawable arguments.
Given no arguments a blank pict will be returned.
Given multiple arguments draw will convert each argument
into a pict, then layer the results using cc-superimpose.
In other words: it draw the arguments in order, starting with the first.
This table shows how the drawable objects are converted:
Creates a pict that uses the current brush to fill either a single curve
or to fill areas between curves.
A curve divides the points of the plane in two: the inside and the outside.
The inside is drawn with the brush and the outside is left untouched.
For a simple non-intersecting curve it is simple to decide
whether a point is on the inside or outside. For self-intersecting
curves the so-called winding rule is used. The winding rule is
also used when filling multiple curves
Given a point P consider a ray from P towards infinity. For each
intersection between the ray and the curve(s), determine whether
the curve crosses right-to-left or left-to-right. Each right-to-left
crossing counts as +1 and each left-to-right crossing as -1. If
the total sum of the counts are non-zero, then then point will be
filled.
This examples shows how linear gradients can be used to fill a triangle.
The first example use gradients from one color to another along the
edge of a triangle. The second example shows how fading from a color
c to (change-alphac0) is done.
The rgb-triangle was inspired by Andrew Stacey’s
RGB Triangle.