+++ /dev/null
-// hatch.cpp
-//
-// Part of the Architektonas Project
-// Originally part of QCad Community Edition by Andrew Mustun
-// Extensively rewritten and refactored by James L. Hammons
-// Portions copyright (C) 2001-2003 RibbonSoft
-// Copyright (C) 2010 Underground Software
-// See the README and GPLv2 files for licensing and warranty information
-//
-// JLH = James L. Hammons <jlhamm@acm.org>
-//
-// Who When What
-// --- ---------- -----------------------------------------------------------
-// JLH 05/28/2010 Added this text. :-)
-//
-
-#include "hatch.h"
-
-#include <QtCore>
-#include "drawing.h"
-#include "graphicview.h"
-#include "information.h"
-#include "paintinterface.h"
-#include "pattern.h"
-#include "patternlist.h"
-
-/**
- * Constructor.
- */
-Hatch::Hatch(EntityContainer * parent, const HatchData & d):
- EntityContainer(parent), data(d)
-{
- hatch = NULL;
- updateRunning = false;
- needOptimization = true;
-}
-
-/*virtual*/ Hatch::~Hatch()
-{
-}
-
-Entity * Hatch::clone()
-{
- Hatch * t = new Hatch(*this);
-#warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
-// t->entities.setAutoDelete(entities.autoDelete());
- t->initId();
- t->detach();
- t->hatch = NULL;
- return t;
-}
-
-/** @return RS2::EntityHatch */
-/*virtual*/ RS2::EntityType Hatch::rtti() const
-{
- return RS2::EntityHatch;
-}
-
-/**
- * @return true: if this is a hatch with lines (hatch pattern),
- * false: if this is filled with a solid color.
- */
-/*virtual*/ bool Hatch::isContainer() const
-{
- if (isSolid())
- return false;
-
- return true;
-}
-
-/** @return Copy of data that defines the hatch. */
-HatchData Hatch::getData() const
-{
- return data;
-}
-
-/**
- * Validates the hatch.
- */
-bool Hatch::validate()
-{
- bool ret = true;
-
- // loops:
- for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
- {
- if (l->rtti() == RS2::EntityContainer)
- {
- EntityContainer * loop = (EntityContainer *)l;
- ret = loop->optimizeContours() && ret;
- }
- }
-
- return ret;
-}
-
-/**
- * @return Number of loops.
- */
-int Hatch::countLoops()
-{
- if (data.solid)
- return count();
- else
- return count() - 1;
-}
-
-/** @return true if this is a solid fill. false if it is a pattern hatch. */
-bool Hatch::isSolid() const
-{
- return data.solid;
-}
-
-void Hatch::setSolid(bool solid)
-{
- data.solid = solid;
-}
-
-QString Hatch::getPattern()
-{
- return data.pattern;
-}
-
-void Hatch::setPattern(const QString & pattern)
-{
- data.pattern = pattern;
-}
-
-double Hatch::getScale()
-{
- return data.scale;
-}
-
-void Hatch::setScale(double scale)
-{
- data.scale = scale;
-}
-
-double Hatch::getAngle()
-{
- return data.angle;
-}
-
-void Hatch::setAngle(double angle)
-{
- data.angle = angle;
-}
-
-/**
- * Recalculates the borders of this hatch.
- */
-void Hatch::calculateBorders()
-{
- DEBUG->print("Hatch::calculateBorders");
-
- activateContour(true);
- EntityContainer::calculateBorders();
-
- DEBUG->print("Hatch::calculateBorders: size: %f,%f", getSize().x, getSize().y);
-
- activateContour(false);
-}
-
-/**
- * Updates the Hatch. Called when the
- * hatch or it's data, position, alignment, .. changes.
- */
-void Hatch::update()
-{
- DEBUG->print("Hatch::update");
- DEBUG->print("Hatch::update: contour has %d loops", count());
-
- if (updateRunning)
- return;
-
- if (updateEnabled == false)
- return;
-
- if (data.solid == true)
- return;
-
- DEBUG->print("Hatch::update");
- updateRunning = true;
-
- // delete old hatch:
- if (hatch != NULL)
- {
- removeEntity(hatch);
- hatch = NULL;
- }
-
- if (isUndone())
- {
- updateRunning = false;
- return;
- }
-
- if (!validate())
- {
- DEBUG->print(Debug::D_WARNING, "Hatch::update: invalid contour in hatch found");
- updateRunning = false;
- return;
- }
-
- // search pattern:
- DEBUG->print("Hatch::update: requesting pattern");
- Pattern * pat = PATTERNLIST->requestPattern(data.pattern);
-
- if (pat == NULL)
- {
- updateRunning = false;
- DEBUG->print("Hatch::update: requesting pattern: not found");
- return;
- }
-
- DEBUG->print("Hatch::update: requesting pattern: OK");
-
- DEBUG->print("Hatch::update: cloning pattern");
- pat = (Pattern*)pat->clone();
- DEBUG->print("Hatch::update: cloning pattern: OK");
-
- // scale pattern
- DEBUG->print("Hatch::update: scaling pattern");
- pat->scale(Vector(0.0,0.0), Vector(data.scale, data.scale));
- pat->calculateBorders();
- forcedCalculateBorders();
- DEBUG->print("Hatch::update: scaling pattern: OK");
-
- // find out how many pattern-instances we need in x/y:
- int px1, py1, px2, py2;
- double f;
- Hatch * copy = (Hatch *)this->clone();
- copy->rotate(Vector(0.0, 0.0), -data.angle);
- copy->forcedCalculateBorders();
-
- // create a pattern over the whole contour.
- Vector pSize = pat->getSize();
- Vector cPos = getMin();
- Vector cSize = getSize();
-
- DEBUG->print("Hatch::update: pattern size: %f/%f", pSize.x, pSize.y);
- DEBUG->print("Hatch::update: contour size: %f/%f", cSize.x, cSize.y);
-
- if (cSize.x < 1.0e-6 || cSize.y < 1.0e-6 || pSize.x < 1.0e-6 || pSize.y < 1.0e-6
- || cSize.x > RS_MAXDOUBLE - 1 || cSize.y > RS_MAXDOUBLE - 1
- || pSize.x > RS_MAXDOUBLE - 1 || pSize.y > RS_MAXDOUBLE - 1)
- {
- delete pat;
- delete copy;
- updateRunning = false;
- DEBUG->print("Hatch::update: contour size or pattern size too small");
- return;
- }
- // avoid huge memory consumption:
- else if (cSize.x / pSize.x > 100 || cSize.y / pSize.y > 100)
- {
- DEBUG->print("Hatch::update: contour size too large or pattern size too small");
- return;
- }
-
- f = copy->getMin().x/pat->getSize().x;
- px1 = (int)floor(f);
- f = copy->getMin().y/pat->getSize().y;
- py1 = (int)floor(f);
- f = copy->getMax().x/pat->getSize().x;
- px2 = (int)ceil(f) - 1;
- f = copy->getMax().y/pat->getSize().y;
- py2 = (int)ceil(f) - 1;
-
- EntityContainer tmp; // container for untrimmed lines
-
- // adding array of patterns to tmp:
- DEBUG->print("Hatch::update: creating pattern carpet");
-
- for(int px=px1; px<=px2; px++)
- {
- for(int py=py1; py<=py2; py++)
- {
- for(Entity * e=pat->firstEntity(); e!=NULL; e=pat->nextEntity())
- {
- Entity * te = e->clone();
- te->rotate(Vector(0.0, 0.0), data.angle);
- Vector v1, v2;
- v1.setPolar(px * pSize.x, data.angle);
- v2.setPolar(py * pSize.y, data.angle + M_PI / 2.0);
- te->move(v1 + v2);
- tmp.addEntity(te);
- }
- }
- }
-
- delete pat;
- pat = NULL;
- DEBUG->print("Hatch::update: creating pattern carpet: OK");
-
- DEBUG->print("Hatch::update: cutting pattern carpet");
- // cut pattern to contour shape:
- EntityContainer tmp2; // container for small cut lines
- Line * line = NULL;
- Arc * arc = NULL;
- Circle * circle = NULL;
-
- for(Entity * e=tmp.firstEntity(); e!=NULL; e=tmp.nextEntity())
- {
- Vector startPoint;
- Vector endPoint;
- Vector center = Vector(false);
- bool reversed;
-
- if (e->rtti() == RS2::EntityLine)
- {
- line = (Line *)e;
- arc = NULL;
- circle = NULL;
- startPoint = line->getStartpoint();
- endPoint = line->getEndpoint();
- center = Vector(false);
- reversed = false;
- }
- else if (e->rtti() == RS2::EntityArc)
- {
- arc = (Arc *)e;
- line = NULL;
- circle = NULL;
- startPoint = arc->getStartpoint();
- endPoint = arc->getEndpoint();
- center = arc->getCenter();
- reversed = arc->isReversed();
- }
- else if (e->rtti() == RS2::EntityCircle)
- {
- circle = (Circle *)e;
- line = NULL;
- arc = NULL;
- startPoint = circle->getCenter() + Vector(circle->getRadius(), 0.0);
- endPoint = startPoint;
- center = circle->getCenter();
- reversed = false;
- }
- else
- {
- continue;
- }
-
- // getting all intersections of this pattern line with the contour:
- QList<Vector *> is;
-#warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
-// is.setAutoDelete(true);
- is.append(new Vector(startPoint));
-
- for(Entity * loop=firstEntity(); loop!=NULL; loop=nextEntity())
- {
- if (loop->isContainer())
- {
- for(Entity * p=((EntityContainer *)loop)->firstEntity(); p!=NULL; p=((EntityContainer*)loop)->nextEntity())
- {
- VectorSolutions sol = Information::getIntersection(e, p, true);
-
- for(int i=0; i<=1; ++i)
- {
- if (sol.get(i).valid)
- {
- is.append(new Vector(sol.get(i)));
- DEBUG->print(" pattern line intersection: %f/%f", sol.get(i).x, sol.get(i).y);
- }
- }
- }
- }
- }
-
- is.append(new Vector(endPoint));
-
- // sort the intersection points into is2:
- Vector sp = startPoint;
- double sa = center.angleTo(sp);
-// Q3PtrList<Vector> is2;
- QList<Vector *> is2;
-#warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
-// is2.setAutoDelete(true);
- bool done;
- double minDist;
- double dist = 0.0;
- Vector * av;
- Vector last = Vector(false);
-
- do
- {
- done = true;
- minDist = RS_MAXDOUBLE;
- av = NULL;
-
-// for(Vector * v=is.first(); v!=NULL; v=is.next())
- for(int i=0; i<is.size(); i++)
- {
- Vector * v = is[i];
-
- if (line != NULL)
- {
- dist = sp.distanceTo(*v);
- }
- else if (arc != NULL || circle != NULL)
- {
- double a = center.angleTo(*v);
-
- if (reversed)
- {
- if (a > sa)
- {
- a -= 2 * M_PI;
- }
-
- dist = sa - a;
- }
- else
- {
- if (a < sa)
- {
- a += 2 * M_PI;
- }
-
- dist = a - sa;
- }
-
- if (fabs(dist - 2 * M_PI) < 1.0e-6)
- {
- dist = 0.0;
- }
- }
-
- if (dist < minDist)
- {
- minDist = dist;
- done = false;
- av = v;
- //idx = is.at();
- }
- }
-
- // copy to sorted list, removing double points
- if (!done && av != NULL)
- {
- if (last.valid == false || last.distanceTo(*av) > 1.0e-10)
- {
- is2.append(new Vector(*av));
- last = *av;
- }
-
-// is.remove(av);
- int idx = is.indexOf(av);
-
- if (idx != -1)
- delete is.takeAt(idx);
-
- av = NULL;
- }
- }
- while(!done);
-
- // add small cut lines / arcs to tmp2:
-// for(Vector * v1=is2.first(); v1!=NULL;)
- Vector * v1 = is2[0];
-
- for(int i=1; i<is2.size(); i++)
- {
-// Vector * v2 = is2.next();
- Vector * v2 = is2[i];
-
- if (v1 != NULL && v2 != NULL)
- {
- if (line != NULL)
- {
- tmp2.addEntity(new Line(&tmp2, LineData(*v1, *v2)));
- }
- else if (arc != NULL || circle != NULL)
- {
- tmp2.addEntity(new Arc(&tmp2, ArcData(center, center.distanceTo(*v1),
- center.angleTo(*v1), center.angleTo(*v2), reversed)));
- }
- }
-
- v1 = v2;
- }
- }
-
- // updating hatch / adding entities that are inside
- DEBUG->print("Hatch::update: cutting pattern carpet: OK");
-
- // the hatch pattern entities:
- hatch = new EntityContainer(this);
- hatch->setPen(Pen(RS2::FlagInvalid));
- hatch->setLayer(NULL);
- hatch->setFlag(RS2::FlagTemp);
-
- //calculateBorders();
-
- for(Entity * e=tmp2.firstEntity(); e!=NULL; e=tmp2.nextEntity())
- {
- Vector middlePoint;
- Vector middlePoint2;
-
- if (e->rtti() == RS2::EntityLine)
- {
- Line * line = (Line *)e;
- middlePoint = line->getMiddlepoint();
- middlePoint2 = line->getNearestDist(line->getLength() / 2.1, line->getStartpoint());
- }
- else if (e->rtti() == RS2::EntityArc)
- {
- Arc * arc = (Arc *)e;
- middlePoint = arc->getMiddlepoint();
- middlePoint2 = arc->getNearestDist(arc->getLength() / 2.1, arc->getStartpoint());
- }
- else
- {
- middlePoint = Vector(false);
- middlePoint2 = Vector(false);
- }
-
- if (middlePoint.valid)
- {
- bool onContour = false;
-
- if (Information::isPointInsideContour(middlePoint, this, &onContour)
- || Information::isPointInsideContour(middlePoint2, this))
- {
- Entity * te = e->clone();
- te->setPen(Pen(RS2::FlagInvalid));
- te->setLayer(NULL);
- te->reparent(hatch);
- hatch->addEntity(te);
- }
- }
- }
-
- addEntity(hatch);
- //getGraphic()->addEntity(rubbish);
-
- forcedCalculateBorders();
-
- // deactivate contour:
- activateContour(false);
-
- updateRunning = false;
-
- DEBUG->print("Hatch::update: OK");
-}
-
-/**
- * Activates of deactivates the hatch boundary.
- */
-void Hatch::activateContour(bool on)
-{
- DEBUG->print("Hatch::activateContour: %d", (int)on);
-
- for(Entity * e=firstEntity(); e!=NULL; e=nextEntity())
- {
- if (!e->isUndone())
- {
- if (!e->getFlag(RS2::FlagTemp))
- {
- DEBUG->print("Hatch::activateContour: set visible");
- e->setVisible(on);
- }
- else
- {
- DEBUG->print("Hatch::activateContour: entity temp");
- }
- }
- else
- {
- DEBUG->print("Hatch::activateContour: entity undone");
- }
- }
-
- DEBUG->print("Hatch::activateContour: OK");
-}
-
-/**
- * Overrides drawing of subentities. This is only ever called for solid fills.
- */
-void Hatch::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
-{
- if (!data.solid)
- {
- for(Entity * se=firstEntity(); se!=NULL; se = nextEntity())
- view->drawEntity(se);
-
- return;
- }
-
-// Q3PointArray pa;
-// Q3PointArray jp; // jump points
- QPolygon pa;
- QPolygon jp; // jump points
- uint s = 0;
- uint sj = 0;
- int lastX = 0;
- int lastY = 0;
- bool lastValid = false;
-
- // loops:
- if (needOptimization == true)
- {
- for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
- {
- if (l->rtti() == RS2::EntityContainer)
- {
- EntityContainer * loop = (EntityContainer *)l;
- loop->optimizeContours();
- }
- }
-
- needOptimization = false;
- }
-
- // loops:
- for(Entity * l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone))
- {
- l->setLayer(getLayer());
-
- if (l->rtti() == RS2::EntityContainer)
- {
- EntityContainer * loop = (EntityContainer *)l;
-
- // edges:
- for(Entity * e=loop->firstEntity(RS2::ResolveNone); e!=NULL; e=loop->nextEntity(RS2::ResolveNone))
- {
- e->setLayer(getLayer());
-
- switch (e->rtti())
- {
- case RS2::EntityLine:
- {
- Line * line = (Line *)e;
-
- int x1 = Math::round(view->toGuiX(line->getStartpoint().x));
- int y1 = Math::round(view->toGuiY(line->getStartpoint().y));
- int x2 = Math::round(view->toGuiX(line->getEndpoint().x));
- int y2 = Math::round(view->toGuiY(line->getEndpoint().y));
-
- if (lastValid && (lastX != x1 || lastY != y1))
- {
- jp.resize(++sj);
- jp.setPoint(sj - 1, x1, y1);
- }
-
- pa.resize(++s);
- pa.setPoint(s - 1, x1, y1);
-
- pa.resize(++s);
- pa.setPoint(s - 1, x2, y2);
-
- lastX = x2;
- lastY = y2;
- lastValid = true;
- }
- break;
-
- case RS2::EntityArc:
- {
- Arc * arc = (Arc *)e;
-
- int x1 = Math::round(view->toGuiX(arc->getStartpoint().x));
- int y1 = Math::round(view->toGuiY(arc->getStartpoint().y));
- int x2 = Math::round(view->toGuiX(arc->getEndpoint().x));
- int y2 = Math::round(view->toGuiY(arc->getEndpoint().y));
-
- if (lastValid && (lastX != x1 || lastY != y1))
- {
- jp.resize(++sj);
- jp.setPoint(sj - 1, x1, y1);
- }
-
- pa.resize(++s);
- pa.setPoint(s - 1, x1, y1);
-
-// Q3PointArray pa2;
- QPolygon pa2;
- painter->createArc(pa2, view->toGui(arc->getCenter()),
- view->toGuiDX(arc->getRadius()), arc->getAngle1(),
- arc->getAngle2(), arc->isReversed());
-
- pa.resize(s + pa2.size());
- pa.putPoints(s, pa2.size(), pa2);
- s += pa2.size() - 1;
-
- pa.resize(++s);
- pa.setPoint(s - 1, x2, y2);
-
- lastX = x2;
- lastY = y2;
- lastValid = true;
- }
- break;
-
- case RS2::EntityCircle:
- {
- Circle * circle = (Circle *)e;
-
- int x1 = Math::round(view->toGuiX(circle->getCenter().x + circle->getRadius()));
- int y1 = Math::round(view->toGuiY(circle->getCenter().y));
- int x2 = x1;
- int y2 = y1;
-
- if (lastValid && (lastX != x1 || lastY != y1))
- {
- jp.resize(++sj);
- jp.setPoint(sj - 1, x1, y1);
- }
-
- pa.resize(++s);
- pa.setPoint(s - 1, x1, y1);
-
-// Q3PointArray pa2;
- QPolygon pa2;
- painter->createArc(pa2, view->toGui(circle->getCenter()),
- view->toGuiDX(circle->getRadius()), 0.0, 2 * M_PI, false);
-
- pa.resize(s + pa2.size());
- pa.putPoints(s, pa2.size(), pa2);
- s += pa2.size() - 1;
-
- pa.resize(++s);
- pa.setPoint(s - 1, x2, y2);
-
- lastX = x2;
- lastY = y2;
- lastValid = true;
- }
- break;
-
- default:
- break;
- }
- }
- }
- }
-
- for(int i=(int)jp.count()-1; i>=0; --i)
- {
- pa.resize(++s);
- pa.setPoint(s - 1, jp.point(i));
- }
-
- painter->setBrush(painter->getPen().getColor());
- painter->disablePen();
- painter->drawPolygon(pa);
-}
-
-/*virtual*/ double Hatch::getLength()
-{
- return -1.0;
-}
-
-double Hatch::getDistanceToPoint(const Vector & coord, Entity ** entity,
- RS2::ResolveLevel level, double solidDist)
-{
- if (data.solid == true)
- {
- if (entity != NULL)
- *entity = this;
-
- bool onContour;
-
- if (Information::isPointInsideContour(coord, this, &onContour))
- // distance is the snap range:
- return solidDist;
-
- return RS_MAXDOUBLE;
- }
- else
- {
- return EntityContainer::getDistanceToPoint(coord, entity, level, solidDist);
- }
-}
-
-void Hatch::move(Vector offset)
-{
- EntityContainer::move(offset);
- update();
-}
-
-void Hatch::rotate(Vector center, double angle)
-{
- EntityContainer::rotate(center, angle);
- data.angle = Math::correctAngle(data.angle + angle);
- update();
-}
-
-void Hatch::scale(Vector center, Vector factor)
-{
- EntityContainer::scale(center, factor);
- data.scale *= factor.x;
- update();
-}
-
-void Hatch::mirror(Vector axisPoint1, Vector axisPoint2)
-{
- EntityContainer::mirror(axisPoint1, axisPoint2);
- double ang = axisPoint1.angleTo(axisPoint2);
- data.angle = Math::correctAngle(data.angle + ang*2.0);
- update();
-}
-
-void Hatch::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
-{
- EntityContainer::stretch(firstCorner, secondCorner, offset);
- update();
-}
-
-/**
- * Dumps the point's data to stdout.
- */
-std::ostream & operator<<(std::ostream & os, const Hatch & p)
-{
- os << " Hatch: " << p.getData() << "\n";
- return os;
-}