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: 

Using API, how to Determine the Normal Vector from a given point in a solid to a face

11 REPLIES 11
SOLVED
Reply
Message 1 of 12
tperam
566 Views, 11 Replies

Using API, how to Determine the Normal Vector from a given point in a solid to a face

 

Using API, how to Determine the Normal Vector from a given point inside a solid to a face, the point not being at the face, preferably using C++.

 

Is there an object in Fusion 360 with this functionality?

 

Thurai

11 REPLIES 11
Message 2 of 12
tperam
in reply to: tperam

I tried the following, which did not work.

 

Face[IFace] = Faces->item(IFace);

with IFace ranging from 0 to Number of faces.

Ptr<SurfaceEvaluator> FaceSurfaceEvaluator;

FaceSurfaceEvaluator[IFace] = Face[IFace]->evaluator();

 

/* Output Normal Vector */
Ptr<Vector3D> normalVector;

 

/* A Point inside the Solid */
Ptr<Point3D> point =Point3d::create(0,0,0);

 

bool returnValue = FaceSurfaceEvaluator[IFace]->getNormalAtPoint(point, normalVector);

 

I was hoping normalVector will have the Normal Vector.

 

But it did not work.

 

Thurai

Message 3 of 12
nnikbin
in reply to: tperam

Usually if we find a vector with minimum distance from a point inside a body to its face, it will be normal to the face. It has exceptions but in most cases it works fine. Also for open faces it is possible to have no answer for finding a normal.

 

For finding the minimum distance we can use MeasureManager.measureMinimumDistance Method. The returned MeasureResults has the data for the minimum distance vector. But unfortunately it seems that it does not work for the point inside a single face BRepBody (it works for the outside points).

 

But there is a workaround. You can create a patch using BRepFace. Then the MeasureManager works correctly using the patch face. After your work is done you can delete the patch. In the following code I used OffsetFeature with zero offset to create the patch but perhaps there is some better methods. At the end of this method I checked if the vector is normal to face or not by using CrossProduct.

 

 

 

 

 

Ptr<Vector3D> Test::getNormalVectorToFace(Ptr<Point3D>& point, Ptr<BRepFace>& face)
{
	Ptr<Component> component = face->body()->parentComponent();
	
	Ptr<ObjectCollection> objectCollection = ObjectCollection::create();
	objectCollection->add(face);

	Ptr<OffsetFeatureInput> offsetInput = component->features()->offsetFeatures()->createInput(objectCollection, ValueInput::createByReal(0), FeatureOperations::NewBodyFeatureOperation);

	Ptr<OffsetFeature> offsetFeature = component->features()->offsetFeatures()->add(offsetInput);
	Ptr<BRepFace> offsetFace = offsetFeature->faces()->item(0);

	Ptr<MeasureManager> measureManager = Application::get()->measureManager();
	Ptr<MeasureResults> measureResult = measureManager->measureMinimumDistance(point, offsetFace);

	Ptr<Vector3D> vectorToFace = measureResult->positionTwo()->vectorTo(measureResult->positionOne());
	Ptr<Vector3D> normalAtFace;
	face->geometry()->evaluator()->getNormalAtPoint(measureResult->positionOne(), normalAtFace);

	Ptr<Vector3D> crossProduct = normalAtFace->crossProduct(vectorToFace);
	offsetFace->body()->deleteMe();

	if (crossProduct->length() < 0.000001)
	{
		vectorToFace->normalize();
		return vectorToFace;
	}
	else
	{
		return nullptr;
	}
}

 

 

 

 

 

Message 4 of 12
tperam
in reply to: nnikbin

Thanks nnikbin.


I will try you method.

 

Regards,
Thurai

Message 5 of 12
tperam
in reply to: tperam

Hi nnikbin.

I tried the Following as given below.

 

My Solid has 7 Faces. Your routine Ptr<Vector3D> NormalVectorToFace(Ptr<Point3D>& point, Ptr<BRepFace>& face),

worked for two out of 7 faces; Face 0 and 3, which are planer faces, and gave the following printouts;

 

IFace = 0, normalVector x = 0.000e+000, y = 0.000e+000, z = 1.000e+000

IFace = 3, normalVector x = 0.000e+000, y = 0.000e+000, z = - 1.000e+000.


But it did not work for the other faces; Face 1, 2, 4, 5 and 6. which are obtained by lofting the profiles with the planer faces Face 0 and 3.

 

I would like a method which works for all the faces.

 

Thanks,
Thurai


///////////////////////////////////////////////

Ptr<Vector3D> NormalVectorToFace(Ptr<Point3D>& point, Ptr<BRepFace>& face);


for(IFace = 0; IFace <= longeron.ComponentFaceCount - 1; IFace++)
{//IFace


ComponentFace[IFace] = ComponentFaces->item(IFace);

Ptr<Point3D> Point = Point3D::create(0,0,0);

normalVector[IFace] = NormalVectorToFace(Point, ComponentFace[IFace]);


fprintf(Com.out," \n\n IFace = %4d, normalVectorPoint[JPhi][0] x = %10.3e, y = %10.3e, z = %10.3e \n ",
IFace, longeron.normalVector[JPhi][IFace]->x(), longeron.normalVector[JPhi][IFace]->y(), longeron.normalVector[JPhi][IFace]->z());

 

}//IFace


Ptr<Vector3D> NormalVectorToFace(Ptr<Point3D>& point, Ptr<BRepFace>& face)
{//NormalVectorToFace(
Ptr<Component> component = face->body()->parentComponent();

Ptr<ObjectCollection> objectCollection = ObjectCollection::create();
objectCollection->add(face);

Ptr<OffsetFeatureInput> offsetInput = component->features()->offsetFeatures()->createInput(objectCollection, ValueInput::createByReal(0), FeatureOperations::NewBodyFeatureOperation);

Ptr<OffsetFeature> offsetFeature = component->features()->offsetFeatures()->add(offsetInput);
Ptr<BRepFace> offsetFace = offsetFeature->faces()->item(0);

Ptr<MeasureManager> measureManager = Application::get()->measureManager();
Ptr<MeasureResults> measureResult = measureManager->measureMinimumDistance(point, offsetFace);

Ptr<Vector3D> vectorToFace = measureResult->positionTwo()->vectorTo(measureResult->positionOne());
Ptr<Vector3D> normalAtFace;
face->geometry()->evaluator()->getNormalAtPoint(measureResult->positionOne(), normalAtFace);

Ptr<Vector3D> crossProduct = normalAtFace->crossProduct(vectorToFace);
offsetFace->body()->deleteMe();

if (crossProduct->length() < 0.000001)
{
vectorToFace->normalize();
return vectorToFace;
}
else
{
return nullptr;
}
}//end NormalVectorToFace()

Message 6 of 12
nnikbin
in reply to: tperam

Hi @tperam 

 

I created the following code (the add-in is attached as a zip file) based on the same algorithm in my previous reply.

 

After running the add-in the add-in prompt to select a sketch point, then a body. The add-in creates sketch lines which are corrsponding to minimum distance lines from the selected sketch point to the faces of the selected body. You can easily convert them to unique length vectors.

 

#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>
#include <CAM/CAMAll.h>

using namespace adsk::core;
using namespace adsk::fusion;
using namespace adsk::cam;

Ptr<Application> app;
Ptr<UserInterface> ui;

void drawNormalsToBodyFaces(Ptr<SketchPoint>& sketchPoint, Ptr<BRepBody>& body);

extern "C" XI_EXPORT bool run(const char* context)
{
	app = Application::get();
	if (!app)
		return false;

	ui = app->userInterface();
	if (!ui)
		return false;

	Ptr<Selection> sketchPointSelection = ui->selectEntity("Select a point", "SketchPoints");
	if (!sketchPointSelection)
	{
		ui->messageBox("Selection Canceled");
		return false;
	}

	Ptr<Selection> bodySelection = ui->selectEntity("Select a body", "Bodies");
	if (!bodySelection)
	{
		ui->messageBox("Selection Canceled");
		return false;
	}

	Ptr<SketchPoint> sketchPoint = sketchPointSelection->entity();
	Ptr<BRepBody> body = bodySelection->entity();

	drawNormalsToBodyFaces(sketchPoint, body);
	body->opacity(0.6);

	return true;
}

#ifdef XI_WIN

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hmodule, DWORD reason, LPVOID reserved)
{
	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

void drawNormalsToBodyFaces(Ptr<SketchPoint>& sketchPoint, Ptr<BRepBody>& body)
{
	Ptr<Sketch> sketch = sketchPoint->parentSketch();

	for (Ptr<BRepFace> face : body->faces())
	{
		Ptr<Component> component = body->parentComponent();

		Ptr<ObjectCollection> objectCollection = ObjectCollection::create();
		objectCollection->add(face);

		Ptr<OffsetFeatureInput> offsetInput = component->features()->offsetFeatures()->createInput(objectCollection, ValueInput::createByReal(0), FeatureOperations::NewBodyFeatureOperation);

		Ptr<OffsetFeature> offsetFeature = component->features()->offsetFeatures()->add(offsetInput);
		Ptr<BRepFace> offsetFace = offsetFeature->faces()->item(0);

		Ptr<MeasureManager> measureManager = Application::get()->measureManager();
		Ptr<MeasureResults> measureResult = measureManager->measureMinimumDistance(sketchPoint->worldGeometry(), offsetFace);

		Ptr<Point3D> sketchSpacePoint1 = sketch->modelToSketchSpace(measureResult->positionOne());
		Ptr<Point3D> sketchSpacePoint2 = sketch->modelToSketchSpace(measureResult->positionTwo());

		Ptr<SketchLine> sketchLine = sketch->sketchCurves()->sketchLines()->addByTwoPoints(sketchSpacePoint2, sketchSpacePoint1);
		Ptr<Vector3D> normalAtFace;
		face->geometry()->evaluator()->getNormalAtPoint(measureResult->positionOne(), normalAtFace);

		Ptr<Vector3D> vectorToFace = measureResult->positionTwo()->vectorTo(measureResult->positionOne());

		Ptr<Vector3D> crossProduct = normalAtFace->crossProduct(vectorToFace);
		offsetFace->body()->deleteMe();

		sketchLine->isConstruction(crossProduct->length() > 0.000001);
	}
}
#endif // XI_WIN

 

You can check if the end point of the lines are outside of each face (are on extended faces) and remove them if you do not need normals to extended faces.

 

For demonstration purpose, the minimum distance lines which are not normal to faces are created as construction sketch lines (dashed lines) and the opacity of the selected body is set to 0.60.

 

Here is an snapshot of the output from the add-in:

 

42.jpg

Could you try this add-in with your model and let me know the result?

Message 7 of 12
nnikbin
in reply to: tperam

The attached file is the file that contains the body and the sketch point that I tested the add-in using them in my previous reply.

Message 8 of 12
nnikbin
in reply to: tperam

Here is another output:

43.jpg

Message 9 of 12
tperam
in reply to: nnikbin

Hi nnikbin,

                  Thanks for your effort.

 

I am getting the following Syntax Errors at 

 

for (Ptr<BRepFace> face : body->faces())
{

 

TestNormalToFace.cpp
1>TestNormalToFace.cpp(69): error C2143: syntax error : missing ',' before ':'
1>TestNormalToFace.cpp(70): error C2143: syntax error : missing ';' before '{'
1>TestNormalToFace.cpp(76): warning C4482: nonstandard extension used: enum 'adsk::fusion::FeatureOperations' used in qualified name
1>

 

I am using Visual Studio Came with Fusion 360.

 

Thanks,

Thurai

Message 10 of 12
nnikbin
in reply to: tperam

@tperam 

Did you download the add-in zip file attached to one of my previous replies? Do you get any error in compiling it?

Have you used the following using statements in your code?

using namespace adsk::core;
using namespace adsk::fusion;

 

Message 11 of 12
tperam
in reply to: nnikbin

Hi nnikbin,

                  I unzipped the files.

 

I have the 

 

using namespace adsk::core;
using namespace adsk::fusion;

statements in the source code.

 

I am changing the code as a work around.

 

Thanks

Thurai

 

Message 12 of 12
tperam
in reply to: tperam

Hi nnikbin,

                   It worked Okay for all the 7 faces.

 

Thanks a lot nnilbin.

 

Good job.

 

Thurai

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