99import org .variantsync .diffdetective .show .engine .input .ZoomViaMouseWheel ;
1010import org .variantsync .diffdetective .show .variation .input .ExitOnEscape ;
1111import org .variantsync .diffdetective .show .variation .input .NodeDragAndDrop ;
12+ import org .variantsync .diffdetective .util .StringUtils ;
1213import org .variantsync .diffdetective .variation .diff .DiffNode ;
1314import org .variantsync .diffdetective .variation .diff .DiffTree ;
1415import org .variantsync .diffdetective .variation .diff .DiffType ;
1516import org .variantsync .diffdetective .variation .diff .Time ;
1617import org .variantsync .diffdetective .variation .diff .serialize .Format ;
1718import org .variantsync .diffdetective .variation .diff .serialize .GraphvizExporter ;
19+ import org .variantsync .diffdetective .variation .diff .serialize .TikzExporter ;
1820import org .variantsync .diffdetective .variation .diff .serialize .edgeformat .DefaultEdgeLabelFormat ;
1921import org .variantsync .diffdetective .variation .diff .serialize .edgeformat .EdgeLabelFormat ;
2022import org .variantsync .diffdetective .variation .diff .serialize .nodeformat .*;
2123
2224import javax .swing .*;
2325import javax .swing .filechooser .FileNameExtensionFilter ;
2426import java .awt .event .MouseEvent ;
27+ import java .io .BufferedOutputStream ;
2528import java .io .File ;
2629import java .io .IOException ;
30+ import java .nio .file .Files ;
31+ import java .nio .file .Path ;
2732import java .util .ArrayList ;
2833import java .util .HashMap ;
2934import java .util .List ;
@@ -53,6 +58,8 @@ public class DiffTreeApp extends App {
5358 private boolean rootDancing = false ;
5459 private final Dance rootDance ;
5560
61+ private DiffNodeLabelFormat currentFormat = DEFAULT_FORMAT .getNodeFormat ();
62+
5663 public DiffTreeApp (final String name , final DiffTree diffTree , Vec2 resolution ) {
5764 super (new Window (name , (int )resolution .x (), (int )resolution .y ()));
5865 this .diffTree = diffTree ;
@@ -81,6 +88,12 @@ private void setupMenu() {
8188 saveScreenshotMenuItem .addActionListener (event -> saveScreenshot ());
8289 fileMenu .add (saveScreenshotMenuItem );
8390
91+ // Tikz export
92+ final JMenuItem tikzExportMenuItem = new JMenuItem ("Tikz Export" );
93+ tikzExportMenuItem .setAccelerator (javax .swing .KeyStroke .getKeyStroke (java .awt .event .KeyEvent .VK_T , java .awt .event .InputEvent .CTRL_MASK ));
94+ tikzExportMenuItem .addActionListener (event -> saveTikz ());
95+ fileMenu .add (tikzExportMenuItem );
96+
8497 // Switch layout
8598 for (final GraphvizExporter .LayoutAlgorithm layoutAlgorithm : GraphvizExporter .LayoutAlgorithm .values ()) {
8699 final JMenuItem setLayoutMenuItem = new JMenuItem (layoutAlgorithm .getExecutableName ());
@@ -127,12 +140,12 @@ private void toggleRootDance() {
127140 }
128141
129142 private void saveScreenshot () {
130- JFileChooser fileChooser = new JFileChooser ();
143+ final JFileChooser fileChooser = new JFileChooser ();
131144 fileChooser .setFileFilter (
132145 new FileNameExtensionFilter ("Image Files" , javax .imageio .ImageIO .getReaderFileSuffixes ())
133146 );
134147
135- int result = fileChooser .showSaveDialog (getWindow ());
148+ final int result = fileChooser .showSaveDialog (getWindow ());
136149 if (result == JFileChooser .APPROVE_OPTION ) {
137150 final File targetFile = fileChooser .getSelectedFile ();
138151 final Texture screenshot = getWindow ().getScreen ().screenshot ();
@@ -144,7 +157,52 @@ private void saveScreenshot() {
144157 }
145158 }
146159
160+ private void saveTikz () {
161+ final JFileChooser fileChooser = new JFileChooser ();
162+ fileChooser .setFileFilter (
163+ new FileNameExtensionFilter ("TeX" , "tex" )
164+ );
165+
166+ final int result = fileChooser .showSaveDialog (getWindow ());
167+ if (result == JFileChooser .APPROVE_OPTION ) {
168+ final File targetFile = fileChooser .getSelectedFile ();
169+ final TikzExporter tikzExporter = new TikzExporter (new Format (currentFormat , new DefaultEdgeLabelFormat ()));
170+
171+ try (
172+ var unbufferedOutput = Files .newOutputStream (targetFile .toPath ());
173+ var output = new BufferedOutputStream (unbufferedOutput )
174+ ) {
175+ final Vec2 flipY = new Vec2 (1.0 , -1.0 );
176+ final double nodeRadiusInTikz = 6.5 ;
177+ final Vec2 scaleToTikz = Vec2 .all (nodeRadiusInTikz ).dividedBy (resolution );
178+
179+ tikzExporter .exportDiffTree (
180+ diffTree ,
181+ node -> {
182+ Vec2 pos = nodes .get (node ).getLocation ();
183+ pos = pos .scale (flipY );
184+ pos = pos .scale (scaleToTikz );
185+ return pos ;
186+ },
187+ output
188+ );
189+ } catch (IOException e ) {
190+ JOptionPane .showMessageDialog (
191+ getWindow (),
192+ "Export to "
193+ + targetFile
194+ + " failed because:"
195+ + StringUtils .LINEBREAK
196+ + e ,
197+ "Tikz export failed" ,
198+ JOptionPane .ERROR_MESSAGE
199+ );
200+ }
201+ }
202+ }
203+
147204 private void setLabelFormat (DiffNodeLabelFormat labelFormat ) {
205+ this .currentFormat = labelFormat ;
148206 for (Entity e : nodes .values ()) {
149207 e .forComponent (GraphNodeGraphics .class ,
150208 graphNodeGraphics -> graphNodeGraphics .node .setLabelFormat (labelFormat )
@@ -176,7 +234,7 @@ private void spawnDiffTree(final World world) {
176234 e .add (new GraphNodeGraphics (
177235 new NodeView (
178236 diffNode ,
179- DEFAULT_FORMAT . getNodeFormat ()
237+ currentFormat
180238 )
181239 ));
182240 e .setZ (Z .FOR_NODES );
0 commit comments