18 |
18 |
|
19 |
19 |
public class ObjectShape
|
20 |
20 |
{
|
|
21 |
private static final float[] mCanonical = new float[] {0.343f,0.509f,0.789f};
|
21 |
22 |
private static final float[] mTmp1 = new float[4];
|
22 |
23 |
private static final float[] mTmp2 = new float[4];
|
23 |
24 |
|
... | ... | |
81 |
82 |
int numFaces =mMultigonIndices.length;
|
82 |
83 |
str.append("numFaces: ");
|
83 |
84 |
str.append(numFaces);
|
84 |
|
str.append("\n");
|
|
85 |
str.append("\n ");
|
85 |
86 |
|
86 |
|
for(int[][] mMultigonIndex : mMultigonIndices)
|
|
87 |
for(int f=0; f<numFaces; f++)
|
87 |
88 |
{
|
88 |
|
for(int[] multigonIndex : mMultigonIndex)
|
|
89 |
int[][] multigonIndex = mMultigonIndices[f];
|
|
90 |
|
|
91 |
for(int[] mulIndex : multigonIndex)
|
89 |
92 |
{
|
90 |
|
int len = multigonIndex.length;
|
|
93 |
int len = mulIndex.length;
|
91 |
94 |
str.append(" {");
|
92 |
95 |
|
93 |
|
for(int i=0; i<len; i++)
|
|
96 |
for(int v=0; v<len; v++)
|
94 |
97 |
{
|
95 |
|
str.append( i==0 ? " " : ", ");
|
96 |
|
str.append(multigonIndex[i]);
|
|
98 |
str.append( v==0 ? " " : ", ");
|
|
99 |
str.append(mulIndex[v]);
|
97 |
100 |
}
|
98 |
101 |
str.append("},");
|
99 |
102 |
}
|
100 |
|
str.append("\n");
|
|
103 |
|
|
104 |
if( mFaceIsOuter!=null ) str.append( mFaceIsOuter[f] ? " outer" : " inner");
|
|
105 |
else str.append(" mFaceIsOuter is null");
|
|
106 |
str.append("\n ");
|
101 |
107 |
}
|
102 |
108 |
}
|
103 |
109 |
else
|
104 |
110 |
{
|
105 |
111 |
int numFaces =mVertIndices.length;
|
106 |
|
str.append("\nnumFaces: ");
|
|
112 |
str.append("numFaces: ");
|
107 |
113 |
str.append(numFaces);
|
108 |
|
str.append("\n ");
|
|
114 |
str.append("\n ");
|
109 |
115 |
|
110 |
|
for(int[] vertIndex : mVertIndices)
|
|
116 |
for(int f=0; f<numFaces; f++)
|
111 |
117 |
{
|
|
118 |
int[] vertIndex = mVertIndices[f];
|
|
119 |
|
112 |
120 |
for(int index : vertIndex)
|
113 |
121 |
{
|
114 |
122 |
str.append(" ");
|
115 |
123 |
str.append(index);
|
116 |
124 |
}
|
117 |
|
str.append("\n ");
|
|
125 |
|
|
126 |
if( mFaceIsOuter!=null ) str.append( mFaceIsOuter[f] ? " outer" : " inner");
|
|
127 |
else str.append(" mFaceIsOuter is null");
|
|
128 |
str.append("\n ");
|
118 |
129 |
}
|
119 |
130 |
}
|
120 |
131 |
|
... | ... | |
129 |
140 |
public int[][] getVertIndices() { return mVertIndices; }
|
130 |
141 |
public int[][][] getMultigonIndices() { return mMultigonIndices; }
|
131 |
142 |
|
|
143 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
144 |
// Return 1 if arr1>arr2, 0 if arr1==arr2, -1 if arr1<arr2.
|
|
145 |
// if arr1.length > arr2.length, then arr1>arr2.
|
|
146 |
// if arr1.length==arr2.lenght, then treat individual integers of arrays as digits in a 2^32-mal
|
|
147 |
// system and compare those two 2^32-mal numbers.
|
|
148 |
|
|
149 |
private int compare(int[] arr1, int[] arr2)
|
|
150 |
{
|
|
151 |
int len1 = arr1.length;
|
|
152 |
int len2 = arr2.length;
|
|
153 |
|
|
154 |
if( len1>len2 ) return 1;
|
|
155 |
if( len1<len2 ) return -1;
|
|
156 |
|
|
157 |
for(int i=0; i<len1; i++)
|
|
158 |
{
|
|
159 |
int diff = arr1[i]-arr2[i];
|
|
160 |
if( diff>0 ) return 1;
|
|
161 |
if( diff<0 ) return -1;
|
|
162 |
}
|
|
163 |
|
|
164 |
return 0;
|
|
165 |
}
|
|
166 |
|
|
167 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
168 |
// return the same array, but rotate it so that its smallest value is first.
|
|
169 |
// (the values in the array are guaranteed to not repeat)
|
|
170 |
|
|
171 |
private int[] rotateSmallestValueToFirstIndex(int[] indices)
|
|
172 |
{
|
|
173 |
int smallest = Integer.MAX_VALUE;
|
|
174 |
int smallestIndex = -1;
|
|
175 |
int len = indices.length;
|
|
176 |
|
|
177 |
for(int i=0; i<len; i++)
|
|
178 |
if( indices[i]<smallest )
|
|
179 |
{
|
|
180 |
smallest = indices[i];
|
|
181 |
smallestIndex = i;
|
|
182 |
}
|
|
183 |
|
|
184 |
int[] ret = new int[len];
|
|
185 |
for(int i=0; i<len; i++) ret[i] = indices[(smallestIndex+i)%len];
|
|
186 |
|
|
187 |
return ret;
|
|
188 |
}
|
|
189 |
|
|
190 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
191 |
// Canonical form. The point: we want to be able to quickly see if two instances of ObjectShape are
|
|
192 |
// in fact the same shape. (the 'equals' function)
|
|
193 |
//
|
|
194 |
// Multigon not supported (rationale: this is only needed in TwistyCutSolid - i.e. for objects which
|
|
195 |
// are created by cutting a solid along a number of planes. This will never result in multigon shapes)
|
|
196 |
//
|
|
197 |
// 1. Sort the vertices from left to right according to the order they are on the mCanonical vector
|
|
198 |
// (here first compute the 'auxiliary' and 'permutation' arrays so that we don't have to keep
|
|
199 |
// rearranging the indices in the VertIndices multiarrays every time we swap two vertices)
|
|
200 |
// 2. Then, sort the indices on every every face by placing the lowest index first.
|
|
201 |
// 3. Then, sort the faces (and correspondingly the mFaceIsOuters).
|
|
202 |
// Each face contains a list of indices (I1,I2,...,IN). Sort by those lists by treating each
|
|
203 |
// integer in it as a digit in 2^32-mal number system and thus as if the whole list were a single
|
|
204 |
// 2^32-mal integer.
|
|
205 |
|
|
206 |
public void toCanonicalForm()
|
|
207 |
{
|
|
208 |
int numVertices = mVertices.length;
|
|
209 |
int[] permutation = new int[numVertices];
|
|
210 |
float[] auxiliary = new float[numVertices];
|
|
211 |
|
|
212 |
for(int v=0; v<numVertices; v++)
|
|
213 |
{
|
|
214 |
float[] vert = mVertices[v];
|
|
215 |
auxiliary[v] = mCanonical[0]*vert[0] + mCanonical[1]*vert[1] + mCanonical[2]*vert[2];
|
|
216 |
permutation[v] = v;
|
|
217 |
}
|
|
218 |
|
|
219 |
// insertion sort auxiliary[] and permutation[]
|
|
220 |
int sortedAlready = 1;
|
|
221 |
|
|
222 |
while( sortedAlready<numVertices )
|
|
223 |
{
|
|
224 |
float x = auxiliary[sortedAlready];
|
|
225 |
int n = permutation[sortedAlready];
|
|
226 |
int i = sortedAlready;
|
|
227 |
|
|
228 |
while( i>0 && auxiliary[i-1]>x )
|
|
229 |
{
|
|
230 |
auxiliary[i] = auxiliary[i-1];
|
|
231 |
permutation[i] = permutation[i-1];
|
|
232 |
i--;
|
|
233 |
}
|
|
234 |
auxiliary[i] = x;
|
|
235 |
permutation[i]= n;
|
|
236 |
sortedAlready++;
|
|
237 |
}
|
|
238 |
|
|
239 |
// now 'permutation' is in a form which is slow to use when we rename mVerIndices. Change it to
|
|
240 |
// another form where map[i]=j means 'index which used to be i must be changed to j'
|
|
241 |
int[] perm = new int[numVertices];
|
|
242 |
for(int i=0; i<numVertices; i++) perm[permutation[i]] = i;
|
|
243 |
|
|
244 |
if( mFacesMultigon )
|
|
245 |
{
|
|
246 |
android.util.Log.e("D", "error in ObjectShape.toCanonicalForm: multigon unsupported!");
|
|
247 |
}
|
|
248 |
else
|
|
249 |
{
|
|
250 |
float[][] tmpVertices = new float[numVertices][];
|
|
251 |
for(int i=0; i<numVertices; i++) tmpVertices[i] = mVertices[permutation[i]];
|
|
252 |
for(int i=0; i<numVertices; i++) mVertices[i] = tmpVertices[i];
|
|
253 |
|
|
254 |
int numFaces = mVertIndices.length;
|
|
255 |
for(int f=0; f<numFaces; f++)
|
|
256 |
{
|
|
257 |
int[] indices = mVertIndices[f];
|
|
258 |
int len = indices.length;
|
|
259 |
for(int i=0; i<len; i++) indices[i] = perm[indices[i]];
|
|
260 |
mVertIndices[f] = rotateSmallestValueToFirstIndex(indices);
|
|
261 |
}
|
|
262 |
|
|
263 |
// insertion sort mVertIndices and mFaceIsOuter
|
|
264 |
sortedAlready = 1;
|
|
265 |
|
|
266 |
while( sortedAlready<numFaces )
|
|
267 |
{
|
|
268 |
int[] x = mVertIndices[sortedAlready];
|
|
269 |
boolean n = mFaceIsOuter[sortedAlready];
|
|
270 |
int i = sortedAlready;
|
|
271 |
|
|
272 |
while( i>0 && compare(mVertIndices[i-1],x)>0 )
|
|
273 |
{
|
|
274 |
mVertIndices[i] = mVertIndices[i-1];
|
|
275 |
mFaceIsOuter[i] = mFaceIsOuter[i-1];
|
|
276 |
i--;
|
|
277 |
}
|
|
278 |
mVertIndices[i] = x;
|
|
279 |
mFaceIsOuter[i] = n;
|
|
280 |
sortedAlready++;
|
|
281 |
}
|
|
282 |
}
|
|
283 |
}
|
|
284 |
|
|
285 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
286 |
// both shapes already in canonical form!
|
|
287 |
|
|
288 |
public boolean equals(ObjectShape shape)
|
|
289 |
{
|
|
290 |
int len1 = mFaceIsOuter.length;
|
|
291 |
int len2 = shape.mFaceIsOuter.length;
|
|
292 |
|
|
293 |
if( len1 != len2 ) return false;
|
|
294 |
|
|
295 |
for(int f=0; f<len1; f++)
|
|
296 |
if( mFaceIsOuter[f] != shape.mFaceIsOuter[f] ) return false;
|
|
297 |
|
|
298 |
if( mFacesMultigon != shape.mFacesMultigon ) return false;
|
|
299 |
|
|
300 |
if( mFacesMultigon )
|
|
301 |
{
|
|
302 |
int numIndices1 = mMultigonIndices.length;
|
|
303 |
int numIndices2 = shape.mMultigonIndices.length;
|
|
304 |
|
|
305 |
if( numIndices1 != numIndices2 ) return false;
|
|
306 |
|
|
307 |
for(int i=0; i<numIndices1; i++)
|
|
308 |
{
|
|
309 |
int[][] indi1 = mMultigonIndices[i];
|
|
310 |
int[][] indi2 = shape.mMultigonIndices[i];
|
|
311 |
|
|
312 |
int ilen1 = indi1.length;
|
|
313 |
int ilen2 = indi2.length;
|
|
314 |
|
|
315 |
if( ilen1 != ilen2 ) return false;
|
|
316 |
|
|
317 |
for(int j=0; j<ilen1; j++)
|
|
318 |
{
|
|
319 |
int[] ind1 = indi1[j];
|
|
320 |
int[] ind2 = indi2[j];
|
|
321 |
int l1 = ind1.length;
|
|
322 |
int l2 = ind2.length;
|
|
323 |
|
|
324 |
if( l1 != l2 ) return false;
|
|
325 |
|
|
326 |
for(int k=0; k<l1; k++)
|
|
327 |
if( ind1[k] != ind2[k] ) return false;
|
|
328 |
}
|
|
329 |
}
|
|
330 |
}
|
|
331 |
else
|
|
332 |
{
|
|
333 |
int numIndices1 = mVertIndices.length;
|
|
334 |
int numIndices2 = shape.mVertIndices.length;
|
|
335 |
|
|
336 |
if( numIndices1 != numIndices2 ) return false;
|
|
337 |
|
|
338 |
for(int i=0; i<numIndices1; i++)
|
|
339 |
{
|
|
340 |
int[] ind1 = mVertIndices[i];
|
|
341 |
int[] ind2 = shape.mVertIndices[i];
|
|
342 |
int ilen1 = ind1.length;
|
|
343 |
int ilen2 = ind2.length;
|
|
344 |
|
|
345 |
if( ilen1 != ilen2 ) return false;
|
|
346 |
|
|
347 |
for(int j=0; j<ilen1; j++)
|
|
348 |
if( ind1[j] != ind2[j] ) return false;
|
|
349 |
}
|
|
350 |
}
|
|
351 |
|
|
352 |
float MIN_ERR = 0.001f;
|
|
353 |
int numv1 = mVertices.length;
|
|
354 |
int numv2 = shape.mVertices.length;
|
|
355 |
|
|
356 |
if( numv1 != numv2 ) return false;
|
|
357 |
|
|
358 |
for(int v=0; v<numv1; v++)
|
|
359 |
{
|
|
360 |
float[] v1 = mVertices[v];
|
|
361 |
float[] v2 = shape.mVertices[v];
|
|
362 |
|
|
363 |
float dx = v1[0]-v2[0];
|
|
364 |
float dy = v1[1]-v2[1];
|
|
365 |
float dz = v1[2]-v2[2];
|
|
366 |
|
|
367 |
if( dx*dx+dy*dy+dz*dz > MIN_ERR ) return false;
|
|
368 |
}
|
|
369 |
|
|
370 |
return true;
|
|
371 |
}
|
|
372 |
|
|
373 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
374 |
|
|
375 |
public ObjectShape rotate(Static4D quat)
|
|
376 |
{
|
|
377 |
float[] tmp = new float[4];
|
|
378 |
int numVertices = mVertices.length;
|
|
379 |
float[][] verts = new float[numVertices][];
|
|
380 |
float qx = quat.get0();
|
|
381 |
float qy = quat.get1();
|
|
382 |
float qz = quat.get2();
|
|
383 |
float qw = quat.get3();
|
|
384 |
float[] q = new float[] {qx,qy,qz,qw};
|
|
385 |
|
|
386 |
for(int v=0; v<numVertices; v++)
|
|
387 |
{
|
|
388 |
float[] vert = mVertices[v];
|
|
389 |
QuatHelper.rotateVectorByQuat(tmp,vert[0],vert[1],vert[2],1,q);
|
|
390 |
verts[v] = new float[] {tmp[0],tmp[1],tmp[2]};
|
|
391 |
}
|
|
392 |
|
|
393 |
int numOuter = mFaceIsOuter.length;
|
|
394 |
boolean[] outer= new boolean[numOuter];
|
|
395 |
for(int f=0; f<numOuter; f++) outer[f] = mFaceIsOuter[f];
|
|
396 |
|
|
397 |
if( mFacesMultigon )
|
|
398 |
{
|
|
399 |
int numIndices = mMultigonIndices.length;
|
|
400 |
int[][][] indices = new int[numIndices][][];
|
|
401 |
|
|
402 |
for(int i=0; i<numIndices; i++)
|
|
403 |
{
|
|
404 |
int[][] indi = mMultigonIndices[i];
|
|
405 |
int len = indi.length;
|
|
406 |
indices[i] = new int[len][];
|
|
407 |
|
|
408 |
for(int j=0; j<len; j++)
|
|
409 |
{
|
|
410 |
int[] ind = indi[j];
|
|
411 |
int l = ind.length;
|
|
412 |
int[] t = new int[l];
|
|
413 |
for(int k=0; k<l; k++) t[k] = ind[k];
|
|
414 |
indices[i][j] = t;
|
|
415 |
}
|
|
416 |
}
|
|
417 |
|
|
418 |
ObjectShape ret = new ObjectShape(verts,indices);
|
|
419 |
ret.setFaceOuter(outer);
|
|
420 |
return ret;
|
|
421 |
}
|
|
422 |
else
|
|
423 |
{
|
|
424 |
int numIndices = mVertIndices.length;
|
|
425 |
int[][] indices = new int[numIndices][];
|
|
426 |
|
|
427 |
for(int i=0; i<numIndices; i++)
|
|
428 |
{
|
|
429 |
int[] ind = mVertIndices[i];
|
|
430 |
int len = ind.length;
|
|
431 |
int[] t = new int[len];
|
|
432 |
for(int j=0; j<len; j++) t[j] = ind[j];
|
|
433 |
indices[i] = t;
|
|
434 |
}
|
|
435 |
|
|
436 |
ObjectShape ret = new ObjectShape(verts,indices);
|
|
437 |
ret.setFaceOuter(outer);
|
|
438 |
return ret;
|
|
439 |
}
|
|
440 |
}
|
|
441 |
|
132 |
442 |
///////////////////////////////////////////////////////////////////////////////////////////////////
|
133 |
443 |
|
134 |
444 |
private void computeNormalVector(float[] v0, float[] v1, float[] v2, float[] output)
|
... | ... | |
168 |
478 |
{
|
169 |
479 |
final float MAX_ERROR_DIST = 0.001f;
|
170 |
480 |
float fx = faceAxis.get0();
|
171 |
|
float fy = faceAxis.get0();
|
172 |
|
float fz = faceAxis.get0();
|
|
481 |
float fy = faceAxis.get1();
|
|
482 |
float fz = faceAxis.get2();
|
173 |
483 |
float x = vertex[0]+pos[0];
|
174 |
484 |
float y = vertex[1]+pos[1];
|
175 |
485 |
float z = vertex[2]+pos[2];
|
progress with unification of cubits created by FacotyCutSolid