package latex; import graphexpr.ExprResolver; import graphexpr.GraphExpr; import graphexpr.MorphExpr; import graphexpr.NamedNodeExpr; import graphexpr.NodeExpr; import graphexpr.NodeVarExpr; import graphexpr.PatternExpr; import graphexpr.StmtExpr; import graphexpr.TripleExpr; import graphexpr.TripleVarExpr; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import pregroup.TypeLink; import pregroup.TypeReduction; import rdf.GraphString; import util.FileUtils; import xmllexicon.SemanticLexicon; //! Creates a TikZ output from a graph string public class TikzGraphExpr { public TikzGraphExpr() { try { tikzHeader = FileUtils.readFile("latex/header.tex"); } catch (IOException e) { System.err.println("Warning: failed to load the tikz header in latex/header.tex"); tikzHeader = "\begin{tikzpicture}\n% Generated by http://github.com/wetneb/MorozParser\n% Header missing\n\n"; e.printStackTrace(); } } private String tikzHeader = ""; private static final String tikzFooter = "\n\\end{tikzpicture}\n"; //! Vertical space between the types and the links below them private static final double distToTypes = 0.25; //! Horizontal space between two simple types in the same word private static final double typeSpacing = 0.4; //! Horizontal space between the types of two words private static final double wordSpacing = 1.1; //! Vertical space between the original words and their types private static final double textDistance = 3; //! Vertical space between the graph expressions and their types private static final double graphExprDist = 0.25; //! Vertical spacing of fresh nodes private static final double freshNodeDist = 0.5; //! Vertical spacing of morph nodes (alpha & beta) private static final double morphDist = 1; //! Vertical spacing of literal nodes private static final double literalDist= 0.2; //! Horizontal spacing of links in a S link private static final double distanceStatement = 0.15; public String drawLexicon(SemanticLexicon lex) { String output = "% Lexicon generated by http://github.com/wetneb/MorozParser\n"+ "\\paragraph{Assignments from Penn Treebank tags}\n\n"; output += drawHashMap(lex.getTagDict()); output += "\n\n\\paragraph{Assignments from surface form}\n\n"; output += drawHashMap(lex.getFormDict()); return output; } public String drawHashMap(HashMap<String, List<List<GraphExpr>>> lex) { String output = "\\begin{tabular}{l l c}\n"; Iterator it = lex.entrySet().iterator(); boolean firstKey = true; while(it.hasNext()) { if(firstKey) { output += "\\hline\n"; firstKey = false; } String key = (String)it.next(); List<List<GraphExpr>> val = lex.get(key); boolean firstRow = true; for(List<GraphExpr> semType : val) { if(firstRow) { output += key; firstRow = false; } // Print the syntactic type String syntacticType = "", semVal = ""; for(GraphExpr simpleExpr : semType) { syntacticType += simpleExpr.getType().toLatex(); } semVal = ""; // TODO drawGraphExpr() output += " & $"+syntacticType+"$ &\n"+semVal+" \\\\\n"; } output += "\\hline\n"; } output += "\\end{tabular}\n"; return output; } public String draw(GraphString phrase, List<String> words, TypeReduction red, ExprResolver res, boolean expand) { int n = phrase.size(); double[] pos = new double[n]; boolean[] used = new boolean[n]; String output = tikzHeader; //! Compute the used types for(int i = 0; i < n; i++) used[i] = false; for(TypeLink l : red) { used[l.start] = true; used[l.end] = true; } //! Compute the position of the used types double curPos = 0; int curWord = 0; int nbTypes = 0; double sumPos = 0; for(int i = 0; i < n-1; i++) { if(used[i]) { pos[i] = curPos; sumPos += curPos; nbTypes++; output += "\\node at ("+curPos+",0) (t"+i+") {$"+phrase.get(i).toLatex()+"$};\n"; curPos += typeSpacing; } else if(phrase.get(i).isRB() && nbTypes > 0) { output += "\\node at ("+(sumPos/nbTypes)+","+textDistance+") (w"+curWord+") {"+words.get(curWord)+"};\n"; curWord++; sumPos = 0; nbTypes = 0; curPos += wordSpacing-typeSpacing; } } //! Compute the maximum radius double maxRad = 0; for(TypeLink l : red) { double rad = (pos[l.end] - pos[l.start])/2.; if(rad > maxRad && l.end != n-1) maxRad = rad; } output += "\n\n% Curves below :\n\n"; //! Draw the curves for(TypeLink l : red) { double from = pos[l.start]; double to = pos[l.end]; if(l.end != n-1) { if(phrase.get(l.start).isS()) output += drawSLink(from, to, -distToTypes, false); else output += drawLoop(from, to, distToTypes, false); output += "\n"; } else { output += "\\draw ("+(from-distanceStatement)+",-"+distToTypes+") -- (" +(from-distanceStatement)+",-"+(distToTypes+maxRad)+");\n" + "\\draw ("+from+",-"+distToTypes+") -- ("+from+",-"+(distToTypes+maxRad)+");\n" + "\\draw ("+(from+distanceStatement)+",-"+distToTypes+") -- ("+ (from+distanceStatement)+",-"+(distToTypes+maxRad)+");\n"; } } output += "\n% Curves above:\n\n"; //! Draw the graph expressions for(int i = 0; i < n-1; i++) { if(used[i] && phrase.get(i).isProducer()) { output += drawGraphExpr(phrase.getPattern(i), res, pos, pos[i], graphExprDist, expand); output += "\n"; } } output += tikzFooter; return output; } private static String drawLoop(double from, double to, double height, boolean over) { if(to < from) { double tmp = from; from = to; to = tmp; } double radius = (to - from)/2.; double center = (from + to) /2.; double vradius = (over ? -radius : radius); return "\\draw ("+from+",-"+height+")"+ " .. controls ("+from+",-"+(height+0.555*vradius)+")"+ " and ("+(center-0.555*radius)+",-"+(height+vradius)+")"+ " .. ("+center+",-"+(height+vradius)+")"+ " .. controls ("+(center+0.555*radius)+",-"+(height+vradius)+")"+ " and ("+to+",-"+(height+0.555*vradius)+")"+ " .. ("+to+",-"+height+");\n"; } private static String drawGraphExpr(PatternExpr p, ExprResolver res, double[] pos, double nodePos, double height, boolean expand) { if(p.isNode()) return drawNodeExpr((NodeExpr)p,res,pos,nodePos,height,expand); else if(p.isStmt()) return drawStmtExpr((StmtExpr)p,res,pos,nodePos,height,expand); return ""; } private static String drawNodeExpr(NodeExpr n, ExprResolver res, double[] pos, double nodePos, double height, boolean expand) { if(n.isVar()) { NodeVarExpr nv = (NodeVarExpr)n; if(expand) return drawNodeExpr((NodeExpr) res.get(nv.id), res,pos,nodePos,height,expand); else return drawVarLink(nodePos, pos[nv.id], height); } else if(n.isFresh() || n.isNamedNode() || n.isLiteral()) { double dist = 0; String size = "medium-triangle"; if(n.isFresh() || n.isNamedNode()) dist = freshNodeDist; else { dist = literalDist; size="small-triangle"; } return "\\node["+size+"] at (" +nodePos+","+(height+dist)+") (fresh) {};\n"+ "\\draw ("+nodePos+","+height+") -- (fresh);\n"; } else if(n.isNamedNode()) { return "\\node[circle,draw] at ("+nodePos+","+(height+freshNodeDist)+") (named) {};\n"+ "\\draw ("+nodePos+","+height+") -- (named);\n"; } else if(n.isMorph()) { MorphExpr m = (MorphExpr)n; String start = "\\node[rectangle,draw] at ("+nodePos+","+(height+morphDist)+")"+ " (morph) {$"+(m.isReify ? "\\beta" : "\\alpha")+"$};\n"+ "\\draw ("+nodePos+","+height+") -- (morph);\n"; return start+drawStmtExpr(m.triple,res,pos,nodePos,height+morphDist+0.25, expand); } return ""; } private static String drawStmtExpr(StmtExpr s, ExprResolver res, double[] pos, double nodePos, double height, boolean expand) { if(s.isTriple()) { TripleExpr t = (TripleExpr)s; double pos_1 = nodePos - distanceStatement; double pos_2 = nodePos; double pos_3 = nodePos + distanceStatement; return drawNodeExpr(t.subj, res, pos, pos_1, height, expand) + drawNodeExpr(t.prop, res, pos, pos_2, height, expand) + drawNodeExpr(t.obj, res, pos, pos_3, height, expand); } else if(s.isVar()) { TripleVarExpr v = (TripleVarExpr)s; double to = pos[v.id]; return drawSLink(nodePos, to, height, true); } return ""; } private static String drawVarLink(double from, double to, double height) { String output = drawLoop(from, to, -height, true); output += "\\draw ("+to+","+height+") -- ("+to+","+graphExprDist+");\n"; return output; } private static String drawSLink(double from, double to, double height, boolean above) { if(above) return drawVarLink(from - distanceStatement, to + distanceStatement, height) + drawVarLink(from, to, height) + drawVarLink(from + distanceStatement, to - distanceStatement, height); else return drawLoop(from - distanceStatement, to + distanceStatement, -height, false) + drawLoop(from, to, -height, false) + drawLoop(from + distanceStatement, to - distanceStatement, -height, false); } }