RadialGauge.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. using System;
  2. using System.ComponentModel;
  3. using System.Drawing;
  4. using System.Drawing.Design;
  5. using System.Drawing.Drawing2D;
  6. using FastReport.Utils;
  7. namespace FastReport.Gauge.Radial
  8. {
  9. #region Enums
  10. /// <summary>
  11. /// Radial Gauge types
  12. /// </summary>
  13. [Flags]
  14. public enum RadialGaugeType
  15. {
  16. /// <summary>
  17. /// Full sized gauge
  18. /// </summary>
  19. Circle = 1,
  20. /// <summary>
  21. /// Half of the radial gauge
  22. /// </summary>
  23. Semicircle = 2,
  24. /// <summary>
  25. /// Quarter of the radial gauge
  26. /// </summary>
  27. Quadrant = 4
  28. }
  29. /// <summary>
  30. /// Radial Gauge position types
  31. /// </summary>
  32. [Flags]
  33. public enum RadialGaugePosition
  34. {
  35. /// <summary>
  36. /// None
  37. /// </summary>
  38. None = 0,
  39. /// <summary>
  40. /// Top
  41. /// </summary>
  42. Top = 1,
  43. /// <summary>
  44. /// Bottom
  45. /// </summary>
  46. Bottom = 2,
  47. /// <summary>
  48. /// Left
  49. /// </summary>
  50. Left = 4,
  51. /// <summary>
  52. /// Right
  53. /// </summary>
  54. Right = 8
  55. }
  56. #endregion // Enums
  57. /// <summary>
  58. /// Represents a linear gauge.
  59. /// </summary>
  60. public partial class RadialGauge : GaugeObject
  61. {
  62. private const double RAD = Math.PI / 180.0;
  63. private PointF center;
  64. private RadialGaugeType type;
  65. private RadialGaugePosition position;
  66. private float semicircleOffsetRatio;
  67. #region Properties
  68. /// <inheritdoc/>
  69. public override float Width
  70. {
  71. get { return base.Width; }
  72. set
  73. {
  74. base.Width = value;
  75. if (base.Height != base.Width)
  76. {
  77. base.Height = Width;
  78. }
  79. }
  80. }
  81. /// <inheritdoc/>
  82. public override float Height
  83. {
  84. get { return base.Height; }
  85. set
  86. {
  87. base.Height = value;
  88. if (base.Width != base.Height)
  89. {
  90. base.Width = Height;
  91. }
  92. }
  93. }
  94. /// <summary>
  95. /// Returns centr of the gauge
  96. /// </summary>
  97. [Browsable(false)]
  98. public PointF Center
  99. {
  100. get { return center; }
  101. set { center = value; }
  102. }
  103. /// <summary>
  104. /// The number of radians in one degree
  105. /// </summary>
  106. public static double Radians
  107. {
  108. get { return RAD; }
  109. }
  110. /// <summary>
  111. /// Gets or sets the Radial Gauge type
  112. /// </summary>
  113. [Browsable(true)]
  114. [Category("Appearance")]
  115. public RadialGaugeType Type
  116. {
  117. get { return type; }
  118. set
  119. {
  120. if (value == RadialGaugeType.Circle)
  121. {
  122. position = RadialGaugePosition.None;
  123. type = value;
  124. }
  125. if (value == RadialGaugeType.Semicircle &&
  126. !(Position == RadialGaugePosition.Bottom ||
  127. Position == RadialGaugePosition.Left ||
  128. Position == RadialGaugePosition.Right ||
  129. Position == RadialGaugePosition.Top))
  130. {
  131. position = RadialGaugePosition.Top;
  132. type = value;
  133. }
  134. else if (value == RadialGaugeType.Quadrant &&
  135. !(
  136. ((Position & RadialGaugePosition.Left) != 0 && (Position & RadialGaugePosition.Top) != 0 &&
  137. (Position & RadialGaugePosition.Right) == 0 && (Position & RadialGaugePosition.Bottom) == 0) ||
  138. ((Position & RadialGaugePosition.Right) != 0 && (Position & RadialGaugePosition.Top) != 0 &&
  139. (Position & RadialGaugePosition.Left) == 0 && (Position & RadialGaugePosition.Bottom) == 0) ||
  140. ((Position & RadialGaugePosition.Left) != 0 && (Position & RadialGaugePosition.Bottom) != 0 &&
  141. (Position & RadialGaugePosition.Right) == 0 && (Position & RadialGaugePosition.Top) == 0) ||
  142. ((Position & RadialGaugePosition.Right) != 0 && (Position & RadialGaugePosition.Bottom) != 0 &&
  143. (Position & RadialGaugePosition.Left) == 0 && (Position & RadialGaugePosition.Top) == 0)
  144. ))
  145. {
  146. position = RadialGaugePosition.Top | RadialGaugePosition.Left;
  147. type = value;
  148. }
  149. }
  150. }
  151. /// <summary>
  152. /// Gats or sets the Radial Gauge position. Doesn't work for Full Radial Gauge.
  153. /// </summary>
  154. [Category("Appearance")]
  155. [Editor("FastReport.TypeEditors.FlagsEditor, FastReport", typeof(UITypeEditor))]
  156. public RadialGaugePosition Position
  157. {
  158. get { return position; }
  159. set
  160. {
  161. if(Type == RadialGaugeType.Semicircle &&
  162. (value == RadialGaugePosition.Bottom ||
  163. value == RadialGaugePosition.Left ||
  164. value == RadialGaugePosition.Right ||
  165. value == RadialGaugePosition.Top))
  166. position = value;
  167. else if (Type == RadialGaugeType.Quadrant &&
  168. (
  169. ((value & RadialGaugePosition.Left) != 0 && (value & RadialGaugePosition.Top) != 0 &&
  170. (value & RadialGaugePosition.Right) == 0 && (value & RadialGaugePosition.Bottom) == 0) ||
  171. ((value & RadialGaugePosition.Right) != 0 && (value & RadialGaugePosition.Top) != 0 &&
  172. (value & RadialGaugePosition.Left) == 0 && (value & RadialGaugePosition.Bottom) == 0) ||
  173. ((value & RadialGaugePosition.Left) != 0 && (value & RadialGaugePosition.Bottom) != 0 &&
  174. (value & RadialGaugePosition.Right) == 0 && (value & RadialGaugePosition.Top) == 0) ||
  175. ((value & RadialGaugePosition.Right) != 0 && (value & RadialGaugePosition.Bottom) != 0 &&
  176. (value & RadialGaugePosition.Left) == 0 && (value & RadialGaugePosition.Top) == 0)
  177. ))
  178. position = value;
  179. else if (Type == RadialGaugeType.Circle)
  180. position = 0;
  181. }
  182. }
  183. /// <summary>
  184. /// Gets or sets the semicircles offset
  185. /// </summary>
  186. [Category("Appearance")]
  187. public float SemicircleOffsetRatio
  188. {
  189. get { return semicircleOffsetRatio; }
  190. set { semicircleOffsetRatio = value; }
  191. }
  192. #endregion // Properties
  193. #region Constructors
  194. /// <summary>
  195. /// Initializes a new instance of the <see cref="RadialGauge"/> class.
  196. /// </summary>
  197. public RadialGauge() : base()
  198. {
  199. InitializeComponent();
  200. Scale = new RadialScale(this);
  201. Pointer = new RadialPointer(this, Scale as RadialScale);
  202. Label = new RadialLabel(this);
  203. Height = 4.0f * Units.Centimeters;
  204. Width = 4.0f * Units.Centimeters;
  205. semicircleOffsetRatio = type == RadialGaugeType.Semicircle &&
  206. (position == RadialGaugePosition.Left || position == RadialGaugePosition.Right) ? 1.5f : 1;
  207. Type = RadialGaugeType.Circle;
  208. Border.Lines = BorderLines.None;
  209. }
  210. #endregion // Constructor
  211. #region Public Methods
  212. /// <inheritdoc/>
  213. public override void Assign(Base source)
  214. {
  215. base.Assign(source);
  216. RadialGauge src = source as RadialGauge;
  217. Type = src.Type;
  218. Position = src.Position;
  219. }
  220. /// <inheritdoc/>
  221. public override void Draw(FRPaintEventArgs e)
  222. {
  223. IGraphics g = e.Graphics;
  224. float x = (AbsLeft + Border.Width / 2) * e.ScaleX;
  225. float y = (AbsTop + Border.Width / 2) * e.ScaleY;
  226. float dx = (Width - Border.Width) * e.ScaleX - 1;
  227. float dy = (Height - Border.Width) * e.ScaleY - 1;
  228. float x1 = x + dx;
  229. float y1 = y + dy;
  230. Pen pen = e.Cache.GetPen(Border.Color, Border.Width * e.ScaleX, Border.DashStyle);
  231. Brush brush;
  232. if (Fill is SolidFill)
  233. brush = e.Cache.GetBrush((Fill as SolidFill).Color);
  234. else
  235. brush = Fill.CreateBrush(new RectangleF(x, y, dx, dy), e.ScaleX, e.ScaleY);
  236. center = new PointF(x + dx / 2, y + dy / 2);
  237. if (type == RadialGaugeType.Circle)
  238. {
  239. g.FillAndDrawEllipse(pen, brush, x, y, dx, dy);
  240. }
  241. else if (type == RadialGaugeType.Semicircle)
  242. {
  243. float semiOffset = (Width / 16f /2f + 2f) * semicircleOffsetRatio * e.ScaleY;
  244. PointF[] points = new PointF[4];
  245. if (position == RadialGaugePosition.Top)
  246. {
  247. g.FillPie(brush, x, y, dx, dy, -180, 180);
  248. g.DrawArc(pen, x, y, dx, dy, -180, 180);
  249. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, -90 * RAD, center)[0];
  250. points[0] = new PointF(startPoint.X, startPoint.Y - 1 * e.ScaleY);
  251. points[1] = new PointF(startPoint.X, startPoint.Y + semiOffset);
  252. points[2] = new PointF(startPoint.X + dx, startPoint.Y + semiOffset);
  253. points[3] = new PointF(startPoint.X + dx, startPoint.Y - 1 * e.ScaleY);
  254. }
  255. else if(position == RadialGaugePosition.Bottom)
  256. {
  257. g.FillPie(brush, x, y, dx, dy, 0, 180);
  258. g.DrawArc(pen, x, y, dx, dy, 0, 180);
  259. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, 90 * RAD, center)[0];
  260. points[0] = new PointF(startPoint.X, startPoint.Y + 1 * e.ScaleY);
  261. points[1] = new PointF(startPoint.X, startPoint.Y - semiOffset);
  262. points[2] = new PointF(startPoint.X - dx, startPoint.Y - semiOffset);
  263. points[3] = new PointF(startPoint.X - dx, startPoint.Y + 1 * e.ScaleY);
  264. }
  265. else if (position == RadialGaugePosition.Left)
  266. {
  267. g.FillPie(brush, x, y, dx, dy, 90, 180);
  268. g.DrawArc(pen, x, y, dx, dy, 90, 180);
  269. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, 180 * RAD, center)[0];
  270. points[0] = new PointF(startPoint.X - 1 * e.ScaleX, startPoint.Y);
  271. points[1] = new PointF(startPoint.X + semiOffset, startPoint.Y);
  272. points[2] = new PointF(startPoint.X + semiOffset, startPoint.Y - dy);
  273. points[3] = new PointF(startPoint.X - 1 * e.ScaleX, startPoint.Y - dy);
  274. }
  275. else if (position == RadialGaugePosition.Right)
  276. {
  277. g.FillPie(brush, x, y, dx, dy, -90, 180);
  278. g.DrawArc(pen, x, y, dx, dy, -90, 180);
  279. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, -180 * RAD, center)[0];
  280. points[0] = new PointF(startPoint.X + 1 * e.ScaleX, startPoint.Y);
  281. points[1] = new PointF(startPoint.X - semiOffset, startPoint.Y);
  282. points[2] = new PointF(startPoint.X - semiOffset, startPoint.Y - dy);
  283. points[3] = new PointF(startPoint.X + 1 * e.ScaleX, startPoint.Y - dy);
  284. }
  285. if(position != RadialGaugePosition.None)
  286. {
  287. GraphicsPath path = new GraphicsPath();
  288. path.AddLines(points);
  289. g.FillAndDrawPath(pen, brush, path);
  290. }
  291. }
  292. else if (type == RadialGaugeType.Quadrant)
  293. {
  294. float semiOffset = (Width / 16f / 2f + 2f) * semicircleOffsetRatio * e.ScaleY;
  295. if (RadialUtils.IsTop(this) && RadialUtils.IsLeft(this))
  296. {
  297. g.FillPie(brush, x, y, dx, dy, -180, 90);
  298. g.DrawArc(pen, x, y, dx, dy, -180, 90);
  299. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, -90 * RAD, center)[0];
  300. PointF[] points = new PointF[5];
  301. points[0] = new PointF(startPoint.X, startPoint.Y - 1 * e.ScaleY);
  302. points[1] = new PointF(startPoint.X, startPoint.Y + semiOffset);
  303. points[2] = new PointF(startPoint.X + dx / 2 + semiOffset, startPoint.Y + semiOffset);
  304. points[3] = new PointF(startPoint.X + dx / 2 + semiOffset, y);
  305. points[4] = new PointF(startPoint.X + dx / 2 - 1 * e.ScaleX, y);
  306. GraphicsPath path = new GraphicsPath();
  307. path.AddLines(points);
  308. g.FillAndDrawPath(pen, brush, path);
  309. }
  310. else if (RadialUtils.IsBottom(this) && RadialUtils.IsLeft(this))
  311. {
  312. g.FillPie(brush, x, y, dx, dy, -270, 90);
  313. g.DrawArc(pen, x, y, dx, dy, -270, 90);
  314. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, -90 * RAD, center)[0];
  315. PointF[] points = new PointF[5];
  316. points[0] = new PointF(startPoint.X, startPoint.Y + 1 * e.ScaleY);
  317. points[1] = new PointF(startPoint.X, startPoint.Y - semiOffset);
  318. points[2] = new PointF(startPoint.X + dx / 2 + semiOffset, startPoint.Y - semiOffset);
  319. points[3] = new PointF(startPoint.X + dx / 2 + semiOffset, y + dy);
  320. points[4] = new PointF(x + dx / 2 - 1 * e.ScaleX, y + dy);
  321. GraphicsPath path = new GraphicsPath();
  322. path.AddLines(points);
  323. g.FillAndDrawPath(pen, brush, path);
  324. }
  325. else if (RadialUtils.IsTop(this) && RadialUtils.IsRight(this))
  326. {
  327. g.FillPie(brush, x, y, dx, dy, -90, 90);
  328. g.DrawArc(pen, x, y, dx, dy, -90, 90);
  329. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, 90 * RAD, center)[0];
  330. PointF[] points = new PointF[5];
  331. points[0] = new PointF(startPoint.X, startPoint.Y - 1 * e.ScaleY);
  332. points[1] = new PointF(startPoint.X, startPoint.Y + semiOffset);
  333. points[2] = new PointF(startPoint.X - dx / 2 - semiOffset, startPoint.Y + semiOffset);
  334. points[3] = new PointF(x + dx / 2 - semiOffset , y);
  335. points[4] = new PointF(x + dx / 2 + 1 * e.ScaleX, y);
  336. GraphicsPath path = new GraphicsPath();
  337. path.AddLines(points);
  338. g.FillAndDrawPath(pen, brush, path);
  339. }
  340. else if (RadialUtils.IsBottom(this) && RadialUtils.IsRight(this))
  341. {
  342. g.FillPie(brush, x, y, dx, dy, 0, 90);
  343. g.DrawArc(pen, x, y, dx, dy, 0, 90);
  344. PointF startPoint = RadialUtils.RotateVector(new PointF[] { new PointF(x + dx / 2, y), center }, 90 * RAD, center)[0];
  345. PointF[] points = new PointF[5];
  346. points[0] = new PointF(startPoint.X, startPoint.Y + 1 * e.ScaleY);
  347. points[1] = new PointF(startPoint.X, startPoint.Y - semiOffset);
  348. points[2] = new PointF(x + dx / 2 - semiOffset, startPoint.Y - semiOffset);
  349. points[3] = new PointF(x + dx / 2 - semiOffset, y + dy);
  350. points[4] = new PointF(x + dx / 2 + 1 * e.ScaleX, y + dy);
  351. GraphicsPath path = new GraphicsPath();
  352. path.AddLines(points);
  353. g.FillAndDrawPath(pen, brush, path);
  354. }
  355. }
  356. Scale.Draw(e);
  357. Pointer.Draw(e);
  358. Label.Draw(e);
  359. DrawMarkers(e);
  360. if (!(Fill is SolidFill))
  361. brush.Dispose();
  362. if (Report != null && Report.SmoothGraphics)
  363. {
  364. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  365. g.SmoothingMode = SmoothingMode.AntiAlias;
  366. }
  367. }
  368. /// <inheritdoc/>
  369. public override void Serialize(FRWriter writer)
  370. {
  371. RadialGauge c = writer.DiffObject as RadialGauge;
  372. base.Serialize(writer);
  373. if (Type != c.Type)
  374. {
  375. writer.WriteValue("Type", Type);
  376. }
  377. if (Position != c.Position)
  378. {
  379. writer.WriteValue("Position", Position);
  380. }
  381. }
  382. #endregion // Public Methods
  383. }
  384. }