Curves workbench

Post here for help on using FreeCAD's graphical user interface (GUI).
Forum rules
and Helpful information
IMPORTANT: Please click here and read this first, before asking for help

Also, be nice to others! Read the FreeCAD code of conduct!
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Curves workbench

Post by edwilliams16 »

I'm still unclear as to whether you actually want the isocurves (which are curves, not surfaces, they just embody the surface's coordinate lines), or they are a means to an end.
To just generate the cubes in location, per my previous post and onekk's:

Code: Select all

doc = App.ActiveDocument
obj = doc.getObject("GDMLEllipsoid")
shp = obj.Shape
sub = obj.getSubObject("Face1")
surf = sub.Surface
umin, umax, vmin, vmax = sub.ParameterRange
nu = 4
ulist = [i*(umax - umin)/nu + umin for i in range(nu+1)]
nv = 4
vlist = [i*(vmax - vmin)/nv + vmin for i in range(nv+1)]
boxLength = 2
for u in ulist:
    for v in vlist:
        base = surf.value(u, v)
        normal = surf.normal(u,v)
        rot = App.Rotation(App.Vector(0,0,1), normal)
        box = Part.makeBox(boxLength, boxLength, boxLength)
        centerpl = App.Placement(-(boxLength/2) * App.Vector(1,1,1), App.Rotation()) # put origin at center of cube
        box.Placement = App.Placement(base, rot) * centerpl
        Part.show(box)

Screen Shot 2022-12-03 at 9.18.02 AM.png
Screen Shot 2022-12-03 at 9.18.02 AM.png (43.42 KiB) Viewed 1079 times
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Curves workbench

Post by keithsloan52 »

My Bad : Just looked at code and the IsoCurve object has a property Face, was staring me in the eye all along.
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Curves workbench

Post by keithsloan52 »

edwilliams16 wrote: Sat Dec 03, 2022 7:19 pm I'm still unclear as to whether you actually want the isocurves (which are curves, not surfaces, they just embody the surface's coordinate lines), or they are a means to an end.
They are a means to an end, I am trying to code a macro that in effect takes two parameters, i.e two things are selected and then the macro is run.

The two things are 1) an IsoCurve created by the curves workbench 2) A GDML Object.
The purpose is to create an Array of GDML Objects at the intersection points of IsoLines
The end result is the Array of GDML Objects.
Creating a surface and using IsoCurves is just a means to an end the Array of Objects, after that
the surface and IsoCurve could be deleted and it only of use if the user wants to change things that they have any purpose after creation.

I only want to pass to the macro a selected IsoCurve Object and a GDML Object, I want to avoid having to pass anything else.

Code: Select all

>>> doc = App.getDocument("IsoCurve_Test")
>>> obj = doc.getObject("IsoCurve")
>>> shp = obj.Shape
>>> doc = App.getDocument("IsoCurve_Test")
>>> obj = doc.getObject("IsoCurve")
>>> shp = obj.Shape
>>> ### End command Std_SendToPythonConsole
>>> print(obj.Face)
(<Part::PartFeature>, ['Face1'])
>>> print(obj.Face[0])
<Part::PartFeature>
>>> print(obj.Face[1])
['Face1']
>>> faceObj=obj.Face[0]
>>> faceName = obj.Face[1]
>>> sub=faceObj.getSubObject(faceName)
>>> print(dir(sub))
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
>>> print(sub.Surface)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'Surface'
If I try something similar to your code with the info in the IsoCurve.

I think the Face info in the IsoCurve i.e. IsoCurve.Face[0] is a Linked Object and I suspect I have to do something to be able to access the "source" object and have done something similar in the past but cannot remember the details.
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Curves workbench

Post by keithsloan52 »

edwilliams16 wrote: Sat Dec 03, 2022 7:19 pm I'm still unclear as to whether you actually want the isocurves (which are curves, not surfaces, they just embody the surface's coordinate lines), or they are a means to an end.
Think of the surface creation and isocurves as just part of the design/specification process, the desired end result is an array of GDML Objects
at the intersection of the isolines.
User avatar
onekk
Veteran
Posts: 6144
Joined: Sat Jan 17, 2015 7:48 am
Contact:

Re: Curves workbench

Post by onekk »

Notvsure if it help but when you accessed the subobject you have an error saying that a tuple object has not this property.

But as a tuple sub[0] will return the object, maybe this is the Face?

or try to print the sub object and see what is.

For other considerations sadly I'm on mobile now.

Regards

Carlo D.
GitHub page: https://github.com/onekk/freecad-doc.
- In deep articles on FreeCAD.
- Learning how to model with scripting.
- Various other stuffs.

Blog: https://okkmkblog.wordpress.com/
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Curves workbench

Post by keithsloan52 »

onekk wrote: Sat Dec 03, 2022 9:36 pm Notvsure if it help but when you accessed the subobject you have an error saying that a tuple object has not this property.

But as a tuple sub[0] will return the object, maybe this is the Face?

or try to print the sub object and see what is.

For other considerations sadly I'm on mobile now.

Regards

Carlo D.
Thanks Carlo

Least that gets me a Surface from the IsoCurve but code is not there, will look again tomorrow.
223B433D-F4EE-46BF-AA85-3FCE1F8DB8D3.jpeg
223B433D-F4EE-46BF-AA85-3FCE1F8DB8D3.jpeg (129.52 KiB) Viewed 958 times
Main function is now

Code: Select all

def arrayIsoIntersections(iso, obj):
	import DraftGeomUtils as dgmu
	print("Array at IsoCurve Intersections")
	print("IsoCurve")
	print(dir(iso))
	print(f"Number U {iso.NumberU} Number V {iso.NumberV}")
	print("IsoCurve Shape")
	print(dir(iso.Shape))
	print(f"Number of isoCurve Faces {len(iso.Shape.Faces)}")
	parent = getParent(obj)
	lnkObj = False
	faceObj = iso.Face[0]
	faceName = iso.Face[1]
	sub = faceObj.getSubObject(faceName)
	surf = sub[0].Surface
	for u in range(0, iso.NumberU):
		for v in range(0, iso.NumberV):
			base = surf.value(u,v)
			normal = surf.normal(u,v)
			rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), normal)
			if lnkObj: 
				new = parent.newObject("App::Link","Link_"+obj.Name)			
				new.LinkedObject = obj
				new.LinkPlacement.Base = base
				new.LinkPlacement.Rotation = rot
			else:
				obj.Placement.Base = base
				obj.Placement.Rotation = rot
				lnkObj = True			
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Curves workbench

Post by edwilliams16 »

keithsloan52 wrote: Sat Dec 03, 2022 8:38 pm

I think the Face info in the IsoCurve i.e. IsoCurve.Face[0] is a Linked Object and I suspect I have to do something to be able to access the "source" object and have done something similar in the past but cannot remember the details.
Let me say it AGAIN. An isoCurve has NO FACES. It consists only of edges, because it is just a collection of curves. (If you check the Faces property, you'll find it is an empty list.)

If you pass your routine the ellipsoidal surface and the object and somehow provide an array of u-values and v-values, you can place the objects at the intersection in the manner I just posted.

It is possible to find the normal direction at the intersection of two isolines. It is the cross product of the tangent vectors of the two curves - but this is a very roundabout way of obtaining information that was directly available from the surface, itself. The only purpose IMO for the Isolines object is for visualization.
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Curves workbench

Post by keithsloan52 »

edwilliams16 wrote: Sat Dec 03, 2022 10:25 pm Let me say it AGAIN. An isoCurve has NO FACES. It consists only of edges, because it is just a collection of curves. (If you check the Faces property, you'll find it is an empty list.)
Sorry but I don't think you are correct - If you look at the isoCurve Objects Properties it has a property called "Face" it is a list of two items.
1F6E020C-3A27-419A-9755-E7437AA13CE9.jpeg
1F6E020C-3A27-419A-9755-E7437AA13CE9.jpeg (73.93 KiB) Viewed 933 times
GDMLEllipsoid and "Face1"

It has at least info on the Face used to create the IsoCurve.

The isoCurve objects Shape just has edges.
edwilliams16
Veteran
Posts: 3106
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Curves workbench

Post by edwilliams16 »

keithsloan52 wrote: Sat Dec 03, 2022 10:33 pm
GDMLEllipsoid and "Face1"

It has at least info on the Face used to create the IsoCurve.

The isoCurve objects Shape just has edges.
Agreed. I thought we were talking about the (shape) content of isoCurves and trying to find the normal vector from them.

You can retrieve the face of the ellipsoid with

Code: Select all

doc = App.ActiveDocument
obj = doc.getObject("IsoCurve")
ob, fa = obj.Face
face = getattr(ob.Shape, fa[0])
Then you can continue with my code. You will of course retrieve obj via selection.
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: Curves workbench

Post by keithsloan52 »

edwilliams16 wrote: Sat Dec 03, 2022 11:08 pm
keithsloan52 wrote: Sat Dec 03, 2022 10:33 pm
GDMLEllipsoid and "Face1"

It has at least info on the Face used to create the IsoCurve.

The isoCurve objects Shape just has edges.
Agreed. I thought we were talking about the (shape) content of isoCurves and trying to find the normal vector from them.

You can retrieve the face of the ellipsoid with

Code: Select all

doc = App.ActiveDocument
obj = doc.getObject("IsoCurve")
ob, fa = obj.Face
face = getattr(ob.Shape, fa[0])
Then you can continue with my code. You will of course retrieve obj via selection.
Okay I tried that but not sure how I get to access the surface
currently have surf = face.Faces[0].Surface

Code: Select all

def arrayIsoIntersections(iso, obj):
	print("Array at IsoCurve Intersections")
	print("IsoCurve")
	print(dir(iso))
	print(f"Number U {iso.NumberU} Number V {iso.NumberV}")
	print("IsoCurve Shape")
	print(dir(iso.Shape))
	print(f"Number of isoCurve Faces {len(iso.Shape.Faces)}")
	parent = getParent(obj)
	lnkObj = False
	faceObj, faceNames = iso.Face
	#faceObj = iso.Face[0]
	#faceName = iso.Face[1]
	face = getattr(faceObj.Shape, faceNames[0])
	#sub = faceObj.getSubObject(faceName)
	#surf = sub[0].Surface
	surf = face.Faces[0].Surface
	for u in range(0, iso.NumberU):
		for v in range(0, iso.NumberV):
			base = surf.value(u,v)
			normal = surf.normal(u,v)
			rot = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), normal)
			if lnkObj: 
				new = parent.newObject("App::Link","Link_"+obj.Name)			
				new.LinkedObject = obj
				new.LinkPlacement.Base = base
				new.LinkPlacement.Rotation = rot
			else:
				obj.Placement.Base = base
				obj.Placement.Rotation = rot
				lnkObj = True	
See also python console

Code: Select all

>>> doc = App.getDocument("IsoCurve_Test")
>>> obj = doc.getObject("IsoCurve")
>>> shp = obj.Shape
>>> ### End command Std_SendToPythonConsole
>>> ob, fa = obj.Face
>>> face = getattr(ob.Shape, fa[0])
>>> print(dir(face))
['Area', 'BoundBox', 'CenterOfGravity', 'CenterOfMass', 'CompSolids', 'Compounds', 'Content', 'Edges', 'Faces', 'Length', 'Mass', 'MatrixOfInertia', 'MemSize', 'Module', 'Orientation', 'OuterWire', 'ParameterRange', 'Placement', 'PrincipalProperties', 'ShapeType', 'Shells', 'Solids', 'StaticMoments', 'SubShapes', 'Surface', 'Tag', 'Tolerance', 'TypeId', 'Vertexes', 'Volume', 'Wire', 'Wires', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'addWire', 'ancestorsOfType', 'applyRotation', 'applyTranslation', 'check', 'childShapes', 'cleaned', 'common', 'complement', 'copy', 'countElement', 'countSubElements', 'curvatureAt', 'curveOnSurface', 'cut', 'cutHoles', 'defeaturing', 'derivative1At', 'derivative2At', 'distToShape', 'dumpContent', 'dumpToString', 'exportBinary', 'exportBrep', 'exportBrepToString', 'exportIges', 'exportStep', 'exportStl', 'extrude', 'findPlane', 'fix', 'fixTolerance', 'fuse', 'generalFuse', 'getAllDerivedFrom', 'getElement', 'getElementTypes', 'getFaces', 'getFacesFromSubElement', 'getLines', 'getLinesFromSubElement', 'getPoints', 'getTolerance', 'getUVNodes', 'globalTolerance', 'hashCode', 'importBinary', 'importBrep', 'importBrepFromString', 'inTolerance', 'isClosed', 'isCoplanar', 'isDerivedFrom', 'isEqual', 'isInfinite', 'isInside', 'isNull', 'isPartOfDomain', 'isPartner', 'isSame', 'isValid', 'limitTolerance', 'makeChamfer', 'makeEvolved', 'makeFillet', 'makeHalfSpace', 'makeOffset', 'makeOffset2D', 'makeOffsetShape', 'makeParallelProjection', 'makePerspectiveProjection', 'makeShapeFromMesh', 'makeThickness', 'makeWires', 'mirror', 'multiFuse', 'normalAt', 'nullify', 'oldFuse', 'optimalBoundingBox', 'overTolerance', 'project', 'proximity', 'read', 'reflectLines', 'removeInternalWires', 'removeShape', 'removeSplitter', 'replaceShape', 'restoreContent', 'reverse', 'reversed', 'revolve', 'rotate', 'rotated', 'scale', 'scaled', 'section', 'sewShape', 'slice', 'slices', 'tangentAt', 'tessellate', 'toNurbs', 'transformGeometry', 'transformShape', 'transformed', 'translate', 'translated', 'validate', 'valueAt', 'writeInventor']
>>> print(face.SubShapes)
[<Wire object at 0x60000b217280>]
>>> print(face.Faces)
[<Face object at 0x60000b215780>]
>>> print(dir(face.Faces))
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> print(dir(face.Faces[0]))
['Area', 'BoundBox', 'CenterOfGravity', 'CenterOfMass', 'CompSolids', 'Compounds', 'Content', 'Edges', 'Faces', 'Length', 'Mass', 'MatrixOfInertia', 'MemSize', 'Module', 'Orientation', 'OuterWire', 'ParameterRange', 'Placement', 'PrincipalProperties', 'ShapeType', 'Shells', 'Solids', 'StaticMoments', 'SubShapes', 'Surface', 'Tag', 'Tolerance', 'TypeId', 'Vertexes', 'Volume', 'Wire', 'Wires', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'addWire', 'ancestorsOfType', 'applyRotation', 'applyTranslation', 'check', 'childShapes', 'cleaned', 'common', 'complement', 'copy', 'countElement', 'countSubElements', 'curvatureAt', 'curveOnSurface', 'cut', 'cutHoles', 'defeaturing', 'derivative1At', 'derivative2At', 'distToShape', 'dumpContent', 'dumpToString', 'exportBinary', 'exportBrep', 'exportBrepToString', 'exportIges', 'exportStep', 'exportStl', 'extrude', 'findPlane', 'fix', 'fixTolerance', 'fuse', 'generalFuse', 'getAllDerivedFrom', 'getElement', 'getElementTypes', 'getFaces', 'getFacesFromSubElement', 'getLines', 'getLinesFromSubElement', 'getPoints', 'getTolerance', 'getUVNodes', 'globalTolerance', 'hashCode', 'importBinary', 'importBrep', 'importBrepFromString', 'inTolerance', 'isClosed', 'isCoplanar', 'isDerivedFrom', 'isEqual', 'isInfinite', 'isInside', 'isNull', 'isPartOfDomain', 'isPartner', 'isSame', 'isValid', 'limitTolerance', 'makeChamfer', 'makeEvolved', 'makeFillet', 'makeHalfSpace', 'makeOffset', 'makeOffset2D', 'makeOffsetShape', 'makeParallelProjection', 'makePerspectiveProjection', 'makeShapeFromMesh', 'makeThickness', 'makeWires', 'mirror', 'multiFuse', 'normalAt', 'nullify', 'oldFuse', 'optimalBoundingBox', 'overTolerance', 'project', 'proximity', 'read', 'reflectLines', 'removeInternalWires', 'removeShape', 'removeSplitter', 'replaceShape', 'restoreContent', 'reverse', 'reversed', 'revolve', 'rotate', 'rotated', 'scale', 'scaled', 'section', 'sewShape', 'slice', 'slices', 'tangentAt', 'tessellate', 'toNurbs', 'transformGeometry', 'transformShape', 'transformed', 'translate', 'translated', 'validate', 'valueAt', 'writeInventor']
Result still looks like
4B647C8D-B52C-47AB-8C15-0AAE14864689.jpeg
4B647C8D-B52C-47AB-8C15-0AAE14864689.jpeg (100.05 KiB) Viewed 849 times
Test file before
IsoCurve_Test.FCStd
(161.27 KiB) Downloaded 15 times
Latest Macro
Test-ISO-B.FCMacro
(2.47 KiB) Downloaded 11 times
Post Reply