2 %%%% MF2PT1.MP, by Scott Pakin, scott+mf@pakin.org
4 %%%% This file is used to dump a special version of MetaPost with:
5 %%%% mpost -progname=mpost -ini mf2pt1 \\dump
7 %%%% To pretty-print this file, you'll need LaTeX and the mftinc package
8 %%%% (available from CTAN).
11 %%%% ==================================================================== %%%%
13 %%%% Copyright (C) 2008 Scott Pakin %%%%
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. %%%%
19 %%%% The latest version of this license is in: %%%%
21 %%%% http://www.latex-project.org/lppl.txt %%%%
23 %%%% and version 1.3c or later is part of all distributions of LaTeX %%%%
24 %%%% version 2006/05/20 or later. %%%%
25 %%%% ==================================================================== %%%%
29 %%% addto makepath makepen
30 %%% length clockwise counterclockwise
31 %%% scaled dashed withcolor
33 %% \begin{explaincode}
34 %% Enable a \MF\ file to determine if it's being built with
38 newinternal ps_output;
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
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.
54 def beginchar(expr c,w_sharp,h_sharp,d_sharp) =
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;
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",
69 if known scantokens (fvar & "_"):
70 special "% MF2PT1: " & fvar & " " & scantokens (fvar & "_");
73 for fvar = "font_underline_position", "font_underline_thickness":
74 if known scantokens (fvar & "_"):
75 special "% MF2PT1: " & fvar & " " &
76 scantokens ("decimal " & fvar & "_");
79 special "% MF2PT1: font_fixed_pitch " &
80 (if font_fixed_pitch_: "1" else: "0" fi);
84 %% \begin{explaincode}
85 %% Enable a character to specify explicitly the PostScript glyph
86 %% name associated with it.
88 def glyph_name expr name =
89 special "% MF2PT1: glyph_name " & name;
93 %% \begin{explaincode}
94 %% Store the value of \mfcomment
95 % |font_slant_|, so we can recall it at each |beginchar|.
100 def font_slant expr x =
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.
114 mm := 2.83464 / bpppix_;
115 pt := 0.99626 / bpppix_;
116 dd := 1.06601 / bpppix_;
118 cm := 28.34645 / bpppix_;
119 pc := 11.95517 / bpppix_;
120 cc := 12.79213 / bpppix_;
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.
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;");
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;");
144 boolean font_fixed_pitch_;
145 font_fixed_pitch_ := false;
146 def font_fixed_pitch expr x = font_fixed_pitch_ := x enddef;
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
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
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)--
170 pencircle := fakepencircle;
173 %% \begin{explaincode}
175 % |true| if a path is cyclic, |false| otherwise.
178 def is_cyclic expr cpath =
179 (point 0 of cpath = point (length cpath) of cpath)
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
192 % |crossproduct|, |makeline|, and |is_clockwise| functions were
193 %% provided by Werner Lemberg.
196 %% The algorithm used is quite simple:
199 %% \item Find a point~$P$ on the path which has a non-zero direction,
200 %% and which is on a not-too-short path element.
202 %% \item Construct a ray of ``infinite'' length, starting in the
203 %% vicinity of~$P$ which intersects the path at this point.
205 %% \item Use \mfcomment
206 % |intersectiontimes| to find the intersection. If the direction of
207 %% the path at this point is (near) zero, or if we have a grazing
208 %% intersection or even a tangent, get a new ray.
210 %% \item Shorten the ray so that it starts right after the
211 %% intersection. Repeat the previous step until no intersection is
212 %% found. Then go back to the last intersection and compare the path's
213 %% direction with the direction of the ray. According to the
214 %% \emph{nonzero winding number} rule we have found a clockwise
215 %% oriented path if it crosses the ray from left to right.
218 %% This method completely avoids any problems with the geometry of
219 %% B\'{e}zier curves. If problems arise, a different ray is tried.
220 %% Since it isn't necessary to analyze the whole path it runs quite fast
221 %% in spite of using \mfcomment
222 % |intersectiontimes| which is a slow MetaPost command.
225 vardef crossproduct (expr u, v) =
232 abs (xpart u_ * ypart v_ - ypart u_ * xpart v_)
235 vardef makeline primary p =
236 save start, bad_n, loop, distance, d, i, n;
241 for i := 0 step 1 until length p - 1:
242 distance := length (point i of p - point (i + 1) of p);
245 % In case we don't find something better.
262 % Add some randomness to get different lines for each function call.
263 n := n + uniformdeviate 0.8 + 0.1;
264 start := point n of p;
267 % Construct a line which misses the degenerated path.
271 d := direction n of p;
273 % Again, some added randomness.
274 n := uniformdeviate 150 + 15;
275 d := unitvector (d rotated n);
277 % Construct a line which intersects the path at least once.
283 vardef is_clockwise primary p =
284 save line, cut, cut_new, res, line_dir, tangent_dir;
286 pair cut, cut_new, line_dir, tangent_dir;
289 line_dir := direction 0 of line;
291 % Find the outermost intersection.
294 cut_new := line intersectiontimes p;
295 exitif cut_new = (-1, -1);
297 % Compute a new line if we have a strange intersection.
298 tangent_dir := direction (ypart cut_new) of p;
299 if abs tangent_dir < eps:
300 % The vector is zero or too small.
302 line_dir := direction 0 of line;
304 elseif abs (ypart cut_new - floor (ypart cut_new + 0.5)) < eps:
305 % Avoid possible tangent touching in a corner or cusp.
307 line_dir := direction 0 of line;
309 elseif crossproduct (tangent_dir, line_dir) < 0.2:
310 % Grazing intersection (arcsin 0.2 ~= 11.5 degrees).
312 line_dir := direction 0 of line;
317 line := subpath (xpart cut + eps, infinity) of line;
321 tangent_dir := direction (ypart cut) of p;
322 if tangent_dir <> (0, 0):
323 res := (angle tangent_dir - angle line_dir + 180) mod 360 - 180;
331 %% \begin{explaincode}
332 %% Make a given path run clockwise or counterclockwise. \mfcomment
333 % (|counterclockwise| is defined by \texttt{mfplain} but we override
337 vardef counterclockwise primary c =
338 (if is_clockwise c: (reverse c) else: c fi)
341 vardef clockwise primary c =
342 (if is_clockwise c: c else: (reverse c) fi)
346 %% \begin{explaincode}
347 %% Redefine \mfcomment
348 % |fill| and |unfill| to ensure that filled paths run
349 %% counterclockwise and unfilled paths run clockwise, as is required
350 %% by PostScript Type~1 fonts.
354 addto currentpicture contour counterclockwise c t_ pc_
358 addto currentpicture contour clockwise c t_ pc_ withcolor background
362 %% \begin{explaincode}
363 %% Convert \mfcomment
364 % |filldraw| and |unfilldraw| to |fill| and |unfill|.
367 let mfplain_filldraw := filldraw;
368 def filldraw expr c =
370 message "! Warning: Replacing filldraw with fill.";
375 let mfplain_unfilldraw := unfilldraw;
376 def unfilldraw expr c =
378 message "! Warning: Replacing unfilldraw with unfill.";
384 %% \begin{explaincode}
386 % |true| if |currentpen| looks like a |pencircle|.
389 def using_pencircle =
391 path qpath, circlepath;
392 qpath = makepath currentpen;
394 circlepath = makepath pencircle;
395 circlediv = xpart (lrcorner circlepath);
397 (length qpath = length circlepath) and (pen_rt <> 0) and (pen_top <> 0)
398 for pp = 0 upto (length qpath)-1:
399 and ((xpart (point pp of qpath) / pen_rt,
400 ypart (point pp of qpath) / pen_top) =
401 point pp of circlepath / circlediv)
407 %% \begin{explaincode}
408 %% If the pen looks like a circular pen, draw a nice circle. Otherwise,
409 %% draw the pen as is.
417 cpath = makepath currentpen;
418 clength = length cpath;
419 fill ((point 0 of cpath)
420 ..(point clength/4 of cpath)
421 ..(point clength/2 of cpath)
422 ..(point 3*clength/4 of cpath)
423 ..cycle) shifted z t_
426 addto currentpicture contour makepath currentpen shifted z
432 %% \begin{explaincode}
433 %% Do the same as the above, but unfill the current pen.
436 def undrawdot expr z =
441 cpath = makepath currentpen;
442 clength = length cpath;
443 unfill ((point 0 of cpath)
444 ..(point clength/4 of cpath)
445 ..(point clength/2 of cpath)
446 ..(point 3*clength/4 of cpath)
447 ..cycle) shifted z t_
450 unfill makepath currentpen shifted z t_
455 %% \begin{explaincode}
456 %% MetaPost renders \mfcomment
457 % |draw| with a filled curve.
458 %% Hence, we need to ensure the orientation is correct (i.e.,
459 %% counterclockwise). Unfortunately, we have no way to check for
460 %% overlap, and it's fairly common for MetaPost to output
461 %% self-overlapping curve outlines, even if the curve itself has no
470 doublepath counterclockwise p t_ withpen currentpen
472 if is_clockwise (p--cycle):
473 doublepath (reverse p) t_ withpen currentpen
475 doublepath p t_ withpen currentpen
486 doublepath clockwise p t_ withpen currentpen
488 if is_clockwise (p--cycle):
489 doublepath p t_ withpen currentpen
491 doublepath (reverse p) t_ withpen currentpen
494 pc_ withcolor background