These tutorials all involve the use of Fields. Don't know what a Field is? Check out this page for a basic overview.
Each of the tutorials has a corresponding Field image. Be sure to load it. Also, ensure that
This tutorial will show you three basic ways to grow some cells into a particular shape using Fields. We call these basic collections of cells scrums.
FileName: tutorial_13.cyr
Field FileName: tutorial_13_field.png
V_VAR press_in_vector
U_VAR death_count
STATE Scrum_A
ENTERING_STATE :: TYPICAL_AREA=50; RGB(255,255,255); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=.5
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=.5
FIELD[1]; ACTUAL_AREA[TYPICAL_AREA,INF]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF] :: MITOSIS
STATE Scrum_B
ENTERING_STATE :: TYPICAL_AREA=50; RGB(255,255,255); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=.5
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=.5
FIELD[1]; FIELD_EDGES{0}[0]; ACTUAL_AREA[TYPICAL_AREA,INF]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF] :: MITOSIS
FIELD[0] :: DIE
STATE Scrum_C
ENTERING_STATE :: TYPICAL_AREA=40; RGB(255,255,255); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
FIELD[1]; FIELD_EDGES{0}[0]; ACTUAL_AREA[TYPICAL_AREA,INF]; ADJ_MEMBRANES_PERCENT{MEDIUM}[50,INF]; :: MITOSIS
FIELD[1]; FIELD_EDGES{0}[1,INF] :: SET_VEC{press_in_vector}[1]; MIGRATE[press_in_vector,-20]
FIELD[1]; FIELD_EDGES{0}[0] ; ADJ_MEMBRANES_PERCENT{MEDIUM}[20,INF]; :: SET_VEC{press_in_vector}[MEDIUM]; MIGRATE[press_in_vector,-20]
FIELD[0]; FIELD_EDGES{1}[1,INF] :: SET_VEC{press_in_vector}[1]; MIGRATE[press_in_vector,-20]
FIELD[0]; FIELD_EDGES{1}[0] :: SET_VEC{press_in_vector}[Scrum_C]; MIGRATE[press_in_vector,-40]
FIELD[0] :: death_count+=1
FIELD[1] :: death_count=0
death_count[100,INF] :: DIE
A Field lets our cells know where they are so that they know when to stop growing. In this way, we can have them grow into specific forms.
It isn't as complicated as it looks. The video shows three different ways to create a form. (There are many others you can come up with.)
Each version -- specifically, cell states
A few things to notice before we proceed. As in the video, you can check
You'll also notice the fields jostling a bit... this is because each cell is varying its Presumed Position (PP) orientation and location components to try to get in synch with its neighbors.
Go to the
The first three lines should be old hat. Notice, though, the
The last line is where all the magic happens.
The last two conditions are straightforward: the second condition (
The first condition (
As the cells split and jostle about, and as they adjust their Presumed Positions, eventually, the cells on the periphery will realize they must be outside zone 1. They consquently quit splitting.
Using just this simple condition, the cells will more or less make the desired form. But, as shown in the video, it will be a bit 'blobby' and not very precise.
In any case, the take-away from Scrum_A is that this technique, although quick and easy, sort of sucks with the finer details of a shape. Let's try something else.
One of the things you can try to make Scrum_A less blobby and more precise is to add this line:
The gist is to have a cell die if its outsize of zone 1. We've also added
The problem, you'll find, is that the cells constantly die, and then new ones are formed, and the periphery of your creature is in a constant cycle of flux. Seems a bit sloppy.
The last two examples could be improved. For instance, with Scrum_B you could use a
But, by far, the best technique is to do what real cells do; namely, move.
In Scrum_C we have more or less the same first few lines. But, then we have four sentences that involve the
If the cell is in zone 1, but some of its edges are hanging outside zone 1, we want the cell to move back in torwards zone 1. So, we set a
In the alternative, if the cell is in zone 1, and none of its edges are hanging outside zone 1, we check to see if it has any empty space on its membrane. If so, we tell the cell to move towards that empty space a bit. Why? Well, just to make the cells spread out as they're growing and fill in the zone 1.
In the alternative, if the cell's center is outside of zone 1, but some of its edges are still in zone 1, then clearly we want to make the cell move back to zone 1.
Finally, if the cell is entirely outside zone 1, he's basically screwed. At the very least, lets have him move in towards his neighbors; with any luck, he will worm his way back to zone 1.
Remember that the
In this example, each of the four sentences are mutually exclusive; only one, at most, will be triggered. If, on the other hand, you have a scenario where several
For instance:
SET_VEC{holder}[1] :: press_in_vector+=holder;
SET_VEC{holder}[2] :: press_in_vector+=holder;
SET_VEC{holder}[3] :: press_in_vector+=holder;
MIGRATE[press_in_vector, -20]
would work just fine.
On the other hand, the following:
SET_VEC{press_in_vector}[1] ::MIGRATE[press_in_vector, -20]
SET_VEC{press_in_vector}[2] ::MIGRATE[press_in_vector, -20]
SET_VEC{press_in_vector}[3] ::MIGRATE[press_in_vector, -20]
will not work as you might expect, as all the
The last three lines take care of cells that stay outside of zone 1 too long... each cycle they are in zone 0, a counter increases, and when it reaches 100, the cell will die.
As you'll see in the video, this technique leads to a nice precise fit. You can check
Regenerating a missing body part is easy when all the cells are capable of dividing... the organism just grows back what it needs. But, what if the cells can't divide? This tutorial will demonstrate how an organism, which has had something amputated, can reconfigure its remaining cells to form a smaller version of itself by adjusting the scale component of each cell's Presumed Position.
FileName: tutorial_14.cyr
Field FileName: tutorial_14_field.png
V_VAR press_in_vector
V_VAR press_in_vector_2
V_VAR fill_vector
V_VAR holder_vector
U_VAR death_count
ADHESIONS
<Quickstart,Quickstart>(-6.0)
<Quickstart,Scrum_A>(-6.0)
<Quickstart,Scrum_B>(-6.0)
<Scrum_A,Scrum_A>(-6.0)
<Scrum_B,Scrum_B>(-6.0)
STATE Quickstart
ENTERING_STATE :: TYPICAL_AREA=35; RGB(255,255,255); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
!FIELD[0]; FIELD_EDGES{0}[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[20,INF]; ACTUAL_AREA[TYPICAL_AREA,INF] :: MITOSIS
//move away from field 0
!FIELD[0]; FIELD_EDGES{0}[1,INF] :: SET_VEC{press_in_vector_2}[0]; MIGRATE[press_in_vector_2,40]
FIELD[0]; FIELD_EDGES{1}[1,INF] :: SET_VEC{holder_vector}[1]; press_in_vector+=holder_vector;
FIELD[0]; FIELD_EDGES{3}[1,INF] :: SET_VEC{holder_vector}[3]; press_in_vector+=holder_vector;
FIELD[0]; FIELD_EDGES{4}[1,INF] :: SET_VEC{holder_vector}[4]; press_in_vector+=holder_vector;
FIELD[0]; :: SET_VEC{holder_vector}[Quickstart]; press_in_vector+=holder_vector;
MIGRATE[press_in_vector,-40]
//move towards empty space
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[1]; fill_vector+=holder_vector;
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[3]; fill_vector+=holder_vector;
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[4]; fill_vector+=holder_vector;
MIGRATE[fill_vector,-40]
FIELD[0] :: death_count+=1
!FIELD[0] :: death_count=0
death_count[5,INF] :: DIE
ADJ_CELLS{Default_Blue}[1,INF] :: CHANGE_STATE{Scrum_A}
ADJ_CELLS{Scrum_A}[1,INF] :: CHANGE_STATE{Scrum_A}
ADJ_CELLS{Default_Yellow}[1,INF] :: CHANGE_STATE{Scrum_B}
ADJ_CELLS{Scrum_B}[1,INF] :: CHANGE_STATE{Scrum_B}
STATE Scrum_A
ENTERING_STATE :: TYPICAL_AREA=35; RGB(232, 208, 176); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
//move away from field 0
!FIELD[0]; FIELD_EDGES{0}[1,INF] :: SET_VEC{press_in_vector_2}[0]; MIGRATE[press_in_vector_2,40]
FIELD[0]; FIELD_EDGES{1}[1,INF] :: SET_VEC{holder_vector}[1]; press_in_vector+=holder_vector;
FIELD[0]; FIELD_EDGES{3}[1,INF] :: SET_VEC{holder_vector}[3]; press_in_vector+=holder_vector;
FIELD[0]; FIELD_EDGES{4}[1,INF] :: SET_VEC{holder_vector}[4]; press_in_vector+=holder_vector;
FIELD[0]; :: SET_VEC{holder_vector}[Scrum_A]; press_in_vector+=holder_vector;
MIGRATE[press_in_vector,-40]
//move towards empty space
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[1]; fill_vector+=holder_vector;
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[3]; fill_vector+=holder_vector;
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[4]; fill_vector+=holder_vector;
MIGRATE[fill_vector,-90]
//Adjust Scale
FIELD[1,4]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF]; :: ADJUST_PP_SCALE[.05]
FIELD[0]; :: PP_SCALED_WIDTH*=1.05; PP_SCALED_HEIGHT*=1.05
PP_SCALED_WIDTH[3,INF] :: PP_SCALED_WIDTH=3
PP_SCALED_HEIGHT[3,INF] :: PP_SCALED_HEIGHT=3
//Color stuff
RGB(100,100,200)
RGB(232, 208, 176)
FIELD_EDGES{4}[1,INF]::RGB(64, 173, 36)
FIELD_EDGES{3}[1,INF]:: RGB (173, 36, 54)
STATE Scrum_B
ENTERING_STATE :: TYPICAL_AREA=35; RGB(232, 208, 176); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
//move away from field 0
!FIELD[0]; FIELD_EDGES{0}[1,INF] :: SET_VEC{press_in_vector_2}[0]; MIGRATE[press_in_vector_2,40]
FIELD[0]; FIELD_EDGES{1}[1,INF] :: SET_VEC{holder_vector}[1]; press_in_vector+=holder_vector;
FIELD[0]; FIELD_EDGES{3}[1,INF] :: SET_VEC{holder_vector}[3]; press_in_vector+=holder_vector;
FIELD[0]; FIELD_EDGES{4}[1,INF] :: SET_VEC{holder_vector}[4]; press_in_vector+=holder_vector;
FIELD[0]; :: SET_VEC{holder_vector}[Scrum_B]; press_in_vector+=holder_vector;
MIGRATE[press_in_vector,-40]
//move towards empty space
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[1]; fill_vector+=holder_vector;
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[3]; fill_vector+=holder_vector;
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[15,INF] :: SET_VEC{holder_vector}[4]; fill_vector+=holder_vector;
MIGRATE[fill_vector,-90]
//Adjust Scale
!FIELD[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF]; FIELD_EDGES{0}[10,INF] :: PP_SCALED_WIDTH*=1.05; PP_SCALED_HEIGHT*=1.05
FIELD_EDGES{0}[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF] :: PP_SCALED_WIDTH*=.95; PP_SCALED_HEIGHT*=.95;
FIELD[0]; :: PP_SCALED_WIDTH*=1.05; PP_SCALED_HEIGHT*=1.05
PP_SCALED_WIDTH[3,INF] :: PP_SCALED_WIDTH=3
PP_SCALED_HEIGHT[3,INF] :: PP_SCALED_HEIGHT=3
//Color stuff
RGB(100,100,200)
RGB(232, 208, 176)
FIELD_EDGES{4}[1,INF]::RGB(64, 173, 36)
FIELD_EDGES{3}[1,INF]:: RGB (173, 36, 54)
This tutorial is all about doing expirements on a little lobster-looking thing, the cells of which can't undergo mitosis.
This raises the question of how our little lobster is going to grow in the first place to its normal size and shape if its cells can't undergo mitosis! We'll show you a quick hack that we at CYCELL use when we want to get a basic body-plan on the screen.
Specifially, place a Quickstart cell... it will quickly grow into a lobster. Once its done growing -- should just take a second or two -- we can magically turn all the cells into State_A or State_B by 'touching' the lobster with a Default_Blue or Default_Yellow. There's nothing 'biological' about this; like we said, its just a hack to quickly get what we want in the Petri Dish: a fully-grown lobster composed entirely of State_A or State_B cells.
The first and second commented section keep the cells within the desired field. It's similar to what we did in the previous tutorial. Briefly, let's go through each sentence under the
Regarding the sentences under the
Regarding the sentences under the
NOW... on to the IMPORTANT part:
//Adjust Scale
FIELD[1,4]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF]; :: ADJUST_PP_SCALE[.05]
FIELD[0]; :: PP_SCALED_WIDTH*=1.05; PP_SCALED_HEIGHT*=1.05
PP_SCALED_WIDTH[3,INF] :: PP_SCALED_WIDTH=3
PP_SCALED_HEIGHT[3,INF] :: PP_SCALED_HEIGHT=3
We're interested in cells that are within the body but have 30% or so empty space around them. The percentage is somewhat arbitrary... what we're trying to do is focus on cells that aren't completely surrounded by neighbors. Specifically, we want these cells to wonder why they are not completely surrounded by neighbors.
For instance, if we were to chop off our lobster's arm, the cells at the stump would find temselves inside the body, yet missing some neighbors. If they could undergo mitosis, this might trigger them to start dividing. But, what if they can't undergo mitosis? Being smart little cells, they would begin to assume that they are incorrect about the size of the lobster of which they are part. Maybe, they would think, they're part of a smaller lobster.
Likewise, let's say one of the cells is inside the body, yet has a big portion of itself dangling out into Field zone 0. Well, the cell will try to migrate away from the edge, but eventually, it might start thinking that perhaps the lobster of which he is part is actually larger than he thinks.
In both cases, the
The second sentence covers a special case and isn't entirely necessary. Essentially, if the cell finds itself entirely outside the body, it's reasonable for the cell to think his idea of the lobster must be too small... so he increases the scale components by 5%. Now, merely increasing the scale isn't going to get the cell back into Field zone nos. 1-4. But, remember that each cell's scale components are effected by their neighbors' scale components; so, for a cell stuck outside, this signal that he thinks the lobster needs to be bigger, will trickle through the scrum, and eventually, the cells' idea of the lobster's size will result in our lonely outlier getting pulled back into the body.
The last two sentences are just caps put on the scale values; there is a lot going on under the hood and some scenarios might make the scale component of a cell get crazy large. No matter what, though, these two sentences will put a limit on the value. (There is a lower limit as well of 0.01. It's built-in to the Program so there's no need to control for minimums unless you find 0.01 isn't cutting it.)
What to look for in the video:
Scrum_B is the same in all respects except that we are manually adjusting the scale components instead of using
We are going to use the
FileName: tutorial_15.cyr
Field FileName: tutorial_15_field.png
V_VAR press_in_vector
U_VAR death_count
V_VAR translate_vector
STATE Scrum_A
ENTERING_STATE :: TYPICAL_AREA=40; RGB(255,255,255); FIELD_ACTIVE[1]
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1
FIELD[1]; FIELD_EDGES{0}[0]; ACTUAL_AREA[TYPICAL_AREA,INF]; ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF]; :: MITOSIS
FIELD[1]; FIELD_EDGES{0}[1,INF] :: SET_VEC{press_in_vector}[1]; MIGRATE[press_in_vector,-20]
FIELD[1]; FIELD_EDGES{0}[0] ; ADJ_MEMBRANES_PERCENT{MEDIUM}[20,INF]; :: SET_VEC{press_in_vector}[MEDIUM]; MIGRATE[press_in_vector,-20]
FIELD[0]; FIELD_EDGES{1}[1,INF] :: SET_VEC{press_in_vector}[1]; MIGRATE[press_in_vector,-20]
FIELD[0]; FIELD_EDGES{1}[0] :: SET_VEC{press_in_vector}[Scrum_A]; MIGRATE[press_in_vector,-40]
//adjust location
FIELD_EDGES{1}[1,INF]; ADJ_CELLS{OBSTRUCTION}[1,INF] :: SET_VEC{translate_vector}[OBSTRUCTION]; MOD_PP_LOCATION[translate_vector,2.5,0]
FIELD[0] :: death_count+=1
FIELD[1] :: death_count=0
death_count[100,INF] :: DIE
You may have noticed a default cell state in the
The code contains the typical stuff to get your initial cell to grow into, and maintain, the long slender rod-like shape contained in the field file.
The only new stuff is the sentence under the
The sentence has us focus in on cells that are entirely within the body and which are touching an OBSTRUCTION. If these conditions are met, we set a vector and process the
Normally, the location component of a cell's Presumed Position is adjusted as a matter of course, depending on its neighbor's membranes and the like, as described ad nauseum on the Fields page. But, we can also adjust it manually, as we do here. Why would we want to?
Well, to answer that, let us view the world from the cell's point of view...
Let's say a cell is completely within the body. He's not touching any of the emptiness that is zone 0. So, the cell would naturally expect there to be neighboring cells touching him on all sides.
But, alas, to his shock he finds one of his sides -- let's say the right side -- is touching the non-living OBSTRUCTION!
The cell thinks, "If I am where I think I am, there should be a cell on my right side. Instead, there's this OBSTRUCTION thing. I must not be in the correct position."
He might consider that he is too far to the right... but that would make no sense... to his left, afterall, there are a bunch of cells pressed up against him and they all think they are in the correct position.
Instead, the cell concludes he must be too far to the left... it the only alternative.
So, he moves his presumed location a smidgen to the right. On the next cycle, the OBSTRUCTION is still there. Damn. So, he adjusts it again. Eventually, he will move far enough right that the membranes touching the OBSTRUCTION are hanging out in Zone 0. At that point, the problem is solved. For, the cell couldn't care less if the OBSTRUCTION is touching his right side as his right side is in Zone 0 and he doesn't expect any neighbors to be out in Zone 0.
This is precisly what is happening in this sentence: if the conditions are met, the cell sets a vector in the direction of the OBSTRUCTION and adjusts the location component of his Presumed Position in that direction. (Keep in mind, the cell doesn't literally move; rather, his mental picture of where he is moves.)
The upshot of all this is pretty dang neat. When the growing column hits an obstruction, it will bend.
You can see this in the video. We've turned the
This tutorial is more of a demonstration of how one can model neoblasts.
FileName: tutorial_16.cyr
Field FileName: tutorial_16_field.png
In real living systems, cell reproduction is often limited to special cells called neoblasts. Instead of a skin cell dividing, for instance, another type of cell creates a new skin cell.
In this example, our neoblasts will travel throughout our little man, and if they reach an area that is in need of new Body cells, they will create them.
This state has the typical
You'll note there is no
So, a cell that is need of neighbors will send out wound_signal. At the same time, if a cell has part of its membrane sticking out into zone 0, it knows that it doesn't need any more neighbors. It will set a
If you look at the
The Neoblast cells constantly move towards the highest concentration of wound_signal. When they get to a high concentration of same AND they are not touching a Body cell that contains mitosis_inhibit AND they are touching a Body cell that contains perimeter, then they need to undergo
Instead of just splitting, though, they perform a quick test using the
That's about it. Except, with all these Neoblasts squirming around, they may have a tendancy to clumped up. So, we change a Neoblast to a Body cell if it is touching 2 or more other Neoblasts. (We stuck in a
The
Things to watch for in the video: