Xamarin Forms - AbsoluteLayout - How does works positions - xamarin.forms

I'm working with Xamarin.Forms with AbsoluteLayout, however, I'm not sure to understand how to works the positionning of elements.
I'm working with Proportional Values so if I'm placing an element at AbsoluteLayout.LayoutBounds="1, 0.05, 0.15, 0.1" where each values is Proportional (so the flags are "all" AbsoluteLayout.LayoutFlags="All")
It will be placed at the top/right of the screen. It will not take a place a bit outside however. So what does it means? Each element are repositionned into the screen if they go outside?
But now, another question comes, when you place an element, on what is based the X/Y position? Does is the center or another point?
On this example, I tried with 0.15 but the rendering was a bit weird, so I put 0 and then the rendering match with what I want.
You could say "Test it and you'll see.", however, It's a waste of time for the designer and me, to position every elements, because we're not sure to understand how does it works. So we juste make try with debuging..
We are also searching to know if a software exist to generate positions about the design made by the designer. We mean the position X/Y of the element in percent.
Thank in advance !

With AbsoluteLayoutFlag.All, the Rectangle bounds parameters have the following meaning:
x means the percentage of the remaining space (i.e parent width - control width) which should be on the left of the control
y means the percentage of the remaining space (i.e parent height - control height) which should be on the top of the control
width is the width of the control in percentage of the parent width
height is the height of the control in percentage of the parent height
Width and height are what people usually expect. However, x and y are not as people are more used to "left" and "top". So you can write a converter to convert left percentage into x and top percentage into y:
x = left / (1 - width)
y = top / (1 - height)

<AbsoluteLayout BackgroundColor="Yellow">
<BoxView
Color="Red"
AbsoluteLayout.LayoutBounds="1.0, 1.0, 0.5, 0.5"
AbsoluteLayout.LayoutFlags="All" />
<BoxView
Color="Green"
WidthRequest="50"
HeightRequest="50"
AbsoluteLayout.LayoutBounds="0.1, 0.1, AutoSize, AutoSize"
AbsoluteLayout.LayoutFlags="PositionProportional" />
<BoxView
Color="Blue"
AbsoluteLayout.LayoutBounds="0.25, 0.25, 0.5, 0.5"
AbsoluteLayout.LayoutFlags="All" />
</AbsoluteLayout>
When I researched AbsoluteLayout I created this sample and
Test it and you'll see
What I've decided from my investigation:
X and Y are coordinates of top left corner of View. It's relative position. As you can see for red rectangle 1.0, 1.0 is center position, so, as I understand, 100% width of screen is 2.0(same for height). All views inside AbsoluteLayout positioned dependently on values of parent AbsoluteLayout.
Edited:
X and Y are center coordinate of View, not left top corner. And 100% of Width is 1.0.

I did some testing and found that,With AbsoluteLayoutFlag.All, essentially the X value given as a Percentage basically represents where the anchor is on a rectangle. As in if X = 10% then the anchor is 10% from the left of the Rectangle:
example
At X = 0.1 with a given width of 20%. 90% of the length will go to the right and 10% will go to the left.
At X = 0.5 with a given width of 20%. 10% will go to left and 10% to the right.
Formula for X bounds
Variables in Rectangle (Ax, Ay, Wx, Wy):
W - required width
Ax - X value. (anchor position)
X1 - X position value of top left corner relative to left side (X = 0)
X2 - x position value of top right corner relative to left side (X = 0)
The Formula has 5 possible scenarios for an Anchor value
Ax = 0:
X1 = 0 & X2 = Wx
Ax = 1:
X2 = 1 & X1 = (1 - Wx)
Ax = 0.5
X1 = Ax - (0.5)(Wx)
X2 = Ax + (0.5)(Wx)
0 < Ax < 0.5
X1 = Ax - (Ax)(Wx)
X2 = Ax + (1 - Ax)(Wx)
0.5 < Ax < 1
X1 = Ax - (Ax)(Wx)
X2 = Ax + (1-Ax)(Wx)

X && Y are the center of the element

As a late answer, I've put a nuget package with a fork of AbsoluteLayout but working as I'd expect.
Install nuget package SmartMachines.AbsoluteLayout, add namespace xmlns:sm="clr-namespace:SmartMachines;assembly=AbsoluteLayout" and you will have exactly same AbsoluteLayout as native Xamarin, but with expected Proportional alignment behavior.
Hope that will save other people several hours on googling and debugging.

With AbsoluteLayout.LayoutFlags="All", the LayoutBounds (x, y, width, height) should be interpreted as follows:
width and height simply represent the respective dimensions of the child as a proportion of the AbsoluteLayout's overall size. For example, a width of 0.1 would create a child 10% of the width of the layout, 0.9 would cover 90% and 1.0 will fill the parent from left to right.
x and y represent the position within the parent, relative to the extremes of movement possible with the child still wholly visible within the layout. So: an x value of 0.0 places the left edge of the child against the left edge of the layout container and a value of 1.0 aligns the child's right edge with the container's right edge. Values between 0 and 1 represent positions between those extremes, moving the child proportionately through the range of movement available to it when the child is kept completely within the layout.
A nice way to think about the x / y positioning is to compare it with a window scroll bar or a value slider control because the whole scroll bar is visible at all times and it can be thought of as sliding between a value of 0 (document at top) and 1 (document at bottom).
Needless to say, centring a child is done by setting x or y to 0.5, regardless of width or height values.

Related

Calculate how much smaller a square would have to be to fit after rotated 45 degrees

Background: I'm creating a UI element in Unity and need to rotate it. It's a square, and will be rotated 45 degrees. Since rotation in Unity's UI happens after all sizing and positioning is done, the corners of this square stick outside the maximum area.
So I have a square inside a square. By default it's the same size as the parent square. After rotating 45 degrees, it's too large to fit in the parent square. How do I calculate the percentage by which to scale down the child square before rotating to make it fit inside the parent square after rotating?
A square of edge length a has a diagonal of length d = sqrt(2)*a, because, by Pythagoras theorem, the triangle formed by the diagonal and two of the square's edges satisfies d^2 = a^2 + a^2 = 2*a^2.
Therefore the inner square must be scaled by 1/sqrt(2) which is identical to sin(pi/4) = sin(45°) and cos(pi/4) = cos(45°) and approximately 0.7071....
If the scaling is performed centered at the sqaure's center, it doesn't matter whether rotation happens before or after scaling.
Actually, I just figured it out. The size of the child square needs to be a percentage of the parent square's size, which we can get from the sine (or cosine also works, I think?) of 45 degrees.
sin(45) == 0.70710678118654752440084436210485
To get to that size, I need to reduce my child square's size by the difference between 100% and 70.7106...%.
1 - sin(45) == 0.29289321881345247559915563789515
Since I'm using Unity and controlling the size of my square UI element with anchor positions, I just divided that by 2 and set the min X anchor to 0.146 and the max X anchor to 0.854. With an Aspect Ratio Fitter component set to Width Controls Height and a ratio of 1, this causes my child square to always fit perfectly inside my parent square even when rotated.

How to precisely find thumb position of input [type="range"]?

The idea was to put a tooltip with value related to slider. I thought initially to accomplish that task by using css grid. CSS provides you with grid of any size, 10 or 1000 cols, doesn't matter. By utilizing grid functionality, we can align out tooltip as we wish.
What we really get is:
Thumb position is sort of unpredictable. It seems that it is being offset, and the direction of that offset is dependent on whether input value is in the left or right part of slider.
Note: default thumb behaves exactly the same way. I mean, shape of thumb is not of concern.
So, how does html calculate position of thumb based on its value?
For anyone else stumbling upon this problem, the issue is that the first and last position of thumb are offset by half the width of thumb. Because of this, all of the other positions are slightly offset to compensate for the first and last one.
One simple solution for this is to normalize the thumb position between actual first and last thumb positions which are actually 0 + halfThumb and width - halfThumb respectively.
To normalize, we can use the formula recommended in this answer.
So the left offset of absolutely positioned element in px would be:
const left = (((value - minValue) / (valueMax - valueMin)) * ((totalInputWidth - thumbHalfWidth) - thumbHalfWidth)) + thumbHalfWidth;
Where
value is actual input value.
minValue is actual minimum value for input.
maxValue is actual maximum value for input.
thumbHalfWidth is half of the width of thumb used for slider drag.
totalInputWidth is width of input element in pixels.
You have to take into consideration the size of your thumb
It is possible to achieve this using a little bit of javascript (unless you can get the range ratio which I am not aware of as of now)
Therorical
Calculate the ratio of your current value relative to the min and max of your input (between 0 and 1)
(value - el.min) / (el.max - el.min)
So its position (starting from the arrow relative to the input container) would be:
thumbSize / 2 + ratio * 100% - ration * thumbSize
Method
This might give an idea of how to implement this in javascript
const thumbSize = 10
const range = document.querySelector('input[type=range]')
const tooltip = document.querySelector('.tooltip')
range.addEventListener('input', e => {
const ratio = (range.value - range.min) / (range.max - range.min)
tooltip.style.left = `calc(${thumbSize / 10}px + ${ratio * 100} - ${ratio} * ${thumbSize}px)`
}
This above code has not been tested but the same method has been implemented

Calculate Max Width and Height

I need to calculate the width and height of X number of elements based on the overall width and height of the screen in pixels so I can write a function. The individual elements x and y should be the same for each element.
I know the height and width of the screen but the number of elements could vary.
For example, if I have 20 elements/buttons, I want them to take up the whole screen and likewise, if I have 10 elements/buttons, I want them to take up the whole screen.
Example: Screen is 628x1289 and I have 20 elements. How can I calculate the size of each element?
This is similar to web responsive but it's not for a web application so I need to figure out how to calculate it using the pixels.
I hope that makes sense but please let me know if it doesn't.
Steve
P.S. This seems relevant but I'm no math expert so have no idea exactly what they're saying...
https://en.wikipedia.org/wiki/Knapsack_problem
Seems like they already know the size of the objects which is the opposite to my needs. I have a space and want to know what x and y I need to set each object to fit the space.
There are too many free parameters. Make some restrictions to solve the task.
Example: Let's element proportions (width/height ratio) are equal to screen proportions
C = ScreenWidth / ScreenHeight
Now let's unknown element height is h, so width is w = C * h
So every row may contain ScreenWidth div (C * h), every column ScreenHeight div h, where div is integer division.
Now you can find solution (max h value) of inequality
numberX <= (ScreenWidth div (C * h)) * (ScreenHeight div h)
and get height and width of numberX elements filling the screen (last row might be incomplete)

Rotating a Rectangle around a Grid To Calculate Players View

I have a player who can rotate and move around a 2D Cartesian grid, I need to calculate where to draw the enemies on screen.
The player should have a certain viewpoint which is the size of the screen in front of the direction the player is facing. (and a little behind)
I've tried tons of ways to implement this messing with Bi-Polar co-ordinates and Trig but I havn't been able to solve the problem of calculating where on the screen the enemies should be drawn.
The problem is best represent in the form of a graph with green being the viewpoint which is a rectangle that can rotate and move around the grid, and dots representing player and enemy.
So I need to work out the positions of the enemies on screen relative to the players rotation and position.
If you're going for a Doom-like perspective, you should imagine the viewing area as a parallelogram, rather than a rectangle. Imagine that behind your character is a camera man with its own position and angle.
The enemy's screen position is related to the angle between the camera and the enemy.
//indicates where on the screen an enemy should be drawn.
//-1 represents the leftmost part of the screen,
//and 1 is the rightmost.
//Anything larger or smaller is off the edge of the screen and should not be drawn.
float calculateXPosition(camera, enemy){
//the camera man can see anything 30 degrees to the left or right of its line of sight.
//This number is arbitrary; adjust to your own tastes.
frustumWidth = 60;
//the angle between the enemy and the camera, in relation to the x axis.
angle = atan2(enemy.y - camera.y, enemy.x - camera.x);
//the angle of the enemy, in relation to the camera's line of sight. If the enemy is on-camera, this should be less than frustumWidth/2.
objectiveAngle = camera.angle - angle;
//scale down from [-frustumWidth/2, frustumWidth/2] to [-1, 1]
return objectiveAngle / (frustrumWidth / 2);
}
These diagrams visualize what the variables I'm using here represent:
Once you have an "X position" in the range of [-1, 1], it should be easy enough to convert that into pixel coordinates. For example, if your screen is 500 pixels wide, you can do something like ((calculateXPosition(camera, enemy) + 1) / 2) * 500;
Edit:
You can do something similar to find the y-coordinate of a point, based on the point's height and distance from the camera.
(I'm not sure how you should define the height of the enemy and camera - any number should be fine as long as they somewhat match the scale set by the x and y dimensions of the cartesian grid.)
//this gives you a number between -1 and 1, just as calculateXPosition does.
//-1 is the bottom of the screen, 1 is the top.
float getYPosition(pointHeight, cameraHeight, distanceFromCamera){
frustrumWidth = 60;
relativeHeight = pointHeight - cameraHeight;
angle = atan2(relativeHeight, distanceFromCamera);
return angle / (frustrumWidth / 2);
}
You can call the method twice to determine the y position of both the top and the bottom of the enemy:
distanceFromCamera = sqrt((enemy.x - camera.x)^2 + (enemy.y - camera.y)^2);
topBoundary = convertToPixels(getYPosition(enemy.height, camera.height, distanceFromCamera));
bottomBoundary = convertToPixels(getYPosition(0, camera.height, distanceFromCamera));
That should give you enough information to properly scale and position the enemy's sprite.
(aside: the frustrumWidths in the two methods don't need to be the same - in fact, they should be different if the screen you are drawing to is rectangular. The ratios of the x frustrum and y frustrum should be equal to the ratios of the width and height of the screen.)

How to compute viewport from scrollbar?

This is probably straight forward enough but for some reason I just can't crack it (well actually its because I'm terrible at maths sadly!).
I have a viewport that shows 800 pixels at a time and I want to move it left and right over a virtual number of pixels say 2400 pixels wide.
I use the relative position of a scrollbar (0-1) to determine the virtual pixel that the viewport should have moved to.
I have a loop n = 800 that I run through.
I want to start reading an array of plottin points from the position the slider tells me and iterate through the next 800 points from that point.
The problem I have is when my scrollbar is all the way to the right and its relative position = 1 my readpoint = 2400..... which in a way is right. But that makes my read position of plotting points go to the last point and when I begin my iteration I have no points left to read.
Based on my moving an 800 pixel wide sliding box analagy the left point of the box should be 1600 in this case and its right point should be 2400
How can I achieve this?
Do I need some sort of value mapping formula?
private function calculateShuttleRelativePosition():void
{
var _x:Number=_scrollthumb.x
_sliderCenter=_x + (_scrollthumb.width / 2)
_sliderRange=_scrollbar.width - _scrollthumb.width;
_slider_rel_pos=(_sliderCenter - _sliderCenterMinLeft) / _sliderRange
}
pixel = slider relative position * 2400
readpoint= pixel
The range of your scrollbar should be the range that the left most pixel can take, not the range of all the pixels.
So your range would be 1600 (2400 - 800), not 2400 alone. This is the scaling factor you should apply to determine your offset.
As a word of warning always be on the lookout for off by one errors in these kinds of things. Since your bar ranges from 0 to 1 your outputted pixels will range from 0 to 800 if you are not careful, which could overflow your array if you don't watch out :).
The answer is actually pretty easy -- instead of making the relative position slide across the entire width (which is what you have above), you start at 0 and run to width - 800 (or the width of your viewport).
Some C-ish code for that would look like:
int viewPortWidth = 800;
int virtualWindowWidth = 2400;
// I'm assuming your code above is right -- I didn't check
float sliderRelativePosition = calculateShuttleRelativePosition();
// The casts (int and float here), make sure that you're rounding down to an integer
// pixel between 0 and virtualWindowWidth - 1, inclusive
int pixel = (int)(sliderRelativePosition * (float)(virtualWindowWidth - viewPortWidth - 1));
readPixels(pixel, viewPortWidth); // Function that loops through the pixels you want

Resources