|
DataMuseum.dkPresents historical artifacts from the history of: Rational R1000/400 Tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Rational R1000/400 Tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - downloadIndex: ┃ B T ┃
Length: 104403 (0x197d3) Types: TextFile Names: »B«
└─⟦85b835f43⟧ Bits:30000549 8mm tape, Rational 1000, Xlib rev 6.00 └─ ⟦0c20f784e⟧ »DATA« └─⟦1abbe589f⟧ └─⟦059497ac5⟧ └─⟦this⟧
with Unchecked_Deallocation; with Xlbt_Arithmetic; use Xlbt_Arithmetic; with Xlbt_Basic; use Xlbt_Basic; with Xlbt_Exceptions; use Xlbt_Exceptions; with Xlbt_Gc; use Xlbt_Gc; with Xlbt_Graphics; use Xlbt_Graphics; with Xlbt_Region; use Xlbt_Region; with Xlbt_Region2; use Xlbt_Region2; with Xlbp_Gc; use Xlbp_Gc; package body Xlbp_Region is ------------------------------------------------------------------------------ -- X Library Regions -- -- Xlbp_Region - Create and manipulate regions. ------------------------------------------------------------------------------ -- Copyright 1989 - 1991 by Rational, Santa Clara, California. -- Copyright 1987 - 1989 by Digital Equipment Corporation, Maynard, Mass. -- Copyright 1987 - 1989 by Massachusetts Institute of Technology, -- Cambridge, Massachusetts. -- -- All Rights Reserved. -- -- Permission to use, copy, modify, and distribute this software and its -- documentation for any purpose and without fee is hereby granted, -- provided that the above copyright notice(s) appear in all copies and that -- both that copyright notice(s) and this permission notice appear in -- supporting documentation, and that the names of Digital, MIT, or Rational -- not be used in advertising or publicity pertaining to distribution of -- the software without specific, written prior permission. -- -- Digital, MIT, and Rational disclaim all warranties with regard to this -- software, including all implied warranties of merchantability and fitness, -- in no event shall Digital, MIT, or Rational be liable for any special, -- indirect or consequential damages or any damages whatsoever resulting from -- loss of use, data or profits, whether in an action of contract, negligence -- or other tortious action, arising out of or in connection with the use or -- performance of this software. ------------------------------------------------------------------------------ --\f ------------------------------------------------------------------------------ -- This file contains a few routines to help track -- the edge of a filled object. The object is assumed -- to be filled in scan_line order, and thus the -- algorithm used is an extension of Bresenham's line -- drawing algorithm which assumes that y is always the -- major axis. -- Since these pieces of code are the same for any filled shape, -- it is more convenient to gather the library in one -- place, but since these pieces of code are also in -- the inner loops of output primitives, procedure call -- overhead is out of the question. -- See the author for a derivation if needed. ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- This structure contains all of the information needed -- to run the Bresenham algorithm. -- The variables may be hardcoded into the declarations -- instead of using this structure to make use of -- register declarations. ------------------------------------------------------------------------------ type Bres_Info is record Minor_Axis : S_Long := 0; -- minor axis D : S_Long := 0; -- decision variable M : S_Long := 0; -- slope M1 : S_Long := 0; -- slope+1 Incr1 : S_Long := 0; -- error increment Incr2 : S_Long := 0; -- error increment end record; ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ type X_Winding_Number_Rule is (Clockwise, Counterclockwise); None_X_Winding_Rule : constant X_Winding_Number_Rule := X_Winding_Number_Rule'Val (0); ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ type X_Edge_Table_Entry_Rec; type X_Edge_Table_Entry is access X_Edge_Table_Entry_Rec; type X_Edge_Table_Entry_Rec is record Y_Max : S_Long := 0; -- y_coord at which we exit this edge. Bres : Bres_Info; -- Bresenham info to run the edge Next : X_Edge_Table_Entry; -- next in the list Back : X_Edge_Table_Entry; -- for insertion sort Next_Wete : -- for winding num rule X_Edge_Table_Entry; Clock_Wise : -- flag for winding number rule X_Winding_Number_Rule := None_X_Winding_Rule; end record; --/ if Enable_Deallocation then pragma Enable_Deallocation (X_Edge_Table_Entry); --/ end if; -- procedure Free_X_Edge_Table_Entry is -- new Unchecked_Deallocation (X_Edge_Table_Entry_Rec, -- X_Edge_Table_Entry); type X_Edge_Table_Entry_Array is array (S_Natural range <>) of X_Edge_Table_Entry; ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ type X_Scan_Line_List_Rec; type X_Scan_Line_List is access X_Scan_Line_List_Rec; type X_Scan_Line_List_Rec is record Scan_Line : S_Long := 0; -- the scan_line represented Edgelist : -- header node X_Edge_Table_Entry; Next : X_Scan_Line_List; -- next in the list end record; --/ if Enable_Deallocation then pragma Enable_Deallocation (X_Scan_Line_List); --/ end if; -- procedure Free_X_Scan_Line_List is -- new Unchecked_Deallocation (X_Scan_Line_List_Rec, -- X_Scan_Line_List); ------------------------------------------------------------------------------ -- Here is a struct to help with storage allocation -- so we can allocate a big chunk at a time, and then take -- pieces from this heap when we need to. ------------------------------------------------------------------------------ Slls_Per_Block : constant := 25; type X_Scan_Line_List_Block_Rec; type X_Scan_Line_List_Block is access X_Scan_Line_List_Block_Rec; type X_Scan_Line_List_Rec_Array is array (S_Natural range <>) of X_Scan_Line_List; type X_Scan_Line_List_Block_Rec is record Slls : X_Scan_Line_List_Rec_Array (0 .. Slls_Per_Block); Next : X_Scan_Line_List_Block; end record; --/ if Enable_Deallocation then pragma Enable_Deallocation (X_Scan_Line_List_Block); --/ end if; procedure Real_Free_X_Scan_Line_List_Block is new Unchecked_Deallocation (X_Scan_Line_List_Block_Rec, X_Scan_Line_List_Block); ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ type X_Edge_Table is record Y_Max : S_Long := 0; -- y_max for the polygon Y_Min : S_Long := 0; -- y_min for the polygon Scan_Lines : -- header node X_Scan_Line_List; end record; ------------------------------------------------------------------------------ -- number of points to buffer before sending them off -- to scan_lines() : Must be an even number ------------------------------------------------------------------------------ X_Num_Pts_To_Buffer : constant := 200; ------------------------------------------------------------------------------ -- X_Point_Block_Rec - used to allocate buffers for points and link the -- buffers together ------------------------------------------------------------------------------ type X_Point_Block_Rec; type X_Point_Block is access X_Point_Block_Rec; type X_Point_Block_Rec is record Pts : X_Point_Array (0 .. X_Num_Pts_To_Buffer - 1); Next : X_Point_Block := null; end record; --/ if Enable_Deallocation then pragma Enable_Deallocation (X_Point_Block); --/ end if; procedure Free_X_Point_Block is new Unchecked_Deallocation (X_Point_Block_Rec, X_Point_Block); ------------------------------------------------------------------------------ -- These are the data structures needed to scan -- convert regions. Two different scan conversion -- methods are available -- the even-odd method, and -- the winding number method. -- The even-odd rule states that a point is inside -- the polygon if a ray drawn from that point in any -- direction will pass through an odd number of -- path segments. -- By the winding number rule, a point is decided -- to be inside the polygon if a ray drawn from that -- point in any direction passes through a different -- number of clockwise and counter-clockwise path -- segments. -- -- These data structures are adapted somewhat from -- the algorithm in (Foley/Van D_am) for scan converting -- polygons. -- The basic algorithm is to start at the top (smallest y) -- of the polygon, stepping down to the bottom of -- the polygon by incrementing the y coordinate. We -- keep a list of edges which the current scan_line crosses, -- sorted by x. This list is called the Active Edge Table (AET) -- As we change the y-coordinate, we update each entry in -- in the active edge table to reflect the edges new x_coord. -- This list must be sorted at each scan_line in case -- two edges intersect. -- We also keep a data structure known as the Edge Table (ET), -- which keeps track of all the edges which the current -- scan_line has not yet reached. The ET is basically a -- list of Scan_Line_List structures containing a list of -- edges which are entered at a given scan_line. There is one -- Scan_Line_List per scan_line at which an edge is entered. -- When we enter a new edge, we move it from the ET to the AET. -- -- From the AET, we can implement the even-odd rule as in -- (Foley/Van D_am). -- The winding number rule is a little trickier. We also -- keep the Edge_Table_Entries in the AET linked by the -- next_WETE (winding Edge_Table_Entry) link. This allows -- the edges to be linked just as before for updating -- purposes, but only uses the edges linked by the next_WETE -- link as edges representing spans of the polygon to -- drawn (as with the even-odd rule). ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- In scan converting polygons, we want to choose those pixels -- which are inside the polygon. Thus, we add .5 to the starting -- x coordinate for both left and right edges. Now we choose the -- first pixel which is inside the polygon for the left edge and the -- first pixel which is outside the polygon for the right edge. -- Draw the left pixel, but not the right. -- -- How to add .5 to the starting x coordinate: -- If the edge is moving to the right, then subtract dy from the -- error term from the general form of the algorithm. -- If the edge is moving to the left, then add dy to the error term. -- -- The reason for the difference between edges moving to the left -- and edges moving to the right is simple: If an edge is moving -- to the right, then we want the algorithm to flip immediately. -- If it is moving to the left, then we don't want it to flip until -- we traverse an entire pixel. ------------------------------------------------------------------------------ Max_Short : constant := 32767; Min_Short : constant := -Max_Short; Large_Coordinate : constant := 1000000; Small_Coordinate : constant := -Large_Coordinate; --\f function Extent_Check (R1 : X_Box_Rec; R2 : X_Box_Rec) return Boolean is begin return not ((R1.X2 <= R2.X1) or else (R1.X1 >= R2.X2) or else (R1.Y2 <= R2.Y1) or else (R1.Y1 >= R2.Y2)); end Extent_Check; --\f -- procedure Extents (R : X_Box_Rec; Id_Rect : X_Region) is -- begin -- if R.X1 < Id_Rect.Extents.X1 then -- Id_Rect.Extents.X1 := R.X1; -- end if; -- if R.Y1 < Id_Rect.Extents.Y1 then -- Id_Rect.Extents.Y1 := R.Y1; -- end if; -- if R.X2 > Id_Rect.Extents.X2 then -- Id_Rect.Extents.X2 := R.X2; -- end if; -- if R.Y2 > Id_Rect.Extents.Y2 then -- Id_Rect.Extents.Y2 := R.Y2; -- end if; -- end Extents; --\f -- procedure Mem_Check (Reg : X_Region) is -- New_Rect : X_Box_List; -- begin -- -- if Reg.Num_Rects >= Reg.Size - 1 then -- New_Rect := new X_Box_Array (1 .. 2 * s_natural (Reg.Size)); -- New_Rect (Reg.Rects'Range) := Reg.Rects.all; -- Free_X_Box_List (Reg.Rects); -- Reg.Rects := New_Rect; -- Reg.Size := Reg.Size * 2; -- end if; -- -- end Mem_Check; --\f procedure Empty_Region (P_Reg : X_Region) is begin P_Reg.Num_Rects := 0; end Empty_Region; --\f function Region_Not_Empty (P_Reg : X_Region) return Boolean is begin return P_Reg.Num_Rects /= 0; end Region_Not_Empty; --\f function In_Box (R : X_Box_Rec; X : S_Short; Y : S_Short) return Boolean is begin return (R.X2 >= X) and then (R.X1 <= X) and then (R.Y2 >= Y) and then (R.Y1 <= Y); end In_Box; --\f ------------------------------------------------------------------------------ -- Clean up our act. ------------------------------------------------------------------------------ procedure Free_X_Scan_Line_List_Block (P_Sll_Block : in out X_Scan_Line_List_Block) is Tmp_Sll_Block : X_Scan_Line_List_Block; begin while P_Sll_Block /= null loop Tmp_Sll_Block := P_Sll_Block.Next; Real_Free_X_Scan_Line_List_Block (P_Sll_Block); P_Sll_Block := Tmp_Sll_Block; end loop; end Free_X_Scan_Line_List_Block; --\f procedure B_Res_Init_Pgon (Dy : S_Long; X1 : S_Long; X2 : S_Long; X_Start : out S_Long; D : out S_Long; M : out S_Long; M1 : out S_Long; Incr1 : out S_Long; Incr2 : out S_Long) is Dx : S_Long; -- local storage Q : S_Long; Q1 : S_Long; begin -- if the edge is horizontal, then it is ignored -- and assumed not to be processed. Otherwise, do this stuff. if Dy /= 0 then X_Start := X1; Dx := X2 - X1; if Dx < 0 then Q := Dx / Dy; M := Q; Q1 := Q - 1; M1 := Q1; Incr1 := -2 * Dx + 2 * Dy * Q1; Incr2 := -2 * Dx + 2 * Dy * Q; D := 2 * Q * Dy - 2 * Dx - 2 * Dy; else Q := Dx / Dy; M := Q; Q1 := Q + 1; M1 := Q1; Incr1 := 2 * Dx - 2 * Dy * Q1; Incr2 := 2 * Dx - 2 * Dy * Q; D := (-2) * Q * Dy + 2 * Dx; end if; end if; end B_Res_Init_Pgon; --\f procedure B_Res_Incr_Pgon (D : in out S_Long; Minval : in out S_Long; M : S_Long; M1 : S_Long; Incr1 : S_Long; Incr2 : S_Long) is begin if M1 > 0 then if D > 0 then Minval := Minval + M1; D := D + Incr1; else Minval := Minval + M; D := D + Incr2; end if; else if D >= 0 then Minval := Minval + M1; D := D + Incr1; else Minval := Minval + M; D := D + Incr2; end if; end if; end B_Res_Incr_Pgon; --\f procedure B_Res_Init_Pgon_Struct (Dmaj : S_Long; Min1 : S_Long; Min2 : S_Long; Bres : out Bres_Info) is begin B_Res_Init_Pgon (Dmaj, Min1, Min2, Bres.Minor_Axis, Bres.D, Bres.M, Bres.M1, Bres.Incr1, Bres.Incr2); end B_Res_Init_Pgon_Struct; --\f procedure B_Res_Incr_Pgon_Struct (Bres : in out Bres_Info) is begin B_Res_Incr_Pgon (Bres.D, Bres.Minor_Axis, Bres.M, Bres.M1, Bres.Incr1, Bres.Incr2); end B_Res_Incr_Pgon_Struct; --\f ------------------------------------------------------------------------------ -- a few macros for the inner loops of the fill code where -- performance considerations don't allow a procedure call. -- -- Evaluate the given edge at the given scan_line. -- If the edge has expired, then we leave it and fix up -- the active edge table; otherwise, we increment the -- x value to be ready for the next scan_line. -- The winding number rule is in effect, so we must notify -- the caller when the edge has been removed so he -- can reorder the Winding Active Edge Table. ------------------------------------------------------------------------------ procedure Evaluate_Edge_Winding (P_Aet : in out X_Edge_Table_Entry; P_Prev_Aet : in out X_Edge_Table_Entry; Y : S_Long; Fix_Waet : in out Boolean) is begin if P_Aet.Y_Max = Y then -- leaving this edge P_Prev_Aet.Next := P_Aet.Next; P_Aet := P_Prev_Aet.Next; Fix_Waet := True; if P_Aet /= null then P_Aet.Back := P_Prev_Aet; end if; else B_Res_Incr_Pgon_Struct (P_Aet.Bres); P_Prev_Aet := P_Aet; P_Aet := P_Aet.Next; end if; end Evaluate_Edge_Winding; --\f ------------------------------------------------------------------------------ -- Evaluate the given edge at the given scan_line. -- If the edge has expired, then we leave it and fix up -- the active edge table; otherwise, we increment the -- x value to be ready for the next scan_line. -- The even-odd rule is in effect. ------------------------------------------------------------------------------ procedure Evaluate_Edge_Even_Odd (P_Aet : in out X_Edge_Table_Entry; P_Prev_Aet : in out X_Edge_Table_Entry; Y : S_Long) is begin if P_Aet.Y_Max = Y then -- leaving this edge P_Prev_Aet.Next := P_Aet.Next; P_Aet := P_Prev_Aet.Next; if P_Aet /= null then P_Aet.Back := P_Prev_Aet; end if; else B_Res_Incr_Pgon_Struct (P_Aet.Bres); P_Prev_Aet := P_Aet; P_Aet := P_Aet.Next; end if; end Evaluate_Edge_Even_Odd; --\f ------------------------------------------------------------------------------ -- Insert_Edge_In_ET -- -- Insert the given edge into the edge table. -- First we must find the correct bucket in the -- Edge table, then find the right slot in the -- bucket. Finally, we can insert it. ------------------------------------------------------------------------------ procedure Insert_Edge_In_Et (Et : X_Edge_Table; Ete : X_Edge_Table_Entry; Scan_Line : S_Long; Sll_Block : in out X_Scan_Line_List_Block; I_Sll_Block : in out S_Natural) is Start : X_Edge_Table_Entry; Prev : X_Edge_Table_Entry; P_Sll : X_Scan_Line_List; P_Prev_Sll : X_Scan_Line_List; Tmp_Sll_Block : X_Scan_Line_List_Block; begin ----Find the right bucket to put the edge into. P_Prev_Sll := Et.Scan_Lines; P_Sll := P_Prev_Sll.Next; while P_Sll /= null and then P_Sll.Scan_Line < Scan_Line loop P_Prev_Sll := P_Sll; P_Sll := P_Sll.Next; end loop; ----Reassign p_SLL (pointer to Scan_Line_List) if necessary if P_Sll = null or else P_Sll.Scan_Line > Scan_Line then if I_Sll_Block > Slls_Per_Block - 1 then Tmp_Sll_Block := new X_Scan_Line_List_Block_Rec; Sll_Block.Next := Tmp_Sll_Block; Tmp_Sll_Block.Next := null; Sll_Block := Tmp_Sll_Block; I_Sll_Block := 0; end if; P_Sll := Sll_Block.Slls (I_Sll_Block); I_Sll_Block := I_Sll_Block + 1; P_Sll.Next := P_Prev_Sll.Next; P_Sll.Edgelist := null; P_Prev_Sll.Next := P_Sll; end if; P_Sll.Scan_Line := Scan_Line; ----Now insert the edge in the right bucket Prev := null; Start := P_Sll.Edgelist; while Start /= null and then Start.Bres.Minor_Axis < Ete.Bres.Minor_Axis loop Prev := Start; Start := Start.Next; end loop; Ete.Next := Start; if Prev /= null then Prev.Next := Ete; else P_Sll.Edgelist := Ete; end if; end Insert_Edge_In_Et; --\f procedure Create_Et_And_Aet (Pts : X_Point_Array; Et : in out X_Edge_Table; Aet : X_Edge_Table_Entry; P_Etes : X_Edge_Table_Entry_Array; P_Sll_Block_In : X_Scan_Line_List_Block) is ------------------------------------------------------------------------------ -- Create_Edge_Table -- -- This routine creates the edge table for -- scan converting polygons. -- The Edge Table (ET) looks like: -- -- Edge_Table -- --------- -- | y_max | Scan_Line_Lists -- |scan_line|...---------.....---------.... -- -------- |scan_line| |scan_line| -- |edgelist | |edgelist | -- --------- --------- -- | | -- | | -- V V list of ETEs list of ETEs -- -- where ETE is an Edge_Table_Entry data structure, -- and there is one Scan_Line_List per scan_line at -- which an edge is initially entered. -- ------------------------------------------------------------------------------ P_Sll_Block : X_Scan_Line_List_Block := P_Sll_Block_In; Top : S_Natural; Bottom : S_Natural; Prev_Pt : S_Natural; I_Sll_Block : S_Natural := 0; Dy : S_Long; P_Etes_I : S_Natural := P_Etes'First; begin if Pts'Length < 2 then return; end if; ----Initialize the Active Edge Table Aet.Next := null; Aet.Back := null; Aet.Next_Wete := null; Aet.Bres.Minor_Axis := Small_Coordinate; ----Initialize the Edge Table. Et.Scan_Lines.Next := null; Et.Y_Max := Small_Coordinate; Et.Y_Min := Large_Coordinate; P_Sll_Block.Next := null; Prev_Pt := Pts'Length - 1; ----For each vertex in the array of points... -- In this loop we are dealing with two vertices at a time. These make up -- one edge of the polygon. for Curr_Pt in Pts'First .. Pts'First + Pts'Length - 1 loop ----Find out which point is above and which is below. if Pts (Prev_Pt).Y > Pts (Curr_Pt).Y then Bottom := Prev_Pt; Top := Curr_Pt; P_Etes (P_Etes_I).Clock_Wise := Counterclockwise; else Bottom := Curr_Pt; Top := Prev_Pt; P_Etes (P_Etes_I).Clock_Wise := Clockwise; end if; ----Don't add horizontal edges to the Edge table. if Pts (Bottom).Y /= Pts (Top).Y then P_Etes (P_Etes_I).Y_Max := S_Long (Pts (Bottom).Y) - 1; -- -1 so we don't get last scan_line ----Initialize integer edge algorithm Dy := S_Long (Pts (Bottom).Y) - S_Long (Pts (Top).Y); B_Res_Init_Pgon_Struct (Dy, S_Long (Pts (Top).X), S_Long (Pts (Bottom).X), P_Etes (P_Etes_I).Bres); Insert_Edge_In_Et (Et, P_Etes (P_Etes_I), S_Long (Pts (Top).Y), P_Sll_Block, I_Sll_Block); Et.Y_Max := Max (Et.Y_Max, S_Long (Pts (Prev_Pt).Y)); Et.Y_Min := Min (Et.Y_Min, S_Long (Pts (Prev_Pt).Y)); P_Etes_I := P_Etes_I + 1; end if; Prev_Pt := Curr_Pt; end loop; end Create_Et_And_Aet; --\f procedure Load_Aet (Aet_In : X_Edge_Table_Entry; Etes_In : X_Edge_Table_Entry) is ------------------------------------------------------------------------------ -- load_AET -- -- This routine moves Edge_Table_Entries from the -- Edge_Table into the Active Edge Table, -- leaving them sorted by smaller x coordinate. -- ------------------------------------------------------------------------------ Aet : X_Edge_Table_Entry := Aet_In; Etes : X_Edge_Table_Entry := Etes_In; P_Prev_Aet : X_Edge_Table_Entry; Tmp : X_Edge_Table_Entry; begin P_Prev_Aet := Aet; Aet := Aet.Next; while Etes /= null loop while Aet /= null and then Aet.Bres.Minor_Axis < Etes.Bres.Minor_Axis loop P_Prev_Aet := Aet; Aet := Aet.Next; end loop; Tmp := Etes.Next; Etes.Next := Aet; if Aet /= null then Aet.Back := Etes; end if; Etes.Back := P_Prev_Aet; P_Prev_Aet.Next := Etes; P_Prev_Aet := Etes; Etes := Tmp; end loop; end Load_Aet; --\f procedure Compute_Waet (Aet_In : X_Edge_Table_Entry) is ------------------------------------------------------------------------------ -- compute_WAET -- -- This routine links the AET by the -- next_WETE (winding Edge_Table_Entry) link for -- use by the winding number rule. The final -- Active Edge Table (AET) might look something -- like: -- -- AET -- ---------- --------- --------- -- |y_max or |y_max or |y_max or -- or ... or |... or |... | -- |next |.|next |.|next |.... -- |next_WETE| |next_WETE| |next_WETE| -- --------- --------- ^-------- -- or or | -- V------------------. V--. ... -- ------------------------------------------------------------------------------ P_Wete : X_Edge_Table_Entry; Aet : X_Edge_Table_Entry := Aet_In; Inside : Boolean := True; Isinside : Integer := 0; begin Aet.Next_Wete := null; P_Wete := Aet; Aet := Aet.Next; while Aet /= null loop if Aet.Clock_Wise = Clockwise then Isinside := Isinside + 1; else Isinside := Isinside - 1; end if; if (not Inside and then Isinside = 0) or else (Inside and then Isinside /= 0) then P_Wete.Next_Wete := Aet; P_Wete := Aet; Inside := not Inside; end if; Aet := Aet.Next; end loop; P_Wete.Next_Wete := null; end Compute_Waet; --\f function Insertion_Sort (Aet_In : X_Edge_Table_Entry) return Boolean is ------------------------------------------------------------------------------ -- Insertion_Sort -- -- Just a simple insertion sort using -- pointers and back pointers to sort the Active -- Edge Table. ------------------------------------------------------------------------------ Aet : X_Edge_Table_Entry := Aet_In; P_Ete_Chase : X_Edge_Table_Entry; P_Ete_Insert : X_Edge_Table_Entry; P_Ete_Chase_Back_Tmp : X_Edge_Table_Entry; Changed : Boolean := False; begin Aet := Aet.Next; while Aet /= null loop P_Ete_Insert := Aet; P_Ete_Chase := Aet; while P_Ete_Chase.Back.Bres.Minor_Axis > Aet.Bres.Minor_Axis loop P_Ete_Chase := P_Ete_Chase.Back; end loop; Aet := Aet.Next; if P_Ete_Chase /= P_Ete_Insert then P_Ete_Chase_Back_Tmp := P_Ete_Chase.Back; P_Ete_Insert.Back.Next := Aet; if Aet /= null then Aet.Back := P_Ete_Insert.Back; end if; P_Ete_Insert.Next := P_Ete_Chase; P_Ete_Chase.Back.Next := P_Ete_Insert; P_Ete_Chase.Back := P_Ete_Insert; P_Ete_Insert.Back := P_Ete_Chase_Back_Tmp; Changed := True; end if; end loop; return Changed; end Insertion_Sort; --\f procedure Pts_To_Region (Num_Full_Pt_Blocks_In : S_Natural; I_Cur_Pt_Block : S_Natural; First_Pt_Block : X_Point_Block; Reg : X_Region; Success : out Boolean) is ------------------------------------------------------------------------------ -- Create an array of rectangles from a list of points. -- If indeed these things (POINTS, RECTS) are the same, -- then this proc is still needed, because it allocates -- storage for the array, which was allocated on the -- stack by the calling procedure. ------------------------------------------------------------------------------ Num_Full_Pt_Blocks : S_Natural := Num_Full_Pt_Blocks_In; Rects : S_Natural; Cur_Pt_Block : X_Point_Block; Extents : X_Box_Rec renames Reg.Extents; Num_Rects : S_Natural; begin Num_Rects := (Num_Full_Pt_Blocks * X_Num_Pts_To_Buffer + I_Cur_Pt_Block) / 2; begin Reg.Rects := new X_Box_Array (1 .. Num_Rects); exception when Storage_Error => Success := False; return; end; Reg.Size := Num_Rects; Cur_Pt_Block := First_Pt_Block; Rects := 1; Extents.X1 := Max_Short; Extents.X2 := Min_Short; while Num_Full_Pt_Blocks /= 0 loop Num_Full_Pt_Blocks := Num_Full_Pt_Blocks - 1; for I in 1 .. S_Natural (X_Num_Pts_To_Buffer) / 2 loop -- the loop uses 2 points per iteration declare Pts : X_Point renames Cur_Pt_Block.Pts (Cur_Pt_Block.Pts'First - 1 + I); Pts1 : X_Point renames Cur_Pt_Block.Pts (Cur_Pt_Block.Pts'First + I); Rect : X_Box_Rec renames Reg.Rects (Rects); begin Rect.X1 := Pts.X; Rect.Y1 := Pts.Y; if Pts1.X = Rect.X1 then Num_Rects := Num_Rects - 1; else Rect.X2 := Pts1.X; Rect.Y2 := Pts1.Y + 1; Extents.X1 := Min (Extents.X1, Pts.X); Extents.X2 := Max (Extents.X2, Pts1.X); Rects := Rects + 1; end if; end; end loop; Cur_Pt_Block := Cur_Pt_Block.Next; end loop; if I_Cur_Pt_Block /= 0 then for I in 1 .. I_Cur_Pt_Block / 2 loop while I_Cur_Pt_Block /= 0 loop declare Pts : X_Point renames Cur_Pt_Block.Pts (Cur_Pt_Block.Pts'First - 2 + 2 * I); Pts1 : X_Point renames Cur_Pt_Block.Pts (Cur_Pt_Block.Pts'First - 1 + 2 * I); Rect : X_Box_Rec renames Reg.Rects (Rects); begin Rect.X1 := Pts.X; Rect.Y1 := Pts.Y; if Pts1.X = Rect.X1 then Num_Rects := Num_Rects - 1; else Rect.X2 := Pts1.X; Rect.Y2 := Pts1.Y + 1; Extents.X1 := Min (Extents.X1, Pts.X); Extents.X2 := Max (Extents.X2, Pts1.X); Rects := Rects + 1; end if; end; end loop; end loop; if Num_Rects > 0 then Extents.Y1 := Reg.Rects (0).Y1; Extents.Y2 := Cur_Pt_Block.Pts (Cur_Pt_Block.Pts'First + I_Cur_Pt_Block).Y + 1; end if; end if; Reg.Num_Rects := Num_Rects; Reg.Size := Reg.Num_Rects; Success := True; end Pts_To_Region; --\f function X_Polygon_Region (Points : X_Point_Array; Rule : X_Fill_Rule) return X_Region is ------------------------------------------------------------------------------ -- poly_to_region -- -- Scan converts a polygon by returning a run-length encoding of the resultant -- bitmap -- the run-length encoding is in the form of an array of rectangles. ------------------------------------------------------------------------------ Regn : X_Region; Y : S_Long; -- current scan line P_Aet : X_Edge_Table_Entry; -- Active Edge Table I_Pts : S_Natural := 0; -- number of points in buffer P_Wete : X_Edge_Table_Entry; -- Winding Edge Table Entry P_Sll : X_Scan_Line_List; -- current scan_Line_List L_Pts : X_Point_Block; -- output buffer L_I_Pts : S_Natural; P_Prev_Aet : X_Edge_Table_Entry; -- ptr to previous AET Et : X_Edge_Table; -- header node for ET Aet : X_Edge_Table_Entry; -- header node for AET P_Etes : X_Edge_Table_Entry_Array (1 .. Points'Length);-- ETE pool Sll_Block : X_Scan_Line_List_Block; -- header for scan_line_list Fix_Waet : Boolean := False; First_Pt_Block : X_Point_Block; Cur_Pt_Block : X_Point_Block; -- Pt_Block buffers Tmp_Pt_Block : X_Point_Block; Num_Full_Pt_Blocks : S_Natural := 0; Void : Boolean; begin Regn := X_Create_Region; if Regn = None_X_Region then return None_X_Region; end if; ----Special case a rectangle. I_Pts := Points'First; if (Points'Length = 4 or else (Points'Length = 5 and then Points (4).X = Points (0).X and then Points (4).Y = Points (0).Y)) and then ((Points (0).Y = Points (1).Y and then Points (1).X = Points (2).X and then Points (2).Y = Points (3).Y and then Points (3).X = Points (0).X) or else (Points (0).X = Points (1).X and then Points (1).Y = Points (2).Y and then Points (2).X = Points (3).X and then Points (3).Y = Points (0).Y)) then Regn.Extents.X1 := Min (Points (0).X, Points (2).X); Regn.Extents.Y1 := Min (Points (0).Y, Points (2).Y); Regn.Extents.X2 := Max (Points (0).X, Points (2).X); Regn.Extents.Y2 := Max (Points (0).Y, Points (2).Y); if Regn.Extents.X1 /= Regn.Extents.X2 and then Regn.Extents.Y1 /= Regn.Extents.Y2 then Regn.Num_Rects := 1; Regn.Rects := new X_Box_Array'(1 => Regn.Extents); end if; return Regn; end if; First_Pt_Block := new X_Point_Block_Rec; L_Pts := First_Pt_Block; L_I_Pts := L_Pts.Pts'First; Create_Et_And_Aet (Points, Et, Aet, P_Etes, Sll_Block); P_Sll := Et.Scan_Lines.Next; Cur_Pt_Block := First_Pt_Block; if Rule = Even_Odd_Rule then ----For each scan_line.... Y := Et.Y_Min; while Y < Et.Y_Max loop ----Add a new edge to the active edge table when we get to the next edge. if P_Sll /= null and then Y = P_Sll.Scan_Line then Load_Aet (Aet, P_Sll.Edgelist); P_Sll := P_Sll.Next; end if; P_Prev_Aet := Aet; P_Aet := Aet.Next; ----For each active edge.... while P_Aet /= null loop L_Pts.Pts (L_I_Pts).X := S_Short (P_Aet.Bres.Minor_Axis); L_Pts.Pts (L_I_Pts).Y := S_Short (Y); L_I_Pts := L_I_Pts + 1; I_Pts := I_Pts + 1; ----Send out the buffer. if I_Pts = X_Num_Pts_To_Buffer then Tmp_Pt_Block := new X_Point_Block_Rec; Cur_Pt_Block.Next := Tmp_Pt_Block; Cur_Pt_Block := Tmp_Pt_Block; L_Pts := Cur_Pt_Block; L_I_Pts := L_Pts.Pts'First; Num_Full_Pt_Blocks := Num_Full_Pt_Blocks + 1; I_Pts := 0; end if; Evaluate_Edge_Even_Odd (P_Aet, P_Prev_Aet, Y); end loop; Void := Insertion_Sort (Aet); Y := Y + 1; end loop; else ----For each scan_line.... for Y in Et.Y_Min .. Et.Y_Max - 1 loop ----Add a new edge to the active edge table when we get to the next edge. if P_Sll /= null and then Y = P_Sll.Scan_Line then Load_Aet (Aet, P_Sll.Edgelist); Compute_Waet (Aet); P_Sll := P_Sll.Next; end if; P_Prev_Aet := Aet; P_Aet := Aet.Next; P_Wete := P_Aet; ----For each active edge.... while P_Aet /= null loop ----Add to the buffer only those edges that are in the Winding active edge table. if P_Wete = P_Aet then L_Pts.Pts (L_I_Pts).X := S_Short (P_Aet.Bres.Minor_Axis); L_Pts.Pts (L_I_Pts).Y := S_Short (Y); L_I_Pts := L_I_Pts + 1; I_Pts := I_Pts + 1; ----Send out the buffer if I_Pts = X_Num_Pts_To_Buffer then Tmp_Pt_Block := new X_Point_Block_Rec; Cur_Pt_Block.Next := Tmp_Pt_Block; Cur_Pt_Block := Tmp_Pt_Block; L_Pts := Cur_Pt_Block; L_I_Pts := L_Pts.Pts'First; Num_Full_Pt_Blocks := Num_Full_Pt_Blocks + 1; I_Pts := 0; end if; P_Wete := P_Wete.Next_Wete; end if; Evaluate_Edge_Winding (P_Aet, P_Prev_Aet, Y, Fix_Waet); end loop; ----Recompute the winding active edge table if we just resorted or have exited -- an edge. if Insertion_Sort (Aet) or else Fix_Waet then Compute_Waet (Aet); Fix_Waet := False; end if; end loop; end if; Free_X_Scan_Line_List_Block (Sll_Block.Next); Pts_To_Region (Num_Full_Pt_Blocks, I_Pts, First_Pt_Block, Regn, Void); ----Free up storage used. Tmp_Pt_Block := First_Pt_Block.Next; loop Free_X_Point_Block (First_Pt_Block); First_Pt_Block := Tmp_Pt_Block; if First_Pt_Block = null then exit; end if; Tmp_Pt_Block := First_Pt_Block.Next; end loop; return Regn; exception when others => Free_X_Region (Regn); while First_Pt_Block /= null loop Tmp_Pt_Block := First_Pt_Block.Next; Free_X_Point_Block (First_Pt_Block); First_Pt_Block := Tmp_Pt_Block; end loop; raise; end X_Polygon_Region; --\f function Max (A, B : S_Natural) return S_Natural is begin if A > B then return A; else return B; end if; end Max; --/ if INLINE then --// pragma Inline (Max); --/ end if; --\f -- -- The functions in this file implement the Region abstraction used extensively -- throughout the X11 sample server. A Region is simply an area, as the name -- implies, and is implemented as a "y-x-banded" array of rectangles. To -- explain: Each Region is made up of a certain number of rectangles sorted -- by y coordinate first, and then by x coordinate. -- -- Furthermore, the rectangles are banded such that every rectangle with a -- given upper-left y coordinate (y1) will have the same lower-right y -- coordinate (y2) and vice versa. If a rectangle has scan_lines in a band, it -- will span the entire vertical distance of the band. This means that some -- areas that could be merged into a taller rectangle will be represented as -- several shorter rectangles to account for shorter rectangles to its left -- or right but within its "vertical scope". -- -- An added constraint on the rectangles is that they must cover as much -- horizontal area as possible. E.g. no two rectangles in a band are allowed -- to touch. -- -- Whenever possible, bands will be merged together to cover a greater vertical -- distance (and thus reduce the number of rectangles). Two bands can be merged -- only if the bottom of one touches the top of the other and they have -- rectangles in the same places (of the same width, of course). This maintains -- the y-x-banding that's so nice to have... -- --\f function X_Create_Region return X_Region is ------------------------------------------------------------------------------ -- Create a new empty region ------------------------------------------------------------------------------ Temp : X_Region; begin Temp := new X_Region_Rec; Temp.Rects := new X_Box_Array (1 .. 1); Temp.Num_Rects := 0; Temp.Extents.X1 := 0; Temp.Extents.Y1 := 0; Temp.Extents.X2 := 0; Temp.Extents.Y2 := 0; Temp.Size := 1; return Temp; exception when others => Free_X_Region (Temp); raise; end X_Create_Region; --\f procedure X_Clip_Box (Region : X_Region; Rectangle : out X_Rectangle) is begin Rectangle.X := Region.Extents.X1; Rectangle.Y := Region.Extents.Y1; Rectangle.Width := U_Short (Region.Extents.X2 - Region.Extents.X1); Rectangle.Height := U_Short (Region.Extents.Y2 - Region.Extents.Y1); end X_Clip_Box; --\f procedure X_Union_Rect_With_Region (Rectangle : X_Rectangle; Region : X_Region; New_Region : X_Region) is Lregion : X_Region; begin Lregion := new X_Region_Rec; Lregion.Extents.X1 := Rectangle.X; Lregion.Extents.Y1 := Rectangle.Y; Lregion.Extents.X2 := Rectangle.X + S_Short (Rectangle.Width); Lregion.Extents.Y2 := Rectangle.Y + S_Short (Rectangle.Height); Lregion.Size := 1; Lregion.Rects := new X_Box_Array'(1 => Region.Extents); Lregion.Num_Rects := 1; X_Union_Region (Lregion, Region, New_Region); Free_X_Region (Lregion); Free_X_Box_List (Lregion.Rects); exception when others => Free_X_Region (Lregion); raise; end X_Union_Rect_With_Region; --\f procedure Mi_Set_Extents (P_Reg : X_Region) is ------------------------------------------------------------------------- -- mi_Set_Extents -- -- Reset the extents of a region to what they should be. Called by -- mi_Subtract and mi_Intersect b/c they can't figure it out along the -- way or do so easily, as mi_Union can. -- -- Results: -- None. -- -- Side Effects: -- The region's 'extents' structure is overwritten. -- ------------------------------------------------------------------------- P_Extents : X_Box_Rec renames P_Reg.Extents; P_Box : X_Box_List renames P_Reg.Rects; begin ----No rectangles means no extents. if P_Reg.Num_Rects = 0 then P_Extents.X1 := 0; P_Extents.Y1 := 0; P_Extents.X2 := 0; P_Extents.Y2 := 0; return; end if; ----Since p_Box is the first rectangle in the region, it must have the smallest -- y1 and since p_Box_End is the last rectangle in the region, it must have -- the largest y2, because of banding. Initialize x1 and x2 from p_Box and -- p_Box_End, resp., as good things to initialize them to... P_Extents.X1 := P_Box (1).X1; P_Extents.Y1 := P_Box (1).Y1; P_Extents.X2 := P_Box (P_Reg.Num_Rects).X2; P_Extents.Y2 := P_Box (P_Reg.Num_Rects).Y2; --/ if DEBUG then if not (P_Extents.Y1 < P_Extents.Y2) then raise X_Library_Confusion; end if; --/ end if; for Pbi in 1 .. P_Reg.Num_Rects loop if P_Box (Pbi).X1 < P_Extents.X1 then P_Extents.X1 := P_Box (Pbi).X1; end if; if P_Box (Pbi).X2 > P_Extents.X2 then P_Extents.X2 := P_Box (Pbi).X2; end if; end loop; --/ if DEBUG then if not (P_Extents.X1 < P_Extents.X2) then raise X_Library_Confusion; end if; --/ end if; end Mi_Set_Extents; --\f procedure X_Set_Region (Display : X_Display; Gc : X_Gc; Region : X_Region) is X_R : X_Rectangle_Array (1 .. Region.Num_Rects); begin for I in X_R'Range loop X_R (I).X := Region.Rects (I).X1; X_R (I).Y := Region.Rects (I).Y1; X_R (I).Width := U_Short (Region.Rects (I).X2 - Region.Rects (I).X1); X_R (I).Height := U_Short (Region.Rects (I).Y2 - Region.Rects (I).Y1); end loop; X_Set_Clip_Rectangles (Display, Gc, 0, 0, X_R, Unsorted); end X_Set_Region; --\f procedure X_Offset_Region (Region : X_Region; Dx : S_Short; Dy : S_Short) is ------------------------------------------------------------------------------ -- Translate_Region(Region, x, y) -- translates in place -- added by raymond ------------------------------------------------------------------------------ Pbox : X_Box_List renames Region.Rects; begin for I in 1 .. Region.Num_Rects loop Pbox (I).X1 := Pbox (I).X1 + Dx; Pbox (I).X2 := Pbox (I).X2 + Dx; Pbox (I).Y1 := Pbox (I).Y1 + Dy; Pbox (I).Y2 := Pbox (I).Y2 + Dy; end loop; Region.Extents.X1 := Region.Extents.X1 + Dx; Region.Extents.X2 := Region.Extents.X2 + Dx; Region.Extents.Y1 := Region.Extents.Y1 + Dy; Region.Extents.Y2 := Region.Extents.Y2 + Dy; end X_Offset_Region; --\f -- Utility procedure Compress: -- Replace r by the region r', where -- p in r' iff (Quantifer m <= dx) (p + m in r), and -- Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and -- (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE. -- -- Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region -- of all points p such that p and the next dx points on the same -- horizontal scan line are all in r. We do this using by noting -- that p is the head of a run of length 2^i + k iff p is the head -- of a run of length 2^i and p+2^i is the head of a run of length -- k. Thus, the loop invariant: s contains the region corresponding -- to the runs of length shift. r contains the region corresponding -- to the runs of length 1 + dxo & (shift-1), where dxo is the original -- value of dx. dx = dxo & ~(shift-1). As parameters, s and t are -- scratch regions, so that we don't have to allocate them on every -- call. procedure Compress (R : X_Region; S : X_Region; T : X_Region; Dx : S_Short; Xdir : Boolean; Grow : Boolean) is Shift : S_Short := 1; Rp : X_Region := R; Sp : X_Region := S; Tp : X_Region := T; Dxi : S_Short := Dx; procedure Zopregion (A : X_Region; B : X_Region; C : X_Region) is begin if Grow then X_Union_Region (A, B, C); else X_Intersect_Region (A, B, C); end if; end Zopregion; procedure Zshiftregion (A : X_Region; B : S_Short) is begin if Xdir then X_Offset_Region (A, B, 0); else X_Offset_Region (A, 0, B); end if; end Zshiftregion; procedure Zcopyregion (A : X_Region; B : X_Region) is begin X_Union_Region (A, A, B); end Zcopyregion; begin Zcopyregion (Rp, Sp); while Dxi /= 0 loop if Dxi mod 2 /= 0 then Zshiftregion (Rp, -Shift); Zopregion (Rp, Sp, Rp); Dxi := Dxi / 2; if Dxi = 0 then exit; end if; end if; Zcopyregion (Sp, Tp); Zshiftregion (Sp, -Shift); Zopregion (Sp, Tp, Sp); if Dxi /= 0 then ----We don't want overflow; dxi /= 0 means this is ok. Shift := Shift + Shift; end if; end loop; end Compress; --\f procedure X_Shrink_Region (Region : X_Region; Dx : S_Short; Dy : S_Short) is Regioni : X_Region := Region; S : X_Region; T : X_Region; Grow : Boolean; Dxi : S_Short := Dx; Dyi : S_Short := Dy; begin if Dx = 0 and then Dy = 0 then return; end if; S := new X_Region_Rec; T := new X_Region_Rec; Grow := Dxi < 0; if Grow then Dxi := -Dxi; end if; if Dxi /= 0 then Compress (Regioni, S, T, 2 * Dxi, True, Grow); end if; Grow := Dyi < 0; if Grow then Dyi := -Dyi; end if; if Dyi /= 0 then Compress (Regioni, S, T, 2 * Dyi, False, Grow); end if; X_Offset_Region (Region, Dxi, Dyi); Free_X_Region (S); Free_X_Region (T); exception when others => Free_X_Region (S); Free_X_Region (T); raise; end X_Shrink_Region; --\f -- #ifdef notdef -- --********************************************************** -- -- Bop down the array of rects until we have passed -- -- scan_line y. num_Rects is the size of the array. -- --********************************************************* -- -- static BOX -- *Index_Rects(rects, num_Rects, y) -- BOX *rects; -- s_long num_Rects; -- s_long y; -- { -- while ((num_Rects--) and then (rects.y2 <= y)) -- rects++; -- return(rects); -- } -- #endif --\f --====================================================================== -- Generic Region Operator --==================================================================== function Mi_Coalesce (P_Reg : X_Region; -- Region to coalesce Prev_Start_In : S_Natural; -- Index of start of previous band Cur_Start_In : S_Natural) -- Index of start of current band return S_Natural is ------------------------------------------------------------------------- -- mi_Coalesce -- -- Attempt to merge the boxes in the current band with those in the -- previous one. Used only by mi_Region_Op. -- -- Results: -- The new index for the previous band. -- -- Side Effects: -- If coalescing takes place: -- - rectangles in the previous band will have their y2 fields altered. -- - p_Reg.num_Rects will be decreased. -- ------------------------------------------------------------------------- Prev_Start : S_Natural := Prev_Start_In; Cur_Start : S_Natural := Cur_Start_In; P_Prev_Box : S_Natural; -- Current box in previous band P_Cur_Box : S_Natural; -- Current box in current band P_Reg_End : S_Natural; -- End of regionn Cur_Num_Rects : S_Natural; -- Number of rectangles in current band Prev_Num_Rects : S_Natural; -- Number of rectangles in previous band Bandy1 : S_Short; -- Y1 coordinate for current band begin P_Reg_End := P_Reg.Num_Rects + 1; P_Prev_Box := Prev_Start; Prev_Num_Rects := Cur_Start - Prev_Start; ----Figure out how many rectangles are in the current band. Have to do this -- because multiple bands could have been added in mi_Region_Op at the end -- when one region has been exhausted. P_Cur_Box := Cur_Start; Bandy1 := P_Reg.Rects (P_Cur_Box).Y1; Cur_Num_Rects := 0; while P_Cur_Box < P_Reg_End and then P_Reg.Rects (P_Cur_Box).Y1 = Bandy1 loop Cur_Num_Rects := Cur_Num_Rects + 1; P_Cur_Box := P_Cur_Box + 1; end loop; ----If more than one band was added, we have to find the start of the last band -- added so the next coalescing job can start at the right place... (given when -- multiple bands are added, this may be pointless -- see above). if P_Cur_Box < P_Reg_End then Cur_Start := P_Reg_End - 1; while P_Reg.Rects (Cur_Start - 1).Y1 = P_Reg.Rects (Cur_Start).Y1 loop Cur_Start := Cur_Start - 1; end loop; end if; if Cur_Num_Rects = Prev_Num_Rects and then Cur_Num_Rects /= 0 then P_Cur_Box := P_Cur_Box - Cur_Num_Rects; ----The bands may only be coalesced if the bottom of the previous matches the -- top scan_line of the current. if P_Reg.Rects (P_Prev_Box).Y2 = P_Reg.Rects (P_Cur_Box).Y1 then ----Make sure the bands have boxes in the same places. This assumes that boxes -- have been added in such a way that they cover the most area possible. I.e. -- two boxes in a band must have some horizontal space between them. loop if P_Reg.Rects (P_Prev_Box).X1 /= P_Reg.Rects (P_Cur_Box).X1 or else P_Reg.Rects (P_Prev_Box).X2 /= P_Reg.Rects (P_Cur_Box).X2 then ----The bands don't line up so they can't be coalesced. return Cur_Start; end if; P_Prev_Box := P_Prev_Box + 1; P_Cur_Box := P_Cur_Box + 1; Prev_Num_Rects := Prev_Num_Rects - 1; if Prev_Num_Rects = 0 then exit; end if; end loop; P_Reg.Num_Rects := P_Reg.Num_Rects - Cur_Num_Rects; P_Cur_Box := P_Cur_Box - Cur_Num_Rects; P_Prev_Box := P_Prev_Box - Cur_Num_Rects; ----The bands may be merged, so set the bottom y of each box in the previous -- band to that of the corresponding box in the current band. loop P_Reg.Rects (P_Prev_Box).Y2 := P_Reg.Rects (P_Cur_Box).Y2; P_Prev_Box := P_Prev_Box + 1; P_Cur_Box := P_Cur_Box + 1; Cur_Num_Rects := Cur_Num_Rects - 1; if Cur_Num_Rects = 0 then exit; end if; end loop; ----If only one band was added to the region, we have to backup cur_Start to -- the start of the previous band. -- -- If more than one band was added to the region, copy the other bands down. -- The assumption here is that the other bands came from the same region as -- the current one and no further coalescing can be done on them since it's -- all been done already... Cur_Start is already in the right place. if P_Cur_Box = P_Reg_End then Cur_Start := Prev_Start; else loop P_Reg.Rects (P_Prev_Box) := P_Reg.Rects (P_Cur_Box); P_Prev_Box := P_Prev_Box + 1; P_Cur_Box := P_Cur_Box + 1; if P_Cur_Box = P_Reg_End then exit; end if; end loop; end if; end if; end if; return Cur_Start; end Mi_Coalesce; --\f generic Overlap_Func_Non_Null : in Boolean; Non_Overlap1_Func_Non_Null : in Boolean; Non_Overlap2_Func_Non_Null : in Boolean; with procedure Overlap_Func (New_Reg : X_Region; Reg1 : X_Box_List; Reg1_Begin : S_Natural; Reg1_End : S_Natural; Reg2 : X_Box_List; Reg2_Begin : S_Natural; Reg2_End : S_Natural; Y_Top : S_Short; Y_Bot : S_Short); -- called for overlapping bands with procedure Non_Overlap1_Func (New_Reg : X_Region; Reg1 : X_Box_List; Reg1_Begin : S_Natural; Reg1_End : S_Natural; Top : S_Short; Bot : S_Short); -- called for non-overlapping bands in region 1 with procedure Non_Overlap2_Func (New_Reg : X_Region; Reg2 : X_Box_List; Reg2_Begin : S_Natural; Reg2_End : S_Natural; Top : S_Short; Bot : S_Short); -- called for non-overlapping bands in region 2 procedure Mi_Region_Op (New_Reg : X_Region; -- Place to store result Reg1 : X_Region; -- First region in operation Reg2 : X_Region); -- 2d region in operation procedure Mi_Region_Op (New_Reg : X_Region; -- Place to store result Reg1 : X_Region; -- 1st region in operation Reg2 : X_Region) is -- 2nd region in operation ------------------------------------------------------------------------- -- mi_Region_Op -- -- Apply an operation to two regions. Called by mi_Union, mi_Inverse, -- mi_Subtract, mi_Intersect... -- -- Results: -- None. -- -- Side Effects: -- The new region is overwritten. -- -- Notes: -- The idea behind this funct is to view the two regions as sets. Together -- they cover a rectangle of area that this funct divides into horizontal -- bands where points are covered only by one region or by both. For the -- first case, the non_Overlap_Func is called with each the band and the -- band's upper and lower extents. For the second, the Overlap_Func is -- called to process the entire band. It is responsible for clipping the -- rectangles in the band, though this function provides the boundaries. -- At the end of each band, the new region is coalesced, if possible, to -- reduce the number of rectangles in the region. -- ------------------------------------------------------------------------- Rects1 : X_Box_List := Reg1.Rects; Rects2 : X_Box_List := Reg2.Rects; R1 : S_Natural; -- Pointer into 1st region R2 : S_Natural; -- Pointer into 2nd region R1_End : S_Natural; -- End of 1st region R2_End : S_Natural; -- End of 2nd region Y_Bot : S_Short; -- Bottom of intersection Y_Top : S_Short; -- Top of intersection Old_Rects : X_Box_List; -- Old rects for new_Reg Prev_Band : S_Natural; -- Index of start of previous band -- in new_Reg Cur_Band : S_Natural; -- Index of start of current band -- in new_Reg R1_Band_End : S_Natural; -- End of current band in r1 R2_Band_End : S_Natural; -- End of current band in r2 Top : S_Short; -- Top of non-overlapping band Bot : S_Short; -- Bottom of non-overlapping band begin ----Initialization: -- Set r1, r2, r1_End and r2_End appropriately, preserve the important parts -- of the destination region until the end in case it's one of the two source -- regions, then mark the "new" region empty, allocating another array of -- rectangles for it to use. R1 := 1; R2 := 1; R1_End := Reg1.Num_Rects + 1; R2_End := Reg2.Num_Rects + 1; Old_Rects := New_Reg.Rects; Empty_Region (New_Reg); ----Allocate a reasonable number of rectangles for the new region. The idea is -- to allocate enough so the individual functions don't need to reallocate and -- copy the array, which is time consuming, yet we don't have to worry about -- using too much memory. I hope to be able to nuke the X_realloc() at the end -- of this funct eventually. New_Reg.Size := Max (Reg1.Num_Rects, Reg2.Num_Rects) * 2; New_Reg.Rects := new X_Box_Array (0 .. New_Reg.Size - 1); ----Initialize y_bot and y_top. In the upcoming loop, y_bot and y_top serve -- different functions depending on whether the band being handled is an -- overlapping or non-overlapping band. -- In the case of a non-overlapping band (only one of the regions has points -- in the band), y_bot is the bottom of the most recent intersection and thus -- clips the top of the rectangles in that band. y_top is the top of the -- next intersection between the two regions and serves to clip the bottom of -- the rectangles in the current band. -- For an overlapping band (where the two regions intersect), y_top clips the -- top of the rectangles of both regions and y_bot clips the bottoms. if Reg1.Extents.Y1 < Reg2.Extents.Y1 then Y_Bot := Reg1.Extents.Y1; else Y_Bot := Reg2.Extents.Y1; end if; ----Prev_Band serves to mark the start of the previous band so rectangles can -- be coalesced into larger rectangles. qv. mi_Coalesce, above. In the -- beginning, there is no previous band, so prev_Band = cur_Band (cur_Band is -- set later on, of course, but the first band will always start at index 1). -- prev_Band and cur_Band must be indices because of the possible expansion, -- and resultant moving, of the new region's array of rectangles. Prev_Band := 1; loop Cur_Band := New_Reg.Num_Rects; ----This algorithm proceeds one source-band (as opposed to a destination band, -- which is determined by where the two regions intersect) at a time. -- r1_Band_End and r2_Band_End serve to mark the rectangle after the last one -- in the current band for their respective regions. R1_Band_End := R1; while R1_Band_End < R1_End and then Rects1 (R1_Band_End).Y1 = Rects1 (R1).Y1 loop R1_Band_End := R1_Band_End + 1; end loop; R2_Band_End := R2; while R2_Band_End < R2_End and then Rects2 (R2_Band_End).Y1 = Rects2 (R2).Y1 loop R2_Band_End := R2_Band_End + 1; end loop; ----First handle the band that doesn't intersect, if any. -- Note that attention is restricted to one band in the non-intersecting region -- at once, so if a region has N bands between the current the_position and -- the next place it overlaps the other, this entire loop will be passed -- through N times. if Rects1 (R1).Y1 < Rects2 (R2).Y1 then Top := Max (Rects1 (R1).Y1, Y_Bot); Bot := Min (Rects1 (R1).Y2, Rects2 (R2).Y1); if Non_Overlap1_Func_Non_Null and then Top /= Bot then Non_Overlap1_Func (New_Reg, Rects1, R1, R1_Band_End, Top, Bot); end if; Y_Top := Rects2 (R2).Y1; elsif Rects2 (R2).Y1 < Rects1 (R1).Y1 then Top := Max (Rects2 (R2).Y1, Y_Bot); Bot := Min (Rects2 (R2).Y2, Rects1 (R1).Y1); if Non_Overlap2_Func_Non_Null and then Top /= Bot then Non_Overlap2_Func (New_Reg, Rects2, R2, R2_Band_End, Top, Bot); end if; Y_Top := Rects1 (R1).Y1; else Y_Top := Rects1 (R1).Y1; end if; ----If any rectangles got added to the region, try and coalesce them with -- rectangles from the previous band. Note we could just do this test in -- mi_Coalesce, but some machines incur a not inconsiderable cost for -- function calls, so... if New_Reg.Num_Rects /= Cur_Band then Prev_Band := Mi_Coalesce (New_Reg, Prev_Band, Cur_Band + 1); end if; ----Now see if we've hit an intersecting band. The two bands only intersect -- if y_bot > y_top. Y_Bot := Min (Rects1 (R1).Y2, Rects2 (R2).Y2); Cur_Band := New_Reg.Num_Rects; if Overlap_Func_Non_Null and then Y_Bot > Y_Top then Overlap_Func (New_Reg, Rects1, R1, R1_Band_End, Rects2, R2, R2_Band_End, Y_Top, Y_Bot); end if; if New_Reg.Num_Rects /= Cur_Band then Prev_Band := Mi_Coalesce (New_Reg, Prev_Band, Cur_Band + 1); end if; ----If we've finished with a band (y2 = y_bot) we skip forward in the region to -- the next band. if Rects1 (R1).Y2 = Y_Bot then R1 := R1_Band_End; end if; if Rects2 (R2).Y2 = Y_Bot then R2 := R2_Band_End; end if; if R1 = R1_End or else R2 = R2_End then exit; end if; end loop; ----Deal with whichever region still has rectangles left. Cur_Band := New_Reg.Num_Rects; if R1 < R1_End and then Non_Overlap1_Func_Non_Null then loop R1_Band_End := R1 + 1; while R1_Band_End < R1_End and then Rects1 (R1_Band_End).Y1 = Rects1 (R1).Y1 loop R1_Band_End := R1_Band_End + 1; end loop; Non_Overlap1_Func (New_Reg, Rects1, R1, R1_Band_End, Max (Rects1 (R1).Y1, Y_Bot), Rects1 (R1).Y2); R1 := R1_Band_End; if R1 >= R1_End then exit; end if; end loop; elsif R2 < R2_End and then Non_Overlap2_Func_Non_Null then loop R2_Band_End := R2 + 1; while R2_Band_End < R2_End and then Rects2 (R2_Band_End).Y1 = Rects2 (R2).Y1 loop R2_Band_End := R2_Band_End + 1; end loop; Non_Overlap2_Func (New_Reg, Rects2, R2, R2_Band_End, Max (Rects2 (R2).Y1, Y_Bot), Rects2 (R2).Y2); R2 := R2_Band_End; if R2 >= R2_End then exit; end if; end loop; end if; if New_Reg.Num_Rects /= Cur_Band then Prev_Band := Mi_Coalesce (New_Reg, Prev_Band, Cur_Band + 1); end if; ----A bit of cleanup. To keep regions from growing without bound, we shrink -- the array of rectangles to match the new number of rectangles in the region. -- This never goes to 0, however... -- -- Only do this stuff if the number of rectangles allocated is more than twice -- the number of rectangles in the region (a simple optimization...). if New_Reg.Num_Rects < New_Reg.Size / 2 then if Region_Not_Empty (New_Reg) then New_Reg.Size := New_Reg.Num_Rects; declare Newa : X_Box_List := new X_Box_Array (1 .. New_Reg.Size); begin Newa.all := New_Reg.Rects (Newa'Range); Free_X_Box_List (New_Reg.Rects); New_Reg.Rects := Newa; end; ----No point in doing the extra work involved in a copy if the region is empty. else New_Reg.Size := 1; Free_X_Box_List (New_Reg.Rects); New_Reg.Rects := new X_Box_Array (1 .. 1); end if; end if; Free_X_Box_List (Old_Rects); exception when others => if New_Reg.Rects = Old_Rects then Old_Rects := null; end if; Free_X_Box_List (New_Reg.Rects); Free_X_Box_List (Old_Rects); raise; end Mi_Region_Op; --\f -- procedure Null_Overlap_Func (New_Reg : X_Region; -- Reg1 : X_Box_List; -- Reg1_Begin : S_Natural; -- Reg1_End : S_Natural; -- Reg2 : X_Box_List; -- Reg2_Begin : S_Natural; -- Reg2_End : S_Natural; -- Y_Top : S_Short; -- Y_Bot : S_Short) is -- -- called for overlapping bands -- begin -- null; -- end Null_Overlap_Func; -- procedure Null_Non_Overlap1_Func (New_Reg : X_Region; Reg1 : X_Box_List; Reg1_Begin : S_Natural; Reg1_End : S_Natural; Top : S_Short; Bot : S_Short) is -- called for non-overlapping bands in region 1 begin null; end Null_Non_Overlap1_Func; procedure Null_Non_Overlap2_Func (New_Reg : X_Region; Reg2 : X_Box_List; Reg2_Begin : S_Natural; Reg2_End : S_Natural; Top : S_Short; Bot : S_Short) is -- called for non-overlapping bands in region 2 begin null; end Null_Non_Overlap2_Func; --\f --====================================================================== -- Region Intersection --==================================================================== procedure Mi_Intersect_O (P_Reg : X_Region; Rects1 : X_Box_List; R1 : S_Natural; R1_End : S_Natural; Rects2 : X_Box_List; R2 : S_Natural; R2_End : S_Natural; Y1 : S_Short; Y2 : S_Short) is ------------------------------------------------------------------------- -- mi_Intersect_O -- -- Handle an overlapping band for mi_Intersect. -- -- Results: -- None. -- -- Side Effects: -- Rectangles may be added to the region. -- ------------------------------------------------------------------------- X1 : S_Short; X2 : S_Short; R1_I : S_Natural := 1; R2_I : S_Natural := 1; I : S_Natural := P_Reg.Num_Rects; begin while R1_I < R1_End and then R2_I < R2_End loop X1 := Max (Rects1 (R1_I).X1, Rects2 (R2_I).X1); X2 := Min (Rects1 (R1_I).X2, Rects2 (R2_I).X2); ----If there's any overlap between the two rectangles, add that overlap to the -- new region. There's no need to check for subsumption because the only way -- such a need could arise is if some region has two rectangles right next to -- each other. Since that should never happen... if X1 < X2 then --/ if DEBUG then if not (Y1 < Y2) then raise X_Library_Confusion; end if; --/ end if; P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).X1 := X1; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).X2 := X2; P_Reg.Rects (I).Y2 := Y2; end if; ----Need to advance the pointers. Shift the one that extends to the right the -- least, since the other still has a chance to overlap with that region's -- next rectangle, if you see what I mean. if Rects1 (R1_I).X2 < Rects2 (R2_I).X2 then R1_I := R1_I + 1; elsif Rects2 (R2_I).X2 < Rects1 (R1_I).X2 then R2_I := R2_I + 1; else R1_I := R1_I + 1; R2_I := R2_I + 1; end if; end loop; end Mi_Intersect_O; --\f procedure Mi_Region_Op_Intersect is new Mi_Region_Op (True, False, False, Mi_Intersect_O, Null_Non_Overlap1_Func, Null_Non_Overlap2_Func); --\f procedure X_Intersect_Region (Region1 : X_Region; Region2 : X_Region; -- source regions New_Region : X_Region) is -- destination Region begin ----Check for trivial reject if Region1.Num_Rects = 0 or else Region2.Num_Rects = 0 or else not Extent_Check (Region1.Extents, Region2.Extents) then New_Region.Num_Rects := 0; else Mi_Region_Op_Intersect (New_Region, Region1, Region2); end if; ----Can't alter New_Region's extents before we call mi_Region_Op because it might -- be one of the source regions and mi_Region_Op depends on the extents of -- those regions being the same. Besides, this way there's no checking against -- rectangles that will be nuked due to coalescing, so we have to examine -- fewer rectangles. Mi_Set_Extents (New_Region); end X_Intersect_Region; --\f procedure Mi_Region_Copy (Dst_Rgn : X_Region; Rgn : X_Region) is begin if Dst_Rgn /= Rgn -- don't want to copy to itself then if Dst_Rgn.Size < Rgn.Num_Rects then if Dst_Rgn.Rects /= null then Free_X_Box_List (Dst_Rgn.Rects); end if; Dst_Rgn.Rects := new X_Box_Array (1 .. Rgn.Num_Rects); Dst_Rgn.Size := Rgn.Num_Rects; end if; Dst_Rgn.Num_Rects := Rgn.Num_Rects; Dst_Rgn.Extents := Rgn.Extents; Dst_Rgn.Rects (1 .. Rgn.Num_Rects) := Rgn.Rects (1 .. Rgn.Num_Rects); end if; end Mi_Region_Copy; --\f -- procedure Combine_Regs -- (New_Reg : X_Region_internal; -- Reg1 : X_Region_internal; -- Reg2 : X_Region_internal) is -- ------------------------------------------------------------------------------ -- -- if one region is above or below the other. -- ------------------------------------------------------------------------------ -- Temp_Reg : X_Region_internal; -- Rects : s_natural; -- Rects1 : s_natural; -- Rects2 : s_natural; -- Total : s_natural; -- begin -- -- Rects1 := 1; -- Rects2 := 1; -- -- Total := Reg1.Num_Rects + Reg2.Num_Rects; -- Temp_Reg := X_Create_Region; -- Temp_Reg.Size := Total; -- -- ----Region 1 is below region 2 -- -- if Reg1.Extents.Y1 > Reg2.Extents.Y1 then -- Mi_Region_Copy (Temp_Reg, Reg2); -- Rects := Temp_Reg.Num_Rects + 1; -- for I in 0 .. Total - Temp_Reg.Num_Rects - 1 loop -- Temp_Reg.Rects (Rects + I) := Reg1.Rects (Rects1 + I); -- end loop; -- else -- Mi_Region_Copy (Temp_Reg, Reg1); -- Rects := Temp_Reg.Num_Rects + 1; -- for I in 0 .. Total - Temp_Reg.Num_Rects - 1 loop -- Temp_Reg.Rects (Rects + 1) := Reg2.Rects (Rects2 + 1); -- end loop; -- end if; -- -- Temp_Reg.Extents := Reg1.Extents; -- Temp_Reg.Num_Rects := Total; -- Extents (Reg2.Extents, Temp_Reg); -- Mi_Region_Copy (New_Reg, Temp_Reg); -- Free_X_Region_internal (Temp_Reg); -- -- end Combine_Regs; -- #ifdef notdef -- -- -- -- Quick_Check checks to see if it does not have to go through all the -- -- the ugly code for the region call. It returns 1 if it did all -- -- the work for Union, otherwise 0 - still work to be done. -- -- -- -- static int -- Quick_Check(new_Reg, reg1, reg2) -- Region new_Reg, reg1, reg2; -- { -- -- -- if unioning with itself or no rects to union with -- if ( (reg1 = reg2) or else (!(reg1.num_Rects)) ) -- { -- mi_Region_Copy(new_Reg, reg2); -- return(TRUE); -- } -- -- -- if nothing to union -- if (!(reg2.num_Rects)) -- { -- mi_Region_Copy(new_Reg, reg1); -- return(TRUE); -- } -- -- -- could put an extent check to see if add above or below -- -- if ((reg1.extents.y1 >= reg2.extents.y2) or else -- (reg2.extents.y1 >= reg1.extents.y2) ) -- { -- combine_Regs(new_Reg, reg1, reg2); -- return(TRUE); -- } -- return(FALSE); -- } -- #endif -- -- #ifdef notdef -- -- Top_Rects(rects, reg1, reg2) -- -- N.B. We now assume that reg1 and reg2 intersect. Therefore we are -- -- NOT checking in the two while loops for stepping off the end of the -- -- region. -- -- -- -- static int -- Top_Rects(new_Reg, rects, reg1, reg2, First_Rect) -- Region new_Reg; -- box_ptr rects; -- Region reg1; -- Region reg2; -- box_ptr First_Rect; -- { -- box_ptr temp_Rects; -- -- -- need to add some rects from region 1 -- if (reg1.extents.y1 < reg2.extents.y1) -- { -- temp_Rects := reg1.rects; -- while(temp_Rects.y1 < reg2.extents.y1) -- { -- MEM_CHECK(new_Reg, rects, First_Rect); -- ADD_RECT_NOX(new_Reg,rects, temp_Rects.x1, temp_Rects.y1, -- temp_Rects.x2, MIN(temp_Rects.y2, reg2.extents.y1)); -- temp_Rects++; -- } -- } -- -- need to add some rects from region 2 -- if (reg2.extents.y1 < reg1.extents.y1) -- { -- temp_Rects := reg2.rects; -- while (temp_Rects.y1 < reg1.extents.y1) -- { -- MEM_CHECK(new_Reg, rects, First_Rect); -- ADD_RECT_NOX(new_Reg, rects, temp_Rects.x1,temp_Rects.y1, -- temp_Rects.x2, MIN(temp_Rects.y2, reg1.extents.y1)); -- temp_Rects++; -- } -- } -- return(1); -- } -- #endif --\f --====================================================================== -- Region Union --==================================================================== procedure Mi_Union_Non_O (P_Reg : X_Region; Rects : X_Box_List; R : S_Natural; R_End : S_Natural; Y1 : S_Short; Y2 : S_Short) is ------------------------------------------------------------------------- -- mi_Union_Non_O -- -- Handle a non-overlapping band for the union operation. Just -- Adds the rectangles into the region. Doesn't have to check for -- subsumption or anything. -- -- Results: -- None. -- -- Side Effects: -- p_Reg.num_Rects is incremented and the final rectangles overwritten -- with the rectangles we're passed. -- ------------------------------------------------------------------------- I : S_Natural; begin --/ if DEBUG then if not (Y1 < Y2) then raise X_Library_Confusion; end if; --/ end if; for Ri in R .. R_End - 1 loop --/ if DEBUG then if not (Rects (Ri).X1 < Rects (Ri).X2) then raise X_Library_Confusion; end if; --/ end if; P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).X1 := Rects (Ri).X1; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).X2 := Rects (Ri).X2; P_Reg.Rects (I).Y2 := Y2; end loop; end Mi_Union_Non_O; --\f procedure Mi_Union_O (P_Reg : X_Region; Rects1 : X_Box_List; R1 : S_Natural; R1_End : S_Natural; Rects2 : X_Box_List; R2 : S_Natural; R2_End : S_Natural; Y1 : S_Short; Y2 : S_Short) is ------------------------------------------------------------------------- -- mi_Union_O -- -- Handle an overlapping band for the union operation. Picks the -- left-most rectangle each time and merges it into the region. -- -- Results: -- None. -- -- Side Effects: -- Rectangles are overwritten in p_Reg.rects and p_Reg.num_Rects will -- be changed. -- ------------------------------------------------------------------------- I : S_Natural; R1_I : S_Natural := R1; R2_I : S_Natural := R2; procedure Merge_Rect (R : X_Box_List; Ri : in out S_Natural) is begin if I /= 0 and then P_Reg.Rects (I - 1).Y1 = Y1 and then P_Reg.Rects (I - 1).Y2 = Y2 and then P_Reg.Rects (I - 1).X2 >= R (Ri).X1 then if P_Reg.Rects (I - 1).X2 < R (Ri).X2 then P_Reg.Rects (I - 1).X2 := R (R1).X2; --/ if DEBUG then if not (P_Reg.Rects (I - 1).X1 < P_Reg.Rects (I - 1).X2) then raise X_Library_Confusion; end if; --/ end if; end if; else P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).Y2 := Y2; P_Reg.Rects (I).X1 := R (Ri).X1; P_Reg.Rects (I).X2 := R (Ri).X2; end if; Ri := Ri + 1; end Merge_Rect; begin I := P_Reg.Num_Rects; --/ if DEBUG then if not (Y1 < Y2) then raise X_Library_Confusion; end if; --/ end if; while R1_I < R1_End and then R2_I < R2_End loop if Rects1 (R1_I).X1 < Rects2 (R2_I).X1 then Merge_Rect (Rects1, R1_I); else Merge_Rect (Rects2, R2_I); end if; end loop; if R1_I < R1_End then loop Merge_Rect (Rects1, R1_I); if R1 = R1_End then exit; end if; end loop; else while R2 < R2_End loop Merge_Rect (Rects2, R2_I); end loop; end if; end Mi_Union_O; --\f procedure Mi_Region_Op_Union is new Mi_Region_Op (True, True, True, Mi_Union_O, Mi_Union_Non_O, Mi_Union_Non_O); --\f procedure X_Union_Region (Region1 : X_Region; Region2 : X_Region; -- source regions New_Region : X_Region) is -- destination regions begin ----Checks all the simple cases ----Region 1 and 2 are the same or region 1 is empty if Region1 = Region2 or else Region1.Num_Rects = 0 then if New_Region /= Region2 then Mi_Region_Copy (New_Region, Region2); end if; return; end if; ----If nothing to union (region 2 empty) if Region2.Num_Rects = 0 then if New_Region /= Region1 then Mi_Region_Copy (New_Region, Region1); end if; return; end if; ----Region 1 completely subsumes region 2 if Region1.Num_Rects = 1 and then Region1.Extents.X1 <= Region2.Extents.X1 and then Region1.Extents.Y1 <= Region2.Extents.Y1 and then Region1.Extents.X2 >= Region2.Extents.X2 and then Region1.Extents.Y2 >= Region2.Extents.Y2 then if New_Region /= Region1 then Mi_Region_Copy (New_Region, Region1); end if; return; end if; ----Region 2 completely subsumes region 1 if Region2.Num_Rects = 1 and then Region2.Extents.X1 <= Region1.Extents.X1 and then Region2.Extents.Y1 <= Region1.Extents.Y1 and then Region2.Extents.X2 >= Region1.Extents.X2 and then Region2.Extents.Y2 >= Region1.Extents.Y2 then if New_Region /= Region2 then Mi_Region_Copy (New_Region, Region2); end if; return; end if; Mi_Region_Op_Union (New_Region, Region1, Region2); New_Region.Extents.X1 := Min (Region1.Extents.X1, Region2.Extents.X1); New_Region.Extents.Y1 := Min (Region1.Extents.Y1, Region2.Extents.Y1); New_Region.Extents.X2 := Max (Region1.Extents.X2, Region2.Extents.X2); New_Region.Extents.Y2 := Max (Region1.Extents.Y2, Region2.Extents.Y2); end X_Union_Region; --\f --====================================================================== -- Region Subtraction --==================================================================== procedure Mi_Subtract_Non_O1 (P_Reg : X_Region; Rects : X_Box_List; R : S_Natural; R_End : S_Natural; Y1 : S_Short; Y2 : S_Short) is ------------------------------------------------------------------------- -- mi_Subtract_Non_O -- -- Deal with non-overlapping band for subtraction. Any parts from -- region 2 we discard. Anything from region 1 we add to the region. -- -- Results: -- None. -- -- Side Effects: -- p_Reg may be affected. -- ------------------------------------------------------------------------- I : S_Natural; begin --/ if DEBUG then if not (Y1 < Y2) then raise X_Library_Confusion; end if; --/ end if; for Ri in R .. R_End - 1 loop --/ if DEBUG then if not (Rects (Ri).X1 < Rects (Ri).X2) then raise X_Library_Confusion; end if; --/ end if; P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).X1 := Rects (Ri).X1; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).X2 := Rects (Ri).X2; P_Reg.Rects (I).Y2 := Y2; end loop; end Mi_Subtract_Non_O1; --\f procedure Mi_Subtract_O (P_Reg : X_Region; Rects1 : X_Box_List; R1 : S_Natural; R1_End : S_Natural; Rects2 : X_Box_List; R2 : S_Natural; R2_End : S_Natural; Y1 : S_Short; Y2 : S_Short) is ------------------------------------------------------------------------- -- mi_Subtract_O -- -- Overlapping band subtraction. x1 is the left-most point not yet -- checked. -- -- Results: -- None. -- -- Side Effects: -- p_Reg may have rectangles added to it. -- ------------------------------------------------------------------------- X1 : S_Short; R1_I : S_Natural := R1; R2_I : S_Natural := R2; I : S_Natural; begin X1 := Rects1 (R1_I).X1; --/ if DEBUG then if not (Y1 < Y2) then raise X_Library_Confusion; end if; --/ end if; while R1_I < R1_End and then R2_I < R2_End loop ----If subtrahend missed the boat: go to next subtrahend. if Rects2 (R2_I).X2 <= X1 then R2_I := R2_I + 1; ----If subtrahend preceeds minuend: nuke left edge of minuend. elsif Rects2 (R2_I).X1 <= X1 then X1 := Rects2 (R2_I).X2; ----If minuend completely covered: advance to next minuend and reset left fence -- to edge of new minuend. if X1 >= Rects1 (R1_I).X2 then R1_I := R1_I + 1; X1 := Rects1 (R1_I).X1; ----Else subtrahend now used up since it doesn't extend beyond minuend else R2_I := R2_I + 1; end if; ----Left part of subtrahend covers part of minuend: add uncovered part of -- minuend to region and skip to next subtrahend. elsif Rects2 (R2_I).X1 < Rects1 (R1_I).X2 then --/ if DEBUG then if not (X1 < Rects2 (R2_I).X1) then raise X_Library_Confusion; end if; --/ end if; P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).X1 := X1; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).X2 := Rects2 (R2_I).X1; P_Reg.Rects (I).Y2 := Y2; X1 := Rects2 (R2_I).X2; ---If minuend used up: advance to new... if X1 >= Rects1 (R2_I).X2 then R1_I := R1_I + 1; X1 := Rects1 (R1_I).X1; ----Else subtrahend used up else R2_I := R2_I + 1; end if; ----Minuend used up: add any remaining piece before advancing. else if Rects1 (R1_I).X2 > X1 then P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).X1 := X1; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).X2 := Rects1 (R1_I).X2; P_Reg.Rects (I).Y2 := Y2; end if; R1_I := R1_I + 1; X1 := Rects1 (R1_I).X1; end if; end loop; ----Add remaining minuend rectangles to region. while R1_I < R1_End loop --/ if DEBUG then if not (X1 < Rects1 (R1_I).X2) then raise X_Library_Confusion; end if; --/ end if; P_Reg.Num_Rects := P_Reg.Num_Rects + 1; I := P_Reg.Num_Rects; P_Reg.Rects (I).X1 := X1; P_Reg.Rects (I).Y1 := Y1; P_Reg.Rects (I).X2 := Rects1 (R1_I).X2; P_Reg.Rects (I).Y2 := Y2; --/ if DEBUG then if not (P_Reg.Num_Rects <= P_Reg.Size) then raise X_Library_Confusion; end if; --/ end if; R1_I := R1_I + 1; if R1_I < R1_End then X1 := Rects1 (R1_I).X1; end if; end loop; end Mi_Subtract_O; --\f procedure Mi_Region_Op_Subtract is new Mi_Region_Op (True, True, False, Mi_Subtract_O, Mi_Subtract_Non_O1, Null_Non_Overlap2_Func); --\f procedure X_Subtract_Region (Region1 : X_Region; Region2 : X_Region; New_Region : X_Region) is ------------------------------------------------------------------------- -- mi_Subtract -- -- Subtract Region2 from Region1 and leave the result in New_Region. -- S stands for subtrahend, M for minuend and D for difference. -- -- Results: -- TRUE. -- -- Side Effects: -- New_Region is overwritten. -- ------------------------------------------------------------------------- begin ----Check for trivial reject. if Region1.Num_Rects = 0 or else Region2.Num_Rects = 0 or else not Extent_Check (Region1.Extents, Region2.Extents) then Mi_Region_Copy (New_Region, Region1); return; end if; Mi_Region_Op_Subtract (New_Region, Region1, Region2); ----Can't alter New_Region's extents before we call mi_Region_Op because it might -- be one of the source regions and mi_Region_Op depends on the extents of -- those regions being unaltered. Besides, this way there's no checking -- against rectangles that will be nuked due to coalescing, so we have to -- examine fewer rectangles. Mi_Set_Extents (New_Region); end X_Subtract_Region; --\f procedure X_Xor_Region (Region1 : X_Region; Region2 : X_Region; New_Region : X_Region) is Tra : X_Region; Trb : X_Region; begin Tra := new X_Region_Rec; Trb := new X_Region_Rec; X_Subtract_Region (Region1, Region2, Tra); X_Subtract_Region (Region2, Region1, Trb); X_Union_Region (Tra, Trb, New_Region); Free_X_Region (Tra); Free_X_Region (Trb); exception when others => Free_X_Region (Tra); Free_X_Region (Trb); raise; end X_Xor_Region; --\f function X_Empty_Region (Region : X_Region) return Boolean is ------------------------------------------------------------------------------ -- Check to see if the region is empty. Assumes a region is passed -- as a parameter ------------------------------------------------------------------------------ begin return Region.Num_Rects = 0; end X_Empty_Region; --\f function X_Equal_Region (Region1 : X_Region; Region2 : X_Region) return Boolean is ------------------------------------------------------------------------------ -- Check to see if two regions are equal ------------------------------------------------------------------------------ begin if Region1.Num_Rects /= Region2.Num_Rects then return False; elsif Region1.Num_Rects = 0 then return True; elsif Region1.Extents /= Region2.Extents then return False; end if; for I in 1 .. Region1.Num_Rects loop if Region1.Rects (I) /= Region2.Rects (I) then return False; end if; end loop; return True; end X_Equal_Region; --\f function X_Point_In_Region (Region : X_Region; X : S_Short; Y : S_Short) return Boolean is begin if Region.Num_Rects = 0 then return False; end if; if not In_Box (Region.Extents, X, Y) then return False; end if; for I in 1 .. Region.Num_Rects loop if In_Box (Region.Rects (I), X, Y) then return True; end if; end loop; return False; end X_Point_In_Region; --\f function X_Rect_In_Region (Region : X_Region; X : S_Short; Y : S_Short; Width : U_Short; Height : U_Short) return X_Rect_In_Region_Returns is Prect : X_Box_Rec; Part_In : Boolean; Part_Out : Boolean; Lx : S_Short := X; Ly : S_Short := Y; begin Prect.X1 := Lx; Prect.Y1 := Ly; Prect.X2 := S_Short (Width) + Lx; Prect.Y2 := S_Short (Height) + Ly; ----This is (just) a useful optimization. if Region.Num_Rects = 0 or else not Extent_Check (Region.Extents, Prect) then return Rectangle_Out; end if; Part_Out := False; Part_In := False; ----Can stop when both part_Out and part_In are TRUE, or we reach prect.y2. for Pbox in 1 .. Region.Num_Rects loop declare Box : X_Box_Rec renames Region.Rects (Pbox); begin if Box.Y2 <= Ly then -- getting up to speed or skipping remainder of band goto Continue_Loop; end if; if Box.Y1 > Ly then Part_Out := True; -- missed part of rectangle above if Part_In or else Box.Y1 >= Prect.Y2 then exit; end if; Ly := Box.Y1; -- x guaranteed to be = prect.x1 end if; if Box.X2 <= Lx then goto Continue_Loop; -- not far enough over yet end if; if Box.X1 > Lx then Part_Out := True; -- missed part of rectangle to left if Part_In then exit; end if; end if; if Box.X1 < Prect.X2 then Part_In := True; -- definitely overlap if Part_Out then exit; end if; end if; if Box.X2 >= Prect.X2 then Ly := Box.Y2; -- finished with this band if Ly >= Prect.Y2 then exit; end if; Lx := Prect.X1; -- reset x out to left again else ----Because boxes in a band are maximal width, if the first box to overlap the -- rectangle doesn't completely cover it in that band, the rectangle must be -- partially out, since some of it will be uncovered in that band. part_In -- will have been set true by now... exit; end if; end; <<Continue_Loop>> null; end loop; if Part_In then if Ly < Prect.Y2 then return Rectangle_Part; else return Rectangle_In; end if; else return Rectangle_Out; end if; end X_Rect_In_Region; end Xlbp_Region;