diff --git a/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas b/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas index 5f9126e3d..0148ca01e 100644 --- a/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas +++ b/applications/fpvviewer/fpvectorialsrc/dxfvectorialreader.pas @@ -148,22 +148,22 @@ const // Valid for DXF up to AutoCad 2004, after that RGB is available AUTOCAD_COLOR_PALETTE: array[0..15] of TFPColor = ( - (Red: $0000; Green: $0000; Blue: $0000; Alpha: FPValphaOpaque), // 0 - Black - (Red: $0000; Green: $0000; Blue: $8080; Alpha: FPValphaOpaque), // 1 - Dark blue - (Red: $0000; Green: $8080; Blue: $0000; Alpha: FPValphaOpaque), // 2 - Dark green - (Red: $0000; Green: $8080; Blue: $8080; Alpha: FPValphaOpaque), // 3 - Dark cyan - (Red: $8080; Green: $0000; Blue: $0000; Alpha: FPValphaOpaque), // 4 - Dark red - (Red: $8080; Green: $0000; Blue: $8080; Alpha: FPValphaOpaque), // 5 - Dark Magenta - (Red: $8080; Green: $8080; Blue: $0000; Alpha: FPValphaOpaque), // 6 - Dark - (Red: $c0c0; Green: $c0c0; Blue: $c0c0; Alpha: FPValphaOpaque), // 7 - Light Gray - (Red: $8080; Green: $8080; Blue: $8080; Alpha: FPValphaOpaque), // 8 - Medium Gray - (Red: $0000; Green: $0000; Blue: $ffff; Alpha: FPValphaOpaque), // 9 - Light blue - (Red: $0000; Green: $ffff; Blue: $0000; Alpha: FPValphaOpaque), // 10 - Light green - (Red: $0000; Green: $ffff; Blue: $ffff; Alpha: FPValphaOpaque), // 11 - Light cyan - (Red: $ffff; Green: $0000; Blue: $0000; Alpha: FPValphaOpaque), // 12 - Light red - (Red: $ffff; Green: $0000; Blue: $ffff; Alpha: FPValphaOpaque), // 13 - Light Magenta - (Red: $ffff; Green: $ffff; Blue: $0000; Alpha: FPValphaOpaque), // 14 - Light Yellow - (Red: $ffff; Green: $ffff; Blue: $ffff; Alpha: FPValphaOpaque) // 15 - White + (Red: $0000; Green: $0000; Blue: $0000; Alpha: alphaOpaque), // 0 - Black + (Red: $0000; Green: $0000; Blue: $8080; Alpha: alphaOpaque), // 1 - Dark blue + (Red: $0000; Green: $8080; Blue: $0000; Alpha: alphaOpaque), // 2 - Dark green + (Red: $0000; Green: $8080; Blue: $8080; Alpha: alphaOpaque), // 3 - Dark cyan + (Red: $8080; Green: $0000; Blue: $0000; Alpha: alphaOpaque), // 4 - Dark red + (Red: $8080; Green: $0000; Blue: $8080; Alpha: alphaOpaque), // 5 - Dark Magenta + (Red: $8080; Green: $8080; Blue: $0000; Alpha: alphaOpaque), // 6 - Dark + (Red: $c0c0; Green: $c0c0; Blue: $c0c0; Alpha: alphaOpaque), // 7 - Light Gray + (Red: $8080; Green: $8080; Blue: $8080; Alpha: alphaOpaque), // 8 - Medium Gray + (Red: $0000; Green: $0000; Blue: $ffff; Alpha: alphaOpaque), // 9 - Light blue + (Red: $0000; Green: $ffff; Blue: $0000; Alpha: alphaOpaque), // 10 - Light green + (Red: $0000; Green: $ffff; Blue: $ffff; Alpha: alphaOpaque), // 11 - Light cyan + (Red: $ffff; Green: $0000; Blue: $0000; Alpha: alphaOpaque), // 12 - Light red + (Red: $ffff; Green: $0000; Blue: $ffff; Alpha: alphaOpaque), // 13 - Light Magenta + (Red: $ffff; Green: $ffff; Blue: $0000; Alpha: alphaOpaque), // 14 - Light Yellow + (Red: $ffff; Green: $ffff; Blue: $ffff; Alpha: alphaOpaque) // 15 - White ); { TDXFToken } diff --git a/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas b/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas index 8e8b75f9c..6eb3bd78e 100644 --- a/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas +++ b/applications/fpvviewer/fpvectorialsrc/fpvectorial.pas @@ -44,6 +44,8 @@ const STR_ENCAPSULATEDPOSTSCRIPT_EXTENSION = '.eps'; type + { Pen, Brush and Font } + TvPen = record Color: TFPColor; Style: TFPPenStyle; @@ -55,11 +57,20 @@ type Style: TFPBrushStyle; end; -const - FPValphaTransparent = $00; - FPValphaOpaque = $FF; + 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. + } + Orientation: Double; + end; + + { Coordinates and polyline segments } -type T3DPoint = record X, Y, Z: Double; end; @@ -128,60 +139,47 @@ type X3, Y3, Z3: Double; end; - TPath = class + { Now all elements } + + {@@ + All elements should derive from TvEntity, regardless of whatever properties + they might contain. + } + + TvEntity = class + public + {@@ 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; + end; + + 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 - {@@ The global Pen for the entire path. This Pen might be overriden by - individual elements of the polyline. } - Pen: TvPen; - {@@ Sets a Brush to paint the inner area inside the path. - There is no inner area if Brush.Style = bsClear, which is the default. } - Brush: TvBrush; - constructor Create(); - procedure Assign(APath: TPath); + procedure Assign(ASource: TPath); procedure PrepareForSequentialReading; function Next(): TPathSegment; 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. - } - Orientation: Double; - end; - {@@ TvText represents a text in memory. At the moment fonts are unsupported, only simple texts up to 255 chars are supported. } - TvText = class + TvText = class(TvEntity) public X, Y, Z: Double; // Z is ignored in 2D formats Value: utf8string; Font: TvFont; end; - {@@ - } - - { TvEntity } - - TvEntity = class - public - Pen: TvPen; - Brush: TvBrush; - constructor Create; virtual; - end; - {@@ } TvCircle = class(TvEntity) @@ -200,9 +198,6 @@ type {@@ } - - { TvEllipse } - TvEllipse = class(TvEntity) public // Mandatory fields @@ -215,6 +210,8 @@ type end; {@@ + The brush has no effect in this class + DimensionLeft ---text--- DimensionRight | | | | BaseRight @@ -239,8 +236,6 @@ type TvVectorialDocument = class private - FPaths: TFPList; - FTexts: TFPList; FEntities: TFPList; FTmpPath: TPath; FTmpText: TvText; @@ -255,6 +250,8 @@ type { Base methods } constructor Create; 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); @@ -268,16 +265,13 @@ type { Data reading methods } function GetPath(ANum: Cardinal): TPath; function GetPathCount: Integer; - function GetText(ANum: Cardinal): TvText; - function GetTextCount: Integer; function GetEntity(ANum: Cardinal): TvEntity; - function GetEntityCount: Integer; + function GetEntitiesCount: Integer; { Data removing methods } procedure Clear; - procedure RemoveAllPaths; - procedure RemoveAllTexts; { Data writing methods } - procedure AddPath(APath: TPath); + procedure AddEntity(AEntity: TvEntity); + procedure AddPathCopyMem(APath: TPath); procedure StartPath(AX, AY: Double); overload; procedure StartPath(); overload; procedure AddMoveToPath(AX, AY: Double); @@ -517,8 +511,6 @@ constructor TvVectorialDocument.Create; begin inherited Create; - FPaths := TFPList.Create; - FTexts := TFPList.Create; FEntities := TFPList.Create; FTmpPath := TPath.Create; end; @@ -530,40 +522,35 @@ destructor TvVectorialDocument.Destroy; begin Clear; - FPaths.Free; - FTexts.Free; FEntities.Free; inherited Destroy; end; -{@@ - Clears the list of Vectors and releases their memory. -} -procedure TvVectorialDocument.RemoveAllPaths; +procedure TvVectorialDocument.Assign(ASource: TvVectorialDocument); +var + i: Integer; begin -// FPaths.ForEachCall(RemoveCallback, nil); - FPaths.Clear; + Clear; + + for i := 0 to ASource.GetEntitiesCount - 1 do + Self.AddEntity(ASource.GetEntity(i)); end; -procedure TvVectorialDocument.RemoveAllTexts; +procedure TvVectorialDocument.AssignTo(ADest: TvVectorialDocument); begin -// FTexts.ForEachCall(RemoveCallback, nil); - FTexts.Clear; + ADest.Assign(Self); end; -procedure TvVectorialDocument.AddPath(APath: TPath); +procedure TvVectorialDocument.AddPathCopyMem(APath: TPath); var lPath: TPath; Len: Integer; begin lPath := TPath.Create; lPath.Assign(APath); - FPaths.Add(Pointer(lPath)); + AddEntity(lPath); //WriteLn(':>TvVectorialDocument.AddPath 1 Len = ', Len); - //WriteLn(':>TvVectorialDocument.AddPath 2'); - //WriteLn(':>TvVectorialDocument.AddPath 3'); - //WriteLn(':>TvVectorialDocument.AddPath 4'); end; {@@ @@ -746,7 +733,7 @@ end; procedure TvVectorialDocument.EndPath(); begin if FTmPPath.Len = 0 then Exit; - AddPath(FTmPPath); + AddPathCopyMem(FTmPPath); ClearTmpPath(); end; @@ -761,7 +748,7 @@ begin lText.Z := AZ; lText.Font.Name := FontName; lText.Font.Size := FontSize; - FTexts.Add(lText); + AddEntity(lText); end; procedure TvVectorialDocument.AddText(AX, AY, AZ: Double; AStr: utf8string); @@ -778,7 +765,7 @@ begin lCircle.CenterY := ACenterY; lCircle.CenterZ := ACenterZ; lCircle.Radius := ARadius; - FEntities.Add(lCircle); + AddEntity(lCircle); end; procedure TvVectorialDocument.AddCircularArc(ACenterX, ACenterY, ACenterZ, @@ -794,7 +781,7 @@ begin lCircularArc.StartAngle := AStartAngle; lCircularArc.EndAngle := AEndAngle; lCircularArc.Pen.Color := AColor; - FEntities.Add(lCircularArc); + AddEntity(lCircularArc); end; procedure TvVectorialDocument.AddEllipse(CenterX, CenterY, CenterZ, @@ -809,7 +796,15 @@ begin lEllipse.MajorHalfAxis := MajorHalfAxis; lEllipse.MinorHalfAxis := MinorHalfAxis; lEllipse.Angle := Angle; - FEntities.Add(lEllipse); + AddEntity(lEllipse); +end; + +{@@ + Don't free the passed TvText because it will be added directly to the list +} +procedure TvVectorialDocument.AddEntity(AEntity: TvEntity); +begin + FEntities.Add(Pointer(AEntity)); end; procedure TvVectorialDocument.AddAlignedDimension(BaseLeft, BaseRight, @@ -822,7 +817,7 @@ begin lDim.BaseRight := BaseRight; lDim.DimensionLeft := DimLeft; lDim.DimensionRight := DimRight; - FEntities.Add(lDim); + AddEntity(lDim); end; {@@ @@ -1062,31 +1057,32 @@ begin end; function TvVectorialDocument.GetPath(ANum: Cardinal): TPath; +var + i: Integer; + Index: Integer = - 1; begin - if ANum >= FPaths.Count then raise Exception.Create('TvVectorialDocument.GetPath: Path number out of bounds'); + Result := nil; - if FPaths.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetPath: Invalid Path number'); + if ANum >= FEntities.Count then raise Exception.Create('TvVectorialDocument.GetPath: Path number out of bounds'); - Result := TPath(FPaths.Items[ANum]); + for i := 0 to FEntities.Count - 1 do + begin + if TvEntity(FEntities.Items[i]) is TPath then + begin + Inc(Index); + if Index = ANum then Result := TPath(FEntities.Items[i]); + end; + end; end; function TvVectorialDocument.GetPathCount: Integer; +var + i: Integer; begin - Result := FPaths.Count; -end; + Result := 0; -function TvVectorialDocument.GetText(ANum: Cardinal): TvText; -begin - if ANum >= FTexts.Count then raise Exception.Create('TvVectorialDocument.GetText: Text number out of bounds'); - - if FTexts.Items[ANum] = nil then raise Exception.Create('TvVectorialDocument.GetText: Invalid Text number'); - - Result := TvText(FTexts.Items[ANum]); -end; - -function TvVectorialDocument.GetTextCount: Integer; -begin - Result := FTexts.Count; + for i := 0 to FEntities.Count - 1 do + if TvEntity(FEntities.Items[i]) is TPath then Inc(Result); end; function TvVectorialDocument.GetEntity(ANum: Cardinal): TvEntity; @@ -1098,7 +1094,7 @@ begin Result := TvEntity(FEntities.Items[ANum]); end; -function TvVectorialDocument.GetEntityCount: Integer; +function TvVectorialDocument.GetEntitiesCount: Integer; begin Result := FEntities.Count; end; @@ -1108,8 +1104,7 @@ end; } procedure TvVectorialDocument.Clear; begin - RemoveAllPaths(); - RemoveAllTexts(); + FEntities.Clear(); end; { TvCustomVectorialReader } @@ -1220,20 +1215,14 @@ end; { TPath } -constructor TPath.Create(); +procedure TPath.Assign(ASource: TPath); begin - Brush.Style := bsClear; - inherited Create(); -end; - -procedure TPath.Assign(APath: TPath); -begin - Len := APath.Len; - Points := APath.Points; - PointsEnd := APath.PointsEnd; - CurPoint := APath.CurPoint; - Pen := APath.Pen; - Brush := APath.Brush; + Len := ASource.Len; + Points := ASource.Points; + PointsEnd := ASource.PointsEnd; + CurPoint := ASource.CurPoint; + Pen := ASource.Pen; + Brush := ASource.Brush; end; procedure TPath.PrepareForSequentialReading; diff --git a/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas b/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas index b240f388d..35c68fb76 100644 --- a/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas +++ b/applications/fpvviewer/fpvectorialsrc/fpvtocanvas.pas @@ -10,23 +10,22 @@ uses Classes, SysUtils, Math, {$ifdef USE_LCL_CANVAS} Graphics, LCLIntf, - {$else} - fpcanvas, {$endif} + fpcanvas, fpimage, fpvectorial; procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument; - {$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} + ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); -procedure DrawFPVPathsToCanvas(ASource: TvVectorialDocument; - {$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} +procedure DrawFPVPathToCanvas(ASource: TvVectorialDocument; CurPath: TPath; + ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); -procedure DrawFPVEntitiesToCanvas(ASource: TvVectorialDocument; - {$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} +procedure DrawFPVEntityToCanvas(ASource: TvVectorialDocument; CurEntity: TvEntity; + ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); -procedure DrawFPVTextToCanvas(ASource: TvVectorialDocument; - {$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} +procedure DrawFPVTextToCanvas(ASource: TvVectorialDocument; CurText: TvText; + ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); implementation @@ -47,17 +46,16 @@ begin end; procedure DrawRotatedEllipse( - {$ifdef USE_LCL_CANVAS} - ADest: TCanvas; - {$else} ADest: TFPCustomCanvas; - {$endif} CurEllipse: TvEllipse; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); var PointList: array[0..6] of TPoint; f: TPoint; dk, x1, x2, y1, y2: Integer; + {$ifdef USE_LCL_CANVAS} + ALCLDest: TCanvas absolute ADest; + {$endif} begin {$ifdef USE_LCL_CANVAS} CurEllipse.CalculateBoundingRectangle(); @@ -82,7 +80,7 @@ begin // Conrollpoint of secondpart endpoint PointList[6] := PointList[0]; // Endpoint of // Back to the startpoint - ADest.PolyBezier(Pointlist[0]); + ALCLDest.PolyBezier(Pointlist[0]); {$endif} end; @@ -102,24 +100,32 @@ end; } {.$define FPVECTORIAL_TOCANVAS_DEBUG} procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument; - {$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} + ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); +var + i: Integer; + CurEntity: TvEntity; begin {$ifdef FPVECTORIAL_TOCANVAS_DEBUG} WriteLn(':>DrawFPVectorialToCanvas'); {$endif} - DrawFPVPathsToCanvas(ASource, ADest, ADestX, ADestY, AMulX, AMulY); - DrawFPVEntitiesToCanvas(ASource, ADest, ADestX, ADestY, AMulX, AMulY); - DrawFPVTextToCanvas(ASource, ADest, ADestX, ADestY, AMulX, AMulY); + for i := 0 to ASource.GetEntitiesCount - 1 do + begin + CurEntity := ASource.GetEntity(i); + + if CurEntity is TPath then DrawFPVPathToCanvas(ASource, TPath(CurEntity), ADest, ADestX, ADestY, AMulX, AMulY) + else if CurEntity is TvText then DrawFPVTextToCanvas(ASource, TvText(CurEntity), ADest, ADestX, ADestY, AMulX, AMulY) + else DrawFPVEntityToCanvas(ASource, CurEntity, ADest, ADestX, ADestY, AMulX, AMulY); + end; {$ifdef FPVECTORIALDEBUG} WriteLn(': 0 then + begin} + FinalStartAngle := CurArc.StartAngle; + FinalEndAngle := CurArc.EndAngle; + {end + else // AMulY is negative begin - CurCircle := CurEntity as TvCircle; - ADest.Ellipse( - CoordToCanvasX(CurCircle.CenterX - CurCircle.Radius), - CoordToCanvasY(CurCircle.CenterY - CurCircle.Radius), - CoordToCanvasX(CurCircle.CenterX + CurCircle.Radius), - CoordToCanvasY(CurCircle.CenterY + CurCircle.Radius) - ); - end - else if CurEntity is TvEllipse then + // Inverting the angles generates the correct result for Y axis inversion + if CurArc.EndAngle = 0 then FinalStartAngle := 0 + else FinalStartAngle := 360 - 1* CurArc.EndAngle; + if CurArc.StartAngle = 0 then FinalEndAngle := 0 + else FinalEndAngle := 360 - 1* CurArc.StartAngle; + end;} + IntStartAngle := Round(16*FinalStartAngle); + IntAngleLength := Round(16*(FinalEndAngle - FinalStartAngle)); + // On Gtk2 and Carbon, the Left really needs to be to the Left of the Right position + // The same for the Top and Bottom + // On Windows it works fine either way + // On Gtk2 if the positions are inverted then the arcs are screwed up + // In Carbon if the positions are inverted, then the arc is inverted + if BoundsLeft > BoundsRight then begin - CurEllipse := CurEntity as TvEllipse; - DrawRotatedEllipse(ADest, CurEllipse); - end - else if CurEntity is TvCircularArc then + IntTmp := BoundsLeft; + BoundsLeft := BoundsRight; + BoundsRight := IntTmp; + end; + if BoundsTop > BoundsBottom then begin - CurArc := CurEntity as TvCircularArc; - {$ifdef USE_LCL_CANVAS} - // ToDo: Consider a X axis inversion - // If the Y axis is inverted, then we need to mirror our angles as well - BoundsLeft := CoordToCanvasX(CurArc.CenterX - CurArc.Radius); - BoundsTop := CoordToCanvasY(CurArc.CenterY - CurArc.Radius); - BoundsRight := CoordToCanvasX(CurArc.CenterX + CurArc.Radius); - BoundsBottom := CoordToCanvasY(CurArc.CenterY + CurArc.Radius); - {if AMulY > 0 then - begin} - FinalStartAngle := CurArc.StartAngle; - FinalEndAngle := CurArc.EndAngle; - {end - else // AMulY is negative - begin - // Inverting the angles generates the correct result for Y axis inversion - if CurArc.EndAngle = 0 then FinalStartAngle := 0 - else FinalStartAngle := 360 - 1* CurArc.EndAngle; - if CurArc.StartAngle = 0 then FinalEndAngle := 0 - else FinalEndAngle := 360 - 1* CurArc.StartAngle; - end;} - IntStartAngle := Round(16*FinalStartAngle); - IntAngleLength := Round(16*(FinalEndAngle - FinalStartAngle)); - // On Gtk2 and Carbon, the Left really needs to be to the Left of the Right position - // The same for the Top and Bottom - // On Windows it works fine either way - // On Gtk2 if the positions are inverted then the arcs are screwed up - // In Carbon if the positions are inverted, then the arc is inverted - if BoundsLeft > BoundsRight then - begin - IntTmp := BoundsLeft; - BoundsLeft := BoundsRight; - BoundsRight := IntTmp; - end; - if BoundsTop > BoundsBottom then - begin - IntTmp := BoundsTop; - BoundsTop := BoundsBottom; - BoundsBottom := IntTmp; - end; - // Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: Integer); - {$ifdef FPVECTORIALDEBUG} - WriteLn(Format('Drawing Arc Center=%f,%f Radius=%f StartAngle=%f AngleLength=%f', - [CurArc.CenterX, CurArc.CenterY, CurArc.Radius, IntStartAngle/16, IntAngleLength/16])); - {$endif} - ADest.Pen.FPColor := CurArc.Pen.Color; - ADest.Arc( - BoundsLeft, BoundsTop, BoundsRight, BoundsBottom, - IntStartAngle, IntAngleLength - ); - ADest.Pen.Color := clBlack; - // Debug info + IntTmp := BoundsTop; + BoundsTop := BoundsBottom; + BoundsBottom := IntTmp; + end; + // Arc(ALeft, ATop, ARight, ABottom, Angle16Deg, Angle16DegLength: Integer); + {$ifdef FPVECTORIALDEBUG} + WriteLn(Format('Drawing Arc Center=%f,%f Radius=%f StartAngle=%f AngleLength=%f', + [CurArc.CenterX, CurArc.CenterY, CurArc.Radius, IntStartAngle/16, IntAngleLength/16])); + {$endif} + ADest.Pen.FPColor := CurArc.Pen.Color; + ALCLDest.Arc( + BoundsLeft, BoundsTop, BoundsRight, BoundsBottom, + IntStartAngle, IntAngleLength + ); + ADest.Pen.FPColor := colBlack; + // Debug info // {$define FPVECTORIALDEBUG} // {$ifdef FPVECTORIALDEBUG} // WriteLn(Format('Drawing Arc x1y1=%d,%d x2y2=%d,%d start=%d end=%d', // [BoundsLeft, BoundsTop, BoundsRight, BoundsBottom, IntStartAngle, IntAngleLength])); // {$endif} { ADest.TextOut(CoordToCanvasX(CurArc.CenterX), CoordToCanvasY(CurArc.CenterY), - Format('R=%d S=%d L=%d', [Round(CurArc.Radius*AMulX), Round(FinalStartAngle), - Abs(Round((FinalEndAngle - FinalStartAngle)))])); - ADest.Pen.Color := TColor($DDDDDD); - ADest.Rectangle( - BoundsLeft, BoundsTop, BoundsRight, BoundsBottom); - ADest.Pen.Color := clBlack;} - {$endif} - end - else if CurEntity is TvAlignedDimension then + Format('R=%d S=%d L=%d', [Round(CurArc.Radius*AMulX), Round(FinalStartAngle), + Abs(Round((FinalEndAngle - FinalStartAngle)))])); + ADest.Pen.Color := TColor($DDDDDD); + ADest.Rectangle( + BoundsLeft, BoundsTop, BoundsRight, BoundsBottom); + ADest.Pen.Color := clBlack;} + {$endif} + end + else if CurEntity is TvAlignedDimension then + begin + CurDim := CurEntity as TvAlignedDimension; + // + // Draws this shape: + // vertical horizontal + // ___ + // | | or ---| X cm + // | --| + // Which marks the dimension + ADest.MoveTo(CoordToCanvasX(CurDim.BaseRight.X), CoordToCanvasY(CurDim.BaseRight.Y)); + ADest.LineTo(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y)); + ADest.LineTo(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y)); + ADest.LineTo(CoordToCanvasX(CurDim.BaseLeft.X), CoordToCanvasY(CurDim.BaseLeft.Y)); + // Now the arrows + // horizontal + SetLength(Points, 3); + if CurDim.DimensionRight.Y = CurDim.DimensionLeft.Y then begin - CurDim := CurEntity as TvAlignedDimension; - // - // Draws this shape: - // vertical horizontal - // ___ - // | | or ---| X cm - // | --| - // Which marks the dimension - ADest.MoveTo(CoordToCanvasX(CurDim.BaseRight.X), CoordToCanvasY(CurDim.BaseRight.Y)); - ADest.LineTo(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y)); - ADest.LineTo(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y)); - ADest.LineTo(CoordToCanvasX(CurDim.BaseLeft.X), CoordToCanvasY(CurDim.BaseLeft.Y)); - // Now the arrows - // horizontal - SetLength(Points, 3); - if CurDim.DimensionRight.Y = CurDim.DimensionLeft.Y then + ADest.Brush.FPColor := colBlack; + ADest.Brush.Style := bsSolid; + // Left arrow + Points[0] := Point(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y)); + Points[1] := Point(Points[0].X + 7, Points[0].Y - 3); + Points[2] := Point(Points[0].X + 7, Points[0].Y + 3); + ADest.Polygon(Points); + // Right arrow + Points[0] := Point(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y)); + Points[1] := Point(Points[0].X - 7, Points[0].Y - 3); + Points[2] := Point(Points[0].X - 7, Points[0].Y + 3); + ADest.Polygon(Points); + ADest.Brush.Style := bsClear; + // Dimension text + Points[0].X := CoordToCanvasX((CurDim.DimensionLeft.X+CurDim.DimensionRight.X)/2); + Points[0].Y := CoordToCanvasY(CurDim.DimensionLeft.Y); + LowerDim.X := CurDim.DimensionRight.X-CurDim.DimensionLeft.X; + ADest.Font.Size := 10; + ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [LowerDim.X])); + end + else + begin + ADest.Brush.FPColor := colBlack; + ADest.Brush.Style := bsSolid; + // There is no upper/lower preference for DimensionLeft/Right, so we need to check + if CurDim.DimensionLeft.Y > CurDim.DimensionRight.Y then begin - {$ifdef USE_LCL_CANVAS} - ADest.Brush.Color := clBlack; - {$else} - ADest.Brush.FPColor := colBlack; - {$endif} - ADest.Brush.Style := bsSolid; - // Left arrow - Points[0] := Point(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y)); - Points[1] := Point(Points[0].X + 7, Points[0].Y - 3); - Points[2] := Point(Points[0].X + 7, Points[0].Y + 3); - ADest.Polygon(Points); - // Right arrow - Points[0] := Point(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y)); - Points[1] := Point(Points[0].X - 7, Points[0].Y - 3); - Points[2] := Point(Points[0].X - 7, Points[0].Y + 3); - ADest.Polygon(Points); - ADest.Brush.Style := bsClear; - // Dimension text - Points[0].X := CoordToCanvasX((CurDim.DimensionLeft.X+CurDim.DimensionRight.X)/2); - Points[0].Y := CoordToCanvasY(CurDim.DimensionLeft.Y); - LowerDim.X := CurDim.DimensionRight.X-CurDim.DimensionLeft.X; - ADest.Font.Size := 10; - ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [LowerDim.X])); + UpperDim := CurDim.DimensionLeft; + LowerDim := CurDim.DimensionRight; end else begin - {$ifdef USE_LCL_CANVAS} - ADest.Brush.Color := clBlack; - {$else} - ADest.Brush.FPColor := colBlack; - {$endif} - ADest.Brush.Style := bsSolid; - // There is no upper/lower preference for DimensionLeft/Right, so we need to check - if CurDim.DimensionLeft.Y > CurDim.DimensionRight.Y then - begin - UpperDim := CurDim.DimensionLeft; - LowerDim := CurDim.DimensionRight; - end - else - begin - UpperDim := CurDim.DimensionRight; - LowerDim := CurDim.DimensionLeft; - end; - // Upper arrow - Points[0] := Point(CoordToCanvasX(UpperDim.X), CoordToCanvasY(UpperDim.Y)); - Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y - Round(AMulY*3)); - Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y - Round(AMulY*3)); - ADest.Polygon(Points); - // Lower arrow - Points[0] := Point(CoordToCanvasX(LowerDim.X), CoordToCanvasY(LowerDim.Y)); - Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y + Round(AMulY*3)); - Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y + Round(AMulY*3)); - ADest.Polygon(Points); - ADest.Brush.Style := bsClear; - // Dimension text - Points[0].X := CoordToCanvasX(CurDim.DimensionLeft.X); - Points[0].Y := CoordToCanvasY((CurDim.DimensionLeft.Y+CurDim.DimensionRight.Y)/2); - LowerDim.Y := CurDim.DimensionRight.Y-CurDim.DimensionLeft.Y; - if LowerDim.Y < 0 then LowerDim.Y := -1 * LowerDim.Y; - ADest.Font.Size := 10; - ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [LowerDim.Y])); + UpperDim := CurDim.DimensionRight; + LowerDim := CurDim.DimensionLeft; end; - SetLength(Points, 0); -{ // Debug info - ADest.TextOut(CoordToCanvasX(CurDim.BaseRight.X), CoordToCanvasY(CurDim.BaseRight.Y), 'BR'); - ADest.TextOut(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y), 'DR'); - ADest.TextOut(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y), 'DL'); - ADest.TextOut(CoordToCanvasX(CurDim.BaseLeft.X), CoordToCanvasY(CurDim.BaseLeft.Y), 'BL');} + // Upper arrow + Points[0] := Point(CoordToCanvasX(UpperDim.X), CoordToCanvasY(UpperDim.Y)); + Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y - Round(AMulY*3)); + Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y - Round(AMulY*3)); + ADest.Polygon(Points); + // Lower arrow + Points[0] := Point(CoordToCanvasX(LowerDim.X), CoordToCanvasY(LowerDim.Y)); + Points[1] := Point(Points[0].X + Round(AMulX), Points[0].Y + Round(AMulY*3)); + Points[2] := Point(Points[0].X - Round(AMulX), Points[0].Y + Round(AMulY*3)); + ADest.Polygon(Points); + ADest.Brush.Style := bsClear; + // Dimension text + Points[0].X := CoordToCanvasX(CurDim.DimensionLeft.X); + Points[0].Y := CoordToCanvasY((CurDim.DimensionLeft.Y+CurDim.DimensionRight.Y)/2); + LowerDim.Y := CurDim.DimensionRight.Y-CurDim.DimensionLeft.Y; + if LowerDim.Y < 0 then LowerDim.Y := -1 * LowerDim.Y; + ADest.Font.Size := 10; + ADest.TextOut(Points[0].X, Points[0].Y, Format('%.1f', [LowerDim.Y])); end; + SetLength(Points, 0); +{ // Debug info + ADest.TextOut(CoordToCanvasX(CurDim.BaseRight.X), CoordToCanvasY(CurDim.BaseRight.Y), 'BR'); + ADest.TextOut(CoordToCanvasX(CurDim.DimensionRight.X), CoordToCanvasY(CurDim.DimensionRight.Y), 'DR'); + ADest.TextOut(CoordToCanvasX(CurDim.DimensionLeft.X), CoordToCanvasY(CurDim.DimensionLeft.Y), 'DL'); + ADest.TextOut(CoordToCanvasX(CurDim.BaseLeft.X), CoordToCanvasY(CurDim.BaseLeft.Y), 'BL');} end; end; -procedure DrawFPVTextToCanvas(ASource: TvVectorialDocument; - {$ifdef USE_LCL_CANVAS}ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} +procedure DrawFPVTextToCanvas(ASource: TvVectorialDocument; CurText: TvText; + ADest: TFPCustomCanvas; ADestX: Integer = 0; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); function CoordToCanvasX(ACoord: Double): Integer; @@ -458,29 +445,20 @@ procedure DrawFPVTextToCanvas(ASource: TvVectorialDocument; var i: Integer; - // For text - CurText: TvText; + {$ifdef USE_LCL_CANVAS} + ALCLDest: TCanvas absolute ADest; + {$endif} // LowerDim: T3DPoint; begin - // Draws all text - for i := 0 to ASource.GetTextCount - 1 do - begin - CurText := ASource.GetText(i); + ADest.Font.Size := Round(AmulX * CurText.Font.Size); + ADest.Pen.Style := psSolid; + ADest.Pen.FPColor := colBlack; + ADest.Brush.Style := bsClear; + ALCLDest.Font.Orientation := Round(CurText.Font.Orientation * 16); - ADest.Font.Size := Round(AmulX * CurText.Font.Size); - ADest.Pen.Style := psSolid; - {$ifdef USE_LCL_CANVAS} - ADest.Pen.Color := clBlack; - {$else} - ADest.Pen.FPColor := colBlack; - {$endif} - ADest.Brush.Style := bsClear; - ADest.Font.Orientation := Round(CurText.Font.Orientation * 16); - - LowerDim.Y := CurText.Y + CurText.Font.Size; - ADest.TextOut(CoordToCanvasX(CurText.X), CoordToCanvasY(LowerDim.Y), CurText.Value); - end; + LowerDim.Y := CurText.Y + CurText.Font.Size; + ADest.TextOut(CoordToCanvasX(CurText.X), CoordToCanvasY(LowerDim.Y), CurText.Value); end; end. diff --git a/applications/fpvviewer/fpvectorialsrc/fpvutils.pas b/applications/fpvviewer/fpvectorialsrc/fpvutils.pas index e8662d93f..d88393ad8 100644 --- a/applications/fpvviewer/fpvectorialsrc/fpvutils.pas +++ b/applications/fpvviewer/fpvectorialsrc/fpvutils.pas @@ -29,6 +29,7 @@ function FPColorToRGBHexString(AColor: TFPColor): string; function RGBToFPColor(AR, AG, AB: byte): TFPColor; inline; // Other routine function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline; +function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer; function SeparateString(AString: string; ASeparator: char): T10Strings; implementation @@ -54,12 +55,26 @@ end; points upwards in FPVectorial and downwards in TCanvas. The X axis doesn't change. The fix is trivial and requires only the Height of the Canvas as extra info. + + @param AHeight Should receive TCanvas.Height } function CanvasCoordsToFPVectorial(AY: Integer; AHeight: Integer): Integer; inline; begin Result := AHeight - AY; end; +{@@ + LCL Text is positioned based on the top-left corner of the text. + Besides that, one also needs to take the general coordinate change into account too. + + @param ACanvasHeight Should receive TCanvas.Height + @param ATextHeight Should receive TFont.Size +} +function CanvasTextPosToFPVectorial(AY: Integer; ACanvasHeight, ATextHeight: Integer): Integer; +begin + Result := CanvasCoordsToFPVectorial(AY, ACanvasHeight) - ATextHeight; +end; + {@@ Reads a string and separates it in substring using ASeparator to delimite them. diff --git a/applications/fpvviewer/fpvectorialsrc/svgvectorialwriter.pas b/applications/fpvviewer/fpvectorialsrc/svgvectorialwriter.pas index 3c8f03eff..38f146887 100644 --- a/applications/fpvviewer/fpvectorialsrc/svgvectorialwriter.pas +++ b/applications/fpvviewer/fpvectorialsrc/svgvectorialwriter.pas @@ -23,9 +23,9 @@ type FPointSeparator, FCommaSeparator: TFormatSettings; procedure WriteDocumentSize(AStrings: TStrings; AData: TvVectorialDocument); procedure WriteDocumentName(AStrings: TStrings; AData: TvVectorialDocument); - procedure WritePaths(AStrings: TStrings; AData: TvVectorialDocument); procedure WritePath(AIndex: Integer; APath: TPath; AStrings: TStrings; AData: TvVectorialDocument); - procedure WriteTexts(AStrings: TStrings; AData: TvVectorialDocument); + procedure WriteText(AStrings: TStrings; lText: TvText; AData: TvVectorialDocument); + procedure WriteEntities(AStrings: TStrings; AData: TvVectorialDocument); procedure ConvertFPVCoordinatesToSVGCoordinates( const AData: TvVectorialDocument; const ASrcX, ASrcY: Double; var ADestX, ADestY: double); @@ -61,19 +61,6 @@ begin AStrings.Add(' sodipodi:docname="New document 1">'); end; -procedure TvSVGVectorialWriter.WritePaths(AStrings: TStrings; AData: TvVectorialDocument); -var - i: Integer; - lPath: TPath; -begin - for i := 0 to AData.GetPathCount() - 1 do - begin - lPath := AData.GetPath(i); - lPath.PrepareForSequentialReading; - WritePath(i ,lPath, AStrings, AData); - end; -end; - {@@ SVG Coordinate system measures things only in pixels, so that we have to hardcode a DPI value for the screen, which is usually 72. @@ -227,43 +214,52 @@ begin // Now data AStrings.Add(' '); - WritePaths(AStrings, AData); - WriteTexts(AStrings, AData); + WriteEntities(AStrings, AData); AStrings.Add(' '); // finalization AStrings.Add(''); end; -procedure TvSVGVectorialWriter.WriteTexts(AStrings: TStrings; AData: TvVectorialDocument); +procedure TvSVGVectorialWriter.WriteText(AStrings: TStrings; lText: TvText; AData: TvVectorialDocument); var i, j, FontSize: Integer; TextStr, FontName, SVGFontFamily: string; - lText: TvText; PtX, PtY: double; begin - for i := 0 to AData.GetTextCount() - 1 do - begin - TextStr := ''; - lText := AData.GetText(i); + TextStr := ''; - ConvertFPVCoordinatesToSVGCoordinates( - AData, lText.X, lText.Y, PtX, PtY); + ConvertFPVCoordinatesToSVGCoordinates( + AData, lText.X, lText.Y, PtX, PtY); - TextStr := lText.Value; - FontSize:= ceil(lText.Font.Size / FLOAT_MILIMETERS_PER_PIXEL); - SVGFontFamily := 'Arial, sans-serif';//lText.FontName; + TextStr := lText.Value; + FontSize:= ceil(lText.Font.Size / FLOAT_MILIMETERS_PER_PIXEL); + SVGFontFamily := 'Arial, sans-serif';//lText.FontName; - AStrings.Add(' '); - AStrings.Add(' '); + AStrings.Add(' '); - AStrings.Add(TextStr + ''); + AStrings.Add(' >'); + AStrings.Add(TextStr + ''); +end; + +procedure TvSVGVectorialWriter.WriteEntities(AStrings: TStrings; + AData: TvVectorialDocument); +var + lEntity: TvEntity; + i: Integer; +begin + for i := 0 to AData.GetEntitiesCount() - 1 do + begin + lEntity := AData.GetEntity(i); + + if lEntity is TPath then WritePath(i, TPath(lEntity), AStrings, AData) + else if lEntity is TvText then WriteText(AStrings, TvText(lEntity), AData); end; end;