@c * (1) GNU 3DLDF 1.2.0.0 Manual --- Plain Text Version @c ** (2) Copyright and License Copyright (C) 2003, 2004, 2005, 2006, 2007 The Free Software Foundation See the section "GNU Free Documentation License" in the file fdl.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 eval:(read-abbrev-file "/home/uni4/UHSK/lfinsto1/.abbrev_defs") eval:(auto-fill-mode t) End: