I'm trying to create a dataset of raw volumetric data consisting of geometrical shapes. The point is to use volume ray casting to project them in 2D but first I want to create the volume manually.
The geometry is consisting of one cylinder that is in the middle of the volume, along the Z axis and 2 smaller cylinders that are around the first one, deriving from rotations around the axes.
Here is my function so far:
function cyl= createCylinders(a, b, c, rad1, h1, rad2, h2)
% a : data width
% b : data height
% c : data depth
% rad1: radius of the big center cylinder
% rad2: radius of the smaller cylinders
% h1: height of the big center cylinder
% h2: height of the smaller cylinders
[Y X Z] =meshgrid(1:a,1:b,1:c); %matlab saves in a different order so X must be Y
centerX = a/2;
centerY = b/2;
centerZ = c/2;
theta = 0; %around y
fi = pi/4; %around x
% First cylinder
cyl = zeros(a,b,c);
% create for infinite height
R = sqrt((X-centerX).^2 + (Y-centerY).^2);
startZ = ceil(c/2) - floor(h1/2);
endZ = startZ + h1 - 1;
% then trim it to height = h1
temp = zeros(a,b,h1);
temp( R(:,:,startZ:endZ)<rad1 ) = 255;
cyl(:,:,startZ:endZ) = temp;
% Second cylinder
cyl2 = zeros(a,b,c);
A = (X-centerX)*cos(theta) + (Y-centerY)*sin(theta)*sin(fi) + (Z-centerZ)*cos(fi)*sin(theta);
B = (Y-centerY)*cos(fi) - (Z-centerZ)*sin(fi);
% create again for infinite height
R2 = sqrt(A.^2+B.^2);
cyl2(R2<rad2) = 255;
%then use 2 planes to trim outside of the limits
N = [ cos(fi)*sin(theta) -sin(fi) cos(fi)*cos(theta) ];
P = (rad2).*N + [ centerX centerY centerZ];
T = (X-P(1))*N(1) + (Y-P(2))*N(2) + (Z-P(3))*N(3);
cyl2(T<0) = 0;
P = (rad2+h2).*N + [ centerX centerY centerZ];
T = (X-P(1))*N(1) + (Y-P(2))*N(2) + (Z-P(3))*N(3);
cyl2(T>0) = 0;
% Third cylinder
% ...
cyl = cyl + cyl2;
cyl = uint8(round(cyl));
% ...
The concept is that the first cylinder is created and then "cut" according to the z-axis value, to define its height. The other cylinder is created using the relation A2 + B 2 = R2 where A and B are rotated accordingly using the rotation matrices only around x and y axes, using Ry(θ)Rx(φ) as described here.
Until now everything seems to be working, because I have implemented code (tested that it works well) to display the projection and the cylinders seem to have correct rotation when they are not "trimmed" from infinite height.
I calculate N which is the vector [0 0 1] aka z-axis rotated in the same way as the cylinder. Then I find two points P of the same distances that I want the cylinder's edges to be and calculate the plane equations T according to that points and normal vector. Lastly, I trim according to that equality. Or at least that's what I think I'm doing, because after the trimming I usually don't get anything (every value is zero). Or, the best thing I could get when I was experimenting was cylinders trimmed, but the planes of the top and bottom where not oriented well.
I would appreciate any help or corrections at my code, because I've been looking at the geometry equations and I can't find where the mistake is.
Edit:
This is a quick screenshot of the object I'm trying to create. NOTE that the cylinders are opaque in the volume data, all the inside is considered as homogeneous material.
I think instead of:
T = (X-P(1))*N(1) + (Y-P(2))*N(2) + (Z-P(3))*N(3);
you should try the following at both places:
T = (X-P(1)) + (Y-P(2)) + (Z-P(3));
Multiplying by N is to account for the direction of the axis of the 2nd cylinder which you have already done just above that step.
Related
I deployed number of random points with their speed and displacement, how can I make these point move frequently each second,
since the Euclidean distance between these points is updated each time interval (second) based on the coordinates of new positions?
NumNode=2;
ro=1000;
center=[0 0];
Initial_Direction = rand(NumNode, 1) * 2 *pi; v = 15/3.6; % [m/s] velocity of node
theta_Node=2*pi*(rand(NumNode,1));
% let the Nodes deployed away from the center of circle network layout
g = 0.5 * ro + 0.5 * ro * rand(NumNode,1);
PosNode_x=center(1)+g.*cos(theta_Node); % Initial positions
PosNode_y=center(2)+g.*sin(theta_Node);
PosNode = [PosNode_x ,PosNode_y];
DX2 = [cos(Initial_Direction(:)) .* v,sin(Initial_Direction(:)) .* v]; % displacement of Node
PosNodeNew = PosNode + DX2; % New position of node without rotating
rotaion2 = [cosd(Initial_Direction), -sind(Initial_Direction); sind(Initial_Direction), cosd(Initial_Direction)]; % Rotation matrix to make the movement of node similar to move on arc
% Index approach to multiply different matrix dimensions
index=1:numel(PosNode);
xyRotated1 = rotaion2;
xyRotated1(index)= rotaion2(index).*PosNode(index); % rotated matrix multiply by position of node
index2 = 1:numel(DX2);
Newposss=xyRotated1;
NewPosss(index2) = xyRotated1(index2) + DX2(index2);
gg= NewPosss(index2); % New position of node with rotating on arc
figure(1)
scatter(PosDrone_x,PosDrone_x,'r+')
figure(2)
scatter(PosDroneNew(:,1),PosDroneNew(:,2),'b*')
figure (3)
scatter (xyRotated1(:,1),xyRotated1(:,2),'r.')
axis equal
figure (4)
scatter(NewPosss(1,:),NewPosss(1,:),'b*')
axis equal
I am interested in building a hexagonal Torus using a mesh of points?
I think I can start with a 2-d polygon, and then iterate 360 times (1 deg resolution) to build a complete solid.
Is this the best way to do this? What I'm really after is building wing profiles with variable cross section geometry over it's span.
In Your way You can do this with polyhedron(). Add an appropriate number of points per profile in defined order to a vector „points“, define faces by the indices of the points in a second vector „faces“ and set both vectors as parameter in polyhedron(), see documentation. You can control the quality of the surface by the number of points per profile and the distance between the profiles (sectors in torus).
Here an example code:
// parameter:
r1 = 20; // radius of torus
r2 = 4; // radius of polygon/ thickness of torus
s = 360; // sections per 360 deg
p = 6; // points on polygon
a = 30; // angle of the first point on Polygon
// points on cross-section
// angle = 360*i/p + startangle, x = r2*cos(angle), y = 0, z = r2*sin(angle)
function cs_point(i) = [r1 + r2*cos(360*i/p + a), 0, r2*sin(360*i/p + a)];
// returns to the index in the points - vector the section number and the number of the point on this section
function point_index(i) = [floor(i/p), i - p*floor(i/p)];
// returns the points x-, y-, z-coordinates by rotatating the corresponding point from crossection around the z-axis
function iterate_cs(i) = [cs[point_index(i)[1]][0]*cos(360*floor(i/p)/s), cs[point_index(i)[1]][0]*sin(360*floor(i/p)/s), cs[point_index(i)[1]][2]];
// for every point find neighbour points to build faces, ( + p: point on the next cross-section), points ordered clockwise
// to connect point on last section to corresponding points on first section
function item_add1(i) = i >= (s - 1)*p ? -(s)*p : 0;
// to connect last point on section to first points on the same and the next section
function item_add2(i) = i - p*floor(i/p) >= p-1 ? -p : 0;
// build faces
function find_neighbours1(i) = [i, i + 1 + item_add2(i), i + 1 + item_add2(i) + p + item_add1(i)];
function find_neighbours2(i) = [i, i + 1 + + item_add2(i) + p + item_add1(i), i + p + item_add1(i)];
cs = [for (i = [0:p-1]) cs_point(i)];
points = [for (i = [0:s*p - 1]) iterate_cs(i)];
faces1 = [for (i = [0:s*p - 1]) find_neighbours1(i)];
faces2 = [for (i = [0:s*p - 1]) find_neighbours2(i)];
faces = concat(faces1, faces2);
polyhedron(points = points, faces = faces);
here the result:
Since openscad 2015-03 faces can have more than 3 points, if all points of the face are on the same plane. So in this case faces could be build in one step too.
Are you building smth. like NACA airfoils? https://en.wikipedia.org/wiki/NACA_airfoil
There are a few OpenSCAD designs for those floating around, see e.g. https://www.thingiverse.com/thing:898554
I have created a synthetic image that consists of a circle at the centre of a box with the code below.
%# Create a logical image of a circle with image size specified as follows:
imageSizeY = 400;
imageSizeX = 300;
[ygv, xgv] = meshgrid(1:imageSizeY, 1:imageSizeX);
%# Next create a logical mask for the circle with specified radius and center
centerY = imageSizeY/2;
centerX = imageSizeX/2;
radius = 100;
Img = double( (ygv - centerY).^2 + (xgv - centerX).^2 <= radius.^2 );
%# change image labels from double to numeric
for ii = 1:numel(Img)
if Img(ii) == 0
Img(ii) = 2; %change label from 0 to 2
end
end
%# plot image
RI = imref2d(size(Img),[0 size(Img, 2)],[0 size(Img, 1)]);
figure, imshow(Img, RI, [], 'InitialMagnification','fit');
Now, i need to create a rectangular mask (with label == 3, and row/col dimensions: 1 by imageSizeX) across the image from top to bottom and at known angles with the edges of the circle (see attached figure). Also, how can i make the rectangle thicker than 1 by imageSizeX?. As another option, I would love to try having the rectangle stop at say column 350. Lastly, any ideas how I can improve on the resolution? I mean is it possible to keep the image size the same while increasing/decreasing the resolution.
I have no idea how to go about this. Please i need any help/advice/suggestions that i can get. Many thanks!.
You can use the cos function to find the x coordinate with the correct angle phi.
First notice that the angle between the radius that intersects the vertex of phi has angle with the x-axis given by:
and the x coordinate of that vertex is given by
so the mask simply needs to set that row to 3.
Example:
phi = 45; % Desired angle in degrees
width = 350; % Desired width in pixels
height = 50; % Desired height of bar in pixels
theta = pi-phi*pi/180; % The radius angle
x = centerX + round(radius*cos(theta)); % Find the nearest row
x0 = max(1, x-height); % Find where to start the bar
Img(x0:x,1:width)=3;
The resulting image looks like:
Note that the max function is used to deal with the case where the bar thickness would extend beyond the top of the image.
Regarding resolution, the image resolution is determined by the size of the matrix you create. In your example that is (400,300). If you want higher resolution simply increase those numbers. However, if you would like to link the resolution to a higher DPI (Dots per Inch) so there are more pixels in each physical inch you can use the "Export Setup" window in the figure File menu.
Shown here:
I need to slide a window over a 3d volume. The sliding is only on one layer of the 3d volume, i.e for each x,y with one specific z.
This is what I want to do in a loop:
for each x,y,z, for example:
px =9; py =9; pz =12;
a = rand(50,50,50);
[x y z] = meshgrid(1:50,1:50,1:50);
r = 3;
%-------------loop starts here:
% creating a shaped window, for example sphere of radius r
inds = find((x-px).^2 + (y-py).^2 + (z-pz).^2 <= r.^2);
% getting the relevant indices, here, it is the sphere around px,py,pz
[i,j,k] = ind2sub(size(a),inds);
% adjust the center of the sphere to be at (0,0,0) instead of (px,py,pz)
adj_inds = bsxfun(#minus,[i,j,k],[px,py,pz]);
% Computing for each sphere some kind of median point
cx = sum(a(inds).*adj_inds(:,1))./sum(a(inds));
cy = sum(a(inds).*adj_inds(:,2))./sum(a(inds));
cz = sum(a(inds).*adj_inds(:,3))./sum(a(inds));
%Saving the result: the distance between the new point and the center of the sphere.
res(yc,xc) = sqrt(sum([cx,cy,cz].^2));
%-------------
Now, all of this should happen many many time, ( ~300000), loop takes ages, convolution returns 3d volume (for each x,y,z) while I need to perform this only for each (x,y) and a list of z's.
Help please...
Thanks
matlabit
I'm extracting the outline of blob the following way:
bw = im2bw(image, threshold);
boundaries = bwboundaries(bw);
plot(boundaries(:, 2), boundaries(:, 1), 'k', 'LineWidth', 2);
what I would like to do now, is to scale boundaries so that I can plot a smaller version of the boundaries inside the original boundaries. Is there an easy way to do this?
Here's an example on what the result should look like: black is the original bounding box, and red is the same bounding box, just scaled (but with same center as black box).
EDIT:
I guess I can scale each point individually, but then I still have to recenter the coordinates. Is there a better way of doing this?
scale = 0.7
nbr_points = size(b, 1);
b_min = nan(nbr_points, 2);
for k = 1 : nbr_points
b_min(k, :) = ([scale 0; 0 scale] * b(k, 1:2)')';
end
Just creating a function which does this should be easy.
function scaledB = scaleBoundaries(B,scaleFactor)
% B is a cell array of boundaries. The output is a cell array
% containing the scaled boundaries, with the same center of mass
% as the input boundaries.
%%
for k = 1:length(B)
scaledB{k} = B{k} .* scaleFactor;
com = mean(B{k}); % Take the center of mass of each boundary
sCom = mean(scaledB{k}); % Take the center of mass of each scaled boundary
difference = com-sCom; % Difference between the centers of mass
% Recenter the scaled boundaries, by adding the difference in the
% centers of mass to the scaled boundaries:
scaledB{k}(:,1) = scaledB{k}(:,1) + difference(1);
scaledB{k}(:,2) = scaledB{k}(:,2) + difference(2);
end
end
Or did you want to avoid something unoptimized for speed purposes?