Browse Source

Merge pull request #201 from omac777pti/master

make linestyle visible as a property from within atshapelinebgra
sganz 1 year ago
parent
commit
320d8c282a

+ 10 - 1
bgracontrols.lpk

@@ -34,7 +34,7 @@
     <Description Value="BGRA Controls is a set of graphical UI elements that you can use with Lazarus LCL applications."/>
     <License Value="Modified LGPL"/>
     <Version Major="9" Release="1" Build="6"/>
-    <Files Count="79">
+    <Files Count="81">
       <Item1>
         <Filename Value="atshapelinebgra.pas"/>
         <HasRegisterProc Value="True"/>
@@ -421,6 +421,15 @@
         <HasRegisterProc Value="True"/>
         <UnitName Value="BCLeaEngrave"/>
       </Item79>
+      <Item80>
+        <Filename Value="supergauge.pas"/>
+        <HasRegisterProc Value="True"/>
+        <UnitName Value="supergauge"/>
+      </Item80>
+      <Item81>
+        <Filename Value="supergaugecommon.pas"/>
+        <UnitName Value="supergaugecommon"/>
+      </Item81>
     </Files>
     <CompatibilityMode Value="True"/>
     <LazDoc Paths="fpdoc"/>

+ 2 - 1
bgracontrols.pas

@@ -25,7 +25,7 @@ uses
   dtthemedclock, dtthemedgauge, MaterialColors, bgrasvgimagelistform, 
   BCLeaLCDDisplay, BCLeaLED, BCLeaQLED, BCLeaRingSlider, BCLeaSelector, 
   BCLeaTheme, BCLeaLCDDisplay_EditorRegister, BCLeaBoard, BCLeaEngrave, 
-  LazarusPackageIntf;
+  supergauge, supergaugecommon, LazarusPackageIntf;
 
 implementation
 
@@ -93,6 +93,7 @@ begin
     @BCLeaLCDDisplay_EditorRegister.Register);
   RegisterUnit('BCLeaBoard', @BCLeaBoard.Register);
   RegisterUnit('BCLeaEngrave', @BCLeaEngrave.Register);
+  RegisterUnit('supergauge', @supergauge.Register);
 end;
 
 initialization

+ 58 - 0
icons/supergauge.lrs

@@ -0,0 +1,58 @@
+LazarusResources.Add('tsupergauge','PNG',[
+  #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#3#0#0#0#215#169#205
+  +#202#0#0#3#0'PLTE'#255#255#255#255#255#255#128#128#128#185'wGooo~~~'#247#247
+  +#247'((('#5#5#5#18#18#18#253#253#253#16#16#16#12#12#12#174#174#174'555222{{{'
+  +#207#207#207#203#203#203'&&&333'#177#177#177#236#236#236#127#127#127#220#220
+  +#127'hhh'#231#231#231#134#134#128#9#9#9'>>>WWW'#214'))nnnTTTpppUUU'#13#13#13
+  +#217#217#217'vvvbbb'#214#214#214#222#222#222'sss'#218#218#218'ddd}}}www'#204
+  +#204#204#173#173#173#19#19#19'[[[jjj'#206#206#206'YYY'#212#212#212#223#223
+  +#223#202#202#202#132#132#132#180#180#180#141#141#141#220#220#220#252#252#252
+  +#245#245#245#163#163#163#183#183#183#134#134#134'!!!'#208#208#208#158#158#158
+  +'ZZZ'#130#130#130'+++'#172#172#172#227#227#227#152#152#152#159#159#159#145
+  +#145#145'rrr'#187#187#187#137#137#137#196#196#196#178#178#178#143#143#143#219
+  +#219#219#139#139#139'```'#149#149#149#149'kk'#167'xi'#210#210#210#205#205#205
+  +#160#159'{'#158#158#128'lll'#168#168#168#180'LL'#195#135'b'#164#164#128#158
+  +#158'|'#177#177#128#136#136#136#182#129'['#169'uO'#209'..'#198#181'w'#147#147
+  +#128#130#130'r'#199#198#127#185#129'X'#175'vM'#134'zz'#175#175#175#217#217
+  +#127'|||///xxx'#219#219#127#129#129#128'   '#198'99'#180'zc'#191#191#127'444'
+  +#175#175#128#178#178#128#176'OO'#196'A>'#242#242#242'SSS'#226#226#226'yyx'
+  +#211#210#127#157#157#128#143'pp'#206'11'#194'EA'#216#216#127#153#153#128#29
+  +#29#29'yyy,,,'#131#131#128#209#208#127#189#189#128#213'Z*'#205'32'#184#143'l'
+  +#151#151#128#225#225#225#166#166#166#165#165#128#215#214#127#210#209#127#188
+  +#188#128#212#146'G'#221#140'?'#208#205'~'#216#215#127#181#181#128#132#132#128
+  +'AAA'#133#133#128#155#155#128#204#137'G'#201#134'G'#137#137#128'BBB999666'''
+  +'''''zzz888'#30#30#30#164#164#164#184#184#184'aaa'#152#152#128#209#209#209
+  +#174#174#128#239#239#127#237#237#237#153'gg'#200'77'#246#9#9#201'66'#163#148
+  +'y'#138#138#138#194#194#127#167#167#128#198#198#127#239'`'#17#245#10#10#236
+  +#19#19#194'=='#157#128'q'#165#165#128#214#213#127#144#144#128#225's!'#231'p'
+  +#28#170#145's'#188#188#128#224#223#127#189#189#128#137#137#128#204#203#127
+  +#229#229#127#253#142' '#251#141' '#223#222#127#199#199#127#164#164#128#132
+  +#132#128'vvv'#182#182#182't'#0'x;'#0'*.'#0'pp'#0't;'#0'*.'#0'po'#0'tx'#0';*'
+  +#0'.p'#0'ot'#0'|M'#0'ic'#0'ro'#0'so'#0'ft'#0' X'#0'PS'#0' ('#0'*.'#0'xp'#0's'
+  +')'#0' |'#0'*.'#0'xp'#0's|'#0#0#216'4'#29#134#2#0#0#0#0#0#0#0#232'4'#29#134#2
+  +#0#0#0#0#0#0#0#248'4'#29#134#2#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0'Hw'#178#210#0
+  +#0#0#1'tRNS'#0'@'#230#216'f'#0#0#0#9'pHYs'#0#0#14#195#0#0#14#195#1#199'o'#168
+  +'d'#0#0#2'GIDAT('#145'b'#192#10#24#24#24#0#0#0#0#255#255#130'aF+'#29'vnnvW'#3
+  +'6'#16#143#129#129#1#0#0#0#255#255#130'b'#19'~v'#29#1#1#1#129#216#229'Nk'#25
+  +#24#24#24#24#24#0#0#0#0#255#255#130'`'#3'nWK('#136'eo`c```'#0#0#0#0#255#255#2
+  +'c+'#238'XKK'#203'E'#139#151','#181#180#180#20'X'#182#156#141#129#129#1#0#0#0
+  +#255#255#2#137'Kq'#235'XN'#155'>'#163#127#214#236'9'#211#231'YZZ.se``'#0#0#0
+  +#0#255#255#2'I4,'#179#156#211'oii'#201'<A'#190'm'#146'D'#166#165#0#251'Z'#6#6
+  +#0#0#0#0#255#255'b``'#208#148#17#152#190#8'd<3'#179'ek['#221#244'E'#150'ZNl'
+  +#12#0#0#0#0#255#255'b``'#208#226#175#6#25#0#150#176#180#172#173#147#176#180
+  +#228#183'b'#0#0#0#0#255#255'b``'#224'w'#149#128#184#8',aY^Qi'#25#235#202#0#0
+  +#0#0#255#255'b``'#224#206#202#134'H'#164#230#130#200'<'#249#233#150#2#236#12
+  +#0#0#0#0#255#255'b``'#144'I'#130#152#20#146#154'&'#0#162#219'22'#5'd'#24#0#0
+  +#0#0#255#255#2'K'#128#173#182#212#210#138#213#2#209#241#9#211#4'd'#24#0#0#0#0
+  +#255#255#2#25#5#242#29'X'#6#194#8#143#200#20'`g'#0#0#0#0#255#255#2'Y'#222#0
+  +'2G'#211'D'#211'*DSS'#195#4'$'#167#163#195#0#0#0#0#255#255'b``'#176'd'#183
+  +#180#180'4'#144#10's1'#208'0'#177#12#3'K'#176#251'1'#0#0#0#0#255#255'b``'#168
+  +#151#137#5#233'`d[[o`ib'#226'bi'#233#202#207#200#0#0#0#0#255#255#2#5#137#150
+  +#147#128#165#149#149#165#137#137#134#137#165#149#166#165'e,'#183#9#3#3#0#0#0
+  +#255#255#2'G'#18'?'#191#192'Z'#19#13#141#176#16#13'M'#147#0'K'#29'n'#3#6#6#6
+  +#0#0#0#0#255#255#2#7'{='#191#19#204'e'#150#2#203#184#13#24#24#24#24#0#0#0#0
+  +#255#255#130'F'#172#150#12'{C'#172#128'@'#172'+'#191'L'#131#20#3#3#3#3#3#0#0
+  +#0#255#255#130#225'z'#129#229#220'22'#220#252'Z'#154' '#30#3#3#3#0#0#0#255
+  +#255#194#142#25#24#24#0#0#0#0#255#255#194#142#25#24#24#0#0#0#0#255#255#3#0']'
+  +#22']'#217#225'\&c'#0#0#0#0'IEND'#174'B`'#130
+]);

BIN
images/bgracontrols_images.res


+ 4 - 1
images/bgracontrols_images_list.txt

@@ -133,4 +133,7 @@ TBCLeaBoard_150.png
 TBCLeaBoard_200.png
 TBCLeaEngrave.png
 TBCLeaEngrave_150.png
-TBCLeaEngrave_200.png
+TBCLeaEngrave_200.png
+tsupergauge.png
+tsupergauge_150.png
+tsupergauge_200.png

+ 28 - 0
images/svg/tsupergauge.svg

@@ -0,0 +1,28 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!--Generator: Xara Designer (www.xara.com), SVG filter version: 6.7.0.0-->
+<svg fill="none" fill-rule="evenodd" stroke="black" stroke-width="0.501" stroke-linejoin="bevel" stroke-miterlimit="10" font-family="Times New Roman" font-size="16" style="font-variant-ligatures:none" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" overflow="visible" width="18pt" height="18pt" viewBox="0 -18 18 18">
+ <defs>
+  <image id="Bitmap" preserveAspectRatio="none" width="1" height="1" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAARElEQVQImWLctWvXyu/fv2vs37//BgAAAP//Yjl06JDaq1ev9E6dOvUPAAAA//9i/v37t9OePXv+vnv37iYAAAD//wMADLcbvYBdmiYAAAAASUVORK5CYII=">
+   <title>1</title>
+  </image>
+ </defs>
+ <g id="Layer 1" transform="scale(1 -1)">
+  <ellipse cx="9" cy="8.999" rx="7.5" ry="7.5" fill-rule="nonzero" stroke-linejoin="miter" stroke-width="0.75" fill="#808080" stroke="#000000"/>
+  <rect x="-8.9995" y="-8.9995" width="17.999" height="17.999" rx="0" ry="0" transform="matrix(1 0 0 1 8.999 8.998)" fill-rule="nonzero" stroke-linejoin="miter" stroke-width="0.75" stroke="none"/>
+  <path d="M 2.976,8.586 C 2.86,16.311 14.911,16.05 15.025,8.037" fill="none" stroke="#fffe7f" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 9.374,13.389 C 12.432,12.811 14.043,10.924 14.264,8.048" fill="none" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" stroke="#ff0000"/>
+  <rect x="8.626" y="9" width="0.749" height="5.999" fill="#ff6b00" fill-rule="nonzero" stroke-linejoin="miter" stroke-width="0.75" stroke="none"/>
+  <text xml:space="preserve" transform="translate(5.475 3.941) scale(1 -1)" kerning="auto" fill="#ffffff" font-size="5.189" stroke="none" stroke-width="0.162" stroke-linecap="round" stroke-linejoin="round" font-family="Clarendon"><tspan x="0" y="0">SG</tspan></text>
+  <g id="Group_1" stroke="none">
+   <path d="M 8.413,9.764 C 8.834,10.088 9.44,10.008 9.764,9.587 C 10.088,9.166 10.008,8.56 9.587,8.236 C 9.166,7.912 8.56,7.992 8.236,8.413 C 7.912,8.834 7.992,9.44 8.413,9.764 Z" stroke-linejoin="miter" stroke-width="0.75" fill="#666666" marker-start="none" marker-end="none"/>
+   <clipPath id="clip-path">
+    <use xlink:href="#Shape"/>
+   </clipPath>
+   <g clip-path="url(#clip-path)">
+    <use xlink:href="#Bitmap" transform="translate(8.037 9.963) scale(1.926 -1.926)"/>
+   </g>
+   <path d="M 8.236,8.413 C 7.912,8.834 7.992,9.44 8.413,9.764 C 8.834,10.088 9.44,10.008 9.764,9.587 C 10.088,9.166 10.008,8.56 9.587,8.236 C 9.166,7.912 8.56,7.992 8.236,8.413 Z M 9.465,8.394 C 9.798,8.651 9.861,9.131 9.605,9.465 C 9.348,9.798 8.868,9.861 8.534,9.605 C 8.201,9.348 8.138,8.868 8.394,8.534 C 8.651,8.201 9.131,8.138 9.465,8.394 Z" stroke-width="0.05" marker-start="none" marker-end="none" fill="none" id="Shape"/>
+  </g>
+ </g>
+</svg>

BIN
images/tsupergauge.png


BIN
images/tsupergauge_150.png


BIN
images/tsupergauge_200.png


+ 2147 - 0
supergauge.pas

@@ -0,0 +1,2147 @@
+// SPDX-License-Identifier: LGPL-3.0-linking-exception
+{
+  Part of BGRA Controls. Made by third party.
+  For detailed information see readme.txt
+
+  Site: https://sourceforge.net/p/bgra-controls/
+  Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
+  Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
+
+}
+{******************************* CONTRIBUTOR(S) ******************************
+- Edivando S. Santos Brasil | [email protected]
+  (Compatibility with delphi VCL 11/2018)
+- Sandy Ganz | [email protected]
+  Evolved from DTAnalogCommon, specific for New Gauge Work
+  Massive overhaul, fixes and features, begat Super Gauge
+  Needed to split off as changes broke compatibility badly
+
+***************************** END CONTRIBUTOR(S) *****************************}
+unit supergauge;
+
+{$I bgracontrols.inc}
+
+interface
+
+uses
+  Classes, SysUtils, Graphics, {$IFDEF FPC}LResources,{$ELSE} BGRAGraphics, {$ENDIF} Forms, Controls, Dialogs, SuperGaugeCommon,
+  BGRABitmap, BGRABitmapTypes, BGRAVectorize, BGRAPath, math, bctypes, bctools;
+
+const
+  INTERNAL_GAUGE_MIN_VALUE = 0;   // internal lowest value
+  INTERNAL_GAUGE_MAX_VALUE = 270; // internal highest value
+  VERSIONSTR = '1.00';            // SG version, Should ALWAYS show as a delta when merging!
+
+type
+
+  { TSGCustomSuperGauge }
+
+  TBandsArray = array[0..3] of TSGBandSettings;
+  TTextsArray = array[0..2] of TSGTextSettings;
+  TMarkersArray = array[0..2] of TSGMarkerSettings;
+
+  TTextsBitmapArray = array[0..2] of TBGRABitmap;
+
+  TSGRangeStateErrorEvent = procedure(Sender: TObject; OutOfRangeValue: single) of object;  // called anytime out of range
+  TSGRangeStateOKEvent = procedure(Sender: TObject; RangeValue: single) of object;          // called only when back to in range
+  TSGRangeStateChangeEvent = procedure(Sender: TObject; Value: single) of object;           // called when state RangeLed Active changes to True
+
+  TSGCustomSuperGauge = class(TGraphicControl)
+  private
+    { Private declarations }
+    FDirty: boolean;
+
+    FFaceSettings: TSGFaceSettings;
+    FFrameSettings: TSGFrameSettings;
+    FPointerCapSettings: TSGPointerCapSettings;
+    FScaleSettings: TSGScaleSettings;
+    FBandsSettings: TBandsArray;
+    FTextsSettings: TTextsArray;
+    FPointerSettings: TSGPointerSettings;
+    FRangeLEDSettings: TSGRangeCheckLEDSettings;
+    FMarkersSettings: TMarkersArray;
+    FGaugeBitmap: TBGRABitmap;
+    FFrameBitmap: TBGRABitmap;
+    FFaceBitmap: TBGRABitmap;
+    FTextBitmap: TBGRABitmap;
+    FScaleBitmap: TBGRABitmap;
+    FBandBitmap: TBGRABitmap;
+    FTextsBitmaps: TTextsBitmapArray;
+
+    FMultiBitmap: TBGRABitmap;
+    FPointerBitmap: TBGRABitmap;
+    FMarkerBitmap: TBGRABitmap;
+    FPointerCapBitmap: TBGRABitmap;
+    FLEDActiveBitmap: TBGRABitmap;
+    FLEDInActiveBitmap: TBGRABitmap;
+
+    FMinValue: single;  // the min value mapped to lowest/leftmost angle on the gauge
+    FMaxValue: single;  // the max value mapped to highest/rightmost angle on the gauge
+    FValue: single;     // this is the VALUE not a position
+    FOutOfRange: TSGRangeStateErrorEvent;         // change of state ONLY
+    FBackInRange: TSGRangeStateOKEvent;           // change of state ONLY
+    FRangeLEDActive: TSGRangeStateChangeEvent;    // change of state ONLY
+    FRangeLEDInactive: TSGRangeStateChangeEvent;  // change of state ONLY
+    FOutOfRangeState: boolean;
+    FRangeLEDStateChanged: boolean;
+    FAutoScale: boolean;
+
+    procedure SetBandSettings1(AValue: TSGBandSettings);
+    procedure SetBandSettings2(AValue: TSGBandSettings);
+    procedure SetBandSettings3(AValue: TSGBandSettings);
+    procedure SetBandSettings4(AValue: TSGBandSettings);
+
+    procedure SetTextSettings1(AValue: TSGTextSettings);
+    procedure SetTextSettings2(AValue: TSGTextSettings);
+    procedure SetTextSettings3(AValue: TSGTextSettings);
+
+    procedure SetMarkerSettings1(AValue: TSGMarkerSettings);
+    procedure SetMarkerSettings2(AValue: TSGMarkerSettings);
+    procedure SetMarkerSettings3(AValue: TSGMarkerSettings);
+
+    procedure SetFaceSettings(AValue: TSGFaceSettings);
+    procedure SetScaleSettings(AValue: TSGScaleSettings);
+    procedure SetFrameSettings(AValue: TSGFrameSettings);
+    procedure SetPointerSettings(AValue: TSGPointerSettings);
+    procedure SetRangeLedSettings(AValue: TSGRangeCheckLEDSettings);
+    procedure SetPointerCapSettings(AValue: TSGPointerCapSettings);
+    procedure SetMaxValue(AValue: single);
+    procedure SetMinValue(AValue: single);
+    function GetMaxValue: single;
+    function GetMinValue: single;
+
+    procedure SetValue(AValue: single);
+    function GetValue: single;
+    procedure SetAutoScale(AValue: boolean);
+    function CheckOutOfRange(AValue: single): single;
+
+  protected
+    { Protected declarations }
+    procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
+    procedure DoChange({%H-}Sender: TObject);
+    procedure DoRangeLEDChange({%H-}Sender: TObject);
+    procedure DoPictureChange({%H-}Sender: TObject);
+    procedure DoChangeFont1({%H-}ASender: TObject; {%H-}AData: PtrInt); // Wrapper for FontEx DoChange
+    procedure DoChangeFont2({%H-}ASender: TObject; {%H-}AData: PtrInt); // Wrapper for FontEx DoChange
+    procedure DoChangeFont3({%H-}ASender: TObject; {%H-}AData: PtrInt); // Wrapper for FontEx DoChange
+    procedure SetAllBandsDirtyState(AValue: boolean);
+    procedure SetAllTextsDirtyState(AValue: boolean);
+    procedure SetAllMarkersDirtyState(AValue: boolean);
+    function IsAnyBandDirty: boolean;
+    function IsAnyMarkerDirty: boolean;
+    property Dirty: boolean read FDirty write FDirty;
+
+  public
+    { Public declarations }
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+
+    property PointerSettings: TSGPointerSettings read FPointerSettings write SetPointerSettings; // sjg added
+    property PointerCapSettings: TSGPointerCapSettings read FPointerCapSettings write SetPointerCapSettings;
+    property FaceSettings: TSGFaceSettings read FFaceSettings write SetFaceSettings;
+    property FrameSettings: TSGFrameSettings read FFrameSettings write SetFrameSettings;
+    property ScaleSettings: TSGScaleSettings read FScaleSettings write SetScaleSettings;
+    property BandSettings1: TSGBandSettings read FBandsSettings[0] write SetBandSettings1; // will need an array thing here
+    property BandSettings2: TSGBandSettings read FBandsSettings[1] write SetBandSettings2; // will need an array thing here
+    property BandSettings3: TSGBandSettings read FBandsSettings[2] write SetBandSettings3; // will need an array thing here
+    property BandSettings4: TSGBandSettings read FBandsSettings[3] write SetBandSettings4; // will need an array thing here
+    property TextSettings1: TSGTextSettings read FTextsSettings[0] write SetTextSettings1;
+    property TextSettings2: TSGTextSettings read FTextsSettings[1] write SetTextSettings2;
+    property TextSettings3: TSGTextSettings read FTextsSettings[2] write SetTextSettings3;
+    property RangeLedSettings: TSGRangeCheckLEDSettings read FRangeLEDSettings write SetRangeLedSettings;
+    property MarkerSettings1: TSGMarkerSettings read FMarkersSettings[0] write SetMarkerSettings1;
+    property MarkerSettings2: TSGMarkerSettings read FMarkersSettings[1] write SetMarkerSettings2;
+    property MarkerSettings3: TSGMarkerSettings read FMarkersSettings[2] write SetMarkerSettings3;
+    property MinValue: single read GetMinValue write SetMinValue default 0.0;
+    property MaxValue: single read GetMaxValue write SetMaxValue default 100.0;
+    property AutoScale: boolean read FAutoScale write SetAutoScale default False;
+    property Value: single read GetValue write SetValue default 0.0;
+    property OutOfRange: TSGRangeStateErrorEvent read FOutOfRange write FOutOfRange;
+    property BackInRange: TSGRangeStateOKEvent read FBackInRange write FBackInRange;
+    property RangeLEDActive: TSGRangeStateChangeEvent read FRangeLEDActive write FRangeLEDActive;
+    property RangeLEDInActive: TSGRangeStateChangeEvent read FRangeLEDInactive write FRangeLEDInactive;
+    function RemapRange(OldValue: single; OldMin, OldMax, NewMin, NewMax: single): single;
+    function GaugeToUser(GaugeValue: single): single;
+    function UserToGauge(UserValue: single): single;
+
+    procedure Paint; override;
+    procedure DrawFrame;
+    procedure DrawFace;
+    procedure DrawScale;
+    procedure DrawBand(const BandSettings: TSGBandSettings);
+    procedure DrawBands;
+    procedure DrawMulti;
+    procedure DrawText(TextBitmap: TBGRABitmap; const TextSettings: TSGTextSettings);
+    procedure DrawLed;
+    procedure DrawMarker(MarkerBitmap: TBGRABitmap; const MarkerSettings: TSGMarkerSettings);
+    procedure DrawMarkers;
+    procedure DrawPointer;
+    procedure DrawPointerCap;
+    function CheckRangeLED(AValue: single): boolean;
+  end;
+
+  { TSuperGauge }
+
+  TSuperGauge = class(TSGCustomSuperGauge)
+  private
+    { Private declarations }
+  protected
+    { Protected declarations }
+  public
+    { Public declarations }
+  published
+    { Published declarations }
+    property MinValue;
+    property MaxValue;
+    property FaceSettings;
+    property BandSettings1;
+    property BandSettings2;
+    property BandSettings3;
+    property BandSettings4;
+    property TextSettings1;
+    property TextSettings2;
+    property TextSettings3;
+    property FrameSettings;
+    property ScaleSettings;
+    property RangeLedSettings;
+
+    property MarkerSettings1;
+    property MarkerSettings2;
+    property MarkerSettings3;
+
+    property PointerSettings;
+    property PointerCapSettings;
+    property AutoScale;
+    property Value;
+    property OutOfRange;
+    property BackInRange;
+    property RangeLEDActive;
+    property RangeLEDInactive;
+
+    // Added missing events
+    property Anchors;
+    property OnClick;
+    property OnDblClick;
+    property OnMouseDown;
+    property OnMouseUp;
+    property OnMouseMove;
+    property OnMouseEnter;
+    property OnMouseLeave;
+  end;
+
+  {$IFDEF FPC}procedure Register;{$ENDIF}
+
+implementation
+{$IFDEF FPC}
+procedure Register;
+begin
+  RegisterComponents('BGRA Controls', [TSuperGauge]);
+end;
+{$ENDIF}
+
+{ TSGCustomSuperGauge }
+
+constructor TSGCustomSuperGauge.Create(AOwner: TComponent);
+var
+    i: integer;
+begin
+  inherited Create(AOwner);
+
+  Width := 240;
+  Height := 240;
+
+  FFaceSettings := TSGFaceSettings.Create;
+  FaceSettings.OnChange := DoChange;
+  FaceSettings.Picture.OnChange := DoPictureChange; // need to set this so we can catch changes to the picture!
+
+  FFrameSettings := TSGFrameSettings.Create;
+  FrameSettings.OnChange := DoChange;
+
+  FScaleSettings := TSGScaleSettings.Create;
+  ScaleSettings.OnChange := DoChange;
+
+  for i := low(FBandsSettings) to high(FBandsSettings) do
+  begin
+    FBandsSettings[i] := TSGBandSettings.Create;
+    FBandsSettings[i].OnChange := DoChange;
+    FBandsSettings[i].Text := 'Band ' + IntToStr(i + 1);
+  end;
+
+  for i := low(FTextsSettings) to high(FTextsSettings) do
+  begin
+    FTextsSettings[i] := TSGTextSettings.Create;
+    FTextsSettings[i].OnChange := DoChange;
+    FTextsBitmaps[i] := TBGRABitmap.Create;
+  end;
+
+  // Set Text font change events and defaults
+
+  FTextsSettings[0].FontEx.OnChange := DoChangeFont1;
+  FTextsSettings[1].FontEx.OnChange := DoChangeFont2;
+  FTextsSettings[2].FontEx.OnChange := DoChangeFont3;
+  FTextsSettings[0].Text := 'Text1';
+  FTextsSettings[1].Text := 'Text2';
+  FTextsSettings[2].Text := 'Text3';
+
+  // Pointer Cap
+
+  FPointerCapSettings := TSGPointerCapSettings.Create;
+  FPointerCapSettings.OnChange := DoChange;
+
+  // Pointer
+
+  FPointerSettings := TSGPointerSettings.Create;
+  FPointerSettings.OnChange := DoChange;
+  FPointerSettings.Color := BGRA(255, 127, 63); // orange
+
+  // RangeLED
+
+  FRangeLEDSettings := TSGRangeCheckLEDSettings.Create;
+  FRangeLEDSettings.OnChange := DoRangeLEDChange;
+
+  // Markers
+
+  for i := low(FMarkersSettings) to high(FMarkersSettings) do
+  begin
+    FMarkersSettings[i] := TSGMarkerSettings.Create;
+    FMarkersSettings[i].OnChange := DoChange;
+  end;
+
+  // make marker each different to save confusion
+
+  FMarkersSettings[0].Color := clLime;
+  FMarkersSettings[1].Color := clRed;
+  FMarkersSettings[2].Color := clYellow;
+
+  // create needed bitmaps, Don't Forget to FREE!!!
+
+  FFaceBitmap := TBGRABitmap.Create;
+  FFrameBitmap := TBGRABitmap.Create;
+  FGaugeBitmap := TBGRABitmap.Create;
+  FTextBitmap := TBGRABitmap.Create;
+  FPointerBitmap := TBGRABitmap.Create;
+  FPointerCapBitmap := TBGRABitmap.Create;
+  FScaleBitmap := TBGRABitmap.Create;
+  FBandBitmap := TBGRABitmap.Create;
+  FMultiBitmap := TBGRABitmap.Create;
+  FLEDActiveBitmap := TBGRABitmap.Create;
+  FLEDInActiveBitmap := TBGRABitmap.Create;
+  FMarkerBitmap := TBGRABitmap.Create;
+
+  // initialized (some above)
+
+  FOutOfRangeState := False;
+  FRangeLEDStateChanged := False;
+  FValue := 0;
+  FAutoScale := false;
+  FMinValue := 0;
+  FMaxValue := 100;
+
+  FDirty := True;   // Always force initial paint/draw on everything!
+end;
+
+destructor TSGCustomSuperGauge.Destroy;
+var
+    i: integer;
+begin
+
+  FPointerCapSettings.OnChange := nil;
+  FPointerCapSettings.Free;
+
+  FPointerSettings.OnChange := nil;
+  FPointerSettings.Free;
+
+  FRangeLEDSettings.OnChange := nil;
+  FRangeLEDSettings.Free;
+
+  ScaleSettings.OnChange := nil;
+  FScaleSettings.Free;
+
+  for i := low(FTextsSettings) to high(FTextsSettings) do
+  begin
+   FBandsSettings[i].OnChange := nil;
+   FBandsSettings[i].Free;
+  end;
+
+  for i := low(FTextsSettings) to high(FTextsSettings) do
+  begin
+   FTextsSettings[i].OnChange := nil;
+   FTextsSettings[i].FontEx.OnChange := nil;
+   FTextsSettings[i].Free;
+   FTextsBitmaps[i].Free;
+  end;
+
+  for i := low(FMarkersSettings) to high(FMarkersSettings) do
+  begin
+   FMarkersSettings[i].OnChange := nil;
+   FMarkersSettings[i].Free;
+  end;
+
+  FFaceSettings.OnChange := nil;
+  FFaceSettings.Free;
+
+  FFrameSettings.OnChange := nil;
+  FFrameSettings.Free;
+
+  // now clean bitmaps, should match what's in creat method
+
+  FLEDActiveBitmap.Free;
+  FLEDInactiveBitmap.Free;
+  FMarkerBitmap.Free;
+  FBandBitmap.Free;
+  FScaleBitmap.Free;
+  FPointerBitmap.Free;
+  FPointerCapBitmap.Free;
+  FTextBitmap.Free;
+  FFaceBitmap.Free;
+  FMultiBitmap.Free;
+  FFrameBitmap.Free;
+  FGaugeBitmap.Free;
+
+  inherited Destroy;
+end;
+
+function TSGCustomSuperGauge.RemapRange(OldValue: single; OldMin, OldMax, NewMin, NewMax: single): single;
+begin
+  // Generic mapping of ranges. Value is the number to remap, returns number
+  // in the new range. Looks for odd div by 0 condition and fixes
+
+  if OldMin = OldMax then
+  begin
+    // need to return something reasonable
+
+    if OldValue <= OldMin then
+      Exit(NewMin)
+    else
+      Exit(NewMax);
+  end;
+
+  Result := (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin;
+end;
+
+function TSGCustomSuperGauge.GaugeToUser(GaugeValue: single): single;
+begin
+  // Helper to translates internal gauge value to external user value
+
+  Result := RemapRange(GaugeValue, INTERNAL_GAUGE_MIN_VALUE, INTERNAL_GAUGE_MAX_VALUE, FMinValue, FMaxValue);
+end;
+
+function TSGCustomSuperGauge.UserToGauge(UserValue: single): single;
+begin
+  // Helper to translates external user value to internal gauge value
+
+  Result := RemapRange(UserValue, FMinValue, FMaxValue, INTERNAL_GAUGE_MIN_VALUE, INTERNAL_GAUGE_MAX_VALUE);
+end;
+
+function TSGCustomSuperGauge.GetValue: single;
+begin
+  // Scale from internal back to user range
+
+  Result := GaugeToUser(FValue);
+end;
+
+procedure TSGCustomSuperGauge.SetValue(AValue: single);
+var
+    gaugeValue: single;
+begin
+
+  // Tricky case here, since we are calling the RangeLED range check
+  // here too, if that is in any way dirty we should process the value
+  // and not skip. Triggering any change on RangeLEDSettings should call this.
+
+  // Get the user value into gauge value space
+
+  gaugeValue := UserToGauge(AValue);
+
+  // skip if a few conditions exit. This is a bit tricky as the gauge value will reset
+  // to min or max values on overload so need to always update if that's the case. Should
+  // not affect performance. Similar for LED, if dirty no skip.
+
+  if (FValue = gaugeValue) and (not FRangeLEDSettings.Dirty) and (not FOutOfRangeState) then
+    Exit;
+
+  // If out of range conditions are at play the gauge Value (FValue) will never
+  // be out of range. This value is passed to the out of range handler for the
+  // user to deal with and DO SOMETHING to indicate it.
+
+  FValue := CheckOutOfRange(gaugeValue);
+
+  // If we have a change in the of the LED's Active property we need
+  // to call the event handlers too. Also we will check it's values and set
+  // if needed. NOTE : that if the range type is set to rtNone, it will always
+  // return the state of the RangeLEDSettings.Active, otherwise it will calculate
+  // the needed value for a range check as set. FRangeLEDStateChanged is set in
+  // IsRangeLEDActive function so should be called before this!
+
+  // MUST NOT CALL .Active as this will cause a recursive call, use the
+  // hacked ActiveNoDoChange which will just set the property value with
+  // no side effects
+
+  // True if LED Should be On, False if not, AValue is in User space for LED's
+
+  FRangeLEDSettings.ActiveNoDoChange := CheckRangeLED(AValue);
+
+  // We must dirty the Pointer here or no redraw
+
+  PointerSettings.Dirty := True;
+  DoChange(self);
+end;
+
+function TSGCustomSuperGauge.CheckOutOfRange(AValue: single): Single;
+begin
+  // These values are in gauge space, so typically never less than 0, or > 270
+
+  Result := AValue; // SAFE so always will return a value
+
+  if AValue < INTERNAL_GAUGE_MIN_VALUE then
+  begin
+    // Under Range event
+
+    FOutOfRangeState := True;
+    if Assigned(FOutOfRange) then
+      FOutOfRange(Self, GaugeToUser(AValue));
+    Result := INTERNAL_GAUGE_MIN_VALUE;
+  end
+    else
+      if AValue > INTERNAL_GAUGE_MAX_VALUE then
+      begin
+        // Over Range event
+
+        FOutOfRangeState := True;
+        if Assigned(FOutOfRange) then
+          FOutOfRange(Self, GaugeToUser(AValue)); // must translate back to user space
+        Result := INTERNAL_GAUGE_MAX_VALUE;
+      end
+      else
+        begin
+          // If NOT over/under flow then will need to clear
+          // that state/flag and reset any indicators if was in a overange state
+
+          if FOutOfRangeState then
+          begin
+            if Assigned(FBackInRange) then
+              FBackInRange(self, GaugeToUser(AValue)); // here to, get into user space
+
+            FOutOfRangeState := False;  // reset so no more calls
+          end;
+      end;
+end;
+
+procedure TSGCustomSuperGauge.SetAutoScale(AValue: boolean);
+begin
+  if FAutoScale = AValue then
+    exit;
+
+  FAutoScale := AValue;
+  FScaleSettings.Dirty := True;  // set it, as it will need a repaint
+
+  DoChange(self);
+end;
+
+function TSGCustomSuperGauge.GetMaxValue: single;
+begin
+  Result := FMaxValue;
+end;
+
+procedure TSGCustomSuperGauge.SetMaxValue(AValue: single);
+var
+    currUser: single;
+begin
+  // Note : MinValue and MaxValue can span negative ranges and be increasing
+  //        or decreasing
+
+  // Min and Max out of order, bounce
+
+  if (FMinValue >= AValue) then
+    exit;
+
+  // If changing min/max must refresh the value to adjust
+
+  currUser := GaugeToUser(FValue);
+  FMaxValue := AValue;  // setting this will change UserToGauge() in SetValue!
+
+  // Recompute
+
+  SetValue(currUser);
+end;
+
+function TSGCustomSuperGauge.GetMinValue: single;
+begin
+  Result := FMinValue;
+end;
+
+procedure TSGCustomSuperGauge.SetMinValue(AValue: single);
+var
+    currUser: single;
+begin
+  // Note : MinValue and MaxValue can span negative ranges and be increasing
+  //        or decreasing
+
+  // Min and Max out of order, bounce
+
+  if (FMaxValue <= AValue) then
+    exit;
+
+  // If changing min/max must refresh the value to adjust
+
+  currUser := GaugeToUser(FValue);
+  FMinValue := AValue;  // setting this will change UserToGauge() in SetValue!
+
+  // Recompute
+
+  SetValue(currUser);
+end;
+
+procedure TSGCustomSuperGauge.SetFaceSettings(AValue: TSGFaceSettings);
+begin
+  if FFaceSettings = AValue then
+    Exit;
+
+  FFaceSettings := AValue;
+  FFaceSettings.Dirty := True;  // set it, as it will need a repaint
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetFrameSettings(AValue: TSGFrameSettings);
+begin
+  if FFrameSettings = AValue then
+    Exit;
+
+  FFrameSettings := AValue;
+  FFrameSettings.Dirty := True;  // set it, as it will need a repaint
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetScaleSettings(AValue: TSGScaleSettings);
+begin
+  if FScaleSettings = AValue then
+    Exit;
+
+  FScaleSettings := AValue;
+  FScaleSettings.Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetAllBandsDirtyState(AValue: boolean);
+var
+  i: integer;
+begin
+  // helper to just set all bands to a specific state!
+
+  for i := low(FBandsSettings) to high(FBandsSettings) do
+    FBandsSettings[i].Dirty := AValue;
+end;
+
+function TSGCustomSuperGauge.IsAnyBandDirty : boolean;
+var
+  i: integer;
+begin
+  // helper to just see if any band has a dirty flag
+
+  for i := low(FBandsSettings) to high(FBandsSettings) do
+  begin
+    if FBandsSettings[i].Dirty then
+      exit(True);
+  end;
+
+  result := False;
+end;
+
+procedure TSGCustomSuperGauge.SetBandSettings1(AValue: TSGBandSettings);
+begin
+  if FBandsSettings[0] = AValue then
+    Exit;
+
+  FBandsSettings[0] := AValue;
+  FBandsSettings[0].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetBandSettings2(AValue: TSGBandSettings);
+begin
+  if FBandsSettings[1] = AValue then
+    Exit;
+
+  FBandsSettings[1] := AValue;
+  FBandsSettings[1].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetBandSettings3(AValue: TSGBandSettings);
+begin
+  if FBandsSettings[2] = AValue then
+    Exit;
+
+  FBandsSettings[2] := AValue;
+  FBandsSettings[2].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetBandSettings4(AValue: TSGBandSettings);
+begin
+  if FBandsSettings[3] = AValue then
+    Exit;
+
+  FBandsSettings[3] := AValue;
+  FBandsSettings[3].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetAllTextsDirtyState(AValue: boolean);
+var
+  i: integer;
+begin
+  // helper to just set all texts to a specific state!
+
+  for i := low(FTextsSettings) to high(FTextsSettings) do
+    FTextsSettings[i].Dirty := AValue;
+end;
+
+procedure TSGCustomSuperGauge.SetTextSettings1(AValue: TSGTextSettings);
+begin
+  if FTextsSettings[0] = AValue then
+    Exit;
+
+  FTextsSettings[0] := AValue;
+  FTextsSettings[0].Dirty := True;  // set it, as it will need a repaint
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetTextSettings2(AValue: TSGTextSettings);
+begin
+  if FTextsSettings[1] = AValue then
+    Exit;
+
+  FTextsSettings[1] := AValue;
+  FTextsSettings[1].Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetTextSettings3(AValue: TSGTextSettings);
+begin
+  if FTextsSettings[2] = AValue then
+    Exit;
+
+  FTextsSettings[2] := AValue;
+  FTextsSettings[2].Dirty := True;
+
+  DoChange(self);
+end;
+
+function TSGCustomSuperGauge.IsAnyMarkerDirty: boolean;
+var
+  i: integer;
+begin
+  // helper to just see if any band has a dirty flag
+
+  for i := low(FMarkersSettings) to high(FMarkersSettings) do
+  begin
+    if FMarkersSettings[i].Dirty then
+      exit(True);
+  end;
+
+  result := False;
+end;
+
+procedure TSGCustomSuperGauge.SetAllMarkersDirtyState(AValue: boolean);
+var
+  i: integer;
+begin
+  // helper to just set all markers to a specific state!
+
+  for i := low(FMarkersSettings) to high(FMarkersSettings) do
+    FMarkersSettings[i].Dirty := AValue;
+end;
+
+procedure TSGCustomSuperGauge.SetMarkerSettings1(AValue: TSGMarkerSettings);
+begin
+  if FMarkersSettings[0] = AValue then
+    Exit;
+
+  FMarkersSettings[0] := AValue;
+  FMarkersSettings[0].Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetMarkerSettings2(AValue: TSGMarkerSettings);
+begin
+  if FMarkersSettings[1] = AValue then
+    Exit;
+
+  FMarkersSettings[1] := AValue;
+  FMarkersSettings[1].Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetMarkerSettings3(AValue: TSGMarkerSettings);
+begin
+  if FMarkersSettings[2] = AValue then
+    Exit;
+
+  FMarkersSettings[2] := AValue;
+  FMarkersSettings[2].Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetPointerSettings(AValue: TSGPointerSettings);
+begin
+  if FPointerSettings = AValue then
+    Exit;
+
+  FPointerSettings := AValue;
+  FPointerSettings.Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetRangeLedSettings(AValue: TSGRangeCheckLEDSettings);
+begin
+  if FRangeLEDSettings = AValue then
+    Exit;
+
+  FRangeLEDSettings := AValue;
+  FRangeLEDSettings.Dirty := True;
+
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.SetPointerCapSettings(AValue: TSGPointerCapSettings);
+begin
+  if FPointerCapSettings = AValue then
+    Exit;
+
+  FPointerCapSettings := AValue;
+  FPointerCapSettings.Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer);
+begin
+  inherited;
+  FDirty := true; // Called on Resize of component
+end;
+
+procedure TSGCustomSuperGauge.DoChange(Sender: TObject);
+begin
+  Invalidate;
+end;
+
+procedure TSGCustomSuperGauge.DoRangeLEDChange(Sender: TObject);
+begin
+  // This is needed as anytime a RangeLED settings is updated we
+  // MAY need to update and call event handlers. update as the RangeLEDSettings.Dirty
+
+  CheckRangeLED(Value); // Tricky may not work!
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.DoPictureChange(Sender: TObject);
+begin
+  // This is similar to DoRangeLEDChange, if we have a picture change this
+  // is how we can propagate it up to the gauge to tell if a repaint is needed.
+
+  FaceSettings.Dirty := True;  // trigger a redraw since the image has changed
+  DoChange(Sender);
+end;
+
+
+procedure TSGCustomSuperGauge.DoChangeFont1(ASender: TObject; AData: PtrInt);
+begin
+  // Simlar to the regular dochange but needed a different procedure signature
+  // so just a wrapper, TObject is not a gauge so use Self here for DoChange()
+
+  FTextsSettings[0].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.DoChangeFont2(ASender: TObject; AData: PtrInt);
+begin
+  // Simlar to the regular dochange but needed a different procedure signature
+  // so just a wrapper, TObject is not a gauge so use Self here for DoChange()
+
+  FTextsSettings[1].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.DoChangeFont3(ASender: TObject; AData: PtrInt);
+begin
+  // Simlar to the regular dochange but needed a different procedure signature
+  // so just a wrapper, TObject is not a gauge so use Self here for DoChange()
+
+  FTextsSettings[2].Dirty := True;
+  DoChange(self);
+end;
+
+procedure TSGCustomSuperGauge.Paint;
+var
+  i: integer;
+  offsetX, offsetY: integer;
+  gaugeCenX, gaugeCenY: integer;
+begin
+  inherited Paint;
+
+  // IF the component is resized OR moved (this is safer) we
+  // need to make sure EVERYTHING redraws. The base class will
+  // also do it's own thing to invalidate and redraw it all.
+
+  if FDirty then
+  begin
+    FFrameSettings.Dirty := True;
+    FFaceSettings.Dirty := True;
+    FScaleSettings.Dirty := True;
+    SetAllBandsDirtyState(True);
+    SetAllTextsDirtyState(True);
+    FRangeLEDSettings.Dirty := True;
+    FPointerCapSettings.Dirty := True;
+    FPointerSettings.Dirty := True;
+    SetAllMarkersDirtyState(True);
+    FDirty := False;  // everything here marked, so can reset
+  end;
+
+  // Now start Drawing into the offscreen bitmaps. IF the particular
+  // subcomponent is not changed, the DrawXXXX will just leave it as is
+  // and not waste cycles to redraw it.
+
+  FGaugeBitmap.Fill(BGRA(0, 0, 0, 0));
+  FGaugeBitmap.SetSize(Width, Height);
+  gaugeCenX := FGaugeBitmap.Width div 2;
+  gaugeCenY := FGaugeBitmap.Height div 2;
+
+  // Face, Frame, Scale and Bands are usually static, so do yet another
+  // bitmap for these that will require less Blend Images.
+
+  DrawMulti;
+  FGaugeBitmap.BlendImage(0, 0, FMultiBitmap, boLinearBlend);
+
+  // now draw any texts if enabled and dirty
+
+  for i := low(FTextsSettings) to high(FTextsSettings) do
+  begin
+    if FTextsSettings[i].Enabled then
+    begin
+      DrawText(FTextsBitmaps[i], FTextsSettings[i]);
+      offsetX := FTextsSettings[i].OffsetX + gaugeCenX - FTextsBitmaps[i].Width div 2;
+      offsetY := FTextsSettings[i].OffsetY + gaugeCenY - FTextsBitmaps[i].Height div 2;
+      FGaugeBitmap.BlendImage(offsetX, offsetY, FTextsBitmaps[i], boLinearBlend);
+    end;
+  end;
+
+  FGaugeBitmap.BlendImage(offsetX, offsetY, FTextBitmap, boLinearBlend);
+
+  // Draw range LED, small bitmap so center and move
+
+  DrawLed;
+  offsetX := FRangeLEDSettings.OffsetX + gaugeCenX - FLEDActiveBitmap.Width div 2;
+  offsetY := FRangeLEDSettings.OffsetY + gaugeCenY - FLEDActiveBitmap.height div 2;
+
+  // set up the led, if user sets Active state will keep led on even if
+  // the out of range state is set.
+
+  if FRangeLEDSettings.Active then
+    FGaugeBitmap.BlendImage(offsetX, offsetY, FLEDActiveBitmap, boLinearBlend)
+  else
+    FGaugeBitmap.BlendImage(offsetX, offsetY, FLEDInActiveBitmap, boLinearBlend);
+
+  // Draw Markers BEFORE the pointer(s)
+
+  DrawMarkers;
+  FGaugeBitmap.BlendImage(0, 0, FMarkerBitmap,boLinearBlend);
+
+  // draw cap over or under the pointer. Note that the pointer is a special
+  // case when drawing since it's almost always dirty.
+
+  if PointerCapSettings.CapStyle <> csNone then
+    begin
+      DrawPointerCap;
+      offsetX := gaugeCenX  - FPointerCapBitmap.Width div 2;
+      offsetY := gaugeCenY - FPointerCapBitmap.Height div 2;
+
+      if PointerCapSettings.CapPosition = cpOver then
+        begin
+          DrawPointer;
+          FGaugeBitmap.BlendImage(offsetX, offsetY, FPointerCapBitmap, boLinearBlend); // Cap on top
+        end
+      else
+        begin
+          FGaugeBitmap.BlendImage(offsetX, offsetY, FPointerCapBitmap, boLinearBlend); // Cap on Bottom
+          DrawPointer;
+        end;
+    end
+      else
+        DrawPointer;
+
+  // make it all visable to the user!
+
+  FGaugeBitmap.Draw(Canvas, 0, 0, False);
+end;
+
+procedure TSGCustomSuperGauge.DrawMulti;
+begin
+  // The strategy here is that these typically only change infrequently
+  // so if so, just draw as a bundle and saves some blendimages calls. Each of the
+  // drawXXX still handles it's own dirty flag. The bitmap will be set up
+  // as on instantiation so all of the others have their dirty flag set True, so no
+  // need to do any initialization. Makes painting much faster even
+  // with the individual dirty flags!
+
+  if FFrameSettings.Dirty or FFaceSettings.Dirty or FScaleSettings.Dirty or IsAnyBandDirty then
+    begin
+      Initializebitmap(FMultiBitmap, Width, Height);
+
+      DrawFrame;
+      FMultiBitmap.BlendImage(0, 0, FFrameBitmap, boLinearBlend);
+
+      DrawFace;
+      FMultiBitmap.BlendImage(0, 0, FFaceBitmap, boLinearBlend);
+
+      DrawBands; // will handle the enable/disable and draw of each band
+      FMultiBitmap.BlendImage(0, 0, FBandBitmap, boLinearBlend);
+
+      DrawScale;
+      FMultiBitmap.BlendImage(0, 0, FScaleBitmap, boLinearBlend);
+    end;
+end;
+
+procedure TSGCustomSuperGauge.DrawFrame;
+var
+  Origin: TSGOrigin;
+  r: integer;
+begin
+  if not FrameSettings.Dirty then
+    Exit;
+
+  FrameSettings.Dirty := False;
+
+  Origin := Initializebitmap(FFrameBitmap, Width, Height);
+
+  // Always fills the space so AutoScale is sorta' always on
+
+  r := round(Origin.Radius * 0.95);
+
+  // Draw Bitmap frame
+
+  FFrameBitmap.FillEllipseAntialias(Origin.CenterPoint.x,
+    Origin.CenterPoint.y,
+    r, r, FFrameSettings.FrameColor);
+
+  // Draw thin antialiased border to smooth against background
+
+  FFrameBitmap.EllipseAntialias(Origin.CenterPoint.x,
+    Origin.CenterPoint.y,
+    r, r, FFrameSettings.BorderColor, FFrameSettings.BorderRadius);
+end;
+
+procedure TSGCustomSuperGauge.DrawFace;
+var
+  OriginFace: TSGOrigin;
+  r: integer;
+  image: TBGRABitmap;
+
+begin
+
+  if not FaceSettings.Dirty then
+    Exit;
+
+  FaceSettings.Dirty := False;
+
+  OriginFace := Initializebitmap(FFaceBitmap, Width, Height);
+
+  // Always fills the space so AutoScale is sorta' always on for the face
+
+  r := round(OriginFace.Radius * 0.95) - 5;
+
+  case FFaceSettings.FillStyle of
+    fsGradient:
+      FFaceBitmap.FillEllipseLinearColorAntialias(OriginFace.CenterPoint.x,
+        OriginFace.CenterPoint.y, r, r, FFaceSettings.OuterColor,
+        FFaceSettings.InnerColor);
+
+    fsNone:
+      FFaceBitmap.FillEllipseAntialias(OriginFace.CenterPoint.x, OriginFace.CenterPoint.y,
+        r, r, FFaceSettings.OuterColor);
+  end;
+
+  // see if valid size and enabled, draw if so!
+
+  if ((FaceSettings.Picture.Width > 0) or (FaceSettings.Picture.Height > 0)) and (FFaceSettings.PictureEnabled) then
+  begin
+
+    image := TBGRABitmap.Create(FaceSettings.Picture.Bitmap);
+    FFaceBitmap.BlendImage(
+                OriginFace.CenterPoint.X + FaceSettings.PictureOffsetX,
+                OriginFace.CenterPoint.y + FaceSettings.PictureOffsetY,
+                image,
+                boLinearBlend);
+    image.Free; // needed!
+  end;
+end;
+
+procedure TSGCustomSuperGauge.DrawBands;
+var
+  i: integer;
+begin
+  // Draw mult bands on the same bitmap. we can do this since
+  // we are drawing over the entire fullsized bitmap. Since
+  // this is the case, you can draw all of the bands here in one shot
+  // and on one bitmap. Init bitmap here!
+
+  // Only change if something dirty
+  // nothing dirty, no init, no draw, just bounce!
+
+  if not IsAnyBandDirty then
+    exit;
+
+  Initializebitmap(FBandBitmap, Width, Height); // clear it before we draw anything
+
+  for i := low(FBandsSettings) to high(FBandsSettings) do
+  begin
+    FBandsSettings[i].Dirty := True;  // force draw, if any band is dirty they are are dirty
+    DrawBand(FBandsSettings[i]);      // will clear any dirty for specific band
+  end;
+end;
+
+procedure TSGCustomSuperGauge.DrawBand(const BandSettings : TSGBandSettings);
+var
+  BandRadius, TextRadius: single;
+  TextSize: integer;
+  baseAngle, startAngle, endAngle: single;
+  cenX, cenY: integer;
+  fontRenderer: TBGRAVectorizedFontRenderer;
+  TextPath: TBGRAPath;
+begin
+
+  // TODO : Maybe be removed since calling here always paints them all
+  if not BandSettings.Dirty then
+    Exit;
+
+  BandSettings.Dirty := False;
+
+  // Now, if not enabled we can leave if flag reset!
+
+  if not BandSettings.Enabled then
+    exit;
+
+  TextSize := BandSettings.TextSize * 15;
+
+  // Origin := Initializebitmap(FBandBitmap, Width, Height); drawbands needs to set this up
+
+  cenX := Width div 2;
+  cenY := Height div 2;
+
+  BandRadius := BandSettings.BandRadius - BandSettings.Thickness div 2;    // may need to adjust for band thickness
+  TextRadius := BandSettings.TextRadius - BandSettings.TextSize div 2 - BandSettings.Thickness div 2; // offset to center
+
+  // Start = 225 degree is 0 on gague scale (Not the angle), and -45 degree is 100 on scale
+
+  // 270, down (gauge angle 0)180 flat, increase moves towards 0 decrease towards 100
+  // 0 is flat line, right most end. Increase goes backwards towards 0, -45 is 100 percent on scale
+
+  baseAngle := 225 * PI / 180;
+  startAngle := baseAngle - ((BandSettings.StartValue * 270 / 100) * PI / 180);
+  endAngle := baseAngle - ((BandSettings.EndValue * 270 / 100) * PI / 180);
+
+  FBandBitmap.LineCap := pecFlat; // caps should be flat
+  FBandBitmap.Arc(
+                   cenX, cenY,
+                   BandRadius + 0.5, BandRadius + 0.5, // push down a bit
+                   // (360-135) 225, -45
+                   // 3.92699,-0.785398, // must use start and end angle, internally Point calcs won't work due to arcsin2() limits
+                   startAngle, endAngle,
+                   BandSettings.BandColor,
+                   BandSettings.Thickness,
+                   false,
+                   BGRA(0,0,0,0) // last param is alpha, so no interior color, inner routings ONLY draw the arc, no fill
+  );
+
+  if BandSettings.EnableText then
+  begin
+    FontRenderer := TBGRAVectorizedFontRenderer.Create;
+    FBandBitmap.FontRenderer := fontRenderer;     // assign text vectorial font renderer
+    FBandBitmap.FontHeight := round(TextSize * 0.09);
+    FBandBitmap.FontQuality := fqFineAntialiasing;
+    FBandBitmap.FontName := BandSettings.TextFont;
+    FBandBitmap.FontStyle := BandSettings.TextStyle;
+    FontRenderer.OutlineColor := BGRABlack;
+    FontRenderer.OutlineWidth := TextSize / 600;
+    FontRenderer.OutlineVisible := true;
+    FBandBitmap.FontVerticalAnchor := fvaBaseline;
+    TextPath := TBGRAPath.Create;
+
+    // drawing is backwards on textpath
+
+    TextPath.Arc(cenX, cenY, TextRadius, -startAngle, -endAngle, False);
+    FBandBitmap.TextOutCurved(TextPath, BandSettings.Text, BandSettings.TextColor, taCenter, 0);
+  end;
+end;
+
+procedure TSGCustomSuperGauge.DrawText(TextBitmap: TBGRABitmap; const TextSettings: TSGTextSettings);
+var
+  TextBoxWidth, TextBoxHeight: integer;
+  TextRect: TRect;
+begin
+
+  if not TextSettings.Dirty then
+    Exit;
+
+  TextSettings.Dirty := False;
+
+  // get the bounding box so we can create a SMALLER bitmap. This will be referenced
+  // to the Center of the text and gauge
+
+  CalculateTextSize(TextSettings.Text, TextSettings.FontEx, TextBoxWidth, TextBoxHeight, TextSettings.FontEx.Shadow);
+  Initializebitmap(TextBitmap, TextBoxWidth, TextBoxHeight);
+
+  // Set up text bounding box,
+
+  TextRect.Left := 0;
+  TextRect.Top := 0;
+  TextRect.Height := TextBoxHeight;
+  TextRect.Width := TextBoxWidth;
+
+  // Draw into the TextBitmap for later use
+
+  RenderText(TextRect, TextSettings.FontEx, TextSettings.Text, TextBitmap, Enabled);
+end;
+
+procedure TSGCustomSuperGauge.DrawScale;
+var
+  Origin: TSGOrigin;
+  i, n, x, y, xt, yt: integer;
+  scaleStartValue, scaleBump: integer;
+  ScaleRadius, TextRadius: single;
+  TextSize: integer;
+  pStart, pEnd: TPointF;
+  startAngle, endAngle: single;
+  innerTicRadius: single;
+begin
+  // if nothing dirty then skip it, we have a bitmap with
+  // the scale already drawn. This is slow so saves a lot of time
+  // as scales are slow to draw
+
+  if not ScaleSettings.Dirty then
+    Exit;
+
+  ScaleSettings.Dirty := False;  // mark as clean, so next run will not need a rebuild!
+
+  Origin := Initializebitmap(FScaleBitmap, Width, Height);
+
+  // Calc radius for scale and text or set it from the user
+
+  if FAutoScale then
+  begin
+    ScaleRadius := Round(Origin.Radius * 0.90);
+    TextRadius := Round(Origin.Radius * 0.65);
+    TextSize := Round(Origin.Radius * 0.15);
+
+    // fix up scaling for small or large gauges
+
+    if (Width < 250) or (Height < 250) then
+    begin
+      TextSize := 15;
+      TextRadius := TextRadius - 10;
+    end
+    else
+      begin
+        if (Width > 500) or (Height > 500) then
+        begin
+          TextSize := TextSize + 5;
+          TextRadius := TextRadius + 10;
+        end;
+      end;
+    end
+  else
+    begin
+      ScaleRadius := ScaleSettings.ScaleRadius;
+      TextRadius := ScaleSettings.TextRadius;
+      TextSize := ScaleSettings.TextSize;
+    end;
+
+  // Draw SubTicks
+
+  if ScaleSettings.EnableSubTicks then
+  begin
+    n := ScaleSettings.MainTickCount * ScaleSettings.SubTickCount;
+
+    for i := 0 to n do
+    begin
+      // Calculate draw from point
+
+      X := Origin.CenterPoint.x - Round(ScaleRadius * cos((-45 + i * 270 / n) * Pi / 180));
+      Y := Origin.CenterPoint.y - Round(ScaleRadius * sin((-45 + i * 270 / n) * Pi / 180));
+
+      // Calculate draw to point
+
+      Xt := Origin.CenterPoint.x - Round((ScaleRadius - ScaleSettings.LengthSubTick) *
+        cos((-45 + i * 270 / n) * Pi / 180));
+      Yt := Origin.CenterPoint.y - Round((ScaleRadius - ScaleSettings.LengthSubTick) *
+        sin((-45 + i * 270 / n) * Pi / 180));
+
+      FScaleBitmap.DrawLineAntialias(x, y, xt, yt, ScaleSettings.TickColor, ScaleSettings.ThicknessSubTick);
+
+      if (ScaleSettings.TickArcStyle = taboth) and (not ScaleSettings.EnableMainTicks) then
+        begin
+          // need caps on the ends so the gauge doesn't look stupid if both inner and outer
+          // tic arcs are visiable
+
+          if (i = 0) or (i = n) then
+            begin
+              if not ScaleSettings.EnableMainTicks then
+                innerTicRadius := ScaleSettings.LengthSubTick
+              else
+                innerTicRadius := ScaleSettings.LengthMainTick;
+
+              // draw end pieces in the MainTick thickness to match
+
+              Xt := Origin.CenterPoint.x - Round((ScaleRadius - innerTicRadius) *
+                cos((-45 + i * 270 / n) * Pi / 180));
+              Yt := Origin.CenterPoint.y - Round((ScaleRadius - innerTicRadius) *
+                sin((-45 + i * 270 / n) * Pi / 180));
+
+              FScaleBitmap.DrawLineAntialias(x, y, xt, yt, ScaleSettings.TickColor,
+                ScaleSettings.ThicknessMainTick);
+            end;
+        end;
+    end;
+  end;
+
+  // Draw after the sub-tics
+
+  if ScaleSettings.EnableMainTicks then
+  begin
+    n := ScaleSettings.MainTickCount;
+
+    for i := 0 to n do
+    begin
+
+      // Draw main ticks
+      // Calculate draw from point bottom
+
+      x := Origin.CenterPoint.x - Round(ScaleRadius * cos((-45 + i * 270 / n) * Pi / 180));
+      y := Origin.CenterPoint.y - Round(ScaleRadius * sin((-45 + i * 270 / n) * Pi / 180));
+
+      // Calculate draw to point top
+
+      xt := Origin.CenterPoint.x - Round((ScaleRadius - ScaleSettings.LengthMainTick) *
+        cos((-45 + i * 270 / n) * Pi / 180));
+      yt := Origin.CenterPoint.y - Round((ScaleRadius - ScaleSettings.LengthMainTick) *
+        sin((-45 + i * 270 / n) * Pi / 180));
+
+      FScaleBitmap.DrawLineAntialias(x, y, xt, yt, ScaleSettings.TickColor, ScaleSettings.ThicknessMainTick);
+    end;
+  end;
+
+  // Draw text, these are only for the Main Ticks
+
+  if ScaleSettings.EnableScaleText then
+    begin
+
+      FScaleBitmap.FontName := ScaleSettings.TextFont;
+      FScaleBitmap.FontHeight := TextSize;
+      FScaleBitmap.FontQuality := fqFineAntialiasing;
+      FScaleBitmap.FontStyle := FScaleSettings.TextStyle;
+
+      n := ScaleSettings.MainTickCount;
+
+      // if draw the scale reversed, do some tricky stuff so we can
+      // count up or down. Start is swapped with the actual end value here
+
+      if ScaleSettings.ReverseScale then
+      begin
+        scaleBump := -1;
+        scaleStartValue := n * ScaleSettings.Step + ScaleSettings.Start;
+      end
+      else
+      begin
+        scaleBump := 1;
+        scaleStartValue := ScaleSettings.Start;
+      end;
+
+      // Draw text for main ticks
+
+      for i := 0 to n do
+      begin
+        xt := Origin.CenterPoint.x - Round(TextRadius * cos((-45 + i * 270 / n) * Pi / 180));
+        yt := Origin.CenterPoint.y - Round(TextRadius * sin((-45 + i * 270 / n) * Pi / 180));
+
+        FScaleBitmap.TextOut(xt, yt - (FScaleBitmap.FontHeight / 1.7),
+          IntToStr(scaleStartValue + i * ScaleSettings.Step * scaleBump),
+          ScaleSettings.TextColor, taCenter);
+      end;
+    end;
+
+    // draw outer rings/bands
+
+    if (ScaleSettings.TickArcStyle = taOuter) or (ScaleSettings.TickArcStyle = taboth) then
+    begin
+      // draw arc OUSIDE on the tics, doesn't matter main or sub, all at the top
+      // inner of tic
+
+      pStart.x := Origin.CenterPoint.x - Round(ScaleRadius * cos(-45 * Pi / 180));
+      pStart.y := Origin.CenterPoint.y - Round(ScaleRadius * sin(-45 * Pi / 180));
+
+      startAngle := arctan2((Origin.CenterPoint.y - pStart.y),(Origin.CenterPoint.x - pStart.x)) + 4.71239; // add 270
+
+      // Calculate draw to point outer
+
+      pEnd.x := Origin.CenterPoint.x - Round((ScaleRadius - ScaleSettings.LengthMainTick) * cos(225 * Pi / 180));
+      pEnd.y := Origin.CenterPoint.y - Round((ScaleRadius - ScaleSettings.LengthMainTick) * sin(225 * Pi / 180));
+
+      endAngle :=  -arctan2((pEnd.y - Origin.CenterPoint.y),(pEnd.x - Origin.CenterPoint.x));
+      FScaleBitmap.Arc(
+                     Origin.CenterPoint.x, Origin.CenterPoint.y,
+                     ScaleRadius + 0.5, ScaleRadius + 0.5, // push down a bit
+                     startAngle, endAngle,
+                     ScaleSettings.TickColor,
+                     ScaleSettings.ThicknessMainTick,
+                     false,
+                     BGRA(0,0,0,0) // last param is alpha, so no interior color, inner routings ONLY draw the arc, no fill
+                     );
+    end;
+
+    if (ScaleSettings.TickArcStyle = taInner) or (ScaleSettings.TickArcStyle = taBoth) then
+    begin
+      // Inner will chose main tics (for now) if both main and sub tics on)
+      // will need to find out the radius for what selected... or do something
+      // like use what ever tic is LONGER (logic here will need a change)
+
+      // draw arc OUSIDE on the tics, doesn't matter main or sub, all at the top
+
+      // inner of tick
+
+      pStart.x := Origin.CenterPoint.x - Round(ScaleRadius * cos(-45 * Pi / 180));
+      pStart.y := Origin.CenterPoint.y - Round(ScaleRadius * sin(-45 * Pi / 180));
+
+      startAngle := arctan2((Origin.CenterPoint.y - pStart.y),(Origin.CenterPoint.x - pStart.x)) + 4.71239; // add 270
+
+      // Calculate draw to point outer
+
+      pEnd.x := Origin.CenterPoint.x - Round((ScaleRadius - ScaleSettings.LengthMainTick) * cos(225 * Pi / 180));
+      pEnd.y := Origin.CenterPoint.y - Round((ScaleRadius - ScaleSettings.LengthMainTick) * sin(225 * Pi / 180));
+
+      endAngle := -arctan2((pEnd.y - Origin.CenterPoint.y),(pEnd.x - Origin.CenterPoint.x));
+
+      // be nice and if not displaying main tics, use the sub tic length to bottom
+      // up against them
+
+      if not ScaleSettings.EnableMainTicks then
+        innerTicRadius := ScaleSettings.LengthSubTick
+     else
+        innerTicRadius := ScaleSettings.LengthMainTick;
+
+      FScaleBitmap.Arc(
+                     Origin.CenterPoint.x, Origin.CenterPoint.y,
+                     ScaleRadius - 0.5 - innerTicRadius, ScaleRadius - 0.5 - innerTicRadius,
+                     startAngle, endAngle,
+                     ScaleSettings.TickColor,
+                     ScaleSettings.ThicknessMainTick,
+                     false,
+                     BGRA(0,0,0,0) // last param is alpha, so no interior color, inner routings ONLY draw the arc, no fill
+                     );
+    end;
+end;
+
+procedure TSGCustomSuperGauge.DrawPointer;
+var
+  Origin: TSGOrigin;
+  x, y, x1, y1, extLen: integer;
+  commonSubEx: single;
+  PointerLength: single;
+  startAngle, endAngle: single;
+  bandRadius: single;
+
+begin
+  // Note : Min and max values are the GAUGE Settings, not the Scales,
+  //        the scale display is independant of the value of the gauge to
+  //        allow for multiple pointers if later needed
+
+  if not PointerSettings.Dirty then
+    Exit;
+
+  Origin.CenterPoint.X:= FGaugeBitmap.Width div 2;
+  Origin.CenterPoint.Y:= FGaugeBitmap.Height div 2;
+
+  // radius is smaller of the 2 dimensions
+
+  if Origin.CenterPoint.x < Origin.CenterPoint.y then
+    Origin.Radius := Origin.CenterPoint.x
+  else
+    Origin.Radius := Origin.CenterPoint.Y;
+
+  // Set the pointer length, does not apply to arc
+
+  if FAutoScale then
+    begin
+      PointerLength := Round(Origin.Radius * 0.85);
+    end
+    else
+      begin
+        PointerLength := PointerSettings.Length;
+      end;
+
+  // draw the arc style of pointer
+
+  if PointerSettings.Style = psArc then
+    begin
+      // drawn arc pointer, ensure not negative or crash, zero no need to draw
+
+      if FValue <= 0.0 then
+        Exit;
+
+       BandRadius := PointerLength - PointerSettings.Thickness div 2;    // adjust for band thickness so end of pointer is top
+
+       // Start = 225 degree is 0 on gague scale (Not the angle), and -45 degree is 100 on scale
+       // 270, down (gauge angle 0)180 flat, increase moves towards 0 decrease towards 100
+       // 0 is flat line, right most end. Increase goes backwards towards 0, -45 is 100 percent on scale
+
+       startAngle := 225 * PI / 180;  // start at 0 on the gauge
+       endAngle := startAngle - FValue * PI / 180;
+
+       FGaugeBitMap.LineCap := pecFlat; // caps should be flat, rounded does not align to scales well
+       FGaugeBitMap.Arc(
+                        Origin.CenterPoint.x, Origin.CenterPoint.y,
+                        BandRadius + 0.5, BandRadius + 0.5, // push down a bit
+                        startAngle, endAngle,
+                        PointerSettings.Color,
+                        PointerSettings.Thickness,
+                        false,
+                        BGRA(0,0,0,0) // last param is alpha, so no interior color, inner routings ONLY draw the arc, no fill
+                   );
+    end
+      else
+      begin
+        // if we are need to draw the extension behind the cap, we can
+        // recalc the ending point to just do one line draw instead of
+        // 2 discrete lines from the center. That is easier, but slower
+        // If extension len is 0, skip as will show a partial pixel
+
+        FGaugeBitMap.LineCap := pecRound; // caps should be round for line type pointers
+
+        if (PointerSettings.Style = psLineExt) and (PointerSettings.ExtensionLength > 0) then
+          begin
+            // The extension is always pixels visable from the center or edge of the
+            // cap, fix as needed. Makes nice for the user.
+
+            if PointerCapSettings.CapStyle = csNone then
+              extLen := PointerSettings.ExtensionLength
+            else
+              extLen := PointerSettings.ExtensionLength + PointerCapSettings.Radius;
+
+            // compute end point of pointer if an extension
+
+            commonSubEx := (-225 + FValue) * Pi / 180;
+            x1 := Origin.CenterPoint.x - Round(extLen * cos(commonSubEx));
+            y1 := Origin.CenterPoint.y - Round(extLen * sin(commonSubEx));
+
+          end
+            else
+              begin
+                // no extension or extension length is 0, just draw to center
+
+                x1 := Origin.CenterPoint.x;
+                y1 := Origin.CenterPoint.y;
+              end;
+
+        // computer start point of pointer
+
+        commonSubEx := (-45 + FValue) * Pi / 180;
+        x := Origin.CenterPoint.x - Round(PointerLength * cos(commonSubEx));
+        y := Origin.CenterPoint.y - Round(PointerLength * sin(commonSubEx));
+
+        // finally draw it
+
+        FGaugeBitMap.DrawLineAntialias(x, y, x1, y1, PointerSettings.Color, PointerSettings.Thickness)
+      end;
+end;
+
+procedure TSGCustomSuperGauge.DrawPointerCap;
+var
+  Origin: TSGOrigin;
+  sizeWH : integer;
+  pCapEdge : integer;
+  tx, ty: integer;
+  h: single;
+  d2: single;
+  v: TPointF;
+  p: PBGRAPixel;
+  Center: TPointF;
+  yb: integer;
+  xb: integer;
+  mask: TBGRABitmap;
+  Map: TBGRABitmap;
+
+begin
+
+  // skip drawing if nothing changed
+
+  if not PointerCapSettings.Dirty then
+    Exit;
+
+  PointerCapSettings.Dirty := False;
+
+  // drawing is the size of the cap, not of the entire gauge!
+
+  sizeWH := (PointerCapSettings.Radius + PointerCapSettings.EdgeThickness) * 2 + 2;
+  Origin := Initializebitmap(FPointerCapBitmap, SizeWH, SizeWH);
+  pCapEdge := PointerCapSettings.Radius + PointerCapSettings.EdgeThickness div 2;
+
+  if PointerCapSettings.CapStyle = csFlat then
+    begin
+      // Draw the flat cap, but make sure size is similar to the shaded below or will be odd
+
+      FPointerCapBitmap.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+        pCapEdge,
+        pCapEdge,
+        PointerCapSettings.EdgeColor,
+        PointerCapSettings.EdgeThickness,
+        PointerCapSettings.FillColor);
+    end
+    else
+      begin
+
+        tx := PointerCapSettings.Radius * 2; // keeps size consistent with flat cap
+        ty := tx;
+
+        if (tx = 0) or (ty = 0) then
+          Exit;
+
+        if PointerCapSettings.CapStyle = csPhong then
+          begin
+            //compute knob height map
+
+            Center := PointF((tx - 1) / 2, (ty - 1) / 2);
+            Map := TBGRABitmap.Create(tx, ty);
+
+            for yb := 0 to ty - 1 do
+            begin
+              p := map.ScanLine[yb];
+              for xb := 0 to tx - 1 do
+              begin
+
+                //compute vector between center and current pixel
+
+                v := PointF(xb, yb) - Center;
+
+                //scale down to unit circle (with 1 pixel margin for soft border)
+
+                v.x := v.x / (tx / 2 + 1);
+                v.y := v.y / (ty / 2 + 1);
+
+                //compute squared distance with scalar product
+
+                d2 := v {$if FPC_FULLVERSION < 30203}*{$ELSE}**{$ENDIF} v;
+
+                //interpolate as quadratic curve and apply power function
+                if d2 > 1 then
+                  h := 0
+                else
+                  h := power(1 - d2, PointerCapSettings.CurveExponent);
+                p^ := MapHeightToBGRA(h, 255);
+                Inc(p);
+              end;
+            end;
+
+            // mask image round with and antialiased border
+
+            mask := TBGRABitmap.Create(tx, ty, BGRABlack);
+            Mask.FillEllipseAntialias(Center.x, Center.y, tx / 2, ty / 2, BGRAWhite);
+            map.ApplyMask(mask);
+            Mask.Free;
+
+            // now draw on the pointer bitmap, not sure if this is going to work since
+            // it's the pointer not just thje cap, mayh need to do a different
+            // bitmap and overlay, or how the needle will look????
+
+            PointerCapSettings.FPhong.Draw(FPointerCapBitmap, Map, 30,
+                    origin.CenterPoint.x - tx div 2, origin.CenterPoint.y - ty div 2,
+                    PointerCapSettings.FillColor);
+            Map.Free;
+
+            // Draw a flat radius around the cap if set, must be alpha 0 or will not
+            // be an outline
+
+            if PointerCapSettings.EdgeThickness > 0 then
+              FPointerCapBitmap.EllipseAntialias(origin.CenterPoint.x, origin.CenterPoint.y,
+                pCapEdge,
+                pCapEdge,
+                PointerCapSettings.EdgeColor,
+                PointerCapSettings.EdgeThickness, BGRA(0,0,0,0));
+          end
+        else
+          begin
+            // Regular shading
+
+            FPointerCapBitmap.FillEllipseLinearColorAntialias(origin.CenterPoint.x, origin.CenterPoint.y,
+              pCapEdge,
+              pCapEdge,
+              PointerCapSettings.FillColor,
+              PointerCapSettings.EdgeColor
+              );
+
+            // draw edge since the shading is backwards ending on fill color not Edge
+
+            FPointerCapBitmap.EllipseAntialias(origin.CenterPoint.x, origin.CenterPoint.y,
+              pCapEdge,
+              pCapEdge,
+              PointerCapSettings.EdgeColor,
+              PointerCapSettings.EdgeThickness, BGRA(0,0,0,0)
+              );
+
+          end;
+      end;
+end;
+
+// Pass in the FRangeLEDSettings with the index or the entire array??
+
+procedure TSGCustomSuperGauge.DrawLed;
+var
+  Origin: TSGOrigin;
+  sizeWH : integer;
+  mask: TBGRABitmap;
+begin
+
+  // skip drawing if nothing changed or not drawn
+
+  if not FRangeLEDSettings.Dirty then
+    Exit;
+
+  FRangeLEDSettings.Dirty := False;
+
+  // compute the size needed NOT a full gauge bitmap
+
+  sizeWH := FRangeLEDSettings.Size * 2 + 2; // square size at lease LED radius and a bit more
+  Origin := Initializebitmap(FLEDActiveBitmap, sizeWH, sizeWH);
+  Initializebitmap(FLEDInActiveBitmap, sizeWH, sizeWH);
+
+  // offset must be done later in the Paint proc to
+  // keep bitmap small so the center point is the centerpoint of the bitmap
+  // The caller MUST move to the correct offset
+
+  // draw both active and inactive so we never need to do either unless props changed
+  // need to find/get x, y to place the LED
+
+  if RangeLEDSettings.Shape = lshRound then
+    begin
+    if FRangeLEDSettings.Style = lsFlat then
+      begin
+        FLEDActiveBitmap.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+          FRangeLEDSettings.Size,
+          FRangeLEDSettings.Size,
+          FRangeLEDSettings.BorderColor,
+          1,
+          FRangeLEDSettings.ActiveColor);
+      end
+    else
+      if FRangeLEDSettings.Style = lsShaded then
+        begin
+          // draw shaded, could do better here but good for starts
+
+          FLEDActiveBitmap.FillEllipseLinearColorAntialias(
+            Origin.CenterPoint.x,
+            Origin.CenterPoint.y,
+            FRangeLEDSettings.Size,
+            FRangeLEDSettings.Size,
+            FRangeLEDSettings.InactiveColor,
+            FRangeLEDSettings.ActiveColor);
+
+          // draw border
+
+          FLEDActiveBitmap.EllipseAntialias(
+            Origin.CenterPoint.x, Origin.CenterPoint.y,
+            FRangeLEDSettings.Size,
+            FRangeLEDSettings.Size,
+            FRangeLEDSettings.BorderColor,
+            1,
+            BGRA(0,0,0,0));  // fill transparent
+        end;
+
+    // Simple flat round for inactive always
+
+    if RangeLedSettings.Style <> lsNone then
+      begin
+        FLEDInactiveBitmap.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+          FRangeLEDSettings.Size,
+          FRangeLEDSettings.Size,
+          FRangeLEDSettings.BorderColor,
+          1,
+          FRangeLEDSettings.InActiveColor);
+      end;
+    end // Round
+      else
+        if RangeLEDSettings.Shape = lshSquare then
+          begin
+            // draw a Square LED
+
+            if FRangeLEDSettings.Style = lsFlat then
+              begin
+                // Flat
+
+                FLEDActiveBitmap.FillRoundRectAntialias(
+                    0, 0,
+                    FLEDActiveBitmap.Width,
+                    FLEDActiveBitmap.Height,
+                    Origin.Radius / 2,
+                    Origin.Radius / 2,
+                    FRangeLEDSettings.ActiveColor);
+
+                // draw border for Flat
+
+                FLEDActiveBitmap.RoundRectAntialias(
+                    0,0,
+                    FLEDActiveBitmap.Width - 1,
+                    FLEDActiveBitmap.Height - 1,
+                    Origin.Radius / 2,
+                    Origin.Radius / 2,
+                    FRangeLEDSettings.BorderColor,
+                    1);
+              end
+            else
+              if FRangeLEDSettings.Style = lsShaded then
+                begin
+                  // draw shaded
+
+                  FLEDActiveBitmap.GradientFill(
+                      0, 0,
+                      FLEDActiveBitmap.Width,
+                      FLEDActiveBitmap.Height,
+                      FRangeLEDSettings.ActiveColor,
+                      BGRA(0,0,0),
+                      gtRadial,
+                      PointF(FLEDActiveBitmap.Width / 2, FLEDActiveBitmap.Height / 2),
+                      PointF(FLEDActiveBitmap.Width * 1.5,FLEDActiveBitmap.Height * 1.5),
+                      dmSet);
+
+                  mask := TBGRABitmap.Create(FLEDActiveBitmap.Width, FLEDActiveBitmap.Height, BGRABlack);
+                  mask.FillRoundRectAntialias(
+                      0, 0,
+                      FLEDActiveBitmap.Width,
+                      FLEDActiveBitmap.Height,
+                      Origin.Radius / 2,
+                      Origin.Radius / 2,
+                      BGRAWhite);
+
+                  FLEDActiveBitmap.ApplyMask(mask);
+                  mask.Free;
+
+                  // draw border for shaded
+
+                  FLEDActiveBitmap.RoundRectAntialias(
+                            0, 0,
+                            FLEDActiveBitmap.Width - 1,
+                            FLEDActiveBitmap.Height - 1,
+                            Origin.Radius / 2,
+                            Origin.Radius / 2,
+                            FRangeLEDSettings.BorderColor,
+                            1);
+                end;
+
+            // Simple flat square for inactive always
+
+            if RangeLEDSettings.Style <> lsNone then
+              begin
+                // Need to draw the filled
+                FLEDInactiveBitmap.FillRoundRectAntialias(
+                    0, 0,
+                    FLEDActiveBitmap.Width,
+                    FLEDActiveBitmap.Height,
+                    Origin.Radius / 2,
+                    Origin.Radius / 2,
+                    FRangeLEDSettings.InactiveColor);
+
+                // Now the border
+                FLEDInactiveBitmap.RoundRectAntialias(
+                          0, 0,
+                          FLEDActiveBitmap.Width - 1,
+                          FLEDActiveBitmap.Height - 1,
+                          Origin.Radius / 2,
+                          Origin.Radius / 2,
+                          FRangeLEDSettings.BorderColor,
+                          1);
+              end;
+          end // square
+        else
+          if RangeLEDSettings.Shape = lshTriangle then
+            begin
+              // draw a triangle and border
+
+              if FRangeLEDSettings.Style = lsFlat then  // TODO : add lsShaded
+                begin
+                  FLEDActiveBitmap.DrawPolyLineAntialias(
+                      [ PointF(FLEDActiveBitmap.Width / 2, 1),
+                        PointF(FLEDActiveBitmap.Width - 1, FLEDActiveBitmap.Height - 1),
+                        PointF(1, FLEDActiveBitmap.Height - 1),
+                        PointF(FLEDActiveBitmap.Width / 2, 1) // close it for border
+                      ],
+                      FRangeLEDSettings.BorderColor,
+                      1,
+                      FRangeLEDSettings.ActiveColor);
+
+                end
+              else
+                if FRangeLEDSettings.Style = lsShaded then
+                  begin
+                    // draw shaded
+                     FLEDActiveBitmap.FillPolyLinearColor(
+                          [ PointF(FLEDActiveBitmap.Width / 2, 1),
+                          PointF(FLEDActiveBitmap.Width - 1, FLEDActiveBitmap.Height - 1),
+                          PointF(1, FLEDActiveBitmap.Height - 1)],
+                          [FRangeLEDSettings.InactiveColor,
+                          FRangeLEDSettings.ActiveColor,
+                          FRangeLEDSettings.ActiveColor]);
+                    // draw border
+                     FLEDActiveBitmap.DrawPolyLineAntialias(
+                         [ PointF(FLEDActiveBitmap.Width / 2, 1),
+                           PointF(FLEDActiveBitmap.Width - 1, FLEDActiveBitmap.Height - 1),
+                           PointF(1, FLEDActiveBitmap.Height - 1),
+                           PointF(FLEDActiveBitmap.Width / 2, 1) // close it for border
+                         ],
+                         FRangeLEDSettings.BorderColor,
+                         1,
+                         BGRA(0,0,0,0));
+                  end;
+                if RangeLEDSettings.Style <> lsNone then
+                  begin
+                    FLEDInactiveBitmap.DrawPolyLineAntialias(
+                        [ PointF(FLEDActiveBitmap.Width / 2, 1),
+                          PointF(FLEDActiveBitmap.Width - 1, FLEDActiveBitmap.Height - 1),
+                          PointF(1, FLEDActiveBitmap.Height - 1),
+                          PointF(FLEDActiveBitmap.Width / 2, 1) // close it for border
+                        ],
+                        FRangeLEDSettings.BorderColor,
+                        1,
+                        FRangeLEDSettings.InactiveColor);
+                  end;
+            end // triangle
+          else
+            if RangeLEDSettings.Shape = lshDownTriangle then
+              begin
+                  // draw a downward pointing triangle and border
+                  if FRangeLEDSettings.Style = lsFlat then
+                    begin
+                      FLEDActiveBitmap.DrawPolyLineAntialias(
+                        [ PointF(1,1),
+                          PointF(FLEDActiveBitmap.Width / 2, FLEDActiveBitmap.Height  - 1),
+                          PointF(FLEDActiveBitmap.Width - 1, 1),
+                          PointF(1,1)
+                        ],
+                        FRangeLEDSettings.BorderColor,
+                        1,
+                        FRangeLEDSettings.ActiveColor);
+                      end
+                    else
+                      if FRangeLEDSettings.Style = lsShaded then
+                        begin
+                          // draw shaded
+
+                          FLEDActiveBitmap.FillPolyLinearColor(
+                             [ PointF(1,1),
+                               PointF(FLEDActiveBitmap.Width / 2, FLEDActiveBitmap.Height  - 1),
+                               PointF(FLEDActiveBitmap.Width - 1, 1)
+                             ],
+                             [FRangeLEDSettings.InactiveColor,
+                             FRangeLEDSettings.ActiveColor,
+                             FRangeLEDSettings.ActiveColor]);
+
+                          // draw border
+
+                          FLEDActiveBitmap.DrawPolyLineAntialias(
+                             [ PointF(1,1),
+                               PointF(FLEDActiveBitmap.Width / 2, FLEDActiveBitmap.Height  - 1),
+                               PointF(FLEDActiveBitmap.Width - 1, 1),
+                               PointF(1,1)
+                             ],
+                               FRangeLEDSettings.BorderColor,
+                               1,
+                               BGRA(0,0,0,0));
+                        end;
+                  // Draw Inactive DownTri
+                  if RangeLEDSettings.Style <> lsNone then
+                  begin
+                    FLEDInactiveBitmap.DrawPolyLineAntialias(
+                      [ PointF(1,1),
+                        PointF(FLEDActiveBitmap.Width / 2, FLEDActiveBitmap.Height  - 1),
+                        PointF(FLEDActiveBitmap.Width - 1, 1),
+                        PointF(1,1)
+                      ],
+                      FRangeLEDSettings.BorderColor,
+                      1,
+                      FRangeLEDSettings.InactiveColor);
+                  end;
+
+              end;
+end;
+
+procedure TSGCustomSuperGauge.DrawMarkers;
+var
+  i: integer;
+begin
+  if not IsAnyMarkerDirty then
+    exit;
+
+  Initializebitmap(FMarkerBitmap, Width, Height); // clear it before we draw anything
+
+  // Draws low to high, so if overlapping, last will be visible
+
+  for i := low(FMarkersSettings) to high(FMArkersSettings) do
+  begin
+    FMarkersSettings[i].Dirty := True; // need to dirty them all
+    DrawMarker(FMarkerBitmap, FMarkersSettings[i]);    // will clear any dirty
+  end;
+end;
+
+procedure TSGCustomSuperGauge.DrawMarker(MarkerBitmap: TBGRABitmap; const MarkerSettings: TSGMarkerSettings);
+var
+  x1, y1, x2, y2: integer;
+  cenX, cenY: integer;
+  j, vecLen: single;
+  A, B, U, V: TPointF;
+
+begin
+  // skip drawing if nothing changed or not drawn
+
+  if not MarkerSettings.Dirty then
+    Exit;
+
+  MarkerSettings.Dirty := False;
+
+  if not MarkerSettings.Enabled then
+    Exit;
+
+  // Center of bitmap
+
+  cenX := MarkerBitmap.Width div 2;
+  cenY := MarkerBitmap.Height div 2;
+
+  j := (180 - 270) / 2;
+  x1 := cenX - Round(MarkerSettings.Radius * cos((j + MarkerSettings.Value * 270 / 100) * Pi / 180));
+  y1 := cenY - Round(MarkerSettings.Radius * sin((j + MarkerSettings.Value * 270 / 100) * Pi / 180));
+  A := PointF(x1,y1);
+
+  // Calculate draw to point top
+
+  x2 := cenX - Round((MarkerSettings.Radius - MarkerSettings.Height) * cos((j + MarkerSettings.Value * 270 / 100) * Pi / 180));
+  y2 := cenY - Round((MarkerSettings.Radius - MarkerSettings.Height) * sin((j + MarkerSettings.Value * 270 / 100) * Pi / 180));
+  B := PointF(X2, y2);
+
+  // set line cap just in case
+
+  FMarkerBitmap.LineCap := pecRound; // Ensure Round Cap
+
+  // This is the vector that runs from outer to inner
+
+  U := B - A;
+
+  // build the perpendicular vector
+  // (clockwise in screen coordinates while the opposite would be counter clockwise)
+
+  V := PointF(-U.y, U.x);
+
+  // scale it to set the new segment length
+
+  vecLen := VectLen(V);
+
+  // catch odd case of zero len vector, do nothing
+
+  if vecLen = 0.0 then
+    Exit;
+
+  V := V * (MarkerSettings.Width / vecLen);
+
+  case MarkerSettings.Style of
+    msCenter: // triangle centered on the value
+      begin
+        MarkerBitmap.FillPolyAntialias([B, A + V, A - V], MarkerSettings.Color);
+      end;
+
+    msLeft:   // triangle left side only (if looking at it at half way on the gauge)
+      begin
+        MarkerBitmap.FillPolyAntialias([B, A + V, A], MarkerSettings.Color);
+      end;
+
+    msRight:
+      begin   // triangle right side only
+        MarkerBitmap.FillPolyAntialias([B, A, A - V], MarkerSettings.Color);
+      end;
+  end;
+end;
+
+/////////////////
+
+function TSGCustomSuperGauge.CheckRangeLED(AValue: single): boolean;
+begin
+  // If a single value is used for both StartRangeValue and
+  // EndRangeValue the option for rcBetween makes no sense and is a not valid
+  // and will never trigger. Also Manually setting the .Active prop will ONLY
+  // work if rcNone is set, otherwise the range checks will prevail as the
+  // way the Active state is set and overide the manual setting.
+  //
+  // Current List
+  // TSGRangeCheckType = (rcNone, rcBetween, rcBothInclusive, rcStartInclusive,
+  //                      rcEndInclusive, rcBothBetweenOutside,
+  //                      rcBothInclusiveOutside, rcGreaterStart, RangeEndValue);
+  //
+  // NOTE - rcGreaterStart, RangeEndValue ignore RangeEnd and RangeStart respectivly
+
+  if FRangeLEDSettings.RangeType = rcNone then
+  begin
+    Result := FRangeLEDSettings.Active;   // need to always return the current state here, Will never trigger RangeLED Events
+  end
+  else
+    if FRangeLEDSettings.Rangetype = rcGaugeOutOfRange then     // Special case to ONLY look at the gauge state, ignores the start/end
+      Result := FOutOfRangeState                                // Will NOT trigger any events for RangeLED, this is handled elsewhere
+    else
+      if FRangeLEDSettings.RangeType = rcGreaterStart then
+        Result := (AValue > FRangeLEDSettings.RangeStartValue)  // ignore range end, most common case
+      else
+        if FRangeLEDSettings.RangeType = rcLessEnd then
+          Result := (AValue < FRangeLEDSettings.RangeEndValue)  // ignor range start
+        else
+          if FRangeLEDSettings.RangeType = rcBetween then
+            Result := (AValue > FRangeLEDSettings.RangeStartValue) and (AValue < FRangeLEDSettings.RangeEndValue)
+          else
+            if FRangeLEDSettings.Rangetype = rcBothInclusive then
+              Result := (AValue >= FRangeLEDSettings.RangeStartValue) and (AValue <= FRangeLEDSettings.RangeEndValue)
+            else
+              if FRangeLEDSettings.Rangetype = rcBothBetweenOutside then
+                Result := (AValue < FRangeLEDSettings.RangeStartValue) or (AValue > FRangeLEDSettings.RangeEndValue)
+              else
+                if FRangeLEDSettings.Rangetype = rcStartInclusive then
+                  Result := (AValue >= FRangeLEDSettings.RangeStartValue) and (AValue < FRangeLEDSettings.RangeEndValue)
+                else
+                  if FRangeLEDSettings.Rangetype = rcEndInclusive then
+                    Result := (AValue > FRangeLEDSettings.RangeStartValue) and (AValue <= FRangeLEDSettings.RangeEndValue)
+                  else
+                    if FRangeLEDSettings.Rangetype = rcBothInclusiveOutside then
+                      Result := (AValue <= FRangeLEDSettings.RangeStartValue) or (AValue >= FRangeLEDSettings.RangeEndValue);
+
+  // Now set the flag we have changed so others SetValue() can update as needed
+
+  FRangeLEDStateChanged := FRangeLEDStateChanged or (Result <> FRangeLEDSettings.Active);
+
+  // Try the callbacks now, should hit one or the other depending on Active state
+  // if they are assigned! Rember some will NEVER casuse a call back, rcNone and
+  // rcGaugeOutOfRange
+
+  if FRangeLEDStateChanged and (FRangeLEDSettings.RangeType <> rcNone)
+    and (FRangeLEDSettings.RangeType <> rcGaugeOutOfRange) then
+  begin
+      if Assigned(FRangeLedActive) and Result then
+        FRangeLEDActive(Self, AValue)
+      else
+        if Assigned(FRangeLedActive) and (not Result) then
+          FRangeLEDInactive(Self, AValue);
+
+      FRangeLEDStateChanged := False;   // clear the state
+  end;
+
+  FRangeLEDSettings.ActiveNoDoChange := Result;
+end;
+
+end.

+ 1644 - 0
supergaugecommon.pas

@@ -0,0 +1,1644 @@
+// SPDX-License-Identifier: LGPL-3.0-linking-exception
+{
+  Part of BGRA Controls. Made by third party.
+  For detailed information see readme.txt
+
+  Site: https://sourceforge.net/p/bgra-controls/
+  Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
+  Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
+
+}
+{******************************* CONTRIBUTOR(S) ******************************
+- Edivando S. Santos Brasil | [email protected]
+  (Compatibility with delphi VCL 11/2018)
+- Sandy Ganz | [email protected]
+  Evolved from DTAnalogCommon, specific for New Gauge Work
+  Massive overhaul, fixes and features, begat Super Gauge
+  Needed to split off as changes broke compatibility badly
+
+***************************** END CONTRIBUTOR(S) *****************************}
+unit supergaugecommon;
+
+{$I bgracontrols.inc}
+
+interface
+
+uses
+  Classes, SysUtils, {$IFDEF FPC}LResources,{$ELSE}Types, {$ENDIF} Forms, Controls, Graphics, Dialogs,
+  BGRABitmap, BGRABitmapTypes, BGRAGradients, BCTypes;
+
+type
+  TSGFillStyle = (fsNone, fsGradient{, fsTexture}); // Add more if needed here
+  TSGPointerStyle = (psLine, psLineExt, psArc {, nsTriangle, nsTriangleExt}); // Todo : Add others at some point
+  TSGLEDStyle = (lsNone, lsFlat, lsShaded);
+  TSGLEDShape = (lshRound, lshSquare, lshTriangle, lshDownTriangle);
+  TSGPointerCapPosition = (cpUnder, cpOver);
+  TSGCapStyle = (csNone, csFlat, csShaded, csPhong);
+  TSGTickArc = (taNone, taOuter, taInner, taBoth); // Arc above or below ticks, inner/both is automatic on inner, main if exist, minor othewise
+  TSGRangeCheckType = (rcNone, rcGaugeOutOfRange, rcBetween, rcBothInclusive, rcStartInclusive,
+                      rcEndInclusive, rcBothBetweenOutside, rcBothInclusiveOutside,
+                      rcGreaterStart, rcLessEnd); // added for range check led, see code for details
+  TSGMarkerStyle = (msCenter, msLeft, msRight);
+
+  { TSGOrigin }
+
+  TSGOrigin = packed record
+    CenterPoint: TPoint;
+    Radius: integer;
+  end;
+
+  { TSGPointerCapSettings }
+
+  TSGPointerCapSettings = class(TPersistent)
+  private
+    FEdgeColor: TColor;
+    FEdgeThickness: integer;
+    FFillColor: TColor;
+    FOnChange: TNotifyEvent;
+    FRadius: integer;
+    FCurveExponent: single;
+    FCapStyle: TSGCapStyle;
+    FCapPosition: TSGPointerCapPosition;
+    FDirty: boolean;
+
+    procedure SetEdgeColor(AValue: TColor);
+    procedure SetEdgeThickness(AValue: integer);
+    procedure SetFillColor(AValue: TColor);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure SetRadius(AValue: integer);
+    procedure SetLightIntensity(const AValue: integer);
+    function GetLightIntensity: integer;
+    procedure SetCurveExponent(const AValue: single);
+    procedure SetCapStyle(const AValue: TSGCapStyle);
+    procedure SetPointerCapPos(const AValue: TSGPointerCapPosition);
+    procedure DirtyOnChange;
+  protected
+  public
+    FPhong: TPhongShading;
+    property Dirty: boolean read FDirty write FDirty;
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+
+  published
+    property EdgeColor: TColor read FEdgeColor write SetEdgeColor default clGray;
+    property FillColor: TColor read FFillColor write SetFillColor default clBlack;
+    property Radius: integer read FRadius write SetRadius default 30;
+    property EdgeThickness: integer read FEdgeThickness write SetEdgeThickness default 2;
+    property LightIntensity: integer read GetLightIntensity write SetLightIntensity default 300;
+    property CurveExponent: single read FCurveExponent write SetCurveExponent default 0.05;
+    property CapStyle: TSGCapStyle read FCapStyle write SetCapStyle default csPhong;
+    property CapPosition: TSGPointerCapPosition read FCapPosition write SetPointerCapPos default cpUnder;
+  end;
+
+  { TSGPointerSettings }
+
+  TSGPointerSettings = class(TPersistent)
+  private
+    FColor: TColor;
+    FLength: integer;
+    FExtensionLength: integer;
+    FOnChange: TNotifyEvent;
+    FThickness: integer;
+    FStyle: TSGPointerStyle;
+    FDirty: boolean;
+
+    procedure SetColor(AValue: TColor);
+    procedure SetLength(AValue: integer);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure SetThickness(AValue: integer);
+    procedure SetStyle(AValue: TSGPointerStyle);
+    procedure SetExtensionLength(AValue: integer);
+    procedure DirtyOnChange;
+
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property Color: TColor read FColor write SetColor;
+    property Length: integer read FLength write SetLength default 160;
+    property ExtensionLength: integer read FExtensionLength write SetExtensionLength default 20;
+    property Thickness: integer read FThickness write SetThickness default 5;
+    property Style: TSGPointerStyle read FStyle write SetStyle default psLineExt;
+  end;
+
+  { TSGScaleSettings }
+
+  TSGScaleSettings = class(TPersistent)
+  private
+    FEnableScaleText: boolean;
+    FStart: integer;
+    FStep: integer;
+    FMaximum: integer;
+    FMinimum: integer;
+    FTextFont: string;
+    FTextRadius: integer;
+    FTextSize: integer;
+    FTextStyle: TFontStyles;
+    FTickColor: TColor;
+    FEnableMainTicks: boolean;
+    FEnableSubTicks: boolean;
+    FReverseScale: boolean;
+    FThicknessMainTick: integer;
+    FThicknessSubTick: integer;
+    FLengthMainTick: integer;
+    FLengthSubTick: integer;
+    FMainTickCount: integer;
+    FOnChange: TNotifyEvent;
+    FSubTickCount: integer;
+    FTextColor: TColor;
+    FScaleRadius: integer;
+    FTickArcStyle: TSGTickArc;
+    FDirty: boolean;
+
+    procedure SetEnableScaleText(AValue: boolean);
+    procedure SetStart(AValue: integer);
+    procedure SetStep(AValue: integer);
+    procedure SetMaximum(AValue: integer);
+    procedure SetMinimum(AValue: integer);
+    procedure SetTextFont(AValue: string);
+    procedure SetTextRadius(AValue: integer);
+    procedure SetTextSize(AValue: integer);
+    procedure SetTextStyle(AValue: TFontStyles);
+    procedure SetTickColor(AValue: TColor);
+    procedure SetEnableMainTicks(AValue: boolean);
+    procedure SetEnableSubTicks(AValue: boolean);
+    procedure SetReverseScale(AValue: boolean);
+    procedure SetLengthMainTick(AValue: integer);
+    procedure SetLengthSubTick(AValue: integer);
+    procedure SetMainTickCount(AValue: integer);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure SetSubTickCount(AValue: integer);
+    procedure SetTextColor(AValue: TColor);
+    procedure SetThicknessMainTick(AValue: integer);
+    procedure SetThicknessSubTick(AValue: integer);
+    procedure SetTickArcStyle(AValue: TSGTickArc);
+    procedure SetScaleRadius(AValue: integer);
+    procedure DirtyOnChange;
+
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property TickColor: TColor read FTickColor write SetTickColor;
+    property TextColor: TColor read FTextColor write SetTextColor;
+    property TextSize: integer read FTextSize write SetTextSize default 20;
+    property TextStyle: TFontStyles read FTextStyle write SetTextStyle;
+    property TextFont: string read FTextFont write SetTextFont;
+    property EnableMainTicks: boolean read FEnableMainTicks write SetEnableMainTicks;
+    property EnableSubTicks: boolean read FEnableSubTicks write SetEnableSubTicks;
+    property EnableScaleText: boolean read FEnableScaleText write SetEnableScaleText;
+    property ReverseScale: boolean read FReverseScale write SetReverseScale default False;
+    property Start: integer read FStart write SetStart default 0;
+    property Step: integer read FStep write SetStep default 1;
+    property MainTickCount: integer read FMainTickCount write SetMainTickCount;
+    property SubTickCount: integer read FSubTickCount write SetSubTickCount;
+    property LengthMainTick: integer read FLengthMainTick write SetLengthMainTick;
+    property LengthSubTick: integer read FLengthSubTick write SetLengthSubTick;
+    property ThicknessMainTick: integer read FThicknessMainTick write SetThicknessMainTick;
+    property ThicknessSubTick: integer read FThicknessSubTick write SetThicknessSubTick;
+    property TextRadius: integer read FTextRadius write SetTextRadius default 120;
+    property ScaleRadius: integer read FScaleRadius write SetScaleRadius;
+    property TickArcStyle : TSGTickArc read FTickArcStyle write SetTickArcStyle default taOuter;
+  end;
+
+  { TSGBandSettings }
+
+  TSGBandSettings = class(TPersistent)
+  private
+    FEnabled: boolean;
+    FStartValue: single;
+    FEndValue: single;
+    FEnableText: boolean;
+    FText: TCaption;
+    FTextFont: string;
+    FTextStyle: TFontStyles;
+    FTextRadius: integer;
+    FTextSize: integer;
+    FTextColor: TColor;
+    FOnChange: TNotifyEvent;
+    FBandThickness: integer;
+    FBandRadius: integer;       // defines the outer Radius length in pixels, likely center of width/thickness
+    FBandColor: TColor;
+    FDirty: boolean;
+
+    procedure SetEnabled(AValue: boolean);
+    procedure SetStartValue(AValue: single);
+    procedure SetEndValue(AValue: single);
+    procedure SetEnableText(AValue: boolean);
+    procedure SetText(AValue: TCaption);
+    procedure SetTextSize(AValue: integer);
+    procedure SetTextFont(AValue: string);
+    procedure SetTextStyle(AValue: TFontStyles);
+    procedure SetTextRadius(AValue: integer);
+    procedure SetTextColor(AValue: TColor);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure SetBandThickness(AValue: integer);
+    procedure SetBandRadius(AValue: integer);
+    procedure SetBandColor(AValue: TColor);
+    procedure DirtyOnChange;
+
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property Enabled: boolean read FEnabled write SetEnabled default False;
+    property StartValue: single read FStartValue write SetStartValue default 0.0;
+    property EndValue: single read FEndValue write SetEndValue default 100.0;
+    property EnableText: boolean read FEnableText write SetEnableText;
+    property Text: TCaption read FText write SetText;
+    property TextSize: integer read FTextSize write SetTextSize default 20;
+    property TextFont: string read FTextFont write SetTextFont;
+    property TextStyle: TFontStyles read FTextStyle write SetTextStyle;
+    property TextRadius: integer read FTextRadius write SetTextRadius;
+    property TextColor: TColor read FTextColor write SetTextColor;
+    property Thickness: integer read FBandThickness write SetBandThickness;
+    property BandRadius: integer read FBandRadius write SetBandRadius;
+    Property BandColor: TColor read FBandColor write SetBandColor;
+  end;
+
+  { TSGFaceSettings }
+
+  TSGFaceSettings = class(TPersistent)
+  private
+    FInnerColor: TColor;
+    FOuterColor: TColor;
+    FFillStyle: TSGFillStyle;
+    FPicture: TPicture;
+    FPictureEnabled: boolean;
+    FPictureOffsetX, FPictureOffsetY: integer;
+    FOnChange: TNotifyEvent;
+    FDirty: boolean;
+
+    procedure SetInnerColor(AValue: TColor);
+    procedure SetOuterColor(AValue: TColor);
+    procedure SetFillStyle(AValue: TSGFillStyle);
+    procedure SetPicture(AValue: TPicture);
+    procedure SetPictureEnabled(AValue: boolean);
+    procedure SetPictureOffsetX(AValue: integer);
+    procedure SetPictureOffsetY(AValue: integer);
+
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure DirtyOnChange;
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property FillStyle: TSGFillStyle read FFillStyle write SetFillStyle;
+    property InnerColor: TColor read FInnerColor write SetInnerColor;
+    property OuterColor: TColor read FOuterColor write SetOuterColor;
+    property Picture: TPicture read FPicture write SetPicture;
+    property PictureEnabled: boolean read FPictureEnabled write SetPictureEnabled;
+    property PictureOffsetX: integer read FPictureOffsetX write SetPictureOffsetX default 0;
+    property PictureOffsetY: integer read FPictureOffsetY write SetPictureOffsetY default 0;
+  end;
+
+  { TSGFrameSettings }
+
+  TSGFrameSettings = class(TPersistent)
+  private
+    FFrameColor: TColor;
+    FBorderColor: TColor;
+    FBorderRadius: integer;
+    FOnChange: TNotifyEvent;
+    FDirty: boolean;
+
+    procedure SetBorderRadius(AValue: integer);
+    procedure SetFrameColor(AValue: TColor);
+    procedure SetBorderColor(AValue: TColor);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure DirtyOnChange;
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property BorderRadius: integer read FBorderRadius write SetBorderRadius;
+    property FrameColor: TColor read FFrameColor write SetFrameColor;
+    property BorderColor: TColor read FBorderColor write SetBorderColor;
+  end;
+
+  { TSGLEDSettings }
+
+  TSGLEDSettings = class(TPersistent)
+  private
+    FActiveColor: TColor;
+    FInactiveColor: TColor;
+    FBorderColor: TColor;
+    FSize: integer;
+    FOffsetX, FOffsetY: integer;
+    FStyle: TSGLEDStyle;
+    FShape: TSGLEDShape;
+    FOnChange: TNotifyEvent;
+    FActive: boolean;
+    FDirty: boolean;
+
+    procedure SetActive(AValue: boolean);
+    procedure SetActiveNoDoChange(AValue: boolean);
+    procedure SetActiveColor(AValue: TColor);
+    procedure SetInactiveColor(AValue: TColor);
+    procedure SetBorderColor(AValue: TColor);
+    procedure SetSize(AValue: integer);
+    procedure SetOffsetX(AValue: integer);
+    procedure SetOffsetY(AValue: integer);
+    procedure SetStyle(AValue: TSGLEDStyle);
+    procedure SetShape(AValue: TSGLEDShape);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure DirtyOnChange;
+
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property ActiveColor: TColor read FActiveColor write SetActiveColor;
+    property InactiveColor: TColor read FInactiveColor write SetInactiveColor;
+    property BorderColor: TColor read FBorderColor write SetBorderColor;
+    property Size: integer read FSize write SetSize default 10;
+    property OffsetX: integer read FOffsetX write SetOffsetX default 0;
+    property OffsetY: integer read FOffsetY write SetOffsetY default 50;
+    property Style: TSGLEDStyle read FStyle write SetStyle default lsShaded;
+    property Shape: TSGLEDShape read FShape write SetShape default lshRound;
+    property Active: boolean read FActive write SetActive;
+    property ActiveNoDoChange: boolean read FActive write SetActiveNoDoChange;
+  end;
+
+  { TSGRangeCheckLEDSettings }
+
+  TSGRangeCheckLEDSettings = class(TSGLEDSettings)
+    private
+      FRangeStartValue : single;
+      FRangeEndValue : single;
+      FRangeType: TSGRangeCheckType;
+      procedure SetRangeStartValue(AValue: single);
+      procedure SetRangeEndValue(AValue: single);
+      procedure SetRangeType(AValue: TSGRangeCheckType);
+
+    protected
+    public
+      constructor Create;
+      destructor Destroy; override;
+
+    published
+      property RangeStartValue: single read FRangeStartValue write SetRangeStartValue default 0;
+      property RangeEndValue: single read FRangeEndValue write SetRangeEndValue default 100;
+      property RangeType: TSGRangeCheckType read FRangeType write SetRangeType;
+      property ActiveColor;
+      property InactiveColor;
+      property BorderColor;
+      property Size;
+      property OffsetX;   // origin at center based offset
+      property OffsetY;
+      property Style;
+  end;
+
+{ TSGTextSettings }
+
+TSGTextSettings = class(TPersistent)
+private
+  FEnabled: boolean;
+  FFontEx: TBCFont;
+  FText : TCaption;
+  FOffsetX, FOffsetY: integer;
+  FOnChange: TNotifyEvent;
+  FDirty: boolean;
+
+  procedure SetEnabled(AValue: boolean);
+  procedure SetOffsetX(AValue: integer);
+  procedure SetOffsetY(AValue: integer);
+  procedure SetOnChange(AValue: TNotifyEvent);
+  procedure DirtyOnChange;
+  procedure SetText(AValue: TCaption);
+  procedure SetFontEx(AValue: TBCFont);
+
+protected
+public
+  constructor Create;
+  destructor Destroy; override;
+  property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+  property Dirty: boolean read FDirty write FDirty;
+
+published
+  property Enabled: boolean read FEnabled write SetEnabled default False;
+  property FontEx: TBCFont read FFontEx write SetFontEx;
+  property Text: TCaption read FText write SetText;
+  property OffsetX: integer read FOffsetX write SetOffsetX;
+  property OffsetY: integer read FOffsetY write SetOffsetY;
+end;
+
+{ TSGMarkerSettings }
+
+//  Marker can be left or right or centered. The flat side should
+//  be aligned with the markers value or in the case of centered, will
+//  be the center of the marker. Like -  \| - Left, |/ - Right, \/ - Centered
+
+TSGMarkerSettings = class(TPersistent)
+private
+  FValue: single;     // this is the internal gauge value not user value
+  FEnabled: boolean;
+  FColor: TColor;
+  FHeight: integer;
+  FRadius: integer;
+  FWidth: integer;
+  FStyle: TSGMarkerStyle;
+  FOnChange: TNotifyEvent;
+  FDirty: boolean;
+
+  procedure SetValue(AValue: single);
+  procedure SetEnabled(AValue: boolean);
+  procedure SetColor(AValue: TColor);
+  procedure SetHeight(AValue: integer);
+  procedure SetRadius(AValue: integer);
+  procedure SetWidth(AValue: integer);
+  procedure SetOnChange(AValue: TNotifyEvent);
+  procedure SetStyle(AValue: TSGMarkerStyle);
+  procedure DirtyOnChange;
+
+protected
+public
+  constructor Create;
+  destructor Destroy; override;
+  property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+  property Dirty: boolean read FDirty write FDirty;
+
+published
+  property Value: single read FValue write SetValue default 0.0;
+  property Enabled: boolean read FEnabled write SetEnabled default False;
+  property Color: TColor read FColor write SetColor;
+  property Height: integer read FHeight write SetHeight;
+  property Radius: integer read FRadius write SetRadius;
+  property Width: integer read FWidth write SetWidth;
+  property Style: TSGMarkerStyle read FStyle write SetStyle default msCenter;
+end;
+
+function Initializebitmap(var Bitmap: TBGRABitmap; Width, Height: integer): TSGOrigin;
+
+implementation
+
+function Initializebitmap(var Bitmap: TBGRABitmap; Width, Height: integer): TSGOrigin;
+begin
+  Bitmap.SetSize(Width, Height);
+
+  // Clear bitmap to transparent
+
+  BitMap.Fill(BGRA(0, 0, 0, 0));
+
+  // Get origin information
+
+  Result.CenterPoint.x := Width div 2;
+  Result.CenterPoint.y := Height div 2;
+
+  // Take the smallest so radius will always fit
+
+  if Result.CenterPoint.x < Result.CenterPoint.y then
+    Result.Radius := Result.CenterPoint.x
+  else
+    Result.Radius := Result.CenterPoint.y;
+end;
+
+{ TSGPointerCapSettings }
+
+procedure TSGPointerCapSettings.SetCapStyle(const AValue: TSGCapStyle);
+begin
+  if FCapStyle = AValue then
+    Exit;
+
+  FCapStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerCapSettings.SetPointerCapPos(const AValue: TSGPointerCapPosition);
+begin
+  if FCapPosition = AValue then
+    Exit;
+
+  FCapPosition := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerCapSettings.SetLightIntensity(const AValue: integer);
+begin
+  if AValue = FPhong.LightSourceIntensity then
+    Exit;
+
+  FPhong.LightSourceIntensity := AValue;
+  DirtyOnChange;
+end;
+
+function TSGPointerCapSettings.GetLightIntensity: integer;
+begin
+  Result := round(FPhong.LightSourceIntensity);
+end;
+
+procedure TSGPointerCapSettings.SetCurveExponent(const AValue: single);
+begin
+  if FCurveExponent = AValue then
+    Exit;
+
+  FCurveExponent := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerCapSettings.SetEdgeColor(AValue: TColor);
+begin
+  if FEdgeColor = AValue then
+    Exit;
+
+  FEdgeColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerCapSettings.SetEdgeThickness(AValue: integer);
+begin
+  if FEdgeThickness = AValue then
+    Exit;
+
+  FEdgeThickness := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerCapSettings.SetFillColor(AValue: TColor);
+begin
+  if FFillColor = AValue then
+    Exit;
+
+  FFillColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerCapSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // no dirty needed possibly, call directly
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGPointerCapSettings.SetRadius(AValue: integer);
+begin
+  if FRadius = AValue then
+    Exit;
+
+  FRadius := AValue;
+  DirtyOnChange;
+end;
+
+constructor TSGPointerCapSettings.Create;
+begin
+  // create a phong shader, will need to delete on clean up
+
+  FPhong := TPhongShading.Create;
+
+  FPhong.LightPositionZ := 100;
+  FPhong.LightSourceIntensity := 300;
+  FPhong.NegativeDiffusionFactor := 0.8;
+  FPhong.AmbientFactor := 0.5;
+  FPhong.DiffusionFactor := 0.6;
+  FCurveExponent := 0.05;
+  FCapStyle := csPhong;
+  FCapPosition := cpUnder;
+  FEdgeColor := clGray;
+  FFillColor := clBlack;
+  FRadius := 20;
+  FEdgeThickness := 2;
+  FDirty := True;
+end;
+
+destructor TSGPointerCapSettings.Destroy;
+begin
+  FPhong.Free;
+  inherited Destroy;
+end;
+
+procedure TSGPointerCapSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here some props must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSGPointerSettings }
+
+procedure TSGPointerSettings.SetColor(AValue: TColor);
+begin
+  if FColor = AValue then
+    Exit;
+
+  FColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerSettings.SetLength(AValue: integer);
+begin
+  if FLength = AValue then
+    Exit;
+
+  FLength := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGPointerSettings.SetThickness(AValue: integer);
+begin
+  if FThickness = AValue then
+    Exit;
+
+  FThickness := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerSettings.SetStyle(AValue: TSGPointerStyle);
+begin
+  if FStyle = AValue then
+    Exit;
+
+  FStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGPointerSettings.SetExtensionLength(AValue: integer);
+begin
+  if FExtensionLength = AValue then
+    Exit;
+
+  FExtensionLength := AValue;
+  DirtyOnChange;
+end;
+
+constructor TSGPointerSettings.Create;
+begin
+  FColor := BGRA(255, 127, 63); // Orange pointer
+  FLength := 160;
+  FThickness := 5;
+  FExtensionLength := 20;
+  FStyle := psLineExt;
+  FDirty := True;
+end;
+
+procedure TSGPointerSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here a prop must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+destructor TSGPointerSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+{ TSGScaleSettings }
+
+constructor TSGScaleSettings.Create;
+begin
+  FTickColor := BGRA(223, 196, 125);  // Tan
+  FTextColor := BGRA(140, 208, 211);  // Light Blue
+  FTextFont := 'Calibri';
+  FTextSize := 20;
+  FTextRadius := 120;
+  FEnableMainTicks := True;
+  FEnableSubTicks := True;
+  FEnableScaleText := True;
+  FReverseScale := False;
+  FMainTickCount := 10;
+  FSubTickCount := 5;
+  FStart := 0;
+  FStep := 1;
+  FLengthMainTick := 15;
+  FLengthSubTick := 8;
+  FThicknessMainTick := 3;
+  FThicknessSubTick := 1;
+  FTickArcStyle := taOuter;
+  FScaleRadius := 155;
+  FDirty := True;
+end;
+
+destructor TSGScaleSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSGScaleSettings.SetTextFont(AValue: string);
+begin
+  if FTextFont = AValue then
+    Exit;
+  FTextFont := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetEnableScaleText(AValue: boolean);
+begin
+  if FEnableScaleText = AValue then
+    Exit;
+  FEnableScaleText := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetReverseScale(AValue: boolean);
+begin
+  if FReverseScale = AValue then
+    Exit;
+  FReverseScale := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetScaleRadius(AValue: integer);
+begin
+  if (FScaleRadius = AValue) or (AValue < 1) then
+    Exit;
+
+  FScaleRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetMaximum(AValue: integer);
+begin
+  if (FMaximum = AValue) or (AValue <= FMinimum) then
+    Exit;
+
+  FMaximum := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetStart(AValue: integer);
+begin
+  if (FStart = AValue)then
+    Exit;
+
+  FStart := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetStep(AValue: integer);
+begin
+  if (FStep = AValue)then
+    Exit;
+
+  FStep := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetMinimum(AValue: integer);
+begin
+  if (FMinimum = AValue) then
+    Exit;
+
+  FMinimum := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetTextRadius(AValue: integer);
+begin
+  if (FTextRadius = AValue) or (AValue < 1) then
+    Exit;
+
+  FTextRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetTextSize(AValue: integer);
+begin
+  if (FTextSize = AValue) or (AValue < 1) then
+    Exit;
+
+  FTextSize := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetTickColor(AValue: TColor);
+begin
+  if FTickColor = AValue then
+    Exit;
+
+  FTickColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetTickArcStyle(AValue: TSGTickArc);
+begin
+  if FTickArcStyle = AValue then
+     exit;
+
+  FTickArcStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetEnableMainTicks(AValue: boolean);
+begin
+  if FEnableMainTicks = AValue then
+    Exit;
+
+  FEnableMainTicks := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetEnableSubTicks(AValue: boolean);
+begin
+  if FEnableSubTicks = AValue then
+    Exit;
+
+  FEnableSubTicks := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetLengthMainTick(AValue: integer);
+begin
+  if (FLengthMainTick = AValue) or (AValue < 1) then
+    Exit;
+
+  FLengthMainTick := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetLengthSubTick(AValue: integer);
+begin
+  if (FLengthSubTick = AValue) or (AValue < 1) then
+    Exit;
+
+  FLengthSubTick := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetMainTickCount(AValue: integer);
+begin
+  if (FMainTickCount = AValue) or (AValue < 1) then
+    Exit;
+
+  FMainTickCount := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGScaleSettings.SetSubTickCount(AValue: integer);
+begin
+  if (FSubTickCount = AValue) or (AValue < 1) then
+    Exit;
+
+  FSubTickCount := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetTextColor(AValue: TColor);
+begin
+  if FTextColor = AValue then
+    Exit;
+
+  FTextColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetTextStyle(AValue: TFontStyles);
+begin
+  if FTextStyle = AValue then
+    Exit;
+
+  FTextStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetThicknessMainTick(AValue: integer);
+begin
+  if (FThicknessMainTick = AValue) or (AValue < 1) then
+    Exit;
+
+  FThicknessMainTick := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.SetThicknessSubTick(AValue: integer);
+begin
+  if (FThicknessSubTick = AValue) or (AValue < 1)  then
+    Exit;
+
+  FThicknessSubTick := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGScaleSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here a prop must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSGBandSettings}
+
+constructor TSGBandSettings.Create;
+begin
+  FEnabled := False;
+  FEnableText := False;
+  FText := '';
+  FTextColor := clBlack;
+  FTextFont := 'default';
+  FTextStyle := [];
+  FTextSize := 20;
+  FTextRadius := 100;
+  FStartValue := 0;
+  FEndValue := 20;
+  FBandRadius := 100;
+  FBandColor := clGreen;
+  FBandThickness := 40;
+  FStartValue := 0;
+  FEndValue := 100;
+
+  FDirty := True;
+end;
+
+destructor TSGBandSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSGBandSettings.SetEnabled(AValue: boolean);
+begin
+  if FEnabled = AValue then
+    Exit;
+
+  FEnabled := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetTextFont(AValue: string);
+begin
+  if FTextFont = AValue then
+    Exit;
+
+  FTextFont := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetTextStyle(AValue: TFontStyles);
+begin
+  if FTextStyle = AValue then
+    Exit;
+
+  FTextStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetEnableText(AValue: boolean);
+begin
+  if FEnableText = AValue then
+    Exit;
+
+  FEnableText := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetText(AValue: TCaption);
+begin
+  if FText = AValue then
+    Exit;
+
+  FText := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetTextRadius(AValue: integer);
+begin
+  if (FTextRadius = AValue) or (AValue < 1) then
+    Exit;
+
+  FTextRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetBandRadius(AValue: integer);
+begin
+  if (FBandRadius = AValue) or (AValue < 1) then
+    Exit;
+
+  FBandRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetStartValue(AValue: single);
+begin
+  if (FStartValue = AValue) or (AValue >= FEndValue) then
+    Exit;
+
+  FStartValue := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetEndValue(AValue: single);
+begin
+  if (FEndValue = AValue) or (AValue <= FStartValue) then
+    Exit;
+
+  FEndValue := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetTextSize(AValue: integer);
+begin
+  if (FTextSize = AValue) or (AValue < 1) then
+    Exit;
+
+  FTextSize := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGBandSettings.SetTextColor(AValue: TColor);
+begin
+  if FTextColor = AValue then
+    Exit;
+
+  FTextColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetBandColor(AValue: TColor);
+begin
+  if FBandColor = AValue then
+    Exit;
+
+  FBandColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.SetBandThickness(AValue: integer);
+begin
+  if (FBandThickness = AValue) or (AValue < 1) then
+    Exit;
+
+  FBandThickness := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGBandSettings.DirtyOnChange;
+begin
+  FDirty := True;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSGFaceSettings }
+
+constructor TSGFaceSettings.Create;
+begin
+  FOuterColor := clBlack;
+  FInnerColor := clGray;
+  FFillStyle := fsGradient;
+  FPicture := TPicture.Create;
+  FPictureEnabled := FALSE;
+  FPictureOffsetX := 0;
+  FPictureOffsetY := 0;
+
+  FDirty := True;
+end;
+
+destructor TSGFaceSettings.Destroy;
+begin
+  FPicture.Free;
+  inherited Destroy;
+end;
+
+procedure TSGFaceSettings.SetInnerColor(AValue: TColor);
+begin
+  if FInnerColor = AValue then
+    Exit;
+
+  FInnerColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetOuterColor(AValue: TColor);
+begin
+  if FOuterColor = AValue then
+    Exit;
+
+  FOuterColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetFillStyle(AValue: TSGFillStyle);
+begin
+  if FFillStyle = AValue then
+    Exit;
+
+  FFillStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetPicture(AValue: TPicture);
+begin
+  if FPicture = AValue then
+    Exit;
+
+  FPicture := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetPictureEnabled(AValue: boolean);
+begin
+
+  if FPictureEnabled = AValue then
+    Exit;
+
+  FPictureEnabled := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetPictureOffsetX(AValue: integer);
+begin
+  if FPictureOffsetX = AValue then
+    Exit;
+
+  FPictureOffsetX := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetPictureOffsetY(AValue: integer);
+begin
+  if FPictureOffsetY = AValue then
+    Exit;
+
+  FPictureOffsetY := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFaceSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGFaceSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here a prop must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSGFrameSettings }
+
+constructor TSGFrameSettings.Create;
+begin
+  FFrameColor := clBlack;
+  FBorderColor := clGray;
+  FBorderRadius := 2;
+  FDirty := True;
+end;
+
+destructor TSGFrameSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSGFrameSettings.SetBorderRadius(AValue: integer);
+begin
+  if (FBorderRadius = AValue) or (AValue < 0) then
+    Exit;
+
+  FBorderRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFrameSettings.SetFrameColor(AValue: TColor);
+begin
+  if FFrameColor = AValue then
+    Exit;
+
+  FFrameColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFrameSettings.SetBorderColor(AValue: TColor);
+begin
+  if FBorderColor = AValue then
+    Exit;
+
+  FBorderColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGFrameSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGFrameSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here a prop must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSGLEDSettings }
+
+constructor TSGLEDSettings.Create;
+begin
+  FActiveColor := clRed;
+  FInActiveColor := clBlack;
+  FBorderColor := clGray;
+  FSize := 10;
+  FOffsetX := 0;
+  FOffsetY := 50;
+  FStyle := lsShaded;
+
+  FDirty := True;
+end;
+
+destructor TSGLEDSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSGLEDSettings.SetActive(AValue: boolean);
+begin
+  if FActive = AValue then
+    Exit;
+
+  FActive := AValue;
+  DirtyOnChange;
+end;
+
+// HACK, need to have a way to NOT dirty on change
+
+procedure TSGLEDSettings.SetActiveNoDoChange(AValue: boolean);
+begin
+  if FActive = AValue then
+    Exit;
+
+  FActive := AValue;
+end;
+
+procedure TSGLEDSettings.SetActiveColor(AValue: TColor);
+begin
+  if FActiveColor = AValue then
+    Exit;
+
+  FActiveColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetInactiveColor(AValue: TColor);
+begin
+  if FInactiveColor = AValue then
+    Exit;
+
+  FInActiveColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetBorderColor(AValue: TColor);
+begin
+  if FBorderColor = AValue then
+    Exit;
+
+  FBorderColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetSize(AValue: integer);
+begin
+  if FSize = AValue then
+    Exit;
+
+  FSize := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetOffsetX(AValue: integer);
+begin
+  if FOffsetX = AValue then
+    Exit;
+
+  FOffsetX := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetOffsetY(AValue: integer);
+begin
+  if FOffsetY = AValue then
+    Exit;
+
+  FOffsetY := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetStyle(AValue: TSGLEDStyle);
+begin
+  if FStyle = AValue then
+    Exit;
+
+  FStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetShape(AValue: TSGLEDShape);
+begin
+  if FShape = AValue then
+    Exit;
+
+  FShape := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGLEDSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // this will not dirty it, may need to not sure
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGLEDSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here a prop must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSGRangeCheckLEDSettings }
+
+constructor TSGRangeCheckLEDSettings.Create;
+begin
+  inherited Create;
+
+  FRangeStartValue := 0;
+  FRangeEndValue := 100;
+  FRangeType := rcNone;
+  FOffsetX := 90;
+  FOffsetY := 120;
+end;
+
+destructor TSGRangeCheckLEDSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSGRangeCheckLEDSettings.SetRangeStartValue(AValue: single);
+begin
+  if (FRangeStartValue = AValue) or (AValue > FRangeEndValue) then
+    Exit;
+
+  FRangeStartValue := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGRangeCheckLEDSettings.SetRangeEndValue(AValue: single);
+begin
+  if (FRangeEndValue = AValue) or (AValue < FRangeStartValue) then
+    Exit;
+
+  FRangeEndValue := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGRangeCheckLEDSettings.SetRangeType(AValue: TSGRangeCheckType);
+begin
+  if FRangeType = AValue then
+    Exit;
+
+  FRangeType := AValue;
+  DirtyOnChange;
+end;
+
+{ TSGTextSettings }
+
+constructor TSGTextSettings.Create;
+begin
+  FText := 'Gauge';
+  OffsetX := 0;
+  OffsetY := 50; // default should be clear of default cap radius when it's drawn
+  FDirty := True;
+
+  // create font, must free in dtor
+
+  FFontEx := TBCFont.Create(nil);
+  FFontEx.Color := clWhite;
+  FFontEx.Style := [fsBold];
+  FFontEx.Height := 24;
+end;
+
+destructor TSGTextSettings.Destroy;
+begin
+  FFontEx.Free;
+
+  inherited Destroy;
+end;
+
+procedure TSGTextSettings.SetEnabled(AValue: boolean);
+begin
+  if FEnabled = AValue then
+    Exit;
+
+  FEnabled := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGTextSettings.SetText(AValue: TCaption);
+begin
+  if FText = AValue then
+    Exit;
+
+  FText := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGTextSettings.SetOffsetX(AValue: integer);
+begin
+  if FOffsetX = AValue then
+    Exit;
+
+  FOffsetX := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGTextSettings.SetOffsetY(AValue: integer);
+begin
+  if FOffsetY = AValue then
+    Exit;
+
+  FOffsetY := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGTextSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // this will not dirty it, may need to not sure
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGTextSettings.DirtyOnChange;
+begin
+  FDirty := True;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGTextSettings.SetFontEx(AValue: TBCFont);
+begin
+  FFontEx.Assign(AValue);
+  FDirty := True;
+  DirtyOnChange;
+end;
+
+{TSGMarkerSettings}
+
+constructor TSGMarkerSettings.Create;
+begin
+  FEnabled := False;
+  FColor := clLime;
+  FHeight := 20;
+  FWidth := 10;
+  FRadius := 165;
+  FStyle := msCenter;
+  FDirty := True;
+  FValue := 0.0;
+end;
+
+destructor TSGMarkerSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSGMarkerSettings.SetValue(AValue: single);
+begin
+  if FValue = AValue then
+    Exit;
+
+  FValue := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.SetEnabled(AValue: boolean);
+begin
+  if FEnabled = AValue then
+    Exit;
+
+  FEnabled := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.SetColor(AValue: TColor);
+begin
+  if FColor = AValue then
+    Exit;
+
+  FColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.SetHeight(AValue: integer);
+begin
+  if FHeight = AValue then
+    Exit;
+
+  FHeight := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.SetRadius(AValue: integer);
+begin
+  if FRadius = AValue then
+    Exit;
+
+  FRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.SetWidth(AValue: integer);
+begin
+  if FWidth = AValue then
+    Exit;
+
+  FWidth := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // no dirty needed possibly, call directly
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSGMarkerSettings.SetStyle(AValue: TSGMarkerStyle);
+begin
+  if FStyle = AValue then
+    Exit;
+
+  FStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSGMarkerSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here some props must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+end.