Calculating fast line end point - c++

What i want is that, I have info of 2 points, the starting x,y and mid point x,y and i need to find end line like until some kind of border, like window
here is what I do:
//function for calculating the end point from one location, to specific end location
//like a bullet moving forward in a line
//x,y start location(mouse), x2,y2(rect point location one of the 4) mid point, qx,qy end point(shadow or triangle draw location)
void screenEnd(int x, int y, int x2, int y2, int*qx,int*qy)
{
x = x2-x;
y = y2-y;
float tx = x2,ty = y2;
float result = atan2((float)y,(float)x) * 180 / PI;
float tempx = cos ( result * PI / 180.0 );
float tempy = sin ( result * PI / 180.0 );
bool check = true;
//this part needs optimization
while(check)
{
if(tx < 0|| ty < 0|| tx > 1280 || ty > 720)
{
check = false;
}
else
{
tx += tempx;
ty += tempy;
}
}
*qx = tx;
*qy = ty;
}
what I do is just increase point until it reaches the end.
Is there any way faster?

A classic window clipping task.
Consider a parametric equation where p is the point (x,y).
p(0) = x, y
p(0.5) = x2, y2
p(1) = x+2*(x2-x), y + 2*(y2-y)
p(t) = p(0) + t*(p(1) - p(0))
clip window = 0,0 to 720, 1280 (suspect you really want 719,1279)
The segment to draw initially ranges from t=0.0 to t=1.0. The segment is tested against each of the 4 sides of the bounding box, potentially reducing the t range. Maybe even eliminating all together.
Follows is some old code, enough to get you going.
#include <math.h>
int cliptest(int dz, int z, double *t0, double *t1) {
if (dz < 0) {
double t = ((double) z) / dz;
if (t > *t1)
return 0;
if (t > *t0)
*t0 = t;
} else if (dz > 0) {
double t = ((double) z) / dz;
if (t < *t0)
return 0;
if (t < *t1)
*t1 = t;
} else {
if (z < 0)
return 0;
}
return 1;
}
int clipper(int *px0, int *py0, int *px1, int *py1, int minx, int miny,
int maxx, int maxy) {
double t0, t1;
int dx, dy;
t0 = 0.0;
t1 = 1.0;
dy = *py1 - *py0;
dx = *px1 - *px0;
if (cliptest(-dx, *px0 - minx, &t0, &t1)
&& cliptest(dx, maxx - *px0, &t0, &t1)
&& cliptest(-dy, *py0 - miny, &t0, &t1)
&& cliptest(dy, maxy - *py0, &t0, &t1)) {
if (t1 < 1.0) {
*px1 = round(*px0 + t1*dx);
*py1 = round(*py0 + t1*dy);
}
if (t0 > 0.0) {
*px0 = round(*px0 + t0*dx);
*py0 = round(*py0 + t0*dy);
}
return 1;
}
return 0;
}
int x0 = x;
int y0 = y;
int x1 = x + 2*(x2-x); // Form end point
int y1 = x + 2*(y2-y);
if (clipper(&x0, &y0, &x1, &y1, 0, 0, 720, 1280))
Draw(x0, y0, x1, y2);
else
Handle_LineTotallyClippedOut();

Related

Efficient Algorithm to obtain Points in a Circle around a Center

Problem
I want to get all pixels that are within a circle of a given radius about a given point, where points can only have integer coordinates, i.e. pixels in a canvas.
So I want to obtain all points in the yellow area given (x, y) and r.
Approaches
The most efficient way I can think of is to loop through a square around (x, y) and check the Euclidean distance for each point:
for (int px = x - r; px <= x + r; px++) {
for (int py = y - r; py <= y + r; py++) {
int dx = x - px, dy = y - py;
if (dx * dx + dy * dy <= r * r) {
// Point is part of the circle.
}
}
}
However, this means that this algorithm will check (r * 2)^2 * (4 - pi) / 4 pixels that are not part of the circle. dx * dx + dy * dy <= r * r, which seems rather expensive, is called redundantly almost 1 / 4 of the time.
Integrating something like what was proposed here might enhance performance:
for (int px = x - r; px <= x + r; px++) {
for (int py = y - r; py <= y + r; py++) {
int dx = abs(x - px), dy = abs(y - py);
if (dx + dy <= r || (!(dx > r || dy > r) && (dx * dx + dy * dy <= r * r))) {
// Point is part of the circle.
}
}
}
However as the author themselves pointed out, this is likely not going to be faster when most of the points are going to be inside of the circle (especially because of abs), which pi / 4 are in this case.
I was not able to find any resources on this question. I am looking specifically for a solution in C++ and not something in SQL.
Alright here are the benchmarks I promised.
Setup
I used google benchmark and the task was to insert all points within the perimiter of the circle into a std::vector<point>. I benchmark for a set of radii and a constant center:
radii = {10, 20, 50, 100, 200, 500, 1000}
center = {100, 500}
language: C++17
compiler: msvc 19.24.28316 x64
platform: windows 10
optimization: O2 (full optimization)
threading: single threaded execution
The results of each algorithm is tested for correctness (compared against the output of OPs algorithm).
So far the following algorithms are benchmarked:
OP's algorithm enclosing_square.
My algorithm containing_square.
creativecreatorormaybenot's algorithm edge_walking.
Mandy007's algorithm binary_search.
Results
Run on (12 X 3400 MHz CPU s)
CPU Caches:
L1 Data 32K (x6)
L1 Instruction 32K (x6)
L2 Unified 262K (x6)
L3 Unified 15728K (x1)
-----------------------------------------------------------------------------
Benchmark Time CPU Iterations
-----------------------------------------------------------------------------
binary_search/10/manual_time 804 ns 3692 ns 888722
binary_search/20/manual_time 2794 ns 16665 ns 229705
binary_search/50/manual_time 16562 ns 105676 ns 42583
binary_search/100/manual_time 66130 ns 478029 ns 10525
binary_search/200/manual_time 389964 ns 2261971 ns 1796
binary_search/500/manual_time 2286526 ns 15573432 ns 303
binary_search/1000/manual_time 9141874 ns 68384740 ns 77
edge_walking/10/manual_time 703 ns 5492 ns 998536
edge_walking/20/manual_time 2571 ns 49807 ns 263515
edge_walking/50/manual_time 15533 ns 408855 ns 45019
edge_walking/100/manual_time 64500 ns 1794889 ns 10899
edge_walking/200/manual_time 389960 ns 7970151 ns 1784
edge_walking/500/manual_time 2286964 ns 55194805 ns 308
edge_walking/1000/manual_time 9009054 ns 234575321 ns 78
containing_square/10/manual_time 629 ns 4942 ns 1109820
containing_square/20/manual_time 2485 ns 40827 ns 282058
containing_square/50/manual_time 15089 ns 361010 ns 46311
containing_square/100/manual_time 62825 ns 1565343 ns 10990
containing_square/200/manual_time 381614 ns 6788676 ns 1839
containing_square/500/manual_time 2276318 ns 45973558 ns 312
containing_square/1000/manual_time 8886649 ns 196004747 ns 79
enclosing_square/10/manual_time 1056 ns 4045 ns 660499
enclosing_square/20/manual_time 3389 ns 17307 ns 206739
enclosing_square/50/manual_time 18861 ns 106184 ns 37082
enclosing_square/100/manual_time 76254 ns 483317 ns 9246
enclosing_square/200/manual_time 421856 ns 2295571 ns 1654
enclosing_square/500/manual_time 2474404 ns 15625000 ns 284
enclosing_square/1000/manual_time 9728718 ns 68576389 ns 72
Code
The complete test code is below, you can copy & paste it and test it yourself. fill_circle.cpp contains the implementation of the different algorithms.
main.cpp
#include <string>
#include <unordered_map>
#include <chrono>
#include <benchmark/benchmark.h>
#include "fill_circle.hpp"
using namespace std::string_literals;
std::unordered_map<const char*, circle_fill_func> bench_tests =
{
{"enclosing_square", enclosing_square},
{"containing_square", containing_square},
{"edge_walking", edge_walking},
{"binary_search", binary_search},
};
std::vector<int> bench_radii = {10, 20, 50, 100, 200, 500, 1000};
void postprocess(std::vector<point>& points)
{
std::sort(points.begin(), points.end());
//points.erase(std::unique(points.begin(), points.end()), points.end());
}
std::vector<point> prepare(int radius)
{
std::vector<point> vec;
vec.reserve(10ull * radius * radius);
return vec;
}
void bm_run(benchmark::State& state, circle_fill_func target, int radius)
{
using namespace std::chrono;
constexpr point center = {100, 500};
auto expected_points = prepare(radius);
enclosing_square(center, radius, expected_points);
postprocess(expected_points);
for (auto _ : state)
{
auto points = prepare(radius);
auto start = high_resolution_clock::now();
target(center, radius, points);
auto stop = high_resolution_clock::now();
postprocess(points);
if (expected_points != points)
{
auto text = "Computation result incorrect. Expected size: " + std::to_string(expected_points.size()) + ". Actual size: " + std::to_string(points.size()) + ".";
state.SkipWithError(text.c_str());
break;
}
state.SetIterationTime(duration<double>(stop - start).count());
}
}
int main(int argc, char** argv)
{
for (auto [name, target] : bench_tests)
for (int radius : bench_radii)
benchmark::RegisterBenchmark(name, bm_run, target, radius)->Arg(radius)->UseManualTime();
benchmark::Initialize(&argc, argv);
if (benchmark::ReportUnrecognizedArguments(argc, argv))
return 1;
benchmark::RunSpecifiedBenchmarks();
}
fill_circle.hpp
#pragma once
#include <vector>
struct point
{
int x = 0;
int y = 0;
};
constexpr bool operator<(point const& lhs, point const& rhs) noexcept
{
return lhs.x != rhs.x
? lhs.x < rhs.x
: lhs.y < rhs.y;
}
constexpr bool operator==(point const& lhs, point const& rhs) noexcept
{
return lhs.x == rhs.x && lhs.y == rhs.y;
}
using circle_fill_func = void(*)(point const& center, int radius, std::vector<point>& points);
void enclosing_square(point const& center, int radius, std::vector<point>& points);
void containing_square(point const& center, int radius, std::vector<point>& points);
void edge_walking(point const& center, int radius, std::vector<point>& points);
void binary_search(point const& center, int radius, std::vector<point>& points);
fill_circle.cpp
#include "fill_circle.hpp"
constexpr double sqrt2 = 1.41421356237309504880168;
constexpr double pi = 3.141592653589793238462643;
void enclosing_square(point const& center, int radius, std::vector<point>& points)
{
int sqr_rad = radius * radius;
for (int px = center.x - radius; px <= center.x + radius; px++)
{
for (int py = center.y - radius; py <= center.y + radius; py++)
{
int dx = center.x - px, dy = center.y - py;
if (dx * dx + dy * dy <= sqr_rad)
points.push_back({px, py});
}
}
}
void containing_square(point const& center, int radius, std::vector<point>& points)
{
int sqr_rad = radius * radius;
int half_side_len = radius / sqrt2;
int sq_x_end = center.x + half_side_len;
int sq_y_end = center.y + half_side_len;
// handle inner square
for (int x = center.x - half_side_len; x <= sq_x_end; x++)
for (int y = center.y - half_side_len; y <= sq_y_end; y++)
points.push_back({x, y});
// probe the rest
int x = 0;
for (int y = radius; y > half_side_len; y--)
{
int x_line1 = center.x - y;
int x_line2 = center.x + y;
int y_line1 = center.y - y;
int y_line2 = center.y + y;
while (x * x + y * y <= sqr_rad)
x++;
for (int i = 1 - x; i < x; i++)
{
points.push_back({x_line1, center.y + i});
points.push_back({x_line2, center.y + i});
points.push_back({center.x + i, y_line1});
points.push_back({center.x + i, y_line2});
}
}
}
void edge_walking(point const& center, int radius, std::vector<point>& points)
{
int sqr_rad = radius * radius;
int mdx = radius;
for (int dy = 0; dy <= radius; dy++)
{
for (int dx = mdx; dx >= 0; dx--)
{
if (dx * dx + dy * dy > sqr_rad)
continue;
for (int px = center.x - dx; px <= center.x + dx; px++)
{
for (int py = center.y - dy; py <= center.y + dy; py += 2 * dy)
{
points.push_back({px, py});
if (dy == 0)
break;
}
}
mdx = dx;
break;
}
}
}
void binary_search(point const& center, int radius, std::vector<point>& points)
{
constexpr auto search = []( const int &radius, const int &squad_radius, int dx, const int &y)
{
int l = y, r = y + radius, distance;
while (l < r)
{
int m = l + (r - l) / 2;
distance = dx * dx + (y - m) * (y - m);
if (distance > squad_radius)
r = m - 1;
else if (distance < squad_radius)
l = m + 1;
else
r = m;
}
if (dx * dx + (y - l) * (y - l) > squad_radius)
--l;
return l;
};
int squad_radius = radius * radius;
for (int px = center.x - radius; px <= center.x + radius; ++px)
{
int upper_limit = search(radius, squad_radius, px - center.x, center.y);
for (int py = 2*center.y - upper_limit; py <= upper_limit; ++py)
{
points.push_back({px, py});
}
}
}
for (line = 1; line <= r; line++) {
dx = (int) sqrt(r * r - line * line);
for (ix = 1; ix <= dx; ix++) {
putpixel(x - ix, y + line)
putpixel(x + ix, y + line)
putpixel(x - ix, y - line)
putpixel(x + ix, y - line)
}
}
To avoid repeated generation of pixels at axes, it is worth to start loops from 1 and draw central lines (ix==0 or line==0) in separate loop.
Note that there is also pure integer Bresenham algorithm to generate circumference points.
Alright, first of all we calculate the inner square of the circle. The formula for it is straight forward:
x² + y² = r² // circle formula
2h² = r² // all sides of square are of equal length so x == y, lets define h := x
h = r / sqrt(2) // half side length of the inner square
Now, every point between (-h, -h) and (+h, +h) lies within the circle. Here is an image of what I mean:
The remaining blue part is a bit tricky, but not too complicated either. We start at the very top of the blue circle (x = 0, y = -radius). Next, we walk right (x++) until we leave the circle perimiter (until x²+y² < r² doesn't hold anymore). Everything between (0, y) and (x, y) is within the circle. Because of symmetry we can extend this 8 fold by
(-x, -y), (+x, -y)
(-x, +y), (+x, +y)
(-y, -x), (-y, +x)
(+y, -x), (+y, +x)
now we go down 1 line (y--) and repeat the steps above (while keeping the most recent value of x). Add the center of the circle to each of the points and you're done.
Here is a visualization. There are some artifacts because of the upscaling. The red dot shows what we're testing at each iteration:
Here is the full code (using opencv to draw the stuff):
#include <opencv2/opencv.hpp>
constexpr double sqrt2 = 1.41421356237309504880168;
int main()
{
cv::Point center(200, 200);
constexpr int radius = 180;
// create test image
cv::Mat img(400, 400, CV_8UC3);
cv::circle(img, center, radius, {180, 0, 0}, cv::FILLED);
cv::imshow("img", img);
cv::waitKey();
// calculate inner rectangle
int halfSideLen = radius / sqrt2;
cv::Rect innerRect(center.x - halfSideLen, center.y - halfSideLen, halfSideLen * 2, halfSideLen * 2);
cv::rectangle(img, innerRect, {0, 180, 0}, cv::FILLED);
cv::imshow("img", img);
cv::waitKey();
// probe the rest
int x = 0;
for (int y = radius; y >= halfSideLen; y--)
{
for (; x * x + y * y < radius * radius; x++)
{
// anything between the following points lies within the circle
// each pair of points represents a line
// (-x, -y), (+x, -y)
// (-x, +y), (+x, +y)
// (-y, -x), (-y, +x)
// (+y, -x), (+y, +x)
// center + {(-X..X) x (-Y..Y)} is inside the circle
cv::line(img, cv::Point(center.x - x, center.y - y), cv::Point(center.x + x, center.y - y), {180, 180, 0});
cv::line(img, cv::Point(center.x - x, center.y + y), cv::Point(center.x + x, center.y + y), {180, 180, 0});
cv::line(img, cv::Point(center.x - y, center.y - x), cv::Point(center.x - y, center.y + x), {180, 180, 0});
cv::line(img, cv::Point(center.x + y, center.y - x), cv::Point(center.x + y, center.y + x), {180, 180, 0});
cv::imshow("img", img);
cv::waitKey(20);
}
}
cv::waitKey();
return 0;
}
This is an optimization that reduce 1/4 the dimension of search:
for (int px = x; px <= x + r; ++px) {
bool find = false;
int dx = x - px, dy;
for (int py = y; !find && py <= y + r; ++py) {
dy = y - py;
if (dx * dx + dy * dy <= r * r)) {
/* (px, py), (px, y+y-py+r), (x+x-px+r, py)
& (x+x-px+r, y+y-py+r) are part of the circle.*/
}else{
find = true; //Avoid increasing on the axis y
}
}
}
or better, improving performance the iteration of second circle for avoiding the if conditional
for (int px = x; px <= x + r; ++px) {
int dx = x - px, py = y;
for (; dx * dx + (py-y) * (py-y) <= r * r; ++py) {
/* (px, py), (px, y+y-py+r), (x+x-px+r, py)
& (x+x-px+r, y+y-py+r) are part of the circle.*/
}
}
well i think that other option is a binary search for upper limit:
int binarySearch(int R, int dx, int y){
int l=y, r=y+R;
while (l < r) {
int m = l + (r - l) / 2;
if(dx*dx + (y - m)*(y - m) > R*R) r = m - 1;
else if(dx*dx + (y - m)*(y - m) < R*R) l = m + 1;
else r = m;
}
if(dx*dx + (y - l)*(y - l) > R*R) --l;
return l;
}
for (int px = x; px <= x + r; ++px) {
int upperLimit = binarySearch(r, px-x, y);
for (int py = y; py <= upperLimit; ++py) {
/* (px, py), (px, y+y-py+r), (x+x-px+r, py)
& (x+x-px+r, y+y-py+r) are part of the circle.*/
}
}
The idea of binary search is to find the upper limit optimally, avoiding the if condition and calculations within the for cycle. For this, it is checked which is the largest integer that makes the distance between the current point and the radius within the circle.
PD: Sorry my English.
Code
Based on the idea from #ScottHunter, I came up with the following algorithm:
#include <functional>
// Executes point_callback for every point that is part of the circle
// defined by the center (x, y) and radius r.
void walk_circle(int x, int y, int r,
std::function<void(int x, int y)> point_callback) {
for (int px = x - r; px < x + r; px++)
point_callback(px, y);
int mdx = r;
for (int dy = 1; dy <= r; dy++)
for (int dx = mdx; dx >= 0; dx--) {
if (dx * dx + dy * dy > r * r)
continue;
for (int px = x - dx; px <= x + dx; px++) {
point_callback(px, y + dy);
point_callback(px, y - dy);
}
mdx = dx;
break;
}
}
Algorithm explained
This algorithm performs a minute number of checks. Specifically, it only checks in each row until the first point that is part of the circle is reached. Furthermore, it will skip points to the left of the previously identified point in the next row. Additionally, by using symmetry, only half of the rows (n/2 + 1/2 as we start at 0) are checked.
This is a visualization of the algorithm I created. The red outline indicates the square that would have previously been checked and the black pixels indicate the real circle (with the red pixel in the middle being the center). The algorithm checks points (marked blue) and loops through valid points (marked green).
As you can see, the number of blue pixels at the end is minute, i.e. there are only a few points being looped over that are not part of the circle. Additionally, notice that only the first green pixel each time needs a check, the others are only looped through, which is why they appear instantly.
Notes
The axes could easily be reversed, obviously.
This could be optimized by taking advantage of symmetry even more, i.e. that the rows are going to be the same as the columns (going through all rows is the same as going through all columns, left to right, up to down, vise versa, vise vera) and going down only a quarter of the rows from the center would be enough to determine exactly what points are going to be part of the circle. However, I feel like the minor performance bump this is going to give is not worth the additional code.
If someone wants to code it out, propose an edit to this answer.
Code with comments
#include <functional>
// Executes point_callback for every point that is part of the circle
// defined by the center (x, y) and radius r.
void walk_circle(int x, int y, int r,
std::function<void(int x, int y)> point_callback) {
// Walk through the whole center line as it will always be completely
// part of the circle.
for (int px = x - r; px < x + r; px++)
point_callback(px, y);
// Define a maximum delta x that shrinks whith every row as the arc
// is closing.
int mdx = r;
// Start directly below the center row to make use of symmetry.
for (int dy = 1; dy <= r; dy++)
for (int dx = mdx; dx >= 0; dx--) {
// Check if the point is part of the circle using Euclidean distance.
if (dx * dx + dy * dy > r * r)
continue;
// If a point in a row left to the center is part of the circle,
// all points to the right of it until the center are going to be
// part of the circle as well.
// Then, we can use horizontal symmetry to move the same distance
// to the right from the center.
for (int px = x - dx; px <= x + dx; px++) {
// Use y - dy and y + dy thanks to vertical symmetry
point_callback(px, y + dy);
point_callback(px, y - dy);
}
// The next row will never have a point in the circle further left.
mdx = dx;
break;
}
}
The problem has a fixed complexity of O(n^2) where n is the radius of the circle. The same complexity as a square or any regular 2D shape
There is no getting past the fact that you can not reduce the number of pixels in a circle, even if you take advantage of the symmetry the complexity remains the same.
So ignoring complexity and looking for optimization.
In your question you state that the abs is a little too expensive per pixel (or 4th pixel)
Once per row is better than once per pixel
You can reduce it down to 1 square root per row. For a circle radius 256 that 128 square roots
void circle(int x, int y, int radius) {
int y1 = y, y2 = y + 1, r = 0, rSqr = radius * radius;
while (r < radius) {
int x1 = x, x2 = x + 1, right = x + sqrt(rSqr - r * r) + 1.5;;
while (x2 < right) {
pixel(x1, y1);
pixel(x2, y1);
pixel(x1--, y2);
pixel(x2++, y2);
}
y1--;
y2++;
r++;
}
}
To get more out of it you can crate a lookup table for the sqrt root calculations.
All integer
Alternatively you can use the variation on the bresenham line that replaces the square root with all integer math. However it is a mess and would not be of any benefit unless the device does not have a floating point unit.
void circle(int x, int y, int radius) {
int l, yy = 0, xx = radius - 1, dx = 1, dy = 1;
int err = dx - (radius << 1);
int l2 = x, y0 = y, r2 = x + 1;
int l1 = x - xx, r1 = r2 + xx;
int y2 = y0 - xx, y1 = y0 + 1, y3 = y1 + xx;
while (xx >= yy) {
l = l1;
while (l < r1) {
pixel(l, y1);
pixel(l++, y0);
}
l = l2;
while (l < r2) {
pixel(l, y3);
pixel(l++, y2);
}
err += dy;
dy += 2;
y0--;
yy++;
y1++;
l2--;
r2++;
if (err > 0) {
dx += 2;
err += (-radius << 1) + dx;
xx--;
r1--
l1++
y3--
y2++
}
}
}
You can draw a square that fits inside the circle and it is pretty straightforward to find if the point falls in.
This will solve most points(2 * r^2) in O(1) time, instead of searching all (4 * r^2) points.
Edit: For the rest of the points, you don't need to loop all the other pixels. You need to loop for the 4 rectangles sized with dimensions [(2r/sqrt(2)), r-(r/sqrt(2))] on the 4 sides(north,east,south,west) of the square that is inside. It means that you never have to search for the squares on the corners. Since, it is completely symmetric, we can take the absolute values of input points and search if the point is inside half-squares on the positive side of the coordinate plane. Which means we only loop once instead of 4.
int square_range = r/sqrt(2);
int abs_x = abs(x);
int abs_y = abs(y);
if(abs_x < square_range && abs_y < square_range){
//point is in
}
else if(abs_x < r && abs_y < r){ // if it falls in the outer square
// this is the only loop that has to be done
if(abs_x < abs_y){
int temp = abs_y;
abs_y = abs_x;
abs_x = temp;
}
for(int x = r/sqrt(2) ; x < r ; x++){
for(int y = 0 ; y < r/sqrt(2) ; y++){
if(x*x + y*y < r*r){
//point is in
}
}
}
}
Overall complexity of the code is O((r-r/sqrt(2))* (r/sqrt(2))). Which is only looping for a half of a single rectangle(8 way symmetry) that is in between the inner square and cirle's outer border.

Windows Console frame rate dropped after running program

As the title suggests I'm having an issue with my windows console application in c++. So far I've created a class, Console, to represent all of the functions I repeatedly use to construct a windows console.
I implemented a line drawing algorithm and had up to 500 fps when filling the screen with magenta characters and drawing a line in white.
However, I implemented a triangle drawing algorithm next (just three line drawing calls consecutively) and was surprised to find out that the frame rate dropped to about 20. I removed this code again but the bad frame rate persisted. I really don't know why this has happend because I've essentially ended up not changing anything.
For reference, here is the Console code (without header):
Console::Console(int width, int height):
m_handle(GetStdHandle(STD_OUTPUT_HANDLE)),
m_width(width),
m_height(height),
m_screen({ 0, 0, 1, 1 }),
m_title(L"Demo"),
m_buffer(new CHAR_INFO[(size_t)m_width * (size_t)m_height])
{
memset(m_buffer, 0, sizeof(CHAR_INFO) * m_width * m_height);
}
Console::~Console()
{
if (m_buffer)
delete[] m_buffer;
}
int Console::Construct(int char_w, int char_h)
{
if (m_handle == INVALID_HANDLE_VALUE)
return BAD_HANDLE;
if (!SetConsoleWindowInfo(m_handle, true, &m_screen))
return WINDOW_INFO_ERROR;
COORD screen_coord = { (short)m_width, (short)m_height };
if (!SetConsoleScreenBufferSize(m_handle, screen_coord))
return SCREEN_BUFFER_SIZE_ERROR;
CONSOLE_FONT_INFOEX cinfo;
cinfo.cbSize = sizeof(cinfo);
cinfo.dwFontSize.X = (short)char_w;
cinfo.dwFontSize.Y = (short)char_h;
cinfo.FontFamily = FF_DONTCARE;
cinfo.FontWeight = FW_NORMAL;
cinfo.nFont = 0;
wcscpy_s(cinfo.FaceName, L"Consolas");
if (!SetCurrentConsoleFontEx(m_handle, false, &cinfo))
return CONSOLE_FONT_ERROR;
CONSOLE_SCREEN_BUFFER_INFO cbuffinfo;
if (!GetConsoleScreenBufferInfo(m_handle, &cbuffinfo))
return GET_BUFFER_INFO_ERROR;
if (cbuffinfo.dwMaximumWindowSize.X < m_width)
return HORIZONTAL_SIZE_TOO_LARGE_ERROR;
if (cbuffinfo.dwMaximumWindowSize.Y < m_height)
return VERTICAL_SIZE_TOO_LARGE_ERROR;
m_screen = { 0, 0, (short)m_width - 1, (short)m_height - 1 };
if (!SetConsoleWindowInfo(m_handle, true, &m_screen))
return WINDOW_INFO_ERROR;
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)HandleClose, true))
return CLOSE_HANDLER_ERROR;
return OK;
}
void Console::Start()
{
active = true;
std::thread t(&Console::DoLoop, this);
t.join();
}
void Console::DoLoop()
{
if (!OnCreate())
active = false;
std::chrono::high_resolution_clock::time_point t1, t2;
t1 = std::chrono::high_resolution_clock::now();
t2 = t1;
while (active)
{
t2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> diff = t2 - t1;
t1 = t2;
float dt = diff.count();
if (!OnUpdate(dt))
active = false;
wchar_t title_buff[256];
swprintf_s(title_buff, L"Console Application - %s - FPS: %3.2f", m_title.c_str(), 1.0f / dt);
SetConsoleTitle(title_buff);
WriteConsoleOutput(m_handle, m_buffer, { (short)m_width, (short)m_height }, { 0, 0 }, &m_screen);
}
finished.notify_one();
}
void Console::Fill(int x, int y, short glyph, short color)
{
if (x < 0 || y < 0 || x >= m_width || y >= m_height) return;
CHAR_INFO& ci = m_buffer[y * m_width + x];
ci.Char.UnicodeChar = glyph;
ci.Attributes = color;
}
void Console::Fill(int x1, int y1, int x2, int y2, short glyph, short color)
{
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x1 >= m_width) return;
if (y1 >= m_height) return;
if (x2 >= m_width) x2 = m_width - 1;
if (y2 >= m_height) y2 = m_height - 1;
if (x2 < 0) return;
if (y2 < 0) return;
for (int x = x1; x <= x2; x++)
{
for (int y = y1; y <= y2; y++)
{
CHAR_INFO& ci = m_buffer[y * m_width + x];
ci.Char.UnicodeChar = glyph;
ci.Attributes = color;
}
}
}
void Console::Clear(short glyph, short color)
{
for (int x = 0; x < m_width; x++)
{
for (int y = 0; y < m_height; y++)
{
CHAR_INFO& ci = m_buffer[y * m_width + x];
ci.Char.UnicodeChar = glyph;
ci.Attributes = color;
}
}
}
void Console::Line(int x1, int y1, int x2, int y2, short glyph, short color)
{
int dx = x2 - x1;
int dy = y2 - y1;
int adx = dx > 0 ? dx : -dx;
int ady = dy > 0 ? dy : -dy;
int dy2 = dy + dy;
int dx2 = dx + dx;
int adx2 = dx2 > 0 ? dx2 : -dx2;
int ady2 = dy2 > 0 ? dy2 : -dy2;
if (adx > ady)
{
if (x1 > x2)
{
int x = x1;
x1 = x2;
x2 = x;
dx = -dx;
dx2 = -dx2;
int y = y1;
y1 = y2;
y2 = y;
dy = -dy;
dy2 = -dy2;
}
int sy = dy > 0 ? 1 : dy < 0 ? -1 : 0;
int err = ady;
for (int x = x1, y = y1; x <= x2; x++)
{
Fill(x, y, glyph, color);
err += ady2;
if (err > adx2)
{
err -= adx2;
y += sy;
}
}
}
else
{
if (y1 > y2)
{
int x = x1;
x1 = x2;
x2 = x;
dx = -dx;
dx2 = -dx2;
int y = y1;
y1 = y2;
y2 = y;
dy = -dy;
dy2 = -dy2;
}
int sx = dx > 0 ? 1 : dx < 0 ? -1 : 0;
int err = adx;
for (int x = x1, y = y1; y <= y2; y++)
{
Fill(x, y, glyph, color);
err += adx2;
if (err > ady2)
{
err -= ady2;
x += sx;
}
}
}
}
void Console::Triangle(int x1, int y1, int x2, int y2, int x3, int y3, short glyph, short color)
{
Line(x1, y1, x2, y2, glyph, color);
Line(x2, y2, x3, y3, glyph, color);
Line(x3, y3, x1, y1, glyph, color);
}
BOOL Console::HandleClose(DWORD evt)
{
if (evt == CTRL_CLOSE_EVENT)
{
active = false;
std::unique_lock<std::mutex> ul(lock);
finished.wait(ul);
}
return true;
}
Here is the very short main code:
class Game : public Console
{
public:
Game(int width, int height):
Console(width, height)
{
}
bool OnCreate() override
{
return true;
}
bool OnUpdate(float dt) override
{
Clear(GLYPH_SOLID, FG_BLACK);
//Triangle(20, 20, 300, 40, 150, 200);
return true;
}
};
int main()
{
Game game(400, 300);
int err = game.Construct(2, 2);
if (err == Console::OK)
game.Start();
}
The error constants are just defined integer codes in the header file.
If any additional code is needed please let me know. Thanks in advance!
EDIT:

Delaunay triangulation : too many triangles

I'm trying to implement the Delaunay triangulation in C++. Currently it's working, but I'm not getting the correct amount of triangles.
I try it with 4 points in a square pattern : (0,0), (1,0), (0,1), (1,1).
Here's the algorithm I use :
std::vector<Triangle> Delaunay::triangulate(std::vector<Vec2f> &vertices) {
// Determinate the super triangle
float minX = vertices[0].getX();
float minY = vertices[0].getY();
float maxX = minX;
float maxY = minY;
for(std::size_t i = 0; i < vertices.size(); ++i) {
if (vertices[i].getX() < minX) minX = vertices[i].getX();
if (vertices[i].getY() < minY) minY = vertices[i].getY();
if (vertices[i].getX() > maxX) maxX = vertices[i].getX();
if (vertices[i].getY() > maxY) maxY = vertices[i].getY();
}
float dx = maxX - minX;
float dy = maxY - minY;
float deltaMax = std::max(dx, dy);
float midx = (minX + maxX) / 2.f;
float midy = (minY + maxY) / 2.f;
Vec2f p1(midx - 20 * deltaMax, midy - deltaMax);
Vec2f p2(midx, midy + 20 * deltaMax);
Vec2f p3(midx + 20 * deltaMax, midy - deltaMax);
// Add the super triangle vertices to the end of the vertex list
vertices.push_back(p1);
vertices.push_back(p2);
vertices.push_back(p3);
// Add the super triangle to the triangle list
std::vector<Triangle> triangleList = {Triangle(p1, p2, p3)};
// For each point in the vertex list
for(auto point = begin(vertices); point != end(vertices); point++)
{
// Initialize the edges buffer
std::vector<Edge> edgesBuff;
// For each triangles currently in the triangle list
for(auto triangle = begin(triangleList); triangle != end(triangleList);)
{
if(triangle->inCircumCircle(*point))
{
Edge tmp[3] = {triangle->getE1(), triangle->getE2(), triangle->getE3()};
edgesBuff.insert(end(edgesBuff), tmp, tmp + 3);
triangle = triangleList.erase(triangle);
}
else
{
triangle++;
}
}
// Delete all doubly specified edges from the edge buffer
// Black magic by https://github.com/MechaRage
auto ite = begin(edgesBuff), last = end(edgesBuff);
while(ite != last) {
// Search for at least one duplicate of the current element
auto twin = std::find(ite + 1, last, *ite);
if(twin != last)
// If one is found, push them all to the end.
last = std::partition(ite, last, [&ite](auto const &o){ return !(o == *ite); });
else
++ite;
}
// Remove all the duplicates, which have been shoved past "last".
edgesBuff.erase(last, end(edgesBuff));
// Add the triangle to the list
for(auto edge = begin(edgesBuff); edge != end(edgesBuff); edge++)
triangleList.push_back(Triangle(edge->getP1(), edge->getP2(), *point));
}
// Remove any triangles from the triangle list that use the supertriangle vertices
triangleList.erase(std::remove_if(begin(triangleList), end(triangleList), [p1, p2, p3](auto t){
return t.containsVertex(p1) || t.containsVertex(p2) || t.containsVertex(p3);
}), end(triangleList));
return triangleList;
}
And here's what I obtain :
Triangle:
Point x: 1 y: 0
Point x: 0 y: 0
Point x: 1 y: 1
Triangle:
Point x: 1 y: 0
Point x: 1 y: 1
Point x: 0 y: 1
Triangle:
Point x: 0 y: 0
Point x: 1 y: 1
Point x: 0 y: 1
While this would be the correct output :
Triangle:
Point x: 1 y: 0
Point x: 0 y: 0
Point x: 0 y: 1
Triangle:
Point x: 1 y: 0
Point x: 1 y: 1
Point x: 0 y: 1
I have no idea why there is a triangle with the (0, 0) and the (1, 1).
I need an outside eye to review the code and find out what's going wrong.
All the sources are on my Github repo. Feel free to fork it and to PR your code.
Thanks!
what about this implementation of Paul Bourke's Delaunay triangulation algorithm. Take a look at Triangulate() I have used this source many times without any complains
#include <iostream>
#include <stdlib.h> // for C qsort
#include <cmath>
#include <time.h> // for random
const int MaxVertices = 500;
const int MaxTriangles = 1000;
//const int n_MaxPoints = 10; // for the test programm
const double EPSILON = 0.000001;
struct ITRIANGLE{
int p1, p2, p3;
};
struct IEDGE{
int p1, p2;
};
struct XYZ{
double x, y, z;
};
int XYZCompare(const void *v1, const void *v2);
int Triangulate(int nv, XYZ pxyz[], ITRIANGLE v[], int &ntri);
int CircumCircle(double, double, double, double, double, double, double, double, double&, double&, double&);
using namespace std;
////////////////////////////////////////////////////////////////////////
// CircumCircle() :
// Return true if a point (xp,yp) is inside the circumcircle made up
// of the points (x1,y1), (x2,y2), (x3,y3)
// The circumcircle centre is returned in (xc,yc) and the radius r
// Note : A point on the edge is inside the circumcircle
////////////////////////////////////////////////////////////////////////
int CircumCircle(double xp, double yp, double x1, double y1, double x2,
double y2, double x3, double y3, double &xc, double &yc, double &r){
double m1, m2, mx1, mx2, my1, my2;
double dx, dy, rsqr, drsqr;
/* Check for coincident points */
if(abs(y1 - y2) < EPSILON && abs(y2 - y3) < EPSILON)
return(false);
if(abs(y2-y1) < EPSILON){
m2 = - (x3 - x2) / (y3 - y2);
mx2 = (x2 + x3) / 2.0;
my2 = (y2 + y3) / 2.0;
xc = (x2 + x1) / 2.0;
yc = m2 * (xc - mx2) + my2;
}else if(abs(y3 - y2) < EPSILON){
m1 = - (x2 - x1) / (y2 - y1);
mx1 = (x1 + x2) / 2.0;
my1 = (y1 + y2) / 2.0;
xc = (x3 + x2) / 2.0;
yc = m1 * (xc - mx1) + my1;
}else{
m1 = - (x2 - x1) / (y2 - y1);
m2 = - (x3 - x2) / (y3 - y2);
mx1 = (x1 + x2) / 2.0;
mx2 = (x2 + x3) / 2.0;
my1 = (y1 + y2) / 2.0;
my2 = (y2 + y3) / 2.0;
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
yc = m1 * (xc - mx1) + my1;
}
dx = x2 - xc;
dy = y2 - yc;
rsqr = dx * dx + dy * dy;
r = sqrt(rsqr);
dx = xp - xc;
dy = yp - yc;
drsqr = dx * dx + dy * dy;
return((drsqr <= rsqr) ? true : false);
}
///////////////////////////////////////////////////////////////////////////////
// Triangulate() :
// Triangulation subroutine
// Takes as input NV vertices in array pxyz
// Returned is a list of ntri triangular faces in the array v
// These triangles are arranged in a consistent clockwise order.
// The triangle array 'v' should be malloced to 3 * nv
// The vertex array pxyz must be big enough to hold 3 more points
// The vertex array must be sorted in increasing x values say
//
// qsort(p,nv,sizeof(XYZ),XYZCompare);
///////////////////////////////////////////////////////////////////////////////
int Triangulate(int nv, XYZ pxyz[], ITRIANGLE v[], int &ntri){
int *complete = NULL;
IEDGE *edges = NULL;
IEDGE *p_EdgeTemp;
int nedge = 0;
int trimax, emax = 200;
int status = 0;
int inside;
int i, j, k;
double xp, yp, x1, y1, x2, y2, x3, y3, xc, yc, r;
double xmin, xmax, ymin, ymax, xmid, ymid;
double dx, dy, dmax;
/* Allocate memory for the completeness list, flag for each triangle */
trimax = 4 * nv;
complete = new int[trimax];
/* Allocate memory for the edge list */
edges = new IEDGE[emax];
/*
Find the maximum and minimum vertex bounds.
This is to allow calculation of the bounding triangle
*/
xmin = pxyz[0].x;
ymin = pxyz[0].y;
xmax = xmin;
ymax = ymin;
for(i = 1; i < nv; i++){
if (pxyz[i].x < xmin) xmin = pxyz[i].x;
if (pxyz[i].x > xmax) xmax = pxyz[i].x;
if (pxyz[i].y < ymin) ymin = pxyz[i].y;
if (pxyz[i].y > ymax) ymax = pxyz[i].y;
}
dx = xmax - xmin;
dy = ymax - ymin;
dmax = (dx > dy) ? dx : dy;
xmid = (xmax + xmin) / 2.0;
ymid = (ymax + ymin) / 2.0;
/*
Set up the supertriangle
his is a triangle which encompasses all the sample points.
The supertriangle coordinates are added to the end of the
vertex list. The supertriangle is the first triangle in
the triangle list.
*/
pxyz[nv+0].x = xmid - 20 * dmax;
pxyz[nv+0].y = ymid - dmax;
pxyz[nv+1].x = xmid;
pxyz[nv+1].y = ymid + 20 * dmax;
pxyz[nv+2].x = xmid + 20 * dmax;
pxyz[nv+2].y = ymid - dmax;
v[0].p1 = nv;
v[0].p2 = nv+1;
v[0].p3 = nv+2;
complete[0] = false;
ntri = 1;
/*
Include each point one at a time into the existing mesh
*/
for(i = 0; i < nv; i++){
xp = pxyz[i].x;
yp = pxyz[i].y;
nedge = 0;
/*
Set up the edge buffer.
If the point (xp,yp) lies inside the circumcircle then the
three edges of that triangle are added to the edge buffer
and that triangle is removed.
*/
for(j = 0; j < ntri; j++){
if(complete[j])
continue;
x1 = pxyz[v[j].p1].x;
y1 = pxyz[v[j].p1].y;
x2 = pxyz[v[j].p2].x;
y2 = pxyz[v[j].p2].y;
x3 = pxyz[v[j].p3].x;
y3 = pxyz[v[j].p3].y;
inside = CircumCircle(xp, yp, x1, y1, x2, y2, x3, y3, xc, yc, r);
if (xc + r < xp)
// Suggested
// if (xc + r + EPSILON < xp)
complete[j] = true;
if(inside){
/* Check that we haven't exceeded the edge list size */
if(nedge + 3 >= emax){
emax += 100;
p_EdgeTemp = new IEDGE[emax];
for (int i = 0; i < nedge; i++) { // Fix by John Bowman
p_EdgeTemp[i] = edges[i];
}
delete []edges;
edges = p_EdgeTemp;
}
edges[nedge+0].p1 = v[j].p1;
edges[nedge+0].p2 = v[j].p2;
edges[nedge+1].p1 = v[j].p2;
edges[nedge+1].p2 = v[j].p3;
edges[nedge+2].p1 = v[j].p3;
edges[nedge+2].p2 = v[j].p1;
nedge += 3;
v[j] = v[ntri-1];
complete[j] = complete[ntri-1];
ntri--;
j--;
}
}
/*
Tag multiple edges
Note: if all triangles are specified anticlockwise then all
interior edges are opposite pointing in direction.
*/
for(j = 0; j < nedge - 1; j++){
for(k = j + 1; k < nedge; k++){
if((edges[j].p1 == edges[k].p2) && (edges[j].p2 == edges[k].p1)){
edges[j].p1 = -1;
edges[j].p2 = -1;
edges[k].p1 = -1;
edges[k].p2 = -1;
}
/* Shouldn't need the following, see note above */
if((edges[j].p1 == edges[k].p1) && (edges[j].p2 == edges[k].p2)){
edges[j].p1 = -1;
edges[j].p2 = -1;
edges[k].p1 = -1;
edges[k].p2 = -1;
}
}
}
/*
Form new triangles for the current point
Skipping over any tagged edges.
All edges are arranged in clockwise order.
*/
for(j = 0; j < nedge; j++) {
if(edges[j].p1 < 0 || edges[j].p2 < 0)
continue;
v[ntri].p1 = edges[j].p1;
v[ntri].p2 = edges[j].p2;
v[ntri].p3 = i;
complete[ntri] = false;
ntri++;
}
}
/*
Remove triangles with supertriangle vertices
These are triangles which have a vertex number greater than nv
*/
for(i = 0; i < ntri; i++) {
if(v[i].p1 >= nv || v[i].p2 >= nv || v[i].p3 >= nv) {
v[i] = v[ntri-1];
ntri--;
i--;
}
}
delete[] edges;
delete[] complete;
return 0;
}
int XYZCompare(const void *v1, const void *v2){
XYZ *p1, *p2;
p1 = (XYZ*)v1;
p2 = (XYZ*)v2;
if(p1->x < p2->x)
return(-1);
else if(p1->x > p2->x)
return(1);
else
return(0);
}
I didn't go with a debugger, but from the resulting triangles it seems that this is an accuracy/ambiguity problem.
When you are triangulating a square there are two ways to split it into triangles and both are OK from Delaunay criteria (circumscribed circle center is on border of triangle).
So if you evaluate every triangle independently you may sometimes get even 4 triangles (depending on implementation).
Normally in such cases I recommend to build algorithm as a series of questions which cannot produce contradicting answers. In this case the question is "to which point goes triangle based on edge (1,0)-(1,1)". But often this requires significant changes to the algorithm.
A quick fix usually involves adding some tolerances for comparisons and extra checks (like non-intersecting triangles). But usually it just makes problems rarer.
Most likely you didn't delete all the double edges, especially not the edges from same triangles but with vertices only in another order. The correct function is in the answer from #cMinor.

My Gravitational Pull Algorithm behaves Oddly in Certain Situations

While trying to create my own physics engine (don't try persuading me not to), I decided to create a class for each pixel, called Particle, this system has an x and a y, and a x and y velocity, as shown below. Unfortunately, the code for calculateGravitationalVelocity doesn't abide by the laws of physics in certain situations. For example, if the x of the particle and the x of the other particle is the same, the particle will fall towards the object realistically, but when the particle gets too close, it pings off towards the positive x. I am only going to include the class source code, but I can include the source code of the other file, though it is partly written in SFML
Particle.cpp:
#include <iostream>
#include <string>
#include <math.h>
class Particle
{
private:
//Coords:
double x, y;
//Velocities:
double xVelocity = 0;
double yVelocity = 0;
//Material:
std::string material = "Generic";
//Mass:
double mass = 0;
public:
//Coords:
void setCoords(double, double);
float getCoords(char);
//Velocities:
void giveVelocity(char, float);
void setVelocity(char, float);
float getVelocity(char);
//Gravitational Velocity:
void calculateGravitationalVelocity(Particle);
//Material:
void setMaterial(std::string);
std::string getMaterial();
//Mass:
void setMass(double);
double getMass();
//Update:
void update();
};
//Coords:
void Particle::setCoords(double newX, double newY)
{
x = newX;
y = newY;
}
float Particle::getCoords(char axis)
{
if (axis == 'x')
{
//return floor(x);
return x;
}
else if (axis == 'y')
{
//return floor(y);
return y;
}
}
//Velocities:
void Particle::giveVelocity(char axis, float addedVelocity)
{
if (axis == 'x') {xVelocity = xVelocity + addedVelocity;}
else if (axis == 'y') {yVelocity = yVelocity + addedVelocity;}
}
void Particle::setVelocity(char axis, float newVelocity)
{
if (axis == 'x') {xVelocity = newVelocity;}
else if (axis == 'y') {yVelocity = newVelocity;}
}
float Particle::getVelocity(char axis)
{
if (axis == 'x') {return xVelocity;}//floor(xVelocity);}
else if (axis == 'y') {return xVelocity;}//floor(yVelocity);}
}
//Gravitational Velocity (Where the problems probably are):
void Particle::calculateGravitationalVelocity(Particle distantParticle)
{
//Physics constants:
const double pi = 3.14159265359; //Pi
const double G = 0.00000000006673; //Gravitational Constant (or Big G)
//Big Triangle Trigonometry:
//Get coords of moving particle:
double x1 = x;
double y1 = y;
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
if (x1 != x2)
{
//Work out the angle:
double A = atan((y2 - y1) / (x2 - x1)) * 180 / pi;
//Remove the minus sign:
A = fabs(A);
//Small Triangle Trigonometry:
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
//For testing purposes:
//std::cout << "X: " << (cos(A) * gravitationalField) / 1000 << std::endl;
//std::cout << "Y: " << (sin(A) * gravitationalField) / 1000 << std::endl;
//Work out the X velocity:
xVelocity = xVelocity + (cos(A) * gravitationalField) / 1000;
//Work out the Y velocity:
yVelocity = yVelocity + (sin(A) * gravitationalField) / 1000;
}
else
{
//Work out the hypotenuse of the big triangle:
double hyp = sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));
//Work out the gravitational field (hyp of small triangle):
long double gravitationalField = G * (distantParticle.getMass() / pow(hyp, 2));
yVelocity = yVelocity + gravitationalField / 1000;
}
}
//Material:
void Particle::setMaterial(std::string newMaterialType)
{
material = newMaterialType;
}
std::string Particle::getMaterial()
{
return material;
}
//Mass:
void Particle::setMass(double newMass)
{
mass = newMass;
}
double Particle::getMass()
{
return mass;
}
//Update:
void Particle::update()
{
x = x + xVelocity;
y = y + yVelocity;
}
I am sorry for the very open question, and it probably goes against the rules somewhere, but I couldn't find it. The code for working out mostly uses a two triangles to make a x and y velocity. Here is an image of what I was hoping the code would do as a triangle (sorry it doesn't look great, but I like using a whiteboard):
You don't need to perform any trigonometric calculation.
...
//Get coords of particle with gravity:
double x2 = distantParticle.getCoords('x');
double y2 = distantParticle.getCoords('y');
// Get difference vector
double rx = x1 - x2;
double ry = y1 - y2;
// square of distance
double r2 = rx * rx + ry * ry;
// distance
double r = sqrt (r2);
if (r != 0) {
// normalize difference vector
double ux = rx / r;
double uy = ry / r;
// acceleration of gravity
double a = - G * distantParticle.getMass() / r2;
xVelocity += a * ux / 1000;
yVelocity += a * uy / 1000;
}
}

Draw a line with a constant length by mouse direction

As question says, I want to draw a line starting at X,Y position to, for example, 10 pixels on mouse direction... The function I already have draws a line between 2 points, but I can't figure how to do it with a constant lenght on mouse direction
Here is the function:
void D3DGraphics::DrawLine( int x1,int y1,int x2,int y2,int r,int g,int blu )
{
int dx = x2 - x1;
int dy = y2 - y1;
if( dy == 0 && dx == 0 )
{
PutPixel( x1,y1,r,g,blu );
}
else if( abs( dy ) > abs( dx ) )
{
if( dy < 0 )
{
int temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
float m = (float)dx / (float)dy;
float b = x1 - m*y1;
for( int y = y1; y <= y2; y = y + 1 )
{
int x = (int)(m*y + b + 0.5f);
PutPixel( x,y,r,g,blu );
}
}
else
{
if( dx < 0 )
{
int temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
float m = (float)dy / (float)dx;
float b = y1 - m*x1;
for( int x = x1; x <= x2; x = x + 1 )
{
int y = (int)(m*x + b + 0.5f);
PutPixel( x,y,r,g,blu );
}
}
}
I also have a function that gets the mouse X and Y position on the screen (getmouseX(), getmouseY())
Direct3D has D3DXVECTOR2, D3DXVECTOR3, D3DXCOLOR and similar structures. You should probably use them. Or use typedef D3DXVECTOR2 Vec2; or something like that. Those structures come with mathematical functions, so using them makes sense. Oh, and they operate on floats.
Drawing one pixel at a time is a bad theme - it is slow. Also you can easily draw entire line in one call using IDirect3DDevice9->DrawPrimitive(D3DPT_LINELIST..)
Even if you don't want to use D3DX* structures, you should probably use strctures to store colors and coordinates:
Example:
struct Coord{
int x, y;
};
struct Color{
unsigned char a, b, g, r;
};
regarding your question.
typedef D3DXVECTOR2 Vec2;
....
Vec2 startPos = ...;
Vec2 endPos = getMousePos();
const float desiredLength = ...;//whatever you need here.
Vec2 diff = endPos - startPos; //should work. If it doesn't, use
//D3DXVec2Subtract(&diff, &endPos, &startPoss);
float currentLength = D3DXVec2Length(&diff);
if (currentLength != 0)
D3DXVec2Scale(&diff, &diff, desiredLength/currentLength);// diff *= desiredLength/currentLength
else
diff = Vec2(0.0f, 0.0f);
endPos = startPos + diff; //if it doesn't work, use D3DXVec2Add(&endPos, &startPos, &diff);
This way endPos will not be further than desiredLength than startPos. Unless startPos == endPos
P.S. If you REALLY want to draw the line yourself, you may want to research bresenham line drawing algorithm.
ratio = (length betweeen start position and mouse position)/10
x = startX+(mouseX-startX)/ratio
y = startY+(mouseY-startY)/ratio
I think its something like this