/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.core.ui.model.utils.algo;
import java.awt.Rectangle;
import java.io.Serializable;
import java.util.List;
/**
* The Class Contour.
*
* @author Nicolas Roduit
*/
public class Contour implements Serializable {
private static final long serialVersionUID = 9056317484468365515L;
public static final int[] DIRX = { 1, 1, 0, -1, -1, -1, 0, 1 };
public static final int[] DIRY = { 0, -1, -1, -1, 0, 1, 1, 1 };
private int coordx;
private int coordy;
private byte[] codeFreeman;
private int area;
private List<Double> parameters;
public Contour(int coordx, int coordy, byte[] codeFreeman, int area, List<Double> parameters) {
this.coordx = coordx;
this.coordy = coordy;
this.codeFreeman = codeFreeman;
this.area = area;
this.parameters = parameters;
}
public byte[] getCodeFreeman() {
return codeFreeman;
}
public int getCoordx() {
return coordx;
}
public int getCoordy() {
return coordy;
}
public int getArea() {
return area;
}
public List<Double> getParameters() {
return parameters;
}
public Rectangle getBounds() {
if (codeFreeman == null) {
return null;
}
int[] dirX = DIRX;
int[] dirY = DIRY;
int boundsMinX = Integer.MAX_VALUE;
int boundsMinY = Integer.MAX_VALUE;
int boundsMaxX = Integer.MIN_VALUE;
int boundsMaxY = Integer.MIN_VALUE;
int x = coordx;
int y = coordy;
for (int i = 0; i < codeFreeman.length; i++) {
int index = codeFreeman[i];
x += dirX[index];
boundsMinX = Math.min(boundsMinX, x);
boundsMaxX = Math.max(boundsMaxX, x);
y += dirY[index];
boundsMinY = Math.min(boundsMinY, y);
boundsMaxY = Math.max(boundsMaxY, y);
}
return new Rectangle(boundsMinX, boundsMinY, boundsMaxX - boundsMinX + 1, boundsMaxY - boundsMinY + 1);
}
private static int changeDir(int lastDir, int index) {
int change = index - lastDir;
// resolution du passage du 7 au 0
if (change < -4) {
change = (change + 8) % 8;
} else if (change > 4) {
change = (change - 8) % 8;
}
return change;
}
private void writeTurnPosition(byte[][] matrix, int missX, int missY, int missPos, int offset, byte[] border) {
int[] dirX = DIRX;
int[] dirY = DIRY;
if (offset > 2) {
// si le pixel de la première couche ne peut pas être écrit, on abandonne pour les autres couches
int extY = 2;
lineY: while (extY < offset + 1) {
int extX = 2;
boolean canWriteX = true;
while (extX < offset - (extY - 2)) {
int x = missX + dirX[missPos] * (extX - 2);
int y = missY + dirY[missPos] * (extY - 2);
if (matrix[y][x] != border[extX + extY]) {
if (extX == 2) {
break lineY;
} else {
canWriteX = false;
break;
}
}
extX++;
}
if (canWriteX) {
int x = missX + dirX[missPos] * (extX - 2);
int y = missY + dirY[missPos] * (extY - 2);
if (matrix[y][x] == 0) {
matrix[y][x] = border[offset + 2];
}
}
extY++;
}
} else if (offset == 2) {
if (matrix[missY][missX] == 0) {
matrix[missY][missX] = border[4];
}
}
}
private static void writeNewPosition(byte[][] matrix, int index, int nextDir, int newPos1, int offset, int xdir, int ydir,
byte[] border) {
int[] dirX = DIRX;
int[] dirY = DIRY;
// si un pixel avant la couche à écrire ne peut pas être écrit, on abandonne l'écriture
int ext = 1;
boolean canWrite = true;
while (ext < offset) {
int x = xdir + dirX[newPos1] * ext;
int y = ydir + dirY[newPos1] * ext;
if (matrix[y][x] != border[ext + 2]) {
canWrite = false;
break;
}
ext++;
}
if (canWrite) {
int x = xdir + dirX[newPos1] * offset;
int y = ydir + dirY[newPos1] * offset;
if (matrix[y][x] == 0 && canWriteInDir(index, nextDir, newPos1)) {
matrix[y][x] = border[offset + 2];
}
}
}
private static void writeNewPositionWithNoRestriction(byte[][] matrix, int newPos1, int offset, int xdir, int ydir,
byte[] border) {
int[] dirX = DIRX;
int[] dirY = DIRY;
// si un pixel avant la couche à écrire ne peut pas être écrit, on abandonne l'écriture
int ext = 1;
boolean canWrite = true;
while (ext < offset) {
int x = xdir + dirX[newPos1] * ext;
int y = ydir + dirY[newPos1] * ext;
if (matrix[y][x] != border[ext + 2]) {
canWrite = false;
break;
}
ext++;
}
if (canWrite) {
int x = xdir + dirX[newPos1] * offset;
int y = ydir + dirY[newPos1] * offset;
if (matrix[y][x] == 0) {
matrix[y][x] = border[offset + 2];
}
}
}
private static boolean canWriteInDir(int index, int nextDir, int newPos) {
return (newPos - (index - 4) + 8) % 8 < (nextDir - (index - 4) + 8) % 8;
}
private void writeLayer(byte[][] matrix, int xdir, int ydir, byte[] border, int layer) {
int[] dirX = DIRX;
int[] dirY = DIRY;
// 2 le bias pour compter les position des couches de l'intérieur : 1 vers l'extérieur : n
int offset = layer - 2;
matrix[ydir - offset][xdir] = border[layer];
// matrix[ydir - offset+1][xdir - offset] = border[layer];
matrix[ydir][xdir] = border[1];
int lastDir = codeFreeman[codeFreeman.length - 1]; // dernière direction
for (int i = 0; i < codeFreeman.length; i++) {
int index = codeFreeman[i];
int nextDir;
if (i == codeFreeman.length - 1) {
nextDir = codeFreeman[0];
} else {
nextDir = codeFreeman[i + 1];
}
xdir += dirX[index];
ydir += dirY[index];
matrix[ydir][xdir] = border[1];
int change = changeDir(lastDir, index);
if (change == -2) {
int newPos1 = (index - 1 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
} else if (change == -1 || change == 0) {
if (index % 2 == 0) {
int newPos1 = (index - 2 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
} else {
int newPos1 = (index - 1 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
int newPos2 = (index - 3 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos2, offset, xdir, ydir, border);
}
} else if (change == 1) {
if (index % 2 == 0) {
int newPos1 = (index - 2 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
} else {
int newPos1 = (index - 1 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
int newPos2 = (index - 3 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos2, offset, xdir, ydir, border);
if (changeDir(index, nextDir) >= 0) {
// position pour allonger la courbure des couches extérieures
int newPos3 = (index - 2 + 8) % 8;
int missX = xdir + dirX[newPos3];
int missY = ydir + dirY[newPos3];
writeTurnPosition(matrix, missX, missY, newPos3, offset, border);
}
}
} else if (change == 2) {
if (index % 2 == 0) {
int newPos1 = (index - 2 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
// revient en arrière (direction opposée)
int newPos2 = (index - 4 + 8) % 8;
int missX = xdir + dirX[newPos2];
int missY = ydir + dirY[newPos2];
writeNewPosition(matrix, index, nextDir, newPos1, offset, missX, missY, border);
// position pour allonger la courbure des couches extérieures
int newPos3 = (lastDir - 1 + 8) % 8;
int missX2 = missX + dirX[newPos3];
int missY2 = missY + dirY[newPos3];
writeTurnPosition(matrix, missX2, missY2, newPos3, offset, border);
} else {
int newPos1 = (index - 1 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
int newPos2 = (index - 3 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos2, offset, xdir, ydir, border);
// position pour allonger la courbure des couches extérieures
int newPos3 = (index - 2 + 8) % 8;
int missX = xdir + dirX[newPos3];
int missY = ydir + dirY[newPos3];
writeTurnPosition(matrix, missX, missY, newPos3, offset, border);
}
} else if (change == 3 || change == 4 || change == -4) {
if (index % 2 == 0) {
int newPos1 = (index - 2 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
// revient en arrière (direction opposée)
int newPos2 = (index - 4 + 8) % 8;
int missX = xdir + dirX[newPos2];
int missY = ydir + dirY[newPos2];
writeNewPositionWithNoRestriction(matrix, newPos1, offset, missX, missY, border);
if (change == 3) {
// position pour allonger la courbure des couches extérieures
int missX2 = missX + dirX[lastDir];
int missY2 = missY + dirY[lastDir];
writeTurnPosition(matrix, missX2, missY2, lastDir, offset, border);
} else { // change 4 ou -4
writeNewPositionWithNoRestriction(matrix, newPos2, offset, missX, missY, border);
// postion précédent n'arrive pas à écrire à cause de la restriction (qui ne marche pas parce
// que
// le calcul dépassse la moitié d'un cadran
int newPos6 = (newPos2 - 2 + 8) % 8;
writeNewPositionWithNoRestriction(matrix, newPos6, offset, missX, missY, border);
// position pour allonger la courbure des couches extérieures
int newPos4 = (newPos2 - 1 + 8) % 8;
int missX2 = missX + dirX[newPos4];
int missY2 = missY + dirY[newPos4];
writeTurnPosition(matrix, missX2, missY2, newPos4, offset, border);
int newPos5 = (newPos2 + 1 + 8) % 8;
missX2 = missX + dirX[newPos5];
missY2 = missY + dirY[newPos5];
writeTurnPosition(matrix, missX2, missY2, newPos5, offset, border);
}
} else { // change 3 et 4 identique
int newPos1 = (index - 1 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos1, offset, xdir, ydir, border);
// cette position est forcément plus au sommet de l'angle
int newPos2 = (index - 3 + 8) % 8;
writeNewPosition(matrix, index, nextDir, newPos2, offset, xdir, ydir, border);
// 1 er position pour allonger la courbure des couches extérieures
int newPos3 = (index - 2 + 8) % 8;
int missX = xdir + dirX[newPos3];
int missY = ydir + dirY[newPos3];
writeTurnPosition(matrix, missX, missY, newPos3, offset, border);
// revient en arrière (direction opposée)
int newPos4 = (index - 4 + 8) % 8;
int missX2 = xdir + dirX[newPos4];
int missY2 = ydir + dirY[newPos4];
if (change == 4 || change == -4) {
// postion précédent n'arrive pas à écrire à cause de la restriction (qui ne marche pas parce
// que
// le calcul dépassse la moitié d'un cadran
int newPos6 = (newPos4 - 1 + 8) % 8;
writeNewPositionWithNoRestriction(matrix, newPos6, offset, missX2, missY2, border);
newPos6 = (newPos4 - 3 + 8) % 8;
writeNewPositionWithNoRestriction(matrix, newPos6, offset, missX2, missY2, border);
}
int newPos5 = (newPos4 + 1 + 8) % 8;
writeNewPositionWithNoRestriction(matrix, newPos5, offset, missX2, missY2, border);
int missX3 = missX2 + dirX[newPos4];
int missY3 = missY2 + dirY[newPos4];
writeTurnPosition(matrix, missX3, missY3, newPos4, offset, border);
if (change == 4 || change == -4) {
int newPos7 = (newPos4 - 2 + 8) % 8;
missX3 = missX2 + dirX[newPos7];
missY3 = missY2 + dirY[newPos7];
writeTurnPosition(matrix, missX3, missY3, newPos7, offset, border);
}
}
}
lastDir = index;
}
}
public byte[][] dilate(int layer) {
/* 3 2 1 */
/* 4 0 */
/* 5 6 7 */
/* Table given index offset for each of the 8 directions. */
if (layer < 1) {
return null;
}
Rectangle bound = getBounds();
// +2 pour la dilation de la forme d'un pixel
byte[][] matrix = new byte[bound.height + 2 * layer][bound.width + 2 * layer];
byte[] border = new byte[layer + 3];
for (int i = 0; i < border.length; i++) {
border[i] = (byte) i;
}
int xdir = coordx - bound.x + layer;
int ydir = layer;
for (int k = 3; k < layer + 3; k++) {
writeLayer(matrix, xdir, ydir, border, k);
}
return matrix;
}
public static Contour getContourFromChainListCoord(List<ChainPoint> chain) {
byte[] val = new byte[chain.size()];
ChainPoint last = chain.get(0);
for (int j = 1; j < chain.size(); j++) {
ChainPoint pt = chain.get(j);
val[j - 1] = getDirFromTwoCoord(last, pt);
last = pt;
}
ChainPoint first = chain.get(0);
val[val.length - 1] = getDirFromTwoCoord(chain.get(chain.size() - 1), first);
return new Contour(first.x, first.y, val, 50, null);
}
public static byte getDirFromTwoCoord(ChainPoint last, ChainPoint next) {
int x = next.x - last.x;
int y = next.y - last.y;
if (x == 1) {
if (y == 1) {
return (byte) 7;
} else if (y == -1) {
return (byte) 1;
} else {
return (byte) 0;
}
} else if (x == -1) {
if (y == 1) {
return (byte) 5;
} else if (y == -1) {
return (byte) 3;
} else {
return (byte) 4;
}
} else {
if (y == 1) {
return (byte) 6;
} else {
return (byte) 2;
}
}
}
}