Extracting points and edge vectors - c#

I am creating a program to generate a path for a CNC machine laser/plasma cutting. In it, the user should be able to cut shapes in the base element and be able to acquire the points and vectors of those cuts. I added the possibility to draw arrows (points and vectors) on selected walls according to which the tool should travel. This is based on obtaining the normal vector of the selected wall, which is used to determine the angle of cut.
Unfortunately, I do not know how to get the same effect on walls with a variable normal vector. An example of such an edge is the inclined cylinder. When I apply arrows to such an edge they all have the same vector.
Code sample:
public List<Mesh> DrawArrowsOnSelectedFace(Entity entity)
{
List<Mesh> arrowList = new List<Mesh>();
Brep ent = (Brep)entity;
for (int i = 0; i < ent.Faces.Length; i++)
{
if (ent.GetFaceSelection(i))
{
Surface[] sf = ent.Faces[i].ConvertToSurface(ent);
foreach (Surface surf in sf)
{
ICurve[] extractedEdges = surf.ExtractEdges();
Vector3D rotation = CalculatePerpenticularToNormalVector(surf);
foreach (ICurve curve in extractedEdges)
{
Point3D[] segmented = curve.GetPointsByLengthPerSegment(5);
for (int j = 1; j <= segmented.Length - 1; j++)
{
Point3D point1 = segmented[j - 1];
Mesh arrow = CreateArrow(point1, rotation);
arrowList.Add(arrow);
}
}
}
}
}
return arrowList;
}
private Vector3D CalculatePerpenticularToNormalVector(Surface surface)
{
Point3D point3D1 = new Point3D(surface.ControlPoints[0, 0].X, surface.ControlPoints[0, 0].Y, surface.ControlPoints[0, 0].Z);
Point3D point3D2 = new Point3D(surface.ControlPoints[0, 1].X, surface.ControlPoints[0, 1].Y, surface.ControlPoints[0, 1].Z);
Point3D point3D3 = new Point3D(surface.ControlPoints[1, 0].X, surface.ControlPoints[1, 0].Y, surface.ControlPoints[1, 0].Z);
Plane plane = new Plane(point3D1, point3D2, point3D3);
Vector3D equation = new Vector3D(plane.Equation.X, plane.Equation.Y, plane.Equation.Z);
Vector3D vectorZ = new Vector3D();
vectorZ.PerpendicularTo(Vector3D.AxisMinusY);
Vector3D result = CalculateRotation(vectorZ, equation);
result.Normalize();
return result;
}
private Mesh CreateArrow(Point3D point3D, Vector3D rotation)
{
if (point3D.Z >= -0.5)
{
return Mesh.CreateArrow(point3D, rotation, 0.3, 5, 0.35, 2, 36, Mesh.natureType.Smooth, Mesh.edgeStyleType.Sharp);
}
else return null;
}
private Vector3D CalculateRotation(Vector3D vector, Vector3D equation)
{
return vector - Vector3D.Dot(vector, equation) * equation;
}
What type should I best use for Boolean operations?
I also have a part of the code prepared where the arrows are drawn based on the common part of the basic element and the cut shape. Both of these shapes are BREPs. Unfortunately, this uses a lot of memory and takes some time.

You can convert the yellow face to a Surface using Brep.Faces[i].ConvertToSurface() and generating U or V isocurves of the resulting surface at equally spaced parameters using Surface.IsocurveU(t) or Surface.IsocurveU(t).

Related

How to check if 4 points form a convex quadrilatera

I'm quite new to coding in general. I have found some answers for this question but the answers seem advanced for me.
I'm trying to write my own Finite Element Project. For this I would like to write a method that checks if random 4 nodes given as input form a convex quadrilateral.
My method is supposed to look like this:
private bool IsConvex(Node[4] corners)
{
bool isConvex;
//CODE//
return isConvex;
}
the Node class is defined by three public properties referring to their coordinates (.coordX, .coordY, .coordZ)
In order to know if a quadrilateral is convex or not, you can make a triangle of three points and see if the fourth point is located inside that triangle or not. If you manage finding one triangle, which contains the fourth point, then you don't have a convex quadrilateral.
Ok, and how can you know if a point is located inside a triangle?
Well, you start by determining at which side a point is located compared to a vector.
Come again?
Well, for each vector, you can find out if a point is located at the left side or at the right side: you just rotate the vector back to the Y-axis, you do the same with the point and if the X coordinate of the point is negative your point is located at the left side, otherwise it's at the right side, like in these three cases (left, left and right):
Once you have figured that out, you define a point being inside a triangle if, after having described the triangle as a triangle of vectors, your point is at the same side of all vectors, like in this example (be aware that your triangle consists of the vectors AB, BC and CA: the points must follow up each other):
Good luck
First a little helper class to handle things related to triangles made up of three nodes.
using System.Numerics;
public readonly struct Triangle
{
public const float DistanceTolerance = 1e-6f;
public Triangle(Vector3 a, Vector3 b, Vector3 c)
{
A = a;
B = b;
C = c;
}
public Vector3 A { get; }
public Vector3 B { get; }
public Vector3 C { get; }
private Vector3 AreaVector { get => (Vector3.Cross(A, B) + Vector3.Cross(B, C) + Vector3.Cross(C, A)) / 2; }
public float Area { get => AreaVector.Length(); }
public Vector3 Normal { get => Vector3.Normalize(AreaVector); }
public float DistanceTo(Vector3 point) => Vector3.Dot(Normal, point - A);
public Vector3 Project(Vector3 point)
{
// A projected point lies on the plane defined by the three veertices A,B,C
Vector3 n = Normal;
float d = Vector3.Dot(n, point - A);
return point - n * d;
}
public void Barycentric(Vector3 P, out (float w_A, float w_B, float w_C) coordinates)
{
Vector3 n = Vector3.Cross(A, B) + Vector3.Cross(B, C) + Vector3.Cross(C, A);
float w_A = Vector3.Dot(n, Vector3.Cross(P, B) + Vector3.Cross(B, C) + Vector3.Cross(C, P));
float w_B = Vector3.Dot(n, Vector3.Cross(A, P) + Vector3.Cross(P, C) + Vector3.Cross(C, A));
float w_C = Vector3.Dot(n, Vector3.Cross(A, B) + Vector3.Cross(B, P) + Vector3.Cross(P, A));
float sum = w_A + w_B + w_C;
coordinates = (w_A / sum, w_B / sum, w_C / sum);
}
public bool Contains(Vector3 P)
{
if (Math.Abs(DistanceTo(P)) <= DistanceTolerance)
{
Barycentric(P, out var coordinates);
return coordinates.w_A >= 0 && coordinates.w_A <= 1
&& coordinates.w_B >= 0 && coordinates.w_B <= 1
&& coordinates.w_C >= 0 && coordinates.w_C <= 1;
}
return false;
}
}
If you are not familiar with barycentric coordinates, they are the linear combinations of the vertices that make up an interior (or exterior) point.
For example if a point is defined as P = 0.3*A + 0.5*B + 0.2*C then the barycentric coordinates of P are (0.3,0.5,0.2). The only restriction here is that the sum of the barycentric coordinates must equal to 1.
A point P is interior to the triangle ABC if all the barycentric coordinates of P are between 0 and 1.
This is the rule that I am using to write the Triangle.Contains(point) function. I also check to see if the point is on the same plane as the triangle.
Now to get to the algorithm to check if an n-gon is convex, all I have to do is take 3 vertices at a time, and check that all remaining other vertices are exterior to those three.
public static bool IsConvex(Vector3[] nodes)
{
for (int i = 0; i < nodes.Length; i++)
{
// pick three nodes at a time i,j,k
var j = (i + 1) % nodes.Length;
var k = (i + 2) % nodes.Length;
var A = nodes[i];
var B = nodes[j];
var C = nodes[k];
// deefine triangle ABC from three nodes
var trig = new Triangle(A, B, C);
// check nodes after the three and wrap around to grab first nodes also
for (int r = 3; r < nodes.Length; r++)
{
var P = nodes[(r + i) % nodes.Length];
// if _any_ node is interior to ABC then non-convex
if (trig.Contains(P))
{
return false;
}
}
}
return true;
}
and some test code to make sure it all works as intended.
static readonly Random rng = new Random();
static void Main(string[] args)
{
// Generate a random 3D triangle
var trig = new Triangle(
new Vector3(10 * (float)rng.NextDouble(), 0, 0),
new Vector3(0, 10 * (float)rng.NextDouble(), 0),
new Vector3(0, 0, 10 * (float)rng.NextDouble()));
// Generate an interior point (in the plane)
var point1 = 0.3f * trig.A + 0.5f * trig.B + 0.2f * trig.C;
// Check that it is contained inside the triangle
Debug.Assert(trig.Contains(point1));
// Generate an exterior point (on the plane)
var point2 = -0.3f * trig.A + 0.5f * trig.B + 0.8f * trig.C;
// Check that it is not contained inside the triangle
Debug.Assert(!trig.Contains(point2));
// Generate a point out of plane
var point3 = point1 + 2.5f * trig.Normal;
// Check that it is not contained inside the triangle
Debug.Assert(!trig.Contains(point3));
// Generate a convex quadrilateral
var poly1 = new Vector3[] {
new Vector3(0f,0f,0f),
new Vector3(5f,0f,0f),
new Vector3(5f,3f,0f),
new Vector3(1f,7f,0f),
};
// Generate a non-convex quadrilateral
var poly2 = new Vector3[] {
new Vector3(0f,0f,0f),
new Vector3(5f,0f,0f),
new Vector3(2f,2f,0f),
new Vector3(1f,7f,0f),
};
// Check that it is convex
Debug.Assert(IsConvex(poly1));
// Check that it is not convex
Debug.Assert(!IsConvex(poly2));
}

integer lattice pairs within a circle c#

I am having trouble trying to write a method (in c#) that returns all of the integer lattice pairs within a circle of a given radius at a specific offset. I found this article https://en.wikipedia.org/wiki/Gauss_circle_problem but unfortunately it seems more interested in calculating the number of lattice pairs rather than identifying each individual lattice pair.
I am also having issues understanding some of the math terminology/symbols as my math is sadly somewhat lacking, so code examples or detailed explanations would be super helpful if possible.
my plan so far is to just check each integer value combination from the radius to negative radius and then simply check the distance to the origin, before applying the offset to the vectors that are within range.
Am i on the right track with this, or is there a more optimized way to achieve this?
example stub:
public class CircleTest ()
{
public List<Vector2>GetContainedIntegerVectors(float radius, Vector2 centerPosition)
{
//math goes here
}
}
Simple Vector2 class
public class Vector2 ()
{
public float x {get; set;}
public float y {get; set;}
}
Thanks!
For my understanding you are on the right track, but there are some optimizations possible:
use the System.Windows.Vector class from C# instead of your own
you only have to calculate the points of a quarter of the circle, eg for x>0 and y>=0 and mirror the rest (plus include centerpoint).
here a possible implementation:
List<Vector> tmpList = new List<Vector();
List<Vector> list = new List<Vector();
double rSquared=r*r; // using sqared reduces execution time (no square root needed)
for(int x=1; x<=r; x++)
for(int y=0; y<=r; y++) {
Vector v = new Vector(x,y);
if(v.LengthSquared<=rSquared)
tmpList.Add(v);
else
break;
}
list.Add(centerVector);
foreach(Vector v in tmpList) {
Vector vMirr = new Vector(v.X, -1*v.Y);
list.Add(Vector.Add(centerVector, v));
list.Add(Vector.Add(centerVector, v.Negate()));
list.Add(Vector.Add(centerVector, vMirr));
list.Add(Vector.Add(centerVector, vMirr.Negate));
}
public List&ltVector2&gtGetContainedVectors(int radius, Vector2 offset)
{
List<Vector2> returnValues = new List&ltVector2&gt();
for (int x = radius; x > -radius; x--)
{
for (int y = radius; y > -radius; y--)
{
if(Vector2.Distance(new Vector2(x,y), Vector2.zero) <= radius)
{
returnValues.Add(new Vector2(x + offset.x, y + offset.y));
}
}
}
return returnValues;
}

Sorting voronoi cell vertices to compute polygon

I'm currently trying to get the clipped cells from a Polygon-Voronoi-Intersection.
Here is what I've got so far:
I have a polygon and computed some points in it to calculate a voronoi diagram and the red lines on the figure below are the voronoi edges.
Then I used an algorithm to get the corner points from every cell and now I need to get the corners in the right direction (clockwise) to generate the cell polygon.
Found Corners for one cell
First I was using this method:
private List<Vector> sortClockwise(List<Vector> points)
{
points = points.OrderBy(x => Math.Atan2(x.X, x.Y)).ToList();
return points;
}
but in some special concave polygons this doesn't work and the right order gets mixed up.
Does anyone have a suggetion or hint how this could be done the most simplest way? Consider that the polygon points are in the right order already and the voronoi corners are mixed up and need to get sorted to the polygon points.
My idea:
Find first polygon point in cell corners
go along polygon direction and look if point of voronoi vertices is on that line.
if yes: get endpoint of found voronoi edge and look for shared voronoi edges.
if shared edges found, always take the most right one
do until you reach fist point
Is that the only way I could do that?
EDIT - UPDATE
Okay I have some sort of half answer now.
As I said, I have all the vertices which belong to one of the voronoi's cells but the order is still messed up so I thought I could sort them by angle from the centroid like so:
private List<Vector> sortClockwiseBySentroid(List<Vector> points, Vector center)
{
points = points.OrderBy(x => Math.Atan2(x.X - center.X, x.Y - center.Y)).ToList();
return points;
}
But this doesn't always work. Here are the examples when its working and when not. The problem is that on concave edges the angle from the centorid to the corner is sometimes smaller than the one I really need. Any suggestion on how to fix this?
Here its working
Here its not working...
This is how is sort the list of vertices in clockwise order for a cell in my Voronoi generation. Assuming you know which vertices you need to sort this code should do the job.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class ConvexHull
{
private List<Vector2> vertices;
public ConvexHull()
{
vertices = new List<Vector2>();
}
public ConvexHull(Vector2[] vertices)
{
this.vertices = new List<Vector2>(vertices);
}
public void AddVertices(Vector2[] vertices)
{
this.vertices.AddRange(new List<Vector2>(vertices));
}
public Vector2[] Generate()
{
if (vertices.Count > 1)
{
int k = 0;
Vector2[] hull = new Vector2[2 * vertices.Count];
// Make the list distinct and sorted
vertices = vertices.Distinct().OrderBy(v => v.x).ToList();
//vertices.Sort();
//Array.Sort(vertices);
// Build lower hull
for (int i = 0; i < vertices.Count; ++i)
{
while (k >= 2 && Cross(hull[k - 2], hull[k - 1], vertices[i]) <= 0)
k--;
hull[k++] = vertices[i];
}
// Build upper hull
for (int i = vertices.Count - 2, t = k + 1; i >= 0; i--)
{
while (k >= t && Cross(hull[k - 2], hull[k - 1], vertices[i]) <= 0)
k--;
hull[k++] = vertices[i];
}
if (k > 1)
{
hull = hull.Take(k - 1).ToArray();
}
return hull;
}
if (vertices.Count <= 1)
{
return vertices.ToArray();
}
return null;
}
private float Cross(Vector2 p1, Vector2 p2, Vector2 p3)
{
return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
}
}

DrawBone Angle variable using Kinect

when we want to draw Bones into XNA (using KnectSDK and C#), you have to calculate differences between two joints and draw bones between them. this is the function for drawing Bones:
private void DrawBone(JointCollection joints, JointType startJoint, JointType endJoint)
{
Vector2 start = this.mapMethod(joints[startJoint].Position);
Vector2 end = this.mapMethod(joints[endJoint].Position);
Vector2 diff = end - start;
Vector2 scale = new Vector2(1.0f, diff.Length() / this.boneTexture.Height);
float angle = (float)Math.Atan2(diff.Y, diff.X) - MathHelper.PiOver2;
Color color = Color.LightGreen;
if (joints[startJoint].TrackingState != JointTrackingState.Tracked ||
joints[endJoint].TrackingState != JointTrackingState.Tracked)
{
color = Color.Gray;
}
this.SharedSpriteBatch.Draw(this.boneTexture, start, null, color, angle, this.boneOrigin, scale, SpriteEffects.None, 1.0f);
}
I just want to understand the way of calculating angle and how the angle formula works
Thanks
Here is the code to calculate the angle between two vectors (Explanation below code):
public class Angles
{
public double AngleBetweenTwoVectors(Vector3D vectorA, Vector3D vectorB)
{
double dotProduct = 0.0;
dotProduct = Vector3D.DotProduct(vectorA, vectorB);
return (double)Math.Acos(dotProduct)/Math.PI*180;
}
public double[] GetVector(Skeleton skeleton)
{
Vector3D ShoulderCenter = new Vector3D(skeleton.Joints[JointType.ShoulderCenter].Position.X, skeleton.Joints[JointType.ShoulderCenter].Position.Y, skeleton.Joints[JointType.ShoulderCenter].Position.Z);
Vector3D RightShoulder = new Vector3D(skeleton.Joints[JointType.ShoulderRight].Position.X, skeleton.Joints[JointType.ShoulderRight].Position.Y, skeleton.Joints[JointType.ShoulderRight].Position.Z);
Vector3D LeftShoulder = new Vector3D(skeleton.Joints[JointType.ShoulderLeft].Position.X, skeleton.Joints[JointType.ShoulderLeft].Position.Y, skeleton.Joints[JointType.ShoulderLeft].Position.Z);
Vector3D RightElbow = new Vector3D(skeleton.Joints[JointType.ElbowRight].Position.X, skeleton.Joints[JointType.ElbowRight].Position.Y, skeleton.Joints[JointType.ElbowRight].Position.Z);
Vector3D LeftElbow = new Vector3D(skeleton.Joints[JointType.ElbowLeft].Position.X, skeleton.Joints[JointType.ElbowLeft].Position.Y, skeleton.Joints[JointType.ElbowLeft].Position.Z);
Vector3D RightWrist = new Vector3D(skeleton.Joints[JointType.WristRight].Position.X, skeleton.Joints[JointType.WristRight].Position.Y, skeleton.Joints[JointType.WristRight].Position.Z);
Vector3D LeftWrist = new Vector3D(skeleton.Joints[JointType.WristLeft].Position.X, skeleton.Joints[JointType.WristLeft].Position.Y, skeleton.Joints[JointType.WristLeft].Position.Z);
/* ShoulderCenter.Normalize();
RightShoulder.Normalize();
LeftShoulder.Normalize();
RightElbow.Normalize();
LeftElbow.Normalize();
RightWrist.Normalize();
LeftWrist.Normalize();
if (skeleton.Joints[JointType.ShoulderCenter].TrackingState == JointTrackingState.Tracked) {
}
*/
double AngleRightElbow = AngleBetweenTwoVectors(RightElbow - RightShoulder, RightElbow - RightWrist);
double AngleRightShoulder = AngleBetweenTwoVectors(RightShoulder - ShoulderCenter, RightShoulder - RightElbow);
double AngleLeftElbow = AngleBetweenTwoVectors(LeftElbow - LeftShoulder, LeftElbow - LeftWrist);
double AngleLeftShoulder = AngleBetweenTwoVectors(LeftShoulder - ShoulderCenter, LeftShoulder - LeftElbow);
double[] Angles = {AngleRightElbow, AngleRightShoulder, AngleLeftElbow, AngleLeftShoulder};
return Angles;
}
}
First of all we'll have a look at the method GetVector(Skeleton skeleton). In this method we first define our Vector3D(for the joints you choose). We then call the method AngleBetweenTwoVectors and give it two parameters.
But attention. If we would just give it the Vector of the Joints, we would not get the correct angle. We first have to substract the surrounding Vectors from the Vector (Vector we want to get the angle from). We then pass the two Vectors to the method AngleBetweenTwoVectors.
In this method we first have to calculate the dot-product. For more information on the dot-product click here.
With the dot-product we can calculate the angle. To do that we just need to use the arcos() method.
Another thing I struggled with: The assemblies.
You need to import:
using System.Windows.Media;
using Microsoft.Kinect;
using Microsoft.Kinect.Toolkit.Fusion;
using System.Windows.Media.Media3D;
To get the [...].Toolkit.Fusion assembly go to "Add Reference" and hit "Browse". You will then guide to the assembly directory from the MicrosoftSDK's/Kinect/Developer Toolkit v1.8.0/Assemblies. Import the assembly and add it to your project.

Bullet Physics Convex hulls with cubes

I'm developing a game engine in c# and am using BulletSharp for physics. It's working well except with cubes:
http://i.stack.imgur.com/EPfrw.png
(The Axis-Aligned Bounding box is the transparent red, the model is the white)
At rest, the stand on their edges. Because I'm loading from Collada models, I am creating a ConvexHullShape() and adding the data as a vector cloud. While using BoxShape() would be more efficient (and work correctly), I cannot as it is not guaranteed that all models are cubes. I cannot figure out why they rest on vertices and not on the flat edges. Is my implementation of ConvexHullShape wrong or do I need to use a different type of shape (for the physics to work correctly)?
public RigidBody AddDynamicGeometry(ColladaGeometry geometry, Matrix4 transform)
{
List<Vector3> points = new List<Vector3>();
foreach (Triangle tri in geometry.triangles)
{
points.Add(tri.vertices[0]);
points.Add(tri.vertices[1]);
points.Add(tri.vertices[2]);
}
CollisionShape shape = new ConvexHullShape(points);
shape.UserObject = geometry;
collisionShapes.Add(shape);
RigidBody body = CreateRigidBody(geometry.triangles.Count * 10, transform, shape);
return body;
}
public RigidBody CreateRigidBody(float mass, Matrix4 startTransform, CollisionShape shape)
{
bool isDynamic = (mass != 0.0f);
Vector3 localInertia = Vector3.Zero;
if (isDynamic)
shape.CalculateLocalInertia(mass, out localInertia);
DefaultMotionState myMotionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, shape, localInertia);
RigidBody body = new RigidBody(rbInfo);
physics_world.AddRigidBody(body);
return body;
}
ConvexHullShape expects the center of mass (COM) to be (0,0,0), but the cube is offset from the center, making it tilt towards the corner.
You can find the correct COM with ConvexTriangleMeshShape.CalculatePrincipalAxisTransform. Then you could subtract the COM from each vertex to bring the COM back to 0. However, it's easier to create a CompoundShape with a local center for the cube.
// Create a ConvexTriangleMeshShape from the points
const int indexStride = 3 * sizeof(int);
const int vertexStride = 12;
int vertexCount = points.Count;
int indexCount = vertexCount / 3;
TriangleIndexVertexArray vertexArray = new TriangleIndexVertexArray();
IndexedMesh mesh = new IndexedMesh();
mesh.Allocate(vertexCount, vertexStride, indexCount, indexStride);
Vector3Array vdata = mesh.Vertices;
IntArray idata = mesh.TriangleIndices;
for (int i = 0; i < vertexCount; i++)
{
vdata[i] = points[i];
idata[i] = i;
}
vertexArray.AddIndexedMesh(mesh);
ConvexTriangleMeshShape shape = new ConvexTriangleMeshShape(vertexArray, true);
// Calculate center of mass
Matrix center = Matrix.Identity;
Vector3 inertia;
float volume;
shape.CalculatePrincipalAxisTransform(ref center, out inertia, out volume);
// Create a CompoundShape with COM offset
CompoundShape compound = new CompoundShape();
compound.AddChildShape(Matrix.Invert(center), shape);
Note: ConvexTriangleMeshShape.CalculatePrincipalAxisTransform works in SVN trunk, but not in BulletSharp 2.82. There will be a bugfix release soon.

Categories

Resources