I currently have code that will generate segment points and at each segment point will generate some points within a short cylinder around the point (in 3D, the segments positions all have a z value of 0.0F (however I'd like to have varying z-values that are in a line - i.e. it's still in a line, but in the line z = 3x for example), but the x, and y are randomised). However currently all the points generated are in a cylinder that is facing upwards, I want to be able to rotate the points such that the cylinder they are 'generated' in is facing in the direction between the two segments. Here's an image of what it should look like vs. what it currently looks like
I found this similar question about rotating points around an axis; I took the answer and used that code for my RotatePoints() function but it doesn't seem to work correctly and I'm not sure why. Below is my psuedo code, what would I need to do get this function working correctly? Is there a better way to do this? The points just need to be generated within a rotated cylinder so would a completely different method be more efficient and easier?
All I have is the location of each segment and each point stored as a Vector3 {x,y,z} in local space.
Psuedo-Code
double radius;
// Generates the positions where the points will be generated around
// These are just the x,y,z positions of the object in world space
Vector3[] segmentLocations = GenerateSegmentPositions(numSegments);
for (int i = 0; i < numSegments; i++) {
// Generates points in a cylinder facing up the +ve y-axis
// This works fine
Vector3[][] pointsAroundSegment = GeneratePoints(segmentLocations[i], radius);
if (i != numSegments - 1 && i > 0) {
// Generate a normalise direction vector for the new direction
Vector3 newDir = Vector3.Normalise(segmentLocations[i + 1] - segmentLocations[i]);
double theta = Vector3.AngleBetween(newDir - Vector3.Normalise(segmentLocations[i] - segmentLocations[i - 1]));
// Rotates points (this currently rotates the points so they 'should' be facing the new direction, I haven't yet modified this to face the halfway point)
// This doesn't work
pointsAroundSegment = RotatePoints(pointsAroundSegment, newDir, theta/2);
} else if (i == numSegments - 1) {
// Generate final point
// This works fine
pointsAboutSegment = GenerateFinalPoint(segmentLocations[i]);
}
}
// This is the actual rotation function
// RotatePoints() effectively just calls this for each point in the array
public static double[] Rotate(double x, double y, double z, double u, double v, double w, double theta) {
double[] c = new double[3];
c [0] = u * (u * x + v * y + w * z) * (1 - Math.Cos (theta)) + x * Math.Cos (theta) + (-w * y + v * z) * Math.Sin (theta);
c [1] = v * (u * x + v * y + w * z) * (1 - Math.Cos (theta)) + y * Math.Cos (theta) + (w * x - u * z) * Math.Sin (theta);
c [2] = w * (u * x + v * y + w * z) * (1 - Math.Cos (theta)) + z * Math.Cos (theta) + (-v * x + u * y) * Math.Sin (theta);
return c;
}
Answer courtesy of Poosh;
To rotate the point (x,y,z) about the line through (a,b,c) with the normalised (u^2 + v^2 + w^2 = 1) direction vector by the angle theta use the following function:
public static double[] Rotate(double x, double y, double z, double a, double b, double c, double nu, double nv, double nw, double theta) {
double[] rP = new double[3];
rP [0] = (a * (nv * nv + nw * nw) - nu * (b * nv + c * nw - nu * x - nv * y - nw * z)) * (1 - Math.Cos (theta)) + x * Math.Cos (theta) + (-c * nv + b * nw - nw * y + nv * z) * Math.Sin (theta);
rP [1] = (b * (nu * nu + nw * nw) - nv * (a * nu + c * nw - nu * x - nv * y - nw * z)) * (1 - Math.Cos (theta)) + y * Math.Cos (theta) + (c * nu - a * nw + nw * x - nu * z) * Math.Sin (theta);
rP [2] = (c * (nu * nu + nv * nv) - nw * (a * nu + b * nv - nu * x - nv * y - nw * z)) * (1 - Math.Cos (theta)) + z * Math.Cos (theta) + (-b * nu + a * nv - nv * x + nu * y) * Math.Sin (theta);
return rP;
}
Related
I have a 2d space with multiple objects(Lets call them B). Lets say object A our automated actor, he moves in a specific path and he has to shoot only the objects it can destroy. The other objects might or might not move.
I need to find the direction that I should fire the bullet that will collide with the object B. The bullet is moving with a different speed that object A and it has a specific lifetime.
I've tried to solve it with Quadratic but I always get infinity, is this a wrong approach?
Vector3 vectorFromVictim = bullet.Position - victim.Position;
float distanceToVictim = vectorFromVictim.Length();
double victimSpeed = victim.Position.Length();
double a = bulletSpeed * bulletSpeed - victimSpeed * victimSpeed;
double b = 2 * vectorFromVictim.Dot(victim.LinearVelocity);
double c = -distanceToVictim * distanceToVictim;
float t = (QuadraticSolver(a, b, c));
if (float.IsInfinity(t))
{
return;
}
interceptionPosition = victim.Position + victim.LinearVelocity * t;
if (t <= bulletLifetime)
{
ShootAtDirection(interceptionPosition);
}
Edit: My QuadraticSolver is this
double d = Math.Pow(b, 2) - (4 * a * c);
if (d < 0)
{
return float.PositiveInfinity;
}
float t;
if (d == 0)
{
t = (float) (-b / (2 * a));
if (float.IsNaN(t))
{
return float.PositiveInfinity;
}
return t;
}
t = (float) ((-b - Math.Sqrt(d)) / (2 * a));
float t2 = (float) ((-b + Math.Sqrt(d)) / (2 * a));
if (t < t2)
{
return t < 0 ? float.PositiveInfinity : t;
}
return t2 < 0 ? float.PositiveInfinity : t2;
B (target) coordinates are
bx + ux * t, by + uy * t
where ux, uy are components of B velocity vector
Bullet coordinates are
ax + v * cos(f) * t, ay + v * sin(f) * t
where v is bullet speed, f is directional angle (unknown yet)
ax + v * cos(f) * t = bx + ux * t
ay + v * sin(f) * t = y + uy * t
t * (v * cos(f) - ux) = bx - ax = dx
t * (v * sin(f) - uy) = bx - ax = dy
dx, dy is position difference, negated your vectorFromVictim
exclude t
dy * (v * cos(f) - ux) = dx * (v * sin(f) - uy)
dy * v * cos(f) - dy * ux = dx * v * sin(f) - dx * uy
v * (dy*cos(f) - dx*sin(f)) = dy * ux - dx * uy
let
g = atan2(dy, dx)
L = vectorFromVictim.Length
so
v * sin(g - f) = L * (dy * ux - dx * uy)
sin(g - f) = L/v * (dy * ux - dx * uy)
g - f = arcsin(L/v * (dy * ux - dx * uy) )
and finally
f = g - arcsin(L/v * (dy * ux - dx * uy) )
Quiclk Python test
import math
def aiming(ax, ay, bx, by, ux, uy, v):
dx = bx - ax
dy = by - ay
g = math.atan2(dy, dx)
L = math.hypot(dy, dx)
if (v * math.cos(ang) - ux):
t = dx / (v * math.cos(ang) - ux)
elif (v * math.sin(ang) - uy):
t = dy / (v * math.sin(ang) - uy)
else:
return None
coll_x = bx + ux * t
coll_y = by + uy * t
return ang, coll_x, coll_y
print(aiming(0, 0, 0, 1, 1, 0, 1.4142))
gives correct value 0.7854 = Pi/4 radians = 45 degrees, and point (1,1)
I have tried to adapt some code I came across to draw an equilateral triangle in c#
public void drawTriangle(PaintEventArgs e, int x, int y, int distance)
{
float angle = 0;
SolidBrush brs = new SolidBrush(Color.Green);
PointF[] p = new PointF[3];
p[0].X = x;
p[0].Y = y;
p[1].Y = (float)( x + distance * Math.Cos(angle + Math.PI / 3));
p[1].X = (float)( y + distance * Math.Sin(angle + Math.PI / 3));
p[2].Y = (float)( x + distance * Math.Cos(angle - Math.PI / 3));
p[2].X = (float)( y + distance * Math.Sin(angle - Math.PI / 3));
e.Graphics.FillPolygon(brs, p);
}
Unfortunately, this doesn't even come close. I have drawn equilateral triangles, but the points were always based on the centers of congruent circles. I am trying to find a simpler way. I am sure there must be an obvious problem with this code, but I am trying to learn the math needed as I go, so I don't know what it is. Thanks for your time.
Try this approach. I assume that for zero angle p[0] is left bottom vertex, p[1] is right bottom (the same horizontal).
(BTW, you have got strange mangling of Y/X)
p[0].X = x;
p[0].Y = y;
p[1].X = (float)( x + distance * Math.Cos(angle));
p[1].Y = (float)( y + distance * Math.Sin(angle));
p[2].X = (float)( x + distance * Math.Cos(angle + Math.PI / 3));
p[2].Y = (float)( y + distance * Math.Sin(angle + Math.PI / 3));
I'm trying to get a character to throw something in an arc at a target.
I know the vertex(x,y) and the target(x,y) and I want to get an arc from the origin(x,y) to the target with a max height of vertex.y
What I have is based off the vertex form of y = a(x-h)^2 + k
public static Vector3 parabola(Vector2 origin, Vector2 target, float height)
{
float dist = target.x - origin.x;
Vector2 vertex = new Vector2(origin.x + (dist / 2), origin.y + height);
//a = (y-k) / (x-h)^2
float a = (target.y - vertex.y) / ((target.x - vertex.x) * (target.x - vertex.x));
//b = (-h + -h) * a
float b = (-vertex.x + -vertex.x) * a;
//c = (h * h) * a + k
float c = (vertex.x * vertex.x) * a + vertex.y;
return new Vector3(a, b, c);
}
x += Time.DeltaTime;
float yPos = a * ((x - h) * (x - h)) + k;
This doesn't produce the correct arc. It's usually much too steep or much too shallow. Is my algebra wrong, or am I using the wrong approach?
Thanks
Here is a good solution: Wiki:Trajectory of a projectile.
Let's say I want to draw a rectangle with an angle inside a windows form.
I can do this with
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.RotateTransform(20);
e.Graphics.DrawRectangle(Pens.Black, 0, 0, e.ClipRectangle.Width, e.ClipRectangle.Height);
}
but this will just rotate the rectangle and thus the left and bottom part is missing.
But what I really want to achive is that I want to draw the biggest possible rectangle with a certain angle inside my form like this
What's the best way to do this?
We can find the rectangle at angle θ which touches all four sides. If we let W, H be the width and height of our window, th the angle in radians which are all known beforehand. Now let a, b be the width and height of the rectangle we are trying to fit in the box, these are the two values we wish to find. Let x0,y0 be the coordinates of the point in the center of the window x0=W/2, y0=H/2. One of the corners of the rectangle will be
x1 = x0 - 0.5 * a * sin(th) + 0.5 * b * cos(th)
y1 = y0 + 0.5 * a * cos(th) + 0.5 * b * sin(th)
the other point are similar with different signs. A bit of trigonometry will show this.
For the rectangle to touch the sides we want
a * sin(th) + b cos(th) = W
a * cos(th) + b sin(th) = H
this gives us a pair of simultaneous equations which we can solve. Multiply the first by sin(th) and the second by cos(th)
a * sin(th) sin(th) + b cos(th) sin(th) = W sin(th)
a * cos(th) cos(th) + b sin(th) cos(th) = H cos(th)
subtract
a ( sin(th) sin(th) - cos(th) cos(th) ) = W sin(th) - H cos(th)
divide by ( sin(th) sin(th) - cos(th) cos(th) ) gives
a = (W sin(th) - H cos(th)) / ( sin(th) sin(th) - cos(th) cos(th) )
A similar process gives
b = (H sin(th) - W cos(th)) / ( sin(th) sin(th) - cos(th) cos(th) )
Once we have the a and b we can calculate the corners of the rectangles and draw it.
I've put the code in a jsfiddle http://jsfiddle.net/SalixAlba/5jcT7/ the code is
// canvas and mousedown related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// save canvas size to vars b/ they're used often
var W = canvas.width;
var H = canvas.height;
var spinner = $( "#startAng" ).spinner({
spin: function( event, ui ) {
if ( ui.value > 180 ) {
$( this ).spinner( "value", ui.value - 360 );
draw();
return false;
} else if ( ui.value < -180 ) {
$( this ).spinner( "value", ui.value + 360 );
draw();
return false;
}
draw();
}
});
var angle = 10.0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
angle = $("#startAng").spinner("value");
var th = Math.PI*angle/180.0;
var S = Math.sin(th);
var C = Math.cos(th);
var S2 = S*S - C*C;
var a = (W*S-H*C)/S2;
var b = (H*S-W*C)/S2;
var x0 = W/2;
var y0 = H/2;
//alert("angle "+angle+"S "+S+" "+C+" "+a+" "+b);
var x1 = x0 - 0.5 * a * Math.sin(th) + 0.5 * b * Math.cos(th);
var y1 = y0 + 0.5 * a * Math.cos(th) + 0.5 * b * Math.sin(th);
var x2 = x0 - 0.5 * a * Math.sin(th) - 0.5 * b * Math.cos(th);
var y2 = y0 + 0.5 * a * Math.cos(th) - 0.5 * b * Math.sin(th);
var x3 = x0 + 0.5 * a * Math.sin(th) + 0.5 * b * Math.cos(th);
var y3 = y0 - 0.5 * a * Math.cos(th) + 0.5 * b * Math.sin(th);
var x4 = x0 + 0.5 * a * Math.sin(th) - 0.5 * b * Math.cos(th);
var y4 = y0 - 0.5 * a * Math.cos(th) - 0.5 * b * Math.sin(th);
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x4,y4);
ctx.lineTo(x3,y3);
ctx.lineTo(x1,y1);
ctx.closePath();
ctx.stroke();
}
$( ".ui-spinner-input" ).on( "spinchange", draw );
$( ".ui-spinner-input" ).on( "spinstop", draw );
$( "#baseRad" ).on( "spin", draw );
draw();
Note this does not necessarily give the largest rectangle as there may be larger one which touch two sides, also there is some limit on the possible angle.
I did some sketching to solve this problem. The problem is more about math than programming, but here's my idea. I did in paint but try to follow:
When you rotate the rectangle, you need to calculate the newWidth and newHeight for it. Make a new rectangle with those dimensions, and place it to the upper left corner, like your current rectangle. The alpha α stands for the degree you want to rotate. After rotating, you need to move this new rectangle to right, the amounth of X. Then you have the biggest possible rectangle positioned perfectly at your drawing area.
Here's some idea for the code. I haven't tested it:
private void Form1_Paint(object sender, PaintEventArgs e)
{
double angle = 20;
double width = Convert.ToDouble(e.ClipRectangle.Width),
height = Convert.ToDouble(e.ClipRectangle.Height),
h = Math.Sqrt((width*width) + (height*height)),
y = (width/h);
int newHeight = Convert.ToInt32(height*y),
newWidth = Convert.ToInt32(width*y),
x = Convert.ToInt32((Math.Sin(angle) * height);
e.Graphics.RotateTransform(angle);
e.Graphics.DrawRectangle(Pens.Black, x, 0, newWidth, newHeight);
}
I'd like to copy a roughly rectangular area to a rectangular area. Example:
Both areas are defined by their corner points. The general direction is kept (no flipping etc).
Simply rotating the source image does not work since opposing sides may be of different length.
So far I found no way to do this in pure C# (except manual pixel copying), so I guess I have to resort to the Windows API or some 3rd party library?
Since I could not find an answer, I wrote a naive implementation myself. It works reasonably well.
Examples
I drew all examples manually in Paint, so they are not very exact - it was just enough to test some basics.
a) Slight rotation.
Source:
Result:
b) Various sides
Source:
Result:
c) Perspective
Source:
Result:
Code
(it's specialized to my use case, but it should be easy to adapt):
// _Corners are, well, the 4 corners in the source image
// _Px is an array of pixels extracted from the source image
public void Rescale ()
{
RescaleImage (
_Corners[0],
_Corners[1],
_Corners[3],
_Corners[2],
100,
100);
}
private void RescaleImage (PointF TL, PointF TR, PointF LL, PointF LR, int sx, int sy)
{
var bmpOut = new Bitmap (sx, sy);
for (int x = 0; x < sx; x++) {
for (int y = 0; y < sy; y++) {
/*
* relative position
*/
double rx = (double) x / sx;
double ry = (double) y / sy;
/*
* get top and bottom position
*/
double topX = TL.X + rx * (TR.X - TL.X);
double topY = TL.Y + rx * (TR.Y - TL.Y);
double bottomX = LL.X + rx * (LR.X - LL.X);
double bottomY = LL.Y + rx * (LR.Y - LL.Y);
/*
* select center between top and bottom point
*/
double centerX = topX + ry * (bottomX - topX);
double centerY = topY + ry * (bottomY - topY);
/*
* store result
*/
var c = PolyColor (centerX, centerY);
bmpOut.SetPixel (x, y, c);
}
}
bmpOut.Save (_Path + "out5 rescale out.bmp");
}
private Color PolyColor (double x, double y)
{
// get fractions
double xf = x - (int) x;
double yf = y - (int) y;
// 4 colors - we're flipping sides so we can use the distance instead of inverting it later
Color cTL = _Px[(int) y + 1, (int) x + 1];
Color cTR = _Px[(int) y + 1, (int) x + 0];
Color cLL = _Px[(int) y + 0, (int) x + 1];
Color cLR = _Px[(int) y + 0, (int) x + 0];
// 4 distances
double dTL = Math.Sqrt (xf * xf + yf * yf);
double dTR = Math.Sqrt ((1 - xf) * (1 - xf) + yf * yf);
double dLL = Math.Sqrt (xf * xf + (1 - yf) * (1 - yf));
double dLR = Math.Sqrt ((1 - xf) * (1 - xf) + (1 - yf) * (1 - yf));
// 4 parts
double factor = 1.0 / (dTL + dTR + dLL + dLR);
dTL *= factor;
dTR *= factor;
dLL *= factor;
dLR *= factor;
// accumulate parts
double r = dTL * cTL.R + dTR * cTR.R + dLL * cLL.R + dLR * cLR.R;
double g = dTL * cTL.G + dTR * cTR.G + dLL * cLL.G + dLR * cLR.G;
double b = dTL * cTL.B + dTR * cTR.B + dLL * cLL.B + dLR * cLR.B;
Color c = Color.FromArgb ((int) (r + 0.5), (int) (g + 0.5), (int) (b + 0.5));
return c;
}
Generally speaking, what you want to do is map the destination coordinates to the source coordinates through a transform function:
for (int y = 0; y < destHeight; y++) {
for (x=0; x < destWidth; x++) {
Color c = Transform(x, y, sourceImage, sourceTransform);
SetPixel(destImage, x, y, c);
}
}
Let's assume that sourceTransform is an object that encapsulates a transformation from source to dest coordinates (and vice versa).
Working in dest coordinates will make it easier to avoid that curve in your retransformed source image and will allow you to better antialias, as you can map the corners of the dest pixel to the source image and sample within it and interpolate/extrapolate.
In your case you're going to have a set of linear equations that do the mapping - in this case this is known as quadrilateral warping - see this previous question.