1+ module TikzGraph
2+ export rgbcolor!, Node, Line, BoundingBox, Mesh, Canvas, >> , command, canvas, generate_standalone, StringElement, PlainText
3+
4+ const instance_counter = Ref (0 )
5+ abstract type AbstractTikzElement end
6+
7+ struct Canvas
8+ header:: String
9+ colors:: Dict{String, Tuple{Int,Int,Int}}
10+ contents:: Vector{AbstractTikzElement}
11+ props:: Dict{String,String}
12+ end
13+
14+ function canvas (f; header= " " , colors= Dict {String,Tuple{Int,Int,Int}} (), props= Dict {String,String} ())
15+ canvas = Canvas (header, colors, AbstractTikzElement[], props)
16+ f (canvas)
17+ return canvas
18+ end
19+
20+ Base.:(>> )(element:: AbstractTikzElement , canvas:: Canvas ) = push! (canvas. contents, element)
21+ Base.:(>> )(element:: String , canvas:: Canvas ) = push! (canvas. contents, StringElement (element))
22+
23+ function rgbcolor! (canvas:: Canvas , red:: Int , green:: Int , blue:: Int )
24+ instance_counter[] += 1
25+ colorname = " color$(instance_counter[]) "
26+ canvas. colors[colorname] = (red,green,blue)
27+ return colorname
28+ end
29+ function generate_rgbcolor (name, red, green, blue)
30+ return " \\ definecolor{$name }{RGB}{$red ,$green ,$blue }"
31+ end
32+
33+ struct StringElement <: AbstractTikzElement
34+ str:: String
35+ end
36+ command (s:: StringElement ) = s. str
37+
38+ struct BoundingBox <: AbstractTikzElement
39+ xmin:: Float64
40+ xmax:: Float64
41+ ymin:: Float64
42+ ymax:: Float64
43+ end
44+ function command (box:: BoundingBox )
45+ return " \\ useasboundingbox ($(box. xmin) ,$(box. ymin) ) rectangle ($(box. xmax- box. xmin) ,$(box. ymax- box. ymin) );"
46+ end
47+
48+ struct Node <: AbstractTikzElement
49+ x:: Float64
50+ y:: Float64
51+ shape:: String
52+ id:: String
53+ text:: String
54+ props:: Dict{String,String}
55+ end
56+
57+ function Node (x, y;
58+ shape:: String = " circle" ,
59+ id = string ((instance_counter[] += 1 ; instance_counter[])),
60+ text:: String = " " ,
61+ fill = " none" ,
62+ draw = " black" ,
63+ inner_sep = " 0cm" ,
64+ minimum_size = " 0.2cm" ,
65+ line_width = 0.03 ,
66+ kwargs... )
67+ props = build_props (;
68+ fill = fill,
69+ draw = draw,
70+ inner_sep = inner_sep,
71+ minimum_size = minimum_size,
72+ line_width = line_width,
73+ kwargs... )
74+ return Node (x, y, shape, id, text, props)
75+ end
76+ function build_props (; kwargs... )
77+ Dict ([replace (string (k), " _" => " " )=> string (v) for (k,v) in kwargs])
78+ end
79+
80+ function command (node:: Node )
81+ return " \\ node[$(node. shape) , $(command (node. props)) ] at ($(node. x) , $(node. y) ) ($(node. id) ) {$(node. text) };"
82+ end
83+
84+ struct Mesh <: AbstractTikzElement
85+ xmin:: Float64
86+ xmax:: Float64
87+ ymin:: Float64
88+ ymax:: Float64
89+ props:: Dict{String,String}
90+ end
91+
92+ function Mesh (xmin, xmax, ymin, ymax; step= " 1.0cm" , draw= " gray" , line_width= 0.03 , kwargs... )
93+ Mesh (xmin, xmax, ymin, ymax, build_props (; step= step, draw= draw, line_width= line_width, kwargs... ))
94+ end
95+ function command (grid:: Mesh )
96+ return " \\ draw[$(command (grid. props)) ] ($(grid. xmin) ,$(grid. ymin) ) grid ($(grid. xmax) ,$(grid. ymax) );"
97+ end
98+
99+ struct Line <: AbstractTikzElement
100+ src:: String
101+ dst:: String
102+ controls:: Vector{Int}
103+ props:: Dict{String,String}
104+ end
105+
106+ function Line (src, dst; controls= Int[], line_width = " 0.03" , kwargs... )
107+ Line (string (src), string (dst), controls, build_props (; line_width= line_width, kwargs... ))
108+ end
109+ Line (src:: Node , dst:: Node ; kwargs... ) = Line (src. id, dst. id)
110+
111+ function command (edge:: Line )
112+ head = " \\ draw[$(command (edge. props)) ]"
113+ if isempty (edge. controls)
114+ return " $head ($(edge. src) ) -- ($(edge. dst) );"
115+ else
116+ return " $head ($(edge. src) ) .. controls $(join ([" ($c )" for c in edge. controls], " and " )) .. ($(edge. dst) );"
117+ end
118+ end
119+
120+ struct PlainText <: AbstractTikzElement
121+ x:: Float64
122+ y:: Float64
123+ text:: String
124+ props:: Dict{String,String}
125+ end
126+ function PlainText (x, y, text; kwargs... )
127+ PlainText (x, y, text, build_props (; kwargs... ))
128+ end
129+ function command (text:: PlainText )
130+ " \\ node[$(command (text. props)) ] at ($(text. x) , $(text. y) ) {$(text. text) };"
131+ end
132+
133+ function command (node:: Dict ) # properties
134+ return join ([" $k =$v " for (k,v) in node], " , " )
135+ end
136+
137+ function generate_standalone (header:: String , props:: Dict , content:: String )
138+ return """
139+ \\ documentclass[crop,tikz]{standalone}
140+ $(header)
141+ \\ begin{document}
142+ \\ begin{tikzpicture}[$(command (props)) ]
143+ $content
144+ \\ end{tikzpicture}
145+ \\ end{document}
146+ """
147+ end
148+ generate_standalone (canvas:: Canvas ) = generate_standalone (canvas. header, canvas. props, join ([[generate_rgbcolor (k,v... ) for (k,v) in canvas. colors]. .. , command .(canvas. contents)... ], " \n " ))
149+
150+ function Base. write (io:: IO , canvas:: Canvas )
151+ write (io, generate_standalone (canvas))
152+ end
153+
154+ end
0 commit comments