diff --git a/c++/contourgl/Makefile b/c++/contourgl/Makefile index dca43ca..afc16e4 100644 --- a/c++/contourgl/Makefile +++ b/c++/contourgl/Makefile @@ -1,8 +1,9 @@ -DEPS = gl x11 +DEPLIBS = gl x11 -CXXFLAGS = -O2 -g -Wall -fmessage-length=0 `pkg-config --libs $(DEPS)` -DGL_GLEXT_PROTOTYPES -OBJS = contourgl.o -LIBS = `pkg-config --libs $(DEPS)` +CXXFLAGS = -O3 -Wall -fmessage-length=0 `pkg-config --libs $(DEPLIBS)` -DGL_GLEXT_PROTOTYPES +DEPS = contour.h contourbuilder.h +OBJS = contour.o contourgl.o contourbuilder.o +LIBS = `pkg-config --libs $(DEPLIBS)` TARGET = contourgl $(TARGET): $(OBJS) diff --git a/c++/contourgl/contour.cpp b/c++/contourgl/contour.cpp new file mode 100644 index 0000000..93748a7 --- /dev/null +++ b/c++/contourgl/contour.cpp @@ -0,0 +1,306 @@ +/* + ......... 2015 Ivan Mahonin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include + +#include "contour.h" + + +using namespace std; + + +const Vector Contour::blank; + + +void Contour::clear() { + if (!chunks.empty()) { + chunks.clear(); + first = 0; + } +} + +void Contour::move_to(const Vector &v) { + if (chunks.empty()) { + if (!v.is_equal_to(blank)) + chunks.push_back(Chunk(MOVE, v)); + } else { + if (!v.is_equal_to(chunks.back().p1)) { + if (chunks.back().type == MOVE) + chunks.back().p1 = v; + else + if (chunks.back().type == CLOSE) + chunks.push_back(Chunk(MOVE, v)); + else { + chunks.push_back(Chunk(CLOSE, chunks[first].p1)); + chunks.push_back(Chunk(MOVE, v)); + } + } + } + first = chunks.size(); +} + +void Contour::line_to(const Vector &v) { + if (!v.is_equal_to(current())) + chunks.push_back(Chunk(LINE, v)); +} + +void Contour::conic_to(const Vector &v, const Vector &t) { + if (!v.is_equal_to(current())) + chunks.push_back(Chunk(CONIC, v, t)); +} + +void Contour::cubic_to(const Vector &v, const Vector &t0, const Vector &t1) { + if (!v.is_equal_to(current())) + chunks.push_back(Chunk(CUBIC, v, t0, t1)); +} + +void Contour::close() { + if (chunks.size() > first) { + if (first > 0) + chunks.push_back(Chunk(CLOSE, chunks[first-1].p1)); + else + chunks.push_back(Chunk(CLOSE, blank)); + first = chunks.size(); + } +} + +void Contour::assign(const Contour &other) { + chunks = other.chunks; + first = other.first; +} + +void Contour::line_split( + Rect &ref_line_bounds, + const Rect &bounds, + const Vector &min_size, + const Vector &p1 ) +{ + line_to(p1); + return; + + // TODO: fix bugs + + ref_line_bounds = ref_line_bounds.expand(p1); + + if (bounds.intersects(ref_line_bounds)) { + Vector s = ref_line_bounds.p1 - ref_line_bounds.p0; + if ( fabs(s.x) > min_size.x + || fabs(s.y) > min_size.y ) + { + line_to(p1); + ref_line_bounds.p0 = p1; + ref_line_bounds.p1 = p1; + return; + } + } + + if (!chunks.empty()) + chunks.back().p1 = p1; +} + +void Contour::conic_split( + Rect &ref_line_bounds, + const Rect &bounds, + const Vector &min_size, + const Vector &p1, + const Vector ¢er, + double radius, + double radians0, + double radians1, + int level ) +{ + assert(level > 0); + + const Vector &p0 = current(); + if ( fabs(p1.x - p0.x) > min_size.x + || fabs(p1.y - p0.y) > min_size.y ) + { + Rect b = conic_bounds(p0, p1, center, radius, radians0, radians1); + if (bounds.intersects(b)) { + double radians = 0.5*(radians0 + radians1); + Vector p( radius*cos(radians) + center.x, + radius*sin(radians) + center.y ); + conic_split(ref_line_bounds, bounds, min_size, p, center, radius, radians0, radians, level - 1); + conic_split(ref_line_bounds, bounds, min_size, p1, center, radius, radians, radians1, level - 1); + return; + } + } + line_split(ref_line_bounds, bounds, min_size, p1); +} + +void Contour::cubic_split( + Rect &ref_line_bounds, + const Rect &bounds, + const Vector &min_size, + const Vector &p1, + const Vector &bezier_pp0, + const Vector &bezier_pp1, + int level ) +{ + assert(level > 0); + + const Vector &p0 = current(); + if ( fabs(p1.x - p0.x) > min_size.x + || fabs(p1.y - p0.y) > min_size.y ) + { + Rect b = cubic_bounds(p0, p1, bezier_pp0, bezier_pp1); + if (bounds.intersects(b)) { + Vector pp = (bezier_pp0 + bezier_pp1)*0.5; + Vector pp00 = (p0 + bezier_pp0)*0.5; + Vector pp11 = (p1 + bezier_pp1)*0.5; + Vector pp01 = (pp00 + pp)*0.5; + Vector pp10 = (pp11 + pp)*0.5; + Vector p = (pp01 + pp10)*0.5; + + cubic_split(ref_line_bounds, bounds, min_size, p, pp00, pp01, level - 1); + cubic_split(ref_line_bounds, bounds, min_size, p1, pp10, pp11, level - 1); + return; + } + } + line_split(ref_line_bounds, bounds, min_size, p1); +} + +void Contour::split(Contour &c, const Rect &bounds, const Vector &min_size) const { + Rect line_bounds; + line_bounds.p0 = c.current(); + line_bounds.p1 = c.current(); + + for(ChunkList::const_iterator i = chunks.begin(); i != chunks.end(); ++i) { + switch(i->type) { + case MOVE: + c.move_to(i->p1); + line_bounds.p0 = c.current(); + line_bounds.p1 = c.current(); + break; + case LINE: + c.line_split(line_bounds, bounds, min_size, i->p1); + break; + case CLOSE: + c.close(); + break; + case CONIC: + { + const Vector &p0 = c.current(); + Vector center; + double radius = 0.0; + double radians0 = 0.0; + double radians1 = 0.0; + if (conic_convert(p0, i->p1, i->t0, center, radius, radians0, radians1)) + c.conic_split(line_bounds, bounds, min_size, i->p1, center, radius, radians0, radians1); + else + c.line_split(line_bounds, bounds, min_size, i->p1); + } + break; + case CUBIC: + { + const Vector &p0 = c.current(); + Vector pp0, pp1; + cubic_convert(p0, i->p1, i->t0, i->t1, pp0, pp1); + c.cubic_split(line_bounds, bounds, min_size, i->p1, pp0, pp1); + } + break; + } + } +} + +bool Contour::conic_convert( + const Vector &p0, + const Vector &p1, + const Vector &t, + Vector &out_center, + double &out_radius, + double &out_radians0, + double &out_radians1 ) +{ + double tl = sqrt(t.x*t.x + t.y*t.y); + if (fabs(tl) < 1e-6) { + out_center = Vector(); + out_radius = 0.0; + out_radians0 = 0.0; + out_radians1 = 0.0; + return false; + } + + Vector d = p1 - p0; + Vector n(-t.y/tl, t.x/tl); + + double r = 0.5*(d.x*d.x + d.y*d.y)/(d.x*n.x + d.y*n.y); + out_center = p0 + n*r; + out_radius = fabs(r); + out_radians0 = atan2(p0.y - out_center.y, p0.x - out_center.x); + out_radians1 = atan2(p1.y - out_center.y, p1.x - out_center.x); + bool ccw = r > 0.0; + + out_radians0 = wrap_angle(out_radians0, 2.0*M_PI); + out_radians1 = wrap_angle(out_radians1, 2.0*M_PI); + if (ccw) { if (out_radians1 < out_radians0) out_radians1 += 2.0*M_PI; } + else { if (out_radians1 > out_radians0) out_radians1 -= 2.0*M_PI; } + + return true; +} + +Rect Contour::conic_bounds( + const Vector &p0, + const Vector &p1, + const Vector ¢er, + double radius, + double radians0, + double radians1 ) +{ + radius = fabs(radius); + + Rect r; + r.p0 = p0; + r.p1 = p1; + + if (angle_between(radians0, radians1, 0.0*M_PI, 2.0*M_PI)) + r = r.expand(Vector(center.x + radius, center.y)); + if (angle_between(radians0, radians1, 0.5*M_PI, 2.0*M_PI)) + r = r.expand(Vector(center.x, center.y + radius)); + if (angle_between(radians0, radians1, 1.0*M_PI, 2.0*M_PI)) + r = r.expand(Vector(center.x - radius, center.y)); + if (angle_between(radians0, radians1, 1.5*M_PI, 2.0*M_PI)) + r = r.expand(Vector(center.x, center.y - radius)); + + return r; +} + +void Contour::cubic_convert( + const Vector &p0, + const Vector &p1, + const Vector &t0, + const Vector &t1, + Vector &out_bezier_pp0, + Vector &out_bezier_pp1 ) +{ + out_bezier_pp0 = t0/3.0 + p0; + out_bezier_pp1 = p1 - t1/3.0; +} + +Rect Contour::cubic_bounds( + const Vector &p0, + const Vector &p1, + const Vector &bezier_pp0, + const Vector &bezier_pp1 ) +{ + Rect r; + r.p0.x = min(min(p0.x, bezier_pp0.x), min(p1.x, bezier_pp1.x)); + r.p0.y = min(min(p0.y, bezier_pp0.y), min(p1.y, bezier_pp1.y)); + r.p1.x = max(max(p0.x, bezier_pp0.x), max(p1.x, bezier_pp1.x)); + r.p1.y = max(max(p0.y, bezier_pp0.y), max(p1.y, bezier_pp1.y)); + return r; +} diff --git a/c++/contourgl/contour.h b/c++/contourgl/contour.h new file mode 100644 index 0000000..643586e --- /dev/null +++ b/c++/contourgl/contour.h @@ -0,0 +1,204 @@ +/* + ......... 2015 Ivan Mahonin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _CONTOUR_H_ +#define _CONTOUR_H_ + +#include +#include + +#include +#include + +template +bool intersects(const T &a0, const T &a1, const T &b0, const T &b1) { + return !(std::max(b0, b1) < std::min(a0, a1)) + && !(std::max(a0, a1) < std::min(b0, b1)); +} + +inline double wrap_angle(double a, double round) { + double rounds = a/round + 0.5; + return (rounds - floor(rounds) - 0.5)*round; +} + +inline bool angle_between(double a0, double a1, double a, double round) { + if (a1 < a0) std::swap(a0, a1); + a0 = wrap_angle(a0, round); + a1 = wrap_angle(a1, round); + a = wrap_angle(a, round); + if (a < a0) a += round; + if (a1 < a0) a1 += round; + return a0 < a && a < a1; +} + +class Vector { +public: + union { + struct { double x, y; }; + struct { double coords[]; }; + }; + + Vector(): + x(), y() { } + Vector(double x, double y): + x(x), y(y) { } + + double& operator[] (int index) + { return coords[index]; } + const double& operator[] (int index) const + { return coords[index]; } + bool is_equal_to(const Vector &other) const + { return fabs(x - other.x) < 1e-6 && fabs(y - other.y) < 1e-6; } + + Vector operator+(const Vector &a) const + { return Vector(x + a.x, y + a.y); } + Vector operator-(const Vector &a) const + { return Vector(x - a.x, y - a.y); } + Vector operator*(double a) const + { return Vector(x*a, y*a); } + Vector operator/(double a) const + { return Vector(x/a, y/a); } + + static Vector zero() { return Vector(); } +}; + +class Rect { +public: + Vector p0, p1; + + bool intersects(const Rect &other) const + { return ::intersects(p0.x, p1.x, other.p0.x, other.p1.x) + && ::intersects(p0.y, p1.y, other.p0.y, other.p1.y); } + + Rect expand(const Vector &p) const { + Rect r; + r.p0.x = std::min(std::min(p0.x, p1.x), p.x); + r.p0.y = std::min(std::min(p0.y, p1.y), p.y); + r.p1.x = std::max(std::max(p0.x, p1.x), p.x); + r.p1.y = std::max(std::max(p0.y, p1.y), p.y); + return r; + } +}; + +inline bool intersects(const Rect &a, const Rect &b) + { return a.intersects(b); } + +class Contour +{ +public: + enum ChunkType { + CLOSE, + MOVE, + LINE, + CUBIC, + CONIC + }; + + struct Chunk { + ChunkType type; + Vector p1, t0, t1; + Chunk(): type() { } + Chunk(ChunkType type, const Vector &p1, const Vector &t0 = Vector(), const Vector &t1 = Vector()): + type(type), p1(p1), t0(t0), t1(t1) { } + }; + + typedef std::vector ChunkList; + +private: + static const Vector blank; + ChunkList chunks; + size_t first; + +public: + Contour(): first(0) { } + + void clear(); + void move_to(const Vector &v); + void line_to(const Vector &v); + void cubic_to(const Vector &v, const Vector &t0, const Vector &t1); + void conic_to(const Vector &v, const Vector &t); + void close(); + + void assign(const Contour &other); + + const ChunkList& get_chunks() const { return chunks; } + + const Vector& current() const + { return chunks.empty() ? blank : chunks.back().p1; } + + void split(Contour &c, const Rect &bounds, const Vector &min_size) const; + +private: + void line_split( + Rect &ref_line_bounds, + const Rect &bounds, + const Vector &min_size, + const Vector &p1 ); + + void conic_split( + Rect &ref_line_bounds, + const Rect &bounds, + const Vector &min_size, + const Vector &p1, + const Vector ¢er, + double radius, + double radians0, + double radians1, + int level = 64 ); + + void cubic_split( + Rect &ref_line_bounds, + const Rect &bounds, + const Vector &min_size, + const Vector &p1, + const Vector &bezier_pp0, + const Vector &bezier_pp1, + int level = 64 ); + + static bool conic_convert( + const Vector &p0, + const Vector &p1, + const Vector &t, + Vector &out_center, + double &out_radius, + double &out_radians0, + double &out_radians1 ); + + static Rect conic_bounds( + const Vector &p0, + const Vector &p1, + const Vector ¢er, + double radius, + double radians0, + double radians1 ); + + static void cubic_convert( + const Vector &p0, + const Vector &p1, + const Vector &t0, + const Vector &t1, + Vector &out_bezier_pp0, + Vector &out_bezier_pp1 ); + + static Rect cubic_bounds( + const Vector &p0, + const Vector &p1, + const Vector &bezier_pp0, + const Vector &bezier_pp1 ); +}; + +#endif diff --git a/c++/contourgl/contourbuilder.cpp b/c++/contourgl/contourbuilder.cpp new file mode 100644 index 0000000..92162f3 --- /dev/null +++ b/c++/contourgl/contourbuilder.cpp @@ -0,0 +1,124 @@ +/* + ......... 2015 Ivan Mahonin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "contourbuilder.h" + +using namespace std; + +void ContourBuilder::build_simple(vector &c) { + const float min_segment_length = 0.001f; + const float rounds = 10.f; + const float rounds2 = 1.f; + + vector back; + + float angle = 360.f; + float offset = 0.25f/(rounds + 1.f); + + // go front + while(true) { + float radius = angle/360.f/(rounds + 1.f); + float step = min_segment_length*180.f/M_PI/radius; + if (radius > 1.f - 2.f*offset) break; + + float fr = radius + offset; + float fx = fr*sinf(angle/180.f*M_PI); + float fy = fr*cosf(angle/180.f*M_PI); + + float br = radius - offset; + float bx = br*sinf(angle/180.f*M_PI); + float by = br*cosf(angle/180.f*M_PI); + + c.push_back(Vector(fx, fy)); + back.push_back(Vector(bx, by)); + + angle += step; + } + + float max_angle = angle; + + while(true) { + float radius = max_angle/360.f/(rounds + 1.f) + + (max_angle-angle)/360.f/rounds2; + float step = min_segment_length*180.f/M_PI/radius; + if (radius < 1.f/(rounds + 1.f)) + break; + + float fr = radius + offset; + float fx = fr*sinf(angle/180.f*M_PI); + float fy = fr*cosf(angle/180.f*M_PI); + + float br = radius - offset; + float bx = br*sinf(angle/180.f*M_PI); + float by = br*cosf(angle/180.f*M_PI); + + c.push_back(Vector(fx, fy)); + back.push_back(Vector(bx, by)); + + angle += step; + } + + + // go back + c.reserve(c.size() + back.size() + 1); + for(vector::reverse_iterator ri = back.rbegin(); ri != back.rend(); ++ri) + c.push_back(*ri); + + // close + c.push_back(c.front()); +} + +void ContourBuilder::build_car(Contour &c, const Vector &o, double s) { + c.move_to( Vector( 5, -1)*s + o); + c.line_to( Vector( 4, -1)*s + o); + c.conic_to( Vector( 2, -1)*s + o, Vector( 0, -1)*s); + c.line_to( Vector(-2, -1)*s + o); + c.conic_to( Vector(-4, -1)*s + o, Vector( 0, -1)*s); + c.line_to( Vector(-5, -1)*s + o); + c.line_to( Vector(-5, 1)*s + o); + c.line_to( Vector(-4, 1)*s + o); + c.cubic_to( Vector(-1, 3)*s + o, Vector( 0, 2)*s, Vector( 4, 0)*s); + c.cubic_to( Vector( 3, 1)*s + o, Vector( 4, 0)*s, Vector( 2, -2)*s); + c.line_to( Vector( 5, 1)*s + o); + c.close(); +} + +void ContourBuilder::build(Contour &c) { + double scale = 0.8/5.0; + + int count = 100; + double size = (double)(count + 2)/(double)(count); + double step = 2.0*size/(double)(count + 1); + double origin = step - size; + double s = 2*size*scale/(double)(count); + for(int i = 0; i < count; ++i) + for(int j = 0; j < count; ++j) + build_car(c, Vector(origin + i*step, origin + j*step), s); + + count = 100; + size = (double)(count + 2)/(double)(count); + step = 2.0*size/(double)(count + 1); + origin = step - size; + s = size*scale/(double)(count); + for(int i = 0; i < count; ++i) + for(int j = 0; j < count; ++j) + build_car(c, Vector(origin + i*step, origin + j*step), s); + + build_car(c, Vector::zero(), scale); + build_car(c, Vector::zero(), 0.5*scale); +} + diff --git a/c++/contourgl/contourbuilder.h b/c++/contourgl/contourbuilder.h new file mode 100644 index 0000000..17c4e67 --- /dev/null +++ b/c++/contourgl/contourbuilder.h @@ -0,0 +1,25 @@ +/* + ......... 2015 Ivan Mahonin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "contour.h" + +class ContourBuilder { +public: + static void build_simple(std::vector &c); + static void build_car(Contour &c, const Vector &o, double s); + static void build(Contour &c); +}; diff --git a/c++/contourgl/contourgl.cpp b/c++/contourgl/contourgl.cpp index d3aa000..2c7b10f 100644 --- a/c++/contourgl/contourgl.cpp +++ b/c++/contourgl/contourgl.cpp @@ -28,6 +28,7 @@ #include #include +#include "contourbuilder.h" #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 @@ -37,9 +38,6 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display*, GLXFBConfig, GLXC using namespace std; -typedef pair vec2; -typedef vector contour; - void save_rgba(const void *buffer, int width, int height, const string &filename) { // create file ofstream f(filename.c_str(), ofstream::out | ofstream::trunc | ofstream::binary); @@ -72,7 +70,6 @@ void save_rgba(const void *buffer, int width, int height, const string &filename } void save_viewport(const string &filename) { - cout << filename << endl; glFinish(); int vp[4] = {}; glGetIntegerv(GL_VIEWPORT, vp); @@ -81,82 +78,40 @@ void save_viewport(const string &filename) { save_rgba(buffer, vp[2], vp[3], filename); } -void build_contour(contour &c) { - const float min_segment_length = 0.001f; - const float rounds = 10.f; - const float rounds2 = 1.f; - - contour back; - - float angle = 360.f; - float offset = 0.25f/(rounds + 1.f); - - // go front - while(true) { - float radius = angle/360.f/(rounds + 1.f); - float step = min_segment_length*180.f/M_PI/radius; - if (radius > 1.f - 2.f*offset) break; - - float fr = radius + offset; - float fx = fr*sinf(angle/180.f*M_PI); - float fy = fr*cosf(angle/180.f*M_PI); - - float br = radius - offset; - float bx = br*sinf(angle/180.f*M_PI); - float by = br*cosf(angle/180.f*M_PI); - - c.push_back(vec2(fx, fy)); - back.push_back(vec2(bx, by)); - - angle += step; - } - - float max_angle = angle; - - while(true) { - float radius = max_angle/360.f/(rounds + 1.f) - + (max_angle-angle)/360.f/rounds2; - float step = min_segment_length*180.f/M_PI/radius; - if (radius < 1.f/(rounds + 1.f)) - break; - - float fr = radius + offset; - float fx = fr*sinf(angle/180.f*M_PI); - float fy = fr*cosf(angle/180.f*M_PI); - - float br = radius - offset; - float bx = br*sinf(angle/180.f*M_PI); - float by = br*cosf(angle/180.f*M_PI); - - c.push_back(vec2(fx, fy)); - back.push_back(vec2(bx, by)); - - angle += step; +void draw_contour_strip(const vector &c) { + glBegin(GL_TRIANGLE_STRIP); + for(vector::const_iterator i = c.begin(); i != c.end(); ++i) { + glVertex2d(i->x, i->y); + glVertex2d(-1.0, i->y); } - - - // go back - c.reserve(c.size() + back.size() + 1); - for(contour::reverse_iterator ri = back.rbegin(); ri != back.rend(); ++ri) - c.push_back(*ri); - - // close - c.push_back(c.front()); - - cout << c.size() << " vertices" << endl; + glEnd(); } -void draw_contour_strip(const contour &c) { +void draw_contour_strip(const Contour &c) { glBegin(GL_TRIANGLE_STRIP); - for(contour::const_iterator i = c.begin(); i != c.end(); ++i) { - glVertex2f(i->first, i->second); - glVertex2f(-1.f, i->second); + const Contour::ChunkList &chunks = c.get_chunks(); + Vector prev; + for(Contour::ChunkList::const_iterator i = chunks.begin(); i != chunks.end(); ++i) { + if ( i->type == Contour::LINE + || i->type == Contour::CLOSE) + { + glVertex2d(i->p1.x, i->p1.y); + glVertex2d(-1.0, i->p1.y); + prev.x = -1.0; + prev.y = i->p1.y; + } else { + glVertex2d(prev.x, prev.y); + glVertex2d(prev.x, prev.y); + glVertex2d(i->p1.x, i->p1.y); + glVertex2d(i->p1.x, i->p1.y); + prev = i->p1; + } } glEnd(); } - -void draw_contour(const contour &c, bool even_odd, bool invert) { +template +void draw_contour(const T &c, bool even_odd, bool invert) { glPushAttrib(GL_ALL_ATTRIB_BITS); glEnable(GL_STENCIL_TEST); @@ -185,10 +140,10 @@ void draw_contour(const contour &c, bool even_odd, bool invert) { glStencilFunc(GL_EQUAL, 0, 1); glBegin(GL_TRIANGLE_STRIP); - glVertex2d(-1.f, -1.f); - glVertex2d( 1.f, -1.f); - glVertex2d(-1.f, 1.f); - glVertex2d( 1.f, 1.f); + glVertex2d(-1.0, -1.0); + glVertex2d( 1.0, -1.0); + glVertex2d(-1.0, 1.0); + glVertex2d( 1.0, 1.0); glEnd(); glPopAttrib(); @@ -204,46 +159,104 @@ public: test_wrapper(const string &filename): filename(filename), t(clock()) { } ~test_wrapper() { glFinish(); - cout << 1000.0*(double)(clock() - t)/(double)(CLOCKS_PER_SEC) << " ms" << endl; - cout << filename << endl; - save_viewport(filename); + double ms = 1000.0*(double)(clock() - t)/(double)(CLOCKS_PER_SEC); + cout << ms << " ms - " << filename << endl; + if (filename.size() > 4 && filename.substr(filename.size()-4, 4) == ".tga") + save_viewport(filename); glClear(GL_COLOR_BUFFER_BIT); + glFinish(); } }; -void test() { - contour c; - build_contour(c); +void test1() { + vector c; + ContourBuilder::build_simple(c); + cout << c.size() << " vertices" << endl; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glColor4d(0.0, 0.0, 1.0, 1.0); + + { + test_wrapper t("test_1_contour.tga"); + glBegin(GL_LINE_STRIP); + for(vector::const_iterator i = c.begin(); i != c.end(); ++i) + glVertex2d(i->x, i->y); + glEnd(); + } + + { + test_wrapper t("test_1_contour_fill.tga"); + draw_contour(c, false, false); + } + + { + test_wrapper t("test_1_contour_fill_invert.tga"); + draw_contour(c, false, true); + } + + { + test_wrapper t("test_1_contour_evenodd.tga"); + draw_contour(c, true, false); + } + + { + test_wrapper t("test_1_contour_evenodd_invert.tga"); + draw_contour(c, true, true); + } + + glPopAttrib(); +} - glColor4f(0.f, 0.f, 1.f, 1.f); +void test2() { + Contour c, cc; + ContourBuilder::build(cc); + cout << cc.get_chunks().size() << " commands" << endl; + + Rect bounds; + bounds.p0 = Vector(-1.0, -1.0); + bounds.p1 = Vector( 1.0, 1.0); + Vector min_size(1.0/1024.0, 1.0/1024.0); { - test_wrapper t("test_contour.tga"); + test_wrapper("test_2_split"); + cc.split(c, bounds, min_size); + } + + const Contour::ChunkList &chunks = c.get_chunks(); + cout << chunks.size() << " vertices" << endl; + + glPushAttrib(GL_ALL_ATTRIB_BITS); + glColor4d(0.0, 0.0, 1.0, 1.0); + + { + test_wrapper t("test_2_contour.tga"); glBegin(GL_LINE_STRIP); - for(contour::const_iterator i = c.begin(); i != c.end(); ++i) - glVertex2f(i->first, i->second); + for(Contour::ChunkList::const_iterator i = chunks.begin(); i != chunks.end(); ++i) + glVertex2d(i->p1.x, i->p1.y); glEnd(); } { - test_wrapper t("test_contour_fill.tga"); + test_wrapper t("test_2_contour_fill.tga"); draw_contour(c, false, false); } { - test_wrapper t("test_contour_fill_invert.tga"); + test_wrapper t("test_2_contour_fill_invert.tga"); draw_contour(c, false, true); } { - test_wrapper t("test_contour_evenodd.tga"); + test_wrapper t("test_2_contour_evenodd.tga"); draw_contour(c, true, false); } { - test_wrapper t("test_contour_evenodd_invert.tga"); + test_wrapper t("test_2_contour_evenodd_invert.tga"); draw_contour(c, true, true); } + + glPopAttrib(); } int main() { @@ -298,7 +311,8 @@ int main() { glViewport(0, 0, pbuffer_width, pbuffer_height); // do something - test(); + test1(); + test2(); // deinitialization glXMakeContextCurrent(display, None, None, NULL); diff --git a/c++/contourgl/vector.h b/c++/contourgl/vector.h new file mode 100644 index 0000000..ce674a4 --- /dev/null +++ b/c++/contourgl/vector.h @@ -0,0 +1,258 @@ +/* + ......... 2015 Ivan Mahonin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _VECTOR_H_ +#define _VECTOR_H_ + +#include + +class Vector { +public: + typedef float float; + +private: + float _x, _y; + +public: + Vector(): _x(0.0), _y(0.0) { }; + Vector(const float &x, const float &y):_x(x),_y(y) { }; + Vector(const float &radius, const Angle &angle): + _x(radius*Angle::cos(angle).get()), + _y(radius*Angle::sin(angle).get()) + { }; + + bool is_valid()const { return !(isnan(_x) || isnan(_y)); } + bool is_nan_or_inf()const { return isnan(_x) || isnan(_y) || isinf(_x) || isinf(_y); } + + float & + operator[](const int& i) + { return i?_y:_x; } + + const float & + operator[](const int& i) const + { return i?_y:_x; } + + const Vector & + operator+=(const Vector &rhs) + { + _x+=rhs._x; + _y+=rhs._y; + return *this; + } + + const Vector & + operator-=(const Vector &rhs) + { + _x-=rhs._x; + _y-=rhs._y; + return *this; + } + + const Vector & + operator*=(const float &rhs) + { + _x*=rhs; + _y*=rhs; + return *this; + } + + const Vector & + operator/=(const float &rhs) + { + float tmp=1.0/rhs; + _x*=tmp; + _y*=tmp; + return *this; + } + + Vector + operator+(const Vector &rhs)const + { return Vector(*this)+=rhs; } + + Vector + operator-(const Vector &rhs)const + { return Vector(*this)-=rhs; } + + Vector + operator*(const float &rhs)const + { return Vector(*this)*=rhs; } + + Vector + operator/(const float &rhs)const + { return Vector(*this)/=rhs; } + + Vector + operator-()const + { return Vector(-_x,-_y); } + + float + operator*(const Vector &rhs)const + { return _x*rhs._x+_y*rhs._y; } + + bool + operator==(const Vector &rhs)const + { return _x==rhs._x && _y==rhs._y; } + + bool + operator!=(const Vector &rhs)const + { return _y!=rhs._y || _x!=rhs._x; } + + //! Returns the squared magnitude of the vector + float mag_squared()const + { return _x*_x+_y*_y; } + + //! Returns the magnitude of the vector + float mag()const + { return sqrt(mag_squared()); } + + //! Returns the reciprocal of the magnitude of the vector + float inv_mag()const + { return 1.0/sqrt(mag_squared()); } + + //! Returns a normalized version of the vector + Vector norm()const + { return (*this)*inv_mag(); } + + //! Returns a perpendicular version of the vector + Vector perp()const + { return Vector(_y,-_x); } + + Angle angle()const + { return Angle::rad(atan2(_y, _x)); } + + bool is_equal_to(const Vector& rhs)const + { + static const float epsilon(0.0000000000001); +// return (_x>rhs._x)?_x-rhs._x<=epsilon:rhs._x-_x<=epsilon && (_y>rhs._y)?_y-rhs._y<=epsilon:rhs._y-_y<=epsilon; + return (*this-rhs).mag_squared()<=epsilon; + } + + static const Vector zero() { return Vector(0,0); } + + Vector multiply_coords(const Vector &rhs) const + { return Vector(_x*rhs._x, _y*rhs._y); } + Vector divide_coords(const Vector &rhs) const + { return Vector(_x/rhs._x, _y/rhs._y); } + Vector one_divide_coords() const + { return Vector(1.0/_x, 1.0/_y); } + Vector rotate(const Angle &rhs) const + { + float s = Angle::sin(rhs).get(); + float c = Angle::cos(rhs).get(); + return Vector(c*_x - s*_y, s*_x + c*_y); + } +}; + +/*! \typedef Point +** \todo writeme +*/ +typedef Vector Point; + + + +}; // END of namespace synfig + +namespace std { + +inline synfig::Vector::float +abs(const synfig::Vector &rhs) + { return rhs.mag(); } + +}; // END of namespace std + +#include + +_ETL_BEGIN_NAMESPACE + +template <> +class bezier_base : public std::unary_function +{ +public: + typedef synfig::Vector float; + typedef float time_type; +private: + + bezier_base bezier_x,bezier_y; + + float a,b,c,d; + +protected: + affine_combo affine_func; + +public: + bezier_base() { } + bezier_base( + const float &a, const float &b, const float &c, const float &d, + const time_type &r=0.0, const time_type &s=1.0): + a(a),b(b),c(c),d(d) { set_rs(r,s); sync(); } + + void sync() + { + bezier_x[0]=a[0],bezier_y[0]=a[1]; + bezier_x[1]=b[0],bezier_y[1]=b[1]; + bezier_x[2]=c[0],bezier_y[2]=c[1]; + bezier_x[3]=d[0],bezier_y[3]=d[1]; + bezier_x.sync(); + bezier_y.sync(); + } + + float + operator()(time_type t)const + { + return synfig::Vector(bezier_x(t),bezier_y(t)); + } + + void evaluate(time_type t, float &f, float &df) const + { + t=(t-get_r())/get_dt(); + + const float p1 = affine_func( + affine_func(a,b,t), + affine_func(b,c,t) + ,t); + const float p2 = affine_func( + affine_func(b,c,t), + affine_func(c,d,t) + ,t); + + f = affine_func(p1,p2,t); + df = (p2-p1)*3; + } + + void set_rs(time_type new_r, time_type new_s) { bezier_x.set_rs(new_r,new_s); bezier_y.set_rs(new_r,new_s); } + void set_r(time_type new_r) { bezier_x.set_r(new_r); bezier_y.set_r(new_r); } + void set_s(time_type new_s) { bezier_x.set_s(new_s); bezier_y.set_s(new_s); } + const time_type &get_r()const { return bezier_x.get_r(); } + const time_type &get_s()const { return bezier_x.get_s(); } + time_type get_dt()const { return bezier_x.get_dt(); } + + float & + operator[](int i) + { return (&a)[i]; } + + const float & + operator[](int i) const + { return (&a)[i]; } + + //! Bezier curve intersection function + time_type intersect(const bezier_base &/*x*/, time_type /*near*/=0.0)const + { + return 0; + } +}; + +#endif