Klassen des Pakets AstroUI: Zur Doku-Startseite Zurück zur Homepage

Klasse HoroApplet

Javadoc von HoroApplet   HoroApplet.java herunterladen

package AstroUI;

import java.applet.Applet;

import java.awt.Image;
import java.awt.Font;
import java.awt.MediaTracker;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Component;

import java.net.URL;

import java.io.BufferedReader;
import java.io.StringReader;

import AstroOL.Calculator;
import AstroOL.ICalculator;
import AstroOL.LowPrecCalculator;

import HttpRequestor;

/** Dieses Applet dient der Repräsentation eines Horoskops. Das Applet
ist über das Javascript/Java-Interface des Browsers sowie über einen
umfangreichen Satz von Parametern weitgehend extern steuerbar. So kann es
sowohl für interaktive Anwendungen als auch in statischen HTML-Seiten
als Graphik zur Präsentation bestimmter Horoskope verwendet werden.
Der Vorteil, ein Applet statt eines "ausgehärteten" GIF zu benutzen,
besteht darin, dass die Graphik zum Zeitpunkt des Aufrufs der Webseite im
Browser aus den als Applet-Parameter mitgegebenen Horoskopdaten erstellt
wird. Die Horoskopdaten selbst, aber auch spezielle Präsentierungsdaten
(sollen die Positionen mit angezeigt werden? Welche Hintergrundfarbe soll
verwendet werden? u.a.) sind in der HTML-Quelle mit wenig Aufwand
änderbar
<p>
Das Applet nimmt entweder einen String mit Datum, Uhrzeit und Ort entgegen,
berechnet die resultierenden Planeten- und Hauspositionen mittels einer
ICalculator-Instanz und zeichnet sich dann &mdash; ebensogut ist es aber möglich,
die Planeten- und Hauspositionen extern mitzugeben.
<p>
Die Kommunikation mehrerer Applets auf einer HTML-Seite wird durch das
Interface {@link ILoadListener} bestimmt. Das HoroApplet implementiert dieses
Interface zwar nicht, informiert aber andere Applets, die dieses Interface
implementiert haben, über die erfolgreiche Beendigung des Ladevorgangs.
Dies löst das Problem, dass es beim Laden einer Seite nicht definiert ist,
welches Applet als erstes aufgebaut ist und damit in der Lage ist, auf Java-
Anweisungen von anderen Applets zu reagieren.
*/
public class HoroApplet extends java.applet.Applet {

/** Bilder der zwölf Tierkreiszeichen (im GIF-Format) */
   public static Image[] signImage = new Image[12];

/** Bilder der zehn astrologischen Planeten (im GIF-Format) */
   public static Image[] planetImage = new Image[10];

/** Ekliptikale Längen der zehn astrologischen Planeten */
   public double pl[] = new double[10];

/** Ekliptikale Längen der zwölf Häuserspitzen, gezählt ab Index 1 */
   public double h[] = new double[13];

/** Julianisches Datum (in <i>Weltzeit</i>, nicht Ephemeridenzeit). */
   public double xjd;

/** Extern mitgegebene Caption, anstelle der automatisch generierten */
   public String caption;

/** Geographische Länge */
   public double lon;
/** Geographische Breite */
   public double lat;

/** Kritischer Abstand (Ü;berlappung) zwischen zwei Planeten */
   protected static double criticalDistance = 8.;

/** noHouses = true: Keine Häuser darstellen. Wenn der Array houses ein Element
house[1] enthält, so wird dieses zur Orientierung des Horoskops verwendet, <i>als
wäre dies der Aszendent</i>. Der entsprechende Längenwert wird also links
dargestellt. Wenn <tt>houses[]</tt> leer ist, kommt der Widderpunkt links zur
Darstellung, entsprechend der Haus-Zeichen-Analogie. */
   public boolean noHouses = false;

   protected Graphics g;
   protected Image bi;
   protected static final String houses = "ASC IIIII IV  V VI";
   protected static final double factor105 = 1.05;
   protected static final double factor1 = .85;
   protected static final double factor_tick1 = .83;
   protected static final double factor_tick2 = .81;
   protected static final double factor_planet = .75;
   protected static final double factor2 = .2;
   protected static final double factor_hno = .15;
   protected static final Font courierplain12 = new Font("Courier",Font.PLAIN, 12);
   protected static final Font courierbold12  = new Font("Courier",Font.BOLD,  12);
   protected static final Font courierbold8  = new Font("Courier",Font.BOLD,  8);
   protected int r,d=0;
   protected int xm, ym;
   protected int height, width;
   protected long precision = Calculator.SEC;
   protected Color bgcolor,bgcolor2;
   protected double rd=1d;
   protected double plGraph[] = { 0., 30., 60., 90., 120., 150.,
                         180., 210., 240., 270. };
   protected double tile = 0.77d;
   protected static MediaTracker tracker;


/** called = true: Planeten- und Hauspositionen sowie caption werden
mit JavaScript gesteuert, das heisst: In diesem Modus rechnet das Applet
nicht selbst. Das Flag wird von den aufgerufenen Methoden
setPlanetsAndHouses() und setCaption() intern gesetzt. */
   protected boolean called = false;

/** Die Zeile mit Ort und Zeit soll weggelassen werden */
   protected boolean noTimePlace = false;

/** Instanz des Planeten- und Hausberechners */
   protected ICalculator calc;

// Defaultkonstruktor
  public HoroApplet() {
    }

/* Testmethode für Bugfixing etc.
  public static void main(String[] argv) {
    int i;
    HoroApplet horoa = new HoroApplet();
    String servletURL = "http://localhost:8080/examples/servlet/Ephemeris?planets&houses&jd=2438498.438888889&lon=7.266666666666667&lat=52.333333333333336";
    Calculator.getPlanetsAndHouses( servletURL, horoa.pl, horoa.h);
    horoa.plGraph = horoa.getPlGraph( horoa.pl );
    for (i=0;i<10;i++) System.out.println(horoa.pl[i] + "\t-> " + horoa.plGraph[i]);
  } */

/** Das Applet initialisieren. In der init()-Methode werden die Tierkreis- und
Planetensymbole geladen, falls dies noch nicht erfolgt ist. Das Laden erfolgt synchron &mdash;
vor der Programmfortsetzung wird abgewartet, bis der Ladevorgang abgeschlossen ist.
Die Graphiken müssen, von der Codebase ausgehend, im Unterverzeichnis
<code>./rsc/mime</code> zu finden sein (oder im .jar-File unter diesem relativen
Pfad abgelegt sein). <p>
Darüberhinaus wird die korrekte <code>ICalculator</code>-Implementierung
anhand des Applet-Parameters <code>calculator</code> identifiziert und eine
<code>ICalculator</code>-Instanz erzeugt. Die Hintergrundfarbe (Parmeter <code>bgcolor</code>
und das Platzverhätnis der Horoskopgraphik zum Datenteil (Parameter <code>tile</code>)
wird errechnet. Intern benötigte Attribute werden initialisiert.
*/
  public void init() {

    int i, fontHeight, fontAscent;
    String s;

 // Tierkreis- und Planetenbilder holen
    if (HoroApplet.signImage[0] == null) {
     if (tracker == null) tracker = new MediaTracker( (Component) this );
     try {
       URL ressourceURL = new URL( getCodeBase(), "rsc/mime/" );
       for ( i = 0; i < 12; i++) {
           HoroApplet.signImage[i] = this.getImage(  ressourceURL, Calculator.sknam.substring(2*i,2*i+2)+".gif" );
           tracker.addImage(signImage[i],0);
           }
       for ( i = 0; i < 10; i++ ) {
           HoroApplet.planetImage[i] = this.getImage( ressourceURL, Calculator.pknam.substring(2*i,2*i+2)+".gif" );
           tracker.addImage(planetImage[i],0);
           }
       }
// Abfangen der potentiellen MalformedURLException
// ist zwar syntaktisch vorgeschrieben, hier aber überflüssig
     catch (Exception e) {
       System.err.println("Fehler beim Laden der Ressourcen");
       }
     }

// Warten, bis alle Bilder fertiggeladen
   waitForImages();

// Genauigkeit
   s = getParameter("precision");
   if ((s!=null) && (s.toLowerCase().startsWith("min"))) precision = 0;

// Instanz des Rechners für Planeten- und Hausberechnungen
   s = getParameter("calculator");
   if (s == null)
// Calculator ist der Default
     calc = (ICalculator) new Calculator();
   else if (s.equals("LowPrecCalculator") ||
            s.equals("AstroOL.LowPrecCalculator"))
// LowPrecCalculator für eine billige Ephemeride am Frontend
     calc = (ICalculator) new LowPrecCalculator();
   else
// Generische Instanziierung eines anderen Calculators durch
// Aufruf des Defaultkonstruktors
     try {
       calc = (ICalculator) Class.forName(s).newInstance();
   } catch (Exception ex) { ex.printStackTrace();
                            calc = (ICalculator) new Calculator();
                            }

// Falls vorhanden, dem Calculator die servletURL mitteilen
   if ((s = getParameter("servletURL")) != null) {
     if (!s.substring(0,7).equals("http://")) {
// Relative URL mit Bezug zur Codebase des Applets berechnen
       try {
         s = new URL( getCodeBase(), s).toString();
         } catch (java.net.MalformedURLException ex) { System.out.println("Bad URL:"+s); }
       }
     calc.setServletURL(s);
     }


// Hintergrundfarbe
   s = getParameter("bgcolor");
   if ( s != null)
     if (s.charAt(0)=='#')
       bgcolor = new Color(Integer.parseInt(s.substring(1),16));
     else
       bgcolor = new Color(Integer.getInteger(s,new Integer(52479)).intValue());
   else
     bgcolor = new Color(52479);  // Meine Lieblingsfarbe

// Zweite Hintergrundfarbe (für Leiste mit Planetenpositionen)
   s = getParameter("bgcolor2");
   if ( s != null)
     if (s.charAt(0)=='#')
       bgcolor2 = new Color(Integer.parseInt(s.substring(1),16));
     else
       bgcolor2 = new Color(Integer.getInteger(s,new Integer(52479)).intValue());
   else
     bgcolor2 = Color.yellow;

// Wenn Planeten und Häuser von aussen mitgegeben werden,
// will man eventuell keinen Zeit/Ort-String verwenden
   noTimePlace = false;
   if (!called && getParameter("noTimePlace")!=null)
     noTimePlace = getParameter("noTimePlace").equals("true");

// Häuser darstellen?
  if ((s=getParameter("noHouses"))!=null)
    noHouses = Boolean.valueOf(s).booleanValue();

// Wenn eine Unterschrift statisch mitgegeben wurde
  if (!called) caption = getParameter("caption");

// Systemmeldungen an die Konsole durchreichen
  String messages = getParameter( "messages" );
  if (messages != null) System.out.println( messages );

// Hoehe und Breite gemäss tile-Parameter
  height = getSize().height;
  width  = getSize().width;
  String tileS = getParameter( "tile" );
  if (tileS != null) tile = (new Double(tileS)).doubleValue();
// Breite der fürs Horoskop vorgesehenen Zeichenfläche
  width = (int)(width*tile);
// Resultierender Radius des Horoskops
// rd ist mit 1 vorbelegt in Klasse HoroApplet.java
// rd ist mit 0.9 vorbelegt in Klasse HoroCompare.java
  rd *= ( ( ( height < width ) ? height : width ) ) / 2;
// ... muss etwas kleiner gewählt werden, damit die Figur nicht
// am Rand klebt
  rd *= .84;
// Mittelpunktskoordinaten
  r = (int) rd;
  xm = (int)(width * .515);
  ym = (int) (rd * 1.15); // (int)(height * .515);

// Berechne einen gefälligen Zwischenraum zwischen der Horoskopgraphik
// und ihrem Untertitel
  g = this.getGraphics();
  g.setFont(courierbold12);
  fontAscent = g.getFontMetrics().getAscent();
  fontHeight = g.getFontMetrics().getHeight();
  d = (height
         - 2*r             // Durchmesser des Horoskops
         - 2*fontHeight    // Platz für Schrift MC / IC
         - ( 2*fontHeight + fontAscent) // Platz für zwei Unterschriften
         ) / 4;            // Freien Platz in vier Teile aufteilen
  ym = d + r + fontHeight;
  }

  /** Startmethode <br>
      Die Horoskopgrafik, einmal erzeugt, wird aus Effizienzgründen
      nicht wieder neu berechnet, wenn die Start-Methode später noch
      einmal gerufen wird. <br>
      In der Startmethode wird der Parameter <tt>timePlace</tt>
      ausgewertet - aus ihm werden Datum, Zeit und Ort abgeleitet. <br>
      Für die Planetenstände und Häuser gibt es folgende Optionen:
      <ul>
      <li>Übergabe als komma-separierte Liste in den Parametern
      <tt>planets</tt> und <tt>houses</tt> - oder
      <li>automatische Berechnung, wenn diese Daten fehlen.
      </ul>
      */
  public void start() {

// Wird dieses Applet von einem anderen gerufen oder aktualisiert?
     String caller = getParameter("caller");
// Ja - dann Notify-Methode aufrufen
     if (caller != null) notify(caller);

     if (!noTimePlace && !called)
       setTimePlace(getParameter("timePlace"));
     else doShow();
   }


/** Das rufende Applet über den Abschluß des Ladevorgangs informieren.
Näheres hierzu in der Dokumentation des {@link ILoadListener ILoadListener} Interface. */
   public void notify(String iCaller) {
       ILoadListener caller;
       try {
       caller = (ILoadListener) getAppletContext().getApplet(iCaller);
       caller.onLoad(this);
// Applet war noch nicht geladen? Dann wird es selbst die
// Synchronisation vornehmen, wenn es geladen ist
      } catch (Exception e) {System.out.println("x2");}
     }


   /** Aktualisierungsmethode. Planetenpositionen neu beschaffen oder berechnen, danach
   das Applet neu zeichnen. Der Aufruf dieser Methode kann nötig sein, wenn die
   Zeichenfläche des Applets durch andere Objekte der HTML-Seite benutzt oder
   verändert wird. */
   public void doShow() {
       getPlanetsAndHouses();
// Alles neu zeichnen
       drawAll( );
// Paint aufrufen
       update(this.getGraphics());
     }

  /** Haus- und Planetenpositionen direkt angeben.<br>
      In manchen Fällen ist es gewünscht, die Planetenstände dem Applet
      direkt mitzugeben, zum Beispiel weil es sich um hypothetische Stände
      handelt (beispielsweise das berühmte Thema Mundi, mit fiktiven,
      rein symbolischen Planetenständen, oder die vorgeschobenen oder
      primären Stände zu einem Horoskop, die keine reale Entsprechung mit
      dem Sternenhimmel haben), oder weil man die Stände mit
      einem eigenen Berechnungsprogramm ermittelt hat.<br>
      In diesen Fällen sind die Stände dem Applet mit der Methode
      setPlanetsAndHouses() mitzuteilen. Die Methode nimmt einen String
      entgegen, der die Planeten- und Hauslängen enthält.<br>
      @param response Enthält, von den Schlüsselwörtern "planets:" und "houses:"
      gefolgt, die 10 bzw. 12 Positionen als komma-getrennte Liste von
      Doubles.<br>
      Beispiel:
      "planets:15.1,30,45,60,75,90,105,120,135,150<br>
       houses:0,30,60,90,120,150,180,210,240,270,300,330,360" */
  public void setPlanetsAndHouses( String response ) {
    Calculator.analyzeString(new BufferedReader(new StringReader(response)), h, pl);
    called = true;
    }

/** Mit dieser Methode kann dem Applet ein neuer Datum/Zeit/Ort-String
    übergeben werden. Das Applet zeichnet sich dann neu. Zur Berechnung
    der Planetenstände wird die aktuelle calc-Instanz verwendet. */
  public void setTimePlace(String s) {

     double[] tp = new double[3];
     String servletURL;
      if (!Calculator.parseTimePlace(s,tp)) {
         xjd = Calculator.thisjd();
         lat = Calculator.DEFAULT_LAT;
         lon = Calculator.DEFAULT_LON;
         }
       else {
         xjd = tp[0];
         lon = tp[1];
         lat = tp[2];
         }
     doShow();

   }

/** Übergabe neuer Horoskopdaten - Julianisches Datum, Länge und Breite.
    Das Applet zeichnet sich dann neu. Zur Berechnung
    der Planetenstände wird die aktuelle calc-Instanz verwendet. */
   public void setTimePlace(double xjd, double lon, double lat) {
     this.xjd = xjd;
     this.lon = lon;
     this.lat = lat;
     doShow();
     }

/** Eine Unterschrift des Horoskops von aussen setzen */
   public void setCaption(String iCaption) {
     this.caption = iCaption;
     noTimePlace = false;
     called = true;
     }

   private void getPlanetsAndHouses() {

       double[] h0 = new double[13];
       int i;

// Parameter werden dem Applet übergeben
       if (!called) {
         if ( (Calculator.parseDoubleList(getParameter("planets"),pl) == 10) &&
              (Calculator.parseDoubleList(getParameter("houses"),h) == 12) ) {
// Rechts-Shift der Häuser
           for( i = 11; i >= 0; i--) h[i+1] = h[i];
           h[0] = 0.;
           }
// Planetenpositionen durch den Rechner ermitteln
         else {
           calc.getPlanetsAndHouses( xjd, lon, lat, pl, h);
           if (noHouses &&
              ((i=Calculator.parseDoubleList(getParameter("houses"),h0)) > 0) ) {
// Rechts-Shift der Häuser
              for( --i; i >= 0; i--) h[i+1] = h0[i];
              h[0] = 0.;
              }
           }

        }
     }


   protected void drawAll( ) {
     try {
// Positionen der graphischen Darstellung ermitteln
     plGraph = this.getPlGraph(pl);
// Buffered Image bereits vorhanden? Nein, dann erzeugen
     if (bi == null) bi = createImage(this.getSize().width,this.getSize().height);
// Graphische Zeichenfläche (Canvas) holen
     g = bi.getGraphics();
     if (g != null) {
// Hintergrundfarbe setzen
       g.setColor(bgcolor);
       g.fillRect(0,0,width,height);
// Hintergrundfarbe für Positionsleiste
       g.setColor(bgcolor2);
       g.fillRect(width,0,this.getSize().width-width, height);
// Zeichenfarbe schwarz
       g.setColor(Color.black);
// Horoskopfigur zeichnen
       drawHoroscope();
// Positionen zeichnen
       if (tile < 1) drawPositions();
       }
       } catch (Exception ex) {ex.printStackTrace();}

     }

/** Applet-Information holen */
  public String getAppletInfo() {
    return "Horoskop-Applet\nCopyright 2002-2005 Rüdiger Plantiko\nRehweg 19\nCH-8400 Winterthur";
  }


/** x-Koordinate aus Polarkoordinaten. Das Argument ist eine ekliptikale Länge. Die
Koordinate wird so berechnet, dass der Aszendent auf der angezeigten Zeichenfläche
horizontal nach links gerichtet ist.*/
     protected int x( double l_, double r_ ) {
       return (int) (rd*(1.-Calculator.cos(h[1]-l_)*r_)) + (xm - r);
       }

/** y-Koordinate aus Polarkoordinaten. Das Argument ist eine ekliptikale Länge. Die
Koordinate wird so berechnet, dass der Aszendent auf der angezeigten Zeichenfläche
horizontal nach links gerichtet ist.*/
     protected int y( double l_, double r_ ) {
       return (int) (rd*(1.-Calculator.sin(h[1]-l_)*r_)) + (ym - r);
       }


/** Einen Kreis vom Radius r zeichnen */
     protected void circle( int r ) {

// Circles, circles, circles
        g.drawOval(xm-r,ym-r,2*r,2*r);

       }


/** Eine Strecke auf einer Ursprungsgeraden in Richtung
der ekliptikalen Länge l zeichnen. Die Abstände des Anfangs-
und Endpunktes vom Mittelpunkt sind durch <code>r_from</code>
und <code>r_to</code> gegeben. */
     protected void line( double l, double r_from, double r_to ) {
       int x1, y1, x2, y2;

       double c = Calculator.cos(h[1]-l),
              s = Calculator.sin(h[1]-l);

       x1 = (int) ( rd * ( 1. - c * r_from)) + ( xm - r) ;
       y1 = (int) ( rd * ( 1. - s * r_from)) + ( ym - r) ;
       x2 = (int) ( rd * ( 1. - c * r_to)) + ( xm - r) ;
       y2 = (int) ( rd * ( 1. - s * r_to)) + ( ym - r) ;

       g.drawLine(x1, y1, x2, y2);

       }

/** Horoskopgraphik aktualisieren. */
     public void update(Graphics pg) {
       paint(pg);
     }

/** Zeit/Ort-String zurückgeben. Es wird die Information über Zeit und
    Ort, wie sie auch unterhalb des Horoskopkreises angezeigt wird,
    zurückgegeben.<p>
    Die Funktion ist für den Einsatz des Applets im Zusammenspiel mit JavaScript gedacht. */
    public String getTimePlace() {
    return Calculator.renderTimePlace(xjd, lon, lat, precision);
    }

/** Aktuelles Julianisches Datum zurückgeben.<p>
    Die Funktion ist für den Einsatz des Applets im
    Zusammenspiel mit JavaScript gedacht. */
   public String getJd() {
    return Double.toString(xjd);
    }

/** Aktuelles Julianisches Datum, Länge und Breite als String (durch Zeilenumbruch
    getrennt) zurückgeben. Die Funktion ist für den Einsatz des Applets im
    Zusammenspiel mit JavaScript gedacht. */
  public String getTriple() {
      return
      Double.toString(xjd) + "\n" +
      Double.toString(lon) + "\n" +
      Double.toString(lat) ;
      }

 protected static boolean waitForImages() {
    try {
      tracker.waitForAll();
    } catch(InterruptedException ie) {}
    return(!tracker.isErrorAny());
    }


     public void paint( Graphics pg )
  {
       if (bi != null)
         pg.drawImage(bi,0,0,getSize().width,getSize().height,null);

  } // paint method

  protected void drawPositions() {
    int i;
    int halign1 = width * 102 / 100,
        halign2 = width * 110 / 100,
        voffset = 30;
    g.setFont(courierplain12);
    for (i = 0; i < 10; i++) {
       g.drawImage( HoroApplet.planetImage[i],
                    halign1,
                    20*i + voffset,
                    this );
       g.drawString(Calculator.dms(pl[i],Calculator.ECL+precision),
                    halign2,
                    20*i + 13 + voffset );

      }

    if (!noHouses)
      for (i = 0; i < 6; i++) {
        g.setFont(courierbold12);
        g.drawString(houses.substring(3*i,3*i+3),
                     halign1,
                     20*i + 223 + voffset );
        g.setFont(courierplain12);
        g.drawString(Calculator.dms(h[i+1],Calculator.ECL+precision),
                     halign2,
                     20*i+223 + voffset );

        }

    g.setFont(courierbold12);
    g.drawString("Länge",halign2, voffset - 7);

    }

  protected void drawHoroscope() {
      double l;
      int i, stringwidth;


      circle( this.r );
      circle( (int)( rd * factor1) );
      circle( (int)( rd * factor2) );


      if (!noHouses) {
        line( h[1], 1., -1. );
        line( h[10], 1., -1. );

        line( h[2], factor1, -factor1 );
        line( h[3], factor1, -factor1 );
        line( h[11], factor1, -factor1 );
        line( h[12], factor1, -factor1 );
        }

      for (l = 0.; l < 360.; l+= 30.) line( l, 1., factor1);

      for (i = 0; i < 12; i++) {
          g.drawImage( HoroApplet.signImage[i],
                       x(15.+30.*i,0.93) - HoroApplet.signImage[i].getWidth(this)/2,
                       y(15.+30.*i,0.93) - HoroApplet.signImage[i].getHeight(this)/2,
                       this );
          }

      for (i = 0; i < 10; i++) {
          line( pl[i], factor1, factor_tick1);
          g.drawLine(x(pl[i], factor_tick1),
                     y(pl[i], factor_tick1),
                     x(plGraph[i], factor_tick2),
                     y(plGraph[i], factor_tick2));

          g.drawImage( HoroApplet.planetImage[i],
                       x(plGraph[i],factor_planet) - HoroApplet.planetImage[i].getWidth(this)/2,
                       y(plGraph[i],factor_planet) - HoroApplet.planetImage[i].getHeight(this)/2,
                         this );
          }

      if (!noHouses) {
        g.setFont(courierbold8);
        l = h[1] + Calculator.arcdiff(h[2],h[1])/2.;
        int h1 = 3, w1 = 3, h2 = 3, w2 = 4;
        g.drawString("1",x( l, factor_hno)-w1,y( l,factor_hno)+h1);
        g.drawString("7",x( l, -factor_hno)-w1,y( l, -factor_hno)+h1);
        l = h[2] + Calculator.arcdiff(h[3],h[2])/2.;
        g.drawString("2",x( l, factor_hno)-w1,y( l,factor_hno)+h1);
        g.drawString("8",x( l, -factor_hno)-w1,y( l,-factor_hno)+h1);
        l = h[3] + Calculator.arcdiff(h[10]+180.,h[3])/2.;
        g.drawString("3",x( l, factor_hno)-w1,y( l,factor_hno)+h1);
        g.drawString("9",x( l, -factor_hno)-w1,y( l,-factor_hno)+h1);
        l = Calculator.fmod360(h[10]+180) + Calculator.arcdiff(h[11],h[10])/2.;
        g.drawString("4",x( l, factor_hno)-w1,y( l,factor_hno)+h1);
        g.drawString("10",x( l, -factor_hno)-w2,y( l,-factor_hno)+h2);
        l = h[11] + Calculator.arcdiff(h[12],h[11])/2.;
        g.drawString("11",x( l, factor_hno)-w2,y( l,factor_hno)+h2);
        g.drawString("5",x( l, -factor_hno)-w2,y( l,-factor_hno)+h2);
        l = h[12] + Calculator.arcdiff(h[1],h[12])/2.;
        g.drawString("12",x( l, factor_hno)-w2,y( l,factor_hno)+h2);
        g.drawString("6",x( l, -factor_hno)-w2,y( l,-factor_hno)+h2);

        g.setFont(courierbold12);
        g.drawString("ASC", x( h[1], factor105 )-15,
                            y( h[1], factor105 )+3);
        g.drawString("MC",  x( h[10], factor105 )-12,
                            y( h[10], factor105 ));
      }

      g.setFont(courierbold12);
      try {
      if (caption==null) {
        if (!noTimePlace) {
          stringwidth = ((precision==Calculator.SEC)?41:30)*g.getFontMetrics().charWidth('1');
          double[] tp = { xjd, lon, lat};
          g.drawString( Calculator.renderTimePlace(tp, precision),
                        xm - stringwidth/2,
                        height - d-g.getFontMetrics().getHeight()
                        );
          }
        }
      else {
        if (!noTimePlace)
// String zentriert schreiben
          g.drawString( caption,
            xm - (int) g.getFontMetrics().stringWidth(caption.trim())/2 ,
            height - d-g.getFontMetrics().getHeight()
            );
          }
        } catch (java.lang.NullPointerException e) {}


  }

/** Ausrichtungsfunktion der Planetenkoordinaten. Um eine möglichst
überlappungsfreie Darstellung der einzelnen Planetensymbole zu gewährleisten,
werden die Planeten dort, wo sie sich häufen, auseinandergezogen. Dies
geschieht durch Identifizierung von Clustern, also Ketten von Planeten, die
einer vom nächsten einen hinreichend kleinen Abstand aufweisen. Von diesen
Clustern wird der Schwerpunkt berechnet, und die Längen der am Cluster
beteiligten Planeten werden vom Schwerpunkt aus so weit gestreckt, dass
der kritische Abstand nicht mehr unterschritten wird. Dabei können sich
natürlich am Rand neue Cluster bilden, so dass das Verfahren letztlich iterativ
arbeiten muss: Bestehende Cluster müssen soweit gestreckt werden, bis sich
schliesslich keine neuen Cluster mehr bilden.
  */
  protected double[] getPlGraph(double[] p_pl){
    double[] plGraph = new double[10];
    double[] plSorted = new double[10];
    long[] plLongInd  = new long[10];
    int i,z=0;
    int[] index;

    for (i = 0; i < 10; i++ )
      plLongInd[i] = i + 100*((long)(10*p_pl[i]));
    Calculator.sort(plLongInd);
    for (i = 0; i < 10; i++ )
      plSorted[i] = ((double)(plLongInd[i])/100)/10.;

    do {
      index = getCluster( plSorted );
      if ((++z>50)||(index[0] == index[1])) break;
      resolveCluster( index, plSorted );
    } while (true);

    for (i = 0; i < 10; i++)
      plGraph[(int)(plLongInd[i]%100)] = plSorted[i];

    return plGraph;

  }

  protected int[] getCluster(double[] p) {

// Länge des Clusters
   int n = 0, z = 0;

// Es wird ein Array der Länge 2 für die Unter- und Obergrenze
// des ermittelten Clusters definiert. Dies hat den Vorteil, dass
// die Zahlen per Referenz an den Cluster-Auflöser übergeben werden
// können, da bei der Clusterauflösung eventuell weitere Elemente
// zum Cluster hinzukommen - Unter- und Obergrenze müssen also in
// der Funktion resolveClustert() noch änderbar sein
    int[] i = new int[2];

// Eine grösstmögliche Kette von Elementen ermitteln, deren Abstände
// jeweils kleiner als die criticalDistance sind
    i[0] = 0;
    i[1] = 0;
    do {

      if (++z > 50) break;

      n = 0;
      while ((Calculator.arcdiffAbs(p[(i[0]+n+1)%10],p[(i[0]+n)%10]) < criticalDistance)
              && (n < 9)) n++;
      if (n==0) i[0]++;
      } while ((n==0) && (i[0] < 9));
    i[1] = (i[0]+n)%10;

// Wurde ein Cluster ab i[0]=0 ermittelt, so muss auch in rückwärtiger
// Richtung gesucht werden
    if ((n>0) && (i[0]==0))
      while((Calculator.arcdiffAbs(p[(i[0]+9)%10],p[i[0]]) < criticalDistance)
            && (i[0] > 1)) i[0] = (i[0]+9)%10;

// Unter- und Obergrenze zurückgeben
// Wurde kein Cluster gefunden, so ist i[0] = i[1] = 0
   return i;

   }

  protected void resolveCluster(int[] i, double[] pExt) {

    int j, n=0, z=0;
    double centerOfMass,outsideOfCluster_Start,outsideOfCluster_Size;
    boolean leave1 = false, leave2 = false;
    double[] p = new double[10];

// Für einen einelementigen Cluster ist nichts zu tun
    if (i[0] == i[1]) return;

// Schleife wird verlassen, wenn zwei Leave-Bedingungen erfüllt sind
    while ( (!leave1 || !leave2) && (++z < 50) ) {

// Stände-Array kopieren
    for (j=0;j<10;j++) p[j] = pExt[j];

// Anzahl der Elemente im Cluster
    n = (i[1]-i[0]+10)%10 + 1;

// Berechne den Schwerpunkt des Clusters
    centerOfMass = p[i[0]];
    for (j = 1 ; j < n; j++ )
      centerOfMass += (Calculator.arcdiff(p[(i[0]+j)%10],p[(i[0]+j+9)%10])*(double)(n-j))/n;

// Berechne die korrigierten Längen der Elemente des Clusters
    for (j = 0; j < n; j++ )
      p[(i[0]+j)%10] = centerOfMass - (double)(((double)n-1.)/2. - (double)j)*criticalDistance;

// Stelle fest, ob weitere Elemente in den Cluster hineinzunehmen sind
    outsideOfCluster_Start = p[i[1]] + criticalDistance;
    outsideOfCluster_Size  = Calculator.fmod360(p[i[0]]-outsideOfCluster_Start-criticalDistance);

// Das dem Cluster nächstfolgende Element liegt in dem vom Cluster
// überstrichenen Bereich: Cluster vergrössern
    if (!(leave1 = (Calculator.fmod360(p[(i[1]+1)%10]-outsideOfCluster_Start) < outsideOfCluster_Size)))
      i[1] = (i[1] + 1 ) % 10;

// Das dem Cluster vorangehende Element liegt in dem vom Cluster
// überstrichenen Bereich: Cluster vergrössern
    if (!(leave2 = (Calculator.fmod360(p[(i[0]+9)%10]-outsideOfCluster_Start) < outsideOfCluster_Size)))
      i[0] = (i[0] + 9 ) % 10;

    }

// Die veränderten Werte in den übergebenen Array zurückkopieren
  for (j = 0; j < n; j++) pExt[(j+i[0])%10] = p[(j+i[0])%10];

  }


}


Zum Seitenanfang Lizenzbedingungen Der Quellcode wird mit dem GNU source-highlight 1.7 dargestellt