(*------------------------------------------------------------------------*) MODULE FaceBender; (* 10.01.89 V 1.1 *) (*------------------------------------------------------------------------*) (* *) (* Versione 1.0 per Amiga-Modula2 17.09.88 *) (* *) (* Aggiornamenti : 10.01.89 V 1.1 *) (* Il programma puo' essere ora richiamato da *) (* Workbench. *) (* *) (* Autore Originale : Giovanni Michelon, AmigaMagazine *) (* Ringraziamenti : Paolo Fontanot, che ha progettato e realizzato *) (* in Pascal la struttura dell'editor facciale *) (* a linee. *) (* Adriano De Minicis per la realizzazione del *) (* modulo Startup. *) (* *) (*------------------------------------------------------------------------*) (* *) (* Il programma FacBender e' suggerito nel numero 220 di Le Scienze, si *) (* faccia pertanto riferimento a tale fonte per maggiori delucidazioni. *) (* *) (*------------------------------------------------------------------------*) FROM SYSTEM IMPORT NULL; FROM Text IMPORT Text; FROM Pens IMPORT SetAPen,SetBPen,Move,Draw,SetDrMd; FROM GraphicsLibrary IMPORT DrawingModes,DrawingModeSet; FROM Intuition IMPORT IDCMPFlagSet,IDCMPFlags,ItemFlags,SelectUp, SelectDown,IntuiMessagePtr; FROM IntuiUtils IMPORT MenuNum,ItemNum,SubNum; FROM Ports IMPORT MessagePtr,GetMsg,ReplyMsg; FROM Tasks IMPORT SignalSet,SIGNAL,Wait; FROM MathLib0 IMPORT real,entier; FROM Windows IMPORT ModifyIDCMP; FROM InOut IMPORT OpenInputOutputFile,CloseInputOutput, WriteString,WriteLn,ReadCard,ReadInt,Read; FROM RealInOut IMPORT ReadReal; FROM Startup IMPORT WBStart,WBEnd; FROM BSpline IMPORT OpenedBSplineDraw; FROM FaceBenUtils IMPORT WinPtr,SA,MySubItem, SetWindow,SetMenu,RectShape,ClearShape, DrawWindow,EndProgram,SaveFace,LoadFace; (*------------------------------------------------------------------------*) CONST NE = 39; (* numero elementi volto *) NP = 186; (* numero punti volto *) TYPE String = ARRAY [0..79] OF CHAR; Point = RECORD x:INTEGER; (* coordinate punto volto *) y:INTEGER; END; Piece = RECORD l :INTEGER; (* numero punti elemento volto *) s :INTEGER; (* indice primo punto elemento in Face *) e :INTEGER; (* indice ultimo punto elemento in Face *) END; Base = ARRAY [1..NE] OF Piece; (* contiene struttura volto *) Face = ARRAY [1..NP] OF Point; (* insieme punti volto *) ProcDraw = PROCEDURE(CARDINAL,INTEGER,INTEGER); VAR base :Base ; (* dati forma volto *) MemFaces :ARRAY [1..4] OF Face; (* dati faccie 1..4 *) xc,yc :INTEGER; (* coordinate cursore *) key :BOOLEAN; (* tasto conferma *) show :BOOLEAN; Xo,Yo :INTEGER; (* origine riquadro *) Fx,Fy :REAL ; (* fattori di scala *) MsgPtr :IntuiMessagePtr; DrawPieceAs :ProcDraw; SelectedShape :CARDINAL; (* riquadro selezionato 1..4 *) (*------------------------------------------------------------------------*) PROCEDURE InitBase; (* prepara la base con i dati dei pezzi del volto *) VAR i,n :INTEGER; BEGIN base[ 1].l:=1; base[20].l:=4; base[ 2].l:=1; base[21].l:=7; base[ 3].l:=5; base[22].l:=7; base[ 4].l:=5; base[23].l:=7; base[ 5].l:=3; base[24].l:=7; base[ 6].l:=3; base[25].l:=3; base[ 7].l:=3; base[26].l:=3; base[ 8].l:=3; base[27].l:=7; base[ 9].l:=3; base[28].l:=7; base[10].l:=3; base[29].l:=11; base[11].l:=3; base[30].l:=13; base[12].l:=3; base[31].l:=13; base[13].l:=6; base[32].l:=3; base[14].l:=6; base[33].l:=3; base[15].l:=6; base[34].l:=3; base[16].l:=6; base[35].l:=3; base[17].l:=6; base[36].l:=2; base[18].l:=6; base[37].l:=2; base[19].l:=4; base[38].l:=2; base[39].l:=3; i:=1; FOR n:=1 TO NE DO base[n].s:=i; i:=i+base[n].l; base[n].e:=i-1 END; show:=TRUE; END InitBase; (*------------------------------------------------------------------------*) PROCEDURE SetDefaultFace; (* definisce i valori del volto medio, default *) VAR a :ARRAY [0..18],[0..59] OF CHAR; ch :CHAR; i,j,k,n,c:INTEGER; BEGIN a[ 0]:='130102185102129098123101128106135101130098185098179101184106'; a[ 1]:='191101185098114104128097142103172104185098198104116104128107'; a[ 2]:='142103172104186107196105113100127094143099171100186094199100'; a[ 3]:='122111130110139107173108182111191111151097151110151122149129'; a[ 4]:='151136156139161097161110161123163129162136156139145126142130'; a[ 5]:='141135143139148136156139168126171129172135169139165136158139'; a[ 6]:='107094108089120084134085145088147093166093168089181086194085'; a[ 7]:='203089206094107095119089133091147093166093182091195089205094'; a[ 8]:='132160144156151153157156163154172156182159133160143160151159'; a[ 9]:='158160165159173160181159133160144160151159158160165159172159'; a[10]:='181160136161143164150167158168166167174164180160098098096117'; a[11]:='099138214097217116213136094107087101083106086117089131094144'; a[12]:='099141219106226101229108227117225130219142214141099138103156'; a[13]:='110171124185142197157200175196191185202172210156214135096101'; a[14]:='102086109071115061126052141049155050169052183053196060205071'; a[15]:='212083217100088161073130071099077058094027124003153001183002'; a[16]:='212021231051240091245125228157140132134139130147173133180140'; a[17]:='185148100135104141107147213135209140206146154143154150160143'; a[18]:='160150157189157195148175157173168176'; i:=0; j:=0; FOR n:=1 TO NP DO c:=0; FOR k:=0 TO 2 DO IF j=60 THEN j:=0; INC(i); END; ch:=a[i][j]; c:=c*10+VAL(INTEGER,ORD(ch)-48); INC(j); END; MemFaces[1][n].x:=c; c:=0; FOR k:=0 TO 2 DO IF j=60 THEN j:=0; INC(i); END; ch:=a[i][j]; c:=c*10+VAL(INTEGER,ORD(ch)-48); INC(j); END; MemFaces[1][n].y:=c; END; MemFaces[2]:=MemFaces[1]; MemFaces[3]:=MemFaces[1]; MemFaces[4]:=MemFaces[1]; END SetDefaultFace; (*------------------------------------------------------------------------*) PROCEDURE SetScale(NumShape:CARDINAL); (* setta le scala e l'origine per i riquadri, si veda Pset e Line *) BEGIN Fx:=150./316.; Fy:=98./200.; CASE NumShape OF 0 : Xo:=0; Yo:=0; Fx:=1.; Fy:=1.; | 1 : Xo:=320; Yo:=0; | 2 : Xo:=478; Yo:=0; | 3 : Xo:=320; Yo:=102; | 4 : Xo:=478; Yo:=102; END; END SetScale; (*------------------------------------------------------------------------*) PROCEDURE Pset(X,Y,Color:INTEGER); (* disegna un punto tenendo conto dei fattori *) (* di scala e origine dei singoli riquadri *) BEGIN SetDrMd(SA,DrawingModeSet{}); IF Color>4 THEN Color:=Color-4; SetDrMd(SA,DrawingModeSet{Complement}); END; SetAPen(SA,Color); X:=entier(real(X)*Fx)+Xo; Y:=entier(real(Y)*Fy)+Yo; Move(SA,X,Y); Draw(SA,X,Y); SetDrMd(SA,DrawingModeSet{}); END Pset; (*------------------------------------------------------------------------*) PROCEDURE Line(X1,Y1,X2,Y2,Color:INTEGER); (* disegna una linea tenendo conto dei fattori *) (* di scala e origine dei singoli riquadri *) BEGIN SetDrMd(SA,DrawingModeSet{}); IF Color>4 THEN Color:=Color-4; SetDrMd(SA,DrawingModeSet{Complement}); END; SetAPen(SA,Color); X1:=entier(real(X1)*Fx)+Xo; Y1:=entier(real(Y1)*Fy)+Yo; X2:=entier(real(X2)*Fx)+Xo; Y2:=entier(real(Y2)*Fy)+Yo; Move(SA,X1,Y1); Draw(SA,X2,Y2); SetDrMd(SA,DrawingModeSet{}); END Line; (*------------------------------------------------------------------------*) PROCEDURE DrawPieceLine(NumShape:CARDINAL; n,color:INTEGER); (* disegna il tratto n della faccia NumShape con linee *) VAR i,s,e :INTEGER; x0,y0,x1,y1 :INTEGER; BEGIN s:=base[n].s; e:=base[n].e; x0:=MemFaces[NumShape][s].x; y0:=MemFaces[NumShape][s].y; FOR i:=s+1 TO e DO x1:=MemFaces[NumShape][i].x; y1:=MemFaces[NumShape][i].y; Line(x0,y0,x1,y1,color); IF show THEN Pset(x0,y0,3); END; x0:=x1; y0:=y1; END; IF show THEN Pset(x0,y0,3); END; END DrawPieceLine; (*------------------------------------------------------------------------*) PROCEDURE DrawPieceBSpline(NumShape:CARDINAL; n,color:INTEGER); (* disegna il tratto n della faccia NumShape con BSpline *) TYPE CardPoint = RECORD x,y :CARDINAL; END; VAR i,s,e :INTEGER; x,y :REAL; Element :ARRAY [0..12] OF CardPoint; BEGIN IF base[n].l<3 THEN DrawPieceLine(NumShape,n,color); ELSE s:=base[n].s; e:=base[n].e; FOR i:=s TO e DO x:=real(MemFaces[NumShape][i].x); y:=real(MemFaces[NumShape][i].y); Element[i-s].x:=VAL(CARDINAL,entier(x*Fx)+Xo); Element[i-s].y:=VAL(CARDINAL,entier(y*Fy)+Yo); END; SetDrMd(SA,DrawingModeSet{}); IF color>4 THEN color:=color-4; SetDrMd(SA,DrawingModeSet{Complement}); END; SetAPen(SA,color); OpenedBSplineDraw(SA,base[n].l,Element); IF show THEN SetAPen(SA,3); FOR i:=0 TO e-s DO Move(SA,Element[i].x,Element[i].y); Draw(SA,Element[i].x,Element[i].y); END; END; SetDrMd(SA,DrawingModeSet{}); END; END DrawPieceBSpline; (*------------------------------------------------------------------------*) PROCEDURE DrawFace(NumShape:CARDINAL; color:INTEGER); (* disegna l' intero volto con la procedura giusta *) VAR i:INTEGER; BEGIN FOR i:=1 TO NE DO DrawPieceAs(NumShape,i,color); END; END DrawFace; (*------------------------------------------------------------------------*) PROCEDURE RedrawFace; (* ridisegna la faccia selezionata nel riquadro di lavoro *) BEGIN ClearShape(0); SetScale(0); DrawFace(SelectedShape,1); RectShape(0,3); ClearShape(SelectedShape); SetScale(SelectedShape); DrawFace(SelectedShape,1); RectShape(SelectedShape,2); END RedrawFace; (*------------------------------------------------------------------------*) PROCEDURE DrawPoint(f:CARDINAL; i,n,color:INTEGER); (* disegna il punto i appartenente al tratto n della faccia f *) VAR x,y:INTEGER; BEGIN x:=MemFaces[f][i].x; y:=MemFaces[f][i].y; IF i>base[n].s THEN Line(MemFaces[f][i-1].x,MemFaces[f][i-1].y,x,y,color); END; IF ii) OR (base[n].e Cancel'); WriteLn; WriteString('> '); ReadReal(f); IF NOT ( (ABS(f)>=1.) OR (f=0.) ) THEN FOR i:=1 TO NP DO x :=real(MemFaces[SS][i].x); y :=real(MemFaces[SS][i].y); xn:=real(MemFaces[4][i].x); yn:=real(MemFaces[4][i].y); MemFaces[SS][i].x:=entier(x+f*(x-xn)); MemFaces[SS][i].y:=entier(y+f*(y-yn)); END; RedrawFace; END; END; CloseInputOutput; END Caricature; (*------------------------------------------------------------------------*) PROCEDURE Trasform; (* trasforma il volto selezionato in un altro *) VAR f,x,y,xn,yn :REAL; i,n :INTEGER; SS,NS :CARDINAL; ch :CHAR; BEGIN SS:=SelectedShape; OpenInputOutputFile('CON:100/100/480/50/Trasform'); IF (SS # 4) THEN WriteString('Insert Number New Face and Number Step (0 => Cancel)'); WriteLn; WriteString('Face> '); ReadCard(NS); WriteString('Step> '); ReadInt(n); IF (NS # SS) AND (NS # 0) AND (n # 0 ) THEN f:=1./real(n); REPEAT FOR i:=1 TO NP DO x :=real(MemFaces[SS][i].x); y :=real(MemFaces[SS][i].y); xn:=real(MemFaces[NS][i].x); yn:=real(MemFaces[NS][i].y); MemFaces[SS][i].x:=entier(x+f*(xn-x)); MemFaces[SS][i].y:=entier(y+f*(yn-y)); END; RedrawFace; f:=f+1./real(n); WriteLn; WriteLn; WriteString("Type to continue, 'Q' AND to quit"); Read(ch); IF CAP(ch)='Q' THEN f:=2.; END; UNTIL f>1.; END; END; CloseInputOutput; END Trasform; (*------------------------------------------------------------------------*) PROCEDURE ExecMenu(Code:CARDINAL); VAR i :CARDINAL; BEGIN CASE MenuNum(Code) OF 0 : CASE ItemNum(Code) OF 0 : Move(SA,250,211); SetAPen(SA,1); Text(SA,'Type @ to Cancel',16); LoadFace(MemFaces[SelectedShape]); ClearShape(5); RectShape(5,1); RedrawFace; | 1 : Move(SA,250,211); SetAPen(SA,1); Text(SA,'Type @ to Cancel',16); SaveFace(MemFaces[SelectedShape]); ClearShape(5); RectShape(5,1); |ELSE END; | 1 : CASE ItemNum(Code) OF 0 : i:=SubNum(Code)+1; IF (i # SelectedShape) THEN ClearShape(i); MemFaces[i]:=MemFaces[SelectedShape]; SetScale(i); DrawFace(i,1); RectShape(i,3); END; | 1 : CASE SubNum(Code) OF 0 : EXCL(MySubItem[5].Flags,Checked); DrawPieceAs:=DrawPieceLine; RedrawFace; | 1 : EXCL(MySubItem[4].Flags,Checked); DrawPieceAs:=DrawPieceBSpline; RedrawFace; |ELSE END; |ELSE END; | 2 :CASE ItemNum(Code) OF 0 : Caricature; | 1 : Trasform; |ELSE END; |ELSE END; END ExecMenu; (*------------------------------------------------------------------------*) PROCEDURE GoProgram; VAR ss :SignalSet; Class :IDCMPFlagSet; Code :CARDINAL; i :CARDINAL; quit :BOOLEAN; BEGIN DrawPieceAs:=DrawPieceLine; SelectedShape:=1; SetScale(0); DrawFace(1,1); FOR i:=1 TO 4 DO SetScale(i); DrawFace(i,1); END; RectShape(SelectedShape,2); quit:=FALSE; REPEAT ss:=SignalSet{}; INCL(ss,SIGNAL(WinPtr^.UserPort^.mpSigBit)); ss:=Wait(ss); MsgPtr:=IntuiMessagePtr(GetMsg(WinPtr^.UserPort)); WHILE MsgPtr # NULL DO Class:=MsgPtr^.Class; Code:=MsgPtr^.Code; xc:=WinPtr^.GZZMouseX; yc:=WinPtr^.GZZMouseY; ReplyMsg(MessagePtr(MsgPtr)); IF (MouseButtons IN Class) AND (Code = SelectDown) THEN EditFace; ELSIF (MenuPick IN Class) THEN ExecMenu(Code); ELSIF (CloseWindowFlag IN Class) THEN quit:=TRUE; END; MsgPtr:=IntuiMessagePtr(GetMsg(WinPtr^.UserPort)); END; UNTIL quit; END GoProgram; (*------------------------------------------------------------------------*) BEGIN (* Main Program *) (*------------------------------------------------------------------------*) WBStart; SetWindow; SetMenu; DrawWindow; InitBase; SetDefaultFace; GoProgram; EndProgram; WBEnd; (*------------------------------------------------------------------------*) END FaceBender. (* © G1M 1988 *) (*------------------------------------------------------------------------*)