source: trunk/Packages/bgracontrols/bgraknob.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 12.6 KB
Line 
1unit BGRAKnob;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
9 BGRAGradients, BGRABitmap, BGRABitmapTypes;
10
11type
12 TBGRAKnobPositionType = (kptLineSquareCap, kptLineRoundCap, kptFilledCircle,
13 kptHollowCircle);
14 TBGRAKnobValueChangedEvent = procedure(Sender: TObject; Value: single) of object;
15
16 { TBGRAKnob }
17
18 TBGRAKnob = class(TGraphicControl)
19 private
20 { Private declarations }
21 FPhong: TPhongShading;
22 FCurveExponent: single;
23 FKnobBmp: TBGRABitmap;
24 FKnobColor: TColor;
25 FAngularPos: single;
26 FPositionColor: TColor;
27 FPositionMargin: single;
28 FPositionOpacity: byte;
29 FPositionType: TBGRAKnobPositionType;
30 FPositionWidth: single;
31 FSettingAngularPos: boolean;
32 FUsePhongLighting: boolean;
33 FMinValue, FMaxValue: single;
34 FOnKnobValueChange: TBGRAKnobValueChangedEvent;
35 FStartFromBottom: boolean;
36 procedure CreateKnobBmp;
37 function GetLightIntensity: integer;
38 function GetValue: single;
39 procedure SetCurveExponent(const AValue: single);
40 procedure SetLightIntensity(const AValue: integer);
41 procedure SetStartFromBottom(const AValue: boolean);
42 procedure SetValue(AValue: single);
43 procedure SetMaxValue(AValue: single);
44 procedure SetMinValue(AValue: single);
45 procedure SetPositionColor(const AValue: TColor);
46 procedure SetPositionMargin(AValue: single);
47 procedure SetPositionOpacity(const AValue: byte);
48 procedure SetPositionType(const AValue: TBGRAKnobPositionType);
49 procedure SetPositionWidth(const AValue: single);
50 procedure SetUsePhongLighting(const AValue: boolean);
51 procedure UpdateAngularPos(X, Y: integer);
52 procedure SetKnobColor(const AValue: TColor);
53 protected
54 { Protected declarations }
55 procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
56 X, Y: integer); override;
57 procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
58 procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
59 procedure Paint; override;
60 procedure Resize; override;
61 function ValueCorrection(var AValue: single): boolean; virtual; overload;
62 function ValueCorrection: boolean; virtual; overload;
63 public
64 { Public declarations }
65 constructor Create(AOwner: TComponent); override;
66 destructor Destroy; override;
67 published
68 { Published declarations }
69 property Anchors;
70 property CurveExponent: single read FCurveExponent write SetCurveExponent;
71 property KnobColor: TColor read FKnobColor write SetKnobColor;
72 property LightIntensity: integer read GetLightIntensity write SetLightIntensity;
73 property PositionColor: TColor read FPositionColor write SetPositionColor;
74 property PositionWidth: single read FPositionWidth write SetPositionWidth;
75 property PositionOpacity: byte read FPositionOpacity write SetPositionOpacity;
76 property PositionMargin: single read FPositionMargin write SetPositionMargin;
77 property PositionType: TBGRAKnobPositionType
78 read FPositionType write SetPositionType;
79 property UsePhongLighting: boolean read FUsePhongLighting write SetUsePhongLighting;
80 property MinValue: single read FMinValue write SetMinValue;
81 property MaxValue: single read FMaxValue write SetMaxValue;
82 property Value: single read GetValue write SetValue;
83 property OnValueChanged: TBGRAKnobValueChangedEvent
84 read FOnKnobValueChange write FOnKnobValueChange;
85 property StartFromBottom: boolean read FStartFromBottom write SetStartFromBottom;
86 end;
87
88
89procedure Register;
90
91implementation
92
93uses Math;
94
95procedure Register;
96begin
97 {$I bgraknob_icon.lrs}
98 RegisterComponents('BGRA Controls', [TBGRAKnob]);
99end;
100
101{ TBGRAKnob }
102
103procedure TBGRAKnob.CreateKnobBmp;
104var
105 tx, ty: integer;
106 h: single;
107 d2: single;
108 v: TPointF;
109 p: PBGRAPixel;
110 center: TPointF;
111 yb: integer;
112 xb: integer;
113 mask: TBGRABitmap;
114 Map: TBGRABitmap;
115 BGRAKnobColor: TBGRAPixel;
116begin
117 tx := ClientWidth;
118 ty := ClientHeight;
119 if (tx = 0) or (ty = 0) then
120 exit;
121
122 FreeAndNil(FKnobBmp);
123
124 FKnobBmp := TBGRABitmap.Create(tx, ty);
125 center := PointF((tx - 1) / 2, (ty - 1) / 2);
126 BGRAKnobColor := ColorToBGRA(ColorToRGB(KnobColor));
127
128 if UsePhongLighting then
129 begin
130 //compute knob height map
131 Map := TBGRABitmap.Create(tx, ty);
132 for yb := 0 to ty - 1 do
133 begin
134 p := map.ScanLine[yb];
135 for xb := 0 to tx - 1 do
136 begin
137 //compute vector between center and current pixel
138 v := PointF(xb, yb) - center;
139 //scale down to unit circle (with 1 pixel margin for soft border)
140 v.x /= tx / 2 + 1;
141 v.y /= ty / 2 + 1;
142 //compute squared distance with scalar product
143 d2 := v * v;
144 //interpolate as quadratic curve and apply power function
145 if d2 > 1 then
146 h := 0
147 else
148 h := power(1 - d2, FCurveExponent);
149 p^ := MapHeightToBGRA(h, 255);
150 Inc(p);
151 end;
152 end;
153 //antialiased border
154 mask := TBGRABitmap.Create(tx, ty, BGRABlack);
155 Mask.FillEllipseAntialias(center.x, center.y, tx / 2, ty / 2, BGRAWhite);
156 map.ApplyMask(mask);
157 Mask.Free;
158
159 FPhong.Draw(FKnobBmp, Map, 30, 0, 0, BGRAKnobColor);
160 Map.Free;
161 end
162 else
163 begin
164 FKnobBmp.FillEllipseAntialias(center.x, center.y, tx / 2, ty / 2, BGRAKnobColor);
165 end;
166
167end;
168
169function TBGRAKnob.GetLightIntensity: integer;
170begin
171 Result := round(FPhong.LightSourceIntensity);
172end;
173
174function TBGRAKnob.GetValue: single;
175begin
176 Result := FAngularPos * 180 / Pi;
177 if Result < 0 then
178 Result += 360;
179 Result := 270 - Result;
180 if Result < 0 then
181 Result += 360;
182end;
183
184procedure TBGRAKnob.SetCurveExponent(const AValue: single);
185begin
186 if FCurveExponent = AValue then
187 exit;
188 FCurveExponent := AValue;
189 FreeAndNil(FKnobBmp);
190 Invalidate;
191end;
192
193procedure TBGRAKnob.SetKnobColor(const AValue: TColor);
194begin
195 if FKnobColor = AValue then
196 exit;
197 FKnobColor := AValue;
198 FreeAndNil(FKnobBmp);
199 Invalidate;
200end;
201
202procedure TBGRAKnob.SetLightIntensity(const AValue: integer);
203begin
204 if AValue <> FPhong.LightSourceIntensity then
205 begin
206 FPhong.LightSourceIntensity := AValue;
207 FreeAndNil(FKnobBmp);
208 Invalidate;
209 end;
210end;
211
212procedure TBGRAKnob.SetStartFromBottom(const AValue: boolean);
213begin
214 if FStartFromBottom = AValue then
215 exit;
216 FStartFromBottom := AValue;
217 Invalidate;
218end;
219
220procedure TBGRAKnob.SetValue(AValue: single);
221var
222 NewAngularPos: single;
223begin
224 ValueCorrection(AValue);
225 NewAngularPos := 3 * Pi / 2 - AValue * Pi / 180;
226 if NewAngularPos > Pi then
227 NewAngularPos -= 2 * Pi;
228 if NewAngularPos < -Pi then
229 NewAngularPos += 2 * Pi;
230 if NewAngularPos <> FAngularPos then
231 begin
232 FAngularPos := NewAngularPos;
233 Invalidate;
234 end;
235end;
236
237procedure TBGRAKnob.SetMaxValue(AValue: single);
238begin
239 if AValue < 0 then
240 AValue := 0;
241 if AValue > 360 then
242 AValue := 360;
243 if FMaxValue = AValue then
244 exit;
245 FMaxValue := AValue;
246 if FMinValue > FMaxValue then
247 FMinValue := FMaxValue;
248 if ValueCorrection then
249 Invalidate;
250end;
251
252procedure TBGRAKnob.SetMinValue(AValue: single);
253begin
254 if AValue < 0 then
255 AValue := 0;
256 if AValue > 360 then
257 AValue := 360;
258 if FMinValue = AValue then
259 exit;
260 FMinValue := AValue;
261 if FMaxValue < FMinValue then
262 FMaxValue := FMinValue;
263 if ValueCorrection then
264 Invalidate;
265end;
266
267procedure TBGRAKnob.SetPositionColor(const AValue: TColor);
268begin
269 if FPositionColor = AValue then
270 exit;
271 FPositionColor := AValue;
272 Invalidate;
273end;
274
275procedure TBGRAKnob.SetPositionMargin(AValue: single);
276begin
277 if FPositionMargin = AValue then
278 exit;
279 FPositionMargin := AValue;
280 Invalidate;
281end;
282
283procedure TBGRAKnob.SetPositionOpacity(const AValue: byte);
284begin
285 if FPositionOpacity = AValue then
286 exit;
287 FPositionOpacity := AValue;
288 Invalidate;
289end;
290
291procedure TBGRAKnob.SetPositionType(const AValue: TBGRAKnobPositionType);
292begin
293 if FPositionType = AValue then
294 exit;
295 FPositionType := AValue;
296 Invalidate;
297end;
298
299procedure TBGRAKnob.SetPositionWidth(const AValue: single);
300begin
301 if FPositionWidth = AValue then
302 exit;
303 FPositionWidth := AValue;
304 Invalidate;
305end;
306
307procedure TBGRAKnob.SetUsePhongLighting(const AValue: boolean);
308begin
309 if FUsePhongLighting = AValue then
310 exit;
311 FUsePhongLighting := AValue;
312 FreeAndNil(FKnobBmp);
313 Invalidate;
314end;
315
316procedure TBGRAKnob.UpdateAngularPos(X, Y: integer);
317var
318 FPreviousPos, Sign: single;
319begin
320 FPreviousPos := FAngularPos;
321 if FStartFromBottom then
322 Sign := 1
323 else
324 Sign := -1;
325 FAngularPos := ArcTan2((-Sign) * (Y - ClientHeight / 2) / ClientHeight,
326 Sign * (X - ClientWidth / 2) / ClientWidth);
327 ValueCorrection;
328 Invalidate;
329 if (FPreviousPos <> FAngularPos) and Assigned(FOnKnobValueChange) then
330 FOnKnobValueChange(Self, Value);
331end;
332
333procedure TBGRAKnob.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer);
334begin
335 inherited MouseDown(Button, Shift, X, Y);
336 if Button = mbLeft then
337 begin
338 FSettingAngularPos := True;
339 UpdateAngularPos(X, Y);
340 end;
341end;
342
343procedure TBGRAKnob.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer);
344begin
345 inherited MouseUp(Button, Shift, X, Y);
346 if Button = mbLeft then
347 FSettingAngularPos := False;
348end;
349
350procedure TBGRAKnob.MouseMove(Shift: TShiftState; X, Y: integer);
351begin
352 inherited MouseMove(Shift, X, Y);
353 if FSettingAngularPos then
354 UpdateAngularPos(X, Y);
355end;
356
357procedure TBGRAKnob.Paint;
358var
359 Bmp: TBGRABitmap;
360 Center, Pos: TPointF;
361 PosColor: TBGRAPixel;
362 PosLen: single;
363begin
364 if (ClientWidth = 0) or (ClientHeight = 0) then
365 exit;
366 if FKnobBmp = nil then
367 begin
368 CreateKnobBmp;
369 if FKnobBmp = nil then
370 Exit;
371 end;
372 Bmp := TBGRABitmap.Create(ClientWidth, ClientHeight);
373 Bmp.PutImage(0, 0, FKnobBmp, dmSet);
374
375 //draw current position
376 PosColor := ColorToBGRA(ColorToRGB(FPositionColor), FPositionOpacity);
377 Center := PointF(ClientWidth / 2, ClientHeight / 2);
378 Pos.X := Cos(FAngularPos) * (ClientWidth / 2);
379 Pos.Y := -Sin(FAngularPos) * (ClientHeight / 2);
380 if not FStartFromBottom then
381 Pos := -Pos;
382 PosLen := sqrt(Pos * Pos);
383
384 Pos := Pos * ((PosLen - PositionMargin - FPositionWidth) / PosLen);
385 Pos := Center + Pos;
386
387 case PositionType of
388 kptLineSquareCap:
389 begin
390 Bmp.LineCap := pecSquare;
391 Bmp.DrawLineAntialias(Center.X, Center.Y, Pos.X, Pos.Y, PosColor, FPositionWidth);
392 end;
393 kptLineRoundCap:
394 begin
395 Bmp.LineCap := pecRound;
396 Bmp.DrawLineAntialias(Center.X, Center.Y, Pos.X, Pos.Y, PosColor, FPositionWidth);
397 end;
398 kptFilledCircle:
399 begin
400 Bmp.FillEllipseAntialias(Pos.X, Pos.Y, FPositionWidth, FPositionWidth, PosColor);
401 end;
402 kptHollowCircle:
403 begin
404 Bmp.EllipseAntialias(Pos.X, Pos.Y, FPositionWidth * 2 / 3,
405 FPositionWidth * 2 / 3, PosColor, FPositionWidth / 3);
406 end;
407 end;
408
409 Bmp.Draw(Canvas, 0, 0, False);
410 Bmp.Free;
411end;
412
413procedure TBGRAKnob.Resize;
414begin
415 inherited Resize;
416 if (FKnobBmp <> nil) and ((ClientWidth <> FKnobBmp.Width) or
417 (ClientHeight <> FKnobBmp.Height)) then
418 FreeAndNil(FKnobBmp);
419end;
420
421function TBGRAKnob.ValueCorrection(var AValue: single): boolean;
422begin
423 if AValue < MinValue then
424 begin
425 AValue := MinValue;
426 Result := True;
427 end
428 else
429 if AValue > MaxValue then
430 begin
431 AValue := MaxValue;
432 Result := True;
433 end
434 else
435 Result := False;
436end;
437
438function TBGRAKnob.ValueCorrection: boolean;
439var
440 LValue: single;
441begin
442 LValue := Value;
443 Result := ValueCorrection(LValue);
444 if Result then
445 Value := LValue;
446end;
447
448constructor TBGRAKnob.Create(AOwner: TComponent);
449begin
450 inherited Create(AOwner);
451 with GetControlClassDefaultSize do
452 SetInitialBounds(0, 0, CX, CY);
453 FPhong := TPhongShading.Create;
454 FPhong.LightPositionZ := 100;
455 FPhong.LightSourceIntensity := 300;
456 FPhong.NegativeDiffusionFactor := 0.8;
457 FPhong.AmbientFactor := 0.5;
458 FPhong.DiffusionFactor := 0.6;
459 FKnobBmp := nil;
460 FCurveExponent := 0.2;
461 FKnobColor := clBtnFace;
462 FPositionColor := clBtnText;
463 FPositionOpacity := 192;
464 FPositionWidth := 4;
465 FPositionMargin := 4;
466 FPositionType := kptLineSquareCap;
467 FUsePhongLighting := True;
468 FOnKnobValueChange := nil;
469 FStartFromBottom := True;
470 FMinValue := 30;
471 FMaxValue := 330;
472end;
473
474destructor TBGRAKnob.Destroy;
475begin
476 FPhong.Free;
477 FKnobBmp.Free;
478 inherited Destroy;
479end;
480
481end.
482
Note: See TracBrowser for help on using the repository browser.