I've trying to generate figures by revolution reading the profile of figure from a ply. I follow the steps from here and other similar questioons, but my problem persist. When I try to rotate a Ply with only 2 points 36 stesps I get this if I puth the camara on the top the cilinder:
My code after a revision of the method rotate is:
void Figura::rotateY(int ngiros){
//Variables de rotacion.
//double alfa = 2*M_PI/ngiros;
int long_perfil = vertices.size();
vector<_vertex3f> new_vertices;
cout << long_perfil << " vertices" << endl;
_vertex3f aux1, aux2;
for(int i=0; i < ngiros; i++){
double alfa = (2*M_PI/ngiros)*i;
for(int j=0; j < long_perfil; j++){
aux1 = vertices.at(j);
aux1._0 = (cos(alfa) * aux1._0) + (sin(alfa) * aux1._2);
aux1._2 = (cos(alfa) * aux1._2) - (sin(alfa) * aux1._0);
vertices.push_back(aux1);
}
}
//vertices.clear();
//vertices = new_vertices;
//caras
for(int i=0; i < vertices.size(); i++){
_vertex3i aux(i, i+1, i+long_perfil);
_vertex3i aux2(i, i+long_perfil+1, i+1);
caras.push_back(aux);
caras.push_back(aux2);
}
}
}
I can't find my error. Some help will be welcome.
It looks like you're unclear over what coordinate system your original curve is in, and how you're applying a rotation to it. With your current code, you're just rotating the points by a variable amount, but keep them all within the same plane. You can tell from just looking at the code superficially: You never set a value for the y-coordinate of any of the points, so the whole result is not a 3D shape, but entirely in the y=0 plane. As flat as a pancake...
Another thing you need to be careful about is that you don't modify values while you're still using the old value:
aux1._0 = (cos(alfa) * aux1._0) + (sin(alfa) * aux1._2);
aux1._2 = (cos(alfa) * aux1._2) - (sin(alfa) * aux1._0);
Here, you're modifying the value of aux1._0 in the first statement, while the second statement should in fact still use the old value.
Let's say your original curve is in the x/y-plane, and you want to rotate around the y-axis. For this to result in a nice 3D shape, all the x-coordinates of the curve should be positive:
^ y
|
|--
| \
| \_
| | x
--------------->
| |
| /
| /
| _/
|/
Picture the z-axis pointing out of the screen.
Now, to rotate this curve by a given angle alpha around the y-axis, we leave the y-coordinate unchanged, and rotate the point (x, 0) by alpha within the xz-plane to get the new values for x and z. The new coordinates (x', y', z') for input point (x, y) of the shape are then:
x' = x * cos(alpha)
y' = y
z' = x * sin(alpha)
As a modified version of your code:
for(int i=0; i < ngiros; i++){
double alfa = (2*M_PI/ngiros)*i;
for(int j=0; j < long_perfil; j++){
aux1 = vertices.at(j);
aux2._0 = cos(alfa) * aux1._0;
aux2._1 = aux1._1;
aux2._2 = sin(alfa) * aux1._0;
vertices.push_back(aux2);
}
}
Related
Dears,
With the below code, I rotate my cv::Mat object (I'm not using any Cv's functions, apart from load/save/convertionColor.., as this is a academic project) and I receive a cropped Image
rotation function:
float rads = angle*3.1415926/180.0;
float _cos = cos(-rads);
float _sin = sin(-rads);
float xcenter = (float)(src.cols)/2.0;
float ycenter = (float)(src.rows)/2.0;
for(int i = 0; i < src.rows; i++)
for(int j = 0; j < src.cols; j++){
int x = ycenter + ((float)(i)-ycenter)*_cos - ((float)(j)-xcenter)*_sin;
int y = xcenter + ((float)(i)-ycenter)*_sin + ((float)(j)-xcenter)*_cos;
if (x >= 0 && x < src.rows && y >= 0 && y < src.cols) {
dst.at<cv::Vec4b>(i ,j) = src.at<cv::Vec4b>(x, y);
}
else {
dst.at<cv::Vec4b>(i ,j)[3] = 0;
}
}
I would like to know, How I can keep my Full image every time I want to rotate it.
Am I missing something in my function maybe?
thanks in advance
The rotated image usually has to be large than the old image to store all pixel values.
Each point (x,y) is translated to
(x', y') = (x*cos(rads) - y*sin(rads), x*sin(rads) + y*cos(rads))
An image with height h and width w, center at (0,0) and corners at
(h/2, w/2)
(h/2, -w/2)
(-h/2, w/2)
(-h/2, -w/2)
has a new height of
h' = 2*y' = 2 * (w/2*sin(rads) + h/2*cos(rads))
and a new width of
w' = 2*x' = 2 * (w/2*cos(rads) + h/2*sin(rads))
for 0 <= rads <= pi/4. It is x * y <= x' * y' and for rads != k*pi/2 with k = 1, 2, ... it is x * y < x' * y'
In any case the area of the rotated image is same size as or larger than the area of the old image.
If you use the old size, you cut off the corners.
Example:
Your image has h=1, w=1 and rads=pi/4. You need a new image with h'=sqrt(2)=1.41421356237 and w'=sqrt(2)=1.41421356237 to store all pixel values. The pixel from (1,1) is translated to (0, sqrt(2)).
I've loaded a texture mapped OBJ via vtkOBJReader and loaded it into a vtkModifiedBSPTree:
auto readerOther(vtkSmartPointer<vtkOBJReader>::New());
auto rawOtherPath(modelPathOther.toLatin1());
readerOther->SetFileName(rawOtherPath.data());
readerOther->Update();
auto meshDataOther(readerOther->GetOutput());
auto bspTreeOther(vtkSmartPointer<vtkModifiedBSPTree>::New());
bspTreeOther->SetDataSet(meshDataOther);
bspTreeOther->BuildLocator();
I then compute my line segment start and end and feed that into
if (bspTreeOther->IntersectWithLine(p1, p2, tolerance, distanceAlongLine, intersectionCoords, pcoords, subId, cellId, cell))
With all the relevant predefined variables of course.
What I need is the texture's UV coordinates at the point of intersection.
I'm so very new to VTK that I've not yet caught the logic of how its put together yet; the abstraction layers are still losing me while I'm digging through the source.
I've hunted for this answer across SO and the VTK users archives and found vague hints given by those who understood VTK deeply to those who were nearly there themselves, and thus of little help to me thus far.
(Appended 11/9/2018)
To clarify, I'm working with non-degenerate triangulated meshes created by a single 3D scanner shot, so quads and other higher polygons are not going to be ever seen by my code. A general solution should account for such things, but that can be accomplished via triangulating the mesh first via a good application of handwavium.
Code
Note that if one vertex belongs to several polygons and has different texture coordinates, VTK will create duplicates of the vertex.
I don't use vtkCleanPolyData, because VTK will merge such "duplicates" and we will lose needed information, as far as I know.
I use vtkCellLocator instead of vtkModifiedBSPTree,
because in my case it was faster.
The main file main.cpp.
You can find magic numbers in start and end arrays — these are your p1 and p2.
I've set these values just for example
#include <vtkSmartPointer.h>
#include <vtkPointData.h>
#include <vtkCellLocator.h>
#include <vtkGenericCell.h>
#include <vtkOBJReader.h>
#include <vtkTriangleFilter.h>
#include <vtkMath.h>
#include <iostream>
int main(int argc, char * argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << argv[0] << " OBJ_file_name" << std::endl;
return EXIT_FAILURE;
}
auto reader{vtkSmartPointer<vtkOBJReader>::New()};
reader->SetFileName(argv[1]);
reader->Update();
// Triangulate the mesh if needed
auto triangleFilter{vtkSmartPointer<vtkTriangleFilter>::New()};
triangleFilter->SetInputConnection(reader->GetOutputPort());
triangleFilter->Update();
auto mesh{triangleFilter->GetOutput()};
// Use `auto mesh(reader->GetOutput());` instead if no triangulation needed
// Build a locator to find intersections
auto locator{vtkSmartPointer<vtkCellLocator>::New()};
locator->SetDataSet(mesh);
locator->BuildLocator();
// Initialize variables needed for intersection calculation
double start[3]{-1, 0, 0.5};
double end[3]{ 1, 0, 0.5};
double tolerance{1E-6};
double relativeDistanceAlongLine;
double intersectionCoordinates[3];
double parametricCoordinates[3];
int subId;
vtkIdType cellId;
auto cell{vtkSmartPointer<vtkGenericCell>::New()};
// Find intersection
int intersected = locator->IntersectWithLine(
start,
end,
tolerance,
relativeDistanceAlongLine,
intersectionCoordinates,
parametricCoordinates,
subId,
cellId,
cell.Get()
);
// Get points of intersection cell
auto pointsIds{vtkSmartPointer<vtkIdList>::New()};
mesh->GetCellPoints(cellId, pointsIds);
// Store coordinates and texture coordinates of vertices of the cell
double meshTrianglePoints[3][3];
double textureTrianglePoints[3][2];
auto textureCoordinates{mesh->GetPointData()->GetTCoords()};
for (unsigned pointNumber = 0; pointNumber < cell->GetNumberOfPoints(); ++pointNumber)
{
mesh->GetPoint(pointsIds->GetId(pointNumber), meshTrianglePoints[pointNumber]);
textureCoordinates->GetTuple(pointsIds->GetId(pointNumber), textureTrianglePoints[pointNumber]);
}
// Normalize the coordinates
double movedMeshTrianglePoints[3][3];
for (unsigned i = 0; i < 3; ++i)
{
movedMeshTrianglePoints[0][i] = 0;
movedMeshTrianglePoints[1][i] =
meshTrianglePoints[1][i] -
meshTrianglePoints[0][i];
movedMeshTrianglePoints[2][i] =
meshTrianglePoints[2][i] -
meshTrianglePoints[0][i];
}
// Normalize the texture coordinates
double movedTextureTrianglePoints[3][2];
for (unsigned i = 0; i < 2; ++i)
{
movedTextureTrianglePoints[0][i] = 0;
movedTextureTrianglePoints[1][i] =
textureTrianglePoints[1][i] -
textureTrianglePoints[0][i];
movedTextureTrianglePoints[2][i] =
textureTrianglePoints[2][i] -
textureTrianglePoints[0][i];
}
// Calculate SVD of a matrix consisting of normalized vertices
double U[3][3];
double w[3];
double VT[3][3];
vtkMath::SingularValueDecomposition3x3(movedMeshTrianglePoints, U, w, VT);
// Calculate pseudo inverse of a matrix consisting of normalized vertices
double pseudoInverse[3][3]{0};
for (unsigned i = 0; i < 3; ++i)
{
for (unsigned j = 0; j < 3; ++j)
{
for (unsigned k = 0; k < 3; ++k)
{
if (w[k] != 0)
{
pseudoInverse[i][j] += VT[k][i] * U[j][k] / w[k];
}
}
}
}
// Calculate interpolation matrix
double interpolationMatrix[3][2]{0};
for (unsigned i = 0; i < 3; ++i)
{
for (unsigned j = 0; j < 2; ++j)
{
for (unsigned k = 0; k < 3; ++k)
{
interpolationMatrix[i][j] += pseudoInverse[i][k] * movedTextureTrianglePoints[k][j];
}
}
}
// Calculate interpolated texture coordinates of the intersection point
double interpolatedTexturePoint[2]{textureTrianglePoints[0][0], textureTrianglePoints[0][1]};
for (unsigned i = 0; i < 2; ++i)
{
for (unsigned j = 0; j < 3; ++j)
{
interpolatedTexturePoint[i] += (intersectionCoordinates[j] - meshTrianglePoints[0][j]) * interpolationMatrix[j][i];
}
}
// Print the result
std::cout << "Interpolated texture coordinates";
for (unsigned i = 0; i < 2; ++i)
{
std::cout << " " << interpolatedTexturePoint[i];
}
std::cout << std::endl;
return EXIT_SUCCESS;
}
CMake project file CMakeLists.txt
cmake_minimum_required(VERSION 3.1)
PROJECT(IntersectInterpolate)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
add_executable(IntersectInterpolate MACOSX_BUNDLE main.cpp)
if(VTK_LIBRARIES)
target_link_libraries(IntersectInterpolate ${VTK_LIBRARIES})
else()
target_link_libraries(IntersectInterpolate vtkHybrid vtkWidgets)
endif()
Math
What we need
Suppose you have a mesh consisting of triangles and your vertices have texture coordinates.
Given vertices of a triangle A, B and C, corresponding texture coordinates A', B' and C', you want to find a mapping (to interpolate) from another inner and boundary points of the triangle to the texture.
Let's make some rational assumptions:
Points A, B, C should correspond to their texture coordinates A', B', C';
Each point X on the border, say AB, should correspond to the points of A'B' line in following way: |AX| / |AB| = |A'X'| / |A'B'| — half way on the original triangle should be a half way on the texture map;
Centroid of the triangle (A + B + C) / 3 should correspond to centroid of the texture triangle (A' + B' + C') / 3.
Equations to solve
Looks like we want to have affine mapping: coordinates of vertices of the original triangle should be multiplied by some coefficients and be added to some constants.
Let's construct the system of equations
Ax * Mxx + Ay * Myx + Az * Mzx + M0x = A'x
Ax * Mxy + Ay * Myy + Az * Mzy + M0y = A'y
Ax * Mxz + Ay * Myz + Az * Mzz + M0z = 0
and the same for B and C.
You can see that we have 9 equations and 12 unknowns.
Though, equations containing Miz (for i in {x, y, z}) have the solution 0 and don't play any role in further computations, so we can just set them equal to 0.
Thus, we have system with 6 equations and 8 unknowns
Ax * Mxx + Ay * Myx + Az * Mzx + M0x = A'x
Ax * Mxy + Ay * Myy + Az * Mzy + M0y = A'y
Let's write entire system in matrix view
-- -- -- -- -- --
| 1 Ax Ay Az | | M0x M0y | | A'x A'y |
| 1 Bx By Bz | x | Mxx Mxy | = | B'x B'y |
| 1 Cx Cy Cz | | Myx Myy | | C'x C'y |
-- -- | Mzx Mzy | -- --
-- --
I subtract coordinates of A vertex from B and C
and texture coordinates A' from B' and C',
and now we have the triangle with the first vertex located
in start of coordinate system as well as corresponding texture coordinates.
This means that now triangles are not translated (moved) one relative to another
and we don't need M0 part of interpolation matrix
-- -- -- -- -- --
| Bx By Bz | | Mxx Mxy | | B'x B'y |
| Cx Cy Cz | x | Myx Myy | = | C'x C'y |
-- -- | Mzx Mzy | -- --
-- --
Solution
Let's call the first matrix P, the second M and the last one T
P M = T
The matrix P is not square.
If we add zero row to it, the matrix becomes singular.
So, we have to calculate pseudo-inverse of it in order to solve the equation.
There's no function for calculating pseudo-inverse matrix in VTK.
We go to Moore–Penrose inverse article on Wikipedia and see that it can be calculated using SVD.
VTKMath::SingularValueDecomposition3x3 function allows us to do it.
The function gives us U, S and VT matrices.
I'll write pseudo-inverse of matrix P as P",
transposition of U as UT and transposition of VT as V.
Pseudo-inverse of diagonal matrix S is a matrix with 1 / Sii elements
where Sii is not a zero and 0 for zero elements
P = U S VT
P" = V S" UT
M = P" T
Usage
To apply interpolation matrix,
we need to not forget that we need to translate input and output vectors.
A' is a 2D vector of texture coordinates of the first vertex in the triangle,
A is a 3D vector of coordinates of the vertex,
M is the found interpolation matrix,
p is a 3D intersection point we want to get texture coordinates for,
t' is the resulting 2D vector with interpolated texture coordinates
t' = A' + (p - A) M
[Rewritten 2019/5/7 to reflect an updated understanding.]
After finding out that the parametric coordinates are inputs to a function from which one can get barycentric coordinates in the case of triangular cells, and then learning about what barycentric coordinates are, I was able to work out the following.
const auto readerOther(vtkSmartPointer<vtkOBJReader>::New());
const auto rawOtherPath(modelPathOther.toLatin1());
readerOther->SetFileName(rawOtherPath.data());
readerOther->Update();
const auto meshDataOther(readerOther->GetOutput());
const auto bspTreeOther(vtkSmartPointer<vtkModifiedBSPTree>::New());
bspTreeOther->SetDataSet(meshDataOther);
bspTreeOther->BuildLocator();
double point1[3]{0.0, 0.0, 0.0}; // start of line segment used to intersect the model.
double point2[3]{0.0, 0.0, 10.0}; // end of line segment
double distanceAlongLine;
double intersectionCoords[3]; // The coordinate of the intersection.
double parametricCoords[3]; // Parametric Coordinates of the intersection - see https://lorensen.github.io/VTKExamples/site/VTKBook/08Chapter8/#82-interpolation-functions
int subId; // ?
vtkIdType cellId;
double intersectedTextureCoords[2];
if (bspTreeOther->IntersectWithLine(point1, point2, TOLERANCE, distanceAlongLine, intersectionCoords, parametricCoords, subId, cellId))
{
const auto textureCoordsOther(meshDataOther->GetPointData()->GetTCoords());
const auto pointIds{meshDataOther->GetCell(cellId)->GetPointIds()};
const auto vertexIndex0{pointIds->GetId(0)};
const auto vertexIndex1{pointIds->GetId(1)};
const auto vertexIndex2{pointIds->GetId(2)};
double texCoord0[2];
double texCoord1[2];
double texCoord2[2];
textureCoordsOther->GetTuple(vertexIndex0, texCoord0);
textureCoordsOther->GetTuple(vertexIndex1, texCoord1);
textureCoordsOther->GetTuple(vertexIndex2, texCoord2);
const auto parametricR{parametricCoords[0]};
const auto parametricS{parametricCoords[1]};
const auto barycentricW0{1 - parametricR - parametricS};
const auto barycentricW1{parametricR};
const auto barycentricW2{parametricS};
intersectedTextureCoords[0] =
barycentricW0 * texCoord0[0] +
barycentricW1 * texCoord1[0] +
barycentricW2 * texCoord2[0];
intersectedTextureCoords[1] =
barycentricW0 * texCoord0[1] +
barycentricW1 * texCoord1[1] +
barycentricW2 * texCoord2[1];
}
Please note that this code is an interpretation of the actual code I'm using; I'm using Qt and its QVector2D and QVector3D classes along with some interpreter glue functions to go to and from arrays of doubles.
See https://lorensen.github.io/VTKExamples/site/VTKBook/08Chapter8 for details about the parametric coordinate systems of various cell types.
Given two (very simplified) classes:
class Rectangle {
public:
int iL,jL; // lower left point of rectangle
int width, length; // width: in i-direction, length: in j-direction
};
class Circle {
public:
int iC,jC; // center-point of circle
int radius;
};
If I want to iterate over all elements in a Rectangle, I can simply do that by:
for (int i = iL; i < iL-width; i--)
for (int j = jL; j < jL+length; j++)
doSomething();
My problem is to implement a smart way of iterating over all elements in Circle. My current solution looks as follows:
for (int i = iC-radius; i <= iC+radius; i++)
for (int j = jC-radius; j <= jC+radius; j++)
if ( sqrt(pow(i-iC,2)+pow(j-jC,2)) <= r ) // checking if (i,j) lies within the circle (or its boundary)
doSomething();
However, for radius getting large my current solution is very time-expensive (since I touch many elements which aren't in the Circle and since I always need to evaluate pow). Can you think of a more intelligent and efficient way of iteration over all Circle-elements?
For every line find the first column that belongs to the circle, and walk from this column to one mirrored relative to the circle center. Pseudo code
for (int iy = - radius to radius; iy++)
dx = (int) sqrt(radius * radius - iy * iy)
for (int ix = - dx to dx; ix++)
doSomething(CX + ix, CY + iy);
Let the radius of the circle be r. Consider a Square of size (2r+1)*(2r+1) around the circle to be drawn. So equidistant points in square are stored in 2D array .
Now walk through every point inside the square. For every point (x,y), if (x, y) lies inside the circle (or x^2+ y^2 < r^2), then print it. For example, equidistant points form a 10x10 array, and a chosen "center" of the array at (x,y):
for i from 0 to 9 {
for j from 0 to 9 {
a = i - x
b = j - y
if a*a + b*b <= r*r {
// Do something here
}
}
}
Let's say I have image, which I need to rotate 90 degrees in any direction and I just can't understand how to do that clear. I need to work with matrix, where Width - it's X, and Height - it's Y. I've already done rotating an image 180 degrees, but can't figure out 90 degrees.
Here are the examples. Let's say I have an image 3x4 pixels. Width = 3, Height = 4, the amount of data in each cell - N = Width * Height = 3 * 4 = 12. Let's make the matrix:
The formula to easily go through the matrix is y*Width + x. And the formula for our rotating 180 degrees is:
N - Width + x - y * Width
So we have:
DataOut [y * Width + x] = DataIn [N - Width + x - y * Width]
Using this formula we get:
But I can't come up with the formula of rotating 90 degrees. Can you help me, please?
you can simply rotate the matrix by this:
for(int i=0; i<cols; i++)
{
for(int j=1; j<=rows; j++)
{
datOut[i][j]= datIn[rows-j][i];
}
}
and in 1-D array:
for(int i=0; i<cols; i++)
{
for(int j=1; j<=rows; j++)
{
datOut[i * rows + j]= datIn[(rows-j) * cols + i];
}
}
You can easily convert the (x + y * width) to a simpler (x, y) representation.
using P = point_data_type;
P point(int x, int y){
return DataIn[x + y * width]; // N - ...?
}
Now a right angle rotation is just a coordinate switch and maybe a sign correction.
P rotate90(int x, int y){
return point(y, x);
}
This is a positive rotation in a left-handed system.
Subtract x from Width if the rotation is in the wrong direction.
How do I draw a cylinder with OpenGL in OpenTK?
Sample code from an older project of mine. This creates an "uncapped" cylinder (top and bottom are empty).
int segments = 10; // Higher numbers improve quality
int radius = 3; // The radius (width) of the cylinder
int height = 10; // The height of the cylinder
var vertices = new List<Vector3>();
for (double y = 0; y < 2; y++)
{
for (double x = 0; x < segments; x++)
{
double theta = (x / (segments - 1)) * 2 * Math.PI;
vertices.Add(new Vector3()
{
X = (float)(radius * Math.Cos(theta)),
Y = (float)(height * y),
Z = (float)(radius * Math.Sin(theta)),
});
}
}
var indices = new List<int>();
for (int x = 0; x < segments - 1; x++)
{
indices.Add(x);
indices.Add(x + segments);
indices.Add(X + segments + 1);
indices.Add(x + segments + 1);
indices.Add(x + 1);
indices.Add(x);
}
You can now render the cylinder like this:
GL.Begin(BeginMode.Triangles);
foreach (int index in indices)
GL.Vertex3(vertices[index]);
GL.End();
You can also upload vertices and indices into a vertex buffer object to improve performance.
Generating the geometry for a cylinder is quite simple (let's consider a Z-aligned cylinder). Let me use pseudocode:
points = list of (x,y,z)
where x = sin(a)*RADIUS, y = cos(a)*RADIUS, z = b,
for each a in [0..2*PI) with step StepA,
for each b in [0..HEIGHT] with step StepB
About the indices: Let us assume N equal to the number of "levels" or "slices" of the cylinder (which depends on HEIGHT and StepB) and M equal to the number of points on every "slice" (which depends on StepA).
The cylinder contains some quads, each spanning 2 neighbouring slices, so the indices would look like:
indices = list of (a,b,c,d)
where a = M * slice + point,
b = M * slice + (point+1) % M,
c = (M+1) * slice + (point+1) % M,
d = (M+1) * slice + point
for each slice in [0..N-2]
for each point in [0..M-1]
If you need normals for the cylinder, they are simple to generate:
normals = (x/RADIUS,y/RADIUS,0)
for each (x,y,z) in points
That's it for the round part of the cylinder, you might also want the "caps" but I believe they are easy to do.
I'll leave the fun part of translating my pseudocode into your language of choice for you. :)
The rest is to create/bind the VBO, load up the geometry, set pointers, use your shader of choice and call glDrawArrays(...) - any OpenGL 3 tutorial should cover this; are you familiar with that part?