curvedraw

changeset 0:8e524989c904

getting there
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 15 Dec 2015 07:15:53 +0200
parents
children 7dcd0f6113e5
files .hgignore Makefile src/app.cc src/app.h src/curve.cc src/curve.h src/main.cc src/opengl.h src/widgets.cc src/widgets.h
diffstat 10 files changed, 737 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Tue Dec 15 07:15:53 2015 +0200
     1.3 @@ -0,0 +1,4 @@
     1.4 +\.o$
     1.5 +\.d$
     1.6 +\.swp$
     1.7 +^curvedraw$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/Makefile	Tue Dec 15 07:15:53 2015 +0200
     2.3 @@ -0,0 +1,45 @@
     2.4 +PREFIX = /usr/local
     2.5 +
     2.6 +src = $(wildcard src/*.cc)
     2.7 +obj = $(src:.cc=.o)
     2.8 +dep = $(obj:.o=.d)
     2.9 +bin = curvedraw
    2.10 +
    2.11 +CXXFLAGS = -pedantic -Wall -g
    2.12 +LDFLAGS = $(libgl) -lvmath -ldrawtext -lm
    2.13 +
    2.14 +sys := $(shell uname -s | sed 's/MINGW.*/win32/')
    2.15 +
    2.16 +ifeq ($(sys), Darwin)
    2.17 +	libgl = -framework OpenGL -framework GLUT
    2.18 +else ifeq ($(sys), win32)
    2.19 +	libgl = -lopengl32 -lglut32
    2.20 +else
    2.21 +	libgl = -lGL -lglut
    2.22 +endif
    2.23 +
    2.24 +$(bin): $(obj)
    2.25 +	$(CXX) -o $@ $(obj) $(LDFLAGS)
    2.26 +
    2.27 +-include $(dep)
    2.28 +
    2.29 +%.d: %.cc
    2.30 +	@$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@
    2.31 +
    2.32 +
    2.33 +.PHONY: clean
    2.34 +clean:
    2.35 +	rm -f $(obj) $(bin)
    2.36 +
    2.37 +.PHONY: cleandep
    2.38 +cleandep:
    2.39 +	rm -f $(dep)
    2.40 +
    2.41 +.PHONY: install
    2.42 +install: $(bin)
    2.43 +	mkdir -p $(DESTDIR)$(PREFIX)/bin
    2.44 +	cp $(bin) $(DESTDIR)$(PREFIX)/bin/$(bin)
    2.45 +
    2.46 +.PHONY: uninstall
    2.47 +uninstall:
    2.48 +	rm -f $(DESTDIR)$(PREFIX)/bin/$(bin)
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/app.cc	Tue Dec 15 07:15:53 2015 +0200
     3.3 @@ -0,0 +1,292 @@
     3.4 +#include <stdlib.h>
     3.5 +#include <float.h>
     3.6 +#include <vector>
     3.7 +#include <algorithm>
     3.8 +#include "opengl.h"
     3.9 +#include "app.h"
    3.10 +#include "curve.h"
    3.11 +#include "widgets.h"
    3.12 +
    3.13 +static void draw_grid(float sz, float sep, float alpha = 1.0f);
    3.14 +static void draw_curve(const Curve *curve);
    3.15 +static void on_click(int bn, float u, float v);
    3.16 +
    3.17 +static int win_width, win_height;
    3.18 +static float win_aspect;
    3.19 +
    3.20 +static float view_pan_x, view_pan_y;
    3.21 +static float view_scale = 1.0f;
    3.22 +
    3.23 +static std::vector<Curve*> curves;
    3.24 +static Curve *sel_curve, *new_curve;
    3.25 +static int sel_pidx = -1;
    3.26 +
    3.27 +static Label *weight_label;
    3.28 +
    3.29 +
    3.30 +bool app_init(int argc, char **argv)
    3.31 +{
    3.32 +	glEnable(GL_MULTISAMPLE);
    3.33 +	glEnable(GL_CULL_FACE);
    3.34 +	return true;
    3.35 +}
    3.36 +
    3.37 +void app_cleanup()
    3.38 +{
    3.39 +	for(size_t i=0; i<curves.size(); i++) {
    3.40 +		delete curves[i];
    3.41 +	}
    3.42 +	curves.clear();
    3.43 +}
    3.44 +
    3.45 +void app_draw()
    3.46 +{
    3.47 +	glClearColor(0.16, 0.16, 0.16, 1);
    3.48 +	glClear(GL_COLOR_BUFFER_BIT);
    3.49 +
    3.50 +	glMatrixMode(GL_MODELVIEW);
    3.51 +	glLoadIdentity();
    3.52 +	glTranslatef(-view_pan_x, -view_pan_y, 0);
    3.53 +	glScalef(view_scale, view_scale, view_scale);
    3.54 +
    3.55 +	draw_grid(std::max(win_aspect, 1.0f / win_aspect), 0.1);
    3.56 +
    3.57 +	for(size_t i=0; i<curves.size(); i++) {
    3.58 +		draw_curve(curves[i]);
    3.59 +	}
    3.60 +	if(new_curve) {
    3.61 +		// TODO special drawing with more feedback
    3.62 +		draw_curve(new_curve);
    3.63 +	}
    3.64 +	if(weight_label) {
    3.65 +		weight_label->draw();
    3.66 +	}
    3.67 +}
    3.68 +
    3.69 +static void draw_grid(float sz, float sep, float alpha)
    3.70 +{
    3.71 +	float x = 0.0f;
    3.72 +
    3.73 +	glLineWidth(1.0);
    3.74 +	glBegin(GL_LINES);
    3.75 +	glColor4f(0.6, 0.3, 0.2, alpha);
    3.76 +	glVertex2f(-sz, 0);
    3.77 +	glVertex2f(sz, 0);
    3.78 +	glColor4f(0.2, 0.3, 0.6, alpha);
    3.79 +	glVertex2f(0, -sz);
    3.80 +	glVertex2f(0, sz);
    3.81 +	glColor4f(0.4, 0.4, 0.4, alpha);
    3.82 +	while(x < sz) {
    3.83 +		x += sep;
    3.84 +		glVertex2f(-sz, x);
    3.85 +		glVertex2f(sz, x);
    3.86 +		glVertex2f(-sz, -x);
    3.87 +		glVertex2f(sz, -x);
    3.88 +		glVertex2f(x, -sz);
    3.89 +		glVertex2f(x, sz);
    3.90 +		glVertex2f(-x, -sz);
    3.91 +		glVertex2f(-x, sz);
    3.92 +	}
    3.93 +	glEnd();
    3.94 +}
    3.95 +
    3.96 +static void draw_curve(const Curve *curve)
    3.97 +{
    3.98 +	int numpt = curve->get_point_count();
    3.99 +	int segm = numpt * 16.0f;
   3.100 +
   3.101 +	glBegin(GL_LINE_STRIP);
   3.102 +	if(curve == sel_curve) {
   3.103 +		glLineWidth(3.0);
   3.104 +		glColor3f(0.2, 0.8, 0.3);
   3.105 +	} else {
   3.106 +		glLineWidth(2.0);
   3.107 +		glColor3f(0.8, 0.75, 0.3);
   3.108 +	}
   3.109 +	for(int i=0; i<segm; i++) {
   3.110 +		float t = (float)i / (float)(segm - 1);
   3.111 +		Vector2 v = curve->interpolate(t);
   3.112 +		glVertex2f(v.x, v.y);
   3.113 +	}
   3.114 +	glEnd();
   3.115 +	glLineWidth(1.0);
   3.116 +
   3.117 +	glPointSize(5.0);
   3.118 +	glBegin(GL_POINTS);
   3.119 +	glColor3f(1.0, 0.5, 0.2);
   3.120 +	for(int i=0; i<numpt; i++) {
   3.121 +		Vector2 pt = curve->get_point(i);
   3.122 +		glVertex2f(pt.x, pt.y);
   3.123 +	}
   3.124 +	glEnd();
   3.125 +	glPointSize(1.0);
   3.126 +}
   3.127 +
   3.128 +void app_reshape(int x, int y)
   3.129 +{
   3.130 +	win_width = x;
   3.131 +	win_height = y;
   3.132 +	win_aspect = (float)x / (float)y;
   3.133 +
   3.134 +	glViewport(0, 0, x, y);
   3.135 +	glMatrixMode(GL_PROJECTION);
   3.136 +	glLoadIdentity();
   3.137 +	glOrtho(-win_aspect, win_aspect, -1, 1, -1, 1);
   3.138 +}
   3.139 +
   3.140 +void app_keyboard(int key, bool pressed)
   3.141 +{
   3.142 +	if(pressed) {
   3.143 +		switch(key) {
   3.144 +		case 27:
   3.145 +			exit(0);
   3.146 +
   3.147 +		case 'l':
   3.148 +		case 'L':
   3.149 +			if(sel_curve) {
   3.150 +				sel_curve->set_type(CURVE_LINEAR);
   3.151 +				post_redisplay();
   3.152 +			}
   3.153 +			break;
   3.154 +
   3.155 +		case 'b':
   3.156 +		case 'B':
   3.157 +			if(sel_curve) {
   3.158 +				sel_curve->set_type(CURVE_BSPLINE);
   3.159 +				post_redisplay();
   3.160 +			}
   3.161 +			break;
   3.162 +
   3.163 +		case 'h':
   3.164 +		case 'H':
   3.165 +			if(sel_curve) {
   3.166 +				sel_curve->set_type(CURVE_HERMITE);
   3.167 +				post_redisplay();
   3.168 +			}
   3.169 +			break;
   3.170 +		}
   3.171 +	}
   3.172 +}
   3.173 +
   3.174 +static Vector2 pixel_to_uv(int x, int y)
   3.175 +{
   3.176 +	float u = win_aspect * (2.0 * (float)x / (float)win_width - 1.0);
   3.177 +	float v = 1.0 - 2.0 * (float)y / (float)win_height;
   3.178 +	return Vector2(u, v);
   3.179 +}
   3.180 +
   3.181 +static int prev_x, prev_y;
   3.182 +static int click_pos[8][2];
   3.183 +static unsigned int bnstate;
   3.184 +
   3.185 +#define BNBIT(x)	(1 << (x))
   3.186 +
   3.187 +void app_mouse_button(int bn, bool pressed, int x, int y)
   3.188 +{
   3.189 +	prev_x = x;
   3.190 +	prev_y = y;
   3.191 +	if(pressed) {
   3.192 +		bnstate |= BNBIT(bn);
   3.193 +	} else {
   3.194 +		bnstate &= ~BNBIT(bn);
   3.195 +	}
   3.196 +
   3.197 +	if(pressed) {
   3.198 +		click_pos[bn][0] = x;
   3.199 +		click_pos[bn][1] = y;
   3.200 +	} else {
   3.201 +		int dx = x - click_pos[bn][0];
   3.202 +		int dy = y - click_pos[bn][1];
   3.203 +
   3.204 +		if(abs(dx) + abs(dy) < 3) {
   3.205 +			Vector2 uv = pixel_to_uv(x, y);
   3.206 +			on_click(bn, uv.x, uv.y);
   3.207 +		}
   3.208 +
   3.209 +		if(!(bnstate & BNBIT(2))) {
   3.210 +			delete weight_label;
   3.211 +			weight_label = 0;
   3.212 +			post_redisplay();
   3.213 +		}
   3.214 +	}
   3.215 +}
   3.216 +
   3.217 +static void hover(int x, int y)
   3.218 +{
   3.219 +	float thres = pixel_to_uv(5, 0).x - pixel_to_uv(0, 0).x;
   3.220 +
   3.221 +	Vector2 uv = pixel_to_uv(x, y);
   3.222 +	for(size_t i=0; i<curves.size(); i++) {
   3.223 +		int pidx = curves[i]->nearest_point(uv);
   3.224 +		if(pidx == -1) continue;
   3.225 +
   3.226 +		Vector2 cp = curves[i]->get_point(pidx);
   3.227 +		if((cp - uv).length_sq() < thres * thres) {
   3.228 +			sel_curve = curves[i];
   3.229 +			sel_pidx = pidx;
   3.230 +			return;
   3.231 +		}
   3.232 +	}
   3.233 +
   3.234 +	sel_curve = 0;
   3.235 +	sel_pidx = -1;
   3.236 +}
   3.237 +
   3.238 +void app_mouse_motion(int x, int y)
   3.239 +{
   3.240 +	int dx = x - prev_x;
   3.241 +	int dy = y - prev_y;
   3.242 +	prev_x = x;
   3.243 +	prev_y = y;
   3.244 +
   3.245 +	if(!dx && !dy) return;
   3.246 +
   3.247 +	if(!new_curve && !bnstate) {
   3.248 +		hover(x, y);
   3.249 +		post_redisplay();
   3.250 +	}
   3.251 +
   3.252 +	if(sel_curve && sel_pidx != -1) {
   3.253 +		if(bnstate & BNBIT(0)) {
   3.254 +			float w = sel_curve->get_weight(sel_pidx);
   3.255 +			sel_curve->set_point(sel_pidx, pixel_to_uv(x, y), w);
   3.256 +			post_redisplay();
   3.257 +		}
   3.258 +
   3.259 +		if(bnstate & BNBIT(2)) {
   3.260 +			float w = sel_curve->get_weight(sel_pidx);
   3.261 +			w -= dy * 0.1;
   3.262 +			if(w < FLT_MIN) w = FLT_MIN;
   3.263 +			sel_curve->set_weight(sel_pidx, w);
   3.264 +
   3.265 +			if(!weight_label) {
   3.266 +				weight_label = new Label;
   3.267 +			}
   3.268 +			weight_label->set_position(pixel_to_uv(x, y));
   3.269 +			weight_label->set_textf("w=%g", w);
   3.270 +			post_redisplay();
   3.271 +		}
   3.272 +	}
   3.273 +}
   3.274 +
   3.275 +static void on_click(int bn, float u, float v)
   3.276 +{
   3.277 +	switch(bn) {
   3.278 +	case 0:
   3.279 +		if(!new_curve) {
   3.280 +			new_curve = new Curve;
   3.281 +		}
   3.282 +		new_curve->add_point(Vector2(u, v));
   3.283 +		post_redisplay();
   3.284 +		break;
   3.285 +
   3.286 +	case 2:
   3.287 +		curves.push_back(new_curve);
   3.288 +		new_curve = 0;
   3.289 +		post_redisplay();
   3.290 +		break;
   3.291 +
   3.292 +	default:
   3.293 +		break;
   3.294 +	}
   3.295 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/app.h	Tue Dec 15 07:15:53 2015 +0200
     4.3 @@ -0,0 +1,15 @@
     4.4 +#ifndef APP_H_
     4.5 +#define APP_H_
     4.6 +
     4.7 +bool app_init(int argc, char **argv);
     4.8 +void app_cleanup();
     4.9 +
    4.10 +void app_draw();
    4.11 +void app_reshape(int x, int y);
    4.12 +void app_keyboard(int key, bool pressed);
    4.13 +void app_mouse_button(int bn, bool pressed, int x, int y);
    4.14 +void app_mouse_motion(int x, int y);
    4.15 +
    4.16 +void post_redisplay();	// in main.cc
    4.17 +
    4.18 +#endif	// APP_H_
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/curve.cc	Tue Dec 15 07:15:53 2015 +0200
     5.3 @@ -0,0 +1,144 @@
     5.4 +#include <float.h>
     5.5 +#include "curve.h"
     5.6 +
     5.7 +Curve::Curve(CurveType type)
     5.8 +{
     5.9 +	this->type = type;
    5.10 +}
    5.11 +
    5.12 +void Curve::set_type(CurveType type)
    5.13 +{
    5.14 +	this->type = type;
    5.15 +}
    5.16 +
    5.17 +CurveType Curve::get_type() const
    5.18 +{
    5.19 +	return type;
    5.20 +}
    5.21 +
    5.22 +void Curve::add_point(const Vector2 &p, float weight)
    5.23 +{
    5.24 +	cp.push_back(Vector3(p.x, p.y, weight));
    5.25 +}
    5.26 +
    5.27 +bool Curve::remove_point(int idx)
    5.28 +{
    5.29 +	if(idx < 0 || idx >= (int)cp.size()) {
    5.30 +		return false;
    5.31 +	}
    5.32 +	cp.erase(cp.begin() + idx);
    5.33 +	return true;
    5.34 +}
    5.35 +
    5.36 +int Curve::nearest_point(const Vector2 &p)
    5.37 +{
    5.38 +	int res = -1;
    5.39 +	float bestsq = FLT_MAX;
    5.40 +
    5.41 +	for(size_t i=0; i<cp.size(); i++) {
    5.42 +		float d = (get_point(i) - p).length_sq();
    5.43 +		if(d < bestsq) {
    5.44 +			bestsq = d;
    5.45 +			res = i;
    5.46 +		}
    5.47 +	}
    5.48 +
    5.49 +	return res;
    5.50 +}
    5.51 +
    5.52 +int Curve::get_point_count() const
    5.53 +{
    5.54 +	return (int)cp.size();
    5.55 +}
    5.56 +
    5.57 +const Vector3 &Curve::get_homo_point(int idx) const
    5.58 +{
    5.59 +	return cp[idx];
    5.60 +}
    5.61 +
    5.62 +Vector2 Curve::get_point(int idx) const
    5.63 +{
    5.64 +	return Vector2(cp[idx].x, cp[idx].y);
    5.65 +}
    5.66 +
    5.67 +float Curve::get_weight(int idx) const
    5.68 +{
    5.69 +	return cp[idx].z;
    5.70 +}
    5.71 +
    5.72 +bool Curve::set_point(int idx, const Vector2 &p, float weight)
    5.73 +{
    5.74 +	if(idx < 0 || idx >= (int)cp.size()) {
    5.75 +		return false;
    5.76 +	}
    5.77 +	cp[idx] = Vector3(p.x, p.y, weight);
    5.78 +	return true;
    5.79 +}
    5.80 +
    5.81 +bool Curve::set_weight(int idx, float weight)
    5.82 +{
    5.83 +	if(idx < 0 || idx >= (int)cp.size()) {
    5.84 +		return false;
    5.85 +	}
    5.86 +	cp[idx].z = weight;
    5.87 +	return true;
    5.88 +}
    5.89 +
    5.90 +Vector2 Curve::interpolate(float t, CurveType type) const
    5.91 +{
    5.92 +	int num_cp = (int)cp.size();
    5.93 +	if(!num_cp) {
    5.94 +		return Vector2(0, 0);
    5.95 +	}
    5.96 +	if(num_cp == 1) {
    5.97 +		return Vector2(cp[0].x, cp[0].y);
    5.98 +	}
    5.99 +
   5.100 +	Vector3 res;
   5.101 +	int idx0 = (int)floor(t * (num_cp - 1));
   5.102 +	int idx1 = idx0 + 1;
   5.103 +
   5.104 +	float dt = 1.0 / (float)(num_cp - 1);
   5.105 +	float t0 = (float)idx0 * dt;
   5.106 +	float t1 = (float)idx1 * dt;
   5.107 +
   5.108 +	t = (t - t0) / (t1 - t0);
   5.109 +	if(t < 0.0) t = 0.0;
   5.110 +	if(t > 1.0) t = 1.0;
   5.111 +
   5.112 +	if(type == CURVE_LINEAR || num_cp <= 2) {
   5.113 +		res = lerp(cp[idx0], cp[idx1], t);
   5.114 +	} else {
   5.115 +		int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1;
   5.116 +		int idx_next = idx1 >= num_cp - 1 ? idx1 : idx1 + 1;
   5.117 +
   5.118 +		if(type == CURVE_HERMITE) {
   5.119 +			res = catmull_rom_spline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t);
   5.120 +		} else {
   5.121 +			res = bspline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t);
   5.122 +			if(res.z != 0.0f) {
   5.123 +				res.x /= res.z;
   5.124 +				res.y /= res.z;
   5.125 +			}
   5.126 +		}
   5.127 +	}
   5.128 +
   5.129 +	return Vector2(res.x, res.y);
   5.130 +}
   5.131 +
   5.132 +Vector2 Curve::interpolate(float t) const
   5.133 +{
   5.134 +	return interpolate(t, type);
   5.135 +}
   5.136 +
   5.137 +Vector2 Curve::operator ()(float t) const
   5.138 +{
   5.139 +	return interpolate(t);
   5.140 +}
   5.141 +
   5.142 +float Curve::map_param(float x) const
   5.143 +{
   5.144 +	if(x < 0.0f) x = 0.0f;
   5.145 +	if(x >= 1.0f) x = 1.0f;
   5.146 +	return x * ((float)cp.size() - 1);
   5.147 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/curve.h	Tue Dec 15 07:15:53 2015 +0200
     6.3 @@ -0,0 +1,44 @@
     6.4 +#ifndef CURVE_H_
     6.5 +#define CURVE_H_
     6.6 +
     6.7 +#include <vector>
     6.8 +#include <vmath/vmath.h>
     6.9 +
    6.10 +enum CurveType {
    6.11 +	CURVE_LINEAR,
    6.12 +	CURVE_HERMITE,
    6.13 +	CURVE_BSPLINE
    6.14 +};
    6.15 +
    6.16 +class Curve {
    6.17 +private:
    6.18 +	std::vector<Vector3> cp;
    6.19 +	CurveType type;
    6.20 +
    6.21 +	float map_param(float x) const;
    6.22 +
    6.23 +public:
    6.24 +	Curve(CurveType type = CURVE_HERMITE);
    6.25 +
    6.26 +	void set_type(CurveType type);
    6.27 +	CurveType get_type() const;
    6.28 +
    6.29 +	void add_point(const Vector2 &p, float weight = 1.0f);
    6.30 +	bool remove_point(int idx);
    6.31 +
    6.32 +	int nearest_point(const Vector2 &p);
    6.33 +
    6.34 +	int get_point_count() const;
    6.35 +	const Vector3 &get_homo_point(int idx) const;	// homogeneous point
    6.36 +	Vector2 get_point(int idx) const;
    6.37 +	float get_weight(int idx) const;
    6.38 +
    6.39 +	bool set_point(int idx, const Vector2 &p, float weight = 1.0f);
    6.40 +	bool set_weight(int idx, float weight);
    6.41 +
    6.42 +	Vector2 interpolate(float t, CurveType type) const;
    6.43 +	Vector2 interpolate(float t) const;
    6.44 +	Vector2 operator ()(float t) const;
    6.45 +};
    6.46 +
    6.47 +#endif	// CURVE_H_
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/main.cc	Tue Dec 15 07:15:53 2015 +0200
     7.3 @@ -0,0 +1,68 @@
     7.4 +#include <stdio.h>
     7.5 +#include <stdlib.h>
     7.6 +#ifdef __APPLE__
     7.7 +#include <GLUT/glut.h>
     7.8 +#else
     7.9 +#include <GL/glut.h>
    7.10 +#endif
    7.11 +#include "app.h"
    7.12 +
    7.13 +static void display();
    7.14 +static void keydown(unsigned char key, int x, int y);
    7.15 +static void keyup(unsigned char key, int x, int y);
    7.16 +static void mouse(int bn, int st, int x, int y);
    7.17 +
    7.18 +int main(int argc, char **argv)
    7.19 +{
    7.20 +	glutInit(&argc, argv);
    7.21 +	glutInitWindowSize(1280, 720);
    7.22 +	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
    7.23 +	glutCreateWindow("Curve Draw");
    7.24 +
    7.25 +	glutDisplayFunc(display);
    7.26 +	glutReshapeFunc(app_reshape);
    7.27 +	glutKeyboardFunc(keydown);
    7.28 +	glutKeyboardUpFunc(keyup);
    7.29 +	glutMouseFunc(mouse);
    7.30 +	glutMotionFunc(app_mouse_motion);
    7.31 +	glutPassiveMotionFunc(app_mouse_motion);
    7.32 +
    7.33 +	if(!app_init(argc, argv)) {
    7.34 +		return 1;
    7.35 +	}
    7.36 +	atexit(app_cleanup);
    7.37 +
    7.38 +	glutMainLoop();
    7.39 +	return 0;
    7.40 +}
    7.41 +
    7.42 +void post_redisplay()
    7.43 +{
    7.44 +	glutPostRedisplay();
    7.45 +}
    7.46 +
    7.47 +static void display()
    7.48 +{
    7.49 +	app_draw();
    7.50 +	glutSwapBuffers();
    7.51 +}
    7.52 +
    7.53 +static void reshape(int x, int y)
    7.54 +{
    7.55 +	app_reshape(x, y);
    7.56 +}
    7.57 +
    7.58 +static void keydown(unsigned char key, int x, int y)
    7.59 +{
    7.60 +	app_keyboard(key, true);
    7.61 +}
    7.62 +
    7.63 +static void keyup(unsigned char key, int x, int y)
    7.64 +{
    7.65 +	app_keyboard(key, false);
    7.66 +}
    7.67 +
    7.68 +static void mouse(int bn, int st, int x, int y)
    7.69 +{
    7.70 +	app_mouse_button(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y);
    7.71 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/opengl.h	Tue Dec 15 07:15:53 2015 +0200
     8.3 @@ -0,0 +1,14 @@
     8.4 +#ifndef OPENGL_H_
     8.5 +#define OPENGL_H_
     8.6 +
     8.7 +#ifdef WIN32
     8.8 +#include <windows.h>
     8.9 +#endif
    8.10 +
    8.11 +#ifdef __APPLE__
    8.12 +#include <OpenGL/gl.h>
    8.13 +#else
    8.14 +#include <GL/gl.h>
    8.15 +#endif
    8.16 +
    8.17 +#endif	// OPENGL_H_
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/widgets.cc	Tue Dec 15 07:15:53 2015 +0200
     9.3 @@ -0,0 +1,81 @@
     9.4 +#include <stdio.h>
     9.5 +#include <stdlib.h>
     9.6 +#include <string.h>
     9.7 +#include <stdarg.h>
     9.8 +#include <drawtext.h>
     9.9 +#include "opengl.h"
    9.10 +#include "widgets.h"
    9.11 +
    9.12 +static dtx_font *font;
    9.13 +
    9.14 +static void init_font()
    9.15 +{
    9.16 +	if(font) return;
    9.17 +
    9.18 +	if(!(font = dtx_open_font("data/droid_sans.ttf", 16))) {
    9.19 +		fprintf(stderr, "failed to load font\n");
    9.20 +		abort();
    9.21 +	}
    9.22 +	dtx_use_font(font, 16);
    9.23 +}
    9.24 +
    9.25 +Widget::Widget()
    9.26 +{
    9.27 +	text = 0;
    9.28 +}
    9.29 +
    9.30 +Widget::~Widget()
    9.31 +{
    9.32 +	delete [] text;
    9.33 +}
    9.34 +
    9.35 +void Widget::set_position(const Vector2 &p)
    9.36 +{
    9.37 +	pos = p;
    9.38 +}
    9.39 +
    9.40 +const Vector2 &Widget::get_position() const
    9.41 +{
    9.42 +	return pos;
    9.43 +}
    9.44 +
    9.45 +void Widget::set_text(const char *str)
    9.46 +{
    9.47 +	text = new char[strlen(str) + 1];
    9.48 +	strcpy(text, str);
    9.49 +}
    9.50 +
    9.51 +void Widget::set_textf(const char *str, ...)
    9.52 +{
    9.53 +	va_list ap;
    9.54 +	int sz = strlen(str) * 4;
    9.55 +	char *buf = (char*)alloca(sz);
    9.56 +
    9.57 +	va_start(ap, str);
    9.58 +	vsnprintf(buf, sz - 1, text, ap);
    9.59 +	va_end(ap);
    9.60 +
    9.61 +	set_text(buf);
    9.62 +}
    9.63 +
    9.64 +const char *Widget::get_text() const
    9.65 +{
    9.66 +	return text;
    9.67 +}
    9.68 +
    9.69 +// ---- label ----
    9.70 +
    9.71 +void Label::draw() const
    9.72 +{
    9.73 +	init_font();
    9.74 +
    9.75 +	glMatrixMode(GL_MODELVIEW);
    9.76 +	glPushMatrix();
    9.77 +	glTranslatef(pos.x, pos.y, 0);
    9.78 +	glScalef(0.1, 0.1, 1);
    9.79 +
    9.80 +	dtx_string(text);
    9.81 +	dtx_flush();
    9.82 +
    9.83 +	glPopMatrix();
    9.84 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/widgets.h	Tue Dec 15 07:15:53 2015 +0200
    10.3 @@ -0,0 +1,30 @@
    10.4 +#ifndef WIDGETS_H_
    10.5 +#define WIDGETS_H_
    10.6 +
    10.7 +#include <vmath/vmath.h>
    10.8 +
    10.9 +class Widget {
   10.10 +protected:
   10.11 +	Vector2 pos;
   10.12 +	char *text;
   10.13 +
   10.14 +public:
   10.15 +	Widget();
   10.16 +	virtual ~Widget();
   10.17 +
   10.18 +	virtual void set_position(const Vector2 &p);
   10.19 +	virtual const Vector2 &get_position() const;
   10.20 +
   10.21 +	virtual void set_text(const char *str);
   10.22 +	virtual void set_textf(const char *str, ...);
   10.23 +	virtual const char *get_text() const;
   10.24 +
   10.25 +	virtual void draw() const = 0;
   10.26 +};
   10.27 +
   10.28 +class Label : public Widget {
   10.29 +public:
   10.30 +	virtual void draw() const;
   10.31 +};
   10.32 +
   10.33 +#endif	// WIDGETS_H_