Advances the reading support of DXF in fpvviewer

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1462 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat
2011-01-26 16:05:00 +00:00
parent 5e3e7833cb
commit c079c6138b
3 changed files with 264 additions and 56 deletions

View File

@ -66,15 +66,11 @@ type
TvDXFVectorialReader = class(TvCustomVectorialReader)
private
// CIRCLE
CircleCenterX, CircleCenterY, CircleCenterZ, CircleRadius: Double;
// LINE
LineStartX, LineStartY, LineStartZ: Double;
LineEndX, LineEndY, LineEndZ: Double;
//
function SeparateString(AString: string; ASeparator: Char): T10Strings;
procedure ReadENTITIES(ATokens: TDXFTokens; AData: TvVectorialDocument);
procedure ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvVectorialDocument);
procedure ReadENTITIES_ARC(ATokens: TDXFTokens; AData: TvVectorialDocument);
procedure ReadENTITIES_CIRCLE(ATokens: TDXFTokens; AData: TvVectorialDocument);
procedure ReadENTITIES_ELLIPSE(ATokens: TDXFTokens; AData: TvVectorialDocument);
procedure ReadENTITIES_TEXT(ATokens: TDXFTokens; AData: TvVectorialDocument);
@ -318,44 +314,9 @@ begin
for i := 0 to ATokens.Count - 1 do
begin
CurToken := TDXFToken(ATokens.Items[i]);
if CurToken.StrValue = 'CIRCLE' then
begin
CircleCenterX := 0.0;
CircleCenterY := 0.0;
CircleCenterZ := 0.0;
CircleRadius := 0.0;
ReadENTITIES_CIRCLE(CurToken.Childs, AData);
AData.AddCircle(CircleCenterX, CircleCenterY,
CircleCenterZ, CircleRadius);
end
else if CurToken.StrValue = 'ELLIPSE' then
begin
// ...
ReadENTITIES_ELLIPSE(CurToken.Childs, AData);
end
else if CurToken.StrValue = 'LINE' then
begin
// Initial values
LineStartX := 0;
LineStartY := 0;
LineStartZ := 0;
LineEndX := 0;
LineEndY := 0;
LineEndZ := 0;
// Read the data of the line
ReadENTITIES_LINE(CurToken.Childs, AData);
// And now write it
{$ifdef FPVECTORIALDEBUG}
WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY]));
{$endif}
AData.StartPath(LineStartX, LineStartY);
AData.AddLineToPath(LineEndX, LineEndY);
AData.EndPath();
end
if CurToken.StrValue = 'CIRCLE' then ReadENTITIES_CIRCLE(CurToken.Childs, AData)
else if CurToken.StrValue = 'ELLIPSE' then ReadENTITIES_ELLIPSE(CurToken.Childs, AData)
else if CurToken.StrValue = 'LINE' then ReadENTITIES_LINE(CurToken.Childs, AData)
else if CurToken.StrValue = 'TEXT' then
begin
// ...
@ -367,7 +328,18 @@ procedure TvDXFVectorialReader.ReadENTITIES_LINE(ATokens: TDXFTokens; AData: TvV
var
CurToken: TDXFToken;
i: Integer;
// LINE
LineStartX, LineStartY, LineStartZ: Double;
LineEndX, LineEndY, LineEndZ: Double;
begin
// Initial values
LineStartX := 0;
LineStartY := 0;
LineStartZ := 0;
LineEndX := 0;
LineEndY := 0;
LineEndZ := 0;
for i := 0 to ATokens.Count - 1 do
begin
// Now read and process the item name
@ -388,6 +360,62 @@ begin
31: LineEndZ := CurToken.FloatValue;
end;
end;
// And now write it
{$ifdef FPVECTORIALDEBUG}
WriteLn(Format('Adding Line from %f,%f to %f,%f', [LineStartX, LineStartY, LineEndX, LineEndY]));
{$endif}
AData.StartPath(LineStartX, LineStartY);
AData.AddLineToPath(LineEndX, LineEndY);
AData.EndPath();
end;
{
100 Subclass marker (AcDbCircle)
39 Thickness (optional; default = 0)
10 Center point (in OCS) DXF: X value; APP: 3D point
20, 30 DXF: Y and Z values of center point (in OCS)
40 Radius
100 Subclass marker (AcDbArc)
50 Start angle
51 End angle
210 Extrusion direction. (optional; default = 0, 0, 1) DXF: X value; APP: 3D vector
220, 230 DXF: Y and Z values of extrusion direction (optional)
}
procedure TvDXFVectorialReader.ReadENTITIES_ARC(ATokens: TDXFTokens;
AData: TvVectorialDocument);
var
CurToken: TDXFToken;
i: Integer;
CenterX, CenterY, CenterZ, Radius, StartAngle, EndAngle: Double;
begin
CenterX := 0.0;
CenterY := 0.0;
CenterZ := 0.0;
Radius := 0.0;
StartAngle := 0.0;
EndAngle := 0.0;
for i := 0 to ATokens.Count - 1 do
begin
// Now read and process the item name
CurToken := TDXFToken(ATokens.Items[i]);
// Avoid an exception by previously checking if the conversion can be made
if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or
(CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue;
CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue));
case CurToken.GroupCode of
10: CenterX := CurToken.FloatValue;
20: CenterY := CurToken.FloatValue;
30: CenterZ := CurToken.FloatValue;
40: Radius := CurToken.FloatValue;
end;
end;
AData.AddCircularArc(CenterX, CenterY, CenterZ, Radius, StartAngle, EndAngle);
end;
{
@ -405,7 +433,13 @@ procedure TvDXFVectorialReader.ReadENTITIES_CIRCLE(ATokens: TDXFTokens;
var
CurToken: TDXFToken;
i: Integer;
CircleCenterX, CircleCenterY, CircleCenterZ, CircleRadius: Double;
begin
CircleCenterX := 0.0;
CircleCenterY := 0.0;
CircleCenterZ := 0.0;
CircleRadius := 0.0;
for i := 0 to ATokens.Count - 1 do
begin
// Now read and process the item name
@ -424,6 +458,9 @@ begin
40: CircleRadius := CurToken.FloatValue;
end;
end;
AData.AddCircle(CircleCenterX, CircleCenterY,
CircleCenterZ, CircleRadius);
end;
{
@ -443,7 +480,7 @@ procedure TvDXFVectorialReader.ReadENTITIES_ELLIPSE(ATokens: TDXFTokens;
var
CurToken: TDXFToken;
i: Integer;
CenterX, CenterY, CenterZ: Double;
CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double;
begin
for i := 0 to ATokens.Count - 1 do
begin
@ -461,7 +498,11 @@ begin
20: CenterY := CurToken.FloatValue;
30: CenterZ := CurToken.FloatValue;
end;
end;
//
AData.AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle);
end;
{
@ -498,8 +539,35 @@ end;
}
procedure TvDXFVectorialReader.ReadENTITIES_TEXT(ATokens: TDXFTokens;
AData: TvVectorialDocument);
var
CurToken: TDXFToken;
i: Integer;
PosX, PosY, PosZ: Double;
Str: string;
begin
for i := 0 to ATokens.Count - 1 do
begin
// Now read and process the item name
CurToken := TDXFToken(ATokens.Items[i]);
// Avoid an exception by previously checking if the conversion can be made
if (CurToken.GroupCode = DXF_ENTITIES_HANDLE) or
(CurToken.GroupCode = 1) or
(CurToken.GroupCode = DXF_ENTITIES_AcDbEntity) then Continue;
CurToken.FloatValue := StrToFloat(Trim(CurToken.StrValue));
case CurToken.GroupCode of
1: Str := CurToken.StrValue;
10: PosX := CurToken.FloatValue;
20: PosY := CurToken.FloatValue;
30: PosZ := CurToken.FloatValue;
end;
end;
//
// AData.AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle);
end;
function TvDXFVectorialReader.GetCoordinateValue(AStr: shortstring): Double;

View File

@ -18,7 +18,7 @@ unit fpvectorial;
interface
uses
Classes, SysUtils;
Classes, SysUtils, Math;
type
TvVectorialFormat = (
@ -121,13 +121,41 @@ type
Value: utf8string;
end;
{@@
}
TvEntity = class
public
end;
{@@
}
TvCircle = class(TvEntity)
public
X, Y, Z, Radius: Double;
CenterX, CenterY, CenterZ, Radius: Double;
end;
{@@
}
TvCircularArc = class(TvEntity)
public
CenterX, CenterY, CenterZ, Radius: Double;
StartAngle, EndAngle: Double;
end;
{@@
}
{ TvEllipse }
TvEllipse = class(TvEntity)
public
// Mandatory fields
CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis: Double;
{@@ The Angle is measured in radians in relation to the positive X axis }
Angle: Double;
// Calculated fields
BoundingRect: TRect;
procedure CalculateBoundingRectangle;
end;
type
@ -184,7 +212,9 @@ type
procedure EndPath();
procedure AddText(AX, AY, AZ: Double; FontName: string; FontSize: integer; AText: utf8string); overload;
procedure AddText(AX, AY, AZ: Double; AStr: utf8string); overload;
procedure AddCircle(AX, AY, AZ, ARadius: Double);
procedure AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
procedure AddCircularArc(ACenterX, ACenterY, ACenterZ, ARadius, AStartAngle, AEndAngle: Double);
procedure AddEllipse(CenterX, CenterY, CenterZ, MajorHalfAxis, MinorHalfAxis, Angle: Double);
{ properties }
property PathCount: Integer read GetPathCount;
property Paths[Index: Cardinal]: TPath read GetPath;
@ -331,6 +361,38 @@ begin
end;
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 := CenterX + MajorHalfAxis*cos(t)*cos(Angle) - MinorHalfAxis*sin(t)*sin(Angle);
BoundingRect.Right := Round(tmp);
end;
{ TsWorksheet }
{@@
@ -537,18 +599,48 @@ begin
AddText(AX, AY, AZ, '', 10, AStr);
end;
procedure TvVectorialDocument.AddCircle(AX, AY, AZ, ARadius: Double);
procedure TvVectorialDocument.AddCircle(ACenterX, ACenterY, ACenterZ, ARadius: Double);
var
lCircle: TvCircle;
begin
lCircle := TvCircle.Create;
lCircle.X := AX;
lCircle.Y := AY;
lCircle.Z := AZ;
lCircle.CenterX := ACenterX;
lCircle.CenterY := ACenterY;
lCircle.CenterZ := ACenterZ;
lCircle.Radius := ARadius;
FEntities.Add(lCircle);
end;
procedure TvVectorialDocument.AddCircularArc(ACenterX, ACenterY, ACenterZ,
ARadius, AStartAngle, AEndAngle: Double);
var
lCircularArc: TvCircularArc;
begin
lCircularArc := TvCircularArc.Create;
lCircularArc.CenterX := ACenterX;
lCircularArc.CenterY := ACenterY;
lCircularArc.CenterZ := ACenterZ;
lCircularArc.Radius := ARadius;
lCircularArc.StartAngle := AStartAngle;
lCircularArc.EndAngle := AEndAngle;
FEntities.Add(lCircularArc);
end;
procedure TvVectorialDocument.AddEllipse(CenterX, CenterY, CenterZ,
MajorHalfAxis, MinorHalfAxis, Angle: Double);
var
lEllipse: TvEllipse;
begin
lEllipse := TvEllipse.Create;
lEllipse.CenterX := CenterX;
lEllipse.CenterY := CenterY;
lEllipse.CenterZ := CenterZ;
lEllipse.MajorHalfAxis := MajorHalfAxis;
lEllipse.MinorHalfAxis := MinorHalfAxis;
lEllipse.Angle := Angle;
FEntities.Add(lEllipse);
end;
{@@
Convenience method which creates the correct
writer object for a given vector graphics document format.

View File

@ -14,6 +14,42 @@ procedure DrawFPVectorialToCanvas(ASource: TvVectorialDocument; ADest: TFPCustom
implementation
{function Rotate2DPoint(P,Fix :TPoint; alpha:double): TPoint;
var
sinus, cosinus : Extended;
begin
SinCos(alpha, sinus, cosinus);
P.x := P.x - Fix.x;
P.y := P.y - Fix.y;
result.x := Round(p.x*cosinus + p.y*sinus) + fix.x ;
result.y := Round(-p.x*sinus + p.y*cosinus) + Fix.y;
end;}
procedure DrawRotatedEllipse(ADest: TFPCustomCanvas; CurEllipse: TvEllipse);
{var
PointList: array[0..6] of TPoint;
f: TPoint;
dk: Integer;}
begin
{ dk := Round(0.654 * Abs(y2-y1));
f.x := CurEllipse.CenterX;
f.y := CurEllipse.CenterY - 1;
PointList[0] := Rotate2DPoint(Point(x1, f.y), f, Alpha) ; // Startpoint
PointList[1] := Rotate2DPoint(Point(x1, f.y - dk), f, Alpha);
//Controlpoint of Startpoint first part
PointList[2] := Rotate2DPoint(Point(x2- 1, f.y - dk), f, Alpha);
//Controlpoint of secondpoint first part
PointList[3] := Rotate2DPoint(Point(x2 -1 , f.y), f, Alpha);
// Firstpoint of secondpart
PointList[4] := Rotate2DPoint(Point(x2-1 , f.y + dk), f, Alpha);
// Controllpoint of secondpart firstpoint
PointList[5] := Rotate2DPoint(Point(x1, f.y + dk), f, Alpha);
// Conrollpoint of secondpart endpoint
PointList[6] := PointList[0]; // Endpoint of
// Back to the startpoint
PolyBezier(canvas.handle, Pointlist[0], 7);}
end;
{@@
This function draws a FPVectorial vectorial image to a TFPCustomCanvas
descendent, such as TCanvas from the LCL.
@ -43,6 +79,8 @@ var
// For entities
CurEntity: TvEntity;
CurCircle: TvCircle;
CurEllipse: TvEllipse;
CurCircularArc: TvCircularArc;
begin
{$ifdef FPVECTORIALDEBUG}
WriteLn(':>DrawFPVectorialToCanvas');
@ -106,15 +144,25 @@ begin
for i := 0 to ASource.GetEntityCount - 1 do
begin
CurEntity := ASource.GetEntity(i);
CurCircle := CurEntity as TvCircle;
if CurEntity is TvCircle then
begin
CurCircle := CurEntity as TvCircle;
ADest.Ellipse(
Round(ADestX + AmulX * (CurCircle.X - CurCircle.Radius)),
Round(ADestY + AMulY * (CurCircle.Y - CurCircle.Radius)),
Round(ADestX + AmulX * (CurCircle.X + CurCircle.Radius)),
Round(ADestY + AMulY * (CurCircle.Y + CurCircle.Radius))
Round(ADestX + AmulX * (CurCircle.CenterX - CurCircle.Radius)),
Round(ADestY + AMulY * (CurCircle.CenterY - CurCircle.Radius)),
Round(ADestX + AmulX * (CurCircle.CenterX + CurCircle.Radius)),
Round(ADestY + AMulY * (CurCircle.CenterY + CurCircle.Radius))
);
end
else if CurEntity is TvEllipse then
begin
CurEllipse := CurEntity as TvEllipse;
DrawRotatedEllipse(ADest, CurEllipse);
end
else if CurEntity is TvCircularArc then
begin
CurCircularArc := CurEntity as TvCircularArc;
// ADest.Arc(ADest, CurEllipse);
end;
end;