简体   繁体   中英

3D transformation WPF

I draw two spheres in 3d WPF which has points like Point3D(0,0,0) and Point3D(-1.0,1.0,2.0) with the radius is 0.10

Now i want to draw a Cylinder joining these spheres, the only thing i have for this is radius 0.02. I want to know how to calculate the 3d point, height, direction etc for this cylinder.

I tried by finding the midpoint btw sphere points, it's placing the cylinder in the middle of those two spheres but not in the right direction. I want to rotate the cylinder in the right angle. I used Vector3D.angleBetween(v1,v2) to find the angle it's giving me "NaN". I put the code which i am using in below.

    Vector3D v1 = new Vector3D(0, 0, 0);
    Vector3D v2 = new Vector3D(1.0, -1.0, 2.0);

    Vector3D center = v1+ v2/2;
    Vector3D axis = Vector3D.CrossProduct(v1, v2);
    double angle = Vector3D.AngleBetween(v1, v2);
    AxisAngleRotation3D axisAngle = new AxisAngleRotation3D(axis, angle);
    RotateTransform3D myRotateTransform = new RotateTransform3D(axisAngle, center);
    center.X = myRotateTransform.CenterX;
    center.Y = myRotateTransform.CenterY;
    center.Z = myRotateTransform.CenterZ;

[EDIT]

First of all thank you so much for the response. I am having some issues with this code it's working good with your example. but with my points It's not drawing the cylinder btw the two circle points in a right direction and also not until the end point (it's only connecting to second point) One more thing, if the mid point of the Z-axis is (midpoint.Z = 0), it not even drawing the cylinder.

I am just wondering, is it because of the way i am drawing my circle. Please take a look

public ModelVisual3D CreateSphere(Point3D center, double radius, int u, int v, Color color)
{
        Model3DGroup spear = new Model3DGroup();

        if (u < 2 || v < 2)
            return null;
        Point3D[,] pts = new Point3D[u, v];
        for (int i = 0; i < u; i++)
        {
            for (int j = 0; j < v; j++)
            {
                pts[i, j] = GetPosition(radius,
                i * 180 / (u - 1), j * 360 / (v - 1));
                pts[i, j] += (Vector3D)center;
            }
        }

        Point3D[] p = new Point3D[4];
        for (int i = 0; i < u - 1; i++)
        {
            for (int j = 0; j < v - 1; j++)
            {
                p[0] = pts[i, j];
                p[1] = pts[i + 1, j];
                p[2] = pts[i + 1, j + 1];
                p[3] = pts[i, j + 1];
                spear.Children.Add(CreateTriangleModel(p[0], p[1], p[2], color));
                spear.Children.Add(CreateTriangleModel(p[2], p[3], p[0], color));
            }
        }
        ModelVisual3D model = new ModelVisual3D();
        model.Content = spear;
        return model;
    }

    private Point3D GetPosition(double radius, double theta, double phi)
    {
        Point3D pt = new Point3D();
        double snt = Math.Sin(theta * Math.PI / 180);
        double cnt = Math.Cos(theta * Math.PI / 180);
        double snp = Math.Sin(phi * Math.PI / 180);
        double cnp = Math.Cos(phi * Math.PI / 180);
        pt.X = radius * snt * cnp;
        pt.Y = radius * cnt;
        pt.Z = -radius * snt * snp;
        return pt;
    }

    public Model3DGroup CreateTriangleFace(Point3D p0, Point3D p1, Point3D p2, Color color)
    {
        MeshGeometry3D mesh = new MeshGeometry3D();
        mesh.Positions.Add(p0);
        mesh.Positions.Add(p1);
        mesh.Positions.Add(p2);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);

        Vector3D normal = VectorHelper.CalcNormal(p0, p1, p2);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);

        Material material = new DiffuseMaterial(new SolidColorBrush(color));
        GeometryModel3D model = new GeometryModel3D(mesh, material);
        Model3DGroup group = new Model3DGroup();
        group.Children.Add(model);
        return group;
    }

  private class VectorHelper
    {
        public static Vector3D CalcNormal(Point3D p0, Point3D p1, Point3D p2)
        {                
            Vector3D v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
            Vector3D v1 = new Vector3D( p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
            return Vector3D.CrossProduct(v0, v1);
        }
    }

almost same as your code>

      my sample point are :
                    p1 = Point3D(0,0,0)
                    p2= Point3D(-1.0, 1.0, 2.0)
                    p3= Point3D(-1.0, 1.0, 2.0)
                    p4 =Point3D(1.0, -1.0, 2.0)

i want to draw cylinders btw p1 to p2 ,p1 to p3, p1 to p4 p2 to p3 , p2 to p4

Please Let me know if you need any more clarification, I have to get this out. Thanks for all your time.

I don't know whether this will be of use to you, but you commented on some similar code I recently posted, asking for assistance - so I thought I'd convert that code into two objects, connected by a cylinder spinning around in 3D space - the Init() method takes two points, creates 10x10x10 cubes at the points and then connects them with a cylinder.

I think this is roughly what your trying to achieve, though I admit that my example is a little contrived, (you can only change the Z axis on each of the two points in!!!), but hopefully you'll get something from it!

Sorry the code is a bit of a mess but I've compiled it from a number of my classes to get it into one easy cut and paste-able lump!

Anyway, hope this helps ...

Heres the XAML ... this just sets up the ViewPort and light source ...

<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>

    <Viewport3D Name="mainViewport" ClipToBounds="True" HorizontalAlignment="Stretch" Height="300">
        <Viewport3D.Camera>
            <PerspectiveCamera 
                  LookDirection="0,0,-20"
                  UpDirection="0,1,0"
                  Position="0,0,100" 
                  />
        </Viewport3D.Camera>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <Model3DGroup x:Name="group3d">

                    <SpotLight Position="30,30,30" x:Name="mySpotLight" Color="Yellow"  InnerConeAngle="100" OuterConeAngle="1000" Range="100" />
                </Model3DGroup>
            </ModelVisual3D.Content>

        </ModelVisual3D>
    </Viewport3D>

</StackPanel>

... and heres the code behind ...

 using System;
 using System.Collections.Generic;
 using System.Timers;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media.Media3D;
 using System.Windows.Threading;

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        Init(new Point3D(0, 0, 30), new Point3D(0,0,-30));
    }

    private Timer _timer;
    private readonly List<ModelVisual3D> _models = new List<ModelVisual3D>();
    private double _angle;



    public void Init(Point3D firstPoint, Point3D secondPoint)
    {
        var midPoint = firstPoint - secondPoint;
        var size = new Size3D(10,10,10);
        _models.Add(GetCube(GetSurfaceMaterial(Colors.Green), firstPoint, size));
        _models.Add(GetCube(GetSurfaceMaterial(Colors.Green), secondPoint, size));
        _models.Add(GetCylinder(GetSurfaceMaterial(Colors.Red), secondPoint, 2, midPoint.Z));

        _models.ForEach(x => mainViewport.Children.Add(x));
        _timer = new Timer(10);
        _timer.Elapsed += TimerElapsed;
        _timer.Enabled = true;
    }



    void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        Dispatcher.Invoke(DispatcherPriority.Normal, new Action<double>(Transform), 0.5d);
    }

    public MaterialGroup GetSurfaceMaterial(Color colour)
    {
        var materialGroup = new MaterialGroup();
        var emmMat = new EmissiveMaterial(new SolidColorBrush(colour));
        materialGroup.Children.Add(emmMat);
        materialGroup.Children.Add(new DiffuseMaterial(new SolidColorBrush(colour)));
        var specMat = new SpecularMaterial(new SolidColorBrush(Colors.White), 30);
        materialGroup.Children.Add(specMat);
        return materialGroup;
    }

    public ModelVisual3D GetCube(MaterialGroup materialGroup, Point3D point, Size3D size)
    {
        var farPoint = new Point3D(point.X - (size.X / 2), point.Y - (size.Y / 2), point.Z - (size.Z / 2));
        var nearPoint = new Point3D(point.X + (size.X / 2), point.Y + (size.Y / 2), point.Z + (size.Z / 2));

        var cube = new Model3DGroup();
        var p0 = new Point3D(farPoint.X, farPoint.Y, farPoint.Z);
        var p1 = new Point3D(nearPoint.X, farPoint.Y, farPoint.Z);
        var p2 = new Point3D(nearPoint.X, farPoint.Y, nearPoint.Z);
        var p3 = new Point3D(farPoint.X, farPoint.Y, nearPoint.Z);
        var p4 = new Point3D(farPoint.X, nearPoint.Y, farPoint.Z);
        var p5 = new Point3D(nearPoint.X, nearPoint.Y, farPoint.Z);
        var p6 = new Point3D(nearPoint.X, nearPoint.Y, nearPoint.Z);
        var p7 = new Point3D(farPoint.X, nearPoint.Y, nearPoint.Z);
        //front side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p3, p2, p6));
        cube.Children.Add(CreateTriangleModel(materialGroup, p3, p6, p7));
        //right side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p1, p5));
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p5, p6));
        //back side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p1, p0, p4));
        cube.Children.Add(CreateTriangleModel(materialGroup, p1, p4, p5));
        //left side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p0, p3, p7));
        cube.Children.Add(CreateTriangleModel(materialGroup, p0, p7, p4));
        //top side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p7, p6, p5));
        cube.Children.Add(CreateTriangleModel(materialGroup, p7, p5, p4));
        //bottom side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p3, p0));
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p0, p1));
        var model = new ModelVisual3D();
        model.Content = cube;
        return model;
    }

    private Model3DGroup CreateTriangleModel(MaterialGroup materialGroup, Triangle triangle)
    {
        return CreateTriangleModel(materialGroup, triangle.P0, triangle.P1, triangle.P2);
    }

    private Model3DGroup CreateTriangleModel(Material material, Point3D p0, Point3D p1, Point3D p2)
    {
        var mesh = new MeshGeometry3D();
        mesh.Positions.Add(p0);
        mesh.Positions.Add(p1);
        mesh.Positions.Add(p2);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);
        var normal = CalculateNormal(p0, p1, p2);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);

        var model = new GeometryModel3D(mesh, material);

        var group = new Model3DGroup();
        group.Children.Add(model);
        return group;
    }

    private Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
    {
        var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
        var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
        return Vector3D.CrossProduct(v0, v1);
    }

    void Transform(double adjustBy)
    {
        _angle += adjustBy;

        var rotateTransform3D = new RotateTransform3D {CenterX = 0, CenterZ = 0};
        var axisAngleRotation3D = new AxisAngleRotation3D {Axis = new Vector3D(1, 1, 1), Angle = _angle};
        rotateTransform3D.Rotation = axisAngleRotation3D;
        var myTransform3DGroup = new Transform3DGroup();
        myTransform3DGroup.Children.Add(rotateTransform3D);
        _models.ForEach(x => x.Transform = myTransform3DGroup);
    }


    public ModelVisual3D GetCylinder(MaterialGroup materialGroup, Point3D midPoint, double radius, double depth)
    {
        var cylinder = new Model3DGroup();
        var nearCircle = new CircleAssitor();
        var farCircle = new CircleAssitor();

        var twoPi = Math.PI * 2;
        var firstPass = true;

        double x;
        double y;

        var increment = 0.1d;
        for (double i = 0; i < twoPi + increment; i = i + increment)
        {
            x = (radius * Math.Cos(i));
            y = (-radius * Math.Sin(i));

            farCircle.CurrentTriangle.P0 = midPoint;
            farCircle.CurrentTriangle.P1 = farCircle.LastPoint;
            farCircle.CurrentTriangle.P2 = new Point3D(x + midPoint.X, y + midPoint.Y, midPoint.Z);

            nearCircle.CurrentTriangle = farCircle.CurrentTriangle.Clone(depth, true);

            if (!firstPass)
            {
                cylinder.Children.Add(CreateTriangleModel(materialGroup, farCircle.CurrentTriangle));
                cylinder.Children.Add(CreateTriangleModel(materialGroup, nearCircle.CurrentTriangle));

                cylinder.Children.Add(CreateTriangleModel(materialGroup, farCircle.CurrentTriangle.P2, farCircle.CurrentTriangle.P1, nearCircle.CurrentTriangle.P2));
                cylinder.Children.Add(CreateTriangleModel(materialGroup, nearCircle.CurrentTriangle.P2, nearCircle.CurrentTriangle.P1, farCircle.CurrentTriangle.P2));
            }
            else
            {
                farCircle.FirstPoint = farCircle.CurrentTriangle.P1;
                nearCircle.FirstPoint = nearCircle.CurrentTriangle.P1;
                firstPass = false;
            }

            farCircle.LastPoint = farCircle.CurrentTriangle.P2;
            nearCircle.LastPoint = nearCircle.CurrentTriangle.P2;
        }

        var model = new ModelVisual3D {Content = cylinder};
        return model;
    }


}

public class CircleAssitor
{

    public CircleAssitor()
    {
        CurrentTriangle = new Triangle();
    }

    public Point3D FirstPoint { get; set; }
    public Point3D LastPoint { get; set; }
    public Triangle CurrentTriangle { get; set; }

}

public class Triangle
{


    public Point3D P0 { get; set; }
    public Point3D P1 { get; set; }
    public Point3D P2 { get; set; }

    public Triangle Clone(double z, bool switchP1andP2)
    {
        var newTriangle = new Triangle();
        newTriangle.P0 = GetPointAdjustedBy(this.P0, new Point3D(0, 0, z));

        var point1 = GetPointAdjustedBy(this.P1, new Point3D(0, 0, z));
        var point2 = GetPointAdjustedBy(this.P2, new Point3D(0, 0, z));

        if (!switchP1andP2)
        {
            newTriangle.P1 = point1;
            newTriangle.P2 = point2;
        }
        else
        {
            newTriangle.P1 = point2;
            newTriangle.P2 = point1;
        }
        return newTriangle;
    }

    private Point3D GetPointAdjustedBy(Point3D point, Point3D adjustBy)
    {
        var newPoint = new Point3D { X = point.X, Y = point.Y, Z = point.Z };
        newPoint.Offset(adjustBy.X, adjustBy.Y, adjustBy.Z);
        return newPoint;
    }



}
}

I've integrated your sphere code with my example and it works fine - the cylinder connects both spheres.

Heres the code ...

Cheers, Andy

ViewPort as before ...

<Window x:Class="wpfspin.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>

    <Viewport3D Name="mainViewport" ClipToBounds="True" HorizontalAlignment="Stretch" Height="300">
        <Viewport3D.Camera>
            <PerspectiveCamera 
              LookDirection="0,0,-20"
              UpDirection="0,1,0"
              Position="0,0,100" 
              />
        </Viewport3D.Camera>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <Model3DGroup x:Name="group3d">

                    <SpotLight Position="30,30,30" x:Name="mySpotLight" Color="Yellow"  InnerConeAngle="100" OuterConeAngle="1000" Range="100" />
                </Model3DGroup>
            </ModelVisual3D.Content>

        </ModelVisual3D>
    </Viewport3D>

</StackPanel>

... and heres the code behind ...

using System;
using System.Collections.Generic;
using System.Timers;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Threading;

namespace wpfspin
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        Init(new Point3D(0, 0, 30), new Point3D(0, 0, -30));
    }

    private Timer _timer;
    private readonly List<ModelVisual3D> _models = new List<ModelVisual3D>();
    private double _angle;



    public void Init(Point3D firstPoint, Point3D secondPoint)
    {
        var midPoint = firstPoint - secondPoint;

        _models.Add(CreateSphere(firstPoint, 10, 10, 10, Colors.AliceBlue ));
        _models.Add(CreateSphere(secondPoint, 10, 10, 10, Colors.AliceBlue));
        _models.Add(GetCylinder(GetSurfaceMaterial(Colors.Red), secondPoint, 2, midPoint.Z));

        _models.ForEach(x => mainViewport.Children.Add(x));
        _timer = new Timer(10);
        _timer.Elapsed += TimerElapsed;
        _timer.Enabled = true;
    }



    void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        Dispatcher.Invoke(DispatcherPriority.Normal, new Action<double>(Transform), 0.5d);
    }

    public MaterialGroup GetSurfaceMaterial(Color colour)
    {
        var materialGroup = new MaterialGroup();
        var emmMat = new EmissiveMaterial(new SolidColorBrush(colour));
        materialGroup.Children.Add(emmMat);
        materialGroup.Children.Add(new DiffuseMaterial(new SolidColorBrush(colour)));
        var specMat = new SpecularMaterial(new SolidColorBrush(Colors.White), 30);
        materialGroup.Children.Add(specMat);
        return materialGroup;
    }

    public ModelVisual3D GetCube(MaterialGroup materialGroup, Point3D point, Size3D size)
    {
        var farPoint = new Point3D(point.X - (size.X / 2), point.Y - (size.Y / 2), point.Z - (size.Z / 2));
        var nearPoint = new Point3D(point.X + (size.X / 2), point.Y + (size.Y / 2), point.Z + (size.Z / 2));

        var cube = new Model3DGroup();
        var p0 = new Point3D(farPoint.X, farPoint.Y, farPoint.Z);
        var p1 = new Point3D(nearPoint.X, farPoint.Y, farPoint.Z);
        var p2 = new Point3D(nearPoint.X, farPoint.Y, nearPoint.Z);
        var p3 = new Point3D(farPoint.X, farPoint.Y, nearPoint.Z);
        var p4 = new Point3D(farPoint.X, nearPoint.Y, farPoint.Z);
        var p5 = new Point3D(nearPoint.X, nearPoint.Y, farPoint.Z);
        var p6 = new Point3D(nearPoint.X, nearPoint.Y, nearPoint.Z);
        var p7 = new Point3D(farPoint.X, nearPoint.Y, nearPoint.Z);
        //front side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p3, p2, p6));
        cube.Children.Add(CreateTriangleModel(materialGroup, p3, p6, p7));
        //right side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p1, p5));
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p5, p6));
        //back side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p1, p0, p4));
        cube.Children.Add(CreateTriangleModel(materialGroup, p1, p4, p5));
        //left side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p0, p3, p7));
        cube.Children.Add(CreateTriangleModel(materialGroup, p0, p7, p4));
        //top side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p7, p6, p5));
        cube.Children.Add(CreateTriangleModel(materialGroup, p7, p5, p4));
        //bottom side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p3, p0));
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p0, p1));
        var model = new ModelVisual3D();
        model.Content = cube;
        return model;
    }

    private Model3DGroup CreateTriangleModel(MaterialGroup materialGroup, Triangle triangle)
    {
        return CreateTriangleModel(materialGroup, triangle.P0, triangle.P1, triangle.P2);
    }

    private Model3DGroup CreateTriangleModel(Material material, Point3D p0, Point3D p1, Point3D p2)
    {
        var mesh = new MeshGeometry3D();
        mesh.Positions.Add(p0);
        mesh.Positions.Add(p1);
        mesh.Positions.Add(p2);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);
        var normal = CalculateNormal(p0, p1, p2);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);

        var model = new GeometryModel3D(mesh, material);

        var group = new Model3DGroup();
        group.Children.Add(model);
        return group;
    }

    private Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
    {
        var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
        var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
        return Vector3D.CrossProduct(v0, v1);
    }

    void Transform(double adjustBy)
    {
        _angle += adjustBy;

        var rotateTransform3D = new RotateTransform3D { CenterX = 0, CenterZ = 0 };
        var axisAngleRotation3D = new AxisAngleRotation3D { Axis = new Vector3D(1, 1, 1), Angle = _angle };
        rotateTransform3D.Rotation = axisAngleRotation3D;
        var myTransform3DGroup = new Transform3DGroup();
        myTransform3DGroup.Children.Add(rotateTransform3D);
        _models.ForEach(x => x.Transform = myTransform3DGroup);
    }


    public ModelVisual3D GetCylinder(MaterialGroup materialGroup, Point3D midPoint, double radius, double depth)
    {
        var cylinder = new Model3DGroup();
        var nearCircle = new CircleAssitor();
        var farCircle = new CircleAssitor();

        var twoPi = Math.PI * 2;
        var firstPass = true;

        double x;
        double y;

        var increment = 0.1d;
        for (double i = 0; i < twoPi + increment; i = i + increment)
        {
            x = (radius * Math.Cos(i));
            y = (-radius * Math.Sin(i));

            farCircle.CurrentTriangle.P0 = midPoint;
            farCircle.CurrentTriangle.P1 = farCircle.LastPoint;
            farCircle.CurrentTriangle.P2 = new Point3D(x + midPoint.X, y + midPoint.Y, midPoint.Z);

            nearCircle.CurrentTriangle = farCircle.CurrentTriangle.Clone(depth, true);

            if (!firstPass)
            {
                cylinder.Children.Add(CreateTriangleModel(materialGroup, farCircle.CurrentTriangle));
                cylinder.Children.Add(CreateTriangleModel(materialGroup, nearCircle.CurrentTriangle));

                cylinder.Children.Add(CreateTriangleModel(materialGroup, farCircle.CurrentTriangle.P2, farCircle.CurrentTriangle.P1, nearCircle.CurrentTriangle.P2));
                cylinder.Children.Add(CreateTriangleModel(materialGroup, nearCircle.CurrentTriangle.P2, nearCircle.CurrentTriangle.P1, farCircle.CurrentTriangle.P2));
            }
            else
            {
                farCircle.FirstPoint = farCircle.CurrentTriangle.P1;
                nearCircle.FirstPoint = nearCircle.CurrentTriangle.P1;
                firstPass = false;
            }

            farCircle.LastPoint = farCircle.CurrentTriangle.P2;
            nearCircle.LastPoint = nearCircle.CurrentTriangle.P2;
        }

        var model = new ModelVisual3D { Content = cylinder };
        return model;
    }



    public ModelVisual3D CreateSphere(Point3D center, double radius, int u, int v, Color color)
    {
        Model3DGroup spear = new Model3DGroup();

        if (u < 2 || v < 2)
            return null;
        Point3D[,] pts = new Point3D[u, v];
        for (int i = 0; i < u; i++)
        {
            for (int j = 0; j < v; j++)
            {
                pts[i, j] = GetPosition(radius,
                i * 180 / (u - 1), j * 360 / (v - 1));
                pts[i, j] += (Vector3D)center;
            }
        }

        Point3D[] p = new Point3D[4];
        for (int i = 0; i < u - 1; i++)
        {
            for (int j = 0; j < v - 1; j++)
            {
                p[0] = pts[i, j];
                p[1] = pts[i + 1, j];
                p[2] = pts[i + 1, j + 1];
                p[3] = pts[i, j + 1];
                spear.Children.Add(CreateTriangleFace(p[0], p[1], p[2], color));
                spear.Children.Add(CreateTriangleFace(p[2], p[3], p[0], color));
            }
        }
        ModelVisual3D model = new ModelVisual3D();
        model.Content = spear;
        return model;
    }

    private Point3D GetPosition(double radius, double theta, double phi)
    {
        Point3D pt = new Point3D();
        double snt = Math.Sin(theta * Math.PI / 180);
        double cnt = Math.Cos(theta * Math.PI / 180);
        double snp = Math.Sin(phi * Math.PI / 180);
        double cnp = Math.Cos(phi * Math.PI / 180);
        pt.X = radius * snt * cnp;
        pt.Y = radius * cnt;
        pt.Z = -radius * snt * snp;
        return pt;
    }

    public Model3DGroup CreateTriangleFace(Point3D p0, Point3D p1, Point3D p2, Color color)
    {
        MeshGeometry3D mesh = new MeshGeometry3D(); mesh.Positions.Add(p0); mesh.Positions.Add(p1); mesh.Positions.Add(p2); mesh.TriangleIndices.Add(0); mesh.TriangleIndices.Add(1); mesh.TriangleIndices.Add(2);

        Vector3D normal = VectorHelper.CalcNormal(p0, p1, p2);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);

        Material material = new DiffuseMaterial(
            new SolidColorBrush(color));
        GeometryModel3D model = new GeometryModel3D(
            mesh, material);
        Model3DGroup group = new Model3DGroup();
        group.Children.Add(model);
        return group;
    }

    private class VectorHelper
    {
        public static Vector3D CalcNormal(Point3D p0, Point3D p1, Point3D p2)
        {
            Vector3D v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
            Vector3D v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
            return Vector3D.CrossProduct(v0, v1);
        }
    }


}

public class CircleAssitor
{

    public CircleAssitor()
    {
        CurrentTriangle = new Triangle();
    }

    public Point3D FirstPoint { get; set; }
    public Point3D LastPoint { get; set; }
    public Triangle CurrentTriangle { get; set; }

}

public class Triangle
{


    public Point3D P0 { get; set; }
    public Point3D P1 { get; set; }
    public Point3D P2 { get; set; }

    public Triangle Clone(double z, bool switchP1andP2)
    {
        var newTriangle = new Triangle();
        newTriangle.P0 = GetPointAdjustedBy(this.P0, new Point3D(0, 0, z));

        var point1 = GetPointAdjustedBy(this.P1, new Point3D(0, 0, z));
        var point2 = GetPointAdjustedBy(this.P2, new Point3D(0, 0, z));

        if (!switchP1andP2)
        {
            newTriangle.P1 = point1;
            newTriangle.P2 = point2;
        }
        else
        {
            newTriangle.P1 = point2;
            newTriangle.P2 = point1;
        }
        return newTriangle;
    }

    private Point3D GetPointAdjustedBy(Point3D point, Point3D adjustBy)
    {
        var newPoint = new Point3D { X = point.X, Y = point.Y, Z = point.Z };
        newPoint.Offset(adjustBy.X, adjustBy.Y, adjustBy.Z);
        return newPoint;
    }

}

}

heres some more code - i think i've found a solution for you ...

Again, its a cobbled together example you'll have to refine.

Please note that the transform code has changed dramatically, i'm only just getting to grips with WPF 3D myself and i've realised I was doing quite a few things wrong. I was recreating the Transform3DGroup each time instead of just changing the angle of the Transform that was already applied.

Have a look at the code and note the altTransform group - what i'm doing is adding an extra transfomation to this alt group. I've then used the altTransform on a cylinder and sphere that would otherwise be in same position as one of the other cylinders and spheres. I've marked this cylinder green so that you can see which one it is.

My suggestion to you is that you create all your spheres and cylinders on the same axis and then transform them to your desired location.

I've also changed the sphere and cylinder sizes to what you requested in an earlier comment.

Have a look at the code and see what you think?

Cheers,

Andy

 <Window x:Class="wpfspin.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="600" Width="600">
<StackPanel>

    <Viewport3D Name="mainViewport" ClipToBounds="True" HorizontalAlignment="Stretch" Height="600">
        <Viewport3D.Camera>
            <PerspectiveCamera 
              LookDirection="0,0,-5"
              UpDirection="0,1,0"
              Position="0,0,10" 
              />
        </Viewport3D.Camera>
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <Model3DGroup x:Name="group3d">

                    <SpotLight Position="30,30,30" x:Name="mySpotLight" Color="Yellow"  InnerConeAngle="100" OuterConeAngle="1000" Range="100" />
                </Model3DGroup>
            </ModelVisual3D.Content>

        </ModelVisual3D>
    </Viewport3D>

</StackPanel>
</Window>

and heres the code behind ...

using System;
using System.Collections.Generic;
using System.Timers;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Threading;

namespace wpfspin
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        Init();
    }

    private Timer _timer;
    private readonly List<ModelVisual3D> _models = new List<ModelVisual3D>();
    private double _angle;
    private Transform3DGroup _transform3DGroup;
    private AxisAngleRotation3D _axisAngleRotation3D;
    private Transform3DGroup _altTransform;

    public void Init()
    {
        _models.Add(CreateSphere(new Point3D(0,0,0), 0.1, 10, 10, Colors.AliceBlue));
        _models.Add(CreateSphere(new Point3D(0,0,2), 0.1, 10, 10, Colors.AliceBlue));
        //notice that the following two spheres are created in the same place
        _models.Add(CreateSphere(new Point3D(0, 0, -2), 0.1, 10, 10, Colors.AliceBlue));
        _models.Add(CreateSphere(new Point3D(0, 0, -2), 0.1, 10, 10, Colors.AliceBlue));
        _models.Add(GetCylinder(GetSurfaceMaterial(Colors.Red), new Point3D(0,0,0), 0.02, 2));
        //notice that the following to cylinders are also created in the same place
        _models.Add(GetCylinder(GetSurfaceMaterial(Colors.Red), new Point3D(0, 0, 0), 0.02, -2));
        _models.Add(GetCylinder(GetSurfaceMaterial(Colors.Green), new Point3D(0, 0, 0), 0.02, -2));

        _transform3DGroup = new Transform3DGroup();


        _models.ForEach(x => x.Transform = _transform3DGroup);

        //heres my alt transform that i'm just using for the duplicate sphere and cylinder
        _altTransform = new Transform3DGroup();
        _models[3].Transform = _altTransform;
        _models[6].Transform = _altTransform;

        var rotateTransform3D = new RotateTransform3D { CenterX = 0, CenterZ = 0 };
        _axisAngleRotation3D = new AxisAngleRotation3D { Axis = new Vector3D(1, 1, 1), Angle = _angle };
        rotateTransform3D.Rotation = _axisAngleRotation3D;

        //i'm adding the animation rotation to both groups so that all my models spin
        _transform3DGroup.Children.Add(rotateTransform3D);
        _altTransform.Children.Add(rotateTransform3D);

        //but my alt transform gets an extra transformation
        var altRotate = new RotateTransform3D { CenterX = 0, CenterZ = 0 };
        var altAxis = new AxisAngleRotation3D { Axis = new Vector3D(0, 1, 1), Angle = 90 };
        altRotate.Rotation = altAxis;
        _altTransform.Children.Add(altRotate);

        _models.ForEach(x => mainViewport.Children.Add(x));
        _timer = new Timer(10);
        _timer.Elapsed += TimerElapsed;
        _timer.Enabled = true;
    }



    void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        Dispatcher.Invoke(DispatcherPriority.Normal, new Action<double>(Transform), 0.5d);
    }

    public MaterialGroup GetSurfaceMaterial(Color colour)
    {
        var materialGroup = new MaterialGroup();
        var emmMat = new EmissiveMaterial(new SolidColorBrush(colour));
        materialGroup.Children.Add(emmMat);
        materialGroup.Children.Add(new DiffuseMaterial(new SolidColorBrush(colour)));
        var specMat = new SpecularMaterial(new SolidColorBrush(Colors.White), 30);
        materialGroup.Children.Add(specMat);
        return materialGroup;
    }

    public ModelVisual3D GetCube(MaterialGroup materialGroup, Point3D point, Size3D size)
    {
        var farPoint = new Point3D(point.X - (size.X / 2), point.Y - (size.Y / 2), point.Z - (size.Z / 2));
        var nearPoint = new Point3D(point.X + (size.X / 2), point.Y + (size.Y / 2), point.Z + (size.Z / 2));

        var cube = new Model3DGroup();
        var p0 = new Point3D(farPoint.X, farPoint.Y, farPoint.Z);
        var p1 = new Point3D(nearPoint.X, farPoint.Y, farPoint.Z);
        var p2 = new Point3D(nearPoint.X, farPoint.Y, nearPoint.Z);
        var p3 = new Point3D(farPoint.X, farPoint.Y, nearPoint.Z);
        var p4 = new Point3D(farPoint.X, nearPoint.Y, farPoint.Z);
        var p5 = new Point3D(nearPoint.X, nearPoint.Y, farPoint.Z);
        var p6 = new Point3D(nearPoint.X, nearPoint.Y, nearPoint.Z);
        var p7 = new Point3D(farPoint.X, nearPoint.Y, nearPoint.Z);
        //front side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p3, p2, p6));
        cube.Children.Add(CreateTriangleModel(materialGroup, p3, p6, p7));
        //right side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p1, p5));
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p5, p6));
        //back side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p1, p0, p4));
        cube.Children.Add(CreateTriangleModel(materialGroup, p1, p4, p5));
        //left side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p0, p3, p7));
        cube.Children.Add(CreateTriangleModel(materialGroup, p0, p7, p4));
        //top side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p7, p6, p5));
        cube.Children.Add(CreateTriangleModel(materialGroup, p7, p5, p4));
        //bottom side triangles
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p3, p0));
        cube.Children.Add(CreateTriangleModel(materialGroup, p2, p0, p1));
        var model = new ModelVisual3D();
        model.Content = cube;
        return model;
    }

    private Model3DGroup CreateTriangleModel(MaterialGroup materialGroup, Triangle triangle)
    {
        return CreateTriangleModel(materialGroup, triangle.P0, triangle.P1, triangle.P2);
    }

    private Model3DGroup CreateTriangleModel(Material material, Point3D p0, Point3D p1, Point3D p2)
    {
        var mesh = new MeshGeometry3D();
        mesh.Positions.Add(p0);
        mesh.Positions.Add(p1);
        mesh.Positions.Add(p2);
        mesh.TriangleIndices.Add(0);
        mesh.TriangleIndices.Add(1);
        mesh.TriangleIndices.Add(2);
        var normal = CalculateNormal(p0, p1, p2);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);

        var model = new GeometryModel3D(mesh, material);

        var group = new Model3DGroup();
        group.Children.Add(model);
        return group;
    }

    private Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
    {
        var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
        var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
        return Vector3D.CrossProduct(v0, v1);
    }

    void Transform(double adjustBy)
    {
        _angle += adjustBy;
        _axisAngleRotation3D.Angle = _angle;
    }


    public ModelVisual3D GetCylinder(MaterialGroup materialGroup, Point3D midPoint, double radius, double depth)
    {
        var cylinder = new Model3DGroup();
        var nearCircle = new CircleAssitor();
        var farCircle = new CircleAssitor();

        var twoPi = Math.PI * 2;
        var firstPass = true;

        double x;
        double y;

        var increment = 0.1d;
        for (double i = 0; i < twoPi + increment; i = i + increment)
        {
            x = (radius * Math.Cos(i));
            y = (-radius * Math.Sin(i));

            farCircle.CurrentTriangle.P0 = midPoint;
            farCircle.CurrentTriangle.P1 = farCircle.LastPoint;
            farCircle.CurrentTriangle.P2 = new Point3D(x + midPoint.X, y + midPoint.Y, midPoint.Z);

            nearCircle.CurrentTriangle = farCircle.CurrentTriangle.Clone(depth, true);

            if (!firstPass)
            {
                cylinder.Children.Add(CreateTriangleModel(materialGroup, farCircle.CurrentTriangle));
                cylinder.Children.Add(CreateTriangleModel(materialGroup, nearCircle.CurrentTriangle));

                cylinder.Children.Add(CreateTriangleModel(materialGroup, farCircle.CurrentTriangle.P2, farCircle.CurrentTriangle.P1, nearCircle.CurrentTriangle.P2));
                cylinder.Children.Add(CreateTriangleModel(materialGroup, nearCircle.CurrentTriangle.P2, nearCircle.CurrentTriangle.P1, farCircle.CurrentTriangle.P2));
            }
            else
            {
                farCircle.FirstPoint = farCircle.CurrentTriangle.P1;
                nearCircle.FirstPoint = nearCircle.CurrentTriangle.P1;
                firstPass = false;
            }

            farCircle.LastPoint = farCircle.CurrentTriangle.P2;
            nearCircle.LastPoint = nearCircle.CurrentTriangle.P2;
        }

        var model = new ModelVisual3D { Content = cylinder };
        return model;
    }



    public ModelVisual3D CreateSphere(Point3D center, double radius, int u, int v, Color color)
    {
        Model3DGroup spear = new Model3DGroup();

        if (u < 2 || v < 2)
            return null;
        Point3D[,] pts = new Point3D[u, v];
        for (int i = 0; i < u; i++)
        {
            for (int j = 0; j < v; j++)
            {
                pts[i, j] = GetPosition(radius,
                i * 180 / (u - 1), j * 360 / (v - 1));
                pts[i, j] += (Vector3D)center;
            }
        }

        Point3D[] p = new Point3D[4];
        for (int i = 0; i < u - 1; i++)
        {
            for (int j = 0; j < v - 1; j++)
            {
                p[0] = pts[i, j];
                p[1] = pts[i + 1, j];
                p[2] = pts[i + 1, j + 1];
                p[3] = pts[i, j + 1];
                spear.Children.Add(CreateTriangleFace(p[0], p[1], p[2], color));
                spear.Children.Add(CreateTriangleFace(p[2], p[3], p[0], color));
            }
        }
        ModelVisual3D model = new ModelVisual3D();
        model.Content = spear;
        return model;
    }

    private Point3D GetPosition(double radius, double theta, double phi)
    {
        Point3D pt = new Point3D();
        double snt = Math.Sin(theta * Math.PI / 180);
        double cnt = Math.Cos(theta * Math.PI / 180);
        double snp = Math.Sin(phi * Math.PI / 180);
        double cnp = Math.Cos(phi * Math.PI / 180);
        pt.X = radius * snt * cnp;
        pt.Y = radius * cnt;
        pt.Z = -radius * snt * snp;
        return pt;
    }

    public Model3DGroup CreateTriangleFace(Point3D p0, Point3D p1, Point3D p2, Color color)
    {
        MeshGeometry3D mesh = new MeshGeometry3D(); mesh.Positions.Add(p0); mesh.Positions.Add(p1); mesh.Positions.Add(p2); mesh.TriangleIndices.Add(0); mesh.TriangleIndices.Add(1); mesh.TriangleIndices.Add(2);

        Vector3D normal = VectorHelper.CalcNormal(p0, p1, p2);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);
        mesh.Normals.Add(normal);

        Material material = new DiffuseMaterial(
            new SolidColorBrush(color));
        GeometryModel3D model = new GeometryModel3D(
            mesh, material);
        Model3DGroup group = new Model3DGroup();
        group.Children.Add(model);
        return group;
    }

    private class VectorHelper
    {
        public static Vector3D CalcNormal(Point3D p0, Point3D p1, Point3D p2)
        {
            Vector3D v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
            Vector3D v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
            return Vector3D.CrossProduct(v0, v1);
        }
    }


}

public class CircleAssitor
{

    public CircleAssitor()
    {
        CurrentTriangle = new Triangle();
    }

    public Point3D FirstPoint { get; set; }
    public Point3D LastPoint { get; set; }
    public Triangle CurrentTriangle { get; set; }

}

public class Triangle
{


    public Point3D P0 { get; set; }
    public Point3D P1 { get; set; }
    public Point3D P2 { get; set; }

    public Triangle Clone(double z, bool switchP1andP2)
    {
        var newTriangle = new Triangle();
        newTriangle.P0 = GetPointAdjustedBy(this.P0, new Point3D(0, 0, z));

        var point1 = GetPointAdjustedBy(this.P1, new Point3D(0, 0, z));
        var point2 = GetPointAdjustedBy(this.P2, new Point3D(0, 0, z));

        if (!switchP1andP2)
        {
            newTriangle.P1 = point1;
            newTriangle.P2 = point2;
        }
        else
        {
            newTriangle.P1 = point2;
            newTriangle.P2 = point1;
        }
        return newTriangle;
    }

    private Point3D GetPointAdjustedBy(Point3D point, Point3D adjustBy)
    {
        var newPoint = new Point3D { X = point.X, Y = point.Y, Z = point.Z };
        newPoint.Offset(adjustBy.X, adjustBy.Y, adjustBy.Z);
        return newPoint;
    }



}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM