]> git.donarmstrong.com Git - lilypond.git/blob - mf/mf2pt1.mp
Merge branch 'master' of git://git.sv.gnu.org/lilypond
[lilypond.git] / mf / mf2pt1.mp
1 %%%%
2 %%%% MF2PT1.MP, by Scott Pakin, scott+mf@pakin.org
3 %%%%
4 %%%% This file is used to dump a special version of MetaPost with:
5 %%%%     mpost -progname=mpost -ini mf2pt1 \\dump
6 %%%%
7 %%%% To pretty-print this file, you'll need LaTeX and the mftinc package
8 %%%% (available from CTAN).
9 %%%%
10
11 %%%% ==================================================================== %%%%
12 %%%% mf2pt1                                                               %%%%
13 %%%% Copyright (C) 2007 Scott Pakin                                       %%%%
14 %%%%                                                                      %%%%
15 %%%% This program may be distributed and/or modified under the conditions %%%%
16 %%%% of the LaTeX Project Public License, either version 1.3c of this     %%%%
17 %%%% license or (at your option) any later version.                       %%%%
18 %%%%                                                                      %%%%
19 %%%% The latest version of this license is in:                            %%%%
20 %%%%                                                                      %%%%
21 %%%%    http://www.latex-project.org/lppl.txt                             %%%%
22 %%%%                                                                      %%%%
23 %%%% and version 1.3c or later is part of all distributions of LaTeX      %%%%
24 %%%% version 2006/05/20 or later.                                         %%%%
25 %%%% ==================================================================== %%%%
26
27 input mfplain;
28
29 %%% addto makepath makepen
30 %%% length clockwise counterclockwise
31 %%% scaled dashed withcolor
32
33 %% \begin{explaincode}
34 %%   Enable a \MF\ file to determine if it's being built with
35 %%   \texttt{mf2pt1}.
36 %% \end{explaincode}
37
38 newinternal ps_output;
39 ps_output := 1;
40
41
42 %% \begin{explaincode}
43 %%   The following was taken right out of \texttt{mfplain.mp}.  The \mfcomment
44 %    |def| and the |special|s at the end
45 %%   are the sole additions.  Normally, MetaPost outputs a tight bounding
46 %%   box around the character in its PostScript output.  The purpose of the
47 %%   first \mfcomment
48 %    |special|
49 %%   is to pass \texttt{mf2pt1} a bounding box that includes the proper
50 %%   surrounding whitespace.  The purpose of the second special is to
51 %%   provide \texttt{mf2pt1} with a default PostScript font name.
52 %% \end{explaincode}
53
54 def beginchar(expr c,w_sharp,h_sharp,d_sharp) =
55   begingroup
56     charcode:=if known c: byte c else: 0 fi;
57     charwd:=w_sharp;      charht:=h_sharp;       chardp:=d_sharp;
58     w:=charwd*pt; h:=charht*pt; d:=chardp*pt;
59     charic:=0; clearxy; clearit; clearpen; scantokens extra_beginchar;
60
61     def to_bp (expr num) = decimal (ceiling (num*bp_per_pixel)) enddef;
62     special "% MF2PT1: glyph_dimensions 0 " & to_bp (-d) & " " & to_bp(w) & " " & to_bp(h);
63     special "% MF2PT1: font_size " & decimal designsize;
64     special "% MF2PT1: font_slant " & decimal font_slant_;
65     special "% MF2PT1: charwd " & decimal charwd;   % Must come after the |font_size| |special|
66     for fvar = "font_identifier", "font_coding_scheme", "font_version",
67       "font_comment", "font_family", "font_weight", "font_unique_id",
68       "font_name":
69       if known scantokens (fvar & "_"):
70         special "% MF2PT1: " & fvar & " " & scantokens (fvar & "_");
71       fi;
72     endfor;
73     for fvar = "font_underline_position", "font_underline_thickness":
74       if known scantokens (fvar & "_"):
75         special "% MF2PT1: " & fvar & " " &
76           scantokens ("decimal " & fvar & "_");
77       fi;
78     endfor;
79     special "% MF2PT1: font_fixed_pitch " &
80             (if font_fixed_pitch_: "1" else: "0" fi);
81 enddef;
82
83
84 %% \begin{explaincode}
85 %%   Enable a character to specify explicitly the PostScript glyph
86 %%   name associated with it.
87 %% \end{explaincode}
88 def glyph_name expr name =
89   special "% MF2PT1: glyph_name " & name;
90 enddef;
91
92
93 %% \begin{explaincode}
94 %%   Store the value of \mfcomment
95 %    |font_slant_|, so we can recall it at each |beginchar|.
96 %% \end{explaincode}
97
98 font_slant_ := 0;
99
100 def font_slant expr x =
101   font_slant_ := x;
102   fontdimen 1: x
103 enddef;
104
105
106 %% \begin{explaincode}
107 %%   Redefine \mfcomment
108 %    |bpppix_|, the number of ``big'' points per pixel. \mfcomment
109 %    This in turn redefines |mm|, |in|, |pt|, and other derived units.
110 %% \end{explaincode}
111
112 def bpppix expr x =
113   bpppix_ := x;
114   mm := 2.83464 / bpppix_;
115   pt := 0.99626 / bpppix_;
116   dd := 1.06601 / bpppix_;
117   bp := 1 / bpppix_;
118   cm := 28.34645 / bpppix_;
119   pc := 11.95517 / bpppix_;
120   cc := 12.79213 / bpppix_;
121   in := 72 / bpppix_;
122   hppp := pt;
123   vppp := pt;
124 enddef;
125
126
127 %% \begin{explaincode}
128 %%   Define a bunch of PostScript font parameters to be used by
129 %%   \texttt{mf2pt1.pl}.  Default values are specified in
130 %%   \texttt{mf2pt1.pl}, not here.
131 %% \end{explaincode}
132
133 forsuffixes fvar = font_version, font_comment, font_family, font_weight,
134                    font_name, font_unique_id:
135   scantokens ("string " & str fvar & "_;");
136   scantokens ("def " & str fvar & " expr x = " & str fvar & "_ := x enddef;");
137 endfor;
138
139 forsuffixes fvar = font_underline_position, font_underline_thickness:
140   scantokens ("numeric " & str fvar & "_;");
141   scantokens ("def " & str fvar & " expr x = " & str fvar & "_ := x enddef;");
142 endfor;
143
144 boolean font_fixed_pitch_;
145 font_fixed_pitch_ := false;
146 def font_fixed_pitch expr x = font_fixed_pitch_ := x enddef;
147
148
149 %% \begin{explaincode}
150 %%   We'd like to be able to use calligraphic pens.  Normally, MetaPost's
151 %%   output routine does all the work for us of converting these to filled
152 %%   PostScript paths.  The only exception occurs for paths drawn using a
153 %%   pen that was transformed from  \mfcomment
154 %    |pencircle|.  MetaPost outputs these paths as stroked PostScript
155 %%   paths.  The following code tricks MetaPost into using a filled path
156 %%   for  \mfcomment
157 %    |pencircle| by replacing the primitive |pencircle| pen with a
158 %%   non-primitive approximation.  Note that we use a 20-gon for our circle
159 %%   instead of a diamond, so we get better results from  \mfcomment
160 %    |draw|.
161 %% \end{explaincode}
162
163 pen fakepencircle, mfplain_pencircle;
164 mfplain_pencircle := pencircle;
165 fakepencircle := makepen (for deg=0 step 360/20 until 359:
166     (0.5 cosd deg, 0.5 sind deg)--
167   endfor cycle);
168 save pencircle;
169 pen pencircle;
170 pencircle := fakepencircle;
171
172
173 %% \begin{explaincode}
174 %%   Return  \mfcomment
175 %    |true| if a path is cyclic, |false| otherwise.
176 %% \end{explaincode}
177
178 def is_cyclic expr cpath =
179   (point 0 of cpath = point (length cpath) of cpath)
180 enddef;
181
182
183 %% \begin{explaincode}
184 %%   Determine the direction of a path which doesn't intersect
185 %%   itself. \mfcomment
186 %    Returns |true| if the curve is clockwise, |false| if
187 %%   counterclockwise.  For non-cyclic paths the result is not
188 %%   predictable.
189 %%   \bigskip
190 %%
191 %%   The \mfcomment
192 %    |crossproduct|, |makeline|, and |is_clockwise| functions were
193 %%   provided by Werner Lemberg.
194 %%   \bigskip
195 %%
196 %%   The algorithm used is quite simple:
197 %%
198 %%   \begin{itemize}
199 %%     \item Find a point~$P$ on the path which has a non-zero direction.
200 %%
201 %%     \item Construct a ray of ``infinite'' length, starting in the
202 %%     vicinity of~$P$ which intersects the path at this point.
203 %%
204 %%     \item Use \mfcomment
205 %      |intersectiontimes| to find the intersection.  If the direction of
206 %%     the path at this point is (near) zero, or if we have a grazing
207 %%     intersection, get a new ray.
208 %%
209 %%     \item Shorten the ray so that it starts right after the
210 %%     intersection.  Repeat the previous step until no intersection is
211 %%     found.  Then go back to the last intersection and compare the path's
212 %%     direction with the direction of the ray.  According to the
213 %%     \emph{nonzero winding number} rule we have found a clockwise
214 %%     oriented path if it crosses the ray from left to right.
215 %%   \end{itemize}
216 %%
217 %%   This method completely avoids any problems with the geometry of
218 %%   B\'{e}zier curves.  If problems arise, a different ray is tried.
219 %%   Since it isn't necessary to analyze the whole path it runs quite fast
220 %%   in spite of using \mfcomment
221 %    |intersectiontimes| which is a slow MetaPost command.
222 %% \end{explaincode}
223
224 vardef crossproduct (expr u, v) =
225   save u_, v_;
226   pair u_, v_;
227
228   u_ := unitvector u;
229   v_ := unitvector v;
230
231   abs (xpart u_ * ypart v_ - ypart u_ * xpart v_)
232 enddef;
233
234 vardef makeline primary p =
235   save start, loop, d, n;
236   pair start, d;
237
238   loop := 0;
239   for i = 0.5 step 1 until length p - 0.5:
240     n := uniformdeviate 0.9 - 0.45 + i;    % Add some randomness to get different lines for each function call.
241     start := point n of p;
242     d := direction n of p;
243     if d <> (0, 0):
244       loop := 1;
245     fi;
246     exitif loop = 1;
247   endfor;
248
249   if loop = 0:
250     errmessage ("Cannot find a starting point on path for orientation test");
251   fi;
252
253   d := unitvector (d rotated (uniformdeviate 160 + 10));  % Again, some added randomness
254
255   % Construct a line which intersects the path at least once.
256   start - eps * d
257   -- infinity * d
258 enddef;
259
260 vardef is_clockwise primary p =
261   save line, cut, cut_new, res, line_dir, tangent_dir;
262   path line;
263   pair cut, cut_new, line_dir, tangent_dir;
264
265   res := 1;
266
267   line := makeline p;
268   line_dir := direction 0 of line;
269
270   % Find the outermost intersection.
271   cut := (0, 0);
272   forever:
273     cut_new := line intersectiontimes p;
274     exitif cut_new = (-1, -1);
275
276     % Compute a new line if we have a strange intersection.
277     tangent_dir := direction (ypart cut_new) of p;
278     if abs tangent_dir < eps:
279       % The vector is zero or too small.
280       line := makeline p;
281       line_dir := direction 0 of line;
282     elseif crossproduct (tangent_dir, line_dir) < 0.02:
283       % Grazing intersection
284       line := makeline p;
285       line_dir := direction 0 of line;
286     else:
287       % Try again.
288       cut := cut_new;
289       line := subpath (xpart cut + eps, infinity) of line;
290     fi;
291   endfor;
292
293   tangent_dir := direction (ypart cut) of p;
294   res := (angle tangent_dir - angle line_dir + 180) mod 360 - 180;
295
296   res < 0
297 enddef;
298
299
300 %% \begin{explaincode}
301 %%   Make a given path run clockwise or counterclockwise.  \mfcomment
302 %    (|counterclockwise| is defined by \texttt{mfplain} but we override
303 %%   it here.)
304 %% \end{explaincode}
305
306 vardef counterclockwise primary c =
307   (if is_clockwise c: (reverse c) else: c fi)
308 enddef;
309
310 vardef clockwise primary c =
311   (if is_clockwise c: c else: (reverse c) fi)
312 enddef;
313
314
315 %% \begin{explaincode}
316 %%   Redefine  \mfcomment
317 %    |fill| and |unfill| to ensure that filled paths run
318 %%   counterclockwise and unfilled paths run clockwise, as is required
319 %%   by PostScript Type~1 fonts.
320 %% \end{explaincode}
321
322 def fill expr c =
323   addto currentpicture contour counterclockwise c t_ pc_
324 enddef;
325
326 def unfill expr c =
327   addto currentpicture contour clockwise c t_ pc_ withcolor background
328 enddef;
329
330
331 %% \begin{explaincode}
332 %%   Convert  \mfcomment
333 %    |filldraw| and |unfilldraw| to |fill| and |unfill|.
334 %% \end{explaincode}
335
336 let mfplain_filldraw := filldraw;
337 def filldraw expr c =
338   begingroup
339     message "! Warning: Replacing filldraw with fill.";
340     fill c
341   endgroup
342 enddef;
343
344 let mfplain_unfilldraw := unfilldraw;
345 def unfilldraw expr c =
346   begingroup
347     message "! Warning: Replacing unfilldraw with unfill.";
348     unfill c
349   endgroup
350 enddef;
351
352
353 %% \begin{explaincode}
354 %%   Return  \mfcomment
355 %    |true| if |currentpen| looks like a |pencircle|.
356 %% \end{explaincode}
357
358 def using_pencircle =
359   begingroup
360     path qpath, circlepath;
361     qpath = makepath currentpen;
362     numeric circlediv;
363     circlepath = makepath pencircle;
364     circlediv = xpart (lrcorner circlepath);
365
366     (length qpath = length circlepath) and (pen_rt <> 0) and (pen_top <> 0)
367     for pp = 0 upto (length qpath)-1:
368       and ((xpart (point pp of qpath) / pen_rt,
369             ypart (point pp of qpath) / pen_top) =
370            point pp of circlepath / circlediv)
371     endfor
372   endgroup
373 enddef;
374
375
376 %% \begin{explaincode}
377 %%   If the pen looks like a circular pen, draw a nice circle.  Otherwise,
378 %%   draw the pen as is.
379 %% \end{explaincode}
380
381 def drawdot expr z =
382   if using_pencircle:
383     begingroup
384       path cpath;
385       numeric clength;
386       cpath = makepath currentpen;
387       clength = length cpath;
388       fill ((point 0 of cpath)
389         ..(point clength/4 of cpath)
390         ..(point clength/2 of cpath)
391         ..(point 3*clength/4 of cpath)
392         ..cycle) shifted z t_
393     endgroup
394   else:
395     addto currentpicture contour makepath currentpen shifted z
396     t_ pc_
397   fi
398 enddef;
399
400
401 %% \begin{explaincode}
402 %%   Do the same as the above, but unfill the current pen.
403 %% \end{explaincode}
404
405 def undrawdot expr z =
406   if using_pencircle:
407     begingroup
408       path cpath;
409       numeric clength;
410       cpath = makepath currentpen;
411       clength = length cpath;
412       unfill ((point 0 of cpath)
413         ..(point clength/4 of cpath)
414         ..(point clength/2 of cpath)
415         ..(point 3*clength/4 of cpath)
416         ..cycle) shifted z t_
417     endgroup
418   else:
419     unfill makepath currentpen shifted z t_
420   fi
421 enddef;
422
423
424 %% \begin{explaincode}
425 %%   MetaPost renders \mfcomment
426 %    |draw| with a filled curve.
427 %%   Hence, we need to ensure the orientation is correct (i.e.,
428 %%   counterclockwise).  Unfortunately, we have no way to check for
429 %%   overlap, and it's fairly common for MetaPost to output
430 %%   self-overlapping curve outlines, even if the curve itself has no
431 %%   overlap.
432 %% \end{explaincode}
433
434 def draw expr p =
435   addto currentpicture
436   if picture p:
437     also p
438   elseif is_cyclic p:
439     doublepath counterclockwise p t_ withpen currentpen
440   else:
441     if is_clockwise (p--cycle):
442       doublepath (reverse p) t_ withpen currentpen
443     else:
444       doublepath p t_ withpen currentpen
445     fi
446   fi
447   pc_
448 enddef;
449
450 def undraw expr p =
451   addto currentpicture
452   if picture p:
453     also p
454   elseif is_cyclic p:
455     doublepath clockwise p t_ withpen currentpen
456   else:
457     if is_clockwise (p--cycle):
458       doublepath p t_ withpen currentpen
459     else:
460       doublepath (reverse p) t_ withpen currentpen
461     fi
462   fi
463   pc_ withcolor background
464 enddef;