Radial Tree layout algorithm - c++

I have implemented a tree data structure in which every node holds (recursivly) a list of pointers to it's children.
I am trying to calculate the (x,y) coordinates for visualizing the tree.
I went through this article:
http://gbook.org/projects/RadialTreeGraph.pdf
Cut I can't figure out how to gest past the first level, i.e This is what I have written so far:
for (int i = 0; i < GetDepth()+1; i++)
{
if (i == 0)
{
GetNodesInDepth(i).at(0)->SetXRadial(MIDDLE(m_nWidth));
GetNodesInDepth(i).at(0)->SetYRadial(MIDDLE(m_nHeight));
continue;
}
double dNodesInDepth = GetNodesInDepth(i).size();
double dAngleSpace = 2 * PI / dNodesInDepth;
for (int j = 0; j < dNodesInDepth; j++)
{
Node * pCurrentNode = GetNodesInDepth(i).at(j);
pCurrentNode->SetXRadial((SPACING * i) * qCos(j * dAngleSpace) + MIDDLE(m_nWidth));
pCurrentNode->SetYRadial((SPACING * i) * qSin(j * dAngleSpace) + MIDDLE(m_nHeight));
pCurrentNode->m_dAngle = dAngleSpace * j;
if (pCurrentNode->IsParent())
{
//..(I'm stuck here)..//
}
}
}
I am not sure how to calculate the limits, bisectors etc.
this is what my drawer did so far:
which is obviously not what i'm looking for since the second (0 based) level.
I have access to every info that I need in order to obtain what I'm looking for.

Probably by now you figured it out yourself. If not, here
double dNodesInDepth = GetNodesInDepth(i).size();
double dAngleSpace = 2 * PI / dNodesInDepth;
you're setting the angle space to PI (180 degreees) at your second level, as there are only two nodes at that level, dNodesInDepth = 2. That's why after drawing the node 20, the node 30 is 180 degrees away. That method would be fine for very dense trees because that angle will be small. But in your case you want to keep the angle as close as possible to the angle of the parent. So I suggest you get the angle of the parent for nodes at level 2 and higher, and spread the children so they have an angle space of sibilingAngle = min(dAngleSpace, PI/10). So the first child will have the exact angle of the parent, and the remaining children are sibilingAngle away from one another. You get the idea and probably come with a better method. I'm using min in case you have got too many nodes at that level you want to squeeze the nodes closer to each other.
The article you've linked to, uses a solution that is illustrated in Figure 2 – Tangent and bisector limits for directories. I don't like that method much because if you determine the absolute angle of the children rather than relative to the parent you can have a simpler/cleaner solution that does exactly what that method tries to do with so many operations.
Update:
The following code produces this image:
I think you can easily figure out how to center the child nodes and etc.
#include <cairo/cairo.h>
#include <math.h>
#include <vector>
using namespace std;
class Node {
public:
Node() {
parent = 0;
angle = 0;
angleRange = 2*M_PI;
depth = 0;
}
void addChildren(int n) {
for (int i=0; i<n; i++) {
Node* c = new Node;
c->parent = this;
c->depth = depth+1;
children.push_back(c);
}
}
vector<Node*> children;
float angle;
float angleRange;
Node* parent;
int depth;
int x, y;
};
void rotate(float x, float y, float angle, float& nx, float& ny) {
nx = x * cos(angle) - y * sin(angle);
ny = x * sin(angle) + y * cos(angle);
}
void draw(Node* root, cairo_t *cr) {
if (root->parent == 0) {
root->x = root->y = 300;
cairo_arc(cr, root->x, root->y, 3, 0, 2 * M_PI);
}
int n = root->children.size();
for (int i=0; i<root->children.size(); i++) {
root->children[i]->angle = root->angle + root->angleRange/n * i;
root->children[i]->angleRange = root->angleRange/n;
float x, y;
rotate(40 * root->children[i]->depth, 0, root->children[i]->angle, x, y);
root->children[i]->x = 300+x;
root->children[i]->y = 300+y;
cairo_move_to(cr, root->x, root->y);
cairo_line_to(cr, root->children[i]->x, root->children[i]->y);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_stroke(cr);
cairo_arc(cr, 300+x, 300+y, 3, 0, 2 * M_PI);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_stroke_preserve(cr);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_fill(cr);
draw(root->children[i], cr);
}
}
int main(void) {
Node root;
root.addChildren(4);
root.children[0]->addChildren(3);
root.children[0]->children[0]->addChildren(2);
root.children[1]->addChildren(5);
root.children[2]->addChildren(5);
root.children[2]->children[1]->addChildren(2);
root.children[2]->children[1]->children[1]->addChildren(2);
cairo_surface_t *surface;
cairo_t *cr;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 600, 600);
cr = cairo_create(surface);
cairo_rectangle(cr, 0.0, 0.0, 600, 600);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_fill(cr);
cairo_set_line_width(cr, 2);
for (int i=0; i<6; i++) {
cairo_arc(cr, 300, 300, 40*i, 0, 2 * M_PI);
cairo_set_source_rgb(cr, .5, .5, .5);
cairo_stroke(cr);
}
draw(&root, cr);
cairo_surface_write_to_png(surface, "image.png");
cairo_destroy(cr);
cairo_surface_destroy(surface);
return 0;
}
Update 2:
Just to make it easier for you, here is how to center the nodes:
for (int i=0; i<root->children.size(); i++) {
float centerAdjust = 0;
if (root->parent != 0) {
centerAdjust = (-root->angleRange + root->angleRange / n) / 2;
}
root->children[i]->angle = root->angle + root->angleRange/n * i + centerAdjust;
root->children[i]->angleRange = root->angleRange/n;
Showing a more populated tree:

Here is an implementation of the algorithm from the article that should work (note: I didn't compile it since I don't have other parts of your program):
void Tree::CalculateAngles()
{
// IsEmpty() returns true if the tree is empty, false otherwise
if (!IsEmpty())
{
Node* pRoot = GetNodesInDepth(0).at(0);
pRoot->SetAngle(0);
// Relative to the current angle
pRoot->SetTangentLimit(PI);
// Absolute limit
pRoot->SetLowerBisector(-PI);
pRoot->SetHigherBisector(PI);
}
for (int depth = 1; depth < GetDepth() + 1; depth++)
{
double dDepth = (double)depth;
// The last non-leaf node in of the current depth (i.e. node with children)
Node* pPreviousNonleafNode = NULL;
// The first non-leaf node
Node* pFirstNonleafNode = NULL;
// The parent of the previous node
Node* pPreviousParent = NULL;
int indexInCurrentParent = 0;
double dTangentLimt = acos( dDepth / (dDepth + 1.0) );
for (int i = 0; i < GetNodesInDepth(depth).size(); i++)
{
Node* pCurrentNode = GetNodesInDepth(depth).at(i);
Node* pParent = pCurrentNode->GetParent();
if (pParent != pPreviousParent)
{
pPreviousParent = pParent;
indexInCurrentParent = 0;
}
// (GetMaxChildAngle() - GetMinChildAngle()) / GetChildCount()
double angleSpace = pParent->GetAngleSpace();
pCurrentNode->SetAngle(angleSpace * (indexInCurrentParent + 0.5));
pCurrentNode->SetTangentLimit(dTangentLimt);
if (pCurrentNode->IsParent())
{
if (!pPreviousNonleafNode)
{
pFirstNonleafNode = pCurrentNode;
}
else
{
double dBisector = (pPreviousNonleafNode->GetAngle() + pCurrentNode->GetAngle()) / 2.0;
pPreviousNonleafNode->SetHigherBisector(dBisector);
pCurrentNode->SetLowerBisector(dBisector);
}
pPreviousNonleafNode = pCurrentNode;
}
indexInCurrentParent++;
}
if (pPreviousNonleafNode && pFirstNonleafNode)
{
if (pPreviousNonleafNode == pFirstNonleafNode)
{
double dAngle = pFirstNonleafNode->GetAngle();
pFirstNonleafNode->SetLowerBisector(dAngle - PI);
pFirstNonleafNode->SetHigherBisector(dAngle + PI);
}
else
{
double dBisector = PI + (pPreviousNonleafNode->GetAngle() + pFirstNonleafNode->GetAngle()) / 2.0;
pFirstNonleafNode->SetLowerBisector(dBisector);
pPreviousNonleafNode->SetHigherBisector(dBisector);
}
}
}
}
void Tree::CalculatePositions()
{
for (int depth = 0; depth < GetDepth() + 1; depth++)
{
double redius = SPACING * depth;
for (int i = 0; i < GetNodesInDepth(depth).size(); i++)
{
Node* pCurrentNode = GetNodesInDepth(depth).at(i);
double angle = pCurrentNode->GetAngle();
pCurrentNode->SetXRadial(redius * qCos(angle) + MIDDLE(m_nWidth));
pCurrentNode->SetYRadial(redius * qSin(angle) + MIDDLE(m_nHeight));
}
}
}
void Tree::CalculateLayout ()
{
CalculateAngles();
CalculatePositions();
}
double Node::GetAngleSpace()
{
return (GetMaxChildAngle() - GetMinChildAngle()) / GetChildCount();
}
Note: I tried to mimic your code style so you won't have to refactor it to match other parts of your program.
P.S. If you spot any bugs, please notify me in the comments - I'll edit my answer.

Related

Values being redrawn to screen when using depth buffer

I have implemented a depth buffer using a std::vector with size 640 * 480. I can write and read from the buffer fine, but I have noticed the buffer appears to be copied along the left and right edges. The buffer is written row by row, going left to right and then going down one row.
I am quite certain the issue is related to the depth buffer, as disabling read from the buffer fixes the artifacts and shows the buffer is still being written properly.
I am using SDL as the graphics library, but not OpenGL.
This buffer should only show one trapezium down the middle. The extra bits on the left and right should not appear.
What is happening to cause these artifacts? Alternatively, could I know some methods to debug this better.
Minimum code to replicate (as far as I can tell):
#include "SDL.h"
#include <vector>
#include <algorithm>
#include <iostream>
struct vec3d {
float x = 0;
float y = 0;
float z = 0;
};
struct tri3d {
vec3d p1;
vec3d p2;
vec3d p3;
};
struct vector2d {
float x;
float y;
};
float vect_dot_vect(vector2d a, vector2d b) {
return(a.x * b.x + a.y * b.y);
}
int draw_tri(SDL_Renderer* renderer, std::vector<float>& buffer_out, tri3d triangle, int half_x_width, int half_y_width, int depth_test) { // depthmap is a linear array. Buffer out is pointing to the first value
tri3d scaled_tri = triangle;
// Find bounding box of tri
int x = (int)std::min(std::min(floor(scaled_tri.p1.x), floor(scaled_tri.p2.x)), floor(scaled_tri.p3.x));
int y = (int)std::min(std::min(floor(scaled_tri.p1.y), floor(scaled_tri.p2.y)), floor(scaled_tri.p3.y));
int wx = (int)std::max(std::max(ceil(scaled_tri.p1.x), ceil(scaled_tri.p2.x)), ceil(scaled_tri.p3.x)) - x;
int wy = (int)std::max(std::max(ceil(scaled_tri.p1.y), ceil(scaled_tri.p2.y)), ceil(scaled_tri.p3.y)) - y;
// Find edge vectors
vector2d ac;
ac.x = scaled_tri.p3.x - scaled_tri.p1.x;
ac.y = scaled_tri.p3.y - scaled_tri.p1.y;
vector2d ab;
ab.x = scaled_tri.p2.x - scaled_tri.p1.x;
ab.y = scaled_tri.p2.y - scaled_tri.p1.y;
float cc = vect_dot_vect(ac, ac);
float cb = vect_dot_vect(ac, ab);
float cp;
float bb = vect_dot_vect(ab, ab);
float bp;
float invDenom = 1 / (cc * bb - pow(cb, 2));
float u;
float v;
float w;
float x_dif = x - scaled_tri.p1.x;
float y_dif = y - scaled_tri.p1.y;
int full_y_width = half_y_width * 2;
float twoarea = (ab.x * ac.y - ab.y * ac.x);
float barycentric_depth_weights[3] = { scaled_tri.p1.z, scaled_tri.p2.z, scaled_tri.p3.z };
float depth_map_value;
for (size_t i = wy; i != 0; i--) {
for (size_t q = wx; q != 0; q--) {
vector2d ap;
ap.x = q + x_dif;
ap.y = i + y_dif;
cp = vect_dot_vect(ac, ap);
bp = vect_dot_vect(ab, ap);
// Find barycentric coords
u = (bb * cp - cb * bp) * invDenom;
v = (cc * bp - cb * cp) * invDenom;
w = abs(1 - u - v);
depth_map_value = (w * barycentric_depth_weights[0] + v * barycentric_depth_weights[1] + u * barycentric_depth_weights[2]);
// Test if in tri
if (u >= 0 && v >= 0 && u + v < 1) {
// Test depth buffer
if (buffer_out[(y + i) * full_y_width + x + q] < (0.0625 + depth_map_value)) {
buffer_out[(y + i) * full_y_width + x + q] = depth_map_value;
}
}
}
}
return 0;
}
SDL_Window* win_make_window(int display_width, int display_height, SDL_WindowFlags flags) {
// Returns an SDL window given screen size and flags
SDL_Window* window = NULL;
window = SDL_CreateWindow("Minimum code", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, display_width, display_height, flags);
if (window == NULL) {
printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
}
return window;
}
int draw_buffer(SDL_Renderer* renderer, std::vector<float>& buffer, int half_screen_x, int half_screen_y) {
// Iterate over every pixel and draw
int depth_map_value;
int screen_y = 2 * half_screen_y;
for (size_t i = 0; i < screen_y; i++) {
for (size_t q = 0; q < half_screen_x * 2; q++) {
depth_map_value = buffer.at(screen_y * i + q) * 100;
SDL_SetRenderDrawColor(renderer, depth_map_value, depth_map_value, depth_map_value, 255);
SDL_RenderDrawPoint(renderer, (int)q, (int)i);
}
}
return 0;
}
int main(int argc, char* argv[]) {
const int half_screen_size[2] = { 320, 240 }; // Half size of screen. Needed it elsewhere
const SDL_WindowFlags flags = SDL_WINDOW_SHOWN;
// SDL startup boilerplate
SDL_Window* window = NULL;
SDL_Surface* screenSurface = NULL;
SDL_Renderer* renderer = NULL;
// The tris, already projected
tri3d tri1;
tri1.p1 = { 577.173828, 453.201538, 1.37657264 };
tri1.p2 = { 108.381744, 399.609772, 1.03054810 };
tri1.p3 = { 547.989380,70.1635742,1.20407486 };
tri3d tri2;
tri2.p1 = { 108.381744, 399.609772, 1.03054810 };
tri2.p2 = { 131.230850, 108.719635, 0.930727124 };
tri2.p3 = { 547.989380, 70.1635742, 1.20407486 };
//Create depth buffer
std::vector<float> depth_buffer = {0};
depth_buffer.resize(4 * static_cast<__int64>(half_screen_size[0]) * static_cast<__int64>(half_screen_size[1]));
// Catch startup errors
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); // Catch startup errors
else {
SDL_Event event_handle;
window = win_make_window(half_screen_size[0] * 2, half_screen_size[1] * 2, flags);
screenSurface = SDL_GetWindowSurface(window);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// Draw tris to screen. No pixels actually drawn for debug purposes, only modifies depth buffer
draw_tri(renderer, depth_buffer, tri1, half_screen_size[0], half_screen_size[1], 1);
draw_tri(renderer, depth_buffer, tri2, half_screen_size[0], half_screen_size[1], 1);
// Draw the buffer to screen
draw_buffer(renderer, depth_buffer, half_screen_size[0], half_screen_size[1]);
SDL_RenderPresent(renderer);
}
// Close everything else
std::cin.get();
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}
This is a school project and as such, I am not allowed to use SDL provided features except to draw to screen and for handling windows.
I modified the code to draw the depth buffer as it was being calculated, and noticed that when drawing from left to right, columnwise, the leftmost artifact no longer renderers. By changing the range of the region rendered, it appears that writing to one spot on the depth buffer also writes to another. No idea what to make of this yet.
No idea what's the deal with "half" sizes as you seem to use full size everywhere, but your array indexing is wrong. When iterating rectangle with [width, height], correct indexing code is e.g.:
for(int y = 0; y != height; ++y) {
for(int x = 0; x != width; ++x) {
int pixel = pixel_array[y*width+x]; // not y*height!
}
}
Correct that in both places you index your depth array:
in draw_tri, buffer_out[(y + i) * full_y_width + x + q] - should be full_x_width, which you don't have yet,
in draw_buffer, depth_map_value = buffer.at(screen_y * i + q) * 100; - should be screen_x.

How to get the current position of a shape in qt

I have a circle with these two methods in qt:
void MyGLWidget::drawCircle() {
int n = vertices.count();
qglColor(Qt::white);
glBegin(GL_POLYGON);
for(int i = 0; i < n; i++) {
glVertex2f(vertices[i].first, vertices[i].second);
}
glEnd();
}
void MyGLWidget::calculateVetices()
{
double a = 3.14 / 90;
vertices.append(QPair<double, double> (0,0));
for(int i = 0; i <= 360; i++) {
vertices.append(QPair<double, double> (cos(i * a) * radius, sin(i * a) * radius));
}
}
Now I want to get the current position of this circle. How can I do that?

OpenGL Cone Function Not Drawing Correctly

I found an example online that shows how to draw a cone in OpenGL, which is located here: It was written in C++, and so I translated it to C#. Here is the new code:
public void RenderCone(Vector3 d, Vector3 a, float h, float rd, int n)
{
Vector3 c = new Vector3(a + (-d * h));
Vector3 e0 = Perp(d);
Vector3 e1 = Vector3.Cross(e0, d);
float angInc = (float)(360.0 / n * GrimoireMath.Pi / 180);
// calculate points around directrix
List<Vector3> pts = new List<Vector3>();
for (int i = 0; i < n; ++i)
{
float rad = angInc * i;
Vector3 p = c + (((e0 * (float)Math.Cos((rad)) + (e1 * (float)Math.Sin(rad))) * rd));
pts.Add(p);
}
// draw cone top
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(a);
for (int i = 0; i < n; ++i)
{
GL.Vertex3(pts[i]);
}
GL.End();
// draw cone bottom
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = n - 1; i >= 0; --i)
{
GL.Vertex3(pts[i]);
}
GL.End();
}
public Vector3 Perp(Vector3 v)
{
float min = Math.Abs(v.X);
Vector3 cardinalAxis = new Vector3(1, 0, 0);
if (Math.Abs(v.Y) < min)
{
min = Math.Abs(v.Y);
cardinalAxis = new Vector3(0, 1, 0);
}
if (Math.Abs(v.Z) < min)
{
cardinalAxis = new Vector3(0, 0, 1);
}
return Vector3.Cross(v, cardinalAxis);
}
I think I am using the parameters correctly(the page isnt exactly coherent in terms of actual function-usage). Here is the legend that the original creator supplied:
But when I enter in the following as parameters:
RenderCone(new Vector3(0.0f, 1.0f, 0.0f), new Vector3(1.0f, 1.0f, 1.0f), 20.0f, 10.0f, 8);
I receive this(Wireframe enabled):
As you can see, I'm missing a slice, either at the very beginning, or the very end. Does anyone know what's wrong with this method? Or what I could be doing wrong that would cause an incomplete cone?
// draw cone bottom
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = n - 1; i >= 0; --i)
{
GL.Vertex3(pts[i]);
}
GL.End();
That connects all vertices to each other and center but there is one connection missing. There is nothing the specifies connection from first to last vertex. Adding GL.Vertex3(pts[n-1]); after loop would add the missing connection.
The Solution was actually extremely simple, I needed to increase the number of slices by 1. Pretty special if you ask me.
public void RenderCone(Vector3 baseToApexLength, Vector3 apexLocation, float height, float radius, int slices)
{
Vector3 c = new Vector3(apexLocation + (-baseToApexLength * height));
Vector3 e0 = Perpendicular(baseToApexLength);
Vector3 e1 = Vector3.Cross(e0, baseToApexLength);
float angInc = (float)(360.0 / slices * GrimoireMath.Pi / 180);
slices++; // this was the fix for my problem.
/**
* Compute the Vertices around the Directrix
*/
Vector3[] vertices = new Vector3[slices];
for (int i = 0; i < vertices.Length; ++i)
{
float rad = angInc * i;
Vector3 p = c + (((e0 * (float)Math.Cos((rad)) + (e1 * (float)Math.Sin(rad))) * radius));
vertices[i] = p;
}
/**
* Draw the Top of the Cone.
*/
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(apexLocation);
for (int i = 0; i < slices; ++i)
{
GL.Vertex3(vertices[i]);
}
GL.End();
/**
* Draw the Base of the Cone.
*/
GL.Begin(PrimitiveType.TriangleFan);
GL.Vertex3(c);
for (int i = slices - 1; i >= 0; --i)
{
GL.Vertex3(vertices[i]);
}
GL.End();
}

ld: library not found for -lglfw

I well compiled/build the glfw library and after removed/replaced some functions in a file main.cc ( glfw 2 to version 3 ), it tells me this error
ld: library not found for -lglfw
clang: error: linker command failed with exit code 1 (use -v to see invocation)
This is the file :
/*
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
* http://code.google.com/p/poly2tri/
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Poly2Tri nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstdlib>
#include </usr/local/include/GLFW/glfw3.h>
#include <time.h>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;
#include "../poly2tri/poly2tri.h"
using namespace p2t;
void Init();
void ShutDown(int return_code);
void MainLoop(const double zoom);
void Draw(const double zoom);
void DrawMap(const double zoom);
void ConstrainedColor(bool constrain);
double StringToDouble(const std::string& s);
double Random(double (*fun)(double), double xmin, double xmax);
double Fun(double x);
/// Dude hole examples
vector<Point*> CreateHeadHole();
vector<Point*> CreateChestHole();
float rotate_y = 0,
rotate_z = 0;
const float rotations_per_tick = .2;
/// Screen center x
double cx = 0.0;
/// Screen center y
double cy = 0.0;
/// Constrained triangles
vector<Triangle*> triangles;
/// Triangle map
list<Triangle*> map;
/// Polylines
vector< vector<Point*> > polylines;
/// Draw the entire triangle map?
bool draw_map = false;
/// Create a random distribution of points?
bool random_distribution = false;
template <class C> void FreeClear( C & cntr ) {
for ( typename C::iterator it = cntr.begin();
it != cntr.end(); ++it ) {
delete * it;
}
cntr.clear();
}
int main(int argc, char* argv[])
{
int num_points = 0;
double max, min;
double zoom;
if (argc != 5) {
cout << "-== USAGE ==-" << endl;
cout << "Load Data File: p2t filename center_x center_y zoom" << endl;
cout << "Example: ./build/p2t testbed/data/dude.dat 500 500 1" << endl;
return 1;
}
if(string(argv[1]) == "random") {
num_points = atoi(argv[2]);
random_distribution = true;
char* pEnd;
max = strtod(argv[3], &pEnd);
min = -max;
cx = cy = 0;
zoom = atof(argv[4]);
} else {
zoom = atof(argv[4]);
cx = atof(argv[2]);
cy = atof(argv[3]);
}
vector<p2t::Point*> polyline;
if(random_distribution) {
// Create a simple bounding box
polyline.push_back(new Point(min,min));
polyline.push_back(new Point(min,max));
polyline.push_back(new Point(max,max));
polyline.push_back(new Point(max,min));
} else {
// Load pointset from file
// Parse and tokenize data file
string line;
ifstream myfile(argv[1]);
if (myfile.is_open()) {
while (!myfile.eof()) {
getline(myfile, line);
if (line.size() == 0) {
break;
}
istringstream iss(line);
vector<string> tokens;
copy(istream_iterator<string>(iss), istream_iterator<string>(),
back_inserter<vector<string> >(tokens));
double x = StringToDouble(tokens[0]);
double y = StringToDouble(tokens[1]);
polyline.push_back(new Point(x, y));
num_points++;
}
myfile.close();
} else {
cout << "File not opened" << endl;
}
}
cout << "Number of constrained edges = " << polyline.size() << endl;
polylines.push_back(polyline);
Init();
/*
* Perform triangulation!
*/
double init_time = glfwGetTime();
/*
* STEP 1: Create CDT and add primary polyline
* NOTE: polyline must be a simple polygon. The polyline's points
* constitute constrained edges. No repeat points!!!
*/
CDT* cdt = new CDT(polyline);
/*
* STEP 2: Add holes or Steiner points if necessary
*/
string s(argv[1]);
if(s.find("dude.dat", 0) != string::npos) {
// Add head hole
vector<Point*> head_hole = CreateHeadHole();
num_points += head_hole.size();
cdt->AddHole(head_hole);
// Add chest hole
vector<Point*> chest_hole = CreateChestHole();
num_points += chest_hole.size();
cdt->AddHole(chest_hole);
polylines.push_back(head_hole);
polylines.push_back(chest_hole);
} else if (random_distribution) {
max-=(1e-4);
min+=(1e-4);
for(int i = 0; i < num_points; i++) {
double x = Random(Fun, min, max);
double y = Random(Fun, min, max);
cdt->AddPoint(new Point(x, y));
}
}
/*
* STEP 3: Triangulate!
*/
cdt->Triangulate();
double dt = glfwGetTime() - init_time;
triangles = cdt->GetTriangles();
map = cdt->GetMap();
cout << "Number of points = " << num_points << endl;
cout << "Number of triangles = " << triangles.size() << endl;
cout << "Elapsed time (ms) = " << dt*1000.0 << endl;
MainLoop(zoom);
// Cleanup
delete cdt;
// Free points
for(int i = 0; i < polylines.size(); i++) {
vector<Point*> poly = polylines[i];
FreeClear(poly);
}
ShutDown(0);
return 0;
}
void Init()
{
const int window_width = 800,
window_height = 600;
if (glfwInit() != GL_TRUE)
ShutDown(1);
// 800 x 600, 16 bit color, no depth, alpha or stencil buffers, windowed
if (glfwCreateWindow(window_width, window_height,"Poly2Tri - C++", NULL, NULL))
ShutDown(1);
glfwSwapInterval(1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.0, 0.0, 0.0, 0.0);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
}
void ShutDown(int return_code)
{
glfwTerminate();
exit(return_code);
}
void MainLoop(const double zoom)
{
// the time of the previous frame
double old_time = glfwGetTime();
// this just loops as long as the program runs
bool running = true;
while (running) {
// calculate time elapsed, and the amount by which stuff rotates
double current_time = glfwGetTime(),
delta_rotate = (current_time - old_time) * rotations_per_tick * 360;
old_time = current_time;
// escape to quit, arrow keys to rotate view
// Check if ESC key was pressed or window was closed
// z axis always rotates
rotate_z += delta_rotate;
// Draw the scene
if (draw_map) {
DrawMap(zoom);
} else {
Draw(zoom);
}
// swap back and front buffers
glfwPollEvents();
}
}
void ResetZoom(double zoom, double cx, double cy, double width, double height)
{
double left = -width / zoom;
double right = width / zoom;
double bottom = -height / zoom;
double top = height / zoom;
// Reset viewport
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Reset ortho view
glOrtho(left, right, bottom, top, 1, -1);
glTranslatef(-cx, -cy, 0);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_DEPTH_TEST);
glLoadIdentity();
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT);
}
void Draw(const double zoom)
{
// reset zoom
Point center = Point(cx, cy);
ResetZoom(zoom, center.x, center.y, 800, 600);
for (int i = 0; i < triangles.size(); i++) {
Triangle& t = *triangles[i];
Point& a = *t.GetPoint(0);
Point& b = *t.GetPoint(1);
Point& c = *t.GetPoint(2);
// Red
glColor3f(1, 0, 0);
glBegin(GL_LINE_LOOP);
glVertex2f(a.x, a.y);
glVertex2f(b.x, b.y);
glVertex2f(c.x, c.y);
glEnd();
}
// green
glColor3f(0, 1, 0);
for(int i = 0; i < polylines.size(); i++) {
vector<Point*> poly = polylines[i];
glBegin(GL_LINE_LOOP);
for(int j = 0; j < poly.size(); j++) {
glVertex2f(poly[j]->x, poly[j]->y);
}
glEnd();
}
}
void DrawMap(const double zoom)
{
// reset zoom
Point center = Point(cx, cy);
ResetZoom(zoom, center.x, center.y, 800, 600);
list<Triangle*>::iterator it;
for (it = map.begin(); it != map.end(); it++) {
Triangle& t = **it;
Point& a = *t.GetPoint(0);
Point& b = *t.GetPoint(1);
Point& c = *t.GetPoint(2);
ConstrainedColor(t.constrained_edge[2]);
glBegin(GL_LINES);
glVertex2f(a.x, a.y);
glVertex2f(b.x, b.y);
glEnd( );
ConstrainedColor(t.constrained_edge[0]);
glBegin(GL_LINES);
glVertex2f(b.x, b.y);
glVertex2f(c.x, c.y);
glEnd( );
ConstrainedColor(t.constrained_edge[1]);
glBegin(GL_LINES);
glVertex2f(c.x, c.y);
glVertex2f(a.x, a.y);
glEnd( );
}
}
void ConstrainedColor(bool constrain)
{
if (constrain) {
// Green
glColor3f(0, 1, 0);
} else {
// Red
glColor3f(1, 0, 0);
}
}
vector<Point*> CreateHeadHole() {
vector<Point*> head_hole;
head_hole.push_back(new Point(325, 437));
head_hole.push_back(new Point(320, 423));
head_hole.push_back(new Point(329, 413));
head_hole.push_back(new Point(332, 423));
return head_hole;
}
vector<Point*> CreateChestHole() {
vector<Point*> chest_hole;
chest_hole.push_back(new Point(320.72342,480));
chest_hole.push_back(new Point(338.90617,465.96863));
chest_hole.push_back(new Point(347.99754,480.61584));
chest_hole.push_back(new Point(329.8148,510.41534));
chest_hole.push_back(new Point(339.91632,480.11077));
chest_hole.push_back(new Point(334.86556,478.09046));
return chest_hole;
}
double StringToDouble(const std::string& s)
{
std::istringstream i(s);
double x;
if (!(i >> x))
return 0;
return x;
}
double Fun(double x)
{
return 2.5 + sin(10 * x) / x;
}
double Random(double (*fun)(double), double xmin = 0, double xmax = 1)
{
static double (*Fun)(double) = NULL, YMin, YMax;
static bool First = true;
// Initialises random generator for first call
if (First)
{
First = false;
srand((unsigned) time(NULL));
}
// Evaluates maximum of function
if (fun != Fun)
{
Fun = fun;
YMin = 0, YMax = Fun(xmin);
for (int iX = 1; iX < RAND_MAX; iX++)
{
double X = xmin + (xmax - xmin) * iX / RAND_MAX;
double Y = Fun(X);
YMax = Y > YMax ? Y : YMax;
}
}
// Gets random values for X & Y
double X = xmin + (xmax - xmin) * rand() / RAND_MAX;
double Y = YMin + (YMax - YMin) * rand() / RAND_MAX;
// Returns if valid and try again if not valid
return Y < fun(X) ? X : Random(Fun, xmin, xmax);
}
This is a file test of a library ( for triangulation in c++/opengl)
I used a lot a line compilation like :
cc -o main main.cc -lglfw -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo
g++ main.cpp -o main -lglwf
etc..
Someone can help me ?
PS : I'm on MAC OS X
Did you try using -lglfw3? You are using glfw3, I would try that
In my case after building glfw i've got file libglfw.3.dylib and when build my client code i've come with the similar link error. Solution was to rename dylib file to lglfw.3.dylib. I saw that previously for different libs, when cmake for some reason provide different lib name than expected. Hope that will help someone)

Making a heart in c++

I am extremely interested in making a heart.
I am aware of the geometric primitive types.
http://www.opentk.com/doc/chapter/2/opengl/geometry/primitives
I am curious about how I would go about getting a curved line. Would I have to use the cmath library and connect it from two points somehow?
I have been looking at a lot of different sites about the math behind making hearts.
http://www16.ocn.ne.jp/~akiko-y/heart2/index_heart2_E.html
http://www.mathematische-basteleien.de/heart.htm
I'm struggling with porting this math to c++, not the actual math; I am just beginning to learn the language.
I would love it if someone could please provide me with some example code and an explanation as I am unable to find this on the internet. Also I am using the SFML framework for this project.
Thank you!
Here is an example of the current code.
#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
int main()
{
sf::RenderWindow Window;
Window.create(sf::VideoMode(800, 600), "My First Smfl Game");
Window.setKeyRepeatEnabled(false);
sf::Texture pTexture;
while(Window.isOpen())
{
sf::Event Event;
while(Window.pollEvent(Event))
{
switch(Event.type)
{
case sf::Event::Closed:
Window.close();
break;
}
}
sf::VertexArray vArray(sf::Lines, 20);
vArray[0].position = sf::Vector2f(82, 300);
vArray[1].position = sf::Vector2f(82, 84);
vArray[2].position = sf::Vector2f(82, 84);
vArray[3].position = sf::Vector2f(200, 84);
vArray[4].position = sf::Vector2f(200, 84);
vArray[5].position = sf::Vector2f(200, 100);
vArray[6].position = sf::Vector2f(200, 100);
vArray[7].position = sf::Vector2f(99, 100);
vArray[8].position = sf::Vector2f(99, 100);
vArray[9].position = sf::Vector2f(99, 284);
vArray[10].position = sf::Vector2f(99, 284);
vArray[11].position = sf::Vector2f(200, 284);
vArray[12].position = sf::Vector2f(200, 284);
vArray[13].position = sf::Vector2f(200, 300);
vArray[14].position = sf::Vector2f(200, 300);
vArray[15].position = sf::Vector2f(82, 300);
vArray[16].position = sf::Vector2f(250, 300);
vArray[17].position = sf::Vector2f(300, 82);
vArray[18].position = sf::Vector2f(380, 300);
vArray[19].position = sf::Vector2f(320, 82);
for(int k = 0; k < 20; k++)
{
int red = rand() % 255;
int green = rand() % 255;
int blue = rand() % 255;
vArray[k].color = sf::Color(red, green, blue);
}
Window.draw(vArray);
Window.display();
Window.clear();
}
}
Replace the hard-coded coordinates for your curve (all the vArray[.].position assignments) by code that generates the coordinates. To generate these coordinates, you simply have to sample one of the proposed curves from your references. What follows is a possible implementation of method 3 from your second link (it's the one with the four squares, which seemed simple enough to implement):
#include <vector>
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif // M_PI
// ...
int x0 = 800 / 2; // Coordinates of the center of the heart
int y0 = 600 / 2;
int size = 400; // Size of the heart
int r = size / 4; // Radius of the curves
int total_curve_vertex_count = 40; // Maximum number of vertices per curve
int total_vertex_count = 80; // Total number of vertices: 30 + 10 + 10 + 30
struct CurveInfo // Store information for each of the four square curves
{
int vertex_count;
double t0; // Angle origin
double s; // Angle sign: +1 or -1
int cx, cy; // (Relative) coordinates of the center of the curve
}
curve_infos[4] =
{
// Upper-left
{ 3 * total_curve_vertex_count / 4, 0.0, -1.0, -r, -r},
// Lower-left
{ total_curve_vertex_count / 4, 1.5 * M_PI, 1.0, -r, r},
// Lower-right
{ total_curve_vertex_count / 4, M_PI, 1.0, r, r},
// Upper-right
{ 3 * total_curve_vertex_count / 4, 0.5 * M_PI, -1.0, r, -r},
};
std::vector<sf::Vector2f> vertices(total_vertex_count);
int vertex_index = 0;
for(int i = 0; i < 4; i++)
{
CurveInfo& curve_info = curve_infos[i];
int vertex_count = curve_info.vertex_count;
double t0 = curve_info.t0;
double s = curve_info.s;
int cx = x0 + curve_info.cx;
int cy = y0 + curve_info.cy;
for(int j = 0; j < vertex_count; j++)
{
double dt = s * 2.0 * M_PI * j / (total_curve_vertex_count - 1);
int x = cx + r * cos(t0 + dt);
int y = cy + r * sin(t0 + dt);
vertices[vertex_index++] = sf::Vector2f(x, y);
}
}
// Generate the vertices of the lines primitives
int total_line_count = total_vertex_count - 1;
// Don't duplicate the first and last vertices
int line_vertex_count = 2 * total_vertex_count - 2;
sf::VertexArray vArray(sf::Lines, line_vertex_count);
int line_index = 0;
vertex_index = 0;
for(int k = 0; k < total_line_count; k++)
{
vArray[line_index++].position = vertices[vertex_index++];
vArray[line_index++].position = vertices[vertex_index];
}
for(int k = 0; k < line_vertex_count; k++)
{
int red = rand() % 255;
int green = rand() % 255;
int blue = rand() % 255;
vArray[k].color = sf::Color(red, green, blue);
}
// ...