Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epoxy
GitHub Repository: epoxy/proj11
Path: blob/master/SLICK_HOME/src/org/newdawn/slick/geom/Shape.java
1461 views
1
package org.newdawn.slick.geom;
2
3
import java.io.Serializable;
4
5
/**
6
* The description of any 2D shape that can be transformed. The points provided approximate the intent
7
* of the shape.
8
*
9
* @author Mark
10
*/
11
public abstract class Shape implements Serializable {
12
/** The points representing this polygon. */
13
protected float points[];
14
/** Center point of the polygon. */
15
protected float center[];
16
/** The left most point of this shape. */
17
protected float x;
18
/** The top most point of this shape. */
19
protected float y;
20
/** The right most point of this shape */
21
protected float maxX;
22
/** The bottom most point of this shape */
23
protected float maxY;
24
/** The left most point of this shape. */
25
protected float minX;
26
/** The top most point of this shape. */
27
protected float minY;
28
/** Radius of a circle that can completely enclose this shape. */
29
protected float boundingCircleRadius;
30
/** Flag to tell whether points need to be generated */
31
protected boolean pointsDirty;
32
/** The triangles that define the shape */
33
protected Triangulator tris;
34
/** True if the triangles need updating */
35
protected boolean trianglesDirty;
36
37
/**
38
* Shape constructor.
39
*
40
*/
41
public Shape() {
42
pointsDirty = true;
43
}
44
45
/**
46
* Set the location of this shape
47
*
48
* @param x The x coordinate of the new location of the shape
49
* @param y The y coordinate of the new location of the shape
50
*/
51
public void setLocation(float x, float y) {
52
setX(x);
53
setY(y);
54
}
55
56
/**
57
* Apply a transformation and return a new shape. This will not alter the current shape but will
58
* return the transformed shape.
59
*
60
* @param transform The transform to be applied
61
* @return The transformed shape.
62
*/
63
public abstract Shape transform(Transform transform);
64
65
/**
66
* Subclasses implement this to create the points of the shape.
67
*
68
*/
69
protected abstract void createPoints();
70
71
/**
72
* Get the x location of the left side of this shape.
73
*
74
* @return The x location of the left side of this shape.
75
*/
76
public float getX() {
77
return x;
78
}
79
80
/**
81
* Set the x position of the left side this shape.
82
*
83
* @param x The new x position of the left side this shape.
84
*/
85
public void setX(float x) {
86
if (x != this.x) {
87
float dx = x - this.x;
88
this.x = x;
89
90
if ((points == null) || (center == null)) {
91
checkPoints();
92
}
93
// update the points in the special case
94
for (int i=0;i<points.length/2;i++) {
95
points[i*2] += dx;
96
}
97
center[0] += dx;
98
x += dx;
99
maxX += dx;
100
minX += dx;
101
trianglesDirty = true;
102
}
103
}
104
105
/**
106
* Set the y position of the top of this shape.
107
*
108
* @param y The new y position of the top of this shape.
109
*/
110
public void setY(float y) {
111
if (y != this.y) {
112
float dy = y - this.y;
113
this.y = y;
114
115
if ((points == null) || (center == null)) {
116
checkPoints();
117
}
118
// update the points in the special case
119
for (int i=0;i<points.length/2;i++) {
120
points[(i*2)+1] += dy;
121
}
122
center[1] += dy;
123
y += dy;
124
maxY += dy;
125
minY += dy;
126
trianglesDirty = true;
127
}
128
}
129
130
/**
131
* Get the y position of the top of this shape.
132
*
133
* @return The y position of the top of this shape.
134
*/
135
public float getY() {
136
return y;
137
}
138
139
/**
140
* Set the location of this shape
141
*
142
* @param loc The new location of the shape
143
*/
144
public void setLocation(Vector2f loc) {
145
setX(loc.x);
146
setY(loc.y);
147
}
148
149
/**
150
* Get the x center of this shape.
151
*
152
* @return The x center of this shape.
153
*/
154
public float getCenterX() {
155
checkPoints();
156
157
return center[0];
158
}
159
160
/**
161
* Set the x center of this shape.
162
*
163
* @param centerX The center point to set.
164
*/
165
public void setCenterX(float centerX) {
166
if ((points == null) || (center == null)) {
167
checkPoints();
168
}
169
170
float xDiff = centerX - getCenterX();
171
setX(x + xDiff);
172
}
173
174
/**
175
* Get the y center of this shape.
176
*
177
* @return The y center of this shape.
178
*/
179
public float getCenterY() {
180
checkPoints();
181
182
return center[1];
183
}
184
185
/**
186
* Set the y center of this shape.
187
*
188
* @param centerY The center point to set.
189
*/
190
public void setCenterY(float centerY) {
191
if ((points == null) || (center == null)) {
192
checkPoints();
193
}
194
195
float yDiff = centerY - getCenterY();
196
setY(y + yDiff);
197
}
198
199
/**
200
* Get the right most point of this shape.
201
*
202
* @return The right most point of this shape.
203
*/
204
public float getMaxX() {
205
checkPoints();
206
return maxX;
207
}
208
/**
209
* Get the bottom most point of this shape.
210
*
211
* @return The bottom most point of this shape.
212
*/
213
public float getMaxY() {
214
checkPoints();
215
return maxY;
216
}
217
218
/**
219
* Get the left most point of this shape.
220
*
221
* @return The left most point of this shape.
222
*/
223
public float getMinX() {
224
checkPoints();
225
return minX;
226
}
227
228
/**
229
* Get the top most point of this shape.
230
*
231
* @return The top most point of this shape.
232
*/
233
public float getMinY() {
234
checkPoints();
235
return minY;
236
}
237
238
/**
239
* Get the radius of a circle that can completely enclose this shape.
240
*
241
* @return The radius of the circle.
242
*/
243
public float getBoundingCircleRadius() {
244
checkPoints();
245
return boundingCircleRadius;
246
}
247
248
/**
249
* Get the point closet to the center of all the points in this Shape
250
*
251
* @return The x,y coordinates of the center.
252
*/
253
public float[] getCenter() {
254
checkPoints();
255
return center;
256
}
257
258
/**
259
* Get the points that outline this shape. Use CW winding rule
260
*
261
* @return an array of x,y points
262
*/
263
public float[] getPoints() {
264
checkPoints();
265
return points;
266
}
267
268
/**
269
* Get the number of points in this polygon
270
*
271
* @return The number of points in this polygon
272
*/
273
public int getPointCount() {
274
checkPoints();
275
return points.length / 2;
276
}
277
278
/**
279
* Get a single point in this polygon
280
*
281
* @param index The index of the point to retrieve
282
* @return The point's coordinates
283
*/
284
public float[] getPoint(int index) {
285
checkPoints();
286
287
float result[] = new float[2];
288
289
result[0] = points[index * 2];
290
result[1] = points[index * 2 + 1];
291
292
return result;
293
}
294
295
/**
296
* Get the combine normal of a given point
297
*
298
* @param index The index of the point whose normal should be retrieved
299
* @return The combined normal of a given point
300
*/
301
public float[] getNormal(int index) {
302
float[] current = getPoint(index);
303
float[] prev = getPoint(index - 1 < 0 ? getPointCount() - 1 : index - 1);
304
float[] next = getPoint(index + 1 >= getPointCount() ? 0 : index + 1);
305
306
float[] t1 = getNormal(prev, current);
307
float[] t2 = getNormal(current, next);
308
309
if ((index == 0) && (!closed())) {
310
return t2;
311
}
312
if ((index == getPointCount()-1) && (!closed())) {
313
return t1;
314
}
315
316
float tx = (t1[0]+t2[0])/2;
317
float ty = (t1[1]+t2[1])/2;
318
float len = (float) Math.sqrt((tx*tx)+(ty*ty));
319
return new float[] {tx/len,ty/len};
320
}
321
322
/**
323
* Check if the shape passed is entirely contained within
324
* this shape.
325
*
326
* @param other The other shape to test against this one
327
* @return True if the other shape supplied is entirely contained
328
* within this one.
329
*/
330
public boolean contains(Shape other) {
331
if (other.intersects(this)) {
332
return false;
333
}
334
335
for (int i=0;i<other.getPointCount();i++) {
336
float[] pt = other.getPoint(i);
337
if (!contains(pt[0], pt[1])) {
338
return false;
339
}
340
}
341
342
return true;
343
}
344
/**
345
* Get the normal of the line between two points
346
*
347
* @param start The start point
348
* @param end The end point
349
* @return The normal of the line between the two points
350
*/
351
private float[] getNormal(float[] start, float[] end) {
352
float dx = start[0] - end[0];
353
float dy = start[1] - end[1];
354
float len = (float) Math.sqrt((dx*dx)+(dy*dy));
355
dx /= len;
356
dy /= len;
357
return new float[] {-dy,dx};
358
}
359
360
/**
361
* Check if the given point is part of the path that
362
* forms this shape
363
*
364
* @param x The x position of the point to check
365
* @param y The y position of the point to check
366
* @return True if the point is includes in the path of the polygon
367
*/
368
public boolean includes(float x, float y) {
369
if (points.length == 0) {
370
return false;
371
}
372
373
checkPoints();
374
375
Line testLine = new Line(0,0,0,0);
376
Vector2f pt = new Vector2f(x,y);
377
378
for (int i=0;i<points.length;i+=2) {
379
int n = i+2;
380
if (n >= points.length) {
381
n = 0;
382
}
383
testLine.set(points[i], points[i+1], points[n], points[n+1]);
384
385
if (testLine.on(pt)) {
386
return true;
387
}
388
}
389
390
return false;
391
}
392
393
/**
394
* Get the index of a given point
395
*
396
* @param x The x coordinate of the point
397
* @param y The y coordinate of the point
398
* @return The index of the point or -1 if the point is not part of this shape path
399
*/
400
public int indexOf(float x, float y) {
401
for (int i=0;i<points.length;i+=2) {
402
if ((points[i] == x) && (points[i+1] == y)) {
403
return i / 2;
404
}
405
}
406
407
return -1;
408
}
409
410
/**
411
* Check if this polygon contains the given point
412
*
413
* @param x The x position of the point to check
414
* @param y The y position of the point to check
415
* @return True if the point is contained in the polygon
416
*/
417
public boolean contains(float x, float y) {
418
checkPoints();
419
if (points.length == 0) {
420
return false;
421
}
422
423
boolean result = false;
424
float xnew,ynew;
425
float xold,yold;
426
float x1,y1;
427
float x2,y2;
428
int npoints = points.length;
429
430
xold=points[npoints - 2];
431
yold=points[npoints - 1];
432
for (int i=0;i < npoints;i+=2) {
433
xnew = points[i];
434
ynew = points[i + 1];
435
if (xnew > xold) {
436
x1 = xold;
437
x2 = xnew;
438
y1 = yold;
439
y2 = ynew;
440
}
441
else {
442
x1 = xnew;
443
x2 = xold;
444
y1 = ynew;
445
y2 = yold;
446
}
447
if ((xnew < x) == (x <= xold) /* edge "open" at one end */
448
&& ((double)y - (double)y1) * (x2 - x1)
449
< ((double)y2 - (double)y1) * (x - x1)) {
450
result = !result;
451
}
452
xold = xnew;
453
yold = ynew;
454
}
455
456
return result;
457
}
458
459
/**
460
* Check if this shape intersects with the shape provided.
461
*
462
* @param shape The shape to check if it intersects with this one.
463
* @return True if the shapes do intersect, false otherwise.
464
*/
465
public boolean intersects(Shape shape) {
466
/*
467
* Intersection formula used:
468
* (x4 - x3)(y1 - y3) - (y4 - y3)(x1 - x3)
469
* UA = ---------------------------------------
470
* (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
471
*
472
* (x2 - x1)(y1 - y3) - (y2 - y1)(x1 - x3)
473
* UB = ---------------------------------------
474
* (y4 - y3)(x2 - x1) - (x4 - x3)(y2 - y1)
475
*
476
* if UA and UB are both between 0 and 1 then the lines intersect.
477
*
478
* Source: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
479
*/
480
checkPoints();
481
482
boolean result = false;
483
float points[] = getPoints(); // (x3, y3) and (x4, y4)
484
float thatPoints[] = shape.getPoints(); // (x1, y1) and (x2, y2)
485
int length = points.length;
486
int thatLength = thatPoints.length;
487
double unknownA;
488
double unknownB;
489
490
if (!closed()) {
491
length -= 2;
492
}
493
if (!shape.closed()) {
494
thatLength -= 2;
495
}
496
497
// x1 = thatPoints[j]
498
// x2 = thatPoints[j + 2]
499
// y1 = thatPoints[j + 1]
500
// y2 = thatPoints[j + 3]
501
// x3 = points[i]
502
// x4 = points[i + 2]
503
// y3 = points[i + 1]
504
// y4 = points[i + 3]
505
for(int i=0;i<length;i+=2) {
506
int iNext = i+2;
507
if (iNext >= points.length) {
508
iNext = 0;
509
}
510
511
for(int j=0;j<thatLength;j+=2) {
512
int jNext = j+2;
513
if (jNext >= thatPoints.length) {
514
jNext = 0;
515
}
516
517
unknownA = (((points[iNext] - points[i]) * (double) (thatPoints[j + 1] - points[i + 1])) -
518
((points[iNext+1] - points[i + 1]) * (thatPoints[j] - points[i]))) /
519
(((points[iNext+1] - points[i + 1]) * (thatPoints[jNext] - thatPoints[j])) -
520
((points[iNext] - points[i]) * (thatPoints[jNext+1] - thatPoints[j + 1])));
521
unknownB = (((thatPoints[jNext] - thatPoints[j]) * (double) (thatPoints[j + 1] - points[i + 1])) -
522
((thatPoints[jNext+1] - thatPoints[j + 1]) * (thatPoints[j] - points[i]))) /
523
(((points[iNext+1] - points[i + 1]) * (thatPoints[jNext] - thatPoints[j])) -
524
((points[iNext] - points[i]) * (thatPoints[jNext+1] - thatPoints[j + 1])));
525
526
if(unknownA >= 0 && unknownA <= 1 && unknownB >= 0 && unknownB <= 1) {
527
result = true;
528
break;
529
}
530
}
531
if(result) {
532
break;
533
}
534
}
535
536
return result;
537
}
538
539
/**
540
* Check if a particular location is a vertex of this polygon
541
*
542
* @param x The x coordinate to check
543
* @param y The y coordinate to check
544
* @return True if the cordinates supplied are a vertex of this polygon
545
*/
546
public boolean hasVertex(float x, float y) {
547
if (points.length == 0) {
548
return false;
549
}
550
551
checkPoints();
552
553
for (int i=0;i<points.length;i+=2) {
554
if ((points[i] == x) && (points[i+1] == y)) {
555
return true;
556
}
557
}
558
559
return false;
560
}
561
562
/**
563
* Get the center of this polygon.
564
*
565
*/
566
protected void findCenter() {
567
center = new float[]{0, 0};
568
int length = points.length;
569
for(int i=0;i<length;i+=2) {
570
center[0] += points[i];
571
center[1] += points[i + 1];
572
}
573
center[0] /= (length / 2);
574
center[1] /= (length / 2);
575
}
576
577
/**
578
* Calculate the radius of a circle that can completely enclose this shape.
579
*
580
*/
581
protected void calculateRadius() {
582
boundingCircleRadius = 0;
583
584
for(int i=0;i<points.length;i+=2) {
585
float temp = ((points[i] - center[0]) * (points[i] - center[0])) +
586
((points[i + 1] - center[1]) * (points[i + 1] - center[1]));
587
boundingCircleRadius = (boundingCircleRadius > temp) ? boundingCircleRadius : temp;
588
}
589
boundingCircleRadius = (float)Math.sqrt(boundingCircleRadius);
590
}
591
592
/**
593
* Calculate the triangles that can fill this shape
594
*/
595
protected void calculateTriangles() {
596
if (!trianglesDirty) {
597
return;
598
}
599
if (points.length >= 6) {
600
boolean clockwise = true;
601
float area = 0;
602
for (int i=0;i<(points.length/2)-1;i++) {
603
float x1 = points[(i*2)];
604
float y1 = points[(i*2)+1];
605
float x2 = points[(i*2)+2];
606
float y2 = points[(i*2)+3];
607
608
area += (x1 * y2) - (y1 * x2);
609
}
610
area /= 2;
611
clockwise = area > 0;
612
613
tris = new NeatTriangulator();
614
for (int i=0;i<points.length;i+=2) {
615
tris.addPolyPoint(points[i], points[i+1]);
616
}
617
tris.triangulate();
618
}
619
620
trianglesDirty = false;
621
}
622
623
/**
624
* Increase triangulation
625
*/
626
public void increaseTriangulation() {
627
checkPoints();
628
calculateTriangles();
629
630
tris = new OverTriangulator(tris);
631
}
632
633
/**
634
* The triangles that define the filled version of this shape
635
*
636
* @return The triangles that define the
637
*/
638
public Triangulator getTriangles() {
639
checkPoints();
640
calculateTriangles();
641
return tris;
642
}
643
644
/**
645
* Check the dirty flag and create points as necessary.
646
*/
647
protected final void checkPoints() {
648
if (pointsDirty) {
649
createPoints();
650
findCenter();
651
calculateRadius();
652
653
maxX = points[0];
654
maxY = points[1];
655
minX = points[0];
656
minY = points[1];
657
for (int i=0;i<points.length/2;i++) {
658
maxX = Math.max(points[i*2],maxX);
659
maxY = Math.max(points[(i*2)+1],maxY);
660
minX = Math.min(points[i*2],minX);
661
minY = Math.min(points[(i*2)+1],minY);
662
}
663
664
pointsDirty = false;
665
trianglesDirty = true;
666
}
667
}
668
669
/**
670
* Cause all internal state to be generated and cached
671
*/
672
public void preCache() {
673
checkPoints();
674
getTriangles();
675
}
676
677
/**
678
* True if this is a closed shape
679
*
680
* @return True if this is a closed shape
681
*/
682
public boolean closed() {
683
return true;
684
}
685
686
/**
687
* Prune any required points in this shape
688
*
689
* @return The new shape with points pruned
690
*/
691
public Shape prune() {
692
Polygon result = new Polygon();
693
694
for (int i=0;i<getPointCount();i++) {
695
int next = i+1 >= getPointCount() ? 0 : i+1;
696
int prev = i-1 < 0 ? getPointCount() - 1 : i-1;
697
698
float dx1 = getPoint(i)[0] - getPoint(prev)[0];
699
float dy1 = getPoint(i)[1] - getPoint(prev)[1];
700
float dx2 = getPoint(next)[0] - getPoint(i)[0];
701
float dy2 = getPoint(next)[1] - getPoint(i)[1];
702
703
float len1 = (float) Math.sqrt((dx1*dx1) + (dy1*dy1));
704
float len2 = (float) Math.sqrt((dx2*dx2) + (dy2*dy2));
705
dx1 /= len1;
706
dy1 /= len1;
707
dx2 /= len2;
708
dy2 /= len2;
709
710
if ((dx1 != dx2) || (dy1 != dy2)) {
711
result.addPoint(getPoint(i)[0],getPoint(i)[1]);
712
}
713
}
714
715
return result;
716
}
717
718
/**
719
* Subtract the given shape from this one. Note that this method only deals
720
* with edges, it will not create holes in polygons.
721
*
722
* @param other The other shape to subtract from this one
723
* @return The newly created set of shapes resulting from the operation
724
*/
725
public Shape[] subtract(Shape other) {
726
return new GeomUtil().subtract(this, other);
727
}
728
729
/**
730
* Join this shape with another.
731
*
732
* @param other The other shape to join with this one
733
* @return The newly created set of shapes resulting from the operation
734
*/
735
public Shape[] union(Shape other) {
736
return new GeomUtil().union(this, other);
737
}
738
739
/**
740
* Get the width of the shape
741
*
742
* @return The width of the shape
743
*/
744
public float getWidth() {
745
return maxX - minX;
746
}
747
748
749
/**
750
* Get the height of the shape
751
*
752
* @return The height of the shape
753
*/
754
public float getHeight() {
755
return maxY - minY;
756
}
757
}
758
759