X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdimension.cpp;h=c9777e50bd673688bc5d06dee646c6a4753cd19d;hb=2549a213bacfb2f6993ec143083d72b716854345;hp=4ccd48e52352e97ad69938f3873b0090221fc11e;hpb=bd9b40058a376c946318a444dd6c77737ec6ac98;p=architektonas diff --git a/src/dimension.cpp b/src/dimension.cpp index 4ccd48e..c9777e5 100644 --- a/src/dimension.cpp +++ b/src/dimension.cpp @@ -4,51 +4,87 @@ // (C) 2011 Underground Software // See the README and GPLv3 files for licensing and warranty information // -// JLH = James L. Hammons +// JLH = James Hammons // // WHO WHEN WHAT // --- ---------- ------------------------------------------------------------ // JLH 04/04/2011 Created this file, basic rendering +// JLH 03/14/2013 Updated to new connection system // #include "dimension.h" #include +#include "geometry.h" #include "mathconstants.h" #include "painter.h" -Dimension::Dimension(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint(p2), +Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Object * p/*= NULL*/): + Object(p1, p), endpoint(p2), dragging(false), draggingHandle1(false), draggingHandle2(false), - length(p2.Magnitude()), point1(NULL), point2(NULL) + length(p2.Magnitude()), dimensionType(dt), size(0.25), point1(NULL), point2(NULL) { + // We set the size to 1/4 base unit. Could be anything. + type = OTDimension; } + // This is bad, p1 & p2 could be NULL, causing much consternation... -Dimension::Dimension(Vector * p1, Vector * p2, Object * p/*= NULL*/): Object(*p1, p), endpoint(*p2), +Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/): dragging(false), draggingHandle1(false), draggingHandle2(false), - length(p2->Magnitude()), point1(p1), point2(p2) + length(0), dimensionType(dt), size(0.25), point1(p1), point2(p2) { + type = OTDimension; } + Dimension::~Dimension() { } + +/* +The approach used below creates a hierarchy: Dimension is subservient to Line. + +Does this solve our problem of connected objects? Maybe, partially. Let's think this +through. It only works for endpoints, not points in the middle... + +Also: this is bad, depending on the Draw() function to update the internal + position(s) of the data of the object! (is it though?) + +How to move: click once moves only the object/point clicked on, all connected +objects deform themselves accordingly. click twice selects ALL connected objects; +all objects move as a unified whole. + +*/ + /*virtual*/ void Dimension::Draw(Painter * painter) { // If there are valid Vector pointers in here, use them to update the internal // positions. Otherwise, we just use the internal positions by default. - if (point1) - position = *point1; + if (point1.object) + position = point1.object->GetPointAtParameter(point1.t); - if (point2) - endpoint = *point2; + if (point2.object) + endpoint = point2.object->GetPointAtParameter(point2.t); + painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine)); + + if ((state == OSSelected) || ((state == OSInactive) && hitPoint1)) + painter->DrawHandle(position); + + if ((state == OSSelected) || ((state == OSInactive) && hitPoint2)) + painter->DrawHandle(endpoint); +#if 0 if (state == OSSelected) painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine)); else - painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine)); +// painter->SetPen(QPen(Qt::blue, 1.0, Qt::SolidLine)); +#endif + painter->SetPen(QPen(Qt::blue, 1.0 * Painter::zoom * size, Qt::SolidLine)); + + painter->SetBrush(QBrush(QColor(Qt::blue))); // Draw an aligned dimension line double angle = Vector(endpoint - position).Angle(); @@ -56,72 +92,84 @@ Dimension::~Dimension() Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle)); Vector unit = Vector(endpoint - position).Unit(); - // Get our line parallel to our points - Point p1 = position + (orthogonal * 10.0); - Point p2 = endpoint + (orthogonal * 10.0); +// Arrowhead: +// Point p1 = head - (unit * 9.0 * size); +// Point p2 = p1 + (orthogonal * 3.0 * size); +// Point p3 = p1 - (orthogonal * 3.0 * size); - // Draw main dimension line - painter->DrawLine(p1, p2); +/* +The numbers hardcoded into here, what are they? +I believe they are pixels. +*/ - Point p3 = position + (orthogonal * 16.0); - Point p4 = endpoint + (orthogonal * 16.0); - Point p5 = position + (orthogonal * 4.0); - Point p6 = endpoint + (orthogonal * 4.0); + // Get our line parallel to our points + Point p1 = position + (orthogonal * 10.0 * size); + Point p2 = endpoint + (orthogonal * 10.0 * size); + + Point p3 = position + (orthogonal * 16.0 * size); + Point p4 = endpoint + (orthogonal * 16.0 * size); + Point p5 = position + (orthogonal * 4.0 * size); + Point p6 = endpoint + (orthogonal * 4.0 * size); // Draw extension lines painter->DrawLine(p3, p5); painter->DrawLine(p4, p6); + // Calculate whether or not the arrowheads are too crowded to put inside + // the extension lines. 9.0 is the length of the arrowhead. +// double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size)); + double t = Geometry::ParameterOfLineAndPoint(position, endpoint, endpoint - (unit * 9.0 * size)); +//printf("Dimension::Draw(): t = %lf\n", t); + +// On the screen, it's acting like this is actually 58%... +// This is correct, we want it to happen at > 50% + if (t > 0.58) + { + // Draw main dimension line + arrowheads + painter->DrawLine(p1, p2); + painter->DrawArrowhead(p1, p2, size); + painter->DrawArrowhead(p2, p1, size); + } + else + { + // Draw outside arrowheads + Point p7 = p1 - (unit * 9.0 * size); + Point p8 = p2 + (unit * 9.0 * size); + painter->DrawArrowhead(p1, p7, size); + painter->DrawArrowhead(p2, p8, size); + painter->DrawLine(p1, p1 - (unit * 14.0 * size)); + painter->DrawLine(p2, p2 + (unit * 14.0 * size)); + } + // Draw length of dimension line... - painter->SetFont(QFont("Arial", 10)); + painter->SetFont(QFont("Arial", 8.0 * Painter::zoom * size)); Vector v1((p1.x - p2.x) / 2.0, (p1.y - p2.y) / 2.0); Point ctr = p2 + v1; - // This is in pixels, which isn't even remotely correct... !!! FIX !!! - QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude()); -// int textWidth = QFontMetrics(painter->font()).width(dimText); -// int textHeight = QFontMetrics(painter->font()).height(); -#if 0 -//We have to do transformation voodoo to make the text come out readable and in correct orientation... -//Some things to note here: if angle > 90 degrees, then we need to take the negative of the angle -//for our text. -painter->save(); -painter->translate(ctr.x, ctr.y); -int yOffset = -8; -//16 : printf("textHeight: %d\n", textHeight); - -//Fix text so it isn't upside down... -if ((angle > PI * 0.5) && (angle < PI * 1.5)) -{ - angle += PI; - yOffset = 18; -} -painter->rotate(angle * RADIANS_TO_DEGREES); -painter->scale(1.0, -1.0); -//painter->translate(-textWidth / 2, -24); -// painter->drawText(0, 0, textWidth, 20, Qt::AlignCenter, dimText); - // This version draws the y-coord from the baseline of the font - painter->DrawText(-textWidth / 2, yOffset, dimText); -//painter->setPen(QPen(QColor(0xFF, 0x20, 0x20), 1.0, Qt::SolidLine)); -//painter->drawLine(20, 0, -20, 0); -//painter->drawLine(0, 20, 0, -20); -painter->restore(); +#if 0 + QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude()); #else -// painter->DrawText(QRectF(QPointF(ctr.x, ctr.y), QPointF(ctr.x + textWidth, ctr.y + textHeight)), Qt::AlignVCenter, dimText); -// Now that we've taken our own good advice, maybe we should have the painter class -// do a nice abstracted text draw routine? :-) - painter->DrawAngledText(ctr, angle, dimText); + QString dimText; + double length = Vector(endpoint - position).Magnitude(); + + if (length < 12.0) + dimText = QString("%1\"").arg(length); + else + { + double feet = (double)((int)length / 12); + double inches = length - (feet * 12.0); + + if (inches == 0) + dimText = QString("%1'").arg(feet); + else + dimText = QString("%1' %2\"").arg(feet).arg(inches); + } #endif -/* -All of the preceeding makes me think that rather than try to compensate for Qt's unbelieveably -AWFUL decision to go with a wrong-handed graphics subsystem, it may be better to just stuff -all of that crap into some kind of subclass that handles all the nastiness behind the scenes. -I mean, really, all this crap just to get some proplerly rendered text on the screen? How -retarded is that? :-/ -*/ + painter->DrawAngledText(ctr, angle, dimText, size); } + /*virtual*/ Vector Dimension::Center(void) { // Technically, this is the midpoint but who are we to quibble? :-) @@ -129,114 +177,53 @@ retarded is that? :-/ return endpoint + v; } -/*virtual*/ bool Dimension::Collided(Vector /*point*/) + +/*virtual*/ bool Dimension::Collided(Vector point) { -#if 0 + // Someone told us to fuck off, so we'll fuck off. :-) + if (ignoreClicks) + return false; + + // We can assume this, since this is a mouse down event here. objectWasDragged = false; - Vector lineSegment = endpoint - position; - Vector v1 = point - position; - Vector v2 = point - endpoint; - double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance; - - // Geometric interpretation: - // pp is the paremeterized point on the vector ls where the perpendicular intersects ls. - // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls, - // then the perpendicular lies beyond the 2nd endpoint. - - if (parameterizedPoint < 0.0) - distance = v1.Magnitude(); - else if (parameterizedPoint > lineSegment.Magnitude()) - distance = v2.Magnitude(); - else // distance = ?Det?(ls, v1) / |ls| - distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude()); - - // If the segment endpoints are s and e, and the point is p, then the test for the perpendicular - // intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and - // {e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is - // computed by first computing the area of the triangle the three points form, then dividing by the - // length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the - // triangle formed by three points is the determinant of the following matrix: - // - // sx sy 1 - // ex ey 1 - // px py 1 - // - // By translating the start point to the origin, this can be rewritten as: - // By subtracting row 1 from all rows, you get the following: - // [because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted - // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!] - // - // 0 0 0 0 0 0 - // (ex - sx) (ey - sy) 0 ==> ex ey 0 - // (px - sx) (py - sy) 0 px py 0 - // - // which greatly simplifies the calculation of the determinant. - - if (state == OSInactive) + HitTest(point); + + // Now that we've done our hit testing on the non-snapped point, snap it if + // necessary... + if (snapToGrid) + point = SnapPointToGrid(point); + + if (hitPoint1) { -//printf("Line: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance); -//printf(" v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude()); -//printf(" point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z); -//printf(" \n", ); -//How to translate this into pixels from Document space??? -//Maybe we need to pass a scaling factor in here from the caller? That would make sense, as -//the caller knows about the zoom factor and all that good kinda crap - if (v1.Magnitude() < 10.0) - { - oldState = state; - state = OSSelected; - oldPoint = position; //maybe "position"? - draggingHandle1 = true; - return true; - } - else if (v2.Magnitude() < 10.0) - { - oldState = state; - state = OSSelected; - oldPoint = endpoint; //maybe "position"? - draggingHandle2 = true; - return true; - } - else if (distance < 2.0) - { - oldState = state; - state = OSSelected; - oldPoint = point; - dragging = true; - return true; - } + oldState = state; + state = OSSelected; + oldPoint = position; + draggingHandle1 = true; + return true; } - else if (state == OSSelected) + else if (hitPoint2) { - // Here we test for collision with handles as well! (SOON!) -/* -Like so: - if (v1.Magnitude() < 2.0) // Handle #1 - else if (v2.Magnitude() < 2.0) // Handle #2 -*/ - if (distance < 2.0) - { - oldState = state; -// state = OSInactive; - oldPoint = point; - dragging = true; - return true; - } + oldState = state; + state = OSSelected; + oldPoint = endpoint; + draggingHandle2 = true; + return true; } -#endif state = OSInactive; return false; } + /*virtual*/ void Dimension::PointerMoved(Vector point) { +#if 0 // We know this is true because mouse move messages don't come here unless // the object was actually clicked on--therefore we *know* we're being // dragged... objectWasDragged = true; - if (dragging) +/* if (dragging) { // Here we need to check whether or not we're dragging a handle or the object itself... Vector delta = point - oldPoint; @@ -247,7 +234,7 @@ Like so: oldPoint = point; needUpdate = true; } - else if (draggingHandle1) + else*/ if (draggingHandle1) { Vector delta = point - oldPoint; @@ -267,11 +254,35 @@ Like so: } else needUpdate = false; +#else + // Hit test tells us what we hit (if anything) through boolean variables. (It + // also tells us whether or not the state changed. --not any more) + SaveHitState(); + HitTest(point); + needUpdate = HitStateChanged(); + + objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2); + + if (objectWasDragged) + { + Vector delta = point - oldPoint; + + if (draggingHandle1)// || draggingLine) + position += delta; + + if (draggingHandle2)// || draggingLine) + endpoint += delta; + + oldPoint = point; + needUpdate = true; + } +#endif } + /*virtual*/ void Dimension::PointerReleased(void) { - if (draggingHandle1 || draggingHandle2) +/* if (draggingHandle1 || draggingHandle2) { // Set the length (in case the global state was set to fixed (or not)) if (Object::fixedLength) @@ -279,23 +290,22 @@ Like so: if (draggingHandle1) // startpoint { - Vector v = Vector(position - endpoint).Unit() * length; + Vector v = Vector(endpoint, position).Unit() * length; position = endpoint + v; } else // endpoint { -// Vector v1 = endpoint - position; - Vector v = Vector(endpoint - position).Unit() * length; + Vector v = Vector(position, endpoint).Unit() * length; endpoint = position + v; } } - else + else*/ { // Otherwise, we calculate the new length, just in case on the next move // it turns out to have a fixed length. :-) length = Vector(endpoint - position).Magnitude(); } - } +/* }*/ dragging = false; draggingHandle1 = false; @@ -311,18 +321,168 @@ about keeping track of old states... state = oldState; } -void Dimension::SetPoint1(Vector * v) + +/*virtual*/ bool Dimension::HitTest(Point point) { - point1 = v; - needUpdate = true; + hitPoint1 = hitPoint2 = false; +// Vector lineSegment(position, endpoint); + Vector v1(position, point); + Vector v2(endpoint, point); +// double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point); +// double distance; + +// if (t < 0.0) +// distance = v1.Magnitude(); +// else if (t > 1.0) +// distance = v2.Magnitude(); +// else + // distance = ?Det?(ls, v1) / |ls| +// distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) +// / lineSegment.Magnitude()); + + if ((v1.Magnitude() * Painter::zoom) < 8.0) + hitPoint1 = true; + else if ((v2.Magnitude() * Painter::zoom) < 8.0) + hitPoint2 = true; + + return (hitPoint1 || hitPoint2 ? true : false); } -void Dimension::SetPoint2(Vector * v) + +void Dimension::SaveHitState(void) { - point2 = v; - needUpdate = true; + oldHitPoint1 = hitPoint1; + oldHitPoint2 = hitPoint2; +// oldHitLine = hitLine; } + +bool Dimension::HitStateChanged(void) +{ + if ((hitPoint1 != oldHitPoint1) || (hitPoint2 != oldHitPoint2)) + return true; + + return false; +} + + +/*virtual*/ void Dimension::Enumerate(FILE * file) +{ + fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type); +} + + +/*virtual*/ Object * Dimension::Copy(void) +{ +#warning "!!! This doesn't take care of attached Dimensions !!!" +/* +This is a real problem. While having a pointer in the Dimension to this line's points +is fast & easy, it creates a huge problem when trying to replicate an object like this. + +Maybe a way to fix that then, is to have reference numbers instead of pointers. That +way, if you copy them, ... you might still have problems. Because you can't be sure if +a copy will be persistant or not, you then *definitely* do not want them to have the +same reference number. +*/ + + Dimension * d = new Dimension(position, endpoint, dimensionType, parent); + d->size = size; + return d; +} + + +// Dimensions are special: they contain exactly *two* points. Here, we check +// only for zero/non-zero in returning the correct points. +/*virtual*/ Vector Dimension::GetPointAtParameter(double parameter) +{ + if (parameter == 0) + return position; + + return endpoint; +} + + +/*virtual*/ void Dimension::Connect(Object * obj, double param) +{ + // There are four possibilities here... + // The param is only looking for 0 or 1 here. + if (point1.object == NULL && point2.object == NULL) + { + point1.object = obj; + point1.t = param; + } + else if (point1.object == NULL && point2.object != NULL) + { + if (point2.t == param) + point2.object = obj; + else + { + point1.object = obj; + point1.t = param; + } + } + else if (point1.object != NULL && point2.object == NULL) + { + if (point1.t == param) + point1.object = obj; + else + { + point2.object = obj; + point2.t = param; + } + } + else if (point1.object != NULL && point2.object != NULL) + { + if (point1.t == param) + point1.object = obj; + else + point2.object = obj; + } +} + + +/*virtual*/ void Dimension::Disconnect(Object * obj, double param) +{ + if (point1.object == obj && point1.t == param) + point1.object = NULL; + else if (point2.object == obj && point2.t == param) + point2.object = NULL; +} + + +/*virtual*/ void Dimension::DisconnectAll(Object * obj) +{ + if (point1.object == obj) + point1.object = NULL; + + if (point2.object == obj) + point2.object = NULL; +} + + +/*virtual*/ QRectF Dimension::Extents(void) +{ + Point p1 = position; + Point p2 = endpoint; + + if (point1.object) + p1 = point1.object->GetPointAtParameter(point1.t); + + if (point2.object) + p2 = point2.object->GetPointAtParameter(point2.t); + + return QRectF(QPointF(p1.x, p1.y), QPointF(p2.x, p2.y)); +} + + +#if 0 +/*virtual*/ ObjectType Dimension::Type(void) +{ + return OTDimension; +} +#endif + + void Dimension::FlipSides(void) { #if 0 @@ -330,9 +490,16 @@ void Dimension::FlipSides(void) position = endpoint; endpoint = tmp; #else - Vector * tmp = point1; + Connection tmp = point1; point1 = point2; point2 = tmp; +// double tmp = point1.t; +// point1.t = point2.t; +// point2.t = tmp; +// Object * tmp = point1.object; +// point1.object = point2.object; +// point2.object = tmp; #endif needUpdate = true; } +