author | paulson <lp15@cam.ac.uk> |
Tue, 12 Nov 2019 12:33:05 +0000 | |
changeset 71096 | ec7cc76e88e5 |
parent 67547 | aefe7a7b330a |
child 71601 | 97ccf48c2f0c |
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:
49557
diff
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:
50471
diff
changeset
|
21 |
|
59302
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
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:
59294
diff
changeset
|
28 |
/* layout state */ |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
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:
59294
diff
changeset
|
35 |
def visible_graph: Graph_Display.Graph = layout.input_graph |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
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:
59294
diff
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:
59294
diff
changeset
|
50 |
def bounding_box(): Rectangle2D.Double = |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
51 |
{ |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
52 |
var x0 = 0.0 |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
53 |
var y0 = 0.0 |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
54 |
var x1 = 0.0 |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
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:
59294
diff
changeset
|
67 |
new Rectangle2D.Double(x0, y0, x1 - x0, y1 - y0) |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
68 |
} |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
69 |
|
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
70 |
def update_layout() |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
71 |
{ |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
72 |
val metrics = Metrics(make_font()) |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
73 |
val visible_graph = model.make_visible_graph() |
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
74 |
|
59384 | 75 |
def node_text(node: Graph_Display.Node, content: XML.Body): String = |
76 |
if (show_content) { |
|
77 |
val s = |
|
67547
aefe7a7b330a
clarified breakgain: keeping it constant avoids margin fluctuation in Pretty_Tooltip vs. Pretty_Text_Area;
wenzelm
parents:
61590
diff
changeset
|
78 |
XML.content(Pretty.formatted(content, |
aefe7a7b330a
clarified breakgain: keeping it constant avoids margin fluctuation in Pretty_Tooltip vs. Pretty_Text_Area;
wenzelm
parents:
61590
diff
changeset
|
79 |
margin = options.int("graphview_content_margin").toDouble, |
aefe7a7b330a
clarified breakgain: keeping it constant avoids margin fluctuation in Pretty_Tooltip vs. Pretty_Text_Area;
wenzelm
parents:
61590
diff
changeset
|
80 |
metric = metrics.Pretty_Metric)) |
59384 | 81 |
if (s.nonEmpty) s else node.toString |
82 |
} |
|
83 |
else node.toString |
|
84 |
||
85 |
_layout = Layout.make(options, metrics, node_text _, visible_graph) |
|
59302
4d985afc0565
explict layout graph structure, with dummies and coordinates;
wenzelm
parents:
59294
diff
changeset
|
86 |
} |
59290 | 87 |
|
50476
1cb983bccb5b
more official graphics context with font metrics;
wenzelm
parents:
50475
diff
changeset
|
88 |
|
59233 | 89 |
/* tooltips */ |
90 |
||
91 |
def make_tooltip(parent: JComponent, x: Int, y: Int, body: XML.Body): String = null |
|
92 |
||
93 |
||
59228
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
94 |
/* main colors */ |
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
95 |
|
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
96 |
def foreground_color: Color = Color.BLACK |
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
97 |
def background_color: Color = Color.WHITE |
61176 | 98 |
def selection_color: Color = new Color(204, 204, 255) |
99 |
def highlight_color: Color = new Color(255, 255, 224) |
|
59228
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
100 |
def error_color: Color = Color.RED |
59251 | 101 |
def dummy_color: Color = Color.GRAY |
59228
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
102 |
|
56b34fc7a015
more dynamic visualizer -- re-use Isabelle/jEdit options;
wenzelm
parents:
59220
diff
changeset
|
103 |
|
50475 | 104 |
/* font rendering information */ |
105 |
||
59290 | 106 |
def make_font(): Font = |
59286
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
wenzelm
parents:
59265
diff
changeset
|
107 |
{ |
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
wenzelm
parents:
59265
diff
changeset
|
108 |
val family = options.string("graphview_font_family") |
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
wenzelm
parents:
59265
diff
changeset
|
109 |
val size = options.int("graphview_font_size") |
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
wenzelm
parents:
59265
diff
changeset
|
110 |
new Font(family, Font.PLAIN, size) |
ac74eedb910a
GUI.imitate_font: more explicit result size, e.g. relevant for caching;
wenzelm
parents:
59265
diff
changeset
|
111 |
} |
50475 | 112 |
|
113 |
||
114 |
/* rendering parameters */ |
|
115 |
||
59294 | 116 |
// owned by GUI thread |
59384 | 117 |
var show_content = false |
118 |
var show_arrow_heads = false |
|
59294 | 119 |
var show_dummies = false |
61176 | 120 |
var editor_style = false |
50475 | 121 |
|
122 |
object Colors |
|
123 |
{ |
|
124 |
private val filter_colors = List( |
|
59220 | 125 |
new Color(0xD9, 0xF2, 0xE2), // blue |
126 |
new Color(0xFF, 0xE7, 0xD8), // orange |
|
127 |
new Color(0xFF, 0xFF, 0xE5), // yellow |
|
128 |
new Color(0xDE, 0xCE, 0xFF), // lilac |
|
129 |
new Color(0xCC, 0xEB, 0xFF), // turquoise |
|
130 |
new Color(0xFF, 0xE5, 0xE5), // red |
|
131 |
new Color(0xE5, 0xE5, 0xD9) // green |
|
50475 | 132 |
) |
133 |
||
134 |
private var curr : Int = -1 |
|
59220 | 135 |
def next(): Color = |
50475 | 136 |
{ |
137 |
curr = (curr + 1) % filter_colors.length |
|
138 |
filter_colors(curr) |
|
139 |
} |
|
140 |
} |
|
141 |
||
59460 | 142 |
def paint(gfx: Graphics2D) |
59294 | 143 |
{ |
144 |
gfx.setRenderingHints(Metrics.rendering_hints) |
|
59410 | 145 |
|
59459 | 146 |
for (node <- graphview.current_node) |
147 |
Shapes.highlight_node(gfx, graphview, node) |
|
59410 | 148 |
|
59294 | 149 |
for (edge <- visible_graph.edges_iterator) |
59459 | 150 |
Shapes.Cardinal_Spline_Edge.paint(gfx, graphview, edge) |
59410 | 151 |
|
59294 | 152 |
for (node <- visible_graph.keys_iterator) |
59459 | 153 |
Shapes.paint_node(gfx, graphview, node) |
59294 | 154 |
} |
50472
bad1a1ca61e1
separate instance of class Parameters for each Main_Panel -- avoid global program state;
wenzelm
parents:
50471
diff
changeset
|
155 |
|
59392 | 156 |
var current_node: Option[Graph_Display.Node] = None |
157 |
||
50465 | 158 |
object Selection |
159 |
{ |
|
61590 | 160 |
private val state = Synchronized[List[Graph_Display.Node]](Nil) |
50465 | 161 |
|
59443
5b552b4f63a5
support for off-line graph output, without GUI thread;
wenzelm
parents:
59410
diff
changeset
|
162 |
def get(): List[Graph_Display.Node] = state.value |
59245
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
wenzelm
parents:
59242
diff
changeset
|
163 |
def contains(node: Graph_Display.Node): Boolean = get().contains(node) |
50465 | 164 |
|
59443
5b552b4f63a5
support for off-line graph output, without GUI thread;
wenzelm
parents:
59410
diff
changeset
|
165 |
def add(node: Graph_Display.Node): Unit = state.change(node :: _) |
5b552b4f63a5
support for off-line graph output, without GUI thread;
wenzelm
parents:
59410
diff
changeset
|
166 |
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
|
167 |
} |
50465 | 168 |
|
59220 | 169 |
sealed case class Node_Color(border: Color, background: Color, foreground: Color) |
50464 | 170 |
|
59245
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
wenzelm
parents:
59242
diff
changeset
|
171 |
def node_color(node: Graph_Display.Node): Node_Color = |
59410 | 172 |
if (Selection.contains(node)) |
59245
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
wenzelm
parents:
59242
diff
changeset
|
173 |
Node_Color(foreground_color, selection_color, foreground_color) |
59410 | 174 |
else if (current_node == Some(node)) |
175 |
Node_Color(foreground_color, highlight_color, foreground_color) |
|
59245
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
wenzelm
parents:
59242
diff
changeset
|
176 |
else |
be4180f3c236
more formal Graph_Display.Node (with ordering) and Graph_Display.Edge;
wenzelm
parents:
59242
diff
changeset
|
177 |
Node_Color(foreground_color, model.colors.getOrElse(node, background_color), foreground_color) |
59220 | 178 |
|
59407 | 179 |
def edge_color(edge: Graph_Display.Edge, downwards: Boolean): Color = |
180 |
if (downwards || show_arrow_heads) foreground_color |
|
181 |
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
|
182 |
} |