1 module PixelPerfectEngine.collision.objectCollision;
2 
3 /*
4  * Copyright (C) 2015-2020, by Laszlo Szeremi under the Boost license.
5  *
6  * Pixel Perfect Engine, collision.objectCollision module.
7  */
8 
9 import PixelPerfectEngine.system.advBitArray;
10 
11 import collections.treemap;
12 import PixelPerfectEngine.collision.common;
13 //import PixelPerfectEngine.collision.boxCollision;
14 
15 /**
16  * Object-to-object collision detector.
17  */
18 public class ObjectCollisionDetector {
19 	ObjectMap			objects;	///Contains all of the objects 
20 	int					contextID;	///Stores the identifier of this detector
21 	/**
22 	 * The delegate where the events will be passed.
23 	 * Must be set up before using the collision detector.
24 	 */
25 	void delegate(ObjectCollisionEvent event)			objectToObjectCollision;
26 	/**
27 	 * Tests all shapes against each other
28 	 */
29 	public void testAll() {
30 		foreach (int iA, ref CollisionShape shA; objects) {
31 			testSingle(iA, &shA);
32 		}
33 	}
34 	/**
35 	 * Tests a single shape against the others
36 	 */
37 	public void testSingle(int objectID) {
38 		testSingle(objectID, objects.ptrOf(objectID));
39 	}
40 	///Ditto
41 	protected void testSingle(int iA, CollisionShape* shA) {
42 		foreach (int iB, ref CollisionShape shB; objects) {
43 			if (iA != iB) {
44 				ObjectCollisionEvent event = testCollision(shA, &shB);
45 				if (event! is null) {
46 					event.idA = iA;
47 					event.idB = iB;
48 					objectToObjectCollision(event);
49 				}
50 			}
51 		}
52 	}
53 	/**
54 	 * Tests two objects. Calls cl if collision have happened, with the appropriate values.
55 	 */
56 	protected ObjectCollisionEvent testCollision(CollisionShape* shA, CollisionShape* shB) pure {
57 		if (shA.position.bottom < shB.position.top || shA.position.top > shB.position.bottom || 
58 				shA.position.right < shB.position.left || shA.position.left > shB.position.right){
59 			//test if edge collision have happened with side edges
60 			if (shA.position.bottom >= shB.position.top && shA.position.top <= shB.position.bottom && 
61 					(shA.position.right - shB.position.left == -1 || shA.position.left - shB.position.right == 1)) {
62 				//calculate edge collision area
63 				Coordinate cc;
64 				if(shA.position.top >= shB.position.top)
65 					cc.top = shA.position.top;
66 				else
67 					cc.top = shB.position.top;
68 				if(shA.position.bottom >= shB.position.bottom)
69 					cc.bottom = shB.position.bottom;
70 				else
71 					cc.bottom = shA.position.bottom;
72 				if (shA.position.right < shB.position.left) {
73 					cc.left = shA.position.right;
74 					cc.right = shB.position.left;
75 				} else {
76 					cc.left = shB.position.right;
77 					cc.right = shA.position.left;
78 				}
79 				return new ObjectCollisionEvent(shA, shB, contextID, cc, ObjectCollisionEvent.Type.BoxEdge);
80 			} else if (shA.position.right >= shB.position.left && shA.position.left <= shB.position.right && 
81 					(shA.position.bottom - shB.position.top == -1 || shA.position.top - shB.position.bottom == 1)) {
82 				//calculate edge collision area
83 				Coordinate cc;
84 				if(shA.position.left >= shB.position.left)
85 					cc.left = shA.position.left;
86 				else
87 					cc.left = shB.position.left;
88 				if(shA.position.right >= shB.position.right)
89 					cc.right = shB.position.right;
90 				else
91 					cc.right = shA.position.right;
92 				if (shA.position.bottom < shB.position.top) {
93 					cc.top = shA.position.bottom;
94 					cc.bottom = shB.position.top;
95 				} else {
96 					cc.top = shB.position.bottom;
97 					cc.bottom = shA.position.top;
98 				}
99 				return new ObjectCollisionEvent(shA, shB, contextID, cc, ObjectCollisionEvent.Type.BoxEdge);
100 			} else return null;
101 		} else {
102 			//if there's a bitmap for both shapes, then proceed to per-pixel testing
103 			Coordinate ca, cb, cc; // test area coordinates
104 			ca.right = shA.position.width;
105 			ca.bottom = shA.position.height;
106 			cb.right = shB.position.width;
107 			cb.bottom = shB.position.height;
108 			if(shA.position.top >= shB.position.top){
109 				ca.top += shA.position.top - shB.position.top;
110 				cc.top = shA.position.top;
111 			} else {
112 				cb.top += shB.position.top - shA.position.top;
113 				cc.top = shB.position.top;
114 			}
115 			if(shA.position.bottom >= shB.position.bottom) {
116 				ca.bottom -= shA.position.bottom - shB.position.bottom;
117 				cc.bottom = shB.position.bottom;
118 			} else {
119 				cb.bottom -= shB.position.bottom - shA.position.bottom;
120 				cc.bottom = shA.position.bottom; 
121 			}
122 			if(shA.position.left >= shB.position.left) {
123 				ca.left += shA.position.left - shB.position.left;
124 				cc.left = shA.position.left;
125 			} else {
126 				cb.left += shB.position.left - shA.position.left;
127 				cc.left = shB.position.left;
128 			}
129 			if(shA.position.right >= shB.position.right) {
130 				ca.right -= shA.position.right - shB.position.right;
131 				cc.right = shB.position.right;
132 			} else {
133 				cb.right -= shB.position.right - shA.position.right;
134 				cc.right = shA.position.right;
135 			}
136 			debug {
137 				assert ((ca.width == cb.width) && (cb.width == cc.width), "Width mismatch error!");
138 				assert ((ca.height == cb.height) && (cb.height == cc.height), "Height mismatch error!");
139 			}
140 			ObjectCollisionEvent event = new ObjectCollisionEvent(shA, shB, contextID, cc, ObjectCollisionEvent.Type.BoxOverlap);
141 			if(shA.shape !is null && shB.shape !is null) {
142 				for (int y ; y < cc.width ; y++) {
143 					for (int x ; x < cc.height ; x++) {
144 						if (shA.shape.readPixel(ca.left + x, ca.top + y) && shB.shape.readPixel(cb.left + x, cb.top + y)) {
145 							event.type = ObjectCollisionEvent.Type.ShapeOverlap;
146 							return event;
147 						}
148 					}
149 				}
150 			}
151 			return event;
152 		}
153 	}
154 	/+/**
155 	 * Tests two boxes together. Returns true on collision.
156 	 */
157 	protected bool areColliding(ref CollisionShape a, ref CollisionShape b){
158 		if (a.position.bottom < b.position.top) return false;
159 		if (a.position.top > b.position.bottom) return false;
160 		
161 		if (a.position.right < b.position.left) return false;
162 		if (a.position.left > b.position.right) return false;
163 
164 		Coordinate ca, cb, cc; // test area coordinates
165 		int cTPA, cTPB; // testpoints 
166 
167 		// process the test area and calculate the test points
168 		if(a.position.top >= b.position.top){
169 			cc.top = a.position.top;
170 			cTPA = a.position.width() * (a.position.top - b.position.top);
171 		}else{
172 			cc.top = b.position.top;
173 			cTPB = b.position.width() * (b.position.top - a.position.top);
174 		}
175 		if(a.position.bottom >= b.position.bottom){
176 			cc.bottom = b.position.bottom;
177 		}else{
178 			cc.bottom = a.position.bottom;
179 		}
180 		if(a.position.left >= b.position.left){
181 			cc.left = a.position.left;
182 			cTPA += a.position.left - b.position.left;
183 		}else{
184 			cc.left = b.position.left;
185 			cTPB += b.position.left - a.position.left;
186 		}
187 		if(a.position.right >= b.position.right){
188 			cc.right = b.position.right;
189 		}else{
190 			cc.right = a.position.right;
191 		}
192 		//writeln("A: x: ", ca.left," y: ", ca.top, "B: x: ", cb.left," y: ", cb.top, "C: x: ", cc.left," y: ", cc.top);
193 		for(int y ; y < cc.height ; y++) {
194 			for(int x ; x < cc.width ; x++) {
195 
196 			}
197 			cTPA += a.position.width();
198 			cTPB += b.position.width();
199 		}
200 
201 		return false;
202 	}+/
203 }