static FiniteStateMachineLayoutScore() { Perfect = new FiniteStateMachineLayoutScore(); Perfect.AnalyzeData(); Worst = new FiniteStateMachineLayoutScore(); Worst.AnalyzeData(); Worst.Penalty = Double.MinValue; }
/* * private Dictionary<int, double> GetStatesTooCloseToEdge(Dictionary<int, Point> layout, * int stateId1, int stateId2, double threshold) * { * if (stateId1 == stateId2) * return null; * * if (!transitions.Any(x => x.Item1 == stateId1 && x.Item3 == stateId2)) * return null; * * var p1 = layout[stateId1]; * var p2 = layout[stateId2]; * * Dictionary<int, double> results = null; * foreach (var key in layout.Keys) * { * if (key == stateId1 || key == stateId2) * continue; * * double dist = layout[key].DistanceToLine(p1, p2); * * if (dist < threshold) * { * if (results == null) * results = new Dictionary<int, double>(); * results.Add(key, dist); * } * } * return results; * } */ /// <summary> /// Returns a score of a given layout. /// /// This score is relative, and unless it is zero (perfect score), /// there needs to be another layout for comparison in order for any conclusions to be drawn. /// Zero means that if the machine is drawn using this layout, it will be very easy for human eye /// to analyse it, and that humans in generally will think of it as a clear and understandable drawing. /// For example, you can receive -1000, and think this is bad, but new layout may receive -10^6. /// Also if you receive -0.001 yo may think this is good, but new layout can still receive -10^-6. /// /// Score is based on number of intersecting edges, number of nodes that lay on some edge, /// number of nodes that are very close to each other, etc. /// </summary> /// <returns>zero if layout is perfect, negative value if it is not</returns> private FiniteStateMachineLayoutScore CalculateScore(Dictionary <int, Point> layoutVertices) { FiniteStateMachineLayoutScore currentScore = null; if (layoutVertices == null || layoutVertices.Count <= 1) { return(FiniteStateMachineLayoutScore.Perfect); } foreach (var key1 in layoutVertices.Keys) { foreach (var key2 in layoutVertices.Keys) { if (key1 == key2) { continue; } var p1 = layoutVertices[key1]; var p2 = layoutVertices[key2]; #region state on state // check if point are too close to each other if (key1 < key2) { double dist = p1.Distance(p2); if (dist < MinimumStateDistance) { //nodesDistanceScore -= Math.Sqrt(50 - dist); if (currentScore == null) { currentScore = new FiniteStateMachineLayoutScore(); } currentScore.VerticesOnVertices.Add(new Tuple <int, int, double>(key1, key2, dist)); } } #endregion // check connected vertices int transitionIndex = transitions.IndexOf(x => x.InitialStateId == key1 && x.ResultingStateId == key2); if (transitionIndex == -1) { continue; } #region state on edge // check if any state is obstructed by the edge foreach (var key in layoutVertices.Keys) { if (key == key1 || key == key2) { continue; } double dist = layoutVertices[key].DistanceToLine(p1, p2); if (dist < 30) { if (currentScore == null) { currentScore = new FiniteStateMachineLayoutScore(); } currentScore.VerticesOnEdges.Add(new Tuple <int, int, double>(key, transitionIndex, dist)); } } #endregion // check if the edge intersects with any other edge foreach (var keyOther1 in layoutVertices.Keys) { foreach (var keyOther2 in layoutVertices.Keys) { if (keyOther1 == keyOther2) { continue; } if (keyOther1 == key1 || keyOther2 == key2 || keyOther1 == key2 || keyOther2 == key1) { continue; } int transitionIndexOther = transitions.IndexOf( x => x.InitialStateId == keyOther1 && x.ResultingStateId == keyOther2); if (transitionIndexOther == -1) { continue; } var pOther1 = layoutVertices[keyOther1]; var pOther2 = layoutVertices[keyOther2]; if (p1.Intersects(p2, pOther1, pOther2)) { if (currentScore == null) { currentScore = new FiniteStateMachineLayoutScore(); } currentScore.IntersectingEdges.Add(new Tuple <int, int>(transitionIndex, transitionIndexOther)); } } } } } return(currentScore == null ? FiniteStateMachineLayoutScore.Perfect : currentScore); }
/// <summary> /// /// </summary> /// <param name="sessionId"></param> /// <param name="currentSessionId"></param> /// <exception cref="ApplicationException">when at least one of the layout threads fails</exception> /// <returns></returns> public bool Create(int sessionId, ref int currentSessionId) { int workGroupSize = 8; var layoutsAll = new List <KeyValuePair <Dictionary <int, Point>, FiniteStateMachineLayoutScore> >(); int bestLayout = 0; double defaultDistanceRatio = MinimumStateDistance / 72; for (int i = 0; i < 32; ++i) { var layoutsScores = new FiniteStateMachineLayoutScore[workGroupSize]; var layouts = new Dictionary <int, Point> [workGroupSize]; try { // try several layouts in parallel Parallel.For(0, workGroupSize, (int n) => { IDictionary <string, Point> createdVertices = null; //if (n % 2 == 0) createdVertices = Create4(); //else // createdVertices = Create6(); var layout = new Dictionary <int, Point>(); var firstCreatedVertex = createdVertices[vertexLabels[0]]; double minX = firstCreatedVertex.X, minY = firstCreatedVertex.Y; foreach (var pos in createdVertices) { var location = pos.Value; double x = location.X * defaultDistanceRatio; double y = location.Y * defaultDistanceRatio; if (x < minX) { minX = x; } if (y < minY) { minY = y; } layout.Add(int.Parse(pos.Key), new Point(x, y)); } minX -= LayoutOffset + 1; minY -= LayoutOffset + 1; //bool invalid = false; for (int key = 0; key < layout.Count; ++key) { layout[key] = new Point(layout[key].X - minX, layout[key].Y - minY); //if (layout[key].X < LayoutOffset || layout[key].Y < LayoutOffset) invalid = true; } var score = CalculateScore(layout); score.AnalyzeData(); //if (invalid) // score = FiniteStateMachineLayoutScore.Perfect; //else // score = FiniteStateMachineLayoutScore.Worst; layoutsScores[n] = score; layouts[n] = layout; }); } catch (AggregateException e) { //foreach (var exception in e.InnerExceptions) //{ // System.Console.Out.WriteLine("one of the layout jobs failed:"); // System.Console.Out.WriteLine(exception.ToString()); //} throw new ApplicationException("at least one of the layout finding threads caused an error", e); } if (sessionId != currentSessionId) { return(false); } int newBestLayout = 0; for (int j = 1; j < workGroupSize; ++j) { if (layoutsScores[j] == FiniteStateMachineLayoutScore.Perfect) { newBestLayout = j; break; } if (layoutsScores[j].CompareTo(layoutsScores[newBestLayout]) > 0) { newBestLayout = j; } } layoutsAll.Add(new KeyValuePair <Dictionary <int, Point>, FiniteStateMachineLayoutScore>( layouts[newBestLayout], layoutsScores[newBestLayout])); if (layoutsScores[newBestLayout] == FiniteStateMachineLayoutScore.Perfect) { bestLayout = layoutsAll.Count - 1; break; } if (layoutsScores[newBestLayout].CompareTo(layoutsAll[bestLayout].Value) > 0) { bestLayout = layoutsAll.Count - 1; } } vertices = layoutsAll[bestLayout].Key; layoutScore = layoutsAll[bestLayout].Value; CreateEdges(); return(true); }