]> Shamusworld >> Repos - architektonas/blob - src/base/hatch.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / hatch.cpp
1 // hatch.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  05/28/2010  Added this text. :-)
15 //
16
17 #include "hatch.h"
18
19 #include <QtCore>
20 #include "drawing.h"
21 #include "graphicview.h"
22 #include "information.h"
23 #include "paintinterface.h"
24 #include "pattern.h"
25 #include "patternlist.h"
26
27 /**
28  * Constructor.
29  */
30 Hatch::Hatch(EntityContainer * parent, const HatchData & d):
31         EntityContainer(parent), data(d)
32 {
33         hatch = NULL;
34         updateRunning = false;
35         needOptimization = true;
36 }
37
38 /*virtual*/ Hatch::~Hatch()
39 {
40 }
41
42 Entity * Hatch::clone()
43 {
44         Hatch * t = new Hatch(*this);
45 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
46 //      t->entities.setAutoDelete(entities.autoDelete());
47         t->initId();
48         t->detach();
49         t->hatch = NULL;
50         return t;
51 }
52
53 /**     @return RS2::EntityHatch */
54 /*virtual*/ RS2::EntityType Hatch::rtti() const
55 {
56         return RS2::EntityHatch;
57 }
58
59 /**
60  * @return true: if this is a hatch with lines (hatch pattern),
61  *         false: if this is filled with a solid color.
62  */
63 /*virtual*/ bool Hatch::isContainer() const
64 {
65         if (isSolid())
66                 return false;
67
68         return true;
69 }
70
71 /** @return Copy of data that defines the hatch. */
72 HatchData Hatch::getData() const
73 {
74         return data;
75 }
76
77 /**
78  * Validates the hatch.
79  */
80 bool Hatch::validate()
81 {
82         bool ret = true;
83
84         // loops:
85         for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
86         {
87                 if (l->rtti() == RS2::EntityContainer)
88                 {
89                         EntityContainer * loop = (EntityContainer *)l;
90                         ret = loop->optimizeContours() && ret;
91                 }
92         }
93
94         return ret;
95 }
96
97 /**
98  * @return Number of loops.
99  */
100 int Hatch::countLoops()
101 {
102         if (data.solid)
103                 return count();
104         else
105                 return count() - 1;
106 }
107
108 /** @return true if this is a solid fill. false if it is a pattern hatch. */
109 bool Hatch::isSolid() const
110 {
111         return data.solid;
112 }
113
114 void Hatch::setSolid(bool solid)
115 {
116         data.solid = solid;
117 }
118
119 QString Hatch::getPattern()
120 {
121         return data.pattern;
122 }
123
124 void Hatch::setPattern(const QString & pattern)
125 {
126         data.pattern = pattern;
127 }
128
129 double Hatch::getScale()
130 {
131         return data.scale;
132 }
133
134 void Hatch::setScale(double scale)
135 {
136         data.scale = scale;
137 }
138
139 double Hatch::getAngle()
140 {
141         return data.angle;
142 }
143
144 void Hatch::setAngle(double angle)
145 {
146         data.angle = angle;
147 }
148
149 /**
150  * Recalculates the borders of this hatch.
151  */
152 void Hatch::calculateBorders()
153 {
154         DEBUG->print("Hatch::calculateBorders");
155
156         activateContour(true);
157         EntityContainer::calculateBorders();
158
159         DEBUG->print("Hatch::calculateBorders: size: %f,%f", getSize().x, getSize().y);
160
161         activateContour(false);
162 }
163
164 /**
165  * Updates the Hatch. Called when the
166  * hatch or it's data, position, alignment, .. changes.
167  */
168 void Hatch::update()
169 {
170         DEBUG->print("Hatch::update");
171         DEBUG->print("Hatch::update: contour has %d loops", count());
172
173         if (updateRunning)
174                 return;
175
176         if (updateEnabled == false)
177                 return;
178
179         if (data.solid == true)
180                 return;
181
182         DEBUG->print("Hatch::update");
183         updateRunning = true;
184
185         // delete old hatch:
186         if (hatch != NULL)
187         {
188                 removeEntity(hatch);
189                 hatch = NULL;
190         }
191
192         if (isUndone())
193         {
194                 updateRunning = false;
195                 return;
196         }
197
198         if (!validate())
199         {
200                 DEBUG->print(Debug::D_WARNING, "Hatch::update: invalid contour in hatch found");
201                 updateRunning = false;
202                 return;
203         }
204
205         // search pattern:
206         DEBUG->print("Hatch::update: requesting pattern");
207         Pattern * pat = PATTERNLIST->requestPattern(data.pattern);
208
209         if (pat == NULL)
210         {
211                 updateRunning = false;
212                 DEBUG->print("Hatch::update: requesting pattern: not found");
213                 return;
214         }
215
216         DEBUG->print("Hatch::update: requesting pattern: OK");
217
218         DEBUG->print("Hatch::update: cloning pattern");
219         pat = (Pattern*)pat->clone();
220         DEBUG->print("Hatch::update: cloning pattern: OK");
221
222         // scale pattern
223         DEBUG->print("Hatch::update: scaling pattern");
224         pat->scale(Vector(0.0,0.0), Vector(data.scale, data.scale));
225         pat->calculateBorders();
226         forcedCalculateBorders();
227         DEBUG->print("Hatch::update: scaling pattern: OK");
228
229         // find out how many pattern-instances we need in x/y:
230         int px1, py1, px2, py2;
231         double f;
232         Hatch * copy = (Hatch *)this->clone();
233         copy->rotate(Vector(0.0, 0.0), -data.angle);
234         copy->forcedCalculateBorders();
235
236         // create a pattern over the whole contour.
237         Vector pSize = pat->getSize();
238         Vector cPos = getMin();
239         Vector cSize = getSize();
240
241         DEBUG->print("Hatch::update: pattern size: %f/%f", pSize.x, pSize.y);
242         DEBUG->print("Hatch::update: contour size: %f/%f", cSize.x, cSize.y);
243
244         if (cSize.x < 1.0e-6 || cSize.y < 1.0e-6 || pSize.x < 1.0e-6 || pSize.y < 1.0e-6
245                 || cSize.x > RS_MAXDOUBLE - 1 || cSize.y > RS_MAXDOUBLE - 1
246                 || pSize.x > RS_MAXDOUBLE - 1 || pSize.y > RS_MAXDOUBLE - 1)
247         {
248                 delete pat;
249                 delete copy;
250                 updateRunning = false;
251                 DEBUG->print("Hatch::update: contour size or pattern size too small");
252                 return;
253         }
254         // avoid huge memory consumption:
255         else if (cSize.x / pSize.x > 100 || cSize.y / pSize.y > 100)
256         {
257                 DEBUG->print("Hatch::update: contour size too large or pattern size too small");
258                 return;
259         }
260
261         f = copy->getMin().x/pat->getSize().x;
262         px1 = (int)floor(f);
263         f = copy->getMin().y/pat->getSize().y;
264         py1 = (int)floor(f);
265         f = copy->getMax().x/pat->getSize().x;
266         px2 = (int)ceil(f) - 1;
267         f = copy->getMax().y/pat->getSize().y;
268         py2 = (int)ceil(f) - 1;
269
270         EntityContainer tmp;   // container for untrimmed lines
271
272         // adding array of patterns to tmp:
273         DEBUG->print("Hatch::update: creating pattern carpet");
274
275         for(int px=px1; px<=px2; px++)
276         {
277                 for(int py=py1; py<=py2; py++)
278                 {
279                         for(Entity * e=pat->firstEntity(); e!=NULL; e=pat->nextEntity())
280                         {
281                                 Entity * te = e->clone();
282                                 te->rotate(Vector(0.0, 0.0), data.angle);
283                                 Vector v1, v2;
284                                 v1.setPolar(px * pSize.x, data.angle);
285                                 v2.setPolar(py * pSize.y, data.angle + M_PI / 2.0);
286                                 te->move(v1 + v2);
287                                 tmp.addEntity(te);
288                         }
289                 }
290         }
291
292         delete pat;
293         pat = NULL;
294         DEBUG->print("Hatch::update: creating pattern carpet: OK");
295
296         DEBUG->print("Hatch::update: cutting pattern carpet");
297         // cut pattern to contour shape:
298         EntityContainer tmp2;   // container for small cut lines
299         Line * line = NULL;
300         Arc * arc = NULL;
301         Circle * circle = NULL;
302
303         for(Entity * e=tmp.firstEntity(); e!=NULL; e=tmp.nextEntity())
304         {
305                 Vector startPoint;
306                 Vector endPoint;
307                 Vector center = Vector(false);
308                 bool reversed;
309
310                 if (e->rtti() == RS2::EntityLine)
311                 {
312                         line = (Line *)e;
313                         arc = NULL;
314                         circle = NULL;
315                         startPoint = line->getStartpoint();
316                         endPoint = line->getEndpoint();
317                         center = Vector(false);
318                         reversed = false;
319                 }
320                 else if (e->rtti() == RS2::EntityArc)
321                 {
322                         arc = (Arc *)e;
323                         line = NULL;
324                         circle = NULL;
325                         startPoint = arc->getStartpoint();
326                         endPoint = arc->getEndpoint();
327                         center = arc->getCenter();
328                         reversed = arc->isReversed();
329                 }
330                 else if (e->rtti() == RS2::EntityCircle)
331                 {
332                         circle = (Circle *)e;
333                         line = NULL;
334                         arc = NULL;
335                         startPoint = circle->getCenter() + Vector(circle->getRadius(), 0.0);
336                         endPoint = startPoint;
337                         center = circle->getCenter();
338                         reversed = false;
339                 }
340                 else
341                 {
342                         continue;
343                 }
344
345                 // getting all intersections of this pattern line with the contour:
346                 QList<Vector *> is;
347 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
348 //        is.setAutoDelete(true);
349                 is.append(new Vector(startPoint));
350
351                 for(Entity * loop=firstEntity(); loop!=NULL; loop=nextEntity())
352                 {
353                         if (loop->isContainer())
354                         {
355                                 for(Entity * p=((EntityContainer *)loop)->firstEntity(); p!=NULL; p=((EntityContainer*)loop)->nextEntity())
356                                 {
357                                         VectorSolutions sol = Information::getIntersection(e, p, true);
358
359                                         for(int i=0; i<=1; ++i)
360                                         {
361                                                 if (sol.get(i).valid)
362                                                 {
363                                                         is.append(new Vector(sol.get(i)));
364                                                         DEBUG->print("  pattern line intersection: %f/%f", sol.get(i).x, sol.get(i).y);
365                                                 }
366                                         }
367                                 }
368                         }
369                 }
370
371                 is.append(new Vector(endPoint));
372
373                 // sort the intersection points into is2:
374                 Vector sp = startPoint;
375                 double sa = center.angleTo(sp);
376 //              Q3PtrList<Vector> is2;
377                 QList<Vector *> is2;
378 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
379 //              is2.setAutoDelete(true);
380                 bool done;
381                 double minDist;
382                 double dist = 0.0;
383                 Vector * av;
384                 Vector last = Vector(false);
385
386                 do
387                 {
388                         done = true;
389                         minDist = RS_MAXDOUBLE;
390                         av = NULL;
391
392 //                      for(Vector * v=is.first(); v!=NULL; v=is.next())
393                         for(int i=0; i<is.size(); i++)
394                         {
395                                 Vector * v = is[i];
396
397                                 if (line != NULL)
398                                 {
399                                         dist = sp.distanceTo(*v);
400                                 }
401                                 else if (arc != NULL || circle != NULL)
402                                 {
403                                         double a = center.angleTo(*v);
404
405                                         if (reversed)
406                                         {
407                                                 if (a > sa)
408                                                 {
409                                                         a -= 2 * M_PI;
410                                                 }
411
412                                                 dist = sa - a;
413                                         }
414                                         else
415                                         {
416                                                 if (a < sa)
417                                                 {
418                                                         a += 2 * M_PI;
419                                                 }
420
421                                                 dist = a - sa;
422                                         }
423
424                                         if (fabs(dist - 2 * M_PI) < 1.0e-6)
425                                         {
426                                                 dist = 0.0;
427                                         }
428                                 }
429
430                                 if (dist < minDist)
431                                 {
432                                         minDist = dist;
433                                         done = false;
434                                         av = v;
435                                         //idx = is.at();
436                                 }
437                         }
438
439                         // copy to sorted list, removing double points
440                         if (!done && av != NULL)
441                         {
442                                 if (last.valid == false || last.distanceTo(*av) > 1.0e-10)
443                                 {
444                                         is2.append(new Vector(*av));
445                                         last = *av;
446                                 }
447
448 //                              is.remove(av);
449                                 int idx = is.indexOf(av);
450
451                                 if (idx != -1)
452                                         delete is.takeAt(idx);
453
454                                 av = NULL;
455                         }
456                 }
457                 while(!done);
458
459                 // add small cut lines / arcs to tmp2:
460 //              for(Vector * v1=is2.first(); v1!=NULL;)
461                 Vector * v1 = is2[0];
462
463                 for(int i=1; i<is2.size(); i++)
464                 {
465 //                      Vector * v2 = is2.next();
466                         Vector * v2 = is2[i];
467
468                         if (v1 != NULL && v2 != NULL)
469                         {
470                                 if (line != NULL)
471                                 {
472                                         tmp2.addEntity(new Line(&tmp2, LineData(*v1, *v2)));
473                                 }
474                                 else if (arc != NULL || circle != NULL)
475                                 {
476                                         tmp2.addEntity(new Arc(&tmp2, ArcData(center, center.distanceTo(*v1),
477                                                 center.angleTo(*v1), center.angleTo(*v2), reversed)));
478                                 }
479                         }
480
481                         v1 = v2;
482                 }
483         }
484
485         // updating hatch / adding entities that are inside
486         DEBUG->print("Hatch::update: cutting pattern carpet: OK");
487
488         // the hatch pattern entities:
489         hatch = new EntityContainer(this);
490         hatch->setPen(Pen(RS2::FlagInvalid));
491         hatch->setLayer(NULL);
492         hatch->setFlag(RS2::FlagTemp);
493
494         //calculateBorders();
495
496         for(Entity * e=tmp2.firstEntity(); e!=NULL; e=tmp2.nextEntity())
497         {
498                 Vector middlePoint;
499                 Vector middlePoint2;
500
501                 if (e->rtti() == RS2::EntityLine)
502                 {
503                         Line * line = (Line *)e;
504                         middlePoint = line->getMiddlepoint();
505                         middlePoint2 = line->getNearestDist(line->getLength() / 2.1, line->getStartpoint());
506                 }
507                 else if (e->rtti() == RS2::EntityArc)
508                 {
509                         Arc * arc = (Arc *)e;
510                         middlePoint = arc->getMiddlepoint();
511                         middlePoint2 = arc->getNearestDist(arc->getLength() / 2.1, arc->getStartpoint());
512                 }
513                 else
514                 {
515                         middlePoint = Vector(false);
516                         middlePoint2 = Vector(false);
517                 }
518
519                 if (middlePoint.valid)
520                 {
521                         bool onContour = false;
522
523                         if (Information::isPointInsideContour(middlePoint, this, &onContour)
524                                 || Information::isPointInsideContour(middlePoint2, this))
525                         {
526                                 Entity * te = e->clone();
527                                 te->setPen(Pen(RS2::FlagInvalid));
528                                 te->setLayer(NULL);
529                                 te->reparent(hatch);
530                                 hatch->addEntity(te);
531                         }
532                 }
533         }
534
535         addEntity(hatch);
536         //getGraphic()->addEntity(rubbish);
537
538         forcedCalculateBorders();
539
540         // deactivate contour:
541         activateContour(false);
542
543         updateRunning = false;
544
545         DEBUG->print("Hatch::update: OK");
546 }
547
548 /**
549  * Activates of deactivates the hatch boundary.
550  */
551 void Hatch::activateContour(bool on)
552 {
553         DEBUG->print("Hatch::activateContour: %d", (int)on);
554
555         for(Entity * e=firstEntity(); e!=NULL; e=nextEntity())
556         {
557                 if (!e->isUndone())
558                 {
559                         if (!e->getFlag(RS2::FlagTemp))
560                         {
561                                 DEBUG->print("Hatch::activateContour: set visible");
562                                 e->setVisible(on);
563                         }
564                         else
565                         {
566                                 DEBUG->print("Hatch::activateContour: entity temp");
567                         }
568                 }
569                 else
570                 {
571                         DEBUG->print("Hatch::activateContour: entity undone");
572                 }
573         }
574
575         DEBUG->print("Hatch::activateContour: OK");
576 }
577
578 /**
579  * Overrides drawing of subentities. This is only ever called for solid fills.
580  */
581 void Hatch::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
582 {
583         if (!data.solid)
584         {
585                 for(Entity * se=firstEntity(); se!=NULL; se = nextEntity())
586                         view->drawEntity(se);
587
588                 return;
589         }
590
591 //      Q3PointArray pa;
592 //      Q3PointArray jp;   // jump points
593         QPolygon pa;
594         QPolygon jp;   // jump points
595         uint s = 0;
596         uint sj = 0;
597         int lastX = 0;
598         int lastY = 0;
599         bool lastValid = false;
600
601         // loops:
602         if (needOptimization == true)
603         {
604                 for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
605                 {
606                         if (l->rtti() == RS2::EntityContainer)
607                         {
608                                 EntityContainer * loop = (EntityContainer *)l;
609                                 loop->optimizeContours();
610                         }
611                 }
612
613                 needOptimization = false;
614         }
615
616         // loops:
617         for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
618         {
619                 l->setLayer(getLayer());
620
621                 if (l->rtti() == RS2::EntityContainer)
622                 {
623                         EntityContainer * loop = (EntityContainer *)l;
624
625                         // edges:
626                         for(Entity * e=loop->firstEntity(RS2::ResolveNone); e!=NULL; e=loop->nextEntity(RS2::ResolveNone))
627                         {
628                                 e->setLayer(getLayer());
629
630                                 switch (e->rtti())
631                                 {
632                                 case RS2::EntityLine:
633                                 {
634                                         Line * line = (Line *)e;
635
636                                         int x1 = Math::round(view->toGuiX(line->getStartpoint().x));
637                                         int y1 = Math::round(view->toGuiY(line->getStartpoint().y));
638                                         int x2 = Math::round(view->toGuiX(line->getEndpoint().x));
639                                         int y2 = Math::round(view->toGuiY(line->getEndpoint().y));
640
641                                         if (lastValid && (lastX != x1 || lastY != y1))
642                                         {
643                                                 jp.resize(++sj);
644                                                 jp.setPoint(sj - 1, x1, y1);
645                                         }
646
647                                         pa.resize(++s);
648                                         pa.setPoint(s - 1, x1, y1);
649
650                                         pa.resize(++s);
651                                         pa.setPoint(s - 1, x2, y2);
652
653                                         lastX = x2;
654                                         lastY = y2;
655                                         lastValid = true;
656                                 }
657                                         break;
658
659                                 case RS2::EntityArc:
660                                 {
661                                         Arc * arc = (Arc *)e;
662
663                                         int x1 = Math::round(view->toGuiX(arc->getStartpoint().x));
664                                         int y1 = Math::round(view->toGuiY(arc->getStartpoint().y));
665                                         int x2 = Math::round(view->toGuiX(arc->getEndpoint().x));
666                                         int y2 = Math::round(view->toGuiY(arc->getEndpoint().y));
667
668                                         if (lastValid && (lastX != x1 || lastY != y1))
669                                         {
670                                                 jp.resize(++sj);
671                                                 jp.setPoint(sj - 1, x1, y1);
672                                         }
673
674                                         pa.resize(++s);
675                                         pa.setPoint(s - 1, x1, y1);
676
677 //                                      Q3PointArray pa2;
678                                         QPolygon pa2;
679                                         painter->createArc(pa2, view->toGui(arc->getCenter()),
680                                                 view->toGuiDX(arc->getRadius()), arc->getAngle1(),
681                                                 arc->getAngle2(), arc->isReversed());
682
683                                         pa.resize(s + pa2.size());
684                                         pa.putPoints(s, pa2.size(), pa2);
685                                         s += pa2.size() - 1;
686
687                                         pa.resize(++s);
688                                         pa.setPoint(s - 1, x2, y2);
689
690                                         lastX = x2;
691                                         lastY = y2;
692                                         lastValid = true;
693                                 }
694                                         break;
695
696                                 case RS2::EntityCircle:
697                                 {
698                                         Circle * circle = (Circle *)e;
699
700                                         int x1 = Math::round(view->toGuiX(circle->getCenter().x + circle->getRadius()));
701                                         int y1 = Math::round(view->toGuiY(circle->getCenter().y));
702                                         int x2 = x1;
703                                         int y2 = y1;
704
705                                         if (lastValid && (lastX != x1 || lastY != y1))
706                                         {
707                                                 jp.resize(++sj);
708                                                 jp.setPoint(sj - 1, x1, y1);
709                                         }
710
711                                         pa.resize(++s);
712                                         pa.setPoint(s - 1, x1, y1);
713
714 //                                      Q3PointArray pa2;
715                                         QPolygon pa2;
716                                         painter->createArc(pa2, view->toGui(circle->getCenter()),
717                                                 view->toGuiDX(circle->getRadius()), 0.0, 2 * M_PI, false);
718
719                                         pa.resize(s + pa2.size());
720                                         pa.putPoints(s, pa2.size(), pa2);
721                                         s += pa2.size() - 1;
722
723                                         pa.resize(++s);
724                                         pa.setPoint(s - 1, x2, y2);
725
726                                         lastX = x2;
727                                         lastY = y2;
728                                         lastValid = true;
729                                 }
730                                         break;
731
732                                 default:
733                                         break;
734                                 }
735                         }
736                 }
737         }
738
739         for(int i=(int)jp.count()-1; i>=0; --i)
740         {
741                 pa.resize(++s);
742                 pa.setPoint(s - 1, jp.point(i));
743         }
744
745         painter->setBrush(painter->getPen().getColor());
746         painter->disablePen();
747         painter->drawPolygon(pa);
748 }
749
750 /*virtual*/ double Hatch::getLength()
751 {
752         return -1.0;
753 }
754
755 double Hatch::getDistanceToPoint(const Vector & coord, Entity ** entity,
756         RS2::ResolveLevel level, double solidDist)
757 {
758         if (data.solid == true)
759         {
760                 if (entity != NULL)
761                         *entity = this;
762
763                 bool onContour;
764
765                 if (Information::isPointInsideContour(coord, this, &onContour))
766                         // distance is the snap range:
767                         return solidDist;
768
769                 return RS_MAXDOUBLE;
770         }
771         else
772         {
773                 return EntityContainer::getDistanceToPoint(coord, entity, level, solidDist);
774         }
775 }
776
777 void Hatch::move(Vector offset)
778 {
779         EntityContainer::move(offset);
780         update();
781 }
782
783 void Hatch::rotate(Vector center, double angle)
784 {
785         EntityContainer::rotate(center, angle);
786         data.angle = Math::correctAngle(data.angle + angle);
787         update();
788 }
789
790 void Hatch::scale(Vector center, Vector factor)
791 {
792         EntityContainer::scale(center, factor);
793         data.scale *= factor.x;
794         update();
795 }
796
797 void Hatch::mirror(Vector axisPoint1, Vector axisPoint2)
798 {
799         EntityContainer::mirror(axisPoint1, axisPoint2);
800         double ang = axisPoint1.angleTo(axisPoint2);
801         data.angle = Math::correctAngle(data.angle + ang*2.0);
802         update();
803 }
804
805 void Hatch::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
806 {
807         EntityContainer::stretch(firstCorner, secondCorner, offset);
808         update();
809 }
810
811 /**
812  * Dumps the point's data to stdout.
813  */
814 std::ostream & operator<<(std::ostream & os, const Hatch & p)
815 {
816         os << " Hatch: " << p.getData() << "\n";
817         return os;
818 }