/*
* (C) Copyright 2002 Arnaud Bailly (arnaud.oqube@gmail.com),
* Yves Roos (yroos@lifl.fr) and others.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rationals.converters;
import rationals.Automaton;
import rationals.State;
import rationals.Transition;
import rationals.converters.algorithms.Coord;
import rationals.converters.algorithms.LayoutAlgorithm;
import rationals.converters.algorithms.SimulatedAnnealing;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
/**
* This Displayer subclass produces a postscript file from an
* automaton. <p>
* It uses a LayoutAlgorithm object to produce its output<p>.
*
* @author Arnaud Bailly
* @version 22032002
*/
public class PSDisplayer implements StreamDisplayer {
private double ratio;
private double maxHeight = ((29.7 /2.54) * 72) ;
private double maxWidth = ((21.0 /2.54) * 72);
private static String header =
"%!PS-Adobe-2.0 EPSF-2.0 \n"
+ "%%BoundingBox:\n"
+ "%%Creator:autops\n"
+ "%%Title:\n"
+ "%%CreationDate:\n"
+ "%%EndComments\n"
+ "\n"
+ "/autodict \n"
+ "%%dictionnaire\n"
+ "dict def \n"
+ "autodict begin\n"
+ "\n"
+ "% transforme des coordonnees alg en polaire\n"
+ "% relativement a x0 y0\n"
+ "% x0 y0 x1 y1 polaire norme cos(v) sin(v)\n"
+ "/polaire { \n"
+ "exch 4 -1 roll 4 copy % y0 y1 x1 x0 y0 y1 x1 x0\n"
+ "sub dup mul % y0 y1 (x1 - x0)2\n"
+ "3 -2 roll exch\n"
+ "sub dup mul % xv yv\n"
+ "add sqrt % || v ||\n"
+ "5 1 roll % |v| yo y1 x1 x0\n"
+ "sub 3 index div % |v| yo y1 cos(v)\n"
+ "4 1 roll exch % cos(v) |v| y1 y0\n"
+ "sub 1 index div % cos(v) |v| sin(v)\n"
+ "exch 3 1 roll} bind def \n"
+ "\n"
+ "% calcule une nouvelle matrice de transformation a partir\n"
+ "% des coordonnees polaires relatives a deux etats \n"
+ "% cos(v) sin(v) x0 y0 transformCTM matrice\n"
+ "/transformCTM {\n"
+ "3 index 3 index neg exch % cos sin x y -sin cos\n"
+ "4 2 roll % cos sin -sin cos x y\n"
+ "6 array astore } bind def\n"
+ "\n"
+ "% affiche une double fleche de sortie\n"
+ "% sur l'etat xi yi a l'angle cos sin\n"
+ "% cos sin x y sortie -\n"
+ "/sortie {\n"
+ "gsave \n"
+ "transformCTM concat \n"
+ "newpath\n"
+ "20 3 moveto 6 0 rlineto \n"
+ "3 -3 rlineto -3 -3 rlineto -6 0 rlineto \n"
+ "stroke\n"
+ "grestore } bind def\n"
+ "\n"
+ "% affiche un etat \n"
+ "% (nom-etat) x y etat -\n"
+ "/etat {\n"
+ "1 index 1 index\n"
+ "newpath 20 0 360 arc stroke\n"
+ "exch 5 sub exch\n"
+ "moveto show } bind def \n"
+ "\n"
+ "% affiche un etat initial \n"
+ "% (nom-etat) x y etat_init -\n"
+ "/etat_init {\n"
+ "gsave\n"
+ "1 index 1 index\n"
+ "newpath 20 0 360 arc fill\n"
+ "exch 5 sub exch\n"
+ "moveto 1 setgray show \n"
+ "grestore} bind def \n"
+ "\n"
+ "% affiche un etat final \n"
+ "% (nom-etat) x y etat_final -\n"
+ "/etat_final {\n"
+ "1 index 1 index\n"
+ "newpath 20 0 360 arc stroke\n"
+ "% trace de la sortie a 45 degres\n"
+ "1 index 1 index % x y x y\n"
+ "1 index 20 add 1 index 20 add % x y x y x+20 y+20\n"
+ "polaire 3 -1 roll pop % x y cos sin\n"
+ "3 index 3 index % x y cos sin x y\n"
+ "sortie\n"
+ "exch 5 sub exch\n"
+ "moveto show } bind def \n"
+ "\n"
+ "% affiche un etat final et initial\n"
+ "% (nom-etat) x y etat_init_final -\n"
+ "/etat_init_final {\n"
+ "gsave\n"
+ "1 index 1 index\n"
+ "newpath 20 0 360 arc fill\n"
+ "% trace de la sortie a 45 degres\n"
+ "1 index 1 index % x y x y\n"
+ "1 index 20 add 1 index 20 add % x y x y x+20 y+20\n"
+ "polaire 3 -1 roll pop % x y cos sin\n"
+ "3 index 3 index % x y cos sin x y\n"
+ "sortie\n"
+ "exch 5 sub exch\n"
+ "moveto 1 setgray show grestore} bind def \n"
+ "\n"
+ "\n"
+ "% trace une pointe de fleche a l'extremite du segment \n"
+ "% passe en parametre \n"
+ "% xa ya xb yb fleche -\n"
+ "/fleche {\n"
+ "3 index 3 index 4 -2 roll % xa ya xa ya xb yb\n"
+ "gsave\n"
+ "polaire % xa ya |v| cosv sinv\n"
+ "5 -2 roll transformCTM concat % |v|\n"
+ "newpath\n"
+ "0 moveto -6 2 rlineto 2 -2 rlineto -2 -2 rlineto closepath fill\n"
+ "grestore } bind def \n"
+ "\n"
+ "% trace une transition entre qi et qj \n"
+ "% sous la forme d'un arc aplati avec une fleche a son extremite\n"
+ "% la lettre representant la transition est affichee au sommet de l'arc\n"
+ "% string-lt string-i xi yi string-j xj yj transition -\n"
+ "/transition_courbe {\n"
+ "6 -1 roll pop\n"
+ "3 -1 roll pop % lt xi yi xj yj\n"
+ "3 index 3 index 7 2 roll % xi yi lt xi yi xj yj\n"
+ "gsave % sauvegarde de l'etat graphique\n"
+ "polaire % xi yi lt |v| cos sin\n"
+ "6 -2 roll % lt |v| cos sin xi yi\n"
+ "transformCTM concat % lt |v| le systeme de coordonnees est modifie\n"
+ "newpath\n"
+ "0 0 moveto\n"
+ "dup 2 div dup 10 exch 10 % lt |v| |v|/3 20 2|v|/3 20\n"
+ "4 index 20 sub 0 % lt |v| x1 y1 x2 y2 x3 y3 \n"
+ "20 0 rmoveto curveto % lt |v| \n"
+ "currentpoint stroke % la transition est tracee\n"
+ "2 index 2 div 10 4 2 roll % lt |v| xm ym xa ya\n"
+ "fleche % trace la fleche a l'arrivee\n"
+ "2 div 12 moveto % le milieu du texte est positionne\n"
+ "dup stringwidth % calcule la taille de lt\n"
+ "pop 2 div neg 0 rmoveto \n"
+ "show \n"
+ "grestore } bind def \n"
+ "\n"
+ "% trace une transition entre qi et qj \n"
+ "% sous la forme d'un segment avec une fleche a son extremite\n"
+ "% la lettre representant la transition est affichee au dessus du segment\n"
+ "% string-lt string-i xi yi string-j xj yj transition -\n"
+ "/transition_droite {\n"
+ "6 -1 roll pop\n"
+ "3 -1 roll pop % lt xi yi xj yj\n"
+ "3 index 3 index 7 2 roll % xi yi lt xi yi xj yj\n"
+ "gsave % sauvegarde de l'etat graphique\n"
+ "polaire % xi yi lt |v| cos sin\n"
+ "6 -2 roll % lt |v| cos sin xi yi\n"
+ "transformCTM concat % lt |v| le systeme de coordonnees est modifie\n"
+ "newpath\n"
+ "0 0 moveto\n"
+ "20 0 rmoveto dup 20 sub 0 lineto\n"
+ "stroke\n"
+ "20 0 2 index 20 sub 0 fleche\n"
+ "2 div 5 moveto \n"
+ "dup stringwidth pop 2 div neg 0 rmoveto\n"
+ "show \n"
+ "grestore } bind def \n"
+ "\n"
+ "% affiche une transition d'un etat sur lui_meme\n"
+ "% perpendiculairement a un segment\n"
+ "% string-lt string-i xi yi string-j xj yj transition -\n"
+ "/auto_transition {\n"
+ "6 -1 roll pop\n"
+ "3 -1 roll pop % lt xi yi xj yj\n"
+ "3 index 3 index 7 2 roll % xi yi lt xi yi xj yj\n"
+ "gsave % sauvegarde de l'etat graphique\n"
+ "polaire % xi yi lt |v| cos sin\n"
+ "6 -2 roll % lt |v| cos sin xi yi\n"
+ "transformCTM concat % lt |v| le systeme de coordonnees est modifie\n"
+ "newpath\n"
+ "0 20 moveto\n"
+ "-15 40 15 40 currentpoint curveto stroke\n"
+ "10 40 0 20 fleche\n"
+ "0 40 moveto pop show\n"
+ "grestore } bind def\n"
+ "\n"
+ "%%defetat\n"
+ "\n"
+ "end\n"
+ "%%EndProlog\n"
+ "autodict begin\n"
+ "\n"
+ "%%translation\n"
+ "translate \n"
+ "%%scaling\n"
+ "scale \n"
+ "/Helvetica findfont 10 scalefont setfont \n"
+ "0.5 setlinewidth\n"
+ "\n"
+ "%%etats\n"
+ "\n"
+ "%%transitions\n"
+ "\n"
+ "end\n"
+ "showpage\n"
+ "%%Trailer\n";
private static Map tokenMap = new java.util.HashMap();
static {
try {
tokenMap.put(
"%%BoundingBox:",
PSDisplayer.class.getMethod("outputBBox", null));
tokenMap.put(
"%%dictionnaire",
PSDisplayer.class.getMethod("outputDict", null));
tokenMap.put(
"%%translation",
PSDisplayer.class.getMethod("outputTranslation", null));
tokenMap.put(
"%%scaling",
PSDisplayer.class.getMethod("outputScaling", null));
tokenMap.put(
"%%defetat",
PSDisplayer.class.getMethod("outputDefetat", null));
tokenMap.put(
"%%etats",
PSDisplayer.class.getMethod("outputEtats", null));
tokenMap.put(
"%%transitions",
PSDisplayer.class.getMethod("outputTransitions", null));
} catch (Exception ex) {
System.err.println(
"Error in output functions initialization :" + ex);
}
}
/** width of display */
private double width = 200;
/** height of display */
private double height = 200;
private double border = 40;
private long minx = Integer.MAX_VALUE;
private long maxx = Integer.MIN_VALUE;
private long miny = Integer.MAX_VALUE;
private long maxy = Integer.MIN_VALUE;
/** display algorithm to use */
LayoutAlgorithm algo;
/** print stream to use */
PrintStream ps;
/** map of states to coord */
Map statesCoord;
Automaton automata;
/////////////////////////////////////////////////:
// CONSTRUCTORS
/////////////////////////////////////////////////
/**
* Constructs a default PSDisplayer.
*/
public PSDisplayer() {
algo = new SimulatedAnnealing();
ps = System.out;
}
/////////////////////////////////////////////////////:
// PUBLIC METHODS FOR OUTPUT
/////////////////////////////////////////////////////
public void outputBBox() {
Iterator it = statesCoord.entrySet().iterator();
minx = Integer.MAX_VALUE;
maxx = Integer.MIN_VALUE;
miny = Integer.MAX_VALUE;
maxy = Integer.MIN_VALUE;
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
State s = (State) entry.getKey();
Coord c = (Coord) entry.getValue();
long ix = Math.round(c.x);
long iy = Math.round(c.y);
if (ix < minx)
minx = ix;
if (ix > maxx)
maxx = ix;
if (iy < miny)
miny = iy;
if (iy > maxy)
maxy = iy;
}
width = maxx - minx + 2 * border;
height = maxy - miny + 2 * border;
double xratio = maxWidth/width;
double yratio = maxHeight/height;
ratio = xratio < yratio ? xratio : yratio;
ratio = ratio > 1 ? 1 : ratio;
ps.println(
"%%BoundingBox: 0 0 "
+ Math.round(width*ratio)
+ " "
+ Math.round(height*ratio));
}
public void outputDict() {
ps.println("%%dictionnaire");
ps.println(10 + statesCoord.size()); // ?????
}
public void outputTranslation() {
ps.println("%%translation");
ps.println("0 " + Math.round(height*ratio)); // ?????
}
public void outputScaling() {
ps.println("%%scaling");
/* rescale only if greater than requested - keep aspect ratio */
ps.println(ratio +" " + ratio); // ?????
}
public void outputDefetat() {
ps.println("%%defetat");
Iterator it = statesCoord.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
State s = (State) entry.getKey();
Coord c = (Coord) entry.getValue();
ps.println(
"/q"
+ s
+ " { (q"
+ s
+ ") "
+ Math.round(c.x - minx + border)
+ " "
+ (Math.round(c.y - maxy - border))
+ " } bind def");
}
}
public void outputEtats() {
ps.println("%%defetat");
Iterator it = statesCoord.keySet().iterator();
while (it.hasNext()) {
State s = (State) it.next();
if (s.isInitial() && s.isTerminal())
ps.println("q" + s + " etat_init_final ");
else if (s.isTerminal())
ps.println("q" + s + " etat_final ");
else if (s.isInitial())
ps.println("q" + s + " etat_init ");
else
ps.println("q" + s + " etat ");
}
}
public void outputTransitions() {
ps.println("%%transitions");
Iterator it = statesCoord.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
State s = (State) entry.getKey();
Coord cs = (Coord) entry.getValue();
Iterator it2 = automata.delta(s).iterator();
all_trans : while (it2.hasNext()) {
Transition t = (Transition) it2.next();
State s2 = t.end();
Coord cs2 = (Coord) statesCoord.get(s2);
Object lbl = t.label();
if (s2.equals(s)) {
ps.println(
"("
+ lbl
+ ") q"
+ s
+ " (a) "
+ Math.round(minx - cs.x + border)
+ " "
+ Math.round(cs.y - maxy + border)
+ " auto_transition");
} else {
Iterator it3 = automata.delta(s2).iterator();
while (it3.hasNext()) {
Transition t2 = (Transition) it3.next();
State s3 = (t2.end());
if (s3.equals(s)) {
ps.println(
"("
+ t.label()
+ ") q"
+ s
+ " q"
+ s2
+ " transition_courbe");
continue all_trans;
}
}
ps.println(
"("
+ t.label()
+ ") q"
+ s
+ " q"
+ s2
+ " transition_droite");
}
}
}
}
/**
* output data
*/
private void output() {
StringTokenizer st = new StringTokenizer(header, "\n\r");
while (st.hasMoreTokens()) {
String tok = st.nextToken();
java.lang.reflect.Method m =
(java.lang.reflect.Method) tokenMap.get(tok);
if (m != null)
try {
m.invoke(this, null);
} catch (java.lang.reflect.InvocationTargetException ex) {
System.err.println(
"Caught exception : " + ex.getTargetException());
ex.printStackTrace();
} catch (IllegalAccessException aex) {
System.err.println("Caught exception : " + aex);
} else
ps.println(tok);
}
}
///////////////////////////////////////////////////////
// IMPLEMENTATION OF OUTDISPLAYER
///////////////////////////////////////////////////////
public void display() throws ConverterException {
try {
algo.layout(automata);
while (!algo.done())
algo.work();
statesCoord = algo.getState();
output();
ps.close();
} catch (Exception ex) {
throw new ConverterException("Error :" + ex);
}
}
public void setAutomaton(Automaton a) {
automata = a;
}
public void setAlgorithm(
rationals.converters.algorithms.LayoutAlgorithm algo) {
this.algo = algo;
}
public void setOutputStream(java.io.OutputStream os)
throws java.io.IOException {
ps = new java.io.PrintStream(os);
}
public void setOutputFileName(String fname) throws java.io.IOException {
ps = new java.io.PrintStream(new java.io.FileOutputStream(fname));
}
/**
* Sets the output size in 1/72 inches
* @param width
* @param height
*/
public void setOutputFormat(int width,int height){
this.maxWidth = width;
this.maxHeight = height;
}
}