# HG changeset patch # User John Tsiombikas # Date 1450595164 -7200 # Node ID 37ab3a4c02f8105b16c7df9be30e7237c4b45c25 # Parent b625f0575d66cd2e8b69a0acdfe9ae26bc186719# Parent 4da693339d99d606b08332506f262d6df8ee3acf merged diff -r b625f0575d66 -r 37ab3a4c02f8 Makefile --- a/Makefile Thu Dec 17 16:41:42 2015 +0200 +++ b/Makefile Sun Dec 20 09:06:04 2015 +0200 @@ -5,17 +5,17 @@ dep = $(obj:.o=.d) bin = curvedraw -CXXFLAGS = -pedantic -Wall -g +CXXFLAGS = -std=c++11 -pedantic -Wall -g LDFLAGS = $(libgl) -lvmath -ldrawtext -lm sys := $(shell uname -s | sed 's/MINGW.*/win32/') ifeq ($(sys), Darwin) - libgl = -framework OpenGL -framework GLUT + libgl = -framework OpenGL -framework GLUT -lGLEW else ifeq ($(sys), win32) - libgl = -lopengl32 -lglut32 + libgl = -lopengl32 -lglut32 -lglew32 else - libgl = -lGL -lglut + libgl = -lGL -lglut -lGLEW endif $(bin): $(obj) diff -r b625f0575d66 -r 37ab3a4c02f8 curvedraw.sln --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/curvedraw.sln Sun Dec 20 09:06:04 2015 +0200 @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "curvedraw", "curvedraw.vcxproj", "{04CA7CB1-80DB-4E89-8FD3-54715CDA43CE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {04CA7CB1-80DB-4E89-8FD3-54715CDA43CE}.Debug|Win32.ActiveCfg = Debug|Win32 + {04CA7CB1-80DB-4E89-8FD3-54715CDA43CE}.Debug|Win32.Build.0 = Debug|Win32 + {04CA7CB1-80DB-4E89-8FD3-54715CDA43CE}.Release|Win32.ActiveCfg = Release|Win32 + {04CA7CB1-80DB-4E89-8FD3-54715CDA43CE}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff -r b625f0575d66 -r 37ab3a4c02f8 curvedraw.vcxproj --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/curvedraw.vcxproj Sun Dec 20 09:06:04 2015 +0200 @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {04CA7CB1-80DB-4E89-8FD3-54715CDA43CE} + Win32Proj + curvedraw + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + false + MultiByte + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;FREEGLUT_LIB_PRAGMAS=0;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + 4244;4996;4305 + + + Console + true + freeglut.lib;libdrawtext.lib;opengl32.lib;glu32.lib;glew32.lib;libvmath-dbg.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;FREEGLUT_LIB_PRAGMAS=0;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + 4244;4996;4305 + + + Console + true + true + true + freeglut.lib;libdrawtext.lib;opengl32.lib;glu32.lib;glew32.lib;libvmath.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -r b625f0575d66 -r 37ab3a4c02f8 curvedraw.vcxproj.filters --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/curvedraw.vcxproj.filters Sun Dec 20 09:06:04 2015 +0200 @@ -0,0 +1,43 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;h + + + + + src + + + src + + + src + + + src + + + src + + + + + src + + + src + + + src + + + src + + + src + + + \ No newline at end of file diff -r b625f0575d66 -r 37ab3a4c02f8 src/app.cc --- a/src/app.cc Thu Dec 17 16:41:42 2015 +0200 +++ b/src/app.cc Sun Dec 20 09:06:04 2015 +0200 @@ -7,6 +7,7 @@ #include "app.h" #include "curve.h" #include "widgets.h" +#include "curvefile.h" enum SnapMode { SNAP_NONE, @@ -29,21 +30,24 @@ static float grid_size = 1.0; static SnapMode snap_mode; +static bool show_bounds; + static std::vector curves; static Curve *sel_curve; // selected curve being edited static Curve *new_curve; // new curve being entered static Curve *hover_curve; // curve the mouse is hovering over (click to select) -static int sel_pidx = -1; // selected point of the selected or hovered-over curve +static int sel_pidx = -1; // selected point of the selected curve +static int hover_pidx = -1; // hovered over point static Label *weight_label; // floating label for the cp weight -#ifdef DRAW_MOUSE_POINTER static Vector2 mouse_pointer; -#endif bool app_init(int argc, char **argv) { + glewInit(); + glEnable(GL_MULTISAMPLE); glEnable(GL_CULL_FACE); @@ -148,6 +152,20 @@ int numpt = curve->size(); int segm = numpt * 16; + if(show_bounds) { + Vector3 bmin, bmax; + curve->get_bbox(&bmin, &bmax); + + glLineWidth(1.0); + glColor3f(0, 1, 0); + glBegin(GL_LINE_LOOP); + glVertex2f(bmin.x, bmin.y); + glVertex2f(bmax.x, bmin.y); + glVertex2f(bmax.x, bmax.y); + glVertex2f(bmin.x, bmax.y); + glEnd(); + } + glLineWidth(curve == hover_curve ? 4.0 : 2.0); if(curve == sel_curve) { glColor3f(0.3, 0.4, 1.0); @@ -159,7 +177,7 @@ glBegin(GL_LINE_STRIP); for(int i=0; iinterpolate(t); + Vector3 v = curve->interpolate(t); glVertex2f(v.x, v.y); } glEnd(); @@ -180,10 +198,23 @@ glColor3f(0.2, 1.0, 0.2); } } - Vector2 pt = curve->get_point(i); + Vector2 pt = curve->get_point2(i); glVertex2f(pt.x, pt.y); } glEnd(); + + // draw the projected mouse point on the selected curve + /* + if(curve == sel_curve) { + Vector3 pp = curve->proj_point(Vector3(mouse_pointer.x, mouse_pointer.y, 0.0)); + + glPointSize(5.0); + glBegin(GL_POINTS); + glColor3f(1, 0.8, 0.2); + glVertex2f(pp.x, pp.y); + glEnd(); + } + */ glPointSize(1.0); } @@ -215,40 +246,64 @@ } break; - case 'l': - case 'L': + case '1': + case '2': + case '3': if(sel_curve) { - sel_curve->set_type(CURVE_LINEAR); + sel_curve->set_type((CurveType)((int)CURVE_LINEAR + key - '1')); post_redisplay(); } if(new_curve) { - new_curve->set_type(CURVE_LINEAR); + new_curve->set_type((CurveType)((int)CURVE_LINEAR + key - '1')); post_redisplay(); } break; case 'b': case 'B': + show_bounds = !show_bounds; + post_redisplay(); + break; + + case 'n': + case 'N': if(sel_curve) { - sel_curve->set_type(CURVE_BSPLINE); - post_redisplay(); - } - if(new_curve) { - new_curve->set_type(CURVE_BSPLINE); + sel_curve->normalize(); post_redisplay(); } break; - case 'h': - case 'H': - if(sel_curve) { - sel_curve->set_type(CURVE_HERMITE); - post_redisplay(); + case 'e': + case 'E': + // TODO: GUI for filename at least + if(!save_curves("test.curves", &curves[0], (int)curves.size())) { + fprintf(stderr, "failed to export curves\n"); } - if(new_curve) { - new_curve->set_type(CURVE_HERMITE); - post_redisplay(); + printf("exported %d curves\n", (int)curves.size()); + break; + + case 'l': + case 'L': + { + std::list clist = load_curves("test.curves"); + if(clist.empty()) { + fprintf(stderr, "failed to import curves\n"); + } + + for(size_t i=0; i::iterator it = clist.begin(); + while(it != clist.end()) { + curves.push_back(*it++); + ++num; + } + printf("imported %d curves\n", num); } + post_redisplay(); break; } } @@ -335,7 +390,7 @@ int pidx = curves[i]->nearest_point(pos); if(pidx == -1) continue; - Vector2 cp = curves[i]->get_point(pidx); + Vector2 cp = curves[i]->get_point2(pidx); if((cp - pos).length_sq() < thres * thres) { *curveret = curves[i]; *pidxret = pidx; @@ -347,6 +402,28 @@ return false; } +static bool hit_test(const Vector2 &pos, Curve **curveret, int *pidxret) +{ + float thres = 0.02 / view_scale; + + if(point_hit_test(pos, curveret, pidxret)) { + return true; + } + + Vector3 pos3 = Vector3(pos.x, pos.y, 0.0f); + for(size_t i=0; idistance_sq(pos3)) < thres * thres) { + *curveret = curves[i]; + *pidxret = -1; + return true; + } + } + *curveret = 0; + *pidxret = -1; + return false; +} + static Vector2 snap(const Vector2 &p) { switch(snap_mode) { @@ -407,10 +484,8 @@ if(!dx && !dy) return; Vector2 uv = pixel_to_uv(x, y); -#ifdef DRAW_MOUSE_POINTER mouse_pointer = uv; - post_redisplay(); -#endif + //post_redisplay(); /* when entering a new curve, have the last (extra) point following * the mouse until it's entered by a click (see on_click). @@ -422,7 +497,10 @@ if(!new_curve && !bnstate) { // not dragging, highlight curve under mouse - point_hit_test(uv, &hover_curve, &sel_pidx); + hit_test(uv, &hover_curve, &hover_pidx); + if(hover_curve == sel_curve) { + sel_pidx = hover_pidx; + } post_redisplay(); } else { @@ -482,6 +560,7 @@ if(hover_curve) { // if we're hovering: click selects sel_curve = hover_curve; + sel_pidx = hover_pidx; hover_curve = 0; } else if(sel_curve) { // if we have a selected curve: click adds point (enter new_curve mode) @@ -520,9 +599,11 @@ // in selected curve mode: delete control point or unselect Curve *hit_curve; int hit_pidx; - if(point_hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) { - hit_curve->remove_point(hit_pidx); - sel_pidx = -1; + if(hit_test(uv, &hit_curve, &hit_pidx) && hit_curve == sel_curve) { + if(hit_pidx != -1) { + hit_curve->remove_point(hit_pidx); + sel_pidx = -1; + } } else { sel_curve = 0; sel_pidx = -1; diff -r b625f0575d66 -r 37ab3a4c02f8 src/curve.cc --- a/src/curve.cc Thu Dec 17 16:41:42 2015 +0200 +++ b/src/curve.cc Sun Dec 20 09:06:04 2015 +0200 @@ -1,9 +1,39 @@ #include +#include +#include #include "curve.h" Curve::Curve(CurveType type) { this->type = type; + bbvalid = true; +} + +Curve::Curve(const Vector4 *cp, int numcp, CurveType type) + : Curve(type) +{ + this->cp.resize(numcp); + for(int i=0; icp[i] = cp[i]; + } +} + +Curve::Curve(const Vector3 *cp, int numcp, CurveType type) + : Curve(type) +{ + this->cp.resize(numcp); + for(int i=0; icp[i] = Vector4(cp[i].x, cp[i].y, cp[i].z, 1.0f); + } +} + +Curve::Curve(const Vector2 *cp, int numcp, CurveType type) + : Curve(type) +{ + this->cp.resize(numcp); + for(int i=0; icp[i] = Vector4(cp[i].x, cp[i].y, 0.0f, 1.0f); + } } void Curve::set_type(CurveType type) @@ -16,9 +46,20 @@ return type; } +void Curve::add_point(const Vector4 &p) +{ + cp.push_back(p); + inval_bounds(); +} + +void Curve::add_point(const Vector3 &p, float weight) +{ + add_point(Vector4(p.x, p.y, p.z, weight)); +} + void Curve::add_point(const Vector2 &p, float weight) { - cp.push_back(Vector3(p.x, p.y, weight)); + add_point(Vector4(p.x, p.y, 0.0f, weight)); } bool Curve::remove_point(int idx) @@ -27,23 +68,14 @@ return false; } cp.erase(cp.begin() + idx); + inval_bounds(); return true; } -int Curve::nearest_point(const Vector2 &p) +void Curve::clear() { - int res = -1; - float bestsq = FLT_MAX; - - for(size_t i=0; i= (int)cp.size()) { + return false; + } + cp[idx] = Vector4(p.x, p.y, p.z, weight); + inval_bounds(); + return true; } bool Curve::set_point(int idx, const Vector2 &p, float weight) @@ -86,7 +134,8 @@ if(idx < 0 || idx >= (int)cp.size()) { return false; } - cp[idx] = Vector3(p.x, p.y, weight); + cp[idx] = Vector4(p.x, p.y, 0.0, weight); + inval_bounds(); return true; } @@ -95,7 +144,17 @@ if(idx < 0 || idx >= (int)cp.size()) { return false; } - cp[idx].z = weight; + cp[idx].w = weight; + return true; +} + +bool Curve::move_point(int idx, const Vector3 &p) +{ + if(idx < 0 || idx >= (int)cp.size()) { + return false; + } + cp[idx] = Vector4(p.x, p.y, p.z, cp[idx].w); + inval_bounds(); return true; } @@ -104,22 +163,211 @@ if(idx < 0 || idx >= (int)cp.size()) { return false; } - cp[idx] = Vector3(p.x, p.y, cp[idx].z); + cp[idx] = Vector4(p.x, p.y, 0.0f, cp[idx].w); + inval_bounds(); return true; } -Vector2 Curve::interpolate(float t, CurveType type) const + +int Curve::nearest_point(const Vector3 &p) const { - if(cp.empty()) { - return Vector2(0, 0); + int res = -1; + float bestsq = FLT_MAX; + + for(size_t i=0; ibbmin; + *bbmax = this->bbmax; +} + +void Curve::calc_bbox(Vector3 *bbmin, Vector3 *bbmax) const +{ + if(empty()) { + *bbmin = *bbmax = Vector3(0, 0, 0); + return; + } + + Vector3 bmin = cp[0]; + Vector3 bmax = bmin; + for(size_t i=1; i bmax[j]) bmax[j] = v[j]; + } + } + *bbmin = bmin; + *bbmax = bmax; +} + +void Curve::normalize() +{ + if(!bbvalid) { + calc_bounds(); + } + + Vector3 bsize = bbmax - bbmin; + Vector3 boffs = (bbmin + bbmax) * 0.5; + + Vector3 bscale; + bscale.x = bsize.x == 0.0f ? 1.0f : 1.0f / bsize.x; + bscale.y = bsize.y == 0.0f ? 1.0f : 1.0f / bsize.y; + bscale.z = bsize.z == 0.0f ? 1.0f : 1.0f / bsize.z; + + for(size_t i=0; i 1.0) t = 1.0; + + Vector4 res; + if(type == CURVE_LINEAR || num_cp == 2) { + res = lerp(cp[a], cp[b], t); + } else { + int prev = a <= 0 ? a : a - 1; + int next = b >= num_cp - 1 ? b : b + 1; + + if(type == CURVE_HERMITE) { + res = catmull_rom_spline(cp[prev], cp[a], cp[b], cp[next], t); + } else { + res = bspline(cp[prev], cp[a], cp[b], cp[next], t); + if(res.w != 0.0f) { + res.x /= res.w; + res.y /= res.w; + res.z /= res.w; + } + } + } + + return Vector3(res.x, res.y, res.z); +} + +Vector3 Curve::interpolate(float t) const +{ + if(empty()) { + return Vector3(0, 0, 0); } int num_cp = (int)cp.size(); if(num_cp == 1) { - return Vector2(cp[0].x, cp[0].y); + return Vector3(cp[0].x, cp[0].y, cp[0].z); } - Vector3 res; + if(t < 0.0) t = 0.0; + if(t > 1.0) t = 1.0; + int idx0 = std::min((int)floor(t * (num_cp - 1)), num_cp - 2); int idx1 = idx0 + 1; @@ -128,35 +376,17 @@ float t1 = (float)idx1 * dt; t = (t - t0) / (t1 - t0); - if(t < 0.0) t = 0.0; - if(t > 1.0) t = 1.0; - if(type == CURVE_LINEAR || num_cp <= 2) { - res = lerp(cp[idx0], cp[idx1], t); - } else { - int idx_prev = idx0 <= 0 ? idx0 : idx0 - 1; - int idx_next = idx1 >= num_cp - 1 ? idx1 : idx1 + 1; + return interpolate_segment(idx0, idx1, t); +} - if(type == CURVE_HERMITE) { - res = catmull_rom_spline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t); - } else { - res = bspline(cp[idx_prev], cp[idx0], cp[idx1], cp[idx_next], t); - if(res.z != 0.0f) { - res.x /= res.z; - res.y /= res.z; - } - } - } - +Vector2 Curve::interpolate2(float t) const +{ + Vector3 res = interpolate(t); return Vector2(res.x, res.y); } -Vector2 Curve::interpolate(float t) const -{ - return interpolate(t, type); -} - -Vector2 Curve::operator ()(float t) const +Vector3 Curve::operator ()(float t) const { return interpolate(t); } diff -r b625f0575d66 -r 37ab3a4c02f8 src/curve.h --- a/src/curve.h Thu Dec 17 16:41:42 2015 +0200 +++ b/src/curve.h Sun Dec 20 09:06:04 2015 +0200 @@ -12,37 +12,74 @@ class Curve { private: - std::vector cp; + std::vector cp; CurveType type; + // bounding box + mutable Vector3 bbmin, bbmax; + mutable bool bbvalid; + + void calc_bounds() const; + void inval_bounds() const; + public: Curve(CurveType type = CURVE_HERMITE); + Curve(const Vector4 *cp, int numcp, CurveType type = CURVE_HERMITE); // homogenous + Curve(const Vector3 *cp, int numcp, CurveType type = CURVE_HERMITE); // 3D points, w=1 + Curve(const Vector2 *cp, int numcp, CurveType type = CURVE_HERMITE); // 2D points, z=0, w=1 void set_type(CurveType type); CurveType get_type() const; + void add_point(const Vector4 &p); + void add_point(const Vector3 &p, float weight = 1.0f); void add_point(const Vector2 &p, float weight = 1.0f); bool remove_point(int idx); - int nearest_point(const Vector2 &p); + void clear(); // remove all control points + bool empty() const; // true if 0 control points + int size() const; // returns number of control points + // access operators for control points + Vector4 &operator [](int idx); + const Vector4 &operator [](int idx) const; + const Vector4 &get_point(int idx) const; - bool empty() const; - int size() const; - Vector3 &operator [](int idx); - const Vector3 &operator [](int idx) const; - - const Vector3 &get_homo_point(int idx) const; // homogeneous point - Vector2 get_point(int idx) const; + Vector3 get_point3(int idx) const; + Vector2 get_point2(int idx) const; float get_weight(int idx) const; + bool set_point(int idx, const Vector3 &p, float weight = 1.0f); bool set_point(int idx, const Vector2 &p, float weight = 1.0f); bool set_weight(int idx, float weight); // move point without changing its weight + bool move_point(int idx, const Vector3 &p); bool move_point(int idx, const Vector2 &p); + + int nearest_point(const Vector3 &p) const; + // nearest control point on the 2D plane z=0 + int nearest_point(const Vector2 &p) const; - Vector2 interpolate(float t, CurveType type) const; - Vector2 interpolate(float t) const; - Vector2 operator ()(float t) const; + /* get_bbox returns the axis-aligned bounding box of the curve's + * control points. + * NOTE: hermite curves can go outside of the bounding box of their control points + * NOTE: lazy calculation of bounds is performed, use calc_bbox in multithreaded programs + */ + void get_bbox(Vector3 *bbmin, Vector3 *bbmax) const; + void calc_bbox(Vector3 *bbmin, Vector3 *bbmax) const; + // normalize the curve's bounds to coincide with the unit cube + void normalize(); + + // project a point to the curve (nearest point on the curve) + Vector3 proj_point(const Vector3 &p, float refine_thres = 0.01) const; + // equivalent to (proj_point(p) - p).length() + float distance(const Vector3 &p) const; + // equivalent to fabs((proj_point(p) - p).length_sq()) + float distance_sq(const Vector3 &p) const; + + Vector3 interpolate_segment(int a, int b, float t) const; + Vector3 interpolate(float t) const; + Vector2 interpolate2(float t) const; + Vector3 operator ()(float t) const; }; #endif // CURVE_H_ diff -r b625f0575d66 -r 37ab3a4c02f8 src/curvefile.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/curvefile.cc Sun Dec 20 09:06:04 2015 +0200 @@ -0,0 +1,204 @@ +#include +#include +#include +#include "curvefile.h" + +static bool save_curve(FILE *fp, const Curve *curve); + +bool save_curves(const char *fname, const Curve * const *curves, int count) +{ + FILE *fp = fopen(fname, "wb"); + if(!fp) return false; + + bool res = save_curves(fp, curves, count); + fclose(fp); + return res; +} + +bool save_curves(FILE *fp, const Curve * const *curves, int count) +{ + fprintf(fp, "GCURVES\n"); + + for(int i=0; iget_type())); + fprintf(fp, " cpcount %d\n", curve->size()); + for(int i=0; isize(); i++) { + Vector4 cp = curve->get_point(i); + fprintf(fp, " cp %g %g %g %g\n", cp.x, cp.y, cp.z, cp.w); + } + fprintf(fp, "}\n"); + return true; +} + +std::list load_curves(const char *fname) +{ + std::list res; + FILE *fp = fopen(fname, "r"); + if(!fp) return res; + + res = load_curves(fp); + fclose(fp); + return res; +} + +static std::string next_token(FILE *fp) +{ + std::string s; + int c; + while((c = fgetc(fp)) != -1) { + if(!isspace(c)) { + ungetc(c, fp); + break; + } + } + + if(feof(fp)) return s; + while((c = fgetc(fp)) != -1) { + if(isspace(c)) { + ungetc(c, fp); + break; + } + s.push_back(c); + } + return s; +} + +static bool expect_str(FILE *fp, const char *s) +{ + std::string tok = next_token(fp); + if(tok != std::string(s)) { + if(tok.empty() && feof(fp)) { + fprintf(stderr, "expected: %s\n", s); + } + return false; + } + return true; +} + +static bool expect_float(FILE *fp, float *ret) +{ + std::string tok = next_token(fp); + const char *cs = tok.c_str(); + char *endp; + *ret = strtod(cs, &endp); + if(endp != cs + tok.length()) { + if(!(tok.empty() && feof(fp))) { + fprintf(stderr, "number expected\n"); + } + return false; + } + return true; +} + +static bool expect_int(FILE *fp, int *ret) +{ + std::string tok = next_token(fp); + const char *cs = tok.c_str(); + char *endp; + *ret = strtol(cs, &endp, 0); + if(endp != cs + tok.length()) { + if(!(tok.empty() && feof(fp))) { + fprintf(stderr, "integer expected\n"); + } + return false; + } + return true; +} + +static Curve *curve_block(FILE *fp) +{ + if(!expect_str(fp, "curve") || !expect_str(fp, "{")) { + return 0; + } + + Curve *curve = new Curve; + int cpcount = -1; + std::string tok; + while(!(tok = next_token(fp)).empty() && tok != "}") { + if(tok == "cpcount") { + if(cpcount != -1 || !expect_int(fp, &cpcount) || cpcount <= 0) { + goto err; + } + } else if(tok == "type") { + tok = next_token(fp); + if(tok == "polyline") { + curve->set_type(CURVE_LINEAR); + } else if(tok == "hermite") { + curve->set_type(CURVE_HERMITE); + } else if(tok == "bspline") { + curve->set_type(CURVE_BSPLINE); + } else { + goto err; + } + } else { + if(tok != "cp") { + goto err; + } + Vector4 cp; + for(int i=0; i<4; i++) { + if(!expect_float(fp, &cp[i])) { + goto err; + } + } + curve->add_point(Vector3(cp.x, cp.y, cp.z), cp.w); + } + } + + if(curve->size() != cpcount) { + fprintf(stderr, "warning: curve cpcount was %d, but read %d control points\n", cpcount, curve->size()); + } + + return curve; +err: + fprintf(stderr, "failed to parse curve block\n"); + delete curve; + return 0; +} + +std::list load_curves(FILE *fp) +{ + std::list curves; + if(!expect_str(fp, "GCURVES")) { + fprintf(stderr, "load_curves: failed to load, invalid file format\n"); + return curves; + } + + Curve *curve; + while((curve = curve_block(fp))) { + curves.push_back(curve); + } + + if(!feof(fp)) { + std::list::iterator it = curves.begin(); + while(it != curves.end()) { + delete *it++; + } + return curves; + } + + return curves; +} + diff -r b625f0575d66 -r 37ab3a4c02f8 src/curvefile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/curvefile.h Sun Dec 20 09:06:04 2015 +0200 @@ -0,0 +1,14 @@ +#ifndef CURVEFILE_H_ +#define CURVEFILE_H_ + +#include +#include +#include "curve.h" + +bool save_curves(const char *fname, const Curve * const *curves, int count); +bool save_curves(FILE *fp, const Curve * const *curves, int count); + +std::list load_curves(const char *fname); +std::list load_curves(FILE *fp); + +#endif // CURVEFILE_H_ \ No newline at end of file diff -r b625f0575d66 -r 37ab3a4c02f8 src/main.cc --- a/src/main.cc Thu Dec 17 16:41:42 2015 +0200 +++ b/src/main.cc Sun Dec 20 09:06:04 2015 +0200 @@ -47,11 +47,6 @@ glutSwapBuffers(); } -static void reshape(int x, int y) -{ - app_reshape(x, y); -} - static void keydown(unsigned char key, int x, int y) { app_keyboard(key, true); diff -r b625f0575d66 -r 37ab3a4c02f8 src/opengl.h --- a/src/opengl.h Thu Dec 17 16:41:42 2015 +0200 +++ b/src/opengl.h Sun Dec 20 09:06:04 2015 +0200 @@ -1,14 +1,6 @@ #ifndef OPENGL_H_ #define OPENGL_H_ -#ifdef WIN32 -#include -#endif - -#ifdef __APPLE__ -#include -#else -#include -#endif +#include #endif // OPENGL_H_ diff -r b625f0575d66 -r 37ab3a4c02f8 src/widgets.cc --- a/src/widgets.cc Thu Dec 17 16:41:42 2015 +0200 +++ b/src/widgets.cc Sun Dec 20 09:06:04 2015 +0200 @@ -2,6 +2,11 @@ #include #include #include +#ifdef _MSC_VER +#include +#else +#include +#endif #include #include "opengl.h" #include "widgets.h"