using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Media; using System.Windows.Media.Media3D; namespace HelixToolkit { /// /// Helper methods for objects. /// public static class Visual3DHelper { /// /// Finds the bounding box for a collection of Visual3Ds. /// /// The children. /// public static Rect3D FindBounds(Visual3DCollection children) { var bounds = Rect3D.Empty; foreach (var visual in children) { var b = FindBounds(visual, Transform3D.Identity); bounds.Union(b); } return bounds; } /// /// Finds the bounding box for the specified visual. /// /// The visual. /// The transform if the visual. /// public static Rect3D FindBounds(Visual3D visual, Transform3D transform) { var bounds = Rect3D.Empty; var childTransform = Transform3DHelper.CombineTransform(visual.Transform, transform); var model = GetModel(visual); if (model != null) { // apply transform var transformedBounds = childTransform.TransformBounds(model.Bounds); bounds.Union(transformedBounds); } foreach (var child in GetChildren(visual)) { var b = FindBounds(child, childTransform); bounds.Union(b); } return bounds; } private static readonly PropertyInfo Visual3DModelPropertyInfo = typeof(Visual3D).GetProperty("Visual3DModel", BindingFlags.Instance | BindingFlags.NonPublic); private static Model3D GetModel(Visual3D visual) { Model3D model; var mv = visual as ModelVisual3D; if (mv != null) { model = mv.Content; } else { model = Visual3DModelPropertyInfo.GetValue(visual, null) as Model3D; } return model; } private static IEnumerable GetChildren(Visual3D visual) { int n = VisualTreeHelper.GetChildrenCount(visual); for (int i = 0; i < n; i++) { var child = VisualTreeHelper.GetChild(visual, i) as Visual3D; if (child == null) continue; yield return child; } } /// /// Traverses the Visual3D/Model3D tree. Run the specified action for each Model3D. /// /// /// The visuals. /// The action. public static void Traverse(Visual3DCollection visuals, Action action) where T : Model3D { foreach (var child in visuals) Traverse(child, action); } /// /// Traverses the Visual3D/Model3D tree. Run the specified action for each Model3D. /// /// /// The visual. /// The action. public static void Traverse(Visual3D visual, Action action) where T : Model3D { Traverse(visual, Transform3D.Identity, action); } private static void Traverse(Visual3D visual, Transform3D transform, Action action) where T : Model3D { var childTransform = Transform3DHelper.CombineTransform(visual.Transform, transform); var model = GetModel(visual); if (model != null) { TraverseModel(model, childTransform, action); } foreach (var child in GetChildren(visual)) { Traverse(child, childTransform, action); } } /// /// Traverses the Model3D tree. Run the specified action for each Model3D. /// /// /// The model. /// The action. public static void TraverseModel(Model3D model, Action action) where T : Model3D { TraverseModel(model, Transform3D.Identity, action); } /// /// Traverses the Model3D tree. Run the specified action for each Model3D. /// /// /// The model. /// The transform. /// The action. public static void TraverseModel(Model3D model, Transform3D transform, Action action) where T : Model3D { var mg = model as Model3DGroup; if (mg != null) { var childTransform = Transform3DHelper.CombineTransform(model.Transform, transform); foreach (var m in mg.Children) TraverseModel(m, childTransform, action); } var gm = model as T; if (gm != null) { var childTransform = Transform3DHelper.CombineTransform(model.Transform, transform); action(gm, childTransform); } } public static T Find(DependencyObject parent) where T : DependencyObject { // todo: this should be improved foreach (DependencyObject d in LogicalTreeHelper.GetChildren(parent)) { var a = Find(d); if (a != null) return a; } var model = parent as ModelVisual3D; if (model != null) { var modelgroup = model.Content as Model3DGroup; if (modelgroup != null) { return modelgroup.Children.OfType().FirstOrDefault(); } } return null; } public static Matrix3D GetTotalTransform(Visual3D visual) { var totalTransform = Matrix3D.Identity; DependencyObject obj = visual; while (obj!=null) { var vis = obj as Viewport3DVisual; if (vis!=null) { var matxViewport = Viewport3DHelper.GetTotalTransform(vis); totalTransform.Append(matxViewport); return totalTransform; } var mv = obj as ModelVisual3D; if (mv!=null) { if (mv.Transform != null) totalTransform.Append(mv.Transform.Value); } obj = VisualTreeHelper.GetParent(obj); } throw new InvalidOperationException("The visual is not added to a Viewport3D."); // At this point, we know obj is Viewport3DVisual } public static bool IsAttachedToViewport3D(Visual3D visual) { DependencyObject obj = visual; while (obj != null) { var vis = obj as Viewport3DVisual; if (vis != null) { return true; } obj = VisualTreeHelper.GetParent(obj); } return false; } } }