// 2013-11-08 TRS: First commit with basic functionality. Uses GraphViz // for rendering graphs to human-viewable form. Only minimal // processing is implemented, i.e. scale timestamp-based weights // into [0.0, 1.0] and remove links lighter than 1%. // 2013-11-13 TRS: Adjusted processing so that new links are initialized // to t^2 and repeat links get t added. Postprocessing now scales and // applies a highpass filter at 90%. Also, filenames are now inlcuded // in the nodes; thanks Braden. // TODO: // - Make the DOT files prettier, e.g. different node styles for // different types. // - Improve/tune postprocessing. // - Add functional-style utilities (I love lisps). // Including classes tends to blow everyone else // away since classes contain everything but get // parsed in the same way; including comments and, // to a lesser extent, attributes makes the // graph noisy. // private static SourceCodeEntityType[] EXCLUDED_TYPES = { // SourceCodeEntityType.COMMENT, // SourceCodeEntityType.CLASS, // //SourceCodeEntityType.ATTRIBUTE, // //SourceCodeEntityType.METHOD, // }; public static Dictionary<EntityLink, double> gen_graph( GazeResults gaze_results, SourceCodeEntitiesFileCollection collection) { Dictionary<EntityLink,double> src2src_links = new Dictionary<EntityLink,double> (); HashSet <SourceCodeEntity> previous = null; HashSet <SourceCodeEntity> current = new HashSet <SourceCodeEntity>(); foreach (GazeData gaze_data in gaze_results.gazes) { // find out which SourceCodeEntity the subject // was looking at for this GazeData foreach (SourceCodeEntitiesFile file in collection) { if (gaze_data.filename != file.FileName) { continue; } foreach (SourceCodeEntity entity in file) { // if this GazeData looks at an ignored type, skip // if (((IList<SourceCodeEntityType>) EXCLUDED_TYPES).Contains( // entity.Type)) { // continue; // } // Sorry about the ugliness, but I hate code // duplication more than this; i"1.0" encoding=t should be // write-only, anyway. if (((gaze_data.line > entity.LineStart) && (gaze_data.line < entity.LineEnd)) || ((gaze_data.line == entity.LineStart) && (gaze_data.col >= entity.ColumnStart)) || ((gaze_data.line == entity.LineEnd) && (gaze_data.col <= entity.ColumnEnd))) { current.Add(entity); //break; } } } // if there was a change of entity, make a note of it if ((current != previous) && (previous != null) && (previous.Count > 0) && (current.Count > 0)) { EntityLink link = new EntityLink (); link.left = new HashSet <SourceCodeEntity> (previous); link.right = new HashSet <SourceCodeEntity> (current); if (src2src_links.ContainsKey (link)) { //src2src_links [link] += Math.Pow (gaze_data.timestamp, 2.0); src2src_links [link] += Math.Pow (100, gaze_data.timestamp); } else { //src2src_links [link] = Math.Pow (gaze_data.timestamp, 2.0); src2src_links [link] = Math.Pow (100, gaze_data.timestamp); } } previous = current; current = new HashSet <SourceCodeEntity> (); } return src2src_links; }