libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNStroke.cpp
Go to the documentation of this file.
1 /* Copyright 2008-2016 INSA Lyon, CoReNum, ENS-Lyon
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNStroke.cpp
19  * \author Yann LEYDIER
20  */
21 
22 #include <CRNGeometry/CRNStroke.h>
23 #include <CRNException.h>
24 #include <limits>
25 #include <math.h>
26 #include <CRNData/CRNDataFactory.h>
27 #include <CRNi18n.h>
28 
29 using namespace crn;
30 
34 Stroke::Stroke() = default;
38 Stroke::~Stroke() = default;
39 
40 Stroke::Stroke(const Stroke &other):
41  points(other.points)
42 {
43  if (other.bbox)
44  bbox = CloneAs<Rect>(*other.bbox);
45 }
46 
48 {
49  points = other.points;
50  if (other.bbox)
51  bbox = CloneAs<Rect>(*other.bbox);
52  return *this;
53 }
54 
64 {
65  if (el.GetName() != "Stroke")
66  {
67  throw ExceptionInvalidArgument(StringUTF8("void Stroke::Deserialize(xml::Element &el): ") +
68  _("Wrong XML element."));
69  }
70  points.clear();
71  bbox = nullptr;
72  xml::Node n(el.GetFirstChild());
73  if (!n)
74  return; // no content
75  xml::Text t(n.AsText()); // may throw
76  const auto pts = String(t.GetValue()).Split(U" \n\r\t");
77  for (size_t tmp = 0; tmp < pts.size() / 2; ++tmp)
78  AddPoint(Point2DInt(pts[tmp + tmp].ToInt(), pts[tmp + tmp + 1].ToInt()));
79 }
80 
88 {
89  xml::Element el(parent.PushBackElement("Stroke"));
90  StringUTF8 pts;
91  for (const auto &p : points)
92  {
93  pts += p.X;
94  pts += " ";
95  pts += p.Y;
96  pts += " ";
97  }
98  el.PushBackText(pts);
99  return el;
100 }
101 
108 {
109  points.push_back(p);
110  bbox = nullptr;
111 }
112 
113 /*****************************************************************************/
121 void Stroke::MergeAtEnd(const Stroke &str)
122 {
123  if (&str == this)
124  throw ExceptionInvalidArgument(_("Invalid stroke"));
125 
126  auto p = points.back();
127  int offx = p.X;
128  int offy = p.Y;
129  p = str.points.front();
130  offx -= p.X;
131  offy -= p.Y;
132  for (size_t tmp = 1; tmp < str.points.size(); tmp++)
133  {
134  p = str.points[tmp];
135  p.X += offx;
136  p.Y += offy;
137  AddPoint(p);
138  }
139 
140  bbox = nullptr;
141 }
142 
143 /*****************************************************************************/
152 void Stroke::MergeWithXInterpolation(const Stroke &str, unsigned int step)
153 {
154  if (&str == this)
155  throw ExceptionInvalidArgument(_("Invalid stroke"));
156 
157  if (step <= 0)
158  {
159  throw ExceptionDomain(StringUTF8("void Stroke::MergeWithXInterpolation("
160  "const Stroke &str, double step): ") +
161  _("Null step."));
162  }
163  auto p = points.back();
164  int bx = p.X;
165  double by = p.Y;
166  p = str.points.front();
167  int ex = p.X;
168  double ey = p.Y;
169 
170  if (Abs(ex - bx) > int(step))
171  {
172  double yoffset = (ey - by) / Abs(ex - bx);
173  double y = by + yoffset;
174  if (bx > ex)
175  { // right to left
176  for (int x = bx - step; x > ex; x -= step)
177  {
178  AddPoint(Point2DInt(x, (int)y));
179  y += yoffset;
180  }
181  }
182  else
183  { // left to right
184  for (int x = bx + step; x < ex; x += step)
185  {
186  AddPoint(Point2DInt(x, (int)y));
187  y += yoffset;
188  }
189  }
190  }
191 
192  for (const auto &p : str.points)
193  {
194  AddPoint(p);
195  }
196  bbox = nullptr;
197 }
198 
199 /*****************************************************************************/
209 {
210  if (index >= points.size())
211  throw ExceptionDomain(StringUTF8("Point2DInt& Stroke::GetPoint(int index): ") +
212  _("index out of range."));
213  return points[index];
214 }
215 
216 /*****************************************************************************/
225 const Point2DInt& Stroke::GetPoint(size_t index) const
226 {
227  if (index >= points.size())
228  throw ExceptionDomain(StringUTF8("SPoint2DInt& Stroke::GetPoint(int index) const: ") +
229  _("index out of range."));
230  return points[index];
231 }
232 
233 /*****************************************************************************/
242 double Stroke::GetFirstYAtX(double x) const
243 {
244  for (const auto &p : points)
245  if (p.X == x)
246  return p.Y;
247  throw ExceptionDomain(StringUTF8("double Stroke::GetFirstYAtX(double x) const:") +
248  _("Cannot reach abscissa."));
249 }
250 
251 /*****************************************************************************/
258 {
259  if (points.size() > 0)
260  {
261  // is the bbox already computed?
262  if (bbox)
263  return *bbox;
264  // else update bbox
265  auto fp = points.front();
266  Rect r(fp.X, fp.Y, fp.X, fp.Y);
267  for (size_t tmp = 1; tmp < points.size(); tmp++)
268  {
269  const auto &p = points[tmp];
270  r |= Rect(p.X, p.Y, p.X, p.Y);
271  }
272  bbox = CloneAs<Rect>(r); // store to prevent recomputing
273  return r;
274  }
275  else
276  return Rect();
277 }
278 
279 /*****************************************************************************/
287 {
288  auto s = Stroke();
289  for (const auto &p : points)
290  {
291  if (rect.Contains(p.X, p.Y))
292  {
293  s.AddPoint(p);
294  }
295  }
296  return s;
297 }
298 
299 /*****************************************************************************/
307 {
308  auto s = Stroke();
309  auto filling = false;
310  for (const auto &p : points)
311  {
312  if (rect.Contains(p.X, p.Y))
313  {
314  filling = true;
315  s.AddPoint(p);
316  }
317  else if (filling)
318  { // stop after the first intersection
319  break;
320  }
321  }
322  return s;
323 }
324 
327  Cloner::Register<Stroke>();
329 
Rect GetBBox() const
Returns the bounding box using integer values.
Definition: CRNStroke.cpp:257
Stroke & operator=(const Stroke &other)
Definition: CRNStroke.cpp:47
XML element.
Definition: CRNXml.h:135
StringUTF8 GetName() const
Gets the label of the element.
Definition: CRNXml.h:146
Stroke MakeIntersection(const Rect &rect) const
Creates a new stroke that is the intersection of the stroke with a rectangle.
Definition: CRNStroke.cpp:286
#define _(String)
Definition: CRNi18n.h:51
void AddPoint(const Point2DInt &p)
Adds a point to the stroke.
Definition: CRNStroke.cpp:107
Stroke()
Default constructor.
A list of points.
Definition: CRNStroke.h:47
Stroke MakeFirstIntersection(const Rect &rect) const
Creates a new stroke that is the first intersection of the stroke with a rectangle.
Definition: CRNStroke.cpp:306
void MergeAtEnd(const Stroke &str)
Adds a batch of points directly at the end of the stroke.
Definition: CRNStroke.cpp:121
void MergeWithXInterpolation(const Stroke &str, unsigned int step=1)
Adds a batch of points at their original position and adds points to interpolate. ...
Definition: CRNStroke.cpp:152
#define CRN_END_CLASS_CONSTRUCTOR(classname)
Defines a class constructor.
Definition: CRNObject.h:198
A UTF32 character string class.
Definition: CRNString.h:61
A generic domain error.
Definition: CRNException.h:83
Point2DInt & GetPoint(size_t index)
Returns a reference to a point.
Definition: CRNStroke.cpp:208
bool Contains(int x, int y) const noexcept
Checks if the rectangle contains a point.
Definition: CRNRect.cpp:514
XML text.
Definition: CRNXml.h:394
#define CRN_DATA_FACTORY_REGISTER(elemname, classname)
Registers a class to the data factory.
void Deserialize(xml::Element &el)
Initializes the object from an XML element.
Definition: CRNStroke.cpp:63
Node GetFirstChild()
Gets the first child node.
Definition: CRNXml.cpp:303
void Abs(Image< T > &img, typename std::enable_if< std::is_arithmetic< T >::value >::type *dummy=nullptr) noexcept
Replaces each pixel by its absolute value.
Definition: CRNImageGray.h:47
double GetFirstYAtX(double x) const
Returns the ordinate of the first point with abscissa = x.
Definition: CRNStroke.cpp:242
virtual ~Stroke() override
Destructor.
A character string class.
Definition: CRNStringUTF8.h:49
A 2D point class.
Definition: CRNPoint2DInt.h:39
Element PushBackElement(const StringUTF8 &name)
Adds an element at the end of the children list.
Definition: CRNXml.cpp:355
xml::Element Serialize(xml::Element &parent) const
Dumps the object to an XML element.
Definition: CRNStroke.cpp:87
XML node.
Definition: CRNXml.h:60
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107
#define CRN_BEGIN_CLASS_CONSTRUCTOR(classname)
Defines a class constructor.
Definition: CRNObject.h:185
A rectangle class.
Definition: CRNRect.h:46