How to Initialize a Point Array? - c++

I am writing some Win32 program. and I meet a problem.
I define an array of Point,just like this:
POINT points[3];
and now I want to Initialize it, and I know this is illegal
POINT points[3] = { (295,295),(200,200),(400,500) };
so I need the correct way.

You can do it simply as
POINT points[3] = { 295, 295, 200, 200, 400, 500 };
but a safer thing to do would be this
POINT points[3] = { { 295, 295 }, { 200, 200 }, { 400, 500 } };
The amusing part is that what you originally wrote is not illegal (where did you get that idea?). The () you used inside your initializer will cause the inner , to be interpreted as comma operator. For example, expression (400, 500) evaluates to 500. That means that your original initializer is actually treated as
POINT points[3] = { 295, 200, 500 };
which is in turn equivalent to
POINT points[3] = { { 295, 200 }, { 500, 0 }, { 0, 0 } };
It doesn't do what you want it to do, but it is certainly not illegal.

As per the comments:
POINT points[] = {{295,295}, {200,200}, {400,500}};

Related

Dissapearing sf::Text after transform applying

I'm trying to apply a transformation matrix to sf::Text class, and when I change its size, the sf::Text just disappears.
I had the same problem with a sf::Sprite, but there I'd solved it by rewriting the whole class. Furthermore, I know where's the problem. It is in the sf::Transformable part and the scale SFML politic that the size elements in a transform matrix have to be scale coefficients. I don't like it because I want to work with my objects using math.
Anyway, like in the sf::Sprite, this problem is in the vertex array generating inside sf::Text class, but I cannot reimplement it because our sf::Text class is a friend for a sf::Texture class 😖
Someone... Help...
P.S. If you need more information about this problem, please, continue to read.
So. Here's a detailed problem description.
I have some system, which read JSON like that and make some scene stuff:
"title": {
"type": "node",
"enabled": true,
"components": {
"color": "magenta",
"layout": {
"priority": 1,
"anchor": { "x": 0.5, "y": 0.05 },
"pivot": { "x": 0.5, "y": 0 },
"size": { "x": "80%", "y": "20%" },
"draw_rect": true
},
"text": {
"priority": 2,
"settings": {
"data": "Some Lode Runner",
"size": 40,
"letter_spacing": 1.0,
"line_spacing": 1.0,
"style": "regular"
},
"outline": {
"color": "white",
"thickness": 1.0
},
"font": {
"path": "res/fonts/win_cmd.ttf",
"smooth": true
}
}
}
}
And, as you can notice, the priority says which component will be used first. And it works perfectly with my rewrote sprite, because the SFML sprite has the following implementation:
void Sprite::updatePositions()
{
FloatRect bounds = getLocalBounds();
m_vertices[0].position = Vector2f(0, 0);
m_vertices[1].position = Vector2f(0, bounds.height);
m_vertices[2].position = Vector2f(bounds.width, 0);
m_vertices[3].position = Vector2f(bounds.width, bounds.height);
}
void Sprite::updateTexCoords()
{
FloatRect convertedTextureRect(m_textureRect);
float left = convertedTextureRect.left;
float right = left + convertedTextureRect.width;
float top = convertedTextureRect.top;
float bottom = top + convertedTextureRect.height;
m_vertices[0].texCoords = Vector2f(left, top);
m_vertices[1].texCoords = Vector2f(left, bottom);
m_vertices[2].texCoords = Vector2f(right, top);
m_vertices[3].texCoords = Vector2f(right, bottom);
}
If you know the OpenGL a little, you have to know that the vertex and the texture coordinates can be implemented as [-1, 1] for the first one, and [0, 1] for the second one.
And to fix it, I rewrote the SFML sprite class like that:
void Sprite::updateVertices() {
mVertices[asID(Corner::LeftDown)].position = { 0.0f , 0.0f };
mVertices[asID(Corner::LeftUp)].position = { 0.0f , 1.0f };
mVertices[asID(Corner::RightUp)].position = { 1.0f , 0.0f };
mVertices[asID(Corner::RightDown)].position = { 1.0f , 1.0f };
}
void Sprite::updateTextureCoords() {
const auto &[width, height]{ mTexture.mData->getSize() };
const float left = mTextureCoords.left * width;
const float right = left + mTextureCoords.width * width;
const float top = mTextureCoords.top * height;
const float bottom = top + mTextureCoords.height * height;
mVertices[asID(Corner::LeftDown)].texCoords = { left , top };
mVertices[asID(Corner::LeftUp)].texCoords = { left , bottom };
mVertices[asID(Corner::RightUp)].texCoords = { right, top };
mVertices[asID(Corner::RightDown)].texCoords = { right, bottom };
}
And it works perfectly. Now I can apply to my sprite transform like that:
render->pushTransform(transform);
render->draw(mySprite);
render->popTransform();
So, I understand that the sf::Text class has the same problem because when I put set my transform component size like 2 and 1 pixel, the text size become so large:
The result using [1, 1] size:
The result using [2, 1] size:
But I need to set size in a normal things like [1024, 720] or, as you can notice, by percents.
So, I have to change the vertex position and textures inside the sf::Text sprite, but I cannot rewrite it like a sprite because of this 431 line in <SFML/Graphics/Text.cpp> file:
// Save the current fonts texture id
m_fontTextureId = m_font->getTexture(m_characterSize).m_cacheId;
The m_cacheId is a sf::Texture's private member, and if we look up inside texture class, we find those lines:
private:
friend class Text;
friend class RenderTexture;
friend class RenderTarget;
It means that I cannot reimplement my own Text class without touching SFML sources :c
But in the SFML original site I found this information about rewriting sf::Text stuff, but I don't really want to spend a lot of time for making bicycle with glyphs

Bad pointer error in class when passing array

So I'm writing a simple battlesystem for a game and I'm getting an error passing an array of pointers to the battlesystem class.
//Create the player and 3 enemies
Battler player("Player", 100, 100, 50, 50, 50, 50, 90);
Battler foe1("Imp", 100, 100, 50, 50, 50, 50, 80);
Battler foe2("Ogre", 100, 100, 50, 50, 50, 50, 75);
Battler foe3("Giant", 100, 100, 50, 50, 50, 50, 60);
//Create an array of pointers that point to the enemies
Battler *foes[3];
foes[0] = &foe1;
foes[1] = &foe2;
foes[2] = &foe3;
//Initialize the battlesystem passing the player, the array of enemies
//and the number of enemies (3)
BattleSystem *btl = new BattleSystem(&player, *foes, 3);
So this was working fine, but when I pass the array to the class, the first member is passed fine, but the rest are passed and when I do a breakpoint, they are sent as "Badptr".
Here is the code for the battlesystem constructor:
BattleSystem::BattleSystem(Battler *plyr, Battler enemies[], int numEnemies)
{
player = plyr;
//foe is declared as Battler *foe; So it just points to the first member of the enemies
// array so I can access them. But only the first member gets a value the rest get
// "Bad ptr" with garbage values and when I look through the enemies array passed
// to the constructor, it has BAD PTRs in everything but the first element.
foe = enemies;
numFoes = numEnemies;
totalTurns = 0;
foeTurns = new int[numFoes];
turnList = new Battler*[numFoes + 1];
for(int i = 0; i <= numFoes; i++)
{
turnList[i] = &foe[i];
}
turnList[numFoes + 1] = player1;
}
I'm missing something obvious I think, but can anyone share some wisdom?
Thank you.
Leaving aside style issues about naked pointers and ownership, I believe you mean
// v-- array of pointers
BattleSystem::BattleSystem(Battler *plyr, Battler *enemies[], int numEnemies)
And
BattleSystem *btl = new BattleSystem(&player, foes, 3);

Initializing a private array c++

SOLVED! (See Edit)
I am trying to initialize an couple of arrays that are private members of a class. I am trying to use a public function to initialize these private arrays. My code looks like this:
void AP_PitchController::initGains(void){
_fvelArray[] = {20, 25, 30, 60, 90, 130, 160, 190, 220, 250, 280};
_kpgArray[] = {6.0, 6.0, 8.0, 4.0, 3.0, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5};
_kdgArray[] = {2000, 2000, 1900, 300, 300, 200, 200, 200, 200, 200, 200};
_kigArray[] = {0.1, 0.1, 0.2, 0.25, 0.3, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5};
}
These arrays are found in the header file AP_PitchController where they are declared private. When I try to compile the code, I get one of these messages for each initialization:
/../AP_PitchController.cpp:106: error: expected primary-expression before ']' token
/../AP_PitchController.cpp:106: error: expected primary-expression before '{' token
/../AP_PitchController.cpp:106: error: expected `;' before '{' token
And here are my private declarations:
Private:
uint8_t _fvelArray[];
float _kpgArray[];
float _kdgArray[];
float _kigArray[];
Does anyone know what I am doing wrong to initialize these arrays upon the call of initGains()?
EDIT:
I found the answer in one of the related questions.
All i need to do is provide an array size for the initialization:
static float _kpgArray[11];
And then initialize it outside of a function in the .cpp file:
uint8_t AP_PitchController::_fvelArray[11] = {20, 25, 30, 60, 90, 130, 160, 190, 220, 250, 280};
Thank you for your input!
You can only use initialization syntax at declaration:
float _array[2] = {0.1f, 0.2f};
After it is declared you will have to initialize the members individually:
_array[0] = 0.1f;
_array[1] = 0.2f;
Or you could do it in a loop:
float temp[2] = {0.1f, 0.2f};
for( int i = 0; i < 2; ++i )
_array[i] = temp[i];
First, you cannot use the the initialization-list syntax that you're using since you've already declared your arrays (e.g. uint8_t _fvelArray = { ... }; would be valid when you first declare it under private: but _fvelArray = { ... }; is not valid in your initGains method). You must also declare the size of each array in your private declarations:
private:
uint8_t _fvelArray[10]; // or whatever size you want
Once you've taken those steps, you can populate the arrays:
_fvelArray[0] = 20;
_fvelArray[1] = 25;
// ...
Is there a reason you don't initialize your arrays right away? Will the gain values change? Your method is called initGains after all. If not, use the initializer-list syntax at the point of declaration:
private:
uint8_t _fvelArray[] = {20, 25, 30, 60, 90, 130, 160, 190, 220, 250, 280};
You have several issues here:
uint8_t _fvelArray[]; does not declare an array, but a pointer, the same as uint8_t *_fvelArray;. If you want to declare fixed-size array, you need to write uint8_t _fvelArray[11]; or in c++11 std::array<uint8_t, 11> _fvelArray;. For variable-length array you should use std::vector<uint8_t> _fvelArray;.
{20, ...} expression is an initializer-list an cannot be used for array initialization outside of its definition. That means that you can write uint8_t _fvelArray_tmp[] = {20, ...}; and then copy it to your variable : memcpy (_fvelArray, _fvelArray_tmp, sizeof (_fvelArray_tmp)); but not to initialize some already existing variable. But if you use std::array or std::vector for _fvelArray type, you could simply write _fvelArray = {20, ...}; (but it only works for c++11).

Inherit from struct data

Imagine the following situation:
I want to create various monster factories. These monster factories create monsters based on the data provided by a struct array. The monsters only differ in these stats, therefore creating a subclass for each monster is overkill.
struct monster_data
{
int HP;
int strength;
int speed;
// even more attributes
};
A class monster can handle all the behavior of a monster based on a monster_data:
class monster
{
public:
monster(monster_data* initial_stats, int length);
void attack();
void walk();
void die();
// and so forth
};
So far, so good. Now I have a class monster_factory that creates monsters based on a hard coded monster_data array:
const monster_data district1_monsters[]
{
{ 500, 20, 4 }, // monster1
{ 550, 5, 12 }, // monster2
{ 420, 8, 10 }, // monster3
{ 310, 30, 7 }, // monster4
// 100 more monsters
};
class monster_factory
{
public:
monster_factory(monster_data* monster_to_create) ;
monster* create_random_monster();
};
My problem is that I have to support several monster_factories for several districts with with minor differences in the lists:
const monster_data district1_monsters[]
{
{ 500, 20, 4 }, // monster1
{ 550, 5, 12 }, // monster2
{ 420, 8, 10 }, // monster3
{ 310, 30, 7 }, // monster4
// 100 more monsters
};
const monster_data district2_monsters[]
{
{ 500, 20, 4 }, // monster1
{ 750, 5, 12 }, // MONSTER2B <<
{ 420, 8, 10 }, // monster3
{ 310, 30, 7 }, // monster4
// monsters 5 - 80 from district 1
};
const monster_data district3_monsters[]
{
{ 500, 20, 4 }, // monster1
{ 550, 5, 12 }, // monster2
{ 720, 80, 10 }, // MONSTER3B <<
{ 310, 30, 7 }, // monster4
// monsters 8 - 90 from district 1
};
Instead of copying and pasting the array data, I would like to somehow inherit from it, because the data stays mostly the same between the various versions. Copying the whole struct array declaration just to have a slightly different variant seems like the wrong way. Too bad that district 2 and 3 just don't append data, they modify and omit existing entries. Of course they change more than one monster, too.
In addition changes on the monster data of district 1 should apply to district 2 and 3 as well.
Another problem is that there are districts that will have monster data completely unrelated to districts 1,2 and 3.
const monster_data district4_monsters[]
{
{ 100, 20, 10 }, // monster 401
{ 200, 50, 20 }, // monster 402
{ 300, 40, 5 }, // monster 403
{ 400, 30, 30 }, // monster 404
// 20 more monsters unrelated to district 1,2 & 3
};
Now to the question: How can the outlined design be changed, so that redundant monster_data declarations are avoided and that districts can be added that either derive their monster_data from an existing declaration or use a completely new one?
Bonus points, if your design ensures that there can only be one factory instance for every variant of the monster stats list.
This can be solved elegantly by the decorator pattern by decorating the "default" table with the changes in each layer:
class MonsterTable
{
public:
virtual monster_data const* getMonsterForIndex(int i)=0;
};
class DefaultMonsterTable : public MonsterTable
{
public:
monster_data const* getMonsterForIndex(int i)
{
return district1_monsters+i;
}
};
class OverlayMonsterTable : public MonsterTable
{
public:
//public for brevity, should be private in real code - can also be std::map
std::unordered_map<int, monster_data> OverlayData;
// Init this with the "previous layer", which is always the Default table in your examples
MonsterTable* Decorated;
monster_data const* getMonsterForIndex(int i)
{
typedef std::unordered_map<VGLindex, monster_data>::const_iterator Iterator;
Iterator Overlay=OverlayData.find(i);
if (Overlay!=OverlayData.end()) // Monster data was changed in this layer
return &Overlay->second;
return Decorated->getMonsterFromIndex(i); // Defer to next layer
}
};
You would then add all "changes" in higher districts to the OverlayData and let the OverlayMonsterTable refer to the default table (district1).
To support omitting of data, you can either add another decorator "layer" that remaps indices (for example, maps [0...80] to [0...10], [30...100]), or integrate this functionality into the existing OverlayMonsterTable. Either way, you have full flexibility. For example:
class OmitMonsterTable : public MonsterTable
{
public:
int OmitBegin, OmitEnd;
MonsterTable* Decorated;
monster_data const* getMonsterForIndex(int i)
{
if (i > OmitBegin)
i += OmitEnd;
return Decorated->getMonsterForIndex(i);
}
};
Your factory would just take a MonsterTable pointer/reference.
You keep using the word "inherit" but I would definitely not consider inheritance here, you only have one type of behaviour, i.e. one type of factory class, you just want to initialize the factories with different data.
I would have one large array with all the distinct monster_data values:
const monster_data all_data[] = {
// district1_monsters
{ 500, 20, 4 }, // monster1
{ 550, 5, 12 }, // monster2
{ 420, 8, 10 }, // monster3
{ 310, 30, 7 }, // monster4
// 100 more monsters
// ...
// district 2 monsters (index 104)
{ 750, 5, 12 }, // MONSTER2B <<
// district 3 monsters (index 105)
{ 720, 80, 10 }, // MONSTER3B <<
// district4 monsters (index 106)
{ 100, 20, 10 },
{ 200, 50, 20 },
{ 300, 40, 5 },
{ 400, 30, 30 },
// 20 more monsters unrelated to district 1,2 & 3
// ...
};
Then create sequences containing the right ones:
typedef std::vector<monster_data> data_seq;
data_seq district1_data(all_data, all_data + 104);
data_seq district2_data(all_data, all_data + 80);
district2_data[2] = all_data[104];
data_seq district3_data(all_data, all_data + 3);
district3_data.push_back( all_data[105] );
district3_data.insert(district3_data.end(), all_data+8, all_data+90);
data_seq district4_data(all_data+106, all_data + 126);
Then create factories from those sequences:
class monster_factory
{
public:
monster_factory(const data_seq& monsters) ;
monster* create_random_monster();
};
monster_factory district1_factory(district1_data);
monster_factory district2_factory(district2_data);
monster_factory district3_factory(district3_data);
monster_factory district4_factory(district4_data);
If the monster_data type is only three integers that should be fine. If it's a bigger class then you could make data_seq a vector<const monster_data*> so it only holds pointers to the elements of the all_data array. That avoids copying the monster_data objects, they just live in the master all_data array and everything else refers to those master copies through pointers. That would take a little more work to create the vector objects, as you'd need to fill it with addresses of the array elements, not simple copies of the elements, but that's something you'd only need to do once at program startup, so writing a little more code to do it right is worth it:
struct address_of {
const monster_data* operator()(const monster_data& m) const
{ return &m; }
};
// ...
typedef std::vector<const monster_data*> data_seq;
data_seq district1_data;
std::transform(all_data, all_data + 104,
std::back_inserter(district1_data), address_of());
data_seq district2_data;
std::transform(all_data, all_data + 80,
std::back_inserter(district2_data), address_of());
district2_data[2] = &all_data[104];
data_seq district3_data;
std::transform(all_data, all_data + 3,
std::back_inserter(district3_data), address_of());
district3_data.push_back( all_data[105] );
std::transform(all_data+8, all_data + 90,
std::back_inserter(district3_data), address_of());
data_seq district4_data;
std::transform(all_data+106, all_data + 126,
std::back_inserter(district4_data), address_of());
An alternative, probably more maintainable, way to initialize the sequence for each district would be to have arrays of indices for each district:
int district1_indices[] = { 0, 1, 2, 3, 4, ... 103 };
int district2_indices[] = { 0, 1, 104, 3, 4, ... 79 };
int district3_indices[] = { 0, 1, 2, 105, 7, 8, 9, 10 ... 89 };
int district4_indices[] = { 106, 107, 108, 109 ... 125 };
Then construct a factory with one of those arrays (and its length). The factory can just pick an index from the list and then use it to index into all_data to get a monster_data.
Storing data in a binary is often bad practice and does not scale, especially if it is going to be a huge amount of data. You shouldn't have much trouble to define your own mini-language that supports simple inheritance of data and then parse it into a class that contains an unordered_map. That would enable you to also implement simple data sharing and a more complex property system, should you ever require it.
I'd have one factory where I pass the district when I ask for a monster.
Then I can do something like (pseudo code only)
getMonster(int district)
{
monster_data dat = getRandomBaseMonster();
// dat has to be a copy so we don't stomp in the base data
if (district == 2) {
dat.HP += 10;
}
return dat;
}
One solution may be to have a base table that contains the "standard" monster data, and then for each district you have a table containing only a list of the modified monsters.
something like this:
const monster_data base_monsters[] = {
{ 500, 20, 4 }, // monster1
{ 550, 5, 12 }, // monster2
{ 420, 8, 10 }, // monster3
{ 310, 30, 7 }, // monster4
// 100 more monsters
};
struct monster_change_data
{
int monster; /* Index into base table */
struct monster_data data; /* Modified monster data */
};
const struct monster_change_data district2_monsters[] = {
{ 1, { 750, 5, 12 } }, // MONSTER2B
};
const struct monster_change_data district3_monsters[] = {
{ 2, { 720, 80, 10 } }, // MONSTER3B
};
This way you only have to list the changed monsters.
For completeness' sake I'll post the design I came up with before ltjax posted his answer, although mine is inferior. As it has a different approach it might be of interest to others nevertheless.
It combines the factory with its table as the table by itself has little meaning.
The filling of the table is done in the factory's constructor. This way other factories can inherit the constructor and make changes to the table.
The drawback is that every factory creates its own full table therefore storing redundant data during runtime. At least maintenance becomes easier.
This might be improved by moving the helper methods add, replace and remove to a separate table class to encapsulate them properly. But monster_factory_abstract would be basically empty in this case, IMO.
class monster_factory_abstract
{
private:
monster_data* table; // or map with sequential indices
int table_length;
protected:
// add monster to table
void add(int HP, int strength, int speed, etc.);
// index starts with one to match monster names in this example
void replace(int index, int HP, int strength, int speed, etc.);
void remove(int index); // nulls an entry
void remove(int from, int to);
public:
virtual monster* create_random_monster();
}
class monster_factory_district1 : public monster_factory_abstract
{
public:
monster_factory_district1()
{
table_length = 0;
add( 500, 20, 4 ); // monster1
add( 550, 5, 12 ); // monster2
add( 420, 8, 10 ); // monster3
add( 310, 30, 7 ); // monster4
// add 100 more monsters
}
};
class monster_factory_district2 : public monster_factory_district1
{
public:
monster_factory_district2() : monster_factory_district1
{
replace( 2, 750, 5, 12 ); // MONSTER2B <<
remove(81, 100);
}
};
class monster_factory_district3 : public monster_factory_district1
{
public:
monster_factory_district3() : monster_factory_district1
{
replace( 3, 720, 80, 10 ); // MONSTER3B <<
remove(5, 8);
remove(91, 100);
}
};
class monster_factory_district4 : public monster_factory_abstract
{
public:
monster_factory_district4() : monster_factory_abstract
{
table_length = 0;
add( 100, 20, 10 ); // monster 401
add( 200, 50, 20 ); // monster 402
add( 300, 40, 5 ); // monster 403
add( 400, 30, 30 ); // monster 404
}
};

Rectangle approximation algorithm

I have an enumeration of just under 32 absolute rectangle sizes and I need to given dimensions and find the best approximation among my enumeration.
Is there any better (ie more readable and maintainable) way than the spaghetti code I am formulating out of lots of nested if's and else's?
At the moment I have just:
enum imgOptsScale {
//Some relative scales
w005h005 = 0x8,
w010h010 = 0x9,
w020h020 = 0xA,
w040h040 = 0xB,
w070h070 = 0xC,
w100h100 = 0xD,
w150h150 = 0xE,
w200h200 = 0xF,
w320h320 = 0x10,
w450h450 = 0x11,
w200h010 = 0x12,
w200h020 = 0x13,
w200h070 = 0x14,
w010h200 = 0x15,
w020h200 = 0x16,
w070h200 = 0x17
};
imgOptsScale getClosestSizeTo(int width, int height);
and I thought I'd ask for help before I got too much further into coding up. I should emphasise a bias away from too elaborate libraries though I am more interested in algorithms than containers this is supposed to run on a resource constrained system.
I think I'd approach this with a few arrays of structs, one for horizontal measures and one for vertical measures.
Read through the arrays to find the next larger size, and return the corresponding key. Build the final box measure from the two keys. (Since 32 only allows 5 bits, this is probably not very ideal -- you'd probably want 2.5 bits for the horizontal and 2.5 bits for the vertical, but my simple approach here requires 6 bits -- 3 for horizontal and 3 for vertical. You can remove half the elements from one of the lists (and maybe adjust the << 3 as well) if you're fine with one of the dimensions having fewer degrees of freedom. If you want both dimensions to be better represented, this will probably require enough re-working that this approach might not be suitable.)
Untested pseudo-code:
struct dimen {
int x;
int key;
}
struct dimen horizontal[] = { { .x = 10, .key = 0 },
{ .x = 20, .key = 1 },
{ .x = 50, .key = 2 },
{ .x = 90, .key = 3 },
{ .x = 120, .key = 4 },
{ .x = 200, .key = 5 },
{ .x = 300, .key = 6 },
{ .x = 10000, .key = 7 }};
struct dimen vertical[] = { { .x = 10, .key = 0 },
{ .x = 20, .key = 1 },
{ .x = 50, .key = 2 },
{ .x = 90, .key = 3 },
{ .x = 120, .key = 4 },
{ .x = 200, .key = 5 },
{ .x = 300, .key = 6 },
{ .x = 10000, .key = 7 }};
/* returns 0-63 as written */
int getClosestSizeTo(int width, int height) {
int horizontal_key = find_just_larger(horizontal, width);
int vertical_key = find_just_larger(vertical, height);
return (horizontal_kee << 3) & vertical_key;
}
int find_just_larger(struct dimen* d, size) {
int ret = d.key;
while(d.x < size) {
d++;
ret = d.key;
}
return ret;
}
Yes ... place your 32 different sizes in a pre-built binary search tree, and then recursively search through the tree for the "best" size. Basically you would stop your search if the left child pre-built rectangle of the current node's rectangle is smaller than your input rectangle, and the current node's rectangle is larger than the input rectangle. You would then return the pre-defined rectangle that is "closest" to your input rectangle between the two.
One nice addition to the clean code the recursive search creates is that it would also be logarithmic rather than linear in search time.
BTW, you will want to randomize the order that you insert the initial pre-defined rectangle values into the binary search tree, otherwise you will end up with a degenerate tree that looks like a linked list, and you won't get logarithmic search time since the height of the tree will be the number of nodes, rather than logarithmic to the number of nodes.
So for instance, if you've sorted the tree by the area of your rectangles (provided there are no two rectangles with the same area), then you could do something like the following:
//for brevity, find the rectangle that is the
//greatest rectangle smaller than the input
const rec_bstree* find_best_fit(const rec_bstree* node, const rec& input_rec)
{
if (node == NULL)
return NULL;
rec_bstree* return_node;
if (input_rec.area < node->area)
return_node = find_best_fit(node->left_child, input_rec);
else if (input_rec.area > node->area)
return_node = find_best_fit(node->right_child, input_rec);
if (return_node == NULL)
return node;
}
BTW, if a tree is too complex, you could also simply do an array or std::vector of instances of your rectangles, sort them using some type of criteria using std::sort, and then do binary searched on the array.
Here is my proposed solution,
enum imgOptsScale {
notScaled = 0x0,
//7 relative scales upto = 0x7
w010h010, w010h025, w010h060, w010h120, w010h200, w010h310, w010h450,
w025h010, w025h025, w025h060, w025h120, w025h200, w025h310, w025h450,
w060h010, w060h025, w060h060, w060h120, w060h200, w060h310, w060h450,
w120h010, w120h025, w120h060, w120h120, w120h200, w120h310, w120h450,
w200h010, w200h025, w200h060, w200h120, w200h200, w200h310, w200h450,
w310h010, w310h025, w310h060, w310h120, w310h200, w310h310, w310h450,
w450h010, w450h025, w450h060, w450h120, w450h200, w450h310, w450h450,
w730h010, w730h025, w730h060, w730h120, w730h200, w730h310, w730h450
};
//Only call if width and height are actually specified. else 0=>10px
imgOptsScale getClosestSizeTo(int width, int height) {
static const int possSizes[] = {10, 25, 60, 120, 200, 310, 450, 730};
static const int sizesHalfways[] = {17, 42, 90, 160, 255, 380, 590};
int widthI = 6;
while (sizesHalfways[widthI - 1] > width && --widthI>0);
int heightI = 6;
while (sizesHalfways[heightI - 1] > height && --heightI>0);
return (imgOptsScale)(8 + 7 * widthI + heightI);
}