React newbie again.
I have and admin page for a forum. It's a tree structure with potentially infinite depth, ordered by order_id field.
I can get it from the api either as a tree (with a children array field holding the children of each node) or as a flat array with an ancestry (a string that holds the whole path to the node, separated by a /) and a parent_id field.
I need to be able to move them around, expand and collapse subtrees and be able to add a node in any place.
Now I have several questions:
Is there a good out-of-the-box UI library to be able to drag-drop them to rearrange? I found this one: https://github.com/frontend-collective/react-sortable-tree but I can't get it to not have a limited height frame, and also customizing those themes looks too complicated.
If I am to do it from scratch using react-dnd, what is the more efficient way of working with this tree - have it as a tree (how do I add/update the nodes?) or store it flat and build the tree on render (easy to manage the collection, but complicates rendering and sorting logic).
This sounds like a complicated and interesting problem. If the library cannot do what you want it to stylistically then you will have to build it from scratch.
I would store all of your node objects in an Array with a children property that holds a reference to all of it's children; you could use their order_id as a reference if it is guaranteed to be unique.
I would also store the parent node's order_id as well, as it will help when you manipulate the tree.
Nodes = [
{order_id: 1, name:"order1", children_ids:[2,3], parent_id: 0},
{order_id: 2, name:"order2", children_ids:[], parent_id: 1},
{order_id: 3, name:"order3", children_ids:[], parent_id: 1},
]
You will have to build the tree on render, it may be a brain twister but it's more than doable with a bit of work.
When you add a new node, you will have to update the parent and children of it's new spot in the tree.
When you rearrange a node, you will have to travel up to the parent and remove it from it's children. You will also have to travel down to it's children and set their parents to the rearranged node's old parent. This removes the node from the tree. Then, add the rearranged node to the tree.
Any manipulation you do will involve manipulating the parent and children of that node - this is why I recommend storing both children's id and the parent's id. It saves you from having to do too many tree traversals which is not computationally efficient.
I don't recommend using an ancestry field as you mentioned. The logic for moving nodes around will be too complicated and you would have to traverse and edit potentially the entire tree if you do it this way.
The first step is to integrate with react-dnd; make each node draggable. When you drop it onto another node to rearrange, you trigger your rearrange handler.
Best of luck.
Related
Is there a way to solidify a SCNNode scale and orientation by recalculating its geometry?
Basically I am loading a SCNNode out of a scn file that was converted from a sketchup file that was exported to a DAE file. Because ARKit works in meters and with a different axis orientation I have to set the loaded SCNNode’s scale (to 0.0254) and eulerangle (x -90deg) to correctly show it. This all works fine however thus scaling and rotation messes up some logic down the line because this logic also uses rotation thus overriding the previous one... :(
I think it would be great if I could simply tell the SCNNode to recalculate its geometry based on its current scale, orientation... (basically its transformation matrix) which would result in an SCNNode with transformation matrix the zero matrix....
If you “simply” want to tell the node to scale its vertices permanently, you will have to write a function to do so as there is no standard option.
That function should read the vertex source of the node’s geometry into an array of vectors, then use GLKMatrix4MultiplyAndProjectVector3 to apply the transformation to each vector, and then create a new SCNGeometry with the new verts as a source.
GLKMatrix4 scalemat = GLKMatrix4MakeScale(aNode.scale.x, aNode.scale.y, aNode.scale.z);
for (HEVertex* vert in toBeTransformedVerts) {
vert.pos = SCNVector3FromGLKVector3(GLKMatrix4MultiplyAndProjectVector3(scalemat, SCNVector3ToGLKVector3(vert.pos)) );
}
//reset node scale property.
aNode.scale = SCNVector3Make(1.0, 1.0, 1.0);
HEVertex is a class I use to store vertices, the pos property is a SCNVector3. In your case you would have to read the vertex source of the .geometry of the SCNNode into an array of SCNVector3 and loop through those instead.
After transforming the position of each vertex, you need to update the node's geometry. I.e. something like this:
SCNGeometrySource *_vertexSource =
[SCNGeometrySource geometrySourceWithVertices:_meshVertices count:_vertexCount];
aNode.geometry = [SCNGeometry geometryWithSources:#[_vertexSource, _aNode.geometry.geometrySources[1], _aNode.geometry.geometrySources[2]] elements:#[_aNode.geometry.geometryElements.firstObject]];
That is just a rough example, it updates only the vertex source, and reuses the normal and color geometry sources and the geometryElement, which can differ highly per model.
Very doable, but not nearly as simple as re-exporting the model with the appropriate size.
If the node doesn't have any subnodes, you can simply parent your node to an empty node, perform the desired transforms on your node, then call flatten on the empty parent node. Your node's center and orientation will match what was the empty parent node. If your node has a tree of child nodes, you'll need to do this recursively.
As an alternative to more complicated approaches you can easily accomplish this with a simple child node...
Create a new node
Move the geometry (and any other node properties you wish) from your original node to the new node
Add the new node as a child of the original node
Apply the scale transform to the child node.
Essentially, after step 3 you should be back exactly where you were before doing anything, except you now have a new child node to apply your scale transforms to, leaving the parent node alone so it can use the identity matrix (or whatever matrix you want.)
A similar approach is to use the existing node as-is...
Create a new parent node
Add it to the same parent as your existing node
Move your existing node from the old parent to this new parent
Copy the transform from your existing child node to the new parent node
Replace the transform on the child node with the scale transform only
Choosing between the two really depends on how much stuff you have on your node that you want to move as well as what transforms it originally had on it. (i.e. if you scale a node, it may be out of place from its original position relative to the model.) You choose which of the above you wish to do based on that information.
That said, if it's just the geometry alone and scaling it in-place works, go with the first. If there are lots of node properties, go with the second. Point being, you're adding a new node specifically to apply the scaling to your existing node, no direct geometry modifications needed.
I am using Extjs 4.2.1. I have a bufferedrenderer tree grid, where the requirement is to append many children to specific node.
Here is my code
Ext.suspendLayouts();
Ext.getCmp('bufferedTree').getStore().suspendEvents();
for(var i=0;i<node.length;i++){
node1.appendChild(node[i]);
nodesArray.push(node[i]);
}
node1.appendChild(nodesArray);
Ext.resumeLayouts(true);
Ext.getCmp('bufferedTree').getStore().resumeEvents();
node is selected nodes to append
node1 is parent to which nodes to be appended.
Its working fine, but the problem is when number of nodes to append increases, time required to appendchild increases.
Any solution to reduce time?
Each node addition is going to trigger a layout update, which is going to have a cost with performance. So the more you append, the greater the cost.
If you look at the docs for appendChild(), you'll notice that you can append either a single node, or an array of nodes. Instead of appending at each iteration of the loop, I would create an array of all the nodes you want to append, and then append them as one call. That way, the layout updates will only happen once, instead of once per node addition. This will almost certainly improve the performance.
I have to solve the "rush hour puzzle" by iterative deepening algorithm. I have read a lot of topics here on stackoverflow and also on the internet. I think that I understand the iterative deepening algorithm. Basically you just go deeper into the tree and try to find the solution.
I figured that I need to create a graph or a tree from the puzzle, but I really don't have an idea how. Also, if I would have the tree, then how would I tell if something is a valid move or a final state?
There were answers that the nodes should be possible moves and the edges are between the nodes that can be reached in one move. I can imagine this, but somehow I'm getting trouble in see how this can be useful or better yet how can this solve the problem.
Please help me, I'm not asking for complete solution or code sample, I just need some easy explanation of the problem.
There is a reason you need to use the deepening algorithm. Imagine you name each car A, B, C, D... The root node of your tree is the initial board state. Now, move car A. You go down one node in the tree. Move car A back. You are at the initial state, but you made two moves to get here, so you are two nodes down the tree. Repeat over and over. You will never hit a final state.
The root node of your tree is the initial board state. Given that node, add a child node to it for every possible valid move. So, each child node will be what the initial tree looks like after one move. Now, for each of those child nodes, do the same thing: make a child node where each node is one move off the original child node.
Eventually, you will hit a solution to the puzzle. When that happens, you print the moves from the root node to the solution child node and quit. This algorithm ensures that you find a solution with the least number of moves.
I know that I can get an array of all the children of a CCSpriteBatchNode by using its children property, but can I get an array easily of just the subset of children that share a common tag?
What I do now is:
Get the array of the children of the batch node
Make a new array for the children with the tag of interest
Iterate through the children, and if the individual child has that tag, add it to the new array
Seems kind of cumbersome so I thought there might be a way to to it easily. If you just want a single child, you can use getChildByTag I think...
That's the way to do it.
However you can (and should) initialize an array with the children that use the same tag in your class, and every time you add a child with that tag you'll also add it to the "childsWithTagX" array. Same for removing. That way you have an always up-to-date separate children array containing only nodes with a given tag.
I think I'll have to add this as a feature to the Kobold2D Roadmap. I needed that a few times already.
I'm trying to check whether a certain node exists beneath a branch of an ExtJS tree. Knowing the ID of the parent node, is there a library function to check whether a node exists beneath the parent (by its ID)?
I've checked the API numerous times over, and can only seem to accomplish this by iterating through the entire branch of the tree.
Is there a library function which allows me to check if a child exists (by its ID) if the parent node ID is known?
Thanks!
PS, to find the parent ID, I'm using the following:
tree.getNodeById('myID');
Ext.tree.TreeNode "contains" function does exactly what you want:
var parent = tree.getNodeById('myID');
parent.contains(tree.getNodeById('childId'));
Have you looked at DomQuery? The API defines the method jsSelect: selects a group of elements.
jsSelect( String selector, [Node/String root] ) : Array
Parameters:
selector : String
The selector/xpath query (can be a comma separated list of selectors)
root : Node/String
(optional) The start of the query (defaults to document).
Returns an Array of DOM elements which match the selector. If there are no matches, and empty Array is returned.