Vector graphics floodfill algorithms lazarus / 39-Graphics ---------------------------------------------------------------------- zamtmn · 2022-05-21 10:49 Hi All! Are there algorithms for vector filling of the area? I have several contours of a non-convex area (possibly with holes) and a vector description of the pattern to fill (a set of lines) Or maybe there are ready-made libraries with open source implementing this? ---------------------------------------------------------------------- Roland57 · 2022-05-21 12:41 Hi. Not sure that it be what you look for, but there is Clipper. https://github.com/AngusJohnson/Clipper2 ---------------------------------------------------------------------- zamtmn · 2022-05-21 15:12 Thanks, but this is not exactly what I need, this finding offset contours. Quote from: Roland57 on May 21, 2022, 02:41:47 pmHi. Not sure that it be what you look for, but there is Clipper. https://github.com/AngusJohnson/Clipper2 ---------------------------------------------------------------------- VTwin · 2022-05-21 16:11 Maybe this: https://wiki.lazarus.freepascal.org/Developing_with_Graphics#Self- overlapping_polygons Perhaps not, only color fills. ---------------------------------------------------------------------- zamtmn · 2022-05-21 16:40 I have the contours of the area, by making a triangulation I have a set of triangles that make up the area. There are no problems with this. next, for example, I want to shade the area with oblique lines in increments of 10, starting from the point 0,0. I need to find out the coordinates of all the dinias falling inside the area. ---------------------------------------------------------------------- Seenkao · 2022-05-21 19:39 Привет. Одним из самых действенных средств будет обычный перебор. Описано в этой игре. Вероятнее всего, можно немного ускорить процедуру, записывая все пиксели отдельно, а потом просто полученные пиксели сразу залить определённым цветом. Сложность в том, что если надо заливать каким-то узором, надо дублировать изображение и все пиксели отмечать на втором изображении (для сплошной заливки не обязательно дублировать изображение, можно просто координаты всех пикселей взять) и в соответствии с отмеченными пикселями заполнять узором то, что было отмечено. Точнее в любом случае, если ты хочешь узором заливать, то надо знать координаты всех точек, которые ты хочешь залить и сличить их с координатами полностью залитой картинки (или какой-то области). Можешь заглянуть ещё в LazPaint, как там это реализовывалось. ---------------------------------------------- Google translate: Hello. One of the most effective means will be the usual enumeration. Described in this game. Most likely, you can speed up the procedure a little by writing all the pixels separately, and then simply fill the resulting pixels with a certain color. The difficulty is that if you need to fill with some kind of pattern, you need to duplicate the image and mark all the pixels on the second image (for a solid fill, you don’t have to duplicate the image, you can just take the coordinates of all the pixels) and, in accordance with the marked pixels, fill with a pattern that was noted. More precisely, in any case, if you want to fill with a pattern, then you need to know the coordinates of all the points that you want to fill and compare them with the coordinates of a completely filled image (or some area). You can also look at LazPaint, how it was implemented there. ---------------------------------------------------------------------- circular · 2022-05-21 21:31 What you’re looking for is a polygon filling. You can do that for example with BGRABitmap by defining an array of TPointF with EmptyPointF to separate the main part and each hole. This can be filled using FillPolyAntialias or PolygonAntialias. You need to prepare a bitmap containing the pattern and supply it as fill parameter instead of a single color. You can look at CreateBrushTexture function for example. ---------------------------------------------------------------------- BobDog · 2022-05-21 22:44 Flood fill with scan lines. Windows. Code: Pascal [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i useswindows;{$RANGECHECKS ON}type pt =objectx,y:single;end; type Arr2D = array of array of single; Arrpt=array of pt; Const xres=1024;Const yres=768;const DC_BRUSH=18; DC_PEN=19; procedure setfontsize(h:hdc;size:integer;style:pchar);begin Sele ctObject(h,CreateFont(size,0,0,0,400,0,0,0,DEFAULT_CHARSET,OUT_OUTLINE _PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,VARIABLE_PITCH,style)) ; End; procedure setfontcolours(h:hdc;text:longword;background:longword);begin SetTextColor(h,text) ; SetBkColor(h,background);End; procedure text(h:hdc;x:integer;y:integer;s:pchar); Var l:integer; begin l:=Length(s); textouta(h,x,y,s,L);End; procedure ClearScreen(h:hdc;colour:longint); begin SetDCBrushColor(h,colour); SetDCPenColor(h,colour); rectangle(h,0,0,xres,yres);End; procedure hidecursor();var consoleHandle:handle; info:CONSOLE_CURSOR_INFO; begin consolehandle := GetStdHandle(STD_OUTPUT_HANDLE); info.dwSize := 100; info.bVisible := FALSE; SetConsoleCursorInfo(consoleHandle, @info); End; procedure switch(var a:single;var b: single);var temp: single;begin temp := a; a := b; b := temp;end; procedure fill(h:hdc;p:Arrpt;c:longword); // hdc only for winapivarsy:single=100 0000;by:single=-1000000;dx,dy:single;a:Arr2D=nil;i,j,y,k:int32;S:array of single=nil;xi:array of single=nil;begin // colours//SetDCPenColor(h,c);//end colours// SetLength(a, length(p)+1, 2); For i :=0 To high(p) do begin a[i,0]:=p[i].x; a[i,1]:=p[i].y; If (Sy>p[i].y) Then Sy:=p[i].y; If (By
0) And (dx<>0) Then S[i]:=dx/dy; end; For y:=trunc(Sy-1) To trunc(By+1) do begin k:=0; For i:=0 To high(a)-1 do begin If ((a[i,1]<=y) And (a[i+1,1]>y)) Or (a[i,1]>y) And (a[i+1,1]<=y) Then begin xi[k]:=(a[i,0]+S[i]*(y-a[i,1])); k:=k+1; End; end; For j:=0 To k-2 do begin For i:=0 To k-2 do begin If xi[i]>xi[i+1] Then Switch (xi[i],xi[i+1]); end; end; i:=0; while (i< k-1) do begin // graphics// MoveToEx(h,trunc(xi[i]),y,nil); LineTo(h, trunc(xi[i+1]+1),y); //end graphics// i:=i+2 end; end;End; function createpolygon(p:pt;num:int32):Arrpt;const pi=3.1415 92653589793;varz,step:single;t:arrpt=nil;temp:pt;counter:int32=0;begin step:=2*pi/num;z:=0;while (z<=2*pi) dobegincounter:=counter+1;temp.x:= p.x+(50+random(300))*cos(z);temp.y:=p.y+(50+random(300))*sin(z);setlen gth(t,counter);t[counter-1].x:=temp.x;t[counter- 1].y:=temp.y;z:=z+step;end;exit(t);end; var Memhdc,WorkingScreen:hdc;p :hwnd; Membitmap:HBITMAP; sysmenu:qword; pts:arrpt;centre:pt;colour:lo ngint;numsides:int32;s:ansistring; begin p:=getconsolewindow();setwind owpos(p, HWND_TOPMOST, 100, 100, xres, yres,SWP_SHOWWINDOW);WorkingScreen:=GetDC(p);Memhdc := CreateCompatibleDC(WorkingScreen);Membitmap := CreateCompatibleBitmap(WorkingScreen, xres, yres); SelectObject(Memhdc, Membitmap);SelectObject(Memhdc,GetStockObj ect(DC_BRUSH));SelectObject(Memhdc,GetStockObject(DC_PEN));setfontsize (Memhdc,35,'comic sans ms');setfontcolours(Memhdc,rgb(200,100,0),0);//'some console instructionssysMenu := GetSystemMenu(p, False);DeleteMenu(sysMenu, SC_CLOSE, MF_BYCOMMAND) ; // 'cannot close consoleDeleteMenu(sysMenu, SC_MINIMIZE, MF_BYCOMMAND); //'To prevent user from minimizing console windowDeleteMenu(sysMenu, SC_MAXIMIZE, MF_BYCOMMAND); //'To prevent user from maximizing console windowDeleteMenu(sysMenu, SC_SIZE, MF_BYCOMMAND); //'non resizable consolehidecursor();ShowScrollBar(p, SB_BOTH, FALSE);SetBkMode (Memhdc, TRANSPARENT);centre.x:=xres/2;centre.y:=yres/2;While true do begin numsides:=(4+random(8)); pts:=createpolygon(centre,numsides); colour:=rgb(random(255),random(255),random(255)); clearscreen(Memhdc,rgb(55,255,255)); text(Memhdc,10,35,'This is drawn on the console.'); text(Memhdc,10,60,'Press escape to finish.'); str(numsides,s); text(Memhdc,10,700,'Number of sides '); text(Memhdc,230,700,pchar(s)); fill(Memhdc,pts,colour); BitBlt(WorkingScreen, 0, 0, xres, yres,Memhdc, 0, 0,SRCCOPY); If (GetAsyncKeyState($1B)<>0) Then //' escape key begin DeleteObject(Membitmap); DeleteDC (Memhdc); GetSystemMenu(p,true); Exit; end; sleep(500);end;end. ---------------------------------------------------------------------- zamtmn · 2022-05-22 04:09 Thanks. But without pixels, without DC and other graphics. Only mathematics ---------------------------------------------------------------------- circular · 2022-05-22 05:29 Ah ok. The algorithm I have been using is the following: going through horizontal lines and compute intersections with the segments of the polygon and from there determine the beginning and end of the area to fill for each Y coordinate. If you want lines that are oblique, for the patterns, you could use a similar algorithm, but by applying first a rotation of the the polygon, then compute the lines, then rotate back the lines. You can look at the code in BGRAFillInfo unit of BGRABitmap to see how it is done and make your own algorithm, or use the class TSimpleFillPolyInfo or TOnePassFillPolyInfo in it: - create a TPointF array with your area to fill (EmptyPointF to separate parts) - create instance of TSimpleFillPolyInfo with the TPointF coordinates of your area to fill - initialize an array of intersections with CreateIntersectionArray - with GetBounds determine all the possible Y - call ComputeAndSort for each Y coordinate. With the nbInter value, you know how many lines there are. For example if you have [0, 100, 150, 250, ...] and nbInter 4, then there are two lines : from 0 to 100 and from 150 to 250 - free intersection array with FreeIntersectionArray ---------------------------------------------------------------------- Seenkao · 2022-05-22 10:03 Quote from: zamtmn on May 22, 2022, 06:09:58 amThanks. But without pixels, without DC and other graphics. Only mathematics В любом из вариантов будет использоваться математика. ))) Проблемы поползут сразу со всех сторон, если ты будешь делать это пытаясь вычислить области и залить их. Первое, если это будет происходить отдельно, то надо будет состыковывать узор между собой. Второе, если заливаемое пространство будет очень сложным, то на вычисления этого пространства уйдёт очень много времени. Третье, заливка области математическим путём... это очень трудоёмкое занятие, как в плане вычислений, так и в плане работы. Если заливку просто сличать с областью выделения, будет намного проще и быстрее. Потому работа с каждым пикселями даст тебе возможность полного выбора любого пространства. Проблема только в том, что если пространство огромно, то и сложность вычисления будет длительно (но это в любом случае). Выбор всегда за тобой! Будешь заливать пространство, делай отдельное изображение для найденного пространства. Это даст тебе возможность смещать узор, не пересчитывая всё заново. Google translate: Either option will use math. ))) Problems will creep in from all directions at once if you do this by trying to figure out areas and fill them. First, if this happens separately, then it will be necessary to dock the pattern with each other. Second, if the space being filled is very complex, then it will take a very long time to calculate this space. Third, filling an area mathematically... is a very time-consuming task, both in terms of calculations and in terms of work. If the fill is simply compared to the selection area, it will be much easier and faster. Therefore, working with each pixel will give you the opportunity to fully select any space. The only problem is that if the space is huge, then the complexity of the calculation will be long (but this is in any case). The choice is always yours! When you fill in the space, make a separate image for the found space. This will give you the opportunity to shift the pattern without recalculating everything again. ---------------------------------------------------------------------- circular · 2022-05-22 12:25 @Seenkao: it is not necessarily so complicated, as I explained above. It is possible to determine where the lines are by comping scanlines. Here those are simply rotated 45 degrees. ---------------------------------------------------------------------- BobDog · 2022-05-22 12:54 I only use GDI to draw the polygon and lines. I have marked the non GDI procedures. steps create a polygon rotate it by 45 degrees get scan lines in the rotated polygon. rotate the scan lines back 45 degrees to fit into the original polygon. draw the original polygon and the new scan lines (via GDI) to see what is going on. I have set a gap of 10 for this demo viz: lines:=GetLines(newpoints,10); Coded and tested in Geany ide. Code: Pascal [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i useswindows;{$RANGECHECKS ON}type pt =objectx,y:single;end; type line=objectstart,finish:ptend; type Arrpt=array of pt; arrline=array of line; function rotate(pivot:pt;p:pt;a:single):pt; // not gdi var t:pt; begin t.x:=(Cos(a*0.0174533)*(p.x-pivot.x)-Sin(a*0.0174533)*(p.y-pivot.y)) +pivot.x; t.y:=(Sin(a*0.0174533)*(p.x-pivot.x)+Cos(a*0.0174533)*(p.y-pivot.y)) +pivot.y; exit(t); end; function centroid(p:arrpt):pt; // not gdi var i:int32; var ans:pt; begin ans.x:=0; ans.y:=0; for i:=0 to high(p) do begin ans.x:=ans.x+p[i].x; ans.y:=ans.y+p[i].y; end; ans.x:=ans.x/length(p); ans.y:=ans.y/length(p); exit(ans); end; function rotatepolygon(p:arrpt;angle:single):arrpt; // not gdi var c:pt; var i:int32; var ans:arrpt=nil; begin setlength(ans,length(p)); c:=centroid(p); for i:=0 to high(p) do ans[i]:=rotate(c,p[i],angle); exit(ans); end; function rotatelines(pivot:pt;L:arrline;angle:single):arrline; // not gdi var i:int32; begin for i:=0 to high(L) do begin L[i].start:=rotate(pivot,L[i].start,angle); L[i].finish:=rotate(pivot,L[i].finish,angle); end; exit(L); end; procedure switch(var a:single;var b: single); // not gdi used in getlines var temp: single; begin temp := a; a := b; b := temp; end; function GetLines(p:Arrpt;gap:int32):arrline; // not gditype Arr2D = array of array of single; var sy:single=1e6; by:single =-1e6; dx,dy:single; a:Arr2D=nil; i,j,y,k,count:int32; S:array of single=nil; xi:array of single=nil; ans:arrline=nil;begincount:=-1; SetLength(a, length(p)+1, 2); For i :=0 To high(p) do begin a[i,0]:=p[i].x; a[i,1]:=p[i].y; If (Sy>p[i].y) Then Sy:=p[i].y; If (By
0) And (dx<>0) Then S[i]:=dx/dy; end; For y:=trunc(Sy-1) To trunc(By+1) do begin k:=0; For i:=0 To high(a)-1 do begin If ((a[i,1]<=y) And (a[i+1,1]>y)) Or (a[i,1]>y) And (a[i+1,1]<=y) Then begin xi[k]:=(a[i,0]+S[i]*(y-a[i,1])); k:=k+1; End; end; For j:=0 To k-2 do begin For i:=0 To k-2 do begin If xi[i]>xi[i+1] Then Switch (xi[i],xi[i+1]); end; end; i:=0; while (i< k-1) do begin if (y mod gap=0) then begin count:=count+1; setlength(ans,count+1); ans[count].start.x:=xi[i]; ans[count].start.y:=y; ans[count].finish.x:=xi[i+1]+1; ans[count].finish.y:=y; end; i:=i+2 end; end; exit(ans);End; function createpolygon(p:pt;num:int32):Arrpt; // not GDI, rough way to get a polygonconst pi=3.141592653589793;varz, step:single;t:arrpt=nil;temp:pt;counter:int32=0;beginstep:=2*pi/num;z: =0;while (z<=2*pi) dobegincounter:=counter+1;temp.x:=p.x+(50+random(30 0))*cos(z);temp.y:=p.y+(50+random(300))*sin(z);setlength(t,counter);t[ counter-1].x:=temp.x;t[counter- 1].y:=temp.y;z:=z+step;end;exit(t);end; ///////////////////////// gdi stuff ////////////////////////// procedure drawpolygon(h:hdc;p:arrpt;colour:longword); var i:int32; begin SetDCPenColor(h,colour); for i:=0 to high(p)-1 do begin MoveToEx(h,trunc(p[i].x),trunc(p[i].y),nil); LineTo(h, trunc(p[i+1].x),trunc(p[i+1].y)); end; MoveToEx(h,trunc(p[high(p)].x),trunc(p[high(p)].y),nil); LineTo(h, trunc(p[0].x),trunc(p[0].y)); end; procedure drawlines(h:hdc;L:arrline;colour:longword); var i:int32; begin SetDCPenColor(h,colour); for i:=0 to high(L) do begin MoveToEx(h,trunc(L[i].start.x),trunc(L[i].start.y),nil); LineTo(h, trunc(L[i].finish.x),trunc(L[i].finish.y)); end; end; procedure setfontsize(h:hdc;size:integer;style:pchar);begin SelectOb ject(h,CreateFont(size,0,0,0,400,0,0,0,DEFAULT_CHARSET,OUT_OUTLINE_PRE CIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,VARIABLE_PITCH,style)); End; procedure setfontcolours(h:hdc;text:longword;background:longword);begin SetTextColor(h,text) ; SetBkColor(h,background);End; procedure text(h:hdc;x:integer;y:integer;s:pchar); Var l:integer; begin l:=Length(s); textouta(h,x,y,s,L);End; procedure ClearScreen(h:hdc;colour:longint); begin SetDCBrushColor(h,colour); SetDCPenColor(h,colour); rectangle(h,0,0,1024,768);End; procedure hidecursor();var consoleHandle:handle; info:CONSOLE_CURSOR_INFO; begin consolehandle := GetStdHandle(STD_OUTPUT_HANDLE); info.dwSize := 100; info.bVisible := FALSE; SetConsoleCursorInfo(consoleHandle, @info); End; const DC_BRUSH=18; DC_PEN=19; var Memhdc,WorkingScreen:hdc; p:hwnd; Membi tmap:HBITMAP; sysmenu:qword; pts:arrpt; centre:pt; numsides:int32; s:a nsistring; lines:arrline; newpoints:arrpt; newlines:arrline;begin p:=g etconsolewindow();setwindowpos(p, HWND_TOPMOST, 100, 100, 1024, 768,SWP_SHOWWINDOW);WorkingScreen:=GetDC(p);Memhdc := CreateCompatibleDC(WorkingScreen);Membitmap := CreateCompatibleBitmap(WorkingScreen, 1024, 768); SelectObject(Memhdc, Membitmap);SelectObject(Memhdc,GetStockObject(DC_BRUSH));SelectObject( Memhdc,GetStockObject(DC_PEN));setfontsize(Memhdc,35,'comic sans ms');setfontcolours(Memhdc,rgb(200,100,0),0);//'some console instructionssysMenu := GetSystemMenu(p, False);DeleteMenu(sysMenu, SC_CLOSE, MF_BYCOMMAND) ; // 'cannot close consoleDeleteMenu(sysMenu, SC_MINIMIZE, MF_BYCOMMAND); //'To prevent user from minimizing console windowDeleteMenu(sysMenu, SC_MAXIMIZE, MF_BYCOMMAND); //'To prevent user from maximizing console windowDeleteMenu(sysMenu, SC_SIZE, MF_BYCOMMAND); //'non resizable consolehidecursor();ShowScrollBar(p, SB_BOTH, FALSE);SetBkMode (Memhdc, TRANSPARENT);centre.x:=1024/2;centre.y:=768/2;While true do begin //non GDI numsides:=(4+random(8)); pts:=createpolygon(centre,numsides); newpoints:=rotatepolygon(pts,45); lines:=GetLines(newpoints,10); newlines:=rotatelines(centroid(pts),lines,-45); // GDI clearscreen(Memhdc,rgb(55,255,255)); text(Memhdc,10,60,'Press escape to finish.'); str(numsides,s); text(Memhdc,10,700,'Number of sides '); text(Memhdc,230,700,pchar(s)); drawpolygon(Memhdc,pts,rgb(200,0,0)); //original polygon drawlines(Memhdc,newlines,rgb(0,0,0)); // angled lines BitBlt(WorkingScreen, 0, 0, 1024, 768,Memhdc, 0, 0,SRCCOPY); If (GetAsyncKeyState($1B)<>0) Then //' escape key begin DeleteObject(Membitmap); DeleteDC (Memhdc); GetSystemMenu(p,true); Exit; end; sleep(1000);end;end. ---------------------------------------------------------------------- Seenkao · 2022-05-22 14:01 Quote from: circular on May 22, 2022, 02:25:25 pm@Seenkao: it is not necessarily so complicated, as I explained above. It is possible to determine where the lines are by comping scanlines. Here those are simply rotated 45 degrees. Да, в каких-то ситуациях это будет даже проще, чем перебирать все биты. Но чем сложнее фигуры (более загнутые, например спиральные) которые надо заливать, то с каждым шагом процесс усложняется. А для перебора будет без разницы насколько сложная фигура. Потому и надо выбирать от сложности заливаемых изображений. Точнее от поставленной задачи. Google translate: Yes, in some situations it will be even easier than iterating over all the bits. But the more complex the shapes (more curved, for example spiral ones) that need to be filled in, the process becomes more complicated with each step. And for enumeration, it will not matter how complex the figure is. Therefore, it is necessary to choose from the complexity of the fill. More precisely from the task at hand. ---------------------------------------------------------------------- zamtmn · 2022-05-22 14:07 circular >>You can look at the code in BGRAFillInfo unit of BGRABitmap Thanks! Thanks. I'll watch this Seenkao >>Потому работа с каждым пикселями даст тебе возможность полного выбора любого пространства I repeat: I don't have a raster, bmp, ogl context, DC and similar... vectos and coords only. and as result I needed a array of coordinates (in model space, not display space), not a raster >>Проблема только в том, что если пространство огромно, то и сложность вычисления будет длительно (но это в любом случае) this is one of the reasons. Raster will not be usable for this! BobDog Thanks! ---------------------------------------------------------------------- circular · 2022-05-22 16:34 You're welcome. By the way, to rotate the points you can use unit BGRATransform to create affine matrices that you can apply to arrays of TPointF. ---------------------------------------------------------------------- zamtmn · 2022-05-22 16:51 There will be no problems with transformations. I thought that this topic already has a lot of solutions, as it is for raster. and I was surprised when I didn't find anything on the first try ---------------------------------------------------------------------- BobDog · 2022-05-22 18:01 I noticed a couple of hints in my code, so I repaired them. What will be your graphics method to plot these lines once you have sorted out an algorithm? I more or less use the Windows console for simple graphics in freepascal. I tend just to write plain code (no projects) in Geany, I don't go through the process of creating a library (unit) on the fly. I avoid those complications. I only use Lazarus for testing, it is very good at giving warnings and hints. ---------------------------------------------------------------------- zamtmn · 2022-05-22 18:07 >>What will be your graphics method to plot these lines once you have sorted out an algorithm? OpenGL or GDI it for zcad https://forum.lazarus.freepascal.org/index.php/topic,16665.0.html ---------------------------------------------------------------------- BobDog · 2022-05-22 18:51 Thanks, I see your project. I ran a couple of samples in zcad.exe. Looks very neat. ---------------------------------------------------------------------- ADMGNS · 2022-05-27 23:42 hello have you tried "point in polygon" ways? you know, it's a kind of solution for irregular polygon filling.. pls see the attachment regs ---------------------------------------------------------------------- zamtmn · 2022-06-01 06:21 ADMGNS thanks, it's a little different Seenkao >>First, if this happens separately, then it will be necessary to dock the pattern with each other Everything turned out to be much simpler than it seems at first glance. Nothing needs to be dock-connect-attach. For example, in the picture it seems that this is a complex pattern, but in fact it is just a set of dotted lines at different angles with different steps ---------------------------------------------------------------------- ADMGNS · 2022-06-01 20:12 hello again.. in my example code, you can see a line below... imgDraw.Canvas.Pixels[x, y] := imgPattern.Canvas.Pixels[x, y]; so, if you have a bitmap that made of a tiled pattern, you can copy the pixels from this bitmap into the polygon using "point in polygon" methods.. and, as a result, you can get a pattern filled polygon.. i know, this is a very simple, even maybe a primitive approach. if we talk more wideview angle, we must define a record that consists of (multi)shapes (ie, ellipses, polygons, arcs etc) and some statues, ie including or excluding, and also color/stroke/pattern etc features.. as you know, this is a REGION logic supported many graphics libraries.. anyway.. regs