public void LoadSigma(SigmaGraphModel graph) { nodes = graph.Nodes?.Select(n => new V8NodeModel() { id = n.Id, x = n.X, y = n.Y, size = n.Size }).ToArray() ?? new V8NodeModel[0]; edges = graph.Edges?.Select(e => new V8EdgeModel() { id = e.Id, source = e.Source, target = e.Target }).ToArray() ?? new V8EdgeModel[0]; }
/// <summary> /// Assumes layout has been already applied /// Note: method mutates graph and graphLayout /// </summary> public void ImproveAppliedLayout(SigmaGraphModel graph, GraphLayout graphLayout, LayoutSettings layoutSettings, TimeSpan duration) { using (var engine = new V8ScriptEngine()) { try { var v8Graph = new V8GraphModel(); v8Graph.LoadSigma(graph); engine.AddHostObject("log", _log); engine.Execute("var graph = " + JsonConvention.SerializeObject(v8Graph) + ";"); engine.Execute("var settings = " + JsonConvention.SerializeObject(layoutSettings) + ";"); engine.Execute("var duration = " + JsonConvention.SerializeObject(duration.TotalMilliseconds) + ";"); var baseDirectory = AppDomain.CurrentDomain.BaseDirectory; var forceAtlas2 = engine.Compile(File.ReadAllText($@"{baseDirectory}\App\Graph\Layout\ForceAtlas2.js")); var serviceScript = engine.Compile(File.ReadAllText($@"{baseDirectory}\App\Graph\Layout\GraphLayoutService.js")); engine.Execute(forceAtlas2); engine.Execute(serviceScript); var nodesJson = engine.Evaluate("JSON.stringify(nodes)").ToString(); var nodes = JsonConvention.DeserializeObject <V8NodeModel[]>(nodesJson); for (int i = 0; i < nodes.Length; i++) { var node = graph.Nodes[i]; node.X = nodes[i].x; node.Y = nodes[i].y; var id = node.Entity; if (id.HasValue) { graphLayout[id.Value] = new GraphLayout.Coords() { X = nodes[i].x, Y = nodes[i].y }; } } } catch (ScriptEngineException e) { _log.Error("V8 exception: " + e.ErrorDetails); throw; } } }
private bool ApplyLayout(SigmaGraphModel graph, GraphLayout graphLayout) { var allNodesCovered = true; var rnd = new Random(); foreach (var node in graph.Nodes) { GraphLayout.Coords coords; // should we interpolate vertex position for group by clause if (node.Props != null && node.Props.ContainsKey("members") && graph.Data != null && graph.Data.ContainsKey("grouped_vertices")) { if (InterpolateCoords( node.Props["members"] as HashSet <int>, graph.Data["grouped_vertices"] as List <PropertyVertexModel>, graphLayout, rnd, out coords)) { node.X = coords.X; node.Y = coords.Y; } else { node.X = rnd.NextDouble(); node.Y = rnd.NextDouble(); allNodesCovered = false; } } // no interpolation but layout available else if (node.Entity.HasValue && graphLayout != null && graphLayout.TryGetValue(node.Entity.Value, out coords)) { node.X = coords.X; node.Y = coords.Y; } else { node.X = rnd.NextDouble(); node.Y = rnd.NextDouble(); allNodesCovered = false; } } return(allNodesCovered); }
/// <summary> /// 1) jeśli nie layoutu ma to utwórz nowy layout, synchronicznie odpal layout i dodaj do bazy /// 2) jeśli jest już layout to go wczytaj /// 2a) jeśli któryś z węzłow nie ma współżędnych to synchronicznie odpal layout ale na krótkie 0.5 sekundy /// 2b) zakolejkuj kolejny krok rozłożenia z niskim priorytetem /// </summary> public async Task LayoutGraph(SigmaGraphModel graph, LayoutTask layoutTask, TimeSpan?duration) { if (Guid.Empty.Equals(layoutTask.Query.NetworkId)) { throw new ArgumentNullException(nameof(layoutTask.Query.NetworkId)); } if (graph.Nodes == null) { return; } var networkId = layoutTask.Query.NetworkId; var layout = await _repository.Db.Layouts.SingleOrDefaultAsync(l => l.NetworkId == networkId && l.Key == layoutTask.Query.LayoutKey); // Ad 1 if (layout == null) { lock (LayoutLock) { // prevent entering here from many threads layout = _repository.Db.Layouts.SingleOrDefault( l => l.NetworkId == networkId && l.Key == layoutTask.Query.LayoutKey); if (layout == null) { layout = new Entities.Layout() { Id = Guid.NewGuid(), NetworkId = networkId, Key = layoutTask.Query.LayoutKey }; _repository.Db.Layouts.Add(layout); _repository.Db.SaveChanges(); } } ApplyLayout(graph, null); var graphLayout = new GraphLayout(); ImproveAppliedLayout(graph, graphLayout, layoutTask.Settings, duration ?? _firstTimeLayoutDuration); // save layout layout.GraphLayout = graphLayout; _repository.Db.SaveChanges(); } else // Ad 2 { var graphLayout = layout.GraphLayout; if (!ApplyLayout(graph, graphLayout)) { // ad 2a ImproveAppliedLayout(graph, graphLayout, layoutTask.Settings, duration ?? _missingNodesImproveLayoutDuration); layout.GraphLayout = graphLayout; await _repository.Db.SaveChangesAsync(); } else { // ad 2b if (Background == null) { ImproveAppliedLayout(graph, graphLayout, layoutTask.Settings, duration ?? _missingNodesImproveLayoutDuration); layout.GraphLayout = graphLayout; await _repository.Db.SaveChangesAsync(); } else { _log.Debug("Scheduling background layout"); if (layoutTask.ModifyLayout) { Background.Execute(new ImproveLayoutCommand() { Task = layoutTask, Duration = duration ?? _improveLayoutInBackgroundDuration }, new BackgroundPrincipal(Auth.UserName)); } } } } }