00001 #include "streamlines.h"
00002 #include <cmath>
00003 #include <algorithm>
00004
00005 float StreamlineGenerator::GetMinDistance(const vec2 &v) const
00006 {
00007 typedef std::list<vec2>::const_iterator iter_t;
00008
00009 int cx, cy;
00010 ComputeCellPosition(v, cx, cy);
00011
00012
00013
00014 float mindist2 = HUGE;
00015
00016 const iter_t beg = grid(cx, cy).begin();
00017 const iter_t end = grid(cx, cy).end();
00018
00019 for (iter_t it = beg; it != end; ++it) {
00020 const vec2 diff = v - *it;
00021 mindist2 = std::min(mindist2, dot(diff, diff));
00022 }
00023
00024 const int y0 = std::max(0, cy - 1);
00025 const int y1 = std::min(grid.GetSizeY() - 1, cy + 1);
00026 const int x0 = std::max(0, cx - 1);
00027 const int x1 = std::min(grid.GetSizeX() - 1, cx + 1);
00028
00029 for (int y = y0; y <= y1; ++y) {
00030 for (int x = x0; x <= x1; ++x) {
00031 if (x == cx && y == cy) continue;
00032
00033 const iter_t beg = grid(x, y).begin();
00034 const iter_t end = grid(x, y).end();
00035
00036 for (iter_t it = beg; it != end; ++it) {
00037 const vec2 diff = v - *it;
00038 mindist2 = std::min(mindist2, dot(diff, diff));
00039 }
00040 }
00041 }
00042
00043 return std::sqrt(mindist2);
00044 }
00045
00046 void StreamlineGenerator::PutIntoGrid(const vec2 &v)
00047 {
00048 int cx, cy;
00049 ComputeCellPosition(v, cx, cy);
00050 grid(cx, cy).push_back(v);
00051 }
00052
00053 void StreamlineGenerator::DelayedPutIntoGrid(const vec2 &v)
00054 {
00055 point_queue.push_back(v);
00056
00057 if (point_queue.size() > grid_add_delay) {
00058 const vec2 &p = point_queue.front();
00059 PutIntoGrid(p);
00060 point_queue.pop_front();
00061 }
00062 }
00063
00064 void StreamlineGenerator::BeginTraceLine() {}
00065
00066 void StreamlineGenerator::EndTraceLine()
00067 {
00069 while (point_queue.size()) {
00070 const vec2 &p = point_queue.back();
00071 PutIntoGrid(p);
00072 point_queue.pop_back();
00073 }
00074 }
00075
00076 bool StreamlineGenerator::InterpolateTo(vec2 &position, bool forward)
00077 {
00078 vec2 steppos;
00079 float direction = (forward)?(1.0f):(-1.0f);
00080
00081 switch(inter_type)
00082 {
00083 case ITER_RK:
00084 steppos = position + (v(position) * direction) * stepsize_half;
00085 if (IsOutsideBounds(steppos)) return false;
00086 else position = position + (v(steppos) *direction) * stepsize;
00087 break;
00088 case ITER_EULER:
00089 position = position + (v(position) * direction) * stepsize;
00090 break;
00091 }
00092
00093 return true;
00094 }
00095
00096 void StreamlineGenerator::TraceLine(const vec2& startpos, Streamline &sline)
00097 {
00098 Streamline tmpline;
00099
00100 BeginTraceLine();
00101
00102 bool trace_fwd = true;
00103 bool trace_back = true;
00104
00105 vec2 fpos = startpos;
00106 vec2 bpos = startpos;
00107
00108
00109 if (IsOutsideBounds(fpos)) return;
00110
00111 const float mindist = GetMinDistance(fpos);
00112 if (mindist < dtest) return;
00113
00114 if(!InterpolateTo(fpos, true)) trace_fwd = false;
00115
00116 for (int i = 0; i < maxiter / 2; ++i) {
00117 if (trace_fwd) {
00118 if (IsOutsideBounds(fpos)) goto tf_break;
00119
00120 const float fmindist = GetMinDistance(fpos);
00121 if (fmindist < dtest) goto tf_break;
00122
00123 const vec2 v_fpos = v(fpos);
00124 tmpline.push_back(SamplePoint(fpos, v_fpos, ComputeThickness(fmindist)));
00125
00126 DelayedPutIntoGrid(fpos);
00127
00128 if(!InterpolateTo(fpos, true)) goto tf_break;
00129 }
00130
00131 goto tf_end;
00132 tf_break: trace_fwd = false;
00133 tf_end: ;
00134
00135 if (trace_back) {
00136 if (IsOutsideBounds(bpos)) goto tb_break;
00137
00138 const float bmindist = GetMinDistance(bpos);
00139 if (bmindist < dtest) goto tb_break;
00140
00141 const vec2 v_bpos = v(bpos);
00142 sline.push_back(SamplePoint(bpos, v_bpos, ComputeThickness(bmindist)));
00143
00144 DelayedPutIntoGrid(bpos);
00145
00146 if(!InterpolateTo(bpos, false)) goto tb_break;
00147 }
00148
00149 goto tb_end;
00150 tb_break: trace_back = false;
00151 tb_end: ;
00152 }
00153
00154
00155
00156 std::reverse(sline.begin(), sline.end());
00157
00158
00159 sline.insert(sline.end(), tmpline.begin(), tmpline.end());
00160
00161 EndTraceLine();
00162 }
00163
00164 void StreamlineGenerator::TraceAndStore(const vec2 &v, Streamline &sl)
00165 {
00166 sl.clear();
00167 TraceLine(v, sl);
00168
00169
00170 if (sl.size()) streamlines.push_back(sl);
00171 }
00172
00173 StreamlineGenerator::StreamlineGenerator(
00174 const SL_Rectangle &domain,
00175 float dsep,
00176 float dtest,
00177 float stepsize,
00178 int maxiter,
00179 VelocityFunc v,
00180 SLIterAlg inter_type
00181 )
00182 : domain(domain)
00183 , dsep(dsep)
00184 , dtest(dtest)
00185 , stepsize(stepsize)
00186 , stepsize_half(stepsize / 2.0f)
00187 , maxiter(maxiter)
00188 , v(v)
00189 , grid_add_delay(std::ceil(2.0f * (0.5f * (dtest + dsep)) / stepsize))
00190 , grid(std::ceil(domain.GetWidth() / dsep), std::ceil(domain.GetHeight() / dsep))
00191 , inter_type(inter_type)
00192 {
00193
00194
00195
00196 Streamline sline;
00197
00198
00199 const float x = (domain.left + domain.right) * 0.5f;
00200 const float y = (domain.bottom + domain.top) * 0.5f;
00201 const vec2 startpos(x, y);
00202 TraceAndStore(startpos, sline);
00203
00204
00205 for (size_t cli = 0; cli < streamlines.size(); ++cli) {
00206 for (size_t i = 0, _n = streamlines[cli].size(); i < _n; ++i) {
00207 const Streamline &line = streamlines[cli];
00208
00209
00210 const vec2 offset = rotate90(line[i].dir) * dsep;
00211 const vec2 sp1 = line[i].pos + offset;
00212 const vec2 sp2 = line[i].pos - offset;
00213
00214
00215 TraceAndStore(sp1, sline);
00216 TraceAndStore(sp2, sline);
00217 }
00218 }
00219 }