Masses and Springs
I'll now detail the foundation of my Masses-and-Springs library. As
I've mentioned before, I've gone through this a number of times. I'm
sure if I went back to my Software Engineering texts, I'd find the
"correct' way of representing the problem, but I think I've done all
right for now.
These are the major players in the system, usually represented by individual threads.
Each of them is linked to the Javadoc-generated documentation for their associated
Space is a collection of Masses and Springs. It is the container that
is filled by the Collector, that is acted upon by the Physics, and is
represented by the Viewer.
The base representation of a node. This might represent a host on a
network, a webpage on a website, a person in a family tree or a file
in a filesystem. Its value as a mass represents its "repulsiveness"
to other Masses, allowing it to push more or less as needed. This can
even be variable, so "popular" nodes push harder to keep the
associated nodes further away (and thus more readable). Masses have
dimensionality, and require a location in Space, thus having an
Vectors represent a location in space, whether a position on a line
(in 1D space), on a plane (in 2D space) or in volume (in 3D space).
While there are mathematical vectors available in Java libraries, I
decided to implement my own for "completeness".
A Spring represents a relationship between two Masses. This might be
a connection between two hosts on a network, a hyperlink on one
webpage to another, a marriage or birth in a family tree or a file
within a directory in a filesystem. Its value represents the
"attractiveness" between the two Masses, perhaps to represent
physical distance or latency in a network. Springs always connect
exactly two Masses. They have no dimensionality themselves, but
they should enforce that their two Masses are of the same dimension.
Collectors are the ones that get the data from an outside source and
into a system. It might be a traceroute program, finding network
links; a web crawler finding links between webpages on a website; a
parser reading a genealogical datafile; or a filesystem reader
traversing a directory structure. Once it has the data, it is
responsible for inserting new Masses into Space, inserting new
Springs, and/or possibly changing existing Masses and Springs, if data
or relationships change.
Physics continuously applies rules to the Masses and Springs. This is
tied closely to the Viewer, as it's usually the Viewer that has
requirements on the distances that objects rest at within Space. All
interaction between Masses and Masses, and Masses and Springs, happen
here -- neither Masses nor Springs know how to interact with each other.
The final representation of the system is seen with the Viewer.
Masses and Springs are repeatedly rendered as they are added in by the
Collector and acted upon by the Physics. Viewers might allow user
interaction, changing zooms, rotating space or pruning trees.
A few other classes make up the base of the system:
This is an extension of Space, but ensures that no Mass or Spring
shares a name with another. If an attempt is made to add an item to
UniqueSpace that has an existing name, the values of the original Mass
or Spring or changed to the new object's.
An extension of UniqueSpace, this environment increases an Mass's mass
every time a new Spring is connected to it. This makes popular Mass's
repel other Masses with more force, thus making it easier to view the
popular Mass amongst its connections.
An extension of Viewer for text-based output. Mainly used for testing
purposes, though I suppose you could write a visualizer using the curses
library if you really wanted to.
Another extension of Viewer, this provides the foundation for a graphical
More of a coding class than an implementation one, this exception is used
when disparate dimensions are used together.
Here is a JAR of the source for the abstract
objects mentioned so far.
©2002-2018 Wayne Pearson