fpspreadsheet: Fix width of bar series

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9200 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2024-02-02 21:50:56 +00:00
parent 5c17dcf97e
commit 5afa428929
5 changed files with 127 additions and 72 deletions

View File

@ -60,6 +60,7 @@ begin
ch.RotatedAxes := true;
fn := fn + '-rotated';
end;
ch.BarGapWidthPercent := 75;
// Add 1st bar series ("Student 1")
ser := TsBarSeries.Create(ch);

View File

@ -514,6 +514,7 @@ type
FSupportsRegression: Boolean;
FXErrorBars: TsChartErrorBars;
FYErrorBars: TsChartErrorBars;
FGroupIndex: Integer; // series with the same GroupIndex can be stacked
procedure SetXErrorBars(AValue: TsChartErrorBars);
procedure SetYErrorBars(AValue: TsChartErrorBars);
protected
@ -552,6 +553,7 @@ type
property DataLabelCalloutShape: TsChartLabelCalloutShape read FDataLabelCalloutShape write FDataLabelCalloutShape;
property DataPointStyles: TsChartDatapointStyleList read FDataPointStyles;
property FillColorRange: TsChartRange read FFillColorRange write FFillColorRange;
property GroupIndex: Integer read FGroupIndex write FGroupIndex;
property LabelBackground: TsChartFill read FLabelBackground write FLabelBackground;
property LabelBorder: TsChartLine read FLabelBorder write FLabelBorder;
property LabelFont: TsFont read FLabelFont write FLabelFont;
@ -582,13 +584,8 @@ type
end;
TsBarSeries = class(TsChartSeries)
private
FBarWidthPercent: Integer;
FBarOffsetPercent: Integer;
public
constructor Create(AChart: TsChart); override;
property BarWidthPercent: Integer read FBarWidthPercent write FBarWidthPercent;
property BarOffsetPercent: Integer read FBarOffsetPercent write FBarOffsetPercent;
property Regression;
end;
@ -757,6 +754,8 @@ type
FRotatedAxes: Boolean; // For bar series: vertical columns <--> horizontal bars
FStackMode: TsChartStackMode; // For bar and area series
FInterpolation: TsChartInterpolation; // For line/scatter series: data connection lines
FBarGapWidthPercent: Integer; // For bar series: distance between bars (relative to single bar width)
FBarOverlapPercent: Integer; // For bar series: overlap between bars
FTitle: TsChartText;
FSubTitle: TsChartText;
@ -834,6 +833,11 @@ type
{ Attributes of the plot's secondary y axis (right) }
property Y2Axis: TsChartAxis read FY2Axis write FY2Axis;
{ Gap between bars/bar groups, as percentage of single bar width }
property BarGapWidthPercent: Integer read FBarGapWidthPercent write FBarGapWidthPercent;
{ Overlapping of bars, as percentage of single bar width }
property BarOverlapPercent: Integer read FBarOverlapPercent write FBarOverlapPercent;
{ Connecting line between data points (for line and scatter series) }
property Interpolation: TsChartInterpolation read FInterpolation write FInterpolation;
{ x and y axes exchanged (mainly for bar series, but works also for scatter and bubble series) }
@ -2039,6 +2043,7 @@ begin
FLineColorRange := TsChartRange.Create(AChart);
FLabelRange := TsChartRange.Create(AChart);
FTitleAddr := TsChartCellAddr.Create(AChart);
FGroupIndex := -1;
FFill := TsChartFill.Create;
FFill.Style := cfsSolid;
@ -2284,6 +2289,7 @@ begin
inherited Create(AChart);
FChartType := ctArea;
FSupportsRegression := true;
FGroupIndex := 0;
end;
@ -2292,10 +2298,9 @@ end;
constructor TsBarSeries.Create(AChart: TsChart);
begin
inherited Create(AChart);
FBarWidthPercent := 80;
FBarOffsetPercent := 0;
FChartType := ctBar;
FSupportsRegression := true;
FGroupIndex := 0;
end;
@ -2314,7 +2319,7 @@ begin
inherited;
end;
{ Empty sheet name will be replace by name of the sheet containing the chart. }
{ Empty sheet name will be replaced by the name of the sheet containing the chart. }
procedure TsBubbleSeries.SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
begin
SetBubbleRange('', ARow1, ACol1, '', ARow2, ACol2);
@ -2620,7 +2625,6 @@ begin
FHatches := TsChartHatchList.Create;
FImages := TsChartImageList.Create;
fgradients.AddLinearGradient('g1', scRed, scBlue, 0, 0, 1, 1, 0, 0);
FWorksheet := nil;
@ -2678,6 +2682,9 @@ begin
FY2Axis.Position := capEnd;
FSeriesList := TsChartSeriesList.Create;
FBarGapWidthPercent := 50;
FBarOverlapPercent := 0;
end;
destructor TsChart.Destroy;

View File

@ -836,6 +836,14 @@ begin
s := GetAttrValue(AStyleNode, 'style:rotation-angle');
if (s <> '') and TryStrToFloat(s, value, FPointSeparatorSettings) then
Axis.LabelRotation := Round(value);
s := GetAttrValue(AStyleNode, 'chart:gap-width'); // why did they put this here ???
if TryStrToFloat(s, value, FPointSeparatorSettings) then
AChart.BarGapWidthPercent := round(value);
s := GetAttrValue(AStyleNode, 'chart:overlap'); // why did they put this here ???
if TryStrToFloat(s, value, FPointSeparatorSettings) then
AChart.BarOverlapPercent := round(value);
end;
end;
AStyleNode := AStyleNode.NextSibling;
@ -2220,6 +2228,11 @@ begin
angle := Axis.LabelRotation;
chartProps := chartProps + Format('style:rotation-angle="%.1f" ', [angle], FPointSeparatorSettings);
// Bar series gap distance and over lap -- why did they put it here?
if (chart.GetChartType = ctBar) and (Axis = chart.YAxis) then
chartProps := chartProps + Format(
'chart:gap-width="%d" chart:overlap="%d" ', [chart.BarGapWidthPercent, chart.BarOverlapPercent]);
// Label orientation
graphProps := 'svg:stroke-color="' + ColorToHTMLColorStr(Axis.AxisLine.Color) + '" ';

View File

@ -156,6 +156,8 @@ const
FALSE_TRUE: Array[boolean] of String = ('0', '1');
LEGEND_POS: Array[TsChartLegendPosition] of string = ('r', 't', 'b', 'l');
GROUPING: Array[TsChartStackMode] of string = ('clustered', 'stacked', 'percentStacked');
{$INCLUDE xlsxooxmlchart_hatch.inc}
@ -454,6 +456,7 @@ begin
while Assigned(ANode) do
begin
nodeName := ANode.NodeName;
s := GetAttrValue(ANode, 'val');
case nodeName of
'c:ser':
begin
@ -461,27 +464,23 @@ begin
ReadChartSeriesProps(ANode.FirstChild, ser);
end;
'c:barDir':
begin
s := GetAttrValue(ANode, 'val');
case s of
'col': AChart.RotatedAxes := false;
'bar': AChart.RotatedAxes := true;
end;
case s of
'col': AChart.RotatedAxes := false;
'bar': AChart.RotatedAxes := true;
end;
'c:grouping':
begin
s := GetAttrValue(ANode, 'val');
case s of
'stacked': AChart.StackMode := csmStacked;
'percentStacked': AChart.StackMode := csmStackedPercentage;
end;
case s of
'stacked': AChart.StackMode := csmStacked;
'percentStacked': AChart.StackMode := csmStackedPercentage;
end;
'c:varyColors':
;
'c:dLbls':
s := '';
'c:gapWidth':
;
; // see BarSeries
'c:overlap':
; // see BarSeries
'c:axId':
ReadChartSeriesAxis(ANode, ser);
end;
@ -499,11 +498,19 @@ begin
s := GetAttrValue(ANode, 'val');
case nodeName of
'c:gapWidth':
if TryStrToFloat(s, n, FPointSeparatorSettings) then
AChart.BarGapWidthPercent := round(n);
{
if TryStrToFloat(s, n, FPointSeparatorSettings) then
ser.BarWidthPercent := round(100 / (1 + n/100));
}
'c:overlap':
if TryStrToFloat(s, n, FPointSeparatorSettings) then
AChart.BarOverlapPercent := round(n);
{
if TryStrToFloat(s, n, FPointSeparatorSettings) then
ser.BarOffsetPercent := round(n);
}
end;
ANode := ANode.NextSibling;
end;
@ -3349,27 +3356,69 @@ procedure TsSpreadOOXMLChartWriter.WriteBarSeries(AStream: TStream;
var
indent: String;
chart: TsChart;
gapWidth: Integer = 0;
overlap: Integer = 999;
isFirstOfGroup: Boolean = true;
isLastOfGroup: Boolean = true;
{
function GroupWidth: Integer;
var
i: Integer;
ser: TsChartSeries;
begin
Result := 1;
if chart.StackMode = csmSideBySide then
for i := ASeriesIndex-1 downto 0 do
begin
ser := chart.Series[i];
if (ser is TsBarseries) and (ser.GroupIndex = ASeries.GroupIndex) then
inc(Result);
end;
end;
}
begin
indent := DupeString(' ', AIndent);
chart := ASeries.Chart;
AppendToStream(AStream,
indent + '<c:barChart>' + LE +
indent + ' <c:barDir val="col"/>' + LE +
indent + ' <c:grouping val="clustered"/>' + LE
);
if ASeries.GroupIndex > -1 then
begin
if (ASeriesIndex > 0) and (chart.Series[ASeriesIndex-1].GroupIndex = ASeries.GroupIndex) then
isfirstOfGroup := false;
if (ASeriesIndex < chart.Series.Count-1) and (chart.Series[ASeriesIndex+1].GroupIndex = ASeries.GroupIndex) then
isLastOfGroup := false;
if chart.StackMode <> csmSideBySide then
overlap := 100
end;
if isFirstOfGroup then
AppendToStream(AStream, Format(
indent + '<c:barChart>' + LE +
indent + ' <c:barDir val="col"/>' + LE +
indent + ' <c:grouping val="%s"/>' + LE,
[ GROUPING[chart.StackMode] ]
));
WriteChartSeriesNode(AStream, AIndent + 2, ASeries, ASeriesIndex);
AppendToStream(AStream, Format(
indent + ' <c:axId val="%d"/>' + LE +
indent + ' <c:axId val="%d"/>' + LE +
indent + '</c:barChart>' + LE,
[
FAxisID[ASeries.Chart.XAxis.Alignment], // <c:axId>
FAxisID[ASeries.Chart.YAxis.Alignment] // <c:axId>
]
));
if isLastOfGroup then
begin
if overlap = 999 then
overlap := chart.BarOverlapPercent;
gapWidth := chart.BarGapWidthPercent;
AppendToStream(AStream, Format(
indent + ' <c:gapWidth val="%d"/>' + LE +
indent + ' <c:overlap val="%d"/>' + LE +
indent + ' <c:axId val="%d"/>' + LE +
indent + ' <c:axId val="%d"/>' + LE +
indent + '</c:barChart>' + LE,
[
gapWidth, // <c:gapWidth>
overlap, // <c:overlap>
FAxisID[ASeries.Chart.XAxis.Alignment], // <c:axId>
FAxisID[ASeries.Chart.YAxis.Alignment] // <c:axId>
]
));
end;
end;
{@@ ----------------------------------------------------------------------------

View File

@ -142,7 +142,6 @@ type
function ActiveChartSeries(ASeries: TsChartSeries): TChartSeries;
procedure AddSeries(ASeries: TsChartSeries);
procedure FixAreaSeries({%H-}AWorkbookChart: TsChart);
procedure FixBarSeries(AWorkbookChart: TsChart);
procedure FixSource(AChartSeries: TBasicPointSeries);
procedure ClearChart;
procedure ConstructHatchPattern(AWorkbookChart: TsChart; AFill: TsChartFill; ABrush: TBrush);
@ -1638,38 +1637,6 @@ begin
end;
{$ENDIF}
{@@ ----------------------------------------------------------------------------
Adjusts bar widths and offsets for side-by-side bar charts.
-------------------------------------------------------------------------------}
procedure TsWorkbookChartLink.FixBarSeries(AWorkbookChart: TsChart);
const
TOTAL_BARWIDTH = 75;
var
i, n: Integer;
wBar: Integer;
offs: Integer;
ser: TBarSeries;
begin
if AWorkbookChart.GetChartType <> ctBar then
exit;
// Count number of bar series
n := 0;
for i := 0 to FChart.SeriesCount - 1 do
if FChart.Series[i] is TBarSeries then inc(n);
// Calc bar width and adjust offset of each series within group
wBar := TOTAL_BARWIDTH div n;
offs := (wBar - TOTAL_BARWIDTH) div 2;
for i := 0 to FChart.SeriesCount - 1 do
if FChart.Series[i] is TBarSeries then
begin
ser := TBarSeries(FChart.Series[i]);
ser.BarWidthPercent := wBar;
ser.BarOffsetPercent := offs + wBar * i;
end;
end;
procedure TsWorkbookChartLink.FixSource(AChartSeries: TBasicPointSeries);
var
i, j, nx, ny: Integer;
@ -1901,12 +1868,31 @@ end;
procedure TsWorkbookChartLink.UpdateBarSeries(AWorkbookSeries: TsBarSeries;
AChartSeries: TBarSeries);
function CalcBarWidthPercent: Integer;
var
ser: TsChartSeries;
gapwidth: Integer;
i, n: Integer;
begin
n := 1;
if (AWorkbookSeries.Chart.GetChartType = ctBar) and (AWorkbookSeries.Chart.StackMode = csmSideBySide) then
for i := 0 to AWorkbookSeries.Chart.Series.Count-1 do
begin
ser := AWorkbookSeries.Chart.Series[i];
if (ser <> AWorkbookSeries) and (ser.GroupIndex = AWorkbookSeries.GroupIndex) then
inc(n);
end;
gapWidth := AWorkbookSeries.Chart.BarGapWidthPercent;
Result := round(100/(n + gapWidth/100) * n);
end;
begin
UpdateChartBrush(AWorkbookSeries.Chart, AWorkbookSeries.Fill, AChartSeries.BarBrush);
UpdateChartPen(AWorkbookSeries.Chart, AWorkbookSeries.Line, AChartSeries.BarPen);
AChartSeries.Transparency := round(AWorkbookSeries.Fill.Transparency * 255);
AChartSeries.BarWidthPercent := AWorkbookSeries.BarWidthPercent;
AChartSeries.BarOffsetPercent := AWorkbookSeries.BarOffsetPercent;
AChartSeries.BarWidthPercent := CalcBarWidthPercent; //AWorkbookSeries.BarWidthPercent;
AChartSeries.BarOffsetPercent := 0; //AWorkbookSeries.BarOffsetPercent;
AChartSeries.BarWidthStyle := bwPercentMin;
AChartSeries.Stacked := AWorkbookSeries.Chart.StackMode <> csmSideBySide;
if AChartSeries.Source is TCalculatedChartSource then
@ -1973,7 +1959,6 @@ begin
FChart.Prepare;
UpdateChartAxisLabels(ch);
FixAreaSeries(ch);
FixBarSeries(ch);
end;
procedure TsWorkbookChartLink.UpdateChartAxis(AWorkbookAxis: TsChartAxis);