In my application I want to move an object on a path from point to point (based on some event). I want to extract these positions from cocosbuilder file (.ccbi). So how do I extract positions in an array?
One way would be to put sprites on positions and assign them with variables like:
spr1, spr2 spr3
and take the spr1.position in the code.
Another method would be to make an position-animation in cocos-builder. Now extract these keyframes positions in the code. So my question is: "Is there a way to extract positions from the animation keyframes?"
Here is a solution that works with CocosBuilder 2.1.
Add the following function to CCBAnimationManager class:
- (void)enumeratePropertiesForSequence:(NSString*)name Block:(BOOL(^)(CCNode *node, CCBSequenceProperty *seqProp))block
{
int seqId = [self sequenceIdForSequenceNamed:name];
if (seqId == -1)
{
NSLog(#"Sequence %# couldn't be found",name);
return;
}
for (NSValue* nodePtr in nodeSequences)
{
CCNode* node = [nodePtr pointerValue];
NSDictionary* seqs = [nodeSequences objectForKey:nodePtr];
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
// Nodes that have sequence node properties
for (NSString* propName in seqNodeProps)
{
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
if (!block(node, seqProp))
return;
}
}
}
Using this enumerator you may access all available properties, e.g. if you are interested just in positions then:
#import "MyClass.h"
#import "CCBAnimationManager.h"
#import "CCBSequenceProperty.h"
#import "CCBKeyframe.h"
#import "CCNode+CCBRelativePositioning.h"
#implementation MyClass
-(void)fn
{
CCBAnimationManager* animationManager = self.userObject;
BOOL(^block)(CCNode *node, CCBSequenceProperty *seqProp) =
^BOOL(CCNode *node, CCBSequenceProperty *seqProp)
{
NSLog(#"Node tag %d, Prop name [%#], type %d", node.tag, seqProp.name, seqProp.type);
for (CCBKeyframe *kf in seqProp.keyframes)
{
if ([seqProp.name isEqualToString:#"position"])
{
id value = kf.value;
// Get relative position
float x = [[value objectAtIndex:0] floatValue];
float y = [[value objectAtIndex:1] floatValue];
// Get position type
int type = [[[self.userObject baseValueForNode:node propertyName:seqProp.name] objectAtIndex:2] intValue];
CGSize containerSize = [self.userObject containerSize:node.parent];
CGPoint absPos = [node absolutePositionFromRelative:ccp(x,y) type:type parentSize:containerSize propertyName:seqProp.name];
NSLog(#"--- relative position (%f, %f), type %d, abs position (%f, %f)", x, y, type, absPos.x, absPos.y);
}
}
return YES; // YES to continue, NO to stop enumeration
};
[animationManager enumeratePropertiesForSequence:#"MySequence" Block:block];
}
#end
And here is how to instantiate MyClass and to call the enumerator:
CCNode *myclass = [CCBReader nodeGraphFromFile:#"MyClass.ccbi"];
[myclass fn];
I think it is possible, although I havent done that myself.
If you look into a ccb file, you will notice that it is just an XML file.
And this XML file stores only keyframe information (which keyframe happens when).
There is a well-documented structure of the ccb file here.
You might wanna have a look how to get the keyframes info.
Related
I want to select rectangle/polyline through scene with mouse click and should be able to print it's name and other property. It's name and other properties are in the graph node. But I dont want to interact graph again.
So when I was drawing rectangle/polyline through graph co-ordinates, I should be able to store some pointer of graph node on rectangle/polyline so when I will click on rectangle/polyline, then through that pointer I can access it's name and other properties.
Question is `Is this possible ?
Among all above parameters, I want to store only _Ptr ( it is basically a pointer, but store as long, while using it will be type cast )
rect = new myRect();
while(true)
{
for(auto iter = verts.begin();iter != verts.end();++iter)
{
// getting co-ordinates of rectangle
QGraphicsRectItem* rectItem = rect->createRect(co-ordinates of rectangle);
rect->_Ptr = iter->_Ptr; // trying to store _crossRefPtr
}
}
myRect.h
class myRect : public QGraphicsRectItem
{
........
QGraphicsRectItem* createRect(QRectF& rect);
}
And when I will click on rectangle on scene through mouse I am doing like this:
if(_scene->selectedItems().count() != 0)
{
foreach(QGraphicsItem* currentItem, _scene->selectedItems())
{
QGraphicsRectItem* rItem = qgraphicsitem_cast<QGraphicsRectItem*>(currentItem);
if(rItem)
{
myRect* r = reinterpret_cast<myRect*>(rItem);
if(r)
{
Instance (rectangle)
Instance* i = reinterpret_cast<Instance*>(r->_boostRefPtr);
qDebug()<< i->Name();
}
}
But aobve way is wrong. Getting run time errors ( Showing Verbose stack trace)
So the question is :
How to store pointer on QGraphicsItem so that, once they get selected,
that pointer will be accessed ?
Use Qt's dynamic properties, check QObject::setProperty. It should do the trick.
But AFAIC, I would have used a double QMap to associate directly <graph_node, QGraphicsItem> AND <QGraphicsItem, graph_node> - so you can search quickly for both associations, and both in O(log2(n)) complexity. You can store this either as a static part of your graph (better) or standalone (not the best idea). Obviously, if your graph is already in O(log2(n)), you don't need this map and you need only the <QGraphicsItem, graph_node> one.
I have a scene and inside the scene I have the ellipses (circles) to which I change the position with setPos() so when I ask for its position later I will not get 0,0 coordinates, but now when I want to delete the object, the member function contains() does not ever evaluate to true understandably. The question is, how can I get to the scene coordinates or object coordinates so when I click on the object I get the true evaluation of contains() member function. I have tried the mapToScene(), mapFromScene() which do not help. (still kinda lost in Qt coordinate system)
Code sample:
void MyQGraphicsView::mousePressEvent(QMouseEvent * e)
{
QPointF pt = mapToScene(e->pos());
if(e->button()==Qt::RightButton)
{
// remove point
QList<QGraphicsItem *> listIt = scene->items();
QList<QGraphicsItem *>::const_iterator stlIter;
QGraphicsItem * itemToRemove = NULL;
QGraphicsEllipseItem it; // for type checking
for(stlIter = listIt.begin(); stlIter != listIt.end(); ++stlIter)
{
// if it has the expected type and the point is inside (type checking is redundant)
if(((*stlIter)->type() == it.type()) && ((*stlIter)->contains(pt))){
// contains(pt) is never true - understandably
itemToRemove = *stlIter;
break;
}
}
if(itemToRemove != NULL) scene->removeItem(itemToRemove);
}else{ // leftClick to add ellipse
double rad = 10;
QGraphicsEllipseItem* pEllipse = scene->addEllipse(-rad, -rad, rad*2.0, rad*2.0, QPen(Qt::red,0), QBrush(Qt::red,Qt::SolidPattern));
pEllipse->setPos(pt.x(), pt.y()); // set the postion so it does not return 0,0
}
}
The QGraphicsItem::contains method takes points in local coordinates, that is in coordinates with (0, 0) being the center of the QGraphicsItem.
You are using points in global scene coordinates.
To get a point in local coordinates of a given QGprahicsItem you can use the QGraphicsItem::mapFromScene(const QPointF & point) method.
You might want to do something like :
for(Object& obj : objects)
if(obj.contains(obj.mapFromScene(point)))
// do stuf because point is inside obj
Sources :
http://doc.qt.io/qt-4.8/graphicsview.html#item-coordinates
http://doc.qt.io/qt-4.8/qgraphicsitem.html#contains
There is a Geode whose Geometry is a ball with a MatrixTransform() assigned above it. It's callback function makes it falls. When the ball intersects with the ground, I hope to remove it from the scene.
The following code throws exception:
//inside the ball's callback
virtual void operator()(osg::Node* node ,osg::NodeVisitor* nv)
{
using namespace osg;
MatrixTransform* matrix_node = dynamic_cast<MatrixTransform*>(node);
Matrix matrix = matrix_node->getMatrix();
velocity += Vec3(0, 0, -0.002);
matrix.postMultTranslate(velocity);
matrix_node->setMatrix(matrix);
Vec3 now_position = start_position * matrix;
osgUtil::IntersectVisitor ivXY;
osg::ref_ptr<osg::LineSegment> lineXY = new osg::LineSegment(now_position, now_position+velocity);
ivXY.addLineSegment(lineXY);
GAME.main_camera->m_pHostViewer->getSceneData()->accept(ivXY) ;
if(ivXY.hits())
{
node->getParent(0)->removeChild(node);
}
return;
}
How to do it correctly? Thank you!
This is an excerpt from the OpenSceneGraph Group class (from which MatrixTransform inherits):
void Group::traverse(NodeVisitor& nv)
{
for(NodeList::iterator itr=_children.begin();
itr!=_children.end();
++itr)
{
(*itr)->accept(nv);
}
}
bool Group::removeChild( Node *child )
{
unsigned int pos = getChildIndex(child);
if (pos<_children.size()) return removeChildren(pos,1);
else return false;
}
Your code (which is called from traverse) throws an exception probably because the iterator gets invalidated in the middle of the loop, when removeChild is called. To remove the node you would have to wait at least until your node callback returns.
To resolve this I would just use a node mask to control whether the ball is displayed or not. Initially, set the ball node mask to the "visible" value. When the ball hits the ground, set the node mask to the "hidden" value. The node will not be traversed/rendered, but will still be in memory.
If memory is an issue, you can move the code to the parent node or outside the update traversal (e.g. use a modified Viewer::run method).
I have the Image of an arrow in my QML file. I would like to rotate it to a specific position. The position is set by a C++ code.
In the C++ code I have a QThread which change the value of the position every 1 ms. In this situation a previous rotation is not ended (i suppose) and in the display i see an "echo" of the previous position.
I would like to avoid the change of the position value, until the previous rotation is over.
For example: If i set the position to 40 and it takes 3 ms to execute the rotation, in the next 3 ms the C++ code doesn't have to change the position.
QML File:
Item {
id: speedo
property real speedvalue: 20
property real prevspeedvalue: 20
property string numero: "000000000";
property alias tachiText: tachi.text
x:0
y:0
width:1400
height:540
Rectangle
{
x:0
y:0
width:1400
height:540
color: "black"
}
Image {
source:"BigDash_images/sfondo.png"
id:sfondo
x:0 ; y:0
width:1400
height:540
}
Image {
source:"BigDash_images/meter_wheel.png"
id:meter_wheel
x:430 ; y:13
width:539
height:513
}
Text {
text:'02478981'
font.pixelSize:22
color:Qt.rgba(1, 1, 1, 1)
id:tachi
x:627 ; y:390
width:148
height:33
font.letterSpacing : 5
}
Image {
source:"BigDash_images/indicatore.png"
id:indicatore
x:556 ; y:256
width:192
height:143
transform: Rotation {origin.x: 142; origin.y:46; angle:(speedo.speedvalue-20)*1.25}
smooth: true
}
Image {
source:"BigDash_images/color_adjustments.png"
id:color_adjustments
x:0 ; y:0
width:1
height:1
}
}
main.cpp
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QScopedPointer<QApplication> app(createApplication(argc, argv));
QWSDisplay::setTransformation(QTransformedScreen::Rot180, 0);
QDeclarativeView mainView;
mainView.setResizeMode(QDeclarativeView::SizeRootObjectToView);
mainView.setSource(QUrl::fromLocalFile("BigDashboard/BigDash.qml"));
mainView.setAttribute(Qt::WA_AutoOrientation,true);
mainView.showFullScreen();
QObject *object = (QGraphicsObject *)mainView.rootObject();
Threddi th(object);
th.start();
return app->exec();
}
Rotation QThread Code:
Threddi::Threddi(QObject *pobj)
{
m_pView = pobj;
m_pFreccia = m_pView->findChild<QObject *>("indicatore");
}
#define GRANO ((int)1)
void Threddi::run()
{
int verso = GRANO;
int prepos = 20;
int pos = 20;
int numero = 0;
while (1)
{
QThread::msleep(1);
prepos = pos;
pos+=verso;
if (pos>=220)
{
verso = -GRANO;
pos+=verso;
}
else if (pos<=20)
{
verso = GRANO;
pos+=verso;
}
if (m_pView!=NULL)
{
m_pView->setProperty("speedvalue",pos);
}
else
printf("m_pView == NULL!!!!!\n\r");
}
}
Any Ideas?
Shubhanga
I have implemented your suggestion in this way: In the QML File I put a new property named abilitato. onAngleChanged put this property at 1. In the C++ code if abilitato is 1 then abilitato = 0 and change pos. The result is the same as before. I think that anglechanged is emitted when the property is changed and not when the rotation is finished.
c++ code snippet:
QVariant a = m_pView->property("abilitato");
int val = a.toInt();
if (val == 1)
{
m_pView->setProperty("abilitato",0);
m_pView->setProperty("speedvalue",pos);
}
QML File snippet:
property real abilitato: 1
transform: Rotation
{
origin.x: 142;
origin.y:46;
angle:(speedo.speedvalue-20)*1.25
onAngleChanged: speedo.abilitato= 1
}
From your code what I learnt is that you are setting the property
m_pView->setProperty("speedvalue",pos);
which changes the rotation of the Image. When you are doing this, you don't know whether the previous rotation has ended or not. To make sure that the new value of rotation is applied to the image after previous rotation is completed, the decision has to be taken at QML side. So my idea would be as follows (Please note that I have not tried out your code)
1)When you calculate new rotation value for image in the thread, don't set the speedvalue property. Instead store the new speedvaluein a queue.
2)In QML side once the rotation of the image is complete, onAngleChanged: you can read the next value for speedValue from the queue and use it set the new angle. Remember to remove the read value from the queue once you have read it.
Once again, I would like to state that I have not tested the above idea.
I try to clone a CCNode hierarchy, the problems is I need to reset and set all the variable by type, can I have a way to do that more automatically ?
basiclly what I want to do is :
- store a CCNode* (with some child, for example an image at Pos 10-10, and a Label at Pos 100-50 with the text "Test");
- then clone it, for get a new CCNode* with the same default value and childs.
I need to copy it, because after they will get modify, is like a template of Node, before get custom value.
If you know a simple way to copy, and set all the hierarchy (with correct type also), without big if/else statement for each kind of type, it will help me a lot ^^
thanks
This code clones CCNode and all child CCNodes recursively. You can add other subclasses and other properties to copy.
+ (CCNode*) cloneCCNode:(CCNode*)source
{
CCNode* clone = [CCNode node];
for (CCNode* srcSubnode in source.children) {
CCNode* subnode;
if ([srcSubnode isKindOfClass:[CCSprite class]]) { //only CCSprites are copied, add other subclasses if you need to
CCSprite* srcSprite = (CCSprite*)srcSubnode;
subnode = [CCSprite spriteWithTexture:srcSprite.texture];
((CCSprite*)subnode).displayFrame = srcSprite.displayFrame;
} else {
subnode = [self cloneCCNode:srcSubnode];
}
subnode.rotation = srcSubnode.rotation;
subnode.position = srcSubnode.position;
subnode.anchorPoint = srcSubnode.anchorPoint;
subnode.zOrder = srcSubnode.zOrder;
[clone addChild:subnode];
}
return clone;
}