Delphi 逆ポーランド記法 四則計算機 関数部ソースコード ― 2022年08月12日 17:53
unit ReversePolish;
interface
uses
  SysUtils, Classes;
  procedure Infix2RP(StIn, StOut: TStringList);
  function CalcRPN(St: TStringList): Currency;
implementation
//----------------------------------------------------------------------------
function Priority(S: String): Integer;
begin
  S := Trim(S);
  if S = '=' then
    Result := 0
  else if S = ')' then
    Result := 1
  else if S = '+' then
    Result := 2
  else if S = '-' then
    Result := 2
  else if S = '*' then
    Result := 3
  else if S = '/' then
    Result := 3
  else if S = '(' then
    Result := 4
  else
    Result := 5;
end;
//----------------------------------------------------------------------------
//  変換処理
//             StIn : 中置記法          A = ( B - C ) / D + E * F
//             StOut: 逆ポーランド記法  A B C - D / E F * + =
//----------------------------------------------------------------------------
procedure Infix2RP(StIn, StOut: TStringList);
var
  StStack: TStringList;
  i: Integer;
  Token: String;
begin
  StStack := TStringList.Create;
  try
    for i := 0 to StIn.Count - 1 do
    begin
      Token := Trim(StIn[i]);
      if Length(Token) = 0 then
        Continue;
      while (StStack.Count <> 0) and
      (StStack[StStack.Count - 1] <> '(') and
      (Priority(Token) <= Priority(StStack[StStack.Count - 1])) do
      begin
        if (StStack[StStack.Count - 1] <> '(') and (StStack[StStack.Count - 1]
<> ')') then
          StOut.Add(StStack[StStack.Count - 1]);
        StStack.Delete(StStack.Count - 1);
      end;
      if Token <> ')' then
      begin
        StStack.Add(Token);
      end
      else
      begin
        StStack.Delete(StStack.Count - 1);
      end;
    end;
    while (StStack.Count <> 0) do
    begin
      if (StStack[StStack.Count - 1] <> '(') and (StStack[StStack.Count - 1]
<> ')') then
        StOut.Add(StStack[StStack.Count - 1]);
      StStack.Delete(StStack.Count - 1);
    end;
  finally
    StStack.Free;
  end;
end;
//----------------------------------------------------------------------------
//  逆ポーランド記法の計算式を計算
//----------------------------------------------------------------------------
function CalcRPN(St: TStringList): Currency;
var
  StStack: TStringList;
  i: Integer;
  wkC, op1, op2: Currency;
begin
  StStack := TStringList.Create;
  try
    for i := 0 to St.Count - 1 do
    begin
      try
        wkC := StrToFloat(St[i]);
        StStack.Add(St[i]);
      except
        begin
          op2 := StrToFloat(StStack[StStack.Count - 1]);
          StStack.Delete(StStack.Count - 1);
          op1 := StrToFloat(StStack[StStack.Count - 1]);
          StStack.Delete(StStack.Count - 1);
          if St[i] = '/' then
            wkC := op1 / op2
          else if St[i] = '*' then
            wkC := op1 * op2
          else if St[i] = '-' then
            wkC := op1 - op2
          else if St[i] = '+' then
            wkC := op1 + op2;
          StStack.Add(FloatToStr(wkC));
        end;
      end;
    end;
    Result := StrToFloat(StStack[StStack.Count - 1]);
  finally
    StStack.Free;
  end;
end;
end.
Delphi 逆ポーランド記法 四則計算機 本体ソースコード ― 2022年08月12日 17:55
unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Memo2: TMemo;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Memo3: TMemo;
    edMoto: TEdit;
    btnMotoHen: TButton;
    btnPora: TButton;
    edPora: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure btnMotoHenClick(Sender: TObject);
    procedure btnPoraClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;
var
  Form1: TForm1;
implementation
uses
  ReversePolish;
{$R *.dfm}
procedure TForm1.btnMotoHenClick(Sender: TObject);
var
  i,j,iBan,naga:integer;
  sdmy,sdmyS:string;
begin
  memo1.Lines.Clear;
  naga := Length(edMoto.Text);
  sdmyS := '';
  j := 0;
  for iBan := 1 to naga do begin
    sdmy := Copy(edMoto.Text,iBan,1);
    //showmessage('naga='+intToStr(naga)+','+'iBan='+intToStr(iBan)+','+sdmy+'END');
    if sdmy = ' ' then begin
      memo1.Lines.Add(sdmyS);
      sdmyS := '';
      j := 0;
    end;
    if sdmy <> ' ' then begin
      inc(j);
      insert(sdmy,sdmyS,j);
      //showmessage('naga='+intToStr(naga)+','+'iBan='+intToStr(iBan)+','+sdmy+',j='+intToStr(j)+','+sdmyS);
    end;
  end;
  memo1.Lines.Add(sdmyS);
end;
procedure TForm1.btnPoraClick(Sender: TObject);
var
  sdmy,sdmyS:string;
  i,j,gyou:integer;
begin
  edPora.Text := '';
  gyou := memo2.Lines.Count;
  //showmessage('gyou='+inttostr(gyou));
  j := -1;
  sdmyS := '';
  for i := 1 to gyou do begin
    j := j + 2;
    sdmy := memo2.Lines[i-1]+' ';
    edPora.Text := edPora.text + sdmy;
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin //変換
  Memo2.Lines.Clear;
  Infix2RP(TStringList(Memo1.Lines), TStringList(Memo2.Lines));
end;
procedure TForm1.Button2Click(Sender: TObject);
begin //計算
  Edit1.Text := '';
  Edit1.Text := FloatToStr(CalcRPN(TStringList(Memo2.Lines)));
end;
end.
C# 逆ポーランド記法 四則計算機 ソースコード ― 2022年08月12日 18:05
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public struct nameSet // 実行用スタックの要素
        {   public string name;            // 変数名,評価済みのとき"*VALUE*"
            public double value;            // 評価済み値
            public nameSet(string n, double v) { name = n; value = v;}
        }
        public struct opeSet // 逆ポーランド変換用スタックの要素
        {   public string name;             // 演算子
            public int value;               // 優先順位
            public opeSet(string n, int v) { name = n; value = v;}
        }
         public struct token // 単一トークンの形式
         {   public string atr;               // トークンの種類"NUM","OPE","VAR"
             public string str;               // トークンの文字列
             public token(string a, string s) { atr = a.ToUpper(); str=s;}
         }
        public nameSet[] STab=new nameSet[500];    // 記号表
        public int numStab;                        // 記号表のサイズ
        public token[] Polish = new token[500];    // 逆ポーランド記法の出力先
        public int numPolish;                      // 逆ポーランド記法のサイズ
        public nameSet[] Stack = new nameSet[500]; // 実行用スタック
        public int stackP;                         // スタックポインタ
        public opeSet[] StackOpe = new opeSet[500];// 逆ポーランド変換用スタック
        public string Alltext;                     // 入力文字列
        public string Delmiter = "()+-*/=";        // 区切り記号(演算子)
        public string Number = "0123456789";       // 数字
        public token[] LAData = new token[500];    // 語彙解析結果
        public int numLA;                          //  語彙解析結果のサイズ
        public Form1()
        {   InitializeComponent();
        }
        private void push(nameSet X)  // 実行用スタック Push
        {   if (stackP >= 500) MessageBox.Show("Stack Over");
            else
            {   Stack[stackP] = X;
                if(checkBox1.Checked)MessageBox.Show("push(" + stackP.ToString() + ")" +
                                                X.name + " Value=" + X.value.ToString());
                stackP++;
            }
        }
        private nameSet pop()           // 実行用スタック Pop
        {   if (stackP <= 0)
            {   MessageBox.Show("Stack Empty"); return new nameSet("*Error*", 0);}
            else
            {   stackP--;nameSet X = Stack[stackP];
                if (checkBox1.Checked) MessageBox.Show("pop(" + stackP.ToString() + ")" +
                                                  X.name +" Value=" + X.value.ToString());
                return X;
            }
        }
        private void pushOpe(opeSet X)     // 変換用 Push
        {   if (stackP >= 500) MessageBox.Show("Stack Over");
            else
            {   StackOpe[stackP] = X;
                if (checkBox1.Checked) MessageBox.Show("push(" + stackP.ToString() + ")" + 
                                               X.name +" Priority=" + X.value.ToString());
                stackP++;
            }
        }
        private opeSet popOpe()            // 変換用 Pop
        {   if (stackP <= 0)
            {   MessageBox.Show("Stack Empty"); return new opeSet("*Error*", 0);}
            else
            {   stackP--; opeSet X=StackOpe[stackP];
                if (checkBox1.Checked) MessageBox.Show("pop(" + stackP.ToString() + ")" + 
                                              X.name +" Priority=" + X.value.ToString());
                return X;
            }
        }
        private nameSet sepValue(string X)// TextBoxの変数名/値分離
        {   string[] S = new string[2]; S=X.Split('=');
            return new nameSet(S[0].Trim(' '),double.Parse(S[1]));
        }
        private token setPolish(string X) // TextBoxのトークン設定
        {   string[] S = new string[2]; S = X.Split(':');
            return new token(S[0].Trim(' '), S[1].Trim(' '));
        }
        private void setValue(string Name, double V)//変数値の設定
        {   STab[numStab].name=Name;
            int i=0; while (STab[i].name!=Name) i++;
            STab[i].value = V; if (i >= numStab) numStab++;
            dspName();
        }
        private double getValue(string Name) // 与えられた変数の値
        {   for (int i = 0; i < STab.Length; i++)
                if (STab[i].name == Name) return STab[i].value;
            return 0;
        }
        private void outStab(string s)   // "変数名=値"のデータを記号表に登録
        {   STab[numStab] = sepValue(s); numStab++;  
        }
        private void outSPolish(string s) // "種類:記号"のデータをトークンとして設定
        {   Polish[numPolish] = setPolish(s); numPolish++;
        }
        private void initialize() // 実行用初期設定
        {   numStab = 0;  // 名前表
            for (int i = 0; i < textBoxSymbol.Lines.Length; i++)
                    if (textBoxSymbol.Lines[i].Length >= 2) outStab(textBoxSymbol.Lines[i]);
            numPolish = 0; // 逆ポーランド記法
            for (int i = 0; i < textBoxPolish.Lines.Length; i++)
                   if (textBoxPolish.Lines[i].Length >= 2) outSPolish(textBoxPolish.Lines[i]);
            stackP = 0; // スタックポインタ
        }
        private void outLAData(string s) // "種類:記号"のデータを語彙解析結果として設定
        {   LAData[numLA] = setPolish(s); numLA++;   
        }
        private void initializeSA() // 変換の初期設定
        {   numLA = 0;  // 語彙解析結果
            for (int i = 0; i < textBoxLA.Lines.Length; i++)
                if (textBoxLA.Lines[i].Length >= 2) outLAData(textBoxLA.Lines[i]);
            stackP = 0;
        }
        private double evalDT(nameSet X) // 値を求める
        {   if (X.name == "*VALUE*") return X.value;
            return getValue(X.name);
        }
        private void exePolish()  // 逆ポーランド記法の実行
        {   initialize();         //初期設定
            for (int i = 0; i < numPolish; i++)  //【注】変数Pushの場合、値が必要になった時点で評価。
            {   if(Polish[i].atr=="VAR") push(new nameSet(Polish[i].str, 0));//変数Push(ここでは値はダミー)
                else if (Polish[i].atr == "OPE")
            {       nameSet A1, A2;
                    if (checkBox1.Checked) MessageBox.Show("演算実行 " + Polish[i].str);  //演算実行
                    A2 = pop(); double V2 = evalDT(A2);       // 演算対象Popと評価
                    if (Polish[i].str == "$+") { }      // 単項演算子実行 $+
                    else if (Polish[i].str == "$-") V2 = -V2; //                $-
                    else
                    {   A1 = pop();                           // 演算対象Pop
                        if (Polish[i].str == "=")             //  代入処理
                        {   if (A1.name == "*VALUE*") MessageBox.Show("代入に矛盾があります");
                            else setValue(A1.name, V2);
                        }
                        else                                  // 以下2項演算
                        {   double V1 = evalDT(A1);
                            if (Polish[i].str == "+") V2 = V1 + V2;
                            else if (Polish[i].str == "-") V2 = V1 - V2;
                            else if (Polish[i].str == "*") V2 = V1 * V2;
                            else if (Polish[i].str == "/") V2 = V1 / V2;
                            else MessageBox.Show("演算子の例外");
                        }
                    }
                    push(new nameSet("*VALUE*", V2)); // 演算結果のPush
                }
                else push(new nameSet("*VALUE*", double.Parse(Polish[i].str)));// 数値のPush
             }
        }
        private void dspName() // 記号表をテキストボックスに表示
        {   string S = "";
            for(int i=0;i<numStab;i++)S=S+STab[i].name+"="+STab[i].value.ToString()+"\r\n";
            textBoxSymbol.Text=S;
        }
        private void button1_Click(object sender, EventArgs e) { exePolish();}
        private void startLA() // 語彙解析開始
        {   Alltext = textBoxInput.Text.TrimStart();
        }
        private token numberProc(string ch)// 数字の設定
        {
            Boolean dot = false;
            if (ch == ".") dot = true;
            string s = ch;
            if (Alltext != "")
            {   ch = Alltext.Substring(0, 1);
                while (ch=="." || Number.IndexOf(ch) > -1) // 数字のあいだ以下を繰り返す
                {   if (ch == ".")
                    {   if (dot) MessageBox.Show(".の位置の誤り");
                        dot = true;
                    } 
                    s += ch; Alltext = Alltext.Substring(1, Alltext.Length - 1);
                    if (Alltext == "") break;
                    ch = Alltext.Substring(0, 1);
                }
            }
            return (new token("Num", s));
        }
        private token nameProc(string ch)// 変数名の設定
        {   string s = ch;
            if (Alltext != "")
            {   ch = Alltext.Substring(0, 1);
                while (ch != " " && Delmiter.IndexOf(ch) <= -1)// 空白、区切り記号のいずれ
                {   s += ch;                                   // でもないあいだ繰り返す
                    Alltext = Alltext.Substring(1, Alltext.Length - 1);
                    if (Alltext == "") break;
                    ch = Alltext.Substring(0, 1);
                }
            }
            return (new token("Var", s));
        }
        private token getLAToken() // 語彙解析
        {   Alltext=Alltext.TrimStart();
            if (Alltext == "") return (new token("$EOF$", "$EOF$"));
            string ch = Alltext.Substring(0, 1);
            Alltext = Alltext.Substring(1, Alltext.Length - 1);
            if (Delmiter.IndexOf(ch) > -1) return (new token("Ope", ch));
            else if (ch=="." || Number.IndexOf(ch) > -1) return numberProc(ch);
            else return nameProc(ch);
        }
        private void dspPolish()//逆ポーランド記法をテキストボックスに表示
        {   string S = "";
            for (int i = 0; i < numPolish; i++)
                S += Polish[i].atr + ":" + Polish[i].str + "\r\n";
            textBoxPolish.Text = S;
        }
        private void button2_Click(object sender, EventArgs e)// 連続語彙解析
        {   startLA();  string S = ""; token TK = getLAToken();
            while (TK.atr != "$EOF$")
            {   string X = TK.atr + ":" + TK.str; S = S + X + "\r\n";
                if (checkBox1.Checked) MessageBox.Show(X);
                TK = getLAToken();
            }
            textBoxLA.Text = S;
        }
        private int priority(string s) // 演算子による優先順位の設定
        {   if (s == "=") return 10;
            else if (s == "+" || s == "-") return 20;
            else if (s == "*" || s == "/") return 30;
            else if (s == ")") return  -1;
            else return 0;           
        }
        private void singleOpe(string s) // 単項演算子と左カッコの処理
        {   if (s == "+" || s == "-")             // 2項演算子の+,-と区別するために
                pushOpe(new opeSet("$" + s, 200));// それぞれ$を先頭に付ける。 
            else if (s == "(")                    // 左括弧の場合Push
                pushOpe(new opeSet("(", 0)); 
            else MessageBox.Show("演算子エラー");
        }
        private int operandProc(token S) // 演算対象の場合、そのまま変換結果に移動
        {   Polish[numPolish] = S;
            numPolish++;
            return 1; // 次のモードを演算子モードにする。
        }
        private void rightParPop()// 右カッコの処理
        {   if (stackP > 0)       // 左カッコになるまでPopして変換結果に移動
            {   opeSet X = popOpe();
                while (X.name != "(" && stackP >= 0)
                {   Polish[numPolish] = new token("OPE", X.name); numPolish++; X = popOpe();
                }
            }
            else MessageBox.Show("左カッコが足りません");
        }
        private int operationProc(string s, int Opt)// 演算子処理
        {   if (stackP > 0)       // スタック上に優先順位が高いか、
            {   while (stackP>0)  // 同じ演算子があればPopして変換結果に移動
                {   if (StackOpe[stackP-1].value < Opt) break;
                    opeSet X = popOpe(); Polish[numPolish] = new token("OPE", X.name); numPolish++;
                }
            }
            pushOpe(new opeSet(s, Opt));// 演算子をPush
            return 0; // 次のモードを演算対象モードとする。 
        }
        private void endPop()// スタック上に残った演算子を変換結果に移動。
        {   while (stackP > 0)
            {   opeSet X = popOpe();
                Polish[numPolish] = new token("OPE", X.name); numPolish++;
            }
        }
        private void SA()  // 逆ポーランドへの変換
        {   initializeSA(); int SMode = 0; numPolish = 0;
            for (int i = 0; i < numLA; i++)
            {   string ATR = LAData[i].atr.ToUpper();
                if (SMode == 0)  // 演算対象モード(演算子は、+,-(単項演算子)か左括弧)
                {   if (ATR == "OPE") singleOpe(LAData[i].str);// 単項演算子と左括弧の処理
                    else SMode = operandProc(LAData[i]);       //演算対象処理
                }
                else // 演算子モード(2項演算子か右括弧だけのモード)
                {   if (ATR == "OPE")
                    {   int Opt = priority(LAData[i].str);             // 演算子の優先順位設定
                        if (Opt == 0) MessageBox.Show("演算子エラー"); // (0:エラー,-1:右括弧)
                        else if (Opt < 0) rightParPop();               // 右括弧の処理
                        else SMode = operationProc(LAData[i].str, Opt);// 2項演算子処理
                    }
                    else MessageBox.Show("演算子がありません");
                }
            }
            endPop(); dspPolish(); // スタックに残った演算子を変換結果に移して結果表示
        }
        private void button3_Click(object sender, EventArgs e)
        {   SA();
        }
    }
}
Java アナログ時計 ソースコード ― 2022年08月17日 10:09
// -*- mode:java; encoding:utf-8 -*-
// vim:set fileencoding=utf-8:
// https://ateraimemo.com/Swing/AnalogClock.html
//package example;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.time.LocalTime;
import java.time.ZoneId;
import javax.swing.*;
public final class MainPanel extends JPanel {
  private MainPanel() {
    super(new BorderLayout());
    add(new AnalogClock());
    setPreferredSize(new Dimension(250, 250));
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(MainPanel::createAndShowGui);
  }
  private static void createAndShowGui() {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
      ex.printStackTrace();
      Toolkit.getDefaultToolkit().beep();
    }
    JFrame frame = new JFrame("AnalogClock");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new MainPanel());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}
class AnalogClock extends JPanel {
  protected LocalTime time;// = LocalTime.now(ZoneId.systemDefault());
  protected AnalogClock() {
    super();
    new Timer(100, e -> {
      time = LocalTime.now(ZoneId.systemDefault());
      repaint();
    }).start();
  }
  @Override protected void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    Rectangle rect = SwingUtilities.calculateInnerArea(this, null);
    g2.setColor(Color.BLACK);
    g2.fill(rect);
    float radius = Math.min(rect.width, rect.height) / 2f - 10f;
    // g2.fill(new Ellipse2D.Double(rect.getCenterX() - radius, rect.getCenterY() - radius, radius * 2f, radius * 2f));
    g2.translate(rect.getCenterX(), rect.getCenterY());
    // Drawing the hour markers
    float hourMarkerLen = radius / 6f - 10f;
    Shape hourMarker = new Line2D.Float(0f, hourMarkerLen - radius, 0f, -radius);
    Shape minuteMarker = new Line2D.Float(0f, hourMarkerLen / 2f - radius, 0f, -radius);
    AffineTransform at = AffineTransform.getRotateInstance(0d);
    g2.setStroke(new BasicStroke(2f));
    g2.setColor(Color.WHITE);
    for (int i = 0; i < 60; i++) {
      if (i % 5 == 0) {
        g2.draw(at.createTransformedShape(hourMarker));
      } else {
        g2.draw(at.createTransformedShape(minuteMarker));
      }
      at.rotate(Math.PI / 30d);
    }
    //文字12 3 6 9 rectは横250,縦250 たてよこのセンターが原点0,0
    Font font = new Font("Arial", Font.BOLD, 14);
    g2.setFont(font);
    g2.drawString("12", radius * 0.75f * (int)Math.cos(Math.PI / 2f) - 7f, -radius * 0.75f * (int)Math.sin(Math.PI / 2f));
    g2.drawString("3", radius * 0.75f * (int)Math.cos(0f), -radius * 0.75f * (int)Math.sin(0f) + 7f);
    g2.drawString("6", radius * 0.75f * (int)Math.cos(-Math.PI / 2f) - 7f, -radius * 0.75f * (int)Math.sin(-Math.PI / 2f) + 7f);
    g2.drawString("9", radius * 0.75f * (int)Math.cos(Math.PI) - 7f, -radius * 0.75f * (int)Math.sin(Math.PI) + 7f);
    double miriRot = time.getNano() * Math.PI / 30000000000d;//OK!
    double miriSecondRot = time.getSecond() * Math.PI / 30d + miriRot;
    double secondRot = time.getSecond() * Math.PI / 30d;
    double minuteRot = time.getMinute() * Math.PI / 30d + secondRot / 60d;
    // double hourRot = time.getHour() * Math.PI * 2d / 12d + time.getMinute() * Math.PI * 2d / (12d * 60d);
    double hourRot = time.getHour() * Math.PI / 6d + minuteRot / 12d;
    // Drawing the hour hand
    float hourHandLen = radius / 1.5f;
    Shape hourHand = new Line2D.Float(0f, 0f, 0f, -hourHandLen);
    g2.setStroke(new BasicStroke(8f));
    g2.setPaint(Color.cyan);    //LIGHT_GRAY);
    g2.draw(AffineTransform.getRotateInstance(hourRot).createTransformedShape(hourHand));
    // Drawing the minute hand
    float minuteHandLen = 5f * radius / 6f;
    Shape minuteHand = new Line2D.Float(0f, 0f, 0f, -minuteHandLen);
    g2.setStroke(new BasicStroke(4f));
    g2.setPaint(Color.green);  //WHITE);
    g2.draw(AffineTransform.getRotateInstance(minuteRot).createTransformedShape(minuteHand));
    // Drawing the second hand
    float r = radius / 6f;
    float secondHandLen = radius - r;
    Shape secondHand = new Line2D.Float(0f, r, 0f, -secondHandLen);
    g2.setPaint(Color.RED);
    g2.setStroke(new BasicStroke(1f));
    g2.draw(AffineTransform.getRotateInstance(miriSecondRot).createTransformedShape(secondHand));
    g2.fill(new Ellipse2D.Float(-r / 4f, -r / 4f, r / 2f, r / 2f));
    g2.dispose();
  }
}
C# WPF アナログ時計 ソースコード ― 2022年08月17日 10:13
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;
using System.Windows.Threading;
using System.Diagnostics;
////////////////////////////////////////できたぞ!////////////////////////////////////////
namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        readonly DispatcherTimer timer = new DispatcherTimer();
        private DispatcherTimer _timer;
        Brush[] _brush = new Brush[3];
        int[] _futo = new int[3];
        Boolean timerStart = false;
        BitmapImage img = new BitmapImage(new Uri("img/banmen.png", UriKind.Relative));
        ImageBrush myImageBrush; //= new ImageBrush(img);
        string jikan;
        double fun, byo, miriByo;
        const int BYO = 0;
        const int FUN = 1;
        const int JIK = 2;
        const int BYO_NAGA2 = 20;
        const int BYO_NAGA = 90;
        const int FUN_NAGA = 80;
        const int JIK_NAGA = 60;
        const int BYO_360 = 6; //秒 6 は 360 / 6 = 60秒
        const int FUN_360 = 6; //分 6 は 360 / 6 = 60分
        const int JIK_360 = 30; //時 30 は 360 / 30 = 12時間
        const int BYO_FUTO = 1;
        const int FUN_FUTO = 2;
        const int JIK_FUTO = 4;
        public MainWindow()
        {
            InitializeComponent();
            timer.Interval = new TimeSpan(0, 0, 0, 0, 100);    //インターバルを100ミリ秒に設定 日,時,分,秒,ミリ秒
            timer.Tick += new EventHandler(TimerMethod);  //インターバル毎に発生するイベントを設定
            //Topmost = true;                               //最画面表示(このプロパティはなくても良い)
            _brush[BYO] = System.Windows.Media.Brushes.Red;
            _brush[FUN] = System.Windows.Media.Brushes.Blue;
            _brush[JIK] = System.Windows.Media.Brushes.Black;
            _futo[BYO] = BYO_FUTO;
            _futo[FUN] = FUN_FUTO;
            _futo[JIK] = JIK_FUTO;
            myImageBrush = new ImageBrush(img);
            timerStart = true;
            timer.Start();
        }
        private void TimerMethod(object sender, EventArgs e)
        {
            myTimerMethod();
        }
        private Image CreateImage(double x, double y, double width, double height, Stretch stretch)
        {// Imageオブジェクトの生成
            Image image = new Image();
            image.Source = new BitmapImage(new Uri("img/banmen.png", UriKind.Relative));
            Canvas.SetLeft(image, 0);
            Canvas.SetTop(image, 0);
            image.Width = 200;
            image.Height = 200;
            image.Stretch = stretch;
            return image;
        }
        private ContentControl CreateText(string text, double fontSize, Brush brush, double x, double y, double widh, double height, HorizontalAlignment hAlign, VerticalAlignment vAlign)
        {// TextBlockオブジェクトの生成(配置を指定するためのContentControlでラップする)
            ContentControl content = new ContentControl();
            Canvas.SetLeft(content, x);
            Canvas.SetTop(content, y);
            content.Width = widh;
            content.Height = height;
            TextBlock tb = new TextBlock();
            tb.Text = text;
            tb.FontSize = fontSize;
            tb.Foreground = brush;
            tb.HorizontalAlignment = hAlign;
            tb.VerticalAlignment = vAlign;
            content.Content = tb;
            return content;
        }
        private void myTimerMethod()
        {
            if (timerStart)
            {                
                DateTime d = DateTime.Now;
                miriByo = d.Millisecond / 1000.0;
                byo = d.Second / 60.0; //C#の実数割り算はどちらかに.0をつけること!
                fun = d.Minute / 60.0;
                canv01.Children.Clear();
                jikan = d.ToLongTimeString();
                canv01.Children.Add(CreateImage(0, 0, 200, 200, Stretch.Fill));
                canv01.Children.Add(CreateText(jikan, 16.0d, Brushes.Black, 65.0d, 110.0d, 200.0d, 30.0d, HorizontalAlignment.Left, VerticalAlignment.Center));
                //MOTOmydraw(BYO_360, BYO, d.Second, BYO_NAGA);
                mydraw(BYO_360, BYO, d.Second + miriByo, BYO_NAGA);
                mydraw(BYO_360, BYO, d.Second + miriByo + 30, BYO_NAGA2); //秒針の中心より反対側を描く
                mydraw(FUN_360, FUN, d.Minute + byo, FUN_NAGA);
                mydraw(JIK_360, JIK, d.Hour + fun, JIK_NAGA);
            }
        }
        private void mydraw(int bairitu, int ban, double time, int harinaga)
        {            
            int cx = 200 / 2;//中心点
            int cy = 200 / 2;            
            int x1 = cx;//原点
            int y1 = cy;            
            double kakudotani = Math.PI / 180;//針
            double kakudo = kakudotani * bairitu * time - Math.PI / 2;
            int x2 = cx + (int)(Math.Cos(kakudo) * harinaga);
            int y2 = cy + (int)(Math.Sin(kakudo) * harinaga);
            Line myLine = new Line();
            myLine.Stroke = _brush[ban];//System.Windows.Media.Brushes.LightSteelBlue;
            myLine.X1 = x1;
            myLine.X2 = x2;
            myLine.Y1 = y1;
            myLine.Y2 = y2;
            myLine.HorizontalAlignment = HorizontalAlignment.Left;
            myLine.VerticalAlignment = VerticalAlignment.Center;
            myLine.StrokeThickness = _futo[ban];
            Canvas.SetLeft(myLine, 0);
            Canvas.SetTop(myLine, 0);
            canv01.Children.Add(myLine);
        }
        private void btn02_Click(object sender, RoutedEventArgs e)
        {//私がVisibilityをHiddenにしているぞ
            MessageBox.Show("ボタン2がクリックされました。timerStart");
            myImageBrush = new ImageBrush(img);
            timerStart = true;
            timer.Start();//ストップウォッチ開始
        }
    }
}


