CyCell Logo
HOME >> LEARN>>TUTORIAL Part I>>TUTORIAL Part II

Tutorial Part II

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 Use Field is checked in the Console.

Easier Way to Make Scrums
^

Overview...

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 Scrum_A, Scrum_B, and Scrum_C -- is discussed below.

A few things to notice before we proceed. As in the video, you can check Draw Fields to see each cell's field. The more cells, the more fields. In the video, it may look like there is one red field... in reality, each cell's individual field is being drawn and overlaid with the other. If you wish to see a single cell's field, uncheck Draw Fields and use your spacebar to select a single cell to monitor.

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 Field Editor tab and load tutorial_13_field.png. (Also, ensure that Use Field is checked in the Console. Ditto for all the tutorials that follow.)

STATE Scrum_A - Simple but blobby

The first three lines should be old hat. Notice, though, the FIELD_ACTIVE command; this must be set to 1 in order for our cells to sense their Fields.

The last line is where all the magic happens.

The last two conditions are straightforward: the second condition (ACTUAL_AREA[TYPICAL_AREA,INF]) ensures the cell is big enough to undergo mitosis. The third condition (ADJ_MEMBRANES_PERCENT{MEDIUM}[30,INF]) limits mitosis to the cells which have 30% or more of their membranes not touching neighbors; this is to keep the inner cells from splitting.

The first condition (FIELD[1]) is new. The condition means that the cell must think it's center node is in zone 1 of the Field in order to undergo mitosis.

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.

STATE Scrum_B - A bit less blobby, but chaotic...

One of the things you can try to make Scrum_A less blobby and more precise is to add this line:
FIELD[0] :: DIE
The gist is to have a cell die if its outsize of zone 1. We've also added FIELD_EDGES{0}[0] as a condition to mitosis; a cell won't undergo mitosis if part of it is hanging outside zone 1.

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.

STATE Scrum_C - Using Migration

The last two examples could be improved. For instance, with Scrum_B you could use a U_VAR to have cells that are about to die exude a chemical into their neighbors to inhibit mitosis for a bit. That way, you could slow down the churning on the periphery. There are innumerable other techniques.

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 MIGRATE command. Let's look at each in turn.

FIELD[1]; FIELD_EDGES{0}[1,INF] :: SET_VEC{press_in_vector}[1]; MIGRATE[press_in_vector,-20]

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 V_VAR pointing towards zone 1, and have the cell migrate in that direction.

FIELD[1]; FIELD_EDGES{0}[0] ; ADJ_MEMBRANES_PERCENT{MEDIUM}[20,INF]; :: SET_VEC{press_in_vector}[MEDIUM]; MIGRATE[press_in_vector,-20]

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.

FIELD[0]; FIELD_EDGES{1}[1,INF] :: SET_VEC{press_in_vector}[1]; MIGRATE[press_in_vector,-20]

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.

FIELD[0]; FIELD_EDGES{1}[0] :: SET_VEC{press_in_vector}[Scrum_C]; MIGRATE[press_in_vector,-40]

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 MIGRATE commands are taken care of after the Rules are evaluated. So, if you use a given V_VAR as a parameter for a MIGRATE command, and then subsequently modify that V_VAR variable, you'll get unexpected results.

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 MIGRATE commands might be called, it is important to use different V_VARs for each, or use a dummy-variable as a holder that you add to your main V_VAR as you go.

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 MIGRATE commands, when the time comes to process them, will have the same value as a parameter.

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 Draw Vectors to see which way the cells wish to move.

Regeneration is straightforward. Draw any shape you want. Use the controls under the Manipulate tab to chop off a piece. You'll witness how the organism regrows what has been removed.
ADJUST_SCALE
^

Overview...

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)

STATE Quickstart

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.

STATE Scrum_A

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 //move away from Field zone 0 comment.

Regarding the sentences under the //move towards empty space comment, we're just telling cells that are completley within the body yet aren't entirely squished up next to neighbor cells, to move in the direction of the empty space around them. This just makes them fill in the shape more quickly.

Regarding the sentences under the //Color stuff comment, we're just coloring the head, tail, and tips of our lobster's arms to keep track of what's going on.

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 ADJUST_PP_SCALE command will adjust the cell's idea of the lobster's size. How much is the adjustment? In this case, it's 0.05. Try different values... there's nothing special about 0.05.

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:

STATE Scrum_B

Scrum_B is the same in all respects except that we are manually adjusting the scale components instead of using ADJUST_PP_SCALE. ADJUST_PP_SCALE has the ability to change the proportions of the Field. You can see some of this in the video where some cells think they are part of a fat lobster, and others a skinny lobster. Scrum_B's manipulations, on the other hand, keep the proportions the same throughout. This leads to some subtle differences in the morphollaxis. Left running long enough, both techniques will have the same end-result. But, with some forms, ADJUST_PP_SCALE gets you there faster, and with other forms, it takes longer.

MOD_PP_LOCATION
^

Overview...

We are going to use the MOD_PP_LOCATION command to have our cells respond to obstructions.

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 Manual Placement widget called OBSTRUCTION. We're going to be using it. While technically a cell, an OBSTRUCTION doesn't move, grow, or do much anything. We're going to use it as a wall.

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 //adjust location comment.

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 MOD_PP_LOCATION command.

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 Draw Field on so you can see how the cells' orientation components are altering. Keep in mind the orientation component is not being changed directly, as perhaps it might be in a kinematic 2d physics engine. Instead, the orientations are changing on their own as a result of the little random fluctuations in the cells' Presumed Position components as they continuously try to get in synch.

Using Neoblasts
^

Overview...

This tutorial is more of a demonstration of how one can model neoblasts.

FileName: tutorial_16.cyr

Field FileName: tutorial_16_field.png

ADHESIONS
<Body,Neoblast>(-6.5)
<Neoblast,Neoblast>(4.5)
<Neoblast,MEDIUM>(6)
<Neoblast,SELF>(-15)

GAP_JUNCTIONS
<Body,Body,wound_signal>(0.01)
<Body,Neoblast,wound_signal>(0.01)
<Neoblast,Neoblast,wound_signal>(0.01)
<Body,Neoblast,perimeter>(0.01)
<Body,Neoblast,mitosis_inhibit>(0.01)

V_VAR fill_in_vector
V_VAR press_in_vector
V_VAR press_in_vector_2
V_VAR holder_vector
V_VAR neoblast_movement

U_VAR count
U_VAR rand
U_VAR wound_signal
U_VAR mitosis_inhibit
U_VAR dummy_variable
U_VAR perimeter
U_VAR death_count
//*****************************
//************ Neoblast *******
//*****************************
STATE Neoblast
ENTERING_STATE :: TYPICAL_AREA=30; RGB(245,220,184); FIELD_ACTIVE[1]; count=0; rand=0;
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=1
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=1

//move towards wound_signal
SET_VEC{holder_vector}[wound_signal]; neoblast_movement+=holder_vector;
MIGRATE[neoblast_movement,-90]

//mitosis
count=0
mitosis_inhibit[0]; perimeter(0,INF]; wound_signal[100,INF]; ACTUAL_AREA[TYPICAL_AREA,INF]; :: count=1
count[1] :: SET_RAND{rand}[1,100]
count[1]; rand[1,70] :: MITOSIS{Body}
count[1]; rand(70,100] :: MITOSIS

//special case for single neoblast
ADJ_CELLS[0]:: MITOSIS{Body}

//if too many neoblasts around, change to Body
POISSON[2]; ADJ_CELLS{Neoblast}[2,INF] :: CHANGE_STATE{Body}

//clear signals
mitosis_inhibit=0
perimeter=0

//*****************************
//************ Body ***********
//*****************************
STATE Body
ENTERING_STATE :: TYPICAL_AREA=30; RGB(255,255,255); death_count=0; FIELD_ACTIVE[1]; mitosis_inhibit=0
ACTUAL_AREA(TYPICAL_AREA,INF] :: GOAL_AREA-=.5
ACTUAL_AREA[0,TYPICAL_AREA) :: GOAL_AREA+=.5

//clear variables
perimeter=0
mitosis_inhibit=0

//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{4}[1,INF] :: SET_VEC{holder_vector}[4]; press_in_vector+=holder_vector;
FIELD[0]; :: SET_VEC{holder_vector}[Body]; press_in_vector+=holder_vector
MIGRATE[press_in_vector,-40]

//signal if cell is on perimeter
ADJ_MEMBRANES_PERCENT{MEDIUM}[20,INF] :: perimeter=1

//wound signal
FIELD_EDGES{0}[0]; ADJ_MEMBRANES_PERCENT{MEDIUM}[20,INF] :: wound_signal+=10

//mitosis_inhibit signal
!FIELD_EDGES{0}[0]; :: mitosis_inhibit=1;

//reduce diffusion of wound_signal
!FIELD_EDGES{0}[0] :: wound_signal*=.98

//tint color according to wound_signal
RGB(255,255,255)
dummy_variable=wound_signal; dummy_variable*=.3
wound_signal[mitosis_inhibit,INF] :: BLUE-=dummy_variable
wound_signal[mitosis_inhibit,INF] :: RED-=dummy_variable

//kill if outside field too long
FIELD[0] :: death_count+=1
death_count[50,INF] :: DIE

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.

STATE Body

This state has the typical MIGRATE commands that keep everything within the field. This should be old-hat.

You'll note there is no MITOSIS command in this state. They Body cells can't reproduce. But, as they are using fields, they know when they need to reproduce. If they are completely within the field, but over 20% of their membranes are exposed to the MEDIUM, the cell knows it needs a neighbor. When these conditions are met, it starts increasing the wound_signale U_VAR. (Later in the state, you'll see that the cell's amount of wound_signal corresponds to a greenish-tint, so that you can visualize what's happening.)

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 U_VAR called mitosis_inhibit to 1.

If you look at the GAP_JUNCTIONS, you'll see that wound_signal can spread from cell to cell; not so with mitosis_inhibit. The only external cell that can read its mitosis_inhibit value is a Neoblast. Likewise, there is another 'private' flag-like variable called perimeter which is set to 1 if the cell is at the edge of whatever is being grown. We'll see how these flags are used below.

STATE Neoblast

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 MITOSIS.

Instead of just splitting, though, they perform a quick test using the SET_RAND command and a helper variable so that 30% of the time the daughter cell will be another Neoblast and 70% of the time, it will be a new Body cell.

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 POISSON condition as well so that a group of them Neoblasts won't simultaneously change.)

The ADHESIONS settings keep Neoblasts away from each other and bit squirmy. (This is fairly similar to what was discussed in the GAP_JUNCTIONS tutorial above.)

Demonstration...

Things to watch for in the video:

ON TO PART III...