2 lookup.cc -- implement simple Lookup methods.
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
8 Jan Nieuwenhuizen <janneke@gnu.org>
17 #include "line-interface.hh"
19 #include "dimensions.hh"
21 #include "file-path.hh"
23 #include "lily-guile.hh"
26 Lookup::dot (Offset p
, Real radius
)
28 SCM at
= (scm_list_n (ly_symbol2scm ("dot"),
29 scm_from_double (p
[X_AXIS
]),
30 scm_from_double (p
[Y_AXIS
]),
31 scm_from_double (radius
),
34 box
.add_point (p
- Offset (radius
, radius
));
35 box
.add_point (p
+ Offset (radius
, radius
));
36 return Stencil (box
, at
);
40 Lookup::beam (Real slope
, Real width
, Real thick
, Real blot
)
46 p
= Offset (0, thick
/ 2);
48 p
+= Offset (1, -1) * (blot
/ 2);
52 points
= scm_cons (scm_from_double (p
[X_AXIS
]),
53 scm_cons (scm_from_double (p
[Y_AXIS
]),
56 p
= Offset (0, -thick
/ 2);
58 p
+= Offset (1, 1) * (blot
/ 2);
60 points
= scm_cons (scm_from_double (p
[X_AXIS
]),
61 scm_cons (scm_from_double (p
[Y_AXIS
]),
64 p
= Offset (width
, width
* slope
- thick
/ 2);
66 p
+= Offset (-1, 1) * (blot
/ 2);
68 points
= scm_cons (scm_from_double (p
[X_AXIS
]),
69 scm_cons (scm_from_double (p
[Y_AXIS
]),
72 p
= Offset (width
, width
* slope
+ thick
/ 2);
74 p
+= Offset (-1, -1) * (blot
/ 2);
76 points
= scm_cons (scm_from_double (p
[X_AXIS
]),
77 scm_cons (scm_from_double (p
[Y_AXIS
]),
80 SCM expr
= scm_list_n (ly_symbol2scm ("polygon"),
81 ly_quote_scm (points
),
82 scm_from_double (blot
),
86 return Stencil (b
, expr
);
90 Lookup::dashed_slur (Bezier b
, Real thick
, Real dash_period
, Real dash_fraction
)
94 Real on
= dash_fraction
* dash_period
;
95 Real off
= dash_period
- on
;
98 l
= scm_cons (ly_offset2scm (b
.control_
[i
]), l
);
100 SCM at
= (scm_list_n (ly_symbol2scm ("dashed-slur"),
101 scm_from_double (thick
),
102 scm_from_double (on
),
103 scm_from_double (off
),
107 Box
box (b
.extent (X_AXIS
), b
.extent (Y_AXIS
));
108 return Stencil (box
, at
);
112 Lookup::rotated_box (Real slope
, Real width
, Real thick
, Real blot
)
115 Offset
rot (1, slope
);
119 rot
/= sqrt (1 + slope
*slope
);
120 pts
.push_back (Offset (0, -thick
/ 2) * rot
);
121 pts
.push_back (Offset (width
, -thick
/ 2) * rot
);
122 pts
.push_back (Offset (width
, thick
/ 2) * rot
);
123 pts
.push_back (Offset (0, thick
/ 2) * rot
);
124 return Lookup::round_filled_polygon (pts
, blot
);
128 Lookup::horizontal_line (Interval w
, Real th
)
130 SCM at
= scm_list_n (ly_symbol2scm ("draw-line"),
131 scm_from_double (th
),
132 scm_from_double (w
[LEFT
]),
134 scm_from_double (w
[RIGHT
]),
140 box
[Y_AXIS
] = Interval (-th
/ 2, th
/ 2);
142 return Stencil (box
, at
);
146 Lookup::blank (Box b
)
148 return Stencil (b
, scm_from_locale_string (""));
152 Lookup::filled_box (Box b
)
154 return round_filled_box (b
, 0.0);
160 * __________________________________
165 * |\ _ _ / v \ _ _ /| |
168 * | <------>| | extent
169 * | blot | | (Y_AXIS)
177 * x\_____/______________\_____/|_____v
181 * |<-------------------------->|
182 * Box extent (X_AXIS)
185 Lookup::round_filled_box (Box b
, Real blotdiameter
)
187 if (b
.x ().length () < blotdiameter
)
188 blotdiameter
= b
.x ().length ();
189 if (b
.y ().length () < blotdiameter
)
190 blotdiameter
= b
.y ().length ();
192 SCM at
= (scm_list_n (ly_symbol2scm ("round-filled-box"),
193 scm_from_double (-b
[X_AXIS
][LEFT
]),
194 scm_from_double (b
[X_AXIS
][RIGHT
]),
195 scm_from_double (-b
[Y_AXIS
][DOWN
]),
196 scm_from_double (b
[Y_AXIS
][UP
]),
197 scm_from_double (blotdiameter
),
200 return Stencil (b
, at
);
204 * Create Stencil that represents a filled polygon with round edges.
208 * (a) Only outer (convex) edges are rounded.
210 * (b) This algorithm works as expected only for polygons whose edges
211 * do not intersect. For example, the polygon ((0, 0), (q, 0), (0,
212 * q), (q, q)) has an intersection at point (q/2, q/2) and therefore
213 * will give a strange result. Even non-adjacent edges that just
214 * touch each other will in general not work as expected for non-null
217 * (c) Given a polygon ((x0, y0), (x1, y1), ... , (x (n-1), y (n-1))),
218 * if there is a natural number k such that blotdiameter is greater
219 * than the maximum of { | (x (k mod n), y (k mod n)) - (x ((k+1) mod n),
220 * y ((k+1) mod n)) |, | (x (k mod n), y (k mod n)) - (x ((k+2) mod n),
221 * y ((k+2) mod n)) |, | (x ((k+1) mod n), y ((k+1) mod n)) - (x ((k+2)
222 * mod n), y ((k+2) mod n)) | }, then the outline of the rounded
223 * polygon will exceed the outline of the core polygon. In other
224 * words: Do not draw rounded polygons that have a leg smaller or
225 * thinner than blotdiameter (or set blotdiameter to a sufficiently
226 * small value -- maybe even 0.0)!
228 * NOTE: Limitations (b) and (c) arise from the fact that round edges
229 * are made by moulding sharp edges to round ones rather than adding
230 * to a core filled polygon. For details of these two different
231 * approaches, see the thread upon the ledger lines patch that started
232 * on March 25, 2002 on the devel mailing list. The below version of
233 * round_filled_polygon () sticks to the moulding model, which the
234 * majority of the list participants finally voted for. This,
235 * however, results in the above limitations and a much increased
236 * complexity of the algorithm, since it has to compute a shrinked
237 * polygon -- which is not trivial define precisely and unambigously.
238 * With the other approach, one simply could move a circle of size
239 * blotdiameter along all edges of the polygon (which is what the
240 * postscript routine in the backend effectively does, but on the
241 * shrinked polygon). --jr
244 Lookup::round_filled_polygon (vector
<Offset
> const &points
,
247 /* TODO: Maybe print a warning if one of the above limitations
248 applies to the given polygon. However, this is quite complicated
251 const Real epsilon
= 0.01;
254 /* remove consecutive duplicate points */
255 for (vsize i
= 0; i
< points
.size (); i
++)
257 int next
= (i
+ 1) % points
.size ();
258 Real d
= (points
[i
] - points
[next
]).length ();
260 programming_error ("Polygon should not have duplicate points");
264 /* special cases: degenerated polygons */
265 if (points
.size () == 0)
267 if (points
.size () == 1)
268 return dot (points
[0], 0.5 * blotdiameter
);
269 if (points
.size () == 2)
270 return Line_interface::make_line (blotdiameter
, points
[0], points
[1]);
272 /* shrink polygon in size by 0.5 * blotdiameter */
273 vector
<Offset
> shrunk_points
;
274 shrunk_points
.resize (points
.size ());
275 bool ccw
= 1; // true, if three adjacent points are counterclockwise ordered
276 for (vsize i
= 0; i
< points
.size (); i
++)
279 int i1
= (i
+ 1) % points
.size ();
280 int i2
= (i
+ 2) % points
.size ();
281 Offset p0
= points
[i0
];
282 Offset p1
= points
[i1
];
283 Offset p2
= points
[i2
];
284 Offset p10
= p0
- p1
;
285 Offset p12
= p2
- p1
;
286 if (p10
.length () != 0.0)
288 Real phi
= p10
.arg ();
289 // rotate (p2 - p0) by (-phi)
290 Offset q
= complex_multiply (p2
- p0
, complex_exp (Offset (1.0, -phi
)));
294 else if (q
[Y_AXIS
] < 0)
296 else {} // keep ccw unchanged
298 else {} // keep ccw unchanged
299 Offset p10n
= (1.0 / p10
.length ()) * p10
; // normalize length to 1.0
300 Offset p12n
= (1.0 / p12
.length ()) * p12
;
301 Offset p13n
= 0.5 * (p10n
+ p12n
);
302 Offset p14n
= 0.5 * (p10n
- p12n
);
304 Real d
= p13n
.length () * p14n
.length (); // distance p3n to line (p1..p0)
306 // special case: p0, p1, p2 are on a single line => build
307 // vector orthogonal to (p2-p0) of length 0.5 blotdiameter
309 p13
[X_AXIS
] = p10
[Y_AXIS
];
310 p13
[Y_AXIS
] = -p10
[X_AXIS
];
311 p13
= (0.5 * blotdiameter
/ p13
.length ()) * p13
;
314 p13
= (0.5 * blotdiameter
/ d
) * p13n
;
315 shrunk_points
[i1
] = p1
+ ((ccw
) ? p13
: -p13
);
318 /* build scm expression and bounding box */
319 SCM shrunk_points_scm
= SCM_EOL
;
321 for (vsize i
= 0; i
< shrunk_points
.size (); i
++)
323 SCM x
= scm_from_double (shrunk_points
[i
][X_AXIS
]);
324 SCM y
= scm_from_double (shrunk_points
[i
][Y_AXIS
]);
325 shrunk_points_scm
= scm_cons (x
, scm_cons (y
, shrunk_points_scm
));
326 box
.add_point (points
[i
]);
328 SCM polygon_scm
= scm_list_n (ly_symbol2scm ("polygon"),
329 ly_quote_scm (shrunk_points_scm
),
330 scm_from_double (blotdiameter
),
334 Stencil polygon
= Stencil (box
, polygon_scm
);
335 shrunk_points
.clear ();
343 Lookup::frame (Box b
, Real thick
, Real blot
)
347 for (Axis a
= X_AXIS
; a
< NO_AXES
; a
= Axis (a
+ 1))
349 Axis o
= Axis ((a
+ 1)%NO_AXES
);
353 edges
[a
] = b
[a
][d
] + 0.5 * thick
* Interval (-1, 1);
354 edges
[o
][DOWN
] = b
[o
][DOWN
] - thick
/ 2;
355 edges
[o
][UP
] = b
[o
][UP
] + thick
/ 2;
357 m
.add_stencil (round_filled_box (edges
, blot
));
359 while (flip (&d
) != LEFT
);
365 Make a smooth curve along the points
368 Lookup::slur (Bezier curve
, Real curvethick
, Real linethick
)
370 Real alpha
= (curve
.control_
[3] - curve
.control_
[0]).arg ();
372 Offset perp
= curvethick
* complex_exp (Offset (0, alpha
+ M_PI
/ 2)) * 0.5;
374 back
.control_
[1] += perp
;
375 back
.control_
[2] += perp
;
377 curve
.control_
[1] -= perp
;
378 curve
.control_
[2] -= perp
;
382 for (int i
= 0; i
< 4; i
++)
383 scontrols
[i
] = ly_offset2scm (back
.control_
[i
]);
384 for (int i
= 0; i
< 4; i
++)
385 scontrols
[i
+ 4] = ly_offset2scm (curve
.control_
[i
]);
388 Need the weird order b.o. the way PS want its arguments
390 int indices
[] = {5, 6, 7, 4, 1, 2, 3, 0};
392 for (int i
= 8; i
--;)
393 list
= scm_cons (scontrols
[indices
[i
]], list
);
395 SCM at
= (scm_list_n (ly_symbol2scm ("bezier-sandwich"),
397 scm_from_double (linethick
),
399 Box
b (curve
.extent (X_AXIS
),
400 curve
.extent (Y_AXIS
));
402 b
[X_AXIS
].unite (back
.extent (X_AXIS
));
403 b
[Y_AXIS
].unite (back
.extent (Y_AXIS
));
405 b
.widen (0.5 * linethick
, 0.5 * linethick
);
406 return Stencil (b
, at
);
433 Lookup::bezier_sandwich (Bezier top_curve
, Bezier bottom_curve
)
436 Need the weird order b.o. the way PS want its arguments
439 list
= scm_cons (ly_offset2scm (bottom_curve
.control_
[3]), list
);
440 list
= scm_cons (ly_offset2scm (bottom_curve
.control_
[0]), list
);
441 list
= scm_cons (ly_offset2scm (bottom_curve
.control_
[1]), list
);
442 list
= scm_cons (ly_offset2scm (bottom_curve
.control_
[2]), list
);
443 list
= scm_cons (ly_offset2scm (top_curve
.control_
[0]), list
);
444 list
= scm_cons (ly_offset2scm (top_curve
.control_
[3]), list
);
445 list
= scm_cons (ly_offset2scm (top_curve
.control_
[2]), list
);
446 list
= scm_cons (ly_offset2scm (top_curve
.control_
[1]), list
);
448 SCM horizontal_bend
= scm_list_n (ly_symbol2scm ("bezier-sandwich"),
450 scm_from_double (0.0),
453 Interval x_extent
= top_curve
.extent (X_AXIS
);
454 x_extent
.unite (bottom_curve
.extent (X_AXIS
));
455 Interval y_extent
= top_curve
.extent (Y_AXIS
);
456 y_extent
.unite (bottom_curve
.extent (Y_AXIS
));
457 Box
b (x_extent
, y_extent
);
459 return Stencil (b
, horizontal_bend
);
463 Lookup::repeat_slash (Real w
, Real s
, Real t
)
466 vector
<Offset
> points
;
467 Real blotdiameter
= 0.0;
470 Offset
p2 (w
, w
* s
);
472 return Lookup::round_filled_polygon (points
, blotdiameter
);
475 SCM wid
= scm_from_double (w
);
476 SCM sl
= scm_from_double (s
);
477 SCM thick
= scm_from_double (t
);
478 SCM slashnodot
= scm_list_n (ly_symbol2scm ("repeat-slash"),
479 wid
, sl
, thick
, SCM_UNDEFINED
);
481 Box
b (Interval (0, w
+ sqrt (sqr (t
/ s
) + sqr (t
))),
482 Interval (0, w
* s
));
484 return Stencil (b
, slashnodot
); // http://slashnodot.org
488 Lookup::bracket (Axis a
, Interval iv
, Real thick
, Real protrude
, Real blot
)
491 Axis other
= Axis ((a
+ 1)%2);
493 b
[other
] = Interval (-1, 1) * thick
* 0.5;
495 Stencil m
= round_filled_box (b
, blot
);
497 b
[a
] = Interval (iv
[UP
] - thick
, iv
[UP
]);
498 Interval oi
= Interval (-thick
/ 2, thick
/ 2 + fabs (protrude
));
499 oi
*= sign (protrude
);
501 m
.add_stencil (round_filled_box (b
, blot
));
502 b
[a
] = Interval (iv
[DOWN
], iv
[DOWN
] + thick
);
503 m
.add_stencil (round_filled_box (b
, blot
));
509 Lookup::triangle (Interval iv
, Real thick
, Real protrude
)
512 b
[X_AXIS
] = Interval (0, iv
.length ());
513 b
[Y_AXIS
] = Interval (min (0., protrude
), max (0.0, protrude
));
515 vector
<Offset
> points
;
516 points
.push_back (Offset (iv
[LEFT
], 0));
517 points
.push_back (Offset (iv
[RIGHT
], 0));
518 points
.push_back (Offset (iv
.center (), protrude
));
520 return points_to_line_stencil (thick
, points
);
527 Lookup::points_to_line_stencil (Real thick
, vector
<Offset
> const &points
)
530 for (vsize i
= 1; i
< points
.size (); i
++)
532 if (points
[i
-1].is_sane () && points
[i
].is_sane ())
535 = Line_interface::make_line (thick
, points
[i
-1], points
[i
]);
536 ret
.add_stencil (line
);