Bildebok
Modulen er bygget opp med en gjennomgang av grunnleggende operasjoner med både bezierflater og teksturer. Jeg vil prøve å legge ut en "tutorial" med små enkle eksempler om de aktuelle temaene. Jeg vil til slutt prøve å summere alt opp i mitt ferdige program som er bildeboken.
Bezierflate
En bezierflate er en flate spesifisert av x,y og z koordinater. Man bruker en slik flate i tilfeller der en ikke kan klare seg med enkle figurer som sylindre, sirkler o.l.
Det kan først være en ide å beskrive en bezierkurve og hvordan kontrollpunktene
fungerer. La oss tenke oss at vi har en kurve som har 4 kontrollpunkter.
Et punkt på hver ende av linjen, og to midt på. Dette kan illustreres slik:
Linjen vil alltid henge fast i endepunktene, men ikke nødvendigvis i de to på
midten. For å få en annen kurve på linjen kan man flytte på disse punktene:
Det samme prinsippet gjelder når en lager en bezier-flate. Endepunktene henger
også her fast, mens alle andre kontrollpunkter er punkter som er med på å forme
kurven.
For å lage en slik flate er det et par ting som må gjøres:
- Sett opp kontrollpunkter
- Kall map
- Kall mapGrid
- Kall evalMesh
Et eksempel på prosessen å tegne ut en slik flate kan være:
... float kontrollpunkter[] = { 0.6f, -2.0f,0.0f, 4.0f,-2.0f,0.0f, 0.6f, -1.0f,0.0f, 4.0f,-1.0f,0.0f, 0.6f, 1.0f,0.0f, 4.0f,1.0f,0.0f, 0.6f, 2.0f,0.0f, 4.0f,2.0f,0.0f, }; gl.glMap2f(GL_MAP2_VERTEX_3,0.0f,1.0f,3,2,0.0f,1.0f,6,4, kontrollpunkter); gl.glMapGrid2f(20,0.0f,1.0f,20,0.0f, 1.0f); gl.glEvalMesh2(GL_LINE, 0, 20, 0, 20); ...
Denne koden vil tegne ut denne flaten:
Det er nødvendig å forklare litt mer om metoden gl.glMap2f.
Denne metoden tar inn en del parametre:
1 2 3 4 gl.glMap2f(GL_MAP2_VERTEX_3,0.0f,1.0f,3,2,0.0f,1.0f,6,4, kontrollpunkter);
Legg merke til verdiene i kommandoen som har tall over seg. Dette er de viktigste parametrene som en må forstå for å kunne tegne ut en enkel flate. Forklaringen er som følger:
- Antall verdier for hvert punkt
- Antall punkter for hver del av flaten
- Antall koordinater for hver del (a*b)
- Antall deler
Tekstur
Denne delen tar for seg en grunnleggende del av det å bruke teksturer. Den vil fokusere på å legge en tekstur på bezierflate.
For å legge en tekstur på en flate trenger man å gjøre følgende operasjoner
- Gjøre klart bildet
- Laste det
- Spesifisere kontrollpunkter for bildet
- "Mappe" det
- Tegne det ut
Et viktig moment med tanke på selve bildet er at det må være på et spesielt format. Bildet må være av størrelse 2x x 2y. Dette er typisk et bilde med for eksempel width: 128, height: 256.
Før en kan tegne ut bildet må man laste det. Her har man mange forskjellige opsjoner en kan velge. Jeg kommer ikke til å gå inn på disse her. En måte å laste bildet på er å lage en metode som tar inn et parameter; filnavn, og som gjør resten av jobben med lastingen. En slik metode kan være:
public void loadGLTexture(String filename){ PngTextureLoader texLoader = new PngTextureLoader(gl, glu); texLoader.readTexture(filename); if(texLoader.isOk()){ gl.glGenTextures(1, texture); gl.glBindTexture(GL_TEXTURE_2D, texture[0]); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexImage2D(GL_TEXTURE_2D, 0, 3, texLoader.getImageWidth(), texLoader.getImageHeight(), 0, myParent.GL_RGB, myParent.GL_UNSIGNED_BYTE, texLoader.getTexture() ); } }
En ting som er verdt å nevne i sammenheng med denne metoden, er at denne laster
bilder som er på png-format. Man er ikke begrenset til å bare bruke png-bilder, men
jeg vil ikke omtale, andre formater her.
Man kaller da metoden slik:
loadGLTexture("picture.png");
Det neste som må gjøres, er å spesifisere kontrollpunkter for bildet. For å få et bilde til å dekke hele flaten vil kontrollpunktenen kunne være slik:
float txpts[] = { -1.0f, -1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f };
For å tegne ut bildet, trenger du å mappe det. Dette gjøres med denne kommandoen:
a b c d gl.glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, txpts);
a, b, c og d er på samme måte her som når en mapper en bezierflate. For å tegne ut flaten med texturen på, kaller man på samme metode som for å tegne ut bare flaten.
gl.glMapGrid2f(20,0.0f,1.0f,20,0.0f, 1.0f); gl.glEvalMesh2(GL_FILL, 0, 20, 0, 20);
En flate tilsvarende den som er beskrevet over med textur, kan for eksempel se slik ut:
Animasjon
Animasjon kan gjøres på mange måter. En ting som er nødvendig når det er snakk om bevegelse i javaprogrammer, er threading. Man oppretter threads for å kunne ha animasjon gående, samtidig som resten av programmet fortsatt er fullt brukbart. Måten som dette kan implementeres på er som følger:
- La Canvas klassen implementere runnable
- Opprett en ny Thread av Canvaset i den kjørbare klassen (Thread t = new Thread(canvas);)
- kall t.start()
Animasjon med openGL foregår som regel med en løkke av et slag, som forandrer koordinatene til et objekt. Et pseudokode-eksempel på dette kan være:
float distance = 0.0f; ... public void display(){ ... gl.glTranslatef(0.0f, 0.0f, distance); tegnObject ... } ... for(int i = 0; i < 100; i++){ distance += 1.0f; repaint(); } ...
Matematikk
Det som er verdt å nevne om matematikk i denne modulen er teorien bak en sirkel. En sirkel kan beskrives slik:
y(t)=r·sin(2·pi·t) x(t)=r·cos(2·pi·t)
Bildebok
Perm
Når det gjelder permen, så var det flere muligheter å vurdere før jeg fant ut at jeg ville ha permen stiv som tykke bøker. Jeg kunne velge å ha en ringperm, eller jeg kunne lage et blad, som ikke har stiv perm. Jeg har valgt å ha permen stiv, da dette ser penere ut. Valget med å ha stiv perm, gir meg litt flere utfordringer, enn jeg ville fått med å ha en ringperm, eller å ha boken som et hefte. Disse problemstillingene tar jeg opp når jeg forklarer hvordan boken blir åpnet.
Jeg har bygget opp permen helt og fullt av bezier-flater. For å få permen til å se tykk ut, og mest mulig lik en ekte bok, har jeg lagt alt dobbelt. Måten dette er gjort på i pseudo-kode er:
1. Push 2. tegn flate 3. flytt 0.5 ned 4. tegn flate 5. Pop
Som sagt, så er hele boken bygget opp av bezierflater. Toppen, og bunn av boken er bygget opp med disse kontrollpunktene:
float topPerm[] = { 0.6f, -2.0f,0.5f, 4.0f,-2.0f,0.5f, 0.6f, -1.0f,0.5f, 4.0f,-1.0f,0.5f, 0.6f, 1.0f,0.5f, 4.0f, 1.0f,0.5f, 0.6f, 2.0f,0.5f, 4.0f, 2.0f,0.5f, };
Jeg tegner så toppen, og deretter bunnen. Jeg har en metode som tar for seg å tegne
denne flaten to ganger med et mellomrom på 0.05f. Deretter "translater" jeg -0.5f på
z-aksen og kaller på metoden som tegner flatene igjen. Dette vil se slik ut:
I tillegg til disse flatene, må jeg tegne noe som binder disse sammen, slik at det ser ut som en perm, som er litt tykk. Dette gjøres med bezierflater også. Jeg definerer her tre flater, som skal binde de to sidene sammen. Disse er:
float topPermFrontSide[] = { 0.6f, -2.0f, 0.5f, 4.0f, -2.0f, 0.5f, 0.6f, -2.25f, 0.47f, 4.0f + 0.25f, -2.25f, 0.47f, 0.6f, -2.0f, 0.45f, 4.0f, -2.0f, 0.45f }; float topPermBackSide[] = { 0.6f, 2.0f, 0.5f, 4.0f, 2.0f, 0.5f, 0.6f, 2.25f, 0.47f, 4.0f + 0.25f, 2.25f, 0.47f, 0.6f, 2.0f, 0.45f, 4.0f, 2.0f, 0.45f }; float topOuter[] = { 4.0f, -2.0f, 0.5f, 4.0f, 0.0f, 0.5f, 4.0f, 2.0f, 0.5f, 4.25f, -2.25f, 0.47f, 4.25f, 0.0f, 0.47f, 4.25f, 2.25f, 0.47f, 4.0f, -2.0f, 0.45f, 4.0f, 0.0f, 0.45f, 4.0f, 2.0f, 0.45f };
Ved å tegne ut disse flatene, så vil topp og bunn på boken se slik ut:
Flatene som er heltrukket med farge er bindeleddene.
For å få permen til å se mest mulig naturtro ut, valgte jeg å ta med den lille bøyen som er på bøker før man kommer til ryggen. Dette måtte jeg også lage dobbelt. Punktene er som følger:
float topCurl[] = { 0.5f, -2.0f,0.5f, 0.55f,-2.0f,0.45f, 0.6f, -2.0f,0.5f, 0.5f, -1.0f,0.5f, 0.55f,-1.0f,0.45f, 0.6f, -1.0f,0.5f, 0.5f, 1.0f,0.5f, 0.55f,1.0f,0.45f, 0.6f, 1.0f,0.5f, 0.5f, 2.0f,0.5f, 0.55f,2.0f,0.45f, 0.6f,2.0f,0.5f, }; float botCurl[] = { 0.5f, -2.0f,0.0f, 0.55f,-2.0f,0.05f, 0.6f, -2.0f,0.0f, 0.5f, -1.0f,0.0f, 0.55f,-1.0f,0.05f, 0.6f, -1.0f,0.0f, 0.5f, 1.0f,0.0f, 0.55f,1.0f,0.05f, 0.6f, 1.0f,0.0f, 0.5f, 2.0f,0.0f, 0.55f,2.0f,0.05f, 0.6f,2.0f,0.0f, }; float curlSide[] = { 0.5f, -2.0f, 0.5f, 0.5f, -2.25f, 0.47f, 0.5f, -2.0f, 0.45f, 0.55f, -2.0f, 0.45f, 0.55f, -2.25f, 0.42f, 0.55f, -2.0f, 0.40f, 0.6f, -2.0f, 0.5f, 0.6f, -2.25f, 0.47f, 0.6f, -2.0f, 0.45f, };
Disse punktene tegnet ut, utgjør bøyen som ser slik ut:
Til slutt tegnet jeg opp ryggen på boken. Denne består som resten av permen av to flater, som blir bundet sammen. Punktene er som følger:
float innerZFirst = 0.0f; float innerZLast = 0.45f; float innerXSec = 0.45f; float innerXThird = 0.45f; float backPerm[] = { 0.5f, -2.0f,-0.05f, 0.4f,-2.0f,0.15f, 0.4f, -2.0f,0.3f, 0.5f,-2.0f,0.5f, 0.5f, -1.0f,-0.05f, 0.4f,-1.0f,0.15f, 0.4f, -1.0f,0.3f, 0.5f,-1.0f,0.5f, 0.5f, 1.0f,-0.05f, 0.4f,1.0f,0.15f, 0.4f, 1.0f,0.3f, 0.5f,1.0f,0.5f, 0.5f, 2.0f,-0.05f, 0.4f,2.0f,0.15f, 0.4f,2.0f,0.3f, 0.5f,2.0f,0.5f, }; float backPermInner[] = { 0.5f, -2.0f,innerZFirst, innerXSec,-2.0f,0.15f, innerXThird, -2.0f,0.3f, 0.5f,-2.0f,innerZLast, 0.5f, -1.0f,innerZFirst, innerXSec,-1.0f,0.15f, innerXThird, -1.0f,0.3f, 0.5f,-1.0f,innerZLast, 0.5f, 1.0f,innerZFirst, innerXSec,1.0f,0.15f, innerXThird, 1.0f,0.3f, 0.5f,1.0f,innerZLast, 0.5f, 2.0f,innerZFirst, innerXSec,2.0f,0.15f, innerXThird,2.0f,0.3f, 0.5f,2.0f,innerZLast, };
Det som er verdt å nevne med denne delen, er at backPermInner, bruker noen
variabler i arrayen. Hvorfor det er sånn, kommer jeg tilbake til i delen som
forklarer hvordan åpningen av boken foregår.
Når ryggen på boken er tegnet ut, er permen fullstendig. Uten textur vil permen
se slik ut, åpen og lukket:
Sidene
Jeg har trikset litt for å lage sidene i boken. Her har jeg tre bezierflater, som forandres ettersom man blar i boken. Jeg vil først forklare hvordan disse sidene er bygget opp, deretter tar jeg for meg hvordan jeg har bygget opp innholdet til å se ut som om boken er full.
Den siden som vises når en åpner boken refereres til som aktiv. I tilleg har jeg to andre sider som er forrige side, og neste side. Disse er bygget opp på følgende måte:
float textPage[] = { txt1X, 2.0f, txt1Z, txt2X, 2.0f, txt2Z, txt3X, 2.0f, txt3Z, txt4X, 2.0f, txt4Z, txt1X, 1.0f, txt1Z, txt2X, 1.0f, txt2Z, txt3X, 1.0f, txt3Z, txt4X, 1.0f, txt4Z, txt1X,-1.0f, txt1Z, txt2X, -1.0f, txt2Z, txt3X, -1.0f, txt3Z, txt4X, -1.0f, txt4Z, txt1X,-2.0f, txt1Z, txt2X, -2.0f, txt2Z, txt3X, -2.0f, txt3Z, txt4X, -2.0f, txt4Z, }; float prevPage[] = { prev1X, 2.0f, prev1Z, prev2X, 2.0f, prev2Z, prev3X, 2.0f, prev3Z, prev4X, 2.0f, prev4Z, prev1X, 1.0f, prev1Z, prev2X, 1.0f, prev2Z, prev3X, 1.0f, prev3Z, prev4X, 1.0f, prev4Z, prev1X,-1.0f, prev1Z, prev2X, -1.0f, prev2Z, prev3X, -1.0f, prev3Z, prev4X, -1.0f, prev4Z, prev1X,-2.0f, prev1Z, prev2X, -2.0f, prev2Z, prev3X, -2.0f, prev3Z, prev4X, -2.0f, prev4Z, }; float nextPage[] = { next1X, 2.0f, next1Z, next2X, 2.0f, next2Z, next3X, 2.0f, next3Z, next4X, 2.0f, next4Z, next1X, 1.0f, next1Z, next2X, 1.0f, next2Z, next3X, 1.0f, next3Z, next4X, 1.0f, next4Z, next1X,-1.0f, next1Z, next2X, -1.0f, next2Z, next3X, -1.0f, next3Z, next4X, -1.0f, next4Z, next1X,-2.0f, next1Z, next2X, -2.0f, next2Z, next3X, -2.0f, next3Z, next4X, -2.0f, next4Z, };
Disse tre sidene består bare av den ene flaten. Jeg har valgt å ikke lage dem doble,
grunnet at en side i en bok er såpass tynn at det ikke er nødvendig.
Variablene som er brukt i disse tre sidene, kommer jeg til å forklare nærmere under
delen som omhandler blaing. Ved å tegne ut textPage med textur på, så vil denne se
slik ut:
For å få det til å se ut som om det er en bok full av sider har jeg bygget opp fem flater.
Se illustrasjon:
Åpning av perm
Når permen skal åpnes dukker det opp en del problemstillinger. Hvor langt skal permen åpnes? Hvordan henger sidene fast? For å ta det første først. En har flere muligheter når det gjelder hvordan permen skal åpnes.
- Åpne den helt opp så permen ligger flat
- Åpne den delvis opp, så ryggen fortstatt står ca. 90 grader på bakpermen.
Alternativ 1
Når permen åpnes, må sidene henge fast i ryggen. Man må da lage en algoritme som gjør dette mest mulig naturlig. Man må forandre verdiene på sidene slik at de får den nødvendige bøyen, samtidig som permen må åpne seg.
Alternativ 2
Dette vil bli en litt mer avansert metode å åpne boken på. Selve åpningen vil gå greit, men det vil bli en ganske mye mer kompleks prosess når en blar sidene. Permen må da etterhvert som man blar pga. tyngden av arkene falle mer og mer ned mot bordet.
Mitt valg
Jeg har valgt alternativ 1 som en løsning i mitt prosjekt. Grunnen til dette, er at
den er mindre kompleks, men ser allikevel naturlig ut.
Åpningen av permen foregår på en slik måte:
1. Så lenge i er mindre enn 90 1.1 Roter rygg med i grader 1.2 Roter topen av boken med i*2 grader 1.3 Juster punktene i sidene
Tegningen av boken foregår slik:
1. public void drawBook(){ 2. myColor.setLightBlueMaterial(); 3. gl.glPushMatrix(); 4. gl.glTranslatef(0.5f, -2.0f,-0.05f); 5. gl.glRotatef(deg, 0.0f, -1.0f, 0.0f); 6. gl.glTranslatef( -0.5f, 2.0f, 0.05f); 7. 8. gl.glPushMatrix(); 9. gl.glTranslatef(0.5f, -2.0f,0.5f); 10. gl.glRotatef(deg, 0.0f, -1.0f, 0.0f); 11. gl.glTranslatef( -0.5f, 2.0f, -0.5f); 12. drawTopOfBook(true); 13. drawTopCurl(); 14. drawTopOuter(); 15. gl.glPopMatrix(); 16. 17. drawBack(); 18. drawBackCurl(); 19. drawLeftKant(); 20. gl.glPopMatrix(); 21. 22. gl.glPushMatrix(); 23. gl.glTranslatef(0.0f, 0.0f, -0.5f); 24. drawTopOfBook(false); 25. drawTopOuter(); 26. gl.glPopMatrix(); 27. drawBotCurl(); 28. drawKanter(true, true); 29. }
Variabelen "deg" som du finner på linje 5 og 10, blir i utgangspunktet satt til å være 0.0. Når boken åpnes går det en løkke fra 0 til 90, som plusser på deg med 1 for hver gang. Det vil til slutt resultere i at boken har blitt åpnet. I tillegg til dette må punktene til flatene som skal representere innholdet forandres. De forandres etter følgende linjer med kode:
xFUp=(float)(pagesRadUp*Math.cos((Math.PI/2)*(double)((i+90)/90))); zFUp=(float)(pagesRadUp*Math.sin((Math.PI/2)*(double)((i+90)/90))) - 0.055f; xFMid2=(float)(pagesRadMid2*Math.cos((Math.PI/2)*(double)((i+90)/90))); zFMid2=(float)(pagesRadMid2*Math.sin((Math.PI/2)*(double)((i+90)/90))) - 0.055f; xFMid1=(float)(pagesRadMid1*Math.cos((Math.PI/2)*(double)((i+90)/90))); zFMid1=(float)(pagesRadMid1*Math.sin((Math.PI/2)*(double)((i+90)/90))) - 0.055f; xFDown=(float)(pagesRadDown*Math.cos((Math.PI/2)*(double)((i+90)/90))); zFDown=(float)(pagesRadDown*Math.sin((Math.PI/2)*(double)((i+90)/90))) - 0.055f; myPage.setBotPages( (xFDown), (xFMid1), (xFMid2), (xFUp), (zFDown + toAdd), (zFMid1 + toAdd), (zFMid2 + toAdd), (zFUp + toAdd) );
Som illustrasjonen over viser, så er det punktene FUp, FMid2, FMid1 og FDown som forandres etterhvert.
Punktene som forandres følger en sirkel når de beveges. Dette kan sees slik:(tegningen er unøyaktig)
Som vi ser ut i fra tegningen, så har de forskjellige sirklene en forskjellig radius. Dette kommer til
uttrykk i koden som variablene pagesRadUp, pagesRadMid2, pagesRadMid1 og pagesRadDown.
pagesRadUp er radius til FUp,
pagesRadMid2 er radius til FMid2,
pagesRadMid1 er radius til FMid1 og
pagesRadDown er radius til FDown
Andre ting som er verdt å nevne i sammenheng med åpningen av boken er at når boken åpnes, blir første siden i boken gjort klar.
En snap-shot serie av åpningen av boken ser slik ut:
Blaing
Når deg gjelder blaingen er det også flere angrepsmetoder.
- Ta tak i nedre høyre hjørne og bla derfra
- Ta tak i øvre høyre hjørne og bla derfra
- Ta tak midt på siden og bla derfra
- Skyve siden fra midten og over.
Mitt valg
Jeg har valgt å bla etter fremgangsmåte 3. Grunnen til dette er at det vil være litt mer trivielt å få til med tanke på den tekniske biten. Et alternativ kunne vært å bruke fremgangsmåte nummer 4. Grunnen til at jeg ikke har valgt denne metoden, er at jeg synes det er mer naturlig å bla etter metode nummer 3.
Før jeg beskriver hvordan en side blas, er det nødvendig å illustrere hvordan siden er bygget opp.
Som illustrasjonen viser, tar jeg hensyn til tre akser gjennom siden. Den aksen lengst til venstre, tar jeg
ikke hensyn til, da denne ikke skal forandres. (Siden rives ikke løs fra boken).
De tre aksene er outer, midOuter og midInner. For å få en nødvendig bøy/blafring i arket, kan ikke disse tre
aksene forandre seg i samsvar med hverandre. Blaingen foregår på følgende måte:
1. evig løkke 1.1 Hvis outer har krysset midInner: 1.1.1 Hvis midInner er på plass: 1.1.1.1 Stopp med å forandre midInner. 1.1.2 Forandre midInner etter sirkelformel med høyeste hastighet 1.2 Hvis outer har krysset midOuter: 1.2.1 Hvis midOuter er på plass: 1.2.1.1 Stopp med å forandre midOuter 1.1.2 Forandre midOuter etter sirkelformel med høy hastighet 1.5 Hvis outer er på plass: 1.5.1 Slutt å bla og returner 1.6 Forandre outer i etter sirkelformel med normal hastighet
Denne prosessen sørger for at outer er den første aksen som starter med å bevege seg, og den siste som "kommer i mål". midOuter er nummer to til å starte, og nummer to til å komme i mål. midInner er nummer 3 til å starte, og nummer 1 til å komme i mål.
Denne animasjonen følger linjen til en sirkel.
Aksene følger en radius som er tilsvarende så langt fra roteringspunktet som de ligger.
Avstanden fra origo til roteringspunktet blir lagt til for å få riktig rotering.
For at boken skal virke naturlig er det ganske mange andre ting som må skje når en side blas. For å liste opp disse:
- Minske tykkelsen på høyre side av boken
- Øke tykkelsen på venstre side av boken
- Tegne ut neste side, slik at den ligger klar når første side er bladd.
- Tegne ut venstresides øverste side
Jeg har definert en viss tykkelse på hvert ark. Så minskingen og økningen blir påvirket av denne tykkelsen.
En serie snap-shots av blaingen ser slik ut:
Begrensninger/Utvidelser
For å rekke å bli leveringsklar, har jeg tatt et par begrensninger til boken.
- Kan bare bla 6 sider
- Gjennomsiktige sider
- Lukke igjen boken
- Automatisk generering av innhold
1 - 6 siders blaing
Taktikken som er brukt for å løse bla-problemet, er rimelig lett å utvide til at man kan bla seg gjennom boken. Noe av grunnen til at jeg stopper her, er at tiden begynner å bli knapp. Har ikke flere sider å vise heller... Jeg ser det som viktigst å ha et produkt som er ferdig, i motsetning til å ha et halvferdig program, med en metode som ikke er skikkelig implementert. Dette er en utfordring som jeg gjerne vil løse når jeg får tid.
2 - Gjennomsiktige sider
Jeg har hatt et problem med å legge to teksturer på samme bezierflate. Dette problemet har jeg ikke fått løst, og jeg har valgt å ikke bruke samme teknikk på sidene som på permen. Å lage doble sider ville blitt veldig tungvint, og unødvendig. Det må finnes en løsning på problemet annet enn å lage doble sider.
3 - Lukke igjen boken
Dette er også en funksjon som jeg gjerne ville hatt med. Logikken for å lukke boken, blir mer eller mindre lik som for å åpne boken. For å en fullstendig bok, burde dette vært implementert
4 - Automatisk generering av innhold
En funksjon som kunne vært interessant å legge til var muligheten for å spesifisere størrelsen og innholdet på boken i runtime. F.eks gjennom at du har en katalog med bilder som du gjerne vil ha som innhold i boken.