4 #include "vmath/vmath.h"
19 SDL_Cursor *tool_cursors[TOOL_COUNT];
24 std::vector<Curve*> curves;
32 void reset_state(EditState *s = 0);
34 Vector2 world_to_dev(const Vector2 &pt);
35 Vector2 dev_to_world(const Vector2 &pt);
36 void key_handler(SDLKey key, bool pressed);
37 void motion_handler(int x, int y);
38 void bn_handler(int x, int y, int bn, bool pressed);
41 int xsz = 800, ysz = 600;
45 bool view_valid = false;
47 int main(int argc, char **argv) {
48 const char *usage = "curve_draw [-s(--size) WxH] [curve(s) to load]\n";
52 for(int i=1; i<argc; i++) {
53 if(argv[i][0] == '-') {
54 if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--size")) {
55 if(!isdigit(argv[++i][0])) {
56 cerr << "invalid size argument: " << argv[i] << endl;
61 char *ptr = strchr(argv[i], 'x');
62 if(!ptr || !isdigit(*(ptr + 1))) {
63 cerr << "invalid size argument: " << argv[i] << endl;
68 } else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
76 Curve *curve = load_curve(argv[i]);
78 fprintf(stderr, "failed to load curve: %s\n", argv[i]);
81 state.curves.push_back(curve);
86 SDL_Init(SDL_INIT_VIDEO);
87 if(!(sdl_fb = SDL_SetVideoMode(xsz, ysz, 32, SDL_SWSURFACE))) {
88 cerr << "failed to set initialize graphics\n";
92 SDL_WM_SetCaption("curvedraw - press h for usage help", 0);
96 tool_cursors[TOOL_SELECT] = cursor_std;
97 tool_cursors[TOOL_INSERT] = cursor_cross;
98 tool_cursors[TOOL_REMOVE] = cursor_x;
102 while(!done && SDL_WaitEvent(&event)) {
111 key_handler(event.key.keysym.sym, event.type == SDL_KEYDOWN);
114 case SDL_MOUSEMOTION:
115 motion_handler(event.motion.x, event.motion.y);
118 case SDL_MOUSEBUTTONDOWN:
119 case SDL_MOUSEBUTTONUP:
120 bn_handler(event.button.x, event.button.y, event.button.button, event.button.state == SDL_PRESSED);
126 } while(SDL_PollEvent(&event));
128 if(!view_valid) redraw();
136 void reset_state(EditState *s) {
139 s->view_size.y = 100;
140 s->view_size.x = s->view_size.y * ((float)xsz / (float)ysz);
142 s->tool = TOOL_SELECT;
145 s->sel_x = s->sel_y = -1;
147 s->draw_wire = false;
150 for(size_t i=0; i<s->curves.size(); i++) {
158 SDL_FillRect(sdl_fb, 0, 0);
160 if(SDL_MUSTLOCK(sdl_fb)) {
161 SDL_LockSurface(sdl_fb);
163 unsigned int *fb = (unsigned int*)sdl_fb->pixels;
165 memset(fb + xsz * ysz / 2, 0x18, xsz * 4);
167 unsigned int *ptr = fb + xsz / 2;
168 for(int i=0; i<ysz; i++) {
174 for(size_t i=0; i<state.curves.size(); i++) {
177 // draw a line between control points
178 if(state.draw_wire) {
179 for(int j=0; j<state.curves[i]->get_point_count(); j++) {
180 Vector3 *ptptr = state.curves[i]->get_control_point(j);
181 Vector2 pt = world_to_dev(*ptptr);
184 draw_line((int)prev.x, (int)prev.y, (int)pt.x, (int)pt.y, 0x404040, fb, xsz);
191 // draw the spline as a polyline
192 const int draw_seg = 12 * state.curves[i]->get_segment_count();
193 for(int j=0; j<=draw_seg; j++) {
194 float t = (float)j / (float)draw_seg;
195 Vector2 pt = (*state.curves[i])(t);
197 pt = world_to_dev(pt);
200 unsigned int col = state.sel_curve == (int)i ? 0x00ff00 : 0x0000ff;
201 draw_line((int)prev.x, (int)prev.y, (int)pt.x, (int)pt.y, col, fb, xsz);
207 // draw the control points
209 for(int j=0; j<state.curves[i]->get_point_count(); j++) {
210 Vector3 *ptptr = state.curves[i]->get_control_point(j);
211 Vector2 pt = world_to_dev(*ptptr);
213 unsigned int col = 0x808080;
214 if(state.sel_curve == (int)i) {
215 col = state.sel_pt == j ? 0xffff00 : 0xff0000;
218 draw_point((int)pt.x, (int)pt.y, col, fb, xsz);
223 if(SDL_MUSTLOCK(sdl_fb)) {
224 SDL_UnlockSurface(sdl_fb);
231 Vector2 world_to_dev(const Vector2 &pt) {
232 Vector2 norm = (Vector2(1, -1) * pt + state.view_size / 2.0) / state.view_size;
233 return norm * Vector2(xsz, ysz);
236 Vector2 dev_to_world(const Vector2 &pt) {
237 Vector2 norm = pt / Vector2(xsz, ysz);
238 return Vector2(1, -1) * ((norm * state.view_size) - state.view_size / 2.0);
242 void key_handler(SDLKey key, bool pressed) {
249 state.sel_curve = -1;
260 state.draw_wire = !state.draw_wire;
267 state.draw_pt = !state.draw_pt;
274 if(state.sel_curve <= 0) {
275 state.sel_curve = state.curves.size() - 1;
287 if(state.sel_curve == -1 || state.sel_curve >= (int)state.curves.size() - 1) {
298 if(!pressed && state.sel_curve != -1) {
299 Curve *current = state.curves[state.sel_curve];
300 Curve *curve = dynamic_cast<BSplineCurve*>(current) ? (Curve*)new CatmullRomSplineCurve : (Curve*)new BSplineCurve;
302 for(int i=0; i<current->get_point_count(); i++) {
303 Vector3 pt = *current->get_control_point(i);
305 curve->add_control_point(pt);
309 state.curves[state.sel_curve] = curve;
316 cout << "----- controls -----\n";
317 cout << "[keyboard]\n";
318 cout << "space - cycle through curves\n";
319 cout << "right - cycle through curves forward\n";
320 cout << "left - cycle through curves backwards\n";
321 cout << "esc - deselect selected curve\n";
322 cout << "enter - toggle curve type (bspline/catmull-rom)\n";
323 cout << "q - quit\n";
324 cout << "s - save all curves to current directory\n";
326 cout << "left-button - perform action (select tool also drags points around)\n";
327 cout << "right-button - cycle through tools (select/insert/remove)\n";
328 cout << "mousewheel - zoom in/out\n";
336 for(size_t i=0; i<state.curves.size(); i++) {
337 sprintf(fname, "curve%02d", (int)i);
338 save_curve(fname, state.curves[i]);
340 cout << state.curves.size() << " curves saved.\n";
349 void motion_handler(int x, int y) {
350 Vector2 mouse = dev_to_world(Vector2(x, y));
352 if(state.sel_x != -1) {
353 if(x != state.sel_x || y != state.sel_y) {
354 Curve *curve = state.curves[state.sel_curve];
355 Vector3 *ptptr = curve->get_control_point(state.sel_pt);
365 void bn_handler(int x, int y, int bn, bool pressed) {
366 Vector2 mouse = dev_to_world(Vector2(x, y));
368 if(bn == 3 && pressed) {
369 state.tool = (state.tool + 1) % TOOL_COUNT;
370 SDL_SetCursor(tool_cursors[state.tool]);
373 if(bn == 1 && state.tool == TOOL_SELECT) {
376 if(state.sel_curve >= 0) {
377 Curve *curve = state.curves[state.sel_curve];
378 for(int i=0; i<curve->get_point_count(); i++) {
379 Vector3 *ptptr = curve->get_control_point(i);
382 if((pt - mouse).length() < state.view_size.y * 0.008) {
391 } else { // depressed
397 if(bn == 1 && state.tool == TOOL_REMOVE) {
398 static int press_x, press_y;
404 if(press_x == x && press_y == y && state.sel_curve >= 0) {
405 Curve *curve = state.curves[state.sel_curve];
406 for(int i=0; i<curve->get_point_count(); i++) {
407 Vector3 *ptptr = curve->get_control_point(i);
410 if((pt - mouse).length() < state.view_size.y * 0.008) {
411 curve->remove_control_point(i);
412 if(state.sel_pt >= i) state.sel_pt--;
413 if(!curve->get_point_count()) {
415 state.curves.erase(state.curves.begin() + state.sel_curve);
425 if(bn == 1 && state.tool == TOOL_INSERT) {
426 static int press_x, press_y;
432 if(press_x == x && press_y == y) {
433 if(state.sel_curve == -1) {
434 // create a new curve...
435 Curve *curve = new CatmullRomSplineCurve;
436 curve->add_control_point(Vector3(mouse.x, mouse.y, 0.0));
438 state.sel_curve = state.curves.size();
440 state.curves.push_back(curve);
442 // add a control point to the selected curve
443 Curve *curve = state.curves[state.sel_curve];
444 state.sel_pt = curve->get_point_count();
445 curve->add_control_point(Vector3(mouse.x, mouse.y, 0.0));
452 if(bn == 4 && pressed) {
453 state.view_size *= 0.95;
457 if(bn == 5 && pressed) {
458 state.view_size *= 1.05;