Object subclass: #CVSid instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !CVSid commentStamp: 'Brian Tabone 10/27/2004 12:04' prior: 0! Just a class to hold the CVS Id in a string, for InformationSpace CVSid asString. ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! CVSid class instanceVariableNames: ''! !CVSid class methodsFor: 'fileIn/Out'! asString "Send CVS ID string of InformationSpace. We file in/out the whole kit, so this ID should pertain to all classes in InformationSpace" | | ^ '$Id: InformationSpace.st,v 1.33 2005/03/07 00:43:48 briant Exp $'! ! Object subclass: #CausalBit instanceVariableNames: 'xPoint yPoint zPoint onOffBool generationInt stateHistoryDictionary initialCollectionSize ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !CausalBit commentStamp: 'Brian Tabone 8/31/2004 23:42' prior: 0! This is the object representing a bit in the number space. This object supports up to 3 dimensions in space. Structure: xPoint: Location on the X line yPoint: Location on the Y line zPoint: Location on the Z line onOffBool: Bit state (On or Off) generationInt: which generation are we in (Starts out at 0) stateFileName: Data file to which we write our state after each generation calculation This class defines the objects that represent points in the number space. Each object has a location and a state! !CausalBit methodsFor: 'accessing'! asString "String representation of this bit object" | bitString | bitString := Text new. bitString append:generationInt asString; append:','; append:xPoint asString; append:','; append:yPoint asString; append:','; append:zPoint asString; append:','; append:onOffBool asString. ^bitString. ! ! !CausalBit methodsFor: 'accessing'! getGeneration "Returns the generation of this bit state represents" ^ generationInt! ! !CausalBit methodsFor: 'accessing'! getPosition "Returns a dictionary with X, Y, Z elements and an integer value for each " | positionCollection | positionCollection _ Dictionary new. positionCollection at: 'X' put: xPoint. positionCollection at: 'Y' put: yPoint. positionCollection at: 'Z' put: zPoint. ^positionCollection.! ! !CausalBit methodsFor: 'accessing'! getState "Returns the current state of this bit" ^ self getStateAtGeneration: generationInt.! ! !CausalBit methodsFor: 'accessing'! getStateAtGeneration: genINT "Returns the state of this bit for the specified generation" genINT > generationInt ifTrue: ["If we ask for a generation that is past our current generation, return our last known state, This pertains to edge bits which never get updated,they remain their original state since they have no neighbors mapped into our world" ^ stateHistoryDictionary at: generationInt] ifFalse: [^ stateHistoryDictionary at: genINT]! ! !CausalBit methodsFor: 'accessing'! setInitialState: onOffBoolean "Sets the initial state of this bit" onOffBool _ onOffBoolean. stateHistoryDictionary at: 1 put: onOffBool! ! !CausalBit methodsFor: 'accessing'! setState: onOffBoolean "Sets the state of this bit, increments our generation count as well" onOffBool _ onOffBoolean. generationInt _ generationInt + 1. "Add the updated state to state history dictionary" "If we are past the initial generation size, call addLast on the ordered collection" generationInt > initialCollectionSize ifTrue: [stateHistoryDictionary addLast: onOffBool] ifFalse: [stateHistoryDictionary at: generationInt put: onOffBool]! ! !CausalBit methodsFor: 'accessing'! setX:xPosInt "Set the X position of this Bit" xPoint := xPosInt. ! ! !CausalBit methodsFor: 'accessing'! setY: yPosInt "Set the Y position of this Bit" yPoint _ yPosInt! ! !CausalBit methodsFor: 'accessing'! setZ: zPosInt "Set the Z position of this Bit" zPoint _ zPosInt! ! !CausalBit methodsFor: 'initialize'! initialize: initialStateBool xPos: xPosInt yPos: yPosInt zPos: zPosInt "Set the new bit element with the passed in settings" "If our history dictionary isn't nil, we've already been initialized. The user must create a new object, this one should not be re-initialized. This is to encourage the keeping of state histories, so that no accidental holes are made in the information space. If the user wants a new bit for this location, they need to explicitly create a new one and initialize it" stateHistoryDictionary = nil ifFalse: [Error signal: 'Can not re-initialize this object, a new object must be created']. initialCollectionSize := 50. onOffBool _ initialStateBool. generationInt _ 1. "stateHistoryDictionary _ Dictionary new." stateHistoryDictionary _ OrderedCollection ofSize: initialCollectionSize. "Put our initial state in the state history dictionary" stateHistoryDictionary at: generationInt put: onOffBool. xPoint _ xPosInt. yPoint _ yPosInt. zPoint _ zPosInt! ! Object subclass: #CausalLigature instanceVariableNames: 'bitBlockDictionary ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !CausalLigature commentStamp: 'Brian Tabone 10/27/2004 12:03' prior: 0! This is where the meat of the work happens in InformationSpace, Our causal ligature, coupled with the initial conditions, determine the shape of the information space (How the bits are laid out in space time).! !CausalLigature methodsFor: 'accessing'! addNeighborBit: neighborBitObject atTheta: theta Radius: radius polarZ: polarZint "Add a neighbor bit using polar coordinates relative to the root bit" "Theta must be a multiple of 45" | XYplaneDictionary | theta \\ 45 = 0 ifFalse: [Error signal: 'Theta must be a multiple of 45 degrees']. theta < 0 ifTrue: [Error signal: 'Theta must be a positive angle']. theta > 360 ifTrue: [Error signal: 'Theta must be between 0 and 360']. polarZint > 1 ifTrue: [Error signal: 'Only neighbor bits may be added, z must be -1, 0, or 1']. polarZint < -1 ifTrue: [Error signal: 'Only neighbor bits may be added, z must be -1, 0, or 1']. radius > 1 ifTrue: [Error signal: 'Only neighbor bits may be added, r must be 0, or 1']. radius < 0 ifTrue: [Error signal: 'Only neighbor bits may be added, r must be 0, or 1']. "Attach the bit objects into the bit block, at the specified xy plane" XYplaneDictionary _ bitBlockDictionary at: polarZint + 2. radius = 0 ifTrue: [XYplaneDictionary at: 1 put: neighborBitObject] ifFalse: [XYplaneDictionary at: (theta / 45 + 2) asInteger put: neighborBitObject]! ! !CausalLigature methodsFor: 'accessing'! getNextGeneration:currentGenerationInt "Calculate the bit state for the root bit, for the next generation after the specified generation" "This method should be over ridden by a subclass, this where the meat of the work happens for InformationSpace" self subclassResponsibility.! ! !CausalLigature methodsFor: 'accessing'! linkUpRootBit: rootBitObject "Connect all of the causal bits for this cycle We assume polar coords, relative to the root bit" bitBlockDictionary _ OrderedCollection ofSize:3. bitBlockDictionary at: 1 put: (OrderedCollection ofSize:9). bitBlockDictionary at: 2 put: (OrderedCollection ofSize:9). bitBlockDictionary at: 3 put: (OrderedCollection ofSize:9). self addNeighborBit: rootBitObject atTheta: 0 Radius: 0 polarZ: 0! ! CausalLigature subclass: #GameOfLife instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !GameOfLife commentStamp: 'Brian Tabone 10/27/2004 12:03' prior: 0! John Conway's game of life. ! !GameOfLife methodsFor: 'accessing'! getNextGeneration: currentGenerationInt "Calculate the bit state for the root bit, for the next generation after the specified generation. This causal ligature is the game of life by John Conway. The rules are as follows:(Taken from http://www.tech.org/~stuart/life/rules.html) The Rules The Game of Life was invented by John Conway (as you might have gathered). The game is played on a field of cells, each of which has eight neighbors (adjacent cells). A cell is either occupied (by an organism) or not. The rules for deriving a generation from the previous one are these: Death If an occupied cell has 0, 1, 4, 5, 6, 7, or 8 occupied neighbors, the organism dies (0, 1: of loneliness; 4 thru 8: of overcrowding). Survival If an occupied cell has two or three neighbors, the organism survives to the next generation. Birth If an unoccupied cell has three occupied neighbors, it becomes occupied." | rootBit liveNeighborCount XYplaneDict | liveNeighborCount _ 0. XYplaneDict _ bitBlockDictionary at: 2. rootBit _ XYplaneDict at: 1. XYplaneDict collect: [:neighborBit | neighborBit == rootBit ifFalse: [(neighborBit getStateAtGeneration: currentGenerationInt) = true ifTrue: [liveNeighborCount _ liveNeighborCount + 1]]]. (rootBit getStateAtGeneration: currentGenerationInt) = true ifTrue: ["Do we survive or die in the next generation" liveNeighborCount = 2 | (liveNeighborCount = 3) ifTrue: ["Bit surivives into the next gen" rootBit setState: true] ifFalse: ["Bit dies off" rootBit setState: false]] ifFalse: ["Do we come to life?" liveNeighborCount = 3 ifTrue: [rootBit setState: true] ifFalse: ["Even though the state is already false, we must explicitly set the state of the next generation" rootBit setState: false]]! ! BorderedMorph subclass: #InformationSpaceController instanceVariableNames: 'logFileText patternFileText maxGenerationsText startGenText endGenText spaceSizeText runTimeLabel exploreButtonLockout visibleGensText ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !InformationSpaceController commentStamp: 'Brian Tabone 10/27/2004 12:01' prior: 0! This is the class that creates a Morphic UI that controlls the InformationSpace explorer and visualizer. This makes usage easier. InformationSpaceController openInWorld. ! !InformationSpaceController methodsFor: 'private'! privateAddDivider "Put a divider morph between the visualizer side and the exploration side" | dividerMorph | dividerMorph _ Morph new. dividerMorph initialize. dividerMorph position: self position + (200 @ 5). dividerMorph extent: 1 @ 220. dividerMorph color: Color gray. self addMorph: dividerMorph! ]style[(17 2 74 3 13 4 12 3 5 7 12 14 12 11 4 19 1 4 12 9 1 3 3 3 12 8 5 8 4 11 12)f1b,f1,f1c128026000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c178178102,f1,f1cblue;i,f1,f1c178178102,f1,f1c178178102,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i! ! !InformationSpaceController methodsFor: 'private'! privateChooseFile: targetTextBox suffix: suffix "Use a FileList2 to choose a file and populate its name in the textBox passed in" | loadFile suffixArray | suffixArray _ Array new: 1. suffixArray at: 1 put: suffix. loadFile _ FileList2 modalFileSelectorForSuffixes: suffixArray. loadFile = nil ifTrue: [^ self]. targetTextBox contents: loadFile fullName wrappedTo: 158. "The FileList opens the file, so lets close it here, there is probably a more effecient way to do this, but I don't want to directly connect the file handle to the filehandle we use to read in the file, in case the user edits and changes the name in the text box" loadFile close! ! !InformationSpaceController methodsFor: 'private'! privateCreateButtons "Put all the buttons on our form, connect all our targets. All the button making love happens here" | visualizeButton exploreButton chooseLogFileButton choosePatternFileButton logFileArgs patternFileArgs animateButton | logFileArgs _ Array new: 2. patternFileArgs _ Array new: 2. logFileArgs at: 1 put: logFileText; at: 2 put: 'log'. patternFileArgs at: 1 put: patternFileText; at: 2 put: 'txt'. visualizeButton _ SimpleButtonMorph new. exploreButton _ SimpleButtonMorph new. chooseLogFileButton _ SimpleButtonMorph new. choosePatternFileButton _ SimpleButtonMorph new. animateButton _ SimpleButtonMorph new. chooseLogFileButton initialize. visualizeButton initialize. exploreButton initialize. animateButton initialize. choosePatternFileButton initialize. chooseLogFileButton position: self position + (178 @ 50). choosePatternFileButton position: self position + (382 @ 50). visualizeButton position: self position + (115 @ 200). exploreButton position: visualizeButton position + (220 @ 0). animateButton position: self position + (15 @ 200). "Setup our button labels" chooseLogFileButton label: '^'. choosePatternFileButton label: '^'. visualizeButton label: 'Visualize'. exploreButton label: 'Explore'. animateButton label: 'Animate'. "Setup button colors" chooseLogFileButton color: (Color fromArray: #(0.439 0.658 0.976 )). choosePatternFileButton color: (Color fromArray: #(0.439 0.658 0.976 )). visualizeButton color: Color red. exploreButton color: Color yellow. animateButton color: (Color fromArray: #(0.3 0.75 0.976 )). visualizeButton addDropShadow. exploreButton addDropShadow. animateButton addDropShadow. "Setup the button targets" visualizeButton target: self. visualizeButton actionSelector: #privateLaunchCroquetVisualization. visualizeButton arguments: #(). exploreButton target: self. exploreButton actionSelector: #privateLaunchExploration. exploreButton arguments: #(). animateButton target: self. animateButton actionSelector: #privateLaunchCroquetAnimation. animateButton arguments: #(). chooseLogFileButton target: self. chooseLogFileButton actionSelector: #privateChooseFile:suffix:. chooseLogFileButton arguments: logFileArgs. choosePatternFileButton target: self. choosePatternFileButton actionSelector: #privateChooseFile:suffix:. choosePatternFileButton arguments: patternFileArgs. "Setup the button mouseover help balloons" visualizeButton setBalloonText: 'Run the Croquet based Visualizer on the data in the log file'. exploreButton setBalloonText: 'Run the explorer using the pattern file as initial conditions, writing results into the log file'. chooseLogFileButton setBalloonText: 'Click to choose a log file from a File Dialogue'. choosePatternFileButton setBalloonText: 'Click to choose a pattern file from a File Dialogue'. animateButton setBalloonText: 'Open a croque visualization and start an animation from start to end gen, visualizing only visible gens at a time '. self addMorph: visualizeButton; addMorph: exploreButton; addMorph: chooseLogFileButton; addMorph: choosePatternFileButton; addMorph: animateButton! ! !InformationSpaceController methodsFor: 'private' stamp: 'Brian T 11/9/2004 23:49'! privateCreateLabels "Put all the text boxes on our form" "Build our labels" | visualizerLabel explorerLabel logLabel patternLabel startGenLabel endGenLabel maxGenerationsLabel spaceSizeLabel visibleGensLabel | logLabel _ StringMorph new. logLabel initialize. logLabel contents: 'Log Filename'. logLabel position: self position + (15 @ 35). visualizerLabel _ StringMorph new. visualizerLabel initialize. visualizerLabel contents: 'Visualizer'. visualizerLabel position: self position + (65 @ 10). visualizerLabel labelColor: Color blue. explorerLabel _ StringMorph new. explorerLabel initialize. explorerLabel contents: 'Explorer'. explorerLabel position: self position + (280 @ 10). explorerLabel labelColor: Color blue. patternLabel _ StringMorph new. patternLabel initialize. patternLabel contents: 'Pattern Filename'. patternLabel position: self position + (220 @ 35). visibleGensLabel _ StringMorph new. visibleGensLabel initialize. visibleGensLabel contents: 'Visible gens'. visibleGensLabel position: self position + (15 @ 130). maxGenerationsLabel _ StringMorph new. maxGenerationsLabel initialize. maxGenerationsLabel contents: 'Num gens to link'. maxGenerationsLabel position: self position + (220 @ 90). startGenLabel _ StringMorph new. startGenLabel initialize. startGenLabel contents: 'From gen'. startGenLabel position: self position + (15 @ 90). endGenLabel _ StringMorph new. endGenLabel initialize. endGenLabel contents: 'To gen'. endGenLabel position: self position + (90 @ 90). spaceSizeLabel _ StringMorph new. spaceSizeLabel initialize. spaceSizeLabel contents: 'Grid Size'. spaceSizeLabel position: self position + (220 @ 130). self addMorph: logLabel; addMorph: patternLabel; addMorph: maxGenerationsLabel; addMorph: explorerLabel; addMorph: visualizerLabel; addMorph: startGenLabel; addMorph: endGenLabel; addMorph: spaceSizeLabel; addMorph:visibleGensLabel.! ]style[(19 2 56 3 130 4 8 3 11 7 8 14 8 11 14 3 8 11 4 13 2 3 2 4 15 3 11 7 15 14 15 11 12 3 15 11 4 13 2 3 2 4 15 13 5 8 13 3 11 7 13 14 13 11 10 3 13 11 4 13 3 3 2 4 13 13 5 8 12 3 11 7 12 14 12 11 18 3 12 11 4 13 3 3 2 4 16 3 11 7 16 14 16 11 14 3 16 11 4 25 19 3 11 7 19 14 19 11 18 3 19 11 4 13 3 3 2 4 13 3 11 7 13 14 13 11 10 3 13 11 4 13 2 3 2 4 11 3 11 7 11 14 11 11 8 3 11 11 4 13 2 3 2 4 14 3 11 7 14 14 14 11 11 3 14 11 4 13 3 3 3 4 4 11 8 15 12 15 19 15 13 15 15 15 13 15 11 15 44)f1b,f1,f1c142040000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c192192116,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c192192116,f1,f1c192192116,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i! ! !InformationSpaceController methodsFor: 'private' stamp: 'Brian T 11/9/2004 23:44'! privateCreateTextBoxes "Put all the text boxes on our form" "The log file Text box" | tabList | tabList _ OrderedCollection new. logFileText _ TabbedTextMorph new. logFileText initialize: tabList. logFileText autoFit: false. logFileText position: self position + (15 @ 50). logFileText extent: 158 @ 15. logFileText borderWidth: 1; margins: 4 @ 0. logFileText contents: '' asText. logFileText wrapFlag: true. logFileText setBalloonText: 'The name of the file where we log the explorer data. For visualizations, this must be a log file from a previous exploration run For explorations, this is the name of the log file that will be created or overwritten'. visibleGensText _ TabbedTextMorph new. visibleGensText initialize: tabList. visibleGensText autoFit: false. visibleGensText position: self position + (15 @ 145). visibleGensText extent: 30 @ 15. visibleGensText borderWidth: 1; margins: 4 @ 0. visibleGensText contents: '' asText. visibleGensText wrapFlag: true. visibleGensText setBalloonText: 'The number of generations visible during an animation'. "Pattern file text box for running explorations" patternFileText _ TabbedTextMorph new. patternFileText initialize: tabList. patternFileText autoFit: false. patternFileText position: self position + (220 @ 50). patternFileText extent: 158 @ 15. patternFileText borderWidth: 1; margins: 4 @ 0. patternFileText contents: '' asText. patternFileText wrapFlag: true. patternFileText setBalloonText: 'The name of the pattern file that contains the initial conditions (starting pattern)'. "Number of generations to link text box, for running explorations" maxGenerationsText _ TabbedTextMorph new. maxGenerationsText initialize: tabList. maxGenerationsText autoFit: false. maxGenerationsText position: self position + (220 @ 105). maxGenerationsText extent: 40 @ 15. maxGenerationsText borderWidth: 1; margins: 4 @ 0. maxGenerationsText contents: '' asText. maxGenerationsText wrapFlag: true. maxGenerationsText setBalloonText: 'The number of generations to render into the log file'. "Start gen for our visualization" startGenText _ TabbedTextMorph new. startGenText initialize: tabList. startGenText autoFit: false. startGenText position: self position + (15 @ 105). startGenText extent: 30 @ 15. startGenText borderWidth: 1; margins: 4 @ 0. startGenText contents: '' asText. startGenText wrapFlag: true. startGenText setBalloonText: 'The generation to start the visualization at'. "End gen for our visualization" endGenText _ TabbedTextMorph new. endGenText initialize: tabList. endGenText autoFit: false. endGenText position: self position + (90 @ 105). endGenText extent: 30 @ 15. endGenText borderWidth: 1; margins: 4 @ 0. endGenText contents: '' asText. endGenText wrapFlag: true. endGenText setBalloonText: 'The generation to stop the visualization at'. "Size of the InformationSpace (Size ends up being size^2)" spaceSizeText _ TabbedTextMorph new. spaceSizeText initialize: tabList. spaceSizeText autoFit: false. spaceSizeText position: self position + (220 @ 145). spaceSizeText extent: 40 @ 15. spaceSizeText borderWidth: 1; margins: 4 @ 0. spaceSizeText contents: '' asText. spaceSizeText wrapFlag: true. spaceSizeText setBalloonText: 'The size of one side of the square space(50 makes a 50 X 50 cell space) Larger spaces take exponentially longer time to render'. "Set the tab order" tabList addLast: logFileText; addLast: patternFileText; addLast: startGenText; addLast: endGenText; addLast: maxGenerationsText; addLast: spaceSizeText. "Attach all our morphs to our main window" self addMorph: spaceSizeText; addMorph: visibleGensText; addMorph: maxGenerationsText; addMorph: endGenText; addMorph: startGenText; addMorph: patternFileText; addMorph: logFileText. "This has to be done after adding the morph, so the text box doesn't collapse to one char wide" logFileText autoFit: true. patternFileText autoFit: true! ! !InformationSpaceController methodsFor: 'private'! privateDisplayStatus: statusString "Put a string that displays the time it takes to run an exploration" runTimeLabel = nil ifTrue: [runTimeLabel _ StringMorph new. runTimeLabel initialize. runTimeLabel contents: statusString. runTimeLabel position: self position + (220 @ 185). self addMorph: runTimeLabel] ifFalse: [runTimeLabel contents: statusString]! ! !InformationSpaceController methodsFor: 'private'! privateLaunchCroquetAnimation "Open a croquet visualization using the specified log file" | TeaPot | TeaPot _ VisualizeWhirledTea new. TeaPot openInWorld. TeaPot viewRange: logFileText text fromGen: startGenText text asString asInteger toGen: endGenText text asString asInteger rangeDepth: visibleGensText text asString asInteger randomBitColors: true! ! !InformationSpaceController methodsFor: 'private'! privateLaunchCroquetVisualization "Open a croquet visualization using the specified log file" | TeaPot | TeaPot _ VisualizeWhirledTea new. TeaPot openInWorld. TeaPot loadLogFile: logFileText text fromGen: startGenText text asString asInteger toGen: endGenText text asString asInteger randomBitColors: true! ! !InformationSpaceController methodsFor: 'private'! privateLaunchExploration "Open a croquet visualization using the specified log file" | exploration causality patternFile logFile maxGenerations gridSize runTime | exploreButtonLockout = true ifTrue: ["If our lockout is set to true, simply return, this gets unset once we are done with our current exploration calculations"^ false]. patternFile _ patternFileText text. logFile _ logFileText text. patternFile = nil | (patternFile = '' asText) ifTrue: ["Simply return, no values are specified" Warning signal: 'A pattern file must be specified, click proceed to enter a file name'. ^ self]. logFile = nil | (logFile = '' asText) ifTrue: ["No log file specified" Warning signal: 'A log file must be specified, click proceed to enter a file name'. ^ self]. maxGenerations _ maxGenerationsText text asString asInteger. maxGenerations = nil ifTrue: ["No max generations specified" Warning signal: 'Max generations must be greater than 0'. ^ self]. maxGenerations <= 0 ifTrue: ["No max generations specified" Warning signal: 'Max generations must be greater than 0'. ^ self]. gridSize _ spaceSizeText text asString asInteger. gridSize = nil ifTrue: ["Grid size must be > than 0" Warning signal: 'Grid size must be > than 0'. ^ self]. gridSize <= 0 ifTrue: ["Grid size must be > than 0" Warning signal: 'Grid size must be > than 0'. ^ self]. exploration _ InformationSpace new. "We'll make this choosable in the future, but for today, 75 X 75" exploration init3DWithXdepth: gridSize yDepth: gridSize zDepth: 1. "For now just assume Game of life, we'll make this choosable in the future" causality _ GameOfLife new. exploration setCausality: causality. exploration loadPatternFile: patternFileText text asString. exploration initLogFile: logFileText text asString. self privateDisplayStatus: 'Computing, please wait..'. exploreButtonLockout _ true. [runTime _ (Time millisecondsToRun: [exploration linkGenerations: maxGenerations]) asString. self privateDisplayStatus: (runTime asText append: ' milliseconds to run' asString). exploreButtonLockout _ false] forkAt: Processor userBackgroundPriority! ! !InformationSpaceController methodsFor: 'accessing'! tabHitWithEvent: anEvent "The tab key was hit. The keyboard focus has referred this event to me, though this perhaps seems rather backwards. Anyway, the assumption is that I have the property #tabAmongFields, so now the task is to tab to the next field." | currentFocus fieldList anIndex itemToHighlight | currentFocus _ anEvent hand keyboardFocus. fieldList _ self allMorphs select: [:aMorph | aMorph wouldAcceptKeyboardFocusUponTab and: [aMorph isLocked not]]. anIndex _ fieldList indexOf: currentFocus ifAbsent: []. itemToHighlight _ fieldList atWrap: (anIndex ifNil: [1] ifNotNil: [anEvent shiftPressed ifTrue: [anIndex - 1] ifFalse: [anIndex + 1]]). anEvent hand newKeyboardFocus: itemToHighlight. self flag: #arNote. "really???" itemToHighlight editor selectAll. itemToHighlight invalidRect: itemToHighlight bounds! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! InformationSpaceController class instanceVariableNames: ''! !InformationSpaceController class methodsFor: 'initialize-release'! openInWorld "Instantiate and open a controller in world" | newController newWindow | newController _ self new. newController initialize. "Set up our newController size and position" "Setup our buttons" newWindow _ newController embeddedInMorphicWindowLabeled: 'InformationSpace controller'. newWindow newBounds: (Rectangle origin: 300 @ 300 corner: 713 @ 550). newController setProperty: #tabAmongFields toValue: true. "Fixed size window" newWindow allowReframeHandles: false. newController privateCreateTextBoxes. newController privateCreateButtons. newController privateCreateLabels. newController privateAddDivider. newWindow setWindowColor: Color gray. newWindow openAsIs! ! B3DScene subclass: #InformationSpaceScene instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !InformationSpaceScene commentStamp: '' prior: 0! Basic B3DScene with a reset Bounding box method added, so that objects can be added to the scene post rendering! !InformationSpaceScene methodsFor: 'accessing'! resetBoundingBox "Re-set our bounding box to nil, will get re-calculated next time someone calls boundingBox" box := nil! ! Object subclass: #InformationSpaceVisualization instanceVariableNames: 'scene targetBit startGen endGen cameraTargetX cameraTargetY camera view maxVisibleGenerations ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !InformationSpaceVisualization commentStamp: '' prior: 0! Visualizer toolkit ! !InformationSpaceVisualization methodsFor: 'scene management'! addBitAtX: xPosition atY: yPosition atGeneration: generation "Point the camera at the first bit" | sceneObj bitName scale | scale := 3. bitName := Text new. bitName append: xPosition asString; append: ','; append: yPosition asString; append: ','; append: generation asString. sceneObj := B3DSceneObject named: bitName. sceneObj geometry: self createBit. sceneObj geometry translateBy: (B3DVector3 x: xPosition * scale y: yPosition * scale z: generation * (scale * 1.5)). sceneObj geometry transformBy: (B3DMatrix4x4 identity setScale: (B3DVector3 x: 0.5 y: 0.5 z: 0.25)). scene objects add: sceneObj. cameraTargetX = nil ifTrue: [cameraTargetX := xPosition. cameraTargetY := yPosition. targetBit := sceneObj]! ! !InformationSpaceVisualization methodsFor: 'scene management'! createLightsForScene | ambientLight | ambientLight _ B3DAmbientLight new. ambientLight lightColor: (B3DMaterialColor color: Color white). scene lights add: ambientLight! ! !InformationSpaceVisualization methodsFor: 'scene management'! createScene camera := B3DCamera new. scene := InformationSpaceScene new. scene defaultCamera: camera. self createLightsForScene! ! !InformationSpaceVisualization methodsFor: 'scene management'! descriptionScene | descriptionText | descriptionText _ Text new. descriptionText append: '3D (Z axis is time) representation of the causal set output from generation '; append: startGen asString; append:' to '; append: endGen asString. ^ descriptionText asString! ! !InformationSpaceVisualization methodsFor: 'scene management'! showBits "Information Space Visualization show view" camera setTargetFrom: targetBit. "camera position: cameraTargetX + 30 @ cameraTargetY - 40 @ 0. camera target: cameraTargetX @ cameraTargetY @ 0. camera direction: cameraTargetX @ cameraTargetY @ 0. camera fov: 20.0." (view := B3DTutorialScenePresenterMorph new) source: self description: #descriptionScene; scene: scene. view clearColor: (Color gray: 0.5). view panBy: 0 @ 0 @ 0; addDolly: -0.7; rotateZ: -90; rotateX: 45; openInSystemWindow! ]style[(8 2 43 2 6 16 9 3 193 3 4 4 30 14 4 14 17 12 5 3 4 16 5 7 3 4 4 8 1 3 1 3 1 15 4 14 3 14 2 23)f2b,f2,f2c118016000,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2c118016000,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2c170170092,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2c170170092,f2,f2cmagenta;,f2,f2c170170092,f2,f2c170170092,f2,f2c170170092,f2,f2c170170092,f2,f2c170170092,f2,f2c170170092,f2! ! !InformationSpaceVisualization methodsFor: 'common methods'! createBit | faces colors | colors _ Array with: Color red asB3DColor with: Color blue asB3DColor with: Color green asB3DColor with: Color yellow asB3DColor with: Color cyan asB3DColor with: Color orange asB3DColor. faces _ B3DIndexedMesh vrmlCreateBoxFaces. 1 to: faces size do: [:whichFace | | thisFace | thisFace _ faces at: whichFace. thisFace do: [:faceVertex | faceVertex color: (colors at: whichFace)]]. ^ (B3DSimpleMesh withAll: faces) asIndexedMesh! ! !InformationSpaceVisualization methodsFor: 'common methods'! createBodyWithFaceColors: colors "return a B3DSimpleMesh that represents an dodecahedron The solid is point symmetric to the origin of the coordinate system. It is therefore sufficient tocompute 10 of its 20 vertrices. The other vertices are obtained by multiplication with -1." | points meshFaces normal vertices normalFor | normalFor _ [:v | | v1 v2 v3 d1 d2 | v1 _ v at: 1. v2 _ v at: 2. v3 _ v at: 3. d1 _ v3 - v1. d2 _ v2 - v1. d1 safelyNormalize. d2 safelyNormalize. (d1 cross: d2) safelyNormalize]. points _ Array new: 10. 1 to: 5 do: [:idx | points at: idx put: (B3DVector3 x: (Float pi * (idx * 2 + 1) / 5) cos * 2 y: (Float pi * (idx * 2 + 1) / 5) sin * 2 z: 1.0 + 5.0 sqrt / 2 + 1.0). points at: idx + 5 put: (B3DVector3 x: (Float pi * (idx * 2 + 1) / 5) cos * (1.0 + 5.0 sqrt) y: (Float pi * (idx * 2 + 1) / 5) sin * (1.0 + 5.0 sqrt) z: 1.0 + 5.0 sqrt / 2 - 1.0)]. 1 to: 10 do: [:idx | points at: idx put: (points at: idx) / (points at: idx) squaredLength sqrt * (0.81573786516665 / 0.79465447229177)]. meshFaces _ OrderedCollection new: 12. vertices _ points copyFrom: 1 to: 5. normal _ normalFor value: vertices. meshFaces add: (B3DSimpleMeshFace withAll: (vertices collect: [:p | B3DSimpleMeshVertex new position: p; normal: normal; color: colors first])). vertices _ vertices collect: [:p | p negated]. normal _ normalFor value: vertices. meshFaces add: (B3DSimpleMeshFace withAll: (vertices collect: [:p | B3DSimpleMeshVertex new position: p; normal: normal; color: colors first])). 1 to: 5 do: [:idx | vertices _ Array with: (points at: idx) with: (points at: idx + 5) with: (points at: idx + 2 \\ 5 + 1 + 5) negated with: (points at: idx \\ 5 + 1 + 5) with: (points at: idx \\ 5 + 1). normal _ normalFor value: vertices. meshFaces add: (B3DSimpleMeshFace withAll: (vertices collect: [:p | B3DSimpleMeshVertex new position: p; normal: normal; color: (colors at: idx + 1)])). vertices _ vertices collect: [:p | p negated]. normal _ normalFor value: vertices. meshFaces add: (B3DSimpleMeshFace withAll: (vertices collect: [:p | B3DSimpleMeshVertex new position: p; normal: normal; color: (colors at: idx + 1)]))]. ^ B3DSimpleMesh withAll: meshFaces! ! !InformationSpaceVisualization methodsFor: 'common methods'! createStick | faces | faces := B3DIndexedMesh vrmlCreateBoxFaces. ^(B3DSimpleMesh withAll: faces) asIndexedMesh! ! !InformationSpaceVisualization methodsFor: 'initialize'! initialize "Set up our scene" self createScene. ^ self! ! !InformationSpaceVisualization methodsFor: 'accessing'! getScene "return the scene object" ^scene.! ! !InformationSpaceVisualization methodsFor: 'accessing'! getView "return the view object" ^ view! ! !InformationSpaceVisualization methodsFor: 'accessing'! loadLogFile: logFileName fromGen: startGenInt toGen: endGenInt "Read the log file, visualizing generations startGenInt to endGenInt. This call must be forked off at background priority so that we can do the updates to the UI during drawing." [| dataBytes dataString fieldIndex generation xPos yPos logFileStreamObj hasScene lastRendered generationMarkersDict sceneObjIndex clipGenMarker | generationMarkersDict := Dictionary new. sceneObjIndex := 0. hasScene := false. lastRendered := 1. startGen := startGenInt. endGen := endGenInt. logFileStreamObj := StandardFileStream new. (logFileStreamObj open: logFileName asString forWrite: false) = nil ifTrue: [| errorMessage | "Halt if we can't open the file for reading" errorMessage := 'Could not open datafile: ' asText. errorMessage append: logFileName. Error signal: errorMessage]. fieldIndex := 0. [dataBytes := logFileStreamObj upTo: Character lf. dataString := LogString new. dataString append: dataBytes. dataString commaDelimitedFieldsDo: [:field | fieldIndex = 0 ifTrue: [generation := field asInteger]. fieldIndex = 1 ifTrue: [xPos := field asInteger]. fieldIndex = 2 ifTrue: [yPos := field asInteger]. fieldIndex := fieldIndex + 1]. "Reset the field index to 0 for the next log line" fieldIndex := 0. generation >= startGenInt & (generation <= endGenInt) ifTrue: [self addBitAtX: xPos atY: yPos atGeneration: generation - startGenInt + 1. sceneObjIndex := sceneObjIndex + 1. hasScene = false ifTrue: [hasScene := true. [self showBits] fork]. generation > lastRendered ifTrue: [lastRendered := generation. generationMarkersDict at: lastRendered - startGen - 1 put: sceneObjIndex - 1. "Reset the object counter for the next generation" sceneObjIndex := 0. maxVisibleGenerations ifNotNil: [(lastRendered - startGen) > maxVisibleGenerations ifTrue: [clipGenMarker := generationMarkersDict at: (lastRendered - startGen) - maxVisibleGenerations. scene objects removeFirst: clipGenMarker]]. scene resetBoundingBox. camera setClippingPlanesFrom: scene. view changed]]. logFileStreamObj atEnd | (generation > endGenInt)] whileFalse] forkAt: Processor userBackgroundPriority! ]style[(13 11 10 11 8 9 3 184 5 9 1 10 1 10 1 10 1 4 1 4 1 16 1 8 1 12 1 21 1 13 1 13 5 21 4 10 7 13 4 1 3 8 4 5 3 12 4 1 3 8 4 11 3 6 4 9 3 16 4 18 8 16 7 11 20 5 7 3 14 12 7 44 4 12 4 27 12 12 9 11 5 5 9 12 4 10 4 1 4 9 4 16 7 9 6 10 4 9 7 10 9 9 3 10 28 7 6 10 3 1 14 10 4 5 16 10 3 1 14 4 4 5 16 10 3 1 14 4 4 5 16 10 4 10 3 1 4 50 2 10 4 1 3 10 4 11 4 10 4 9 13 4 16 4 10 4 19 10 3 11 3 1 5 13 4 13 3 1 5 8 3 5 14 8 4 4 8 4 21 10 3 12 14 12 4 10 7 21 5 12 3 8 3 1 6 13 3 1 7 50 6 13 4 1 7 21 19 24 3 21 18 13 4 21 6 24 3 21 11 5 22 13 9 5 24 6 24 5 7 4 13 16 10 10 3 9 25 9 23)f2b,f2cblue;b,f2b,f2cblue;b,f2b,f2cblue;b,f2,f2c117015000,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2c170170091,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cblue;i,f2,f2c117015000,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cred;,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2c170170091,f2,f2c117015000,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cblue;i,f2,f2c170170091,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2c170170091,f2,f2cblue;i,f2,f2c170170091,f2,f2c117015000,f2,f2cblue;i,f2,f2c170170091,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cmagenta;,f2,f2cblue;i,f2,f2cblue;i,f2,f2cblue;i,f2,f2cmagenta;,f2! ! !InformationSpaceVisualization methodsFor: 'accessing'! loadLogFile: logFileName fromGen: startGenInt toGen: endGenInt maxVisibleGen: visibleGenDepth maxVisibleGenerations := visibleGenDepth. self loadLogFile: logFileName fromGen: startGenInt toGen: endGenInt.! ! Text subclass: #LogString instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !LogString commentStamp: '' prior: 0! Convenience subclass of Text, that allows us to easily handle comma delimmeted files! !LogString methodsFor: 'accessing'! commaDelimitedFieldsDo: aBlock "Considering the receiver as a holder of tab-delimited fields, evaluate execute aBlock with each field in this string. The separatilng tabs are not included in what is passed to aBlock" | start end | start _ 1. [start <= self size] whileTrue: [end _ self asString indexOf: ',' asCharacter startingAt: start ifAbsent: [self size + 1]. end _ end - 1. aBlock value: (self asString copyFrom: start to: end). start _ end + 2]! ! CausalLigature subclass: #NKStriangle instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !NKStriangle commentStamp: 'Brian Tabone 1/9/2005 22:35' prior: 0! Causal ligature for 1 dimensional spaces found in Stephen Wolfram's New Kind of Science ! !NKStriangle methodsFor: 'accessing'! getNextGeneration: currentGenerationInt "Calculate the bit state for the root bit, for the next generation after the specified generation. This causal ligature is from Stephen Wolfram's New Kind of Science, chapter 2, section 1, page 25. The rules are: A cell should be black whenever one of its neighbors was black in the previous generation, but not both neighbors." | rootBit liveNeighborCount XYplaneDict | liveNeighborCount _ 0. XYplaneDict _ bitBlockDictionary at: 2. rootBit _ XYplaneDict at: 1. XYplaneDict do: [:neighborBit | neighborBit == rootBit | (neighborBit == nil) ifFalse: [(neighborBit getStateAtGeneration: currentGenerationInt) = true ifTrue: [liveNeighborCount _ liveNeighborCount + 1]]]. liveNeighborCount = 1 ifTrue: [ rootBit setState: true] ifFalse: [rootBit setState: false]! ! Object subclass: #NumberSpace instanceVariableNames: 'numberSpaceArray xRange yRange zRange spacialDimensions ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !NumberSpace commentStamp: '' prior: 0! All information spaces exist in the numberspace, this is the foundation of everything! !NumberSpace methodsFor: 'initialize' stamp: 'Brian Tabone 1/9/2005 22:12'! getBitAtX: xLocationInt "Get the causal bit object at the specified location" "Increment the passed in location since the user uses 0 start number space, and Array's are 1 start based" | xLocation | xLocation _ xLocationInt + 1. ^ numberSpaceArray at: xLocation. ! ]style[(11 12 1 2 164 3 12 4 9 3 12 3 1 5 1 1 16 5 9 3)f1b,f1cblue;b,f1b,f1,f1c146044000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1! ! !NumberSpace methodsFor: 'initialize'! getBitAtX: xLocationInt atY: yLocationInt "Get the causal bit object at the specified location" "Increment the passed in location since the user uses 0 start number space, and Array's are 1 start based" | yArray xLocation yLocation | xLocation _ xLocationInt + 1. yLocation _ yLocationInt + 1. yArray _ numberSpaceArray at: xLocation. ^ yArray at: yLocation! ! !NumberSpace methodsFor: 'initialize'! getBitAtX: xLocationInt atY: yLocationInt atZ: zLocationInt "Get the causal bit object at the specified location" "Increment the passed in location since the user uses 0 start number space, and Array's are 1 start based" | yArray zArray xLocation yLocation zLocation | yRange = 0 ifTrue: ["Handle 1 dimensional space" ^ self getBitAtX: xLocationInt]. xLocation _ xLocationInt + 1. yLocation _ yLocationInt + 1. zLocation _ zLocationInt + 1. yArray _ numberSpaceArray at: xLocation. zArray _ yArray at: yLocation. ^ zArray at: zLocation! ! !NumberSpace methodsFor: 'initialize'! init1DWithXdepth: xDepth "Create a new 1D number space with xdimensions" "The number space goes by xit is an array of length X" xRange _ xDepth - 1. yRange _ 0. zRange _ 0. numberSpaceArray _ Array new: xDepth. 1 to: xDepth do: [:xElement | | causalBit | causalBit _ CausalBit new. causalBit initialize: false xPos: xElement - 1 yPos: 0 zPos: 0. numberSpaceArray at: xElement put: causalBit].! ! !NumberSpace methodsFor: 'initialize'! init2DWithXdepth: xDepth yDepth: yDepth "Create a new 2D number space with x,y dimensions" "The number space goes by x, y IE, it is an array of length X, with each element containing a y array of yDepth length" xRange _ xDepth. yRange _ yDepth. zRange _ 1. numberSpaceArray _ Array new: xDepth. 1 to: xDepth do: [:xElement | | yArray | yArray _ Array new: yDepth. 1 to: yDepth do: [:yElement | | causalBit | causalBit _ CausalBit new. causalBit initialize:false xPos: xElement - 1 yPos: yElement - 1 zPos: 0. yArray at: yElement put: causalBit]. numberSpaceArray at: xElement put: yArray]! ! !NumberSpace methodsFor: 'initialize'! init3DWithXdepth: xDepth yDepth: yDepth zDepth: zDepth "Create a new 3D number space with x,y,z dimensions" "The number space goes by x, y, z. IE, it is an array of length X, with each element containing a y array of yDepth length, in each of those arrays, each element is an array of zDepth" "Since arrays start at 1, and the number space starts at 0, we add one to our range" xRange _ xDepth - 1. yRange _ yDepth - 1. zRange _ zDepth - 1. numberSpaceArray _ Array new: xDepth. 1 to: xDepth do: [:xElement | | yArray | yArray _ Array new: yDepth. 1 to: yDepth do: [:yElement | | zArray | zArray _ Array new: zDepth. 1 to: zDepth do: [:zElement | | causalBit | causalBit _ CausalBit new. causalBit initialize: false xPos: xElement - 1 yPos: yElement - 1 zPos: zElement - 1. zArray at: zElement put: causalBit]. yArray at: yElement put: zArray]. numberSpaceArray at: xElement put: yArray]! ! NumberSpace subclass: #InformationSpace instanceVariableNames: 'causalLigatureObject logFileStreamObject currentGenerationInt visualizationObj ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !InformationSpace methodsFor: 'initialize'! initLogFile: logFileFullPathName "Log file path name, here we open that file and that's what we log each generation to" logFileStreamObject _ StandardFileStream new. (logFileStreamObject open: logFileFullPathName forWrite: true) = nil ifTrue: ["Fail on inability to write log file" Error signal: (('Could not open ' asText append: logFileFullPathName) append: ' for writing')]. ! ! !InformationSpace methodsFor: 'initialize'! setCausality: causalObject "Adding a causal ligature object to this information space" causalLigatureObject _ causalObject! ! !InformationSpace methodsFor: 'accessing'! linkGenerations: maxGenerationInt "Link generations 1 through maxGenerationInt into our world. Why do we call it link? Because all informations spaces exist by default in the number space, but not all are causally linked in with ours. (In fact all are causally isolated from eachother) When we examine a function on the number line, what we are doing in effect is causally linking that information space (or part of it) into our own." currentGenerationInt _ 1. "We start at 1 and move out to range - 1. We can only set the causal states of the bits one in from the edge in the XY plane. We start in the middle in the Z plane, in a 3D space, we start at 1, (One above the floor) and stop one below the roof (Think polar cylindrical coords), in a 2D space we start at 0" [yRange = 0 ifTrue: ["Handle a 1 Dimensional space" 1 to: xRange - 1 do: [:xPos | causalLigatureObject linkUpRootBit: (self getBitAtX: xPos); addNeighborBit: (self getBitAtX: xPos + 1) atTheta: 90 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos - 1) atTheta: 180 Radius: 1 polarZ: 0. "Our root bit and its neighbors are now causaly linked, do the calculation for the next generation" causalLigatureObject getNextGeneration: currentGenerationInt]]. yRange > 0 & (zRange = 0) ifTrue: ["Handle a 2 Dimensional space" 1 to: xRange - 1 do: [:xPos | 1 to: yRange - 1 do: [:yPos | "2D number space, link the root bit and neighbors to the causal ligature for bit state calculation" causalLigatureObject linkUpRootBit: (self getBitAtX: xPos atY: yPos atZ: 0); addNeighborBit: (self getBitAtX: xPos + 1 atY: yPos atZ: 0) atTheta: 0 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos + 1 atY: yPos + 1 atZ: 0) atTheta: 45 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos atY: yPos + 1 atZ: 0) atTheta: 90 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos - 1 atY: yPos + 1 atZ: 0) atTheta: 135 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos - 1 atY: yPos atZ: 0) atTheta: 180 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos - 1 atY: yPos - 1 atZ: 0) atTheta: 225 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos atY: yPos - 1 atZ: 0) atTheta: 270 Radius: 1 polarZ: 0; addNeighborBit: (self getBitAtX: xPos + 1 atY: yPos - 1 atZ: 0) atTheta: 315 Radius: 1 polarZ: 0. "Our root bit and its neighbors are now causaly linked, do the calculation for the next generation" causalLigatureObject getNextGeneration: currentGenerationInt]]]. currentGenerationInt _ currentGenerationInt + 1. currentGenerationInt < maxGenerationInt] whileTrue. "Write out our log file of states for each generation" self logGenerations! ! !InformationSpace methodsFor: 'accessing'! loadPatternFile: patternFileName "Read a pattern file, set generation 1 to this pattern" | dataBytes yIndex patternFileStreamObj trueChar thisBit baseX baseY | patternFileStreamObj _ StandardFileStream new. "What character is used in the file as the true or on bit" trueChar _ $*. baseX _ (xRange / 2) asInteger. yRange > 0 ifTrue: [baseY _ (yRange / 2) asInteger] ifFalse: [baseY _ 0]. (patternFileStreamObj open: patternFileName asString forWrite: false) = nil ifTrue: [| errorMessage | "Halt if we can't open the file for reading" errorMessage _ 'Could not open patternfile: ' asText. errorMessage append: patternFileName. Error signal: errorMessage]. yIndex _ 0. [dataBytes _ patternFileStreamObj upTo: Character lf. 1 to: dataBytes size do: [:xIndex | trueChar = (dataBytes at: xIndex) ifTrue: [yRange = 0 ifTrue: [thisBit _ self getBitAtX: baseX + xIndex] ifFalse: [thisBit _ self getBitAtX: baseX + xIndex atY: baseY + yIndex atZ: 0]. thisBit setInitialState: true]]. yRange = 0 ifTrue: ["If we are 1 Dimensional, only read the first line of the pattern file " patternFileStreamObj close. ^ true]. yIndex _ yIndex + 1. patternFileStreamObj atEnd] whileFalse. patternFileStreamObj close.! ]style[(17 15 3 55 3 67 4 20 3 18 7 58 2 8 3 2 3 5 4 6 3 1 14 6 3 1 12 5 4 6 3 1 25 5 3 1 5 20 7 15 20 5 7 3 14 12 7 44 4 12 3 30 12 12 9 15 5 5 9 12 4 6 3 1 4 9 3 20 7 9 6 1 7 9 13 8 2 8 9 9 5 6 15 6 3 1 16 7 3 4 12 5 3 6 18 7 3 4 22 5 3 6 16 5 3 6 16 1 8 7 18 4 5 6 3 1 12 77 27 6 4 4 6 3 6 3 1 3 20 21 27)f1b,f1cblue;b,f1,f1c146044000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c146044000,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c196196120,f1,f1cmagenta;,f1,f1c196196120,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c196196120,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c146044000,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c196196120,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c196196120,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c196196120,f1,f1c146044000,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c196196120,f1,f1cblue;i,f1,f1cblue;i! ! !InformationSpace methodsFor: 'accessing'! loadPatternFromLogFile: logFileName generation: genInt "Read a specific generation out of a log file, set generation 1 to this pattern This method exists so that you can start a new exploration where a previous one has left off, instead of having to re-render the whole thing, or tediously parse the log file manually for the gen data" | dataBytes dataString fieldIndex generation xPos yPos logFileStreamObj thisBit | logFileStreamObj _ StandardFileStream new. (logFileStreamObj open: logFileName asString forWrite: false) = nil ifTrue: [| errorMessage | "Halt if we can't open the file for reading" errorMessage _ 'Could not open datafile: ' asText. errorMessage append: logFileName. Error signal: errorMessage]. fieldIndex _ 0. [dataBytes _ logFileStreamObj upTo: Character lf. dataString _ LogString new. dataString append: dataBytes. dataString commaDelimitedFieldsDo: [:field | fieldIndex = 0 ifTrue: [generation _ field asInteger]. fieldIndex = 1 ifTrue: [xPos _ field asInteger]. fieldIndex = 2 ifTrue: [yPos _ field asInteger]. fieldIndex _ fieldIndex + 1]. "Reset the field index to 0 for the next log line" fieldIndex _ 0. generation = genInt ifTrue: [thisBit _ self getBitAtX: xPos atY: yPos atZ: 0. thisBit setInitialState: true]. logFileStreamObj atEnd | (generation > genInt)] whileFalse! ]style[(24 11 13 6 3 289 3 78 4 16 3 18 8 16 7 11 20 5 7 3 14 12 7 44 4 12 3 27 12 12 9 11 5 5 9 12 4 10 3 1 4 9 3 16 7 9 6 10 3 9 7 10 9 9 3 10 28 7 6 10 3 1 14 10 3 5 16 10 3 1 14 4 3 5 16 10 3 1 14 4 3 5 16 10 3 10 3 1 4 50 2 10 3 1 3 10 3 6 12 7 3 4 18 4 12 4 12 1 5 7 18 4 4 16 10 10 3 6 13)f1b,f1cblue;b,f1b,f1cblue;b,f1,f1c124022000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c124022000,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c174174098,f1,f1c124022000,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c174174098,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1! ! !InformationSpace methodsFor: 'accessing'! logGenerations "Write out our states for each generation, to the specified log file. If logfile is null, simply return" logFileStreamObject = nil ifFalse: ["Log our generations" 1 to: currentGenerationInt do: [:thisGeneration | 0 to: xRange do: [:xElement | 0 to: yRange do: [:yElement | 0 to: zRange do: [:zElement | | causalBit bitState logString | causalBit _ self getBitAtX: xElement atY: yElement atZ: zElement. bitState _ causalBit getStateAtGeneration: thisGeneration. "Bit states are only written if true, false is implied" bitState = true ifTrue: [ logString _ Text new. logString append: thisGeneration asString; append: ','; append: xElement asString; append: ','; append: yElement asString; append: ','; append: zElement asString; append: ','; append: bitState asString; append: Character cr; append: Character lf. logFileStreamObject nextPutAll: logString asString]]]]]. logFileStreamObject flush. logFileStreamObject close.]! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! InformationSpace class instanceVariableNames: ''! !InformationSpace class methodsFor: 'examples'! gliderGun: logFileName "Demonstrates the usage of InformationSpace and CausalLigature, using John Conway's Game of Life, and a glider gun arrangement." | gameOfLife gofInfoSpace thisBit visualizeGliders baseX baseY rootX rootY | visualizeGliders := InformationSpaceVisualization new. visualizeGliders initialize. "Get a causal ligature which defines the rules in the game of life" gameOfLife := GameOfLife new. "Get an information space to manifest the causal ligature on" gofInfoSpace := InformationSpace new. gofInfoSpace initLogFile: logFileName . gofInfoSpace init3DWithXdepth: 75 yDepth: 75 zDepth: 1. "Set up our pattern" rootX := 20. rootY := 20. "pattern 1" baseX := rootX + 0. baseY := rootY + 16. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. "pattern 2" baseX := rootX + 2. baseY := rootX + 12. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. "pattern 3" baseX := rootX + 8. baseY := rootY + 14. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. "pattern 4" baseX := rootX + 9. baseY := rootX + 10. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. "pattern 5" baseX := rootX + 14. baseY := rootY + 9. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. "pattern 6" baseX := rootX + 20. baseY := rootY + 8. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. "pattern 7" baseX := rootX + 22. baseY := rootY + 16. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. "pattern 8" baseX := rootX + 23. baseY := rootY + 12. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. "pattern 9" baseX := rootX + 28. baseY := rootY + 11. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. "pattern 10" baseX := rootX + 28. baseY := rootY + 0. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. "pattern 11" baseX := rootX + 34. baseY := rootY + 18. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. "pattern 12" baseX := rootX + 36. baseY := rootY + 14. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. "pattern 13" baseX := rootX + 39. baseY := rootY + 5. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 0 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 0 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 1 atY: baseY + 2 atZ: 0. thisBit setInitialState: true. thisBit := gofInfoSpace getBitAtX: baseX + 2 atY: baseY + 1 atZ: 0. thisBit setInitialState: true. "Set the causal ligature of the information space (Attach the game of life causal ligature object" gofInfoSpace setCausality: gameOfLife. "Render 50 generations, writing them out to the specified log file" gofInfoSpace linkGenerations: 50. "Open up a 3D visualization of this information space. The Z axis is time (One generation per XY plane." visualizeGliders loadLogFile: logFileName fromGen: 1 toGen: 15! ! TextMorph subclass: #TabbedTextMorph instanceVariableNames: 'tabList ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !TabbedTextMorph methodsFor: 'initialize'! initialize:theTabList super initialize. tabList := theTabList. ^ self.! ! TeapotMorph subclass: #VisualizeWhirledTea instanceVariableNames: 'genHasNotifier space lookAtX lookAtY lookAtZ avatar scale randomColorizer generationDictionary animDepth nextGen lastGen ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !VisualizeWhirledTea commentStamp: 'Brian T 11/8/2004 23:12' prior: 0! Croquet visualization engine for InformationSpace. genHasNotifier -> The latest generation to have a notifier established. We set this so that only one bit per generations gets a notifier target that needs to be called when it deletes itself. space -> Our TSpace object in which we put our bits lookAtX -> X position to have the Avatar look at, we set this to the X of the first bit we add lookAtY -> Y position to have the Avatar look at, we set this to the Y of the first bit we add lookAtZ -> Z position to have the Avatar look at, we set this to the Z of the first bit we add avatar -> Our avatar in the space, we set the look at position for the avatar scale -> The spacing between bits randomColorizer -> Boolean for turning on and off the random colorizer generationDictionary -> Dictionary containing arrays of objects for each generation, used for animated renderings animDepth -> How many generations are visible during the animation. nextGen -> The next generation to load when this current generation is destroyed lastGen -> The last generation to be visualized ! !VisualizeWhirledTea methodsFor: 'accessing'! addBitAtX: xPosition atY: yPosition atGeneration: generation "Add a bit to our visualization" | newBit | lookAtX isNil ifTrue: [lookAtX _ xPosition. lookAtY _ yPosition. lookAtZ _ generation]. scale _ 1.75. newBit _ WhirledBits new. newBit colorize: Color blue asB3DColor. newBit translationX: xPosition * scale y: generation * scale z: yPosition * scale. space addChild: newBit. ! ]style[(11 9 6 9 15 10 3 32 3 7 4 7 18 7 3 9 5 7 3 9 5 7 3 10 4 5 3 4 3 6 3 11 7 6 11 5 19 6 17 9 3 5 6 10 3 5 6 9 3 5 3 5 11 6 3)f1b,f1cblue;b,f1b,f1cblue;b,f1b,f1cblue;b,f1,f1c132030000,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c182182106,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/9/2004 21:56'! addBitAtX: xPosition atY: yPosition atGeneration: generation randomColor: boolRandom "Add a bit to our visualization" | newBit | newBit _ self createBitAtX: xPosition atY: yPosition atGeneration: generation randomColor: boolRandom. space addChild: newBit.! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/8/2004 23:13'! addBitToDictionaryAtX: xPosition atY: yPosition atGeneration: generation randomColor: boolRandom "Add a bit to our bit dictionary, we use a dictionary of bits for our animated rendering. This reduces the amount of file I/O needed to render" | newBit bitArray args | newBit _ self createBitAtX: xPosition atY: yPosition atGeneration: generation randomColor: boolRandom. generationDictionary ifNil: [generationDictionary _ Dictionary new]. bitArray _ generationDictionary at: generation ifAbsent: [generationDictionary at: generation put: OrderedCollection new. "If this is our first bit in this generation, set its notifier target" (generation + animDepth <= lastGen) ifTrue:[ args _ Array new: 2. args at: 1 put: generation + animDepth. args at: 2 put: animDepth. newBit setNotifyTarget: self perform: #animAddGen:atZ: withArguments: args.]. generationDictionary at: generation]. bitArray add: newBit! ! !VisualizeWhirledTea methodsFor: 'accessing'! addRandomColorBitAtX: xPosition atY: yPosition atGeneration: generation "Add a bit to our visualization" | newBit newColor | newColor _ Color r: randomColorizer next g: randomColorizer next b: randomColorizer next. lookAtX isNil ifTrue: [lookAtX _ xPosition. lookAtY _ yPosition. lookAtZ _ generation]. scale _ 1.75. newBit _ WhirledBits new. newBit colorize: newColor asB3DColor. newBit translationX: xPosition * scale y: generation * scale z: yPosition * scale. space addChild: newBit.! ]style[(22 9 6 9 15 10 3 32 3 16 4 8 3 5 8 15 13 15 13 15 8 7 18 7 3 9 5 7 3 9 5 7 3 10 4 5 3 4 3 6 3 11 7 6 11 8 14 6 17 9 3 5 6 10 3 5 6 9 3 5 3 5 11 6 1)f1b,f1cblue;b,f1b,f1cblue;b,f1b,f1cblue;b,f1,f1c132030000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c182182106,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/8/2004 23:10'! animAddGen: generation atZ: zPosition "Add a generation to our animation" | bitCollection currentTransform | bitCollection _ generationDictionary at: generation. nextGen _ generation + 1. bitCollection do: [:newBit | "Set the z position for this generation of bits" currentTransform _ newBit localTransform. currentTransform a24: zPosition * scale. space addChild: newBit. newBit stepTime: 100. newBit startStepping]! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/8/2004 23:38'! createBitAtX: xPosition atY: yPosition atGeneration: generation randomColor: boolRandom "Create a bit. Both addBitAtX and addBitToDictionaryAtX create bits, so we have factored that code into one message here, that both can call" | newBit newColor newMat| lookAtX isNil ifTrue: [lookAtX _ xPosition. lookAtY _ yPosition. lookAtZ _ generation]. scale _ 1.75. newBit _ WhirledBits new. boolRandom ifTrue: [newColor _ Color r: randomColorizer next g: randomColorizer next b: randomColorizer next] ifFalse: [newColor _ Color blue]. newMat := TMaterial new. newMat ambientColor:newColor asB3DColor. newMat diffuseColor:newColor asB3DColor. newBit material:newMat. "newBit colorize: newColor asB3DColor." newBit translationX: xPosition * scale y: generation * scale z: yPosition * scale. newBit parentSpace: space. ^ newBit! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/7/2004 20:15'! loadLogFile: logFileName fromGen: startGenInt toGen: endGenInt "Read the log file, visualizing generations startGenInt to endGenInt. This call must be forked off at background priority so that we can do the updates to the UI during drawing." | dataBytes dataString fieldIndex generation xPos yPos logFileStreamObj | logFileStreamObj _ StandardFileStream new. (logFileStreamObj open: logFileName asString forWrite: false) = nil ifTrue: [| errorMessage | "Halt if we can't open the file for reading" errorMessage _ 'Could not open datafile: ' asText. errorMessage append: logFileName. Error signal: errorMessage]. fieldIndex _ 0. [dataBytes _ logFileStreamObj upTo: Character lf. dataString _ LogString new. dataString append: dataBytes. dataString commaDelimitedFieldsDo: [:field | fieldIndex = 0 ifTrue: [generation _ field asInteger]. fieldIndex = 1 ifTrue: [xPos _ field asInteger]. fieldIndex = 2 ifTrue: [yPos _ field asInteger]. fieldIndex _ fieldIndex + 1]. "Reset the field index to 0 for the next log line" fieldIndex _ 0. generation >= startGenInt & (generation <= endGenInt) ifTrue: [self addBitAtX: xPos atY: yPos atGeneration: generation - startGenInt + 1 randomColor: false]. logFileStreamObj atEnd | (generation > endGenInt)] whileFalse. avatar lookAt: lookAtX * scale @ (lookAtZ * scale) @ (lookAtY * scale) up: nil! ]style[(13 11 10 11 8 9 3 184 3 70 4 16 3 18 8 16 7 11 20 5 7 3 14 12 7 44 4 12 3 27 12 12 9 11 5 5 9 12 4 10 3 1 4 9 3 16 7 9 6 10 3 9 7 10 9 9 3 10 28 7 6 10 3 1 14 10 3 5 16 10 3 1 14 4 3 5 16 10 3 1 14 4 3 5 16 10 3 10 3 1 4 50 2 10 3 1 3 10 4 11 4 10 4 9 13 4 16 4 10 4 19 10 3 11 3 1 18 5 4 16 10 10 3 9 16 6 9 7 3 5 4 7 3 5 5 7 3 5 6 3)f1b,f1cblue;b,f1b,f1cblue;b,f1b,f1cblue;b,f1,f1c140038000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c140038000,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cred;,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c190190114,f1,f1c140038000,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c190190114,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/7/2004 20:28'! loadLogFile: logFileName fromGen: startGenInt toGen: endGenInt randomBitColors: rcolorBool "Read the log file, visualizing generations startGenInt to endGenInt. This call must be forked off at background priority so that we can do the updates to the UI during drawing." | dataBytes dataString fieldIndex generation xPos yPos logFileStreamObj haveLook | haveLook _ false. logFileStreamObj _ StandardFileStream new. (logFileStreamObj open: logFileName asString forWrite: false) = nil ifTrue: [| errorMessage | "Halt if we can't open the file for reading" errorMessage _ 'Could not open datafile: ' asText. errorMessage append: logFileName. Error signal: errorMessage]. fieldIndex _ 0. [dataBytes _ logFileStreamObj upTo: Character lf. dataString _ LogString new. dataString append: dataBytes. dataString commaDelimitedFieldsDo: [:field | fieldIndex = 0 ifTrue: [generation _ field asInteger]. fieldIndex = 1 ifTrue: [xPos _ field asInteger]. fieldIndex = 2 ifTrue: [yPos _ field asInteger]. fieldIndex _ fieldIndex + 1]. "Reset the field index to 0 for the next log line" fieldIndex _ 0. generation >= startGenInt & (generation <= endGenInt) ifTrue: [self addBitAtX: xPos atY: yPos atGeneration: generation - startGenInt + 1 randomColor: rcolorBool]. haveLook ifFalse: [haveLook _ true. avatar lookAt: lookAtX * scale @ (lookAtZ * scale) @ (lookAtY * scale) up: nil]. logFileStreamObj atEnd | (generation > endGenInt)] whileFalse! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian Tabone 3/6/2005 18:02'! makeRenderError: sp | err tframe box scale dy | err := TRenderErrorTest new. err translation: 10@3@0. tframe _ CroquetData loadAliceFile: #('Content' 'Alice' 'Animals' 'Bunny.mdl'). tframe ifNotNil:[ box _ tframe boundingBox. scale _ 4.708164/box extentY. dy _ box centerY * scale negated. tframe scale: scale. box _ tframe boundingBox. tframe translationX: 0 y: 0.616526961326599+dy-2.3 z: 0. tframe singleParent: false. tframe solidTree: false. tframe objectOwner: nil. ]. err addChild: tframe. sp addChild: err.! ! !VisualizeWhirledTea methodsFor: 'accessing' stamp: 'Brian T 11/8/2004 23:15'! viewRange: logFileName fromGen: startGenInt toGen: endGenInt rangeDepth: depth randomBitColors: rcolorBool "Read the log file, visualizing generations startGenInt to endGenInt. This call must be forked off at background priority so that we can do the updates to the UI during drawing." | dataBytes dataString fieldIndex generation xPos yPos logFileStreamObj haveLook | lastGen := endGenInt. haveLook _ false. animDepth _ depth. logFileStreamObj _ StandardFileStream new. (logFileStreamObj open: logFileName asString forWrite: false) = nil ifTrue: [| errorMessage | "Halt if we can't open the file for reading" errorMessage _ 'Could not open datafile: ' asText. errorMessage append: logFileName. Error signal: errorMessage]. fieldIndex _ 0. [dataBytes _ logFileStreamObj upTo: Character lf. dataString _ LogString new. dataString append: dataBytes. dataString commaDelimitedFieldsDo: [:field | fieldIndex = 0 ifTrue: [generation _ field asInteger]. fieldIndex = 1 ifTrue: [xPos _ field asInteger]. fieldIndex = 2 ifTrue: [yPos _ field asInteger]. fieldIndex _ fieldIndex + 1]. "Reset the field index to 0 for the next log line" fieldIndex _ 0. generation >= startGenInt & (generation <= endGenInt) ifTrue: [self addBitToDictionaryAtX: xPos atY: yPos atGeneration: generation - startGenInt + 1 randomColor: rcolorBool]. haveLook ifFalse: [haveLook _ true. avatar lookAt: lookAtX * scale @ (lookAtZ * scale) @ (lookAtY * scale) up: nil]. logFileStreamObj atEnd | (generation > endGenInt)] whileFalse. "Add the first generations to the visualization, start the animation" 1 to: depth do: [:plane | self animAddGen: plane atZ: plane]! ! !VisualizeWhirledTea methodsFor: 'initialize'! initializeDefaultSpace "self initializeTweakWorld." "self makeJIVE." | tframe | randomColorizer _ Random new. randomColorizer initialize. space _ TSpace new. space url: 'http://www.reed.com/TeaLand/spaces/intro.tea'. activeCamera avatar enter: space. "space ambientSound: (self loadAmbientSound:'intro.mp3')." self makeLight: space. self world doOneCycleNow. txtr _ TTexture new initializeWithFileName: 'checker.bmp' mipmap: true shrinkFit: false. self world doOneCycleNow. self makeMirror: space. tframe _ TSkyBox new initializeWithFileName: 'JUL'. space addChild: tframe. self world doOneCycleNow. avatar := activeCamera avatar. ^ space! ]style[(22 2 46 3 7 4 15 3 6 7 15 14 5 3 6 7 5 6 46 3 12 15 5 3 58 2 4 12 5 3 4 23 4 3 8 33 13 13 4 16 5 3 4 23 4 13 5 3 6 3 7 29 5 3 5 11 6 3 4 57 5)f1b,f1,f1c152050000,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c202202126,f1,f1cmagenta;,f1,f1cblue;i,f1,f1c152050000,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c202202126,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c202202126,f1,f1cblue;i,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cblue;i! ! !VisualizeWhirledTea methodsFor: 'initialize' stamp: 'Brian Tabone 3/6/2005 18:34'! initializeDefaultSpace2 "Initialize our space and camera" | tframe | randomColorizer _ Random new. randomColorizer initialize. space _ TSpace new. space url: 'http://www.softcentral.com/informationspace/whirled.tea'. self makeRenderError: space. self makeTBeep: space. "Make a light" self makeLight: space. tframe _ TSkyBox new initializeWithFileName: 'JUL'. space addChild: tframe. activeCamera _ TUserCamera new initializeWithViewPort: self. activeCamera viewClip: false. avatarPath _ #('Content' 'Alice' 'Animals' 'WhiteRabbit.mdl' ). avatar _ TAvatar new. avatar loadAvatar: avatarPath. avatar translationX: 0 y: 0 z: 30. space addChild: avatar. activeCamera avatar: avatar. self addZoomNavigator. self makeMirror: space. self makeLight: space. activeCamera avatar enter: space. ^ space! ]style[(23 2 33 3 7 4 15 3 6 7 15 14 5 3 6 7 5 6 57 3 4 18 5 3 4 12 5 3 14 2 4 12 5 3 6 3 7 29 5 3 5 11 6 3 12 3 11 29 4 3 12 11 5 3 10 3 49 3 6 3 7 7 6 13 10 3 6 17 1 6 1 6 2 3 5 11 6 3 12 9 6 3 4 20 4 13 5 3 4 12 5 3 12 15 5 5 5)f1b,f1,f1c152050000,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c202202126,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c152050000,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c202202126,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c202202126,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c202202126,f1,f1c202202126,f1,f1c202202126,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;! ! !VisualizeWhirledTea methodsFor: 'initialize'! initializeSpace "Initialize our space and camera" | tframe | randomColorizer _ Random new. randomColorizer initialize. space _ TSpace new. space url: 'http://www.softcentral.com/informationspace/whirled.tea'. tframe _ TSkyBox new initializeWithFileName: 'JUL'. space addChild: tframe. activeCamera _ TUserCamera new initializeWithViewPort: self. activeCamera viewClip: false. avatarPath _ #('Content' 'Alice' 'Animals' 'WhiteRabbit.mdl' ). avatar _ TAvatar new. avatar loadAvatar: avatarPath. avatar translationX: 0 y: 0 z: 30. space addChild: avatar. activeCamera avatar: avatar. self addZoomNavigator. self makeMirror: space. self makeLight: space. activeCamera avatar enter: space. activeCamera snapshot! ]style[(15 2 33 3 7 4 15 3 6 7 15 14 5 3 6 7 5 6 57 3 6 3 7 29 5 3 5 11 6 3 12 3 11 29 4 3 12 11 5 3 10 3 49 3 6 3 7 7 6 13 10 3 6 17 1 6 1 6 2 3 5 11 6 3 12 9 6 3 4 46 4 12 5 3 12 15 5 3 12 9)f1b,f1,f1c128026000,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c178178102,f1,f1cblue;i,f1,f1cmagenta;,f1,f1c178178102,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c178178102,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c178178102,f1,f1c178178102,f1,f1c178178102,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1! ! !VisualizeWhirledTea methodsFor: 'initialize'! openInWorld super openInWorld. "self initializeDefaultSpace"! ]style[(11 2 5 16 4 24)f1b,f1,f1cmagenta;,f1,f1cmagenta;,f1! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! VisualizeWhirledTea class instanceVariableNames: ''! !VisualizeWhirledTea class methodsFor: 'initialize-release'! initialize "Set up our space and camera, return a new us" | whirledTea | whirledTea _ self new. whirledTea initializeSpace. ^ whirledTea! ! TCube subclass: #WhirledBits instanceVariableNames: 'gotSpin mySpace clippingDepth notifyTarget notifySelector notifyArgs transparency ' classVariableNames: '' poolDictionaries: '' category: 'InformationSpace'! !WhirledBits commentStamp: 'Brian Tabone 10/27/2004 12:02' prior: 0! The Croquet object we use for bit representation in the Croquet space. Subclassing here lets us add features such as spinning on key press, etc.! !WhirledBits methodsFor: 'transform' stamp: 'Brian T 11/8/2004 23:12'! setNotifyTarget: target perform: selector withArguments: args "Set our notification target , selector, and arguments. We call this notfier when we delete ourselves" notifyTarget _ target. notifySelector _ selector. notifyArgs _ args! ! !WhirledBits methodsFor: 'transform'! spin: ang self rotationAroundY: ang. gotSpin == true ifTrue: [ self future: 50 perform: #spin: withArguments: {ang + 5}]! ]style[(6 3 3 4 18 3 2 7 4 4 16 4 13 2 14 6 21 3 3 1 2)f1b,f1cblue;b,f1,f1cmagenta;,f1,f1cblue;i,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c178178102,f1,f1c178178102,f1,f1cblue;i,f1,f1c178178102,f1! ! !WhirledBits methodsFor: 'transform' stamp: 'Brian T 11/8/2004 23:41'! step "Translate this cube a set amount for each step" | currentTransform | currentTransform _ self localTransform. currentTransform a24: currentTransform a24 - 0.15. transparency ifNil: [transparency _ 100]. transparency > 25 ifTrue: [transparency _ transparency - 1. self material transparency: transparency. self changed.]. currentTransform a24 <= 0 ifTrue: [mySpace removeChild: self. self stopStepping. notifyTarget ifNotNil: [notifyTarget perform: notifySelector withArguments: notifyArgs]]! ! !WhirledBits methodsFor: 'accessing'! handlesKeyboard: ptr ^true.! ! !WhirledBits methodsFor: 'accessing'! handlesPointerDown: ptr ^ true! ]style[(20 3 5 4)f1b,f1cblue;b,f1,f1cmagenta;! ! !WhirledBits methodsFor: 'accessing'! isComponent ^ true.! ! !WhirledBits methodsFor: 'accessing' stamp: 'Brian T 11/6/2004 19:47'! keyDown: pointer "Detect a click and start spinning if stationary, if not stationary, stop spinning" | c | c _ pointer event2D keyCharacter. c = $s ifTrue: [gotSpin == nil ifTrue: ["Start spinning" gotSpin _ true. self spin: 0] ifFalse: ["Stop the spinning" "Reset our position" gotSpin _ nil. self spin: 0]]. c = $t ifTrue:[self step].! ]style[(9 7 3 86 3 2 4 1 3 7 24 1 3 2 12 7 4 3 14 16 6 7 3 4 7 4 7 1 16 45 6 7 3 3 7 4 7 1 34)f1b,f1cblue;b,f1,f1c140038000,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1cblue;i,f1,f1c190190114,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c140038000,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c190190114,f1,f1c140038000,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1cmagenta;,f1,f1c190190114,f1! ! !WhirledBits methodsFor: 'accessing' stamp: 'Brian T 11/6/2004 22:11'! parentSpace:parentSpace "Set our parent space, we use this to delete ourselves from that space once we pass our clipping plane" mySpace := parentSpace.! ! !WhirledBits methodsFor: 'accessing'! pick:pointer ^true.! ! VisualizeWhirledTea initialize!