GfxRegions.Mod 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250
  1. (* ETH Oberon, Copyright 2001 ETH Zuerich Institut fuer Computersysteme, ETH Zentrum, CH-8092 Zuerich.
  2. Refer to the "General ETH Oberon System Source License" contract available at: http://www.oberon.ethz.ch/ *)
  3. MODULE GfxRegions; (** portable *) (* eos *)
  4. (** AUTHOR "eos"; PURPOSE "Arbitrarily shaped two_dimensional regions"; *)
  5. (*
  6. 17.2.97 - eliminated rectangle type, added shift offsets, made enumerator extensible
  7. 2.5.97 - prevent dropouts when validating
  8. 17.7.97 - fixed bug in Validate (trying to copy filler spans if data was reallocated)
  9. 17.7.97 - eliminated size field
  10. 12.3.98 - eliminated shifted operations, fixed Shift to treat boundary cases correctly
  11. 5.5.98 - fixed bug in Intersect (wrong index into arg region)
  12. *)
  13. CONST
  14. (** mode for reducing regions to non_overlapping areas **)
  15. Winding* = 0; (** non_zero winding rule **)
  16. EvenOdd* = 1; (** exclusive_or rule **)
  17. (** interval of valid region coordinates (UBound - LBound is still representable within INTEGER **)
  18. UBound* = MAX(INTEGER) DIV 2;
  19. LBound* = MIN(INTEGER) DIV 2;
  20. BlockSize = 512; (* size increment for region data blocks *)
  21. Enter = 1; Exit = -1; (* direction of bounding curve at scanline intersection *)
  22. FirstSlice = 2; (* index of first slice *)
  23. Bottom = MIN(INTEGER); Top = MAX(INTEGER); (* sentinel values *)
  24. TYPE
  25. RegionData = POINTER TO ARRAY OF LONGINT;
  26. (** regions of arbitrary shape **)
  27. Region* = POINTER TO RegionDesc;
  28. RegionDesc* = RECORD
  29. llx*, lly*, urx*, ury*: INTEGER; (** bounding box **)
  30. mode*: INTEGER; (** mode for reducing region to non_overlapping areas (Winding/EvenOdd) **)
  31. valid: BOOLEAN; (* flag if points in data array are consistent (i.e. sorted & compacted) *)
  32. data: RegionData; (* points defining region boundary *)
  33. points: LONGINT; (* number of data points actually used *)
  34. END;
  35. (** region enumeration **)
  36. EnumData* = RECORD END;
  37. Enumerator* = PROCEDURE (llx, lly, urx, ury: INTEGER; VAR edata: EnumData);
  38. VAR
  39. Data: RegionData; (* temporary region data for merging *)
  40. DataSize: LONGINT; (* number of points allocated for Data *)
  41. RectRegion: Region; (* temporary rectangular region for boolean operations *)
  42. (**--- Rectangles ---**)
  43. (** make rectangle large enough to include a point **)
  44. PROCEDURE IncludePoint* (VAR llx, lly, urx, ury: INTEGER; x, y: INTEGER);
  45. BEGIN
  46. IF x < llx THEN llx := x END;
  47. IF x > urx THEN urx := x END;
  48. IF y < lly THEN lly := y END;
  49. IF y > ury THEN ury := y END
  50. END IncludePoint;
  51. (** make rectangle large enough to include other rectangle **)
  52. PROCEDURE IncludeRect* (VAR llx, lly, urx, ury: INTEGER; illx, illy, iurx, iury: INTEGER);
  53. BEGIN
  54. IF illx < llx THEN llx := illx END;
  55. IF iurx > urx THEN urx := iurx END;
  56. IF illy < lly THEN lly := illy END;
  57. IF iury > ury THEN ury := iury END
  58. END IncludeRect;
  59. (** shrink rectangle to area within other rectangle **)
  60. PROCEDURE ClipRect* (VAR llx, lly, urx, ury: INTEGER; cllx, clly, curx, cury: INTEGER);
  61. BEGIN
  62. IF cllx > llx THEN llx := cllx END;
  63. IF curx < urx THEN urx := curx END;
  64. IF clly > lly THEN lly := clly END;
  65. IF cury < ury THEN ury := cury END
  66. END ClipRect;
  67. (** return whether rectangle is empty **)
  68. PROCEDURE RectEmpty* (llx, lly, urx, ury: INTEGER): BOOLEAN;
  69. BEGIN
  70. RETURN (llx >= urx) OR (lly >= ury)
  71. END RectEmpty;
  72. (** return whether (non_empty) rectangle is completely inside other rectangle **)
  73. PROCEDURE RectInRect* (llx, lly, urx, ury, illx, illy, iurx, iury: INTEGER): BOOLEAN;
  74. BEGIN
  75. RETURN (llx >= illx) & (urx <= iurx) & (lly >= illy) & (ury <= iury)
  76. END RectInRect;
  77. (** return whether (non_empty) rectangle intersects other rectangle **)
  78. PROCEDURE RectsIntersect* (llx, lly, urx, ury, illx, illy, iurx, iury: INTEGER): BOOLEAN;
  79. BEGIN
  80. RETURN (llx < iurx) & (urx > illx) & (lly < iury) & (ury > illy)
  81. END RectsIntersect;
  82. (** return whether rectangle contains point **)
  83. PROCEDURE PointInRect* (x, y: INTEGER; llx, lly, urx, ury: INTEGER): BOOLEAN;
  84. BEGIN
  85. RETURN (x >= llx) & (x < urx) & (y >= lly) & (y < ury)
  86. END PointInRect;
  87. (*--- Auxiliary Routines For Managing Regions ---*)
  88. (*
  89. Implementation notes:
  90. Regions are managed by slicing them horizontally. For each scanline y, a set of spans on the scanline defines which
  91. parts of the scanline are part of the region. The spans are defined through the x_coordinates of their end points.
  92. Every point on a scanline has a direction attribute, which specifies whether the point starts a span (Enter) or ends
  93. one (Exit), allowing spans to nest or overlap.
  94. The x_ and y_coordinates of a point along with its direction are encoded into a LONGINT. The chosen encoding
  95. weights the y_coordinate most, followed by the x_coordinate and the direction of an intersection. Visiting all
  96. encoded points in ascending order therefore traverses all spans of the region from the bottom left corner to the
  97. top right corner.
  98. In order to save space, identical slices adjacent to each other are stored only once. The bottommost scanline of
  99. an identical sequence of scanlines serves as a representant for the whole sequence; all others are eliminated.
  100. This means that if no points exist for a certain y_coordinate, the spans of the corresponding scanline are identical
  101. to those of the one below it. As a consequence, scanlines that are completely outside the region need an empty
  102. filler span to distinguish them from eliminated scanlines. A filler span consists of two points located at UBound,
  103. one entering the region and the other leaving it.
  104. Most operations modifying regions append new points in ascending order to the sequence of existing points and
  105. then merge the two sequences again. If points cannot be appended in order, the whole set of points has to be
  106. sorted before any other operation can be executed. Doing this immediately after the sequence of points has been
  107. invalidated can decrease performance significantly if a lot of invalidating operations are issued in sequence, as is
  108. typically the case with AddPoint. This is why regions have a valid flag, indicating whether encoded points are sorted
  109. or not. Invalidating operations only have to set valid to FALSE, other operations will eventually validate the region
  110. again, at the same time eliminating multiple points and overlapping spans.
  111. *)
  112. (* encode point coordinates and curve direction into a LONGINT *)
  113. PROCEDURE Encode (VAR item: LONGINT; u, v, dir: LONGINT);
  114. BEGIN
  115. item := ASH(v, 16) + ASH((u + 4000H) MOD 8000H, 1) + ASH(1 + dir, -1)
  116. END Encode;
  117. (* restore point coordinates and curve direction from an encoded LONGINT *)
  118. PROCEDURE Decode (item: LONGINT; VAR u, v, dir: INTEGER);
  119. BEGIN
  120. v := INTEGER(ASH(item, -16));
  121. u := INTEGER(ASH(item, -1) MOD 8000H - 4000H);
  122. dir := INTEGER(ASH(item MOD 2, 1) - 1)
  123. END Decode;
  124. (* copy points between region data blocks *)
  125. PROCEDURE CopyPoints (src, dst: RegionData; points: LONGINT);
  126. VAR i: LONGINT;
  127. BEGIN
  128. i := 0;
  129. WHILE (i < points) & (i < LEN(dst)) & (i < LEN(src)) DO
  130. dst[i] := src[i];
  131. INC(i)
  132. END
  133. END CopyPoints;
  134. (* append point to region data *)
  135. PROCEDURE Append (reg: Region; u, v, dir: INTEGER);
  136. VAR size: LONGINT; data: RegionData;
  137. BEGIN
  138. IF reg.data = NIL THEN
  139. NEW(reg.data, BlockSize)
  140. ELSIF reg.points >= LEN(reg.data^) THEN (* grow data array *)
  141. size := LEN(reg.data^) + BlockSize;
  142. NEW(data, size);
  143. CopyPoints(reg.data, data, reg.points);
  144. reg.data := data
  145. END;
  146. Encode(reg.data[reg.points], u, v, dir);
  147. INC(reg.points)
  148. END Append;
  149. (* copy region data *)
  150. PROCEDURE CopyData (src, dst: Region);
  151. VAR size: LONGINT;
  152. BEGIN
  153. IF src.points > 0 THEN
  154. IF (dst.data = NIL) OR (LEN(dst.data^) < src.points) THEN
  155. size := src.points + (-src.points) MOD BlockSize; (* round up to multiple of BlockSize *)
  156. NEW(dst.data, size)
  157. END;
  158. CopyPoints(src.data, dst.data, src.points)
  159. END;
  160. dst.points := src.points;
  161. dst.llx := src.llx; dst.lly := src.lly;
  162. dst.urx := src.urx; dst.ury := src.ury;
  163. dst.valid := src.valid
  164. END CopyData;
  165. (* re_calculate bounding box of (valid!) region *)
  166. PROCEDURE CalcRect (reg: Region);
  167. VAR data: RegionData; n: LONGINT; u, v, dir, x: INTEGER;
  168. BEGIN
  169. ASSERT(reg.valid);
  170. IF reg.points > 0 THEN
  171. data := reg.data;
  172. n := FirstSlice;
  173. Decode(data[n], u, v, dir);
  174. reg.llx := u; reg.urx := u; reg.lly := v;
  175. REPEAT
  176. reg.ury := v; x := u;
  177. REPEAT
  178. IF (dir = Enter) & (u < reg.llx) THEN
  179. reg.llx := u; x := u
  180. ELSIF (dir = Exit) & (u > reg.urx) & (u > x) THEN (* last term excludes filler spans *)
  181. reg.urx := u
  182. END;
  183. INC(n);
  184. Decode(data[n], u, v, dir)
  185. UNTIL v > reg.ury;
  186. UNTIL v = Top
  187. END
  188. END CalcRect;
  189. (* quick sort with limited recursion guarantee *)
  190. PROCEDURE SafeQuickSort(VAR data: ARRAY OF LONGINT; lo, hi: LONGINT);
  191. CONST limit = 8;
  192. VAR i, x, j, t, shortLo, shortHi, longLo, longHi: LONGINT;
  193. BEGIN
  194. WHILE (hi > lo) DO
  195. IF hi - lo < limit THEN (* use straight insertion for less than limit entries... *)
  196. i := lo + 1;
  197. WHILE i <= hi DO
  198. x := data[i];
  199. j := i;
  200. WHILE (j > lo) & (x < data[j - 1]) DO
  201. data[j] := data[j - 1];
  202. DEC(j)
  203. END;
  204. data[j] := x;
  205. INC(i)
  206. END;
  207. hi := lo; (* termination! *)
  208. ELSE
  209. i := lo; j := hi;
  210. x := data[(lo + hi) DIV 2];
  211. REPEAT
  212. WHILE data[i] < x DO INC(i) END;
  213. WHILE data[j] > x DO DEC(j) END;
  214. IF i <= j THEN
  215. t := data[i]; data[i] := data[j]; data[j] := t;
  216. INC(i); DEC(j)
  217. END
  218. UNTIL i > j;
  219. IF (j - lo) < (hi - i) THEN (* generalized interval [lo,j] is smaller than interval [hi, i] *)
  220. shortLo := lo; shortHi := j;
  221. longLo := i; longHi := hi;
  222. ELSE
  223. longLo := lo; longHi := j;
  224. shortLo := i; shortHi := hi;
  225. END;
  226. IF (shortLo < shortHi) THEN
  227. SafeQuickSort(data, shortLo, shortHi);
  228. END;
  229. (* now: tail recursion, call of: SortRange(data, longLo, longHi) *)
  230. lo := longLo; hi := longHi;
  231. END
  232. END;
  233. END SafeQuickSort;
  234. (* haven't needed this in a long time but better keep my fingers crossed
  235. PROCEDURE Show* (data: RegionData; points: LONGINT);
  236. VAR n: LONGINT; y, x, dir, v: INTEGER;
  237. BEGIN
  238. n := 0;
  239. Decode(data[n], x, y, dir);
  240. INC(n);
  241. WHILE n < points DO
  242. Out.Int(y, 0); Out.String(": "); Out.Int(x, 0); IF dir = Enter THEN Out.Char("+") ELSE Out.Char("-") END;
  243. LOOP
  244. Decode(data[n], x, v, dir);
  245. INC(n);
  246. IF (n >= points) OR (v # y) THEN EXIT END;
  247. Out.Char(" "); Out.Int(x, 0); IF dir = Enter THEN Out.Char("+") ELSE Out.Char("-") END
  248. END;
  249. y := v;
  250. Out.Ln
  251. END;
  252. Out.Int(y, 0); Out.String(": "); Out.Int(x, 0); IF dir = Enter THEN Out.Char("+") ELSE Out.Char("-") END; Out.Ln;
  253. Out.String("---"); Out.Ln
  254. END Show;
  255. PROCEDURE ShowReg* (reg: Region);
  256. BEGIN
  257. IF reg.points = 0 THEN
  258. Out.String("["); Out.Int(reg.llx, 5); Out.Int(reg.lly, 5); Out.Int(reg.urx, 5); Out.Int(reg.ury, 5); Out.Char("]"); Out.Ln
  259. ELSE
  260. Show(reg.data, reg.points)
  261. END
  262. END ShowReg;
  263. *)
  264. (* eliminate duplicate slices *)
  265. PROCEDURE Compact (reg: Region; src: RegionData);
  266. VAR rslice, dslice, sn, rn, dn: LONGINT; dst: RegionData; su, sv, sdir, ru, rv, rdir, sy, ry: INTEGER;
  267. BEGIN
  268. rslice := 0; (* start of current reference slice is the bottom sentinel slice *)
  269. dslice := FirstSlice; (* start of current destination slice *)
  270. sn := FirstSlice; (* current reading position *)
  271. dst := reg.data;
  272. Decode(src[sn], su, sv, sdir);
  273. REPEAT
  274. (* compare next source slice to current reference slice *)
  275. rn := rslice; dn := dslice;
  276. Decode(dst[rn], ru, rv, rdir);
  277. sy := sv; ry := rv;
  278. WHILE (sv = sy) & (rv = ry) & (su = ru) & (sdir = rdir) DO (* copy while slices are equal *)
  279. dst[dn] := src[sn];
  280. INC(dn); INC(sn); INC(rn);
  281. Decode(src[sn], su, sv, sdir);
  282. Decode(dst[rn], ru, rv, rdir)
  283. END;
  284. IF (sv = sy) OR (rv = ry) THEN (* slices are different => copy rest of source slice to destination *)
  285. WHILE sv = sy DO
  286. dst[dn] := src[sn];
  287. INC(dn); INC(sn);
  288. Decode(src[sn], su, sv, sdir)
  289. END;
  290. (* the slice just written becomes the new reference slice *)
  291. rslice := dslice;
  292. dslice := dn
  293. END
  294. UNTIL sv = Top;
  295. IF dn = 6 THEN (* region contains only one rectangle *)
  296. Decode(dst[FirstSlice], reg.llx, reg.lly, rdir);
  297. Decode(dst[FirstSlice + 1], reg.urx, reg.lly, rdir);
  298. Decode(dst[FirstSlice + 2], ru, reg.ury, rdir);
  299. reg.points := 0
  300. ELSE
  301. Encode(dst[dn], UBound, Top, Exit);
  302. reg.points := dn + 1
  303. END
  304. END Compact;
  305. (* merge two runs of data points *)
  306. PROCEDURE Merge (reg: Region; split: LONGINT);
  307. VAR data: RegionData; n, N, m, M, p, tmp: LONGINT; nu, nv, ndir, mu, mv, mdir, sum, u, v, inc, nsum: INTEGER;
  308. BEGIN{EXCLUSIVE}
  309. data := reg.data;
  310. n := 0; N := split;
  311. Decode(data[n], nu, nv, ndir);
  312. m := split; M := reg.points;
  313. Decode(data[m], mu, mv, mdir);
  314. p := 0;
  315. Append(reg, UBound, Top, Exit); (* sentinel for upper part *)
  316. IF DataSize <= M THEN (* reallocate temporary buffer *)
  317. DataSize := M - M MOD BlockSize + BlockSize;
  318. NEW(Data, DataSize)
  319. END;
  320. WHILE (n < N) & (m < M) DO
  321. tmp := p;
  322. v := MIN(nv, mv);
  323. (* eliminate overlapping spans before copying them *)
  324. sum := 0;
  325. REPEAT
  326. (* get next point *)
  327. IF (nv < mv) OR (nv = mv) & (nu <= mu) THEN
  328. u := nu; inc := ndir;
  329. INC(n);
  330. Decode(data[n], nu, nv, ndir)
  331. ELSE
  332. u := mu; inc := mdir;
  333. INC(m);
  334. Decode(data[m], mu, mv, mdir)
  335. END;
  336. (* accumulate directions of coincident points *)
  337. WHILE (nv = v) & (nu = u) DO
  338. INC(inc, ndir); INC(n);
  339. Decode(data[n], nu, nv, ndir)
  340. END;
  341. WHILE (mv = v) & (mu = u) DO
  342. INC(inc, mdir); INC(m);
  343. Decode(data[m], mu, mv, mdir)
  344. END;
  345. IF inc # 0 THEN (* append point to merged data *)
  346. nsum := sum + inc;
  347. IF reg.mode = Winding THEN
  348. IF (sum <= 0) & (nsum > 0) THEN
  349. Encode(Data[p], u, v, Enter); INC(p)
  350. ELSIF (sum > 0) & (nsum <= 0) THEN
  351. Encode(Data[p], u, v, Exit); INC(p)
  352. END
  353. ELSIF (reg.mode = EvenOdd) & ((sum > 0) & ODD(sum) # (nsum > 0) & ODD(nsum)) THEN
  354. IF ODD(sum) THEN
  355. Encode(Data[p], u, v, Exit)
  356. ELSE
  357. Encode(Data[p], u, v, Enter)
  358. END;
  359. INC(p)
  360. END;
  361. sum := nsum
  362. END
  363. UNTIL (nv > v) & (mv > v);
  364. IF p = tmp THEN (* line is empty => append filler slice *)
  365. Encode(Data[p], UBound, v, Enter); INC(p);
  366. Encode(Data[p], UBound, v, Exit); INC(p)
  367. END
  368. END;
  369. (* copy remaining points *)
  370. WHILE n < N DO
  371. Data[p] := data[n];
  372. INC(p); INC(n)
  373. END;
  374. WHILE m < M DO
  375. Data[p] := data[m];
  376. INC(p); INC(m)
  377. END;
  378. (* copy merged data back and eliminate duplicate scanlines *)
  379. Compact(reg, Data)
  380. END Merge;
  381. (* bring region data into consistent state *)
  382. PROCEDURE Validate (reg: Region);
  383. VAR data: RegionData; points, rn, wn, tmp: LONGINT; u, v, dir, y, sum, x, inc: INTEGER;
  384. BEGIN
  385. IF ~reg.valid THEN
  386. data := reg.data;
  387. SafeQuickSort(data^,0,reg.points-1);
  388. points := reg.points;
  389. rn := FirstSlice; wn := FirstSlice; (* read and write position *)
  390. Decode(data[rn], u, v, dir);
  391. REPEAT
  392. tmp := wn;
  393. y := v;
  394. sum := 0;
  395. REPEAT
  396. (* accumulate directions of coincident points *)
  397. x := u; inc := 0;
  398. REPEAT
  399. INC(inc, dir); INC(rn);
  400. Decode(data[rn], u, v, dir)
  401. UNTIL (v > y) OR (u > x);
  402. IF x < UBound THEN
  403. IF reg.mode = Winding THEN
  404. IF sum = 0 THEN
  405. Encode(data[wn], x, y, Enter); INC(wn);
  406. INC(x) (* prevent dropouts *)
  407. END;
  408. INC(sum, inc);
  409. IF sum = 0 THEN
  410. Encode(data[wn], x, y, Exit); INC(wn)
  411. END
  412. ELSIF reg.mode = EvenOdd THEN
  413. IF ~ODD(sum) THEN
  414. Encode(data[wn], x, y, Enter); INC(wn);
  415. INC(x) (* prevent dropouts *)
  416. END;
  417. INC(sum, inc);
  418. IF ~ODD(sum) THEN
  419. Encode(data[wn], x, y, Exit); INC(wn)
  420. END
  421. END
  422. END
  423. UNTIL v > y;
  424. IF wn = tmp THEN (* insert filler span if all slices have been eliminated *)
  425. Encode(data[wn], UBound, y, Enter); INC(wn);
  426. Encode(data[wn], UBound, y, Exit); INC(wn)
  427. ELSIF v > y + 1 THEN (* add filler slice for disconnected regions *)
  428. INC(y);
  429. Append(reg, UBound, y, Enter);
  430. Append(reg, UBound, y, Exit)
  431. END
  432. UNTIL v = Top;
  433. Encode(data[wn], UBound, Top, Exit); INC(wn);
  434. IF reg.points > points THEN (* added filler slices => must merge *)
  435. IF wn < points THEN (* some points have been discarded => move filler slices *)
  436. rn := points; points := wn;
  437. REPEAT
  438. IF (wn < LEN(data)) & (rn < LEN(reg.data)) THEN
  439. data[wn] := reg.data[rn]; (* data may have been reallocated! *)
  440. END;
  441. INC(wn); INC(rn)
  442. UNTIL rn = reg.points;
  443. reg.data := data;
  444. reg.points := wn
  445. END;
  446. Merge(reg, points)
  447. ELSE (* points are still sorted *)
  448. reg.points := wn;
  449. Compact(reg, reg.data)
  450. END;
  451. reg.valid := TRUE
  452. END
  453. END Validate;
  454. (* find first point on line y or higher *)
  455. PROCEDURE FindUpper (reg: Region; y: INTEGER; VAR n: LONGINT);
  456. VAR item, i, j, m: LONGINT;
  457. BEGIN
  458. item := ASH(LONG(y), 16); (* leftmost possible point on line y *)
  459. i := 0; j := reg.points;
  460. WHILE i + 1 < j DO
  461. m := (i + j) DIV 2;
  462. IF reg.data[m] < item THEN
  463. i := m
  464. ELSE
  465. j := m
  466. END
  467. END;
  468. n := j
  469. END FindUpper;
  470. (* find first point on line y or lower *)
  471. PROCEDURE FindLower (reg: Region; y: INTEGER; VAR n: LONGINT);
  472. VAR v: INTEGER;
  473. BEGIN
  474. FindUpper(reg, y, n);
  475. v := INTEGER(ASH(reg.data[n], -16));
  476. IF v > y THEN (* => find leftmost point on lower slice *)
  477. DEC(n);
  478. y := INTEGER(ASH(reg.data[n], -16));
  479. REPEAT
  480. DEC(n)
  481. UNTIL (n < 0) OR (ASH(reg.data[n], -16) < y);
  482. INC(n)
  483. END
  484. END FindLower;
  485. (* enumerate (inverted) region within rectangle *)
  486. PROCEDURE Enum (reg: Region; llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData; enter: INTEGER);
  487. VAR data: RegionData; n, lo, hi: LONGINT; u, v, dir, y, top, x: INTEGER;
  488. BEGIN
  489. Validate(reg);
  490. ClipRect(llx, lly, urx, ury, LBound, LBound, UBound, UBound);
  491. data := reg.data;
  492. FindLower(reg, lly, n);
  493. Decode(data[n], u, v, dir);
  494. y := lly;
  495. REPEAT
  496. (* calculate height of slice *)
  497. lo := n;
  498. REPEAT
  499. INC(n);
  500. IF u < llx THEN
  501. lo := n
  502. END;
  503. Decode(data[n], u, v, dir)
  504. UNTIL v > y;
  505. hi := n;
  506. top := MIN(v, ury);
  507. (* enumerate spans of current slice *)
  508. n := lo;
  509. Decode(data[n], u, v, dir);
  510. x := llx;
  511. WHILE (v <= y) & ((u < urx) OR (dir # enter)) DO
  512. IF u > x THEN
  513. IF dir = enter THEN
  514. x := u
  515. ELSE
  516. enum(x, y, MIN(u, urx), top, edata)
  517. END
  518. END;
  519. INC(n);
  520. Decode(data[n], u, v, dir)
  521. END;
  522. IF n < hi THEN
  523. n := hi;
  524. Decode(data[n], u, v, dir)
  525. END;
  526. y := v
  527. UNTIL v >= ury
  528. END Enum;
  529. (* create data points for rectangular region *)
  530. PROCEDURE MakeData (reg: Region);
  531. BEGIN
  532. IF reg.points = 0 THEN
  533. Append(reg, UBound, Bottom, Enter);
  534. Append(reg, UBound, Bottom, Exit);
  535. IF (reg.llx <= reg.urx) & (reg.lly <= reg.ury) THEN
  536. Append(reg, reg.llx, reg.lly, Enter);
  537. Append(reg, reg.urx, reg.lly, Exit);
  538. Append(reg, UBound, reg.ury, Enter);
  539. Append(reg, UBound, reg.ury, Exit)
  540. END;
  541. Append(reg, UBound, Top, Enter)
  542. END
  543. END MakeData;
  544. (**--- Region Queries ---**)
  545. (** return whether region is empty **)
  546. PROCEDURE Empty* (reg: Region): BOOLEAN;
  547. BEGIN
  548. RETURN (reg.llx >= reg.urx) OR (reg.lly >= reg.ury)
  549. END Empty;
  550. (** return whether (non_empty) region is rectangular **)
  551. PROCEDURE IsRect* (reg: Region): BOOLEAN;
  552. BEGIN
  553. Validate(reg);
  554. RETURN reg.points = 0
  555. END IsRect;
  556. (** return whether point is inside (non_empty) region **)
  557. PROCEDURE PointInside* (x, y: INTEGER; reg: Region): BOOLEAN;
  558. VAR data: RegionData; n: LONGINT; u, v, dir: INTEGER;
  559. BEGIN
  560. IF ~PointInRect(x, y, reg.llx, reg.lly, reg.urx, reg.ury) THEN (* point not even within region rectangle *)
  561. RETURN FALSE
  562. ELSIF IsRect(reg) THEN (* region is rectangular *)
  563. RETURN TRUE
  564. END;
  565. (* find span containing point *)
  566. data := reg.data;
  567. FindLower(reg, y, n);
  568. Decode(data[n], u, v, dir);
  569. WHILE u < x DO
  570. INC(n);
  571. Decode(data[n], u, v, dir)
  572. END;
  573. RETURN (u = x) & (dir = Enter) OR (u > x) & (dir = Exit)
  574. END PointInside;
  575. (** return whether (non_empty) rectangle is completely inside (non_empty) region **)
  576. PROCEDURE RectInside* (llx, lly, urx, ury: INTEGER; reg: Region): BOOLEAN;
  577. VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
  578. BEGIN
  579. IF ~RectInRect(llx, lly, urx, ury, reg.llx, reg.lly, reg.urx, reg.ury) THEN (* not even within bounding rectangle *)
  580. RETURN FALSE
  581. ELSIF IsRect(reg) THEN (* region is rectangular *)
  582. RETURN TRUE
  583. END;
  584. data := reg.data;
  585. FindLower(reg, lly, n);
  586. Decode(data[n], u, v, dir);
  587. REPEAT
  588. y := v;
  589. WHILE (v = y) & (u <= llx) DO
  590. INC(n);
  591. Decode(data[n], u, v, dir)
  592. END;
  593. IF (v > y) OR (u < urx) OR (dir = Enter) THEN (* rectangle not covered by span *)
  594. RETURN FALSE
  595. END;
  596. (* skip to next line *)
  597. WHILE v = y DO
  598. INC(n);
  599. Decode(data[n], u, v, dir)
  600. END
  601. UNTIL v >= ury;
  602. RETURN TRUE (* rectangle is fully covered by spans *)
  603. END RectInside;
  604. (** return whether (non_empty) rectangle overlaps (non_empty) region **)
  605. PROCEDURE RectOverlaps* (llx, lly, urx, ury: INTEGER; reg: Region): BOOLEAN;
  606. VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
  607. BEGIN
  608. IF ~RectsIntersect(llx, lly, urx, ury, reg.llx, reg.lly, reg.urx, reg.ury) THEN
  609. RETURN FALSE (* rect does not even intersect region rectangle *)
  610. ELSIF IsRect(reg) THEN (* region is rectangular *)
  611. RETURN TRUE
  612. END;
  613. ClipRect(llx, lly, urx, ury, reg.llx, reg.lly, reg.urx, reg.ury);
  614. data := reg.data;
  615. FindLower(reg, lly, n);
  616. Decode(data[n], u, v, dir);
  617. REPEAT
  618. y := v;
  619. WHILE (v = y) & (u <= llx) DO
  620. INC(n);
  621. Decode(data[n], u, v, dir)
  622. END;
  623. IF (v = y) & ((u < urx) OR (dir = Exit)) THEN
  624. RETURN TRUE
  625. END;
  626. (* skip to next line *)
  627. WHILE v = y DO
  628. INC(n);
  629. Decode(data[n], u, v, dir)
  630. END
  631. UNTIL v >= ury;
  632. RETURN FALSE (* rectangle does not intersect any span *)
  633. END RectOverlaps;
  634. (** return whether region is completely within another region **)
  635. PROCEDURE RegionInside* (inner, outer: Region): BOOLEAN;
  636. VAR idata, odata: RegionData; in, on, is, os: LONGINT; iu, iv, idir, ou, ov, odir, iy, oy: INTEGER;
  637. BEGIN
  638. IF ~RectInRect(inner.llx, inner.lly, inner.urx, inner.ury, outer.llx, outer.lly, outer.urx, outer.ury) THEN
  639. RETURN FALSE (* inner rect not even within outer rect *)
  640. ELSIF IsRect(outer) THEN
  641. RETURN TRUE (* outer region fully covers inner region *)
  642. ELSIF IsRect(inner) THEN
  643. RETURN RectInside(inner.llx, inner.lly, inner.urx, inner.ury, outer)
  644. END;
  645. idata := inner.data; odata := outer.data;
  646. in := FirstSlice;
  647. FindLower(outer, inner.lly, on);
  648. Decode(idata[in], iu, iv, idir);
  649. Decode(odata[on], ou, ov, odir);
  650. is := in; os := on;
  651. REPEAT
  652. iy := iv; oy := ov;
  653. (* skip empty slices *)
  654. WHILE (iv = iy) & (iu = UBound) DO
  655. INC(in);
  656. Decode(idata[in], iu, iv, idir)
  657. END;
  658. (* compare slices *)
  659. WHILE (iv = iy) OR (ov = oy) DO
  660. IF (ov > oy) OR (iv = iy) & (idir = Exit) & (odir = Enter) THEN
  661. RETURN FALSE
  662. END;
  663. IF (iv > iy) OR (ou <= iu) THEN
  664. INC(on);
  665. Decode(odata[on], ou, ov, odir)
  666. ELSE
  667. INC(in);
  668. Decode(idata[in], iu, iv, idir)
  669. END
  670. END;
  671. (* reset to begin of slice if not on same line *)
  672. IF iv > ov THEN
  673. in := is; os := on;
  674. Decode(idata[in], iu, iv, idir)
  675. ELSIF ov > iv THEN
  676. on := os; is := in;
  677. Decode(odata[on], ou, ov, odir)
  678. ELSE
  679. is := in; os := on
  680. END
  681. UNTIL iv = inner.ury;
  682. RETURN TRUE (* all spans were covered by enclosing region *)
  683. END RegionInside;
  684. (** return whether two regions intersect each other **)
  685. PROCEDURE RegionOverlaps* (reg, arg: Region): BOOLEAN;
  686. VAR rdata, adata: RegionData; bot, top, ru, rv, rdir, au, av, adir, ry, ay: INTEGER; rn, an, rs, as: LONGINT;
  687. BEGIN
  688. IF ~RectsIntersect(reg.llx, reg.lly, reg.urx, reg.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
  689. RETURN FALSE (* rect does not even intersect arg's bounding box *)
  690. ELSIF IsRect(reg) THEN
  691. RETURN RectOverlaps(reg.llx, reg.lly, reg.urx, reg.ury, arg)
  692. ELSIF IsRect(arg) THEN
  693. RETURN RectOverlaps(arg.llx, arg.lly, arg.urx, arg.ury, reg)
  694. END;
  695. rdata := reg.data; adata := arg.data;
  696. bot := MAX(reg.lly, arg.lly);
  697. top := MIN(reg.ury, arg.ury);
  698. FindLower(reg, bot, rn);
  699. FindLower(arg, bot, an);
  700. Decode(rdata[rn], ru, rv, rdir);
  701. Decode(adata[an], au, av, adir);
  702. rs := rn; as := an;
  703. REPEAT
  704. ry := rv; ay := av;
  705. (* compare slices *)
  706. WHILE (rv = ry) OR (av = ay) DO
  707. IF (rv = ry) & (av = ay) & (rdir = Exit) & (adir = Exit) THEN
  708. RETURN TRUE
  709. END;
  710. IF (av > ay) OR (rv = ry) & (ru <= au) THEN
  711. INC(rn);
  712. Decode(rdata[rn], ru, rv, rdir)
  713. ELSE
  714. INC(an);
  715. Decode(adata[an], au, av, adir)
  716. END
  717. END;
  718. (* reset to begin of line if not on same line *)
  719. IF rv > av THEN
  720. rn := rs; as := an;
  721. Decode(rdata[rn], ru, rv, rdir)
  722. ELSIF av > rv THEN
  723. an := as; rs := rn;
  724. Decode(adata[an], au, av, adir)
  725. ELSE
  726. rs := rn; as := an
  727. END
  728. UNTIL (rv = top) OR (av = top);
  729. RETURN FALSE (* no pair of spans intersected *)
  730. END RegionOverlaps;
  731. (** enumerate region within rectangle **)
  732. PROCEDURE Enumerate* (reg: Region; llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
  733. BEGIN
  734. IF RectsIntersect(reg.llx, reg.lly, reg.urx, reg.ury, llx, lly, urx, ury) THEN
  735. ClipRect(llx, lly, urx, ury, reg.llx, reg.lly, reg.urx, reg.ury);
  736. IF ~RectEmpty(llx, lly, urx, ury) THEN
  737. IF IsRect(reg) THEN
  738. enum(llx, lly, urx, ury, edata)
  739. ELSE
  740. Enum(reg, llx, lly, urx, ury, enum, edata, Enter)
  741. END
  742. END
  743. END
  744. END Enumerate;
  745. (** enumerate parts of rectangle not within region **)
  746. PROCEDURE EnumerateInv* (reg: Region; llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
  747. BEGIN
  748. IF RectsIntersect(reg.llx, reg.lly, reg.urx, reg.ury, llx, lly, urx, ury) THEN
  749. IF IsRect(reg) & RectInRect(reg.llx, reg.lly, reg.urx, reg.ury, llx, lly, urx, ury) THEN
  750. IF lly < reg.lly THEN enum(llx, lly, urx, reg.lly, edata) END;
  751. IF llx < reg.llx THEN enum(llx, reg.lly, reg.llx, reg.ury, edata) END;
  752. IF urx > reg.urx THEN enum(reg.urx, reg.lly, urx, reg.ury, edata) END;
  753. IF ury > reg.ury THEN enum(llx, reg.ury, urx, ury, edata) END
  754. ELSE
  755. Enum(reg, llx, lly, urx, ury, enum, edata, Exit)
  756. END
  757. ELSE
  758. enum(llx, lly, urx, ury, edata)
  759. END
  760. END EnumerateInv;
  761. (**--- Region Construction ---**)
  762. (** make region empty **)
  763. PROCEDURE Clear* (reg: Region);
  764. BEGIN
  765. reg.llx := UBound; reg.lly := UBound;
  766. reg.urx := LBound; reg.ury := LBound;
  767. reg.valid := TRUE;
  768. reg.points := 0
  769. END Clear;
  770. (** set region mode **)
  771. PROCEDURE SetMode* (reg: Region; mode: INTEGER);
  772. BEGIN
  773. reg.mode := mode
  774. END SetMode;
  775. (** initialize region **)
  776. PROCEDURE Init* (reg: Region; mode: INTEGER);
  777. BEGIN
  778. reg.mode := mode;
  779. (* reg.data := NIL; *)
  780. Clear(reg)
  781. END Init;
  782. (** make region rectangular **)
  783. PROCEDURE SetToRect* (reg: Region; llx, lly, urx, ury: INTEGER);
  784. BEGIN
  785. IF RectEmpty(llx, lly, urx, ury) THEN
  786. Clear(reg)
  787. ELSE
  788. ClipRect(llx, lly, urx, ury, LBound, LBound, UBound, UBound);
  789. reg.llx := llx; reg.lly := lly; reg.urx := urx; reg.ury := ury;
  790. reg.valid := TRUE;
  791. reg.points := 0
  792. END
  793. END SetToRect;
  794. (** shift region **)
  795. PROCEDURE Shift* (reg: Region; dx, dy: INTEGER);
  796. VAR rdata: RegionData; rn: LONGINT; ru, rv, rdir: INTEGER;
  797. BEGIN
  798. IF (dx # 0) OR (dy # 0) THEN
  799. INC(reg.llx, dx); INC(reg.lly, dy); INC(reg.urx, dx); INC(reg.ury, dy);
  800. IF reg.points > 0 THEN
  801. rdata := reg.data; rn := FirstSlice;
  802. Decode(rdata[rn], ru, rv, rdir);
  803. WHILE rv < Top DO
  804. IF (ru <= LBound) OR (ru + dx <= LBound) THEN ru := LBound
  805. ELSIF (ru >= UBound) OR (ru + dx >= UBound) THEN ru := UBound
  806. ELSE INC(ru, dx)
  807. END;
  808. IF (dy < 0) & (rv < Bottom - dy) THEN rv := Bottom
  809. ELSIF (dy > 0) & (rv > Top - dy) THEN rv := Top
  810. ELSE INC(rv, dy)
  811. END;
  812. Encode(rdata[rn], ru, rv, rdir);
  813. INC(rn);
  814. Decode(rdata[rn], ru, rv, rdir)
  815. END
  816. END
  817. END
  818. END Shift;
  819. (** copy region **)
  820. PROCEDURE Copy* (from, to: Region);
  821. BEGIN
  822. to.mode := from.mode;
  823. CopyData(from, to)
  824. END Copy;
  825. (** add second region to first **)
  826. PROCEDURE Add* (reg, arg: Region);
  827. VAR rdata, adata: RegionData; points, aslice, an, rn, rslice: LONGINT; au, av, adir, ru, rv, rdir, top, ry, ay, y: INTEGER;
  828. BEGIN
  829. IF ~RectEmpty(arg.llx, arg.lly, arg.urx, arg.ury) THEN
  830. IF RectEmpty(reg.llx, reg.lly, reg.urx, reg.ury) THEN
  831. CopyData(arg, reg)
  832. ELSIF IsRect(arg) & RectInside(arg.llx, arg.lly, arg.urx, arg.ury, reg) THEN
  833. (* do nothing *)
  834. ELSIF IsRect(reg) & RectInside(reg.llx, reg.lly, reg.urx, reg.ury, arg) THEN
  835. CopyData(arg, reg)
  836. ELSE
  837. Validate(reg); Validate(arg);
  838. MakeData(reg); MakeData(arg);
  839. rdata := reg.data; adata := arg.data;
  840. points := reg.points;
  841. IF arg.lly < reg.lly THEN
  842. (* copy scanlines below reg *)
  843. FindUpper(arg, reg.lly, aslice);
  844. an := FirstSlice;
  845. WHILE an < aslice DO
  846. Decode(adata[an], au, av, adir);
  847. Append(reg, au, av, adir);
  848. INC(an)
  849. END;
  850. rn := FirstSlice;
  851. FindLower(arg, reg.lly, an)
  852. ELSE
  853. FindLower(reg, arg.lly, rn);
  854. an := FirstSlice
  855. END;
  856. Decode(rdata[rn], ru, rv, rdir);
  857. Decode(adata[an], au, av, adir);
  858. rslice := rn; aslice := an;
  859. top := MIN(reg.ury, arg.ury);
  860. WHILE (av < top) OR (rv < top) DO
  861. (* merge slices *)
  862. ry := rv; ay := av; y := MAX(ry, ay);
  863. REPEAT
  864. IF (av > ay) OR (rv = ry) & (ru <= au) THEN
  865. IF rv # y THEN (* do not duplicate points *)
  866. Append(reg, ru, y, rdir)
  867. END;
  868. INC(rn);
  869. Decode(rdata[rn], ru, rv, rdir)
  870. ELSE
  871. Append(reg, au, y, adir);
  872. INC(an);
  873. Decode(adata[an], au, av, adir)
  874. END
  875. UNTIL (rv > ry) & (av > ay);
  876. (* advance to next slice *)
  877. IF rv < av THEN
  878. an := aslice; rslice := rn;
  879. Decode(adata[an], au, av, adir)
  880. ELSIF av < rv THEN
  881. rn := rslice; aslice := an;
  882. Decode(rdata[rn], ru, rv, rdir)
  883. ELSE
  884. rslice := rn; aslice := an
  885. END
  886. END;
  887. (* copy slices above reg *)
  888. IF arg.ury > reg.ury THEN
  889. REPEAT
  890. Append(reg, au, av, adir);
  891. INC(an);
  892. Decode(adata[an], au, av, adir)
  893. UNTIL av = Top
  894. END;
  895. Merge(reg, points);
  896. IncludeRect(reg.llx, reg.lly, reg.urx, reg.ury, arg.llx, arg.lly, arg.urx, arg.ury)
  897. END
  898. END
  899. END Add;
  900. (** add rectangle to region **)
  901. PROCEDURE AddRect* (reg: Region; llx, lly, urx, ury: INTEGER);
  902. BEGIN{EXCLUSIVE}
  903. SetToRect(RectRegion, llx, lly, urx, ury);
  904. Add(reg, RectRegion)
  905. END AddRect;
  906. (** subtract second region from first **)
  907. PROCEDURE Subtract* (reg, arg: Region);
  908. VAR rdata, adata: RegionData; points, rn, an, rslice, aslice: LONGINT; ru, rv, rdir, au, av, adir, top, ry, ay, y: INTEGER;
  909. BEGIN
  910. IF ~RectEmpty(arg.llx, arg.lly, arg.urx, arg.ury) THEN
  911. IF RectEmpty(reg.llx, reg.lly, reg.urx, reg.ury) OR RegionInside(reg, arg) THEN
  912. Clear(reg)
  913. ELSIF RectsIntersect(reg.llx, reg.lly, reg.urx, reg.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
  914. Validate(reg); Validate(arg);
  915. MakeData(reg); MakeData(arg);
  916. rdata := reg.data; adata := arg.data;
  917. points := reg.points;
  918. IF reg.lly <= arg.lly THEN
  919. FindLower(reg, arg.lly, rn);
  920. an := FirstSlice
  921. ELSE
  922. rn := FirstSlice;
  923. FindLower(arg, reg.lly, an)
  924. END;
  925. Decode(rdata[rn], ru, rv, rdir);
  926. Decode(adata[an], au, av, adir);
  927. rslice := rn; aslice := an;
  928. top := MIN(reg.ury, arg.ury);
  929. WHILE (rv < top) OR (av < top) DO
  930. (* merge slices *)
  931. ry := rv; ay := av; y := MAX(ry, ay);
  932. REPEAT
  933. IF (av > ay) OR (rv = ry) & (ru <= au) THEN
  934. IF rv # y THEN (* do not duplicate points *)
  935. Append(reg, ru, y, rdir)
  936. END;
  937. INC(rn);
  938. Decode(rdata[rn], ru, rv, rdir)
  939. ELSE
  940. Append(reg, au, y, -adir);
  941. INC(an);
  942. Decode(adata[an], au, av, adir)
  943. END
  944. UNTIL (rv > ry) & (av > ay);
  945. (* advance to next slice *)
  946. IF rv < av THEN
  947. an := aslice; rslice := rn;
  948. Decode(adata[an], au, av, adir)
  949. ELSIF av < rv THEN
  950. rn := rslice; aslice := an;
  951. Decode(rdata[rn], ru, rv, rdir)
  952. ELSE
  953. rslice := rn; aslice := an
  954. END
  955. END;
  956. Merge(reg, points);
  957. CalcRect(reg)
  958. END
  959. END
  960. END Subtract;
  961. (** subtract rectangle from region **)
  962. PROCEDURE SubtractRect* (reg: Region; llx, lly, urx, ury: INTEGER);
  963. BEGIN{EXCLUSIVE}
  964. SetToRect(RectRegion, llx, lly, urx, ury);
  965. Subtract(reg, RectRegion)
  966. END SubtractRect;
  967. (** intersect first region with second region **)
  968. PROCEDURE Intersect* (reg, arg: Region);
  969. VAR rdata, adata: RegionData; points, rn, an, rslice, aslice: LONGINT; ru, rv, rdir, au, av, adir, ry, ay, y: INTEGER;
  970. BEGIN
  971. IF ~RectsIntersect(reg.llx, reg.lly, reg.urx, reg.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
  972. Clear(reg)
  973. ELSIF ~RectInside(reg.llx, reg.lly, reg.urx, reg.ury, arg) THEN
  974. Validate(reg); Validate(arg);
  975. MakeData(reg); MakeData(arg);
  976. rdata := reg.data; adata := arg.data;
  977. points := reg.points;
  978. (* cut off slices above arg *)
  979. IF reg.ury > arg.ury THEN
  980. FindUpper(reg, arg.ury, points);
  981. Encode(rdata[points], UBound, arg.ury, Enter); INC(points);
  982. Encode(rdata[points], UBound, arg.ury, Exit); INC(points);
  983. Encode(rdata[points], UBound, Top, Exit); INC(points);
  984. reg.points := points
  985. END;
  986. (* delete slices below arg *)
  987. IF reg.lly < arg.lly THEN
  988. FindLower(reg, arg.lly, rn);
  989. IF rn > FirstSlice THEN
  990. points := FirstSlice;
  991. WHILE rn < reg.points DO
  992. rdata[points] := rdata[rn];
  993. INC(points); INC(rn)
  994. END;
  995. reg.points := points
  996. END;
  997. rn := FirstSlice;
  998. Decode(rdata[rn], ru, rv, rdir);
  999. ry := rv;
  1000. REPEAT
  1001. Encode(rdata[rn], ru, arg.lly, rdir);
  1002. INC(rn);
  1003. Decode(rdata[rn], ru, rv, rdir)
  1004. UNTIL rv > ry;
  1005. rn := FirstSlice; an := FirstSlice
  1006. ELSE
  1007. rn := FirstSlice;
  1008. FindLower(arg, reg.lly, an)
  1009. END;
  1010. Decode(rdata[rn], ru, rv, rdir);
  1011. Decode(adata[an], au, av, adir);
  1012. rslice := rn; aslice := an;
  1013. WHILE rv < reg.ury DO
  1014. (* merge intersecting slices *)
  1015. ry := rv; ay := av; y := MAX(ry, ay);
  1016. Append(reg, LBound, y, Exit);
  1017. REPEAT
  1018. IF (av > ay) OR (rv = ry) & (ru <= au) THEN
  1019. IF rv # y THEN (* do not duplicate existing points *)
  1020. Append(reg, ru, y, rdir)
  1021. END;
  1022. INC(rn);
  1023. Decode(rdata[rn], ru, rv, rdir)
  1024. ELSE
  1025. Append(reg, au, y, adir);
  1026. INC(an);
  1027. Decode(adata[an], au, av, adir)
  1028. END
  1029. UNTIL (rv > ry) & (av > ay);
  1030. Append(reg, UBound, y, Enter);
  1031. (* advance to next slice *)
  1032. IF rv < av THEN
  1033. an := aslice; rslice := rn;
  1034. Decode(adata[an], au, av, adir)
  1035. ELSIF av < rv THEN
  1036. rn := rslice; aslice := an;
  1037. Decode(rdata[rn], ru, rv, rdir)
  1038. ELSE
  1039. rslice := rn; aslice := an
  1040. END
  1041. END;
  1042. Merge(reg, points);
  1043. CalcRect(reg)
  1044. END
  1045. END Intersect;
  1046. (** intersect region with rectangle **)
  1047. PROCEDURE IntersectRect* (reg: Region; llx, lly, urx, ury: INTEGER);
  1048. BEGIN{EXCLUSIVE}
  1049. SetToRect(RectRegion, llx, lly, urx, ury);
  1050. Intersect(reg, RectRegion)
  1051. END IntersectRect;
  1052. (** invert region **)
  1053. PROCEDURE Invert* (reg: Region);
  1054. VAR data: RegionData; points, n: LONGINT; u, v, dir, y: INTEGER;
  1055. BEGIN
  1056. IF RectEmpty(reg.llx, reg.lly, reg.urx, reg.ury) THEN
  1057. SetToRect(reg, LBound, LBound, UBound, UBound)
  1058. ELSE
  1059. Validate(reg);
  1060. MakeData(reg);
  1061. data := reg.data;
  1062. points := reg.points;
  1063. n := FirstSlice;
  1064. Decode(data[n], u, v, dir);
  1065. IF reg.lly > LBound THEN
  1066. Append(reg, LBound, LBound, Enter);
  1067. Append(reg, UBound, LBound, Exit)
  1068. END;
  1069. REPEAT
  1070. y := v;
  1071. Append(reg, LBound, y, Enter);
  1072. REPEAT
  1073. Encode(data[n], u, y, -dir);
  1074. INC(n);
  1075. Decode(data[n], u, y, dir)
  1076. UNTIL v > y;
  1077. Append(reg, UBound, y, Exit)
  1078. UNTIL v >= UBound;
  1079. IF y < UBound THEN
  1080. Append(reg, LBound, y, Enter);
  1081. Append(reg, UBound, y, Exit)
  1082. END;
  1083. Merge(reg, points);
  1084. CalcRect(reg)
  1085. END
  1086. END Invert;
  1087. (**
  1088. In addition to creating rectangular regions and using Boolean operations to combine several regions, a region
  1089. can also be built by tracing its outline with AddPoint. In order to allow the correct handling of self_intersecting
  1090. contours, a direction parameter is needed which indicates whether the curve is going up (dy = 1) or down
  1091. (dy = -1) at the given point.
  1092. When performing Boolean operations upon regions or when building regions from self_intersecting contours,
  1093. it is possible that some areas in the resulting region get "covered" more than once. Since most query operations
  1094. reduce regions to non_overlapping areas, a rule which decides whether a point is inside a region or not is needed.
  1095. Imagine a ray originating at such a point and counting every intersection of the ray with the boundary curve of the
  1096. region as +1 if the curve crosses the ray from right to left and as -1 otherwise.
  1097. - for mode Winding (the default), a point is inside the region if the resulting sum is non_zero
  1098. - for mode EvenOdd, a point is inside the region if the resulting sum is odd
  1099. Behaviour of all region queries and region operations is undefined for contours which are not closed.
  1100. **)
  1101. (** add a scanline intersection to a region **)
  1102. PROCEDURE AddPoint* (reg: Region; x, y, dy: INTEGER);
  1103. BEGIN
  1104. IF (dy # 0) & (y >= LBound) & (y <= UBound) THEN
  1105. IF x < LBound THEN x := LBound
  1106. ELSIF x > UBound THEN x := UBound
  1107. END;
  1108. MakeData(reg);
  1109. IncludePoint(reg.llx, reg.lly, reg.urx, reg.ury, x, y);
  1110. Append(reg, x, y + (-dy) DIV 2, dy); (* dy = -1 => y, dy = 1 => y - 1 *)
  1111. reg.valid := FALSE
  1112. END
  1113. END AddPoint;
  1114. BEGIN
  1115. NEW(RectRegion);
  1116. Init(RectRegion, Winding)
  1117. END GfxRegions.