Scaling the TRotLayer - delphi

I'm working on graphics32. And I'm using the given component from its examples, the TRotLayer. Basing on the example (Examples/Layers/RotLayer_Ex), the created RotLayer only scales together with the ImgView. If ImgView.Bitmap is not assigned, the RotLayer doesn't scale. So I tinkered the source code, changing it's behavior. I changed the TRotLayer.AdjustTransformation procedure. Here's what I did.
procedure TRotLayer.AdjustTransformation;
var
ScaleX, ScaleY,
ShiftX, ShiftY: Single;
begin
Transformation.Clear;
Transformation.Translate(-BitmapCenter.X, -BitmapCenter.Y);
Transformation.Rotate(0, 0, Angle);
Transformation.Translate(Position.X, Position.Y);
Transformation.Scale(Scale.X, Scale.Y);
Transformation.Translate(Shift.X, Shift.Y);
// if Scaled and Assigned(LayerCollection) then
// with LayerCollection do
// begin
// GetViewportScale(ScaleX, ScaleY);
// GetViewportShift(ShiftX, ShiftY);
// Transformation.Scale(ScaleX, ScaleY);
// Transformation.Translate(ShiftX, ShiftY);
// end;
end;
I just ommitted the restriction and simply executed the .Scale procedure. I passed values for Scale.X and Scale.Y and it worked as I expected. The image was resized but now my problem is the positioning. The image moves up or down if I scale it's height then it moves right or left if I scale it's width. I just wanted it to resize and just stay in it's original position. I noticed that the function .Translate can possibly fix my problem but I don't know what to pass in the parameters. Or I don't know how to compute the values to pass.
Anyone can help me with this problem. Thanks.

Pseudo code:
Calculate the original bounds:
Transformation.SrcRect := FloatRect(0, 0, Old.Width, Old.Height);
Shift the origin to the center of that bounds:
Transformation.Translate(-0.5 * Old.Width, -0.5 * Old.Height);
Rotate around the new origin:
Transformation.Rotate(0, 0, Degrees);
Scale
Calculate the new bounds:
New.Bounds := Transformation.GetTransformedBounds;
Shift the origin back to (0, 0) of the new bounds:
Transformation.Translate(0.5 * New.Width, 0.5 * New.Height);
You might also take a look at Rotate bitmap by real angle for a Graphics32 example (without scaling).

Related

Font orientation in TDirect2DCanvas is not working?

I need to draw angled text on TDirect2DCanvas, but no success.
procedure TForm1.FormPaint(Sender: TObject);
var
LCanvas: TDirect2DCanvas;
const
myText = 'Kikimor';
begin
LCanvas := TDirect2DCanvas.Create(Canvas, ClientRect);
LCanvas.BeginDraw;
try
LCanvas.Font.Orientation := 90;
LCanvas.TextOut(100,100,myText);
finally
LCanvas.EndDraw;
LCanvas.Free;
end;
end;
No matter what angle I give for orientation, it always draws a straight text.
Is orientation not working or I need to do something else?
Setting TDirect2DCanvas.Font.Orientation does not have any effect (most likely not implemented, sorry, no time to debug). Direct2D wrapper supplied in Delphi is very basic.
To achieve your goal, apply transformation by hand:
procedure TForm1.FormPaint(Sender: TObject);
var
LCanvas: TDirect2DCanvas;
currentTransform: TD2D1Matrix3x2F;
ptf: TD2DPoint2f;
const
myText = 'Kikimor';
begin
LCanvas := TDirect2DCanvas.Create(self.Canvas, ClientRect);
LCanvas.BeginDraw;
try
// backup the current transformation
LCanvas.RenderTarget.GetTransform(currentTransform);
ptf.x:= 100.0; ptf.y:= 100.0; //rotation center point
// apply transformation to rotate text at 90 degrees:
LCanvas.RenderTarget.SetTransform(TD2D1Matrix3x2F.Rotation(90, ptf));
// draw the text (rotated)
LCanvas.TextOut(100, 100, myText);
// restore the original transform
LCanvas.RenderTarget.SetTransform(currentTransform);
finally
LCanvas.EndDraw;
LCanvas.Free;
end;
end;
For more extensive information/effects you can look at:
Drawing text using the IDWriteTextLayout.Draw()
The whole Direct2D category at the same site is also interesting (use Google Translate).
For those using C++ Builder I got this to work:
#include <Vcl.Direct2D.hpp>
// needed for the D2D1::Matrix3x2F::Rotation transform
#ifdef _WIN64
#pragma comment(lib,"D2D1.a")
#else
#pragma comment(lib,"D2D1.lib")
#endif
TD2DPoint2f point; // rotation centre
point.x = 100.0;
point.y = 100.0;
canvas_2d->RenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(90, point));
canvas_2d->TextOut(100, 100, text);
// restore 0 rotation afterwards
canvas_2d->RenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(0, point));
Note that trying to use GetTransform like in the Delphi version causes an exception, so I cleared the transform by passing it a new one with zero rotation, there may be a better way to do this.
The pragma is needed due to a link error, see this answer for details.

UIViews with subviews: calculating position when scaling

I have a view that I draw using Core Graphics, which in this example is a segmented circle. The user can touch the circle to create a point along its circumference; this creates a subview on the UIView that contains the circle graphic.
Then I've implemented a pinch-zoom gesture which causes the circle to redraw to its new size. I've seen most implementations of pinch zoom use transform properties, but I've chosen to redraw because it's all vectors and gives a clean result.
My problem is repositioning the point views. I calculate the required position of those points based on the scale of the parent view: as it changes I update the x/y coords of the point views. However, it seems there are some precision issues: as the circle shape size increases, the points drift so they aren't right on the line anymore. Here's a couple examples:
This is where the circle is at 100% scale. Note the perfect positioning of that black point. But when you zoom in...
The point drifts off-line.
And here's some code. I derive the new size of the circle from the pinch gesture's scale (I modify if a bit to constrain and slow it down for UI purposes, so that's deltaScale) and then draw it like so:
let currentSize = self.shape!.bounds.size
let newSize = CGSize(width: self.originalSize.width * deltaScale, height: self.originalSize.height * deltaScale)
self.shape?.frame.size = newSize
self.shape?.center = self.originalCentre!
self.shape?.shapeSize = newSize
self.shape?.setNeedsDisplay()
As the pinch-zoom gesture completes, I calculate the factor:
let xScale = Double(newSize.width) / Double(currentSize.width)
let yScale = Double(newSize.height) / Double(currentSize.height)
self.points = self.points.map{(thisPoint) -> UIView in
thisPoint.center = CGPoint(x: Double(thisPoint.center.x) * xScale, y: Double(thisPoint.center.y) * yScale)
return thisPoint
}
(I was using CGFloats, but switched to Doubles in the hope that it would give me the precision I needed. Alas.)
You're accumulating roundoff errors. This is getting executed repeatedly:
thisPoint.center = CGPoint(x: Double(thisPoint.center.x) * xScale, y: Double(thisPoint.center.y) * yScale)
Repeating any calculation of the form 'x=f(x)' with anything less than unlimited precision will result in drift.
Trick is to not have 'thisPoint.center' on both sides of the equal sign. Best way to do that is to have thisPoint.center be a pure function of some other state. Commenter suggested storing desired angle, that would work well. Then you could do:
thisPoint.center = f(thisPoint.someRadians), where 'f' converts from polar to rectangular coordinates, factoring in the scale of the circle.

Direct3D9 fullscreen app - deformed renderering

I have been hardly coding on a Direct3D9 based game. Everything went excellent util I hit a big problem. I created a class that wraps the process of loading a mesh from a .x file. I successfully loaded a cube with only one face visible. In theory, that face should look like a square but it is actually rendered as a rectangle. I am quite sure that there is something wrong with the D3DPRESENT_PARAMETERS structure. Down bellow are only the most important lines of my application's initialization.
First part to be created is the focus window:
HWND hWnd = CreateWindowEx(0UL, L"NewFrontiers3DWindowClass", Title.c_str(), WS_POPUP | WS_EX_TOPMOST, 0, 0, 1280, 1024, nullptr, (HMENU)false, hInstance, nullptr);
Then I fill out the D3DPRESENT_PARAMETERS structure.
D3DDISPLAYMODE D3DMM;
SecureZeroMemory(&D3DMM, sizeof(D3DDISPLAYMODE));
if(FAILED(hr = Direct3D9->GetAdapterDisplayMode(Adapter, &D3DMM)))
{
// Error is processed here
}
PresP.BackBufferWidth = D3DMM.Width;
PresP.BackBufferHeight = D3DMM.Height;
PresP.BackBufferFormat = BackBufferFormat;
PresP.BackBufferCount = 1U;
PresP.MultiSampleType = D3DMULTISAMPLE_NONE;
PresP.MultiSampleQuality = 0UL;
PresP.SwapEffect = D3DSWAPEFFECT_DISCARD;
PresP.hDeviceWindow = hWnd;
PresP.Windowed = false;
PresP.EnableAutoDepthStencil = EnableAutoDepthStencil;
PresP.AutoDepthStencilFormat = AutoDepthStencilFormat;
PresP.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
PresP.FullScreen_RefreshRateInHz = D3DMM.RefreshRate;
PresP.PresentationInterval = PresentationInterval;
Then the Direct3D9 device is created, followed by the SetRenderState functions.
Next, the viewport is assigned.
D3DVIEWPORT9 D3D9Viewport;
SecureZeroMemory(&D3D9Viewport, sizeof(D3DVIEWPORT9));
D3D9Viewport.X = 0UL;
D3D9Viewport.Y = 0UL;
D3D9Viewport.Width = (DWORD)D3DMM.Width;
D3D9Viewport.Height = (DWORD)D3DMM.Height;
D3D9Viewport.MinZ = 0.0f;
D3D9Viewport.MaxZ = 1.0f;
if(FAILED(Direct3D9Device->SetViewport(&D3D9Viewport)))
{
// Error is processed here
}
After this initialization, I globally declare some parameters that will be used later.
D3DXVECTOR3 EyePt(0.0f, 0.0f, -5.0f), Up(0.0f, 1.0f, 0.0f), LookAt(0.0f, 0.0f, 0.0f);
D3DXMATRIX View, Proj, World;
The update function looks like this:
Mesh.Render(Direct3D9Device);
D3DXMatrixLookAtLH(&View, &EyePt, &LookAt, &Up);
Direct3D9Device->SetTransform(D3DTS_VIEW, &View);
D3DXMatrixPerspectiveFovLH(&Proj, D3DX_PI/4, 1.0f, 1.0f, 1000.f);
Direct3D9Device->SetTransform(D3DTS_PROJECTION, &Proj);
D3DXMatrixTranslation(&World, 0.0f, 0.0f, 0.0f);
Direct3D9Device->SetTransform(D3DTS_WORLD, &World);
The device is not a null pointer.
I recently realized that there is no difference between declaring and setting up a view port and not doing so.
If there is anybody who can point me to the right answer, please help me solve this annoying problem.
If you don't set any transformation matrices, so the identity transformation is applied to your mesh, then face of the cube will be stretched to the same shape of the viewport. If your viewport isn't square (eg. it's the same size as the screen) then your cube's face also won't be square.
You can use a square viewport to workaround this problem, but that will limit your rendering to just that square on the screen. If you want to render to the entire screen you'll need to set a suitable projection matrix. You can calculate a normal perspective perspective matrix using D3DXMatrixPerspectiveFovLH. If you want an orthogonal perspective, where everything is the same size regardless of the distance from the camera, then use D3DXMatrixOrthoLH to calculate the perspective matrix. Note that if you use your viewport's width and height with the later function it will shrink your cube. A unit size cube will be rendered as a single pixel on the screen. You can either use a world or view transform to scale it up again, or use something like width/height and 1 as your width and height parameters to D3DXMatrixOrthoLH.
If you go with D3DXMatrixPerspectiveFovLH then you want something like this:
D3DXMatrixPerspectiveFovLH(&Proj, D3DX_PI/4, (double) D3DMM.Width / D3DMM.Height,
1.0f, 1000.f);
I think your problem not in D3DPP parameters but in your projective matrix. If you use D3DXMatrixPerspectiveFovLH, check aspect ratio to be 1280 / 1024 = 1.3333f

Drawing on the canvas with a constant component in Delphi

Is there a way to draw in a canvas with a layout. I mean that I have an object that moves and constant axis. Everybody knows when the object moves from point A to point B we should fill the rectangle by some color. One attentive user may notice that image blinks.
One way: If I have a constant canvas with a set of objects like axis, grids etc... and another one with the moving objects.
I can combine them like
SumCanvas.Pixels[i, j] = ConstCanvas.Pixels[i, j] + MovingCanvas.Pixels[i, j]
filling only the MovingCanvas.
Maybe there is another way?
UPD
A piece of code:
Self is inherited from.
// ...
Canvas.Rectangle(-1, -1, Self.Width, Self.Height);
// Fill all area of canvas by
// clWhite. This is a reason of an image blinking.
// ...
Shape.position := position; // the coordinate of permanently moving object;
// ...
{The constant part}
AddGrid; // drawing a constantCanvas (axis and grids);
for Shape in FListOfShapes do // TListOfShapes = TList<IShape>
shape.draw; // a set of another shape with constant coordinates.
// ...

Scaling sprites (not textures) for target viewport size/device in MonoGame

When you have to display a series of visual components (sprites) within the context of a game each taking a literal height and width that needs to be relative to the height & width of the Viewport (not necessarily aspect ratio) of the target device:
Is there a scaling class to help come up with scaling ratio in a dynamic fashion based on current device viewport size?
Will I need to roll my own scaling ratio algorithm?
Any cross platform issues I should be aware of?
This is not a question relating to the loading of assets based on target device nor is it a question of how to perform the scaling of the sprite (which is described here: http://msdn.microsoft.com/en-us/library/bb194913.aspx), rather a question of how to determine the scale of sprites based on view port size.
You can always create your own implementation of scaling.
For example, the default target viewport dimensions are:
const int defaultWidth = 1280, defaultHeight = 720;
And your current screen dimensions are 800×600, which gives you a (let's use a Vector2 instead of two floats):
int currentWidth = GraphicsDevice.Viewport.Width,
currentHeight = GraphicsDevice.Viewport.Height;
Vector2 scale = new Vector2(currentWidth / defaultWidth,
currentHeight / defaultHeight);
This gives you a {0.625; 0.83333}. You can now use this in a handy SpriteBatch.Draw() overload that takes a Vector2 scaling variable:
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effects,
float layerDepth
)
Alternatively, you can draw all your stuff to a RenderTarget2D and copy the resulting image from there to a stretched texture on the main screen, but that will still require the above SpriteBatch.Draw() overload, though it might save you time if you have lots of draw calls.
Another Option to generate the scale would be to leverage:
var scaleMatrix = Matrix.CreateScale(
(float)GraphicsDevice.Viewport.Width / View.Width,
(float)GraphicsDevice.Viewport.Width / View.Width, 1f);
http://msdn.microsoft.com/en-gb/library/bb195692.aspx.
But this did not meet my needs, as I would then have to roll my own transform to map touch input location to the 'transformed' sprites (which respond to user touch input by knowing their own position and size).
In the end I used a percentage based approach.
I basically got the viewport height and width...
GraphicsDevice.Viewport.Width
GraphicsDevice.Viewport.Height
...then calculated the Height and Width of my sprites (Note: "as mentioned in question they take a literal height and width") based on their relative size to the screen myself using percentages.
//I want the buttons height and width to be 20% of the viewport
var x, y = GraphicsDevice.Viewport.Width * 0.2f; //20% of screen width
var btnsize = new Vector(x,y);
var button = new GameButton(btnsize);
Then once I have the size of the button I am able to calculate the position on the screen to render the button based of the size of the button and the available viewport size, against working in relative position based in percentages.

Resources