package com.github.lindenb.jvarkit.util; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JOptionPane; import javafx.scene.canvas.GraphicsContext; public class Hershey { private static final Logger LOG=Logger.getLogger("jvarkit"); private double scalex=15; private double scaley=15; private enum Op {MOVETO,LINETO}; private AffineTransform affineTransform=new AffineTransform(); private static class PathOp { Op operator; double x; double y; @Override public String toString() { return "("+operator+","+x+","+y+")"; } } private static final String LETTERS[]=new String[]{ " 9MWRMNV RRMVV RPSTS", " 16MWOMOV ROMSMUNUPSQ ROQSQURUUSVOV", " 11MXVNTMRMPNOPOSPURVTVVU", " 12MWOMOV ROMRMTNUPUSTURVOV", " 12MWOMOV ROMUM ROQSQ ROVUV", " 9MVOMOV ROMUM ROQSQ", " 15MXVNTMRMPNOPOSPURVTVVUVR RSRVR", " 9MWOMOV RUMUV ROQUQ", " 3PTRMRV", " 7NUSMSTRVPVOTOS", " 9MWOMOV RUMOS RQQUV", " 6MVOMOV ROVUV", " 12LXNMNV RNMRV RVMRV RVMVV", " 9MWOMOV ROMUV RUMUV", " 14MXRMPNOPOSPURVSVUUVSVPUNSMRM", " 10MWOMOV ROMSMUNUQSROR", " 17MXRMPNOPOSPURVSVUUVSVPUNSMRM RSTVW", " 13MWOMOV ROMSMUNUQSROR RRRUV", " 13MWUNSMQMONOOPPTRUSUUSVQVOU", " 6MWRMRV RNMVM", " 9MXOMOSPURVSVUUVSVM", " 6MWNMRV RVMRV", " 12LXNMPV RRMPV RRMTV RVMTV", " 6MWOMUV RUMOV", " 7MWNMRQRV RVMRQ", " 9MWUMOV ROMUM ROVUV" }; private static final String DIGITS[]=new String[]{ " 12MWRMPNOPOSPURVTUUSUPTNRM", " 4MWPORMRV", " 9MWONQMSMUNUPTROVUV", " 15MWONQMSMUNUPSQ RRQSQURUUSVQVOU", " 7MWSMSV RSMNSVS", " 14MWPMOQQPRPTQUSTURVQVOU RPMTM", " 14MWTMRMPNOPOSPURVTUUSTQRPPQOS", " 6MWUMQV ROMUM", " 19MWQMONOPQQSQUPUNSMQM RQQOROUQVSVUUURSQ", " 14MWUPTRRSPROPPNRMTNUPUSTURVPV" }; private Set<Character> undefined=new HashSet<Character>(); private String charToHersheyString(char c) { if(Character.isLetter(c)) { return LETTERS[Character.toUpperCase(c)-'A']; } if(Character.isDigit(c)) { return DIGITS[Character.toUpperCase(c)-'0']; } switch(c) { case '.': return " 6PURURVSVSURU";//210 case ',': return " 7PUSVRVRUSUSWRY";//211 case ':': return " 12PURPRQSQSPRP RRURVSVSURU";//212 case ';': return " 13PURPRQSQSPRP RSVRVRUSUSWRY";//213 case '!': return " 12PURMRR RSMSR RRURVSVSURU";//214 case '?': return " 17NWPNRMSMUNUPRQRRSRSQUP RRURVSVSURU";//215 case '\'':return " 3PTRMRQ";//216 case '\"':return " 6NVPMPQ RTMTQ";//217 case '/': return " 3MWVLNW";//220 case '(': return " 7OVTLRNQPQSRUTW";//221 case ')': return " 7NUPLRNSPSSRUPW";//222 case '|': return " 3PTRLRW";//223 case '#': return " 12MXRLPW RULSW ROPVP ROSVS";//233 case '*': return " 9JZRLRX RMOWU RWOMU";//728 case '=': return " 6LXNPVP RNTVT";//226 case '-': return " 3KYKRYR";//806 case '_': return " 3JZJZZZ";//998 case '[': return " 12MWPHP\\ RQHQ\\ RPHUH RP\\U\\";//1223 case ']': return " 12MWSHS\\ RTHT\\ ROHTH RO\\T\\";//1224 case '{': return " 38LWSHQIPJPLRNSP RQIPL RSNRQ RPJQLSNSPRQPRRSSTSVQXPZ RRSSV RPXQ[ RSTRVPXPZQ[S\\"; case '}': return " 38MXQHSITJTLRNQP RSITL RQNRQ RTJSLQNQPRQTRRSQTQVSXTZ RRSQV RTXS[ R QTRVTXTZS[Q\\"; default: { if(!undefined.contains(c)) { LOG.info("missing hershey char: "+c); undefined.add(c); } return null; } } } private Map<Character, List<PathOp>> letter2path=new HashMap<Character, List<PathOp>>(); public AffineTransform getTransform() { return this.affineTransform; } public void setTransform(AffineTransform tr) { if(tr==null) tr=new AffineTransform(); this.affineTransform=tr; } private PathOp transform(PathOp op) { Point2D p=new Point2D.Double(op.x,op.y); getTransform().transform(p,p); PathOp op2=new PathOp(); op2.operator=op.operator; op2.x=p.getX(); op2.y=p.getY(); return op2; } private List<PathOp> charToPathOp(char letter) { int i; if(letter==' ') return Collections.emptyList(); List<PathOp> array=letter2path.get(letter); if(array!=null) return array; String s=this.charToHersheyString(letter); if(s==null) return Collections.emptyList(); int num_vertices=0; for( i=0;i< 3;++i) { char c=s.charAt(i); if(Character.isSpaceChar(c)) continue; num_vertices = num_vertices*10+(c-'0'); } num_vertices--; i+=2; int nop=0; array=new ArrayList<Hershey.PathOp>(num_vertices); while(nop<num_vertices) { PathOp pathOp=new PathOp(); pathOp.operator=(array.isEmpty()?Op.MOVETO:Op.LINETO); char c=s.charAt(i++); if(c==' ') { c=s.charAt(i++); if(c!='R') throw new IllegalArgumentException(s); nop++; pathOp.operator=Op.MOVETO; c=s.charAt(i++); } pathOp.x=c-'R'; c=s.charAt(i++); pathOp.y=c-'R'; nop++; array.add(pathOp); } letter2path.put(letter, array); return array; } public void paint( Graphics2D g, String s, Shape shape ) { Rectangle2D r=shape.getBounds2D(); paint(g,s,r.getX(),r.getY(),r.getWidth(),r.getHeight()); } public void paint( Graphics2D g, String s, double x, double y, double width, double height ) { if(s.isEmpty() || width==0 || height==0) return; double dx=width/s.length(); for(int i=0;i < s.length();++i) { List<PathOp> array=charToPathOp(s.charAt(i)); for(int n=0;n< array.size();++n) { PathOp p2=transform(array.get(n)); if(p2.operator==Op.MOVETO) continue; PathOp p1=transform(array.get(n-1)); double x1=(p1.x/this.scalex)*dx + x+dx*i +dx/2.0; double y1=(p1.y/this.scaley)*height +y +height/2.0 ; double x2=(p2.x/this.scalex)*dx + x+dx*i +dx/2.0; double y2=(p2.y/this.scaley)*height +y +height/2.0 ; g.draw(new Line2D.Double(x1, y1, x2, y2)); } } } public void paint( final GraphicsContext g, final String s, final double x, final double y, final double width, final double height ) { if(s.isEmpty() || width==0 || height==0) return; double dx=width/s.length(); for(int i=0;i < s.length();++i) { List<PathOp> array=charToPathOp(s.charAt(i)); for(int n=0;n< array.size();++n) { PathOp p2=transform(array.get(n)); if(p2.operator==Op.MOVETO) continue; PathOp p1=transform(array.get(n-1)); double x1=(p1.x/this.scalex)*dx + x+dx*i +dx/2.0; double y1=(p1.y/this.scaley)*height +y +height/2.0 ; double x2=(p2.x/this.scalex)*dx + x+dx*i +dx/2.0; double y2=(p2.y/this.scaley)*height +y +height/2.0 ; g.strokeLine(x1, y1, x2, y2); } } } public String svgPath( String s, Shape shape ) { Rectangle2D r=shape.getBounds2D(); return svgPath(s,r.getX(),r.getY(),r.getWidth(),r.getHeight()); } public String svgPath( String s, double x, double y, double width, double height ) { StringWriter sw=new StringWriter(); if(s.isEmpty() || width==0 || height==0) return ""; double dx=width/s.length(); for(int i=0;i < s.length();++i) { List<PathOp> array=charToPathOp(s.charAt(i)); for(int n=0;n< array.size();++n) { PathOp p2=transform(array.get(n)); double x2=(p2.x/this.scalex)*dx + x+dx*i +dx/2.0; double y2=(p2.y/this.scaley)*height +y +height/2.0 ; switch(p2.operator) { case LINETO: sw.append("L ");break; case MOVETO: sw.append("M ");break; default: throw new IllegalStateException(); } sw.append(String.format("%.3f",x2)); sw.append(" "); sw.append(String.format("%.3f",y2)); sw.append(" "); } } //sw.append("z"); return sw.toString(); } public static void main(String[] args) { BufferedImage img=new BufferedImage(300, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g=img.createGraphics(); g.setColor(Color.RED); new Hershey().paint(g,"01234567891",0,0,300,100); g.dispose(); JOptionPane.showMessageDialog(null,new JLabel(new ImageIcon(img))); } }