{ /*************************************************************************** TATypes.pas ----------- Component Library Standard Graph Element Types ***************************************************************************/ ***************************************************************************** See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** Authors: Luнs Rodrigues, Philippe Martinole, Alexander Klenin } unit TATypes; {$MODE ObjFPC}{$H+} interface uses Classes, Graphics, Controls, FPCanvas, TAChartUtils, TADrawUtils; const DEF_MARGIN = 4; DEF_MIN_DATA_SPACE = 10; DEF_MARKS_DISTANCE = 20; DEF_POINTER_SIZE = 4; MARKS_YINDEX_ALL = -1; DEF_ARROW_LENGTH = 10; DEF_ARROW_WIDTH = 5; DEF_SHADOW_OFFSET = 8; DEF_SHADOW_TRANSPARENCY = 128; CMD_QUERY_SERIESEXTENT = 0; type TCustomChart = class(TCustomControl) public procedure Notify(ACommand: Integer; AParam1, AParam2: Pointer; var Data); virtual; abstract; procedure StyleChanged(Sender: TObject); virtual; abstract; procedure ZoomFull(AImmediateRecalc: Boolean = false); virtual; abstract; end; { TChartPen } TChartPen = class(TPen) strict private FVisible: Boolean; procedure SetVisible(AValue: Boolean); public constructor Create; override; public procedure Assign(ASource: TPersistent); override; function EffVisible: Boolean; inline; published property Color default clDefault; property Visible: Boolean read FVisible write SetVisible default true; end; TClearBrush = class(TBrush) public constructor Create; override; published // property Style default bsClear; { wp: Removed because bsClear would no longer work - TBrush.SetColor switches Style to clSolid if Style is bsClear } end; TFPCanvasHelperClass = class of TFPCanvasHelper; { TChartElement } TChartElement = class(TPersistent) strict protected FOwner: TCustomChart; FVisible: Boolean; procedure InitHelper(var AResult; AClass: TFPCanvasHelperClass); procedure SetVisible(AValue: Boolean); procedure StyleChanged(Sender: TObject); virtual; protected public constructor Create(AOwner: TCustomChart); public procedure Assign(ASource: TPersistent); override; function GetOwner: TPersistent; override; procedure SetOwner(AOwner: TCustomChart); property Visible: Boolean read FVisible write SetVisible; end; TSeriesPointerStyle = ( psNone, psRectangle, psCircle, psCross, psDiagCross, psStar, psLowBracket, psHighBracket, psLeftBracket, psRightBracket, psDiamond, psTriangle, psLeftTriangle, psRightTriangle, psVertBar, psHorBar, psPoint, psDownTriangle, psHexagon, psFullStar); { TSeriesPointer } TSeriesPointer = class(TChartElement) strict private FBrush: TBrush; FHorizSize: Integer; FOverrideColor: TOverrideColors; FPen: TChartPen; FStyle: TSeriesPointerStyle; FUpdateLock: Integer; FVertSize: Integer; procedure SetBrush(AValue: TBrush); procedure SetHorizSize(AValue: Integer); procedure SetOverrideColor(AValue: TOverrideColors); procedure SetPen(AValue: TChartPen); procedure SetStyle(AValue: TSeriesPointerStyle); procedure SetVertSize(AValue: Integer); strict protected procedure StyleChanged(Sender: TObject); override; public constructor Create(AOwner: TCustomChart); destructor Destroy; override; public procedure Assign(Source: TPersistent); override; procedure Draw(ADrawer: IChartDrawer; ACenter: TPoint; AColor: TColor; ABrushAlreadySet: Boolean = false); procedure DrawSize(ADrawer: IChartDrawer; ACenter, ASize: TPoint; AColor: TColor; AAngle: Double = 0.0; ABrushAlreadySet: Boolean = false); procedure LockUpdate; procedure UnlockUpdate; function UpdateLocked: Boolean; published property Brush: TBrush read FBrush write SetBrush; property HorizSize: Integer read FHorizSize write SetHorizSize default DEF_POINTER_SIZE; property OverrideColor: TOverrideColors read FOverrideColor write SetOverrideColor default [ocBrush]; property Pen: TChartPen read FPen write SetPen; property Style: TSeriesPointerStyle read FStyle write SetStyle default psRectangle; property VertSize: Integer read FVertSize write SetVertSize default DEF_POINTER_SIZE; property Visible default false; end; EExtentError = class(EChartError); TChartRange = class(TChartElement) strict private FBounds: array [1..2] of Double; FUseBounds: array [1..2] of Boolean; function GetBounds(AIndex: Integer): Double; function GetUseBounds(AIndex: integer): Boolean; function IsBoundsStored(AIndex: Integer): Boolean; procedure SetBounds(AIndex: Integer; const AValue: Double); procedure SetUseBounds(AIndex: Integer; AValue: Boolean); public procedure Assign(ASource: TPersistent); override; procedure CheckBoundsOrder; procedure Intersect(var AMin, AMax: Double); published property Max: Double index 2 read GetBounds write SetBounds stored IsBoundsStored; property Min: Double index 1 read GetBounds write SetBounds stored IsBoundsStored; property UseMax: Boolean index 2 read GetUseBounds write SetUseBounds default false; property UseMin: Boolean index 1 read GetUseBounds write SetUseBounds default false; end; { TChartExtent } TChartExtent = class(TChartElement) strict private FExtent: TDoubleRect; FUseBounds: array [1..4] of Boolean; function GetBounds(AIndex: Integer): Double; function GetUseBounds(AIndex: integer): Boolean; function IsBoundsStored(AIndex: Integer): Boolean; procedure SetBounds(AIndex: Integer; const AValue: Double); procedure SetUseBounds(AIndex: Integer; AValue: Boolean); public procedure Assign(ASource: TPersistent); override; procedure CheckBoundsOrder; procedure FixTo(const ABounds: TDoubleRect); published property UseXMax: Boolean index 3 read GetUseBounds write SetUseBounds default false; property UseXMin: Boolean index 1 read GetUseBounds write SetUseBounds default false; property UseYMax: Boolean index 4 read GetUseBounds write SetUseBounds default false; property UseYMin: Boolean index 2 read GetUseBounds write SetUseBounds default false; property XMax: Double index 3 read GetBounds write SetBounds stored IsBoundsStored; property XMin: Double index 1 read GetBounds write SetBounds stored IsBoundsStored; property YMax: Double index 4 read GetBounds write SetBounds stored IsBoundsStored; property YMin: Double index 2 read GetBounds write SetBounds stored IsBoundsStored; end; TChartExtentHistory = specialize THistory; TRectArray = array [1..4] of Integer; { TChartMargins } TChartMargins = class(TChartElement) strict private FData: record case Integer of 0: (FRect: TRect;); 1: (FCoords: TRectArray;); end; public constructor Create(AOwner: TCustomChart); function GetValue(AIndex: Integer): Integer; procedure SetValue(AIndex: Integer; AValue: TChartDistance); public procedure Assign(Source: TPersistent); override; procedure ExpandRectScaled(ADrawer: IChartDrawer; var ARect: TRect); property Data: TRect read FData.FRect; published property Left: TChartDistance index 1 read GetValue write SetValue default DEF_MARGIN; property Top: TChartDistance index 2 read GetValue write SetValue default DEF_MARGIN; property Right: TChartDistance index 3 read GetValue write SetValue default DEF_MARGIN; property Bottom: TChartDistance index 4 read GetValue write SetValue default DEF_MARGIN; end; TChartArrow = class(TChartElement) strict private FBaseLength: TChartDistance; FInverted: Boolean; FLength: TChartDistance; FWidth: TChartDistance; procedure SetBaseLength(AValue: TChartDistance); procedure SetInverted(AValue: Boolean); procedure SetLength(AValue: TChartDistance); procedure SetWidth(AValue: TChartDistance); public constructor Create(AOwner: TCustomChart); public procedure Assign(ASource: TPersistent); override; procedure Draw( ADrawer: IChartDrawer; const AEndPos: TPoint; AAngle: Double; APen: TFPCustomPen); published property BaseLength: TChartDistance read FBaseLength write SetBaseLength default 0; property Inverted: Boolean read FInverted write SetInverted default false; property Length: TChartDistance read FLength write SetLength default DEF_ARROW_LENGTH; property Visible default false; property Width: TChartDistance read FWidth write SetWidth default DEF_ARROW_WIDTH; end; TChartShadow = class(TChartElement) strict private FColor: TColor; FOffset: TPoint; FTransparency: TChartTransparency; procedure SetColor(AValue: TColor); procedure SetOffsetX(AValue: Integer); procedure SetOffsetY(AValue: Integer); procedure SetTransparency(AValue: TChartTransparency); public constructor Create(AOwner: TCustomChart); public procedure Assign(ASource: TPersistent); override; published property Color: TColor read FColor write SetColor default clBlack; property OffsetX: Integer read FOffset.X write SetOffsetX default DEF_SHADOW_OFFSET; property OffsetY: Integer read FOffset.Y write SetOffsetY default DEF_SHADOW_OFFSET; property Transparency: TChartTransparency read FTransparency write SetTransparency default DEF_SHADOW_TRANSPARENCY; property Visible default false; end; TChartErrorBar = class(TChartElement) private FWidth: Integer; FPen: TPen; procedure SetPen(const AValue: TPen); procedure SetWidth(const AValue: Integer); public constructor Create(AOwner: TCustomChart); destructor Destroy; override; procedure Assign(ASource: TPersistent); override; published property Pen: TPen read FPen write SetPen; property Visible default false; property Width: Integer read FWidth write SetWidth default -1; end; implementation uses Math, SysUtils, TAGeometry; { TChartPen } procedure TChartPen.Assign(ASource: TPersistent); begin if ASource is TChartPen then FVisible := TChartPen(ASource).Visible; inherited Assign(ASource); end; constructor TChartPen.Create; begin inherited Create; SetPropDefaults(Self, ['Color', 'Style', 'Visible']); end; function TChartPen.EffVisible: Boolean; begin Result := Visible and (Style <> psClear); end; procedure TChartPen.SetVisible(AValue: Boolean); begin FVisible := AValue; if Assigned(OnChange) then OnChange(Self); end; { TClearBrush } constructor TClearBrush.Create; begin inherited; Style := bsClear; end; { TChartElement } procedure TChartElement.Assign(ASource: TPersistent); begin if ASource is TChartElement then with TChartElement(ASource) do begin Self.FVisible := FVisible; Self.FOwner := FOwner; end; end; constructor TChartElement.Create(AOwner: TCustomChart); begin inherited Create; FOwner := AOwner; end; function TChartElement.GetOwner: TPersistent; begin Result := FOwner; end; procedure TChartElement.InitHelper(var AResult; AClass: TFPCanvasHelperClass); begin TFPCanvasHelper(AResult) := AClass.Create; TFPCanvasHelper(AResult).OnChange := @StyleChanged; end; procedure TChartElement.SetOwner(AOwner: TCustomChart); begin FOwner := AOwner; end; procedure TChartElement.SetVisible(AValue: Boolean); begin if FVisible = AValue then exit; FVisible := AValue; StyleChanged(Self); end; procedure TChartElement.StyleChanged(Sender: TObject); begin if FOwner <> nil then FOwner.StyleChanged(Sender); end; { TSeriesPointer } procedure TSeriesPointer.Assign(Source: TPersistent); begin if Source is TSeriesPointer then with TSeriesPointer(Source) do begin Self.FBrush.Assign(Brush); Self.FHorizSize := HorizSize; Self.FOverrideColor := OverrideColor; Self.FPen.Assign(Pen); Self.FStyle := Style; Self.FVertSize := VertSize; end; inherited Assign(Source); end; constructor TSeriesPointer.Create(AOwner: TCustomChart); begin inherited Create(AOwner); InitHelper(FBrush, TBrush); InitHelper(FPen, TChartPen); FHorizSize := DEF_POINTER_SIZE; SetPropDefaults(Self, ['OverrideColor', 'Style']); FVertSize := DEF_POINTER_SIZE; FVisible := false; end; destructor TSeriesPointer.Destroy; begin FreeAndNil(FBrush); FreeAndNil(FPen); inherited; end; { Draws the pointer. If ABrushAlreadySet is true then the method assumes that the brush already has been set by the calling routine and does not change it any further (needed for applying ChartStyle brush). } procedure TSeriesPointer.Draw(ADrawer: IChartDrawer; ACenter: TPoint; AColor: TColor; ABrushAlreadySet: Boolean = false); begin DrawSize(ADrawer, ACenter, Point(ADrawer.Scale(HorizSize), ADrawer.Scale(VertSize)), AColor, 0, ABrushAlreadySet ); end; procedure TSeriesPointer.DrawSize(ADrawer: IChartDrawer; ACenter, ASize: TPoint; AColor: TColor; AAngle: Double; ABrushAlreadySet: Boolean); function PointByIndex(AIndex: Char): TPoint; inline; // 7--8--9 // 4 5 6 // 1--2--3 const V: array ['1'..'9'] of -1..1 = (1, 1, 1, 0, 0, 0, -1, -1, -1); H: array ['1'..'9'] of -1..1 = (-1, 0, 1, -1, 0, 1, -1, 0, 1); begin Result := ACenter + RotatePoint( Point(H[AIndex] * ASize.X, V[AIndex] * ASize.Y), AAngle); end; procedure DrawByString(const AStr: String); var pts: array of TPoint = nil; i: Integer; j: Integer = 0; notClosed: Boolean; begin notClosed := (AStr[1] <> AStr[Length(AStr) - 1]); SetLength(pts, Length(AStr)); for i := 1 to Length(AStr) do begin if AStr[i] = ' ' then begin if (Brush.Style = bsClear) or notClosed then ADrawer.Polyline(pts, 0, j) else ADrawer.Polygon(pts, 0, j); // Winding? j := 0; end else begin pts[j] := PointByIndex(AStr[i]); Inc(j); end; end; end; procedure DrawPolygon(AStyle: TSeriesPointerStyle); const INNER = 0.5; var p: array of TPoint = nil; pt: TDoublePoint; phi, sinphi, cosphi, dPhi: Math.float; i: Integer; begin case AStyle of psHexagon : begin dPhi := pi / 3; SetLength(p, 7); end; psFullStar : begin dPhi := pi / 6; SetLength(p, 13); end; else ; end; phi := 0; for i := 0 to High(p) do begin SinCos(phi, sinphi, cosphi); pt := DoublePoint(ASize.X * cosPhi, ASize.Y * sinPhi); if odd(i) and (AStyle = psFullStar) then pt := pt * INNER; p[i] := ACenter + RoundPoint(RotatePoint(pt, AAngle)); phi += dPhi; end; ADrawer.Polygon(p, 0, Length(p)); end; const DRAW_STRINGS: array [TSeriesPointerStyle] of String = ( // psNone, psRectangle, psCircle, psCross, psDiagCross, psStar, // psLowBracket, psHighBracket, psLeftBracket, psRightBracket, psDiamond, // psTriangle, psLeftTriangle, psRightTriangle, psVertBar, psHorBar, // psPoint, pwDownTriangle, psHexagon, psFullStar '', '17931', '', '28 46', '19 73', '', //28 46 19 73', '41236', '47896', '87412', '89632', '84268', '1831', '8428', '8628', '82', '46', '', '7927', '', ''); begin if not ABrushAlreadySet then begin ADrawer.Brush := Brush; if (ocBrush in OverrideColor) and (AColor <> clTAColor) then ADrawer.SetBrushParams(Brush.Style, AColor); end; ADrawer.Pen := Pen; if (ocPen in OverrideColor) and (AColor <> clTAColor) then ADrawer.SetPenParams(Pen.Style, AColor); case Style of psNone : ; psPoint : ADrawer.PutPixel(ACenter.X, ACenter.Y, Pen.Color); psCircle : ADrawer.Ellipse( ACenter.X - ASize.X, ACenter.Y - ASize.Y, ACenter.X + ASize.X + 1, ACenter.Y + ASize.Y + 1); psHexagon : DrawPolygon(psHexagon); psStar : begin DrawByString(DRAW_STRINGS[psCross] + ' '); ASize := Point(ASize.X * 7 div 10, ASize.Y * 7 div 10); DrawByString(DRAW_STRINGS[psDiagCross] + ' '); end; psFullStar: DrawPolygon(psFullStar); else DrawByString(DRAW_STRINGS[Style] + ' '); end; end; procedure TSeriesPointer.LockUpdate; begin inc(FUpdateLock); end; procedure TSeriesPointer.UnlockUpdate; begin dec(FUpdateLock); end; function TSeriesPointer.UpdateLocked: Boolean; begin Result := FUpdateLock <> 0; end; procedure TSeriesPointer.SetBrush(AValue: TBrush); begin FBrush.Assign(AValue); StyleChanged(Self); end; procedure TSeriesPointer.SetHorizSize(AValue: Integer); begin if FHorizSize = AValue then exit; FHorizSize := AValue; StyleChanged(Self); end; procedure TSeriesPointer.SetOverrideColor(AValue: TOverrideColors); begin if FOverrideColor = AValue then exit; FOverrideColor := AValue; StyleChanged(Self); end; procedure TSeriesPointer.SetPen(AValue: TChartPen); begin FPen.Assign(AValue); StyleChanged(Self); end; procedure TSeriesPointer.SetStyle(AValue: TSeriesPointerStyle); begin if FStyle = AValue then exit; FStyle := AValue; StyleChanged(Self); end; procedure TSeriesPointer.SetVertSize(AValue: Integer); begin if FVertSize = AValue then exit; FVertSize := AValue; StyleChanged(Self); end; procedure TSeriesPointer.StyleChanged(Sender: TObject); begin if (FUpdateLock = 0) then inherited StyleChanged(Sender); end; { TChartRange } procedure TChartRange.Assign(ASource: TPersistent); begin if ASource is TChartRange then with TChartRange(ASource) do begin Self.FBounds := FBounds; Self.FUseBounds := FUseBounds; end; inherited Assign(ASource); end; procedure TChartRange.CheckBoundsOrder; begin if UseMin and UseMax and (Min >= Max) then begin UseMin := false; UseMax := false; raise EExtentError.Create('ChartRange: Min >= Max'); end; end; function TChartRange.GetBounds(AIndex: Integer): Double; begin Result := FBounds[AIndex]; end; function TChartRange.GetUseBounds(AIndex: integer): Boolean; begin Result := FUseBounds[AIndex]; end; procedure TChartRange.Intersect(var AMin, AMax: Double); begin if UseMin and (Min > AMin) then AMin := Min; if UseMax and (Max < AMax)then AMax := Max; end; function TChartRange.IsBoundsStored(AIndex: Integer): Boolean; begin Result := not SameValue(FBounds[AIndex], 0); end; procedure TChartRange.SetBounds(AIndex: Integer; const AValue: Double); begin FBounds[AIndex] := AValue; StyleChanged(Self); end; procedure TChartRange.SetUseBounds(AIndex: Integer; AValue: Boolean); begin FUseBounds[AIndex] := AValue; StyleChanged(Self); end; { TChartExtent } procedure TChartExtent.Assign(ASource: TPersistent); begin if ASource is TChartExtent then with TChartExtent(ASource) do begin Self.FExtent := FExtent; Self.FUseBounds := FUseBounds; end; inherited Assign(ASource); end; procedure TChartExtent.CheckBoundsOrder; begin if UseXMin and UseXMax and (XMin >= XMax) then begin UseXMin := false; UseXMax := false; raise EExtentError.Create('ChartExtent: XMin >= XMax'); end; if UseYMin and UseYMax and (YMin >= YMax) then begin UseYMin := false; UseYMax := false; raise EExtentError.Create('ChartExtent: YMin >= YMax'); end; end; procedure TChartExtent.FixTo(const ABounds: TDoubleRect); begin FExtent := ABounds; FillChar(FUseBounds, SizeOf(FUseBounds), true); StyleChanged(Self); end; function TChartExtent.GetBounds(AIndex: Integer): Double; begin Result := FExtent.coords[AIndex]; end; function TChartExtent.GetUseBounds(AIndex: Integer): Boolean; begin Result := FUseBounds[AIndex]; end; function TChartExtent.IsBoundsStored(AIndex: Integer): Boolean; begin Result := not SameValue(FExtent.coords[AIndex], 0.0); end; procedure TChartExtent.SetBounds(AIndex: Integer; const AValue: Double); begin FExtent.coords[AIndex] := AValue; StyleChanged(Self); end; procedure TChartExtent.SetUseBounds(AIndex: Integer; AValue: Boolean); begin FUseBounds[AIndex] := AValue; StyleChanged(Self); end; { TChartMargins } procedure TChartMargins.Assign(Source: TPersistent); begin if Source is TChartMargins then Self.FData.FRect := TChartMargins(Source).FData.FRect; inherited Assign(Source); end; constructor TChartMargins.Create(AOwner: TCustomChart); begin inherited Create(AOwner); SetPropDefaults(Self, ['Left', 'Top', 'Right', 'Bottom']); end; procedure TChartMargins.ExpandRectScaled( ADrawer: IChartDrawer; var ARect: TRect); begin ARect.TopLeft -= Point(ADrawer.Scale(Left), ADrawer.Scale(Top)); ARect.BottomRight += Point(ADrawer.Scale(Right), ADrawer.Scale(Bottom)); end; function TChartMargins.GetValue(AIndex: Integer): Integer; begin Result := FData.FCoords[AIndex]; end; procedure TChartMargins.SetValue(AIndex: Integer; AValue: TChartDistance); begin if FData.FCoords[AIndex] = AValue then exit; FData.FCoords[AIndex] := AValue; StyleChanged(Self); end; { TChartArrow } procedure TChartArrow.Assign(ASource: TPersistent); begin if ASource is TChartArrow then with TChartArrow(ASource) do begin Self.FBaseLength := FBaseLength; Self.FLength := FLength; Self.FWidth := FWidth; end; inherited Assign(ASource); end; constructor TChartArrow.Create(AOwner: TCustomChart); begin inherited Create(AOwner); FLength := DEF_ARROW_LENGTH; FVisible := false; FWidth := DEF_ARROW_WIDTH; end; procedure TChartArrow.Draw( ADrawer: IChartDrawer; const AEndPos: TPoint; AAngle: Double; APen: TFPCustomPen); var da: Double; diag: Integer; pt1, pt2, ptBase: TPoint; sgn: Integer; begin if not Visible then exit; da := ArcTan2(Width, Length); sgn := Math.IfThen(FInverted, -1, +1); diag := -ADrawer.Scale(Round(Sqrt(Sqr(Length) + Sqr(Width)))); pt1 := AEndPos + RotatePointX(diag, AAngle - da)*sgn; pt2 := AEndPos + RotatePointX(diag, AAngle + da)*sgn; ADrawer.SetPenParams(psSolid, FPColorToTColor(APen.FPColor), APen.Width); if BaseLength > 0 then begin ptBase := AEndPos + RotatePointX(-ADrawer.Scale(BaseLength), AAngle)*sgn; ADrawer.SetBrushParams(bsSolid, FPColorToChartColor(APen.FPColor)); ADrawer.Polygon([pt1, AEndPos, pt2, ptBase], 0, 4); end else ADrawer.Polyline([pt1, AEndPos, pt2], 0, 3); end; procedure TChartArrow.SetBaseLength(AValue: TChartDistance); begin if FBaseLength = AValue then exit; FBaseLength := AValue; StyleChanged(Self); end; procedure TChartArrow.SetInverted(AValue: Boolean); begin if FInverted = AValue then exit; FInverted := AValue; StyleChanged(Self); end; procedure TChartArrow.SetLength(AValue: TChartDistance); begin if FLength = AValue then exit; FLength := AValue; StyleChanged(Self); end; procedure TChartArrow.SetWidth(AValue: TChartDistance); begin if FWidth = AValue then exit; FWidth := AValue; StyleChanged(Self); end; { TChartShadow } procedure TChartShadow.Assign(ASource: TPersistent); begin if ASource is TChartShadow then with TChartShadow(ASource) do begin Self.FColor := Color; Self.FOffset := FOffset; Self.FTransparency := Transparency; end; inherited Assign(ASource); end; constructor TChartShadow.Create(AOwner: TCustomChart); begin inherited Create(AOwner); FColor := clBlack; FOffset := Point(DEF_SHADOW_OFFSET, DEF_SHADOW_OFFSET); FTransparency := DEF_SHADOW_TRANSPARENCY; end; procedure TChartShadow.SetColor(AValue: TColor); begin if FColor = AValue then exit; FColor := AValue; StyleChanged(Self); end; procedure TChartShadow.SetOffsetX(AValue: Integer); begin if FOffset.X = AValue then exit; FOffset.X := AValue; StyleChanged(Self); end; procedure TChartShadow.SetOffsetY(AValue: Integer); begin if FOffset.Y = AValue then exit; FOffset.Y := AValue; StyleChanged(Self); end; procedure TChartShadow.SetTransparency(AValue: TChartTransparency); begin if FTransparency = AValue then exit; FTransparency := AValue; StyleChanged(Self); end; { TChartErrorBar } constructor TChartErrorBar.Create(AOwner: TCustomChart); begin inherited Create(AOwner); FWidth := -1; // -1 = same width as series pointer FPen := TPen.Create; FPen.OnChange := @StyleChanged; FVisible := false; end; destructor TChartErrorBar.Destroy; begin FPen.Free; inherited Destroy; end; procedure TChartErrorBar.Assign(ASource: TPersistent); begin if ASource is TChartErrorBar then begin FPen.Assign(TChartErrorBar(ASource).Pen); FWidth := TChartErrorBar(ASource).Width; end; inherited; end; procedure TChartErrorBar.SetPen(const AValue: TPen); begin FPen.Assign(AValue); StyleChanged(Self); end; procedure TChartErrorBar.SetWidth(const AValue: Integer); begin if FWidth = AValue then exit; FWidth := AValue; StyleChanged(self); end; end.