{ This file is part of the Mufasa Macro Library (MML) Copyright (c) 2009 by Raymond van Venetiƫ and Merlijn Wajer MML is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. MML is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with MML. If not, see . See the file COPYING, included in this distribution, for details about the copyright. DTM class for the Mufasa Macro Library } unit dtm; {$mode objfpc}{$H+} interface uses Classes, SysUtils, MufasaTypes; type { TMDTM } TMDTM = class(TObject) private FPoints : TMDTMPointArray; FLen : integer; function GetPointerPoints: PMDTMPoint; procedure SetPointCount(const AValue: integer); public Name : string; Index : integer; function ToString : string; function SaveToFile(const FileName : string) : boolean; function LoadFromString(const s : string) : boolean; procedure Normalize; function Valid : boolean; procedure DeletePoint( Point : integer); procedure SwapPoint(p1,p2 : integer); procedure MovePoint(fromIndex,toIndex : integer); procedure AddPoint( Point : TMDTMPoint); property PPoints : PMDTMPoint read GetPointerPoints; property Count : integer read FLen write SetPointCount; property Points : TMDTMPointArray read FPoints; end; { TMDTMS } TMDTMS = class(TObject) //Manages the DTMs TMufasaDTMs private Client: TObject; DTMList: Array Of TMDTM; FreeSpots: Array Of Integer; procedure CheckIndex(index : integer); public function AddDTM(const d: TSDTM): Integer;overload; function AddDTM(const d: TMDTM): Integer;overload; function GetDTM(index: Integer) :TMDTM; procedure FreeDTM(DTM: Integer); function StringToDTM(const S: String): Integer; property DTM[Index : integer]: TMDTM read GetDTM; default; constructor Create(Owner: TObject); destructor Destroy; override; end; implementation uses dtmutil, paszlib, client, DCPbase64, graphics, // for TColor math // for max ; constructor TMDTMS.Create(Owner: TObject); begin inherited Create; Self.Client := Owner; SetLength(DTMList, 0); SetLength(FreeSpots, 0); end; {$DEFINE DTM_DEBUG} destructor TMDTMS.Destroy; var i, j: integer; b:boolean; WriteStr : string; begin WriteStr := '['; for i := 0 to high(DTMList) do begin b := false; for j := 0 to high(freespots) do if i = freespots[j] then begin b := true; break; end; if not b then begin; if DTMList[i].name <> '' then WriteStr := WriteStr + DTMList[i].name + ', ' else WriteStr := WriteStr + inttostr(i) + ', '; FreeDTM(i); end; end; if WriteStr <> '[' then //Has unfreed DTMs begin SetLength(WriteStr,length(WriteStr)-1); WriteStr[Length(writeStr)] := ']'; TClient(Client).Writeln(Format('The following DTMs were not freed: %s',[WriteStr])); end; SetLength(DTMList, 0); SetLength(FreeSpots, 0); inherited Destroy; end; // Rotates the given point (p) by A (in radians) around the point defined by cx, cy. function RotatePoint(const p: TPoint;const angle, mx, my: Extended): TPoint; inline; begin Result.X := Round(mx + cos(angle) * (p.x - mx) - sin(angle) * (p.y - my)); Result.Y := Round(my + sin(angle) * (p.x - mx) + cos(angle) * (p.y- my)); end; function HexToInt(const HexNum: string): LongInt;inline; begin Result:=StrToInt('$' + HexNum); end; function TMDTMS.StringToDTM(const S: String): Integer; var aDTM : TMDTM; begin aDTM := TMDTM.Create; aDTM.LoadFromString(s); Result := AddDTM(aDTM); end; procedure TMDTMS.CheckIndex(index: integer); begin if (index < 0) or (index >= Length(DTMList)) or (DTMList[Index] = nil) then raise Exception.CreateFmt('The given DTM Index[%d] doesn''t exist',[index]); end; function TMDTMS.AddDTM(const d: TSDTM): Integer; begin Result := AddDTM(SDTMToMDTM(d)); end; {/\ Adds the given pDTM to the DTM Array, and returns it's index. /\} function TMDTMS.AddDTM(const d: TMDTM): Integer; begin if Length(FreeSpots) > 0 then begin Result := FreeSpots[High(FreeSpots)]; SetLength(FreeSpots, High(FreeSpots)); end else begin SetLength(DTMList, Length(DTMList) + 1); Result := High(DTMList); end; DTMList[Result] := d; DTMList[Result].Index:= Result; DTMList[result].Normalize; end; {/\ Returns the DTM (pDTM type) in the variable dtm at the given index. Returns true is succesfull, false if the dtm does not exist. /\} function TMDTMS.GetDTM(index: Integer) :TMDTM; begin CheckIndex(index); result := DTMList[index]; end; {/\ Unloads the DTM at the given index from the DTM Array. Notes: Will keep track of not used index, so it is very memory efficient. /\} procedure TMDTMS.FreeDTM(DTM: Integer); begin CheckIndex(DTM); DTMList[DTM].Free; DTMList[DTM] := nil; SetLength(FreeSpots, Length(FreeSpots) + 1); FreeSpots[High(FreeSpots)] := DTM; end; { TMDTM } function TMDTM.GetPointerPoints: PMDTMPoint; begin if count < 1 then result := nil else result := @FPoints[0]; end; procedure TMDTM.SetPointCount(const AValue: integer); begin SetLength(FPoints,AValue); FLen := AValue; end; //We save the data as arrays for each point (so we can add features to DTMs without having to change //the way the ToString is done.. //E.G. A DTM with 3 points would become //LenXXXYYYCCCTTTASZASZASZBPBPBP function TMDTM.ToString: string; var i,len : integer; Ptr,Start : Pointer; Destlen : Longword; procedure WriteInteger(int : integer); begin PLongInt(Ptr)^ := int; Inc(ptr,sizeof(int)); end; procedure WriteBool(bool : boolean); begin; PBoolean(Ptr)^ := bool; inc(ptr,sizeof(boolean)); end; begin result := ''; if Count < 1 then exit; len := Count * TMDTMPointSize + SizeOf(Integer); Start:= GetMem(len); Ptr := Start; WriteInteger(FLen); for i := 0 to FLen-1 do WriteInteger(FPoints[i].x); for i := 0 to FLen-1 do WriteInteger(FPoints[i].y); for i := 0 to FLen-1 do WriteInteger(FPoints[i].c); for i := 0 to FLen-1 do WriteInteger(FPoints[i].t); for i := 0 to FLen-1 do WriteInteger(FPoints[i].asz); for i := 0 to FLen-1 do WriteBool(FPoints[i].bp); Destlen :=BufferLen; if compress(BufferString,destlen,pchar(start),len) = Z_OK then begin setlength(result,Destlen + SizeOf(Integer)); PInteger(@result[1])^ := len; Move(bufferstring[0],result[1 + sizeof(integer)],Destlen); //We now have Size + Compressed data.. Lets Base64Encrypt it! Result := 'm' + Base64EncodeStr(result); //It now looks like m + base64encoded data! The 'm' is to indicate we used this encryption method. end; Freemem(start,len); end; function TMDTM.SaveToFile(const FileName: string): boolean; begin end; function TMDTM.LoadFromString(const s: string): boolean; var MDTM : TMDTM; Source : String; DestLen : longword; i,ii,c : integer; DPoints : PMDTMPoint; Ptr : Pointer; function ReadInteger : integer; begin Result := PInteger(ptr)^; inc(ptr,sizeof(integer)); end; function ReadBoolean : boolean; begin result := PBoolean(ptr)^; inc(ptr,sizeof(boolean)); end; begin Result := false; ii := Length(S); if (ii = 0) then exit; if S[1] = 'm' then begin if ii < 9 then raise Exception.CreateFMT('Invalid DTM-String passed to StringToDTM: %s',[s]); Source := Base64DecodeStr(copy(s,2,ii-1)); i:= PLongint(@source[1])^; //The 4 four bytes should contain the dest len! if i < 1 then raise Exception.CreateFMT('Invalid DTM-String passed to StringToDTM: %s',[s]); DestLen := BufferLen; ptr := @Source[1 + sizeof(longint)]; if uncompress(BufferString,DestLen,ptr,length(source)-sizeof(integer)) = Z_OK then begin ptr := BufferString; Self.Count:= ReadInteger; ii := Self.Count; if (Self.Count * TMDTMPointSize) <> (Destlen - SizeOf(integer)) then raise Exception.CreateFMT('Invalid DTM-String passed to StringToDTM: %s',[s]); DPoints := Self.PPoints; for i := 0 to ii-1 do DPoints[i].x := ReadInteger; for i := 0 to ii-1 do DPoints[i].y := ReadInteger; for i := 0 to ii-1 do DPoints[i].c := ReadInteger; for i := 0 to ii-1 do DPoints[i].t := ReadInteger; for i := 0 to ii-1 do DPoints[i].asz := ReadInteger; for i := 0 to ii-1 do DPoints[i].bp := ReadBoolean; Result := true; end; end else begin if (ii mod 2 <> 0) then exit; ii := ii div 2; SetLength(Source,ii); for i := 1 to ii do Source[i] := Chr(HexToInt(S[i * 2 - 1] + S[i * 2])); DestLen := BufferLen; if uncompress(Bufferstring,Destlen,pchar(Source), ii) = Z_OK then begin; if (Destlen mod 36) > 0 then raise Exception.CreateFMT('Invalid DTM-String passed to StringToDTM: %s',[s]); DestLen := DestLen div 36; Self.Count:= DestLen; DPoints := Self.PPoints; ptr := bufferstring; for i := 0 to DestLen - 1 do begin; DPoints[i].x :=PInteger(ptr + 1)^; DPoints[i].y := PInteger(ptr + 5)^; DPoints[i].asz := PInteger(ptr + 12)^; // DPoints.ash[i] := PInteger(@b^[c+16])^; DPoints[i].c := PInteger(ptr + 20)^; DPoints[i].t := PInteger(ptr + 24)^; DPoints[i].bp := False; inc(ptr,36); end; Result := true; end; end; if result then Normalize; end; procedure TMDTM.Normalize; var i:integer; begin if (self = nil) or (Self.count < 1) or ((Self.Points[0].x = 0) and (Self.Points[0].y = 0)) then //Already normalized exit; for i := 1 to Self.Count - 1 do begin Self.Points[i].x := Self.Points[i].x - Self.Points[0].x; Self.Points[i].y := Self.Points[i].y - Self.Points[0].y; end; Self.Points[0].x := 0; Self.Points[0].y := 0; end; function TMDTM.Valid: boolean; begin result := false; if Count < 1 then exit; Normalize; result := true; end; procedure TMDTM.DeletePoint(Point: integer); begin MovePoint(Point,FLen-1); Count := Count - 1; end; procedure TMDTM.SwapPoint(p1, p2: integer); var Temp : TMDTMPoint; begin Temp := FPoints[p1]; FPoints[p1] := FPoints[p2]; FPoints[p2] := Temp; end; procedure TMDTM.MovePoint(fromIndex, toIndex: integer); var i : integer; begin if fromIndex > toIndex then //We are going down begin for i := fromindex downto Toindex+1 do SwapPoint(i,i-1); end else if fromIndex < toIndex then for i := fromindex to toindex - 1 do SwapPoint(i,i+1); end; procedure TMDTM.AddPoint(Point: TMDTMPoint); begin Count:= Count + 1; FPoints[FLen-1] := Point; end; end.