|
@@ -59,7 +59,9 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
typically the case with AddPoint. This is why regions have a valid flag, indicating whether encoded points are sorted
|
|
|
or not. Invalidating operations only have to set valid to FALSE, other operations will eventually validate the region
|
|
|
again, at the same time eliminating multiple points and overlapping spans.
|
|
|
- *)
|
|
|
+ *)
|
|
|
+
|
|
|
+
|
|
|
Region* = OBJECT
|
|
|
VAR
|
|
|
llx*, lly*, urx*, ury*: INTEGER; (** bounding box **)
|
|
@@ -68,258 +70,8 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
data: RegionData; (* points defining region boundary *)
|
|
|
points: LONGINT; (* number of data points actually used *)
|
|
|
|
|
|
- (** initialize region **)
|
|
|
- PROCEDURE Init* (mode: INTEGER);
|
|
|
- BEGIN
|
|
|
- SELF.mode := mode;
|
|
|
- SELF.Clear()
|
|
|
- END Init;
|
|
|
|
|
|
- (**--- Region Queries ---**)
|
|
|
-
|
|
|
- (** return whether region is empty **)
|
|
|
- PROCEDURE Empty* (): BOOLEAN;
|
|
|
- BEGIN
|
|
|
- RETURN (SELF.llx >= SELF.urx) OR (SELF.lly >= SELF.ury)
|
|
|
- END Empty;
|
|
|
-
|
|
|
- (** return whether (non_empty) region is rectangular **)
|
|
|
- PROCEDURE IsRect* (): BOOLEAN;
|
|
|
- BEGIN
|
|
|
- SELF.Validate();
|
|
|
- RETURN SELF.points = 0
|
|
|
- END IsRect;
|
|
|
-
|
|
|
- (** return whether point is inside (non_empty) region **)
|
|
|
- PROCEDURE PointInside* (x, y: INTEGER): BOOLEAN;
|
|
|
- VAR data: RegionData; n: LONGINT; u, v, dir: INTEGER;
|
|
|
- BEGIN
|
|
|
- IF ~PointInRect(x, y, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN (* point not even within region rectangle *)
|
|
|
- RETURN FALSE
|
|
|
- ELSIF SELF.IsRect() THEN (* region is rectangular *)
|
|
|
- RETURN TRUE
|
|
|
- END;
|
|
|
-
|
|
|
- (* find span containing point *)
|
|
|
- data := SELF.data;
|
|
|
- SELF.FindLower(y, n);
|
|
|
- Decode(data[n], u, v, dir);
|
|
|
- WHILE u < x DO
|
|
|
- INC(n);
|
|
|
- Decode(data[n], u, v, dir)
|
|
|
- END;
|
|
|
- RETURN (u = x) & (dir = Enter) OR (u > x) & (dir = Exit)
|
|
|
- END PointInside;
|
|
|
-
|
|
|
- (** return whether (non_empty) rectangle is completely inside (non_empty) region **)
|
|
|
- PROCEDURE RectInside* (llx, lly, urx, ury: INTEGER): BOOLEAN;
|
|
|
- VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
|
|
|
- BEGIN
|
|
|
- IF ~RectInRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN (* not even within bounding rectangle *)
|
|
|
- RETURN FALSE
|
|
|
- ELSIF SELF.IsRect() THEN (* region is rectangular *)
|
|
|
- RETURN TRUE
|
|
|
- END;
|
|
|
-
|
|
|
- data := SELF.data;
|
|
|
- SELF.FindLower(lly, n);
|
|
|
- Decode(data[n], u, v, dir);
|
|
|
- REPEAT
|
|
|
- y := v;
|
|
|
- WHILE (v = y) & (u <= llx) DO
|
|
|
- INC(n);
|
|
|
- Decode(data[n], u, v, dir)
|
|
|
- END;
|
|
|
- IF (v > y) OR (u < urx) OR (dir = Enter) THEN (* rectangle not covered by span *)
|
|
|
- RETURN FALSE
|
|
|
- END;
|
|
|
-
|
|
|
- (* skip to next line *)
|
|
|
- WHILE v = y DO
|
|
|
- INC(n);
|
|
|
- Decode(data[n], u, v, dir)
|
|
|
- END
|
|
|
- UNTIL v >= ury;
|
|
|
-
|
|
|
- RETURN TRUE (* rectangle is fully covered by spans *)
|
|
|
- END RectInside;
|
|
|
-
|
|
|
- (** return whether (non_empty) rectangle overlaps (non_empty) region **)
|
|
|
- PROCEDURE RectOverlaps* (llx, lly, urx, ury: INTEGER): BOOLEAN;
|
|
|
- VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
|
|
|
- BEGIN
|
|
|
- IF ~RectsIntersect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
|
|
|
- RETURN FALSE (* rect does not even intersect region rectangle *)
|
|
|
- ELSIF SELF.IsRect() THEN (* region is rectangular *)
|
|
|
- RETURN TRUE
|
|
|
- END;
|
|
|
-
|
|
|
- ClipRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury);
|
|
|
- data := SELF.data;
|
|
|
- SELF.FindLower(lly, n);
|
|
|
- Decode(data[n], u, v, dir);
|
|
|
- REPEAT
|
|
|
- y := v;
|
|
|
- WHILE (v = y) & (u <= llx) DO
|
|
|
- INC(n);
|
|
|
- Decode(data[n], u, v, dir)
|
|
|
- END;
|
|
|
- IF (v = y) & ((u < urx) OR (dir = Exit)) THEN
|
|
|
- RETURN TRUE
|
|
|
- END;
|
|
|
-
|
|
|
- (* skip to next line *)
|
|
|
- WHILE v = y DO
|
|
|
- INC(n);
|
|
|
- Decode(data[n], u, v, dir)
|
|
|
- END
|
|
|
- UNTIL v >= ury;
|
|
|
-
|
|
|
- RETURN FALSE (* rectangle does not intersect any span *)
|
|
|
- END RectOverlaps;
|
|
|
-
|
|
|
- (** return whether region is completely within another region **)
|
|
|
- PROCEDURE RegionInside* (outer: Region): BOOLEAN;
|
|
|
- VAR idata, odata: RegionData; in, on, is, os: LONGINT; iu, iv, idir, ou, ov, odir, iy, oy: INTEGER;
|
|
|
- BEGIN
|
|
|
- IF ~RectInRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, outer.llx, outer.lly, outer.urx, outer.ury) THEN
|
|
|
- RETURN FALSE (* SELF rect not even within outer rect *)
|
|
|
- ELSIF outer.IsRect() THEN
|
|
|
- RETURN TRUE (* outer region fully covers SELF region *)
|
|
|
- ELSIF SELF.IsRect() THEN
|
|
|
- RETURN outer.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury)
|
|
|
- END;
|
|
|
-
|
|
|
- idata := SELF.data; odata := outer.data;
|
|
|
- in := FirstSlice;
|
|
|
- outer.FindLower(SELF.lly, on);
|
|
|
- Decode(idata[in], iu, iv, idir);
|
|
|
- Decode(odata[on], ou, ov, odir);
|
|
|
- is := in; os := on;
|
|
|
- REPEAT
|
|
|
- iy := iv; oy := ov;
|
|
|
-
|
|
|
- (* skip empty slices *)
|
|
|
- WHILE (iv = iy) & (iu = UBound) DO
|
|
|
- INC(in);
|
|
|
- Decode(idata[in], iu, iv, idir)
|
|
|
- END;
|
|
|
-
|
|
|
- (* compare slices *)
|
|
|
- WHILE (iv = iy) OR (ov = oy) DO
|
|
|
- IF (ov > oy) OR (iv = iy) & (idir = Exit) & (odir = Enter) THEN
|
|
|
- RETURN FALSE
|
|
|
- END;
|
|
|
- IF (iv > iy) OR (ou <= iu) THEN
|
|
|
- INC(on);
|
|
|
- Decode(odata[on], ou, ov, odir)
|
|
|
- ELSE
|
|
|
- INC(in);
|
|
|
- Decode(idata[in], iu, iv, idir)
|
|
|
- END
|
|
|
- END;
|
|
|
-
|
|
|
- (* reset to begin of slice if not on same line *)
|
|
|
- IF iv > ov THEN
|
|
|
- in := is; os := on;
|
|
|
- Decode(idata[in], iu, iv, idir)
|
|
|
- ELSIF ov > iv THEN
|
|
|
- on := os; is := in;
|
|
|
- Decode(odata[on], ou, ov, odir)
|
|
|
- ELSE
|
|
|
- is := in; os := on
|
|
|
- END
|
|
|
- UNTIL iv = SELF.ury;
|
|
|
-
|
|
|
- RETURN TRUE (* all spans were covered by enclosing region *)
|
|
|
- END RegionInside;
|
|
|
-
|
|
|
- (** return whether two regions intersect each other **)
|
|
|
- PROCEDURE RegionOverlaps* (arg: Region): BOOLEAN;
|
|
|
- VAR rdata, adata: RegionData; bot, top, ru, rv, rdir, au, av, adir, ry, ay: INTEGER; rn, an, rs, as: LONGINT;
|
|
|
- BEGIN
|
|
|
- IF ~RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
|
|
|
- RETURN FALSE (* rect does not even intersect arg's bounding box *)
|
|
|
- ELSIF SELF.IsRect() THEN
|
|
|
- RETURN arg.RectOverlaps(SELF.llx, SELF.lly, SELF.urx, SELF.ury)
|
|
|
- ELSIF arg.IsRect() THEN
|
|
|
- RETURN SELF.RectOverlaps(arg.llx, arg.lly, arg.urx, arg.ury)
|
|
|
- END;
|
|
|
-
|
|
|
- rdata := SELF.data; adata := arg.data;
|
|
|
- bot := MAX(SELF.lly, arg.lly);
|
|
|
- top := MIN(SELF.ury, arg.ury);
|
|
|
- SELF.FindLower(bot, rn);
|
|
|
- arg.FindLower(bot, an);
|
|
|
- Decode(rdata[rn], ru, rv, rdir);
|
|
|
- Decode(adata[an], au, av, adir);
|
|
|
- rs := rn; as := an;
|
|
|
- REPEAT
|
|
|
- ry := rv; ay := av;
|
|
|
-
|
|
|
- (* compare slices *)
|
|
|
- WHILE (rv = ry) OR (av = ay) DO
|
|
|
- IF (rv = ry) & (av = ay) & (rdir = Exit) & (adir = Exit) THEN
|
|
|
- RETURN TRUE
|
|
|
- END;
|
|
|
- IF (av > ay) OR (rv = ry) & (ru <= au) THEN
|
|
|
- INC(rn);
|
|
|
- Decode(rdata[rn], ru, rv, rdir)
|
|
|
- ELSE
|
|
|
- INC(an);
|
|
|
- Decode(adata[an], au, av, adir)
|
|
|
- END
|
|
|
- END;
|
|
|
-
|
|
|
- (* reset to begin of line if not on same line *)
|
|
|
- IF rv > av THEN
|
|
|
- rn := rs; as := an;
|
|
|
- Decode(rdata[rn], ru, rv, rdir)
|
|
|
- ELSIF av > rv THEN
|
|
|
- an := as; rs := rn;
|
|
|
- Decode(adata[an], au, av, adir)
|
|
|
- ELSE
|
|
|
- rs := rn; as := an
|
|
|
- END
|
|
|
- UNTIL (rv = top) OR (av = top);
|
|
|
-
|
|
|
- RETURN FALSE (* no pair of spans intersected *)
|
|
|
- END RegionOverlaps;
|
|
|
-
|
|
|
- (** enumerate region within rectangle **)
|
|
|
- PROCEDURE Enumerate* (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
|
|
|
- BEGIN
|
|
|
- IF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
|
|
|
- ClipRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury);
|
|
|
- IF ~RectEmpty(llx, lly, urx, ury) THEN
|
|
|
- IF SELF.IsRect() THEN
|
|
|
- enum(llx, lly, urx, ury, edata)
|
|
|
- ELSE
|
|
|
- SELF.Enum(llx, lly, urx, ury, enum, edata, Enter)
|
|
|
- END
|
|
|
- END
|
|
|
- END
|
|
|
- END Enumerate;
|
|
|
-
|
|
|
- (** enumerate parts of rectangle not within region **)
|
|
|
- PROCEDURE EnumerateInv* (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
|
|
|
- BEGIN
|
|
|
- IF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
|
|
|
- IF SELF.IsRect() & RectInRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
|
|
|
- IF lly < SELF.lly THEN enum(llx, lly, urx, SELF.lly, edata) END;
|
|
|
- IF llx < SELF.llx THEN enum(llx, SELF.lly, SELF.llx, SELF.ury, edata) END;
|
|
|
- IF urx > SELF.urx THEN enum(SELF.urx, SELF.lly, urx, SELF.ury, edata) END;
|
|
|
- IF ury > SELF.ury THEN enum(llx, SELF.ury, urx, ury, edata) END
|
|
|
- ELSE
|
|
|
- SELF.Enum(llx, lly, urx, ury, enum, edata, Exit)
|
|
|
- END
|
|
|
- ELSE
|
|
|
- enum(llx, lly, urx, ury, edata)
|
|
|
- END
|
|
|
- END EnumerateInv;
|
|
|
-
|
|
|
-
|
|
|
- (**--- Region Construction ---**)
|
|
|
+ (**--- Region Construction ---**)
|
|
|
|
|
|
(** make region empty **)
|
|
|
PROCEDURE Clear* ();
|
|
@@ -336,6 +88,14 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
SELF.mode := mode
|
|
|
END SetMode;
|
|
|
|
|
|
+ (** initialize region **)
|
|
|
+ PROCEDURE Init* (mode: INTEGER);
|
|
|
+ BEGIN
|
|
|
+ SELF.mode := mode;
|
|
|
+ (* SELF.data := NIL; *)
|
|
|
+ SELF.Clear()
|
|
|
+ END Init;
|
|
|
+
|
|
|
(** make region rectangular **)
|
|
|
PROCEDURE SetToRect* (llx, lly, urx, ury: INTEGER);
|
|
|
BEGIN
|
|
@@ -466,12 +226,10 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
|
|
|
(** add rectangle to region **)
|
|
|
PROCEDURE AddRect* (llx, lly, urx, ury: INTEGER);
|
|
|
- VAR rectRegion: Region; (*TODO use rect *)
|
|
|
- BEGIN
|
|
|
- rectRegion := NEW Region( );
|
|
|
- rectRegion.Init( Winding );
|
|
|
- rectRegion.SetToRect(llx, lly, urx, ury);
|
|
|
- SELF.Add(rectRegion)
|
|
|
+ VAR reg: Region;
|
|
|
+ BEGIN{EXCLUSIVE}
|
|
|
+ reg.SetToRect(llx, lly, urx, ury);
|
|
|
+ SELF.Add(reg)
|
|
|
END AddRect;
|
|
|
|
|
|
(** subtract second region from first **)
|
|
@@ -535,110 +293,107 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
|
|
|
(** subtract rectangle from region **)
|
|
|
PROCEDURE SubtractRect* (llx, lly, urx, ury: INTEGER);
|
|
|
- VAR rectRegion: Region;
|
|
|
- BEGIN
|
|
|
- rectRegion := NEW Region( );
|
|
|
- rectRegion.Init( Winding );
|
|
|
- rectRegion.SetToRect(llx, lly, urx, ury);
|
|
|
- SELF.Subtract(rectRegion)
|
|
|
+ VAR reg: Region;
|
|
|
+ BEGIN{EXCLUSIVE}
|
|
|
+ reg.SetToRect(llx, lly, urx, ury);
|
|
|
+ SELF.Subtract(reg)
|
|
|
END SubtractRect;
|
|
|
|
|
|
- (** intersect first region with second region **)
|
|
|
- PROCEDURE Intersect* (arg: Region);
|
|
|
- VAR rdata, adata: RegionData; points, rn, an, rslice, aslice: LONGINT; ru, rv, rdir, au, av, adir, ry, ay, y: INTEGER;
|
|
|
- BEGIN
|
|
|
- IF ~RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
|
|
|
- SELF.Clear()
|
|
|
-
|
|
|
- ELSIF ~arg.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
|
|
|
- SELF.Validate(); arg.Validate();
|
|
|
- SELF.MakeData(); arg.MakeData();
|
|
|
- rdata := SELF.data; adata := arg.data;
|
|
|
- points := SELF.points;
|
|
|
+ (** intersect first region with second region **)
|
|
|
+ PROCEDURE Intersect* (arg: Region);
|
|
|
+ VAR rdata, adata: RegionData; points, rn, an, rslice, aslice: LONGINT; ru, rv, rdir, au, av, adir, ry, ay, y: INTEGER;
|
|
|
+ BEGIN
|
|
|
+ IF ~RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
|
|
|
+ SELF.Clear()
|
|
|
|
|
|
- (* cut off slices above arg *)
|
|
|
- IF SELF.ury > arg.ury THEN
|
|
|
- SELF.FindUpper(arg.ury, points);
|
|
|
- Encode(rdata[points], UBound, arg.ury, Enter); INC(points);
|
|
|
- Encode(rdata[points], UBound, arg.ury, Exit); INC(points);
|
|
|
- Encode(rdata[points], UBound, Top, Exit); INC(points);
|
|
|
- SELF.points := points
|
|
|
- END;
|
|
|
+ ELSIF ~arg.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
|
|
|
+ SELF.Validate(); arg.Validate();
|
|
|
+ SELF.MakeData(); arg.MakeData();
|
|
|
+ rdata := SELF.data; adata := arg.data;
|
|
|
+ points := SELF.points;
|
|
|
+
|
|
|
+ (* cut off slices above arg *)
|
|
|
+ IF SELF.ury > arg.ury THEN
|
|
|
+ SELF.FindUpper(arg.ury, points);
|
|
|
+ Encode(rdata[points], UBound, arg.ury, Enter); INC(points);
|
|
|
+ Encode(rdata[points], UBound, arg.ury, Exit); INC(points);
|
|
|
+ Encode(rdata[points], UBound, Top, Exit); INC(points);
|
|
|
+ SELF.points := points
|
|
|
+ END;
|
|
|
|
|
|
- (* delete slices below arg *)
|
|
|
- IF SELF.lly < arg.lly THEN
|
|
|
- SELF.FindLower(arg.lly, rn);
|
|
|
- IF rn > FirstSlice THEN
|
|
|
- points := FirstSlice;
|
|
|
- WHILE rn < SELF.points DO
|
|
|
- rdata[points] := rdata[rn];
|
|
|
- INC(points); INC(rn)
|
|
|
- END;
|
|
|
- SELF.points := points
|
|
|
+ (* delete slices below arg *)
|
|
|
+ IF SELF.lly < arg.lly THEN
|
|
|
+ SELF.FindLower(arg.lly, rn);
|
|
|
+ IF rn > FirstSlice THEN
|
|
|
+ points := FirstSlice;
|
|
|
+ WHILE rn < SELF.points DO
|
|
|
+ rdata[points] := rdata[rn];
|
|
|
+ INC(points); INC(rn)
|
|
|
END;
|
|
|
-
|
|
|
- rn := FirstSlice;
|
|
|
- Decode(rdata[rn], ru, rv, rdir);
|
|
|
- ry := rv;
|
|
|
- REPEAT
|
|
|
- Encode(rdata[rn], ru, arg.lly, rdir);
|
|
|
- INC(rn);
|
|
|
- Decode(rdata[rn], ru, rv, rdir)
|
|
|
- UNTIL rv > ry;
|
|
|
-
|
|
|
- rn := FirstSlice; an := FirstSlice
|
|
|
- ELSE
|
|
|
- rn := FirstSlice;
|
|
|
- arg.FindLower(SELF.lly, an)
|
|
|
+ SELF.points := points
|
|
|
END;
|
|
|
|
|
|
+ rn := FirstSlice;
|
|
|
Decode(rdata[rn], ru, rv, rdir);
|
|
|
- Decode(adata[an], au, av, adir);
|
|
|
- rslice := rn; aslice := an;
|
|
|
+ ry := rv;
|
|
|
+ REPEAT
|
|
|
+ Encode(rdata[rn], ru, arg.lly, rdir);
|
|
|
+ INC(rn);
|
|
|
+ Decode(rdata[rn], ru, rv, rdir)
|
|
|
+ UNTIL rv > ry;
|
|
|
|
|
|
- WHILE rv < SELF.ury DO
|
|
|
- (* merge intersecting slices *)
|
|
|
- ry := rv; ay := av; y := MAX(ry, ay);
|
|
|
- SELF.Append(LBound, y, Exit);
|
|
|
- REPEAT
|
|
|
- IF (av > ay) OR (rv = ry) & (ru <= au) THEN
|
|
|
- IF rv # y THEN (* do not duplicate existing points *)
|
|
|
- SELF.Append(ru, y, rdir)
|
|
|
- END;
|
|
|
- INC(rn);
|
|
|
- Decode(rdata[rn], ru, rv, rdir)
|
|
|
- ELSE
|
|
|
- SELF.Append(au, y, adir);
|
|
|
- INC(an);
|
|
|
- Decode(adata[an], au, av, adir)
|
|
|
- END
|
|
|
- UNTIL (rv > ry) & (av > ay);
|
|
|
- SELF.Append(UBound, y, Enter);
|
|
|
+ rn := FirstSlice; an := FirstSlice
|
|
|
+ ELSE
|
|
|
+ rn := FirstSlice;
|
|
|
+ arg.FindLower(SELF.lly, an)
|
|
|
+ END;
|
|
|
|
|
|
- (* advance to next slice *)
|
|
|
- IF rv < av THEN
|
|
|
- an := aslice; rslice := rn;
|
|
|
- Decode(adata[an], au, av, adir)
|
|
|
- ELSIF av < rv THEN
|
|
|
- rn := rslice; aslice := an;
|
|
|
+ Decode(rdata[rn], ru, rv, rdir);
|
|
|
+ Decode(adata[an], au, av, adir);
|
|
|
+ rslice := rn; aslice := an;
|
|
|
+
|
|
|
+ WHILE rv < SELF.ury DO
|
|
|
+ (* merge intersecting slices *)
|
|
|
+ ry := rv; ay := av; y := MAX(ry, ay);
|
|
|
+ SELF.Append(LBound, y, Exit);
|
|
|
+ REPEAT
|
|
|
+ IF (av > ay) OR (rv = ry) & (ru <= au) THEN
|
|
|
+ IF rv # y THEN (* do not duplicate existing points *)
|
|
|
+ SELF.Append(ru, y, rdir)
|
|
|
+ END;
|
|
|
+ INC(rn);
|
|
|
Decode(rdata[rn], ru, rv, rdir)
|
|
|
ELSE
|
|
|
- rslice := rn; aslice := an
|
|
|
+ SELF.Append(au, y, adir);
|
|
|
+ INC(an);
|
|
|
+ Decode(adata[an], au, av, adir)
|
|
|
END
|
|
|
- END;
|
|
|
+ UNTIL (rv > ry) & (av > ay);
|
|
|
+ SELF.Append(UBound, y, Enter);
|
|
|
+
|
|
|
+ (* advance to next slice *)
|
|
|
+ IF rv < av THEN
|
|
|
+ an := aslice; rslice := rn;
|
|
|
+ Decode(adata[an], au, av, adir)
|
|
|
+ ELSIF av < rv THEN
|
|
|
+ rn := rslice; aslice := an;
|
|
|
+ Decode(rdata[rn], ru, rv, rdir)
|
|
|
+ ELSE
|
|
|
+ rslice := rn; aslice := an
|
|
|
+ END
|
|
|
+ END;
|
|
|
|
|
|
- SELF.Merge(points);
|
|
|
- SELF.CalcRect()
|
|
|
- END
|
|
|
- END Intersect;
|
|
|
+ SELF.Merge(points);
|
|
|
+ SELF.CalcRect()
|
|
|
+ END
|
|
|
+ END Intersect;
|
|
|
|
|
|
(** intersect region with rectangle **)
|
|
|
PROCEDURE IntersectRect* (llx, lly, urx, ury: INTEGER);
|
|
|
- VAR rectRegion: Region; (**TODO use rect *)
|
|
|
- BEGIN
|
|
|
- rectRegion := NEW Region( ); rectRegion.Init( Winding );
|
|
|
- rectRegion.SetToRect(llx, lly, urx, ury);
|
|
|
- SELF.Intersect(rectRegion)
|
|
|
+ VAR reg: Region;
|
|
|
+ BEGIN{EXCLUSIVE}
|
|
|
+ reg.SetToRect(llx, lly, urx, ury);
|
|
|
+ SELF.Intersect(reg)
|
|
|
END IntersectRect;
|
|
|
|
|
|
(** invert region **)
|
|
@@ -710,11 +465,253 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
SELF.Append(x, y + (-dy) DIV 2, dy); (* dy = -1 => y, dy = 1 => y - 1 *)
|
|
|
SELF.valid := FALSE
|
|
|
END
|
|
|
- END AddPoint;
|
|
|
+ END AddPoint;
|
|
|
|
|
|
- (*--- Auxiliary Routines For Managing Regions ---*)
|
|
|
+ (**--- Region Queries ---**)
|
|
|
+
|
|
|
+ (** return whether region is empty **)
|
|
|
+ PROCEDURE Empty* (): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ RETURN (SELF.llx >= SELF.urx) OR (SELF.lly >= SELF.ury)
|
|
|
+ END Empty;
|
|
|
+
|
|
|
+ (** return whether (non_empty) region is rectangular **)
|
|
|
+ PROCEDURE IsRect* (): BOOLEAN;
|
|
|
+ BEGIN
|
|
|
+ SELF.Validate();
|
|
|
+ RETURN SELF.points = 0
|
|
|
+ END IsRect;
|
|
|
+
|
|
|
+ (** return whether point is inside (non_empty) region **)
|
|
|
+ PROCEDURE PointInside* (x, y: INTEGER): BOOLEAN;
|
|
|
+ VAR data: RegionData; n: LONGINT; u, v, dir: INTEGER;
|
|
|
+ BEGIN
|
|
|
+ IF ~PointInRect(x, y, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN (* point not even within region rectangle *)
|
|
|
+ RETURN FALSE
|
|
|
+ ELSIF SELF.IsRect() THEN (* region is rectangular *)
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* find span containing point *)
|
|
|
+ data := SELF.data;
|
|
|
+ SELF.FindLower(y, n);
|
|
|
+ Decode(data[n], u, v, dir);
|
|
|
+ WHILE u < x DO
|
|
|
+ INC(n);
|
|
|
+ Decode(data[n], u, v, dir)
|
|
|
+ END;
|
|
|
+ RETURN (u = x) & (dir = Enter) OR (u > x) & (dir = Exit)
|
|
|
+ END PointInside;
|
|
|
+
|
|
|
+ (** return whether (non_empty) rectangle is completely inside (non_empty) region **)
|
|
|
+ PROCEDURE RectInside* (llx, lly, urx, ury: INTEGER): BOOLEAN;
|
|
|
+ VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
|
|
|
+ BEGIN
|
|
|
+ IF ~RectInRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN (* not even within bounding rectangle *)
|
|
|
+ RETURN FALSE
|
|
|
+ ELSIF SELF.IsRect() THEN (* region is rectangular *)
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+
|
|
|
+ data := SELF.data;
|
|
|
+ SELF.FindLower(lly, n);
|
|
|
+ Decode(data[n], u, v, dir);
|
|
|
+ REPEAT
|
|
|
+ y := v;
|
|
|
+ WHILE (v = y) & (u <= llx) DO
|
|
|
+ INC(n);
|
|
|
+ Decode(data[n], u, v, dir)
|
|
|
+ END;
|
|
|
+ IF (v > y) OR (u < urx) OR (dir = Enter) THEN (* rectangle not covered by span *)
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* skip to next line *)
|
|
|
+ WHILE v = y DO
|
|
|
+ INC(n);
|
|
|
+ Decode(data[n], u, v, dir)
|
|
|
+ END
|
|
|
+ UNTIL v >= ury;
|
|
|
+
|
|
|
+ RETURN TRUE (* rectangle is fully covered by spans *)
|
|
|
+ END RectInside;
|
|
|
+
|
|
|
+ (** return whether (non_empty) rectangle overlaps (non_empty) region **)
|
|
|
+ PROCEDURE RectOverlaps* (llx, lly, urx, ury: INTEGER): BOOLEAN;
|
|
|
+ VAR data: RegionData; n: LONGINT; u, v, dir, y: INTEGER;
|
|
|
+ BEGIN
|
|
|
+ IF ~RectsIntersect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury) THEN
|
|
|
+ RETURN FALSE (* rect does not even intersect region rectangle *)
|
|
|
+ ELSIF SELF.IsRect() THEN (* region is rectangular *)
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+
|
|
|
+ ClipRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury);
|
|
|
+ data := SELF.data;
|
|
|
+ SELF.FindLower(lly, n);
|
|
|
+ Decode(data[n], u, v, dir);
|
|
|
+ REPEAT
|
|
|
+ y := v;
|
|
|
+ WHILE (v = y) & (u <= llx) DO
|
|
|
+ INC(n);
|
|
|
+ Decode(data[n], u, v, dir)
|
|
|
+ END;
|
|
|
+ IF (v = y) & ((u < urx) OR (dir = Exit)) THEN
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* skip to next line *)
|
|
|
+ WHILE v = y DO
|
|
|
+ INC(n);
|
|
|
+ Decode(data[n], u, v, dir)
|
|
|
+ END
|
|
|
+ UNTIL v >= ury;
|
|
|
+
|
|
|
+ RETURN FALSE (* rectangle does not intersect any span *)
|
|
|
+ END RectOverlaps;
|
|
|
+
|
|
|
+ (** return whether region is completely within another region **)
|
|
|
+ PROCEDURE RegionInside* (outer: Region): BOOLEAN;
|
|
|
+ VAR idata, odata: RegionData; in, on, is, os: LONGINT; iu, iv, idir, ou, ov, odir, iy, oy: INTEGER;
|
|
|
+ BEGIN
|
|
|
+ IF ~RectInRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, outer.llx, outer.lly, outer.urx, outer.ury) THEN
|
|
|
+ RETURN FALSE (* SELF rect not even within outer rect *)
|
|
|
+ ELSIF outer.IsRect() THEN
|
|
|
+ RETURN TRUE (* outer region fully covers SELF region *)
|
|
|
+ ELSIF SELF.IsRect() THEN
|
|
|
+ RETURN outer.RectInside(SELF.llx, SELF.lly, SELF.urx, SELF.ury)
|
|
|
+ END;
|
|
|
+
|
|
|
+ idata := SELF.data; odata := outer.data;
|
|
|
+ in := FirstSlice;
|
|
|
+ outer.FindLower(SELF.lly, on);
|
|
|
+ Decode(idata[in], iu, iv, idir);
|
|
|
+ Decode(odata[on], ou, ov, odir);
|
|
|
+ is := in; os := on;
|
|
|
+ REPEAT
|
|
|
+ iy := iv; oy := ov;
|
|
|
+
|
|
|
+ (* skip empty slices *)
|
|
|
+ WHILE (iv = iy) & (iu = UBound) DO
|
|
|
+ INC(in);
|
|
|
+ Decode(idata[in], iu, iv, idir)
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* compare slices *)
|
|
|
+ WHILE (iv = iy) OR (ov = oy) DO
|
|
|
+ IF (ov > oy) OR (iv = iy) & (idir = Exit) & (odir = Enter) THEN
|
|
|
+ RETURN FALSE
|
|
|
+ END;
|
|
|
+ IF (iv > iy) OR (ou <= iu) THEN
|
|
|
+ INC(on);
|
|
|
+ Decode(odata[on], ou, ov, odir)
|
|
|
+ ELSE
|
|
|
+ INC(in);
|
|
|
+ Decode(idata[in], iu, iv, idir)
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* reset to begin of slice if not on same line *)
|
|
|
+ IF iv > ov THEN
|
|
|
+ in := is; os := on;
|
|
|
+ Decode(idata[in], iu, iv, idir)
|
|
|
+ ELSIF ov > iv THEN
|
|
|
+ on := os; is := in;
|
|
|
+ Decode(odata[on], ou, ov, odir)
|
|
|
+ ELSE
|
|
|
+ is := in; os := on
|
|
|
+ END
|
|
|
+ UNTIL iv = SELF.ury;
|
|
|
+
|
|
|
+ RETURN TRUE (* all spans were covered by enclosing region *)
|
|
|
+ END RegionInside;
|
|
|
+
|
|
|
+ (** return whether two regions intersect each other **)
|
|
|
+ PROCEDURE RegionOverlaps* (arg: Region): BOOLEAN;
|
|
|
+ VAR rdata, adata: RegionData; bot, top, ru, rv, rdir, au, av, adir, ry, ay: INTEGER; rn, an, rs, as: LONGINT;
|
|
|
+ BEGIN
|
|
|
+ IF ~RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, arg.llx, arg.lly, arg.urx, arg.ury) THEN
|
|
|
+ RETURN FALSE (* rect does not even intersect arg's bounding box *)
|
|
|
+ ELSIF SELF.IsRect() THEN
|
|
|
+ RETURN arg.RectOverlaps(SELF.llx, SELF.lly, SELF.urx, SELF.ury)
|
|
|
+ ELSIF arg.IsRect() THEN
|
|
|
+ RETURN SELF.RectOverlaps(arg.llx, arg.lly, arg.urx, arg.ury)
|
|
|
+ END;
|
|
|
+
|
|
|
+ rdata := SELF.data; adata := arg.data;
|
|
|
+ bot := MAX(SELF.lly, arg.lly);
|
|
|
+ top := MIN(SELF.ury, arg.ury);
|
|
|
+ SELF.FindLower(bot, rn);
|
|
|
+ arg.FindLower(bot, an);
|
|
|
+ Decode(rdata[rn], ru, rv, rdir);
|
|
|
+ Decode(adata[an], au, av, adir);
|
|
|
+ rs := rn; as := an;
|
|
|
+ REPEAT
|
|
|
+ ry := rv; ay := av;
|
|
|
+
|
|
|
+ (* compare slices *)
|
|
|
+ WHILE (rv = ry) OR (av = ay) DO
|
|
|
+ IF (rv = ry) & (av = ay) & (rdir = Exit) & (adir = Exit) THEN
|
|
|
+ RETURN TRUE
|
|
|
+ END;
|
|
|
+ IF (av > ay) OR (rv = ry) & (ru <= au) THEN
|
|
|
+ INC(rn);
|
|
|
+ Decode(rdata[rn], ru, rv, rdir)
|
|
|
+ ELSE
|
|
|
+ INC(an);
|
|
|
+ Decode(adata[an], au, av, adir)
|
|
|
+ END
|
|
|
+ END;
|
|
|
+
|
|
|
+ (* reset to begin of line if not on same line *)
|
|
|
+ IF rv > av THEN
|
|
|
+ rn := rs; as := an;
|
|
|
+ Decode(rdata[rn], ru, rv, rdir)
|
|
|
+ ELSIF av > rv THEN
|
|
|
+ an := as; rs := rn;
|
|
|
+ Decode(adata[an], au, av, adir)
|
|
|
+ ELSE
|
|
|
+ rs := rn; as := an
|
|
|
+ END
|
|
|
+ UNTIL (rv = top) OR (av = top);
|
|
|
+
|
|
|
+ RETURN FALSE (* no pair of spans intersected *)
|
|
|
+ END RegionOverlaps;
|
|
|
+
|
|
|
+ (** enumerate region within rectangle **)
|
|
|
+ PROCEDURE Enumerate* (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
|
|
|
+ BEGIN
|
|
|
+ IF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
|
|
|
+ ClipRect(llx, lly, urx, ury, SELF.llx, SELF.lly, SELF.urx, SELF.ury);
|
|
|
+ IF ~RectEmpty(llx, lly, urx, ury) THEN
|
|
|
+ IF SELF.IsRect() THEN
|
|
|
+ enum(llx, lly, urx, ury, edata)
|
|
|
+ ELSE
|
|
|
+ SELF.Enum(llx, lly, urx, ury, enum, edata, Enter)
|
|
|
+ END
|
|
|
+ END
|
|
|
+ END
|
|
|
+ END Enumerate;
|
|
|
+
|
|
|
+ (** enumerate parts of rectangle not within region **)
|
|
|
+ PROCEDURE EnumerateInv* (llx, lly, urx, ury: INTEGER; enum: Enumerator; VAR edata: EnumData);
|
|
|
+ BEGIN
|
|
|
+ IF RectsIntersect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
|
|
|
+ IF SELF.IsRect() & RectInRect(SELF.llx, SELF.lly, SELF.urx, SELF.ury, llx, lly, urx, ury) THEN
|
|
|
+ IF lly < SELF.lly THEN enum(llx, lly, urx, SELF.lly, edata) END;
|
|
|
+ IF llx < SELF.llx THEN enum(llx, SELF.lly, SELF.llx, SELF.ury, edata) END;
|
|
|
+ IF urx > SELF.urx THEN enum(SELF.urx, SELF.lly, urx, SELF.ury, edata) END;
|
|
|
+ IF ury > SELF.ury THEN enum(llx, SELF.ury, urx, ury, edata) END
|
|
|
+ ELSE
|
|
|
+ SELF.Enum(llx, lly, urx, ury, enum, edata, Exit)
|
|
|
+ END
|
|
|
+ ELSE
|
|
|
+ enum(llx, lly, urx, ury, edata)
|
|
|
+ END
|
|
|
+ END EnumerateInv;
|
|
|
+
|
|
|
+ (*--- Auxiliary Routines For Managing Regions ---*)
|
|
|
|
|
|
-
|
|
|
(* append point to region data *)
|
|
|
PROCEDURE Append (u, v, dir: INTEGER);
|
|
|
VAR size: LONGINT; data: RegionData;
|
|
@@ -822,7 +819,9 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
(* merge two runs of data points *)
|
|
|
PROCEDURE Merge (split: LONGINT);
|
|
|
VAR data: RegionData; n, N, m, M, p, tmp: LONGINT; nu, nv, ndir, mu, mv, mdir, sum, u, v, inc, nsum: INTEGER;
|
|
|
- BEGIN
|
|
|
+ Data: RegionData; (* temporary region data for merging *)
|
|
|
+ DataSize: LONGINT; (* number of points allocated for Data *)
|
|
|
+ BEGIN{EXCLUSIVE}
|
|
|
data := SELF.data;
|
|
|
n := 0; N := split;
|
|
|
Decode(data[n], nu, nv, ndir);
|
|
@@ -1085,11 +1084,6 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
EnumData* = RECORD END;
|
|
|
Enumerator* = PROCEDURE (llx, lly, urx, ury: INTEGER; VAR edata: EnumData);
|
|
|
|
|
|
-
|
|
|
- VAR
|
|
|
- Data: RegionData; (* temporary region data for merging *)
|
|
|
- DataSize: LONGINT; (* number of points allocated for Data *)
|
|
|
-
|
|
|
(**--- Rectangles ---**)
|
|
|
|
|
|
(** make rectangle large enough to include a point **)
|
|
@@ -1146,35 +1140,6 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
|
|
|
(*--- Auxiliary Routines For Managing Regions ---*)
|
|
|
|
|
|
- (*
|
|
|
- Implementation notes:
|
|
|
-
|
|
|
- Regions are managed by slicing them horizontally. For each scanline y, a set of spans on the scanline defines which
|
|
|
- parts of the scanline are part of the region. The spans are defined through the x_coordinates of their end points.
|
|
|
- Every point on a scanline has a direction attribute, which specifies whether the point starts a span (Enter) or ends
|
|
|
- one (Exit), allowing spans to nest or overlap.
|
|
|
-
|
|
|
- The x_ and y_coordinates of a point along with its direction are encoded into a LONGINT. The chosen encoding
|
|
|
- weights the y_coordinate most, followed by the x_coordinate and the direction of an intersection. Visiting all
|
|
|
- encoded points in ascending order therefore traverses all spans of the region from the bottom left corner to the
|
|
|
- top right corner.
|
|
|
-
|
|
|
- In order to save space, identical slices adjacent to each other are stored only once. The bottommost scanline of
|
|
|
- an identical sequence of scanlines serves as a representant for the whole sequence; all others are eliminated.
|
|
|
- This means that if no points exist for a certain y_coordinate, the spans of the corresponding scanline are identical
|
|
|
- to those of the one below it. As a consequence, scanlines that are completely outside the region need an empty
|
|
|
- filler span to distinguish them from eliminated scanlines. A filler span consists of two points located at UBound,
|
|
|
- one entering the region and the other leaving it.
|
|
|
-
|
|
|
- Most operations modifying regions append new points in ascending order to the sequence of existing points and
|
|
|
- then merge the two sequences again. If points cannot be appended in order, the whole set of points has to be
|
|
|
- sorted before any other operation can be executed. Doing this immediately after the sequence of points has been
|
|
|
- invalidated can decrease performance significantly if a lot of invalidating operations are issued in sequence, as is
|
|
|
- typically the case with AddPoint. This is why regions have a valid flag, indicating whether encoded points are sorted
|
|
|
- or not. Invalidating operations only have to set valid to FALSE, other operations will eventually validate the region
|
|
|
- again, at the same time eliminating multiple points and overlapping spans.
|
|
|
- *)
|
|
|
-
|
|
|
(* encode point coordinates and curve direction into a LONGINT *)
|
|
|
PROCEDURE Encode (VAR item: LONGINT; u, v, dir: LONGINT);
|
|
|
BEGIN
|
|
@@ -1302,7 +1267,6 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
reg.EnumerateInv(llx, lly, urx, ury, enum, edata);
|
|
|
END EnumerateInv;
|
|
|
|
|
|
-
|
|
|
(**--- Region Construction ---**)
|
|
|
|
|
|
(** make region empty **)
|
|
@@ -1383,23 +1347,6 @@ MODULE GfxRegions; (** portable *) (* eos *)
|
|
|
reg.Invert();
|
|
|
END Invert;
|
|
|
|
|
|
- (**
|
|
|
- In addition to creating rectangular regions and using Boolean operations to combine several regions, a region
|
|
|
- can also be built by tracing its outline with AddPoint. In order to allow the correct handling of self_intersecting
|
|
|
- contours, a direction parameter is needed which indicates whether the curve is going up (dy = 1) or down
|
|
|
- (dy = -1) at the given point.
|
|
|
-
|
|
|
- When performing Boolean operations upon regions or when building regions from self_intersecting contours,
|
|
|
- it is possible that some areas in the resulting region get "covered" more than once. Since most query operations
|
|
|
- reduce regions to non_overlapping areas, a rule which decides whether a point is inside a region or not is needed.
|
|
|
- Imagine a ray originating at such a point and counting every intersection of the ray with the boundary curve of the
|
|
|
- region as +1 if the curve crosses the ray from right to left and as -1 otherwise.
|
|
|
- - for mode Winding (the default), a point is inside the region if the resulting sum is non_zero
|
|
|
- - for mode EvenOdd, a point is inside the region if the resulting sum is odd
|
|
|
-
|
|
|
- Behaviour of all region queries and region operations is undefined for contours which are not closed.
|
|
|
- **)
|
|
|
-
|
|
|
(** add a scanline intersection to a region **)
|
|
|
PROCEDURE AddPoint* (reg: Region; x, y, dy: INTEGER);
|
|
|
BEGIN
|