そもそも前回の矢印は、形状定義時の原点が以下の位置になっているわけです。

テン・シー・シー-fig.1

 回転は原点中心なので、当然矢印の付け根での回転にはならずに、こうなっちゃう。

テン・シー・シー-fig.2

 なので、この回転でずれる分を、後からまた平行移動してやれば、つじつまが合うというわけで、このためにCGContextConvertPointToDeviceSpaceを使うわけです。
 こいつは、受け取った座標を、現在のCTMを適用した座標に変換してくれるので、(50, -20)という、本来矢印の回転軸にしたい座標を渡して、CTM適用後の座標をもらい、この座標が(0、0)になるようにしているのが

CGContextTranslateCTM(inContext, -origin.x , -origin.y);

だったわけですわ。

テン・シー・シー-fig.3

 ということで、今日の本題、クリック座標を元に0.0~1.0への変換。
 いよいよ、指定された扇の角度の範囲で割り出した角度をクリップして、0.0~1.0に変換するようにします。まずはクリップする角度の範囲をどうやって割り出すか?
 ちょっとy軸の向きが逆なんでとまどうけど、角度はx軸の増分方向を0として反時計回りに増加するので、図のような角度120度の扇を考えると30度から150度の角度が領域内と考えたらいいことになる。

テン・シー・シー-fig.4

 つまり

開始角 = (180度 - 扇の角度) / 2
終了角 = 開始角 + 扇の角度

 この範囲でクリップすればいい(ただしacos関数の返り値aはラジアン角なんで、ソースはラジアン角での計算となる)。

double minAngle = (3.14 - angle) / 2;
double maxAngle = minAngle + angle;
if (a < minAngle)
a = minAngle;
if (a > maxAngle)
a = maxAngle;

で、クリップされたaをminAngleからの変化量にするため

a = a - minAngle;

とし、これをangle内での0.0~1.0の変化に正規化。

a = a / angle;

 ただし、レベルメータは左から時計回りに増加するデザインにしているので、増分方向が逆なので

a = 1.0 - a;

 これで、時計回り増分で0.0~1.0を返すようになるので、

pointToNumber

に実装する。あとy座標がcenter.yより大きい場合はクリックx座標がcenter.xより大きければ1.0、小さければ0.0を入れて完成。

 それと当然だけど、pointToNumber側が0.0~1.0を返すようになったので、draw側も対応する必要がある。これは、上の計算を逆にするだけ。受け取ったinNumberから

double minAngle = (3.14 - angle) / 2;
double maxAngle = minAngle + angle;
double a = (maxAngle - (angle * (1.0 - inNumber))) - (3.14 / 2);

とし、このaを前回の処理

CGContextRotateCTM(inContext, 3.14 / 2 - inNumber);

で使っていた3.14 / 2 - inNumberの代わりに使えばいい。上の計算はminAngle、maxAngleを省略したもう少し単純なものにできるけど、それは勝手にやってちょ。

文字盤の追加
 でもって、いよいよhelloCTM-5でやった文字盤表示を追加。まずは

static void drawString(CGContextRef context, int inValue)
{


double targetNum = 2.0; // 拡大する番号
double radius = 100.0; // 半径


}

を引数に移動して

static void drawString(
CGContextRef context, int inValue, double radius,
double targetNum)

それぞれの定義を削除する。

double targetNum = 2.0; → 削除
double radius = 100.0; → 削除

次に

- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext() ;
CGContextTranslateCTM(context, rect.size.width / 2, rect.size.height / 2);
CGContextRotateCTM(context, -90.0 / 360.0 * (M_PI * 2.0));
drawString(context, 1);
for (int i = 1; i < 10; i++) {
CGContextRotateCTM(context, (180.0 / 9.0) / 360.0 * (M_PI * 2.0));
drawString(context, i + 1);
}
}

をLevelMeter.mで利用できるように

- (void)drawRect:(CGRect)rect



static void drawNumbers(
CGContextRef context, CGPoint inCenter, double inRadius,
double inAngle, double targetNum)

とし、それぞれ

CGContextRef context = UIGraphicsGetCurrentContext() ; → 削除
rect.size.width / 2 → inCenter.x
rect.size.height / 2 → inCenter.y
-90.0 / 360.0 * (M_PI * 2.0) → -inAngle / 2.0
(180.0 / 9.0) / 360.0 * (M_PI * 2.0) → inAngle / 9.0

としLevelMeter.mに実装。
 このdrawNumbersで呼び出すdrawStringに新しく追加された引数targetNumは1~10の範囲で値を受け取るので

targetNum = (targetNum * 10.0) + 1.0;

とすることも忘れずに。これだと1.0の時、11.0になっちゃけど、drawStringに渡す値は拡大中心の指標でしか利用しないので特に問題はない。

テン・シー・シー-fig.5
 なかなか気持ちよい動きですな。

------------
サンプルプロジェクト:helloCTM-8.zip