mirror of
https://codeberg.org/qg-info-unterricht/zpg-graphentester.git
synced 2026-03-25 04:58:24 +01:00
678 lines
25 KiB
Java
678 lines
25 KiB
Java
package graph;
|
|
|
|
import imp.*;
|
|
import javafx.beans.property.BooleanProperty;
|
|
import javafx.beans.property.ObjectProperty;
|
|
import javafx.beans.property.SimpleObjectProperty;
|
|
import javafx.beans.property.SimpleBooleanProperty;
|
|
import java.awt.Graphics2D;
|
|
|
|
import javafx.geometry.Point2D;
|
|
import javafx.geometry.Pos;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.input.MouseButton;
|
|
import java.nio.file.*;
|
|
import java.net.URI;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import javafx.scene.control.Tooltip;
|
|
import javafx.util.Duration;
|
|
import javafx.animation.AnimationTimer;
|
|
import java.awt.image.*;
|
|
|
|
import org.apache.commons.io.FileUtils;
|
|
/**
|
|
* Der GraphPlotter ist das Herzstueck der Visualisierung und dient als Schnittstelle zur GUI.
|
|
*
|
|
* @author Thomas Schaller
|
|
* @version 07.02.2023 (v7.0)
|
|
* v6.9: Context-Menü schließt, wenn an andere Stelle geklickt wird
|
|
* v7.0: MouseOver - Infos für Knoten und Kanten, Infos können ausgewählt werden.
|
|
*
|
|
*/
|
|
public class GraphPlotter extends PictureViewer {
|
|
// Anfang Attribute
|
|
private Graph graph;
|
|
private GraphOptions options;
|
|
|
|
private boolean multiselect = false;
|
|
private boolean editable = false;
|
|
private ArrayList<GraphElement> selected=new ArrayList<GraphElement>();
|
|
private Knoten dragKnoten = null;
|
|
private int dragMode = 0;
|
|
|
|
// Tooltip
|
|
private double mouseX, mouseY;
|
|
private Tooltip t;
|
|
|
|
private GraphElement restrictTo = null;
|
|
private Point2D offset = new Point2D(0,0);
|
|
|
|
private ObjectProperty<Point2D> mouseLocation = new SimpleObjectProperty<Point2D>(new Point2D(0, 0));
|
|
private BooleanProperty mouseMoving = new SimpleBooleanProperty();
|
|
|
|
// private JTextArea jTAMeldungen = new JTextArea("");
|
|
// private JScrollPane jTAMeldungenScrollPane = new JScrollPane(jTAMeldungen);
|
|
|
|
// Ende Attribute
|
|
|
|
/**
|
|
* Der Konstruktor legt sowohl Einstellungen des mxGraphen (Drag&Drop, Editable, ...) als auch des Graphen (gewichtet, gerichtet, ...) fest.
|
|
*
|
|
* @param boolean isDirected Gibt an, ob der Graph gerichtet oder ungerichtet ist
|
|
* @param boolean isWeighted Gibt an, ob der Graph gewichtet oder ungewichtet ist
|
|
* @param String hintergrundBild Gibt den Namen eines Hintergrundbildes an
|
|
*/
|
|
public GraphPlotter() {
|
|
graph = new Graph();
|
|
options = new GraphOptions(graph);
|
|
|
|
this.setStyle("-fx-background:#FFFFE8");
|
|
|
|
// add(jTAMeldungenScrollPane, BorderLayout.SOUTH);
|
|
setOnMouseClicked(mouseEvent -> mouseClicked(mouseEvent));
|
|
this.widthProperty().addListener((value, oldWidth, newWidth) -> updateImage());
|
|
this.heightProperty().addListener((value, oldWidth, newWidth) -> updateImage());
|
|
|
|
setOnMouseMoved(e -> mouseLocation.set(new Point2D(e.getSceneX(), e.getSceneY())));
|
|
mouseMoving.addListener((obs, wasMoving, isNowMoving) -> {
|
|
updateImage();
|
|
});
|
|
|
|
AnimationTimer timer = new AnimationTimer() {
|
|
private double lastMouseX ;
|
|
private double lastMouseY ;
|
|
long lastMouseMovement ;
|
|
long MIN_STATIONARY_TIME = 2000;
|
|
@Override
|
|
public void handle(long timestamp) {
|
|
double x = mouseLocation.get().getX();
|
|
double y = mouseLocation.get().getY();
|
|
if (Math.abs(lastMouseX-x) > 5 || Math.abs(lastMouseY-y)>5) {
|
|
lastMouseMovement = timestamp ;
|
|
lastMouseX = x;
|
|
lastMouseY = y;
|
|
}
|
|
|
|
mouseMoving.set(timestamp - lastMouseMovement < MIN_STATIONARY_TIME);
|
|
|
|
}
|
|
};
|
|
timer.start();
|
|
|
|
}
|
|
|
|
public void setEditable() {
|
|
editable = true;
|
|
//setOnMousePressed(mouseEvent -> mouseDown(mouseEvent)); // wird durch MousePressed in EditTab realisiert und dann hier aufgerufen
|
|
setOnMouseReleased(mouseEvent -> mouseUp(mouseEvent));
|
|
setOnDragDetected(mouseEvent -> startFullDrag());
|
|
setOnMouseDragOver(mouseEvent-> mouseDragged(mouseEvent));
|
|
setOnMouseClicked(null);
|
|
}
|
|
|
|
public void setGraph(Graph graph, GraphOptions options) {
|
|
this.graph = graph;
|
|
this.options = options;
|
|
updateImage();
|
|
}
|
|
|
|
public void setRestrictTo(GraphElement k) {
|
|
if(restrictTo != k) {
|
|
restrictTo = k;
|
|
selected.clear();
|
|
if (k!= null) selected.add(k);
|
|
updateImage();
|
|
}
|
|
}
|
|
|
|
public GraphElement getRestrictTo() {
|
|
return restrictTo;
|
|
}
|
|
|
|
public void mouseClicked(MouseEvent mouseEvent) {
|
|
|
|
Point2D local = this.getContent().sceneToLocal(mouseEvent.getSceneX(), mouseEvent.getSceneY());
|
|
GraphElement k = getKnotenAt((int) local.getX(), (int) local.getY());
|
|
if(k!=null && mouseEvent.isShiftDown() && restrictTo != null && restrictTo instanceof Knoten) { // mit Shift-Knotenklick Kante auswählen, damit unsichtbare Kanten gewählt werden können (für TSP)
|
|
k = graph.getKante((Knoten) restrictTo, (Knoten) k);
|
|
}
|
|
if(k!=null && mouseEvent.isShiftDown() && selected.size() == 1 && selected.get(0) instanceof Knoten) { // mit Shift-Knotenklick Kante auswählen, damit unsichtbare Kanten gewählt werden können (für TSP)
|
|
k = graph.getKante((Knoten) (selected.get(0)), (Knoten) k);
|
|
}
|
|
if(k==null) k = getKanteAt((int) local.getX(), (int) local.getY());
|
|
if(!multiselect) {
|
|
selected.clear();
|
|
}
|
|
if(k != null) {
|
|
if(selected.contains(k)) selected.remove(k);
|
|
else selected.add(k);
|
|
}
|
|
updateImage();
|
|
}
|
|
|
|
public void mouseDown(MouseEvent mouseEvent) {
|
|
|
|
if(mouseEvent.isPrimaryButtonDown()) {
|
|
Point2D local = this.getContent().sceneToLocal(mouseEvent.getSceneX(), mouseEvent.getSceneY());
|
|
dragKnoten = getKnotenAt((int) local.getX(), (int) local.getY());
|
|
dragMode = 3; // Linksclick aber nicht auf Knoten
|
|
if(dragKnoten != null) {
|
|
mouseEvent.setDragDetect(true);
|
|
double distance = Math.sqrt(Math.pow(local.getX()-dragKnoten.getX(),2)+Math.pow(local.getY()-dragKnoten.getY(),2));
|
|
if(distance < options.vertexSize/4)
|
|
dragMode = 1; // Knoten verschieben
|
|
else
|
|
dragMode = 2; // Kante ziehen
|
|
}
|
|
}
|
|
}
|
|
|
|
public void mouseDragged(MouseEvent mouseEvent) {
|
|
|
|
if(dragKnoten != null) {
|
|
Point2D local = this.getContent().sceneToLocal(mouseEvent.getSceneX(), mouseEvent.getSceneY());
|
|
if(dragMode == 1) {
|
|
dragKnoten.setX((int) (local.getX()));
|
|
dragKnoten.setY((int) (local.getY()));
|
|
}
|
|
updateImage();
|
|
if(dragMode == 2) {
|
|
Picture p = getImage();
|
|
p.stroke(0);
|
|
p.strokeWeight(3);
|
|
//p.line(dragKnoten.getX(), dragKnoten.getY(), (int) local.getX(), (int) local.getY());
|
|
drawArrow(p,dragKnoten.getX(), dragKnoten.getY(), (int) local.getX(), (int) local.getY());
|
|
setImage(p,false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void mouseUp(MouseEvent mouseEvent) {
|
|
|
|
Point2D local = this.getContent().sceneToLocal(mouseEvent.getSceneX(), mouseEvent.getSceneY());
|
|
Knoten k = getKnotenAt((int) local.getX(), (int) local.getY());
|
|
if(dragMode == 3 && k==null && getKanteAt((int) local.getX(), (int) local.getY())==null) { // neuer Knoten
|
|
if(getContextMenu() == null) {
|
|
graph.neuerKnoten(new Knoten((int)local.getX(), (int) local.getY())) ;
|
|
} else { setContextMenu(null); }
|
|
} else {
|
|
if(dragMode == 2 && k != null && k != dragKnoten) {
|
|
graph.neueKante(dragKnoten, k, 0.0);
|
|
}
|
|
}
|
|
dragKnoten = null;
|
|
dragMode = 0;
|
|
mouseEvent.setDragDetect(false);
|
|
updateImage();
|
|
|
|
}
|
|
|
|
private Knoten getKnotenAt(int x, int y) {
|
|
List<Knoten> knoten = graph.getAlleKnoten();
|
|
if(restrictTo != null) {
|
|
knoten.clear();
|
|
if(restrictTo instanceof Knoten) {
|
|
knoten = graph.getNachbarknoten((Knoten) restrictTo);
|
|
knoten.add((Knoten) restrictTo);
|
|
}
|
|
if(restrictTo instanceof Kante) {
|
|
knoten.add(((Kante) restrictTo).getStart());
|
|
knoten.add(((Kante) restrictTo).getZiel());
|
|
}
|
|
|
|
}
|
|
|
|
for(Knoten k : knoten) {
|
|
if(Math.sqrt(Math.pow(k.getX()-x,2)+Math.pow(k.getY()-y,2)) < options.vertexSize/2+1) {
|
|
return k;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private Kante getKanteAt(int x, int y) {
|
|
List<Kante> kanten = graph.getAlleKanten();
|
|
if(restrictTo != null) {
|
|
kanten.clear();
|
|
if(restrictTo instanceof Knoten) {
|
|
kanten = graph.getAusgehendeKanten((Knoten) restrictTo);
|
|
}
|
|
if(restrictTo instanceof Kante) {
|
|
kanten.add(((Kante) restrictTo));
|
|
}
|
|
|
|
}
|
|
|
|
for (Kante k: kanten) {
|
|
if (x>=Math.min(k.getStart().getX(), k.getZiel().getX())-2 &&
|
|
x<=Math.max(k.getStart().getX(), k.getZiel().getX())+2 &&
|
|
y>=Math.min(k.getStart().getY(), k.getZiel().getY())-2 &&
|
|
y<=Math.max(k.getStart().getY(), k.getZiel().getY())+2) {
|
|
double startX = k.getStart().getX();
|
|
double startY = k.getStart().getY();
|
|
double endX = k.getZiel().getX();
|
|
double endY = k.getZiel().getY();
|
|
double dy = (endY-startY);
|
|
double dx = (endX-startX);
|
|
double l = Math.sqrt(dx*dx+dy*dy);
|
|
dy = dy /l; dx = dx / l;
|
|
startX += dx * (options.vertexSize/2+1);
|
|
startY += dy * (options.vertexSize/2+1);
|
|
endX -= dx * (options.vertexSize/2+1);
|
|
endY -= dy * (options.vertexSize/2+1);
|
|
double dx2 = dy*5;
|
|
double dy2 = -dx*5;
|
|
if (graph.isGerichtet() && graph.getKante(k.getZiel(), k.getStart())!=null){
|
|
startX += dx2;
|
|
startY += dy2;
|
|
endX += dx2;
|
|
endY += dy2;
|
|
}
|
|
double nx = dy;
|
|
double ny = -dx;
|
|
double abx = x - startX;
|
|
double aby = y - startY;
|
|
double abs = Math.abs(abx*nx+aby*ny);
|
|
if (abs < 3) return k;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void drawArrow(Picture p, int startx, int starty, int endx, int endy) {
|
|
|
|
double deltax = startx - endx;
|
|
double result;
|
|
|
|
if (deltax == 0.0d) {
|
|
result = (starty > endy ? Math.PI / 2 : -Math.PI / 2);
|
|
}
|
|
else {
|
|
result = Math.atan((starty - endy) / deltax) + (startx < endx ? Math.PI : 0);
|
|
}
|
|
|
|
double angle = result;
|
|
double arrowAngle = Math.PI / 8.0d;
|
|
double arrowSize = 10;
|
|
double x1 = arrowSize * Math.cos(angle - arrowAngle);
|
|
double y1 = arrowSize * Math.sin(angle - arrowAngle);
|
|
double x2 = arrowSize * Math.cos(angle + arrowAngle);
|
|
double y2 = arrowSize * Math.sin(angle + arrowAngle);
|
|
double cx = (arrowSize / 2.0f) * Math.cos(angle);
|
|
double cy = (arrowSize / 2.0f) * Math.sin(angle);
|
|
int x[] = {(int) endx, (int) (endx+x1), (int) (endx+x2), (int) endx};
|
|
int y[] = {(int) endy, (int) (endy+y1), (int) (endy+y2), (int) endy};
|
|
p.line(startx, starty, endx, endy);
|
|
p.polygon(x,y);
|
|
|
|
}
|
|
|
|
private String darker(String color) {
|
|
String red = color.substring(0, 2);
|
|
String green = color.substring(2,4);
|
|
String blue = color.substring(4,6);
|
|
long r = Long.parseLong(red,16)/2;
|
|
long g = Long.parseLong(green,16) /2;
|
|
long b = Long.parseLong(blue,16) / 2;
|
|
String sr = "0"+Long.toHexString(r);
|
|
String sg = "0"+Long.toHexString(g);
|
|
String sb = "0"+Long.toHexString(b);
|
|
return sr.substring(sr.length()-2)+sg.substring(sg.length()-2)+sb.substring(sb.length()-2);
|
|
}
|
|
|
|
private String brighter(String color) {
|
|
String red = color.substring(0, 2);
|
|
String green = color.substring(2,4);
|
|
String blue = color.substring(4,6);
|
|
long r = (Long.parseLong(red,16)+255)/2;
|
|
long g = (Long.parseLong(green,16)) /2;
|
|
long b = (Long.parseLong(blue,16)) / 2;
|
|
String sr = "0"+Long.toHexString(r);
|
|
String sg = "0"+Long.toHexString(g);
|
|
String sb = "0"+Long.toHexString(b);
|
|
return sr.substring(sr.length()-2)+sg.substring(sg.length()-2)+sb.substring(sb.length()-2);
|
|
}
|
|
|
|
private String format(double d) {
|
|
if((int) d == d) {
|
|
return ""+(int) d;
|
|
} else {
|
|
return ""+d;
|
|
}
|
|
}
|
|
|
|
public Picture updateImage() {
|
|
Picture p = new Picture(2000,2000,"FFFFE8");
|
|
Graphics2D g = (Graphics2D) p.getImage().getGraphics();
|
|
|
|
Knoten restrictToKnoten = null;
|
|
Kante restrictToKante = null;
|
|
if(restrictTo != null && restrictTo instanceof Knoten) restrictToKnoten = (Knoten) restrictTo;
|
|
if(restrictTo != null && restrictTo instanceof Kante) restrictToKante = (Kante) restrictTo;
|
|
|
|
List<Knoten> knoten = graph.getAlleKnoten();
|
|
List<Kante> kanten = graph.getAlleKanten();
|
|
|
|
int maxx = Integer.MIN_VALUE; int maxy = Integer.MIN_VALUE;
|
|
int minx = Integer.MAX_VALUE; int miny = Integer.MAX_VALUE;
|
|
for(Knoten k: knoten) {
|
|
maxx = Math.max(maxx,k.getX());
|
|
minx = Math.min(minx,k.getX());
|
|
maxy = Math.max(maxy,k.getY());
|
|
miny = Math.min(miny,k.getY());
|
|
}
|
|
|
|
if(restrictToKnoten != null) {
|
|
knoten = graph.getNachbarknoten(restrictToKnoten);
|
|
kanten = graph.getAusgehendeKanten(restrictToKnoten);
|
|
knoten.add(restrictToKnoten);
|
|
// this.getContent().setTranslateY(this.getHeight()/2-restrictToKnoten.getY());
|
|
// this.getContent().setTranslateX(this.getWidth()/2-restrictToKnoten.getX());
|
|
this.getContent().setTranslateY(1000-(restrictToKnoten.getY()));
|
|
this.getContent().setTranslateX(1000-(restrictToKnoten.getX()));
|
|
} else if (restrictToKante != null ) {
|
|
kanten.clear();
|
|
kanten.add(restrictToKante);
|
|
knoten.clear();
|
|
knoten.add(restrictToKante.getStart());
|
|
knoten.add(restrictToKante.getZiel());
|
|
// this.getContent().setTranslateY(this.getHeight()/2-restrictToKnoten.getY());
|
|
// this.getContent().setTranslateX(this.getWidth()/2-restrictToKnoten.getX());
|
|
this.getContent().setTranslateX(1000-(restrictToKante.getStart().getX()+restrictToKante.getZiel().getX())/2);
|
|
this.getContent().setTranslateY(1000-(restrictToKante.getStart().getY()+restrictToKante.getZiel().getY())/2);
|
|
}
|
|
else
|
|
{
|
|
/*this.getContent().setTranslateX(1000-(minx+maxx)/2);
|
|
this.getContent().setTranslateY(1000-(miny+maxy)/2);*/
|
|
}
|
|
p.textMode(Picture.CENTER);
|
|
|
|
if(options.bildAnzeigen && !options.bildDatei.isEmpty()) {
|
|
Picture p2 = new Picture("./images/"+options.bildDatei);
|
|
p.getImage().getGraphics().drawImage(p2.getImage(), 0, 0, null);
|
|
minx = options.vertexSize;
|
|
miny = options.vertexSize;
|
|
maxx = p2.getWidth()-options.vertexSize;
|
|
maxy = p2.getHeight()-options.vertexSize;
|
|
}
|
|
|
|
// Zone in der Mitte markieren
|
|
if(restrictToKnoten!=null) {
|
|
p.fill("FFE8E8");
|
|
p.stroke("FFE8E8");
|
|
p.ellipse(restrictToKnoten.getX(), restrictToKnoten.getY(), options.vertexSize*2, options.vertexSize*2);
|
|
}
|
|
if(restrictToKante!=null) {
|
|
p.fill("FFE8E8");
|
|
p.stroke("FFE8E8");
|
|
p.strokeWeight(30);
|
|
p.line(restrictToKante.getStart().getX(), restrictToKante.getStart().getY(), restrictToKante.getZiel().getX(),restrictToKante.getZiel().getY());
|
|
}
|
|
|
|
for (Kante k : kanten) {
|
|
if (!options.farbenKanten[k.getFarbe()].equals("invisible") || selected.contains(k)) {
|
|
|
|
double startX = k.getStart().getX();
|
|
double startY = k.getStart().getY();
|
|
double endX = k.getZiel().getX();
|
|
double endY = k.getZiel().getY();
|
|
double dy = (endY-startY);
|
|
double dx = (endX-startX);
|
|
double l = Math.sqrt(dx*dx+dy*dy);
|
|
dy = dy /l; dx = dx / l;
|
|
startX += dx * (options.vertexSize/2+1);
|
|
startY += dy * (options.vertexSize/2+1);
|
|
endX -= dx * (options.vertexSize/2+1);
|
|
endY -= dy * (options.vertexSize/2+1);
|
|
double dx2 = dy*5;
|
|
double dy2 = -dx*5;
|
|
if (graph.isGerichtet() && graph.getKante(k.getZiel(), k.getStart())!=null){
|
|
startX += dx2;
|
|
startY += dy2;
|
|
endX += dx2;
|
|
endY += dy2;
|
|
}
|
|
|
|
if(selected.contains(k)) {
|
|
p.stroke("FF0000");
|
|
p.strokeWeight(4);
|
|
p.line((int) startX, (int) startY, (int) endX, (int) endY);
|
|
}
|
|
if (!options.farbenKanten[k.getFarbe()].equals("invisible")){
|
|
p.stroke(options.farbenKanten[k.getFarbe()]);
|
|
p.fill(options.farbenKanten[k.getFarbe()]);
|
|
}
|
|
else
|
|
{
|
|
p.stroke("505050");
|
|
p.fill("505050");
|
|
}
|
|
|
|
p.strokeWeight(2);
|
|
|
|
if(graph.isGerichtet()) {
|
|
drawArrow(p, (int) startX, (int) startY, (int) endX, (int) endY);
|
|
} else {
|
|
p.line((int) startX, (int) startY, (int) endX, (int) endY);
|
|
}
|
|
}
|
|
}
|
|
for (Kante k : kanten) {
|
|
if (!options.farbenKanten[k.getFarbe()].equals("invisible") || selected.contains(k)) {
|
|
double startX = k.getStart().getX();
|
|
double startY = k.getStart().getY();
|
|
double endX = k.getZiel().getX();
|
|
double endY = k.getZiel().getY();
|
|
double dy = (endY-startY);
|
|
double dx = (endX-startX);
|
|
double l = Math.sqrt(dx*dx+dy*dy);
|
|
dy = dy /l; dx = dx / l;
|
|
startX += dx * (options.vertexSize/2+1);
|
|
startY += dy * (options.vertexSize/2+1);
|
|
endX -= dx * (options.vertexSize/2+1);
|
|
endY -= dy * (options.vertexSize/2+1);
|
|
double dx2 = dy*5;
|
|
double dy2 = -dx*5;
|
|
|
|
if(graph.isGerichtet()) {
|
|
if (graph.getKante(k.getZiel(), k.getStart())!=null){
|
|
startX += dx2;
|
|
startY += dy2;
|
|
endX += dx2;
|
|
endY += dy2;
|
|
}
|
|
|
|
}
|
|
if(options.showEdgeWeights) {
|
|
|
|
double my = (startY+startY+endY)/3;
|
|
double mx = (startX+startX+endX)/3;
|
|
|
|
p.fill(255);
|
|
p.stroke(0);
|
|
p.strokeWeight(1);
|
|
|
|
int lh = g.getFontMetrics().getAscent();
|
|
List<String> t = k.getKurztext(options.kanteKurztext);
|
|
if(t.size() == 1) {
|
|
p.rect((int) mx-15, (int) my-(lh+4)/2, 30, lh+4);
|
|
p.fill(0);
|
|
p.text(t.get(0), (int) mx, (int) my);
|
|
}
|
|
if(t.size() > 1) {
|
|
p.rect((int) mx-15, (int) my-(lh+2), 30, lh*2+4);
|
|
p.fill(0);
|
|
p.text(t.get(0), (int) mx, (int) my-lh/2);
|
|
p.text(t.get(1), (int) mx, (int) my+lh/2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Knoten k : knoten) {
|
|
|
|
p.fill(options.farbenKnoten[k.getFarbe()]);
|
|
|
|
p.stroke(darker(options.farbenKnoten[k.getFarbe()]));
|
|
p.strokeWeight(3);
|
|
p.ellipse(k.getX(), k.getY(), options.vertexSize, options.vertexSize);
|
|
if(selected.contains(k)) {
|
|
p.noFill();
|
|
p.strokeWeight(2);
|
|
p.stroke(255,0,0);
|
|
p.ellipse(k.getX(), k.getY(), options.vertexSize+2, options.vertexSize+2);
|
|
}
|
|
|
|
p.fill(0);
|
|
p.stroke(0);
|
|
p.strokeWeight(0);
|
|
p.textMode(Picture.CENTER);
|
|
if (options.showVertexText) {
|
|
p.text(""+graph.getNummer(k), k.getX(), k.getY());
|
|
} else {
|
|
if (options.showVertexValue) {
|
|
List<String> t = k.getKurztext(options.knotenKurztext);
|
|
if(t.size() == 1) {
|
|
p.text(t.get(0), k.getX(), k.getY());
|
|
} else {
|
|
int lh = g.getFontMetrics().getAscent();
|
|
p.text(t.get(0), k.getX(), k.getY()-lh/2);
|
|
p.text(t.get(1), k.getX(), k.getY()+lh/2);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Knotenbezeichnung
|
|
p.textMode(Picture.CORNER);
|
|
p.fill("000080");
|
|
if(options.showVertexInfo && !k.getInfotext().equals("")) {
|
|
p.text(k.getInfotext(), k.getX()+options.vertexSize/2+5,k.getY());
|
|
}
|
|
|
|
}
|
|
// Tooltip anzeigen, aber nicht wenn im Editiermodus
|
|
if(!mouseMoving.get() && !editable) {
|
|
Point2D local = this.getContent().sceneToLocal(mouseLocation.get().getX(), mouseLocation.get().getY());
|
|
|
|
int x = (int) local.getX();
|
|
int y = (int) local.getY();
|
|
|
|
// sowohl bei Kante wie auch Knoten
|
|
GraphElement k = getKnotenAt(x,y);
|
|
if(k == null) { k = getKanteAt(x,y);}
|
|
if(k != null) {
|
|
p.fill(200);
|
|
p.stroke(0);
|
|
p.strokeWeight(2);
|
|
|
|
List<String> t;
|
|
if(k instanceof Knoten) t = k.getLangtext(options.knotenLangtext);
|
|
else t = k.getLangtext(options.kanteLangtext);
|
|
|
|
// Größe des Kastens berechnen
|
|
int w = 0;
|
|
int lh = g.getFontMetrics().getAscent();
|
|
int h = t.size() * lh;
|
|
for(int i = 0; i<t.size(); i++) {
|
|
int w2 = g.getFontMetrics().stringWidth(t.get(i));
|
|
if(w2 > w) w = w2;
|
|
}
|
|
|
|
// Rechteck mit Text ausgeben
|
|
p.rect(x, y, w+16, h+10);
|
|
p.fill("303030");
|
|
for(int i = 0; i<t.size(); i++) {
|
|
p.text(t.get(i), x+8, y+(i+1)*lh+3);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
this.setImage(p, false);
|
|
Picture zugeschnitten = new Picture(maxx-minx+2*options.vertexSize,maxy-miny+2*options.vertexSize);
|
|
zugeschnitten.getImage().getGraphics().drawImage(p.getImage(), 0, 0, maxx-minx+2*options.vertexSize, maxy-miny+2*options.vertexSize, minx-options.vertexSize, miny-options.vertexSize, maxx+options.vertexSize, maxy+options.vertexSize, null);
|
|
return zugeschnitten;
|
|
}
|
|
|
|
public GraphOptions getGraphOptions() {
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Gibt den Graphen zurueck.
|
|
*
|
|
* @return Graph
|
|
*/
|
|
public Graph getGraph() {
|
|
return graph;
|
|
}
|
|
|
|
/**
|
|
* Gibt das selektierte Knotenobjekt zurueck.
|
|
*
|
|
* @return Object
|
|
*/
|
|
public Knoten getSelectedKnoten() {
|
|
if(selected.size()==1 && selected.get(0) instanceof Knoten)
|
|
return ((Knoten) (selected.get(0)));
|
|
else
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gibt die selektierte KnotenobjektListe (als Array) zurueck.
|
|
*
|
|
* @return Object[]
|
|
*/
|
|
public List<Knoten> getSelectedKnotenListe() {
|
|
List<Knoten> l = new ArrayList<Knoten>();
|
|
for(GraphElement g : selected) {
|
|
if(g instanceof Knoten) l.add((Knoten) g);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
/**
|
|
* Gibt das selektierte Kantenobjekt zurueck.
|
|
*
|
|
* @return Object
|
|
*/
|
|
public Kante getSelectedKante() {
|
|
if(selected.size()==1 && selected.get(0) instanceof Kante)
|
|
return ((Kante) (selected.get(0)));
|
|
else
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Ueberschreibt die Methode toString. Eine String-Repraesentation des GraphPlotters wird ausgegeben.
|
|
*
|
|
* @return String Die String-Repraesentation des GraphPlotters
|
|
*/
|
|
public String toString() {
|
|
String s = "";
|
|
if(graph.isGerichtet())
|
|
s += "Gerichteter, ";
|
|
else
|
|
s += "Ungerichteter, ";
|
|
if(graph.isGewichtet())
|
|
s += "gewichteter Graph mit Hintergrundbild: ";
|
|
else
|
|
s += "ungewichteter Graph mit Hintergrundbild: ";
|
|
if(options.bildDatei.equals(""))
|
|
s += " kein Bild!";
|
|
else
|
|
s += options.bildDatei + "!";
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Gibt die String-Repraesentation des GraphPlotters auf der Konsole aus.
|
|
*/
|
|
public void ausgabe() {
|
|
System.out.println(toString() + "\n"+graph.toString());
|
|
}
|
|
// Ende Methoden
|
|
}
|