円反転 — 内と外を入れ替える
中心 O 半径 r の円に対して、点 P を OP の延長線上の r² / |OP|² 倍の位置へ飛ばす。中心に近い点は遠くへ、遠い点は中へ。円の内と外が裏返る。
円反転。中心 O・半径 r の円に対し点 P を、半直線 OP 上の OP·OP' = r² を満たす点 P' へ写す変換。円周上(|OP| = r)の点は動かず、内側の点は外へ、外側の点は内へ移る。直線と円をまとめて「広い意味の円」と見ると、反転はこの族を族に写す——中心を通らない直線は円に、中心を通る直線は直線のまま、中心を通る円は直線に化ける。角度を保つ(等角)性質も持ち、メビウス変換の構成要素になる。
まっすぐな直線を一本だけ反転円に通す。直線の上を細かく刻んで各点を invert で飛ばし、飛んだ先を繋ぐ。元の直線(薄い)と反転後(濃い)を重ねている。中心を通らない直線は円弧になって返ってくる。
invert の k = r² / d² が全部。点が反転円の中心から離れているほど d² が大きく、k が小さくなって中心側へ引き寄せられる。逆に近いほど遠くへ飛ぶ。飛んだ先を順に繋ぐと、直線が円弧に化ける。反転円の真上を通っていた直線が、円を内接させる弧として返ってくる。
const invert = (px, py) => {
const dx = px - cx
const dy = py - cy
const d2 = dx * dx + dy * dy + 0.0001 // 中心からの距離の二乗
const k = r2 / d2 // 近いほど大きく飛ぶ
return [cx + dx * k, cy + dy * k]
}格子をまるごと反転円に通すと、円弧の束になる。d² が中心付近でほぼ 0 になり、k が発散して点が画面外へ吹き飛ぶので、飛びすぎた点はそこで線を切って繋がない。中心を通る縦横の線だけは直線のまま残り、それ以外がぜんぶ円弧に化ける。
drawInverted の Number.isFinite と座標の範囲チェックで、吹き飛んだ点を境に started = false に戻して線を切っている。範囲チェックを外すと中心付近の点が画面の端から端へ繋がって、汚い直線が一本走る。中心を通る縦横の線だけは直線のまま残り、それ以外が円弧の束になって反転円の周りに巻きつく。反転円の半径 r を変えると、巻きつく弧の大きさが変わる。