Hvordan er EasyGraphics programmert?
NB! Denne siden er for spesielt interesserte, og tar for seg emner som ligger godt utenfor et grunnkurs i programmering. Beskrivelsen er i første rekke skrevet for faglærere som bruker boken i undervisning. Hensikten er å introdusere de viktigste ideene. Se også kommentarer i kildekoden.
Krav til løsningen
EasyGraphics blir brukt i de fire første kapitlene i boken, som tar for seg variabler, kontrollstrukturer og (statiske) metoder. Vi ønsket oss mulighet for å illustrere slike grunnleggende begrep med grafikkprogrammering, på en så enkel måte som mulig. Vi satt følgende krav til løsningen:
- Det skal være mulig å tegne enkle figurer (linjer, rektangel, sirkler m.fl.), samt skalere og flytte (animere) slike figurer.
- Det skal være mulig å lese inndata fra brukeren, helst via et tekstfelt integrert i tegnevinduet.
- Grafikkprogram (program som bruker EasyGraphics) skal være så enkle som mulig. Det skal bl.a. ikke være nødvendig å opprette objekt, eller å forholde seg til hendelser. Kall på grafikkmetoder skal være på formen met(...), altså uten behov for punktnotasjon.
- Grafikkprogram skal bygge på en rent sekvensiell tankegang. Hvis man f.eks. først ber om en animasjon og deretter gjør innlesing fra brukeren vha. EasyGraphics-metoder, skal innlesingen først skje når animasjonen er fullført.
- Grafikkprogram skal fritt kunne bruke variabler, kontrollstrukturer og metoder.
- Ett og samme brukerprogram skal kunne kjøres både som selvstendig applikasjon og som en applett i nettleseren.
Vi unngår punktnotasjon ved å kreve at brukerprogrammet subklasser EasyGraphics. Dette medfører riktignok at vi må godta en extends-besvergelse som kompliserer litt, men vi mener dette er til å leve med.
Løsningen legger opp til at all innlesing gjøres via et tekstfelt integrert i tegnevinduet, og utdata vises i selve tegnevinduet. I forbindelse med testing kan det være nyttig å skrive ut til konsollet. EasyGraphics tilbyr en metode println for dette formålet, slik at utskriften kommer på "forventet" tidspunkt (den venter på at eventuelle animasjoner blir fullført). Denne metoden bør altså brukes i stedet for System.out.println.
Oppbygging
Setningene i grafikkprogrammet blir utført på en egen tråd, vi kaller den for brukertråden. Tegne-operasjoner blir utført på event-tråden. Kall på tegne-metoder blir representert i en datastruktur (en kø) og deretter overført til event-tråden for utførelse.
De viktigste datastrukturene i EasyGraphics:
- Steg-kø: For hver metode som grafikkprogrammet kaller, blir det opprettet et steg-objekt (EGStep). Dette kan f.eks. innebære å tegne en figur, eller å flytte (animere) en figur. Brukertråden legger slike steg-objekt inn bakerst i køen, mens event-tråden tar ut steg-objekt først i køen for utførelse. Steg-køen er egentlig en kø av køer for å redusere synkronisering mellom trådene.
- Figur-liste: Kall på tegneoperasjonene (drawXXX/fillXXX) skal føre til at et nytt figur-objekt (EGFigure) blir opprettet. Samlingen av alle figur-objekt blir representert i en liste, ordnet fra eldste til nyeste.
Event-tråden blir kontrollert av et Timer-objekt som kontinuerlig kaller opp en hendelsesmetode (actionPerformed i tegneflaten EGCanvas). For hver hendelse skjer følgende:
- Neste steg-objekt blir hentet fra køen og utført (alle steg-objekt har en metode execute). Effekten kan f.eks. være at et figur-objekt blir flyttet, reskalert, eller gjort usynlig. Lengre forflytninger/reskaleringer blir brutt ned i flere "mini-steg". Motsatt, vil flere små endringer (på ulike figurer) bli utført i én operasjon.
- Figur-listen blir gjennomløpt og oppdaterte figurer blir tegnet på nytt. Kun den delen av vinduet som er endret blir tegnet på nytt.
I visse situasjoner må det legges inn venting, blant annet i forbindelse med brukerkommunikasjon:
- Når brukerprogrammet kaller på EasyGraphics-metoden getText for å gjøre innlesing fra brukeren, settes brukertråden på vent til alle steg-objekt er ferdig utført. Dette håndteres av klassen EGLatch.
I forbindelse med oppstart av programmet er det nødvendig å opprette et objekt av klassen deklarert i grafikkprogrammet (programmet som brukeren skriver). Dette gjøres ved bruk av klassen ClassLoader.
EasyGraphics tilbyr en metode setSpeed for å styre farten på animasjoner. Denne er meget enkel, og sørger kun for å legge inn korte pauser mellom hvert steg. Farten på en forflytning vil variere med hvilken maskin man kjører på, og om man gjør lange eller (mange) korte forflytninger med moveXXX-metodene.
Klassene i EasyGraphics
- EasyGraphics: Hovedklassen med tegnemetodene.
- EGGui: Oppretter brukergrensesnittet.
- EGCanvas: Tegneflaten.
- EGFigure: Abstrakt superklasse for figur-objekt.
- EGArc: Representasjon av sirkelbuer og "kakestykker".
- EGCircle: Representasjon av sirkler.
- EGEllipse: Representasjon av ellipser (ovaler).
- EGLine: Representasjon av linjestykker.
- EGRectangle: Representasjon av rektangel.
- EGText: Representasjon av (grafisk) tekst.
- EGStep: Abstrakt superklasse for "steg", der hvert steg tilsvarer en operasjon utført av brukerprogrammet, f.eks. et tegnesteg eller et animasjonssteg.
- EGErrorStep: Avslutter programkjøring med feilmelding.
- EGGlobalStep: Setter bakgrunnsfarge og/eller fart.
- EGFinishStep: Fullfører alle ventende animasjoner.
- EGInputStep: Leser inndata fra brukeren.
- EGMakeStep: Oppretter en figur.
- EGMoveStep: Flytter en figur.
- EGPauseStep: Legger inn en (kort) pause.
- EGPrintStep: Skriver ut en melding til konsollet.
- EGResizeStep: Endrer størrelse på en figur.
- EGShowStep: Gjør en figur synlig/usynlig.
- EGWindowStep: Gjør tegnevinduet synlig.
- EGCommon: Felles konstanter og hjelpemetoder.
- EGRunner: Starter handlingene i brukerprogrammet (run) på event-tråden.
- EGBuffer: Håndtering av stegkøene som deles av brukertråden og event-tråden.
- EGLatch: Sørger for synkronisering mellom brukertråden og event-tråden.