I'm trying to draw the cursor position, with a square, when mouse moving.
I'm using SharpGL and the OpenGLControl, but there is a delay between cursor arrow and the drawn square.
On MouseMove event I store a square centered in the mouse position (mapped in _pointCursor that is a list of vertex), then in the Draw event of the control, I actually draw it:
private void DrawPointers(OpenGL gl)
{
if (_pointCursor == null)
return;
SetColor(gl, Color.LightGray);
gl.Begin(OpenGL.GL_LINE_LOOP);
foreach (var item in _pointCursor)
{
gl.Vertex(item.X, item.Y, item.Z);
}
gl.End();
}
Is there any trick,configuration or strategy in order to avoid this delay?
I think the problem is how everything is drawn. If I remove DrawAxis() and DrawGrid() there is not more delay. In this case it can depend on my drawing strategy, I use a lot of gl.Begin() and gl.End().
Another thing is that DrawGrid() and DrawAxis() continues to draw a fixed background. I don't use shaders.
This is the code:
private void MouseDrawCursors(XglVertex v)
{
if ((_view == ViewType._2D) && ((_status == EditStatus.Idle) || (_status == EditStatus.DrawLine)))
{
_pointCursor = v.DrawSquare(0.1f); //Return a list of vertex
_pointCross = v.DrawCross(20); //Return a list of vertex
}
}
private void openGLControl1_MouseMove(object sender, MouseEventArgs e)
{
if (_gl == null)
return;
double worldX = 0;
double worldY = 0;
double worldZ = 0;
_gl.GetWorldCoords(e.X, e.Y, ref worldX, ref worldY, ref worldZ);
XglVertex v = new XglVertex((float)worldX, (float)worldY, (float)worldZ);
MouseDrawCursors(v);
}
private void Draw()
{
if (_gl == null)
return;
//Enable 3D
if (_view == ViewType._2D)
_gl.Disable(OpenGL.GL_DEPTH_TEST);
else
_gl.Enable(OpenGL.GL_DEPTH_TEST);
_gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
//Enable Aplpha
_gl.Enable(OpenGL.GL_BLEND);
_gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);
#region Projection
_gl.MatrixMode(OpenGL.GL_MODELVIEW);
_gl.LoadIdentity();
_gl.Scale(Params.ScaleFactor, Params.ScaleFactor, Params.ScaleFactor);
_gl.Translate(Params.X, Params.Y, Params.Z);
if (_status == EditStatus.Rotate)
_arc_ball_effect.ArcBall.TransformMatrix(_gl);
else
_gl.Rotate(Params.XAngle, Params.YAngle, Params.ZAngle);
_gl.MatrixMode(OpenGL.GL_PROJECTION); //matrice di proiezione
_gl.LoadIdentity(); //ripristina matrice identità
float worldWidth = Params.Width;
float worldHeight = Params.Height;
float near = Params.Near;
float far = Params.Far;
_gl.Viewport(0, 0, openGLControl1.Width, openGLControl1.Height);
if (_aspect_ratio >= 1.0)
_gl.Ortho(0, (worldWidth * _aspect_ratio), 0, worldHeight, near, far);
else
_gl.Ortho(0, worldWidth, 0, (worldHeight / _aspect_ratio), near, far);
#endregion
if (Params.ShowPointers)
DrawPointers(_gl);
if (Params.ShowAxis)
DrawAxis(_gl);
if (Params.ShowGrid)
DrawGrid(_gl);
_gl.Flush();
}
private void DrawPointers(OpenGL gl)
{
if (_pointCursor == null)
return;
if (_pointCursor.Count == 0)
return;
SetColor(gl, Color.LightGray);
gl.Begin(OpenGL.GL_LINE_LOOP);
foreach (var item in _pointCursor)
{
gl.Vertex(item.X, item.Y, item.Z);
}
gl.End();
if (_pointCross == null)
return;
SetColor(gl, Color.LightGreen);
gl.Begin(OpenGL.GL_LINES);
foreach (var item in _pointCross)
{
gl.Vertex(item.X, item.Y, item.Z);
}
gl.End();
}
private void DrawGrid(OpenGL gl)
{
if (_gl == null)
return;
if (_grid == null)
return;
if (!Params.ShowGrid)
return;
SetColor(gl, _grid.GridColor);
if (_grid.EnableXY)
{
gl.Begin(OpenGL.GL_POINTS);
foreach (var p in _grid.XY)
{
gl.Vertex(p.X, p.Y, p.Z);
}
gl.End();
}
if (_grid.EnableYZ)
{
gl.Begin(OpenGL.GL_POINTS);
foreach (var p in _grid.YZ)
{
gl.Vertex(p.X, p.Y, p.Z);
}
gl.End();
}
if (_grid.EnableZX)
{
gl.Begin(OpenGL.GL_POINTS);
foreach (var p in _grid.ZX)
{
gl.Vertex(p.X, p.Y, p.Z);
}
gl.End();
}
}
Related
Greetings Community of experts, I am trying to synchronize the movement and zoom in two windows of AutoCAD models, that is, in two different planes open .dwg divided into two windows one active and the other inactive synchronize the zoom (scroll + or -) or the movement (PAN) from the active window to the inactive one (In model with two open planes in AutoCAD M3D -> SYSWINDOWS :: tile Vertical), I was researching and I found this code that does what I want but only with one plane, the problem is that I can not make it work in VS2019 c ++, I get an error in the lines with "WinCallBack" indicating that BOOL cannot become a constant, I appreciate your help in advance.
#include "StdAfx.h"
#include "resource.h"
#pragma warning( disable : 4278 )
#include <windows.h>
#include <stdio.h>
#include "acedCmdNF.h"
#include "AcMyEditorReactor.h"
#include "AcMyInputContextReactor.h"
// Viewchanged notification is not received during a pan or zoom using mouse wheel.
// So identify those using WM messages.
BOOL WinCallBack(MSG *pMsg)
{
if( pMsg->message == WM_VSCROLL ||
pMsg->message == WM_HSCROLL ||
pMsg->message == WM_MOUSEWHEEL ||
pMsg->message == WM_MBUTTONUP)
{
// Sync the modelspace viewports
acDocManager->sendStringToExecute(acDocManager->mdiActiveDocument(), ACRX_T("SyncVTR "), false, true, false);
}
return FALSE;
}
class CMyTest1App : public AcRxArxApp
{
private:
AcMyEditorReactor *pEditorReactor;
AcMyInputContextReactor *pInputContextReactor;
public:
CMyTest1App () : AcRxArxApp ()
{
}
virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt)
{
AcRx::AppRetCode retCode =AcRxArxApp::On_kInitAppMsg (pkt) ;
// Editor reactor to receive to ViewChanged notification
pEditorReactor = new AcMyEditorReactor(true);
// InputContext reactor to receive quiescent state change notification
pInputContextReactor = new AcMyInputContextReactor();
// Viewchanged notification is not received during a pan or zoom using mouse wheel.
// So identify those using WM messages.
acedRegisterFilterWinMsg(WinCallBack);
return (retCode);
}
virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt)
{
AcRx::AppRetCode retCode =AcRxArxApp::On_kUnloadAppMsg (pkt) ;
// cleanup
if(pEditorReactor)
{
delete pEditorReactor;
pEditorReactor = NULL;
}
if(pInputContextReactor)
{
delete pInputContextReactor;
pInputContextReactor = NULL;
}
acedRemoveFilterWinMsg(WinCallBack);
return (retCode);
}
virtual AcRx::AppRetCode On_kLoadDwgMsg (void *pkt)
{
AcRx::AppRetCode retCode =AcRxArxApp::On_kLoadDwgMsg (pkt) ;
return (retCode) ;
}
virtual void RegisterServerComponents ()
{
}
// Command to sync the model space viewport parameters
static void AdskMyTestSyncVTR()
{
// Get the VTR updated
acedVports2VportTableRecords();
// We will update the other VTR only if view parameters change
Adesk::Boolean updateNeeded = Adesk::kFalse;
Acad::ErrorStatus es;
AcDbDatabase *pDb = acdbHostApplicationServices()->workingDatabase();
AcApDocument *pDoc = acDocManager->document(pDb);
if( pDoc == NULL )
return;
es = acDocManager->lockDocument(pDoc);
// This code at present can only deal with 2 Modelspace viewports split vertically in half
if(pDb->tilemode() == Adesk::kFalse)
{
struct resbuf rb;
if(ads_getvar(_T("cvport"), &rb) != RTNORM)
{
acutPrintf(_T("\nError using ads_getvar().\n"));
return;
}
if(rb.resval.rint == 1)
{
return; // Can only work with model space viewports.
}
}
AcDbViewportTable *pVT = NULL;
pDb->getViewportTable(pVT,AcDb::kForRead);
// Identify the left and right modelspace viewports
AcDbViewportTableRecord *pLeftVTR = NULL;
AcDbViewportTableRecord *pRightVTR = NULL;
AcDbViewportTableIterator *pIter = NULL;
es = pVT->newIterator(pIter);
if(es == Acad::eOk)
{
for (;!pIter->done();pIter->step())
{
AcDbViewportTableRecord *pVTR = NULL;
es = pIter->getRecord(pVTR, AcDb::kForRead);
if(es == Acad::eOk)
{
AcGePoint2d ll = pVTR->lowerLeftCorner();
AcGePoint2d ur = pVTR->upperRightCorner();
if(ll.isEqualTo(AcGePoint2d(0, 0)))
{// Left modelspace viewport
pLeftVTR = pVTR;
}
else if(ur.isEqualTo(AcGePoint2d(1.0, 1.0)))
{// Right modelspace viewport
pRightVTR = pVTR;
}
else
pVTR->close();
}
}
// If for some reason, we did not have two modelspace viewports,
// lets stop here.
if(pLeftVTR == NULL)
{
if(pRightVTR != NULL)
pRightVTR->close();
return;
}
if(pRightVTR == NULL)
{
if(pLeftVTR != NULL)
pLeftVTR->close();
return;
}
// Ensure that the two viewports are split vertically in half.
// If not, the view parameters when applied from one to another
// may not apply directly using this code.
// If the viewports were resized manually, set them right.
AcGePoint2d ll1 = pLeftVTR->lowerLeftCorner();
AcGePoint2d ur1 = pLeftVTR->upperRightCorner();
AcGePoint2d ll2 = pRightVTR->lowerLeftCorner();
AcGePoint2d ur2 = pRightVTR->upperRightCorner();
if(ll1.isEqualTo(AcGePoint2d(0.0, 0.0)) == false)
{
if(! pLeftVTR->isWriteEnabled())
pLeftVTR->upgradeOpen();
pLeftVTR->setLowerLeftCorner(AcGePoint2d(0.0, 0.0));
}
if(ur1.isEqualTo(AcGePoint2d(0.5, 1.0)) == false)
{
if(! pLeftVTR->isWriteEnabled())
pLeftVTR->upgradeOpen();
pLeftVTR->setUpperRightCorner(AcGePoint2d(0.5, 1.0));
}
if(ll2.isEqualTo(AcGePoint2d(0.5, 0.0)) == false)
{
if(! pRightVTR->isWriteEnabled())
pRightVTR->upgradeOpen();
pRightVTR->setLowerLeftCorner(AcGePoint2d(0.5, 0.0));
}
if(ur2.isEqualTo(AcGePoint2d(1.0, 1.0)) == false)
{
if(! pRightVTR->isWriteEnabled())
pRightVTR->upgradeOpen();
pRightVTR->setUpperRightCorner(AcGePoint2d(1.0, 1.0));
}
// Get the active model space viewport
struct resbuf res;
acedGetVar(L"CVPORT", &res);
short vpnumber = res.resval.rint;
// Identify the model space viewports from/to which settings will be copied
// The active modelspace viewport is the viewport from which settings will be copied
AcDbViewportTableRecord *pFromVTR = NULL;
AcDbViewportTableRecord *pToVTR = NULL;
if(pLeftVTR->number() == vpnumber)
{
pFromVTR = pLeftVTR;
pToVTR = pRightVTR;
}
if(pRightVTR->number() == vpnumber)
{
pFromVTR = pRightVTR;
pToVTR = pLeftVTR;
}
// Sorry, we did not identify the active viewport
// from which settings need to be copied.
if(pFromVTR == NULL || pToVTR == NULL)
return;
// Copy the VTR settings from one modelspace viewport to another
// only if they are different. We will use a tolerance to ensure
// very small differences do not get us in a soup. I meant loop :)
AcGeTol newTol;
newTol.setEqualPoint (0.00001);
newTol.setEqualVector(0.00001);
// ViewDirection
AcGeVector3d fromViewDir = pFromVTR->viewDirection();
AcGeVector3d toViewDir = pToVTR->viewDirection();
if(pFromVTR->viewDirection().isEqualTo(pToVTR->viewDirection(), newTol) == false)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setViewDirection(pFromVTR->viewDirection());
updateNeeded = Adesk::kTrue;
}
// ViewTwist
if(abs(pFromVTR->viewTwist() - pToVTR->viewTwist()) > 0.01)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setViewTwist(pFromVTR->viewTwist());
updateNeeded = Adesk::kTrue;
}
// Target
if(pFromVTR->target().isEqualTo(pToVTR->target(), newTol) == false)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setTarget(pFromVTR->target());
updateNeeded = Adesk::kTrue;
}
// BackClipEnabled
if(pFromVTR->backClipEnabled() != pToVTR->backClipEnabled())
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setBackClipEnabled(pFromVTR->backClipEnabled());
updateNeeded = Adesk::kTrue;
}
// BackClipDistance
if(abs(pFromVTR->backClipDistance() - pToVTR->backClipDistance()) > 0.01)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setBackClipDistance(pFromVTR->backClipDistance());
updateNeeded = Adesk::kTrue;
}
// FrontClipEnabled
if(pFromVTR->frontClipEnabled() != pToVTR->frontClipEnabled())
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setFrontClipEnabled(pFromVTR->frontClipEnabled());
updateNeeded = Adesk::kTrue;
}
// FrontClipDistance
if(abs(pFromVTR->frontClipDistance() - pToVTR->frontClipDistance()) > 0.01)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setFrontClipDistance(pFromVTR->frontClipDistance());
updateNeeded = Adesk::kTrue;
}
// Elevation
if(abs(pFromVTR->elevation() - pToVTR->elevation()) > 0.01)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setElevation(pFromVTR->elevation());
updateNeeded = Adesk::kTrue;
}
// centerPoint
if(pFromVTR->centerPoint().isEqualTo(pToVTR->centerPoint(), newTol) == false)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setCenterPoint(pFromVTR->centerPoint());
updateNeeded = Adesk::kTrue;
}
// Height
if(abs(pFromVTR->height() - pToVTR->height()) > 0.01)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setHeight(pFromVTR->height());
updateNeeded = Adesk::kTrue;
}
// Width
if(abs(pFromVTR->width() - pToVTR->width()) > 0.01)
{
if(! pToVTR->isWriteEnabled())
pToVTR->upgradeOpen();
pToVTR->setWidth(pFromVTR->width());
updateNeeded = Adesk::kTrue;
}
// Done with the VTR
pLeftVTR->close();
pRightVTR->close();
delete pIter;
}
es = pVT->close();
es = acDocManager->unlockDocument(pDoc);
// Update the Vports if we did change any of the VTR parameters
if(updateNeeded)
{
acedVportTableRecords2Vports();
}
}
};
//-----------------------------------------------------------------------------
IMPLEMENT_ARX_ENTRYPOINT(CMyTest1App)
ACED_ARXCOMMAND_ENTRY_AUTO(CMyTest1App, AdskMyTest, SyncVTR, SyncVTR, ACRX_CMD_MODAL, NULL)
https://adndevblog.typepad.com/autocad/2014/07/synchronizing-model-space-viewports.html
I found two apps that solve the question in question, DWGsync (free and compatible until autocad 2021) https://apps.autodesk.com/ACD/es/Detail/Index?id=2788892389049910944&appLang=en&os=Win32_64 and Drawing Sync (licensed and compatible autocad 2018) https://apps.autodesk.com/ACD/en/Detail/Index?id=2152736212918385179&appLang=en&os=Win32_64
Colleagues warned me that I was wrong to ask. That's why I completely redrafted my pervious question.
a) where best to create a new line
b) how to reliably select a specific line between 20 others
c) how best to move starting point of a line
d) how best to move end point of a line
e) how best to move line
f) how to delete a line
Everything is described in the documentation and examples, but each example chooses a different place for the actions. Someone uses the view, some scene and the other does most of the things in line. Someone uses bouding rect, another not, someone uses editing mode in view another not etc...
myscene.cpp
#include "myscene.h"
#include "myview.h"
#include "mymovingpoint.h"
#include <qgraphicsitem.h>
#include <qgraphicsview.h>
#include <qobject.h>
#include <qgraphicsview.h>
#include <qpoint.h>
#include <qmath.h>
/*
*
*
*/
myScene::myScene(QObject *parent)
: QGraphicsScene(parent)
{
myMode = myMode::InsertItem;
}
/*
*
*
*/
void myScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
# remove item added by clickevent, if doubleclicked
if(line)
{
removeItem(line);
}
# if doubleclick and left button, select existing item
if(mouseEvent->buttons().testFlag(Qt::LeftButton))
{
//QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
# itemAt() returns only the object exactly under the mouse
# function getNearObject return object 3 points around
QGraphicsItem *item = getNearObject(mouseEvent);
if(item)
{
myMode = myMode::EditItem;
myLineItem *tempLine = dynamic_cast<myLineItem*>(item);
oldLine = line;
line = tempLine;
QColor myclr;
myclr.setRgb(255,0,0,255);
line->setPen(QPen(myclr, 2));
line->addMovingPoints();
}
else
{
myMode = myMode::InsertItem;
}
}
}
/*
*
*
*/
#return nearest objects
QGraphicsItem* myScene::getNearObject(QGraphicsSceneMouseEvent *mouseEvent)
{
int roundvalue = -3;
int roundx;
int roundy;
QPointF pointf = mouseEvent->scenePos();
QPointF pointtmp;
roundx = roundvalue;
roundy = roundvalue;
while(roundy <= roundvalue*-1)
{
while(roundx <= roundvalue*-1)
{
pointtmp.setX(pointf.x()-roundx);
pointtmp.setY(pointf.y()-roundy);
QGraphicsItem *item = itemAt(pointtmp, QTransform());
if(item)
{
return item;
}
roundx += 1;
}
roundx += roundvalue;
roundy += 1;
}
return NULL;
}
/*
*
*
*/
void myScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if(mouseEvent->buttons().testFlag(Qt::LeftButton))
{
QGraphicsItem *item = itemAt(mouseEvent->scenePos(), QTransform());
if(myMode == myMode::InsertItem)
{
if(oldLine)
{
myLineColor.setRgb(213, 182, 10, 255);
oldLine->setPen(QPen(myLineColor, 2));
}
}
if(myMode == myMode::EditItem)
{
myLineItem *tempLine = dynamic_cast<myLineItem*>(item);
if(tempLine)
{
oldLine = line;
line = tempLine;
QColor myclr;
myclr.setRgb(255,0,0,255);
line->setPen(QPen(myclr, 2));
myMovingPoint myPoint(line);
}
}
else if(myMode == myMode::InsertItem)
{
oldLine = line;
if(mouseEvent->modifiers() & Qt::ControlModifier)
{
line = new myLineItem(QLineF(toNearest5(mouseEvent->scenePos()), toNearest5(mouseEvent->scenePos())));
}
else
{
line = new myLineItem(QLineF(mouseEvent->scenePos(), mouseEvent->scenePos()));
}
myLineColor.setRgb(213, 182, 10, 255);
line->setPen(QPen(myLineColor, 2));
}
else
{
QGraphicsScene::mousePressEvent(mouseEvent);
}
}
}
/*
*
*
*/
void myScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
if(!items().contains(line))
{
addItem(line);
}
if(mouseEvent->buttons().testFlag(Qt::LeftButton))
{
QLineF newLine;
if(myMode == myMode::InsertItem)
{
if(mouseEvent->modifiers() & Qt::ControlModifier)
{
newLine = QLineF(toNearest5(line->line().p1()), toNearest5(mouseEvent->scenePos()));
}
else
{
newLine = QLineF(line->line().p1(), mouseEvent->scenePos());
}
myLineColor.setRgb(213, 182, 10, 255);
line->setLine(newLine);
update();
}
else if(myMode == myMode::EditItem)
{
QLineF newLine = QLineF(line->line().p1(), mouseEvent->scenePos());
line->setLine(newLine);
update();
}
}
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
/*
*
*
*/
void myScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
myMode = myMode::InsertItem;
QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
/*
*
*
*/
void myScene::keyPressEvent(QKeyEvent * keyEvent)
{
if(keyEvent->key() == Qt::Key_Delete)
{
if(line)
{
removeItem(line);
}
}
}
/*
*
*
*/
QPointF myScene::toNearest5(QPointF i)
{
int shift = 10;
int r = i.x();
int s = i.y();
r = (i.x()+5.0)/10;
s = (i.y()+5.0)/10;
r = r*10;
s = s*10;
QPointF output = QPointF(r, s);
return (output);
}
mysceneview.cpp
#include "myview.h"
#include <qmath.h>
#include <qmatrix.h>
myView::myView(QGraphicsScene *scene, QWidget *parent)
: QGraphicsView(scene, parent)
{
}
void myView::keyPressEvent(QKeyEvent *event)
{
QGraphicsView::keyPressEvent(event);
}
void myView::keyReleaseEvent(QKeyEvent *event)
{
QGraphicsView::keyReleaseEvent(event);
}
void myView::enterEvent(QEvent *event)
{
viewport()->setCursor(Qt::ArrowCursor);
QGraphicsView::enterEvent(event);
}
void myView::mousePressEvent(QMouseEvent* event)
{
if (event->modifiers() & Qt::ControlModifier)
{
viewport()->setCursor(Qt::ArrowCursor);
_lastPos = QPoint(event->pos());
//_lastPos = QPoint(event->pos().x()-event->pos().x()%5, event->pos().y()-event->pos().y()%5);
}
else
{
viewport()->setCursor(Qt::ArrowCursor);
_lastPos = event->pos();
}
QGraphicsView::mousePressEvent(event);
}
void myView::mouseMoveEvent(QMouseEvent* event)
{
viewport()->setCursor(Qt::ArrowCursor);
if (event->buttons().testFlag(Qt::RightButton))
{
QScrollBar *hBar = horizontalScrollBar();
QScrollBar *vBar = verticalScrollBar();
QPoint delta = event->pos() - _lastPos;
_lastPos = event->pos();
hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
vBar->setValue(vBar->value() - delta.y());
//event->ignore();
}
QGraphicsView::mouseMoveEvent(event);
}
void myView::mouseReleaseEvent(QMouseEvent* event)
{
viewport()->setCursor(Qt::ArrowCursor);
QGraphicsView::mouseReleaseEvent(event);
}
#if QT_CONFIG(wheelevent)
void myView::wheelEvent(QWheelEvent *e)
{
if (e->delta() > 0)
{
x=x+0.1;
qreal scale = x;
QMatrix matrix;
matrix.scale(scale, scale);
setMatrix(matrix);
//zoomIn(6);
}
else
{
x=x-0.1;
qreal scale = x;
QMatrix matrix;
matrix.scale(scale, scale);
setMatrix(matrix);
//zoomOut(6);
}
e->accept();
}
#endif
you should define in your model, what's a scene, what's a view, and what is and an item.
logically this is how it goes:
scene: collection of drawable objects, cannot be viewed.
view: a window on the scene, if the object belonging to the scene is in this window, it is shown, if it is not then it's occluded
item: is a draw-able object, stores the position of its vertex in object coordinate.
when you draw you draw in the view, then the object stores the points in its object space coordinate, they will be constant during zooms, moves, rotations and pans, they change only when you select the vertex and move it in the view.
so mylineitem is only a container for an absolute coordinate.
scene is a container of all the mylineitem, just a list of objects
view is your controller of the way the system draws the items of the scene.
this model allows you to export your drawable items either individually or as a scene without worrying about remaping the points to the new coordinate space of the new view.
Do all your object editing in the view, because it defines the distance between the points in its coordinate system.
expl:
view, coordinate system: Cartesian, 10x10x10 cm
your draw a line p1-p2
p1(0,0,0) origin of the object space, p2(1,1,1) and it maps in the view to p1view(3,5,4) expl and p2view(13,15,14) in view coordinate. means each unit in object space is 10cm in view space.
export this line to another view where system: 20x20x20 cm then line keeps its relative size and changes its coordinate to preserve it.
Currently I have this player.cpp class that I'm using for my sprite animations. I'm using a counter to update every frame. It animates, but it flies through the animations.
I want to slow this down. I found code that can be used to slow down sprite animations but I'm unsure how to implement it into my current program.
Below are my player.cpp file and following it is the code I found that can slow down sprite animations. When I've tried to add a clock to the counterWalking++ it didn't animate at all, and I've tried implementing this code to the same effect.
player::player()
{
rect.setSize(sf::Vector2f(32, 32));
rect.setFillColor(sf::Color::White);
rect.setPosition(300, 300);
sprite.setTextureRect(sf::IntRect(0, 0, 32, 32));
}
void player::update()
{
sprite.setPosition(rect.getPosition());
}
void player::updateMovement()
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
if (canMoveRight == true) {
rect.move(movementSpeed, 0);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 64, 32, 32));
direction = 4;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
if (canMoveLeft == true) {
rect.move(-movementSpeed, 0);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 32, 32, 32));
direction = 3;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) {
if (canMoveUp == true) {
rect.move(0, -movementSpeed);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 96, 32, 32));
direction = 1;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
if (canMoveDown == true) {
rect.move(0, movementSpeed);
sprite.setTextureRect(sf::IntRect(counterWalking * 32, 0, 32, 32));
direction = 2;
canMoveUp = true;
canMoveDown = true;
canMoveLeft = true;
canMoveRight = true;
}
}
else {
//Player not moving
}
counterWalking++;
if (counterWalking == 3)
counterWalking = 0;
}
Here is the code I found that displays a slow animation:
int main()
{
sf::RenderWindow renderWindow(sf::VideoMode(640, 480), "Demo Game");
sf::Event event;
sf::Texture texture;
texture.loadFromFile("images/player.png");
sf::IntRect rectSourceSprite(0, 0, 32, 32);
sf::Sprite sprite(texture, rectSourceSprite);
sf::Clock clock;
while (renderWindow.isOpen()) {
while (renderWindow.pollEvent(event)) {
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
if (clock.getElapsedTime().asSeconds() > 1.0f) {
if (rectSourceSprite.left == 96)
rectSourceSprite.left = 0;
else
rectSourceSprite.left += 32;
sprite.setTextureRect(rectSourceSprite);
clock.restart();
}
renderWindow.clear();
renderWindow.draw(sprite);
renderWindow.display();
}
}
Since a solution has been given in comment but not implemented in an answer, here's some kind of "code review"
Key Binding
This code is actually using the sf::Keyboard::isKeyPressed and sf::Keyboard::isKeyReleased wich needs to be called every loop while a simple boolean switched when the key is pressed/released could to the work using events.
while (renderWindow.pollEvent(event)) {
if (event.type == sf::Event::EventType::Closed)
renderWindow.close();
}
The Event used in the main loop does also contain information about KeyEvents but isn't used that way. I'll suggest sending the evet to the player eac time a key Event happens:
while (renderWindow.pollEvent(event)) {
switch(event.type){
case sf::Event::EventType::Closed:
renderWindow.close();
break;
case sf::Event::KeyPressed: case sf::Event::KeyReleased:
myPlayer.keyEvent(event);
break;
}
}
The keyEvent function in player should then look like this:
void player::keyEvent(sf::Event event){
//Keycode of your keyboard's arrows goes from 71 to 74
if (event.key.code >= 71 && event.key.code <= 74){
//Gets the array ID from the enumeration value
int ID = event.key.code - 71;
//Stores false if the key is release, and true if it's pressed
keys[ID] = (event.type == sf::Event::KeyPressed);
}
}
Wich changes each values of the array in Player.h:
private:
bool keys[4];
Movement
Now all you have to do is call the player update() function in your game loop each second:
if (clock.getElapsedTime().asSeconds() > 1.0f) {
if (rectSourceSprite.left == 96)
rectSourceSprite.left = 0;
else
rectSourceSprite.left += 32;
sprite.setTextureRect(rectSourceSprite);
myPlayer.update();
clock.restart();
}
In the update() function, ou will then build you movement vector based on wich key is pressed:
void player::update()
{
sf::Vector2f movementVector(0,0);
//Left
if (keys[0]){
movementVector.x -= movementSpeed;
//Sends the sprite Rectangle position based on the movement type
move(32);
}
//Right
if (keys[1]){
movementVector.x += movementSpeed;
move(64);
}
//Up
if (keys[2]){
movementVector.y -= movementSpeed;
move(96);
}
//Down
if (keys[3]){
movementVector.y += movementSpeed;
move(0);
}
}
The move function will then move your sprite Position depending on the movement vector and the counter, the sprite will then be set.
Your movement counter will be incremented up to 30 and your player may only move when this counter is equal to 0, 10 or 20:
void move(int spritePos){
//Here, the check() function should tell if the player isn't going outside of the screen / in a wall
if (check(sprite.getPosition() + movementVector)){
sprite.move(movementVector);
++counterWalking %= 30;
if(!counterWalking % 10)
sprite.setTextureRect(sf::IntRect(counterWalking / 10 * 32, spritePos, 32, 32));
}
}
There are multiple ways of doing this, that's only the way I would do it
Sounds like you might need to learn about controlling the time-step. Essentially you only execute certain code when a desired amount of time has passed.
You'll commonly see this site referenced so maybe check it out:
https://gafferongames.com/post/fix_your_timestep/
I need a double buffer because i'm starting to notice tearing when I want to move my texture made tile map around the screen via mouse click.
I'm using SDL2 and this is a SDL2 specific question, check out my code that produces tearing, whats wrong?
//set up buffer and display
bufferTexture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_STREAMING,800,600);
displayTexture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,800,600);
while(running)
{
handleEvents();
SDL_SetRenderTarget(renderer,bufferTexture); // everything goes into this buffer
SDL_RenderClear(renderer); // clear buffer before draw
gTileMovement.updateMapCoordinates();
for(int i = 0; i < MAP_ROWS; i++)//rows
{
for(int j = 0; j < MAP_COLUMNS; j++)//columns
{
x = (j * 100) - (i * 100);
y = ((i * 100) + (j * 100)) / 2;
drawTiles(i,j,x,y);
}
}
//move from buffer to display texture
memcpy(&displayTexture,&bufferTexture,sizeof((&bufferTexture)+1));
//change render target back to display texture
SDL_SetRenderTarget(renderer,displayTexture);
//show it all on screen
SDL_RenderPresent(renderer);
}
for all it matters here is my drawTiles function too, is this not conventional? :
void drawTiles(int i,int j,int x,int y)
{
//updates based on a mouse clicks xy coords
gTileMovement.updateMapCoordinates();
if(tileMap[i][j] == 1) // grass?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[1]);
}
if(tileMap[i][j] == 0) // wall?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[0]);
}
if(tileMap[i][j] == 2) // tree?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[2]);
}
}
Which followes into how I SDL_RenderCopy the tiles through a class. This copies the textures onto the current targeted renderer does it not? Which is the buffer texture if i'm not mistaken.
void LTexture::render(int x, int y, SDL_Rect * clip)
{
SDL_Rect renderQuad = {x, y, mWidth, mHeight};
if(clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
SDL_RenderCopy(renderer, mTexture, clip, &renderQuad);
}
After collision using listener one, sprite1 should be destroyed and other sprite2 will jump. I destroy sprite1 but sprite2 is not jumping after collision with sprite1. Here is my code:
Update(float dt) {
if(collisionOccurs == 1)
{
CCJumpTo * myJump = CCJumpTo::create(1, ccp(winSize.width/6,winSize.height/8),winSize.height/2 ,1);
Sprite2->runAction(myJump);
collisionOccurs = 0;
if(Sprite1 != NULL && BodyofSprite1 != NULL)
{
BodyofSprite1->SetActive(false);
Sprite1->removeFromParentAndCleanup(true);
}
}
}