[Bug?] pointOnFace in rare cases returns a point at a corner of the face

[Bug?] pointOnFace in rare cases returns a point at a corner of the face

nnikbin
Collaborator Collaborator
419 Views
2 Replies
Message 1 of 3

[Bug?] pointOnFace in rare cases returns a point at a corner of the face

nnikbin
Collaborator
Collaborator

BRepFace.pointOnFace in some rare cases returns a point at a corner of the face. For example it returns (40, 40, 0) for the following face. The related file is attached.

54.png

It seems to be in contrast to what is promised in the relevant document:

 

Returns a sample point guaranteed to lie on the face's surface, within the face's boundaries, and not on a boundary edge.

 

It may not seem a big deal but for an add-in which depends on the accuracy of this property it could mean the potential for wrong functionality.

 

0 Likes
420 Views
2 Replies
  • bug
Replies (2)
Message 2 of 3

kandennti
Mentor
Mentor

HI @nnikbin .

 

I'm sorry, but I don't see this as a bug.

 

At least the "Shape Manager" seems to consider a point on an edge to be on a surface.
You can check this by using the isParameterOnFace method.

 

https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-e6682eb7-48f8-472d-9872-71da3c5ba8c8 

# Fusion360API Python script
import adsk.core, adsk.fusion, traceback

def run(context):
    ui = adsk.core.UserInterface.cast(None)
    try:
        app :adsk.fusion.Application = adsk.core.Application.get()
        ui = app.userInterface
        des :adsk.fusion.Design = app.activeProduct
        root :adsk.fusion.Component = des.rootComponent

        face :adsk.fusion.BRepFace = root.bRepBodies[0].faces[0]
        pointOnFace :adsk.core.Point3D = face.pointOnFace
        eva :adsk.core.SurfaceEvaluator = face.evaluator
        _, prm = eva.getParameterAtPoint(pointOnFace)

        ui.messageBox(f'{eva.isParameterOnFace(prm)}')

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

 

 

If it is not convenient on the edge, I think there is no other way but to create such a mechanism, so I made one.

# Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import random

def run(context):
    ui = adsk.core.UserInterface.cast(None)
    try:
        app :adsk.fusion.Application = adsk.core.Application.get()
        ui = app.userInterface
        des :adsk.fusion.Design = app.activeProduct
        root :adsk.fusion.Component = des.rootComponent

        # Point On Face
        face :adsk.fusion.BRepFace = root.bRepBodies[0].faces[0]
        pointOnFace :adsk.core.Point3D = face.pointOnFace
        dumpPoint(pointOnFace, root, 'Point On Face')

        # Point On Face  Exclude On The Edge
        for _ in range(10):
            excludeOnEdge = getPointOnFace_ExcludeOnEdge(face)
            dumpPoint(excludeOnEdge, root, 'Exclude On The Edge')

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

def getPointOnFace_ExcludeOnEdge(
    face :adsk.fusion.BRepFace) -> adsk.core.Point3D:

    def getUniqueParameter(
        minPrm :adsk.core.Point2D,
        maxPrm :adsk.core.Point2D) -> adsk.core.Point2D:

        return adsk.core.Point2D.create(
            random.uniform(minPrm.x, maxPrm.x),
            random.uniform(minPrm.y, maxPrm.y))

    def isPointOnEdges(
        pnt :adsk.core.Point3D,
        toleranceEdgeDistance :float = 0.1) -> bool:

        for edge in face.edges:
            if isPointOnEdge(pnt, edge, toleranceEdgeDistance):
                return True

        return False

    def isPointOnEdge(
        pnt :adsk.core.Point3D,
        edge :adsk.fusion.BRepEdge,
        toleranceEdgeDistance :float) -> bool:

        eva :adsk.core.CurveEvaluator3D = edge.evaluator
        _, prm = eva.getParameterAtPoint(pnt)

        _, prmPnt = eva.getPointAtParameter(prm)
        return pnt.isEqualToByTolerance(prmPnt, toleranceEdgeDistance)

    # ***

    pnt :adsk.core.Point3D = face.pointOnFace
    if not isPointOnEdges(pnt):
        return pnt

    surfEva :adsk.core.SurfaceEvaluator = face.evaluator
    bound :adsk.core.BoundingBox2D = surfEva.parametricRange()

    while True:
        prm = getUniqueParameter(bound.minPoint, bound.maxPoint)

        if not surfEva.isParameterOnFace(prm):
            continue

        _, pnt = surfEva.getPointAtParameter(prm)

        if not isPointOnEdges(pnt):
            return pnt


def dumpPoint(
    pnt :adsk.core.Point3D,
    comp :adsk.fusion.Component,
    name :str):

    skt :adsk.fusion.Sketch = comp.sketches.add(comp.xYConstructionPlane)
    skt.name = name
    skt.sketchPoints.add(pnt)

 

Since it can be done in a random position, the result will change every time you run it.
There may be a better algorithm.

Message 3 of 3

nnikbin
Collaborator
Collaborator

Hi @kandennti , Thank you for your reply and your code. Actually after finding the rare case issue, I used the almost same method in my code based on this post without even using face.pointOnFace. I think based on its random nature it is almost impossible that this method to return a point on an edge.

 

In my opinion it is a bug. I think the whole point of face.pointOnFace is to return a point which is guaranteed to be within the face's boundaries (not on the boundary) because the boundary conditions causes problems in many algorithms (for example offsetting the edges inward or outward). If we want to find a point on a face and we do not care if it is on an edge or not, we can just use a point on edge or corner and easily find it.