diff --git a/geometry.cpp b/geometry.cpp new file mode 100644 index 0000000..79ee487 --- /dev/null +++ b/geometry.cpp @@ -0,0 +1,4 @@ + +#include "geometry.h" + + diff --git a/geometry.h b/geometry.h new file mode 100644 index 0000000..28f4e9d --- /dev/null +++ b/geometry.h @@ -0,0 +1,53 @@ + +#ifndef GEOMETRY_H +#define GEOMETRY_H + + +#include + + +typedef double Real; + + +class Vector2 { +public: + union { + struct { Real x, y; }; + struct { Real c[2]; }; + }; + + explicit Vector2(Real x = 0, Real y = 0): + x(x), y(y) { } + + Real len_sqr() const { return x*x + y*y; } + Real len() const { return sqrt(len_sqr()); } +}; + + +class Vector3 { +public: + union { + struct { Real x, y, z; }; + struct { Real r, g, b; }; + struct { Real c[3]; }; + }; + + explicit Vector3(Real x = 0, Real y = 0, Real z = 0): + x(x), y(y), z(z) { } +}; + + +class Vector4 { +public: + union { + struct { Real x, y, z, w; }; + struct { Real r, g, b, a; }; + struct { Real c[4]; }; + }; + + explicit Vector4(Real x = 0, Real y = 0, Real z = 0, Real w = 0): + x(x), y(y), z(z), w(w) { } +}; + + +#endif diff --git a/loader.cpp b/loader.cpp index f100997..0bee2d5 100644 --- a/loader.cpp +++ b/loader.cpp @@ -17,7 +17,7 @@ public: bool valid_point; bool newline; - Point point; + TrackPoint point; Internal(Track &track, const std::string &filename): track(track), diff --git a/main.cpp b/main.cpp index d1a5ead..d545c65 100644 --- a/main.cpp +++ b/main.cpp @@ -8,6 +8,7 @@ #include "scene.h" #include "simulator.h" +#include "tool.h" #include "loader.h" class Main { @@ -160,11 +161,11 @@ int main(int argc, char **argv) { Main app; if (!app.init()) return -1; - SimulatorXYZA simulator_xyza(800, 720); + SimulatorXYZA simulator_xyza(400, 360); if (argc > 1) { Loader::load(app.scene->track, argv[1]); app.scene->simulator = &simulator_xyza; - app.scene->simulator->simulate(app.scene->track, 1.5); + app.scene->simulator->simulate(app.scene->track, FlatTool(1.5)); } app.loop(); return 0; diff --git a/raster.cpp b/raster.cpp index dfd4b15..8c65cc9 100644 --- a/raster.cpp +++ b/raster.cpp @@ -1,28 +1,101 @@ +#include + #include "raster.h" -void Raster::touch(int index, Real value, Real x, Real y, Real dx, Real dy, TouchPointList &points) { - dx = std::max(0.500001, dx); - dy = std::max(0.500001, dy); - int x0 = std::max(0, (int)ceil(x - dx)); - int y0 = std::max(0, (int)ceil(y - dy)); - int x1 = std::min(width, (int)ceil(x + dx)); - int y1 = std::min(height, (int)ceil(y + dy)); - dx = 1/dx; - dy = 1/dy; +void Raster::touch(const Painter &painter, const Vector2 &position, int index, TouchPointList &points) { + Vector2 rxy = painter.get_radius_xy(); + rxy.x = fabs(rxy.x) + 0.52; + rxy.y = fabs(rxy.y) + 0.52; + + int x0, x1, y0, y1; + if (rxy.x < width) { // to handle values greater than INT_MAX + x0 = std::max(0 , (int)ceil(position.x - rxy.x)); + x1 = std::min(width , (int)ceil(position.x + rxy.x)); + } else { + x0 = 0; x1 = width; + } + if (rxy.y < height) { + y0 = std::max(0 , (int)ceil(position.y - rxy.y)); + y1 = std::min(height, (int)ceil(position.y + rxy.y)); + } else { + y0 = 0; y1 = height; + } for(int iy = y0; iy < y1; ++iy) { for(int ix = x0; ix < x1; ++ix) { - Real ddx = (x-ix)*dx; - Real ddy = (y-iy)*dy; - if (ddx + ddy <= 1.00001) { - Real &pixel = (*this)[iy][ix]; - if (pixel > value) { - points.push_back( TouchPoint(index, iy, ix, pixel, value) ); - pixel = value; - } + Vector2 v( + std::max(0.0, fabs(ix - position.x) - 0.51), + std::max(0.0, fabs(iy - position.y) - 0.51) ); + Real h = painter.get_height_xy(v); + Real &pixel = (*this)[iy][ix]; + if (pixel > h) { + points.push_back( TouchPoint(index, iy, ix, pixel, h) ); + pixel = h; } } } } + + +Vector4 PolarRaster::color_by_distance(Real d) const { + Real dv = max_value - min_value; + Real k = fabs(dv) < 1e-5 ? 0 : (d - min_value)/dv; + return Vector4( + 1 - k, + 1 - 2*fabs(0.5 - k), + k, + 0.5 ); +} + +void PolarRaster::draw() const +{ + int w = raster.get_width(); + int h = raster.get_height(); + if (w < 2 || h < 2) return; + + Vector3 v, v0, v1; + + Real kx = (x1 - x0)/w; + Real ka = 2*M_PI/h; + + glEnable(GL_LIGHTING); + + glBegin(GL_TRIANGLE_STRIP); + for(int r = 0; r < h; ++r) { + int rr = r ? r-1 : h-1; + Real a0 = rr*ka; + Real a1 = r*ka; + Real s0 = sin(a0), s1 = sin(a1); + Real c0 = cos(a0), c1 = cos(a1); + for(int c = 0; c < w; ++c) { + v0.x = v1.x = x0 + c*kx; + Real r0 = raster[rr][c]; + Real r1 = raster[r][c]; + v0.y = -s0*r0, v0.z = c0*r0; + v1.y = -s1*r1, v1.z = c1*r1; + + Vector3 dv0(v0.x - v.x, v0.y - v.y, v0.z - v.z); + Vector3 dv1(v1.x - v.x, v1.y - v.y, v1.z - v.z); + Vector3 norm( + dv0.y*dv1.z - dv0.z*dv1.y, + dv0.z*dv1.x - dv0.x*dv1.z, + dv0.x*dv1.y - dv0.y*dv1.x ); + Real l = sqrt(norm.x*norm.x + norm.y*norm.y + norm.z*norm.z); + l = l > 1e-5 ? 1/l : 0; + glNormal3d(norm.x*l, norm.y*l, norm.z*l); + + if (!c) glVertex3dv(v0.c); + glColor4dv(color_by_distance(r0).c); + glVertex3dv(v0.c); + glColor4dv(color_by_distance(r0).c); + glVertex3dv(v1.c); + v = v1; + } + glVertex3dv(v.c); + } + glEnd(); + + glDisable(GL_LIGHTING); +} diff --git a/raster.h b/raster.h index ebf2198..c4e39e0 100644 --- a/raster.h +++ b/raster.h @@ -12,6 +12,15 @@ class TouchPoint; typedef std::vector TouchPointList; +class Painter { +public: + virtual ~Painter() { } + virtual Real get_min_height() const = 0; + virtual Vector2 get_radius_xy() const = 0; + virtual Real get_height_xy(const Vector2 &offset) const = 0; +}; + + class Raster { private: Real *data; @@ -62,7 +71,7 @@ public: void set(int x, int y, Real value) { if (x >= 0 && y >= 0 && x < width && y < height) (*this)[y][x] = value; } - void touch(int index, Real value, Real x, Real y, Real dx, Real dy, TouchPointList &points); + void touch(const Painter &painter, const Vector2 &position, int index, TouchPointList &points); }; @@ -84,4 +93,32 @@ public: }; +class PolarRaster { +public: + Real x0; + Real x1; + Real min_value; + Real max_value; + Raster raster; + + explicit PolarRaster( + int xpixels = 0, + int ypixels = 0, + Real x0 = 0, + Real x1 = 1, + Real min_value = 0, + Real max_value = 1 + ): + x0(x0), + x1(x1), + min_value(min_value), + max_value(max_value), + raster(xpixels, ypixels, min_value) + { } + + Vector4 color_by_distance(Real d) const; + void draw() const; +}; + + #endif diff --git a/scene.cpp b/scene.cpp index 98f7ddd..bbb3fa2 100644 --- a/scene.cpp +++ b/scene.cpp @@ -14,7 +14,7 @@ Scene::Scene(): time(), time_forward(0.0), time_backward(20.0), - time_speed(10.0), + time_speed(30.0), simulator() { } @@ -118,10 +118,10 @@ void Scene::draw_track() { int index = track.index_by_time(time); if (index <= 0) return; - const Point &p0 = track.points[index-1]; - const Point &p1 = track.points[index]; + const TrackPoint &p0 = track.points[index-1]; + const TrackPoint &p1 = track.points[index]; Real l = p1.duration > 1e-5 ? (p0.time - time)/p1.duration : 0.5; - Point p = Point::median(l, p0, p1); + TrackPoint p = TrackPoint::median(l, p0, p1); glBegin(GL_LINE_STRIP); for(int i = 0; i < index; ++i) { diff --git a/simulator.cpp b/simulator.cpp index d03bcdd..22614f6 100644 --- a/simulator.cpp +++ b/simulator.cpp @@ -1,56 +1,63 @@ #include -#include - #include "simulator.h" -void SimulatorXYZA::simulate(const Track &track, Real tool_radius) { +void SimulatorXYZA::simulate(const Track &track, const Tool &tool) { points.clear(); simu_index = 0; - x0 = x1 = 0; + raster.x0 = raster.x1 = raster.min_value = raster.max_value = 0; - if (!raster.get_data() || track.points.empty()) return; + if (!raster.raster.get_data() || track.points.empty()) return; std::cout << "SimulatorXYZA::simulate" << std::endl; - x0 = x1 = track.points.front().position.x; - Real max_radius = 0; - for(PointList::const_iterator i = track.points.begin(); i != track.points.end(); ++i) { - Real radius = sqrt(i->position.y*i->position.y + i->position.z*i->position.z); - if (max_radius < radius) max_radius = radius; - if (x0 > i->position.x) x0 = i->position.x; - if (x1 < i->position.x) x1 = i->position.x; + raster.x0 = raster.x1 = track.points.front().position.x; + for(TrackPointList::const_iterator i = track.points.begin(); i != track.points.end(); ++i) { + Real distance = sqrt(i->position.y*i->position.y + i->position.z*i->position.z); + if (raster.max_value < distance) raster.max_value = distance; + if (raster.x0 > i->position.x) raster.x0 = i->position.x; + if (raster.x1 < i->position.x) raster.x1 = i->position.x; } - raster.fill(max_radius); + raster.raster.fill(raster.max_value); - if (x1 + 1e-5 < x0) { + if (raster.x1 + 1e-5 < raster.x0) { std::cout << "SimulatorXYZA::simulate: zero bound" << std::endl; return; } - Real h = raster.get_height(); - Real kx = raster.get_width()/(x1 - x0); - Real ka = 1/360.0; - Real kr = h/(2*M_PI); + Real x0 = raster.x0; + Real dx = raster.x1 - x0; + Real h = raster.raster.get_height(); + Real w = raster.raster.get_width(); + + Vector2 scale_to_raster( w/dx , 1.0/360.0 ); + Vector2 scale_to_tool ( dx/w , 2*M_PI/h ); + PolarToolPainter painter(&tool, scale_to_tool, 1); - Real dx = fabs(tool_radius*kx); + unsigned long long max_count = 2ull*1024*1024*1024/sizeof(points.front()); + int index = 0; - for(PointList::const_iterator i = track.points.begin(); i != track.points.end(); ++i, ++index) { - Real radius = sqrt(i->position.y*i->position.y + i->position.z*i->position.z); - if (radius < 1e-5) radius = 1e-5; - Real dy = fabs(tool_radius/radius*kr); + for(TrackPointList::const_iterator i = track.points.begin(); i != track.points.end(); ++i, ++index) { + painter.distance = sqrt(i->position.y*i->position.y + i->position.z*i->position.z); + if (painter.distance < 1e-5) painter.distance = 1e-5; - Real x = (i->position.x - x0)*kx; - Real y = i->angle*ka; - y = (y - floor(y))*h; + Vector2 position( + scale_to_raster.x * (i->position.x - x0), + scale_to_raster.y * i->angle ); + position.y = (position.y - floor(position.y))*h; + Vector2 rxy = painter.get_radius_xy(); - raster.touch(index, radius, x, y, dx, dy, points); - if (y - dy < 0) - raster.touch(index, radius, x, y + h, dx, dy, points); - if (y + dy > h) - raster.touch(index, radius, x, y - h, dx, dy, points); + raster.raster.touch(painter, position, index, points); + if (position.y - rxy.y < 0) + raster.raster.touch(painter, Vector2(position.x, position.y + h), index, points); + if (position.y + rxy.y > h) + raster.raster.touch(painter, Vector2(position.x, position.y - h), index, points); + if (points.size() >= max_count) { + std::cerr << "SimulatorXYZA::simulate: reached memory limit at time " << i->time << std::endl; + break; + } if (index % 1000 == 0) std::cout << "." << std::flush; @@ -59,62 +66,14 @@ void SimulatorXYZA::simulate(const Track &track, Real tool_radius) { } simu_index = (int)points.size(); + std::cout << std::endl; std::cout << "SimulatorXYZA::simulate: done" << std::endl; } void SimulatorXYZA::scroll_to(int index) { while(simu_index < (int)points.size() && points[simu_index].index <= index) - points[simu_index++].apply_next(raster); + points[simu_index++].apply_next(raster.raster); while(simu_index > 0 && points[simu_index-1].index > index) - points[--simu_index].apply_prev(raster); + points[--simu_index].apply_prev(raster.raster); } - -void SimulatorXYZA::draw() const { - int w = raster.get_width(); - int h = raster.get_height(); - if (w < 2 || h < 2) return; - - Vector3 v, v0, v1; - - Real kx = (x1 - x0)/w; - Real ka = 2*M_PI/h; - glEnable(GL_LIGHTING); - - glColor4d(0, 0, 1, 0.5); - - glBegin(GL_TRIANGLE_STRIP); - for(int r = 0; r < h; ++r) { - int rr = r ? r-1 : h-1; - Real a0 = rr*ka; - Real a1 = r*ka; - Real s0 = sin(a0), s1 = sin(a1); - Real c0 = cos(a0), c1 = cos(a1); - for(int c = 0; c < w; ++c) { - v0.x = v1.x = x0 + c*kx; - Real r0 = raster[rr][c]; - Real r1 = raster[r][c]; - v0.y = -s0*r0, v0.z = c0*r0; - v1.y = -s1*r1, v1.z = c1*r1; - - Vector3 dv0(v0.x - v.x, v0.y - v.y, v0.z - v.z); - Vector3 dv1(v1.x - v.x, v1.y - v.y, v1.z - v.z); - Vector3 norm( - dv0.y*dv1.z - dv0.z*dv1.y, - dv0.z*dv1.x - dv0.x*dv1.z, - dv0.x*dv1.y - dv0.y*dv1.x ); - Real l = sqrt(norm.x*norm.x + norm.y*norm.y + norm.z*norm.z); - l = l > 1e-5 ? 1/l : 0; - glNormal3d(norm.x*l, norm.y*l, norm.z*l); - - if (!c) glVertex3dv(v0.c); - glVertex3dv(v0.c); - glVertex3dv(v1.c); - v = v1; - } - glVertex3dv(v.c); - } - glEnd(); - - glDisable(GL_LIGHTING); -} diff --git a/simulator.h b/simulator.h index 14d81e0..ff9efa1 100644 --- a/simulator.h +++ b/simulator.h @@ -7,12 +7,13 @@ #include "track.h" #include "raster.h" +#include "tool.h" class Simulator { public: virtual ~Simulator() { } - virtual void simulate(const Track &track, Real tool_radius) = 0; + virtual void simulate(const Track &track, const Tool &tool) = 0; virtual void scroll_to(int index) = 0; virtual void draw() const = 0; }; @@ -24,29 +25,19 @@ private: int simu_index; public: - Real x0; - Real x1; - Raster raster; + PolarRaster raster; - SimulatorXYZA(): simu_index(), x0(), x1() { } - - SimulatorXYZA(int xpixels, int ypixels): - SimulatorXYZA() - { resize(xpixels, ypixels); } + explicit SimulatorXYZA(int xpixels = 0, int ypixels = 0): + simu_index(), raster(xpixels, ypixels, 0, 0, 0, 0) { } const TouchPointList& get_points() const { return points; } int get_simu_index() const { return simu_index; } int get_index() const { return simu_index <= 0 ? -1 : points[simu_index-1].index; } - void resize(int xpixels, int ypixels) { - points.clear(); - simu_index = 0; - raster.resize(xpixels, ypixels); - } - - void simulate(const Track &track, Real tool_radius); - void scroll_to(int index); - void draw() const; + void simulate(const Track &track, const Tool &tool) override; + void scroll_to(int index) override; + void draw() const override + { raster.draw(); } }; diff --git a/tool.cpp b/tool.cpp new file mode 100644 index 0000000..49d7d58 --- /dev/null +++ b/tool.cpp @@ -0,0 +1,3 @@ + +#include "tool.h" + diff --git a/tool.h b/tool.h new file mode 100644 index 0000000..14d39cd --- /dev/null +++ b/tool.h @@ -0,0 +1,105 @@ + +#ifndef TOOL_H +#define TOOL_H + + +#include + +#include "raster.h" + + +class Tool { +public: + virtual ~Tool() { } + virtual Real get_radius() const = 0; // must be >= 0 + virtual Real get_height(Real offset) const = 0; // must be >= 0 + virtual Real get_height_polar(Real distance, Real offset, Real angle) const { // must be >= distance + // not exact solution, only for small angles + if (distance < 1e-5) return 1e-5; + Vector2 v(offset, tan(angle)*distance); + return sqrt(distance*distance + v.y*v.y) + get_height(v.len()); + } +}; + + +class FlatTool: public Tool { +public: + Real radius; + + explicit FlatTool(Real radius = 0.5): + radius(radius) { } + + Real get_radius() const override + { return radius; } + Real get_height(Real offset) const override + { return fabs(offset) <= fabs(radius) ? 0 : INFINITY; } + Real get_height_polar(Real distance, Real offset, Real angle) const override { + if (distance < 1e-5) return 1e-5; + if (!(fabs(offset) <= fabs(radius))) return INFINITY; + Real t2 = tan(angle)*distance; t2 *= t2; + Real r2 = radius*radius - offset*offset; + return t2 <= r2 ? sqrt(distance*distance + t2) : INFINITY; + } +}; + + +class ToolPainter: public Painter { +public: + const Tool *tool; + Vector2 scale; + Real distance; + + explicit ToolPainter(const Tool *tool = 0, const Vector2 scale = Vector2(1, 1)): + tool(tool), scale(scale), distance() { } + Real get_min_height() const override + { return distance; } + Vector2 get_radius_xy() const override { + if (!tool) return Vector2(); + Vector2 rxy = get_tool_radius_xy(); + return Vector2(rxy.x/scale.x, rxy.y/scale.y); + } + Real get_height_xy(const Vector2 &offset) const override { + if (!tool) return INFINITY; + return get_tool_height_xy(Vector2(offset.x*scale.x, offset.y*scale.y)); + } + + virtual Vector2 get_tool_radius_xy() const = 0; + virtual Real get_tool_height_xy(const Vector2 &offset) const = 0; +}; + +class FlatToolPainter: public ToolPainter { + using ToolPainter::ToolPainter; // parent constructors + + Vector2 get_tool_radius_xy() const override { + Real r = tool->get_radius(); + return Vector2(r, r); + } + + Real get_tool_height_xy(const Vector2 &offset) const override + { return distance + tool->get_height(offset.len()); } +}; + +class PolarToolPainter: public ToolPainter { +public: + int coord; + + explicit PolarToolPainter(const Tool *tool = 0, const Vector2 scale = Vector2(1, 1), int coord = 0): + ToolPainter(tool, scale), coord(coord) { } + + Vector2 get_tool_radius_xy() const override { + assert(coord > 0 && coord < 2); + Real r = tool->get_radius(); + Vector2 rxy(r, r); + rxy.c[coord] = distance > 1e-5 ? rxy.c[coord]/distance : INFINITY; + return rxy; + } + + Real get_tool_height_xy(const Vector2 &offset) const override { + if (!tool) return INFINITY; + assert(coord > 0 && coord < 2); + return tool->get_height_polar(distance, offset.c[1-coord], offset.c[coord]); + } +}; + + +#endif diff --git a/track.cpp b/track.cpp index 39406b3..6900214 100644 --- a/track.cpp +++ b/track.cpp @@ -5,10 +5,10 @@ void Track::split(Real da, Real dl, Real dt) { if (points.size() <= 1u) return; - PointList new_points; + TrackPointList new_points; new_points.push_back(points.front()); - for(PointList::iterator i = points.begin() + 1; i != points.end(); ++i) { - Point &prev = *(i - 1); + for(TrackPointList::iterator i = points.begin() + 1; i != points.end(); ++i) { + TrackPoint &prev = *(i - 1); int count = 0; if (da > 1e-5) { @@ -25,7 +25,7 @@ void Track::split(Real da, Real dl, Real dt) { } for(int j = 1; j < count-1; ++j) - new_points.push_back( Point::median(j/(Real)count, prev, *i) ); + new_points.push_back( TrackPoint::median(j/(Real)count, prev, *i) ); new_points.push_back(*i); } @@ -35,12 +35,12 @@ void Track::split(Real da, Real dl, Real dt) { void Track::calc_length() { if (points.empty()) return; - points.front().real_pos = Point::calc_real_pos( points.front() ); + points.front().real_pos = TrackPoint::calc_real_pos( points.front() ); Vector3 prev = points.front().real_pos; Real l = 0, time = 0; - for(PointList::iterator i = points.begin(); i != points.end(); ++i) { + for(TrackPointList::iterator i = points.begin(); i != points.end(); ++i) { if (i->speed < 1e-3) i->speed = 1e-3; - const Vector3 &p = i->real_pos = Point::calc_real_pos(*i); + const Vector3 &p = i->real_pos = TrackPoint::calc_real_pos(*i); Vector3 d(p.x - prev.x, p.y - prev.y, p.y - prev.y); prev = p; diff --git a/track.h b/track.h index ce4e9eb..0d67586 100644 --- a/track.h +++ b/track.h @@ -5,40 +5,12 @@ #include #include - #include - -typedef double Real; - -class Vector3 { -public: - union { - struct { Real x, y, z; }; - struct { Real r, g, b; }; - struct { Real c[3]; }; - }; - - explicit Vector3(Real x = 0, Real y = 0, Real z = 0): - x(x), y(y), z(z) { } -}; - - -class Vector4 { -public: - union { - struct { Real x, y, z, w; }; - struct { Real r, g, b, a; }; - struct { Real c[4]; }; - }; - - explicit Vector4(Real x = 0, Real y = 0, Real z = 0, Real w = 0): - x(x), y(y), z(z), w(w) { } -}; - +#include "geometry.h" -class Point { +class TrackPoint { public: Vector3 position; Real angle; @@ -50,9 +22,9 @@ public: Real duration; Vector3 real_pos; - Point(): angle(), speed(), l(), time(), length() { } + TrackPoint(): angle(), speed(), l(), time(), length() { } - static Vector3 calc_real_pos(const Point &point) { + static Vector3 calc_real_pos(const TrackPoint &point) { Real a = point.angle*(M_PI/180); Real s = sin(a); Real c = cos(a); @@ -62,9 +34,9 @@ public: point.position.y*s + point.position.z*c ); } - static Point median(Real l, const Point &p0, const Point &p1) { + static TrackPoint median(Real l, const TrackPoint &p0, const TrackPoint &p1) { Real k = 1 - l; - Point p; + TrackPoint p; p.position.x = p0.position.x*k + p1.position.x*l; p.position.y = p0.position.y*k + p1.position.y*l; p.position.z = p0.position.z*k + p1.position.z*l; @@ -81,11 +53,11 @@ public: } }; -typedef std::vector PointList; +typedef std::vector TrackPointList; class Track { public: - PointList points; + TrackPointList points; void split(Real da, Real dl, Real dt); void calc_length();