{ Fab Academy 2015 : Koichi Shiraishi }
{ Home } { Final project } { Class } { Fab Academy }
- Week 16: Mechanical Design/Machine Design -
Weekly Assignment
-
make a machine, including the end effector
-
build the passive parts and operate it manually
-
document the group project and your individual contribution
-
automate your machine
-
document the group project and your individual contribution
Machine Design "Ketchuplotter"
I made a “Ketchuplotter.”
It was developed by myself. because a student attend the fab academy from kitakagaya is only me.


Idea
It is a omelette on a rice. Japanese call it as “Omurice”.
All japanese write message on it by Ketchup. It is necessary.
I planed making a automatic massage writer.
I hoped to developed automatic drawing generate system.
Users only take a photo, machine draw the picture.
However It too difficult to make for me.
Working mechanism
My machine arrangement is based on Nadya’s drawing bot. A original picture put on a PC what run a Processing code. The processing code detect outlines. After then It make a line drawn with a single stroke. It can switch drawing lines or not. Steppers are controlled by simple protocol as follows.

It use compressed air to vomit a Ketchup. The air send by motor pump. I modify a bottle fill a ketchup to receive compressed air.
My gestalt node was broken in the middle of development. therefore I developed final version machine by “Arduino.”
Make cardborad stages and the control board
I made housing by cardboard with “paraStage_2.gh”. After then I made a FTDI connection board based on tutorial.
Results
It is frail. Therefore I remake by acrylic board.
I fixed by bolts easy to modify each parts.
I arranged holes to engage a case side and a table.
Aluminum poles was threaded M6 for fixing.
I bended a side plate to improve the strength



Make the stages made out of acrylic boards



Printed data
Test single node.py
It moved.
Problem
It did not move smoothly. Because friction is too much at near the base.
Solution
I loosed several bolts below.


After then The gestalt bodes was broken.
Test XY light plotter
Developed Arduino code
The protocol is above role. I added a tact switch for initialize sequence.

It draw a 16x16 pixel picture.
At first, a light round whole steps on X axis. After that It down one step on Y axis.
I adjusted steps.
Results
It takes much time. I got to change drawing with a single stroke.

Test drawing
Developed Processing apps
I wrote a outline detection code. It was easy. However changing a single stroke is difficult. It was highly technical matters for me. I asked a friend to develop it.
Test
I put in a pen at table. I tried to run the machine.
results
It draw a mysterious signature at first challenge. I thought signals from Processing overflow the arduino memory. I added a delay at processing. The machine eventually became to write a characters.





Arduino code
#include
#include
#include "utility/Adafruit_PWMServoDriver.h"
// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Or, create it with a different I2C address (say for stacking)
// Adafruit_MotorShield AFMS = Adafruit_MotorShield(0x61);
// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor port #2 (M3 and M4)
//Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);
Adafruit_StepperMotor *stepperX = AFMS.getStepper(300, 2);
Adafruit_StepperMotor *stepperY = AFMS.getStepper(300, 1);
// Steps per revolution of the stepping motor.
const int stepsPerRevolution = 24;
const int limitPinX = 8;
const int limitPinY = 9;
const int actuatorKPin = 11;
const int actuatorMPin = 12;
const int workingPin = 13;
const int delayMsec = 15;
const float kSpeed = 0.6;
int isWorking = 0;
void setup()
{
AFMS.begin(); // create with the default frequency 1.6KHz
//AFMS.begin(1000); // OR with a different frequency, say 1KHz
// Set the speed of the stepper at 240 rpm??.
stepperX->setSpeed(240);
stepperY->setSpeed(240);
pinMode(workingPin, OUTPUT);
pinMode(limitPinY, INPUT_PULLUP);
pinMode(limitPinX, INPUT_PULLUP);
pinMode(actuatorKPin, OUTPUT);
// Initially, LEDs off.
digitalWrite(actuatorKPin, LOW);
// Serial start.
Serial.begin(9600);
//Serial.println("Now initializing!");
initialize();
}
void loop()
{
// pin 13 LED
if (isWorking == 1)
{
digitalWrite(13, HIGH);
}
else
{
digitalWrite(13, LOW);
}
// Check the serial connection
if (Serial.available() > 0){
int order = Serial.read();
switch (order) {
case 0:
// actuator OFF
//digitalWrite(actuatorKPin, LOW);
//delay(delayMsec);
move(0.0);
break;
case 1:
// actuator ON
//digitalWrite(actuatorKPin, HIGH);
//delay(delayMsec);
move(kSpeed);
break;
case 2:
// MOVE up left
stepperX->step(stepsPerRevolution, FORWARD, DOUBLE);
stepperY->step(stepsPerRevolution, FORWARD, DOUBLE);
delay(delayMsec);
break;
case 3:
//MOVE up
stepperY->step(stepsPerRevolution, FORWARD, DOUBLE);
delay(delayMsec);
break;
case 4:
//MOVE up right
stepperX->step(stepsPerRevolution, BACKWARD, DOUBLE);
stepperY->step(stepsPerRevolution, FORWARD, DOUBLE);
delay(delayMsec);
break;
case 5:
//MOVE right
stepperX->step(stepsPerRevolution, BACKWARD, DOUBLE);
delay(delayMsec);
break;
case 6:
//MOVE down right
stepperX->step(stepsPerRevolution, BACKWARD, DOUBLE);
stepperY->step(stepsPerRevolution, BACKWARD, DOUBLE);
delay(delayMsec);
break;
case 7:
//MOVE down
stepperY->step(stepsPerRevolution, BACKWARD, DOUBLE);
delay(delayMsec);
break;
case 8:
//MOVE down left
stepperX->step(stepsPerRevolution, FORWARD, DOUBLE);
stepperY->step(stepsPerRevolution, BACKWARD, DOUBLE);
delay(delayMsec);
break;
case 9:
//MOVE left
stepperX->step(stepsPerRevolution, FORWARD, DOUBLE);
delay(delayMsec);
break;
case 10:
//Idle
delay(delayMsec);
break;
case 88:
//Start
initialize();
//Serial.println("Start!");
delay(1000);
isWorking = 1;
break;
case 99:
//finish
initialize();
//Serial.println("Finish!");
delay(1000);
isWorking = 0;
break;
default:
//Serial.println("Out of renge");
break;
}
}
else if (isWorking == 1)
{
Serial.write(55);
delay(100);
}
}
void initialize()
{
digitalWrite(actuatorKPin,LOW);
digitalWrite(actuatorMPin,LOW);
//digitalWrite(workingPin,LOW);
// Initially, X position
int limmitX = digitalRead(limitPinX);
while (limmitX == HIGH){
stepperX->step(5, FORWARD, DOUBLE);
delay(delayMsec);
limmitX = digitalRead(limitPinX);
}
stepperX->step(100, BACKWARD, DOUBLE);
// Initially, Y position
int limmitY = digitalRead(limitPinY);
while (limmitY == HIGH){
stepperY->step(5, FORWARD, DOUBLE);
delay(delayMsec);
limmitY = digitalRead(limitPinY);
}
stepperY->step(100, BACKWARD, DOUBLE);
}
void move(float speed){
if (speed == 0.0)
{
analogWrite(actuatorKPin, 0);
return;
}
int speedValue = (int)(abs(speed) * 255);
if (speedValue > 255)
{
speedValue = 255;
}
analogWrite(actuatorKPin, speedValue);
}
Prosessing codes
ImageToLine20.pde
import gab.opencv.*;
//import java.util.Collections;
//import java.util.Comparator;
//import java.util.Dictionary;
//import java.util.Enumeration;
//import java.util.Hashtable;
import java.util.Map;
import java.util.HashSet;
// For serial communication.
import processing.serial.*;
// Serial connection
Serial serial;
String portName = "/dev/tty.usbmodem1411";
int fRate = 1000;
//String imagePath = "test.jpg";
//String imagePath = "IMG_7219.jpg";
String imagePath = "fab.png";
OpenCV cv;
PImage orgImage;
PImage edgImage;
int th1 = 128;
int th2 = 128;
// Special EdgePoint for jump.
EdgePoint jumpPoint = new EdgePoint(null);
EdgePoint originPoint = new EdgePoint(new PVector(0, 0));
PVector currentPoint = null;
EdgePoint initialPoint = null;
EdgePoint nextPoint = null;
// Actuator state
int actuatorState = 0;
Boolean isFirst = true;
int orderActuatorOff = 0;
int orderActuatorOn = 1;
int orderMoveUpLeft = 2;
int orderMoveUp = 3;
int orderMoveUpRight = 4;
int orderMoveRight = 5;
int orderMoveDownRight = 6;
int orderMoveDown = 7;
int orderMoveDownLeft = 8;
int orderMoveLeft = 9;
int orderShortStop = 10;
int orderIdle = 10;
int orderWorkStart = 88;
int orderWorkEnd = 99;
int orderCount = 0;
int orderCountLimit = 20;
Boolean isWorking = false;
void setup() {
// Setup serial port.
serial = new Serial(this, portName, 9600);
serial.clear();
HashMap ePoints;
// Load image.
orgImage = loadImage(imagePath);
size(orgImage.width, orgImage.height);
// Mirror image.
PImage mirrorImage = createImage(orgImage.width, orgImage.height, RGB);
// Loop to walk through every pixel
for (int x = 0; x < orgImage.width; x++) {
for (int y = 0; y < orgImage.height; y++) {
// Find edge points.
int idx = getIndex(x, y);
int idxMirror = getIndex(orgImage.width - 1 - x, y);
mirrorImage.pixels[idxMirror] = orgImage.pixels[idx];
}
}
orgImage = mirrorImage;
// Edge detection.
cv = new OpenCV(this, orgImage);
cv.findCannyEdges(th1,th2);
edgImage = cv.getSnapshot();
// Show the edge.
//image(edgImage, 0, 0);
background(0);
// List all the edge points.
ePoints = new HashMap();
color white = color(255, 255, 255);
// Loop to walk through every pixel
for (int x = 0; x < orgImage.width; x++) {
for (int y = 0; y < orgImage.height; y++) {
// Find edge points.
int idx = getIndex(x, y);
if (edgImage.pixels[idx] == white) {
// EdgePoint
EdgePoint ep = new EdgePoint(new PVector(x, y));
ePoints.put(idx, ep);
}
}
}
// Check surrounding 8 pixels.
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
for (int xa = -1; xa <= 1; xa++) {
for (int ya = -1; ya <= 1; ya++) {
if (!(xa == 0 && ya ==0)) {
int x = (int) ep.p.x + xa;
int y = (int) ep.p.y + ya;
int idx = getIndex(x, y);
if (edgImage.get(x, y) == white) {
EdgePoint nEp = ePoints.get(idx);
ep.neighbors.add(nEp);
}
}
}
}
}
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.neighbors.size() == 0) {
ep.from = jumpPoint;
ep.to = jumpPoint;
} else if (ep.neighbors.size() == 1) {
ep.from = jumpPoint;
} else if (ep.neighbors.size() == 2) {
// Connect a "route" with a "route".
for (int i = 0; i < ep.neighbors.size(); i++) {
EdgePoint p = ep.neighbors.get(i);
if (p.neighbors.size() == 2) {
if (ep.from != p && ep.to != p) {
ep.connectAtEither(p);
}
}
}
}
}
// Connect strategy.
while (true) {
int flgEndConnect = 0;
// Connect the end point
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() == 1 && ep.getFreeNeighbors().size() == 1) {
ep.connectAtEither(ep.getFreeNeighbors().get(0));
flgEndConnect++;
}
}
println("End Connect", flgEndConnect);
if (flgEndConnect > 0) {
continue;
}
// Connect the nearer point from two of the candidates.
int flgNearerConnect = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() == 1 && ep.getFreeNeighbors().size() == 2) {
EdgePoint np = ep.getFreeNeighbors().get(0);
EdgePoint fp = ep.getFreeNeighbors().get(1);
if (ep.l1Distance(np) > ep.l1Distance(fp)) {
np = ep.getFreeNeighbors().get(1);
fp = ep.getFreeNeighbors().get(0);
}
if (ep.l1Distance(np) == 1 && ep.l1Distance(fp) == 2) {
ep.connectAtEither(np);
flgNearerConnect++;
}
}
}
println("Nearer Connect", flgNearerConnect);
if (flgNearerConnect > 0) {
continue;
}
// Triangle connect
int flgTriangleConnect = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() == 2 && ep.getFreeNeighbors().size() == 0) {
Boolean flgDone = false;
for (int i = 0; i < ep.neighbors.size() - 1; i++) {
for (int j = i + 1; j < ep.neighbors.size(); j++) {
EdgePoint e1 = ep.neighbors.get(i);
EdgePoint e2 = ep.neighbors.get(j);
if (e1.to == e2) {
e1.to = ep;
ep.from = e1;
ep.to = e2;
e2.from = ep;
flgDone = true;
} else if (e1.from == e2) {
e2.to = ep;
ep.from = e2;
ep.to = e1;
e1.from = ep;
flgDone = true;
}
if (flgDone) {
break;
}
}
if (flgDone) {
break;
}
}
if (flgDone == false) {
ep.from = jumpPoint;
ep.to = jumpPoint;
}
flgTriangleConnect++;
}
}
println("Triangle Connect", flgTriangleConnect);
if (flgTriangleConnect > 0) {
continue;
}
// New End Point
int flgNewEnd = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() > 0 && ep.getFreeNeighbors().size() == 1) {
ep.connectAtEither(jumpPoint);
flgNewEnd++;
}
}
println("New End", flgNewEnd);
if (flgNewEnd > 0) {
continue;
}
// Isolate connect
int flgIsolateConnect = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() == 1 && ep.getFreeNeighbors().size() == 0) {
ep.connectAtEither(jumpPoint);
flgIsolateConnect++;
}
}
println("Isolate Connect", flgIsolateConnect);
if (flgIsolateConnect > 0) {
continue;
}
// Route Near Connect
int flgRouteNearConnect = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() > 0 && ep.getFreeNeighbors().size() == 2) {
for (int i = 0; i < ep.getFreeNeighbors().size(); i++) {
EdgePoint p = ep.getFreeNeighbors().get(i);
if (ep.l1Distance(p) == 1 && ep.to != p && ep.from != p) {
ep.connectAtEither(p);
flgRouteNearConnect++;
}
}
}
}
println("Route Near Connect", flgRouteNearConnect);
if (flgRouteNearConnect > 0) {
continue;
}
// Near Connect
int flgNearConnect = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() > 0 && ep.getFreeNeighbors().size() > 0) {
for (int i = 0; i < ep.getFreeNeighbors().size(); i++) {
EdgePoint p = ep.getFreeNeighbors().get(i);
if (ep.l1Distance(p) == 1 && ep.to != p && ep.from != p) {
ep.connectAtEither(p);
flgNearConnect++;
}
}
}
}
println("Near Connect", flgNearConnect);
if (flgNearConnect > 0) {
continue;
}
// Anyway
int flgAnywayConnect = 0;
for(Map.Entry e : ePoints.entrySet()) {
EdgePoint ep = (EdgePoint) e.getValue();
if (ep.freeCount() > 0 && ep.getFreeNeighbors().size() > 0) {
EdgePoint p = ep.getFreeNeighbors().get(0);
if (ep.to != p && ep.from != p) {
ep.connectAtEither(p);
flgAnywayConnect++;
}
}
}
println("Anyway Connect", flgAnywayConnect);
if (flgAnywayConnect > 0) {
continue;
}
break;
}
ArrayList groups = new ArrayList();
HashSet pointCheck = new HashSet();
for(Map.Entry e : ePoints.entrySet()) {
int k = (Integer) e.getKey();
EdgePoint ep = (EdgePoint) e.getValue();
if (pointCheck.contains(k) == false) {
RouteGroup rg = new RouteGroup();
ArrayList allPoints = rg.createRouteGroup(ep);
for (int i = 0; i < allPoints.size(); i++) {
int idx = getIndex((int) allPoints.get(i).p.x, (int) allPoints.get(i).p.y);
pointCheck.add(idx);
}
groups.add(rg);
}
}
println("# of groups: ", groups.size());
int[][] distTable = new int[groups.size()][];
for (int i = 0; i < groups.size() - 1; i++) {
distTable[i] = new int[groups.size()];
for (int j = i + 1; j < groups.size(); j++) {
int dist = groups.get(i).l1Distance(groups.get(j));
distTable[i][j] = dist;
}
}
int[] groupIds = new int[groups.size()];
for (int i = 0; i < groups.size(); i++) {
groupIds[i] = i;
}
// Grouping.
while (true) {
int minDist = Integer.MAX_VALUE;
int group1 = -1;
int group2 = -1;
for (int i = 0; i < groups.size() - 1; i++) {
for (int j = i + 1; j < groups.size(); j++) {
if (distTable[i][j] < minDist) {
minDist = distTable[i][j];
group1 = i;
group2 = j;
}
}
}
//println(groupIds);
//println("minDist: ", minDist, ", ids: ", group1, ", ", group2);
if (minDist == Integer.MAX_VALUE) {
break;
}
groups.get(group1).connectGroup(groups.get(group2));
// Update group id vector
int newGroupId = groupIds[group1];
int oldGroupId = groupIds[group2];
for (int i = 0; i < groups.size(); i++) {
if (groupIds[i] == oldGroupId) {
groupIds[i] = newGroupId;
}
}
// Update distance table
for (int i = 0; i < groups.size() - 1; i++) {
for (int j = i + 1; j < groups.size(); j++) {
if (groupIds[i] == groupIds[j]) {
distTable[i][j] = Integer.MAX_VALUE;
} else if (i == group1 || i == group2 || j == group1 || j == group2) {
int dist = groups.get(i).l1Distance(groups.get(j));
distTable[i][j] = dist;
}
}
}
}
EdgePoint[] jps = new EdgePoint[2];
int jpsc = 0;
for (int i = 0; i < groups.size(); i++) {
RouteGroup rg = groups.get(i);
for (int j = 0; j < 2; j++) {
if (rg.connectPointsHere[j].to == jumpPoint) {
rg.connectPointsHere[j].to = null;
jps[jpsc] = rg.connectPointsHere[j];
jpsc++;
}
if (rg.connectPointsHere[j].from == jumpPoint) {
rg.connectPointsHere[j].from = null;
jps[jpsc] = rg.connectPointsHere[j];
jpsc++;
}
}
}
//println(jps[0]);
//println(jps[1]);
jps[0].connectAtEither(jps[1]);
int minDistFromOrg = Integer.MAX_VALUE;
int minDistToNext = Integer.MAX_VALUE;
//EdgePoint initialPoint = null;
for (int i = 0; i < groups.size(); i++) {
RouteGroup rg = groups.get(i);
for (int j = 0; j < 2; j++) {
int distFromOrg = rg.connectPointsHere[j].l1Distance(originPoint);
if (minDistFromOrg > distFromOrg) {
initialPoint = rg.connectPointsHere[j];
minDistFromOrg = distFromOrg;
minDistToNext = initialPoint.l1Distance(initialPoint.to);
} else if (minDistFromOrg == distFromOrg) {
int distToNext = rg.connectPointsHere[j].l1Distance(rg.connectPointsHere[j].to);
if (minDistToNext > distToNext) {
initialPoint = rg.connectPointsHere[j];
minDistToNext = distToNext; //<>//
}
}
}
}
nextPoint = initialPoint;
currentPoint = new PVector(0, 0);
frameRate(fRate);
/* Start working.
isWorking = true;
// Start sending the orders.
sendOrder(orderWorkStart);
// First, it should wait for an ack from the machine.
noLoop();
*/
// Start working.
noLoop();
isWorking = true;
// Start sending the orders.
sendOrder(orderIdle);
delay(3000);
sendOrder(orderWorkStart);
}
void draw() {
// Actuator Off
if (actuatorState == 0) {
stroke(255, 0, 0);
} else {
stroke(0, 255, 255);
}
// Point
point(currentPoint.x, currentPoint.y);
if (nextPoint.p.x == currentPoint.x && nextPoint.p.y == currentPoint.y) {
// finished?
if (nextPoint == initialPoint) {
if (isFirst) {
isFirst = false;
} else {
sendOrder(orderWorkEnd);
isWorking = false;
noLoop();
return;
}
}
// distance to the next point.
float diffX = nextPoint.p.x - nextPoint.to.p.x;
float diffY = nextPoint.p.y - nextPoint.to.p.y;
int step = max(abs((int) diffX), abs((int) diffY));
if (step > 1) {
if (actuatorState == 0) {
// ON and wait a little and OFF.
// ON
sendOrder(orderActuatorOn);
actuatorState = 1;
stroke(0, 255, 255);
// Wait
sendOrder(orderShortStop);
point(currentPoint.x, currentPoint.y);
// OFF
sendOrder(orderActuatorOff);
actuatorState = 0;
} else {
// OFF
sendOrder(orderActuatorOff);
actuatorState = 0;
}
} else if (actuatorState == 0) {
// ON
sendOrder(orderActuatorOn);
actuatorState = 1;
}
nextPoint = nextPoint.to;
} else {
float diffX = nextPoint.p.x - currentPoint.x;
float diffY = nextPoint.p.y - currentPoint.y;
int dirX = 0;
if (diffX > 0) {
dirX = +1;
} else if (diffX < 0) {
dirX = -1;
}
int dirY = 0;
if (diffY > 0) {
dirY = +1;
} else if (diffY < 0) {
dirY = -1;
}
int step = max(abs((int) diffX), abs((int) diffY));
int goX = (int) (diffX / step + dirX * 0.5);
int goY = (int) (diffY / step + dirY * 0.5);
int order = 0;
if (goX == -1 && goY == -1)
order = orderMoveUpLeft;
if (goX == 0 && goY == -1)
order = orderMoveUp;
if (goX == 1 && goY == -1)
order = orderMoveUpRight;
if (goX == 1 && goY == 0)
order = orderMoveRight;
if (goX == 1 && goY == 1)
order = orderMoveDownRight;
if (goX == 0 && goY == 1)
order = orderMoveDown;
if (goX == -1 && goY == 1)
order = orderMoveDownLeft;
if (goX == -1 && goY == 0)
order = orderMoveLeft;
// Move
sendOrder(order);
currentPoint.x += (float) goX;
currentPoint.y += (float) goY;
}
// order count
orderCount++;
if (orderCount >= orderCountLimit) {
noLoop();
}
}
void serialEvent(Serial p) {
// Receive something.
int val = p.read();
p.clear();
println("Receive: ", val);
if (isWorking && val == 55) {
orderCount = 0;
loop();
}
}
int getIndex(int x, int y)
{
return x + (y * orgImage.width);
}
void sendOrder(int code)
{
println("Send: ", code);
serial.write(code);
}
EdgePoint.pde
// Class for an edge point.
class EdgePoint {
PVector p;
EdgePoint from;
EdgePoint to;
ArrayList neighbors;
EdgePoint(PVector p) {
this.p = p;
this.from = null;
this.to = null;
neighbors = new ArrayList();
}
int freeCount() {
int c = 2;
if (this.from != null) {
c--;
}
if (this.to != null) {
c--;
}
return c;
}
void changeDirection() {
EdgePoint oldFrom = this.from;
EdgePoint oldTo = this.to;
if (oldFrom != null && oldFrom != jumpPoint) {
oldFrom.to = null;
oldFrom.changeDirection();
oldFrom.from = this;
}
if (oldTo != null && oldTo != jumpPoint) {
oldTo.from = null;
oldTo.changeDirection();
oldTo.to = this;
}
this.from = oldTo;
this.to = oldFrom;
}
Boolean connectAtTo(EdgePoint ep) {
if (this.to != null) {
return false;
}
// Here, this.to is null.
if (ep == jumpPoint) {
this.to = ep;
return true;
}
if (ep.from != null) {
if (ep.to != null) {
return false;
} else {
ep.changeDirection();
}
}
this.to = ep;
ep.from = this;
return true;
}
Boolean connectAtFrom(EdgePoint ep) {
if (this.from != null) {
return false;
}
if (ep == jumpPoint) {
this.from = ep;
return true;
}
if (ep.to != null) {
if (ep.from != null) {
return false;
} else {
ep.changeDirection();
}
}
this.from = ep;
ep.to = this;
return true;
}
Boolean connectAtEither(EdgePoint ep) {
if (ep == null) {
return false;
}
if (this.from == null) {
return this.connectAtFrom(ep);
} else if (this.to == null) {
return this.connectAtTo(ep);
} else {
return false;
}
}
int l1Distance(EdgePoint ep) {
int d = 0;
d += (int) (abs(this.p.x - ep.p.x) + (abs(this.p.y - ep.p.y)));
return d;
}
ArrayList getFreeNeighbors() {
ArrayList result = new ArrayList();
for (int i = 0; i < this.neighbors.size(); i++) {
EdgePoint ep = this.neighbors.get(i);
if (ep.freeCount() != 0) {
result.add(ep);
}
}
return result;
}
String toString() {
String r = this.p.toString();
if (this.from != null) {
if (this.from == jumpPoint) {
r = r + "f" + "[jumpPoint]";
} else {
r = r + "f" + this.from.p.toString();
}
}
if (this.to != null) {
if (this.to == jumpPoint) {
r = r + "t" + "[jumpPoint]";
} else {
r = r + "t" + this.to.p.toString();
}
}
r = r + "\n";
return r;
}
}
RouteGroup.pde
class RouteGroup {
EdgePoint[] connectPointsHere;
Boolean[] cpIsUsed;
ArrayList members;
RouteGroup() {
this.connectPointsHere = new EdgePoint[2];
this.cpIsUsed = new Boolean[2];
this.cpIsUsed[0] = false;
this.cpIsUsed[1] = false;
this.members = new ArrayList();
}
ArrayList createRouteGroup(EdgePoint ep) {
EdgePoint startCand = ep;
do {
if (startCand.from == jumpPoint) {
this.connectPointsHere[0] = startCand;
break;
} else {
startCand = startCand.from;
}
} while (startCand != ep);
EdgePoint endCand = startCand;
ArrayList result = new ArrayList();
do {
result.add(endCand);
if (endCand.to == jumpPoint) {
this.connectPointsHere[1] = endCand;
break;
} else {
endCand = endCand.to;
}
} while (endCand != startCand);
this.members = result;
return result;
}
ArrayList getConnectCandidate() {
ArrayList result = new ArrayList();
if (this.connectPointsHere[0] == null) {
result.addAll(this.members);
} else {
if (this.cpIsUsed[0] == false) {
result.add(this.connectPointsHere[0]);
}
if (this.cpIsUsed[1] == false) {
result.add(this.connectPointsHere[1]);
}
}
return result;
}
int l1Distance(RouteGroup routeGroup)
{
ArrayList myCands = this.getConnectCandidate();
ArrayList hisCands = routeGroup.getConnectCandidate();
int nearest = Integer.MAX_VALUE;
for (int i = 0; i < myCands.size(); i++) {
for (int j = 0; j < hisCands.size(); j++) {
EdgePoint e1 = myCands.get(i);
EdgePoint e2 = hisCands.get(j);
int d = e1.l1Distance(e2);
if (nearest > d) {
nearest = d;
}
}
}
return nearest;
}
void connectGroup(RouteGroup routeGroup) {
ArrayList candsHere = this.getConnectCandidate();
ArrayList candsThere = routeGroup.getConnectCandidate();
EdgePoint epHere = null;
EdgePoint epThere = null;
int dist = Integer.MAX_VALUE;
for (int i = 0; i < candsHere.size(); i++) {
for (int j = 0; j < candsThere.size(); j++) {
epHere = candsHere.get(i);
epThere = candsThere.get(j);
int d = epHere.l1Distance(epThere);
if (dist > d) {
dist = d;
}
}
}
this.createConnectPoints(epHere);
routeGroup.createConnectPoints(epThere);
EdgePoint targetHere = null;
EdgePoint targetThere = null;
for (int i = 0; i < 2; i++) {
if (this.cpIsUsed[i] == false && this.connectPointsHere[i].p == epHere.p) {
targetHere = this.connectPointsHere[i];
this.cpIsUsed[i] = true;
break;
}
}
for (int i = 0; i < 2; i++) {
if (routeGroup.cpIsUsed[i] == false && routeGroup.connectPointsHere[i].p == epThere.p) {
targetThere = routeGroup.connectPointsHere[i];
routeGroup.cpIsUsed[i] = true;
break;
}
}
// Connect
if (targetHere.from == jumpPoint) {
targetHere.from = null;
} else if (targetHere.to == jumpPoint) {
targetHere.to = null;
}
if (targetThere.from == jumpPoint) {
targetThere.from = null;
} else if (targetThere.to == jumpPoint) {
targetThere.to = null;
}
targetHere.connectAtEither(targetThere);
}
void createConnectPoints(EdgePoint ep) {
if (this.connectPointsHere[0] == null) {
this.connectPointsHere[0] = new EdgePoint(ep.p);
this.connectPointsHere[0].from = jumpPoint;
this.connectPointsHere[0].to = ep.to;
ep.to.from = this.connectPointsHere[0];
this.connectPointsHere[1] = new EdgePoint(ep.p);
this.connectPointsHere[1].from = ep.from;
this.connectPointsHere[1].to = jumpPoint;
ep.from.to = this.connectPointsHere[1];
}
}
}
Prepare the omelette for final demonstration
I took away three “Omrice” from coffee shop to use presentation.

