# Hilfe, mehrdimensionale Arrays



## districon (2. Mai 2021)

Hey Leute, ich probiere nun schon seit Tagen rum und finde meinen Fehler einfach nicht. Ich wäre echt erfreut wenn mir hier jmd weiterhelfen könnte.
Zuerst einmal zum allgemeinen Verständnis der Aufgabe :

In dieser Aufgabe sollen Sie eine „Methodenbibliothek“ für ASCII-Geometrie entwickeln. Erstellen Sie dazu die Klasse Zeichengeometrie und ergänzen Sie GENAU die folgenden öffentlichen statischen Methoden nach den Anweisungen in den jeweiligen Teilaufgaben .Beachten Sie dabei folgenden wichtigen Zusammenhang: Die von Methode zu Methode weiter gereichte Zeichenfläche ist ein char[][]-Feld, dessen „Ecke[0][0]“ gedanklich „oben/links“ ist,während alle Methoden das aus der Geometrie typische Koordinatensystem verwenden, bei dem der„Ursprung(0,0)“ tatsächlich „unten/links“ ist, wie es Abbildung 1 verdeutlicht. Beachten Sie auch, dass die zu zeichnenden Objekte über die Zeichenfläche hinausragen dürfen allerdings müssen nur die innerhalb der Zeichenfläche liegenden Teile davon auch tatsächlich gezeichnet werden. 

In der ersten Aufgabe soll man eine Zeichenfläche erstellen. Das hat bei mir funktioniert. Aufgabe 2 besteht daraus, einen einzelnen Punkt zu markieren. Nun zur Aufgabe 3 :

linie(char[][] zf, int a, int b, int c, int d, char z) soll analog zu punkt eine ganze Linie zwischen (a, b) und (c, d) zeichnen, wobei die Endpunkte der Linie immer gezeichnet werden müssen (auch wenn sie gleich sind und die Linie damit zu einem Punkt degeneriert). Bedenken Sie, dass die Linie horizontal, vertikal (!) oder beliebig diagonal verlaufen kann sowie dass sie über den Zeichenrand hinausgehen darf. Es ist auch nicht definiert, wie Startpunkt(a, b)und Endpunkt(c, d) zueinander liegen! Zeichnen Sie so genau wie möglich(Multiplikationen vor Divisionen!). Hinweis: Verwenden Sie wann immer möglich die Geradengleichung: g(x) =b + (x−a)·d−b/c−a.

Nun mein Code dazu :


```
public static char[][] linie(char[][] zf, int a, int b, int c, int d, char z) {
        int yW = 0;
        if (c > a) {
            for (int x = a; x <= c; x++) {
                yW = b + (((x - a) * (d - b)) / (c - a));
                if (zf.length - 1 - yW >= 0 && x < zf[zf.length - 1 - yW].length) {
                    zf[zf.length - 1 - yW][x] = z;
                }
            }
        }
        if (c < a) {
            for (int x = c; x <= a; x++) {
                yW = b + (((x - a) * (d - b)) / (c - a));
                if (zf.length - 1 - yW >= 0 && x < zf[zf.length - 1 - yW].length) {
                    zf[zf.length - 1 - yW][x] = z;
                }
            }
        }
        if (c == a) {
            if (b > d) {
                for (int yW1 = d; yW1 <= b; yW1++) {
                    if (zf.length - 1 - yW1 >= 0 && a < zf[zf.length - 1 - yW1].length) {
                        zf[zf.length - 1 - yW1][a] = z;
                    }
                }
            }
            if (b < d) {
                for (int yW1 = b; yW1 <= d; yW1++) {
                    if (zf.length - 1 - yW1 >= 0 && a < zf[zf.length - 1 - yW1].length) {
                        zf[zf.length - 1 - yW1][a] = z;
                    }
                }
            }
            if (b == d) {
                if (a < c) {
                    for (int x = a; x <= c; x++) {
                        if (zf.length - 1 - b >= 0 && x < zf[zf.length - 1 - b].length) {

                            zf[zf.length - 1 - b][x] = z;
                        }
                    }
                }
                if (a > c) {
                    for (int x = c; x <= a; x++) {
                        if (zf.length - 1 - b >= 0 && x < zf[zf.length - 1 - b].length) {
                            zf[zf.length - 1 - b][x] = z;
                        }
                    }
                }
            }
        }
        if (a == c && b == d) {
            if (zf.length - 1 - b >= 0 && a < zf[zf.length - 1 - yW].length) {
                zf[zf.length - 1 - b][a] = z;
            }
        }
        return zf;
}
```

Ich darf außer Array.length keine anderen Methoden oder Klassen der Java API verwenden!!!
Ich komme einfach nicht weiter. Wo liegt mein Fehler, kann mir das jmd sagen?
Danke


----------



## Oneixee5 (2. Mai 2021)

Wenn dies deine Geradengleichung ist: g(x) =b + (x−a)·d−b/c−a dann ist deine Klammersetzung falsch: `yW = b + (((x - a) * (d - b)) / (c - a));`. Entferne die Klammern, so dass sie der Gleichung entsprechen.


----------



## districon (2. Mai 2021)

Oneixee5 hat gesagt.:


> Wenn dies deine Geradengleichung ist: g(x) =b + (x−a)·d−b/c−a dann ist deine Klammersetzung falsch: `yW = b + (((x - a) * (d - b)) / (c - a));`. Entferne die Klammern, so dass sie der Gleichung entsprechen.


In meinem Code hab ich das ja auch getan


----------



## mihe7 (2. Mai 2021)

Der Code ist viel zu kompliziert. Es gibt nur zwei Fälle, die zu unterscheiden sind.

Fall 1: die Linie verläuft vertikal

Sei minY der kleinere der beiden gegeben y-Werte, maxY der größere. Außerderm sei minY >= 0 und maxY < zf.length. Der Punkt (x, minY) wird gesetzt. Für jedes y aus dem Intervall ]minY; maxY] wird Punkt (x, y) gesetzt.

Fall 2: die Linie verläuft nicht vertikal

Hier kann die Geradengleichung benutzt werden. Sei minX der kleinere der beiden gegebenen x-Werte, maxX der größere. Außerdem sei minX >= 0 und maxX < zf[0].length. Für jedes x aus dem Intervall [minX;maxX] wird der y-Wert ermittelt und der Punkt (x,y) gesetzt.


----------



## districon (2. Mai 2021)

mihe7 hat gesagt.:


> Der Code ist viel zu kompliziert. Es gibt nur zwei Fälle, die zu unterscheiden sind.
> 
> Fall 1: die Linie verläuft vertikal
> 
> ...


Ich weiß einfach nicht wie ich das jetzt genau umschreiben soll


----------



## mihe7 (2. Mai 2021)

Wo hängt es denn?


----------



## districon (2. Mai 2021)

mihe7 hat gesagt.:


> Wo hängt es denn?


Ich bin halt jetzt schon in meinem Code drin...Weißt du wo der Fehler bei meinem Code liegt?


----------



## mihe7 (2. Mai 2021)

districon hat gesagt.:


> Ich bin halt jetzt schon in meinem Code drin...Weißt du wo der Fehler bei meinem Code liegt?


Das ist kein Argument. Wenn Code für die Tonne ist, macht man ihn neu. Der Fehler ist für mich also erstmal, dass er zu kompliziert ist.

Nehmen wir mal Deine Zeilen 3 bis 10:

```
if (c > a) {
            for (int x = a; x <= c; x++) {
                yW = b + (((x - a) * (d - b)) / (c - a));
                if (zf.length - 1 - yW >= 0 && x < zf[zf.length - 1 - yW].length) {
                    zf[zf.length - 1 - yW][x] = z;
                }
            }
        }
```

Diese Zeilen behandeln den Fall, wenn der zweite gegebene Punkt weiter rechts als der erste liegt. Worin unterscheidet sich dieser Code denn nun vom umgekehrten Fall, also wenn der zweite Punkt weiter links als der erste liegt? Der einzige Unterschied ist in Zeile 12. Dort hast Du a und c vertauscht.

Es geht also nur um die Laufvariable der Schleife. Dafür braucht man nicht den ganzen Code zu wiederholen, man kann entsprechende Variablen einführen. Man will vom kleineren x-Wert zum größeren x-Wert iterieren. Man braucht also nur den kleineren und größeren x-Wert zu ermitteln. Unter der Maßgabe, dass keine Bibliotheksfunktionen verwendet werden dürfen, erhält man unabhängig von der relativen Lage der beiden Punkte:

```
if (c != a) {
            int minX = a < c ? a : c;
            int maxX = a < c ? c : a;
            for (int x = minX; x <= maxX; x++) {
                yW = b + (((x - a) * (d - b)) / (c - a));
                if (zf.length - 1 - yW >= 0 && x < zf[zf.length - 1 - yW].length) {
                    zf[zf.length - 1 - yW][x] = z;
                }
            }
        }
```
Das yW in Zeile 5 kann man lokal deklarieren, da der Wert außerhalb des Blocks nicht benötigt wird. Die Klammerung ist in der Form auch nicht notwendig. Außerdem heißt es:


districon hat gesagt.:


> Aufgabe 2 besteht daraus, einen einzelnen Punkt zu markieren


Gibt es dafür eine Methode? Ich nehme mal etwas wie setzePunkt an. Die kann und sollte man dann statt der Zeilen 6-8 verwenden. Daraus ergibt sich:

```
if (c != a) {
            int minX = a < c ? a : c;
            int maxX = a < c ? c : a;
            for (int x = minX; x <= maxX; x++) {
                int y = b + (x - a) * (d - b) / (c - a);
                setzePunkt(x, y, z);
            }
        }
```
Das sieht doch gleich ganz anders aus. Jetzt bleibt nur noch der Fall c == a übrig. Da machst Du wieder zig Fallunterscheidungen - nur der Laufvariablen wegen. Hier kommt also wieder das gleiche Prinzip wie oben zur Anwendung.

```
int minY = b < d ? b : d;
            int maxY = b < d ? d : b;
            for (int y = minY; y <= maxY; y++) {
                setzePunkt(a, y, z);
            }
```
Den Fall, dass b==d gilt, muss man nicht extra behandeln. Dann ist minY == maxY und damit die Schleifenbedingung genau einmal erfüllt.

Drehen wir den Spaß noch um, erhält man insgesamt:

```
if (c == a) {
            int minY = b < d ? b : d;
            int maxY = b < d ? d : b;
            for (int y = minY; y <= maxY; y++) {
                setzePunkt(a, y, z);
            }
        } else {
            int minX = a < c ? a : c;
            int maxX = a < c ? c : a;
            for (int x = minX; x <= maxX; x++) {
                int y = b + (x - a) * (d - b) / (c - a);
                setzePunkt(x, y, z);
            }
        }
```

Die Aufgabe von setzePunkt ist
a) die Koordinate zu transformieren (y up -> y down)
b) das Zeichen z nur dann zu setzen, wenn die transformierte Koordinate auch Teil der Zeichenfläche ist.

Man könnte jetzt noch ein wenig optimieren, indem man minX/maxX bzw. minY/maxY von vornherein auf die Zeichenfläche beschränkt. Das kannst Du selber machen, wenn es Dir Spaß macht.


----------



## fhoffmann (2. Mai 2021)

(ich hatte schon angefangen, diese Atwort zu schreiben bevor @mihe7 ihre Antwort geschrieben hat. Dennoch will ich sie vollenden.)


districon hat gesagt.:


> Weißt du wo der Fehler bei meinem Code liegt?


Wie sollen wir deinen Fehler finden? Deine Variablen "a", "b", "c" und "d" sind nichtssagend. Du magst heute noch wissen, was du damit meinst. Wir wissen nicht, was du mit diesen Variablennamen meinst und du selbst wirst es auch in zehn Jahren selber nicht mehr wissen.


----------



## mihe7 (2. Mai 2021)

fhoffmann hat gesagt.:


> bevor @mihe7 ihre Antwort


s. https://de.wikipedia.org/wiki/Liste_der_bairischen_Vornamen


----------



## fhoffmann (2. Mai 2021)

sorry


----------



## mihe7 (2. Mai 2021)

Wofür? Ist doch witzig. Normalerweise passiert das ja nur @MoxxiManagarm. Hätte auch was asiatisches sein können -> mi he, hört sich schon irgendwie nach Nudelsuppe an.


----------



## fhoffmann (2. Mai 2021)

Also bist du ein Mann oder eine Nudelsuppe?


----------



## mihe7 (2. Mai 2021)

Wo ist der Unterschied?

EDIT: ich bestehe zum Großteil aus Wasser und Nudel hab ich auch.


----------



## districon (2. Mai 2021)

mihe7 hat gesagt.:


> Wo ist der Unterschied?
> 
> EDIT: ich bestehe zum Großteil aus Wasser und Nudel hab ich auch.





mihe7 hat gesagt.:


> Das ist kein Argument. Wenn Code für die Tonne ist, macht man ihn neu. Der Fehler ist für mich also erstmal, dass er zu kompliziert ist.
> 
> Nehmen wir mal Deine Zeilen 3 bis 10:
> 
> ...


Erstmal danke für die ausführliche Antwort. Allerdings gibt es dann ein Problem bei der nächsten Aufgabe die lautet:

polygon(char[][] zf, int[] koordinaten, char z)erhält im Feld koordinaten den Linienzug x0, y0, x1, y1, x2, y2, . . ., d.h. je zwei aufeinander folgende Einträge in diesem Feld stellen einen Punkt(xi, yi) dar, der durch eine Linie mit(xi−1, yi−1)verbunden werden soll.Sie dürfen hier ohne weitere Prüfung annehmen, dass koordinaten != null ist und die Anzahl der Einträge geradzahlig ist.

Mein Code dazu:

```
public static char[][] polygon(char[][] zf, int[] koordinaten, char z) {
        //
        if (koordinaten.length != 0 && koordinaten.length != 1 && koordinaten.length != 2 && koordinaten.length != 3) {
            for (int i = 2; i < koordinaten.length - 1; i = i + 2) {
                linie(zf, koordinaten[i - 2], koordinaten[i - 1], koordinaten[i], koordinaten[i + 1], z);
            }
        }
        return zf;
    }
```

Da kommt jedoch dann eine ArrayIndexoutofBounds Exception. Ich dachte eigentlich die Methode wäre korrekt


----------



## mihe7 (2. Mai 2021)

Überleg mal, was Deine Schleife macht und vergleiche es mit der Aufgabenstellung. Wenn Du durch Überlegen nicht draufkommst, schreib Dir auf, was Deine Schleife macht.


----------



## districon (2. Mai 2021)

mihe7 hat gesagt.:


> Überleg mal, was Deine Schleife macht und vergleiche es mit der Aufgabenstellung. Wenn Du durch Überlegen nicht draufkommst, schreib Dir auf, was Deine Schleife macht.


ok ich hab ein paar Durchläufe gemacht, finde den Fehler aber trotzdem nicht


----------



## mihe7 (3. Mai 2021)

Wie sehen denn Deine Überlegungen aus?


----------



## Blender3D (3. Mai 2021)

districon hat gesagt.:


> eine ganze Linie zwischen (a, b) und (c, d) zeichnen


[CODE lang="java" title="CharCanvas" highlight="16-36"]public class CharCanvas {

    private char[][] data;

    public CharCanvas(int width, int height) {
        data = new char[height][width];
    }

    public void drawPoint(int x, int y, char c) {
        if (!isInRange(x, y))
            return;
        data[getHeight() - y - 1][x] = c;
    }

    public void drawLine(int x1, int y1, int x2, int y2, char c) {
        if (x1 == x2)
            drawVertical(x1, Math.min(y1, y2), Math.abs(y2 - y1), c);
        else if (y1 == y2)
            drawHorizontal(Math.min(x1, x2), y1, Math.abs(x2 - x1), c);
        else {
            if (x1 > x2) {
                int tmp = x1;
                x1 = x2;
                x2 = tmp;
                tmp = y1;
                y1 = y2;
                y2 = tmp;
            }
            // y = k*x+d
            double k = (double) (y2 - y1) / (double) (x2 - x1);
            double step = 1 / k;
            for (double x = x1; x <= x2; x += step) {
                double y = (x - x1) * k + y1;
                drawPoint((int) Math.round(x), (int) Math.round(y), c);
            }
        }
    }

    private void drawHorizontal(int x, int y, int lenght, char c) {
        for (int i = 0; i < lenght; i++)
            drawPoint(x + i, y, c);
    }

    private void drawVertical(int x, int y, int lenght, char c) {
        for (int i = 0; i < lenght; i++)
            drawPoint(x, y + i, c);
    }

    public void fill(char c) {
        for (int y = 0; y < getHeight(); y++) {
            for (int x = 0; x < getWidth(); x++)
                data[y][x] = c;
        }

    }

    public int getHeight() {
        return data.length;
    }

    public int getWidth() {
        return data[0].length;
    }

    public boolean isInRange(int x, int y) {
        if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight())
            return false;
        return true;
    }

    @Override
    public String toString() {
        StringBuffer tmp = new StringBuffer();
        for (int y = 0; y < getHeight(); y++) {
            for (int x = 0; x < getWidth(); x++)
                tmp.append(data[y][x]);
            tmp.append(((getHeight() - y - 1) % 10) + System.lineSeparator());
        }
        for (int x = 0; x < getWidth(); x++)
            tmp.append((x % 10));
        return tmp.toString();
    }

}[/CODE]
[CODE lang="java" title="TestCharGeom"]public class TestCharGeom {
    public static void main(String[] args) {
        CharCanvas canvas = new CharCanvas(20, 15);
        canvas.fill('.');
        canvas.drawLine(1, 1, 7, 13, '*');
        System.out.println(canvas);
    }
}[/CODE]


----------

