コード例 #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();
        }
    }
}
            void AbstractFeatureMatcher::ReconsiderOldPoints(const std::shared_ptr<Core::PointOfView> &pointOfView, std::vector<Core::Projection> detectedFeatures)
            {
                if (!pointOfView->GetCameraParameters().IsPoseDetermined())
                {
                    return;
                }

                std::vector<Core::Projection> projectionsInCurrentView;
                for (auto it = scene->GetFeatures()->Begin(); it != scene->GetFeatures()->End(); ++it)
                {
                    Core::Projection projection = it->ProjectPoint(pointOfView);
                    if (projection.GetX() < 0 || projection.GetY() < 0
                            || projection.GetX() > pointOfView->GetImage()->GetSize().width
                            || projection.GetY() > pointOfView->GetImage()->GetSize().height)
                    {
                        continue;
                    }

                    projectionsInCurrentView.push_back(projection);
                }

                for (const Core::Projection &projection : projectionsInCurrentView)
                {
                    for (const Core::Projection &actualFeature : detectedFeatures)
                    {

                    }
                }

                // project all points to this POV
                // see which ones are inside the image
                // use them and try matching against detected keypoints
            }
コード例 #3
0
ファイル: Matrix.cpp プロジェクト: Andrel322/gecko-dev
Rect Matrix4x4::ProjectRectBounds(const Rect& aRect) const
{
  Point4D points[4];

  points[0] = ProjectPoint(aRect.TopLeft());
  points[1] = ProjectPoint(aRect.TopRight());
  points[2] = ProjectPoint(aRect.BottomRight());
  points[3] = ProjectPoint(aRect.BottomLeft());

  Float min_x = std::numeric_limits<Float>::max();
  Float min_y = std::numeric_limits<Float>::max();
  Float max_x = -std::numeric_limits<Float>::max();
  Float max_y = -std::numeric_limits<Float>::max();

  bool foundPoint = false;
  for (int i=0; i<4; i++) {
    // Only use points that exist above the w=0 plane
    if (points[i].HasPositiveWCoord()) {
      foundPoint = true;
      Point point2d = points[i].As2DPoint();
      min_x = min<Float>(point2d.x, min_x);
      max_x = max<Float>(point2d.x, max_x);
      min_y = min<Float>(point2d.y, min_y);
      max_y = max<Float>(point2d.y, max_y);
    }

    int next = (i == 3) ? 0 : i + 1;
    if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) {
      // If the line between two points crosses the w=0 plane, then interpolate a point
      // as close to the w=0 plane as possible and use that instead.
      Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]);

      Point point2d = intercept.As2DPoint();
      min_x = min<Float>(point2d.x, min_x);
      max_x = max<Float>(point2d.x, max_x);
      min_y = min<Float>(point2d.y, min_y);
      max_y = max<Float>(point2d.y, max_y);
    }
  }

  if (!foundPoint) {
    return Rect(0, 0, 0, 0);
  }

  return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
}
コード例 #4
0
void CCadEdge2DPolyline::SetCadEdge(const Cad::CCadObj2D& cad_2d, const unsigned int id_e,
                                    const Com::CVector2D& pick_pos)
{
  this->pick_pos = pick_pos;
  this->m_IdECad = id_e;
  this->ClearMemory();
  std::vector<double> aRelCoPoly;  
  {
    assert( cad_2d.IsElemID(Cad::EDGE,id_e) );
    const Cad::CEdge2D& e = cad_2d.GetEdge(id_e);
    aRelCoPoly = e.GetCurveRelPoint();
  }
  std::vector<double> aXYs;
  const unsigned int ndiv = aRelCoPoly.size()/2+1;
  nno = ndiv+1;
  aXYs.reserve(nno*2);
  unsigned int id_vs = cad_2d.GetIdVertex_Edge(id_e,true );
  unsigned int id_ve = cad_2d.GetIdVertex_Edge(id_e,false);
  const Com::CVector2D& vs = cad_2d.GetVertexCoord(id_vs);
  const Com::CVector2D& ve = cad_2d.GetVertexCoord(id_ve);
  const Com::CVector2D hse = ve-vs;
  const Com::CVector2D vse = Com::CVector2D(-hse.y,hse.x);  
  aXYs.push_back(vs.x);
  aXYs.push_back(vs.y);    
  for(unsigned int i=0;i<ndiv-1;i++){
    Com::CVector2D v = vs+aRelCoPoly[i*2+0]*hse+aRelCoPoly[i*2+1]*vse;
    aXYs.push_back(v.x);
    aXYs.push_back(v.y);    
  }
  aXYs.push_back(ve.x);
  aXYs.push_back(ve.y);      
  //////////////////////////////////////
  this->m_mat = new CTriDiaMat3(nno);
  ini_x = new double [nno*2];
  ut = new double [nno*3];
  Res = new double [nno*3];  
  dut = new double [nno*3];
  bc_flag = new int [nno*3];  
  ////
  for(unsigned int i=0;i<nno*2;i++){ ini_x[i] = aXYs[i]; }  
  for(unsigned int i=0;i<nno*3;i++){ ut[i] = 0; }    
  for(unsigned int i=0;i<nno*3;i++){ bc_flag[i] = 0; }  
  //////////////////////////////////////
  this->SetFixedBoundaryFlag(0,0);
  this->SetFixedBoundaryFlag(0,1);
  this->SetFixedBoundaryFlag(nno-1,0);
  this->SetFixedBoundaryFlag(nno-1,1);    
  double alpha, ndist, norm_x, norm_y;
  ProjectPoint(pick_pos.x, pick_pos.y, idiv_picked,
               alpha, ndist, norm_x, norm_y);
  if( idiv_picked == -1 ) return;
  this->SetFixedBoundaryFlag(idiv_picked,0);
  this->SetFixedBoundaryFlag(idiv_picked,1); 
  this->SetFixedBoundaryFlag(idiv_picked+1,0);
  this->SetFixedBoundaryFlag(idiv_picked+1,1);     
}
コード例 #5
0
  int MeshOptimize2dOCCSurfaces :: ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const
  {
    double u = gi.u;
    double v = gi.v;

    Point<3> hp = p;
    if (geometry.FastProject (surfind, hp, u, v))
      {
	p = hp;
	return 1;
      }
    ProjectPoint (surfind, p); 
    return CalcPointGeomInfo (surfind, gi, p); 
  }
コード例 #6
0
ファイル: smoothing2.5.cpp プロジェクト: Resistancerus/Netgen
void MeshOptimize2d :: ProjectBoundaryPoints(Array<int> & surfaceindex,
        const Array<Point<3>* > & from, Array<Point<3>* > & dest)
{
    for(int i=0; i<surfaceindex.Size(); i++)
    {
        if(surfaceindex[i] >= 0)
        {
            *dest[i] = *from[i];
            ProjectPoint(surfaceindex[i],*dest[i]);
        }
    }


}
コード例 #7
0
void GPSView::AddGPSPoints(GPSPoints &p){

	int width;
	int height;
	GetClientSize(&width, &height);

	Point minXY(-1, -1);
	Point maxXY(-1, -1);

	for (size_t i = 0; i < p.Count(); i++){
		Point point = ProjectPoint(p[i]);
		minXY.x = ((minXY.x == -1) ? point.x : std::min(minXY.x, point.x));
		minXY.y = ((minXY.y == -1) ? point.y : std::min(minXY.y, point.y));
		m_trackPoints.Add(point);
	}

	for (size_t i = 0; i < m_trackPoints.size(); i++){
		Point point = m_trackPoints[i];
		point.x = point.x - minXY.x;
        point.y = point.y - minXY.y;
        // now, we need to keep track the max X and Y values
        maxXY.x = (maxXY.x == -1) ? point.x : std::max(maxXY.x, point.x);
        maxXY.y = (maxXY.y == -1) ? point.y : std::max(maxXY.y, point.y);
	}

	int paddingBothSides = MIN_PADDING * 2;

	// the actual drawing space for the map on the image
    int mapWidth = width - paddingBothSides;
    int mapHeight = height - paddingBothSides;

    // determine the width and height ratio because we need to magnify the map to fit into the given image dimension
    double mapWidthRatio = mapWidth / maxXY.x;
    double mapHeightRatio = mapHeight / maxXY.y;

    // using different ratios for width and height will cause the map to be stretched. So, we have to determine
    // the global ratio that will perfectly fit into the given image dimension
    m_globalRatio = std::min(mapWidthRatio, mapHeightRatio);

    // now we need to readjust the padding to ensure the map is always drawn on the center of the given image dimension
    m_heightPadding = (height - (m_globalRatio * maxXY.y)) / 2;
    m_widthPadding = (width - (m_globalRatio * maxXY.x)) / 2;
    m_offsetPoint = minXY;
    UpdateMinMax(m_trackPoints);
    RefreshBackground();
}
コード例 #8
0
BLORTSubRect BLORTObjectsManager::GetSubRect(int object_size)
{
    BLORTSubRect res;
    int minX = camPar.width; int maxX = 0;
    int minY = camPar.height; int maxY = 0;
    for(std::map<std::string, TomGine::tgPose>::iterator it = positions.begin(); it != positions.end(); ++it)
    {
        int u = 0; int v = 0;
        ProjectPoint(it->second, u, v);
        if(u < minX) { minX = u; }
        if(u > maxX) { maxX = u; }
        if(v < minY) { minY = v; }
        if(v > maxY) { maxY = v; }
    }
    minX = minX > object_size/2 ? minX - object_size/2 : 0;
    maxX = maxX + object_size/2 > camPar.height - 1 ? camPar.height - 1 : maxX + object_size/2;
    minY = minY > object_size/2 ? minY - object_size/2 : 0;
    maxY = maxY + object_size/2 > camPar.height - 1 ? camPar.height - 1 : maxY + object_size/2;
    res.left = minX;
    res.top = minY;
    res.width = maxX - minX;
    res.height = maxY - minY;
    /* Preserve aspect ratio */
    if(res.width > res.height)
    {
        int heightDiff = (double)camPar.height/(double)camPar.width * res.width - res.height;
        res.height += heightDiff;
        res.top -= heightDiff/2;
        res.top = res.top < 0 ? 0 : res.top;
    }
    else
    {
        int widthDiff = (double)camPar.width/(double)camPar.height * res.height - res.width;
        res.width += widthDiff;
        res.left -= widthDiff/2;
        res.left = res.left < 0 ? 0 : res.left;
    }
    return res;
}
コード例 #9
0
ファイル: Matrix.cpp プロジェクト: hoosteeno/gecko-dev
Rect Matrix4x4::ProjectRectBounds(const Rect& aRect, const Rect &aClip) const
{
  // This function must never return std::numeric_limits<Float>::max() or any
  // other arbitrary large value in place of inifinity.  This often occurs when
  // aRect is an inversed projection matrix or when aRect is transformed to be
  // partly behind and in front of the camera (w=0 plane in homogenous
  // coordinates) - See Bug 1035611

  // Some call-sites will call RoundGfxRectToAppRect which clips both the
  // extents and dimensions of the rect to be bounded by nscoord_MAX.
  // If we return a Rect that, when converted to nscoords, has a width or height
  // greater than nscoord_MAX, RoundGfxRectToAppRect will clip the overflow
  // off both the min and max end of the rect after clipping the extents of the
  // rect, resulting in a translation of the rect towards the infinite end.

  // The bounds returned by ProjectRectBounds are expected to be clipped only on
  // the edges beyond the bounds of the coordinate system; otherwise, the
  // clipped bounding box would be smaller than the correct one and result
  // bugs such as incorrect culling (eg. Bug 1073056)

  // To address this without requiring all code to work in homogenous
  // coordinates or interpret infinite values correctly, a specialized
  // clipping function is integrated into ProjectRectBounds.

  // Callers should pass an aClip value that represents the extents to clip
  // the result to, in the same coordinate system as aRect.
  Point4D points[4];

  points[0] = ProjectPoint(aRect.TopLeft());
  points[1] = ProjectPoint(aRect.TopRight());
  points[2] = ProjectPoint(aRect.BottomRight());
  points[3] = ProjectPoint(aRect.BottomLeft());

  Float min_x = std::numeric_limits<Float>::max();
  Float min_y = std::numeric_limits<Float>::max();
  Float max_x = -std::numeric_limits<Float>::max();
  Float max_y = -std::numeric_limits<Float>::max();

  for (int i=0; i<4; i++) {
    // Only use points that exist above the w=0 plane
    if (points[i].HasPositiveWCoord()) {
      Point point2d = aClip.ClampPoint(points[i].As2DPoint());
      min_x = std::min<Float>(point2d.x, min_x);
      max_x = std::max<Float>(point2d.x, max_x);
      min_y = std::min<Float>(point2d.y, min_y);
      max_y = std::max<Float>(point2d.y, max_y);
    }

    int next = (i == 3) ? 0 : i + 1;
    if (points[i].HasPositiveWCoord() != points[next].HasPositiveWCoord()) {
      // If the line between two points crosses the w=0 plane, then interpolate
      // to find the point of intersection with the w=0 plane and use that
      // instead.
      Point4D intercept = ComputePerspectivePlaneIntercept(points[i], points[next]);
      // Since intercept.w will always be 0 here, we interpret x,y,z as a
      // direction towards an infinite vanishing point.
      if (intercept.x < 0.0f) {
        min_x = aClip.x;
      } else if (intercept.x > 0.0f) {
        max_x = aClip.XMost();
      }
      if (intercept.y < 0.0f) {
        min_y = aClip.y;
      } else if (intercept.y > 0.0f) {
        max_y = aClip.YMost();
      }
    }
  }

  if (max_x < min_x || max_y < min_y) {
    return Rect(0, 0, 0, 0);
  }

  return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
}
コード例 #10
0
void GraphicsWindow::SplitLinesOrCurves(void) {
    if(!LockedInWorkplane()) {
        Error("Must be sketching in workplane to split.");
        return;
    }

    GroupSelection();
    if(!(gs.n == 2 &&(gs.lineSegments +
                      gs.circlesOrArcs + 
                      gs.cubics +
                      gs.periodicCubics) == 2))
    {
        Error("Select two entities that intersect each other (e.g. two lines "
              "or two circles or a circle and a line).");
        return;
    }

    hEntity ha = gs.entity[0],
            hb = gs.entity[1];
    Entity *ea = SK.GetEntity(ha),
           *eb = SK.GetEntity(hb);
   
    // Compute the possibly-rational Bezier curves for each of these entities
    SBezierList sbla, sblb;
    ZERO(&sbla);
    ZERO(&sblb);
    ea->GenerateBezierCurves(&sbla);
    eb->GenerateBezierCurves(&sblb);
    // and then compute the points where they intersect, based on those curves.
    SPointList inters;
    ZERO(&inters);
    sbla.AllIntersectionsWith(&sblb, &inters);

    if(inters.l.n > 0) {
        Vector pi;
        // If there's multiple points, then take the one closest to the
        // mouse pointer.
        double dmin = VERY_POSITIVE;
        SPoint *sp;
        for(sp = inters.l.First(); sp; sp = inters.l.NextAfter(sp)) {
            double d = ProjectPoint(sp->p).DistanceTo(currentMousePosition);
            if(d < dmin) {
                dmin = d;
                pi = sp->p;
            }
        }

        SS.UndoRemember();
        hEntity hia = SplitEntity(ha, pi),
                hib = SplitEntity(hb, pi);
        // SplitEntity adds the coincident constraints to join the split halves
        // of each original entity; and then we add the constraint to join
        // the two entities together at the split point.
        if(hia.v && hib.v) {
            Constraint::ConstrainCoincident(hia, hib);
        }
    } else {
        Error("Can't split; no intersection found.");
    }

    // All done, clean up and regenerate.
    inters.Clear();
    sbla.Clear();
    sblb.Clear();
    ClearSelection();
    SS.later.generateAll = true;
}
コード例 #11
0
ファイル: improve2.hpp プロジェクト: 11235813/netgen-csg2d
 /// project point, use gi as initial value, and compute new gi
 virtual int ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const 
 { ProjectPoint (surfind, p); return CalcPointGeomInfo (surfind, gi, p); }
コード例 #12
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;
}
コード例 #13
0
ファイル: mouse.cpp プロジェクト: astarasikov/solvespace
void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
    if(GraphicsEditControlIsVisible()) return;
    SS.TW.HideEditControl();

    if(hover.constraint.v) {
        constraintBeingEdited = hover.constraint;
        ClearSuper();

        Constraint *c = SK.GetConstraint(constraintBeingEdited);
        if(!c->HasLabel()) {
            // Not meaningful to edit a constraint without a dimension
            return;
        }
        if(c->reference) {
            // Not meaningful to edit a reference dimension
            return;
        }

        Vector p3 = c->GetLabelPos();
        Point2d p2 = ProjectPoint(p3);

        char s[1024];
        switch(c->type) {
            case Constraint::COMMENT:
                strcpy(s, c->comment.str);
                break;

            case Constraint::ANGLE:
            case Constraint::LENGTH_RATIO:
                sprintf(s, "%.3f", c->valA);
                break;

            default: {
                double v = fabs(c->valA);

                // If displayed as radius, also edit as radius.
                if(c->type == Constraint::DIAMETER && c->disp.toggleA)
                    v /= 2;

                char *def = SS.MmToString(v);
                double eps = 1e-12;
                if(fabs(SS.StringToMm(def) - v) < eps) {
                    // Show value with default number of digits after decimal,
                    // which is at least enough to represent it exactly.
                    strcpy(s, def);
                } else {
                    // Show value with as many digits after decimal as
                    // required to represent it exactly, up to 10.
                    v /= SS.MmPerUnit();
                    int i;
                    for(i = 0; i <= 10; i++) {
                        sprintf(s, "%.*f", i, v);
                        if(fabs(atof(s) - v) < eps) break;
                    }
                }
                break;
            }
        }
        ShowGraphicsEditControl((int)p2.x, (int)p2.y-4, s);
    }
}
コード例 #14
0
void GPSView::SetMarker(GPSPoint &p)
{
	m_marker = ProjectPoint(p);
	Refresh();
}
コード例 #15
0
ファイル: draw.cpp プロジェクト: tmpvar/solvespace
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();
    }
}