2017-10-14 160 views
3

在WPF中,我们可以使用VisualTreeHelper.GetDescendantBounds(Viewport3D)轻松获取ModelVisual3D的可见2D内容边界而无需转换。但是,当ModelVisual3D发生变形时,GetDescendantBounds会返回比可见内容更大的边界。我如何获得可见内容的准确界限?WPF中的可见2D内容边界ModelVisual3D

代码XAML:

<Grid Background="LightGray"> 
    <Viewport3D x:Name="MyViewport"> 
     <Viewport3D.Camera> 
      <OrthographicCamera Position="3 3 5" LookDirection="-3 -3 -5" Width="3"/> 
     </Viewport3D.Camera> 
     <Viewport3D.Children> 
      <ModelVisual3D> 
       <ModelVisual3D.Content> 
        <DirectionalLight Color="White" Direction="-1 -1 -1"/> 
       </ModelVisual3D.Content> 
      </ModelVisual3D> 
      <ModelVisual3D x:Name="MyVisual"> 
       <ModelVisual3D.Content> 
        <GeometryModel3D> 
         <GeometryModel3D.Geometry> 
          <MeshGeometry3D Positions="0,0,0 1,0,0 0,1,0 1,1,0 0,0,1 1,0,1 0,1,1 1,1,1" 
              TriangleIndices="0,2,1 1,2,3 0,4,2 2,4,6 0,1,4 1,5,4 1,7,5 1,3,7 4,5,6 7,6,5 2,6,3 3,6,7"/> 
         </GeometryModel3D.Geometry> 
         <GeometryModel3D.Material> 
          <DiffuseMaterial Brush="Red"/> 
         </GeometryModel3D.Material> 
         <!--<GeometryModel3D.Transform> 
          <RotateTransform3D> 
           <RotateTransform3D.Rotation> 
            <AxisAngleRotation3D Axis="1 1 0" Angle="5"/> 
           </RotateTransform3D.Rotation> 
          </RotateTransform3D> 
         </GeometryModel3D.Transform>--> 
        </GeometryModel3D> 
       </ModelVisual3D.Content> 
      </ModelVisual3D> 
     </Viewport3D.Children> 
    </Viewport3D> 
    <Rectangle x:Name="MyRegion" Stroke="Blue" StrokeThickness="1" VerticalAlignment="Top" HorizontalAlignment="Left"/> 
</Grid> 

代码behing:

var bounds = VisualTreeHelper.GetDescendantBounds(MyViewport); 

MyRegion.Width = bounds.Width; 
MyRegion.Height = bounds.Height; 

MyRegion.Margin = new Thickness(bounds.Left, bounds.Top, 0, 0); 

回答

2

如果你可以在你的网格几何体很容易地找到没有性能问题都三角点,你可以尝试下面的这个方法。我所做的是将所有Point3D转化为二维坐标,并获得所有二维点的边界。

GeneralTransform3DTo2D transform = MyVisual.TransformToAncestor(MyViewport); 
MeshGeometry3D geometry = (MeshGeometry3D) ((GeometryModel3D) MyVisual.Content).Geometry; 
Rect wholeBounds = Rect.Empty; 
if (transform != null) 
{ 
    for (int i = 0; i < geometry.TriangleIndices.Count;) 
    { 
     Polygon p = new Polygon 
     { 
      Stroke = Brushes.Blue, 
      StrokeThickness = 0.25 
     }; 
     var tr = ((GeometryModel3D) MyVisual.Content).Transform; 
     p.Points.Add(transform.Transform(tr.Transform(geometry.Positions[geometry.TriangleIndices[i++]]))); 
     p.Points.Add(transform.Transform(tr.Transform(geometry.Positions[geometry.TriangleIndices[i++]]))); 
     p.Points.Add(transform.Transform(tr.Transform(geometry.Positions[geometry.TriangleIndices[i++]]))); 
     foreach (Point point in p.Points) 
     { 
      wholeBounds.Union(point); 
     } 
    } 
    MyRegion.Width = wholeBounds.Width; 
    MyRegion.Height = wholeBounds.Height; 

    MyRegion.Margin = new Thickness(wholeBounds.Left, wholeBounds.Top, 0, 0); 
} 

enter image description here

+0

请注意,我使用了“多边形”来显示屏幕上的线条,以显示我如何完成您的问题。如果您只想显示外部边界,则不需要使用它。顺便说一下,所有的点都在“Rect”中三次结合。 – walterlv

+0

thx。它工作得非常好。我只是改进你的方法来满足更复杂的层次结构。 – Iron

1

我提高@ walterlv的方法,以满足更复杂的层次结构。

private void MainWindow_OnMouseDoubleClick(object sender, MouseButtonEventArgs e) 
{ 
    var bounds = CalculateBounds(MyVisual); 

    MyRegion.Width = bounds.Width; 
    MyRegion.Height = bounds.Height; 

    MyRegion.Margin = new Thickness(bounds.Left, bounds.Top, 0, 0); 
} 

public static Viewport3DVisual GetViewport3DVisual(Visual3D visual3D) 
{ 
    DependencyObject obj = visual3D; 

    while (obj != null) 
    { 
     var visual = obj as Viewport3DVisual; 
     if (visual != null) 
     { 
      return visual; 
     } 

     obj = VisualTreeHelper.GetParent(obj); 
    } 

    return null; 
} 

public static Rect CalculateBounds(Visual3D visual) 
{ 
    var transform = visual.TransformToAncestor(GetViewport3DVisual(visual)); 
    if (transform == null) 
    { 
     return Rect.Empty; 
    } 

    var bounds = Rect.Empty; 

    var modelVisual3D = visual as ModelVisual3D; 
    if (modelVisual3D != null) 
    { 
     bounds.Union(CalculateBounds(transform, modelVisual3D.Content, Matrix3D.Identity)); 

     // Unio the bounds of Children 
     foreach (var child in modelVisual3D.Children) 
     { 
      bounds.Union(CalculateBounds(child)); 
     } 
    } 
    else 
    { 
     // UIElement3D or Viewport2DVisual3D 
     bounds.Union(transform.TransformBounds(VisualTreeHelper.GetDescendantBounds(visual))); 
    } 

    return bounds; 
} 

public static Rect CalculateBounds(GeneralTransform3DTo2D transform, Model3D model, Matrix3D rootMatrix) 
{ 
    var region = Rect.Empty; 
    var matrix = Matrix3D.Identity; 

    matrix.Prepend(rootMatrix); 
    if (model.Transform != null) 
    { 
     matrix.Prepend(model.Transform.Value); 
    } 

    var geometryModel3D = model as GeometryModel3D; 
    if (geometryModel3D != null) 
    { 
     var meshGeometry3D = geometryModel3D.Geometry as MeshGeometry3D; 
     if (meshGeometry3D != null) 
     { 
      var innerTransform = new MatrixTransform3D(matrix); 
      foreach (var position in meshGeometry3D.Positions) 
      { 
       region.Union(transform.Transform(innerTransform.Transform(position))); 
      } 
     } 
    } 
    else 
    { 
     var model3DGroup = model as Model3DGroup; 
     if (model3DGroup != null) 
     { 
      foreach (var child in model3DGroup.Children) 
      { 
       region.Union(CalculateBounds(transform, child, matrix)); 
      } 
     } 
    } 

    return region; 
}