Rysowanie Krzywej Beziera na podstawie czterech podanych punktów

0

Witam. Mam do napisania program który rysuję z 4 podanych punktów krzywą Beziera. Program ogólnie działa bez zarzutu lecz potrzebuję jeszcze jednej funkcjonalności a mianowicie muszę po naciśnięciu np. lewego guzika myszy skończyć rysować obecną krzywą i rozpocząć rysowanie nowej lecz z możliwością dalszej edycji starych krzywych.Obecnie po narysowaniu jednej krzywej mogę ją dowolnie zmieniać, ale nie wiem już jak zrobić dodawanie dodatkowych krzywych i ich edycji.Niżej zamieszczam kod:

import java.awt.*;
import java.awt.event.*;

public class Bezier extends Frame {
   Point[] punkty;
   int numpoints;
   double t;
   double k=.025;   
   int moveflag = 5;      
    MenuBar pasek;
    Menu plik;
   MenuItem newB, zamknij;
   
public   Bezier() {
      super("Krzywa Beziera");
      pasek=new MenuBar();
      plik=new Menu("Plik");
      newB=new MenuItem("Nowy");
      zamknij=new MenuItem("Zamknij");
      plik.add(newB);
      plik.add(zamknij);
      pasek.add(plik);
      setMenuBar(pasek);
   }
      
   public void init() {
   resize(600,600);
   setLayout(new FlowLayout());
   punkty = new Point[4];
   numpoints = 0;
   }

   public void paint(Graphics g) {
   setBackground(Color.lightGray);
   g.clearRect(0,0,size().width,size().height);
   for(int i=0;i<numpoints;i++) {
   g.setColor(Color.red);
   g.fillOval(punkty[i].x-2, punkty[i].y-2,4,4);
   g.setColor(Color.red);
         }
       Licz(g);
   }
   
   public void Licz(Graphics g) {
   if(numpoints == 4) {
   double x1,x2,y1,y2;
   x1 = punkty[0].x;
   y1 = punkty[0].y;
   for(t=k;t<=1+k;t+=k){
x2=(punkty[0].x+t*(-punkty[0].x*3+t*(3*punkty[0].x-punkty[0].x*t)))+t*(3*punkty[1].x+t*(-6*punkty[1].x+
punkty[1].x*3*t))+t*t*(punkty[2].x*3-punkty[2].x*3*t)+punkty[3].x*t*t*t;
y2=(punkty[0].y+t*(-punkty[0].y*3+t*(3*punkty[0].y-punkty[0].y*t)))+t*(3*punkty[1].y+t*(-6*punkty[1].y+
punkty[1].y*3*t))+t*t*(punkty[2].y*3-punkty[2].y*3*t)+punkty[3].y*t*t*t;
   g.setColor(Color.gray);
   g.drawLine((int)x1,(int)y1,(int)x2,(int)y2);
   x1 = x2;
   y1 = y2;
   }
   }

}

public boolean action(Event e, Object o) {
   if (e.target == newB) {
   numpoints = 0;
   repaint();
   return true;
   }
   else if(e.target == zamknij) {
      System.exit(0);
   }
   return false;

}
public boolean mouseDown(Event evt, int x, int y) {
   Point point = new Point(x,y);

   if(numpoints < 4) {
   punkty[numpoints] = point;
   numpoints++;
   repaint();
   }
   else
   for(int i=0;i<numpoints;i++)
   for(int j=-2;j<3;j++)
   for(int l=-2;l<3;l++)
   if(point.equals(new Point(punkty[i].x+j,punkty[i].y+l)))
   moveflag = i;
   return true;
   }
   public boolean mouseDrag(Event evt, int x, int y) {
   if(moveflag < numpoints) {
   punkty[moveflag].move(x,y);
   repaint();
   }
   return true;
   }
   public boolean mouseUp(Event evt, int x, int y) {
   moveflag = 5;
   return true;
   }   
public static void main(String args[]) {
   Bezier B=new Bezier(); B.init(); B.show();
   }
}
1

Musisz stworzyć kolekcję krzywych Beziera, a w metodzie paint() rysować je w pętli. Napisałem program do rysowania krzywych Beziera (w Swingu). Z boku panelu wyświetlam (korzystając z JTable) listę krzywych, zaznaczona krzywa rysowana jest grubiej. Użytkownik może edytować lub usunąć zaznaczoną krzywą.

Btw, w AWT jest funkcja rysująca krzywe Beziera (podajesz tylko punkty kontrolne), dlaczego wymyślałaś koło na nowo?

0

Zdaję sobie sprawę że w Javie jest funkcja rysująca Beziera ale nasz prowadzący uparł się że musimy sami zaimplementować wzór podany przez niego.Czyli muszę utworzyć JTable i po narysowaniu 4 pkt ma mi tablicę przenieść do JTable i rozpocząć rysowanie od nowa tak?? Ale czy w tym miejscu nie muszę zmieniać funkcji na edycję pkt. a żeby odwoływała się do JTable czy będzie pamiętać jaki punkty są z jakiej tablicy??

0

Po jakiejkolwiek zmianie w JTable jedną funkcją kopiujesz do tablicy np Point[] arr=new Point[N]; zaś druga funkcja rysuje z Point[] arr oczywiście nic nie robi jeżeli tablica zbyt krótka.

0

Chyba nie załapałem :/

0

JTable jest pomocnicze, można ją zastąpić innym komponentem (JList, JComboBox), w którym możliwe jest zaznaczenie wiersza. Podstawą jest kolekcja krzywych Beziera, np.

ArrayList<Point[]> krzywe = ArrayList<Point[]>();

Możesz też zajrzeć do tego wątku krzywe beziera

0

Już rozumiem po osiągnięciu 4 pkt. przekazujesz je do Arraylist gdzie są trzymane ale jak dodać do tego schematu możliwość edycji narysowanych pkt. muszę odwołać się do Arraylist??

0

U mnie wygląda to tak:

    int index = table.getSelectedRow();
    if(index == -1)
    {
        JOptionPane.showMessageDialog(this,
                  "Zaznacz jakąś krzywą w oknie po prawej","",JOptionPane.INFORMATION_MESSAGE);
        return;
    }
    points = curves.get(index).points; //odczytanie punktów kontrolnych wskazanej krzywej
    numpoints = 4; //
    status = EDITION; //pole status zawiera informację co użytkownik robi, wpływa na działanie mousePressed i mouseClicked
    edited = index; //pole edited jest wykorzystywane przez funkcję paint, wskazana krzywa jest rysowana grubszą kreską 
0

No tak ale Ty tutaj korzystasz z JTable myślałem a żeby zmodyfikować kod i dodać rozróżnianie na kliknięcia że jak prawy to rysuję pkt jak lewy to dodaję pkt do Arraylist a jak środkowy to przesunięcie pkt i próbowałem użyć

isMetaDown()

gdzie użycie prawego powinno zwrócić true i postawienie pkt ale nie chce mi działać ;/

0

Udało mi się poprawić kod i osiągnąć połowiczny sukces a mianowicie zaznaczam 4 pkt mogę je poprawić lecz nie rysuję mi krzywej. Dodałem do menu opcję dodaj krzywą po jej uruchomieniu i kliknięciu w dowolnym miejscu pojawia mi się krzywa i mogę dodawać nowe pkt które znowu mogę edytować lecz nie mogę ruszyć tych starych. Da się to jakoś przeskoczyć??

import java.awt.*; 
import java.awt.event.*; 
import java.util.*;
 
public class Bezier extends Frame 
{ 
    Point[] punkty = new Point[4];; 
    int numpoints = 0; 
    double t; 
    double k=.025;    
    int moveflag = 5;       
    MenuBar pasek; 
    Menu plik; 
    MenuItem newB, zamknij;
    ArrayList<Point[]> krzywe = new ArrayList<Point[]>();   
 
    public   Bezier() 
    { 
        super("Krzywa Beziera"); 
        pasek=new MenuBar(); 
        plik=new Menu("Plik"); 
        newB=new MenuItem("Nowa krzywa"); 
        zamknij=new MenuItem("Zamknij"); 
        
        plik.add(newB); 
        
        pasek.add(plik); 
        plik.add(zamknij);
        setMenuBar(pasek); 
    } 
 
    public void init() 
    { 
        resize(600,600); 
        setLayout(new FlowLayout()); 
    } 
 
    public void paint(Graphics g) 
    { 
        setBackground(Color.lightGray); 
        g.clearRect(0,0,size().width,size().height); 
        //System.out.println(krzywe.size());
        for(int k=0;k<krzywe.size();k++)
        {
            Point[] points = krzywe.get(k);
            for(int i=0;i<4;i++) 
            { 
                g.setColor(Color.red); 
                g.fillOval(points[i].x-2, points[i].y-2,4,4); 
                g.setColor(Color.red); 
            } 
            licz(g,points); 
        }
        for(int i=0;i<numpoints;i++) 
        { 
            g.setColor(Color.red); 
            g.fillOval(punkty[i].x-2, punkty[i].y-2,4,4); 
            g.setColor(Color.red); 
        }        
    } 
 
    public void licz(Graphics g,Point[] punkty) 
    { 
        double x1,x2,y1,y2; 
        x1 = punkty[0].x; 
        y1 = punkty[0].y; 
        for(t=k;t<=1+k;t+=k)
        { 
            x2=(punkty[0].x+t*(-punkty[0].x*3+t*(3*punkty[0].x-punkty[0].x*t)))+t*(3*punkty[1].x+t*(-6*punkty[1].x+ 
            punkty[1].x*3*t))+t*t*(punkty[2].x*3-punkty[2].x*3*t)+punkty[3].x*t*t*t; 
            y2=(punkty[0].y+t*(-punkty[0].y*3+t*(3*punkty[0].y-punkty[0].y*t)))+t*(3*punkty[1].y+t*(-6*punkty[1].y+ 
            punkty[1].y*3*t))+t*t*(punkty[2].y*3-punkty[2].y*3*t)+punkty[3].y*t*t*t; 
            g.setColor(Color.gray); 
            g.drawLine((int)x1,(int)y1,(int)x2,(int)y2); 
            x1 = x2; 
            y1 = y2; 
        }  
    } 
 
    public boolean action(Event e, Object o) 
    { 
        if (e.target == newB) 
        { 
            moveflag = 10;
        } 
        else if(e.target == zamknij) 
        { 
            System.exit(0); 
        }    
        return false;  
    } 
    public boolean mouseDown(Event e, int x, int y) 
    { 
        Point point = new Point(x,y); 
 
        if(numpoints < 4) 
        { 
            punkty[numpoints] = point; 
            numpoints++; 
            repaint(); 
        }
        else if(numpoints == 4)
        {
             
            for(int i=0;i<numpoints;i++) 
            for(int j=-2;j<3;j++) 
            for(int l=-2;l<3;l++) 
            if(point.equals(new Point(punkty[i].x+j,punkty[i].y+l))) 
            moveflag = i; 
        }
        if(moveflag == 10)
        {  
            krzywe.add(punkty);      
            punkty = new Point[4];
            numpoints = 0;
            repaint();
        }   
        return true; 
    } 
    public boolean mouseDrag(Event e, int x, int y) 
    { 
        if(moveflag < numpoints) 
        { 
            punkty[moveflag].move(x,y); 
            repaint(); 
        } 
        return true; 
    } 
    public boolean mouseUp(Event e, int x, int y) 
    { 
        moveflag = 5; 
        return true; 
    }   
    public static void main(String args[]) 
    { 
        Bezier B=new Bezier(); 
        B.init(); 
        B.show(); 
    } 
}
1

Po wybraniu z menu nowej krzywej

    public boolean action(Event e, Object o) 
    { 
        if (e.target == newB) 
        { 
            moveflag = 10;
        } 

moveflag ustawiasz na 10.
W mouseDown masz to

        if(moveflag == 10)
        {  
            krzywe.add(punkty);      
            punkty = new Point[4];
            numpoints = 0;
            repaint();
        }

Powyższy kod wykona się już po pierwszym kliknięciu (po dodaniu pierwszego punktu). Wykona się zatem odrysowanie, w metodzie paint masz to

        for(int k=0;k<krzywe.size();k++)
        {
            Point[] points = krzywe.get(k);
            for(int i=0;i<4;i++) 
            { 
                g.setColor(Color.red); 
                g.fillOval(points[i].x-2, points[i].y-2,4,4); 
                g.setColor(Color.red); 
            } 
            licz(g,points); 
        }

Ale w tablicy points są aktualnie cztery nulle.

0

Ok już wiem gdzie robiłem błąd a istnieje możliwość odwołania się do poprzednich pkt. a żeby mieć możliwość edycji wszystkich krzywych na raz ??

0

Jest taka możliwość. Użytkownik klika w jakiś punkt, a Ty przeszukujesz ArrayList<Point[]>. Kliknięty punkt powinien leżeć w odpowiednim kole o promieniu 2 (bo tak punkty kontrolne widzi użytkownik). Ma to wadę, jeżeli punkty kontrolne z dwóch różnych krzywych prawie się pokrywają, to możesz wybrać krzywą niezgodną z intencją użytkownika. (Dlatego ja wybrałem takie rozwiązanie, że użytkownik wpierw wskazuje krzywą.)

1 użytkowników online, w tym zalogowanych: 0, gości: 1