(*------------------------------------------------------------------------*)
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 *)
(*------------------------------------------------------------------------*)