using System; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.Media3D; namespace HelixToolkit { public class TextVisual3D : ModelVisual3D { public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TextVisual3D), new UIPropertyMetadata(null, VisualChanged)); public static readonly DependencyProperty FillProperty = DependencyProperty.Register("Fill", typeof(Brush), typeof(TextVisual3D), new UIPropertyMetadata(Brushes.Black, VisualChanged)); public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(double), typeof(TextVisual3D), new UIPropertyMetadata(11.0, VisualChanged)); public static readonly DependencyProperty CenterProperty = DependencyProperty.Register("Center", typeof(Point3D), typeof(TextVisual3D), new UIPropertyMetadata(new Point3D(0, 0, 0), VisualChanged)); public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register("Direction", typeof(Vector3D), typeof(TextVisual3D), new UIPropertyMetadata(new Vector3D(1, 0, 0), VisualChanged)); public static readonly DependencyProperty UpProperty = DependencyProperty.Register("Up", typeof(Vector3D), typeof(TextVisual3D), new UIPropertyMetadata(new Vector3D(0, 0, 1), VisualChanged)); public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public Brush Fill { get { return (Brush)GetValue(FillProperty); } set { SetValue(FillProperty, value); } } public double Height { get { return (double)GetValue(HeightProperty); } set { SetValue(HeightProperty, value); } } public Point3D Center { get { return (Point3D)GetValue(CenterProperty); } set { SetValue(CenterProperty, value); } } public Vector3D Direction { get { return (Vector3D)GetValue(DirectionProperty); } set { SetValue(DirectionProperty, value); } } public Vector3D Up { get { return (Vector3D)GetValue(UpProperty); } set { SetValue(UpProperty, value); } } private static void VisualChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((TextVisual3D)d).OnVisualChanged(); } private void OnVisualChanged() { if (String.IsNullOrEmpty(Text)) Content = null; else Content = CreateTextLabel3D(Text, Fill, true, Height, Center, Direction, Up); } // http://www.ericsink.com/wpf3d/4_Text.html /// /// Creates a ModelVisual3D containing a text label. /// /// The string /// The color of the text. /// Visible from both sides? /// Height of the characters /// The center of the label /// Horizontal direction of the label /// Vertical direction of the label /// Suitable for adding to your Viewport3D public static GeometryModel3D CreateTextLabel3D( string text, Brush textColor, bool bDoubleSided, double height, Point3D center, Vector3D over, Vector3D up) { // First we need a textblock containing the text of our label var tb = new TextBlock(new Run(text)); tb.Foreground = textColor; tb.FontFamily = new FontFamily("Arial"); // Now use that TextBlock as the brush for a material var mat = new DiffuseMaterial(); mat.Brush = new VisualBrush(tb); // We just assume the characters are square double width = text.Length * height; // Since the parameter coming in was the center of the label, // we need to find the four corners // p0 is the lower left corner // p1 is the upper left // p2 is the lower right // p3 is the upper right Point3D p0 = center - width / 2 * over - height / 2 * up; Point3D p1 = p0 + up * 1 * height; Point3D p2 = p0 + over * width; Point3D p3 = p0 + up * 1 * height + over * width; // Now build the geometry for the sign. It's just a // rectangle made of two triangles, on each side. var mg = new MeshGeometry3D(); mg.Positions = new Point3DCollection { p0, p1, p2, p3 }; if (bDoubleSided) { mg.Positions.Add(p0); // 4 mg.Positions.Add(p1); // 5 mg.Positions.Add(p2); // 6 mg.Positions.Add(p3); // 7 } mg.TriangleIndices.Add(0); mg.TriangleIndices.Add(3); mg.TriangleIndices.Add(1); mg.TriangleIndices.Add(0); mg.TriangleIndices.Add(2); mg.TriangleIndices.Add(3); if (bDoubleSided) { mg.TriangleIndices.Add(4); mg.TriangleIndices.Add(5); mg.TriangleIndices.Add(7); mg.TriangleIndices.Add(4); mg.TriangleIndices.Add(7); mg.TriangleIndices.Add(6); } // These texture coordinates basically stretch the // TextBox brush to cover the full side of the label. mg.TextureCoordinates.Add(new Point(0, 1)); mg.TextureCoordinates.Add(new Point(0, 0)); mg.TextureCoordinates.Add(new Point(1, 1)); mg.TextureCoordinates.Add(new Point(1, 0)); if (bDoubleSided) { mg.TextureCoordinates.Add(new Point(1, 1)); mg.TextureCoordinates.Add(new Point(1, 0)); mg.TextureCoordinates.Add(new Point(0, 1)); mg.TextureCoordinates.Add(new Point(0, 0)); } return new GeometryModel3D(mg, mat); } } }