chinh nguyen van
chinh nguyen van

Reputation: 889

How to create a circle in 3D with known center point, radius and it is on a plane which perpendicular to a line in WPF?

How to create a circle in 3D with known center point, radius and it is on a plane which perpendicular to a line (vector) in WPF?

Upvotes: 3

Views: 6862

Answers (2)

L Chougrani
L Chougrani

Reputation: 736

old thread but still, if anyone comes accross. The "newb" Answer would work but is quitte lazy math and time consuming wize, here's why :

  • there are 2 for loops where really 1 is needed.
  • using transform after building a circle on (x,z) plane centered on (0,0,0) is overkill since you know the maths and vectors constraints.

I would suggest you implement it that way (i made it so it works for ellipses too, just set uSize = vSize for circles, u and v really just need to no be parallel for it to work but for mesh quality purposes i force them to be perpendicular):

public static Mesh CreateEllipse3D(Vector3 center, Vector3 normal, Vector3 u, 
                                   Vector3 v, float uSize, float vSize, int 
                                   subdiv = 30)
    {
        if (Math.Abs(Vector3.Dot(u, v)) != 0)
        {
            Debug.LogError("Vectors u and v must be orthogonals");
            return new Mesh();
        }
        var mesh = new Mesh();
        var vertices = new List<Vector3>();
        var normals = new List<Vector3>();
        var triangles = new List<int>();
        vertices.Add(center);
        float t = 2 * Mathf.PI / subdiv;
        int a = 0, b, c;
        for (int i = 0; i < subdiv; i++)
        {
            vertices.Add(center + u.normalized * uSize * Mathf.Cos(t * i) - 
            v.normalized * vSize * Mathf.Sin(t * i));
            
            b = i + 1;
            c = (i < (subdiv - 1)) ? i + 2 : 1;

            triangles.Add(a);
            triangles.Add(b);
            triangles.Add(c);

            normals.Add(normal);
            normals.Add(normal);
            normals.Add(normal);
        }

        mesh.vertices = vertices.ToArray();
        mesh.triangles = triangles.ToArray();
        mesh.normals = normals.ToArray();
        return mesh;
    }

Upvotes: 0

newb
newb

Reputation: 856

Following is an example based on the comment I posted earlier.

First we define a function to generate the model of the circle:

    /// <summary>
    /// Generates a model of a Circle given specified parameters
    /// </summary>
    /// <param name="radius">Radius of circle</param>
    /// <param name="normal">Vector normal to circle's plane</param>
    /// <param name="center">Center position of the circle</param>
    /// <param name="resolution">Number of slices to iterate the circumference of the circle</param>
    /// <returns>A GeometryModel3D representation of the circle</returns>
    private GeometryModel3D GetCircleModel(double radius, Vector3D normal, Point3D center, int resolution)
    {
        var mod = new GeometryModel3D();
        var geo = new MeshGeometry3D();

        // Generate the circle in the XZ-plane
        // Add the center first
        geo.Positions.Add(new Point3D(0, 0, 0));

        // Iterate from angle 0 to 2*PI
        double t = 2 * Math.PI / resolution;
        for (int i = 0; i < resolution; i++)
        {
            geo.Positions.Add(new Point3D(radius * Math.Cos(t * i), 0, -radius * Math.Sin(t * i)));
        }

        // Add points to MeshGeoemtry3D
        for (int i = 0; i < resolution; i++)
        {
            var a = 0;
            var b = i + 1;
            var c = (i < (resolution - 1)) ? i + 2 : 1;

            geo.TriangleIndices.Add(a);
            geo.TriangleIndices.Add(b);
            geo.TriangleIndices.Add(c);
        }

        mod.Geometry = geo;

        // Create transforms
        var trn = new Transform3DGroup();
        // Up Vector (normal for XZ-plane)
        var up = new Vector3D(0, 1, 0);
        // Set normal length to 1
        normal.Normalize();
        var axis = Vector3D.CrossProduct(up, normal); // Cross product is rotation axis
        var angle = Vector3D.AngleBetween(up, normal); // Angle to rotate
        trn.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(axis, angle)));
        trn.Children.Add(new TranslateTransform3D(new Vector3D(center.X, center.Y, center.Z)));

        mod.Transform = trn;

        return mod;
    }

Setting up our ViewPort3D:

    <Grid Background="Black">
        <Viewport3D Name="mainViewPort">
            <Viewport3D.Camera>
                <PerspectiveCamera NearPlaneDistance="0.1" FarPlaneDistance="100" UpDirection="0,1,0" Position="0,0,10" 
                               LookDirection="0,0,-1" FieldOfView="60" />
            </Viewport3D.Camera>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup>
                        <AmbientLight Color="#40FFFFFF" />
                        <DirectionalLight Color="#FFFFFF" Direction="1,-1,-1" />
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D>
    </Grid>

Then to test:

    private void AddCircleModel()
    {
        var mod = GetCircleModel(1.5, new Vector3D(0, 1, 1), new Point3D(0, -1, 0), 20);
        mod.Material = new DiffuseMaterial(Brushes.Silver);
        var vis = new ModelVisual3D() { Content = mod };
        mainViewPort.Children.Add(vis);
    }

Load up your window, call AddCircleModel(); and enjoy. Adjust view/parameters to your heart's content.

Upvotes: 5

Related Questions