/// <summary> /// Create a list of Channels, using the mesh hierarchy to get the visible frames representing the channels, /// and the gene or the physiology to get the species-specific parameters such as chemical selectivity /// </summary> /// <param name="gene"></param> /// <returns></returns> private Channel[] LoadChannels(Gene gene) { // Read any channels defined by the gene Channel[] channel = gene.GetChannels(); // If none are defined, create some and load with the default chemical#s from the cell type if (channel.Length == 0) { ChannelData[] chanData = physiology.GetChannelData(); // read the default src, dst, chem if (chanData != null) { channel = new Channel[chanData.GetLength(0)]; // create enough channels for (int c = 0; c < channel.Length; c++) // copy in the default chemical selectivity { channel[c] = new Channel(); channel[c].chemical = chanData[c].Chemical; channel[c].constant = chanData[c].Constant; // and default for user-definable constant } } } //// Get a list of the "chan#" frames (visible organelles), in order, then copy to the channels //JointFrame[] channelFrame = null; //try //{ // channelFrame = JointFrame.IndexFrameType(rootFrame, JointFrame.FrameType.Channel); // for (int c = 0; c < channel.Length; c++) // { // channel[c].organelle = channelFrame[c]; // } //} //catch //{ // throw new SDKException(this.name + " has different numbers of channels in the cell type (" // + channel.Length + ") and the mesh (" + channelFrame.Length + ")"); //} return channel; }
/// <summary> /// Get a clone of this Cell from the library, loading it first if necessary /// </summary> /// <returns>A modifiable clone of the named Cell</returns> /// <param name="gene">The gene containing the cell's properties, orientation and wiring</param> /// <param name="owner">The organism that owns me</param> public static Cell Get(Gene gene, Organism owner) { Cell cell = null; // Get name of the cell from the gene string fullname = gene.XFile(); // if the library already contains a Cell of that name, that's what we'll clone from if (library.ContainsKey(fullname)) { cell = (Cell)library[fullname]; // Debug.WriteLine(" Cell: Created new Cell instance: "+name+":"+cell.instance); } // otherwise, create a new entry in the library by loading a whole X file else { cell = new Cell(fullname); // Debug.WriteLine(" Cell: Created new Cell archetype: "+name+":"+cell.instance); } // Return a cloned instance of this Cell Cell newCell = cell.Clone(gene, owner); newCell.CreateBoundMarker(); // temp: create marker to show bounding sphere return newCell; }
/// <summary> /// Given the gene that has/would be used to create this cell, update its parameters /// to reflect the current wiring setup, etc. Then recurse through children and siblings /// until the whole tree has been constructed /// </summary> /// <param name="gene"></param> public void UpdateGene(Gene gene) { // The gene type is our cell type, which we get from ToString() gene.Type = physiology.ToString(); // Create a new list of channels from the user-defined wiring gene.Channels = new List<Channel>(); gene.Channels.AddRange(channel); // Get our current plug orientation matrix gene.Orientation = PlugOrientation; // Get the name of our parent socket (if we're not the root) if (parentSocket != null) { gene.Socket = parentSocket.Name.Substring(0,4); // convert "skt3-0" to skt3 } // Recursively build up the rest of the genome... // If we have a child, create a new gene for it, link it into the genome tree and ask the child to update this gene if (this.firstChild != null) { gene.FirstChild = new Gene(); this.firstChild.UpdateGene(gene.FirstChild); } // If we have a sibling, blah blah blah if (this.sibling != null) { gene.Sibling = new Gene(); this.sibling.UpdateGene(gene.Sibling); } }
/// <summary> /// Clone this cell /// </summary> /// <param name="gene">The gene specifying this cell instance's orientation, wiring, etc.</param> /// <param name="owner">The organism that owns me</param> private Cell Clone(Gene gene, Organism owner) { // Make a shallow copy of all members Cell newCell = (Cell)this.MemberwiseClone(); // Replace members that are CLONED copies of objects in the master... newCell.rootFrame = JointFrame.CloneHierarchy(rootFrame); // my unique copy of the frame hierarchy // TODO: Clone any other members here // Replace or create members that are UNIQUE to this instance... newCell.instance = instance++; // my instance number (tally in master) newCell.PlugOrientation = gene.Orientation; // my orientation in the parent socket newCell.owner = owner; // my owning organism newCell.joint = JointFrame.IndexFrameType( newCell.rootFrame,JointFrame.FrameType.Animating); // lists of my animation, hotspot & skt frames newCell.hotspot = JointFrame.IndexFrameType( newCell.rootFrame, JointFrame.FrameType.Hotspot); newCell.socket = JointFrame.IndexFrameType( newCell.rootFrame, JointFrame.FrameType.Socket); newCell.physiology = Physiology.LoadPhysiology(newCell.group, newCell.name, newCell.variantName, // my Physiology class containing functionality newCell, newCell.joint.Length, newCell.socket.Length); newCell.GetMyVariantIndex(); // Convert variant name into an index before trying to access channel data newCell.channel = newCell.LoadChannels(gene); // lists of channel info from gene AND mesh frames newCell.collisionFrames = new List<JointFrame>(); newCell.GetMeshFrames(newCell.rootFrame, newCell.collisionFrames, false); // a list of mesh-containing frames for collision tests // Set all the internal (organelle) frames to invisible, because they aren't normally rendered newCell.HideOrganelles(); // TODO: Initialise other instance-specific members here // Now that everything is set up, call the Physiology subclass's Init() method to allow the physiology to initialise itself newCell.physiology.Init(); // Add a device reset handler to rebuild this cell's resources Engine.Device.DeviceReset += new System.EventHandler(newCell.OnReset); return newCell; }
/// <summary> /// Recursively walk the gene tree, extract the Cell filenames, clone the Cell from the library and /// record their interconnections (root->joint) /// </summary> /// <param name="gene">the gene being expressed</param> /// <param name="parent">the Cell that is the parent of this new one</param> /// <returns>the Cell created by this gene</returns> private Cell GetCells(Gene gene, Cell parent) { // Get the X filename from the gene and either load or fetch that Cell from the library, Cell s = Cell.Get(gene, this); // Add it to the flat partlist partList[numParts++] = s; // Locate this cell's SOCKET by locating the frame with the correct name in the // parent (if any) if (parent!=null) s.Attach(parent, gene.Socket); // if the gene has any siblings, recursively attach their structures as siblings of this one if (gene.Sibling!=null) s.Sibling = GetCells(gene.Sibling, parent); // we share a parent // if the gene has a child, recursively attach this as the child of this one if (gene.FirstChild!=null) s.FirstChild = GetCells(gene.FirstChild, s); // I am the parent return s; // return the new Cell to the parent/sibling }
/// <summary> /// Add a cell of the selected type to the creature at the selected socket (if any) /// </summary> public void Add(string name) { // If no socket is selected, quit if (Lab.SelectedSocket == null) return; // If the selected socket already has a cell attached to it, we mustn't add another! if (SelectedSocketIsOccupied()) return; // OK to add try { Gene gene = new Gene(name); // Create a gene for the cell type Cell newCell = Cell.Get(gene, Lab.SelectedOrg); // create a cell from the gene if (Lab.SelectedCell.FirstChild == null) // if the selected cell has no children Lab.SelectedCell.FirstChild = newCell; // attach the new cell to it as the first child else // otherwise, drop to the first child and find its last { // sibling. The new cell becomes the youngest sibling Cell sib = Lab.SelectedCell.FirstChild; while (sib.Sibling != null) sib = sib.Sibling; sib.Sibling = newCell; } newCell.Attach(Lab.SelectedCell, Lab.SelectedSocket); // record the cell's .parent and .parentSocket Refresh(); // update org's part list, channels, etc. Lab.SelectedCell = newCell; // Select the cell we've just added newCell.SelectFirst(); // and the first socket on that cell } catch (Exception e) { throw new SDKException("Unable to create new cell of selected type: " + name, e); } }
/// <summary> /// Read genome from XML stream /// </summary> /// <param name="xml"></param> private void Read(XmlTextReader xml) { string tag = ""; while (xml.Read()) // for each node in the file... { if (xml.NodeType == XmlNodeType.Element) // <tag> rather than final </document> { switch (xml.Name) // depending on tag... { case "gene": // <gene> the ROOT gene </gene> root = Gene.RecursiveRead(xml,null); // read recursively to support part hierarchy break; // Any other root level tags go here // anything else must be a data tag, so wait for its text to arrive default: tag = xml.Name; break; } } // text within tags else if (xml.NodeType == XmlNodeType.Text) { switch (tag) { // data tags relating to the Genome as a whole (not a gene) go here default: throw new XmlException("unexpected tag type: "+tag); } } } }
/// <summary> /// Print an entire genome's hierarchy to the debug output stream /// </summary> /// <param name="root">root of hierarchy</param> /// <param name="level">current depth into hierarchy (set this to 0)</param> /// <returns></returns> public static void DebugHierarchy(Gene root, int level) { // set up an indent level string indent = ""; for (int i=0; i<level; i++) indent += "\t"; Debug.WriteLine(indent+"*** GENE ["+root.Type+"] ***"); Debug.WriteLine(indent+" Socket: ["+root.Socket+"]"); // continue walking the tree if (root.FirstChild!=null) { DebugHierarchy(root.FirstChild, level+1); } if (root.Sibling!=null) { DebugHierarchy(root.Sibling, level); } }
/// <summary> /// Create a minimal genome for building a new creature. /// The genome consists of a single gene defining a CORE cell /// </summary> public Genome() { root = new Gene("Core"); }
/// <summary> /// Recurse through a genome writing the genes to an XML file /// </summary> /// <param name="xml">The output stream</param> /// <param name="gene">The gene to write (and then descend into)</param> public static void RecursiveWrite(XmlWriter xml, Gene gene) { xml.WriteStartElement("gene"); xml.WriteElementString("type", gene.Type); if (gene.Socket!=null) xml.WriteElementString("socket", gene.Socket); xml.WriteElementString("orientation", EncodeMatrix(gene.Orientation)); // Write <channel> nodes foreach (Channel c in gene.Channels) { c.WriteXml(xml); } // If this gene has children, nest them inside if (gene.FirstChild != null) RecursiveWrite(xml, gene.FirstChild); // Close the </gene> xml.WriteEndElement(); // If this gene has siblings, write them at the same level if (gene.Sibling != null) RecursiveWrite(xml, gene.Sibling); }
/// <summary> /// Read this <gene></gene> and any nested genes too /// </summary> /// <param name="xml"></param> /// <param name="previous">previous SIBLING of the gene being created</param> /// <returns></returns> public static Gene RecursiveRead(XmlTextReader xml, Gene previous) { Gene gene = null; Gene elder = null; string tag = ""; while (xml.Read()) // for each node... { // <tag> if (xml.NodeType == XmlNodeType.Element) { // Handle the tag switch (xml.Name) // depending on tag... { // A child gene definition is nested inside this one, so create it recursively case "gene": // the first such gene is the child of this one if (gene.FirstChild==null) { gene.FirstChild = RecursiveRead(xml,null); elder = gene.FirstChild; } // subsequent genes are siblings of each other else { elder.Sibling = RecursiveRead(xml,null); elder = elder.Sibling; } break; // A channel case "channel": gene.Channels.Add(ReadChannel(xml)); break; // anything else must be a standalone <tag>text</tag>, so wait for its text to arrive default: tag = xml.Name; break; } } // text within tags else if (xml.NodeType == XmlNodeType.Text) { switch (tag) // action depends on the tag this text is part of { // create the named gene as a sibling of the previous one case "type": // <type> this gene's type </type> gene = new Gene(); // start a new gene gene.Type = xml.Value; // store its type name break; // <attachment> case "socket": gene.Socket = xml.Value; break; // <position> case "orientation": gene.Orientation = ReadMatrix(xml.Value); break; // Other gene initialisers here default: throw new XmlException("unexpected gene tag type: "+tag); } } // closing tag else if (xml.NodeType == XmlNodeType.EndElement) { if (xml.Name == "gene") { return gene; // return the gene I just created } } } throw new XmlException("missing </gene> tag"); // should always return before end of file }