Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Assembleur - Les ellipes et les cercles
(Article écrit par Thomas Landspurg et extrait d'Amiga News Tech - février 1992)
|
|
Aujourd'hui, je vais vous parler d'un sujet qui me tient à coeur, les ellipses et les cercles,
ces derniers n'étant rien d'autre qu'un cas particulier des premières.
Eh oui, je vais vous expliquer comment afficher de magnifiques ellipses à l'écran, et ce de manière extrêmement
rapide. Hélas, petite contrainte : nos ellipses seront parallèles aux axes ! Pour mieux comprendre, regardez le
schéma 1 ci-dessous, vous y trouverez toutes les informations nécessaires.
Une ellipse est donc définie par son centre (xc,yc) et ses rayons a et b. Si a=b on obtient un cercle.
Cette ellipse est définie mathématiquement par l'équation : x/a+y/b=1.
Une fois obtenus x et y vérifiant cette équation, il ne reste plus qu'à additionner les
coordonnées du centre xc et yc à l'ensemble des coordonnées des points.
Pour tracer notre ellipse, nous allons dessiner un point par ligne d'écran et ses symétriques,
puis remplir le tout à l'aide du Blitter en mode remplissage. Il y a en effet deux symétries dans
une ellipse de ce type : une horizontale et une verticale. Il suffit donc de calculer un seul quart de
l'ellipse puis d'afficher les autres points par symétrie.
Nous partons du centre de l'ellipse, du point de coordonnées (x=a,y=0), qui vérifie évidemment l'équation
de l'ellipse. Puis nous cherchons le point suivant d'abscisse x1=x+delta et d'ordonnée y1=y+1.
Tout le problème est de calculer delta. La démonstration est un peu longue pour tenir dans la place qui
m'est impartie, mais on arrive au résultat suivant.
Pour calculer delta, on effectue la somme (2*y-1)*a*a/b*b puis on ajoute -2*x+1, en décrémentant x
à chaque itération jusqu'à ce que cette somme devienne négative. Je m'explique un peu : cette valeur delta est
représentative de la différence entre l'ellipse affichée et l'ellipse théorique. Si delta est positif, cela
veut dire que le point (x,y) est à l'extérieur de la surface de l'ellipse, et si delta est négatif ce
point est à l'intérieur de l'ellipse. Si enfin, delta est nul, alors (x,y) est sur l'ellipse. Plus delta est
petit, plus le point de coordonnées (x,y) est proche de l'ellipse. C'est vrai, ce n'est pas évident à comprendre,
mais une ellipse est un peu plus compliquée qu'une droite !
Nous avons donc le point (x,y) que nous affichons, ainsi que ses symétriques. Une fois que cela est fait pour
tous les points de l'ellipse, il ne reste plus qu'à donner un petit coup de Blitter pour remplir le tout.
Quelques remarques : il n'y a pas de détourage (clipping), c'est-à-dire que si l'ellipse vient à sortir de
l'écran, elle risque fort d'aller embêter la mémoire des petits copains ! De plus, ce listing trace une ellipse
pleine. Pour ne dessiner que le contour, il suffit d'enlever le remplissage au Blitter et d'afficher un point à
chaque itération du calcul de delta.
Le listing ci-dessous est l'application de cet algorithme. Il est bien entendu sous Devpac, car je pense que toute
personne sensée à définitivement cessé d'utiliser Seka. Vous pouvez changer les paramètres A et B (rayons de l'ellipse)
en manipulant la souris et déplacer le centre de l'ellipse à l'aide de la manette. Cette dernière opération
est à utiliser avec modération pour ne pas faire sortir l'ellipse de l'écran.
Voilà, j'espère que tout le monde n'a pas abandonné la lecture ! La prochaine fois, j'essaierai de trouver quelque chose de
plus simple.
; *******************************
; * Ellipse.s *
; * Auteur: Thomas LANDSPURG *
; * *
; * A assembler avec Devpac 2 *
; *******************************
OPT O1+,O2+,W-,A-,C-
NB_BYTE_LIGNE=40 ; Taille sur x de l'ecran en octets
NB_LIGNE=200 ; Hauteur de l'ecran en lignes
prof=1 ; profondeur de l'ecran
CUSTOM=$dff000
incdir "include:"
include exec/exec_lib.i
include graphics/graphics_lib.i
include hardware/custom.i
; DEBUT DU PROGRAMME
start:
move.l #graphname,a1 ; ouverture de la graphics library
CALLEXEC OpenLibrary
move.l d0,_GfxBase
move.l d0,a0
move.l $32(a0),oldcop ; Ancienne copperlist
bsr allocscr
tst.l abitplane0 ; en cas d'erreur memoire,
beq.s error_mem ; on sort
CALLGRAF OwnBlitter
CALLEXEC Forbid
move.b #%10000111,$bfd100 ; Arrete le drive (BEURK!)
lea CUSTOM,a5
move.w #$20,dmacon(a5) ; Desactive DMA sprites
bsr main_pg ; pg principal
out:
lea CUSTOM,a5
move.w #$8020,dmacon(a5); reactive DMA sprites
CALLEXEC Permit ; reautorise le multitache
CALLGRAF DisownBlitter ; et l'utilisation du blitter
bsr freescr ; liberation de l'ecran
move.l _GfxBase(pc),a0 ; restore la copperlist
move.l oldcop,$32(a0)
move.l $26(a0),CUSTOM+cop1lc
error_mem:
move.l _GfxBase(pc),a1
CALLEXEC CloseLibrary ; referme les librairies
moveq.l #0,d0
rts
; ****************
; * PG PRINCIPAL *
; ****************
main_pg:
bsr sync ; attente de la synchro
bsr switch ; switching de page
bsr bltclear ; effacement de l'ecran
bsr waitblit
lea CUSTOM,a5
move.w joy0dat(a5),d4 ; recupere coord de la souris
move.w d4,d5
lsr.w #8,d5
and.w #$7f,d4
move.w d4,rayona ; A de l'ellipse
lsr.w #2,d5
move.w d5,rayonb ; B de l'ellipse
bsr joyst
bsr draw_ellipse ; affichage de l'ellipse
btst #6,$bfe001 ; test du bouton droit de la souris
bne.s main_pg
rts
; switching de page
switch:
move.l abitplane0(pc),a5
move.l bbitplane0(pc),a4
tst.b flagcop ; gestion switching de page
bne copb
exg a5,a4 ; on inverse les deux tampons
copb:
not.b flagcop
move.l a4,bitplane0 ; tampon de travail
bsr initbrush ; on change la copperlist
move.l _GfxBase(pc),a0 ; on reaffiche la copperlist
move.l #mycop,$32(a0)
lea CUSTOM,a5
move.l #mycop,cop1lc(a5)
clr.w copjmp1(a5)
rts
; ATTENTE DE LA SYNCHRO
sync:
btst #0,vposr+1(a5)
beq sync
sync2:
cmp.b #20,vhposr(a5)
bne sync2
rts
; JOYSTICK
joyst:
move.w joy1dat(a5),d0
btst #1,d0
beq pasdroite
add.w #1,circlex
pasdroite:btst #9,d0
beq pasgauche
sub.w #1,circlex
pasgauche:
move.w d0,d1
asr.w #1,d1
eor.w d1,d0
btst #8,d0
beq pashaut
add.w #1,circley
pashaut:btst #0,d0
beq pasbas
sub.w #1,circley
pasbas:
out_joy:
rts
; Allocation memoire pour les tampons ecran
allocscr:
move.l #taillescr*2,d0 ; 2 ecrans car double tampon memoire
move.l #$10002,d1
CALLEXEC AllocMem
move.l d0,abitplane0 ; premier tampon
move.l d0,bitplane0
add.l #taillescr,d0
move.l d0,bbitplane0 ; deuxieme tampon
rts
; desallocation memoire des tampons
freescr:
move.l abitplane0(pc),a1
move.l #taillescr*2,d0
CALLEXEC FreeMem
rts
; MET DANS LA LISTE COPPER LES ADRESSES DES PLANS DE BITS
initbrush: ; d0 contient l'adresse du prem plan
lea p_bitpl,a0
move.w #$00e0,d1 ; adresses de bitp0h
move.w #prof-1,d2
ibrush:
move.l a5,d0
bsr metadr
bsr metadr
lea NB_BYTE_LIGNE(a5),a5
dbf d2,ibrush
rts
metadr: ; cet ici qu'on le met dans la coplist
swap d0
move.w d1,(a0)+
move.w d0,(a0)+
addq.w #2,d1
rts
; Le coeur du programme: la routine de trace d'ellipses
; utilise les variables rayona,rayonb,circlex et circley
; ainsi que plan de bits 0
;
draw_ellipse:
move.w rayona(pc),d6
and.w #$ff,d6 ; Pour eviter les ellipses trop grandes
beq out_ellipse ; si A=0 on sort
move.w rayonb(pc),d7
and.w #$ff,d7 ; pour les ellipses trop grandes!
beq out_ellipse ; si B=0 on sort
clr.l d4
move.w d6,d4 ; d4=A
lsl.l #8,d4
divu d7,d4 ; d4=A/B
mulu d4,d4 ; d4=(A/B)carre
move.l d4,a0 ; d4=alpha
neg.l d4
add.l a0,a0
clr.l d0
move.w d6,d0 ; d0=a=x
move.l d0,d1
add.w d1,d1 ; d1=delta=2*x-1
swap d1
move.l bitplane0(pc),a1; pointeur sur le tampon ecran
move.w circley(pc),d2 ; d2 contiendra la ligne haute
move.w d2,d5
move.w d2,d3 ; d3 la ligne basse
subq.w #1,d3
mulu #NB_BYTE_LIGNE,d5; d5 contient le decalage en octets
add.l d5,a1 ; a1 pointeur sur la ligne du bas
move.l a1,a2 ; a2 pointeur sur la ligne du haut
lea -40(a1),a1
subq.w #1,d7 ; a cause du dbf!
move.w circlex(pc),a5 ; a5 contient la coord x du centre0
loop_ellipse2:
add.l a0,d4 ; alpha=alpha+2*(a*a)/(b*b)
sub.l d4,d1 ; delta=delta-alpha
bpl.s delta_pos2 ; delta>0 on affiche le point
swap d1 ; sinon on itere
delta_neg2:
subq.w #$1,d0 ; x=x-1
bmi.s out_ellipse_before2 ; x<0 ->fin de l'ellipse
add.w d0,d1 ; \
add.w d0,d1 ;--delta+2*x
subq.w #$1,d1 ; delta+2*x-1
bmi.s delta_neg2 ; tant que delta<0
swap d1 ; d1=d1*65536
delta_pos2:
move.w a5,d5 ; d5=centrex
sub.w d0,d5 ; d5=centrex-x
move.w d5,d6 ; d6 contient cette position
lsr.w #3,d6 ; on se prepare a l'affichage
and.b #$7,d5 ; de ce point
move.b #$80,d3
lsr.b d5,d3
eor.b d3,(a1,d6.w) ; on l'affiche en haut a gauche
eor.b d3,(a2,d6.w) ; et son symetrique en bas a gauche
move.w a5,d5 ; d5=centrex
add.w d0,d5 ; d5=centrex+x
move.w d5,d6 ; d6 contient cette position
lsr.w #3,d6 ; on se prepare la aussi
and.b #$7,d5 ; a l'affichage de ce point
move.b #$80,d3
lsr.b d5,d3
eor.b d3,(a1,d6.w) ; on l'affiche en haut a droite
eor.b d3,(a2,d6.w) ; et en bas a droite
lea -40(a1),a1 ; prochaine ligne haut
lea 40(a2),a2 ; prochaine ligne bas
dbf d7,loop_ellipse2; tant qu'il y aura des lignes...
out_ellipse_before2:
bsr blit_circle ; un petit coup de blitter
out_ellipse:
rts
; Remplissage au blitter
; on remplit tout l'ecran, meme si ce
; n'est pas necessaire
blit_circle:
move.l bitplane0(pc),a0; pointeur sur l'ecran
; on se place a la fin de l'ecran,
; car le remplissage se fait en mode
; 'descendant'
add.l #NB_LIGNE*prof*NB_BYTE_LIGNE,a0
lea CUSTOM,a5
bsr waitblit
move.l a0,bltdpt(a5) ; source
move.l a0,bltapt(a5) ; destination
move.l #-1,bltafwm(a5) ; masque
move.w #$09f0,bltcon0(a5) ; D=A
move.w #$000a,bltcon1(a5) ; mode remplissage et descente
clr.w bltdmod(a5) ; modulo=0
clr.w bltamod(a5) ;
move.w #NB_LIGNE*prof<<6+NB_BYTE_LIGNE/2,bltsize(a5)
; Recopie
rts
rayona: dc.w 100
rayonb: dc.w 80
circlex:dc.w 160
circley:dc.w 100
; EFFACEMENT AU BLITTER D'UN BITMAP
bltclear:
bsr waitblit
move.l bitplane0(pc),bltdpt(a5)
move.w #$01f0,bltcon0(a5) ; D=A en desactivant A
clr.w bltcon1(a5)
clr.w bltadat(a5) ; donnee=0
clr.w bltdmod(a5) ; modulo=0
move.w #NB_LIGNE*prof<<6+NB_BYTE_LIGNE/2,bltsize(a5)
rts
waitblit:
btst #14,dmaconr(a5)
bne.s waitblit
rts
graphname:GRAFNAME
even
_GfxBase:dcb.l 1
oldcop: dc.l 0
bitplane0:dcb.l 1
abitplane0:dcb.l 1
bbitplane0:dcb.l 1
flagcop:dc.b 0
tailleplan=NB_LIGNE*40
taillescr=tailleplan*prof
section coplist,DATA_C
mycop:
dc.w $1081,$fffe
dc.w $180,0,$182,$ff
dc.w $008e,$2581
dc.w $0090,$40c1
dc.w $0092,$0038
dc.w $0094,$00cc
dc.w $102,0
dc.w $108,(prof-1)*40
dc.w $10a,(prof-1)*40
dc.w $009c,$8010
dc.w $3101,$fffe
dc.w $0100,prof<<12
p_bitpl
dcb.w prof*4,0
dc.l $f0dffffe
dc.l $01000000
dc.l $fffffffe
fincop4:
|
|