Making a heart in c++ - 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);
}
// ...

Related

How to fill sf::VertexArray of sf::TriangleStrip with color?

I would like to increase the drawing performance by using a single sf::VertexArray to display thousands of circles. To make sure it works, I wrote this example:
#include <SFML/Graphics.hpp>
#include <iostream>
#define WIDTH 100
#define HEIGHT 100
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "RTREE",
sf::Style::Close);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
sf::CircleShape circle(50);
circle.setPosition(100,100);
size_t count = circle.getPointCount();
sf::VertexArray objects(sf::TriangleStrip, count);
for (int i = 0; i < count; i++) {
objects.append(circle.getPoint(i));
}
for (int i = 0; i < objects.getVertexCount(); i++) {
objects[i].color = sf::Color::Blue;
}
window.clear();
window.draw(objects);
window.display();
}
}
However, the color only applies to the line, not the fill:
Is it possible to fill the shape with a single color?
You are right, displaying more than 1000 circles with a window.draw between each of them result in poor performances. The vertexArray is indeed a good solution.
You used sf::TriangleStrip which does not work as you expected. Your verticles are indeed painted, but they are only on the edge of the circle. You need to use either sf::TriangleFan or sf::Triangles if you want more than one circle per array.
Here an example. I was able to display 513'000 verticles at 60 fps (each circle is about 30 verticles, which is about 10'000 circles.
#include <SFML/Graphics.hpp>
#include <cmath>
#include <iostream>
#include <sstream>
#define WIDTH 800
#define HEIGHT 800
using namespace std;
template <typename T>
std::string to_string_with_precision(const T a_value, const int n = 6)
{
std::ostringstream out;
out.precision(n);
out << std::fixed << a_value;
return out.str();
}
void addCircle(sf::VertexArray &array, sf::Vector2f position, float radius, sf::Color color) {
size_t count = 30;
for (int i = 0; i < count; i += 1) {
sf::Vertex v0;
v0.position = sf::Vector2f(position.x, position.y);
v0.color = color;
array.append(v0);
sf::Vertex v1;
float angle = i * 2 * M_PI / count;
v1.position = sf::Vector2f(position.x + cos(angle) * radius, position.y + sin(angle) * radius);
v1.color = color;
array.append(v1);
sf::Vertex v2;
angle = (i + 1) * 2 * M_PI / count;
v2.position = sf::Vector2f(position.x + cos(angle) * radius, position.y + sin(angle) * radius);
v2.color = color;
array.append(v2);
}
}
#define FRAMERATE 120
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "RTREE", sf::Style::Close);
// Fps text
sf::Font font;
if (!font.loadFromFile("../assets/collegiate.ttf")) {
std::cout << "Error loading font" << std::endl;
}
sf::Text fps;
fps.setFont(font);
fps.setPosition(10, 10);
fps.setCharacterSize(16);
fps.setFillColor(sf::Color::White);
// Time management
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
sf::Time timePerFrame = sf::seconds(1.f / FRAMERATE);
sf::Time timeSinceLastDraw = sf::Time::Zero;
window.setFramerateLimit(FRAMERATE);
float vr = 0;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
size_t count = 15;
sf::Color color = sf::Color::Red;
color.a = 50;
sf::Color color2 = sf::Color::Blue;
color2.a = 20;
sf::VertexArray objects(sf::Triangles);
for (int k = 0; k < 5; k++) {
sf::Color c = k % 2 ? color : color2;
for (int j = 20; j < 400; j += 5) {
for (int i = 0; i < count; i++) {
float angle = i * 2 * M_PI / count;
angle += (i % 2 ? vr : -vr) * k * (k % 2 ? 1 : -1);
addCircle(objects, sf::Vector2f(WIDTH / 2 + cos(angle) * j, HEIGHT / 2 + sin(angle) * j), j / 5, c);
}
}
}
timeSinceLastDraw = clock.restart();
timeSinceLastUpdate += timeSinceLastDraw;
double timeFps = 1.f / timeSinceLastDraw.asSeconds();
fps.setString("verticles: " + to_string_with_precision(objects.getVertexCount(), 0) + " fps: " + to_string_with_precision(timeFps, 0));
while (timeSinceLastUpdate > timePerFrame) {
timeSinceLastUpdate -= timePerFrame;
vr += 0.01;
}
window.clear();
window.draw(objects);
window.draw(fps);
window.display();
}
}

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.

c++ / SFML - Memory leak shown in valgrind report

I've this little code that repeatedly draws a shape using SFML. It aborts abruptly with different error messages, like corrupted size vs. prev_size / Aborted (core dumped), munmap_chunk(): invalid pointer / or Segmentation fault (core dumped).
I used valgrind to track down what looks like some memory leak, but the report is rather cryptic to me. Nevertheless, as far as I understand the thing, "definitely lost: 4,096 bytes in 1 blocks" is not a good omen. The most puzzling is that it does not abort when I run it through valgrind.
I'll keep on investigating, but if anyone could give me a hint, that would be great.
Best regards,
MC
g++ -std=c++11 ./k.cpp -o ./k -Wfatal-errors -lsfml-graphics -lsfml-window -lsfml-system
#include "SFML/Graphics.hpp"
#include <iostream>
#include <math.h>
#include <random>
#include <stdio.h>
#include <string>
using namespace std;
struct point {
double x;
double y;
};
struct curve {
int index;
point centerPoint;
double radius;
sf::ConvexShape shape;
sf::Text curveName;
point curveNamePosition;
};
curve computeCurve(point centerPoint, double radius) {
unsigned short numberOfPoints = 200;
curve curve;
curve.shape.setPointCount(numberOfPoints);
curve.centerPoint.x = centerPoint.x;
curve.centerPoint.y = centerPoint.y;
curve.radius = radius;
double alpha = 2 * M_PI / numberOfPoints;
unsigned short a = 1;
point point;
for (unsigned short i = 0; i < numberOfPoints + 1; i++) {
point.x = radius * (0.5 + cos(2 * a * alpha * i) / 2) * cos(alpha * i) +
centerPoint.x;
point.y = radius * sin(a * alpha * i) + centerPoint.y;
curve.shape.setPoint(i, sf::Vector2f(point.x, point.y));
};
for (unsigned short i = 0; i < numberOfPoints - 1; i++) {
point.x = radius * (0.5 + cos(2 * a * alpha * i) / 2) * cos(alpha * i) +
centerPoint.x;
point.y = -radius * sin(a * alpha * i) + centerPoint.y;
curve.shape.setPoint(2 * numberOfPoints - i,
sf::Vector2f(point.x, point.y));
};
curve.shape.setOrigin(curve.centerPoint.x, curve.centerPoint.y);
curve.shape.setPosition(curve.centerPoint.x, curve.centerPoint.y);
curve.curveNamePosition.x = curve.centerPoint.x;
curve.curveNamePosition.y = curve.centerPoint.y;
curve.curveName.setString("Curve");
curve.curveName.setPosition(curve.centerPoint.x, curve.centerPoint.y);
return curve;
}
int main(int argc, char **argv) {
const unsigned short windowWidth = 800;
const unsigned short windowHeight = 800;
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "Demo",
sf::Style::Default); // Default / None // Fullscreen
string myfontFileName = "./media/Arial.ttf";
sf::Font myFont;
if (!myFont.loadFromFile(myfontFileName)) {
cout << "Could not find the font " << myfontFileName << endl;
}
sf::Event myEvent;
sf::Clock curveClock;
// Initialization
point centerPoint;
centerPoint.x = 300;
centerPoint.y = 300;
double radius = 200;
curve mt = computeCurve(centerPoint, radius);
mt.shape.setOutlineColor(sf::Color::Red);
mt.shape.setFillColor(sf::Color(40, 140, 10, 127));
mt.shape.setOutlineThickness(1.f);
mt.curveName.setFont(myFont);
mt.curveName.setCharacterSize(20);
mt.curveName.setFillColor(sf::Color::White);
std::random_device randomDevice;
std::mt19937 seed(randomDevice());
while (window.isOpen()) {
while (window.pollEvent(myEvent)) {
if (myEvent.type == sf::Event::EventType::Closed) {
window.close();
}
}
window.clear();
if (curveClock.getElapsedTime().asMilliseconds() > 200.0f) {
std::uniform_int_distribution<std::mt19937::result_type> rDistribution(
10, 300);
double radius = rDistribution(seed);
std::uniform_int_distribution<std::mt19937::result_type> cDistribution(
100, 300);
centerPoint.x = cDistribution(seed);
centerPoint.y = cDistribution(seed);
mt = computeCurve(centerPoint, radius);
mt.shape.setOutlineColor(sf::Color::Red);
mt.shape.setFillColor(sf::Color(40, 140, 10, 127));
mt.shape.setOutlineThickness(1.f);
mt.curveName.setFont(myFont);
mt.curveName.setCharacterSize(20);
mt.curveName.setFillColor(sf::Color::White);
curveClock.restart();
}
window.draw(mt.shape);
window.draw(mt.curveName);
window.display();
}
return EXIT_SUCCESS;
}
There was definitely an error in the way the equation was defined. Here is a corrected code if someone is interested.
Best regards,
MC
#include "SFML/Graphics.hpp"
#include <iostream>
#include <math.h>
#include <random>
#include <stdio.h>
#include <string>
using namespace std;
struct point {
double x;
double y;
};
struct curve {
int index;
point centerPoint;
double radius;
sf::ConvexShape shape;
sf::Text name;
point namePosition;
};
curve computeCurve(point centerPoint, double radius) {
std::random_device randomDevice;
std::mt19937 seed(randomDevice());
std::uniform_int_distribution<std::mt19937::result_type> aDistribution(1, 4);
int a = aDistribution(seed);
unsigned short numberOfPoints = 100 * a;
curve curve;
curve.shape.setPointCount(numberOfPoints);
curve.centerPoint.x = centerPoint.x;
curve.centerPoint.y = centerPoint.y;
curve.radius = radius;
double alpha = 2 * M_PI / numberOfPoints;
point point;
for (unsigned short i = 0; i < numberOfPoints + 1; i++) {
point.x = radius * (0.5 + cos(2 * a * alpha * i) / 2) * cos(alpha * i) +
centerPoint.x;
point.y = radius * sin(a * alpha * i) + centerPoint.y;
curve.shape.setPoint(i, sf::Vector2f(point.x, point.y));
};
curve.shape.setOrigin(curve.centerPoint.x, curve.centerPoint.y);
curve.shape.setPosition(curve.centerPoint.x, curve.centerPoint.y);
curve.namePosition.x = curve.centerPoint.x;
curve.namePosition.y = curve.centerPoint.y;
curve.name.setString("Curve");
curve.name.setPosition(curve.centerPoint.x, curve.centerPoint.y);
return curve;
}
curve computeCardioid(point centerPoint, double radius) {
double perimeter = 8 * radius;
int numberOfPoints = max(floor(perimeter / 3), 20.0d);
curve curve;
curve.shape.setPointCount(numberOfPoints);
curve.centerPoint.x = centerPoint.x;
curve.centerPoint.y = centerPoint.y;
curve.radius = radius;
double alpha = 2 * M_PI / numberOfPoints;
point point;
for (unsigned short i = 0; i < numberOfPoints; i++) {
point.x = 2 * radius * (1 - cos(alpha * i)) * cos(alpha * i) +
curve.centerPoint.x;
point.y = 2 * radius * (1 - cos(alpha * i)) * sin(alpha * i) +
curve.centerPoint.y;
curve.shape.setPoint(i, sf::Vector2f(point.x, point.y));
};
curve.shape.setOrigin(curve.centerPoint.x - 1.5 * radius,
curve.centerPoint.y);
curve.shape.setPosition(curve.centerPoint.x, curve.centerPoint.y);
curve.namePosition.x = curve.centerPoint.x;
curve.namePosition.y = curve.centerPoint.y;
curve.name.setString("Card");
curve.name.setPosition(curve.centerPoint.x, curve.centerPoint.y);
return curve;
}
int main(int argc, char **argv) {
const unsigned short windowWidth = 800;
const unsigned short windowHeight = 800;
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight), "Demo",
sf::Style::Default); // Default / None // Fullscreen
string myfontFileName = "./media/Arial.ttf";
sf::Font myFont;
if (!myFont.loadFromFile(myfontFileName)) {
cout << "Could not find the font " << myfontFileName << endl;
}
sf::Event myEvent;
sf::Clock curveClock;
// Initialization
point centerPoint;
centerPoint.x = 300;
centerPoint.y = 300;
double radius = 200;
curve curve = computeCurve(centerPoint, radius);
curve.shape.setOutlineColor(sf::Color::Red);
curve.shape.setFillColor(sf::Color(40, 140, 10, 127));
curve.shape.setOutlineThickness(1.f);
curve.name.setFont(myFont);
curve.name.setCharacterSize(20);
curve.name.setFillColor(sf::Color::White);
random_device randomDevice;
mt19937 seed(randomDevice());
while (window.isOpen()) {
while (window.pollEvent(myEvent)) {
if (myEvent.type == sf::Event::EventType::Closed) {
window.close();
}
}
window.clear();
if (curveClock.getElapsedTime().asMilliseconds() > 1000.0f) {
uniform_int_distribution<mt19937::result_type> rDistribution(10, 300);
double radius = rDistribution(seed);
point centerPoint;
uniform_int_distribution<mt19937::result_type> cDistribution(100, 300);
centerPoint.x = cDistribution(seed);
centerPoint.y = cDistribution(seed);
curve = computeCurve(centerPoint, radius);
curve.shape.setOutlineColor(sf::Color::Red);
curve.shape.setFillColor(sf::Color(40, 140, 10, 127));
curve.shape.setOutlineThickness(1.f);
curve.name.setFont(myFont);
curve.name.setCharacterSize(20);
curve.name.setFillColor(sf::Color::White);
curveClock.restart();
}
window.draw(curve.shape);
window.draw(curve.name);
window.display();
}
return EXIT_SUCCESS;
}

[OpenCv][C++] Creating a smooth, airbrush-like brush stroke, similar to Photoshop?

I have a circular brush of with a diameter of 200px and hardness of 0 (the brush is a circular gradient). The spacing between each brush is 25% of the brush diameter. However, when I compare the stroke my program draws and the stroke Photoshop draws, where all settings are equal...
It is clear that photoshop's is much smoother! I can't reduce the spacing because that causes the edges to become harder
How can i make my stroke like photoshop's?
Here is the relevant code from my program...
//defining a circle
Mat alphaBrush(2*outerRadius,2*outerRadius,CV_32FC1);
float floatInnerRadius = outerRadius * hardness;
for(int i = 0; i < alphaBrush.rows; i++ ){
for(int j=0; j<alphaBrush.cols; j++ ){
int x = outerRadius - i;
int y = outerRadius - j;
float radius=hypot((float) x, (float) y );
auto& pixel = alphaBrush.at<float>(i,j);
if(radius>outerRadius){ pixel=0.0; continue;} // transparent
if(radius<floatInnerRadius){ pixel=1.0; continue;} // solid
pixel=1-((radius-floatInnerRadius)/(outerRadius-floatInnerRadius)); // partial
}
}
/*
(...irrelevant stuff)
*/
//drawing the brush onto the canvas
for (int j = 0; j < inMatROI.rows; j++) {
Vec3b *thisBgRow = inMatROI.ptr<Vec3b>(j);
float *thisAlphaRow = brushROI.ptr<float>(j);
for (int i = 0; i < inMatROI.cols; i++) {
for (int c = 0; c < 3; c++) {
thisBgRow[i][c] = saturate_cast<uchar>((brightness * thisAlphaRow[i]) + ((1.0 - thisAlphaRow[i]) * thisBgRow[i][c]));
}
}
}
I have also tried resultValue = max(backgroundValue, brushValue), but the intersection between the two circles is pretty obvious.
this is the approach, drawing a solid thin line and afterwards computing the distance of each pixel to that line.
As you can see there are some artifacts, probably mostly because of only approximated distance values from cv::distanceTransform. If you compute the distances precisely (and maybe in double precision) you should get very smooth results.
int main()
{
cv::Mat canvas = cv::Mat(768, 768, CV_8UC3, cv::Scalar::all(255));
cv::Mat canvasMask = cv::Mat::zeros(canvas.size(), CV_8UC1);
// make sure the stroke has always a size of >= 2, otherwise will be cv::line way not work...
std::vector<cv::Point> strokeSampling;
strokeSampling.push_back(cv::Point(250, 100));
strokeSampling.push_back(cv::Point(250, 200));
strokeSampling.push_back(cv::Point(600, 300));
strokeSampling.push_back(cv::Point(600, 400));
strokeSampling.push_back(cv::Point(250, 500));
strokeSampling.push_back(cv::Point(250, 650));
for (int i = 0; i < strokeSampling.size() - 1; ++i)
cv::line(canvasMask, strokeSampling[i], strokeSampling[i + 1], cv::Scalar::all(255));
// computing a distance map:
cv::Mat tmp1 = 255 - canvasMask;
cv::Mat distMap;
cv::distanceTransform(tmp1, distMap, CV_DIST_L2, CV_DIST_MASK_PRECISE);
float outerRadius = 50;
float innerRadius = 10;
cv::Scalar strokeColor = cv::Scalar::all(0);
for (int y = 0; y < distMap.rows; ++y)
for (int x = 0; x < distMap.cols; ++x)
{
float percentage = 0.0f;
float radius = distMap.at<float>(y, x);
if (radius>outerRadius){ percentage = 0.0; } // transparent
else
if (radius<innerRadius){ percentage = 1.0; } // solid
else
{
percentage = 1 - ((radius - innerRadius) / (outerRadius - innerRadius)); // partial
}
if (percentage > 0)
{
// here you could use the canvasMask if you like to, instead of directly drawing on the canvas
cv::Vec3b canvasColor = canvas.at<cv::Vec3b>(y, x);
cv::Vec3b cColor = cv::Vec3b(strokeColor[0], strokeColor[1], strokeColor[2]);
canvas.at<cv::Vec3b>(y, x) = percentage*cColor + (1 - percentage) * canvasColor;
}
}
cv::imshow("out", canvas);
cv::imwrite("C:/StackOverflow/Output/stroke.png", canvas);
cv::waitKey(0);
}

glDrawElements doesn't draw when vertices are in for loop

I'm trying to create a circle using vertex arrays, I'm filling the array in a for loop with the points, but nothing is rendered, instead, If I put the values manually in the array everything works fine, I don't know what must be wrong.
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
#define PI 3.14159265358979324
static float R = 40.0; // Radius of circle.
static float X = 50.0; // X-coordinate of center of circle.
static float Y = 50.0; // Y-coordinate of center of circle.
static int numVertices = 5; // Number of vertices on circle.
static float vertices[15] =
{
};
static unsigned int stripIndices[] = { 0, 1, 2, 3, 4};
// Drawing routine.
void drawScene(void)
{
float t = 0; // Angle parameter.
int i;
int k = 1;
for (i = 0; i < numVertices; i++)
{
vertices[k] = X + R * cos(t);
k++;
vertices[k] = Y + R * sin(t);
k++;
vertices[k] = 0.0;
k++;
t += 2 * PI / numVertices;
}
glClear(GL_COLOR_BUFFER_BIT);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(1.0, 0.0, 0.0);
glDrawElements(GL_LINE_LOOP, 5, GL_UNSIGNED_INT, stripIndices);
glFlush();
}
Oh, just noticed that you do 'k=1'. That should be 'k=0'.