• category: swing folder: ClockWithArabicOrRomanNumerals title: AffineTransformを使用してアナログ時計の文字盤に数字を配置する tags: [AffineTransform, Font, TextLayout, TextAttribute, JPanel] tags: [AffineTransform, Font, TextLayout, TextAttribute, JPanel, Clock] author: aterai pubdate: 2022-08-22T00:18:25+09:00 description: AffineTransformを使用してアナログ時計の文字盤にアラビア数字やローマ数字を配置します。 image: https://drive.google.com/uc?id=1lJ0MWGTsDflFSRzI8m5spBQZ6Ls_RT1m hreflang:
       href: https://java-swing-tips.blogspot.com/2022/08/use-affinetransform-to-place-roman.html
       lang: en

概要

AffineTransformを使用してアナログ時計の文字盤にアラビア数字やローマ数字を配置します。

サンプルコード

#spandel
private void paintClockNumbers(Graphics2D g2, float radius, float hourMarkerLen) {
#spanend
#spanadd
private final String[] arabicNumerals = {
#spanend
  "12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"
#spanadd
};
#spanend
#spanadd
private final String[] romanNumerals = {
#spanend
  "XII", "I", "II", "III", "IIII", "V", "VI", "VII", "VIII", "IX", "X", "XI"
#spanadd
};
#spanend
#spanadd

#spanend
#spanadd
private void paintClockNumbers(
#spanend
    Graphics2D g2, double radius, double hourMarkerLen) {
  AffineTransform at = AffineTransform.getRotateInstance(0d);
  g2.setColor(Color.WHITE);
  Font font = g2.getFont();
  FontRenderContext frc = g2.getFontRenderContext();
  if (isRomanNumerals) {
    AffineTransform si = AffineTransform.getScaleInstance(1d, 2d);
    for (String txt : romanNumerals) {
      Shape s = moveTo12o(getOutline(txt, font, frc), radius, hourMarkerLen);
      g2.fill(at.createTransformedShape(s));
      Shape s = getTextLayout(txt, font, frc).getOutline(si);
      Rectangle2D r = s.getBounds2D();
      double tx = r.getCenterX();
      double ty = radius - hourMarkerLen - r.getHeight() + r.getCenterY() * .5;
      Shape t = AffineTransform.getTranslateInstance(-tx, -ty)
                               .createTransformedShape(s);
      g2.fill(at.createTransformedShape(t));
      at.rotate(Math.PI / 6d);
    }
  } else {
    Point2D ptSrc = new Point2D.Double();
    for (int i = 0; i < 12; i++) {
      String txt = i == 0 ? "12" : Objects.toString(i);
      Rectangle r = getOutline(txt, font, frc).getBounds();
      double ty = radius - hourMarkerLen - r.getHeight();
    for (String txt : arabicNumerals) {
      Shape s = getTextLayout(txt, font, frc).getOutline(null);
      Rectangle2D r = s.getBounds2D();
      double ty = radius - hourMarkerLen - r.getHeight() - r.getCenterY() * .5;
      ptSrc.setLocation(0d, -ty);
      Point2D pt = at.transform(ptSrc, null);
      double dx = pt.getX() - r.getCenterX();
      double dy = pt.getY() - r.getCenterY();
      g2.drawString(txt, (float) dx, (float) dy);
      // g2.drawString(txt, (float) dx, (float) dy);
      g2.fill(AffineTransform.getTranslateInstance(dx, dy)
                             .createTransformedShape(s));
      at.rotate(Math.PI / 6d);
    }
  }
}

#spandel
protected Shape moveTo12o(Shape s, double radius, double hourMarkerLen) {
#spanend
  Rectangle2D r = s.getBounds2D();
  double tx = r.getCenterX();
  double ty = radius - hourMarkerLen - r.getHeight() + r.getCenterY();
  return AffineTransform.getTranslateInstance(-tx, -ty).createTransformedShape(s);
#spanadd
private static TextLayout getTextLayout(
#spanend
    String txt, Font font, FontRenderContext frc) {
  return new TextLayout(txt, font, frc);
}
#spandel

#spanend
#spandel
private static Shape getOutline(String txt, Font font, FontRenderContext frc) {
#spanend
  return new TextLayout(txt, font, frc).getOutline(null);
#spandel
}
#spanend
View in GitHub: Java, Kotlin

解説

  • アラビア数字
    • Fontに負のトラッキング値(字送り、文字間)を設定して文字列幅を縮小
    • アラビア数字文字列からFontTextLayoutを使用して文字図形を取得
    • 時計の中心から12時の位置までPointを移動
    • 時計の中心を原点にプラス30°回転をAffineTransformに追加してPointを回転
    • Pointが文字図形の中心になるよう座標を調整してGraphics#drawString(...)メソッドで数字文字列を描画
    • 時計盤の中心から12時の位置までPointを移動
    • 時計盤の中心を原点にプラス30°回転をAffineTransformに追加してPointを回転
    • Pointが文字図形の中心になるよう座標を調整してGraphics#drawString(...)メソッドで数字文字列文字図形を描画
  • ローマ数字
    • Fontに負のトラッキング値(字送り、文字間)を設定して文字列幅を縮小
    • ローマ数字文字列からFontTextLayoutを使用して文字図形を取得
    • 時計の中心から12時の位置まで文字図形を移動
    • 時計の中心を原点にプラス30°回転をAffineTransformに追加して文字図形を回転
    • ローマ数字文字列からFontTextLayoutを使用して高さのみ200%拡大した文字図形を取得
    • 時計盤の中心から12時の位置まで文字図形を移動
    • 時計盤の中心を原点にプラス30°回転をAffineTransformに追加して文字図形を回転

参考リンク

コメント