Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/8772179 to your computer and use it in GitHub Desktop.
Save nitaku/8772179 to your computer and use it in GitHub Desktop.
Space-flling curve layout (Gosper)

A more engineered example of a space-filling curve layout (see also this random fractal map), using the Gosper curve to displace the leaves of a tree (or a sequence) in a compact, 2D representation. Siblings are placed near to each other, so leaves that are "near" according to the hierarchy are also near to each other in this representation. Please note that internal nodes (non-leaf ones) cannot be represented with this technique. Only the sequence of leaves is shown. Therefore, this layout is particularly suited for datasets in which leaves are "more important" than internal nodes.

Visually, the layout is reminiscent of geographical maps. The color of the hexagonal cells indicates the depth of the corresponding leaf, like a sort of elevation map. As it's common for digital maps, you can zoom and pan the view.

This example is different from the previous ones about fractal maps: the layout step (i.e. decide where to place each leaf) has been given its own Javascript module. Subsequent process (such as creating a fractal map, a.k.a. jigsaw treemap) can be performed using the coordinates given by this layout module. It is also more easy to modify the layout to use a different space-filling curve (e.g. Hilbert).

The example shows the flare software package: each hexagonal cell represents a class within the package. Classes are displaced according to their position in the pacakage hierarchy (not shown). Class size is also not represented.

We assume the package hierarchy to be an unordered tree. As such, a canonical sorting is performed before feeding the tree into the layout.

Merging of hexagonal regions (to obtain the land boundary) is performed by the powerful clipper.js library (documentation).

[{
"name": "flare.analytics.cluster.AgglomerativeCluster",
"size": 3938,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.DataList",
"flare.util.math.IMatrix",
"flare.analytics.cluster.MergeEdge",
"flare.analytics.cluster.HierarchicalCluster",
"flare.vis.data.Data"]
},
{
"name": "flare.analytics.cluster.CommunityStructure",
"size": 3812,
"imports": ["flare.analytics.cluster.HierarchicalCluster",
"flare.animate.Transitioner",
"flare.vis.data.DataList",
"flare.analytics.cluster.MergeEdge",
"flare.util.math.IMatrix"]
},
{
"name": "flare.analytics.cluster.HierarchicalCluster",
"size": 6714,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataList",
"flare.vis.data.Tree",
"flare.util.Arrays",
"flare.analytics.cluster.MergeEdge",
"flare.util.Sort",
"flare.vis.operator.Operator",
"flare.util.Property",
"flare.vis.data.Data"]
},
{
"name": "flare.analytics.cluster.MergeEdge",
"size": 743,
"imports": []
},
{
"name": "flare.analytics.graph.BetweennessCentrality",
"size": 3534,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataList",
"flare.util.Arrays",
"flare.vis.data.Data",
"flare.util.Property",
"flare.vis.operator.Operator"]
},
{
"name": "flare.analytics.graph.LinkDistance",
"size": 5731,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.data.EdgeSprite",
"flare.analytics.graph.ShortestPaths",
"flare.vis.data.Data",
"flare.util.Property",
"flare.vis.operator.Operator"]
},
{
"name": "flare.analytics.graph.MaxFlowMinCut",
"size": 7840,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.data.EdgeSprite",
"flare.vis.data.Data",
"flare.util.Property",
"flare.vis.operator.Operator"]
},
{
"name": "flare.analytics.graph.ShortestPaths",
"size": 5914,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite",
"flare.animate.Transitioner",
"flare.vis.operator.Operator",
"flare.util.heap.HeapNode",
"flare.util.heap.FibonacciHeap",
"flare.util.Property",
"flare.vis.data.Data"]
},
{
"name": "flare.analytics.graph.SpanningTree",
"size": 3416,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.operator.IOperator",
"flare.vis.Visualization",
"flare.vis.data.TreeBuilder",
"flare.vis.operator.Operator"]
},
{
"name": "flare.analytics.optimization.AspectRatioBanker",
"size": 7074,
"imports": ["flare.animate.Transitioner",
"flare.util.Arrays",
"flare.vis.data.DataSprite",
"flare.scale.Scale",
"flare.vis.axis.CartesianAxes",
"flare.vis.Visualization",
"flare.util.Property",
"flare.vis.operator.Operator"]
},
{
"name": "flare.animate.Easing",
"size": 17010,
"imports": ["flare.animate.Transition"]
},
{
"name": "flare.animate.FunctionSequence",
"size": 5842,
"imports": ["flare.util.Maths",
"flare.animate.Transition",
"flare.util.Arrays",
"flare.animate.Sequence",
"flare.animate.Transitioner"]
},
{
"name": "flare.animate.interpolate.ArrayInterpolator",
"size": 1983,
"imports": ["flare.util.Arrays",
"flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.ColorInterpolator",
"size": 2047,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.DateInterpolator",
"size": 1375,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.Interpolator",
"size": 8746,
"imports": ["flare.animate.interpolate.NumberInterpolator",
"flare.animate.interpolate.ColorInterpolator",
"flare.animate.interpolate.PointInterpolator",
"flare.animate.interpolate.ObjectInterpolator",
"flare.animate.interpolate.MatrixInterpolator",
"flare.animate.interpolate.RectangleInterpolator",
"flare.animate.interpolate.DateInterpolator",
"flare.util.Property",
"flare.animate.interpolate.ArrayInterpolator"]
},
{
"name": "flare.animate.interpolate.MatrixInterpolator",
"size": 2202,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.NumberInterpolator",
"size": 1382,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.ObjectInterpolator",
"size": 1629,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.PointInterpolator",
"size": 1675,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.interpolate.RectangleInterpolator",
"size": 2042,
"imports": ["flare.animate.interpolate.Interpolator"]
},
{
"name": "flare.animate.ISchedulable",
"size": 1041,
"imports": ["flare.animate.Scheduler"]
},
{
"name": "flare.animate.Parallel",
"size": 5176,
"imports": ["flare.animate.Easing",
"flare.animate.Transition",
"flare.util.Arrays"]
},
{
"name": "flare.animate.Pause",
"size": 449,
"imports": ["flare.animate.Transition"]
},
{
"name": "flare.animate.Scheduler",
"size": 5593,
"imports": ["flare.animate.ISchedulable",
"flare.animate.Pause",
"flare.animate.Transition"]
},
{
"name": "flare.animate.Sequence",
"size": 5534,
"imports": ["flare.animate.Easing",
"flare.util.Maths",
"flare.animate.Transition",
"flare.util.Arrays"]
},
{
"name": "flare.animate.Transition",
"size": 9201,
"imports": ["flare.animate.Transitioner",
"flare.animate.TransitionEvent",
"flare.animate.Scheduler",
"flare.animate.Pause",
"flare.animate.Parallel",
"flare.animate.Easing",
"flare.animate.Sequence",
"flare.animate.ISchedulable",
"flare.util.Maths",
"flare.animate.Tween"]
},
{
"name": "flare.animate.Transitioner",
"size": 19975,
"imports": ["flare.util.IValueProxy",
"flare.animate.Parallel",
"flare.animate.Easing",
"flare.animate.Sequence",
"flare.animate.Transition",
"flare.animate.Tween",
"flare.util.Property"]
},
{
"name": "flare.animate.TransitionEvent",
"size": 1116,
"imports": ["flare.animate.Transition"]
},
{
"name": "flare.animate.Tween",
"size": 6006,
"imports": ["flare.animate.Transitioner",
"flare.animate.Transition",
"flare.animate.interpolate.Interpolator",
"flare.util.Property"]
},
{
"name": "flare.data.converters.Converters",
"size": 721,
"imports": ["flare.data.converters.IDataConverter",
"flare.data.converters.GraphMLConverter",
"flare.data.converters.JSONConverter",
"flare.data.converters.DelimitedTextConverter"]
},
{
"name": "flare.data.converters.DelimitedTextConverter",
"size": 4294,
"imports": ["flare.data.DataSet",
"flare.data.DataUtil",
"flare.data.DataTable",
"flare.data.converters.IDataConverter",
"flare.data.DataSchema",
"flare.data.DataField"]
},
{
"name": "flare.data.converters.GraphMLConverter",
"size": 9800,
"imports": ["flare.data.DataSet",
"flare.data.DataUtil",
"flare.data.DataTable",
"flare.data.converters.IDataConverter",
"flare.data.DataSchema",
"flare.data.DataField"]
},
{
"name": "flare.data.converters.IDataConverter",
"size": 1314,
"imports": ["flare.data.DataSet",
"flare.data.DataSchema"]
},
{
"name": "flare.data.converters.JSONConverter",
"size": 2220,
"imports": ["flare.data.DataSet",
"flare.data.DataUtil",
"flare.data.DataTable",
"flare.data.converters.IDataConverter",
"flare.data.DataSchema",
"flare.data.DataField",
"flare.util.Property"]
},
{
"name": "flare.data.DataField",
"size": 1759,
"imports": ["flare.data.DataUtil"]
},
{
"name": "flare.data.DataSchema",
"size": 2165,
"imports": ["flare.data.DataField",
"flare.util.Arrays"]
},
{
"name": "flare.data.DataSet",
"size": 586,
"imports": ["flare.data.DataTable"]
},
{
"name": "flare.data.DataSource",
"size": 3331,
"imports": ["flare.data.converters.IDataConverter",
"flare.data.converters.Converters",
"flare.data.DataSchema"]
},
{
"name": "flare.data.DataTable",
"size": 772,
"imports": ["flare.data.DataSchema"]
},
{
"name": "flare.data.DataUtil",
"size": 3322,
"imports": ["flare.data.DataField",
"flare.data.DataSchema"]
},
{
"name": "flare.display.DirtySprite",
"size": 8833,
"imports": []
},
{
"name": "flare.display.LineSprite",
"size": 1732,
"imports": ["flare.display.DirtySprite"]
},
{
"name": "flare.display.RectSprite",
"size": 3623,
"imports": ["flare.util.Colors",
"flare.display.DirtySprite"]
},
{
"name": "flare.display.TextSprite",
"size": 10066,
"imports": ["flare.display.DirtySprite"]
},
{
"name": "flare.flex.FlareVis",
"size": 4116,
"imports": ["flare.display.DirtySprite",
"flare.data.DataSet",
"flare.vis.Visualization",
"flare.vis.axis.CartesianAxes",
"flare.vis.axis.Axes",
"flare.vis.data.Data"]
},
{
"name": "flare.physics.DragForce",
"size": 1082,
"imports": ["flare.physics.Simulation",
"flare.physics.Particle",
"flare.physics.IForce"]
},
{
"name": "flare.physics.GravityForce",
"size": 1336,
"imports": ["flare.physics.Simulation",
"flare.physics.Particle",
"flare.physics.IForce"]
},
{
"name": "flare.physics.IForce",
"size": 319,
"imports": ["flare.physics.Simulation"]
},
{
"name": "flare.physics.NBodyForce",
"size": 10498,
"imports": ["flare.physics.Simulation",
"flare.physics.Particle",
"flare.physics.IForce"]
},
{
"name": "flare.physics.Particle",
"size": 2822,
"imports": []
},
{
"name": "flare.physics.Simulation",
"size": 9983,
"imports": ["flare.physics.Particle",
"flare.physics.NBodyForce",
"flare.physics.DragForce",
"flare.physics.GravityForce",
"flare.physics.Spring",
"flare.physics.SpringForce",
"flare.physics.IForce"]
},
{
"name": "flare.physics.Spring",
"size": 2213,
"imports": ["flare.physics.Particle"]
},
{
"name": "flare.physics.SpringForce",
"size": 1681,
"imports": ["flare.physics.Simulation",
"flare.physics.Particle",
"flare.physics.Spring",
"flare.physics.IForce"]
},
{
"name": "flare.query.AggregateExpression",
"size": 1616,
"imports": ["flare.query.Expression"]
},
{
"name": "flare.query.And",
"size": 1027,
"imports": ["flare.query.CompositeExpression",
"flare.query.Expression"]
},
{
"name": "flare.query.Arithmetic",
"size": 3891,
"imports": ["flare.query.BinaryExpression",
"flare.query.Expression"]
},
{
"name": "flare.query.Average",
"size": 891,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.BinaryExpression",
"size": 2893,
"imports": ["flare.query.Expression"]
},
{
"name": "flare.query.Comparison",
"size": 5103,
"imports": ["flare.query.Not",
"flare.query.BinaryExpression",
"flare.query.Expression",
"flare.query.Or"]
},
{
"name": "flare.query.CompositeExpression",
"size": 3677,
"imports": ["flare.query.Expression",
"flare.query.If"]
},
{
"name": "flare.query.Count",
"size": 781,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.DateUtil",
"size": 4141,
"imports": ["flare.query.Fn"]
},
{
"name": "flare.query.Distinct",
"size": 933,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.Expression",
"size": 5130,
"imports": ["flare.query.Variable",
"flare.query.IsA",
"flare.query.ExpressionIterator",
"flare.util.IPredicate",
"flare.query.Literal",
"flare.util.IEvaluable",
"flare.query.If"]
},
{
"name": "flare.query.ExpressionIterator",
"size": 3617,
"imports": ["flare.query.Expression"]
},
{
"name": "flare.query.Fn",
"size": 3240,
"imports": ["flare.query.DateUtil",
"flare.query.CompositeExpression",
"flare.query.Expression",
"flare.query.StringUtil"]
},
{
"name": "flare.query.If",
"size": 2732,
"imports": ["flare.query.Expression"]
},
{
"name": "flare.query.IsA",
"size": 2039,
"imports": ["flare.query.Expression",
"flare.query.If"]
},
{
"name": "flare.query.Literal",
"size": 1214,
"imports": ["flare.query.Expression"]
},
{
"name": "flare.query.Match",
"size": 3748,
"imports": ["flare.query.BinaryExpression",
"flare.query.Expression",
"flare.query.StringUtil"]
},
{
"name": "flare.query.Maximum",
"size": 843,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.methods.add",
"size": 593,
"imports": ["flare.query.methods.or",
"flare.query.Arithmetic"]
},
{
"name": "flare.query.methods.and",
"size": 330,
"imports": ["flare.query.And",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.average",
"size": 287,
"imports": ["flare.query.Average",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.count",
"size": 277,
"imports": ["flare.query.Count",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.distinct",
"size": 292,
"imports": ["flare.query.Distinct",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.div",
"size": 595,
"imports": ["flare.query.methods.or",
"flare.query.Arithmetic"]
},
{
"name": "flare.query.methods.eq",
"size": 594,
"imports": ["flare.query.Comparison",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.fn",
"size": 460,
"imports": ["flare.query.methods.or",
"flare.query.Fn"]
},
{
"name": "flare.query.methods.gt",
"size": 603,
"imports": ["flare.query.Comparison",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.gte",
"size": 625,
"imports": ["flare.query.Comparison",
"flare.query.methods.gt",
"flare.query.methods.eq",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.iff",
"size": 748,
"imports": ["flare.query.methods.or",
"flare.query.If"]
},
{
"name": "flare.query.methods.isa",
"size": 461,
"imports": ["flare.query.IsA",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.lt",
"size": 597,
"imports": ["flare.query.Comparison",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.lte",
"size": 619,
"imports": ["flare.query.Comparison",
"flare.query.methods.lt",
"flare.query.methods.eq",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.max",
"size": 283,
"imports": ["flare.query.Maximum",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.min",
"size": 283,
"imports": ["flare.query.Minimum",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.mod",
"size": 591,
"imports": ["flare.query.methods.or",
"flare.query.Arithmetic"]
},
{
"name": "flare.query.methods.mul",
"size": 603,
"imports": ["flare.query.methods.lt",
"flare.query.methods.or",
"flare.query.Arithmetic"]
},
{
"name": "flare.query.methods.neq",
"size": 599,
"imports": ["flare.query.Comparison",
"flare.query.methods.eq",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.not",
"size": 386,
"imports": ["flare.query.Not",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.or",
"size": 323,
"imports": ["flare.query.Or"]
},
{
"name": "flare.query.methods.orderby",
"size": 307,
"imports": ["flare.query.Query",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.range",
"size": 772,
"imports": ["flare.query.methods.max",
"flare.query.Range",
"flare.query.methods.or",
"flare.query.methods.min"]
},
{
"name": "flare.query.methods.select",
"size": 296,
"imports": ["flare.query.Query"]
},
{
"name": "flare.query.methods.stddev",
"size": 363,
"imports": ["flare.query.methods.and",
"flare.query.Variance",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.sub",
"size": 600,
"imports": ["flare.query.methods.or",
"flare.query.Arithmetic"]
},
{
"name": "flare.query.methods.sum",
"size": 280,
"imports": ["flare.query.Sum",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.update",
"size": 307,
"imports": ["flare.query.Query"]
},
{
"name": "flare.query.methods.variance",
"size": 335,
"imports": ["flare.query.Variance",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods.where",
"size": 299,
"imports": ["flare.query.Query",
"flare.query.methods.lt",
"flare.query.methods.lte"]
},
{
"name": "flare.query.methods.xor",
"size": 354,
"imports": ["flare.query.Xor",
"flare.query.methods.or"]
},
{
"name": "flare.query.methods._",
"size": 264,
"imports": ["flare.query.Literal",
"flare.query.methods.or"]
},
{
"name": "flare.query.Minimum",
"size": 843,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.Not",
"size": 1554,
"imports": ["flare.query.Expression"]
},
{
"name": "flare.query.Or",
"size": 970,
"imports": ["flare.query.CompositeExpression",
"flare.query.Expression"]
},
{
"name": "flare.query.Query",
"size": 13896,
"imports": ["flare.query.Variable",
"flare.query.Sum",
"flare.query.Expression",
"flare.util.Sort",
"flare.query.Not",
"flare.query.AggregateExpression",
"flare.query.Literal",
"flare.util.Filter",
"flare.util.Property",
"flare.query.If"]
},
{
"name": "flare.query.Range",
"size": 1594,
"imports": ["flare.query.And",
"flare.query.Comparison",
"flare.query.Expression"]
},
{
"name": "flare.query.StringUtil",
"size": 4130,
"imports": ["flare.query.Fn"]
},
{
"name": "flare.query.Sum",
"size": 791,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.Variable",
"size": 1124,
"imports": ["flare.query.Expression",
"flare.util.Property"]
},
{
"name": "flare.query.Variance",
"size": 1876,
"imports": ["flare.query.Expression",
"flare.query.AggregateExpression"]
},
{
"name": "flare.query.Xor",
"size": 1101,
"imports": ["flare.query.CompositeExpression",
"flare.query.Expression"]
},
{
"name": "flare.scale.IScaleMap",
"size": 2105,
"imports": ["flare.scale.Scale"]
},
{
"name": "flare.scale.LinearScale",
"size": 1316,
"imports": ["flare.util.Maths",
"flare.util.Strings",
"flare.scale.Scale",
"flare.scale.QuantitativeScale",
"flare.scale.ScaleType"]
},
{
"name": "flare.scale.LogScale",
"size": 3151,
"imports": ["flare.util.Maths",
"flare.util.Strings",
"flare.scale.Scale",
"flare.scale.QuantitativeScale",
"flare.scale.ScaleType"]
},
{
"name": "flare.scale.OrdinalScale",
"size": 3770,
"imports": ["flare.scale.ScaleType",
"flare.util.Arrays",
"flare.scale.Scale"]
},
{
"name": "flare.scale.QuantileScale",
"size": 2435,
"imports": ["flare.util.Maths",
"flare.util.Strings",
"flare.scale.Scale",
"flare.scale.ScaleType"]
},
{
"name": "flare.scale.QuantitativeScale",
"size": 4839,
"imports": ["flare.util.Maths",
"flare.util.Strings",
"flare.scale.Scale"]
},
{
"name": "flare.scale.RootScale",
"size": 1756,
"imports": ["flare.util.Maths",
"flare.util.Strings",
"flare.scale.Scale",
"flare.scale.QuantitativeScale",
"flare.scale.ScaleType"]
},
{
"name": "flare.scale.Scale",
"size": 4268,
"imports": ["flare.scale.ScaleType",
"flare.util.Strings"]
},
{
"name": "flare.scale.ScaleType",
"size": 1821,
"imports": ["flare.scale.Scale"]
},
{
"name": "flare.scale.TimeScale",
"size": 5833,
"imports": ["flare.util.Maths",
"flare.util.Dates",
"flare.scale.Scale",
"flare.scale.ScaleType"]
},
{
"name": "flare.util.Arrays",
"size": 8258,
"imports": ["flare.util.IValueProxy",
"flare.util.Property",
"flare.util.IEvaluable"]
},
{
"name": "flare.util.Colors",
"size": 10001,
"imports": ["flare.util.Filter"]
},
{
"name": "flare.util.Dates",
"size": 8217,
"imports": ["flare.util.Maths"]
},
{
"name": "flare.util.Displays",
"size": 12555,
"imports": ["flare.util.IValueProxy",
"flare.util.Filter",
"flare.util.Property",
"flare.util.IEvaluable",
"flare.util.Sort"]
},
{
"name": "flare.util.Filter",
"size": 2324,
"imports": ["flare.util.IPredicate",
"flare.util.Property"]
},
{
"name": "flare.util.Geometry",
"size": 10993,
"imports": []
},
{
"name": "flare.util.heap.FibonacciHeap",
"size": 9354,
"imports": ["flare.util.heap.HeapNode"]
},
{
"name": "flare.util.heap.HeapNode",
"size": 1233,
"imports": ["flare.util.heap.FibonacciHeap"]
},
{
"name": "flare.util.IEvaluable",
"size": 335,
"imports": []
},
{
"name": "flare.util.IPredicate",
"size": 383,
"imports": []
},
{
"name": "flare.util.IValueProxy",
"size": 874,
"imports": []
},
{
"name": "flare.util.math.DenseMatrix",
"size": 3165,
"imports": ["flare.util.math.IMatrix"]
},
{
"name": "flare.util.math.IMatrix",
"size": 2815,
"imports": []
},
{
"name": "flare.util.math.SparseMatrix",
"size": 3366,
"imports": ["flare.util.math.IMatrix"]
},
{
"name": "flare.util.Maths",
"size": 17705,
"imports": ["flare.util.Arrays"]
},
{
"name": "flare.util.Orientation",
"size": 1486,
"imports": []
},
{
"name": "flare.util.palette.ColorPalette",
"size": 6367,
"imports": ["flare.util.palette.Palette",
"flare.util.Colors"]
},
{
"name": "flare.util.palette.Palette",
"size": 1229,
"imports": []
},
{
"name": "flare.util.palette.ShapePalette",
"size": 2059,
"imports": ["flare.util.palette.Palette",
"flare.util.Shapes"]
},
{
"name": "flare.util.palette.SizePalette",
"size": 2291,
"imports": ["flare.util.palette.Palette"]
},
{
"name": "flare.util.Property",
"size": 5559,
"imports": ["flare.util.IPredicate",
"flare.util.IValueProxy",
"flare.util.IEvaluable"]
},
{
"name": "flare.util.Shapes",
"size": 19118,
"imports": ["flare.util.Arrays"]
},
{
"name": "flare.util.Sort",
"size": 6887,
"imports": ["flare.util.Arrays",
"flare.util.Property"]
},
{
"name": "flare.util.Stats",
"size": 6557,
"imports": ["flare.util.Arrays",
"flare.util.Property"]
},
{
"name": "flare.util.Strings",
"size": 22026,
"imports": ["flare.util.Dates",
"flare.util.Property"]
},
{
"name": "flare.vis.axis.Axes",
"size": 1302,
"imports": ["flare.animate.Transitioner",
"flare.vis.Visualization"]
},
{
"name": "flare.vis.axis.Axis",
"size": 24593,
"imports": ["flare.animate.Transitioner",
"flare.scale.LinearScale",
"flare.util.Arrays",
"flare.scale.ScaleType",
"flare.util.Strings",
"flare.display.TextSprite",
"flare.scale.Scale",
"flare.util.Stats",
"flare.scale.IScaleMap",
"flare.vis.axis.AxisLabel",
"flare.vis.axis.AxisGridLine"]
},
{
"name": "flare.vis.axis.AxisGridLine",
"size": 652,
"imports": ["flare.vis.axis.Axis",
"flare.display.LineSprite"]
},
{
"name": "flare.vis.axis.AxisLabel",
"size": 636,
"imports": ["flare.vis.axis.Axis",
"flare.display.TextSprite"]
},
{
"name": "flare.vis.axis.CartesianAxes",
"size": 6703,
"imports": ["flare.animate.Transitioner",
"flare.display.RectSprite",
"flare.vis.axis.Axis",
"flare.display.TextSprite",
"flare.vis.axis.Axes",
"flare.vis.Visualization",
"flare.vis.axis.AxisGridLine"]
},
{
"name": "flare.vis.controls.AnchorControl",
"size": 2138,
"imports": ["flare.vis.controls.Control",
"flare.vis.Visualization",
"flare.vis.operator.layout.Layout"]
},
{
"name": "flare.vis.controls.ClickControl",
"size": 3824,
"imports": ["flare.vis.events.SelectionEvent",
"flare.vis.controls.Control"]
},
{
"name": "flare.vis.controls.Control",
"size": 1353,
"imports": ["flare.vis.controls.IControl",
"flare.util.Filter"]
},
{
"name": "flare.vis.controls.ControlList",
"size": 4665,
"imports": ["flare.vis.controls.IControl",
"flare.util.Arrays",
"flare.vis.Visualization",
"flare.vis.controls.Control"]
},
{
"name": "flare.vis.controls.DragControl",
"size": 2649,
"imports": ["flare.vis.controls.Control",
"flare.vis.data.DataSprite"]
},
{
"name": "flare.vis.controls.ExpandControl",
"size": 2832,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.controls.Control",
"flare.vis.Visualization"]
},
{
"name": "flare.vis.controls.HoverControl",
"size": 4896,
"imports": ["flare.vis.events.SelectionEvent",
"flare.vis.controls.Control"]
},
{
"name": "flare.vis.controls.IControl",
"size": 763,
"imports": ["flare.vis.controls.Control"]
},
{
"name": "flare.vis.controls.PanZoomControl",
"size": 5222,
"imports": ["flare.util.Displays",
"flare.vis.controls.Control"]
},
{
"name": "flare.vis.controls.SelectionControl",
"size": 7862,
"imports": ["flare.vis.events.SelectionEvent",
"flare.vis.controls.Control"]
},
{
"name": "flare.vis.controls.TooltipControl",
"size": 8435,
"imports": ["flare.animate.Tween",
"flare.display.TextSprite",
"flare.vis.controls.Control",
"flare.vis.events.TooltipEvent"]
},
{
"name": "flare.vis.data.Data",
"size": 20544,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite",
"flare.util.Arrays",
"flare.vis.data.DataSprite",
"flare.vis.data.Tree",
"flare.vis.events.DataEvent",
"flare.data.DataSet",
"flare.vis.data.TreeBuilder",
"flare.vis.data.DataList",
"flare.data.DataSchema",
"flare.util.Sort",
"flare.data.DataField",
"flare.util.Property"]
},
{
"name": "flare.vis.data.DataList",
"size": 19788,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.util.Arrays",
"flare.util.math.DenseMatrix",
"flare.vis.data.DataSprite",
"flare.vis.data.EdgeSprite",
"flare.vis.events.DataEvent",
"flare.util.Stats",
"flare.util.math.IMatrix",
"flare.util.Sort",
"flare.util.Filter",
"flare.util.Property",
"flare.util.IEvaluable",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.data.DataSprite",
"size": 10349,
"imports": ["flare.util.Colors",
"flare.vis.data.Data",
"flare.display.DirtySprite",
"flare.vis.data.render.IRenderer",
"flare.vis.data.render.ShapeRenderer"]
},
{
"name": "flare.vis.data.EdgeSprite",
"size": 3301,
"imports": ["flare.vis.data.render.EdgeRenderer",
"flare.vis.data.DataSprite",
"flare.vis.data.NodeSprite",
"flare.vis.data.render.ArrowType",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.data.NodeSprite",
"size": 19382,
"imports": ["flare.animate.Transitioner",
"flare.util.Arrays",
"flare.vis.data.DataSprite",
"flare.vis.data.EdgeSprite",
"flare.vis.data.Tree",
"flare.util.Sort",
"flare.util.Filter",
"flare.util.IEvaluable",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.data.render.ArrowType",
"size": 698,
"imports": []
},
{
"name": "flare.vis.data.render.EdgeRenderer",
"size": 5569,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataSprite",
"flare.vis.data.render.IRenderer",
"flare.util.Shapes",
"flare.util.Geometry",
"flare.vis.data.render.ArrowType"]
},
{
"name": "flare.vis.data.render.IRenderer",
"size": 353,
"imports": ["flare.vis.data.DataSprite"]
},
{
"name": "flare.vis.data.render.ShapeRenderer",
"size": 2247,
"imports": ["flare.util.Shapes",
"flare.vis.data.render.IRenderer",
"flare.vis.data.DataSprite"]
},
{
"name": "flare.vis.data.ScaleBinding",
"size": 11275,
"imports": ["flare.scale.TimeScale",
"flare.scale.ScaleType",
"flare.scale.LinearScale",
"flare.scale.LogScale",
"flare.scale.OrdinalScale",
"flare.scale.RootScale",
"flare.scale.Scale",
"flare.scale.QuantileScale",
"flare.util.Stats",
"flare.scale.QuantitativeScale",
"flare.vis.events.DataEvent",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.data.Tree",
"size": 7147,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.events.DataEvent",
"flare.vis.data.NodeSprite",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.data.TreeBuilder",
"size": 9930,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite",
"flare.vis.data.Tree",
"flare.util.heap.HeapNode",
"flare.util.heap.FibonacciHeap",
"flare.util.Property",
"flare.util.IEvaluable",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.events.DataEvent",
"size": 2313,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataList",
"flare.vis.data.DataSprite"]
},
{
"name": "flare.vis.events.SelectionEvent",
"size": 1880,
"imports": ["flare.vis.events.DataEvent"]
},
{
"name": "flare.vis.events.TooltipEvent",
"size": 1701,
"imports": ["flare.vis.data.EdgeSprite",
"flare.vis.data.NodeSprite"]
},
{
"name": "flare.vis.events.VisualizationEvent",
"size": 1117,
"imports": ["flare.animate.Transitioner"]
},
{
"name": "flare.vis.legend.Legend",
"size": 20859,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.ScaleBinding",
"flare.util.palette.SizePalette",
"flare.scale.ScaleType",
"flare.vis.legend.LegendItem",
"flare.display.RectSprite",
"flare.display.TextSprite",
"flare.scale.Scale",
"flare.vis.legend.LegendRange",
"flare.util.Displays",
"flare.util.Orientation",
"flare.util.palette.ShapePalette",
"flare.util.palette.Palette",
"flare.util.palette.ColorPalette"]
},
{
"name": "flare.vis.legend.LegendItem",
"size": 4614,
"imports": ["flare.util.Shapes",
"flare.display.TextSprite",
"flare.vis.legend.Legend",
"flare.display.RectSprite"]
},
{
"name": "flare.vis.legend.LegendRange",
"size": 10530,
"imports": ["flare.util.Colors",
"flare.vis.legend.Legend",
"flare.display.RectSprite",
"flare.display.TextSprite",
"flare.scale.Scale",
"flare.util.Stats",
"flare.scale.IScaleMap",
"flare.util.Orientation",
"flare.util.palette.ColorPalette"]
},
{
"name": "flare.vis.operator.distortion.BifocalDistortion",
"size": 4461,
"imports": ["flare.vis.operator.distortion.Distortion"]
},
{
"name": "flare.vis.operator.distortion.Distortion",
"size": 6314,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.DataSprite",
"flare.vis.events.VisualizationEvent",
"flare.vis.axis.Axis",
"flare.vis.axis.CartesianAxes",
"flare.vis.operator.layout.Layout",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.distortion.FisheyeDistortion",
"size": 3444,
"imports": ["flare.vis.operator.distortion.Distortion"]
},
{
"name": "flare.vis.operator.encoder.ColorEncoder",
"size": 3179,
"imports": ["flare.animate.Transitioner",
"flare.scale.ScaleType",
"flare.vis.operator.encoder.Encoder",
"flare.util.palette.Palette",
"flare.util.palette.ColorPalette",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.encoder.Encoder",
"size": 4060,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.DataSprite",
"flare.vis.operator.Operator",
"flare.vis.data.ScaleBinding",
"flare.util.palette.Palette",
"flare.util.Filter",
"flare.util.Property",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.encoder.PropertyEncoder",
"size": 4138,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.DataList",
"flare.vis.data.Data",
"flare.vis.operator.encoder.Encoder",
"flare.util.Filter",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.encoder.ShapeEncoder",
"size": 1690,
"imports": ["flare.util.palette.Palette",
"flare.scale.ScaleType",
"flare.util.palette.ShapePalette",
"flare.vis.operator.encoder.Encoder",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.encoder.SizeEncoder",
"size": 1830,
"imports": ["flare.util.palette.Palette",
"flare.scale.ScaleType",
"flare.vis.operator.encoder.Encoder",
"flare.util.palette.SizePalette",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.filter.FisheyeTreeFilter",
"size": 5219,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataSprite",
"flare.vis.data.EdgeSprite",
"flare.vis.data.Tree",
"flare.vis.operator.Operator",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.filter.GraphDistanceFilter",
"size": 3165,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.operator.Operator",
"flare.vis.data.DataSprite",
"flare.vis.data.EdgeSprite"]
},
{
"name": "flare.vis.operator.filter.VisibilityFilter",
"size": 3509,
"imports": ["flare.vis.operator.Operator",
"flare.animate.Transitioner",
"flare.util.Filter",
"flare.vis.data.DataSprite",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.IOperator",
"size": 1286,
"imports": ["flare.animate.Transitioner",
"flare.vis.Visualization",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.label.Labeler",
"size": 9956,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.DataSprite",
"flare.display.TextSprite",
"flare.vis.operator.Operator",
"flare.util.Shapes",
"flare.util.Filter",
"flare.util.Property",
"flare.util.IEvaluable",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.label.RadialLabeler",
"size": 3899,
"imports": ["flare.vis.operator.label.Labeler",
"flare.util.Shapes",
"flare.display.TextSprite",
"flare.vis.data.DataSprite",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.label.StackedAreaLabeler",
"size": 3202,
"imports": ["flare.vis.operator.label.Labeler",
"flare.display.TextSprite",
"flare.vis.data.DataSprite",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.AxisLayout",
"size": 6725,
"imports": ["flare.scale.ScaleType",
"flare.vis.data.DataSprite",
"flare.vis.axis.CartesianAxes",
"flare.vis.data.ScaleBinding",
"flare.util.Property",
"flare.vis.operator.layout.Layout",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.BundledEdgeRouter",
"size": 3727,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.util.Arrays",
"flare.vis.data.DataSprite",
"flare.vis.data.EdgeSprite",
"flare.util.Shapes",
"flare.vis.operator.layout.Layout",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.layout.CircleLayout",
"size": 9317,
"imports": ["flare.vis.data.NodeSprite",
"flare.vis.data.DataList",
"flare.vis.data.ScaleBinding",
"flare.util.Property",
"flare.vis.operator.layout.Layout",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.CirclePackingLayout",
"size": 12003,
"imports": ["flare.vis.data.NodeSprite",
"flare.vis.data.render.ShapeRenderer",
"flare.util.Shapes",
"flare.util.Sort",
"flare.vis.operator.layout.Layout",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.DendrogramLayout",
"size": 4853,
"imports": ["flare.util.Property",
"flare.vis.data.NodeSprite",
"flare.util.Orientation",
"flare.vis.operator.layout.Layout",
"flare.vis.data.EdgeSprite"]
},
{
"name": "flare.vis.operator.layout.ForceDirectedLayout",
"size": 8411,
"imports": ["flare.physics.Simulation",
"flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataSprite",
"flare.physics.Particle",
"flare.physics.Spring",
"flare.vis.operator.layout.Layout",
"flare.vis.data.EdgeSprite",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.IcicleTreeLayout",
"size": 4864,
"imports": ["flare.vis.data.NodeSprite",
"flare.util.Orientation",
"flare.vis.operator.layout.Layout"]
},
{
"name": "flare.vis.operator.layout.IndentedTreeLayout",
"size": 3174,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.util.Arrays",
"flare.vis.operator.layout.Layout",
"flare.vis.data.EdgeSprite"]
},
{
"name": "flare.vis.operator.layout.Layout",
"size": 7881,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.vis.data.DataList",
"flare.vis.data.DataSprite",
"flare.vis.data.EdgeSprite",
"flare.vis.Visualization",
"flare.vis.axis.CartesianAxes",
"flare.vis.axis.Axes",
"flare.animate.TransitionEvent",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.layout.NodeLinkTreeLayout",
"size": 12870,
"imports": ["flare.vis.data.NodeSprite",
"flare.util.Arrays",
"flare.util.Orientation",
"flare.vis.operator.layout.Layout"]
},
{
"name": "flare.vis.operator.layout.PieLayout",
"size": 2728,
"imports": ["flare.vis.data.DataList",
"flare.vis.data.DataSprite",
"flare.util.Shapes",
"flare.util.Property",
"flare.vis.operator.layout.Layout",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.RadialTreeLayout",
"size": 12348,
"imports": ["flare.vis.data.NodeSprite",
"flare.util.Arrays",
"flare.vis.operator.layout.Layout"]
},
{
"name": "flare.vis.operator.layout.RandomLayout",
"size": 870,
"imports": ["flare.vis.operator.layout.Layout",
"flare.vis.data.DataSprite",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.operator.layout.StackedAreaLayout",
"size": 9121,
"imports": ["flare.scale.TimeScale",
"flare.scale.LinearScale",
"flare.util.Arrays",
"flare.scale.OrdinalScale",
"flare.vis.data.NodeSprite",
"flare.scale.Scale",
"flare.vis.axis.CartesianAxes",
"flare.util.Stats",
"flare.util.Orientation",
"flare.scale.QuantitativeScale",
"flare.util.Maths",
"flare.vis.operator.layout.Layout"]
},
{
"name": "flare.vis.operator.layout.TreeMapLayout",
"size": 9191,
"imports": ["flare.animate.Transitioner",
"flare.vis.data.NodeSprite",
"flare.util.Property",
"flare.vis.operator.layout.Layout"]
},
{
"name": "flare.vis.operator.Operator",
"size": 2490,
"imports": ["flare.animate.Transitioner",
"flare.vis.operator.IOperator",
"flare.util.Property",
"flare.util.IEvaluable",
"flare.vis.Visualization"]
},
{
"name": "flare.vis.operator.OperatorList",
"size": 5248,
"imports": ["flare.animate.Transitioner",
"flare.util.Arrays",
"flare.vis.operator.IOperator",
"flare.vis.Visualization",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.OperatorSequence",
"size": 4190,
"imports": ["flare.animate.Transitioner",
"flare.util.Arrays",
"flare.vis.operator.IOperator",
"flare.vis.operator.OperatorList",
"flare.animate.FunctionSequence",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.OperatorSwitch",
"size": 2581,
"imports": ["flare.animate.Transitioner",
"flare.vis.operator.OperatorList",
"flare.vis.operator.IOperator",
"flare.vis.operator.Operator"]
},
{
"name": "flare.vis.operator.SortOperator",
"size": 2023,
"imports": ["flare.vis.operator.Operator",
"flare.animate.Transitioner",
"flare.util.Arrays",
"flare.vis.data.Data"]
},
{
"name": "flare.vis.Visualization",
"size": 16540,
"imports": ["flare.animate.Transitioner",
"flare.vis.operator.IOperator",
"flare.animate.Scheduler",
"flare.vis.events.VisualizationEvent",
"flare.vis.data.Tree",
"flare.vis.events.DataEvent",
"flare.vis.axis.Axes",
"flare.vis.axis.CartesianAxes",
"flare.util.Displays",
"flare.vis.operator.OperatorList",
"flare.vis.controls.ControlList",
"flare.animate.ISchedulable",
"flare.vis.data.Data"]
}]
### read flare-imports data and provide it in form of tree (for the package hierarchy) and links (for the imports) ###
### code adapted from http://bl.ocks.org/mbostock/4341134 ###
`
window.flare_reader = {
// Lazily construct the package hierarchy from class names.
tree: function(classes) {
var map = {};
function find(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
var flare = map['flare'];
delete flare.parent; // CHANGED root node is not ""
return flare;
},
// Return a list of imports for the given array of nodes.
imports: function(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.imports) d.imports.forEach(function(i) {
imports.push({source: map[d.name], target: map[i]});
});
});
return imports;
}
};
`
/* read flare-imports data and provide it in form of tree (for the package hierarchy) and links (for the imports)
*/
/* code adapted from http://bl.ocks.org/mbostock/4341134
*/
(function() {
window.flare_reader = {
// Lazily construct the package hierarchy from class names.
tree: function(classes) {
var map = {};
function find(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
var flare = map['flare'];
delete flare.parent; // CHANGED root node is not ""
return flare;
},
// Return a list of imports for the given array of nodes.
imports: function(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.imports) d.imports.forEach(function(i) {
imports.push({source: map[d.name], target: map[i]});
});
});
return imports;
}
};
;
}).call(this);
/* Converts Paths to SVG path string
*/
/* and scales down the coordinates
*/
/* from http://jsclipper.sourceforge.net/6.1.3.1/index.html?p=starter_boolean.html
*/
(function() {
var paths2string;
paths2string = function(paths, scale) {
var i, p, path, svgpath, _i, _len, _len2;
svgpath = '';
if (!(scale != null)) scale = 1;
for (_i = 0, _len = paths.length; _i < _len; _i++) {
path = paths[_i];
for (i = 0, _len2 = path.length; i < _len2; i++) {
p = path[i];
if (i === 0) {
svgpath += 'M';
} else {
svgpath += 'L';
}
svgpath += p.X / scale + ", " + p.Y / scale;
}
svgpath += 'Z';
}
if (svgpath === '') svgpath = 'M0,0';
return svgpath;
};
window.hex_utils = {
svg_hex: function(a) {
var r;
r = a / Math.sin(Math.PI / 3);
return "M" + r + " 0 L" + (r / 2) + " " + a + " L" + (-r / 2) + " " + a + " L" + (-r) + " 0 L" + (-r / 2) + " " + (-a) + " L" + (r / 2) + " " + (-a) + " Z";
},
hex_region: function(points, a) {
var cpr, hexes, p, r, region, scale;
r = a / Math.sin(Math.PI / 3);
hexes = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = points.length; _i < _len; _i++) {
p = points[_i];
_results.push([
{
X: p.x + r,
Y: p.y
}, {
X: p.x + r / 2,
Y: p.y + a
}, {
X: p.x - r / 2,
Y: p.y + a
}, {
X: p.x - r,
Y: p.y
}, {
X: p.x - r / 2,
Y: p.y - a
}, {
X: p.x + r / 2,
Y: p.y - a
}
]);
}
return _results;
})();
scale = 100;
ClipperLib.JS.ScaleUpPaths(hexes, scale);
cpr = new ClipperLib.Clipper();
cpr.AddPaths(hexes, ClipperLib.PolyType.ptSubject, true);
region = new ClipperLib.Paths();
cpr.Execute(ClipperLib.ClipType.ctUnion, region, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero);
return paths2string(region, scale);
}
};
}).call(this);
width = 960
height = 500
### create the SVG ###
svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
vis = svg.append('g')
map = vis.append('g')
.attr('transform', "translate(#{width/2},#{height/2})")
### define a zoom behavior ###
zoom = d3.behavior.zoom()
.scaleExtent([1,10]) # min-max zoom
.on 'zoom', () ->
### whenever the user zooms, ###
### modify translation and scale of the zoom group accordingly ###
vis.attr('transform', "translate(#{zoom.translate()})scale(#{zoom.scale()})")
### bind the zoom behavior to the main SVG ###
svg.call(zoom)
### read flare data ###
d3.json 'flare-imports.json', (data) ->
### package tree ###
tree = flare_reader.tree(data)
hierarchy = d3.layout.hierarchy()
nodes = hierarchy(tree)
### imports links ###
# graph_links = flare_reader.imports(nodes)
### this tree is unordered, we need a canonical ordering for it ###
tree_utils.canonical_sort(tree)
### obtain the sequence of leaves ###
leaves = tree_utils.get_leaves(tree)
### VISUALIZATION ###
### compute the space-filling curve layout ###
scale = 26
sfc_layout.displace(leaves, sfc_layout.GOSPER, scale, -Math.PI/6)
### compute also the position of internal nodes ###
sfc_layout.displace_tree(tree)
### define a bundle layout ###
# bundle = d3.layout.bundle()
# bundles = bundle(graph_links)
# link_generator = d3.svg.line()
# .interpolate('bundle')
# .tension(0.99)
# .x((d) -> d.x)
# .y((d) -> d.y)
### define a color scale for leaf depth ###
depth_color = d3.scale.linear()
.domain([1, d3.max(leaves,(d)->d.depth)])
.range(['#FFF7DB', '#F0A848'])
.interpolate(d3.interpolateHcl)
### define a thickness scale for region depth ###
# depth_thickness = d3.scale.sqrt()
# .domain([0, d3.max(leaves,(d)->d.depth)-1])
# .range([4, 0.1])
### translate size to cell scale ###
# size2cellscale = d3.scale.sqrt()
# .domain([0, d3.max(nodes,(d)->d.size)])
# .range([0,scale])
### translate depth to label font size ###
# depth2fontsize = d3.scale.sqrt()
# .domain([0, d3.max(nodes,(d)->d.depth)])
# .range([40,4])
### compute all the internal nodes regions ###
jigsaw.treemap(tree, scale, jigsaw.HEX_CELL)
### define the level zero region (the land) ###
defs = svg.append('defs')
defs.append('path')
.attr('id', 'land')
.attr('d', jigsaw.get_svg_path tree.region)
### faux land glow (using filters takes too much resources) ###
map.append('use')
.attr('class', 'land-glow-outer')
.attr('xlink:href', '#land')
map.append('use')
.attr('class', 'land-glow-inner')
.attr('xlink:href', '#land')
### draw the cells ###
map.selectAll('.cell')
.data(leaves)
.enter().append('path')
.attr('class', 'cell')
# .attr('d', (d) -> jigsaw.hex_generate_svg_path size2cellscale(d.size) )
.attr('d', jigsaw.hex_generate_svg_path scale )
.attr('transform', (d) -> "translate(#{d.x},#{d.y})")
.attr('fill', (d) -> depth_color(d.depth))
# .attr('stroke', (d) -> depth_color(d.depth))
.attr('stroke', 'white')
### draw the level one region boundaries ###
# map.selectAll('.region')
# .data(nodes.filter((d)->d.depth is 1))
# .enter().append('path')
# .attr('class', 'region')
# .attr('d', (d) -> jigsaw.get_svg_path d.region)
## .attr('stroke-width', (d) -> depth_thickness(d.depth))
# .attr('stroke-width', '2px')
### draw the land border (above cells) ###
map.append('use')
.attr('class', 'land-fill')
.attr('xlink:href', '#land')
### draw the graph links ###
# map.selectAll('.graph_link')
# .data(bundles)
# .enter().append('path')
# .attr('class', 'graph_link')
# .attr('d', link_generator)
### draw the level one labels ###
# map.selectAll('.label')
# .data(nodes.filter((d)->d.depth is 1))
# .enter().append('text')
# .attr('class', 'label')
# .attr('font-size', (d) -> depth2fontsize(d.depth))
# .attr('dy', '0.35em')
# .attr('transform', (d) -> "translate(#{d.x},#{d.y})")
# .text((d) -> d.name.split('.').reverse()[0])
### draw the leaf labels ###
map.selectAll('.label')
.data(leaves)
.enter().append('text')
.attr('class', 'label')
.attr('font-size', '4px')
.attr('dy', '0.35em')
.attr('transform', (d) -> "translate(#{d.x},#{d.y})")
.text((d) -> d.name.split('.').reverse()[0])
.land-glow-outer {
fill: none;
stroke: #eeeeee;
stroke-width: 16px;
}
.land-glow-inner {
fill: none;
stroke: #dddddd;
stroke-width: 8px;
}
.land-fill {
fill: none;
stroke: #444444;
stroke-width: 1px;
}
.cell {
stroke-width: 1px;
}
.region {
fill: none;
stroke: #444444;
}
@font-face {
font-family: "Lacuna";
src: url("lacuna.ttf");
}
.label {
fill: #333333;
text-anchor: middle;
pointer-events: none;
font-family: "Lacuna";
}
.graph_link {
stroke: teal;
stroke-width: 0.05;
fill: none;
pointer-events: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Space-filling curve layout</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://jsclipper.sourceforge.net/6.1.3.1/clipper.js"></script>
<script src="zip.js"></script>
<script src="flare_reader.js"></script>
<script src="tree_utils.js"></script>
<script src="sfc_layout.js"></script>
<script src="jigsaw.js"></script>
</head>
<body>
</body>
<script src="index.js"></script>
</html>
(function() {
var height, map, svg, vis, width, zoom;
width = 960;
height = 500;
/* create the SVG
*/
svg = d3.select('body').append('svg').attr('width', width).attr('height', height);
vis = svg.append('g');
map = vis.append('g').attr('transform', "translate(" + (width / 2) + "," + (height / 2) + ")");
/* define a zoom behavior
*/
zoom = d3.behavior.zoom().scaleExtent([1, 10]).on('zoom', function() {
/* whenever the user zooms,
*/
/* modify translation and scale of the zoom group accordingly
*/ return vis.attr('transform', "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")");
});
/* bind the zoom behavior to the main SVG
*/
svg.call(zoom);
/* read flare data
*/
d3.json('flare-imports.json', function(data) {
/* package tree
*/
var defs, depth_color, hierarchy, leaves, nodes, scale, tree;
tree = flare_reader.tree(data);
hierarchy = d3.layout.hierarchy();
nodes = hierarchy(tree);
/* imports links
*/
/* this tree is unordered, we need a canonical ordering for it
*/
tree_utils.canonical_sort(tree);
/* obtain the sequence of leaves
*/
leaves = tree_utils.get_leaves(tree);
/* VISUALIZATION
*/
/* compute the space-filling curve layout
*/
scale = 26;
sfc_layout.displace(leaves, sfc_layout.GOSPER, scale, -Math.PI / 6);
/* compute also the position of internal nodes
*/
sfc_layout.displace_tree(tree);
/* define a bundle layout
*/
/* define a color scale for leaf depth
*/
depth_color = d3.scale.linear().domain([
1, d3.max(leaves, function(d) {
return d.depth;
})
]).range(['#FFF7DB', '#F0A848']).interpolate(d3.interpolateHcl);
/* define a thickness scale for region depth
*/
/* translate size to cell scale
*/
/* translate depth to label font size
*/
/* compute all the internal nodes regions
*/
jigsaw.treemap(tree, scale, jigsaw.HEX_CELL);
/* define the level zero region (the land)
*/
defs = svg.append('defs');
defs.append('path').attr('id', 'land').attr('d', jigsaw.get_svg_path(tree.region));
/* faux land glow (using filters takes too much resources)
*/
map.append('use').attr('class', 'land-glow-outer').attr('xlink:href', '#land');
map.append('use').attr('class', 'land-glow-inner').attr('xlink:href', '#land');
/* draw the cells
*/
map.selectAll('.cell').data(leaves).enter().append('path').attr('class', 'cell').attr('d', jigsaw.hex_generate_svg_path(scale)).attr('transform', function(d) {
return "translate(" + d.x + "," + d.y + ")";
}).attr('fill', function(d) {
return depth_color(d.depth);
}).attr('stroke', 'white');
/* draw the level one region boundaries
*/
/* draw the land border (above cells)
*/
map.append('use').attr('class', 'land-fill').attr('xlink:href', '#land');
/* draw the graph links
*/
/* draw the level one labels
*/
/* draw the leaf labels
*/
return map.selectAll('.label').data(leaves).enter().append('text').attr('class', 'label').attr('font-size', '4px').attr('dy', '0.35em').attr('transform', function(d) {
return "translate(" + d.x + "," + d.y + ")";
}).text(function(d) {
return d.name.split('.').reverse()[0];
});
});
}).call(this);
.land-glow-outer
fill: none
stroke: #EEE
stroke-width: 16px
.land-glow-inner
fill: none
stroke: #DDD
stroke-width: 8px
.land-fill
fill: none
stroke: #444
stroke-width: 1px
.cell
stroke-width: 1px
.region
fill: none
stroke: #444
@font-face
font-family: "Lacuna"
src: url("lacuna.ttf")
.label
fill: #333
text-anchor: middle
pointer-events: none
font-family: "Lacuna"
.graph_link
stroke: teal
stroke-width: 0.05
fill: none
pointer-events: none
window.jigsaw = {
hex_generate_svg_path: (scale) ->
a = scale/2
r = a / Math.sin(Math.PI/3)
return "M#{r} 0 L#{r/2} #{a} L#{-r/2} #{a} L#{-r} 0 L#{-r/2} #{-a} L#{r/2} #{-a} Z"
HEX_CELL: (node, scale) ->
a = scale/2
r = a / Math.sin(Math.PI/3)
region = [[{X:node.x+r, Y:node.y}, {X:node.x+r/2, Y:node.y+a}, {X:node.x-r/2, Y:node.y+a}, {X:node.x-r, Y:node.y}, {X:node.x-r/2, Y:node.y-a}, {X:node.x+r/2, Y:node.y-a}]]
return region
treemap: (node, scale, base) ->
if not node.children?
node.region = base(node, scale)
return node.region
children_paths = (jigsaw.treemap(child, scale, base) for child in node.children).reduce((a, d) -> a.concat(d))
upscale = 100
ClipperLib.JS.ScaleUpPaths(children_paths, upscale)
cpr = new ClipperLib.Clipper()
cpr.AddPaths(children_paths, ClipperLib.PolyType.ptSubject, true)
node.region = new ClipperLib.Paths()
cpr.Execute(ClipperLib.ClipType.ctUnion, node.region, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero)
ClipperLib.JS.ScaleDownPaths(children_paths, upscale)
ClipperLib.JS.ScaleDownPaths(node.region, upscale)
return node.region
### Converts Paths to SVG path string ###
### and scales down the coordinates ###
### from http://jsclipper.sourceforge.net/6.1.3.1/index.html?p=starter_boolean.html ###
get_svg_path: (paths, scale) ->
svgpath = ''
if not scale?
scale = 1
for path in paths
for p, i in path
if i is 0
svgpath += 'M'
else
svgpath += 'L'
svgpath += p.X/scale + ", " + p.Y/scale
svgpath += 'Z'
if svgpath is ''
svgpath = 'M0,0'
return svgpath
}
(function() {
window.jigsaw = {
hex_generate_svg_path: function(scale) {
var a, r;
a = scale / 2;
r = a / Math.sin(Math.PI / 3);
return "M" + r + " 0 L" + (r / 2) + " " + a + " L" + (-r / 2) + " " + a + " L" + (-r) + " 0 L" + (-r / 2) + " " + (-a) + " L" + (r / 2) + " " + (-a) + " Z";
},
HEX_CELL: function(node, scale) {
var a, r, region;
a = scale / 2;
r = a / Math.sin(Math.PI / 3);
region = [
[
{
X: node.x + r,
Y: node.y
}, {
X: node.x + r / 2,
Y: node.y + a
}, {
X: node.x - r / 2,
Y: node.y + a
}, {
X: node.x - r,
Y: node.y
}, {
X: node.x - r / 2,
Y: node.y - a
}, {
X: node.x + r / 2,
Y: node.y - a
}
]
];
return region;
},
treemap: function(node, scale, base) {
var child, children_paths, cpr, upscale;
if (!(node.children != null)) {
node.region = base(node, scale);
return node.region;
}
children_paths = ((function() {
var _i, _len, _ref, _results;
_ref = node.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
_results.push(jigsaw.treemap(child, scale, base));
}
return _results;
})()).reduce(function(a, d) {
return a.concat(d);
});
upscale = 100;
ClipperLib.JS.ScaleUpPaths(children_paths, upscale);
cpr = new ClipperLib.Clipper();
cpr.AddPaths(children_paths, ClipperLib.PolyType.ptSubject, true);
node.region = new ClipperLib.Paths();
cpr.Execute(ClipperLib.ClipType.ctUnion, node.region, ClipperLib.PolyFillType.pftNonZero, ClipperLib.PolyFillType.pftNonZero);
ClipperLib.JS.ScaleDownPaths(children_paths, upscale);
ClipperLib.JS.ScaleDownPaths(node.region, upscale);
return node.region;
},
/* Converts Paths to SVG path string
*/
/* and scales down the coordinates
*/
/* from http://jsclipper.sourceforge.net/6.1.3.1/index.html?p=starter_boolean.html
*/
get_svg_path: function(paths, scale) {
var i, p, path, svgpath, _i, _len, _len2;
svgpath = '';
if (!(scale != null)) scale = 1;
for (_i = 0, _len = paths.length; _i < _len; _i++) {
path = paths[_i];
for (i = 0, _len2 = path.length; i < _len2; i++) {
p = path[i];
if (i === 0) {
svgpath += 'M';
} else {
svgpath += 'L';
}
svgpath += p.X / scale + ", " + p.Y / scale;
}
svgpath += 'Z';
}
if (svgpath === '') svgpath = 'M0,0';
return svgpath;
}
};
}).call(this);
### FIXME update this code to the optimized version ###
### compute a Lindenmayer system given an axiom, a number of steps and rules ###
fractalize = (config) ->
input = config.axiom
for i in [0...config.steps]
output = ''
for char in input
if char of config.rules
output += config.rules[char]
else
output += char
input = output
return output
### execute a curve string and return all the generated points ###
execute = (curve_string, angle, scale, orientation) ->
points = [{x: 0, y: 0}]
for char in curve_string
if char == '+'
orientation += angle
else if char == '-'
orientation -= angle
else if char == 'F'
last_point = points[points.length-1]
points.push {
x: last_point.x + scale * Math.cos(orientation),
y: last_point.y + scale * Math.sin(orientation)
}
return points
### custom base for logarithm (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log) ###
base_log = (x, base) -> Math.log(x) / Math.log(base)
window.sfc_layout = {
GOSPER: {
base: 7
angle: Math.PI/3
axiom: 'A'
rules:
A: 'A+BF++BF-FA--FAFA-BF+'
B: '-FA+BFBF++BF+FA--FA-B'
}
HILBERT: {
base: 4
angle: Math.PI/2
axiom: 'A'
rules:
A: '-BF+AFA+FB-'
B: '+AF-BFB-FA+'
}
displace: (seq, curve_cfg, scale, orientation) ->
scale = if scale? then scale else 10
orientation = if orientation? then orientation else 0
### create the minimal curve that can accommodate the whole sequence ###
steps = Math.ceil(base_log(seq.length, curve_cfg.base))
### generate the Lindenmayer system string for the requested curve ###
curve_string = fractalize
steps: steps
axiom: curve_cfg.axiom
rules: curve_cfg.rules
### execute the string, producing the actual points of the curve ###
curve = execute(curve_string, curve_cfg.angle, scale, orientation)
### stores the coordinates in the given sequence ###
for [d,point] in zip(seq, curve)
d.x = point.x
d.y = point.y
### center the layout coordinates in the center of its bounding box ###
max_x = d3.max(seq, (d)->d.x)
max_y = d3.max(seq, (d)->d.y)
min_x = d3.min(seq, (d)->d.x)
min_y = d3.min(seq, (d)->d.y)
for d in seq
d.x -= (max_x+min_x)/2
d.y -= (max_y+min_y)/2
### recursively assign positions to internal nodes too ###
displace_tree: (node) ->
if not node.children?
return [node]
### an internal node's position is the centroid of its leaf descendants ###
leaf_descendants = (sfc_layout.displace_tree(c) for c in node.children).reduce((a, d) -> a.concat(d))
node.x = d3.mean(leaf_descendants, (d)->d.x)
node.y = d3.mean(leaf_descendants, (d)->d.y)
### pass descendants up to the hierarchy ###
return leaf_descendants
}
/* FIXME update this code to the optimized version
*/
/* compute a Lindenmayer system given an axiom, a number of steps and rules
*/
(function() {
var base_log, execute, fractalize;
fractalize = function(config) {
var char, i, input, output, _i, _len, _ref;
input = config.axiom;
for (i = 0, _ref = config.steps; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
output = '';
for (_i = 0, _len = input.length; _i < _len; _i++) {
char = input[_i];
if (char in config.rules) {
output += config.rules[char];
} else {
output += char;
}
}
input = output;
}
return output;
};
/* execute a curve string and return all the generated points
*/
execute = function(curve_string, angle, scale, orientation) {
var char, last_point, points, _i, _len;
points = [
{
x: 0,
y: 0
}
];
for (_i = 0, _len = curve_string.length; _i < _len; _i++) {
char = curve_string[_i];
if (char === '+') {
orientation += angle;
} else if (char === '-') {
orientation -= angle;
} else if (char === 'F') {
last_point = points[points.length - 1];
points.push({
x: last_point.x + scale * Math.cos(orientation),
y: last_point.y + scale * Math.sin(orientation)
});
}
}
return points;
};
/* custom base for logarithm (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log)
*/
base_log = function(x, base) {
return Math.log(x) / Math.log(base);
};
window.sfc_layout = {
GOSPER: {
base: 7,
angle: Math.PI / 3,
axiom: 'A',
rules: {
A: 'A+BF++BF-FA--FAFA-BF+',
B: '-FA+BFBF++BF+FA--FA-B'
}
},
HILBERT: {
base: 4,
angle: Math.PI / 2,
axiom: 'A',
rules: {
A: '-BF+AFA+FB-',
B: '+AF-BFB-FA+'
}
},
displace: function(seq, curve_cfg, scale, orientation) {
var curve, curve_string, d, max_x, max_y, min_x, min_y, point, steps, _i, _j, _len, _len2, _ref, _ref2, _results;
scale = scale != null ? scale : 10;
orientation = orientation != null ? orientation : 0;
/* create the minimal curve that can accommodate the whole sequence
*/
steps = Math.ceil(base_log(seq.length, curve_cfg.base));
/* generate the Lindenmayer system string for the requested curve
*/
curve_string = fractalize({
steps: steps,
axiom: curve_cfg.axiom,
rules: curve_cfg.rules
});
/* execute the string, producing the actual points of the curve
*/
curve = execute(curve_string, curve_cfg.angle, scale, orientation);
/* stores the coordinates in the given sequence
*/
_ref = zip(seq, curve);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
_ref2 = _ref[_i], d = _ref2[0], point = _ref2[1];
d.x = point.x;
d.y = point.y;
}
/* center the layout coordinates in the center of its bounding box
*/
max_x = d3.max(seq, function(d) {
return d.x;
});
max_y = d3.max(seq, function(d) {
return d.y;
});
min_x = d3.min(seq, function(d) {
return d.x;
});
min_y = d3.min(seq, function(d) {
return d.y;
});
_results = [];
for (_j = 0, _len2 = seq.length; _j < _len2; _j++) {
d = seq[_j];
d.x -= (max_x + min_x) / 2;
_results.push(d.y -= (max_y + min_y) / 2);
}
return _results;
},
/* recursively assign positions to internal nodes too
*/
displace_tree: function(node) {
var c, leaf_descendants;
if (!(node.children != null)) return [node];
/* an internal node's position is the centroid of its leaf descendants
*/
leaf_descendants = ((function() {
var _i, _len, _ref, _results;
_ref = node.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
c = _ref[_i];
_results.push(sfc_layout.displace_tree(c));
}
return _results;
})()).reduce(function(a, d) {
return a.concat(d);
});
node.x = d3.mean(leaf_descendants, function(d) {
return d.x;
});
node.y = d3.mean(leaf_descendants, function(d) {
return d.y;
});
/* pass descendants up to the hierarchy
*/
return leaf_descendants;
}
};
}).call(this);
tcmp = (a,b) ->
children_a = (if a.children? then a.children else [])
children_b = (if b.children? then b.children else [])
for [ai, bi] in zip(children_a,children_b)
ci = tcmp(ai,bi)
if ci isnt 0
return ci
return children_b.length-children_a.length
rsort = (t) ->
children = (if t.children? then t.children else [])
for c in children
rsort(c)
children.sort(tcmp)
window.tree_utils = {
### sort the given unordered tree using a canonical ordering ###
### see Constant time generation of free trees - Wright et al. 1986 ###
canonical_sort: (tree) ->
rsort(tree)
### return the ordered sequence of leaves of a given tree ###
get_leaves: (tree) ->
seq = []
parse_leaves = (node) ->
if not node.children?
seq.push node
else
for c in node.children
parse_leaves(c)
parse_leaves(tree)
return seq
}
(function() {
var rsort, tcmp;
tcmp = function(a, b) {
var ai, bi, children_a, children_b, ci, _i, _len, _ref, _ref2;
children_a = (a.children != null ? a.children : []);
children_b = (b.children != null ? b.children : []);
_ref = zip(children_a, children_b);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
_ref2 = _ref[_i], ai = _ref2[0], bi = _ref2[1];
ci = tcmp(ai, bi);
if (ci !== 0) return ci;
}
return children_b.length - children_a.length;
};
rsort = function(t) {
var c, children, _i, _len;
children = (t.children != null ? t.children : []);
for (_i = 0, _len = children.length; _i < _len; _i++) {
c = children[_i];
rsort(c);
}
return children.sort(tcmp);
};
window.tree_utils = {
/* sort the given unordered tree using a canonical ordering
*/
/* see Constant time generation of free trees - Wright et al. 1986
*/
canonical_sort: function(tree) {
return rsort(tree);
},
/* return the ordered sequence of leaves of a given tree
*/
get_leaves: function(tree) {
var parse_leaves, seq;
seq = [];
parse_leaves = function(node) {
var c, _i, _len, _ref, _results;
if (!(node.children != null)) {
return seq.push(node);
} else {
_ref = node.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
c = _ref[_i];
_results.push(parse_leaves(c));
}
return _results;
}
};
parse_leaves(tree);
return seq;
}
};
}).call(this);
### python-like zip ###
window.zip = () ->
args = [].slice.call(arguments)
shortest = if args.length == 0 then [] else args.reduce(((a,b) ->
if a.length < b.length then a else b
))
return shortest.map(((_,i) ->
args.map((array) -> array[i])
))
/* python-like zip
*/
(function() {
window.zip = function() {
var args, shortest;
args = [].slice.call(arguments);
shortest = args.length === 0 ? [] : args.reduce((function(a, b) {
if (a.length < b.length) {
return a;
} else {
return b;
}
}));
return shortest.map((function(_, i) {
return args.map(function(array) {
return array[i];
});
}));
};
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment