Exemple #1
0
void GraphicsWindow::MouseScroll(double x, double y, int delta) {
    double offsetRight = offset.Dot(projRight);
    double offsetUp = offset.Dot(projUp);

    double righti = x/scale - offsetRight;
    double upi = y/scale - offsetUp;

    if(delta > 0) {
        scale *= 1.2;
    } else if(delta < 0) {
        scale /= 1.2;
    } else return;

    double rightf = x/scale - offsetRight;
    double upf = y/scale - offsetUp;

    offset = offset.Plus(projRight.ScaledBy(rightf - righti));
    offset = offset.Plus(projUp.ScaledBy(upf - upi));

    if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) {
        if(havePainted) {
            SS.ScheduleShowTW();
        }
    }
    havePainted = false;
    InvalidateGraphics();
}
Exemple #2
0
void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
    int i;
    double d, dmin = 1e12;
    Selection s;
    ZERO(&s);

    // Always do the entities; we might be dragging something that should
    // be auto-constrained, and we need the hover for that.
    for(i = 0; i < SK.entity.n; i++) {
        Entity *e = &(SK.entity.elem[i]);
        // Don't hover whatever's being dragged.
        if(e->h.request().v == pending.point.request().v) {
            // The one exception is when we're creating a new cubic; we
            // want to be able to hover the first point, because that's
            // how we turn it into a periodic spline.
            if(!e->IsPoint()) continue;
            if(!e->h.isFromRequest()) continue;
            Request *r = SK.GetRequest(e->h.request());
            if(r->type != Request::CUBIC) continue;
            if(r->extraPoints < 2) continue;
            if(e->h.v != r->h.entity(1).v) continue;
        }

        d = e->GetDistance(mp);
        if(d < 10 && d < dmin) {
            memset(&s, 0, sizeof(s));
            s.entity = e->h;
            dmin = d;
        }
    }

    // The constraints and faces happen only when nothing's in progress.
    if(pending.operation == 0) {
        // Constraints
        for(i = 0; i < SK.constraint.n; i++) {
            d = SK.constraint.elem[i].GetDistance(mp);
            if(d < 10 && d < dmin) {
                memset(&s, 0, sizeof(s));
                s.constraint = SK.constraint.elem[i].h;
                dmin = d;
            }
        }

        // Faces, from the triangle mesh; these are lowest priority
        if(s.constraint.v == 0 && s.entity.v == 0 && showShaded && showFaces) {
            Group *g = SK.GetGroup(activeGroup);
            SMesh *m = &(g->displayMesh);

            uint32_t v = m->FirstIntersectionWith(mp);
            if(v) {
                s.entity.v = v;
            }
        }
    }

    if(!s.Equals(&hover)) {
        hover = s;
        InvalidateGraphics();
    }
}
void TextWindow::ScreenChangeCanvasSizeAuto(int link, uint32_t v) {
    if(link == 't') {
        SS.exportCanvasSizeAuto = true;
    } else {
        SS.exportCanvasSizeAuto = false;
    }
    InvalidateGraphics();
}
Exemple #4
0
void TextWindow::ScreenDeleteStyle(int link, uint32_t v) {
    SS.UndoRemember();
    hStyle hs = { v };
    Style *s = SK.style.FindByIdNoOops(hs);
    if(s) {
        SK.style.RemoveById(hs);
        // And it will get recreated automatically if something is still using
        // the style, so no need to do anything else.
    }
    SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES);
    InvalidateGraphics();
}
Exemple #5
0
void GraphicsWindow::MouseLeftUp(double mx, double my) {
    orig.mouseDown = false;
    hoverWasSelectedOnMousedown = false;

    switch(pending.operation) {
        case DRAGGING_POINTS:
            SS.extraLine.draw = false;
            // fall through
        case DRAGGING_CONSTRAINT:
        case DRAGGING_NORMAL:
        case DRAGGING_RADIUS:
            ClearPending();
            InvalidateGraphics();
            break;

        case DRAGGING_MARQUEE:
            SelectByMarquee();
            ClearPending();
            InvalidateGraphics();
            break;

        case 0:
            // We need to clear the selection here, and not in the mouse down
            // event, since a mouse down without anything hovered could also
            // be the start of marquee selection. But don't do that on the
            // left click to cancel a context menu. The time delay is an ugly
            // hack.
            if(hover.IsEmpty() &&
                (contextMenuCancelTime == 0 ||
                 (GetMilliseconds() - contextMenuCancelTime) > 200))
            {
                ClearSelection();
            }
            break;

        default:
            break;  // do nothing
    }
}
Exemple #6
0
void GraphicsWindow::ToggleBool(bool *v) {
    *v = !*v;

    // The faces are shown as special stippling on the shaded triangle mesh,
    // so not meaningful to show them and hide the shaded.
    if(!showShaded) showFaces = false;

    // We might need to regenerate the mesh and edge list, since the edges
    // wouldn't have been generated if they were previously hidden.
    if(showEdges) (SK.GetGroup(activeGroup))->displayDirty = true;

    SS.GenerateAll();
    InvalidateGraphics();
    SS.ScheduleShowTW();
}
Exemple #7
0
void GraphicsWindow::ClearNonexistentSelectionItems(void) {
    bool change = false;
    Selection *s;
    selection.ClearTags();
    for(s = selection.First(); s; s = selection.NextAfter(s)) {
        if(s->constraint.v && !(SK.constraint.FindByIdNoOops(s->constraint))) {
            s->tag = 1;
            change = true;
        }
        if(s->entity.v && !(SK.entity.FindByIdNoOops(s->entity))) {
            s->tag = 1;
            change = true;
        }
    }
    selection.RemoveTagged();
    if(change) InvalidateGraphics();
}
Exemple #8
0
void GraphicsWindow::AnimateOnto(Quaternion quatf, Vector offsetf) {
    // Get our initial orientation and translation.
    Quaternion quat0 = Quaternion::From(projRight, projUp);
    Vector offset0 = offset;

    // Make sure we take the shorter of the two possible paths.
    double mp = (quatf.Minus(quat0)).Magnitude();
    double mm = (quatf.Plus(quat0)).Magnitude();
    if(mp > mm) {
        quatf = quatf.ScaledBy(-1);
        mp = mm;
    }
    double mo = (offset0.Minus(offsetf)).Magnitude()*scale;

    // Animate transition, unless it's a tiny move.
    int32_t dt = (mp < 0.01 && mo < 10) ? (-20) :
                     (int32_t)(100 + 1000*mp + 0.4*mo);
    // Don't ever animate for longer than 2000 ms; we can get absurdly
    // long translations (as measured in pixels) if the user zooms out, moves,
    // and then zooms in again.
    if(dt > 2000) dt = 2000;
    int64_t tn, t0 = GetMilliseconds();
    double s = 0;
    Quaternion dq = quatf.Times(quat0.Inverse());
    do {
        offset = (offset0.ScaledBy(1 - s)).Plus(offsetf.ScaledBy(s));
        Quaternion quat = (dq.ToThe(s)).Times(quat0);
        quat = quat.WithMagnitude(1);

        projRight = quat.RotationU();
        projUp    = quat.RotationV();
        PaintGraphics();

        tn = GetMilliseconds();
        s = (tn - t0)/((double)dt);
    } while((tn - t0) < dt);

    projRight = quatf.RotationU();
    projUp = quatf.RotationV();
    offset = offsetf;
    InvalidateGraphics();
    // If the view screen is open, then we need to refresh it.
    SS.ScheduleShowTW();
}
Exemple #9
0
void Style::AssignSelectionToStyle(uint32_t v) {
    bool showError = false;
    SS.GW.GroupSelection();

    SS.UndoRemember();
    int i;
    for(i = 0; i < SS.GW.gs.entities; i++) {
        hEntity he = SS.GW.gs.entity[i];
        Entity *e = SK.GetEntity(he);
        if(!e->IsStylable()) continue;

        if(!he.isFromRequest()) {
            showError = true;
            continue;
        }

        hRequest hr = he.request();
        Request *r = SK.GetRequest(hr);
        r->style.v = v;
        SS.MarkGroupDirty(r->group);
    }
    for(i = 0; i < SS.GW.gs.constraints; i++) {
        hConstraint hc = SS.GW.gs.constraint[i];
        Constraint *c = SK.GetConstraint(hc);
        if(!c->IsStylable()) continue;

        c->disp.style.v = v;
    }

    if(showError) {
        Error("Can't assign style to an entity that's derived from another "
              "entity; try assigning a style to this entity's parent.");
    }

    SS.GW.ClearSelection();
    InvalidateGraphics();
    SS.ScheduleGenerateAll();

    // And show that style's info screen in the text window.
    SS.TW.GoToScreen(TextWindow::Screen::STYLE_INFO);
    SS.TW.shown.style.v = v;
    SS.ScheduleShowTW();
}
Exemple #10
0
void TextWindow::ScreenStepDimGo(int link, DWORD v) {
    hConstraint hc = SS.TW.shown.constraint;
    Constraint *c = SK.constraint.FindByIdNoOops(hc);
    if(c) {
        SS.UndoRemember();
        double start = c->valA, finish = SS.TW.shown.dimFinish;
        int i, n = SS.TW.shown.dimSteps;
        for(i = 1; i <= n; i++) {
            c = SK.GetConstraint(hc);
            c->valA = start + ((finish - start)*i)/n;
            SS.MarkGroupDirty(c->group);
            SS.GenerateAll();
            if(!SS.AllGroupsOkay()) {
                // Failed to solve, so quit
                break;
            }
            PaintGraphics();
        }
    }
    InvalidateGraphics();
    SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
}
Exemple #11
0
void GraphicsWindow::SpaceNavigatorButtonUp(void) {
    ZoomToFit(false);
    InvalidateGraphics();
}
Exemple #12
0
void TextWindow::ScreenChangeStyleYesNo(int link, uint32_t v) {
    SS.UndoRemember();
    hStyle hs = { v };
    Style *s = Style::Get(hs);
    switch(link) {
        // Units for the width
        case 'w':
            if(s->widthAs != Style::UnitsAs::MM) {
                s->widthAs = Style::UnitsAs::MM;
                s->width /= SS.GW.scale;
                s->stippleScale /= SS.GW.scale;
            }
            break;
        case 'W':
            if(s->widthAs != Style::UnitsAs::PIXELS) {
                s->widthAs = Style::UnitsAs::PIXELS;
                s->width *= SS.GW.scale;
                s->stippleScale *= SS.GW.scale;
            }
            break;

        // Units for the height
        case 'g':
            if(s->textHeightAs != Style::UnitsAs::MM) {
                s->textHeightAs = Style::UnitsAs::MM;
                s->textHeight /= SS.GW.scale;
            }
            break;

        case 'G':
            if(s->textHeightAs != Style::UnitsAs::PIXELS) {
                s->textHeightAs = Style::UnitsAs::PIXELS;
                s->textHeight *= SS.GW.scale;
            }
            break;

        case 'e':
            s->exportable = !(s->exportable);
            break;

        case 'v':
            s->visible = !(s->visible);
            break;

        case 'f':
            s->filled = !(s->filled);
            break;

        // Horizontal text alignment
        case 'L':
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin |  (uint32_t)Style::TextOrigin::LEFT);
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::RIGHT);
            break;
        case 'H':
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::LEFT);
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::RIGHT);
            break;
        case 'R':
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::LEFT);
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin |  (uint32_t)Style::TextOrigin::RIGHT);
            break;

        // Vertical text alignment
        case 'B':
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin |  (uint32_t)Style::TextOrigin::BOT);
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::TOP);
            break;
        case 'V':
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::BOT);
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::TOP);
            break;
        case 'T':
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::BOT);
            s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin |  (uint32_t)Style::TextOrigin::TOP);
            break;
    }
    InvalidateGraphics();
}
Exemple #13
0
void GraphicsWindow::SpaceNavigatorMoved(double tx, double ty, double tz,
                                         double rx, double ry, double rz,
                                         bool shiftDown)
{
    if(!havePainted) return;
    Vector out = projRight.Cross(projUp);

    // rotation vector is axis of rotation, and its magnitude is angle
    Vector aa = Vector::From(rx, ry, rz);
    // but it's given with respect to screen projection frame
    aa = aa.ScaleOutOfCsys(projRight, projUp, out);
    double aam = aa.Magnitude();
    if(aam > 0.0) aa = aa.WithMagnitude(1);

    // This can either transform our view, or transform an imported part.
    GroupSelection();
    Entity *e = NULL;
    Group *g = NULL;
    if(gs.points == 1   && gs.n == 1) e = SK.GetEntity(gs.point [0]);
    if(gs.entities == 1 && gs.n == 1) e = SK.GetEntity(gs.entity[0]);
    if(e) g = SK.GetGroup(e->group);
    if(g && g->type == Group::IMPORTED && !shiftDown) {
        // Apply the transformation to an imported part. Gain down the Z
        // axis, since it's hard to see what you're doing on that one since
        // it's normal to the screen.
        Vector t = projRight.ScaledBy(tx/scale).Plus(
                   projUp   .ScaledBy(ty/scale).Plus(
                   out      .ScaledBy(0.1*tz/scale)));
        Quaternion q = Quaternion::From(aa, aam);

        // If we go five seconds without SpaceNavigator input, or if we've
        // switched groups, then consider that a new action and save an undo
        // point.
        int64_t now = GetMilliseconds();
        if(now - lastSpaceNavigatorTime > 5000 ||
           lastSpaceNavigatorGroup.v != g->h.v)
        {
            SS.UndoRemember();
        }

        g->TransformImportedBy(t, q);

        lastSpaceNavigatorTime = now;
        lastSpaceNavigatorGroup = g->h;
        SS.MarkGroupDirty(g->h);
        SS.ScheduleGenerateAll();
    } else {
        // Apply the transformation to the view of the everything. The
        // x and y components are translation; but z component is scale,
        // not translation, or else it would do nothing in a parallel
        // projection
        offset = offset.Plus(projRight.ScaledBy(tx/scale));
        offset = offset.Plus(projUp.ScaledBy(ty/scale));
        scale *= exp(0.001*tz);

        if(aam > 0.0) {
            projRight = projRight.RotatedAbout(aa, -aam);
            projUp    = projUp.   RotatedAbout(aa, -aam);
            NormalizeProjectionVectors();
        }
    }

    havePainted = false;
    InvalidateGraphics();
}
Exemple #14
0
void Constraint::MenuConstrain(int id) {
    Constraint c;
    ZERO(&c);
    c.group = SS.GW.activeGroup;
    c.workplane = SS.GW.ActiveWorkplane();

    SS.GW.GroupSelection();
#define gs (SS.GW.gs)

    switch(id) {
        case GraphicsWindow::MNU_DISTANCE_DIA: {
            if(gs.points == 2 && gs.n == 2) {
                c.type = PT_PT_DISTANCE;
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 1 && gs.n == 1) {
                c.type = PT_PT_DISTANCE;
                Entity *e = SK.GetEntity(gs.entity[0]);
                c.ptA = e->point[0];
                c.ptB = e->point[1];
            } else if(gs.vectors == 1 && gs.points == 2 && gs.n == 3) {
                c.type = PROJ_PT_DISTANCE;
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
                c.entityA = gs.vector[0];
            } else if(gs.workplanes == 1 && gs.points == 1 && gs.n == 2) {
                c.type = PT_PLANE_DISTANCE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
                c.type = PT_LINE_DISTANCE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.faces == 1 && gs.points == 1 && gs.n == 2) {
                c.type = PT_FACE_DISTANCE;
                c.ptA = gs.point[0];
                c.entityA = gs.face[0];
            } else if(gs.circlesOrArcs == 1 && gs.n == 1) {
                c.type = DIAMETER;
                c.entityA = gs.entity[0];
            } else {
                Error(
"Bad selection for distance / diameter constraint. This "
"constraint can apply to:\n\n"
"    * two points (distance between points)\n"
"    * a line segment (length)\n"
"    * two points and a line segment or normal (projected distance)\n"
"    * a workplane and a point (minimum distance)\n"
"    * a line segment and a point (minimum distance)\n"
"    * a plane face and a point (minimum distance)\n"
"    * a circle or an arc (diameter)\n");
                return;
            }
            if(c.type == PT_PT_DISTANCE || c.type == PROJ_PT_DISTANCE) {
                Vector n = SS.GW.projRight.Cross(SS.GW.projUp);
                Vector a = SK.GetEntity(c.ptA)->PointGetNum();
                Vector b = SK.GetEntity(c.ptB)->PointGetNum();
                c.disp.offset = n.Cross(a.Minus(b));
                c.disp.offset = (c.disp.offset).WithMagnitude(50/SS.GW.scale);
            } else {
                c.disp.offset = Vector::From(0, 0, 0);
            }

            c.valA = 0;
            c.ModifyToSatisfy();
            AddConstraint(&c);
            break;
        }

        case GraphicsWindow::MNU_ON_ENTITY:
            if(gs.points == 2 && gs.n == 2) {
                c.type = POINTS_COINCIDENT;
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
            } else if(gs.points == 1 && gs.workplanes == 1 && gs.n == 2) {
                c.type = PT_IN_PLANE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.points == 1 && gs.lineSegments == 1 && gs.n == 2) {
                c.type = PT_ON_LINE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.points == 1 && gs.circlesOrArcs == 1 && gs.n == 2) {
                c.type = PT_ON_CIRCLE;
                c.ptA = gs.point[0];
                c.entityA = gs.entity[0];
            } else if(gs.points == 1 && gs.faces == 1 && gs.n == 2) {
                c.type = PT_ON_FACE;
                c.ptA = gs.point[0];
                c.entityA = gs.face[0];
            } else {
                Error("Bad selection for on point / curve / plane constraint. "
                      "This constraint can apply to:\n\n"
                      "    * two points (points coincident)\n"
                      "    * a point and a workplane (point in plane)\n"
                      "    * a point and a line segment (point on line)\n"
                      "    * a point and a circle or arc (point on curve)\n"
                      "    * a point and a plane face (point on face)\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_EQUAL:
            if(gs.lineSegments == 2 && gs.n == 2) {
                c.type = EQUAL_LENGTH_LINES;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
            } else if(gs.lineSegments == 2 && gs.points == 2 && gs.n == 4) {
                c.type = EQ_PT_LN_DISTANCES;
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];
                c.entityB = gs.entity[1];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 1 && gs.points == 2 && gs.n == 3) {
                // The same line segment for the distances, but different
                // points.
                c.type = EQ_PT_LN_DISTANCES;
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];
                c.entityB = gs.entity[0];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 2 && gs.points == 1 && gs.n == 3) {
                c.type = EQ_LEN_PT_LINE_D;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
                c.ptA = gs.point[0];
            } else if(gs.vectors == 4 && gs.n == 4) {
                c.type = EQUAL_ANGLE;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
                c.entityC = gs.vector[2];
                c.entityD = gs.vector[3];
            } else if(gs.vectors == 3 && gs.n == 3) {
                c.type = EQUAL_ANGLE;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
                c.entityC = gs.vector[1];
                c.entityD = gs.vector[2];
            } else if(gs.circlesOrArcs == 2 && gs.n == 2) {
                c.type = EQUAL_RADIUS;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
            } else if(gs.arcs == 1 && gs.lineSegments == 1 && gs.n == 2) {
                c.type = EQUAL_LINE_ARC_LEN;
                if(SK.GetEntity(gs.entity[0])->type == Entity::ARC_OF_CIRCLE) {
                    c.entityA = gs.entity[1];
                    c.entityB = gs.entity[0];
                } else {
                    c.entityA = gs.entity[0];
                    c.entityB = gs.entity[1];
                }
            } else {
                Error("Bad selection for equal length / radius constraint. "
                      "This constraint can apply to:\n\n"
                      "    * two line segments (equal length)\n"
                      "    * two line segments and two points "
                              "(equal point-line distances)\n"
                      "    * a line segment and two points "
                              "(equal point-line distances)\n"
                      "    * a line segment, and a point and line segment "
                              "(point-line distance equals length)\n"
                      "    * four line segments or normals "
                              "(equal angle between A,B and C,D)\n"
                      "    * three line segments or normals "
                              "(equal angle between A,B and B,C)\n"
                      "    * two circles or arcs (equal radius)\n"
                      "    * a line segment and an arc "
                              "(line segment length equals arc length)\n");
                return;
            }
            if(c.type == EQUAL_ANGLE) {
                // Infer the nearest supplementary angle from the sketch.
                Vector a1 = SK.GetEntity(c.entityA)->VectorGetNum(),
                       b1 = SK.GetEntity(c.entityB)->VectorGetNum(),
                       a2 = SK.GetEntity(c.entityC)->VectorGetNum(),
                       b2 = SK.GetEntity(c.entityD)->VectorGetNum();
                double d1 = a1.Dot(b1), d2 = a2.Dot(b2);

                if(d1*d2 < 0) {
                    c.other = true;
                }
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_RATIO:
            if(gs.lineSegments == 2 && gs.n == 2) {
                c.type = LENGTH_RATIO;
                c.entityA = gs.entity[0];
                c.entityB = gs.entity[1];
            } else {
                Error("Bad selection for length ratio constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two line segments\n");
                return;
            }

            c.valA = 0;
            c.ModifyToSatisfy();
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_AT_MIDPOINT:
            if(gs.lineSegments == 1 && gs.points == 1 && gs.n == 2) {
                c.type = AT_MIDPOINT;
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];

                // If a point is at-midpoint, then no reason to also constrain
                // it on-line; so auto-remove that.
                DeleteAllConstraintsFor(PT_ON_LINE, c.entityA, c.ptA);
            } else if(gs.lineSegments == 1 && gs.workplanes == 1 && gs.n == 2) {
                c.type = AT_MIDPOINT;
                int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
                c.entityA = gs.entity[i];
                c.entityB = gs.entity[1-i];
            } else {
                Error("Bad selection for at midpoint constraint. This "
                      "constraint can apply to:\n\n"
                      "    * a line segment and a point "
                            "(point at midpoint)\n"
                      "    * a line segment and a workplane "
                            "(line's midpoint on plane)\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_SYMMETRIC:
            if(gs.points == 2 &&
                                ((gs.workplanes == 1 && gs.n == 3) ||
                                 (gs.n == 2)))
            {
                c.entityA = gs.entity[0];
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
            } else if(gs.lineSegments == 1 &&
                                ((gs.workplanes == 1 && gs.n == 2) ||
                                 (gs.n == 1)))
            {
                int i = SK.GetEntity(gs.entity[0])->IsWorkplane() ? 1 : 0;
                Entity *line = SK.GetEntity(gs.entity[i]);
                c.entityA = gs.entity[1-i];
                c.ptA = line->point[0];
                c.ptB = line->point[1];
            } else if(SS.GW.LockedInWorkplane()
                        && gs.lineSegments == 2 && gs.n == 2)
            {
                Entity *l0 = SK.GetEntity(gs.entity[0]),
                       *l1 = SK.GetEntity(gs.entity[1]);

                if((l1->group.v != SS.GW.activeGroup.v) ||
                   (l1->construction && !(l0->construction)))
                {
                    SWAP(Entity *, l0, l1);
                }
                c.ptA = l1->point[0];
                c.ptB = l1->point[1];
                c.entityA = l0->h;
                c.type = SYMMETRIC_LINE;
            } else if(SS.GW.LockedInWorkplane()
                        && gs.lineSegments == 1 && gs.points == 2 && gs.n == 3)
            {
                c.ptA = gs.point[0];
                c.ptB = gs.point[1];
                c.entityA = gs.entity[0];
                c.type = SYMMETRIC_LINE;
            } else {
                Error("Bad selection for symmetric constraint. This constraint "
                      "can apply to:\n\n"
                      "    * two points or a line segment "
                          "(symmetric about workplane's coordinate axis)\n"
                      "    * line segment, and two points or a line segment "
                          "(symmetric about line segment)\n"
                      "    * workplane, and two points or a line segment "
                          "(symmetric about workplane)\n");
                return;
            }
            if(c.type != 0) {
                // Already done, symmetry about a line segment in a workplane
            } else if(c.entityA.v == Entity::NO_ENTITY.v) {
                // Horizontal / vertical symmetry, implicit symmetry plane
                // normal to the workplane
                if(c.workplane.v == Entity::FREE_IN_3D.v) {
                    Error("Must be locked in to workplane when constraining "
                          "symmetric without an explicit symmetry plane.");
                    return;
                }
                Vector pa = SK.GetEntity(c.ptA)->PointGetNum();
                Vector pb = SK.GetEntity(c.ptB)->PointGetNum();
                Vector dp = pa.Minus(pb);
                EntityBase *norm = SK.GetEntity(c.workplane)->Normal();;
                Vector u = norm->NormalU(), v = norm->NormalV();
                if(fabs(dp.Dot(u)) > fabs(dp.Dot(v))) {
                    c.type = SYMMETRIC_HORIZ;
                } else {
                    c.type = SYMMETRIC_VERT;
                }
                if(gs.lineSegments == 1) {
                    // If this line segment is already constrained horiz or
                    // vert, then auto-remove that redundant constraint.
                    DeleteAllConstraintsFor(HORIZONTAL, (gs.entity[0]),
                        Entity::NO_ENTITY);
                    DeleteAllConstraintsFor(VERTICAL, (gs.entity[0]),
                        Entity::NO_ENTITY);

                }
            } else {
                // Symmetry with a symmetry plane specified explicitly.
                c.type = SYMMETRIC;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_VERTICAL:
        case GraphicsWindow::MNU_HORIZONTAL: {
            hEntity ha, hb;
            if(c.workplane.v == Entity::FREE_IN_3D.v) {
                Error("Select workplane before constraining horiz/vert.");
                return;
            }
            if(gs.lineSegments == 1 && gs.n == 1) {
                c.entityA = gs.entity[0];
                Entity *e = SK.GetEntity(c.entityA);
                ha = e->point[0];
                hb = e->point[1];
            } else if(gs.points == 2 && gs.n == 2) {
                ha = c.ptA = gs.point[0];
                hb = c.ptB = gs.point[1];
            } else {
                Error("Bad selection for horizontal / vertical constraint. "
                      "This constraint can apply to:\n\n"
                      "    * two points\n"
                      "    * a line segment\n");
                return;
            }
            if(id == GraphicsWindow::MNU_HORIZONTAL) {
                c.type = HORIZONTAL;
            } else {
                c.type = VERTICAL;
            }
            AddConstraint(&c);
            break;
        }

        case GraphicsWindow::MNU_ORIENTED_SAME: {
            if(gs.anyNormals == 2 && gs.n == 2) {
                c.type = SAME_ORIENTATION;
                c.entityA = gs.anyNormal[0];
                c.entityB = gs.anyNormal[1];
            } else {
                Error("Bad selection for same orientation constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two normals\n");
                return;
            }
            SS.UndoRemember();

            Entity *nfree = SK.GetEntity(c.entityA);
            Entity *nref  = SK.GetEntity(c.entityB);
            if(nref->group.v == SS.GW.activeGroup.v) {
                SWAP(Entity *, nref, nfree);
            }
            if(nfree->group.v == SS.GW.activeGroup.v &&
               nref ->group.v != SS.GW.activeGroup.v)
            {
                // nfree is free, and nref is locked (since it came from a
                // previous group); so let's force nfree aligned to nref,
                // and make convergence easy
                Vector ru = nref ->NormalU(), rv = nref ->NormalV();
                Vector fu = nfree->NormalU(), fv = nfree->NormalV();

                if(fabs(fu.Dot(ru)) < fabs(fu.Dot(rv))) {
                    // There might be an odd*90 degree rotation about the
                    // normal vector; allow that, since the numerical
                    // constraint does
                    SWAP(Vector, ru, rv);
                }
                fu = fu.Dot(ru) > 0 ? ru : ru.ScaledBy(-1);
                fv = fv.Dot(rv) > 0 ? rv : rv.ScaledBy(-1);

                nfree->NormalForceTo(Quaternion::From(fu, fv));
            }
            AddConstraint(&c, false);
            break;
        }

        case GraphicsWindow::MNU_OTHER_ANGLE:
            if(gs.constraints == 1 && gs.n == 0) {
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
                if(c->type == ANGLE) {
                    SS.UndoRemember();
                    c->other = !(c->other);
                    c->ModifyToSatisfy();
                    break;
                }
                if(c->type == EQUAL_ANGLE) {
                    SS.UndoRemember();
                    c->other = !(c->other);
                    SS.MarkGroupDirty(c->group);
                    SS.ScheduleGenerateAll();
                    break;
                }
            }
            Error("Must select an angle constraint.");
            return;

        case GraphicsWindow::MNU_REFERENCE:
            if(gs.constraints == 1 && gs.n == 0) {
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
                if(c->HasLabel() && c->type != COMMENT) {
                    (c->reference) = !(c->reference);
                    SK.GetGroup(c->group)->clean = false;
                    SS.GenerateAll();
                    break;
                }
            }
            Error("Must select a constraint with associated label.");
            return;

        case GraphicsWindow::MNU_ANGLE: {
            if(gs.vectors == 2 && gs.n == 2) {
                c.type = ANGLE;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
                c.valA = 0;
            } else {
                Error("Bad selection for angle constraint. This constraint "
                      "can apply to:\n\n"
                      "    * two line segments\n"
                      "    * a line segment and a normal\n"
                      "    * two normals\n");
                return;
            }

            Entity *ea = SK.GetEntity(c.entityA),
                   *eb = SK.GetEntity(c.entityB);
            if(ea->type == Entity::LINE_SEGMENT &&
               eb->type == Entity::LINE_SEGMENT)
            {
                Vector a0 = SK.GetEntity(ea->point[0])->PointGetNum(),
                       a1 = SK.GetEntity(ea->point[1])->PointGetNum(),
                       b0 = SK.GetEntity(eb->point[0])->PointGetNum(),
                       b1 = SK.GetEntity(eb->point[1])->PointGetNum();
                if(a0.Equals(b0) || a1.Equals(b1)) {
                    // okay, vectors should be drawn in same sense
                } else if(a0.Equals(b1) || a1.Equals(b0)) {
                    // vectors are in opposite sense
                    c.other = true;
                } else {
                    // no shared point; not clear which intersection to draw
                }
            }
            c.ModifyToSatisfy();
            AddConstraint(&c);
            break;
        }

        case GraphicsWindow::MNU_PARALLEL:
            if(gs.vectors == 2 && gs.n == 2) {
                c.type = PARALLEL;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
            } else if(gs.lineSegments == 1 && gs.arcs == 1 && gs.n == 2) {
                Entity *line = SK.GetEntity(gs.entity[0]);
                Entity *arc  = SK.GetEntity(gs.entity[1]);
                if(line->type == Entity::ARC_OF_CIRCLE) {
                    SWAP(Entity *, line, arc);
                }
                Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
                       l1 = SK.GetEntity(line->point[1])->PointGetNum();
                Vector a1 = SK.GetEntity(arc->point[1])->PointGetNum(),
                       a2 = SK.GetEntity(arc->point[2])->PointGetNum();

                if(l0.Equals(a1) || l1.Equals(a1)) {
                    c.other = false;
                } else if(l0.Equals(a2) || l1.Equals(a2)) {
                    c.other = true;
                } else {
                    Error("The tangent arc and line segment must share an "
                          "endpoint. Constrain them with Constrain -> "
                          "On Point before constraining tangent.");
                    return;
                }
                c.type = ARC_LINE_TANGENT;
                c.entityA = arc->h;
                c.entityB = line->h;
            } else if(gs.lineSegments == 1 && gs.cubics == 1 && gs.n == 2) {
                Entity *line  = SK.GetEntity(gs.entity[0]);
                Entity *cubic = SK.GetEntity(gs.entity[1]);
                if(line->type == Entity::CUBIC) {
                    SWAP(Entity *, line, cubic);
                }
                Vector l0 = SK.GetEntity(line->point[0])->PointGetNum(),
                       l1 = SK.GetEntity(line->point[1])->PointGetNum();
                Vector as = cubic->CubicGetStartNum(),
                       af = cubic->CubicGetFinishNum();

                if(l0.Equals(as) || l1.Equals(as)) {
                    c.other = false;
                } else if(l0.Equals(af) || l1.Equals(af)) {
                    c.other = true;
                } else {
                    Error("The tangent cubic and line segment must share an "
                          "endpoint. Constrain them with Constrain -> "
                          "On Point before constraining tangent.");
                    return;
                }
                c.type = CUBIC_LINE_TANGENT;
                c.entityA = cubic->h;
                c.entityB = line->h;
            } else if(gs.cubics + gs.arcs == 2 && gs.n == 2) {
                if(!SS.GW.LockedInWorkplane()) {
                    Error("Curve-curve tangency must apply in workplane.");
                    return;
                }
                Entity *eA = SK.GetEntity(gs.entity[0]),
                       *eB = SK.GetEntity(gs.entity[1]);
                Vector as = eA->EndpointStart(),
                       af = eA->EndpointFinish(),
                       bs = eB->EndpointStart(),
                       bf = eB->EndpointFinish();
                if(as.Equals(bs)) {
                    c.other = false; c.other2 = false;
                } else if(as.Equals(bf)) {
                    c.other = false; c.other2 = true;
                } else if(af.Equals(bs)) {
                    c.other = true; c.other2 = false;
                } else if(af.Equals(bf)) {
                    c.other = true; c.other2 = true;
                } else {
                    Error("The curves must share an endpoint. Constrain them "
                          "with Constrain -> On Point before constraining "
                          "tangent.");
                    return;
                }
                c.type = CURVE_CURVE_TANGENT;
                c.entityA = eA->h;
                c.entityB = eB->h;
            } else {
                Error("Bad selection for parallel / tangent constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two line segments (parallel)\n"
                      "    * a line segment and a normal (parallel)\n"
                      "    * two normals (parallel)\n"
                      "    * two line segments, arcs, or beziers, that share "
                            "an endpoint (tangent)\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_PERPENDICULAR:
            if(gs.vectors == 2 && gs.n == 2) {
                c.type = PERPENDICULAR;
                c.entityA = gs.vector[0];
                c.entityB = gs.vector[1];
            } else {
                Error("Bad selection for perpendicular constraint. This "
                      "constraint can apply to:\n\n"
                      "    * two line segments\n"
                      "    * a line segment and a normal\n"
                      "    * two normals\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_WHERE_DRAGGED:
            if(gs.points == 1 && gs.n == 1) {
                c.type = WHERE_DRAGGED;
                c.ptA = gs.point[0];
            } else {
                Error("Bad selection for lock point where dragged constraint. "
                      "This constraint can apply to:\n\n"
                      "    * a point\n");
                return;
            }
            AddConstraint(&c);
            break;

        case GraphicsWindow::MNU_COMMENT:
            SS.GW.pending.operation = GraphicsWindow::MNU_COMMENT;
            SS.GW.pending.description = "click center of comment text";
            SS.ScheduleShowTW();
            break;

        default: oops();
    }

    SS.GW.ClearSelection();
    InvalidateGraphics();
}
Exemple #15
0
void SolveSpaceUI::GenerateAll(Generate type, bool andFindFree, bool genForBBox) {
    int first, last, i, j;

    SK.groupOrder.Clear();
    for(int i = 0; i < SK.group.n; i++)
        SK.groupOrder.Add(&SK.group.elem[i].h);
    std::sort(&SK.groupOrder.elem[0], &SK.groupOrder.elem[SK.groupOrder.n],
        [](const hGroup &ha, const hGroup &hb) {
            return SK.GetGroup(ha)->order < SK.GetGroup(hb)->order;
        });

    switch(type) {
        case Generate::DIRTY: {
            first = INT_MAX;
            last  = 0;

            // Start from the first dirty group, and solve until the active group,
            // since all groups after the active group are hidden.
            for(i = 0; i < SK.groupOrder.n; i++) {
                Group *g = SK.GetGroup(SK.groupOrder.elem[i]);
                if((!g->clean) || !g->IsSolvedOkay()) {
                    first = min(first, i);
                }
                if(g->h.v == SS.GW.activeGroup.v) {
                    last = i;
                }
            }
            if(first == INT_MAX || last == 0) {
                // All clean; so just regenerate the entities, and don't solve anything.
                first = -1;
                last  = -1;
            } else {
                SS.nakedEdges.Clear();
            }
            break;
        }

        case Generate::ALL:
            first = 0;
            last  = INT_MAX;
            break;

        case Generate::REGEN:
            first = -1;
            last  = -1;
            break;

        case Generate::UNTIL_ACTIVE: {
            for(i = 0; i < SK.groupOrder.n; i++) {
                if(SK.groupOrder.elem[i].v == SS.GW.activeGroup.v)
                    break;
            }

            first = 0;
            last  = i;
            break;
        }
    }

    // If we're generating entities for display, first we need to find
    // the bounding box to turn relative chord tolerance to absolute.
    if(!SS.exportMode && !genForBBox) {
        GenerateAll(type, andFindFree, /*genForBBox=*/true);
        BBox box = SK.CalculateEntityBBox(/*includeInvisibles=*/true);
        Vector size = box.maxp.Minus(box.minp);
        double maxSize = std::max({ size.x, size.y, size.z });
        chordTolCalculated = maxSize * chordTol / 100.0;
    }

    // Remove any requests or constraints that refer to a nonexistent
    // group; can check those immediately, since we know what the list
    // of groups should be.
    while(PruneOrphans())
        ;

    // Don't lose our numerical guesses when we regenerate.
    IdList<Param,hParam> prev = {};
    SK.param.MoveSelfInto(&prev);
    SK.entity.Clear();

    for(i = 0; i < SK.groupOrder.n; i++) {
        Group *g = SK.GetGroup(SK.groupOrder.elem[i]);

        // The group may depend on entities or other groups, to define its
        // workplane geometry or for its operands. Those must already exist
        // in a previous group, so check them before generating.
        if(PruneGroups(g->h))
            goto pruned;

        for(j = 0; j < SK.request.n; j++) {
            Request *r = &(SK.request.elem[j]);
            if(r->group.v != g->h.v) continue;

            r->Generate(&(SK.entity), &(SK.param));
        }
        g->Generate(&(SK.entity), &(SK.param));

        // The requests and constraints depend on stuff in this or the
        // previous group, so check them after generating.
        if(PruneRequests(g->h) || PruneConstraints(g->h))
            goto pruned;

        // Use the previous values for params that we've seen before, as
        // initial guesses for the solver.
        for(j = 0; j < SK.param.n; j++) {
            Param *newp = &(SK.param.elem[j]);
            if(newp->known) continue;

            Param *prevp = prev.FindByIdNoOops(newp->h);
            if(prevp) {
                newp->val = prevp->val;
                newp->free = prevp->free;
            }
        }

        if(g->h.v == Group::HGROUP_REFERENCES.v) {
            ForceReferences();
            g->solved.how = SolveResult::OKAY;
            g->clean = true;
        } else {
            if(i >= first && i <= last) {
                // The group falls inside the range, so really solve it,
                // and then regenerate the mesh based on the solved stuff.
                if(genForBBox) {
                    SolveGroup(g->h, andFindFree);
                } else {
                    g->GenerateLoops();
                    g->GenerateShellAndMesh();
                    g->clean = true;
                }
            } else {
                // The group falls outside the range, so just assume that
                // it's good wherever we left it. The mesh is unchanged,
                // and the parameters must be marked as known.
                for(j = 0; j < SK.param.n; j++) {
                    Param *newp = &(SK.param.elem[j]);

                    Param *prevp = prev.FindByIdNoOops(newp->h);
                    if(prevp) newp->known = true;
                }
            }
        }
    }

    // And update any reference dimensions with their new values
    for(i = 0; i < SK.constraint.n; i++) {
        Constraint *c = &(SK.constraint.elem[i]);
        if(c->reference) {
            c->ModifyToSatisfy();
        }
    }

    // Make sure the point that we're tracing exists.
    if(traced.point.v && !SK.entity.FindByIdNoOops(traced.point)) {
        traced.point = Entity::NO_ENTITY;
    }
    // And if we're tracing a point, add its new value to the path
    if(traced.point.v) {
        Entity *pt = SK.GetEntity(traced.point);
        traced.path.AddPoint(pt->PointGetNum());
    }

    prev.Clear();
    InvalidateGraphics();

    // Remove nonexistent selection items, for same reason we waited till
    // the end to put up a dialog box.
    GW.ClearNonexistentSelectionItems();

    if(deleted.requests > 0 || deleted.constraints > 0 || deleted.groups > 0) {
        // All sorts of interesting things could have happened; for example,
        // the active group or active workplane could have been deleted. So
        // clear all that out.
        if(deleted.groups > 0) {
            SS.TW.ClearSuper();
        }
        ScheduleShowTW();
        GW.ClearSuper();

        // People get annoyed if I complain whenever they delete any request,
        // and I otherwise will, since those always come with pt-coincident
        // constraints.
        if(deleted.requests > 0 || deleted.nonTrivialConstraints > 0 ||
           deleted.groups > 0)
        {
            // Don't display any errors until we've regenerated fully. The
            // sketch is not necessarily in a consistent state until we've
            // pruned any orphaned etc. objects, and the message loop for the
            // messagebox could allow us to repaint and crash. But now we must
            // be fine.
            Message("Additional sketch elements were deleted, because they "
                    "depend on the element that was just deleted explicitly. "
                    "These include: \n"
                    "     %d request%s\n"
                    "     %d constraint%s\n"
                    "     %d group%s"
                    "%s",
                       deleted.requests, deleted.requests == 1 ? "" : "s",
                       deleted.constraints, deleted.constraints == 1 ? "" : "s",
                       deleted.groups, deleted.groups == 1 ? "" : "s",
                       undo.cnt > 0 ? "\n\nChoose Edit -> Undo to undelete all elements." : "");
        }

        deleted = {};
    }

    FreeAllTemporary();
    allConsistent = true;
    return;

pruned:
    // Restore the numerical guesses
    SK.param.Clear();
    prev.MoveSelfInto(&(SK.param));
    // Try again
    GenerateAll(type, andFindFree, genForBBox);
}
Exemple #16
0
bool TextWindow::EditControlDoneForConfiguration(const char *s) {
    switch(edit.meaning) {
        case EDIT_LIGHT_INTENSITY:
            SS.lightIntensity[edit.i] = min(1, max(0, atof(s)));
            InvalidateGraphics();
            break;

        case EDIT_LIGHT_DIRECTION: {
            double x, y, z;
            if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) {
                SS.lightDir[edit.i] = Vector::From(x, y, z);
            } else {
                Error("Bad format: specify coordinates as x, y, z");
            }
            InvalidateGraphics();
            break;
        }
        case EDIT_COLOR: {
            Vector rgb;
            if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
                rgb = rgb.ClampWithin(0, 1);
                SS.modelColor[edit.i] = RGBf(rgb.x, rgb.y, rgb.z);
            } else {
                Error("Bad format: specify color as r, g, b");
            }
            break;
        }
        case EDIT_CHORD_TOLERANCE: {
            SS.chordTol = min(10, max(0.1, atof(s)));
            SS.GenerateAll(0, INT_MAX);
            break;
        }
//RT New stuff
        case EDIT_RENDERDETAIL: {
			SS.renderDetailWhenSaving =   atof(s);
			if (SS.renderDetailWhenSaving)			SS.GW.scaleWin =SS.renderDetailWhenSaving;
			SS.GenerateAll(0, INT_MAX);
            break;
        }
//RT End stuff
		case EDIT_MAX_SEGMENTS: {
            SS.maxSegments = min(1000, max(7, atoi(s)));
            SS.GenerateAll(0, INT_MAX);
            break;
        }
        case EDIT_CAMERA_TANGENT: {
            SS.cameraTangent = (min(2, max(0, atof(s))))/1000.0;
            if(!SS.usePerspectiveProj) {
                Message("The perspective factor will have no effect until you "
                        "enable View -> Use Perspective Projection.");
            }
            InvalidateGraphics();
            break;
        }
        case EDIT_GRID_SPACING: {
            SS.gridSpacing = (float)min(1e4, max(1e-3, SS.StringToMm(s)));
            InvalidateGraphics();
            break;
        }
        case EDIT_DIGITS_AFTER_DECIMAL: {
            int v = atoi(s);
            if(v < 0 || v > 8) {
                Error("Specify between 0 and 8 digits after the decimal.");
            } else {
                SS.SetUnitDigitsAfterDecimal(v);
            }
            InvalidateGraphics();
            break;
        }
        case EDIT_EXPORT_SCALE: {
            Expr *e = Expr::From(s, true);
            if(e) {
                double ev = e->Eval();
                if(fabs(ev) < 0.001 || isnan(ev)) {
                    Error("Export scale must not be zero!");
                } else {
                    SS.exportScale = (float)ev;
                }
            }
            break;
        }
        case EDIT_EXPORT_OFFSET: {
            Expr *e = Expr::From(s, true);
            if(e) {
                double ev = SS.ExprToMm(e);
                if(isnan(ev) || ev < 0) {
                    Error("Cutter radius offset must not be negative!");
                } else {
                    SS.exportOffset = (float)ev;
                }
            }
            break;
        }
        case EDIT_CANVAS_SIZE: {
            Expr *e = Expr::From(s, true);
            if(!e) {
                break;
            }
            float d = (float)SS.ExprToMm(e);
            switch(edit.i) {
                case  0: SS.exportMargin.left   = d;    break;
                case  1: SS.exportMargin.right  = d;    break;
                case  2: SS.exportMargin.bottom = d;    break;
                case  3: SS.exportMargin.top    = d;    break;

                case 10: SS.exportCanvas.width  = d;    break;
                case 11: SS.exportCanvas.height = d;    break;
                case 12: SS.exportCanvas.dx     = d;    break;
                case 13: SS.exportCanvas.dy     = d;    break;
            }
            break;
        }
        case EDIT_G_CODE_DEPTH: {
            Expr *e = Expr::From(s, true);
            if(e) SS.gCode.depth = (float)SS.ExprToMm(e);
            break;
        }
        case EDIT_G_CODE_PASSES: {
            Expr *e = Expr::From(s, true);
            if(e) SS.gCode.passes = (int)(e->Eval());
            SS.gCode.passes = max(1, min(1000, SS.gCode.passes));
            break;
        }
        case EDIT_G_CODE_FEED: {
            Expr *e = Expr::From(s, true);
            if(e) SS.gCode.feed = (float)SS.ExprToMm(e);
            break;
        }
        case EDIT_G_CODE_PLUNGE_FEED: {
            Expr *e = Expr::From(s, true);
            if(e) SS.gCode.plungeFeed = (float)SS.ExprToMm(e);
            break;
        }

        default: return false;
    }
    return true;
}
Exemple #17
0
void TextWindow::ScreenChangeBackFaces(int link, uint32_t v) {
    SS.drawBackFaces = !SS.drawBackFaces;
    InvalidateGraphics();
}
Exemple #18
0
void TextWindow::ScreenChangePwlCurves(int link, uint32_t v) {
    SS.exportPwlCurves = !SS.exportPwlCurves;
    InvalidateGraphics();
}
Exemple #19
0
void TextWindow::ScreenChangeStyleYesNo(int link, uint32_t v) {
    SS.UndoRemember();
    hStyle hs = { v };
    Style *s = Style::Get(hs);
    switch(link) {
        // Units for the width
        case 'w':
            if(s->widthAs != Style::UNITS_AS_MM) {
                s->widthAs = Style::UNITS_AS_MM;
                s->width /= SS.GW.scale;
            }
            break;
        case 'W':
            if(s->widthAs != Style::UNITS_AS_PIXELS) {
                s->widthAs = Style::UNITS_AS_PIXELS;
                s->width *= SS.GW.scale;
            }
            break;

        // Units for the height
        case 'g':
            if(s->textHeightAs != Style::UNITS_AS_MM) {
                s->textHeightAs = Style::UNITS_AS_MM;
                s->textHeight /= SS.GW.scale;
            }
            break;

        case 'G':
            if(s->textHeightAs != Style::UNITS_AS_PIXELS) {
                s->textHeightAs = Style::UNITS_AS_PIXELS;
                s->textHeight *= SS.GW.scale;
            }
            break;

        case 'e':
            s->exportable = !(s->exportable);
            break;

        case 'v':
            s->visible = !(s->visible);
            break;

        case 'f':
            s->filled = !(s->filled);
            break;

        // Horizontal text alignment
        case 'L':
            s->textOrigin |=  Style::ORIGIN_LEFT;
            s->textOrigin &= ~Style::ORIGIN_RIGHT;
            break;
        case 'H':
            s->textOrigin &= ~Style::ORIGIN_LEFT;
            s->textOrigin &= ~Style::ORIGIN_RIGHT;
            break;
        case 'R':
            s->textOrigin &= ~Style::ORIGIN_LEFT;
            s->textOrigin |=  Style::ORIGIN_RIGHT;
            break;

        // Vertical text alignment
        case 'B':
            s->textOrigin |=  Style::ORIGIN_BOT;
            s->textOrigin &= ~Style::ORIGIN_TOP;
            break;
        case 'V':
            s->textOrigin &= ~Style::ORIGIN_BOT;
            s->textOrigin &= ~Style::ORIGIN_TOP;
            break;
        case 'T':
            s->textOrigin &= ~Style::ORIGIN_BOT;
            s->textOrigin |=  Style::ORIGIN_TOP;
            break;
    }
    InvalidateGraphics();
}
void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) {
    int i;
    SEdgeList edges;
    ZERO(&edges);
    SBezierList beziers;
    ZERO(&beziers);

    SMesh *sm = NULL;
    if(SS.GW.showShaded) {
        Group *g = SK.GetGroup(SS.GW.activeGroup);
        g->GenerateDisplayItems();
        sm = &(g->displayMesh);
    }
    if(sm && sm->IsEmpty()) {
        sm = NULL;
    }

    for(i = 0; i < SK.entity.n; i++) {
        Entity *e = &(SK.entity.elem[i]);
        if(!e->IsVisible()) continue;
        if(e->construction) continue;

        if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines) ||
                                 fabs(SS.exportOffset) > LENGTH_EPS)
        {
            // We will be doing hidden line removal, which we can't do on
            // exact curves; so we need things broken down to pwls. Same
            // problem with cutter radius compensation.
            e->GenerateEdges(&edges);
        } else {
            e->GenerateBezierCurves(&beziers);
        }
    }

    if(SS.GW.showEdges) {
        Group *g = SK.GetGroup(SS.GW.activeGroup);
        g->GenerateDisplayItems();
        SEdgeList *selr = &(g->displayEdges);
        SEdge *se;
        for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) {
            edges.AddEdge(se->a, se->b, Style::SOLID_EDGE);
        }
    }

    if(SS.GW.showConstraints) {
        Constraint *c;
        for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
            c->GetEdges(&edges);
        }
    }

    if(wireframe) {
        VectorFileWriter *out = VectorFileWriter::ForFile(filename);
        if(out) {
            ExportWireframeCurves(&edges, &beziers, out);
        }
    } else {
        Vector u = SS.GW.projRight,
               v = SS.GW.projUp,
               n = u.Cross(v),
               origin = SS.GW.offset.ScaledBy(-1);

        VectorFileWriter *out = VectorFileWriter::ForFile(filename);
        if(out) {
            ExportLinesAndMesh(&edges, &beziers, sm,
                               u, v, n, origin, SS.CameraTangent()*SS.GW.scale,
                               out);
        }

        if(out && !out->HasCanvasSize()) {
            // These file formats don't have a canvas size, so they just
            // get exported in the raw coordinate system. So indicate what
            // that was on-screen.
            SS.justExportedInfo.draw = true;
            SS.justExportedInfo.pt = origin;
            SS.justExportedInfo.u = u;
            SS.justExportedInfo.v = v;
            InvalidateGraphics();
        }
    }

    edges.Clear();
    beziers.Clear();
}
Exemple #21
0
void TextWindow::ScreenChangeShadedTriangles(int link, uint32_t v) {
    SS.exportShadedTriangles = !SS.exportShadedTriangles;
    InvalidateGraphics();
}
Exemple #22
0
void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
            bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown)
{
    if(GraphicsEditControlIsVisible()) return;
    if(context.active) return;

    SS.extraLine.draw = false;

    if(!orig.mouseDown) {
        // If someone drags the mouse into our window with the left button
        // already depressed, then we don't have our starting point; so
        // don't try.
        leftDown = false;
    }

    if(rightDown) {
        middleDown = true;
        shiftDown = !shiftDown;
    }

    if(SS.showToolbar) {
        if(ToolbarMouseMoved((int)x, (int)y)) {
            hover.Clear();
            return;
        }
    }

    if(!leftDown && (pending.operation == DRAGGING_POINTS ||
                     pending.operation == DRAGGING_MARQUEE))
    {
        ClearPending();
        InvalidateGraphics();
    }

    Point2d mp = Point2d::From(x, y);
    currentMousePosition = mp;

    if(rightDown && orig.mouse.DistanceTo(mp) < 5 && !orig.startedMoving) {
        // Avoid accidentally panning (or rotating if shift is down) if the
        // user wants a context menu.
        return;
    }
    orig.startedMoving = true;

    // If the middle button is down, then mouse movement is used to pan and
    // rotate our view. This wins over everything else.
    if(middleDown) {
        hover.Clear();

        double dx = (x - orig.mouse.x) / scale;
        double dy = (y - orig.mouse.y) / scale;

        if(!(shiftDown || ctrlDown)) {
            double s = 0.3*(PI/180)*scale; // degrees per pixel
            projRight = orig.projRight.RotatedAbout(orig.projUp, -s*dx);
            projUp = orig.projUp.RotatedAbout(orig.projRight, s*dy);

            NormalizeProjectionVectors();
        } else if(ctrlDown) {
            double theta = atan2(orig.mouse.y, orig.mouse.x);
            theta -= atan2(y, x);
            SS.extraLine.draw = true;
            SS.extraLine.ptA = UnProjectPoint(Point2d::From(0, 0));
            SS.extraLine.ptB = UnProjectPoint(mp);

            Vector normal = orig.projRight.Cross(orig.projUp);
            projRight = orig.projRight.RotatedAbout(normal, theta);
            projUp = orig.projUp.RotatedAbout(normal, theta);

            NormalizeProjectionVectors();
        } else {
            offset.x = orig.offset.x + dx*projRight.x + dy*projUp.x;
            offset.y = orig.offset.y + dx*projRight.y + dy*projUp.y;
            offset.z = orig.offset.z + dx*projRight.z + dy*projUp.z;
        }

        orig.projRight = projRight;
        orig.projUp = projUp;
        orig.offset = offset;
        orig.mouse.x = x;
        orig.mouse.y = y;

        if(SS.TW.shown.screen == TextWindow::SCREEN_EDIT_VIEW) {
            if(havePainted) {
                SS.ScheduleShowTW();
            }
        }
        InvalidateGraphics();
        havePainted = false;
        return;
    }

    if(pending.operation == 0) {
        double dm = orig.mouse.DistanceTo(mp);
        // If we're currently not doing anything, then see if we should
        // start dragging something.
        if(leftDown && dm > 3) {
            Entity *e = NULL;
            if(hover.entity.v) e = SK.GetEntity(hover.entity);
            if(e && e->type != Entity::WORKPLANE) {
                Entity *e = SK.GetEntity(hover.entity);
                if(e->type == Entity::CIRCLE && selection.n <= 1) {
                    // Drag the radius.
                    ClearSelection();
                    pending.circle = hover.entity;
                    pending.operation = DRAGGING_RADIUS;
                } else if(e->IsNormal()) {
                    ClearSelection();
                    pending.normal = hover.entity;
                    pending.operation = DRAGGING_NORMAL;
                } else {
                    if(!hoverWasSelectedOnMousedown) {
                        // The user clicked an unselected entity, which
                        // means they're dragging just the hovered thing,
                        // not the full selection. So clear all the selection
                        // except that entity.
                        ClearSelection();
                        MakeSelected(e->h);
                    }
                    StartDraggingBySelection();
                    if(!hoverWasSelectedOnMousedown) {
                        // And then clear the selection again, since they
                        // probably didn't want that selected if they just
                        // were dragging it.
                        ClearSelection();
                    }
                    hover.Clear();
                    pending.operation = DRAGGING_POINTS;
                }
            } else if(hover.constraint.v &&
                            SK.GetConstraint(hover.constraint)->HasLabel())
            {
                ClearSelection();
                pending.constraint = hover.constraint;
                pending.operation = DRAGGING_CONSTRAINT;
            }
            if(pending.operation != 0) {
                // We just started a drag, so remember for the undo before
                // the drag changes anything.
                SS.UndoRemember();
            } else {
                if(!hover.constraint.v) {
                    // That's just marquee selection, which should not cause
                    // an undo remember.
                    if(dm > 10) {
                        if(hover.entity.v) {
                            // Avoid accidentally selecting workplanes when
                            // starting drags.
                            MakeUnselected(hover.entity, false);
                            hover.Clear();
                        }
                        pending.operation = DRAGGING_MARQUEE;
                        orig.marqueePoint =
                            UnProjectPoint(orig.mouseOnButtonDown);
                    }
                }
            }
        } else {
            // Otherwise, just hit test and give up; but don't hit test
            // if the mouse is down, because then the user could hover
            // a point, mouse down (thus selecting it), and drag, in an
            // effort to drag the point, but instead hover a different
            // entity before we move far enough to start the drag.
            if(!leftDown) HitTestMakeSelection(mp);
        }
        return;
    }

    // If the user has started an operation from the menu, but not
    // completed it, then just do the selection.
    if(pending.operation < FIRST_PENDING) {
        HitTestMakeSelection(mp);
        return;
    }

    // We're currently dragging something; so do that. But if we haven't
    // painted since the last time we solved, do nothing, because there's
    // no sense solving a frame and not displaying it.
    if(!havePainted) {
        if(pending.operation == DRAGGING_POINTS && ctrlDown) {
            SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown);
            SS.extraLine.ptB = UnProjectPoint(mp);
            SS.extraLine.draw = true;
        }
        return;
    }
    switch(pending.operation) {
        case DRAGGING_CONSTRAINT: {
            Constraint *c = SK.constraint.FindById(pending.constraint);
            UpdateDraggedNum(&(c->disp.offset), x, y);
            orig.mouse = mp;
            InvalidateGraphics();
            break;
        }

        case DRAGGING_NEW_LINE_POINT:
        case DRAGGING_NEW_POINT:
            UpdateDraggedPoint(pending.point, x, y);
            HitTestMakeSelection(mp);
            SS.MarkGroupDirtyByEntity(pending.point);
            orig.mouse = mp;
            InvalidateGraphics();
            break;

        case DRAGGING_POINTS:
            if(shiftDown || ctrlDown) {
                // Edit the rotation associated with a POINT_N_ROT_TRANS,
                // either within (ctrlDown) or out of (shiftDown) the plane
                // of the screen. So first get the rotation to apply, in qt.
                Quaternion qt;
                if(ctrlDown) {
                    double d = mp.DistanceTo(orig.mouseOnButtonDown);
                    if(d < 25) {
                        // Don't start dragging the position about the normal
                        // until we're a little ways out, to get a reasonable
                        // reference pos
                        orig.mouse = mp;
                        break;
                    }
                    double theta = atan2(orig.mouse.y-orig.mouseOnButtonDown.y,
                                         orig.mouse.x-orig.mouseOnButtonDown.x);
                    theta -= atan2(y-orig.mouseOnButtonDown.y,
                                   x-orig.mouseOnButtonDown.x);

                    Vector gn = projRight.Cross(projUp);
                    qt = Quaternion::From(gn, -theta);

                    SS.extraLine.draw = true;
                    SS.extraLine.ptA = UnProjectPoint(orig.mouseOnButtonDown);
                    SS.extraLine.ptB = UnProjectPoint(mp);
                } else {
                    double dx = -(x - orig.mouse.x);
                    double dy = -(y - orig.mouse.y);
                    double s = 0.3*(PI/180); // degrees per pixel
                    qt = Quaternion::From(projUp,   -s*dx).Times(
                         Quaternion::From(projRight, s*dy));
                }
                orig.mouse = mp;

                // Now apply this rotation to the points being dragged.
                List<hEntity> *lhe = &(pending.points);
                for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
                    Entity *e = SK.GetEntity(*he);
                    if(e->type != Entity::POINT_N_ROT_TRANS) {
                        if(ctrlDown) {
                            Vector p = e->PointGetNum();
                            p = p.Minus(SS.extraLine.ptA);
                            p = qt.Rotate(p);
                            p = p.Plus(SS.extraLine.ptA);
                            e->PointForceTo(p);
                            SS.MarkGroupDirtyByEntity(e->h);
                        }
                        continue;
                    }

                    Quaternion q = e->PointGetQuaternion();
                    Vector     p = e->PointGetNum();
                    q = qt.Times(q);
                    e->PointForceQuaternionTo(q);
                    // Let's rotate about the selected point; so fix up the
                    // translation so that that point didn't move.
                    e->PointForceTo(p);
                    SS.MarkGroupDirtyByEntity(e->h);
                }
            } else {
                List<hEntity> *lhe = &(pending.points);
                for(hEntity *he = lhe->First(); he; he = lhe->NextAfter(he)) {
                    UpdateDraggedPoint(*he, x, y);
                    SS.MarkGroupDirtyByEntity(*he);
                }
                orig.mouse = mp;
            }
            break;

        case DRAGGING_NEW_CUBIC_POINT: {
            UpdateDraggedPoint(pending.point, x, y);
            HitTestMakeSelection(mp);

            hRequest hr = pending.point.request();
            if(pending.point.v == hr.entity(4).v) {
                // The very first segment; dragging final point drags both
                // tangent points.
                Vector p0 = SK.GetEntity(hr.entity(1))->PointGetNum(),
                       p3 = SK.GetEntity(hr.entity(4))->PointGetNum(),
                       p1 = p0.ScaledBy(2.0/3).Plus(p3.ScaledBy(1.0/3)),
                       p2 = p0.ScaledBy(1.0/3).Plus(p3.ScaledBy(2.0/3));
                SK.GetEntity(hr.entity(1+1))->PointForceTo(p1);
                SK.GetEntity(hr.entity(1+2))->PointForceTo(p2);
            } else {
                // A subsequent segment; dragging point drags only final
                // tangent point.
                int i = SK.GetEntity(hr.entity(0))->extraPoints;
                Vector pn   = SK.GetEntity(hr.entity(4+i))->PointGetNum(),
                       pnm2 = SK.GetEntity(hr.entity(2+i))->PointGetNum(),
                       pnm1 = (pn.Plus(pnm2)).ScaledBy(0.5);
                SK.GetEntity(hr.entity(3+i))->PointForceTo(pnm1);
            }

            orig.mouse = mp;
            SS.MarkGroupDirtyByEntity(pending.point);
            break;
        }
        case DRAGGING_NEW_ARC_POINT: {
            UpdateDraggedPoint(pending.point, x, y);
            HitTestMakeSelection(mp);

            hRequest hr = pending.point.request();
            Vector ona = SK.GetEntity(hr.entity(2))->PointGetNum();
            Vector onb = SK.GetEntity(hr.entity(3))->PointGetNum();
            Vector center = (ona.Plus(onb)).ScaledBy(0.5);

            SK.GetEntity(hr.entity(1))->PointForceTo(center);

            orig.mouse = mp;
            SS.MarkGroupDirtyByEntity(pending.point);
            break;
        }
        case DRAGGING_NEW_RADIUS:
        case DRAGGING_RADIUS: {
            Entity *circle = SK.GetEntity(pending.circle);
            Vector center = SK.GetEntity(circle->point[0])->PointGetNum();
            Point2d c2 = ProjectPoint(center);
            double r = c2.DistanceTo(mp)/scale;
            SK.GetEntity(circle->distance)->DistanceForceTo(r);

            SS.MarkGroupDirtyByEntity(pending.circle);
            break;
        }

        case DRAGGING_NORMAL: {
            Entity *normal = SK.GetEntity(pending.normal);
            Vector p = SK.GetEntity(normal->point[0])->PointGetNum();
            Point2d p2 = ProjectPoint(p);

            Quaternion q = normal->NormalGetNum();
            Vector u = q.RotationU(), v = q.RotationV();

            if(ctrlDown) {
                double theta = atan2(orig.mouse.y-p2.y, orig.mouse.x-p2.x);
                theta -= atan2(y-p2.y, x-p2.x);

                Vector normal = projRight.Cross(projUp);
                u = u.RotatedAbout(normal, -theta);
                v = v.RotatedAbout(normal, -theta);
            } else {
                double dx = -(x - orig.mouse.x);
                double dy = -(y - orig.mouse.y);
                double s = 0.3*(PI/180); // degrees per pixel
                u = u.RotatedAbout(projUp, -s*dx);
                u = u.RotatedAbout(projRight, s*dy);
                v = v.RotatedAbout(projUp, -s*dx);
                v = v.RotatedAbout(projRight, s*dy);
            }
            orig.mouse = mp;
            normal->NormalForceTo(Quaternion::From(u, v));

            SS.MarkGroupDirtyByEntity(pending.normal);
            break;
        }

        case DRAGGING_MARQUEE:
            orig.mouse = mp;
            InvalidateGraphics();
            break;

        default: oops();
    }

    if(pending.operation != 0 &&
       pending.operation != DRAGGING_CONSTRAINT &&
       pending.operation != DRAGGING_MARQUEE)
    {
        SS.GenerateAll();
    }
    havePainted = false;
}
Exemple #23
0
void GraphicsWindow::MouseRightUp(double x, double y) {
    SS.extraLine.draw = false;
    InvalidateGraphics();

    // Don't show a context menu if the user is right-clicking the toolbar,
    // or if they are finishing a pan.
    if(ToolbarMouseMoved((int)x, (int)y)) return;
    if(orig.startedMoving) return;

    if(context.active) return;

    if(pending.operation == DRAGGING_NEW_LINE_POINT ||
       pending.operation == DRAGGING_NEW_CUBIC_POINT)
    {
        // Special case; use a right click to stop drawing lines, since
        // a left click would draw another one. This is quicker and more
        // intuitive than hitting escape. Likewise for new cubic segments.
        ClearPending();
        return;
    }

    context.active = true;

    if(!hover.IsEmpty()) {
        MakeSelected(&hover);
        SS.ScheduleShowTW();
    }
    GroupSelection();

    bool itemsSelected = (gs.n > 0 || gs.constraints > 0);

    if(itemsSelected) {
        if(gs.stylables > 0) {
            ContextMenuListStyles();
            AddContextMenuItem("Assign to Style", CONTEXT_SUBMENU);
        }
        if(gs.n + gs.constraints == 1) {
            AddContextMenuItem("Group Info", CMNU_GROUP_INFO);
        }
        if(gs.n + gs.constraints == 1 && gs.stylables == 1) {
            AddContextMenuItem("Style Info", CMNU_STYLE_INFO);
        }
        if(gs.withEndpoints > 0) {
            AddContextMenuItem("Select Edge Chain", CMNU_SELECT_CHAIN);
        }
        if(gs.constraints == 1 && gs.n == 0) {
            Constraint *c = SK.GetConstraint(gs.constraint[0]);
            if(c->HasLabel() && c->type != Constraint::COMMENT) {
                AddContextMenuItem("Toggle Reference Dimension",
                    CMNU_REFERENCE_DIM);
            }
            if(c->type == Constraint::ANGLE ||
               c->type == Constraint::EQUAL_ANGLE)
            {
                AddContextMenuItem("Other Supplementary Angle",
                    CMNU_OTHER_ANGLE);
            }
        }
        if(gs.comments > 0 || gs.points > 0) {
            AddContextMenuItem("Snap to Grid", CMNU_SNAP_TO_GRID);
        }

        if(gs.points == 1) {
            Entity *p = SK.GetEntity(gs.point[0]);
            Constraint *c;
            IdList<Constraint,hConstraint> *lc = &(SK.constraint);
            for(c = lc->First(); c; c = lc->NextAfter(c)) {
                if(c->type != Constraint::POINTS_COINCIDENT) continue;
                if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
                    break;
                }
            }
            if(c) {
                AddContextMenuItem("Delete Point-Coincident Constraint",
                                   CMNU_DEL_COINCIDENT);
            }
        }
        AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
        if(LockedInWorkplane()) {
            AddContextMenuItem("Cut",  CMNU_CUT_SEL);
            AddContextMenuItem("Copy", CMNU_COPY_SEL);
        }
    }

    if(SS.clipboard.r.n > 0 && LockedInWorkplane()) {
        AddContextMenuItem("Paste", CMNU_PASTE_SEL);
    }

    if(itemsSelected) {
        AddContextMenuItem("Delete", CMNU_DELETE_SEL);
        AddContextMenuItem(NULL, CONTEXT_SEPARATOR);
        AddContextMenuItem("Unselect All", CMNU_UNSELECT_ALL);
    }
    // If only one item is selected, then it must be the one that we just
    // selected from the hovered item; in which case unselect all and hovered
    // are equivalent.
    if(!hover.IsEmpty() && selection.n > 1) {
        AddContextMenuItem("Unselect Hovered", CMNU_UNSELECT_HOVERED);
    }

    int ret = ShowContextMenu();
    switch(ret) {
        case CMNU_UNSELECT_ALL:
            MenuEdit(MNU_UNSELECT_ALL);
            break;

        case CMNU_UNSELECT_HOVERED:
            if(!hover.IsEmpty()) {
                MakeUnselected(&hover, true);
            }
            break;

        case CMNU_SELECT_CHAIN:
            MenuEdit(MNU_SELECT_CHAIN);
            break;

        case CMNU_CUT_SEL:
            MenuClipboard(MNU_CUT);
            break;

        case CMNU_COPY_SEL:
            MenuClipboard(MNU_COPY);
            break;

        case CMNU_PASTE_SEL:
            MenuClipboard(MNU_PASTE);
            break;

        case CMNU_DELETE_SEL:
            MenuClipboard(MNU_DELETE);
            break;

        case CMNU_REFERENCE_DIM:
            Constraint::MenuConstrain(MNU_REFERENCE);
            break;

        case CMNU_OTHER_ANGLE:
            Constraint::MenuConstrain(MNU_OTHER_ANGLE);
            break;

        case CMNU_DEL_COINCIDENT: {
            SS.UndoRemember();
            if(!gs.point[0].v) break;
            Entity *p = SK.GetEntity(gs.point[0]);
            if(!p->IsPoint()) break;

            SK.constraint.ClearTags();
            Constraint *c;
            for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
                if(c->type != Constraint::POINTS_COINCIDENT) continue;
                if(c->ptA.v == p->h.v || c->ptB.v == p->h.v) {
                    c->tag = 1;
                }
            }
            SK.constraint.RemoveTagged();
            ClearSelection();
            break;
        }

        case CMNU_SNAP_TO_GRID:
            MenuEdit(MNU_SNAP_TO_GRID);
            break;

        case CMNU_GROUP_INFO: {
            hGroup hg;
            if(gs.entities == 1) {
                hg = SK.GetEntity(gs.entity[0])->group;
            } else if(gs.points == 1) {
                hg = SK.GetEntity(gs.point[0])->group;
            } else if(gs.constraints == 1) {
                hg = SK.GetConstraint(gs.constraint[0])->group;
            } else {
                break;
            }
            ClearSelection();

            SS.TW.GoToScreen(TextWindow::SCREEN_GROUP_INFO);
            SS.TW.shown.group = hg;
            SS.ScheduleShowTW();
            break;
        }

        case CMNU_STYLE_INFO: {
            hStyle hs;
            if(gs.entities == 1) {
                hs = Style::ForEntity(gs.entity[0]);
            } else if(gs.points == 1) {
                hs = Style::ForEntity(gs.point[0]);
            } else if(gs.constraints == 1) {
                hs = SK.GetConstraint(gs.constraint[0])->disp.style;
                if(!hs.v) hs.v = Style::CONSTRAINT;
            } else {
                break;
            }
            ClearSelection();

            SS.TW.GoToScreen(TextWindow::SCREEN_STYLE_INFO);
            SS.TW.shown.style = hs;
            SS.ScheduleShowTW();
            break;
        }

        case CMNU_NEW_CUSTOM_STYLE: {
            uint32_t v = Style::CreateCustomStyle();
            Style::AssignSelectionToStyle(v);
            break;
        }

        case CMNU_NO_STYLE:
            Style::AssignSelectionToStyle(0);
            break;

        default:
            if(ret >= CMNU_FIRST_STYLE) {
                Style::AssignSelectionToStyle(ret - CMNU_FIRST_STYLE);
            } else {
                // otherwise it was cancelled, so do nothing
                contextMenuCancelTime = GetMilliseconds();
            }
            break;
    }

    context.active = false;
    SS.ScheduleShowTW();
}
Exemple #24
0
void GraphicsWindow::MenuRequest(int id) {
    const char *s;
    switch(id) {
        case MNU_SEL_WORKPLANE: {
            SS.GW.GroupSelection();
            Group *g = SK.GetGroup(SS.GW.activeGroup);

            if(SS.GW.gs.n == 1 && SS.GW.gs.workplanes == 1) {
                // A user-selected workplane
                g->activeWorkplane = SS.GW.gs.entity[0];
            } else if(g->type == Group::DRAWING_WORKPLANE) {
                // The group's default workplane
                g->activeWorkplane = g->h.entity(0);
                Message("No workplane selected. Activating default workplane "
                        "for this group.");
            }

            if(!SS.GW.LockedInWorkplane()) {
                Error("No workplane is selected, and the active group does "
                      "not have a default workplane. Try selecting a "
                      "workplane, or activating a sketch-in-new-workplane "
                      "group.");
                break;
            }
            // Align the view with the selected workplane
            SS.GW.AnimateOntoWorkplane();
            SS.GW.ClearSuper();
            SS.ScheduleShowTW();
            break;
        }
        case MNU_FREE_IN_3D:
            SS.GW.SetWorkplaneFreeIn3d();
            SS.GW.EnsureValidActives();
            SS.ScheduleShowTW();
            InvalidateGraphics();
            break;

        case MNU_TANGENT_ARC:
            SS.GW.GroupSelection();
            if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
                SS.GW.MakeTangentArc();
            } else if(SS.GW.gs.n != 0) {
                Error("Bad selection for tangent arc at point. Select a "
                      "single point, or select nothing to set up arc "
                      "parameters.");
            } else {
                SS.TW.GoToScreen(TextWindow::SCREEN_TANGENT_ARC);
                SS.GW.ForceTextWindowShown();
                SS.ScheduleShowTW();
                InvalidateGraphics(); // repaint toolbar
            }
            break;

        case MNU_ARC: s = "click point on arc (draws anti-clockwise)"; goto c;
        case MNU_DATUM_POINT: s = "click to place datum point"; goto c;
        case MNU_LINE_SEGMENT: s = "click first point of line segment"; goto c;
        case MNU_CONSTR_SEGMENT: s = "click first point of construction line segment"; goto c;
        case MNU_CUBIC: s = "click first point of cubic segment"; goto c;
        case MNU_CIRCLE: s = "click center of circle"; goto c;
        case MNU_WORKPLANE: s = "click origin of workplane"; goto c;
        case MNU_RECTANGLE: s = "click one corner of rectangle"; goto c;
        case MNU_TTF_TEXT: s = "click top left of text"; goto c;
c:
            SS.GW.pending.operation = id;
            SS.GW.pending.description = s;
            SS.ScheduleShowTW();
            InvalidateGraphics(); // repaint toolbar
            break;

        case MNU_CONSTRUCTION: {
            SS.UndoRemember();
            SS.GW.GroupSelection();
            if(SS.GW.gs.entities == 0) {
                Error("No entities are selected. Select entities before "
                      "trying to toggle their construction state.");
            }
            int i;
            for(i = 0; i < SS.GW.gs.entities; i++) {
                hEntity he = SS.GW.gs.entity[i];
                if(!he.isFromRequest()) continue;
                Request *r = SK.GetRequest(he.request());
                r->construction = !(r->construction);
                SS.MarkGroupDirty(r->group);
            }
            SS.GW.ClearSelection();
            SS.GenerateAll();
            break;
        }

        case MNU_SPLIT_CURVES:
            SS.GW.SplitLinesOrCurves();
            break;

        default: oops();
    }
}
Exemple #25
0
void GraphicsWindow::MenuEdit(int id) {
    switch(id) {
        case MNU_UNSELECT_ALL:
            SS.GW.GroupSelection();
            // If there's nothing selected to de-select, and no operation
            // to cancel, then perhaps they want to return to the home
            // screen in the text window.
            if(SS.GW.gs.n               == 0 &&
               SS.GW.gs.constraints     == 0 &&
               SS.GW.pending.operation  == 0)
            {
                if(!(TextEditControlIsVisible() ||
                     GraphicsEditControlIsVisible()))
                {
                    if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) {
                        SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES);
                    } else {
                        SS.TW.ClearSuper();
                    }
                }
            }
            SS.GW.ClearSuper();
            SS.TW.HideEditControl();
            SS.nakedEdges.Clear();
            SS.justExportedInfo.draw = false;
            // This clears the marks drawn to indicate which points are
            // still free to drag.
            Param *p;
            for(p = SK.param.First(); p; p = SK.param.NextAfter(p)) {
                p->free = false;
            }
            if(SS.exportMode) {
                SS.exportMode = false;
                SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
            }
            break;

        case MNU_SELECT_ALL: {
            Entity *e;
            for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
                if(e->group.v != SS.GW.activeGroup.v) continue;
                if(e->IsFace() || e->IsDistance()) continue;
                if(!e->IsVisible()) continue;

                SS.GW.MakeSelected(e->h);
            }
            InvalidateGraphics();
            SS.ScheduleShowTW();
            break;
        }

        case MNU_SELECT_CHAIN: {
            Entity *e;
            int newlySelected = 0;
            bool didSomething;
            do {
                didSomething = false;
                for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) {
                    if(e->group.v != SS.GW.activeGroup.v) continue;
                    if(!e->HasEndpoints()) continue;
                    if(!e->IsVisible()) continue;

                    Vector st = e->EndpointStart(),
                           fi = e->EndpointFinish();

                    bool onChain = false, alreadySelected = false;
                    List<Selection> *ls = &(SS.GW.selection);
                    for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
                        if(!s->entity.v) continue;
                        if(s->entity.v == e->h.v) {
                            alreadySelected = true;
                            continue;
                        }
                        Entity *se = SK.GetEntity(s->entity);
                        if(!se->HasEndpoints()) continue;

                        Vector sst = se->EndpointStart(),
                               sfi = se->EndpointFinish();

                        if(sst.Equals(st) || sst.Equals(fi) ||
                           sfi.Equals(st) || sfi.Equals(fi))
                        {
                            onChain = true;
                        }
                    }
                    if(onChain && !alreadySelected) {
                        SS.GW.MakeSelected(e->h);
                        newlySelected++;
                        didSomething = true;
                    }
                }
            } while(didSomething);
            if(newlySelected == 0) {
                Error("No additional entities share endpoints with the "
                      "selected entities.");
            }
            InvalidateGraphics();
            SS.ScheduleShowTW();
            break;
        }

        case MNU_ROTATE_90: {
            SS.GW.GroupSelection();
            Entity *e = NULL;
            if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
                e = SK.GetEntity(SS.GW.gs.point[0]);
            } else if(SS.GW.gs.n == 1 && SS.GW.gs.entities == 1) {
                e = SK.GetEntity(SS.GW.gs.entity[0]);
            }
            SS.GW.ClearSelection();

            hGroup hg = e ? e->group : SS.GW.activeGroup;
            Group *g = SK.GetGroup(hg);
            if(g->type != Group::IMPORTED) {
                Error("To use this command, select a point or other "
                      "entity from an imported part, or make an import "
                      "group the active group.");
                break;
            }


            SS.UndoRemember();
            // Rotate by ninety degrees about the coordinate axis closest
            // to the screen normal.
            Vector norm = SS.GW.projRight.Cross(SS.GW.projUp);
            norm = norm.ClosestOrtho();
            norm = norm.WithMagnitude(1);
            Quaternion qaa = Quaternion::From(norm, PI/2);

            g->TransformImportedBy(Vector::From(0, 0, 0), qaa);

            // and regenerate as necessary.
            SS.MarkGroupDirty(hg);
            SS.GenerateAll();
            break;
        }

        case MNU_SNAP_TO_GRID: {
            if(!SS.GW.LockedInWorkplane()) {
                Error("No workplane is active. Select a workplane to define "
                      "the plane for the snap grid.");
                break;
            }
            SS.GW.GroupSelection();
            if(SS.GW.gs.points == 0 && SS.GW.gs.constraintLabels == 0) {
                Error("Can't snap these items to grid; select points, "
                      "text comments, or constraints with a label. "
                      "To snap a line, select its endpoints.");
                break;
            }
            SS.UndoRemember();

            List<Selection> *ls = &(SS.GW.selection);
            for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
                if(s->entity.v) {
                    hEntity hp = s->entity;
                    Entity *ep = SK.GetEntity(hp);
                    if(!ep->IsPoint()) continue;

                    Vector p = ep->PointGetNum();
                    ep->PointForceTo(SS.GW.SnapToGrid(p));
                    SS.GW.pending.points.Add(&hp);
                    SS.MarkGroupDirty(ep->group);
                } else if(s->constraint.v) {
                    Constraint *c = SK.GetConstraint(s->constraint);
                    c->disp.offset = SS.GW.SnapToGrid(c->disp.offset);
                }
            }
            // Regenerate, with these points marked as dragged so that they
            // get placed as close as possible to our snap grid.
            SS.GenerateAll();
            SS.GW.ClearPending();

            SS.GW.ClearSelection();
            InvalidateGraphics();
            break;
        }

        case MNU_UNDO:
            SS.UndoUndo();
            break;

        case MNU_REDO:
            SS.UndoRedo();
            break;

        case MNU_REGEN_ALL:
            SS.ReloadAllImported();
            SS.GenerateAll(SolveSpaceUI::GENERATE_UNTIL_ACTIVE);
            SS.ScheduleShowTW();
            break;

        default: oops();
    }
}
Exemple #26
0
void GraphicsWindow::MenuView(int id) {
    switch(id) {
        case MNU_ZOOM_IN:
            SS.GW.scale *= 1.2;
            SS.ScheduleShowTW();
            break;

        case MNU_ZOOM_OUT:
            SS.GW.scale /= 1.2;
            SS.ScheduleShowTW();
            break;

        case MNU_ZOOM_TO_FIT:
            SS.GW.ZoomToFit(/*includingInvisibles=*/false, /*useSelection=*/true);
            SS.ScheduleShowTW();
            break;

        case MNU_SHOW_GRID:
            SS.GW.showSnapGrid = !SS.GW.showSnapGrid;
            if(SS.GW.showSnapGrid && !SS.GW.LockedInWorkplane()) {
                Message("No workplane is active, so the grid will not "
                        "appear.");
            }
            SS.GW.EnsureValidActives();
            InvalidateGraphics();
            break;

        case MNU_PERSPECTIVE_PROJ:
            SS.usePerspectiveProj = !SS.usePerspectiveProj;
            if(SS.cameraTangent < 1e-6) {
                Error("The perspective factor is set to zero, so the view will "
                      "always be a parallel projection.\n\n"
                      "For a perspective projection, modify the perspective "
                      "factor in the configuration screen. A value around 0.3 "
                      "is typical.");
            }
            SS.GW.EnsureValidActives();
            InvalidateGraphics();
            break;

        case MNU_ONTO_WORKPLANE:
            if(SS.GW.LockedInWorkplane()) {
                SS.GW.AnimateOntoWorkplane();
                SS.GW.ClearSuper();
                SS.ScheduleShowTW();
                break;
            }  // if not in 2d mode fall through and use ORTHO logic
        case MNU_NEAREST_ORTHO:
        case MNU_NEAREST_ISO: {
            static const Vector ortho[3] = {
                Vector::From(1, 0, 0),
                Vector::From(0, 1, 0),
                Vector::From(0, 0, 1)
            };
            double sqrt2 = sqrt(2.0), sqrt6 = sqrt(6.0);
            Quaternion quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
            Quaternion quatf = quat0;
            double dmin = 1e10;

            // There are 24 possible views; 3*2*2*2
            int i, j, negi, negj;
            for(i = 0; i < 3; i++) {
                for(j = 0; j < 3; j++) {
                    if(i == j) continue;
                    for(negi = 0; negi < 2; negi++) {
                        for(negj = 0; negj < 2; negj++) {
                            Vector ou = ortho[i], ov = ortho[j];
                            if(negi) ou = ou.ScaledBy(-1);
                            if(negj) ov = ov.ScaledBy(-1);
                            Vector on = ou.Cross(ov);

                            Vector u, v;
                            if(id == MNU_NEAREST_ORTHO || id == MNU_ONTO_WORKPLANE) {
                                u = ou;
                                v = ov;
                            } else {
                                u =
                                    ou.ScaledBy(1/sqrt2).Plus(
                                    on.ScaledBy(-1/sqrt2));
                                v =
                                    ou.ScaledBy(-1/sqrt6).Plus(
                                    ov.ScaledBy(2/sqrt6).Plus(
                                    on.ScaledBy(-1/sqrt6)));
                            }

                            Quaternion quatt = Quaternion::From(u, v);
                            double d = min(
                                (quatt.Minus(quat0)).Magnitude(),
                                (quatt.Plus(quat0)).Magnitude());
                            if(d < dmin) {
                                dmin = d;
                                quatf = quatt;
                            }
                        }
                    }
                }
            }

            SS.GW.AnimateOnto(quatf, SS.GW.offset);
            break;
        }

        case MNU_CENTER_VIEW:
            SS.GW.GroupSelection();
            if(SS.GW.gs.n == 1 && SS.GW.gs.points == 1) {
                Quaternion quat0;
                // Offset is the selected point, quaternion is same as before
                Vector pt = SK.GetEntity(SS.GW.gs.point[0])->PointGetNum();
                quat0 = Quaternion::From(SS.GW.projRight, SS.GW.projUp);
                SS.GW.AnimateOnto(quat0, pt.ScaledBy(-1));
                SS.GW.ClearSelection();
            } else {
                Error("Select a point; this point will become the center "
                      "of the view on screen.");
            }
            break;

        case MNU_SHOW_MENU_BAR:
            ToggleMenuBar();
            SS.GW.EnsureValidActives();
            InvalidateGraphics();
            break;

        case MNU_SHOW_TOOLBAR:
            SS.showToolbar = !SS.showToolbar;
            SS.GW.EnsureValidActives();
            InvalidateGraphics();
            break;

        case MNU_SHOW_TEXT_WND:
            SS.GW.showTextWindow = !SS.GW.showTextWindow;
            SS.GW.EnsureValidActives();
            break;

        case MNU_UNITS_INCHES:
            SS.viewUnits = SolveSpaceUI::UNIT_INCHES;
            SS.ScheduleShowTW();
            SS.GW.EnsureValidActives();
            break;

        case MNU_UNITS_MM:
            SS.viewUnits = SolveSpaceUI::UNIT_MM;
            SS.ScheduleShowTW();
            SS.GW.EnsureValidActives();
            break;

        case MNU_FULL_SCREEN:
            ToggleFullScreen();
            SS.GW.EnsureValidActives();
            break;

        default: oops();
    }
    InvalidateGraphics();
}
Exemple #27
0
void GraphicsWindow::MouseLeftDown(double mx, double my) {
    orig.mouseDown = true;

    if(GraphicsEditControlIsVisible()) {
        orig.mouse = Point2d::From(mx, my);
        orig.mouseOnButtonDown = orig.mouse;
        HideGraphicsEditControl();
        return;
    }
    SS.TW.HideEditControl();

    if(SS.showToolbar) {
        if(ToolbarMouseDown((int)mx, (int)my)) return;
    }

    // Make sure the hover is up to date.
    MouseMoved(mx, my, false, false, false, false, false);
    orig.mouse.x = mx;
    orig.mouse.y = my;
    orig.mouseOnButtonDown = orig.mouse;

    // The current mouse location
    Vector v = offset.ScaledBy(-1);
    v = v.Plus(projRight.ScaledBy(mx/scale));
    v = v.Plus(projUp.ScaledBy(my/scale));

    hRequest hr;
    switch(pending.operation) {
        case MNU_DATUM_POINT:
            hr = AddRequest(Request::DATUM_POINT);
            SK.GetEntity(hr.entity(0))->PointForceTo(v);
            ConstrainPointByHovered(hr.entity(0));

            ClearSuper();
            break;

        case MNU_LINE_SEGMENT:
            hr = AddRequest(Request::LINE_SEGMENT);
            SK.GetEntity(hr.entity(1))->PointForceTo(v);
            ConstrainPointByHovered(hr.entity(1));

            ClearSuper();

            pending.operation = DRAGGING_NEW_LINE_POINT;
            pending.point = hr.entity(2);
            pending.description = "click next point of line, or press Esc";
            SK.GetEntity(pending.point)->PointForceTo(v);
            break;

        case MNU_RECTANGLE: {
            if(!SS.GW.LockedInWorkplane()) {
                Error("Can't draw rectangle in 3d; select a workplane first.");
                ClearSuper();
                break;
            }
            hRequest lns[4];
            int i;
            SS.UndoRemember();
            for(i = 0; i < 4; i++) {
                lns[i] = AddRequest(Request::LINE_SEGMENT, false);
            }
            for(i = 0; i < 4; i++) {
                Constraint::ConstrainCoincident(
                    lns[i].entity(1), lns[(i+1)%4].entity(2));
                SK.GetEntity(lns[i].entity(1))->PointForceTo(v);
                SK.GetEntity(lns[i].entity(2))->PointForceTo(v);
            }
            for(i = 0; i < 4; i++) {
                Constraint::Constrain(
                    (i % 2) ? Constraint::HORIZONTAL : Constraint::VERTICAL,
                    Entity::NO_ENTITY, Entity::NO_ENTITY,
                    lns[i].entity(0));
            }
            ConstrainPointByHovered(lns[2].entity(1));

            pending.operation = DRAGGING_NEW_POINT;
            pending.point = lns[1].entity(2);
            pending.description = "click to place other corner of rectangle";
            break;
        }
        case MNU_CIRCLE:
            hr = AddRequest(Request::CIRCLE);
            // Centered where we clicked
            SK.GetEntity(hr.entity(1))->PointForceTo(v);
            // Normal to the screen
            SK.GetEntity(hr.entity(32))->NormalForceTo(
                Quaternion::From(SS.GW.projRight, SS.GW.projUp));
            // Initial radius zero
            SK.GetEntity(hr.entity(64))->DistanceForceTo(0);

            ConstrainPointByHovered(hr.entity(1));

            ClearSuper();

            pending.operation = DRAGGING_NEW_RADIUS;
            pending.circle = hr.entity(0);
            pending.description = "click to set radius";
            break;

        case MNU_ARC: {
            if(!SS.GW.LockedInWorkplane()) {
                Error("Can't draw arc in 3d; select a workplane first.");
                ClearPending();
                break;
            }
            hr = AddRequest(Request::ARC_OF_CIRCLE);
            // This fudge factor stops us from immediately failing to solve
            // because of the arc's implicit (equal radius) tangent.
            Vector adj = SS.GW.projRight.WithMagnitude(2/SS.GW.scale);
            SK.GetEntity(hr.entity(1))->PointForceTo(v.Minus(adj));
            SK.GetEntity(hr.entity(2))->PointForceTo(v);
            SK.GetEntity(hr.entity(3))->PointForceTo(v);
            ConstrainPointByHovered(hr.entity(2));

            ClearSuper();

            pending.operation = DRAGGING_NEW_ARC_POINT;
            pending.point = hr.entity(3);
            pending.description = "click to place point";
            break;
        }
        case MNU_CUBIC:
            hr = AddRequest(Request::CUBIC);
            SK.GetEntity(hr.entity(1))->PointForceTo(v);
            SK.GetEntity(hr.entity(2))->PointForceTo(v);
            SK.GetEntity(hr.entity(3))->PointForceTo(v);
            SK.GetEntity(hr.entity(4))->PointForceTo(v);
            ConstrainPointByHovered(hr.entity(1));

            ClearSuper();

            pending.operation = DRAGGING_NEW_CUBIC_POINT;
            pending.point = hr.entity(4);
            pending.description = "click next point of cubic, or press Esc";
            break;

        case MNU_WORKPLANE:
            if(LockedInWorkplane()) {
                Error("Sketching in a workplane already; sketch in 3d before "
                      "creating new workplane.");
                ClearSuper();
                break;
            }
            hr = AddRequest(Request::WORKPLANE);
            SK.GetEntity(hr.entity(1))->PointForceTo(v);
            SK.GetEntity(hr.entity(32))->NormalForceTo(
                Quaternion::From(SS.GW.projRight, SS.GW.projUp));
            ConstrainPointByHovered(hr.entity(1));

            ClearSuper();
            break;

        case MNU_TTF_TEXT: {
            if(!SS.GW.LockedInWorkplane()) {
                Error("Can't draw text in 3d; select a workplane first.");
                ClearSuper();
                break;
            }
            hr = AddRequest(Request::TTF_TEXT);
            Request *r = SK.GetRequest(hr);
            r->str.strcpy("Abc");
            r->font.strcpy("arial.ttf");

            SK.GetEntity(hr.entity(1))->PointForceTo(v);
            SK.GetEntity(hr.entity(2))->PointForceTo(v);

            pending.operation = DRAGGING_NEW_POINT;
            pending.point = hr.entity(2);
            pending.description = "click to place bottom left of text";
            break;
        }

        case MNU_COMMENT: {
            ClearSuper();
            Constraint c;
            ZERO(&c);
            c.group       = SS.GW.activeGroup;
            c.workplane   = SS.GW.ActiveWorkplane();
            c.type        = Constraint::COMMENT;
            c.disp.offset = v;
            c.comment.strcpy("NEW COMMENT -- DOUBLE-CLICK TO EDIT");
            Constraint::AddConstraint(&c);
            break;
        }

        case DRAGGING_RADIUS:
        case DRAGGING_NEW_POINT:
            // The MouseMoved event has already dragged it as desired.
            ClearPending();
            break;

        case DRAGGING_NEW_ARC_POINT:
            ConstrainPointByHovered(pending.point);
            ClearPending();
            break;

        case DRAGGING_NEW_CUBIC_POINT: {
            hRequest hr = pending.point.request();
            Request *r = SK.GetRequest(hr);

            if(hover.entity.v == hr.entity(1).v && r->extraPoints >= 2) {
                // They want the endpoints coincident, which means a periodic
                // spline instead.
                r->type = Request::CUBIC_PERIODIC;
                // Remove the off-curve control points, which are no longer
                // needed here; so move [2,ep+1] down, skipping first pt.
                int i;
                for(i = 2; i <= r->extraPoints+1; i++) {
                    SK.GetEntity(hr.entity((i-1)+1))->PointForceTo(
                        SK.GetEntity(hr.entity(i+1))->PointGetNum());
                }
                // and move ep+3 down by two, skipping both
                SK.GetEntity(hr.entity((r->extraPoints+1)+1))->PointForceTo(
                  SK.GetEntity(hr.entity((r->extraPoints+3)+1))->PointGetNum());
                r->extraPoints -= 2;
                // And we're done.
                SS.MarkGroupDirty(r->group);
                SS.ScheduleGenerateAll();
                ClearPending();
                break;
            }

            if(ConstrainPointByHovered(pending.point)) {
                ClearPending();
                break;
            }

            Entity e;
            if(r->extraPoints >= (int)arraylen(e.point) - 4) {
                ClearPending();
                break;
            }

            (SK.GetRequest(hr)->extraPoints)++;
            SS.GenerateAll(-1, -1);

            int ep = r->extraPoints;
            Vector last = SK.GetEntity(hr.entity(3+ep))->PointGetNum();

            SK.GetEntity(hr.entity(2+ep))->PointForceTo(last);
            SK.GetEntity(hr.entity(3+ep))->PointForceTo(v);
            SK.GetEntity(hr.entity(4+ep))->PointForceTo(v);
            pending.point = hr.entity(4+ep);
            break;
        }

        case DRAGGING_NEW_LINE_POINT: {
            if(hover.entity.v) {
                Entity *e = SK.GetEntity(hover.entity);
                if(e->IsPoint()) {
                    hRequest hrl = pending.point.request();
                    Entity *sp = SK.GetEntity(hrl.entity(1));
                    if(( e->PointGetNum()).Equals(
                       (sp->PointGetNum())))
                    {
                        // If we constrained by the hovered point, then we
                        // would create a zero-length line segment. That's
                        // not good, so just stop drawing.
                        ClearPending();
                        break;
                    }
                }
            }

            if(ConstrainPointByHovered(pending.point)) {
                ClearPending();
                break;
            }

            // Create a new line segment, so that we continue drawing.
            hRequest hr = AddRequest(Request::LINE_SEGMENT);
            SK.GetEntity(hr.entity(1))->PointForceTo(v);
            // Displace the second point of the new line segment slightly,
            // to avoid creating zero-length edge warnings.
            SK.GetEntity(hr.entity(2))->PointForceTo(
                v.Plus(projRight.ScaledBy(0.5/scale)));

            // Constrain the line segments to share an endpoint
            Constraint::ConstrainCoincident(pending.point, hr.entity(1));

            // And drag an endpoint of the new line segment
            pending.operation = DRAGGING_NEW_LINE_POINT;
            pending.point = hr.entity(2);
            pending.description = "click next point of line, or press Esc";

            break;
        }

        case 0:
        default:
            ClearPending();
            if(!hover.IsEmpty()) {
                hoverWasSelectedOnMousedown = IsSelected(&hover);
                MakeSelected(&hover);
            }
            break;
    }

    SS.ScheduleShowTW();
    InvalidateGraphics();
}
Exemple #28
0
//-----------------------------------------------------------------------------
// The edit control is visible, and the user just pressed enter.
//-----------------------------------------------------------------------------
void TextWindow::EditControlDone(const char *s) {
    edit.showAgain = false;

    switch(edit.meaning) {
        case EDIT_TIMES_REPEATED: {
            Expr *e = Expr::From(s, true);
            if(e) {
                SS.UndoRemember();

                double ev = e->Eval();
                if((int)ev < 1) {
                    Error("Can't repeat fewer than 1 time.");
                    break;
                }
                if((int)ev > 999) {
                    Error("Can't repeat more than 999 times.");
                    break;
                }

                Group *g = SK.GetGroup(edit.group);
                g->valA = ev;

                if(g->type == Group::ROTATE) {
                    int i, c = 0;
                    for(i = 0; i < SK.constraint.n; i++) {
                        if(SK.constraint.elem[i].group.v == g->h.v) c++;
                    }
                    // If the group does not contain any constraints, then
                    // set the numerical guess to space the copies uniformly
                    // over one rotation. Don't touch the guess if we're
                    // already constrained, because that would break
                    // convergence.
                    if(c == 0) {
                        double copies = (g->skipFirst) ? (ev + 1) : ev;
                        SK.GetParam(g->h.param(3))->val = PI/(2*copies);
                    }
                }

                SS.MarkGroupDirty(g->h);
                SS.later.generateAll = true;
            }
            break;
        }
        case EDIT_GROUP_NAME: {
            if(!StringAllPrintable(s) || !*s) {
                Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -");
            } else {
                SS.UndoRemember();

                Group *g = SK.GetGroup(edit.group);
                g->name.strcpy(s);
            }
            break;
        }
        case EDIT_GROUP_SCALE: {
            Expr *e = Expr::From(s, true);
            if(e) {
                double ev = e->Eval();
                if(fabs(ev) < 1e-6) {
                    Error("Scale cannot be zero.");
                } else {
                    Group *g = SK.GetGroup(edit.group);
                    g->scale = ev;
                    SS.MarkGroupDirty(g->h);
                    SS.later.generateAll = true;
                }
            }
            break;
        }
        case EDIT_GROUP_COLOR: {
            Vector rgb;
            if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
                rgb = rgb.ClampWithin(0, 1);

                Group *g = SK.group.FindByIdNoOops(SS.TW.shown.group);
                if(!g) break;
                g->color = RGBf(rgb.x, rgb.y, rgb.z);

                SS.MarkGroupDirty(g->h);
                SS.later.generateAll = true;
                SS.GW.ClearSuper();
            } else {
                Error("Bad format: specify color as r, g, b");
            }
            break;
        }
        case EDIT_TTF_TEXT: {
            SS.UndoRemember();
            Request *r = SK.request.FindByIdNoOops(edit.request);
            if(r) {
                r->str.strcpy(s);
                SS.MarkGroupDirty(r->group);
                SS.later.generateAll = true;
            }
            break;
        }
        case EDIT_STEP_DIM_FINISH: {
            Expr *e = Expr::From(s, true);
            if(!e) {
                break;
            }
            if(shown.dimIsDistance) {
                shown.dimFinish = SS.ExprToMm(e);
            } else {
                shown.dimFinish = e->Eval();
            }
            break;
        }
        case EDIT_STEP_DIM_STEPS:
            shown.dimSteps = min(300, max(1, atoi(s)));
            break;

        case EDIT_TANGENT_ARC_RADIUS: {
            Expr *e = Expr::From(s, true);
            if(!e) break;
            if(e->Eval() < LENGTH_EPS) {
                Error("Radius cannot be zero or negative.");
                break;
            }
            SS.tangentArcRadius = SS.ExprToMm(e);
            break;
        }

        default: {
            int cnt = 0;
            if(EditControlDoneForStyles(s))         cnt++;
            if(EditControlDoneForConfiguration(s))  cnt++;
            if(EditControlDoneForPaste(s))          cnt++;
            if(EditControlDoneForView(s))           cnt++;
            if(cnt > 1) {
                // The identifiers were somehow assigned not uniquely?
                oops();
            }
            break;
        }
    }
    InvalidateGraphics();
    SS.later.showTW = true;

    if(!edit.showAgain) {
        HideEditControl();
        edit.meaning = EDIT_NOTHING;
    }
}
Exemple #29
0
void SolveSpaceUI::MenuAnalyze(int id) {
    SS.GW.GroupSelection();
#define gs (SS.GW.gs)

    switch(id) {
        case GraphicsWindow::MNU_STEP_DIM:
            if(gs.constraints == 1 && gs.n == 0) {
                Constraint *c = SK.GetConstraint(gs.constraint[0]);
                if(c->HasLabel() && !c->reference) {
                    SS.TW.shown.dimFinish = c->valA;
                    SS.TW.shown.dimSteps = 10;
                    SS.TW.shown.dimIsDistance =
                        (c->type != Constraint::ANGLE) &&
                        (c->type != Constraint::LENGTH_RATIO) &&
                        (c->type != Constraint::LENGTH_DIFFERENCE);
                    SS.TW.shown.constraint = c->h;
                    SS.TW.shown.screen = TextWindow::SCREEN_STEP_DIMENSION;

                    // The step params are specified in the text window,
                    // so force that to be shown.
                    SS.GW.ForceTextWindowShown();

                    SS.ScheduleShowTW();
                    SS.GW.ClearSelection();
                } else {
                    Error("Constraint must have a label, and must not be "
                          "a reference dimension.");
                }
            } else {
                Error("Bad selection for step dimension; select a constraint.");
            }
            break;

        case GraphicsWindow::MNU_NAKED_EDGES: {
            SS.nakedEdges.Clear();

            Group *g = SK.GetGroup(SS.GW.activeGroup);
            SMesh *m = &(g->displayMesh);
            SKdNode *root = SKdNode::From(m);
            bool inters, leaks;
            root->MakeCertainEdgesInto(&(SS.nakedEdges),
                SKdNode::NAKED_OR_SELF_INTER_EDGES, true, &inters, &leaks);

            InvalidateGraphics();

            const char *intersMsg = inters ?
                "The mesh is self-intersecting (NOT okay, invalid)." :
                "The mesh is not self-intersecting (okay, valid).";
            const char *leaksMsg = leaks ?
                "The mesh has naked edges (NOT okay, invalid)." :
                "The mesh is watertight (okay, valid).";

            std::string cntMsg = ssprintf("\n\nThe model contains %d triangles, from "
                            "%d surfaces.", g->displayMesh.l.n, g->runningShell.surface.n);

            if(SS.nakedEdges.l.n == 0) {
                Message("%s\n\n%s\n\nZero problematic edges, good.%s",
                    intersMsg, leaksMsg, cntMsg.c_str());
            } else {
                Error("%s\n\n%s\n\n%d problematic edges, bad.%s",
                    intersMsg, leaksMsg, SS.nakedEdges.l.n, cntMsg.c_str());
            }
            break;
        }

        case GraphicsWindow::MNU_INTERFERENCE: {
            SS.nakedEdges.Clear();

            SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
            SKdNode *root = SKdNode::From(m);
            bool inters, leaks;
            root->MakeCertainEdgesInto(&(SS.nakedEdges),
                SKdNode::SELF_INTER_EDGES, false, &inters, &leaks);

            InvalidateGraphics();

            if(inters) {
                Error("%d edges interfere with other triangles, bad.",
                    SS.nakedEdges.l.n);
            } else {
                Message("The assembly does not interfere, good.");
            }
            break;
        }

        case GraphicsWindow::MNU_VOLUME: {
            SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);

            double vol = 0;
            int i;
            for(i = 0; i < m->l.n; i++) {
                STriangle tr = m->l.elem[i];

                // Translate to place vertex A at (x, y, 0)
                Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
                tr.a = (tr.a).Minus(trans);
                tr.b = (tr.b).Minus(trans);
                tr.c = (tr.c).Minus(trans);

                // Rotate to place vertex B on the y-axis. Depending on
                // whether the triangle is CW or CCW, C is either to the
                // right or to the left of the y-axis. This handles the
                // sign of our normal.
                Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
                u = u.WithMagnitude(1);
                Vector v = Vector::From(tr.b.x, tr.b.y, 0);
                v = v.WithMagnitude(1);
                Vector n = Vector::From(0, 0, 1);

                tr.a = (tr.a).DotInToCsys(u, v, n);
                tr.b = (tr.b).DotInToCsys(u, v, n);
                tr.c = (tr.c).DotInToCsys(u, v, n);

                n = tr.Normal().WithMagnitude(1);

                // Triangles on edge don't contribute
                if(fabs(n.z) < LENGTH_EPS) continue;

                // The plane has equation p dot n = a dot n
                double d = (tr.a).Dot(n);
                // nx*x + ny*y + nz*z = d
                // nz*z = d - nx*x - ny*y
                double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;

                double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
                double xc = tr.c.x, yb = tr.b.y;

                // I asked Maple for
                //    int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
                double integral =
                    (1.0/3)*(
                        A*(mbc-mac)+
                        (1.0/2)*B*(mbc*mbc-mac*mac)
                    )*(xc*xc*xc)+
                    (1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
                    C*yb*xc+
                    (1.0/2)*B*yb*yb*xc;

                vol += integral;
            }

            std::string msg = ssprintf("The volume of the solid model is:\n\n""    %.3f %s^3",
                vol / pow(SS.MmPerUnit(), 3),
                SS.UnitName());

            if(SS.viewUnits == SolveSpaceUI::UNIT_MM) {
                msg += ssprintf("\n    %.2f mL", vol/(10*10*10));
            }
            msg += "\n\nCurved surfaces have been approximated as triangles.\n"
                   "This introduces error, typically of around 1%.";
            Message("%s", msg.c_str());
            break;
        }

        case GraphicsWindow::MNU_AREA: {
            Group *g = SK.GetGroup(SS.GW.activeGroup);
            if(g->polyError.how != Group::POLY_GOOD) {
                Error("This group does not contain a correctly-formed "
                      "2d closed area. It is open, not coplanar, or self-"
                      "intersecting.");
                break;
            }
            SEdgeList sel = {};
            g->polyLoops.MakeEdgesInto(&sel);
            SPolygon sp = {};
            sel.AssemblePolygon(&sp, NULL, true);
            sp.normal = sp.ComputeNormal();
            sp.FixContourDirections();
            double area = sp.SignedArea();
            double scale = SS.MmPerUnit();
            Message("The area of the region sketched in this group is:\n\n"
                    "    %.3f %s^2\n\n"
                    "Curves have been approximated as piecewise linear.\n"
                    "This introduces error, typically of around 1%%.",
                area / (scale*scale),
                SS.UnitName());
            sel.Clear();
            sp.Clear();
            break;
        }

        case GraphicsWindow::MNU_SHOW_DOF:
            // This works like a normal solve, except that it calculates
            // which variables are free/bound at the same time.
            SS.GenerateAll(SolveSpaceUI::GENERATE_ALL, true);
            break;

        case GraphicsWindow::MNU_TRACE_PT:
            if(gs.points == 1 && gs.n == 1) {
                SS.traced.point = gs.point[0];
                SS.GW.ClearSelection();
            } else {
                Error("Bad selection for trace; select a single point.");
            }
            break;

        case GraphicsWindow::MNU_STOP_TRACING: {
            std::string exportFile;
            if(GetSaveFile(&exportFile, "", CsvFileFilter)) {
                FILE *f = ssfopen(exportFile, "wb");
                if(f) {
                    int i;
                    SContour *sc = &(SS.traced.path);
                    for(i = 0; i < sc->l.n; i++) {
                        Vector p = sc->l.elem[i].p;
                        double s = SS.exportScale;
                        fprintf(f, "%.10f, %.10f, %.10f\r\n",
                            p.x/s, p.y/s, p.z/s);
                    }
                    fclose(f);
                } else {
                    Error("Couldn't write to '%s'", exportFile.c_str());
                }
            }
            // Clear the trace, and stop tracing
            SS.traced.point = Entity::NO_ENTITY;
            SS.traced.path.l.Clear();
            InvalidateGraphics();
            break;
        }

        default: oops();
    }
}
Exemple #30
0
void TextWindow::ScreenChangeCheckClosedContour(int link, uint32_t v) {
    SS.checkClosedContour = !SS.checkClosedContour;
    InvalidateGraphics();
}