Example #1
0
void GraphicsWindow::AnimateOntoWorkplane(void) {
    if(!LockedInWorkplane()) return;

    Entity *w = SK.GetEntity(ActiveWorkplane());
    Quaternion quatf = w->Normal()->NormalGetNum();
    Vector offsetf = (SK.GetEntity(w->point[0])->PointGetNum()).ScaledBy(-1);

    AnimateOnto(quatf, offsetf);
}
Example #2
0
GraphicsWindow::SuggestedConstraint GraphicsWindow::SuggestLineConstraint(hRequest request) {
    if(LockedInWorkplane()) {
        Entity *ptA = SK.GetEntity(request.entity(1)),
               *ptB = SK.GetEntity(request.entity(2));

        Expr *au, *av, *bu, *bv;

        ptA->PointGetExprsInWorkplane(ActiveWorkplane(), &au, &av);
        ptB->PointGetExprsInWorkplane(ActiveWorkplane(), &bu, &bv);

        double du = au->Minus(bu)->Eval();
        double dv = av->Minus(bv)->Eval();

        const double TOLERANCE_RATIO = 0.02;
        if(fabs(dv) > LENGTH_EPS && fabs(du / dv) < TOLERANCE_RATIO)
            return SUGGESTED_VERTICAL;
        else if(fabs(du) > LENGTH_EPS && fabs(dv / du) < TOLERANCE_RATIO)
            return SUGGESTED_HORIZONTAL;
        else
            return SUGGESTED_NONE;
    } else {
        return SUGGESTED_NONE;
    }
}
Example #3
0
Vector GraphicsWindow::SnapToGrid(Vector p) {
    if(!LockedInWorkplane()) return p;

    EntityBase *wrkpl = SK.GetEntity(ActiveWorkplane()),
               *norm  = wrkpl->Normal();
    Vector wo = SK.GetEntity(wrkpl->point[0])->PointGetNum(),
           wu = norm->NormalU(),
           wv = norm->NormalV(),
           wn = norm->NormalN();

    Vector pp = (p.Minus(wo)).DotInToCsys(wu, wv, wn);
    pp.x = floor((pp.x / SS.gridSpacing) + 0.5)*SS.gridSpacing;
    pp.y = floor((pp.y / SS.gridSpacing) + 0.5)*SS.gridSpacing;
    pp.z = 0;

    return pp.ScaleOutOfCsys(wu, wv, wn).Plus(wo);
}
Example #4
0
hRequest GraphicsWindow::AddRequest(int type, bool rememberForUndo) {
    if(rememberForUndo) SS.UndoRemember();

    Request r;
    memset(&r, 0, sizeof(r));
    r.group = activeGroup;
    Group *g = SK.GetGroup(activeGroup);
    if(g->type == Group::DRAWING_3D || g->type == Group::DRAWING_WORKPLANE) {
        r.construction = false;
    } else {
        r.construction = true;
    }
    r.workplane = ActiveWorkplane();
    r.type = type;
    SK.request.AddAndAssignId(&r);

    // We must regenerate the parameters, so that the code that tries to
    // place this request's entities where the mouse is can do so. But
    // we mustn't try to solve until reasonable values have been supplied
    // for these new parameters, or else we'll get a numerical blowup.
    SS.GenerateAll(-1, -1);
    SS.MarkGroupDirty(r.group);
    return r.h;
}
Example #5
0
//-----------------------------------------------------------------------------
// A single point must be selected when this function is called. We find two
// non-construction line segments that join at this point, and create a
// tangent arc joining them.
//-----------------------------------------------------------------------------
void GraphicsWindow::MakeTangentArc(void) {
    if(!LockedInWorkplane()) {
        Error("Must be sketching in workplane to create tangent "
              "arc.");
        return;
    }

    // The point corresponding to the vertex to be rounded.
    Vector pshared = SK.GetEntity(gs.point[0])->PointGetNum();
    ClearSelection();

    // First, find two requests (that are not construction, and that are
    // in our group and workplane) that generate entities that have an
    // endpoint at our vertex to be rounded.
    int i, c = 0;
    Entity *ent[2];
    Request *req[2];
    hRequest hreq[2];
    hEntity hent[2];
    bool pointf[2];
    for(i = 0; i < SK.request.n; i++) {
        Request *r = &(SK.request.elem[i]);
        if(r->group.v != activeGroup.v) continue;
        if(r->workplane.v != ActiveWorkplane().v) continue;
        if(r->construction) continue;
        if(r->type != Request::LINE_SEGMENT &&
           r->type != Request::ARC_OF_CIRCLE)
        {
            continue;
        }

        Entity *e = SK.GetEntity(r->h.entity(0));
        Vector ps = e->EndpointStart(),
               pf = e->EndpointFinish();
        
        if(ps.Equals(pshared) || pf.Equals(pshared)) {
            if(c < 2) {
                // We record the entity and request and their handles,
                // and whether the vertex to be rounded is the start or
                // finish of this entity.
                ent[c] = e;
                hent[c] = e->h;
                req[c] = r;
                hreq[c] = r->h;
                pointf[c] = (pf.Equals(pshared));
            }
            c++;
        }
    }
    if(c != 2) {
        Error("To create a tangent arc, select a point where two "
              "non-construction lines or cicles in this group and "
              "workplane join.");
        return;
    }

    Entity *wrkpl = SK.GetEntity(ActiveWorkplane());
    Vector wn = wrkpl->Normal()->NormalN();

    // Based on these two entities, we make the objects that we'll use to
    // numerically find the tangent arc.
    ParametricCurve pc[2];
    pc[0].MakeFromEntity(ent[0]->h, pointf[0]);
    pc[1].MakeFromEntity(ent[1]->h, pointf[1]);

    // And thereafter we mustn't touch the entity or req ptrs,
    // because the new requests/entities we add might force a
    // realloc.
    memset(ent, 0, sizeof(ent));
    memset(req, 0, sizeof(req));

    Vector pinter;
    double r, vv;
    // We now do Newton iterations to find the tangent arc, and its positions
    // t back along the two curves, starting from shared point of the curves
    // at t = 0. Lots of iterations helps convergence, and this is still
    // ~10 ms for everything.
    int iters = 1000;
    double t[2] = { 0, 0 }, tp[2];
    for(i = 0; i < iters + 20; i++) {
        Vector p0 = pc[0].PointAt(t[0]),
               p1 = pc[1].PointAt(t[1]),
               t0 = pc[0].TangentAt(t[0]),
               t1 = pc[1].TangentAt(t[1]);

        pinter = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
                                               p1, p1.Plus(t1),
                                               NULL, NULL, NULL);

        // The sign of vv determines whether shortest distance is
        // clockwise or anti-clockwise.
        Vector v = (wn.Cross(t0)).WithMagnitude(1);
        vv = t1.Dot(v);

        double dot = (t0.WithMagnitude(1)).Dot(t1.WithMagnitude(1));
        double theta = acos(dot);

        if(SS.tangentArcManual) {
            r = SS.tangentArcRadius;
        } else {
            r = 200/scale;
            // Set the radius so that no more than one third of the 
            // line segment disappears.
            r = min(r, pc[0].LengthForAuto()*tan(theta/2));
            r = min(r, pc[1].LengthForAuto()*tan(theta/2));;
        }
        // We are source-stepping the radius, to improve convergence. So
        // ramp that for most of the iterations, and then do a few at
        // the end with that constant for polishing.
        if(i < iters) {
            r *= 0.1 + 0.9*i/((double)iters);
        }

        // The distance from the intersection of the lines to the endpoint
        // of the arc, along each line.
        double el = r/tan(theta/2);

        // Compute the endpoints of the arc, for each curve
        Vector pa0 = pinter.Plus(t0.WithMagnitude(el)),
               pa1 = pinter.Plus(t1.WithMagnitude(el));

        tp[0] = t[0];
        tp[1] = t[1];

        // And convert those points to parameter values along the curve.
        t[0] += (pa0.Minus(p0)).DivPivoting(t0);
        t[1] += (pa1.Minus(p1)).DivPivoting(t1);
    }

    // Stupid check for convergence, and for an out of range result (as
    // we would get, for example, if the line is too short to fit the
    // rounding arc).
    if(fabs(tp[0] - t[0]) > 1e-3 || fabs(tp[1] - t[1]) > 1e-3 ||
        t[0] < 0.01 || t[1] < 0.01 ||
        t[0] > 0.99 || t[1] > 0.99 ||
        isnan(t[0]) || isnan(t[1]))
    {
        Error("Couldn't round this corner. Try a smaller radius, or try "
              "creating the desired geometry by hand with tangency "
              "constraints.");
        return;
    }

    // Compute the location of the center of the arc
    Vector center = pc[0].PointAt(t[0]),
           v0inter = pinter.Minus(center);
    int a, b;
    if(vv < 0) {
        a = 1; b = 2;
        center = center.Minus(v0inter.Cross(wn).WithMagnitude(r));
    } else {
        a = 2; b = 1;
        center = center.Plus(v0inter.Cross(wn).WithMagnitude(r));
    }

    SS.UndoRemember();

    hRequest harc = AddRequest(Request::ARC_OF_CIRCLE, false);
    Entity *earc = SK.GetEntity(harc.entity(0));
    hEntity hearc = earc->h;

    SK.GetEntity(earc->point[0])->PointForceTo(center);
    SK.GetEntity(earc->point[a])->PointForceTo(pc[0].PointAt(t[0]));
    SK.GetEntity(earc->point[b])->PointForceTo(pc[1].PointAt(t[1]));
    
    earc = NULL;

    pc[0].CreateRequestTrimmedTo(t[0], !SS.tangentArcDeleteOld,
                hent[0], hearc, (b == 1));
    pc[1].CreateRequestTrimmedTo(t[1], !SS.tangentArcDeleteOld,
                hent[1], hearc, (a == 1));

    // Now either make the original entities construction, or delete them
    // entirely, according to user preference.
    Request *re;
    SK.request.ClearTags();
    for(re = SK.request.First(); re; re = SK.request.NextAfter(re)) {
        if(re->h.v == hreq[0].v || re->h.v == hreq[1].v) {
            if(SS.tangentArcDeleteOld) {
                re->tag = 1;
            } else {
                re->construction = true;
            }
        }
    }
    if(SS.tangentArcDeleteOld) {
        DeleteTaggedRequests();
    }

    SS.later.generateAll = true;
}
void GraphicsWindow::CopySelection(void) {
    SS.clipboard.Clear();

    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();

    List<Selection> *ls = &(selection);
    for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
        if(!s->entity.v) continue;
        // Work only on entities that have requests that will generate them.
        Entity *e = SK.GetEntity(s->entity);
        bool hasDistance;
        int req, pts;
        if(!EntReqTable::GetEntityInfo(e->type, e->extraPoints,
                &req, &pts, NULL, &hasDistance))
        {
            continue;
        }
        if(req == Request::WORKPLANE) continue;

        ClipboardRequest cr;
        ZERO(&cr);
        cr.type         = req;
        cr.extraPoints  = e->extraPoints;
        cr.style        = e->style;
        cr.str.strcpy(    e->str.str);
        cr.font.strcpy(   e->font.str);
        cr.construction = e->construction;
        for(int i = 0; i < pts; i++) {
            Vector pt = SK.GetEntity(e->point[i])->PointGetNum();
            pt = pt.Minus(p);
            pt = pt.DotInToCsys(u, v, n);
            cr.point[i] = pt;
        }
        if(hasDistance) {
            cr.distance = SK.GetEntity(e->distance)->DistanceGetNum();
        }

        cr.oldEnt = e->h;
        for(int i = 0; i < pts; i++) {
            cr.oldPointEnt[i] = e->point[i];
        }

        SS.clipboard.r.Add(&cr);
    }

    Constraint *c;
    for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
        if(c->type == Constraint::POINTS_COINCIDENT) {
            if(!SS.clipboard.ContainsEntity(c->ptA)) continue;
            if(!SS.clipboard.ContainsEntity(c->ptB)) continue;
        } else {
            continue;
        }
        SS.clipboard.c.Add(c);
    }
}
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;
}
Example #8
0
void GraphicsWindow::CopySelection(void) {
    SS.clipboard.Clear();

    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();

    List<Selection> *ls = &(selection);
    for(Selection *s = ls->First(); s; s = ls->NextAfter(s)) {
        if(!s->entity.v) continue;
        // Work only on entities that have requests that will generate them.
        Entity *e = SK.GetEntity(s->entity);
        bool hasDistance;
        int req, pts;
        if(!EntReqTable::GetEntityInfo(e->type, e->extraPoints,
                &req, &pts, NULL, &hasDistance))
        {
            continue;
        }
        if(req == Request::WORKPLANE) continue;

        ClipboardRequest cr;
        ZERO(&cr);
        cr.type         = req;
        cr.extraPoints  = e->extraPoints;
        cr.style        = e->style;
        cr.str.strcpy(    e->str.str);
        cr.font.strcpy(   e->font.str);
        cr.construction = e->construction;
        {for(int i = 0; i < pts; i++) {
            Vector pt = SK.GetEntity(e->point[i])->PointGetNum();
            pt = pt.Minus(p);
            pt = pt.DotInToCsys(u, v, n);
            cr.point[i] = pt;
        }}
        if(hasDistance) {
            cr.distance = SK.GetEntity(e->distance)->DistanceGetNum();
        }

        cr.oldEnt = e->h;
        for(int i = 0; i < pts; i++) {
            cr.oldPointEnt[i] = e->point[i];
        }

        SS.clipboard.r.Add(&cr);
    }

    Constraint *c;
    for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) {
        if(c->type == Constraint::POINTS_COINCIDENT) {
            if(!SS.clipboard.ContainsEntityWithPoint(c->ptA)) continue;  //mv no entities for given points -> empty points -> don't constrain
            if(!SS.clipboard.ContainsEntityWithPoint(c->ptB)) continue;
        }
		else if (c->type == Constraint::HORIZONTAL || c->type == Constraint::VERTICAL) {
			if (!SS.copyConstraints) continue;
            Entity *ea = SK.GetEntityNoOops(c->ptA);
            Entity *eb = SK.GetEntityNoOops(c->ptB);
            if(ea||eb) {
				if (!SS.clipboard.ContainsEntityWithPoint(c->ptA)) continue;
				if (!SS.clipboard.ContainsEntityWithPoint(c->ptB)) continue;
            }
            if(ea||eb) {
				Entity *ec = SK.GetEntityNoOops(c->entityA);
                if(ec) continue;
            }
			if (!(ea&&eb)) {	//mv constraint with no points -> most likely constrained object
				if (!SS.clipboard.ContainsEntity(c->entityA)) continue;
			}
        }
		else if (c->type == Constraint::PT_PT_DISTANCE) {
			if (!SS.copyConstraints) continue;
			if (!SS.clipboard.ContainsEntityWithPoint(c->ptA)) continue;
			if (!SS.clipboard.ContainsEntityWithPoint(c->ptB)) continue;
        }
		else if (c->type == Constraint::DIAMETER) {
			if (!SS.copyConstraints) continue;
			if (!SS.clipboard.ContainsEntity(c->entityA)) continue;
		}
		else if (c->type == Constraint::EQUAL_RADIUS) {
			if (!SS.copyConstraints) continue;
			if (!SS.clipboard.ContainsEntity(c->entityA) && !SS.clipboard.ContainsEntity(c->entityB)) continue;
			//x if (SS.clipboard.ContainsEntity(c->entityA) && SS.clipboard.ContainsEntity(c->entityB)) continue;	//mv note: only one circle with this particular constraint can be copied!
		}
		else if (c->type == Constraint::PARALLEL || c->type == Constraint::ANGLE) {
			if (!SS.copyConstraints) continue;
			if (!SS.clipboard.ContainsEntity(c->entityA) && !SS.clipboard.ContainsEntity(c->entityB)) continue;
			//x if (!SS.clipboard.ContainsEntity(c->entityB)) continue;
		}
		else if (c->type == Constraint::SYMMETRIC_LINE) {
			if (!SS.copyConstraints) continue;
			if (!SS.clipboard.ContainsEntityWithPoint(c->ptA)) continue;
			if (!SS.clipboard.ContainsEntityWithPoint(c->ptB)) continue;
			if (!SS.clipboard.ContainsEntity(c->entityA)) continue;
		}
		else if (c->type == Constraint::PT_LINE_DISTANCE) {
			if (!SS.copyConstraints) continue;
			if (!SS.clipboard.ContainsEntity(c->ptA)) continue;
			if (!SS.clipboard.ContainsEntity(c->entityA)) continue;
		}
#if 1
		else if (c->type == Constraint::EQUAL_LENGTH_LINES) {	//Dangerous. You have to know what are you doing.
			if (!SS.copyConstraints) continue;					//It works well only for copying of constrained copy of original.
																//Otherwise it can make solver busy and solvespace unresponsive.
			if (!SS.clipboard.ContainsEntity(c->entityA) && !SS.clipboard.ContainsEntity(c->entityB)) continue;	//no entity in clipboard -> don't copy
			//x if (SS.clipboard.ContainsEntity(c->entityA) && SS.clipboard.ContainsEntity(c->entityB)) continue;	//both entities in clipboard -> don't copy
		}
#endif
		else {
            continue;
        }
        SS.clipboard.c.Add(c);
    }
}
Example #9
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;
	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;
}
Example #10
0
void GraphicsWindow::EnsureValidActives(void) {
    bool change = false;
    // The active group must exist, and not be the references.
    Group *g = SK.group.FindByIdNoOops(activeGroup);
    if((!g) || (g->h.v == Group::HGROUP_REFERENCES.v)) {
        int i;
        for(i = 0; i < SK.groupOrder.n; i++) {
            if(SK.groupOrder.elem[i].v != Group::HGROUP_REFERENCES.v) {
                break;
            }
        }
        if(i >= SK.groupOrder.n) {
            // This can happen if the user deletes all the groups in the
            // sketch. It's difficult to prevent that, because the last
            // group might have been deleted automatically, because it failed
            // a dependency. There needs to be something, so create a plane
            // drawing group and activate that. They should never be able
            // to delete the references, though.
            activeGroup = SS.CreateDefaultDrawingGroup();
            // We've created the default group, but not the workplane entity;
            // do it now so that drawing mode isn't switched to "Free in 3d".
            SS.GenerateAll(SolveSpaceUI::GENERATE_ALL);
        } else {
            activeGroup = SK.groupOrder.elem[i];
        }
        SK.GetGroup(activeGroup)->Activate();
        change = true;
    }

    // The active coordinate system must also exist.
    if(LockedInWorkplane()) {
        Entity *e = SK.entity.FindByIdNoOops(ActiveWorkplane());
        if(e) {
            hGroup hgw = e->group;
            if(hgw.v != activeGroup.v && SS.GroupsInOrder(activeGroup, hgw)) {
                // The active workplane is in a group that comes after the
                // active group; so any request or constraint will fail.
                SetWorkplaneFreeIn3d();
                change = true;
            }
        } else {
            SetWorkplaneFreeIn3d();
            change = true;
        }
    }

    // And update the checked state for various menus
    bool locked = LockedInWorkplane();
    RadioMenuById(MNU_FREE_IN_3D, !locked);
    RadioMenuById(MNU_SEL_WORKPLANE, locked);

    SS.UndoEnableMenus();

    switch(SS.viewUnits) {
        case SolveSpaceUI::UNIT_MM:
        case SolveSpaceUI::UNIT_INCHES:
            break;
        default:
            SS.viewUnits = SolveSpaceUI::UNIT_MM;
            break;
    }
    RadioMenuById(MNU_UNITS_MM, SS.viewUnits == SolveSpaceUI::UNIT_MM);
    RadioMenuById(MNU_UNITS_INCHES, SS.viewUnits == SolveSpaceUI::UNIT_INCHES);

    ShowTextWindow(SS.GW.showTextWindow);
    CheckMenuById(MNU_SHOW_TEXT_WND, SS.GW.showTextWindow);

#if defined(__APPLE__)
    CheckMenuById(MNU_SHOW_MENU_BAR, MenuBarIsVisible());
#endif
    CheckMenuById(MNU_SHOW_TOOLBAR, SS.showToolbar);
    CheckMenuById(MNU_PERSPECTIVE_PROJ, SS.usePerspectiveProj);
    CheckMenuById(MNU_SHOW_GRID, SS.GW.showSnapGrid);
#if defined(HAVE_GTK) || defined(__APPLE__)
    CheckMenuById(MNU_FULL_SCREEN, FullScreenIsActive());
#endif

    if(change) SS.ScheduleShowTW();
}
Example #11
0
void GraphicsWindow::Paint(void) {
    int i;
    havePainted = true;

    int w, h;
    GetGraphicsWindowSize(&w, &h);
    width = w; height = h;
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glScaled(scale*2.0/w, scale*2.0/h, scale*1.0/30000);

    double mat[16];
    // Last thing before display is to apply the perspective
    double clp = SS.CameraTangent()*scale;
    MakeMatrix(mat, 1,              0,              0,              0,
                    0,              1,              0,              0,
                    0,              0,              1,              0,
                    0,              0,              clp,            1);
    glMultMatrixd(mat);
    // Before that, we apply the rotation
    Vector n = projUp.Cross(projRight);
    MakeMatrix(mat, projRight.x,    projRight.y,    projRight.z,    0,
                    projUp.x,       projUp.y,       projUp.z,       0,
                    n.x,            n.y,            n.z,            0,
                    0,              0,              0,              1);
    glMultMatrixd(mat);
    // And before that, the translation
    MakeMatrix(mat, 1,              0,              0,              offset.x,
                    0,              1,              0,              offset.y,
                    0,              0,              1,              offset.z,
                    0,              0,              0,              1);
    glMultMatrixd(mat);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glShadeModel(GL_SMOOTH);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glEnable(GL_LINE_SMOOTH);
    // don't enable GL_POLYGON_SMOOTH; that looks ugly on some graphics cards,
    // drawn with leaks in the mesh
    glEnable(GL_POLYGON_OFFSET_LINE);
    glEnable(GL_POLYGON_OFFSET_FILL);
    glEnable(GL_DEPTH_TEST);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_NORMALIZE);

    // At the same depth, we want later lines drawn over earlier.
    glDepthFunc(GL_LEQUAL);

    if(SS.AllGroupsOkay()) {
        glClearColor(SS.backgroundColor.redF(),
                     SS.backgroundColor.greenF(),
                     SS.backgroundColor.blueF(), 1.0f);
    } else {
        // Draw a different background whenever we're having solve problems.
        RgbColor rgb = Style::Color(Style::DRAW_ERROR);
        glClearColor(0.4f*rgb.redF(), 0.4f*rgb.greenF(), 0.4f*rgb.blueF(), 1.0f);
        // And show the text window, which has info to debug it
        ForceTextWindowShown();
    }
    glClear(GL_COLOR_BUFFER_BIT);
    glClearDepth(1.0);
    glClear(GL_DEPTH_BUFFER_BIT);

    if(SS.bgImage.fromFile) {
        // If a background image is loaded, then we draw it now as a texture.
        // This handles the resizing for us nicely.
        glBindTexture(GL_TEXTURE_2D, TEXTURE_BACKGROUND_IMG);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,     GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,     GL_CLAMP);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
                     SS.bgImage.rw, SS.bgImage.rh,
                     0,
                     GL_RGB, GL_UNSIGNED_BYTE,
                     SS.bgImage.fromFile);

        double tw = ((double)SS.bgImage.w) / SS.bgImage.rw,
               th = ((double)SS.bgImage.h) / SS.bgImage.rh;

        double mmw = SS.bgImage.w / SS.bgImage.scale,
               mmh = SS.bgImage.h / SS.bgImage.scale;

        Vector origin = SS.bgImage.origin;
        origin = origin.DotInToCsys(projRight, projUp, n);
        // Place the depth of our origin at the point that corresponds to
        // w = 1, so that it's unaffected by perspective.
        origin.z = (offset.ScaledBy(-1)).Dot(n);
        origin = origin.ScaleOutOfCsys(projRight, projUp, n);

        // Place the background at the very back of the Z order, though, by
        // mucking with the depth range.
        glDepthRange(1, 1);
        glEnable(GL_TEXTURE_2D);
        glBegin(GL_QUADS);
            glTexCoord2d(0, 0);
            ssglVertex3v(origin);

            glTexCoord2d(0, th);
            ssglVertex3v(origin.Plus(projUp.ScaledBy(mmh)));

            glTexCoord2d(tw, th);
            ssglVertex3v(origin.Plus(projRight.ScaledBy(mmw).Plus(
                                     projUp.   ScaledBy(mmh))));

            glTexCoord2d(tw, 0);
            ssglVertex3v(origin.Plus(projRight.ScaledBy(mmw)));
        glEnd();
        glDisable(GL_TEXTURE_2D);
    }
    ssglDepthRangeOffset(0);

    // Nasty case when we're reloading the imported files; could be that
    // we get an error, so a dialog pops up, and a message loop starts, and
    // we have to get called to paint ourselves. If the sketch is screwed
    // up, then we could trigger an oops trying to draw.
    if(!SS.allConsistent) return;

    // Let's use two lights, at the user-specified locations
    GLfloat f;
    glEnable(GL_LIGHT0);
    f = (GLfloat)SS.lightIntensity[0];
    GLfloat li0[] = { f, f, f, 1.0f };
    glLightfv(GL_LIGHT0, GL_DIFFUSE, li0);
    glLightfv(GL_LIGHT0, GL_SPECULAR, li0);

    glEnable(GL_LIGHT1);
    f = (GLfloat)SS.lightIntensity[1];
    GLfloat li1[] = { f, f, f, 1.0f };
    glLightfv(GL_LIGHT1, GL_DIFFUSE, li1);
    glLightfv(GL_LIGHT1, GL_SPECULAR, li1);

    Vector ld;
    ld = VectorFromProjs(SS.lightDir[0]);
    GLfloat ld0[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
    glLightfv(GL_LIGHT0, GL_POSITION, ld0);
    ld = VectorFromProjs(SS.lightDir[1]);
    GLfloat ld1[4] = { (GLfloat)ld.x, (GLfloat)ld.y, (GLfloat)ld.z, 0 };
    glLightfv(GL_LIGHT1, GL_POSITION, ld1);

    if(SS.drawBackFaces) {
        // For debugging, draw the backs of the triangles in red, so that we
        // notice when a shell is open
        glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1);
    } else {
        glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0);
    }

    GLfloat ambient[4] = { (float)SS.ambientIntensity,
                           (float)SS.ambientIntensity,
                           (float)SS.ambientIntensity, 1 };
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);

    ssglUnlockColor();

    if(showSnapGrid && LockedInWorkplane()) {
        hEntity he = ActiveWorkplane();
        EntityBase *wrkpl = SK.GetEntity(he),
                   *norm  = wrkpl->Normal();
        Vector wu, wv, wn, wp;
        wp = SK.GetEntity(wrkpl->point[0])->PointGetNum();
        wu = norm->NormalU();
        wv = norm->NormalV();
        wn = norm->NormalN();

        double g = SS.gridSpacing;

        double umin = VERY_POSITIVE, umax = VERY_NEGATIVE,
               vmin = VERY_POSITIVE, vmax = VERY_NEGATIVE;
        int a;
        for(a = 0; a < 4; a++) {
            // Ideally, we would just do +/- half the width and height; but
            // allow some extra slop for rounding.
            Vector horiz = projRight.ScaledBy((0.6*width)/scale  + 2*g),
                   vert  = projUp.   ScaledBy((0.6*height)/scale + 2*g);
            if(a == 2 || a == 3) horiz = horiz.ScaledBy(-1);
            if(a == 1 || a == 3) vert  = vert. ScaledBy(-1);
            Vector tp = horiz.Plus(vert).Minus(offset);

            // Project the point into our grid plane, normal to the screen
            // (not to the grid plane). If the plane is on edge then this is
            // impossible so don't try to draw the grid.
            bool parallel;
            Vector tpp = Vector::AtIntersectionOfPlaneAndLine(
                                            wn, wn.Dot(wp),
                                            tp, tp.Plus(n),
                                            &parallel);
            if(parallel) goto nogrid;

            tpp = tpp.Minus(wp);
            double uu = tpp.Dot(wu),
                   vv = tpp.Dot(wv);

            umin = min(uu, umin);
            umax = max(uu, umax);
            vmin = min(vv, vmin);
            vmax = max(vv, vmax);
        }

        int i, j, i0, i1, j0, j1;

        i0 = (int)(umin / g);
        i1 = (int)(umax / g);
        j0 = (int)(vmin / g);
        j1 = (int)(vmax / g);

        if(i0 > i1 || i1 - i0 > 400) goto nogrid;
        if(j0 > j1 || j1 - j0 > 400) goto nogrid;

        glLineWidth(1);
        ssglColorRGBa(Style::Color(Style::DATUM), 0.3);
        glBegin(GL_LINES);
        for(i = i0 + 1; i < i1; i++) {
            ssglVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j0*g)));
            ssglVertex3v(wp.Plus(wu.ScaledBy(i*g)).Plus(wv.ScaledBy(j1*g)));
        }
        for(j = j0 + 1; j < j1; j++) {
            ssglVertex3v(wp.Plus(wu.ScaledBy(i0*g)).Plus(wv.ScaledBy(j*g)));
            ssglVertex3v(wp.Plus(wu.ScaledBy(i1*g)).Plus(wv.ScaledBy(j*g)));
        }
        glEnd();

        // Clear the depth buffer, so that the grid is at the very back of
        // the Z order.
        glClear(GL_DEPTH_BUFFER_BIT);
nogrid:;
    }

    // Draw the active group; this does stuff like the mesh and edges.
    (SK.GetGroup(activeGroup))->Draw();

    // Now draw the entities
    if(showHdnLines) glDisable(GL_DEPTH_TEST);
    Entity::DrawAll();

    // Draw filled paths in all groups, when those filled paths were requested
    // specially by assigning a style with a fill color, or when the filled
    // paths are just being filled by default. This should go last, to make
    // the transparency work.
    Group *g;
    for(g = SK.group.First(); g; g = SK.group.NextAfter(g)) {
        if(!(g->IsVisible())) continue;
        g->DrawFilledPaths();
    }


    glDisable(GL_DEPTH_TEST);
    // Draw the constraints
    for(i = 0; i < SK.constraint.n; i++) {
        SK.constraint.elem[i].Draw();
    }

    // Draw the traced path, if one exists
    glLineWidth(Style::Width(Style::ANALYZE));
    ssglColorRGB(Style::Color(Style::ANALYZE));
    SContour *sc = &(SS.traced.path);
    glBegin(GL_LINE_STRIP);
    for(i = 0; i < sc->l.n; i++) {
        ssglVertex3v(sc->l.elem[i].p);
    }
    glEnd();

    // And the naked edges, if the user did Analyze -> Show Naked Edges.
    glLineWidth(Style::Width(Style::DRAW_ERROR));
    ssglColorRGB(Style::Color(Style::DRAW_ERROR));
    ssglDrawEdges(&(SS.nakedEdges), true);

    // Then redraw whatever the mouse is hovering over, highlighted.
    glDisable(GL_DEPTH_TEST);
    ssglLockColorTo(Style::Color(Style::HOVERED));
    hover.Draw();

    // And finally draw the selection, same mechanism.
    ssglLockColorTo(Style::Color(Style::SELECTED));
    for(Selection *s = selection.First(); s; s = selection.NextAfter(s)) {
        s->Draw();
    }

    ssglUnlockColor();

    // If a marquee selection is in progress, then draw the selection
    // rectangle, as an outline and a transparent fill.
    if(pending.operation == DRAGGING_MARQUEE) {
        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);

        Vector tl = UnProjectPoint(Point2d::From(xmin, ymin)),
               tr = UnProjectPoint(Point2d::From(xmax, ymin)),
               br = UnProjectPoint(Point2d::From(xmax, ymax)),
               bl = UnProjectPoint(Point2d::From(xmin, ymax));

        glLineWidth((GLfloat)1.3);
        ssglColorRGB(Style::Color(Style::HOVERED));
        glBegin(GL_LINE_LOOP);
            ssglVertex3v(tl);
            ssglVertex3v(tr);
            ssglVertex3v(br);
            ssglVertex3v(bl);
        glEnd();
        ssglColorRGBa(Style::Color(Style::HOVERED), 0.10);
        glBegin(GL_QUADS);
            ssglVertex3v(tl);
            ssglVertex3v(tr);
            ssglVertex3v(br);
            ssglVertex3v(bl);
        glEnd();
    }

    // An extra line, used to indicate the origin when rotating within the
    // plane of the monitor.
    if(SS.extraLine.draw) {
        glLineWidth(1);
        ssglLockColorTo(Style::Color(Style::DATUM));
        glBegin(GL_LINES);
            ssglVertex3v(SS.extraLine.ptA);
            ssglVertex3v(SS.extraLine.ptB);
        glEnd();
    }

    // A note to indicate the origin in the just-exported file.
    if(SS.justExportedInfo.draw) {
        ssglColorRGB(Style::Color(Style::DATUM));
        Vector p = SS.justExportedInfo.pt,
               u = SS.justExportedInfo.u,
               v = SS.justExportedInfo.v;

        glLineWidth(1.5);
        glBegin(GL_LINES);
            ssglVertex3v(p.Plus(u.WithMagnitude(-15/scale)));
            ssglVertex3v(p.Plus(u.WithMagnitude(30/scale)));
            ssglVertex3v(p.Plus(v.WithMagnitude(-15/scale)));
            ssglVertex3v(p.Plus(v.WithMagnitude(30/scale)));
        glEnd();

        ssglWriteText("(x, y) = (0, 0) for file just exported",
            DEFAULT_TEXT_HEIGHT,
            p.Plus(u.ScaledBy(10/scale)).Plus(v.ScaledBy(10/scale)),
            u, v, NULL, NULL);
        ssglWriteText("press Esc to clear this message",
            DEFAULT_TEXT_HEIGHT,
            p.Plus(u.ScaledBy(40/scale)).Plus(
                   v.ScaledBy(-(DEFAULT_TEXT_HEIGHT)/scale)),
            u, v, NULL, NULL);
    }

    // And finally the toolbar.
    if(SS.showToolbar) {
        ToolbarDraw();
    }
}