Thursday, June 29, 2006

Right! Got the voronoi working up to 300 cells (blocks) without crashing, and then some very subtle bug creeps in... watch this space.

Here it is working on a tricky case - the blue is the output lines (roads), the red shows line ownership (which block a road is adjacent too) and the green line show cells (which roads make a block). Its in perspective so some of the lines dont meet up. You can appreciate how each cell is cut out of the existing mesh. This mean that its O(n^2) in time complexity (there are some O(n log(n)) algorithms):

This Voronoi implementation overran by a week . My maths and abstract thinking haven't seen daylight since my A levels - the only maths Ive done at bristol is to repeat a prescribed algorithm in graphics. The problem is that to teach computer science Bristol would need to fire most of its lecturers(not really, they're all researchers) and hire teachers and assistants like they do in UCSC. There's no way you can teach the kind of computer science you need to build the next silicon valley with class sizes of 100+ and no tutorials. Bristol labs don't count (the people wearing yellow t shirts are branded lab-monkeys for a reason).

Ranting aside, I have a plan! If I:
  • change the Voronoi to allow points to be weighted (mesh should still be valid) - 10 minute change
  • allow the input shape to be concave - just have to locate one of several bisector inside a shape
Then the voronoi may become a really good tool for laying out floorplans because of its ability to assign all the input space. Watch this space.....
Today I gave up on the voronoi (just for today tho) and worked on a Opengl/JMonkey graphics display system. The Voronoi was a little to much to face again, and the graphics needed updating because the speed of the maya interface was too slow. You control the camera using the WASD keys, and its all in jME so it should be really easy to turn into a full blown game :) The new window is in the bottom right here, all very nicely lit and everything!

Not much to say, and not much progress, but at least its a post today!

Tuesday, June 27, 2006

After solving another few bugs and looking at the result, and things just not doing what they should I started to doubt whether the last 5 days where worth while. The options where
  • Rip some code of the net and get it working - loose 5 days work
  • Carry on working - another day to 3 work
  • Find another way of generating streets - it'd be better to grab someone else's code
  • Implement a different algorithm (say the faster, but curve-math required Fortune's method)
In the end I decdied to carry on working after investigating Fortune's method. It would probably have had as much hidden complexity as my implementation.

So plugging on I improved the debugging code....

Playblast of it working (till it crashes...) of the debugging code running - blue lines are output edges, green edge list and red cell ownership:

Monday, June 26, 2006

Right, new week and fourth new start on the veronoi (curse that name) implementation and I'm a little fustrated to say the least, I have many cool ideas for this project, but little things keep eating up all my time. I have a feeling that this is how life is.

A tricky bulk of the voronoi code involves calling a routine that splits a cell (block) apart to make room for a new block. The routine has to return lots of thing about the state. It could be one monolithic lump of code, but I tried to split it up. The problem is the shared state (variables and return values) is huge. I ended up having a snapshot of the shared state (instanced return variables) as global values in the class, this works, but I'm not convinced there isn't a better way of doing it.

Another problem I stubled across was that because I only followed edges after a bisector, until an unkown cell was found I didn't create lines created in the unknown cell before the bisector was created.

The general algorithm (right) is then:
  • We add each point in turn. The first point owns the whole starting shape / "cell"
  • for each subsequent point we find the cell that it falls into, and that cells centre point.
  • We mark a start point at the start of the bisector of both points.
  • We remove everything on the new points side of the bisecors from the old points cell.
  • We then move onto the next cell (clockwise in my case) in which the bisector ends.
  • This is all well and good untill we meet an edge. Here we follow the painstakingly ( bug-ridden) list of edge pointers until we come across a cell we havn't already seen.
  • We traverse from the point we entered the new cell until we reach the cells bisector, then continue as above.
  • We repeat until we end up where we started.

Friday, June 23, 2006

Right today was another appauling day for productivity, no real progress has been made but I've rewritten the voronoi code twice and its still a mess. I'm beginning to wish I'd chosen the other way of implementing it (Fortune's method). Writing this after the pub and still a way away from having it work, perhaps I'll sit down tomorrow evening and not get up till it works!- or I'll never be able to have a street of houses by Monday.

Thoughts on the floor plan: If the floorplan, at any one height, is just one complete straight skeleton it would allow (really easily) for roofs to combine with walls with changing slope. So we have components, such as "bay window", "main house", "extension", "chimney" arranged in a hierarchy with the biggest at the top. Each part is responsible for deciding any changes in slope (such as the overhang of a balcony or the start of a roof), and makes these changes dynamically
in response to certain events:
  • reaching a certain height
  • being given a "proximity warning" from the component higher up the hierarchy that it is will change slope/perform a discrete change a set height futher up
  • Having a straight skeleton merge event of some kind
Each component is responsible for assigning decorators (I like that name) to each edge in the skeleton. These will be concepts such as "plain brick", "brick with windows", "roof tiles" or "yellow paint". The dectorator can change on any of the above events.

So the user will specify a house base and a bunch of components (say a rectangular house with bay windows, followed by a hatched roof) , modify and dectorators, and then this complex skeletoning procedure will create the house. All co-planar areas with the same decorator will be merged together, before the dectorator creates windows etc.... It'd be cool if the dectorators could interact at corners (say make a continuous brick pattern continue around a bend) but thats 'outside the scope of this project' :(

Thursday, June 22, 2006

Here's the first output from the Voronoi/Road network generator. Its been really slow going and i dont know why... The green are the generating dots (random at the moment) and the blue lines currently just keep dividing each time a dot is added. Not a true veronoi yet, but soon!

Heres another, with some funny boundary cases fixed up and much larger. Implementing the Voronoi is much more work that I thought because of details such as boundary cases. Its gonna take up at least all of tomorrow (makeing 3 days total) too.... :(

Wednesday, June 21, 2006

So kinda a fustrating day today. I didnt get much done, probably a hazzard of working at home. I'm still trying to implmenet a Voronoi algorithm, but got sidetracked into the details. The number of algorithms that exist for clipping a line to the insides of a polygon (a necessary part of voronoi) are huge, and i spent the morning just trying to find one that wasn't too slow, but wouldn't take forever to implment.

In the evening I ened up at a Jazz concert at Girton, tis a reall poncy college (photo top) in Cambridge, somewhere that Wills would of given his back teeth to have in Bristol (but just blends in with general cambridge poncy buildings). It did get me thinking about the roofs in the above photo (while not listening to my little brothers clarinet solo). To build a roof with a slight overhang, all you need is to add a hole to the inside of the roof. Here it is!:

Google video now has to be verified (since i started the blog) but has beter colour reproduction. YouTube has naff colour but no edge aliasing errors.

The other thing that this video shows at the end is the strange effect of two split events at the same location not being treated correctly. It starts to go wrong at the top of the gabled roof, and spreads upwards. It still generate a valid roof, but part of it is upside down. To solve this I either need to ensure that no perfect rectangles are present (unlikely in real architecutre) or spend a day adding code to collect split events...but its not needed now so it'll wait.

Last thing I finally figured out the maths for my parametric line clipper. I think its just a Cyrus and Beck repetition, using GCSE math, but I hadn't done any real thinking about maths for 3 years so it took a while:
It means that the line segments can clipped against cells in the Voronoi. I used the cross product from the two lines to determine if the maximum or minimum parametric value should be set. If, by the end there is no start or end or the min is greater than the max then the line has been clipped out of existance. Its also nicely efficient as it just by keeps track of the max and min paramatric value (t2 above).

Tuesday, June 20, 2006

OK, I lied last night I tried to make a skeleton animation this morning and ran into two more issues (right). They were both really hard to track down, and I'm not sure they are correct for all cases. By lunch time i had a working implementation that would animate nicely. So here it is - you can finally clearly see the effect of changing the weight of a side. The skeleton is simialar to Mayas bevel function, but it treats corners gracefully.

While that was rendering out I was thinking about the roads. The inital road making algorithm to create the roads should be something like:
  • drop many points at regular intervals
  • take each point for a random walk to shuffle them up as much as we want
  • perform a Voronoi (fun link) triangulation routine to create a set of roads from the dots. Set the size of each road.
  • (maybe: perform a down-hill iterative operation over the roads to shufty them around a bit giving preference for large roads that go straight ahead)
  • Find the shape of each block between the roads
  • Shrink the blocks in using my (now with added bug-free(tm)) straight skeleton to leave room for the roads. The weight of each edge change the width of the roads.
  • project the whole thing onto a hillside to make it work in 3D

Hmmm....youTube isn't that good quality. Googles started needing verification for its video (update google video for comparison is here) posts, so its pants for blogs. You can still kind of see the near side edge at the start change as the animation progresses.

Turns out using Voronoi diagrams for streets wasn't as original as I thought it was.

Monday, June 19, 2006

Right, eventually found the error causing my line intersections to be asymmetrical - it was a missing minus sign in the library code I wrote while I was really ill 3 months ago. This is why computer science degrees are more time consuming (or time-wasting) than most. If you miss that minus sign its your 5 hours trying to find it. Tis also why good programmers are orders of magnitude better than bad ones- if you know your going to have to find a needle in a haystack, it had better be a well designed haystack.

The next modification is to fix the issue of escaping bisectors (roof edges). If they hit an opposite edge at exactly the right place, between intersections (as happens with regular roofs), the standard intersection fails every so often. The text book way to do this is to project the ray-plane problem into 2d (by dropping a dimension) and using the Jordan curve idea. The standard implementation (Alan Chalmer-esque) doesn't deal with borderline cases (where the ray just grazes the plane) in a reliable manner. This diagram shows modifications for how I think it should work.

The next of my grand schemes to sink was the algorithm i wrote about last Thursday, to convert the 2D skeleton to 3D planes. The problem was to make the right choice at each vertex, without resorting to doing minimum angle computations (which would of had to be in 3D for 0-weighted camp skeleton edges). Linking from one point to the next always in an uphill direction is fine, untill we consider split events, where there are multiple ups and downs throughout an edge. The alteration was to keep a hashtable of dots and lists of dots, indexing the possible roof edges at each vertex. Then it was simple to get each edge to store a list of related points, and then traverse. Ill be keeping the Thursday data structures tho - they let someone correlate output points to input points, and I have another grand design for that scrap of data.

The next technical thing to come up was that when a split operation occurs (when a bisector reaches another face before colliding with either of the bisectors on either side), it splits the opposite edge into two. This is well described by Felkel (see my inset, right), but needed modifying because Felkel used lists of points that constitute a circuit of points. In reality this is unescessary and I just use a big ol' hashSet.

If its not obvious all this corelDraw pics are what I plan to use as illustrations in the writeup! Its often easier to create the pretty pictures first and then make the code changes as it makes you think about the details.

As ever I didn't get the skeleton quite as far as I wanted. It should have been possible to create them with non-planar bases, especially since most of my maths is already in 3D, but it'd be new work (havn't found it documented anywhere) so theres bound to be mines. It'd be really useful for creating the ground floor of a house on a hill, or projecting windows or butresses onto walls that change their slope half way up.

Onwards and upwards (just as long as its away from another week of maths) - tomorrow I'll start on the street layout and making a hundred houses at a time. Buzzwords I expect to use tomorrow: random-walk, point cloud, downhill search, camp skeleton,Voronoi polygons.

One final thought is my ponderings on how to construct floorplan procedurally to include details such as bay windows.
  • A good idea already out there is to take a floorplan that is already union'ed bunch of primitive shapes and grow it upwards. This would mean knowing what we wanted as the building was starting out.
  • The alternative is to start with a concave building 'core' and for each face project extensions, such as another wing, butress or balcony onto it from the side of the building. This would make it much harder to fit the building onto a plot (top-down), but make more elaborate features easier.
Both are doable, and I'm leaning towards the first idea. This'll be the week after this' work.

Friday, June 16, 2006

First up fixing regular shapes. The problem is that where more than two skeleton edges meet at one point (eg: top of a square pyramid), their order is non-determinstic("random") to the algorithm. So with a regular octagon we have no idea which edges are going to form next. The solution turned out to be to terminate the algorithm when the two top points where very close (for some definition of very).

Now concave shapes...As Felkel explains when a roof is concave sometimes the bisector from a concave point runs into an opposing edge before colliding with either of its neightbours. This has the effect of splitting the remaining polygon into two seperate loops of points.

I look for these intersections as lines intersecting with quads(see adv comp graphics, jordan curve, yada yada....). Problem is there can be a lot of quads, have to test each point against all other lines, leaving this stage of the algorithmO(n*n). This method has to be really bullet proof to be useful as we may expect it to close holes in a surface that have negative edge weights.

The quads are formed by the bisectors and may well be infinite (for example a vertical face, or one with bisectors moving away from each other). But as long as the collision happens a very long way away we dont care about it, so we can just make the planes very, very big and not worry about the limiting case.

Getting this up and going is a real timewaster with picky border conditions. For example the bisectors must intersect with edges in all borderline cases, so that it cant escape between two edges. It'll probably take monday (at least) to finish this, a bit depressing really.

Thursday, June 15, 2006

Here's my first blog video! WooWoo... Its my debugging render of the straight skeleton algorithm running on a non-regular octagon.

  • Yellow is the output skeleton

  • The red arrows show the list of active points still to be processed

  • Green arrows show the current edge assignment of each point (eg which two edges collide at this points bisector)

  • Blue arrows show the bisector of each point

Its only a maya playblast - I have no idea how to render colour in maya when you only have Vertex colours(Edit Polygon-> Colors-> Apply Color, but only grey when you render). But the time spent putting this together has already saved itself in debugging time. I've spotted two subtle but critical flaws in previous versions.

Again, but with weighted edges (a camp skeleton). The weight determines the speed or steepness of each edge. Note the bottom left where the skeleton goes outside of the base polygon due to a negative weight.

It even works with edges with 0 speed (as long as one edge has speed). Setting one edge to have a 0 speed allows a cross-gabled roof. Setting all but one edge to zero speed creates a flat-roof, with all the other sections vertical. The plan is to merge these vertical sections into the walls to create gabled rooves with windows in the gable.

This evening (took the afternoon off):

To convert the skeletons (yellow lines in the above animations) to solid roofs(hehe, had to look up the plural of roof there), we have to traverse the 2d heap of lines in some way. All the papers I read describe a method of using the minimum interior angle in a 2D projection, however this wont work with the vertical walls as the 2D projection will have the roof ridges ontop of the vertical 'wall' edges. By keeping track of the direction of each line in the skeleton (so we know which way is uphill) we can start from the two known corners of an output face (adjoinging edge points on the input shape) and work our way up the edges until they meet. Its important to ensure that the two sides of an output face dont miss each other, so we step them -
  1. the first side moves up one edge on the output face
  2. the second side moves up edges until it reaches the same corner as the first side (or very close, if so then done) or finds a corner higher than the first.
  3. the first side moves up edges until it reaches the same corner as the second side (or very close, if so then done) or finds a corner higher than the second.
  4. return to 2. and reapeat
By storing all the pairs of edges in a hash table, we have a data structure that will take a lower point and return the next point up on a roof edge (edges only merge as you move upwards, they dont split) and be hot-turd fast (not that speed has anything to do with this).

The difficult thing here is dealing with the very small lines between output vertices in regular shapes, such as the top of a square based pyramid. Sometimes tie-breaking is required when, while climbing the two edges get to a common height that isn't the same point.

Assigning directions to lines is done arbirarily when the line is level. It is of little consequence which way the line is traversed.

The irregular skeleton above comes out easily of this:

Tommorow the plan is to fix the bit that converts the skeleton to 3D so it works with regular shapes. Then work on getting the skeleton to work on concave shapes. This means testing each bisector against each opposing face to find if it ever splits it before intersecting with any of its neighbours. That'll probably take the last two days of my (monday-centric) week. I was hoping to have a day to work on getting the base non-planar, but as ever on this project its time to move on...

Wednesday, June 14, 2006

First thing I started working on collisions between the straight skeleton bisectors (interior roof edges). Finding the place where two lines cross (in front of a pair of points) is easy, as is returning large values or null when the lines are , or almost are, parallel. The fun starts when the lines are coincident (on top of each other, so they are parallel as well). If the lines go in opposite directions they can't collide, but otherwise they might. If they are both going in the same direction one may catch up and overtake another, and if they are going towards each other then they meet somewhere in the middle. Where they meet is then just 'two trains leave moscow and prague at 12pm and 6pm...' type maths.

All this complication with speeds is because I'm trying for a weighted straight skeleton (which isn't documented anywhere except in Eppstein's paper). This means that the edges of the roof have different speeds. Does this mean I get to name it? If I do then it's a camp skeleton forevermore.

I implemented line-on-line collisions. Comming up with the following to simplify the problem of colliding co-incident lines and allowing the input shape to be non-planar(would be mega-cool):

Then I set up a queue to contain the collision events and had it return the event with the lowest height. Finally a bit of work on a graphical debugger finished it all off.

Tuesday, June 13, 2006

Spent this morning finishing up the GUI, couldn't stand those nasty boxes. Then I read some more papers on the straight skeleton - Felkel's and Eppstein's were both useful. Then I looked at the 2K of code I thought I already had, on first impressions it was bad. An hour of working with it later I thought it was *really* bad, programming when your not really able to concentrate is a waste of time.

So I burned all that code and set to work on a new, cleaner version with an emphasis on debugging tools. The first one I wrong displays the bisectors of each edge:

The shape I'm trying to create the skeleton for is in yellow. The skinny triangles show the starting angles (bisectors) for the skeleton from each corner. Its all outputing in maya ( i got it output coloured vertices finally!) because its much easier to examine the results from all angles.

Next stage is to re implement collisions. The idea is that we work up the height of the roof finding when these bisectors interact with each other. Its really difficult because of all the borderline cases - what happens when two lines are co-incident? when do they cross? what happens when two events happen at the same height? this and many more pointless questions to be answered in tomorrow...

Monday, June 12, 2006

A bit of tidying up of the GUI came first, fixing the judder that happened when you switched to another object and allowing the waterfalls to be deleted when they were dragged off the screen

Found out that (of course) the Class type in Java 1.5 is generic!

Fixed the palette so that when you drag a connector line from the output of a water fall and drop it, it opens the palette. This shows only the valid classes to add at this position. To find the classes it trawls the Waterfall classes to find which extend the correct interface.

I hope to get around to creating a demo file (a .jar) that can be run outside of eclipse soon

Learnt a new bit of java - variable length argument lists. Allowed a really nice reflection wrapper to be written:

private void setup(Class c, Object... arguments)

Class[] ca = new Class[arguments.length];
for (int i = 0; i 〈 arguments.length; i++)
ca[i] = arguments[i].getClass();
Constructor constructor = c.getConstructor(ca);
o = (T) createObject(constructor, arguments);
catch (NoSuchMethodException e)

Then other methods in Mojo can call function. Great, very time saving for the huge quantity of reflection going on to create the GUI.

Little dissapointed that there wasn't time to finish the gui in its entirity. I had some neat ideas for on-the-fly model previews. You could select a node and have the display show just that node. Maybe if I have a spare day.

It'd also be great to have it use a 3rd party look and feel like substance -would mean that you couldn't tell it was java ;)

5 days, 5K lines of code, not bad going - but it is a gui and all the reflection saved time (at the expense of lots of swearing, java was never built to handle reflection)

Tommorow, the start of the second week I'll quickly clean up the uglyest GUI glitches, and then attack the straight skeleton problem. I really got bogged down in this last time so I might rewrite the class carefully(this time it might be 1K lines over a week as the maths kicks in!). The first thing to do is go and re-read some of the old papers ive collected on skeleton implementations. Its a very simple idea, but getting all the borderline conditions just right is a nightmare.

So skeleton start tomorrow, and by next tuesday hopefully I can start on getting some real results out there....

Saturday, June 10, 2006

While trying to get a game up and running I kept getting a similar problem with my graphics driver after reading up on it the JME forum recommended the omega drivers. These now have Sity up and running on my XP box, so now its a proper XP/Linux/Mac cross platform app :).

I hope people doesn't realise I'm playing with graphics drivers and not watching England...

Friday, June 09, 2006

First an explination of whats what in todays picture - The big squares are the waterfall, the little square on the top is a plug (they all have one these except the root). The little squares on the bottom are sockets (zero to many of these. You can plug multiple plugs into one socket and assign probabilities, eg probability of having a mansard or gabled roof ontop of a particular type of floor layout.

Well, four days in and I've made good progress on the GUI. It feels quite polished now (despite lack of polishing). Today I added the ability to make and break links between components(waterfalls). You can also set the probabilities for each subsequent compent using a nice slider sytem. This updates the colour of the lines to show which of the subsequent components is most likely.

I also had the idea of colouring the plugs to show what could be plugged into what. The idea is that you could have a doorstep profile creator that returns a plug of type line. The door then has a line socket. This means that you cant plug a roof into a door door-step profile(good!). Its quite sucessful, but I'm not sure it will work all the way because the type system is based on hierarchical classes extending one another, so you could put a green plug into a red socket, but not a blue plug into a red socket. I wonder if theres an easy way to show inference through colours

Next step is to create the GUI parameters for each waterfall. Which parameters a waterfall has are coded into it:

public ProbInt VAR_numberSections = new ProbInt(1,Integer.MAX_VALUE, 0, 1);
public String DEF_numberSections = "number of floorspace segments joined together";

public ProbDouble VAR_story = new ProbDouble(0.1,100,0.5,1);
public String DEF_story = "how tall is one story";

Using java's funky reflection i'm gonna extract straight out of the code and create a properties panel for each waterfall without having to code them up in GUI/swing stuff. It'll be neat if it works. As I work on this I also have to figure out a way to save and load the waterfalls so I might do that at the same time.

Thursday, June 08, 2006

This morning was spent on creating the links between the waterfalls in the graph edit (had to go back to scratch in a few places), also there is now a nice pop up contect menu and the probability selection slider is up and working. It'd be really nice to make the context menu close when the mouse leaves it - i'm not sure it really worth the time.

I got the links moving around, but then i headed into trouble. Trying the system on my mac i got a strange error.... This site details the problem with a new version of the libraries (it was only posted today!) This ment straying into the world of inter thread communications, trying to synchronise java's swing interface and jMonkeys output (you can only query the graphics state from within the render thread). After this I got the links moving around, this was just graphics trickery, but gives the thing a polished feel.

Theres a big issue that the code brings down my XP box at the moment. This is really worrying since its Java, albeit with a lot of native graphics calls. Hopefully its just my old XP box getting hot in this weather!

Blogspot keeps going down in the afternoons, its like its being dossed or something...

Wednesday, June 07, 2006

Started off getting JMonkey (3D interface) to work within Javas swing environment. This was a bit of a mess and took a bit of hacking together of the JME and Swing API's. I eventually got it able to open more than one 3d window within one swing application. It was a drag that

They should let the labs get warmer when its hot outside - the air conditioning is freezing in here.

Picture is of hypergraph/'waterfall' system. All done in a 3D engine for opengl niceness of graphics (and dev speed)!

After lunch I made storming progress getting the Maya-like hypergraph up and running. You can select and move components (known as waterfalls). It even draws nice curves between them (but doesn't yet move the curves as you move the waterfalls). The screen shot from today is really pretty.

The idea is to have three windows, one of this waterfall view, one of the predicted output from the currently selected node (eg if your working on a window it shows typical designes that the tree from that point forward would generate) and one with boring load/save etc... buttons. The options for the waterfall will appear in this last window when the waterfall is selected

  • Make the curves/links/'flows' visible over the waterfalls
  • Make the curves move with the waterfalls
  • Make it possible to make and break flows in the GUI
  • Add some text to the waterfalls
  • Create the reflection code that will generate the options for each waterfall
  • [if time: create a grouping/boxing mechanism...]
  • Saving and loading a waterfall structure (and all associated options) comes next...

Dunno why blogspot was down all afternoon, but heres the post!

Tuesday, June 06, 2006

Right! Here we go. Today I:

I'm having a go using a time tracking tool called task coach, just because I'm very aware of how much time i waste in development being a perfectionist where its not really called for. I might even post the logs of how much time I spend on what on this blog.

Had a chat with Neill about the project. Had a suggestion about using a shrunk mesh to position the windows of a house. Still no ideas about creating symettry in an L System. Its gonna be a real achievement to get that going.

Neill agreed that the problem of adding drain pipes contradicts the concept of an L System as they go across rooftops, down walls and around windows. Again the idea of adding them on later using a spider-like search that starts at the guttering and seeks out the fastest way to the 'floor'? This means that connected walls need to know who they are connected to. Should be possible to get this information from the straight skeleton.

set up the JMonkey Engine on Linux - this is a scenegaph based game engine similar to java3d (but good and fast) to replace Maya as outputting through MEL is a nightmare for debugging. It should fly along on my laptop now :) Got the whole thing going with normals (normals on the eaves under the roof sections are inverted)

Plan for tomorrow is to get JMonkey cooperating with key input (it just hogs the mouse and System.exits() when a jmonkey window is closed). Then in some order:
  • add loadable and saveable components
  • display the waterfall components
  • add interface to output to JMonkey preview or Maya with a click of a mouse
  • remove the "random" element (or at least make it an option)
  • make the waterfall component/hypergraphy bit in 3d (big job)
  • use reflection to display the ways to tweak a component