fpvectorial: Implements the basic formula support in the generic structures

git-svn-id: trunk@37145 -
This commit is contained in:
sekelsenmat 2012-05-03 07:28:10 +00:00
parent 4b43946e95
commit 5a9e3fa502
2 changed files with 269 additions and 29 deletions

View File

@ -22,8 +22,9 @@ interface
uses uses
Classes, SysUtils, Math, Classes, SysUtils, Math,
// FCL-Image // FCL-Image
fpcanvas, fpimage fpcanvas, fpimage,
// LCL // LCL
lazutf8
{$ifdef USE_LCL_CANVAS} {$ifdef USE_LCL_CANVAS}
, Graphics, LCLIntf, LCLType , Graphics, LCLIntf, LCLType
{$endif} {$endif}
@ -175,8 +176,10 @@ type
public public
X, Y, Z: Double; X, Y, Z: Double;
constructor Create; virtual; constructor Create; virtual;
procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); virtual; // in CalculateBoundingBox always remember to treat correctly the case of ADest=nil!!!
procedure ExpandBoundingBox(var ALeft, ATop, ARight, ABottom: Double); // This cased is utilized to guess the size of a document even before getting a canvas to draw at
procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); virtual;
procedure ExpandBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); // helper to help CalculateBoundingBox
{@@ ASubpart is only valid if this routine returns vfrSubpartFound } {@@ ASubpart is only valid if this routine returns vfrSubpartFound }
function TryToSelect(APos: TPoint; var ASubpart: Cardinal): TvFindEntityResult; virtual; function TryToSelect(APos: TPoint; var ASubpart: Cardinal): TvFindEntityResult; virtual;
procedure Move(ADeltaX, ADeltaY: Integer); virtual; procedure Move(ADeltaX, ADeltaY: Integer); virtual;
@ -219,12 +222,15 @@ type
procedure Assign(ASource: TPath); procedure Assign(ASource: TPath);
procedure PrepareForSequentialReading; procedure PrepareForSequentialReading;
function Next(): TPathSegment; function Next(): TPathSegment;
procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); override; procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); override;
procedure AppendSegment(ASegment: TPathSegment); procedure AppendSegment(ASegment: TPathSegment);
end; end;
{@@ {@@
TvText represents a text entity. TvText represents a text entity.
The text starts in X, Y and grows downwards, towards a smaller Y,
just like the text in the LCL TCanvas
} }
{ TvText } { TvText }
@ -266,14 +272,12 @@ type
VertHalfAxis: Double; // This half-axis is the vertical one when Angle=0 VertHalfAxis: Double; // This half-axis is the vertical one when Angle=0
{@@ The Angle is measured in degrees in relation to the positive X axis } {@@ The Angle is measured in degrees in relation to the positive X axis }
Angle: Double; Angle: Double;
procedure CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); override; procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); override;
procedure Render(ADest: TFPCustomCanvas; ADestX: Integer = 0; procedure Render(ADest: TFPCustomCanvas; ADestX: Integer = 0;
ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override; ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override;
end; end;
{@@ {@@
The brush has no effect in this class
DimensionLeft ---text--- DimensionRight DimensionLeft ---text--- DimensionRight
| | | |
| | BaseRight | | BaseRight
@ -283,7 +287,7 @@ type
{ TvAlignedDimension } { TvAlignedDimension }
TvAlignedDimension = class(TvEntityWithPenAndBrush) TvAlignedDimension = class(TvEntityWithPen)
public public
// Mandatory fields // Mandatory fields
BaseLeft, BaseRight, DimensionLeft, DimensionRight: T3DPoint; BaseLeft, BaseRight, DimensionLeft, DimensionRight: T3DPoint;
@ -316,6 +320,73 @@ type
public public
end; end;
{@@
The elements bellow describe a formula
The main element of a formula is TvFormula which contains a horizontal list of
the elements of the formula. Those can then have sub-elements
The formula starts in X, Y and grows downwards, towards a smaller Y
}
TvFormula = class;
TvFormulaElementKind = (
fekVariable, // Text is the text of the variable
fekEqual, // = symbol
fekSubtraction, // - symbol
fekMultiplication, // either a point . or a small x
fekSum, // + symbol
fekPlusMinus, // The +/- symbol
fekFraction, // a division with Formula on the top and BottomFormula in the bottom
fekRoot, // A root. For example sqrt(something). Number gives the root, usually 2, and inside it goes a Formula
fekNumberWithPower, // A number elevated to a given power, example: 2^5
fekVariableWithPower, // A variable elevated to a given power, example: X^5
fekParenteses,// This is utilized to group elements. Inside it goes a Formula
fekParentesesWithPower, // The same as parenteses, but elevated to the power of "Number"
fekSomatory // Sum of a variable given by Text from Number to AdjacentNumber
);
{ TvFormulaElement }
TvFormulaElement = class
public
Kind: TvFormulaElementKind;
Text: string;
Number: Double;
AdjacentNumber: Double;
Formula: TvFormula;
BottomFormula: TvFormula;
function CalculateHeight: Single; // 1.0 = the normal text height, will return for example 2.2 for 2,2 times the text height
function CalculateWidth(ADest: TFPCustomCanvas): Integer; // in pixels
function AsText: string;
end;
{ TvFormula }
TvFormula = class(TvEntityWithPenAndBrush)
private
FCurIndex: Integer;
SpacingBetweenElementsX: Integer;
procedure CallbackDeleteElement(data,arg:pointer);
protected
FElements: TFPList; // of TvFormulaElement
public
constructor Create; override;
destructor Destroy; override;
//
function GetFirstElement: TvFormulaElement;
function GetNextElement: TvFormulaElement;
procedure AddElement(AElement: TvFormulaElement);
procedure Clear;
//
function CalculateHeight: Single; // 1.0 = the normal text height, will return for example 2.2 for 2,2 times the text height
function CalculateWidth(ADest: TFPCustomCanvas): Integer;
procedure CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double); override;
procedure Render(ADest: TFPCustomCanvas; ADestX: Integer = 0;
ADestY: Integer = 0; AMulX: Double = 1.0; AMulY: Double = 1.0); override;
end;
TvProgressEvent = procedure (APercentage: Byte) of object; TvProgressEvent = procedure (APercentage: Byte) of object;
{ TvVectorialDocument } { TvVectorialDocument }
@ -574,6 +645,24 @@ begin
Result.Z := 0; Result.Z := 0;
end; end;
{ TvEntityWithPen }
constructor TvEntityWithPen.Create;
begin
inherited Create;
Pen.Style := psSolid;
Pen.Color := colBlack;
end;
{ TvEntityWithPenAndBrush }
constructor TvEntityWithPenAndBrush.Create;
begin
inherited Create;
Brush.Style := bsClear;
Brush.Color := colBlue;
end;
{ TvRasterImage } { TvRasterImage }
procedure TvRasterImage.InitializeWithConvertionOf3DPointsToHeightMap(APage: TvVectorialPage; AWidth, AHeight: Integer); procedure TvRasterImage.InitializeWithConvertionOf3DPointsToHeightMap(APage: TvVectorialPage; AWidth, AHeight: Integer);
@ -621,20 +710,157 @@ begin
end; end;
end; end;
constructor TvEntityWithPen.Create; { TvFormulaElement }
function TvFormulaElement.CalculateHeight: Single;
begin begin
inherited Create; case Kind of
Pen.Style := psSolid; //fekVariable, // Text is the text of the variable
Pen.Color := colBlack; //fekEqual, // = symbol
//fekSubtraction, // - symbol
//fekMultiplication, // either a point . or a small x
//fekSum, // + symbol
//fekPlusMinus, // The +/- symbol
fekFraction: Result := 2.3;
fekRoot: Result := Formula.CalculateHeight();
fekNumberWithPower,
fekVariableWithPower: Result := 1.1;
//fekParenteses: Result,// This is utilized to group elements. Inside it goes a Formula
fekParentesesWithPower: Result := 1.1;
fekSomatory: Result := 1.5;
else
Result := 1.0;
end;
end; end;
{ TvEntityWithPenAndBrush } function TvFormulaElement.CalculateWidth(ADest: TFPCustomCanvas): Integer;
var
lText: String;
begin
Result := 0;
constructor TvEntityWithPenAndBrush.Create; lText := AsText;
if lText <> '' then
begin
if ADest = nil then Result := 10 * UTF8Length(lText)
else Result := TCanvas(ADest).TextWidth(lText);
Exit;
end;
{ case Kind of
fekFraction: Result := 2.3;
fekRoot: Result := Formula.CalculateHeight();
fekNumberWithPower,
fekVariableWithPower: Result := 1.1;
//fekParenteses: Result,// This is utilized to group elements. Inside it goes a Formula
fekParentesesWithPower: Result := 1.1;
fekSomatory: Result := 1.5;
else
Result := 1.0;
end;}
end;
function TvFormulaElement.AsText: string;
begin
case Kind of
fekVariable: Result := Text;
fekEqual: Result := '=';
fekSubtraction: Result := '-';
fekMultiplication: Result := 'x';
fekSum: Result := '+';
fekPlusMinus: Result := '+/-';
else
Result := '';
end;
end;
{ TvFormula }
procedure TvFormula.CallbackDeleteElement(data, arg: pointer);
begin
TvFormulaElement(data).Free;
end;
constructor TvFormula.Create;
begin begin
inherited Create; inherited Create;
Brush.Style := bsClear; FElements := TFPList.Create;
Brush.Color := colBlue; SpacingBetweenElementsX := 10;
end;
destructor TvFormula.Destroy;
begin
FElements.Free;
inherited Destroy;
end;
function TvFormula.GetFirstElement: TvFormulaElement;
begin
if FElements.Count = 0 then Exit(nil);
Result := FElements.Items[0];
FCurIndex := 1;
end;
function TvFormula.GetNextElement: TvFormulaElement;
begin
if FElements.Count < FCurIndex then Exit(nil);
Result := FElements.Items[FCurIndex];
Inc(FCurIndex);
end;
procedure TvFormula.AddElement(AElement: TvFormulaElement);
begin
FElements.Add(AElement);
end;
procedure TvFormula.Clear;
begin
FElements.ForEachCall(CallbackDeleteElement, nil);
FElements.Clear;
end;
function TvFormula.CalculateHeight: Single;
var
lElement: TvFormulaElement;
begin
Result := 1.0;
lElement := GetFirstElement();
while lElement <> nil do
begin
Result := Max(Result, lElement.CalculateHeight());
lElement := GetNextElement;
end;
end;
function TvFormula.CalculateWidth(ADest: TFPCustomCanvas): Integer;
var
lElement: TvFormulaElement;
begin
Result := 0;
lElement := GetFirstElement();
while lElement <> nil do
begin
Result := Result + lElement.CalculateWidth(ADest) + SpacingBetweenElementsX;
lElement := GetNextElement;
end;
end;
procedure TvFormula.CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight,
ABottom: Double);
begin
ALeft := X;
ATop := Y;
ARight := CalculateWidth(ADest);
if ADest = nil then ABottom := CalculateHeight() * 15
else ABottom := CalculateHeight() * TCanvas(ADest).TextHeight('Źç');
ARight := X + ARight;
ABottom := Y + ABottom;
end;
procedure TvFormula.Render(ADest: TFPCustomCanvas; ADestX: Integer;
ADestY: Integer; AMulX: Double; AMulY: Double);
begin
end; end;
{ TvVectorialPage } { TvVectorialPage }
@ -1089,19 +1315,19 @@ constructor TvEntity.Create;
begin begin
end; end;
procedure TvEntity.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); procedure TvEntity.CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double);
begin begin
ALeft := 0; ALeft := X;
ATop := 0; ATop := Y;
ARight := 0; ARight := X+1;
ABottom := 0; ABottom := Y+1;
end; end;
procedure TvEntity.ExpandBoundingBox(var ALeft, ATop, ARight, ABottom: Double); procedure TvEntity.ExpandBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double);
var var
lLeft, lTop, lRight, lBottom: Double; lLeft, lTop, lRight, lBottom: Double;
begin begin
CalculateBoundingBox(lLeft, lTop, lRight, lBottom); CalculateBoundingBox(ADest, lLeft, lTop, lRight, lBottom);
if lLeft < ALeft then ALeft := lLeft; if lLeft < ALeft then ALeft := lLeft;
if lTop < ATop then ATop := lTop; if lTop < ATop then ATop := lTop;
if lRight > ARight then ARight := lRight; if lRight > ARight then ARight := lRight;
@ -1141,7 +1367,7 @@ end;
{ TvEllipse } { TvEllipse }
procedure TvEllipse.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); procedure TvEllipse.CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double);
var var
t, tmp: Double; t, tmp: Double;
begin begin
@ -1201,7 +1427,7 @@ var
ALCLDest: TCanvas absolute ADest; ALCLDest: TCanvas absolute ADest;
{$endif} {$endif}
begin begin
CalculateBoundingBox(fx1, fy1, fx2, fy2); CalculateBoundingBox(ADest, fx1, fy1, fx2, fy2);
x1 := CoordToCanvasX(fx1); x1 := CoordToCanvasX(fx1);
x2 := CoordToCanvasX(fx2); x2 := CoordToCanvasX(fx2);
y1 := CoordToCanvasY(fy1); y1 := CoordToCanvasY(fy1);
@ -1485,7 +1711,7 @@ begin
for i := 0 to CurPage.GetEntitiesCount() - 1 do for i := 0 to CurPage.GetEntitiesCount() - 1 do
begin begin
lEntity := CurPage.GetEntity(I); lEntity := CurPage.GetEntity(I);
lEntity.ExpandBoundingBox(lLeft, lTop, lRight, lBottom); lEntity.ExpandBoundingBox(nil, lLeft, lTop, lRight, lBottom);
end; end;
end; end;
@ -1668,13 +1894,13 @@ begin
CurPoint := Result; CurPoint := Result;
end; end;
procedure TPath.CalculateBoundingBox(var ALeft, ATop, ARight, ABottom: Double); procedure TPath.CalculateBoundingBox(ADest: TFPCustomCanvas; var ALeft, ATop, ARight, ABottom: Double);
var var
lSegment: TPathSegment; lSegment: TPathSegment;
l2DSegment: T2DSegment; l2DSegment: T2DSegment;
lFirstValue: Boolean = True; lFirstValue: Boolean = True;
begin begin
inherited CalculateBoundingBox(ALeft, ATop, ARight, ABottom); inherited CalculateBoundingBox(ADest, ALeft, ATop, ARight, ABottom);
PrepareForSequentialReading(); PrepareForSequentialReading();
lSegment := Next(); lSegment := Next();

View File

@ -28,6 +28,7 @@ type
{ General reading methods } { General reading methods }
constructor Create; override; constructor Create; override;
Destructor Destroy; override; Destructor Destroy; override;
procedure ReadFormulaFromNode(ACurNode: TDOMNode; APage: TvVectorialPage; var AFormula: TvFormula);
procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override; procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
end; end;
@ -54,12 +55,19 @@ begin
inherited Destroy; inherited Destroy;
end; end;
procedure TvMathMLVectorialReader.ReadFormulaFromNode(ACurNode: TDOMNode;
APage: TvVectorialPage; var AFormula: TvFormula);
begin
end;
procedure TvMathMLVectorialReader.ReadFromStream(AStream: TStream; procedure TvMathMLVectorialReader.ReadFromStream(AStream: TStream;
AData: TvVectorialDocument); AData: TvVectorialDocument);
var var
Doc: TXMLDocument; Doc: TXMLDocument;
lFirstLayer, lCurNode: TDOMNode; lFirstLayer, lCurNode: TDOMNode;
lPage: TvVectorialPage; lPage: TvVectorialPage;
lFormula: TvFormula;
begin begin
try try
// Read in xml file from the stream // Read in xml file from the stream
@ -77,7 +85,13 @@ begin
lPage.Height := AData.Height; lPage.Height := AData.Height;
while Assigned(lCurNode) do while Assigned(lCurNode) do
begin begin
//ReadFormulaFromNode(lCurNode, lPage, AData); if lCurNode.ToString = 'row' then
begin
lFormula := TvFormula.Create;
ReadFormulaFromNode(lCurNode, lPage, lFormula);
lPage.AddEntity(lFormula);
end;
lCurNode := lCurNode.NextSibling; lCurNode := lCurNode.NextSibling;
end; end;
finally finally