// Java/Ring WiSe 1996/1997 (Jan/Feb 1997) // Gruppe : GRAPHENEDITOR // Programmierer: Tjabo Kloppenburg // Thomas B"oerngen // // Version: 1.0 (Fehlerbereinigte Endversion). // // Dieser Quellcode darf frei kopiert werden und als Programmier-Beispiel // genutzt werden - und sei es als Beispiel dafuer, das man auch mit Java // prozedural programmieren kann... ;-) // import java.awt.*; class _Knoten { // Jeder Knoten hat Koordinaten, kann markiert sein, int x; // und traegt eine persoenliche Nummer, auf die er int y; // stolz ist. boolean markiert; int nummer; } // Anmerkung: Keine Knoten-Methoden. Die Methoden // pfuschen mitunter bei den Verbindungen mit rein // und umgekehrt. // Deshalb sind die Knoten Methoden der Klasse graph09b. class _Verbindung { // Verbindungen (Kanten) zwischen Knoten. int von; // Einer von zwei Knoten int nach; // der andere, welcher sonst? :-) } public class graph10 extends java.applet.Applet { int KnotenName = 0; // Jeder neue Knoten soll eine neue Nummer erhalten int KnotenMax = 100; // Maximal 100 Knoten erlaubt. int num_knoten = 0; // Knoten-Index _Knoten Knoten[] = new _Knoten[KnotenMax]; // Feld von 100 Knoten. int num_verb = 0; // Index im Verbindungs-Feld int VerbindungMax = 120; _Verbindung Verbindung[] = new _Verbindung[VerbindungMax]; // Groesse der Knoten-Kaestchen von Font ableiten: private Font font = new Font("Helvetica",Font.BOLD,11); FontMetrics fm1 = this.getFontMetrics(font); int breite = fm1.stringWidth("888"); int hoehe = fm1.getAscent(); boolean im_knoten = false; // wurde IN einen Knoten geklickt? int aktueller_knoten; // Nummer des angeklickten Knotens. // Zustaende unseres endlichen Automaten: int schieben = 1; // schieben/markieren (->mouseDown) int hinzu = 2; // Knoten neu hinzufuegen int modus = schieben; boolean verschoben = false; // wurde geklickt und verschoben? // Optimierungen fuer schnellere Grafik (double_buffer): Image offscreen; // hierrein wird gezeichnet, und dann komplett oder // ausschnittsweise in das sichtbare Image kopiert. int lastx,lasty; // fuer das Verschieben von Knoten, letzte Koordinaten // --------------------------------------------------------------------- // Hinzufuegen eines neuen Knotens an den Koordinaten x,y: void KnotenHinzu(int x, int y) { if (num_knoten < KnotenMax) { _Knoten k = new _Knoten(); k.x = x; k.y = y; k.markiert = false; k.nummer = KnotenName++; Knoten[num_knoten] = k; num_knoten++; } } // Hinzufuegen einer neuen Verbindung zwischen 2 Knoten: void VerbindungHinzu(int v, int n) { if (num_verb < VerbindungMax) { _Verbindung ve = new _Verbindung(); ve.von = v; ve.nach = n; Verbindung[num_verb] = ve; num_verb++; } } // Wenn man einen Knoten loescht, aendern sich die Indices: void Verbindungen_refreshen(int alt, int neu) { for (int i=0; i< num_verb; i++) { if (Verbindung[i].von == alt) Verbindung[i].von = neu; if (Verbindung[i].nach == alt) Verbindung[i].nach = neu; } } // Damit am Anfang ein paar Knoten schon da sind: void KnotenInit() { KnotenHinzu( 50, 50); KnotenHinzu(250, 50); KnotenHinzu(250, 250); KnotenHinzu( 50, 250);// 50/50 250/50 VerbindungHinzu(0,1); // (_)-------(_) VerbindungHinzu(1,2); // | . ' | VerbindungHinzu(2,3); // | . | VerbindungHinzu(3,0); // (_)-------(_) VerbindungHinzu(3,1); // 50/250 250/250 // ein kleines Raetsel noch: :-) KnotenHinzu( 110, 110); // 4 KnotenHinzu( 190, 110); // 5 KnotenHinzu( 110, 190); // 6 KnotenHinzu( 190, 190); // 7 VerbindungHinzu(4,5); VerbindungHinzu(5,7); VerbindungHinzu(7,6); VerbindungHinzu(6,4); VerbindungHinzu(0,4); VerbindungHinzu(4,7); VerbindungHinzu(7,2); } public void init() { // war mal start(), war nicht so gut (reload)... KnotenInit(); add(new Button("Loeschen")); // Kontroll-Buttons: add(new Button("Verbindung")); add(new Button("Verschieben")); add(new Button("Knoten hinzu")); } // Event-Abfrage (Buttons): public boolean action(Event evt, Object arg) { int knoten1 = 0; int knoten2 = 0; if (evt.target instanceof Button) { if (arg.equals("Verschieben")) { modus = schieben; offpaint(); // neu painten mittels double-buffer. (im off) } else if (arg.equals("Knoten hinzu")) { modus = hinzu; offpaint(); } else // Loeschen der markierten Knoten inkl. Verbindungen: if (arg.equals("Loeschen")) { // zuerst jene Verbindungen, die markierte Knoten haben... int vb = 0; while (vb < num_verb) { if ( (Knoten[Verbindung[vb].von].markiert) || (Knoten[Verbindung[vb].nach].markiert)) { // Verbindung loeschen: (trickreich!) Verbindung[vb] = Verbindung[num_verb-1]; Verbindung[num_verb-1] = null; num_verb --; } // <--- nicht vb++, damit die hingeschobene Verbindung else { // auch noch getestet wird. vb++; } } // ...und dann die knoten: int kn = 0; while (kn < num_knoten) { if ((Knoten[kn] != null) && (Knoten[kn].markiert)) { Knoten[kn] = Knoten[num_knoten-1]; // Indices haben sich ge„ndert, flugs korrigieren: Verbindungen_refreshen((num_knoten-1),kn); Knoten[num_knoten-1] = null; num_knoten--; // nicht kn++, damit der hingeschobene Knoten auch } else { // noch gecheckt wird. kn++; } } // repaint(); offpaint(); } else if (arg.equals("Verbindung")) { int count = 0; for( int i=0 ; i= (Knoten[i].x - breite/2)) && (x <= (Knoten[i].x + breite/2)) && (y >= (Knoten[i].y - hoehe/2)) && (y <= (Knoten[i].y + hoehe/2)) ) { im_knoten = true; aktueller_knoten = i; } i++; } // NICHT in einem Knoten, ausserdem "Knoten-Hinzu"-Modus: if ((!im_knoten) && (modus == hinzu) && (y > 32) ) { KnotenHinzu(x,y); } verschoben = false; // um in MouseUp festzustellen, ob nur geklickt // oder auch verschoben wurde. if ((im_knoten) && (modus == schieben)) { // merken... lastx = Knoten[aktueller_knoten].x; lasty = Knoten[aktueller_knoten].y; } return true; } public boolean mouseDrag(Event evt, int x, int y) { if ((im_knoten) && (modus == schieben)) { lastx = Knoten[aktueller_knoten].x; lasty = Knoten[aktueller_knoten].y; Knoten[aktueller_knoten].x = x; Knoten[aktueller_knoten].y = y; // repaint(); // painten des Ausschnitts, in dem sich etwas veraendert hat: offpaint_clip(aktueller_knoten,lastx,lasty); } verschoben = true; return true; } public boolean mouseUp(Event evt, int x, int y) { if ((im_knoten) && (!verschoben)) { // if ((im_knoten) && (!verschoben) && (modus == schieben)) { Knoten[aktueller_knoten].markiert = ! Knoten[aktueller_knoten].markiert; } verschoben = false; if ((modus==hinzu) || (im_knoten)) // repaint(); offpaint(); // komplett neuzeichnen, ist sicherer. return true; } // Hier folgen die paint-Methoden, die mit double-buffering arbeiten. // Das bedeutet, das Bild wird in einem nicht-sichtbaren Bild komplett neu // aufgebaut, und dann als ganzes sichtbar gemacht. // Methode 1, Sichtbarmachung des kompletten neuen Bildes: // -------------------------------------------------------------- // double_buffering ohne clipping: // -------------------------------------------------------------- public void offpaint() { Dimension d = this.size(); // Make sure the offscreen image is created and is the right size. if (offscreen == null) { // if (offscreen != null) offscreen.flush(); offscreen = this.createImage(d.width, d.height); } Graphics g = offscreen.getGraphics(); // g.clipRect(r.x, r.y, r.width, r.height); // Draw into the off-screen image. paint(g); // Copy it all at once to the screen, using clipping. g = this.getGraphics(); // g.clipRect(r.x, r.y, r.width, r.height); g.drawImage(offscreen, 0, 0, this); } // Methode 1, Sichtbarmachung des Teilausschnitts mit Veraenderungen // -------------------------------------------------------------- // double_buffering MIT clipping: // -------------------------------------------------------------- // ist nicht optimiert, daher recht schneckig... :-) public void offpaint_clip(int kn_num, int last_x, int last_y) { int xmax,ymax,xmin,ymin; Dimension d = this.size(); // Make sure the offscreen image is created and is the right size. if (offscreen == null) { // if (offscreen != null) offscreen.flush(); offscreen = this.createImage(d.width, d.height); } // Clipping-Rechteck bestimmen, dazu schauen, welche Knoten mit dem aktuellen // verbunden sind, und das kleinste X, das groesste X, das kleinste Y, sowie // das groesste Y ermitteln: if (last_x > Knoten[kn_num].x) { xmax = last_x; xmin = Knoten[kn_num].x; } else { xmax = Knoten[kn_num].x; xmin = last_x; } if (last_y > Knoten[kn_num].y) { ymax = last_y; ymin = Knoten[kn_num].y; } else { ymax = Knoten[kn_num].y; ymin = last_y; } int i=num_verb-1; while ( i >= 0) { if ((Verbindung[i].von == kn_num) || (Verbindung[i].nach == kn_num)) { // Verbindung mit unserem verschobenen Knoten! if (Knoten[Verbindung[i].von].x < xmin) xmin = Knoten[Verbindung[i].von].x; if (Knoten[Verbindung[i].von].x > xmax) xmax = Knoten[Verbindung[i].von].x; if (Knoten[Verbindung[i].von].y < ymin) ymin = Knoten[Verbindung[i].von].y; if (Knoten[Verbindung[i].von].y > ymax) ymax = Knoten[Verbindung[i].von].y; if (Knoten[Verbindung[i].nach].x < xmin) xmin = Knoten[Verbindung[i].nach].x; if (Knoten[Verbindung[i].nach].x > xmax) xmax = Knoten[Verbindung[i].nach].x; if (Knoten[Verbindung[i].nach].y < ymin) ymin = Knoten[Verbindung[i].nach].y; if (Knoten[Verbindung[i].nach].y > ymax) ymax = Knoten[Verbindung[i].nach].y; } i--; } xmax = xmax + breite; xmin = xmin - breite; // if (xmin<0) xmax = 0; ymax = ymax + hoehe ; ymin = ymin - hoehe ; // if (ymin<0) ymax = 0; Graphics g = offscreen.getGraphics(); g.clipRect(xmin, ymin, xmax-xmin, ymax-ymin); // Draw into the off-screen image. paint(g); // Copy it all at once to the screen, using clipping. g = this.getGraphics(); g.clipRect(xmin, ymin, xmax-xmin, ymax-ymin); g.drawImage(offscreen, 0, 0, this); } // Hier die eigentliche paint()-Methode: // -------------------------------------------------------------- public void paint(Graphics g) { // Bildschirm "loeschen": Rectangle r = this.bounds(); g.setColor(this.getBackground()); g.fillRect(r.x, r.y, r.width-1, r.height-1); g.setColor(Color.black); // und einen 3D-Rahmen drumherum: g.drawRect(r.x, r.y, r.width-1, r.height-1); g.setColor(Color.white); g.drawLine(r.x , r.y + r.height-1 , r.x + r.width-1, r.y + r.height-1); g.drawLine(r.x + r.width-1, r.y , r.x + r.width-1, r.y + r.height-1); // aktuellen Modus anzeigen: g.setColor(Color.black); if (modus==1) g.drawString("Modus: Verschieben",10,20); if (modus==2) g.drawString("Modus: Knoten hinzu",10,20); // alle Verbindungen zeichnen: for(int i=0 ; i