C++ R-Tree Library - Do I have to compute the bouding boxes? - c++

I'm trying to use the following library here (the templated version) but in the example shown in the library the user defines the bounding boxes. In my problem I have data of unknown dimensionality each time, so I don't know how to use it. Apart from this, shouldn't the R-Tree be able to calculate the bounding boxes each time there is an insertion?
This is the sample code of the library, as you can see the user defines the bounding boxes each time:
#include <stdio.h>
#include "RTree.h"
struct Rect
{
Rect() {}
Rect(int a_minX, int a_minY, int a_maxX, int a_maxY)
{
min[0] = a_minX;
min[1] = a_minY;
max[0] = a_maxX;
max[1] = a_maxY;
}
int min[2];
int max[2];
};
struct Rect rects[] =
{
Rect(0, 0, 2, 2), // xmin, ymin, xmax, ymax (for 2 dimensional RTree)
Rect(5, 5, 7, 7),
Rect(8, 5, 9, 6),
Rect(7, 1, 9, 2),
};
int nrects = sizeof(rects) / sizeof(rects[0]);
Rect search_rect(6, 4, 10, 6); // search will find above rects that this one overlaps
bool MySearchCallback(int id, void* arg)
{
printf("Hit data rect %d\n", id);
return true; // keep going
}
void main()
{
RTree<int, int, 2, float> tree;
int i, nhits;
printf("nrects = %d\n", nrects);
for(i=0; i<nrects; i++)
{
tree.Insert(rects[i].min, rects[i].max, i); // Note, all values including zero are fine in this version
}
nhits = tree.Search(search_rect.min, search_rect.max, MySearchCallback, NULL);
printf("Search resulted in %d hits\n", nhits);
// Iterator test
int itIndex = 0;
RTree<int, int, 2, float>::Iterator it;
for( tree.GetFirst(it);
!tree.IsNull(it);
tree.GetNext(it) )
{
int value = tree.GetAt(it);
int boundsMin[2] = {0,0};
int boundsMax[2] = {0,0};
it.GetBounds(boundsMin, boundsMax);
printf("it[%d] %d = (%d,%d,%d,%d)\n", itIndex++, value, boundsMin[0], boundsMin[1], boundsMax[0], boundsMax[1]);
}
// Iterator test, alternate syntax
itIndex = 0;
tree.GetFirst(it);
while( !it.IsNull() )
{
int value = *it;
++it;
printf("it[%d] %d\n", itIndex++, value);
}
getchar(); // Wait for keypress on exit so we can read console output
}
An example of what I want to save in an R-Tree is:
-------------------------------
| ID | dimension1 | dimension2|
-------------------------------
| 1 | 8 | 9 |
| 2 | 3 | 5 |
| 3 | 2 | 1 |
| 4 | 6 | 7 |
-------------------------------

Dimensionality
There will be some limit in your requirements to the dimensionality. This is because computers only have infinite storage so cannot store an infinite number of dimensions. Really it is a decision for you how many dimensions you wish to support. The most common numbers of course are two and three. Do you actually need to support eleven? When are you going to use it?
You can do this either by always using an R-tree with the maximum number you support, and passing zero as the other coordinates, or preferably you would create several code paths, one for each supported number of dimensions. I.e. you would have one set of routines for two-dimensional data and another for three dimensional, and so on.
Calculating the bounding box
The bounding box is the rectangle or cuboid which is aligned to the axes, and completely surrounds the object you wish to add.
So if you are inserting axis-aligned rectangles/cuboids etc, then the shape is the bounding box.
If you are inserting points, the min and max of each dimension are just the point value of that dimension.
Any other shape, you have to calculate the bounding box. E.g. if you are inserting a triangle, you need to calculate the rectangle which completely surrounds the triangle as the bounding box.
The library can't do this for you because it doesn't know what you are inserting. You might be inserting spheres stored as centre + radius, or complex triangle mesh shapes. The R-Tree can provide the spatial index but needs you to provide that little bit of information to fill in the gaps.

Related

How can I get the vertices of all cells in a container with Voro++ Library?

I try to implement a simple 3d voronoi application with Voro++. I have a container containing particles. After putting all the particles in the container, how can I get the vertices of all voronoicells computed by Voro++.
from the documentation it should be something like this:
http://math.lbl.gov/voro++/examples/polygons/
the doc says:
On line 47, a call is made to the face_vertices routine, which returns information about which vertices comprise each face. It is a vector of integers with a specific format: the first entry is a number k corresponding to the number of vertices making up a face, and this is followed k additional entries describing which vertices make up this face. For example, the sequence (3, 16, 20, 13) would correspond to a triangular face linking vertices 16, 20, and 13 together. On line 48, the vertex positions are returned: this corresponds to a vector of triplets (x, y, z) describing the position of each vertex.
i modified the example script so that it stores every particles cell connections and vertex positions in vectors. but please verify this!
hope this helps!
#include "voro++.hh"
#include "container.hh"
#include <v_compute.hh>
#include <c_loops.hh>
#include <vector>
#include <iostream>
using namespace voro;
int main() {
// Set up constants for the container geometry
const double x_min=-5,x_max=5;
const double y_min=-5,y_max=5;
const double z_min=0,z_max=10;
unsigned int i,j;
int id,nx,ny,nz;
double x,y,z;
std::vector<int> neigh;
voronoicell_neighbor c;
// Set up the number of blocks that the container is divided into
const int n_x=6,n_y=6,n_z=6;
// Create a container with the geometry given above, and make it
// non-periodic in each of the three coordinates. Allocate space for
// eight particles within each computational block
container con(x_min,x_max,y_min,y_max,z_min,z_max,n_x,n_y,n_z,
false,false,false,8);
//Randomly add particles into the container
con.import("pack_six_cube");
// Save the Voronoi network of all the particles to text files
// in gnuplot and POV-Ray formats
con.draw_cells_gnuplot("pack_ten_cube.gnu");
con.draw_cells_pov("pack_ten_cube_v.pov");
// Output the particles in POV-Ray format
con.draw_particles_pov("pack_ten_cube_p.pov");
// Loop over all particles in the container and compute each Voronoi
// cell
c_loop_all cl(con);
int dimension = 0;
if(cl.start()) do if(con.compute_cell(c,cl)) {
dimension+=1;
} while (cl.inc());
std::vector<std::vector<int> > face_connections(dimension);
std::vector<std::vector<double> > vertex_positions(dimension);
int counter = 0;
if(cl.start()) do if(con.compute_cell(c,cl)) {
cl.pos(x,y,z);id=cl.pid();
std::vector<int> f_vert;
std::vector<double> v;
// Gather information about the computed Voronoi cell
c.neighbors(neigh);
c.face_vertices(f_vert);
c.vertices(x,y,z,v);
face_connections[counter] = f_vert;
vertex_positions[counter] = v;
std::cout << f_vert.size() << std::endl;
std::cout << v.size() << std::endl;
counter += 1;
} while (cl.inc());
}
Regards, Lukas

Tallest tower with stacked boxes in the given order

Given N boxes. How can i find the tallest tower made with them in the given order ? (Given order means that the first box must be at the base of the tower and so on). All boxes must be used to make a valid tower.
It is possible to rotate the box on any axis in a way that any of its 6 faces gets parallel to the ground, however the perimeter of such face must be completely restrained inside the perimeter of the superior face of the box below it. In the case of the first box it is possible to choose any face, because the ground is big enough.
To solve this problem i've tried the following:
- Firstly the code generates the rotations for each rectangle (just a permutation of the dimensions)
- secondly constructing a dynamic programming solution for each box and each possible rotation
- finally search for the highest tower made (in the dp table)
But my algorithm is taking wrong answer in unknown test cases. What is wrong with it ? Dynamic programming is the best approach to solve this problem ?
Here is my code:
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <cstring>
struct rectangle{
int coords[3];
rectangle(){ coords[0] = coords[1] = coords[2] = 0; }
rectangle(int a, int b, int c){coords[0] = a; coords[1] = b; coords[2] = c; }
};
bool canStack(rectangle &current_rectangle, rectangle &last_rectangle){
for (int i = 0; i < 2; ++i)
if(current_rectangle.coords[i] > last_rectangle.coords[i])
return false;
return true;
}
//six is the number of rotations for each rectangle
int dp(std::vector< std::vector<rectangle> > &v){
int memoization[6][v.size()];
memset(memoization, -1, sizeof(memoization));
//all rotations of the first rectangle can be used
for (int i = 0; i < 6; ++i) {
memoization[i][0] = v[0][i].coords[2];
}
//for each rectangle
for (int i = 1; i < v.size(); ++i) {
//for each possible permutation of the current rectangle
for (int j = 0; j < 6; ++j) {
//for each permutation of the previous rectangle
for (int k = 0; k < 6; ++k) {
rectangle &prev = v[i - 1][k];
rectangle &curr = v[i][j];
//is possible to put the current rectangle with the previous rectangle ?
if( canStack(curr, prev) ) {
memoization[j][i] = std::max(memoization[j][i], curr.coords[2] + memoization[k][i-1]);
}
}
}
}
//what is the best solution ?
int ret = -1;
for (int i = 0; i < 6; ++i) {
ret = std::max(memoization[i][v.size()-1], ret);
}
return ret;
}
int main ( void ) {
int n;
scanf("%d", &n);
std::vector< std::vector<rectangle> > v(n);
for (int i = 0; i < n; ++i) {
rectangle r;
scanf("%d %d %d", &r.coords[0], &r.coords[1], &r.coords[2]);
//generate all rotations with the given rectangle (all combinations of the coordinates)
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
if(j != k) //micro optimization disease
for (int l = 0; l < 3; ++l)
if(l != j && l != k)
v[i].push_back( rectangle(r.coords[j], r.coords[k], r.coords[l]) );
}
printf("%d\n", dp(v));
}
Input Description
A test case starts with an integer N, representing the number of boxes (1 ≤ N ≤ 10^5).
Following there will be N rows, each containing three integers, A, B and C, representing the dimensions of the boxes (1 ≤ A, B, C ≤ 10^4).
Output Description
Print one row containing one integer, representing the maximum height of the stack if it’s possible to pile all the N boxes, or -1 otherwise.
Sample Input
2
5 2 2
1 3 4
Sample Output
6
Sample image for the given input and output.
Usually you're given the test case that made you fail. Otherwise, finding the problem is a lot harder.
You can always approach it from a different angle! I'm going to leave out the boring parts that are easily replicated.
struct Box { unsigned int dim[3]; };
Box will store the dimensions of each... box. When it comes time to read the dimensions, it needs to be sorted so that dim[0] >= dim[1] >= dim[2].
The idea is to loop and read the next box each iteration. It then compares the second largest dimension of the new box with the second largest dimension of the last box, and same with the third largest. If in either case the newer box is larger, it adjusts the older box to compare the first largest and third largest dimension. If that fails too, then the first and second largest. This way, it always prefers using a larger dimension as the vertical one.
If it had to rotate a box, it goes to the next box down and checks that the rotation doesn't need to be adjusted there too. It continues until there are no more boxes or it didn't need to rotate the next box. If at any time, all three rotations for a box failed to make it large enough, it stops because there is no solution.
Once all the boxes are in place, it just sums up each one's vertical dimension.
int main()
{
unsigned int size; //num boxes
std::cin >> size;
std::vector<Box> boxes(size); //all boxes
std::vector<unsigned char> pos(size, 0); //index of vertical dimension
//gets the index of dimension that isn't vertical
//largest indicates if it should pick the larger or smaller one
auto get = [](unsigned char x, bool largest) { if (largest) return x == 0 ? 1 : 0; return x == 2 ? 1 : 2; };
//check will compare the dimensions of two boxes and return true if the smaller one is under the larger one
auto check = [&boxes, &pos, &get](unsigned int x, bool largest) { return boxes[x - 1].dim[get(pos[x - 1], largest)] < boxes[x].dim[get(pos[x], largest)]; };
unsigned int x = 0, y; //indexing variables
unsigned char change; //detects box rotation change
bool fail = false; //if it cannot be solved
for (x = 0; x < size && !fail; ++x)
{
//read in the next three dimensions
//make sure dim[0] >= dim[1] >= dim[2]
//simple enough to write
//mine was too ugly and I didn't want to be embarrassed
y = x;
while (y && !fail) //when y == 0, no more boxes to check
{
change = pos[y - 1];
while (check(y, true) || check(y, false)) //while invalid rotation
{
if (++pos[y - 1] == 3) //rotate, when pos == 3, no solution
{
fail = true;
break;
}
}
if (change != pos[y - 1]) //if rotated box
--y;
else
break;
}
}
if (fail)
{
std::cout << -1;
}
else
{
unsigned long long max = 0;
for (x = 0; x < size; ++x)
max += boxes[x].dim[pos[x]];
std::cout << max;
}
return 0;
}
It works for the test cases I've written, but given that I don't know what caused yours to fail, I can't tell you what mine does differently (assuming it also doesn't fail your test conditions).
If you are allowed, this problem might benefit from a tree data structure.
First, define the three possible cases of block:
1) Cube - there is only one possible option for orientation, since every orientation results in the same height (applied toward total height) and the same footprint (applied to the restriction that the footprint of each block is completely contained by the block below it).
2) Square Rectangle - there are three possible orientations for this rectangle with two equal dimensions (for examples, a 4x4x1 or a 4x4x7 would both fit this).
3) All Different Dimensions - there are six possible orientations for this shape, where each side is different from the rest.
For the first box, choose how many orientations its shape allows, and create corresponding nodes at the first level (a root node with zero height will allow using simple binary trees, rather than requiring a more complicated type of tree that allows multiple elements within each node). Then, for each orientation, choose how many orientations the next box allows but only create nodes for those that are valid for the given orientation of the current box. If no orientations are possible given the orientation of the current box, remove that entire unique branch of orientations (the first parent node with multiple valid orientations will have one orientation removed by this pruning, but that parent node and all of its ancestors will be preserved otherwise).
By doing this, you can check for sets of boxes that have no solution by checking whether there are any elements below the root node, since an empty tree indicates that all possible orientations have been pruned away by invalid combinations.
If the tree is not empty, then just walk the tree to find the highest sum of heights within each branch of the tree, recursively up the tree to the root - the sum value is your maximum height, such as the following pseudocode:
std::size_t maximum_height() const{
if(leftnode == nullptr || rightnode == nullptr)
return this_node_box_height;
else{
auto leftheight = leftnode->maximum_height() + this_node_box_height;
auto rightheight = rightnode->maximum_height() + this_node_box_height;
if(leftheight >= rightheight)
return leftheight;
else
return rightheight;
}
}
The benefits of using a tree data structure are
1) You will greatly reduce the number of possible combinations you have to store and check, because in a tree, the invalid orientations will be eliminated at the earliest possible point - for example, using your 2x2x5 first box, with three possible orientations (as a Square Rectangle), only two orientations are possible because there is no possible way to orient it on its 2x2 end and still fit the 4x3x1 block on it. If on average only two orientations are possible for each block, you will need a much smaller number of nodes than if you compute every possible orientation and then filter them as a second step.
2) Detecting sets of blocks where there is no solution is much easier, because the data structure will only contain valid combinations.
3) Working with the finished tree will be much easier - for example, to find the sequence of orientations of the highest, rather than just the actual height, you could pass an empty std::vector to a modified highest() implementation, and let it append the actual orientation of each highest node as it walks the tree, in addition to returning the height.

Boolean operations on 2d rectangles

I have written my own rectangle class and it includes a method to subtract one rectangle from another. The algorithm simply determines which edge the source rectangle is overlapping on the destination rectangle and then chugs through all possible cases, including being completely inside, just on the edge, completely enclosing and so on. In fact there are so many cases I'm looking at the code and wondering if there are algorithms or examples of boolean operations on rectangles already available.
I know there are generalised clipping algorithms for 2d polytopes but I was looking for something specific to 2d rectangles, with the appropriate concomitant optimisations and simplifications.
Can anyone point me in the right direction, or is Weiler-Atherton the last word on this general class of problem of which the rectangle is just a single case?
If you separate the two directions, you have only a few base cases, which you can then combine in a nested loop.
The base cases are sketched below:
| |
XXXXX |..............| 1 section
| |
XXXXXXX...........| 2 sections
| |
|...XXXXXXX....| 3 sections
| |
|..........XXXXXXXX 2 secions
| |
|..............| XXXX 1 section
| |
XXXXXXXXXXXXXXXXXXXX nothing
| |
The vertical bars are the edges of the original rectangle, the X is the rectangle that is to be subtracted, the dots mark sections. X between the vertical bars are also sections that are kept, except when combined with an X section of the other direction. (If that sounds too complicated: The hole left behind is designated by the X section in both directions.
We can separate the directions by redesigning the rectangles properties left, top, right and bottom into arrays of min/max values:
typedef struct Rect Rect;
struct Rect {
int min[2];
int max[2];
};
(The code is C, not C++, I'm afraid.)
Then we can find the sections for each direction:
int rect_sub_dir(int sec[], int *skip, Rect a, Rect b, int dir)
{
int n = 0;
sec[n++] = a.min[dir];
if (b.min[dir] > a.min[dir] && b.min[dir] < a.max[dir]) {
sec[n++] = b.min[dir];
}
*skip = n - 1;
if (b.max[dir] < a.max[dir] && b.max[dir] > a.min[dir]) {
sec[n++] = b.max[dir];
}
sec[n] = a.max[dir];
// Backpatch if rectangles don't overlap
if (b.max[dir] < a.min[dir]) *skip = -1;
if (b.min[dir] > a.max[dir]) *skip = -1;
return n;
}
This creates an array of n + 1 boundaries, between n sections. The skip value denotes a section marked X between vertical bars.
You can then combine the sections of the two directions:
int rect_sub(Rect res[], Rect a, Rect b)
{
int hor[4];
int ver[4];
int hskip, nhor;
int vskip, nver;
int h, v;
int n = 0;
nhor = rect_sub_dir(hor, &hskip, a, b, 0);
nver = rect_sub_dir(ver, &vskip, a, b, 1);
printf("%d, %d\n", hskip, vskip);
for (h = 0; h < nhor; h++) {
for (v = 0; v < nver; v++) {
if (h == hskip && v == vskip) continue;
res[n++] = rect(hor[h], ver[v], hor[h + 1], ver[v + 1]);
}
}
return n;
}
This solution is not optimal. It will create eight rectangles when the second rectangle is contained in the first one, which may not be what you are looking for. You could always try to merge adjacent rectangles afterwards. Or you could rewrite the code to split the rectangles more intelligently.
I have tested the code with some cases, but because there are many possible arrangements, the code is not fully tested.

How to input data into an array from an Independant function

I am currently working on a project. I need write a program that computes the perimeter and volume of 3 boxes. The height, width and depth of the boxes are 10, 20, 30, 40, 50, 60, 70, 80, 90. The first 3 elements represent the height, width and depth of the first box, respectively speaking. The second group of three represent the second box and the last three represent the last box (height, width, depth).
Now I need to put the 9 given values in array, which I have done. Then i need to use 2 independent functions to calculate the volume and perimeter and I need to use a loop to repeat the calculations for all 3 boxes. once the functions calculate the perimeter and volume, the 6 values (3 perimeters and 3 volumes) need to be placed in array, then displayed.
I initialized an Array and stored the 9 values in the code. I created two independant functions that will compute the perimeter and the volume. I used a loop so so that the perimeter and the volume will be computed for all three boxes. Now I am having trouble figuring out how to store the computed values into an array?
Here is my code:
#include<iostream>
using namespace std;
struct myArrayWrapper
{
int m_array[9];//Array for the 9 given values
int n_array[6];//Array for the 6 values we will be computing
};
int perimeter(myArrayWrapper a)//function to calculate the perimiter
{
int p;
int* A = a.m_array;
int* B = a.n_array;
for(int b = 0 && int a = 1 && int s = 0; a < 8 && b < 9; a+=3 && b+=3 && s+=2) {//for loop to calculate the perimeter of the 3 boxes
p = 2*A[a] + 2*A[b];
}
}
int volume(myArrayWrapper a)// function to calculate the volume
{
int v;
int* B = a.m_array;//pointer
for(int c = 0 && int d = 3 && int e = 6; c < 3; c+=3 && d+=3 && e+=3){
int v;
v = B[c]*B[d]*B[e];
}
}
int main()
{
myArrayWrapper obj;
obj.m_array[0] = 10;//height of box 1
obj.m_array[1] = 40;//height of box 2
obj.m_array[2] = 70;//height of box 3
obj.m_array[3] = 20;//width of box 1
obj.m_array[4] = 50;//width of box 2
obj.m_array[5] = 80;//width of box 3
obj.m_array[6] = 30;//depth of box 1
obj.m_array[7] = 60;//depth of box 2
obj.m_array[8] = 90;//depth of box 3
for(int x = 0; x < 8; x++){//Loop that checks to make sure that the given dimensions are greater than 0
if(obj.m_array[x]>0)
cout << "Element number " << x << "is greater than 0" << endl;
else
cout << "The element is not greater than 0" << endl;
return 0;
}
perimeter(obj);
volume(obj);
}
What it sounds like you need to use is the return statement. This will allow your functions to actually return the value that they are calculating, so your perimeter function would look more like this:
int perimeter(myArrayWrapper a)//function to calculate the perimiter
{
int p;
/* your code */
p = 2*A[a] + 2*A[b];
return p;
}
This will return the integer value calculated for p, then in your main loop you can assigne the returned value to your wanted location in your array.
There is more information on the return statement here.
One other thing that may give you trouble that I noticed was that your return statement in your main function is going to be called on the first iteration of your for loop. When a return statement is called within a function, the function will actually stop running there and return that value, meaning your main function is stopping before it actually reaches the calls to your perimeter and volume functions.

How to determine Scale of Line Graph based on Pixels/Height?

I have a problem due to my terrible math abilities, that I cannot figure out how to scale a graph based on the maximum and minimum values so that the whole graph will fit onto the graph-area (400x420) without parts of it being off the screen (based on a given equation by user).
Let's say I have this code, and it automatically draws squares and then the line graph based on these values. What is the formula (what do I multiply) to scale it so that it fits into the small graphing area?
vector<int> m_x;
vector<int> m_y; // gets automatically filled by user equation or values
int HeightInPixels = 420;// Graphing area size!!
int WidthInPixels = 400;
int best_max_y = GetMaxOfVector(m_y);
int best_min_y = GetMinOfVector(m_y);
m_row = 0;
m_col = 0;
y_magnitude = (HeightInPixels/(best_max_y+best_min_y)); // probably won't work
x_magnitude = (WidthInPixels/(int)m_x.size());
m_col = m_row = best_max_y; // number of vertical/horizontal lines to draw
////x_magnitude = (WidthInPixels/(int)m_x.size())/2; Doesn't work well
////y_magnitude = (HeightInPixels/(int)m_y.size())/2; Doesn't work well
ready = true; // we have values, graph it
Invalidate(); // uses WM_PAINT
////////////////////////////////////////////
/// Construction of Graph layout on WM_PAINT, before painting line graph
///////////////////////////////////////////
CPen pSilver(PS_SOLID, 1, RGB(150, 150, 150) ); // silver
CPen pDarkSilver(PS_SOLID, 2, RGB(120, 120, 120) ); // dark silver
dc.SelectObject( pSilver ); // silver color
CPoint pt( 620, 620 ); // origin
int left_side = 310;
int top_side = 30;
int bottom_side = 450;
int right_side = 710; // create a rectangle border
dc.Rectangle(left_side,top_side,right_side,bottom_side);
int origin = 310;
int xshift = 30;
int yshift = 30;
// draw scaled rows and columns
for(int r = 1; r <= colrow; r++){ // draw rows
pt.x = left_side;
pt.y = (ymagnitude)*r+top_side;
dc.MoveTo( pt );
pt.x = right_side;
dc.LineTo( pt );
for(int c = 1; c <= colrow; c++){
pt.x = left_side+c*(magnitude);
pt.y = top_side;
dc.MoveTo(pt);
pt.y = bottom_side;
dc.LineTo(pt);
} // draw columns
}
// grab the center of the graph on x and y dimension
int top_center = ((right_side-left_side)/2)+left_side;
int bottom_center = ((bottom_side-top_side)/2)+top_side;
You are using ax^2 + bx + c (quadratic equation). You will get list of (X,Y) values inserted by user.
Let us say 5 points you get are
(1,1)
(2,4)
(4,1)
(5,6)
(6,7)
So, here your best_max_y will be 7 and best_min_y will be 1.
Now you have total graph area is
Dx = right_side - left_side //here, 400 (710 - 310)
Dy = bottom_side - top_side //here, 420 (450 - 30)
So, you can calculate x_magnitude and y_magnitude using following equation :
x_magnitude = WidthInPixels / Dx;
y_magnitude = HeightInPixels / Dy;
What I did was to determine how many points I had going in the x and y directions, and then divide that by the x and y dimensions, then divide that by 3, as I wanted each minimum point to be three pixels, so it could be seen.
The trick then is that you have to aggregate the data so that you are showing several points with one point, so it may be the average of them, but that depends on what you are displaying.
Without knowing more about what you are doing it is hard to make a suggestion.
For this part, subtract, don't add:
best_max_y+best_min_y as you want the difference.
The only other thing would be to divide y_magnitude and x_magnitude by 3. That was an arbitrary number I came up with, just so the users could see the points, you may find some other number to work better.