I am currently working on subdividing my cocos2dx-cpp game into a more modular system. I want to have one layer to receive all Touches and direct those touches to the affected CCSprite-derived objects.
The derived objects are stored in a CCArray in an EntityManager (which helps me create and manage the entities).
The problem I am facing is that I can't seem to access the correct virtual method for my derived CCSprites.
Here is the code from my Touch layer (called TouchManager):
void TouchManager::ccTouchesBegan( cocos2d::CCSet* pTouches , cocos2d::CCEvent* pEvents )
{
cocos2d::CCSetIterator i;
cocos2d::CCTouch* touch;
cocos2d::CCPoint tap;
auto entities = EntityManager::sharedManager()->getVisibleEntities();
for ( i = pTouches->begin() ; i != pTouches->end() ; ++i )
{
touch = ( cocos2d::CCTouch* ) ( *i );
if ( touch )
{
tap = touch->getLocation();
for ( unsigned int entityIndex = 0 ; entityIndex < entities->size() ; ++entityIndex )
{
auto entity = entities->at( entityIndex );
// OLD: auto entity = ( TouchableSprite* )entities->objectAtIndex( entityIndex );
if ( entity->boundingBox().containsPoint( tap ) )
{
entity->setTouch( touch );
entity->onTouch( tap );
}
}
}
}
}
I want to have the TouchManager detect the entity that has been touched, and send the Touch to it. But there is my problem: it detects the touch but doesn't send it further. Either I have a crash or nothing at all.
I have created a Touchable interface class:
#include "cocos2d.h"
class Touchable : public cocos2d::CCSprite
{
cocos2d::CCTouch* m_pTouch;
public:
virtual cocos2d::CCTouch* getTouch();
virtual void setTouch( cocos2d::CCTouch* touch );
virtual void onTouch( cocos2d::CCPoint location ) = 0 ;
virtual void onMoved( cocos2d::CCPoint location ) = 0 ;
virtual void onEnded( cocos2d::CCPoint location ) = 0 ;
};
as well as a TouchableSprite base class:
#include "cocos2d.h"
#include "Touchable.h"
class TouchableSprite : public Touchable
{
//cocos2d::CCTouch* m_pTouch;
public:
//virtual cocos2d::CCTouch* getTouch();
//virtual void setTouch( cocos2d::CCTouch* touch );
static TouchableSprite* createSpriteWithFile( const char* fileName );
void resetPosition( float positionX = 0.0f , float positionY = 0.0f );
virtual void onTouch( cocos2d::CCPoint location ) ;
virtual void onMoved( cocos2d::CCPoint location ) ;
virtual void onEnded( cocos2d::CCPoint location ) ;
TouchableSprite(void);
~TouchableSprite(void);
};
with simple implementation (TouchableSprite.cpp):
#include "TouchableSprite.h"
TouchableSprite::TouchableSprite(void)
{
}
TouchableSprite::~TouchableSprite(void)
{
}
TouchableSprite* TouchableSprite::createSpriteWithFile( const char* fileName )
{
auto sprite = new TouchableSprite();
if ( sprite && sprite->initWithFile( fileName ) )
{
sprite->autorelease();
return ( TouchableSprite* )sprite;
}
CC_SAFE_DELETE( sprite );
// should not reach this point
return NULL;
}
void TouchableSprite::resetPosition( float positionX , float positionY )
{
this->setPosition( ccp( positionX , positionY ) );
}
void TouchableSprite::onTouch( cocos2d::CCPoint location )
{
}
void TouchableSprite::onMoved( cocos2d::CCPoint location )
{
}
void TouchableSprite::onEnded( cocos2d::CCPoint location )
{
}
And finally, here's my derived class (in this case, ControlStickSprite):
#include "cocos2d.h"
#include "RenderSystem.h"
#include "EntityManager.h"
#include "TouchableSprite.h"
class ControlStickSprite : public TouchableSprite
{
ControlStickSprite* m_sprite;
public:
cocos2d::CCNode* create( cocos2d::CCNode* parent );
void onTouch( cocos2d::CCPoint location ) ;
void onMoved( cocos2d::CCPoint location ) ;
void onEnded( cocos2d::CCPoint location ) ;
ControlStickSprite(void);
~ControlStickSprite(void);
};
with simple implementation for testing (skipping the Create part because it works):
void ControlStickSprite::onTouch( cocos2d::CCPoint location )
{
this->setScale( 0.5f );
}
void ControlStickSprite::onMoved( cocos2d::CCPoint location )
{
this->setPosition( location );
}
void ControlStickSprite::onEnded( cocos2d::CCPoint location )
{
}
Please help me get this working! I'm not too familiar with the usage of virtual methods so maybe I missed something there. I'm also relatively new to C++ and cocos2dx programming.
Thanks in advance!
EDIT: Thanks to #musikov for fixing the first part! I updated the above code to reflect the changes. I replaced the CCArray with std::vector< TouchableSprite* > to eliminate the need for casting the from CCObject*.
Now, I am facing the problem that when touched, ControlStickSprite::onTouch() is never chosen; it's always TouchableSprite::onTouch().
Added ControlStickSprite::create and EntityManager::createEntity methods:
My ControlStickSprite::create() method is like this:
ControlStickSprite* ControlStickSprite::create( cocos2d::CCNode* parent )
{
// auto parent = this->getParent();
auto entityType = "control-stick";
auto scale = 6.0f;
auto rotation = 0.0f;
auto positionX = RenderSystem::sharedRenderSystem()->getScreenWidth() * 0.9f ;
auto positionY = RenderSystem::sharedRenderSystem()->getScreenHeight() * 0.25f ;
auto sprite = EntityManager::sharedManager()->createEntity(
parent ,
entityType ,
scale ,
rotation ,
positionX ,
positionY
);
m_sprite = ( ControlStickSprite* )sprite;
return m_sprite;
}
which makes use of my EntityManager:
cocos2d::CCNode* EntityManager::createEntity( cocos2d::CCNode* parent , const char* entityType , float scale , float rotation , float positionX , float positionY )
{
std::string extension = ".png";
std::string fileName = entityType + extension;
auto entity = TouchableSprite::createSpriteWithFile( fileName.c_str() );
entity->setRotation( rotation );
entity->setScale( scale );
entity->resetPosition( positionX , positionY );
parent->addChild( entity );
// add to VisibleEntities vector
this->addEntity( entity , true );
return entity;
}
The only thing I can think of is that the createEntity() method creates a TouchableSprite* but returns a CCNode*, which I then cast to a ControlStickSprite*. Am I doing this wrong again? :)
Thanks for all your help!
You have wrong create method implementation in TouchableSprite and maybe in ControlStickSprite too.
Your create method creates Sprite instance and casts it to TouchableSprite class. It's completely wrong :)
That's why your program crashed on setTouch method call - because your calling this method in Sprite instance.
You need to change your create method:
TouchableSprite* TouchableSprite::createSpriteWithFile( const char* fileName )
{
auto sprite = new TouchableSprite();
if ( sprite && sprite->initWithFile( fileName ) )
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE( sprite );
// should not reach this point
return NULL;
}
Added controlstick implementation
ControlStickSprite.h
#include "cocos2d.h"
#include "RenderSystem.h"
#include "EntityManager.h"
#include "TouchableSprite.h"
class ControlStickSprite : public TouchableSprite
{
public:
static ControlStickSprite* create();
bool init();
void onTouch( cocos2d::CCPoint location ) ;
void onMoved( cocos2d::CCPoint location ) ;
void onEnded( cocos2d::CCPoint location ) ;
ControlStickSprite(void);
~ControlStickSprite(void);
};
ControlStickSprite.cpp
bool ControlStickSprite::init()
{
auto entityType = "control-stick";
std::string extension = ".png";
std::string fileName = entityType + extension;
if (initWithFile( fileName.c_str() )) {
// auto parent = this->getParent();
auto scale = 6.0f;
auto rotation = 0.0f;
auto positionX = RenderSystem::sharedRenderSystem()->getScreenWidth() * 0.9f ;
auto positionY = RenderSystem::sharedRenderSystem()->getScreenHeight() * 0.25f ;
sprite->setRotation( rotation );
sprite->setScale( scale );
sprite->resetPosition( positionX , positionY );
return true;
}
return false;
}
ControlStickSprite* ControlStickSprite::create()
{
auto sprite = new ControlStickSprite();
if ( sprite && sprite->init() )
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE( sprite );
// should not reach this point
return NULL;
}
EntityManager:
void EntityManager::addEntity( cocos2d::CCNode* parent , TouchableSprite* entity )
{
parent->addChild( entity );
// add to VisibleEntities vector
this->addEntity( entity , true );
}
Remember to call EntityManager::addEntity(parent, entity) after ControlStickSprite::create(), if you decide to use this solution
Related
I've been trying to create a dynamic light effect with cocos2d-x, but I cannot find a good tutorial for the 4.0 version.
For the context, I'm developping a top-down game for as a personnal project. The light would spread directly in front of the player like a flash-light, lighting every object in front of him and "colliding" with the walls and entities
The Shader hint
I've found multiple repos creating a custom light effect with cocos2d-x, but they all use the 3.0 version. The 4.0 version changed how the OpenGL code is managed in the Cocos backend.
Here's the repos I've found :
https://github.com/CodeAndWeb/cocos2d-x-dynamic-lighting
https://github.com/zerodarkzone/Cocos2d-x-lights
https://github.com/wantnon2/EffectNodes-for-cocos2dx
I've also read the Cocos2d-x Gitbook which explains what exactly changed in the CCSprite file.
Since I'm a beginner to Cocos2d-x and shader rendering, I did not understand what I had to change to make the old repos work ..
The polygon hint
I tried to create a VisionZone that would be a Cocos2d-x Node. The node would have a physic body colliding with the walls. On collision, the shape would update and adapt its shape.
Here's what the code would look like :
VisionZone.h
class VisionZone : public Node {
public:
static std::vector<Vec2> sm_shapeCollisionPoints;
static VisionZone *create(Vec2 origin, int visionDetail);
bool init() override;
void update(float delta) override;
void UpdateWithPlayer(Vec2 playerPos);
private:
void CreateNewPhysicBody(std::vector<Vec2> &points);
void UpdateShapeAndRedraw();
static bool IsInLine(Vec2 segmentStart, Vec2 segmentEnd, Vec2 point);
void CompareWithContactPoints();
void Rotate();
std::vector<Vec2> m_nonCollidedSegmentsEnds;
std::vector<Vec2> m_collidedSegmentsEnds;
Vec2 m_origin;
DrawNode *m_pVisionZoneDrawer;
int m_visionDetail;
VisionZone.cpp
std::vector<Vec2> VisionZone::sm_shapeCollisionPoints = {};
VisionZone *VisionZone::create(Vec2 origin, int visionDetail) {
auto *_obj = new(std::nothrow) VisionZone();
if (_obj && _obj->init()) {
_obj->m_origin = origin;
_obj->m_visionDetail = visionDetail;
int mid = std::floor(_obj->m_visionDetail * 0.5);
for (int i = -(mid); i <= mid; i++) {
_obj->m_nonCollidedSegmentsEnds.emplace_back(
static_cast<float>(_obj->m_origin.x + float(i) * 1.3f), _obj->m_origin.y + 300
);
}
_obj->m_collidedSegmentsEnds = _obj->m_nonCollidedSegmentsEnds;
std::vector<Vec2> _points = _obj->m_nonCollidedSegmentsEnds;
_points.emplace_back(_obj->m_origin);
_obj->CreateNewPhysicBody(_points);
} else
CC_SAFE_DELETE(_obj);
return _obj;
}
bool VisionZone::init() {
if (!Node::init()) return false;
m_pVisionZoneDrawer = DrawNode::create();
m_pVisionZoneDrawer->setPosition(m_origin);
assert(m_pVisionZoneDrawer);
addChild(m_pVisionZoneDrawer);
return true;
}
void VisionZone::update(float delta) {
Node::update(delta);
CompareWithContactPoints();
Rotate();
UpdateShapeAndRedraw();
}
void VisionZone::UpdateWithPlayer(Vec2 playerPos) {
m_origin = playerPos;
setPosition(m_origin);
}
void VisionZone::CreateNewPhysicBody(std::vector<Vec2> &points) {
points.push_back(m_origin);
PhysicsBody *_body = PhysicsBody::createEdgePolygon(
&points.front(),
int(points.size()),
PHYSICSBODY_MATERIAL_DEFAULT,
1
);
_body->setCategoryBitmask(vision_zone_collision_bitmask);
_body->setCollisionBitmask(map_collision_bitmask);
_body->setContactTestBitmask(true);
_body->setDynamic(false);
setPhysicsBody(_body);
}
bool VisionZone::IsInLine(Vec2 segmentStart, Vec2 segmentEnd, Vec2 point) {
float _startToPoint = sqrt(pow((segmentStart.x - point.x), 2) + pow((segmentStart.y - point.y), 2));
float _pointToEnd = sqrt(pow((point.x - segmentEnd.x), 2) + pow((point.y - segmentEnd.y), 2));
float _startToEnd = sqrt(pow((segmentStart.x - segmentEnd.x), 2) + pow((segmentStart.y - segmentEnd.y), 2));
return (_startToPoint + _pointToEnd == _startToEnd);
}
void VisionZone::CompareWithContactPoints() {
if (sm_shapeCollisionPoints.empty()) {
m_collidedSegmentsEnds = {m_origin, m_nonCollidedSegmentsEnds.front(), m_nonCollidedSegmentsEnds.back()};
return;
}
for (Vec2 &_nonCollidedEnd: m_nonCollidedSegmentsEnds) {
for (Vec2 &_contactPoint: sm_shapeCollisionPoints) {
if (IsInLine(m_origin, _nonCollidedEnd, _contactPoint)) {
Vec2 _midPoint = (m_nonCollidedSegmentsEnds.front() + m_nonCollidedSegmentsEnds.back()) / 2;
if (IsInLine(m_nonCollidedSegmentsEnds.front(), _midPoint, _contactPoint)) {
m_collidedSegmentsEnds = {
m_origin, sm_shapeCollisionPoints.front(), sm_shapeCollisionPoints.back(),
m_nonCollidedSegmentsEnds.back()
};
} else if (IsInLine(_midPoint, m_nonCollidedSegmentsEnds.back(), _contactPoint)) {
m_collidedSegmentsEnds = {
m_origin, m_nonCollidedSegmentsEnds.front(), sm_shapeCollisionPoints.front(),
sm_shapeCollisionPoints.back()
};
}
}
}
}
}
void VisionZone::Rotate() {
float _distanceX = InputManager::GetCursorPosX() - getPositionX();
float _distanceY = InputManager::GetCursorPosY() - getPositionY();
float _angle = atan2(_distanceY, _distanceX) * 180.f / static_cast<float>(M_PI);
setRotation(_angle);
}
void VisionZone::UpdateShapeAndRedraw() {
PhysicsShape *_newShape = getPhysicsBody()->addShape(PhysicsShapePolygon::create(
m_collidedSegmentsEnds.data(), int(m_collidedSegmentsEnds.size()))
);
assert(_newShape);
m_pVisionZoneDrawer->clear();
m_pVisionZoneDrawer->drawSolidPoly(
&m_collidedSegmentsEnds.front(),
m_collidedSegmentsEnds.size(),
Color4F(1.f, 0.94, 0.7, 1)
);
}
CollisionManager.cpp
#include "CollisionManager.h"
#include "Utility/Bitmasks.h"
USING_NS_CC;
EventListenerPhysicsContact *CollisionManager::m_contactListener = nullptr;
void CollisionManager::Init() {
m_contactListener = EventListenerPhysicsContact::create();
m_contactListener->onContactBegin = [](PhysicsContact &contact) { return ContactBeginCallback(contact); };
m_contactListener->onContactSeparate = [](PhysicsContact &contact) { return ContactSeparateCallback(contact); };
}
bool CollisionManager::ContactBeginCallback(PhysicsContact &contact) {
PhysicsBody *_bodyA = contact.getShapeA()->getBody();
PhysicsBody *_bodyB = contact.getShapeB()->getBody();
const bool visionAndWallCondition = ((_bodyA->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyB->getCategoryBitmask() == map_collision_bitmask) ||
(_bodyB->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyA->getCategoryBitmask() == map_collision_bitmask));
if (visionAndWallCondition) {
for (Vec2 _p: contact.getContactData()->points) VisionZone::sm_shapeCollisionPoints.push_back(_p);
return true;
}
return false;
}
bool CollisionManager::ContactSeparateCallback(PhysicsContact &contact) {
PhysicsBody *_bodyA = contact.getShapeA()->getBody();
PhysicsBody *_bodyB = contact.getShapeB()->getBody();
const bool visionAndWallCondition = ((_bodyA->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyB->getCategoryBitmask() == map_collision_bitmask) ||
(_bodyB->getCategoryBitmask() == vision_zone_collision_bitmask &&
_bodyA->getCategoryBitmask() == map_collision_bitmask));
if (visionAndWallCondition) {
VisionZone::sm_shapeCollisionPoints.clear();
return true;
}
return false;
}
GameLayer.cpp
bool GameLayer::init() {
if (!Layer::init()) return false;
CollisionManager::Init();
_eventDispatcher->addEventListenerWithSceneGraphPriority(CollisionManager::GetContactListener(), this);
// ... SOME CODE ... //
m_visionZone = VisionZone::create(player->getPosition(), 100);
addChild(m_visionZone, 1);
return true;
}
void GameLayer::update(float delta) {
Node::update(delta);
m_visionZone->UpdateWithPlayer(m_player->getPosition());
}
I am not sure about this method though ... I feels like it is not optimal at all ?
Really curious to have some feedback on this, thanks in advance, sorry for the quantity of code :)
Let me know if you need more informations, code or links
I'm working at QT application that have a OSGWidget. How could i get coordinates of picked object in osg viewer and transfer it to QT textEdit in Dialog window. Could you give advice how to extract osg::Vec3d of selected object from viewer convert it to string and show it off? For example. I have these scene:
And when i click on add button it opens a dialog window where i have a textEdit object. Transfer coords in this editText. How could be this done? Forget to add that this cubes are imported from ads file. Probably it can help in any place.
In order to retrieve an object's coordinates from your scene, you'll need to add new event handler to your viewer. Let's call it a PickHandler.
Here's a basic code that will get you started. You'll need to add the "includes" and modify it to suit your needs. (Please note that I haven't tested it. I wrote it "by memory", but if there are any errors the should be very easy to fix).
PickHandler.h
class PickHandler: public QObject, public osgGA::GUIEventHandler {
Q_OBJECT
public:
PickHandler();
virtual bool handle( const osgGA::GUIEventAdapter& ea,
osgGA::GUIActionAdapter& aa );
signals:
void query( osg::Vec3f );
protected:
virtual ~PickHandler()
{
}
bool pick( const double x, const double y, osgViewer::Viewer* viewer );
private:
bool getPickedPoint( const double x, const double y, float buffer,
osgViewer::Viewer* viewer, osg::Vec3f& point );
};
PickHandler.cpp
PickHandler::PickHandler() : osgGA::GUIEventHandler()
{
}
bool PickHandler::handle( const osgGA::GUIEventAdapter &ea,
osgGA::GUIActionAdapter &aa )
{
osgViewer::View* viewer = dynamic_cast<osgViewer::Viewer*>( &aa );
if( !viewer ) {
return false;
}
switch( ea.getEventType() ) {
default:
break;
case osgGA::GUIEventAdapter::RELEASE: {
if( pick(ea.getXnormalized(), ea.getYnormalized(),
viewer ) )
{
return true;
}
break;
}
}
return false;
}
bool PickHandler::pick( const double x, const double y,
osgViewer::Viewer* viewer )
{
if( !viewer->getSceneData() ) {
return false;
}
osg::Vec3f point;
float buffer = 0.005f;
if( getPickedPoint( x, y, buffer, viewer, point ) ) {
emit query( point );
}
return false;
}
bool PickHandler::getPickedPoint( const double x, const double y, float buffer,
osgViewer::Viewer* viewer,
osg::Vec3f& point )
{
osg::ref_ptr<osgUtil::PolytopeIntersector> intersector( 0 );
try {
intersector = new osgUtil::PolytopeIntersector(
osgUtil::Intersector::PROJECTION,
x - buffer, y - buffer, x + buffer,
y + buffer )
;
} catch( const std::bad_alloc& ) {
return false;
}
// DimZero = check only for points
intersector->setDimensionMask( osgUtil:: PolytopeIntersector::DimZero );
//
intersector->setIntersectionLimit( osgUtil::Intersector::LIMIT_NEAREST );
osgUtil::IntersectionVisitor iv( intersector );
viewer->getCamera()->accept( iv );
if( intersector->containsIntersections() ) {
osgUtil::PolytopeIntersector::Intersection intersection =
*( intersector->getIntersections().begin() )
;
const osg::Vec3f& P = intersection.intersectionPoints[ 0 ];
if( P.isNaN() ) {
return false;
}
point.set( P[ 0 ], P[ 1 ], P[ 2 ] );
return true;
}
return false;
}
I'm using a PolytopeIntersector, since I don't have any solid models, like the cessna in the OSG's example data; only a lot of points and using a LineIntersector (the fastest) is almost impossible to get a hit. The Polytope will build a frustrum volume intersects with anything in the area you have specified (with the parameters when constructing the Polytope).
Also, you might need to play with the parameters you send to the pick function, like the buffer size. I use ea.getXNormalized() and inside pick() an osgUtil::Intersector::PROJECTION value.
If you use, say an osgUtil::Intersector::WINDOW value, you don't need to normalize the mouse values. If you don't have any "strange" transformations in your view, most likely the PROJECTION value is what you need.
Last thing, this code is rather old. With newer osg versions, maybe some of it will be seen as deprecated. I'm not sure as I haven't updated yet my picker code.
Now, with this code, when an itersection is found, it retrieves the FIRST one and send the point values via an emit. You just have to connect this emit to your SLOT and receive the cliked point coords.
Lastly, for converting something to a string, you can use the Qt String function, QString::number(...).
For example:
const QString x = QString::number( point[0], 'd', 6 );
will stringify the x-coord of the point using fixed-point format with 6 decimal places.
This is my first question here and I believe that I havent seen anyone else asking for this specific problem but what I'm currently dealing with is a qwt plot that doesn't want to replot.
What I want to do: Call the replot() method to clear my current plot.
My problem: When calling replot() does not clear my current plot and my plots are drawn on top of eachother.
Here is a link to an image of my problem
As can be seen in the image, the new curves are drawn on top of the existing ones and this is what I want to solve.
Here is some of my code: (let me know if I missed some parts)
plot.cpp
#include "plot.h"
#include <qwt_plot.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_directpainter.h>
#include <qwt_curve_fitter.h>
#include <qwt_painter.h>
class CurveData: public QwtArraySeriesData<QPointF>
{
public:
CurveData()
{
}
virtual QRectF boundingRect() const
{
if ( d_boundingRect.width() < 0.0 )
d_boundingRect = qwtBoundingRect( *this );
return d_boundingRect;
}
inline void append( const QPointF &point )
{
d_samples += point;
}
void clear()
{
d_samples.clear();
d_samples.squeeze();
d_boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 );
}
};
Plot::Plot( QWidget *parent ):
QwtPlot( parent )
{
d_directPainter = new QwtPlotDirectPainter(this);
setAutoReplot(false);
setAxisScale(QwtPlot::yLeft, 0.014,0.016);
setAxisScale(QwtPlot::xBottom, 0, 1000);
d_curve = new QwtPlotCurve();
d_curve->setData(new CurveData());
d_curve->attach(this);
}
void Plot::AppendPoint(const QPointF &point)
{
CurveData *data = static_cast<CurveData *>(d_curve->data());
data->append(point);
}
void Plot::DrawCurveSegment()
{
CurveData *data = static_cast<CurveData *>(d_curve->data());
d_directPainter->drawSeries(d_curve, data->size()-11, data->size()-1);
}
void Plot::ClearPlot()
{
CurveData *data = static_cast<CurveData *>(d_curve->data());
data->clear();
QwtPlot::replot();
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent):
QWidget( parent )
{
d_plot = new Plot();
counter = 0;
loopCounter = 0;
// ... //
}
void MainWindow::timerEvent(QTimerEvent *) {
if (counter>0 && counter%1000==0)
{
d_plot->ClearPlot();
d_plot->replot();
loopCounter++;
qDebug()<<"clear!";
}
for (int ii=0; ii<10;ii++)
{
double y = someArray[counter];
double x = (double)counter-((double)loopCounter*1000);
counter++;
d_plot->AppendPoint(QPointF(x,y));
}
d_plot->DrawCurveSegment();
}
Would very much appreciate if someone can see what I'm doing wrong.
Best regards!
I solved the problem by changing the clearPlot method to the following:
void Plot::ClearPlot()
{
CurveData *data = static_cast<CurveData *>(d_curve->data());
data->clear();
QwtPlot::replot();
QwtPlot::repaint();
}
You can also do this:
QwtPlot::detachItems(QwtPlotItem::Rtti_PlotItem, true);
In my project I need to position things around other things in a spherical way, so I figured I needed a spherical coordinate representation. I'm using Ogre3D graphic rendering engine which provide some maths constructs which at the moment I use everywhere in my project.
The source code of Ogre is available there, in the 1.9 branch as I'm using an old version of it. I checked yesterday that the maths types didn't change in a long time so my code is relying on the code you can see there, in particular Maths, Vector3 and Quaternion. Here I'm assuming that Ogre's code is correct, but you never know, which is why I'm pointing to it as my code is relying heavily on it. Also know that the referential used in Ogre is right handed, with -Z being the default orientation of any entity, +Y being up of the screen, +X being the right side of the screen, and +Z being the direction toward you, -Z being the direction toward the screen.
Now, here is the spherical coordinate code I have a problem with:
#ifndef HGUARD_NETRUSH_ZONEVIEW_SPHERICAL_HPP__
#define HGUARD_NETRUSH_ZONEVIEW_SPHERICAL_HPP__
#include <iosfwd>
#include <OgreMath.h>
#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include "api.hpp"
namespace netrush {
namespace zoneview {
/** Spherical coordinates vector, used for spherical coordinates and transformations.
Some example values:
( radius = 1.0, theta = 0.0deg , phi = 0.0deg ) <=> Y unit vector in cartesian space
( radius = 1.0, theta = 90.0deg, phi = 0.0deg ) <=> Z unit vector in cartesian space
( radius = 1.0, theta = 90.0deg , phi = 90.0deg ) <=> X unit vector in cartesian space
*/
struct SphereVector
{
Ogre::Real radius; ///< Rho or Radius is the distance from the center of the sphere.
Ogre::Radian theta; ///< Theta is the angle around the x axis (latitude angle counterclockwise), values range from 0 to PI.
Ogre::Radian phi; ///< Phi is the angle around the y axis (longitude angle counterclockwise), values range from 0 to 2PI.
NETRUSH_ZONEVIEW_API static const SphereVector ZERO;
NETRUSH_ZONEVIEW_API static const SphereVector UNIT_X;
NETRUSH_ZONEVIEW_API static const SphereVector UNIT_Y;
NETRUSH_ZONEVIEW_API static const SphereVector UNIT_Z;
NETRUSH_ZONEVIEW_API static const SphereVector NEGATIVE_UNIT_X;
NETRUSH_ZONEVIEW_API static const SphereVector NEGATIVE_UNIT_Y;
NETRUSH_ZONEVIEW_API static const SphereVector NEGATIVE_UNIT_Z;
SphereVector() = default;
SphereVector( Ogre::Real radius, Ogre::Radian theta, Ogre::Radian phi )
: radius( std::move(radius) ), theta( std::move(theta) ), phi( std::move(phi) )
{}
explicit SphereVector( const Ogre::Vector3& cartesian_vec )
{
*this = from_cartesian( cartesian_vec );
}
void normalize()
{
using namespace Ogre;
while( phi > Degree(360.f) ) phi -= Degree(360.f);
while( theta > Degree(180.f) ) theta -= Degree(180.f);
while( phi < Radian(0) ) phi += Degree(360.f);
while( theta < Radian(0) ) theta += Degree(180.f);
}
SphereVector normalized() const
{
SphereVector svec{*this};
svec.normalize();
return svec;
}
/** #return a relative Cartesian vector coordinate from this relative spherical coordinate. */
Ogre::Vector3 to_cartesian() const
{
using namespace Ogre;
const auto svec = normalized();
Vector3 result;
result.x = radius * Math::Sin( svec.phi ) * Math::Sin( svec.theta );
result.z = radius * Math::Cos( svec.phi ) * Math::Sin( svec.theta );
result.y = radius * Math::Cos( svec.theta );
return result;
}
/** #return a relative spherical coordinate from a cartesian vector. */
static SphereVector from_cartesian( const Ogre::Vector3& cartesian )
{
using namespace Ogre;
SphereVector result = SphereVector::ZERO;
result.radius = cartesian.length();
if( result.radius == 0 )
return result;
result.phi = Math::ATan2( cartesian.x, cartesian.z );
result.theta = Math::ATan2( Math::Sqrt( Math::Sqr( cartesian.x ) + Math::Sqr( cartesian.z ) ), cartesian.y );
result.normalize();
return result;
}
friend SphereVector operator-( const SphereVector& value )
{
SphereVector result;
result.radius = -value.radius;
result.theta = -value.theta;
result.phi = -value.phi;
return result;
}
friend SphereVector operator+( const SphereVector& left, const SphereVector& right )
{
SphereVector result;
result.radius = left.radius + right.radius;
result.theta = left.theta + right.theta;
result.phi = left.phi + right.phi;
return result;
}
friend SphereVector operator-( const SphereVector& left, const SphereVector& right )
{
return left + (-right);
}
SphereVector& operator+=( const SphereVector& other )
{
*this = *this + other;
return *this;
}
SphereVector& operator-=( const SphereVector& other )
{
*this = *this - other;
return *this;
}
/// Rotation of the position around the relative center of the sphere.
friend SphereVector operator*( const SphereVector& sv, const Ogre::Quaternion& rotation )
{
const auto cartesian_vec = sv.to_cartesian();
const auto rotated_vec = rotation * cartesian_vec;
SphereVector result { rotated_vec };
result.normalize();
return result;
}
/// Rotation of the position around the relative center of the sphere.
friend SphereVector operator*( const Ogre::Quaternion& rotation, const SphereVector& sv ) { return sv * rotation; }
/// Rotation of the position around the relative center of the sphere.
SphereVector& operator*=( const Ogre::Quaternion& rotation )
{
*this = *this * rotation;
return *this;
}
friend bool operator==( const SphereVector& left, const SphereVector& right )
{
return Ogre::Math::RealEqual( left.radius, right.radius )
&& left.phi == right.phi
&& left.theta == right.theta
;
}
friend bool operator!=( const SphereVector& left, const SphereVector& right )
{
return !( left == right );
}
};
inline std::ostream& operator<<( std::ostream& out, const SphereVector& svec )
{
out << "{ radius = " << svec.radius
<< ", theta = " << svec.theta
<< ", phi = " << svec.phi
<< " }";
return out;
}
inline bool real_equals( const SphereVector& left, const SphereVector& right, Ogre::Real tolerance = 1e-03 )
{
using namespace Ogre;
return Math::RealEqual( left.radius, right.radius, tolerance )
&& Math::RealEqual( left.theta.valueAngleUnits(), right.theta.valueAngleUnits(), tolerance )
&& Math::RealEqual( left.phi.valueAngleUnits(), right.phi.valueAngleUnits(), tolerance )
;
}
}}
#endif
The constants are defined in the cpp:
#include "spherical.hpp"
namespace netrush {
namespace zoneview {
const SphereVector SphereVector::ZERO( 0.f, Ogre::Radian( 0.f ), Ogre::Radian( 0.f ) );
const SphereVector SphereVector::UNIT_X( Ogre::Vector3::UNIT_X );
const SphereVector SphereVector::UNIT_Y( Ogre::Vector3::UNIT_Y );
const SphereVector SphereVector::UNIT_Z( Ogre::Vector3::UNIT_Z );
const SphereVector SphereVector::NEGATIVE_UNIT_X( Ogre::Vector3::NEGATIVE_UNIT_X );
const SphereVector SphereVector::NEGATIVE_UNIT_Y( Ogre::Vector3::NEGATIVE_UNIT_Y );
const SphereVector SphereVector::NEGATIVE_UNIT_Z( Ogre::Vector3::NEGATIVE_UNIT_Z );
}}
The failing test (the lines marked ast FAILURE):
inline bool check_compare( SphereVector left, SphereVector right )
{
std::cout << "----"
<< "\nComparing "
<< "\n Left: " << left
<< "\n Right: " << right
<< std::endl;
return real_equals( left, right );
}
// ...
TEST( Test_SphereVector, axe_rotation_quaternion )
{
using namespace Ogre;
const auto init_svec = SphereVector::NEGATIVE_UNIT_Z;
static const auto ROTATION_TO_X = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::UNIT_X );
static const auto ROTATION_TO_Y = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::UNIT_Y );
static const auto ROTATION_TO_Z = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::UNIT_Z );
static const auto ROTATION_TO_NEGATIVE_X = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::NEGATIVE_UNIT_X );
static const auto ROTATION_TO_NEGATIVE_Y = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::NEGATIVE_UNIT_Y );
static const auto ROTATION_TO_NEGATIVE_Z = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::NEGATIVE_UNIT_Z );
static const auto ROTATION_360 = ROTATION_TO_Z * 2;
const auto svec_x = init_svec * ROTATION_TO_X;
const auto svec_y = init_svec * ROTATION_TO_Y;
const auto svec_z = init_svec * ROTATION_TO_Z;
const auto svec_nx = init_svec * ROTATION_TO_NEGATIVE_X;
const auto svec_ny = init_svec * ROTATION_TO_NEGATIVE_Y;
const auto svec_nz = init_svec * ROTATION_TO_NEGATIVE_Z;
const auto svec_360 = init_svec * ROTATION_360;
EXPECT_TRUE( check_compare( svec_x.to_cartesian() , Vector3::UNIT_X ) );
EXPECT_TRUE( check_compare( svec_y.to_cartesian() , Vector3::UNIT_Y ) );
EXPECT_TRUE( check_compare( svec_z.to_cartesian() , Vector3::UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_nx.to_cartesian() , Vector3::NEGATIVE_UNIT_X ) );
EXPECT_TRUE( check_compare( svec_ny.to_cartesian() , Vector3::NEGATIVE_UNIT_Y ) );
EXPECT_TRUE( check_compare( svec_nz.to_cartesian() , Vector3::NEGATIVE_UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_360.to_cartesian(), Vector3::NEGATIVE_UNIT_Z ) ); // FAILURE 1
EXPECT_TRUE( check_compare( svec_x , SphereVector::UNIT_X ) );
EXPECT_TRUE( check_compare( svec_y , SphereVector::UNIT_Y ) ); // FAILURE 2
EXPECT_TRUE( check_compare( svec_z , SphereVector::UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_nx , SphereVector::NEGATIVE_UNIT_X ) );
EXPECT_TRUE( check_compare( svec_ny , SphereVector::NEGATIVE_UNIT_Y ) ); // FAILURE 3
EXPECT_TRUE( check_compare( svec_nz , SphereVector::NEGATIVE_UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_360, SphereVector::NEGATIVE_UNIT_Z ) ); // FAILURE 4
}
Excerpt from the test report:
Failure 1:
4> ----
4> Comparing
4> Left: Vector3(9.61651e-007, -3.0598e-007, 7)
4> Right: Vector3(0, 0, -1)
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(210): error : Value of: check_compare( svec_360.to_cartesian(), Vector3::NEGATIVE_UNIT_Z )
4> Actual: false
4> Expected: true
Failure 2:
4> ----
4> Comparing
4> Left: { radius = 1, theta = Radian(1.4783e-007), phi = Radian(5.65042) }
4> Right: { radius = 1, theta = Radian(0), phi = Radian(0) }
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(213): error : Value of: check_compare( svec_y , SphereVector::UNIT_Y )
4> Actual: false
4> Expected: true
Failure 3:
4> ----
4> Comparing
4> Left: { radius = 1, theta = Radian(3.14159), phi = Radian(5.82845) }
4> Right: { radius = 1, theta = Radian(3.14159), phi = Radian(0) }
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(216): error : Value of: check_compare( svec_ny , SphereVector::NEGATIVE_UNIT_Y )
4> Actual: false
4> Expected: true
Failure 4:
4> Comparing
4> Left: { radius = 7, theta = Radian(1.5708), phi = Radian(1.37379e-007) }
4> Right: { radius = 1, theta = Radian(1.5708), phi = Radian(3.14159) }
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(218): error : Value of: check_compare( svec_360, SphereVector::NEGATIVE_UNIT_Z )
4> Actual: false
4> Expected: true
The same code on gist: https://gist.github.com/Klaim/8633895 with constants defined there: https://gist.github.com/Klaim/8634224
Full tests (using GTest): https://gist.github.com/Klaim/8633917
Full test report: https://gist.github.com/Klaim/8633937
(I can't put it here because of the text size limitation)
As you can see in the error report, there is 4 errors. I just can't find a solution for these, so maybe someone here could point me to what I'm doing wrong. I believe the problem could be from the test itself, but I'm not sure at all. Also, note that there are tests missing that I plan to add. The
The api.hpp include only expose the macros for shared library symbol export/import, used for constants.
This code is supposed to be extracted to be provided as a separate small open source library.
What I'm asking is: is this code incorrect? Or is my test incorrect?
I have a class MotionDirection with static members. The source code of the class is below. But I just can not initialize the static members of the class and I can get the reason. So the problem is in the MotionDirection.cpp, see the comments for the details about the compiler errors.
MotionDirection.h
#ifndef MOTION_DIRECTION
#define MOTION_DIRECTION
namespace game{
class IntPosition;
class MotionDirection {
private:
IntPosition* positionDisplacement;
float angle;
MotionDirection* returnDirection;
public:
MotionDirection( IntPosition* positionDisplacement, float angle );
void setReturnDirection ( MotionDirection* returnDirection );
IntPosition* getPositionDisplacement();
float getAngle();
MotionDirection* getReturnDirection();
static MotionDirection* NONE;
static MotionDirection* LEFT;
static MotionDirection* RIGHT;
static MotionDirection* UP;
static MotionDirection* DOWN;
static void initStatics();
};
}
#endif
MotionDirection.cpp
#include "MotionDirection.h"
#include "IntPosition.h"
namespace game{
MotionDirection::NONE = new MotionDirection ( new IntPosition( 0, 0), 0.0f );
// here I get an error:
// MotionDirection.cpp:10:5: error: 'NONE' in 'class game::MotionDirection' does not name a type
MotionDirection::MotionDirection( IntPosition* positionDisplacement, float angle ) {
this->positionDisplacement = positionDisplacement;
this->angle = angle;
}
void MotionDirection::setReturnDirection ( MotionDirection* returnDirection ) {
this->returnDirection = returnDirection;
}
IntPosition* MotionDirection::getPositionDisplacement() {
return positionDisplacement;
}
float MotionDirection::getAngle() {
return angle;
}
MotionDirection* MotionDirection::getReturnDirection() {
return returnDirection;
}
void MotionDirection::initStatics () {
MotionDirection::NONE = new MotionDirection ( new IntPosition( 0, 0), 0.0f );
MotionDirection::LEFT = new MotionDirection ( new IntPosition(-1, 0), 180.0f );
MotionDirection::RIGHT = new MotionDirection ( new IntPosition( 1, 0), 0.0f );
MotionDirection::UP = new MotionDirection ( new IntPosition( 0,-1), 90.0f );
MotionDirection::DOWN = new MotionDirection ( new IntPosition( 0, 1), 270.0f );
}
MotionDirection::initStatics();
// or here I get an error:
// MotionDirection.cpp:45:35: error: expected constructor, destructor, or type conversion before ';' token
}
P.S. This is Android-NDK project, I run compilation from the cygwin console.
First error
Just replace
MotionDirection::NONE = new MotionDirection ( new IntPosition( 0, 0), 0.0f );
with
MotionDirection* MotionDirection::NONE = new MotionDirection ( new IntPosition( 0, 0), 0.0f );
Note MotionDirection* type before name of the variable: you need to provide compiler with a type.
Second error
You can't put expressions out of function blocks. There are two ways to do this "right":
1.
Make initStatics() return value.
Add private static variable.
Initialize new static variable by assigning value of initStatics() to it.
2.
Add nested class.
Write call to initStatics() in its constructor.
Add private static variable of that nested class.