All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentcs.guing.common.components.drawing.Ruler Maven / Gradle / Ivy

There is a newer version: 6.2.0
Show newest version
/**
 * Copyright (c) The openTCS Authors.
 *
 * This program is free software and subject to the MIT license. (For details,
 * see the licensing information (LICENSE.txt) you should have received with
 * this copy of the software.)
 */
package org.opentcs.guing.common.components.drawing;

import static java.util.Objects.requireNonNull;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EventObject;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import org.jhotdraw.draw.DrawingView;
import org.opentcs.guing.common.components.drawing.course.Origin;
import org.opentcs.guing.common.components.drawing.course.OriginChangeListener;

/**
 * A ruler.
 */
public abstract class Ruler
    extends
      JComponent
    implements
      PropertyChangeListener,
      OriginChangeListener {

  /**
   * Size of the rulers (height of the horizontal ruler, width of the vertical).
   */
  private static final int SIZE = 25;
  /**
   * The standard translation of the drawing view. Not sure though why
   * it is -12.
   */
  private static final int STANDARD_TRANSLATION = -12;
  /**
   * The DrawingView.
   */
  protected final DrawingView drawingView;
  /**
   * The current scale factor.
   */
  protected double scaleFactor = 1.0;
  /**
   * The scale factor for the horizontal ruler.
   */
  protected double horizontalRulerScale = Origin.DEFAULT_SCALE;
  /**
   * The scale factor for the vertical ruler.
   */
  protected double verticalRulerScale = Origin.DEFAULT_SCALE;

  /**
   * Creates a new instance.
   *
   * @param drawingView The drawing view.
   */
  private Ruler(DrawingView drawingView) {
    this.drawingView = requireNonNull(drawingView, "drawingView");

  }

  /**
   * A horizontal ruler.
   */
  public static class Horizontal
      extends
        Ruler {

    /**
     * Creates a new instance.
     *
     * @param drawingView The drawing view.
     */
    public Horizontal(DrawingView drawingView) {
      super(drawingView);
    }

    /**
     * Sets a new width of the ruler and repaints it.
     *
     * @param preferredWidth The new width.
     */
    public void setPreferredWidth(int preferredWidth) {
      setPreferredSize(new Dimension(preferredWidth, SIZE));
      repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);

      Rectangle drawHere = g.getClipBounds();
      Point translation = new Point(
          (int) -drawingView.getDrawingToViewTransform().getTranslateX(),
          (int) -drawingView.getDrawingToViewTransform().getTranslateY()
      );
      // If we scroll right, the translation isn't incremented by default.
      // We use the translation of the visible rectangle instead.
      int visibleRectX = drawingView.getComponent().getVisibleRect().x + STANDARD_TRANSLATION;
      if (STANDARD_TRANSLATION == translation.x) {
        translation.x = visibleRectX;
      }

      Graphics2D g2d = (Graphics2D) g;
      g2d.setFont(new Font("Arial", Font.PLAIN, 10));
      // i translated
      int translated;
      // i normalized to decimal
      int draw;
      int drawOld = 0;
      // draw translated
      int drawTranslated;
      String translatedAsString;
      String lastIndex;

      // base line
      g2d.drawLine(
          0, SIZE - 1,
          getWidth(), SIZE - 1
      );

      for (int i = drawHere.x; i < getWidth(); i += 10) {
        translated = translateValue(i, translation);
        translatedAsString = Integer.toString(translated);
        lastIndex = translatedAsString.substring(translatedAsString.length() - 1);

        int decimal = Integer.parseInt(lastIndex);
        {
          // These steps are neccessary to guarantee lines are drawn
          // at every pixel. It always rounds i to a decimal, so the modulo
          // operators work
          draw = i;

          if (translated < 0) {
            draw += decimal;
          }
          else {
            draw -= decimal;
          }

          drawTranslated = translateValue(draw, translation);
          // draw has to be incremented by 1, otherwise the drawn lines
          // are wrong by 1 pixel
          draw++;
        }

        if (drawTranslated % (10 * scaleFactor) == 0) {
          g2d.drawLine(draw, SIZE - 1, draw, SIZE - 4);
        }

        if (drawTranslated % (50 * scaleFactor) == 0) {
          g2d.drawLine(draw, SIZE - 1, draw, SIZE - 7);
        }

        if (drawTranslated % (100 * scaleFactor) == 0) {
          g2d.drawLine(draw, SIZE - 1, draw, SIZE - 11);
          int value = (int) (drawTranslated / scaleFactor) * (int) horizontalRulerScale;
          String textValue = Integer.toString(value);
          if (scaleFactor < 0.06) {
            if (value % 5000 == 0) {
              g2d.drawString(textValue, value == 0 ? draw - 2 : draw - 8, 9);
            }
          }
          else if ((draw - drawOld) < 31) {
            if (value % 500 == 0) {
              g2d.drawString(textValue, value == 0 ? draw - 2 : draw - 8, 9);
            }
          }
          else {
            g2d.drawString(textValue, value == 0 ? draw - 2 : draw - 8, 9);
          }

          drawOld = draw;
        }
      }

    }

    /**
     * Returns a translated value, considering current translation of the view.
     *
     * @param i The value.
     * @return The translated value.
     */
    private int translateValue(int i, Point translation) {
      if (translation.x < 0) {
        return i + translation.x;
      }
      else {
        return i;
      }
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      if (evt.getPropertyName().equals("scaleFactor")) {
        scaleFactor = (double) evt.getNewValue();
        SwingUtilities.invokeLater(new Runnable() {

          @Override
          public void run() {
            setPreferredWidth(drawingView.getComponent().getWidth());
          }
        });

      }
    }

    @Override
    public void originLocationChanged(EventObject evt) {
    }

    @Override
    public void originScaleChanged(EventObject evt) {
      if (evt.getSource() instanceof Origin) {
        Origin origin = (Origin) evt.getSource();
        SwingUtilities.invokeLater(() -> {
          horizontalRulerScale = origin.getScaleX();
          repaint();
        });
      }
    }
  }

  /**
   * A vertical ruler.
   */
  public static class Vertical
      extends
        Ruler {

    /**
     * Creates a new instance.
     *
     * @param drawingView The drawing view.
     */
    public Vertical(DrawingView drawingView) {
      super(drawingView);
    }

    /**
     * Sets a new height of the ruler and repaints it.
     *
     * @param preferredHeight The new height.
     */
    public void setPreferredHeight(int preferredHeight) {
      setPreferredSize(new Dimension(SIZE, preferredHeight));
      repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
      super.paintComponent(g);

      Rectangle drawHere = g.getClipBounds();
      Point translation = new Point(
          (int) -drawingView.getDrawingToViewTransform().getTranslateX(),
          (int) -drawingView.getDrawingToViewTransform().getTranslateY()
      );
      // If we scroll downwards, the translation isn't incremented by default.
      // We use the translation of the visible rectangle instead.
      if (translation.y == STANDARD_TRANSLATION) {
        translation.y = drawingView.getComponent().getVisibleRect().y + STANDARD_TRANSLATION;
      }

      Graphics2D g2d = (Graphics2D) g;
      g2d.setFont(new Font("Arial", Font.PLAIN, 10));
      // i translated
      int translated;
      // i normalized to decimal
      int draw;
      int drawOld = 0;
      // draw translated
      int drawTranslated;
      String translatedAsString;
      String lastIndex;

      // base line
      g2d.drawLine(
          SIZE - 1, 0,
          SIZE - 1, getHeight()
      );

      // Rotate the font for vertical axis
      AffineTransform fontAT = new AffineTransform();
      fontAT.rotate(270 * java.lang.Math.PI / 180);
      Font font = g2d.getFont().deriveFont(fontAT);
      g2d.setFont(font);

      for (int i = drawHere.y; i < getHeight(); i += 10) {
        translated = translateValue(i, translation);
        translatedAsString = Integer.toString(translated);
        lastIndex = translatedAsString.substring(translatedAsString.length() - 1);
        int decimal = Integer.parseInt(lastIndex);

        {
          // These steps are neccessary to guarantee lines are drawn
          // at every pixel. It always rounds i to a decimal, so the modulo
          // operators work
          draw = i;

          if (translated < 0) {
            draw += decimal;
          }
          else {
            draw -= decimal;
          }

          drawTranslated = translateValue(draw, translation);
          draw++;
        }

        if (drawTranslated % (10 * scaleFactor) == 0) {
          g2d.drawLine(SIZE - 1, draw, SIZE - 4, draw);
        }

        if (drawTranslated % (50 * scaleFactor) == 0) {
          g2d.drawLine(SIZE - 1, draw, SIZE - 7, draw);
        }

        if (drawTranslated % (100 * scaleFactor) == 0) {
          g2d.drawLine(SIZE - 1, draw, SIZE - 11, draw);
          int value = -(int) (drawTranslated / scaleFactor) * (int) verticalRulerScale;
          String textValue = Integer.toString(value);

          if (scaleFactor < 0.06) {
            if (value % 5000 == 0) {
              g2d.drawString(textValue, 9, value == 0 ? draw + 2 : draw + 8);
            }
          }
          else if ((draw - drawOld) < 31) {
            if (value % 500 == 0) {
              g2d.drawString(textValue, 9, value == 0 ? draw + 2 : draw + 8);
            }
          }
          else {
            g2d.drawString(textValue, 9, value == 0 ? draw + 2 : draw + 8);
          }

          drawOld = draw;
        }
      }
    }

    /**
     * Returns a translated value, considering current translation of the view.
     *
     * @param i The value.
     * @return The translated value.
     */
    private int translateValue(int i, Point translation) {
      if (translation.y < 0) {
        return i + translation.y;
      }
      else {
        return i;
      }
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      if (evt.getPropertyName().equals("scaleFactor")) {
        scaleFactor = (double) evt.getNewValue();
        SwingUtilities.invokeLater(new Runnable() {

          @Override
          public void run() {
            setPreferredHeight(drawingView.getComponent().getHeight());
          }
        });

      }
    }

    @Override
    public void originLocationChanged(EventObject evt) {
    }

    @Override
    public void originScaleChanged(EventObject evt) {
      if (evt.getSource() instanceof Origin) {
        Origin origin = (Origin) evt.getSource();
        SwingUtilities.invokeLater(() -> {
          verticalRulerScale = origin.getScaleY();
          repaint();
        });
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy