# encoding: utf-8 require_relative 'yzz/version' require_relative 'yzz/side' require_relative 'yzz/side_pair' # Yzz implements Ted Nelson's zz (hyperorthogonal, Zig-Zag...) structure. It # provides a mixin that imbues objects with zz properties. # # A zz structure consists of zz objects, which exist in multiple dimensions. Zz # objects can be connected by directed edges -- connections. Connected objects # are called neighbors. Each connection belongs to some dimension. A zz object # is considered as having two sides in each dimension: posward side and # negward side. A connection always points away from the posward side, and # towards the neighbor's negward side. In each dimension, zz object can have at # most one posward and one negward neighbor. A zz object can be connected to # itself, forming a loop. # # To these basic properties, Ted Nelson adds a bunch of other terminology. # A rank is a series of zz objects connected along one dimension. A rank viewed # horizontally is referred to as row. A rank viewed vertically is referred to # as column. # # Mixin defining Zz structure (aka. hyperorthogonal structure). As represented # by YTed::Zz, a Zz structure is a collection of objects, whose connectivity is # defined in a multidimensional space in such way, that each object, along each # dimension, has at most one posward neighbor, and one negward neighbor. The # relation is bijective: If B is a posward neighbor of A along some dimension, # then A must be a negward neighbor of B along that dimension, and vice-versa. # module Yzz # Adds initialization of the @zz_dimensions hash to #initialize. # def initialize *args @zz_dimensions = Hash.new { |ꜧ, missing_dimension| ꜧ[ missing_dimension ] = Yzz::SidePair .new( zz: self, dimension: missing_dimension ) } # initialize the @zz_dimensions hash super # and proceed as usual end # Returns a SidePair instance along the requested dimension. # def along dimension @zz_dimensions[ dimension ] end # Returns all sides actually connected to a zz object. # def connections @zz_dimensions.map { |_, pair| [ pair.negward, pair.posward ] } .reduce( [], :+ ).select { |side| side.neighbor.is_a_zz? } end alias connectivity connections # Returns all neighbors of a zz object. # def neighbors connections.map &:neighbor end # Returns all sides facing another zz object supplied as argument. (Note that # this can be more than 1 side: object A can be connected to B along # more than 1 dimension. # def towards other connectivity.select { |side| side.neighbor == other } end # Prints the labels of the sides facing towards a given zz object. # def tw other puts towards( other ).map &:label end # Short string describing the object. # def to_s "#" end # Inspect string of the object. # def inspect to_s end end class Object def is_a_zz? is_a? ::Yzz # class_complies? ::YTed::Zz end end