Spline path - prevent overshoot - c#

I have a NavAgent that follows the mouse cursor. A line renderer draws the nav path from a fixed starting point to the cursor with some smoothing achieved by what - with my limited math brain - I understand to be a cubic Hermit spline, adapted from this great Wikibook page:
IEnumerator IDrawPath(Vector3 endPos)
{
while (drawLine)
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 1500))
{
transform.position = endPos;
nav.SetDestination(hit.point);
line.positionCount = numberOfPoints * (nav.path.corners.Length - 1);
for (int j = 0; j < nav.path.corners.Length - 1; j++)
{
p0 = nav.path.corners[j];
p1 = nav.path.corners[j + 1];
if (j > 0)
{
m0 = 0.5f * (nav.path.corners[j + 1] - nav.path.corners[j - 1]);
}
else
{
m0 = nav.path.corners[j + 1] - nav.path.corners[j];
}
if (j < nav.path.corners.Length - 2)
{
m1 = 0.5f * (nav.path.corners[j + 2] - nav.path.corners[j]);
}
else
{
m1 = nav.path.corners[j + 1] - nav.path.corners[j];
}
pointStep = 1.0f / numberOfPoints;
if (j == nav.path.corners.Length - 2)
{
pointStep = 1.0f / (numberOfPoints - 1.0f);
}
for (int i = 0; i < numberOfPoints; i++)
{
t = i * pointStep;
position = (2.0f * t * t * t - 3.0f * t * t + 1.0f) * p0
+ (t * t * t - 2.0f * t * t + t) * m0
+ (-2.0f * t * t * t + 3.0f * t * t) * p1
+ (t * t * t - t * t) * m1;
line.SetPosition(i + j * numberOfPoints,
position);
}
}
}
yield return new WaitForFixedUpdate();
}
}
Sometimes the line 'overshoots' an optimal curve position and doubles back on itself:
I'd like the line to proceed smoothly round those corners instead.
From reading up on splines from various sources I gather this behaviour is correlated to the values of the tangents being calculated (the variables M0 and M1 in this case). I have adjusted these without success; larger values cause the line to stray away from the path points (and increase the size of the overshoot), smaller values cause a 'knot' rather than a loop as the resultant positions are very close together.
I tried filtering out some of the extreme points, hoping this might lead to a smoother line by adding some logic to say if the angle of the next point is above a certain threshold don't add it to the line renderer positions but this yields some odd behaviour. I've tried filtering out points that are very close together, again leading to some odd results.
This is probably simple stuff for a math wizz but I'm all out of ideas. How can I avoid the line doubling back on itself. I've readthrough Monotone cubic interpolation which seems promising but frankly it's way over my head. Can the calculation above be adjusted to do this, or do I need to look for another way?

Related

Parsing LSM6DSL raw values

I'm trying to parse the values given from a device with a LSM6DSL chip (gyroscopic and acc.) and I'm having a hard time parsing the data properly for positioning and angle.
From the vendor I've received the information that the unit is running on a resolution of 2000 for the gyro, 8g for the acc.
I receive the data in bytes that are converted by the following to shorts;
public int[] BufferToMotionData(byte[] buffer, int segments = 2)
{
int[] motionDataArray = new int[segments * 3];
int offset = Constants.BufferSizeImage + Constants.CommandLength;
for (int i = 0; i < 6; i++)
{
motionDataArray[i] = BitConverter.ToInt16(buffer, offset + (i * 2));
if (motionDataArray[i] >= Int16.MaxValue)
motionDataArray[i] -= 65535;
}
return motionDataArray;
}
(Edit; Cleaned up version)
This returns values in the range of (example) 961, -16223, -1635, 664, -269, -597.
According to the spec sheet I'm supposed to multiply each vector with it's corresponding value.. * 70f for gyro, .448f for acc.
From the documentation I understand that for the G forces these are in milliG's and gyro in millidegrees per sec?
// Gyro X,Y,Z
gx = Mathf.Deg2Rad * (motionData[0] * 70f / 1000f);
gy = Mathf.Deg2Rad * (motionData[1] * 70f / 1000f);
gz = Mathf.Deg2Rad * (motionData[2] * 70f / 1000f);
// Acc X,Y,Z
ax = motionData[3] * 0.488f / 1000f;
ay = motionData[4] * 0.488f / 1000f;
az = motionData[5] * 0.488f / 1000f;
Update(gx, gy, gz, ax, ay, az);
Update(..) is Madgwick's quaternion formul, although for velocity I use the acceleration vectors.
G force values that I'm getting at this moment after calculation;
X 0.047824 Y -0.320128 Z 0.006344
X 0.07076 Y -0.2562 Z 0.020008
X 0.099552 Y -0.063928 Z -0.13664
These look awfully low, and if applied as velocity it just runs off in a given direction, I know I'm missing a gravity correct although not entirely sure how to apply this.
I'm under the assumption that I do not need to apply drag to my velocity vector since values should be negated by the acceleration values received?
Anyone with experience with this type of chip and actually applying the values to yaw/pitch/roll (or quaternion) and applying the G forces as linear acceleration.
By looking on existing code on GitHub, it's looks like the sensitivity factor for 8g is 244 µg/digit and not 488 µg/digit as you coded it.
Also it look's like raw values are shifted and are in [-r/2,r/2] instead of [0, r]. So you have to add 500µg or 500µdps to it. (But maybe it's linked to a uint/int issue, anyway are you sure about the endianness?)
See here for acc data and here for gyro data.
Based on that, the code should look likes this:
// Gyro X,Y,Z (in rad/s)
gx = Mathf.Deg2Rad * (motionData[0] * 70000f + 500) / 1000000;
gy = Mathf.Deg2Rad * (motionData[1] * 70000f + 500) / 1000000;
gz = Mathf.Deg2Rad * (motionData[2] * 70000f + 500) / 1000000;
// Acc X,Y,Z (in g)
ax = (motionData[3] * 244f + 500) / 1000000;
ay = (motionData[4] * 244f + 500) / 1000000;
az = (motionData[5] * 244f + 500) / 1000000;
Update(gx, gy, gz, ax, ay, az);

Make an 8 by 8 grid have the correct FEN notation. Like a Chess Board

i made a 8 by 8 grid in Unity just by using DrawLine and for loops.
My next step is to make each box have its official combination, for example the bottom-left should be 1a and the top-left should be 8a...
My plan was making again for loops inside the existing loops but it just gives errors.
Could someone give me some tips on how this could be achieved
private void DrawChessboard()
{
//8 units of 1 meter to the right
Vector3 widthLine = Vector3.right * 8;
//8 units of 1 meter up
Vector3 heightLine = Vector3.forward * 8;
//makes the 8 by 8
for(int i = 0; i <= 8; i++)
{
Vector3 start = Vector3.forward * i;
Debug.DrawLine(start, start + widthLine);
for (int j = 0; j <= 8; j++)
{
start = Vector3.right * j;
Debug.DrawLine(start, start + heightLine);
}
}
//This draws the selection
if (selectionX >= 0 && selectionY >= 0)
{
Debug.DrawLine(
//Bottom left to top right
Vector3.forward * selectionY + Vector3.right * selectionX,
//this is the end point. +1 to make it diagonal
Vector3.forward * (selectionY + 1) + Vector3.right * (selectionX + 1));
Debug.DrawLine(
//Bottom left to top right
Vector3.forward * (selectionY +1) + Vector3.right * selectionX,
//this is the end point. +1 to make it diagonal
Vector3.forward * selectionY + Vector3.right * (selectionX + 1));
}
}
I'm still not sure where your method gets called exactly but since you use Debug.DrawLine I guess it is some kind of editor method or script. So I'll make my example in OnDrawGizmos
As said you can use Handles.Label(position, "Text") to draw a text as debug label in the SceneView.
In order to get the correct chars I simply use
nextChar = (char)(currentChar + 1);
(To make it complete I just also added the black and white fields)
so it might look like
using UnityEditor;
using UnityEngine;
public class ChessFieldDebug : MonoBehaviour
{
private void OnDrawGizmos()
{
//8 units of 1 meter to the right
var widthLine = Vector3.right * 8;
//8 units of 1 meter up
var heightLine = Vector3.forward * 8;
const char firstLetter = 'a';
// Only for you I also added black and white fields
var isBlackField = false;
var black = new Color(0, 0, 0, 0.25f);
var white = new Color(1, 1, 1, 0.25f);
//makes the 8 by 8
//rows
for (var i = 0; i <= 8; i++)
{
var start = Vector3.forward * i;
Debug.DrawLine(start, start + widthLine);
//colums
for (var j = 0; j <= 8; j++)
{
var currentLatter = (char)(firstLetter + j);
start = Vector3.right * j;
Debug.DrawLine(start, start + heightLine);
// this flag alternates between black and white
isBlackField = !isBlackField;
// Since you draw the last line but don't want a field added skip if over 8
if (i >= 8 || j >= 8) continue;
var centerOfField = Vector3.forward * (i + 0.5f) + Vector3.right * (j + 0.5f);
// Draw text label on fields with colum char + row index
Handles.Label(centerOfField, currentLatter.ToString() + (i + 1));
Gizmos.color = isBlackField ? black : white;
Gizmos.DrawCube(Vector3.forward * (i + 0.5f) + Vector3.right * (j + 0.5f), new Vector3(1, 0.01f, 1));
}
}
}
}
Result
Make sure to either put this in an Editor folder or use pre-processor tags arround like
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
public class ChessFieldDebug : MonoBehaviour
{
#if UNITY_EDITOR
private void OnDrawGizmos()
{
// ...
}
#endif
}
to avoid Exception when you build your project

How are GDI+ functions so fast?

I am trying to recreate very simple GDI+ functions, such as scaling and rotating an image. The reason is that some GDI functions can't be done on multiple threads (I found a work around using processes but didn't want to get into that), and processing thousands of images on one thread wasn't nearly cutting it.
Also my images are grayscale, so a custom function would only have to worry about one value instead of 4.
No matter what kind of function I try to recreate, even when highly optimized, it is always SEVERAL times slower, despite being greatly simplified compared to what GDI is doing (I am operating on a 1D array of bytes, one byte per pixel)
I thought maybe the way I was rotating each point could be the difference, so I took it out completely, and basically had a function that goes through each pixel and just sets it to what it already is, and that was only roughly tied with the speed of GDI, even though GDI was doing an actual rotation and changing 4 different values per pixel.
What makes this possible? Is there a way to match it using your own function?
The GDI+ code is written in C/C++, or possibly even partially in assembly. Some GDI+ calls may use GDI, an old and well optimized API. You will find it difficult to match the performance, even if you know all the pixel manipulation tricks.
I am adding my own answer along with my code to help anyone else who may be looking to do this.
From a combination of pointers and using an approximation of Sine and Cosine instead of calling an outside function for the rotation, I have come pretty darn close to reaching GDI speeds. No outside functions are called at all.
It still takes about 50% more time than GDI, but my earlier implementation took over 10 times longer than GDI. And when you consider multi threading, this method can be 10 times faster than GDI. This function can rotate a 300x400 picture in 3 milliseconds on my machine.
Keep in mind that this is for grayscale images and each byte in the input array represents one pixel.
If you have any ideas to make it faster please share!
private unsafe byte[] rotate(byte[] input, int inputWidth, int inputHeight, int cx, int cy, double angle)
{
byte[] result = new byte[input.Length];
int
tx, ty, ix, iy, x1, y1;
double
px, py, fx, fy, sin, cos, v;
byte a, b;
//Approximate Sine and Cosine of the angle
if (angle < 0)
sin = 1.27323954 * angle + 0.405284735 * angle * angle;
else
sin = 1.27323954 * angle - 0.405284735 * angle * angle;
angle += 1.57079632;
if (angle > 3.14159265)
angle -= 6.28318531;
if (angle < 0)
cos = 1.27323954 * angle + 0.405284735 * angle * angle;
else
cos = 1.27323954 * angle - 0.405284735 * angle * angle;
angle -= 1.57079632;
fixed (byte* pInput = input, pResult = result)
{
byte* pi = pInput;
byte* pr = pResult;
for (int x = 0; x < inputWidth; x++)
for (int y = 0; y < inputHeight; y++)
{
tx = x - cx;
ty = y - cy;
px = tx * cos - ty * sin + cx;
py = tx * sin + ty * cos + cy;
ix = (int)px;
iy = (int)py;
fx = px - ix;
fy = py - iy;
if (ix < inputWidth && iy < inputHeight && ix >= 0 && iy >= 0)
{
//keep in array bounds
x1 = ix + 1;
y1 = iy + 1;
if (x1 >= inputWidth)
x1 = ix;
if (y1 >= inputHeight)
y1 = iy;
//bilinear interpolation using pointers
a = *(pInput + (iy * inputWidth + ix));
b = *(pInput + (y1 * inputWidth + ix));
v = a + ((*(pInput + (iy * inputWidth + x1)) - a) * fx);
pr = (pResult + (y * inputWidth + x));
*pr = (byte)(v + (((b + ((*(pInput + (y1 * inputWidth + x1)) - b) * fx)) - v) * fy));
}
}
}
return result;
}

2D Elastic Collisions 'Sticking' Issue

I have a simulation with multiple circles moving in 2D space.
There is collision detection between them, and the elastic collisions work 95% of the time. Occasionally however, when two balls hit each other, they stick to each other and overlap, often orbiting each other while being stuck together.
I'm unsure how to solve this problem.
My collision management function looks like this:
void manageCollision(Particle particleA, Particle particleB)
{
float distanceX = particleA.Position.X - particleB.Position.X;
float distanceY = particleA.Position.Y - particleB.Position.Y;
double collisionAngle = Math.Atan2(distanceY, distanceX);
double pA_magnitude = Math.Sqrt(particleA.Velocity.X * particleA.Velocity.X + particleA.Velocity.Y * particleA.Velocity.Y);
double pB_magnitude = Math.Sqrt(particleB.Velocity.X * particleB.Velocity.X + particleB.Velocity.Y * particleB.Velocity.Y);
double pA_direction = Math.Atan2(particleA.Velocity.Y, particleA.Velocity.X);
double pB_direction = Math.Atan2(particleB.Velocity.Y, particleB.Velocity.X);
double pA_newVelocityX = pA_magnitude * Math.Cos(pA_direction - collisionAngle);
double pA_newVelocityY = pA_magnitude * Math.Sin(pA_direction - collisionAngle);
double pB_newVelocityX = pB_magnitude * Math.Cos(pB_direction - collisionAngle);
double pB_newVelocityY = pB_magnitude * Math.Sin(pB_direction - collisionAngle);
double pA_finalVelocityX = ((particleA.Mass - particleB.Mass) * pA_newVelocityX + (particleB.Mass + particleB.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
double pB_finalVelocityX = ((particleA.Mass + particleA.Mass) * pA_newVelocityX + (particleB.Mass - particleA.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
double pA_finalVelocityY = pA_newVelocityY;
double pB_finalVelocityY = pB_newVelocityY;
particleA.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pA_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pA_finalVelocityY), (float)(Math.Sin(collisionAngle) * pA_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pA_finalVelocityY));
particleB.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pB_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pB_finalVelocityY), (float)(Math.Sin(collisionAngle) * pB_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pB_finalVelocityY));
}
Each ball or particle spawns with a random mass and radius.
The function is called within an update type of method, like this:
Particle pA = particles[i];
for (int k = i + 1; k < particles.Count(); k++)
{
Particle pB = particles[k];
Vector2 delta = pA.Position - pB.Position;
float dist = delta.Length();
if (dist < particles[i].Radius + particles[k].Radius && !particles[i].Colliding && !particles[k].Colliding)
{
particles[i].Colliding = true;
particles[k].Colliding = true;
manageCollision(particles[i], particles[k]);
particles[i].initColorTable(); // Upon collision, change the color
particles[k].initColorTable();
totalCollisions++;
}
else
{
particles[i].Colliding = false;
particles[k].Colliding = false;
}
}
This situation stems from the discrete computation and big step size of duration.
When you observe the objects with some time interval dt, you can observe some intersection between two circles and call your collision method but in the next time step they may still overlap although they are going in different directions after the collision in the previous step.
To reduce this effect, you can try a lower time step size so that the overlap ratio between objects may be reduced.
As a more complicated solution, you can keep a list of your collided objects for every step and during iterations you can check this list if current intersected circles had any "affairs" in the previous step.

How to return all points along a bezier curve?

I posted a previous question about generating a bezier curve based on only the start and end points, and I was able thanks to the answers in that create a bezier curve using the information I have.
This is the code that allows me to draw the types of curve that I want on a form.
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Random rnd = new Random();
Point startp = new Point(rnd.Next(0, this.Width), rnd.Next(0, this.Height));
Point endp = new Point(rnd.Next(0, this.Width), rnd.Next(0, this.Height));
int xMod = 0;
int yMod = 0;
if (startp.X > endp.X) {
xMod = -1;
} else {
xMod = 1;
}
if (startp.Y > endp.Y) {
yMod = 1;
} else {
yMod = -1;
}
Point control1p = new Point(endp.X + (rnd.Next(20, 50) * xMod), endp.Y + (rnd.Next(20, 50) * yMod));
Point control2p = new Point(endp.X + (rnd.Next(5, 20) * xMod), endp.Y + (rnd.Next(5, 20) * yMod));
Point[] pts = {
startp,
control1p,
control2p,
endp
};
Pen dashed_pen = new Pen(Color.Black, 0);
dashed_pen.DashStyle = Drawing2D.DashStyle.Dash;
for (int i = 0; i <= 2; i++) {
e.Graphics.DrawLine(dashed_pen, pts(i), pts(i + 1));
}
e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
Pen bez_pen = new Pen(Color.Black, 3);
e.Graphics.DrawBezier(bez_pen, pts(0), pts(1), pts(2), pts(3))
}
Is there a way, or can someone help me with returning all the points that form the curve? I'd like for each point of a curve calculated from those points to be returned in an array of points, but I'm having no luck figuring it out, and haven't been able to find a similar solution on stackoverflow or google in general.
Thanks.
What you want to do is to convert a Bezier Curve (Cubic from the looks of it) into a Polyline
Use the Equation on this page...Value of t should be between 0 to 1...Calculate all values of Bx(t) and By(t) by using the equation for values of t in increments of "0, 0.01, 0.02....1" (Convert them to integers of course) The smaller your increments, the more accurate your points will be.
Here's a C Sample of the DeCasteljau Algorithm (almost the same procedure, but its a bit optimized i believe) :)
Perfect algorithm for creating smooth Bezier curve with optimal number of points is described by Maxim Shemanarev on Anti-Grain Geometry page: Adaptive Subdivision of Bezier Curves.
It may help if you use a lerp or float t derivatives in-between the draw bezier. I've found it helps with accuracy; considering the number of float calcs .
I know this is an old post, but, having found none of the current answers all that satisfying, hopefully others will get some use out of the following:
using System.Collections.Generic;
using System.Drawing;
public List<Point> CubicBezierToPoints(Point P0, Point P1, Point P2, Point P3, double step = 0.01)
{
var pointList = new List<Point>();
for (var t = 0.00; t <= 1; t += step)
{
var x = Math.Pow(1 - t, 3) * P0.X + 3 * Math.Pow(1 - t, 2) * t * P1.X +
3 * (1 - t) * Math.Pow(t, 2) * P2.X + Math.Pow(t, 3) * P3.X;
var y = Math.Pow(1 - t, 3) * P0.Y + 3 * Math.Pow(1 - t, 2) * t * P1.Y +
3 * (1 - t) * Math.Pow(t, 2) * P2.Y + Math.Pow(t, 3) * P3.Y;
pointList.Add(new Point((int)x,(int)y));
}
return pointList;
}

Categories

Resources