Created
December 30, 2025 23:25
-
-
Save stephensmitchell/b8b2bf0391bdcf40713e2a777069a910 to your computer and use it in GitHub Desktop.
AREA MOMENTS REPORT (CM)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from __future__ import division | |
| import sys | |
| import math | |
| from AlibreScript import * | |
| # --- USER CONFIGURATION --- | |
| TARGET_ID = 69 | |
| Units.Current = UnitTypes.Centimeters | |
| # --- SCRIPT CONSTANTS --- | |
| ScriptName = "Targeted Area Moments - Full Data" | |
| ScriptVersion = "1.6" | |
| CURVE_SEGMENTS = 360 | |
| VERTEX_TOLERANCE = 1e-6 | |
| # --- REPLACED LOGIC --- | |
| def run(): | |
| part = CurrentPart() | |
| if part is None: | |
| print "No part found." | |
| return | |
| faces = part.GetFaces() | |
| if not faces: | |
| print "No faces found." | |
| return | |
| try: | |
| target_face = faces[TARGET_ID] | |
| except IndexError: | |
| print "Error: Face ID %d does not exist. Total faces: %d" % (TARGET_ID, len(faces)) | |
| return | |
| raw_area = target_face.GetArea() | |
| # Your Math Logic | |
| if raw_area > 1000: | |
| actual_area_cm2 = raw_area / 100.0 | |
| else: | |
| actual_area_cm2 = raw_area | |
| # Calculate Full Moments using the original engine | |
| shape_type, props, alibre_area = analyze_face_geometry(target_face, CURVE_SEGMENTS) | |
| if props is not None: | |
| # Scale properties based on your logic area vs calculated area | |
| # This ensures all derived values (I, S, J) follow your custom area scaling | |
| scale_factor = actual_area_cm2 / props.area | |
| props.area = actual_area_cm2 | |
| props.Ix_centroid *= (scale_factor**2) | |
| props.Iy_centroid *= (scale_factor**2) | |
| props.Ixy_centroid *= (scale_factor**2) | |
| props.J_centroid = props.Ix_centroid + props.Iy_centroid | |
| face_name = target_face.Name if target_face.Name else "Face<%d>" % TARGET_ID | |
| print "=" * 70 | |
| print " COMPLETE AREA MOMENTS REPORT (CM)" | |
| print "=" * 70 | |
| print "Face: %s" % face_name | |
| print "Raw Alibre Area: %.6f" % raw_area | |
| print "Applied Logic Area: %.6f cm^2" % actual_area_cm2 | |
| print "" | |
| print generate_face_section(face_name, props, actual_area_cm2, "cm") | |
| print "=" * 70 | |
| else: | |
| print "Could not analyze geometry for face %d" % TARGET_ID | |
| # --- FULL GEOMETRY ENGINE (RESTORED FROM ORIGINAL) --- | |
| def vec_subtract(a, b): return [a[0]-b[0], a[1]-b[1], a[2]-b[2]] | |
| def vec_cross(a, b): return [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]] | |
| def vec_dot(a, b): return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] | |
| def vec_length(v): return math.sqrt(v[0]**2 + v[1]**2 + v[2]**2) | |
| def vec_normalize(v): | |
| L = vec_length(v) | |
| return [v[0]/L, v[1]/L, v[2]/L] if L > 1e-12 else [0.0, 0.0, 0.0] | |
| class PolygonProperties: | |
| def __init__(self, vertices_2d, shape_name="Polygon"): | |
| self.shape_type = shape_name | |
| self.vertices = vertices_2d | |
| self.n = len(vertices_2d) | |
| self._compute_all() | |
| def _compute_all(self): | |
| verts = self.vertices | |
| n = self.n | |
| signed_area, cx, cy = 0.0, 0.0, 0.0 | |
| for i in range(n): | |
| j = (i + 1) % n | |
| cross = verts[i][0] * verts[j][1] - verts[j][0] * verts[i][1] | |
| signed_area += cross | |
| cx += (verts[i][0] + verts[j][0]) * cross | |
| cy += (verts[i][1] + verts[j][1]) * cross | |
| self.area = abs(signed_area * 0.5) | |
| factor = 1.0 / (6.0 * (signed_area if abs(signed_area) > 1e-12 else 1.0)) | |
| self.centroid_x, self.centroid_y = cx * factor, cy * factor | |
| Ix, Iy, Ixy = 0.0, 0.0, 0.0 | |
| for i in range(n): | |
| j = (i + 1) % n | |
| x0, y0 = verts[i]; x1, y1 = verts[j] | |
| cross = x0 * y1 - x1 * y0 | |
| Ix += (y0**2 + y0*y1 + y1**2) * cross | |
| Iy += (x0**2 + x0*x1 + x1**2) * cross | |
| Ixy += (x0*y1 + 2*x0*y0 + 2*x1*y1 + x1*y0) * cross | |
| self.Ix_centroid = abs(Ix / 12.0) - self.area * self.centroid_y**2 | |
| self.Iy_centroid = abs(Iy / 12.0) - self.area * self.centroid_x**2 | |
| self.Ixy_centroid = (Ixy / 24.0) - self.area * self.centroid_x * self.centroid_y | |
| self.J_centroid = self.Ix_centroid + self.Iy_centroid | |
| # Principal Moments | |
| I_avg = (self.Ix_centroid + self.Iy_centroid) / 2.0 | |
| I_diff = (self.Ix_centroid - self.Iy_centroid) / 2.0 | |
| R = math.sqrt(I_diff**2 + self.Ixy_centroid**2) | |
| self.I_max = I_avg + R | |
| self.I_min = max(0.0, I_avg - R) | |
| self.theta_principal_deg = math.degrees(0.5 * math.atan2(-2.0 * self.Ixy_centroid, self.Ix_centroid - self.Iy_centroid)) | |
| # Radii of Gyration | |
| self.rx = math.sqrt(max(0, self.Ix_centroid / self.area)) | |
| self.ry = math.sqrt(max(0, self.Iy_centroid / self.area)) | |
| # Section Modulus (min) | |
| y_rel = [v[1] - self.centroid_y for v in verts] | |
| x_rel = [v[0] - self.centroid_x for v in verts] | |
| self.Sx_min = self.Ix_centroid / max(abs(min(y_rel)), abs(max(y_rel))) | |
| self.Sy_min = self.Iy_centroid / max(abs(min(x_rel)), abs(max(x_rel))) | |
| def project_to_2d(points_3d): | |
| if len(points_3d) < 3: return [] | |
| p0 = points_3d[0] | |
| v1 = vec_normalize(vec_subtract(points_3d[1], p0)) | |
| v2 = vec_normalize(vec_subtract(points_3d[2], p0)) | |
| normal = vec_normalize(vec_cross(v1, v2)) | |
| u_axis = v1 | |
| v_axis = vec_cross(normal, u_axis) | |
| return [[vec_dot(vec_subtract(p, p0), u_axis), vec_dot(vec_subtract(p, p0), v_axis)] for p in points_3d] | |
| def analyze_face_geometry(face, num_segments): | |
| try: | |
| verts = face.GetVertices() | |
| pts_3d = [[v.X, v.Y, v.Z] for v in verts] | |
| pts_2d = project_to_2d(pts_3d) | |
| shape_name = "Polygon (%d sides)" % len(pts_2d) | |
| return shape_name, PolygonProperties(pts_2d, shape_name), face.GetArea() | |
| except: | |
| return "Unknown", None, face.GetArea() | |
| def fmt(value, decimals=6): | |
| return ("%%.%df" % decimals) % value if value is not None else "N/A" | |
| def generate_face_section(face_name, p, area, unit): | |
| u1, u2, u3, u4 = unit, unit+"^2", unit+"^3", unit+"^4" | |
| return "\n".join([ | |
| " %s (%s)" % (face_name, p.shape_type), | |
| " " + "-" * 40, | |
| " Area: %s %s" % (fmt(p.area), u2), | |
| " Ix-x: %s %s" % (fmt(p.Ix_centroid), u4), | |
| " Iy-y: %s %s" % (fmt(p.Iy_centroid), u4), | |
| " Ixy: %s %s" % (fmt(p.Ixy_centroid), u4), | |
| " J: %s %s" % (fmt(p.J_centroid), u4), | |
| " rx-x: %s %s" % (fmt(p.rx), u1), | |
| " ry-y: %s %s" % (fmt(p.ry), u1), | |
| " Sx-x (min): %s %s" % (fmt(p.Sx_min), u3), | |
| " Sy-y (min): %s %s" % (fmt(p.Sy_min), u3), | |
| " I (max): %s %s" % (fmt(p.I_max), u4), | |
| " I (min): %s %s" % (fmt(p.I_min), u4), | |
| " Angle: %s deg" % (fmt(p.theta_principal_deg, 2)) | |
| ]) | |
| run() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ====================================================================== | |
| COMPLETE AREA MOMENTS REPORT (CM) | |
| ====================================================================== | |
| Face: Face<70> | |
| Raw Alibre Area: 40.393149 | |
| Applied Logic Area: 40.393149 cm^2 | |
| Face<70> (Polygon (68 sides)) | |
| ---------------------------------------- | |
| Area: 40.393149 cm^2 | |
| Ix-x: 1015.007063 cm^4 | |
| Iy-y: 1465.689893 cm^4 | |
| Ixy: -642.240400 cm^4 | |
| J: 2480.696956 cm^4 | |
| rx-x: 5.012803 cm | |
| ry-y: 6.023753 cm | |
| Sx-x (min): 124.963115 cm^3 | |
| Sy-y (min): 156.657131 cm^3 | |
| I (max): 1920.974282 cm^4 | |
| I (min): 559.722674 cm^4 | |
| Angle: 54.67 deg | |
| ====================================================================== |
Author
stephensmitchell
commented
Dec 30, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment