                          - SEPTIC'S DEMOSKOLA -

                                Lektion 13
                                ¯¯¯¯¯¯¯¯¯¯
                        Skriven av Vicious / Septic

                                11 Mars 94



                                 Inledning
                                 ¯¯¯¯¯¯¯¯¯
Hejsan igen! Nu kan ni snart det mesta inom demo-programmering, men vi har
några saker kvar att gå igenom. I den här lektionen ska vi behandla det
sista inom vektor-blocket, nämligen 2d-klippning. Visserligen finns det en
hel hög med saker man skulle kunna gå in på, så som inkonvexa, komplexa
vektorer, 3d-klippning, glenz/RGB/shadow-vektorer osv. men de är egentligen
ganska enkla när man väl behärskar de grundläggande vektor-operationerna
som jag skrivit om i skolan.
  Däremot har vi lite intressanta saker kvar som sfärer och bitmap-
rotation/zoom att ta upp, så håll er till tåls! Nu är det 2d-klippning som
gäller...

                                   Teori
                                   ¯¯¯¯¯
Att klippa tvådimensionellt är - som namnet förtäljer - ett sätt att
begränsa vektorobjektets tvådimensionella bild till ett fönster med
speciella koordinater. Detta är främst användbart för att kunna låta ett
objekt glida in på skärmen från en kant, eller för att ha ett objekt som är
mycket större än skärmen, utan att skärmen behöver vara jättestor, för att
förhindra att vektorn ritas utanför skärmen (och förstör annan viktig
information i minnet).
  Detta åstadkommer man genom att klippa VARJE ny linje som ritas ut i en
polygon. Man måste även ta hänsyn till hur blitterns fyllfunktion fungerar,
och lägga till extra linjer där det behövs för att fyllningen ska bli
korrekt (förutsatt att man ska använda fyllda vektorer, givetvis...)
  2d-klippning utförs precis innan linjen ska ritas ut på skärmen, alltså
efter all rotation och 3d->2d-konvertering, och klipp-rutinen ska kunna
behandla alla möjliga fall som kan uppkomma. T.ex kan linjen HELT plockas
bort, delvis klippas en eller två gånger, eller inte klippas alls.
  Här är alla möjliga klippsituationer som kan förekomma:

     ____________________________________
    |                                    |
    |                                    | <-Skärm
    |                                    |
    |            /                       |     \
    |           /                        |      \
    |         A/                         |       \D
    |         /                          |        \
    |        /                           | /       \
    |       /                            |/C
    |                                    /
    |          \                        /|
    |___________\______________________/_|
                B\                    /
                  \                  /
                   \

Linje A ligger helt och hållet inom skärmens gränser och skall inte klippas
alls. Linje B ligger delvis utanför skärmen, alltså ska vi klippa den, och
samma sak gäller linjen C, fast med skillnaden att den bryter gränserna TVÅ
gånger. Till sist, linjen D, skall plockas bort, eftersom den inte är
synlig på något ställe på skärmen.
  Efter klippning ska vi alltså få följande skärmutseende:

     ____________________________________
    |                                    |
    |                                    | <-Skärm
    |                                    |
    |            /                       |
    |           /                        |
    |         A/                         |
    |         /                          |
    |        /                           |
    |       /                            |
    |                                    |
    |          \B                      C/|
    |___________\______________________/_|

När vi ska klippa fyllda polygoner måste vi som sagt ta hänsyn till
blitterns fyllnings-krav, och med det menar jag att ytan måste vara helt
sluten för att fyllas korrekt. Om då vissa delar av den ligger utanför
skärmen måste man automatiskt knyta ihop ställena där klippning ägde rum.
  Titta på följande bild för att förstå lättare:

     ____________________________________
    |                                    |
    |                                    | <-Skärm
    |                                    |
    |                                ____|___
    |                               /    |F  \ <-Polygon
    |                             A/     |    \E
    |                             /      |     \
    |                             \      |     /
    |                              \     |    /
    |                              B\    |   /D
    |                                \___|__/
    |____________________________________|C

Om vi ska klippa ovanstående polygon måste vi göra det på följande sätt:

1.Rita ut linje A (utan klippning eftersom den ligger inom skärmgränserna)
2.Rita ut linje B (också utan klippning...)
3.Rita ut linje C, och klipp linjen. Kom ihåg klipp-koordinat.
4.Linje D ligger utanför, och därför ritar vi inte ut den.
5.Samma sak gäller linje E.
6.Linje F klipps som linje C, men istället för att komma ihåg en ny
klipp-koordinat, märker vi att det redan finns en gammal klipp-koordinat
lagrad. Alltså ritar vi ut en linje mellan dessa två koordinater och rensar
sedan den lagrade klipp-koordinaten.

Om vi har ett annorlunda fall, där en del av polygonen går utanför skärmen
både i understa kanten och den högra gör vi på ungefär samma sätt...Vi
erinrar oss bara att blittern endast behöver extra linjer i höger och
vänsterkanten (eftersom den fyller mellan punkter från höger till vänster).

     ____________________________________
    |                                    |
    |                                    | <-Skärm
    |                                    |
    |                                    |
    |                                    |
    |                                    |
    |                                  __|__
    |                                 /  |F \
    |                               A/   |   \E
    |                               /    |    \
    |                               \    |     \
    |________________________________\___|     /
                                     B\       /D
                                       \_____/
                                          C
1.Linje A ritas ut utan klippning.
2.Linje B klipps och klipp-koordinaten lagras. Eftersom vi bara ska rita ut
linjer i höger/vänster kant ser vi nu att vi inte behöver X-koordinaten.
Y-koordinaten räcker alltså.
3.Linje C, D och E ritas inte ut.
4.Linje F klipps och vi ser att vi har en klipp-koordinat lagrad. Alltså
ritar vi ut en extra linje från F, rakt nedåt i skärmkanten till den
understa kanten.
  Till sist, för att behandla det fall då en linje skär en kant två gånger
gör man så att man klipper för den ena kanten först, och sen den andra
kanten. På så sätt delar man upp det hela och det blir inte svårare än en
normal "en-skärnings"-klippning.

Detta kan säkert verka lite svårt och obegripbart till en början, men jag
tror ni kommer förstå allt snart.
  Okej, nu till den verkliga frågan: Hur GÖR man för att klippa en linje?

Jo, det är inte alls så svårt. Det bygger helt enkelt på lite likformighet
igen. Vi tittar på ett klippningsfall:

              Klippgräns
             |
             |
             |  x1;y1
             |  /<-Linje
             | /
             |/
             |
            /|
           / |
          /  |
         /   |
     x2;y2   |

Vi känner till linjens start och slutkoordinat, dvs x1;y1 och x2;y2, och vi
känner även till klippgränsen eftersom vi satt den själv.
  Vi har fått två trianglar som är likformiga och där endast en längd är
okänd:

              Klippgräns
             |
             |
             |        _
             |  /|     |                     a - Stora triangelns höjd
             | / |     |                     b - Store triangelns bredd
             |/  |     |                     c - Lilla triangelns höjd
             |   |     |- a=y2-y1            d - Lilla triangelns bredd
            /|   |     |
           / |c  |     |
          /  |   |     |
         /___|___|    _|
             |        

         |___|
           |
           d=Klippgräns-x2
         |_______|
             |
             b=x2-x1

Vi vet alltså a,b och d, men inte c. Och vad anger c? Jo, den anger alltså
hur långt ifrån y2 som det nya y1 ska ligga. Vi har alltså skapat nya
värden för x1;y1, där x1=Klippgränsen och y1=y2-c.
  Värdet på c bestäms genom likformighet:

   a   c
   - = -
   b   d

       a*d
   c = ---
        b

Den här formeln kan givetvis även användas vid klippning mot de övriga
kanterna också! Koordinaterna behöver endast bytas ut mot de aktuella
istället. Hur det ska göras kan ni se i programexemplet...

Tja, det var allt jag tänkte förklara om klippning. Tänk på att det inte
BARA behöver användas för att klippa i skärmens utkanter. Man kan t.ex göra
snygga saker genom att klippa i ett litet fönster på skärmen samtidigt som
andra saker förekommer där. Det blir alltså enklare att designa något
häftigt om man kan klippa vektorerna...


                                 Register
                                 ¯¯¯¯¯¯¯¯
Inga nya register i den här lektionen. Klippningen får skötas helt och
hållet av processorn.


                              Programexemplet
                              ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Dagens progamexempel innehåller, inte helt oväntat, en vektorkub som klipps
i skärmkanterna!
  Som ni ser klipper jag en bit på skärmen för att ni ska se hur
klippningen går till. I vanliga fall brukar man klippa i skärmens utkant
och då syns det ju inte.
  Tja, det enda nya är egentligen klipp-rutinen (konstigt nog :-) och den
heter Clip2d. Vi anropar den precis innan vi ska rita ut vår rutin,
eftersom den modifierar linjens start och/eller slutpunkt så att linjen
klipps till de gränser vi satt. Den kan också lämna en returkod på -6000
och då betyder det att linjen ligger helt utanför skärmen och inte ska
ritas ut alls.
  Okej, jag kan gå igenom vad rutinen gör lite mer steg för steg kanske...
Först och främst lagrar jag linjens färgvärde för att vi kan behöva det när
vi ritar ut en extra linje.
  Sedan gör jag en liten speciell sak som kanske inte är så självklar.
Genom att försäkra mig om att X1 är mindre än X2 räcker det sedan att testa
om X1 ska klippas mot vänsterkanten eller om X2 ska klippas mot höger-
kanten! Man behöver alltså inte testa båda värdena mot båda kanterna med
den här metoden. Antingen ligger ju bara X1 till vänster om klippgränsen
eller också så ligger de båda utanför och då ska linjen inte ritas ut alls.
  Om nu X1 bara ligger utanför då, så kommer vi en bit längre ner i koden,
till labeln .CutLeft.
  Här gör jag diverse beräkningar enligt formeln ovan, för att slutligen få
fram den nya Y koordinaten i den förkortade linjen. Jag lagrar även det här
värdet om det inte redan finns ett gammalt värde. Finns det ett gammalt
värde lagrat så betyder det att vi klippt en gång och alltså vandrat iväg
utanför skärmen med vår polygon. Nu när vi kommer tillbaka in måste
polygonen bindas ihop med en extra linje, och därför hoppar jag till
rutinen XtraLineLeft.
  De andra klippningarna går till på ungefär samma sätt, och notera även
att jag gör samma koll med Y1 och Y2 som jag i början gjorde med X1 och X2,
allt för att slippa att kolla dubbelt.
  Om vi kikar på rutinen XtraLineLeft nu då, så upptäcker ni att det inte
är några egentliga konstigheter här. Jag bara kollar så att den extra
linjen hamnar innanför gränserna och sedan ritar jag ut den i rätt bitplan.
  Nere bland variablerna hittar vi ClipLeft, ClipRight, ClipUp och
ClipDown. Dessa är, som namnen avslöjar, klipp-koordinaterna som bestämmer
fönstrets storlek. Testa gärna att ändra dem, men akta er för att sätta dem
större än skärmens storlek, eftersom det säkerligen leder till att datorn
kraschar.
  Sådär, svårare var det inte!
