How to correctly use VTK ConstrainedDelaunay2D? - c++

I've started from the VTK ConstrainedDelaunay2D example and added my own points:
#include <vtkSmartPointer.h>
#include <vtkDelaunay2D.h>
#include <vtkCellArray.h>
#include <vtkProperty.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolygon.h>
#include <vtkMath.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkNamedColors.h>
#include <vtkVersionMacros.h> // For version macros
int main(int, char *[])
{
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
int ptsHeight = 400;
std::vector<std::vector<int>> pts{ {166, 127},{103, 220},{166, 190},{174, 291},{189, 226},{227, 282},{213, 187},{242, 105},{196, 131},{182, 83} };
for (size_t i = 0; i < pts.size(); i++)
{
// !important: flip y
int x = pts[i][0];
int y = ptsHeight - pts[i][1];
points->InsertNextPoint(x, y, 0);
}
vtkSmartPointer<vtkPolyData> aPolyData = vtkSmartPointer<vtkPolyData>::New();
aPolyData->SetPoints(points);
// Create a cell array to store the polygon in
vtkSmartPointer<vtkCellArray> aCellArray = vtkSmartPointer<vtkCellArray>::New();
// Define a polygonal hole with a clockwise polygon
vtkSmartPointer<vtkPolygon> aPolygon = vtkSmartPointer<vtkPolygon>::New();
for (unsigned int i = 0; i < pts.size(); i++)
{
aPolygon->GetPointIds()->InsertNextId(i);
}
aCellArray->InsertNextCell(aPolygon);
// Create a polydata to store the boundary. The points must be the
// same as the points we will triangulate.
vtkSmartPointer<vtkPolyData> boundary =
vtkSmartPointer<vtkPolyData>::New();
boundary->SetPoints(aPolyData->GetPoints());
boundary->SetPolys(aCellArray);
// Triangulate the grid points
vtkSmartPointer<vtkDelaunay2D> delaunay =
vtkSmartPointer<vtkDelaunay2D>::New();
delaunay->SetInputData(aPolyData);
delaunay->SetSourceData(boundary);
// Visualize
vtkSmartPointer<vtkPolyDataMapper> meshMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
meshMapper->SetInputConnection(delaunay->GetOutputPort());
vtkSmartPointer<vtkNamedColors> colors =
vtkSmartPointer<vtkNamedColors>::New();
vtkSmartPointer<vtkActor> meshActor =
vtkSmartPointer<vtkActor>::New();
meshActor->SetMapper(meshMapper);
meshActor->GetProperty()->EdgeVisibilityOn();
meshActor->GetProperty()->SetEdgeColor(colors->GetColor3d("Peacock").GetData());
meshActor->GetProperty()->SetInterpolationToFlat();
meshActor->GetProperty()->SetBackfaceCulling(true);
// Create a renderer, render window, and interactor
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
// Add the actor to the scene
renderer->AddActor(meshActor);
//renderer->AddActor(boundaryActor);
renderer->SetBackground(colors->GetColor3d("Mint").GetData());
// Render and interact
renderWindow->SetSize(640, 480);
renderWindow->Render();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
I'm experiencing two issues:
I get different results if I flip the Y coordinates: why is that ?
Why are there faces pointing in the wrong direction (flipped normal / wrong winding )?
Here's what I mean by the 1st issue:
If I don't flip the Y coordinates I get this:
I get the same effect if I don't flip the Y axis but insert the boundary polygon in reverse order:
for (unsigned int i = 0; i < pts.size(); i++)
{
aPolygon->GetPointIds()->InsertNextId(pts.size() - 1 - i);
}
I don't think I fully understand how the boundary/constraint works.
I thoght that the same points should produce the same triangulation wether the vertices are flipped vertically or not. (I suspect the order of indices changes then ?)
Regarding the second issue (unpredictable flipped faces) I'm not sure what the best way forward is. I had a look at the vtkDelaunay2D class and couldn't find anything related.
(I've tried setting projection plane mode to VTK_DELAUNAY_XY_PLANE, but it didn't seem to affect the output)
I've also tried to use vtkPolyDataNormals but got no output:
vtkSmartPointer<vtkPolyDataNormals> normalGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
normalGenerator->SetInputData(delaunay->GetOutput());
normalGenerator->ComputePointNormalsOff();
normalGenerator->ComputeCellNormalsOn();
normalGenerator->FlipNormalsOn();
normalGenerator->Update();
(normalGenerator's output has 0 cells and points)
Is there a way to compute constrained delaunay triangulation for a list of 2d points and ensure all the faces point the same way ? (If so, how ? Would it be possible to do this with the vtkDelaunay2D class alone or is it necessary to use other filters?)
Any hints/tips are more than welcome :)
I'm using VTK 8.2 by the way.

the flipping in y effectively reverses the faces orientation (what is clockwise becomes anti-clockwise, like in a mirror).
I'm not sure I can reproduce your example above. A quick test in python seems to give the expected behavior, maybe you can start from this and map it to your c++ version:
import vedo
pts = [
[166, 127],
[103, 220],
[166, 190],
[174, 291],
[189, 226],
[227, 282],
[213, 187],
[242, 105],
[196, 131],
[182, 83],
]
ids = [[2,4,6], [0,2,8]] # faces to erase by pt-index (clockwise)
dly = vedo.delaunay2D(pts, mode='xy', boundaries=ids)
dly.c('grey5').lc('red4').lw(2)
labels = vedo.Points(pts).labels('id').z(1)
vedo.show(labels, dly, axes=1)

Related

Point cloud visualizer does not work properly

I run the point cloud visualizer simpleVis from the code given in the official docu (see
here.)
Below you find a shortened version of the code.
#include <iostream>
#include <thread>
#include <pcl/common/common_headers.h>
#include <pcl/features/normal_3d.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/parse.h>
using namespace std::chrono_literals;
pcl::visualization::PCLVisualizer::Ptr simpleVis (pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0);
viewer->addPointCloud<pcl::PointXYZ> (cloud, "sample cloud");
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
viewer->addCoordinateSystem (1.0);
viewer->initCameraParameters ();
return (viewer);
}
// --------------
// -----Main-----
// --------------
int
main (int argc, char** argv)
{
// ------------------------------------
// -----Create example point cloud-----
// ------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr basic_cloud_ptr (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud_ptr (new pcl::PointCloud<pcl::PointXYZRGB>);
std::cout << "Generating example point clouds.\n\n";
// We're going to make an ellipse extruded along the z-axis. The colour for
// the XYZRGB cloud will gradually go from red to green to blue.
std::uint8_t r(255), g(15), b(15);
for (float z(-1.0); z <= 1.0; z += 0.05)
{
for (float angle(0.0); angle <= 360.0; angle += 5.0)
{
pcl::PointXYZ basic_point;
basic_point.x = 0.5 * std::cos (pcl::deg2rad(angle));
basic_point.y = sinf (pcl::deg2rad(angle));
basic_point.z = z;
basic_cloud_ptr->points.push_back(basic_point);
pcl::PointXYZRGB point;
point.x = basic_point.x;
point.y = basic_point.y;
point.z = basic_point.z;
std::uint32_t rgb = (static_cast<std::uint32_t>(r) << 16 |
static_cast<std::uint32_t>(g) << 8 | static_cast<std::uint32_t>(b));
point.rgb = *reinterpret_cast<float*>(&rgb);
point_cloud_ptr->points.push_back (point);
}
if (z < 0.0)
{
r -= 12;
g += 12;
}
else
{
g -= 12;
b += 12;
}
}
basic_cloud_ptr->width = basic_cloud_ptr->size ();
basic_cloud_ptr->height = 1;
point_cloud_ptr->width = point_cloud_ptr->size ();
point_cloud_ptr->height = 1;
pcl::visualization::PCLVisualizer::Ptr viewer;
viewer = simpleVis(basic_cloud_ptr);
while (!viewer->wasStopped ())
{
viewer->resetCamera();
viewer->spinOnce (100);
std::this_thread::sleep_for(100ms);
}
}
Consequently, a window pops up which should visualize the point cloud.
However, I only see green, red and black areas as in the image below.
Then I press r and the following appears:
As far as I understand it, the appearing object is the point cloud.
Now, if I try to move it around. The point cloud "smears" as shown the image below.
I figured the following out:
If I run the simpler cloud viewer as described
here.
The visualisation works properly.
In the last few months, I tried to run a Python library for point cloud visualization that also uses
PCL under the hood. The problem also appears there.
Do you have any idea what the problem is?
This is a deep zoom-in of the meeting of the axes at (0,0,0).
To see more of the point cloud, you need to zoom-out (roll the mouse wheel).
Zoom-out may be limited depending on the definition of focus and the point of view.
Pressing the 'f' key when the mouse cursor hovers over the center will usually take you far enough away to zoom out.
Once you have a reasonable view, you can press 'c' to get (in the stdout) the camera parameters and set them in the code.

drawing bezier paths with cairo

I need to draw a collection of Bezier paths with the help of Cairo in C++.
So far I can draw a single path.
But I need to draw many paths consisting of multiple bezier curves and lines.
I load collection of paths from the external dictionaty generated by python.
Here, for this minimal example I introduce a path by the variable path0.
It is a vector, consisting of 2 subvectors. The first one represents a bezier spline, while the second one is a simple straight line.
The first subvector naturally has 4 components, while the second one has just 2.
Here is the example of my code.
#include <iostream>
#include <vector>
#include "cairo.h"
#include <string>
int main(int argc, char* argv[])
{
cairo_surface_t* surface;
cairo_t* cr;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1000, 1000);
cr = cairo_create(surface);
cairo_scale(cr, 1, 1);
cairo_set_line_width(cr, 1);
cairo_set_source_rgb(cr, 0, 0, 0);
// Here goes the svg-path. It is a bezier curve and unconnected line.
std::vector<std::vector<std::vector<double>>> path0 = { {{82.801,-204.48},{66.241,-204.48},{52.561,-214.56},{52.561,-235.44}},{{277.921,-192.24},{277.921,-173.5}} };
// Here I put the initial point in the middle of the canvas.
double x0 = 300+path0[0][0][0];
double y0 = 700+ path0[0][0][1];
cairo_move_to(cr, x0, y0);
// Parsing path
for (int i = 0; i < path0.size(); i++) {
if (path0[i].size() == 4) {
cairo_rel_curve_to(cr, path0[i][1][0], path0[i][1][1], path0[i][2][0], path0[i][2][1], path0[i][3][0], path0[i][3][1]);
cairo_stroke(cr);
}
if (path0[i].size() == 2) {
cairo_rel_line_to(cr, symbol[0][i][1][0], symbol[0][i][1][1]);
cairo_stroke(cr);
}
}
cairo_surface_write_to_png(surface, "stroke.png");
cairo_destroy(cr);
cairo_surface_destroy(surface);
return 0;
}
The point is it I see only one curve on the canvas. Probably, the second one is outside.
Could you help me to understand what I'm doing wrong?
Yaroslav.
Could you clarify what you are expecting to see? With the following change, I get something like two lines, but I am not sure it is what you want to see.
--- t.cpp.orig 2020-10-13 17:00:12.404871474 +0200
+++ t.cpp 2020-10-13 17:04:20.573101739 +0200
## -29,13 +29,12 ## int main(int argc, char* argv[])
for (int i = 0; i < path0.size(); i++) {
if (path0[i].size() == 4) {
cairo_rel_curve_to(cr, path0[i][1][0], path0[i][1][1], path0[i][2][0], path0[i][2][1], path0[i][3][0], path0[i][3][1]);
- cairo_stroke(cr);
}
if (path0[i].size() == 2) {
- cairo_rel_line_to(cr, symbol[0][i][1][0], symbol[0][i][1][1]);
- cairo_stroke(cr);
+ cairo_rel_line_to(cr, path0[i][1][0], path0[i][1][1]);
}
}
+ cairo_stroke(cr);
cairo_surface_write_to_png(surface, "stroke.png");
cairo_destroy(cr);
cairo_surface_destroy(surface);
Result is:
What happens in the above program is:
First you call cairo_move_to(cr, 382.8, 495.5). This decides where the following curve begins.
Then there is call to cairo_rel_curve_to(). This adds a curve from the current point to something like 382.8+52.5, 495.5-235.4. The other two points describe how the curve is bent.
Next there is a call to cairo_rel_line_to(cr, 277.9, -173.5);.
In my version, this adds a line segment starting from the end of the curve.
In your version, the call to cairo_stroke() deleted the path. Since there is no current point, the line_to does not do anything (well, it sets the current point for following drawing commands).

Shrink/Expand the outline of a polygon with holes

I want to expand/shrink a polygon with holes using boost::polygon. So to clarify that a bit, I have a single data structure
boost::polygon::polygon_with_holes_data<int> inPoly
where inPoly contains data that describe a rectangular outline and a triangle which forms the hole within this rectangle (in picture below this is the left, black drawing).
Now I want to
a) expand the whole stuff so that the rectangle becomes bigger and the hole becomes smaller (resulting in the red polygon in image below) or
b) shrink it so that the rectangle becomes smaller and the hole bigger (resulting in the green image below).
The corners don't necessarily need to be straight, the also can be rounded or somehow "rough".
My question: how can this be done using boost::polygon?
Thanks!
I answered this Expand polygons with boost::geometry?
And yes you can teach Boost Geometry to act on Boost Polygon types:
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
I came up with a test polygon like you described:
boost::polygon::polygon_with_holes_data<int> inPoly;
bg::read_wkt("POLYGON ((0 0,0 1000,1000 1000,1000 0,0 0),(100 100,900 100,500 700,100 100))", inPoly);
Now, apparently we can't just buffer on the adapted polygon, nor can we bg::assign or bg::convert directly. So, I came up with an ugly workaround of converting to WKT and back. And then you can do the buffer, and conver back similarly.
It's not very elegant, but it does work:
poly in;
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(inPoly)), in);
Full Demo
Include SVG output:
Live On Coliru
#include <boost/polygon/polygon.hpp>
#include <boost/polygon/polygon_set_data.hpp>
#include <boost/polygon/polygon_with_holes_data.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/algorithms/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
#include <fstream>
namespace bp = boost::polygon;
namespace bg = boost::geometry;
using P = bp::polygon_with_holes_data<int>;
using PS = bp::polygon_set_data<int>;
using coordinate_type = bg::coordinate_type<P>::type;
int main() {
P inPoly, grow, shrink;
bg::read_wkt("POLYGON ((0 0,0 1000,1000 1000,1000 0,0 0),(100 100,900 100,500 700,100 100))", inPoly);
{
// define our boost geometry types
namespace bs = bg::strategy::buffer;
namespace bgm = bg::model;
using pt = bgm::d2::point_xy<coordinate_type>;
using poly = bgm::polygon<pt>;
using mpoly = bgm::multi_polygon<poly>;
// define our buffering strategies
using dist = bs::distance_symmetric<coordinate_type>;
bs::side_straight side_strategy;
const int points_per_circle = 12;
bs::join_round join_strategy(points_per_circle);
bs::end_round end_strategy(points_per_circle);
bs::point_circle point_strategy(points_per_circle);
poly in;
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(inPoly)), in);
for (auto [offset, output_p] : { std::tuple(+15, &grow), std::tuple(-15, &shrink) }) {
mpoly out;
bg::buffer(in, out, dist(offset), side_strategy, join_strategy, end_strategy, point_strategy);
assert(out.size() == 1);
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(out.front())), *output_p);
}
}
{
std::ofstream svg("output.svg");
using pt = bg::model::d2::point_xy<coordinate_type>;
boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
mapper.add(inPoly);
mapper.add(grow);
mapper.add(shrink);
mapper.map(inPoly, "fill-opacity:0.3;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(grow, "fill-opacity:0.05;fill:rgb(255,0,0);stroke:rgb(255,0,0);stroke-width:2");
mapper.map(shrink, "fill-opacity:0.05;fill:rgb(0,0,255);stroke:rgb(0,0,255);stroke-width:2");
}
}
The output.svg written:
More or less accidentally I found boost::polygon also provides a single function for that which is quite easy to use: boost::polygon::polygon_set_data offers a function resize() which is doing exactly what is described above. Using the additional, parameters corner_fill_arc and num_segments rounded corners can be created.
No idea why this function is located in boost::polygon::polygon_set_data and not in boost::polygon::polygon_with_holes_data which in my opinion would be the more logically place for such a function...

Disable all light sources in Qt 3d

In my company there is a shift from an old 3D engine to Qt3d. One goal of this undertaking is to compare the rendered view of the old 3D engine with the Qt3d rendering.
For this I wrote a small example application, where I can compare old and new rendering. There are still plenty differences. My first idea was to switch of all light sources in both engines and compare the silhouettes of both renderings.
Now, there is some thing, that I really don't understand and this has to do with the Qt3d lighting model.
In my small example app I define a simple sphere mesh and a camera and a check box, which can disable a point light source. The sphere is lighted by the phong reflection model.
No, if I switch out mylight, I'm expecting a simple black sphere in my viewer, as there is really no light. Instead there is still some lighting (from a different source). I think, that there is some other light source, that is activated by default.
How, can I disable all light sources in Qt3d? As a side question I'm also wondering why meshMaterial->setAmbient(QColor(255, 0, 0)); has no visible effect on the material after all. It really doesn't matter what you will enter here.
#include <QApplication>
#include <QWidget>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QFrame>
#include <Qt3DCore/QTransform.h>
#include <Qt3DRender/QCamera.h>
#include <Qt3DRender/QRenderSettings.h>
#include <Qt3DRender/QPointLight.h>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/qforwardrenderer.h>
#include <Qt3DExtras/Qt3DWindow.h>
#include <Qt3DExtras/QFirstPersonCameraController.h>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(255,255,255));
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
auto cameraEntity = view->camera();
cameraEntity->lens()->setPerspectiveProjection(45.0f, 1., 0.1f, 10000.0f);
cameraEntity->setPosition(QVector3D(5, 5, 5));
cameraEntity->setUpVector(QVector3D(0, 1, 0));
cameraEntity->setViewCenter(QVector3D(0, 0, 0));
auto lightEntity = new Qt3DCore::QEntity(rootEntity);
auto light = new Qt3DRender::QPointLight(lightEntity);
light->setColor("white");
light->setIntensity(1);
lightEntity->addComponent(light);
auto lightTransform = new Qt3DCore::QTransform(lightEntity);
lightTransform->setTranslation(cameraEntity->position());
lightEntity->addComponent(lightTransform);
lightEntity->setEnabled(false);
// For camera controls
auto camController = new Qt3DExtras::QFirstPersonCameraController(rootEntity);
camController->setCamera(cameraEntity);
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(1.);
auto meshMaterial = new Qt3DExtras::QPhongMaterial();
meshMaterial->setDiffuse(QColor(0, 255, 0));
meshMaterial->setAmbient(QColor(255, 0, 0));
meshMaterial->setSpecular(QColor(0,0,255));
meshMaterial->setShininess(23);
auto meshEntity = new Qt3DCore::QEntity(rootEntity);
meshEntity->addComponent(mesh);
meshEntity->addComponent(meshMaterial);
meshEntity->setEnabled(true);
auto disableLight = new QCheckBox();
auto container = QWidget::createWindowContainer(view);
QFrame frame;
frame.setLayout(new QVBoxLayout);
frame.layout()->addWidget(container);
frame.layout()->addWidget(disableLight);
QObject::connect(disableLight, &QCheckBox::stateChanged, [lightEntity](auto state) {
lightEntity->setEnabled(state == Qt::CheckState::Checked);
});
frame.setFixedSize(500, 500);
frame.show();
return a.exec();
}
If no light entities are created in your Qt3D scene Qt3D will add one for you. This is to prevent users not seeing anything without light.
Once you add a light yourself the default one is omitted
You can workaround this default behaviour by adding a light with intensity set to zero:
DirectionalLight {
worldDirection: Qt.vector3d(-1, 1, -1)
intensity: 0.0
}
This will give you the following effect:
test with cuboid and sphere mesh with PhongMaterial:
So tinkering with the intensity property of the light might give what you want.

VTK texture scale

I'm working with eye images, and want I want to do is take one of these images and apply it as a texture for a sphere. However, I'm having a little bit of trouble with the scaling, here's how it looks as of now:
And here's the image I'm working with:
So far I've tried various methods but I still haven't got the desired result.
Here's the code I've been working on:
#include <vtkSmartPointer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkActor.h>
#include <vtkPolyDataMapper.h>
#include <vtkTransformTextureCoords.h>
#include <vtkTexture.h>
#include <vtkTextureMapToSphere.h>
#include <vtkImageReader2Factory.h>
#include <vtkImageReader.h>
#include <vtkTexturedSphereSource.h>
#include <vtkSphereSource.h>
int main(int argc, char *argv[])
{
std::string fileName = "C:\\VTK\\eyeTextureNew2.jpg";
//Create sphere
vtkSmartPointer<vtkTexturedSphereSource> source =
vtkSmartPointer<vtkTexturedSphereSource>::New();
source->SetPhiResolution(100);
source->SetThetaResolution(100);
// Read texture file
vtkSmartPointer<vtkImageReader2Factory> readerFactory =
vtkSmartPointer<vtkImageReader2Factory>::New();
vtkImageReader2 *imageReader = readerFactory->CreateImageReader2(fileName.c_str());
imageReader->SetFileName(fileName.c_str());
// Create texture
vtkSmartPointer<vtkTexture> texture =
vtkSmartPointer<vtkTexture>::New();
texture->SetInputConnection(imageReader->GetOutputPort());
vtkSmartPointer<vtkTransformTextureCoords> transformTexture =
vtkSmartPointer<vtkTransformTextureCoords>::New();
transformTexture->SetInputConnection(source->GetOutputPort());
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(transformTexture->GetOutputPort());
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
actor->SetTexture(texture);
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(actor);
renderer->SetBackground(.1, .2, .3);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renWinInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renWinInteractor->SetRenderWindow(renderWindow);
renderWindow->Render();
renWinInteractor->Start();
imageReader->Delete();
return EXIT_SUCCESS;
}
Any ideas on what could I be doing wrong/missing? Thanks in advance!
with vedo I get this (but symmetric on the backside):
from vedo import Sphere
s = Sphere().texture('QeXKq.jpg')
s.show()
the texture function actually does this:
def texture(self, tname):
"""Assign a texture to actor from image file or predefined texture tname."""
import os
tmapper = vtk.vtkTextureMapToPlane()
tmapper.AutomaticPlaneGenerationOn()
tmapper.SetInputData(self.polydata())
tmapper.Update()
tc = tmapper.GetOutput().GetPointData().GetTCoords()
self.polydata().GetPointData().SetTCoords(tc)
self.polydata().GetPointData().AddArray(tc)
fn = settings.textures_path + tname + ".jpg"
if os.path.exists(tname):
fn = tname
elif not os.path.exists(fn):
colors.printc("~sad Texture", tname, "not found in", settings.textures_path, c="r")
colors.printc("~pin Available textures:", c="m", end=" ")
for ff in os.listdir(settings.textures_path):
colors.printc(ff.split(".")[0], end=" ", c="m")
print()
return self
if ".png" in fn.lower():
reader = vtk.vtkPNGReader()
elif ".jp" in fn.lower():
reader = vtk.vtkJPEGReader()
else:
colors.printc("~times Supported texture files: PNG or JPG", c="r")
return self
reader.SetFileName(fn)
reader.Update()
img = reader.GetOutput()
atext = vtk.vtkTexture()
atext.SetInputData(img)
self.GetProperty().SetColor(1, 1, 1)
self.SetTexture(atext)
self.Modified()
return self