nana/source/gui/layout_utility.cpp

285 lines
6.9 KiB
C++

/*
* Utility Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2015 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/layout_utility.hpp
* @contributors: Ryan Gonzalez
*
*/
#include <nana/gui/layout_utility.hpp>
namespace nana
{
//overlap test if overlaped between r1 and r2
bool overlap(const rectangle& r1, const rectangle& r2)
{
if (r1.y + (long long)(r1.height) <= r2.y) return false;
if(r1.y >= r2.y + (long long)(r2.height)) return false;
if(r1.x + (long long)(r1.width) <= r2.x) return false;
if(r1.x >= r2.x + (long long)(r2.width)) return false;
return true;
}
//overlap, compute the overlap area between r1 and r2. the rect is for root
bool overlap(const rectangle& r1, const rectangle& r2, rectangle& r)
{
if(overlap(r1, r2))
{
auto l1 = static_cast<long long>(r1.x) + r1.width;
auto l2 = static_cast<long long>(r2.x) + r2.width;
auto b1 = static_cast<long long>(r1.y) + r1.height;
auto b2 = static_cast<long long>(r2.y) + r2.height;
r.x = r1.x < r2.x ? r2.x : r1.x;
r.y = r1.y < r2.y ? r2.y : r1.y;
r.width = static_cast<unsigned>(l1 < l2 ? l1 - r.x: l2 - r.x);
r.height = static_cast<unsigned>(b1 < b2 ? b1 - r.y: b2 - r.y);
return true;
}
return false;
}
bool overlap(const rectangle& ir, const size& valid_input_area, const rectangle & dr, const size& valid_dst_area, rectangle& op_ir, rectangle& op_dr)
{
rectangle valid_r{ valid_input_area };
if (overlap(ir, valid_r, op_ir) == false)
return false;
valid_r = valid_dst_area;
rectangle good_dr;
if (overlap(dr, valid_r, good_dr) == false)
return false;
zoom(ir, op_ir, dr, op_dr);
if (covered(op_dr, good_dr))
{
overlap({ op_dr }, good_dr, op_dr);
}
else
{
op_dr = good_dr;
zoom(dr, good_dr, ir, op_ir);
}
return true;
}
bool intersection(const rectangle & r, point pos_beg, point pos_end, point& good_pos_beg, point& good_pos_end)
{
const int right = r.right();
const int bottom = r.bottom();
if(pos_beg.x > pos_end.x)
std::swap(pos_beg, pos_end);
bool good_beg = (0 <= pos_beg.x && pos_beg.x < right && 0 <= pos_beg.y && pos_beg.y < bottom);
bool good_end = (0 <= pos_end.x && pos_end.x < right && 0 <= pos_end.y && pos_end.y < bottom);
if(good_beg && good_end)
{
good_pos_beg = pos_beg;
good_pos_end = pos_end;
return true;
}
else if(pos_beg.x == pos_end.x)
{
if(r.x <= pos_beg.x && pos_beg.x < right)
{
if(pos_beg.y < r.y)
{
if(pos_end.y < r.y)
return false;
good_pos_beg.y = r.y;
good_pos_end.y = (pos_end.y < bottom ? pos_end.y : bottom - 1);
}
else if(pos_beg.y >= bottom)
{
if(pos_end.y >= bottom)
return false;
good_pos_beg.y = bottom - 1;
good_pos_end.y = (pos_end.y < r.y ? r.y : pos_end.y);
}
good_pos_beg.x = good_pos_end.x = r.x;
return true;
}
return false;
}
else if(pos_beg.y == pos_end.y)
{
if(r.y <= pos_beg.y && pos_beg.y < bottom)
{
if(pos_beg.x < r.x)
{
if(pos_end.x < r.x)
return false;
good_pos_beg.x = r.x;
good_pos_end.x = (pos_end.x < right ? pos_end.x : right - 1);
}
else if(pos_beg.x >= right)
{
if(pos_end.x >= right)
return false;
good_pos_beg.x = right - 1;
good_pos_end.x = (pos_end.x < r.x ? r.x : pos_end.x);
}
good_pos_beg.y = good_pos_end.y = r.y;
return true;
}
return false;
}
double m = (pos_end.y - pos_beg.y ) / double(pos_end.x - pos_beg.x);
bool is_nw_to_se = (m >= 0.0);
//The formulas for the line.
//y = m * (x - pos_beg.x) + pos_beg.y
//x = (y - pos_beg.y) / m + pos_beg.x
if(!good_beg)
{
good_pos_beg.y = static_cast<int>(m * (r.x - pos_beg.x)) + pos_beg.y;
if(r.y <= good_pos_beg.y && good_pos_beg.y < bottom)
{
good_pos_beg.x = r.x;
}
else
{
bool cond;
int y;
if(is_nw_to_se)
{
y = r.y;
cond = good_pos_beg.y < y;
}
else
{
y = bottom - 1;
cond = good_pos_beg.y > y;
}
if(cond)
{
good_pos_beg.x = static_cast<int>((y - pos_beg.y) / m) + pos_beg.x;
if(r.x <= good_pos_beg.x && good_pos_beg.x < right)
good_pos_beg.y = y;
else
return false;
}
else
return false;
}
if(good_pos_beg.x < pos_beg.x)
return false;
}
else
good_pos_beg = pos_beg;
if(!good_end)
{
good_pos_end.y = static_cast<int>(m * (right - 1 - pos_beg.x)) + pos_beg.y;
if(r.y <= good_pos_end.y && good_pos_end.y < bottom)
{
good_pos_end.x = right - 1;
}
else
{
bool cond;
int y;
if(is_nw_to_se)
{
y = bottom - 1;
cond = good_pos_end.y > y;
}
else
{
y = r.y;
cond = good_pos_end.y < y;
}
if(cond)
{
good_pos_end.x = static_cast<int>((y - pos_beg.y) / m) + pos_beg.x;
if(r.x <= good_pos_end.x && good_pos_end.x < right)
good_pos_end.y = y;
else
return false;
}
else
return false;
}
if(good_pos_end.x > pos_end.x)
return false;
}
else
good_pos_end = pos_end;
return true;
}
void fit_zoom(const size& input_s, const size& ref_s, size& result_s)
{
double rate_input = double(input_s.width) / double(input_s.height);
double rate_ref = double(ref_s.width) / double(ref_s.height);
if(rate_input < rate_ref)
{
result_s.height = ref_s.height;
result_s.width = static_cast<unsigned>(ref_s.height * rate_input);
}
else if(rate_input > rate_ref)
{
result_s.width = ref_s.width;
result_s.height = static_cast<unsigned>(ref_s.width / rate_input);
}
else
result_s = ref_s;
}
size fit_zoom(const size& input_s, size ref_s)
{
double rate_input = double(input_s.width) / double(input_s.height);
double rate_ref = double(ref_s.width) / double(ref_s.height);
if (rate_input < rate_ref)
ref_s.width = static_cast<unsigned>(ref_s.height * rate_input);
else if (rate_input > rate_ref)
ref_s.height = static_cast<unsigned>(ref_s.width / rate_input);
return ref_s;
}
void zoom(const rectangle& ref, const rectangle& scaled, const rectangle& ref_dst, rectangle& r)
{
double rate_x = (scaled.x - ref.x) / double(ref.width);
double rate_y = (scaled.y - ref.y) / double(ref.height);
r.x = static_cast<int>(rate_x * ref_dst.width) + ref_dst.x;
r.y = static_cast<int>(rate_y * ref_dst.height) + ref_dst.y;
r.width = static_cast<unsigned>(scaled.width / double(ref.width) * ref_dst.width);
r.height = static_cast<unsigned>(scaled.height / double(ref.height) * ref_dst.height);
}
//covered test if r2 covers r1.
bool covered(const rectangle& r1, //Rectangle 1 is must under rectangle 2
const rectangle& r2) //Rectangle 2
{
if(r1.x < r2.x || r1.right() > r2.right()) return false;
if(r1.y < r2.y || r1.bottom() > r2.bottom()) return false;
return true;
}
}