コード例 #1
0
ファイル: draw.cpp プロジェクト: tmpvar/solvespace
//-----------------------------------------------------------------------------
// Select everything that lies within the marquee view-aligned rectangle. For
// points, we test by the point location. For normals, we test by the normal's
// associated point. For anything else, we test by any piecewise linear edge.
//-----------------------------------------------------------------------------
void GraphicsWindow::SelectByMarquee(void) {
    Point2d begin = ProjectPoint(orig.marqueePoint);
    double xmin = min(orig.mouse.x, begin.x),
           xmax = max(orig.mouse.x, begin.x),
           ymin = min(orig.mouse.y, begin.y),
           ymax = max(orig.mouse.y, begin.y);

    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;

        if(e->IsPoint() || e->IsNormal()) {
            Vector p = e->IsPoint() ? e->PointGetNum() :
                                      SK.GetEntity(e->point[0])->PointGetNum();
            Point2d pp = ProjectPoint(p);
            if(pp.x >= xmin && pp.x <= xmax &&
               pp.y >= ymin && pp.y <= ymax)
            {
                MakeSelected(e->h);
            }
        } else {
            // Use the 3d bounding box test routines, to avoid duplication;
            // so let our bounding square become a bounding box that certainly
            // includes the z = 0 plane.
            Vector ptMin = Vector::From(xmin, ymin, -1),
                   ptMax = Vector::From(xmax, ymax, 1);
            SEdgeList sel;
            ZERO(&sel);
            e->GenerateEdges(&sel, true);
            SEdge *se;
            for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) {
                Point2d ppa = ProjectPoint(se->a),
                        ppb = ProjectPoint(se->b);
                Vector  ptA = Vector::From(ppa.x, ppa.y, 0),
                        ptB = Vector::From(ppb.x, ppb.y, 0);
                if(Vector::BoundingBoxIntersectsLine(ptMax, ptMin,
                                                     ptA, ptB, true) ||
                   !ptA.OutsideAndNotOn(ptMax, ptMin) ||
                   !ptB.OutsideAndNotOn(ptMax, ptMin))
                {
                    MakeSelected(e->h);
                    break;
                }
            }
            sel.Clear();
        }
    }
}
コード例 #2
0
ファイル: mouse.cpp プロジェクト: astarasikov/solvespace
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;
}
コード例 #3
0
ファイル: mouse.cpp プロジェクト: astarasikov/solvespace
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();
}
コード例 #4
0
ファイル: mouse.cpp プロジェクト: astarasikov/solvespace
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();
}
コード例 #5
0
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
    Entity *wrkpl  = SK.GetEntity(ActiveWorkplane());
    Entity *wrkpln = SK.GetEntity(wrkpl->normal);
    Vector u = wrkpln->NormalU(),
           v = wrkpln->NormalV(),
           n = wrkpln->NormalN(),
           p = SK.GetEntity(wrkpl->point[0])->PointGetNum();

    ClipboardRequest *cr;
    for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) {
        hRequest hr = AddRequest(cr->type, false);
        Request *r = SK.GetRequest(hr);
        r->extraPoints  = cr->extraPoints;
        r->style        = cr->style;
        r->str.strcpy(    cr->str.str);
        r->font.strcpy(   cr->font.str);
        r->construction = cr->construction;
        // Need to regen to get the right number of points, if extraPoints
        // changed.
        SS.GenerateAll(-1, -1);
        SS.MarkGroupDirty(r->group);
        bool hasDistance;
        int i, pts;
        EntReqTable::GetRequestInfo(r->type, r->extraPoints,
            NULL, &pts, NULL, &hasDistance);
        for(i = 0; i < pts; i++) {
            Vector pt = cr->point[i];
            // We need the reflection to occur within the workplane; it may
            // otherwise correspond to just a rotation as projected.
            if(scale < 0) {
                pt.x *= -1;
            }
            // Likewise the scale, which could otherwise take us out of the
            // workplane.
            pt = pt.ScaledBy(fabs(scale));
            pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
            pt = pt.Plus(p);
            pt = pt.RotatedAbout(n, theta);
            pt = pt.Plus(trans);
            SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
        }
        if(hasDistance) {
            SK.GetEntity(hr.entity(64))->DistanceForceTo(
                                            cr->distance*fabs(scale));
        }

        cr->newReq = hr;
        MakeSelected(hr.entity(0));
        for(i = 0; i < pts; i++) {
            MakeSelected(hr.entity(i+1));
        }
    }
    
    Constraint *c;
    for(c = SS.clipboard.c.First(); c; c = SS.clipboard.c.NextAfter(c)) {
        if(c->type == Constraint::POINTS_COINCIDENT) {
            Constraint::ConstrainCoincident(SS.clipboard.NewEntityFor(c->ptA),
                                            SS.clipboard.NewEntityFor(c->ptB));
        }
    }

    SS.later.generateAll = true;
}
コード例 #6
0
ファイル: clipboard.cpp プロジェクト: eMVe/solvespace
void GraphicsWindow::PasteClipboard(Vector trans, double theta, double scale) {
    Entity *wrkpl  = SK.GetEntity(ActiveWorkplane());
    Entity *wrkpln = SK.GetEntity(wrkpl->normal);
    Vector u = wrkpln->NormalU(),
           v = wrkpln->NormalV(),
           n = wrkpln->NormalN(),
           p = SK.GetEntity(wrkpl->point[0])->PointGetNum();

    ClipboardRequest *cr;
	int requestCount = 0;
    for(cr = SS.clipboard.r.First(); cr; cr = SS.clipboard.r.NextAfter(cr)) {
        hRequest hr = AddRequest(cr->type, false);
        Request *r = SK.GetRequest(hr);
        r->extraPoints  = cr->extraPoints;
        r->style        = cr->style;
        r->str.strcpy(    cr->str.str);
        r->font.strcpy(   cr->font.str);
        r->construction = cr->construction;
        // Need to regen to get the right number of points, if extraPoints
        // changed.
        SS.GenerateAll(-1, -1);
        SS.MarkGroupDirty(r->group);
        bool hasDistance;
        int i, pts;
        EntReqTable::GetRequestInfo(r->type, r->extraPoints,
            NULL, &pts, NULL, &hasDistance);
        for(i = 0; i < pts; i++) {
            Vector pt = cr->point[i];
            // We need the reflection to occur within the workplane; it may
            // otherwise correspond to just a rotation as projected.
            if(scale < 0) {
                pt.x *= -1;
            }
            // Likewise the scale, which could otherwise take us out of the
            // workplane.
            pt = pt.ScaledBy(fabs(scale));
            pt = pt.ScaleOutOfCsys(u, v, Vector::From(0, 0, 0));
            pt = pt.Plus(p);
            pt = pt.RotatedAbout(n, theta);
            pt = pt.Plus(trans);
            SK.GetEntity(hr.entity(i+1))->PointForceTo(pt);
        }
        if(hasDistance) {
            SK.GetEntity(hr.entity(64))->DistanceForceTo(
                                            cr->distance*fabs(scale));
        }

        cr->newReq = hr;
        MakeSelected(hr.entity(0));
        for(i = 0; i < pts; i++) {
            MakeSelected(hr.entity(i+1));
        }
		++requestCount;
    }
    
    Constraint *c;
    for(c = SS.clipboard.c.First(); c; c = SS.clipboard.c.NextAfter(c)) {
        if(c->type == Constraint::POINTS_COINCIDENT) {
            Constraint::ConstrainCoincident(SS.clipboard.NewEntityFor(c->ptA),
                                            SS.clipboard.NewEntityFor(c->ptB));
        }
		if (c->type == Constraint::HORIZONTAL || c->type == Constraint::VERTICAL) { //mv horizontally constrainted object is either a line segment or 2 points
			if (!SS.copyConstraints) continue;
            Entity *ea = SK.GetEntityNoOops(c->entityA);
            if(ea && ea->type == Entity::LINE_SEGMENT) {
				if (!SS.copyConstraints) continue;
                Constraint::Constrain(c->type,
                                        Entity::NO_ENTITY,
                                        Entity::NO_ENTITY,
                                        SS.clipboard.NewEntityFor(c->entityA));                                        
            }
        
            else {
                Constraint::Constrain(c->type, SS.clipboard.NewEntityFor(c->ptA),
                                            SS.clipboard.NewEntityFor(c->ptB),
                                            Entity::NO_ENTITY);
            }
        }
		if (c->type == Constraint::DIAMETER) {
			if (!SS.copyConstraints) continue;
			Entity *ea = SK.GetEntityNoOops(c->entityA);
			if (ea && (ea->type == Entity::CIRCLE || ea->type == Entity::ARC_OF_CIRCLE)) {
				Constraint::ConstrainValue(c->type,
					Entity::NO_ENTITY,
					Entity::NO_ENTITY,
					SS.clipboard.NewEntityFor(c->entityA),
					Entity::NO_ENTITY,
					c->valA);
			}
		}
		if (c->type == Constraint::PT_PT_DISTANCE) {
			if (!SS.copyConstraints) continue;
			Constraint::ConstrainValue(c->type,
				SS.clipboard.NewEntityFor(c->ptA),
				SS.clipboard.NewEntityFor(c->ptB),
				Entity::NO_ENTITY,
				Entity::NO_ENTITY,
				c->valA);
		}
		if (c->type == Constraint::PT_LINE_DISTANCE) {
			if (!SS.copyConstraints) continue;
			Constraint::ConstrainValue(c->type,
				SS.clipboard.NewEntityFor(c->ptA),
				Entity::NO_ENTITY,
				SS.clipboard.NewEntityFor(c->entityA),
				Entity::NO_ENTITY,
				c->valA);
		}
		if (c->type == Constraint::EQUAL_RADIUS /*&& requestCount == 1*/) {
			if (!SS.copyConstraints) continue;	
			ConstrainSameSomething(c);
		}
		if (c->type == Constraint::EQUAL_LENGTH_LINES) {   //eMVe6. Dangerous. Can make solvespace unresponsive.
			if (!SS.copyConstraints) continue;
			ConstrainSameSomething(c);
		}
		if (c->type == Constraint::ANGLE) {
			if (!SS.copyConstraints) continue;
				/* UNRELIABLE & SLOW AS HELL
			if (SS.clipboard.ContainsEntity(c->entityA) && SS.clipboard.ContainsEntity(c->entityB)) {
				Constraint::ConstrainValue(c->type,
					Entity::NO_ENTITY,
					Entity::NO_ENTITY,
					SS.clipboard.NewEntityFor(c->entityA),
					SS.clipboard.NewEntityFor(c->entityB),
					c->valA);
			}
			*/
		}
		if (c->type == Constraint::PARALLEL) {
			if (!SS.copyConstraints) continue;
			ConstrainSameSomething(c);
		}
		if (c->type == Constraint::SYMMETRIC_LINE) {
			if (SS.clipboard.ContainsEntity(c->ptA) &&
				SS.clipboard.ContainsEntity(c->ptB) &&
				SS.clipboard.ContainsEntity(c->entityA)) {
				Constraint::Constrain(c->type,
					SS.clipboard.NewEntityFor(c->ptA),
					SS.clipboard.NewEntityFor(c->ptB),
					SS.clipboard.NewEntityFor(c->entityA),
					Entity::NO_ENTITY,
					false,
					false);
			}
		}
	}
    SS.later.generateAll = true;
}
コード例 #7
0
ファイル: draw.cpp プロジェクト: tmpvar/solvespace
//-----------------------------------------------------------------------------
// Select an item, if it isn't selected already.
//-----------------------------------------------------------------------------
void GraphicsWindow::MakeSelected(hEntity he) {
    Selection stog;
    ZERO(&stog);
    stog.entity = he;
    MakeSelected(&stog);
}