1 module collider;
2 
3 import optional;
4 import std.algorithm;
5 import dsfml.graphics;
6 
7 /// Represents a collider composed of multiple rectangles
8 class Collider : Drawable {
9 	private static draw_colliders = false;
10 
11   private FloatRect[] rects;
12   private FloatRect bounds;
13   private float initial_tex_width = 0;
14   private auto delayed_translation = optional.no!Vector2f;
15 
16   private void ensure_translation() {
17     if(delayed_translation == optional.none) {
18       return;
19     }
20     immutable vec = delayed_translation.or(Vector2f(0, 0));
21 
22     foreach(ref r; rects) {
23       r.top += vec.y;
24       r.left += vec.x;
25     }
26     delayed_translation = optional.no!Vector2f;
27   }
28 
29   /// Initializes a collider from a texture, all pixels with alpha > 127 collide
30   this(const Texture tex) {
31 		bounds = FloatRect(0, 0, tex.getSize.x, tex.getSize.y);
32     auto image = tex.copyToImage;
33     foreach(x; 0..image.getSize.x) {
34       auto fr = FloatRect(x, 0, 1, 0);
35 
36       foreach(y; 0..image.getSize.y) {
37         immutable color = image.getPixel(x, y);
38         immutable ok = color.a > 127 && color != Color(247, 247, 247);
39 
40         if(ok) {
41           fr.height += 1;
42         } else {
43           if(fr.height != 0) {
44             rects ~= fr;
45           }
46           fr = FloatRect(x, y, 1, 0);
47         }
48       }
49       if(fr.height != 0) {
50         rects ~= fr;
51       }
52     }
53 
54     initial_tex_width = tex.getSize.x;
55   }
56 
57 	override void draw(RenderTarget target, RenderStates states) {
58 		if(!draw_colliders) {
59 			return;
60 		}
61 
62     ensure_translation();
63 		auto rs = new RectangleShape;
64 		foreach(r; rects) {
65 			rs.position(Vector2f(r.left, r.top));
66 			rs.size(Vector2f(r.width, r.height));
67 			rs.fillColor(Color(0, 255, 0, 127));
68 			target.draw(rs, states);
69 		}
70 
71 		rs.position(Vector2f(bounds.left, bounds.top));
72 		rs.size(Vector2f(bounds.width, bounds.height));
73 		rs.fillColor(Color(255, 0, 0, 127));
74 		target.draw(rs);
75 	}
76 
77   /// Returns true if two colliders intersect
78   bool intersects(Collider other) {
79 		if(!bounds.intersects(other.bounds)) {
80 			return false;
81 		}
82 
83     ensure_translation();
84     other.ensure_translation();
85     foreach(r1; rects)
86     foreach(r2; other.rects) {
87       if(r1.intersects(r2)) {
88         return true;
89       }
90     }
91 
92     return false;
93   }
94 
95   /// Initializes an empty collider
96   private this() { }
97 
98   /// Returns a collider translated by the given vector
99   Collider translate(Vector2f vec) const {
100     auto c = new Collider;
101     c.rects = rects.dup;
102     if(delayed_translation == optional.none) {
103       c.delayed_translation = optional.some(vec);
104     } else {
105       c.delayed_translation = vec + delayed_translation;
106     }
107 
108 		c.bounds = bounds;
109 		c.bounds.top += vec.y;
110 		c.bounds.left += vec.x;
111     c.initial_tex_width = initial_tex_width;
112 
113     return c;
114   }
115 
116 	/// Merges colliders
117 	void merge(Collider other) {
118     ensure_translation();
119     other.ensure_translation();
120 
121 		foreach(rect; other.rects) {
122 			rects ~= rect;
123 		}
124 		if(bounds.left > other.bounds.left) {
125 			bounds.left = other.bounds.left;
126 		}
127 		if(bounds.top > other.bounds.top) {
128 			bounds.top = other.bounds.top;
129 		}
130 		if(bounds.left + bounds.width < other.bounds.left + other.bounds.width) {
131 			bounds.width = other.bounds.left - bounds.left + other.bounds.width;
132 		}
133 		if(bounds.top + bounds.height < other.bounds.top + other.bounds.height) {
134 			bounds.height = other.bounds.top - bounds.top + other.bounds.height;
135 		}
136 
137     initial_tex_width = max(initial_tex_width, other.initial_tex_width);
138 	}
139 
140   /// Replicates a collider horizontally
141   Collider replicate(uint times) const {
142     Collider c = new Collider;
143 
144     foreach(i; 0..times) {
145       c.merge(translate(Vector2f(i * initial_tex_width, 0)));
146     }
147 
148     c.initial_tex_width = initial_tex_width * times;
149 
150     return c;
151   }
152 }