// ---------------//
  // isExtensionOf //
  // ---------------//
  public boolean isExtensionOf(
      Stick other, int maxDeltaCoord, int maxDeltaPos, double maxDeltaSlope) {
    // Check that a pair of start/stop is compatible
    if ((Math.abs(other.getStart() - getStop()) <= maxDeltaCoord)
        || (Math.abs(other.getStop() - getStart()) <= maxDeltaCoord)) {
      // Check that a pair of positions is compatible
      if ((Math.abs(other.getLine().yAt(other.getStart()) - getLine().yAt(other.getStop()))
              <= maxDeltaPos)
          || (Math.abs(other.getLine().yAt(other.getStop()) - getLine().yAt(other.getStart()))
              <= maxDeltaPos)) {
        // Check that slopes are compatible (a useless test ?)
        if (Math.abs(other.getLine().getSlope() - getLine().getSlope()) <= maxDeltaSlope) {
          return true;
        } else if (logger.isFineEnabled()) {
          logger.fine("isExtensionOf:  Incompatible slopes");
        }
      } else if (logger.isFineEnabled()) {
        logger.fine("isExtensionOf:  Incompatible positions");
      }
    } else if (logger.isFineEnabled()) {
      logger.fine("isExtensionOf:  Incompatible coordinates");
    }

    return false;
  }
  // -------------//
  // computeLine //
  // -------------//
  public void computeLine() {
    line = new BasicLine();

    for (GlyphSection section : glyph.getMembers()) {
      StickSection ss = (StickSection) section;
      line.includeLine(ss.getLine());
    }

    if (logger.isFineEnabled()) {
      logger.fine(
          line
              + " pointNb="
              + line.getNumberOfPoints()
              + " meanDistance="
              + (float) line.getMeanDistance());
    }
  }
  // ------------------//
  // getAlienPixelsIn //
  // ------------------//
  public int getAlienPixelsIn(Rectangle area) {
    int count = 0;
    final int posMin = area.y;
    final int posMax = (area.y + area.height) - 1;
    final List<GlyphSection> neighbors = glyph.getLag().getSectionsIn(area);

    for (GlyphSection section : neighbors) {
      // Keep only non-patch sections that are not part of the stick
      if (!section.isPatch() && (section.getGlyph() != glyph)) {
        int pos = section.getFirstPos() - 1; // Ordinate for horizontal,
        // Abscissa for vertical

        for (Run run : section.getRuns()) {
          pos++;

          if (pos > posMax) {
            break;
          }

          if (pos < posMin) {
            continue;
          }

          int coordMin = Math.max(area.x, run.getStart());
          int coordMax = Math.min((area.x + area.width) - 1, run.getStop());

          if (coordMax >= coordMin) {
            count += (coordMax - coordMin + 1);
          }
        }
      }
    }

    if (logger.isFineEnabled()) {
      logger.fine("Stick" + glyph.getId() + " " + area + " getAlienPixelsIn=" + count);
    }

    return count;
  }
  /**
   * In a specified system, look for all stems that should not be kept, rebuild surrounding glyphs
   * and try to recognize them. If this action does not lead to some recognized symbol, then we
   * restore the stems.
   *
   * @return the number of symbols recognized
   */
  public int runStemPattern() {
    int nb = 0;

    // Collect all undue stems
    List<Glyph> SuspectedStems = new ArrayList<Glyph>();

    for (Glyph glyph : system.getGlyphs()) {
      if (glyph.isStem() && glyph.isActive()) {
        Set<Glyph> goods = new HashSet<Glyph>();
        Set<Glyph> bads = new HashSet<Glyph>();
        glyph.getSymbolsBefore(reliableStemSymbols, goods, bads);
        glyph.getSymbolsAfter(reliableStemSymbols, goods, bads);

        if (goods.isEmpty()) {
          if (logger.isFineEnabled()) {
            logger.finest("Suspected Stem " + glyph);
          }

          SuspectedStems.add(glyph);

          // Discard "bad" ones
          for (Glyph g : bads) {
            if (logger.isFineEnabled()) {
              logger.finest("Deassigning bad glyph " + g);
            }

            g.setShape((Shape) null);
          }
        }
      }
    }

    // Remove these stem glyphs since nearby stems are used for recognition
    for (Glyph glyph : SuspectedStems) {
      system.removeGlyph(glyph);
    }

    // Extract brand new glyphs (removeInactiveGlyphs + retrieveGlyphs)
    system.extractNewGlyphs();

    // Try to recognize each glyph in turn
    List<Glyph> symbols = new ArrayList<Glyph>();
    final GlyphEvaluator evaluator = GlyphNetwork.getInstance();
    final double maxDoubt = GlyphInspector.getPatternsMaxDoubt();

    for (Glyph glyph : system.getGlyphs()) {
      if (glyph.getShape() == null) {
        Evaluation vote = evaluator.vote(glyph, maxDoubt);

        if (vote != null) {
          glyph.setShape(vote.shape, vote.doubt);

          if (glyph.isWellKnown()) {
            if (logger.isFineEnabled()) {
              logger.finest("New symbol " + glyph);
            }

            symbols.add(glyph);
            nb++;
          }
        }
      }
    }

    // Keep stems that have not been replaced by symbols, definitively
    // remove the others
    for (Glyph stem : SuspectedStems) {
      // Check if one of its section is now part of a symbol
      boolean known = false;
      Glyph glyph = null;

      for (GlyphSection section : stem.getMembers()) {
        glyph = section.getGlyph();

        if ((glyph != null) && glyph.isWellKnown()) {
          known = true;

          break;
        }
      }

      if (!known) {
        // Remove the newly created glyph
        if (glyph != null) {
          system.removeGlyph(glyph);
        }

        // Restore the stem
        system.addGlyph(stem);
      }
    }

    return nb;
  }