P
Plotter
Gast
Hallo,
ich habe im Internet (irgenwo in den Sun-Foren) einen 3D-Funktionsplotter gefunden (source unten), der 3D Funktionen in 2D plottet.
Meine Frage ist keine technische sondern eine logische: wie macht der das? Also wie plottet man in 2D eine 3D Funktion ohne eine 3D Engine?
Ich hoffe, dass Ihr mir helfen könnt - das ist sehr wichtig für mich!
Gruß,
Gast
=========
EDIT: Hier gefunden: Index of /~simonbr/applets/fplot
FPlot3D:
Ansicht3D.java
ich habe im Internet (irgenwo in den Sun-Foren) einen 3D-Funktionsplotter gefunden (source unten), der 3D Funktionen in 2D plottet.
Meine Frage ist keine technische sondern eine logische: wie macht der das? Also wie plottet man in 2D eine 3D Funktion ohne eine 3D Engine?
Ich hoffe, dass Ihr mir helfen könnt - das ist sehr wichtig für mich!
Gruß,
Gast
=========
EDIT: Hier gefunden: Index of /~simonbr/applets/fplot
FPlot3D:
Java:
package fplot;
//
// 3D-Darstellung von Funktionen f(x,y)
//
// Simon Bruderer, Mai 2002
//
import java.applet.Applet;
import java.awt.event.*;
import java.awt.*;
//////////////////////////////////////////////////////////////
//
// Class Fplot: Benutzeroberfl?che, Generierung der Daten, Applet
//
//////////////////////////////////////////////////////////////
public class Fplot3D extends Applet implements ItemListener, ActionListener {
String[] ColBez = { "Weiss", "Hellgrau", "Grau", "Dunkelgrau", "Schwarz",
"Rot", "Blau", "Gr?n", "Gelb", "Magenta", "Cyan", "Orange", "Pink" };
Color[] ColCol = { Color.white, Color.lightGray, Color.gray,
Color.darkGray, Color.black, Color.red, Color.blue, Color.green,
Color.yellow, Color.magenta, Color.cyan, Color.orange, Color.pink };
Panel EinstellungsPanel1 = new Panel();
Panel EinstellungsPanel2 = new Panel();
Panel AusdruckPanel = new Panel();
Panel OptionenPanel = new Panel();
Panel ControlPanel = new Panel();
Panel StatusPanel = new Panel();
Ansicht3D MeineAnsicht3D = new Ansicht3D(10, 10);
Choice C_Color = new Choice();
Checkbox Cb_Schatten = new Checkbox("Schatten", true);
Checkbox Cb_Rand = new Checkbox("Rand", false);
Checkbox Cb_Backsurf = new Checkbox("R?ckseite", false);
TextField Tf_Minx = new TextField("-3");
TextField Tf_Maxx = new TextField("3");
TextField Tf_Miny = new TextField("-3");
TextField Tf_Maxy = new TextField("3");
TextField Tf_XStep = new TextField("30");
TextField Tf_YStep = new TextField("30");
TextField Tf_ZFakt = new TextField("0.5");
TextField Tf_Expr = new TextField("2*(x^3+3*y^2+x-y)*exp(-x^2-y^2)");
Label Lb_Status = new Label();
Button Bt_Zeichnen = new Button("Zeichnen");
public void init() {
setBackground(Color.white);
OptionenPanel.add(new Label("Farbe", Label.RIGHT));
OptionenPanel.add(C_Color);
C_Color.addItemListener(this);
OptionenPanel.add(Cb_Schatten);
Cb_Schatten.addItemListener(this);
OptionenPanel.add(Cb_Rand);
Cb_Rand.addItemListener(this);
OptionenPanel.add(Cb_Backsurf);
Cb_Backsurf.addItemListener(this);
OptionenPanel.add(Bt_Zeichnen);
Bt_Zeichnen.addActionListener(this);
OptionenPanel.setLayout(new GridLayout(1, 6));
AusdruckPanel.add(new Label("f(x,y)=", Label.RIGHT));
AusdruckPanel.add(Tf_Expr);
AusdruckPanel.setLayout(new GridLayout(1, 2));
EinstellungsPanel1.add(new Label("Min-x", Label.RIGHT));
EinstellungsPanel1.add(Tf_Minx);
EinstellungsPanel1.add(new Label("Max-x", Label.RIGHT));
EinstellungsPanel1.add(Tf_Maxx);
EinstellungsPanel1.add(new Label("Min-y", Label.RIGHT));
EinstellungsPanel1.add(Tf_Miny);
EinstellungsPanel1.add(new Label("Max-y", Label.RIGHT));
EinstellungsPanel1.add(Tf_Maxy);
EinstellungsPanel1.setLayout(new GridLayout(1, 8));
EinstellungsPanel2.add(new Label("x-Step", Label.RIGHT));
EinstellungsPanel2.add(Tf_XStep);
EinstellungsPanel2.add(new Label("y-Step", Label.RIGHT));
EinstellungsPanel2.add(Tf_YStep);
EinstellungsPanel2.add(new Label("z-Faktor", Label.RIGHT));
EinstellungsPanel2.add(Tf_ZFakt);
EinstellungsPanel2.add(new Label(""));
EinstellungsPanel2.add(new Label(""));
EinstellungsPanel2.setLayout(new GridLayout(1, 8));
StatusPanel.add(Lb_Status);
StatusPanel.setLayout(new GridLayout(1, 1));
ControlPanel.add(AusdruckPanel);
ControlPanel.add(EinstellungsPanel1);
ControlPanel.add(EinstellungsPanel2);
ControlPanel.add(OptionenPanel);
ControlPanel.add(StatusPanel);
ControlPanel.setLayout(new GridLayout(5, 1));
setLayout(new BorderLayout(5, 5));
add(ControlPanel, BorderLayout.SOUTH);
add(MeineAnsicht3D, BorderLayout.CENTER);
for (int i = 0; i < ColBez.length; i++)
C_Color.add(ColBez[i]);
}
public void start() {
initplot();
}
public void itemStateChanged(ItemEvent event) {
MeineAnsicht3D.schatten = Cb_Schatten.getState();
MeineAnsicht3D.rand = Cb_Rand.getState();
MeineAnsicht3D.backsurf = Cb_Backsurf.getState();
MeineAnsicht3D.setCol(ColCol[C_Color.getSelectedIndex()]);
MeineAnsicht3D.zeichnen();
}
public void actionPerformed(ActionEvent event) {
if (event.getActionCommand().equals("Zeichnen")) {
initplot();
}
}
private double trytodouble(String Zahl) {
double dbzahl = 0;
try {
dbzahl = Double.valueOf(Zahl).doubleValue();
} catch (NumberFormatException nfe) {
dbzahl = 0;
}
return dbzahl;
}
public void initplot() {
Lb_Status.setText("");
double xmin = trytodouble(Tf_Minx.getText());
double xmax = trytodouble(Tf_Maxx.getText());
if (xmax <= xmin) {
Lb_Status.setText("Fehler: Max-x<=Min-x");
return;
}
double ymin = trytodouble(Tf_Miny.getText());
double ymax = trytodouble(Tf_Maxy.getText());
if (ymax <= ymin) {
Lb_Status.setText("Fehler: Max-y<=Min-y");
return;
}
int xstep = (int) trytodouble(Tf_XStep.getText()) + 1;
if (xstep < 3 || xstep > 101) {
Lb_Status.setText("Fehler: x-Step muss in [2..100] sein");
return;
}
int ystep = (int) trytodouble(Tf_YStep.getText()) + 1;
if (ystep < 3 || ystep > 101) {
Lb_Status.setText("Fehler: y-Step muss in [2..100] sein");
return;
}
double zfaktor = trytodouble(Tf_ZFakt.getText());
if (zfaktor == 0) {
Lb_Status.setText("Fehler: z-Faktor darf nicht null sein");
return;
}
String Ausdr = Tf_Expr.getText();
MyParser p = new MyParser(Ausdr);
try {
p.newvar("x", xmin);
p.newvar("y", ymin);
double fxy = p.parse();
} catch (SyntaxException err) {
String Aus = err.getMessage();
Lb_Status.setText("Syntax-Fehler: " + Aus);
return;
}
plot3d(xmin, xmax, ymin, ymax, xstep, ystep, zfaktor, Ausdr);
Lb_Status.setText("f(x,y)=" + p.getinput() + " ("
+ xmin + "," + ymin + ")-(" + xmax + "," + ymax + ")");
}
public void plot3d(double xmin, double xmax, double ymin, double ymax,
int xstep, int ystep, double zfaktor, String Ausdr) {
MeineAnsicht3D.reset(xstep * ystep + 2 * (xstep + ystep), xstep * ystep
* 2 + 10);
MeineAnsicht3D.schatten = Cb_Schatten.getState();
MeineAnsicht3D.rand = Cb_Rand.getState();
MeineAnsicht3D.backsurf = Cb_Backsurf.getState();
MeineAnsicht3D.setCol(ColCol[C_Color.getSelectedIndex()]);
double xdiff = (xmax - xmin) / (xstep - 1);
double ydiff = (ymax - ymin) / (ystep - 1);
double[][] werte = new double[xstep][ystep];
MyParser p = new MyParser(Ausdr);
try {
p.newvar("x", 0.0);
p.newvar("y", 0.0);
} catch (SyntaxException err) {
System.out.println(err.getMessage());
}
for (int xi = 0; xi < xstep; xi++) {
double x = xmin + xi * xdiff;
for (int yi = 0; yi < ystep; yi++) {
double y = ymin + yi * ydiff;
werte[xi][yi] = 0;
try {
p.setvar("x", x);
p.setvar("y", y);
werte[xi][yi] = p.parse();
if (Double.isInfinite(werte[xi][yi])
|| Double.isNaN(werte[xi][yi])) {
werte[xi][yi] = 0;
}
} catch (SyntaxException err) {
System.out.println(err.getMessage());
}
}
}
double xdiff2 = 2.0 / (double) (xstep - 1);
double ydiff2 = 2.0 / (double) (ystep - 1);
for (int xi = 0; xi < xstep; xi++) {
double x = -1 + xdiff2 * xi;
for (int yi = 0; yi < ystep; yi++) {
double y = -1 + ydiff2 * yi;
MeineAnsicht3D.addPoint(1.5 * x, 1.5 * y, werte[xi][yi]
* zfaktor);
}
}
for (int xi = 0; xi < xstep - 1; xi++) {
for (int yi = 0; yi < ystep - 1; yi++) {
int xo = xi * ystep + yi;
MeineAnsicht3D.addTriangle(xo + 1, xo, xo + ystep);
MeineAnsicht3D.addTriangle(xo + ystep, xo + ystep + 1, xo + 1);
}
}
MeineAnsicht3D.zeichnen();
}
}
Java:
package fplot;
import java.awt.event.*;
import java.awt.*;
//////////////////////////////////////////////////////////////
//
// Class Ansicht3D: 3D-Darstellung von Dreiecken-mit simpler Schattierung
//
// Simon Bruderer, M?rz 2002, ?berarbeitet im Mai 2002
//
//////////////////////////////////////////////////////////////
class Ansicht3D extends Panel implements MouseListener,MouseMotionListener{
public int phi=0;
public int rho=0;
public double xbetrachter=20;
public double xschirm=1500;
public int maxpoint;
public int maxtriangle;
public double[][] point;
public int[][] triangle;
public int anzpoint=0;
public int anztriangle=0;
public boolean schatten=false;
public boolean rand=false;
public boolean backsurf=false;
private double[][] drehpoint;
private int[][] projpoint;
private double[] xabstand;
private int[] xreihenfolge;
private int xgedrueckt=0;
private int ygedrueckt=0;
private int phi0=0;
private int rho0=0;
private boolean gedrueckt=false;
private Image puffer;
private Graphics pufferg;
private Color Grundfarbe;
private float[] GrundfarbeHSB=new float[3];
public Ansicht3D(int setmaxpoint,int setmaxtriangle){
super();
addMouseListener(this);
addMouseMotionListener(this);
maxpoint=setmaxpoint;
maxtriangle=setmaxtriangle;
point=new double[maxpoint][3];
triangle=new int[maxtriangle][3];
drehpoint=new double[maxpoint][3];
projpoint=new int[maxpoint][2];
xabstand=new double[maxtriangle];
xreihenfolge=new int[maxtriangle];
}
public void reset(int setmaxpoint,int setmaxtriangle){
maxpoint=setmaxpoint;
maxtriangle=setmaxtriangle;
point=new double[maxpoint][3];
triangle=new int[maxtriangle][3];
drehpoint=new double[maxpoint][3];
projpoint=new int[maxpoint][2];
xabstand=new double[maxtriangle];
xreihenfolge=new int[maxtriangle];
anzpoint=0;
anztriangle=0;
phi=0;
rho=0;
xbetrachter=20;
xschirm=1500;
}
public void addPoint(double x, double y, double z){
point[anzpoint][0]=x;
point[anzpoint][1]=y;
point[anzpoint][2]=z;
anzpoint++;
}
public void addTriangle(int p1,int p2,int p3){
triangle[anztriangle][0]=p1;
triangle[anztriangle][1]=p2;
triangle[anztriangle][2]=p3;
anztriangle++;
}
public void setCol(Color c){
Grundfarbe=c;
Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), GrundfarbeHSB);
}
private void quickSort(int l, int r){
// Quicksort-Algorithmus
// Habe ich ?bernommen von R. St?rk, Vorlesung Informatik I f?r
// Mathematiker und Physiker an der ETH-Z?rich
if (l < r){
int m = partition(l, r);
quickSort(m + 1, r);
quickSort(l, m);
}
}
private void swap(int i,int j){
double xa=xabstand[i];
int xr=xreihenfolge[i];
xabstand[i]=xabstand[j];
xreihenfolge[i]=xreihenfolge[j];
xabstand[j]=xa;
xreihenfolge[j]=xr;
}
private int partition(int l, int r) {
double x=xabstand[(l+r)/2];
while (l<=r) {
while (x<xabstand[r]) r--;
while (xabstand[l]<x ) l++;
if (l<r){
swap(l++,r--);
}
else{
return r;
}
}
return r;
}
double[] licht={1.0/Math.sqrt(3),1.0/Math.sqrt(3),1.0/Math.sqrt(3)};
private double winkelschatten(int i){
// 1. Normalenvektor n des Dreiecks berechnen
// 2. n normieren auf L?nge 1
// 3. Skalarprodukt mit normiertem Lichtvektor berechnen
double p0[]=drehpoint[triangle[i][0]];
double p1[]=drehpoint[triangle[i][1]];
double p2[]=drehpoint[triangle[i][2]];
double[] n1={p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]};
double[] n2={p2[0]-p0[0],p2[1]-p0[1],p2[2]-p0[2]};
double[] n={n1[1]*n2[2]-n1[2]*n2[1],n1[2]*n2[0]-n1[0]*n2[2],n1[0]*n2[1]-n1[1]*n2[0]};
double nnorm=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
double sp=(n[0]*licht[0]+n[1]*licht[1]+n[2]*licht[2])/(nnorm);
return sp;
}
private boolean winkelsichtbar(int i){
// Hier wird berechnet, ob der Normalenvektor des Dreiecks nach
// vorne oder nach hinten zeigt.
// Eigentlich die gleiche Funktion wie "winkelschatten", jedoch
// optimiert, da der Sichtvektor immer in Richtung der x-Achse zeigt
double n11=drehpoint[triangle[i][1]][1]-drehpoint[triangle[i][0]][1];
double n12=drehpoint[triangle[i][1]][2]-drehpoint[triangle[i][0]][2];
double n21=drehpoint[triangle[i][2]][1]-drehpoint[triangle[i][0]][1];
double n22=drehpoint[triangle[i][2]][2]-drehpoint[triangle[i][0]][2];
double n=n11*n22-n12*n21;
return n>0?true:false;
}
public void zeichnen(){
int breit=getSize().width;
int hoch=getSize().height;
if(puffer==null){
puffer=createImage(getSize().width,getSize().height);
pufferg=puffer.getGraphics();
}
if(puffer.getWidth(this)!=breit || puffer.getHeight(this)!=hoch){
puffer=createImage(getSize().width,getSize().height);
pufferg=puffer.getGraphics();
}
pufferg.setColor(Color.white);
pufferg.fillRect(0,0,this.getSize().width,this.getSize().height);
// Vorbereitung: Winkel und Trigonometrische Funktionen berechnen
phi=phi%360;
if(rho>90) rho=90;
if(rho<-90) rho=-90;
if(phi<0) phi=360+phi;
double phirad=((phi*Math.PI)/180);
double rhorad=((rho*Math.PI)/180);
double cosrotphi=Math.cos(phirad);
double sinrotphi=Math.sin(phirad);
double cosrotrho=Math.cos(rhorad);
double sinrotrho=Math.sin(rhorad);
int breit2=(int)(breit/2.0);
int hoch2=(int)(hoch/2.0);
double zstrich;
double ystrich;
int anztrianglebacksurf=0;
// Punkte drehen, gleichzeitig Projektion in Bildschirmkoordinaten
for(int i=0;i<anzpoint;i++){
drehpoint[i][0]=cosrotrho*cosrotphi*point[i][0]-cosrotrho*sinrotphi*point[i][1]-sinrotrho*point[i][2];
drehpoint[i][1]=sinrotphi*point[i][0]+cosrotphi*point[i][1];
drehpoint[i][2]=sinrotrho*cosrotphi*point[i][0]-sinrotrho*sinrotphi*point[i][1]+cosrotrho*point[i][2];
zstrich=xschirm*drehpoint[i][2]/(xbetrachter-drehpoint[i][0]);
ystrich=xschirm*drehpoint[i][1]/(xbetrachter-drehpoint[i][0]);
projpoint[i][0]=(int)ystrich+breit2;
projpoint[i][1]=-(int)zstrich+hoch2;
}
// Mittlerer x-Abstand pro Dreieck berechnen
// und Dreiecke ausscheiden, deren Normalenvektor nach hinten zeigt.
if(backsurf){
for(int i=0;i<anztriangle;i++){
if(winkelsichtbar(i)){
double x1=drehpoint[triangle[i][0]][0];
double x2=drehpoint[triangle[i][1]][0];
double x3=drehpoint[triangle[i][2]][0];
double xm=(x1+x2+x3)/3;
xabstand[anztrianglebacksurf]=xm;
xreihenfolge[anztrianglebacksurf]=i;
anztrianglebacksurf++;
}
}
}
else{
for(int i=0;i<anztriangle;i++){
double x1=drehpoint[triangle[i][0]][0];
double x2=drehpoint[triangle[i][1]][0];
double x3=drehpoint[triangle[i][2]][0];
xabstand[i]=(x1+x2+x3)/3;
xreihenfolge[i]=i;
}
anztrianglebacksurf=anztriangle;
}
// Zeichnungsreihenfolge sortieren nach mittlerem x-Abstand
quickSort(0,anztrianglebacksurf-1);
// Zeichnen!
for(int i=0;i<anztrianglebacksurf;i++){
int j=xreihenfolge[i];
int[] arx={projpoint[triangle[j][0]][0],
projpoint[triangle[j][1]][0],
projpoint[triangle[j][2]][0]};
int[] ary={projpoint[triangle[j][0]][1],
projpoint[triangle[j][1]][1],
projpoint[triangle[j][2]][1]};
if(schatten){
double w=winkelschatten(j);
w=w/2.0+0.5;
pufferg.setColor(Color.getHSBColor(GrundfarbeHSB[0],GrundfarbeHSB[1],(float)w));
}
else{
pufferg.setColor(Grundfarbe);
}
pufferg.fillPolygon(arx,ary,3);
if(rand){
pufferg.setColor(Color.black);
pufferg.drawPolygon(arx,ary,3);
}
}
pufferg.setColor(Color.black);
pufferg.drawString("Phi:"+Double.toString(phi),10,20);
pufferg.drawString("Rho:"+Double.toString(rho),10,35);
paint(getGraphics());
//repaint();
}
public void paint(Graphics g){
if(puffer!=null) g.drawImage(puffer,0,0,this);
}
public void update(Graphics g){
paint(g);
}
public void mousePressed(MouseEvent e){
phi0=phi;
rho0=rho;
xgedrueckt=e.getX();
ygedrueckt=e.getY();
gedrueckt=true;
}
public void mouseReleased(MouseEvent e){
phi=phi0-(int)((xgedrueckt-e.getX())/2);
rho=rho0+(int)((ygedrueckt-e.getY())/2);
zeichnen();
gedrueckt=false;
}
public void mouseDragged(MouseEvent e){
if(gedrueckt==true){
phi=phi0-(int)((xgedrueckt-e.getX())/2);
rho=rho0+(int)((ygedrueckt-e.getY())/2);
zeichnen();
}
}
public void mouseMoved(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
}