← Back to team overview

kicad-developers team mailing list archive

Zone filling optimizations, first results


Our goal is to lower the total number of segments needed to fully cover
a zone area like those:
As Dick stated, there are at least two (non mutually exclusive) methods
do achieve this: augment segment width and/or decimate segments in of a 
certain direction.
I've explored the second path, choosing to decimate the vertical
segments (because i suppose that horizontal segments are drawn quickly).
I tested two approaches:

First approach:

1. decimate ALL the vertical segments which cover areas already
covered by horizontal segments.
This will obviously worsen the rendering of the vertical sides of the
filled zone and the circular regions around pads/etc too:

2. use my first patch (smooth algorithm) to connect together every
endpoint nearer than (gGridRoutingSize * sqrt(2)) using new segments. 
This will produce
smooth vertical and oblique margins in all the regions (and smoothing in
the circular/square regions around pads/thermal/etc).
But will also add a LOT of new short segments and introduce oblique zone
segments (which where absent before):

3. use an algorithm to merge together all the consecutive vertical
segments in one long segment (and all the oblique segments with the same
slope into one long segment), therefore decimating all the small
segments introduced in step 2:

4. with step 2, you can not eliminate 'damages' introduced by step 1
around pads/etc because the smoothing algorithm smooths by connecting
segments endpoints and so is useless situations like this:
So I developed an algorithm to try to resolve this issue. The results 
are good:
but it reintroduces a lot of short segments, so even if the visual 
result is good, the complexity introduced is definitely too much...

- we get smooth sides on vertical and oblique sides of the filled zone
- the overall numbers of segments is slightly reduced for small boards

- this approach is slooow: for every step you have nested iterations
through the zone segment list (i.e. for every segment you have to check
all the other segments against some rule), therefore the complexity is
(This is not completely true for step 2, because for every segments you
need to iterate only forward in the list thanks to the way segments
endpoint are connected. So O() it's something less than O(n^2))
- the overall numbers of segments is incremented for big boards

Second approach:

1. delete all the vertical segments which do NOT cover any another
segment endpoints. In this way you do not introduce the awful
aberrations introduced by step 1 of the first approach.
So you'll eliminate all the vertical segments which start and end INSIDE
an horizontal segment and which do not pass through other segments
2. shorten remaining vertical segments which starts INSIDE an horizontal
segment but ends on another horizontal segment endpoint, to a 2 unit
segment (Please see images, way better than my explanation)

- the overall number of vertical segments is highly reduced and many non
deleted segments are reduced to minimum length
- relatively fast: step 1 and 2 are done in the same time (but
complexity is still O(n^2))
- the visual result of the filled zone is identical to the one obtained
with the original method (or at least it should be...)

- slower than original fill method
- does not improve zone beautifulness :-)

I guess that with the second approach, the increased computational time
for zone filling is worth the obtained faster redrawing on the screen,
but this could not be true for big and complicated zones.

Note that we can still apply the smoothing algorithm and the merging
algorithm after the second approach only to the oblique sides of the
zones to obtain smooth lines. I'm still investigating the best way to
obtain the best from the two methods applied together.

Some results applying the above methods to two demo boards:
pic_programmer.brd, video.brd
for both fill grid 0.0050, clearance 0.0200
Note that times are highly approximative !!!
(PC is a Dell Latitude D820, Core Duo 2 T7200@2GHz, Ubuntu 8.04)

*********** pic_programmer ****************

Initial zone segments (added by original fill): 13536
time to fill: 2 secs

First approach:
(1) Delete vertical segments: -6925
(2) Smooth added segments: 12514
(3) Collinear optimized: vertical= -8280, oblique= -2318
(4 Heal damages) Added segments: 4127

Final zone segments: 12654 (-6%)
time to fill: 5 secs

Second approach:
(1) Deleted vertical segments: -3801
(2) Shortened vertical segments: -1884

Final zone segments*: 9735 (-28%)
time to fill: 3 secs

************* video ********************

Initial zone segments (added by original fill): 41891
time to fill: 4 secs

First approach:
(1) Delete vertical segments: -23288
(2) Smooth added segments: 32983
(3) Collinear optimized vertical= -15074, oblique= -5907
(4 Heal damages) Added segments: 16469

Final zone segments: 47074 (+12%)
time to fill: 60

Second approach:
(1) Deleted vertical segments: -10839
(2) Shortened vertical segments: -5969

Final zone segments*: 31052 (-25%)
time to fill: 22 secs

*note: we do not consider shortened segments because they are still 
there, but since their length has been shortened a lot, we could take 
them into account too

the second approach seems worth including in the zone filling source.
The others approach must be optimized and refined. I'll post to the list
only the second approach patch (because it's the only useful for now),
but everyone interested could email me directly and I'll be glad to
share all the other algorithm code.
Dick (or anyone interested), may you please try the attached patch
against your big board or send me your board for performance testing? I
think that if you send it to me without all the modules/labels/etc, i.e.
tracks and zones only, that should be ok for you about everything
concerning privacy/security/copyrights/etc. Am I right?

At this time deleted zone segments are not removed from the screen until 
you force a refresh (zooming in or out for example).
The relative function is commented out in source because I've not yet 
figured out what to put as DrawPanel (The kicad class hierarchy is not 
yet completely clear to me :-) )
Can someone please give some advice? Thanks

 --------------000902060503020009060802 Content-Type: text/x-diff;
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;

--- /tmp/zone_filling_algorithm.cpp	2008-09-13 12:45:24.000000000 +0200
+++ zone_filling_algorithm.cpp	2008-09-13 12:38:56.000000000 +0200
@@ -15,6 +15,7 @@

/* Local functions */
static void Genere_Segments_Zone( WinEDA_PcbFrame* frame, wxDC* DC, int net_code, int layer );
+static void Shorthen_Vertical_Segments( WinEDA_PcbFrame* frame, wxDC* DC, int net_code, int layer );

/* Local variables */
static bool Zone_Debug = false;
@@ -452,8 +453,103 @@
msg.Printf( wxT( "%d" ), nbsegm );
Affiche_1_Parametre( frame, -1, wxEmptyString, msg, BROWN );
+ // Comment this out to disable zone zegments optimization
+ Shorthen_Vertical_Segments( frame, DC, net_code, layer );

+static void Shorthen_Vertical_Segments( WinEDA_PcbFrame* frame, wxDC* DC, int net_code, int layer )
+ SEGZONE* next;
+ int cnt;
+ int nbsegm = 0;
+ int shor = 0;
+ bool is_on_top_endp = false, is_on_bot_endp = false;
+ int ys, ye, x, z_start_x, z_start_y, z_end_x, z_end_y;
+ for( SEGZONE* zone = frame->m_Pcb->m_Zone; zone != NULL; zone = next )
+ {
+ next = zone->Next();
+ // Skip non vertical tracks
+ if ( (zone->m_Start).x != (zone->m_End).x )
+ continue;
+ //printf("(%i,%i -> %i,%i)\n", (zone->m_Start).x, (zone->m_Start).y, (zone->m_End).x, (zone->m_End).y);
+ // For now we skip bottom-up tracks
+ // TODO: Non si dovrebbe verificare mai, eliminare
+ if ( (zone->m_Start).y > (zone->m_End).y )
+ {
+ //printf("Skip (INVERTED)\n");
+ continue;
+ }
+ ys = (zone->m_Start).y;
+ ye = (zone->m_End).y;
+ x = (zone->m_Start).x;
+ cnt = 0;
+ is_on_top_endp = is_on_bot_endp = false;
+ // Check if this vertical track covers space already covered by horizontal tracks
+ for( SEGZONE* z= frame->m_Pcb->m_Zone; z != NULL; z = z->Next() )
+ {
+ if ( z == zone)
+ continue;
+ z_start_y = (z->m_Start).y;
+ z_end_y = (z->m_End).y;
+ // Skip non horizontal tracks
+ if ( z_start_y != z_end_y)
+ continue;
+ // Skip non intersecting tracks
+ if ( (z_start_y < ys) || (z_start_y > ye) )
+ continue;
+ z_start_x = (z->m_Start).x;
+ z_end_x = (z->m_End).x;
+ if ( (z_start_x > x) || (z_end_x < x) )
+ continue;
+ if ( (z_start_x == x) || (z_end_x == x) )
+ {
+ if ( z_start_y == ys )
+ is_on_top_endp = true;
+ else if ( z_start_y == ye )
+ is_on_bot_endp = true;
+ else
+ // Exit and skip this whole vertical if it passes through any horizontal track start/end
+ {
+ //printf("\tBREAK (%i,%i -> %i,%i\n", z_start_x, z_start_y, z_end_x, z_end_y);
+ break;
+ }
+ }
+ cnt ++;
+ //printf("\t\t[%i,%i -> %i,%i]\n", z_start_x, z_start_y, z_end_x, z_end_y);
+ }
+ //printf("\tcnt= %i, res= %i\n", cnt, ys + g_GridRoutingSize * (cnt-1));
+ if ( (cnt > 0) && (ye == ys + g_GridRoutingSize * (cnt-1)) )
+ {
+ if ( !is_on_top_endp && !is_on_bot_endp )
+ {
+ //printf("\tDELETED !\n");
+ // Erase segment from screen
+ //if( DC )
+ // Trace_Une_Piste( DrawPanel, DC, zone, 0, GR_XOR );
+ // Remove item from linked list and free memory
+ zone->DeleteStructure();
+ nbsegm++;
+ }
+ else if (!is_on_top_endp)
+ {
+ (zone->m_Start).x = x;
+ (zone->m_Start).y = ye - g_GridRoutingSize;
+ shor++;
+ }
+ else if (!is_on_bot_endp)
+ {
+ (zone->m_End).x = x;
+ (zone->m_End).y = ys + g_GridRoutingSize;
+ shor++;
+ }
+ }
+ }
+ //printf("Deleted= -%i, Shorthened= -%i\n", nbsegm, shor);

int Propagation( WinEDA_PcbFrame* frame )

Follow ups