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
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 05/28/2010 Added this text. :-)
21 #include "graphicview.h"
22 #include "information.h"
23 #include "paintinterface.h"
25 #include "patternlist.h"
30 Hatch::Hatch(EntityContainer * parent, const HatchData & d):
31 EntityContainer(parent), data(d)
34 updateRunning = false;
35 needOptimization = true;
38 /*virtual*/ Hatch::~Hatch()
42 Entity * Hatch::clone()
44 Hatch * t = new Hatch(*this);
45 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
46 // t->entities.setAutoDelete(entities.autoDelete());
53 /** @return RS2::EntityHatch */
54 /*virtual*/ RS2::EntityType Hatch::rtti() const
56 return RS2::EntityHatch;
60 * @return true: if this is a hatch with lines (hatch pattern),
61 * false: if this is filled with a solid color.
63 /*virtual*/ bool Hatch::isContainer() const
71 /** @return Copy of data that defines the hatch. */
72 HatchData Hatch::getData() const
78 * Validates the hatch.
80 bool Hatch::validate()
85 for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
87 if (l->rtti() == RS2::EntityContainer)
89 EntityContainer * loop = (EntityContainer *)l;
90 ret = loop->optimizeContours() && ret;
98 * @return Number of loops.
100 int Hatch::countLoops()
108 /** @return true if this is a solid fill. false if it is a pattern hatch. */
109 bool Hatch::isSolid() const
114 void Hatch::setSolid(bool solid)
119 QString Hatch::getPattern()
124 void Hatch::setPattern(const QString & pattern)
126 data.pattern = pattern;
129 double Hatch::getScale()
134 void Hatch::setScale(double scale)
139 double Hatch::getAngle()
144 void Hatch::setAngle(double angle)
150 * Recalculates the borders of this hatch.
152 void Hatch::calculateBorders()
154 DEBUG->print("Hatch::calculateBorders");
156 activateContour(true);
157 EntityContainer::calculateBorders();
159 DEBUG->print("Hatch::calculateBorders: size: %f,%f", getSize().x, getSize().y);
161 activateContour(false);
165 * Updates the Hatch. Called when the
166 * hatch or it's data, position, alignment, .. changes.
170 DEBUG->print("Hatch::update");
171 DEBUG->print("Hatch::update: contour has %d loops", count());
176 if (updateEnabled == false)
179 if (data.solid == true)
182 DEBUG->print("Hatch::update");
183 updateRunning = true;
194 updateRunning = false;
200 DEBUG->print(Debug::D_WARNING, "Hatch::update: invalid contour in hatch found");
201 updateRunning = false;
206 DEBUG->print("Hatch::update: requesting pattern");
207 Pattern * pat = PATTERNLIST->requestPattern(data.pattern);
211 updateRunning = false;
212 DEBUG->print("Hatch::update: requesting pattern: not found");
216 DEBUG->print("Hatch::update: requesting pattern: OK");
218 DEBUG->print("Hatch::update: cloning pattern");
219 pat = (Pattern*)pat->clone();
220 DEBUG->print("Hatch::update: cloning pattern: OK");
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");
229 // find out how many pattern-instances we need in x/y:
230 int px1, py1, px2, py2;
232 Hatch * copy = (Hatch *)this->clone();
233 copy->rotate(Vector(0.0, 0.0), -data.angle);
234 copy->forcedCalculateBorders();
236 // create a pattern over the whole contour.
237 Vector pSize = pat->getSize();
238 Vector cPos = getMin();
239 Vector cSize = getSize();
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);
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)
250 updateRunning = false;
251 DEBUG->print("Hatch::update: contour size or pattern size too small");
254 // avoid huge memory consumption:
255 else if (cSize.x / pSize.x > 100 || cSize.y / pSize.y > 100)
257 DEBUG->print("Hatch::update: contour size too large or pattern size too small");
261 f = copy->getMin().x/pat->getSize().x;
263 f = copy->getMin().y/pat->getSize().y;
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;
270 EntityContainer tmp; // container for untrimmed lines
272 // adding array of patterns to tmp:
273 DEBUG->print("Hatch::update: creating pattern carpet");
275 for(int px=px1; px<=px2; px++)
277 for(int py=py1; py<=py2; py++)
279 for(Entity * e=pat->firstEntity(); e!=NULL; e=pat->nextEntity())
281 Entity * te = e->clone();
282 te->rotate(Vector(0.0, 0.0), data.angle);
284 v1.setPolar(px * pSize.x, data.angle);
285 v2.setPolar(py * pSize.y, data.angle + M_PI / 2.0);
294 DEBUG->print("Hatch::update: creating pattern carpet: OK");
296 DEBUG->print("Hatch::update: cutting pattern carpet");
297 // cut pattern to contour shape:
298 EntityContainer tmp2; // container for small cut lines
301 Circle * circle = NULL;
303 for(Entity * e=tmp.firstEntity(); e!=NULL; e=tmp.nextEntity())
307 Vector center = Vector(false);
310 if (e->rtti() == RS2::EntityLine)
315 startPoint = line->getStartpoint();
316 endPoint = line->getEndpoint();
317 center = Vector(false);
320 else if (e->rtti() == RS2::EntityArc)
325 startPoint = arc->getStartpoint();
326 endPoint = arc->getEndpoint();
327 center = arc->getCenter();
328 reversed = arc->isReversed();
330 else if (e->rtti() == RS2::EntityCircle)
332 circle = (Circle *)e;
335 startPoint = circle->getCenter() + Vector(circle->getRadius(), 0.0);
336 endPoint = startPoint;
337 center = circle->getCenter();
345 // getting all intersections of this pattern line with the contour:
347 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
348 // is.setAutoDelete(true);
349 is.append(new Vector(startPoint));
351 for(Entity * loop=firstEntity(); loop!=NULL; loop=nextEntity())
353 if (loop->isContainer())
355 for(Entity * p=((EntityContainer *)loop)->firstEntity(); p!=NULL; p=((EntityContainer*)loop)->nextEntity())
357 VectorSolutions sol = Information::getIntersection(e, p, true);
359 for(int i=0; i<=1; ++i)
361 if (sol.get(i).valid)
363 is.append(new Vector(sol.get(i)));
364 DEBUG->print(" pattern line intersection: %f/%f", sol.get(i).x, sol.get(i).y);
371 is.append(new Vector(endPoint));
373 // sort the intersection points into is2:
374 Vector sp = startPoint;
375 double sa = center.angleTo(sp);
376 // Q3PtrList<Vector> is2;
378 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
379 // is2.setAutoDelete(true);
384 Vector last = Vector(false);
389 minDist = RS_MAXDOUBLE;
392 // for(Vector * v=is.first(); v!=NULL; v=is.next())
393 for(int i=0; i<is.size(); i++)
399 dist = sp.distanceTo(*v);
401 else if (arc != NULL || circle != NULL)
403 double a = center.angleTo(*v);
424 if (fabs(dist - 2 * M_PI) < 1.0e-6)
439 // copy to sorted list, removing double points
440 if (!done && av != NULL)
442 if (last.valid == false || last.distanceTo(*av) > 1.0e-10)
444 is2.append(new Vector(*av));
449 int idx = is.indexOf(av);
452 delete is.takeAt(idx);
459 // add small cut lines / arcs to tmp2:
460 // for(Vector * v1=is2.first(); v1!=NULL;)
461 Vector * v1 = is2[0];
463 for(int i=1; i<is2.size(); i++)
465 // Vector * v2 = is2.next();
466 Vector * v2 = is2[i];
468 if (v1 != NULL && v2 != NULL)
472 tmp2.addEntity(new Line(&tmp2, LineData(*v1, *v2)));
474 else if (arc != NULL || circle != NULL)
476 tmp2.addEntity(new Arc(&tmp2, ArcData(center, center.distanceTo(*v1),
477 center.angleTo(*v1), center.angleTo(*v2), reversed)));
485 // updating hatch / adding entities that are inside
486 DEBUG->print("Hatch::update: cutting pattern carpet: OK");
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);
494 //calculateBorders();
496 for(Entity * e=tmp2.firstEntity(); e!=NULL; e=tmp2.nextEntity())
501 if (e->rtti() == RS2::EntityLine)
503 Line * line = (Line *)e;
504 middlePoint = line->getMiddlepoint();
505 middlePoint2 = line->getNearestDist(line->getLength() / 2.1, line->getStartpoint());
507 else if (e->rtti() == RS2::EntityArc)
509 Arc * arc = (Arc *)e;
510 middlePoint = arc->getMiddlepoint();
511 middlePoint2 = arc->getNearestDist(arc->getLength() / 2.1, arc->getStartpoint());
515 middlePoint = Vector(false);
516 middlePoint2 = Vector(false);
519 if (middlePoint.valid)
521 bool onContour = false;
523 if (Information::isPointInsideContour(middlePoint, this, &onContour)
524 || Information::isPointInsideContour(middlePoint2, this))
526 Entity * te = e->clone();
527 te->setPen(Pen(RS2::FlagInvalid));
530 hatch->addEntity(te);
536 //getGraphic()->addEntity(rubbish);
538 forcedCalculateBorders();
540 // deactivate contour:
541 activateContour(false);
543 updateRunning = false;
545 DEBUG->print("Hatch::update: OK");
549 * Activates of deactivates the hatch boundary.
551 void Hatch::activateContour(bool on)
553 DEBUG->print("Hatch::activateContour: %d", (int)on);
555 for(Entity * e=firstEntity(); e!=NULL; e=nextEntity())
559 if (!e->getFlag(RS2::FlagTemp))
561 DEBUG->print("Hatch::activateContour: set visible");
566 DEBUG->print("Hatch::activateContour: entity temp");
571 DEBUG->print("Hatch::activateContour: entity undone");
575 DEBUG->print("Hatch::activateContour: OK");
579 * Overrides drawing of subentities. This is only ever called for solid fills.
581 void Hatch::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
585 for(Entity * se=firstEntity(); se!=NULL; se = nextEntity())
586 view->drawEntity(se);
592 // Q3PointArray jp; // jump points
594 QPolygon jp; // jump points
599 bool lastValid = false;
602 if (needOptimization == true)
604 for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
606 if (l->rtti() == RS2::EntityContainer)
608 EntityContainer * loop = (EntityContainer *)l;
609 loop->optimizeContours();
613 needOptimization = false;
617 for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
619 l->setLayer(getLayer());
621 if (l->rtti() == RS2::EntityContainer)
623 EntityContainer * loop = (EntityContainer *)l;
626 for(Entity * e=loop->firstEntity(RS2::ResolveNone); e!=NULL; e=loop->nextEntity(RS2::ResolveNone))
628 e->setLayer(getLayer());
632 case RS2::EntityLine:
634 Line * line = (Line *)e;
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));
641 if (lastValid && (lastX != x1 || lastY != y1))
644 jp.setPoint(sj - 1, x1, y1);
648 pa.setPoint(s - 1, x1, y1);
651 pa.setPoint(s - 1, x2, y2);
661 Arc * arc = (Arc *)e;
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));
668 if (lastValid && (lastX != x1 || lastY != y1))
671 jp.setPoint(sj - 1, x1, y1);
675 pa.setPoint(s - 1, x1, y1);
679 painter->createArc(pa2, view->toGui(arc->getCenter()),
680 view->toGuiDX(arc->getRadius()), arc->getAngle1(),
681 arc->getAngle2(), arc->isReversed());
683 pa.resize(s + pa2.size());
684 pa.putPoints(s, pa2.size(), pa2);
688 pa.setPoint(s - 1, x2, y2);
696 case RS2::EntityCircle:
698 Circle * circle = (Circle *)e;
700 int x1 = Math::round(view->toGuiX(circle->getCenter().x + circle->getRadius()));
701 int y1 = Math::round(view->toGuiY(circle->getCenter().y));
705 if (lastValid && (lastX != x1 || lastY != y1))
708 jp.setPoint(sj - 1, x1, y1);
712 pa.setPoint(s - 1, x1, y1);
716 painter->createArc(pa2, view->toGui(circle->getCenter()),
717 view->toGuiDX(circle->getRadius()), 0.0, 2 * M_PI, false);
719 pa.resize(s + pa2.size());
720 pa.putPoints(s, pa2.size(), pa2);
724 pa.setPoint(s - 1, x2, y2);
739 for(int i=(int)jp.count()-1; i>=0; --i)
742 pa.setPoint(s - 1, jp.point(i));
745 painter->setBrush(painter->getPen().getColor());
746 painter->disablePen();
747 painter->drawPolygon(pa);
750 /*virtual*/ double Hatch::getLength()
755 double Hatch::getDistanceToPoint(const Vector & coord, Entity ** entity,
756 RS2::ResolveLevel level, double solidDist)
758 if (data.solid == true)
765 if (Information::isPointInsideContour(coord, this, &onContour))
766 // distance is the snap range:
773 return EntityContainer::getDistanceToPoint(coord, entity, level, solidDist);
777 void Hatch::move(Vector offset)
779 EntityContainer::move(offset);
783 void Hatch::rotate(Vector center, double angle)
785 EntityContainer::rotate(center, angle);
786 data.angle = Math::correctAngle(data.angle + angle);
790 void Hatch::scale(Vector center, Vector factor)
792 EntityContainer::scale(center, factor);
793 data.scale *= factor.x;
797 void Hatch::mirror(Vector axisPoint1, Vector axisPoint2)
799 EntityContainer::mirror(axisPoint1, axisPoint2);
800 double ang = axisPoint1.angleTo(axisPoint2);
801 data.angle = Math::correctAngle(data.angle + ang*2.0);
805 void Hatch::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
807 EntityContainer::stretch(firstCorner, secondCorner, offset);
812 * Dumps the point's data to stdout.
814 std::ostream & operator<<(std::ostream & os, const Hatch & p)
816 os << " Hatch: " << p.getData() << "\n";