@c * (1) GNU 3DLDF 1.2.0.0 Manual --- Plain Text Version
@c ** (2) Copyright and License
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
2012, 2013, 2014, 2015, 2016, 2017, 2018 The Free Software Foundation
See the section "GNU Free Documentation License" in the file
fdl-1.3.txt.txt for copying conditions.
@c $Id: manual.txt,v 1.8 2005/01/31 10:22:01 lfinsto1 Exp $
@c ** (2) Introduction
@c ** (2) Declarations
@c *** (3) Data Types
The Metafont language defines the following data types:
`boolean', `numeric', `pair', `path', `pen',`picture',
`string', and `transform'.
MetaPost adds the type `color'.
3DLDF has all of these types except for `pair', which has been
replaced by `point'.
It also has the following, additional
types:
`bool_point',
`focus',
`color,
`dash_pattern',
`reg_polygon',
`rectangle',
`ellipse',
`circle',
`cuboid',
`tetrahedron',
`octahedron',
`dodecahedron',
`icosahedron', and
`trunc_octahedron' (truncated octahedron).
MetaPost has a `dashpattern', but it's a macro, not a data type.
@c **** (4) Vector-types
In addition, 3DLDF has "vector-types" corresponding to each
of the types listed above. For example, `point_vector' corresponds
to `point', `path_vector' to `path', etc.
Vector-type variables make it possible to reference an entire array
in a single operation.
@c *** (3) Declaring variables
Variables can be declared in several ways:
point p;
A single variable `p' of type `point'.
transform t, u, v;
Multiple variables in a single declaration.
circle c[];
A declaration using a `tag' with a `generic subscript'.
It declares an `array' of `circles'. Now, `c' followed
by a single _numerical suffix_ is a `circle' variable, e.g.,
`c0', `c1', and `c[1.5/32]'.
An empty pair of square brackets
must be used to declare arrays of variables; `circle c1;' would
not be valid declaration.
dodecahedron uv[]w[];
An array with two subscripts. After this declaration,
`uv0w21', for example, is a `dodecahedron' variable.
path p, q[], r[]st[]u[];
The types described above can be mixed freely.
When a variable with a subscript is used and the subscript is an
integer, the latter needn't be surrounded by brackets (`t1'), but
it may be, i.e., `t1' and `t[1]' are equivalent.
If the subscript has a decimal point (`t[3.5]') or is a
fraction (`t[1/2]'), then brackets are needed. If the brackets
were left out, `t3.5' would be interpreted as `t[3][5]' and
@c
@c !! Check this. LDF 2005.01.12.
@c This is true, but it doesn't work to test it when typing in input
@c from a shell. All variants work correctly when reading from a
@c file. Explain and say what happens in each case. LDF 2004.11.27.
@c
`t1/2' would be interpreted as `t1' divided by 2.
@c **** (4) numeric Variables
`numeric' variables can be declared in the same way
as other types.
numeric a;
numeric b[], c[]d[];
However, assigning a numeric value to an unknown variable is
equivalent to declaring that variable as a `numeric' and then
assigning to it.
show a;
>> (unknown numeric)
show b1;
>> (unknown numeric)
a := 2.5;
b1 := 1/2;
show a;
>> 2.5
show b1;
>> 0.5
Please note that `b1' isn't a member of an array of
`numerics'! In this case, the ``1'' is not a numerical
subscript.
@c ** (2) Vector-Type Variables
In Metafont, MetaPost, and 3DLDF, it's possible to declare arrays of
variables, as explained above.
numeric a[];
point b[]c[];
After these declarations, it's possible to access `a1',
`b[3/2]c2', etc. If suitable subscripts have been chosen, it's
possible to access them sequentially by using a loop.
point p[];
for i = 0 upto 4:
p[i] := (i, i, i);
show p[i];
endfor;
-->
>> (0, 0, 0)
>> (1, 1, 1)
>> (2, 2, 2)
>> (3, 3, 3)
Neither Metafont nor MetaPost provides a means of accessing
multiple members of an array in a single operation, whereas in
3DLDF it is sometimes necessary to do this.
Therefore, I have introduced the notion of "vector-types".
There is a vector-type corresponding to each of the basic types
defined in 3DLDF.
Variables of vector-types are declared in the same way as
variables of other types, except that it's not possible to declare
arrays of vector-types.
point_vector pv;
bool_point_vector bpv_a, bpv_b;
color_vector cv[]; %% Invalid.
point_vector q[]r[]; %% Invalid.
The reason for this is that the declaration of a vector-type
variable already implies the declaration of an array of the
corresponding type. For example, the declaration
`point_vector pv;' declares a variable `pv' of type `point_vector'
and an array of variables of type `point' with names consisting
of `pv' followed by a single numerical suffix. It is as though
`point pv[]' had also been declared.
`bool_point_vectors' are used for storing the result of some operations for
finding the intersection points of two objects. This result can be assigned
to a `point_vector', if the user isn't interested in the
`boolean' values. `color_vectors' can be passed as arguments to the
drawing and filling functions for solid types (currently `cuboid',
`tetrahedron', `octahedron', `dodecahedron', `icosahedron', and
`trunc_octahedron').
The 3DLDF language itself doesn't use any of the other vector-types for
anything,
but they are available for all types.
All but two of them have a complete expression hierarchy, i.e.,
for a type `x_vector', 3DLDF defines `x_vector_primary',
`x_vector_secondary', `x_vector_tertiary', and `x_vector_expression'.
The two exceptions are `picture' and `macro'. `picture_vectors' and
`macro_vectors' can therefore only be accessed by means of
`picture_vector_variables' and `macro_vector_variables', respectively.
The parser rules for `primaries', `secondaries', `tertiaries', and
`expressions'
involve creating, copying, and destroying temporaries for all types except
for `picture'. I therefore say that `pictures' are "persistent".
This is because `pictures' can contain very large amounts of data, so it's
best not to copy them unless it's really necessary. It would take quite a
bit of work to account for this fact in the rules for `picture_vectors',
and I don't think it would be worth the effort.
I have no plans to add any parser rules that involve
`picture_vector' expressions on the primary, secondary,
tertiary, or expression level.
The situation is somewhat different for `macro_vectors'.
Here, there are no `macro_primaries', `macro_secondaries', `macro_tertiaries',
or `macro_expressions'; `macros' are only ever accessed by means of
variables. Nor do they ever occur on the right-hand side of a parser rule.
@c ** (2) Assignments
When variables are declared, they have no values, they are "unknown".
They receive their values by means of _assignments_. The latter have
a _left-hand side_ and a _right-hand side_, separated by the
_assignment operator_ `:='. Only certain kinds of objects can occur
on either side. Objects that can occur on the left-hand side are called
_lvalues_, while those that can occur on the right-hand side are called
_rvalues_. The most basic form of assignment has a variable on the
left-hand side and an `expression' on the right-hand side.
@c ** (2) Expressions
The GNU 3DLDF language is built around _expressions_. For most of the data
types defined in 3DLDF, there is an _expression hierarchy_. For example,
when a `point' occurs in a `statement', it will be either a `point_primary',
a `point_secondary', a `point_tertiary', or a `point_expression'. The term
`expression' is unfortunately ambiguous, because it can refer to an object
at any level of the hierarchy, i.e., a `primary', `secondary', `tertiary',
or `expression', or specifically to an object at the `expression' level.
When I mean the former, I will say, e.g., "`point' expression", and when
I mean the latter, "`point_expression'". Usually, the meaning should be
clear from the context, anyway.
The expression hierarchy determines the precedence of operations
performed on objects in 3DLDF. For example, multiplication has a
higher precedence than addition and this is reflected in the
parser rules that implement these operations: In the expression
`2 * 3', `2' is a `numeric_secondary', `3' is a `numeric_primary',
and the result (6) is a `numeric_secondary'. On the other hand,
in the expression `5 + 6', `5' is a `numeric_tertiary', `6' is a
`numeric_secondary', and the result (11) is a `numeric_tertiary'.
Thus, the multiplication of two `numerics' is an operation that
takes a `numeric_secondary' and a `numeric_primary' to a
`numeric_secondary ' and the addition of two `numerics' is an
operation that takes a `numeric_tertiary' and a `numeric_secondary'
to a `numeric_tertiary'.
An `expression' by itself is not a valid `statement' in the 3DLDF language.
They must be used together with other syntactic elements to form a
`statement'. There are various kinds of `statements': `declaration',
`assignment', and `command' are three of them.
@c ** (2) Types
@c *** (3) Non-Shape-Types
@c **** (4) `boolean'
`boolean' objects can either be `true' or `false'.
boolean b, c;
b := true;
c := false;
@c ***** (5) Boolean operations
3DLDF implements the Boolean operations `not', `and', and `or', which are at
the `primary', `secondary', and `tertiary' level, respectively.
`NOT' is a unary operator that takes a as its argument.
boolean b, c;
b := true;
show not b;
>> false
c := false;
show not c;
>> true;
`and' is a binary operator at the secondary level, and thus takes a
and a to a , i.e.,
--> AND
`AND' is capitalized in this syntax rule to indicate that it is a _primitive_
of the 3DLDF language. The _symbolic token_ `and' stands for the primitive
`AND' per default but this is not prescribed by the 3DLDF language. Other
`symbolic_tokens' could be defined to stand for `AND', and `and' could be
defined to stand for something else. However, the meaning of `AND' is fixed
and unaffected by changes to `symbolic_tokens'. In this manual, I use the
lowercase to refer to the primitives they represent, except
in the syntax rules themselves. Readers should bear in mind that they merely
refer to primitives, they are not the primitives themselves.
Let us assume that an input to 3DLDF matches this syntax rule.
If and only if the and the on the
right-hand side are both `true', the on the left-hand
side will also be `true'. Otherwise, if either or both of the `booleans' on
the right-hand side are `false', the on the left-hand side
will be `false'.
@c Make sure this works! LDF 2004.01.26
Since `not' is an operator at the `primary' level, it has a higher precedence
than
`and', so if `b' and `c' are `booleans', the expression
`not a and b' is interpreted as `(not a) and b', and not as `not (a and b)'.
@c ***** (5) Predicates
_Predicates_ are tests which can be applied to objects
and which return `boolean_primaries'. Such tests can appear on the
right-hand side of assignments.
boolean a, b;
point p;
a := is_point p;
show a;
>> true
b := is_path p;
>> false;
There is such a test for every data type defined in the 3DLDF language, i.e.,
`is_transform', `is_path_vector', `is_string', etc.
@c **** (4) string
`strings' can be specified by surrounding text with quotation marks:
string s;
s := "abc";
They can be concatenated by using the `&' operator:
s := s & "def";
show s;
>> "abcdef"
@c **** (4) bool_point
@c **** (4) transform
`transform' objects represent _transformations_, which can be applied
to other objects to change their shape and/or position. A `transform'
contains a 4 X 4 matrix of `real' values, so `transforms' can
represent any three-dimensional transformation. In particular, the
transformations represented by `transforms' need not be affine, i.e.,
if two parallel lines are transformed by the same transformation, the
transformed lines will not necessarily be parallel. The perspective
transformation is non-affine. Other non-affine transformations may
also be useful.
Objects of `Shape' types, such as `points', `paths', `ellipses',
`cuboids', etc., can be transformed directly using `transformers':
point p, q;
p := origin shifted (1, 2, 3);
q := p scaled 2 rotated (15, 30, 60);
However, transformations can be stored in a `transform' and applied to such
objects using the `*=' operator:
point p;
p := (1, 2, 3);
transform t;
t := identity scaled (2, 3, 4) rotated (30, 60, 90);
p *= t;
show p;
>>
Invocations of the `*=' operator with a `transform' argument can be _chained_.
The same `transform' is then applied to all of the objects in the chain:
point p;
circle c;
ellipse e;
p := (1, 2, 3);
c := unit_circle;
e := unit_circle scaled (1, 2);
transform t;
t := identity sheared (1, 2, 3, 4, 5, 6);
p *= c *= e *= t;
show p;
>>
show c;
>>
show e;
`transforms' can also be multiplied by other transforms:
@c **** (4) color
Colors are represented in 3DLDF by a triple of numerical values. They
are therefore similar (but not identical) to `points'. An unfortunate
consequence of this is that it's not possible to assign to `colors' in
the obvious way:
color c;
c := (.5, .6, .7); %% Invalid!
The 3DLDF parser interprets a triple of numeric values separated by commata
and surrounded by parentheses as a `point_primary'. The interpretation must
be unambiguous; it's not possible for it to be interpreted as a
`color_primary' when assigning to a `color_variable'. `colors' must therefore
be _set_ using a `set' command:
color c;
set c (.5, .6, .7);
show c;
-->
name ==
red_part == 0.500000
green_part == 0.600000
blue_part == 0.700000
@c **** (4) pen
There are four basic types of `pens':
`pencircle', `pensquare', `penrazor', and `penspeck'.
These keywords can be used to assign to `pen' variables, together with
a transformation, if desired.
pen p;
p := pencircle scaled (1, 2, 3);
show p;
-->
>> Pen:
name == pencircle
type == pencircle
transform:
1 0 0 0
0 2 0 0
0 0 3 0
0 0 0 1
@c **** (4) dash_pattern
In GNU 3DLDF, `dash_pattern' is a data type.
There is no such thing as a "dash pattern" in Metafont, and in MetaPost,
`dashpattern' is a macro, not a data type. There are two predefined
`dash_pattern' variables in 3DLDF, `evenly' and `with_dots' which can be used
to assign to `dash_pattern' variables.
dash_pattern d, e;
d := evenly;
e := with_dots;
It's possible to transform `dash_patterns':
dash_pattern d;
path p;
p := origin -- (1, 1) -- (2, 0);
d := evenly scaled (2, 3) rotated 15;
pickup pensquare scaled 3pt;
draw p;
@c **** (4) picture
`pictures' in GNU 3DLDF Metafont are quite different from those in Metafont.
@c *** (3) Shape-Types
@c **** (4) point
@c **** (4) path
@c **** (4) Polygonal Types
@c ***** (5) rectangle
@c ***** (5) reg_polygon
@c **** (4) Curves
@c ***** (5) Conic Sections
@c ****** (6) ellipse
@c ****** (6) circle
@c **** (4) Solid Types
@c ***** (5) cuboid
@c ***** (5) Polyhedra
@c ****** (6) Platonic Polyhedra
@c ******* (7) tetrahedron
@c ******* (7) octahedron
@c ******* (7) dodecahedron
@c ******* (7) icosahedron
@c ****** (6) Semi-Regular Archimedean Polyhedra
@c ******* (7) trunc_octahedron (Truncated Octahedron)
@c ** (2) Grouping
_Grouping_ makes it possible to declare variables locally.
A _group_ starts with the keyword `begingroup' and ends with the
keyword `endgroup':
point p;
p := (1, 2, 3);
begingroup;
bool p;
b := true;
show p;
>> true
endgroup;
show p;
>> (1, 2, 3)
Unlike Metafont, 3DLDF doesn't have a `save' command.
All variables declared within a group are local.
3DLDF thus has C-like rather than Metafont-like grouping.
Loops and macros implicitly create groups, so that declarations within
these constructions are local:
Loop example:
bool p;
p := true;
for i = 0 upto 3:
point p;
message "p in loop:";
p := (i, i, i);
show p;
endfor;
message "p after loop:";
show p;
-->
p in loop:
>> (0.000000, 0.000000, 0.000000)
p in loop:
>> (1.000000, 1.000000, 1.000000)
p in loop:
>> (2.000000, 2.000000, 2.000000)
p in loop:
>> (3.000000, 3.000000, 3.000000)
p after loop:
>> true
Macro example:
bool q;
q := false;
def m {point i, point j, point k} :=
path q;
q := i .. j .. k;
message "In `m': `q' == ";
show q;
enddef;
point p[];
p0 := (0, 0, 0);
p1 := (1, 1, 1);
p2 := (2, 2, 2);
m {p0, p1, p2};
message "After call to `m': `q' == ";
show q;
-->
In `m': `q' ==
>> Path:
points.size() == 3
connectors.size() == 2
(0.000000, 0.000000, 0.000000) .. (1.000000, 1.000000, 1.000000) ..
(2.000000, 2.000000, 2.000000);
fill_draw_value == 0
draw_color == 0
fill_color == 0
pen == 0
dash_pattern == 0
`arrow' == `Path::NO_ARROW'.
After call to `m': `q' ==
>> false
@c ** (2) Intersections
@c ** (2) Commands
@c *** (3) Drawing
@c *** (3) Labels
@c *** (3) Output
@c ** (2) Conditionals
@c ** (2) Loops
@c ** (2) Macros
Macros in GNU 3DLDF serve the same purpose as macros in Metafont,
but their semantics and syntax are somewhat different.
Macros are commands that are defined by users or supplied in a
"macro package". The simplest form of macro takes no arguments, but
macros can also take _typed_ and/or _untyped_ arguments.
A macro definition consists of the keyword `def' followed by a
_symbolic token_, `=' or `:=', the _replacement text_, and the keyword
`enddef', followed by a semi-colon:
def m :=
a := 3;
message "a:";
show a;
enddef;
Now, `m' can be used like a built-in command:
m;
>> a:
>> 3
When `m' is _invoked_, the 3DLDF scans the _replacement text_,
executing the statements it contains. The replacement text is
implicitly put into a group, so declarations made in the replacement
text are local to the macro:
def n =
point p;
p := (1, 2, 3);
show p;
enddef;
n;
>> (1.000000, 2.000000, 3.000000)
show p; %% `p' is now an undeclared `numeric'.
>> 0
@c *** (3) Untyped arguments
Untyped arguments are specified following the name of the macro,
surrounded by parentheses, and separated by commata:
def m (a, b, c) =
message "a:";
show a;
message "b:";
show b;
message "c:";
show c;
enddef;
Untyped arguments must be `symbolic tokens', i.e., they must consist
of a single `tag' with no suffixes.
@c *** (3) Typed arguments
Typed arguments are specified following the name of the macro,
surrounded by braces, and separated by commata:
def m {point p, path q, transform t) =
point a;
a := (1, 2, 3);
q := p .. a;
transform q by t;
enddef;
Typed arguments consist of a type name, e.g., `point', `path',
`transform', etc., and a single `symbolic token'.
Typed arguments make it possible to pass `expressions' as the
arguments to a macro rather than variables.
@c * (1)
Local Variables:
mode:Outline
outline-regexp:"@c [*\f]+"
abbrev-mode:t
End: