Let's say I have an axis-aligned box. I also have a location outside that box.
How do I get the point on the boundaries of the box that is along the line from the center of the box to that point? (And thus, know how far the box extends in that direction).
EDIT: preferably, if the point is inside the box, it should just return the point... but that's something I could easily calculate myself.
2D diagram (Though the answer will preferably work in 3D as well):
point3d getIntersection(point3d topfrontleft, point3d backbottomright, point3d externpt)
{
//find the middle
point3d middle(topfrontleft/2+backbottomright/2);
//slide box and source as if the "middle" was at the origin
topfrontleft -= middle;
backbottomright-= middle;
externpt-= middle;
//scale source as if the box is the unit square
externpt/= topfrontleft;
//find the largest magnitude of the source offset
point3d mag = abs(externpt);
auto max = std::max(mag.x,std::max(mag.y,mag.z));
//if it's inside the box, don't scale
if (max < 1)
max = 1;
//scale the source so that it touches the box
externpt/= max;
//then scale and slide that touching point back to original coordinates
externpt= externpt* topfrontleft + middle;
return externpt;
}
http://coliru.stacked-crooked.com/a/9d9504964bc650bb
I'm not experienced with 3d math, so there's probably faster and more effective ways. No idea.
As a bonus, this algorithm doesn't make any reference to the number of dimensions except for finding the largest magnitude, so should work in both 2d, 3d, and any other theoretical number of dimensions.
Here is some code that does what I said in comments modified for rectangular boxes.
#include <stdio.h>
#include <math.h>
// Find intersection p between line A->B and box.
// Point A must be the box center.
// The box is [x_left, y_bottom, x_right, y_top].
void find_box_intersect(double *box, double *a, double *b, double *p)
{
double dx = b[0] - a[0];
double dy = b[1] - a[1];
if (fabs(dx * (box[3] - box[1])) > fabs(dy * (box[2] - box[0]))) {
p[0] = dx > 0 ? box[2] : box[0];
p[1] = a[1] + dy * (p[0] - a[0]) / dx;
} else {
p[1] = dy > 0 ? box[3] : box[1];
p[0] = a[0] + dx * (p[1] - a[1]) / dy;
}
}
int main(void) {
double box[] = { 1, 2,
2, 4 };
double p[2], a[] = { 0.5 * (box[0] + box[2]),
0.5 * (box[1] + box[3]) };
int i, n = 16;
for (i = 0; i < n; i++) {
double theta = 2 * 3.14159 * i / n;
double b[] = { a[0] + cos(theta), a[1] + sin(theta) };
find_box_intersect(box, a, b, p);
printf("%.2f, %.2f\n", p[0], p[1]);
}
return 0;
}
The 3d generalization of this is straightforward. Rather than the if statement that determines only 2 cases, there will be a 3 case if-else chain that determines 3: left-right, top-bottom, front-back.
Output:
2.00, 3.00
2.00, 3.21
2.00, 3.50
1.91, 4.00
1.50, 4.00
1.09, 4.00
1.00, 3.50
1.00, 3.21
1.00, 3.00
1.00, 2.79
1.00, 2.50
1.09, 2.00
1.50, 2.00
1.91, 2.00
2.00, 2.50
2.00, 2.79
Short answer: Intersect the line with the (right) border of the box.
Longer answer:
x is easy: It's at the right border of the box.
for y, solve this: y/line_height = (line_width - box_width/2) / line_width
then add the y of the line lower point
This is assuming the line intersects the right border, as in your picture.
To figure which border the line intersects, compare the ratio of line_height/line_width, and the signs, to the ratio of box_height/box_width.
If the box is square and you're only interested in the radius, and not the intersection point, you can throw away the signs of line_width and line_height, sort them, and just solve one case.
If the box is not square, you can still throw away the signs, but need to compare the ratios to pick one of two remaining cases (in 2D).
Assuming that you know the length and width of the rect, if the angle is pointing left or right, then x is known, so you only need to solve for y (r * sin(angle)); if the angle is pointing up or down, then y is known so you solve for x (r * cos(angle)).
// where the center of the rect = 0, 0 and angles are in degrees 0..360
// determine the 45's first
if (angle in (45, 135, 225, 315)) {
return { x:cos(angle) * (width / 2), y:sin(angle) * (height / 2) };
}
if (angle < 45 || angle > 315) {
// pointing right
return { x:(width / 2), y:sin(angle) * (height / 2) };
}
if (angle > 45 && angle < 135) {
// pointing up
return { x:cos(angle) * (width / 2), y:(height / 2) };
}
if (angle > 135 && angle < 225) {
// pointing left
return { x:(width / -2), y:sin(angle) * (height / 2) };
}
if (angle > 225 && angle < 315) {
// pointing down
return { x:cos( angle ) * (width / 2), y:(height / -2) };
}
There are undoubtedly more elegant mathematical solutions to this problem, but this one works for me--understandable, testable, etc.
Related
I'm developing a Processing sketch that, given a certain angle, draws a dot at the edge of a rhombus.
I know the width of the rhombus, and its position, but I'm not sure how to calculate the x-y coordinates of a dot resting at its edge.
Are there any elegant solutions for this problem? Any help in pseudocode would be welcomed.
Let's square side length is A, half-length is H = A/2. Angle Theta. Intersection point P.
All coordinates are relative to the square center.
Rotate square by -Pi/4, angle Alpha = Theta - Pi/4
if Alpha lies in range -Pi/4..Pi/4, then intersection point P' = (H, H*Tan(Alpha))
if Alpha lies in range Pi/4..3*Pi/4, then P' = (H*Cotangent(Alpha), H)
if Alpha lies in range 3*Pi/4..5*Pi/4, then P' = (-H, -H*Tan(Alpha))
if Alpha lies in range 5*Pi/4..7*Pi/4, then P' = (-H*Cotangent(Alpha), -H)
Then rotate point P' back by Pi/4:
S = Sqrt(2)/2
P.X = S * (P'.X - P'.Y)
P.Y = S * (P'.X + P'.Y)
Example (data like your sketch):
A = 200, Theta = 5*Pi/12
H = 200/2 = 100, Alpha =Theta-Pi/4 = Pi/6
P'.X = H = 100
P'.Y = H * Tan(Alpha) = 100 * Tan(Pi/6) ~= 57.7
S = 0.707
P.X = 0.707 * (100 - 57.7) = 30
P.Y = 0.707 * (100 + 57.7) = 111
Based on your image, you want to find the intersection of two equations, that of the line at angle θ, and that of the side of the square with which it intersects.
Assuming the size of your square is n, the equation of the square is y=±(n*(√2/2))±x (by Pythagoras' theorem). The equation for the side you intersect in your image is y=n*(√2/2)-x.
The equation of the radial line can be calculated using trigonometry to be y=tan(θ)*x, with θ expressed in radians.
You can then solve this as a simultaneous equation to determine the intersection. Please note that it will intersect with both sides of the square (both above and below), so if you only want the one you will have to choose the equation for the correct side of the square. Also guard against the case where θ is π/2, as tan(π/2) is undefined. You can easily work out that case, as x=0 and so it will always intersect at y=±(n*(√2/2)).
In your example, the intersection occurs when x*(1+tan(θ))=n*(√n/n), or x=(n*(√n/n))/(1+tan(θ)). You can calculate that, plug it back into y and that is your (x,y) intersection.
Imagine a circle with a larger radius that will intersect your rhombus at the points you want. One way to draw at that location is to use a nested coordinate system that you translate and rotate. All you need to know is the radius and the angle.
Here's a very basic example:
float angle = radians(-80.31);
float radius = 128;
float centerX,centerY;
void setup(){
size(320,320);
noFill();
rectMode(CENTER);
centerX = width * 0.5;
centerY = height * 0.5;
}
void draw(){
background(255);
noFill();
//small circle
strokeWeight(1);
stroke(95,105,120);
ellipse(centerX,centerY,210,210);
rhombus(centerX,centerY,210);
//large circle
strokeWeight(3);
stroke(95,105,120);
ellipse(centerX,centerY,radius * 2,radius * 2);
//line at angle
pushMatrix();
translate(centerX,centerY);
rotate(angle);
stroke(162,42,32);
line(0,0,radius,0);
popMatrix();
//debug
fill(0);
text("angle: " + degrees(angle),10,15);
}
void rhombus(float x,float y,float size){
pushMatrix();
translate(x,y);
rotate(radians(45));
rect(0,0,size,size);
popMatrix();
}
void mouseDragged(){
angle = atan2(centerY-mouseY,centerX-mouseX)+PI;
}
You can try a demo here(you can drag the mouse to change the angle):
var angle;
var radius = 128;
var centerX,centerY;
function setup(){
createCanvas(320,320);
noFill();
rectMode(CENTER);
angle = radians(-80.31);
centerX = width * 0.5;
centerY = height * 0.5;
}
function draw(){
background(255);
noFill();
//small circle
strokeWeight(1);
stroke(95,105,120);
ellipse(centerX,centerY,210,210);
rhombus(centerX,centerY,210);
//large circle
strokeWeight(3);
stroke(95,105,120);
ellipse(centerX,centerY,radius * 2,radius * 2);
//line at angle
push();
translate(centerX,centerY);
rotate(angle);
stroke(162,42,32);
line(0,0,radius,0);
pop();
//debug
fill(0);
noStroke();
text("angle: " + degrees(angle),10,15);
}
function rhombus(x,y,size){
push();
translate(x,y);
rotate(radians(45));
rect(0,0,size,size);
pop();
}
function mouseDragged(){
angle = atan2(centerY-mouseY,centerX-mouseX)+PI;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.0/p5.min.js"></script>
If you want to calculate the position, you can use the polar to cartesian coordinate conversion formula:
x = cos(angle) * radius
y = sin(angle) * radius
Here's an example using that. Note that drawing is done from the centre, therefore the centre coordinates are added to the above:
float angle = radians(-80.31);
float radius = 128;
float centerX,centerY;
void setup(){
size(320,320);
noFill();
rectMode(CENTER);
centerX = width * 0.5;
centerY = height * 0.5;
}
void draw(){
background(255);
noFill();
//small circle
strokeWeight(1);
stroke(95,105,120);
ellipse(centerX,centerY,210,210);
rhombus(centerX,centerY,210);
//large circle
strokeWeight(3);
stroke(95,105,120);
ellipse(centerX,centerY,radius * 2,radius * 2);
//line at angle
float x = centerX+(cos(angle) * radius);
float y = centerX+(sin(angle) * radius);
stroke(162,42,32);
line(centerX,centerY,x,y);
//debug
fill(0);
text("angle: " + degrees(angle),10,15);
}
void rhombus(float x,float y,float size){
pushMatrix();
translate(x,y);
rotate(radians(45));
rect(0,0,size,size);
popMatrix();
}
void mouseDragged(){
angle = atan2(centerY-mouseY,centerX-mouseX)+PI;
}
Another option would be using transformation matrices
i'm trying to code correct 2D affine texture mapping in GLSL.
Explanation:
...NONE of this images is correct for my purposes. Right (labeled Correct) has perspective correction which i do not want. So this: Getting to know the Q texture coordinate solution (without further improvements) is not what I'm looking for.
I'd like to simply "stretch" texture inside quadrilateral, something like this:
but composed from two triangles. Any advice (GLSL) please?
This works well as long as you have a trapezoid, and its parallel edges are aligned with one of the local axes. I recommend playing around with my Unity package.
GLSL:
varying vec2 shiftedPosition, width_height;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
shiftedPosition = gl_MultiTexCoord0.xy; // left and bottom edges zeroed.
width_height = gl_MultiTexCoord1.xy;
}
#endif
#ifdef FRAGMENT
uniform sampler2D _MainTex;
void main() {
gl_FragColor = texture2D(_MainTex, shiftedPosition / width_height);
}
#endif
C#:
// Zero out the left and bottom edges,
// leaving a right trapezoid with two sides on the axes and a vertex at the origin.
var shiftedPositions = new Vector2[] {
Vector2.zero,
new Vector2(0, vertices[1].y - vertices[0].y),
new Vector2(vertices[2].x - vertices[1].x, vertices[2].y - vertices[3].y),
new Vector2(vertices[3].x - vertices[0].x, 0)
};
mesh.uv = shiftedPositions;
var widths_heights = new Vector2[4];
widths_heights[0].x = widths_heights[3].x = shiftedPositions[3].x;
widths_heights[1].x = widths_heights[2].x = shiftedPositions[2].x;
widths_heights[0].y = widths_heights[1].y = shiftedPositions[1].y;
widths_heights[2].y = widths_heights[3].y = shiftedPositions[2].y;
mesh.uv2 = widths_heights;
I recently managed to come up with a generic solution to this problem for any type of quadrilateral. The calculations and GLSL maybe of help. There's a working demo in java (that runs on Android), but is compact and readable and should be easily portable to unity or iOS: http://www.bitlush.com/posts/arbitrary-quadrilaterals-in-opengl-es-2-0
In case anyone's still interested, here's a C# implementation that takes a quad defined by the clockwise screen verts (x0,y0) (x1,y1) ... (x3,y3), an arbitrary pixel at (x,y) and calculates the u and v of that pixel. It was originally written to CPU-render an arbitrary quad to a texture, but it's easy enough to split the algorithm across CPU, Vertex and Pixel shaders; I've commented accordingly in the code.
float Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, A, B, C;
//These are all uniforms for a given quad. Calculate on CPU.
Ax = (x3 - x0) - (x2 - x1);
Bx = (x0 - x1);
Cx = (x2 - x1);
Dx = x1;
Ay = (y3 - y0) - (y2 - y1);
By = (y0 - y1);
Cy = (y2 - y1);
Dy = y1;
float ByCx_plus_AyDx_minus_BxCy_minus_AxDy = (By * Cx) + (Ay * Dx) - (Bx * Cy) - (Ax * Dy);
float ByDx_minus_BxDy = (By * Dx) - (Bx * Dy);
A = (Ay*Cx)-(Ax*Cy);
//These must be calculated per-vertex, and passed through as interpolated values to the pixel-shader
B = (Ax * y) + ByCx_plus_AyDx_minus_BxCy_minus_AxDy - (Ay * x);
C = (Bx * y) + ByDx_minus_BxDy - (By * x);
//These must be calculated per-pixel using the interpolated B, C and x from the vertex shader along with some of the other uniforms.
u = ((-B) - Mathf.Sqrt((B*B-(4.0f*A*C))))/(A*2.0f);
v = (x - (u * Cx) - Dx)/((u*Ax)+Bx);
Tessellation solves this problem. Subdividing quad vertex adds hints to interpolate pixels.
Check out this link.
https://www.youtube.com/watch?v=8TleepxIORU&feature=youtu.be
I had similar question ( https://gamedev.stackexchange.com/questions/174857/mapping-a-texture-to-a-2d-quadrilateral/174871 ) , and at gamedev they suggested using imaginary Z coord, which I calculate using the following C code, which appears to be working in general case (not just trapezoids):
//usual euclidean distance
float distance(int ax, int ay, int bx, int by) {
int x = ax-bx;
int y = ay-by;
return sqrtf((float)(x*x + y*y));
}
void gfx_quad(gfx_t *dst //destination texture, we are rendering into
,gfx_t *src //source texture
,int *quad // quadrilateral vertices
)
{
int *v = quad; //quad vertices
float z = 20.0;
float top = distance(v[0],v[1],v[2],v[3]); //top
float bot = distance(v[4],v[5],v[6],v[7]); //bottom
float lft = distance(v[0],v[1],v[4],v[5]); //left
float rgt = distance(v[2],v[3],v[6],v[7]); //right
// By default all vertices lie on the screen plane
float az = 1.0;
float bz = 1.0;
float cz = 1.0;
float dz = 1.0;
// Move Z from screen, if based on distance ratios.
if (top<bot) {
az *= top/bot;
bz *= top/bot;
} else {
cz *= bot/top;
dz *= bot/top;
}
if (lft<rgt) {
az *= lft/rgt;
cz *= lft/rgt;
} else {
bz *= rgt/lft;
dz *= rgt/lft;
}
// draw our quad as two textured triangles
gfx_textured(dst, src
, v[0],v[1],az, v[2],v[3],bz, v[4],v[5],cz
, 0.0,0.0, 1.0,0.0, 0.0,1.0);
gfx_textured(dst, src
, v[2],v[3],bz, v[4],v[5],cz, v[6],v[7],dz
, 1.0,0.0, 0.0,1.0, 1.0,1.0);
}
I'm doing it in software to scale and rotate 2d sprites, and for OpenGL 3d app you will need to do it in pixel/fragment shader, unless you will be able to map these imaginary az,bz,cz,dz into your actual 3d space and use the usual pipeline. DMGregory gave exact code for OpenGL shaders: https://gamedev.stackexchange.com/questions/148082/how-can-i-fix-zig-zagging-uv-mapping-artifacts-on-a-generated-mesh-that-tapers
I came up with this issue as I was trying to implement a homography warping in OpenGL. Some of the solutions that I found relied on a notion of depth, but this was not feasible in my case since I am working on 2D coordinates.
I based my solution on this article, and it seems to work for all cases that I could try. I am leaving it here in case it is useful for someone else as I could not find something similar. The solution makes the following assumptions:
The vertex coordinates are the 4 points of a quad in Lower Right, Upper Right, Upper Left, Lower Left order.
The coordinates are given in OpenGL's reference system (range [-1, 1], with origin at bottom left corner).
std::vector<cv::Point2f> points;
// Convert points to homogeneous coordinates to simplify the problem.
Eigen::Vector3f p0(points[0].x, points[0].y, 1);
Eigen::Vector3f p1(points[1].x, points[1].y, 1);
Eigen::Vector3f p2(points[2].x, points[2].y, 1);
Eigen::Vector3f p3(points[3].x, points[3].y, 1);
// Compute the intersection point between the lines described by opposite vertices using cross products. Normalization is only required at the end.
// See https://leimao.github.io/blog/2D-Line-Mathematics-Homogeneous-Coordinates/ for a quick summary of this approach.
auto line1 = p2.cross(p0);
auto line2 = p3.cross(p1);
auto intersection = line1.cross(line2);
intersection = intersection / intersection(2);
// Compute distance to each point.
for (const auto &pt : points) {
auto distance = std::sqrt(std::pow(pt.x - intersection(0), 2) +
std::pow(pt.y - intersection(1), 2));
distances.push_back(distance);
}
// Assumes same order as above.
std::vector<cv::Point2f> texture_coords_unnormalized = {
{1.0f, 1.0f},
{1.0f, 0.0f},
{0.0f, 0.0f},
{0.0f, 1.0f}
};
std::vector<float> texture_coords;
for (int i = 0; i < texture_coords_unnormalized.size(); ++i) {
float u_i = texture_coords_unnormalized[i].x;
float v_i = texture_coords_unnormalized[i].y;
float d_i = distances.at(i);
float d_i_2 = distances.at((i + 2) % 4);
float scale = (d_i + d_i_2) / d_i_2;
texture_coords.push_back(u_i*scale);
texture_coords.push_back(v_i*scale);
texture_coords.push_back(scale);
}
Pass the texture coordinates to your shader (use vec3). Then:
gl_FragColor = vec4(texture2D(textureSampler, textureCoords.xy/textureCoords.z).rgb, 1.0);
thanks for answers, but after experimenting i found a solution.
two triangles on the left has uv (strq) according this and two triangles on the right are modifed version of this perspective correction.
Numbers and shader:
tri1 = [Vec2(-0.5, -1), Vec2(0.5, -1), Vec2(1, 1)]
tri2 = [Vec2(-0.5, -1), Vec2(1, 1), Vec2(-1, 1)]
d1 = length of top edge = 2
d2 = length of bottom edge = 1
tri1_uv = [Vec4(0, 0, 0, d2 / d1), Vec4(d2 / d1, 0, 0, d2 / d1), Vec4(1, 1, 0, 1)]
tri2_uv = [Vec4(0, 0, 0, d2 / d1), Vec4(1, 1, 0, 1), Vec4(0, 1, 0, 1)]
only right triangles are rendered using this glsl shader (on left is fixed pipeline):
void main()
{
gl_FragColor = texture2D(colormap, vec2(gl_TexCoord[0].x / glTexCoord[0].w, gl_TexCoord[0].y);
}
so.. only U is perspective and V is linear.
Working with box2d and cocos2d, I've calculated two vectors:
one is the vector pointing in the direction the car is travelling. (center of the vehicle to it's north point).
two is the vector pointing in the direction of the target. (center to the vehicle to the target center.)
I need to rotate the vehicle to also point in the direction of the target. This is done by setting the vehicle's steering either 90 degrees to turn right or -90 to turn left.
At the moment i'm querying the box2d body of the vehicle for its angle, then calculating the angle of the target direction vector using:
if(vector.y == 0){ vector.y = 0.000001f; }
float baseRadians = atan(vector.x/vector.y);
if(vector.y < 0){ baseRadians += PI_CONSTANT; } //Adjust for -Y
return -1 * baseRadians;
The following is crude and where I need help...!
I then compare the angle of the vehicle against the angle returned from the direction vector and set the steering as follows:
if(vehicleAngle < targetAngle)
{
steeringInput = -90.0;
}
else if(vehicleAngle > targetAngle)
{
steeringInput = 90.0;
}
else
{
steeringInput = 0.0;
}
The problem is two fold:
The target angle jumps from -4.71 to 1.57 radians, causing the vehicle to switch to the wrong direction
The vehicle angle is continous and keeps increasing rather than staying within a range.
Is there a way to get both the target vector and the vehicle angle in a set range where, for example, if the target angle is 0-360 and the van angle is 0-360, i can then compare them accurately?
Any other way to do this...? Any help appreciated!
Use atan2 instead of atan in your code. It gets the angle from one object to another.
Using the atan2f function instead I managed to get the vehicle vector angle and target vector angle.
my new implementation is as follows:
//get the direction the vehicle is facing and the direction the target is facing
CGPoint vehicleDirectionVector = ccpNormalize(ccpSub(ccp(vehicleBonnetTip.x*PTM_RATIO, vehicleBonnetTip.y*PTM_RATIO), ccp(vehicleCenter.x*PTM_RATIO, vehicleCenter.y*PTM_RATIO)));
CGPoint targetDirectionVector = ccpNormalize(ccpSub(ccp(origTarget.x, origTarget.y), ccp(vehicleCenter.x*PTM_RATIO, vehicleCenter.y*PTM_RATIO)));
vehicleAngle = atan2f(vehicleDirectionVector.x,vehicleDirectionVector.y);
targetAngle = atan2f(targetDirectionVector.x,targetDirectionVector.y);
if ( targetAngle < -M_PI / 2 && vehicleAngle > M_PI / 2 ) targetAngle += M_PI * 2;
if ( targetAngle > M_PI / 2 && vehicleAngle < -M_PI / 2 ) targetAngle -= M_PI * 2;
if ( vehicleAngle < -M_PI / 2 && targetAngle > M_PI / 2 ) vehicleAngle += M_PI * 2;
if ( vehicleAngle > M_PI / 2 && targetAngle < -M_PI / 2 ) vehicleAngle -= M_PI * 2;
CCLOG(#"vehicle angle is %f, target angle is %f", vehicleAngle, targetAngle);
Then to set my steering, crudely (i'm still working on this one..) i do:
if(vehicleAngle < targetAngle)
{
steeringDegrees = 90.0;
}
else if(vehicleAngle > targetAngle)
{
steeringDegrees = -90.0;
}
else
{
steeringDegrees = 0.0;
}
I've implemented the spiral GLSL shader described in this question in HLSL, but the results are not the same. I think it's because of the mod function in GLSL that I've translated to fmod in HLSL. I suspect that this problem only happens when we have negative numbers in the input of the fmod function.
I've tried replacing the call to mod by a call to a function that I've made which does what is described in the GLSL documentation and it works:
mod returns the value of x modulo y. This is computed as x - y * floor(x/y).
The working code I use instead of fmod is:
float mod(float x, float y)
{
return x - y * floor(x/y)
}
By contrast to GLSL mod, MSDN says the HLSL fmod function does this:
The floating-point remainder is calculated such that x = i * y + f, where i is an integer, f has the same sign as x, and the absolute value of f is less than the absolute value of y.
I've used an HLSL to GLSL converter, and the fmod function is translated as mod. However, I don't know if I can assume that mod translates to fmod.
Questions
What are the differences between GLSL mod and HLSLfmod?
How can I translate MSDN's cryptic description of fmod to a pseudo-code implementation?
GLSL Shader
uniform float time;
uniform vec2 resolution;
uniform vec2 aspect;
void main( void ) {
vec2 position = -aspect.xy + 2.0 * gl_FragCoord.xy / resolution.xy * aspect.xy;
float angle = 0.0 ;
float radius = length(position) ;
if (position.x != 0.0 && position.y != 0.0){
angle = degrees(atan(position.y,position.x)) ;
}
float amod = mod(angle+30.0*time-120.0*log(radius), 30.0) ;
if (amod<15.0){
gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
} else{
gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 );
}
}
HLSL Shader
struct Psl_VertexShaderInput
{
float3 pos : POSITION;
};
struct Psl_VertexShaderOutput
{
float4 pos : POSITION;
};
struct Psl_PixelShaderOutput
{
float4 Output0 : COLOR0;
};
float3 psl_positionOffset;
float2 psl_dimension;
Psl_VertexShaderOutput Psl_VertexShaderFunction(Psl_VertexShaderInput psl_input)
{
Psl_VertexShaderOutput psl_output = (Psl_VertexShaderOutput)0;
psl_output.pos = float4(psl_input.pos + psl_positionOffset, 1);
return psl_output;
}
float time : TIME;
float2 resolution : DIMENSION;
Psl_PixelShaderOutput Psl_PixelShaderFunction(float2 pos : VPOS)
{
Psl_PixelShaderOutput psl_output = (Psl_PixelShaderOutput)0;
float2 aspect = float2(resolution.x / resolution.y, 1.0);
float2 position = -aspect.xy + 2.0 * pos.xy / resolution.xy * aspect.xy;
float angle = 0.0;
float radius = length(position);
if (position.x != 0.0 && position.y != 0.0)
{
angle = degrees(atan2(position.y, position.x));
}
float amod = fmod((angle + 30.0 * time - 120.0 * log(radius)), 30.0);
if (amod < 15.0)
{
psl_output.Output0 = float4(0.0, 0.0, 0.0, 1.0);
return psl_output;
}
else
{
psl_output.Output0 = float4(1.0, 1.0, 1.0, 1.0);
return psl_output;
}
}
technique Default
{
pass P0
{
VertexShader = compile vs_3_0 Psl_VertexShaderFunction();
PixelShader = compile ps_3_0 Psl_PixelShaderFunction();
}
}
As you've noted, they're different. The GLSL mod will always have the same sign as y rather than x. Otherwise it's the same -- a value f such that x = i*y + f where i is an integer and |f| < |y|. If you're trying to make a repeating pattern of some kind, the GLSL mod is generally what you want.
For comparison, the HLSL fmod is equivalent to x - y * trunc(x/y). They're the same when x/y is positive, different when x/y is negative.
I'm trying to port and implement an easing function I found
EDIT
: I pasted in the wrong easing function, Sorry! Here is the correct one:
Math.easeOutQuart = function (t, b, c, d) {
t /= d;
t--;
return -c * (t*t*t*t - 1) + b;
};
The language i'm using is not Flash or Actionscript. Here is my code:
ease:{outquart:{function(t as float,b as float,c as float,d as float) as float
t=t/d
t=t-1
return -c * (t*t*t*t - 1) + b
end function}}
I'm calling the function in a loop with:
EDIT2 - the calling function.
m.move is set to 1 or -1 for direction to move, or -5 +5 to move by 5 lengths.
setspritemoves is called as often as possible, currently it is as fast as the system can call, but I could trigger the call on a millisecond timer.
setspritemoves:function()
if m.move=1 then
m.duration=1
if m.ishd then
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]+m.move*324
next i
else
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]+m.move*224
next i
end if
else if m.move=5 then
m.duration=5
if m.ishd then
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]+m.move*324
next i
else
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]+m.move*224
next i
end if
else if m.move=-1 then
m.duration=1
if m.ishd then
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]-m.move*324
next i
else
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]-m.move*224
next i
end if
else if m.move=-5 then
m.duration=5
if m.ishd then
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]-m.move*324
next i
else
for i=0 to m.spriteposx.count()-1
m.moveto[i]=m.spriteposx[i]-m.move*224
next i
end if
end if
end function
m.moveto[i] is the destination x coordinate, m.time is an integer I increment, m.duration is what I assume to be the amount of time I want the change to take to complete, m.spriteposx is the current position of the object I'm moving. [i] is the current sprite.
What should the increment value be for time what should the duration be, if I want to move 345 pixels in 1/2 second?
In all my experiments, I either overshoot by a huge factor, or only move a few pixels.
currently m.time is incremented by 1 every iteration, and m.duration is 100. I"ve tried all kinds of values and none seems to work consistently.
Why haven't you copied the logic across 1-1? The tween is a simple algorithm, it simply maps co-ordinates from b to b+c in a quartic fashion, i.e. b + c*t^4 where t gets values in the interval [0,1]. You can see by substitution that when t=0 the value is the initial value, b, and as t->1 the position is the required b+c.
That's the reason for the line t \= d, so d is an arbitrary duration and t, the time passed since the beginning of the animation gets a value in the aforementioned range. But you've done t=t-1 and taken negatives, etc. Why?
For example, moving 345px in 0.5s, you would have an initial position, b and c=345 assuming px is the units of measure. d=0.5 and you split the animation into intervals of a length of your choosing (depending on the power of the machine that will run the animation. Mobile devices aren't as powerful as desktops, so you choose a reasonable framerate under the circumstances). Let's say we choose 24 fps, so we split the interval into 0.5*24 = 12 frames, and call the function once every 1/24th of a second, each time with t taking values of 1/24, 2/24, etc. If it's more comfortable to work not in seconds but in frames, then d=12 and t takes values 1,2,...,12. The calculations are the same either way.
Here's a nice example (click the box to run the demo), feel free to fiddle with the values:
http://jsfiddle.net/nKhxw/
Bezier functions
Borrowed from http://blog.greweb.fr/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/
/**
* KeySpline - use bezier curve for transition easing function
* is inspired from Firefox's nsSMILKeySpline.cpp
* Usage:
* var spline = new KeySpline(0.25, 0.1, 0.25, 1.0)
* spline.get(x) => returns the easing value | x must be in [0, 1] range
*/
function KeySpline (mX1, mY1, mX2, mY2) {
this.get = function(aX) {
if (mX1 == mY1 && mX2 == mY2) return aX; // linear
return CalcBezier(GetTForX(aX), mY1, mY2);
}
function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
function C(aA1) { return 3.0 * aA1; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function CalcBezier(aT, aA1, aA2) {
return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
}
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function GetSlope(aT, aA1, aA2) {
return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
}
function GetTForX(aX) {
// Newton raphson iteration
var aGuessT = aX;
for (var i = 0; i < 4; ++i) {
var currentSlope = GetSlope(aGuessT, mX1, mX2);
if (currentSlope == 0.0) return aGuessT;
var currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
}
Aliases for common curves:
{
"ease": [0.25, 0.1, 0.25, 1.0],
"linear": [0.00, 0.0, 1.00, 1.0],
"ease-in": [0.42, 0.0, 1.00, 1.0],
"ease-out": [0.00, 0.0, 0.58, 1.0],
"ease-in-out": [0.42, 0.0, 0.58, 1.0]
}
Should be easy to make your own curves...
Thank you, Jonny!
Here is how to implement Bezier easing functions: C or Objective-C for iOS
// APPLE ORIGINAL TIMINGS:
// linear (0.00, 0.00), (0.00, 0.00), (1.00, 1.00), (1.00, 1.00)
// easeIn (0.00, 0.00), (0.42, 0.00), (1.00, 1.00), (1.00, 1.00)
// easeOut (0.00, 0.00), (0.00, 0.00), (0.58, 1.00), (1.00, 1.00)
// easeInEaseOut (0.00, 0.00), (0.42, 0.00), (0.58, 1.00), (1.00, 1.00)
// default (0.00, 0.00), (0.25, 0.10), (0.25, 1.00), (1.00, 1.00)
+(double)defaultEase_Linear:(double)t
{
return t;
}
// Замедление в начале
+(double)defaultEase_In:(double)t
{
return [AnimationMath easeBezier_t:t
point0_x:0
point0_y:0
point1_x:0.42
point1_y:0
point2_x:1
point2_y:1
point3_x:1
point3_y:1];
}
// Замедление в конце
+(double)defaultEase_Out:(double)t
{
return [AnimationMath easeBezier_t:t
point0_x:0
point0_y:0
point1_x:0
point1_y:0
point2_x:0.58
point2_y:1
point3_x:1
point3_y:1];
}
+(double)defaultEase_InOut:(double)t
{
return [AnimationMath easeBezier_t:t
point0_x:0
point0_y:0
point1_x:0.42
point1_y:0
point2_x:0.58
point2_y:1
point3_x:1
point3_y:1];
}
+(double)defaultEase_default:(double)t
{
return [AnimationMath easeBezier_t:t
point0_x:0
point0_y:0
point1_x:0.25
point1_y:0.1
point2_x:0.25
point2_y:1.0
point3_x:1
point3_y:1];
}
// For *better understanding* there is p1 and p2, because it is a Bezier curve from 0,0 to 1,0. So, you can remove p1 and p2 from this method, it is just for better understanding what's going on here
double ease_bezier_A(double aA1, double aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
double ease_bezier_B(double aA1, double aA2) { return 3.0 * aA2 - 6.0 * aA1; }
double ease_bezier_C(double aA1) { return 3.0 * aA1; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
double ease_bezier_calc(double aT, double aA1, double aA2) {
return ((ease_bezier_A(aA1, aA2)*aT + ease_bezier_B(aA1, aA2))*aT + ease_bezier_C(aA1))*aT;
}
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
double ease_bezier_get_slope(double aT, double aA1, double aA2) {
return 3.0 * ease_bezier_A(aA1, aA2)*aT*aT + 2.0 * ease_bezier_B(aA1, aA2) * aT + ease_bezier_C(aA1);
}
double ease_bezier_get_t_for_x(double aX, double mX1, double mX2) {
// Newton raphson iteration
double aGuessT = aX;
for (int i = 0; i < 4; ++i) {
double currentSlope = ease_bezier_get_slope(aGuessT, mX1, mX2);
if (currentSlope == 0.0) return aGuessT;
double currentX = ease_bezier_calc(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
// Objective-C
// For ***better understanding*** there is p1 and p2, because it is a Bezier curve from 0,0 to 1,0. So, you can remove p1 and p2 from this method, it is just for better understanding what's going on here
// p1_x always = 0
// p1_y always = 0
// p2_x always = 1.0
// p2_y always = 1.0
+(double)easeBezier_t:(double)t
point0_x:(double)point0_x point0_y:(double)point0_y
point1_x:(double)point1_x point1_y:(double)point1_y
point2_x:(double)point2_x point2_y:(double)point2_y
point3_x:(double)point3_x point3_y:(double)point3_y
{
if (point0_x != 0 || point0_y != 0 || point3_x != 1 || point3_y != 1) {
[NSException raise:#"Error! Your bezier is wrong!!!" format:#""];
}
double v = ease_bezier_calc(ease_bezier_get_t_for_x(t, point1_x, point2_x), point1_y, point2_y);
return v;
}