Graphical editor programming challenge solution time limit exceeded c++ [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Below is my solution to the graphical editor programming challenge, detailed here(which has taken me 3 days to solve). While the output is correct, It keeps producing a time_limit_exceeded error. I think that the problem is in the way I've implemented the flood_fill function which gets called when the user enters "F" as the first letter of a line. I would appreciate it if someone explained what is so inefficient about my code and how i could improve it. My code is below:
// graphical_editor.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream> //provides access to cout and cin
#include <string> //Always import <string> if piping std input to a string in .net
#include <vector>
#include <fstream>
using std::ofstream;
using std::cin;
using std::cout;
using std::string;
using std::vector;
//This is where we store the pixels of the image
static vector<vector <string>> image_array;
ofstream myfile;
//our definition of an X,Y coordinate pair.
typedef struct point {
int x_coordinate, y_coordinate;
};
void initialise_image();
void clear_image();
void save_image(string file_name);
int get_image_width();
int get_image_height();
void color_pixel(int x, int y, string color);
void color_point(point p, string color);
void color_vertical_line(int x, int y1, int y2, string color);
void color_horizontal_line(int x1, int x2, int y, string color);
void color_box(int x1, int x2, int y1, int y2, string color);
void flood_fill(point p, string color);
vector<point> get_matching_neighbours(point p, string color);
int main()
{
string command; //first letter of a given line
myfile.open("example.txt");
while (cin >> command) {
//application terminates when command is X
if (command.compare("X") == 0) {
return 0;
} else if (command.compare("I") == 0) {
initialise_image();
}
else if (command.compare("S") == 0) {
string file_name;
cin >> file_name;
save_image(file_name);
}
else if (command.compare("L") == 0) {
string color;
point p;
cin >> p.x_coordinate >> p.y_coordinate >> color;
color_point(p, color);
}
else if (command.compare("V") == 0) {
string color;
int x, y1, y2;
cin >> x >> y1 >> y2 >> color;
color_vertical_line(x, y1, y2, color);
}
else if (command.compare("H") == 0) {
string color;
int x1, x2, y;
cin >> x1 >> x2 >> y >> color;
color_horizontal_line(x1, x2, y, color);
}
else if (command.compare("K") == 0) {
string color;
int x1, x2, y1, y2;
cin >> x1 >> x2 >> y1 >> y2 >> color;
color_box(x1, x2, y1, y2, color);
}
else if (command.compare("F") == 0) {
string color;
point p;
cin >> p.x_coordinate >> p.y_coordinate >> color;
flood_fill(p, color);
}
else if (command.compare("C") == 0) {
clear_image();
}
}
return 0;
}
void initialise_image()
{
/*read parameters height and width*/
int width, height;
cin >> width >> height;
/*first we create a vector of vectors (numRows+1)x(numColumns matrix+1). */
image_array.clear();
for (int i = 0; i < width+ 1; i++) {
image_array.push_back(vector<string>());
}
/*then we initialize each element of it one by one*/
for (int colNo = 0; colNo < width + 1; colNo++) {
for (int rowNo = 0; rowNo < height + 1; rowNo++) {
image_array[colNo].push_back("O");
}
}
}
void clear_image() {
/*we initialize each element of it one by one*/
for (int y = 1; y < get_image_height()+1 ; y++) {
for (int x = 1; x < get_image_width()+1; x++) {
image_array[x][y] = "O";
}
}
}
void save_image(string file_name) {
myfile << file_name << "\n";
//cout << file_name << "\n";
for (int y = 1; y < get_image_height()+1; y++) {
for (int x = 1; x < get_image_width()+1; x++) {
myfile << image_array[x][y];
//cout << image_array[x][y];
}
myfile << "\n";
//cout << "\n";
}
myfile.close();
}
int get_image_width() {
return image_array.size()-1;
}
int get_image_height() {
return image_array[0].size()-1;
}
void color_point(point p, string color) {
color_pixel(p.x_coordinate,p.y_coordinate, color);
}
void color_pixel(int x, int y, string color) {
image_array[x][y] = color;
}
void color_vertical_line(int x, int y1, int y2, string color) {
for (int y = y1; y <= y2; y++) {
color_pixel(x, y, color);
}
}
void color_horizontal_line(int x1, int x2, int y, string color) {
for (int x = x1; x <= x2; x++) {
color_pixel(x, y, color);
}
}
void color_box(int x1, int x2, int y1, int y2, string color) {
for (int x = x1; x <= x2; x++) {
for (int y = y1; y <= y2; y++) {
color_pixel(x, y, color);
}
}
}
string get_point_color(point p) {
return image_array[p.x_coordinate][p.y_coordinate];
}
void flood_fill(point p, string color) {
vector <point> points_queue;
points_queue.push_back(p);
string original_color = get_point_color(p);
point current_point;
while (points_queue.size() > 0) {
current_point = points_queue[0];
//if the point shares a color with the original point then color it in the new color.
if (get_point_color(current_point).compare(original_color) == 0) {
color_point(current_point, color);
}
// remove current point from the queue
points_queue.erase(points_queue.begin());
// add it's neighbours to the queue
vector<point> matching_neighbours = get_matching_neighbours(current_point, original_color);
for (int i = 0; i < matching_neighbours.size(); i++) {
points_queue.push_back(matching_neighbours[i]);
}
}
}
bool is_valid_point(point p) {
if (p.x_coordinate >= 1 && p.x_coordinate < get_image_width() + 1 && p.y_coordinate >= 1 && p.y_coordinate < get_image_height() + 1) {
return true;
}
else {
return false;
}
}
vector<point> get_matching_neighbours(point p, string color) {
vector<point> neighbours;
point left_neighbour, right_neighbour, upper_neighbour, lower_neighbour;
left_neighbour.x_coordinate = p.x_coordinate - 1;
left_neighbour.y_coordinate = p.y_coordinate;
if (is_valid_point(left_neighbour) && get_point_color(left_neighbour).compare(color) == 0) {
neighbours.push_back(left_neighbour);
}
right_neighbour.x_coordinate = p.x_coordinate + 1;
right_neighbour.y_coordinate = p.y_coordinate;
if (is_valid_point(right_neighbour) && get_point_color(right_neighbour).compare(color) == 0) {
neighbours.push_back(right_neighbour);
}
upper_neighbour.x_coordinate = p.x_coordinate;
upper_neighbour.y_coordinate = p.y_coordinate + 1;
if (is_valid_point(upper_neighbour) && get_point_color(upper_neighbour).compare(color) == 0) {
neighbours.push_back(upper_neighbour);
}
lower_neighbour.x_coordinate = p.x_coordinate;
lower_neighbour.y_coordinate = p.y_coordinate - 1;
if (is_valid_point(lower_neighbour) && get_point_color(lower_neighbour).compare(color) == 0) {
neighbours.push_back(lower_neighbour);
}
return neighbours;
}

There are a couple of really bad performance problems with your flood fill.
These are the 2 biggest ones:
1) You are using a vector as a queue. That is terribly slow, since it takes O(N) time to remove an item from the start of a vector. Use a deque instead, or use the vector like a stack instead of a queue by taking items from the end instead of the start.
2) You enqueue everything that get_matching_neighbors returns, but it can return things that area already in the queue. Because of this you can end up scanning and enqueing the same pixel many times.
To fix problem (2), you should should:
a) return from the flood fill immediately if the target pixel is already the right color. Then
b) color pixels when you put them into the queue, not when you take them out. That way you won't enque anything that's already in the queue, because nothing in the queue has the original color.
It is also pretty expensive to have get_matching_neighbors allocating a new vector. You should pass it a reference to an existing vector and have it replace the contents, or pass it a reference to the queue and have it recolor and add the pixels it finds.

Some overall optimization tips you could apply to your code:
So first, the specification says that color is 1 Latin char... so why are you using string? Strings are slow, especially when you know you're going to receive one char.
Second - when you're using vectors and know that you'll be pushing a lot of elements (and you know the amount prior), then reserve() the actual size of the vector, and the number of elements you're going to add.
When you're appending vector to a vector just reserve enough space and then
insert(first.begin(), second.begin(), second.end());
Not to mention that I would actually advice not to use vectors... they're easy to use, but often are slower than using an array. So if the algorithm itself isn't very complicated - use arrays. Also keep in mind that c-style arrays are faster than pointers (vector operates on pointers).
Third - always use constructors when you can, use pre-incrementation/decrementation operators, and always iterate from n to 0 when you can (generates about 2 less asm instructions)for(int i=0; i<n; i++) -> for(int i(n); i>0; --i)
Fourth - If you have if()...else if()...else if() chain consider using switch(). In most cases the compiler makes a lookup table for both of them. But I believe that it's guaranteed to do that for switch, but not for if chain. In short - switch() is most likely to be faster than if
Fifth - If you must use vectors, and have to fill them with some values, either use <algorithm> and iterators, or use C++11 and vector::data() that comes with it to get the pointer to the allocated array, and iterate over it.
If you didn't understand "how" or "why", just do some research. If you already did and still don't understand then I can't help you more - I won't optimize the code for you. That way you wouldn't learn anything.
Also, when given optimizations seem not to optimize enough, consider thinking about new algorithm.

Related

Terminate called after throwing an instance of 'std::invalid_argument' what(): stof

As I was writing the in the other post, I am trying to implement the K-means algorithm in C++. When debugging, I get no errors, but when trying to run the program, I get the error I mentioned in the title:
Terminate called after throwing an instance of 'std:invalid_argument' what():stof
I know that this error comes when the file can't convert to float. Then, what I wanted to ask is, is there something that I should change in my code, or would it be better to use another file format as input, instead of a *.csv file ? (obviosuly, I'm making the hypothesis that the program can't read something from the file. I don't know if that's the right reasoning though.)
Thank you everyone!
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
//Inizializzare il punto
struct Point {
double x, y; // Coordinate del punto
int cluster; // Cluster di default
double minDist; // Distanza minima
Point()
: x(0.0)
, y(0.0)
, cluster(-1)
, minDist(__DBL_MAX__)
{
}
Point(double x, double y)
: x(x)
, y(y)
, cluster(-1)
, minDist(__DBL_MAX__)
{
}
double distance(Point p)
{
return (p.x - x) * (p.x - x) + (p.y - y) * (p.y - y);
}
};
vector<Point> readcsv()
{
vector<Point> points;
string line;
ifstream file("Mall_Customers.csv");
while (getline(file, line)) {
stringstream lineStream(line);
string bit;
double x, y;
getline(lineStream, bit, ',');
x = stof(bit);
getline(lineStream, bit, '\n');
y = stof(bit);
points.push_back(Point(x, y));
}
return points;
}
vector<Point> points = readcsv();
void kMeansClustering(vector<Point>* points, int epochs, int k)
{
int n = points->size();
vector<Point> centroids;
srand(time(0));
for (int i = 0; i < k; ++i) {
centroids.push_back(points->at(rand() % n));
}
for (vector<Point>::iterator c = begin(centroids); c != end(centroids); ++c) {
int clusterId = c - begin(centroids);
{
for (vector<Point>::iterator it = points->begin(); it != points->end(); ++it) {
Point p = *it;
double dist = c->distance(p);
if (dist < p.minDist) {
p.minDist = dist;
p.cluster = clusterId;
}
*it = p;
}
}
}
vector<int> nPoints;
vector<double> sumX, sumY;
for (int j = 0; j < k; j++) {
nPoints.push_back(0.0);
sumX.push_back(0.0);
sumY.push_back(0.0);
}
for (vector<Point>::iterator it = points->begin(); it != points->end(); ++it) {
int clusterId = it->cluster;
nPoints[clusterId] += 1;
sumX[clusterId] += it->x;
sumY[clusterId] += it->y;
it->minDist = __DBL_MAX__; // reset distance
}
// Compute the new centroids
for (vector<Point>::iterator c = begin(centroids); c != end(centroids); ++c) {
int clusterId = c - begin(centroids);
c->x = sumX[clusterId] / nPoints[clusterId];
c->y = sumY[clusterId] / nPoints[clusterId];
}
// Write to csv
ofstream myfile;
myfile.open("output.csv");
myfile << "x,y,c" << endl;
for (vector<Point>::iterator it = points->begin(); it != points->end();
++it) {
myfile << it->x << "," << it->y << "," << it->cluster << endl;
}
myfile.close();
}
int main()
{
vector<Point> points = readcsv();
// Run k-means with 100 iterations and for 5 clusters
kMeansClustering(&points, 100, 5);
}
It seems to work now, thank you everyone for the help you gave me, in particular #Yksisarvinen and #molbdnilo : the problem was in the fact that I blindly imported all the csv file in the program, but I was supposed to import only two columns: one was the income, the other one was the spending column.
Although now I have to figure why it shows me the final result, not in the form of "bubbles",as one would expect from the algorithm,but in the form of points being clustered on a straight line.
When trying to open the original file, the original dataset, I get the error shown in the image below.

hostel visit question (priority queue application )

question: Dean of MAIT is going to visit Hostels of MAIT. As you know that he is a very busy person so he decided to visit only the first "K" nearest Hostels. Hostels are situated on a 2D plane. You are given the coordinates of hostels and you have to answer the Rocket distance of Kth nearest hostel from the origin ( Dean's place )
Input Format
The first line of input contains Q Total no. of queries and K There are two types of queries:
first type: 1 x y For query of 1st type, you came to know about the coordinates ( x, y ) of the newly constructed hostel. second type: 2 For query of 2nd type, you have to output the Rocket distance of Kth nearest hostel till now.
//The Dean will always stay at his place ( origin ). It is guaranteed that there will be at least k queries of type 1 before the first query of type 2.
Rocket distance between two points ( x2 , y2 ) and ( x1 , y1 ) is defined as (x2 - x1)2 + (y2 - y1)2
Constraints
1 < = k < = Q < = 10^5 -10^6 < = x , y < = 10^6
Output Format
For each query of type 2 output the Rocket distance of Kth nearest hostel from Origin.//
This is my code:
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
class roomno
{
public:
int x;
int y;
roomno(int x,int y)
{
this->x=x;
this->y=y;
}
void print()
{
cout<<"location"<<"("<<x<<","<<y<<")"<<endl;
}
int distance ()
{
return (x*x+y*y);
}
};
class roomcompare
{
public:
bool operator() (roomno r1,roomno r2)
{
return r1.distance()>r2.distance();
}
};
int main()
{
int x[1000]},y[1000];
int l,k=0;
priority_queue<roomno,vector<roomno>,roomcompare> pq;
int n,i,j;
cin>>n>>l;
//cin>>n;
cin.ignore();
for( i=0;i<n;i++)
{
cin>>x[i];
}
cin.ignore();
for( j=0;j<n;j++)
{
cin>>y[j];
}
cin.ignore();
for(i=0;i<n;i++ )
{
roomno r1(x[i],y[i]);
pq.push(r1);
}
while(!pq.empty()&&k!=l)
{ k++;
roomno r2=pq.top();
r2.print();
pq.pop();
}
return 0;
}
Original link to code: https://codeshare.io/2j1bkA
What's wrong with my code?
Please consider adding what problem you are facing in your post. I've seen your code but I couldn't help with your code as I even saw a syntax error in your code and didn't know if it was the problem.
If I didn't misunderstand your question, std::set will fit better than priority queue as it is not possible to remove the largest item in a priority queue(min heap). The following code should work:
#include <iostream>
#include <set>
using namespace std;
using ll = long long;
multiset<ll> distances;
int main() {
ll n, k;
cin >> n >> k;
for(ll i = 0; i < n; ++i) {
ll query;
cin >> query;
if (query == 1) {
ll x, y;
cin >> x >> y;
distances.insert(x * x + y * y);
if (distances.size() > k) {
distances.erase(--distances.end());
}
} else {
cout << *distances.rbegin() << '\n';
}
}
return 0;
}

In a recursive function, how can I jump to a different function call on the stack?

To make my question more clear, I'll give an example. Say I want to implement a recursive function that sums up the range of numbers from [1-10] and [15-20], skipping (10-15). I would want to add up [15-20] and skip the function calls on stack of (10-15), and continue with [1-10]. How can I do this?
int summation(int x, int y) {
if(x == y) return x;
// Catch here and return sum of 15-20 back to calls of 1-10
if(x == 10)
catch(int n)
return n;
int sum = x + summation(x+1,y);
// Skip function calls 10-15
if(x==15) throw sum;
return sum;
}
summation(1,20) // >> 160 or [1-10] = 55, [15,20] = 105
I know how to solve the above example with a different approach, but this example gives an idea of what I'm trying to do.
In order to set the try/catch in the right stack frame, you need to know the edge of the skip interval before you recurse deeper. Given that, it will be better to just never make the useless function calls instead of using an exception to unwind them. For example:
int summation(int const x, int const y)
{
if(x == y) return x;
int const next = (x==10)? 15: (x+1); // skip from 10 to 15 directly
return x + summation(next, y);
}
Avoiding exceptions also gives you the possibility to write the function tail-recursively:
int summation(int const x, int const y, int partial_sum = 0)
{
partial_sum += x;
if(x == y) return partial_sum;
int const next = (x==10)? 15: (x+1); // skip from 10 to 15 directly
return summation(next, y, partial_sum);
}
Keep the function simple.
int summation(int x, int y) {
if(x == y) return x;
return x + summation(x+1,y);
}
and use
summation(1, 10) + summation(15, 20);
on the client side.
You can make the client side a little bit simpler by adding another function that takes care of the numbers to skip.
int summation_with_skip(int x1, int x2, int x3, int x4) {
return summation(x1, x2) + summation(x3, x4);
}
and use
summation_with_skip(1, 10, 15, 20);
If you must have the logic to skip items in the function, you can use
int summation_with_skip(int x1, int x2, int x3, int x4)
{
if ( x1 > x4 )
{
return 0;
}
int s = summation(x1+1, x2, x3, x4)
if ( (x1 > x2) && (x1 < x3) )
{
return s;
}
else
{
return x1 + s;
}
}
I like the idea of passing all arguments to the function.
well, this would be a solution without throw&catch
still an odd solution for the given problem
int summation(int x, int y)
{
if(x > y) return 0;
if ((x >= 10) && (x <= 15))
{
return summation(x+1, y);
}
else
{
return x + summation(x+1, y);
}
}

Find and list all char clusters in the string C++

I'm looking for some help finding all clusters of chars in the string in C++. The exact task is:
Given the following “2D string” (in C++ expression):
string text =
"#################aa##a###c######\n" +
"####bbbbaaaabbbbbaaaaa###ccc##cc\n" +
"#o##bbbbaaaabbbbbaaaaa###c#c##cc\n" +
"#oo#bbbbaeeabbbbbbbbaa##cc#ccccc\n" +
"#o##bbbbaeeabbbbbaaaaaa#cc#####c\n" +
"#o##bbbbaaaabbbbbaaaaaa#cc#####c\n";
Write a program computing the area of each contiguous region of the same >symbols. Two equal symbols belong to the same area if they are neighbors either >in a row or in a column. Don’t count the newline (\n) symbols, they are just to >form the 2D string.
The main function should be recursive.
Hint: use an extra 2D array to mark each symbol in the 2D string if it is >already counted or not. Scan the array row-wise until a not counted yet symbol >is found. Then, run the recursive area-computing function starting from this >symbol. Continue until all symbols are marked as counted.
The program output should look (more or less) like:
Region of symbols #, area …
Region of symbols a, area …
Region of symbols #, area …
Region of symbols c, area …
My current code looks like this:
#include <iostream>
#include <string>
using namespace std;
int cords (string str, int x, int y) {
int length, i, position, lines = 0, x_max, y_max;
char symbol;
length = str.length();
for (i = 0; i < length; i++) {
symbol = str[i];
if (symbol == '\n')
lines++;
}
length -= lines;
x_max = length / lines;
y_max = length / x_max;
position = x - 1 + (y - 1) * x_max + y - 1;
if (x <= x_max && y <= y_max)
return position;
}
int clusterMiner (char symbol, string str, int x, int y, int counter, int last) {
if (x > 32 || y > 6) {
return counter;
} else {
if (str[cords(str, x++, y)] == symbol) {
counter++;
return clusterMiner(symbol, str, x++, y, counter, x);
} else if (str[cords(str, 1, y++)] == symbol) {
return clusterMiner(symbol, str, 1, y++, counter, x);
}
}
}
int main () {
int length, lines, i, j, k, l, counter;
string text = // 32 elements per line
"#################aa##a###c######\n" // 32
"####bbbbaaaabbbbbaaaaa###ccc##cc\n" // 64
"#o##bbbbaaaabbbbbaaaaa###c#c##cc\n" // 96
"#oo#bbbbaeeabbbbbbbbaa##cc#ccccc\n" // 128
"#o##bbbbaeeabbbbbaaaaaa#cc#####c\n" // 160
"#o##bbbbaaaabbbbbaaaaaa#cc#####c\n"; // 192
counter = clusterMiner('#', text, 1, 1, 0, 0);
cout << counter;
return 0;
}
Cords function is just for easier interaction with the two dimensions of the string.
I'm not sure what to do next. Right now the program counts only some of the symbols as it stops at the first different one ignoring these which are connected to further nodes.
Thanks!
First, do not calculate x_max and y_max all the time newly, just do it once and store it in a variable. Then, you will have to iterate over the whole field:
char get(int x, int y)
{
// + 1: the newline!!!
return field[x + y * (x_max + 1)];
}
void countAll()
{
calculateMaxima();
// created your visited array now
for(unsigned int y = 0; y <= y_max; ++y)
{
for(int x = 0; x <= x_max; ++x)
{
if(!visited[x, y])
{
count = 0;
search(get(x, y), x, y);
// output count here...
}
}
}
}
Each time we hit a character not yet visited, i. e. a new one, we start a new search. For each search, we have to consider four neighbours for each current position {x, y}:{x +/- 1, y} and {x, y +/- (x_max + 1} (apart from the positions at the edges, which have less). So your search might look like this:
void visit(char symbol, int x, int y)
{
if(!visited[x][y] && get(x, y) == symbol)
{
++count;
++visited[x][y] = true;
}
search(symbol, x, y);
}
void search(char symbol, int x, int y)
{
if(x > 0)
visit(x - 1, y);
if(x < max_x)
visit(x + 1, y);
if(y > 0)
visit(x, y - 1);
if(y < max_y)
visit(x, y + 1);
}
For now, I am assuming count, visited and x/y_max being some global variables. Cleaner, as we are C++, would be writing a separate class for this purpose:
class ClusterMiner
{
unsigned int count;
std::string field;
// ...
void visit(char symbol, int x, int y);
void search(char symbol, int x, int y);
// ...
public:
void countAll();
};
Code is untested and incomplete, it shall only give you the necessary hints to find your way...
Side note: If you have unconnected regions of the same character, these will be detected as such. If this is not desired, you might sum up the results e. g. in a std::map<char, unsigned int> and iterate over this one after you finished counting...

finding the edge connection point between two AABB area

Lets say I have two AABB based areas, each area defined by two coordinates mins{x, y} and maxs{x, y}, I want to find the middle connection point between them.
Since my english is not good, I can't explain all with my words,
see the following picture for easier understanding:
http://i.*.com/WokivEe.png
All I need to find is the red point coordinates.
so If we move this into programming question, actual data structures would look like this:
struct Vec2D {
float x, y;
}
struct Rectangle {
Vec2D min;
Vec2D max;
}
Rectangle obj[2]
Anyone got an idea for an algorithm?
Along either the X or Y axis, sort the coordinates of the sides that touch into order. Then average the 2nd and 3rd ones in that list to find their midpoint. I hope this answers the question sufficiently.
Here is a little algorithm that first find which sides of the objects are closest, and then uses the 4 points along the common side to make a list, sorted along the common axis. The average of the 2 middle points of the sorted list are the answer. This will work for both horizontal and vertical sides. I added accessor functions to the data structures so that they can be indexed; e.g., for a Vec2D, coordinate(0) is the x value and coordinate(1) is the y value.
#include <math.h>
#include <iostream>
#include <limits>
struct Vec2D {
float x, y;
float coordinate(int axis)
{
return (axis & 1) ? y : x;
}
};
struct Rectangle {
Vec2D min;
Vec2D max;
Vec2D corner(int j)
{
return (j & 1) ? max : min;
}
// Get the other corner along the given axis
Vec2D along(int j, int ax)
{
Vec2D p = corner(j);
if (0 == ax)
{
p.x = corner(1-j).x;
}
else
{
p.y = corner(1-j).y;
}
return p;
}
};
using namespace std;
inline Vec2D* vp(const void* p)
{
return (Vec2D*) p;
}
static int compare_x(const void*a, const void*b)
{
if (vp(a)->x < vp(b)->x)
{
return -1;
}
else
if (vp(a)->x > vp(b)->x)
{
return 1;
}
return 0;
}
static int compare_y(const void*a, const void*b)
{
if (vp(a)->y < vp(b)->y)
{
return -1;
}
else
if (vp(a)->y > vp(b)->y)
{
return 1;
}
return 0;
}
int main(void) {
int ax; // axis index
int c0, c1;
float gap = numeric_limits<float>::max();
struct Rectangle obj[2] = {0,2,10,10,10,5,15,20};
struct
{
int ax,c0,c1;
} closest;
// Find out which sides are the closest to each other
for(ax = 0; 2 > ax; ++ax) // Look at x axis and y axis
{
for(c0 = 0; 2 > c0; ++c0) // Look at both corners of obj[0]
{
for(c1 = 0; 2 > c1; ++c1) // Look at both corners of obj[1]
{
float dist = fabs(obj[0].corner(c0).coordinate(ax) - obj[1].corner(c1).coordinate(ax));
if (dist < gap)
{
gap = dist;
closest.ax = ax;
closest.c0 = c0;
closest.c1 = c1;
}
}
}
}
int other = 1 - closest.ax; // The other axis
cout << "The closest gap is along the " << (closest.ax ? 'y' : 'x') << " axis\n";
cout << "The common side is along the " << (other ? 'y' : 'x') << " direction\n";
// Make a list of the 4 points along the common side
Vec2D list[4];
list[0] = obj[0].corner(closest.c0);
list[1] = obj[0].along(closest.c0, other);
list[2] = obj[1].corner(closest.c1);
list[3] = obj[1].along(closest.c1, other);
// Sort them into order along the common axis
qsort(list, 4, sizeof(Vec2D), closest.ax ? compare_x : compare_y);
// Get the average of the 2 middle points along the common axis.
Vec2D answer = {
(list[1].x + list[2].x) / 2,
(list[1].y + list[2].y) / 2
};
cout << "(" << answer.x << "," << answer.y << ")\n";
}