mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-18 09:29:35 +02:00
TAChart: Refinements for long series labels (respect device scaling, introduce chart property MinDataSpace). Issue #34896.
git-svn-id: trunk@60151 -
This commit is contained in:
parent
33691d63bf
commit
e4ec9da618
@ -251,8 +251,8 @@ type
|
||||
function CalcOffset(AScale: Double): Double;
|
||||
function CalcScale(ASign: Integer): Double;
|
||||
constructor Init(AAxis: TChartAxis; AImageLo, AImageHi: Integer;
|
||||
AMarginLo, AMarginHi, ARequiredMarginLo, ARequiredMarginHi: Integer;
|
||||
AMin, AMax: PDouble);
|
||||
AMarginLo, AMarginHi, ARequiredMarginLo, ARequiredMarginHi, AMinDataSpace: Integer;
|
||||
HasMarksInMargin: Boolean; AMin, AMax: PDouble);
|
||||
procedure UpdateMinMax(AConv: TAxisConvFunc);
|
||||
end;
|
||||
|
||||
@ -1245,11 +1245,11 @@ begin
|
||||
end;
|
||||
|
||||
constructor TAxisCoeffHelper.Init(AAxis: TChartAxis; AImageLo, AImageHi: Integer;
|
||||
AMarginLo, AMarginHi, ARequiredMarginLo, ARequiredMarginHi: Integer;
|
||||
AMin, AMax: PDouble);
|
||||
const
|
||||
GUARANTEED_SPACE = 10;
|
||||
AMarginLo, AMarginHi, ARequiredMarginLo, ARequiredMarginHi, AMinDataSpace: Integer;
|
||||
HasMarksInMargin: Boolean; AMin, AMax: PDouble);
|
||||
begin
|
||||
Assert(AAxis <> nil);
|
||||
|
||||
FAxis := AAxis;
|
||||
FImageLo := AImageLo;
|
||||
FImageHi := AImageHi;
|
||||
@ -1258,14 +1258,16 @@ begin
|
||||
FLo := FImageLo + AMarginLo;
|
||||
FHi := FImageHi + AMarginHi;
|
||||
|
||||
if Assigned(AAxis) and AAxis.IsVertical then begin
|
||||
if (FHi + GUARANTEED_SPACE >= FLo) then
|
||||
EnsureGuaranteedSpace(FHi, FLo, FImageHi, FImageLo,
|
||||
ARequiredMarginHi, ARequiredMarginLo, GUARANTEED_SPACE);
|
||||
end else begin
|
||||
if (FLo + GUARANTEED_SPACE >= FHi) then
|
||||
EnsureGuaranteedSpace(FLo, FHi, FImageLo, FImageHi,
|
||||
ARequiredMarginLo, ARequiredMarginHi, GUARANTEED_SPACE);
|
||||
if HasMarksInMargin then begin
|
||||
if FAxis.IsVertical then begin
|
||||
if (FHi + AMinDataSpace >= FLo) then
|
||||
EnsureGuaranteedSpace(FHi, FLo, FImageHi, FImageLo,
|
||||
ARequiredMarginHi, ARequiredMarginLo, AMinDataSpace);
|
||||
end else begin
|
||||
if (FLo + AMinDataSpace >= FHi) then
|
||||
EnsureGuaranteedSpace(FLo, FHi, FImageLo, FImageHi,
|
||||
ARequiredMarginLo, ARequiredMarginHi, AMinDataSpace);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -1275,20 +1277,20 @@ begin
|
||||
Result := ASign
|
||||
else
|
||||
Result := (FHi - FLo) / (FMax^ - FMin^);
|
||||
if (FAxis <> nil) and FAxis.IsFlipped then
|
||||
if FAxis.IsFlipped then
|
||||
Result := -Result;
|
||||
end;
|
||||
|
||||
function TAxisCoeffHelper.CalcOffset(AScale: Double): Double;
|
||||
begin
|
||||
Result := (FLo + FHi) / 2 - AScale * (FMin^ + FMax^) / 2;
|
||||
Result := ((FLo + FHi) - AScale * (FMin^ + FMax^)) * 0.5;
|
||||
end;
|
||||
|
||||
procedure TAxisCoeffHelper.UpdateMinMax(AConv: TAxisConvFunc);
|
||||
begin
|
||||
FMin^ := AConv(FImageLo);
|
||||
FMax^ := AConv(FImageHi);
|
||||
if (FAxis <> nil) and FAxis.IsFlipped then
|
||||
if FAxis.IsFlipped then
|
||||
Exchange(FMin^, FMax^);
|
||||
end;
|
||||
|
||||
|
@ -204,6 +204,7 @@ type
|
||||
FLogicalExtent: TDoubleRect;
|
||||
FMargins: TChartMargins;
|
||||
FMarginsExternal: TChartMargins;
|
||||
FMinDataSpace: Integer;
|
||||
FOnAfterCustomDrawBackground: TChartAfterCustomDrawEvent;
|
||||
FOnAfterCustomDrawBackWall: TChartAfterCustomDrawEvent;
|
||||
FOnAfterDraw: TChartDrawEvent;
|
||||
@ -244,7 +245,8 @@ type
|
||||
FSavedClipRect: TRect;
|
||||
FClipRectLock: Integer;
|
||||
|
||||
procedure CalculateTransformationCoeffs(const AMargin: TRect);
|
||||
procedure CalculateTransformationCoeffs(const AMargin, AChartMargins: TRect;
|
||||
const AMinDataSpace: Integer);
|
||||
procedure DrawReticule(ADrawer: IChartDrawer); deprecated 'Use DatapointCrosshairTool instead';
|
||||
procedure FindComponentClass(
|
||||
AReader: TReader; const AClassName: String; var AClass: TComponentClass);
|
||||
@ -271,6 +273,7 @@ type
|
||||
procedure SetLogicalExtent(const AValue: TDoubleRect);
|
||||
procedure SetMargins(AValue: TChartMargins);
|
||||
procedure SetMarginsExternal(AValue: TChartMargins);
|
||||
procedure SetMinDataSpace(const AValue: Integer);
|
||||
procedure SetOnAfterCustomDrawBackground(AValue: TChartAfterCustomDrawEvent);
|
||||
procedure SetOnAfterCustomDrawBackWall(AValue: TChartAfterCustomDrawEvent);
|
||||
procedure SetOnAfterDraw(AValue: TChartDrawEvent);
|
||||
@ -384,6 +387,8 @@ type
|
||||
property ExtentBroadcaster: TBroadcaster read FExtentBroadcaster;
|
||||
property IsZoomed: Boolean read FIsZoomed;
|
||||
property LogicalExtent: TDoubleRect read FLogicalExtent write SetLogicalExtent;
|
||||
property MinDataSpace: Integer
|
||||
read FMinDataSpace write SetMinDataSpace; // default DEF_MIN_DATA_SPACE;
|
||||
property OnChartPaint: TChartPaintEvent
|
||||
read FOnChartPaint write SetOnChartPaint; experimental;
|
||||
property PrevLogicalExtent: TDoubleRect read FPrevLogicalExtent;
|
||||
@ -577,16 +582,22 @@ begin
|
||||
StyleChanged(ASeries);
|
||||
end;
|
||||
|
||||
procedure TChart.CalculateTransformationCoeffs(const AMargin: TRect);
|
||||
procedure TChart.CalculateTransformationCoeffs(const AMargin, AChartMargins: TRect;
|
||||
const AMinDataSpace: Integer);
|
||||
var
|
||||
rX, rY: TAxisCoeffHelper;
|
||||
begin
|
||||
rX.Init(
|
||||
BottomAxis, FClipRect.Left, FClipRect.Right, AMargin.Left, -AMargin.Right,
|
||||
FMargins.Left, FMargins.Right, @FCurrentExtent.a.X, @FCurrentExtent.b.X);
|
||||
AChartMargins.Left, AChartMargins.Right, AMinDataSpace,
|
||||
(AMargin.Left <> AChartMargins.Left) or (AMargin.Right <> AChartMargins.Right),
|
||||
@FCurrentExtent.a.X, @FCurrentExtent.b.X);
|
||||
rY.Init(
|
||||
LeftAxis, FClipRect.Bottom, FClipRect.Top, -AMargin.Bottom, AMargin.Top,
|
||||
FMargins.Bottom, FMargins.Top, @FCurrentExtent.a.Y, @FCurrentExtent.b.Y);
|
||||
AChartMargins.Bottom, AChartMargins.Top, AMinDataSpace,
|
||||
(AMargin.Top <> AChartMargins.Top) or (AMargin.Bottom <> AChartMargins.Bottom),
|
||||
@FCurrentExtent.a.Y, @FCurrentExtent.b.Y);
|
||||
|
||||
FScale.X := rX.CalcScale(1);
|
||||
FScale.Y := rY.CalcScale(-1);
|
||||
if Proportional then begin
|
||||
@ -730,6 +741,7 @@ begin
|
||||
FExtentSizeLimit := TChartExtent.Create(Self);
|
||||
FMargins := TChartMargins.Create(Self);
|
||||
FMarginsExternal := TChartMargins.Create(Self);
|
||||
FMinDataSpace := DEF_MIN_DATA_SPACE;
|
||||
|
||||
if OnInitBuiltinTools <> nil then
|
||||
FBuiltinToolset := OnInitBuiltinTools(Self);
|
||||
@ -1435,34 +1447,42 @@ var
|
||||
tries: Integer;
|
||||
prevExt: TDoubleRect;
|
||||
axis: TChartAxis;
|
||||
scaled_depth: Integer;
|
||||
scDepth: Integer;
|
||||
scChartMargins: TRect;
|
||||
scMinDataSpace: Integer;
|
||||
begin
|
||||
scaled_depth := ADrawer.Scale(Depth);
|
||||
scDepth := ADrawer.Scale(Depth);
|
||||
scChartMargins.Left := ADrawer.Scale(Margins.Left);
|
||||
scChartMargins.Right := ADrawer.Scale(Margins.Right);
|
||||
scChartMargins.Top := ADrawer.Scale(Margins.Top);
|
||||
scChartMargins.Bottom := ADrawer.Scale(Margins.Bottom);
|
||||
scMinDataSpace := ADrawer.Scale(FMinDataSpace);
|
||||
|
||||
if not AxisVisible then begin
|
||||
FClipRect.Left += scaled_depth;
|
||||
FClipRect.Bottom -= scaled_depth;
|
||||
CalculateTransformationCoeffs(GetMargins(ADrawer));
|
||||
FClipRect.Left += scDepth;
|
||||
FClipRect.Bottom -= scDepth;
|
||||
CalculateTransformationCoeffs(GetMargins(ADrawer), scChartMargins, scMinDataSpace);
|
||||
exit;
|
||||
end;
|
||||
|
||||
AxisList.PrepareGroups;
|
||||
for axis in AxisList do
|
||||
axis.PrepareHelper(ADrawer, Self, @FClipRect, scaled_depth);
|
||||
axis.PrepareHelper(ADrawer, Self, @FClipRect, scDepth);
|
||||
|
||||
// There is a cyclic dependency: extent -> visible marks -> margins.
|
||||
// We recalculate them iteratively hoping that the process converges.
|
||||
CalculateTransformationCoeffs(ZeroRect);
|
||||
CalculateTransformationCoeffs(ZeroRect, scChartMargins, scMinDataSpace);
|
||||
cr := FClipRect;
|
||||
for tries := 1 to 10 do begin
|
||||
axisMargin := AxisList.Measure(CurrentExtent, scaled_depth);
|
||||
axisMargin[calLeft] := Max(axisMargin[calLeft], scaled_depth);
|
||||
axisMargin[calBottom] := Max(axisMargin[calBottom], scaled_depth);
|
||||
axisMargin := AxisList.Measure(CurrentExtent, scDepth);
|
||||
axisMargin[calLeft] := Max(axisMargin[calLeft], scDepth);
|
||||
axisMargin[calBottom] := Max(axisMargin[calBottom], scDepth);
|
||||
FClipRect := cr;
|
||||
for aa := Low(aa) to High(aa) do
|
||||
SideByAlignment(FClipRect, aa, -axisMargin[aa]);
|
||||
prevExt := FCurrentExtent;
|
||||
FCurrentExtent := FLogicalExtent;
|
||||
CalculateTransformationCoeffs(GetMargins(ADrawer));
|
||||
CalculateTransformationCoeffs(GetMargins(ADrawer), scChartMargins, scMinDataSpace);
|
||||
if prevExt = FCurrentExtent then break;
|
||||
prevExt := FCurrentExtent;
|
||||
end;
|
||||
@ -1681,6 +1701,13 @@ begin
|
||||
StyleChanged(Self);
|
||||
end;
|
||||
|
||||
procedure TChart.SetMinDataSpace(const AValue: Integer);
|
||||
begin
|
||||
if FMinDataSpace = abs(AValue) then exit;
|
||||
FMinDataSpace := abs(AValue);
|
||||
StyleChanged(Self);
|
||||
end;
|
||||
|
||||
procedure TChart.SetName(const AValue: TComponentName);
|
||||
var
|
||||
oldName: String;
|
||||
|
@ -27,6 +27,7 @@ uses
|
||||
|
||||
const
|
||||
DEF_MARGIN = 4;
|
||||
DEF_MIN_DATA_SPACE = 10;
|
||||
DEF_MARKS_DISTANCE = 20;
|
||||
DEF_POINTER_SIZE = 4;
|
||||
MARKS_YINDEX_ALL = -1;
|
||||
|
Loading…
Reference in New Issue
Block a user