diff options
Diffstat (limited to 'labb8/lib/StanfordCPPLib/gobjects.cpp')
| -rwxr-xr-x | labb8/lib/StanfordCPPLib/gobjects.cpp | 1033 |
1 files changed, 1033 insertions, 0 deletions
diff --git a/labb8/lib/StanfordCPPLib/gobjects.cpp b/labb8/lib/StanfordCPPLib/gobjects.cpp new file mode 100755 index 0000000..ea22894 --- /dev/null +++ b/labb8/lib/StanfordCPPLib/gobjects.cpp @@ -0,0 +1,1033 @@ +/* + * File: gobjects.cpp + * ------------------ + * This file implements the gobjects.h interface. + */ + +#include <cmath> +#include <iostream> +#include <sstream> +#include "gevents.h" +#include "gmath.h" +#include "gobjects.h" +#include "gtypes.h" +#include "gwindow.h" +#include "platform.h" +#include "vector.h" + +static Platform *pp = getPlatform(); + +const double LINE_TOLERANCE = 1.5; +const double ARC_TOLERANCE = 2.5; +const double DEFAULT_CORNER = 10; +const string DEFAULT_GLABEL_FONT = "Dialog-13"; + +static double dsq(double x0, double y0, double x1, double y1); + +double GObject::getX() const { + return x; +} + +double GObject::getY() const { + return y; +} + +GPoint GObject::getLocation() const { + return GPoint(x, y); +} + +void GObject::setLocation(const GPoint & pt) { + setLocation(pt.getX(), pt.getY()); +} + +void GObject::setLocation(double x, double y) { + this->x = x; + this->y = y; + pp->setLocation(this, x, y); +} + +void GObject::move(double dx, double dy) { + setLocation(x + dx, y + dy); +} + +double GObject::getWidth() const { + return getBounds().getWidth(); +} + +double GObject::getHeight() const { + return getBounds().getHeight(); +} + +GDimension GObject::getSize() const { + GRectangle bounds = getBounds(); + return GDimension(bounds.getWidth(), bounds.getHeight()); +} + +void GObject::setLineWidth(double lineWidth) { + this->lineWidth = lineWidth; + pp->setLineWidth(this, lineWidth); +} + +double GObject::getLineWidth() const { + return lineWidth; +} + +void GObject::setColor(string color) { + setColor(convertColorToRGB(color)); +} + +void GObject::setColor(int rgb) { + this->color = convertRGBToColor(rgb); + pp->setColor(this, this->color); +} + +string GObject::getColor() const { + return color; +} + +void GObject::scale(double sf) { + scale(sf, sf); +} + +void GObject::scale(double sx, double sy) { + // Apply local transform + transformed = true; + pp->scale(this, sx, sy); +} + +void GObject::rotate(double theta) { + // Apply local transform + transformed = true; + pp->rotate(this, theta); +} + +void GObject::setVisible(bool flag) { + visible = flag; + pp->setVisible(this, flag); +} + +bool GObject::isVisible() const { + return visible; +} + +void GObject::sendForward() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendForward(this); +} + +void GObject::sendToFront() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendToFront(this); +} + +void GObject::sendBackward() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendBackward(this); +} + +void GObject::sendToBack() { + GCompound *parent = getParent(); + if (parent != NULL) parent->sendToBack(this); +} + +bool GObject::contains(GPoint pt) const { + return contains(pt.getX(), pt.getY()); +} + +bool GObject::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + return getBounds().contains(x, y); +} + +GCompound *GObject::getParent() const { + return parent; +} + +GObject::GObject() { + x = 0; + y = 0; + color = ""; + lineWidth = 1.0; + transformed = false; + visible = true; +} + +GObject::~GObject() { + pp->deleteGObject(this); +} + +/* + * Implementation notes: GRect class + * --------------------------------- + * The GRect class is the most straightforward of the shape classes. + */ + +GRect::GRect(double width, double height) { + createGRect(width, height); +} + +GRect::GRect(double x, double y, double width, double height) { + createGRect(width, height); + setLocation(x, y); +} + +GRect::~GRect() { + /* Empty */ +} + +void GRect::setSize(const GDimension & size) { + setSize(size.getWidth(), size.getHeight()); +} + +void GRect::setSize(double width, double height) { + if (transformed) error("setSize: Object has been transformed"); + this->width = width; + this->height = height; + pp->setSize(this, width, height); +} + +void GRect::setBounds(const GRectangle & bounds) { + setLocation(bounds.getX(), bounds.getY()); + setSize(bounds.getWidth(), bounds.getHeight()); +} + +void GRect::setBounds(double x, double y, double width, double height) { + setLocation(x, y); + setSize(width, height); +} + +GRectangle GRect::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y, width, height); +} + +void GRect::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GRect::isFilled() const { + return fillFlag; +} + +void GRect::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GRect::setFillColor(int rgb) { + fillColor = convertRGBToColor(rgb); + pp->setFillColor(this, fillColor); +} + +string GRect::getFillColor() const { + return fillColor; +} + +string GRect::getType() const { + return "GRect"; +} + +string GRect::toString() const { + ostringstream oss; + oss << "GRect(" << x << ", " << y << ", " + << width << ", " << height << ")"; + return oss.str(); +} + +GRect::GRect() { + /* Called only by the GRoundRect and G3DRect subclasses */ +} + +void GRect::createGRect(double width, double height) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + fillFlag = false; + fillColor = ""; + pp->createGRect(this, width, height); +} + +/* + * Implementation notes: GRoundRect class + * -------------------------------------- + * Most of the GRoundRect class is inherited from the GRect class. + */ + +GRoundRect::GRoundRect(double width, double height) { + createGRoundRect(width, height, DEFAULT_CORNER); +} + +GRoundRect::GRoundRect(double width, double height, double corner) { + createGRoundRect(width, height, corner); +} + +GRoundRect::GRoundRect(double x, double y, double width, double height) { + createGRoundRect(width, height, DEFAULT_CORNER); + setLocation(x, y); +} + +GRoundRect::GRoundRect(double x, double y, double width, double height, + double corner) { + createGRoundRect(width, height, corner); + setLocation(x, y); +} + +GRoundRect::~GRoundRect() { + /* Empty */ +} + +string GRoundRect::getType() const { + return "GRoundRect"; +} + +string GRoundRect::toString() const { + ostringstream oss; + oss << "GRoundRect(" << x << ", " << y << ", " + << width << ", " << height << ", " << corner << ")"; + return oss.str(); +} + +void GRoundRect::createGRoundRect(double width, double height, double corner) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + this->corner = corner; + fillFlag = false; + fillColor = ""; + pp->createGRoundRect(this, width, height, corner); +} + +/* + * Implementation notes: G3DRect class + * ----------------------------------- + * Most of the G3DRect class is inherited from the GRect class. + */ + +G3DRect::G3DRect(double width, double height) { + createG3DRect(width, height, false); +} + +G3DRect::G3DRect(double width, double height, bool raised) { + createG3DRect(width, height, raised); +} + +G3DRect::G3DRect(double x, double y, double width, double height) { + createG3DRect(width, height, false); + setLocation(x, y); +} + +G3DRect::G3DRect(double x, double y, double width, double height, + bool raised) { + createG3DRect(width, height, raised); + setLocation(x, y); +} + +G3DRect::~G3DRect() { + /* Empty */ +} + +void G3DRect::setRaised(bool raised) { + this->raised = raised; + pp->setRaised(this, raised); +} + +bool G3DRect::isRaised() const { + return raised; +} + +string G3DRect::getType() const { + return "G3DRect"; +} + +string G3DRect::toString() const { + ostringstream oss; + oss << boolalpha << "G3DRect(" << x << ", " << y << ", " + << width << ", " << height << ", " << raised << ")"; + return oss.str(); +} + +void G3DRect::createG3DRect(double width, double height, bool raised) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + this->raised = raised; + fillFlag = false; + fillColor = ""; + pp->createG3DRect(this, width, height, raised); +} + +GOval::GOval(double width, double height) { + createGOval(width, height); +} + +GOval::GOval(double x, double y, double width, double height) { + createGOval(width, height); + setLocation(x, y); +} + +GOval::~GOval() { + /* Empty */ +} + +void GOval::setSize(const GDimension & size) { + setSize(size.getWidth(), size.getHeight()); +} + +void GOval::setSize(double width, double height) { + if (transformed) error("setSize: Object has been transformed"); + this->width = width; + this->height = height; + pp->setSize(this, width, height); +} + +void GOval::setBounds(const GRectangle & bounds) { + setLocation(bounds.getX(), bounds.getY()); + setSize(bounds.getWidth(), bounds.getHeight()); +} + +void GOval::setBounds(double x, double y, double width, double height) { + setLocation(x, y); + setSize(width, height); +} + +GRectangle GOval::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y, width, height); +} + +bool GOval::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + double rx = width / 2; + double ry = height / 2; + if (rx == 0 || ry == 0) return false; + double dx = x - (this->x + rx); + double dy = y - (this->y + ry); + return (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry) <= 1.0; +} + +void GOval::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GOval::isFilled() const { + return fillFlag; +} + +void GOval::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GOval::setFillColor(int color) { + fillColor = convertRGBToColor(color); + pp->setFillColor(this, fillColor); +} + +string GOval::getFillColor() const { + return fillColor; +} + +string GOval::getType() const { + return "GOval"; +} + +string GOval::toString() const { + ostringstream oss; + oss << "GOval(" << x << ", " << y << ", " + << width << ", " << height << ")"; + return oss.str(); +} + +void GOval::createGOval(double width, double height) { + this->x = 0; + this->y = 0; + this->width = width; + this->height = height; + fillFlag = false; + fillColor = ""; + pp->createGOval(this, width, height); +} + +/* GArc class */ + +GArc::GArc(double width, double height, double start, double sweep) { + createGArc(width, height, start, sweep); +} + +GArc::GArc(double x, double y, double width, double height, + double start, double sweep) { + createGArc(width, height, start, sweep); + setLocation(x, y); +} + +void GArc::setStartAngle(double start) { + this->start = start; + pp->setStartAngle(this, start); +} + +double GArc::getStartAngle() const { + return start; +} + +void GArc::setSweepAngle(double sweep) { + this->sweep = sweep; + pp->setSweepAngle(this, sweep); +} + +double GArc::getSweepAngle() const { + return sweep; +} + +GPoint GArc::getStartPoint() const { + return getArcPoint(start); +} + +GPoint GArc::getEndPoint() const { + return getArcPoint(start + sweep); +} + +void GArc::setFrameRectangle(const GRectangle & rect) { + setFrameRectangle(rect.getX(), rect.getY(), rect.getWidth(), + rect.getHeight()); +} + +void GArc::setFrameRectangle(double x, double y, double width, double height) { + this->x = x; + this->y = y; + frameWidth = width; + frameHeight = height; + pp->setFrameRectangle(this, x, y, width, height); +} + +GRectangle GArc::getFrameRectangle() const { + return GRectangle(0, 0, 0, 0); +} + +void GArc::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GArc::isFilled() const { + return fillFlag; +} + +void GArc::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GArc::setFillColor(int color) { + fillColor = convertRGBToColor(color); + pp->setFillColor(this, fillColor); +} + +string GArc::getFillColor() const { + return fillColor; +} + +GRectangle GArc::getBounds() const { + if (transformed) return pp->getBounds(this); + double rx = frameWidth / 2; + double ry = frameHeight / 2; + double cx = x + rx; + double cy = y + ry; + double startRadians = start * PI / 180; + double sweepRadians = sweep * PI / 180; + double p1x = cx + cos(startRadians) * rx; + double p1y = cy - sin(startRadians) * ry; + double p2x = cx + cos(startRadians + sweepRadians) * rx; + double p2y = cy - sin(startRadians + sweepRadians) * ry; + double xMin = min(p1x, p2x); + double xMax = max(p1x, p2x); + double yMin = min(p1y, p2y); + double yMax = max(p1y, p2y); + if (containsAngle(0)) xMax = cx + rx; + if (containsAngle(90)) yMin = cy - ry; + if (containsAngle(180)) xMin = cx - rx; + if (containsAngle(270)) yMax = cy + ry; + if (isFilled()) { + xMin = min(xMin, cx); + yMin = min(yMin, cy); + xMax = max(xMax, cx); + yMax = max(yMax, cy); + } + return GRectangle(xMin, yMin, xMax - xMin, yMax - yMin); +} + +bool GArc::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + double rx = frameWidth / 2; + double ry = frameHeight / 2; + if (rx == 0 || ry == 0) return false; + double dx = x - (this->x + rx); + double dy = y - (this->y + ry); + double r = (dx * dx) / (rx * rx) + (dy * dy) / (ry * ry); + if (fillFlag) { + if (r > 1.0) return false; + } else { + double t = ARC_TOLERANCE / ((rx + ry) / 2); + if (abs(1.0 - r) > t) return false; + } + return containsAngle(atan2(-dy, dx) * 180 / PI); +} + +string GArc::getType() const { + return "GArc"; +} + +string GArc::toString() const { + ostringstream oss; + oss << "GArc(" << x << ", " << y << ", " << frameWidth << ", " + << frameHeight << ", " << start << ", " << sweep << ")"; + return oss.str(); +} + +GPoint GArc::getArcPoint(double theta) const { + double rx = frameWidth / 2; + double ry = frameHeight / 2; + double cx = x + rx; + double cy = y + ry; + double radians = theta * PI / 180; + return GPoint(cx + rx * cos(radians), cy - ry * sin(radians)); +} + +bool GArc::containsAngle(double theta) const { + double start = min(this->start, this->start + this->sweep); + double sweep = abs(this->sweep); + if (sweep >= 360) return true; + theta = (theta < 0) ? 360 - fmod(-theta, 360) : fmod(theta, 360); + start = (start < 0) ? 360 - fmod(-start, 360) : fmod(start, 360); + if (start + sweep > 360) { + return theta >= start || theta <= start + sweep - 360; + } else { + return theta >= start && theta <= start + sweep; + } +} + +void GArc::createGArc(double width, double height, double start, double sweep) { + this->x = 0; + this->y = 0; + frameWidth = width; + frameHeight = height; + this->start = start; + this->sweep = sweep; + fillFlag = false; + fillColor = ""; + pp->createGArc(this, width, height, start, sweep); +} + +GCompound::GCompound() { + pp->createGCompound(this); +} + +void GCompound::add(GObject *gobj) { + pp->add(this, gobj); + contents.add(gobj); + gobj->parent = this; +} + +void GCompound::add(GObject *gobj, double x, double y) { + gobj->setLocation(x, y); + add(gobj); +} + +void GCompound::remove(GObject *gobj) { + int index = findGObject(gobj); + if (index != -1) removeAt(index); +} + +void GCompound::removeAll() { + while (!contents.isEmpty()) { + removeAt(0); + } +} + +int GCompound::getElementCount() { + return contents.size(); +} + +GObject *GCompound::getElement(int index) { + return contents.get(index); +} + +GRectangle GCompound::getBounds() const { + if (transformed) return pp->getBounds(this); + double xMin = +1E20; + double yMin = +1E20; + double xMax = -1E20; + double yMax = -1E20; + for (int i = 0; i < contents.size(); i++) { + GRectangle bounds = contents.get(i)->getBounds(); + xMin = min(xMin, bounds.getX()); + yMin = min(yMin, bounds.getY()); + xMax = max(xMax, bounds.getX()); + yMin = min(yMax, bounds.getY()); + } + return GRectangle(xMin, yMin, xMax - xMin, yMax - yMin); +} + +bool GCompound::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + for (int i = 0; i < contents.size(); i++) { + if (contents.get(i)->contains(x, y)) return true; + } + return false; +} + +string GCompound::getType() const { + return "GCompound"; +} + +string GCompound::toString() const { + return "GCompound(...)"; +} + +void GCompound::sendForward(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != contents.size() - 1) { + contents.remove(index); + contents.insert(index + 1, gobj); + pp->sendForward(gobj); + } +} + +void GCompound::sendToFront(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != contents.size() - 1) { + contents.remove(index); + contents.add(gobj); + pp->sendToFront(gobj); + } +} + +void GCompound::sendBackward(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != 0) { + contents.remove(index); + contents.insert(index - 1, gobj); + pp->sendBackward(gobj); + } +} + +void GCompound::sendToBack(GObject *gobj) { + int index = findGObject(gobj); + if (index == -1) return; + if (index != 0) { + contents.remove(index); + contents.insert(0, gobj); + pp->sendToBack(gobj); + } +} + +int GCompound::findGObject(GObject *gobj) { + int n = contents.size(); + for (int i = 0; i < n; i++) { + if (contents.get(i) == gobj) return i; + } + return -1; +} + +void GCompound::removeAt(int index) { + GObject *gobj = contents[index]; + contents.remove(index); + pp->remove(gobj); + gobj->parent = NULL; +} + +GImage::GImage(string filename) { + createGImage(filename); +} + +GImage::GImage(string filename, double x, double y) { + createGImage(filename); + setLocation(x, y); +} + +GRectangle GImage::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y, width, height); +} + +string GImage::getType() const { + return "GImage"; +} + +string GImage::toString() const { + return "GImage(\"" + filename + "\")"; +} + +void GImage::createGImage(string filename) { + this->filename = filename; + GDimension size = pp->createGImage(this, filename); + width = size.getWidth(); + height = size.getHeight(); +} + +/* + * Implementation notes: GLabel class + * ---------------------------------- + */ + +GLabel::GLabel(string str) { + createGLabel(str); +} + +GLabel::GLabel(string str, double x, double y) { + createGLabel(str); + setLocation(x, y); +} + +void GLabel::createGLabel(const string & str) { + this->str = str; + pp->createGLabel(this, str); + setFont(DEFAULT_GLABEL_FONT); + GDimension size = pp->getGLabelSize(this); + width = size.getWidth(); + height = size.getHeight(); + ascent = pp->getFontAscent(this); + descent = pp->getFontDescent(this); +} + +void GLabel::setFont(string font) { + this->font = font; + pp->setFont(this, font); + GDimension size = pp->getGLabelSize(this); + width = size.getWidth(); + height = size.getHeight(); + ascent = pp->getFontAscent(this); + descent = pp->getFontDescent(this); +} + +string GLabel::getFont() const { + return font; +} + +void GLabel::setLabel(string str) { + this->str = str; + pp->setLabel(this, str); + GDimension size = pp->getGLabelSize(this); + width = size.getWidth(); + height = size.getHeight(); +} + +string GLabel::getLabel() const { + return str; +} + +double GLabel::getFontAscent() const { + return ascent; +} + +double GLabel::getFontDescent() const { + return descent; +} + +GRectangle GLabel::getBounds() const { + if (transformed) return pp->getBounds(this); + return GRectangle(x, y - ascent, width, height); +} + +string GLabel::getType() const { + return "GLabel"; +} + +string GLabel::toString() const { + return "GLabel(\"" + str + "\")"; +} + +/* + * Implementation notes: GLine class + * --------------------------------- + */ + +GLine::GLine(double x0, double y0, double x1, double y1) { + pp->createGLine(this, x0, y0, x1, y1); + x = x0; + y = y0; + dx = x1 - x0; + dy = y1 - y0; +} + +void GLine::setStartPoint(double x, double y) { + dx += this->x - x; + dy += this->y - y; + this->x = x; + this->y = y; + pp->setStartPoint(this, x, y); +} + +GPoint GLine::getStartPoint() const { + return GPoint(x, y); +} + +void GLine::setEndPoint(double x, double y) { + dx = x - this->x; + dy = y - this->y; + pp->setEndPoint(this, x, y); +} + +GPoint GLine::getEndPoint() const { + return GPoint(x + dx, y + dy); +} + +GRectangle GLine::getBounds() const { + if (transformed) return pp->getBounds(this); + double x0 = (dx < 0) ? x + dx : x; + double y0 = (dy < 0) ? y + dy : y; + return GRectangle(x0, y0, abs(dx), abs(dy)); +} + +bool GLine::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + double x0 = getX(); + double y0 = getY(); + double x1 = x0 + dx; + double y1 = y0 + dy; + double tSquared = LINE_TOLERANCE * LINE_TOLERANCE; + if (dsq(x, y, x0, y0) < tSquared) return true; + if (dsq(x, y, x1, y1) < tSquared) return true; + if (x < min(x0, x1) - LINE_TOLERANCE) return false; + if (x > max(x0, x1) + LINE_TOLERANCE) return false; + if (y < min(y0, y1) - LINE_TOLERANCE) return false; + if (y > max(y0, y1) + LINE_TOLERANCE) return false; + if ((float) (x0 - x1) == 0 && (float) (y0 - y1) == 0) return false; + double u = ((x - x0) * (x1 - x0) + (y - y0) * (y1 - y0)) + / dsq(x0, y0, x1, y1); + return dsq(x, y, x0 + u * (x1 - x0), y0 + u * (y1 - y0)) < tSquared; +} + +string GLine::getType() const { + return "GLine"; +} + +string GLine::toString() const { + ostringstream oss; + oss << "GLine(" << x << ", " << y << ", " + << (x + dx) << ", " << (y + dy) << ")"; + return oss.str(); +} + +/* + * Implementation notes: GPolygon class + * ------------------------------------ + */ + +GPolygon::GPolygon() { + fillFlag = false; + fillColor = ""; + pp->createGPolygon(this); +} + +void GPolygon::addVertex(double x, double y) { + cx = x; + cy = y; + vertices.add(GPoint(cx, cy)); + pp->addVertex(this, cx, cy); +} + +void GPolygon::addEdge(double dx, double dy) { + addVertex(cx + dx, cy + dy); +} + +void GPolygon::addPolarEdge(double r, double theta) { + addEdge(r * cos(theta * PI / 180), -r * sin(theta * PI / 180)); +} + +Vector<GPoint> GPolygon::getVertices() const { + return vertices; +} + +void GPolygon::setFilled(bool flag) { + fillFlag = true; + pp->setFilled(this, flag); +} + +bool GPolygon::isFilled() const { + return fillFlag; +} + +void GPolygon::setFillColor(string color) { + fillColor = color; + if (fillColor != "") { + fillColor = convertRGBToColor(convertColorToRGB(color)); + } + pp->setFillColor(this, fillColor); +} + +void GPolygon::setFillColor(int rgb) { + fillColor = convertRGBToColor(rgb); + pp->setFillColor(this, fillColor); +} + +string GPolygon::getFillColor() const { + return fillColor; +} + +GRectangle GPolygon::getBounds() const { + if (transformed) return pp->getBounds(this); + double xMin = 0; + double yMin = 0; + double xMax = 0; + double yMax = 0; + for (int i = 0; i < vertices.size(); i++) { + double x = vertices[i].getX(); + double y = vertices[i].getY(); + if (i == 0 || x < xMin) xMin = x; + if (i == 0 || y < yMin) yMin = y; + if (i == 0 || x > xMax) xMax = x; + if (i == 0 || y > yMax) yMax = y; + } + return GRectangle(xMin, yMin, xMax - xMin, yMax - yMin); +} + +bool GPolygon::contains(double x, double y) const { + if (transformed) return pp->contains(this, x, y); + int crossings = 0; + int n = vertices.size(); + if (n < 2) return false; + if (vertices[0] == vertices[n - 1]) n--; + double x0 = vertices[0].getX(); + double y0 = vertices[0].getY(); + for (int i = 1; i <= n; i++) { + double x1 = vertices[i % n].getX(); + double y1 = vertices[i % n].getY(); + if ((y0 > y) != (y1 > y) && x - x0 < (x1 - x0) * (y - y0) / (y1 - y0)) { + crossings++; + } + x0 = x1; + y0 = y1; + } + return (crossings % 2 == 1); +} + +string GPolygon::getType() const { + return "GPolygon"; +} + +string GPolygon::toString() const { + ostringstream oss; + oss << "GPolygon(" << vertices.size() << " vertices)"; + return oss.str(); +} + +static double dsq(double x0, double y0, double x1, double y1) { + return (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); +} |
