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?
Related
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>";
I'm fairly new to PHP and I'm trying to design a car's gear ratio calculator that takes certain vehicle's dimensions as input. One way to calculate them is trough using geometric progression method. Below are the codes I'm trying to run:
<?
$alowgearf = 14.37;
$ahigear = 3.293;
$noOfGear = 5;
$loopLimit = $noOfGear - 2;
$gears = array(0 => "$alowgearf", "$noOfGear - 1" => "$ahigear");
$nlnh = $alowgearf/$ahigear;
$cgp = pow($nlnh, 1/($noOfGear - 1));
for ( $n = 1; $n <= $loopLimit ; $n++ ) {
$m = $n - 1;
$gear = $gears[$m]/$cgp;
$gears[$n] = $gear;
}
print_r($gears);
?>
The formula for the GPM method of calculating gear ratio is as follows :
N[i+1] = N[i]/Cgp,
where N is gear ratio, i is the gear number and Cgp is the geometric progression constant.
By supplying the formula with the lowest and highest gear ratio and the desired number of gears, we can calculate the rest of the gear ratios.
The problem is, the codes above outputs nothing. I've been trying to fix the code but I can't detect any problem with them. I've been toying with other loop function such as do-while but the results are the same. What's the problem? What I've been doing wrong?
I use the FPDF library to export some document files as PDF. One document includes a list of strings which have a different length. I print all strings as $pdf->MultiCell(). Now I would like to have the current height of that MultiCell to have the same line spacing in case that they have just one line or more.
Code Example:
//MySQL Query
while($row = mysql_fetch_array($res) {
$pdf->SetXY(18, $x);
$pdf->MultiCell(80, 5, $rowr['text']); //text has one or more lines
$x = $x + 10; // Here I would prefer a solution to say: $x = $x + 2 + height of the MultiCell()
}
I had the exact same problem; I use FPDF to generate invoices and there are four cells per row with first cell being a MultiCell with varying height (default height is 5, but if order name is too long it adds another line to a total height of 10, and so on). The problem is that the remaining 3 cells have fixed height.
After looking for solutions it seems the only way is to write a complex function or use some third party tool. Since I want my app to be as light as possible I used the following approach to solve it which in my opinion is way simpler than external plugin.
Rows with details on the Invoice start at Y=95 so I use $Y=95;
before my while loop
My first cell (the MultiCell) is as follows:
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['Name'],1,1,'L');
I use the FPDF's GetY() function to get current height and save it as H:
$H = $pdf->GetY();
If the MultiCell's height is 5 GetY() will give back 100, if the height is 10 GetY() gives back 105 and so on.
I add new variable $height:
$height= $H-$Y;
Which as result gives me precisely the height of the MultiCell.
I use $Y and $height to set current position and column height:
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['RowName'],1,1,'L');
Before finishing the while loop set give $Y the $H's value:
$Y=$H;
Entire loop looks as follows and works perfectly:
$Y= 95;
$query = mysqli_query($con,"SELECT * FROM table");
while($row = mysqli_fetch_array($query)) {
$pdf->SetXY(10,$Y);
$pdf->MultiCell(60,5,$row['ROW1'],1,1,'L');
$H = $pdf->GetY();
$height= $H-$Y;
$pdf->SetXY(70,$Y);
$pdf->Cell(60,$height,$row['ROW2'],1,1,'L');
$pdf->SetXY(130,$Y);
$pdf->Cell(40,$height,$row['ROW3'],1,1,'L');
$pdf->SetXY(170,$Y);
$pdf->Cell(30,$height,$row['ROW4'],1,1,'L');
$Y=$H;
}
If you have 2 MultiCell columns or more in each row it gets tricky but still can be solved in a similar manner.
I found interesting solution here - https://github.com/artkonekt/pdf-invoice/blob/master/src/InvoicePrinter.php#L469
$calculateHeight = new self;
$calculateHeight->addPage();
$calculateHeight->setXY(0, 0);
$calculateHeight->SetFont($this->font, '', 7);
$calculateHeight->MultiCell($this->firstColumnWidth, 3, $item['description'], 0, 'L', 1);
$descriptionHeight = $calculateHeight->getY() + $cellHeight + 2;
So, he literally create a 'temporary' PDF, add multicell, and then simply measure height (newY - oldY)
Also, keep in mind that if text goes to new line - height of cell will be = number_of_lines * $height (height passed to MultiCell as second parameter)
So, if you passed 5 as $height, and temporary PDF measure that cell will be 15, you can be sure that text will spread to 3 lines.
I'm coding in golang so I'll show some pseudo-code. I hope the accessible methods are the same in php as in golang.
There is a method called pdf.SplitLines(text, width). You will pass your string content and the desired width and it will return an array of strings that represents the lines that'll be computed to display that content.
With that its easy. In pseudo-code it could look like:
fontSize = 10;
lineHeight = 12;
targetWidth = 50;
pdf.SetFontSize(fontSize)
nLines = length(pdf.SplitLines(content, targetWidth));
multiCellHeight = nLines * lineHeight;
pdf.Multicell(targetWidth, lineHeight, content, ...)
The rendered MultiCell will have the exact same size as stored in multiCellHeight. This way you'll get the hight before rendering.
This is working because the passed height for the MultiCell is the lineHeight of each row. If you know the rows before rendering, you'll get the total height.
I'm sorry if this fails for any reason for php. Just let me know if that's the case.
Wouldn't it be easier to simply NOT use boarders when you print out the text.
You can just use $pdf->GetY(); to get the curent y value.
Then when you have printed all the text, you can use $pdf->GetY(); to get the height after each piece of text. Compare y values to see which one is the biggest.
All you need to do then is $pdf-> SetY($y); to the original y value and paint the boarders with $pdf->Cell() now that you know height and width;
That's how I'd do it.
Edit: tested it, and seems to work. Now sing with me - "you get the beeeeest of both worlds..." no?
$cellData = array();
$cellData[0] = array();
$cellData[0][0] = array();
$cellData[0][0]['text'] = 'Audiometry';
$cellData[0][0]['width'] = '47';
$cellData[0][1] = array();
$cellData[0][1]['text'] = 'Control of Noise at Work Regulations 2005.';
$cellData[0][1]['width'] = '47';
$cellData[0][2] = array();
$cellData[0][2]['text'] = 'Fit with restrictions (as detailed below) to work in a noise controlled zone.';
$cellData[0][2]['width'] = '47';
$cellData[0][3] = array();
$cellData[0][3]['text'] = 'Recommended review date: 2021-11-27
Referral: Referred to OHP';
$cellData[0][3]['width'] = '47';
setAllCellSizes($cellData, $pdf);
function setAllCellSizes($cellData, $pdf){
$y = $pdf->GetY();
$x = $pdf->GetX();
$largestCell = 0;
for($cordI = 0; $cordI < count($cellData); $cordI++){
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf-> MultiCell($cellData[$cordI][$cordJ]['width'], 5, $cellData[$cordI][$cordJ]['text'], '0', 'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
$cellHeight = $pdf->GetY() - $y;
if($largestCell < $cellHeight){
$largestCell = $cellHeight;
}
}
$curX = 10;
for($cordJ = 0; $cordJ < count($cellData[$cordI]); $cordJ++){
$pdf->SetXY($curX,$y);
$pdf->Cell($cellData[$cordI][$cordJ]['width'],$largestCell,'',1,1,'L');
$curX = $curX + $cellData[$cordI][$cordJ]['width'];
}
}
return $cellData;
}
I'm completely stuck with something that i believe is relatively simple to solve:
In PHP I am drawing filled rectangles on top of an image (using GD) - I want a small gap between each of them - which is specified in the code (gap is the same for each box) - I loop around until i've hit the max amount of rectangles on a row (e.g max columns) - and i've managed the horizontal gap offset but for the life of me can't work out the vertical gap maths.
Here is my code in it's nasty entirety:
http://pastebin.com/MHUqi0tG
But specifically PHP accepts two sets of coordinates to make a rectangle - two for the top left (x and y) and two for the bottom right (x and y).
Here is my code for that in particular:
$left_wall_of_box_x = ( $current_col * $box_size ) + $origin_x ;
if($last_block_x != $origin_x){
$left_wall_of_box_x = $last_block_x + $gap;
}
/*verticals coord 1*/
$left_wall_of_box_y = ( $current_row * $box_size ) + $origin_y;
$right_wall_x = $left_wall_of_box_x + $box_size ;
$right_wall_y = $left_wall_of_box_y + $box_size;
imagefilledrectangle($im, $left_wall_of_box_x, $left_wall_of_box_y, $right_wall_x, $right_wall_y, $red);
$gap = 2;
$origin_x & origin_y = the place i start drawing boxes from.
Output at the moment is nicely separated boxes on the horizontal axis but vertically they are hitting each other/merging together.
Any help obviously greatly appreciated - Tearing hair out on this.
Thanks
The mistake is here:
$left_wall_of_box_y = ( $current_row * $box_size ) + $origin_y;
Should be:
$left_wall_of_box_y = ( $current_row * ($box_size + $gap_vertical)) + $origin_y;
But your code is fragile and hard to read. It's fragile as you're making your calculations more be dependent on variables that represent subtly different things, and as there's more variables, it's harder to fit in your head - which is partly why you didn't spot the error.
So, rather than doing this:
while($x <= $total_boxes_to_draw)
{
if($current_col >= $cols)
{
//reset to start of row
}
}
Write your code like this.
$finished = false;
for ($y=0; ($y<$rows) && ($finished==false) ; $y++)
{
$yPosition = $origin_y + $y * ($box_size + $spacing_vertical);
for ($x=0 ; $x<$cols && ($finished==false) ; $x++)
{
$xPosition = $origin_x + $x * ($box_size + $spacing_horizontal);
//draw box at $xPosition, $yPosition
$boxesDrawn++;
if ($boxesDrawn >= $total_boxes_to_draw){
$finished = true;
}
}
}
You are adding a gap horizontally, but you also have to add it vertically.
A simple fix is subtracting the gap from the height of the rectangles you fill:
imagefilledrectangle($im, $left_wall_of_box_x, $left_wall_of_box_y,
$right_wall_x, $right_wall_y - gap, $red);
So I'm working on making my header change color everyday, and I was attempting to create this using a random color. There are 2 colors in the header and I am making them complimentary colors. the first color is generated randomly, and then the second is modified by changing the Hue via 150`. The problem is when certain colors are chosen, they could be either too vibrant or dark. I have a check running so that I can slightly control the brightness value, but there are still some colors that are too bright ( for instance extreme yellows ). I'l post my code below. Any help or suggestions is appreciated! Thanks!
// grab a random color on hue
$h = rand(0,360);
// color values 50-120 tend to be extremely bright,
// make adjustments to the S and L accordingly
// a better solution is available?
if ($h > 50 && $h < 120) {
$s = rand(60,80);
$l = rand(30,50);
} else {
$s = rand(60,90);
$l = rand(38,63);
}
// declare string to place as css in file for primary color
$randomColor = "hsl(". $h .",". $s ."%,". $l ."%)";
// declare degree for secondary color (30 = analogous, 150 = complimentary)
$degree = 150;
// point to secondary color randomly on either side of chart
$bool = rand(0,1);
if ($bool) {
$x = $degree;
} else {
$x = -$degree;
}
// set value of the new hue
$nh = $h + $degree;
// if the new hue is above 360 or below 0, make adjustments accordingly
if ($nh > 360) {
$nh -= 360;
}
if ($nh < 0 ) {
$nh = 360 - $nh;
}
// set the secondary color
$secondaryColor = "hsl(". abs($h + $x) .",". $s ."%,". $l ."%)";
This seems very simple and I'm sure there is a better method. I looked around, but all I noticed were the basic formula's via degrees for the hue etc. Thanks again!
This is really more of a question of which colors you deem acceptable for viewing. This certainly isn't an optimal solution but it's an approach that is readable at least (it's also slightly more random than your original, if you even care about that):
function randColor() {
return array( rand(0,360), rand(0,100), rand(0,100) );
}
function isAcceptableColor($colorArr) {
// return true if the color meets your criteria
}
do {
$color = randColor();
} while ( ! isAcceptableColor($color) );