Fréchet View  1.6.0
A Tool for Exploring Fréchet Distance Algorithms
path.cpp
Go to the documentation of this file.
1 #define _USE_MATH_DEFINES
2 #include <math.h>
3 #include <path.h>
4 
5 using namespace frechet;
6 
7 Path::Path(QObject* parent)
8  : QObject(parent),
9  points(),opcode(),
10  turtle_angle(0.0),
11  defaultLineStyle(SOLID),
12  lineStyles()
13 { }
14 
15 Path::Path(const Path &that)
16  : QObject(that.parent()),
17  points(that.points), opcode(that.opcode),
18  turtle_angle(that.turtle_angle),
19  defaultLineStyle(that.defaultLineStyle),
20  lineStyles(that.lineStyles)
21 { }
22 
23 Path::Path(const Path *that)
24  : Path(*that)
25 { }
26 
27 Path::Path(QString svg, bool forceAbsolute, QObject* parent)
28  : Path(parent)
29 {
30  parseSvg(svg,forceAbsolute);
31 }
32 
33 Path Path::svgPath(QString str, bool forceAbsolute)
34 {
35  Path path;
36  path.parseSvg(str,forceAbsolute);
37  return path;
38 }
39 
41 {
42  Path path;
43  path.parseIpe(str);
44  return path;
45 }
46 
47 const QPolygonF& Path::toCurve(int precision)
48 {
49  absolutize();
50  if (precision > 0)
51  setPrecision(precision);
52  // remove identical points (because they create various problems, later)
54  return points;
55 }
56 
57 Path& Path::operator=(const Path& that)
58 {
59  points = that.points;
60  opcode = that.opcode;
62  lineStyles = that.lineStyles;
63  return *this;
64 }
65 
66 Path& Path::operator+=(const Path& that)
67 {
68  Q_ASSERT(points.size()==(int)opcode.size());
69 
70  points += that.points;
71  opcode += that.opcode;
72 
73  if (!that.lineStyles.empty()) {
74  lineStyles.resize(size());
75  lineStyles.insert(lineStyles.end(),
76  that.lineStyles.begin(),that.lineStyles.end());
77  }
78  // do not copy default line style
79 
80  Q_ASSERT(points.size()==(int)opcode.size());
81  return *this;
82 }
83 
84 
85 Path& Path::operator+=(QString svg)
86 {
87  return (*this) += Path(svg);
88 }
89 
91  if(!points.empty())
92  append(points[0],'M');
93  return this;
94 }
95 
97  return close();
98 }
99 
101  if (!points.empty()) {
102  points.pop_back();
103  opcode.pop_back();
104  }
105  return this;
106 }
107 
109  if (points.size() > 2) {
110  undo();
111  close();
112  }
113  return this;
114 }
115 
116 Path *Path::polar(double angle, double distance) {
117  angle = -angle*M_PI/180.0;
118  return m( cos(angle)*distance, sin(angle)*distance );
119 }
120 
121 Path* Path::map(QTransform tf) {
122  absolutize(); // rotating relative movements would be awful
123  points = tf.map(points);
124  return this;
125 }
126 
127 Path* Path::map(QMatrix mx) {
128  return map(QTransform(mx));
129 }
130 
131 Path* Path::scale(double ascale) {
132  return scale(ascale,ascale);
133 }
134 
135 Path* Path::scale(double scalex, double scaley) {
136  QTransform tf;
137  tf.scale(scalex,scaley);
138  return map(tf);
139 }
140 
141 Path* Path::rotate(double angle) {
142  QTransform tf;
143  tf.rotate(angle);
144  return map(tf);
145 }
146 
147 Path* Path::translate(QPointF offset) {
148  return translate(offset.x(),offset.y());
149 }
150 
151 Path* Path::translate(double dx, double dy) {
152  QTransform tf;
153  tf.translate(dx,dy);
154  return map(tf);
155 }
156 
158  QMatrix matrix(-1,0,0, +1,0,0);
159  return map(matrix);
160 }
161 
163  QMatrix matrix(+1,0,0, -1,0,0);
164  return map(matrix);
165 }
166 
167 
168 void swap(qreal& a, qreal& b) {
169  qreal x = a;
170  a=b;
171  b=x;
172 }
173 
174 void swap(QPointF& a, QPointF& b) {
175  swap (a.rx(),b.rx());
176  swap (a.ry(),b.ry());
177 }
178 
180 {
181  if (!points.empty())
182  {
183  absolutize();
184  QPointF* a = &points.first();
185  QPointF* b = &points.last();
186  while(a < b)
187  swap(*a++,*b--);
188  }
189  return this;
190 }
191 
192 Path *Path::left(double angle) {
193  turtle_angle += angle;
194  return this;
195 }
196 
197 Path *Path::right(double angle) {
198  turtle_angle -= angle;
199  return this;
200 }
201 
202 Path *Path::reset(double angle) {
203  turtle_angle = angle;
204  return this;
205 }
206 
208  return polar(turtle_angle,distance);
209 }
210 
212 {
213  Q_ASSERT(style > DEFAULT && style <= NONE);
214  // DEFAULT := DEFAULT makes no sense
215  defaultLineStyle = (LineStyle)style;
216 }
217 
218 void Path::lineStyle(int style)
219 {
220  if (size()==0) return;
221 
222  Q_ASSERT(style >= 0 && style <= NONE);
223 
224  if (size() > lineStyles.size())
225  lineStyles.resize(size());
226  lineStyles[size()-1] = (LineStyle)style;
227 }
228 
229 void Path::outerLineStyle(int style)
230 {
231  lineStyle(style);
232  if (lineStyles.size() > 0)
233  lineStyles[0] = (LineStyle)style;
234 }
235 
236 void Path::parseSvg(QString str, bool forceAbsolute)
237 {
238  // SVG path string
239  // split string into numbers
240  bool x=true;
241  bool ok;
242  QPointF point;
243  QRegExp numexp("(\\-?[0-9]*\\.?[0-9]+(e[+\\-]?[0-9]+)?)|[MmLlVvHhZz]");
244  char op='M';
245  int pos = numexp.indexIn(str);
246  while(pos >= 0)
247  {
248  int len = numexp.matchedLength();
249 
250  QChar qc = str[pos];
251  if (qc.isLetter()) {
252  if (forceAbsolute) qc = qc.toUpper();
253  op = qc.toLatin1();
254  x = true;
255 
256  switch (op) {
257  case 'Z':
258  case 'z': Z(); break;
259  }
260  }
261  else
262  {
263  double d = str.midRef(pos,len).toDouble(&ok);
264  if (ok)
265  {
266  switch(op)
267  {
268  case 'M':
269  case 'L':
270  case 'm':
271  case 'l':
272  if (x)
273  point.rx() = d;
274  else
275  point.ry() = d;
276 
277  if (!x) append(point,op);
278  x = !x;
279  break;
280  case 'H':
281  case 'h':
282  point.rx() = d;
283  //point.ry() = 0.0;
284  append(point,op);
285  break;
286  case 'V':
287  case 'v':
288  //point.rx() = 0.0;
289  point.ry() = d;
290  append(point,op);
291  break;
292  }
293  }
294  }
295 
296  pos = numexp.indexIn(str, pos+len);
297  }
298 }
299 
300 void Path::parseIpe(QString str)
301 {
302  // IPE path string
303  //
304  // simply a sequence of
305  // x y (m|l)
306  // where all coordinates are absolute
307  //
308  // h closes the polygon
309  bool x=true;
310  bool ok;
311  QPointF point;
312  QRegExp numexp("(\\-?[0-9]*\\.?[0-9]+(e[+\\-]?[0-9]+)?)|[mlh]");
313  char op='M';
314  int pos = numexp.indexIn(str);
315  while(pos >= 0)
316  {
317  int len = numexp.matchedLength();
318 
319  QChar qc = str[pos];
320  if (qc.isLetter()) {
321  qc = qc.toUpper();
322  op = qc.toLatin1();
323  x = true;
324 
325  switch (op) {
326  case 'H':
327  Z(); break;
328  case 'M':
329  case 'L':
330  append(point,op);
331  break;
332  default:
333  Q_ASSERT(false);
334  break;
335  }
336  }
337  else
338  { // number
339  double d = str.midRef(pos,len).toDouble(&ok);
340  if (ok)
341  {
342  if (x)
343  point.rx() = d;
344  else
345  point.ry() = d;
346  x = !x;
347  }
348  }
349 
350  pos = numexp.indexIn(str, pos+len);
351  }
352 }
353 
354 void Path::append(QPointF p, char op)
355 {
356  Q_ASSERT(points.size()==(int)opcode.size());
357 
358  points.push_back(p);
359  opcode.push_back(op);
360 
361  Q_ASSERT(points.size()==(int)opcode.size());
362 }
363 
365 {
366  QPointF ZERO(0,0);
367  QPointF* prev=&ZERO;
368  QPointF* next;
369  for(int i=0; i < opcode.size(); i++)
370  {
371  next = &points[i];
372  switch(opcode[i])
373  {
374  case 'm': case 'l':
375  next->rx() += prev->rx();
376  next->ry() += prev->ry();
377  break;
378  case 'h':
379  next->rx() += prev->rx();
380  next->ry() = prev->ry();
381  break;
382  case 'v':
383  next->rx() = prev->rx();
384  next->ry() += prev->ry();
385  break;
386  case 'H':
387  next->ry() = prev->ry();
388  break;
389  case 'V':
390  next->rx() = prev->rx();
391  break;
392  }
393  opcode[i] = 'M';
394  prev = next;
395  }
396 }
397 
398 void Path::setPrecision(int precision)
399 {
400  double p = pow(10.0f,precision);
401  for(int i=0; i < points.size(); ++i)
402  {
403  points[i].rx() = floor(points[i].x()*p + 0.5) / p;
404  points[i].ry() = floor(points[i].y()*p + 0.5) / p;
405  }
406 }
407 
414 {
415  for(int i=1; i < points.size(); )
416  if (isnan(points[i].x()) || isnan(points[i].y()) || (points[i-1] == points[i])) {
417  points.remove(i);
418  opcode.erase(i,1);
419  if (i < lineStyles.size())
420  lineStyles.erase(lineStyles.cbegin()+i);
421  }
422  else {
423  ++i;
424  }
425 
426 }
427 
void parseIpe(QString str)
pare an IPE input string
Definition: path.cpp:300
QPolygonF points
the list of vertexes
Definition: path.h:34
Path * mirrorx()
mirror the curve about the x-axis
Definition: path.cpp:157
static int distance(int a, int b, int n)
Definition: poly_utils.cpp:210
void swap(qreal &a, qreal &b)
Definition: path.cpp:168
static Path svgPath(QString str, bool forceAbsolute=false)
static constructor from SVG input string
Definition: path.cpp:33
global definitions for all algorithms.
void parseSvg(QString str, bool forceAbsolute=false)
parse an SVG input string
Definition: path.cpp:236
static Path ipePath(QString str)
static constructor from IPE input string
Definition: path.cpp:40
void removeDuplicates()
remove all duplicate points
Definition: path.cpp:413
Path * translate(QPointF offset)
translate the curve
Definition: path.cpp:147
Path * left(double angle)
turn the turtle's head to the left
Definition: path.cpp:192
void append(QPointF p, char opcode)
Definition: path.cpp:354
LineStyle defaultLineStyle
default style for grid disaplay
Definition: path.h:42
Path * reset(double angle=0.0)
turn the turtle's head to a given angle
Definition: path.cpp:202
Path * map(QTransform tf)
apply a transformation to all vertexes
Definition: path.cpp:121
Path * rotate(double angle)
rotate the curve
Definition: path.cpp:141
Path * ensureClosed()
make sure the curve is closed
Definition: path.cpp:108
Path * undo()
remove the last vertex
Definition: path.cpp:100
draw a solid line
Definition: grid.h:19
void setDefaultLineStyle(int)
change the default style of grid lines
Definition: path.cpp:211
Represents a polygonal curve.
Definition: path.h:30
int size() const
Definition: path.h:136
Path * revert()
revert the order of vertexes
Definition: path.cpp:179
Path * mirrory()
mirror the curve about the y-axis
Definition: path.cpp:162
void outerLineStyle(int)
set the style of outermost grid lines
Definition: path.cpp:229
Path * close()
return to the first vertex, creating a closed curve
Definition: path.cpp:90
Path * forward(double distance)
append a new vertex, relative to the current position
Definition: path.cpp:207
Path * scale(double scale)
scale the polygonal curve
Definition: path.cpp:131
Path * Z()
return to the first vertex, creating a closed curve
Definition: path.cpp:96
Q_INVOKABLE Path & operator+=(const Path &that)
append operator
Definition: path.cpp:66
const QPolygonF & toCurve(int precision=0)
convert to a QPolygonF object, ready for processing by the Frechet distance algorithms
Definition: path.cpp:47
Path * polar(double angle, double distance)
append a new vertex, given by polar coordinates
Definition: path.cpp:116
void absolutize()
replace all relative movements by absolute positions
Definition: path.cpp:364
LineStyleVector lineStyles
line styles for grid disaplay
Definition: path.h:45
double turtle_angle
turtle_angle current direction of the turtle
Definition: path.h:38
Definition: grid.h:18
Q_INVOKABLE Path(QObject *parent=0)
default constructor
Definition: path.cpp:7
draw no grid line
Definition: grid.h:22
#define str(S)
Q_INVOKABLE Path & operator=(const Path &that)
assignment operator
Definition: path.cpp:57
LineStyle
display style of grid lines in free-space diagram
Definition: grid.h:15
std::string opcode
used during assembly of a path: the next "move" operation
Definition: path.h:36
void lineStyle(int)
change the style of grid lines
Definition: path.cpp:218
void setPrecision(int precision)
set the numerical precision for input data (default is unlimited)
Definition: path.cpp:398
Path * m(QPointF p)
append a new vertex, relative to the current position
Definition: path.h:215
Path * right(double angle)
turn the turtle's head to the right
Definition: path.cpp:197