Sets in raphaeljs not real groups? Transform order - raphael

I'm having an issue with sets and how transforms are applied. I'm coming from a graphics background, so I'm familiar with scene graphs as well as the normal SVG group syntax, but Raphael is confusing me. Say I have a circle and a set, on which I want to apply a transform.
circle = paper.circle(0,0.5)
set = paper.set()
If I add the circle first, and then transform, it works.
set.push circle
set.transform("s100,100")
To make a 50 radius circle. If I reverse the order, however,
set.transform("s100,100")
set.push circle
The transform is not applied.
This seems as though it will break many, many rendering and animation type algorithms, where your groups/transforms hold your articulation state, and you add or remove objects to them instead of recreating the entire transform every time. Is there an option somewhere in the documentation that I am not seeing that addresses this, or was this functionality discarded in favor of simplicity? It seems very odd to be missing, given that it is supported directly and easily in the group hierarchy of SVG itself... do I need to manually apply the transform from the set to any children added after the set is transformed?

Sets in Raphael are just simple Arrays.
When you perform some actions on set, Raphael goes through all members via for(...){} loop.
Raphael doesn't support SVG groups <g></g>
UPDATE Raphael's code:
// Set
var Set = function (items) {
this.items = [];
this.length = 0;
this.type = "set";
if (items) {
for (var i = 0, ii = items.length; i < ii; i++) {
if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
this[this.items.length] = this.items[this.items.length] = items[i];
this.length++;
}
}
}
},
As you can see, all items are stored in this.items which is array.

Raphaël's sets are merely intended to provide a convenient way of managing groups of shapes as unified sets of objects, by aggregating element related actions and delegating them (by proxying the corresponding methods in the set level) to each shape sequentially.
It seems very odd to be missing, given that it is supported directly
and easily in the group hierarchy of SVG itself...
Well, Raphaël is not an attempt to elevate the SVG specs to a JavaScript based API, but rather to offer an abstraction for vector graphics manipulation regardless of the underlying implementation (be it SVG in modern browsers, or VML in IE<9). Thus, sets are by no means representations of SVG groups.
do I need to manually apply the transform from the set to any
children added after the set is transformed?
Absolutely not, you only need to make sure to add any shapes to the set before applying transformations.

Related

Retrieving FbxObject3D from an FbxAnimCurveNode

Maya exports all animations to a single FbxAnimStack and FbxAnimLayer. My scene has several objects with independent animations, grouped by top-level objects:
Object_1
this_is_animated_1
this_is_animated_2
Object_2
this_is_animated_3
this_is_animated_4
I'd like to split the single FbxAnimLayer in several layers, one for each Object. My plan is to walk through all FbxAnimCurveNode, get the targeted property, retrieve the FbxObject3D it belongs to, find the top level FbxObject3D and add those curve to the relevant new FbxAnimLayer.
However, I'm stuck: how do I get the targeted property from an animation curve ? FbxAnimCurveNode::GetChannel is private...
for (int i = 0; i < scene->GetSrcObjectCount<FbxAnimCurveNode>() ; ++i) {
auto curve = scene->GetSrcObject<FbxAnimCurveNode>(i);
// Stuck ! How to retrieve the targeted
}
Or maybe my entire approach is wrong ?

Select elements from a list according to their properties

I am developing an application aimed to render a huge number of shapes. Each shape can be assigned to a specific layer.
I get the input data as a list of shapes, where for each shape I have a string property that represents the layer to which the shape belongs.
Now, I need to develop a method that allows me to select (draw) only those shapes that belong to a given list of selected layers.
In pseudo-code:
void draw_if(sorted_list shapes, list<string> selected_layers)
{
for each shape in shapes
{
if (shape.layer in selected_layers)
shape.draw();
}
}
The point is that I would like to perform this operation as fast as possible; therefore I need to choose the right data structures and a proper algorithm.
The list of selected layers is a list of strings (1÷100 different layers), but if needed for performance reasons, it could be converted to other data types.
The shapes are sorted according to their z-order.
Basic intrusive solutions are often overlooked here in search of elaborate data structures and algorithms, but generally are the fastest.
Assuming you have no choice but to keep the selection separate, if you want a really fast solution, store a boolean selection flag in each layer (could be a single bit). When you form a selection, in addition to forming a list, set those flags. Deselecting a layer not only removes it from your selection, but sets that selection flag to false.
Next, turn those strings used to indicate selected layers into indices into a random-access structure (ex: std::vector or even a plain old array if the size can be determined at compile time), like so (simplified):
struct Layer
{
string name;
// Set this to true when the layer is selected, false
// when it is deselected. Use atomics if thread safety
// is required.
bool selected;
};
... and turn shape.layer into an index (or pointer/iterator) to a layer. If you have no choice but to start with a layer string to identify which layer a shape belongs to because you are given string inputs initially (ex: from a file you are loading), then translate those strings into a layer index/pointer/iterator as you are creating the shapes from those string inputs. Use a hash table or at least std::set/map here (the string search on initial shape construction should be logarithmic or better) to convert those layer strings into layer indices/pointers/iterators.
If you need a layer selection list in addition to a layer selection state, then you can do this (pseudocode):
void select(Layer layer, LayerList& layer_selection)
{
if (!layer.selected)
{
layer.selected = true;
layer_selection.insert(&layer);
}
}
void deselect(Layer layer, LayerList& layer_selection)
{
if (layer.selected)
{
layer.selected = false;
layer_selection.erase(&layer);
}
}
... Where your layer selection stores indices/pointers/iterators to layers. Both the select and deselect list insertion/removal can be done in constant-time (even during worst-case) without hashing overhead and while preserving insertion order if you get fancy with the layer selection and use a fixed allocator (this is a complex subject involving placement new, unions, and memory pools so I'll delve into it if desired, but omit it for the time being for brevity).
Now your main pseudocode code turns into something like this:
void draw_if(list shapes, list layers)
{
for each shape in shapes
{
if (layers[shape.layer].selected)
shape.draw();
}
}
... or this if you use pointers/iterators:
void draw_if(list shapes, list layers)
{
for each shape in shapes
{
if (shape.layer->selected)
shape.draw();
}
}
It's hard to beat that in terms of performance as even the most optimal hash table cannot beat a simple indexed array access into memory you would still have to access in addition with the hash. Now if you can consolidate the idea of "selected shapes" and form selected shapes in advance through the process of selecting/deselecting layers, then you can do this:
void draw_selected(list selected_shapes)
{
for each shape in selected_shapes
shape.draw();
}
... which could be even faster provided that the extra cost of forming the selected shapes list is compensated by reusing it repeatedly before it has to change. Note that you still want to convert those strings into indices in this case, because you don't want your "selected shapes" list to have to be anything more than a simple array. To form the selected shapes list:
ShapeList selected_shapes(ShapeList all_shapes, LayerList layers)
{
// Forming this in advance will help if it is reused for
// numerous drawing frames before it needs to change (ex:
// before the Z-order changes, before new elements are inserted
// or existing ones removed, before the layer selection changes).
ShapeList results;
for each shape in all_shapes:
if layers[shape.layer].selected)
results.push_back(shape);
return results;
}
... which is still even cheaper to form and access (due to spatial locality of a perfectly compact shape selection array) than a hash table thanks to that selection state we now store in layers.
This keeps everything cache-friendly and avoids expensive (relatively speaking) data structures like hash tables except during that initial string->index/pointer conversion part (which you only need to do when creating a shape from a string input). In this case, the only place that ever needs to do any kind of search (logarithmic or constant-time hash/trie) is when you convert those shape layer strings you get from user input into indices/pointers/iterators. Everything else is O(1) (even worst-case complexity) and doesn't even require hashing.
I would suggest using a set instead of a list for the selected layers, that way a binary search can be performed to determine in shape.layer is in the selected layers, and insertion while preserving order is quick. Using a list would be inefficient for keeping the order required for binary search.
Another option is to figure out some hashing algorithm and just use a hash map.

Qt: Scale polygons but not children

I am new to Qt. I am representing events from my application as polygons in my scene, using a custom class that inherits QGraphicsPolygomItem. The polygon dimensions are (event duration, fixed height), using 1s : 1px. Event duration can be as low as 1E-6, so I simply scale my view so that the smaller polygon is scaled up to MIN_POLY_WIDTH (10px):
view->scale(qreal(MIN_POLY_WIDTH/min_event_duration), qreal(1.0));
So far so good. However, I have a QGraphicsTextItem child for each polygon, which get stretched by the scale operation to a point they get way outside the polygon boundaries:
The text item is created as follows:
void EventPolygon::setId(QString id) {
if (!this->id) {
this->id = new QGraphicsTextItem(id, this);
} else {
this->id->setPlainText(id);
}
this->id->setPos(0, this->polygon().boundingRect().height() / 2 - this->id->boundingRect().height() / 2);
}
That function is usually called by EventPolygon constructor. I though that was the issue, since scale is done after all items are added to the scene, so it would affect the text items. So I tried calling setId after the scale operation, by iterating over all items in the scene. That way I though only the polygons would be stretched. That was not the case, and the text remained stretched.
I also tried using the following instead of scale:
QTransform t = QTransform();
t.scale(qreal(MIN_POLY_WIDTH/min_event_duration), qreal(1.0));
view->setTransform(t, false);
I thought "false" would avoid the transformation being applied to the polygons children, however it seems that is not the case. Is there any way I can scale the polygons to 10px width min and have a readable text inside them?
The QGraphicsScene forms a scenegraph hierarchy based upon the parent-child relationships, transformations are inherited down this hierarchy - there is no way round this (see the Transformations section here).
So to fix your issue, you will need to make the child QGraphicsTextItem invert the scaling transformation of the parent multiplied by the scaling of your view.
In fact I really recommend that you never set view transformations that are not for simulating a camera operation (pan, zoom, etc.) for this reason. I would simply allow for the seconds per pixel ratio to vary and allow the child items to be able to query this from the view - in other words have the progress items take care of their own size on screen.
That was not the case, and the text remained stretched.
Changing the transformation stack will cause a redraw, that's why it doesn't matter when you set the scale.
I thought "false" would avoid the transformation being applied to the
polygons children
No, the combine argument when false just overrides the existing transformation matrix with the one you are providing.

Raphael Moving Sets

I make a candlestick using two rectangles and making a set.
Then I make 100 candlesticks and make that a set.
When I try to move all 100 candlesticks, they either all move to one coordinate, or all move correctly, but the sticks all move to the edge of the candle.
How can I move the whole set, but retain the inner transforms of the candlesticks?
It is possible to "append" or "prepend" transformations to a set of Raphael objects using the forms "...Tx,y" and "Tx,y..." respectively. In your example, prepending will probably be more functional since you want the absolute movement of the set in the viewbox to occur before any other transformations assigned on a per-element basis.
You should be able to do this or something much like it:
var shift_x = 100, shift_y = 50;
set.attr( { transform: "T" + shift_x + "," + shift_y + "..." } );
You'll notice some elliptical commentary with respect to transform prepend/append in the formal documentation for that function which, sadly, leaves much to be desired.

Positioning Circle Shapes within a Body in Box2D Web

I've had to completely revamp this question as I don't think I was explicit enough about my problem.
I'm attempting to learn the ropes of Box2D Web. I started having problems when I wanted to learn how to put multiple shapes in one rigid body (to form responsive concave bodies). One of the assumptions I made was that this kind of feature would only really be useful if I could change the positions of the shapes (so that I can be in control of what the overall rigid body looked like). An example would be creating an 'L' body with two rectangle shapes, one of which was positioned below and to-the-right of the first shape.
I've gotten that far in so-far-as I've found the SetAsOrientedBox method where you can pass the box its position in the 3rd argument (center).
All well and good. But when I tried to create two circle shapes in one rigid body, I found undesirable behaviour. My instinct was to use the SetLocalPosition method (found in the b2CircleShape class). This seems to work to an extent. In the debug draw, the body responds physically as it should do, but visually (within the debug) it doesn't seem to be drawing the shapes in their position. It simply draws the circle shapes at the centre position. I'm aware that this is probably a problem with Box2D's debug draw logic - but it seems strange to me that there is no online-patter regarding this issue. One would think that creating two circle shapes at different positions in the body's coordinate space would be a popular and well-documented phenomina. Clearly not.
Below is the code I'm using to create the bodies. Assume that the world has been passed to this scope effectively:
// first circle shape and def
var fix_def1 = new b2FixtureDef;
fix_def1.density = 1.0;
fix_def1.friction = 0.5;
fix_def1.restitution = .65;
fix_def1.bullet = false;
var shape1 = new b2CircleShape();
fix_def1.shape = shape1;
fix_def1.shape.SetLocalPosition(new b2Vec2(-.5, -.5));
fix_def1.shape.SetRadius(.3);
// second circle def and shape
var fix_def2 = new b2FixtureDef;
fix_def2.density = 1.0;
fix_def2.friction = 0.5;
fix_def2.restitution = .65;
fix_def2.bullet = false;
var shape2 = new b2CircleShape();
fix_def2.shape = shape2;
fix_def2.shape.SetLocalPosition(new b2Vec2(.5, .5));
fix_def2.shape.SetRadius(.3);
// creating the body
var body_def = new b2BodyDef();
body_def.type = b2Body.b2_dynamicBody;
body_def.position.Set(5, 1);
var b = world.CreateBody( body_def );
b.CreateFixture(fix_def1);
b.CreateFixture(fix_def2);
Please note that I'm using Box2D Web ( http://code.google.com/p/box2dweb/ ) with the HTML5 canvas.
It looks like you are not actually using the standard debug draw at all, but a function that you have written yourself - which explains the lack of online-patter about it (pastebin for posterity).
Take a look in the box2dweb source and look at these functions for a working reference:
b2World.prototype.DrawDebugData
b2World.prototype.DrawShape
b2DebugDraw.prototype.DrawSolidCircle
You can use the canvas context 'arc' function to avoid the need for calculating points with sin/cos and then drawing individual lines to make a circle. It also lets the browser use the most efficient way it knows of to render the curve, eg. hardware support on some browsers.
Since it seems like you want to do custom rendering, another pitfall to watch out for is the different call signatures for DrawCircle and DrawSolidCircle. The second of these takes a parameter for the axis direction, so if you mistakenly use the three parameter version Javascript will silently use the color parameter for the axis, leaving you with an undefined color parameter. Hours of fun!
DrawCircle(center, radius, color)
DrawSolidCircle(center, radius, axis, color)