1+ using Compose, Viznet, Cairo
2+
3+ struct Rescaler{T}
4+ xmin:: T
5+ xmax:: T
6+ ymin:: T
7+ ymax:: T
8+ pad:: T
9+ end
10+
11+ getscale (r:: Rescaler ) = min (1 / (r. xmax- r. xmin+ 2 * r. pad), 1 / (r. ymax- r. ymin+ 2 * r. pad))
12+
13+ function config_plotting (sites)
14+ n = length (sites)
15+ if n <= 1
16+ return (1.0 , 0.5 , 0.4 , 1.0 )
17+ end
18+ shortest_distance = Inf
19+ for i= 1 : n
20+ for j= i+ 1 : n
21+ shortest_distance = min (sqrt (sum (abs2, sites[i] .- sites[j])), shortest_distance)
22+ end
23+ end
24+
25+ rescaler = get_rescaler (sites, 0.0 )
26+ xpad = (rescaler. xmax - rescaler. xmin) * 0.2 + shortest_distance
27+ ypad = (rescaler. ymax - rescaler. ymin) * 0.2 + shortest_distance
28+ pad = max (xpad, ypad)
29+ scale = shortest_distance
30+ return (pad= pad, scale= scale)
31+ end
32+
33+ function (r:: Rescaler{T} )(x; dims= (1 ,2 )) where T
34+ xmin, ymin, xmax, ymax, pad = r. xmin, r. ymin, r. xmax, r. ymax, r. pad
35+ scale = getscale (r)
36+ if dims == (1 ,2 )
37+ return (x[1 ]- xmin+ pad, ymax+ pad- x[2 ]) .* scale
38+ elseif dims == 1
39+ return (x - xmin + pad) * scale
40+ elseif dims == 2
41+ return (ymax + pad - x) * scale
42+ else
43+ throw (ArgumentError (" dims should be (1,2), 1 or 2." ))
44+ end
45+ end
46+
47+ function get_rescaler (locs:: AbstractVector{<:Tuple} , pad)
48+ xmin = minimum (x-> x[1 ], locs)
49+ ymin = minimum (x-> x[2 ], locs)
50+ xmax = maximum (x-> x[1 ], locs)
51+ ymax = maximum (x-> x[2 ], locs)
52+ return Rescaler (promote (xmin, xmax, ymin, ymax, pad)... )
53+ end
54+
55+ default_node_style (scale, stroke_color, fill_color) = compose (context (), Viznet. nodestyle (:default , r= 0.15 cm* scale), Compose. stroke (stroke_color), fill (fill_color), linewidth (0.3 mm* scale))
56+ default_text_style (scale, color) = Viznet. textstyle (:default , fontsize (4 pt* scale), fill (color))
57+ default_bond_style (scale, color) = Viznet. bondstyle (:default , Compose. stroke (color), linewidth (0.3 mm* scale))
58+
59+ """
60+ show_graph(locations, edges;
61+ colors=["black", "black", ...],
62+ texts=["1", "2", ...],
63+ format=SVG,
64+ bond_color="black",
65+ )
66+
67+ Plots vertices at `locations` with colors specified by `colors` and texts specified by `texts`.
68+ You will need a `VSCode`, `Pluto` notebook or `Jupyter` notebook to show the image.
69+ If you want to write this image to the disk without displaying it in a frontend, please try
70+
71+ ```julia
72+ julia> open("test.png", "w") do f
73+ viz_atoms(f, generate_sites(SquareLattice(), 5, 5))
74+ end
75+ ```
76+
77+ The `format` keyword argument can also be `Compose.SVG` or `Compose.PDF`.
78+ """
79+ function show_graph (locations, edges;
80+ colors= nothing ,
81+ texts = nothing ,
82+ format= SVG, io= nothing ,
83+ kwargs... )
84+ if length (locations) == 0
85+ dx, dy = 12 cm, 12 cm
86+ img = Compose. compose (context ())
87+ else
88+ img, (dx, dy) = viz_atoms (locations, edges; colors= colors, texts= texts, config= GraphDisplayConfig (; config_plotting (locations)... , kwargs... ))
89+ end
90+ if io === nothing
91+ Compose. set_default_graphic_size (dx, dy)
92+ return img
93+ else
94+ return format (io, dx, dy)(img)
95+ end
96+ end
97+ function show_graph (graph:: SimpleGraph ; locs, kwargs... )
98+ show_graph (locs, [(e. src, e. dst) for e in edges (graph)]; kwargs... )
99+ end
100+
101+ function fit_image (rescaler:: Rescaler , image_size, imgs... )
102+ X = rescaler. xmax - rescaler. xmin + 2 * rescaler. pad
103+ Y = rescaler. ymax - rescaler. ymin + 2 * rescaler. pad
104+ img_rescale = image_size/ max (X, Y)* cm
105+ if Y < X
106+ return Compose. compose (context (0 , 0 , 1.0 , X/ Y), imgs... ), (X* img_rescale, Y* img_rescale)
107+ else
108+ return Compose. compose (context (0 , 0 , Y/ X, 1.0 ), imgs... ), (X* img_rescale, Y* img_rescale)
109+ end
110+ end
111+
112+ # Returns a 2-tuple of (image::Context, size)
113+ function viz_atoms (locs, edges; colors, texts, config)
114+ rescaler = get_rescaler (locs, config. pad)
115+ img = _viz_atoms (rescaler .(locs), edges, colors, texts, config, getscale (rescaler))
116+ return fit_image (rescaler, config. image_size, img)
117+ end
118+
119+ Base. @kwdef struct GraphDisplayConfig
120+ # line, node and text
121+ scale:: Float64 = 1.0
122+ pad:: Float64 = 1.5
123+
124+ # node
125+ node_text_color:: String = " black"
126+ node_stroke_color = " black"
127+ node_fill_color = " white"
128+ # bond
129+ bond_color:: String = " black"
130+ # image size in cm
131+ image_size:: Float64 = 12
132+ end
133+
134+ function _viz_atoms (locs, edges, colors, texts, config, rescale)
135+ rescale = rescale * config. image_size * config. scale * 1.6
136+ if colors != = nothing
137+ @assert length (locs) == length (colors)
138+ node_styles = [default_node_style (rescale, config. node_stroke_color, color) for color in colors]
139+ else
140+ node_styles = fill (default_node_style (rescale, config. node_stroke_color, config. node_fill_color), length (locs))
141+ end
142+ if texts != = nothing
143+ @assert length (locs) == length (texts)
144+ end
145+ edge_style = default_bond_style (rescale, config. bond_color)
146+ text_style = default_text_style (rescale, config. node_text_color)
147+ img1 = Viznet. canvas () do
148+ for (i, node) in enumerate (locs)
149+ node_styles[i] >> node
150+ if config. node_text_color != = " transparent"
151+ text_style >> (node, texts === nothing ? " $i " : texts[i])
152+ end
153+ end
154+ for (i, j) in edges
155+ edge_style >> (locs[i], locs[j])
156+ end
157+ end
158+ Compose. compose (context (), img1)
159+ end
0 commit comments