Added Øving 1

This commit is contained in:
2020-01-15 13:13:42 +01:00
parent 130cda80fe
commit 6645dca35f
28 changed files with 2906 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
#include "GUI.h"
#include "std_lib_facilities.h"
#include <sstream>
using namespace Graph_lib;
void Button::attach(Graph_lib::Window& win)
{
pw = new Fl_Button(loc.x, loc.y, width, height, label.c_str());
pw->callback(reinterpret_cast<Fl_Callback*>(do_it), &win); // pass the window
own = &win;
}
int In_box::get_int()
{
Fl_Input& pi = reference_to<Fl_Input>(pw);
const char* p = pi.value();
if (!isdigit(p[0]))
return -999999;
return atoi(p);
}
string In_box::get_string()
{
Fl_Input& pi = reference_to<Fl_Input>(pw);
return string(pi.value());
}
void In_box::attach(Graph_lib::Window& win)
{
pw = new Fl_Input(loc.x, loc.y, width, height, label.c_str());
own = &win;
}
void Out_box::put(int i)
{
Fl_Output& po = reference_to<Fl_Output>(pw);
std::stringstream ss;
ss << i;
po.value(ss.str().c_str());
}
void Out_box::put(const string& s)
{
reference_to<Fl_Output>(pw).value(s.c_str());
}
void Out_box::attach(Graph_lib::Window& win)
{
pw = new Fl_Output(loc.x, loc.y, width, height, label.c_str());
own = &win;
}
int Menu::attach(Button& b)
{
b.width = width;
b.height = height;
switch (k) {
case horizontal:
b.loc = Point{loc.x + offset, loc.y};
offset += b.width;
break;
case vertical:
b.loc = Point{loc.x, loc.y + offset};
offset += b.height;
break;
}
selection.push_back(&b);
return int(selection.size() - 1);
}
int Menu::attach(Button* p)
{
return attach(*p);
}

View File

@@ -0,0 +1,167 @@
//
// This is a GUI support code to the chapters 12-16 of the book
// "Programming -- Principles and Practice Using C++" by Bjarne Stroustrup
//
#ifndef GUI_GUARD
#define GUI_GUARD
#include "Window.h"
#include "Graph.h"
namespace Graph_lib {
//------------------------------------------------------------------------------
typedef void* Address; // Address is a synonym for void*
typedef void (*Callback)(Address, Address); // FLTK's required function type for all callbacks
//------------------------------------------------------------------------------
template <class W> W& reference_to(Address pw)
// treat an address as a reference to a W
{
return *static_cast<W*>(pw);
}
//------------------------------------------------------------------------------
class Widget
{
// Widget is a handle to an Fl_widget - it is *not* an Fl_widget
// We try to keep our interface classes at arm's length from FLTK
public:
Widget(Point xy, int w, int h, const string& s, Callback cb)
: loc(xy), width(w), height(h), label(s), do_it(cb)
{}
void redraw()
{
pw->redraw(); // Schedules redrawing of widget, as per FLTK docs
}
virtual void move(int dx, int dy)
{
hide();
pw->position(loc.x += dx, loc.y += dy);
show();
}
virtual void hide()
{
pw->hide();
}
virtual void show()
{
pw->show();
}
virtual void attach(Window&) = 0;
virtual void clear_value()
{}
virtual void set_label(string l)
{
label = l;
pw->label(label.c_str());
}
Point loc;
int width;
int height;
string label;
Callback do_it;
virtual ~Widget()
{}
protected:
Window* own; // every Widget belongs to a Window
Fl_Widget* pw; // connection to the FLTK Widget
private:
Widget& operator=(const Widget&); // don't copy Widgets
Widget(const Widget&);
};
//------------------------------------------------------------------------------
struct Button : Widget
{
Button(Point xy, int w, int h, const string& label, Callback cb) : Widget(xy, w, h, label, cb)
{}
void attach(Window&);
};
//------------------------------------------------------------------------------
struct In_box : Widget
{
In_box(Point xy, int w, int h, const string& s) : Widget(xy, w, h, s, nullptr)
{}
int get_int();
string get_string();
void clear_value() override
{
static_cast<Fl_Input*>(pw)->value("");
}
void attach(Window& win) override;
};
//------------------------------------------------------------------------------
struct Out_box : Widget
{
Out_box(Point xy, int w, int h, const string& s) : Widget(xy, w, h, s, nullptr)
{}
void put(int);
void put(const string&);
void attach(Graph_lib::Window& win) override;
};
//------------------------------------------------------------------------------
struct Menu : Widget
{
enum Kind { horizontal, vertical };
Menu(Point xy, int w, int h, Kind kk, const string& label)
: Widget(xy, w, h, label, nullptr), k(kk), offset(0)
{}
Vector_ref<Button> selection;
Kind k;
int offset;
int attach(Button& b); // Menu does not delete &b
int attach(Button* p); // Menu deletes p
void show() // show all buttons
{
for (int i = 0; i < selection.size(); ++i)
selection[i].show();
}
void hide() // hide all buttons
{
for (int i = 0; i < selection.size(); ++i)
selection[i].hide();
}
void move(int dx, int dy) // move all buttons
{
for (int i = 0; i < selection.size(); ++i)
selection[i].move(dx, dy);
}
void attach(Window& win) // attach all buttons
{
for (int i = 0; i < selection.size(); ++i)
win.attach(selection[i]);
own = &win;
}
};
//------------------------------------------------------------------------------
} // namespace Graph_lib
#endif // GUI_GUARD

View File

@@ -0,0 +1,358 @@
#include "Graph.h"
#include <map>
namespace Graph_lib {
void Shape::draw_lines() const
{
if (color().visibility() && 1 < points.size()) // draw sole pixel?
for (unsigned int i = 1; i < points.size(); ++i)
fl_line(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y);
}
void Shape::draw() const
{
Fl_Color oldc = fl_color();
// there is no good portable way of retrieving the current style
fl_color(lcolor.as_int());
fl_line_style(ls.style(), ls.width());
draw_lines();
fl_color(oldc); // reset color (to pevious) and style (to default)
fl_line_style(0);
}
// does two lines (p1,p2) and (p3,p4) intersect?
// if se return the distance of the intersect point as distances from p1
inline pair<double, double> line_intersect(Point p1, Point p2, Point p3, Point p4, bool& parallel)
{
double x1 = p1.x;
double x2 = p2.x;
double x3 = p3.x;
double x4 = p4.x;
double y1 = p1.y;
double y2 = p2.y;
double y3 = p3.y;
double y4 = p4.y;
double denom = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
if (denom == 0) {
parallel = true;
return pair<double, double>(0, 0);
}
parallel = false;
return pair<double, double>(((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom,
((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom);
}
// intersection between two line segments
// Returns true if the two segments intersect,
// in which case intersection is set to the point of intersection
bool line_segment_intersect(Point p1, Point p2, Point p3, Point p4, Point& intersection)
{
bool parallel;
pair<double, double> u = line_intersect(p1, p2, p3, p4, parallel);
if (parallel || u.first < 0 || u.first > 1 || u.second < 0 || u.second > 1)
return false;
intersection.x = p1.x + u.first * (p2.x - p1.x);
intersection.y = p1.y + u.first * (p2.y - p1.y);
return true;
}
void Polygon::add(Point p)
{
int np = number_of_points();
if (1 < np) { // check that thenew line isn't parallel to the previous one
if (p == point(np - 1))
error("polygon point equal to previous point");
bool parallel;
line_intersect(point(np - 1), p, point(np - 2), point(np - 1), parallel);
if (parallel)
error("two polygon points lie in a straight line");
}
for (int i = 1; i < np - 1; ++i) { // check that new segment doesn't interset and old point
Point ignore{0, 0};
if (line_segment_intersect(point(np - 1), p, point(i - 1), point(i), ignore))
error("intersect in polygon");
}
Closed_polyline::add(p);
}
void Polygon::draw_lines() const
{
if (number_of_points() < 3)
error("less than 3 points in a Polygon");
Closed_polyline::draw_lines();
}
void Open_polyline::draw_lines() const
{
if (fill_color().visibility()) {
fl_color(fill_color().as_int());
fl_begin_complex_polygon();
for (int i = 0; i < number_of_points(); ++i) {
fl_vertex(point(i).x, point(i).y);
}
fl_end_complex_polygon();
fl_color(color().as_int()); // reset color
}
if (color().visibility())
Shape::draw_lines();
}
void Closed_polyline::draw_lines() const
{
Open_polyline::draw_lines();
if (color().visibility()) // draw closing line:
fl_line(point(number_of_points() - 1).x, point(number_of_points() - 1).y, point(0).x,
point(0).y);
}
void Shape::move(int dx, int dy)
{
for (unsigned int i = 0; i < points.size(); ++i) {
points[i].x += dx;
points[i].y += dy;
}
}
void Lines::draw_lines() const
{
// if (number_of_points()%2==1) error("odd number of points in set of lines");
if (color().visibility())
for (int i = 1; i < number_of_points(); i += 2)
fl_line(point(i - 1).x, point(i - 1).y, point(i).x, point(i).y);
}
void Text::draw_lines() const
{
int ofnt = fl_font();
int osz = fl_size();
fl_font(fnt.as_int(), fnt_sz);
fl_draw(lab.c_str(), point(0).x, point(0).y);
fl_font(ofnt, osz);
}
Function::Function(Fct f, double r1, double r2, Point xy, int count, double xscale, double yscale)
// graph f(x) for x in [r1:r2) using count line segments with (0,0) displayed at
// xy x coordinates are scaled by xscale and y coordinates scaled by yscale
{
if (r2 - r1 <= 0)
error("bad graphing range");
if (count <= 0)
error("non-positive graphing count");
double dist = (r2 - r1) / count;
double r = r1;
for (int i = 0; i < count; ++i) {
add(Point{xy.x + int(r * xscale), xy.y - int(f(r) * yscale)});
r += dist;
}
}
void Rectangle::draw_lines() const
{
if (fill_color().visibility()) { // fill
fl_color(fill_color().as_int());
fl_rectf(point(0).x, point(0).y, w, h);
fl_color(color().as_int()); // reset color
}
if (color().visibility()) { // edge on top of fill
fl_color(color().as_int());
fl_rect(point(0).x, point(0).y, w, h);
}
}
Axis::Axis(Orientation d, Point xy, int length, int n, string lab) : label(Point{0, 0}, lab)
{
if (length < 0)
error("bad axis length");
switch (d) {
case Axis::x: {
Shape::add(xy); // axis line
Shape::add(Point{xy.x + length, xy.y}); // axis line
if (1 < n) {
int dist = length / n;
int x = xy.x + dist;
for (int i = 0; i < n; ++i) {
notches.add(Point{x, xy.y}, Point{x, xy.y - 5});
x += dist;
}
}
// label under the line
label.move(length / 3, xy.y + 20);
break;
}
case Axis::y: {
Shape::add(xy); // a y-axis goes up
Shape::add(Point{xy.x, xy.y - length});
if (1 < n) {
int dist = length / n;
int y = xy.y - dist;
for (int i = 0; i < n; ++i) {
notches.add(Point{xy.x, y}, Point{xy.x + 5, y});
y -= dist;
}
}
// label at top
label.move(xy.x - 10, xy.y - length - 10);
break;
}
case Axis::z:
error("z axis not implemented");
}
}
void Axis::draw_lines() const
{
Shape::draw_lines(); // the line
notches.draw(); // the notches may have a different color from the line
label.draw(); // the label may have a different color from the line
}
void Axis::set_color(Color c)
{
Shape::set_color(c);
notches.set_color(c);
label.set_color(c);
}
void Axis::move(int dx, int dy)
{
Shape::move(dx, dy);
notches.move(dx, dy);
label.move(dx, dy);
}
void Circle::draw_lines() const
{
if (fill_color().visibility()) { // fill
fl_color(fill_color().as_int());
fl_pie(point(0).x, point(0).y, r + r - 1, r + r - 1, 0, 360);
fl_color(color().as_int()); // reset color
}
if (color().visibility()) {
fl_color(color().as_int());
fl_arc(point(0).x, point(0).y, r + r, r + r, 0, 360);
}
}
void Ellipse::draw_lines() const
{
if (fill_color().visibility()) { // fill
fl_color(fill_color().as_int());
fl_pie(point(0).x, point(0).y, w + w - 1, h + h - 1, 0, 360);
fl_color(color().as_int()); // reset color
}
if (color().visibility()) {
fl_color(color().as_int());
fl_arc(point(0).x, point(0).y, w + w, h + h, 0, 360);
}
}
void draw_mark(Point xy, char c)
{
static const int dx = 4;
static const int dy = 4;
string m(1, c);
fl_draw(m.c_str(), xy.x - dx, xy.y + dy);
}
void Marked_polyline::draw_lines() const
{
Open_polyline::draw_lines();
for (int i = 0; i < number_of_points(); ++i)
draw_mark(point(i), mark[i % mark.size()]);
}
std::map<string, Suffix::Encoding> suffix_map;
int init_suffix_map()
{
suffix_map["jpg"] = Suffix::jpg;
suffix_map["JPG"] = Suffix::jpg;
suffix_map["jpeg"] = Suffix::jpg;
suffix_map["JPEG"] = Suffix::jpg;
suffix_map["gif"] = Suffix::gif;
suffix_map["GIF"] = Suffix::gif;
suffix_map["bmp"] = Suffix::bmp;
suffix_map["BMP"] = Suffix::bmp;
return 0;
}
Suffix::Encoding get_encoding(const string& s)
// try to deduce type from file name using a lookup table
{
static int x = init_suffix_map(); // oneshot map init
string::const_iterator p = find(s.begin(), s.end(), '.');
if (p == s.end())
return Suffix::none; // no suffix
string suf(p + 1, s.end());
return suffix_map[suf];
}
bool can_open(const string& s)
// check if a file named s exists and can be opened for reading
{
ifstream ff(s.c_str());
return bool{ff};
}
// somewhat overelaborate constructor
// because errors related to image files can be such a pain to debug
Image::Image(Point xy, string s, Suffix::Encoding e) : w(0), h(0), fn(xy, "")
{
add(xy);
if (!can_open(s)) {
fn.set_label("cannot open \"" + s + '\"');
p = new Bad_image(30, 20); // the "error image"
return;
}
if (e == Suffix::none)
e = get_encoding(s);
switch (e) {
case Suffix::jpg:
p = new Fl_JPEG_Image(s.c_str());
break;
case Suffix::gif:
p = new Fl_GIF_Image(s.c_str());
break;
// case Suffix::bmp:
// p = new Fl_BMP_Image(s.c_str());
// break;
default: // Unsupported image encoding
fn.set_label("unsupported file type \"" + s + '\"');
p = new Bad_image(30, 20); // the "error image"
}
}
void Image::draw_lines() const
{
if (fn.label() != "")
fn.draw_lines();
if (w && h)
p->draw(point(0).x, point(0).y, w, h, cx, cy);
else
p->draw(point(0).x, point(0).y);
}
} // namespace Graph_lib

View File

@@ -0,0 +1,623 @@
#ifndef GRAPH_GUARD
#define GRAPH_GUARD 1
#include "Point.h"
#include <vector>
//#include<string>
//#include<cmath>
#include "fltk.h"
#include "std_lib_facilities.h"
namespace Graph_lib {
// defense against ill-behaved Linux macros:
#undef major
#undef minor
struct Color
{
enum Color_type {
red = FL_RED,
blue = FL_BLUE,
green = FL_GREEN,
yellow = FL_YELLOW,
white = FL_WHITE,
black = FL_BLACK,
magenta = FL_MAGENTA,
cyan = FL_CYAN,
dark_red = FL_DARK_RED,
dark_green = FL_DARK_GREEN,
dark_yellow = FL_DARK_YELLOW,
dark_blue = FL_DARK_BLUE,
dark_magenta = FL_DARK_MAGENTA,
dark_cyan = FL_DARK_CYAN,
gray = FL_GRAY,
mid_gray = 48,
dark_gray = 38,
light_gray = 50
};
enum Transparency { invisible = 0, visible = 255 };
Color(Color_type cc) : v(visible), c(Fl_Color(cc))
{}
Color(Color_type cc, Transparency vv) : v(vv), c(Fl_Color(cc))
{}
Color(int cc) : v(visible), c(Fl_Color(cc))
{}
Color(Transparency vv) : v(vv), c(Fl_Color())
{}
int as_int() const
{
return c;
}
char visibility() const
{
return v;
}
void set_visibility(Transparency vv)
{
v = vv;
}
private:
unsigned char v; // 0 or 1 for now
Fl_Color c;
};
struct Line_style
{
enum Line_style_type {
solid = FL_SOLID, // -------
dash = FL_DASH, // - - - -
dot = FL_DOT, // .......
dashdot = FL_DASHDOT, // - . - .
dashdotdot = FL_DASHDOTDOT // -..-..
};
Line_style(Line_style_type ss) : s(ss), w(0)
{}
Line_style(Line_style_type lst, int ww) : s(lst), w(ww)
{}
Line_style(int ss) : s(ss), w(0)
{}
int width() const
{
return w;
}
int style() const
{
return s;
}
private:
int s;
int w;
};
class Font
{
public:
enum Font_type {
helvetica = FL_HELVETICA,
helvetica_bold = FL_HELVETICA_BOLD,
helvetica_italic = FL_HELVETICA_ITALIC,
helvetica_bold_italic = FL_HELVETICA_BOLD_ITALIC,
courier = FL_COURIER,
courier_bold = FL_COURIER_BOLD,
courier_italic = FL_COURIER_ITALIC,
courier_bold_italic = FL_COURIER_BOLD_ITALIC,
times = FL_TIMES,
times_bold = FL_TIMES_BOLD,
times_italic = FL_TIMES_ITALIC,
times_bold_italic = FL_TIMES_BOLD_ITALIC,
symbol = FL_SYMBOL,
screen = FL_SCREEN,
screen_bold = FL_SCREEN_BOLD,
zapf_dingbats = FL_ZAPF_DINGBATS
};
Font(Font_type ff) : f(ff)
{}
Font(int ff) : f(ff)
{}
int as_int() const
{
return f;
}
private:
int f;
};
template <class T> class Vector_ref
{
vector<T*> v;
vector<T*> owned;
public:
Vector_ref()
{}
Vector_ref(T* a, T* b = 0, T* c = 0, T* d = 0)
{
if (a)
push_back(a);
if (b)
push_back(b);
if (c)
push_back(c);
if (d)
push_back(d);
}
~Vector_ref()
{
for (typename vector<T*>::size_type i = 0; i < owned.size(); ++i)
delete owned[i];
}
void push_back(T& s)
{
v.push_back(&s);
}
void push_back(T* p)
{
v.push_back(p);
owned.push_back(p);
}
T& operator[](int i)
{
return *v[i];
}
const T& operator[](int i) const
{
return *v[i];
}
int size() const
{
return v.size();
}
T& back()
{
return *v[v.size() - 1];
}
const T& back() const
{
return *v[v.size() - 1];
}
using iterator = typename vector<T*>::iterator;
iterator begin()
{
return v.begin();
}
auto end()
{
return v.end();
}
auto begin() const
{
return v.begin();
}
auto end() const
{
return v.end();
}
};
typedef double Fct(double);
class Shape
{ // deals with color and style, and holds sequence of lines
protected:
Shape()
{}
// Not by the book:
// Shape(initializer_list<Point> lst); // add() the Points to this Shape
// Changed init_list constructor, i.e. implemented at all
Shape(initializer_list<Point> lst)
{
for (Point p : lst)
add(p);
} // add() the Points to this Shape
void add(Point p)
{
points.push_back(p);
}
void set_point(int i, Point p)
{
points[i] = p;
}
public:
void draw() const; // deal with color and draw_lines
protected:
virtual void draw_lines() const; // simply draw the appropriate lines
public:
virtual void move(int dx, int dy); // move the shape +=dx and +=dy
void set_color(Color col)
{
lcolor = col;
}
Color color() const
{
return lcolor;
}
void set_style(Line_style sty)
{
ls = sty;
}
Line_style style() const
{
return ls;
}
void set_fill_color(Color col)
{
fcolor = col;
}
Color fill_color() const
{
return fcolor;
}
Point point(int i) const
{
return points[i];
}
int number_of_points() const
{
return int(points.size());
}
virtual ~Shape()
{}
Shape(const Shape&) = delete;
Shape& operator=(const Shape&) = delete;
private:
vector<Point> points; // not used by all shapes
Color lcolor{static_cast<int>(fl_color())};
Line_style ls{0};
Color fcolor{Color::invisible};
};
struct Function : Shape
{
// the function parameters are not stored
Function(Fct f, double r1, double r2, Point orig, int count = 100, double xscale = 25,
double yscale = 25);
};
struct Fill
{
Fill() : no_fill(true), fcolor(0)
{}
Fill(Color c) : no_fill(false), fcolor(c)
{}
void set_fill_color(Color col)
{
fcolor = col;
}
Color fill_color()
{
return fcolor;
}
protected:
bool no_fill;
Color fcolor;
};
struct Line : Shape
{
Line(Point p1, Point p2)
{
add(p1);
add(p2);
}
};
struct Rectangle : Shape
{
Rectangle(Point xy, int ww, int hh) : h{hh}, w{ww}
{
if (h <= 0 || w <= 0)
error("Bad rectangle: non-positive side");
add(xy);
}
Rectangle(Point x, Point y) : h{y.y - x.y}, w{y.x - x.x}
{
if (h <= 0 || w <= 0)
error("Bad rectangle: first point is not top left");
add(x);
}
void draw_lines() const;
int height() const
{
return h;
}
int width() const
{
return w;
}
private:
int h; // height
int w; // width
};
bool intersect(Point p1, Point p2, Point p3, Point p4);
struct Open_polyline : Shape
{ // open sequence of lines
using Shape::Shape;
void add(Point p)
{
Shape::add(p);
}
void draw_lines() const;
};
struct Closed_polyline : Open_polyline
{ // closed sequence of lines
using Open_polyline::Open_polyline;
void draw_lines() const;
// void add(Point p) { Shape::add(p); }
};
struct Polygon : Closed_polyline
{ // closed sequence of non-intersecting lines
using Closed_polyline::Closed_polyline;
void add(Point p);
void draw_lines() const;
};
struct Lines : Shape
{ // indepentdent lines
Lines()
{}
Lines(initializer_list<Point> lst) : Shape{lst}
// Not equal to book constructor.
{
if (lst.size() % 2)
error("odd number of points for Lines");
}
Lines(initializer_list<pair<Point, Point>> lst)
{
// This is the constructor from the book, p.450 1st print.
for (auto p : lst)
add(p.first, p.second);
}
void draw_lines() const;
void add(Point p1, Point p2)
{
Shape::add(p1);
Shape::add(p2);
}
};
struct Text : Shape
{
// the point is the bottom left of the first letter
Text(Point x, const string& s) : lab{s}
{
add(x);
}
void draw_lines() const;
void set_label(const string& s)
{
lab = s;
}
string label() const
{
return lab;
}
void set_font(Font f)
{
fnt = f;
}
Font font() const
{
return Font(fnt);
}
void set_font_size(int s)
{
fnt_sz = s;
}
int font_size() const
{
return fnt_sz;
}
private:
string lab; // label
Font fnt{fl_font()};
int fnt_sz{(14 < fl_size()) ? fl_size() : 14}; // at least 14 point
};
struct Axis : Shape
{
// representation left public
enum Orientation { x, y, z };
Axis(Orientation d, Point xy, int length, int nummber_of_notches = 0, string label = "");
void draw_lines() const;
void move(int dx, int dy);
void set_color(Color c);
Text label;
Lines notches;
// Orientation orin;
// int notches;
};
struct Circle : Shape
{
Circle(Point p, int rr) // center and radius
: r{rr}
{
add(Point{p.x - r, p.y - r});
}
void draw_lines() const;
Point center() const
{
return {point(0).x + r, point(0).y + r};
}
void set_radius(int rr)
{
r = rr;
}
int radius() const
{
return r;
}
private:
int r;
};
struct Ellipse : Shape
{
Ellipse(Point p, int ww,
int hh) // center, min, and max distance from center
: w{ww}, h{hh}
{
add(Point{p.x - ww, p.y - hh});
}
void draw_lines() const;
Point center() const
{
return {point(0).x + w, point(0).y + h};
}
Point focus1() const
{
return {center().x + int(sqrt(double(w * w - h * h))), center().y};
}
Point focus2() const
{
return {center().x - int(sqrt(double(w * w - h * h))), center().y};
}
void set_major(int ww)
{
w = ww;
}
int major() const
{
return w;
}
void set_minor(int hh)
{
h = hh;
}
int minor() const
{
return h;
}
private:
int w;
int h;
};
struct Marked_polyline : Open_polyline
{
Marked_polyline(const string& m) : mark(m)
{}
void draw_lines() const;
private:
string mark;
};
struct Marks : Marked_polyline
{
Marks(const string& m) : Marked_polyline(m)
{
set_color(Color(Color::invisible));
}
};
struct Mark : Marks
{
Mark(Point xy, char c) : Marks(string(1, c))
{
add(xy);
}
};
struct Bad_image : Fl_Image
{
Bad_image(int h, int w) : Fl_Image(h, w, 0)
{}
void draw(int x, int y, int, int, int, int)
{
draw_empty(x, y);
}
};
struct Suffix
{
enum Encoding { none, jpg, gif, bmp };
};
Suffix::Encoding get_encoding(const string& s);
struct Image : Shape
{
Image(Point xy, string s, Suffix::Encoding e = Suffix::none);
~Image()
{
delete p;
}
void draw_lines() const;
void set_mask(Point xy, int ww, int hh)
{
w = ww;
h = hh;
cx = xy.x;
cy = xy.y;
}
void move(int dx, int dy)
{
Shape::move(dx, dy);
p->draw(point(0).x, point(0).y);
}
private:
int w, h, cx, cy; // define "masking box" within image relative to
// position (cx,cy)
Fl_Image* p;
Text fn;
};
} // namespace Graph_lib
#endif

View File

@@ -0,0 +1,27 @@
CXX = clang++
DEBUG = -g
RELEASE = -O2
CXXFLAGS = -std=c++14 -Wall -Wpedantic -I../include
LINK = $(CXX)
CXXFLAGS += $(shell fltk-config --use-gl --use-images --cxxflags)
LDFLAGS += $(shell fltk-config --use-gl --use-images --ldflags)
LDSTATIC = $(shell fltk-config --use-gl --use-images --ldstaticflags)
SOURCES = Graph.cpp GUI.cpp Window.cpp
OBJECTS := $(SOURCES:.cpp=.o)
STATIC_LIB = Graph_lib.a
.SUFFIXES: .o .cpp
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(DEBUG) -c $<
$(STATIC_LIB): $(OBJECTS)
ar -rv $(STATIC_LIB) $(OBJECTS)
$(RM) $(OBJECTS)
.PHONY: clean
clean:
$(RM) $(STATIC_LIB)
$(RM) $(OBJECTS)

View File

@@ -0,0 +1,23 @@
#ifndef POINT_GUARD
#define POINT_GUARD
namespace Graph_lib {
struct Point
{
int x;
int y;
};
inline bool operator==(Point a, Point b)
{
return a.x == b.x && a.y == b.y;
}
inline bool operator!=(Point a, Point b)
{
return !(a == b);
}
} // namespace Graph_lib
#endif

View File

@@ -0,0 +1,48 @@
#pragma once
#include "GUI.h" // for Simple_window only (doesn't really belong in Window.h)
using namespace Graph_lib;
// Simple_window is basic scaffolding for ultra-simple interaction with graphics
// it provides one window with one "next" button for ultra-simple animation
struct Simple_window : Graph_lib::Window
{
Simple_window(Point xy, int w, int h, const string& title)
: Window(xy, w, h, title),
next_button(Point{x_max() - 70, 0}, 70, 20, "Next", cb_next),
button_pushed(false)
{
attach(next_button);
}
void wait_for_button()
// modified event loop
// handle all events (as per default), but quit when button_pushed
// becomes true this allows graphics without control inversion
{
while (!button_pushed)
Fl::wait();
button_pushed = false;
Fl::redraw();
}
Button next_button;
private:
bool button_pushed;
static void cb_next(Address,
Address addr) // callback for next_button
// {
// reference_to<Simple_window>(addr).next();
// }
{
static_cast<Simple_window*>(addr)->next();
}
void next()
{
button_pushed = true;
}
};

View File

@@ -0,0 +1,76 @@
#include "Window.h"
#include "Graph.h"
#include "GUI.h"
namespace Graph_lib {
Window::Window(int ww, int hh, const string& title) : Fl_Window(ww, hh, title.c_str()), w(ww), h(hh)
{
init();
}
Window::Window(Point xy, int ww, int hh, const string& title)
: Fl_Window(xy.x, xy.y, ww, hh, title.c_str()), w(ww), h(hh)
{
init();
}
void Window::init()
{
resizable(this);
show();
}
//----------------------------------------------------
void Window::draw()
{
Fl_Window::draw();
for (vector<Shape*>::size_type i = 0; i < shapes.size(); ++i)
shapes[i]->draw();
}
void Window::attach(Widget& widget)
{
begin(); // FLTK: begin attaching new Fl_Wigets to this window
widget.attach(*this); // let the Widget create its Fl_Wigits
end(); // FLTK: stop attaching new Fl_Wigets to this window
}
void Window::detach(Widget& b)
{
b.hide();
}
void Window::attach(Shape& s)
{
shapes.push_back(&s);
// s.attached = this;
}
void Window::detach(Shape& s)
{
for (vector<Shape*>::size_type i = shapes.size(); 0 < i;
--i) // guess last attached will be first released
if (shapes[i - 1] == &s)
shapes.erase(shapes.begin() + (i - 1)); //&shapes[i-1]);
}
void Window::put_on_top(Shape& p)
{
for (vector<Shape*>::size_type i = 0; i < shapes.size(); ++i) {
if (&p == shapes[i]) {
for (++i; i < shapes.size(); ++i)
shapes[i - 1] = shapes[i];
shapes[shapes.size() - 1] = &p;
return;
}
}
}
int gui_main()
{
return Fl::run();
}
} // namespace Graph_lib

View File

@@ -0,0 +1,56 @@
#ifndef WINDOW_GUARD
#define WINDOW_GUARD 1
#include "fltk.h"
#include "std_lib_facilities.h"
#include "Point.h"
namespace Graph_lib {
class Shape; // "forward declare" Shape
class Widget;
class Window : public Fl_Window {
public:
Window(int w, int h, const string& title ); // let the system pick the location
Window(Point xy, int w, int h, const string& title ); // top left corner in xy
virtual ~Window() { }
int x_max() const { return w; }
int y_max() const { return h; }
void resize(int ww, int hh) { w=ww; h=hh; size(ww,hh); }
using Fl_Window::resize;
void set_label(const string& s) { label(s.c_str()); }
void attach(Shape& s);
void attach(Widget& w);
void detach(Shape& s); // remove s from shapes
void detach(Widget& w); // remove w from window (deactivate callbacks)
void put_on_top(Shape& p); // put p on top of other shapes
void redraw() { Fl_Window::redraw(); }
// Schedules redrawing of widget, as per FLTK docs
protected:
void draw();
private:
int w,h; // window size
vector<Shape*> shapes; // shapes attached to window
void init();
};
int gui_main(); // invoke GUI library's main event loop
inline int x_max() { return Fl::w(); } // width of screen in pixels
inline int y_max() { return Fl::h(); } // height of screen in pixels
}
#endif

View File

@@ -0,0 +1,20 @@
#ifndef FLTK_GUARD
#define FLTK_GUARD 1
#include "FL/Fl.H"
#include "FL/Fl_Window.H"
#include "FL/Fl_Button.H"
#include "FL/Fl_Input.H"
#include "FL/Fl_Output.H"
#include "FL/Fl_Multiline_Output.H"
#include <cstdlib> // for exit(0)
#include "FL/fl_draw.H"
#include "FL/Enumerations.H"
#include "FL/Fl_JPEG_Image.H"
#include "FL/Fl_GIF_Image.H"
#endif

View File

@@ -0,0 +1,261 @@
/*
std_lib_facilities.h
*/
/*
simple "Programming: Principles and Practice using C++ (second edition)"
course header to be used for the first few weeks. It provides the most common
standard headers (in the global namespace) and minimal exception/error
support.
Students: please don't try to understand the details of headers just
yet. All will be explained. This header is primarily used so that you don't
have to understand every concept all at once.
By Chapter 10, you don't need this file and after Chapter 21, you'll
understand it
Revised April 25, 2010: simple_error() added
Revised November 25 2013: remove support for pre-C++11 compilers, use
C++11: <chrono> Revised November 28 2013: add a few container algorithms
Revised June 8 2014: added #ifndef to workaround Microsoft C++11
weakness
Revised for TDT4102, NTNU, 21 December 2018:
more std libs included.
*/
#ifndef H112
#define H112 251113L
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdlib>
#include <string>
#include <list>
#include <forward_list>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <array>
#include <regex>
#include <random>
#include <stdexcept>
#include <set>
#include <chrono>
#include <numeric>
#include <map>
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
typedef long Unicode;
//------------------------------------------------------------------------------
using namespace std;
template <class T> string to_string(const T& t)
{
ostringstream os;
os << t;
return os.str();
}
struct Range_error : out_of_range
{ // enhanced vector range error reporting
int index;
Range_error(int i) : out_of_range("Range error: " + to_string(i)), index(i)
{}
};
// trivially range-checked vector (no iterator checking):
template <class T> struct Vector : public std::vector<T>
{
using size_type = typename std::vector<T>::size_type;
using std::vector<T>::vector; // inheriting constructor
T& operator[](unsigned int i) // rather than return at(i);
{
if (i < 0 || this->size() <= i)
throw Range_error(i);
return std::vector<T>::operator[](i);
}
const T& operator[](unsigned int i) const
{
if (i < 0 || this->size() <= i)
throw Range_error(i);
return std::vector<T>::operator[](i);
}
};
// disgusting macro hack to get a range checked vector:
#define vector Vector
// trivially range-checked string (no iterator checking):
struct String : std::string
{
using size_type = std::string::size_type;
// using string::string;
char& operator[](unsigned int i) // rather than return at(i);
{
if (i < 0 || size() <= i)
throw Range_error(i);
return std::string::operator[](i);
}
const char& operator[](unsigned int i) const
{
if (i < 0 || size() <= i)
throw Range_error(i);
return std::string::operator[](i);
}
};
namespace std {
template <> struct hash<String>
{
size_t operator()(const String& s) const
{
return hash<std::string>()(s);
}
};
} // namespace std
struct Exit : runtime_error
{
Exit() : runtime_error("Exit")
{}
};
// error() simply disguises throws:
inline void error(const string& s)
{
throw runtime_error(s);
}
inline void error(const string& s, const string& s2)
{
error(s + s2);
}
inline void error(const string& s, int i)
{
ostringstream os;
os << s << ": " << i;
error(os.str());
}
template <class T> char* as_bytes(T& i) // needed for binary I/O
{
void* addr = &i; // get the address of the first byte
// of memory used to store the object
return static_cast<char*>(addr); // treat that memory as bytes
}
inline void keep_window_open()
{
cin.clear();
cout << "Please enter a character to exit\n";
char ch;
cin >> ch;
return;
}
inline void keep_window_open(string s)
{
if (s == "")
return;
cin.clear();
cin.ignore(120, '\n');
for (;;) {
cout << "Please enter " << s << " to exit\n";
string ss;
while (cin >> ss && ss != s)
cout << "Please enter " << s << " to exit\n";
return;
}
}
// error function to be used (only) until error() is introduced in Chapter 5:
inline void simple_error(string s) // write ``error: s and exit program
{
cerr << "error: " << s << '\n';
keep_window_open(); // for some Windows environments
exit(1);
}
// make std::min() and std::max() accessible on systems with antisocial macros:
#undef min
#undef max
// run-time checked narrowing cast (type conversion).
template <class R, class A> R narrow_cast(const A& a)
{
R r = R(a);
if (A(r) != a)
error(string("info loss"));
return r;
}
// random number generators. See 24.7.
inline int randint(int min, int max)
{
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
static default_random_engine ran{static_cast<unsigned int>(seed)};
return uniform_int_distribution<>{min, max}(ran);
}
inline int randint(int max)
{
return randint(0, max);
}
// container algorithms. See 21.9.
template <typename C> using Value_type = typename C::value_type;
template <typename C> using Iterator = typename C::iterator;
template <typename C>
// requires Container<C>()
void sort(C& c)
{
std::sort(c.begin(), c.end());
}
template <typename C, typename Pred>
// requires Container<C>() && Binary_Predicate<Value_type<C>>()
void sort(C& c, Pred p)
{
std::sort(c.begin(), c.end(), p);
}
template <typename C, typename Val>
// requires Container<C>() && Equality_comparable<C,Val>()
Iterator<C> find(C& c, Val v)
{
return std::find(c.begin(), c.end(), v);
}
template <typename C, typename Pred>
// requires Container<C>() && Predicate<Pred,Value_type<C>>()
Iterator<C> find_if(C& c, Pred p)
{
return std::find_if(c.begin(), c.end(), p);
}
#endif // H112