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
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 05/28/2010 Added this text. :-)
18 #include "rs_graphicview.h"
20 #include "rs_information.h"
21 #include "rs_pattern.h"
22 #include "rs_patternlist.h"
23 #include "paintintf.h"
28 RS_Hatch::RS_Hatch(RS_EntityContainer * parent, const RS_HatchData & d):
29 RS_EntityContainer(parent), data(d)
32 updateRunning = false;
33 needOptimization = true;
36 /*virtual*/ RS_Hatch::~RS_Hatch()
40 RS_Entity * RS_Hatch::clone()
42 RS_Hatch * t = new RS_Hatch(*this);
43 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
44 // t->entities.setAutoDelete(entities.autoDelete());
51 /** @return RS2::EntityHatch */
52 /*virtual*/ RS2::EntityType RS_Hatch::rtti() const
54 return RS2::EntityHatch;
58 * @return true: if this is a hatch with lines (hatch pattern),
59 * false: if this is filled with a solid color.
61 /*virtual*/ bool RS_Hatch::isContainer() const
69 /** @return Copy of data that defines the hatch. */
70 RS_HatchData RS_Hatch::getData() const
76 * Validates the hatch.
78 bool RS_Hatch::validate()
83 for(RS_Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
85 if (l->rtti() == RS2::EntityContainer)
87 RS_EntityContainer * loop = (RS_EntityContainer *)l;
88 ret = loop->optimizeContours() && ret;
96 * @return Number of loops.
98 int RS_Hatch::countLoops()
106 /** @return true if this is a solid fill. false if it is a pattern hatch. */
107 bool RS_Hatch::isSolid() const
112 void RS_Hatch::setSolid(bool solid)
117 QString RS_Hatch::getPattern()
122 void RS_Hatch::setPattern(const QString & pattern)
124 data.pattern = pattern;
127 double RS_Hatch::getScale()
132 void RS_Hatch::setScale(double scale)
137 double RS_Hatch::getAngle()
142 void RS_Hatch::setAngle(double angle)
148 * Recalculates the borders of this hatch.
150 void RS_Hatch::calculateBorders()
152 RS_DEBUG->print("RS_Hatch::calculateBorders");
154 activateContour(true);
155 RS_EntityContainer::calculateBorders();
157 RS_DEBUG->print("RS_Hatch::calculateBorders: size: %f,%f", getSize().x, getSize().y);
159 activateContour(false);
163 * Updates the Hatch. Called when the
164 * hatch or it's data, position, alignment, .. changes.
166 void RS_Hatch::update()
168 RS_DEBUG->print("RS_Hatch::update");
169 RS_DEBUG->print("RS_Hatch::update: contour has %d loops", count());
174 if (updateEnabled == false)
177 if (data.solid == true)
180 RS_DEBUG->print("RS_Hatch::update");
181 updateRunning = true;
192 updateRunning = false;
198 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Hatch::update: invalid contour in hatch found");
199 updateRunning = false;
204 RS_DEBUG->print("RS_Hatch::update: requesting pattern");
205 RS_Pattern * pat = RS_PATTERNLIST->requestPattern(data.pattern);
209 updateRunning = false;
210 RS_DEBUG->print("RS_Hatch::update: requesting pattern: not found");
214 RS_DEBUG->print("RS_Hatch::update: requesting pattern: OK");
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");
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");
227 // find out how many pattern-instances we need in x/y:
228 int px1, py1, px2, py2;
230 RS_Hatch * copy = (RS_Hatch *)this->clone();
231 copy->rotate(Vector(0.0, 0.0), -data.angle);
232 copy->forcedCalculateBorders();
234 // create a pattern over the whole contour.
235 Vector pSize = pat->getSize();
236 Vector cPos = getMin();
237 Vector cSize = getSize();
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);
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)
248 updateRunning = false;
249 RS_DEBUG->print("RS_Hatch::update: contour size or pattern size too small");
252 // avoid huge memory consumption:
253 else if (cSize.x / pSize.x > 100 || cSize.y / pSize.y > 100)
255 RS_DEBUG->print("RS_Hatch::update: contour size too large or pattern size too small");
259 f = copy->getMin().x/pat->getSize().x;
261 f = copy->getMin().y/pat->getSize().y;
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;
268 RS_EntityContainer tmp; // container for untrimmed lines
270 // adding array of patterns to tmp:
271 RS_DEBUG->print("RS_Hatch::update: creating pattern carpet");
273 for(int px=px1; px<=px2; px++)
275 for(int py=py1; py<=py2; py++)
277 for(RS_Entity * e=pat->firstEntity(); e!=NULL; e=pat->nextEntity())
279 RS_Entity * te = e->clone();
280 te->rotate(Vector(0.0, 0.0), data.angle);
282 v1.setPolar(px * pSize.x, data.angle);
283 v2.setPolar(py * pSize.y, data.angle + M_PI / 2.0);
292 RS_DEBUG->print("RS_Hatch::update: creating pattern carpet: OK");
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;
299 RS_Circle * circle = NULL;
301 for(RS_Entity * e=tmp.firstEntity(); e!=NULL; e=tmp.nextEntity())
305 Vector center = Vector(false);
308 if (e->rtti() == RS2::EntityLine)
313 startPoint = line->getStartpoint();
314 endPoint = line->getEndpoint();
315 center = Vector(false);
318 else if (e->rtti() == RS2::EntityArc)
323 startPoint = arc->getStartpoint();
324 endPoint = arc->getEndpoint();
325 center = arc->getCenter();
326 reversed = arc->isReversed();
328 else if (e->rtti() == RS2::EntityCircle)
330 circle = (RS_Circle *)e;
333 startPoint = circle->getCenter() + Vector(circle->getRadius(), 0.0);
334 endPoint = startPoint;
335 center = circle->getCenter();
343 // getting all intersections of this pattern line with the contour:
345 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
346 // is.setAutoDelete(true);
347 is.append(new Vector(startPoint));
349 for(RS_Entity * loop=firstEntity(); loop!=NULL; loop=nextEntity())
351 if (loop->isContainer())
353 for(RS_Entity * p=((RS_EntityContainer *)loop)->firstEntity(); p!=NULL; p=((RS_EntityContainer*)loop)->nextEntity())
355 VectorSolutions sol = RS_Information::getIntersection(e, p, true);
357 for(int i=0; i<=1; ++i)
359 if (sol.get(i).valid)
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);
369 is.append(new Vector(endPoint));
371 // sort the intersection points into is2:
372 Vector sp = startPoint;
373 double sa = center.angleTo(sp);
374 // Q3PtrList<Vector> is2;
376 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
377 // is2.setAutoDelete(true);
382 Vector last = Vector(false);
387 minDist = RS_MAXDOUBLE;
390 // for(Vector * v=is.first(); v!=NULL; v=is.next())
391 for(int i=0; i<is.size(); i++)
397 dist = sp.distanceTo(*v);
399 else if (arc != NULL || circle != NULL)
401 double a = center.angleTo(*v);
422 if (fabs(dist - 2 * M_PI) < 1.0e-6)
437 // copy to sorted list, removing double points
438 if (!done && av != NULL)
440 if (last.valid == false || last.distanceTo(*av) > 1.0e-10)
442 is2.append(new Vector(*av));
447 int idx = is.indexOf(av);
450 delete is.takeAt(idx);
457 // add small cut lines / arcs to tmp2:
458 // for(Vector * v1=is2.first(); v1!=NULL;)
459 Vector * v1 = is2[0];
461 for(int i=1; i<is2.size(); i++)
463 // Vector * v2 = is2.next();
464 Vector * v2 = is2[i];
466 if (v1 != NULL && v2 != NULL)
470 tmp2.addEntity(new RS_Line(&tmp2, RS_LineData(*v1, *v2)));
472 else if (arc != NULL || circle != NULL)
474 tmp2.addEntity(new RS_Arc(&tmp2, RS_ArcData(center, center.distanceTo(*v1),
475 center.angleTo(*v1), center.angleTo(*v2), reversed)));
483 // updating hatch / adding entities that are inside
484 RS_DEBUG->print("RS_Hatch::update: cutting pattern carpet: OK");
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);
492 //calculateBorders();
494 for(RS_Entity * e=tmp2.firstEntity(); e!=NULL; e=tmp2.nextEntity())
499 if (e->rtti() == RS2::EntityLine)
501 RS_Line * line = (RS_Line *)e;
502 middlePoint = line->getMiddlepoint();
503 middlePoint2 = line->getNearestDist(line->getLength() / 2.1, line->getStartpoint());
505 else if (e->rtti() == RS2::EntityArc)
507 RS_Arc * arc = (RS_Arc *)e;
508 middlePoint = arc->getMiddlepoint();
509 middlePoint2 = arc->getNearestDist(arc->getLength() / 2.1, arc->getStartpoint());
513 middlePoint = Vector(false);
514 middlePoint2 = Vector(false);
517 if (middlePoint.valid)
519 bool onContour = false;
521 if (RS_Information::isPointInsideContour(middlePoint, this, &onContour)
522 || RS_Information::isPointInsideContour(middlePoint2, this))
524 RS_Entity * te = e->clone();
525 te->setPen(RS_Pen(RS2::FlagInvalid));
528 hatch->addEntity(te);
534 //getGraphic()->addEntity(rubbish);
536 forcedCalculateBorders();
538 // deactivate contour:
539 activateContour(false);
541 updateRunning = false;
543 RS_DEBUG->print("RS_Hatch::update: OK");
547 * Activates of deactivates the hatch boundary.
549 void RS_Hatch::activateContour(bool on)
551 RS_DEBUG->print("RS_Hatch::activateContour: %d", (int)on);
553 for(RS_Entity * e=firstEntity(); e!=NULL; e=nextEntity())
557 if (!e->getFlag(RS2::FlagTemp))
559 RS_DEBUG->print("RS_Hatch::activateContour: set visible");
564 RS_DEBUG->print("RS_Hatch::activateContour: entity temp");
569 RS_DEBUG->print("RS_Hatch::activateContour: entity undone");
573 RS_DEBUG->print("RS_Hatch::activateContour: OK");
577 * Overrides drawing of subentities. This is only ever called for solid fills.
579 //void RS_Hatch::draw(RS_Painter* painter, RS_GraphicView* view,
580 void RS_Hatch::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
584 for(RS_Entity * se=firstEntity(); se!=NULL; se = nextEntity())
585 view->drawEntity(se);
591 // Q3PointArray jp; // jump points
593 QPolygon jp; // jump points
598 bool lastValid = false;
601 if (needOptimization == true)
603 for(RS_Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
605 if (l->rtti() == RS2::EntityContainer)
607 RS_EntityContainer * loop = (RS_EntityContainer *)l;
608 loop->optimizeContours();
612 needOptimization = false;
616 for(RS_Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
618 l->setLayer(getLayer());
620 if (l->rtti() == RS2::EntityContainer)
622 RS_EntityContainer * loop = (RS_EntityContainer *)l;
625 for(RS_Entity * e=loop->firstEntity(RS2::ResolveNone); e!=NULL; e=loop->nextEntity(RS2::ResolveNone))
627 e->setLayer(getLayer());
631 case RS2::EntityLine:
633 RS_Line * line = (RS_Line *)e;
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));
640 if (lastValid && (lastX != x1 || lastY != y1))
643 jp.setPoint(sj - 1, x1, y1);
647 pa.setPoint(s - 1, x1, y1);
650 pa.setPoint(s - 1, x2, y2);
660 RS_Arc * arc = (RS_Arc *)e;
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));
667 if (lastValid && (lastX != x1 || lastY != y1))
670 jp.setPoint(sj - 1, x1, y1);
674 pa.setPoint(s - 1, x1, y1);
678 painter->createArc(pa2, view->toGui(arc->getCenter()),
679 view->toGuiDX(arc->getRadius()), arc->getAngle1(),
680 arc->getAngle2(), arc->isReversed());
682 pa.resize(s + pa2.size());
683 pa.putPoints(s, pa2.size(), pa2);
687 pa.setPoint(s - 1, x2, y2);
695 case RS2::EntityCircle:
697 RS_Circle * circle = (RS_Circle *)e;
699 int x1 = RS_Math::round(view->toGuiX(circle->getCenter().x + circle->getRadius()));
700 int y1 = RS_Math::round(view->toGuiY(circle->getCenter().y));
704 if (lastValid && (lastX != x1 || lastY != y1))
707 jp.setPoint(sj - 1, x1, y1);
711 pa.setPoint(s - 1, x1, y1);
715 painter->createArc(pa2, view->toGui(circle->getCenter()),
716 view->toGuiDX(circle->getRadius()), 0.0, 2 * M_PI, false);
718 pa.resize(s + pa2.size());
719 pa.putPoints(s, pa2.size(), pa2);
723 pa.setPoint(s - 1, x2, y2);
738 for(int i=(int)jp.count()-1; i>=0; --i)
741 pa.setPoint(s - 1, jp.point(i));
744 painter->setBrush(painter->getPen().getColor());
745 painter->disablePen();
746 painter->drawPolygon(pa);
749 /*virtual*/ double RS_Hatch::getLength()
754 double RS_Hatch::getDistanceToPoint(const Vector & coord, RS_Entity ** entity,
755 RS2::ResolveLevel level, double solidDist)
757 if (data.solid == true)
764 if (RS_Information::isPointInsideContour(coord, this, &onContour))
765 // distance is the snap range:
772 return RS_EntityContainer::getDistanceToPoint(coord, entity, level, solidDist);
776 void RS_Hatch::move(Vector offset)
778 RS_EntityContainer::move(offset);
782 void RS_Hatch::rotate(Vector center, double angle)
784 RS_EntityContainer::rotate(center, angle);
785 data.angle = RS_Math::correctAngle(data.angle + angle);
789 void RS_Hatch::scale(Vector center, Vector factor)
791 RS_EntityContainer::scale(center, factor);
792 data.scale *= factor.x;
796 void RS_Hatch::mirror(Vector axisPoint1, Vector axisPoint2)
798 RS_EntityContainer::mirror(axisPoint1, axisPoint2);
799 double ang = axisPoint1.angleTo(axisPoint2);
800 data.angle = RS_Math::correctAngle(data.angle + ang*2.0);
804 void RS_Hatch::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
806 RS_EntityContainer::stretch(firstCorner, secondCorner, offset);
811 * Dumps the point's data to stdout.
813 std::ostream & operator<<(std::ostream & os, const RS_Hatch & p)
815 os << " Hatch: " << p.getData() << "\n";