Commit 960d054f authored by Andrey Filippov's avatar Andrey Filippov

tested conflicts with variable costs

parent 364204c4
...@@ -248,5 +248,42 @@ public class Conflict{ ...@@ -248,5 +248,42 @@ public class Conflict{
return num_dual; return num_dual;
} }
/**
* Get directions from center and layers of the 2 non-center tiles involved in each triangular conflict
* Full triangle is center:nl1 -> dir1 -> dir2 -> center:nl2
* @return array of 2-tuples:{dir1, dir2}
*/
public int [][] getInvolvedTiles(){
int [][] involved = new int [getNumConflicts()][2];
int nt = 0;
for (int i = 0; i < 4; i++){
if (start_dirs[i + 0]) {
involved[nt][0] = i * 2;
involved[nt++][1] = ((i * 2) + 2) % 8;
}
if (start_dirs[i + 4]) {
involved[nt][0] = ((i * 2) + 2) % 8;
involved[nt++][1] = i * 2;
}
if (start_dirs[i + 8]) {
involved[nt][0] = i * 2;
involved[nt++][1] = ((i * 2) + 1) % 8;
}
if (start_dirs[i + 12]) {
involved[nt][0] = ((i * 2) + 1) % 8;
involved[nt++][1] = i * 2;
}
if (start_dirs[i + 16]) {
involved[nt][0] = i * 2;
involved[nt++][1] = ((i * 2) + 7) % 8;
}
if (start_dirs[i + 20]) {
involved[nt][0] = ((i * 2) + 7) % 8;
involved[nt++][1] = i * 2;
}
}
return involved;
}
} }
...@@ -198,6 +198,34 @@ public class Conflicts { ...@@ -198,6 +198,34 @@ public class Conflicts {
if (better? (num_ortho_dual < 0) : (num_ortho_dual > 0)) num++; if (better? (num_ortho_dual < 0) : (num_ortho_dual > 0)) num++;
} }
if (use_ood) {
for (int i = 0; i < num_ortho_ortho_diag.length; i++){
if (better?(num_ortho_ortho_diag[i] < 0):(num_ortho_ortho_diag[i] > 0)) num++;
}
}
return num;
}
public int sumConflicts(
boolean use_all,
boolean use_odo,
boolean use_ood)
{
int num = 0;
if (use_all) {
for (int i = 0; i < num_all_conflicts.length; i++){
num += num_all_conflicts[i];
}
}
if (use_odo) {
for (int i = 0; i < num_ortho_diag_ortho.length; i++){
num +=num_ortho_diag_ortho[i];
}
}
if (use_ood) {
for (int i = 0; i < num_ortho_ortho_diag.length; i++){
num +=num_ortho_ortho_diag[i];
}
}
return num; return num;
} }
...@@ -286,11 +314,157 @@ public class Conflicts { ...@@ -286,11 +314,157 @@ public class Conflicts {
return conflicts; return conflicts;
} }
/**
* Calculate cost of all conflicts around supertile nsTile0 by adding "star" weight of each tile involved.
* Tile weight can be either from the planes[][] array or from the replacement values in replacement_val_weights
* (when the tiles configuration is not yet committed)
* @param nsTile0 supertile index to process
* @param scaleStartEnd for each conflict triangle add start and end tiles (at the center nsTile0) scaled by this value
* @param conflicts array of calculated conflicts (each is {start_layer, end_layer, direction/type bitmask} or null,
* in that case it will be calculated
* @param replacement_tiles a map of supertile indices to replacement indices (for replacement_neibs and
* replacement_val_weights) or null. If not null, and entry for the supertile full index exists,
* replacement_neibs and replacement_val_weights will be used, otherwise planes[][] data.
* replacement_tiles may be null, in that case planes[][] data will be used unconditionally.
* @param replacement_neibs array of neighbors to use instead of the planes data. First index is an index
* of the replacement supertile (values in the replacement_tiles map), second - layer number and
* the 3-rd one - direction index for 8 connections: N, NE, E...NW. Value is the destination layer
* number. Can be null if not used (when replacement_tiles is null)
* @param replacement_val_weights similar array for tile value/weight data (per-tile index, per layer).
* The innermost data is a tuple {value, weight}
* @param tnSurface TileNeibs instance to navigate through the 2-d array encoded in linescan order
* @return sum ao the weights of all conflicts fro this tile
*/
public double getConflictsCost(
int nsTile0,
double scaleStartEnd, // include start and and layer tiles in the center in overall cost for each triangle (1.0)
int [][] conflicts, //
HashMap<Integer,Integer> replacement_tiles, // null is OK
int [][][] replacement_neibs, // null OK if replacement_tiles == null
double [][][] replacement_val_weights,
TileSurface.TileNeibs tnSurface)
{
TilePlanes.PlaneData [][] planes = st.getPlanes();
// generate conflicts if not provided
if (conflicts == null) {
conflicts = detectTriangularTileConflicts(
nsTile0,
replacement_tiles, //
replacement_neibs,
tnSurface);
}
double cost = 0.0;
if (conflicts != null){
Integer isTile = (replacement_tiles != null) ? replacement_tiles.get(nsTile0) : null;
for (int nConfl = 0; nConfl < conflicts.length; nConfl++){
Conflict conflict = new Conflict(nsTile0, conflicts[nConfl]);
int start_layer = conflict.getStartLayer();
int end_layer = conflict.getEndLayer();
int [] neibs1 = ((isTile == null) ?
planes[nsTile0][start_layer].getNeibBest() :
replacement_neibs[isTile][start_layer]);
int [] neibs2 = ((isTile == null) ?
planes[nsTile0][end_layer].getNeibBest() :
replacement_neibs[isTile][end_layer]);
int [][] involved = conflict.getInvolvedTiles();
for (int nTri = 0; nTri < involved.length; nTri++){
int [] nsTiles = {
tnSurface.getNeibIndex(nsTile0, involved[nTri][0]),
tnSurface.getNeibIndex(nsTile0, involved[nTri][1])};
int [] layers = {neibs1[involved[nTri][0]], neibs2[involved[nTri][1]]};
for (int it = 0; it < nsTiles.length; it++){
Integer isTile1 = (replacement_tiles != null) ? replacement_tiles.get(nsTiles[it]) : null;
if (isTile1 != null){
cost += replacement_val_weights[isTile1][layers[it]][1];
} else {
cost += planes[nsTiles[it]][layers[it]].getStarValueWeight()[1];
}
}
}
if (scaleStartEnd != 0.0) {
if (isTile != null){
cost += scaleStartEnd * (replacement_val_weights[isTile][start_layer][1]+
replacement_val_weights[isTile][end_layer][1]);
} else {
cost += scaleStartEnd * (planes[nsTile0][start_layer].getStarValueWeight()[1]+
planes[nsTile0][end_layer].getStarValueWeight()[1]);
}
}
}
}
return cost;
}
public double getConflictsCost(
int nsTile0,
double scaleStartEnd, // include start and and layer tiles in the center in overall cost for each triangle (1.0)
int [][] conflicts, //
HashMap<Integer,Integer> replacement_tiles, // null is OK
int [][][] replacement_neibs, // null OK if replacement_tiles == null
ConnectionCosts connectionCosts,
TileSurface.TileNeibs tnSurface)
{
TilePlanes.PlaneData [][] planes = st.getPlanes();
// generate conflicts if not provided
if (conflicts == null) {
conflicts = detectTriangularTileConflicts(
nsTile0,
replacement_tiles, //
replacement_neibs,
tnSurface);
}
double cost = 0.0;
if (conflicts != null){
Integer isTile = (replacement_tiles != null) ? replacement_tiles.get(nsTile0) : null;
for (int nConfl = 0; nConfl < conflicts.length; nConfl++){
Conflict conflict = new Conflict(nsTile0, conflicts[nConfl]);
int start_layer = conflict.getStartLayer();
int end_layer = conflict.getEndLayer();
int [] neibs1 = ((isTile == null) ?
planes[nsTile0][start_layer].getNeibBest() :
replacement_neibs[isTile][start_layer]);
int [] neibs2 = ((isTile == null) ?
planes[nsTile0][end_layer].getNeibBest() :
replacement_neibs[isTile][end_layer]);
int [][] involved = conflict.getInvolvedTiles();
for (int nTri = 0; nTri < involved.length; nTri++){
int [] nsTiles = {
tnSurface.getNeibIndex(nsTile0, involved[nTri][0]),
tnSurface.getNeibIndex(nsTile0, involved[nTri][1])};
int [] layers = {neibs1[involved[nTri][0]], neibs2[involved[nTri][1]]};
for (int it = 0; it < nsTiles.length; it++){
cost += connectionCosts. getValWeightLast(
nsTiles[it], // int nsTile,
layers[it], // int nl,
false)[1]; // boolean initialValue)
}
}
if (scaleStartEnd != 0.0) {
cost += scaleStartEnd * connectionCosts. getValWeightLast(
nsTile0, // int nsTile,
start_layer, // int nl,
false)[1]; // boolean initialValue)
cost += scaleStartEnd * connectionCosts. getValWeightLast(
nsTile0, // int nsTile,
end_layer, // int nl,
false)[1]; // boolean initialValue)
}
}
}
return cost;
}
public int [][] detectTriangularTileConflicts( public int [][] detectTriangularTileConflicts(
int nsTile0, int nsTile0,
HashMap<Integer,Integer> replacement_tiles, // HashMap<Integer,Integer> replacement_tiles, // null is OK - will use only planes data
int [][][] replacement_neibs, int [][][] replacement_neibs, // null OK if replacement_tiles == null
TileSurface.TileNeibs tnSurface) TileSurface.TileNeibs tnSurface)
{ {
TilePlanes.PlaneData [][] planes = st.getPlanes(); TilePlanes.PlaneData [][] planes = st.getPlanes();
......
...@@ -36,10 +36,12 @@ public class ConnectionCosts { ...@@ -36,10 +36,12 @@ public class ConnectionCosts {
int [][][] neibs_init; int [][][] neibs_init;
int [] mod_tiles; int [] mod_tiles;
int [] all_tiles; int [] all_tiles;
double [][][] val_weights; double [][][] val_weights; // initial values/weights
double init_val; double init_val;
double init_weight; double init_weight;
double [][][] last_val_weights; // last calculated
HashMap<Integer,Integer> tile_map; // map from tile full index to index in neibs[] and HashMap<Integer,Integer> tile_map; // map from tile full index to index in neibs[] and
HashMap<Integer,Integer> all_tile_map; // map from tile full index to index in val_weights[] and
public ConnectionCosts( public ConnectionCosts(
double orthoWeight, double orthoWeight,
...@@ -93,6 +95,11 @@ public class ConnectionCosts { ...@@ -93,6 +95,11 @@ public class ConnectionCosts {
tile_map.put(mod_tiles[i], i); tile_map.put(mod_tiles[i], i);
} }
all_tile_map = new HashMap<Integer,Integer>();
for (int i = 0; i < all_tiles.length; i++){
all_tile_map.put(all_tiles[i], i);
}
switch (steps){ switch (steps){
case 1: case 1:
val_weights = getConnectionsCostSingleStep ( val_weights = getConnectionsCostSingleStep (
...@@ -110,6 +117,8 @@ public class ConnectionCosts { ...@@ -110,6 +117,8 @@ public class ConnectionCosts {
-1); // int debugLevel) -1); // int debugLevel)
} }
last_val_weights = val_weights;
init_val = 0.0; init_val = 0.0;
init_weight = 0.0; // should not change during update init_weight = 0.0; // should not change during update
for (int isTile = 0; isTile < all_tiles.length; isTile++){ for (int isTile = 0; isTile < all_tiles.length; isTile++){
...@@ -125,6 +134,35 @@ public class ConnectionCosts { ...@@ -125,6 +134,35 @@ public class ConnectionCosts {
return neibs_init; // neighbors to clone return neibs_init; // neighbors to clone
} }
public double [] getValWeightLast(
int nsTile,
int nl,
boolean initialValue)
{
Integer isTile = all_tile_map.get(nsTile);
if ((isTile != null) && (isTile < 0)){
System.out.println("isTile < 0");
}
if (nl < 0){
System.out.println(" nl < 0");
}
if ((isTile != null) && (isTile >= 0)){
return initialValue? val_weights[isTile][nl] : last_val_weights[isTile][nl];
} else {
return planes[nsTile][nl].getStarValueWeight();
}
}
public double [] getValWeight()
{
double [] val_weight = {init_val,init_weight};
return val_weight;
}
public double [][][] getValWeights()
{
return val_weights;
}
public double [][][] getConnectionsCostSingleStep ( public double [][][] getConnectionsCostSingleStep (
int [][][] neibs, int [][][] neibs,
...@@ -272,7 +310,7 @@ public class ConnectionCosts { ...@@ -272,7 +310,7 @@ public class ConnectionCosts {
neibs, neibs,
-1); // int debugLevel) -1); // int debugLevel)
} }
last_val_weights = vw;
// calculate new cost // calculate new cost
double new_value = 0.0; double new_value = 0.0;
double new_weight = 0.0; // should not change during update double new_weight = 0.0; // should not change during update
...@@ -288,6 +326,9 @@ public class ConnectionCosts { ...@@ -288,6 +326,9 @@ public class ConnectionCosts {
return new_value - init_val; // negative - improvement return new_value - init_val; // negative - improvement
} }
double [][][] getLastValueWeight(){
return last_val_weights;
}
/** /**
* Calculate main eigenvalue of the current plane and all connected ones - used to estimate advantage of connection swap * Calculate main eigenvalue of the current plane and all connected ones - used to estimate advantage of connection swap
* @param nsTile supertile index * @param nsTile supertile index
......
...@@ -4412,11 +4412,9 @@ public class SuperTiles{ ...@@ -4412,11 +4412,9 @@ public class SuperTiles{
nsTile, nsTile,
conflicts[nsTile][nConfl][0], // int nl1, conflicts[nsTile][nConfl][0], // int nl1,
conflicts[nsTile][nConfl][1], // int nl2, conflicts[nsTile][nConfl][1], // int nl2,
// conflicts[nsTile][nConfl][2], // int dir_mask,
tnSurface, tnSurface,
conflicts, conflicts,
conflict_stats, // to be updated after applying resolution conflict_stats, // to be updated after applying resolution
// maxEigen, // maximal eigenvalue of planes to consider
starSteps, // How far to look around when calculating connection cost starSteps, // How far to look around when calculating connection cost
orthoWeight, orthoWeight,
diagonalWeight, diagonalWeight,
...@@ -4433,11 +4431,10 @@ public class SuperTiles{ ...@@ -4433,11 +4431,10 @@ public class SuperTiles{
} }
public boolean resolveStarConflict( public boolean resolveStarConflict0(
int nsTile, int nsTile,
int nl1, int nl1,
int nl2, int nl2,
// int dir_mask,
TileSurface.TileNeibs tnSurface, TileSurface.TileNeibs tnSurface,
int [][][] conflicts, int [][][] conflicts,
Conflicts conflict_stats, // to be updated after applying resolution Conflicts conflict_stats, // to be updated after applying resolution
...@@ -4562,13 +4559,30 @@ public class SuperTiles{ ...@@ -4562,13 +4559,30 @@ public class SuperTiles{
double [] variant_costs_diff = new double [neibs_vars.length]; double [] variant_costs_diff = new double [neibs_vars.length];
for (int variant = 0; variant < neibs_vars.length; variant ++){ for (int variant = 0; variant < neibs_vars.length; variant ++){
for (int isTile = 0; isTile < nsTiles.length; isTile++){
variant_conflicts[variant][isTile] = iconflicts.detectTriangularTileConflicts(
nsTiles[isTile], // int nsTile0,
replacement_tiles, //HashMap<Integer,Integer> replacement_tiles, //
neibs_vars[variant], // neibs, // int [][][] replacement_neibs,
tnSurface); // TileSurface.TileNeibs tnSurface)
}
variant_conflicts_stats[variant] = new Conflicts(
variant_conflicts[variant],
this,
debugLevel - 1); // debugLevel);
variant_conflicts_stats[variant].subConflicts(iconflicts); // subtract old number of different types of conflicts
if (debugLevel > 1) { if (debugLevel > 1) {
System.out.println("resolveStarConflict(): resolving conflict for tile "+nsTile+ System.out.println("resolveStarConflict(): resolving conflict for tile "+nsTile+
", nl1 = "+nl1+ ", nl1 = "+nl1+
", nl2 = "+nl2 + ", nl2 = "+nl2 +
", variant = "+variant); ", variant = "+variant+
", num_conflicts diff = "+variant_conflicts_stats[variant].getNumConflicts()
);
} }
if (!newConfl && (variant_conflicts_stats[variant].getNumConflicts() > 0)){
continue;
}
variant_costs_diff[variant] = connectionCosts.getConnectionsCostDiff( variant_costs_diff[variant] = connectionCosts.getConnectionsCostDiff(
neibs_vars[variant], neibs_vars[variant],
debugLevel); debugLevel);
...@@ -4577,23 +4591,11 @@ public class SuperTiles{ ...@@ -4577,23 +4591,11 @@ public class SuperTiles{
System.out.println("resolveStarConflict(): resolving conflict for tile "+nsTile+ System.out.println("resolveStarConflict(): resolving conflict for tile "+nsTile+
", nl1 = "+nl1+ ", nl1 = "+nl1+
", nl2 = "+nl2 + ", nl2 = "+nl2 +
", variant = "+variant+" improvement (negative diff) = "+variant_costs_diff[variant]); ", variant = "+variant+" improvement (negative diff) = "+variant_costs_diff[variant]+
", num_conflicts diff = "+variant_conflicts_stats[variant].getNumConflicts());
} }
for (int isTile = 0; isTile < nsTiles.length; isTile++){
variant_conflicts[variant][isTile] = iconflicts.detectTriangularTileConflicts(
nsTiles[isTile], // int nsTile0,
replacement_tiles, //HashMap<Integer,Integer> replacement_tiles, //
neibs_vars[variant], // neibs, // int [][][] replacement_neibs,
tnSurface); // TileSurface.TileNeibs tnSurface)
}
variant_conflicts_stats[variant] = new Conflicts(
variant_conflicts[variant],
this,
debugLevel - 1); // debugLevel);
variant_conflicts_stats[variant].subConflicts(iconflicts); // subtract old number of different types of conflicts
if (debugLevel > 0) { // -1) { if (debugLevel > 0) { // -1) {
variant_conflicts_stats[variant].printConflictSummary( variant_conflicts_stats[variant].printConflictSummary(
"Conflicts difference after resolution:", true, true, false); "Conflicts difference after resolution:", true, true, false);
...@@ -4603,6 +4605,7 @@ public class SuperTiles{ ...@@ -4603,6 +4605,7 @@ public class SuperTiles{
int best_variant = -1; int best_variant = -1;
int best_ignore_conflicts = -1; int best_ignore_conflicts = -1;
int [][] num_better_worse = new int [neibs_vars.length][2]; int [][] num_better_worse = new int [neibs_vars.length][2];
/*
for (int variant = 0; variant < neibs_vars.length; variant ++){ for (int variant = 0; variant < neibs_vars.length; variant ++){
int num_worse = variant_conflicts_stats[variant].numBetterWorse( int num_worse = variant_conflicts_stats[variant].numBetterWorse(
false, // boolean better, false, // boolean better,
...@@ -4630,6 +4633,24 @@ public class SuperTiles{ ...@@ -4630,6 +4633,24 @@ public class SuperTiles{
best_ignore_conflicts = variant; best_ignore_conflicts = variant;
} }
} }
*/
int [] num_var_conflicts = new int [neibs_vars.length];
for (int variant = 0; variant < neibs_vars.length; variant ++){
// int num_conflicts = variant_conflicts_stats[variant].getNumConflicts();
num_var_conflicts[variant] = variant_conflicts_stats[variant].getNumConflicts();
if ((num_var_conflicts[variant] <= 0) &&
(variant_costs_diff[variant] <= dblTriLoss) && // not too worse
((variant_costs_diff[variant] < 0) || (num_var_conflicts[variant] < 0)) && // either
((best_variant < 0) ||
(num_var_conflicts[variant] < num_var_conflicts[best_variant]) ||
((num_var_conflicts[variant] == num_var_conflicts[best_variant]) &&
(variant_costs_diff[variant] < variant_costs_diff[best_variant])))){
best_variant = variant;
}
if ((best_ignore_conflicts <0) || (variant_costs_diff[variant] < variant_costs_diff[best_ignore_conflicts])){
best_ignore_conflicts = variant;
}
}
if (debugLevel > 1){ if (debugLevel > 1){
System.out.println("resolveStarConflict(): for tile "+nsTile); System.out.println("resolveStarConflict(): for tile "+nsTile);
...@@ -4655,7 +4676,8 @@ public class SuperTiles{ ...@@ -4655,7 +4676,8 @@ public class SuperTiles{
System.out.println("resolveStarConflict(): SUCCESS to find a sutable solution for tile "+nsTile+ System.out.println("resolveStarConflict(): SUCCESS to find a sutable solution for tile "+nsTile+
", nl1 = "+nl1+ ", nl1 = "+nl1+
", nl2 = "+nl2 +". Of "+ neibs_vars.length+" variants - use variant # " + best_variant+ ", nl2 = "+nl2 +". Of "+ neibs_vars.length+" variants - use variant # " + best_variant+
" cost difference (negative) = "+variant_costs_diff[best_variant]+" num conflict reductions = "+num_better_worse[best_variant][0]); " cost difference (negative) = "+variant_costs_diff[best_variant]+
" num conflict reductions = "+(-num_var_conflicts[best_variant])+" (old "+num_better_worse[best_variant][0]+")");
variant_conflicts_stats[best_variant].printConflictSummary( variant_conflicts_stats[best_variant].printConflictSummary(
"Conflicts number change per type: ", "Conflicts number change per type: ",
true, // use_all, true, // use_all,
...@@ -4697,7 +4719,404 @@ public class SuperTiles{ ...@@ -4697,7 +4719,404 @@ public class SuperTiles{
return true; return true;
} }
// public boolean resolveStarConflict(
int nsTile,
int nl1,
int nl2,
TileSurface.TileNeibs tnSurface,
int [][][] conflicts,
Conflicts conflict_stats, // to be updated after applying resolution
int starSteps, // How far to look around when calculationg connection cost
double orthoWeight,
double diagonalWeight,
double starPwr, // Divide cost by number of connections to this power
double dblTriLoss, // When resolving double triangles allow minor degradation (0.0 - strict)
boolean newConfl, // Allow more conflicts if overall cost is reduced
boolean preferDisparity,
int debugLevel)
{
if (newConfl && (dblTriLoss > 0.0)){
dblTriLoss = 0.0; // require strict reducing of the cost if conflict increase is allowed
}
Conflicts iconflicts = new Conflicts(this);
TwoLayerNeighbors twoLayerNeighbors = new TwoLayerNeighbors();
for (int dir = -1; dir < 8; dir++){
int nt = tnSurface.getNeibIndex(nsTile, dir);
if ((nt >= 0) && (planes[nt] != null)) {
int [][] neibs = new int [planes[nt].length][];
for (int nl = 0; nl < planes[nt].length; nl++) if ( planes[nt][nl] != null){
neibs[nl] = planes[nt][nl].getNeibBest();
}
twoLayerNeighbors.setNeighbors(neibs,dir);
}
}
twoLayerNeighbors.setLayers(nl1, nl2);
if (debugLevel > 1) {
System.out.println("resolveStarConflict(): nsTile ="+nsTile+" nl1="+nl1+" nl2="+nl2);
}
int [][][][] neibs_vars_dir = twoLayerNeighbors.getNeighborVariants(debugLevel);
int [] mod_supertiles = {nsTile};
mod_supertiles = getInvolvedSupertiles( // first mod_supertiles.length entries will be mod_supertiles[]
mod_supertiles,
tnSurface);
HashMap<Integer,Integer> replacement_tiles = new HashMap<Integer,Integer>();
for (int i = 0; i < mod_supertiles.length; i++){
replacement_tiles.put(mod_supertiles[i], new Integer(i));
}
// up to 9 tiles
int [] indexToDir = new int[mod_supertiles.length];
for (int i = 0; i < indexToDir.length; i++) indexToDir[i] = -1;
for (int dir = -1; dir < 8; dir++){
int nindx = (dir < 0) ? 8 : dir;
int nt = tnSurface.getNeibIndex(nsTile, dir);
int indx = -1;
Integer Indx = replacement_tiles.get(nt);
if (Indx == null){
System.out.println("resolveStarConflict(): nsTile = "+nsTile+" nindx="+nindx+" Indx == null ");
} else {
indx = Indx;
}
if (indx >= 0) {
indexToDir[indx] = nindx;
}
}
int [][][][] neibs_vars = new int [neibs_vars_dir.length][][][];
for (int variant = 0; variant < neibs_vars_dir.length; variant ++){
neibs_vars[variant] = new int [indexToDir.length][][];
for (int i = 0; i <indexToDir.length; i++){
if (indexToDir[i] >=0){
neibs_vars[variant][i] = neibs_vars_dir[variant][indexToDir[i]];
} else {
System.out.println("resolveStarConflict(): a BUG: indexToDir["+i+"] = "+indexToDir[i]);
}
}
}
// See how this application will influence number of conflicts
// All supertiles that may have different conflicts
int [] nsTiles = getInvolvedSupertiles( // first mod_supertiles.length entries will be mod_supertiles[]
mod_supertiles,
tnSurface);
ConnectionCosts connectionCosts = new ConnectionCosts(
orthoWeight,
diagonalWeight,
starPwr, // Divide cost by number of connections to this power
starSteps,
this.planes,
tnSurface,
preferDisparity);
int [][][] neibs_prev = connectionCosts.initConnectionCosts(mod_supertiles);
// connectionCosts now contains last calculated val/weight pairs for broader array of tile data
int [][][] conflicts_old = new int [nsTiles.length][][];
double conflicts_old_cost = 0.0;
for (int isTile = 0; isTile < nsTiles.length; isTile++){
conflicts_old[isTile] = iconflicts.detectTriangularTileConflicts(
nsTiles[isTile], // int nsTile0,
replacement_tiles, // HashMap<Integer,Integer> replacement_tiles, //
neibs_prev, // int [][][] replacement_neibs,
tnSurface); // TileSurface.TileNeibs tnSurface)
// calculate cost of conflicts
conflicts_old_cost += iconflicts.getConflictsCost(
nsTiles[isTile], // int nsTile0,
1.0, // double scaleStartEnd, // include start and and layer tiles in the center in overall cost for each triangle (1.0)
conflicts_old[isTile], // int [][] conflicts, //
replacement_tiles, // HashMap<Integer,Integer> replacement_tiles, // null is OK
neibs_prev, // int [][][] replacement_neibs, // null OK if replacement_tiles == null
connectionCosts, // ConnectionCosts connectionCosts,
tnSurface); // TileSurface.TileNeibs tnSurface)
}
if (debugLevel > 1) {
System.out.println("Sum of old conflict costs around " + nsTile + " is "+conflicts_old_cost);
System.out.println("Involved supertiles:");
for (int i = 0; i < nsTiles.length; i++){
System.out.println(i+":"+nsTiles[i]);
}
}
if (debugLevel > 1) {
System.out.println("Calculating original conflicts");
}
iconflicts.addConflicts(conflicts_old,
debugLevel - 1); // debugLevel);
// After getting old (referfence data) iterate through all variants, for each calculate cost and number of conflicts.
// First - just collect data - cost and full statistics of the conflicts, print them
// Then select the best one (not incrementing number of conflicts? and reducing cost)
int [][][][] variant_conflicts = new int [neibs_vars.length][nsTiles.length][][];
Conflicts [] variant_conflicts_stats = new Conflicts [neibs_vars.length];
double [] variant_costs_diff = new double [neibs_vars.length];
double [] conflicts_var_cost = new double [neibs_vars.length];
for (int variant = 0; variant < neibs_vars.length; variant ++){
// connections costs are needed before conflicts
variant_costs_diff[variant] = connectionCosts.getConnectionsCostDiff(
neibs_vars[variant],
debugLevel);
conflicts_var_cost[variant] = 0.0;
for (int isTile = 0; isTile < nsTiles.length; isTile++){
variant_conflicts[variant][isTile] = iconflicts.detectTriangularTileConflicts(
nsTiles[isTile], // int nsTile0,
replacement_tiles, //HashMap<Integer,Integer> replacement_tiles, //
neibs_vars[variant], // neibs, // int [][][] replacement_neibs,
tnSurface); // TileSurface.TileNeibs tnSurface)
// connectionCosts now contains last calculated val/weight pairs for broader array of tile data
conflicts_var_cost[variant] += iconflicts.getConflictsCost(
nsTiles[isTile], // int nsTile0,
1.0, // double scaleStartEnd, // include start and and layer tiles in the center in overall cost for each triangle (1.0)
variant_conflicts[variant][isTile], // int [][] conflicts, //
replacement_tiles, // HashMap<Integer,Integer> replacement_tiles, // null is OK
neibs_vars[variant], // int [][][] replacement_neibs, // null OK if replacement_tiles == null
connectionCosts, // ConnectionCosts connectionCosts,
tnSurface); // TileSurface.TileNeibs tnSurface)
}
variant_conflicts_stats[variant] = new Conflicts(
variant_conflicts[variant],
this,
debugLevel - 1); // debugLevel);
variant_conflicts_stats[variant].subConflicts(iconflicts); // subtract old number of different types of conflicts
if (debugLevel > 1) {
System.out.println("resolveStarConflict(): resolving conflict for tile "+nsTile+
", nl1 = "+nl1+
", nl2 = "+nl2 +
", variant = "+variant+
" conflit cost change = "+ (conflicts_var_cost[variant] - conflicts_old_cost)+
", num_conflicts diff = "+variant_conflicts_stats[variant].getNumConflicts()
);
// Initially do not process conflicts cost difference, just view it
}
/*
if (!newConfl && (variant_conflicts_stats[variant].getNumConflicts() > 0)){
continue;
}
*/
if (debugLevel > 0) {
System.out.println("resolveStarConflict(): resolving conflict for tile "+nsTile+
", nl1 = "+nl1+
", nl2 = "+nl2 +
", variant = "+variant+" improvement (negative diff) = "+variant_costs_diff[variant]+
" conflit cost change = "+ (conflicts_var_cost[variant] - conflicts_old_cost)+
", num_conflicts diff = "+variant_conflicts_stats[variant].getNumConflicts());
}
if (debugLevel > 0) { // -1) {
variant_conflicts_stats[variant].printConflictSummary(
"Conflicts difference after resolution:", true, true, false);
}
}
// How to compare? 1 attempt: none of the conflicts get worse, some get better or cost goes down
int best_variant = -1;
int best_ignore_conflicts = -1;
// int [][] num_better_worse = new int [neibs_vars.length][2];
int [] num_var_conflicts = new int [neibs_vars.length];
for (int variant = 0; variant < neibs_vars.length; variant ++){
if (variant_costs_diff[variant] <= dblTriLoss) {
if (conflicts_var_cost[variant] < conflicts_old_cost) {
if ((best_variant < 0) || (conflicts_var_cost[variant] < conflicts_var_cost[best_variant])) {
best_variant = variant;
}
}
if ((best_ignore_conflicts < 0) || (variant_costs_diff[variant] < variant_costs_diff[best_ignore_conflicts])){
best_ignore_conflicts = variant;
}
}
}
if (debugLevel > 1){
System.out.println("resolveStarConflict(): for tile "+nsTile);
}
if ((best_variant < 0) && newConfl && (variant_costs_diff[best_ignore_conflicts] < 0)){ // should be cost improvement
best_variant = best_ignore_conflicts;
if (debugLevel > -1) {
System.out.println("resolveMultiTriangularConflict(): conflicts increase but cost decreases "+nsTile+
", nl1 = "+nl1+
", nl2 = "+nl2 +" of "+ neibs_vars.length+" variants");
}
}
if ((best_variant < 0) || (variant_costs_diff[best_variant] > dblTriLoss)){
if (debugLevel > -1) {
System.out.println("resolveMultiTriangularConflict(): FAILED find a sutable solution for tile "+nsTile+
", nl1 = "+nl1+
", nl2 = "+nl2 +" of "+ neibs_vars.length+" variants");
return false;
}
} else {
if (debugLevel > -1) {
System.out.println("resolveStarConflict(): SUCCESS to find a sutable solution for tile "+nsTile+
", nl1 = "+nl1+
", nl2 = "+nl2 +". Of "+ neibs_vars.length+" variants - use variant # " + best_variant+
" conflit cost change = "+ (conflicts_var_cost[best_variant] - conflicts_old_cost)+
" cost difference (negative) = "+variant_costs_diff[best_variant]+
" num conflict reductions = "+(-num_var_conflicts[best_variant]));
variant_conflicts_stats[best_variant].printConflictSummary(
"Conflicts number change per type: ",
true, // use_all,
true, //use_odo,
true); // use_ood);
iconflicts.printConflictSummary(
"Conflicts before resolution: ",
true, // use_all,
true, //use_odo,
true); // use_ood);
// update statistics
conflict_stats.addConflicts(variant_conflicts_stats[best_variant]);
// update conflict
for (int i = 0; i < nsTiles.length; i++){
conflicts[nsTiles[i]]= variant_conflicts[best_variant][i];
}
// apply resolution
for (int i = 0; i < mod_supertiles.length; i++){
if (debugLevel > 1){
System.out.println("resolveStarConflict(): nsTile = "+nsTile+ "mod_supertiles["+i+"]="+mod_supertiles[i]);
}
if (neibs_vars[best_variant][i] != null) {
for (int nl = 0; nl < neibs_vars[best_variant][i].length; nl ++){
if (debugLevel > 1){
System.out.println("resolveStarConflict(): nl= = "+nl);
}
if (neibs_vars[best_variant][i][nl] != null){
planes[mod_supertiles[i]][nl].setNeibBest(neibs_vars[best_variant][i][nl]);
}
}
}
}
// recalculate starValueWeights for and around tiles with modified neighbors (no outside connections changed )nsTiles
updateStarValueStrength(
nsTiles, // final int [] mod_supertiles,
orthoWeight, // final double orthoWeight,
diagonalWeight, // final double diagonalWeight,
starPwr, // final double starPwr, // Divide cost by number of connections to this power
starSteps, // final int steps,
planes, // final TilePlanes.PlaneData [][] planes,
preferDisparity); // final boolean preferDisparity)
}
}
return true;
}
public void calcStarValueStrength(
final double orthoWeight,
final double diagonalWeight,
final double starPwr, // Divide cost by number of connections to this power
final int steps,
final TilePlanes.PlaneData [][] planes,
final boolean preferDisparity)
{
final int tilesX = tileProcessor.getTilesX();
final int tilesY = tileProcessor.getTilesY();
final int superTileSize = tileProcessor.getSuperTileSize();
// final int tileSize = tileProcessor.getTileSize();
final int stilesX = (tilesX + superTileSize -1)/superTileSize;
final int stilesY = (tilesY + superTileSize -1)/superTileSize;
final int nStiles = stilesX * stilesY;
final TileSurface.TileNeibs tnSurface = tileSurface.new TileNeibs(stilesX, stilesY);
final Thread[] threads = ImageDtt.newThreadArray(tileProcessor.threadsMax);
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
ConnectionCosts connectionCosts = new ConnectionCosts(
orthoWeight, // double orthoWeight,
diagonalWeight, // double diagonalWeight,
starPwr, // double starPwr, // Divide cost by number of connections to this power
steps, // int steps,
planes, // TilePlanes.PlaneData [][] planes,
tnSurface, // TileSurface.TileNeibs tnSurface,
preferDisparity); // boolean preferDisparity)
int [] mod_supertiles = new int[1];
for (int nsTile = ai.getAndIncrement(); nsTile < nStiles; nsTile = ai.getAndIncrement()) {
if ( planes[nsTile] != null) {
mod_supertiles[0] = nsTile;
connectionCosts.initConnectionCosts(mod_supertiles);
double [][][] val_weights = connectionCosts.getValWeights();
for (int np = 0; np < planes[nsTile].length; np++){ // nu
if (planes[nsTile][np] != null) {
planes[nsTile][np].setStarValueWeight(val_weights[0][np]);
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
public void updateStarValueStrength(
final int [] mod_supertiles,
final double orthoWeight,
final double diagonalWeight,
final double starPwr, // Divide cost by number of connections to this power
final int steps,
final TilePlanes.PlaneData [][] planes,
final boolean preferDisparity)
{
final int tilesX = tileProcessor.getTilesX();
final int tilesY = tileProcessor.getTilesY();
final int superTileSize = tileProcessor.getSuperTileSize();
// final int tileSize = tileProcessor.getTileSize();
final int stilesX = (tilesX + superTileSize -1)/superTileSize;
final int stilesY = (tilesY + superTileSize -1)/superTileSize;
final TileSurface.TileNeibs tnSurface = tileSurface.new TileNeibs(stilesX, stilesY);
final Thread[] threads = ImageDtt.newThreadArray(tileProcessor.threadsMax);
final AtomicInteger ai = new AtomicInteger(0);
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
ConnectionCosts connectionCosts = new ConnectionCosts(
orthoWeight, // double orthoWeight,
diagonalWeight, // double diagonalWeight,
starPwr, // double starPwr, // Divide cost by number of connections to this power
steps, // int steps,
planes, // TilePlanes.PlaneData [][] planes,
tnSurface, // TileSurface.TileNeibs tnSurface,
preferDisparity); // boolean preferDisparity)
int [] mod_supertile = new int[1];
for (int isTile = ai.getAndIncrement(); isTile < mod_supertiles.length; isTile = ai.getAndIncrement()) {
int nsTile = mod_supertiles[isTile];
if ((nsTile >= 0) && ( planes[nsTile] != null)) {
mod_supertile[0] = nsTile;
connectionCosts.initConnectionCosts(mod_supertile);
double [][][] val_weights = connectionCosts.getValWeights();
for (int np = 0; np < planes[nsTile].length; np++){ // nu
if (planes[nsTile][np] != null) {
planes[nsTile][np].setStarValueWeight(val_weights[0][np]);
}
}
}
}
}
};
}
ImageDtt.startAndJoin(threads);
}
//
/** /**
* Generate variants for changing connections while preserving number of connections between each pair of tiles * Generate variants for changing connections while preserving number of connections between each pair of tiles
* each variant is free of own conflicts, but may lead to conflicts on other layers or tiles * each variant is free of own conflicts, but may lead to conflicts on other layers or tiles
...@@ -4963,6 +5382,13 @@ public class SuperTiles{ ...@@ -4963,6 +5382,13 @@ public class SuperTiles{
int dbg_Y) int dbg_Y)
{ {
calcStarValueStrength(
orthoWeight, // final double orthoWeight,
diagonalWeight, // final double diagonalWeight,
starPwr, // final double starPwr, // Divide cost by number of connections to this power
starSteps, // final int steps,
this.planes, // final TilePlanes.PlaneData [][] planes,
preferDisparity); // final boolean preferDisparity)
Conflicts iconflicts0 = new Conflicts(this); Conflicts iconflicts0 = new Conflicts(this);
int [][][] conflicts0 = iconflicts0.detectTriangularConflicts( int [][][] conflicts0 = iconflicts0.detectTriangularConflicts(
...@@ -5511,80 +5937,6 @@ public class SuperTiles{ ...@@ -5511,80 +5937,6 @@ public class SuperTiles{
return value_weight; return value_weight;
} }
/**
* Calculate main eigenvalue of the current plane and all connected ones - used to estimate advantage of connection swap
* This version uses two steps - not only directly connected, but neighbors' neighbors also, multiple paths to the same
* tile add together.
* @param nsTile supertile index
* @param nl surface layer
* @param neibs array of 8 neighbors layers (N,NE,...NW), -1 - not connected
* @param neibs2 2-d array of 8 neighbors' neighbors layers (N,NE,...NW), -1 - not connected
* @param orthoWeight multiply contribution of ortho neighbors
* @param diagonalWeight multiply contribution of diagonal neighbors
* @param diagonalWeight divide value by number of connections to this power (if !=0)
* @param tnSurface TileNeibs instance to navigate tile index and control array borders
* @param preferDisparity - the first eigenvalue/vector is the most disparity-like
* (false - smallest eigenvalue)
* @param debugLevel
* @return a pair of eigenvalue of the combine plane and its weight
*/
/*
public double [] getStarValueWeight(
int nsTile,
int nl,
int [] neibs,
int [][] neibs2, // neighbors' neighbors
double orthoWeight,
double diagonalWeight,
double starPwr, // Divide cost by number of connections to this power
TileSurface.TileNeibs tnSurface,
boolean preferDisparity,
int debugLevel)
{
double [] dir_weight = {orthoWeight, diagonalWeight, orthoWeight, diagonalWeight, orthoWeight, diagonalWeight, orthoWeight, diagonalWeight};
HashMap<Point, Double> tile_weights = new HashMap<Point, Double>();
for (int dir = 0; dir < 8; dir++){
int nl1 = neibs[dir];
if (nl1 >= 0){
int nsTile1 = tnSurface.getNeibIndex(nsTile, dir);
double weight1 = dir_weight[dir];
tile_weights.put(new Point(nsTile1, nl1), new Double(weight1)); // no need to check for existence here
for (int dir1 = 0; dir1 < 8; dir1++){
if ((dir1 != dir) && (neibs2[dir]!= null)){
int nl2 =neibs2[dir][dir1];
if (nl2 >= 0){
Point p = new Point (tnSurface.getNeibIndex(nsTile1, dir1), nl2);
Double w0 = tile_weights.get(p);
double weight2 = dir_weight[dir1]*weight1;
if (w0 != null) weight2 += w0;
tile_weights.put(p, new Double(weight2));
}
}
}
}
}
TilePlanes.PlaneData merged_plane = planes[nsTile][nl]; // center point
for (HashMap.Entry<Point, Double> entry : tile_weights.entrySet()){
TilePlanes.PlaneData other_plane = merged_plane.getPlaneToThis( // layer here does not matter
planes[entry.getKey().x][entry.getKey().y],
debugLevel - 1); // debugLevel);
merged_plane = merged_plane.mergePlaneToThis(
other_plane, // PlaneData otherPd,
entry.getValue(), // double scale_other,
false, // boolean ignore_weights,
true, // boolean sum_weights,
preferDisparity,
debugLevel - 1); // int debugLevel)
}
double [] value_weight = {merged_plane.getValue(),merged_plane.getWeight()};
if (starPwr != 0){
value_weight[0] /= (Math.pow(tile_weights.size() + 1.0, starPwr));
}
return value_weight;
}
*/
/** /**
* Calculate array of supertile indices that need to have connection cost recalculated when they are updated * Calculate array of supertile indices that need to have connection cost recalculated when they are updated
* first entries of the result will be the same in input array * first entries of the result will be the same in input array
......
...@@ -82,6 +82,7 @@ public class TilePlanes { ...@@ -82,6 +82,7 @@ public class TilePlanes {
int smplNum = 3; // Number after removing worst int smplNum = 3; // Number after removing worst
double smplRms = 0.1; // Maximal RMS of the remaining tiles in a sample double smplRms = 0.1; // Maximal RMS of the remaining tiles in a sample
double [] starValueWeight = null;
boolean preferDisparity = false; boolean preferDisparity = false;
...@@ -133,9 +134,29 @@ public class TilePlanes { ...@@ -133,9 +134,29 @@ public class TilePlanes {
pd.preferDisparity = this.preferDisparity; pd.preferDisparity = this.preferDisparity;
copyNeib(this,pd); copyNeib(this,pd);
if (starValueWeight != null){
pd.starValueWeight = starValueWeight.clone();
}
return pd; return pd;
} }
public void setStarValueWeight(double value, double weight){
this.starValueWeight = new double[2];
this.starValueWeight[0] = value;
this.starValueWeight[1] = weight;
}
public void setStarValueWeight(double[] val_weight){
this.starValueWeight = val_weight;
}
public double [] getStarValueWeight()
{
return starValueWeight;
}
public void setSelMask (boolean []sel_mask) public void setSelMask (boolean []sel_mask)
{ {
this.sel_mask = sel_mask; this.sel_mask = sel_mask;
......
...@@ -3445,6 +3445,25 @@ public class TileProcessor { ...@@ -3445,6 +3445,25 @@ public class TileProcessor {
1, // final int debugLevel) 1, // final int debugLevel)
clt_parameters.tileX, clt_parameters.tileX,
clt_parameters.tileY); clt_parameters.tileY);
/*
st.resolveConflicts(
clt_parameters.plMaxEigen,
clt_parameters.plConflDualTri, // boolean conflDualTri, // Resolve dual triangles conflict (odoodo)
clt_parameters.plConflMulti, // boolean conflMulti, // Resolve multiple odo triangles conflicts
clt_parameters.plConflDiag, // boolean conflDiag, // Resolve diagonal (ood) conflicts
clt_parameters.plConflStar, // boolean conflStar, // Resolve all conflicts around a supertile
clt_parameters.plStarSteps, // int starSteps, // How far to look around when calculationg connection cost
clt_parameters.plStarOrtho, // double orthoWeight,
clt_parameters.plStarDiag, // double diagonalWeight,
clt_parameters.plStarPwr, // double starPwr, // Divide cost by number of connections to this power
clt_parameters.plDblTriLoss, // double diagonalWeight,
true, // clt_parameters.plNewConfl, // Allow more conflicts if overall cost is reduced
clt_parameters.plPreferDisparity,
1, // final int debugLevel)
clt_parameters.tileX,
clt_parameters.tileY);
*/
if (clt_parameters.plSplitApply) { if (clt_parameters.plSplitApply) {
while (true) { while (true) {
......
...@@ -154,9 +154,29 @@ public class TwoLayerNeighbors { ...@@ -154,9 +154,29 @@ public class TwoLayerNeighbors {
if (old_nl1 >= 0){ if (old_nl1 >= 0){
neibs_start[old_nl1][dir12] = old_nl2; // (old_nl2 may be -1 here) neibs_start[old_nl1][dir12] = old_nl2; // (old_nl2 may be -1 here)
} }
}
public int getConnection(
int dir,
int nl,
int dir2)
{
int [][] neibs_start = getNeighbors(dir);
int dir12 = getDir2From1(dir, dir2);
if (dir12 <0){
throw new IllegalArgumentException ("Invalid connection from "+dir+" to "+dir2+": resulted in direction 1->2 = "+dir12);
}
if (neibs_start.length <= nl){
System.out.println("BUG");
return -1;
}
return neibs_start[nl][dir12];
} }
public void diffToOther(NeibVariant other_variant) public void diffToOther(NeibVariant other_variant)
{ {
for (int dir0 = 0; dir0 < neighbors.length; dir0++){ for (int dir0 = 0; dir0 < neighbors.length; dir0++){
...@@ -200,8 +220,8 @@ public class TwoLayerNeighbors { ...@@ -200,8 +220,8 @@ public class TwoLayerNeighbors {
// increment connection variant if possible // increment connection variant if possible
for (int np = 0; np < PAIRS.length; np++){ for (int np = 0; np < PAIRS.length; np++){
if ((num_se[np] != null) && (num_se[np][0] == 2) && (num_se[np][1] == 2) && (conns[np] != null) && (conns[np].length == 1)){ if ((num_se[np] != null) && (num_se[np][0] == 2) && (num_se[np][1] == 2) && (conns[np] != null) && (conns[np].length == 1)){
if (selection_conns[np] == 0){ if (selection_conns[np] < 2){
selection_conns[np] = 1; selection_conns[np]++; // = 1;
for (int i = 0; i < np; i ++){ for (int i = 0; i < np; i ++){
selection_conns[i] = 0; selection_conns[i] = 0;
} }
...@@ -275,7 +295,9 @@ public class TwoLayerNeighbors { ...@@ -275,7 +295,9 @@ public class TwoLayerNeighbors {
int start_dir = PAIRS[np][0]; int start_dir = PAIRS[np][0];
int end_dir = PAIRS[np][1]; int end_dir = PAIRS[np][1];
boolean swap = (selection_star[start_dir] != selection_star[end_dir]) ^ (selection_conns[np] > 0);
boolean swap = (selection_star[start_dir] != selection_star[end_dir]) ^ (selection_conns[np] == 1);
int [] opts = {0,0}; int [] opts = {0,0};
if (swap){ if (swap){
if (num_se[np][0] > 1){ if (num_se[np][0] > 1){
...@@ -290,6 +312,23 @@ public class TwoLayerNeighbors { ...@@ -290,6 +312,23 @@ public class TwoLayerNeighbors {
end_dir, // int dir2, end_dir, // int dir2,
layers_around[end_dir][opts[1]], // int nl2); layers_around[end_dir][opts[1]], // int nl2);
debugLevel); debugLevel);
if (selection_conns[np] > 1){
// add 3-rd variant if possible, if not - return null
// if at least one of the unused ends has a pair - connect other ends
int nl_start_other = layers_around[start_dir][1-opts[0]];
int nl_end_other = layers_around[end_dir][1-opts[1]];
if ( (variant.getConnection(start_dir,nl_start_other,end_dir) >= 0) ||
(variant.getConnection(end_dir,nl_end_other,start_dir) >= 0)) {
variant.connect(
start_dir, // int dir1,
nl_start_other, // int nl1,
end_dir, // int dir2,
nl_end_other, // int nl2);
debugLevel);
} else {
return null; // failed to swap connection - other ends are both not connected
}
}
} }
if (debugLevel > 1){ if (debugLevel > 1){
System.out.println(); System.out.println();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment