1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480 |
- {
- fpvectorial.pas
- Vector graphics document
- License: The same modified LGPL as the Free Pascal RTL
- See the file COPYING.modifiedLGPL for more details
- AUTHORS: Felipe Monteiro de Carvalho
- Pedro Sol Pegorini L de Lima
- }
- unit fpvectorial;
- {$ifdef fpc}
- {$mode delphi}
- {$endif}
- interface
- uses
- Classes, SysUtils, Math,
- fpcanvas, fpimage;
- type
- TvVectorialFormat = (
- { Multi-purpose document formats }
- vfPDF, vfSVG, vfCorelDrawCDR, vfWindowsMetafileWMF,
- { CAD formats }
- vfDXF,
- { Printing formats }
- vfPostScript, vfEncapsulatedPostScript,
- { GCode formats }
- vfGCodeAvisoCNCPrototipoV5, vfGCodeAvisoCNCPrototipoV6);
- const
- { Default extensions }
- { Multi-purpose document formats }
- STR_PDF_EXTENSION = '.pdf';
- STR_POSTSCRIPT_EXTENSION = '.ps';
- STR_SVG_EXTENSION = '.svg';
- STR_CORELDRAW_EXTENSION = '.cdr';
- STR_WINMETAFILE_EXTENSION = '.wmf';
- STR_AUTOCAD_EXCHANGE_EXTENSION = '.dxf';
- STR_ENCAPSULATEDPOSTSCRIPT_EXTENSION = '.eps';
- type
- { Pen, Brush and Font }
- TvPen = record
- Color: TFPColor;
- Style: TFPPenStyle;
- Width: Integer;
- end;
- TvBrush = record
- Color: TFPColor;
- Style: TFPBrushStyle;
- end;
- TvFont = record
- Color: TFPColor;
- Size: integer;
- Name: utf8string;
- {@@
- Font orientation is measured in degrees and uses the
- same direction as the LCL TFont.orientation, which is counter-clockwise.
- Zero is the normal, horizontal, orientation, directed to the right.
- }
- Orientation: Double;
- end;
- { Coordinates and polyline segments }
- T3DPoint = record
- X, Y, Z: Double;
- end;
- P3DPoint = ^T3DPoint;
- TSegmentType = (
- st2DLine, st2DLineWithPen, st2DBezier,
- st3DLine, st3DBezier, stMoveTo);
- {@@
- The coordinates in fpvectorial are given in millimiters and
- the starting point is in the bottom-left corner of the document.
- The X grows to the right and the Y grows to the top.
- }
- { TPathSegment }
- TPathSegment = class
- public
- SegmentType: TSegmentType;
- // Fields for linking the list
- Previous: TPathSegment;
- Next: TPathSegment;
- end;
- {@@
- In a 2D segment, the X and Y coordinates represent usually the
- final point of the segment, being that it starts where the previous
- segment ends. The exception is for the first segment of all, which simply
- holds the starting point for the drawing and should always be of the type
- stMoveTo.
- }
- T2DSegment = class(TPathSegment)
- public
- X, Y: Double;
- end;
- T2DSegmentWithPen = class(T2DSegment)
- public
- Pen: TvPen;
- end;
- {@@
- In Bezier segments, we remain using the X and Y coordinates for the ending point.
- The starting point is where the previous segment ended, so that the intermediary
- bezier control points are [X2, Y2] and [X3, Y3].
- }
- T2DBezierSegment = class(T2DSegment)
- public
- X2, Y2: Double;
- X3, Y3: Double;
- end;
- T3DSegment = class(TPathSegment)
- public
- {@@
- Coordinates of the end of the segment.
- For the first segment, this is the starting point.
- }
- X, Y, Z: Double;
- end;
- T3DBezierSegment = class(T3DSegment)
- public
- X2, Y2, Z2: Double;
- X3, Y3, Z3: Double;
- end;
- TvFindEntityResult = (vfrNotFound, vfrFound, vfrSubpartFound);
- { Now all elements }
- {@@
- All elements should derive from TvEntity, regardless of whatever properties
- they might contain.
- }
- { TvEntity }
- TvEntity = class
- public
- X, Y, Z: Double;
- {@@ The global Pen for the entire entity. In the case of paths, individual
- elements might be able to override this setting. }
- Pen: TvPen;
- {@@ The global Brush for the entire entity. In the case of paths, individual
- elements might be able to override this setting. }
- Brush: TvBrush;
- constructor Create; virtual;
- procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); virtual;
- procedure ExpandBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
- function TryToSelect(APos: TPoint): TvFindEntityResult; virtual;
- procedure Translate(ADeltaX, ADeltaY: Integer); virtual;
- end;
- TvClipMode = (vcmNonzeroWindingRule, vcmEvenOddRule);
- TPath = class(TvEntity)
- Len: Integer;
- Points: TPathSegment; // Beginning of the double-linked list
- PointsEnd: TPathSegment;// End of the double-linked list
- CurPoint: TPathSegment; // Used in PrepareForSequentialReading and Next
- ClipPath: TPath;
- ClipMode: TvClipMode;
- procedure Assign(ASource: TPath);
- procedure PrepareForSequentialReading;
- function Next(): TPathSegment;
- procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); override;
- procedure AppendSegment(ASegment: TPathSegment);
- end;
- {@@
- TvText represents a text entity.
- }
- { TvText }
- TvText = class(TvEntity)
- public
- Value: TStringList;
- Font: TvFont;
- constructor Create; override;
- destructor Destroy; override;
- function TryToSelect(APos: TPoint): TvFindEntityResult; override;
- end;
- {@@
- }
- TvCircle = class(TvEntity)
- public
- Radius: Double;
- end;
- {@@
- }
- TvCircularArc = class(TvEntity)
- public
- Radius: Double;
- {@@ The Angle is measured in degrees in relation to the positive X axis }
- StartAngle, EndAngle: Double;
- end;
- {@@
- }
- TvEllipse = class(TvEntity)
- public
- // Mandatory fields
- MajorHalfAxis, MinorHalfAxis: Double;
- {@@ The Angle is measured in degrees in relation to the positive X axis }
- Angle: Double;
- // Calculated fields
- BoundingRect: TRect;
- procedure CalculateBoundingRectangle;
- end;
- {@@
- The brush has no effect in this class
- DimensionLeft ---text--- DimensionRight
- | |
- | | BaseRight
- |
- | BaseLeft
- }
- { TvAlignedDimension }
- TvAlignedDimension = class(TvEntity)
- public
- // Mandatory fields
- BaseLeft, BaseRight, DimensionLeft, DimensionRight: T3DPoint;
- end;
- {@@
- Vectorial images can contain raster images inside them and this entity
- represents this.
- If the Width and Height differ from the same data in the image, then
- the raster image will be stretched.
- Note that TFPCustomImage does not implement a storage, so the property
- RasterImage should be filled with either a FPImage.TFPMemoryImage or with
- a TLazIntfImage. The property RasterImage might be nil.
- }
- TvRasterImage = class(TvEntity)
- public
- RasterImage: TFPCustomImage;
- Top, Left, Width, Height: Double;
- end;
- type
- TvCustomVectorialWriter = class;
- TvCustomVectorialReader = class;
- TvVectorialPage = class;
- { TvVectorialDocument }
- TvVectorialDocument = class
- private
- FPages: TFPList;
- FCurrentPageIndex: Integer;
- function CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
- function CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
- public
- Width, Height: Double; // in millimeters
- Name: string;
- // User-Interface information
- ZoomLevel: Double; // 1 = 100%
- { Selection fields }
- SelectedvElement: TvEntity;
- { Base methods }
- constructor Create; virtual;
- destructor Destroy; override;
- procedure Assign(ASource: TvVectorialDocument);
- procedure AssignTo(ADest: TvVectorialDocument);
- procedure WriteToFile(AFileName: string; AFormat: TvVectorialFormat); overload;
- procedure WriteToFile(AFileName: string); overload;
- procedure WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
- procedure WriteToStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
- procedure ReadFromFile(AFileName: string; AFormat: TvVectorialFormat); overload;
- procedure ReadFromFile(AFileName: string); overload;
- procedure ReadFromStream(AStream: TStream; AFormat: TvVectorialFormat);
- procedure ReadFromStrings(AStrings: TStrings; AFormat: TvVectorialFormat);
- class function GetFormatFromExtension(AFileName: string): TvVectorialFormat;
- function GetDetailedFileFormat(): string;
- procedure GuessDocumentSize();
- procedure GuessGoodZoomLevel(AScreenSize: Integer = 500);
- { Page methods }
- function GetPage(AIndex: Integer): TvVectorialPage;
- function GetPageCount: Integer;
- function GetCurrentPage: TvVectorialPage;
- procedure SetCurrentPage(AIndex: Integer);
- function AddPage(): TvVectorialPage;
- { Data removing methods }
- procedure Clear; virtual;
- end;
- { TvVectorialPage }
- TvVectorialPage = class
- private
- FEntities: TFPList;
- FTmpPath: TPath;
- FTmpText: TvText;
- //procedure RemoveCallback(data, arg: pointer);
- procedure ClearTmpPath();
- procedure AppendSegmentToTmpPath(ASegment: TPathSegment);
- public
- Width, Height: Double; // in millimeters
- Owner: TvVectorialDocument;
- { Base methods }
- constructor Create(AOwner: TvVectorialDocument); virtual;
- destructor Destroy; override;
- procedure Assign(ASource: TvVectorialPage);
- { Data reading methods }
- function GetEntity(ANum: Cardinal): TvEntity;
- function GetEntitiesCount: Integer;
- function FindAndSelectEntity(Pos: TPoint): TvFindEntityResult;
- { Data removing methods }
- procedure Clear; virtual;
- { Data writing methods }
- function AddEntity(AEntity: TvEntity): Integer;
- procedure AddPathCopyMem(APath: TPath);
- procedure StartPath(AX, AY: Double); overload;
- procedure StartPath(); overload;
- procedure AddMoveToPath(AX, AY: Double);
- procedure AddLineToPath(AX, AY: Double); overload;
- procedure AddLineToPath(AX, AY: Double; AColor: TFPColor); overload;
- procedure AddLineToPath(AX, AY, AZ: Double); overload;
- procedure GetCurrentPathPenPos(var AX, AY: Double);
- procedure AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double); overload;
- procedure AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double); overload;
- procedure SetBrushColor(AColor: TFPColor);
- procedure SetBrushStyle(AStyle: TFPBrushStyle);
- procedure SetPenColor(AColor: TFPColor);
- procedure SetPenStyle(AStyle: TFPPenStyle);
- procedure SetPenWidth(AWidth: Integer);
- procedure SetClipPath(AClipPath: TPath; AClipMode: TvClipMode);
- procedure EndPath();
- procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
- procedure AddText(AX, AY: Double; AStr: utf8string); overload;
- procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
- procedure AddCircle(ACenterX, ACenterY, ARadius: Double);
- procedure AddCircularArc(ACenterX, ACenterY, ARadius, AStartAngle, AEndAngle: Double; AColor: TFPColor);
- procedure AddEllipse(CenterX, CenterY, MajorHalfAxis, MinorHalfAxis, Angle: Double);
- // Dimensions
- procedure AddAlignedDimension(BaseLeft, BaseRight, DimLeft, DimRight: T3DPoint);
- end;
- {@@ TvVectorialReader class reference type }
- TvVectorialReaderClass = class of TvCustomVectorialReader;
- { TvCustomVectorialReader }
- TvCustomVectorialReader = class
- public
- { General reading methods }
- constructor Create; virtual;
- procedure ReadFromFile(AFileName: string; AData: TvVectorialDocument); virtual;
- procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); virtual;
- procedure ReadFromStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
- end;
- {@@ TvVectorialWriter class reference type }
- TvVectorialWriterClass = class of TvCustomVectorialWriter;
- {@@ TvCustomVectorialWriter }
- { TvCustomVectorialWriter }
- TvCustomVectorialWriter = class
- public
- { General writing methods }
- constructor Create; virtual;
- procedure WriteToFile(AFileName: string; AData: TvVectorialDocument); virtual;
- procedure WriteToStream(AStream: TStream; AData: TvVectorialDocument); virtual;
- procedure WriteToStrings(AStrings: TStrings; AData: TvVectorialDocument); virtual;
- end;
- {@@ List of registered formats }
- TvVectorialFormatData = record
- ReaderClass: TvVectorialReaderClass;
- WriterClass: TvVectorialWriterClass;
- ReaderRegistered: Boolean;
- WriterRegistered: Boolean;
- Format: TvVectorialFormat;
- end;
- var
- GvVectorialFormats: array of TvVectorialFormatData;
- procedure RegisterVectorialReader(
- AReaderClass: TvVectorialReaderClass;
- AFormat: TvVectorialFormat);
- procedure RegisterVectorialWriter(
- AWriterClass: TvVectorialWriterClass;
- AFormat: TvVectorialFormat);
- function Make2DPoint(AX, AY: Double): T3DPoint;
- implementation
- const
- Str_Error_Nil_Path = ' The program attempted to add a segment before creating a path';
- {@@
- Registers a new reader for a format
- }
- procedure RegisterVectorialReader(
- AReaderClass: TvVectorialReaderClass;
- AFormat: TvVectorialFormat);
- var
- i, len: Integer;
- FormatInTheList: Boolean;
- begin
- len := Length(GvVectorialFormats);
- FormatInTheList := False;
- { First search for the format in the list }
- for i := 0 to len - 1 do
- begin
- if GvVectorialFormats[i].Format = AFormat then
- begin
- if GvVectorialFormats[i].ReaderRegistered then
- raise Exception.Create('RegisterVectorialReader: Reader class for format ' {+ AFormat} + ' already registered.');
- GvVectorialFormats[i].ReaderRegistered := True;
- GvVectorialFormats[i].ReaderClass := AReaderClass;
- FormatInTheList := True;
- Break;
- end;
- end;
- { If not already in the list, then add it }
- if not FormatInTheList then
- begin
- SetLength(GvVectorialFormats, len + 1);
- GvVectorialFormats[len].ReaderClass := AReaderClass;
- GvVectorialFormats[len].WriterClass := nil;
- GvVectorialFormats[len].ReaderRegistered := True;
- GvVectorialFormats[len].WriterRegistered := False;
- GvVectorialFormats[len].Format := AFormat;
- end;
- end;
- {@@
- Registers a new writer for a format
- }
- procedure RegisterVectorialWriter(
- AWriterClass: TvVectorialWriterClass;
- AFormat: TvVectorialFormat);
- var
- i, len: Integer;
- FormatInTheList: Boolean;
- begin
- len := Length(GvVectorialFormats);
- FormatInTheList := False;
- { First search for the format in the list }
- for i := 0 to len - 1 do
- begin
- if GvVectorialFormats[i].Format = AFormat then
- begin
- if GvVectorialFormats[i].WriterRegistered then
- raise Exception.Create('RegisterVectorialWriter: Writer class for format ' + {AFormat +} ' already registered.');
- GvVectorialFormats[i].WriterRegistered := True;
- GvVectorialFormats[i].WriterClass := AWriterClass;
- FormatInTheList := True;
- Break;
- end;
- end;
- { If not already in the list, then add it }
- if not FormatInTheList then
- begin
- SetLength(GvVectorialFormats, len + 1);
- GvVectorialFormats[len].ReaderClass := nil;
- GvVectorialFormats[len].WriterClass := AWriterClass;
- GvVectorialFormats[len].ReaderRegistered := False;
- GvVectorialFormats[len].WriterRegistered := True;
- GvVectorialFormats[len].Format := AFormat;
- end;
- end;
- function Make2DPoint(AX, AY: Double): T3DPoint;
- begin
- Result.X := AX;
- Result.Y := AY;
- Result.Z := 0;
- end;
- { TvVectorialPage }
- procedure TvVectorialPage.ClearTmpPath;
- var
- segment, oldsegment: TPathSegment;
- begin
- FTmpPath.Points := nil;
- FTmpPath.PointsEnd := nil;
- FTmpPath.Len := 0;
- FTmpPath.Brush.Color := colBlue;
- FTmpPath.Brush.Style := bsClear;
- FTmpPath.Pen.Color := colBlack;
- FTmpPath.Pen.Style := psSolid;
- FTmpPath.Pen.Width := 1;
- end;
- procedure TvVectorialPage.AppendSegmentToTmpPath(ASegment: TPathSegment);
- begin
- FTmpPath.AppendSegment(ASegment);
- end;
- constructor TvVectorialPage.Create(AOwner: TvVectorialDocument);
- begin
- inherited Create;
- FEntities := TFPList.Create;
- FTmpPath := TPath.Create;
- Owner := AOwner;
- end;
- destructor TvVectorialPage.Destroy;
- begin
- Clear;
- FEntities.Free;
- inherited Destroy;
- end;
- procedure TvVectorialPage.Assign(ASource: TvVectorialPage);
- var
- i: Integer;
- begin
- Clear;
- for i := 0 to ASource.GetEntitiesCount - 1 do
- Self.AddEntity(ASource.GetEntity(i));
- end;
- function TvVectorialPage.GetEntity(ANum: Cardinal): TvEntity;
- begin
- if ANum >= FEntities.Count then raise Exception.Create('TvVectorialDocument.GetEntity: Entity number out of bounds');
- if FEntities.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetEntity: Invalid Entity number');
- Result := TvEntity(FEntities.Items[ANum]);
- end;
- function TvVectorialPage.GetEntitiesCount: Integer;
- begin
- Result := FEntities.Count;
- end;
- function TvVectorialPage.FindAndSelectEntity(Pos: TPoint): TvFindEntityResult;
- var
- lEntity: TvEntity;
- i: Integer;
- begin
- Result := vfrNotFound;
- for i := 0 to GetEntitiesCount() - 1 do
- begin
- lEntity := GetEntity(i);
- Result := lEntity.TryToSelect(Pos);
- if Result <> vfrNotFound then
- begin
- Owner.SelectedvElement := lEntity;
- Exit;
- end;
- end;
- end;
- procedure TvVectorialPage.Clear;
- begin
- FEntities.Clear();
- end;
- {@@
- Adds an entity to the document and returns it's current index
- }
- function TvVectorialPage.AddEntity(AEntity: TvEntity): Integer;
- begin
- Result := FEntities.Count;
- FEntities.Add(Pointer(AEntity));
- end;
- procedure TvVectorialPage.AddPathCopyMem(APath: TPath);
- var
- lPath: TPath;
- Len: Integer;
- begin
- lPath := TPath.Create;
- lPath.Assign(APath);
- AddEntity(lPath);
- //WriteLn(':>TvVectorialDocument.AddPath 1 Len = ', Len);
- end;
- {@@
- Starts writing a Path in multiple steps.
- Should be followed by zero or more calls to AddPointToPath
- and by a call to EndPath to effectively add the data.
- @see EndPath, AddPointToPath
- }
- procedure TvVectorialPage.StartPath(AX, AY: Double);
- var
- segment: T2DSegment;
- begin
- ClearTmpPath();
- FTmpPath.Len := 1;
- segment := T2DSegment.Create;
- segment.SegmentType := stMoveTo;
- segment.X := AX;
- segment.Y := AY;
- FTmpPath.Points := segment;
- FTmpPath.PointsEnd := segment;
- end;
- procedure TvVectorialPage.StartPath;
- begin
- ClearTmpPath();
- end;
- procedure TvVectorialPage.AddMoveToPath(AX, AY: Double);
- var
- segment: T2DSegment;
- begin
- segment := T2DSegment.Create;
- segment.SegmentType := stMoveTo;
- segment.X := AX;
- segment.Y := AY;
- AppendSegmentToTmpPath(segment);
- end;
- {@@
- Adds one more point to the end of a Path being
- writing in multiple steps.
- Does nothing if not called between StartPath and EndPath.
- Can be called multiple times to add multiple points.
- @see StartPath, EndPath
- }
- procedure TvVectorialPage.AddLineToPath(AX, AY: Double);
- var
- segment: T2DSegment;
- begin
- segment := T2DSegment.Create;
- segment.SegmentType := st2DLine;
- segment.X := AX;
- segment.Y := AY;
- AppendSegmentToTmpPath(segment);
- end;
- procedure TvVectorialPage.AddLineToPath(AX, AY: Double; AColor: TFPColor);
- var
- segment: T2DSegmentWithPen;
- begin
- segment := T2DSegmentWithPen.Create;
- segment.SegmentType := st2DLineWithPen;
- segment.X := AX;
- segment.Y := AY;
- segment.Pen.Color := AColor;
- AppendSegmentToTmpPath(segment);
- end;
- procedure TvVectorialPage.AddLineToPath(AX, AY, AZ: Double);
- var
- segment: T3DSegment;
- begin
- segment := T3DSegment.Create;
- segment.SegmentType := st3DLine;
- segment.X := AX;
- segment.Y := AY;
- segment.Z := AZ;
- AppendSegmentToTmpPath(segment);
- end;
- {@@
- Gets the current Pen Pos in the temporary path
- }
- procedure TvVectorialPage.GetCurrentPathPenPos(var AX, AY: Double);
- begin
- // Check if we are the first segment in the tmp path
- if FTmpPath.PointsEnd = nil then raise Exception.Create('[TvVectorialDocument.GetCurrentPathPenPos] One cannot obtain the Pen Pos if there are no segments in the temporary path');
- AX := T2DSegment(FTmpPath.PointsEnd).X;
- AY := T2DSegment(FTmpPath.PointsEnd).Y;
- end;
- {@@
- Adds a bezier element to the path. It starts where the previous element ended
- and it goes throw the control points [AX1, AY1] and [AX2, AY2] and ends
- in [AX3, AY3].
- }
- procedure TvVectorialPage.AddBezierToPath(AX1, AY1, AX2, AY2, AX3, AY3: Double);
- var
- segment: T2DBezierSegment;
- begin
- segment := T2DBezierSegment.Create;
- segment.SegmentType := st2DBezier;
- segment.X := AX3;
- segment.Y := AY3;
- segment.X2 := AX1;
- segment.Y2 := AY1;
- segment.X3 := AX2;
- segment.Y3 := AY2;
- AppendSegmentToTmpPath(segment);
- end;
- procedure TvVectorialPage.AddBezierToPath(AX1, AY1, AZ1, AX2, AY2, AZ2, AX3, AY3, AZ3: Double);
- var
- segment: T3DBezierSegment;
- begin
- segment := T3DBezierSegment.Create;
- segment.SegmentType := st3DBezier;
- segment.X := AX3;
- segment.Y := AY3;
- segment.Z := AZ3;
- segment.X2 := AX1;
- segment.Y2 := AY1;
- segment.Z2 := AZ1;
- segment.X3 := AX2;
- segment.Y3 := AY2;
- segment.Z3 := AZ2;
- AppendSegmentToTmpPath(segment);
- end;
- procedure TvVectorialPage.SetBrushColor(AColor: TFPColor);
- begin
- FTmPPath.Brush.Color := AColor;
- end;
- procedure TvVectorialPage.SetBrushStyle(AStyle: TFPBrushStyle);
- begin
- FTmPPath.Brush.Style := AStyle;
- end;
- procedure TvVectorialPage.SetPenColor(AColor: TFPColor);
- begin
- FTmPPath.Pen.Color := AColor;
- end;
- procedure TvVectorialPage.SetPenStyle(AStyle: TFPPenStyle);
- begin
- FTmPPath.Pen.Style := AStyle;
- end;
- procedure TvVectorialPage.SetPenWidth(AWidth: Integer);
- begin
- FTmPPath.Pen.Width := AWidth;
- end;
- procedure TvVectorialPage.SetClipPath(AClipPath: TPath; AClipMode: TvClipMode);
- begin
- FTmPPath.ClipPath := AClipPath;
- FTmPPath.ClipMode := AClipMode;
- end;
- {@@
- Finishes writing a Path, which was created in multiple
- steps using StartPath and AddPointToPath,
- to the document.
- Does nothing if there wasn't a previous correspondent call to
- StartPath.
- @see StartPath, AddPointToPath
- }
- procedure TvVectorialPage.EndPath;
- begin
- if FTmPPath.Len = 0 then Exit;
- AddPathCopyMem(FTmPPath);
- ClearTmpPath();
- end;
- procedure TvVectorialPage.AddText(AX, AY, AZ: Double; FontName: string;
- FontSize: integer; AText: utf8string);
- var
- lText: TvText;
- begin
- lText := TvText.Create;
- lText.Value.Text := AText;
- lText.X := AX;
- lText.Y := AY;
- lText.Z := AZ;
- lText.Font.Name := FontName;
- lText.Font.Size := FontSize;
- AddEntity(lText);
- end;
- procedure TvVectorialPage.AddText(AX, AY: Double; AStr: utf8string);
- begin
- AddText(AX, AY, 0, '', 10, AStr);
- end;
- procedure TvVectorialPage.AddText(AX, AY, AZ: Double; AStr: utf8string);
- begin
- AddText(AX, AY, AZ, '', 10, AStr);
- end;
- procedure TvVectorialPage.AddCircle(ACenterX, ACenterY, ARadius: Double);
- var
- lCircle: TvCircle;
- begin
- lCircle := TvCircle.Create;
- lCircle.X := ACenterX;
- lCircle.Y := ACenterY;
- lCircle.Radius := ARadius;
- AddEntity(lCircle);
- end;
- procedure TvVectorialPage.AddCircularArc(ACenterX, ACenterY, ARadius,
- AStartAngle, AEndAngle: Double; AColor: TFPColor);
- var
- lCircularArc: TvCircularArc;
- begin
- lCircularArc := TvCircularArc.Create;
- lCircularArc.X := ACenterX;
- lCircularArc.Y := ACenterY;
- lCircularArc.Radius := ARadius;
- lCircularArc.StartAngle := AStartAngle;
- lCircularArc.EndAngle := AEndAngle;
- lCircularArc.Pen.Color := AColor;
- AddEntity(lCircularArc);
- end;
- procedure TvVectorialPage.AddEllipse(CenterX, CenterY, MajorHalfAxis,
- MinorHalfAxis, Angle: Double);
- var
- lEllipse: TvEllipse;
- begin
- lEllipse := TvEllipse.Create;
- lEllipse.X := CenterX;
- lEllipse.Y := CenterY;
- lEllipse.MajorHalfAxis := MajorHalfAxis;
- lEllipse.MinorHalfAxis := MinorHalfAxis;
- lEllipse.Angle := Angle;
- AddEntity(lEllipse);
- end;
- procedure TvVectorialPage.AddAlignedDimension(BaseLeft, BaseRight, DimLeft,
- DimRight: T3DPoint);
- var
- lDim: TvAlignedDimension;
- begin
- lDim := TvAlignedDimension.Create;
- lDim.BaseLeft := BaseLeft;
- lDim.BaseRight := BaseRight;
- lDim.DimensionLeft := DimLeft;
- lDim.DimensionRight := DimRight;
- AddEntity(lDim);
- end;
- { TvText }
- constructor TvText.Create;
- begin
- inherited Create;
- Value := TStringList.Create;
- end;
- destructor TvText.Destroy;
- begin
- Value.Free;
- inherited Destroy;
- end;
- function TvText.TryToSelect(APos: TPoint): TvFindEntityResult;
- var
- lProximityFactor: Integer;
- begin
- lProximityFactor := 5;
- if (APos.X > X - lProximityFactor) and (APos.X < X + lProximityFactor)
- and (APos.Y > Y - lProximityFactor) and (APos.Y < Y + lProximityFactor) then
- Result := vfrFound
- else Result := vfrNotFound;
- end;
- { TvEntity }
- constructor TvEntity.Create;
- begin
- Pen.Style := psSolid;
- Pen.Color := colBlack;
- Brush.Style := bsClear;
- Brush.Color := colBlue;
- end;
- procedure TvEntity.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
- begin
- ALeft := 0;
- ATop := 0;
- ARight := 0;
- ABottom := 0;
- end;
- procedure TvEntity.ExpandBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
- var
- lLeft, lTop, lRight, lBottom: Double;
- begin
- CalculateBoundingBox(lLeft, lTop, lRight, lBottom);
- if lLeft < ALeft then ALeft := lLeft;
- if lTop < ATop then ATop := lTop;
- if lRight > ARight then ARight := lRight;
- if lBottom > ABottom then ABottom := lBottom;
- end;
- function TvEntity.TryToSelect(APos: TPoint): TvFindEntityResult;
- begin
- Result := vfrNotFound;
- end;
- procedure TvEntity.Translate(ADeltaX, ADeltaY: Integer);
- begin
- X := X + ADeltaX;
- Y := Y + ADeltaY;
- end;
- { TvEllipse }
- procedure TvEllipse.CalculateBoundingRectangle;
- var
- t, tmp: Double;
- begin
- {
- To calculate the bounding rectangle we can do this:
- Ellipse equations:You could try using the parametrized equations for an ellipse rotated at an arbitrary angle:
- x = CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle)
- y = CenterY + MinorHalfAxis*sin(t)*cos(Angle) + MajorHalfAxis*cos(t)*sin(Angle)
- You can then differentiate and solve for gradient = 0:
- 0 = dx/dt = -MajorHalfAxis*sin(t)*cos(Angle) - MinorHalfAxis*cos(t)*sin(Angle)
- =>
- tan(t) = -MinorHalfAxis*tan(Angle)/MajorHalfAxis
- =>
- t = cotang(-MinorHalfAxis*tan(Angle)/MajorHalfAxis)
- On the other axis:
- 0 = dy/dt = b*cos(t)*cos(phi) - a*sin(t)*sin(phi)
- =>
- tan(t) = b*cot(phi)/a
- }
- t := cotan(-MinorHalfAxis*tan(Angle)/MajorHalfAxis);
- tmp := X + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle);
- BoundingRect.Right := Round(tmp);
- end;
- { TsWorksheet }
- {@@
- Constructor.
- }
- constructor TvVectorialDocument.Create;
- begin
- inherited Create;
- FPages := TFPList.Create;
- end;
- {@@
- Destructor.
- }
- destructor TvVectorialDocument.Destroy;
- begin
- Clear;
- FPages.Free;
- inherited Destroy;
- end;
- procedure TvVectorialDocument.Assign(ASource: TvVectorialDocument);
- //var
- // i: Integer;
- begin
- // Clear;
- //
- // for i := 0 to ASource.GetEntitiesCount - 1 do
- // Self.AddEntity(ASource.GetEntity(i));
- end;
- procedure TvVectorialDocument.AssignTo(ADest: TvVectorialDocument);
- begin
- ADest.Assign(Self);
- end;
- {@@
- Convenience method which creates the correct
- writer object for a given vector graphics document format.
- }
- function TvVectorialDocument.CreateVectorialWriter(AFormat: TvVectorialFormat): TvCustomVectorialWriter;
- var
- i: Integer;
- begin
- Result := nil;
- for i := 0 to Length(GvVectorialFormats) - 1 do
- if GvVectorialFormats[i].Format = AFormat then
- begin
- if GvVectorialFormats[i].WriterClass <> nil then
- Result := GvVectorialFormats[i].WriterClass.Create;
- Break;
- end;
- if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
- end;
- {@@
- Convenience method which creates the correct
- reader object for a given vector graphics document format.
- }
- function TvVectorialDocument.CreateVectorialReader(AFormat: TvVectorialFormat): TvCustomVectorialReader;
- var
- i: Integer;
- begin
- Result := nil;
- for i := 0 to Length(GvVectorialFormats) - 1 do
- if GvVectorialFormats[i].Format = AFormat then
- begin
- if GvVectorialFormats[i].ReaderClass <> nil then
- Result := GvVectorialFormats[i].ReaderClass.Create;
- Break;
- end;
- if Result = nil then raise Exception.Create('Unsupported vector graphics format.');
- end;
- {@@
- Writes the document to a file.
- If the file doesn't exist, it will be created.
- }
- procedure TvVectorialDocument.WriteToFile(AFileName: string; AFormat: TvVectorialFormat);
- var
- AWriter: TvCustomVectorialWriter;
- begin
- AWriter := CreateVectorialWriter(AFormat);
- try
- AWriter.WriteToFile(AFileName, Self);
- finally
- AWriter.Free;
- end;
- end;
- procedure TvVectorialDocument.WriteToFile(AFileName: string);
- var
- lFormat: TvVectorialFormat;
- begin
- lFormat := GetFormatFromExtension(ExtractFileExt(AFileName));
- WriteToFile(AFileName, lFormat);
- end;
- {@@
- Writes the document to a stream
- }
- procedure TvVectorialDocument.WriteToStream(AStream: TStream; AFormat: TvVectorialFormat);
- var
- AWriter: TvCustomVectorialWriter;
- begin
- AWriter := CreateVectorialWriter(AFormat);
- try
- AWriter.WriteToStream(AStream, Self);
- finally
- AWriter.Free;
- end;
- end;
- procedure TvVectorialDocument.WriteToStrings(AStrings: TStrings;
- AFormat: TvVectorialFormat);
- var
- AWriter: TvCustomVectorialWriter;
- begin
- AWriter := CreateVectorialWriter(AFormat);
- try
- AWriter.WriteToStrings(AStrings, Self);
- finally
- AWriter.Free;
- end;
- end;
- {@@
- Reads the document from a file.
- Any current contents in this object will be removed.
- }
- procedure TvVectorialDocument.ReadFromFile(AFileName: string;
- AFormat: TvVectorialFormat);
- var
- AReader: TvCustomVectorialReader;
- begin
- Self.Clear;
- AReader := CreateVectorialReader(AFormat);
- try
- AReader.ReadFromFile(AFileName, Self);
- finally
- AReader.Free;
- end;
- end;
- {@@
- Reads the document from a file. A variant that auto-detects the format from the extension and other factors.
- }
- procedure TvVectorialDocument.ReadFromFile(AFileName: string);
- var
- lFormat: TvVectorialFormat;
- begin
- lFormat := GetFormatFromExtension(ExtractFileExt(AFileName));
- ReadFromFile(AFileName, lFormat);
- end;
- {@@
- Reads the document from a stream.
- Any current contents in this object will be removed.
- }
- procedure TvVectorialDocument.ReadFromStream(AStream: TStream;
- AFormat: TvVectorialFormat);
- var
- AReader: TvCustomVectorialReader;
- begin
- Self.Clear;
- AReader := CreateVectorialReader(AFormat);
- try
- AReader.ReadFromStream(AStream, Self);
- finally
- AReader.Free;
- end;
- end;
- procedure TvVectorialDocument.ReadFromStrings(AStrings: TStrings;
- AFormat: TvVectorialFormat);
- var
- AReader: TvCustomVectorialReader;
- begin
- Self.Clear;
- AReader := CreateVectorialReader(AFormat);
- try
- AReader.ReadFromStrings(AStrings, Self);
- finally
- AReader.Free;
- end;
- end;
- class function TvVectorialDocument.GetFormatFromExtension(AFileName: string
- ): TvVectorialFormat;
- var
- lExt: string;
- begin
- lExt := ExtractFileExt(AFileName);
- if AnsiCompareText(lExt, STR_PDF_EXTENSION) = 0 then Result := vfPDF
- else if AnsiCompareText(lExt, STR_POSTSCRIPT_EXTENSION) = 0 then Result := vfPostScript
- else if AnsiCompareText(lExt, STR_SVG_EXTENSION) = 0 then Result := vfSVG
- else if AnsiCompareText(lExt, STR_CORELDRAW_EXTENSION) = 0 then Result := vfCorelDrawCDR
- else if AnsiCompareText(lExt, STR_WINMETAFILE_EXTENSION) = 0 then Result := vfWindowsMetafileWMF
- else if AnsiCompareText(lExt, STR_AUTOCAD_EXCHANGE_EXTENSION) = 0 then Result := vfDXF
- else if AnsiCompareText(lExt, STR_ENCAPSULATEDPOSTSCRIPT_EXTENSION) = 0 then Result := vfEncapsulatedPostScript
- else
- raise Exception.Create('TvVectorialDocument.GetFormatFromExtension: The extension (' + lExt + ') doesn''t match any supported formats.');
- end;
- function TvVectorialDocument.GetDetailedFileFormat(): string;
- begin
- end;
- procedure TvVectorialDocument.GuessDocumentSize();
- var
- i, j: Integer;
- lEntity: TvEntity;
- lLeft, lTop, lRight, lBottom: Double;
- CurPage: TvVectorialPage;
- begin
- lLeft := 0;
- lTop := 0;
- lRight := 0;
- lBottom := 0;
- for j := 0 to GetPageCount()-1 do
- begin
- CurPage := GetPage(j);
- for i := 0 to CurPage.GetEntitiesCount() - 1 do
- begin
- lEntity := CurPage.GetEntity(I);
- lEntity.ExpandBoundingBox(lLeft, lTop, lRight, lBottom);
- end;
- end;
- Width := lRight - lLeft;
- Height := lBottom - lTop;
- end;
- procedure TvVectorialDocument.GuessGoodZoomLevel(AScreenSize: Integer);
- begin
- ZoomLevel := AScreenSize / Height;
- end;
- function TvVectorialDocument.GetPage(AIndex: Integer): TvVectorialPage;
- begin
- Result := TvVectorialPage(FPages.Items[AIndex]);
- end;
- function TvVectorialDocument.GetPageCount: Integer;
- begin
- Result := FPages.Count;
- end;
- function TvVectorialDocument.GetCurrentPage: TvVectorialPage;
- begin
- if FCurrentPageIndex >= 0 then
- Result := GetPage(FCurrentPageIndex)
- else
- Result := nil;
- end;
- procedure TvVectorialDocument.SetCurrentPage(AIndex: Integer);
- begin
- FCurrentPageIndex := AIndex;
- end;
- function TvVectorialDocument.AddPage: TvVectorialPage;
- begin
- Result := TvVectorialPage.Create(Self);
- FPages.Add(Result);
- if FCurrentPageIndex < 0 then FCurrentPageIndex := FPages.Count-1;
- end;
- {@@
- Clears all data in the document
- }
- procedure TvVectorialDocument.Clear;
- begin
- end;
- { TvCustomVectorialReader }
- constructor TvCustomVectorialReader.Create;
- begin
- inherited Create;
- end;
- procedure TvCustomVectorialReader.ReadFromFile(AFileName: string; AData: TvVectorialDocument);
- var
- FileStream: TFileStream;
- begin
- FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
- try
- ReadFromStream(FileStream, AData);
- finally
- FileStream.Free;
- end;
- end;
- procedure TvCustomVectorialReader.ReadFromStream(AStream: TStream;
- AData: TvVectorialDocument);
- var
- AStringStream: TStringStream;
- AStrings: TStringList;
- begin
- AStringStream := TStringStream.Create('');
- AStrings := TStringList.Create;
- try
- AStringStream.CopyFrom(AStream, AStream.Size);
- AStringStream.Seek(0, soFromBeginning);
- AStrings.Text := AStringStream.DataString;
- ReadFromStrings(AStrings, AData);
- finally
- AStringStream.Free;
- AStrings.Free;
- end;
- end;
- procedure TvCustomVectorialReader.ReadFromStrings(AStrings: TStrings;
- AData: TvVectorialDocument);
- var
- AStringStream: TStringStream;
- begin
- AStringStream := TStringStream.Create('');
- try
- AStringStream.WriteString(AStrings.Text);
- AStringStream.Seek(0, soFromBeginning);
- ReadFromStream(AStringStream, AData);
- finally
- AStringStream.Free;
- end;
- end;
- { TsCustomSpreadWriter }
- constructor TvCustomVectorialWriter.Create;
- begin
- inherited Create;
- end;
- {@@
- Default file writting method.
- Opens the file and calls WriteToStream
- @param AFileName The output file name.
- If the file already exists it will be replaced.
- @param AData The Workbook to be saved.
- @see TsWorkbook
- }
- procedure TvCustomVectorialWriter.WriteToFile(AFileName: string; AData: TvVectorialDocument);
- var
- OutputFile: TFileStream;
- begin
- OutputFile := TFileStream.Create(AFileName, fmCreate or fmOpenWrite);
- try
- WriteToStream(OutputFile, AData);
- finally
- OutputFile.Free;
- end;
- end;
- {@@
- The default stream writer just uses WriteToStrings
- }
- procedure TvCustomVectorialWriter.WriteToStream(AStream: TStream;
- AData: TvVectorialDocument);
- var
- lStringList: TStringList;
- begin
- lStringList := TStringList.Create;
- try
- WriteToStrings(lStringList, AData);
- lStringList.SaveToStream(AStream);
- finally
- lStringList.Free;
- end;
- end;
- procedure TvCustomVectorialWriter.WriteToStrings(AStrings: TStrings;
- AData: TvVectorialDocument);
- begin
- end;
- { TPath }
- procedure TPath.Assign(ASource: TPath);
- begin
- Len := ASource.Len;
- Points := ASource.Points;
- PointsEnd := ASource.PointsEnd;
- CurPoint := ASource.CurPoint;
- Pen := ASource.Pen;
- Brush := ASource.Brush;
- ClipPath := ASource.ClipPath;
- ClipMode := ASource.ClipMode;
- end;
- procedure TPath.PrepareForSequentialReading;
- begin
- CurPoint := nil;
- end;
- function TPath.Next(): TPathSegment;
- begin
- if CurPoint = nil then Result := Points
- else Result := CurPoint.Next;
- CurPoint := Result;
- end;
- procedure TPath.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double);
- var
- lSegment: TPathSegment;
- l2DSegment: T2DSegment;
- lFirstValue: Boolean = True;
- begin
- inherited CalculateBoundingBox(ALeft, ATop, ARight, ABottom);
- PrepareForSequentialReading();
- lSegment := Next();
- while lSegment <> nil do
- begin
- if lSegment is T2DSegment then
- begin
- l2DSegment := T2DSegment(lSegment);
- if lFirstValue then
- begin
- ALeft := l2DSegment.X;
- ATop := l2DSegment.Y;
- ARight := l2DSegment.X;
- ABottom := l2DSegment.Y;
- lFirstValue := False;
- end
- else
- begin
- if l2DSegment.X < ALeft then ALeft := l2DSegment.X;
- if l2DSegment.Y < ATop then ATop := l2DSegment.Y;
- if l2DSegment.X > ARight then ARight := l2DSegment.X;
- if l2DSegment.Y > ABottom then ABottom := l2DSegment.Y;
- end;
- end;
- lSegment := Next();
- end;
- end;
- procedure TPath.AppendSegment(ASegment: TPathSegment);
- var
- L: Integer;
- begin
- // Check if we are the first segment in the tmp path
- if PointsEnd = nil then
- begin
- if Len <> 0 then
- Exception.Create('[TPath.AppendSegment] Assertion failed Len <> 0 with PointsEnd = nil');
- Points := ASegment;
- PointsEnd := ASegment;
- Len := 1;
- Exit;
- end;
- L := Len;
- Inc(Len);
- // Adds the element to the end of the list
- PointsEnd.Next := ASegment;
- ASegment.Previous := PointsEnd;
- PointsEnd := ASegment;
- end;
- finalization
- SetLength(GvVectorialFormats, 0);
- end.
|