C# - using a Spline Chart to map values - c#

I'm trying to use the .Net Chart object to interactively define a spline function that I use to map from one range of values to another. In other words, I have a 0-4095 range (x axis) that I want to convert to a 0-100 range (y axis) using a spline. I've successfully set up a chart that plots a spline through a group of points. The user can interactively move the points to get the desired function shape. Works great.
Now...once I have the spline like the user wants, how can I (using the spline function), find the corresponding y value for any x value?
I can't seem to find a way to do that. I know that the chart object is doing the calculation somewhere since it's plotting the spline...maybe they don't provide access to that.
The alternative is to make the spline calculations myself...I don't want to go there unless absolutely necessary.
Thanks.
Bryan

You need to find the value of "t" (tension parameter) that produces the desired value of x. if you are using the range of 0 to 1 the parameter "t" value will be somewhere near to 0.5. Once you know t you can calculate the corresponding value of y. Solve a cubic equation which will generate 3 values for "t" that will result in same value of x. Check the link below.
http://algorithmist.wordpress.com/2009/09/28/cardinal-splines-part-2/
Cardinal splines specify the tangents at interior points based on the vector from previous point to subsequent point. Each tangent is parallel to this vector and some multiple of its length. For example, the tangent direction at point P1 is parallel to the vector P2 – P0, or we could simply write something like T1 = s(P2 – P0) where s is a real number.
Check this part of code below where xtarget is the input value x.
Code:
for (Double t = 0; t<=1; t += 0.01)
{
s = (1 - t) / 2;
P(t)x = s(-t3 + 2t2 – t)P1X + s(-t3 + t2)P2X + (2t3 – 3t2 + 1)P2X + s(t3 – 2t2 + t)P3X + (-2t3 + 3t2)P3X + s(t3 – t2)P4X
P(t)y = s(-t3 + 2t2 – t)P1Y + s(-t3 + t2)P2Y + (2t3 – 3t2 + 1)P2Y + s(t3 – 2t2 + t)P3Y+ (-2t3 + 3t2)P3Y + s(t3 – t2)P4Y
if(P(t)x=>xtarget)
{
return P(t)y;
}
}
The above method will give the approximate point P(t)y on the curve.

Related

Check if a 3D Point lies on a given 3D line(between two 3D Points)

I have two 3D Points viz. (x1,y1,z1) and (x2,y2,z2) and a 3D Point (x,y,z).
I would like to know if (x,y,z) lies on the line connecting (x1,y1,z1) and (x2,y2,z2).
I tried the following algorithm:
if ((((x - x1) / x2-x1) == ((y - y1) / y2-y1)) && (((x - x1) / x2 - x1)
== ((z - z1) / z2- z1)) --> then,the 3D Line intersects (x,y,z)
But,what if my x1 = x2 (or) y1 = y2 (or) z1=z2? Then I would be getting an error saying "Division by zero" is not possible.
I would be glad,if someone can propose some alternative method.
Thanks in Advance.
I would use point-line distance measurement, and return true if the distance is less than some error threshold close to zero.
To elaborate, we are using a line made of two points, p1 and p2. We want to know if p3 is on the line. First we find d using the point-to-line distance formula.
d = ((p0 - p1).cross(p0 - p2)).length() / (p2 - p1).length()
That is, assuming you can use +, -, cross, length operations. You might prefer to find d squared for performance reasons.
d2 = ((p0 - p1).cross(p0 - p2)).lengthSquared() / (p2 - p1).lengthSquared()
Now, if d or d2 are exactly zero, then you must be on the line. But this is floating point arithmetic so I would allow a little bit of leeway depending on your application. So in essence, d < 1e6 or something should do the trick.
simple dot product can do this easily ... so let consider we got line defined by two points p0,p1. Any point p on that line will have the same or negative slope to any of the endpoints so
|dot(p1-p0,p-p0)|/(|p1-p0|*|p-p0|) = 1.0
to make it more robust with floating point compare like this:
|dot(p1-p0,p-p0)|/(|p1-p0|*|p-p0|) >= 1.0-1e-10;
Where 1e-10 is small enough epsilon ... rewriten to code:
dx=x1-x0;
dy=y1-y0;
dz=z1-z0;
ex=x-x0;
ey=y-y0;
ez=z-z0;
q =dx*ex;
q+=dy*ey;
q+=dz*zy;
q*=q;
q/=(dx*dx+dy*dy+dz*dz);
q/=(ex*ex+ey*ey+ez*ez);
if (q>=1.0-1e-10) point p(x,y) is on the line
else p(x,y) is not on line
As you can see no need for the sqrt we can compare the power instead ...
However you should handle edge case when p==p0 then either use p1 or return true right away.
In case you want points only inside the line segment (not outside the edge points) then you need a slight change in code
0.0 <= dot(p1-p0,p-p0)/|p-p0| <= 1.0
So:
dx=x1-x0;
dy=y1-y0;
dz=z1-z0;
ex=x-x0;
ey=y-y0;
ez=z-z0;
q =dx*ex;
q+=dy*ey;
q+=dz*zy;
if (q<0.0) p(x,y) is not on line
q*=q;
q/=(ex*ex+ey*ey+ez*ez);
if (q<=1.0) point p(x,y) is on the line
else p(x,y) is not on line
btw the result of the dot product gives you ratio of one vector projected to another perpendicularly or cos of the angle between them (if they are normalized) so for parallel vectors the result is 100% of length or 1.0. If you tweak the 1e-10 value using goniometry and p-p0 you can convert this to detect points up to some perpendicular distance to line (which might get handy for thick lines and or mouse selecting).
To solve the above-mentioned problem you need to check the area of the triangle considering them as 3 points in the 3-d space. To find the area go through this link. If area = 0 then given points are collinear.
if you are not concerned about performance issue you can use the parametric equation of a segment in the space.
P(t) = P0 + t(P1 - P0)
where P0 and P1 are 3d point and t is a parameter ranging from 0 to 1.
this lead to 3 equations
x(t) = x0 + t(x1 - x0)
y(t) = y0 + t(y1 - y0)
z(t) = z0 + t(z1 - z0)
so to check if your (x,y,z) point lies in the line, you could get an initial value for t, for example t = (x - x0)/(x1-x0) then check if that satisfies the other two equations
t = (x - x0)/(x1-x0)
if ( (y0 + t(y1-y0) == y) and (z0 + t(z1-z0) == z) ) then
---> we are in the line
Like #Jay pointed out, this is floating point math you have to deal with some tolerance with values. For example to test y could be y0 + t(y1-y0) - y < 0.001
As you yourself point out, doing this in a robust way is not trivial. Therefore, I suggest you don't reinvent the wheel and use a geometric library. I have good experience with using Wild Magic from geometrictools.com.
In your case, the predicate to use would be gte::DCPQuery to get the distance between a point and the line, and then test if it's close enough to zero for your purpose of "on the line."
An example of use could look like this:
using namespace gte;
Line3<double> line({x1, y1, z1}, {x2 - x1, y2 - y1, z2 - z1});
Vector3<double> point{x, y, z};
DCPPoint3Line3 query;
auto result = query(point, line);
bool pointIsOnLine = (result.distance < some_epsilon);
(Code note touched by compiler, intended to show the approach, not to be "semicolon-perfect").

Get world Vertex Position of a TriMesh - 3ds Max SDK C#

Currently I'm writing a plugin for my company and we want to offset some math operations on meshes to C# to gain some speed in calculations. I'm not only a C# beginner (I'm a Python / Maxscript guy) but I also struggle to find good SDK documentation for 3DS Max.
My current problem: obtaining the world-relative position of a vertex in a mesh. Sounds easy but it poses some problems for me. I can get the vert position by using:
IPoint3 x = mesh.GetVert(vertID);
and for vert 3 which is in [0,0,0] of the scene it returns a Point3 value of [-23,86499, 17,5783, 0], probably relative to... well, center of the object I think. Or some local transformation matrix. The thing I want to get is the position of the vert in world space, which in this case I know is [0,0,0], as well as I can probe it with MaxScript by using:
polyOp.getVert $ 3
The simple function I'm using to poke around is:
List<IINode> nodes = nodesFromHandles(objHandles);
foreach (IINode i in nodes)
{
log(i.Name);
IObjectState iState = i.EvalWorldState(0, true);
IObject iObj = iState.Obj;
IPolyObject iPoly = (IPolyObject)iObj.ConvertToType(0, global.PolyObjectClassID);
ITriObject iTri = (ITriObject)iObj.ConvertToType(0, global.TriObjectClassID);
IMesh mesh = iTri.Mesh;
for (int vertID = 0; vertID < mesh.NumVerts; vertID++)
{
IPoint3 x = mesh.GetVert(vertID); /// RETURNS A BAD POSITION? RELATIVE TO WHAT MATRIX?
log(vertID.ToString() + ": " + x.X.ToString() + ", " + x.Y.ToString() + ", " + x.Z.ToString());
}
}
I guess I should probably do something with the object transform matrix, but I'm in the dark with that. Working on: 3ds Max 2014, MS visual Studio Community 2015 (C#), Win7x64, using Autodesk.Max.DLL. Any and all help appreciated, thank you in advance!
EDIT: (solution?)
Correct me if I'm wrong, but by doing some satanic rituals and sacrificing some of my teeth I've come up with this thing. I essentially grab the object world Transform Matrix at a given time (set to frame 0, because why not?) and then doing a point transform. Seems to work quite well for now, but I need to do more tests.
List<IINode> nodes = nodesFromHandles(objHandles);
foreach (IINode i in nodes)
{
log(i.Name);
IObjectState iState = i.EvalWorldState(0, true);
IObject iObj = iState.Obj;
ITriObject iTri = (ITriObject)iObj.ConvertToType(0, global.TriObjectClassID);
IMesh mesh = iTri.Mesh;
/// OBJECT TRANSFORM MATRIX
IInterval iTimeRange = i.GetTimeRange(0);
IMatrix3 worldTm = i.GetObjTMAfterWSM(0, iTimeRange);
for (int vertID = 0; vertID < mesh.NumVerts; vertID++)
{
IPoint3 x = mesh.GetVert(vertID); /// RETURNS A BAD POSITION? RELATIVE TO WHAT MATRIX?
IPoint3 v = worldTm.PointTransform(x);
log(vertID.ToString() + ": " + v.X.ToString() + ", " + v.Y.ToString() + ", " + v.Z.ToString());
}
}
If anybody can verify or improve this - your welcome. :)
Adding some explanations to your answer. The following line:
IPoint3 vertObjectSpace = mesh.GetVert(vertID);
Returns the vertex position in object space, which is unfortunately not mentioned in the docs. As you are interested in the positions in world space, you have to transform them using the following:
IMatrix3 tmObj2World= i.GetObjectTM(0, iTimeRange); // where i is your IINode
IPoint3 vertWorldSpace = tmObj2World.PointTransform(vertObjectSpace);
Hope that helps for your understanding.
Some notes / further reading:
Docs about GetObjectTM and GetObjTMAfterWSM (guess I would prefer to use GetObjectTM, unless you explicitly have to use GetObjTMAfterWSM):
3ds max uses the following three spaces: world, node/local and object. For some background info, see: http://help.autodesk.com/view/3DSMAX/2018/ENU/?guid=__files_GUID_3B001F21_8FE9_4663_A972_E648682A0ACD_htm
The node transform (unfortunately not the object transform) can be visualized in 3ds max with the following

Implementing Simpson's Rule math.net numerics

I am trying to implement Simpson's Rule using the math.net numerics library. The method that I would like to use takes four arguments, a Func (function pointer), intervalBegin, intervalEnd and partitionNumbers. Currently I am testing the method using Math.Sin, but can someone help me to understand how this Func should be implemented?
var test = MathNet.Numerics.Integration.SimpsonRule.IntegrateComposite(Math.Sin, 1, 4, 20);
Additional material:
Pic taken directly from math is fun.
You can have absolutely any mathematical function like the one char below represents.
Integration calculates area between the line that function draws and X axis.
The reason I say function you pass into Simpsons integration doesn't matter is because ANY function can be used as long as it fits "1 number in, 1 number out" format.
End of additional material.
Simpson's formula is an integration which means it needs a mathematical function to calculate with. Below is the rough formula of simpson's integration (using numbers you passed as arguments and a random function) if you are interested, if not skip this part .
intervalBegin = 1;
intervalEnd = 4;
partitionNumbers = 20;
f(x) = 3x^2;
deltaX = (intervalEnd - intervalBegin) / partitionNumbers;
SimpsonsIntegration = deltaX/3 * (f(intervalBegin) + 4*f(intervalBegin + deltaX*1)+ 2*f(intervalBegin + deltaX*2)+ 4*f(intervalBegin + deltaX*3)+ 2*f(intervalBegin + deltaX*4).....+4*f(intervalBegin + deltaX*19) +f(intervalEnd);
The function in Simpsons integration is ANY function that takes 1 numeric argument and returns one. (it might be type specific like float or double)
public double anyFunction(double number){
double result = [calculations];
return calculations;
}
Your call could look like this:
MathNet.Numerics.Integration.SimpsonRule.IntegrateComposite(anyFunction, 1, 4, 20);

I have a list of points and would like to determine if they form a circle

I'm using C#, I have a list of Vector points and would like to try and approximate how close they look like a circle.
Any ideas how to implement this or if someone else has?
Based on the "gestures" tag I guess you not only want to know how close are these points to the smallest-circle (search for "Smallest-circle problem"), but you have to be concerned also about their order and spread:
I would start with the distance from the smallest-circle. If they are too far, you're done, it's not a circle.
If they are close enough to your configured threshold, compute the angle between the vector defined by the circle center, first point and each other point (picture bellow)
Check that each angle is greater than the previous.
Check that difference between any two angles next to each other is not over some configured threshold.
Check that the last point is close enough to the first one.
You will probably think of some other checks eventually, so make it simple to extend.
Another possibility:
Find the centroid
(http://en.wikipedia.org/wiki/Centroid#Of_a_finite_set_of_points) of
the points
Determine the distance (radius) of each point from the location of
the centroid
Calculate the distribution and determine if the points
are within acceptable tolerances (e.g. standard deviation < ±0.05 × mean radius or something like that)
Without knowing more about the source of the points, it's hard to suggest the best solution.
These might be of use: http://link.springer.com/article/10.1007%2FBF02276879#page-1 and http://www.dtcenter.org/met/users/docs/write_ups/circle_fit.pdf. Those methods will give you the best fitting circle through the points, but you are still going to need to determine whether your data points are close enough for your purposes.
UPDATE: based on the 'gesture' tag, somebody has already implemented it: http://depts.washington.edu/aimgroup/proj/dollar/
1) Pick any three points from that list, find the center of their appropriate circle
We can do this, using triangle circumcircle construction method, you find the medians of all three sides (two are sufficient) and their intersection is the center of the circle. Something like this:
public PointF findCenter(PointF a, PointF b, PointF c)
{
float k1 = (a.Y - b.Y) / (a.X - b.X) //Two-point slope equation
float k2 = (a.Y - c.Y) / (a.X - c.X) //Same for the (A,C) pair
PointF midAB = new PointF((a.X + b.X) / 2, (a.Y + b.Y) / 2) //Midpoint formula
PointF midAC = new PointF((a.X + c.X) / 2, (a.Y + c.Y) / 2) //Same for the (A,C) pair
k1 = -1*k1; //If two lines are perpendicular, then the product of their slopes is -1.
k2 = -1*k2; //Same for the other slope
float n1 = midAB.Y - k1*midAB.X; //Determining the n element
float n2 = midAC.Y - k2*midAC.Y; //Same for (A,C) pair
//Solve y1=y2 for y1=k1*x1 + n1 and y2=k2*x2 + n2
float x = (n2-n1) / (k1-k2);
float y = k1*x + n1;
return new PointF(x,y);
}
2) Check if the other points are equivalently distanced from this center, if yes, you have a circle, if no, you don't.
P.S. I haven't tested the code, so be prepared to debug. Ask if you need anything else
Take any three points from your point set.
If the points are co-linear then, your point set doesn't all lie on a circle.
Find the circumcircle of the triangle. The diameter is given by: d = (a*b*c)/2*area. The center of the circle is the point of intersection of the perpendicular bisectors of the three sides.
Now for every remaining point in the point set, if the distance from the center is not equal to the radius then the points are not on a circle. You can speed up the calculations by comparing the square of the radius against the square of the distance between the given point and the center.

Randomly Generate Orthogonal 3x3 Matrix

I'm looking to do some complex part analysis within Seimens NX. I'm looking to implement the double caliper method of measuring a model in order to find the minimum possible box that it could possibly fit into(for machining purposes). I've got all of my measurement code in place, but I am completely baffled by the idea of a construct that can randomly output normalized 3x3 vectors for use as coordinate systems. The part is measured with respect to this coordinate system, so each coordinate system gives a unique "minimum part envelope". Once analyzed, the smallest envelope is selected and displayed.
this is the type of vector I am talking about:
1 0 0
0 1 0
0 0 1
numbers can be any value between -1 and 1, with decimals not only being accepted but pretty much required.
and no, this isn't my homework. More of an individual pursuit in my free time at work.
If you apply a rotation matrix to an already orthogonal matrix, then the result should also be orthogonal.
So you can redefine your problem as applying a random rotation matrix to the identity matrix.
Perhaps do one random rotation matrix for each axis (x,y,z) and then apply the matrices themselves in a random order?
If you don't mind to consider only a special subset of the orthogonal matrices, there is an easier way to achieve this, which is to take advantage of the Rodrigues' rotation formula to generate rotation matrices (which has an additional constraint that its determinant is equal to 1).
With this, you only need to generate a random 3x1 unit vector (as the rotation axis) and specify a rotation angle. This formula will transform them into a valid rotation matrix.
MATLAB example:
function R = rot(w, theta)
bw = [0, -w(3), w(2); w(3), 0, -w(1); -w(2), w(1), 0];
R = eye(3) + sin(theta)*bw + (1-cos(theta))*bw*bw;
end
w = rand(3,1)
w = w/norm(w)
R = rot(w, 3.14)
C++ example:
// w: the unit vector indicating the rotation axis
// theta: the rotation angle in radian
Eigen::Matrix3d MatrixExp3 (Eigen::Vector3d w, float theta){
Eigen::Matrix3d bw, R;
bw << 0, -w(2), w(1), w(2), 0, -w(0), -w(1), w(0), 0;
R << Eigen::Matrix3d::Identity() + std::sin(theta)*bw + (1-std::cos(theta))*bw*bw;
return R;
}
int main() {
std::srand((unsigned int) time(0));
Eigen::Vector3d w = Eigen::Vector3d::Random();
Eigen::Matrix3d R = MatrixExp3(w.normalized(), 3.14f);
std::cout << R << std::endl;
}

Categories