| author | blanchet | 
| Thu, 25 Jun 2015 12:41:43 +0200 | |
| changeset 60568 | a9b71c82647b | 
| parent 59462 | c7eff4356885 | 
| child 61176 | 9791f631c20d | 
| permissions | -rw-r--r-- | 
| 59459 | 1 | /* Title: Tools/Graphview/graphview.scala | 
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 2 | Author: Markus Kaiser, TU Muenchen | 
| 59240 | 3 | Author: Makarius | 
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 4 | |
| 59459 | 5 | Graphview visualization parameters and GUI state. | 
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 6 | */ | 
| 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 7 | |
| 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 8 | package isabelle.graphview | 
| 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 9 | |
| 50464 | 10 | |
| 49565 
ea4308b7ef0f
ML support for generic graph display, with browser and graphview backends (via print modes);
 wenzelm parents: 
49557diff
changeset | 11 | import isabelle._ | 
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 12 | |
| 59290 | 13 | import java.awt.{Font, Color, Shape, Graphics2D}
 | 
| 59305 | 14 | import java.awt.geom.{Point2D, Rectangle2D}
 | 
| 49729 | 15 | import javax.swing.JComponent | 
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 16 | |
| 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 17 | |
| 59462 | 18 | abstract class Graphview(full_graph: Graph_Display.Graph) | 
| 50465 | 19 | {
 | 
| 59459 | 20 | graphview => | 
| 50472 
bad1a1ca61e1
separate instance of class Parameters for each Main_Panel -- avoid global program state;
 wenzelm parents: 
50471diff
changeset | 21 | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 22 | |
| 59395 | 23 | def options: Options | 
| 59392 | 24 | |
| 59462 | 25 | val model = new Model(full_graph) | 
| 26 | ||
| 59392 | 27 | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 28 | /* layout state */ | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 29 | |
| 59290 | 30 | // owned by GUI thread | 
| 59303 | 31 | private var _layout: Layout = Layout.empty | 
| 32 | def layout: Layout = _layout | |
| 59290 | 33 | |
| 34 | def metrics: Metrics = layout.metrics | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 35 | def visible_graph: Graph_Display.Graph = layout.input_graph | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 36 | |
| 59303 | 37 | def translate_vertex(v: Layout.Vertex, dx: Double, dy: Double): Unit = | 
| 38 | _layout = _layout.translate_vertex(v, dx, dy) | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 39 | |
| 59305 | 40 | def find_node(at: Point2D): Option[Graph_Display.Node] = | 
| 41 |     layout.output_graph.iterator.collectFirst({
 | |
| 59384 | 42 | case (Layout.Node(node), (info, _)) if Shapes.shape(info).contains(at) => node | 
| 59305 | 43 | }) | 
| 44 | ||
| 45 | def find_dummy(at: Point2D): Option[Layout.Dummy] = | |
| 46 |     layout.output_graph.iterator.collectFirst({
 | |
| 59384 | 47 | case (dummy: Layout.Dummy, (info, _)) if Shapes.shape(info).contains(at) => dummy | 
| 59305 | 48 | }) | 
| 49 | ||
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 50 | def bounding_box(): Rectangle2D.Double = | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 51 |   {
 | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 52 | var x0 = 0.0 | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 53 | var y0 = 0.0 | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 54 | var x1 = 0.0 | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 55 | var y1 = 0.0 | 
| 59384 | 56 |     for ((_, (info, _)) <- layout.output_graph.iterator) {
 | 
| 57 | val rect = Shapes.shape(info) | |
| 58 | x0 = x0 min rect.getMinX | |
| 59 | y0 = y0 min rect.getMinY | |
| 60 | x1 = x1 max rect.getMaxX | |
| 61 | y1 = y1 max rect.getMaxY | |
| 62 | } | |
| 63 | x0 = (x0 - metrics.gap).floor | |
| 64 | y0 = (y0 - metrics.gap).floor | |
| 65 | x1 = (x1 + metrics.gap).ceil | |
| 66 | y1 = (y1 + metrics.gap).ceil | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 67 | new Rectangle2D.Double(x0, y0, x1 - x0, y1 - y0) | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 68 | } | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 69 | |
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 70 | def update_layout() | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 71 |   {
 | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 72 | val metrics = Metrics(make_font()) | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 73 | val visible_graph = model.make_visible_graph() | 
| 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 74 | |
| 59384 | 75 | def node_text(node: Graph_Display.Node, content: XML.Body): String = | 
| 76 |       if (show_content) {
 | |
| 77 | val s = | |
| 78 | XML.content(Pretty.formatted( | |
| 79 |             content, options.int("graphview_content_margin").toDouble, metrics.Pretty_Metric))
 | |
| 80 | if (s.nonEmpty) s else node.toString | |
| 81 | } | |
| 82 | else node.toString | |
| 83 | ||
| 84 | _layout = Layout.make(options, metrics, node_text _, visible_graph) | |
| 59302 
4d985afc0565
explict layout graph structure, with dummies and coordinates;
 wenzelm parents: 
59294diff
changeset | 85 | } | 
| 59290 | 86 | |
| 50476 
1cb983bccb5b
more official graphics context with font metrics;
 wenzelm parents: 
50475diff
changeset | 87 | |
| 59233 | 88 | /* tooltips */ | 
| 89 | ||
| 90 | def make_tooltip(parent: JComponent, x: Int, y: Int, body: XML.Body): String = null | |
| 91 | ||
| 92 | ||
| 59228 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 93 | /* main colors */ | 
| 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 94 | |
| 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 95 | def foreground_color: Color = Color.BLACK | 
| 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 96 | def background_color: Color = Color.WHITE | 
| 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 97 | def selection_color: Color = Color.GREEN | 
| 59410 | 98 | def highlight_color: Color = Color.YELLOW | 
| 59228 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 99 | def error_color: Color = Color.RED | 
| 59251 | 100 | def dummy_color: Color = Color.GRAY | 
| 59228 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 101 | |
| 
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
 wenzelm parents: 
59220diff
changeset | 102 | |
| 50475 | 103 | /* font rendering information */ | 
| 104 | ||
| 59290 | 105 | def make_font(): Font = | 
| 59286 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59265diff
changeset | 106 |   {
 | 
| 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59265diff
changeset | 107 |     val family = options.string("graphview_font_family")
 | 
| 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59265diff
changeset | 108 |     val size = options.int("graphview_font_size")
 | 
| 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59265diff
changeset | 109 | new Font(family, Font.PLAIN, size) | 
| 
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
 wenzelm parents: 
59265diff
changeset | 110 | } | 
| 50475 | 111 | |
| 112 | ||
| 113 | /* rendering parameters */ | |
| 114 | ||
| 59294 | 115 | // owned by GUI thread | 
| 59384 | 116 | var show_content = false | 
| 117 | var show_arrow_heads = false | |
| 59294 | 118 | var show_dummies = false | 
| 50475 | 119 | |
| 120 | object Colors | |
| 121 |   {
 | |
| 122 | private val filter_colors = List( | |
| 59220 | 123 | new Color(0xD9, 0xF2, 0xE2), // blue | 
| 124 | new Color(0xFF, 0xE7, 0xD8), // orange | |
| 125 | new Color(0xFF, 0xFF, 0xE5), // yellow | |
| 126 | new Color(0xDE, 0xCE, 0xFF), // lilac | |
| 127 | new Color(0xCC, 0xEB, 0xFF), // turquoise | |
| 128 | new Color(0xFF, 0xE5, 0xE5), // red | |
| 129 | new Color(0xE5, 0xE5, 0xD9) // green | |
| 50475 | 130 | ) | 
| 131 | ||
| 132 | private var curr : Int = -1 | |
| 59220 | 133 | def next(): Color = | 
| 50475 | 134 |     {
 | 
| 135 | curr = (curr + 1) % filter_colors.length | |
| 136 | filter_colors(curr) | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 59460 | 140 | def paint(gfx: Graphics2D) | 
| 59294 | 141 |   {
 | 
| 142 | gfx.setRenderingHints(Metrics.rendering_hints) | |
| 59410 | 143 | |
| 59459 | 144 | for (node <- graphview.current_node) | 
| 145 | Shapes.highlight_node(gfx, graphview, node) | |
| 59410 | 146 | |
| 59294 | 147 | for (edge <- visible_graph.edges_iterator) | 
| 59459 | 148 | Shapes.Cardinal_Spline_Edge.paint(gfx, graphview, edge) | 
| 59410 | 149 | |
| 59294 | 150 | for (node <- visible_graph.keys_iterator) | 
| 59459 | 151 | Shapes.paint_node(gfx, graphview, node) | 
| 59294 | 152 | } | 
| 50472 
bad1a1ca61e1
separate instance of class Parameters for each Main_Panel -- avoid global program state;
 wenzelm parents: 
50471diff
changeset | 153 | |
| 59392 | 154 | var current_node: Option[Graph_Display.Node] = None | 
| 155 | ||
| 50465 | 156 | object Selection | 
| 157 |   {
 | |
| 59443 
5b552b4f63a5
support for off-line graph output, without GUI thread;
 wenzelm parents: 
59410diff
changeset | 158 | private val state = Synchronized(List.empty[Graph_Display.Node]) | 
| 50465 | 159 | |
| 59443 
5b552b4f63a5
support for off-line graph output, without GUI thread;
 wenzelm parents: 
59410diff
changeset | 160 | def get(): List[Graph_Display.Node] = state.value | 
| 59245 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 wenzelm parents: 
59242diff
changeset | 161 | def contains(node: Graph_Display.Node): Boolean = get().contains(node) | 
| 50465 | 162 | |
| 59443 
5b552b4f63a5
support for off-line graph output, without GUI thread;
 wenzelm parents: 
59410diff
changeset | 163 | def add(node: Graph_Display.Node): Unit = state.change(node :: _) | 
| 
5b552b4f63a5
support for off-line graph output, without GUI thread;
 wenzelm parents: 
59410diff
changeset | 164 | def clear(): Unit = state.change(_ => Nil) | 
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 165 | } | 
| 50465 | 166 | |
| 59220 | 167 | sealed case class Node_Color(border: Color, background: Color, foreground: Color) | 
| 50464 | 168 | |
| 59245 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 wenzelm parents: 
59242diff
changeset | 169 | def node_color(node: Graph_Display.Node): Node_Color = | 
| 59410 | 170 | if (Selection.contains(node)) | 
| 59245 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 wenzelm parents: 
59242diff
changeset | 171 | Node_Color(foreground_color, selection_color, foreground_color) | 
| 59410 | 172 | else if (current_node == Some(node)) | 
| 173 | Node_Color(foreground_color, highlight_color, foreground_color) | |
| 59245 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 wenzelm parents: 
59242diff
changeset | 174 | else | 
| 
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
 wenzelm parents: 
59242diff
changeset | 175 | Node_Color(foreground_color, model.colors.getOrElse(node, background_color), foreground_color) | 
| 59220 | 176 | |
| 59407 | 177 | def edge_color(edge: Graph_Display.Edge, downwards: Boolean): Color = | 
| 178 | if (downwards || show_arrow_heads) foreground_color | |
| 179 | else error_color | |
| 49557 
61988f9df94d
added Graphview tool, based on Isabelle/Scala and Swing/Graphics2D;
 Markus Kaiser <markus.kaiser@in.tum.de> parents: diff
changeset | 180 | } |