Plotting evolution of 2D vector in 3D as a ribbon in MATLAB - math

I would like to plot how the amplitude and orientation of a 2D vector evolves over time. To do this I would like to create a graph reminiscent of the canonical E & B field graphs you may recall from an introductory electricity and magnetism class.
Specifically, I would like to connect my 2D vector points with a ribbon, so that they are easy to see. Is there a simple way to do this in MATLAB? quiver3 is pretty close, but it lacks the ribbon. Perhaps some sort of parametric surface?

You can use the plotting functions FILL3 and QUIVER3 to do something like this:
x = linspace(0,4*pi,30); %# Create some x data
y1 = sin(x); %# Create wave 1
y2 = sin(x-pi); %# Create wave 2
u = zeros(size(x)); %# Create a vector of zeroes
hRibbon1 = fill3(x,y1,u,'r'); %# Plot wave 1 and fill underneath with color
set(hRibbon1,'EdgeColor','r',... %# Change the edge color and
'FaceAlpha',0.5); %# make the colored patch transparent
hold on; %# Add to the existing plot
quiver3(x,u,u,u,y1,u,0,'r'); %# Plot the arrows
hRibbon2 = fill3(x,u,y2,'b'); %# Plot wave 2 and fill underneath with color
set(hRibbon2,'EdgeColor','b',... %# Change the edge color and
'FaceAlpha',0.5); %# make the colored patch transparent
quiver3(x,u,u,u,u,y2,0,'b'); %# Plot the arrows
axis equal; %# Use equal axis scaling
And here's the resulting plot:

here's a solution that draws a ribbon between any two lines in 3D space. you can plot your quiver over it & adjust the opacity using 'FaceAlpha' as in gnovice's solution
To make the function clearer, I am first posting it without error-checking and resizing functions (which make up most of the body of the function & aren't particularly interesting)
function h = filledRibbon (x,y,z,u,v,w,c, varargin)
%function filledRibbon (x,y,z,u,v,w,c, varargin)
%
%plots a ribbon spanning the area between the lines x,y,z and x+u,y+v,z+w
%in the color c
%varargin is passed directly to patch
%returns a handle to the patch graphic created
%make up a set of regions that span the space between the lines
xr = [x(1:end-1); x(1:end-1) + u(1:end-1); x(2:end) + u(2:end); x(2:end)];
yr = [y(1:end-1); y(1:end-1) + v(1:end-1); y(2:end) + v(2:end); y(2:end)];
zr = [z(1:end-1); z(1:end-1) + w(1:end-1); z(2:end) + w(2:end); z(2:end)];
%plot the regions with no edges
h = patch(xr,yr,zr,c, 'LineStyle','none', varargin{:});
use this error-checking version in your actual code:
function h = filledRibbon (x,y,z,u,v,w,c, varargin)
%function filledRibbon (x,y,z,u,v,w,c, varargin)
%
%plots a ribbon spanning the area between the lines x,y,z and x+u,y+v,z+w
%in the color c
%varargin is passed directly to patch
%returns a handle to the patch graphic created
if ~exist('w', 'var') || isempty(w)
w = 0;
end
if ~exist('u', 'var') || isempty(u)
u = 0;
end
if ~exist('v', 'var') || isempty(v)
v = 0;
end
if ~exist('c', 'var') || isempty(c)
c = 'b';
end
%make all vectors 1xN
x = reshape(x,1,[]);
y = reshape(y,1,[]);
z = reshape(z,1,[]);
%if any offsets are scalar, expand to a vector
if all(size(u) == 1)
u = repmat(u, size(x));
end
if all(size(v) == 1)
v = repmat(v, size(x));
end
if all(size(w) == 1)
w = repmat(w, size(x));
end
%make up a set of regions that span the space between the lines
xr = [x(1:end-1); x(1:end-1) + u(1:end-1); x(2:end) + u(2:end); x(2:end)];
yr = [y(1:end-1); y(1:end-1) + v(1:end-1); y(2:end) + v(2:end); y(2:end)];
zr = [z(1:end-1); z(1:end-1) + w(1:end-1); z(2:end) + w(2:end); z(2:end)];
%plot the regions with no edges
h = patch(xr,yr,zr,c, 'LineStyle','none', varargin{:});

Related

Extrude 2d vertices/vectors

I have a set of vertices/vectors and I need to extrude them inside object boundaries to give that object a thickness
as an example:
I need to turn something like this:
to something like this:
how can I achieve this?
(I'm using C++, OpenGL & GLM)
-- Update --
thanks to #Futurologist answer, I was able to resolve the issue and hey, It works like a charm!
Sorry for the python, but it's faster and easier for me to write it like that, plus it maybe reveals some the geometric concepts in the background.
Is this what you are after?
'''
angle bisectors and offsetting a polygon
'''
def bisectors(P, thickness):
#P is n x 2 matrix, row P[j,:] is a vertex of a polygon in the plane,
#P is the ordered set of vertices of the polygon
n = P.shape[0];
B = np.zeros((n,2), dtype=float);
for j in range(n):
if j == 0:
v_in = P[0,:] - P[n-1,:];
v_out = P[1,:] - P[0,:];
elif j == n-1:
v_in = P[n-1,:] - P[n-2,:];
v_out = P[0,:] - P[n-1,:];
else:
v_in = P[j,:] - P[j-1,:];
v_out =P[j+1,:] - P[j,:];
v_in = v_in / math.sqrt(v_in.dot(v_in)); #normalize edge-vector
v_out = v_out / math.sqrt(v_out.dot(v_out)); #normalize edge-vector
# bisector of the complementary angle at the vertex j,
# pointing counter clockwise and displacing the vertex so that
# the resulting polygon is "thickness" units inwards in normal direction:
bisector = v_in + v_out;
bisector = bisector / abs(bisector.dot(v_in));
bisector = thickness * bisector
# 90 degree counter clockwise rotation of complementary bisector:
B[j,0] = - bisector[1];
B[j,1] = bisector[0];
return B
def offset_vertices(Polygon, thickness):
Polygon_off = Polygon + bisectors(Polygon, thickness)
return Polygon_off
P = np.array([[0,0],[2,0],[3,1],[1,3]])
P_off = offset_vertices(P, 0.1)
# Plotting
P = np.vstack((P, P[0,:] ))
P_off = np.vstack((P_off, P_off[0,:] ))
fig, axs = plt.subplots(1)
axs.plot(P[:,0], P[:,1], 'bo')
axs.plot(P_off[:,0], P_off[:,1], 'ro')
axs.plot(P[:,0], P[:,1])
axs.plot(P_off[:,0], P_off[:,1])
axs.set_aspect('equal')
plt.grid()
plt.show()

Positioning objects parallel with a mesh

I'm trying to align multiple line objects along a human body circumference depending on the orientation of the triangles from the mesh. I would like to put the lines parallel to the mesh. I correctly assign the position for the lines along the circumference, but I also need to add the rotation of the lines such that to be parallel with the body.
The body is a mesh formed by multiple triangles and every line is "linked" with a triangle.
All I have is:
3 points for the closest triangle from the mesh for every line
The normal of the triangle
The positions for the instantiated lines (2 points, start and end)
I need to calculate the angle for every X, Y, Z axes for the line such that the normal of the triangle is perpendicular with the line mesh. I don't know how to get the desired angle. I really appreciate if someone would like to help me.
input:
FVector TrianglePoints[3];
FVector Triangle_Normal; //Calculated as (B-A)^(C-A), where A,B,C are the points of the triangle
FVector linePosition; //I also have the start line and the endLine position if that helps
ouput:
//FRotator rotation(x,y,z), such that the triangle normal and the line object to be perpendicular.
An overview of the circumference line construction. Now the rotation is calculated using the Start position and End position for each line. When we cross some irregular parts of the mesh we want to rotate the lines correctly. Now the rotation is fixed, depending just on the line start and end position.
If I have understood correctly your goal, here is some related vector geometry:
A,B,C are the vertices of the triangle:
A = [xA, yA, zA],
B = [xB, yB, zB]
C = [xC, yC, zC]
K,L are the endpoints of the line-segment:
K = [xK, yK, zK]
L = [xL, yL, zL]
vectors are interpreted as row-vectors
by . I denote matrix multiplication
by x I denote cross product of 3D vectors
by t() I denote the transpose of a matrix
by | | I denote the norm (magnitude) of a vector
Goal: find the rotation matrix and rotation transformation of segment KL
around its midpoint, so that after rotation KL is parallel to the plane ABC
also, the rotation is the "minimal" angle rotation by witch we need to
rotate KL in order to make it parallel to ABC
AB = B - A
AC = C - A
KL = L - K
n = AB x AC
n = n / |n|
u = KL x n
u = u / |u|
v = n x u
cos = ( KL . t(v) ) / |KL|
sin = ( KL . t(n) ) / |KL|
U = [[ u[0], u[1], u[2] ],
[ v[0], v[1], v[2] ],
[ n[0], n[1], n[2] ],
R = [[1, 0, 0],
[0, cos, sin],
[0, -sin, cos]]
ROT = t(U).R.U
then, one can rotate the segment KL around its midpoint
M = (K + L)/2
Y = M + ROT (X - M)
Here is a python script version
A = np.array([0,0,0])
B = np.array([3,0,0])
C = np.array([2,3,0])
K = np.array([ -1,0,1])
L = np.array([ 2,2,2])
KL = L-K
U = np.empty((3,3), dtype=float)
U[2,:] = np.cross(B-A, C-A)
U[2,:] = U[2,:] / np.linalg.norm(U[2,:])
U[0,:] = np.cross(KL, U[2,:])
U[0,:] = U[0,:] / np.linalg.norm(U[0,:])
U[1,:] = np.cross(U[2,:], U[0,:])
norm_KL = np.linalg.norm(KL)
cos_ = KL.dot(U[1,:]) / norm_KL
sin_ = KL.dot(U[2,:]) / norm_KL
R = np.array([[1, 0, 0],
[0, cos_, sin_],
[0,-sin_, cos_]])
ROT = (U.T).dot(R.dot(U))
M = (K+L) / 2
K_rot = M + ROT.dot( K - M )
L_rot = M + ROT.dot( L - M )
print(L_rot)
print(K_rot)
print(L_rot-K_rot)
print((L_rot-K_rot).dot(U[2,:]))
A more inspired solution was to use a procedural mesh, generated at runtime, that have all the requirements that I need:
Continuously along multiple vertices
Easy to apply a UV map for texture tiling
Can be updated at runtime
Isn't hard to compute/work with it

Finding if a circle is fully contained within multiple triangles?

In a game, an area is defined by triangles that never overlap, and characters are defined by circles.
How can I know whether the full character's collision circle is contained within these triangles?
Example image:
Here, the red parts are outside triangles, so the circle isn't contained within them. Is there an algorithm that can detect this?
I've only came up with "non-perfect" solutions, like sampling points at the border of the circle, then testing if each is inside a triangle.
So basically, the triangles form a domain with polygonal boundary and you want to check if a disk, defined by a center point and a radius is contained inside the domain. So if you start with the triangles, you have to find a way to extract the polygonal boundary of your domain and represent it as a 2D array (matrix) of shape n rows and two columns so that every row is the two coordinates of a vertex point of the polygonal boundary line and the points are ordered so that they are consecutive order along the boundary in a counterclockwise position, i.e. when you walk in a direction from point of index i to the next point i+1 the domain stays on your left. For example, here is the representation of a polygonal boundary of a domain like yours:
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
Observe that the first and the last points are the same.
In such a scenario, maybe you can try the following algorithm:
import numpy as np
import math
def angle_and_dist(p1, p2, o):
p12 = p2 - p1
op1 = p1 - o
op2 = p2 - o
norm_p12 = math.sqrt(p12[0]**2 + p12[1]**2)
norm_op1 = math.sqrt(op1[0]**2 + op1[1]**2)
norm_op2 = math.sqrt(op2[0]**2 + op2[1]**2)
p12_perp = np.array([ - p12[1], p12[0] ])
h = - op1.dot(p12_perp)
theta12 = op1.dot(op2) / (norm_op1*norm_op2)
theta12 = math.acos( theta12 )
if h < 0:
theta12 = - theta12
if op1.dot(p12) > 0:
return theta12, norm_op1
elif op2.dot(p12) < 0:
return theta12, norm_op2
else:
return theta12, h/norm_p12
def is_in_polygon(p, disk):
o, r = disk
n_p = len(p)-1
index_o = 0
h_min = 400
for i in range(n_p):
theta, h = angle_and_dist(p[i,:], p[i+1,:], o)
index_o = index_o + theta
if 0 <= h and h < h_min:
h_min = h
if theta <= math.pi/100:
return 'center of disc is not inside polygon'
elif theta > math.pi/100:
if h_min > r:
return 'disc is inside polygon'
else:
return 'center of disc is inside polygon but disc is not'
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
# A test example:
#disc = (np.array([3*a/4, 2]), a/4-0.001)
disc = (np.array([3*a/4, 2]), math.sqrt(3)*a/8 - 0.0001)
print(is_in_polygon(Pgon, disc))

Calculate the angle between a point and a 3D facet

I have a 3D point (x,y,z) and a facet which is defined by three (x,y,z) points. I am trying to calculate the angle between the facet and the point. This is so I can shade the facet appropriately as though a light were moving across it in 3D space.
Hopefully this image gives an idea of what I'm trying to work out. The 3 dots are points in space relative to the facet and have different angles relative to its facing direction. It is this angle that I want to find. If the dots were points of light, the black dot would provide the brightest light, the blue would be about 50% brightness and the green would be very dark.
While I can calculate the magnitude, length and dot product of any two points, I am at a loss as to how to calculate the angle between the facet itself and the point.
I would like to know how to calculate whether the point is above or level with the facet, i.e.: the angle of the point relative to the facet.
The code I have so far is:
-- Get length of 2D or 3D vector
local function vector2d3dLength( vector )
return math.sqrt( vector.x*vector.x + vector.y*vector.y + (vector.z or 0)*(vector.z or 0) )
end
-- Normalise 2D or 3D vector
local function normalise2d3dVector( vector )
local len = vector2d3dLength( vector )
if (len == 0) then
return vector
end
local normalised = { x=vector.x/len, y=vector.y/len }
if (vector.z) then
normalised.z = vector.z/len
end
return normalised
end
local function crossProduct3d( a, b )
return { x=a.y*b.z − a.z*b.y, y=a.z*b.x − a.x*b.z, z=a.x*b.y − a.y*b.x }
end
local function dotProduct3d( a, b )
return a.x*b.x + a.y*b.y + a.z*b.z
end
-- subtract vector b from vector a
local function subtract_vectors( a, b )
local sub = { x=a.x-b.x, y=a.y-b.y }
if (a.z ~= nil and b.z ~= nil) then
sub.z = a.z-b.z
end
return sub
end
-- black/blue/green point
local lightsource = { x = 111, y = 112, z = 113 }
-- 3 points on the facet, first point is the center
local facet = {{ x = 1, y = 2, z = 3 },
{ x = 4, y = 5, z = 6 },
{ x = 7, y = 8, z = 9 }}
local facet_normal = normalise2d3dVector(crossProduct3d(
subtract_vectors(facet[2], facet[1]),
subtract_vectors(facet[3], facet[1])))
local direction_to_lightsource =
normalise2d3dVector(subtract_vectors(lightsource, facet[1]))
local cos_angle = dotProduct3d( direction_to_lightsource, facet_normal )
-- cos_angle may be negative, it depends on whether facet points are CW or CCW
local facet_brightness = cos_angle * max_brightness

Draw fitted line (OpenCV)

I'm using OpenCV to fit a line from a set of points using cvFitLine()
cvFitLine() returns a normalized vector that is co-linear to the line and a point on the line.
See details here
Using this information how can I get the equation of a line so that I can draw the line?
If cvFitLine() returns normalized vector (vx,vy) and point (x0,y0), then the equation of the line is
(x,y) = (x0,y0) + t*(vx,vy)
where t runs from −∞ to +∞.
This is what you asked for, but probably isn't immediately helpful in drawing the line. You would want to clip it either to the screen boundaries, or perhaps the bounding box of the the original set of points. To clip a line to a rectangle, just solve for values of t where the line crosses the boundary of the rectangle.
Just draw a big line instead of solving for the boundaries. eg:
cv.Line(img, (x0-m*vx[0], y0-m*vy[0]), (x0+m*vx[0], y0+m*vy[0]), (0,0,0))
will do it for example.. for m large enough :)
This just spells out #brainjam's answer in python for any passers by.
The formula for a line using a unit vector (vx, vy) and some point on the line (x0, y0) is:
(x, y) = (x0, y0) + t*(vx, vy)
The return from cv2.fitLine() is:
np.array([vx, vy, x0, y0])
In the example case, I have a line spanning the height of my image, so I want to find the t0 and t1 that intersect with y=0 and y=img.shape[0] (the top/bottom boundaries).
# get the fitLine for your set of points in the array, `line`
fit_line = cv2.fitLine(line, cv2.DIST_L2, 0, 0.01, 0.01)
# compute t0 for y=0 and t1 for y=img.shape[0]: (y-y0)/vy
t0 = (0-fit_line[3])/fit_line[1]
t1 = (img.shape[0]-fit_line[3])/fit_line[1]
# plug into the line formula to find the two endpoints, p0 and p1
# to plot, we need pixel locations so convert to int
p0 = (fit_line[2:4] + (t0 * fit_line[0:2])).astype(np.uint32)
p1 = (fit_line[2:4] + (t1 * fit_line[0:2])).astype(np.uint32)
# draw the line. For my version of opencv, it wants tuples so we
# flatten the arrays and convert
# args: cv2.line(image, p0, p1, color, thickness)
cv2.line(img, tuple(p0.ravel()), tuple(p1.ravel()), (0, 255, 0), 10)
I used a strategy similar to Karpathy up there but used an extra function. As you can see, I'm using cvClipLine to trim the line to the size of the image, which is unnecessary but does add a little niceness.
Also the multiplier here is defined as theMult = max(img->height,img->width) so we dont get numbers that might one day overflow or something.
void drawLine(IplImage * img, float line[4], int thickness,CvScalar color)
{
double theMult = max(img->height,img->width);
// calculate start point
CvPoint startPoint;
startPoint.x = line[2]- theMult*line[0];// x0
startPoint.y = line[3] - theMult*line[1];// y0
// calculate end point
CvPoint endPoint;
endPoint.x = line[2]+ theMult*line[0];//x[1]
endPoint.y = line[3] + theMult*line[1];//y[1]
// draw overlay of bottom lines on image
cvClipLine(cvGetSize(img), &startPoint, &endPoint);
cvLine(img, startPoint, endPoint, color, thickness, 8, 0);
}
Adding to #brainjam answer:
To clip to the bounding box of original set of points:
// std::vector<Point2i> points = ...
//lineParams: [vx,vy, x0,y0]: (normalized vector, point on our contour)
Vec4f lineParams; fitLine(points, lineParams, CV_DIST_L2, 0, 0.01, 0.01);
// derive the bounding xs of points
decltype(points)::iterator minXP, maxXP;
std::tie(minXP, maxXP) = std::minmax_element(points.begin(), points.end(), [](const Point2i& p1, const Point2i& p2){ return p1.x < p2.x; });
// derive y coords of fitted line
float m = lineParams[1] / lineParams[0];
int y1 = ((minXP->x - lineParams[2]) * m) + lineParams[3];
int y2 = ((maxXP->x - lineParams[2]) * m) + lineParams[3];
line(clearTarget, Point(minXP->x, y1), Point(maxXP->x, y2), Scalar(255, 255, 255), 2);
To clip to the entire image boundaries substitute minXP->x to 0 and maxXP->x to image.cols - 1, which was originally answered in https://stackoverflow.com/a/14192660/2380455
we use a " Vec4f fitedLine;" for fitted Line
in fitLine we have 4 parameters
if we consider Line relation az bellow:
Y - Y0 = M (X - X0)
we have
Y0 = FitedLine[3];
X0 = FitedLine[2];
m = FitedLine[1]/FitedLine[0];
so we have a Line equation we can find other points on it.

Resources