Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

3 REPLIES 3
Reply
Message 1 of 4
caner-korkmaz
231 Views, 3 Replies

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

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()

 

3 REPLIES 3
Message 2 of 4
felix85SA3
in reply to: caner-korkmaz

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
Message 3 of 4
BrianEkins
in reply to: caner-korkmaz

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 4
kandennti
in reply to: caner-korkmaz

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.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report