Package com.cburch.logisim.analyze.gui

Source Code of com.cburch.logisim.analyze.gui.KarnaughMapPanel$MyListener

/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.analyze.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.util.List;

import javax.swing.JPanel;

import com.cburch.logisim.analyze.model.AnalyzerModel;
import com.cburch.logisim.analyze.model.OutputExpressionsEvent;
import com.cburch.logisim.analyze.model.OutputExpressionsListener;
import com.cburch.logisim.analyze.model.Entry;
import com.cburch.logisim.analyze.model.Implicant;
import com.cburch.logisim.analyze.model.TruthTable;
import com.cburch.logisim.analyze.model.TruthTableEvent;
import com.cburch.logisim.analyze.model.TruthTableListener;
import com.cburch.logisim.analyze.model.VariableList;
import com.cburch.logisim.util.GraphicsUtil;

class KarnaughMapPanel extends JPanel implements TruthTablePanel {
  private static final Font HEAD_FONT = new Font("Serif", Font.BOLD, 14);
  private static final Font BODY_FONT = new Font("Serif", Font.PLAIN, 14);
  private static final Color[] IMP_COLORS = new Color[] {
    new Color(255, 0, 0, 128),
    new Color(0, 150, 0, 128),
    new Color(0, 0, 255, 128),
    new Color(255, 0, 255, 128),
  };

  private static final int MAX_VARS = 4;
 
  private static final int[] ROW_VARS = { 0, 0, 1, 1, 2 };
  private static final int[] COL_VARS = { 0, 1, 1, 2, 2 };
  private static final int CELL_HORZ_SEP = 10;
  private static final int CELL_VERT_SEP = 10;
  private static final int IMP_INSET = 4;
  private static final int IMP_RADIUS = 5;
 
  private class MyListener
      implements OutputExpressionsListener, TruthTableListener {
    public void expressionChanged(OutputExpressionsEvent event) {
      if (event.getType() == OutputExpressionsEvent.OUTPUT_MINIMAL
          && event.getVariable().equals(output)) {
        repaint();
      }
    }
   
    public void cellsChanged(TruthTableEvent event) {
      repaint();
    }

    public void structureChanged(TruthTableEvent event) {
      computePreferredSize();
    }
   
  }

  private MyListener myListener = new MyListener();
  private AnalyzerModel model;
  private String output;
  private int headHeight;
  private int cellWidth = 1;
  private int cellHeight = 1;
  private int tableWidth;
  private int tableHeight;
  private int provisionalX;
  private int provisionalY;
  private Entry provisionalValue = null;
 
  public KarnaughMapPanel(AnalyzerModel model) {
    this.model = model;
    model.getOutputExpressions().addOutputExpressionsListener(myListener);
    model.getTruthTable().addTruthTableListener(myListener);
    setToolTipText(" ");
  }
 
  public void setOutput(String value) {
    boolean recompute = (output == null || value == null) && output != value;
    output = value;
    if (recompute) computePreferredSize();
    else repaint();
  }
 
  public TruthTable getTruthTable() {
    return model.getTruthTable();
  }
 
  public int getRow(MouseEvent event) {
    TruthTable table = model.getTruthTable();
    int inputs = table.getInputColumnCount();
    if (inputs >= ROW_VARS.length) return -1;
    int left = computeMargin(getWidth(), tableWidth);
    int top = computeMargin(getHeight(), tableHeight);
    int x = event.getX() - left - headHeight - cellWidth;
    int y = event.getY() - top - headHeight - cellHeight;
    if (x < 0 || y < 0) return -1;
    int row = y / cellHeight;
    int col = x / cellWidth;
    int rows = 1 << ROW_VARS[inputs];
    int cols = 1 << COL_VARS[inputs];
    if (row >= rows || col >= cols) return -1;
    return getTableRow(row, col, rows, cols);
  }
 
  public int getOutputColumn(MouseEvent event) {
    return model.getOutputs().indexOf(output);
  }
 
  public void setEntryProvisional(int y, int x, Entry value) {
    provisionalY = y;
    provisionalX = x;
    provisionalValue = value;
    repaint();
  }
 
  @Override
  public String getToolTipText(MouseEvent event) {
    TruthTable table = model.getTruthTable();
    int row = getRow(event);
    int col = getOutputColumn(event);
    Entry entry = table.getOutputEntry(row, col);
    return entry.getErrorMessage();
  }
 
  void localeChanged() {
    computePreferredSize();
    repaint();
  }
 
  private void computePreferredSize() {
    Graphics g = getGraphics();
    TruthTable table = model.getTruthTable();
   
    String message = null;
    if (output == null) {
      message = Strings.get("karnaughNoOutputError");
    } else if (table.getInputColumnCount() > MAX_VARS) {
      message = Strings.get("karnaughTooManyInputsError");
    }
    if (message != null) {
      if (g == null) {
        tableHeight = 15;
        tableWidth = 100;
      } else {
        FontMetrics fm = g.getFontMetrics(BODY_FONT);
        tableHeight = fm.getHeight();
        tableWidth = fm.stringWidth(message);
      }
      setPreferredSize(new Dimension(tableWidth, tableHeight));
      repaint();
      return;
    }
   
    if (g == null) {
      headHeight = 16;
      cellHeight = 16;
      cellWidth = 24;
    } else {
      FontMetrics headFm = g.getFontMetrics(HEAD_FONT);
      headHeight = headFm.getHeight();
     
      FontMetrics fm = g.getFontMetrics(BODY_FONT);
      cellHeight = fm.getAscent() + CELL_VERT_SEP;
      cellWidth = fm.stringWidth("00") + CELL_HORZ_SEP;
    }
   
    int rows = 1 << ROW_VARS[table.getInputColumnCount()];
    int cols = 1 << COL_VARS[table.getInputColumnCount()];
    tableWidth = headHeight + cellWidth * (cols + 1);
    tableHeight = headHeight + cellHeight * (rows + 1);
    setPreferredSize(new Dimension(tableWidth, tableHeight));
    invalidate();
    repaint();
  }

  @Override
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
   
    TruthTable table = model.getTruthTable();
    int inputCount = table.getInputColumnCount();
    Dimension sz = getSize();
    String message = null;
    if (output == null) {
      message = Strings.get("karnaughNoOutputError");
    } else if (inputCount > MAX_VARS) {
      message = Strings.get("karnaughTooManyInputsError");
    }
    if (message != null) {
      g.setFont(BODY_FONT);
      GraphicsUtil.drawCenteredText(g, message, sz.width / 2, sz.height / 2);
      return;
    }

    int left = computeMargin(sz.width, tableWidth);
    int top = computeMargin(sz.height, tableHeight);
    int x = left;
    int y = top;
    int rowVars = ROW_VARS[inputCount];
    int colVars = COL_VARS[inputCount];
    int rows = 1 << rowVars;
    int cols = 1 << colVars;
   
    g.setFont(HEAD_FONT);
    FontMetrics headFm = g.getFontMetrics();
    String rowHeader = header(0, rowVars);
    String colHeader = header(rowVars, rowVars + colVars);
    int xoffs = (tableWidth + headHeight + cellWidth - headFm.stringWidth(colHeader)) / 2;
    g.drawString(colHeader, x + xoffs, y + headFm.getAscent());
    int headerWidth = headFm.stringWidth(rowHeader);
    if (headerWidth <= headHeight) {
      int headX = x + (headHeight - headerWidth) / 2;
      int headY = y + (tableHeight + headHeight + cellHeight + headFm.getAscent()) / 2;
      g.drawString(rowHeader, headX, headY);
    } else if (g instanceof Graphics2D){
      Graphics2D g2 = (Graphics2D) g.create();
      int yoffs = (tableHeight + headHeight + cellHeight + headerWidth) / 2;
      int headX = x + headFm.getAscent();
      int headY = y + yoffs;
      g2.rotate(-Math.PI / 2.0);
      g2.drawString(rowHeader, -headY, headX);
      g2.dispose();
    }
   
    x += headHeight;
    y += headHeight;
    g.setFont(BODY_FONT);
    FontMetrics fm = g.getFontMetrics();
    int dy = (cellHeight + fm.getAscent()) / 2;
    for (int i = 0; i < cols; i++) {
      String label = label(i, cols);
      g.drawString(label,
        x + (i + 1) * cellWidth + (cellWidth - fm.stringWidth(label)) / 2,
        y + dy);
    }
    for (int i = 0; i < rows; i++) {
      String label = label(i, rows);
      g.drawString(label,
        x + (cellWidth - fm.stringWidth(label)) / 2,
        y + (i + 1) * cellHeight + dy);
    }
   
    int outputColumn = table.getOutputIndex(output);
    x += cellWidth;
    y += cellHeight;
    g.setColor(ERROR_COLOR);
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        int row = getTableRow(i, j, rows, cols);
        Entry entry = table.getOutputEntry(row, outputColumn);
        if (provisionalValue != null && row == provisionalY
            && outputColumn == provisionalX) entry = provisionalValue;
        if (entry.isError()) {
          g.fillRect(x + j * cellWidth, y + i * cellHeight, cellWidth, cellHeight);
        }
      }
    }
   
    List<Implicant> implicants = model.getOutputExpressions().getMinimalImplicants(output);
    if (implicants != null) {
      int index = 0;
      for (Implicant imp : implicants) {
        g.setColor(IMP_COLORS[index % IMP_COLORS.length]);
        paintImplicant(g, imp, x, y, rows, cols);
        index++;
      }
    }
   
    g.setColor(Color.GRAY);
    if (cols > 1 || inputCount == 0) g.drawLine(x, y, left + tableWidth, y);
    if (rows > 1 || inputCount == 0) g.drawLine(x, y, x, top + tableHeight);
    if (outputColumn < 0) return;
   
    g.setColor(Color.BLACK);
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        int row = getTableRow(i, j, rows, cols);
        if (provisionalValue != null && row == provisionalY
            && outputColumn == provisionalX) {
          String text = provisionalValue.getDescription();
          g.setColor(Color.GREEN);
          g.drawString(text,
              x + j * cellWidth + (cellWidth - fm.stringWidth(text)) / 2,
              y + i * cellHeight + dy);
          g.setColor(Color.BLACK);
        } else {
          Entry entry = table.getOutputEntry(row, outputColumn);
          String text = entry.getDescription();
          g.drawString(text,
              x + j * cellWidth + (cellWidth - fm.stringWidth(text)) / 2,
              y + i * cellHeight + dy);
        }
      }
    }
  }

  private void paintImplicant(Graphics g, Implicant imp, int x, int y,
      int rows, int cols) {
    int rowMax = -1;
    int rowMin = rows;
    int colMax = -1;
    int colMin = cols;
    boolean oneRowFound = false;
    int count = 0;
    for (Implicant sq : imp.getTerms()) {
      int tableRow = sq.getRow();
      int row = getRow(tableRow, rows, cols);
      int col = getCol(tableRow, rows, cols);
      if (row == 1) oneRowFound = true;
      if (row > rowMax) rowMax = row;
      if (row < rowMin) rowMin = row;
      if (col > colMax) colMax = col;
      if (col < colMin) colMin = col;
      ++count;
    }
   
    int numCols = colMax - colMin + 1;
    int numRows = rowMax - rowMin + 1;
    int covered = numCols * numRows;
    int d = 2 * IMP_RADIUS;
    if (covered == count) {
      g.fillRoundRect(x + colMin * cellWidth + IMP_INSET,
          y + rowMin * cellHeight + IMP_INSET,
          numCols * cellWidth - 2 * IMP_INSET,
          numRows * cellHeight - 2 * IMP_INSET,
          d, d);
    } else if (covered == 16) {
      if (count == 4) {
        int w = cellWidth - IMP_INSET;
        int h = cellHeight - IMP_INSET;
        int x1 = x + 3 * cellWidth + IMP_INSET;
        int y1 = y + 3 * cellHeight + IMP_INSET;
        g.fillRoundRect(x,  y,  w, h, d, d);
        g.fillRoundRect(x1, y,  w, h, d, d);
        g.fillRoundRect(x,  y1, w, h, d, d);
        g.fillRoundRect(x1, y1, w, h, d, d);
      } else if (oneRowFound) { // first and last columns
        int w = cellWidth - IMP_INSET;
        int h = 4 * cellHeight - 2 * IMP_INSET;
        int x1 = x + 3 * cellWidth + IMP_INSET;
        g.fillRoundRect(x,  y + IMP_INSET, w, h, d, d);
        g.fillRoundRect(x1, y + IMP_INSET, w, h, d, d);
      } else { // first and last rows
        int w = 4 * cellWidth - 2 * IMP_INSET;
        int h = cellHeight - IMP_INSET;
        int y1 = y + 3 * cellHeight + IMP_INSET;
        g.fillRoundRect(x + IMP_INSET, y,  w, h, d, d);
        g.fillRoundRect(x + IMP_INSET, y1, w, h, d, d);
      }
    } else if (numCols == 4) {
      int top = y + rowMin * cellHeight + IMP_INSET;
      int w = cellWidth - IMP_INSET;
      int h = numRows * cellHeight - 2 * IMP_INSET;
      // handle half going off left edge
      g.fillRoundRect(x, top, w, h, d, d);
      // handle half going off right edge
      g.fillRoundRect(x + 3 * cellWidth + IMP_INSET, top, w, h, d, d);
      /* This is the proper way, with no rounded rectangles along
       * the table's edge; but I found that the different regions were
       * liable to overlap, particularly the arcs with the rectangles.
       * (Plus, I was too lazy to figure this out for the 16 case.)
      int y0 = y + rowMin * cellHeight + IMP_INSET;
      int y1 = y + rowMax * cellHeight + cellHeight - IMP_INSET;
      int dy = y1 - y0;
      int x0 = x + cellWidth - IMP_INSET;
      int x1 = x + 3 * cellWidth + IMP_INSET;
     
      // half going off left edge
      g.fillRect(x,               y0, cellWidth - IMP_INSET - IMP_RADIUS, dy);
      g.fillRect(x0 - IMP_RADIUS, y0 + IMP_RADIUS, IMP_RADIUS, dy - d);
      g.fillArc(x0 - d, y0, d, d, 0, 90);
      g.fillArc(x0 - d, y1 - d, d, d, 0, -90);
     
      // half going off right edge
      g.fillRect(x1 + IMP_RADIUS, y0, cellWidth - IMP_INSET - IMP_RADIUS, dy);
      g.fillRect(x1, y0 + IMP_RADIUS, IMP_RADIUS, dy - d);
      g.fillArc(x1, y0, d, d, 180, 90);
      g.fillArc(x1, y1 - d, d, d, 180, -90);
      */
    } else { // numRows == 4
      int left = x + colMin * cellWidth + IMP_INSET;
      int w = numCols * cellWidth - 2 * IMP_INSET;
      int h = cellHeight - IMP_INSET;
      // handle half going off top edge
      g.fillRoundRect(left, y, w, h, d, d);
      // handle half going off right edge
      g.fillRoundRect(left, y + 3 * cellHeight + IMP_INSET, w, h, d, d);
    }
  }
 
  private String header(int start, int stop) {
    if (start >= stop) return "";
    VariableList inputs = model.getInputs();
    StringBuilder ret = new StringBuilder(inputs.get(start));
    for (int i = start + 1; i < stop; i++) {
      ret.append(", ");
      ret.append(inputs.get(i));
    }
    return ret.toString();
  }
 
  private String label(int row, int rows) {
    switch (rows) {
    case 2: return "" + row;
    case 4:
      switch (row) {
      case 0: return "00";
      case 1: return "01";
      case 2: return "11";
      case 3: return "10";
      }
    default: return "";
    }
  }
 
  private int getTableRow(int row, int col, int rows, int cols) {
    return toRow(row, rows) * cols + toRow(col, cols);
  }
 
  private int toRow(int row, int rows) {
    if (rows == 4) {
      switch (row) {
      case 2: return 3;
      case 3: return 2;
      default: return row;
      }
    } else {
      return row;
    }
  }
 
  private int getRow(int tableRow, int rows, int cols) {
    int ret = tableRow / cols;
    switch (ret) {
    case 2: return 3;
    case 3: return 2;
    default: return ret;
    }
  }
 
  private int getCol(int tableRow, int rows, int cols) {
    int ret = tableRow % cols;
    switch (ret) {
    case 2: return 3;
    case 3: return 2;
    default: return ret;
    }
  }
 
  private int computeMargin(int compDim, int tableDim) {
    int ret = (compDim - tableDim) / 2;
    return ret >= 0 ? ret : Math.max(-headHeight, compDim - tableDim);
  }

}
TOP

Related Classes of com.cburch.logisim.analyze.gui.KarnaughMapPanel$MyListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.