Отговори (3)
Предполагам, че това е така, защото текстът ще се върти около горната лява позиция на правоъгълника
Не, ротацията е центрирана около текущия произход на платното. По подразбиране това е координата 0, 0
, но може да бъде променена от текущо зададената матрица на деформация. Типичният начин да отидете е: изберете център на въртене, преместете началото в този център, завъртете, върнете се обратно към променения произход и след това начертайте. Вижте TControl.MatrixChanged
за справка. Но има много други начини.
Ето пример за това как да рисувате текст от долния ляв към горния десен ъгъл във формуляр:
procedure TForm1.FormPaint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
var
Angle: Single;
R: TRectF;
S: String;
H: Single;
Matrix: TMatrix;
begin
Canvas.Fill.Color := TAlphaColors.Black;
Angle := -ArcTan2(ClientHeight, ClientWidth);
R := ClientRect;
S := 'Text from bottom-left...';
H := Canvas.TextHeight(S);
Matrix := CreateRotationMatrix(Angle);
Matrix.m31 := Sin(Angle) * (ClientHeight - H);
Matrix.m32 := ClientHeight * (1 - Cos(Angle));
Canvas.SetMatrix(Matrix);
Canvas.FillText(R, S, False, 1, [], TTextAlign.taLeading,
TTextAlign.taTrailing);
S := '...to top-right';
Matrix.m31 := ClientWidth * (1 - Cos(Angle)) + Sin(Angle) * H;
Matrix.m32 := -Sin(Angle) * ClientWidth;
Canvas.SetMatrix(Matrix);
Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
TTextAlign.taLeading);
end;
Актуализация:
Този код все още не отчита вече изместен произход.
В отговор на вашия коментар, следният код изчертава текст от координата 50, 100
надолу, завъртян на 90° около тази точка, използвайки метода, обяснен по-горе, върху PaintBox, който е произволно позициониран във формуляра.
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
const
S = 'Hello World';
var
R: TRectF;
OriginalMatrix: TMatrix;
ShiftMatrix: TMatrix;
RotationMatrix: TMatrix;
ShiftBackMatrix: TMatrix;
Matrix: TMatrix;
begin
PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
R.Right := 50;
R.Bottom := 100;
R.Left := R.Right - 5000;
R.Top := R.Bottom - 5000;
OriginalMatrix := PaintBox1.Canvas.Matrix;
ShiftMatrix := IdentityMatrix;
ShiftMatrix.m31 := -R.Right;
ShiftMatrix.m32 := -R.Bottom;
RotationMatrix := CreateRotationMatrix(DegToRad(-90));
ShiftBackMatrix := IdentityMatrix;
ShiftBackMatrix.m31 := R.Right;
ShiftBackMatrix.m32 := R.Bottom;
Matrix := MatrixMultiply(RotationMatrix, ShiftBackMatrix);
Matrix := MatrixMultiply(ShiftMatrix, Matrix);
Matrix := MatrixMultiply(Matrix, OriginalMatrix);
PaintBox1.Canvas.SetMatrix(Matrix);
PaintBox1.Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
TTextAlign.taTrailing);
PaintBox1.Canvas.SetMatrix(OriginalMatrix);
end;
Което може да се сведе до:
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
const
S = 'Hello World';
var
R: TRectF;
SaveMatrix: TMatrix;
Matrix: TMatrix;
begin
PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
R := RectF(-Canvas.TextWidth(S), -Canvas.TextHeight(S), 0, 0);
SaveMatrix := PaintBox1.Canvas.Matrix;
Matrix := CreateRotationMatrix(DegToRad(-90));
Matrix.m31 := 50;
Matrix.m32 := 100;
PaintBox1.Canvas.MultyMatrix(Matrix);
PaintBox1.Canvas.FillText(R, S, False, 1, [], TTextAlign.taTrailing,
TTextAlign.taTrailing);
PaintBox1.Canvas.SetMatrix(SaveMatrix);
end;
Което от своя страна се развива в тази обща рутина:
procedure DrawRotatedText(Canvas: TCanvas; const P: TPointF; RadAngle: Single;
const S: String; HTextAlign, VTextAlign: TTextAlign);
var
W: Single;
H: Single;
R: TRectF;
SaveMatrix: TMatrix;
Matrix: TMatrix;
begin
W := Canvas.TextWidth(S);
H := Canvas.TextHeight(S);
case HTextAlign of
TTextAlign.taCenter: R.Left := -W / 2;
TTextAlign.taLeading: R.Left := 0;
TTextAlign.taTrailing: R.Left := -W;
end;
R.Width := W;
case VTextAlign of
TTextAlign.taCenter: R.Top := -H / 2;
TTextAlign.taLeading: R.Top := 0;
TTextAlign.taTrailing: R.Top := -H;
end;
R.Height := H;
SaveMatrix := Canvas.Matrix;
Matrix := CreateRotationMatrix(RadAngle);
Matrix.m31 := P.X;
Matrix.m32 := P.Y;
Canvas.MultyMatrix(Matrix);
Canvas.FillText(R, S, False, 1, [], HTextAlign, VTextAlign);
Canvas.SetMatrix(SaveMatrix);
end;
procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
PaintBox1.Canvas.Fill.Color := TAlphaColors.Black;
DrawRotatedText(PaintBox1.Canvas, PointF(50, 100), DegToRad(-90),
'Hello world', TTextAlign.taTrailing, TTextAlign.taTrailing);
end;
procedure TmsLineWithArrow.DoDrawTo(const aCanvas: TCanvas;
const aOrigin: TPointF);
var
l_Proxy : TmsShape;
l_OriginalMatrix: TMatrix;
l_Matrix: TMatrix;
l_Angle : Single;
l_CenterPoint : TPointF;
l_TextRect : TRectF;
begin
inherited;
aCanvas.BeginScene;
if (StartPoint <> FinishPoint) then
begin
l_OriginalMatrix := aCanvas.Matrix;
try
l_Proxy := TmsSmallTriangle.Create(FinishPoint);
try
// in Radian
l_Angle := GetArrowAngleRotation;
// create a point around which will rotate
l_CenterPoint := TPointF.Create(FinishPoint.X, FinishPoint.Y);
l_Matrix := l_OriginalMatrix;
l_Matrix := l_Matrix * TMatrix.CreateTranslation(-l_CenterPoint.X,-l_CenterPoint.Y);
l_Matrix := l_Matrix * TMatrix.CreateRotation(l_Angle);
l_Matrix := l_Matrix * TMatrix.CreateTranslation(l_CenterPoint.X,l_CenterPoint.Y);
aCanvas.SetMatrix(l_Matrix);
l_Proxy.DrawTo(aCanvas, aOrigin);
finally
FreeAndNil(l_Proxy);
end;//try..finally
finally
aCanvas.SetMatrix(l_OriginalMatrix);
aCanvas.EndScene;
end;
end;//(StartPoint <> FinishPoint)
end;
Този код работи в приложението XE5 Firemonkey. всички източници тук https://bitbucket.org/ingword/mindstream
За хората, които се интересуват, тук ще намерите версията на C++Builder, която използвам за FMX (тествана на 10.2 / Токио):
функция:
// Начертайте завъртян текст върху pMainBitmap ; Rot = трябва да е кратно на 90°!
void DrawRotatedText(TBitmap * pMainBitmap, TRectF TextDestRect, String StrTxt, int Rot)
(разбира се, добавете "{" и "}" между следния код... винаги грешка с редактора, за да публикувате този отговор...)
int SizeTextW = pMainBitmap->Canvas->TextWidth( StrTxt );
int SizeTextH = pMainBitmap->Canvas->TextHeight( StrTxt );
int SizeTextMax = (SizeTextW>SizeTextH)?SizeTextW:SizeTextH;
TRectF TheTextRect;
TheTextRect.init( 0, 0, SizeTextMax, SizeTextMax );
TBitmap * pBitmapText = new TBitmap( SizeTextMax, SizeTextMax );
if ( pBitmapText )
{
/* background color used */
pBitmapText->Clear( claBlack );
pBitmapText->Canvas->BeginScene();
// use same color than main bitmap for text
pBitmapText->Canvas->Fill->Color = pMainBitmap->Canvas->Fill->Color;
pBitmapText->Canvas->FillText(TheTextRect, StrTxt, false, 100,
TFillTextFlags()/* << TFillTextFlag::RightToLeft*/, TTextAlign::Center,
TTextAlign::Center);
pBitmapText->Canvas->EndScene();
// Canvas->EndScene must be done before doing bitmap rotate/flip...!
if( Rot==180 )
pBitmapText->FlipVertical( );
else if ( Rot!=0 )
pBitmapText->Rotate( Rot );
int PosSrcX = 0;
int PosSrcY = 0;
if ( SizeTextW>SizeTextH )
PosSrcX = (SizeTextMax-SizeTextH)/2;
else
PosSrcY = (SizeTextMax-SizeTextW)/2;
TheTextRect.init( PosSrcX, PosSrcY, PosSrcX+SizeTextH, PosSrcY+SizeTextW );
int iPosDestX = TextDestRect.left;
int iPosDestY = TextDestRect.top;
if ( (TextDestRect.right-TextDestRect.left)>SizeTextH )
iPosDestX = iPosDestX+(TextDestRect.right-TextDestRect.left-SizeTextH)/2;
if ( (TextDestRect.bottom-TextDestRect.top)>SizeTextW )
iPosDestY = iPosDestY + (TextDestRect.bottom-TextDestRect.top-SizeTextW)/2;
TextDestRect.left = iPosDestX;
TextDestRect.top = iPosDestY;
TextDestRect.right = TextDestRect.left+SizeTextH;
TextDestRect.bottom = TextDestRect.top+SizeTextW;
pMainBitmap->Canvas->DrawBitmap( pBitmapText, TheTextRect, TextDestRect, 100, true );
delete( pBitmapText );
}