# grafik in bereiche einteilen (dartboard)



## teddydeluxe (23. Nov 2012)

hallo,

ich möchte eine grafik (dartboard) in einzelne abschnitte unterteilen, auf die man klicken kann. wenn auf den jeweiligen abschnitt mit den punkten geklickt wird, soll diese punktzahl abgezogen werden.
also ein kleines programm, dass das punkte runterzählen erleichtert bei einem nicht-elektronischen dartboard.
ich weiss nur nicht wie ich es mit der grafik angehen soll, dass bestimmte bereiche zu verschiedenen listenerern gehören oder ob das überhaupt möglich ist. ich wollte es mit java swing machen.

wie könnte ich das realisieren ?

vielen dank


----------



## SlaterB (23. Nov 2012)

es gibt grundsätzlich zwei Varianten:
a)
den Bildschirm mit verschiedenen GUI-Komponenten, etwa bemalten JPanel/JLabel nebeneinander befüllen
( a2) in diesem Fall vielleicht auch eine JTable mit von Haus aus Gitter-Struktur a la Excel)
darauf dann ein bis viele Listener, den Komponenten zugeordet bzw. die Zelle einer JTable feststellbar

b)
nur eine gesamte Zeichenfläche, nur einen Listener darauf, von der genauen Pixelpositon eines Mausklicks exakt zurückrechnen,
welcher Bereich gemeint war, das Zeichnen muss dazu natürlich auch pixelgenau passen


----------



## Marco13 (23. Nov 2012)

Wenn die einzelnen Felder des Dartboards als Shapes gespeichert sind, kann man sie erstens schön Zeichnen (auch beliebig skaliert) und zweitens mit shape.contains(mousePos) testen, ob es getroffen wurde.


----------



## teddydeluxe (23. Nov 2012)

vieln dank euch beiden. ich hab mir gedanken darüber gemacht einfach ein bild zu nehmen und die klickposition zurück zu rechnen und damit zu bestimmen auf welches feld geklickt wurde, aber das scheint mir bei diesen "pizzastück" artigen formen recht viel aufwand, wenn man es genau haben will.

ich werde erstmal schauen, dass ich die pizzastücke mit shapes forme und dann weiter schauen.

dankeschön erstmal


----------



## SlaterB (23. Nov 2012)

ach so, Pizza Dartboard, dann fällt die JTable ja aus,

hilfreich ist hier noch, zum Mittelpunkt den Radius und Winkel als Kreis betrachtet zu errechnen,
so kommst du noch mit überschaubaren Aufwand zum Ziel, 
Liste aller Felder 20, 1, 18, 4 usw.
automatisch aufgeteilt Winkel 18 Grad, 36 Grad, 54 Grad usw., je nach Radius 2x, 1x, 3x usw.
mit paar Schleifen kommt man dabei recht weit

auf diese Weise kannst du natürlich auch Punkte für Shapes ausrechnen, das ist dann Jacke wie Hose, 
Kreisbahn evtl. schwierig bei Shapes, aber muss ja nicht sein nur fürs Klicken, dann auch nicht zwingend besonders kleine Trefffelder,
mit den Radien spielen,

wenn erstmal das Modell/ die Berechnungen stehen, dann wird bei Anpassungen automatisch richtig neu gemalt und gleichzeitig richtig zurückgerechnet, schöne Sache,
besser als nur einmalig Shapes mit bestimmten manuell gesetzen Pixelpositionen

das Thema gabs auch schon ein paar Mal im Forum, richtige Lösung ist aber glaube ich nicht dabei,
'dart board java' in Suchmaschinen liefert auch Ergebnisse, aber zu durchsuchen


----------



## Marco13 (23. Nov 2012)

Um das getroffene Feld wirklich _auszurechnen_ braucht's auch nicht so viel. Man kann aus der Positon der Maus und dem Mittelpunkt des Malbereichs alles ausrechnen, was man braucht: den Winkel und den Abstand zum Mittelpunkt. Aus dem Winkel bekommt man raus, welches der 20 Pizzastücke getroffen wurde. Aus dem Abstand bekommt man den "Ring". Allerdings muss diese Information (speziell die Radien der Ringe) dann irgendwo gespeichert werden, und ggf. ein
int segmentIndexToNumber[] = { 20, 1, 18 ... }

Aber für die Shapes kannst du mal in http://www.java-forum.org/awt-swing-swt/108611-roulettescheibe-zeichnen.html schauen. Das ist im Prinzip das gleiche


----------



## teddydeluxe (14. Dez 2012)

ok, ich bin mal wieder dazu gekommen, daran weiter zu arbeiten. das zeichnen der 20 kreisabschnitte hab ich jetzt ohne shapes gelöst. 


```
boolean color = true;
        for (int i = 0; i < 20; i++) {

            if (color) {
                g.setColor(Color.red);
                color = false;
            } 
            else {
                g.setColor(Color.blue);
                color = true;
            }
            int angle = i * 18;
            g.fillArc(0, 0, 600, 600, angle, 18);
        }
    }
```

aber ich hab schwierigkeiten mit der mathematischen funktion, um die koordinaten jedes einzelnen feldes zu begrenzen, um das klicken darauf abzufangen.
ich habe den mittelpunkt, den radius des gesamten kreises und den winkel des feldes. wie kann ich daraus die grenzen eines feldes berechnen, um sie mit der mausposition zu vergelchen ?


----------



## Marco13 (14. Dez 2012)

```
Point mousePos = ...
Point center = ...
int dx = mousePos.x-center.x;
int dy = mousePos.y-center.y;
double angle = Math.atan2(dy, dx);
double distance = mousePos.distance(center);
```
sollte reichen.


----------



## SlaterB (14. Dez 2012)

wie schon gesagt wurde, den Winkel musst du ausrechnen, aus der x/y-Abweichung ergibt sich mit sin/cos und rechtwinkligen Dreieck ein Winkel,
evt. für die 4 Sektoren unterscheiden
Sinus und Kosinus ? Wikipedia

der Abstand zum Mittelpunkt ist hoffentlich nicht zu erklären,

je nachdem ob dann der Abstand > ist als Grenze x und je nach einer der 20 Kategorien die sich aus dem Winkel ergeben weißt du welches Feld es ist,

genauer möchte ich persönlich es vorerst nicht erklären, wäre ja Musterlösung


fang doch mal mit einem MouseListener und Berechnung des Abstands zum Mittelpunkt an,
schon 3 schnelle Klicks und Ausgaben
"Abstand 4.7"
"Abstand 3.3"
"Abstand 0.1"
wären ein Stück des Weges

 (edit: ok, Berechnungen sind etwas weiter nun, wobei auch noch auf 4 Sektoren zu achten,
aber auch erst Hälfte des Wegs zum Feld)


----------



## teddydeluxe (14. Dez 2012)

danke euch beiden, den listener habe ich schon, ich muss noch daraus auf das feld schliessen können. ich bastel erstmal weiter mit den informationen  ich melde mich bestimmt nochmal wieder


----------



## teddydeluxe (16. Dez 2012)

so nochmal vielen dank, das hat hat super geklappt. ich bräuchte bitte noch ein wenig hilfe beim zeichnen der zahlen auf der scheibe. die zahlen sind noch nicht richtig angeordnet (siehe bild) und wahrscheinlich liegt es an meiner formel oder an der abweichung beim casten in int.


```
final int[] score = new int[]{6,13,4,18,1,20,5,12,9,14,11,8,16,7,19,3,17,2,15,10};  
 int middleX = 350;
        int middleY = 350;
        double pi = 3.141592654;
        int radius = 210;
        double angle = (5 * pi / 180);
        double radiant = 18 * pi / 180;
        
        
        for (int i = 0; i < score.length; i++) {
            String s = String.valueOf(score[i]);

            g.drawString(s,
                    (int) (middleX + (Math.cos(angle) * radius)),
                    (int) (middleY - (Math.sin(angle) * radius))); 
            angle += radiant;
        }
```


----------



## SlaterB (16. Dez 2012)

ungewöhnliche Abweichungen, aber mag so sein,
fange besser mit dem Malen eines Punktes an, z.B. genau in der Mitte jeden Bereiches mit gebührenden Radius,

danach male die Zahl genau an diesem Punkt, schau dir an wie sie sich dazu verschiebt,
baue gegebenenfalls kleine Korrekturen ein, standardmäßig etwas links + höher, 
vielleicht bei extremen Positionen noch stärker abweichend

wenn du noch den Code für das restliche Malen postest können andere eher auch testen


----------



## teddydeluxe (16. Dez 2012)

danke, das versuche ich später. hier ist der code für das zeichnen :


```
import java.applet.Applet;
import java.awt.*;
import javax.swing.JFrame;


public class DartBoard extends Applet {

    Point center = new Point(350, 350);
    Point mousePos;
    final int[] score = new int[]{6, 13, 4, 18, 1, 20, 5, 12, 9, 14, 11, 8, 16, 7, 19, 3, 17, 2, 15, 10};


    @Override
    public void paint(Graphics gr) {

        Graphics2D g = (Graphics2D) gr;
        g.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        Color beige = new Color(255, 211, 155);
        Color forestGreen = new Color(34, 139, 34);
        boolean color = true;

        // triple
        for (int i = 0; i < 20; i++) {
            int w = (i * 18) + 9;
            if (color) {
                g.setColor(forestGreen);
                color = false;
            } else {
                g.setColor(Color.red);
                color = true;
            }
            g.fillArc(50, 50, 600, 600, w, 18);
        }

        // double
        for (int i = 0; i < 20; i++) {
            int w = (i * 18) + 9;
            if (color) {
                g.setColor(beige);
                color = false;
            } else {
                g.setColor(Color.black);
                color = true;
            }
            g.fillArc(75, 75, 550, 550, w, 18);
        }

        // single
        for (int i = 0; i < 20; i++) {
            int w = (i * 18) + 9;
            g.setColor(Color.black);

            if (color) {
                g.setColor(Color.black);
                color = false;
            } else {
                g.setColor(beige);
                color = true;
            }
            g.fillArc(100, 100, 500, 500, w, 18);
        }

        // bull
        g.setColor(forestGreen);
        g.fillOval(319, 319, 60, 60);

        // bullseye
        g.setColor(Color.red);
        g.fillOval(337, 337, 25, 25);

        // zahlen
        int fontSize = (int) (22);
        Font font = new Font("Serif", Font.BOLD, fontSize);
        g.setFont(font);
        g.setColor(Color.WHITE);


        int middleX = 350;
        int middleY = 350;
        double pi = 3.141592654;
        int radius = 210;
        double angle = (5 * pi / 180);
        double radiant = 18 * pi / 180;

        for (int i = 0; i < score.length; i++) {
            String s = String.valueOf(score[i]);

            g.drawString(s,
                    (int) (middleX + (Math.cos(angle) * radius)),
                    (int) (middleY - (Math.sin(angle) * radius)));
            angle += radiant;
        }
    }

    public static void main(String[] args) {

        JFrame f = new JFrame("DartBoard");
        f.setSize(768, 768);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new DartBoard(), BorderLayout.CENTER);
        f.setVisible(true);
    }
}
```


----------



## Marco13 (16. Dez 2012)

Beachte dass der "Nullpunkt" eines Strings unten links liegt. Vermutlich wäre die sauberste Lösung, auch die Größe des Strings (20 vs. 1) mit einzubeziehen. Im verlinkten Roulettescheiben-Thread werden auch die Zahlen gemalt (und sogar richtig gedreht  )


----------



## teddydeluxe (16. Dez 2012)

das war das problem, die unterschiedlichen grössen der strings und, dass es nicht der mittelpunkt der zahl war. mit etwas schieben siehts gut aus. dankeschön


```
int middleX = 350;
        int middleY = 340;
        double pi = 3.141592654;
        int radius = 210;
        //double angle = (2 * pi / 180);
        double angle = 0.00;
        double radiant = 18 * pi / 180;
        
        FontMetrics fontMetrics = g.getFontMetrics();
        int fontHeight = fontMetrics.getHeight();
        
               
        for (int i = 0; i < score.length; i++) {
            String s = String.valueOf(score[i]);
            
            int delta = fontMetrics.stringWidth(s)/2;

            g.drawString(s,
                    (int) (middleX + (Math.cos(angle) * radius) - delta),
                    (int) (middleY - (Math.sin(angle) * radius)) + fontHeight - fontMetrics.getDescent()); 
            angle += radiant;
        }
```


----------

