@@ -739,6 +739,228 @@ function d3_layout_hierarchyValue(d) {
739739function d3_layout_hierarchySort ( a , b ) {
740740 return b . value - a . value ;
741741}
742+ d3 . layout . pack = function ( ) {
743+ var hierarchy = d3 . layout . hierarchy ( ) ,
744+ separation = 1 , // spacing
745+ size = [ 1 , 1 ] ;
746+ radius = d3_layout_packRadius ;
747+
748+ function pack ( d , i ) {
749+ var nodes = hierarchy . call ( this , d , i ) ,
750+ root = nodes [ 0 ] ;
751+
752+ d3_layout_packRadii ( nodes , radius ) ;
753+
754+ /* Recursively compute the layout. */
755+ root . x = 0 ;
756+ root . y = 0 ;
757+ root . radius = d3_layout_packTree ( root , separation ) ;
758+
759+ var w = size [ 0 ] ,
760+ h = size [ 1 ] ,
761+ k = 1 / Math . max ( 2 * root . radius / w , 2 * root . radius / h ) ;
762+ d3_layout_packTransform ( root , w / 2 , h / 2 , k ) ;
763+ return nodes ;
764+ }
765+
766+ pack . sort = d3 . rebind ( pack , hierarchy . sort ) ;
767+ pack . children = d3 . rebind ( pack , hierarchy . children ) ;
768+ pack . value = d3 . rebind ( pack , hierarchy . value ) ;
769+
770+ pack . separation = function ( x ) {
771+ if ( ! arguments . length ) return separation ;
772+ separation = x ;
773+ return pack ;
774+ } ;
775+
776+ pack . size = function ( x ) {
777+ if ( ! arguments . length ) return size ;
778+ size = x ;
779+ return pack ;
780+ } ;
781+
782+ pack . radius = function ( x ) {
783+ if ( ! arguments . length ) return radius ;
784+ radius = x ;
785+ return pack ;
786+ } ;
787+
788+ return pack ;
789+ } ;
790+
791+ function d3_layout_packRadius ( d ) {
792+ return d . radius ;
793+ }
794+
795+ function d3_layout_packCircle ( nodes , spacing ) {
796+ var xMin = Infinity ,
797+ xMax = - Infinity ,
798+ yMin = Infinity ,
799+ yMax = - Infinity ,
800+ a , b , c , j , k ;
801+
802+ function bound ( n ) {
803+ xMin = Math . min ( n . x - n . radius , xMin ) ;
804+ xMax = Math . max ( n . x + n . radius , xMax ) ;
805+ yMin = Math . min ( n . y - n . radius , yMin ) ;
806+ yMax = Math . max ( n . y + n . radius , yMax ) ;
807+ }
808+
809+ function insert ( a , b ) {
810+ var c = a . n ;
811+ a . n = b ;
812+ b . p = a ;
813+ b . n = c ;
814+ c . p = b ;
815+ }
816+
817+ function splice ( a , b ) {
818+ a . n = b ;
819+ b . p = a ;
820+ }
821+
822+ function intersects ( a , b ) {
823+ var dx = b . x - a . x ,
824+ dy = b . y - a . y ,
825+ dr = a . radius + b . radius ;
826+ return ( dr * dr - dx * dx - dy * dy ) > .001 ; // within epsilon
827+ }
828+
829+ /* Create first node. */
830+ a = nodes [ 0 ] ;
831+ a . x = - a . radius ;
832+ a . y = 0 ;
833+ bound ( a ) ;
834+
835+ /* Create second node. */
836+ if ( nodes . length > 1 ) {
837+ b = nodes [ 1 ] ;
838+ b . x = b . radius ;
839+ b . y = 0 ;
840+ bound ( b ) ;
841+
842+ /* Create third node and build chain. */
843+ if ( nodes . length > 2 ) {
844+ c = nodes [ 2 ] ;
845+ d3_layout_packPlace ( a , b , c ) ;
846+ bound ( c ) ;
847+ insert ( a , c ) ;
848+ a . p = c ;
849+ insert ( c , b ) ;
850+ b = a . n ;
851+
852+ /* Now iterate through the rest. */
853+ for ( var i = 3 ; i < nodes . length ; i ++ ) {
854+ d3_layout_packPlace ( a , b , c = nodes [ i ] ) ;
855+
856+ /* Search for the closest intersection. */
857+ var isect = 0 , s1 = 1 , s2 = 1 ;
858+ for ( j = b . n ; j != b ; j = j . n , s1 ++ ) {
859+ if ( intersects ( j , c ) ) {
860+ isect = 1 ;
861+ break ;
862+ }
863+ }
864+ if ( isect == 1 ) {
865+ for ( k = a . p ; k != j . p ; k = k . p , s2 ++ ) {
866+ if ( intersects ( k , c ) ) {
867+ if ( s2 < s1 ) {
868+ isect = - 1 ;
869+ j = k ;
870+ }
871+ break ;
872+ }
873+ }
874+ }
875+
876+ /* Update node chain. */
877+ if ( isect == 0 ) {
878+ insert ( a , c ) ;
879+ b = c ;
880+ bound ( c ) ;
881+ } else if ( isect > 0 ) {
882+ splice ( a , j ) ;
883+ b = j ;
884+ i -- ;
885+ } else if ( isect < 0 ) {
886+ splice ( j , b ) ;
887+ a = j ;
888+ i -- ;
889+ }
890+ }
891+ }
892+ }
893+
894+ /* Re-center the circles and return the encompassing radius. */
895+ var cx = ( xMin + xMax ) / 2 ,
896+ cy = ( yMin + yMax ) / 2 ,
897+ cr = 0 ;
898+ for ( var i = 0 ; i < nodes . length ; i ++ ) {
899+ var n = nodes [ i ] ;
900+ n . x -= cx ;
901+ n . y -= cy ;
902+ cr = Math . max ( cr , n . radius + Math . sqrt ( n . x * n . x + n . y * n . y ) ) ;
903+ }
904+ return cr + spacing ;
905+ }
906+
907+ function d3_layout_packTree ( n , spacing ) {
908+ var nodes = [ ] ,
909+ children = n . children ;
910+ if ( children ) {
911+ for ( var i = 0 , l = children . length ; i < l ; i ++ ) {
912+ var c = children [ i ] ;
913+ if ( c . children ) c . radius = d3_layout_packTree ( c , spacing ) ;
914+ c . n = c . p = c ;
915+ nodes . push ( c ) ;
916+ }
917+ }
918+ return d3_layout_packCircle ( nodes , spacing ) ;
919+ }
920+
921+ function d3_layout_packTransform ( n , x , y , k ) {
922+ var children = n . children ;
923+ if ( children ) {
924+ for ( var i = 0 , l = children . length ; i < l ; i ++ ) {
925+ var c = children [ i ] ;
926+ c . x += n . x ;
927+ c . y += n . y ;
928+ d3_layout_packTransform ( c , x , y , k ) ;
929+ }
930+ }
931+ n . x = x + k * n . x ;
932+ n . y = y + k * n . y ;
933+ n . radius *= k ;
934+ }
935+
936+ function d3_layout_packPlace ( a , b , c ) {
937+ var da = b . radius + c . radius ,
938+ db = a . radius + c . radius ,
939+ dx = b . x - a . x ,
940+ dy = b . y - a . y ,
941+ dc = Math . sqrt ( dx * dx + dy * dy ) ,
942+ cos = ( db * db + dc * dc - da * da ) / ( 2 * db * dc ) ,
943+ theta = Math . acos ( cos ) ,
944+ x = cos * db ,
945+ h = Math . sin ( theta ) * db ;
946+ dx /= dc ;
947+ dy /= dc ;
948+ c . x = a . x + x * dx + h * dy ;
949+ c . y = a . y + x * dy - h * dx ;
950+ }
951+
952+ // Computes the radii of the leaf nodes.
953+ // TODO (from Protovis version):
954+ // Is it possible for spacing to operate in pixel space?
955+ // Right now it appears to be multiples of the smallest radius.
956+ function d3_layout_packRadii ( nodes , radius ) {
957+ for ( var i = 0 , n = nodes . length ; i < n ; i ++ ) {
958+ var c = nodes [ i ] ;
959+ if ( ! c . children ) {
960+ c . radius = radius . call ( this , c , i ) ;
961+ }
962+ }
963+ }
742964// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
743965d3 . layout . tree = function ( ) {
744966 var hierarchy = d3 . layout . hierarchy ( ) ,
0 commit comments