question

EmonHaque-1485 avatar image
0 Votes"
EmonHaque-1485 asked PeterFleischer-3316 commented

How to draw lines in Viewport3D?

Was looking at some examples of 3D drawing and used the approach shown here to draw y = x^2 + z^2 and a plane at y=0 in Viewport3D. Here's how it looks like:

87897-test1.gif

and here's the code:

 class Plot3D : FrameworkElement
 {
     Viewport3D viewPort;
     PerspectiveCamera camera;
     Model3DGroup group;
     Point3DAnimationUsingKeyFrames positionAnim;
     Vector3DAnimationUsingKeyFrames lookAnim;

     public Plot3D() {
         camera = new PerspectiveCamera() {
             FieldOfView = 120,
             LookDirection = new Vector3D(0, -12, 9),
             Position = new Point3D(0, 12, -9),
             UpDirection = new Vector3D(0, 1, 0)
         };

         group = new Model3DGroup() { Children = { new AmbientLight(Colors.White) } };
         viewPort = new Viewport3D() {
             Camera = camera,
             Children = { new ModelVisual3D() { Content = group } }
         };
         AddVisualChild(viewPort);
         addModel();

         positionAnim = new Point3DAnimationUsingKeyFrames() {
             KeyFrames = {
                 new LinearPoint3DKeyFrame(new Point3D( 0, 12,-9), TimeSpan.FromSeconds(0)),
                 new LinearPoint3DKeyFrame(new Point3D(-9, 12, 0), TimeSpan.FromSeconds(5)),
                 new LinearPoint3DKeyFrame(new Point3D( 0, 12, 9), TimeSpan.FromSeconds(10)),
                 new LinearPoint3DKeyFrame(new Point3D( 9, 12, 0), TimeSpan.FromSeconds(15)),
                 new LinearPoint3DKeyFrame(new Point3D( 0, 12,-9), TimeSpan.FromSeconds(20))
             },
             RepeatBehavior = RepeatBehavior.Forever
         };
         lookAnim = new Vector3DAnimationUsingKeyFrames() {
             KeyFrames = {
                 new LinearVector3DKeyFrame(new Vector3D( 0, -12, 9), TimeSpan.FromSeconds(0)),
                 new LinearVector3DKeyFrame(new Vector3D( 9, -12, 0), TimeSpan.FromSeconds(5)),
                 new LinearVector3DKeyFrame(new Vector3D( 0, -12,-9), TimeSpan.FromSeconds(10)),
                 new LinearVector3DKeyFrame(new Vector3D(-9, -12, 0), TimeSpan.FromSeconds(15)),
                 new LinearVector3DKeyFrame(new Vector3D( 0, -12, 9), TimeSpan.FromSeconds(20))
             },
             RepeatBehavior = RepeatBehavior.Forever
         };
         Loaded += animate;
     }

     void animate(object sender, RoutedEventArgs e) {
         camera.BeginAnimation(PerspectiveCamera.PositionProperty, positionAnim);
         camera.BeginAnimation(PerspectiveCamera.LookDirectionProperty, lookAnim);
     }

     void addModel() {
         var mesh = new MeshGeometry3D();
         double dx, dz; dx = dz = 0.25;
         for (double x = -2; x < 2; x += dx) {
             for (double z = -2; z < 2; z += dz) {
                 var p1 = new Point3D(x, f(x, z), z);
                 var p2 = new Point3D(x + dx, f(x + dx, z), z);
                 var p3 = new Point3D(x + dx, f(x + dx, z + dz), z + dz);
                 var p4 = new Point3D(x, f(x, z + dz), z + dz);
                 addTriangle(mesh, p1, p2, p3);
                 addTriangle(mesh, p3, p4, p1);
             }
         }
         var surface = new GeometryModel3D() {
             Geometry = mesh,
             Material = new DiffuseMaterial(Brushes.LightBlue),
             BackMaterial = new DiffuseMaterial(Brushes.SkyBlue),
         };
         group.Children.Add(surface);

         var plane = new GeometryModel3D() {
             Geometry = new MeshGeometry3D() {
                 Positions = {
                     new Point3D(-3, 0, 3),
                     new Point3D( 3, 0, 3),
                     new Point3D(-3, 0,-3),
                     new Point3D( 3, 0,-3)
                 },
                 TriangleIndices = { 0, 1, 2, 2, 1, 3 }
             },
             Material = new DiffuseMaterial(Brushes.LightGray)
         };
         group.Children.Add(plane);
            
         //var line = new Line() { X1 = 0, X2 = 0, Y1 = 0, Y2 = 8, Stroke = Brushes.Blue, StrokeThickness = 4 };
         //var line2d = new Viewport2DVisual3D() {
         //    Geometry = plane.Geometry,
         //    Visual = line,
         //    Material = new DiffuseMaterial(Brushes.Blue)
         //};
         //line2d.Material.SetValue(Viewport2DVisual3D.IsVisualHostMaterialProperty, true);
         //viewPort.Children.Add(line2d);
     }

     double f(double x, double z) => x * x + z * z;
     void addTriangle(MeshGeometry3D mesh, Point3D p1, Point3D p2, Point3D p3) {
         int index1 = addPoint(mesh.Positions, p1);
         int index2 = addPoint(mesh.Positions, p2);
         int index3 = addPoint(mesh.Positions, p3);
         mesh.TriangleIndices.Add(index1);
         mesh.TriangleIndices.Add(index2);
         mesh.TriangleIndices.Add(index3);
     }

     int addPoint(Point3DCollection positions, Point3D point) {
         for (int i = 0; i < positions.Count; i++) {
             if ((point.X == positions[i].X) &&
                 (point.Y == positions[i].Y) &&
                 (point.Z == positions[i].Z))
                 return i;
         }
         positions.Add(point);
         return positions.Count - 1;
     }
     protected override Size ArrangeOverride(Size finalSize) {
         viewPort.Width = finalSize.Width;
         viewPort.Height = finalSize.Height;
         viewPort.Measure(finalSize);
         viewPort.Arrange(new Rect(viewPort.DesiredSize));
         return finalSize;
     }
     protected override Visual GetVisualChild(int index) => viewPort;
     protected override int VisualChildrenCount => 1;
 }

in MainWindow.xaml, I've used it like:

 <Grid Margin="20">
     <cc:Plot3D/>
 </Grid>

Wanted to draw some gridlines on the plane and the y-axis, perpendicular to the plane, BUT looks like there's is no Line3D, Ellipse3D, Path3D, etc. to draw lines, parametric curve or point in space!

How do you draw these in Viewport3D?

windows-wpf
test1.gif (1019.1 KiB)
· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

If you want to draw lines you'll have to build it as a quadrangle (2 connex triangles).

1 Vote 1 ·

anonymous user-3316, drawing lines/curves with triangles sounds very complex, if you don't mind could you please provide an example of doing so with triangles?

0 Votes 0 ·
  ...
 group.Children.Add(plane);
    
       var line1 = new GeometryModel3D()
       {
         Geometry = new MeshGeometry3D()
         {
           Positions = {
                        new Point3D(-5, .1, .01),
                        new Point3D( 5, .1, .01),
                        new Point3D(-5, .1, -.01),
                        new Point3D( 5, .1, -.01)
                    },
           TriangleIndices = { 0, 1, 2, 2, 1, 3 }
         },
         Material = new DiffuseMaterial(Brushes.Black)
       };
       group.Children.Add(line1);
    
 ...
    
       //var line = new Line() { X1 = 0, X2 = 0, Y1 = 0, Y2 = 8, Stroke = Brushes.Blue, StrokeThickness = 4 };
 ...
1 Vote 1 ·

1 Answer

DaisyTian-1203 avatar image
1 Vote"
DaisyTian-1203 answered EmonHaque-1485 commented

I add below code to your Plot3D, the code refers to three class MathUtils, Matrix3DStack and ScreenSpaceLines3D, they come from Microsoft's free, open source project, you can download it from 3DTools.

 public Plot3D()
         {
             //........Your code
             Point3DCollection point3Ds_X = new Point3DCollection();
             point3Ds_X.Add(new Point3D(0, 0, -20));
             point3Ds_X.Add(new Point3D(0, 0, 20));
             ScreenSpaceLines3D X_axis = new ScreenSpaceLines3D() { Points = point3Ds_X, Thickness = 2, Color = Colors.Red };
    
    
             Point3DCollection point3Ds_Y = new Point3DCollection();
             point3Ds_Y.Add(new Point3D(0, -20, 0));
             point3Ds_Y.Add(new Point3D(0, 20, 0));
             ScreenSpaceLines3D Y_axis = new ScreenSpaceLines3D() { Points = point3Ds_Y, Thickness = 2, Color = Colors.Green };
    
    
             Point3DCollection point3Ds_Z = new Point3DCollection();
             point3Ds_Z.Add(new Point3D(-20, 0, 0));
             point3Ds_Z.Add(new Point3D(20, 0, 0));
             ScreenSpaceLines3D Z_axis = new ScreenSpaceLines3D() { Points = point3Ds_Z, Thickness = 2, Color = Colors.Yellow };
    
             viewPort.Children.Add(X_axis);
             viewPort.Children.Add(Y_axis);
             viewPort.Children.Add(Z_axis);
             Loaded += animate;
         }

And the result picture is:
88136-2.gif


If the response is helpful, please click "Accept Answer" and upvote it.
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.



2.gif (153.1 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@DaisyTian-MSFT, why aren't these included in visual studio by default, does it have any issue that restrained MSFT from including it with standard installation?

0 Votes 0 ·