diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/jflap2tikz |
Initial commit
Diffstat (limited to 'graphics/jflap2tikz')
17 files changed, 1127 insertions, 0 deletions
diff --git a/graphics/jflap2tikz/CHANGES b/graphics/jflap2tikz/CHANGES new file mode 100644 index 0000000000..40b14b20b5 --- /dev/null +++ b/graphics/jflap2tikz/CHANGES @@ -0,0 +1,5 @@ +Changes in version 1.2 +Added the command line option -k, for keeping the state names used in the +JFLAP file. The default behavior is to replace the state names with names of +the form '$q_{id}$', where id is the unique state number. When using this +option state names will not be sanitized and thus may contain invalid TeX. diff --git a/graphics/jflap2tikz/JFLAP2TikZ.pdf b/graphics/jflap2tikz/JFLAP2TikZ.pdf Binary files differnew file mode 100644 index 0000000000..9395d434c8 --- /dev/null +++ b/graphics/jflap2tikz/JFLAP2TikZ.pdf diff --git a/graphics/jflap2tikz/JFLAP2TikZ.tex b/graphics/jflap2tikz/JFLAP2TikZ.tex new file mode 100644 index 0000000000..281a39f9f9 --- /dev/null +++ b/graphics/jflap2tikz/JFLAP2TikZ.tex @@ -0,0 +1,190 @@ +\documentclass[11pt]{article} + +%packages +\usepackage[colorlinks,urlcolor=blue,linkcolor=blue]{hyperref} +\usepackage{tikz} +\usepackage{mathabx} +\usetikzlibrary{automata,positioning} +\usepackage{fancyhdr} +\usepackage[top=0.4in,bottom=0.4in,left=0.75in,right=0.75in, + nofoot,head=14pt,headsep=8pt,includeheadfoot] {geometry} + +%commands +\newcommand{\tkz}{Ti{\em k}Z} +\newcommand{\JFLAP}{JFLAP} +\newcommand{\jtot}{{\JFLAP}2\tkz} +\newcommand{\httpURL}[1]{\href{http://#1}{#1}} + +%title +\title{\jtot} +\author{Andrew Mertz and William Slough} +\date{} + +%header +\pagestyle{fancy} +\fancyhead{} +\fancyfoot{} +\fancyhead[RO,LE]{\thepage} +\fancyhead[LO,RE]{\jtot\ v1.1} + +\begin{document} +\maketitle +\thispagestyle{empty} +\section*{Introduction} + +\href{http://www.jflap.org}{\JFLAP}\footnote{\httpURL{www.jflap.org}} is a +popular Java program for experimenting with finite state machines, Turing +machines and other concepts from Formal Languages and Automata Theory. Version +7 of \JFLAP\ can export JPG, PNG, GIF, BMP, or SVG images. \jtot\ is a +\href{http://groovy.codehaus.org}{Groovy}\footnote{\httpURL{groovy.codehaus.org}} script +that converts a \JFLAP\ jff file representing a finite automaton, pushdown +automaton, or Turing machine into a \LaTeX\ file depicting the automaton +graphically using \tkz. + +\section*{Requirements} + +To use \jtot\ you will need \href{http://www.java.com}{Java} +installed. Additionally, you may find it useful to have +\href{http://groovy.codehaus.org}{Groovy} installed as well. You will +need to download either {\tt jflap2tikz.jar} (if you only have Java +installed) or {\tt jflap2tikz.groovy} (if you also have groovy +installed). + +\section*{Usage} + +\jtot\ is invoked from the command line with: +\begin{quote} +\begin{verbatim} +java -jar jflap2tikz.jar -i example.jff +\end{verbatim} +\end{quote} +or: +\begin{quote} +\begin{verbatim} +groovy jflap2tikz.groovy -i example.jff +\end{verbatim} +\end{quote} +In the above cases the output will be written to the console. Use the -h option +to see the full usage information, which is also given in +Figure~\ref{fig:usage}. + +\begin{figure} +\begin{center} +\begin{verbatim} +usage: jflap2tikz [options] +Version 1.2 + -d,--accepting-distance <distance> Distance, in pt, between the circles of an + accepting state (default is 2) + -g,--grid <size> Round positions so that they are on a + grid. If a size is given it sets the + spacing of the grid (default is 20.0) + -h,--help Show usage information and quit + -i,--input-file <filename> Name of a JFLAP jff file representing a + finite automaton, pushdown automaton, or + Turing machine. If a file is not given + standard input will be used. + -k,--keep-names Use the state names from the JFLAP file. + The default is to replace the state names + with names of the form '$q_{id}$', where + id is the unique state number. Note state + names will not be sanitized and thus may + contain invalid TeX. + -l,--arrow-length <length> Length of arrows in points (default is 9) + -o,--output-file <filename> Name of a file for writing output. If this + file already exists it will be + overwritten. + -r,--rotate Rotate labels along edges + -s,--scale <x> 1 pixel in JFLAP = x points in LaTeX + (default is 1.0) + -w,--arrow-width <width> Width of arrows in points (default is 6) + \end{verbatim} + \end{center} + \caption{Usage information} + \label{fig:usage} +\end{figure} + +\section*{Examples} + +The following figures show machines from the book +\href{http://www.jflap.org/jflapbook/}{JFLAP: An Interactive Formal + Languages and Automata Package} being converted to \tkz\ using +\jtot. Note that \jtot\ currently ignores \JFLAP\ state +annotations. Furthermore, not all \JFLAP\ files will be converted +perfectly. You may have to adjust the scale and/or gridsize, or edit +the resulting \tkz\ code to achieve the effect you want. However, +\jtot\ should provide a good starting point. + +\begin{figure} + \begin{center} + \includegraphics{examples/ex0_1a.png} + \end{center} + \caption{{\tt ex0.1a.jff}} +\end{figure} + +\begin{figure} + \begin{center} + \includegraphics{examples/ex0_1a.pdf} + \end{center} + \caption{{\tt ex0.1a.jff} converted to \tkz\ using default values} +\end{figure} + +\begin{figure} + \begin{center} + \includegraphics{examples/ex0_1aScale2.pdf} + \end{center} \caption{{\tt ex0.1a.jff} converted to \tkz\ using a scale of 2} +\end{figure} + +\begin{figure} + \begin{center} + \includegraphics{examples/ex0_1aGrid100.pdf} + \end{center} \caption{{\tt ex0.1a.jff} converted to \tkz\ using a gridsize of 100} +\end{figure} + +\begin{figure} + \begin{center} + \includegraphics[width=\textwidth]{examples/ex9-anbncn.png} + \end{center} + \caption{{\tt ex9-anbncn.jff}} +\end{figure} + +\begin{figure} + \begin{center} + \includegraphics{examples/ex9-anbncn.pdf} + \end{center} + \caption{{\tt ex9-anbncn.jff} converted to \tkz\ using a gridsize of + 50 and label rotations on} +\end{figure} + +\newpage + +\section*{License} + +\jtot\ and its documentation are licensed under an MIT style license + +\begin{verbatim} + Copyright (c) 2009, 2014, 2015 Andrew Mertz and William Slough + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +\end{verbatim} + +\end{document} + +% LocalWords: JFlap TikZ jff documentclass LocalWords filename gridsize nofoot +% LocalWords: includeheadfoot anbncn codehaus www jflap jflapbook +% LocalWords: NONINFRINGEMENT diff --git a/graphics/jflap2tikz/README b/graphics/jflap2tikz/README new file mode 100644 index 0000000000..509fa06bfc --- /dev/null +++ b/graphics/jflap2tikz/README @@ -0,0 +1,74 @@ +Introduction + +JFLAP is a popular Java program for experimenting with finite state machines, +Turing machines and other concepts from Formal Languages and Automata Theory. +Version 7 of JFLAP can export JPG, PNG, GIF, BMP, or SVG images. JFLAP2TikZ is +a groovy script that converts a JFLAP jff file representing a finite automaton, +pushdown automaton, or Turing machine into a LaTeX file depicting the automaton +graphically using TikZ. + +Requirements + +To use JFLAP2TikZ you will need Java installed. Additionally, you may find it +useful to have Groovy installed as well. You will need to download either +jflap2tikz.jar (if you only have Java installed) or jflap2tikz.groovy (if you +also have groovy installed). + +Usage + +JFLAP2TikZ is invoked from the command line with: + +java -jar jflap2tikz.jar -i example.jff + +or: + +groovy jflap2tikz.groovy -i example.jff + +In the above cases the output will be written to the console. Use the -h option +to see the full usage information, which is also given below. + +usage: jflap2tikz [options] +Version 1.1 + -d,--accepting-distance <distance> Distance, in pt, between the circles of an + accepting state (default is 2) + -g,--grid <size> Round positions so that they are on a + grid. If a size is given it sets the + spacing of the grid (default is 20.0) + -h,--help Show usage information and quit + -i,--input-file <filename> Name of a JFLAP jff file representing a + finite automaton, pushdown automaton, or + Turing machine. If a file is not given + standard input will be used. + -l,--arrow-length <length> Length of arrows in points (default is 9) + -o,--output-file <filename> Name of a file for writing output. If this + file already exists it will be + overwritten. + -r,--rotate Rotate labels along edges + -s,--scale <x> 1 pixel in JFLAP = x points in LaTeX + (default is 1.0) + -w,--arrow-width <width> Width of arrows in points (default is 6) + +License + +JFLAP2TikZ and its documentation are licensed under an MIT style license + +Copyright (c) 2009,2014 Andrew Mertz and William Slough + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/graphics/jflap2tikz/examples/ex0.1a.jff b/graphics/jflap2tikz/examples/ex0.1a.jff new file mode 100644 index 0000000000..e4068c24db --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0.1a.jff @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><!--Created with JFLAP 6.4.--><structure> + <type>fa</type> + <automaton> + <!--The list of states.--> + <state id="0" name="q0"> + <x>62.0</x> + <y>114.0</y> + <initial/> + </state> + <state id="1" name="q1"> + <x>163.0</x> + <y>51.0</y> + </state> + <state id="2" name="q2"> + <x>184.0</x> + <y>162.0</y> + </state> + <state id="3" name="q3"> + <x>275.0</x> + <y>92.0</y> + <final/> + </state> + <!--The list of transitions.--> + <transition> + <from>0</from> + <to>2</to> + <read>b</read> + </transition> + <transition> + <from>2</from> + <to>0</to> + <read>a</read> + </transition> + <transition> + <from>1</from> + <to>2</to> + <read>a</read> + </transition> + <transition> + <from>0</from> + <to>1</to> + <read/> + </transition> + <transition> + <from>1</from> + <to>3</to> + <read>a</read> + </transition> + </automaton> +</structure>
\ No newline at end of file diff --git a/graphics/jflap2tikz/examples/ex0_1a.pdf b/graphics/jflap2tikz/examples/ex0_1a.pdf Binary files differnew file mode 100644 index 0000000000..09e99dbb16 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1a.pdf diff --git a/graphics/jflap2tikz/examples/ex0_1a.png b/graphics/jflap2tikz/examples/ex0_1a.png Binary files differnew file mode 100644 index 0000000000..a9d221d787 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1a.png diff --git a/graphics/jflap2tikz/examples/ex0_1a.tex b/graphics/jflap2tikz/examples/ex0_1a.tex new file mode 100644 index 0000000000..d0032caf99 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1a.tex @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{tikz} +\usetikzlibrary{automata,positioning,arrows.meta} +\usepackage[graphics,tightpage,active,pdftex]{preview} +\setlength{\PreviewBorder}{5pt} +\PreviewEnvironment{tikzpicture} +\begin{document} +\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt, auto] + \draw (62.0pt, -114.0pt)node[state, initial, initial text =](0){$q_{0}$}; + \draw (163.0pt, -51.0pt)node[state](1){$q_{1}$}; + \draw (184.0pt, -162.0pt)node[state](2){$q_{2}$}; + \draw (275.0pt, -92.0pt)node[state, accepting](3){$q_{3}$}; + \path[->] (0) edge[bend left] node{b}(2); + \path[->] (2) edge[bend left] node{a}(0); + \path[->] (1) edge node{a}(2); + \path[->] (0) edge node{$\lambda$}(1); + \path[->] (1) edge node{a}(3); +\end{tikzpicture} +\end{document} diff --git a/graphics/jflap2tikz/examples/ex0_1aGrid100.pdf b/graphics/jflap2tikz/examples/ex0_1aGrid100.pdf Binary files differnew file mode 100644 index 0000000000..a1ee90038e --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1aGrid100.pdf diff --git a/graphics/jflap2tikz/examples/ex0_1aGrid100.tex b/graphics/jflap2tikz/examples/ex0_1aGrid100.tex new file mode 100644 index 0000000000..a0cfc47152 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1aGrid100.tex @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{tikz} +\usetikzlibrary{automata,positioning,arrows.meta} +\usepackage[graphics,tightpage,active,pdftex]{preview} +\setlength{\PreviewBorder}{5pt} +\PreviewEnvironment{tikzpicture} +\begin{document} +\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt, auto] + \draw (100.0pt, -100.0pt)node[state, initial, initial text =](0){$q_{0}$}; + \draw (200.0pt, -100.0pt)node[state](1){$q_{1}$}; + \draw (200.0pt, -200.0pt)node[state](2){$q_{2}$}; + \draw (300.0pt, -100.0pt)node[state, accepting](3){$q_{3}$}; + \path[->] (0) edge[bend left] node{b}(2); + \path[->] (2) edge[bend left] node{a}(0); + \path[->] (1) edge node{a}(2); + \path[->] (0) edge node{$\lambda$}(1); + \path[->] (1) edge node{a}(3); +\end{tikzpicture} +\end{document} diff --git a/graphics/jflap2tikz/examples/ex0_1aScale2.pdf b/graphics/jflap2tikz/examples/ex0_1aScale2.pdf Binary files differnew file mode 100644 index 0000000000..860b0c8906 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1aScale2.pdf diff --git a/graphics/jflap2tikz/examples/ex0_1aScale2.tex b/graphics/jflap2tikz/examples/ex0_1aScale2.tex new file mode 100644 index 0000000000..2c0e8264c2 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex0_1aScale2.tex @@ -0,0 +1,19 @@ +\documentclass{article} +\usepackage{tikz} +\usetikzlibrary{automata,positioning,arrows.meta} +\usepackage[graphics,tightpage,active,pdftex]{preview} +\setlength{\PreviewBorder}{5pt} +\PreviewEnvironment{tikzpicture} +\begin{document} +\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt, auto] + \draw (124.0pt, -228.0pt)node[state, initial, initial text =](0){$q_{0}$}; + \draw (326.0pt, -102.0pt)node[state](1){$q_{1}$}; + \draw (368.0pt, -324.0pt)node[state](2){$q_{2}$}; + \draw (550.0pt, -184.0pt)node[state, accepting](3){$q_{3}$}; + \path[->] (0) edge[bend left] node{b}(2); + \path[->] (2) edge[bend left] node{a}(0); + \path[->] (1) edge node{a}(2); + \path[->] (0) edge node{$\lambda$}(1); + \path[->] (1) edge node{a}(3); +\end{tikzpicture} +\end{document} diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.jff b/graphics/jflap2tikz/examples/ex9-anbncn.jff new file mode 100644 index 0000000000..024b7d80ef --- /dev/null +++ b/graphics/jflap2tikz/examples/ex9-anbncn.jff @@ -0,0 +1,249 @@ +<?xml version="1.0"?> +<!-- Created with JFLAP 4.0b10. --> +<structure> + <type>turing</type> + <!--The list of states.--> + <state id="2"> + <x>367.0</x> + <y>79.0</y> + </state> + <state id="7"> + <x>101.0</x> + <y>411.0</y> + </state> + <state id="5"> + <x>524.0</x> + <y>304.0</y> + </state> + <state id="0"> + <x>253.0</x> + <y>266.0</y> + </state> + <state id="3"> + <x>498.0</x> + <y>132.0</y> + </state> + <state id="9"> + <x>60.0</x> + <y>319.0</y> + <initial /> + </state> + <state id="10"> + <x>149.0</x> + <y>235.0</y> + </state> + <state id="4"> + <x>388.0</x> + <y>236.0</y> + </state> + <state id="1"> + <x>257.0</x> + <y>143.0</y> + </state> + <state id="8"> + <x>256.0</x> + <y>411.0</y> + <final /> + </state> + <state id="6"> + <x>384.0</x> + <y>381.0</y> + </state> + <!--The list of transitions.--> + <transition> + <from>5</from> + <to>6</to> + <read>c</read> + <write></write> + <move>L</move> + </transition> + <transition> + <from>10</from> + <to>10</to> + <read>a</read> + <write>a</write> + <move>L</move> + </transition> + <transition> + <from>10</from> + <to>10</to> + <read>b</read> + <write>b</write> + <move>L</move> + </transition> + <transition> + <from>10</from> + <to>10</to> + <read>c</read> + <write>c</write> + <move>L</move> + </transition> + <transition> + <from>6</from> + <to>6</to> + <read>a</read> + <write>a</write> + <move>L</move> + </transition> + <transition> + <from>6</from> + <to>6</to> + <read>b</read> + <write>b</write> + <move>L</move> + </transition> + <transition> + <from>6</from> + <to>6</to> + <read>c</read> + <write>c</write> + <move>L</move> + </transition> + <transition> + <from>6</from> + <to>6</to> + <read>#</read> + <write>#</write> + <move>L</move> + </transition> + <transition> + <from>10</from> + <to>0</to> + <read></read> + <write></write> + <move>R</move> + </transition> + <transition> + <from>4</from> + <to>4</to> + <read>c</read> + <write>c</write> + <move>R</move> + </transition> + <transition> + <from>3</from> + <to>3</to> + <read>b</read> + <write>b</write> + <move>R</move> + </transition> + <transition> + <from>2</from> + <to>2</to> + <read>#</read> + <write>#</write> + <move>R</move> + </transition> + <transition> + <from>1</from> + <to>1</to> + <read>a</read> + <write>a</write> + <move>R</move> + </transition> + <transition> + <from>7</from> + <to>7</to> + <read>#</read> + <write>#</write> + <move>R</move> + </transition> + <transition> + <from>9</from> + <to>9</to> + <read>c</read> + <write>c</write> + <move>R</move> + </transition> + <transition> + <from>9</from> + <to>9</to> + <read>b</read> + <write>b</write> + <move>R</move> + </transition> + <transition> + <from>9</from> + <to>9</to> + <read>a</read> + <write>a</write> + <move>R</move> + </transition> + <transition> + <from>1</from> + <to>2</to> + <read>#</read> + <write>#</write> + <move>R</move> + </transition> + <transition> + <from>6</from> + <to>0</to> + <read></read> + <write></write> + <move>R</move> + </transition> + <transition> + <from>9</from> + <to>10</to> + <read></read> + <write></write> + <move>L</move> + </transition> + <transition> + <from>0</from> + <to>1</to> + <read>a</read> + <write></write> + <move>R</move> + </transition> + <transition> + <from>1</from> + <to>3</to> + <read>b</read> + <write>#</write> + <move>R</move> + </transition> + <transition> + <from>0</from> + <to>7</to> + <read>#</read> + <write>#</write> + <move>R</move> + </transition> + <transition> + <from>7</from> + <to>8</to> + <read></read> + <write></write> + <move>L</move> + </transition> + <transition> + <from>3</from> + <to>4</to> + <read>c</read> + <write>c</write> + <move>R</move> + </transition> + <transition> + <from>2</from> + <to>3</to> + <read>b</read> + <write>#</write> + <move>R</move> + </transition> + <transition> + <from>0</from> + <to>8</to> + <read></read> + <write></write> + <move>S</move> + </transition> + <transition> + <from>4</from> + <to>5</to> + <read></read> + <write></write> + <move>L</move> + </transition> +</structure> diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.pdf b/graphics/jflap2tikz/examples/ex9-anbncn.pdf Binary files differnew file mode 100644 index 0000000000..401999f25e --- /dev/null +++ b/graphics/jflap2tikz/examples/ex9-anbncn.pdf diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.png b/graphics/jflap2tikz/examples/ex9-anbncn.png Binary files differnew file mode 100644 index 0000000000..26d1999c87 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex9-anbncn.png diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.tex b/graphics/jflap2tikz/examples/ex9-anbncn.tex new file mode 100644 index 0000000000..ad4ed30f52 --- /dev/null +++ b/graphics/jflap2tikz/examples/ex9-anbncn.tex @@ -0,0 +1,44 @@ +\documentclass{article} +\usepackage{tikz} +\usetikzlibrary{automata,positioning,arrows.meta} +\usepackage{amssymb} + +\usepackage[graphics,tightpage,active,pdftex]{preview} +\setlength{\PreviewBorder}{5pt} +\PreviewEnvironment{tikzpicture} +\begin{document} +\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt] + \draw (350.0pt, -100.0pt)node[state](2){$q_{2}$}; + \draw (100.0pt, -400.0pt)node[state](7){$q_{7}$}; + \draw (500.0pt, -300.0pt)node[state](5){$q_{5}$}; + \draw (250.0pt, -250.0pt)node[state](0){$q_{0}$}; + \draw (500.0pt, -150.0pt)node[state](3){$q_{3}$}; + \draw (50.0pt, -300.0pt)node[state, initial, initial text =](9){$q_{9}$}; + \draw (150.0pt, -250.0pt)node[state](10){$q_{10}$}; + \draw (400.0pt, -250.0pt)node[state](4){$q_{4}$}; + \draw (250.0pt, -150.0pt)node[state](1){$q_{1}$}; + \draw (250.0pt, -400.0pt)node[state, accepting](8){$q_{8}$}; + \draw (400.0pt, -400.0pt)node[state](6){$q_{6}$}; + \path[->] (5) edge node[sloped,anchor=north]{c : $\Box$, L}(6); + \path[->] (10) edge[loop above] node[align=center]{a : a, L\\ b : b, L\\ c : c, L}(10); + \path[->] (6) edge[loop above] node[align=center]{a : a, L\\ b : b, L\\ c : c, L\\ \# : \#, L}(6); + \path[->] (10) edge node[sloped,anchor=south]{$\Box$ : $\Box$, R}(0); + \path[->] (4) edge[loop above] node{c : c, R}(4); + \path[->] (3) edge[loop above] node{b : b, R}(3); + \path[->] (2) edge[loop above] node{\# : \#, R}(2); + \path[->] (1) edge[loop above] node{a : a, R}(1); + \path[->] (7) edge[loop above] node{\# : \#, R}(7); + \path[->] (9) edge[loop above] node[align=center]{c : c, R\\ b : b, R\\ a : a, R}(9); + \path[->] (1) edge node[sloped,anchor=south]{\# : \#, R}(2); + \path[->] (6) edge node[sloped,anchor=north]{$\Box$ : $\Box$, R}(0); + \path[->] (9) edge node[sloped,anchor=south]{$\Box$ : $\Box$, L}(10); + \path[->] (0) edge node[sloped,anchor=north]{a : $\Box$, R}(1); + \path[->] (1) edge node[sloped,anchor=south]{b : \#, R}(3); + \path[->] (0) edge node[sloped,anchor=north]{\# : \#, R}(7); + \path[->] (7) edge node[sloped,anchor=south]{$\Box$ : $\Box$, L}(8); + \path[->] (3) edge node[sloped,anchor=north]{c : c, R}(4); + \path[->] (2) edge node[sloped,anchor=south]{b : \#, R}(3); + \path[->] (0) edge node[sloped,anchor=north]{$\Box$ : $\Box$, S}(8); + \path[->] (4) edge node[sloped,anchor=south]{$\Box$ : $\Box$, L}(5); +\end{tikzpicture} +\end{document} diff --git a/graphics/jflap2tikz/jflap2tikz.groovy b/graphics/jflap2tikz/jflap2tikz.groovy new file mode 100644 index 0000000000..c049e5f0f4 --- /dev/null +++ b/graphics/jflap2tikz/jflap2tikz.groovy @@ -0,0 +1,458 @@ +/* + * This groovy script converts a JFLAP jff file representing a finite automaton + * to LaTeX file depicting the automaton graphically using TikZ. + * + * Copyright (c) 2009, 2014, 2015 Andrew Mertz and William Slough + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Define a few constants +scale = 1.0 // 1 pixel in JFLAP = scale points in LaTeX +gridsize = 20.0 // gridsize is measured in pixels +turingBlank = '$\\Box$' // the symbol to be used for a blank in a TM +blank = '$\\lambda$' // the symbol to be used for blank otherwise +arrowWidth = 6 // width of an arrow in points +arrowLength = 9 // length of an arrow in points +arrowType = 'Stealth' // arrow type from arrows.meta TikZ library +acceptingDistance = 2 // distance, in pt, between circles in accepting + // state +version = '1.2' +infilename = null +outputStream = null + +// Define a function that cleans up text that could be "bad" for LaTeX to +// ingest +String fixText(String text) +{ + if(text) + { + text = text.replace('\\', '$\\backslash$') + text = text.replace('~', '$\\sim$') + text = text.replaceAll('[$%^&_#{}]', {'\\'+ it[0]}) + } + return text +} + +// Setup the command line options +// TODO: maybe add a switch for arrow type +// TODO: maybe allow different scales and grid sizes for each axis +// TODO: maybe allow user to set preview boarder +cmdLine = new CliBuilder(usage: 'jflap2tikz [options]', + header: "Version ${version}", + width: 80) + +cmdLine.d(longOpt:'accepting-distance', + args: 1, + argName: 'distance', + required: false, + "Distance, in pt, between the circles of an accepting state (default is ${acceptingDistance})") +cmdLine.i(longOpt: 'input-file', + args: 1, + argName: 'filename', + 'Name of a JFLAP jff file representing a finite automaton, pushdown automaton, or Turing machine. If a ' + + 'file is not given standard input will be used.') +cmdLine.g(longOpt:'grid', + args:1, + optionalArg: true, + argName: 'size', + required: false, + "Round positions so that they are on a grid. If a size is given it sets the spacing of the grid (default " + + "is ${gridsize})") +cmdLine.h(longOpt:'help', + required: false, + 'Show usage information and quit') +cmdLine.l(longOpt:'arrow-length', + args:1, + argName: 'length', + required: false, + "Length of arrows in points (default is ${arrowLength})") +cmdLine.o(longOpt: 'output-file', + args: 1, + argName: 'filename', + 'Name of a file for writing output. If this file already exists it will be overwritten.') +cmdLine.r(longOpt:'rotate', + required: false, + 'Rotate labels along edges') +cmdLine.s(longOpt:'scale', + args:1, + argName:'x', + required: false, + "1 pixel in JFLAP = x points in LaTeX (default is ${scale})") +cmdLine.w(longOpt:'arrow-width', + args:1, + argName:'width', + required: false, + "Width of arrows in points (default is ${arrowWidth})") +cmdLine.k(longOpt:'keep-names', + required: false, + "Use the state names from the JFLAP file. The default is to replace the state names with names of the " + + "form '\$q_{id}\$', where id is the unique state number. Note state names will not be sanitized and thus " + + "may contain invalid TeX.") + +// Parse the command line options +options = cmdLine.parse(args) + +// If there is a parse error, quit. Note usage information will already have +// been displayed. +if(!options) +{ + System.exit(1) +} + +// If the user has asked for help print the usage information and quit +if(options.h) +{ + cmdLine.usage() + System.exit(0) +} + +keapNames = options.k + +grid = false +if(options.g) +{ + grid = true + + // Verify that gridsize is valid (a positive double) + if(!(options.g instanceof Boolean)) + { + try + { + gridsize = Double.valueOf(options.g) + if (gridsize <= 0) throw new NumberFormatException() + } + catch(NumberFormatException ex) + { + System.err.println "error: gridsize must be a positive number" + cmdLine.usage() + System.exit(2) + } + } +} + +// Verify that the scale is valid (a positive double) +try +{ + if(options.s) + { + scale = Double.valueOf(options.s) + if (scale <= 0) + { + throw new NumberFormatException() + } + } +} +catch(NumberFormatException ex) +{ + System.err.println "error: scale must be a positive number" + cmdLine.usage() + System.exit(3) +} + +// Verify that the accepting distance is valid (a positive double) +try +{ + if(options.d) + { + acceptingDistance = Double.valueOf(options.d) + if (acceptingDistance <= 0) + { + throw new NumberFormatException() + } + } +} +catch(NumberFormatException ex) +{ + System.err.println "error: accepting distance must be a positive number" + cmdLine.usage() + System.exit(4) +} + +if(options.i) +{ + infilename = options.i +} + +if(options.o) +{ + try + { + outputStream = new FileWriter(options.o) + } + catch(Exception e) + { + System.err.println "error: unable to write to output file ${options.o}" + System.exit(5) + } +} +else +{ + outputStream = System.out +} + +// Try to get the text from the file +if(infilename) +{ + try + { + body = new File(infilename).text + } + catch(IOException ex) + { + System.err.println "error: unable to read ${infilename}" + System.exit(4) + } +} +else +{ + body = System.in.text +} + +// Parse the XML +try +{ + structure = new XmlParser().parseText(body) +} +catch(Exception ex) +{ + System.err.println "error: parsing XML" + System.exit(5) +} + +// Check the type to be sure it is a machine that can be converted +type = structure.type.text() +if(type != "fa" && type != "pda" && type != "turing") +{ + System.err.println "error: unsupported machine type --- only FAs, PDAs and Turing machines can be converted" + System.exit(6); +} + +// If the automaton is a Turing machine, get the number of tapes +try +{ + tapes = Integer.valueOf(structure.tapes.text()) +} +catch (Exception ex) +{ + tapes = 1 +} + +// Find the automaton in the XML tree +automaton = structure.automaton +if(!automaton) +{ + automaton = structure +} + +// Print the LaTeX header +outputStream.println """\\documentclass{article} +\\usepackage{tikz} +\\usetikzlibrary{automata,positioning,arrows.meta}${type == "turing" ? "\n\\usepackage{amssymb}\n" : ""} +\\usepackage[graphics,tightpage,active,pdftex]{preview} +\\setlength{\\PreviewBorder}{5pt} +\\PreviewEnvironment{tikzpicture} +\\begin{document} +\\begin{tikzpicture}[>={Stealth[width=${arrowWidth}pt,length=${arrowLength}pt]}, accepting/.style={double distance = ${acceptingDistance}pt, outer sep = ${acceptingDistance/2.0}pt + \\pgflinewidth}, shorten >=1pt${options.r ? "" : ", auto"}]""" + +// For each state in the XML define a TikZ node and record their position +statePosition = [:] +automaton.state.each +{ + // Get the x and y value of the position, correcting for the change in + // coordinate systems + x = Double.valueOf(it.x.text()) * scale + y = -Double.valueOf(it.y.text()) * scale + if(grid) + { + x = Math.round(x / gridsize) * gridsize + y = Math.round(y / gridsize) * gridsize + } + + // record position + statePosition[it.@id] = [x,y] + + // get state name + def stateName + if(keapNames) + { + stateName = it.@name + } + else + { + stateName = "\$q_{${fixText(it.@id)}}\$" + } + + // Output the TikZ version of this state + outputStream.println " \\draw (${x}pt, ${y}pt)" + + "node[state${it.initial ? ", initial, initial text =" : ""}" + + "${it.get("final") ? ", accepting" : ""}]" + + "(${fixText(it.@id)}){$stateName};" +} + +// Define a function that makes a key based on the end points of a transition +// without taking into account the order of the keys. In other words, edge +// (a,b) results in the same key as the edge (b,a). +String makeKey(String a, String b) +{ + return a < b ? "${a},${b}" : "${b},${a}" +} + +// Scan transitions and create their labels. Also, check to see if there are +// edges going in both directions. +doubleEdges = [:] +edgeLabels = [:] +automaton.transition.each +{ + orderedKey = [it.from.text(), it.to.text()] + + // Have we seen the reverse of this edge before? + if(edgeLabels.get([it.to.text(), it.from.text()]) && + it.to.text() != it.from.text()) + { + doubleEdges.put(makeKey(it.from.text(), it.to.text()), true) + } + + // Get the read symbol. If it is blank use lambda for the symbol. + if(!(readSymbol = fixText(it.read.text()))) + { + readSymbol = blank + } + + // Get the pop and push symbols if the machine is a PDA + if(type == "pda") + { + if(!(popSymbol = fixText(it.pop.text()))) + { + popSymbol = blank + } + if(!(pushSymbol = fixText(it.push.text()))) + { + pushSymbol = blank + } + } + + // If the machine is a Turing machine, get all of the read, write, and move + // information + if(type == "turing") + { + readSymbols = [] + it.read.each{r-> + readSymbols << (r.text() ? fixText(r.text()) : turingBlank) + } + + writeSymbols = [] + it.write.each{w-> + writeSymbols << (w.text() ? fixText(w.text()) : turingBlank) + } + + moves = [] + it.move.each{m-> + moves << fixText(m.text()) + } + } + + // Make the label for this transition + if(type == "fa") + { + label = readSymbol; + } + else if(type == "pda") + { + label = "${readSymbol}, ${popSymbol}; ${pushSymbol}" + } + else if(type == "turing") + { + // Merge the read, write and move lists into one label + label = [] + readSymbols.eachWithIndex{read, i-> + label << "${read} : ${writeSymbols[i]}, ${moves[i]}" + } + label = label.join('$|$') + } + + // Add this edge's label to the edge label list + if((oldLabel = edgeLabels.get(orderedKey))) + { + oldLabel << label + } + else + { + edgeLabels.put(orderedKey, [label]) + } +} + +// For each transition draw an edge +edgeLabels.each +{ + // Turn the label into a string + if(type == "fa") + { + label = it.value.join(", ") + } + else + { + label = it.value.join("\\\\ ") + } + + // Construct the options for the node that will hold the label + nodeOptions = [] + if(type != "fa" && it.value.size() > 1) // Are line breaks needed? + { + nodeOptions << "align=center" + } + if(options.r && it.key[0] != it.key[1]) // Does the text need to be rotated? + { + nodeOptions << "sloped" + + // In the case of rotations auto positioning is not used. Thus, an anchor + // point for the node must be given. + if(statePosition[it.key[0]][0] < statePosition[it.key[1]][0]) + { + nodeOptions << "anchor=south" + } + else + { + nodeOptions << "anchor=north" + } + } + + // Turn the node options into a string. If there are no options then use the + // empty string. Otherwise join the option with commas and surround them with + // square brackets. + if(nodeOptions.size() > 0) + { + nodeOptions = "[${nodeOptions.join(",")}]" + } + else + { + nodeOptions = "" + } + + outputStream.println " \\path[->] (${it.key[0]}) edge" + + (it.key[0] == it.key[1] ? "[loop above]" : "") + // Do we have a self loop? + (doubleEdges.get(makeKey(it.key[0], it.key[1])) ? "[bend left]" : "") + // Do we have more than one edge? If so add a bend. + " node${nodeOptions}" + + "{${label}}(${it.key[1]});" +} + +// Print the LaTeX footer +outputStream.println """\\end{tikzpicture} +\\end{document}""" +outputStream.close() |