Goldberg polyhedron macro failure

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
edwilliams16
Veteran
Posts: 3112
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Goldberg polyhedron macro failure

Post by edwilliams16 »

@wangnick
Bringing out the big guns! Since the Goldberg Polyhedron is convex, it is the convex hull of its vertices.

Code: Select all

import Part
import numpy as np
from scipy.spatial import ConvexHull

def dualofsphtriangle(n1, n2, n3):
    ''' Dual vertex location from triangle'''
    return (n1.cross(n2) + n2.cross(n3) + n3.cross(n1))/n1.dot(n2.cross(n3))


def vToNpArray(vectorList):
    return np.array([[v.x, v.y, v.z] for v in vectorList])


def vListToWirelist(vectorList):
    hull = ConvexHull(vToNpArray(vectorList))
    wires = []
    for simplex in hull.simplices:
        points = [vectorList[i] for i in simplex]
        points.append(vectorList[simplex[0]])# make cyclic
        wires.append(Part.makePolygon(points))
    return wires

def makeDual(shp , r):
    dualVertexLocs  = [(r**2) * dualofsphtriangle(*[v.Point for v in face.Vertexes[0:3]]) for face in shp.Faces]
    wires = vListToWirelist(dualVertexLocs)
    faces = [Part.Face(wire) for wire in wires]
    shell = Part.Shell(faces).removeSplitter()
    return shell 


doc = App.ActiveDocument
shp = doc.getObject('GoldbergGeodesicSphere').Shape
r = shp.Vertexes[0].Point.Length
shell = makeDual(shp , r)
Part.show(shell, "GoldbergPolyhedron")
#dual of dual
shelldual = makeDual(shell , r)
Part.show(shelldual, "DualDualGoldbergGeodesicSphere")

The above computes the dual of the Geodesic Polyhedron, then the dual of that, which gets you back to the starting point!
Attachments
goldbergtest04_EW.FCStd
(56.61 KiB) Downloaded 16 times
edwilliams16
Veteran
Posts: 3112
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Goldberg polyhedron macro failure

Post by edwilliams16 »

@wangnick

It bothered me that I hadn't really used the topology of the dual. So I figured out another way.
The following will take the dual of the selected polyhedron about a sphere of radius r.

Code: Select all

import FreeCAD as App
import FreeCADGui as Gui
import Part

doc = App.ActiveDocument

def dualofsphtriangle(v1, v2, v3):
    ''' Dual vertex location from triangle'''
    return (v1.cross(v2) + v2.cross(v3) + v3.cross(v1))/v1.dot(v2.cross(v3))

def sharesEdge(face1, face2):
    shared = False
    for edge1 in face1.Edges:
        shared = shared or any([edge1.isSame(edge2) for edge2 in face2.Edges])
    return shared

def dualWireFromVertex(shp, v):
    sharedFaces = shp.ancestorsOfType(v, Part.Face) #faces containing v
    # vertices dual to faces containing v
    dualVs = [(r**2)*dualofsphtriangle(*[v.Point for v in face.Vertexes[0:3]])  for face in sharedFaces]
    dualEdges = []
    for i, face1 in enumerate(sharedFaces):
        for j, face2 in enumerate(sharedFaces):
            if j > i and sharesEdge(face1, face2):
                #create edges dual to edges shared between adjacent faces.
                dualEdges.append(Part.Edge(Part.LineSegment(dualVs[i], dualVs[j])))

    dualWire = Part.Wire(Part.sortEdges(dualEdges)[0]) #join sorted edges into a wire 
    return dualWire

def makeDual(shp, r):
    dualWires = [dualWireFromVertex(shp, v) for v in shp.Vertexes]
    dualFaces = [Part.Face(wire) for wire in dualWires] 
    dualShell = Part.Shell(dualFaces)
    dualSolid = Part.Solid(dualShell)
    return (dualWires, dualFaces, dualShell, dualSolid)

#shp = doc.getObject("GoldbergGeodesicSphere").Shape
obj = Gui.Selection.getSelection()[0]
shp = obj.Shape
r = shp.Vertexes[0].Point.Length #this isn't necessarily the optimum choice - but it only affects the scale.
dualWires, dualFaces, dualShell, dualSolid = makeDual(shp, r)
Part.show(dualShell, 'Dual' + obj.Name)



Here's a dodecahedron and its dual the icosahedron.
Screen Shot 2022-11-24 at 10.51.59 AM.png
Screen Shot 2022-11-24 at 10.51.59 AM.png (12.5 KiB) Viewed 386 times
Attachments
goldbergtest05_EW.FCStd
(59.95 KiB) Downloaded 13 times
wangnick
Posts: 19
Joined: Thu Nov 03, 2022 9:27 am

Re: Goldberg polyhedron macro failure

Post by wangnick »

It bothered me that I hadn't really used the topology of the dual. So I figured out another way.
The following will take the dual of the selected polyhedron about a sphere of radius r.
I understand. shape.ancestorsOfType() and shape.isSame() together with the shape.Faces and shape.Edges iterables give access to the topology of the shape. Nicely done indeed!

Kind regards,
Sebastian

PS: I'm still asking myself why Shapes don't implement __hash__ and __eq__ such that you could use them directly in Python sets and dicts ...
edwilliams16
Veteran
Posts: 3112
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Goldberg polyhedron macro failure

Post by edwilliams16 »

wangnick wrote: Thu Nov 24, 2022 9:18 pm
I understand. shape.ancestorsOfType() and shape.isSame() together with the shape.Faces and shape.Edges iterables give access to the topology of the shape.

PS: I'm still asking myself why Shapes don't implement __hash__ and __eq__ such that you could use them directly in Python sets and dicts ...
Yes, that's how it works.

As to the latter, could it be related to TNP?
wangnick
Posts: 19
Joined: Thu Nov 03, 2022 9:27 am

Re: Goldberg polyhedron macro failure

Post by wangnick »

edwilliams16 wrote: Thu Nov 24, 2022 10:38 pm As to the latter, could it be related to TNP?
I don't fully understand TNP yet, but I don't think so. Shape.hashCode() and Shape.isSame() exist and can be made good use of, so why not have Shape implement def __hash__(self): return self.hashCode() and def __eq__(self,other): return self.isSame(other) and thereby make shapes eligible to become elements of set() and keys of dict() is Python macros? I guess its some design decision of the devs, but I fail to see the reason.

Kind regards,
Sebastian
Post Reply