How to incrementially concatenate ranges? - c++

Background
What I am trying to do is to implement some classes that represents geometry. Any instance of a geometry class has a method called vertices() that returns a non-owning view of vertices. A geometry class can be expressed in terms of multiple other geometry classes, so the geometry class' vertices()-method would ideally just do something like this (pseudocode):
vertices()
{
return join(part1.vertices(), part2.vertices(), part3.vertices());
}
subject to not copying nor moving vertices.
In C++20 this is something that I believe can be done with ranges & views but I can't figure out how to do it.
My attempt
#include <iostream>
#include <ranges>
#include <vector>
struct Vertex { float x, y, z; };
struct GeometryA {
auto vertices() {
return std::ranges::ref_view(v);
}
std::vector<Vertex> v {{0.0f, 0.0f, 1.0f}};
};
struct GeometryB {
auto vertices() {
return std::ranges::ref_view(v);
}
std::vector<Vertex> v {{0.0f, 1.0f, 0.0f}};
};
struct GeometryC {
auto vertices() {
// OK: All elements of vector are of same type
return std::vector{ a.vertices(), b.vertices(), std::ranges::ref_view(v)} | std::views::join;
}
GeometryA a;
GeometryB b;
std::vector<Vertex> v {{0.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}};
};
struct GeometryD {
auto vertices() {
// Compilation fails: Elements of vector have different types
return std::vector{ c.vertices(), std::ranges::ref_view(v)} | std::views::join;
}
GeometryC c;
std::vector<Vertex> v {{1.0f, 0.0f, 1.0f}};
};
int main() {
GeometryD d;
for(Vertex const& vertex : d.vertices()) {
// Should print {0,0,1} {0,1,0} {0,1,1} {1,0,0} {1,0,1}
std::cout << "{" << vertex.x << "," << vertex.y << "," << vertex.z << "} ";
}
return 0;
}
Compilation fails in GeometryD::vertices since I am trying to deduce the template parameter T of the outmost vector from the elements at initialization (c.vertices() and std::ranges::ref_view(v)) but these do not have the same type hence T can't be deduced.
I am at a loss on how to approach this problem.
Question
Is it possible to use the standard ranges library to incrementally concatenate ranges?
I suppose I could gather all vertex-data directly or indirectly owned by a geometry class by using some recursive template-trickery and then just use std::views::join once, but before I get my hands dirty with that I'd like to get some input on my current attempt.

You can do this using Eric Niebler's Range-v3 library.
Just concat the different range views with ranges::views::concat. E.g., for GeometryC:
return ranges::views::concat(a.vertices(), b.vertices(), v);
[Demo]
Much of the Range-v3's stuff is being gradually adopted by the standard Ranges library, although it seems this feature hasn't made it yet.

Related

Extracting vectors from Structure in header file

This is the header file in question:
namespace osc {
using namespace gdt;
struct TriangleMesh {
std::vector<vec3f> vertex;
std::vector<vec3f> normal;
std::vector<vec2f> texcoord;
std::vector<vec3i> index;
// material data:
vec3f diffuse;
};
struct Model {
~Model()
{ for (auto mesh : meshes) delete mesh; }
std::vector<TriangleMesh *> meshes;
//! bounding box of all vertices in the model
box3f bounds;
};
Model *loadOBJ(const std::string &objFile);
}
I have successfully been able to use this header file to import an .obj of the 3D model in my main C++ code using the loadOBJ() function described in the header. I now want to operate on the vertices of that model. From the looks of it, the vertex points are in the structures defined in the header file. How do I extract these vertex vectors from the structure (and display them)?
loadOBJ() gives you a pointer to a Model object.
Model has a std::vector (a dynamic array) member named meshes that holds pointers to TriangleMesh objects.
TriangleMesh has a std::vector member named vertex holding vec3f objects.
You can iterate the various vectors like this:
osc::Model *model = osc::loadOBJ(...);
if (!model) ... // error handling
for (auto&& mesh : model->meshes)
{
cout << "vertex:\n";
for (auto&& vtx : mesh->vertex)
{
cout << "x=" << vtx.x << ", y=" << vtx.y << "z=" << vtx.z << "\n";
}
cout << "\n";
// repeat for mesh->normal, mesh->texcoord, mesh->index, etc...
}
delete model;
std::vector is a dynamic array in c++
if you want to enumerate
std::vector<vec3f> vertex;
you do
for(auto vert: vertex)
{
// here vert is of type vec3F
}
you can also access it like an array
vec3f v2 = vertex[2];
of course you must make sure vertex has at least 2 elements. You can find the length with vertex.size()

Data structure for interpolating T along axis of numerical keys

Is there a data structure that when initialized with a key1 at 10 and key2 at 20, when accessed at 15 will call an interpolation callback function with (key1, key2, 0.5) with return value T?
The callback ensures that we can use our preferred S-curve for the interpolation. I'm expecting accesses on each end to pass the same key in both with 1.0 as the factor.
Pseudo-code:
Container<float, MyStruct> container;
container.insert(10, MyStruct{ .x = 1.0f });
container.insert(20, MyStruct{ .x = 2.0f });
auto middlepoint = container.get(15,
[] (const MyStruct& a, const MyStruct& b, float factor) {
return MyStruct{ a.x * factor + b.x * (1.0 - factor) };
});
assert(middlepoint.x ~ 1.5f);
Have you seen this? What is this data structure called?
Thanks
Its not just a data structure you are looking for, but a way to retrieve information from it. You can use a map + find_if:
#include <map>
#include <algorithm>
#include <iostream>
template <typename C,typename I>
double get(const C& container,double x,I interp) {
auto it = std::find_if(container.begin(),container.end(),[x](auto e){ return e.first > x;});
return interp(std::prev(it)->second,it->second);
}
int main() {
std::map<double,double> m{ {1,0.0},{2,3.0},{3,5.0}};
std::cout << get(m,1.5,[](auto x1,auto x2){ return 0.5*(x1 + x2);});
}
Prints: 1.5
Maybe 2-3 more lines are needed to handle edge cases, but otherwise above is all you need. Sometimes a wheel is really just a wheel and can be crafted by building on existing containers and algorithms. I don't expect to find this anywhere as a container, but rather as a interpolation function along the line of interpolate(some_array_with_x_values,some_array_with_y_values,x).

error: vector iterators incompatible

This is the line which causes the error
vector<transform>::iterator transformIter;
for (transformIter = (*objIter)->transforms.begin(); transformIter != (*objIter)->transforms.end(); objIter++) {
handleTransform((*transformIter));
}
It occurs on the second iteration of the loop regardless of how many transofrms are in the vector.
The obj struct looks like this:
struct obj {
vector<glm::vec4> vertices;
vector<int> elements;
vector<object> objects;
vector<transform> transforms;
};
and the function handleTransform is:
void handleTransform(transform currentTransform) {
if (currentTransform.type == 'r') {
glRotatef(currentTransform.coordinates.x, 1.0f, 0.0f, 0.0f);
glRotatef(currentTransform.coordinates.y, 0.0f, 1.0f, 0.0f);
glRotatef(currentTransform.coordinates.z, 0.0f, 0.0f, 1.0f);
}
if (currentTransform.type == 's') {
glScalef(currentTransform.coordinates.x, currentTransform.coordinates.y, currentTransform.coordinates.z);
}
if (currentTransform.type == 't') {
glTranslatef(currentTransform.coordinates.x, currentTransform.coordinates.y, currentTransform.coordinates.z);
}
}
Iterating through the other vectors in an obj doesn't cause any vector issues, so I have to imagine it's something to do with the handleTransform function, but I can't figure it out.
Replace objIter++ by ++transformIter.
You are erroneously incrementing objIter, not transformIter:
for (transformIter = (*objIter)->transforms.begin();
transformIter != (*objIter)->transforms.end();
objIter++) // ??
It should be:
for (transformIter = (*objIter)->transforms.begin();
transformIter != (*objIter)->transforms.end();
++transformIter)
To avoid mistakes like this, use std::for_each, or if using C++ 11, a ranged-based for loop:
http://en.cppreference.com/w/cpp/language/range-for
With either of these options, you eliminate the need to declare iterators, make mistakes iterating to the next item, or if not those issues, perform a post-increment instead of a faster pre-increment of the iterator (as your original code is doing).

c++ change of coordinates (Pixels 1920 x 1080 to 640 x 480)

I am developing a small program that can draw a rectangle in different videos modes (for example 1920 x 1080 -> 640 x 480). I can resize a rectangle. But I am getting stuck, because I cannot find a clear method to achieve the problem. I am currently trying to create a matrix to perform a scale operation over the pixels but I cannot get the right answer. Part of the problem seems to be that TransformMatrix::transform is not scaling correctly.
#include <iostream>
typedef struct _Pixel
{
_Pixel():X(1920)
,Y(1080)
{}
unsigned X;
unsigned Y;
}Pixel;
typedef struct TransformMatrix
{
constexpr TransformMatrix(const float aWeigth = 0.3f
,const float aHeigth = 0.225f):W(aWeigth)
,H(aHeigth)
{}
void transform( const Pixel & aPixel)
{
auto x_value=static_cast<float>(aPixel.X)*W;
auto y_value=static_cast<float>(aPixel.Y)*H;
std::cout<<"x_value: "<<x_value<<std::endl;
std::cout<<"y_value: "<<y_value<<std::endl;
}
const float W;
const float H;
}TransformMatrix;
int main()
{
Pixel aPixel;
TransformMatrix _TransformMatrix;
_TransformMatrix.transform(aPixel);
return 0;
}
The actual result:
x_value: 576
y_value: 243
The expected result:
x_value: 640
y_value: 480
How can I perform the right operation? Is it just a change of basis?
Should I scale only or also do a transformation operation?
Wokay, since people urged me, let me explain the most important style improvements here:
Naming: don't start names with underscores: It's illegal and the program invokes undefined behaviour
struct tags: they're a thing of C, and obsolete in C++ ever since C++98
Single Responsibility Principle - don't make the transform function print things
Make the Pixel struct capable of printing itself (using operator<< for iostreams)
Pure Functions: make transform return a modified value, instead of mutating the argument. In general this makes code a lot safer, and it can enable a class of optimizations. In the rare case where you wanted to update the pixel in-place you'd simply write
pixel = transform(pixel); // the optimizer will see right through this
Make TransformMatrix a calleable object (by implementing transform as operator() instead. This way, you can simply use it as a function, e.g. in an algorithm:
std::vector<Pixel> poly1, poly2;
std::transform(poly1.begin(), poly1.end(),
back_inserter(poly2), TransformMatrix());
Which simply transforms all pixels in poly1 to poly2.
Exercise for the reader: name TransformMatrix so it does what it says. Right now, it's more like ScalePixel
Live On Coliru
#include <iostream>
struct Pixel {
Pixel(unsigned x = 1920, unsigned y = 1080) : X(x), Y(y) {}
unsigned X;
unsigned Y;
friend std::ostream& operator<<(std::ostream& os, Pixel const& p) {
return os << "Pixel(" << p.X << ", " << p.Y << ")";
}
};
struct TransformMatrix {
constexpr TransformMatrix(float aWidth = 640/1920.f, float aHeigth = 480/1080.f) : W(aWidth), H(aHeigth) {}
Pixel operator()(const Pixel &aPixel) const {
return { static_cast<unsigned>(aPixel.X * W), static_cast<unsigned>(aPixel.Y * H) };
}
float W;
float H;
};
int main() {
Pixel aPixel;
TransformMatrix xfrm;
std::cout << aPixel << " -> " << xfrm(aPixel) << "\n";
}
Prints:
Pixel(1920, 1080) -> Pixel(640, 480)

C++ Vectors and OpenGL

I have a strange error with the OpenGL code I am writing. As a test, I'm creating a vector of spheres and using push_back(s1). I'm adding multiple spheres to the vector. However, when I run the program it only draws the sphere that was most recently pushed into the vector.
#include "Sphere.h";
#include <iostream>;
#include <vector>;
using namespae std;
vector<Sphere> spheres;
Sphere s1 = Sphere(1.0, "One");
Sphere s2 = Sphere(2.0, "Two");
Sphere s3 = Sphere(3.0, "Three");
void init(void) {
spheres.push_back(s1);
spheres.push_back(s2);
spheres.push_back(s3);
for each(Sphere s in spheres) {
cout << s.getName() << "\n";
}
}
// OTHER CODE OMMITED
void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 0.0);
glPushMatrix();
for each(Sphere in s) {
s.draw();
}
glPopMatrix();
}
Obviously there is a main method in there where all the GL stuff is setup and I know that there is no issue there.
So the sphere has its own draw method. Now the interesting part is that in the console it outputs:
Three
Three
Three
and proceeds to draw s3, three times to the screen.
So my question is: why is it only drawing the last item in the vector three times? I have also tried using an iterator and a normal for loop but they all produce the same result.
Anyone have an idea?
EDITS
getName() function:
string Sphere::getName() {
return name;
}
iterator for vector:
vector<Sphere>::iterator it;
void display() {
for(it = planets.begin(); it != planets.end(); ++it) {
it->draw();
}
}
draw code in Sphere:
GLdouble r = 0.0;
GLfloat X = 0.0f;
string name = " ";
Sphere::Sphere(GLdouble ra, GLfloat x, string n)
{
r = ra;
X = pos;
name = n;
}
Sphere::~Sphere(void)
{
}
void Sphere::draw(void)
{
glutSolidSphere(r, 10, 8);
glTranslatef(X, 0.0, 0.0);
}
string Sphere::getName(void)
{
return name;
}
The problem appears to be that you have defined 3 global variables in Sphere.cpp, instead of class member variables. So every time the constructor runs, it overwrites the previous values, and you only see the last object constructed.
The solution is to declare them as members.
In Sphere.h, inside the class definition for Sphere, put
class Sphere {
// constructors, your current functions, and so on...
private:
GLdouble r;
GLfloat X;
string name;
}
Finally, questions like this are an example of why it's important that you provide a small example that demonstrates the problem. The first reason is it makes it easier for us to determine the source of the problem. The second is that is that it makes you examine your code in small parts. Once you've isolated the problem, it is more likely you'll be able to recognize the problem on your own.