Creating best matching plane using several(more than 3) points script

Creating best matching plane using several(more than 3) points script

caner-korkmaz
Participant Participant
1,010 Views
4 Replies
Message 1 of 5

Creating best matching plane using several(more than 3) points script

caner-korkmaz
Participant
Participant

Hello,

I'm trying to create a plane using several points.Reason for this is i'm working on surveyers point cloud data and using random 3 points in a surface doesn't always creates the best plane because of the slight surface inperfections or bad readings.Instead what i want is selecting several points in a what i know is a surface and calculating best matching plane.I know resulting plane could not intersect all the point but thats okay.I am not good at python but i tried chatgpt and i think the code it gave me could work.But it gives an error.I think the highligted section in the below code is the problem.If anyone gives me a direction iwill be greatfull.Thank you.


selected_points = []
for point in design.activeProduct.activeSelections:
if isinstance(point.entity, adsk.fusion.SketchPoint):
selected_points.append(point.entity.worldGeometry)

 

import adsk.core, adsk.fusion

def create_best_fit_plane():
# Get the current active document and root component
app = adsk.core.Application.get()
design = app.activeProduct
root_comp = design.rootComponent

# Get the selected sketch points
selected_points = []
for point in design.activeProduct.activeSelections:
if isinstance(point.entity, adsk.fusion.SketchPoint):
selected_points.append(point.entity.worldGeometry)

num_points = len(selected_points)

if num_points < 3:
# At least 3 points are required to create a plane
return

# Calculate the centroid of the selected points
centroid = adsk.core.Point3D.create(0, 0, 0)
for point in selected_points:
centroid += point
centroid /= num_points

# Calculate the covariance matrix
covariance = adsk.core.Matrix3D.create()
for point in selected_points:
x = point.x - centroid.x
y = point.y - centroid.y
z = point.z - centroid.z
covariance.data[0][0] += x * x
covariance.data[0][1] += x * y
covariance.data[0][2] += x * z
covariance.data[1][0] += y * x
covariance.data[1][1] += y * y
covariance.data[1][2] += y * z
covariance.data[2][0] += z * x
covariance.data[2][1] += z * y
covariance.data[2][2] += z * z

# Find the eigenvector corresponding to the smallest eigenvalue
eigenvalues, eigenvectors = covariance.eigenVectors()
min_eigenvalue = min(eigenvalues)
min_eigenvector = eigenvectors[eigenvalues.index(min_eigenvalue)]

# Create the plane through the centroid with the normal defined by the eigenvector
plane_input = root_comp.constructionPlanes.createInput()
plane_input.setByPlaneAndPoint(
adsk.core.Plane.create(centroid, min_eigenvector),
centroid
)
plane = root_comp.constructionPlanes.add(plane_input)

return plane

# Run the function to create the best-fit plane
create_best_fit_plane()

 

0 Likes
1,011 Views
4 Replies
Replies (4)
Message 2 of 5

felix85SA3
Participant
Participant
You can find the Object Reference Documentation at (Expand Objects) https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-7B5A90C8-E94C-48DA-B16B-430729B734DC
You need to do at least some minimal research to get that script working. Have a look at the Code Samples as well: https://help.autodesk.com/view/fusion360/ENU/?guid=SampleList
0 Likes
Message 3 of 5

BrianEkins
Mentor
Mentor

I think at the present time, ChatGPT isn't a dependable resource for creating Fusion programs. It will create code, but it will often have little errors that will be difficult for a new programmer to find and fix. It also seems that when it can't determine if Fusion has certain functionality, it just makes up functionality. For example, it calls the eigenVectors function on the Matrix3D object, but it doesn't support that function. It's also treating a Matrix3D object as a Python List, which it is not.

 

I suspect the algorithm it uses to find the points' average and the plane normal is valid, but it will take some work to convert it into usable code.

 

 

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
Message 4 of 5

kandennti
Mentor
Mentor

Hi @caner-korkmaz -San.

 

I asked ChatGPT how to do planar fitting without external modules, but they replied that it is difficult.
Therefore, we created a sample using numpy.

 

# Fusion360API Python script

import traceback
import adsk.core as core
import adsk.fusion as fusion

import os
import sys
import inspect
import random

script_path = os.path.abspath(inspect.getfile(inspect.currentframe()))
script_name = os.path.splitext(os.path.basename(script_path))[0]
script_dir = os.path.dirname(script_path)
sys.path.append(script_dir + "/site-packages")

import numpy as np

def run(context):
    ui: core.UserInterface = None
    try:
        app: core.Application = core.Application.get()
        ui = app.userInterface

        points = create_sample(200)
        dump_points(points, "sample data")

        create_fit_plane(points)

        ui.messageBox("Done")

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


def create_fit_plane(
    pointList: list[core.Point3D]
) -> fusion.ConstructionPlane:

    def fit_plane(pointList: list[core.Point3D]):
        point_cloud = np.array(pointList)

        com = np.sum(point_cloud, axis=0) / len(point_cloud)
        q = point_cloud - com
        Q = np.dot(q.T, q)
        la, vectors = np.linalg.eig(Q)
        plane_v = vectors.T[np.argmin(la)]

        return plane_v, com
    # *************

    normal, origin = fit_plane(
        [p.asArray() for p in pointList]
    )

    app: core.Application = core.Application.get()
    des: fusion.Design = app.activeProduct
    root: fusion.Component = des.rootComponent

    plane: core.Plane = core.Plane.create(
        core.Point3D.create(*origin.tolist()),
        core.Vector3D.create(*normal.tolist()),
    )

    baseFeat: fusion.BaseFeature = None
    if des.designType == fusion.DesignTypes.ParametricDesignType:
        baseFeat = root.features.baseFeatures.add()

    planes: fusion.ConstructionPlanes = root.constructionPlanes
    planeIpt: fusion.ConstructionPlaneInput = planes.createInput()
    constPlane: fusion.ConstructionPlane = None
    if baseFeat:
        try:
            baseFeat.startEdit()
            planeIpt.setByPlane(plane)
            constPlane = planes.add(planeIpt)
        except:
            pass
        finally:
            baseFeat.finishEdit()
    else:
        planeIpt.setByPlane(plane)
        constPlane = planes.add(planeIpt)
    constPlane.name = "Fit Plane"

    return constPlane


def create_sample(
    count: int
) -> list[core.Point3D]:

    return [
        core.Point3D.create(
            random.uniform(-10, 10),
            random.uniform(-10, 10),
            random.uniform(-1, 1),
        )
        for _ in range(count)
    ]


def dump_points(
    points: list,
    name: str = '',
) -> fusion.Sketch:

    app: core.Application = core.Application.get()
    des: fusion.Design = app.activeProduct
    root: fusion.Component = des.rootComponent

    skt: fusion.Sketch = root.sketches.add(
        root.xYConstructionPlane
    )

    if len(name) > 0:
        skt.name = name

    sktPoints: fusion.SketchPoints = skt.sketchPoints

    skt.isComputeDeferred = True
    [sktPoints.add(p) for p in points]
    skt.isComputeDeferred = False

    return skt

 

 

200 random sketch points are created and the plane obtained by plane fitting is created.

 

The fit_plane function that does the plane fitting is a modified version of one I found on the web, so I do not understand the content itself.
I think it is probably a simple least squares method.

 

The attached file also includes numpy, so perhaps you can run it in your environment.

 

 

As a side note, in the CAD software I use for my work (CATIA V5), there is a command to create such a plane.
It would be useful to have a similar command in Fusion360.

0 Likes
Message 5 of 5

karelQUS5R
Community Visitor
Community Visitor

i used the code above but nothing happens when I run the code...

dou you heve the same issue?

0 Likes