Draw a circle on the Canvas of a TImage with Delphi Firemonekey - delphi

I am trying draw a circle on the TImage canvas in Delphi 11 Firemonkey using the OnMouseDown event of the TImage component.
I have written the following code:
procedure TForm1.FormCreate(Sender: TObject);
begin
// sets the size of the TBitmap
Image1.Bitmap.SetSize(Round(Image1.Width), Round(Image1.Height));
Image1.Bitmap.Clear(TAlphaColors.White);
end;
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
MyRect: TRectF;
Point:TPointF;
begin
// sets the circumscribed rectangle of the ellipse
Caption:=X.ToString +',' +Y.ToString;
Point:=TPointF.Create(x,y);
//Point:=ClientToScreen(Point);
Point:=Image1.AbsoluteToLocal(Point);
MyRect := TRectF.Create(Point,5,5);
// draws the ellipse on the canvas
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.DrawEllipse(MyRect, 20);
Image1.Bitmap.Canvas.EndScene;
end;
When I run the app and click on the Timage, the circle is drawn as expected, but not at the exact position, it has an offset away from the mouse pointer.
How can I correct this displacement?

The X,Y coordinates provided by the OnMouseDown event are expressed as local coordinates relative to the TImage's client area, they are not expressed as global absolute coordinates. So, you should not be trying to convert Point from absolute coordinates to local coordinates at all, it is already in local coordinates.
You need to remove the call to Image1.AbsoluteToLocal() and just use the coordinates provided as-is, eg:
procedure TForm1.Image1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
MyRect: TRectF;
Point: TPointF;
begin
// sets the circumscribed rectangle of the ellipse
Caption := X.ToString + ',' + Y.ToString;
Point := TPointF.Create(X, Y);
MyRect := TRectF.Create(Point, 5, 5);
// draws the ellipse on the canvas
Image1.Bitmap.Canvas.BeginScene;
Image1.Bitmap.Canvas.DrawEllipse(MyRect, 20);
Image1.Bitmap.Canvas.EndScene;
end;

Related

Is there a way to activate a visible coordinate system on Canvas?

Is there a particular way to see the coordinates(x, y) on a TPaintBox (Canvas) in Delphi? I know that the (0; 0) point is on the left top of the canvas, but i would like to know if we can see the other numbers. Thanks in advance.
You can see the coordinates on caption of form by this way;
procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
Self.Caption := IntToStr(X) + 'x' + IntToStr(Y);
end;
When you leave PaintBox1 the caption will show you last cordinates.

Clear lines drawn with TCanvas

I am using TCanvas to draw a blue line whenever the left mouse button is clicked, and a red line whenever the right mouse button is clicked. Currently whenever I click another line is drawn on the chart. What I want to do is to clear the old line, and then draw a new line.
Below is some sample code.
The code for the onClick event
procedure TForm2.Chart1ChartClick(Sender: TJvChart; Button: TMouseButton;
Shift: TShiftState; X, Y, ChartValueIndex, ChartPenIndex: Integer;
var ShowHint, HintFirstLineBold: Boolean; HintStrs: TStrings);
begin
if Button = mbLeft then
begin
canvas.pen.color := clblue;
PlotHorizontal(X, Y);
end
else if Button = mbRight then
begin
Canvas.Pen.color := clred;
PlotHorizontal(X, Y);
end
end;
The PlotHorizontal procedure
procedure TForm2.PlotHorizontal(X, Y : integer);
begin
with canvas do
begin
// draw the horizontal line
MoveTo(X, Y);
// without the 4 the line doesn't seem to reach the end of the graph
LineTo(X, Chart1.Options.XStartOffset -4);
MoveTo(X, Y);
LineTo(X, Chart1.Options.XStartOffset +
+ Chart1.Options.Yend);
end;
end;
I was able to get it working, by saving the old X, and Y values for where the lines were draw. Then when the mouse was clicked again, I refreshed the chart, and redrew the line again.

TeeChart Series OnMouseEnter event

I am using the version of TeeChart that ships with Rad Studio XE3.
TeeChart provides a TChartSeries event which fires when the mouse pointer moves over a series line. I use this event to display the name of the series under the pointer.
The problem is, give a series line 1 pixel wide, it’s difficult to get the pointer exactly over the line. Is there some way to add ‘padding’ to the event so it fires X number of pixels to each side of the line?
Or is there some other way to accomplish this?
I'm adding a new property to Line (TLineSeries) and FastLine (TFastLineSeries) classes to accomplish this.
Series1.ClickTolerance := 4; // <-- number of pixels around mouse XY
The default value is zero (mouse XY should be exactly over the line), like the current behavior.
As a workaround, if you are using TLineSeries, pointers can be displayed at line point positions, and the internal "clicked" function will consider pointer size:
Series1.Pointer.Visible:=True;
And for more custom control, the code below is very similar to the internal code use to detect mouse clicks. The Tolerance constant specifies the number of extra pixels to consider "in the line".
procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
const
Tolerance=4;
var Clicked,
t : Integer;
Position,
P,Old : TPoint;
begin
Clicked:= -1;
Position.X:=X;
Position.Y:=Y;
for t:=Series1.FirstValueIndex to Series1.LastValueIndex do
begin
P.X:=Series1.CalcXPos(t);
P.Y:=Series1.CalcYPos(t);
if t>Series1.FirstValueIndex then
if PointInLine(Position,P.X,P.Y,Old.X,Old.Y,Tolerance) then
begin
Clicked:=t;
break;
end;
Old:=P;
end;
if Clicked = -1 then
Caption:=''
else
Caption:=IntToStr(Clicked);
end;
You can use the PointInLineTolerance function to check it at OnMouseMove event.
However, you have to loop the series points manually to transform the series values into pixels and pass them to this function.
uses Series;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=false;
for i:=0 to 5 do
Chart1.AddSeries(TLineSeries).FillSampleValues;
end;
procedure TForm1.Chart1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var series, valueIndex: Integer;
P0, P1: TPoint;
begin
Chart1.Draw;
for series:=0 to Chart1.SeriesCount-1 do
with Chart1[series] do
for valueIndex:=FirstValueIndex to LastValueIndex-1 do
begin
P0.X:=CalcXPos(valueIndex);
P0.Y:=CalcYPos(valueIndex);
P1.X:=CalcXPos(valueIndex+1);
P1.Y:=CalcYPos(valueIndex+1);
if PointInLineTolerance(Point(X, Y), P0.X, P0.Y, P1.X, P1.Y, 5) then
begin
Chart1.Canvas.TextOut(X+5,Y-10,'Series ' + IntToStr(series));
exit;
end;
end;
end;

Move TRichEdit Caretpos

is there a way to change the caret position in pixel?
i would like to move the care pos everytime i move the mouse mouse.
like:
Onmousemove:
MoveCaretPos(X, Y);
No, you cannot set the position of the caret in a specific point, instead you must set the caret in a character position. To do this you must use the EM_CHARFROMPOS message to retrieve the closest character to a specified point and then set the value returned to the SelStart property.
Check this sample
procedure TForm1.RichEdit1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
APoint : TPoint;
Index : Integer;
begin
APoint := Point(X, Y);
Index := SendMessage(TRichEdit(Sender).Handle,EM_CHARFROMPOS, 0, Integer(#APoint));
if Index<0 then Exit;
TRichEdit(Sender).SelStart:=Index;
end;

Canvas/Bitmap scrolling question

I'm trying to make a small game based on the canvas in Delphi. Basically, I'd like to make a fairly large bitmap ( 3000x3000, for example ), then load it into the canvas, and being able to scroll right/left/up/down just like an ordinary image viewer, however I can't seem to find what I'm looking for. Any ideas?
Load the image to an off-screen TBitmap object. Then, OnPaint, or whenever is suitable in your particular application, use BitBlt or Canvas.Draw to draw a rectangular subimage of the TBitmap onto the canvas. The subpart should start at (X, Y) on the TBitmap and have a width and height equal to ClientWidth and ClientHeight of the form, respectively.
Now, respond to keyboard events. Write a FormKeyDown event handler, and listen to Key = VK_LEFT, Key = VK_RIGHT, Key = VK_UP, and Key = VK_DOWN (use a case statement). When you detect such a key being pressed, increase/decrease X or Y, as appropriate, and paint the scene again using this starting point.
You can also respond to the MouseDown, MouseMove, and MouseUp events to scroll using the mouse. Either you can use the middle one only (MouseMove): You can check if the cursor is near an edge of the form, and if so, scroll in this direction smoothly (using a TTimer, for instance). Alternatively, you can set a FMouseDown flag to true in MouseDown, and reset it to false in MouseUp. Then, in MouseMove, scroll the bitmap by a delta X-XOld in the x direction if FMouseDown is true, and a delta Y-YOld in the y direction. (Here, X and Y are parameters of the MouseMove event handler; (X, Y) is the current position of the cursor.) The MouseMove procedure should end with
XOld := X;
YOld := Y;
no matter if FMouseDown is on or off.
I had the same problem. My Bitmap is about 5000x5000 pixel, loaded into an Timage of 500x500 pixels.
I wrote a code to move the bitmap arround in the Timage, and it cant go out of the "borders"
AlteMausPos is declared in Form1 var at the beginning.
Kerzenbitmap is your bitmap that contains the 5000x5000 picture.
MausPosDifferenz contains the absolut amount of pixels(x,y) you have moved your mouse while mousekey down.
Then it checks if everything is in Range of the bitmap before copying it with CopyRect.
It took some time for my brain to find out that the best way to copy the rect ist to use the absolut changed mouseposition.
procedure Form1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var picLimits: Tpoint;
begin
AlteMausPos.X := X;
AlteMausPos.Y := Y;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var SourceRect, DestRect: TRect;
begin
var tempBMP:= Tbitmap.Create;
MausPosDifferenz.X := MausPosDifferenz.X+ (AlteMausPos.X- X);
MausPosDifferenz.Y := MausPosDifferenz.Y+ (AlteMausPos.Y- Y);
if MausPosDifferenz.X >= Kerzenbitmap.Width- Image1.Width then MausPosDifferenz.X := Kerzenbitmap.Width-Image1.Width;
if MausPosDifferenz.X < 0 then MausPosDifferenz.X:=0;
if MausPosDifferenz.Y >= Kerzenbitmap.Height-Image1.Height then MausPosDifferenz.Y := Kerzenbitmap.Height-Image1.Height;
if MausPosDifferenz.Y < 0 then MausPosDifferenz.Y:=0;
SourceRect:= Rect( MausPosDifferenz.X, MausPosDifferenz.Y, Image1.Width+ MausPosDifferenz.X, Image1.Height+ MausPosDifferenz.Y);
DestRect:= Rect( 0,0, Image1.Width, Image1.Height);
tempBMP.Assign(Kerzenbitmap);
TempBMP.Canvas.CopyRect(DestRect, Kerzenbitmap.Canvas, SourceRect);
Image1.Picture.Assign(tempBMP);
tempBMP.Free;
end;

Resources