Commit 754da0ef authored by Davis King's avatar Davis King
Browse files

Properly organized the svn repository. Finally.

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402199
parent 49388714
// Copyright (C) 2005 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_RECTANGLe_
#define DLIB_RECTANGLe_
#include "rectangle_abstract.h"
#include "../algs.h"
#include <algorithm>
#include <iostream>
#include "../serialize.h"
#include "vector.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class rectangle
{
/*!
INITIAL VALUE
The initial value of this object is defined by its constructor.
CONVENTION
left() == l
top() == t
right() == r
bottom() == b
!*/
public:
rectangle (
long l_,
long t_,
long r_,
long b_
) :
l(l_),
t(t_),
r(r_),
b(b_)
{}
rectangle (
unsigned long w,
unsigned long h
) :
l(0),
t(0),
r(static_cast<long>(w)-1),
b(static_cast<long>(h)-1)
{
DLIB_ASSERT(w > 0 && h > 0 || w == 0 && h == 0,
"\trectangle(width,height)"
<< "\n\twidth and height must be > 0 or both == 0"
<< "\n\twidth: " << w
<< "\n\theight: " << h
<< "\n\tthis: " << this
);
}
rectangle (
const point& p
) :
l(p.x()),
t(p.y()),
r(p.x()),
b(p.y())
{
}
template <typename T>
rectangle (
const vector<T>& v
) :
l(static_cast<long>(v.x()+0.5)),
t(static_cast<long>(v.y()+0.5)),
r(static_cast<long>(v.x()+0.5)),
b(static_cast<long>(v.y()+0.5))
{
}
rectangle (
const point& p1,
const point& p2
)
{
*this = rectangle(p1) + rectangle(p2);
}
template <typename T>
rectangle (
const vector<T>& v1,
const vector<T>& v2
)
{
*this = rectangle(v1) + rectangle(v2);
}
rectangle (
) :
l(0),
t(0),
r(-1),
b(-1)
{}
long top (
) const { return t; }
long& top (
) { return t; }
void set_top (
long top_
) { t = top_; }
long left (
) const { return l; }
long& left (
) { return l; }
void set_left (
long left_
) { l = left_; }
long right (
) const { return r; }
long& right (
) { return r; }
void set_right (
long right_
) { r = right_; }
long bottom (
) const { return b; }
long& bottom (
) { return b; }
void set_bottom (
long bottom_
) { b = bottom_; }
unsigned long width (
) const
{
if (is_empty())
return 0;
else
return r - l + 1;
}
unsigned long height (
) const
{
if (is_empty())
return 0;
else
return b - t + 1;
}
unsigned long area (
) const
{
return width()*height();
}
bool is_empty (
) const { return (t > b || l > r); }
rectangle operator + (
const rectangle& rhs
) const
{
if (rhs.is_empty())
return *this;
else if (is_empty())
return rhs;
return rectangle (
std::min(l,rhs.l),
std::min(t,rhs.t),
std::max(r,rhs.r),
std::max(b,rhs.b)
);
}
rectangle intersect (
const rectangle& rhs
) const
{
return rectangle (
std::max(l,rhs.l),
std::max(t,rhs.t),
std::min(r,rhs.r),
std::min(b,rhs.b)
);
}
bool contains (
const point& p
) const
{
if (p.x() < l || p.x() > r || p.y() < t || p.y() > b)
return false;
return true;
}
bool contains (
long x,
long y
) const
{
if (x < l || x > r || y < t || y > b)
return false;
return true;
}
bool contains (
const rectangle& rect
) const
{
return (rect + *this == *this);
}
rectangle& operator+= (
const rectangle& rect
)
{
*this = *this + rect;
return *this;
}
bool operator== (
const rectangle& rect
) const
{
return (l == rect.l) && (t == rect.t) && (r == rect.r) && (b == rect.b);
}
bool operator!= (
const rectangle& rect
) const
{
return !(*this == rect);
}
private:
long l;
long t;
long r;
long b;
};
// ----------------------------------------------------------------------------------------
inline void serialize (
const rectangle& item,
std::ostream& out
)
{
try
{
serialize(item.left(),out);
serialize(item.top(),out);
serialize(item.right(),out);
serialize(item.bottom(),out);
}
catch (serialization_error& e)
{
throw serialization_error(e.info + "\n while serializing an object of type rectangle");
}
}
inline void deserialize (
rectangle& item,
std::istream& in
)
{
try
{
deserialize(item.left(),in);
deserialize(item.top(),in);
deserialize(item.right(),in);
deserialize(item.bottom(),in);
}
catch (serialization_error& e)
{
throw serialization_error(e.info + "\n while deserializing an object of type rectangle");
}
}
inline std::ostream& operator<< (
std::ostream& out,
const rectangle& item
)
{
out << "[(" << item.left() << ", " << item.top() << ") (" << item.right() << ", " << item.bottom() << ")]";
return out;
}
inline std::istream& operator>>(
std::istream& in,
rectangle& item
)
{
// ignore any whitespace
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n')
in.get();
// now eat the leading '[' character
if (in.get() != '[')
{
in.setstate(in.rdstate() | std::ios::failbit);
return in;
}
point p1, p2;
in >> p1;
in >> p2;
item = rectangle(p1) + rectangle(p2);
// ignore any whitespace
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n')
in.get();
// now eat the trailing ']' character
if (in.get() != ']')
{
in.setstate(in.rdstate() | std::ios::failbit);
}
return in;
}
// ----------------------------------------------------------------------------------------
inline const rectangle centered_rect (
long x,
long y,
unsigned long width,
unsigned long height
)
{
rectangle result;
result.set_left ( x - static_cast<long>(width) / 2 );
result.set_top ( y - static_cast<long>(height) / 2 );
result.set_right ( result.left() + width - 1 );
result.set_bottom ( result.top() + height - 1 );
return result;
}
// ----------------------------------------------------------------------------------------
inline const point nearest_point (
const rectangle& rect,
const point& p
)
{
point temp(p);
if (temp.x() < rect.left())
temp.x() = rect.left();
else if (temp.x() > rect.right())
temp.x() = rect.right();
if (temp.y() < rect.top())
temp.y() = rect.top();
else if (temp.y() > rect.bottom())
temp.y() = rect.bottom();
return temp;
}
// ----------------------------------------------------------------------------------------
inline const rectangle centered_rect (
const point& p,
unsigned long width,
unsigned long height
)
{
return centered_rect(p.x(),p.y(),width,height);
}
// ----------------------------------------------------------------------------------------
inline const rectangle centered_rect (
const rectangle& rect,
unsigned long width,
unsigned long height
)
{
return centered_rect((rect.left()+rect.right())/2, (rect.top()+rect.bottom())/2, width, height);
}
// ----------------------------------------------------------------------------------------
inline const rectangle translate_rect (
const rectangle& rect,
long x,
long y
)
{
rectangle result;
result.set_top ( rect.top() + y );
result.set_bottom ( rect.bottom() + y );
result.set_left ( rect.left() + x );
result.set_right ( rect.right() + x );
return result;
}
// ----------------------------------------------------------------------------------------
inline const rectangle resize_rect (
const rectangle& rect,
unsigned long width,
unsigned long height
)
{
return rectangle(rect.left(),rect.top(),
rect.left()+width-1,
rect.top()+height-1);
}
// ----------------------------------------------------------------------------------------
inline const rectangle resize_rect_width (
const rectangle& rect,
unsigned long width
)
{
return rectangle(rect.left(),rect.top(),
rect.left()+width-1,
rect.bottom());
}
// ----------------------------------------------------------------------------------------
inline const rectangle resize_rect_height (
const rectangle& rect,
unsigned long height
)
{
return rectangle(rect.left(),rect.top(),
rect.right(),
rect.top()+height-1);
}
// ----------------------------------------------------------------------------------------
inline const rectangle move_rect (
const rectangle& rect,
long x,
long y
)
{
return rectangle(x, y, x+rect.width()-1, y+rect.height()-1);
}
// ----------------------------------------------------------------------------------------
}
namespace std
{
/*!
Define std::less<rectangle> so that you can use rectangles in the associative containers.
!*/
template<>
struct less<dlib::rectangle> : public binary_function<dlib::rectangle ,dlib::rectangle,bool>
{
inline bool operator() (const dlib::rectangle& a, const dlib::rectangle& b) const
{
if (a.left() < b.left()) return true;
else if (a.left() > b.left()) return false;
else if (a.top() < b.top()) return true;
else if (a.top() > b.top()) return false;
else if (a.right() < b.right()) return true;
else if (a.right() > b.right()) return false;
else if (a.bottom() < b.bottom()) return true;
else if (a.bottom() > b.bottom()) return false;
else return false;
}
};
}
#endif // DLIB_RECTANGLe_
// Copyright (C) 2005 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_RECTANGLe_ABSTRACT_
#ifdef DLIB_RECTANGLe_ABSTRACT_
#include "vector_abstract.h"
#include <iostream>
#include "../serialize.h"
namespace dlib
{
class rectangle
{
/*!
INITIAL VALUE
The initial value of this object is defined by its constructor.
WHAT THIS OBJECT REPRESENTS
This object represents a rectangular region inside a Cartesian
coordinate system. The region is the rectangle with its upper
left corner at position (left(),top()) and its lower right corner
at (right(),bottom()).
Note that the origin of the coordinate system, i.e. (0,0), is located
at the upper left corner. That is, points such as (1,1) or (3,5)
represent locations that are below and to the right of the origin.
Also note that rectangles where top() > bottom() or left() > right()
represent empty rectangles.
!*/
public:
rectangle (
const rectangle& rect
);
/*!
ensures
- #*this represents the same rectangle as rect
!*/
rectangle (
);
/*!
ensures
- #left() == 0
- #top() == 0
- #right() == -1
- #bottom() == -1
- #is_empty() == true
!*/
rectangle (
long left_,
long top_,
long right_,
long bottom_
);
/*!
ensures
- #left() == left_
- #top() == top_
- #right() == right_
- #bottom() == bottom_
!*/
rectangle (
unsigned long width_,
unsigned long height_
);
/*!
ensures
- #left() == 0
- #top() == 0
- #width() == width_
- #height() == height_
!*/
rectangle (
const point& p
);
/*!
ensures
- #left() == p.x()
- #top() == p.y()
- #right() == p.x()
- #bottom() == p.y()
!*/
template <typename T>
rectangle (
const vector<T>& v
);
/*!
ensures
- #left() == static_cast<long>(floor(v.x()+0.5))
- #top() == static_cast<long>(floor(v.y()+0.5))
- #right() == static_cast<long>(floor(v.x()+0.5))
- #bottom() == static_cast<long>(floor(v.y()+0.5))
!*/
rectangle (
const point& p1,
const point& p2
);
/*!
ensures
- #*this == rectangle(p1) + rectangle(p2)
!*/
template <typename T>
rectangle (
const vector<T>& v1,
const vector<T>& v2
);
/*!
ensures
- #*this == rectangle(v1) + rectangle(v2)
!*/
long left (
) const;
/*!
ensures
- returns the x coordinate for the left side of this rectangle
!*/
long& left (
);
/*!
ensures
- returns a non-const reference to the x coordinate for the left side
of this rectangle
!*/
void set_left (
long left_
);
/*!
ensures
- #left() == left_
!*/
long top (
) const;
/*!
ensures
- returns the y coordinate for the top of this rectangle
!*/
long& top (
);
/*!
ensures
- returns a non-const reference to the y coordinate for the
top of this rectangle
!*/
void set_top (
long top_
);
/*!
ensures
- #top() == top_
!*/
long right (
) const;
/*!
ensures
- returns the x coordinate for the right side of this rectangle
!*/
long& right (
);
/*!
ensures
- returns a non-const reference to the x coordinate for the right
side of this rectangle
!*/
void set_right (
long right_
);
/*!
ensures
- #right() == right_
!*/
long bottom (
) const;
/*!
ensures
- returns the y coordinate for the bottom of this rectangle
!*/
long& bottom (
);
/*!
ensures
- returns a non-const reference to the y coordinate for the bottom
of this rectangle
!*/
void set_bottom (
long bottom_
);
/*!
ensures
- #bottom() == bottom_
!*/
bool is_empty (
) const;
/*!
ensures
- if (top() > bottom() || left() > right()) then
- returns true
- else
- returns false
!*/
unsigned long width (
) const;
/*!
ensures
- if (is_empty()) then
- returns 0
- else
- returns the width of this rectangle.
(i.e. right() - left() + 1)
!*/
unsigned long height (
) const;
/*!
ensures
- if (is_empty()) then
- returns 0
- else
- returns the height of this rectangle.
(i.e. bottom() - top() + 1)
!*/
unsigned long area (
) const;
/*!
ensures
- returns width()*height()
!*/
rectangle operator + (
const rectangle& rhs
) const;
/*!
ensures
- if (rhs.is_empty() == false && this->is_empty() == false) then
- returns the smallest rectangle that contains both *this and
rhs.
- if (rhs.is_empty() == true && this->is_empty() == false) then
- returns *this
- if (rhs.is_empty() == false && this->is_empty() == true) then
- returns rhs
- if (rhs.is_empty() == true && this->is_empty() == true) then
- returns a rectangle that has is_empty() == true
!*/
rectangle intersect (
const rectangle& rhs
) const;
/*!
ensures
- if (there is a region of intersection between *this and rhs) then
- returns a rectangle that represents the intersection of *this
and rhs.
- else
- returns a rectangle where is_empty() == true
!*/
bool contains (
long x,
long y
) const;
/*!
ensures
- if (the point (x,y) is contained in this rectangle) then
- returns true
- else
- returns false
!*/
bool contains (
const point& p
) const;
/*!
ensures
- if (the point (p.x(),p.y()) is contained in this rectangle) then
- returns true
- else
- returns false
!*/
bool contains (
const rectangle& rect
) const;
/*!
ensures
- if (rect + *this == *this) then
- returns true
(i.e. returns true if *this contains the given rectangle)
- else
- returns false
!*/
rectangle& operator= (
const rectangle& rect
);
/*!
ensures
- #*this represents the same rectangle as rect
- returns #*this
!*/
rectangle& operator+= (
const rectangle& rect
);
/*!
ensures
- #*this == *this + rect
- returns #*this
!*/
bool operator== (
const rectangle& rect
) const;
/*!
ensures
- if (top() == rect.top() && left() == rect.left() &&
right() == rect.right() && bottom() == rect.bottom()) then
- returns true
- else
- returns false
!*/
bool operator!= (
const rectangle& rect
) const;
/*!
ensures
- returns !(*this == rect)
!*/
};
// ----------------------------------------------------------------------------------------
void serialize (
const rectangle& item,
std::ostream& out
);
/*!
provides serialization support
!*/
void deserialize (
rectangle& item,
std::istream& in
);
/*!
provides deserialization support
!*/
std::ostream& operator<< (
std::ostream& out,
const rectangle& item
);
/*!
ensures
- writes item to out in the form "[(left, top) (right, bottom)]"
!*/
std::istream& operator>>(
std::istream& in,
rectangle& item
);
/*!
ensures
- reads a rectangle from the input stream in and stores it in #item.
The data in the input stream should be of the form [(left, top) (right, bottom)]
!*/
// ----------------------------------------------------------------------------------------
inline const rectangle centered_rect (
const point& p,
unsigned long width,
unsigned long height
);
/*!
ensures
- returns a rectangle R such that:
- if (width == 0 || height == 0)
- R.width() == 0
- R.height() == 0
- else
- R.width() == width
- R.height() == height
- The center of R is a the point p
!*/
// ----------------------------------------------------------------------------------------
const rectangle centered_rect (
long x,
long y,
unsigned long width,
unsigned long height
);
/*!
ensures
- returns a rectangle R such that:
- if (width == 0 || height == 0)
- R.width() == 0
- R.height() == 0
- else
- R.width() == width
- R.height() == height
- The center of R is a the point (x,y)
!*/
// ----------------------------------------------------------------------------------------
inline const rectangle centered_rect (
const rectangle& rect,
unsigned long width,
unsigned long height
);
/*!
ensures
- returns a rectangle R such that:
- if (width == 0 || height == 0)
- R.width() == 0
- R.height() == 0
- else
- R.width() == width
- R.height() == height
- The center of R is at the center of rect
!*/
// ----------------------------------------------------------------------------------------
const rectangle translate_rect (
const rectangle& rect,
long x,
long y
);
/*!
ensures
- returns a rectangle R such that:
- R.left() == rect.left() + x
- R.right() == rect.right() + x
- R.top() == rect.top() + y
- R.bottom() == rect.bottom() + y
!*/
// ----------------------------------------------------------------------------------------
const rectangle resize_rect (
const rectangle& rect,
unsigned long width,
unsigned long height
);
/*!
ensures
- returns a rectangle R such that:
- if (width == 0 || height == 0)
- R.width() == 0
- R.height() == 0
- else
- R.width() == width
- R.height() == height
- R.left() == rect.left()
- R.top() == rect.top()
!*/
// ----------------------------------------------------------------------------------------
const rectangle resize_rect_width (
const rectangle& rect,
unsigned long width
);
/*!
ensures
- returns a rectangle R such that:
- R.width() == width
- R.left() == rect.left()
- R.top() == rect.top()
- R.bottom() == rect.bottom()
!*/
// ----------------------------------------------------------------------------------------
const rectangle resize_rect_height (
const rectangle& rect,
unsigned long height
);
/*!
ensures
- returns a rectangle R such that:
- R.height() == height
- R.left() == rect.left()
- R.top() == rect.top()
- R.right() == rect.right()
!*/
// ----------------------------------------------------------------------------------------
const rectangle move_rect (
const rectangle& rect,
long x,
long y
);
/*!
ensures
- returns a rectangle R such that:
- R.width() == rect.width()
- R.height() == rect.height()
- R.left() == x
- R.top() == y
!*/
// ----------------------------------------------------------------------------------------
inline const point nearest_point (
const rectangle& rect,
const point& p
);
/*!
ensures
- if (rect.contains(p)) then
- returns p
- else
- returns the point in rect that is closest to p
!*/
// ----------------------------------------------------------------------------------------
}
namespace std
{
/*!
Define std::less<rectangle> so that you can use rectangles in the associative containers.
!*/
template<>
struct less<dlib::rectangle> : public binary_function<dlib::rectangle,dlib::rectangle,bool>
{
inline bool operator() (const dlib::rectangle& a, const dlib::rectangle& b) const
{
if (a.left() < b.left()) return true;
else if (a.left() > b.left()) return false;
else if (a.top() < b.top()) return true;
else if (a.top() > b.top()) return false;
else if (a.right() < b.right()) return true;
else if (a.right() > b.right()) return false;
else if (a.bottom() < b.bottom()) return true;
else if (a.bottom() > b.bottom()) return false;
else return false;
}
};
}
#endif // DLIB_RECTANGLe_ABSTRACT_
// Copyright (C) 2003 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_VECTOr_H_
#define DLIB_VECTOr_H_
#include <cmath>
#include "vector_abstract.h"
#include "../algs.h"
#include "../serialize.h"
#include <functional>
#include <iostream>
namespace dlib
{
class point;
template <
typename T
>
class vector
{
/*!
INITIAL VALUE
- x_value == 0
- y_value == 0
- z_value == 0
CONVENTION
- x_value == x()
- y_value == y()
- z_value == z()
!*/
public:
typedef T type;
vector (
) :
x_value(0.0),
y_value(0.0),
z_value(0.0)
{}
// ---------------------------------------
vector (
const T _x,
const T _y,
const T _z
) :
x_value(_x),
y_value(_y),
z_value(_z)
{}
// ---------------------------------------
vector (
const vector& v
) :
x_value(v.x_value),
y_value(v.y_value),
z_value(v.z_value)
{}
// ---------------------------------------
inline vector (
const point& p
);
// ---------------------------------------
~vector (
){}
// ---------------------------------------
T length(
) const
{
return (T)std::sqrt((double)(x_value*x_value + y_value*y_value + z_value*z_value));
}
// ---------------------------------------
vector normalize (
) const
{
T tmp = (T)std::sqrt((double)(x_value*x_value + y_value*y_value + z_value*z_value));
return vector ( x_value/tmp,
y_value/tmp,
z_value/tmp
);
}
// ---------------------------------------
T& x (
)
{
return x_value;
}
// ---------------------------------------
T& y (
)
{
return y_value;
}
// ---------------------------------------
T& z (
)
{
return z_value;
}
// ---------------------------------------
const T& x (
) const
{
return x_value;
}
// ---------------------------------------
const T& y (
) const
{
return y_value;
}
// ---------------------------------------
const T& z (
) const
{
return z_value;
}
// ---------------------------------------
T dot (
const vector& rhs
) const
{
return x_value*rhs.x_value + y_value*rhs.y_value + z_value*rhs.z_value;
}
// ---------------------------------------
vector cross (
const vector& rhs
) const
{
return vector (
y_value*rhs.z_value - z_value*rhs.y_value,
z_value*rhs.x_value - x_value*rhs.z_value,
x_value*rhs.y_value - y_value*rhs.x_value
);
}
// ---------------------------------------
vector operator+ (
const vector& rhs
) const
{
return vector (
x_value+rhs.x_value,
y_value+rhs.y_value,
z_value+rhs.z_value
);
}
// ---------------------------------------
vector operator- (
const vector& rhs
) const
{
return vector (
x_value-rhs.x_value,
y_value-rhs.y_value,
z_value-rhs.z_value
);
}
// ---------------------------------------
vector& operator= (
const vector& rhs
)
{
x_value = rhs.x_value;
y_value = rhs.y_value;
z_value = rhs.z_value;
return *this;
}
// ---------------------------------------
vector operator/ (
const T rhs
) const
{
return vector (
x_value/rhs,
y_value/rhs,
z_value/rhs
);
}
// ---------------------------------------
vector& operator += (
const vector& rhs
)
{
x_value += rhs.x_value;
y_value += rhs.y_value;
z_value += rhs.z_value;
return *this;
}
// ---------------------------------------
vector& operator -= (
const vector& rhs
)
{
x_value -= rhs.x_value;
y_value -= rhs.y_value;
z_value -= rhs.z_value;
return *this;
}
// ---------------------------------------
vector& operator *= (
const T rhs
)
{
x_value *= rhs;
y_value *= rhs;
z_value *= rhs;
return *this;
}
// ---------------------------------------
vector& operator /= (
const T rhs
)
{
x_value /= rhs;
y_value /= rhs;
z_value /= rhs;
return *this;
}
// ---------------------------------------
bool operator== (
const vector& rhs
) const
{
return (x_value == rhs.x_value &&
y_value == rhs.y_value &&
z_value == rhs.z_value );
}
// ---------------------------------------
bool operator!= (
const vector& rhs
) const
{
return !((*this) == rhs);
}
// ---------------------------------------
void swap (
vector& item
)
{
exchange(x_value,item.x_value);
exchange(y_value,item.y_value);
exchange(z_value,item.z_value);
}
// ---------------------------------------
private:
T x_value;
T y_value;
T z_value;
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template<typename T, typename U>
inline vector<T> operator* (
const vector<T> & lhs,
const U rhs
)
{
return vector<T> (
lhs.x()*rhs,
lhs.y()*rhs,
lhs.z()*rhs
);
}
// ----------------------------------------------------------------------------------------
template<typename T, typename U>
inline vector<T> operator* (
const U lhs,
const vector<T> & rhs
) { return rhs*lhs; }
// ----------------------------------------------------------------------------------------
template<typename T>
inline void swap (
vector<T> & a,
vector<T> & b
) { a.swap(b); }
// ----------------------------------------------------------------------------------------
template<typename T>
inline void serialize (
const vector<T> & item,
std::ostream& out
)
{
try
{
serialize(item.x(),out);
serialize(item.y(),out);
serialize(item.z(),out);
}
catch (serialization_error e)
{
throw serialization_error(e.info + "\n while serializing object of type vector");
}
}
template<typename T>
inline void deserialize (
vector<T> & item,
std::istream& in
)
{
try
{
deserialize(item.x(),in);
deserialize(item.y(),in);
deserialize(item.z(),in);
}
catch (serialization_error e)
{
item.x() = 0;
item.y() = 0;
item.z() = 0;
throw serialization_error(e.info + "\n while deserializing object of type vector");
}
}
// ----------------------------------------------------------------------------------------
template<typename T>
std::ostream& operator<< (
std::ostream& out,
const vector<T>& item
)
{
out << "(" << item.x() << ", " << item.y() << ", " << item.z() << ")";
return out;
}
template<typename T>
std::istream& operator>>(
std::istream& in,
vector<T>& item
)
{
// eat all the crap up to the '('
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n')
in.get();
// there should be a '(' if not then this is an error
if (in.get() != '(')
{
in.setstate(in.rdstate() | std::ios::failbit);
return in;
}
// eat all the crap up to the first number
while (in.peek() == ' ' || in.peek() == '\t')
in.get();
in >> item.x();
if (!in.good())
return in;
// eat all the crap up to the next number
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == ',')
in.get();
in >> item.y();
if (!in.good())
return in;
// eat all the crap up to the next number
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == ',')
in.get();
in >> item.z();
if (!in.good())
return in;
// eat all the crap up to the ')'
while (in.peek() == ' ' || in.peek() == '\t')
in.get();
// there should be a ')' if not then this is an error
if (in.get() != ')')
in.setstate(in.rdstate() | std::ios::failbit);
return in;
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class point
{
/*!
INITIAL VALUE
The initial value of this object is defined by its constructor.
CONVENTION
- x_ == x()
- y_ == y()
!*/
public:
point (
) : x_(0), y_(0) {}
point (
long x__,
long y__
)
{
x_ = x__;
y_ = y__;
}
point (
const point& p
)
{
x_ = p.x_;
y_ = p.y_;
}
template <typename T>
point (
const vector<T>& v
) :
x_(static_cast<long>(v.x()+0.5)),
y_(static_cast<long>(v.y()+0.5))
{}
long x (
) const { return x_; }
long y (
) const { return y_; }
long& x (
) { return x_; }
long& y (
) { return y_; }
const point operator+ (
const point& rhs
) const
{
return point(x()+rhs.x(), y()+rhs.y());
}
const point operator- (
const point& rhs
) const
{
return point(x()-rhs.x(), y()-rhs.y());
}
point& operator= (
const point& p
)
{
x_ = p.x_;
y_ = p.y_;
return *this;
}
point& operator+= (
const point& rhs
)
{
x_ += rhs.x_;
y_ += rhs.y_;
return *this;
}
point& operator-= (
const point& rhs
)
{
x_ -= rhs.x_;
y_ -= rhs.y_;
return *this;
}
bool operator== (
const point& p
) const { return p.x_ == x_ && p.y_ == y_; }
bool operator!= (
const point& p
) const { return p.x_ != x_ || p.y_ != y_; }
private:
long x_;
long y_;
};
// ----------------------------------------------------------------------------------------
inline void serialize (
const point& item,
std::ostream& out
)
{
try
{
serialize(item.x(),out);
serialize(item.y(),out);
}
catch (serialization_error& e)
{
throw serialization_error(e.info + "\n while serializing an object of type point");
}
}
inline void deserialize (
point& item,
std::istream& in
)
{
try
{
deserialize(item.x(),in);
deserialize(item.y(),in);
}
catch (serialization_error& e)
{
throw serialization_error(e.info + "\n while deserializing an object of type point");
}
}
inline std::ostream& operator<< (
std::ostream& out,
const point& item
)
{
out << "(" << item.x() << ", " << item.y() << ")";
return out;
}
inline std::istream& operator>>(
std::istream& in,
point& item
)
{
// eat all the crap up to the '('
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n')
in.get();
// there should be a '(' if not then this is an error
if (in.get() != '(')
{
in.setstate(in.rdstate() | std::ios::failbit);
return in;
}
// eat all the crap up to the first number
while (in.peek() == ' ' || in.peek() == '\t')
in.get();
bool is_negative = false;
if (in.peek() == '-')
{
in.get();
is_negative = true;
}
// read in the number and store it in item.x()
item.x() = 0;
while (in.peek() >= '0' && in.peek() <= '9')
{
long temp = in.get()-'0';
item.x() = item.x()*10 + temp;
}
if (is_negative)
item.x() *= -1;
if (!in.good())
return in;
// eat all the crap up to the next number
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == ',')
in.get();
is_negative = false;
if (in.peek() == '-')
{
in.get();
is_negative = true;
}
// read in the number and store it in item.y()
item.y() = 0;
while (in.peek() >= '0' && in.peek() <= '9')
{
long temp = in.get()-'0';
item.y() = item.y()*10 + temp;
}
if (is_negative)
item.y() *= -1;
if (!in.good())
return in;
// eat all the crap up to the ')'
while (in.peek() == ' ' || in.peek() == '\t')
in.get();
// there should be a ')' if not then this is an error
if (in.get() != ')')
in.setstate(in.rdstate() | std::ios::failbit);
return in;
}
// ----------------------------------------------------------------------------------------
template <typename T>
vector<T>::vector (
const point& p
) :
x_value(p.x()),
y_value(p.y()),
z_value(0)
{}
// ----------------------------------------------------------------------------------------
}
namespace std
{
/*!
Define std::less<vector<T> > so that you can use vectors in the associative containers.
!*/
template<typename T>
struct less<dlib::vector<T> > : public binary_function<dlib::vector<T> ,dlib::vector<T> ,bool>
{
inline bool operator() (const dlib::vector<T> & a, const dlib::vector<T> & b) const
{
if (a.x() < b.x()) return true;
else if (a.x() > b.x()) return false;
else if (a.y() < b.y()) return true;
else if (a.y() > b.y()) return false;
else if (a.z() < b.z()) return true;
else if (a.z() > b.z()) return false;
else return false;
}
};
/*!
Define std::less<point> so that you can use points in the associative containers.
!*/
template<>
struct less<dlib::point> : public binary_function<dlib::point,dlib::point,bool>
{
inline bool operator() (const dlib::point& a, const dlib::point& b) const
{
if (a.x() < b.x()) return true;
else if (a.x() > b.x()) return false;
else if (a.y() < b.y()) return true;
else if (a.y() > b.y()) return false;
else return false;
}
};
}
#endif // DLIB_VECTOr_H_
// Copyright (C) 2003 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_VECTOR_ABSTRACT_
#ifdef DLIB_VECTOR_ABSTRACT_
#include "../serialize.h"
#include <functional>
#include <iostream>
namespace dlib
{
class point;
template <
typename T
>
class vector
{
/*!
REQUIREMENTS ON T
T should be some object that provides an interface that is
compatible with double, float and the like.
INITIAL VALUE
x() == 0
y() == 0
z() == 0
WHAT THIS OBJECT REPRESENTS
This object represents a three dimensional vector.
THREAD SAFETY
Note that the vector object is not allowed to be reference counted.
This is to ensure a minimum amount of thread safety.
!*/
public:
typedef T type;
vector (
);
/*!
ensures
- #*this has been properly initialized
!*/
vector (
const T _x,
const T _y,
const T _z
);
/*!
ensures
- #*this properly initialized
- #x() == _x
- #y() == _y
- #z() == _z
!*/
vector (
const point& p
);
/*!
ensures
- #*this properly initialized
- #x() == p.x()
- #y() == p.y()
- #z() == 0
!*/
vector (
const vector& v
);
/*!
ensures
- #*this properly initialized
- #x() == v.x()
- #y() == v.y()
- #z() == v.z()
!*/
~vector (
);
/*!
ensures
- all resources associated with *this have been released
!*/
T length(
) const;
/*!
ensures
- returns the length of the vector
!*/
T& x (
);
/*!
ensures
- returns a reference to the x component of the vector
!*/
T& y (
);
/*!
ensures
- returns a reference to the y component of the vector
!*/
T& z (
);
/*!
ensures
- returns a reference to the z component of the vector
!*/
const T& x (
) const;
/*!
ensures
- returns a const reference to the x component of the vector
!*/
const T& y (
) const;
/*!
ensures
- returns a const reference to the y component of the vector
!*/
const T& z (
) const;
/*!
ensures
- returns a const reference to the z component of the vector
!*/
T dot (
const vector& rhs
) const;
/*!
ensures
- returns the result of the dot product between *this and rhs
!*/
vector cross (
const vector& rhs
) const;
/*!
ensures
- returns the result of the cross product between *this and rhs
!*/
vector normalize (
) const;
/*!
ensures
- returns a vector with length() == 1 and in the same direction as *this
!*/
vector operator+ (
const vector& rhs
) const;
/*!
ensures
- returns the result of adding *this to rhs
!*/
vector operator- (
const vector& rhs
) const;
/*!
ensures
- returns the result of subtracting rhs from *this
!*/
vector operator/ (
const T rhs
) const;
/*!
ensures
- returns the result of dividing *this by rhs
!*/
vector& operator= (
const vector& rhs
);
/*!
ensures
- #x() == rhs.x()
- #y() == rhs.y()
- #z() == rhs.z()
- returns #*this
!*/
vector& operator += (
const vector& rhs
);
/*!
ensures
- #*this == *this + rhs
- returns #*this
!*/
vector& operator -= (
const vector& rhs
);
/*!
ensures
- #*this == *this - rhs
- returns #*this
!*/
vector& operator *= (
const T rhs
);
/*!
ensures
- #*this == *this * rhs
- returns #*this
!*/
vector& operator /= (
const T rhs
);
/*!
ensures
- #*this == *this / rhs
- returns #*this
!*/
bool operator== (
const vector& rhs
) const;
/*!
ensures
- if (x() == rhs.x() && y() == rhs.y() && z() == rhs.z()) then
- returns true
- else
- returns false
!*/
bool operator!= (
const vector& rhs
) const;
/*!
ensures
- returns !((*this) == rhs)
!*/
void swap (
vector& item
);
/*!
ensures
- swaps *this and item
!*/
};
template<typename T, typename U>
vector<T> operator* (
const vector<T> & lhs,
const U rhs
);
/*!
ensures
- returns the result of multiplying the scalar rhs by lhs
!*/
template<typename T, typename U>
vector<T> operator* (
const U lhs,
const vector<T> & rhs
);
/*!
ensures
- returns the result of multiplying the scalar lhs by rhs
!*/
template<typename T>
inline void swap (
vector<T> & a,
vector<T> & b
) { a.swap(b); }
/*!
provides a global swap function
!*/
template<typename T>
void serialize (
const vector<T> & item,
std::ostream& out
);
/*!
provides serialization support
!*/
template<typename T>
void deserialize (
vector<T> & item,
std::istream& in
);
/*!
provides deserialization support
!*/
template<typename T>
std::ostream& operator<< (
std::ostream& out,
const vector<T>& item
);
/*!
ensures
- writes item to out in the form "(x, y, z)"
!*/
template<typename T>
std::istream& operator>>(
std::istream& in,
vector<T>& item
);
/*!
ensures
- reads a vector from the input stream in and stores it in #item.
The data in the input stream should be of the form (x, y, z)
!*/
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class point
{
/*!
INITIAL VALUE
The initial value of this object is defined by its constructor.
WHAT THIS OBJECT REPRESENTS
This object represents a point inside a Cartesian coordinate system.
!*/
public:
point (
);
/*!
ensures
- #x() == 0
- #y() == 0
!*/
point (
long x_
long y_
);
/*!
ensures
- #x() == x_
- #y() == y_
!*/
point (
const point& p
);
/*!
ensures
- #x() == p.x()
- #y() == p.y()
!*/
template <typename T>
point (
const vector<T>& v
);
/*!
ensures
- #x() == floor(v.x()+0.5)
- #y() == floor(v.y()+0.5)
!*/
long x (
) const;
/*!
ensures
- returns the x coordinate of this point
!*/
long y (
) const;
/*!
ensures
- returns the y coordinate of this point
!*/
long& x (
);
/*!
ensures
- returns a non-const reference to the x coordinate of
this point
!*/
long& y (
);
/*!
ensures
- returns a non-const reference to the y coordinate of
this point
!*/
const point operator+ (
const point& rhs
) const;
/*!
ensures
- returns point(x()+rhs.x(), y()+rhs.y())
!*/
const point operator- (
const point& rhs
) const;
/*!
ensures
- returns point(x()-rhs.x(), y()-rhs.y())
!*/
point& operator= (
const point& p
);
/*!
ensures
- #x() == p.x()
- #y() == p.y()
- returns #*this
!*/
point& operator+= (
const point& rhs
);
/*!
ensures
- #*this = *this + rhs
- returns #*this
!*/
point& operator-= (
const point& rhs
);
/*!
ensures
- #*this = *this - rhs
- returns #*this
!*/
bool operator== (
const point& p
) const;
/*!
ensures
- if (x() == p.x() && y() == p.y()) then
- returns true
- else
- returns false
!*/
bool operator!= (
const point& p
) const;
/*!
ensures
- returns !(*this == p)
!*/
};
// ----------------------------------------------------------------------------------------
void serialize (
const point& item,
std::ostream& out
);
/*!
provides serialization support
!*/
void deserialize (
point& item,
std::istream& in
);
/*!
provides deserialization support
!*/
std::ostream& operator<< (
std::ostream& out,
const point& item
);
/*!
ensures
- writes item to out in the form "(x, y)"
!*/
std::istream& operator>>(
std::istream& in,
point& item
);
/*!
ensures
- reads a point from the input stream in and stores it in #item.
The data in the input stream should be of the form (x, y)
!*/
// ----------------------------------------------------------------------------------------
}
namespace std
{
/*!
Define std::less<vector<T> > so that you can use vectors in the associative containers.
!*/
template<typename T>
struct less<dlib::vector<T> > : public binary_function<dlib::vector<T> ,dlib::vector<T> ,bool>
{
inline bool operator() (const dlib::vector<T> & a, const dlib::vector<T> & b) const
{
if (a.x() < b.x()) return true;
else if (a.x() > b.x()) return false;
else if (a.y() < b.y()) return true;
else if (a.y() > b.y()) return false;
else if (a.z() < b.z()) return true;
else if (a.z() > b.z()) return false;
else return false;
}
};
/*!
Define std::less<point> so that you can use points in the associative containers.
!*/
template<>
struct less<dlib::point> : public binary_function<dlib::point,dlib::point,bool>
{
inline bool operator() (const dlib::point& a, const dlib::point& b) const
{
if (a.x() < b.x()) return true;
else if (a.x() > b.x()) return false;
else if (a.y() < b.y()) return true;
else if (a.y() > b.y()) return false;
else return false;
}
};
}
#endif // DLIB_VECTOR_ABSTRACT_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GRAPh_
#define DLIB_GRAPh_
#include "graph/graph_kernel_1.h"
#include "memory_manager.h"
namespace dlib
{
template <
typename T,
typename E = char,
typename mem_manager = memory_manager<char>::kernel_1a
>
class graph
{
graph() {}
public:
//----------- kernels ---------------
// kernel_1a
typedef graph_kernel_1<T,E,mem_manager,false>
kernel_1a;
typedef graph_kernel_1<T,E,mem_manager,true>
kernel_1a_c;
};
}
#endif // DLIB_GRAPh_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GRAPH_KERNEl_1_
#define DLIB_GRAPH_KERNEl_1_
#include "../serialize.h"
#include "../noncopyable.h"
#include "../std_allocator.h"
#include "../smart_pointers.h"
#include "../algs.h"
#include <vector>
#include "../memory_manager.h"
#include "graph_kernel_abstract.h"
#include "../is_kind.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <typename node_type, typename graph, bool is_checked>
struct graph_checker_helper
{
/*!
This object is used to check preconditions based on the value of is_checked
!*/
static void check_neighbor (
unsigned long edge_index,
const node_type& self
)
{
// make sure requires clause is not broken
DLIB_CASSERT(edge_index < self.number_of_neighbors(),
"\tnode_type& graph::node_type::neighbor(edge_index)"
<< "\n\tYou have specified an invalid index"
<< "\n\tedge_index: " << edge_index
<< "\n\tnumber_of_neighbors(): " << self.number_of_neighbors()
<< "\n\tthis: " << &self
);
}
static void check_edge (
unsigned long edge_index,
const node_type& self
)
{
// make sure requires clause is not broken
DLIB_CASSERT(edge_index < self.number_of_neighbors(),
"\tE& graph::node_type::edge(edge_index)"
<< "\n\tYou have specified an invalid index"
<< "\n\tedge_index: " << edge_index
<< "\n\tnumber_of_neighbors(): " << self.number_of_neighbors()
<< "\n\tthis: " << &self
);
}
static void check_node (
unsigned long index,
const graph& self
)
{
// make sure requires clause is not broken
DLIB_CASSERT(index < self.number_of_nodes(),
"\tnode_type& graph::node(index)"
<< "\n\tYou have specified an invalid index"
<< "\n\tindex: " << index
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
}
static void check_has_edge (
unsigned long node_index1,
unsigned long node_index2,
const graph& self
)
{
// make sure requires clause is not broken
DLIB_CASSERT(node_index1 < self.number_of_nodes() &&
node_index2 < self.number_of_nodes(),
"\tvoid graph::has_edge(node_index1, node_index2)"
<< "\n\tYou have specified an invalid index"
<< "\n\tnode_index1: " << node_index1
<< "\n\tnode_index2: " << node_index2
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
}
static void check_add_edge (
unsigned long node_index1,
unsigned long node_index2,
const graph& self
)
{
DLIB_CASSERT(node_index1 < self.number_of_nodes() &&
node_index2 < self.number_of_nodes(),
"\tvoid graph::add_edge(node_index1, node_index2)"
<< "\n\tYou have specified an invalid index"
<< "\n\tnode_index1: " << node_index1
<< "\n\tnode_index2: " << node_index2
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
DLIB_CASSERT( self.has_edge(node_index1, node_index2) == false,
"\tvoid graph::add_edge(node_index1, node_index2)"
<< "\n\tYou can't add an edge if it already exists in the graph"
<< "\n\tnode_index1: " << node_index1
<< "\n\tnode_index2: " << node_index2
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
}
static void check_remove_edge (
unsigned long node_index1,
unsigned long node_index2,
const graph& self
)
{
DLIB_CASSERT(node_index1 < self.number_of_nodes() &&
node_index2 < self.number_of_nodes(),
"\tvoid graph::remove_edge(node_index1, node_index2)"
<< "\n\tYou have specified an invalid index"
<< "\n\tnode_index1: " << node_index1
<< "\n\tnode_index2: " << node_index2
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
DLIB_CASSERT( self.has_edge(node_index1, node_index2) == true,
"\tvoid graph::remove_edge(node_index1, node_index2)"
<< "\n\tYou can't remove an edge if it isn't in the graph"
<< "\n\tnode_index1: " << node_index1
<< "\n\tnode_index2: " << node_index2
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
}
static void check_remove_node (
unsigned long index,
const graph& self
)
{
// make sure requires clause is not broken
DLIB_CASSERT(index < self.number_of_nodes(),
"\tvoid graph::remove_node(index)"
<< "\n\tYou have specified an invalid index"
<< "\n\tindex: " << index
<< "\n\tnumber_of_nodes(): " << self.number_of_nodes()
<< "\n\tthis: " << &self
);
}
};
template <typename node_type, typename graph>
struct graph_checker_helper <node_type, graph, false>
{
static inline void check_edge ( unsigned long edge_index, const node_type& self) { }
static inline void check_neighbor ( unsigned long edge_index, const node_type& self) { }
static inline void check_node ( unsigned long index, const graph& self) { }
static inline void check_has_edge ( unsigned long , unsigned long , const graph& ) { }
static inline void check_add_edge ( unsigned long , unsigned long , const graph& ) { }
static inline void check_remove_edge ( unsigned long , unsigned long , const graph& ) { }
static inline void check_remove_node ( unsigned long index, const graph& self) { }
};
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E = char,
typename mem_manager = memory_manager<char>::kernel_1a,
bool is_checked = true
>
class graph_kernel_1 : noncopyable
{
/*!
INITIAL VALUE
- nodes.size() == 0
CONVENTION
- nodes.size() == number_of_nodes()
- for all valid i:
- *nodes[i] == node(i)
- nodes[i]->neighbors.size() == nodes[i]->number_of_neighbors(i)
- nodes[i]->idx == i == nodes[i]->index()
- for all valid n:
- nodes[i]->neighbors[n] == pointer to the n'th parent node of i
- *nodes[i]->neighbors[n] == node(i).neighbor(n)
- *nodes[i]->edges[n] == node(i).edge(n)
!*/
public:
struct node_type;
private:
typedef graph_checker_helper<node_type, graph_kernel_1, is_checked> checker;
public:
typedef T type;
typedef E edge_type;
typedef mem_manager mem_manager_type;
graph_kernel_1(
) {}
virtual ~graph_kernel_1(
) {}
void clear(
) { nodes.clear(); }
void set_number_of_nodes (
unsigned long new_size
);
unsigned long number_of_nodes (
) const { return nodes.size(); }
node_type& node (
unsigned long index
) { checker::check_node(index,*this); return *nodes[index]; }
const node_type& node (
unsigned long index
) const { checker::check_node(index,*this); return *nodes[index]; }
bool has_edge (
unsigned long node_index1,
unsigned long node_index2
) const;
void add_edge (
unsigned long node_index1,
unsigned long node_index2
);
void remove_edge (
unsigned long node_index1,
unsigned long node_index2
);
unsigned long add_node (
);
void remove_node (
unsigned long index
);
void swap (
graph_kernel_1& item
) { nodes.swap(item.nodes); }
public:
struct node_type
{
T data;
typedef graph_kernel_1 graph_type;
unsigned long index(
) const { return idx; }
unsigned long number_of_neighbors (
) const { return neighbors.size(); }
const node_type& neighbor (
unsigned long edge_index
) const { checker::check_neighbor(edge_index,*this); return *neighbors[edge_index]; }
node_type& neighbor (
unsigned long edge_index
) { checker::check_neighbor(edge_index,*this); return *neighbors[edge_index]; }
const E& edge (
unsigned long edge_index
) const { checker::check_edge(edge_index,*this); return *edges[edge_index]; }
E& edge (
unsigned long edge_index
) { checker::check_edge(edge_index,*this); return *edges[edge_index]; }
private:
friend class graph_kernel_1;
typedef std_allocator<node_type*,mem_manager> alloc_type;
typedef std_allocator<shared_ptr<E>,mem_manager> alloc_edge_type;
std::vector<node_type*,alloc_type> neighbors;
std::vector<shared_ptr<E>,alloc_edge_type> edges;
unsigned long idx;
};
private:
typedef std_allocator<shared_ptr<node_type>,mem_manager> alloc_type;
typedef std::vector<shared_ptr<node_type>, alloc_type> vector_type;
vector_type nodes;
};
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
inline void swap (
graph_kernel_1<T,E,mem_manager,is_checked>& a,
graph_kernel_1<T,E,mem_manager,is_checked>& b
) { a.swap(b); }
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
struct is_graph<graph_kernel_1<T,E,mem_manager, is_checked> >
{
static const bool value = true;
};
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
void serialize (
const graph_kernel_1<T,E,mem_manager,is_checked>& item,
std::ostream& out
)
{
try
{
serialize(item.number_of_nodes(), out);
// serialize each node
for (unsigned long i = 0; i < item.number_of_nodes(); ++i)
{
serialize(item.node(i).data, out);
// serialize all the edges
for (unsigned long n = 0; n < item.node(i).number_of_neighbors(); ++n)
{
// only serialize edges that we haven't already serialized
if (item.node(i).neighbor(n).index() >= i)
{
serialize(item.node(i).neighbor(n).index(), out);
serialize(item.node(i).edge(n), out);
}
}
const unsigned long stop_mark = 0xFFFFFFFF;
serialize(stop_mark, out);
}
}
catch (serialization_error& e)
{
throw serialization_error(e.info + "\n while serializing object of type graph_kernel_1");
}
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
void deserialize (
graph_kernel_1<T,E,mem_manager,is_checked>& item,
std::istream& in
)
{
try
{
unsigned long size;
deserialize(size, in);
item.clear();
item.set_number_of_nodes(size);
// deserialize each node
for (unsigned long i = 0; i < item.number_of_nodes(); ++i)
{
deserialize(item.node(i).data, in);
const unsigned long stop_mark = 0xFFFFFFFF;
// Add all the edges going to this node's neighbors
unsigned long index;
deserialize(index, in);
while (index != stop_mark)
{
item.add_edge(i, index);
// find the edge
unsigned long j = 0;
for (j = 0; j < item.node(i).number_of_neighbors(); ++j)
if (item.node(i).neighbor(j).index() == index)
break;
deserialize(item.node(i).edge(j), in);
deserialize(index, in);
}
}
}
catch (serialization_error& e)
{
throw serialization_error(e.info + "\n while deserializing object of type graph_kernel_1");
}
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// member function definitions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
void graph_kernel_1<T,E,mem_manager,is_checked>::
set_number_of_nodes (
unsigned long new_size
)
{
try
{
nodes.resize(new_size);
for (unsigned long i = 0; i < nodes.size(); ++i)
{
nodes[i].reset(new node_type);
nodes[i]->idx = i;
}
}
catch (...)
{
clear();
throw;
}
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
bool graph_kernel_1<T,E,mem_manager,is_checked>::
has_edge (
unsigned long node_index1,
unsigned long node_index2
) const
{
checker::check_has_edge(node_index1, node_index2, *this);
node_type& n = *nodes[node_index1];
// search all the child nodes to see if there is a link to the right node
for (unsigned long i = 0; i < n.neighbors.size(); ++i)
{
if (n.neighbors[i]->idx == node_index2)
return true;
}
return false;
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
void graph_kernel_1<T,E,mem_manager,is_checked>::
add_edge (
unsigned long node_index1,
unsigned long node_index2
)
{
checker::check_add_edge(node_index1, node_index2, *this);
try
{
node_type& n1 = *nodes[node_index1];
node_type& n2 = *nodes[node_index2];
n1.neighbors.push_back(&n2);
shared_ptr<E> e(new E);
n1.edges.push_back(e);
// don't add this twice if this is an edge from node_index1 back to itself
if (node_index1 != node_index2)
{
n2.neighbors.push_back(&n1);
n2.edges.push_back(e);
}
}
catch (...)
{
clear();
throw;
}
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
void graph_kernel_1<T,E,mem_manager,is_checked>::
remove_edge (
unsigned long node_index1,
unsigned long node_index2
)
{
checker::check_remove_edge(node_index1, node_index2, *this);
node_type& n1 = *nodes[node_index1];
node_type& n2 = *nodes[node_index2];
// remove the record of the link from n1
unsigned long pos = find(n1.neighbors.begin(), n1.neighbors.end(), &n2) - n1.neighbors.begin();
n1.neighbors.erase(n1.neighbors.begin() + pos);
n1.edges.erase(n1.edges.begin() + pos);
// check if this is an edge that goes from node_index1 back to itself
if (node_index1 != node_index2)
{
// remove the record of the link from n2
unsigned long pos = find(n2.neighbors.begin(), n2.neighbors.end(), &n1) - n2.neighbors.begin();
n2.neighbors.erase(n2.neighbors.begin() + pos);
n2.edges.erase(n2.edges.begin() + pos);
}
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
unsigned long graph_kernel_1<T,E,mem_manager,is_checked>::
add_node (
)
{
try
{
shared_ptr<node_type> n(new node_type);
n->idx = nodes.size();
nodes.push_back(n);
return n->idx;
}
catch (...)
{
clear();
throw;
}
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename E,
typename mem_manager,
bool is_checked
>
void graph_kernel_1<T,E,mem_manager,is_checked>::
remove_node (
unsigned long index
)
{
checker::check_remove_node(index,*this);
node_type& n = *nodes[index];
// remove all edges pointing to this node from its neighbors
for (unsigned long i = 0; i < n.neighbors.size(); ++i)
{
// remove the edge from this specific parent
unsigned long pos = find(n.neighbors[i]->neighbors.begin(), n.neighbors[i]->neighbors.end(), &n) -
n.neighbors[i]->neighbors.begin();
n.neighbors[i]->neighbors.erase(n.neighbors[i]->neighbors.begin() + pos);
n.neighbors[i]->edges.erase(n.neighbors[i]->edges.begin() + pos);
}
// now remove this node by replacing it with the last node in the nodes vector
nodes[index] = nodes[nodes.size()-1];
// update the index for the node we just moved
nodes[index]->idx = index;
// now remove the duplicated node at the end of the vector
nodes.pop_back();
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_GRAPH_KERNEl_1_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_GRAPH_KERNEl_ABSTRACT_
#ifdef DLIB_GRAPH_KERNEl_ABSTRACT_
#include "../serialize.h"
#include "../memory_manager/memory_manager_kernel_abstract.h"
#include "../noncopyable.h"
namespace dlib
{
template <
typename T,
typename E = char,
typename mem_manager = memory_manager<char>::kernel_1a
>
class graph : noncopyable
{
/*!
REQUIREMENTS ON T
T must be swappable by a global swap() and
T must have a default constructor
REQUIREMENTS ON E
E must be swappable by a global swap() and
E must have a default constructor
REQUIREMENTS ON mem_manager
must be an implementation of memory_manager/memory_manager_kernel_abstract.h or
must be an implementation of memory_manager_global/memory_manager_global_kernel_abstract.h or
must be an implementation of memory_manager_stateless/memory_manager_stateless_kernel_abstract.h
mem_manager::type can be set to anything.
POINTERS AND REFERENCES TO INTERNAL DATA
The only functions that invalidate pointers or references to internal data are
clear(), remove_node(), add_node(), set_number_of_nodes(), and the object's destructor.
INITIAL VALUE
number_of_nodes() == 0
WHAT THIS OBJECT REPRESENTS
This object represents an undirected graph which is a set of nodes with undirected
edges connecting various nodes.
!*/
public:
typedef T type;
typedef E edge_type;
typedef mem_manager mem_manager_type;
graph(
);
/*!
ensures
- #*this is properly initialized
throws
- std::bad_alloc or any exception thrown by T's constructor.
!*/
virtual ~graph(
);
/*!
ensures
- all resources associated with *this has been released
!*/
void clear(
);
/*!
ensures
- #*this has its initial value
throws
- std::bad_alloc or any exception thrown by T's constructor.
If this exception is thrown then *this is unusable
until clear() is called and succeeds
!*/
void set_number_of_nodes (
unsigned long new_size
);
/*!
ensures
- #number_of_nodes() == new_size
- for all i < new_size:
- number_of_neighbors(i) == 0
throws
- std::bad_alloc or any exception thrown by T's constructor.
If this exception is thrown then this object reverts back
to its initial state.
!*/
unsigned long number_of_nodes (
) const;
/*!
ensures
- returns the number of nodes in this graph
!*/
struct node_type
{
T data;
typedef graph graph_type;
unsigned long index(
) const;
/*!
ensures
- let G be the graph that contains the node *this
- returns a number N such that G.node(N) == *this
(i.e. returns the index of this node in the graph)
!*/
unsigned long number_of_neighbors (
) const;
/*!
ensures
- returns the number of nodes in this graph that are
adjacent to this node. I.e. the number of nodes
that are directly connected to this node via an edge.
!*/
const node_type& neighbor (
unsigned long edge_index
) const;
/*!
requires
- edge_index < number_of_neighbors()
ensures
- returns a const reference to the edge_index'th neighbor of *this
!*/
node_type& neighbor (
unsigned long edge_index
);
/*!
requires
- edge_index < number_of_neighbors()
ensures
- returns a non-const reference to the edge_index'th neighbor of *this
!*/
const E& edge (
unsigned long edge_index
) const;
/*!
requires
- edge_index < number_of_neighbors()
ensures
- returns a const reference to the edge_index'th edge data for the
edge connecting to neighbor this->neighbor(edge_index)
!*/
E& edge (
unsigned long edge_index
);
/*!
requires
- edge_index < number_of_neighbors()
ensures
- returns a non-const reference to the edge_index'th edge data for the
edge connecting to neighbor this->neighbor(edge_index)
!*/
};
node_type& node (
unsigned long index
);
/*!
requires
- index < number_of_nodes()
ensures
- returns a non-const reference to the node with the given index
!*/
const node_type& node (
unsigned long index
) const;
/*!
requires
- index < number_of_nodes()
ensures
- returns a const reference to the node with the given index
!*/
bool has_edge (
unsigned long node_index1,
unsigned long node_index2
) const;
/*!
requires
- node_index1 < number_of_nodes()
- node_index2 < number_of_nodes()
ensures
- if (there is an edge connecting node(node_index1) and node(node_index2)) then
- returns true
- else
- returns false
!*/
void add_edge (
unsigned long node_index1,
unsigned long node_index2
);
/*!
requires
- node_index1 < number_of_nodes()
- node_index2 < number_of_nodes()
- has_edge(node_index1, node_index2) == false
ensures
- #has_edge(node_index1, node_index2) == true
throws
- std::bad_alloc
If this exception is thrown then this object reverts back
to its initial state.
!*/
void remove_edge (
unsigned long node_index1,
unsigned long node_index2
);
/*!
requires
- node_index1 < number_of_nodes()
- node_index2 < number_of_nodes()
- has_edge(node_index1, node_index2) == true
ensures
- #has_edge(node_index1, node_index2) == false
throws
- std::bad_alloc
If this exception is thrown then this object reverts back
to its initial state.
!*/
unsigned long add_node (
);
/*!
ensures
- adds a node with index N == number_of_nodes() such that:
- #node(N).number_of_neighbors() == 0
- #number_of_nodes() == number_of_nodes() + 1
- returns N
throws
- std::bad_alloc or any exception thrown by T's constructor.
If this exception is thrown then this object reverts back
to its initial state.
!*/
void remove_node (
unsigned long index
);
/*!
requires
- index < number_of_nodes()
ensures
- removes the node with the given index from the graph.
- removes all edges linking the removed node to the rest
of the graph.
- the remaining node indexes are remapped so that they remain
contiguous. (This means that for all valid N, node(N) doesn't
necessarily reference the same node as #node(N))
- #number_of_nodes() == number_of_nodes() - 1
throws
- std::bad_alloc or any exception thrown by T's constructor.
If this exception is thrown then this object reverts back
to its initial state.
!*/
void swap (
graph& item
);
/*!
ensures
- swaps *this and item
!*/
};
template <
typename T,
typename E,
typename mem_manager
>
inline void swap (
graph<T,E,mem_manager>& a,
graph<T,E,mem_manager>& b
) { a.swap(b); }
/*!
provides a global swap function
!*/
template <
typename T,
typename E,
typename mem_manager
>
void serialize (
const graph<T,E,mem_manager>& item,
std::ostream& out
);
/*!
provides deserialization support
!*/
template <
typename T,
typename E,
typename mem_manager
>
void deserialize (
graph<T,E,mem_manager>& item,
std::istream& in
);
/*!
provides deserialization support
!*/
}
#endif // DLIB_GRAPH_KERNEl_ABSTRACT_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GRAPH_UTILs_H_
#define DLIB_GRAPH_UTILs_H_
#include "graph_utils/graph_utils.h"
#endif // DLIB_GRAPH_UTILs_H_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GRAPH_UTILs_
#define DLIB_GRAPH_UTILs_
#include "../algs.h"
#include <vector>
#include "graph_utils_abstract.h"
#include "../is_kind.h"
#include "../enable_if.h"
#include <algorithm>
#include "../set.h"
#include "../memory_manager.h"
#include "../set_utils.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <typename T>
typename T::edge_type& edge(
T& g,
unsigned long idx,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT(g.has_edge(idx,n) == true,
"\tT::edge_type& edge(g, i, j)"
<< "\n\tyou have requested an invalid edge"
<< "\n\ti: " << idx
<< "\n\tj: " << n
);
for (unsigned long i = 0; i < g.node(idx).number_of_neighbors(); ++i)
{
if (g.node(idx).neighbor(i).index() == n)
return g.node(idx).edge(i);
}
// put this here just so compilers don't complain about a lack of
// a return here
DLIB_CASSERT(false,
"\tT::edge_type& edge(g, i, j)"
<< "\n\tyou have requested an invalid edge"
<< "\n\ti: " << idx
<< "\n\tj: " << n
);
}
template <typename T>
const typename T::edge_type& edge(
const T& g,
unsigned long idx,
unsigned long n
)
{
// make sure requires clause is not broken
DLIB_ASSERT(g.has_edge(idx,n) == true,
"\tT::edge_type& edge(g, i, j)"
<< "\n\tyou have requested an invalid edge"
<< "\n\ti: " << idx
<< "\n\tj: " << n
);
for (unsigned long i = 0; i < g.node(idx).number_of_neighbors(); ++i)
{
if (g.node(idx).neighbor(i).index() == n)
return g.node(idx).edge(i);
}
// put this here just so compilers don't complain about a lack of
// a return here
DLIB_CASSERT(false,
"\tT::edge_type& edge(g, i, j)"
<< "\n\tyou have requested an invalid edge"
<< "\n\ti: " << idx
<< "\n\tj: " << n
);
}
// ----------------------------------------------------------------------------------------
namespace graph_helpers
{
template <typename T, typename U>
inline bool is_same_object (
const T& a,
const U& b
)
{
if (is_same_type<const T,const U>::value == false)
return false;
if ((void*)&a == (void*)&b)
return true;
else
return false;
}
template <
typename T
>
bool search_for_directed_cycles (
const T& node,
std::vector<bool>& visited,
std::vector<bool>& temp
)
/*!
requires
- visited.size() >= number of nodes in the graph that contains the given node
- temp.size() >= number of nodes in the graph that contains the given node
- for all i in temp:
- temp[i] == false
ensures
- checks the connected subgraph containing the given node for directed cycles
and returns true if any are found and false otherwise.
- for all nodes N in the connected subgraph containing the given node:
- #visited[N.index()] == true
- for all i in temp:
- #temp[i] == false
!*/
{
if (temp[node.index()] == true)
return true;
visited[node.index()] = true;
temp[node.index()] = true;
for (unsigned long i = 0; i < node.number_of_children(); ++i)
{
if (search_for_directed_cycles(node.child(i), visited, temp))
return true;
}
temp[node.index()] = false;
return false;
}
// ------------------------------------------------------------------------------------
template <
typename T
>
typename enable_if<is_directed_graph<typename T::graph_type>,bool>::type search_for_undirected_cycles (
const T& node,
std::vector<bool>& visited,
unsigned long prev = -1
)
/*!
requires
- visited.size() >= number of nodes in the graph that contains the given node
- for all nodes N in the connected subgraph containing the given node:
- visited[N.index] == false
ensures
- checks the connected subgraph containing the given node for directed cycles
and returns true if any are found and false otherwise.
- for all nodes N in the connected subgraph containing the given node:
- #visited[N.index()] == true
!*/
{
using namespace std;
if (visited[node.index()] == true)
return true;
visited[node.index()] = true;
for (unsigned long i = 0; i < node.number_of_children(); ++i)
{
if (node.child(i).index() != prev &&
search_for_undirected_cycles(node.child(i), visited, node.index()))
return true;
}
for (unsigned long i = 0; i < node.number_of_parents(); ++i)
{
if (node.parent(i).index() != prev &&
search_for_undirected_cycles(node.parent(i), visited, node.index()))
return true;
}
return false;
}
// ------------------------------------------------------------------------------------
template <
typename T
>
typename enable_if<is_graph<typename T::graph_type>,bool>::type search_for_undirected_cycles (
const T& node,
std::vector<bool>& visited,
unsigned long prev = -1
)
/*!
requires
- visited.size() >= number of nodes in the graph that contains the given node
- for all nodes N in the connected subgraph containing the given node:
- visited[N.index] == false
ensures
- checks the connected subgraph containing the given node for directed cycles
and returns true if any are found and false otherwise.
- for all nodes N in the connected subgraph containing the given node:
- #visited[N.index()] == true
!*/
{
using namespace std;
if (visited[node.index()] == true)
return true;
visited[node.index()] = true;
for (unsigned long i = 0; i < node.number_of_neighbors(); ++i)
{
if (node.neighbor(i).index() != prev &&
search_for_undirected_cycles(node.neighbor(i), visited, node.index()))
return true;
}
return false;
}
}
// ------------------------------------------------------------------------------------
template <
typename graph_type1,
typename graph_type2
>
typename enable_if<is_graph<graph_type1> >::type copy_graph_structure (
const graph_type1& src,
graph_type2& dest
)
{
COMPILE_TIME_ASSERT(is_graph<graph_type1>::value);
COMPILE_TIME_ASSERT(is_graph<graph_type2>::value);
if (graph_helpers::is_same_object(src,dest))
return;
dest.clear();
dest.set_number_of_nodes(src.number_of_nodes());
// copy all the edges from src into dest
for (unsigned long i = 0; i < src.number_of_nodes(); ++i)
{
for (unsigned long j = 0; j < src.node(i).number_of_neighbors(); ++j)
{
const unsigned long nidx = src.node(i).neighbor(j).index();
if (nidx >= i)
{
dest.add_edge(i,nidx);
}
}
}
}
template <
typename graph_type1,
typename graph_type2
>
typename enable_if<is_directed_graph<graph_type1> >::type copy_graph_structure (
const graph_type1& src,
graph_type2& dest
)
{
COMPILE_TIME_ASSERT(is_directed_graph<graph_type1>::value);
COMPILE_TIME_ASSERT(is_directed_graph<graph_type2>::value || is_graph<graph_type2>::value );
if (graph_helpers::is_same_object(src,dest))
return;
dest.clear();
dest.set_number_of_nodes(src.number_of_nodes());
// copy all the edges from src into dest
for (unsigned long i = 0; i < src.number_of_nodes(); ++i)
{
for (unsigned long j = 0; j < src.node(i).number_of_children(); ++j)
{
const unsigned long nidx = src.node(i).child(j).index();
if (dest.has_edge(i,nidx) == false)
{
dest.add_edge(i,nidx);
}
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename T,
typename S
>
typename enable_if<is_graph<typename T::graph_type> >::type find_connected_nodes (
const T& n,
S& visited
)
{
if (visited.is_member(n.index()) == false)
{
unsigned long temp = n.index();
visited.add(temp);
for (unsigned long i = 0; i < n.number_of_neighbors(); ++i)
find_connected_nodes(n.neighbor(i), visited);
}
}
template <
typename T,
typename S
>
typename enable_if<is_directed_graph<typename T::graph_type> >::type find_connected_nodes (
const T& n,
S& visited
)
{
if (visited.is_member(n.index()) == false)
{
unsigned long temp = n.index();
visited.add(temp);
for (unsigned long i = 0; i < n.number_of_parents(); ++i)
find_connected_nodes(n.parent(i), visited);
for (unsigned long i = 0; i < n.number_of_children(); ++i)
find_connected_nodes(n.child(i), visited);
}
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_is_connected (
const T& g
)
{
if (g.number_of_nodes() == 0)
return true;
set<unsigned long>::kernel_1b_c visited;
find_connected_nodes(g.node(0), visited);
return (visited.size() == g.number_of_nodes());
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_contains_directed_cycle (
const T& graph
)
{
using namespace std;
using namespace graph_helpers;
std::vector<bool> visited(graph.number_of_nodes(), false);
std::vector<bool> temp(graph.number_of_nodes(), false);
while (true)
{
// find the first node that hasn't been visited yet
unsigned long i;
for (i = 0; i < visited.size(); ++i)
{
if (visited[i] == false)
break;
}
// if we didn't find any non-visited nodes then we are done
if (i == visited.size())
return false;
if (search_for_directed_cycles(graph.node(i), visited, temp))
return true;
}
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_contains_undirected_cycle (
const T& graph
)
{
using namespace std;
using namespace graph_helpers;
std::vector<bool> visited(graph.number_of_nodes(), false);
while (true)
{
// find the first node that hasn't been visited yet
unsigned long i;
for (i = 0; i < visited.size(); ++i)
{
if (visited[i] == false)
break;
}
// if we didn't find any non-visited nodes then we are done
if (i == visited.size())
return false;
if (search_for_undirected_cycles(graph.node(i), visited))
return true;
}
}
// ----------------------------------------------------------------------------------------
template <
typename directed_graph_type,
typename graph_type
>
void create_moral_graph (
const directed_graph_type& g,
graph_type& moral_graph
)
{
// make sure requires clause is not broken
DLIB_ASSERT(graph_contains_directed_cycle(g) == false,
"\tvoid create_moral_graph(g, moral_graph)"
<< "\n\tYou can only make moral graphs if g doesn't have directed cycles"
);
COMPILE_TIME_ASSERT(is_graph<graph_type>::value);
COMPILE_TIME_ASSERT(is_directed_graph<directed_graph_type>::value);
copy_graph_structure(g, moral_graph);
// now marry all the parents (i.e. add edges between parent nodes)
for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
{
// loop over all combinations of parents of g.node(i)
for (unsigned long j = 0; j < g.node(i).number_of_parents(); ++j)
{
for (unsigned long k = 0; k < g.node(i).number_of_parents(); ++k)
{
const unsigned long p1 = g.node(i).parent(j).index();
const unsigned long p2 = g.node(i).parent(k).index();
if (p1 == p2)
continue;
if (moral_graph.has_edge(p1,p2) == false)
moral_graph.add_edge(p1,p2);
}
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename sets_of_int
>
bool is_clique (
const graph_type& g,
const sets_of_int& clique
)
{
// make sure requires clause is not broken
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
"\tvoid is_clique(g, clique)"
<< "\n\tinvalid graph"
);
#ifdef ENABLE_ASSERTS
clique.reset();
while (clique.move_next())
{
const unsigned long x = clique.element();
DLIB_ASSERT( x < g.number_of_nodes(),
"\tvoid is_clique(g, clique)"
<< "\n\tthe clique set contained an invalid node index"
<< "\n\tx: " << x
<< "\n\tg.number_of_nodes(): " << g.number_of_nodes()
);
}
#endif
COMPILE_TIME_ASSERT(is_graph<graph_type>::value);
std::vector<unsigned long> v;
v.reserve(clique.size());
clique.reset();
while (clique.move_next())
{
v.push_back(clique.element());
}
for (unsigned long i = 0; i < v.size(); ++i)
{
for (unsigned long j = 0; j < v.size(); ++j)
{
if (v[i] == v[j])
continue;
if (g.has_edge(v[i], v[j]) == false)
return false;
}
}
return true;
}
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename sets_of_int
>
bool is_maximal_clique (
const graph_type& g,
const sets_of_int& clique
)
{
// make sure requires clause is not broken
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
"\tvoid is_maximal_clique(g, clique)"
<< "\n\tinvalid graph"
);
DLIB_ASSERT(is_clique(g,clique) == true,
"\tvoid is_maximal_clique(g, clique)"
<< "\n\tinvalid graph"
);
#ifdef ENABLE_ASSERTS
clique.reset();
while (clique.move_next())
{
const unsigned long x = clique.element();
DLIB_ASSERT( x < g.number_of_nodes(),
"\tvoid is_maximal_clique(g, clique)"
<< "\n\tthe clique set contained an invalid node index"
<< "\n\tx: " << x
<< "\n\tg.number_of_nodes(): " << g.number_of_nodes()
);
}
#endif
COMPILE_TIME_ASSERT(is_graph<graph_type>::value);
if (clique.size() == 0)
return true;
// get an element in the clique and make sure that
// none of its neighbors that aren't in the clique are connected
// to all the elements of the clique.
clique.reset();
clique.move_next();
const unsigned long idx = clique.element();
for (unsigned long i = 0; i < g.node(idx).number_of_neighbors(); ++i)
{
const unsigned long n = g.node(idx).neighbor(i).index();
if (clique.is_member(n))
continue;
// now loop over all the clique members and make sure they don't all
// share an edge with node n
bool all_share_edge = true;
clique.reset();
while (clique.move_next())
{
if (g.has_edge(clique.element(), n) == false)
{
all_share_edge = false;
break;
}
}
if (all_share_edge == true)
return false;
}
return true;
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
typename enable_if<is_directed_graph<T>,bool>::type graph_contains_length_one_cycle (
const T& graph
)
{
for (unsigned long i = 0; i < graph.number_of_nodes(); ++i)
{
// make sure none of this guys children are actually itself
for (unsigned long n = 0; n < graph.node(i).number_of_children(); ++n)
{
if (graph.node(i).child(n).index() == i)
return true;
}
}
return false;
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
typename enable_if<is_graph<T>,bool>::type graph_contains_length_one_cycle (
const T& graph
)
{
for (unsigned long i = 0; i < graph.number_of_nodes(); ++i)
{
// make sure none of this guys neighbors are actually itself
for (unsigned long n = 0; n < graph.node(i).number_of_neighbors(); ++n)
{
if (graph.node(i).neighbor(n).index() == i)
return true;
}
}
return false;
}
// ----------------------------------------------------------------------------------------
namespace graph_helpers
{
struct pair
{
unsigned long index;
unsigned long num_neighbors;
bool operator< (const pair& p) const { return num_neighbors < p.num_neighbors; }
};
template <
typename T,
typename S,
typename V
>
void search_graph_for_triangulate (
const T& n,
S& visited,
V& order_visited
)
{
// base case of recursion. stop when we hit a node we have
// already visited.
if (visited.is_member(n.index()))
return;
// record that we have visited this node
order_visited.push_back(n.index());
unsigned long temp = n.index();
visited.add(temp);
// we want to visit all the neighbors of this node but do
// so by visiting the nodes with the most neighbors first. So
// lets make a vector that lists the nodes in the order we
// want to visit them
std::vector<pair> neighbors;
for (unsigned long i = 0; i < n.number_of_neighbors(); ++i)
{
pair p;
p.index = i;
p.num_neighbors = n.neighbor(i).number_of_neighbors();
neighbors.push_back(p);
}
// now sort the neighbors array so that the neighbors with the
// most neighbors come first.
std::sort(neighbors.rbegin(), neighbors.rend());
// now visit all the nodes
for (unsigned long i = 0; i < neighbors.size(); ++i)
{
search_graph_for_triangulate(n.neighbor(neighbors[i].index), visited, order_visited);
}
}
} // end namespace graph_helpers
template <
typename graph_type,
typename set_of_sets_of_int
>
void triangulate_graph_and_find_cliques (
graph_type& g,
set_of_sets_of_int& cliques
)
{
// make sure requires clause is not broken
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
"\tvoid triangulate_graph_and_find_cliques(g, cliques)"
<< "\n\tInvalid graph"
);
DLIB_ASSERT(graph_is_connected(g) == true,
"\tvoid triangulate_graph_and_find_cliques(g, cliques)"
<< "\n\tInvalid graph"
);
COMPILE_TIME_ASSERT(is_graph<graph_type>::value);
using namespace graph_helpers;
using namespace std;
typedef typename set_of_sets_of_int::type set_of_int;
cliques.clear();
// first we find the node with the most neighbors
unsigned long max_index = 0;
unsigned long num_neighbors = 0;
for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
{
if (g.node(i).number_of_neighbors() > num_neighbors)
{
max_index = i;
num_neighbors = g.node(i).number_of_neighbors();
}
}
// now we do a depth first search of the entire graph starting
// with the node we just found. We record the order in which
// we visit each node in the vector order_visited.
std::vector<unsigned long> order_visited;
set_of_int visited;
search_graph_for_triangulate(g.node(max_index), visited, order_visited);
set_of_int clique;
// now add edges to the graph to make it triangulated
while (visited.size() > 0)
{
// we are going to enumerate over the nodes in the reverse of the
// order in which they were visited. So get the last node out.
const unsigned long idx = order_visited.back();
order_visited.pop_back();
visited.destroy(idx);
// as a start add this node to our current clique
unsigned long temp = idx;
clique.clear();
clique.add(temp);
// now we want to make a clique that contains node g.node(idx) and
// all of its neighbors that are still recorded in the visited set
// (except for neighbors that have only one edge).
for (unsigned long i = 0; i < g.node(idx).number_of_neighbors(); ++i)
{
// get the index of the i'th neighbor
unsigned long nidx = g.node(idx).neighbor(i).index();
// add it to the clique if it is still in visited and it isn't
// a node with only one neighbor
if (visited.is_member(nidx) == true &&
g.node(nidx).number_of_neighbors() != 1)
{
// add edges between this new node and all the nodes
// that are already in the clique
clique.reset();
while (clique.move_next())
{
if (g.has_edge(nidx, clique.element()) == false)
g.add_edge(nidx, clique.element());
}
// now also record that we added this node to the clique
clique.add(nidx);
}
}
if (cliques.is_member(clique) == false && is_maximal_clique(g,clique) )
{
cliques.add(clique);
}
// now it is possible that we are missing some cliques of size 2 since
// above we didn't add nodes with only one edge to any of our cliques.
// Now lets make sure all these nodes are accounted for
for (unsigned long i = 0; i < g.number_of_nodes(); ++i)
{
clique.clear();
if (g.node(i).number_of_neighbors() == 1)
{
unsigned long temp = i;
clique.add(temp);
temp = g.node(i).neighbor(0).index();
clique.add(temp);
if (cliques.is_member(clique) == false)
cliques.add(clique);
}
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename join_tree_type
>
void create_join_tree (
const graph_type& g,
join_tree_type& join_tree
)
{
// make sure requires clause is not broken
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
"\tvoid create_join_tree(g, join_tree)"
<< "\n\tInvalid graph"
);
DLIB_ASSERT(graph_is_connected(g) == true,
"\tvoid create_join_tree(g, join_tree)"
<< "\n\tInvalid graph"
);
COMPILE_TIME_ASSERT(is_graph<graph_type>::value);
COMPILE_TIME_ASSERT(is_graph<join_tree_type>::value);
typedef typename join_tree_type::type set_of_int;
typedef typename join_tree_type::edge_type set_of_int_edge;
typedef typename set<set_of_int>::kernel_1b_c set_of_sets_of_int;
copy_graph_structure(g, join_tree);
// don't even bother in this case
if (g.number_of_nodes() == 0)
return;
set_of_sets_of_int cliques;
set_of_int s;
triangulate_graph_and_find_cliques(join_tree, cliques);
join_tree.set_number_of_nodes(cliques.size());
// copy the cliques into each of the nodes of tree
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i)
{
cliques.remove_any(s);
s.swap(join_tree.node(i).data);
}
set_of_int_edge e;
// add all possible edges to the join_tree
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i)
{
for (unsigned long j = i+1; j < join_tree.number_of_nodes(); ++j)
{
set_intersection(
join_tree.node(i).data,
join_tree.node(j).data,
e);
if (e.size() > 0)
{
join_tree.add_edge(i,j);
edge(join_tree,i,j).swap(e);
}
}
}
// now we just need to remove the unnecessary edges so that we get a
// proper join tree
s.clear();
set_of_int& good = s; // rename s to something slightly more meaningful
// good will contain nodes that have been "approved"
unsigned long n = 0;
good.add(n);
std::vector<unsigned long> vtemp;
while (good.size() < join_tree.number_of_nodes())
{
// figure out which of the neighbors of nodes in good has the best edge
unsigned long best_bad_idx = 0;
unsigned long best_good_idx = 0;
unsigned long best_overlap = 0;
good.reset();
while (good.move_next())
{
// loop over all the neighbors of the current node in good
for (unsigned long i = 0; i < join_tree.node(good.element()).number_of_neighbors(); ++i)
{
const unsigned long idx = join_tree.node(good.element()).neighbor(i).index();
if (!good.is_member(idx))
{
const unsigned long overlap = join_tree.node(good.element()).edge(i).size();
if (overlap > best_overlap)
{
best_overlap = overlap;
best_bad_idx = idx;
best_good_idx = good.element();
}
}
}
}
// now remove all the edges from best_bad_idx to the nodes in good except for the
// edge to best_good_idx.
for (unsigned long i = 0; i < join_tree.node(best_bad_idx).number_of_neighbors(); ++i)
{
const unsigned long idx = join_tree.node(best_bad_idx).neighbor(i).index();
if (idx != best_good_idx && good.is_member(idx))
{
vtemp.push_back(idx);
}
}
for (unsigned long i = 0; i < vtemp.size(); ++i)
join_tree.remove_edge(vtemp[i], best_bad_idx);
vtemp.clear();
// and finally add this bad index into the good set
good.add(best_bad_idx);
}
}
// ----------------------------------------------------------------------------------------
namespace graph_helpers
{
template <
typename T,
typename U
>
bool validate_join_tree (
const T& n,
U& deads,
unsigned long parent = 0xffffffff
)
/*!
this function makes sure that a join tree satisfies the following criterion for paths starting at the given node:
- for all valid i and j such that i and j are both < #join_tree.number_of_nodes()
- let X be the set of numbers that is contained in both #join_tree.node(i).data
and #join_tree.node(j).data
- It is the case that all nodes on the unique path between #join_tree.node(i)
and #join_tree.node(j) contain the numbers from X in their sets.
returns true if validation passed and false if there is a problem with the tree
!*/
{
n.data.reset();
while (n.data.move_next())
{
if (deads.is_member(n.data.element()))
return false;
}
for (unsigned long i = 0; i < n.number_of_neighbors(); ++i)
{
if (n.neighbor(i).index() == parent)
continue;
// add anything to dead stuff
n.data.reset();
while (n.data.move_next())
{
if (n.neighbor(i).data.is_member(n.data.element()) == false)
{
unsigned long temp = n.data.element();
deads.add(temp);
}
}
if (validate_join_tree(n.neighbor(i), deads, n.index()) == false)
return false;
// remove this nodes stuff from dead stuff
n.data.reset();
while (n.data.move_next())
{
if (n.neighbor(i).data.is_member(n.data.element()) == false)
{
unsigned long temp = n.data.element();
deads.destroy(temp);
}
}
}
return true;
}
}
template <
typename graph_type,
typename join_tree_type
>
bool is_join_tree (
const graph_type& g,
const join_tree_type& join_tree
)
{
// make sure requires clause is not broken
DLIB_ASSERT(graph_contains_length_one_cycle(g) == false,
"\tvoid create_join_tree(g, join_tree)"
<< "\n\tInvalid graph"
);
DLIB_ASSERT(graph_is_connected(g) == true,
"\tvoid create_join_tree(g, join_tree)"
<< "\n\tInvalid graph"
);
COMPILE_TIME_ASSERT(is_graph<graph_type>::value || is_directed_graph<graph_type>::value);
COMPILE_TIME_ASSERT(is_graph<join_tree_type>::value);
if (graph_contains_undirected_cycle(join_tree))
return false;
if (graph_is_connected(join_tree) == false)
return false;
// verify that the path condition of the join tree is valid
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i)
{
typename join_tree_type::type deads;
if (graph_helpers::validate_join_tree(join_tree.node(i), deads) == false)
return false;
}
typename join_tree_type::edge_type e;
typename join_tree_type::edge_type all;
// now make sure that the edges contain correct intersections
for (unsigned long i = 0; i < join_tree.number_of_nodes(); ++i)
{
set_union(all,join_tree.node(i).data, all);
for (unsigned long j = 0; j < join_tree.node(i).number_of_neighbors(); ++j)
{
set_intersection(join_tree.node(i).data,
join_tree.node(i).neighbor(j).data,
e);
if (!(e == join_tree.node(i).edge(j)))
return false;
}
}
// and finally check that all the nodes in g show up in the join tree
if (all.size() != g.number_of_nodes())
return false;
all.reset();
while (all.move_next())
{
if (all.element() >= g.number_of_nodes())
return false;
}
return true;
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_GRAPH_UTILs_
// Copyright (C) 2007 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_GRAPH_UTILs_ABSTRACT_
#ifdef DLIB_GRAPH_UTILs_ABSTRACT_
#include "../directed_graph.h"
#include "../algs.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T
>
typename T::edge_type& edge(
T& g,
unsigned long i,
unsigned long j
);
/*!
requires
- T is an implementation of graph/graph_kernel_abstract.h
- g.has_edge(i,j)
ensures
- returns a reference to the edge data for the edge connecting nodes i and j
(i.e. returns g.node(i).edge(x) such that g.node(i).neighbor(x).index() == j)
!*/
template <
typename T
>
typename const T::edge_type& edge(
const T& g,
unsigned long i,
unsigned long j
);
/*!
requires
- T is an implementation of graph/graph_kernel_abstract.h
- g.has_edge(i,j)
ensures
- returns a const reference to the edge data for the edge connecting nodes i and j
(i.e. returns g.node(i).edge(x) such that g.node(i).neighbor(x).index() == j)
!*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_contains_directed_cycle (
const T& graph
);
/*!
requires
- T is an implementation of directed_graph/directed_graph_kernel_abstract.h
ensures
- if (there is a directed cycle in the given graph) then
- returns true
- else
- returns false
!*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_contains_undirected_cycle (
const T& graph
);
/*!
requires
- T is an implementation of directed_graph/directed_graph_kernel_abstract.h or
T is an implementation of graph/graph_kernel_abstract.h
ensures
- if (there is an undirected cycle in the given graph) then
- returns true
- else
- returns false
!*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_contains_length_one_cycle (
const T& graph
);
/*!
requires
- T is an implementation of directed_graph/directed_graph_kernel_abstract.h or
T is an implementation of graph/graph_kernel_abstract.h
ensures
- if (it is the case that graph.has_edge(i,i) == true for some i) then
- returns true
- else
- returns false
!*/
// ----------------------------------------------------------------------------------------
template <
typename src_type,
typename dest_type
>
void copy_graph_structure (
const src_type& src,
dest_type& dest
);
/*!
requires
- src_type is an implementation of directed_graph/directed_graph_kernel_abstract.h or
src_type is an implementation of graph/graph_kernel_abstract.h
- dest_type is an implementation of directed_graph/directed_graph_kernel_abstract.h or
dest_type is an implementation of graph/graph_kernel_abstract.h
- dest_type is not a directed_graph when src_type is a graph
ensures
- this function copies the graph structure from src into dest
- #dest.number_of_nodes() == src.number_of_nodes()
- for all valid i: #dest.node(i).item has an initial value for its type
- for all valid i and j:
- if (src.has_edge(i,j) == true) then
- #dest.has_edge(i,j) == true
!*/
// ----------------------------------------------------------------------------------------
template <
typename directed_graph_type,
typename graph_type
>
void create_moral_graph (
const directed_graph_type& g,
graph_type& moral_graph
);
/*!
requires
- directed_graph_type is an implementation of directed_graph/directed_graph_kernel_abstract.h
- graph_type is an implementation of graph/graph_kernel_abstract.h
- graph_contains_directed_cycle(g) == false
ensures
- #moral_graph == the moralized version of the directed graph g
- #moral_graph.number_of_nodes() == g.number_of_nodes()
- for all valid i and j:
- if (g.has_edge(i,j) == true) then
- #moral_graph.has_edge(i,j) == true
(i.e. all the edges that are in g are also in moral_graph)
- for all valid i:
- for all pairs p1 and p2 such that p1 != p2 and g.node(p1) and g.node(p2) are both
parents of node g.node(i):
- #moral_graph.has_edge(p1,p2) == true
(i.e. all the parents of a node are connected in the moral graph)
!*/
// ----------------------------------------------------------------------------------------
template <
typename T,
typename S
>
void find_connected_nodes (
const T& n,
S& visited
);
/*!
requires
- T is a node_type from an implementation of directed_graph/directed_graph_kernel_abstract.h or
T is a node_type from an implementation of graph/graph_kernel_abstract.h
- S is an implementation of set/set_kernel_abstract.h
ensures
- let G be the graph that contains node n
- #visited.is_member(n.index()) == true
- for all i such that there is an undirected path from n to G.node(i):
- #visited.is_member(i) == true
- for all i such that visited.is_member(i):
- #visited.is_member(i) == true
(i.e. this function doesn't remove anything from visited. So if
it contains stuff when you call this function then it will still
contain those things once the function ends)
!*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
bool graph_is_connected (
const T& g
);
/*!
requires
- T is an implementation of directed_graph/directed_graph_kernel_abstract.h or
T is an implementation of graph/graph_kernel_abstract.h
ensures
- every node in g has an undirected path to every other node in g.
I.e. g is a connected graph
!*/
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename sets_of_int
>
bool is_clique (
const graph_type& g,
const sets_of_int& clique
);
/*!
requires
- graph_type is an implementation of graph/graph_kernel_abstract.h
- sets_of_int is an implementation of set/set_kernel_abstract.h
and it contains unsigned long objects.
- graph_contains_length_one_cycle(g) == false
- for all x such that clique.is_member(x):
- x < g.number_of_nodes()
ensures
- if (it is true that for all i and j such that clique.is_member(i) and
clique.is_member(j) then g.has_edge(i,j) == true) then
- returns true
- else
- returns false
- if (clique.size() == 0) then
- returns true
(this is just a special case of the above condition)
- else
- returns false
!*/
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename sets_of_int
>
bool is_maximal_clique (
const graph_type& g,
const sets_of_int& clique
);
/*!
requires
- graph_type is an implementation of graph/graph_kernel_abstract.h
- sets_of_int is an implementation of set/set_kernel_abstract.h
and it contains unsigned long objects.
- graph_contains_length_one_cycle(g) == false
- for all x such that clique.is_member(x):
- x < g.number_of_nodes()
- is_clique(g,clique) == true
ensures
- if (there is no x such that clique.is_member(x) == false
and g.has_edge(i,x) for all i such that cliques.is_member(i)) then
- returns true
- else
- returns false
- if (clique.size() == 0) then
- returns true
(this is just a special case of the above condition)
- else
- returns false
!*/
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename set_of_sets_of_int
>
void triangulate_graph_and_find_cliques (
graph_type& g,
set_of_sets_of_int& cliques
);
/*!
requires
- graph_type is an implementation of graph/graph_kernel_abstract.h
- set_of_sets_of_int is an implementation of set/set_kernel_abstract.h
and it contains another set object which is comparable by operator< and
itself contains unsigned long objects.
(e.g. set<set<unsigned long>::compare_1a>::kernel_1a)
- graph_contains_length_one_cycle(g) == false
- graph_is_connected(g) == true
ensures
- #g.number_of_nodes() == g.number_of_nodes()
- all this function does to g is add edges to it until g becomes a
chordal graph where a chordal graph is a graph where each cycle
in the graph of 4 or more nodes has an edge joining two nodes
that are not adjacent in the cycle.
- #cliques.size() == the number of maximal cliques in the graph #g
- for all valid sets S such that #cliques.is_member(S):
- for all valid integers i and j such that S.is_member(i) == true
and S.is_member(j) == true and i != j:
- #g.has_edge(i,j) == true
!*/
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename join_tree_type
>
bool is_join_tree (
const graph_type& g,
const join_tree_type& join_tree
);
/*!
requires
- graph_type is an implementation of directed_graph/directed_graph_kernel_abstract.h or
graph_type is an implementation of graph/graph_kernel_abstract.h
- join_tree_type is an implementation of graph/graph_kernel_abstract.h
- join_tree_type::type is an implementation of set/set_compare_abstract.h and
this set type contains unsigned long objects.
- join_tree_type::edge_type is an implementation of set/set_compare_abstract.h and
this set type contains unsigned long objects.
- graph_contains_length_one_cycle(g) == false
- graph_is_connected(g) == true
ensures
- if (join_tree is a valid join tree of graph g. That is, join_tree is a
tree decomposition of g) then
- returns true
- else
- returns false
- a join tree of graph g is defined as follows:
- graph_contains_undirected_cycle(join_tree) == false
- graph_is_connected(join_tree) == true
- for all valid i:
- join_tree.node(i).item == a non-empty set containing node indexes
from g. That is, this set contains all the nodes from g that are
in this cluster in the join tree
- for all valid i and j such that i and j are both < join_tree.number_of_nodes()
- let X be the set of numbers that is contained in both join_tree.node(i).item
and join_tree.node(j).item
- It is the case that all nodes on the unique path between join_tree.node(i)
and join_tree.node(j) contain the numbers from X in their sets.
- edge(join_tree,i,j) == a set containing the intersection of
join_tree.node(i).item and join_tree.node(j).item
- the node index for every node in g appears in some node in join_tree at
least once.
!*/
// ----------------------------------------------------------------------------------------
template <
typename graph_type,
typename join_tree_type
>
void create_join_tree (
const graph_type& g,
join_tree_type& join_tree
);
/*!
requires
- graph_type is an implementation of graph/graph_kernel_abstract.h
- join_tree_type is an implementation of graph/graph_kernel_abstract.h
- join_tree_type::type is an implementation of set/set_compare_abstract.h and
this set type contains unsigned long objects.
- join_tree_type::edge_type is an implementation of set/set_compare_abstract.h and
this set type contains unsigned long objects.
- graph_contains_length_one_cycle(g) == false
- graph_is_connected(g) == true
ensures
- #is_join_tree(g, join_tree) == true
!*/
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_GRAPH_UTILs_ABSTRACT_
// Copyright (C) 2005 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GUI_CORe_
#define DLIB_GUI_CORe_
#include "platform.h"
#ifdef WIN32
#include "gui_core/windows.h"
#else
#include "gui_core/xlib.h"
#endif
#endif // DLIB_GUI_CORe_
// Copyright (C) 2005 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GUI_CORE_KERNEL_1_CPp_
#define DLIB_GUI_CORE_KERNEL_1_CPp_
#include "../platform.h"
#ifdef WIN32
#include "gui_core_kernel_1.h"
// tell visual studio to link to the libraries we need if we are
// in fact using visual studio
#ifdef _MSC_VER
#pragma comment (lib, "gdi32.lib")
#pragma comment (lib, "comctl32.lib")
#pragma comment (lib, "user32.lib")
#endif
#include <sstream>
#include "../threads.h"
#include "../assert.h"
#include "../queue.h"
#include "../sync_extension.h"
#include "../queue.h"
#include "../logger.h"
#include <cmath>
#include <vector>
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace gui_core_kernel_1_globals
{
static logger dlog("dlib.gui_core");
static TCHAR window_class_name[] = TEXT ("w3049u6qc2d94thw9m34f4we0gvwa3-tgkser0-b9gm 05");
static HINSTANCE hInstance;
static HWND helper_window = NULL;
static bool core_has_been_initialized = false;
static bool quit_windows_loop = false;
static bool set_window_title_done = true;
static std::string window_title;
static bool move_window_done = true;
static HWND move_window_hwnd = NULL;
static int move_window_width = 0;
static int move_window_height = 0;
static int move_window_x = 0;
static int move_window_y = 0;
static bool request_new_window = false;
static DWORD dwStyle;
static HWND new_window = NULL;
// the window_table.get_mutex() mutex locks the above 11 variables
typedef sync_extension<binary_search_tree<HWND,base_window*>::kernel_1a>::kernel_1a
window_table_type;
// this variable holds a mapping from window handles to the base_window
// objects which represent them. Note that this objects mutex is always locked
// when inside the event loop.
// Also, put these objects on the heap because we want to ensure that they
// aren't destroyed until the event_handler is destroyed
static window_table_type& window_table = *(new window_table_type);
static rsignaler& window_close_signaler = *(new rsignaler(window_table.get_mutex()));
static rsignaler& et_signaler = *(new rsignaler(window_table.get_mutex()));
// note that this is the thread that will perform all the event
// processing.
thread_id_type event_thread_id;
struct user_event_type
{
HWND w;
void* p;
int i;
};
typedef sync_extension<queue<user_event_type,memory_manager<char>::kernel_1b>::kernel_2a_c>::kernel_1a queue_of_user_events;
queue_of_user_events user_events;
enum USER_OFFSETS
{
CREATE_WINDOW,
DESTROY_WINDOW,
SET_ACTIVE_WINDOW,
QUIT_EVENT_HANDLER_THREAD,
USER_EVENTS_READY,
CALL_MOVE_WINDOW,
SHOW_WINDOW_SHOW,
SHOW_WINDOW_HIDE,
CALL_SET_WINDOW_TITLE
};
// ----------------------------------------------------------------------------------------
struct ebh_param
{
std::string text;
std::string title;
};
static void error_box_helper(void* param)
{
ebh_param& p = *reinterpret_cast<ebh_param*>(param);
MessageBox (NULL, TEXT (p.text.c_str()),
p.title.c_str(), MB_OK|MB_ICONERROR|MB_SYSTEMMODAL
);
delete &p;
}
static void error_box (
const char* title,
const char* text,
bool nonblocking = false
)
{
try
{
if (nonblocking)
{
ebh_param* param = new ebh_param;
param->text = text;
param->title = title;
dlib::create_new_thread(error_box_helper,param);
}
else
{
MessageBox (NULL, TEXT (text),
title, MB_OK|MB_ICONERROR|MB_SYSTEMMODAL
);
}
}
catch (...)
{
// we are totally screwed if this happens so just quit
exit(0);
}
}
// ----------------------------------------------------------------------------------------
static bool map_keys (
unsigned long keycode,
bool shift,
bool caps,
unsigned long& result,
bool& is_printable
)
/*!
requires
- if (shift was down for this key) then
- shift == true
- if (caps lock was on for this key) then
- caps == true
- keycode == the keycode from windows that we are to process
- keycode < keyboard_keys_size
ensures
- if (this key should be ignored) then
- returns false
- else
- returns true
- #is_printable == true if result is a printable ascii character
- #result == the keycode converted into the proper number to tbe
returned by the event handler.
!*/
{
is_printable = true;
if (keycode <= '9' && keycode >= '0')
{
result = keycode;
if (shift)
{
switch (result)
{
case '0': result = ')'; break;
case '1': result = '!'; break;
case '2': result = '@'; break;
case '3': result = '#'; break;
case '4': result = '$'; break;
case '5': result = '%'; break;
case '6': result = '^'; break;
case '7': result = '&'; break;
case '8': result = '*'; break;
case '9': result = '('; break;
}
}
}
else if (keycode <= 'Z' && keycode >= 'A')
{
result = keycode;
// make the result lower case if we need to.
if (shift && caps || !caps && !shift)
result = result - 'A' + 'a';
}
else
{
switch (keycode)
{
case VK_BACK:
is_printable = false;
result = base_window::KEY_BACKSPACE;
break;
case VK_SHIFT:
is_printable = false;
result = base_window::KEY_SHIFT;
break;
case VK_CONTROL:
is_printable = false;
result = base_window::KEY_CTRL;
break;
case VK_MENU:
is_printable = false;
result = base_window::KEY_ALT;
break;
case VK_PAUSE:
is_printable = false;
result = base_window::KEY_PAUSE;
break;
case VK_CAPITAL:
is_printable = false;
result = base_window::KEY_CAPS_LOCK;
break;
case VK_ESCAPE:
is_printable = false;
result = base_window::KEY_ESC;
break;
case VK_PRIOR:
is_printable = false;
result = base_window::KEY_PAGE_UP;
break;
case VK_NEXT:
is_printable = false;
result = base_window::KEY_PAGE_DOWN;
break;
case VK_END:
is_printable = false;
result = base_window::KEY_END;
break;
case VK_HOME:
is_printable = false;
result = base_window::KEY_HOME;
break;
case VK_LEFT:
is_printable = false;
result = base_window::KEY_LEFT;
break;
case VK_RIGHT:
is_printable = false;
result = base_window::KEY_RIGHT;
break;
case VK_UP:
is_printable = false;
result = base_window::KEY_UP;
break;
case VK_DOWN:
is_printable = false;
result = base_window::KEY_DOWN;
break;
case VK_INSERT:
is_printable = false;
result = base_window::KEY_INSERT;
break;
case VK_DELETE:
is_printable = false;
result = base_window::KEY_DELETE;
break;
case 0x91:
is_printable = false;
result = base_window::KEY_SCROLL_LOCK;
break;
case VK_F1:
is_printable = false;
result = base_window::KEY_F1;
break;
case VK_F2:
is_printable = false;
result = base_window::KEY_F2;
break;
case VK_F3:
is_printable = false;
result = base_window::KEY_F3;
break;
case VK_F4:
is_printable = false;
result = base_window::KEY_F4;
break;
case VK_F5:
is_printable = false;
result = base_window::KEY_F5;
break;
case VK_F6:
is_printable = false;
result = base_window::KEY_F6;
break;
case VK_F7:
is_printable = false;
result = base_window::KEY_F7;
break;
case VK_F8:
is_printable = false;
result = base_window::KEY_F8;
break;
case VK_F9:
is_printable = false;
result = base_window::KEY_F9;
break;
case VK_F10:
is_printable = false;
result = base_window::KEY_F10;
break;
case VK_F11:
is_printable = false;
result = base_window::KEY_F11;
break;
case VK_F12:
is_printable = false;
result = base_window::KEY_F12;
break;
case VK_SPACE: result = ' '; break;
case VK_TAB: result = '\t'; break;
case VK_RETURN: result = '\n'; break;
case VK_NUMPAD0: result = '0'; break;
case VK_NUMPAD1: result = '1'; break;
case VK_NUMPAD2: result = '2'; break;
case VK_NUMPAD3: result = '3'; break;
case VK_NUMPAD4: result = '4'; break;
case VK_NUMPAD5: result = '5'; break;
case VK_NUMPAD6: result = '6'; break;
case VK_NUMPAD7: result = '7'; break;
case VK_NUMPAD8: result = '8'; break;
case VK_NUMPAD9: result = '9'; break;
case VK_MULTIPLY: result = '*'; break;
case VK_ADD: result = '+'; break;
case VK_SUBTRACT: result = '-'; break;
case VK_DECIMAL: result = '.'; break;
case VK_DIVIDE: result = '/'; break;
case VK_OEM_1:
if (shift) result = ':';
else result = ';';
break;
case VK_OEM_PLUS:
if (shift) result = '+';
else result = '=';
break;
case VK_OEM_COMMA:
if (shift) result = '<';
else result = ',';
break;
case VK_OEM_MINUS:
if (shift) result = '_';
else result = '-';
break;
case VK_OEM_PERIOD:
if (shift) result = '>';
else result = '.';
break;
case VK_OEM_2:
if (shift) result = '?';
else result = '/';
break;
case VK_OEM_3:
if (shift) result = '~';
else result = '`';
break;
case VK_OEM_4:
if (shift) result = '{';
else result = '[';
break;
case VK_OEM_5:
if (shift) result = '|';
else result = '\\';
break;
case VK_OEM_6:
if (shift) result = '}';
else result = ']';
break;
case VK_OEM_7:
if (shift) result = '"';
else result = '\'';
break;
default:
return false;
}
}
return true;
}
// ------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc (
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
using namespace gui_core_kernel_1_globals;
queue_of_user_events user_events_temp;
// Make the event processing thread have a priority slightly above normal.
// This makes the GUI smother if you do heavy processing in other threads.
HANDLE hand = OpenThread(THREAD_ALL_ACCESS,FALSE,GetCurrentThreadId());
SetThreadPriority(hand,THREAD_PRIORITY_ABOVE_NORMAL);
CloseHandle(hand);
auto_mutex M(window_table.get_mutex());
try
{
std::vector<unsigned char> bitmap_buffer;
bool is_double = false;
unsigned long btn = base_window::NONE;
switch (message)
{
case WM_USER+QUIT_EVENT_HANDLER_THREAD:
if (hwnd == helper_window)
{
quit_windows_loop = true;
PostQuitMessage(0);
}
return 0;
case WM_USER+DESTROY_WINDOW:
if (hwnd == helper_window)
{
DestroyWindow((HWND)wParam);
}
return 0;
case WM_USER+CALL_MOVE_WINDOW:
if (hwnd == helper_window)
{
MoveWindow(
move_window_hwnd,
move_window_x,
move_window_y,
move_window_width,
move_window_height,
TRUE);
move_window_done = true;
et_signaler.broadcast();
}
return 0;
case WM_USER+USER_EVENTS_READY:
if (hwnd == helper_window)
{
// this is the signal to look in the user_events queue
user_events.lock();
user_events.swap(user_events_temp);
user_events.unlock();
user_events_temp.reset();
// now dispatch all these user events
while (user_events_temp.move_next())
{
base_window** win_ = window_table[user_events_temp.element().w];
base_window* win;
// if this window exists in the window table then dispatch
// its event.
if (win_)
{
win = *win_;
win->on_user_event(
user_events_temp.element().p,
user_events_temp.element().i
);
}
}
user_events_temp.clear();
}
return 0;
case WM_USER+SET_ACTIVE_WINDOW:
if (hwnd == helper_window)
{
SetActiveWindow((HWND)wParam);
}
return 0;
case WM_USER+SHOW_WINDOW_SHOW:
if (hwnd == helper_window)
{
ShowWindow((HWND)wParam,SW_SHOW);
BringWindowToTop((HWND)wParam);
}
return 0;
case WM_USER+SHOW_WINDOW_HIDE:
if (hwnd == helper_window)
{
ShowWindow((HWND)wParam,SW_HIDE);
}
return 0;
case WM_USER+CALL_SET_WINDOW_TITLE:
if (hwnd == helper_window)
{
SetWindowText((HWND)wParam,window_title.c_str());
set_window_title_done = true;
et_signaler.broadcast();
}
return 0;
case WM_USER+CREATE_WINDOW:
if (hwnd == helper_window)
{
// if this is stupposed to be a popup window then do the popup window thing
if (dwStyle == WS_CHILD)
{
new_window = CreateWindowEx (WS_EX_TOOLWINDOW,window_class_name, "",
dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
helper_window, NULL, hInstance, NULL);
SetParent(new_window,NULL);
}
else
{
new_window = CreateWindow (window_class_name, "",
dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
}
// use the helper_window to indicate that CreateWindow failed
if (new_window == NULL)
new_window = helper_window;
et_signaler.broadcast();
}
return 0;
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
unsigned long state = 0;
bool shift = ((GetKeyState(VK_SHIFT)&0x8000)!=0);
bool ctrl = ((GetKeyState(VK_CONTROL)&0x8000)!=0);
bool caps = ((GetKeyState(VK_CAPITAL)&0x0001)!=0);
if(shift)
state = base_window::KBD_MOD_SHIFT;
if(ctrl)
state |= base_window::KBD_MOD_CONTROL;
if(caps)
state |= base_window::KBD_MOD_CAPS_LOCK;
if((GetKeyState(VK_MENU)&0x8000)!=0)
state |= base_window::KBD_MOD_ALT;
if((GetKeyState(VK_NUMLOCK)&0x0001)!=0)
state |= base_window::KBD_MOD_NUM_LOCK;
if((GetKeyState(VK_SCROLL)&0x0001)!=0)
state |= base_window::KBD_MOD_SCROLL_LOCK;
bool is_printable;
unsigned long result;
if (map_keys(wParam,shift,caps,result,is_printable))
{
// signal the keyboard event
win->on_keydown(result,is_printable,state);
}
}
break;
// treat the user releasing the mouse button on the non client area (e.g. the title bar)
// like focus being lost since that is what X11 does
case WM_NCLBUTTONUP:
case WM_NCMBUTTONUP:
case WM_NCRBUTTONUP:
case WM_SETFOCUS:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal that the window is gaining focus
win->on_focus_gained();
}
break;
// treat the user clicking on the non client area (e.g. the title bar)
// like focus being lost since that is what X11 does
case WM_NCLBUTTONDBLCLK:
case WM_NCMBUTTONDBLCLK:
case WM_NCRBUTTONDBLCLK:
case WM_NCLBUTTONDOWN:
case WM_NCMBUTTONDOWN:
case WM_NCRBUTTONDOWN:
case WM_KILLFOCUS:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal that the window is gaining focus
win->on_focus_lost();
}
break;
case WM_SIZE:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal that the window has been resized
win->on_window_resized();
}
return 0;
case WM_MOVE:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal that the window has moved
win->on_window_moved();
}
return 0;
case WM_MOUSELEAVE:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal that the mouse has left the window
if (win->mouse_in)
{
win->on_mouse_leave();
win->mouse_in = false;
}
}
return 0;
case WM_MOUSEWHEEL:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal the mouse wheel event
if (GET_WHEEL_DELTA_WPARAM(wParam) > 0)
{
win->on_wheel_up();
}
else
{
win->on_wheel_down();
}
}
return 0;
case WM_LBUTTONUP:
btn = base_window::LEFT;
case WM_MBUTTONUP:
if (btn == base_window::NONE)
btn = base_window::MIDDLE;
case WM_RBUTTONUP:
if (btn == base_window::NONE)
btn = base_window::RIGHT;
{
// release the mouse capture if the user isn't holding any
// other mouse buttons
if (!((wParam & MK_LBUTTON) | (wParam & MK_MBUTTON) | (wParam & MK_RBUTTON)))
ReleaseCapture();
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
unsigned long state = 0;
if (wParam & MK_CONTROL)
state |= base_window::CONTROL;
if (wParam & MK_LBUTTON)
state |= base_window::LEFT;
if (wParam & MK_MBUTTON)
state |= base_window::MIDDLE;
if (wParam & MK_RBUTTON)
state |= base_window::RIGHT;
if (wParam & MK_SHIFT)
state |= base_window::SHIFT;
// remove the clicked button from the state
state &= (~btn);
// signal the mouse click
win->on_mouse_up(btn,state,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
}
return 0;
case WM_LBUTTONDBLCLK:
if (btn == base_window::NONE)
btn = base_window::LEFT;
case WM_MBUTTONDBLCLK:
if (btn == base_window::NONE)
btn = base_window::MIDDLE;
case WM_RBUTTONDBLCLK:
if (btn == base_window::NONE)
btn = base_window::RIGHT;
is_double = true;
case WM_LBUTTONDOWN:
if (btn == base_window::NONE)
btn = base_window::LEFT;
case WM_MBUTTONDOWN:
if (btn == base_window::NONE)
btn = base_window::MIDDLE;
case WM_RBUTTONDOWN:
if (btn == base_window::NONE)
btn = base_window::RIGHT;
{
SetCapture(hwnd);
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
unsigned long state = 0;
if (wParam & MK_CONTROL)
state |= base_window::CONTROL;
if (wParam & MK_LBUTTON)
state |= base_window::LEFT;
if (wParam & MK_MBUTTON)
state |= base_window::MIDDLE;
if (wParam & MK_RBUTTON)
state |= base_window::RIGHT;
if (wParam & MK_SHIFT)
state |= base_window::SHIFT;
// remove the clicked button from the state
state &= (~btn);
// signal the mouse click
win->on_mouse_down(btn,state,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),is_double);
}
return 0;
case WM_MOUSEMOVE:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
unsigned long state = 0;
bool mouse_button_down = false;
if (wParam & MK_CONTROL)
state |= base_window::CONTROL;
if (wParam & MK_LBUTTON)
{
state |= base_window::LEFT;
mouse_button_down = true;
}
if (wParam & MK_MBUTTON)
{
mouse_button_down = true;
state |= base_window::MIDDLE;
}
if (wParam & MK_RBUTTON)
{
state |= base_window::RIGHT;
mouse_button_down = true;
}
if (wParam & MK_SHIFT)
state |= base_window::SHIFT;
// signal the mouse movement if this mouse event isn't identical to the
// last one we got
if ( GET_X_LPARAM(lParam) != win->prevx ||
GET_Y_LPARAM(lParam) != win->prevy ||
state != win->prev_state)
{
win->on_mouse_move(state,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
}
// save the event data into the prev* member variables
win->prevx = GET_X_LPARAM(lParam);
win->prevy = GET_Y_LPARAM(lParam);
win->prev_state = state;
// The following block of code checks if the mouse is moving
// into or out of the window.
if (mouse_button_down == false)
{
// if there isn't any mouse button down then the fact that
// we are getting a mouse move message means it is in the
// window
if (win->mouse_in == false)
{
win->on_mouse_enter();
win->mouse_in = true;
// set the tracker for the mouse
TRACKMOUSEEVENT tm;
tm.hwndTrack = hwnd;
tm.cbSize = sizeof(tm);
tm.dwFlags = TME_LEAVE;
_TrackMouseEvent(&tm);
}
}
else if (win->mouse_in)
{
// check if the mouse is currently outside the window
const long mouse_x = GET_X_LPARAM(lParam);
const long mouse_y = GET_Y_LPARAM(lParam);
if (mouse_x < 0 || mouse_y < 0)
{
// the mouse is not in the window
win->mouse_in = false;
win->on_mouse_leave();
}
else
{
unsigned long width, height;
win->get_size(width,height);
if (mouse_x >= static_cast<long>(width) ||
mouse_y >= static_cast<long>(height))
{
// the mouse is not in the window
win->mouse_in = false;
win->on_mouse_leave();
}
}
}
else if (win->mouse_in == false)
{
// at this point we know that the mouse is moving around
// with some of its buttons down. So it might be outside the window.
// get the window size and see if the mouse is outside
// it.
const long mouse_x = GET_X_LPARAM(lParam);
const long mouse_y = GET_Y_LPARAM(lParam);
unsigned long width, height;
win->get_size(width,height);
if (mouse_x < static_cast<long>(width) &&
mouse_y < static_cast<long>(height) &&
mouse_x >= 0 &&
mouse_y >= 0)
{
// The mouse has gone inside the window
win->mouse_in = true;
win->on_mouse_enter();
// set the tracker for the mouse
TRACKMOUSEEVENT tm;
tm.hwndTrack = hwnd;
tm.cbSize = sizeof(tm);
tm.dwFlags = TME_LEAVE;
_TrackMouseEvent(&tm);
}
}
}
return 0;
case WM_PAINT :
{
PAINTSTRUCT ps;
HDC hdc = NULL;
hdc = BeginPaint (hwnd, &ps) ;
try
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
LONG x = ps.rcPaint.left;
LONG y = ps.rcPaint.top;
LONG width = ps.rcPaint.right - x;
LONG height = ps.rcPaint.bottom - y;
if (width != 0 && height != 0)
{
BITMAPINFO bmap_info;
bmap_info.bmiColors[0].rgbBlue = 0;
bmap_info.bmiColors[0].rgbGreen = 0;
bmap_info.bmiColors[0].rgbRed = 0;
bmap_info.bmiColors[0].rgbReserved = 0;
bmap_info.bmiHeader.biSize = sizeof(bmap_info.bmiHeader);
bmap_info.bmiHeader.biWidth = width;
bmap_info.bmiHeader.biHeight = -1*height;
bmap_info.bmiHeader.biPlanes = 1;
bmap_info.bmiHeader.biBitCount = 24;
bmap_info.bmiHeader.biCompression = BI_RGB;
bmap_info.bmiHeader.biSizeImage = 0;
bmap_info.bmiHeader.biXPelsPerMeter = 0;
bmap_info.bmiHeader.biYPelsPerMeter = 0;
bmap_info.bmiHeader.biClrUsed = 0;
bmap_info.bmiHeader.biClrImportant = 0;
unsigned char* bitmap ;
unsigned long size;
unsigned long padding = 0;
if ((width*3)%sizeof(LONG) != 0)
{
padding = sizeof(LONG) - (width*3)%sizeof(LONG);
size = (width*3+padding)*height;
}
else
{
size = width*height*3;
}
if (bitmap_buffer.size() < size)
bitmap_buffer.resize(size);
bitmap = &bitmap_buffer[0];
canvas bits(bitmap,padding,x,y,x+width-1,y+height-1);
win->paint(bits);
SetDIBitsToDevice (
hdc,
ps.rcPaint.left,
ps.rcPaint.top,
width,
height,
0,
0,
0,
height,
bitmap,
&bmap_info,
DIB_RGB_COLORS
);
}
EndPaint (hwnd, &ps) ;
}
catch (...)
{
// make sure EndPaint is called even if an exception
// is thrown.
if (hdc != NULL)
EndPaint (hwnd, &ps);
throw;
}
}
return 0 ;
case WM_ERASEBKGND:
return 1;
case WM_CLOSE:
{
base_window** win_ = window_table[hwnd];
base_window* win;
if (win_)
win = *win_;
else
break;
// signal that the window is being closed
if (win->on_window_close() == base_window::DO_NOT_CLOSE_WINDOW)
{
DLIB_ASSERT(win->has_been_destroyed == false,
"\tYou called close_window() inside the on_window_close() event but"
<< "\n\tthen returned DO_NOT_CLOSE_WINDOW. You can do one or the other but not both."
<< "\n\tthis: " << win
);
// this happens if the on_window_close() callback
// tells us to ignore the close event.
return 0;
}
else
{
if (window_table[hwnd])
{
window_table.destroy(hwnd);
win->has_been_destroyed = true;
win->hwnd = 0;
gui_core_kernel_1_globals::window_close_signaler.broadcast();
}
else
{
// in this case the window must have self destructed by
// calling delete this;
return 0;
}
}
}
return DefWindowProc (hwnd, message, wParam, lParam);
default:
break;
} // switch (message)
}
catch (std::exception& e)
{
error_box("Exception thrown in event handler",e.what());
quit_windows_loop = true;
}
catch (...)
{
error_box("Exception thrown in event handler","Unknown Exception type.");
quit_windows_loop = true;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
// ----------------------------------------------------------------------------------------
void show_window (
HWND hwnd
)
{
using namespace gui_core_kernel_1_globals;
PostMessage(helper_window,WM_USER+SHOW_WINDOW_SHOW,(WPARAM)hwnd,0);
}
// ----------------------------------------------------------------------------------------
void hide_window (
HWND hwnd
)
{
using namespace gui_core_kernel_1_globals;
PostMessage(helper_window,WM_USER+SHOW_WINDOW_HIDE,(WPARAM)hwnd,0);
}
// ----------------------------------------------------------------------------------------
void give_window_focus (
HWND hwnd
)
/*!
ensures
- calls SetActiveWindow(hwnd) from the event handling thread.
!*/
{
using namespace gui_core_kernel_1_globals;
PostMessage(helper_window,WM_USER+SET_ACTIVE_WINDOW,(WPARAM)hwnd,0);
}
// ----------------------------------------------------------------------------------------
void destroy_window (
HWND hwnd
)
/*!
ensures
- calls DestroyWindow(hwnd) from the event handling thread.
!*/
{
using namespace gui_core_kernel_1_globals;
PostMessage(helper_window,WM_USER+DESTROY_WINDOW,(WPARAM)hwnd,0);
}
// ----------------------------------------------------------------------------------------
HWND make_window (
DWORD dwStyle_
)
/*!
ensures
- creates a window by calling CreateWindow and passes on the
dwStyle argument.
- returns the HWND that is returned by CreateWindow
- ensures that CreateWindow is called from the event handler thread
- if (it was unable to create a window) then
- returns NULL or helper_window
!*/
{
using namespace gui_core_kernel_1_globals;
// if we are running in the event handling thread then just call
// CreateWindow directly
if (get_thread_id() == gui_core_kernel_1_globals::event_thread_id)
{
// if this is stupposed to be a popup window then do the popup window thing
if (dwStyle_ == WS_CHILD)
{
HWND tmp = CreateWindowEx (WS_EX_TOOLWINDOW|WS_EX_TOPMOST, window_class_name, "",
dwStyle_,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
helper_window, NULL, hInstance, NULL);
SetParent(tmp,NULL);
return tmp;
}
else
{
return CreateWindow (window_class_name, "",
dwStyle_,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
}
}
else
{
auto_mutex M(window_table.get_mutex());
// wait for our chance to make a new window request
while (request_new_window)
et_signaler.wait();
dwStyle = dwStyle_;
if (PostMessage(helper_window,WM_USER+CREATE_WINDOW,0,0)==0)
{
throw gui_error("Unable to schedule function for execution in event handling thread.");
}
// wait for our request to be serviced
while (new_window == NULL)
et_signaler.wait();
HWND temp = new_window;
new_window = NULL;
request_new_window = false;
et_signaler.broadcast();
// if make_window() returns the helper_window then it means it failed
// to make a new window
if (temp == helper_window)
temp = NULL;
return temp;
}
}
// ------------------------------------------------------------------------------------
class event_handler_thread : public threaded_object
{
public:
enum et_state
{
uninitialized,
initialized,
failure_to_init
};
et_state status;
event_handler_thread(
)
{
status = uninitialized;
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
}
~event_handler_thread ()
{
using namespace gui_core_kernel_1_globals;
if (is_alive())
{
if (PostMessage(helper_window,WM_USER+QUIT_EVENT_HANDLER_THREAD,0,0)==0)
{
dlog << LERROR << "Unable to schedule function for execution in event handling thread.";
}
wait();
}
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct()
{
delete this;
}
private:
void thread (
)
{
event_thread_id = get_thread_id();
hInstance = GetModuleHandle(NULL);
if (hInstance == NULL)
{
dlog << LFATAL << "Error gathering needed resources";
// signal that an error has occurred
window_table.get_mutex().lock();
status = failure_to_init;
et_signaler.broadcast();
window_table.get_mutex().unlock();
return;
}
// register the main window class
WNDCLASS wndclass ;
wndclass.style = CS_DBLCLKS;
wndclass.lpfnWndProc = dlib::gui_core_kernel_1_globals::WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = 0;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = window_class_name ;
if (!RegisterClass (&wndclass))
{
dlog << LFATAL << "Error registering window class";
// signal that an error has occurred
window_table.get_mutex().lock();
status = failure_to_init;
et_signaler.broadcast();
window_table.get_mutex().unlock();
return;
}
// make the helper window that is used to trigger events in the
// event handler loop from other threads
helper_window = CreateWindow(window_class_name,"",WS_DISABLED,0,0,0,0,HWND_MESSAGE,NULL,hInstance,NULL);
if (helper_window == NULL)
{
dlog << LFATAL << "Error gathering needed resources";
// signal that an error has occurred
window_table.get_mutex().lock();
status = failure_to_init;
et_signaler.broadcast();
window_table.get_mutex().unlock();
return;
}
// signal that the event thread is now up and running
window_table.get_mutex().lock();
status = initialized;
et_signaler.broadcast();
window_table.get_mutex().unlock();
// start the event handler loop.
/*
A note about this quit_windows_loop thing. If the user is holding
the mouse button down on the title bar of a window it will cause
the PostQuitMessage() function to be ignored!! This extra bool
is a work around to prevent that from happening.
*/
MSG msg;
while (GetMessage (&msg, NULL, 0, 0) &&
quit_windows_loop == false)
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
};
// ----------------------------------------------------------------------------------------
static event_handler_thread* event_handler = new event_handler_thread;
void init_gui_core ()
{
using namespace dlib::gui_core_kernel_1_globals;
auto_mutex M(window_table.get_mutex());
if (core_has_been_initialized == false)
{
core_has_been_initialized = true;
// start up the event handler thread
event_handler->start();
// wait for the event thread to get up and running
while (event_handler->status == event_handler_thread::uninitialized)
et_signaler.wait();
if (event_handler->status == event_handler_thread::failure_to_init)
throw gui_error("Failed to start event thread");
}
}
} // end namespace gui_core_kernel_1_globals
// ----------------------------------------------------------------------------------------
void canvas::
fill (
unsigned char red_,
unsigned char green_,
unsigned char blue_
) const
{
const unsigned long red = red_;
const unsigned long green = green_;
const unsigned long blue = blue_;
const LONG block1 = (blue<<24) | (red<<16) | (green<<8) | blue;
const LONG block2 = (green<<24) | (blue<<16) | (red<<8) | green;
const LONG block3 = (red<<24) | (green<<16) | (blue<<8) | red;
// remember that row_width is a multiple of 4 because windows
// requires that all bitmaps have row widths that are multiples of 4.
unsigned long size = row_width/4;
for (unsigned long i = 0; i < height_; ++i)
{
unsigned long padding = size%3;
LONG* start = reinterpret_cast<LONG*>(bits+row_width*i);
LONG* end = reinterpret_cast<LONG*>(start) + size - padding;
while (start != end)
{
*start = block1;
++start;
*start = block2;
++start;
*start = block3;
++start;
}
if (padding)
{
*start = block1;
++start;
--padding;
}
if (padding)
{
*start = block2;
}
}
}
// ----------------------------------------------------------------------------------------
void base_window::
trigger_user_event (
void* p,
int i
)
{
using namespace gui_core_kernel_1_globals;
user_event_type e;
e.w = hwnd;
e.p = p;
e.i = i;
{
auto_mutex M(user_events.get_mutex());
user_events.enqueue(e);
}
if (PostMessage(helper_window,WM_USER+USER_EVENTS_READY,0,0)==0)
{
throw gui_error("Unable to schedule function for execution in event handling thread.");
}
}
// ----------------------------------------------------------------------------------------
base_window::
base_window (
bool resizable,
bool undecorated
) :
has_been_destroyed(false),
wm(gui_core_kernel_1_globals::window_table.get_mutex()),
prevx(-1),
prevy(-1),
prev_state(0)
{
DLIB_ASSERT(!(undecorated == true && resizable == true),
"\tbase_window::base_window()"
<< "\n\tThere is no such thing as an undecorated window that is resizable by the user."
<< "\n\tthis: " << this
);
gui_core_kernel_1_globals::init_gui_core();
if (resizable)
style = WS_OVERLAPPEDWINDOW;
else if (undecorated)
style = WS_CHILD;
else
style = WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX;
hwnd = gui_core_kernel_1_globals::make_window(style);
if (hwnd == NULL)
throw gui_error("unable to create base_window");
auto_mutex M(wm);
mouse_in = false;
HWND temp = hwnd;
base_window* ttemp = this;
gui_core_kernel_1_globals::window_table.add(temp,ttemp);
}
// ----------------------------------------------------------------------------------------
base_window::
~base_window (
)
{
close_window();
}
// ----------------------------------------------------------------------------------------
void base_window::
close_window (
)
{
auto_mutex M(wm);
if (has_been_destroyed == false)
{
// do this just to make sure no one tries to call this window's
// calbacks.
gui_core_kernel_1_globals::window_table.destroy(hwnd);
gui_core_kernel_1_globals::destroy_window(hwnd);
hwnd = 0;
has_been_destroyed = true;
gui_core_kernel_1_globals::window_close_signaler.broadcast();
}
}
// ----------------------------------------------------------------------------------------
void base_window::
wait_until_closed (
) const
{
auto_mutex M(wm);
while (has_been_destroyed == false)
gui_core_kernel_1_globals::window_close_signaler.wait();
}
// ----------------------------------------------------------------------------------------
bool base_window::
is_closed (
) const
{
auto_mutex M(wm);
return has_been_destroyed;
}
// ----------------------------------------------------------------------------------------
void base_window::
set_title (
const std::string& title
)
{
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::set_title"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
);
using namespace gui_core_kernel_1_globals;
// call the SetWindowText function with our arguments but make sure it is from
// the event thread. We have to do this because the SetWindowText() apparently blocks
// until something happens in the event thread so we have to
// do this to avoid possible deadlocks.
auto_mutex M(wm);
if (get_thread_id() == gui_core_kernel_1_globals::event_thread_id)
{
SetWindowText(hwnd,title.c_str());
}
else
{
window_title = title;
set_window_title_done = false;
if (PostMessage(helper_window,WM_USER+CALL_SET_WINDOW_TITLE,(WPARAM)hwnd,0)==0)
{
throw gui_error("Unable to schedule SetWindowText function for execution in event handling thread.");
}
// wait for any SetWindowText() calls to finish
while (set_window_title_done == false)
et_signaler.wait();
}
}
// ----------------------------------------------------------------------------------------
void base_window::
show (
)
{
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::show"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
);
using namespace gui_core_kernel_1_globals;
show_window(hwnd);
if (style != WS_CHILD)
give_window_focus(hwnd);
}
// ----------------------------------------------------------------------------------------
void base_window::
hide(
)
{
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::hide"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
);
using namespace gui_core_kernel_1_globals;
hide_window(hwnd);
}
// ----------------------------------------------------------------------------------------
void base_window::
set_size (
int width_,
int height_
)
{
using namespace gui_core_kernel_1_globals;
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::set_size"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
<< "\n\twidth: " << width_
<< "\n\theight: " << height_
);
auto_mutex M(wm);
if (get_thread_id() == gui_core_kernel_1_globals::event_thread_id)
{
RECT info;
GetWindowRect(hwnd,&info);
int x = info.left;
int y = info.top;
int width;
int height;
RECT rect;
rect.top = 0;
rect.left = 0;
rect.bottom = height_;
rect.right = width_;
AdjustWindowRectEx(&rect,style,FALSE,0);
width = std::abs(rect.right - rect.left);
height = std::abs(rect.bottom - rect.top);
MoveWindow(
hwnd,
x,
y,
width,
height,
TRUE);
}
else
{
RECT info;
GetWindowRect(hwnd,&info);
int x = info.left;
int y = info.top;
int width;
int height;
RECT rect;
rect.top = 0;
rect.left = 0;
rect.bottom = height_;
rect.right = width_;
AdjustWindowRectEx(&rect,style,FALSE,0);
width = std::abs(rect.right - rect.left);
height = std::abs(rect.bottom - rect.top);
// call the MoveWindow function with our arguments. We
// have to do this because the MoveWindow() apparently blocks
// until something happens in the event thread so we have to
// do this to avoid possible deadlocks.
move_window_hwnd = hwnd;
move_window_x = x;
move_window_y = y;
move_window_width = width;
move_window_height = height;
move_window_done = false;
if (PostMessage(helper_window,WM_USER+CALL_MOVE_WINDOW,0,0)==0)
{
throw gui_error("Unable to schedule MoveWindow function for execution in event handling thread.");
}
// wait for any MoveWindow calls to finish
while (move_window_done == false)
et_signaler.wait();
}
}
// ----------------------------------------------------------------------------------------
void base_window::
set_pos (
long x_,
long y_
)
{
using namespace gui_core_kernel_1_globals;
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::set_pos"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
<< "\n\tx: " << x_
<< "\n\ty: " << y_
);
auto_mutex M(wm);
if (get_thread_id() == gui_core_kernel_1_globals::event_thread_id)
{
RECT info;
GetWindowRect(hwnd,&info);
int width = info.right - info.left;
int height = info.bottom - info.top;
MoveWindow(
hwnd,
x_,
y_,
width,
height,
TRUE);
}
else
{
RECT info;
GetWindowRect(hwnd,&info);
int width = info.right - info.left;
int height = info.bottom - info.top;
// call the MoveWindow function with our arguments. We
// have to do this because the MoveWindow() apparently blocks
// until something happens in the event thread so we have to
// do this to avoid possible deadlocks.
move_window_hwnd = hwnd;
move_window_x = x_;
move_window_y = y_;
move_window_width = width;
move_window_height = height;
move_window_done = false;
if (PostMessage(helper_window,WM_USER+CALL_MOVE_WINDOW,0,0)==0)
{
throw gui_error("Unable to schedule MoveWindow function for execution in event handling thread.");
}
// wait for any MoveWindow calls to finish
while (move_window_done == false)
et_signaler.wait();
}
}
// ----------------------------------------------------------------------------------------
void base_window::
get_pos (
long& x_,
long& y_
)
{
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::get_pos"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
);
POINT p;
p.x = 0;
p.y = 0;
ClientToScreen(hwnd,&p);
x_ = p.x;
y_ = p.y;
}
// ----------------------------------------------------------------------------------------
void base_window::
get_display_size (
unsigned long& width,
unsigned long& height
) const
{
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::get_display_size"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
);
RECT rc;
GetWindowRect(hwnd, &rc);
HMONITOR hMonitor;
MONITORINFO mi;
//
// get the nearest monitor to the passed rect.
//
hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
//
// get the work area or entire monitor rect.
//
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
rc = mi.rcMonitor;
width = static_cast<unsigned long>(rc.right - rc.left);
height = static_cast<unsigned long>(rc.bottom - rc.top);
}
// ----------------------------------------------------------------------------------------
void base_window::
get_size (
unsigned long& width,
unsigned long& height
) const
{
DLIB_ASSERT(is_closed() == false,
"\tvoid base_window::get_size"
<< "\n\tYou can't do this to a window that has been closed."
<< "\n\tthis: " << this
);
RECT r;
GetClientRect(hwnd,&r);
width = r.right - r.left;
height = r.bottom - r.top;
}
// ----------------------------------------------------------------------------------------
void base_window::
invalidate_rectangle (
const rectangle& rect
)
{
if (rect.is_empty() == false && !has_been_destroyed)
{
RECT info;
info.top = rect.top();
info.left = rect.left();
info.right = rect.right()+1;
info.bottom = rect.bottom()+1;
InvalidateRect(hwnd,&info,FALSE);
}
}
// ----------------------------------------------------------------------------------------
void put_on_clipboard (
const std::string& str
)
{
using namespace gui_core_kernel_1_globals;
using namespace std;
init_gui_core();
if (OpenClipboard(helper_window))
{
EmptyClipboard();
auto_mutex M(window_table.get_mutex());
const unsigned long newlines = count(str.begin(),str.end(),'\n');
HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE,str.size()+newlines+1);
if (mem != NULL)
{
char* buf = reinterpret_cast<char*>(GlobalLock(mem));
if (buf != NULL)
{
// copy str into buf while also replacing all the \n with \r\n
for (string::size_type i = 0; i < str.size(); ++i)
{
if (str[i] != '\n')
{
*buf = str[i];
++buf;
}
else
{
*buf = '\r';
++buf;
*buf = '\n';
++buf;
}
}
*buf = '\0';
GlobalUnlock(mem);
SetClipboardData(CF_TEXT,mem);
}
}
CloseClipboard();
}
}
// ----------------------------------------------------------------------------------------
void get_from_clipboard (
std::string& str
)
{
using namespace gui_core_kernel_1_globals;
using namespace std;
init_gui_core();
auto_mutex M(window_table.get_mutex());
if (OpenClipboard(helper_window))
{
HANDLE data = GetClipboardData(CF_TEXT);
if (data != NULL)
{
char* buf = reinterpret_cast<char*>(GlobalLock(data));
if (buf != 0)
{
str.clear();
// copy the data from buf into str while also removing any '\r'
// characters.
while (*buf != '\0')
{
if (*buf != '\r')
str += *buf;
++buf;
}
GlobalUnlock(data);
}
else
{
Beep(500,500);
}
}
CloseClipboard();
}
}
// ----------------------------------------------------------------------------------------
}
#endif // WIN32
#endif // DLIB_GUI_CORE_KERNEL_1_CPp_
// Copyright (C) 2005 Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_GUI_CORE_KERNEl_1_
#define DLIB_GUI_CORE_KERNEl_1_
#ifdef DLIB_ISO_CPP_ONLY
#error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code. Turn DLIB_ISO_CPP_ONLY off if you want to use it."
#endif
#ifdef DLIB_NO_GUI_SUPPORT
#error "DLIB_NO_GUI_SUPPORT is defined so you can't use the GUI code. Turn DLIB_NO_GUI_SUPPORT off if you want to use it."
#endif
#include <string>
#include "../windows_magic.h"
#include <windows.h>
#include <winuser.h>
#include <windowsx.h>
#include <commctrl.h>
#include "gui_core_kernel_abstract.h"
#ifdef _MSC_VER
// Disable the following warnings for Visual Studio
//
// These two warnings have to do with converting points to and from the LONG
// type. But both these types are 32 bits in windows so it is fine.
#pragma warning(disable: 4244; disable: 4312)
#endif
#include "../algs.h"
#include "../sync_extension.h"
#include "../binary_search_tree.h"
#include "../threads.h"
#include "../geometry/rectangle.h"
#include "../assert.h"
#include "../queue.h"
#include "../pixel.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class base_window;
namespace gui_core_kernel_1_globals
{
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
}
// ----------------------------------------------------------------------------------------
class canvas : public rectangle
{
public:
struct pixel
{
unsigned char blue;
unsigned char green;
unsigned char red;
};
~canvas() { }
inline pixel* operator[] (
unsigned long row
) const
{
DLIB_ASSERT(row < height(),
"\tpixel* canvas::operator[]"
<< "\n\tyou have to give a row that is less than the height()"
<< "\n\tthis: " << this
<< "\n\trow: " << row
<< "\n\theight(): " << height()
);
unsigned char* temp = bits + row_width*row;
return reinterpret_cast<pixel*>(temp);
}
void fill (
unsigned char red_,
unsigned char green_,
unsigned char blue_
) const;
private:
friend LRESULT CALLBACK gui_core_kernel_1_globals::WndProc (HWND, UINT, WPARAM, LPARAM);
canvas (
unsigned char* bits__,
unsigned long padding__,
unsigned long left__,
unsigned long top__,
unsigned long right__,
unsigned long bottom__
) :
rectangle(left__,top__,right__,bottom__),
bits(bits__),
width_(width()),
height_(height()),
row_width(width_*3+padding__)
{}
// restricted functions
canvas(); // normal constructor
canvas(canvas&); // copy constructor
canvas& operator=(canvas&); // assignment operator
unsigned char* const bits;
const unsigned long width_;
const unsigned long height_;
const unsigned long row_width;
};
template <>
struct pixel_traits<canvas::pixel>
{
const static bool rgb = true;
const static bool rgb_alpha = false;
const static bool grayscale = false;
const static bool hsi = false;
const static long num = 3;
static unsigned long max() { return 255;}
const static bool has_alpha = false;
};
// ----------------------------------------------------------------------------------------
void put_on_clipboard (
const std::string& str
);
// ----------------------------------------------------------------------------------------
void get_from_clipboard (
std::string& str
);
// ----------------------------------------------------------------------------------------
class base_window
{
friend LRESULT CALLBACK gui_core_kernel_1_globals::WndProc (HWND, UINT, WPARAM, LPARAM);
HWND hwnd;
DWORD style;
bool has_been_destroyed;
// This is true if the mouse is in this window. false otherwise.
// also note that this variable is only accessed from the event handling thread
// (except for being initialized below in the constructor, but that is inside
// the window_table mutex so it doesn't matter).
bool mouse_in;
// this is a copy of the last inputs we sent to the on_mouse_move() event.
long prevx;
long prevy;
long prev_state;
protected:
const rmutex& wm;
public:
base_window (
bool resizable = true,
bool undecorated = false
);
virtual ~base_window (
);
void close_window (
);
bool is_closed (
) const;
void set_title (
const std::string& title
);
virtual void show (
);
virtual void hide(
);
void set_size (
int width_,
int height_
);
void set_pos (
long x_,
long y_
);
void get_pos (
long& x_,
long& y_
);
void get_size (
unsigned long& width,
unsigned long& height
) const;
void get_display_size (
unsigned long& width,
unsigned long& height
) const;
void invalidate_rectangle (
const rectangle& rect
);
void trigger_user_event (
void* p,
int i
);
void wait_until_closed (
) const;
enum on_close_return_code
{
DO_NOT_CLOSE_WINDOW,
CLOSE_WINDOW
};
enum mouse_state_masks
{
NONE = 0,
LEFT = 1,
RIGHT = 2,
MIDDLE = 4,
SHIFT = 8,
CONTROL = 16
};
enum keyboard_state_masks
{
KBD_MOD_NONE = 0,
KBD_MOD_SHIFT = 1,
KBD_MOD_CONTROL = 2,
KBD_MOD_ALT = 4,
KBD_MOD_META = 8,
KBD_MOD_CAPS_LOCK = 16,
KBD_MOD_NUM_LOCK = 32,
KBD_MOD_SCROLL_LOCK = 64
};
enum non_printable_keyboard_keys
{
KEY_BACKSPACE,
KEY_SHIFT,
KEY_CTRL,
KEY_ALT,
KEY_PAUSE,
KEY_CAPS_LOCK,
KEY_ESC,
KEY_PAGE_UP,
KEY_PAGE_DOWN,
KEY_END,
KEY_HOME,
KEY_LEFT, // This is the left arrow key
KEY_RIGHT, // This is the right arrow key
KEY_UP, // This is the up arrow key
KEY_DOWN, // This is the down arrow key
KEY_INSERT,
KEY_DELETE,
KEY_SCROLL_LOCK,
// Function Keys
KEY_F1,
KEY_F2,
KEY_F3,
KEY_F4,
KEY_F5,
KEY_F6,
KEY_F7,
KEY_F8,
KEY_F9,
KEY_F10,
KEY_F11,
KEY_F12
};
protected:
virtual on_close_return_code on_window_close(
){return CLOSE_WINDOW;}
virtual void on_user_event (
void* p,
int i
){}
virtual void on_window_resized(
){}
virtual void on_window_moved(
){}
virtual void on_mouse_down (
unsigned long btn,
unsigned long state,
long x,
long y,
bool is_double_click
){}
virtual void on_mouse_up (
unsigned long btn,
unsigned long state,
long x,
long y
){}
virtual void on_mouse_move (
unsigned long state,
long x,
long y
){}
virtual void on_mouse_leave (
){}
virtual void on_mouse_enter (
){}
virtual void on_wheel_up (
){}
virtual void on_wheel_down (
){}
virtual void on_focus_gained (
){}
virtual void on_focus_lost (
){}
virtual void on_keydown (
unsigned long key,
bool is_printable,
unsigned long state
){}
private:
virtual void paint (
const canvas& c
) =0;
base_window(base_window&); // copy constructor
base_window& operator=(base_window&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "gui_core_kernel_1.cpp"
#endif
#endif // DLIB_GUI_CORE_KERNEl_1_
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment