{ Fab Academy 2015 : Koichi Shiraishi }

{ Home } { Final project } { Class } { Fab Academy }


- Week 16: Mechanical Design/Machine Design -


Weekly Assignment

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.