{$N+} Uses Graph,CRT; type float = double; tVec = record x,y,z : float; end; const ZeroVec : tVec = (X:0.0;Y:0.0;Z:0.0); MaxSpr = 250; Distance_Thresh = 1e-5; CurID : word = 1; type tID = 1..MaxSpr; pDistArr = ^tDistArr; tDistArr = array [1..10] of float; pSpring = ^tSpring; pSprArr = ^tSprArr; tSpring = record ID : tID; Already : array [1..MaxSpr] of boolean; pos,vel,Force : tVec; mass : float; dists : pDistArr; Ks : pDistArr; neibs : pSprArr; neib_count : word; end; tSprArr = array [1..10] of pSpring; pAnchor = ^tAnchor; tAnchor = record pos : tVec; Node : pSpring; end; tAnchArr = array [1..10] of pAnchor; pAnchArr = ^tAnchArr; pModel = ^tModel; tModel = record Springs : pSprArr; SprNum : tID; Anchors : pAnchArr; AnchNum : tID; Glob_Force : tVec; Gravity : tVec; Pos : tVec; energy_loss : float; end; {----------------------------------------} Procedure VecAdd(var aC:tVec;aA,aB:tVec); begin aC.x := aA.x + aB.x; aC.y := aA.y + aB.y; aC.z := aA.z + aB.z; end;{VecAdd} {-------------------------------} Procedure VecSub(var aC:tVec;aA,aB:tVec); begin aC.x := aA.x - aB.x; aC.y := aA.y - aB.y; aC.z := aA.z - aB.z; end;{VecAdd} {-------------------------------} Procedure VecNeg(var aV:tVec); begin aV.X := -aV.x; aV.y := -aV.y; aV.z := -aV.z; end;{NegVec} {-------------------------------} Procedure VecMul(var aV:tVec;aC:float); begin aV.x := aV.x * aC; aV.y := aV.y * aC; aV.z := aV.z * aC; end;{MulScal} {-------------------------------} Function NewSpring(ax,ay,az:float;aNeib_count:word):pSpring; var mNew : pSpring; begin New(mNew); with mNew^ do begin ID := CurID;Inc(CurID); Pos.x := ax; Pos.y := ay; Pos.z := aZ; Vel := ZeroVec; Force := ZeroVec; neib_Count := aNeib_Count; GetMem(neibs,neib_Count*SizeOf(pSpring)); GetMem(dists,neib_Count*SizeOf(float)); GetMem(ks,neib_Count*SizeOf(float)); FillChar(neibs^,neib_Count*SizeOf(pSpring),0); FillChar(dists^,neib_Count*SizeOf(float),0); FillChar(ks^,neib_Count*SizeOf(float),0); end; NewSpring := mNew; end;{NewSpring} {----------------------------------------} Procedure DelSpring(var aSpr:pSpring); begin if aSpr = nil then exit; FreeMem(aSpr^.Dists,aSpr^.neib_count*sizeof(float)); FreeMem(aSpr^.Ks,aSpr^.neib_count*sizeof(float)); FreeMem(aSpr^.neibs,aSpr^.neib_count*sizeof(pSpring)); Dispose(aSpr); aSpr := nil; end;{DelSpring} {----------------------------------------} Procedure CalcSingleSpring(aA:pSpring); var mDist : float; mB : pSpring; mForce: tVec; i : word; begin for i := 1 to aA^.neib_count do begin mB := aA^.neibs^[i]; {Get cur neigbour} if aA^.Already[mB^.ID] then continue; {If we calculated force, in B's loop so do next :) } aA^.Already[mB^.ID] := true; {Just for US} VecSub(mForce,mB^.pos,aA^.pos); {Calculate delta vector} with mForce do mDist := sqrt(x*x+y*y+z*z); {Calc distance} if mDist < Distance_Thresh then {Why so close ?} mForce := ZeroVec {This is'n true, but don't creates errornous effects} else begin VecMul(mForce,1/mDist); {Normalize force} mDist := mDist - aA^.dists^[i]; {Calc dX in F=k*dX} VecMul(mForce,mDist*aA^.kS^[i]); {Calc force} end; VecAdd(aA^.Force,aA^.Force,mForce); {Add our force} mB^.Already[aA^.ID] := true; {So we calc-ed 2 forces} VecNeg(mForce); {differs by sign} VecAdd(mB^.Force,mB^.Force,mForce); {so add it too} end; end;{CalcSingleSpring} {-----------------------------------} Procedure ModelCalcForces(aM:pModel); var i : tID; begin for i := 1 to aM^.SprNum do begin aM^.Springs^[i]^.Force := ZeroVec; FillChar(aM^.Springs^[i]^.Already,MaxSpr*SizeOf(Boolean),0); end; for i := 1 to aM^.SprNum do CalcSingleSpring(aM^.Springs^[i]); end;{ModelCalcForces} {----------------------------------} Procedure ModelApplyForces(aM:pModel;aTime:float); var mAccel : tVec; mC : pSpring; dX : tVec; i : tID; begin for i := 1 to aM^.SprNum do begin mC := aM^.Springs^[i]; mAccel := mC^.Force; {So our new acceleration is formed from force} VecMul(mAccel,1/mC^.Mass); {dividing by mass} VecAdd(mAccel,mAccel,aM^.Gravity); {Add the 'g'} VecMul(mAccel,aTime); {For modeling time} VecAdd(mC^.Vel,mC^.Vel,mAccel); dX := mC^.Vel; {delta X is Speed} VecMul(dx,aTime); {multiplyed by time} VecAdd(mC^.Pos,mC^.Pos,dx); {That's all with pos folks} if mC^.Pos.X < 0 then begin mC^.Vel.X := -mC^.Vel.X; mC^.Pos.X := 1; end; if mC^.Pos.Y < 0 then begin mC^.Vel.Y := -mC^.Vel.Y; mC^.Pos.Y := 1; end; if mC^.Pos.X > 638 then begin mC^.Vel.X := -mC^.Vel.X; mC^.Pos.X := 637; end; if mC^.Pos.Y > 478 then begin mC^.Vel.Y := -mC^.Vel.Y; mC^.Pos.Y := 477; end; VecMul(mC^.Vel,aM^.Energy_Loss); {Loss some speed in time} end; end;{ModelApplyForces} {----------------------------------} Procedure ModelCheckAnchors(aM:pModel); var i : tID; begin for i := 1 to aM^.AnchNum do aM^.Anchors^[i]^.Node^.Pos := aM^.Anchors^[i]^.Pos; end;{ModelCheckAnchors} {----------------------------------} Procedure RenderStringSpring(aM:pModel); var i : tID; gx,gy : float; Procedure LineCross(aV:tVec); begin Line(round(aV.x+gx)-3, round(aV.y+gy), round(aV.x+gx)+3, round(aV.y+gy)); Line(round(aV.x+gx), round(aV.y+gy)-3, round(aV.x+gx), round(aV.y+gy)+3); end; begin gx := aM^.Pos.X; gy := aM^.Pos.Y; for i := 1 to aM^.SprNum-1 do begin SetColor(Green); Line(round(gx+aM^.Springs^[i]^.pos.x), round(gy+aM^.Springs^[i]^.pos.y), round(gx+aM^.Springs^[i+1]^.pos.x), round(gy+aM^.Springs^[i+1]^.pos.y)); SetColor(Red); LineCross(aM^.Springs^[i]^.pos); end; LineCross(aM^.Springs^[i+1]^.Pos); end;{RenderStringSpring} {----------------------------------} Function InitMouse:boolean;assembler; asm mov ax, 00 int 33h cmp ax, 0ffffh jnz @@1 mov ax, 7 mov cx, 0 mov dx, 639 int 33h mov ax, 08 mov cx, 0 mov dx, 479 int 33h mov ax, 1 @@1: ret end;{InitMouse} Procedure MoveMouse(x,y:word);assembler; asm mov cx, x mov dx, y mov ax, 4 int 33h end;{ModeMouse} Procedure MousePosition(var x,y:word);assembler; asm mov ax, 3 int 33h les di, x mov es:[di], cx les di, y mov es:[di], dx end;{MousePosition} Procedure HideMouse;assembler; asm mov ax, 2 int 33h end; {-------------------------} var mString : pModel; i : tID; mx,my : word; j : longint; gD,gM : integer; SizeX : float; BEGIN New(mString); with mString^ do begin SprNum := 15; AnchNum := 1; SizeX := 600; GetMem(Springs,SprNum*SizeOf(pSpring)); GetMem(Anchors,AnchNum*SizeOf(pAnchor)); Springs^[1] := NewSpring(0,0,0,1); Springs^[SprNum] := NewSpring(SizeX*0.9,0,0,1); for i := 1 to SprNum-2 do Springs^[i+1] := NewSpring(i*SizeX/SprNum,0,0,2); Springs^[1]^.Neibs^[1] := Springs^[2]; Springs^[1]^.Dists^[1] := SizeX/SprNum; Springs^[1]^.Mass := 1; Springs^[1]^.Ks^[1] := 0.25; Springs^[SprNum]^.Neibs^[1] := Springs^[SprNum-1]; Springs^[SprNum]^.Dists^[1] := 0.5*SizeX/SprNum; Springs^[SprNum]^.Mass := 1; Springs^[SprNum]^.Ks^[1] := 0.25; for i := 2 to SprNum-1 do begin Springs^[i]^.Neibs^[1] := Springs^[i-1]; Springs^[i]^.Neibs^[2] := Springs^[i+1]; Springs^[i]^.Dists^[1] := 0.5*SizeX/SprNum; Springs^[i]^.Dists^[2] := 0.5*SizeX/SprNum; Springs^[i]^.Ks^[1] := 0.08; Springs^[i]^.Ks^[2] := 0.08; Springs^[i]^.Mass := 1; end; New(Anchors^[1]); Anchors^[1]^.Node := Springs^[1]; Anchors^[1]^.Pos := ZeroVec; { New(Anchors^[2]); Anchors^[2]^.Node := Springs^[SprNum]; Anchors^[2]^.Pos.X := 600; Anchors^[2]^.Pos.Y := 100; Anchors^[2]^.Pos.Z := 0; } Pos.x := 0; pos.y := 0; pos.z := 0; Gravity := ZeroVec; Gravity.Y := 0.09; Energy_Loss := 0.98; gD:=Detect; InitGraph(gD,gM,''); InitMouse; MoveMouse(10,5); while not keypressed do begin MousePosition(mx,my); Anchors^[1]^.pos.x := mx; Anchors^[1]^.pos.y := my; ModelCalcForces(mString); ModelApplyForces(mString,1); ModelCheckAnchors(mString); repeat until (Port[$03DA] and 8) <> 8; repeat until (Port[$03DA] and 8) = 8; ClearDevice; RenderStringSpring(mString); end; Dispose(Anchors^[1]); For i := 1 To SprNum Do DelSpring(Springs^[i]); FreeMem(Springs, SprNum*SizeOf(pSpring)); FreeMem(Anchors,AnchNum*SizeOf(pAnchor)); CloseGraph; end; END.