source: cpp/frams/canvas/nn_smart_layout.cpp @ 285

Last change on this file since 285 was 285, checked in by Maciej Komosinski, 7 years ago

Mac OS X support

  • Property svn:eol-style set to native
File size: 8.6 KB
Line 
1// This file is a part of the Framsticks GDK.
2// Copyright (C) 1999-2014  Maciej Komosinski and Szymon Ulatowski.  See LICENSE.txt for details.
3// Refer to http://www.framsticks.com/ for further information.
4
5#include "nn_layout.h"
6#include <vector>
7#include "common/nonstd_stl.h"
8#ifdef __BORLANDC__
9 #include <alloc.h> //borland needs for alloc/free
10#endif
11#ifdef MACOS
12 #include <stdlib.h>
13#endif
14
15#define DB(x)
16
17#if DB(1)+0
18#include <assert.h>
19#endif
20
21class block;
22
23/** Information about a single element (neuron). There are N einfo objects in an array called "einfo" */
24struct einfo
25{
26        /** Element's owner */
27        class block *block;
28
29        /** Integer coordinates (neurons are simply placed in a grid, (x,y) is a grid cell) */
30        int x, y;
31};
32
33/** Array[0..N-1] - one einfo for each neuron */
34static struct einfo* einfo;
35
36/** Array[0..N-1] - initially each neuron resides in its own block. The algorithm merges blocks until one single block is created */
37static block **blocks;
38
39/** N = number of neurons */
40static int N;
41
42/** Provides neuron connections information and receives the layout result */
43static NNLayoutState *nn;
44
45static char *JEDEN = (char*)"1";
46
47/** Block is a group of neurons.
48        After "blocking" some neurons, their relative location will not change. */
49class block
50{
51public:
52
53        /** Block's id is its index in the "blocks" array */
54        int id;
55
56        /** Block members (neurons), or actually neuron indexes (0..N-1 ints)  */
57        std::vector<int> elementy;
58
59        /** Block's bounding box (a rectangle containing all elemens)
60                w=maxx-minx+1; h=maxy-miny+1;
61                */
62        int w, h;
63        int minx, miny, maxx, maxy;
64
65        /** 2d array, w*h cells, indicating if a given (x,y) location is taken.
66                This speeds up checking if neurons from two blocks overlap.
67                */
68        char *map;
69
70        /** Creating an initial block consisting of a single neuron */
71        block(int nr) : id(nr), w(1), h(1), minx(0), miny(0), maxx(0), maxy(0)
72        {
73                DB(printf("new block(%d)\n", nr));
74                dodajelement(nr, 0, 0);
75                blocks[id] = this;
76                map = JEDEN;
77        }
78
79        ~block()
80        {
81                DB(printf("~ block(%d)\n", id));
82                blocks[id] = 0;
83                zwolnijmape();
84        }
85
86        void zwolnijmape(void)
87        {
88                if (map)
89                {
90                        if (map != JEDEN) free(map);
91                        map = 0;
92                }
93        }
94
95        void potrzebnamapa(void)
96        {
97                if (map) return;
98                odtworzmape();
99        }
100
101        void odtworzmape(void)
102        {
103                zwolnijmape();
104                w = maxx - minx + 1;
105                h = maxy - miny + 1;
106                map = (char*)calloc(1, w*h);
107                int i, e;
108                DB(printf("mapa bloku #%ld\n", id));
109                for (i = 0; i < elementy.size(); i++)
110                {
111                        e = elementy[i];
112                        map[w*(einfo[e].y - miny) + (einfo[e].x - minx)] = 1;
113                }
114                DB(for (i = 0; i < h; i++){ for (e = 0; e < w; e++)printf("%c", map[w*i + e] ? '*' : '.'); printf("\n"); })
115        }
116
117        /** Add a neuron to a block at location(x,y) */
118        void dodajelement(int nr, int x, int y)
119        {
120                elementy.push_back(nr);
121                einfo[nr].x = x;
122                einfo[nr].y = y;
123                einfo[nr].block = this;
124        }
125};
126
127static int moznadolaczyc(block *b, block *b2, int dx, int dy)
128{
129        /* Check if block b2 can be merged with b with b2 shifted by (dx,dy) so no overlap occurs.
130           All coordinates are relative to b->minx/miny
131           */
132        int x1, y1, x2, y2; // union rectangle
133        x1 = max(0, b2->minx - b->minx + dx);
134        x2 = min(b->maxx - b->minx, -b->minx + dx + b2->maxx);
135        if (x1 > x2) return 1;
136        y1 = max(0, b2->miny - b->miny + dy);
137        y2 = min(b->maxy - b->miny, -b->miny + dy + b2->maxy);
138        if (y1 > y2) return 1;
139        int x, y;
140        dx += b2->minx - b->minx; //dx,dy relative to minx,miny
141        dy += b2->miny - b->miny;
142        b->potrzebnamapa();
143        b2->potrzebnamapa();
144        for (y = y1; y <= y2; y++)
145        {
146                for (x = x1; x <= x2; x++)
147                        if (b->map[b->w*y + x] && b2->map[b2->w*(y - dy) + (x - dx)]) return 0;
148        }
149        return 1;
150}
151
152/** Merge b2 with b shifting b2 by (dx,dy) - adds all b2's neurons to b and deletes b2 */
153static int dolaczblock(block *b, block *b2, int dx, int dy)
154{ // return 1 if successful
155        if (!moznadolaczyc(b, b2, dx, dy)) return 0; // merging causes no collision
156        DB(printf("#%ld(%ld,%ld,%ld,%ld) + #%ld(%ld,%ld,%ld,%ld)<%ld,%ld>", b->id, b->minx, b->miny, b->maxx, b->maxy, b2->id, b2->minx, b2->miny, b2->maxx, b2->maxy, dx, dy));
157
158        b->zwolnijmape();
159        int i, e;
160        for (i = 0; i < b2->elementy.size(); i++)
161        {
162                e = b2->elementy[i];
163                b->dodajelement(e, einfo[e].x + dx, einfo[e].y + dy);
164        }
165        b->minx = min(b->minx, dx + b2->minx);
166        b->miny = min(b->miny, dy + b2->miny);
167        b->maxx = max(b->maxx, dx + b2->maxx);
168        b->maxy = max(b->maxy, dy + b2->maxy);
169
170        DB(printf(" -> (%ld,%ld,%ld,%ld)\n", b->minx, b->miny, b->maxx, b->maxy));
171
172        DB(printf(" ...#%ld...(%ld)...", b->id, b->elementy.size()));
173        for (i = 0; i < b->elementy.size(); i++)
174        {
175                e = b->elementy[i];
176                DB(assert(einfo[e].x >= b->minx);)
177                        DB(assert(einfo[e].x <= b->maxx);)
178                        DB(assert(einfo[e].y >= b->miny);)
179                        DB(assert(einfo[e].y <= b->maxy);)
180                        DB(printf("(%ld)%ld,%ld ", e, einfo[e].x, einfo[e].y));
181        }
182
183        DB(printf("\n"));
184
185        delete b2;
186        return 1;
187}
188
189/** e2 neuron will be connected to e neuron's input:
190        - e and e2 belong to different blocks: shift/merge the blocks so e2 is to the left of e
191        - same block: nothing can be done
192        */
193static void polaczjakowejscie(int e, int e2)
194{
195        block *b, *b2;
196        b = einfo[e].block;
197        if (!einfo[e2].block) new block(e2);
198        b2 = einfo[e2].block;
199        if (b == b2)
200        {
201                DB(printf("--- b==b2 -> cancel\n"));
202                return;
203        }
204        int dx, dy;
205        dx = einfo[e].x - einfo[e2].x;
206        dy = einfo[e].y - einfo[e2].y;
207        DB(printf("  elem.%ld (%ld,%ld@%ld) + elem.%ld (%ld,%ld@%ld)...\n", e, einfo[e].x, einfo[e].y, b->id, e2, einfo[e2].x, einfo[e2].y, b2->id));
208        if (dolaczblock(b, b2, dx - 1, dy)) return;
209        int proba; // retry - increasing the y offset (keeps x offset at -1)
210        for (proba = 1;; proba++)
211        {
212                if (dolaczblock(b, b2, dx - 1, dy - proba)) return;
213                if (dolaczblock(b, b2, dx - 1, dy + proba)) return;
214        }
215}
216
217/** Retrieve the information about neuron e inputs and place the input neurons accordingly
218        unless they are already processed
219        */
220static void ustawelement(int e)
221{
222        if (einfo[e].block)
223        {
224                DB(printf("block#%ld exists\n", e));
225                return;
226        }
227        new block(e);
228        int we;
229        int n = nn->GetInputs(e);
230        for (we = 0; we < n; we++)
231        {
232                int e2 = nn->GetLink(e, we);
233                if (e2 < 0) continue;
234                if (e == e2) continue;
235                ustawelement(e2);
236                polaczjakowejscie(e, e2);
237        }
238}
239
240/**
241   The algorithm:
242   1. Phase one
243    - for each neuron, place its input neurons to the left
244      (at relative location (-1,dy), where dy is any number, ideally 0)
245    - the neuron's location in a block is never changed after the initial assignment
246    - which means that any further connections within a given block are ignored (neurons are already fixed in place)
247    - foreign block connections cause block merges, shifting the blocks so the (-1,dy) condition is met
248      (which also affects all neurons in the other block, since their relative positions are fixed)
249    - the final result is a set of blocks corresponding to the "islands" in the neural network
250   2. Phase two
251    - "islands" are merged into one final block. Any relative offsets can be used, as their neurons
252      are not connected anyway. Here a simple method is used: placing the islands in a vertical stack.
253 */
254void smartlayout(NNLayoutState *nnlayout)
255{
256        DB(printf("\n >>>>>>>>> smartlayout <<<<<<<<<<<\n"));
257        nn = nnlayout;
258        N = nn->GetElements();
259        einfo = (struct einfo*)calloc(N, sizeof(struct einfo));
260        blocks = (class block**)calloc(N, sizeof(class block*));
261
262        int el;
263        for (el = 0; el < N; el++) ustawelement(el);
264
265        DB(printf(" - - merging blocks - -\n"));
266
267        block *first;
268        for (el = 0; el < N; el++) if (blocks[el]) { first = blocks[el]; el++; break; }
269        while (el<N)
270        {
271                if ((first->maxx - first->minx)>(first->maxy - first->miny))
272                {
273                        int y = first->maxy + 2;
274                        int x = first->minx;
275                        int ex = first->maxx;
276                        while (el<N)
277                        {
278                                if (blocks[el])
279                                {
280                                        int dx = blocks[el]->maxx - blocks[el]->minx + 2;
281                                        dolaczblock(first, blocks[el], x - blocks[el]->minx, y - blocks[el]->miny);
282                                        x += dx;
283                                        if (x>ex) break;
284                                }
285                                el++;
286                        }
287                }
288                else
289                {
290                        int x = first->maxx + 2;
291                        int y = first->miny;
292                        int ey = first->maxy;
293                        while (el<N)
294                        {
295                                if (blocks[el])
296                                {
297                                        int dy = blocks[el]->maxy - blocks[el]->miny + 2;
298                                        dolaczblock(first, blocks[el], x - blocks[el]->minx, y - blocks[el]->miny);
299                                        y += dy;
300                                        if (y>ey) break;
301                                }
302                                el++;
303                        }
304                }
305        }
306        /*
307        for (;el<N;el++)
308        if (blocks[el])
309        dolaczblock(first,blocks[el],first->minx-blocks[el]->minx,first->maxy-blocks[el]->miny+1);
310        */
311        if (first) // at this stage we have a single block containing all neurons
312        {
313                DB(printf(" - - setting coordinates - -\n"));
314                int i;
315                DB(first->odtworzmape());
316                for (i = 0; i < first->elementy.size(); i++)
317                {
318                        el = first->elementy[i];
319                        nn->SetXYWH(el, einfo[el].x * 70, -einfo[el].y * 70, 50, 50);
320                }
321                delete first;
322        }
323
324        free(blocks);
325        free(einfo);
326        DB(printf("--------------------------------\n\n"));
327}
Note: See TracBrowser for help on using the repository browser.