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