Multimedia Prosjekt Rapport - Våren 1999

Interaktiv læring i Grafisk Databehandling

Frode Kristensen og Jostein Trondal.


Indeks

Introduksjon

Oppbygning av Aroma

Eksempel på hvordan man legger til en ny Applet

Aroma.java kildekode med kommentarer

dda.java kildekode med kommentarer

Kommentarer til scanline og midp

Tips til hvordan man tilpasser Applets til Aroma

Utviklingsverktøy og Kilder

Hele prosjektet pakket i en zip-fil

 


 

Introduksjon

Formålet med dette prosjektet var å utvikle et system som interaktivt demonstrerer noen deler av pensumet i faget Grafisk Databehandling ved Høgskolen i Agder. Målet er at studenter som følger faget skal kunne bruke systemet til å lettere lære seg hvordan algoritmer og prinsipper i faget fungerer. Systemet er utviklet i Java, og skal kunne kjøres fra en vanlig web-browser.

Vi har tatt for oss tre metoder i Grafisk Databehandling;

  • Digital Differential Analyser for scankonvertering av linjer
  • Bresenhams midpoint algoritme for scankonvertering av linjer
  • Scanlinje algoritmen for fylling av polygoner

Disse metodene demonstreres vha. Applets. Alle metodene har en info-tekst om hva algoritmen går ut på. Vi valgte derfor, å samle fremgangsmåten på å bruke Appletene i en Applet-viewer. For å følge Java-klisjeen om at de fleste programmer utviklet for å håndtere Java .class filer har et navn som har noe å gjøre med kaffe, har vi valgt å kalle Applet-vieweren vår for Aroma. Dette er en videre-utvikling av resultatet av et prosjekt i faget Objektorientert Analyse og Design Implementasjon.

 


 

Oppbygning av

Filer i Aroma-systemet:


fig. 1

Aroma.class

Dette er hoved-Appleten som startes med HTML-parameterne som vist nedenfor.

 

Aroma.html

APPLET code = Aroma.class width=640 height=480>
  <PARAM NAME=NumberOfModules VALUE="3">
  <PARAM NAME=NameOfModule1 VALUE="dda">
  <PARAM NAME=NameOfModule2 VALUE="midp">
  <PARAM NAME=NameOfModule3 VALUE="scanline">
</APPLET>

Dette er kode som gjør det mulig for Aroma å finne Appletene som skal viewes.

 

AromaInterface.class

Alle Appleter som skal brukes i Aroma, må implementere dette interfacet;

interface AromaInterface {
  public String getInfo();
  public String getTitle();
  public String getListname();
  public java.applet.Applet getApplet();
}


fig. 2: Aroma i aksjon. Teksten under henviser til bokstavene i bildet.

getInfo() skal returnere info-teksten om Appleten (C i fig. 2)
getTitle() skal returnere tittelen til Appleten (D i fig. 2)
getListname() skal returnere navnet Appleten skal ha i menyvalget (A i fig. 2)
getApplet() skal returnere peker til en Applet (som regel this) (B i fig. 2)

Knappen (E i fig. 2) brukes til å alternere på størrelsen på feltet B.

 

dda.class

Appleten som demonstrerer Digital Differential Analyser for scankonvertering av linjer.

 

midp.class

Appleten som demonstrerer Bresenhams midpoint algoritme for scankonvertering av linjer.

 

scanline.class

Appleten som demonstrerer Scanlinje algoritmen for fylling av polygoner.

 

splash.gif

Bildet som vises i feltet B i fig. 2 når Aroma startes.

 


 

Eksempel på hvordan man legger til en ny Applet

Steg man må følge for å legge til en ny Applet:

  1. Lag en ny Applet / Ta utgangspunkt i en gammel Applet
  2. Appleten må implementere AromaInterface med tilhørende metoder
  3. Aroma.html (eller tilsvarende fil) sin APPLET tag må endres i parameter-feltene

Vi skal nå legge til en ny Applet, og vise hvert steg;

 

1. Først lager vi en simpel Applet som viser en hvit ellipse på sort bakgrunn;

class nyapplet extends java.applet.Applet {
  public void paint(java.awt.Graphics g) {
    g.setColor(java.awt.Color.black);
    g.fillRect(0, 0, getSize().width, getSize().height);
    g.setColor(java.awt.Color.white);
    g.fillArc(0, 0, getSize().width, getSize().height, 0, 360);
  }
}

Din browser støtter ikke Java. Hvis den hadde gjort det, hadde du sett en hvit ellipse på sort bakgrunn.
Applet med hvit ellipse på sort bakgrunn.

 

2. For at denne Appleten skal kunne brukes i Aroma, må den implementere AromaInterface;

class nyapplet extends java.applet.Applet implements AromaInterface {
  public void paint(java.awt.Graphics g) {
    g.setColor(java.awt.Color.black);
    g.fillRect(0, 0, getSize().width, getSize().height);
    g.setColor(java.awt.Color.white);
    g.fillArc(0, 0, getSize().width, getSize().height, 0, 360);
  }
  public String getInfo() { return "infotekst til nyapplet"; }
  public String getTitle() { return "tittel til nyapplet "; }
  public String getListname() { return "nyapplet"; }
  public java.applet.Applet getApplet() { return this; }
}

 

3. Aroma.html (eller tilsvarende fil) sin APPLET tag må også endres:

<APPLET code = Aroma.class width=640 height=480>
  <PARAM NAME=NumberOfModules VALUE="4">
  <PARAM NAME=NameOfModule1 VALUE="dda">
  <PARAM NAME=NameOfModule2 VALUE="midp">
  <PARAM NAME=NameOfModule3 VALUE="scanline">
  <PARAM NAME=NameOfModule4 VALUE="nyapplet">
</APPLET>

 

Resultat (gif-bilde):

 


 

Aroma.java kildekode med kommentarer

Koden under gjør følgende (hovedtrekk):

- Leser HTML-parameterne og sjekker om Appletene som er referert der eksisterer, og om de virker skikkelig. Feilsjekking sørger for å gi beskjed i info-vinduet om noe er galt, og eventuelt hva som kan gjøres for å rette opp feilen. OK Applets legges inn i listeboksen.

- Lytter til valg i listeboksen og laster inn tilsvarende Applet, initierer og starter den. Hvis andre Applets kjører fra før, stoppes og destroyes de først.

- Lytter til trykk på resize-knappen som tar bort info-feltet og listeboksen, stopper og destroyer kjørende Applet, initierer og starter Appleten igjen med større felt (eller motsatt hvis man kjører i stort felt modus).

// Aroma v1999.mar.16

import java.applet.*;      // classes:    Applet
import java.awt.*;         // classes:    BorderLayout, Button, Color, Graphics, Image, Insets, Label, List, Panel, TextArea
import java.awt.event.* ;  // interfaces: ActionListener, ItemListener
import java.util.Vector;   // classes:    Vector

public class Aroma extends Applet implements ItemListener, ActionListener {
  final String version = "Aroma v1999.mar.16";         // version string
  int width, height;                                   // max width and height of this applet (updated in paint())
  Image splashimage;                                   // image loaded in beginning
  boolean showsplash = true;                           // show image until module choice
  boolean firstTime = true;                            // some things in paint is done only once
  boolean isSmall = true;                              // true if module-area is small
  boolean modulesLoaded = false;                       // true if modules are accessed first time
  int numberOfModulesParam;                            // number of modules (from parameterlist)
  int numberOfModulesLoaded = -1;                      // (how many modules actually found) - 1
  Vector moduleNameList = new Vector();                // storage of names of successfully found modules
  Panel northpanel = new Panel(new BorderLayout(0,0)); // contains title of moduleapplet and resize button
  Button bResize = new Button("big");                  // resize button
  Label title = new Label(version);                    // title of module on top of screen
  List list = new List();                              // listbox with choice of modules
  TextArea info;                                       // info about module on bottom of screen
  AromaInterface module = null;                        // module "handle" / "pointer"

  public void init()    { if (module!=null) module.getApplet().init();    }
  public void start()   { if (module!=null) module.getApplet().start();   }
  public void stop()    { if (module!=null) module.getApplet().stop();    }
  public void destroy() { if (module!=null) module.getApplet().destroy(); }
  public Insets getInsets() { return new Insets(3,3,3,3); }
  public String unknownErrorMessage() { return "This is a strange bug. If you know how to reproduce it, please notify the authors.\n\n"; }

  public boolean moduleLoader(String modName) {
    try { module = (AromaInterface)Class.forName(modName).newInstance(); }
    catch (ClassNotFoundException a) { info.append(modName + "... failure; ClassNotFoundException! --Maybe the module name is misspelled?\n");             return true; } // Thrown by forName

    catch (IllegalAccessException a) { info.append(modName + "... failure; IllegalAccessException!\n"); info.append(unknownErrorMessage());                return true; } // Thrown by newInstance

    catch (InstantiationException a) { info.append(modName + "... failure; InstantiationException! --Maybe the module is abstract?\n");                    return true; } // Thrown by newInstance

    catch (ClassCastException a)     { info.append(modName + "... failure; ClassCastException! --Maybe the module does not implement AromaInterface?\n");  return true; } // Thrown by Java itself (java.lang ?). The AromaInterface cast will not work if the module does not implement AromaInterface

    return false;
  }

  public void paint(Graphics g) {
    if (firstTime) {
      splashimage = getImage(getCodeBase(), "splash.gif");    // get splashimage
      numberOfModulesParam = java.lang.Integer.parseInt(getParameter("NumberOfModules"));
      width = getSize().width;                                // get width of applet
      height = getSize().height;                              // get height of applet
      int c=10; if (height<420) c=(int)(height/48);           // if height is unreasonable small, compensate with info-field
      info = new TextArea("", c, 80, TextArea.SCROLLBARS_VERTICAL_ONLY);
      info.setBackground(Color.white);
      info.setEditable(false);
      list.setBackground(Color.white);
      list.addItemListener(this);
      title.setBackground(Color.lightGray);
      setLayout(new BorderLayout(2,2));                       // 2,2 = Gaps between components
      setBackground(Color.lightGray);                         // Color of applet background
      bResize.setEnabled(false);
      bResize.addActionListener(this);
      northpanel.add("Center",title);
      northpanel.add("East",bResize);
      add("East", list);
      add("South", info);
      add("North", northpanel);
      firstTime = false;
      validate();
    }

    // draw edge around main applet
    g.setColor(Color.white);
    g.drawLine(0,0,width-2,0);
    g.drawLine(0,0,0,height-2);
    g.setColor(Color.black);
    g.drawLine(1,height-1,width-1,height-1);
    g.drawLine(width-1,1,width-1,height-1);

    if (showsplash==true) g.drawImage(splashimage,(int)((width-list.getSize().width-splashimage.getWidth(null)-3*3)/2)+3,(int)((height-info.getSize().height-northpanel.getSize().height-splashimage.getHeight(null)-4*3)/2)+6+northpanel.getSize().height,null);


    if (!modulesLoaded) {  // Tries to access all modules to get info about name and accessability
      String moduleName;
      info.append("HTML claims there are " + numberOfModulesParam + " modules available. Attempting to load:\n");
      for(int n=1; n<=numberOfModulesParam; n++) {
        moduleName = getParameter("NameOfModule"+n);
        if ((moduleName + "").compareTo("null") == 0) { info.append(moduleName + "... failure; No Class Found! --Maybe the NumberOfModules parameter has the wrong value?,\n                                                  or the NameOfModule parameter has the wrong index?\n"); continue; }

        if (moduleLoader(moduleName)) continue;        // Try to load the class, skip if error
        numberOfModulesLoaded++;                       // if no exception happened, we assume all is okay
        moduleNameList.addElement(moduleName);         // add modulename to modulenamelist
        list.addItem(module.getListname());            // add item to list
        info.append(moduleName + "... success\n");     // add success remark to info
      }
      info.append("ready.\n");
      modulesLoaded = true;
    } // if (!modulesLoaded)
  }

  // Interface ActionListener implementation... (for resize button)
  public void actionPerformed(java.awt.event.ActionEvent e) {
    int choice = list.getSelectedIndex();
    if (choice > -1) {
      if (module != null) {
        remove(module.getApplet());       // remove active module applet
        module.getApplet().stop();        // stop active module applet
        module.getApplet().destroy();     // destroy active module applet
      }

      if (isSmall == true) {
        remove(info);                     // remove info
        remove(list);                     // remove list
        isSmall = false;                  // the module applet view is now big
        bResize.setLabel("small");        // update button label to "small"
      } else {
        add("East", list);                // add list again
        add("South", info);               // add info again
        isSmall = true;                   // the module applet view is now small
        bResize.setLabel("big");          // update button label to "big"
      }

      add("Center", module.getApplet());  // add module applet
      module.getApplet().init();          // init module applet
      module.getApplet().start();         // start module applet
      validate();                         // get this.applet to re-layout its members
    }
  } // actionPerformed()


  // Interface ItemListener implementation... (for listbox)
  public void itemStateChanged(java.awt.event.ItemEvent e) {
    int choice = list.getSelectedIndex();
    boolean error;

    if (choice > -1) {
      showsplash=false;
      title.setText("");
      info.setText("");

      if (module != null) {
        remove(module.getApplet());        // remove active module applet
        module.getApplet().stop();         // stop active module applet
        module.getApplet().destroy();      // destroy active module applet
      }

      // Try to load the module (this should almost always work, since we have already established which modules work, and which dont)
      error = moduleLoader((String)moduleNameList.elementAt(choice));

      if (error==false) {
        title.setText(module.getTitle());  // update title
        info.setText(module.getInfo());    // update info
        add("Center", module.getApplet()); // add new module applet
        module.getApplet().init();         // init new module applet
        module.getApplet().start();        // start new module applet
        bResize.setEnabled(true);
        validate();                        // get this.applet to re-layout its members
      }
    }
  } // itemStateChanged()
} // Aroma()

 


 

dda.java kildekode med kommentarer

// dda v1999.mar.9

import java.applet.*;
import java.awt.*;        // classes: Panel, Point, Rectangle, Image, Color, Graphics
import java.awt.event.*;  // interfaces: AdjustmentListener (til scrollbars)

class dda extends Applet implements AromaInterface, AdjustmentListener, MouseListener {
  Image offscreen;                             // doublebuffer
  Scrollbar vertScroll, horiScroll;            // vertikal og horisontal scrollbar
  boolean firstTime;                           // holder rede på om paint er kjørt for første gang
  float dx, dy;                                // grid elementbredde og grid elementhøyde
  float k;                                     // faktor av skjermhøyde brukt til grid (mellom 0 og 1)
  int sizew, sizeh;                            // bredde og høyde på hele appleten
  int x, y;                                    // tellere i for-løkker
  int i, i2;                                   // forskjellige "insets"
  int scrollw;                                 // scrollbar bredde
  int tabw;                                    // lengde fra kant av scrollbar til midten av scrolltab når tab står i den ene siden
  int gh, gw;                                  // høyde og bredde på hele griden
  int nvert, nhoris;                           // antall gridelementer vertikalt og horisontalt
  int ax, ay, bx, by;                          // koordinater til øverste venstre gridhjørne og nederste høyre gridhjørne
  int scrVx, scrVy, scrVw, scrVh;              // vertikal scrollbar koordinater og størrelse
  int scrHx, scrHy, scrHw, scrHh;              // horisontal scrollbar koordinater og størrelse
  int fonth, fontasc;                          // fonthøyde og font-ascent
  int wheregx, wheregy, o_wheregx, o_wheregy;  // hvor scrollbaren er horisontalt og vertikalt
  int ix0, iy0, ix1, iy1;                      // koordinater til rektangel hvor info om variabler står
  int x0, x1, y0, y1, DX, DY, X, Y;            // synlige variabler relatert til dda algoritmen
  float m;                                     // stigning til linje

  // Constructor
  public dda() {
    setBackground(Color.lightGray);
    vertScroll = new Scrollbar(Scrollbar.VERTICAL, 0, 160, 0, 300);
    vertScroll.addAdjustmentListener(this);
    vertScroll.addMouseListener(this);
    horiScroll = new Scrollbar(Scrollbar.HORIZONTAL, 0, 160, 0, 300);
    horiScroll.addAdjustmentListener(this);
    horiScroll.addMouseListener(this);
  }

  public void init() {
    setLayout(null);   // bruker ikke noen layoutmanager; definerer alle koordinater selv
    add(vertScroll);
    add(horiScroll);
    firstTime = true;  // noen ting i paint skal kun skje en gang
  }

  // AromaInterface implementasjon
  public String getInfo() {
    return "\n"
          +"DDA (Digital Differential Analyzer)\n"
          +"\n"
          +"Den mest innlysende måten å tegne opp en linje på er å ta utgangspunkt i den matematiske formelen for en linje (y = m*x + b) hvor m er helningen og b er hvor langt opp på y-aksen linjen krysser. Hvis vi tar for oss en linje med -1 < m < 1, kan vi la x-verdien øke med 1 for hver vurdering av hvilken piksel som skal tennes. Poenget med dette er at vi kan utnytte det ved å lage en mer effektiv algoritme;\n"
          +"\n"
          +"     y{i+1}  =  m*x{i+1} + b  =  m*(x{i} + 1) + b  =  y{i} + m\n"
          +"\n"
          +"Altså: For å finne neste verdi for y, legger vi bare til m for hver x.\n"
          +"\n"
          +"Vi må i de fleste tilfeller runde av eksakt linjekoordinat til nærmeste heltallsverdi for å bestemme hvilken piksel som skal tennes. En avrundings-funksjon er som regel på formen;\n"
          +"\n"
          +"     (  x{i} ,  floor(y{i} + 0.5)  )\n"
          +"\n"
          +"Hvor floor betyr største heltall mindre eller lik y{i} (det samme som å typecaste til en int i Java og c++)\n"
          +"\n"
          +"Ulempen med denne metoden er at avrundingsfeilen som oppstår her blir akkumulert for hver iterasjon, noe som fører til at den tilpassede linjen kan få feil verdi i kritiske punkter. De kritiske punktene er der avstanden fra eksakt tilpassing til ideell linje er mindre enn den akkumulerte feilen.\n"
          +"\n"
          ;
  }
  public String getTitle() { return "Linjetegningsalgoritme / DDA"; }
  public String getListname() { return "Linje / DDA"; }
  public Applet getApplet() { return this; }

  // AdjustmentListener implementasjon
  public void adjustmentValueChanged(AdjustmentEvent e) {
    wheregx=(int)(((float)(horiScroll.getValue())/dx) + 0.5);        // få ny xverdi fra scrollbar
    wheregy=(int)((nvert-(float)(vertScroll.getValue())/dy) + 0.5);  // få ny yverdi fra scrollbar
    if ((o_wheregx != wheregx) || (o_wheregy != wheregy)) repaint(); // hvis x/y verdi har forandret verdi, tegn skjerm på nytt
    o_wheregx = wheregx;
    o_wheregy = wheregy;
  }

  public void mouseClicked(MouseEvent e) {}   // Invoked when the mouse has been clicked on a component.
  public void mouseEntered(MouseEvent e) {}   // Invoked when the mouse enters a component.
  public void mouseExited(MouseEvent e) {}    // Invoked when the mouse exits a component.
  public void mousePressed(MouseEvent e) {}   // Invoked when a mouse button has been pressed on a component.
  public void mouseReleased(MouseEvent e) {   // Invoked when a mouse button has been released on a component
    horiScroll.setValue((int)(wheregx*dx));           // tving horisontal scrollbar til heltallig grid-koordinat
    vertScroll.setValue((int)((nvert-wheregy)*dy));   // tving vertikal scrollbar til heltallig grid-koordinat
  }

  // en del av doublebuffer koden
  public void invalidate() {
    super.invalidate();
    offscreen = null;
  }

  // en del av doublebuffer koden
  public void update(Graphics g) { paint(g); }

  // konverterer grid-koordinater til verdenskoordinater i x-retning
  int grid(float gridcoord, float d, float a) {  //     1 <= gridcoord <= lvert
    return (int)((float)(gridcoord)*d + (float)a + 0.5);
  }

  // konverterer grid-koordinater til verdenskoordinater i y-retning
  int grid2(float gridcoord, float d, float a) {
    return (int)((float)(nvert-gridcoord)*d + (float)a + 0.5);
  }

  // tegner "ideell" linje
  void gridLine(Graphics g, float x, float y) {
    g.drawLine(grid(0,dx,ax), grid2(0,dy,ay), grid(x,dx,ax), grid2(y,dy,ay));
  }

  // tegner en "knott" med ønsket størrelse i grid
  void tegnknott(Graphics g, int x, int y, int w) {
    int d=w/40, r=w/80;
    g.fillArc(x-r, y-r, d, d, 0, 360);
  }

  // regner ut n'te y-verdi av dda-algoritmen
  int ddaline(float n, float x, float y) {
    return (int)((y/x)*n+0.5);
  }

  public void paint(Graphics g) {
    String stmp = new String();  // brukes til tekst ut på skjerm
    Integer itmp;                // brukes til tekst ut på skjerm
    Float ftmp;                  // brukes til tekst ut på skjerm

    // initialisering av disse variablene skal kun gjøres en gang
    // informasjon om hva disse variablene brukes til, står i toppen av dda-klassen
    if (firstTime==true) {
      sizew = getSize().width;
      sizeh = getSize().height;
      fonth = g.getFontMetrics().getHeight();
      fontasc = g.getFontMetrics().getAscent();
      k=(float)0.4;
      i=5;
      i2=8;
      scrollw=13;
      tabw=20;
      ax=i+tabw;
      ay=i+tabw;
      gh=(int)(k*(float)sizeh);
      gw=sizew-(3*i+2*tabw+scrollw);
      bx=ax+gw;
      by=ay+gh;
      scrVx=sizew-(i+scrollw);
      scrVy=i;
      scrVw=scrollw;
      scrVh=gh+2*tabw;
      scrHx=i;
      scrHy=by+tabw+i;
      scrHw=gw+2*tabw;
      scrHh=scrollw;
      nhoris=12;

      dx=(float)gw/(float)(nhoris);
      nvert=(int)(((float)gh/dx)+0.5);
      dy=(float)gh/(float)(nvert);

      vertScroll.setBounds(scrVx,scrVy,scrVw,scrVh);
      vertScroll.setVisibleAmount(1);
      vertScroll.setMinimum(0);
      vertScroll.setMaximum(by-ay);
      vertScroll.setBlockIncrement((int)dy);
      vertScroll.setUnitIncrement((int)dy);

      horiScroll.setBounds(scrHx,scrHy,scrHw,scrHh);
      horiScroll.setVisibleAmount(1);
      horiScroll.setMinimum(0);
      horiScroll.setMaximum(bx-ax);
      horiScroll.setBlockIncrement((int)dx);
      horiScroll.setUnitIncrement((int)dx);

      ix0 = ax;
      iy0 = scrHy+scrollw;
      ix1 = scrHx+scrHw;
      iy1 = sizeh-i;

      vertScroll.setValue(0);
      horiScroll.setValue((bx-ax)/2);
      wheregx=nhoris/2;
      wheregy=nvert;
      o_wheregx = wheregx;
      o_wheregy = wheregy;

      firstTime=false;
    }

    // initialiser doublebuffer
    if(offscreen == null) offscreen = createImage(getSize().width, getSize().height);
    Graphics og = offscreen.getGraphics();
    og.setClip(0,0,sizew,sizeh);
    super.paint(og);
    og.clearRect(0,0,sizew,sizeh);

    // tegn grid og linjetall
    og.setColor(Color.darkGray);
    for(x=0; x<=nhoris; x++) {
      og.drawLine(grid(x,dx,ax),ay-i2,grid(x,dx,ax),by+i2);
      itmp = new Integer(x);
      stmp = itmp.toString();
      og.drawString(stmp,grid(x,dx,ax)-og.getFontMetrics().stringWidth(stmp)/2,by+fontasc+i2+2);
    }
    for(y=0; y<=nvert; y++) {
      og.drawLine(ax-i2,grid(y,dy,ay),bx+i2,grid(y,dy,ay));
      itmp = new Integer(y);
      stmp = itmp.toString();
      og.drawString(stmp,ax-og.getFontMetrics().stringWidth(stmp)-i2-4, grid2(y,dy,ay)+fonth/2-2);
    }

    // tegn "ideell" linje
    og.setColor(Color.blue);
    gridLine(og, nhoris, wheregy);

    // tegn fyllte pixels
    og.setColor(Color.black);
    for(x=0; x<wheregx; x++) {
      tegnknott( og,  grid(x,dx,ax),  grid2(ddaline(x,nhoris,wheregy),dy,ay), sizew);
    }
    og.setColor(Color.blue);
    tegnknott( og,  grid(x,dx,ax),  grid2(ddaline(x,nhoris,wheregy),dy,ay), sizew);

    // skriv ut info om variabler
    og.setColor(Color.black);
    y1 = wheregy; y0 = 0;
    x1 = nhoris; x0 = 0;
    DY = y1-y0; DX = x1-x0;
    m = (float)(DY)/(float)(DX);

    og.drawString("dy  =  y1 - y0  =  " + new Integer(y1-y0).toString(), ax-i2, iy0 + ((iy1-iy0)/4)*1 + fonth/2);
    og.drawString("dy  =  x1 - x0  =  " + new Integer(x1-x0).toString(), ax-i2, iy0 + ((iy1-iy0)/4)*2 + fonth/2);
    og.drawString("m  =  dy / dx  =  " + new Float(m).toString(), ax-i2, iy0 + ((iy1-iy0)/4)*3 + fonth/2);
    og.drawString("x  =  " + new Integer(wheregx).toString(), ix1/2, iy0 + ((iy1-iy0)/3)*1 + fonth/2);

    og.setColor(Color.blue);
    og.drawString("y  =  floor(x * m + 0.5)  =  " + new Integer((int)(wheregx*m+0.5)).toString(), ix1/2, iy0 + ((iy1-iy0)/3)*2 + fonth/2);

    // Tegn offscreen bufferet til skjermen
    g.drawImage(offscreen, 0, 0, null);
    og.dispose();
  }
}

 


 

Kommentarer til scanline og midp

scanline er oppbygd på akkurat samme måte som dda. Det eneste som er forskjellig er koden for selve algoritmen som demonstreres.

midp's hovedforskjell fra dda og scanline er animeringen. Det foretas ikke noe særlig magisk animering i midp; alt tegnes opp på nytt for hvert frame. Denne rutinen kunne vi ha implementert på en annen, mer effektiv måte.

 


 

Tips til hvordan man tilpasser Applets til Aroma

Skjermstørrelse

Når man skal vise ting på skjermen er det viktig å ta hensyn til antall pixels vertikalt og horisontalt som man har tilgjengelig. Bruker man en LayoutManager trenger man ikke tenke noe over det, men skal man gjøre noe som krever litt presisjon på skjermen, duger ikke dette, og man må regne ut koordinater selv.

For å regne ut sine egne koordinater er man som sagt nødt til å vite bredden og høyden på Appleten. Dette kan gjøres med f.eks. getSize().width og getSize().height. For å ikke kalle disse funksjonene hver gang man trenger informasjonen, kan man sette variabler til disse verdiene. Som regel gjøres slik initialisering i init() funksjonen til en Applet.

Ulempen med dette er at man ikke kan være sikker på å få riktig informasjon fra systemet på det tidspunktet i koden. Erfaringer vi har gjort oss viser at man må vente helt til paint() funksjonen for å være sikker på at denne informasjonen er tilgjengelig. Løsningen er da å ha en boolean variabel som sørger for at en del av koden i paint() bare skjer en gang.

 

HTML-parametere

En av ulempene med å kjøre Applets i Aroma er at man ikke kan bruke HTML-parametere. Det finnes ikke noe direkte løsning for dette i Java (enda).

 


 

Utviklingsverktøy og Kilder

Kildekoden er skrevet i Microsoft Notepad 4.0 og/eller TextPad 3.2.5.

All kompilering er foretatt med Jikes 0.39 (ca. 6 ganger raskere enn javac).

Uttesting av Applets foretatt med Microsoft Internet Explorer 5.00.0910.1309.

Bearbeiding av bilder foretatt med Adobe Photoshop 5.0, Kinetix 3D Studio MAX 2.5, Microsoft Paint 4.0, Animagic 1.06a.

HTML-kode utarbeidet med Microsoft FrontPage Express 2.0.2.1118.

 

"Introduction to Computer Graphics", 1995, Foley, van Dam, Fiener, Hughes.

"Core Java Fundamentals", Horstmann, Cornell

"teach yourself Java in 21 days", Laura Lemay, Charles L. Perkins

"JavaTM Platform 1.2 API Specification", Sun

 


 

Hele prosjektet pakket i en zip-fil

Aroma.zip 


Jostein Trondal
Frode K. Kristensen
Høgskolen i Agder, våren 1999