How to emit() only complete path or terminated by repeat loop? - gremlin
My Graph looks like above image. It is simplified form of optical communication network. each vertex transmits one color. when two colors joins ("Mux") then it creates new color and transmits new light. At remote end colours are demuxed and passed to corresponding vertices.
Mux,demux and transmit("straight") are captured as edge. New colors are formed as per vent diagram.
If I start from one color (ex. "Red"), then I have to traverse only through edge which have red (R1,R2) or red shade (D1,D2,P1).
For Example, If I start from R1, then I can traverse [R1,D1,D2,P1,R2]. it has another path [R1,D1,D2,P1,K1,R2] But it is not valid. As K1 doesn't have red shade.
Below query works well. I store the color while traversing through "mux" edge. And use this color to pick demux edge.
g.V().hasLabel("R1").
repeat(choose(values("type")).
option("mux", aggregate(local, "colors").by("color").inV()).
option("demux", filter(values("color").as("c").
where("c", new P(new BitiseAndPredicate(), "colors")).
by().
by(unfold().limit(1))
).inV()).
option("stright", __.inV()).simplePath()).
until(hasLabel("R2")).
path().by(label()).toList();
For Input "R1" to "R2" it works fine. [R1,tx,D1,tx,D2,tx,P1,tx,R2]
For "Y1" to "Y2" it returns empty. It is correct , As there is no edge from G1 to Y1.
I want to make changes to query. If it is not able to reach destination then print till where it reached .In this case [Y1,tx,D1,tx,D2,tx,G1]
I used emit(). But it prints all path combination along the path.
g.V().hasLabel("R1").
repeat(choose(values("type")).
option("mux", aggregate(local, "colors").by("color").inV()).
option("demux", filter(values("color").as("c").
where("c", new P(new BitiseAndPredicate(), "colors")).
by().
by(unfold().limit(1))
).inV()).
option("stright", __.inV()).simplePath()).
until(hasLabel("R2")).emit().
path().by(label()).toList();
If it is simple traversal then I can use emit(out().count().is(eq('0'))). But in this case last edge have more edges, but not it is matching with my "mux & demux" condition. I can't reuse the same condition in "repeat" step. Actual implementation will be more complex.
Is there any way to emit path if until condition met or repeat step doesn't return any vertex.
Here is Java code
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.util.function.Lambda;
import org.janusgraph.core.*;
import org.janusgraph.core.schema.JanusGraphManagement;
import java.util.ArrayList;
import java.util.List;
import static org.apache.tinkerpop.gremlin.process.traversal.P.eq;
import static org.apache.tinkerpop.gremlin.process.traversal.P.without;
import static org.apache.tinkerpop.gremlin.process.traversal.Scope.local;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.*;
import static org.janusgraph.core.Multiplicity.MULTI;
public class ColourTraversal
{
public static void main( String[] args )
{
//External db
GraphTraversalSource g =null;
//inmemory db
JanusGraph graph = JanusGraphFactory.build().set("storage.backend", "inmemory").open();
g = graph.traversal();
try
{
populateData(g);
g.tx().commit();
printPath(g, "R1", "R2");
System.out.println();
printPath(g, "Y1", "Y2");
g.close();
} catch (Exception e)
{
e.printStackTrace();
}
}
private static void printPath(GraphTraversalSource g, String start, String end)
{
List<Path> pathList = g.V().hasLabel(start).
repeat(repeatStep().simplePath()).
emit(hasLabel(end)).
path().by(label()).toList();
for (Path path : pathList)
{
for (int i = 0; i < path.size(); i++)
{
String label = path.get(i);
System.out.println(label);
}
}
}
private static GraphTraversal<?, Vertex> repeatStep()
{
GraphTraversal<?, ?> repeatStepTrav =
outE("tx").choose(values("type")).
option("mux", aggregate(local, "colors").by("color").inV()).
option("demux", filter(values("color").as("c").
where("c", new P(new BitiseAndPredicate(), "colors")).
by().
by(unfold().limit(1))
).inV()).
option("stright", __.inV());
return (GraphTraversal<Vertex, Vertex>) repeatStepTrav;
}
private static void populateData(GraphTraversalSource g)
{
Vertex r1 = g.addV("R1").next();
Vertex r2 = g.addV("R2").next();
Vertex d1 = g.addV("D1").next();
Vertex d2 = g.addV("D2").next();
Vertex g1 = g.addV("G1").next();
Vertex b1 = g.addV("B1").next();
Vertex b2 = g.addV("B2").next();
Vertex b3 = g.addV("B3").next();
Vertex y1 = g.addV("Y1").next();
Vertex p1 = g.addV("P1").next();
addEdge(g, r1, d1, "mux", 4);
addEdge(g, y1, d1, "mux", 2);
addEdge(g, b1, d1, "mux", 1);
addEdge(g, d1, d2, "stright", 7);
addEdge(g, g1, d2, "mux", 3);
addEdge(g, p1, d2, "mux", 5);
addEdge(g, b2, g1, "mux", 1);
addEdge(g, b3,p1, "mux", 1);
addEdge(g, r2,p1, "mux", 4);
addEdge(g, d1, r1, "demux", 4);
addEdge(g, d1, y1, "demux", 2);
addEdge(g, d1, b1, "demux", 1);
addEdge(g, d2, d1, "stright", 7);
addEdge(g, d2,g1, "demux", 3);
addEdge(g, d2,p1, "demux", 5);
addEdge(g, g1,b2, "demux", 1);
addEdge(g, p1,b3, "demux", 1);
addEdge(g, p1,r2, "demux", 4);
Vertex k1 = g.addV("K1").next();
addEdge(g, p1, k1, "demux", 0);
addEdge(g, k1, r2, "demux", 0);
addEdge(g, k1, p1, "mux", 0);
addEdge(g, r2, k1, "mux", 0);
// Vertex y2 = g.addV("Y2").next();
// addEdge(g, y2, g1, "mux", 2);
// addEdge(g, g1,y2, "demux", 2);
}
private static void addEdge(GraphTraversalSource g, Vertex source, Vertex destination,String type,int color) {
GraphTraversal t = g.V(source).addE("tx").to(destination).property("type", type).property("color", color);
t.next();
}
}
import java.util.function.BiPredicate;
public class BitisetAndPredicate implements BiPredicate {
#Override
public boolean test(Object o, Object o2) {
int left = (int) o;
int right = (int) o2;
return (left & right) == right;
}
}
Instead of using the emit() step you can add a disjunct condition to the until() step, testing for paths that have no further out edges.
The session below in the gremlin console using the tinkerpop-modern sample graph shows how:
graph = TinkerFactory.createModern()
g = graph.traversal()
// Your old query
gremlin> g.V(1).repeat(out()).until(has("name", "ripple")).path()
==>[v[1],v[4],v[5]]
// A query with additional condition to stop the repeat
gremlin> g.V(1).repeat(out()).until(or(has("name", "ripple"), out().count().is(eq(0)))).path()
==>[v[1],v[3]]
==>[v[1],v[2]]
==>[v[1],v[4],v[5]]
==>[v[1],v[4],v[3]]
Related
setVertexCount API of QGeometryRenderer and its effect on ray casting results
I create a wireframe mesh of two lines between three points: By these functions: Qt3DRender::QGeometryRenderer *Utils::createWireframeMesh() { Qt3DRender::QGeometryRenderer *mesh = new Qt3DRender::QGeometryRenderer(); Qt3DRender::QGeometry *geometry = new Qt3DRender::QGeometry(mesh); Qt3DRender::QBuffer *vertexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::VertexBuffer, geometry); Qt3DRender::QBuffer *indexDataBuffer = new Qt3DRender::QBuffer(Qt3DRender::QBuffer::IndexBuffer, geometry); QByteArray vertexBufferData; QByteArray indexBufferData; int vertexCount = 3; // Three vertices at (0, -1, 0) and (1, 0, 0) and (0, 1, 0) int lineCount = 2; // Two lines between three vertices vertexBufferData.resize(vertexCount * 3 * sizeof(float)); indexBufferData.resize(lineCount * 2 * sizeof(ushort)); // Arrow triangle is 2D and is inside XY plane float *vPtr = reinterpret_cast<float *>(vertexBufferData.data()); vPtr[0] = 0.0f; vPtr[1] = -1.0f; vPtr[2] = 0.0f; // First vertex at (0, -1, 0) vPtr[3] = 1.0f; vPtr[4] = 0.0f; vPtr[5] = 0.0f; // Second vertex at (1, 0, 0) vPtr[6] = 0.0f; vPtr[7] = +1.0f; vPtr[8] = 0.0f; // Third vertex at (0, 1, 0) ushort *iPtr = reinterpret_cast<ushort *>(indexBufferData.data()); iPtr[0] = 0; iPtr[1] = 1; // First line from index 0 to index 1 iPtr[2] = 1; iPtr[3] = 2; // Second line from index 1 to index 2 vertexDataBuffer->setData(vertexBufferData); indexDataBuffer->setData(indexBufferData); addPositionAttributeToGeometry(geometry, vertexDataBuffer, vertexCount); addIndexAttributeToGeometry(geometry, indexDataBuffer, lineCount * 2); mesh->setInstanceCount(1); mesh->setIndexOffset(0); mesh->setFirstInstance(0); // How to set vertex count here? mesh->setVertexCount(vertexCount); mesh->setPrimitiveType(Qt3DRender::QGeometryRenderer::Lines); mesh->setGeometry(geometry); return mesh; } void Utils::addPositionAttributeToGeometry(Qt3DRender::QGeometry *geometry, Qt3DRender::QBuffer *buffer, int count) { Qt3DRender::QAttribute *posAttribute = new Qt3DRender::QAttribute(); posAttribute->setAttributeType(Qt3DRender::QAttribute::VertexAttribute); posAttribute->setBuffer(buffer); posAttribute->setDataType(Qt3DRender::QAttribute::Float); posAttribute->setDataSize(3); posAttribute->setByteOffset(0); posAttribute->setByteStride(0); posAttribute->setCount(count); posAttribute->setName(Qt3DRender::QAttribute::defaultPositionAttributeName()); geometry->addAttribute(posAttribute); } void Utils::addIndexAttributeToGeometry(Qt3DRender::QGeometry *geometry, Qt3DRender::QBuffer *buffer, int count) { Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute(); indexAttribute->setAttributeType(Qt3DRender::QAttribute::IndexAttribute); indexAttribute->setBuffer(buffer); indexAttribute->setDataType(Qt3DRender::QAttribute::UnsignedShort); indexAttribute->setDataSize(1); indexAttribute->setByteOffset(0); indexAttribute->setByteStride(0); indexAttribute->setCount(count); geometry->addAttribute(indexAttribute); } In above code, I tried three different statements at this line: // How to set vertex count here? mesh->setVertexCount(vertexCount); mesh->setVertexCount(vertexCount * 2); mesh->setVertexCount(vertexCount * 3); With these results - I do some ray casting in my 3D scene which are surprisingly affected too: Documentation explains vertexCount property of Qt3DRender::QGeometryRenderer as: vertexCount : int Holds the primitive count. In my case, primitive count is line count, so I tried it but only one line is drawn: I'm confused about setVertexCount API. Can anybody give me a hint?
vertexCount is the same value that you would pass to glDrawArrays or glDrawElements, ie it's the number of vertices involved in the drawing. Since you're using indexed rendering, that would typically be the number of indexes (assuming you're drawing all in data in the index array). So in the case above, it should be 4. Please note we recently fixed a bug with line picking when using primitive restart, but that doesn't affect the code you included above.
2D array traversal to get distinct 7 digit number combos
I ran into a tricky question from an interview prep book which goes.. You have a 3 by 3 matrix containing integers 1 to 9 as shown below 1 2 3 4 5 6 7 8 9 How do you get unique 7 digit number combos with the first numbers all starting with 4 (matrix[1][0]). The traversal is meant to be like that of a rook on a chess board.. 1 way either horizontally or vertically...(Having 4125874 is valid 7 digit combo btw). I tried writing some code and doing regular 2D matrix traversal with a boolean visited flag here to get an answer and storing each combo in a hashSet to ensure uniqueness but I am stuck. Any kind comments, hints and code revisions to get me code working would be appreciated. class Ideone { void dfs(int[][] matrix, boolean visited) //considered dfs with a boolean visited flag but I am stuck. I want to make my solution recursive { boolean visited = false; } public static HashSet<String> get7DigitCombo(int[][] matrix) { String result = ""; int[][] cache = matrix.clone(); Set<String> comboSet = new HashSet<String>(); boolean visited = false; int resultStart = matrix[1][0]; for(int row = 1; row < matrix.length; row++) { for(int col = 0; col < matrix[0].length; col++) { if (visited == false & result.length < 7) { result += "" + (matrix[row + 1][col] || matrix[row -1][col] || matrix[row][col+1] || matrix[row][col-1]); } } } comboSet.add(result); return comboSet; } public static void main (String[] args) throws java.lang.Exception { // your code goes here int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, }; HashSet<String> comboSet = get7DigitCombo(matrix); System.out.print(comboSet); } }
The following mcve demonstrates recursively getting neighbors and accumulating then into unique combinations. The code is documented with comments: import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; class Ideone { private static final int SIZE = 7; //size of combo private static int[][] directions = { //represents moving directions {-1, 0}, //up { 0,-1}, //left { 0, 1}, //right { 1, 0} //down }; public static void main (String[] args) throws java.lang.Exception { int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, }; Set<String> comboSet = get7DigitCombo(matrix); System.out.print(comboSet.size()); } public static Set<String> get7DigitCombo(int[][] matrix) { Set<String> comboSet = new HashSet<>(); get7DigitCombo(1, 0, matrix, String.valueOf(matrix[1][0]), comboSet); return comboSet; } //recursively get all neighbors. generate combos by appending each neighbor //combo represents a single combination. combos accumulates combination static void get7DigitCombo(int row, int col, int[][] matrix, String combo, Set<String> combos){ if(combo !=null && combo.length() == SIZE) { //when combo reached the right size, add it //System.out.println(combo); combos.add(combo); return; } //get and iterate over all adjacent neighbors for(int[] neighbor : getNeighbors(row, col, matrix)){ get7DigitCombo(neighbor[0], neighbor[1], matrix, combo+neighbor[2], combos); } } //return list of adjacent neighbors. each neighbor is represented by //int[3]: row, column, value private static List<int[]> getNeighbors(int row, int col, int[][] matrix) { List<int[]> neighbors = new ArrayList<>(); for(int[] dir : directions){ int newRow = row + dir[0] ; int newCol = col + dir[1]; if(isValidAddress(newRow, newCol, matrix)) { neighbors.add( new int[]{newRow,newCol, matrix[newRow][newCol]}); } } return neighbors; } private static boolean isValidAddress(int row, int col, int[][] matrix) { if(row < 0 || col < 0) return false; if(row >= matrix.length || col >= matrix[row].length) return false; return true; } }
This is a pacman problem. You must look for or define the neighbors of each matrix value. Then cross the matrix fallowing the neighbors of each matrix value. It usually resolves with recursive functions. I think you code must be change from the ground using a different approach.
Qt - QCustomPlot painting graph
I have a problem with drawing the graph in QCustomPlot library. I would like to draw a logarithm graph but I use drawing on the interval <-3;3>. Because logarithm is not defined from -3 to 0, I tried to do nothing while drawing on this interval. I have this code: QVector<double> x(10001), y(10001); QVector<double> x1(10001), y1(10001); double t=-3; //cas double inkrement = 0.0006; for (int i=0; i<10001; i++)//kvadraticka funkcia { x[i] = t; y[i] = (-1)*t*t-2; t+=inkrement; } int g=0; for(double l=-3;l<3; l+=inkrement) { if(l<=0.0) continue; else { //QMessageBox::warning(this, tr("note"), tr("l=%1\n").arg(l), QMessageBox::Ok); x1[g] = l; y1[g] = log10(l)/log10(exp(1.0)); //QMessageBox::warning(this, tr("note"), tr("x1=%1\ny1=%2").arg(x1[g]).arg(y1[g]), QMessageBox::Ok); //break; g++; } } customPlot->addGraph(); customPlot->graph(0)->setData(x, y); customPlot->addGraph(); customPlot->graph(1)->setData(x1, y1); customPlot->xAxis->setLabel("x"); customPlot->yAxis->setLabel("y"); customPlot->xAxis->setRange(-3, 3); customPlot->yAxis->setRange(-10, 5); customPlot->replot(); where x1 and y1 are QVectors... But the graph is like the first point is in [0,0]. So I have then a line that connects point [0,0] with the logarithm graph and I dont know why :( When I put l=0.0006 before the cycle, everything is OK. Can you help me with that please?
It seems that you set count of x1 and y1 before this loop. QVector is initialized with zeros. So if you don't set any value for some items then x1 and y1 will contain zero values at their end. You should use empty QVector's and add new values if g is OK: QVector<double> x1, y1; //... x1 << l; y1 << log10(l)/log10(exp(1.0)); g variable can be removed then. And I think it's better to remove i variable and use for(double l = -3; l <= 3; l+=increment) loop.
Entity look at for cameras and objects
I'm currently extending my 3D-engine for a better entity system which includes cameras. This allows me to put cameras into parent entities which may also be in another entity ( and so on... ). --Entity ----Entity ------Camera Now i want to set the cameras look direction, i'm doing this with the following method which is also used to set the look at of a entity: public void LookAt(Vector3 target, Vector3 up) { Matrix4x4 oldValue = _Matrix; _Matrix = Matrix4x4.LookAt(_Position, target, up) * Matrix4x4.Scale(_Scale); Vector3 p, s; Quaternion r; Quaternion oldRotation = _Rotation; _Matrix.Decompose(out p, out s, out r); _Rotation = r; // Update dependency properties ForceUpdate(RotationDeclaration, _Rotation, oldRotation); ForceUpdate(MatrixDeclaration, _Matrix, oldValue); } But the code is only working for cameras and not for other entities, when using this method for other entities the object is rotating at it's position ( The entity is at a root node, so it has no parent ). The matrix's look at method looks like this: public static Matrix4x4 LookAt(Vector3 position, Vector3 target, Vector3 up) { // Calculate and normalize forward vector Vector3 forward = position - target; forward.Normalize(); // Calculate and normalie side vector ( side = forward x up ) Vector3 side = Vector3.Cross(up, forward); side.Normalize(); // Recompute up as: up = side x forward up = Vector3.Cross(forward, side); up.Normalize(); //------------------ Matrix4x4 result = new Matrix4x4(false) { M11 = side.X, M21 = side.Y, M31 = side.Z, M41 = 0, M12 = up.X, M22 = up.Y, M32 = up.Z, M42 = 0, M13 = forward.X, M23 = forward.Y, M33 = forward.Z, M43 = 0, M14 = 0, M24 = 0, M34 = 0, M44 = 1 }; result.Multiply(Matrix4x4.Translation(-position.X, -position.Y, -position.Z)); return result; } The decompose method also returns the wrong value for the position variable p. So why is the camera working and the entity not?
I had a similar problem a while ago.The problem is that camera transformations are different from others as those are negated.What I did was first to transform camera eye, center and up into world space (transforming those vectors by its parent model matrix )and then calculating the lookAt() .That is how it worked for me.
Path planning to get close to an unreachable target
I'm working on a game which has tank battles on a tiled map. If a tank is on a cell, that cell is considered unpassable in the A* algorithm, therefore, whenever an unit needs to attack another, I need to plan a path which brings the attacker into range (if range=1, then next to the target). Currently, I use an iterative approach with increasing radius to find a path to a nearby cell and choose a cell which minimizes the A-Cell-B distance. Unfortunately, this is slow for one unit, not to mention for 50 units. Is there a way to extract a partial path from a regular A* search data structures? Just for reference, here is the implementation I have. Set<T> closedSet = U.newHashSet(); Map<T, T> cameFrom = U.newHashMap(); final Map<T, Integer> gScore = U.newHashMap(); final Map<T, Integer> hScore = U.newHashMap(); final Map<T, Integer> fScore = U.newHashMap(); final Comparator<T> smallestF = new Comparator<T>() { #Override public int compare(T o1, T o2) { int g1 = fScore.get(o1); int g2 = fScore.get(o2); return g1 < g2 ? -1 : (g1 > g2 ? 1 : 0); } }; Set<T> openSet2 = U.newHashSet(); List<T> openSet = U.newArrayList(); gScore.put(initial, 0); hScore.put(initial, estimation.invoke(initial, destination)); fScore.put(initial, gScore.get(initial) + hScore.get(initial)); openSet.add(initial); openSet2.add(initial); while (!openSet.isEmpty()) { T current = openSet.get(0); if (current.equals(destination)) { return reconstructPath(cameFrom, destination); } openSet.remove(0); openSet2.remove(current); closedSet.add(current); for (T loc : neighbors.invoke(current)) { if (!closedSet.contains(loc)) { int tentativeScore = gScore.get(current) + distance.invoke(current, loc); if (!openSet2.contains(loc)) { cameFrom.put(loc, current); gScore.put(loc, tentativeScore); hScore.put(loc, estimation.invoke(loc, destination)); fScore.put(loc, gScore.get(loc) + hScore.get(loc)); openSet.add(loc); Collections.sort(openSet, smallestF); openSet2.add(loc); } else if (tentativeScore < gScore.get(loc)) { cameFrom.put(loc, current); gScore.put(loc, tentativeScore); hScore.put(loc, estimation.invoke(loc, destination)); fScore.put(loc, gScore.get(loc) + hScore.get(loc)); Collections.sort(openSet, smallestF); } } } } return Collections.emptyList();
A solution that seems to work (replacing the last return Collections.emptyList();): // if we get here, there was no direct path available // find a target location which minimizes initial-L-destination if (closedSet.isEmpty()) { return Pair.of(false, Collections.<T>emptyList()); } T nearest = Collections.min(closedSet, new Comparator<T>() { #Override public int compare(T o1, T o2) { int d1 = trueDistance.invoke(destination, o1); int d2 = trueDistance.invoke(destination, o2); int c = U.compare(d1, d2); if (c == 0) { d1 = trueDistance.invoke(initial, o1); d2 = trueDistance.invoke(initial, o2); c = U.compare(d1, d2); } return c; } }); return Pair.of(true, reconstructPath(cameFrom, nearest)); Where the trueDistance gives the eucleidian distance of two points. (The base algorithm uses a simpler function yielding 1000 for X-X or YY neightbor, 1414 for XY neighbor).