| author | wenzelm | 
| Sun, 03 Sep 2023 16:44:07 +0200 | |
| changeset 78639 | ca56952b7322 | 
| parent 75393 | 87ebf5a50283 | 
| child 81340 | 30f7eb65d679 | 
| permissions | -rw-r--r-- | 
| 59290 | 1 | /* Title: Tools/Graphview/metrics.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Physical metrics for layout and painting. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle.graphview | |
| 8 | ||
| 9 | ||
| 10 | import isabelle._ | |
| 11 | ||
| 73909 | 12 | import java.util.HashMap | 
| 59290 | 13 | import java.awt.{Font, RenderingHints}
 | 
| 14 | import java.awt.font.FontRenderContext | |
| 59384 | 15 | import java.awt.geom.Rectangle2D | 
| 59290 | 16 | |
| 17 | ||
| 75393 | 18 | object Metrics {
 | 
| 19 |   val rendering_hints: RenderingHints = {
 | |
| 73909 | 20 | val hints = new HashMap[RenderingHints.Key, AnyRef] | 
| 59389 
c427f3de9050
prefer fractional font metrics, for proper scaling of node size;
 wenzelm parents: 
59388diff
changeset | 21 | hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) | 
| 
c427f3de9050
prefer fractional font metrics, for proper scaling of node size;
 wenzelm parents: 
59388diff
changeset | 22 | hints.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON) | 
| 
c427f3de9050
prefer fractional font metrics, for proper scaling of node size;
 wenzelm parents: 
59388diff
changeset | 23 | new RenderingHints(hints) | 
| 
c427f3de9050
prefer fractional font metrics, for proper scaling of node size;
 wenzelm parents: 
59388diff
changeset | 24 | } | 
| 59290 | 25 | |
| 26 | val font_render_context: FontRenderContext = | |
| 59389 
c427f3de9050
prefer fractional font metrics, for proper scaling of node size;
 wenzelm parents: 
59388diff
changeset | 27 | new FontRenderContext(null, true, true) | 
| 59290 | 28 | |
| 29 | def apply(font: Font): Metrics = new Metrics(font) | |
| 30 | ||
| 31 |   val default: Metrics = apply(new Font("Helvetica", Font.PLAIN, 12))
 | |
| 32 | } | |
| 33 | ||
| 75393 | 34 | class Metrics private(val font: Font) {
 | 
| 59384 | 35 | def string_bounds(s: String): Rectangle2D = font.getStringBounds(s, Metrics.font_render_context) | 
| 59290 | 36 |   private val mix = string_bounds("mix")
 | 
| 71383 | 37 |   val space_width: Double = string_bounds(" ").getWidth
 | 
| 59290 | 38 | def char_width: Double = mix.getWidth / 3 | 
| 39 | def height: Double = mix.getHeight | |
| 40 |   def ascent: Double = font.getLineMetrics("", Metrics.font_render_context).getAscent
 | |
| 41 | ||
| 59384 | 42 | def gap: Double = mix.getWidth.ceil | 
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59293diff
changeset | 43 | |
| 59388 | 44 | def dummy_size2: Double = (char_width / 2).ceil | 
| 59384 | 45 | |
| 46 | def node_width2(lines: List[String]): Double = | |
| 73359 | 47 |     ((lines.foldLeft(0.0)({ case (w, s) => w max string_bounds(s).getWidth }) + 2 * char_width) / 2)
 | 
| 48 | .ceil | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59293diff
changeset | 49 | |
| 59384 | 50 | def node_height2(num_lines: Int): Double = | 
| 59388 | 51 | ((height.ceil * (num_lines max 1) + char_width) / 2).ceil | 
| 59384 | 52 | |
| 53 | def level_height2(num_lines: Int, num_edges: Int): Double = | |
| 54 | (node_height2(num_lines) + gap) max (node_height2(1) * (num_edges max 5)) | |
| 55 | ||
| 75393 | 56 |   object Pretty_Metric extends Pretty.Metric {
 | 
| 71383 | 57 | val unit: Double = space_width max 1.0 | 
| 59384 | 58 | def apply(s: String): Double = if (s == "\n") 1.0 else string_bounds(s).getWidth / unit | 
| 59 | } | |
| 59290 | 60 | } | 
| 61 |