summaryrefslogtreecommitdiffstats
path: root/labb8/lib/StanfordCPPLib/gobjects.cpp
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2020-12-03 17:11:43 +0100
committerGustav Sörnäs <gustav@sornas.net>2020-12-08 10:21:07 +0100
commit0c39051ba80f04b1177833a006f2d442a7170b56 (patch)
tree9e657946a061b5b305f9cf75634db7b37e979eb3 /labb8/lib/StanfordCPPLib/gobjects.cpp
parent7b7f6808a7b2db2ed21103767434c1445f7815c2 (diff)
downloadtddd86-0c39051ba80f04b1177833a006f2d442a7170b56.tar.gz
add initial files l8
Diffstat (limited to 'labb8/lib/StanfordCPPLib/gobjects.cpp')
-rwxr-xr-xlabb8/lib/StanfordCPPLib/gobjects.cpp1033
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);
+}