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