Tuesday 29 March 2011

Passed PhD viva with minor corrections..
:-)

Friday 25 March 2011


Lee and I have had our paper accepted for the Istanbul ISEA 2011 in Spetember, the paper is about our VAINS collaboration. No idea how we are going to fund it...we might have to hitch-hike and sleep in ditches. But it's pleasing to get through, apparently there were thousands of applicants, so that's a bit of validation for our project.

"ISEA2011 Istanbul is the international festival of new media, electronic and digital arts. The 17th International Symposium on Electronic Art, a leading world conference and exhibition event for art, media and technology, is scheduled for September 14 to 21, 2011 in Istanbul, Turkey. The ISEA2011 Istanbul exhibition will coincide with the Istanbul Biennial and will provide a fantastic opportunity to showcase contemporary new media arts. "

My viva rehersal on Monday seemed to go ok, but I've got to work on making it clearer exactly how the software works with the book and egg. No one liked my film much so I dont think I'll bother to show it, probably wont have time anyway..

Wednesday 23 March 2011

South film


Other places to load South content, just as good on a kindle or mobile phone...

Our beautiful robot

So today we built our first Buridan's Robot prototype, now it responds to light, next we will give it conflicting desires. Below very simple Urbi code to make it communicate when light is a certain brightness:






















load("/home/student/creativerobotics/urbi-for-bioloid/dynamixel.u");

 var Global.d = Dynamixel.new;

class Thing
{

function init()
{
/* var this.shoulder =
Dynamixel.Device.new(d,1,Dynamixel.DeviceDesc.AX12);
var this.elbow =
Dynamixel.Device.new(d,2,Dynamixel.DeviceDesc.AX12);
var this.wrist =
Dynamixel.Device.new(d,3,Dynamixel.DeviceDesc.AX12);
var this.hand =
Dynamixel.Device.new(d,4,Dynamixel.DeviceDesc.AX12);
*/

var this.sensorH = Dynamixel.Device.new(d,100,
Dynamixel.DeviceDesc.AXS1);
var this.sensorRead = 0;



};

function start_feedback(){
detach
({
every(0.1s)
{
this.sensorRead = this.sensorH.lightLeft;
}
});

};


function startReact(){
whenever(sensorRead10)
{

echo("lighjt");}
};



};
var thing2 = Thing.new;
thing2.start_feedback;

thing2.startReact;


Wednesday 16 March 2011

Creative Robotics Basic plan for Buridan's Robot a robot that needs help to make choices


Basic plan for Buridan's Robot a robot that needs help to make choices
//we are finally getting somewhere with Urbi in Creative Robotics

loadModule("urbi/qt-for-urbi");
load("thing.u");

var thing = Thing.new;

function conv(a){

var res = ((a* 3.4/100.0) -1.7);
res
}|

function gui_stuff()
{
var window = Qt.Widget.new;//create a window
var layout = Qt.GridLayout.new;//create the layout to display the
// things in the window
window.setLayout(layout);//set the layout in the window !notice the comment is longer than the line ....

//here you create the sliders
var shoulder_slider = Qt.Slider.new;
var elbow_slider = Qt.Slider.new;
var wrist_slider = Qt.Slider.new;
var hand_slider = Qt.Slider.new;


//here you add the stuff to the layout
layout.addWidget(shoulder_slider, 0, 0, Qt.Alignment.AlignHCenter);
layout.addWidget(elbow_slider, 0, 1, Qt.Alignment.AlignHCenter);
layout.addWidget(wrist_slider, 0, 2, Qt.Alignment.AlignHCenter);
layout.addWidget(hand_slider, 0, 3, Qt.Alignment.AlignHCenter);


//the following is how you catch an event, notice the event?(var msg) construct
at (shoulder_slider.sliderMoved?(var msg))
thing.shoulder.targetPos=conv(msg);


at (elbow_slider.sliderMoved?(var msg))
echo(msg);


at (wrist_slider.sliderMoved?(var msg))
echo(msg);


at (hand_slider.sliderMoved?(var msg))
echo(msg);


window.show;//here you make the window visible!!!!

};
//hmm interetsing http://support.robotis.com/en/product/bioloid/beginnerkit/download/bioloid_beginner_app.htm
this is our simple idea:



react to sound - wants to turn to sound - we have a sound sensor

react to light - wants to turn to light - we have a light sensor

if it detects both it will signal need for our support how - by moving head - signs of panic

we might use attack duck example - simple movements


we activate proximity sensor to give it permision to make a random decision


decision( Tag)

Saturday 12 March 2011

Another good android example with rotation

Another good android example with rotation from http://bestsiteinthemultiverse.com/2008/11/android-graphics-example/

Executed in Eclispe

package com.android.drawdemo;

import android.app.Activity;
import android.os.Bundle;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;

public class DrawDemo extends Activity {
DemoView demoview;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
demoview = new DemoView(this);
setContentView(demoview);
}

private class DemoView extends View{
public DemoView(Context context){
super(context);
}

@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// custom drawing code here
// remember: y increases from top to bottom
// x increases from left to right
int x = 0;
int y = 0;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);

// make the entire canvas white
paint.setColor(Color.WHITE);
canvas.drawPaint(paint);
// another way to do this is to use:
// canvas.drawColor(Color.WHITE);

// draw a solid blue circle
paint.setColor(Color.BLUE);
canvas.drawCircle(20, 20, 15, paint);

// draw blue circle with antialiasing turned on
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
canvas.drawCircle(60, 20, 15, paint);

// compare the above circles once drawn
// the fist circle has a jagged perimeter
// the second circle has a smooth perimeter

// draw a solid green rectangle
paint.setAntiAlias(false);
paint.setColor(Color.GREEN);
canvas.drawRect(100, 5, 200, 30, paint);

// create and draw triangles
// use a Path object to store the 3 line segments
// use .offset to draw in many locations
// note: this triangle is not centered at 0,0
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.RED);
Path path = new Path();
path.moveTo(0, -10);
path.lineTo(5, 0);
path.lineTo(-5, 0);
path.close();
path.offset(10, 40);
canvas.drawPath(path, paint);
path.offset(50, 100);
canvas.drawPath(path, paint);
// offset is cumlative
// next draw displaces 50,100 from previous
path.offset(50, 100);
canvas.drawPath(path, paint);

// draw some text using STROKE style
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setColor(Color.MAGENTA);
paint.setTextSize(30);
canvas.drawText("Style.STROKE", 75, 75, paint);

// draw some text using FILL style
paint.setStyle(Paint.Style.FILL);
//turn antialiasing on
paint.setAntiAlias(true);
paint.setTextSize(30);
canvas.drawText("Style.FILL", 75, 110, paint);

// draw some rotated text
// get text width and height
// set desired drawing location
x = 75;
y = 185;
paint.setColor(Color.GRAY);
paint.setTextSize(25);
String str2rotate = "Rotated!";

// draw bounding rect before rotating text
Rect rect = new Rect();
paint.getTextBounds(str2rotate, 0, str2rotate.length(), rect);
canvas.translate(x, y);
paint.setStyle(Paint.Style.FILL);
// draw unrotated text
canvas.drawText("!Rotated", 0, 0, paint);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(rect, paint);
// undo the translate
canvas.translate(-x, -y);

// rotate the canvas on center of the text to draw
canvas.rotate(-45, x + rect.exactCenterX(),
y + rect.exactCenterY());
// draw the rotated text
paint.setStyle(Paint.Style.FILL);
canvas.drawText(str2rotate, x, y, paint);

//undo the rotate
canvas.restore();
canvas.drawText("After canvas.restore()", 50, 250, paint);

// draw a thick dashed line
DashPathEffect dashPath =
new DashPathEffect(new float[]{20,5}, 1);
paint.setPathEffect(dashPath);
paint.setStrokeWidth(8);
canvas.drawLine(0, 300 , 320, 300, paint);

}
}
}

Example of simple animation and touch events in Android via Eclipse







Example of simple animation and touch events in Android via Eclipse










package amdroid.TransformationalActivity.com;


import android.graphics.Canvas;
import android.view.SurfaceHolder;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.SurfaceView;


public class TransformationalActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
}
//////////

class Panel extends SurfaceView implements SurfaceHolder.Callback {

private Bitmap mBitmap;
private ViewThread mThread;

private int mX;
private int mY;
private int rad; //my variable for radius and movable x and ys

public Panel(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lotus);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}

public void doDraw(Canvas canvas) {

/*because of the thread this is looping like draw() in Processing*/

//simple way to make circle grow then go small again

rad++;
if(rad>300){
rad=3;

}




canvas.drawColor(Color.BLUE); //background colour

Paint paint = new Paint();


canvas.drawLine(33, 0, 33, 100, paint);
paint.setColor(Color.RED); paint.setStrokeWidth(10); canvas.drawLine(56, 0, 56, 100, paint);
paint.setColor(Color.GREEN); paint.setStrokeWidth(25);
for (int y = 30, alpha = 255; alpha > 2; alpha >>= 1, y += 10) {
paint.setAlpha(alpha);

//canvas.drawRect(cx, cy, radius, paint);
canvas.drawLine(0, y, 100, y, paint);

}



paint.setColor(Color.CYAN);
//canvas.drawCircle(mX-20, mY-20, rad, paint); //use my rad variable for radius
canvas.drawBitmap(mBitmap, mX, mY, null);

//strange solution to drawing a stable sized moving rect
paint.setColor(Color.RED);
Rect simplr = new Rect();
simplr.set(rad+120, 130, rad+156, 156);
canvas.drawRect(simplr, paint);
////////
paint.setColor(Color.CYAN);
canvas.drawCircle(rad*2, 220, 30, paint); //faster circle

paint.setColor(Color.RED);
canvas.drawRect(rad, 67, 68, 45, paint);
//////////////////
paint.setColor(Color.GREEN);
canvas.drawCircle(rad, 125, 45, paint); //move circle across screen
//////////////////////

paint.setColor(Color.MAGENTA);
canvas.drawCircle(125, rad, rad/2, paint); //move circle down screen

paint.setColor(Color.YELLOW);

canvas.drawRect(rad, rad, 45, 45, paint);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()) {
mThread.setRunning(false);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
mX = (int) event.getX() - mBitmap.getWidth() / 2;
mY = (int) event.getY() - mBitmap.getHeight() / 2;
return super.onTouchEvent(event);
}
}


/////////
class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;

public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}

public void setRunning(boolean run) {
mRun = run;
}

@Override
public void run() {
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}

simple android touch screen move image, draw lines

//again thanks to these excellant and clear tutorials:
http://www.droidnova.com/2d-tutorial-series-part-i,770.html

package amdroid.TransformationalActivity.com;


import android.graphics.Canvas;
import android.view.SurfaceHolder;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;


public class TransformationalActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
}
//////////

class Panel extends SurfaceView implements SurfaceHolder.Callback {

private Bitmap mBitmap;
private ViewThread mThread;

private int mX;
private int mY;

public Panel(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lotus);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}

public void doDraw(Canvas canvas) {
canvas.drawColor(Color.BLUE);
Paint paint = new Paint();
canvas.drawLine(33, 0, 33, 100, paint);
paint.setColor(Color.RED); paint.setStrokeWidth(10); canvas.drawLine(56, 0, 56, 100, paint);
paint.setColor(Color.GREEN); paint.setStrokeWidth(25);
for (int y = 30, alpha = 255; alpha > 2; alpha >>= 1, y += 10) {
paint.setAlpha(alpha);

canvas.drawLine(0, y, 100, y, paint);
}

canvas.drawBitmap(mBitmap, mX, mY, null);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()) {
mThread.setRunning(false);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
mX = (int) event.getX() - mBitmap.getWidth() / 2;
mY = (int) event.getY() - mBitmap.getHeight() / 2;
return super.onTouchEvent(event);
}
}


/////////
class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;

public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}

public void setRunning(boolean run) {
mRun = run;
}

@Override
public void run() {
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}

Simplest possible android graphic example




Simplest possible android graphic example - display an image (i.e lotus in the drawable-hdpi folder) make liness create the canvas, use paint, for loop for alpha channel etc
done in Eclipse. Second example below that draws image where user touches screen

//thanks to http://www.droidnova.com/2d-tutorial-series-part-ii,772.html
I've also been reading O'Reilly's Android Application Development, its got a little bit on Graphics and Matrix Transformations...
add this line for text - easy!!! paint.setColor(Color.RED); canvas.drawText("Android", 125, 30, paint);

package amdroid.TransformationalActivity.com; //my silly spelling. still works!




import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

public class TransformationalActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
}


class Panel extends View {

private Bitmap mBitmap;

public Panel(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lotus);
}

@Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLUE);

Paint paint = new Paint();
canvas.drawLine(33, 0, 33, 100, paint);
paint.setColor(Color.RED); paint.setStrokeWidth(10); canvas.drawLine(56, 0, 56, 100, paint);
paint.setColor(Color.GREEN); paint.setStrokeWidth(25);
for (int y = 30, alpha = 255; alpha > 2; alpha >>= 1, y += 10) {
paint.setAlpha(alpha);

canvas.drawLine(0, y, 100, y, paint);
}
//add this for text - EASY!! no need to import a font for this
paint.setColor(Color.RED); canvas.drawText("Hello Androids everywhere", 125, 30, paint);
canvas.drawBitmap(mBitmap, 100, 100, null);
}
}/////////////////

//////////////////////////////

Second example - draw image where user touches screen:

package amdroid.TransformationalActivity.com;


import android.graphics.Canvas;
import android.view.SurfaceHolder;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;


public class TransformationalActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
}
}
//////////

class Panel extends SurfaceView implements SurfaceHolder.Callback {

private Bitmap mBitmap;
private ViewThread mThread;

private int mX;
private int mY;

public Panel(Context context) {
super(context);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lotus);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}

public void doDraw(Canvas canvas) {
canvas.drawColor(Color.BLUE);
canvas.drawBitmap(mBitmap, mX, mY, null);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()) {
mThread.setRunning(false);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
mX = (int) event.getX() - mBitmap.getWidth() / 2;
mY = (int) event.getY() - mBitmap.getHeight() / 2;
return super.onTouchEvent(event);
}
}


/////////
class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;

public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}

public void setRunning(boolean run) {
mRun = run;
}

@Override
public void run() {
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
//////////

Friday 11 March 2011

Cartesian Theatre

Cartesian Theatre:

So far I've mapped the 'characters' to Alpha and Beta channels, so they move on and off the stage as those two of my brain frequencies fluctuate. Would be great to map it to a paper theatre with servos etc via the EEG unit...


EEG controlled theatre of the mind, different characters and stories will unfold depending on brain wave activity, could be in physical realm use Phys Comp, servos etc, or purely screen based...each viewer framed as the homunculous. See Dennett:
Cartesian materialism is the view that there is a crucial finish line or boundary somewhere in the brain, marking a place where the order of arrival equals the order of "presentation" in experience because what happens there is what you are conscious of. [...] Many theorists would insist that they have explicitly rejected such an obviously bad idea. But [...] the persuasive imagery of the Cartesian Theatre keeps coming back to haunt us - laypeople and scientists alike - even after its ghostly dualism has been denounced and exorcized. [p.107, original emphasis.]
also reference:

http://www.opticalsweets.com/

The problem of Buridan's Ass

The problem of Buridan's Ass (thanks to Msc Cognitive computing student, Alex, for this reference), which will be very useful in framing the indescisive robot we are building in Patrick's creative robotics class

"It refers to a hypothetical situation wherein an ass is placed precisely midway between a stack of hay and a pail of water. Since the paradox assumes the ass will always go to whichever is closer, it will die of both hunger and thirst since it cannot make any rational decision to choose one over the other.[1] The paradox is named after the 14th century French philosopher Jean Buridan, whose philosophy of moral determinism it satirises.

The paradox did not originate in Buridan's time; it dates to antiquity, being first found in Aristotle's De Caelo, where Aristotle mentions an example of a man who makes no move because he is as hungry as he is thirsty and is positioned exactly between food and drink.

Buridan nowhere discusses this specific problem, but its relevance is that he did advocate a moral determinism whereby, save for ignorance or impediment, a human faced by alternative courses of action must always choose the greater good. Buridan allowed that the will could delay the choice to more fully assess the possible outcomes of the choice. Later writers satirised this view in terms of an ass which, confronted by both food and water must necessarily die of both hunger and thirst while pondering a decision.

A common variant substitutes two identical piles of hay for both hay and water and advances that the ass, unable to choose between the two, dies of hunger alone.

Some proponents of hard determinism have granted the unpleasantness of the scenario, but have denied that it illustrates a true paradox, since one does not contradict oneself in suggesting that a man might die between two equally plausible routes of action. For example, Baruch Spinoza in his Ethics, suggests that a person who sees two options as truly equally compelling cannot be fully rational:

[I]t may be objected, if man does not act from free will, what will happen if the incentives to action are equally balanced, as in the case of Buridan's ass? [In reply,] I am quite ready to admit, that a man placed in the equilibrium described (namely, as perceiving nothing but hunger and thirst, a certain food and a certain drink, each equally distant from him) would die of hunger and thirst. If I am asked, whether such an one should not rather be considered an ass than a man; I answer, that I do not know, neither do I know how a man should be considered, who hangs himself, or how we should consider children, fools, madmen, &c." etc

Monday's symposium at the Scrolls Museum was special and quite challenging, I'm used to presenting my work to other academically institutionalised people, and take for granted a certain mutual language and theoretical framework. It's useful but quite appalling to realise the ideas and modes of language we take for granted might be alien and meaningless to other people. I was shocked that some people viewed our work as not connected to things that other people find important in their lives. I thought we were all dealing with quite common themes of belonging, loss, abandonment. It's unsettling to think that doesn't always come across, that people might think we are just trying to be clever or abstract etc...
The museum is unique, small and very well designed so you get a sense of proximity to the objects and a sense of individual lives. Evelyn and Ariel who run the museum extended great hospitality and kindness to us in hosting this event. It felt like something very special.

Sunday 6 March 2011

Doing simple Android apps

Doing simple Android apps, not with Processing as that can only work with Android 2.2 onwards, but with eclipse and the android sdk, and today playing with Appinventor - a bit like Lego Mindstorms,quick and v easy. Should be revising but this is too much fun. Thanks to my sister for the lovely G1 phone!

Friday 4 March 2011



http://www.doc.gold.ac.uk/~ma501ed/south/south_Download.html
Trying to organise all the extra bits of south code at the link above. The text photo program produced this image today - apologie sto Jenny Holzer but I'm not sure if I agree with the statement, or really understand it - if a metaphor is a transport perhaps its more useful than a description?