Subversion Repositories wimsdev

Rev

Rev 7307 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     Sketch Elements: Chemistry molecular diagram drawing tool.
  3.    
  4.     (c) 2008 Dr. Alex M. Clark
  5.    
  6.     Released as GNUware, under the Gnu Public License (GPL)
  7.    
  8.     See www.gnu.org for details.
  9. */
  10.  
  11. package WIMSchem;
  12.  
  13. import java.util.*;
  14. import java.text.*;
  15. import java.awt.*;
  16. import java.awt.geom.*;
  17.  
  18. /*
  19.     Wrapper class for molecule drawing, which renders the molecule to a Java Graphics2D context, under the assumption
  20.     that it is going to be displayed on the screen. This class also has options to draw lots of extra stuff on top of the
  21.     basic molecule construction.
  22. */
  23.  
  24. public class DrawMolecule implements ArrangeMeasurement
  25. {
  26.     // public constants
  27.  
  28.     public static final double DEFSCALE=20; // default # Angstroms per pixel
  29.  
  30.     // private data
  31.    
  32.     private Molecule mol;
  33.     private Graphics2D g;
  34.     private ArrangeMolecule arrmol=null;
  35.  
  36.     private final double ASCENT_FUDGE=0.8; // for some #@!& reason, the ascent reserves quite a lot of room at the top
  37.  
  38.     private Color backgr=Color.WHITE;
  39.     private Color colHighlight=null,colSelected=null,colDragged=null;
  40.    
  41.     private double offsetX=0,offsetY=0; // in Angstrom units
  42.     private double scale=DEFSCALE; // pixels per Angstrom
  43.  
  44.     private boolean[] selected=null,dragged=null;
  45.     private int highlightAtom=0,highlightBond=0;
  46.  
  47.     private boolean bondInProgress=false;
  48.     private int bipFrom,bipOrder,bipType;
  49.     private double bipToX,bipToY;
  50.    
  51.     private boolean atomInProgress=false;
  52.     private String aipLabel;
  53.     private double aipToX,aipToY;
  54.    
  55.     private boolean newBondLine=false;
  56.     private double nblX1,nblY1,nblX2,nblY2;
  57.  
  58.     private boolean dragSelect=false;
  59.     private int dslX1,dslY1,dslX2,dslY2;
  60.    
  61.     private boolean dragScale=false;
  62.     private double dscCX,dscCY,dscExtMul;
  63.  
  64.     private boolean dragMove=false;
  65.     private double dmvDX,dmvDY;
  66.     private boolean dmvCopy;
  67.    
  68.     private boolean dragRotate=false;
  69.     private double droTheta;
  70.     private int droX,droY;
  71.    
  72.     private boolean outlineTemplate=false;
  73.     private Molecule oltMol;
  74.  
  75.     // constructor
  76.  
  77.     public DrawMolecule(Molecule Mol,Graphics2D Gr,double Scale)
  78.     {
  79.         mol=Mol;
  80.         g=Gr;
  81.         scale=Scale;
  82.  
  83.         arrmol=new ArrangeMolecule(mol,this);
  84.         arrmol.setDevRounding(true);
  85.     }
  86.    
  87.     // methods to provide other drawing options...
  88.    
  89.     public void setOffset(double OX,double OY) {offsetX=OX; offsetY=OY;}
  90.     public void setShowMode(int Mode) {arrmol.setElementMode(Mode);}
  91.     public void setShowHydr(boolean ShowH) {arrmol.setShowHydrogens(ShowH);}
  92.     public void setShowStereo(boolean ShowSter) {arrmol.setAnnotRS(ShowSter); arrmol.setAnnotEZ(ShowSter);}
  93.     public void setBackground(Color Col) {backgr=Col;}
  94.     public void setSelected(boolean[] Selected,boolean[] Dragged) {selected=Selected; dragged=Dragged;}
  95.     public void setHighlight(int Atom,int Bond) {highlightAtom=Atom; highlightBond=Bond;}
  96.  
  97.     public void bondInProgress(int From,double X,double Y,int Order,int Type)
  98.     {
  99.         bondInProgress=true;
  100.         bipFrom=From;
  101.         bipToX=X;
  102.         bipToY=Y;
  103.         bipOrder=Order;
  104.         bipType=Type;
  105.     }
  106.     public void atomInProgress(String Label,double X,double Y)
  107.     {
  108.         atomInProgress=true;
  109.         aipLabel=Label;
  110.         aipToX=X;
  111.         aipToY=Y;
  112.     }
  113.     public void newBondLine(double X1,double Y1,double X2,double Y2)
  114.     {
  115.         // !! bond order and type should be accounted for...
  116.         newBondLine=true;
  117.         nblX1=X1;
  118.         nblY1=Y1;
  119.         nblX2=X2;
  120.         nblY2=Y2;
  121.     }
  122.     public void dragSelect(int X1,int Y1,int X2,int Y2)
  123.     {
  124.         dragSelect=true;
  125.         dslX1=X1;
  126.         dslY1=Y1;
  127.         dslX2=X2;
  128.         dslY2=Y2;
  129.     }
  130.     public void dragScale(double CX,double CY,double ExtMul)
  131.     {
  132.         dragScale=true;
  133.         dscCX=CX;
  134.         dscCY=CY;
  135.         dscExtMul=ExtMul;
  136.     }
  137.     public void dragMove(double DX,double DY,boolean Copy)
  138.     {
  139.         dragMove=true;
  140.         dmvDX=DX;
  141.         dmvDY=DY;
  142.         dmvCopy=Copy;
  143.     }
  144.     public void dragRotate(double Theta,int X,int Y)
  145.     {
  146.         dragRotate=true;
  147.         droTheta=Theta;
  148.         droX=X;
  149.         droY=Y;
  150.     }
  151.     public void outlineTemplate(Molecule Templ)
  152.     {
  153.         outlineTemplate=true;
  154.         oltMol=Templ;
  155.     }
  156.    
  157.     // perform the actual drawing
  158.    
  159.     public void draw()
  160.     {
  161.         arrmol.arrange();      
  162.         setupColours();
  163.         drawBacklighting();
  164.         drawBonds();
  165.         drawAtoms();
  166.         drawEffects();
  167.         drawCorrections();
  168.         if(MainApplet.USER_SELECTION){
  169.             drawAtomSelection();
  170.         }
  171.     }
  172.    
  173.     public ArrangeMolecule arrangement() {return arrmol;}
  174.  
  175.     // private implementations
  176.    
  177.     // decides on particular colors
  178.     private void setupColours()
  179.     {
  180.         if (colHighlight==null) colHighlight=backgr.darker();
  181.         if (colSelected==null) colSelected=new Color(colHighlight.getRed(),colHighlight.getGreen(),255);
  182.         if (colDragged==null) colDragged=new Color(colHighlight.getRed(),192,255);
  183.     }
  184.  
  185.     // draws various types of highlighting, if appropriate
  186.     private void drawBacklighting()
  187.     {
  188.         for (int n=1;n<=mol.numAtoms();n++)
  189.         {          
  190.             boolean drag=false;
  191.             if (dragged!=null) drag=dragged[n-1];
  192.            
  193.             if (selected[n-1] || n==highlightAtom || drag)
  194.             {
  195.                 g.setColor(selected[n-1] ? colSelected : drag ? colDragged : colHighlight);
  196.                 double cx=arrmol.pointCX(n-1),cy=arrmol.pointCY(n-1),rw=arrmol.pointRW(n-1),rh=arrmol.pointRH(n-1);
  197.                 double ext=rw==0 && rh==0 ? 0.25*scale : Math.max(rw*1.5,rh*1.5);
  198.                 g.fill(new Ellipse2D.Double(cx-ext,cy-ext,2*ext,2*ext));
  199.             }
  200.         }
  201.  
  202.         if (highlightBond!=0) for (int n=0;n<arrmol.numLines();n++) if (arrmol.lineBNum(n)==highlightBond)
  203.         {
  204.             double x1=arrmol.lineX1(n),y1=arrmol.lineY1(n),x2=arrmol.lineX2(n),y2=arrmol.lineY2(n);
  205.             double sz=arrmol.lineSize(n)+0.10*scale;
  206.             double dx=x2-x1,dy=y2-y1,norm=sz/Math.sqrt(dx*dx+dy*dy);
  207.             double ox=norm*dy,oy=-norm*dx;
  208.             Polygon pgn=new Polygon();
  209.             pgn.addPoint(Util.iround(x1+oy*0.5),Util.iround(y1-ox*0.5));
  210.             pgn.addPoint(Util.iround(x1-ox),Util.iround(y1-oy));
  211.             pgn.addPoint(Util.iround(x2-ox),Util.iround(y2-oy));
  212.             pgn.addPoint(Util.iround(x2-oy*0.5),Util.iround(y2+ox*0.5));
  213.             pgn.addPoint(Util.iround(x2+ox),Util.iround(y2+oy));
  214.             pgn.addPoint(Util.iround(x1+ox),Util.iround(y1+oy));
  215.             g.setColor(colHighlight);
  216.             g.fill(pgn);
  217.         }
  218.     }
  219.  
  220.     // draws all of the bonds
  221.     private void drawBonds()
  222.     {
  223.         for (int n=0;n<arrmol.numLines();n++){
  224.             int btype=arrmol.lineType(n);
  225.             double x1=arrmol.lineX1(n),y1=arrmol.lineY1(n),x2=arrmol.lineX2(n),y2=arrmol.lineY2(n);
  226.             double dx=x2-x1,dy=y2-y1;
  227.             int factor = 1;/* multiply linewidth when user selected */
  228.             g.setColor(new Color(arrmol.lineCol(n)));
  229.             if( MainApplet.USER_SELECTION ){
  230.                 if( EditorPane.bondselection.length > mol.numBonds()){ // userbased click selection
  231.                     if( EditorPane.bondselection[arrmol.lineBNum(n)] ){
  232.                         g.setColor(MainApplet.BOND_SELECT_COLOR);
  233.                         factor = 2;
  234.                     }
  235.                     else
  236.                     {
  237.                         g.setColor(new Color(arrmol.lineCol(n)));
  238.                         factor = 1;
  239.                     }
  240.                 }
  241.                 else
  242.                 {
  243.                     EditorPane.bondselection = EditorPane.GrowArray(EditorPane.bondselection,mol.numBonds()+32);
  244.                 }
  245.             }
  246.             else
  247.             {
  248.                 if( MainApplet.ExternalBondSelection != null ){ // for answer molecule ; set by params
  249.                     for(int i=0;i<MainApplet.ExternalBondSelection.length;i++){
  250.                         if( n == MainApplet.ExternalBondSelection[i]){
  251.                             g.setColor(MainApplet.SelectedBondColorArray[i]);
  252.                             factor = 2;
  253.                         }
  254.                     }
  255.                 }
  256.             }
  257.             if (btype==ArrangeMolecule.BLINE_NORMAL){
  258.                 g.setStroke(new BasicStroke( (float) (factor*(arrmol.lineSize(n))) ,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
  259.                 g.draw(new Line2D.Double(x1,y1,x2,y2));
  260.             }
  261.             else if (btype==ArrangeMolecule.BLINE_INCLINED)
  262.             {
  263.                 double norm=factor*0.15*scale/Math.sqrt(dx*dx+dy*dy);
  264.                 double ox=norm*dy,oy=-norm*dx;
  265.                 Path2D path=new Path2D.Double();
  266.                 path.moveTo(x1,y1);
  267.                 path.lineTo(x2-ox,y2-oy);
  268.                 path.lineTo(x2+ox,y2+oy);
  269.                 g.fill(path);
  270.             }
  271.             else if (btype==ArrangeMolecule.BLINE_DECLINED)
  272.             {
  273.                 int nsteps=(int)Math.ceil(Math.sqrt(dx*dx+dy*dy)*0.15);
  274.                 double norm=0.15*scale/Math.sqrt(dx*dx+dy*dy);
  275.                 double ox=norm*dy,oy=-norm*dx;
  276.                 for (int i=0;i<=nsteps+1;i++)
  277.                 {
  278.                     double cx=x1+i*dx/(nsteps+1),cy=y1+i*dy/(nsteps+1);
  279.                     double ix=ox*i/(nsteps+1),iy=oy*i/(nsteps+1);
  280.                     g.setStroke(new BasicStroke((float)(factor*0.05*scale)));
  281.                     g.draw(new Line2D.Double(cx-ix,cy-iy,cx+ix,cy+iy));
  282.                 }
  283.             }
  284.             else if (btype==ArrangeMolecule.BLINE_UNKNOWN)
  285.             {
  286.                 int nsteps=(int)Math.ceil(Math.sqrt(dx*dx+dy*dy)*0.2);
  287.                 double norm=0.2*scale/Math.sqrt(dx*dx+dy*dy);
  288.                 double ox=norm*dy,oy=-norm*dx;
  289.                 for (int i=0;i<=nsteps;i++)
  290.                 {
  291.                     double ax=x1+i*dx/(nsteps+1),ay=y1+i*dy/(nsteps+1);
  292.                     double cx=x1+(i+1)*dx/(nsteps+1),cy=y1+(i+1)*dy/(nsteps+1);
  293.                     double bx=(ax+cx)/2,by=(ay+cy)/2;
  294.                     int sign=i%2==0 ? 1 : -1;
  295.                     g.setStroke(new BasicStroke((float)(factor*0.05*scale)));
  296.                     g.draw(new QuadCurve2D.Double(ax,ay,bx+sign*ox,by+sign*oy,cx,cy));
  297.                 }
  298.             }
  299.             else if (btype==ArrangeMolecule.BLINE_DOTTED)
  300.             {
  301.                 int nsteps=(int)Math.ceil(Math.sqrt(dx*dx+dy*dy)*0.10);
  302.                 float radius=(float)arrmol.lineSize(n);
  303.                 for (int i=0;i<=nsteps+1;i++)
  304.                 {
  305.                     double cx=x1+i*dx/(nsteps+1),cy=y1+i*dy/(nsteps+1);
  306.                     g.fill(new Ellipse2D.Double(cx-radius,cy-radius,2*radius,2*radius));
  307.                 }
  308.             }
  309.         }
  310.  
  311.         if (bondInProgress)
  312.         {
  313.             double x1=arrmol.pointCX(bipFrom-1),y1=arrmol.pointCY(bipFrom-1),x2=angToX(bipToX),y2=angToY(bipToY);
  314.             double dx=x2-x1,dy=y2-y1,norm=0.20*scale/Math.sqrt(dx*dx+dy*dy);
  315.             double ox=norm*dy,oy=-norm*dx;
  316.  
  317.             g.setColor(colHighlight);
  318.  
  319.             if (bipType==Molecule.BONDTYPE_INCLINED || bipType==Molecule.BONDTYPE_DECLINED)
  320.             {
  321.                 Path2D path=new Path2D.Double();
  322.                 path.moveTo(x1,y1);
  323.                 path.lineTo(x2-ox,y2-oy);
  324.                 path.lineTo(x2+ox,y2+oy);
  325.                 path.closePath();
  326.                 if (bipType==Molecule.BONDTYPE_INCLINED) g.fill(path); else g.draw(path);
  327.             }
  328.             else if (bipType==Molecule.BONDTYPE_UNKNOWN || bipOrder==0)
  329.             {
  330.                 int nsteps=(int)Math.ceil(Math.sqrt(dx*dx+dy*dy)*0.15);
  331.                 float radius=(float)(0.15*scale);
  332.                 for (int i=0;i<=nsteps+1;i++)
  333.                 {
  334.                     double cx=x1+i*dx/(nsteps+1),cy=y1+i*dy/(nsteps+1);
  335.                     g.fill(new Ellipse2D.Double(cx-0.05*scale,cy-0.05*scale,radius,radius));
  336.                 }
  337.             }
  338.             else
  339.             {
  340.                 double v=-0.5*(bipOrder-1);
  341.                 g.setStroke(new BasicStroke((float)(0.1*scale),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
  342.                
  343.                 for (int i=0;i<bipOrder;i++,v++)
  344.                     g.draw(new Line2D.Double(x1+ox*v,y1+oy*v,x2+ox*v,y2+oy*v));
  345.                
  346.             }
  347.         }
  348.     }
  349.    
  350.     // draw the atoms proper, as well as standard annotations such as hydrogen atoms and charges
  351.     private void drawAtoms()
  352.     {
  353.         for (int n=0;n<arrmol.numPoints();n++)
  354.         {
  355.             String txt=arrmol.pointText(n);
  356.             if (txt==null) continue; // is a point, so do not draw anything
  357.            
  358.             Font font=new Font("SansSerif",arrmol.pointBold(n) ? Font.BOLD : Font.PLAIN,Util.iround(arrmol.pointFontSize(n)));
  359.             g.setFont(font);
  360.             FontMetrics fm=g.getFontMetrics();
  361.             double ascent=ASCENT_FUDGE*fm.getAscent();
  362.             float cx=(float)(arrmol.pointCX(n)-0.5*fm.stringWidth(txt)),cy=(float)(arrmol.pointCY(n)+0.5*ascent);
  363.             g.setColor(new Color(arrmol.pointCol(n)));
  364.             g.drawString(txt,cx,cy);
  365.  
  366.         }
  367.         if(MainApplet.ExternalAtomSelection !=  null){
  368.             for (int n=0;n<arrmol.numPoints();n++){
  369.                 for(int i=0;i<MainApplet.ExternalAtomSelection.length;i++){
  370.                     if( n == MainApplet.ExternalAtomSelection[i]){
  371.                         g.setColor(MainApplet.SelectedAtomColorArray[i]);
  372.                         g.fill(circle(arrmol.pointCX(n-1),arrmol.pointCY(n-1),0.5*scale));
  373.                     }
  374.                 }    
  375.             }
  376.         }
  377.     }
  378. /******** jm.ever 2013 **********/
  379.     private void drawAtomSelection(){
  380.         g.setColor(MainApplet.ATOM_SELECT_COLOR);
  381.         int len1 = mol.numAtoms();
  382.         int len2 = (EditorPane.atomselection).length;
  383.         if( len2 < len1 - 2){EditorPane.atomselection = EditorPane.GrowArray(EditorPane.atomselection,len1+8);}
  384.         for(int n = 1 ; n < len2 ; n++){
  385.             if (EditorPane.atomselection[n]){
  386.                 g.fill(circle(arrmol.pointCX(n-1),arrmol.pointCY(n-1),0.5*scale));
  387.                 //System.out.println("selected n ="+n+" text = "+arrmol.pointText(n));
  388.             }
  389.         }
  390.     }
  391.  
  392.  
  393. /*********************************/
  394.  
  395.     // draw the effects of various editing-in-progress actions
  396.     private void drawEffects()
  397.     {
  398.         if (atomInProgress)
  399.         {
  400.             g.setColor(Color.BLUE);
  401.             Font font=new Font("SansSerif",Font.PLAIN,Util.iround(arrmol.getFontSizeDev()));
  402.             g.setFont(font);
  403.             double tx=angToX(aipToX),ty=angToY(aipToY);
  404.             double[] wad=measureText(aipLabel,arrmol.getFontSizeDev());
  405.             g.drawString(aipLabel,(float)(tx-0.5*wad[0]),(float)(ty+0.4*wad[1]));
  406.         }
  407.  
  408.         if (newBondLine)
  409.         {
  410.             g.setColor(colHighlight);
  411.             g.draw(new Line2D.Double(angToX(nblX1),angToY(nblY1),angToX(nblX2),angToY(nblY2)));
  412.         }
  413.        
  414.         if (dragSelect)
  415.         {
  416.             g.setXORMode(Color.YELLOW);
  417.             int x=dslX1,y=dslY1,w=dslX2-dslX1,h=dslY2-dslY1;
  418.             if (w<0) {w=-w; x-=w;}
  419.             if (h<0) {h=-h; y-=h;}
  420.             g.drawRect(x,y,w,h);
  421.             g.setXORMode(Color.BLACK);
  422.         }
  423.        
  424.         if (dragScale)
  425.         {
  426.             g.setColor(Color.BLACK);
  427.             g.setStroke(new BasicStroke(1.1F));
  428.             for (int n=1;n<=mol.numAtoms();n++) if (selected[n-1])
  429.             {
  430.                 double sx=angToX((mol.atomX(n)-dscCX)*dscExtMul+dscCX),sy=angToY((mol.atomY(n)-dscCY)*dscExtMul+dscCY);
  431.                 g.draw(new Ellipse2D.Double(sx-scale*0.3,sy-scale*0.3,scale*0.6,scale*0.6));
  432.             }
  433.         }
  434.        
  435.         if (dragMove)
  436.         {
  437.             g.setColor(Color.BLACK);
  438.             g.setStroke(new BasicStroke(1.1F));
  439.             for (int n=1;n<=mol.numAtoms();n++) if (selected[n-1])
  440.             {
  441.                 double sx=arrmol.pointCX(n-1)+dmvDX,sy=arrmol.pointCY(n-1)+dmvDY;
  442.                 g.draw(new Ellipse2D.Double(sx-scale*0.3,sy-scale*0.3,scale*0.6,scale*0.6));
  443.                 if (dmvCopy)
  444.                 {
  445.                     g.draw(new Line2D.Double(sx-scale*0.15,sy,sx+scale*0.15,sy));
  446.                     g.draw(new Line2D.Double(sx,sy-scale*0.15,sx,sy+scale*0.15));
  447.                 }
  448.             }
  449.         }
  450.        
  451.         if (dragRotate)
  452.         {
  453.             double thrad=droTheta*Math.PI/180;
  454.             g.setColor(Color.RED);
  455.             g.setStroke(new BasicStroke(0.5F,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND,1F,new float[]{2,2},0));
  456.             g.draw(new Line2D.Double(droX,droY,droX+50,droY));
  457.             g.setStroke(new BasicStroke(1F));
  458.             g.draw(new Line2D.Double(droX,droY,droX+50*Math.cos(-thrad),droY+50*Math.sin(-thrad)));
  459.             g.draw(new Arc2D.Double(droX-20,droY-20,40,40,0,droTheta,Arc2D.OPEN));
  460.  
  461.             Font font=new Font("SansSerif",Font.PLAIN,12);
  462.             g.setFont(font);
  463.  
  464.             int ty=(int)((droTheta>25 || (droTheta<0 && droTheta>=-25)) ? droY-5 : droY+5+ASCENT_FUDGE*font.getSize());
  465.             DecimalFormat fmt=new DecimalFormat("0");
  466.             g.drawString((droTheta>0?"+":"")+fmt.format(Math.round(droTheta))+"\u00B0",(int)(droX+25),ty);
  467.            
  468.             double ax=xToAng(droX),ay=yToAng(droY);
  469.             g.setStroke(new BasicStroke(1.1F));
  470.             for (int n=1;n<=mol.numAtoms();n++) if (selected[n-1])
  471.             {
  472.                 double rx=mol.atomX(n)-ax,ry=mol.atomY(n)-ay;
  473.                 double rth=Math.atan2(ry,rx),ext=Math.sqrt(rx*rx+ry*ry);
  474.                 rx=ax+ext*Math.cos(rth+thrad);
  475.                 ry=ay+ext*Math.sin(rth+thrad);
  476.                 g.draw(new Ellipse2D.Double(angToX(rx)-scale*0.3,angToY(ry)-scale*0.3,scale*0.6,scale*0.6));
  477.             }
  478.         }
  479.                    
  480.         if (outlineTemplate)
  481.         {
  482.             g.setColor(new Color(128,128,128));
  483.             g.setStroke(new BasicStroke(1));
  484.             for (int n=1;n<=oltMol.numBonds();n++)
  485.             {
  486.                 int from=oltMol.bondFrom(n),to=oltMol.bondTo(n);
  487.                 g.draw(new Line2D.Double(angToX(oltMol.atomX(from)),angToY(oltMol.atomY(from)),
  488.                                          angToX(oltMol.atomX(to)),angToY(oltMol.atomY(to))));
  489.             }
  490.         }
  491.     }
  492.    
  493.     // bring attention to structures which are malformed
  494.     private void drawCorrections()
  495.     {
  496.         // see if any atoms severely overlap, and if so, draw a red circle around them
  497.         for (int i=1;i<=mol.numAtoms()-1;i++)
  498.         for (int j=i+1;j<=mol.numAtoms();j++)
  499.         {
  500.             double dx=mol.atomX(i)-mol.atomX(j),dy=mol.atomY(i)-mol.atomY(j);
  501.             if (dx*dx+dy*dy<0.2*0.2)
  502.             {
  503.                 g.setColor(Color.RED);
  504.                 g.setStroke(new BasicStroke(0.5F));
  505.                 g.draw(new Ellipse2D.Double(arrmol.pointCX(i-1)-scale*0.25,arrmol.pointCY(i-1)-scale*0.25,scale*0.5,scale*0.5));
  506.             }
  507.         }
  508.     }
  509.  
  510.     // implementation of measurement metrics
  511.    
  512.     public double scale() {return scale;}
  513.  
  514.     public double angToX(double AX) {return (offsetX+AX)*scale;}
  515.     public double angToY(double AY) {return (offsetY-AY)*scale;}
  516.     public double xToAng(double PX) {return (PX/scale)-offsetX;}
  517.     public double yToAng(double PY) {return (-PY/scale)+offsetY;}
  518.  
  519.     public double[] measureText(String str,double fontSize)
  520.     {
  521.         Font font=new Font("SansSerif",Font.PLAIN,(int)Math.round(fontSize));
  522.         FontMetrics fm=g.getFontMetrics(font);
  523.         return new double[]{fm.stringWidth(str),fm.getAscent()*ASCENT_FUDGE,fm.getDescent()};
  524.     }
  525.  
  526.     private Ellipse2D circle(double x, double y, double radius){
  527.         double newX = x - radius;
  528.         double newY = y - radius;
  529.         double diam = 2*radius;
  530.         Ellipse2D ellipse = new Ellipse2D.Double(newX, newY, diam, diam );
  531.         return ellipse;
  532.     }
  533. }
  534.  
  535.  
  536.  
  537.