この記事は機械翻訳されたものです。

快適な動作

Windows Phone で仮想世界を表示する (機械翻訳)

Charles Petzold

コード サンプルのダウンロード

Charles Petzoldコペルニクスの時まで — と多くの年後の — 人々 を信じ、宇宙の同心円の天体が地球を取り巻く一連の構築します。 宇宙のモデルを放棄されているが、視聴者として自分の相対的な 3 D 空間内のオブジェクトの場所を識別するための天球の概念を採用するはまだ便利です。

天体球は、仮想現実の世界を表示するためにスマート フォンを使用できますまたは拡張現実感プログラムでは特に便利です。 このようなプログラムでは、携帯電話、写真を取っている、またはビデオを介して、カメラのレンズが、画面に何を参照してくださいが、現実の世界では何もない可能性がある場合保持します。

このようなプログラムは、電話を円弧のスイープして、ユーザーはこの仮想世界をパンすることができますので、3 D 空間の方向を決定する必要があります。 モーション センサーが私はこのコラムの最後の割賦を説明 Windows Phone は、必要な向きの情報を提供するが可能です。

どのように我々 から天球にモーション センサーの情報を変換するか 座標系についてすべてであります。

私たちはすべてに精通していることができる地理座標は私たちの惑星の表面に場所について説明します。 地球の表面上の任意の点は、2 つの数値によって表すことができます。緯度と経度、どちらも地球のセンターでの頂点の角度です。 緯度は赤道の相対的な角度です。それは赤道の北の場所の正と負の場所の南です。 北極の緯度は 90 ° で、南極の緯度は-90 ° です。 経度は測定した 2 つの極をから英国、グリニッジを通る経度線が子午線通過大円間の角度を伴います。

我々 だけではなく、球の表面にも中央概念天球の住んでいます。 この天球上の場所を示すためにいくつかの座標系を使用することができますが、それが地平線上に基づいているので私は焦点を合わせる 1 水平座標システムと呼ばれます。

水平方向の座標

伸ばした腕を使用して、あなたの周りを参照してください、任意のオブジェクトをポイントします。 そのオブジェクトは天球上の場所があります。 その場所とは何ですか? 水平になるので、まっすぐ腕を上下移動 — は、地球の表面に並列します。 あなたの腕を通してこの運動中に振れる角度は、高度と呼ばれます。

高度の正の値は、地平線の上です。 負の値が地平線の下です。 あなたからのまっすぐにあるオブジェクトは天頂とも呼ばれる、90 ° の高度とオブジェクト連続ダウンどん底と呼ばれる、-90 ° の高度をしています。

今は北を指すよう水平差し出された腕をスイングします。 この移動中にあなたの腕をスイング角度は方位角と呼ばれます。 一緒に高度および方位は水平方向の座標を構成します。

水平方向の座標についてどのように遠くに何かのない情報を与えることに注意してください。 日食の間に、太陽と月、同じ水平方向の座標があります。 天球座標系の任意の種類、すべて天球の内部の表面にあると見なされます。

方位コンパスの特定の点を基準にする必要があります。 ほとんどの場合、方位角 0 ° 北東へ移動角の増加、設定されます。 しかし、天文学者は西進角度の増加に伴う南 0 ° に設定する傾向がある; 少なくとも、ジャン ・ Meeus がどのようにそれを彼の古典的な本では、「天文アルゴリズム (Willmann-ベル、1998) 合計かです。

観点が違う点を除いて水平座標は地理的座標に類似しています。 球の表面にされてではなく、外を見るセンター君します。 方位角経度に匹敵であり高度緯度に匹敵します。 円の経度のよう方位の円は常に大円を棒を渡すです。 緯度の円のよう高度の円は常に互いに平行です。 地平線と同じ役割として地理座標の赤道での水平方向の座標を再生します。

今あなた Windows Phone を選ぶし、カメラのレンズ点あなたから中に画面を見ているのでそれを保持します。 カメラのレンズを指している方向は、特定の高度と方位をしています。 水平方向の座標は、概念的には天球の内部上の場所ですが、カメラのレンズの観点からの方向も — 数学的に、3次元ベクトル。

以前のコラムで説明したように、携帯電話暗黙の座標系は、画面から正の Z 軸を拡張があります。 それは、カメラのレンズの他の側点 (0, 0,-1) の 3D ベクトルの方向を意味します。 私はこのコラムの前回の記事で示されるように (msdn.microsoft.com/magazine/jj190811)、Windows Phone でのモーション センサー、携帯電話からの相対の地球の回転方法について説明します、3 D 回転行列を取得することができます。 携帯電話と相対的な地球の回転方法を記述する行列を取得するには、モーション センサーから得られる行列を反転する必要があります。

matrix = Matrix.Invert(matrix);

この反転の行列を使用して回転するには、(0, 0,-1) ベクトル:

Vector3 vector = Vector3.Transform(new Vector3(0, 0, -1), matrix);

今あなたは、カメラのレンズを指す方向を示す 3D ベクトルがあります。 ベクトルは、高度および方位の角度に変換する必要があります。

携帯電話は直立保持されている場合 — は、変換とは、地球の表面に水平ベクトル — Z 成分は 0 であり 2次元直交座標から極座標への既知の変換からの問題を低減します。 C# では、これだけです:

double azimuth = Math.Atan2(vector.X, vector.Y);

それは角度 (ラジアン単位) です。 180 し、π 度に変換するで除算します。

この数式は、北、方位角 0 と値の増加が、東の方向に意味します。

南のゼロを好む場合西の方向の値を増やすと、結果が 180 °、X と Y コンポーネントの署名を変更することによってシフトします。

方位角の数式は変換ベクトルの Z 成分に関係なく実際には有効です。

Z コンポーネントは標高のサインです。 高度範囲の間の 90 ° をのみ否定と肯定的なので、逆正弦関数を使用して計算できます。

double altitude = Math.Asin(vector.Z);

また、180 ° を乗算し、π ラジアンを度に変換する除算。

しかし、我々 はまだ、天球の内側の表面に限定されるため、唯一の 2 つのディメンションを持つ座標に 3次元回転行列を翻訳したことを実現する場合は認識可能性があるものを見逃しています。

3D ベクトルを記載されている特定の方向で電話を目指して軸のようなベクトルの周りの携帯電話を回転させるとどうなりますか? ベクトルを変更しない、また高度および方位角の値が、仮想現実のシーン、携帯電話の画面に相対的な電話回転する必要があります。

この余分な動き傾きとして呼ばれます。 それも、角度が計算は高度および方位よりも、もう少し難しい。

その計算の高度、方位、傾斜度のすべての読書運動変換、作成 HorizontalCoordinate 構造を見ることができます。 この構造体は、この記事のダウンロード可能なコードの間では、AltitudeAndAzimuth プロジェクトに含まれています。 このプログラムは単にモーション センサーを使用して、携帯電話の方向を取得してし、情報を水平方向の座標に変換します。 このプロジェクトには (3 D ベクトルと行列の)、Microsoft.Devices.Sensors (クラスのアセンブリの動き) と、Microsoft.Xna.Framework アセンブリへの参照が必要です。 変換後のベクトルと HorizontalCoordinates 構造体からの値が表示されます。 図 1 電話開催約レンズで直立東約、指摘したし、時計回りに少し傾いた。

The AltitudeAndAzimuth Display
図 1 AltitudeAndAzimuth の表示

大きな画像を得る

はるかにあなたのコンピューターの画面よりも大きいイメージを表示すると仮定します- または、この場合、あなたの携帯電話。

伝統的に、スクロール バーが関与しています。 タッチ スクリーン、スクロールバーを除去することができ、ユーザーの指を使って同様のスクロール操作を実行できます。

しかし天球この大規模なイメージの内部を概念的に壁紙し、携帯電話自体を移動することによって表示するアプローチもあります。 (画像を表示するには、電話を移動すると、左と右または上下の電話を飛行機で移動していないことに注意してください。 運動高度および方位変化している円弧に沿って必要があります。)

携帯電話の動きとしては、自然な方法でが画面にパンをどのように大規模なこのようなイメージをすることができますか?

平均 Windows Phone 画面はおそらく約 2 インチ ワイド 3.33 インチ背が高いです。 携帯電話の分野を占めるいくつか単純な三角法明らかにあなたの顔から 6 インチの携帯電話を保持する場合は、約 19 ° 度ワイドと 31 ° 背が表示します。 受話器を持って、風景モードでは、これら 2 つのフィールドの表示の 360 ° 度と標高 180 ° 全方位からスライスです。 非常に大体、その後、携帯電話の画面風景モードであなたの顔から 6 インチは、水平および垂直の視野は、合計の約 10 % を占める開催。

または、それをこのように考えて:ビットマップの表面上のパンにポートレート モードであなたの電話を使用する場合は、そのビットマップどこか 8000 ピクセル幅と高さ 4800 ピクセルの領域にすることができます。

それは最大の 5649 ピクセル幅と 4000 ピクセル高 8 画像 (絵画、写真、文書、図面、ウィキペディアからほとんどのミックス) のダウンロード リンクが含まれていますとなる総合的なプロジェクトのアイデアです。 XML ファイルを編集することによって、他の画像を簡単に追加できますが、PictureDecoder.DecodeJpeg メソッドを使用して私の経験に基づいて、あなたはあなたがはるかに大きい行く場合メモリ不足例外が発生する可能性が高い。

バック グラウンド ファイル転送

なる総合的なによって参照されるファイルは、2 MB のサイズ以上とそれらの 1 つは 19 MB、これは理想的な機会を作るように見えたイメージのほとんどを考慮した施設の使用 Windows Phone はバック グラウンドでファイルをダウンロードするにはを追加しました。

なる総合的なプログラムでは、MainPage の大部分は、利用可能なファイルの一覧を ListBox を維持し、それらを分離ストレージにダウンロードする向けです。 図 2 ショーのプログラムをいくつか画像を既にダウンロード (これはサムネイルとして表示されます)、1 つのダウンロードの進行状況や他の人をまだダウンロードします。

The BigPicture Main Page
図 2 となる総合的なメインページ

バック グラウンド ファイル転送を使用するには、型を外部のファイルの URL を渡す BackgroundTransferRequest のオブジェクトを作成し、/shared/transfers ディレクトリ内のストレージの場所の URL を分離します。 [プログラムを実行しているとあなたのプログラムを再び起動したとき、アクティブな要求を列挙できますが、状態と進行状況イベント経由で変更を取得できます。

なる総合的なプログラムを起動すると、分離ストレージが既にダウンロードされて、画像の MainPage を検索します。 ファイルで指定するファイル名を直接していくつか他のファイル名を一時ファイルにダウンロードされることを発見しました。 これは、私のプログラムはまだ完全にダウンロードされていたいない、または、そのダウンロードがキャンセルされた可能性がありますファイルが発生したことを意味します。 私は私のプログラムでのみのファイルをダウンロードしたり、ない恒久的ストレージに/shared/transfers ディレクトリを使用していくつかのバグを修正しました。 ダウンロードが完了したら、プログラムはそのファイルを別のディレクトリに移動、サムネイルはまだ別のディレクトリに作成されます。 利便性にすべての 3 つのファイルに同じ名前をが存在しているディレクトリが識別されます。

ファイルとなる総合的なによってダウンロードされているし、ListBox 内の項目をタップすることができますプログラムをプログラムの本当の心がビュー ページに移動します。

大きな画像を表示します。

ビュー ページには、画面上をタップ間代替することができます 2 つの表示モードがあります。 アニメーションを 1 つのモードからをかかります。

通常のモードでは、表示の図 3、概念的には天球のインテリアを伸ばしてそのピクセル サイズでイメージが表示されます。 あなたがイメージの周囲、電話の向きを変更することで概念的に携帯電話を表示する天球の領域に向けてポインティング移動します。 (立ち上がるとあなたの体全体を別の方向に回すし、電話を上下にもポイント役立つ可能性があります。)

BigPicture Showing One Small Part of a Large Painting
大規模な絵画の小さな部分を示す図 3 となる総合的な

携帯電話をタップすると、ズーム アウト モードをシフトします。 全体のイメージの方がポートレート モードでは、後で示すように表示されます図 4。 四角形では、通常モードで表示することはイメージの一部が表示されます。 この例では、その四角形の右下角の近くです。

BigPicture Showing an Entire Large Painting
全体の大規模な絵画を示す図 4 となる総合的な

エッジはどうなりますか? ビットマップの右端を超えて右に携帯電話を移動すると、ビットマップは概念的には天球のインテリアに伸縮するので、左端直面する必要があります。 ただし、Silverlight のレイアウト システムの周りこの方法でラップしません。 プログラム大きなビットマップのエッジの反対側表示できる場合は、2 つのイメージの要素が必要になります。 すべての 4 つのコーナーの出会う場所の時点では、イメージの 4 つの要素が必要になります。

その概念を拒否しました。 ビットマップの右端を超えて、ギャップ、携帯電話のディスプレイの最大寸法に等しいし、左端に表示されます。 両方のエッジの表示を表示することは決してされます。 これも、理論的には上部と下部の絵のポイントに圧縮する、極に問題を解決します。

図 5 ビュー ページのほとんどの XAML ファイルを示します。 もちろん、イメージ要素自体のビットマップが表示されます、None は、ストレッチのプロパティを設定する、そのピクセルのサイズで表示することを示します。 通常大きな画像、レイアウト システム、ディスプレイのでサイズにトリミングされるだろうし、イメージの残りの部分の周りをパンすることができるでしょう。 しかし、オブジェクト全体をレンダリングするのには、キャンバス内のすべてを置くトリックのレイアウト システム。 埋め込みの四角形との国境は縮小モードでは、表示の四角形も表示の通常のモードで画面の中をハグするだけです。 ImageTransform という名前の CompositeTransform は、画像と枠線の両方に適用されます。 その他複合 - borderTransform という名前変換のみを罫線に適用します。

XAML ファイルとなる総合的なページを表示する画像を図 5

<phone:PhoneApplicationPage ...
>
  <Grid x:Name="LayoutRoot" Background="Transparent">
    <Canvas>
      <Grid>
        <Image Name="image" Stretch="None" />
        <Border Name="outlineBorder"
                BorderBrush="White"
                HorizontalAlignment="Left"
                VerticalAlignment="Top">
            <Rectangle Name="outlineRectangle"
                       Stroke="Black" />
            <Border.RenderTransform>
              <CompositeTransform x:Name="borderTransform" />
            </Border.RenderTransform>
        </Border>
        <Grid.RenderTransform>
          <CompositeTransform x:Name="imageTransform" />
        </Grid.RenderTransform>
      </Grid>
    </Canvas>
    <TextBlock Name="titleText"
               Style="{StaticResource PhoneTextNormalStyle}"
               Margin="12,17,0,28" />
    <TextBlock Name="statusText"
               Text="creating image..."
               HorizontalAlignment="Center"
               VerticalAlignment="Center" />
  </Grid>
</phone:PhoneApplicationPage>

分離コード ファイルは、モーション センサーの行くを起動し、それを使用してこれら 2 つの変換のプロパティを設定する HorizontalCoordinate オブジェクトを作成するには、回転行列を適用します。 ビュー ページ クラスは、2 つの表示モード間の遷移をアニメーションの対象である、InterpolationFactor の依存関係プロパティも定義します。 InterpolationFactor は 0 から 1 にアニメーション化されるように、ビューは正常とズーム アウトの間に遷移します。

図 6 の数学のほとんどを示しています。 1 つの最も重要な計算はモーション センサーが更新されたときに発生します。 これは、CenterX の計算と CenterY プロパティ、イメージを CompositeTransform のです、それは高度および方位出番です。 この変換センター周辺のポイントを拡大/縮小および回転が発生する、さらに計算が通常の表示モードの表示の中央にこのポイントを置きます。 四角形の枠もこの点に揃えられます。

図 6 多くの変換の数学となる総合的なの

public partial class ViewPage : PhoneApplicationPage
{
  ...
void OnLoaded(object sender, RoutedEventArgs args)
  {
    // Save the screen dimensions
    screenWidth = this.ActualWidth;
    screenHeight = this.ActualHeight;
    maxDimension = Math.Max(screenWidth, screenHeight);
    // Initialize some values
    outlineBorder.Width = screenWidth;
    outlineBorder.Height = screenHeight;
    borderTransform.CenterX = screenWidth / 2;
    borderTransform.CenterY = screenHeight / 2;
    // Load the image from isolated storage
    ...
// Save image dimensions
    imageWidth = bitmap.PixelWidth;
    imageHeight = bitmap.PixelHeight;
    ...
zoomInScale = Math.Min(screenWidth / imageWidth, 
      screenHeight / imageHeight);
    UpdateImageTransforms();
    ...
}
  ...
void OnMotionCurrentValueChanged(object sender,
          SensorReadingEventArgs<MotionReading> args)
  {
    ...
// Get the rotation matrix & convert to horizontal coordinates
    Matrix matrix = args.SensorReading.Attitude.RotationMatrix;
    HorizontalCoordinate horzCoord = 
      HorizontalCoordinate.FromMotionMatrix(matrix);
    // Set the transform center on the Image element
    imageTransform.CenterX = (imageWidth + maxDimension) *
      (180 + horzCoord.Azimuth) / 360 - maxDimension / 2;
    imageTransform.CenterY = (imageHeight + maxDimension) *
      (90 - horzCoord.Altitude) / 180 - maxDimension / 2;
    // Set the translation on the Border element
    borderTransform.TranslateX = 
      imageTransform.CenterX - screenWidth / 2;
    borderTransform.TranslateY = 
      imageTransform.CenterY - screenHeight / 2;
    // Get rotation from Tilt
    rotation = -horzCoord.Tilt;
    UpdateImageTransforms();
  }
  static void OnInterpolationFactorChanged(DependencyObject obj,
              DependencyPropertyChangedEventArgs args)
  {
    (obj as ViewPage).UpdateImageTransforms();
  }
  void UpdateImageTransforms()
  {
    // If being zoomed out, set scaling
    double interpolatedScale = 1 + InterpolationFactor * 
      (zoomInScale - 1);
    imageTransform.ScaleX =
    imageTransform.ScaleY = interpolatedScale;
    // Move transform center to screen center
    imageTransform.TranslateX = 
      screenWidth / 2 - imageTransform.CenterX;
    imageTransform.TranslateY = 
      screenHeight / 2 - imageTransform.CenterY;
    // If being zoomed out, adjust for scaling
    imageTransform.TranslateX -= InterpolationFactor *
      (screenWidth / 2 - zoomInScale * imageTransform.CenterX);
    imageTransform.TranslateY -= InterpolationFactor *
      (screenHeight / 2 - zoomInScale * imageTransform.CenterY);
    // If being zoomed out, center image in screen
    imageTransform.TranslateX += InterpolationFactor *
      (screenWidth - zoomInScale * imageWidth) / 2;
    imageTransform.TranslateY += InterpolationFactor *
      (screenHeight - zoomInScale * imageHeight) / 2;
    // Set border thickness
    outlineBorder.BorderThickness = 
      new Thickness(2 / interpolatedScale);
    outlineRectangle.StrokeThickness = 2 / interpolatedScale;
    // Set rotation on image and border
    imageTransform.Rotation = (1 - InterpolationFactor) * rotation;
    borderTransform.Rotation = -rotation;
  }
}

方位角が 0 (電話北向き) 高度が 0 (アップライト) の場合は、CenterX とセンター北部研究所プロパティをビットマップの中央に設定されます。 ビットマップ以外の値にこれらの CenterX とセンター北部研究所のプロパティを設定できるように、maxDimension の値を含めることに注意してください。 過去のエッジを掃引するときこのパディングのことができます。

計算の残りの部分のほとんどは、モーション センサーの新しい値を報告したときまたは遷移中に、InterpolationFactor プロパティが変更されたときに呼び出される、UpdateImageTransforms メソッドの中に発生します。 ここでは、スケーリングとのイメージ変換の翻訳が、回転と同様です。

これらの変換の相互作用を理解することに興味があるなら、補間のすべてのコードを排除することによってそれらをきれいにしたいと思うかもしれない。 InterpolationFactor が 0 と 1、ときが簡体字の式を調べるとは実際にかなり簡単であることがわかります。

Charles Petzold 長年 MSDN Magazine の寄稿者で、現在彼の古典的な本"Windows プログラミング"(Microsoft Press、1998年) Windows 8 を更新しています。  彼の Web サイト charlespetzold.com

この記事のレビュー、技術スタッフのおかげで:Donn Morse