This is for a game that I am considering.
I have a point that is noted as moving inside a (2D) circle from an arbitrary point, at an arbitrary direction and at a particular time. The point will bounce off the interior wall of the circle when it intersects.
For this example let's say the circle has a diameter of 100 kilometers with it's center at (0,0) and 10 hours ago the point was at location (20,30) with a heading of 40 degrees at a speed of 50kph .
What is the best way to determine where that point currently is and at what direction it's traveling?
I will be implementing this in PHP with the point and circle data stored in MySQL. Since it is a web page there will be no constantly running host process to keep things up to date and the data will need to be refreshed upon a page load.
I'm certainly not looking for anyone to write the code for me but am hoping someone can help me with a somewhat efficient way to approach this.
Your point-object will travel along what is called chords in geometry.
As the object hits the circle boundary, it will reflect from the circle's tangent at that point, and go along a next chord that has the same length. The next hit will be at the same angle (with the tangent at that hit point) as the previous hit, and so it will continue. At a constant speed, the time between hits will be a constant time.
Given the start time and the current time, one can calculate the number of chords that have been completed, and how much of the current chord has been completed. Calculating the position from that is easy when you know the previous and next hit positions. As these hit positions are at equal distances along the circle boundary, that is a matter of converting polar coordinates (an angle, and a distance of 1 radian) to Cartesian coordinates.
I will demonstrate this with JavaScript code. It will not be a great effort to wrap this in PHP:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var radius = canvas.height / 2 - 5;
var pixel = 1/radius;
// transform the canvas so that 0,0 is in the center of the canvas,
// and a unit circle would cover most of the height of the canvas:
context.setTransform(radius, 0, 0, radius, radius+2, radius+2);
// draw unit circle
context.beginPath();
context.arc(0, 0, 1, 0, 2 * Math.PI, false);
context.lineWidth = pixel;
context.strokeStyle = 'black';
context.stroke();
function drawPoint(point) {
// use a different color every 30 seconds:
context.fillStyle = Date.now() % 60000 > 30000 ? 'red' : 'blue';
context.fillRect(point.x-2*pixel, point.y-2*pixel, 4*pixel, 4*pixel);
}
function polarToCartesian(rad, dist) {
return {
x: Math.cos(rad) * dist,
y: Math.sin(rad) * dist
}
}
function pointBetween(a, b, fractionTravelled) {
return {
x: a.x + (b.x-a.x)*fractionTravelled,
y: a.y + (b.y-a.y)*fractionTravelled
}
}
// 4 parameters are needed:
var startRadians = 0; // distance along circle boundary from (0,1)
var hitAngle = Math.PI/2.931; // PI/2 would be head-on impact along diagonal
var speed = 0.4; // radians per second
var startTime = Date.now()/1000; // seconds
//
// Calculate some derived values which remain constant:
// - theta as used on https://en.wikipedia.org/wiki/Chord_%28geometry%29
// - chordSize formula comes from that wiki article.
var theta = 2 * hitAngle;
var chordSize = 2 * Math.sin(theta/2); // in radians
function drawCurrentPosition() {
// Note that this calculation does not look at the previous result,
// but uses the original parameters and time passed to calculate
// the objects current position.
var elapsedTime = Date.now()/1000 - startTime; // in secs
var distanceTravelled = speed * elapsedTime; // in radians
var chordsTravelled = distanceTravelled / chordSize; // in number of chords
var chordsTravelledComplete = Math.floor(chordsTravelled);
var fractionOnChord = chordsTravelled - chordsTravelledComplete; // 0<=f<1
var lastHitRadians = startRadians + chordsTravelledComplete * theta; // rad
var nextHitRadians = lastHitRadians + theta;
var lastHitPos = polarToCartesian(lastHitRadians, 1); // (x,y)
var nextHitPos = polarToCartesian(nextHitRadians, 1);
var currentPos = pointBetween(lastHitPos, nextHitPos, fractionOnChord);
drawPoint(currentPos);
}
// Demo: keep drawing the object's position every 0.1 second:
setInterval(drawCurrentPosition, 100);
<canvas id="myCanvas" width="200" height="200"></canvas>
Addendum: PHP code
Here is some code that could be useful for use in PHP. It uses the same calculations as the above JavaScript code, but does not keep running. Instead it first checks if there is a started game in the session scope, if not, it starts the "clock". At every request (reload of the page), the new position is calculated and printed on the page as an X,Y pair.
The coordinates are normalised, based on a unit circle (radius 1). The game parameters are hard-coded, but you could easily let them be passed via POST/GET parameters:
session_start(); // needed to persist game data for this user session
function getNewGame($startRadians, $hitAngle, $speed) {
$game = array();
$game["startTime"] = microtime(true);
$game["startRadians"] = $startRadians;
$game["theta"] = 2 * $hitAngle;
$game["chordSize"] = 2 * sin($hitAngle);
$game["speed"] = $speed;
return (object) $game;
}
function polarToCartesian($rad, $dist) {
return (object) array(
"x" => cos($rad) * $dist,
"y" => sin($rad) * $dist
);
}
function pointBetween($a, $b, $fractionTravelled) {
return (object) array(
"x" => $a->x + ($b->x-$a->x)*$fractionTravelled,
"y" => $a->y + ($b->y-$a->y)*$fractionTravelled
);
}
function getCurrentPosition($game) {
// Note that this calculation does not look at the previous result,
// but uses the original parameters and time passed to calculate
// the objects current position.
$elapsedTime = microtime(true) - $game->startTime; // in secs
$distanceTravelled = $game->speed * $elapsedTime; // in radians
$chordsTravelled = $distanceTravelled / $game->chordSize; //number of chords
$chordsTravelledComplete = floor($chordsTravelled);
$fractionOnChord = $chordsTravelled - $chordsTravelledComplete; // 0<=f<1
$lastHitRadians = $game->startRadians
+ $chordsTravelledComplete * $game->theta; // in radians on circle
$nextHitRadians = $lastHitRadians + $game->theta;
$lastHitPos = polarToCartesian($lastHitRadians, 1); // (x,y)
$nextHitPos = polarToCartesian($nextHitRadians, 1);
$currentPos = pointBetween($lastHitPos, $nextHitPos, $fractionOnChord);
return $currentPos;
}
// check if this is the first time the user loads this page:
if (!isset($_SESSION["game"])) {
// start game with some game parameters:
$_SESSION["game"] = getNewGame(0, pi()/2.931, 0.4);
}
// calculate the position based on game info and current time:
$pos = getCurrentPosition($_SESSION["game"]);
// print the result:
echo "Current position: {$pos->x}, {$pos->y}<br>";
Related
I want to list the latitude and longitude of itinerary. It could be all points or it could be all points in 1-2 kilometers.
What I'm trying to do is: user selected A as the starting point and B as the ending point. I want to show some places near the road between A and B on the map. But I need a positions for this.
As an example, a JavaScript code is shared here and It is said that this can be done with DirectionsResult Object.
var request = {
origin: start_point,
destination: end_point,
travelMode: google.maps.TravelMode.DRIVING
};
var directionsService = new google.maps.DirectionsService();
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var path = (response.routes[0].overview_path);
}
});
But I'm trying to do this with php and I have to do this with php.
I read google map api. I've also read the yandex map api, but this seems to be done only with javascript.
Does anyone know a way to do this with php?
From comments I understand the question is to find (using PHP) the intermediate lat,lng pairs that can be extracted from the polyline points in a google directions query.
This is a bit unusual because people normally use the polyline points for map drawing in the browser, and so the JavaScript libraries are well equipped for this task. However, not so in PHP.
The points data appears in the JSON result object as string of ascii characters, sometimes quite long and always 'unreadable'. Into this string is encoded a list of intermediate lat lng pairs between the start and end of each leg. The coding method is presented at the google site https://developers.google.com/maps/documentation/utilities/polylinealgorithm and the algorithm below is just a reversal of that and is commented accordingly.
The example shows a directions find between 2 points, on crescent shaped streets, in Perth, Australia. The start-end points were chosen to encourage multiple intermediate points as would be needed to draw the route. Substitute your own search as needed.
Note that the JSON also provides these fields also at the end of each results object.
"overview_polyline" : {
"points" : "~n{aEmbwaU_B#cCBk#Lo#d#UVOb#Mh#Ab####BBF#DGNABD`#Fh#Pb#VZn#b#d#J"
},
This is much less detailed and less accurate (if you draw will probably depart from actual road lines on map), but can also be decoded in the same way.
The best intermediate points are however, by iterating through the steps using:
"polyline" : {
"points" : "~n{aEmbwaUg##w#?{A?g#BUBUHSJ[XUVOb#Mh#Ab#"
},
Finally, the original source for the algorithm can be found here http://unitstep.net/blog/2008/08/02/decoding-google-maps-encoded-polylines-using-php/. So thanks to Peter Chng for this work back in 2008! Peter also acknowledges Mark MClure who did the original coding in JavaScript. I hacked about with and added more comments - to make more aligned with the google recipe, but no more.
I have also just realised there is this link https://github.com/emcconville/google-map-polyline-encoding-tool which (I think but have not tested) provides a class and a CLI tool to do the conversions both ways.
$json = file_get_contents("https://maps.googleapis.com/maps/api/directions/json?origin=20%20%20Kintyre%20Crescent,%20Churchlands&destination=%2018Kinross%20Crescent,%20Churchlands&key=");
$details = json_decode($json,true);
print_r($details); // show the full result
$points = $details['routes'][0]['legs'][0]['steps'][0]['polyline']['points'];
echo($points); // show the points string for one leg
// show the start and end locations for that leg
print_r($details['routes'][0]['legs'][0]['steps'][0]['start_location']);
print_r($details['routes'][0]['legs'][0]['steps'][0]['end_location']);
// work out the intermdiate points (normally used for drawing)
$decodedPoints= decodePolylinePoints($points);
print_r($decodedPoints); // print out the intermediate points
// This function decodes the polylone points in PHP
function decodePolylinePoints($pointsString)
{
$len = strlen($pointsString);
$latLons = array(); // the output array
$lat = 0; // temp storage for lat and lng
$lng = 0;
$index = 0; // index to curent character
while ($index < $len) // process each lat,lng pair
{
// first build the lat
// NOTE: first lat is an absolute value
// NOTE: subsequent lats are offsets from previous values for coding efficiency
$char = 0; // char as read from points string
$shift = 0; // cumulative shift amount
$value = 0; // temp value during computation
do // Read, convert and shift 5 bit chunks until terminator is reached to get lat
{
$char = ord(substr($pointsString, $index++)) - 63; // return ascii value less 63
$value |= ($char & 0x1f) << $shift; // convert to 5 bit and shift left
$shift += 5; // next shift is 5 extra
}
while ($char >= 0x20); // value of 20 indicates end of lat
$lat += (($value & 1) ? ~($value >> 1) : ($value >> 1)); // convert negative values and save
// now build the lng
// NOTE: first lng is an absolute value
// NOTE: subsequent lngs are offsets from previous values for coding efficiency
$shift = 0;
$value = 0;
do // build up lng from 5 bit chunks
{
$char= ord(substr($pointsString, $index++)) - 63; // return ascii value less 63
$value |= ($char & 0x1f) << $shift; // convert to 5 bit and shift left
$shift += 5; // next shift is 5 extra
}
while ($char >= 0x20); // value of 20 indicates end of lng
$lng += (($value & 1) ? ~($value >> 1) : ($value >> 1)); // convert negative values and save
$latLons[] = array($lat * 1e-5, $lng * 1e-5); // original values were * 1e5
}
return $latLons; // points array converted to lat,lngs
}
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 ...
I'm trying to rotate some 3d objects about the x-axis (I don't need to rotate them on their center so no need to translate to 0,0,0 first then back to their original position). I've just written a quick php script to test it out on an obj file. I've checked out the Wikipedia page for matrix rotation and was pretty confident my code would work...
CreateXrotationMatrix takes an angle and creates the required rotation matrix from
[1 0 0 ]
Rx(α)=[0 cosα -sinα]
[0 sinα cosα ]
function CreateXrotationMatrix($alpha)
{
$matrix = Array();
$matrix[0] = 1;
$matrix[1] = 0;
$matrix[2] = 0;
$matrix[3] = 0;
$matrix[4] = cos($alpha);
$matrix[5] = -sin($alpha);
$matrix[6] = 0;
$matrix[7] = sin($alpha);
$matrix[8] = cos($alpha);
return $matrix;
}
MultiplyMatrices then multiplies a 3x3 matrix by a given point
function MultiplyMatrices($rotationMatrix, $point)
{
$returnMatrix = Array();
$returnMatrix[0] = $rotationMatrix[0]*$point[0] + $rotationMatrix[1]*$point[1] + $rotationMatrix[2]*$point[2];
$returnMatrix[1] = $rotationMatrix[3]*$point[0] + $rotationMatrix[4]*$point[1] + $rotationMatrix[4]*$point[2];
$returnMatrix[2] = $rotationMatrix[6]*$point[0] + $rotationMatrix[7]*$point[1] + $rotationMatrix[8]*$point[2];
return $returnMatrix;
}
I ran this code on an obj file which I tried to rotate by 270 degrees and wrote it out to another file.
When I got the two models side by side in blender they were only 10 degrees apart.
Any ideas what's wrong here?
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!
I have the Lat/Long value of New York City, NY; 40.7560540,-73.9869510 and a flat image of the earth, 1000px × 446px.
I would like to be able to convert, using Javascript, the Lat/Long to an X,Y coordinate where the point would reflect the location.
So the X,Y coordinate form the Top-Left corner of the image would be; 289, 111
Things to note:
don't worry about issues of what projection to use, make your own
assumption or go with what you know
might work
X,Y can be form any corner of the image
Bonus points for
the same solution in PHP (but I
really need the JS)
The projection you use is going to change everything, but this will work assuming a Mercator projection:
<html>
<head>
<script language="Javascript">
var dot_size = 3;
var longitude_shift = 55; // number of pixels your map's prime meridian is off-center.
var x_pos = 54;
var y_pos = 19;
var map_width = 430;
var map_height = 332;
var half_dot = Math.floor(dot_size / 2);
function draw_point(x, y) {
dot = '<div style="position:absolute;width:' + dot_size + 'px;height:' + dot_size + 'px;top:' + y + 'px;left:' + x + 'px;background:#00ff00"></div>';
document.body.innerHTML += dot;
}
function plot_point(lat, lng) {
// Mercator projection
// longitude: just scale and shift
x = (map_width * (180 + lng) / 360) % map_width + longitude_shift;
// latitude: using the Mercator projection
lat = lat * Math.PI / 180; // convert from degrees to radians
y = Math.log(Math.tan((lat/2) + (Math.PI/4))); // do the Mercator projection (w/ equator of 2pi units)
y = (map_height / 2) - (map_width * y / (2 * Math.PI)) + y_pos; // fit it to our map
x -= x_pos;
y -= y_pos;
draw_point(x - half_dot, y - half_dot);
}
</script>
</head>
<body onload="plot_point(40.756, -73.986)">
<!-- image found at http://www.math.ubc.ca/~israel/m103/mercator.png -->
<img src="mercator.png" style="position:absolute;top:0px;left:0px">
</body>
</html>
A basic conversion function in js would be:
MAP_WIDTH = 1000;
MAP_HEIGHT = 446;
function convert(lat, lon){
var y = ((-1 * lat) + 90) * (MAP_HEIGHT / 180);
var x = (lon + 180) * (MAP_WIDTH / 360);
return {x:x,y:y};
}
This will return the number of pixels from upper left.
This function assumes the following:
That your image is properly aligned
with the upper left corner (0,0)
aligning with 90* North by 180*
West.
That your coords are signed with N being -, S being +, W being - and E being +
If you have a picture of the whole earth, the projection does always matter. But maybe I just don't understand your question.