/
testcut1.py
229 lines (212 loc) · 7.8 KB
/
testcut1.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import maya.cmds as mc
import maya.mel as mm
import random
kMoveAwayXDistance = 10
def form_cutting_planes_for_object(obj, num):
"num = number of separations in each direction, result in num - 1 planes"
def random_percentage():
"random a percentage from 90% - 110%"
a = random.randint(-10000, 10000)
a /= 100000.0
return 1 + a
def random_angle():
"random an angle to tilt, from 0 - 35 degrees"
a = random.randint(0, 35000)
a /= 1000.0
return a
res = []
bbox = mc.exactWorldBoundingBox(obj)
xmin = bbox[0]
ymin = bbox[1]
zmin = bbox[2]
xmax = bbox[3]
ymax = bbox[4]
zmax = bbox[5]
# yz planes
for i in range(1, num):
x = xmin + i * ((xmax - xmin) / num)
x *= random_percentage()
# todo add randomness in rotation
p = {'pc': [x, 0, 0], 'ro': [random_angle(), 90, random_angle()]}
res.append(p)
# xz planes
for j in range(1, num):
y = ymin + j *((ymax - ymin) / num)
y *= random_percentage()
p = {'pc': [0, y, 0], 'ro': [-90, random_angle(), random_angle()]}
res.append(p)
# xy planes
for k in range(1, num):
z = zmin + k * ((zmax - zmin) / num)
z *= random_percentage()
p = {'pc': [0, 0, z], 'ro': [random_angle(), random_angle(), 0]}
res.append(p)
return res
# input: one object, total volume, generated cutting planes, list of ratios interested, error threshold
# return: on success, return a list of separated parts moved kMoveAwayXDistance in x direction
# on failure, return a dictionary contains key 'bad_cut_objs' -> list of object that cannot
# match any ratios, and 'ratios_remaining' -> list of remaining ratios,
# and 'good_cut_objs' -> list of objects found
def cut_object_with_planes_and_ratios(obj, volume_total, planes, ratios, threshold):
# create a list of booleans indicating ratios found or not
ratio_found = []
for r in ratios: ratio_found.append(0)
ratios.sort()
results = []
# a list of objects that volume cannot be match anymore
bad_cut_objs = []
all_found = False
# initially we have only one object to cut
objs_to_cut = [obj]
# loop all cut planes
for plane in planes:
# store all object result from this cut
objs_for_next_plane_iteration = []
# for each object in world
for i in range(len(objs_to_cut)):
#print 'cut object: ' + objs_to_cut[i]
mc.select(objs_to_cut[i], r = True)
# cut
mc.polyCut(pc = plane['pc'], ro = plane['ro'], ef = True, eo = [0, 0, 0])
# fill hole
mc.select(objs_to_cut[i], r = True)
mc.polyCloseBorder()
# separate
# if number of pieces < 2, means the plane and the object did not have intersection
if mc.polyEvaluate(objs_to_cut[i], shell = True) < 2:
# add back this object
objs_for_next_plane_iteration.append(objs_to_cut[i])
# continue with other objs_to_cut
continue
parts = mc.polySeparate(objs_to_cut[i])
# add these parts to future objs to cut
objs_for_next_plane_iteration.extend(parts[0:-1])
# for each parts
for j in range(len(parts) - 1):
this_volume_ratio = mm.eval('meshVolume(\"' + parts[j] + '\")') / volume_total
# check volume
first_unfound_volume = True
for k in range(len(ratios)):
if ratio_found[k] == 0:
# this part's volume is less than the smallest volume unfound
if first_unfound_volume and this_volume_ratio + threshold < ratios[k]:
print 'bad volume found, save', this_volume_ratio
objs_for_next_plane_iteration.remove(parts[j])
bad_cut_objs.append(parts[j])
break
# got match
elif abs(this_volume_ratio - ratios[k]) < threshold:
print 'volume found: ', this_volume_ratio
# dup the object
temp = mc.duplicate(parts[j])
mc.select(temp[0], r = True)
# move away the duplication
mc.move(kMoveAwayXDistance, 0, 0, temp[0])
# add it to the result list
results.append(temp[0])
# remove the current object
mc.delete(parts[j])
objs_for_next_plane_iteration.remove(parts[j])
# mark volume as found
ratio_found[k] = 1
# if all parts found
if ratio_found.count(0) == 0:
all_found = True
break
if first_unfound_volume:
first_unfound_volume = False
if all_found: break
if all_found: break
objs_to_cut = objs_for_next_plane_iteration
if all_found:
# todo move back all result obj
print 'FFFFFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUUUUUUUU'
return results
elif len(objs_to_cut) == 0:
# no more cuttings but not all_found
break
# objs_to_cut might be empty due to insufficient planes OR empty
if len(objs_to_cut) != 0:
bad_cut_objs.extend(objs_to_cut)
ratios_remaining = []
for i in range(len(ratio_found)):
if ratio_found[i] == 0:
ratios_remaining.append(ratios[i])
return {'bad_cut_objs': bad_cut_objs, 'ratios_remaining': ratios_remaining, 'good_cut_objs': results}
# object should not contain more than one shell
def rec(object_name, volume_total, cut_planes, volume_ratios, threshold, result, loop_num):
# base cases
if loop_num == 0:
print 'insert more coins to continue'
return False
elif mc.polyEvaluate(object_name, shell = True) > 1:
# more than one shell in object named 'object_name'
print 'NO REDEMPTION'
return False
elif len(volume_ratios) == 1:
# check ratio matches
this_ratio = mm.eval('meshVolume(\"' + object_name + '\")') / volume_total
# since its last one, might have more errors
if abs(this_ratio - volume_ratios[0]) < threshold * 4:
# duplicate the object
temp = mc.duplicate(object_name)
mc.select(temp[0], r = True)
# move away the duplication
mc.move(kMoveAwayXDistance, 0, 0, temp[0])
# remove the current object
mc.delete(object_name)
print 'DONE with last object!'
result.append(temp[0])
print result
return True
else:
print 'last object did NOT match last ratio!', this_ratio, volume_ratios[0]
return False
# recursive step
random.shuffle(cut_planes)
result_from_cutting = cut_object_with_planes_and_ratios(object_name, volume_total, cut_planes, volume_ratios, threshold)
if isinstance(result_from_cutting, list):
# this list contains all successfully cut objects and we are done
result.extend(result_from_cutting)
print 'lucky!'
print result
return True
else:
print 'Enter recursive step'
# dictionary returned
# extend result list with what we have now
result.extend(result_from_cutting['good_cut_objs'])
# merge the remaining objects into one
bad_cut_objs = result_from_cutting['bad_cut_objs']
if mc.polyEvaluate(bad_cut_objs, shell = True) > 1:
united_objects = mc.polyUnite(bad_cut_objs)[0]
mc.polyMergeVertex(united_objects)
else:
united_objects = bad_cut_objs[0]
# get list of ratios un-resolved
ratios_remaining = result_from_cutting['ratios_remaining']
recursion_result = rec(united_objects, volume_total, cut_planes, ratios_remaining, threshold, result, loop_num-1)
return recursion_result
def print_cut_planes(planes):
for p in planes:
temp = mc.polyPlane(ax = [0, 0, 1], h = 5, w = 5)
mc.move(p['pc'][0], p['pc'][1], p['pc'][2], temp)
mc.rotate(p['ro'][0], p['ro'][1], p['ro'][2], temp)
return
mc.select(all=True)
mc.delete()
cube1 = mc.polyCube(sx=1, sy=1, sz=1, h=5, w=5, d=5)
#cube1 = mc.polySphere(r=5, sx=20, sy=20, ax=[0,1,0])
#cube1 = mc.polyCylinder(r=3, h=5, sx=20, sy=1, ax=[0,1,0])
#cube1 = mc.polyCone(r=1, h=2, ax=[0,1,0])
#cube1 = mc.polyPyramid()
object_name = cube1[0]
volume_total = mm.eval('meshVolume(\"' + object_name + '\")')
volume_ratios = [0.2, 0.4, 0.4]
threshold = 0.03
#print mc.objectCenter(cube1[0])
# LOOP A
cut_planes = form_cutting_planes_for_object(object_name, 4)
#print_cut_planes(cut_planes)
#mc.delete(object_name)
rec(object_name, volume_total, cut_planes, volume_ratios, threshold, [], 1)