@@ -739,11 +739,23 @@ private void graphicsBox_MouseMove(object sender, System.Windows.Forms.MouseEven
739739 graphicsBox . Refresh ( ) ;
740740 }
741741 // PointD pt = convertClientToSrc(e.X, e.Y); ; // (260322Ch) 旧実装: int overload helper を経由していた
742- PointD pt = convertClientToSrc ( new Point ( e . X , e . Y ) ) ; ; // (260322Ch) その場で Point を作って直接変換する
743- double azimuth = Math . Asin ( 2 * pt . Y / ( 1 + pt . X * pt . X + pt . Y * pt . Y ) ) ;
744- double tilt = ( Math . Cos ( azimuth ) != 0 ) ? Math . Asin ( 2 * pt . X / ( 1 + pt . X * pt . X + pt . Y * pt . Y ) / Math . Cos ( azimuth ) ) : 0 ;
745- labelYpos . Text = $ "Tilt Y: { azimuth / Math . PI * 180 : f3} °";
746- labelXpos . Text = $ "Tilt X: { tilt / Math . PI * 180 : f3} °";
742+ var pt = convertClientToSrc ( new Point ( e . X , e . Y ) ) ; ; // (260322Ch) その場で Point を作って直接変換する
743+ // 260517Cl 旧実装 (Wulff 公式固定で Schmidt 時に誤値): srcToSphere() に置換
744+ // var azimuth = Math.Asin(2 * pt.Y / (1 + pt.X * pt.X + pt.Y * pt.Y));
745+ // var tilt = (Math.Cos(azimuth) != 0) ? Math.Asin(2 * pt.X / (1 + pt.X * pt.X + pt.Y * pt.Y) / Math.Cos(azimuth)) : 0;
746+ var vSphere = srcToSphere ( pt ) ;
747+ if ( vSphere != null )
748+ {
749+ var azimuth = Asin ( vSphere . Y ) ;
750+ var tilt = Cos ( azimuth ) != 0 ? Asin ( vSphere . X / Cos ( azimuth ) ) : 0 ;
751+ labelXYpos . Text = $ "{ tilt / PI * 180 : f3} ° / { azimuth / PI * 180 : f3} °";
752+ var ( axis , plane ) = FindIndex ( vSphere ) ;
753+ // 260517Cl Miller-Bravais 表示時 (hex/trigonal) は面指数を 4 指数 (h k -h-k l) に
754+ var planeStr = MillerBravaisActive
755+ ? $ "({ plane . H } , { plane . K } , { - plane . H - plane . K } , { plane . L } )"
756+ : $ "({ plane . H } , { plane . K } , { plane . L } )";
757+ labelAxisPlane . Text = $ "[{ axis . U } ,{ axis . V } ,{ axis . W } ] / { planeStr } ";
758+ }
747759
748760 //真ん中ボタンが押されながらマウスが動いたとき
749761 if ( e . Button == MouseButtons . Middle )
@@ -1549,6 +1561,46 @@ public void UpdatePlaneIndices()
15491561
15501562 private void radioButtonDelimiterNone_CheckedChanged ( object sender , EventArgs e ) => Draw ( ) ;
15511563
1564+ /// <summary> ステレオネット上の点に対応する 3D 単位ベクトル (上半球) を返す。Schmidt 有効域外 (ρ²>2) は null。 </summary>
1565+ private Vector3DBase srcToSphere ( PointD p ) // 260517Cl 追加: 投影逆変換を MouseMove と FindIndex で共有
1566+ {
1567+ double X = p . X , Y = p . Y , rho2 = X * X + Y * Y ;
1568+ if ( radioButtonWulff . Checked ) // Wulff (stereographic)
1569+ return new Vector3DBase ( 2 * X , 2 * Y , 1 - rho2 ) / ( 1 + rho2 ) ;
1570+ return rho2 > 2 ? null : new Vector3DBase ( X * Sqrt ( 2 - rho2 ) , Y * Sqrt ( 2 - rho2 ) , 1 - rho2 ) ; // Schmidt (equal-area)
1571+ }
1572+
1573+ /// <summary> 3D 単位ベクトル vSphere (ラボ系・上半球) に最も近い晶帯軸/格子面の指数を返す。 </summary>
1574+ private ( ( int U , int V , int W ) Axis , ( int H , int K , int L ) Plane ) FindIndex ( Vector3DBase vSphere ) // 260517Cl 実装
1575+ {
1576+ //3つの指数の絶対値の合計が、以下の値になる範囲で、最も近い指数を探す。
1577+ int sumMax = 30 ;
1578+
1579+ if ( formMain == null || formMain . Crystal == null || formMain . Crystal . A * formMain . Crystal . B * formMain . Crystal . C == 0 )
1580+ return ( ( 0 , 0 , 0 ) , ( 0 , 0 , 0 ) ) ;
1581+
1582+ // 下半球モードは Z 反転 (DrawStereoNet と整合)。caller の vSphere を破壊しないよう新規生成
1583+ var v = radioButtonLowerSphere . Checked ? new Vector3DBase ( vSphere . X , vSphere . Y , - vSphere . Z ) : vSphere ;
1584+
1585+ // R は直交回転行列なので R^T = R^-1。target を結晶 Cartesian 系に戻して loop 内の行列積を省く
1586+ var crystal = formMain . Crystal ;
1587+ var target = crystal . RotationMatrix . Transpose ( ) * v ;
15521588
1589+ double bestScoreA = - 2 , bestScoreP = - 2 ;
1590+ ( int U , int V , int W ) bestUvw = ( 0 , 0 , 0 ) , bestHkl = ( 0 , 0 , 0 ) ;
1591+ for ( int u = - sumMax , rem1 = sumMax - Abs ( u ) ; u <= sumMax ; rem1 = sumMax - Abs ( ++ u ) )
1592+ for ( int vv = - rem1 , rem2 = rem1 - Abs ( vv ) ; vv <= rem1 ; rem2 = rem1 - Abs ( ++ vv ) )
1593+ for ( int w = - rem2 ; w <= rem2 ; w ++ )
1594+ {
1595+ if ( ( u | vv | w ) == 0 || Algebra . Irreducible ( u , vv , w ) != 1 ) continue ;
1596+ var dirA = crystal . MatrixReal * ( u , vv , w ) ;
1597+ double cosA = dirA * target / dirA . Length ;
1598+ if ( cosA > bestScoreA ) { bestScoreA = cosA ; bestUvw = ( u , vv , w ) ; }
1599+ var dirP = crystal . MatrixInverseTransposed * ( u , vv , w ) ;
1600+ double cosP = dirP * target / dirP . Length ;
1601+ if ( cosP > bestScoreP ) { bestScoreP = cosP ; bestHkl = ( u , vv , w ) ; }
1602+ }
1603+ return ( bestUvw , bestHkl ) ;
1604+ }
15531605}
15541606
0 commit comments