TAChart: Add new TDataPointMarksClickTool

This commit is contained in:
wp_xyz 2022-07-23 19:32:56 +02:00
parent 6c59e95098
commit 9f08e2006b
17 changed files with 260 additions and 16 deletions

View File

@ -1,16 +1,16 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Project-Id-Version: tachartlazaruspkg\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"X-Generator: Poedit 2.4.2\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.1\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: tachartstrconsts.descolor
@ -166,6 +166,10 @@ msgstr "Datenpunkt verschieben"
msgid "Data point hint"
msgstr "Datenpunkt-Hinweis"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr "Klick auf Datenpunkt-Markierungen"
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Entf"
@ -217,7 +221,7 @@ msgstr "Der Typ des Ausdrucksergebnisses muss integer oder float sein, ist aber
#: tachartstrconsts.rserrnumericaloverflow
msgid "Numerical overflow."
msgstr ""
msgstr "Numerischer Überlauf."
#: tachartstrconsts.rsexpressioncolormapseries
msgid "Math expression color map series"
@ -281,7 +285,7 @@ msgstr "Gesamte Quadratsumme (SST)"
#: tachartstrconsts.rsfittvalue
msgid "t value"
msgstr ""
msgstr "t-Wert"
#: tachartstrconsts.rsfitvarianceratio
msgid "Variance ratio F"
@ -492,7 +496,7 @@ msgstr "Editierbare Chart-Datenquelle erforderlich"
#: tachartstrconsts.rssourcesorterror
#, object-pascal-format
msgid "Selected sorting parameters are not supported by %s."
msgstr ""
msgstr "Die ausgewählten Sortier-Parameter werden von %s nicht unterstützt."
#: tachartstrconsts.rsstarsymbol
msgid "Star (lines)"
@ -552,4 +556,3 @@ msgstr "Fehler beim Umbenennen von Komponenten: %s"
#: tachartstrconsts.tastoolseditortitle
msgid "Edit tools"
msgstr "Werkzeuge bearbeiten"

View File

@ -154,6 +154,10 @@ msgstr "Datapisteen vetäminen"
msgid "Data point hint"
msgstr "Datapisteen vihje"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Poista"

View File

@ -164,6 +164,10 @@ msgstr "Glissement de point de donnée"
msgid "Data point hint"
msgstr "Indicateur de point de données"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Supprimer"

View File

@ -164,6 +164,10 @@ msgstr "Adatpont húzása"
msgid "Data point hint"
msgstr "Adatpont tipp"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Törlés"

View File

@ -165,6 +165,10 @@ msgstr "Duomenų taško vilkimas"
msgid "Data point hint"
msgstr "Duomenų taško sufleris"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Naikinti"

View File

@ -164,6 +164,10 @@ msgstr ""
msgid "Data point hint"
msgstr ""
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Usuń"

View File

@ -154,6 +154,10 @@ msgstr ""
msgid "Data point hint"
msgstr ""
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr ""

View File

@ -164,6 +164,10 @@ msgstr "Arrasto ponto de dados"
msgid "Data point hint"
msgstr "Dica ponto de dados"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Excluir"

View File

@ -164,6 +164,10 @@ msgstr "Перетаскивание величины"
msgid "Data point hint"
msgstr "Всплывающая подсказка для величины"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Удалить"

View File

@ -167,6 +167,10 @@ msgstr "Flytta datapunkt"
msgid "Data point hint"
msgstr "Datapunkt tips"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Radera"

View File

@ -167,6 +167,10 @@ msgstr "Перетягування точки даних"
msgid "Data point hint"
msgstr "Спливна підказка точки даних"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "Видалити"

View File

@ -165,6 +165,10 @@ msgstr "数据点拖拽"
msgid "Data point hint"
msgstr "数据点提示"
#: tachartstrconsts.rsdatapointmarksclicktool
msgid "Data point marks click"
msgstr ""
#: tachartstrconsts.rsdelete
msgid "Delete"
msgstr "删除"

View File

@ -69,6 +69,7 @@ resourcestring
rsDataPointDrag = 'Data point drag';
rsDataPointHint = 'Data point hint';
rsDataPointCrossHair = 'Data point crosshair';
rsDataPointMarksClickTool = 'Data point marks click';
rsUserDefinedTool = 'User-defined';
rsDistanceMeasurement = 'Distance measurement';
rsAxisClickTool = 'Axis click';

View File

@ -352,6 +352,8 @@ type
function GetNearestPoint(
const AParams: TNearestPointParams;
out AResults: TNearestPointResults): Boolean; override;
function IsPointInLabel(ADrawer: IChartDrawer; const APoint: TPoint;
var APointIndex, AYIndex: Integer): Boolean;
procedure MovePoint(var AIndex: Integer; const ANewPos: TDoublePoint); override;
procedure MovePointEx(var AIndex: Integer; AXIndex, AYIndex: Integer;
const ANewPos: TDoublePoint); override;
@ -1321,6 +1323,135 @@ begin
end;
end;
function TBasicPointSeries.IsPointInLabel(ADrawer: IChartDrawer;
const APoint: TPoint; var APointIndex, AYIndex: Integer): Boolean;
const
OFFSETS: array [TLabelDirection] of TPoint = (
(X: -1; Y: 0),
(X: 0; Y: -1),
(X: 1; Y: 0),
(X: 0; Y: 1)
);
var
y, ysum: Double;
g: TDoublePoint;
pt: TPoint;
i, si: Integer;
style: TChartStyle;
lfont: TFont;
curr, prev: Double;
ext: TDoubleRect;
yIsNaN: Boolean;
centerLvl: Double;
center: TPoint;
dir: TLabelDirection;
txt: String;
begin
Result := false;
if not Marks.IsMarkLabelsVisible then exit;
lfont := TFont.Create;
try
lfont.Assign(Marks.LabelFont);
ext := Extent;
centerLvl := AxisToGraphY((ext.a.y + ext.b.y) * 0.5);
UpdateLabelDirectionReferenceLevel(0, 0, centerLvl);
for i := FLoBound to FUpBound do begin
if SkipMissingValues(i) then
continue;
prev := IfThen(FSupportsZeroLevel, GetZeroLevel, 0.0);
for si := 0 to Source.YCount - 1 do begin
g := GetLabelDataPoint(i, si);
if FStacked then begin
if si = 0 then begin
y := Source[i]^.Y;
yIsNaN := IsNaN(y);
ysum := IfThen(yIsNaN, prev, y);
end else begin
y := Source[i]^.YList[si-1];
yIsNaN := IsNaN(y);
if yIsNaN then y := 0.0;
if Stacked then begin
ysum += y;
y := ysum;
end;
end;
if IsRotated then
g.X := AxisToGraphY(y)
// Axis-to-graph transformation is independent of axis rotation ->
// Using AxisToGraph_Y_ is correct!
else
g.Y := AxisToGraphY(y);
end else
yIsNaN := IsNaN(g.y);
txt := FormattedMark(i, '', si);
if txt = '' then
continue;
curr := TDoublePointBoolArr(g)[not IsRotated];
if FMarkPositionCentered then begin
if IsRotated then
g := DoublePoint((curr + prev) * 0.5, g.y)
else
g := DoublePoint(g.x, (curr + prev) * 0.5);
end;
if Stacked then
prev := curr;
// check only the requested y index
if (AYIndex >= 0) then begin
if si < AYIndex then
Continue
else if si > AYIndex then
break;
end;
with ParentChart do
if
((Marks.YIndex = MARKS_YINDEX_ALL) or (Marks.YIndex = si)) and
IsPointInViewPort(g) and (not yIsNaN)
then begin
if Styles <> nil then begin
style := Styles.StyleByIndex(si);
if style.UseFont then
Marks.LabelFont.Assign(style.Font)
else
Marks.LabelFont.Assign(lfont);
end;
UpdateLabelDirectionReferenceLevel(i, si, centerLvl);
dir := GetLabelDirection(IfThen(IsRotated, g.X, g.Y), centerLvl);
pt := GraphToImage(g);
if Marks.RotationCenter = rcCenter then
center := pt + OFFSETS[dir] * Marks.CenterOffset(ADrawer, txt)
else
center := pt + OFFSETS[dir] * Marks.CenterHeightOffset(ADrawer, txt);
Result := Marks.IsPointInLabel(
ADrawer,
APoint,
pt,
center,
txt
);
if Result then
begin
APointIndex := i;
AYIndex := si;
exit;
end;
end;
end;
end;
finally
Marks.LabelFont.Assign(lfont);
ParentChart.EnableRedrawing;
lfont.Free;
end;
end;
procedure TBasicPointSeries.DrawLabels(ADrawer: IChartDrawer; AYIndex: Integer = -1);
// Using AYIndex is workaround for issue #35077
var

View File

@ -352,7 +352,7 @@ type
destructor Destroy; override;
procedure Draw(ADrawer: IChartDrawer); override;
function GetAxisBounds(AAxis: TChartAxis; out AMin, AMax: Double): Boolean; override;
function GetAxisBounds({%H-}AAxis: TChartAxis; out {%H-}AMin, {%H-}AMax: Double): Boolean; override;
function GetNearestPoint(
const AParams: TNearestPointParams;
out AResults: TNearestPointResults): Boolean; override;

View File

@ -97,6 +97,8 @@ type
function GetLabelPolygon(
ADrawer: IChartDrawer; ASize: TPoint): TPointArray;
function GetTextRect: TRect;
function IsPointInLabel(ADrawer: IChartDrawer;
const APoint, ADataPoint, ALabelCenter: TPoint; const AText: String): Boolean;
function MeasureLabel(ADrawer: IChartDrawer; const AText: String): TSize;
function MeasureLabelHeight(ADrawer: IChartDrawer; const AText: String): TSize;
procedure SetInsideDir(dx, dy: Double);
@ -497,6 +499,24 @@ begin
Result := DotProduct(textdir, FInsideDir) > 0;
end;
function TChartTextElement.IsPointInLabel(ADrawer: IChartDrawer;
const APoint, ADataPoint, ALabelCenter: TPoint; const AText: String): Boolean;
var
labelPoly: TPointArray;
ptText: TPoint;
i: Integer;
begin
ApplyLabelFont(ADrawer);
ptText := ADrawer.TextExtent(AText, FTextFormat);
labelPoly := GetLabelPolygon(ADrawer, ptText);
for i := 0 to High(labelPoly) do
labelPoly[i] += ALabelCenter;
if CalloutAngle > 0 then
labelPoly := MakeCallout(labelPoly, ALabelCenter, ADataPoint, OrientToRad(CalloutAngle));
Result := IsPointInPolygon(APoint, labelPoly);
end;
function TChartTextElement.IsMarginRequired: Boolean;
begin
Result := (GetLabelBrush.Style <> bsClear) or GetFrame.EffVisible;

View File

@ -22,7 +22,7 @@ uses
Controls, CustomTimer, {GraphMath,} Forms, LCLPlatformDef, InterfaceBase,
LCLType, LCLIntf,
// TAChart
TAChartUtils, TADrawUtils, TAChartAxis, TALegend, TAGraph,
TAChartUtils, TADrawUtils, TAChartAxis, TALegend, TACustomSeries, TAGraph,
TATypes, TATextElements;
type
@ -427,7 +427,7 @@ type
FXIndex: Integer;
FYIndex: Integer;
FSeries: TBasicChartSeries;
procedure FindNearestPoint(APoint: TPoint);
procedure FindNearestPoint(APoint: TPoint); virtual;
property MouseInsideOnly: Boolean
read FMouseInsideOnly write FMouseInsideOnly default false;
property Targets: TNearestPointTargets
@ -481,6 +481,7 @@ type
read FOnDragStart write FOnDragStart;
end;
{ TDataPointClickTool }
TDataPointClickTool = class(TDataPointTool)
@ -497,6 +498,14 @@ type
read FOnPointClick write FOnPointClick;
end;
{ TDataPointMarksClickTool }
TDataPointMarksClickTool = class(TDataPointClickTool)
strict protected
procedure FindNearestPoint(APoint: TPoint); override;
end;
TDataPointHintTool = class;
TChartToolHintEvent = procedure (
@ -583,10 +592,10 @@ type
property MouseInsideOnly;
end;
TChartCrosshairShape = (ccsNone, ccsVertical, ccsHorizontal, ccsCross);
{ TDataPointCrossHairTool }
TChartCrosshairShape = (ccsNone, ccsVertical, ccsHorizontal, ccsCross);
TDataPointCrosshairTool = class(TDataPointDrawTool)
strict private
FPosition: TDoublePoint;
@ -609,7 +618,10 @@ type
property Size: Integer read FSize write FSize default -1;
property Targets;
end;
{ TAxisClickTool }
TAxisClickEvent = procedure (ASender: TChartTool; Axis: TChartAxis;
AHitInfo: TChartAxisHitTests) of object;
@ -630,6 +642,8 @@ type
property OnClick: TAxisClickEvent read FOnClick write FOnClick;
end;
{ TTitleFootClickTool }
TTitleFootClickEvent = procedure (ASender: TChartTool;
ATitle: TChartTitle) of object;
@ -647,6 +661,8 @@ type
property OnClick: TTitleFootClickEvent read FOnClick write FOnClick;
end;
{ TLegendClickTool }
TLegendClickEvent = procedure (ASender: TChartTool;
ALegend: TChartLegend) of object;
TLegendSeriesClickEvent = procedure (ASender: TChartTool;
@ -681,7 +697,7 @@ implementation
uses
LResources,
TAChartStrConsts, TACustomSeries, TAEnumerators, TAGeometry, TAMath;
TAChartStrConsts, TAEnumerators, TAGeometry, TAMath;
function InitBuiltinTools(AChart: TChart): TBasicChartToolset;
var
@ -1997,6 +2013,34 @@ begin
Handled;
end;
{ TDataPointMarksClickTool }
procedure TDataPointMarksClickTool.FindNearestPoint(APoint: TPoint);
var
ser: TBasicPointSeries;
i: Integer;
begin
FSeries := nil;
FPointIndex := -1;
FYIndex := -1;
for i := 0 to FChart.SeriesCount-1 do
begin
if not (FChart.Series[i] is TBasicChartSeries) then
continue;
ser := TBasicPointSeries(FChart.Series[i]);
if ser.Active and ser.IsPointInLabel(FChart.Drawer, APoint, FPointIndex, FYIndex) then
begin
FSeries := ser;
FXIndex := 0; // to do: fix X index
FNearestGraphPoint := FChart.ImageToGraph(APoint);
exit;
end;
end;
end;
{ TDataPointHintTool }
constructor TDataPointHintTool.Create(AOwner: TComponent);
@ -2421,6 +2465,7 @@ initialization
RegisterChartToolClass(TDataPointDragTool, @rsDataPointDrag);
RegisterChartToolClass(TDataPointHintTool, @rsDataPointHint);
RegisterChartToolClass(TDataPointCrosshairTool, @rsDataPointCrosshair);
RegisterChartToolClass(TDataPointMarksClickTool, @rsDataPointMarksClickTool);
RegisterChartToolClass(TAxisClickTool, @rsAxisClickTool);
RegisterChartToolClass(TTitleFootClickTool, @rsHeaderFooterClickTool);
RegisterChartToolClass(TLegendClickTool, @rsLegendClickTool);