With this code I want to draw a rectangle:
procedure TForm1.Button1Click(Sender: TObject);
var rectangle:Trect;
begin
fx:=400;
fy:=400;
sc1:=base/fx;
sc2:=altezza/fy;
sc:=max(sc1, sc2);
lx:=fx*sc;
ly:=fy*sc;
xc:=base/2;
yc:=altezza/2;
x1:=xc-(lx/2); x2:=xc+(lx/2); y1:=yc-(ly/2); y2:=yc+(ly/2);
panel1.Repaint;
panel1.Canvas.Brush.color:= clblack;
panel1.Canvas.line((panel1.width div 2),0,(panel1.Width div 2), panel1.Height);
panel1.Canvas.line(0,(panel1.height div 2), panel1.Width,(panel1.Height div 2));
panel1.canvas.brush.style:=bsclear;
Rectangle:=rect(x1, y1, x2, y2);
end;
But there is a problem because I have to use only integer values.
Is it possible to use real values for drawing a rectangle with TCanvas?
The simple answer is no. Graphic devices as represented by TCanvas use a coordinate system with integral coordinates. If your coordinates are real values then you need to use some form of mapping between your coordinate system and the integral device coordinates.
However, in this instance it looks like it's not that complex. You don't need real valued coordinates per se. You only have real values because you used real division. Perhaps all you need to do is use integer division, div, rather than real division. Or perhaps you would prefer Round.
A bigger problem is that your code is in the wrong place. You cannot paint in a button handler. Windows will not remember what you painted. The next time the window is invalidated it will ask the panel to refresh itself, and your rectangle will be gone. Painting code needs to be inside an overriden Paint method or equivalent. Perhaps you need a paint box control.
Related
I am making some components for my own use, i am trying to make a "riff generator" for making tunes in midi format. Therefore i am writing a pianoroll editor (like in fl studio etc).
The problem im having is drawing relative to the scrollbar positions. I am trying it for two days now, but i cant seem to figure out how to do this. I already added variables and procedures for setting the scroll positions, but i dont understand how i can draw up/down and sideways when i scroll.
I added the code i have so far, i am using the GDI+ unit from Erik Bilsen (www.bilsen.com/gdiplus) for drawing.
If anyone can look at my code, and help me to get the drawing right that would be awesome! I will release this and other components as opensource when i finish, so other people will be able to use these components too.
The component code: https://pastebin.com/562yfDvu
FScrollPosX : Integer;
FScrollPosY : Integer;
FScrollMaxX : Integer;
FScrollMaxY : Integer;
FOldScrollX : Integer;
FOldScrollY : Integer;
Some typical calculations
Scrollbar range
Given Virtual extent (VirtExt) in pixels
Given Viewport extent (VPExt) in pixels
Scrollbar range = VirtExt - VPExt (to leave the last part visible in the viewport)
Object position in ViewPort with a given scroll position
Given Object.pos (ObjPos) (in virtual space)
Given Sroll position (ScrPos)
Position in viewport: ObjPos - ScrPos
Object visibility filter
Visible if (OPos >= ScrPos) and (OPos < ScrPos+VPExt)
I want to take a screenshot of my page and put the result into a bitmap, Because there is a scrollbar on the page, i have to take several screenshots, and i want to merge those bitmaps.
if have used this code to make a screenshot and save it: Take a screenshot of a particular area in Delphi 7
i used the code to merge them from this page http://www.delphigroups.info/2/8/309463.html
if i copied it directly it would result in the first image being used, and i white rectangle for the second. so i tried to change it a little bit, and now i'm getting both images in one file.
This is the code i use to concatenate the bitmaps:
function ConcatenateBitmaps(const MainBitmap: TBitmap; const BitmapToAdd:
TBitmap): TBitmap;
begin
Result := MainBitmap;
If BitmapToAdd.Width > MainBitmap.Width then
Result.Width := BitmapToAdd.Width;
Result.Height := MainBitmap.Height + MainBitmap.Height;
Result.Canvas.CopyRect(
Rect(0,MainBitmap.Height,BitmapToAdd.Width,BitmapToAdd.Height),
BitmapToAdd.Canvas,
Rect(0,0,BitmapToAdd.Width,BitmapToAdd.Height)
);
end;
The problem is that te second image is being flipped, vertical and horizontal;
What am i doing wrong here?
EDIT:
An example of the result, the first image is good, the second image is flipped:
as i see now, my description was wrong, it's horizontaly mirrored, and verticaly flipped
Cause and quickfix:
The problem is in this part:
Rect(0,MainBitmap.Height,BitmapToAdd.Width,BitmapToAdd.Height)
You make a rectangle of which the top is the total height of the resulting image, and the bottom is the height of the bitmap to add. So this rectangle is basically inverted (its bottom is above its top).
And it's likely deformed as well, since the height of this rectangle is not the height of the bitmap to add.
The quickfix would be:
Rect(0,Result.Height- BitmapToAdd.Height,BitmapToAdd.Width,Result.Height)
Other issues and confusion:
But I think the cause of your confusion is because you think that Result and MainBitmap are two different bitmaps, while actually they are both references to the same bitmap. The assignment you do in the beginning just copies the reference, not the actual TBitmap object.
In addition, you mix up 'height' and 'bottom'. TRect expects you to set top and bottom coordinates, not top and height. This, together with the previous issue, causes not only that the bitmap is upside down, but also that it will be stretched, and partially covering the previous images. The more images you add, the more clear that effect will be.
Personally I think it's way more efficient to modify the existing bitmap in this scenario, mainly because you would otherwise have to clean up your old bitmap all the time, plus that you have a function that magically creates bitmaps. You get the question of ownership of the bitmap objects, and with that, the risk of memory leaks, which is not good, especially when dealing with large bitmaps.
My suggested version:
So, I would just make it a procedure, where the first bitmap is modified by adding the second bitmap to it.
In the version below, I also used Canvas.ClipRect, which is for a bitmap essentially the bounding rectangle of the bitmap. And then I used OffsetRect to 'move' this rectangle(increasing its top Y and bottom Y).
By doing this in a separate variable, you can have a relatively clean version compared to the quick fix I presented above, because you can use the dimensions of MainBitmap before actually modifying it.
procedure AppendBitmap(const MainBitmap: TBitmap; const BitmapToAdd:
TBitmap);
var
TargetRect: TRect;
begin
// Widen the main bitmap if needed
if BitmapToAdd.Width > MainBitmap.Width then
MainBitmap.Width := BitmapToAdd.Width;
// Set TargetRect to the right size
TargetRect := BitmapToAdd.Canvas.ClipRect;
// And then to the right position
OffsetRect(TargetRect, 0, MainBitmap.Height);
// Make room for the bitmap to add
MainBitmap.Height := MainBitmap.Height + BitmapToAdd.Height;
// Draw it in the created space
MainBitmap.Canvas.CopyRect(
TargetRect,
BitmapToAdd.Canvas,
BitmapToAdd.Canvas.ClipRect
);
end;
And if you like, you can make a wrapper function with the signature of the original, that creates a copy of the main image and returns that. Note though, that MainBitmap and the result of this function are no longer the same bitmap, and you have to make sure to properly free both of them when you're done.
function ConcatenateBitmaps(const MainBitmap: TBitmap; const BitmapToAdd:
TBitmap): TBitmap;
begin
Result := TBitmap.Create;
Result.Assign(MainBitmap);
AppendBitmap(Result, BitmapToAdd);
end;
PS: I like questions like this from which I learn something. I never realized you could flip an image by flipping the rect passed to CopyRect. :D
I want to get the Left,Top coordinates of a control regarding to the container form. The target control may be, at the same time, inside of any number of other containers such as TPanels and TGroupBoxes. That means that to get the target control origin, the code should have to take into account the Left,Top coordinates of all the other containers + the Left,Top coordinates of the target control itself. Instead, I'm using a second approuch, wich consist in using the ClientToScreen function to get the Left,Top screen coordinates of the target control and after that, subtracting the Left,Top coordinates of the form. Saddly this approach is not working. I'm attaching an image that clarifies my thinkings and has the actual code I used to calculate the desired coordinates. I appreciate any help on this.
#VitaliyG's answer shows how to convert the coordinates of the control's absolute top-left corner to Form-relative client coordinates. If you want to convert the coordinates of the top-left corner of the control's client area instead, you can pass the control's ClientOrigin property to the Form's ScreenToClient() method:
function GetControlClienOrigin(const aControl: TControl: const aForm: TForm): TPoint;
begin
Result := aForm.ScreenToClient(aControl.ClientOrigin);
end;
If the control in question is a TWinControl descendant, an alternative is to use the Win32 API MapWindowPoints() function instead:
function GetControlClientOrigin(const aControl: TWinControl: const aForm: TForm): TPoint;
var
Pt: TPoint;
begin
Pt := Point(0, 0);
SetLastError(0);
if MapWindowPoints(aControl.Handle, aForm.Handle, Pt, 1) = 0 then
CheckOSError(GetLastError);
Result := Pt;
end;
Try using ClientToParent and specify form as Parent parameter.
You have to pass coords relative to control, so Top, Left of the control will be at control's (0,0)
Control.ClientToParent(TPoint.Create(0,0), Form)
So these 3 statements return the same, wanted TPoint:
aControl.ClientOrigin - aForm.ClientOrigin;
aControl.ClientToParent(Point(0,0), aForm);
aForm.ScreenToClient(aControl.ClientOrigin);
I have a line identify by x1,y1,x2,y2 which are double values. Then I have
several graphical objects (Let's name the class TShape) which cordinates
are Left, Top, Right, Bottom: double. Only Top and Left properties are writable
value. When dragging the TShape around the top and left values are updated.
I am using a function to discovery when TShape is near a Line. The function
definition is:
function NearLine(const Target: TPoint; X1, Y1, X2, Y2: double; Off: integer = 5): boolean;
NearLine returns true if point specified by Target is near the line specified by Point1
and Point2. The point must be at the distance specified by Off.
I use the function with Off = 0;
In my implementation Target is the center of the TShape which I keep updated
calculating it from Top and Left properties. Because Target
is TPoint I do:
1-
CPoint.X := Trunc(Center.X);
CPoint.Y := Trunc(Center.Y);
2-
and when the function NearLine above is true I force the mouse to release with:
3-
Mouse_Event(MOUSEEVENTF_ABSOLUTE or MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
1,2 an 3 are called within an event UpdateMove which is called while
dragging the shape.
This allow me to "stick" the TShape almost near the line however it's not
exactly what I want to achive... obviously there is an error due the Trunc function.
The error is:
deltaX := Frac(Center.X);
deltaY := Frac(Center.Y);
After releasing the mouse programmatically how I can force all the center
therefore all the shapes to be perfectly lined (collinear) with the
line?
Any help? :(
You're asking the question wrong, that's why you cant' see the answer your self. If 3 points aren't collinear, you're not going to "force" them collinear unless you change the laws of math and/or physics.
What you probably want is to find a point on the line defined by two points that's closest to your point of reference. That's pretty simple geometry: The closest point is as at the "foot" of a perpendicular drawn from your third point to the line defined by the first two! You can solve that using the Pythagoran theory alone, you don't even need fancy analytic geometry.
I'm writing my Delphi TGraphicControl paint procedure.
I create a canvas and I stretchdraw it onto the graphic area. It works well.
Then I repeat this with another Stretchdraw onto the graphic area but it is drawn in the area of the first Stretchdraw and not onto the graphic area as I direct it.
Is there a way that can place both stretchdraws beside each other in the TGraphicControl's canvas?
TCanvas.StretchDraw paints a graphic onto a canvas in a given rectangular area. The rectangle should, but does not need to be, within the bounds of the canvas. The owner of the canvas determines those bounds. In your case, it sounds like the canvas owner is the TGraphicControl object.
If you want two graphics to be painted beside each other, then the TRect you use to draw the first graphic should represent a rectangle that is adjacent to the TRect you use for the second graphic. You haven't shown any code yet, so it's hard to tell what's going wrong.
If you use the same TRect variable for both calls to StretchDraw, then you need to make sure you modify the rectangle's position between the calls — change the Left property, for starters.
For example:
var
r: TRect;
begin
r := ClientRect;
// First rectangle takes up left half of control
r.Right := r.Right div 2;
Canvas.StretchDraw(r, graphic1);
// Shift the rectangle to the right half
r.Left := r.Right;
r.Right := ClientRect.Right;
Canvas.StretchDraw(r, graphic2);
end;