Obligement - L'Amiga au maximum

Vendredi 24 mai 2019 - 06:03  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

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


Trucs et astuces

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


Glossaire

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


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


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.

Assembleur

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 buffers
copb:
	not.b	flagcop	
	move.l	a4,bitplane0		; buffer 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 buffers ecran
allocscr:
	move.l	#taillescr*2,d0	; 2 ecrans car double buffering
	move.l	#$10002,d1
	CALLEXEC AllocMem
	move.l	d0,abitplane0	; premier buffer
	move.l	d0,bitplane0
	add.l	#taillescr,d0
	move.l	d0,bbitplane0	; deuxieme buffer
	rts
	; desallocation memoire des buffers
freescr:
	move.l	abitplane0(pc),a1
	move.l	#taillescr*2,d0
	CALLEXEC FreeMem
	rts

	; MET DANS LA COPPER LIST LES ADRESSES DES BITPLANES

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 bitplane 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 buffer 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 l'offset 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:


[Retour en haut] / [Retour aux articles]