---
category: swing
folder: ClockWithArabicOrRomanNumerals
title: AffineTransformを使用してアナログ時計の文字盤に数字を配置する
tags: [AffineTransform, Font, TextLayout, TextAttribute, JPanel]
author: aterai
pubdate: 2022-08-22T00:18:25+09:00
description: AffineTransformを使用してアナログ時計の文字盤にアラビア数字やローマ数字を配置します。
image: https://drive.google.com/uc?id=1lJ0MWGTsDflFSRzI8m5spBQZ6Ls_RT1m
---
* 概要 [#summary]
`AffineTransform`を使用してアナログ時計の文字盤にアラビア数字やローマ数字を配置します。

#download(https://drive.google.com/uc?id=1lJ0MWGTsDflFSRzI8m5spBQZ6Ls_RT1m)

* サンプルコード [#sourcecode]
#code(link){{
private void paintClockNumbers(Graphics2D g2, float radius, float hourMarkerLen) {
  AffineTransform at = AffineTransform.getRotateInstance(0d);
  g2.setColor(Color.WHITE);
  Font font = g2.getFont();
  FontRenderContext frc = g2.getFontRenderContext();
  if (isRomanNumerals) {
    for (String txt : romanNumerals) {
      Shape s = moveTo12o(getOutline(txt, font, frc), radius, hourMarkerLen);
      g2.fill(at.createTransformedShape(s));
      at.rotate(Math.PI / 6d);
    }
  } else {
    Point ptSrc = new Point();
    for (int i = 0; i < 12; i++) {
      double ty = radius - hourMarkerLen - font.getSize2D() * .6d;
      AffineTransform at2 = AffineTransform.getTranslateInstance(0d, -ty);
      Point2D pt = at.transform(at2.transform(ptSrc, null), null);
      String txt = i == 0 ? "12" : Objects.toString(i);
      Rectangle r = getOutline(txt, font, frc).getBounds();
      double dx = pt.getX() - r.getCenterX();
      double dy = pt.getY() - r.getCenterY();
      g2.drawString(txt, (float) dx, (float) dy);
      at.rotate(Math.PI / 6d);
    }
  }
}

protected Shape moveTo12o(Shape s, double radius, double tick) {
  Rectangle2D r = s.getBounds2D();
  double ty = radius - tick * 2d - r.getHeight();
  AffineTransform at = AffineTransform.getTranslateInstance(-r.getCenterX(), -ty);
  return at.createTransformedShape(s);
}

private static Shape getOutline(String txt, Font font, FontRenderContext frc) {
  return new TextLayout(txt, font, frc).getOutline(null);
}
}}

* 解説 [#explanation]
- アラビア数字
-- `Font`に負のトラッキング値(字送り、文字間)を設定して文字列幅を縮小
-- アラビア数字文字列から`Font`や`TextLayout`を使用して文字図形を取得
-- 時計の中心から`12`時の位置まで`Point`を移動
-- 時計の中心を原点にプラス`30°`回転を`AffineTransform`に追加して`Point`を回転
-- `Point`が文字図形の中心になるよう座標を調整して`Graphics#drawString(...)`メソッドで数字文字列を描画
- ローマ数字
-- `Font`に負のトラッキング値(字送り、文字間)を設定して文字列幅を縮小
-- ローマ数字文字列から`Font`や`TextLayout`を使用して文字図形を取得
-- 時計の中心から`12`時の位置まで文字図形を移動
-- 時計の中心を原点にプラス`30°`回転を`AffineTransform`に追加して文字図形を回転

* 参考リンク [#reference]
- [[Timerを使用してJPanelにアナログ時計の針を描画する>Swing/AnalogClock]]
- [[Fontにトラッキングを設定して文字列幅を詰めて描画する>Swing/Tracking]]

* コメント [#comment]
#comment
#comment