2 from mathutils import Vector
5 def __init__(self, v=None):
10 self.co = Vector((0, 0, 0))
13 def __init__(self, e=None):
20 def __init__(self, p=None):
25 self.normal = p.normal
27 self.normal = Vector((0, 0, 1))
30 def __init__(self, m=None):
32 self.vertices = [Vertex(v) for v in m.vertices]
33 self.edges = [Edge(e) for e in m.edges]
34 self.polygons = [Polygon(p) for p in m.polygons]
37 e.vertices = [self.vertices[i] for i in e.edge.vertices]
39 for p in self.polygons:
40 p.vertices = [self.vertices[i] for i in p.polygon.vertices]
41 p.edges = [self.edges[m.loops[i].edge_index] for i in p.polygon.loop_indices]
52 def __init__(self, mesh):
54 min_coords = tuple(map(min, zip(*(v.co for v in self.mesh.vertices))))
55 max_coords = tuple(map(max, zip(*(v.co for v in self.mesh.vertices))))
56 self.center = (Vector(min_coords)+Vector(max_coords))/2
57 self.radius = max(max_coords[i]-min_coords[i] for i in range(3))/2
59 def calculate_center_error(self):
61 for v in self.mesh.vertices:
64 p = self.center+p*self.radius
67 return error/len(self.mesh.vertices)
69 def calculate_radius_error(self):
71 for v in self.mesh.vertices:
72 error += self.radius-(v.co-self.center).length
73 return error/len(self.mesh.vertices)
75 def calculate_fit_error(self):
77 for v in self.mesh.vertices:
78 error += (self.radius-(v.co-self.center).length)**2
79 return math.sqrt(error/len(self.mesh.vertices))
81 def fit(self, epsilon=1e-3):
83 error = self.calculate_radius_error()
85 return self.calculate_fit_error()
87 self.center -= self.calculate_center_error()
88 self.radius -= self.calculate_radius_error()
91 def __init__(self, mesh):
93 min_coords = tuple(map(min, zip(*(v.co for v in self.mesh.vertices))))
94 max_coords = tuple(map(max, zip(*(v.co for v in self.mesh.vertices))))
95 self.ranges = [[min_coords[i], max_coords[i]] for i in range(3)]
99 self.dimensions = tuple(r[1]-r[0] for r in self.ranges)
100 self.center = Vector(tuple((r[0]+r[1])/2 for r in self.ranges))
102 def calculate_side_errors(self):
103 errors = [[0, 0], [0, 0], [0, 0]]
104 counts = [[0, 0], [0, 0], [0, 0]]
105 for v in self.mesh.vertices:
106 distances = tuple((r[0]-v.co[i], v.co[i]-r[1]) for i, r in enumerate(self.ranges))
108 for i, d in enumerate(distances):
110 if d[j]>distances[side[0]][side[1]]:
113 errors[side[0]][side[1]] += distances[side[0]][side[1]]
114 counts[side[0]][side[1]] += 1
119 errors[i][j] /= counts[i][j]
123 def calculate_fit_error(self):
125 for v in self.mesh.vertices:
126 distances = tuple((r[0]-v.co[i], v.co[i]-r[1]) for i, r in enumerate(self.ranges))
127 error += max(max(*d) for d in distances)**2
128 return math.sqrt(error/len(self.mesh.vertices))
130 def fit(self, epsilon=1e-3):
132 errors = self.calculate_side_errors()
133 max_error = max(max(map(abs, e)) for e in errors)
134 if max_error<epsilon:
136 return self.calculate_fit_error()
143 self.ranges[i][j] += e
146 def export(self, context, out_file):
147 obj = context.active_object
149 raise Exception("Can only export mesh data")
151 mesh = Mesh(obj.data)
153 from .outfile import open_output
154 out_file = open_output(out_file)
156 shape_type = obj.shape_type
157 if shape_type=="AUTO":
159 errors.append(("SPHERE", SphereFit(mesh).fit()))
160 errors.append(("BOX", BoxFit(mesh).fit()))
161 errors.sort(key=lambda x:x[1])
163 shape_type = errors[0][0]
165 if shape_type=="SPHERE":
166 sphere = SphereFit(mesh)
169 use_center = sphere.center.length>sphere.radius/1e5
171 out_file.begin("transformed")
172 out_file.write("translate", *sphere.center)
174 out_file.begin("sphere")
175 out_file.write("radius", sphere.radius)
181 elif shape_type=="BOX":
185 use_center = box.center.length>min(box.dimensions)/1e5
187 out_file.begin("transformed")
188 out_file.write("translate", *box.center)
190 out_file.begin("box")
191 out_file.write("dimensions", *box.dimensions)