mempool is optional now
[d2df-sdl.git] / src / game / g_holmes_ol.inc
blob23949cac45f1f9f368a54a011ac9f153c7021279
1 (* Copyright (C)  DooM 2D:Forever Developers
2  *
3  * This program is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation, either version 3 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *)
16 {.$INCLUDE ../shared/a_modes.inc}
17 type
18   TOutliner = class
19   private
20     type
21       TSpan = record
22         x0, x1: Integer;
23         next: Integer; // index
24       end;
25       PSpan = ^TSpan;
27   private
28     mWidth, mHeight: Integer;
29     spans: array of TSpan;
30     firstFreeSpan: Integer; // span index or -1
31     usedSpans: Integer;
32     lines: array of Integer; // span indicies
34   private
35     function allocSpan (ax0, ax1: Integer): Integer; // returns span index
36     procedure freeSpan (idx: Integer);
38   public
39     type
40       TSpanX = record
41         x0, x1: Integer;
42       end;
44       TSpanEnumerator = record
45       private
46         spans: array of TSpan;
47         cur: Integer;
48         first: Boolean;
49       public
50         constructor Create (master: TOutliner; y: Integer);
51         function MoveNext (): Boolean; inline;
52         function getCurrent (): TSpanX; inline;
53         function GetEnumerator (): TSpanEnumerator; inline;
54         property Current: TSpanX read getCurrent;
55       end;
57       TSpanEdgeEnumerator = record
58       private
59         spans: array of TSpan;
60         spi, usp: Integer;
61         sx, ex: Integer;
62         cur: TSpanX;
63         doSkipUSP: Boolean;
64       private
65         procedure nextSPI (); inline;
66       public
67         constructor Create (master: TOutliner; y, dy: Integer);
68         function MoveNext (): Boolean; inline;
69         function GetEnumerator (): TSpanEdgeEnumerator; inline;
70         property Current: TSpanX read cur;
71       end;
73   public
74     constructor Create (aw, ah: Integer);
75     destructor Destroy (); override;
77     procedure clear ();
78     procedure setup (aw, ah: Integer);
80     procedure addSpan (ax0, ax1, y: Integer);
81     procedure addRect (x, y, w, h: Integer);
83     function eachSpanAtY (y: Integer): TSpanEnumerator;
84     function eachSpanEdgeAtY (y, dy: Integer): TSpanEdgeEnumerator;
85     //function GetEnumerator (): TValEnumerator;
87   public
88     property width: Integer read mWidth;
89     property height: Integer read mHeight;
90   end;
93 // ////////////////////////////////////////////////////////////////////////// //
94 function TOutliner.allocSpan (ax0, ax1: Integer): Integer;
95 begin
96   result := firstFreeSpan;
97   if (result = -1) then
98   begin
99     result := usedSpans;
100     if (usedSpans = Length(spans)) then SetLength(spans, usedSpans+512);
101     Inc(usedSpans);
102   end
103   else
104   begin
105     firstFreeSpan := spans[result].next;
106   end;
107   with (spans[result]) do
108   begin
109     x0 := ax0;
110     x1 := ax1;
111     next := -1;
112   end;
113 end;
116 procedure TOutliner.freeSpan (idx: Integer);
117 begin
118   if (idx >= 0) and (idx < usedSpans) then
119   begin
120     spans[idx].next := firstFreeSpan;
121     firstFreeSpan := idx;
122   end;
123 end;
126 constructor TOutliner.Create (aw, ah: Integer);
128   f: Integer;
129 begin
130   assert(aw > 0);
131   assert(ah > 0);
132   mWidth := aw;
133   mHeight := ah;
134   SetLength(lines, mHeight);
135   for f := 0 to High(lines) do lines[f] := -1;
136   usedSpans := 0;
137   firstFreeSpan := -1;
138 end;
141 destructor TOutliner.Destroy ();
142 begin
143   spans := nil;
144   lines := nil;
145   inherited;
146 end;
149 procedure TOutliner.setup (aw, ah: Integer);
151   f: Integer;
152 begin
153   assert(aw > 0);
154   assert(ah > 0);
155   if (mWidth <> aw) or (mHeight <> ah) then
156   begin
157     mWidth := aw;
158     mHeight := ah;
159     SetLength(lines, mHeight);
160   end;
161   for f := 0 to High(lines) do lines[f] := -1;
162   usedSpans := 0;
163   firstFreeSpan := -1;
164 end;
167 procedure TOutliner.clear ();
169   f: Integer;
170 begin
171   for f := 0 to High(lines) do lines[f] := -1;
172   usedSpans := 0;
173   firstFreeSpan := -1;
174 end;
177 procedure TOutliner.addSpan (ax0, ax1, y: Integer);
178   procedure fixFrom (spi: Integer);
179   var
180     sp, sn: PSpan;
181     spf: Integer;
182   begin
183     assert(spi <> -1);
184     sp := @spans[spi];
185     while true do
186     begin
187       spf := sp.next;
188       if (spf = -1) then break;
189       sn := @spans[spf];
190       // join?
191       if (sp.x1+1 = sn.x0) then
192       begin
193         //conprintfln("JOIN: sp=(%s,%s); sn=(%s,%s)", sp.x0, sp.x1, sn.x0, sn.x1);
194         sp.x1 := sn.x1;
195       end
196       else if (sn.x0 <= sp.x1) then
197       begin
198         // overlaps
199         //conprintfln("OVER: sp=(%s,%s); sn=(%s,%s)", sp.x0, sp.x1, sn.x0, sn.x1);
200         if (sp.x1 < sn.x1) then sp.x1 := sn.x1;
201       end
202       else
203       begin
204         break;
205       end;
206       sp.next := sn.next;
207       freeSpan(spf);
208     end;
209   end;
212   sprev: Integer = -1;
213   scur: Integer;
214   sp: PSpan;
215 begin
216   if (ax1 < ax0) then exit;
217   if (y < 0) or (y >= mHeight) then exit;
218   if (ax1 < -42) or (ax0 > mWidth+42) then exit;
219   if (ax0 < -42) then ax0 := -42;
220   if (ax1 > mWidth+42) then ax1 := mWidth+42;
221   // new span on empty line?
222   scur := lines[y];
223   if (scur = -1) then
224   begin
225     lines[y] := allocSpan(ax0, ax1);
226     exit;
227   end;
228   // starts before the first span?
229   sp := @spans[scur];
230   if (ax0 < sp.x0) then
231   begin
232     // insert new span as the first one
233     sprev := allocSpan(ax0, ax1);
234     spans[sprev].next := scur;
235     lines[y] := sprev;
236     // fix invalid spans (if any)
237     fixFrom(sprev);
238     exit;
239   end;
240   // find span to expand
241   while (scur <> -1) do
242   begin
243     sp := @spans[scur];
244     // join spans?
245     if (sp.x1+1 = ax0) then
246     begin
247       sp.x1 := ax1;
248       fixFrom(scur);
249       exit;
250     end;
251     // starts in current span?
252     if (ax0 >= sp.x0) and (ax0 <= sp.x1) then
253     begin
254       if (ax1 >= sp.x0) and (ax1 <= sp.x1) then exit; // ends in current span, nothing to do
255       // extend current span, and fix bad spans
256       sp.x1 := ax1;
257       fixFrom(scur);
258       exit;
259     end;
260     // starts after the current span, but before the next span?
261     if (sp.next <> -1) and (ax0 > sp.x1) and (ax0 < spans[sp.next].x0) then
262     begin
263       // insert before next span
264       sprev := allocSpan(ax0, ax1);
265       spans[sprev].next := sp.next;
266       sp.next := sprev;
267       fixFrom(sp.next);
268       exit;
269     end;
270     // try next span
271     sprev := scur;
272     scur := sp.next;
273   end;
274   // just append new span
275   assert(sprev <> -1);
276   spans[sprev].next := allocSpan(ax0, ax1);
277 end;
280 procedure TOutliner.addRect (x, y, w, h: Integer);
281 begin
282   if (w < 1) or (h < 1) then exit;
283   while (h > 0) do
284   begin
285     addSpan(x, x+w-1, y);
286     Inc(y);
287     Dec(h);
288   end;
289 end;
292 function TOutliner.eachSpanAtY (y: Integer): TSpanEnumerator;
293 begin
294   result := TSpanEnumerator.Create(self, y);
295 end;
298 function TOutliner.eachSpanEdgeAtY (y, dy: Integer): TSpanEdgeEnumerator;
299 begin
300   result := TSpanEdgeEnumerator.Create(self, y, dy);
301 end;
304 // ////////////////////////////////////////////////////////////////////////// //
305 constructor TOutliner.TSpanEnumerator.Create (master: TOutliner; y: Integer);
306 begin
307   spans := master.spans;
308   cur := -1;
309   first := true;
310   if (y < 0) or (y >= master.mHeight) then exit;
311   cur := master.lines[y];
312 end;
314 function TOutliner.TSpanEnumerator.MoveNext (): Boolean; inline;
315 begin
316        if first then first := false
317   else if (cur <> -1) then cur := spans[cur].next;
318   result := (cur <> -1);
319 end;
321 function TOutliner.TSpanEnumerator.getCurrent (): TSpanX; inline;
322 begin
323   result.x0 := spans[cur].x0;
324   result.x1 := spans[cur].x1;
325 end;
327 function TOutliner.TSpanEnumerator.GetEnumerator (): TSpanEnumerator; inline;
328 begin
329   result := self;
330 end;
333 // ////////////////////////////////////////////////////////////////////////// //
334 function TOutliner.TSpanEdgeEnumerator.GetEnumerator (): TSpanEdgeEnumerator; inline;
335 begin
336   result := self;
337 end;
339 constructor TOutliner.TSpanEdgeEnumerator.Create (master: TOutliner; y, dy: Integer);
340 begin
341   doSkipUSP := false;
342   spans := master.spans;
343   if (dy = 0) or (y < 0) or (y >= master.mHeight) then begin spi := -1; exit; end;
345   spi := master.lines[y];
346   if (spi = -1) then exit;
348   if (dy < 0) then
349   begin
350     if (y < 1) then begin spi := -1; exit; end;
351     usp := master.lines[y-1];
352   end
353   else
354   begin
355     if (y+1 >= master.mHeight) then begin spi := -1; exit; end;
356     usp := master.lines[y+1];
357   end;
359   sx := spans[spi].x0;
360   ex := spans[spi].x1;
361 end;
363 procedure TOutliner.TSpanEdgeEnumerator.nextSPI (); inline;
364 begin
365   if (spi <> -1) then spi := spans[spi].next;
366   if (spi <> -1) then
367   begin
368     sx := spans[spi].x0;
369     ex := spans[spi].x1;
370   end;
371 end;
373 function TOutliner.TSpanEdgeEnumerator.MoveNext (): Boolean; inline;
374 begin
375   result := false;
377   while true do
378   begin
379     if doSkipUSP then
380     begin
381       doSkipUSP := false;
382       // skip usp (this will draw final dot)
383       cur.x0 := spans[usp].x1;
384       cur.x1 := cur.x0;
385       sx := cur.x1+1;
386       assert(sx <= ex);
387       result := true;
388       exit;
389     end;
391     if (spi = -1) then exit;
393     // skip usp until sx
394     while (usp <> -1) do
395     begin
396       if (spans[usp].x1 < sx) then begin usp := spans[usp].next; continue; end;
397       break;
398     end;
400     // no more usps?
401     if (usp = -1) then
402     begin
403       if (sx <= ex) then
404       begin
405         cur.x0 := sx;
406         cur.x1 := ex;
407         nextSPI();
408         result := true;
409       end
410       else
411       begin
412         nextSPI();
413         result := (spi <> -1);
414         if result then
415         begin
416           cur.x0 := sx;
417           cur.x1 := ex;
418         end;
419       end;
420       exit;
421     end;
423     // usp covers the whole spi?
424     if (sx >= spans[usp].x0) and (ex <= spans[usp].x1) then
425     begin
426       // yes; next spi
427       nextSPI();
428       continue;
429     end;
431     // usp starts after ex?
432     if (ex < spans[usp].x0) then
433     begin
434       // yes; draw that part
435       cur.x0 := sx;
436       cur.x1 := ex;
437       // next spi
438       nextSPI();
439       result := true;
440       exit;
441     end;
443     // usp starts after sx?
444     if (sx < spans[usp].x0) then
445     begin
446       // yes; draw that part
447       cur.x0 := sx;
448       cur.x1 := spans[usp].x0;
449       // does usp covers what is left?
450       if (ex <= spans[usp].x1) then
451       begin
452         // yes; next spi
453         nextSPI();
454       end
455       else
456       begin
457         // no; skip usp
458         doSkipUSP := true;
459         //sx := spans[usp].x1+1;
460         //assert(sx <= ex);
461       end;
462       result := true;
463       exit;
464     end
465     else
466     begin
467       // usp starts before sx
468       assert(sx >= spans[usp].x0);
469       assert(ex > spans[usp].x1);
470     end;
472     // skip usp (this will draw final dot)
473     doSkipUSP := true;
474   end;
475 end;