Commit ac4666cb authored by Davis King's avatar Davis King

Added the perspective_display and perspective_window GUI tools.

parent 3ff14401
......@@ -5652,6 +5652,306 @@ namespace dlib
}
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// perspective_display member functions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
perspective_display::
perspective_display(
drawable_window& w
) :
drawable(w,MOUSE_MOVE|MOUSE_CLICK|MOUSE_WHEEL)
{
clear_overlay();
enable_events();
}
// ----------------------------------------------------------------------------------------
perspective_display::
~perspective_display(
)
{
disable_events();
parent.invalidate_rectangle(rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
set_size (
unsigned long width,
unsigned long height
)
{
auto_mutex lock(m);
rectangle old(rect);
rect = resize_rect(rect,width,height);
tform = camera_transform(tform.get_camera_pos(),
tform.get_camera_looking_at(),
tform.get_camera_up_direction(),
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
parent.invalidate_rectangle(old+rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
add_overlay (
const std::vector<overlay_line>& overlay
)
{
auto_mutex M(m);
if (overlay.size() == 0)
return;
// push this new overlay into our overlay vector
overlay_lines.insert(overlay_lines.end(), overlay.begin(), overlay.end());
for (unsigned long i = 0; i < overlay.size(); ++i)
{
sum_pts += overlay[i].p1;
sum_pts += overlay[i].p2;
max_pts.x() = std::max(overlay[i].p1.x(), max_pts.x());
max_pts.x() = std::max(overlay[i].p2.x(), max_pts.x());
max_pts.y() = std::max(overlay[i].p1.y(), max_pts.y());
max_pts.y() = std::max(overlay[i].p2.y(), max_pts.y());
max_pts.z() = std::max(overlay[i].p1.z(), max_pts.z());
max_pts.z() = std::max(overlay[i].p2.z(), max_pts.z());
}
tform = camera_transform(max_pts,
sum_pts/(overlay_lines.size()*2+overlay_dots.size()),
vector<double>(0,0,1),
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
// make the parent window redraw us now that we changed the overlay
parent.invalidate_rectangle(rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
add_overlay (
const std::vector<overlay_dot>& overlay
)
{
auto_mutex M(m);
if (overlay.size() == 0)
return;
// push this new overlay into our overlay vector
overlay_dots.insert(overlay_dots.end(), overlay.begin(), overlay.end());
for (unsigned long i = 0; i < overlay.size(); ++i)
{
sum_pts += overlay[i].p;
max_pts.x() = std::max(overlay[i].p.x(), max_pts.x());
max_pts.y() = std::max(overlay[i].p.y(), max_pts.y());
max_pts.z() = std::max(overlay[i].p.z(), max_pts.z());
}
tform = camera_transform(max_pts,
sum_pts/(overlay_lines.size()*2+overlay_dots.size()),
vector<double>(0,0,1),
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
// make the parent window redraw us now that we changed the overlay
parent.invalidate_rectangle(rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
clear_overlay (
)
{
auto_mutex lock(m);
overlay_dots.clear();
overlay_lines.clear();
sum_pts = vector<double>();
max_pts = vector<double>(-std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity());
parent.invalidate_rectangle(rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
set_dot_double_clicked_handler (
const any_function<void(const vector<double>&)>& event_handler_
)
{
auto_mutex M(m);
dot_clicked_event_handler = event_handler_;
}
// ----------------------------------------------------------------------------------------
void perspective_display::
draw (
const canvas& c
) const
{
rectangle area = rect.intersect(c);
fill_rect(c, area, 0);
for (unsigned long i = 0; i < overlay_dots.size(); ++i)
{
point p = tform(overlay_dots[i].p) + rect.tl_corner();
if (area.contains(p))
assign_pixel(c[p.y()-c.top()][p.x()-c.left()], overlay_dots[i].color);
}
for (unsigned long i = 0; i < overlay_lines.size(); ++i)
{
draw_line(c, tform(overlay_lines[i].p1), tform(overlay_lines[i].p2), overlay_lines[i].color, area);
}
}
// ----------------------------------------------------------------------------------------
void perspective_display::
on_wheel_up (
unsigned long state
)
{
if (rect.contains(lastx,lasty) == false || hidden || !enabled)
return;
const double alpha = 0.10;
const vector<double> delta = alpha*(tform.get_camera_pos() - tform.get_camera_looking_at());
tform = camera_transform(
tform.get_camera_pos() - delta,
tform.get_camera_looking_at(),
tform.get_camera_up_direction(),
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
parent.invalidate_rectangle(rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
on_wheel_down (
unsigned long state
)
{
if (rect.contains(lastx,lasty) == false || hidden || !enabled)
return;
const double alpha = 0.10;
const vector<double> delta = alpha*(tform.get_camera_pos() - tform.get_camera_looking_at());
tform = camera_transform(
tform.get_camera_pos() + delta,
tform.get_camera_looking_at(),
tform.get_camera_up_direction(),
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
parent.invalidate_rectangle(rect);
}
// ----------------------------------------------------------------------------------------
void perspective_display::
on_mouse_down (
unsigned long btn,
unsigned long state,
long x,
long y,
bool is_double_click
)
{
if (btn == base_window::LEFT || btn == base_window::RIGHT)
{
last = point(x,y);
}
if (is_double_click && btn == base_window::LEFT && enabled && !hidden && overlay_dots.size() != 0)
{
double best_dist = std::numeric_limits<double>::infinity();
unsigned long best_idx = 0;
const dpoint pp(x,y);
for (unsigned long i = 0; i < overlay_dots.size(); ++i)
{
dpoint p = tform(overlay_dots[i].p) + rect.tl_corner();
double dist = length_squared(p-pp);
if (dist < best_dist)
{
best_dist = dist;
best_idx = i;
}
}
if (dot_clicked_event_handler.is_set())
dot_clicked_event_handler(overlay_dots[best_idx].p);
}
}
// ----------------------------------------------------------------------------------------
void perspective_display::
on_mouse_move (
unsigned long state,
long x,
long y
)
{
if (!enabled || hidden)
return;
if (state == base_window::LEFT)
{
const point cur(x, y);
dpoint delta = last-cur;
last = cur;
const vector<double> radius = tform.get_camera_pos()-tform.get_camera_looking_at();
delta *= 2*pi*length(radius)/600.0;
vector<double> tangent_x = tform.get_camera_up_direction().cross(radius).normalize();
vector<double> tangent_y = radius.cross(tangent_x).normalize();
vector<double> new_pos = tform.get_camera_pos() + tangent_x*delta.x() + tangent_y*-delta.y();
// now make it have the correct radius relative to the looking at point.
new_pos = (new_pos-tform.get_camera_looking_at()).normalize()*length(radius) + tform.get_camera_looking_at();
tform = camera_transform(new_pos,
tform.get_camera_looking_at(),
tangent_y,
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
parent.invalidate_rectangle(rect);
}
else if (state == (base_window::LEFT|base_window::SHIFT) ||
state == base_window::RIGHT)
{
const point cur(x, y);
dpoint delta = last-cur;
last = cur;
const vector<double> radius = tform.get_camera_pos()-tform.get_camera_looking_at();
delta *= 2*pi*length(radius)/600.0;
vector<double> tangent_x = tform.get_camera_up_direction().cross(radius).normalize();
vector<double> tangent_y = radius.cross(tangent_x).normalize();
vector<double> offset = tangent_x*delta.x() + tangent_y*-delta.y();
tform = camera_transform(
tform.get_camera_pos()+offset,
tform.get_camera_looking_at()+offset,
tform.get_camera_up_direction(),
tform.get_camera_field_of_view(),
std::min(rect.width(),rect.height()));
parent.invalidate_rectangle(rect);
}
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// image_display member functions
......
......@@ -3586,6 +3586,235 @@ namespace dlib
image_display& operator=(image_display&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class perspective_display : public drawable, noncopyable
{
public:
perspective_display(
drawable_window& w
);
~perspective_display(
);
virtual void set_size (
unsigned long width,
unsigned long height
);
struct overlay_line
{
overlay_line() { assign_pixel(color, 0);}
overlay_line(const vector<double>& p1_, const vector<double>& p2_)
: p1(p1_), p2(p2_) { assign_pixel(color, 255); }
template <typename pixel_type>
overlay_line(const vector<double>& p1_, const vector<double>& p2_, pixel_type p)
: p1(p1_), p2(p2_) { assign_pixel(color, p); }
vector<double> p1;
vector<double> p2;
rgb_pixel color;
};
struct overlay_dot
{
overlay_dot() { assign_pixel(color, 0);}
overlay_dot(const vector<double>& p_)
: p(p_) { assign_pixel(color, 255); }
template <typename pixel_type>
overlay_dot(const vector<double>& p_, pixel_type color_)
: p(p_) { assign_pixel(color, color_); }
vector<double> p;
rgb_pixel color;
};
void add_overlay (
const std::vector<overlay_line>& overlay
);
void add_overlay (
const std::vector<overlay_dot>& overlay
);
void clear_overlay (
);
template <
typename T
>
void set_dot_double_clicked_handler (
T& object,
void (T::*event_handler_)(const vector<double>&)
)
{
auto_mutex M(m);
dot_clicked_event_handler = make_mfp(object,event_handler_);
}
void set_dot_double_clicked_handler (
const any_function<void(const vector<double>&)>& event_handler_
);
private:
void draw (
const canvas& c
) const;
void on_wheel_up (
unsigned long state
);
void on_wheel_down (
unsigned long state
);
void on_mouse_down (
unsigned long btn,
unsigned long state,
long x,
long y,
bool is_double_click
);
void on_mouse_move (
unsigned long state,
long x,
long y
);
point last;
std::vector<overlay_line> overlay_lines;
std::vector<overlay_dot> overlay_dots;
camera_transform tform;
vector<double> sum_pts;
vector<double> max_pts;
any_function<void(const vector<double>&)> dot_clicked_event_handler;
};
// ----------------------------------------------------------------------------------------
class perspective_window : public drawable_window, noncopyable
{
public:
typedef perspective_display::overlay_line overlay_line;
typedef perspective_display::overlay_dot overlay_dot;
perspective_window(
) : disp(*this)
{
set_size(100,100);
}
perspective_window(
const std::vector<dlib::vector<double> >& point_cloud
) :
disp(*this)
{
set_size(100,100);
add_overlay(point_cloud);
show();
}
perspective_window(
const std::vector<dlib::vector<double> >& point_cloud,
const std::string& title
) :
disp(*this)
{
set_size(100,100);
add_overlay(point_cloud);
set_title(title);
show();
}
~perspective_window(
)
{
// You should always call close_window() in the destructor of window
// objects to ensure that no events will be sent to this window while
// it is being destructed.
close_window();
}
void add_overlay (
const std::vector<overlay_line>& overlay
)
{
disp.add_overlay(overlay);
}
void add_overlay (
const std::vector<overlay_dot>& overlay
)
{
disp.add_overlay(overlay);
}
void clear_overlay (
)
{
disp.clear_overlay();
}
void add_overlay(const std::vector<dlib::vector<double> >& d)
{
add_overlay(d, 255);
}
template <typename pixel_type>
void add_overlay(const std::vector<dlib::vector<double> >& d, pixel_type p)
{
std::vector<overlay_dot> temp;
temp.resize(d.size());
for (unsigned long i = 0; i < temp.size(); ++i)
temp[i] = overlay_dot(d[i], p);
add_overlay(temp);
}
template <
typename T
>
void set_dot_double_clicked_handler (
T& object,
void (T::*event_handler_)(const vector<double>&)
)
{
disp.set_dot_double_clicked_handler(object,event_handler_);
}
void set_dot_double_clicked_handler (
const any_function<void(const vector<double>&)>& event_handler_
)
{
disp.set_dot_double_clicked_handler(event_handler_);
}
private:
void on_window_resized(
)
{
drawable_window::on_window_resized();
unsigned long width, height;
get_size(width,height);
disp.set_size(width, height);
}
perspective_display disp;
};
// ----------------------------------------------------------------------------------------
class image_window : public drawable_window
......
......@@ -3158,6 +3158,276 @@ namespace dlib
image_window& operator= (image_window&);
};
// ----------------------------------------------------------------------------------------
class perspective_display : public drawable, noncopyable
{
/*!
WHAT THIS OBJECT REPRESENTS
This object is a tool for displaying 3D point clouds on a screen. You can
navigate the display with the mouse. Left click and drag rotates the
camera around the displayed data. Scrolling the mouse wheel zooms and
shift+left click (or just right click) and drag pans the view around.
!*/
public:
perspective_display(
drawable_window& w
);
/*!
ensures
- #*this is properly initialized
- #*this has been added to window w
- #parent_window() == w
!*/
~perspective_display(
);
/*!
ensures
- all resources associated with *this have been released
!*/
void set_size (
unsigned long width,
unsigned long height
);
/*!
ensures
- #width() == width
- #height() == height
- #top() == top()
- #left() == left()
- i.e. The location of the upper left corner of this widget stays the
same but its width and height are modified.
!*/
struct overlay_line
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents a line that is drawn on the screen. Each line
is represented by its two end points (p1 and p2) as well as a color.
!*/
overlay_line() { assign_pixel(color, 0);}
overlay_line(const vector<double>& p1_, const vector<double>& p2_)
: p1(p1_), p2(p2_) { assign_pixel(color, 255); }
template <typename pixel_type>
overlay_line(const vector<double>& p1_, const vector<double>& p2_, pixel_type p)
: p1(p1_), p2(p2_) { assign_pixel(color, p); }
vector<double> p1;
vector<double> p2;
rgb_pixel color;
};
struct overlay_dot
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents a dot that is drawn on the screen. Each dot is
represented by one point and a color.
!*/
overlay_dot() { assign_pixel(color, 0);}
overlay_dot(const vector<double>& p_)
: p(p_) { assign_pixel(color, 255); }
template <typename pixel_type>
overlay_dot(const vector<double>& p_, pixel_type color_)
: p(p_) { assign_pixel(color, color_); }
vector<double> p; // The location of the dot
rgb_pixel color;
};
void add_overlay (
const std::vector<overlay_line>& overlay
);
/*!
ensures
- Adds the given overlay lines into this object such that it will be
displayed.
!*/
void add_overlay (
const std::vector<overlay_dot>& overlay
);
/*!
ensures
- Adds the given overlay dots into this object such that it will be
displayed.
!*/
void clear_overlay (
);
/*!
ensures
- Removes all overlays from this object. The display will be empty.
!*/
template <typename T>
void set_dot_double_clicked_handler (
T& object,
void (T::*event_handler)(const vector<double>&)
);
/*
requires
- event_handler is a valid pointer to a member function in T
ensures
- The event_handler function is called on object when the user double
clicks on one of the overlay dots. The selected dot will be passed to
event_handler().
- Any previous calls to this function are overridden by this new call.
(i.e. you can only have one event handler associated with this
event at a time)
*/
void set_dot_double_clicked_handler (
const any_function<void(const vector<double>&)>& event_handler
);
/*
ensures
- The event_handler function is called when the user double clicks on one
of the overlay dots. The selected dot will be passed to event_handler().
- Any previous calls to this function are overridden by this new call.
(i.e. you can only have one event handler associated with this
event at a time)
*/
};
// ----------------------------------------------------------------------------------------
class perspective_window : public drawable_window, noncopyable
{
/*!
WHAT THIS OBJECT REPRESENTS
This is a simple window that is just a container for a perspective_display.
It exists to make it easy to throw perspective_displays onto the screen
without having to put together your own drawable_window objects.
!*/
public:
typedef perspective_display::overlay_line overlay_line;
typedef perspective_display::overlay_dot overlay_dot;
perspective_window(
);
/*!
ensures
- The window is displayed on the screen and is 100x100 pixels in size.
!*/
perspective_window(
const std::vector<dlib::vector<double> >& point_cloud
);
/*!
ensures
- The window is displayed on the screen and is 100x100 pixels in size.
- This window will have point_cloud added to it via add_overlay() and the
points will all be white.
!*/
perspective_window(
const std::vector<dlib::vector<double> >& point_cloud,
const std::string& title
);
/*!
ensures
- The window is displayed on the screen and is 100x100 pixels in size.
- This window will have point_cloud added to it via add_overlay() and the
points will all be white.
- The title of the window will be set to the given title string.
!*/
~perspective_window(
);
/*!
ensures
- any resources associated with this object have been released
!*/
void add_overlay (
const std::vector<overlay_line>& overlay
);
/*!
ensures
- Adds the given overlay lines into this object such that it will be
displayed.
!*/
void add_overlay (
const std::vector<overlay_dot>& overlay
);
/*!
ensures
- Adds the given overlay dots into this object such that it will be
displayed.
!*/
void clear_overlay (
);
/*!
ensures
- Removes all overlays from this object. The display will be empty.
!*/
void add_overlay(
const std::vector<dlib::vector<double> >& d
);
/*!
ensures
- Adds the given dots into this object such that it will be
displayed. They will be colored white.
!*/
template <typename pixel_type>
void add_overlay(
const std::vector<dlib::vector<double> >& d,
pixel_type p
);
/*!
ensures
- Adds the given dots into this object such that it will be
displayed. They will be colored by pixel color p.
!*/
template < typename T >
void set_dot_double_clicked_handler (
T& object,
void (T::*event_handler)(const vector<double>&)
);
/*
requires
- event_handler is a valid pointer to a member function in T
ensures
- The event_handler function is called on object when the user double
clicks on one of the overlay dots. The selected dot will be passed to
event_handler().
- Any previous calls to this function are overridden by this new call.
(i.e. you can only have one event handler associated with this
event at a time)
*/
void set_dot_double_clicked_handler (
const any_function<void(const vector<double>&)>& event_handler
);
/*
ensures
- The event_handler function is called when the user double clicks on one
of the overlay dots. The selected dot will be passed to event_handler().
- Any previous calls to this function are overridden by this new call.
(i.e. you can only have one event handler associated with this
event at a time)
*/
};
// ----------------------------------------------------------------------------------------
}
......
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