//----------------------------------------------------------------------------//
// //
// S e c t i o n s B u i l d e r //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.lag;
import omr.run.PixelFilter;
import omr.run.Run;
import omr.run.RunsTable;
import omr.run.RunsTableFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* Class {@code SectionsBuilder} populates a full lag, by building the
* lag sections and junctions, out of a provided {@link RunsTable}
* instance.
*
* @author Hervé Bitteur
*/
public class SectionsBuilder
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(
SectionsBuilder.class);
//~ Instance fields --------------------------------------------------------
/** Policy for detection of junctions */
private JunctionPolicy junctionPolicy;
/** The lag to populate */
private Lag lag;
/** List of sections just created by createSections() */
private List<Section> created;
/** All Active sections in the next column */
private List<Section> nextActives;
/**
* List of sections in previous column that overlap given run in next column
*/
private List<Section> overlappingSections;
/**
* All Active sections in the previous column, i.e. only sections that have
* a run in previous column
*/
private List<Section> prevActives;
//~ Constructors -----------------------------------------------------------
//-----------------//
// SectionsBuilder //
//-----------------//
/**
* Create an instance of SectionsBuilder.
*
* @param lag the lag to populate
* @param junctionPolicy the policy to detect junctions
*/
public SectionsBuilder (Lag lag,
JunctionPolicy junctionPolicy)
{
this.lag = lag;
this.junctionPolicy = junctionPolicy;
}
//~ Methods ----------------------------------------------------------------
//----------------//
// createSections //
//----------------//
/**
* Populate a lag by creating sections from the provided table of runs
*
* @param runsTable the table of runs
* @return the list of created sections
*/
public List<Section> createSections (RunsTable runsTable)
{
// Get brand new collections
created = new ArrayList<>();
nextActives = new ArrayList<>();
overlappingSections = new ArrayList<>();
prevActives = new ArrayList<>();
// All runs (if any) in first column start each their own section
for (Run run : runsTable.getSequence(0)) {
nextActives.add(createSection(0, run));
}
// Now scan each pair of columns, starting at 2nd column
for (int col = 1; col < runsTable.getSize(); col++) {
List<Run> runList = runsTable.getSequence(col);
// If we have runs in this column
if (!runList.isEmpty()) {
// Copy the former next actives sections
// as the new previous active sections
prevActives = nextActives;
nextActives = new ArrayList<>();
// Process all sections of previous column, then prevActives
// will contain only active sections (that may be continued)
logger.debug("Prev column");
for (Section section : prevActives) {
processPrevSide(section, runList);
}
// Process all runs of next column
logger.debug("Next column");
for (Run run : runList) {
processNextSide(col, run);
}
} else {
nextActives.clear();
}
}
// Some housekeeping
prevActives = null;
nextActives = null;
overlappingSections = null;
// Reset proper Ids
for (Section section : lag.getVertices()) {
int id = section.getId();
if (id < 0) {
section.setId(-id);
}
}
// Store the content of runs table into the lag
lag.addRuns(runsTable);
return created;
}
//----------------//
// createSections //
//----------------//
/**
* Populate a lag by creating sections directly out of a pixel source
*
* @param name a name assigned to the runs table
* @param source the source to read pixels from
* @param minRunLength minimum length to consider a run
* @return the list of created sections
*/
public List<Section> createSections (String name,
PixelFilter source,
int minRunLength)
{
// Define a proper table factory
RunsTableFactory factory = new RunsTableFactory(
lag.getOrientation(),
source,
minRunLength);
// Create the runs table
RunsTable table = factory.createTable(name);
// Now proceed to section extraction
return createSections(table);
}
//-----------------//
// continueSection //
//-----------------//
private void continueSection (Section section,
Run run)
{
logger.debug("Continuing section {} with {}", section, run);
section.append(run);
nextActives.add(section);
}
//---------------//
// createSection //
//---------------//
private Section createSection (int firstPos,
Run firstRun)
{
Section section = lag.createSection(firstPos, firstRun);
created.add(section);
return section;
}
//--------//
// finish //
//--------//
private void finish (Section section)
{
section.setId(-section.getId());
}
//------------//
// isFinished //
//------------//
private boolean isFinished (Section section)
{
return section.getId() < 0;
}
//-----------------//
// processNextSide //
//-----------------//
/**
* Process NextSide takes care of the second column, at the given run,
* checking among the prevActives Sections which overlap this run.
*/
private void processNextSide (int col,
Run run)
{
logger.debug("processNextSide for run {}", run);
int nextStart = run.getStart();
int nextStop = run.getStop();
// Check if overlap with a section run in previous column
// All such sections are then stored in overlappingSections
overlappingSections.clear();
for (Section section : prevActives) {
Run lastRun = section.getLastRun();
if (lastRun.getStart() > nextStop) {
break;
}
if (lastRun.getStop() >= nextStart) {
logger.debug("Overlap from {} to {}", lastRun, run);
overlappingSections.add(section);
}
}
// Processing now depends on nb of overlapping runs
logger.debug("overlap={}", overlappingSections.size());
switch (overlappingSections.size()) {
case 0: // Begin a brand new section
nextActives.add(createSection(col, run));
break;
case 1: // Continuing sections (if not finished)
Section prevSection = overlappingSections.get(0);
if (!isFinished(prevSection)) {
continueSection(prevSection, run);
} else {
// Create a new section, linked by a junction
Section sct = createSection(col, run);
nextActives.add(sct);
prevSection.addTarget(sct);
}
break;
default: // Converging sections, end them, start a new one
logger.debug("Converging at {}", run);
Section newSection = createSection(col, run);
nextActives.add(newSection);
for (Section section : overlappingSections) {
section.addTarget(newSection);
}
}
}
//-----------------//
// processPrevSide //
//-----------------//
/**
* Take care of the first column, at the given section/run,
* checking links to the nextColumnRuns that overlap this run.
*
* @param section the section at hand
* @param nextColumnRuns runs of the next column
*/
private void processPrevSide (Section section,
List<Run> nextColumnRuns)
{
Run lastRun = section.getLastRun();
int prevStart = lastRun.getStart();
int prevStop = lastRun.getStop();
logger.debug("processPrevSide for section {}", section);
// Check if overlap with a run in next column
int overlapNb = 0;
Run overlapRun = null;
for (Run run : nextColumnRuns) {
if (run.getStart() > prevStop) {
break;
}
if (run.getStop() >= prevStart) {
logger.debug("Overlap from {} to {}", lastRun, run);
overlapNb++;
overlapRun = run;
}
}
// Now consider how many overlapping runs we have in next column
logger.debug("overlap={}", overlapNb);
switch (overlapNb) {
case 0: // Nothing : end of the section
logger.debug("Ending section {}", section);
break;
case 1: // Continue if consistent
if (junctionPolicy.consistentRun(overlapRun, section)) {
logger.debug("Perhaps extending section {} with run {}",
section, overlapRun);
} else {
logger.debug("Incompatible height between {} and run {}",
section, overlapRun);
finish(section);
}
break;
default: // Diverging, so conclude the section here
finish(section);
}
}
}