Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save whinee/3b9ac29a1f83b7c79d4f9087db47aa00 to your computer and use it in GitHub Desktop.

Select an option

Save whinee/3b9ac29a1f83b7c79d4f9087db47aa00 to your computer and use it in GitHub Desktop.
FreeCAD Programmatically Bisect Two Back-to-Back Symmetrical Triangles Into Four Right Triangles

FreeCAD Programmatically Bisect Two Back-to-Back Symmetrical Triangles Into Four Right Triangles

I am a fucking noob. I don't know how to do this properly, so I created a programmatic way to do this for-realium :3 :3 :3

import math, traceback, Part
from collections import namedtuple
Point = namedtuple("Point", "x y z")
def line_intersect_2d(p1, p2, p3, p4):
x1, y1, x2, y2, x3, y3, x4, y4 = p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y
a, b, c, d, e, f = (x1*y2 - y1*x2), (x3 - x4), (y3 - y4), (x1 - x2), (y1 - y2), (x3*y4 - y3*x4)
denom = d*c-e*b
if abs(denom) < 1e-9:
raise ValueError("Lines are parallel")
return FreeCAD.Vector((a*b-d*f)/denom, (a*c-e*f)/denom, p1.z)
def centroid(pts):
n = len(pts)
return FreeCAD.Vector(
sum(p.x for p in pts)/n,
sum(p.y for p in pts)/n,
sum(p.z for p in pts)/n,
)
def make_polygons(ls_ls):
return [Part.Face(Part.makePolygon(ls)) for ls in ls_ls]
def _main(original_shape_name, new_shape_name, tol):
sel = Gui.Selection.getSelectionEx()
orig_faces = FreeCAD.ActiveDocument.getObject(original_shape_name).Shape.Faces
face_count = sum(1 for s in sel for name in s.SubElementNames if name.startswith("Face"))
assert face_count == 2, f"Expected exactly 2 faces, got {face_count} — reselect!"
pts = set()
for s in sel:
for name, sub in zip(s.SubElementNames, s.SubObjects, strict=True):
if name.startswith("Face"):
for v in sub.Vertexes:
pts.add(Point(x=v.X, y=v.Y, z=v.Z))
assert len(pts) == 4, f"Expected exactly 4 unique points, got {len(pts)} — check for a shared-edge mismatch or duplicate geometry!"
cx, cy = (sum(p.x for p in pts) / 4), (sum(p.y for p in pts) / 4)
p0, p1, p2, p3 = sorted(pts, key=lambda p: math.atan2(p.y - cy, p.x - cx))
I = line_intersect_2d(p0, p2, p1, p3)
f1, f2, f3, f4 = make_polygons(([p0, p1, I, p0], [p1, p2, I, p1], [p2, p3, I, p2], [p3, p0, I, p3]))
c1, c2 = centroid([p0, p2, p1]), centroid([p1, p3, p0])
kept_faces = []
for f in orig_faces:
fc = centroid([FreeCAD.Vector(v.X, v.Y, v.Z) for v in f.Vertexes])
if fc.distanceToPoint(c1) < tol or fc.distanceToPoint(c2) < tol:
continue
kept_faces.append(f)
face_difference = (len(orig_faces) - len(kept_faces))
assert face_difference == 2, f"Expected exactly difference of unique points, got {face_difference} — Change `tol` parameter!"
Part.show(Part.makeCompound([*kept_faces, f1, f2, f3, f4]), new_shape_name)
def main(original_shape_name, new_shape_name, tol):
try:
_main(original_shape_name=original_shape_name, new_shape_name=new_shape_name, tol=tol)
except BaseException:
traceback.print_exc()
main(original_shape_name="CKB_119t_002", new_shape_name="CKB_119t_", tol=0.7)
print()
import math,traceback as B,Part as H
from collections import namedtuple as C
U,E,A=C('Point','x y z'),len,sum
def V(g,h,i,j):
B,C,D,E,F,G,H,I=g.x,g.y,h.x,h.y,i.x,i.y,j.x,j.y;J,K,L,M,N,O=B*E-C*D,F-H,G-I,B-D,C-E,F*I-G*H;A=M*L-N*K
if abs(A)<1e-09:raise ValueError('Lines are parallel')
return FreeCAD.Vector((J*K-M*O)/A,(J*L-N*O)/A,g.z)
def J(a):B=E(a);return FreeCAD.Vector(A(A.x for A in a)/B,A(A.y for A in a)/B,A(A.z for A in a)/B)
def W(a):return[H.Face(H.makePolygon(A))for A in a]
def D(a,b,c):
T='Face';M=Gui.Selection.getSelectionEx();N=FreeCAD.ActiveDocument.getObject(a).Shape.Faces;O=A(1 for A in M for B in A.SubElementNames if B.startswith(T));assert O==2,f"Expected exactly 2 faces, got {O} — reselect!";B=set()
for P in M:
for(X,Y)in zip(P.SubElementNames,P.SubObjects,strict=True):
if X.startswith(T):
for K in Y.Vertexes:B.add(U(x=K.X,y=K.Y,z=K.Z))
assert E(B)==4,f"Expected exactly 4 unique points, got {E(B)} — check for a shared-edge mismatch or duplicate geometry!";Z,d=A(A.x for A in B)/4,A(A.y for A in B)/4;C,D,F,G=sorted(B,key=lambda p:math.atan2(p.y-d,p.x-Z));I=V(C,F,D,G);e,f,g,h=W(([C,D,I,C],[D,F,I,D],[F,G,I,F],[G,C,I,G]));i,j=J([C,F,D]),J([D,G,C]);L=[]
for Q in N:
R=J([FreeCAD.Vector(A.X,A.Y,A.Z)for A in Q.Vertexes])
if R.distanceToPoint(i)<c or R.distanceToPoint(j)<c:continue
L.append(Q)
S=E(N)-E(L);assert S==2,f"Expected exactly difference of unique points, got {S} — Change `tol` parameter!";H.show(H.makeCompound([*L,e,f,g,h]),b)
def main(original,new,tol):
try:D(original,new,tol)
except BaseException:B.print_exc()
main(original='CKB_119t_003',new='CKB_119t_',tol=.7)
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment