Rita linjer och lite annat i Java

Permalänk
Medlem

Rita linjer och lite annat i Java

Hejsan.

Jag har en fråga och det är hur man ritar en linje i Java, utan att veta slutpunkt för x och y.

Tänk er att vi vet en startpunkt och längden på linjen. Vi vet också en vinkel åt vilket håll linjen skall ritas.

Hur gör jag för att åstadkomma detta? Är det mest fördelaktigt att räkna med grader eller radianer?

Och, om jag dessutom vill att "systemet" kollar om linjen har fått ett ny vinkel (i så fall skall den ritas om), hur gör jag detta?

Det jag ska göra är en visare på en klocka, och för att få reda på tiden använder jag get-metoden i GregorianCalander. Syftet är då alltså att rita en klocka som ändras successivt.

Visa signatur

| Chassi: Cooler Master Cosmos S | Mobo: Asus z87 Deluxe | CPU: Intel i7 4770K | CPU-cooler: Noctua NH-U9B | Grafikkort: Asus GTX 780 DCII | RAM: 16GB Corsair Vengeance LP | SSD: Samsung 840 120GB | PSU: Corsair HX1050 |

Permalänk
Medlem

Radianer. Jag gjorde en liten sekundvisare i Javascript som du säkert kan följa.

<html> <body onload="init();" bgcolor="black"> <script type="text/javascript"> var ctx; var sec = Math.PI; var len = 50; function init() { ctx = document.getElementById('canvas').getContext('2d'); return setInterval(draw,1000); } function draw() { ctx.fillStyle = "rgb(0,0,0)"; ctx.clearRect(0,0,320,240); ctx.fillStyle = "rgb(255,255,255)"; var sx = Math.sin(sec)*len; var sy = Math.cos(sec)*len; ctx.beginPath(); ctx.strokeStyle = "rgb(255,255,255)"; ctx.moveTo(160,120); ctx.lineTo(160+sx,120+sy); ctx.stroke(); sec-=(2*Math.PI/60); } </script> <canvas id="canvas" width="640" height="480"> <p>Your browser doesn't support canvas.</p> </canvas> </body> </html>

Permalänk
Medlem

Överlagra metoden paint() i den GUI klass du vill rita i. Om du vill ha en klocka är det kanske enklast att skapa en egen klass som ärver från Canvas och sedan slänga in en sådan i ett fönster. I din canvas kan du sedan skriva om paint().

public void paint(Graphics g) { g.setColor(Color.BLACK); g.drawLine(x1, y1, x2, y2); }

Om du vill rita en visare på en klocka så får du helt enkelt sätta x1 och y1 som centrum på din canvas (eller var du nu vill ha klockans centrum), och sedan räkna ut x2 och y2 baserat på vinkeln för tiden och sin/cos som finns i Math klassen (använd radianer). Sen när klockan tickar och du vill uppdatera hur den är uppritad är det bara att anropa repaint() på ditt canvas-objekt så kommer det rita upp allt på nytt (som du då får basera på den nya datan).

Permalänk
Medlem

Ja, nu kan jag rita strecket men jag har lite kvar innan jag får det att fungera. Det jag tycker verkar märkligt med streckritandet är att det blir tvärtom i y-led när den ritar upp.

public void drawMinute(SimpleWindow w) { // int time = calendar.get(Calendar.MINUTE); // angle = angle - (time * Math.PI / 30); int newX = (int) Math.cos(angle) * length; int newY = (int) Math.sin(angle) * length; w.moveTo(x, y); w.lineTo(x + newX, y + newY);

Det ni inte ser här är angle som är på förhand satt till Math.PI / 2 (90 grader). Då ritas strecket nedåt, medan 90 grader i min värd är uppåt, och ger jag angle värdet 3 * Math.PI / 2 (270 grader) så ritas strecket uppåt. Är det någon som kan förklara varför det blir så?

I X-led ritar den som det är meningen, 0 = 0 och 180 = 180, så att säga.

EDIT 1

Såg inte att någon mer svarat på tråden, det tackar jag för.

Dock är jag inte säker på vad du menar.

Det skall kanske nämnas att det är i Java, och inte Javascript, som detta skall utföras.

Om jag nu vill att programmet skall "kontrollera" om tiden har ändrats, hur gör jag då? Och även om tiden inte har ändrats, så vill jag ju inte att den ska rita visaren igen på samma ställe, eftersom det lär gå åt onödigt med resurser att rita någonting som ändå inte syns, eller?

EDIT 2

Min nuvarande struktur bygger på att jag skapar en klass för varje visare, och sedan en klass med en mainmetod som anropar klasserna...

Visa signatur

| Chassi: Cooler Master Cosmos S | Mobo: Asus z87 Deluxe | CPU: Intel i7 4770K | CPU-cooler: Noctua NH-U9B | Grafikkort: Asus GTX 780 DCII | RAM: 16GB Corsair Vengeance LP | SSD: Samsung 840 120GB | PSU: Corsair HX1050 |

Permalänk
Medlem

Vad är det du inte förstår angående det jag skrev innan?

Jag vet inte vad som är felet med vinkeln. I enhetscirkeln är 0 åt höger, Pi/2 uppåt o.s.v. Du vill dock ha en enhetscirkel som börjar med vinkeln 0 uppåt (en offset på Pi/2) och sedan går medurs (vilket du gör genom att göra vinkeln negativ). Om du vill rita ut minut 15 så bör vinkeln alltså vara 15*2*Pi/60 = 0, vilket ger x = 1 och y = 1. Det enda jag kan tänka mig är att värdet på angle inte är vad du tror. Ersätt raden med angle = Math.PI/2 - time * Math.PI / 30; så borde det fungera i samtliga fall.

För att uppdatera den grafiska klockan kan jag tänka mig 3 egentliga alternativ; 1) skapa en tråd som får kolla om tiden uppdaterats periodiskt och i så fall rensa det som har ritats och rita om klockan, 2) gör som jag sa med paint() och anropa sedan repaint() när tiden ändras, eller 3) använd observer/observable mönstret för att notifiera din grafiska klass om att det har skett något. Jag vet inte hur du har strukturerat ditt program så det är svårt att säga vilket som är lättast/bäst, men jag skulle nog satsa på #2 då jag anser att det är lättast, alternativt #3 som är lite krångligare men mer flexibelt.

Edit: Jag kollade lite på det där med vinkeln i Java, och jag förstår inte heller riktigt hur det går ihop. Man kan i alla fall fixa det genom att använda T*2*Pi/60 - Pi/2 istället. Slängde ihop en ganska ful klocka för att visa sekunder:

import javax.swing.JFrame; public class ClockFrame extends JFrame { public static void main(String[] args) { ClockFrame clock = new ClockFrame(); clock.start(); } ClockCanvas canvas; public ClockFrame() { canvas = new ClockCanvas(500); this.add(canvas); this.pack(); this.setVisible(true); this.setDefaultCloseOperation(EXIT_ON_CLOSE); } public void start() { int sec = 0; while (true) { canvas.repaint(); canvas.setSec(sec); sec = (sec+1) % 60; try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } - import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; public class ClockCanvas extends Canvas { private int curSec; public ClockCanvas(int size) { this.setSize(size, size); } public void paint(Graphics g) { double angle = curSec * 2 * Math.PI / 60 - Math.PI/2; int x = this.getWidth()/2 + (int)(this.getWidth()/4 * Math.cos(angle)); int y = this.getWidth()/2 + (int)(this.getWidth()/4 * Math.sin(angle)); g.setColor(Color.BLACK); g.drawLine(this.getWidth()/2, this.getWidth()/2, x, y); } public void setSec(int sec) { curSec = sec; } }

Permalänk
Medlem
Skrivet av Thomas H:

Vad är det du inte förstår angående det jag skrev innan?

Jag vet inte vad som är felet med vinkeln. I enhetscirkeln är 0 åt höger, Pi/2 uppåt o.s.v. Du vill dock ha en enhetscirkel som börjar med vinkeln 0 uppåt (en offset på Pi/2) och sedan går medurs (vilket du gör genom att göra vinkeln negativ). Om du vill rita ut minut 15 så bör vinkeln alltså vara 15*2*Pi/60 = 0, vilket ger x = 1 och y = 1. Det enda jag kan tänka mig är att värdet på angle inte är vad du tror. Ersätt raden med angle = Math.PI/2 - time * Math.PI / 30; så borde det fungera i samtliga fall.

För att uppdatera den grafiska klockan kan jag tänka mig 3 egentliga alternativ; 1) skapa en tråd som får kolla om tiden uppdaterats periodiskt och i så fall rensa det som har ritats och rita om klockan, 2) gör som jag sa med paint() och anropa sedan repaint() när tiden ändras, eller 3) använd observer/observable mönstret för att notifiera din grafiska klass om att det har skett något. Jag vet inte hur du har strukturerat ditt program så det är svårt att säga vilket som är lättast/bäst, men jag skulle nog satsa på #2 då jag anser att det är lättast, alternativt #3 som är lite krångligare men mer flexibelt.

Edit: Jag kollade lite på det där med vinkeln i Java, och jag förstår inte heller riktigt hur det går ihop. Man kan i alla fall fixa det genom att använda T*2*Pi/60 - Pi/2 istället. Slängde ihop en ganska ful klocka för att visa sekunder:

Nu har jag gjort som följande kod visar:

import se.lth.cs.ptdc.window.SimpleWindow; import java.awt.Color; import java.util.Calendar; import java.util.GregorianCalendar; public class MinuteHand { private Calendar calendar; private int length; private double angle; private int x; private int y; public MinuteHand(Calendar calendar, int x, int y, int length, double angle) { calendar = new GregorianCalendar(); this.x = 225; this.y = 225; this.length = 200; this.angle = angle; } public int getMinute() { int minute = calendar.get(Calendar.MINUTE); return minute; } public void drawMinute(SimpleWindow w) { int time = calendar.get(Calendar.MINUTE); angle = Math.PI / 2 - (time * Math.PI / 30); w.setLineColor(Color.BLUE); int newX = (int) Math.cos(angle) * length; int newY = (int) Math.sin(angle) * length; w.moveTo(x, y); w.lineTo(newX + x, newY + y); } }

När jag exekverar koden så får jag ett NullPointerException... Jag vet inte riktigt varför, då jag har svårt att se att någonting skulle bli noll...

Visa signatur

| Chassi: Cooler Master Cosmos S | Mobo: Asus z87 Deluxe | CPU: Intel i7 4770K | CPU-cooler: Noctua NH-U9B | Grafikkort: Asus GTX 780 DCII | RAM: 16GB Corsair Vengeance LP | SSD: Samsung 840 120GB | PSU: Corsair HX1050 |

Permalänk
Medlem

På vilken rad i koden får du ett exception? Vad säger din stack trace?

Permalänk
Medlem

Det du inte förklarat, som är hyffsat viktigt, är att du läser Java på LTH och har fått en enkel rit-klass att jobba med, nämligen SimpleWindow. Denna har läraren Per Holm själv gjort, och det är därför rätt svårt för utomstående att hjälpa dig med programmet....

Läs på lite i SimpleWindows dokumentation, där (eller kanske var i någon labbhandledning) så behandlas bla. rotation och rita till en punkt om man bara vet vinkel och längd.

Visa signatur

Hackintosh?
Mac OSX Tweaks/Hacks?

--> Besök nya [B]groths.org[/B]

Permalänk
Medlem
Skrivet av Cindori:

Det du inte förklarat, som är hyffsat viktigt, är att du läser Java på LTH och har fått en enkel rit-klass att jobba med, nämligen SimpleWindow. Denna har läraren Per Holm själv gjort, och det är därför rätt svårt för utomstående att hjälpa dig med programmet....

Läs på lite i SimpleWindows dokumentation, där (eller kanske var i någon labbhandledning) så behandlas bla. rotation och rita till en punkt om man bara vet vinkel och längd.

Det du säger stämmer till viss del. Jag läser Java på LTH, dock inte den kursen dessa uppgifter tillhör. Jag har gått tillbaka för att göra dessa uppgifterna, eftersom jag inte känner mig säker på det vi gjorde tidigare. Och att det är Per Holm som har skrivit klassen SimpleWindow stämmer också, dock ser jag inte alls vad det har för relevans i sammanhanget.

SimpleWindow finns här.

Och jag tycker att jag har läst men att ingenting därifrån verkar vara det jag söker. Dessutom, det har väl ingenting med att jag får nullpointerexc att göra?

Jag får nullpointer vid rad 34, när jag skapar variabeln time av typen int och försöker ge den ett värde, "minutens" värde...

Visa signatur

| Chassi: Cooler Master Cosmos S | Mobo: Asus z87 Deluxe | CPU: Intel i7 4770K | CPU-cooler: Noctua NH-U9B | Grafikkort: Asus GTX 780 DCII | RAM: 16GB Corsair Vengeance LP | SSD: Samsung 840 120GB | PSU: Corsair HX1050 |

Permalänk
Medlem

Det är ganska relevant att det är SimpleWindow du jobbar mot, eftersom det innebär att ingen som inte gått den kursen kommer fatta vad det är du vill åstadkomma. De förslag som Thomas har gett dig är inte direkt överförbara på klassen du jobbar mot eftersom det är en klass som normalt inte finns i Java. Det är en "lek-klass" som finns till för att lära sig, det är inte något man använder senare för att rita i Java.

Dina försök tyder på att du saknar grundläggande kunskaper inom objektorienterad programmering. Till exempel har du en konstruktor MinuteHand med parametrar (Calendar, int, int, int, double) som du inte ens använder, utan sätter till eget värde i själva konstruktorn. Jag tycker att du ska sätta dig med kursboken och läsa på lite om metoder och parametrar.

Ett bra sätt att undersöka exceptions är att, som tidigare nämnt, kolla sin stack trace. Kursboken behandlar även detta.

Visa signatur

Hackintosh?
Mac OSX Tweaks/Hacks?

--> Besök nya [B]groths.org[/B]

Permalänk
Medlem
Skrivet av SniFFy<3:

Jag får nullpointer vid rad 34, när jag skapar variabeln time av typen int och försöker ge den ett värde, "minutens" värde...

I så fall är calendar null. Varför vet jag inte, men det är det enda som skulle kunna ge ett NullPointerException vad jag kan se.

Jag fattar dock inte vad du försöker uppnå. Varför behöver du separata klasser för visare? Du har en tid, och utifrån tiden kan du räkna ut vad sekund-, minut- och timvisaren bör stå på. Sen säger du åt GUI:t att rita 3 visare (linjer) från fönstrets mitt och till de kordinater som du beräknat att visaren ska peka mot (kartesiska koordinater), eller vilken vinkel och längd linjen ska ha (polära koordinater) om det finns metoder för det i någon Java-klass.
Utifrån API:t till SimpleWindow vet jag inte hur den ritar upp saker. Vad du behöver göra är att ta bort allt som ritats och sedan rita upp nya linjer när tiden ändras, och det vet jag inte om SimpleWindow gör. Rent spontant känns klassen fundamentalt missanpassad för vad du vill göra, men det kanske är jag som missförstår hur den fungerar. Ser dock ut som den är tänkt att användas som ett enkelt ritprogram för att kladda på skärmen och inte så mycket mer.

Permalänk
Medlem
Skrivet av Thomas H:

I så fall är calendar null. Varför vet jag inte, men det är det enda som skulle kunna ge ett NullPointerException vad jag kan se.

Konstruktorn tar emot en Calendar calendar som argument och new:ar den i bodyn istället för att initiera objektets egen calendar.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem

Ah, visst fan. Såg bara att calendar tilldelades ett nytt objekt, tänkte inte alls på att parametern hade samma namn. Kul med scope och synlighet.

Permalänk
Medlem
Skrivet av Cindori:

Det är ganska relevant att det är SimpleWindow du jobbar mot, eftersom det innebär att ingen som inte gått den kursen kommer fatta vad det är du vill åstadkomma. De förslag som Thomas har gett dig är inte direkt överförbara på klassen du jobbar mot eftersom det är en klass som normalt inte finns i Java. Det är en "lek-klass" som finns till för att lära sig, det är inte något man använder senare för att rita i Java.

Dina försök tyder på att du saknar grundläggande kunskaper inom objektorienterad programmering. Till exempel har du en konstruktor MinuteHand med parametrar (Calendar, int, int, int, double) som du inte ens använder, utan sätter till eget värde i själva konstruktorn. Jag tycker att du ska sätta dig med kursboken och läsa på lite om metoder och parametrar.

Ett bra sätt att undersöka exceptions är att, som tidigare nämnt, kolla sin stack trace. Kursboken behandlar även detta.

Jag är mycket väl medveten om mina begränsningar inom Java, därför jag försöker lära mig mer. Och vad det beträffar SimpleWindow så är denna uppgift tänkt att utföras i SimpleWindow. Jag är även medveten om att SimpleWindow inte är något man använder senare. T.o.m. jag förstår att namnet säger allt.

Mina parametrar i konstruktorn såg inte ut så från början, utan det är saker jag har provat att ändra allteftersom jag inte fick önskat resultat. Från början fick de inget värde...

Istället för att skriva hur man inte ska göra vore det trevligt med lite konkret information hur jag kan göra, förutom att läsa kursboken (vilken redan är läst från pärm till pärm säkert fyra gånger). Ett exempel eller liknande fungerar alldeles utmärkt, vill inte att någon annan ska göra uppgiften åt mig.

Och, ang det förra inlägget där jag ifrågasatte relevansen av SimpleWindow, så menade jag att det hade ingenting med mitt nullpointer att göra, därav var det inte relevant för min frågeställning ang. nullpointer.

Hursomhelst, kan prova att börja om helt för att se om det blir annorlunda. Detta får dock vänta till imorgon, eftersom dagen kommer att tillbringas i en bil.

Väl mött

Visa signatur

| Chassi: Cooler Master Cosmos S | Mobo: Asus z87 Deluxe | CPU: Intel i7 4770K | CPU-cooler: Noctua NH-U9B | Grafikkort: Asus GTX 780 DCII | RAM: 16GB Corsair Vengeance LP | SSD: Samsung 840 120GB | PSU: Corsair HX1050 |