calculate moving object inside box - php

I have an object that has:
start location: (X0,Y0)
and speed: (Vx, Vy)
it is trapped inside a box: boxWidth, boxHeight
when the object hits the border of the box it will flip direction.
ie:
object has speed: (1,3)
now it hits the top of the box
now it's speed will be: (1,-3)
now lets say it hitted the right of the box
and it's speed will be: (-1, -3)
I already made a sekeleton for the point class.
I would need a function that I will give it an "n" time and t will return me the current object location after that time:
my class:
class Point {
protected $x0;
protected $y0;
protected $vx;
protected $vy;
protected $currentX;
protected $currentY;
protected $blockWide;
protected $blockHeight;
public function __construct($x0, $y0, $vx, $vy, $blockHeight, $blockWide) {
$this->x0 = $x0;
$this->y0 = $y0;
$this->vx = $vx;
$this->vy = $vy;
$this->blockHeight = $blockHeight;
$this->blockWide = $blockWide;
$this->currentX = $x0;
$this->currentY = $y0;
}
public function getLoc($time) {
$this->currentX = $this->getLocX($time);
$this->currentY = $this->getLocY($time);
}
protected function getLocX($time) {
$direction = 1;
$flips = 0;
$distance = $this->vx * $time;
if ( $this->blockWide - $this->x0)
return 1;
}
protected function getLocY($time) {
return 0;
}
public function printAsPoint() {
echo '(',$this->currentX,',',$this->currentY,')';
}
}
I simply dont have an idea on how to calculate it with the starting location, speed, and speed flips that will happen each time the point will reach the borders.
some code from your posts:
protected function getLocX($time) {
$corPos = $this->x0 + $time * $this->vx;
$modulo = $corPos%(2*$this->blockWide);
if($modulo > $this->blockWide)
$corPos = 2*$this->blockWide - $modulo;
if($modulo < $this->blockWide)
$corPos = $modulo;
if($modulo == $this->blockWide)
$corPos = $modulo;
return $corPos;
}

If we take only the x - direction, then the object returns to the same state (same position and same velocity) after (2*boxWidth/Vx) time.
So keep subtracting the above quantity from the time till the value of time is greater than this quantity. If you are working with integers, then you can apply the remainder operator also.
Once you get the final time figure, it will be simple to deal with it. You just have to check for one bounce at the maximum.
xFinal = xInitial + Vx * t.
if xFinal > boxWidth || xFinal < 0 it means there is a bounce and proceed accordingly.
Similarly for y direction.

Consider your problem as looking for the X and then for the Y,
considering X0 the initial position, VX the walking step
(i assume this doesn't change in absolute) ,
WX the width of your object and boxWidth the width of the box
simplifications:
- you can consider your object to be 0 width in a box of (boxWidth-WX)
- you can consider your object running at the speed of 1
in a box of (boxWidth-WX)/VX width
- Since your object changes direction each time it hits a border, we can consider it
runs in same direction into a twice bigger box (boxWidth-WX)*2/VX
- finally, the new position after n moves shall be calculated on the base of :
(X0+n) mod (boxWidth-WX)*2/VX which gives you the position in a twice as big box,
and checking if the position is bigger than boxWidth the real position
will be boxWidth-foundPosition

Pretend there are no borders, then imagine there are!
d = s * t
No borders: actual d = d
With borders: actual d =
If moving to the right:
1. Subtract the distance to the right border from d.
2. Divide result by box-width. If the quotient is odd, the remainder
indicates how far you bounced back from the left border; if the quotient
is even, the remainder shows how far you bounced back from the right
border. (or something like that...perhaps you or others can correct me.)
If moving to the left:
Reverse the ideas from 1.
Since from your comments it seems that the positive-motion idea may be working for you, here's an example with motion in the opposite direction:
Let us say s is -1 unit/sec (that is, moving to the left),
t is 6 seconds,
the object is 1 unit from the left border,
and the box width is 2 units.
Total distance then is 1 * 6 = 6. We subtract 1 unit to get to the left border
and have 5 units left.
How many times do we bounce back and forth? We divide 5 by the box width:
the quotient is 2, which means the ball went once all the way to the right, then
back again to the left border. Since the quotient is even, we know we are back
to the left border (if the quotient was odd we would be at the right border).
Any remainder indicates the last distance bouncing back, in this case from the
left border, but not bouncing as far as the right border. Our remainder in this
case is 1, meaning we bounced back from the left border 1 unit, and that's our
current location!

Related

Rotate K coplanar points to a plane parallel to x,y plane

I'm working in php with 3D geometries(not the best choice,I know...).
I have K coplanar 3D points, also with x,y,z value. Together they form a polygon. I need to triangulate this polygon. I have already a working delaunay traingulation function which works for 2D Polygons.
So I want to rotate the given points, so that they lay on a plane parallel to the x,y plane. After that I can triangulated it using the x,y values. The following pseudocode shall describe how I want to get to this goal.
I build up the following code with reference on this (I'm usign the answer accepted from the OP): https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d, but it doesn't work as I expected. In order to know if it worked, every mapped point shall then have the same 'z' value.
Here is the question, how do I get the correct rotation matrix? Or did I made a conceptual mistake?
function matrixRotationMapping(Point $p, Point $q, Point $r)
{
$normalPolygon =calculatePlaneNormal($p, $q, $r);
$v = crossProduct($normalPolygon, new Point(0, 0, 1));
$c = dotProduct($normalPolygon, new Point(0, 0, 1));
$matrix = buildRotationMatrix($v, $c);
return $matrix;
}
function buildRotationMatrix($v, $c)
{
$R2 = new Matrix(array(array(1, -$v->z, $v->y), array($v->z, 1, -$v->x), array(-$v->y, $v->x, 1)));
$costant = 1/(1+$c);
$R3 = multiplyMatrices($R2, $R2);
$R3 = multiplyMatricesWithFactor($R3, $costant);
$finalMatrix = sumMatrices($R2, $R3);
return $finalMatrix;
}
function calc2DMapping($points)
{
$rotationMatrix = matrixRotationMapping($points[0], $points[1], $points[2]);
foreach($points as $point)
{
$mappedPoint = $rotationMatrix->multiplyWithPoint($point);
$mappedPoints[] = new MappedPoint($mappedPoint);
}
}
I found another helpful description of the problem, but I wasn't able to implement it: Mapping coordinates from plane given by normal vector to XY plane
Thanks in advance for your attention.
You need basis vectors X,Y,Z first. So let take the mid point A and two distant points to it B,C (not on single line) from your data set first. The X,Y should lie in the plane and Z should be normal to it so:
X = B-A // any non zero vector inside plane
X = X / |X| // unit in size
Y = C-A // any non zero vector inside plane
(X.Y) != 0 // but not parallel to X !!!
Y = Y / |Y| // unit in size
Compute normal to the plane your points lie in and correct Y axis.
Z = X x Y // cross product gives you perpendicular vector
Y = Z x X // now all vectors are perpendicular and unit
So feed these 3 vectors to rotation part of your transform matrix and set origin to A. But as you need to go from your data set to the plane local coordinate you need inverse matrix (or use pseudo inverse based on transposing)
Anyway now with the basis vectors you can map your plane parametrically like this:
P(u,v) = A + u*X + v*Y
Where u,v = <-inf,+inf> are surface distances form A in X,Y directions. That can get handy sometimes. If you need to compute u,v from P then exploit dot product:
u = ((P-A).X) = dot(P-A,X)
v = ((P-A).Y) = dot(P-A,Y)
Which can be also used to transform to 2D instead of using matrix ...

Coordinate (x,y) list to be sort with a spiral algorithm

I have a list of coordinate to be sorted with a spiral algorithm. My need is to start on the middle of the area and "touch" any coordinate.
To simplify this is the representation of the (unsorted) list of coordinates (x,y marked with a "dot" on following image).
CSV list of coordinates is available here.
X increase from left to right
Y increases from TOP to BOTTOM
Every coordinate is not adjacent to the following one but are instead distanciated by 1 or 2 dice (or more in certain case).
Starting from the center of the area, I need to touch any coordinate with a spiral movement:
to parse each coordinate I've drafted this PHP algorithm:
//$missing is an associative array having as key the coordinate "x,y" to be touched
$direction = 'top';
$distance = 1;
$next = '128,127'; //starting coordinate
$sequence = array(
$next;
)
unset($missing[$next]);
reset($missing);
$loopcount = 0;
while ($missing) {
for ($loop = 1; $loop <= 2; $loop++) {
for ($d = 1; $d <= $distance; $d++) {
list($x,$y) = explode(",", $next);
if ($direction == 'top') $next = ($x) . "," . ($y - 1);
elseif ($direction == 'right') $next = ($x + 1) . "," . ($y);
elseif ($direction == 'bottom') $next = ($x) . "," . ($y + 1);
elseif ($direction == 'left') $next = ($x - 1) . "," . ($y);
if ($missing[$next]) {
unset($missing[$next]); //missing is reduced every time that I pass over a coordinate to be touched
$sequence[] = $next;
}
}
if ($direction == 'top') $direction = 'right';
elseif ($direction == 'right') $direction = 'bottom';
elseif ($direction == 'bottom') $direction = 'left';
elseif ($direction == 'left') $direction = 'top';
}
$distance++;
}
but as coordinate are not equidistant from each other, I obtain this output:
As is clearly visible, the movement in the middle is correct whereas and accordingly with the coordinate position, at a certain instant the jump between each coordinate are not anymore coherent.
How can I modify my code to obtain an approach like this one, instead?
To simplify/reduce the problem: Imagine that dots on shown above image are cities that the salesman have to visit cirurarly. Starting from the "city" in the middle of the area, the next cities to be visited are the ones located near the starting point and located on North, East, Soutch and West of the starting point. The salesman cannot visit any further city unless all the adjacent cities in the round of the starting point hadn't been visited. All the cities must be visited only one time.
Algorithm design
First, free your mind and don't think of a spiral! :-) Then, let's formulate the algorithms constraints (let's use the salesman's perspective):
I am currently in a city and am looking where to go next. I'll have to find a city:
where I have not been before
that is as close to the center as possible (to keep spiraling)
that is as close as possible to my current city
Now, given these three constraints you can create a deterministic algorithm that creates a spiral (well at least for the given example it should, you probably can create cases that require more effort).
Implementation
First, because we can walk in any direction, lets generally use the Euclidean distance to compute distances.
Then to find the next city to visit:
$nextCost = INF;
$nextCity = null;
foreach ($notVisited as $otherCity) {
$cost = distance($current_city, $other_city) + distance($other_city, $centerCity);
if ($cost < $nextCost) {
$nextCost = $cost;
$nextCity = $otherCity;
}
}
// goto: $nextCity
Just repeat this until there are no more cities to visit.
To understand how it works, consider the following picture:
I am currently at the yellow circle and we'll assume the spiral up to this point is correct. Now compare the length of the yellow, pink and blue lines. The length of those lines is basically what we compute using the distance functions. You will find that in every case, the next correct city has the smallest distance (well, at least as long as we have as many points everywhere, you probably can easily come up with a counter-example).
This should get you started to implement a solution for your problem.
(Correctness) Optimization
With the current design, you will have to compare the current city to all remaining cities in each iteration. However, some cities are not of interest and even in the wrong direction. You can further optimize the correctness of the algorithm by excluding some cities from the search space before entering the foreach loop shown above. Consider this picture:
You will not want to go to those cities now (to keep spiraling, you shouldn't go backwards), so don't even take their distance into account. Albeit this is a little more complicated to figure out, if your data points are not as evenly distributed as in your provided example, this optimization should provide you a healthy spiral for more disturbed datasets.
Update: Correctness
Today it suddenly struck me and I rethought the proposed solution. I noticed a case where relying on the two euclidean distances might yield unwanted behavior:
It is easily possible to construct a case where the blue line is definitely shorter than the yellow one and thus gets preferred. However, that would break the spiral movement. To eliminate such cases we can make use of the travel direction. Consider the following image (I apologize for the hand-drawn angles):
The key idea is to compute the angle between the previous travel direction and the new travel direction. We are currently at the yellow dot and need to decide where to go next. Knowing the previous dot, we can obtain a vector representing the previous direction of the movement (e.g. the pink line).
Next, we compute the vector to each city we consider and compute the angle to the previous movement vector. If that vector is <= 180 deg (case 1 in the image), then the direction is ok, otherwise not (case 2 in the image).
// initially, you will need to set $prevCity manually
$prevCity = null;
$nextCost = INF;
$nextCity = null;
foreach ($notVisited as $otherCity) {
// ensure correct travel direction
$angle = angle(vectorBetween($prevCity, $currentCity), vectorBetween($currentCity, $otherCity));
if ($angle > 180) {
continue;
}
// find closest city
$cost = distance($current_city, $other_city) + distance($other_city, $centerCity);
if ($cost < $nextCost) {
$nextCost = $cost;
$nextCity = $otherCity;
}
}
$prevCity = $currentCity;
// goto: $nextCity
Pay attention to compute the angle and vectors correctly. If you need help on that, I can elaborate further or simply ask a new question.
The problem seems to be in the if-conditional when you missing traverse a co-ordinate, I.e because of rounding of the corners. A else conditional with a reverse to the previous calculation of the co-ordinate would fix it.

PHP Pixel Map Group Efficiency

I currently have a map that is 1600 x 1600 stored in MySQL(2,560,000 records). I'm rendering a simple 25x25 map out to users for interaction. Users can "claim" tiles on this map. I'd like to be able to calculate the number of open faces for tiles owned by a given user. I can divide this out by the total tiles owned to determine an arbitrary efficientcy rating.
All map coordinates are simply stored as X/Y values.
I'm looking for something that can potentially process an array of said X/Y values and determine how many open faces are accessible for each owned group. For example...
0 = player
x x x x x
x x 0 x x
x x x x x
4 open faces
x x x x x
x x 0 x x
x x 0 x x
x x x x x
6 open faces
x x x x x
x x x 0 x
x x 0 x x
x x x x x
8 open faces
Right now I'm doing some inefficient array looping to calculate this out. I have a simple counter, then I'm looping through an array of all values and am looking for values +-1 in each direction of X and Y to reduce the count. Each loop either adds 0-4 to the total counter based on the number of finds. The inherent problem with this method is that as a group grows, it will take longer and longer to calculate out. Since it's possible for one group to consume 20,000 points o rmore, it's quite a burden.
Any help is greatly appreciated.
One approach would involve creating a Point class. For example:
class Point {
public $x;
public $y;
public function __construct($x, $y){
$this->x = $x;
$this->y = $y;
}
public function getNeighbors(){
// TODO: What if we are at the edge of the board?
return array(
new Point($x+1, $y+1),
new Point($x+1, $y-1),
new Point($x-1, $y+1),
new Point($x-1, $y-1),
);
}
}
Create instances from that class for each point occupied by a user:
// Generate array of Points from database
$user_points = array(new Point(134, 245), new Point(146, 456));
Iterate through to generate all the neighbors:
// Get a flat array of neighbor Points
$neighbors = array_merge(array_map(function($point){
return $point->getNeighbors();
}, $user_points));
// TOOD: Remove Points that are equal to values in $user_points
Then, lastly, submit a COUNT query for the "neighbor" points to determine how many are occupied by other users and remove those from the total.
(Note: I've added TODOs where more work is to be done.)
The inherent problem with this method is that as a group grows, it will take longer and longer to calculate out.
You should consider using an in-memory key-value store, like Redis. But yes, the look-up time (for occupied blocks), in time complexity, appears linear with regard to the number of entries.
Here's the final block of simple code I came up with to determine the geo efficiency. Some names of things have been changed. :P
I'm running with notices on, and everything's ajax, so I decided to go with single isset checks on a multi-dimensional instead of something else.
$sql = 'SELECT map_x, map_y FROM Map WHERE person_id = :person_id';
$query = $DB->prepare($sql);
$query->execute(array(':nation_id' => $this->person_id));
$counter = 0;
$coords = array();
while($row = $query->fetch())
{
++$counter;
$coords[$row['map_x']][$row['map_y']] = 1;
}
$faces = 0;
foreach($coords as $x => $batch)
{
foreach($batch as $y => $junk)
{
$hits = 4;
if(isset($coords[$x + 1][$y]))
{
--$hits;
}
if(isset($coords[$x - 1][$y]))
{
--$hits;
}
if(isset($coords[$x][$y - 1]))
{
--$hits;
}
if(isset($coords[$x][$y + 1]))
{
--$hits;
}
$faces += $hits;
}
}

Drawing functions starting from a specific point

I have a problem drawing different functions with PHP (GD, of course).
I managed to draw different functions but whenever the parameters of the function change - the function floats wherever it wants.
Let us say that I have a first function y=x^2 and I have to draw it from -5 to 5. This means that the first point would be at (-5;25). And I can move that to whatever point I want if I know that. But if I choose y=2x^2 with an interval x=(-5;5). The first point is at (-5;50). So I need help in calculating how to move any function to, let's say, (0;0).
The functions are parabola/catenary alike.
What you want to do is find the maximum boundaries of the graph you are making. To do this you have to check each inflection point as well as the range bounds. Store each coordinate pair in an array
Part 1 [Range Bounds]:
Collect the coordinates from the range bounds.
<?php
$ybound[] = f($minX);
$ybound[] = f($maxX);
Part 2 [Inflections]:
This part is more difficult. You can either have a series of equations to solve for inflections for each type of parabola, or you can just brute force it. To do this, just choose a small increment, (what ever your small increment is for drawing the line), I will use 0.1
<?php
for($x = $minX; $x <= $maxX; $x += 0.1) {
$ybound[] = f($x);
}
Note, if you brute force, you can skip Part 1, otherwise, it would be faster if you could figure out the inflections for the scope of your project
Part 3 [Min Max]:
Now you get the min and max values from the array of possible y values.
<?php
$minY = min($ybound);
$maxY = max($ybound);
Part 4 [Shift]:
Now that you have this, it should be very simple to adjust. You take the top left corner and set that to 0,0 by adjusting each new coordinate to that value.
<?php
$shiftX = -$minX;
$shiftY = $maxY;
With this info, you can also determine your image size
<?php
$imageX = $maxX - $minX;
$imageY = $maxY - $minY;
Then as you generate your coordinates, you will shift each one, by adding the shift value to the coordinate.
<?php
for($x = -$minX; $x <= $maxX; $x += 0.1) {
$ycoor = $shiftY - f($x);
$xcoor = $x + $shiftX;
//draw ...
}
Drawing the axis is also easy,
<?php
$xaxis = $shiftY;
$yaxis = $shiftX;
(I think I have all my signs correct. Forgive me if they are off)
You first need to determine the bounding box of your function. Then, you calculate the width and the height, and you normalize so it fits into a rectangle whose top left coordinate is (0,0). Maybe you will also need to scale the figure to get it at a specific size.

How to find the % of a value on a Y axis of a LOG graph?

Wow, this one is though!
I'm trying to find in PHP the % of a value relative to the Y axis. If we refer to this graph : http://en.wikipedia.org/wiki/Semi-log_graph (2009 outbreak of influenza A), let's say that I want to find what % is a value "256" on the chart.
Visually, it's easy : it's a bit more than a 1/3 or 33%. If we look at the 1024 value, it's around 50% of the height of the Y axis. 131072 would be 100%.
So how do I calculate this with PHP?
Let's take this graph and take X = day 0 and Y = 256. What is 256 as a % of Y ?
Thanks a lot to anyone can compute this baby :)
percent = 100 * ( log(y) - log(y1) ) / ( log(y2) - log(y1) )
where
y = value
y1 = smallest value in y-axis
y2 = largest value in y-axis.
when y1 = 1.0 then you can simplify the other answers given here (since log(1)=0 by definition)
percent = 100 * log(y)/log(y2)
Note that not all log charts have 1.0 as the lowest value.
ln(131072) = 11.783 is 100%
ln(1024) = 6.931 is 58.824%
in PHP, that function is called log()
no need to set the base, as you are dividing them to find the relative value.
Alex got it, but to generalize for you and PHPize
logPercent = log(x) / log(top) * 100;
If you take the log of your max y value (131072 in your case) and the log of your y value (256), you end up with a height and a y value which is linear in relation to your drawn axis. you can divide them to get a decimal of the height and times by 100 for %:
using log base 2 seen as it gives integers (though any base should be fine).
log(256) / log(131072) = 8/17 = 0.47 = 47%
in php:
(log(256, 2) / log(131072, 2))*100;

Categories