From cb7c9207bb1b6d16e7183deb6402495c6e22e037 Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Mon, 10 Jun 2013 07:04:55 +0000 Subject: [PATCH] fpvectorial: Greatly improves the ellipse arc drawing, now it finally works in many cases =) git-svn-id: trunk@41650 - --- components/fpvectorial/fpvectorial.pas | 153 ++++++++---------- components/fpvectorial/svgvectorialreader.pas | 35 ++-- 2 files changed, 89 insertions(+), 99 deletions(-) diff --git a/components/fpvectorial/fpvectorial.pas b/components/fpvectorial/fpvectorial.pas index 575df919f0..b4d4c735dc 100644 --- a/components/fpvectorial/fpvectorial.pas +++ b/components/fpvectorial/fpvectorial.pas @@ -382,11 +382,14 @@ type It has the opposite direction of text in the LCL TCanvas. } + TvTextAnchor = (vtaStart, vtaMiddle, vtaEnd); + { TvText } TvText = class(TvEntityWithPenBrushAndFont) public Value: TStringList; + TextAnchor: TvTextAnchor; constructor Create; override; destructor Destroy; override; function TryToSelect(APos: TPoint; var ASubpart: Cardinal): TvFindEntityResult; override; @@ -1070,51 +1073,6 @@ begin XStart := T2DSegment(Previous).X; YStart := T2DSegment(Previous).Y; - { - solve 4 equations and 4 unknown values (CX, CY, t1, t2) - X,Y has t=t2 // Xstart,Ystart has t=t1 - CX := X - RX*Cos(t2)*Cos(XRotation) + RY*Sin(t2)*Sin(XRotation); - CX := XStart - RX*Cos(t1)*Cos(XRotation) + RY*Sin(t1)*Sin(XRotation); - CY := Y - RY*Sin(t2)*Cos(XRotation) - RX*Cos(t2)*Sin(XRotation); - CY := YStart - RY*Sin(t1)*Cos(XRotation) - RX*Cos(t1)*Sin(XRotation); - - Substituting we get 2 equations and 2 unknown values (t1, t2) - X - RX*Cos(t2)*Cos(XRotation) + RY*Sin(t2)*Sin(XRotation) = XStart - RX*Cos(t1)*Cos(XRotation) + RY*Sin(t1)*Sin(XRotation) - Y - RY*Sin(t2)*Cos(XRotation) - RX*Cos(t2)*Sin(XRotation) = YStart - RY*Sin(t1)*Cos(XRotation) - RX*Cos(t1)*Sin(XRotation) - - Simplify: - - A = RX*Cos(XRotation) - B = RY*Cos(XRotation) - C = RY*Sin(XRotation) - D = RX*Sin(XRotation) - - X - A*Cos(t2) + C*Sin(t2) = XStart - A*Cos(t1) + C*Sin(t1) - Y - B*Sin(t2) - D*Cos(t2) = YStart - B*Sin(t1) - D*Cos(t1) - - Attempt to corner Sin(t1): - - X - A*Cos(t2) + C*Sin(t2) = XStart - A*sqrt(1-Sin(t1)^2) + C*Sin(t1) - X - A*Cos(t2) + C*Sin(t2) - XStart - C*Sin(t1) = - A*sqrt(1-Sin^2(t1)) - Mega = X - A*Cos(t2) + C*Sin(t2) - XStart - Mega^2 - 2*Mega*C*Sin(t1) + C^2*Sin^2(t1) = A^2*(1-Sin^2(t1)) - Mega^2 - A^2 - 2*Mega*C*Sin(t1) + (C^2+A^2)*Sin^2(t1) = 0 - - Baskara!!! - - AB = (C^2+A^2) - BB = -2*Mega*C - CB = Mega^2 - - DeltaB=BB^2-4*AB*CB - Sin(t1)=(-BB+/-sqrt(DeltaB))/(2*AB) - - Now roll it back! - - X - A*Cos(t2) + C*Sin(t2) = XStart - A*sqrt(1-Sin(t1)^2) + C*Sin(t1) - - } - // Solve by rotating everything to align the ellipse to the axises and then rotating back again E1 := Rotate3DPointInXY(Make3DPoint(XStart,YStart,0), Make3DPoint(0,0,0),-1*XRotation); E2 := Rotate3DPointInXY(Make3DPoint(X,Y,0), Make3DPoint(0,0,0),-1*XRotation); @@ -1137,14 +1095,8 @@ begin lT1 := SolveNumericallyAngle(AlignedEllipseCenterEquationT1, 0.0001, 20); - //lT2 := arccos((- E1.X + RX*cos(lt1) + E2.X)/RX); - - // CX = E1.X - RX*cos(t1) - // CY = E1.Y - RY*sin(t1) CX1 := E1.X - RX*cos(lt1); CY1 := E1.Y - RY*sin(lt1); - //CX2 := E2.X - RX*cos(lt2); - //CY2 := E2.Y - RY*sin(lt2); // Rotate back! RotatedCenter := Rotate3DPointInXY(Make3DPoint(CX1,CY1,0), Make3DPoint(0,0,0),XRotation); @@ -1152,19 +1104,17 @@ begin CY1 := RotatedCenter.Y; // The other ellipse is simetrically positioned - // so that the line between the two ellipse center is orthogonal to the line - // between (X,Y) and (Xstart,Ystart) - CX2 := RotatedCenter.X; - CY2 := RotatedCenter.Y; + if (CX1 > Xstart) then + CX2 := X - (CX1-Xstart) + else + CX2 := Xstart - (CX1-X); + // + if (CY1 > Y) then + CY2 := Ystart - (CY1-Y) + else + CY2 := Y - (CY1-Ystart); - // errado!!!! Apagar quando achar o correto =( - { - CX := X - RX*Cos(0)*Cos(XRotation) + RY*Sin(0)*Sin(XRotation); - CY := Y - RY*Sin(0)*Cos(XRotation) - RX*Cos(0)*Sin(XRotation); - } - - - // ativar quando tiver codigo pra calcular CX1, etc + // Achar qual é a da esquerda e qual a da direita if CX1 < CX2 then begin LeftMostX := CX1; @@ -1233,36 +1183,46 @@ begin CalculateCenter(); - // Search for the minimum and maximum X - t1 := arctan(-RY*tan(XRotation)/RX) - Pi; - t2 := arctan(-RY*tan(XRotation)/RX); - t3 := arctan(-RY*tan(XRotation)/RX) + Pi; + if XRotation = 0 then + begin + ALeft := CX-RX; + ARight := CX+RX; + ATop := CY-RY; + ABottom := CY+RY; + end + else + begin + // Search for the minimum and maximum X + t1 := arctan(-RY*tan(XRotation)/RX); + t2 := arctan(-RY*tan(XRotation)/RX) + Pi/2; + t3 := arctan(-RY*tan(XRotation)/RX) + Pi; - x1 := Cx + RX*Cos(t1)*Cos(XRotation)-RY*Sin(t1)*Sin(XRotation); - x2 := Cx + RX*Cos(t2)*Cos(XRotation)-RY*Sin(t2)*Sin(XRotation); - x3 := Cx + RX*Cos(t3)*Cos(XRotation)-RY*Sin(t3)*Sin(XRotation); + x1 := Cx + RX*Cos(t1)*Cos(XRotation)-RY*Sin(t1)*Sin(XRotation); + x2 := Cx + RX*Cos(t2)*Cos(XRotation)-RY*Sin(t2)*Sin(XRotation); + x3 := Cx + RX*Cos(t3)*Cos(XRotation)-RY*Sin(t3)*Sin(XRotation); - ALeft := Min(x1, x2); - ALeft := Min(ALeft, x3); + ALeft := Min(x1, x2); + ALeft := Min(ALeft, x3); - ARight := Max(x1, x2); - ARight := Max(ARight, x3); + ARight := Max(x1, x2); + ARight := Max(ARight, x3); - // Now the same for Y + // Now the same for Y - t1 := arctan(RY*cotan(XRotation)/RX) - Pi; - t2 := arctan(RY*cotan(XRotation)/RX); - t3 := arctan(RY*cotan(XRotation)/RX) + Pi; + t1 := arctan(RY*cotan(XRotation)/RX); + t2 := arctan(RY*cotan(XRotation)/RX) + Pi/2; + t3 := arctan(RY*cotan(XRotation)/RX) + 3*Pi/2; - y1 := CY + RY*Sin(t1)*Cos(XRotation)+RX*Cos(t1)*Sin(XRotation); - y2 := CY + RY*Sin(t2)*Cos(XRotation)+RX*Cos(t2)*Sin(XRotation); - y3 := CY + RY*Sin(t3)*Cos(XRotation)+RX*Cos(t3)*Sin(XRotation); + y1 := CY + RY*Sin(t1)*Cos(XRotation)+RX*Cos(t1)*Sin(XRotation); + y2 := CY + RY*Sin(t2)*Cos(XRotation)+RX*Cos(t2)*Sin(XRotation); + y3 := CY + RY*Sin(t3)*Cos(XRotation)+RX*Cos(t3)*Sin(XRotation); - ATop := Min(y1, y2); - ATop := Min(ATop, y3); + ATop := Min(y1, y2); + ATop := Min(ATop, y3); - ABottom := Max(y1, y2); - ABottom := Max(ABottom, y3); + ABottom := Max(y1, y2); + ABottom := Max(ABottom, y3); + end; end; { TvVerticalFormulaStack } @@ -2282,12 +2242,33 @@ var i: Integer; // LowerDim: T3DPoint; + XAnchorAdjustment: Integer; + lLongestLine, lLineWidth: Integer; + {$ifdef USE_LCL_CANVAS} + ACanvas: TCanvas absolute ADest; + {$endif} begin inherited Render(ADest, ARenderInfo, ADestX, ADestY, AMulX, AMulY); // Don't draw anything if we have alpha=zero if Font.Color.Alpha = 0 then Exit; + // if an anchor is set, use it + // to do this, first search for the longest line + lLongestLine := 0; + for i := 0 to Value.Count - 1 do + begin + lLineWidth := ACanvas.TextWidth(Value.Strings[i]); + if lLineWidth > lLongestLine then + lLongestLine := lLineWidth; + end; + case TextAnchor of + vtaMiddle: XAnchorAdjustment := -1 * lLongestLine div 2; + vtaEnd: XAnchorAdjustment := -1 * lLongestLine; + else + XAnchorAdjustment := 0; + end; + // TvText supports multiple lines for i := 0 to Value.Count - 1 do begin @@ -2300,7 +2281,7 @@ begin end; ADest.Font.FPColor := AdjustColorToBackground(Font.Color, ARenderInfo); - ADest.TextOut(CoordToCanvasX(X), Round(LowerDim.Y), Value.Strings[i]); + ADest.TextOut(CoordToCanvasX(X)+XAnchorAdjustment, Round(LowerDim.Y), Value.Strings[i]); end; end; diff --git a/components/fpvectorial/svgvectorialreader.pas b/components/fpvectorial/svgvectorialreader.pas index ce7d09f57d..0eb94c47bd 100644 --- a/components/fpvectorial/svgvectorialreader.pas +++ b/components/fpvectorial/svgvectorialreader.pas @@ -912,7 +912,7 @@ begin lNodeName := lCurNode.Attributes.Item[i].NodeName; if lNodeName = 'id' then begin - lLayerName := UTF16ToUTF8(lCurNode.Attributes.Item[i].NodeValue); + lLayerName := lCurNode.Attributes.Item[i].NodeValue; lBlock.Name := lLayerName; end; end; @@ -980,7 +980,7 @@ begin else if lNodeName = 'r' then cr := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue) else if lNodeName = 'id' then - lCircle.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + lCircle.Name := ANode.Attributes.Item[i].NodeValue else if lNodeName = 'style' then ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lCircle) else if IsAttributeFromStyle(lNodeName) then @@ -1030,7 +1030,7 @@ begin else if lNodeName = 'ry' then cry := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue) else if lNodeName = 'id' then - lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + lEllipse.Name := ANode.Attributes.Item[i].NodeValue else if lNodeName = 'style' then ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lEllipse) else if IsAttributeFromStyle(lNodeName) then @@ -1075,7 +1075,7 @@ begin begin lNodeName := ANode.Attributes.Item[i].NodeName; if lNodeName = 'id' then - lLayerName := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue); + lLayerName := ANode.Attributes.Item[i].NodeValue; end; lParentLayer := AData.GetCurrentLayer(); @@ -1097,7 +1097,7 @@ begin begin {$ifdef SVG_MERGE_LAYER_STYLES} lLayerStyleKeys.Add(lNodeName); - lLayerStyleValues.Add(UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)); + lLayerStyleValues.Add(ANode.Attributes.Item[i].NodeValue); {$else} lLayer.SetPenBrushAndFontElements += ReadSVGPenStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lLayer); lLayer.SetPenBrushAndFontElements += ReadSVGBrushStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lLayer); @@ -1211,7 +1211,7 @@ begin begin lNodeName := ANode.Attributes.Item[i].NodeName; if lNodeName = 'id' then - lPath.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + lPath.Name := ANode.Attributes.Item[i].NodeValue else if lNodeName = 'style' then ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lPath) else if IsAttributeFromStyle(lNodeName) then @@ -1469,7 +1469,6 @@ begin begin ConvertSVGCoordinatesToFPVCoordinates(AData, X, Y, X, Y); end; - ConvertSVGDeltaToFPVDelta(AData, X2, Y2, X2, Y2); // Convert SVG flags to fpvectorial flags LeftmostEllipse := (LargeArcFlag and (not SweepFlag)) @@ -1567,7 +1566,7 @@ begin begin lNodeName := ANode.Attributes.Item[i].NodeName; if lNodeName = 'id' then - lPath.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + lPath.Name := ANode.Attributes.Item[i].NodeValue else if lNodeName = 'style' then ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lPath) else if IsAttributeFromStyle(lNodeName) then @@ -1617,7 +1616,7 @@ begin else if lNodeName = 'height' then cy := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue) else if lNodeName = 'id' then - lRect.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + lRect.Name := ANode.Attributes.Item[i].NodeValue else if lNodeName = 'style' then ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lRect) else if IsAttributeFromStyle(lNodeName) then @@ -1648,6 +1647,7 @@ var i: Integer; lNodeName: DOMString; lCurNode: TDOMNode; + lTextAnchor: string = ''; begin lx := 0.0; ly := 0.0; @@ -1663,9 +1663,11 @@ begin else if lNodeName = 'y' then ly := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue) else if lNodeName = 'id' then - lText.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue) + lText.Name := ANode.Attributes.Item[i].NodeValue else if lNodeName = 'style' then - ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lText, True) + ReadSVGStyle(ANode.Attributes.Item[i].NodeValue, lText) + else if lNodeName = 'text-anchor' then + lTextAnchor := ANode.Attributes.Item[i].NodeValue else if IsAttributeFromStyle(lNodeName) then ReadSVGFontStyleWithKeyAndValue(lNodeName, ANode.Attributes.Item[i].NodeValue, lText); end; @@ -1678,9 +1680,16 @@ begin lText.Value.Add(lTextStr); // Set the coordinates - ConvertSVGDeltaToFPVDelta( + ConvertSVGCoordinatesToFPVCoordinates( AData, lx, ly, lText.X, lText.Y); + // Adjust according to the text-anchor, if necessary + lTextAnchor := LowerCase(lTextAnchor); + case lTextAnchor of + 'middle': lText.TextAnchor := vtaMiddle; + 'end': lText.TextAnchor := vtaEnd; + end; + // Now add other lines, which appear as another line // Example: // Several lines: @@ -1880,7 +1889,7 @@ begin begin {$ifdef SVG_MERGE_LAYER_STYLES} lLayerStyleKeys.Add(lNodeName); - lLayerStyleValues.Add(UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)); + lLayerStyleValues.Add(ANode.Attributes.Item[i].NodeValue); {$endif} end; end;